commit 9299623e19913db9f913f94b1e8374853a2640bd Author: Luis Ibanez Date: Sun Feb 5 11:35:58 2012 -0500 ENH: Initial import from sourceforge. These source tree was directly imported from http://sourceforge.net/projects/fis-gtm/files/GT.M-x86-Linux-src/V5.4-002B/ by extracting the file: gtm_V54002B_linux_i686_src.tar.gz diff --git a/COPYING b/COPYING new file mode 100755 index 0000000..dba13ed --- /dev/null +++ b/COPYING @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + 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 +them 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. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero 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 that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + 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 +state 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 Affero General Public License as published by + the Free Software Foundation, either version 3 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/README b/README new file mode 100755 index 0000000..bd2ab98 --- /dev/null +++ b/README @@ -0,0 +1,106 @@ +The make file enclosed (sr_unix/comlist.mk) will build GT.M from source. +The prerequisites are GNU make, GT.M binary installation (which you can +download from http://sourceforge.net/projects/fis-gtm/), Linux x86, tcsh, +Unicode and GPG include files. Unicode include files are automatically +installed if ICU is installed. GPG include files require installing the +GNUPG and related library development packages. GNU make 3.81, +Ubuntu 10.04 LTS and RHEL 5.0 were used to do the test builds for this +distribution. The default ICU and GPG packages were taken from the OS +vendors' repositories. + +To build a production version GT.M for linux do the following steps: +1. Fulfill the pre-requisites. + Download and install GT.M binary distribution from SourceForge if you + do not have GT.M installed already. The build procedure needs an + existing GT.M mumps installed on the machine. + + You can download GT.M from http://sourceforge.net/projects/fis-gtm/ + Unpack the tar file and run the configure script as root. Note: the tar + file unpacks everything into your current working directory, not a new + subdirectory. The Linux Standard Base (LSB) install path for GT.M + V54002 is /opt/lsb-gtm/V5.4-002_i686 or /opt/lsb-gtm/V5.4-002_x8664. + $ tar xfz gtm_V54002_linux_i686_src.tar.gz + $ sudo sh ./configure + +2. Unpack the GT.M sources + Change directory in the directory that you will place the GT.M source, + here after referred to as . + $ mkdir + $ cd + $ tar xfz gtm_V54002_linux_i686_src.tar.gz + + You should find this README, COPYING file and sr_* source directroies. + +3. Define environment variables needed to build GT.M + You will need to use tcsh to build GT.M. GT.M uses several csh script + files the define environment variables used in the build process. You + will need to define several variables prior to intiating your GT.M build. + + - Define 'gtm_curpro' and 'gtm_exe' so that you can compile M programs. + This is the directory in which you installed the GT.M binaries from + SourceForge. + $ setenv gtm_curpro + $ setenv gtm_exe $gtm_curpro + $ setenv HOSTOS `uname -s` + + - Define 'gtm_tools' and 'gtm_inc' + $ setenv gtm_tools $PWD/sr_linux + $ setenv gtm_inc $PWD/sr_linux + + - [OPTIONAL] Ubuntu users must define 'distro' + $ setenv distro ubuntu + + - [OPTIONAL] By default the build procedure will build 64 bit version of + GT.M on a x86_64 bit machine. + If you intend to build 32 bit version of GT.M on a x86_64 bit machine you + have to explicitly set the environment variable 'OBJECT_MODE' to '32' + $ setenv OBJECT_MODE 32 + + - Specify which ICU version is installed. + ICU version needs to be of the form #.# If the result of running + "icu-config --version" has the form #.#.#, just use the first two + parts. For example, if "icu-config --version" returns 3.8.1, use + 3.8 in the "setenv" step. + $ icu-config --version + $ setenv gtm_icu_version + + - Define 'gtm_version_change' and execute gtm_env.csh + $ setenv gtm_version_change 1 + $ source sr_unix/gtm_env.csh + +4. Building GT.M - + +By default, the gmake will build a production version GT.M. The build type +of GT.M can be controlled by a parameter "buildtypes" - dbg (debug), +bta (beta), and pro (production). Passing a subset of dbg, bta, or pro in +"buildtypes" from the environment or the command line will build that subset. +For example: + + gmake -f sr_unix/comlist.mk -I./sr_unix -I./sr_linux buildtypes=dbg gtm_ver=$PWD + +will build just a debuggable GT.M release. + +You can clean your builds by appending 'clean' to the make line. +For example: + gmake -f sr_unix/comlist.mk -I./sr_unix -I./sr_linux buildtypes=dbg gtm_ver=$PWD clean + +5. Packaging GT.M - + +Once the required builds have been done the object distribution can be +tar'ed by doing: + + gmake -f sr_unix/comlist.mk -I./sr_unix -I./sr_linux gtm_ver=$PWD package + +Again, buildtypes can be used to package just a subset. For example: + + gmake -f sr_unix/comlist.mk -I./sr_unix -I./sr_linux buildtypes=pro gtm_ver=$PWD package + +Appendix: Known warnings and errors + - "cc1: note: obsolete option -I- used, please use -iquote instead" + You can safely ignore this warning + + - "chk2lev.mdep:2: *** missing separator. Stop." + tcsh is using the builtin echo, you need to set the environment variable + 'distro' to 'ubuntu' and clean your build. + $ setenv distro ubuntu + diff --git a/sr_i386/aswp.s b/sr_i386/aswp.s new file mode 100644 index 0000000..09f42b7 --- /dev/null +++ b/sr_i386/aswp.s @@ -0,0 +1,27 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title aswp.s + .sbttl aswp +.include "linkage.si" + +# .386 +# .MODEL FLAT, C + + .text +ENTRY aswp + movl 4(%esp),%edx # A(latch longword) + movl 8(%esp),%eax # replacement value +# LOCK xchg (%edx),%eax # return original value + lock + xchgl (%edx),%eax # return original value + ret diff --git a/sr_i386/auto_zlink.c b/sr_i386/auto_zlink.c new file mode 100644 index 0000000..7dd1b77 --- /dev/null +++ b/sr_i386/auto_zlink.c @@ -0,0 +1,97 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "i386.h" +#include "urx.h" +#include "rtnhdr.h" +#include "op.h" +#include "auto_zlink.h" + +#define PEA_SZ 5 +#define XFER_BYTE_SZ 3 +#define XFER_LONG_SZ 6 +#define INST_SZ 1 + +rhdtyp *auto_zlink (unsigned char *pc, int4 **line) +{ + char *adj_pc; /* address of PEA rtnref offset */ + mstr rname; + mident_fixed rname_local; + urx_rtnref *rtnurx; + mval rtn; + rhdtyp *rhead; + error_def (ERR_ROUTINEUNKNOWN); + error_def (ERR_LABELUNKNOWN); + union + { + ModR_M modrm; + unsigned char byte; + } modrm_byte_byte, modrm_byte_long; + +/* ASSUMPTION -- The instruction previous to the current mpc is a transfer table jump. + * This is either a byte or a int4 displacement off of ebx, instruction + * size either 3 or 6 (prefix byte, ModR/M byte, 8- or 32-bit offset). + */ + + modrm_byte_byte.modrm.reg_opcode = I386_INS_CALL_Ev; + modrm_byte_byte.modrm.mod = I386_MOD32_BASE_DISP_8; + modrm_byte_byte.modrm.r_m = I386_REG_EBX; + + modrm_byte_long.modrm.reg_opcode = I386_INS_CALL_Ev; + modrm_byte_long.modrm.mod = I386_MOD32_BASE_DISP_32; + modrm_byte_long.modrm.r_m = I386_REG_EBX; + + if (*(pc - XFER_BYTE_SZ) == I386_INS_Grp5_Prefix && *(pc - XFER_BYTE_SZ + 1) == modrm_byte_byte.byte) + { + assert (*(pc - XFER_BYTE_SZ - PEA_SZ) == I386_INS_PUSH_Iv); + adj_pc = (char *)pc - XFER_BYTE_SZ - PEA_SZ; + } + else if (*(pc - XFER_LONG_SZ) == I386_INS_Grp5_Prefix && *(pc - XFER_LONG_SZ + 1) == modrm_byte_long.byte) + { + assert (*(pc - XFER_LONG_SZ - PEA_SZ) == I386_INS_PUSH_Iv); + adj_pc = (char *)pc - XFER_LONG_SZ - PEA_SZ; + } + else + GTMASSERT; + + if (azl_geturxrtn (adj_pc + INST_SZ, &rname, &rtnurx)) + { + assert (0 <= rname.len && rname.len <= MAX_MIDENT_LEN); + assert (rname.addr); + /* Copy rname into local storage because azl_geturxrtn sets + rname.addr to an address that is freed during op_zlink + and before the call to find_rtn_hdr. + */ + memcpy(rname_local.c, rname.addr, rname.len); + rname.addr = rname_local.c; + assert (rtnurx); + assert (*(adj_pc - PEA_SZ) == I386_INS_PUSH_Iv); + assert (azl_geturxlab (adj_pc - PEA_SZ + INST_SZ, rtnurx)); + assert (!find_rtn_hdr (&rname)); + rtn.mvtype = MV_STR; + rtn.str.len = rname.len; + rtn.str.addr = rname.addr; + op_zlink (&rtn, 0); + if (0 != (rhead = find_rtn_hdr (&rname))) + { + *line = *(int4 **) (adj_pc - PEA_SZ + INST_SZ); + if (!(*line)) + rts_error(VARLSTCNT(1) ERR_LABELUNKNOWN); + return rhead; + } + } + rts_error(VARLSTCNT(1) ERR_ROUTINEUNKNOWN); + return NULL; +} diff --git a/sr_i386/auto_zlink.h b/sr_i386/auto_zlink.h new file mode 100644 index 0000000..cc26928 --- /dev/null +++ b/sr_i386/auto_zlink.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __AUTO_ZLINK_H__ +#define __AUTO_ZLINK_H__ + +rhdtyp *auto_zlink (unsigned char *pc, int4 **line); + +#endif diff --git a/sr_i386/call_dm.s b/sr_i386/call_dm.s new file mode 100644 index 0000000..7a26694 --- /dev/null +++ b/sr_i386/call_dm.s @@ -0,0 +1,35 @@ +################################################################# +# # +# Copyright 2001, 2007 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title call_dm.s + .sbttl call_dm +.include "linkage.si" + +# .386 +# .MODEL FLAT, C + + .DATA + + .text +.extern op_oldvar +.extern opp_dmode + +# PUBLIC call_dm +# call_dm PROC +ENTRY call_dm +l1: call opp_dmode + call op_oldvar + jmp l1 + ret +# call_dm ENDP + +# END diff --git a/sr_i386/caller_id.s b/sr_i386/caller_id.s new file mode 100644 index 0000000..31b8485 --- /dev/null +++ b/sr_i386/caller_id.s @@ -0,0 +1,29 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title caller_id.s + .sbttl caller_id + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + + .text + +# PUBLIC caller_id +ENTRY caller_id + movl 4(%ebp),%eax # address of caller's return address + ret +# caller_id ENDP + +# END diff --git a/sr_i386/callg.s b/sr_i386/callg.s new file mode 100644 index 0000000..bb33d65 --- /dev/null +++ b/sr_i386/callg.s @@ -0,0 +1,58 @@ +################################################################# +# # +# Copyright 2001, 2006 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title callg.s + .sbttl callg + +.include "linkage.si" + +# .386 +# .MODEL FLAT, C + +# Assembler routine to immitate the VAX CALLG instruction, which can redirect +# a routine calls argument list to a prepared list in memory. +# This implementation has the caveat that the argument list must be integers, +# and that it follows our version of the VMS calling conventions - i.e. that +# a count of the arguments following is actually the first thing on the stack. + +routarg = 8 +argsarg = 12 +cntsav = -4 + .text +ENTRY callg + enter $4,$0 + pushl %edi + pushl %esi + pushl %ebx + + movl routarg(%ebp),%edx # routine to call + movl argsarg(%ebp),%esi # argument list + movl (%esi),%ecx + movl %ecx,cntsav(%ebp) # save following argument count + addl $4,%esi # skip argument count + movl %ecx, %eax + negl %eax + leal (%esp,%eax,4),%esp + movl %esp,%edi + pushl %ecx + rep + movsl + call *%edx + popl %edx # discard possibly modified count + movl cntsav(%ebp),%edx # edx to preserve return value in eax + leal (%esp,%edx,4),%esp + + popl %ebx + popl %esi + popl %edi + leave + ret diff --git a/sr_i386/ci_restart.s b/sr_i386/ci_restart.s new file mode 100644 index 0000000..273e127 --- /dev/null +++ b/sr_i386/ci_restart.s @@ -0,0 +1,55 @@ +################################################################# +# # +# Copyright 2001, 2003 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title ci_restart.s + .sbttl ci_restart +# .386 +# .MODEL FLAT, C +.include "linkage.si" + + .DATA +.extern param_list + + .text +ENTRY ci_restart + pushl %ebp # save C frame pointer + movl %esp,%ebp # set frame marker for this procedure + movl param_list,%eax + movl 4(%eax),%eax # argcnt + cmpl $0,%eax # if (argcnt > 0) { + jle L0 + imull $4,%eax,%edx # param_list->args[argcnt] + leal 20(%edx),%edx + pushl %eax + movl param_list,%eax + addl %eax,%edx + popl %eax +L1: pushl 0(%edx) # pushing arguments backwards + subl $4,%edx + subl $1,%eax + cmpl $0,%eax + jg L1 # } +L0: movl param_list,%eax + pushl 4(%eax) #argcnt + pushl 20(%eax) #mask + pushl 16(%eax) #retaddr + pushl 12(%eax) #labaddr + pushl 8(%eax) #rtnaddr + + pushl 4(%ebp) # push the return address to the caller of ci_restart + movl 0(%ebp),%ebp # restore previous C frame pointer + jmp *(%eax) + ret + +# ci_restart ENDP + +# END diff --git a/sr_i386/compswap.s b/sr_i386/compswap.s new file mode 100644 index 0000000..1281cb3 --- /dev/null +++ b/sr_i386/compswap.s @@ -0,0 +1,34 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title compswap.s + .sbttl compswap +.include "linkage.si" + +# .386 +# .MODEL FLAT, C + + .text +ENTRY compswap + movl 4(%esp),%edx # A(latch longword) + movl 8(%esp),%eax # comparison value + movl 12(%esp),%ecx # replacement value +# LOCK cmpxchgl %ecx,(%edx) + lock + cmpxchgl %ecx,(%edx) # compare-n-swap + jnz fail + movl $1,%eax # return TRUE + ret + +fail: + xor %eax,%eax # return FALSE + ret diff --git a/sr_i386/dm_start.s b/sr_i386/dm_start.s new file mode 100644 index 0000000..37ae81e --- /dev/null +++ b/sr_i386/dm_start.s @@ -0,0 +1,73 @@ +################################################################# +# # +# Copyright 2001, 2010 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title dm_start.s + .sbttl dm_start + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .include "error.si" + + .DATA +.extern dollar_truth +.extern xfer_table +.extern frame_pointer +.extern msp +.extern mumps_status +.extern restart + + .text + +.extern mdb_condition_handler +.extern op_unwind + +.ifndef cygwin + .type dm_start,@function +.endif +ENTRY dm_start + enter $0,$0 + pushl %edi + pushl %esi + pushl %ebx + movl $1,mumps_status + leal xfer_table,%ebx + movl $1,dollar_truth + ESTABLISH mdb_condition_handler, l30 + call *restart + +return: movl mumps_status,%eax + popl %ebx + popl %esi + popl %edi + leave + ret + +ENTRY gtm_ret_code + REVERT + call op_unwind + movl msp,%eax + movl (%eax),%eax + movl %eax,frame_pointer + addl $4,msp + jmp return + +# Used by triggers (and eventually call-ins) to return from a nested generated code call +# (a call not at the base C stack level). +ENTRY gtm_levl_ret_code + REVERT + jmp return + +# dm_start ENDP + +# END diff --git a/sr_i386/emit_code.c b/sr_i386/emit_code.c new file mode 100644 index 0000000..70c1922 --- /dev/null +++ b/sr_i386/emit_code.c @@ -0,0 +1,1315 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "compiler.h" +#include "rtnhdr.h" +#include "stack_frame.h" +#include "opcode.h" +#include "xfer_enum.h" +#include "mdq.h" +#include "vxi.h" +#include "vxt.h" +#include "cgp.h" +#include "obj_gen.h" +#include "i386.h" +#include "obj_file.h" +#include "emit_code.h" +#include "hashtab_mname.h" +#include "stddef.h" + + +#define BUFFERED_CODE_SIZE 50 +#define LONG_JUMP_OFFSET (0x7ffffffc) + +#define XFER_BYTE_INST_SIZE 3 +#define XFER_LONG_INST_SIZE 6 +#define BRB_INST_SIZE 2 +#define JMP_LONG_INST_SIZE 5 +/* index in ttt from start of call[sp] and forlcldo to xfer_table index */ +#define CALL_4LCLDO_XFER 2 + +typedef enum +{ + CLEAR, + COMPARE, + INCREMENT, + JUMP, + LOAD, + LOAD_ADDRESS, + PUSH, + PUSH_ADDRESS, + STORE, + TEST +} generic_op; + +void emit_pcrel(generic_op op, unsigned char use_reg); +void emit_trip(generic_op op, oprtype *opr, bool val_output, unsigned char use_reg); +void emit_op_base_offset(generic_op op, short base_reg, int offset, short use_reg); +void emit_op_alit (generic_op op, unsigned char use_reg); +void emit_jmp(short vax_in, short **instp); +unsigned char i386_reg(unsigned char vax_reg); + +union +{ + ModR_M modrm; + unsigned char byte; +} modrm_byte; + +union +{ + SIB sib; + unsigned char byte; +} sib_byte; + +LITREF octabstruct oc_tab[]; /* op-code table */ +LITREF short ttt[]; /* triple templates */ + +static unsigned char code_buf[BUFFERED_CODE_SIZE]; +static unsigned short code_idx; +static int4 jmp_offset, code_reference; +static int force_32; +static int call_4lcldo_variant; /* used in emit_jmp for call[sp] and forlcldo */ + +GBLREF int4 curr_addr; +GBLREF char cg_phase; /* code generation phase */ +GBLDEF uint4 txtrel_cnt; /* count of text relocation records */ + +/* its referenced in ind_code.c */ +GBLDEF int calculated_code_size, generated_code_size; + +void trip_gen(triple *ct) +{ + oprtype **sopr, *opr; /* triple operand */ + oprtype *saved_opr[MAX_ARGS]; + unsigned short oct; + short tp; /* template pointer */ + short *tsp; /* template short pointer */ + triple *ttp; /* temp triple pointer */ + short irep_index; + oprtype *irep_opr; + short *repl, repcnt; /* temp irep ptr */ + int4 off; + error_def (ERR_UNIMPLOP); + error_def (ERR_MAXARGCNT); + + tp = ttt[ct->opcode]; + if (tp <= 0) + { + stx_error(ERR_UNIMPLOP); + return; + } + + code_idx = 0; + code_reference = ct->rtaddr; + oct = oc_tab[ct->opcode].octype; + sopr = &saved_opr[0]; + *sopr++ = &ct->destination; + for (ttp = ct, opr = ttp->operand ; opr < ARRAYTOP(ttp->operand); ) + { + if (opr->oprclass) + { + if (opr->oprclass == TRIP_REF && opr->oprval.tref->opcode == OC_PARAMETER) + { + ttp = opr->oprval.tref; + opr = ttp->operand; + continue; + } + *sopr++ = opr; + if (sopr >= ARRAYTOP(saved_opr)) + rts_error(VARLSTCNT(3) ERR_MAXARGCNT, 1, MAX_ARGS); + } + opr++; + } + *sopr=0; + jmp_offset = 0; + call_4lcldo_variant = 0; + if (oct & OCT_JUMP || ct->opcode == OC_LDADDR || ct->opcode == OC_FORLOOP) + { + if (ct->operand[0].oprval.tref->rtaddr == 0) /* forward reference */ + { + jmp_offset = LONG_JUMP_OFFSET; + assert(cg_phase == CGP_APPROX_ADDR); + } + else + jmp_offset = ct->operand[0].oprval.tref->rtaddr - ct->rtaddr; + + switch (ct->opcode) + { + case OC_CALL: + case OC_FORLCLDO: + case OC_CALLSP: +/* Changes to emit_xfer, emit_base_offset, or emit_jmp may require changes + here since we try to predict how big the call into the xfer_table + and the following jump will be. + There is also an assumption that both the word and long variants + of the opcode will be followed by a jmp with 32 bit offset while + the -BYTE variants will be followed by a BRB with an 8 bit offset. +*/ + tsp = (short *)&ttt[ttt[tp]]; + if (-128 <= tsp[CALL_4LCLDO_XFER] && 127 >= tsp[CALL_4LCLDO_XFER]) + off = jmp_offset - XFER_BYTE_INST_SIZE; + else + off = jmp_offset - XFER_LONG_INST_SIZE; + if (-128 <= (off - BRB_INST_SIZE) && 127 >= (off - BRB_INST_SIZE)) + call_4lcldo_variant = BRB_INST_SIZE; /* used by emit_jmp */ + else + { + call_4lcldo_variant = JMP_LONG_INST_SIZE; /* used by emit_jmp */ + tsp = (short *)&ttt[ttt[tp + 1]]; + if (-128 <= tsp[CALL_4LCLDO_XFER] && 127 >= tsp[CALL_4LCLDO_XFER]) + off = jmp_offset - XFER_BYTE_INST_SIZE; + else + off = jmp_offset - XFER_LONG_INST_SIZE; + if (-32768 > (off - JMP_LONG_INST_SIZE) && + 32767 < (off - JMP_LONG_INST_SIZE)) + tsp = (short *)&ttt[ttt[tp + 2]]; + } + break; + case OC_JMP: + case OC_JMPEQU: + case OC_JMPGEQ: + case OC_JMPGTR: + case OC_JMPLEQ: + case OC_JMPNEQ: + case OC_JMPLSS: + case OC_JMPTSET: + case OC_JMPTCLR: + case OC_LDADDR: + case OC_FORLOOP: + tsp = (short *)&ttt[ttt[tp]]; + break; + default: + GTMASSERT; + break; + } + } + else if (oct & OCT_COERCE) + { + switch (oc_tab[ct->operand[0].oprval.tref->opcode].octype & (OCT_VALUE | OCT_BOOL)) + { + case OCT_MVAL: + tp = ttt[tp]; + break; + case OCT_MINT: + tp = ttt[tp + 3]; + break; + case OCT_BOOL: + tp = ttt[tp + 4]; + break; + default: + GTMASSERT; + break; + } + tsp = (short *)&ttt[tp]; + } + else + tsp = (short *)&ttt[tp]; + + for (; *tsp != VXT_END; ) + { + if (*tsp == VXT_IREPAB || *tsp == VXT_IREPL) + { + repl = tsp; + repl += 2; + repcnt = *repl++; + assert(repcnt != 1); + for (irep_index = repcnt, irep_opr = &ct->operand[1]; irep_index > 2; --irep_index) + { + assert (irep_opr->oprclass == TRIP_REF); + irep_opr = &irep_opr->oprval.tref->operand[1]; + } + + if (irep_opr->oprclass == TRIP_REF) + { + repl = tsp; + do + { + tsp = repl; + tsp = emit_vax_inst(tsp, &saved_opr[0], --sopr); + } while (sopr > &saved_opr[repcnt]); + } + else + { + sopr = &saved_opr[repcnt]; + tsp = repl; + } + } + else + { + assert(*tsp > 0 && *tsp <= 511); + tsp = emit_vax_inst(tsp, &saved_opr[0], sopr); + }/* else */ + }/* for */ +} + + +short *emit_vax_inst(short *inst, oprtype **fst_opr, oprtype **lst_opr) + /* fst_opr and lst_opr are triple operands */ +{ + short sav_in; + bool oc_int; + int4 cnt; + oprtype *opr; + triple *ct; + + code_idx = 0; + force_32 = 0; + + switch (cg_phase) + { + case CGP_ADDR_OPT: + case CGP_APPROX_ADDR: + case CGP_MACHINE: + switch ((sav_in = *inst++)) + { + case VXI_BEQL: + case VXI_BGEQ: + case VXI_BGTR: + case VXI_BLEQ: + case VXI_BLSS: + case VXI_BNEQ: + case VXI_BRB: + case VXI_BRW: + emit_jmp(sav_in, &inst); + break; + case VXI_BLBC: + case VXI_BLBS: + assert (*inst == VXT_REG); + inst++; + inst++; + emit_xfer(4*xf_dt_get); + code_buf[code_idx++] = I386_INS_CMP_eAX_Iv; + *((int4 *)&code_buf[code_idx]) = 0; + code_idx += SIZEOF(int4); + if (sav_in == VXI_BLBC) + emit_jmp(VXI_BEQL, &inst); + else + { + assert (sav_in == VXI_BLBS); + emit_jmp(VXI_BNEQ, &inst); + } + break; + case VXI_BICB2: + case VXI_BISB2: + assert (*inst == VXT_LIT); + inst++; + assert (*inst == 1); + inst++; + assert (*inst == VXT_REG); + inst++; + inst++; + if (sav_in == VXI_BICB2) + emit_xfer(4*xf_dt_false); + else + { + assert (sav_in == VXI_BISB2); + emit_xfer(4*xf_dt_true); + } + break; + case VXI_CALLS: + oc_int = TRUE; + if (*inst == VXT_LIT) + { + inst++; + cnt = (int4) *inst++; + } + else + { + assert(*inst == VXT_VAL); + inst++; + opr = *(fst_opr + *inst); + assert (opr->oprclass == TRIP_REF); + ct = opr->oprval.tref; + if (ct->destination.oprclass) + { + opr = &ct->destination; + } + if (opr->oprclass == TRIP_REF) + { + assert(ct->opcode == OC_ILIT); + cnt = ct->operand[0].oprval.ilit; + if (cnt >= -128 && cnt <= 127) + { + code_buf[code_idx++] = I386_INS_PUSH_Ib; + code_buf[code_idx++] = cnt & 0xff; + } + else + { + code_buf[code_idx++] = I386_INS_PUSH_Iv; + *((int4 *)&code_buf[code_idx]) = cnt; + code_idx += SIZEOF(int4); + } + cnt++; + inst++; + } + else + { + assert(opr->oprclass == TINT_REF); + oc_int = FALSE; + opr = *(fst_opr + *inst++); + emit_trip(PUSH, opr, TRUE, 0); + } + } + assert (*inst == VXT_XFER); + inst++; + emit_xfer(*inst++); + if (oc_int) + { + if (cnt) + { + code_buf[code_idx++] = I386_INS_LEA_Gv_M; + emit_base_offset(I386_REG_ESP, I386_REG_ESP, 4*cnt); + } + } + else + { + emit_trip(LOAD, opr, TRUE, I386_REG_EDX); + + code_buf[code_idx++] = I386_INS_LEA_Gv_M; + emit_base_offset(I386_REG_ESP, I386_REG_ESP, 4); + } + break; + case VXI_CLRL: + assert (*inst == VXT_VAL); + inst++; + emit_trip(CLEAR, *(fst_opr + *inst++), TRUE, 0); + break; + case VXI_CMPL: + assert (*inst == VXT_VAL); + inst++; + emit_trip(LOAD, *(fst_opr + *inst++), TRUE, I386_REG_EDX); + assert (*inst == VXT_VAL); + inst++; + emit_trip(COMPARE, *(fst_opr + *inst++), TRUE, I386_REG_EDX); + break; + case VXI_INCL: + assert (*inst == VXT_VAL); + inst++; + emit_trip(INCREMENT, *(fst_opr + *inst++), TRUE, 0); + break; + case VXI_JMP: + if (*inst == VXT_VAL) + { + inst++; + emit_trip(JUMP, *(fst_opr + *inst++), FALSE, 0); + } + else + { + emit_jmp(sav_in, &inst); + } + break; + case VXI_JSB: + assert (*inst == VXT_XFER); + inst++; + emit_xfer(*inst++); + break; + case VXI_MOVAB: + if (*inst == VXT_JMP) + { + inst += 2; + emit_pcrel(LOAD_ADDRESS, I386_REG_EAX); + assert (*inst == VXT_ADDR); + inst++; + emit_trip(STORE, *(fst_opr + *inst++), FALSE, I386_REG_EAX); + } + else if (*inst == VXT_ADDR || *inst == VXT_VAL) + { + bool addr; + unsigned char reg; + short save_inst; + + addr = (*inst == VXT_VAL); + inst++; + save_inst = *inst++; + assert (*inst == VXT_REG); + inst++; + reg = ((*inst++ & 0x01) ? I386_REG_EDX : I386_REG_EAX); /* r0 and r1 are only ones used */ + emit_trip(LOAD_ADDRESS, *(fst_opr + save_inst), addr, reg); + } + else + GTMASSERT; + break; + case VXI_MOVC3: + assert (*inst == VXT_LIT); + inst += 2; + assert(*inst == VXT_VAL); + inst++; + code_buf[code_idx++] = I386_INS_PUSH_eSI; + code_buf[code_idx++] = I386_INS_PUSH_eDI; + + emit_trip(LOAD_ADDRESS, *(fst_opr + *inst++), TRUE, I386_REG_ECX); + + assert(*inst == VXT_VAL); + inst++; + emit_trip(LOAD_ADDRESS, *(fst_opr + *inst++), TRUE, I386_REG_EDI); + + code_buf[code_idx++] = I386_INS_MOV_Gv_Ev; + modrm_byte.modrm.reg_opcode = I386_REG_ESI; + modrm_byte.modrm.mod = I386_MOD32_REGISTER; + modrm_byte.modrm.r_m = I386_REG_ECX; + code_buf[code_idx++] = modrm_byte.byte; + + code_buf[code_idx++] = I386_INS_MOV_eCX; + *((int4 *)&code_buf[code_idx]) = (int4)SIZEOF(mval); + code_idx += SIZEOF(int4); + + code_buf[code_idx++] = I386_INS_REP_E_Prefix; + code_buf[code_idx++] = I386_INS_MOVSB_Xb_Yb; + + code_buf[code_idx++] = I386_INS_POP_eDI; + code_buf[code_idx++] = I386_INS_POP_eSI; + break; + case VXI_MOVL: + if (*inst == VXT_REG) + { + inst++; + if (*inst > 0x5f) /* OC_CURRHD */ /* any mode >= 6 (deferred), any register */ + { + inst++; + assert (*inst == VXT_ADDR); + inst++; + + emit_xfer(4*xf_get_msf); + emit_op_base_offset(LOAD, I386_REG_EAX, 0, I386_REG_EAX); + emit_trip(STORE, *(fst_opr + *inst++), FALSE, I386_REG_EAX); + } + else + { + bool addr; + + assert (*inst == 0x50); /* register mode: R0 */ + inst++; + if (*inst == VXT_VAL || *inst == VXT_ADDR) + { + addr = (*inst == VXT_VAL); + inst++; + emit_trip(STORE, *(fst_opr + *inst++), addr, I386_REG_EAX); + } + else if (*inst == VXT_REG) + { + unsigned char reg; + + inst++; + if ((*inst & 0x0f) == 10) /* VAX $TEST */ + { + code_buf[code_idx++] = I386_INS_PUSH_eAX; + emit_xfer(4*xf_dt_store); + code_buf[code_idx++] = I386_INS_POP_eAX; + } + else + { + code_buf[code_idx++] = I386_INS_MOV_Ev_Gv; + modrm_byte.modrm.reg_opcode = I386_REG_EAX; + modrm_byte.modrm.mod = I386_MOD32_REGISTER; + modrm_byte.modrm.r_m = i386_reg(*inst); + code_buf[code_idx++] = modrm_byte.byte; + } + inst++; + } + else + GTMASSERT; + } + } + else if (*inst == VXT_VAL) + { + inst++; + emit_trip(LOAD, *(fst_opr + *inst++), TRUE, I386_REG_EDX); + assert (*inst == VXT_REG); + inst++; + assert (*inst == 0x51); /* register mode: R1 */ + inst++; + } + else + GTMASSERT; + break; + case VXT_IREPAB: + assert (*inst == VXT_VAL); + inst += 2; + emit_trip(PUSH_ADDRESS, *lst_opr, TRUE, 0); + break; + case VXI_PUSHAB: + if (*inst == VXT_JMP) + { + inst += 2; + emit_pcrel(PUSH_ADDRESS, 0); + } + else if (*inst == VXT_VAL) + { + inst++; + emit_trip(PUSH_ADDRESS, *(fst_opr + *inst++), TRUE, 0); + } + else + GTMASSERT; + break; + case VXT_IREPL: + assert (*inst == VXT_VAL); + inst += 2; + emit_trip(PUSH, *lst_opr, TRUE, 0); + break; + case VXI_PUSHL: + if (*inst == VXT_LIT) + { + int4 lit; + + inst++; + lit = *inst++; + if (lit >= -128 && lit <= 127) + { + code_buf[code_idx++] = I386_INS_PUSH_Ib; + code_buf[code_idx++] = lit & 0xff; + } + else + { + code_buf[code_idx++] = I386_INS_PUSH_Iv; + *((int4 *)&code_buf[code_idx]) = lit; + code_idx += SIZEOF(int4); + } + } + else if (*inst == VXT_ADDR) + { + inst++; + emit_trip(PUSH, *(fst_opr + *inst++), FALSE, 0); + } + else if (*inst == VXT_VAL) + { + inst++; + emit_trip(PUSH, *(fst_opr + *inst++), TRUE, 0); + } + else + GTMASSERT; + break; + case VXI_TSTL: + if (*inst == VXT_VAL) + { + inst++; + emit_trip(TEST, *(fst_opr + *inst++), TRUE, 0); + } + else if (VXT_REG == *inst) + { + inst++; + code_buf[code_idx++] = I386_INS_CMP_eAX_Iv; + assert(I386_REG_EAX == i386_reg(*inst)); /* VAX R0 */ + inst++; + *((int4 *)&code_buf[code_idx]) = 0; /* 32 bit immediate 0 */ + code_idx += SIZEOF(int4); + } + else + GTMASSERT; + break; + default: + GTMASSERT; + } + break; + default: + GTMASSERT; + break; + } + assert (code_idx < BUFFERED_CODE_SIZE); + if (cg_phase == CGP_MACHINE) + { + generated_code_size += code_idx; + emit_immed ((char *)&code_buf[0], SIZEOF(unsigned char) * code_idx); + } else if (cg_phase != CGP_ASSEMBLY) + { + if (cg_phase == CGP_APPROX_ADDR) + { + calculated_code_size += code_idx; + } + curr_addr += SIZEOF(unsigned char) * code_idx; + } + code_reference += SIZEOF(unsigned char) * code_idx; + jmp_offset -= SIZEOF(unsigned char) * code_idx; + return inst; +} + +/* Changes here or emit_xfer may require changes in trip_gen case for + OC_CALL[SP] and FORLCLDO +*/ +void emit_jmp(short vax_in, short **instp) +{ + + assert (jmp_offset != 0); + jmp_offset -= code_idx * SIZEOF(code_buf[0]); /* size of this particular instruction */ + + assert (**instp == VXT_JMP); + *instp += 1; + assert (**instp == 1); + *instp += 1; + if (jmp_offset == 0) + { + code_buf[code_idx++] = I386_INS_NOP__; + } + else if ((jmp_offset - 2) >= -128 && (jmp_offset - 2) <= 127 && + JMP_LONG_INST_SIZE != call_4lcldo_variant) + { + jmp_offset -= 2; + switch (vax_in) + { + case VXI_BEQL: + code_buf[code_idx++] = I386_INS_JZ_Jb; + break; + case VXI_BGEQ: + code_buf[code_idx++] = I386_INS_JNL_Jb; + break; + case VXI_BGTR: + code_buf[code_idx++] = I386_INS_JNLE_Jb; + break; + case VXI_BLEQ: + code_buf[code_idx++] = I386_INS_JLE_Jb; + break; + case VXI_BLSS: + code_buf[code_idx++] = I386_INS_JL_Jb; + break; + case VXI_BNEQ: + code_buf[code_idx++] = I386_INS_JNZ_Jb; + break; + case VXI_BRB: + case VXI_BRW: + case VXI_JMP: + assert(0 == call_4lcldo_variant || BRB_INST_SIZE == call_4lcldo_variant); + code_buf[code_idx++] = I386_INS_JMP_Jb; + break; + default: + GTMASSERT; + break; + } + code_buf[code_idx++] = jmp_offset & 0xff; + } + else + { + if (vax_in == VXI_BRB || vax_in == VXI_BRW || vax_in == VXI_JMP) + { + assert(0 == call_4lcldo_variant || JMP_LONG_INST_SIZE == call_4lcldo_variant); + jmp_offset -= SIZEOF(int4) + 1; + code_buf[code_idx++] = I386_INS_JMP_Jv; + } + else + { + jmp_offset -= SIZEOF(int4) + 2; + code_buf[code_idx++] = I386_INS_Two_Byte_Escape_Prefix; + switch (vax_in) + { + case VXI_BEQL: + code_buf[code_idx++] = I386_INS_JZ_Jv; + break; + case VXI_BGEQ: + code_buf[code_idx++] = I386_INS_JNL_Jv; + break; + case VXI_BGTR: + code_buf[code_idx++] = I386_INS_JNLE_Jv; + break; + case VXI_BLEQ: + code_buf[code_idx++] = I386_INS_JLE_Jv; + break; + case VXI_BLSS: + code_buf[code_idx++] = I386_INS_JL_Jv; + break; + case VXI_BNEQ: + code_buf[code_idx++] = I386_INS_JNZ_Jv; + break; + default: + GTMASSERT; + break; + } + } + *((int4 *)&code_buf[code_idx]) = jmp_offset; + code_idx += SIZEOF(int4); + } +} + + +void emit_pcrel(generic_op op, unsigned char use_reg) +{ + code_buf[code_idx++] = I386_INS_CALL_Jv; + *((int4 *)&code_buf[code_idx]) = 0; + code_idx += SIZEOF(int4); + + jmp_offset -= code_idx; + code_buf[code_idx++] = I386_INS_POP_eAX; + + emit_op_base_offset(op, I386_REG_EAX, jmp_offset, use_reg); +} + + +GBLREF boolean_t run_time; +GBLREF int4 sa_temps_offset[]; +GBLREF int4 sa_temps[]; +LITREF int4 sa_class_sizes[]; + +void emit_trip(generic_op op, oprtype *opr, bool val_output, unsigned char use_reg) +{ + unsigned char base_reg, temp_reg; + int4 offset, literal; + triple *ct; + + error_def (ERR_UNIMPLOP); + + if (opr->oprclass == TRIP_REF) + { + ct = opr->oprval.tref; + if (ct->destination.oprclass) + { + opr = &ct->destination; + } + /* else lit or error */ + } + + switch (cg_phase) + { + case CGP_ADDR_OPT: + case CGP_APPROX_ADDR: + switch (opr->oprclass) + { + case TRIP_REF: + assert(ct->destination.oprclass == 0); + assert(val_output); + switch (ct->opcode) + { + case OC_LIT: + if (run_time) + { + int4 pc_value_idx; + + switch (op) + { + case LOAD_ADDRESS: + temp_reg = use_reg; + break; + case PUSH: + case PUSH_ADDRESS: + temp_reg = I386_REG_ECX; + break; + default: + rts_error(VARLSTCNT(1) ERR_UNIMPLOP); + break; + } + pc_value_idx = code_idx + 5; + code_idx += 1 + SIZEOF(int4) + 1; + emit_addr(0, (int4)ct->operand[0].oprval.mlit->rt_addr, &offset); + offset -= pc_value_idx; + force_32 = 1; + emit_op_base_offset(op, temp_reg, offset, temp_reg); + force_32 = 0; + } + else + { + emit_op_alit(op, use_reg); + code_idx += SIZEOF(int4); + } + if (cg_phase == CGP_APPROX_ADDR) + txtrel_cnt++; + break; + case OC_CDLIT: + if (cg_phase == CGP_APPROX_ADDR) + define_symbol(GTM_LITERALS, ct->operand[0].oprval.cdlt, 0); + emit_op_alit(op, use_reg); + code_idx += SIZEOF(int4); + break; + case OC_ILIT: + literal = ct->operand[0].oprval.ilit; + switch(op) + { + case COMPARE: /* 1byte(opcode) + 1byte(ModR/M) + 4byte(literal) */ + code_idx += 2 + SIZEOF(int4); + break; + case LOAD: + code_idx += 1 + SIZEOF(int4); + break; + case PUSH: + if (literal >= -128 && literal <= 127) + code_idx += 2; + else + code_idx += 1 + SIZEOF(int4); + break; + default: + rts_error(VARLSTCNT(1) ERR_UNIMPLOP); + break; + } + break; + default: + GTMASSERT; + break; + } + break; + case TINT_REF: + case TVAL_REF: + assert(val_output); + offset = sa_temps_offset[opr->oprclass]; + offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; + if (offset < 0 && offset > 65535) + GTMASSERT; + emit_op_base_offset(op, I386_REG_EDI, offset, use_reg); + break; + case TCAD_REF: + case TVAD_REF: + case TVAR_REF: + offset = sa_temps_offset[opr->oprclass]; + offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; + if (offset < 0 && offset > 65535) + GTMASSERT; + + if (opr->oprclass == TVAR_REF) + base_reg = I386_REG_ESI; + else + base_reg = I386_REG_EDI; + switch (op) + { + case JUMP: + if (val_output) + { + code_idx++; + emit_base_offset(I386_REG_EAX, base_reg, offset); + } + code_idx++; + if (val_output) + emit_base_offset(I386_INS_JMP_Ev, I386_REG_EAX, 0); + else + emit_base_offset(I386_INS_JMP_Ev, base_reg, offset); + break; + case LOAD_ADDRESS: + code_idx++; + emit_base_offset(use_reg, base_reg, offset); + if (opr->oprclass == TVAR_REF) + { + code_idx++; + emit_base_offset(use_reg, use_reg, offsetof(ht_ent_mname, value)); + } + break; + case PUSH: + if (!val_output) + { + code_idx++; + emit_base_offset(I386_INS_PUSH_Ev, base_reg, offset); + } + else + { + code_idx++; + emit_base_offset(I386_REG_ECX, base_reg, offset); + + code_idx++; + emit_base_offset(I386_INS_PUSH_Ev, I386_REG_ECX, 0); + } + break; + case PUSH_ADDRESS: + if (val_output) + { + if (opr->oprclass == TVAR_REF) + { + code_idx++; + emit_base_offset(use_reg, base_reg, offset); + code_idx++; + emit_base_offset(I386_INS_PUSH_Ev, use_reg, offsetof(ht_ent_mname, value)); + } + else + { + code_idx++; + emit_base_offset(I386_INS_PUSH_Ev, base_reg, offset); + } + } + else + { + code_idx++; + emit_base_offset(I386_REG_ECX, base_reg, offset); + code_idx++; + } + break; + case STORE: + if (val_output) + { + if (use_reg == I386_REG_EAX) + temp_reg = I386_REG_EDX; + else + temp_reg = I386_REG_EAX; + code_idx++; + emit_base_offset(temp_reg, base_reg, offset); + } + code_idx++; + if (val_output) + emit_base_offset(use_reg, temp_reg, 0); + else + emit_base_offset(use_reg, base_reg, offset); + break; + default: + rts_error(VARLSTCNT(1) ERR_UNIMPLOP); + break; + } + break; + } + break; + case CGP_MACHINE: + switch (opr->oprclass) + { + case TRIP_REF: + assert(ct->destination.oprclass == 0); + assert(val_output); + switch (ct->opcode) + { + case OC_LIT: + assert(ct->operand[0].oprclass == MLIT_REF); + if (run_time) + { + int4 pc_value_idx; + + switch(op) + { + case LOAD_ADDRESS: + temp_reg = use_reg; + break; + case PUSH: + case PUSH_ADDRESS: + temp_reg = I386_REG_ECX; + break; + default: + rts_error(VARLSTCNT(1) ERR_UNIMPLOP); + break; + } + code_buf[code_idx++] = I386_INS_CALL_Jv; + *((int4 *)&code_buf[code_idx]) = 0; + code_idx += SIZEOF(int4); + + pc_value_idx = code_idx; + code_buf[code_idx++] = I386_INS_POP_eAX + temp_reg; + + emit_addr(0, (int4)ct->operand[0].oprval.mlit->rt_addr, &offset); + offset -= pc_value_idx; + force_32 = 1; + emit_op_base_offset(op, temp_reg, offset, temp_reg); + force_32 = 0; + } + else + { + emit_op_alit(op, use_reg); + emit_addr(code_reference + (code_idx * SIZEOF(unsigned char)), + (int4)ct->operand[0].oprval.mlit->rt_addr, (int4 *)&code_buf[code_idx]); + code_idx += SIZEOF(int4); + } + break; + case OC_CDLIT: + emit_op_alit(op, use_reg); + emit_reference(code_reference + (code_idx * SIZEOF(unsigned char)), + ct->operand[0].oprval.cdlt, (uint4 *)&code_buf[code_idx]); + code_idx += SIZEOF(int4); + break; + case OC_ILIT: + literal = ct->operand[0].oprval.ilit; + switch (op) + { + case COMPARE: /* cmpl $literal,use_reg - 1byte(opcode) + 1byte(ModR/M) + 4byte(literal) */ + code_buf[code_idx++] = I386_INS_Grp1_Ev_Iv_Prefix; + modrm_byte.modrm.reg_opcode = I386_INS_CMP__; + modrm_byte.modrm.mod = I386_MOD32_REGISTER; + modrm_byte.modrm.r_m = use_reg; + code_buf[code_idx++] = modrm_byte.byte; + *((int4 *)&code_buf[code_idx]) = literal; + code_idx += SIZEOF(int4); + break; + case LOAD: + code_buf[code_idx++] = I386_INS_MOV_eAX + use_reg; + *((int4 *)&code_buf[code_idx]) = literal; + code_idx += SIZEOF(int4); + break; + case PUSH: + if (literal >= -128 && literal <= 127) + { + code_buf[code_idx++] = I386_INS_PUSH_Ib; + code_buf[code_idx++] = literal & 0xff; + } + else + { + code_buf[code_idx++] = I386_INS_PUSH_Iv; + *((int4 *)&code_buf[code_idx]) = literal; + code_idx += SIZEOF(int4); + } + break; + default: + rts_error(VARLSTCNT(1) ERR_UNIMPLOP); + break; + } + break; + default: + GTMASSERT; + break; + } + break; + case TINT_REF: + case TVAL_REF: + assert(val_output); + offset = sa_temps_offset[opr->oprclass]; + offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; + if (offset < 0 && offset > 65535) + GTMASSERT; + emit_op_base_offset(op, I386_REG_EDI, offset, use_reg); + break; + case TCAD_REF: + case TVAD_REF: + case TVAR_REF: + offset = sa_temps_offset[opr->oprclass]; + offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; + if (offset < 0 && offset > 65535) + GTMASSERT; + if (opr->oprclass == TVAR_REF) + base_reg = I386_REG_ESI; + else + base_reg = I386_REG_EDI; + + switch (op) + { + case JUMP: + assert (use_reg == 0); + if (val_output) + { + code_buf[code_idx++] = I386_INS_MOV_Gv_Ev; + emit_base_offset(I386_REG_EAX, base_reg, offset); + } + code_buf[code_idx++] = I386_INS_Grp5_Prefix; + if (val_output) + emit_base_offset(I386_INS_JMP_Ev, I386_REG_EAX, 0); + else + emit_base_offset(I386_INS_JMP_Ev, base_reg, offset); + break; + case LOAD_ADDRESS: + if (val_output) + code_buf[code_idx++] = I386_INS_MOV_Gv_Ev; + else + code_buf[code_idx++] = I386_INS_LEA_Gv_M; + emit_base_offset(use_reg, base_reg, offset); + if (opr->oprclass == TVAR_REF) + { + code_buf[code_idx++] = I386_INS_MOV_Gv_Ev; + emit_base_offset(use_reg, use_reg, offsetof(ht_ent_mname, value)); + } + break; + case PUSH: + if (val_output) + { + code_buf[code_idx++] = I386_INS_MOV_Gv_Ev; + emit_base_offset(I386_REG_ECX, base_reg, offset); + + code_buf[code_idx++] = I386_INS_Grp5_Prefix; + emit_base_offset(I386_INS_PUSH_Ev, I386_REG_ECX, 0); + } + else + { + code_buf[code_idx++] = I386_INS_Grp5_Prefix; + emit_base_offset(I386_INS_PUSH_Ev, base_reg, offset); + } + break; + case PUSH_ADDRESS: + if (val_output) + { + if (opr->oprclass == TVAR_REF) + { + code_buf[code_idx++] = I386_INS_MOV_Gv_Ev; + emit_base_offset(use_reg, base_reg, offset); + code_buf[code_idx++] = I386_INS_Grp5_Prefix; + emit_base_offset(I386_INS_PUSH_Ev, use_reg, offsetof(ht_ent_mname, value)); + } + else + { + code_buf[code_idx++] = I386_INS_Grp5_Prefix; + emit_base_offset(I386_INS_PUSH_Ev, base_reg, offset); + } + } + else + { + code_buf[code_idx++] = I386_INS_LEA_Gv_M; + emit_base_offset(I386_REG_ECX, base_reg, offset); + + code_buf[code_idx++] = I386_INS_PUSH_eCX; + } + break; + case STORE: + if (val_output) + { + if (use_reg == I386_REG_EAX) + temp_reg = I386_REG_EDX; + else + temp_reg = I386_REG_EAX; + assert(temp_reg != use_reg); + code_buf[code_idx++] = I386_INS_MOV_Gv_Ev; + emit_base_offset(temp_reg, base_reg, offset); + } + code_buf[code_idx++] = I386_INS_MOV_Ev_Gv; + if (val_output) + emit_base_offset(use_reg, temp_reg, 0); + else + emit_base_offset(use_reg, base_reg, offset); + break; + default: + rts_error(VARLSTCNT(1) ERR_UNIMPLOP); + break; + } + break; + default: + GTMASSERT; + break; + } + break; + default: + GTMASSERT; + break; + } +} + + +/* Changes here, emit_base_offset, or emit_jmp may require changes in trip_gen case for + OC_CALL[SP] and FORLCLDO +*/ +void emit_xfer(short xfer) +{ + + code_buf[code_idx++] = I386_INS_Grp5_Prefix; + emit_base_offset(I386_INS_CALL_Ev, I386_REG_EBX, (int4)xfer); +} + + +void emit_op_base_offset(generic_op op, short base_reg, int offset, short use_reg) +{ + + error_def (ERR_UNIMPLOP); + + switch (op) + { + case CLEAR: + code_buf[code_idx++] = I386_INS_MOV_Ev_Iv; + emit_base_offset(0, base_reg, offset); + *((int4 *)&code_buf[code_idx]) = 0; + code_idx += SIZEOF(int4); + break; + case COMPARE: + code_buf[code_idx++] = I386_INS_CMP_Gv_Ev; + emit_base_offset(use_reg, base_reg, offset); + break; + case INCREMENT: + code_buf[code_idx++] = I386_INS_Grp5_Prefix; + emit_base_offset(I386_INS_INC_Ev, base_reg, offset); + break; + case LOAD: + code_buf[code_idx++] = I386_INS_MOV_Gv_Ev; + emit_base_offset(use_reg, base_reg, offset); + break; + case LOAD_ADDRESS: + code_buf[code_idx++] = I386_INS_LEA_Gv_M; + emit_base_offset(use_reg, base_reg, offset); + break; + case PUSH: + code_buf[code_idx++] = I386_INS_Grp5_Prefix; + emit_base_offset(I386_INS_PUSH_Ev, base_reg, offset); + break; + case PUSH_ADDRESS: + code_buf[code_idx++] = I386_INS_LEA_Gv_M; + emit_base_offset(use_reg, base_reg, offset); + code_buf[code_idx++] = I386_INS_PUSH_eAX + use_reg; + break; + case STORE: + code_buf[code_idx++] = I386_INS_MOV_Ev_Gv; + emit_base_offset(use_reg, base_reg, offset); + break; + case TEST: + code_buf[code_idx++] = I386_INS_Grp1_Ev_Ib_Prefix; + emit_base_offset(I386_INS_CMP__, base_reg, offset); + code_buf[code_idx++] = 0; + break; + default: + rts_error(VARLSTCNT(1) ERR_UNIMPLOP); + break; + } +} + + +/* Changes here, emit_base_offset, or emit_jmp may require changes in trip_gen case for + OC_CALL[SP] and FORLCLDO +*/ +void emit_base_offset (short reg_opcode, short base_reg, int4 offset) +{ + modrm_byte.modrm.reg_opcode = reg_opcode; + + if (offset == 0) + modrm_byte.modrm.mod = I386_MOD32_BASE; + else if ((offset >= -128 && offset <= 127) && force_32 == 0) + modrm_byte.modrm.mod = I386_MOD32_BASE_DISP_8; + else + modrm_byte.modrm.mod = I386_MOD32_BASE_DISP_32; + + if (base_reg == I386_REG_ESP || (base_reg == I386_REG_EBP && offset == 0)) + { + modrm_byte.modrm.r_m = I386_REG_SIB_FOLLOWS; + code_buf[code_idx++] = modrm_byte.byte; + + sib_byte.sib.base = base_reg; + sib_byte.sib.ss = I386_SS_TIMES_1; + sib_byte.sib.index = I386_REG_NO_INDEX; + code_buf[code_idx++] = sib_byte.byte; + } + else + { + modrm_byte.modrm.r_m = base_reg; + code_buf[code_idx++] = modrm_byte.byte; + } + + if (offset == 0) + ; + else if ((offset >= -128 && offset <= 127) && force_32 == 0) + code_buf[code_idx++] = offset & 0xff; + else + { + *((int4 *)&code_buf[code_idx]) = offset; + code_idx += SIZEOF(int4); + } +} + + +void emit_op_alit (generic_op op, unsigned char use_reg) +{ + error_def (ERR_UNIMPLOP); + + switch (op) + { + case LOAD_ADDRESS: + code_buf[code_idx++] = I386_INS_MOV_eAX + use_reg; + break; + case PUSH: + code_buf[code_idx++] = I386_INS_Grp5_Prefix; + modrm_byte.modrm.reg_opcode = I386_INS_PUSH_Ev; + modrm_byte.modrm.mod = I386_MOD32_BASE; + modrm_byte.modrm.r_m = I386_REG_disp32_NO_BASE; + code_buf[code_idx++] = modrm_byte.byte; + break; + case PUSH_ADDRESS: + code_buf[code_idx++] = I386_INS_PUSH_Iv; + break; + default: + rts_error(VARLSTCNT(1) ERR_UNIMPLOP); + break; + } +} + + +unsigned char i386_reg(unsigned char vax_reg) +{ + unsigned char reg; + + switch (vax_reg & 0xf) /* mask out VAX register mode field */ + { + case 0: reg = I386_REG_EAX; break; + case 1: reg = I386_REG_EDX; break; + case 8: reg = I386_REG_ESI; break; + case 9: reg = I386_REG_EDI; break; + case 11: reg = I386_REG_EBX; break; + default: + GTMASSERT; + break; + } + + return reg; +} diff --git a/sr_i386/emit_code.h b/sr_i386/emit_code.h new file mode 100644 index 0000000..4bd43ed --- /dev/null +++ b/sr_i386/emit_code.h @@ -0,0 +1,20 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef EMIT_CODE_INCLUDED +#define EMIT_CODE_INCLUDED + +void trip_gen(triple *ct); +short *emit_vax_inst(short *inst, oprtype **fst_opr, oprtype **lst_opr); +void emit_xfer(short xfer); +void emit_base_offset (short reg_opcode, short base_reg, int4 offset); + +#endif diff --git a/sr_i386/error.si b/sr_i386/error.si new file mode 100644 index 0000000..97a3b83 --- /dev/null +++ b/sr_i386/error.si @@ -0,0 +1,75 @@ +################################################################# +# # +# Copyright 2001, 2007 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + + .sbttl error.si +# PAGE + +#----------------------------------------------- +# Mumps error condition handler macros +#----------------------------------------------- +.ifdef cygwin +# will need another 8 to save sigmasks +chnd_size = 220 +.else +chnd_size = 168 +.endif +chnd_save_active = 0 +chnd_ch_active = 4 +chnd_ch = 8 +chnd_jmp = 12 + +.data +.extern ctxt +.extern active_ch + +.text +.ifdef cygwin +# on cygwin, sigsetjmp is a macro which calls sigprocmask then setjmp +.extern _setjmp +.else +# setjmp is really __sigsetjmp(env,0) +.extern __sigsetjmp +.endif + + .sbttl error.si ESTABLISH +.macro ESTABLISH x, label + addl $chnd_size,ctxt # ctxt++ + movl ctxt,%eax + movl active_ch,%edx # ctxt->save_active_ch = active_ch + movl %edx,chnd_save_active(%eax) + movl $0,chnd_ch_active(%eax) # ctxt->ch_active = FALSE + movl %eax,active_ch # active_ch = ctxt + movl $\x,chnd_ch(%eax) # ctxt->ch = x + addl $chnd_jmp,%eax # setjmp(ctxt->jmp) +.ifndef cygwin + pushl $0 +.endif + pushl %eax +.ifdef cygwin + call _setjmp + addl $4,%esp +.else + call __sigsetjmp + addl $8,%esp +.endif + incl %eax + jne \label + REVERT + jmp return +\label: +.endm + + .sbttl error.si REVERT +.macro REVERT + movl ctxt,%eax # active_ch = ctxt->save_active_c + movl chnd_save_active(%eax),%eax + movl %eax,active_ch + subl $chnd_size,ctxt # ctxt-- +.endm diff --git a/sr_i386/find_line_call.c b/sr_i386/find_line_call.c new file mode 100644 index 0000000..f5aae9e --- /dev/null +++ b/sr_i386/find_line_call.c @@ -0,0 +1,93 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "xfer_enum.h" +#include "i386.h" +#include "rtnhdr.h" /* Needed by zbreak.h */ +#include "zbreak.h" + +zb_code *find_line_call(void *addr) +{ + unsigned char *call_addr; + union + { + ModR_M modrm; + unsigned char byte; + } modrm_byte; + + call_addr = (unsigned char *)addr; + modrm_byte.byte = *(call_addr + 1); + if (*call_addr == I386_INS_Grp5_Prefix && modrm_byte.modrm.reg_opcode == I386_INS_CALL_Ev) + { + call_addr++; + assert (modrm_byte.modrm.r_m == I386_REG_EBX); + call_addr++; + if (modrm_byte.modrm.mod == I386_MOD32_BASE_DISP_8) + { + if (*call_addr == xf_linestart * SIZEOF(int4) || + *call_addr == xf_zbstart * SIZEOF(int4)) + return (zb_code *)call_addr; + + call_addr++; + } + else + { + assert (modrm_byte.modrm.mod == I386_MOD32_BASE_DISP_32); + if (*((int4 *)call_addr) != xf_isformal * SIZEOF(int4)) + return (zb_code *)addr; + + call_addr += SIZEOF(int4); + } + } + + modrm_byte.byte = *(call_addr + 1); + if (*call_addr == I386_INS_PUSH_Ib || *call_addr == I386_INS_PUSH_Iv) + { + while (*call_addr == I386_INS_PUSH_Ib || *call_addr == I386_INS_PUSH_Iv) + { + if (*call_addr == I386_INS_PUSH_Ib) + call_addr += 1 + SIZEOF(unsigned char); + else + { + assert (*call_addr == I386_INS_PUSH_Iv); + call_addr += 1 + SIZEOF(int4); + } + } + modrm_byte.byte = *(call_addr + 1); + if (*call_addr++ != I386_INS_Grp5_Prefix || modrm_byte.modrm.reg_opcode != I386_INS_CALL_Ev) + return (zb_code *)addr; + + assert (modrm_byte.modrm.mod == I386_MOD32_BASE_DISP_8 || modrm_byte.modrm.mod == I386_MOD32_BASE_DISP_32); + assert (modrm_byte.modrm.r_m == I386_REG_EBX); + call_addr++; + if (modrm_byte.modrm.mod == I386_MOD32_BASE_DISP_8) + { + if (*call_addr != xf_linefetch * SIZEOF(int4) && + *call_addr != xf_zbfetch * SIZEOF(int4)) + return (zb_code *)addr; + } + } + else if (*call_addr == I386_INS_Grp5_Prefix && modrm_byte.modrm.reg_opcode == I386_INS_CALL_Ev) + { + call_addr++; + assert (modrm_byte.modrm.mod == I386_MOD32_BASE_DISP_8 || modrm_byte.modrm.mod == I386_MOD32_BASE_DISP_32); + assert (modrm_byte.modrm.r_m == I386_REG_EBX); + call_addr++; + if (modrm_byte.modrm.mod == I386_MOD32_BASE_DISP_8) + { + if (*call_addr != xf_linestart * SIZEOF(int4) && + *call_addr != xf_zbstart * SIZEOF(int4)) + return (zb_code *)addr; + } + } + return (zb_code *)call_addr; +} diff --git a/sr_i386/follow.s b/sr_i386/follow.s new file mode 100644 index 0000000..f641c9e --- /dev/null +++ b/sr_i386/follow.s @@ -0,0 +1,36 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title follow.s + .sbttl follow +.include "linkage.si" + +# .386 +# .MODEL FLAT, C + + .text +.extern op_follow + +# PUBLIC follow +ENTRY follow + movl 4(%esp),%eax + movl 8(%esp),%edx + call op_follow + jle l1 + movl $1,%eax + ret + +l1: movl $0,%eax + ret +# follow ENDP + +# END diff --git a/sr_i386/g_msf.si b/sr_i386/g_msf.si new file mode 100644 index 0000000..ad00b43 --- /dev/null +++ b/sr_i386/g_msf.si @@ -0,0 +1,91 @@ +################################################################# +# # +# Copyright 2001, 2011 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + + .sbttl g_msf.si +# PAGE + +#----------------------------------------------- +# Mumps stack frame manipulation macros +# for the GNU gas i386 assembler version +#----------------------------------------------- +msf_rvector_off = 0 +msf_l_symtab_off = 4 +msf_mpc_off = 8 +msf_ctxt_off = 12 +msf_temps_ptr_off = 16 +msf_vartab_ptr_off = 20 +msf_vartab_len_off = 24 +msf_temp_mvals_off = 26 +msf_old_frame_off = 28 +msf_typ_off = 32 +msf_flags_off = 34 +msf_for_ctrl_stack = 36 + +msf_frame_size = 40 + +SFT_COUNT = 0x01 +SFT_DM = 0x02 +SFT_REP_OP = 0x04 +SFT_ZBRK_ACT = 0x08 +SFT_DEV_ACT = 0x10 +SFT_ZTRAP = 0x20 +SFT_EXTFUN = 0x40 +SFT_ZSTEP_ACT = 0x80 +SFT_ZINTR = 0x100 + +SFF_INDCE = 0x01 +SFF_ZTRAP_ERR = 0x02 +SFF_DEV_ACT_ERR = 0x04 +SFF_CI = 0x08 +SFF_ETRAP_ERR = 0x10 + + .sbttl g_msf.si putframe +.macro putframe + movl frame_pointer,%edx + movl %edi,msf_temps_ptr_off(%edx) + movl %esi,msf_l_symtab_off(%edx) + movl (%esp),%eax + movl %eax,msf_mpc_off(%edx) +.endm + +.extern error_return + + .sbttl g_msf.si getframe +.macro getframe + movl frame_pointer,%edi + movb msf_flags_off(%edi),%dl + andb $SFF_ETRAP_ERR,%dl + jz lab1\@ + call error_return +lab1\@: + movl frame_pointer, %edx + movl msf_temps_ptr_off(%edx),%edi + movl msf_l_symtab_off(%edx),%esi + pushl msf_mpc_off(%edx) +.endm + .sbttl g_msf.si +mrt_jsb = 0 +mrt_src_len = 12 +mrt_src_addr = 16 +mrt_rtn_len = 24 +mrt_rtn_addr = 28 +mrt_var_ptr = 32 +mrt_var_len = 36 +mrt_lab_ptr = 40 +mrt_lab_len = 44 +mrt_lnr_ptr = 48 +mrt_lnr_len = 52 +mrt_ptxt_ptr = 56 +mrt_checksum = 60 +mrt_compiler_qlf = 64 +mrt_oldr_ptr = 68 +mrt_curr_ptr = 72 +mrt_tmp_mv = 76 +mrt_tmp_sz = 78 diff --git a/sr_i386/incr_link.c b/sr_i386/incr_link.c new file mode 100644 index 0000000..dd7faf5 --- /dev/null +++ b/sr_i386/incr_link.c @@ -0,0 +1,441 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_unistd.h" +#include "gtm_stdio.h" +#include "gtm_string.h" +#include + +#include "rtnhdr.h" +#include "compiler.h" +#include "urx.h" +#include "objlabel.h" /* needed for masscomp.h */ +#include "masscomp.h" +#include "gtmio.h" +#include "incr_link.h" +#include "min_max.h" /* MIDENT_CMP needs MIN */ +#include "cmd_qlf.h" /* needed for CQ_UTF8 */ +#include "gtm_text_alloc.h" + +/* INCR_LINK - read and process a mumps object module. Link said module to + currently executing image */ + +LITREF char gtm_release_name[]; +LITREF int4 gtm_release_name_len; + +static char *code; +GBLREF mident_fixed zlink_mname; +GBLREF unsigned char *zl_lab_err; +GBLREF boolean_t gtm_utf8_mode; + +#define RELREAD 50 /* number of relocation entries to buffer */ +typedef struct res_list_struct +{ + struct res_list_struct *next, *list; + unsigned int addr, symnum; +} res_list; + +void res_free(res_list *root); +bool addr_fix(int file, struct exec *fhead, urx_rtnref *urx_lcl, rhdtyp *code); +void zl_error(int4 file, int4 err, int4 err2, int4 len, char *addr); + +bool incr_link(int file_desc) +{ + rhdtyp *hdr, *old_rhead; + int code_size, save_errno, cnt; + int4 rhd_diff,lab_miss_off,*olnt_ent,*olnt_top, read_size; + char *literal_ptr; + var_tabent *curvar; + char module_name[SIZEOF(mident_fixed)]; + lab_tabent *lbt_ent, *lbt_bot, *lbt_top, *olbt_ent, *olbt_bot, *olbt_top, *curlab; + urx_rtnref urx_lcl_anchor; + int order; + struct exec file_hdr; + + error_def(ERR_INVOBJ); + error_def(ERR_LOADRUNNING); + error_def(ERR_TEXT); + + urx_lcl_anchor.len = 0; + urx_lcl_anchor.addr = 0; + urx_lcl_anchor.lab = 0; + urx_lcl_anchor.next = 0; + code = NULL; + + DOREADRL(file_desc, &file_hdr, SIZEOF(file_hdr), read_size); + if (read_size != SIZEOF(file_hdr)) + { + if (-1 == read_size) + { + save_errno = errno; + zl_error(file_desc, ERR_INVOBJ, ERR_TEXT, strlen(STRERROR(save_errno)), + STRERROR(save_errno)); + } + else + zl_error(file_desc, ERR_INVOBJ, ERR_TEXT, RTS_ERROR_TEXT("reading file header")); + } + else if (OMAGIC != file_hdr.a_magic) + zl_error(file_desc, ERR_INVOBJ, ERR_TEXT, RTS_ERROR_TEXT("bad magic")); + else if (OBJ_LABEL != file_hdr.a_stamp) + return FALSE; /* wrong version */ + + assert (file_hdr.a_bss == 0); + code_size = file_hdr.a_text + file_hdr.a_data; + code = GTM_TEXT_ALLOC(code_size); + DOREADRL(file_desc, code, code_size, read_size); + if (read_size != code_size) + { + if (-1 == read_size) + { + save_errno = errno; + zl_error(file_desc, ERR_INVOBJ, ERR_TEXT, strlen(STRERROR(save_errno)), + STRERROR(save_errno)); + } + else + zl_error(file_desc, ERR_INVOBJ, ERR_TEXT, RTS_ERROR_TEXT("reading code")); + } + hdr = (rhdtyp *)code; + if (memcmp(&hdr->jsb[0], "GTM_CODE", SIZEOF(hdr->jsb))) + zl_error(file_desc, ERR_INVOBJ, ERR_TEXT, RTS_ERROR_TEXT("missing GTM_CODE")); + if ((hdr->compiler_qlf & CQ_UTF8) && !gtm_utf8_mode) + zl_error(file_desc, ERR_INVOBJ, ERR_TEXT, + RTS_ERROR_TEXT("Object compiled with CHSET=UTF-8 which is different from $ZCHSET")); + if (!(hdr->compiler_qlf & CQ_UTF8) && gtm_utf8_mode) + zl_error(file_desc, ERR_INVOBJ, ERR_TEXT, + RTS_ERROR_TEXT("Object compiled with CHSET=M which is different from $ZCHSET")); + literal_ptr = code + file_hdr.a_text; + for (cnt = hdr->vartab_len, curvar = VARTAB_ADR(hdr); cnt; --cnt, ++curvar) + { /* relocate the variable table */ + assert(0 < curvar->var_name.len); + curvar->var_name.addr += (uint4)literal_ptr; + } + for (cnt = hdr->labtab_len, curlab = LABTAB_ADR(hdr); cnt; --cnt, ++curlab) + { /* relocate the label table */ + curlab->lab_name.addr += (uint4)literal_ptr; + } + if (!addr_fix(file_desc, &file_hdr, &urx_lcl_anchor, hdr)) + { + urx_free(&urx_lcl_anchor); + zl_error(file_desc, ERR_INVOBJ, ERR_TEXT, RTS_ERROR_TEXT("address fixup failure")); + } + if (!zlput_rname (hdr)) + { + urx_free(&urx_lcl_anchor); + /* Copy routine name to local variable because zl_error free's it. */ + memcpy(&module_name[0], hdr->routine_name.addr, hdr->routine_name.len); + zl_error(file_desc, 0, ERR_LOADRUNNING, hdr->routine_name.len, &module_name[0]); + } + urx_add (&urx_lcl_anchor); + + old_rhead = (rhdtyp *) hdr->old_rhead_ptr; + lbt_bot = (lab_tabent *) ((char *)hdr + hdr->labtab_ptr); + lbt_top = lbt_bot + hdr->labtab_len; + while (old_rhead) + { + rhd_diff = (char *) hdr - (char *) old_rhead; + lab_miss_off = (char *)(&zl_lab_err) - rhd_diff - (char *) old_rhead; + lbt_ent = lbt_bot; + olnt_ent = (int4 *)((char *) old_rhead + old_rhead->lnrtab_ptr); + olnt_top = olnt_ent + old_rhead->lnrtab_len; + for ( ; olnt_ent < olnt_top ;olnt_ent++) + *olnt_ent = lab_miss_off; + olbt_bot = (lab_tabent *) ((char *) old_rhead + old_rhead->labtab_ptr); + olbt_top = olbt_bot + old_rhead->labtab_len; + for (olbt_ent = olbt_bot; olbt_ent < olbt_top ;olbt_ent++) + { + for (; lbt_ent < lbt_top; lbt_ent++) + { + MIDENT_CMP(&olbt_ent->lab_name, &lbt_ent->lab_name, order); + if (order <= 0) + break; + } + if ((lbt_ent < lbt_top) && !order) + { + olnt_ent = (int4 *)((char *) old_rhead + olbt_ent->lab_ln_ptr); + assert(*olnt_ent == lab_miss_off); + *olnt_ent = *((int4 *) (code + lbt_ent->lab_ln_ptr)); + } + } + old_rhead->src_full_name = hdr->src_full_name; + old_rhead->routine_name = hdr->routine_name; + old_rhead->vartab_len = hdr->vartab_len; + old_rhead->vartab_ptr = hdr->vartab_ptr + rhd_diff; + old_rhead->ptext_ptr = hdr->ptext_ptr + rhd_diff; + old_rhead->current_rhead_ptr = rhd_diff; + old_rhead->temp_mvals = hdr->temp_mvals; + old_rhead->temp_size = hdr->temp_size; + old_rhead = (rhdtyp *) old_rhead->old_rhead_ptr; + } + urx_resolve (hdr, lbt_bot, lbt_top); + return TRUE; +} + +bool addr_fix(int file, struct exec *fhead, urx_rtnref *urx_lcl, rhdtyp *code) +{ + res_list *res_root, *new_res, *res_temp, *res_temp1; + char *symbols, *sym_temp, *sym_temp1, *symtop, *res_addr; + struct relocation_info rel[RELREAD]; + int numrel, rel_read, i, string_size, sym_size; + size_t status; + mident_fixed rtnid, labid; + mstr rtn_str; + rhdtyp *rtn; + lab_tabent *label, *labtop; + bool labsym; + urx_rtnref *urx_rp; + urx_addr *urx_tmpaddr; + + res_root = 0; + numrel = (fhead->a_trsize + fhead->a_drsize) / SIZEOF(struct relocation_info); + if (numrel * SIZEOF(struct relocation_info) != fhead->a_trsize + fhead->a_drsize) + return FALSE; + + for ( ; numrel;) + { + rel_read = numrel < RELREAD ? numrel : RELREAD; + DOREADRC(file, rel, rel_read * SIZEOF(struct relocation_info), status); + if (0 != status) + { + res_free(res_root); + return FALSE; + } + numrel -= rel_read; + for (i = 0; i < rel_read; i++) + { if (rel[i].r_extern) + { new_res = (res_list *) malloc(SIZEOF(*new_res)); + new_res->symnum = rel[i].r_symbolnum; + new_res->addr = rel[i].r_address; + new_res->next = new_res->list = 0; + /* Insert the relocation entry in symbol number order on the unresolved chain */ + if (!res_root) + res_root = new_res; + else + { res_temp = res_root; + res_temp1 = 0; + while (res_temp) + { if (res_temp->symnum >= new_res->symnum) + break; + res_temp1 = res_temp; + res_temp = res_temp->next; + } + if (res_temp) + { if (res_temp->symnum == new_res->symnum) + { new_res->list = res_temp->list; + res_temp->list = new_res; + } + else + { if (res_temp1) + { new_res->next = res_temp1->next; + res_temp1->next = new_res; + } + else + { assert(res_temp == res_root); + new_res->next = res_root; + res_root = new_res; + } + } + } + else + res_temp1->next = new_res; + } + } + else + *(unsigned int *)(((char *)code) + rel[i].r_address) += (unsigned int)code; + } + } + /* All relocations within the routine should have been done, so copy the routine_name */ + assert(code->routine_name.len < SIZEOF(zlink_mname.c)); + memcpy(&zlink_mname.c[0], code->routine_name.addr, code->routine_name.len); + zlink_mname.c[code->routine_name.len] = 0; + if (!res_root) + return TRUE; + + if ((off_t)-1 == lseek(file, (off_t)fhead->a_syms, SEEK_CUR)) + { res_free(res_root); + return FALSE; + } + DOREADRC(file, &string_size, SIZEOF(string_size), status); + if (0 != status) + { + res_free(res_root); + return FALSE; + } + string_size -= SIZEOF(string_size); + symbols = malloc(string_size); + DOREADRC(file, symbols, string_size, status); + if (0 != status) + { + free(symbols); + res_free(res_root); + return FALSE; + } + + /* Match up unresolved entries with the null terminated symbol name entries from the + * symbol text pool we just read in. */ + sym_temp = sym_temp1 = symbols; + symtop = symbols + string_size; + for (i = 0; res_root; i++) + { while (i < res_root->symnum) + { /* Forward symbol space until our symnum index (i) matches the symbol we are processing in res_root */ + while (*sym_temp) + { + if (sym_temp >= symtop) + { + free(symbols); + res_free(res_root); + return FALSE; + } + sym_temp++; + } + sym_temp++; + sym_temp1 = sym_temp; + i++; + } + assert (i == res_root->symnum); + /* Find end of routine name that we care about */ + while (*sym_temp1 != '.' && *sym_temp1) + { if (sym_temp1 >= symtop) + { + free(symbols); + res_free(res_root); + return FALSE; + } + sym_temp1++; + } + sym_size = sym_temp1 - sym_temp; + assert(sym_size <= MAX_MIDENT_LEN); + memcpy(&rtnid.c[0], sym_temp, sym_size); + rtnid.c[sym_size] = 0; + if (rtnid.c[0] == '_') + rtnid.c[0] = '%'; + assert(sym_size != mid_len(&zlink_mname) || 0 != memcmp(&zlink_mname.c[0], &rtnid.c[0], sym_size)); + rtn_str.addr = &rtnid.c[0]; + rtn_str.len = sym_size; + rtn = find_rtn_hdr(&rtn_str); /* Routine already resolved? */ + sym_size = 0; + labsym = FALSE; + if (*sym_temp1 == '.') + { /* If symbol is for a label, find the end of the label name */ + sym_temp1++; + sym_temp = sym_temp1; + while (*sym_temp1) + { if (sym_temp1 >= symtop) + { + free(symbols); + res_free(res_root); + return FALSE; + } + sym_temp1++; + } + sym_size = sym_temp1 - sym_temp; + assert(sym_size <= MAX_MIDENT_LEN); + memcpy(&labid.c[0], sym_temp, sym_size); + labid.c[sym_size] = 0; + if (labid.c[0] == '_') + labid.c[0] = '%'; + labsym = TRUE; + } + sym_temp1++; + sym_temp = sym_temp1; + if (rtn) + { /* The routine part at least is known */ + if (labsym) + { /* Look our target label up in the routines label table */ + label = (lab_tabent *)((char *) rtn + rtn->labtab_ptr); + labtop = label + rtn->labtab_len; + for (; label < labtop && (sym_size != label->lab_name.len || + memcmp(&labid.c[0], label->lab_name.addr, sym_size)); label++) + ; + if (label < labtop) + res_addr = (char *)(label->LABENT_LNR_OFFSET + (char *)rtn); + else + res_addr = 0; + } + else + res_addr = (char *)rtn; + if (res_addr) + { /* The external symbol definition is available. Resolve all references to it */ + res_temp = res_root->next; + while(res_root) + { *(uint4 *)(((char *)code) + res_root->addr) = (unsigned int) res_addr; + res_temp1 = res_root->list; + free(res_root); + res_root = res_temp1; + } + res_root = res_temp; + continue; + } + } + /* This symbol is unknown. Put on the (local) unresolved extern chain -- either for labels or routines */ + urx_rp = urx_putrtn(rtn_str.addr, rtn_str.len, urx_lcl); + res_temp = res_root->next; + while(res_root) + { + if (labsym) + urx_putlab(&labid.c[0], sym_size, urx_rp, ((char *)code) + res_root->addr); + else + { urx_tmpaddr = (urx_addr *) malloc(SIZEOF(urx_addr)); + urx_tmpaddr->next = urx_rp->addr; + urx_tmpaddr->addr = (INTPTR_T *)(((char *)code) + res_root->addr); + urx_rp->addr = urx_tmpaddr; + } + res_temp1 = res_root->list; + free(res_root); + res_root = res_temp1; + } + res_root = res_temp; + } + free(symbols); + return TRUE; +} + +void res_free(res_list *root) +{ + res_list *temp; + + while (root) + { while (root->list) + { temp = root->list->list; + free(root->list); + root->list = temp; + } + temp = root->next; + free(root); + root = temp; + } +} + + +/* ZL_ERROR - perform cleanup and signal errors found in zlinking a mumps object module + * err - an error code that accepts no arguments and + * err2 - an error code that accepts two arguments (!AD) */ +void zl_error(int4 file, int4 err, int4 err2, int4 len, char *addr) +{ + int rc; + + if (code) + { + GTM_TEXT_FREE(code); + code = NULL; + } + CLOSEFILE_RESET(file, rc); /* resets "file" to FD_INVALID */ + if (0 != err && 0 != err2) + rts_error(VARLSTCNT(6) err, 0, err2, 2, len, addr); + else if (0 != err) + rts_error(VARLSTCNT(1) err); + else + { + assert(0 != err2); + rts_error(VARLSTCNT(4) err2, 2, len, addr); + } +} diff --git a/sr_i386/linkage.si b/sr_i386/linkage.si new file mode 100644 index 0000000..21a9485 --- /dev/null +++ b/sr_i386/linkage.si @@ -0,0 +1,29 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# #define SYMBOL_NAME_STR(X) #X +# #define SYMBOL_NAME(X) X +# #ifdef __STDC__ +# #define SYMBOL_NAME_LABEL(X) X##: +# #else +# #define SYMBOL_NAME_LABEL(X) X/**/: +# #endif + +.macro SYMBOL_NAME_LABEL X +\X: +.endm + +.macro ENTRY name +# .globl SYMBOL_NAME \name + .globl \name + .align 16,0x90 + SYMBOL_NAME_LABEL \name +.endm diff --git a/sr_i386/make_cimode.c b/sr_i386/make_cimode.c new file mode 100644 index 0000000..e91a472 --- /dev/null +++ b/sr_i386/make_cimode.c @@ -0,0 +1,81 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "error.h" +#include "rtnhdr.h" +#include "op.h" +#include "i386.h" +#include "inst_flush.h" +#include "gtmci.h" +#include "gtm_text_alloc.h" + +#define CALL_SIZE 5 +#define CODE_SIZE (3 * CALL_SIZE) +#define CODE_LINES 3 + +/* The code created and returned by make_cimode() is executed in the frame GTM$CI at level 1 of + * every nested call-in environment. For every M routine being called-in from C, GTM$CI code + * will setup argument registers/stack and executes the M routine. When the M routine returns + * from its final QUIT, GTM$CI returns to gtm_ci(). make_cimode generates machine equivalents + * for the following operations in that order: + * + * CALL ci_restart :setup register/stack arguments from 'param_list' and transfer control + * to op_extcall/op_extexfun which return only after the M routine finishes and QUITs. + * CALL ci_ret_code :transfer control from the M routine back to C (gtm_ci). Never returns. + * CALL opp_ret :an implicit QUIT although it is never executed. + * + * Before GTM$CI executes, it is assumed that the global 'param_list' has been populated with + * argument/return mval*. + */ +rhdtyp *make_cimode(void) +{ + static rhdtyp *base_address = NULL; + lab_tabent *lbl; + int *lnr; + unsigned char *code; + + if (NULL != base_address) + return base_address; + base_address = (rhdtyp *)GTM_TEXT_ALLOC(SIZEOF(rhdtyp) + CODE_SIZE + SIZEOF(lab_tabent) + CODE_LINES * SIZEOF(int4)); + memset(base_address,0,SIZEOF(rhdtyp) + CODE_SIZE + SIZEOF(lab_tabent) + CODE_LINES * SIZEOF(int4)); + base_address->routine_name.len = STR_LIT_LEN(GTM_CIMOD); + base_address->routine_name.addr = GTM_CIMOD; + base_address->ptext_ptr = SIZEOF(rhdtyp); + base_address->vartab_ptr = + base_address->labtab_ptr = SIZEOF(rhdtyp) + CODE_SIZE; /* hdr + code */ + base_address->lnrtab_ptr = SIZEOF(rhdtyp) + CODE_SIZE + SIZEOF(lab_tabent); + base_address->labtab_len = 1; + base_address->lnrtab_len = CODE_LINES; + code = (unsigned char *) base_address + base_address->ptext_ptr; + *code++ = I386_INS_CALL_Jv; + *((int4 *)code) = (int4)((unsigned char *)ci_restart - (code + SIZEOF(int4))); + code += SIZEOF(int4); + *code++ = I386_INS_CALL_Jv; /* a CALL to return control from M to ci_ret_code() which in turn returns to gtm_ci() */ + *((int4 *)code) = (int4)((unsigned char *)ci_ret_code - (code + SIZEOF(int4))); + code += SIZEOF(int4); + *code++ = I386_INS_JMP_Jv; + *((int4 *)code) = (int4)((unsigned char *)opp_ret - (code + SIZEOF(int4))); + code += SIZEOF(int4); + lbl = (lab_tabent *)((int) base_address + base_address->labtab_ptr); + lbl->lab_ln_ptr = base_address->lnrtab_ptr; + lnr = (int *)((int)base_address + base_address->lnrtab_ptr); + *lnr++ = base_address->ptext_ptr; + *lnr++ = base_address->ptext_ptr; + *lnr++ = base_address->ptext_ptr + 2 * CALL_SIZE; + assert(code - ((unsigned char *)base_address + base_address->ptext_ptr) == CODE_SIZE); + zlput_rname(base_address); + inst_flush(base_address, SIZEOF(rhdtyp) + CODE_SIZE + SIZEOF(lab_tabent) + CODE_LINES * SIZEOF(int4)); + return base_address; +} diff --git a/sr_i386/make_dmode.c b/sr_i386/make_dmode.c new file mode 100644 index 0000000..8523abc --- /dev/null +++ b/sr_i386/make_dmode.c @@ -0,0 +1,67 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "error.h" +#include "rtnhdr.h" +#include "op.h" +#include "i386.h" +#include "inst_flush.h" +#include "dm_setup.h" +#include "gtm_text_alloc.h" + +#define CALL_SIZE 5 +#define CODE_SIZE 3*CALL_SIZE +#define CODE_LINES 3 + +rhdtyp *make_dmode(void) +{ + rhdtyp *base_address; + lab_tabent *lbl; + int *lnr; + unsigned char *code; + /* dummy code + label entry + line entries */ + base_address = (rhdtyp *)GTM_TEXT_ALLOC(SIZEOF(rhdtyp) + CODE_SIZE + SIZEOF(lab_tabent) + CODE_LINES * SIZEOF(int4)); + memset(base_address,0,SIZEOF(rhdtyp) + CODE_SIZE + SIZEOF(lab_tabent) + CODE_LINES*SIZEOF(int4)); + base_address->routine_name.len = STR_LIT_LEN(GTM_DMOD); + base_address->routine_name.addr = GTM_DMOD; + base_address->ptext_ptr = SIZEOF(rhdtyp); + base_address->vartab_ptr = + base_address->labtab_ptr = SIZEOF(rhdtyp) + CODE_SIZE; /* hdr + code */ + base_address->lnrtab_ptr = SIZEOF(rhdtyp) + CODE_SIZE + SIZEOF(lab_tabent); + base_address->labtab_len = 1; + base_address->lnrtab_len = CODE_LINES; + code = (unsigned char *) base_address + base_address->ptext_ptr; + *code++ = I386_INS_CALL_Jv; + *((int4 *)code) = (int4)((unsigned char *)dm_setup - (code + SIZEOF(int4))); + code += SIZEOF(int4); + *code++ = I386_INS_CALL_Jv; /* this should be a CALL to maintain uniformity between transfer to mum_tstart from baseframe + and transfers to mum_tstart from error processing (MUM_TSTART marco in + mdb_condition_handler) */ + *((int4 *)code) = (int4)((unsigned char *)mum_tstart - (code + SIZEOF(int4))); + code += SIZEOF(int4); + *code++ = I386_INS_JMP_Jv; + *((int4 *)code) = (int4)((unsigned char *)opp_ret - (code + SIZEOF(int4))); + code += SIZEOF(int4); + lbl = (lab_tabent *)((int) base_address + base_address->labtab_ptr); + lbl->lab_ln_ptr = base_address->lnrtab_ptr; + lnr = (int *)((int)base_address + base_address->lnrtab_ptr); + *lnr++ = base_address->ptext_ptr; + *lnr++ = base_address->ptext_ptr; + *lnr++ = base_address->ptext_ptr + 2 * CALL_SIZE; + assert(code - ((unsigned char *)base_address + base_address->ptext_ptr) == CODE_SIZE); + zlput_rname(base_address); + inst_flush(base_address, SIZEOF(rhdtyp) + CODE_SIZE + SIZEOF(lab_tabent) + CODE_LINES * SIZEOF(int4)); + return base_address; +} diff --git a/sr_i386/masscomp.h b/sr_i386/masscomp.h new file mode 100644 index 0000000..2297fe4 --- /dev/null +++ b/sr_i386/masscomp.h @@ -0,0 +1,77 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +struct exec { + short a_magic; /* magic number */ + short a_stamp; /* version stamp - RTU 2.0+ uses this - see below */ +uint4 a_text; /* size of text segment */ +uint4 a_data; /* size of initialized data */ +uint4 a_bss; /* size of uninitialized data */ +uint4 a_syms; /* size of symbol table */ +uint4 a_entry; /* entry point */ +uint4 a_trsize; /* size of text relocation */ +uint4 a_drsize; /* size of data relocation */ +}; + +/* + * Format of a relocation datum. + */ +struct relocation_info { + int r_address; /* address which is relocated */ +unsigned int r_symbolnum:24, /* local symbol ordinal */ + r_pcrel:1, /* was relocated pc relative already */ + r_length:2, /* 0=byte, 1=word, 2=int4 */ + r_extern:1, /* does not include value of sym referenced */ + r_pad:4; /* nothing, yet */ +}; + +struct rel_table { + struct rel_table *next, *resolve; + struct relocation_info r; +}; + +/* + * Format of a symbol table entry; this file is included by + * and should be used if you aren't interested the a.out header + * or relocation information. + */ +struct nlist { + int4 n_strx; /* index into file string table */ +unsigned char n_type; /* type flag, i.e. N_TEXT etc; see below */ + char n_other; /* unused */ + short n_desc; /* see */ +uint4 n_value; /* value of this symbol (or sdb offset) */ +}; + +struct sym_table { + struct sym_table *next; + struct nlist n; + struct rel_table *resolve; + unsigned short name_len; + unsigned char name[1]; +}; + +/* + * Simple values for n_type. + */ +#define N_UNDF 0x0 /* undefined */ +#define N_ABS 0x2 /* absolute */ +#define N_TEXT 0x4 /* text */ +#define N_DATA 0x6 /* data */ +#define N_BSS 0x8 /* bss */ +#define N_COMM 0x12 /* common (internal to ld) */ +#define N_IPCOMM 0x16 /* initialized private */ +#define N_PCOMM 0x18 /* uninitialized private */ +#define N_FN 0x1f /* file name symbol */ + +#define N_EXT 01 /* external bit, or'ed in */ +#define N_TYPE 0x1e /* mask for all the type bits */ + diff --git a/sr_i386/mint2mval.s b/sr_i386/mint2mval.s new file mode 100644 index 0000000..c4d151d --- /dev/null +++ b/sr_i386/mint2mval.s @@ -0,0 +1,42 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title mint2mval.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .include "mval_def.si" + + .sbttl mint2mval +# PAGE + + .text + +# -------------------------------- +# mint2mval.s +# Convert int to mval +# -------------------------------- + +.extern i2mval + +# PUBLIC mint2mval +ENTRY mint2mval + pushl %edx + leal (%eax),%eax + pushl %eax + call i2mval + addl $8,%esp + ret +# mint2mval ENDP + +# END diff --git a/sr_i386/mum_tstart.s b/sr_i386/mum_tstart.s new file mode 100644 index 0000000..faf2d3d --- /dev/null +++ b/sr_i386/mum_tstart.s @@ -0,0 +1,34 @@ +################################################################# +# # +# Copyright 2001, 2002 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + + .TITLE mum_tstart.s + +.include "linkage.si" + .include "g_msf.si" +#comment perhaps + .sbttl mum_tstart + .data +.extern frame_pointer +.extern proc_act_type + + .text +.extern trans_code +.extern inst_flush + +ENTRY mum_tstart + addl $4,%esp # back up over return address + cmpw $0,proc_act_type + je l1 + call trans_code +l1: getframe + leal xfer_table,%ebx + call inst_flush # smw 99/11/24 is this needed + ret diff --git a/sr_i386/mval2bool.s b/sr_i386/mval2bool.s new file mode 100644 index 0000000..77be39e --- /dev/null +++ b/sr_i386/mval2bool.s @@ -0,0 +1,43 @@ +################################################################# +# # +# Copyright 2001, 2008 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title mval2bool.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "mval_def.si" + + .sbttl mval2bool +# PAGE + +# -------------------------------- +# mval2bool.s +# Convert mval to bool +# -------------------------------- +# edx - src. mval + + .text +.extern s2n +.extern underr + +# PUBLIC mval2bool +ENTRY mval2bool + mv_force_defined %edx, l1 + pushl %edx + mv_force_num %edx, skip_conv + popl %edx + cmpl $0,mval_l_m1(%edx) + ret +# mval2bool ENDP + +# END diff --git a/sr_i386/mval2mint.s b/sr_i386/mval2mint.s new file mode 100644 index 0000000..cda2e9c --- /dev/null +++ b/sr_i386/mval2mint.s @@ -0,0 +1,45 @@ +################################################################# +# # +# Copyright 2001, 2008 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title mval2mint.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "mval_def.si" + + .sbttl mval2mint +# PAGE + +# -------------------------------- +# mval2mint.s +# Convert mval to int +# -------------------------------- +# edx - source mval +# eax - destination mval + + .text +.extern mval2i +.extern s2n +.extern underr + +# PUBLIC mval2mint +ENTRY mval2mint + mv_force_defined %edx, l1 + pushl %edx # preserve src + push it as arg + mv_force_num %edx, skip_conv + call mval2i + addl $4,%esp + ret +# mval2mint ENDP + +# END diff --git a/sr_i386/mval2num.s b/sr_i386/mval2num.s new file mode 100644 index 0000000..b3d17a3 --- /dev/null +++ b/sr_i386/mval2num.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001, 2008 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title mval2num.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "mval_def.si" + + .sbttl mval2num +# PAGE + + .text + +.extern n2s +.extern s2n +.extern underr + +# PUBLIC mval2num +ENTRY mval2num + mv_force_defined %edx, l0 + pushl %edx # save in case call s2n + mv_force_num %edx, l1 + popl %edx + mv_force_str_if_num_approx %edx, l2 + ret +# mval2num ENDP + +# END diff --git a/sr_i386/mval_def.si b/sr_i386/mval_def.si new file mode 100644 index 0000000..96841c3 --- /dev/null +++ b/sr_i386/mval_def.si @@ -0,0 +1,245 @@ +################################################################# +# # +# Copyright 2001, 2011 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + + .sbttl mval_def.si +# PAGE + +# ------------------------------------------- +# mval_def.si +# +# This is an include file for +# SCO Unix 80386 masm assembler containing +# the macros for mval-related assembly routines. +# ------------------------------------------- + +mval_v_nm = 0 +mval_v_int = 1 +mval_v_str = 2 +mval_v_num_approx = 3 +mval_v_canonical = 4 +mval_v_sym = 5 +mval_v_sublit = 6 +mval_v_retarg = 7 +mval_v_utflen = 8 +mval_v_aliascont = 9 + +mval_m_nm = 0x001 +mval_m_int_without_nm = 0x002 +mval_m_int = 0x003 +mval_m_str = 0x004 +mval_m_num_approx = 0x008 +mval_m_canonical = 0x010 +mval_m_sym = 0x020 +mval_m_sublit = 0x040 +mval_m_retarg = 0x080 +mval_m_utflen = 0x100 +mval_m_aliascont = 0x200 + +#smw 99/7/12 Now using MS VC 5.0 so the following paragraph should +# be replaced. +# NOTE: The SCO C compiler pads out bit fields to the length of +# the underlying type. For example, a series of "unsigned int" +# bit fields will be padded out to a multiple of 4 bytes, even if +# they require less than one byte total, because the underlying +# type is "int". Similarly, a series of "unsigned short" bit +# fields will be padded out to a multiple of 2 bytes, and a +# series of "unsigned char" will be padded out to a byte boundary. +# Also note the padding is not related to alignment, only to +# total length. +# +#smw 99/10/19 gcc on Linux doesn't do the above +# +# Length of mval in bytes +mval_byte_len = 24 + +# Offsets of type, exp, strlen, stradd, num in mval structure +mval_w_mvtype = 0 +mval_b_exp = 2 +mval_l_strlen = 16 +mval_a_straddr = 20 + +# Address offset of number in mval +mvalnm_offs = 4 +mval_l_m0 = 4 +mval_l_m1 = 8 +mval_esign_mask = 0x080 + +MV_BIAS = 1000 # 10**3 +MANT_LO = 100000000 # 10**8 +MANT_HI = 1000000000 # 10**9 +INT_HI = 1000000 # 10**6 + +# Stringpool structure offsets +base = 0 +free = 4 +top = 8 + +# mvals passed to these macros must be registers + + .sbttl mval_def.si mv_force_defined +# --------------------------------------- +# mv_force_defined(mval, label) +# --------------------------------------- +.macro mv_force_defined mval, label + testw $(mval_m_str+mval_m_nm),mval_w_mvtype(\mval) + jne \label + pushl \mval + call underr + addl $4,%esp + movl %eax, \mval +\label: +.endm + + .sbttl mval_def.si mv_force_str +# --------------------------------------- +# mv_force_str(mval, label) +# --------------------------------------- +.macro mv_force_str mval, label + testw $mval_m_str,mval_w_mvtype(\mval) + jne \label + pushl \mval + call n2s + addl $4,%esp +\label: +.endm + + .sbttl mval_def.si mv_force_num +# --------------------------------------- +# mv_force_num(mval, label) +# --------------------------------------- +.macro mv_force_num mval, label + testw $mval_m_nm,mval_w_mvtype(\mval) + jne \label + pushl \mval + call s2n + addl $4,%esp +\label: +.endm + + .sbttl mval_def.si mv_force_str_if_num_approx +# --------------------------------------- +# mv_force_str_if_num_approx(mval, label) +# --------------------------------------- +.macro mv_force_str_if_num_approx mval, label + testw $mval_m_num_approx,mval_w_mvtype(\mval) + je \label + pushl \mval + call n2s + addl $4,%esp +\label: +.endm + + .sbttl mval_def.si mv_i2mval +# --------------------------------------- +# mv_i2mval(int, mval) +# --------------------------------------- +.macro mv_i2mval int, mval + movw $mval_m_int,mval_w_mvtype(\mval) + movl \int,%eax + imull $MV_BIAS,%eax,%eax + movl %eax,mval_l_m1(\mval) +.endm + + .sbttl mval_def.si mv_if_string +# --------------------------------------- +# mv_if_string(mval,label) +# --------------------------------------- +.macro mv_if_string mval, label + testw $mval_m_str,mval_w_mvtype(\mval) + jne \label +.endm + + .sbttl mval_def.si mv_if_number +# --------------------------------------- +# mv_if_number(mval,label) +# --------------------------------------- +.macro mv_if_number mval, label + testw $mval_m_nm,mval_w_mvtype(\mval) + jne \label +.endm + + .sbttl mval_def.si mv_if_int +# --------------------------------------- +# mv_if_int(mval,label) +# --------------------------------------- +.macro mv_if_int mval, label + testw $mval_m_int_without_nm,mval_w_mvtype(\mval) + jne \label +.endm + + .sbttl mval_def.si mv_if_notstring +# --------------------------------------- +# mv_if_notstring(mval,label) +# --------------------------------------- +.macro mv_if_notstring mval, label + testw $mval_m_str,mval_w_mvtype(\mval) + je \label +.endm + + .sbttl mval_def.si mv_if_notnumber +# --------------------------------------- +# mv_if_notnumber(mval,label) +# --------------------------------------- +.macro mv_if_notnumber mval, label + testw $mval_m_nm,mval_w_mvtype(\mval) + je \label +.endm + + .sbttl mval_def.si mv_if_notint +# --------------------------------------- +# mv_if_notint(mval,label) +# --------------------------------------- +.macro mv_if_notint mval, label + testw $mval_m_int_without_nm,mval_w_mvtype(\mval) + je \label +.endm + + .sbttl mval_def.si mv_if_defined +# --------------------------------------- +# mv_if_defined(mval,label) +# --------------------------------------- +.macro mv_if_defined mval, label + testw $(mval_m_str+mval_m_nm),mval_w_mvtype(\mval) + jne \label +.endm + + .sbttl mval_def.si mv_if_notdefined +# --------------------------------------- +# mv_if_notdefined(mval,label) +# --------------------------------------- +.macro mv_if_notdefined mval, label + testw $(mval_m_str+mval_m_nm),mval_w_mvtype(\mval) + je \label +.endm + + .sbttl mval_def.si mv_if_canonical +# ------------------------------------------------------------- +# WARNING: +# Following macro needs to be supplied with 2 extra labels that +# are used by local branch instructions, tmp_label1 and tmp_label2 +# ------------------------------------------------------------- +# mv_if_canonical(mval,mainlabel, tmp_label1, tmp_label2) +# ------------------------------------------------------------- +.macro mv_if_canonical mval, mainlabel, tmp_label1, tmp_label2 + testw $mval_m_nm,mval_w_mvtype(\mval) + je \tmp_label1 + testw $mval_m_num_approx,mval_w_mvtype(\mval) + jne \tmp_label2 + jmp \mainlabel + +\tmp_label1: + pushl \mval + call val_iscan + addl $4,%esp + cmpl $0,%eax + jne \mainlabel + +\tmp_label2: +.endm diff --git a/sr_i386/obj_file.c b/sr_i386/obj_file.c new file mode 100644 index 0000000..da40b0e --- /dev/null +++ b/sr_i386/obj_file.c @@ -0,0 +1,479 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include + +#include "compiler.h" +#include "rtnhdr.h" +#include "obj_gen.h" +#include "cgp.h" +#include "mdq.h" +#include "cmd_qlf.h" +#include "objlabel.h" /* needed for masscomp.h */ +#include "masscomp.h" +#include "stringpool.h" +#include "parse_file.h" +#include "gtm_fcntl.h" +#include "gtm_unistd.h" +#include "gtmio.h" +#include "mmemory.h" +#include "obj_file.h" + +LITREF char gtm_release_name[]; +LITREF int4 gtm_release_name_len; + +GBLREF mliteral literal_chain; +GBLREF char source_file_name[]; +GBLREF unsigned short source_name_len; + +GBLREF command_qualifier cmd_qlf; +GBLREF mident routine_name; +GBLREF mident module_name; +GBLREF boolean_t run_time; +GBLREF int4 mlmax, mvmax; +GBLREF int4 code_size, lit_addrs, lits_size; + +GBLDEF int4 psect_use_tab[GTM_LASTPSECT]; /* bytes of each psect in this module */ +GBLREF char object_file_name[]; +GBLREF short object_name_len; +GBLREF int object_file_des; + +static short int current_psect; +static char emit_buff[OBJ_EMIT_BUF_SIZE]; /* buffer for emit output */ +static short int emit_buff_used; /* number of chars in emit_buff */ + +GBLREF uint4 txtrel_cnt; +static uint4 cdlits; +static struct rel_table *data_rel, *data_rel_end; +static struct rel_table *text_rel, *text_rel_end; +DEBUG_ONLY(static uint4 txtrel_cnt_in_hdr;) + +error_def(ERR_OBJFILERR); + +void create_object_file(rhdtyp *rhead) +{ + int status; + unsigned char rout_len; + uint4 stat; + char obj_name[SIZEOF(mident_fixed) + 5]; + mstr fstr; + parse_blk pblk; + struct exec hdr; + error_def(ERR_FILEPARSE); + + assert(!run_time); + + memset(&pblk, 0, SIZEOF(pblk)); + pblk.buffer = object_file_name; + pblk.buff_size = MAX_FBUFF; + /* create the object file */ + fstr.len = (MV_DEFINED(&cmd_qlf.object_file) ? cmd_qlf.object_file.str.len : 0); + fstr.addr = cmd_qlf.object_file.str.addr; + rout_len = module_name.len; + memcpy(&obj_name[0], module_name.addr, rout_len); + obj_name[rout_len] = '.'; + obj_name[rout_len + 1] = 'o'; + obj_name[rout_len + 2] = 0; + pblk.def1_size = rout_len + 2; + pblk.def1_buf = obj_name; + status = parse_file(&fstr, &pblk); + if (!(status & 1)) + rts_error(VARLSTCNT(5) ERR_FILEPARSE, 2, fstr.len, fstr.addr, status); + + object_name_len = pblk.b_esl; + object_file_name[object_name_len] = 0; + + OPEN_OBJECT_FILE(object_file_name, O_CREAT | O_RDWR, object_file_des); + if (FD_INVALID == object_file_des) + rts_error(VARLSTCNT(5) ERR_OBJFILERR, 2, object_name_len, object_file_name, errno); + memcpy(&rhead->jsb[0], "GTM_CODE", SIZEOF(rhead->jsb)); + emit_addr((char *)&rhead->src_full_name.addr - (char *)rhead, + (int4)rhead->src_full_name.addr, (int4 *)&rhead->src_full_name.addr); + emit_addr((char *)&rhead->routine_name.addr - (char *)rhead, + (int4)rhead->routine_name.addr, (int4 *)&rhead->routine_name.addr); + txtrel_cnt += 2; + DEBUG_ONLY(txtrel_cnt_in_hdr = txtrel_cnt;) + + set_psect(GTM_CODE, 0); + hdr.a_magic = OMAGIC; + hdr.a_stamp = OBJ_LABEL; + hdr.a_entry = 0; + hdr.a_bss = 0; + hdr.a_text = code_size; + assert(0 == PADLEN(lits_size, NATIVE_WSIZE)); + hdr.a_data = lits_size; /* and pad to even # */ + hdr.a_syms = (mlmax + cdlits) * SIZEOF(struct nlist); + hdr.a_trsize = txtrel_cnt * SIZEOF(struct relocation_info); + hdr.a_drsize = lit_addrs * SIZEOF(struct relocation_info); + emit_immed((char *)&hdr, SIZEOF(hdr)); + memset(psect_use_tab, 0, SIZEOF(psect_use_tab)); + emit_immed((char *)rhead, SIZEOF(*rhead)); +} + +void close_object_file(void) +{ + assert(0 == PADLEN(lits_size, NATIVE_WSIZE)); + resolve_sym(); + output_relocation(); + output_symbol(); + if (emit_buff_used) + buff_emit(); + if ((off_t)-1 == lseek(object_file_des, (off_t)0, SEEK_SET)) + rts_error(VARLSTCNT(5) ERR_OBJFILERR, 2, object_name_len, object_file_name, errno); +} + + +void drop_object_file(void) +{ + int rc; + + if (FD_INVALID != object_file_des) + { + UNLINK(object_file_name); + CLOSEFILE_RESET(object_file_des, rc); /* resets "object_file_des" to FD_INVALID */ + } +} + +GBLREF spdesc stringpool; + +void emit_addr(int4 refaddr, int4 offset, int4 *result) +{ + struct rel_table *newrel; + + if (run_time) + { + unsigned char *ptr; + ptr = stringpool.free; + *result = offset - (int4) ptr; + } else + { *result = offset + code_size; + newrel = (struct rel_table *) mcalloc(SIZEOF(struct rel_table)); + newrel->next = (struct rel_table *) 0; + newrel->resolve = 0; + newrel->r.r_address = refaddr; + newrel->r.r_symbolnum = N_DATA; + newrel->r.r_pcrel = 0; + newrel->r.r_length = 2; + newrel->r.r_extern = 0; + newrel->r.r_pad = 0; + if (!text_rel) + text_rel = text_rel_end = newrel; + else + { text_rel_end->next = newrel; + text_rel_end = newrel; + } + } + return; +} + + +void emit_pidr(int4 refoffset, int4 data_offset, int4 *result) +{ + struct rel_table *newrel; + + assert(!run_time); + refoffset += code_size; + data_offset += code_size; + *result = data_offset; + newrel = (struct rel_table *) mcalloc(SIZEOF(struct rel_table)); + newrel->next = (struct rel_table *)0; + newrel->resolve = 0; + newrel->r.r_address = refoffset; + newrel->r.r_symbolnum = N_DATA; + newrel->r.r_pcrel = 0; + newrel->r.r_length = 2; + newrel->r.r_extern = 0; + newrel->r.r_pad = 0; + if (!data_rel) + data_rel = data_rel_end = newrel; + else + { data_rel_end->next = newrel; + data_rel_end = newrel; + } +} + + +void emit_reference(uint4 refaddr, mstr *name, uint4 *result) +{ + struct sym_table *sym; + struct rel_table *newrel; + + sym = define_symbol(0, name, 0); + assert(sym); + if (sym->n.n_type == (N_TEXT | N_EXT)) + *result = sym->n.n_value; + else + { + newrel = (struct rel_table *) mcalloc(SIZEOF(struct rel_table)); + newrel->next = (struct rel_table *)0; + newrel->resolve = 0; + newrel->r.r_address = refaddr; + newrel->r.r_symbolnum = 0; + newrel->r.r_pcrel = 0; + newrel->r.r_length = 2; + newrel->r.r_extern = 1; + newrel->r.r_pad = 0; + if (!text_rel) + text_rel = text_rel_end = newrel; + else + { text_rel_end->next = newrel; + text_rel_end = newrel; + } + if (sym->resolve) + newrel->resolve = sym->resolve; + sym->resolve = newrel; + *result = 0; + } +} + + +/* + * emit_immed + * + * Args: buffer of executable code, and byte count to be output. + */ + +error_def(ERR_STRINGOFLOW); +void emit_immed(char *source, uint4 size) +{ + short int write; + + if (run_time) + { + if (stringpool.free + size > stringpool.top) + rts_error(VARLSTCNT(1) ERR_STRINGOFLOW); + memcpy(stringpool.free, source, size); + stringpool.free += size; + } else + { while(size > 0) + { + write = SIZEOF(emit_buff) - emit_buff_used; + write = size < write ? size : write; + memcpy(emit_buff + emit_buff_used, source, write); + size -= write; + source += write; + emit_buff_used += write; + psect_use_tab[current_psect] += write; + if (size) + buff_emit(); + } + } +} + + +/* + * buff_emit + * + * Args: buffer pointer, number of bytes to emit + */ + +void buff_emit(void) +{ + uint4 stat; + + if (-1 == write(object_file_des, emit_buff, emit_buff_used)) + rts_error(VARLSTCNT(5) ERR_OBJFILERR, 2, object_name_len, object_file_name, errno); + emit_buff_used = 0; +} + + +void set_psect(unsigned char psect,unsigned char offset) +{ + current_psect = psect; + return; +} + +/* + * define_symbol + * + * Args: psect index, symbol name, symbol value. + * + * Description: Buffers a definition of a global symbol with the + * given name and value in the given psect. + */ + +static struct sym_table *symbols; +struct sym_table *define_symbol(unsigned char psect, mstr *name, int4 value) +{ + int cmp; + struct sym_table *sym, *sym1, *newsym; + + sym = symbols; + sym1 = 0; + while(sym) + { + if ((cmp = memvcmp(name->addr, name->len, &sym->name[0], sym->name_len - 1)) <= 0) + break; + sym1 = sym; + sym = sym->next; + } + if (cmp || !sym) + { newsym = (struct sym_table *) mcalloc(SIZEOF(struct sym_table) + name->len); + newsym->name_len = name->len + 1; + memcpy(&newsym->name[0], name->addr, name->len); + newsym->name[ name->len ] = 0; + newsym->n.n_strx = 0; + newsym->n.n_type = N_EXT; + if (psect == GTM_CODE) + newsym->n.n_type |= N_TEXT; /* if symbol is in GTM_CODE, it is defined */ + else + txtrel_cnt++; + newsym->n.n_other = 0; + newsym->n.n_desc = 0; + newsym->n.n_value = value; + newsym->resolve = 0; + newsym->next = sym; + if (sym1) + sym1->next = newsym; + else + symbols = newsym; + cdlits++; + return 0; + } + if (!(sym->n.n_type & N_TEXT)) + txtrel_cnt++; + return sym; +} + +void resolve_sym(void) +{ + uint4 symnum; + struct sym_table *sym; + struct rel_table *rel; + + symnum = 0; + sym = symbols; + while (sym) + { if (sym->resolve) + { rel = sym->resolve; + while (rel) + { rel->r.r_symbolnum = symnum; + rel = rel->resolve; + } + } + symnum++; + sym = sym->next; + } +} + + +void output_relocation(void) +{ + struct rel_table *rel; + DEBUG_ONLY(int cnt;) + + DEBUG_ONLY(cnt = 0;) + rel = text_rel; + while (rel) + { + emit_immed((char *)&rel->r, SIZEOF(rel->r)); + rel = rel->next; + DEBUG_ONLY(cnt++;) + } + assert(cnt == txtrel_cnt_in_hdr); + + DEBUG_ONLY(cnt = 0;) + rel = data_rel; + while (rel) + { + emit_immed((char *)&rel->r, SIZEOF(rel->r)); + rel = rel->next; + DEBUG_ONLY(cnt++;) + } + assert(cnt == lit_addrs); +} + + +void output_symbol(void) +{ + uint4 string_length; + struct sym_table *sym; + + string_length = SIZEOF(int4); + sym = symbols; + while (sym) + { + sym->n.n_strx = string_length; + emit_immed((char *)&sym->n, SIZEOF(sym->n)); + string_length += sym->name_len; + sym = sym->next; + } + emit_immed((char *)&string_length, SIZEOF(string_length)); + sym = symbols; + while (sym) + { + emit_immed((char *)&sym->name[0], sym->name_len); + sym = sym->next; + } +} + + +void obj_init(void) +{ + cdlits = txtrel_cnt = 0; + data_rel = text_rel = data_rel_end = text_rel_end = 0; + symbols = 0; +} + + + +void emit_literals(void) +{ + uint4 offset, padsize; + mliteral *p; + + set_psect(GTM_LITERALS, 0); + offset = stringpool.free - stringpool.base; + emit_immed((char *)stringpool.base, offset); + /* comp_lits aligns the start of source path on a NATIVE_WSIZE boundary.*/ + padsize = PADLEN(offset, NATIVE_WSIZE); + if (padsize) + { + emit_immed(PADCHARS, padsize); + offset += padsize; + } + emit_immed(source_file_name, source_name_len); + offset += source_name_len; + /* comp_lits aligns the start of routine_name on a NATIVE_WSIZE boundary.*/ + padsize = PADLEN(offset, NATIVE_WSIZE); + if (padsize) + { + emit_immed(PADCHARS, padsize); + offset += padsize; + } + emit_immed(routine_name.addr, routine_name.len); + offset += routine_name.len; + /* comp_lits aligns the start of the literal area on a NATIVE_WSIZE boundary.*/ + padsize = PADLEN(offset, NATIVE_WSIZE); + if (padsize) + { + emit_immed(PADCHARS, padsize); + offset += padsize; + } + + dqloop(&literal_chain, que, p) + { + assert (p->rt_addr == offset); + MV_FORCE_NUMD(&p->v); + if (p->v.str.len) + emit_pidr(p->rt_addr + ((char *) &p->v.str.addr - (char *)&p->v), + p->v.str.addr - (char *) stringpool.base, (int4 *)&p->v.str.addr); + else + p->v.str.addr = 0; + emit_immed((char *)&p->v, SIZEOF(p->v)); + offset += SIZEOF(p->v); + } + assert(lits_size == offset); +} diff --git a/sr_i386/op_bkpt.s b/sr_i386/op_bkpt.s new file mode 100644 index 0000000..6cf7ff9 --- /dev/null +++ b/sr_i386/op_bkpt.s @@ -0,0 +1,264 @@ +################################################################# +# # +# Copyright 2001, 2010 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_bkpt.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_zstepret +# PAGE + + .DATA +.extern frame_pointer +.extern zstep_level + + .text +.extern gtm_fetch +.extern op_retarg +.extern op_zbreak +.extern op_zst_break +.extern op_zst_over +.extern op_zstepret +.extern opp_ret + +# PUBLIC opp_zstepret +ENTRY opp_zstepret + movl frame_pointer,%eax + movw msf_typ_off(%eax),%dx + testw $1,%dx + je l1 + movl zstep_level,%edx + cmpl %eax, %edx + jg l1 + call op_zstepret +l1: jmp opp_ret +# opp_zstepret ENDP + +# PUBLIC opp_zstepretarg +ENTRY opp_zstepretarg + pushl %eax + pushl %edx + movl frame_pointer,%eax + movw msf_typ_off(%eax),%dx + testw $1,%dx + je l2 + movl zstep_level,%edx + cmpl %eax, %edx + jg l2 + call op_zstepret +l2: popl %edx + popl %eax + jmp op_retarg +# opp_zstepretarg ENDP + +# PUBLIC op_zbfetch +ENTRY op_zbfetch + movl frame_pointer,%edx + popl msf_mpc_off(%edx) + call gtm_fetch + popl %eax +# lea esp, [esp][eax*4] + leal (%esp,%eax,4),%esp + pushl frame_pointer + call op_zbreak + addl $4,%esp + getframe + ret +# op_zbfetch ENDP + +# PUBLIC op_zbstart +ENTRY op_zbstart + movl frame_pointer,%edx + popl msf_mpc_off(%edx) + pushl %edx + call op_zbreak + addl $4,%esp + getframe + ret +# op_zbstart ENDP + +# PUBLIC op_zstepfetch +ENTRY op_zstepfetch + movl frame_pointer,%edx + popl msf_mpc_off(%edx) + call gtm_fetch + popl %eax +# lea esp, [esp][eax*4] + leal (%esp,%eax,4),%esp + call op_zst_break + getframe + ret +# op_zstepfetch ENDP + +# PUBLIC op_zstepstart +ENTRY op_zstepstart + movl frame_pointer,%edx + popl msf_mpc_off(%edx) + call op_zst_break + getframe + ret +# op_zstepstart ENDP + +# PUBLIC op_zstzbfetch +ENTRY op_zstzbfetch + movl frame_pointer,%edx + popl msf_mpc_off(%edx) + call gtm_fetch + popl %eax +# lea esp, [esp][eax*4] + leal (%esp,%eax,4),%esp + pushl frame_pointer + call op_zbreak + addl $4,%esp + call op_zst_break + getframe + ret +# op_zstzbfetch ENDP + +# PUBLIC op_zstzbstart +ENTRY op_zstzbstart + movl frame_pointer,%edx + popl msf_mpc_off(%edx) + pushl %edx + call op_zbreak + addl $4,%esp + call op_zst_break + getframe + ret +# op_zstzbstart ENDP + +# PUBLIC op_zstzb_fet_over +ENTRY op_zstzb_fet_over + movl frame_pointer,%edx + popl msf_mpc_off(%edx) + call gtm_fetch + popl %eax +# lea esp, [esp][eax*4] + leal (%esp,%eax,4),%esp + pushl frame_pointer + call op_zbreak + addl $4,%esp + movl zstep_level,%edx + cmpl frame_pointer,%edx + jle l3 + cmpl $0,%eax + jne l5 + jmp l4 + +l3: call op_zst_break +l4: getframe + ret + +l5: call op_zst_over + movl frame_pointer,%edx + pushl msf_mpc_off(%edx) + ret +# op_zstzb_fet_over ENDP + +# PUBLIC op_zstzb_st_over +ENTRY op_zstzb_st_over + movl frame_pointer,%edx + popl msf_mpc_off(%edx) + pushl %edx + call op_zbreak + addl $4,%esp + movl zstep_level,%edx + cmpl frame_pointer,%edx + jle l6 + cmpl $0,%eax + jne l8 + jmp l7 + +l6: call op_zst_break +l7: getframe + ret + +l8: call op_zst_over + movl frame_pointer,%edx + pushl msf_mpc_off(%edx) + ret +# op_zstzb_st_over ENDP + +# PUBLIC op_zst_fet_over +ENTRY op_zst_fet_over + movl frame_pointer,%edx + popl msf_mpc_off(%edx) + call gtm_fetch + popl %eax +# lea esp, [esp][eax*4] + leal (%esp,%eax,4),%esp + movl zstep_level,%edx + cmpl frame_pointer,%edx + jg l9 + call op_zst_break + getframe + ret + +l9: call op_zst_over + movl frame_pointer,%edx + pushl msf_mpc_off(%edx) + ret +# op_zst_fet_over ENDP + +# PUBLIC op_zst_st_over +ENTRY op_zst_st_over + movl frame_pointer,%eax + popl msf_mpc_off(%eax) + movl zstep_level,%edx + cmpl %eax,%edx + jg l10 + call op_zst_break + getframe + ret + +l10: call op_zst_over + movl frame_pointer,%edx + pushl msf_mpc_off(%edx) + ret +# op_zst_st_over ENDP + +# PUBLIC opp_zst_over_ret +ENTRY opp_zst_over_ret + movl frame_pointer,%eax + movw msf_typ_off(%eax),%dx + testw $1,%dx + je l11 + movl zstep_level,%edx + movl msf_old_frame_off(%eax),%eax + cmpl %eax,%edx + jg l11 + call op_zstepret +l11: jmp opp_ret +# opp_zst_over_ret ENDP + +# PUBLIC opp_zst_over_retarg +ENTRY opp_zst_over_retarg + pushl %eax + pushl %edx + movl frame_pointer,%eax + movw msf_typ_off(%eax),%dx + testw $1,%dx + je l12 + movl zstep_level,%edx + movl msf_old_frame_off(%eax),%eax + cmpl %eax,%edx + jg l12 + call op_zstepret +l12: popl %edx + popl %eax + jmp op_retarg +# opp_zst_over_retarg ENDP + +# END diff --git a/sr_i386/op_call.s b/sr_i386/op_call.s new file mode 100644 index 0000000..2db6725 --- /dev/null +++ b/sr_i386/op_call.s @@ -0,0 +1,51 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_call.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_callb +# PAGE + + .DATA +.extern frame_pointer + + .text +.extern copy_stack_frame + +# PUBLIC op_callb +ENTRY op_callb + movl frame_pointer,%edx + movl (%esp),%eax + movl %eax,msf_mpc_off(%edx) + addl $2,msf_mpc_off(%edx) + call copy_stack_frame + ret +# op_callb ENDP + + .sbttl op_callw, op_calll +# PUBLIC op_callw, op_calll +ENTRY op_calll +ENTRY op_callw + movl frame_pointer,%edx + movl (%esp),%eax + movl %eax,msf_mpc_off(%edx) + addl $5,msf_mpc_off(%edx) + call copy_stack_frame + ret +# op_calll ENDP + +# END diff --git a/sr_i386/op_callsp.s b/sr_i386/op_callsp.s new file mode 100644 index 0000000..8701596 --- /dev/null +++ b/sr_i386/op_callsp.s @@ -0,0 +1,58 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_callsp.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_callsp +# PAGE + + .DATA +.extern dollar_truth +.extern frame_pointer + + .text +.extern exfun_frame +.extern push_tval + + .sbttl op_callspb +# PUBLIC op_callspb +ENTRY op_callspb + movl frame_pointer,%edx + movl (%esp),%eax + movl %eax,msf_mpc_off(%edx) + addl $2,msf_mpc_off(%edx) # store pc in MUMPS stack frame +doit: call exfun_frame + pushl dollar_truth + call push_tval + addl $4,%esp + movl frame_pointer,%edx + movl msf_temps_ptr_off(%edx),%edi + ret +# op_callspb ENDP + + .sbttl op_callspw, op_callspl +# PUBLIC op_callspw, op_callspl +ENTRY op_callspw +ENTRY op_callspl + movl frame_pointer,%edx + movl (%esp),%eax + movl %eax,msf_mpc_off(%edx) + addl $5,msf_mpc_off(%edx) # store pc in MUMPS stack frame + jmp doit +# op_callspw ENDP + +# END diff --git a/sr_i386/op_contain.s b/sr_i386/op_contain.s new file mode 100644 index 0000000..8a0cfbb --- /dev/null +++ b/sr_i386/op_contain.s @@ -0,0 +1,75 @@ +################################################################# +# # +# Copyright 2001, 2009 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_contain.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "mval_def.si" + + .sbttl op_contain +# PAGE + + .text +sav_eax = -4 +sav_edx = -8 + +.extern matchc +.extern n2s +.extern underr + +# PUBLIC op_contain +ENTRY op_contain + enter $8, $0 + pushl %edi + pushl %esi + pushl %ebx + + movl %edx, sav_edx(%ebp) + mv_force_defined %eax, l1 + movl %eax, sav_eax(%ebp) + mv_force_str %eax, l2 + + movl sav_edx(%ebp), %edx + mv_force_defined %edx, l3 + movl %edx, sav_edx(%ebp) + mv_force_str %edx, l4 + + pushl $1 # pieces argument but have to pass its addr + movl %esp, %edx + subl $4, %esp # returned value + movl %esp, %eax + pushl %edx # parm 6 + pushl %eax # parm 5 + movl sav_eax(%ebp), %eax + movl sav_edx(%ebp), %edx + pushl mval_a_straddr(%eax) # parm 4 + movl mval_l_strlen(%eax), %eax + pushl %eax # parm 3 + pushl mval_a_straddr(%edx) # parm 2 + movl mval_l_strlen(%edx), %eax + pushl %eax # parm 1 + call matchc + leal 24(%esp), %esp # remove args + popl %eax # return value + popl %edx # updated pieces value (ignored) + cmpl $0, %eax + + popl %ebx + popl %esi + popl %edi + leave + ret +# op_contain ENDP + +# END diff --git a/sr_i386/op_currtn.s b/sr_i386/op_currtn.s new file mode 100644 index 0000000..bb8fbcf --- /dev/null +++ b/sr_i386/op_currtn.s @@ -0,0 +1,42 @@ +################################################################# +# # +# Copyright 2001, 2006 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_currtn.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "mval_def.si" + + .INCLUDE "g_msf.si" + + .sbttl op_currtn +# PAGE + + .DATA +.extern frame_pointer + + .text + +# PUBLIC op_currtn +ENTRY op_currtn + movw $mval_m_str,mval_w_mvtype(%edx) + movl frame_pointer,%eax + movl msf_rvector_off(%eax),%eax + pushl mrt_rtn_len(%eax) + popl mval_l_strlen(%edx) # %edx->str.len = frame_pointer->rvector->routine_name.len + movl mrt_rtn_addr(%eax),%eax + movl %eax,mval_a_straddr(%edx) # %edx->str.addr = frame_pointer->rvector->routine_name.addr + ret +# op_currtn ENDP + +# END diff --git a/sr_i386/op_equ.s b/sr_i386/op_equ.s new file mode 100644 index 0000000..60ca444 --- /dev/null +++ b/sr_i386/op_equ.s @@ -0,0 +1,33 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_equ.s + .sbttl op_equ +.include "linkage.si" + +# .386 +# .MODEL FLAT, C + + .text +.extern is_equ + +# PUBLIC op_equ +ENTRY op_equ + pushl %edx + pushl %eax + call is_equ + addl $8,%esp + cmpl $0,%eax + ret +# op_equ ENDP + +# END diff --git a/sr_i386/op_equnul.s b/sr_i386/op_equnul.s new file mode 100644 index 0000000..ea26c54 --- /dev/null +++ b/sr_i386/op_equnul.s @@ -0,0 +1,53 @@ +################################################################# +# # +# Copyright 2001, 2006 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_equnul.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "mval_def.si" + + .sbttl op_equnul +# PAGE + + .DATA +.extern undef_inhibit + + .text +.extern underr + +# PUBLIC op_equnul +ENTRY op_equnul + mv_if_notdefined %eax, l3 + testw $mval_m_str,mval_w_mvtype(%eax) + je l2 + cmpl $0,mval_l_strlen(%eax) + jne l2 +l1: movl $1,%eax + cmpl $0,%eax + ret + +l2: movl $0,%eax + cmpl $0,%eax + ret + +l3: cmpb $0,undef_inhibit # not defined + jne l1 # if undef_inhibit, then all undefined + # values are equal to null string + pushl %eax # really undef + call underr + addl $4,%esp + ret +# op_equnul ENDP + +# END diff --git a/sr_i386/op_exfun.s b/sr_i386/op_exfun.s new file mode 100644 index 0000000..8401584 --- /dev/null +++ b/sr_i386/op_exfun.s @@ -0,0 +1,121 @@ +################################################################# +# # +# Copyright 2001, 2002 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_exfun.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_exfun +# PAGE + +# call op_exfun with the following stack: +# +# return PC +# ret_value address +# mask +# actualcnt +# actual1 address +# actual2 address +# ... + + .DATA +.extern ERR_GTMCHECK +.extern dollar_truth +.extern frame_pointer + + .text +.extern exfun_frame +.extern push_parm +.extern rts_error + +JMP_Jb = 0x0eb +JMP_Jv = 0x0e9 + +actual1 = 20 +act_cnt = 16 +mask_arg = 12 +ret_val = 8 +rtn_pc = 4 + +sav_esi = -4 +sav_ebx = -8 +sav_msf = -12 + +# PUBLIC op_exfun +ENTRY op_exfun + pushl %ebp + movl %esp,%ebp + pushl %esi + pushl %ebx + leal sav_msf(%ebp),%esp # establish space for locals + movl act_cnt(%ebp),%eax + addl $3,%eax + negl %eax + leal (%esp,%eax,4),%esp # establish space for temps + + movl frame_pointer,%edx + movl rtn_pc(%ebp),%eax + cmpb $JMP_Jv,(%eax) + je long + cmpb $JMP_Jb,(%eax) + je byte_off +error: pushl ERR_GTMCHECK + pushl $1 + call rts_error + addl $8,%esp + jmp retlab + +byte_off: + movl %eax,msf_mpc_off(%edx) + addl $2,msf_mpc_off(%edx) + jmp cont + +long: movl %eax,msf_mpc_off(%edx) + addl $5,msf_mpc_off(%edx) +cont: call exfun_frame + movl frame_pointer,%edx + movl msf_old_frame_off(%edx),%eax + movl %eax,frame_pointer + movl %edx,sav_msf(%ebp) + leal ret_val(%ebp),%esi + movl %esp,%edi + movl act_cnt(%ebp),%eax + movl %eax,%ecx + addl $3,%ecx + REP + movsl + movl dollar_truth,%ecx + andl $1,%ecx + pushl %ecx # push $T + addl $4,%eax + pushl %eax # push total count + call push_parm # push_parm ($T, ret_value, mask, argc [,arg1, arg2, ...]); +done: movl sav_msf(%ebp),%eax + movl %eax,frame_pointer + orw $SFT_EXTFUN,msf_typ_off(%eax) + movl msf_temps_ptr_off(%eax),%edi +retlab: leal sav_ebx(%ebp),%esp + movl rtn_pc(%ebp),%edx + movl act_cnt(%ebp),%eax + addl $4,%eax + popl %ebx + popl %esi + popl %ebp + leal (%esp,%eax,4),%esp + pushl %edx + ret +# op_exfun ENDP + +# END diff --git a/sr_i386/op_extcall.s b/sr_i386/op_extcall.s new file mode 100644 index 0000000..e2d8ef0 --- /dev/null +++ b/sr_i386/op_extcall.s @@ -0,0 +1,83 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_extcall.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_extcall +# PAGE + + .DATA +.extern ERR_GTMCHECK +.extern ERR_LABELUNKNOWN +.extern frame_pointer + + .text +.extern auto_zlink +.extern new_stack_frame +.extern rts_error + +# PUBLIC op_extcall +ENTRY op_extcall + putframe + addl $4,%esp + popl %edx + popl %eax + cmpl $0,%eax + je l2 +l1: movl (%eax),%eax + addl mrt_curr_ptr(%edx),%eax + addl %edx,%eax + pushl %eax + pushl $0 + pushl %edx + call new_stack_frame + addl $12,%esp + getframe + ret + +l2: cmpl $0,%edx + jne l4 + subl $4,%esp + pushl %esp + movl frame_pointer,%eax + pushl msf_mpc_off(%eax) + call auto_zlink + addl $8,%esp + cmpl $0,%eax + je l3 + movl %eax,%edx + popl %eax + cmpl $0,%eax + jne l1 +l3: addl $4,%esp + pushl ERR_GTMCHECK + pushl $1 + call rts_error + pushl $1 # in original m68020 code ?? + addl $8,%esp + getframe + ret + +l4: pushl ERR_LABELUNKNOWN + pushl $1 + call rts_error + addl $8,%esp + getframe + ret +# op_extcall ENDP + +# END diff --git a/sr_i386/op_extexfun.s b/sr_i386/op_extexfun.s new file mode 100644 index 0000000..9463125 --- /dev/null +++ b/sr_i386/op_extexfun.s @@ -0,0 +1,158 @@ +################################################################# +# # +# Copyright 2001, 2002 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_extexfun.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_extexfun +# PAGE + + +# call op_extexfun with the following stack: +# +# return PC +# routine +# label +# ret_value address +# mask +# actualcnt +# actual1 address +# actual2 address +# ... + + .DATA +.extern ERR_FMLLSTMISSING +.extern ERR_GTMCHECK +.extern ERR_LABELUNKNOWN +.extern dollar_truth +.extern frame_pointer + + .text +.extern auto_zlink +.extern new_stack_frame +.extern push_parm +.extern rts_error + +Grp5_Prefix = 0x0ff # escape for CALL_Ev opcode +CALL_Ev = 0x010 # CALL_Ev part of ModR/M byte +reg_opcode_mask = 0x038 +ISFORMAL = 0x0244 # xf_isformal*4 + +actual1 = 24 +act_cnt = 20 +mask_arg = 16 +ret_val = 12 +label_arg = 8 +routine = 4 + +sav_ebx = -4 +sav_msf = -8 + +# PUBLIC op_extexfun +ENTRY op_extexfun + putframe + addl $4,%esp # burn return PC + pushl %ebp + movl %esp,%ebp + pushl %ebx + leal sav_msf(%ebp),%esp # establish space for locals + movl act_cnt(%ebp),%eax + addl $3,%eax + negl %eax + leal (%esp,%eax,4),%esp # add space for temps + + movl routine(%ebp),%edx + movl label_arg(%ebp),%eax + cmpl $0,%eax + je l3 +l1: movl (%eax),%ebx + addl mrt_curr_ptr(%edx),%ebx + addl %edx,%ebx + cmpb $Grp5_Prefix,0(%ebx) + jne l6 + movb 1(%ebx),%cl + andb $reg_opcode_mask,%cl + cmpb $CALL_Ev,%cl + jne l6 + cmpl $ISFORMAL,2(%ebx) + jne l6 + pushl %ebx + pushl $0 + pushl %edx + call new_stack_frame + addl $12,%esp + movl frame_pointer,%edx + movl msf_old_frame_off(%edx),%eax + movl %eax,frame_pointer + movl %edx,sav_msf(%ebp) + leal ret_val(%ebp),%esi + movl %esp,%edi + movl act_cnt(%ebp),%eax + movl %eax,%ecx + addl $3,%ecx # include: A(ret_value), mask, argc + REP + movsl + movl dollar_truth,%ecx + andl $1,%ecx + pushl %ecx + addl $4,%eax # include: $T(just pushed) plus other 3 + pushl %eax # push total count + call push_parm # push_parm ($T, ret_value, mask, argc [,arg1, arg2, ...]); + movl sav_msf(%ebp),%eax + movl %eax,frame_pointer + orw $SFT_EXTFUN,msf_typ_off(%eax) +retlab: leal sav_ebx(%ebp),%esp + movl act_cnt(%ebp),%eax + addl $5,%eax + popl %ebx + popl %ebp + leal (%esp,%eax,4),%esp + getframe + ret + +l3: cmpl $0,%edx + jne l5 + subl $4,%esp + movl %esp,%eax + pushl %eax + movl frame_pointer,%edx + pushl msf_mpc_off(%edx) + call auto_zlink + addl $8,%esp + cmpl $0,%eax + je l4 + movl %eax,%edx + popl %eax + cmpl $0,%eax + jne l1 +l4: pushl ERR_GTMCHECK + pushl $1 + call rts_error + jmp retlab + +l5: pushl ERR_LABELUNKNOWN + pushl $1 + call rts_error + jmp retlab + +l6: pushl ERR_FMLLSTMISSING + pushl $1 + call rts_error + jmp retlab + +# op_extexfun ENDP + +# END diff --git a/sr_i386/op_extjmp.s b/sr_i386/op_extjmp.s new file mode 100644 index 0000000..dbe7c84 --- /dev/null +++ b/sr_i386/op_extjmp.s @@ -0,0 +1,85 @@ +################################################################# +# # +# Copyright 2001, 2007 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_extjmp.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_extjmp +# PAGE + + .DATA +.extern ERR_GTMCHECK +.extern ERR_LABELUNKNOWN +.extern frame_pointer + + .text +.extern auto_zlink +.extern flush_jmp +.extern rts_error + +# PUBLIC op_extjmp +ENTRY op_extjmp + putframe + addl $4,%esp + popl %edx + cmpl $0,%edx + je l2 + popl %eax + cmpl $0,%eax + je l4 + +l1: movl (%eax),%eax + addl mrt_curr_ptr(%edx),%eax + addl %edx,%eax + pushl %eax + pushl $0 + pushl %edx + call flush_jmp + addl $12,%esp + getframe + ret + +l2: movl %esp,%eax + pushl %eax + movl frame_pointer,%eax + pushl msf_mpc_off(%eax) + call auto_zlink + addl $8,%esp + cmpl $0,%eax + je l3 + movl %eax,%edx + popl %eax + cmpl $0,%eax + jne l1 + jmp l4 + +l3: addl $4,%esp + pushl ERR_GTMCHECK + pushl $1 + call rts_error + addl $8,%esp + getframe + ret + +l4: pushl ERR_LABELUNKNOWN + pushl $1 + call rts_error + addl $8,%esp + getframe + ret +# op_extjmp ENDP + +# END diff --git a/sr_i386/op_fetchintrrpt.s b/sr_i386/op_fetchintrrpt.s new file mode 100644 index 0000000..08ac76d --- /dev/null +++ b/sr_i386/op_fetchintrrpt.s @@ -0,0 +1,53 @@ +################################################################# +# # +# Copyright 2001, 2009 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_fetchintrrpt.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_fetchintrrpt +# PAGE + + .DATA +.extern frame_pointer +.extern neterr_pending + + .text +.extern gtm_fetch +.extern gvcmz_neterr +.extern outofband_clear +.extern async_action + +# PUBLIC op_fetchintrrpt +ENTRY op_fetchintrrpt + movl frame_pointer,%edx + popl msf_mpc_off(%edx) + call gtm_fetch + popl %eax + leal (%esp,%eax,4),%esp + cmpb $0,neterr_pending + je l1 + call outofband_clear + pushl $0 + call gvcmz_neterr + addl $4,%esp +l1: pushl $1 + call async_action + movl frame_pointer,%edx + pushl msf_mpc_off(%edx) + ret +# op_fetchintrrpt ENDP + +# END diff --git a/sr_i386/op_fnget.s b/sr_i386/op_fnget.s new file mode 100644 index 0000000..6a03db1 --- /dev/null +++ b/sr_i386/op_fnget.s @@ -0,0 +1,57 @@ +################################################################# +# # +# Copyright 2001, 2009 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_fnget.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "mval_def.si" + + .sbttl op_fnget +# PAGE + +# ------------------------------------ +# op_fnget.s +# +# Mumps $Get function +# ------------------------------------ + +# edx - src. mval +# eax - dest. mval + + .text +# PUBLIC op_fnget +ENTRY op_fnget + cmpl $0,%edx + je l5 # if arg = 0, set type and len + mv_if_notdefined %edx, l5 + +# Copy the mval from [edx] to [eax]. + pushl %edi + pushl %esi + movl $mval_byte_len,%ecx + movl %edx,%esi + movl %eax,%edi + REP + movsb + andw $~mval_m_aliascont, mval_w_mvtype(%eax) # Don't propagate alias container flag + popl %esi + popl %edi + ret + +l5: movw $mval_m_str,mval_w_mvtype(%eax) # string type + movl $0,mval_l_strlen(%eax) # dest. str. len. = 0 + ret +# op_fnget ENDP + +# END diff --git a/sr_i386/op_fnzextract.s b/sr_i386/op_fnzextract.s new file mode 100644 index 0000000..db28de5 --- /dev/null +++ b/sr_i386/op_fnzextract.s @@ -0,0 +1,93 @@ +################################################################# +# # +# Copyright 2006, 2008 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_fnzextract.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "mval_def.si" + + .sbttl op_fnzextract +# PAGE + +# ------------------------------------ +# op_fnzextract.s +# +# Mumps $Extract function +# ------------------------------------ + +# -------------------------------- +# op_fnzextract (int last, int first, mval *src, mval *dest) +# -------------------------------- +# esi - src mval +# edi - dest mval +# ecx - src. str. len. +# ebx - resultant str. len. +# eax - first index +# edx - last index + +last = 8 +first = 12 +src = 16 +dest = 20 + + .text + +.extern n2s +.extern underr + +# PUBLIC op_fnzextract +ENTRY op_fnzextract + enter $0,$0 + pushl %edi + pushl %esi + pushl %ebx + + movl src(%ebp),%esi # esi - src. mval + mv_force_defined %esi, l00 + movl %esi, src(%ebp) # save possibly modified src ptr + mv_force_str %esi, l01 + movl src(%ebp),%esi # esi - src.mval + movl first(%ebp),%eax # eax - first + cmpl $0,%eax + jg l10 + movl $1,%eax # if first < 1, then first = 1 +l10: movl last(%ebp),%edx # edx - last + movl dest(%ebp),%edi # edi - last + movw $mval_m_str,mval_w_mvtype(%edi) # always a string + movl mval_l_strlen(%esi),%ecx # ecx - src. str. len. + cmpl %eax,%ecx # if left index > str. len, + # then null result + jl l25 + cmpl %edx,%ecx # right index may be at most the len. + jge l20 # of the source string + movl %ecx,%edx +l20: movl %edx,%ebx + subl %eax,%ebx # result len. = end - start + 1 + addl $1,%ebx + jg l30 # if len > 0, then continue +l25: movl $0,mval_l_strlen(%edi) + jmp retlab + +l30: movl %ebx,mval_l_strlen(%edi) # dest. str. len. + subl $1,%eax # base = src.addr + left ind. - 1 + addl mval_a_straddr(%esi),%eax + movl %eax,mval_a_straddr(%edi) # string addr. +retlab: popl %ebx + popl %esi + popl %edi + leave + ret +# op_fnzextract ENDP + +# END diff --git a/sr_i386/op_follow.s b/sr_i386/op_follow.s new file mode 100644 index 0000000..9c1f554 --- /dev/null +++ b/sr_i386/op_follow.s @@ -0,0 +1,61 @@ +################################################################# +# # +# Copyright 2001, 2008 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_follow.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "mval_def.si" + + .sbttl op_follow +# PAGE + + +sav_eax = -4 +sav_edx = -8 + + .text +.extern memvcmp +.extern n2s +.extern underr + +# PUBLIC op_follow +ENTRY op_follow + enter $8, $0 + + movl %edx, sav_edx(%ebp) + mv_force_defined %eax, l1 + movl %eax, sav_eax(%ebp) + mv_force_str %eax, l2 + + movl sav_edx(%ebp), %edx + mv_force_defined %edx, l3 + movl %edx, sav_edx(%ebp) + mv_force_str %edx, l4 + + movl sav_eax(%ebp),%eax + movl sav_edx(%ebp),%edx + movl mval_l_strlen(%edx),%ecx + pushl %ecx + pushl mval_a_straddr(%edx) + movl mval_l_strlen(%eax),%ecx + pushl %ecx + pushl mval_a_straddr(%eax) + call memvcmp + addl $16,%esp + cmpl $0,%eax + leave + ret +# op_follow ENDP + +# END diff --git a/sr_i386/op_forcenum.s b/sr_i386/op_forcenum.s new file mode 100644 index 0000000..40cbd60 --- /dev/null +++ b/sr_i386/op_forcenum.s @@ -0,0 +1,78 @@ +################################################################# +# # +# Copyright 2001, 2008 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_forcenum.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "mval_def.si" + + .sbttl op_forcenum +# PAGE + + .text +.extern s2n + +# edx - source mval +# eax - destination mval + +# PUBLIC op_forcenum +ENTRY op_forcenum + pushl %eax + mv_force_defined %edx, l00 + pushl %edx + mv_force_num %edx, l10 + popl %edx + popl %eax + + testw $mval_m_str,mval_w_mvtype(%edx) + je l20 + testw $mval_m_num_approx,mval_w_mvtype(%edx) + je l40 +l20: testw $mval_m_int_without_nm,mval_w_mvtype(%edx) + je l30 + movw $mval_m_int,mval_w_mvtype(%eax) + movl mval_l_m1(%edx),%edx + movl %edx,mval_l_m1(%eax) + ret + +l30: pushl %ebx + movw $mval_m_nm,mval_w_mvtype(%eax) + movb mval_b_exp(%edx),%bl + movb %bl,mval_b_exp(%eax) + +# Copy the only numeric part of Mval from [edx] to [eax]. + + movl mval_l_m0(%edx),%ebx + movl %ebx,mval_l_m0(%eax) + movl mval_l_m1(%edx),%ebx + movl %ebx,mval_l_m1(%eax) + popl %ebx + ret + +l40: +# Copy the Mval from [edx] to [eax]. + + pushl %edi + pushl %esi + movl %eax,%edi + movl %edx,%esi + movl $mval_byte_len,%ecx + REP + movsb + popl %esi + popl %edi + ret +# op_forcenum ENDP + +# END diff --git a/sr_i386/op_forchk1.s b/sr_i386/op_forchk1.s new file mode 100644 index 0000000..3eae592 --- /dev/null +++ b/sr_i386/op_forchk1.s @@ -0,0 +1,27 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_forchk1.s + .sbttl op_forchk1 + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + + .text +# PUBLIC op_forchk1 +ENTRY op_forchk1 + ret +# op_forchk1 ENDP + +# END diff --git a/sr_i386/op_forinit.s b/sr_i386/op_forinit.s new file mode 100644 index 0000000..19f5d3f --- /dev/null +++ b/sr_i386/op_forinit.s @@ -0,0 +1,66 @@ +################################################################# +# # +# Copyright 2001, 2008 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_forinit.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .INCLUDE "mval_def.si" + + .sbttl op_forinit +# PAGE + + .DATA +.extern frame_pointer + + .text +.extern numcmp +.extern s2n +.extern underr + +# PUBLIC op_forinit +ENTRY op_forinit + movl frame_pointer,%edx + popl msf_mpc_off(%edx) + movl 4(%esp),%eax + mv_force_defined %eax, l0 + movl %eax, 4(%esp) + mv_force_num %eax, t2 + movl 4(%esp),%eax + cmpl $0,mval_l_m1(%eax) + js l2 + mv_if_int %eax, l1 + testb $mval_esign_mask,mval_b_exp(%eax) + jne l2 +l1: movl 8(%esp),%eax + movl %eax,4(%esp) + call numcmp + addl $12,%esp + movl frame_pointer,%edx + pushl msf_mpc_off(%edx) + cmpl $0,%eax + ret + +l2: movl 8(%esp),%eax + pushl %eax + call numcmp + addl $16,%esp + movl frame_pointer,%edx + pushl msf_mpc_off(%edx) + cmpl $0,%eax + ret +# op_forinit ENDP + +# END diff --git a/sr_i386/op_forintrrpt.s b/sr_i386/op_forintrrpt.s new file mode 100644 index 0000000..80571fa --- /dev/null +++ b/sr_i386/op_forintrrpt.s @@ -0,0 +1,44 @@ +################################################################# +# # +# Copyright 2001, 2011 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_forintrrpt.s + .sbttl op_forintrrpt + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + + .DATA +.extern neterr_pending +.extern restart_pc + + .text +.extern gvcmz_neterr +.extern async_action +.extern outofband_clear + +# PUBLIC op_forintrrpt +ENTRY op_forintrrpt + cmpb $0,neterr_pending + je l1 + call outofband_clear + pushl $0 + call gvcmz_neterr + addl $4,%esp +l1: pushl $0 + call async_action + addl $4,%esp + ret +# op_forintrrpt ENDP + +# END diff --git a/sr_i386/op_forlcldo.s b/sr_i386/op_forlcldo.s new file mode 100644 index 0000000..b2887d1 --- /dev/null +++ b/sr_i386/op_forlcldo.s @@ -0,0 +1,53 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_forlcldo.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_forlcldo +# PAGE + + .DATA +.extern frame_pointer + + .text +.extern exfun_frame + + .sbttl op_forlcldob +# PUBLIC op_forlcldob +ENTRY op_forlcldob + movl frame_pointer,%edx + movl (%esp),%eax + movl %eax,msf_mpc_off(%edx) + addl $2,msf_mpc_off(%edx) # store pc in MUMPS stack frame +doit: call exfun_frame + movl frame_pointer,%edx + movl msf_temps_ptr_off(%edx),%edi + ret +# op_forlcldob ENDP + + .sbttl op_forlcldow, op_forlcldol +# PUBLIC op_forlcldow, op_forlcldol +ENTRY op_forlcldol +ENTRY op_forlcldow + movl frame_pointer,%edx + movl (%esp),%eax + movl %eax,msf_mpc_off(%edx) + addl $5,msf_mpc_off(%edx) # store pc in MUMPS stack frame + jmp doit +# op_forlcldol ENDP + +# END diff --git a/sr_i386/op_forloop.s b/sr_i386/op_forloop.s new file mode 100644 index 0000000..e664c74 --- /dev/null +++ b/sr_i386/op_forloop.s @@ -0,0 +1,188 @@ +################################################################# +# # +# Copyright 2001, 2008 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_forloop.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + .INCLUDE "mval_def.si" + + .sbttl op_forloop +# PAGE + +# Called with the stack contents: +# call return +# ptr to index mval +# ptr to step mval +# ptr to terminator mval +# loop address + .DATA +.extern frame_pointer + +# ten_dd DD 10 +ten_dd: +.long 10 + + .text +.extern add_mvals +.extern numcmp +.extern s2n +.extern underr + +term = 12 +step = 8 +indx = 4 + +# PUBLIC op_forloop +ENTRY op_forloop + movl frame_pointer,%edx + popl msf_mpc_off(%edx) + enter $0, $0 + pushl %edi + pushl %esi + pushl %ebx + movl indx(%ebp),%esi + mv_force_defined %esi, l0 + movl %esi, indx(%ebp) + mv_force_num %esi, l1 + movl indx(%ebp),%esi + movl step(%ebp),%edi + movw mval_w_mvtype(%esi),%ax + movw mval_w_mvtype(%edi),%dx + andw %dx,%ax + testw $mval_m_int_without_nm,%ax + je L66 + movl mval_l_m1(%esi),%eax + addl mval_l_m1(%edi),%eax + cmpl $MANT_HI,%eax + jge L68 + cmpl $-MANT_HI,%eax + jle L67 + movw $mval_m_int,mval_w_mvtype(%esi) + movl %eax,mval_l_m1(%esi) + jmp L63 + +L67: movb $mval_esign_mask,mval_b_exp(%esi) # set sign bit + negl %eax + jmp L69 + +L68: movb $0,mval_b_exp(%esi) # clear sign bit +L69: movw $mval_m_nm,mval_w_mvtype(%esi) + orb $69,mval_b_exp(%esi) # set exponent field + movl %eax,%ebx + movl $0,%edx + idivl ten_dd,%eax + movl %eax,mval_l_m1(%esi) + imull $10,%eax,%eax + subl %eax,%ebx + imull $MANT_LO,%ebx,%ebx + movl %ebx,mval_l_m0(%esi) + jmp L63 + +L66: pushl %esi + pushl $0 + pushl %edi + pushl %esi + call add_mvals + addl $16,%esp + movl indx(%ebp),%esi +L63: movl step(%ebp),%edi + testw $mval_m_int_without_nm,mval_w_mvtype(%edi) + jne a + cmpb $0,mval_b_exp(%edi) + jl b + jmp a2 + +a: cmpl $0,mval_l_m1(%edi) + jl b +a2: movl term(%ebp),%edi + jmp e + +b: movl %esi,%edi # if step is negative, reverse compare + movl term(%ebp),%esi +e: # compare indx and term + movw mval_w_mvtype(%esi),%ax + movw mval_w_mvtype(%edi),%dx + andw %dx,%ax + testw $2,%ax + je ccmp + movl mval_l_m1(%esi),%eax + subl mval_l_m1(%edi),%eax + jmp tcmp + +ccmp: pushl %edi + pushl %esi + call numcmp + addl $8,%esp + cmpl $0,%eax +tcmp: jle d + movl indx(%ebp),%esi + movl step(%ebp),%edi + movw mval_w_mvtype(%esi),%ax + movw mval_w_mvtype(%edi),%dx + andw %dx,%ax + testw $mval_m_int_without_nm,%ax + je l66 + movl mval_l_m1(%esi),%eax + subl mval_l_m1(%edi),%eax + cmpl $MANT_HI,%eax + jge l68 + cmpl $-MANT_HI,%eax + jle l67 + movw $mval_m_int,mval_w_mvtype(%esi) + movl %eax,mval_l_m1(%esi) + jmp l63 + +l67: movb $mval_esign_mask,mval_b_exp(%esi) # set sign bit + negl %eax + jmp l69 + +l68: movb $0,mval_b_exp(%esi) # clear sign bit +l69: movw $mval_m_nm,mval_w_mvtype(%esi) + orb $69,mval_b_exp(%esi) # set exponent field + movl %eax,%ebx + movl $0,%edx + idivl ten_dd,%eax + movl %eax,mval_l_m1(%esi) + imull $10,%eax,%eax + subl %eax,%ebx + imull $MANT_LO,%ebx,%ebx + movl %ebx,mval_l_m0(%esi) + jmp l63 + +l66: pushl %esi + pushl $1 + pushl %edi + pushl %esi + call add_mvals + addl $16,%esp +l63: popl %ebx + popl %esi + popl %edi + leave + addl $16,%esp # remove op_forloop arguments + movl frame_pointer,%edx + pushl msf_mpc_off(%edx) + ret + +d: popl %ebx + popl %esi + popl %edi + leave + addl $12,%esp # remove term, step, indx; leave loop addr + ret + +# op_forloop ENDP + +# END diff --git a/sr_i386/op_gettruth.s b/sr_i386/op_gettruth.s new file mode 100644 index 0000000..c74b316 --- /dev/null +++ b/sr_i386/op_gettruth.s @@ -0,0 +1,50 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_gettruth.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "mval_def.si" + + .sbttl op_gettruth +# PAGE + + .DATA +.extern dollar_truth +.extern literal_one +.extern literal_zero + + .text +# PUBLIC op_gettruth +ENTRY op_gettruth + pushl %edi + pushl %esi + + cmpl $0,dollar_truth + jne l1 + leal literal_zero,%esi + jmp doit + +l1: leal literal_one,%esi +doit: movl %edx,%edi + movl $mval_byte_len,%ecx + REP + movsb + + popl %esi + popl %edi + ret +# op_gettruth ENDP + +# END diff --git a/sr_i386/op_iretmvad.s b/sr_i386/op_iretmvad.s new file mode 100644 index 0000000..732c641 --- /dev/null +++ b/sr_i386/op_iretmvad.s @@ -0,0 +1,42 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_iretmvad.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_iretmvad +# PAGE + + .DATA +.extern frame_pointer + + .text +.extern op_unwind + +# PUBLIC op_iretmvad +ENTRY op_iretmvad + movl %edx,%ecx # save input parameter from putframe macro + putframe + addl $4,%esp + movl %ecx,%edx + pushl %edx + call op_unwind + popl %eax # return input parameter + getframe + ret +# op_iretmvad ENDP + +# END diff --git a/sr_i386/op_isformal.s b/sr_i386/op_isformal.s new file mode 100644 index 0000000..91c907e --- /dev/null +++ b/sr_i386/op_isformal.s @@ -0,0 +1,47 @@ +################################################################# +# # +# Copyright 2001, 2002 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_isformal.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_isformal +# PAGE + + .DATA +.extern ERR_ACTLSTEXP +.extern frame_pointer + + .text +.extern rts_error + +# PUBLIC op_isformal +ENTRY op_isformal + movl frame_pointer,%edx + movw msf_typ_off(%edx),%ax + andw $~SFT_EXTFUN,msf_typ_off(%edx) + andw $SFT_EXTFUN,%ax + je l1 + ret + +l1: putframe + pushl ERR_ACTLSTEXP + pushl $1 + call rts_error + addl $8,%esp + ret +# op_isformal ENDP + +# END diff --git a/sr_i386/op_linefetch.s b/sr_i386/op_linefetch.s new file mode 100644 index 0000000..5316cba --- /dev/null +++ b/sr_i386/op_linefetch.s @@ -0,0 +1,41 @@ +################################################################# +# # +# Copyright 2001, 2009 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_linefetch.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_linefetch +# PAGE + + .DATA +.extern frame_pointer + + .text +.extern gtm_fetch + +# PUBLIC op_linefetch +ENTRY op_linefetch + movl frame_pointer,%eax + popl msf_mpc_off(%eax) + call gtm_fetch + popl %eax + leal (%esp,%eax,4),%esp + movl frame_pointer,%eax + pushl msf_mpc_off(%eax) + ret +# op_linefetch ENDP + +# END diff --git a/sr_i386/op_linestart.s b/sr_i386/op_linestart.s new file mode 100644 index 0000000..375e52c --- /dev/null +++ b/sr_i386/op_linestart.s @@ -0,0 +1,35 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_linestart.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_linestart +# PAGE + + .DATA +.extern frame_pointer + + .text +# PUBLIC op_linestart +ENTRY op_linestart + movl frame_pointer,%edx + movl (%esp),%eax + movl %eax,msf_mpc_off(%edx) + ret +# op_linestart ENDP + +# END diff --git a/sr_i386/op_mprofcall.s b/sr_i386/op_mprofcall.s new file mode 100644 index 0000000..bd62512 --- /dev/null +++ b/sr_i386/op_mprofcall.s @@ -0,0 +1,51 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_mprofcall.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_mprofcallb +# PAGE + + .DATA +.extern frame_pointer + + .text +.extern copy_stack_frame_sp + +# PUBLIC op_mprofcallb +ENTRY op_mprofcallb + movl frame_pointer,%edx + movl (%esp),%eax + movl %eax,msf_mpc_off(%edx) + addl $2,msf_mpc_off(%edx) + call copy_stack_frame_sp + ret +# op_callb ENDP + + .sbttl op_mprofcallw, op_mprofcalll +# PUBLIC op_mprofcallw, op_mprofcalll +ENTRY op_mprofcalll +ENTRY op_mprofcallw + movl frame_pointer,%edx + movl (%esp),%eax + movl %eax,msf_mpc_off(%edx) + addl $5,msf_mpc_off(%edx) + call copy_stack_frame_sp + ret +# op_mprofcalll ENDP + +# END diff --git a/sr_i386/op_mprofcallsp.s b/sr_i386/op_mprofcallsp.s new file mode 100644 index 0000000..87ed220 --- /dev/null +++ b/sr_i386/op_mprofcallsp.s @@ -0,0 +1,58 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_mprofcallsp.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_mprofcallsp +# PAGE + + .DATA +.extern dollar_truth +.extern frame_pointer + + .text +.extern exfun_frame_push_dummy_frame +.extern push_tval + + .sbttl op_mprofcallspb +# PUBLIC op_mprofcallspb +ENTRY op_mprofcallspb + movl frame_pointer,%edx + movl (%esp),%eax + movl %eax,msf_mpc_off(%edx) + addl $2,msf_mpc_off(%edx) # store pc in MUMPS stack frame +doit: call exfun_frame_push_dummy_frame + pushl dollar_truth + call push_tval + addl $4,%esp + movl frame_pointer,%edx + movl msf_temps_ptr_off(%edx),%edi + ret +# op_mprofcallspb ENDP + + .sbttl op_mprofcallspw, op_mprofcallspl +# PUBLIC op_mprofcallspw, op_mprofcallspl +ENTRY op_mprofcallspw +ENTRY op_mprofcallspl + movl frame_pointer,%edx + movl (%esp),%eax + movl %eax,msf_mpc_off(%edx) + addl $5,msf_mpc_off(%edx) # store pc in MUMPS stack frame + jmp doit +# op_mprofcallspw ENDP + +# END diff --git a/sr_i386/op_mprofexfun.s b/sr_i386/op_mprofexfun.s new file mode 100644 index 0000000..f1aa874 --- /dev/null +++ b/sr_i386/op_mprofexfun.s @@ -0,0 +1,121 @@ +################################################################# +# # +# Copyright 2001, 2002 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_mprofexfun.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_mprofexfun +# PAGE + +# call op_mprofexfun with the following stack: +# +# return PC +# ret_value address +# mask +# actualcnt +# actual1 address +# actual2 address +# ... + + .DATA +.extern ERR_GTMCHECK +.extern dollar_truth +.extern frame_pointer + + .text +.extern exfun_frame_sp +.extern push_parm +.extern rts_error + +JMP_Jb = 0x0eb +JMP_Jv = 0x0e9 + +actual1 = 20 +act_cnt = 16 +mask_arg = 12 +ret_val = 8 +rtn_pc = 4 + +sav_esi = -4 +sav_ebx = -8 +sav_msf = -12 + +# PUBLIC op_mprofexfun +ENTRY op_mprofexfun + pushl %ebp + movl %esp,%ebp + pushl %esi + pushl %ebx + leal sav_msf(%ebp),%esp # establish space for locals + movl act_cnt(%ebp),%eax + addl $3,%eax + negl %eax + leal (%esp,%eax,4),%esp # establish space for temps + + movl frame_pointer,%edx + movl rtn_pc(%ebp),%eax + cmpb $JMP_Jv,(%eax) + je long + cmpb $JMP_Jb,(%eax) + je byte_off +error: pushl ERR_GTMCHECK + pushl $1 + call rts_error + addl $8,%esp + jmp retlab + +byte_off: + movl %eax,msf_mpc_off(%edx) + addl $2,msf_mpc_off(%edx) + jmp cont + +long: movl %eax,msf_mpc_off(%edx) + addl $5,msf_mpc_off(%edx) +cont: call exfun_frame_sp + movl frame_pointer,%edx + movl msf_old_frame_off(%edx),%eax + movl %eax,frame_pointer + movl %edx,sav_msf(%ebp) + leal ret_val(%ebp),%esi + movl %esp,%edi + movl act_cnt(%ebp),%eax + movl %eax,%ecx + addl $3,%ecx + REP + movsl + movl dollar_truth,%ecx + andl $1,%ecx + pushl %ecx # push $T + addl $4,%eax + pushl %eax # push total count + call push_parm # push_parm ($T, ret_value, mask, argc [,arg1, arg2, ...]); +done: movl sav_msf(%ebp),%eax + movl %eax,frame_pointer + orw $SFT_EXTFUN,msf_typ_off(%eax) + movl msf_temps_ptr_off(%eax),%edi +retlab: leal sav_ebx(%ebp),%esp + movl rtn_pc(%ebp),%edx + movl act_cnt(%ebp),%eax + addl $4,%eax + popl %ebx + popl %esi + popl %ebp + leal (%esp,%eax,4),%esp + pushl %edx + ret +# op_mprofexfun ENDP + +# END diff --git a/sr_i386/op_mprofextcall.s b/sr_i386/op_mprofextcall.s new file mode 100644 index 0000000..62865de --- /dev/null +++ b/sr_i386/op_mprofextcall.s @@ -0,0 +1,83 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_mprofextcall.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_mprofextcall +# PAGE + + .DATA +.extern ERR_GTMCHECK +.extern ERR_LABELUNKNOWN +.extern frame_pointer + + .text +.extern auto_zlink +.extern new_stack_frame_sp +.extern rts_error + +# PUBLIC op_mprofextcall +ENTRY op_mprofextcall + putframe + addl $4,%esp + popl %edx + popl %eax + cmpl $0,%eax + je l2 +l1: movl (%eax),%eax + addl mrt_curr_ptr(%edx),%eax + addl %edx,%eax + pushl %eax + pushl $0 + pushl %edx + call new_stack_frame_sp + addl $12,%esp + getframe + ret + +l2: cmpl $0,%edx + jne l4 + subl $4,%esp + pushl %esp + movl frame_pointer,%eax + pushl msf_mpc_off(%eax) + call auto_zlink + addl $8,%esp + cmpl $0,%eax + je l3 + movl %eax,%edx + popl %eax + cmpl $0,%eax + jne l1 +l3: addl $4,%esp + pushl ERR_GTMCHECK + pushl $1 + call rts_error + pushl $1 # in original m68020 code ?? + addl $8,%esp + getframe + ret + +l4: pushl ERR_LABELUNKNOWN + pushl $1 + call rts_error + addl $8,%esp + getframe + ret +# op_mprofextcall ENDP + +# END diff --git a/sr_i386/op_mprofextexfun.s b/sr_i386/op_mprofextexfun.s new file mode 100644 index 0000000..d1a6275 --- /dev/null +++ b/sr_i386/op_mprofextexfun.s @@ -0,0 +1,158 @@ +################################################################# +# # +# Copyright 2001, 2002 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_mprofextexfun.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_mprofextexfun +# PAGE + + +# call op_mprofextexfun with the following stack: +# +# return PC +# routine +# label +# ret_value address +# mask +# actualcnt +# actual1 address +# actual2 address +# ... + + .DATA +.extern ERR_FMLLSTMISSING +.extern ERR_GTMCHECK +.extern ERR_LABELUNKNOWN +.extern dollar_truth +.extern frame_pointer + + .text +.extern auto_zlink +.extern new_stack_frame_sp +.extern push_parm +.extern rts_error + +Grp5_Prefix = 0x0ff # escape for CALL_Ev opcode +CALL_Ev = 0x010 # CALL_Ev part of ModR/M byte +reg_opcode_mask = 0x038 +ISFORMAL = 0x0244 # xf_isformal*4 + +actual1 = 24 +act_cnt = 20 +mask_arg = 16 +ret_val = 12 +label_arg = 8 +routine = 4 + +sav_ebx = -4 +sav_msf = -8 + +# PUBLIC op_mprofextexfun +ENTRY op_mprofextexfun + putframe + addl $4,%esp # burn return PC + pushl %ebp + movl %esp,%ebp + pushl %ebx + leal sav_msf(%ebp),%esp # establish space for locals + movl act_cnt(%ebp),%eax + addl $3,%eax + negl %eax + leal (%esp,%eax,4),%esp # add space for temps + + movl routine(%ebp),%edx + movl label_arg(%ebp),%eax + cmpl $0,%eax + je l3 +l1: movl (%eax),%ebx + addl mrt_curr_ptr(%edx),%ebx + addl %edx,%ebx + cmpb $Grp5_Prefix,0(%ebx) + jne l6 + movb 1(%ebx),%cl + andb $reg_opcode_mask,%cl + cmpb $CALL_Ev,%cl + jne l6 + cmpl $ISFORMAL,2(%ebx) + jne l6 + pushl %ebx + pushl $0 + pushl %edx + call new_stack_frame_sp + addl $12,%esp + movl frame_pointer,%edx + movl msf_old_frame_off(%edx),%eax + movl %eax,frame_pointer + movl %edx,sav_msf(%ebp) + leal ret_val(%ebp),%esi + movl %esp,%edi + movl act_cnt(%ebp),%eax + movl %eax,%ecx + addl $3,%ecx # include: A(ret_value), mask, argc + REP + movsl + movl dollar_truth,%ecx + andl $1,%ecx + pushl %ecx + addl $4,%eax # include: $T(just pushed) plus other 3 + pushl %eax # push total count + call push_parm # push_parm ($T, ret_value, mask, argc [,arg1, arg2, ...]); + movl sav_msf(%ebp),%eax + movl %eax,frame_pointer + orw $SFT_EXTFUN,msf_typ_off(%eax) +retlab: leal sav_ebx(%ebp),%esp + movl act_cnt(%ebp),%eax + addl $5,%eax + popl %ebx + popl %ebp + leal (%esp,%eax,4),%esp + getframe + ret + +l3: cmpl $0,%edx + jne l5 + subl $4,%esp + movl %esp,%eax + pushl %eax + movl frame_pointer,%edx + pushl msf_mpc_off(%edx) + call auto_zlink + addl $8,%esp + cmpl $0,%eax + je l4 + movl %eax,%edx + popl %eax + cmpl $0,%eax + jne l1 +l4: pushl ERR_GTMCHECK + pushl $1 + call rts_error + jmp retlab + +l5: pushl ERR_LABELUNKNOWN + pushl $1 + call rts_error + jmp retlab + +l6: pushl ERR_FMLLSTMISSING + pushl $1 + call rts_error + jmp retlab + +# op_mprofextexfun ENDP + +# END diff --git a/sr_i386/op_mprofforchk1.s b/sr_i386/op_mprofforchk1.s new file mode 100644 index 0000000..f80a093 --- /dev/null +++ b/sr_i386/op_mprofforchk1.s @@ -0,0 +1,38 @@ +################################################################# +# # +# Copyright 2011 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + + .title op_mprofforchk1.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_mprofforchk1 +# Called with the stack contents: +# call return + .DATA + + .text +.extern forchkhandler + +# PUBLIC op_mprofforchk1 +ENTRY op_mprofforchk1 + + pushl 0(%esp) # throw the current return address on as an arg + call forchkhandler + addl $4, %esp + ret + +# op_mprofforchk1 ENDP + +# END diff --git a/sr_i386/op_mprofforlcldo.s b/sr_i386/op_mprofforlcldo.s new file mode 100644 index 0000000..dc0a3bd --- /dev/null +++ b/sr_i386/op_mprofforlcldo.s @@ -0,0 +1,53 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_mprofforlcldo.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_mprofforlcldo +# PAGE + + .DATA +.extern frame_pointer + + .text +.extern exfun_frame_push_dummy_frame + + .sbttl op_mprofforlcldob +# PUBLIC op_mprofforlcldob +ENTRY op_mprofforlcldob + movl frame_pointer,%edx + movl (%esp),%eax + movl %eax,msf_mpc_off(%edx) + addl $2,msf_mpc_off(%edx) # store pc in MUMPS stack frame +doit: call exfun_frame_push_dummy_frame + movl frame_pointer,%edx + movl msf_temps_ptr_off(%edx),%edi + ret +# op_mprofforlcldob ENDP + + .sbttl op_mprofforlcldow, op_mprofforlcldol +# PUBLIC op_mprofforlcldow, op_mprofforlcldol +ENTRY op_mprofforlcldol +ENTRY op_mprofforlcldow + movl frame_pointer,%edx + movl (%esp),%eax + movl %eax,msf_mpc_off(%edx) + addl $5,msf_mpc_off(%edx) # store pc in MUMPS stack frame + jmp doit +# op_mprofforlcldol ENDP + +# END diff --git a/sr_i386/op_mproflinefetch.s b/sr_i386/op_mproflinefetch.s new file mode 100644 index 0000000..3e456ba --- /dev/null +++ b/sr_i386/op_mproflinefetch.s @@ -0,0 +1,45 @@ +################################################################# +# # +# Copyright 2001, 2011 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_mproflinefetch.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_mproflinefetch +# PAGE + + .DATA +.extern frame_pointer + + .text +.extern gtm_fetch +.extern stack_leak_check +.extern pcurrpos + +# PUBLIC op_mproflinefetch +ENTRY op_mproflinefetch + movl frame_pointer,%eax + popl msf_mpc_off(%eax) + call gtm_fetch + call pcurrpos + popl %eax # popping generated code args off stack before leak check + leal (%esp,%eax,4),%esp + call stack_leak_check + movl frame_pointer,%eax + pushl msf_mpc_off(%eax) + ret +# op_mproflinefetch ENDP + +# END diff --git a/sr_i386/op_mproflinestart.s b/sr_i386/op_mproflinestart.s new file mode 100644 index 0000000..fd6c16f --- /dev/null +++ b/sr_i386/op_mproflinestart.s @@ -0,0 +1,38 @@ +################################################################# +# # +# Copyright 2001, 2011 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_mproflinestart.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_mproflinestart +# PAGE + + .DATA +.extern frame_pointer + + .text +.extern pcurrpos + +# PUBLIC op_mproflinestart +ENTRY op_mproflinestart + movl frame_pointer,%edx + movl (%esp),%eax + movl %eax,msf_mpc_off(%edx) + call pcurrpos + ret +# op_mproflinestart ENDP + +# END diff --git a/sr_i386/op_neg.s b/sr_i386/op_neg.s new file mode 100644 index 0000000..20692a6 --- /dev/null +++ b/sr_i386/op_neg.s @@ -0,0 +1,64 @@ +################################################################# +# # +# Copyright 2001, 2008 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_neg.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "mval_def.si" + + .sbttl op_neg +# PAGE + + .text +# +# op_neg ( mval *u, mval *v ) : u = -v +# +# edx - source mval = &v +# eax - destination mval = &u + +.extern s2n +.extern underr + +# PUBLIC op_neg +ENTRY op_neg + pushl %eax + mv_force_defined %edx, isdefined + popl %eax + mv_if_number %edx, numer + pushl %eax + pushl %edx + call s2n + popl %edx + popl %eax +numer: mv_if_notint %edx, float + movw $mval_m_int,mval_w_mvtype(%eax) + movl mval_l_m1(%edx),%edx + negl %edx + movl %edx,mval_l_m1(%eax) + ret + +float: pushl %ebx # need a temp register + movw $mval_m_nm,mval_w_mvtype(%eax) + movb mval_b_exp(%edx),%bl + xorb $mval_esign_mask,%bl # flip the sign bit + movb %bl,mval_b_exp(%eax) + movl mval_l_m0(%edx),%ebx + movl %ebx,mval_l_m0(%eax) + movl mval_l_m1(%edx),%ebx + movl %ebx,mval_l_m1(%eax) + popl %ebx + ret +# op_neg ENDP + +#END diff --git a/sr_i386/op_numcmp.s b/sr_i386/op_numcmp.s new file mode 100644 index 0000000..0d7a5e0 --- /dev/null +++ b/sr_i386/op_numcmp.s @@ -0,0 +1,44 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_numcmp.s + .sbttl op_numcmp + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + +# op_numcmp calls numcmp to compare two mvals +# +# entry: +# eax mval *u +# edx mval *v +# +# exit: +# condition codes set according to value of +# numcmp (u, v) + + .text +.extern numcmp + +# PUBLIC op_numcmp +ENTRY op_numcmp + pushl %edx + pushl %eax + call numcmp + addl $8,%esp # restore stack + cmpl $0,%eax # set flags according to result from numcmp + ret +# op_numcmp ENDP + +# END diff --git a/sr_i386/op_pattern.s b/sr_i386/op_pattern.s new file mode 100644 index 0000000..fbcb215 --- /dev/null +++ b/sr_i386/op_pattern.s @@ -0,0 +1,50 @@ +################################################################# +# # +# Copyright 2001, 2002 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_pattern.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "mval_def.si" + + .sbttl op_pattern +# PAGE + + .text +.extern do_patfixed +.extern do_pattern + +# PUBLIC op_pattern +ENTRY op_pattern + pushl %edx + pushl %eax + movl mval_a_straddr(%edx),%eax + # + # This is an array of unaligned ints. If the first word is zero, then call do_pattern + # instead of do_patfixed. Only the low order byte is significant and so it is the only + # one we need to test. We would do this in assembly because (1) we need the assmembly + # routine anyway to save the return value into $TEST and (2) it saves an extra level of + # call linkage at the C level to do the decision here. + # + cmpb $0,(%eax) # little endian compare of low order byte + je l1 + call do_patfixed + jmp l2 + +l1: call do_pattern +l2: addl $8,%esp + cmpl $0,%eax + ret +# op_pattern ENDP + +# END diff --git a/sr_i386/op_restartpc.s b/sr_i386/op_restartpc.s new file mode 100644 index 0000000..0e06038 --- /dev/null +++ b/sr_i386/op_restartpc.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_restartpc.s + .sbttl op_restartpc + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" +.include "g_msf.si" + + .DATA +.extern restart_pc +.extern restart_ctxt +.extern frame_pointer + + .text +# PUBLIC op_restartpc +ENTRY op_restartpc + movl (%esp),%eax + subl $6,%eax + movl %eax,restart_pc + movl frame_pointer,%eax + movl msf_ctxt_off(%eax),%edx + movl %edx,restart_ctxt + ret +# op_restartpc ENDP + +# END diff --git a/sr_i386/op_retarg.s b/sr_i386/op_retarg.s new file mode 100644 index 0000000..e07e3c7 --- /dev/null +++ b/sr_i386/op_retarg.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001, 2010 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_retarg.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_retarg +# PAGE + + .DATA +.extern frame_pointer + + .text +.extern unw_retarg + +# PUBLIC op_retarg +ENTRY op_retarg + movl %edx,(%esp) # Reuse return point on stack (not needed) + pushl %eax + call unw_retarg + addl $8,%esp + getframe + ret +# op_retarg ENDP + +# END diff --git a/sr_i386/op_sorts_after.s b/sr_i386/op_sorts_after.s new file mode 100644 index 0000000..a20bbc6 --- /dev/null +++ b/sr_i386/op_sorts_after.s @@ -0,0 +1,51 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_sorts_after.s + .sbttl op_sorts_after + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + +# op_sorts_after.s 80386 +# +# op_sorts_after(mval *mval1, *mval2) +# Call sorts_after() to determine whether mval1 comes after mval2 +# in sorting order. Use alternate local collation sequence if +# present. +# +# entry: +# eax mval *mval1 +# edx mval *mval2 +# +# Sets condition flags and returns in eax: +## 1 mval1 > mval2 +## 0 mval1 = mval2 +## -1 mval1 < mval2 +# + + .text +.extern sorts_after + +# PUBLIC op_sorts_after +ENTRY op_sorts_after + pushl %edx + pushl %eax + call sorts_after + addl $8,%esp # restore stack + cmpl $0,%eax # set flags according to result from + ret # sorts_after. +# op_sorts_after ENDP + +# END diff --git a/sr_i386/op_startintrrpt.s b/sr_i386/op_startintrrpt.s new file mode 100644 index 0000000..3bb32fa --- /dev/null +++ b/sr_i386/op_startintrrpt.s @@ -0,0 +1,48 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_startintrrpt.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl op_startintrrpt +# PAGE + + .DATA +.extern frame_pointer +.extern neterr_pending + + .text +.extern gvcmz_neterr +.extern async_action +.extern outofband_clear + +# PUBLIC op_startintrrpt +ENTRY op_startintrrpt + putframe + cmpb $0,neterr_pending + je l1 + call outofband_clear + pushl $0 + call gvcmz_neterr + addl $4,%esp +l1: pushl $1 + call async_action + addl $8,%esp # 4 bytes to burn return pc, 4 more to remove arg to async_action + getframe + ret +# op_startintrrpt ENDP + +# END diff --git a/sr_i386/op_sto.s b/sr_i386/op_sto.s new file mode 100644 index 0000000..0c3e905 --- /dev/null +++ b/sr_i386/op_sto.s @@ -0,0 +1,57 @@ +################################################################# +# # +# Copyright 2001, 2009 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_sto.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "mval_def.si" + + .sbttl op_sto +# PAGE + + .DATA +.extern literal_null +.extern undef_inhibit + + .text +.extern underr + +# PUBLIC op_sto +ENTRY op_sto + mv_if_notdefined %edx, b +a: pushl %edi + pushl %esi + movl $mval_byte_len,%ecx + movl %edx,%esi + movl %eax,%edi + REP + movsb + andw $~mval_m_aliascont, mval_w_mvtype(%eax) # Don't propagate alias container flag + popl %esi + popl %edi + ret + +b: cmpb $0,undef_inhibit + je clab + leal literal_null,%edx + jmp a + +clab: pushl %edx + call underr + addl $4,%esp + ret + +# op_sto ENDP + +# END diff --git a/sr_i386/op_zhelp.s b/sr_i386/op_zhelp.s new file mode 100644 index 0000000..1be83a3 --- /dev/null +++ b/sr_i386/op_zhelp.s @@ -0,0 +1,37 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title op_zhelp.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + +# PAGE + + .DATA +.extern frame_pointer + + .text +.extern op_zhelp_xfr + +# PUBLIC op_zhelp +ENTRY op_zhelp + movl frame_pointer,%edx + popl msf_mpc_off(%edx) + call op_zhelp_xfr + getframe + ret +# op_zhelp ENDP + +# END diff --git a/sr_i386/opp_break.s b/sr_i386/opp_break.s new file mode 100644 index 0000000..aaa9611 --- /dev/null +++ b/sr_i386/opp_break.s @@ -0,0 +1,38 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_break.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_break +# PAGE + + .DATA +.extern frame_pointer + + .text +.extern op_break + +# PUBLIC opp_break +ENTRY opp_break + putframe + addl $4,%esp + call op_break + getframe + ret +# opp_break ENDP + +# END diff --git a/sr_i386/opp_commarg.s b/sr_i386/opp_commarg.s new file mode 100644 index 0000000..78a5080 --- /dev/null +++ b/sr_i386/opp_commarg.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_commarg.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_commarg +# PAGE + + .DATA +.extern frame_pointer + + .text +.extern op_commarg + +# PUBLIC opp_commarg +ENTRY opp_commarg + putframe + addl $4,%esp + call op_commarg + addl $8,%esp + getframe + ret +# opp_commarg ENDP + +# END diff --git a/sr_i386/opp_dmode.s b/sr_i386/opp_dmode.s new file mode 100644 index 0000000..03f5013 --- /dev/null +++ b/sr_i386/opp_dmode.s @@ -0,0 +1,38 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_dmode.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_dmode +# PAGE + + .DATA +.extern frame_pointer + + .text +.extern op_dmode + +# PUBLIC opp_dmode +ENTRY opp_dmode + putframe + addl $4,%esp + call op_dmode + getframe + ret +# opp_dmode ENDP + +# END diff --git a/sr_i386/opp_hardret.s b/sr_i386/opp_hardret.s new file mode 100644 index 0000000..36db863 --- /dev/null +++ b/sr_i386/opp_hardret.s @@ -0,0 +1,38 @@ +################################################################# +# # +# Copyright 2001, 2007 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_hardret.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_hardret +# PAGE + + .DATA +.extern frame_pointer + + .text +.extern op_hardret + +# PUBLIC opp_hardret +ENTRY opp_hardret + putframe + addl $4,%esp + call op_hardret + getframe + ret +# opp_hardret ENDP + +# END diff --git a/sr_i386/opp_inddevparms.s b/sr_i386/opp_inddevparms.s new file mode 100644 index 0000000..3de8533 --- /dev/null +++ b/sr_i386/opp_inddevparms.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_inddevparms.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_inddevparms +# PAGE + + .DATA +.extern frame_pointer # /* :DWORD */ + + .text +.extern op_inddevparms + +# PUBLIC opp_inddevparms +ENTRY opp_inddevparms # /* PROC */ + putframe + addl $4,%esp + call op_inddevparms + addl $12,%esp + getframe + ret +# opp_inddevparms ENDP + +# END diff --git a/sr_i386/opp_indfnname.s b/sr_i386/opp_indfnname.s new file mode 100644 index 0000000..08b1527 --- /dev/null +++ b/sr_i386/opp_indfnname.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_indfnname.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_indfnname +# PAGE + + .DATA +.extern frame_pointer + + .text +.extern op_indfnname + +# PUBLIC opp_indfnname +ENTRY opp_indfnname + putframe + addl $4,%esp + call op_indfnname + addl $12,%esp + getframe + ret +# opp_indfnname ENDP + +# END diff --git a/sr_i386/opp_indfun.s b/sr_i386/opp_indfun.s new file mode 100644 index 0000000..d145e88 --- /dev/null +++ b/sr_i386/opp_indfun.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_indfun.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_indfun +# PAGE + + .DATA +.extern frame_pointer # /* :DWORD */ + + .text +.extern op_indfun + +# PUBLIC opp_indfun +ENTRY opp_indfun # /* PROC */ + putframe + addl $4,%esp + call op_indfun + addl $12,%esp + getframe + ret +# opp_indfun ENDP + +# END diff --git a/sr_i386/opp_indget.s b/sr_i386/opp_indget.s new file mode 100644 index 0000000..a92b867 --- /dev/null +++ b/sr_i386/opp_indget.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_indget.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_indget +# PAGE + + .DATA +.extern frame_pointer # /* :DWORD */ + + .text +.extern op_indget + +# PUBLIC opp_indget +ENTRY opp_indget # /* PROC */ + putframe + addl $4,%esp + call op_indget + addl $12,%esp + getframe + ret +# opp_indget ENDP + +# END diff --git a/sr_i386/opp_indglvn.s b/sr_i386/opp_indglvn.s new file mode 100644 index 0000000..70a1f86 --- /dev/null +++ b/sr_i386/opp_indglvn.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_indglvn.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_indglvn +# PAGE + + .DATA +.extern frame_pointer # /* :DWORD */ + + .text +.extern op_indglvn + +# PUBLIC opp_indglvn +ENTRY opp_indglvn # /* PROC */ + putframe + addl $4,%esp + call op_indglvn + addl $8,%esp + getframe + ret +# opp_indglvn ENDP + +# END diff --git a/sr_i386/opp_indincr.s b/sr_i386/opp_indincr.s new file mode 100644 index 0000000..df3d2f2 --- /dev/null +++ b/sr_i386/opp_indincr.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2004 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_indincr.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_indincr +# PAGE + + .DATA +.extern frame_pointer # /* :DWORD */ + + .text +.extern op_indincr + +# PUBLIC opp_indincr +ENTRY opp_indincr # /* PROC */ + putframe + addl $4,%esp + call op_indincr + addl $12,%esp + getframe + ret +# opp_indincr ENDP + +# END diff --git a/sr_i386/opp_indlvadr.s b/sr_i386/opp_indlvadr.s new file mode 100644 index 0000000..def668b --- /dev/null +++ b/sr_i386/opp_indlvadr.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_indlvadr.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_indlvadr +# PAGE + + .DATA +.extern frame_pointer # /* :DWORD */ + + .text +.extern op_indlvadr + +# PUBLIC opp_indlvadr +ENTRY opp_indlvadr # /* PROC */ + putframe + addl $4,%esp + call op_indlvadr + addl $4,%esp + getframe + ret +# opp_indlvadr ENDP + +# END diff --git a/sr_i386/opp_indlvarg.s b/sr_i386/opp_indlvarg.s new file mode 100644 index 0000000..2197110 --- /dev/null +++ b/sr_i386/opp_indlvarg.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_indlvarg.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_indlvarg +# PAGE + + .DATA +.extern frame_pointer # /* :DWORD */ + + .text +.extern op_indlvarg + +# PUBLIC opp_indlvarg +ENTRY opp_indlvarg # /* PROC */ + putframe + addl $4,%esp + call op_indlvarg + addl $8,%esp + getframe + ret +# opp_indlvarg ENDP + +# END diff --git a/sr_i386/opp_indlvnamadr.s b/sr_i386/opp_indlvnamadr.s new file mode 100644 index 0000000..31adca5 --- /dev/null +++ b/sr_i386/opp_indlvnamadr.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_indlvnamadr.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_indlvnamadr +# PAGE + + .DATA +.extern frame_pointer # /* :DWORD */ + + .text +.extern op_indlvnamadr + +# PUBLIC opp_indlvnamadr +ENTRY opp_indlvnamadr # /* PROC */ + putframe + addl $4,%esp + call op_indlvnamadr + addl $4,%esp + getframe + ret +# opp_indlvnamadr ENDP + +# END diff --git a/sr_i386/opp_indmerge.s b/sr_i386/opp_indmerge.s new file mode 100644 index 0000000..cb2550a --- /dev/null +++ b/sr_i386/opp_indmerge.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_indmerge.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_indmerge +# PAGE + + .DATA +.extern frame_pointer # /* :DWORD */ + + .text +.extern op_indmerge + +# PUBLIC opp_indmerge +ENTRY opp_indmerge # /* PROC */ + putframe + addl $4,%esp + call op_indmerge + addl $8,%esp + getframe + ret +# opp_indmerge ENDP + +# END diff --git a/sr_i386/opp_indo2.s b/sr_i386/opp_indo2.s new file mode 100644 index 0000000..652bf79 --- /dev/null +++ b/sr_i386/opp_indo2.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_indo2.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_indo2 +# PAGE + + .DATA +.extern frame_pointer # /* :DWORD */ + + .text +.extern op_indo2 + +# PUBLIC opp_indo2 +ENTRY opp_indo2 # /* PROC */ + putframe + addl $4,%esp + call op_indo2 + addl $12,%esp + getframe + ret +# opp_indo2 ENDP + +# END diff --git a/sr_i386/opp_indpat.s b/sr_i386/opp_indpat.s new file mode 100644 index 0000000..c6f719c --- /dev/null +++ b/sr_i386/opp_indpat.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_indpat.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_indpat +# PAGE + + .DATA +.extern frame_pointer # /* :DWORD */ + + .text +.extern op_indpat + +# PUBLIC opp_indpat +ENTRY opp_indpat # /* PROC */ + putframe + addl $4,%esp + call op_indpat + addl $8,%esp + getframe + ret +# opp_indpat ENDP + +# END diff --git a/sr_i386/opp_indrzshow.s b/sr_i386/opp_indrzshow.s new file mode 100644 index 0000000..b16f517 --- /dev/null +++ b/sr_i386/opp_indrzshow.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_indrzshow.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_indrzshow +# PAGE + + .DATA +.extern frame_pointer # /* :DWORD */ + + .text +.extern op_indrzshow + +# PUBLIC opp_indrzshow +ENTRY opp_indrzshow # /* PROC */ + putframe + addl $4,%esp + call op_indrzshow + addl $8,%esp + getframe + ret +# opp_indrzshow ENDP + +# END diff --git a/sr_i386/opp_indset.s b/sr_i386/opp_indset.s new file mode 100644 index 0000000..f8d4a59 --- /dev/null +++ b/sr_i386/opp_indset.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_indset.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_indset +# PAGE + + .DATA +.extern frame_pointer # /* :DWORD */ + + .text +.extern op_indset + +# PUBLIC opp_indset +ENTRY opp_indset # /* PROC */ + putframe + addl $4,%esp + call op_indset + addl $8,%esp + getframe + ret +# opp_indset ENDP + +# END diff --git a/sr_i386/opp_indtext.s b/sr_i386/opp_indtext.s new file mode 100644 index 0000000..60a3fdc --- /dev/null +++ b/sr_i386/opp_indtext.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001, 2002 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_indtext.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_indtext +# PAGE + + .DATA +.extern frame_pointer # /* :DWORD */ + + .text +.extern op_indtext + +# PUBLIC opp_indtext +ENTRY opp_indtext # /* PROC */ + putframe + addl $4,%esp # burn return PC + call op_indtext + addl $16,%esp # remove args from stack + getframe + ret +# opp_indtext ENDP + +# END diff --git a/sr_i386/opp_iretmval.s b/sr_i386/opp_iretmval.s new file mode 100644 index 0000000..99d92aa --- /dev/null +++ b/sr_i386/opp_iretmval.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001, 2007 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_iretmval.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_iretmval +# PAGE + + .DATA +.extern frame_pointer # /* :DWORD */ + + .text +.extern op_iretmval + +# PUBLIC opp_iretmval +ENTRY opp_iretmval # /* PROC */ + putframe + addl $4,%esp + call op_iretmval + addl $4,%esp + getframe + ret +# opp_iretmval ENDP + +# END diff --git a/sr_i386/opp_newintrinsic.s b/sr_i386/opp_newintrinsic.s new file mode 100644 index 0000000..9b01508 --- /dev/null +++ b/sr_i386/opp_newintrinsic.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_newintrinsic.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_newintrinsic +# PAGE + + .DATA +.extern frame_pointer # /* :DWORD */ + + .text +.extern op_newintrinsic + +# PUBLIC opp_newintrinsic +ENTRY opp_newintrinsic # /* PROC */ + putframe + addl $4,%esp + call op_newintrinsic + addl $4,%esp + getframe + ret +# opp_newintrinsic ENDP + +# END diff --git a/sr_i386/opp_newvar.s b/sr_i386/opp_newvar.s new file mode 100644 index 0000000..f5c7695 --- /dev/null +++ b/sr_i386/opp_newvar.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_newvar.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_newvar +# PAGE + + .DATA +.extern frame_pointer # /* :DWORD */ + + .text +.extern op_newvar + +# PUBLIC opp_newvar +ENTRY opp_newvar # /* PROC */ + putframe + addl $4,%esp + call op_newvar + addl $4,%esp + getframe + ret +# opp_newvar ENDP + +# END diff --git a/sr_i386/opp_ret.s b/sr_i386/opp_ret.s new file mode 100644 index 0000000..b10e404 --- /dev/null +++ b/sr_i386/opp_ret.s @@ -0,0 +1,37 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_ret.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_ret +# PAGE + + .DATA +.extern frame_pointer # /* :DWORD */ + + .text +.extern op_unwind + +# PUBLIC opp_ret +ENTRY opp_ret # /* PROC */ + addl $4,%esp + call op_unwind + getframe + ret +# opp_ret ENDP + +#END diff --git a/sr_i386/opp_rterror.s b/sr_i386/opp_rterror.s new file mode 100644 index 0000000..4b7a532 --- /dev/null +++ b/sr_i386/opp_rterror.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001, 2002 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_rterror.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_rterror +# PAGE + + .DATA +.extern frame_pointer # /* :DWORD */ + + .text +.extern op_rterror + +# PUBLIC opp_rterror +ENTRY opp_rterror # /* PROC */ + putframe + addl $4,%esp # burn return PC + call op_rterror + addl $8,%esp # remove args from stack + getframe + ret +# opp_rterror ENDP + +# END diff --git a/sr_i386/opp_svput.s b/sr_i386/opp_svput.s new file mode 100644 index 0000000..198e008 --- /dev/null +++ b/sr_i386/opp_svput.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001, 2002 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_svput.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_svput +# PAGE + + .DATA +.extern frame_pointer # /* :DWORD */ + + .text +.extern op_svput + +# PUBLIC opp_svput +ENTRY opp_svput # /* PROC */ + putframe + addl $4,%esp # burn return PC + call op_svput + addl $8,%esp # remove args from stack + getframe + ret +# opp_svput ENDP + +# END diff --git a/sr_i386/opp_tcommit.s b/sr_i386/opp_tcommit.s new file mode 100644 index 0000000..2345c86 --- /dev/null +++ b/sr_i386/opp_tcommit.s @@ -0,0 +1,38 @@ +################################################################# +# # +# Copyright 2001, 2007 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_tcommit.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_tcommit +# PAGE + + .DATA +.extern frame_pointer # /* :DWORD */ + + .text +.extern op_tcommit + +# PUBLIC opp_tcommit +ENTRY opp_tcommit # /* PROC */ + putframe + addl $4,%esp + call op_tcommit + getframe + ret +# opp_tcommit ENDP + +# END diff --git a/sr_i386/opp_trestart.s b/sr_i386/opp_trestart.s new file mode 100644 index 0000000..40af080 --- /dev/null +++ b/sr_i386/opp_trestart.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001, 2007 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_trestart.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_trestart +# PAGE + + .DATA +.extern frame_pointer # /*:DWORD */ + + .text +.extern op_trestart + +# PUBLIC opp_trestart +ENTRY opp_trestart # /* PROC */ + putframe + addl $4,%esp + call op_trestart + addl $4,%esp + getframe + ret +# opp_trestart ENDP + +# END diff --git a/sr_i386/opp_trollback.s b/sr_i386/opp_trollback.s new file mode 100644 index 0000000..a1ddb6f --- /dev/null +++ b/sr_i386/opp_trollback.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2001, 2003 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_trollback.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_trollback +# PAGE + + .DATA +.extern frame_pointer # /*:DWORD */ + + .text +.extern op_trollback + +# PUBLIC opp_trollback +ENTRY opp_trollback # /* PROC */ + putframe + addl $4,%esp + call op_trollback + addl $4,%esp + getframe + ret +# opp_trollback ENDP + +# END diff --git a/sr_i386/opp_tstart.s b/sr_i386/opp_tstart.s new file mode 100644 index 0000000..7597225 --- /dev/null +++ b/sr_i386/opp_tstart.s @@ -0,0 +1,45 @@ +################################################################# +# # +# Copyright 2001, 2010 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_tstart.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_tstart +# PAGE + + .DATA +.extern frame_pointer # /* :DWORD */ + + .text +.extern op_tstart + +# PUBLIC opp_tstart +ENTRY opp_tstart # /* PROC */ + putframe + movl $0,%eax # put arg0 over return call address since + movl %eax,(%esp) # .. we dont need it: NOT an implicit tstart + call op_tstart + movl 12(%esp),%eax # get number of variables to preserve + cmpl $0,%eax # -1 = not restartable, + jge l1 # -2 = preserve all variables + movl $0,%eax +l1: addl $4,%eax # total args to op_tstart == preservecnt + 4 + leal (%esp,%eax,4),%esp + getframe + ret +# opp_tstart ENDP + +# END diff --git a/sr_i386/opp_xnew.s b/sr_i386/opp_xnew.s new file mode 100644 index 0000000..2628620 --- /dev/null +++ b/sr_i386/opp_xnew.s @@ -0,0 +1,40 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_xnew.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_xnew +# PAGE + + .DATA +.extern frame_pointer + + .text +.extern op_xnew + +# PUBLIC opp_xnew +ENTRY opp_xnew + putframe + addl $4,%esp + call op_xnew + popl %eax + leal (%esp,%eax,4),%esp + getframe + ret +# opp_xnew ENDP + +# END diff --git a/sr_i386/opp_zcont.s b/sr_i386/opp_zcont.s new file mode 100644 index 0000000..d1a28e3 --- /dev/null +++ b/sr_i386/opp_zcont.s @@ -0,0 +1,38 @@ +################################################################# +# # +# Copyright 2001, 2007 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_zcont.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_zcont +# PAGE + + .DATA +.extern frame_pointer + + .text +.extern op_zcont + +# PUBLIC opp_zcont +ENTRY opp_zcont + putframe + addl $4,%esp + call op_zcont + getframe + ret +# opp_zcont ENDP + +# END diff --git a/sr_i386/opp_zg1.s b/sr_i386/opp_zg1.s new file mode 100644 index 0000000..02541be --- /dev/null +++ b/sr_i386/opp_zg1.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2010 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_zg1.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_zg1 +# PAGE + + .DATA +.extern frame_pointer + + .text +.extern op_zg1 + +# PUBLIC opp_zg1 +ENTRY opp_zg1 + putframe + addl $4,%esp # burn return pc + call op_zg1 + addl $4,%esp # burn passed-in arg + getframe + ret +# opp_zg1 ENDP + +# END diff --git a/sr_i386/opp_zgoto.s b/sr_i386/opp_zgoto.s new file mode 100644 index 0000000..d70201c --- /dev/null +++ b/sr_i386/opp_zgoto.s @@ -0,0 +1,39 @@ +################################################################# +# # +# Copyright 2010, 2011 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title opp_zgoto.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl opp_zgoto +# PAGE + + .DATA +.extern frame_pointer + + .text +.extern op_zgoto + +# PUBLIC opp_zgoto +ENTRY opp_zgoto + putframe + addl $4,%esp # burn return pc + call op_zgoto + addl $16,%esp # burn passed in 4 args + getframe + ret +# opp_zgoto ENDP + +# END diff --git a/sr_i386/pseudo_ret.s b/sr_i386/pseudo_ret.s new file mode 100644 index 0000000..ccc2244 --- /dev/null +++ b/sr_i386/pseudo_ret.s @@ -0,0 +1,29 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title pseudo_ret.s + .sbttl pseudo_ret + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + + .text +.extern opp_ret + +# PUBLIC pseudo_ret +ENTRY pseudo_ret + call opp_ret +# pseudo_ret ENDP + +# END diff --git a/sr_i386/zbreaksp.h b/sr_i386/zbreaksp.h new file mode 100644 index 0000000..74bb19e --- /dev/null +++ b/sr_i386/zbreaksp.h @@ -0,0 +1,29 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +typedef unsigned char zb_code; +#define ZB_CODE_MASK 0xff +#define INST_TYPE zb_code + +/* The ZBreak command operates by finding the generated code for the op_linestart or op_linefetch for the source + * line in question and changing the offset in the transfer table load address instruction from the op_linestart or + * op_linefetch offset to the appropriate zbreak functionality opcode offset. + * In some platforms(IA64 and ZOS) since the INSTRUCTION LAYOUT is complex we need following + * macros for instruction manipulation. + * EXTRACT_OFFSET_TO_M_OPCODE + * FIX_OFFSET_WITH_ZBREAK_OFFSET + * EXTRACT_AND_UPDATE_INST + * These macros are called only when COMPLEX_INSTRUCTION_UPDATE is defined + * If COMPLEX_INSTRUCTION_UPDATE is not defined portable code in the caller of these macros + * is invoked. + */ + +#undef COMPLEX_INSTRUCTION_UPDATE diff --git a/sr_i386/zl_lab_err.s b/sr_i386/zl_lab_err.s new file mode 100644 index 0000000..9e1f703 --- /dev/null +++ b/sr_i386/zl_lab_err.s @@ -0,0 +1,40 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# PAGE ,132 + .title zl_lab_err.s + +# .386 +# .MODEL FLAT, C + +.include "linkage.si" + .INCLUDE "g_msf.si" + + .sbttl zl_lab_err +# PAGE + + .DATA +.extern ERR_LABELUNKNOWN +.extern frame_pointer + + .text +.extern op_rterror + +# PUBLIC zl_lab_err +ENTRY zl_lab_err + pushl $0 + pushl ERR_LABELUNKNOWN + call op_rterror + addl $8,%esp + getframe + ret +# zl_lab_err ENDP + +# END diff --git a/sr_linux/arch.gtc b/sr_linux/arch.gtc new file mode 100644 index 0000000..a85ce03 --- /dev/null +++ b/sr_linux/arch.gtc @@ -0,0 +1,12 @@ +################################################################# +# # +# Copyright 2001 Sanchez Computer Associates, Inc. # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# +arch="linux" +export arch diff --git a/sr_linux/caller_id.c b/sr_linux/caller_id.c new file mode 100644 index 0000000..1c2c3aa --- /dev/null +++ b/sr_linux/caller_id.c @@ -0,0 +1,75 @@ +/**************************************************************** + * * + * Copyright 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* caller_id routine called from CRIT_TRACE macro to + return the return address of our caller allowing CRIT_TRACE + (used in various semaphore routines) to determine who was + calling those semaphore routines and for what purpose and + when. This is a system dependent type of operation and is + generally implemented in assembly language. + Presently 32bit linux system has its own implementation in + assembly. Similar implementation will not work on x86_64 + since register rbp is also used as M Frame pointer in its + assembly files. + This particular implementation will work only on Linux x86_64 system + due to its dependency on "backtrace" function call which is not + available on all Unix flovours.*/ + +#include "mdef.h" + +#include +#include + +#include "gtm_stdlib.h" +#include "caller_id.h" + +#define MAX_TRACE_DEPTH 3 +/*We need the callers caller of caller_id */ +#define RETURN_ADDRESS_DEPTH 2 + +GBLREF boolean_t blocksig_initialized; +GBLREF sigset_t block_sigsent; + +static boolean_t caller_id_reent = FALSE; /* If ever true, our helper gets a lobotomy */ + +caddr_t caller_id(void) +{ + void *trace[MAX_TRACE_DEPTH]; + int trace_size; + sigset_t savemask; + + /* We cannot let this routine nest itself due to the impolite things that + occur when the exception routines get re-entered so just play dead. + */ + if (caller_id_reent) + return (caddr_t)-1; + /* When backtrace is processing and a signal occurs, there is the potential for a deadlock -- waiting on a + * lock that this process already holds. A work around is to temporarily block signals (SIGINT, SIGQUIT, + * SIGTERM, SIGTSTP, SIGCONT, SIGALRM) and then restore them after the backtrace call returns. + */ + /* It is possible for this routine to be invoked during process startup (as part of err_init()) so + * block_sigsent could not be initialized yet. Therefore this does not have an assert(blocksig_initialized) + * that similar code in other places (e.g. dollarh.c) has. + */ + if (blocksig_initialized) + sigprocmask(SIG_BLOCK, &block_sigsent, &savemask); + caller_id_reent = TRUE; + trace_size = backtrace(trace, MAX_TRACE_DEPTH); + caller_id_reent = FALSE; + if (blocksig_initialized) + sigprocmask(SIG_SETMASK, &savemask, NULL); + +/* backtrace will return call stack with address.*/ + if (trace_size >= RETURN_ADDRESS_DEPTH) + return (caddr_t)trace[RETURN_ADDRESS_DEPTH]; + else + return NULL; +} diff --git a/sr_linux/genexport.csh b/sr_linux/genexport.csh new file mode 100644 index 0000000..fcdbc8e --- /dev/null +++ b/sr_linux/genexport.csh @@ -0,0 +1,36 @@ +################################################################# +# # +# Copyright 2001, 2010 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +############################################################################################ +# +# genexport.csh - to generate the linker script *.export to export +# all call-in related symbols from libgtmshr.so +# Argument +# $1 - The pathname of a .exp file that list out all symbols to be exposed +# $2 - output verstion script file to be specified with ld --version-script. +# +# Example output: +# { +# global: +# gtm_ci; +# gtm_exit; +# gtm_zstatus; +# local: +# *; +# }; +############################################################################################ + +echo "{" >$2 +echo "global:" >>$2 +sed 's/\(.*\)/ \1;/g' $1 >>$2 +echo "local:" >>$2 +echo " *;" >>$2 +echo "};" >>$2 diff --git a/sr_linux/gtm_env_sp.csh b/sr_linux/gtm_env_sp.csh new file mode 100644 index 0000000..47297df --- /dev/null +++ b/sr_linux/gtm_env_sp.csh @@ -0,0 +1,305 @@ +################################################################# +# # +# Copyright 2001, 2011 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# +# +########################################################################################## +# +# gtm_env_sp.csh - environment variable values and aliases specific to Linux +# +########################################################################################## +# + + +# Once the environment variables have been initialized, be careful not to +# re-initialize them for each subshell (i.e., don't undo any explicit changes that +# have been made since the last version change). + +set platform_name = `uname | sed 's/-//g' | tr '[A-Z]' '[a-z]'` +set mach_type = `uname -m` + +### Sanitize platform_name and match_type +# Cygwin adds the Windows version e.g. uname = CYGWIN_NT-5.1 +set platform_only = `echo $platform_name | sed 's/_.*//'` + +# sanitize i386 thru i686 to one option, do not use this to set build optimizations! +if ( "linux" == $platform_name ) then + set mach_type = `uname -m | sed 's/i[3456]86/ia32/' ` +endif + +# default to 64bit builds when object mode is not set +if (!($?OBJECT_MODE)) then + setenv OBJECT_MODE 64 +endif + +### 64bit vs 32bit builds +# The only 32 bit targets are cygwin, ia32 and x86_64 with specific settings +if ( ( "ia32" == $mach_type ) || ( "cywgin" == $platform_only ) ) then + setenv gt_build_type 32 +# build 32 bit on x86_64 when $gtm_inc/x86_64.h does not exist with comlist.csh OR when OBJECT_MODE is set to 32 with comlist.mk +else if ( "x86_64" == $mach_type && ((! -e $gtm_inc/x86_64.h && "inc" == "${gtm_inc:t}") || "32" == $OBJECT_MODE)) then + setenv gt_build_type 32 +else + setenv gt_build_type 64 + setenv gt_ld_m_shl_options "-shared" +endif + +if ( $?gtm_version_change == "1" ) then + + # Compiler selections: + # + + if !( $?gtm_linux_compiler ) then + setenv gtm_linux_compiler gcc + endif + + if ( "ia64" == $mach_type ) then + if ( "gcc" != $gtm_linux_compiler ) then + if (-r /usr/bin/icc) then + setenv gt_cc_compiler "icc -i-static" # name of C compiler + endif + endif + endif + + # Archiver definitions: + # GNU ar q equals r, S prevents generating the symbol table but + # requires ranlib before linking + + setenv gt_ar_option_create "qSv" # quick, verbose + setenv gt_ar_option_update "rv" # replace, verbose + setenv gt_ar_option_delete "dv" # delete, verbose + setenv gt_ar_use_ranlib "yes" + + # Assembler definitions: + + # From before conversion from MASM or get MASM running under DOSEMU or such + # setenv gt_as_use_prebuilt "yes" + + # GNU as + + if ( "ia64" == $mach_type ) then + if ( "icc" == $gtm_linux_compiler ) then + setenv gt_cpp_compiler "icc" # name of C preprocessor + setenv gt_cpp_options_common "-x assembler-with-cpp" + setenv gt_as_assembler "icc" + setenv gt_as_options_common "-c -i-static" + setenv gt_as_option_optimize "$gt_as_option_optimize -O3" + else + setenv gt_as_assembler "gcc -c" + endif + endif + + if ( "ia64" != $mach_type ) then + setenv gt_as_assembler "as" + if ("s390x" == $mach_type) then + setenv gt_as_options_common "-march=z9-109" + setenv gt_as_option_debug "--gdwarf-2" + else if ( "cygwin" == $platform_only ) then + setenv gt_as_options_common "--defsym cygwin=1" + setenv gt_as_option_debug "--gdwarf-2" + else + setenv gt_as_option_debug "--gstabs" + endif + endif + + # to avoid naming files with .S + # smw 1999/12/04 setenv gt_as_options_common "-c -x assembler-with-cpp" + setenv gt_as_option_DDEBUG "" + + # C definitions: + + # generate position independent code + setenv gt_cc_shl_fpic "-fPIC" + if ( "cygwin" == $platform_only ) then + setenv gt_cc_shl_fpic "" + endif + + # setenv gt_cc_options_common "-c -D_LARGEFILE64_SOURCE=1 -D_FILE_OFFSET_BITS=64" + # For gcc: _BSD_SOURCE for caddr_t, others + # _XOPEN_SOURCE=500 should probably define POSIX 199309 and/or + # POSIX 199506 but doesnt so... + # -fsigned-char for Linux390 but shouldn't hurt x86 + + # setenv gt_cc_options_common "-c -ansi -D_XOPEN_SOURCE=500 -D_BSD_SOURCE -D_POSIX_C_SOURCE=199506L + # setenv gt_cc_options_common "$gt_cc_options_common -D_FILE_OFFSET_BITS=64 -DFULLBLOCKWRITES -fsigned-char" + # _GNU_SOURCE includes _XOPEN_SOURCE=400, _BSD_SOURCE, and _POSIX_C_SOURCE-199506L among others + # Need _XOPEN_SOURCE=600 for posix_memalign() interface (replaces obsolete memalign) + + if ( "cygwin" == $platform_only ) then +# on Cygwin, -ansi defines __STRICT_ANSI__ which suppresses many prototypes + setenv gt_cc_options_common "-c " + else + setenv gt_cc_options_common "-c -ansi " + endif + + setenv gt_cc_options_common "$gt_cc_options_common -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 " + setenv gt_cc_options_common "$gt_cc_options_common -D_XOPEN_SOURCE=600 -fsigned-char " + + if ( "ia64" != $mach_type ) then + set tmpgtmval = `echo $gt_cc_compiler | grep icc` + if ($status) then + setenv gt_cc_options_common "$gt_cc_options_common $gt_cc_shl_fpic" + else + setenv gt_cc_options_common "$gt_cc_options_common $gt_cc_shl_fpic -Wimplicit" + endif + setenv gt_cc_options_common "$gt_cc_options_common -Wmissing-prototypes -D_LARGEFILE64_SOURCE" + endif + + if ( "cygwin" == $platform_only ) then + setenv gt_cc_options_common "$gt_cc_options_common -DNO_SEM_TIME -DNO_SEM_GETPID" + endif + + # 32 bit ICU headers of 32 bit linux are in /emul/ia32-linux/usr/include/ + if ( "32" == $gt_build_type ) then + if (-d /emul/ia32-linux/usr/include/) setenv gt_cc_option_I "-I/emul/ia32-linux/usr/include/" + endif + + if ( "linux" == $platform_name ) then + set lversion=`uname -r` + set ltemp_ver=`echo $lversion | sed 's/./& /g'` + if ($ltemp_ver[3] == "2" && $ltemp_ver[1] == "2") then + setenv gt_cc_options_common "$gt_cc_options_common -DNeedInAddrPort" + endif + if ( "x86_64" == $mach_type ) then + # see if the compiler supports unused-result warnings + # Note: Compilers that dont support --help=warnings will not output unused-result. + # Currently, only compilers that support --help=warnings need to have unused-result + # turned off. Trying to turn it off on compilers that dont support it causes errors. + cc --help=warnings | & grep unused-result >/dev/null + if (! $status ) then + # if it does, turn them off + setenv gt_cc_options_common "$gt_cc_options_common -Wno-unused-result" + endif + endif + endif + + # -fno-defer-pop to prevent problems with assembly/generated code with optimization + # -fno-strict-aliasing since we don't comply with the rules + # -ffloat-store for consistent results avoiding rounding differences + if ( "ia64" != $mach_type ) then + setenv gt_cc_option_optimize "-O2 -fno-defer-pop -fno-strict-aliasing -ffloat-store" + if ( "32" == $gt_build_type ) then + # applies to 32bit x86_64, ia32 and cygwin + setenv gt_cc_option_optimize "$gt_cc_option_optimize -march=i686" + endif + endif + # -g generate debugging information for dbx (no longer overrides -O) + setenv gt_cc_option_debug "-g" + if ( "cygwin" == $platform_only ) then + setenv gt_cc_option_debug "$gt_cc_option_debug -gdwarf-2 -fno-inline -fno-inline-functions" + endif + + # Linker definitions: + setenv gt_ld_linker "$gt_cc_compiler" # redefine to use new C compiler definition + + # -M generate link map onto standard output + setenv gt_ld_options_common "-Wl,-M" + setenv gt_ld_options_gtmshr "-Wl,-u,gtm_filename_to_id -Wl,--version-script,gtmshr_symbols.export" + setenv gt_ld_options_all_exe "-rdynamic -Wl,-u,gtm_filename_to_id -Wl,-u,gtm_zstatus" + setenv gt_ld_options_all_exe "$gt_ld_options_all_exe -Wl,--version-script,gtmexe_symbols.export" + + # optimize for all 64bit platforms + setenv gt_ld_syslibs " -lrt -lelf -lncurses -lm -ldl" + if ( 32 == $gt_build_type ) then + # 32bit x86_64 and ia32 - decided at the beginning of the file + setenv gt_ld_syslibs " -lrt -lncurses -lm -ldl" + endif + if ( "cygwin" == $platform_only ) then + setenv gt_ld_syslibs "-lncurses -lm -lcrypt" + endif + + # -lrt for async I/O in mupip recover/rollback + setenv gt_ld_aio_syslib "-lrt" + if ( "cygwin" == $platform_only ) then + setenv gt_ld_aio_syslib "" + endif + + # Shared library definition overrides: + setenv gt_cc_shl_options "-c $gt_cc_shl_fpic" + + setenv gt_ld_shl_linker "cc" + setenv gt_ld_shl_options "-shared" + + # If we are trying to force a 32 bit build on a 64 bit x86 machine, then we need to explicitly specify a 32 bit + # over-ride option. + if ( "x86_64" == $mach_type && "32" == $gt_build_type ) then + setenv gt_cc_options_common "$gt_cc_options_common -m32" + setenv gt_ld_options_gtmshr "$gt_ld_options_gtmshr -m32" + setenv gt_cc_shl_options "$gt_cc_shl_options -m32" + setenv gt_ld_shl_options "$gt_ld_shl_options -m32" + setenv gt_ld_options_common "$gt_ld_options_common -m32" + endif + + # need to re-define these in terms of new gt_ld_options_common: + setenv gt_ld_options_bta "$gt_ld_options_common" + setenv gt_ld_options_dbg "$gt_ld_options_common" + setenv gt_ld_options_pro "$gt_ld_options_common" + + setenv gt_ld_shl_suffix ".so" + + # lint definition overrides + # setenv gt_lint_linter "" + + setenv gt_lint_options_library "-x" + setenv gt_lint_options_common "" + +endif + +# PBO(Profile based optimization) settings for Linux-IA64, intel compiler. +set gtm_build_image = `basename $gtm_exe` +if ( "ia64" == $mach_type && "pro" == $gtm_build_image && "icc" == $gtm_linux_compiler) then + setenv gt_cc_option_optimize "-O3" + if ($?gtm_pbo_option) then + @ need_mkdir = 0 + if !($?gtm_pbo_db) then + @ need_mkdir = 1 + setenv gtm_pbo_db "$gtm_log/pbo" + else if !(-e $gtm_pbo_db) then + @ need_mkdir = 1 + endif + if (1 == $need_mkdir) then + mkdir -p $gtm_pbo_db + chmod 775 $gtm_pbo_db # Others need ability to write to the pbo files + endif + if ($gtm_pbo_option == "collect") then + setenv gt_cc_option_optimize "-prof-gen -prof-dir $gtm_pbo_db" + setenv gt_ld_options_pro "$gt_ld_options_pro -prof-gen -prof-dir $gtm_pbo_db" + setenv gt_as_option_optimize "$gt_cc_option_optimize" + else if ($gtm_pbo_option == "use") then + setenv gt_cc_option_optimize "-O3 -prof-use -prof-dir $gtm_pbo_db" + setenv gt_ld_options_pro "$gt_ld_options_pro -prof-dir $gtm_pbo_db" + endif + endif +endif + +# Assembler definitions: +# Note: we need to specify the assembler output file name or it will write it to the source directory. +if ( "ia64" == $mach_type ) then + alias gt_as_bta 'gt_as $gt_as_option_debug $gt_as_option_nooptimize' + alias gt_as_dbg 'gt_as $gt_as_option_DDEBUG $gt_as_option_debug $gt_as_option_nooptimize' + alias gt_as_pro 'gt_as $gt_as_option_optimize ' +endif + +if ( "s390x" == $mach_type) then + setenv gt_asm_arch_size "-m64" +else if ( "64" == $gt_build_type) then + setenv gt_asm_arch_size "--64" +else + setenv gt_asm_arch_size "--32" +endif + +if ( "ia64" != $mach_type ) then + alias gt_as_bta \ + 'gt_as $gt_as_option_debug $gt_as_option_nooptimize $gt_asm_arch_size -o `basename \!:1 .s`.o \!:1' + # If the value of the alias variable extends the 132 character limit, use a temporary variable + # (like gtm_as_dbg_var below) and use that in the alias value to stick to the the coding standard. + set gt_as_dbg_var = "$gt_as_option_DDEBUG $gt_as_option_debug $gt_as_option_nooptimize $gt_asm_arch_size" + alias gt_as_dbg 'gt_as $gt_as_dbg_var -o `basename \!:1 .s`.o \!:1' + alias gt_as_pro 'gt_as $gt_as_option_optimize $gt_asm_arch_size -o `basename \!:1 .s`.o \!:1' +endif diff --git a/sr_linux/gtm_env_sp.mk b/sr_linux/gtm_env_sp.mk new file mode 100644 index 0000000..1921d98 --- /dev/null +++ b/sr_linux/gtm_env_sp.mk @@ -0,0 +1,196 @@ +################################################################# +# # +# Copyright 2001, 2011 Fidelity Information Services, Inc # +# # +# This source code contains the intellectual property # +# of its copyright holder(s), and is made available # +# under a license. If you do not know the terms of # +# the license, please stop and do not read further. # +# # +################################################################# + +# +########################################################################################## +# +# gtm_env_sp.mk - environment variable values and aliases specific to Linux +# if not Linux we assume Cygwin and x86 +# +########################################################################################## +# GNU assembler options + +gt_as_assembler=as +gt_as_option_DDEBUG= +gt_as_option_debug=--gstabs +gt_as_option_nooptimize= +gt_as_option_optimize= +gt_as_options= +gt_as_options_common=--64 +gt_as_src_suffix=.s + +ifeq ($(gt_machine_type), ia64) +gt_as_assembler=gcc -c +gt_as_option_debug= +else +ifeq ($(gt_machine_type), s390x) +gt_as_options_common=-march=z9-109 -m64 +gt_as_option_debug=--gdwarf-2 +else +ifeq ($(gt_machine_type), CYGWIN) +gt_as_option_debug=--gdwarf-2 +gt_as_options_common=--defsym cygwin=1 +endif +endif +endif +ifeq ($(gt_build_type), 32) +ifeq ($(gt_os_type),Linux) +gt_as_options_common=--32 +endif +endif + +# C compiler options + +gt_cc_shl_fpic=-fPIC +gt_cc_options_common=-c -ansi +ifeq ($(gt_os_type),CYGWIN) +gt_cc_option_debug=$(gt_cc_option_debug) -gdwarf-2 -fno-inline -fno-inline-functions +gt_cc_shl_fpic= +gt_cc_options_common=-c +endif + +gt_cc_compiler=cc +gt_cc_option_DBTABLD=-DNOLIBGTMSHR +gt_cc_option_DDEBUG=-DDEBUG +gt_cc_option_debug=-g +gt_cc_option_nooptimize= +gt_cc_option_optimize=-O2 -fno-defer-pop -fno-strict-aliasing -ffloat-store +gt_cc_options_common+= -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_XOPEN_SOURCE=600 -fsigned-char + +ifeq ($(gt_build_type),32) +gt_cc_option_I=-I- +endif + +ifeq ($(gt_machine_type), x86_64) +ifeq ($(gt_build_type),32) +# Do not lookup the source directory before include directories specified by -I. +# gcc complains about -I- being obsolete, but using -iquote cause build errors for gcc and as - ABS 2008.12.09 +# +# The -I- option is only needed for 32 bit builds on x86_64. It provides a feature that is not present in +# -iquote - namely -I- disables the ability to search the current directory for include files. This is needed +# when compiling something in sr_port which includes a file that is in both sr_port and the architecture specific +# sr_386. We don't want the sr_port version. An example is sr_port/code_gen.c which includes emit_code.h. +# emit_code.h is found in both sr_port and sr_i386. Using -I- will find the sr_i386 version, but without it the +# sr_port version is used. SLJ 2010.03.31 + +# The /emul/ia32-linux/... directory doesn't exist on most machines, but when it's there we need it. No problem +# with always includeing it. + +gt_cc_option_I+= -I/emul/ia32-linux/usr/include/ +else +gt_cc_option_I= +endif +endif + +# autodepend option +gt_cc_dep_option=-w + +ifeq ($(gt_machine_type), ia64) +gt_cc_option_optimize=-O +else +gt_cc_options_common+= $(gt_cc_shl_fpic) -Wmissing-prototypes -D_LARGEFILE64_SOURCE +ifeq ($(gt_build_type),32) +gt_cc_option_optimize+= -march=i686 +endif +endif + +gt_cc_shl_options=-c $(gt_cc_shl_fpic) + +ifeq ($(gt_os_type),CYGWIN) +gt_cc_options_common+= -DNO_SEM_TIME -DNO_SEM_GETPID +endif + +# Linker definitions: + +gt_ld_aio_syslib= +gt_ld_ci_options=-Wl,-u,gtm_ci -Wl,-u,gtm_filename_to_id -Wl,--version-script,gtmshr_symbols.export +gt_ld_ci_u_option=-Wl,-u,gtm_ci +gt_ld_linker=$(gt_cc_compiler) +gt_ld_m_shl_linker=ld +gt_ld_m_shl_options=-shared +gt_ld_option_output=-o +gt_ld_options_all_exe=-rdynamic -Wl,-u,gtm_filename_to_id -Wl,-u,gtm_zstatus -Wl,--version-script,gtmexe_symbols.export +gt_ld_options_bta=-Wl,-M +gt_ld_options_common=-Wl,-M +gt_ld_options_dbg=-Wl,-M +gt_ld_options_gtmshr=-Wl,-u,gtm_filename_to_id -Wl,--version-script,gtmshr_symbols.export +gt_ld_options_pro=-Wl,-M +gt_ld_shl_linker=cc +gt_ld_shl_options=-shared +gt_ld_shl_suffix=.so +gt_ld_syslibs= -lrt -lelf -lncurses -lm -ldl +gt_ld_sysrtns= + +ifeq ($(gt_build_type),32) +gt_ld_m_shl_options= +gt_ld_syslibs= -lrt -lncurses -lm -ldl +endif + + +# -lrt for async I/O in mupip recover/rollback +ifeq ($(gt_build_type), 64) +gt_ld_syslibs=-lrt -lelf -lncurses -lm -ldl +else +ifeq ($(gt_os_type),Linux) +gt_ld_syslibs=-lrt -lncurses -lm -ldl +else +gt_ld_syslibs=-lncurses -lm -lcrypt +endif +endif + +# lint definition overrides +gt_lint_linter= +gt_lint_options_library=-x +gt_lint_options_common= + +gt_cpus=$(shell grep -c process /proc/cpuinfo) +# used to build VPATH +# Apparently Ubuntu does not like the -e option for echo, delete this if the *.mdep make files generate an error +ifeq ($(distro),ubuntu) +gt_echoe=echo +else +gt_echoe=echo -e +endif + +ifeq ($(gt_build_type), 32) +gt_cc_options_common+=-m32 +gt_ld_options_gtmshr+=-m32 +gt_cc_shl_options+=-m32 +gt_ld_options_common+=-m32 +endif +# +# gas assembly - the preprocessor works +# +define gt-as +$(gt_as_assembler) $(gt_as_options) $< -o $@ +endef +define gt_cpp +$(gt_cpp_compiler) -E $(gt_cpp_options_common) $(gt_cc_option_I) $< > $<_cpp.s +endef +define gt-as_cpp +$(gt_as_assembler) $(gt_as_options) $<_cpp.s -o $@ +endef +# +# gcc specific rule to get the depend file (CC -M) +# +define gt-dep + @echo $*.o $*.d : '\' > $@; \ + echo $(notdir $(filter-out /usr/include% /usr/lib% /usr/local/include% /usr/local/lib/%, $(filter %.c %.h,$(shell $(gt_cc_compiler) -M $(gt_cc_options) $(gt_cc_dep_option) $<)))) >> $@ +endef +define gt-export + @echo "{" >$@ + @echo "global:" >>$@ + @sed 's/\(.*\)/ \1;/g' $< >>$@ + @echo "local:" >>$@ + @echo " *;" >>$@ + @echo "};" >>$@ +endef + diff --git a/sr_linux/gtm_getenv.c b/sr_linux/gtm_getenv.c new file mode 100644 index 0000000..b75c8e4 --- /dev/null +++ b/sr_linux/gtm_getenv.c @@ -0,0 +1,44 @@ +/**************************************************************** +* * +* Copyright 2007 Fidelity Information Services, Inc * +* * +* This source code contains the intellectual property * +* of its copyright holder(s), and is made available * +* under a license. If you do not know the terms of * +* the license, please stop and do not read further. * +* * +****************************************************************/ + +#ifdef __CYGWIN__ + +/* In Cygwin 1.54-2 and gdb 6.5.50, the Cygwin environment variables * + * are not passed to the program being debugged properly. getenv() * + * only sees the Windows variables. * +*/ + +#include "gtm_unistd.h" +#include "gtm_stdlib.h" +#include "gtm_string.h" + +extern char **environ; /* array of pointers, last has NULL */ + +char *gtm_getenv(char *varname) +{ + char *eq, **p; + size_t len; + + if (NULL == environ || NULL == varname) + return NULL; + len = strlen(varname); + for (p = environ; *p; p++) + { + eq = strchr(*p, '='); + if (eq && (*p + len) == eq) + { + if (!strncasecmp(varname, *p, len)) /* environ names are upcased */ + return (eq + 1); + } + } + return NULL; +} +#endif diff --git a/sr_linux/gtm_system.c b/sr_linux/gtm_system.c new file mode 100644 index 0000000..a600659 --- /dev/null +++ b/sr_linux/gtm_system.c @@ -0,0 +1,27 @@ +/**************************************************************** +* * +* Copyright 2007 Fidelity Information Services, Inc * +* * +* This source code contains the intellectual property * +* of its copyright holder(s), and is made available * +* under a license. If you do not know the terms of * +* the license, please stop and do not read further. * +* * +****************************************************************/ + +#ifdef __CYGWIN__ + +/* The version of system() included in Cygwin 1.54-2 * + * (aka newlib libc/stdlib/system.c 1.8) does not properly * + * handle signals or EINTR. For proper functioning of GT.M, * + * replace this routine with a good version of system() * + * until a Cygwin version fixes this problem. * +*/ + +#include "gtm_stdlib.h" + +int gtm_system(const char *line) +{ + return system(line); +} +#endif diff --git a/sr_linux/inst_flush.c b/sr_linux/inst_flush.c new file mode 100644 index 0000000..b3bd2ac --- /dev/null +++ b/sr_linux/inst_flush.c @@ -0,0 +1,19 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* STUB FILE only for non-ia64 versions */ +#include "mdef.h" +#include "inst_flush.h" + +void inst_flush(void *start, int4 len) +{ + IA64_ONLY(cacheflush(start, len, 0 )); +} diff --git a/sr_linux/release_name.h b/sr_linux/release_name.h new file mode 100644 index 0000000..01e94cd --- /dev/null +++ b/sr_linux/release_name.h @@ -0,0 +1,24 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifdef __CYGWIN__ +#define GTM_RELEASE_NAME "GT.M V5.4-002B CYGWIN x86" +#elif defined(__ia64) +#define GTM_RELEASE_NAME "GT.M V5.4-002B Linux IA64" +#elif defined(__x86_64__) +#define GTM_RELEASE_NAME "GT.M V5.4-002B Linux x86_64" +#elif defined(__s390__) +#define GTM_RELEASE_NAME "GT.M V5.4-002B Linux S390X" +#else +#define GTM_RELEASE_NAME "GT.M V5.4-002B Linux x86" +#endif +#define GTM_PRODUCT "GT.M" +#define GTM_VERSION "V5.4" diff --git a/sr_port/act_in_gvt.c b/sr_port/act_in_gvt.c new file mode 100644 index 0000000..578e6b3 --- /dev/null +++ b/sr_port/act_in_gvt.c @@ -0,0 +1,58 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "collseq.h" +#include "spec_type.h" +#ifdef GTM_TRIGGER +#include "rtnhdr.h" +#include "gv_trigger.h" +#endif + +GBLREF gv_namehead *gv_target; +GBLREF gv_key *gv_altkey; + +void act_in_gvt(void) +{ + collseq *csp; + error_def(ERR_COLLATIONUNDEF); + error_def(ERR_COLLTYPVERSION); + error_def(ERR_GVIS); + +# ifdef GTM_TRIGGER + if ((HASHT_GBLNAME_LEN == gv_target->gvname.var_name.len) && + (0 == MEMCMP_LIT(gv_target->gvname.var_name.addr, HASHT_GBLNAME))) + return; /* No collation for triggers */ +# endif + if (csp = ready_collseq((int)(gv_target->act))) + { + if (!do_verify(csp, gv_target->act, gv_target->ver)) + { + gv_target->root = 0; + rts_error(VARLSTCNT(8) ERR_COLLTYPVERSION, 2, gv_target->act, gv_target->ver, + ERR_GVIS, 2, gv_altkey->end - 1, gv_altkey->base); + } + } + else + { + gv_target->root = 0; + rts_error(VARLSTCNT(7) ERR_COLLATIONUNDEF, 1, gv_target->act, + ERR_GVIS, 2, gv_altkey->end - 1, gv_altkey->base); + } + gv_target->collseq = csp; + return; +} diff --git a/sr_port/actuallist.c b/sr_port/actuallist.c new file mode 100644 index 0000000..7e9a77e --- /dev/null +++ b/sr_port/actuallist.c @@ -0,0 +1,110 @@ +/**************************************************************** + * * + * Copyright 2001, 2005 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; +GBLREF mident window_ident; + +int actuallist (oprtype *opr) +{ + triple *ref0, *ref1, *ref2, *masktrip, *counttrip; + oprtype ot; + int mask, parmcount; + error_def (ERR_MAXACTARG); + error_def (ERR_NAMEEXPECTED); + error_def (ERR_COMMAORRPAREXP); + + assert (window_token == TK_LPAREN); + advancewindow (); + masktrip = newtriple (OC_PARAMETER); + mask = 0; + counttrip = newtriple (OC_PARAMETER); + masktrip->operand[1] = put_tref (counttrip); + ref0 = counttrip; + if (window_token == TK_RPAREN) + parmcount = 0; + else + for (parmcount = 1; ; parmcount++) + { + if (parmcount > MAX_ACTUALS) + { + stx_error (ERR_MAXACTARG); + return FALSE; + } + if (window_token == TK_PERIOD) + { + advancewindow (); + if (window_token == TK_IDENT) + { + ot = put_mvar (&window_ident); + mask |= (1 << parmcount - 1); + advancewindow (); + } + else if (window_token == TK_ATSIGN) + { + if (!indirection(&ot)) + return FALSE; + ref2 = newtriple(OC_INDLVNAMADR); + ref2->operand[0] = ot; + ot = put_tref(ref2); + mask |= (1 << parmcount - 1); + } + else + { + stx_error (ERR_NAMEEXPECTED); + return FALSE; + } + } + else if (window_token == TK_COMMA) + { + ref2 = newtriple(OC_NULLEXP); + ot = put_tref(ref2); + } + else + if (!expr (&ot)) return FALSE; + ref1 = newtriple (OC_PARAMETER); + ref0->operand[1] = put_tref (ref1); + ref1->operand[0] = ot; + if (window_token == TK_COMMA) + { advancewindow (); + if (window_token == TK_RPAREN) + { ref0 = ref1; + ref2 = newtriple(OC_NULLEXP); + ot = put_tref(ref2); + ref1 = newtriple (OC_PARAMETER); + ref0->operand[1] = put_tref (ref1); + ref1->operand[0] = ot; + parmcount++; + break; + } + } + else + if (window_token == TK_RPAREN) + break; + else + { + stx_error (ERR_COMMAORRPAREXP); + return FALSE; + } + ref0 = ref1; + } + advancewindow (); + masktrip->operand[0] = put_ilit (mask); + counttrip->operand[0] = put_ilit (parmcount); + parmcount += 2; + *opr = put_tref (masktrip); + return parmcount; +} diff --git a/sr_port/add_atom.c b/sr_port/add_atom.c new file mode 100644 index 0000000..efe844a --- /dev/null +++ b/sr_port/add_atom.c @@ -0,0 +1,112 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "copy.h" +#include "patcode.h" +#include "min_max.h" + +/* This function is part of the MUMPS compiler. It adds one pattern atom to the string of compiled pattern atoms. + * If the atom to be added can be "compressed" with the previous one, this function will allow compress() to do so. + */ +boolean_t add_atom(int *count, + uint4 pattern_mask, + pat_strlit *strlit_buff, + boolean_t infinite, + int *min, + int *max, + int *size, + int *total_min, + int *total_max, + int lower_bound, + int upper_bound, + int altmin, + int altmax, + boolean_t *last_infinite_ptr, + uint4 **fstchar_ptr, + uint4 **outchar_ptr, + uint4 **lastpatptr_ptr) +{ + uint4 *patmaskptr; + gtm_uint64_t bound; + int4 bytelen; + + if ((pattern_mask & PATM_STRLIT) && !strlit_buff->bytelen && *count) + { /* A special case is a pattern like xxx?1N5.7""2A . Since there is an infinite number of empty strings between + * any two characters in a string, a pattern atom that counts repetitions of the fixed string "" can be ignored. + * That is, such an atom can only be ignored if it is not the only one in the pattern... + */ + return TRUE; + } + if (*count && !*(size - 1)) + { /* If the previous atom was an n.m"", it should be removed. In such a case, the last four values + * in the 'outchar' array are PATM_STRLIT (pattern mask), 0 (bytelen), 0 (charlen), flags (ASCII and no BADCHAR). */ + assert(3 == PAT_STRLIT_PADDING); + assert(PATM_STRLIT == *(*outchar_ptr - (PAT_STRLIT_PADDING + 1))); + assert(0 == *(*outchar_ptr - 3)); /* bytelen */ + assert(0 == *(*outchar_ptr - 2)); /* charlen */ + assert(!((*(*outchar_ptr - 1)) & PATM_STRLIT_NONASCII)); /* flags - ascii */ + assert(!((*(*outchar_ptr - 1)) & PATM_STRLIT_BADCHAR)); /* flags - no badchar */ + *outchar_ptr -= (PAT_STRLIT_PADDING + 1); + (*count)--; + assert(0 == *count); + min--; + max--; + size--; + } + if (pattern_mask & PATM_ALT) + { + lower_bound = BOUND_MULTIPLY(lower_bound, altmin, bound); + upper_bound = BOUND_MULTIPLY(upper_bound, altmax, bound); + } + if (*count && pat_compress(pattern_mask, strlit_buff, infinite, *last_infinite_ptr, *lastpatptr_ptr)) + { + min--; + max--; + size--; + *min = MIN(*min + lower_bound, PAT_MAX_REPEAT); + *max = MIN(*max + upper_bound, PAT_MAX_REPEAT); + } else + { + *min = MIN(lower_bound, PAT_MAX_REPEAT); + *max = MIN(upper_bound, PAT_MAX_REPEAT); + *lastpatptr_ptr = patmaskptr = *outchar_ptr; + *last_infinite_ptr = infinite; + (*outchar_ptr)++; + if (*outchar_ptr - *fstchar_ptr > MAX_PATTERN_LENGTH) + return FALSE; + if ((pattern_mask & PATM_ALT) || !(pattern_mask & PATM_STRLIT)) + { + *patmaskptr++ = pattern_mask; + *size = 1; + } else + { + bytelen = strlit_buff->bytelen; + *outchar_ptr += DIVIDE_ROUND_UP(bytelen, SIZEOF(uint4)) + PAT_STRLIT_PADDING; + if (*outchar_ptr - *fstchar_ptr > MAX_PATTERN_LENGTH) + return FALSE; + *patmaskptr++ = pattern_mask; + memcpy(patmaskptr, strlit_buff, bytelen + PAT_STRLIT_PADDING * SIZEOF(uint4)); + *size = strlit_buff->charlen; + } + (*count)++; + } + *total_min += BOUND_MULTIPLY(*size, lower_bound, bound); + if (*total_min > PAT_MAX_REPEAT) + *total_min = PAT_MAX_REPEAT; + *total_max += BOUND_MULTIPLY(*size, upper_bound, bound); + if (*total_max > PAT_MAX_REPEAT) + *total_max = PAT_MAX_REPEAT; + return TRUE; +} diff --git a/sr_port/add_inter.h b/sr_port/add_inter.h new file mode 100644 index 0000000..f7082f0 --- /dev/null +++ b/sr_port/add_inter.h @@ -0,0 +1,20 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef ADD_INTER_DEFINED + +/* Declare parms for add_inter.c */ + +int4 add_inter (int val, sm_int_ptr_t addr, sm_global_latch_ptr_t latch); + +#define ADD_INTER_DEFINED + +#endif diff --git a/sr_port/adjust_frames.c b/sr_port/adjust_frames.c new file mode 100644 index 0000000..b736978 --- /dev/null +++ b/sr_port/adjust_frames.c @@ -0,0 +1,36 @@ +/**************************************************************** + * * + * Copyright 2002, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "rtnhdr.h" +#include "stack_frame.h" + +GBLREF stack_frame *frame_pointer; + +void adjust_frames(unsigned char *old_ptext_beg, unsigned char *old_ptext_end, unsigned char *new_ptext_beg) +{ + stack_frame *fp; + + for (fp = frame_pointer; NULL != fp; fp = fp->old_frame_pointer) + { +#ifdef GTM_TRIGGER + if (fp->type & SFT_TRIGR) + /* Have a trigger baseframe, pick up stack continuation frame_pointer stored by base_frame() */ + fp = *(stack_frame **)(fp + 1); + assert(fp); +#endif + assert((frame_pointer < frame_pointer->old_frame_pointer) || (NULL == frame_pointer->old_frame_pointer)); + if (old_ptext_beg <= fp->mpc && fp->mpc <= old_ptext_end) + fp->mpc += (new_ptext_beg - old_ptext_beg); + } + return; +} diff --git a/sr_port/advancewindow.c b/sr_port/advancewindow.c new file mode 100644 index 0000000..f552afe --- /dev/null +++ b/sr_port/advancewindow.c @@ -0,0 +1,297 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "compiler.h" +#include "toktyp.h" +#include "stringpool.h" +#include "gtm_caseconv.h" +#include "advancewindow.h" +#include "show_source_line.h" + +#ifdef UNICODE_SUPPORTED +#include "gtm_utf8.h" +#include "gtm_icu_api.h" /* U_ISPRINT() needs this header */ +#endif + +GBLREF unsigned char *source_buffer; +GBLREF short int source_column; +GBLREF char window_token; +GBLREF mval window_mval; +GBLREF char director_token; +GBLREF mval director_mval; +GBLREF char *lexical_ptr; +GBLREF spdesc stringpool; +GBLREF boolean_t gtm_utf8_mode; +GBLREF boolean_t run_time; +GBLREF mident window_ident; /* the current identifier */ +GBLREF mident director_ident; /* the look-ahead identifier */ + +LITREF char ctypetab[NUM_CHARS]; + +error_def(ERR_LITNONGRAPH); + +static readonly unsigned char apos_ok[] = +{ + 0,TK_NEXCLAIMATION,0,0,0,0,TK_NAMPERSAND,0 + ,0,0,0,0,0,0,0,0 + ,0,0,0,0,0,0,0,0 + ,0,0,0,0,TK_NLESS,TK_NEQUAL,TK_NGREATER,TK_NQUESTION + ,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,TK_NLBRACKET,0,TK_NRBRACKET,0,0 +}; + +void advancewindow(void) +{ + error_def(ERR_NUMOFLOW); + unsigned char *cp1, *cp2, *cp3, x; + char *tmp, source_line_buff[MAX_SRCLINE + SIZEOF(ARROW)]; + int y, charlen; +# ifdef UNICODE_SUPPORTED + uint4 ch; + unsigned char *cptr; +# endif + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + TREF(last_source_column) = source_column; + source_column = (unsigned char *)lexical_ptr - source_buffer + 1; + window_token = director_token; + window_mval = director_mval; + director_mval.mvtype = 0; /* keeps mval from being GC'd since it is not useful until re-used */ + + /* It is more efficient to swich buffers between window_ident and director_ident + * instead of copying the text from director_ident to window_ident. This can be + * done by exchanging the pointers window_ident.addr and director_ident.addr */ + tmp = window_ident.addr; + window_ident = director_ident; + director_ident.addr = tmp; + + x = *lexical_ptr; + switch (y = ctypetab[x]) + { + case TK_QUOTE: + ENSURE_STP_FREE_SPACE(MAX_SRCLINE); + cp1 = (unsigned char *)lexical_ptr + 1; + cp2 = cp3 = stringpool.free; + for (;;) + { + +#ifdef UNICODE_SUPPORTED + if (gtm_utf8_mode) + cptr = (unsigned char *)UTF8_MBTOWC((sm_uc_ptr_t)cp1, source_buffer + MAX_SRCLINE, ch); +#endif + x = *cp1++; + if ((SP > x) UNICODE_ONLY(|| (gtm_utf8_mode && !(U_ISPRINT(ch))))) + { + TREF(last_source_column) = cp1 - source_buffer; + if ('\0' == x) + { + director_token = window_token = TK_ERROR; + return; + } + if (!run_time) + { + show_source_line(source_line_buff, SIZEOF(source_line_buff), TRUE); + dec_err(VARLSTCNT(1) ERR_LITNONGRAPH); + } + } + if ('\"' == x) + { + UNICODE_ONLY(assert(!gtm_utf8_mode || (cp1 == cptr))); + if ('\"' == *cp1) + cp1++; + else + break; + } + *cp2++ = x; +#ifdef UNICODE_SUPPORTED + if (gtm_utf8_mode && (cptr > cp1)) + { + assert(4 > (cptr - cp1)); + for (; cptr > cp1;) + *cp2++ = *cp1++; + } +#endif + assert(cp2 <= stringpool.top); + } + lexical_ptr = (char *)cp1; + director_token = TK_STRLIT; + director_mval.mvtype = MV_STR; + director_mval.str.addr = (char *)cp3; + director_mval.str.len = INTCAST(cp2 - cp3); + stringpool.free = cp2; + s2n(&director_mval); +#ifdef UNICODE_SUPPORTED + if (gtm_utf8_mode && !run_time) + { /* UTF8 mode and not compiling an indirect gets an optimization to set the + (true) length of the string into the mval + */ + charlen = utf8_len_stx(&director_mval.str); + if (0 > charlen) /* got a BADCHAR error */ + director_token = TK_ERROR; + else + { + assert(charlen == director_mval.str.char_len); + director_mval.mvtype |= MV_UTF_LEN; + } + } +#endif + return; + case TK_LOWER: + case TK_PERCENT: + case TK_UPPER: + cp2 = (unsigned char *)director_ident.addr; + cp3 = cp2 + MAX_MIDENT_LEN; + for (;;) + { + if (cp2 < cp3) + *cp2++ = x; + y = ctypetab[x = *++lexical_ptr]; + if ((TK_UPPER != y) && (TK_DIGIT != y) && (TK_LOWER != y)) + break; + } + director_ident.len = INTCAST(cp2 - (unsigned char*)director_ident.addr); + director_token = TK_IDENT; + return; + case TK_PERIOD: + if (ctypetab[x = *(lexical_ptr + 1)] != TK_DIGIT) + break; + case TK_DIGIT: + director_mval.str.addr = lexical_ptr; + director_mval.str.len = MAX_SRCLINE; + director_mval.mvtype = MV_STR; + lexical_ptr = (char *)s2n(&director_mval); + if (!(director_mval.mvtype &= MV_NUM_MASK)) + { + stx_error(ERR_NUMOFLOW); + director_token = TK_ERROR; + return; + } + if (TREF(s2n_intlit)) + { + director_token = TK_NUMLIT ; + n2s(&director_mval); + } else + { + director_token = TK_INTLIT ; + director_mval.str.len = INTCAST(lexical_ptr - director_mval.str.addr); + ENSURE_STP_FREE_SPACE(director_mval.str.len); + memcpy(stringpool.free, director_mval.str.addr, director_mval.str.len); + assert (stringpool.free <= stringpool.top) ; + } + return; + case TK_APOSTROPHE: + if (( x = *++lexical_ptr) >= 32) + { + x -= 32; + if (x < SIZEOF(apos_ok) / SIZEOF(unsigned char)) + { + if (y = apos_ok[x]) + { + if (DEL < (x = *++lexical_ptr)) + { + director_token = TK_ERROR; + return; + } + if (TK_RBRACKET == ctypetab[x]) + { + lexical_ptr++; + y = TK_NSORTS_AFTER; + } + director_token = y; + return; + } + } + } + director_token = TK_APOSTROPHE; + return; + case TK_SEMICOLON: + while (*++lexical_ptr) ; + y = TK_EOL; + break; + case TK_ASTERISK: + if (DEL < (x = *(lexical_ptr + 1))) + { + director_token = TK_ERROR; + return; + } + if (TK_ASTERISK == ctypetab[x]) + { + lexical_ptr++; + y = TK_EXPONENT; + } + break; + case TK_RBRACKET: + if ((x = *(lexical_ptr + 1)) > DEL) + { + director_token = TK_ERROR; + return; + } + if (TK_RBRACKET == ctypetab[x]) + { + lexical_ptr++; + y = TK_SORTS_AFTER; + } + break; + default: + ; + } + lexical_ptr++; + director_token = y; + return; +} + +#ifdef GTM_TRIGGER +/* The M standard does not allow the '#' character to appear inside mnames but in specific places, we want to allow this + * so that triggers, which have the imbedded '#' character in their routine names, can be debugged and printed. The places + * where this is allowed follow. + * + * 1. $TEXT() + * 2. ZBREAK + * 3. ZPRINT + * + * All other uses still prohibit '#' from being in an MNAME. Routines that need to allow # in a name can call this routine to + * recombine the existing token and the look-ahead (director) token such that '#' is considered part of an mident. + */ +void advwindw_hash_in_mname_allowed(void) +{ + unsigned char *cp2, *cp3, x; + unsigned char ident_buffer[SIZEOF(mident_fixed)]; + int ident_len, ch; + + assert(TK_IDENT == window_token); + assert(TK_HASH == director_token); + /* First copy the existing token we want to expand into our safe-haven */ + memcpy(ident_buffer, window_ident.addr, window_ident.len); + /* Now parse further until we run out of [m]ident */ + cp2 = ident_buffer + window_ident.len; + cp3 = ident_buffer + MAX_MIDENT_LEN; + *cp2++ = '#'; /* We are only called if director token is '#' so put that char in buffer now */ + /* Start processing with the token following the '#' */ + for (x = *lexical_ptr, ch = ctypetab[x]; + ((TK_UPPER == ch) || (TK_DIGIT == ch) || (TK_LOWER == ch) || (TK_HASH == ch)); + x = *++lexical_ptr, ch = ctypetab[x]) + { + if (cp2 < cp3) + *cp2++ = x; + } + director_ident.len = INTCAST(cp2 - ident_buffer); + director_token = TK_IDENT; + memcpy(director_ident.addr, ident_buffer, director_ident.len); + advancewindow(); /* Makes the homogenized token the current token (again) and prereads next token */ +} +#endif diff --git a/sr_port/advancewindow.h b/sr_port/advancewindow.h new file mode 100644 index 0000000..680a9c6 --- /dev/null +++ b/sr_port/advancewindow.h @@ -0,0 +1,19 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef ADVANCEWINDOW_included +#define ADVANCEWINDOW_included + +void advancewindow(void); +#ifdef GTM_TRIGGER +void advwindw_hash_in_mname_allowed(void); +#endif +#endif /* ADVANCEWINDOW_included */ diff --git a/sr_port/alias.h b/sr_port/alias.h new file mode 100644 index 0000000..0a56ef7 --- /dev/null +++ b/sr_port/alias.h @@ -0,0 +1,328 @@ +/**************************************************************** + * * + * Copyright 2009, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef ALIAS_H_ +# define ALIAS_H_ + +#define DOLLAR_ZWRTAC "$ZWRTAC" + +/* Min and max for the number of stringpool garbage collections (SPGC) that will be done without having + done a lv_val garbage collection (aka LVGC aka als_lvval_gc). While I did give these numbers some + thought, they aren't far from semi-random. Since the LVGC is kind of expensive (requiring two + traversals of the lv_vals in a symbol table) I decided 2 SPGCs was good to spread the cost of one + LVGC over. For the max, I didn't want to let it get too high so the tuning algorithm didn't take a + long time to get back to a more frequent value when necessary yet 64 seems fairly infrequent. + These numbers are totally subject to future studies.. [SE 12/2008] +*/ +#define MIN_SPGC_PER_LVGC 2 +#define MAX_SPGC_PER_LVGC 64 + +#include "zwrite.h" + +/* Macro used intermittently in code to debug alias code in general. Note this macro must be specified + as a compile option since it is used in macros that do not pull in this alias.h header file. +*/ +#ifdef DEBUG_ALIAS +# define DBGALS(x) DBGFPF(x) +# define DBGALS_ONLY(x) x +#else +# define DBGALS(x) +# define DBGALS_ONLY(x) +#endif + +/* Macro used intermittently to trace reference count changes */ +/* #define DEBUG_REFCNT */ +#ifdef DEBUG_REFCNT +# define DBGRFCT(x) DBGFPF(x) +# define DBGRFCT_ONLY(x) x +#else +# define DBGRFCT(x) +# define DBGRFCT_ONLY(x) +#endif + +/* Since DBGFPF calls flush_pio, if we are debugging, include the defining header file */ +#if defined(DEBUG_ALIAS) || defined(DEBUG_REFCNT) +# include "io.h" +#endif + +/* Macro to increment total refcount (and optionally trace it) */ +#define INCR_TREFCNT(lv) \ +{ \ + assert(LV_IS_BASE_VAR(lv)); \ + DBGRFCT((stderr, "\nIncrement trefcnt for lv_val at 0x"lvaddr" from %d to %d by %s line %d\n", \ + (lv), (lv)->stats.trefcnt, (lv)->stats.trefcnt + 1, __FILE__, __LINE__)); \ + ++(lv)->stats.trefcnt; \ +} + +/* Macro to decrement total refcount (and optionally trace it) */ +#define DECR_TREFCNT(lv) \ +{ \ + assert(LV_IS_BASE_VAR(lv)); \ + DBGRFCT((stderr, "\nDecrement trefcnt for lv_val at 0x"lvaddr" from %d to %d by %s line %d\n", \ + (lv), (lv)->stats.trefcnt, (lv)->stats.trefcnt - 1, __FILE__, __LINE__)); \ + --(lv)->stats.trefcnt; \ + assert(0 <= (lv)->stats.trefcnt); \ +} + +/* Macro to increment container refcount (and optionally trace it) */ +#define INCR_CREFCNT(lv) \ +{ \ + assert(LV_IS_BASE_VAR(lv)); \ + DBGRFCT((stderr, "\nIncrement crefcnt for lv_val at 0x"lvaddr" from %d to %d by %s line %d\n", \ + (lv), (lv)->stats.crefcnt, (lv)->stats.crefcnt + 1, __FILE__, __LINE__)); \ + ++(lv)->stats.crefcnt; \ +} + +/* Macro to decrement container refcount (and optionally trace it) */ +#define DECR_CREFCNT(lv) \ +{ \ + assert(LV_IS_BASE_VAR(lv)); \ + DBGRFCT((stderr, "\nDecrement crefcnt for lv_val at 0x"lvaddr" from %d to %d by %s line %d\n", \ + (lv), (lv)->stats.crefcnt, (lv)->stats.crefcnt - 1, __FILE__, __LINE__)); \ + --(lv)->stats.crefcnt; \ + assert(0 <= (lv)->stats.crefcnt); \ +} + +/* There are three flavors of DECR_BASE_REF depending on the activities we need to persue. The first two flavors + DECR_BASE_REF and DECR_BASE_REF_RQ take 2 parms (hashtable entry and lv_val addresses). Both of these do hashtable + entry maint in addition to lv_val maint but do it in different ways. The DECR_BASE_REF macro's purpose is to + leave the hashtable always pointing to a valid lv_val. If the one we are decrementing goes to zero, we can just + zap that one and leave it there but if the refcnt is not zero, we can't do that. Since another command may + follow the command doing the DECR_BASE_REF (e.g. KILL *), we can't leave the hash table entry with a null value + since gtm_fetch() won't be called to fill it in so we must allocate a new lv_val for it. By contrast, with the + DECR_BASE_REF_RQ macro, if the lv_val refcnt goes to zero, the lv_val is requeued and in either case, the hte + address is cleared to make way for a new value. The 3rd flavor is the DCR_BASE_REF_NOSYM macro which is used for + orphaned lv_vals not in a hashtable. This macro just requeues lv_vals that hit a refcnt of zero. +*/ + +#define LVP_KILL_SUBTREE_IF_EXISTS(lvp, dotpsave) \ +{ \ + lvTree *lvt_child; \ + \ + assert(LV_IS_BASE_VAR(lvp)); \ + assert(0 == (lvp)->stats.crefcnt); \ + lvt_child = LV_GET_CHILD(lvp); \ + if (NULL != lvt_child) \ + { \ + assert(((lvTreeNode *)(lvp)) == LVT_PARENT(lvt_child)); \ + LV_CHILD(lvp) = NULL; \ + lv_killarray(lvt_child, dotpsave); \ + } \ +} + +/* Macro to decrement a base var reference and do appropriate cleanup */ +#define DECR_BASE_REF(tabent, lvp, dotpsave) \ +{ /* Perform reference count maintenance for base var */ \ + lv_val *dbr_lvp; \ + symval *sym; \ + \ + assert(LV_IS_BASE_VAR(lvp)); \ + assert(0 < (lvp)->stats.trefcnt); \ + DECR_TREFCNT(lvp); \ + sym = LV_GET_SYMVAL(lvp); \ + if (0 == (lvp)->stats.trefcnt) \ + { /* This lv_val can be effectively killed and remain in hte */ \ + LVP_KILL_SUBTREE_IF_EXISTS(lvp, dotpsave); \ + assert(NULL == (lvp)->tp_var); \ + LVVAL_INIT(lvp, sym); \ + } else \ + { /* lv_val otherwise still in use -- put a new one in this hte */ \ + assert((lvp)->stats.trefcnt >= (lvp)->stats.crefcnt); \ + dbr_lvp = lv_getslot(sym); \ + DBGRFCT((stderr, "DECR_BASE_REF: Resetting hte 0x"lvaddr" from 0x"lvaddr" to 0x"lvaddr"\n", \ + tabent, (lvp), dbr_lvp)); \ + LVVAL_INIT(dbr_lvp, sym); \ + tabent->value = dbr_lvp; \ + } \ +} + +/* Macro to decrement a base var reference and do appropriate cleanup except the tabent value is unconditionally + * cleared and the lvval put on the free queue. Used when the tabent is about to be reused for a different lv. + */ +#define DECR_BASE_REF_RQ(tabent, lvp, dotpsave) \ +{ /* Perform reference count maintenance for base var */ \ + assert(LV_IS_BASE_VAR(lvp)); \ + assert(0 < (lvp)->stats.trefcnt); \ + DECR_TREFCNT(lvp); \ + DBGRFCT((stderr, "DECR_BASE_REF_RQ: Resetting hte 0x"lvaddr" from 0x"lvaddr" to NULL\n", \ + tabent, tabent->value)); \ + tabent->value = (void *)NULL; \ + if (0 == (lvp)->stats.trefcnt) \ + { /* This lv_val is done .. requeue it after it is killed */ \ + LVP_KILL_SUBTREE_IF_EXISTS(lvp, dotpsave); \ + LV_FREESLOT(lvp); \ + } else \ + assert((lvp)->stats.trefcnt >= (lvp)->stats.crefcnt); \ +} + +/* Macro to decrement a base var reference and do appropriate cleanup except no hash table + entry value cleanup is done. +*/ +#define DECR_BASE_REF_NOSYM(lvp, dotpsave) \ +{ /* Perform reference count maintenance for base var */ \ + assert(LV_IS_BASE_VAR(lvp)); \ + assert(0 < (lvp)->stats.trefcnt); \ + DECR_TREFCNT(lvp); \ + if (0 == (lvp)->stats.trefcnt) \ + { /* This lv_val is done .. requeue it after it is killed */ \ + LVP_KILL_SUBTREE_IF_EXISTS(lvp, dotpsave); \ + LV_FREESLOT(lvp); \ + } else \ + assert((lvp)->stats.trefcnt >= (lvp)->stats.crefcnt); \ +} + +/* Macro to decrement an alias container reference and do appropriate cleanup */ +#define DECR_AC_REF(lvp, dotpsave) \ +{ \ + if (MV_ALIASCONT & (lvp)->v.mvtype) \ + { /* Killing an alias container, perform reference count maintenance */ \ + \ + GBLREF uint4 dollar_tlevel; \ + \ + lv_val *lvref = (lv_val *)(lvp)->v.str.addr; \ + assert(0 == (lvp)->v.str.len); \ + assert(!LV_IS_BASE_VAR(lvp)); \ + assert(lvref); \ + assert(LV_IS_BASE_VAR(lvref)); \ + assert(0 < lvref->stats.crefcnt); \ + assert(0 < lvref->stats.trefcnt); \ + if (dotpsave && dollar_tlevel && (NULL != lvref->tp_var) \ + && !lvref->tp_var->var_cloned && (1 == lvref->stats.trefcnt)) \ + /* Only clone (here) if target is going to be deleted by decrement */ \ + TP_VAR_CLONE(lvref); \ + DECR_CREFCNT(lvref); \ + DECR_BASE_REF_NOSYM(lvref, dotpsave); \ + } \ +} + +/* Macro to mark nested symvals as having had alias activity. Mark nested symvals until we get + * to a symval owning the lv_val specified. This loop will normally only run once except in the + * case where the lv_val given is owned by a symval nested by an exclusive NEW. + */ +#define MARK_ALIAS_ACTIVE(lv_own_svlvl) \ +{ \ + symval *sv; \ + int4 lcl_own_svlvl; \ + \ + lcl_own_svlvl = lv_own_svlvl; \ + for (sv = curr_symval; ((NULL != sv) && (sv->symvlvl >= lcl_own_svlvl)); sv = sv->last_tab) \ + sv->alias_activity = TRUE; \ +} + +/* The following *_CNTNRS_IN_TREE macros scan the supplied tree for container vars. Note that in debug mode, + * even if "has_aliascont" is NOT set, we will still scan the array for containers but if one is found, then we will assert fail. + * Note this means the processing is different for PRO and DBG builds in that this routine will not even be called in PRO if + * the has_aliascont flag is not on in the base mval. But this allows us to check the integrity of the has_aliascont flag + * in DBG because we will fail if ever a container is found in an array with the flag turned off. + */ + +/* Macro to scan a lvTree for container vars and for each one, treat it as if it had been specified in a TP restart variable list + * by setting it up to be cloned if deleted. This activity should nest so container vars that point to further trees should also + * be scanned. + */ +#define TPSAV_CNTNRS_IN_TREE(lv_base) \ +{ \ + lvTree *lvt; \ + \ + assert(LV_IS_BASE_VAR(lv_base)); \ + if (lv_base->stats.tstartcycle != tstartcycle) \ + { /* If haven't processed this lv_val for this transaction (or nested transaction */ \ + lv_base->stats.tstartcycle = tstartcycle; \ + /* Note it is possible that this lv_val has the current tstart cycle if there has been a restart. We still need \ + to rescan the var anyway since one attempt can execute differently than a following attempt and thus create \ + different variables for us to find -- if it has aliases in it that is. \ + */ \ + if ((NULL != (lvt = LV_GET_CHILD(lv_base))) PRO_ONLY(&& (lv_base)->has_aliascont)) \ + { \ + DBGRFCT((stderr, "\n## TPSAV_CNTNRS_IN_TREE: Beginning processing lvTree at 0x"lvaddr"\n", lv_base)); \ + als_prcs_tpsav_cntnr(lvt); \ + DBGRFCT((stderr, "\n## TPSAV_CNTNRS_IN_TREE: Finished processing lvTree at 0x"lvaddr"\n", lv_base)); \ + } \ + } else \ + DBGRFCT((stderr, "\n## TPSAV_CNTNRS_IN_TREE: Bypassing lvTree at 0x"lvaddr" as already processed\n", lv_base)); \ +} + +/* Macro similar to TPSAV_CNTNRS_IN_TREE() above but in this case, we know we want to increment the reference counts + * for all found container var targets. They have already been saved (we will assert they have a tp_var!) and we just want to + * reestablish the reference counts. This is used when a saved array is being restored and the containers in it need to have + * their reference counts re-established. + */ +#define TPREST_CNTNRS_IN_TREE(lv_base) \ +{ \ + lvTree *lvt; \ + \ + assert(LV_IS_BASE_VAR(lv_base)); \ + if ((NULL != (lvt = LV_GET_CHILD(lv_base))) PRO_ONLY(&& (lv_base)->has_aliascont)) \ + { \ + DBGRFCT((stderr, "\n++ TPREST_CNTNRS_IN_TREE: Beginning processing lvTree at 0x"lvaddr"\n", lv_base)); \ + als_prcs_tprest_cntnr(lvt); \ + DBGRFCT((stderr, "\n++ TPREST_CNTNRS_IN_TREE: Finished processing lvTree at 0x"lvaddr"\n", lv_base)); \ + } \ +} + +/* Macro similar to TPREST_CNTNRS_IN_TREE() above but in this case we want to decrement the containers since we are + in unwind processing */ +#define TPUNWND_CNTNRS_IN_TREE(lv_base) \ +{ \ + lvTree *lvt; \ + \ + assert(LV_IS_BASE_VAR(lv_base)); \ + if ((NULL != (lvt = LV_GET_CHILD(lv_base))) PRO_ONLY(&& (lv_base)->has_aliascont)) \ + { \ + DBGRFCT((stderr, "\n-- TPUNWND_CNTNRS_IN_TREE: Beginning processing lvTree at 0x"lvaddr"\n", lv_base)); \ + als_prcs_tpunwnd_cntnr(lvt); \ + DBGRFCT((stderr, "\n-- TPUNWND_CNTNRS_IN_TREE: Finished processing lvTree at 0x"lvaddr"\n", lv_base)); \ + } else \ + DBGRFCT((stderr, "\n-- TPUNWND_CNTNRS_IN_TREE: Scan for lvTree at 0x"lvaddr" bypassed - no containers", lv_base));\ +} + +/* Macro to scan a tree for container vars, delete what they point to and unmark the container so it is no longer a container */ +#define KILL_CNTNRS_IN_TREE(lv_base) \ +{ \ + lvTree *lvt; \ + \ + assert(LV_IS_BASE_VAR(lv_base)); \ + if ((NULL != (lvt = LV_GET_CHILD(lv_base))) PRO_ONLY(&& (lv_base)->has_aliascont)) \ + als_prcs_kill_cntnr(lvt); \ +} + +/* Macro to scan an lvval for containers pointing to other structures that need to be scanned in xnew pop processing */ +#define XNEWREF_CNTNRS_IN_TREE(lv_base) \ +{ \ + lvTree *lvt; \ + \ + assert(LV_IS_BASE_VAR(lv_base)); \ + if ((NULL != (lvt = LV_GET_CHILD(lv_base))) PRO_ONLY(&& (lv_base)->has_aliascont)) \ + als_prcs_xnewref_cntnr(lvt); \ +} + +/* Macro to mark the base frame of the current var as having a container */ +#define MARK_CONTAINER_ONBOARD(lv_base) \ +{ \ + assert(LV_IS_BASE_VAR(lv_base)); \ + lv_base->has_aliascont = TRUE; \ +} + +void als_lsymtab_repair(hash_table_mname *table, ht_ent_mname *table_base_orig, int table_size_orig); +void als_check_xnew_var_aliases(symval *oldsymtab, symval *cursymtab); +void als_zwrhtab_init(void); +void als_prcs_tpsav_cntnr(lvTree *lvt); +void als_prcs_tprest_cntnr(lvTree *lvt); +void als_prcs_tpunwnd_cntnr(lvTree *lvt); +void als_prcs_kill_cntnr(lvTree *lvt); +void als_prcs_xnewref_cntnr(lvTree *lvt); + +ht_ent_mname *als_lookup_base_lvval(lv_val *lvp); +zwr_alias_var *als_getzavslot(void); +int als_lvval_gc(void); +DBGALS_ONLY(void als_lvmon_output(void);) + +#endif /* !ALIAS_H_ */ diff --git a/sr_port/alias_funcs.c b/sr_port/alias_funcs.c new file mode 100644 index 0000000..b3b7fac --- /dev/null +++ b/sr_port/alias_funcs.c @@ -0,0 +1,1280 @@ +/**************************************************************** + * * + * Copyright 2009, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" +#include "gtm_string.h" + +#include + +#include "rtnhdr.h" +#include "stack_frame.h" +#include "op.h" +#include "stp_parms.h" +#include "lv_val.h" +#include "error.h" +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdskill.h" +#include "gdscc.h" +#include "filestruct.h" +#include "jnl.h" /* needed for tp.h */ +#include "tp.h" +#include "tp_frame.h" +#include "mv_stent.h" +#include "alias.h" +#include "gtm_malloc.h" +#include "stringpool.h" +#include "mmemory.h" + +/* Define macros locally used by this routine only. General use macros are defined in alias.h */ + +/* Macro to decrement a base var reference. This lightweight version (of DECR_BASE_REF) used by "als_check_xnew_var_aliases" + * is used to bypass most of the the LV_FREESLOT/LV_FLIST_ENQUEUE macro and do only reference count maint since the entire + * symtab and all its lv_vals (lv_blks) are going to be released shortly. + */ +#define DECR_BASE_REF_LIGHT(lvp) \ +{ /* Perform reference count maintenance for base var */ \ + lvTree *lvt_child; \ + \ + assert(LV_IS_BASE_VAR(lvp)); \ + assert(0 < (lvp)->stats.trefcnt); \ + DECR_TREFCNT(lvp); \ + assert((lvp)->stats.trefcnt >= (lvp)->stats.crefcnt); \ + if (0 == (lvp)->stats.trefcnt) \ + { \ + (lvp)->v.mvtype = 0; \ + lvt_child = LV_GET_CHILD(lvp); \ + if (lvt_child) \ + { \ + LV_CHILD(lvp) = NULL; \ + als_xnew_killaliasarray(lvt_child); \ + } \ + } \ +} + +/* Macro to decrement an alias container reference. This lightweight version used by "als_check_xnew_var_aliases" + * is used to bypass the DECR_BASE_REF_NOSYM macro invocation (in most cases) and do only reference count maint + * since the entire symtab and all its lv_vals (lv_blks) are going to be released shortly. Note this macro is + * known to only be used on subscripted nodes hence the additional initial assert. + */ +#define DECR_AC_REF_LIGHT(LVP) \ +{ \ + symval *lvp_sym, *lvref_sym; \ + lv_val *lvp_base; \ + \ + assert(!LV_IS_BASE_VAR(LVP)); \ + if (MV_ALIASCONT & (LVP)->v.mvtype) \ + { /* Killing an alias container, perform reference count maintenance */ \ + lv_val *lvref = (lv_val *)(LVP)->v.str.addr; \ + assert(lvref); \ + assert(LV_IS_BASE_VAR(lvref)); \ + assert(0 == (LVP)->v.str.len); \ + assert(0 < lvref->stats.crefcnt); \ + assert(0 < lvref->stats.trefcnt); \ + DECR_CREFCNT(lvref); \ + lvref_sym = LV_GET_SYMVAL(lvref); \ + lvp_base = LV_GET_BASE_VAR(LVP); \ + lvp_sym = LV_GET_SYMVAL(lvp_base); \ + if (lvref_sym == lvp_sym) \ + { /* pointed to lvval owned by popd symval, use light flavor */ \ + DECR_BASE_REF_LIGHT(lvref); \ + } else \ + { /* pointed to lvval owned by other symval, use full flavor */ \ + DECR_BASE_REF_NOSYM(lvref, TRUE); \ + } \ + } \ +} + +/* Macro to mark an lv_val as reachable and process its descendants if any. + * Note that like the _CNTNRS_IN_TREE macros, in dbg mode, we will scan the array + * for containers even if has_aliascont flag is FALSE. + */ +#define MARK_REACHABLE(lvp) \ +{ \ + lvTree *lvt; \ + symval *sym; \ + \ + assert((lvp)); \ + /* Since this macro can be called in cases where the lv_val is NOT valid, such as in \ + * the case an MVST_PVAL mv_stent entry with an mvs_val entry that has been deleted \ + * by alias reassignment (see unw_mv_ent), we need to verify we have an actual lv_val \ + * by the same methods used to build the lv_val list and only then check if this \ + * lv_val has been processed yet. \ + */ \ + sym = LV_SYMVAL(lvp); \ + if ((NULL != sym) && SYM_IS_SYMVAL(sym) && ((lvp)->stats.lvtaskcycle != lvtaskcycle)) \ + { /* This lv_val has not been processed yet */ \ + DBGRFCT((stderr, "\nMARK_REACHABLE: Marking lv 0x"lvaddr" as reachable\n", \ + (lvp))); \ + (lvp)->stats.lvtaskcycle = lvtaskcycle; /* Mark it */ \ + if ((NULL != (lvt = LV_GET_CHILD(lvp))) PRO_ONLY(&& (lvp)->has_aliascont)) \ + { /* And it has descendents to process */ \ + DBGRFCT((stderr, "MARK_REACHABLE: Scanning same lv for containers\n")); \ + als_prcs_markreached_cntnr(lvt); \ + } \ + } \ +} + +/* Macro to clone an lv_val */ +#define CLONE_LVVAL(oldlv, newlv, cursymval) \ +{ \ + assert(LV_IS_BASE_VAR(oldlv)); \ + newlv = lv_getslot(cursymval); \ + *newlv = *oldlv; \ + LV_SYMVAL(newlv) = cursymval; \ + lv_var_clone(newlv, newlv); \ + oldlv->v.mvtype = MV_LVCOPIED; \ + oldlv->ptrs.copy_loc.newtablv = newlv; \ +} + +/* Macro to initialize a ZWR_ZAV_BLK structure */ +#define ZAV_BLK_INIT(zavb, zavbnext) \ + (zavb)->next = (zavbnext); \ + (zavb)->zav_base = (zavb)->zav_free = (zwr_alias_var *)((char *)(zavb) + SIZEOF(zwr_zav_blk)); \ + (zavb)->zav_top = (zwr_alias_var *)((char *)(zavb)->zav_base + (SIZEOF(zwr_alias_var) * ZWR_ZAV_BLK_CNT)); + +/* Macro to run a given tree looking for container vars. Process what they point to in order to make sure what they point to + * doesn't live in the symbol tree being popped. If so, move to the current tree (copying if necessary). If what is being pointed + * to was not passed through then it will not be put into the symbol table but will instead just be data pointed to by the + * container var. Like the _CNTNRS_IN_TREE macros, in dbg mode, we will scan the array for containers even if has_aliascont + * flag is FALSE. + */ +#define RESOLV_ALIAS_CNTNRS_IN_TREE(LV_BASE, POPDSYMVAL, CURSYMVAL) \ +{ \ + lvTree *lvt; \ + \ + if ((NULL != (lvt = LV_GET_CHILD(LV_BASE))) && ((LV_BASE)->stats.lvtaskcycle != lvtaskcycle) \ + PRO_ONLY(&& (LV_BASE)->has_aliascont)) \ + { \ + (LV_BASE)->stats.lvtaskcycle = lvtaskcycle; \ + als_prcs_xnew_alias_cntnr(lvt, POPDSYMVAL, CURSYMVAL); \ + } \ +} + +GBLREF stack_frame *frame_pointer; +GBLREF symval *curr_symval; +GBLREF unsigned char *msp, *stackbase, *stacktop, *stackwarn; +GBLREF mv_stent *mv_chain; +GBLREF tp_frame *tp_pointer; +GBLREF zwr_hash_table *zwrhtab; +GBLREF trans_num local_tn; /* transaction number for THIS PROCESS */ +GBLREF uint4 tstartcycle; +GBLREF uint4 lvtaskcycle; /* lv_val cycle for misc lv_val related tasks */ +GBLREF mstr **stp_array; +GBLREF int stp_array_size; +GBLREF lv_val *zsrch_var, *zsrch_dir1, *zsrch_dir2; +GBLREF tp_frame *tp_pointer; +GBLREF int4 SPGC_since_LVGC; /* stringpool GCs since the last dead-data GC */ +GBLREF boolean_t suspend_lvgcol; +GBLREF lv_xnew_var *xnewvar_anchor; +GBLREF lv_xnew_ref *xnewref_anchor; +GBLREF mval *alias_retarg; + +LITREF mname_entry null_mname_entry; + +/* Local routines -- not made static so they show up in pro core stack traces */ +STATICFNDCL void als_xnew_killaliasarray(lvTree *lvt); +STATICFNDCL void als_prcs_xnew_alias_cntnr(lvTree *lvt, symval *popdsymval, symval *cursymval); +STATICFNDCL void als_prcs_markreached_cntnr(lvTree *lvt); + +CONDITION_HANDLER(als_check_xnew_var_aliases_ch); + + +/***************************************************************************************/ + +/* Routine to repair the l_symtab entries in the stack due to hash table expansion such that the + * l_symtab entries no longer point to valid hash table entries. + * + * Note that the "repair" done by this routine depends on the special processing done in + * expand_hashtab_mname (EXPAND_HASHTAB rtn in hashtab_implementation.h) which does not free the + * old table and places the addresses of the new hash table entries in the the value of the old + * hash table entries. This allows this routine to access the old table with the existing + * l_symtab entries and pull the new values that should be put in the respective l_symtab + * entries before it completes the cleanup and releases the old hash table. + * + * Operation - Loop through the stack and: + * + * 1) For each unique l_symtab, run through the entries in the l_symtab. + * 2) If entry is null, skip. + * 3) If entry falls within range of the old symbol table, load the address in it and verify + * that it falls within the range of the new symbol table. + * 4) If the entry does not fall within the range of the old symtab: + * a) Stop the search as we must have run into an older symtab + * b) If debug, assert fail if this is not the first_symbol in this l_symtab we have seen + * since an l_symtab can only point to one symtab). + * 5) Get new entry address from within the old entry. + * 6) Debug only: Assert fail if the new entry address not in range of new symtab. + * 7) Note that after procesing the stack to get to the l_symtab entries, we also process the + * mv_stent types that contain hash table entry pointers and have to be processed in the same + * fashion as the l_symtab entries. This processing saves us the hashtable lookup necessary to + * pop NEW'd or parameter values when undoing a stack level and restoring previous values. + */ +void als_lsymtab_repair(hash_table_mname *table, ht_ent_mname *table_base_orig, int table_size_orig) +{ + int htcnt; + boolean_t done; + mv_stent *mv_st_ent; + ht_ent_mname *table_top_orig, **last_lsym_hte, **htep, *htenew; + stack_frame *fp, *fpprev; + DEBUG_ONLY(boolean_t first_sym;) + + assert(table); + assert(table_base_orig); + assert(table_base_orig != curr_symval->h_symtab.base); + table_top_orig = table_base_orig + table_size_orig; + last_lsym_hte = NULL; + done = FALSE; + fp = frame_pointer; + assert(frame_pointer); + do + { /* Once through for each stackframe using the same symbol table. Note this loop is similar + * to the stack frame loop in op_clralsvars.c. + */ + if (fp->l_symtab != last_lsym_hte) + { /* Different l_symtab than last time (don't want to update twice) */ + last_lsym_hte = fp->l_symtab; + if (htcnt = fp->vartab_len) /* Note assignment */ + { /* Only process non-zero length l_symtabs */ + DEBUG_ONLY(first_sym = TRUE); + for (htep = fp->l_symtab; htcnt; --htcnt, ++htep) + { + if (NULL == *htep) + continue; + if (*htep < table_base_orig || *htep >= table_top_orig) + { /* Entry doesn't point to the current symbol table */ + assert(first_sym); + done = TRUE; + break; + } + htenew = (ht_ent_mname *)((*htep)->value); /* Pick up entry we should now use */ + assert(htenew >= table->base && htenew < (table->base + table->size)); + *htep = htenew; + DEBUG_ONLY(first_sym = FALSE); + } + } + } + fpprev = fp; + fp = fp->old_frame_pointer; + if (done) + break; + if (SFF_CI & fpprev->flags) + { /* Callins needs to be able to crawl past apparent end of stack to earlier stack segments. + * We should be in the base frame now. See if an earlier frame exists. + * Note we don't worry about trigger base frames here because triggers *always* have a + * different symbol table - previous symbol tables and stack levels are not affected. + */ + fp = *(stack_frame **)(fp + 1); /* Backups up to "prev pointer" created by base_frame() */ + if (NULL == fp || fp >= (stack_frame *)stackbase || fp < (stack_frame *)stacktop) + break; /* Pointer not within the stack -- must be earliest occurence */ + } + } while(fp); + /* Next, check the mv_stents for the stackframes we processed. Certain mv_stents also have hash + * table references in them that need repair. + */ + for (mv_st_ent = mv_chain; + mv_st_ent < (mv_stent *)(fp ? fp : fpprev); /* Last stack frame actually processed */ + mv_st_ent = (mv_stent *)(mv_st_ent->mv_st_next + (char *)mv_st_ent)) + { + switch (mv_st_ent->mv_st_type) + { /* The types processed here contain hash table pointers that need to be modified */ + case MVST_NTAB: + htep = &mv_st_ent->mv_st_cont.mvs_ntab.hte_addr; + break; + case MVST_PVAL: + htep = &mv_st_ent->mv_st_cont.mvs_pval.mvs_ptab.hte_addr; + break; + case MVST_NVAL: + htep = &mv_st_ent->mv_st_cont.mvs_nval.mvs_ptab.hte_addr; + break; + default: + continue; + } + if (NULL == *htep) + continue; + if (*htep < table_base_orig || *htep >= table_top_orig) + /* Entry doesn't point to the current symbol table so ignore it since it didn't change */ + continue; + htenew = (ht_ent_mname *)((*htep)->value); /* Pick up entry we should now use */ + assert(htenew >= table->base && htenew < (table->base + table->size)); + *htep = htenew; + } + /* For debug at least make unusable in case any stragglers point to it -- even though we are somewhat duplicating + * the SmInitAlloc gtmdgblvl flag here, this is so critical for debugging we want to do this even when general + * checking is not being done. SE 09/2008 + */ + DEBUG_ONLY(memset(table_base_orig, 0xfe, table_size_orig * SIZEOF(ht_ent_mname))); +} + +/* Local routine! condition handler whose sole function is to turn off the flag that says we are in "als_check_xnew_var_alias" */ +CONDITION_HANDLER(als_check_xnew_var_aliases_ch) +{ + START_CH; + suspend_lvgcol = FALSE; + NEXTCH; +} + +/* When an xNEW'd symtab is popped, and there are alias concerns, this routine is called to make things right. Things that + * can be wrong: + * 1) If a variable was passed through to the new symtab and then was aliased to a var that belonged to the new symbol table, + * the lv_val in the old symtab was released and a new one assigned in the new symbol table. We have to: + * a) copy that value back to the previous symtab and + * b) we have to fix the reference count since the alias owned by the new symtab is going away. + * 2) If a variable is passed through to the new symtab and within that variable an alias container is created that points + * to a var in the newer symtab we need to copy that var/array back to the older symtab so the value remains. + * 3) This gets more interesting to deal with when that var is also aliased by a passed through variable (combining issues 1 & 2). + * + * Operation: + * 1) When the new symtab was created by op_xnew, if there were variables that were passed through, they are recorded in the + * symtab chained from xnew_var_list. + * 2) Go through that list of variables, lookup each in the popped symbol table and change the hash table entries to deleted + * so those entries are not found in subsequent scans. + * 3) Do a more simplistic kill-alias-all in the popped symtab. This involves the following: + * a) Run the hash table looking for aliased variables. If found, do the reference count maintenance but don't worry about + * deleting any data since it will all go away after we return and unw_mv_ent() releases the symbol table and lv_blk chains. + * b) While running the hash table, run any variable trees we find anchored. + * 4) Go through the list of forwarded vars again (xnew_var_list) in the popped symval and see if any of the lv_vals are owned + * by the symval being popped. If they are, then the lv_vals involved need to be copied to lv_vals owned by the (now) + * current symtab because the popped symtab's lv_vals will be released by unw_mv_ent() when we return. Note that this also + * involves going through the tree of these vars in case any container vars point to an array that is not being dealt with + * in some fashion by one of the other vars we passed through. We avoid processing arrays multiple times by marking them + * with an incremented lvtaskval. + * 5) Go through the list of referenced lv_vals (via containers of xnew_var_list vars) by traversing the xnew_ref_list if it + * exists. These vars were recorded because we may not be able to get to them still just by traversing the reference list vars + * so they were pre-recorded so we could scan the worst case of vars that could have containers pointing to the symtab about + * to be popped. + * 6) If a pending alias return value exists, check it to see if it also needs to be processed. + * + * Note: to prevent re-scanning of already scanned array, this routine uses the lvtaskcycle value. To do this, we use the + * suspend_lvgcol global variable to tell "stp_gcol" to not do LVGC processing (which also uses lvtaskcycle). + */ +void als_check_xnew_var_aliases(symval *popdsymval, symval *cursymval) +{ + lv_xnew_var *xnewvar, *xnewvar_next; + lv_xnew_ref *xnewref, *xnewref_next; + ht_ent_mname *tabent; + hash_table_mname *popdsymtab, *cursymtab; + ht_ent_mname *htep, *htep_top; + lv_val *lv, *prevlv, *currlv, *popdlv; + lv_val *newlv, *oldlv; + boolean_t bypass_lvscan, bypass_lvrepl; + DBGRFCT_ONLY(mident_fixed vname;) + + ESTABLISH(als_check_xnew_var_aliases_ch); + suspend_lvgcol = TRUE; + assert(NULL != popdsymval); + assert(NULL != cursymval); + assert((NULL != popdsymval->xnew_var_list) || (NULL != alias_retarg)); + + DBGRFCT((stderr, "\nals_check_xnew_var_aliases: Beginning xvar pop processing\n")); + /* Step 2: (step 1 done in op_xnew()) - Run the list of vars that were passed through the xnew and remove them + * from the popped hash table so they can not be found by the step 3 scan below - meaning we won't mess with the + * reference counts of these entries but we will record their locations so we can process them in step 4. + */ + popdsymtab = &popdsymval->h_symtab; + cursymtab = &cursymval->h_symtab; + for (xnewvar = popdsymval->xnew_var_list; xnewvar; xnewvar = xnewvar->next) + { + tabent = lookup_hashtab_mname(popdsymtab, &xnewvar->key); + assert(tabent); + xnewvar->lvval = (lv_val *)tabent->value; /* Cache lookup results for 2nd pass in step 4 */ + delete_hashtab_ent_mname(popdsymtab, tabent); + } + /* Step 3: Run popped hash table undoing alias references. */ + DBGRFCT((stderr, "als_check_xnew_var_aliases: Step 3 - running popped symtab tree undoing local refcounts\n")); + for (htep = popdsymtab->base, htep_top = popdsymtab->top; htep < htep_top; htep++) + { + if (HTENT_VALID_MNAME(htep, lv_val, lv)) + DECR_BASE_REF_LIGHT(lv); + } + /* Step 4: See what, if anything, needs to be copied from popped level to current level. There are 3 possible + * cases here. Note in all cases, we must decrement the use counters of prevlv since they were incremented in + * op_xnew to keep things from disappearing prematurely (we don't want the LVs we saved to be scrapped and re-used + * so we make sure they stay around). + * + * Vars used: + * + * prevlv == lv from the current symbol table. + * currlv == lv we are going to eventually put into the current symbol table + * popdlv == lv from the popped symbol table + * + * Cases follow: + * + * Condition Action + * + * a) prevlv == popdlv Scan prevlv for alias containers pointing to popped symtab. + * b) prevlv != popdlv && popdlv in popd symtab. Clone popdlv into currlv & do alias container scan. + * c) prevlv != popdlv && popdlv not in popd symtab. Same as case (a). Note this includes the case where popdlv + * already resides in cursymtab. + */ + DBGRFCT((stderr, "als_check_xnew_var_aliases: Step 4 - beginning unwind scan of passed through vars\n")); + INCR_LVTASKCYCLE; + for (xnewvar = popdsymval->xnew_var_list; xnewvar; xnewvar = xnewvar_next) + { + bypass_lvscan = bypass_lvrepl = FALSE; + tabent = lookup_hashtab_mname(cursymtab, &xnewvar->key); + assert(tabent); /* Had better be there since it was passed in thru the exclusive new */ + DBGRFCT_ONLY( + memcpy(vname.c, tabent->key.var_name.addr, tabent->key.var_name.len); + vname.c[tabent->key.var_name.len] = '\0'; + ); + prevlv = (lv_val *)tabent->value; + popdlv = xnewvar->lvval; /* Value of this var in popped symtab */ + DBGRFCT((stderr, "als_check_xnew_var_aliases: var '%s' prevlv: 0x"lvaddr" popdlv: 0x"lvaddr"\n", + &vname.c, prevlv, popdlv)); + if (prevlv == popdlv) + { /* Case (a) - Just do the scan below */ + currlv = prevlv; + bypass_lvrepl = TRUE; + } else if (popdsymval == LV_GET_SYMVAL(popdlv)) + { /* Case (b) - Clone the var and tree into blocks owned by current symtab with the caveat that we need not + * do this if the array has already been cloned (because more than one var that was passed through it + * pointing to it. + */ + if (MV_LVCOPIED == popdlv->v.mvtype) + { /* This lv_val has been copied over already so use that pointer instead to put into the + * current hash table. + */ + currlv = popdlv->ptrs.copy_loc.newtablv; + assert(LV_GET_SYMVAL(currlv) == cursymval); + bypass_lvscan = TRUE; /* lv_val would have already been scanned */ + DBGRFCT((stderr, "als_check_xnew_var_aliases: lv already copied so setting currlv to 0x"lvaddr"\n", + currlv)); + } else + { + assert(LV_IS_BASE_VAR(popdlv)); + /* lv_val is owned by the popped symtab .. clone it to the new current tree */ + CLONE_LVVAL(popdlv, currlv, cursymval); + DBGRFCT((stderr, "als_check_xnew_var_aliases: lv has been cloned from 0x"lvaddr" to 0x"lvaddr"\n", + popdlv, currlv)); + } + } else + { /* Case (c) - same as (a) except we do replace the lv in cursymtab */ + assert(LV_IS_BASE_VAR(popdlv)); + currlv = popdlv; + } + if (!bypass_lvscan) + { /* Need to run this tree (if any) to look for container vars buried within */ + DBGRFCT((stderr, "als_check_xnew_var_aliases: potentially scanning lv 0x"lvaddr"\n", currlv)); + RESOLV_ALIAS_CNTNRS_IN_TREE(currlv, popdsymval, cursymval); + } + if (1 < prevlv->stats.trefcnt) + /* If prevlv is going to be around after we drop op_xnew's refcnt bumps, make sure it gets processed. + * If it was processed above, then it is marked such and the macro will bypass processing it again. + */ + RESOLV_ALIAS_CNTNRS_IN_TREE(prevlv, popdsymval, cursymval); + DECR_CREFCNT(prevlv); /* undo bump by op_xnew */ + if (!bypass_lvrepl) + { /* Replace the lvval in the current symbol table */ + DBGRFCT((stderr, "als_check_xnew_var_aliases: Resetting variable '%s' hte at 0x"lvaddr" from 0x"lvaddr + " to 0x"lvaddr"\n", &vname.c, tabent, prevlv, currlv)); + tabent->value = (void *)currlv; + } + assert(1 <= prevlv->stats.trefcnt); /* verify op_xnew's bump is still there (may be only one) */ + DECR_BASE_REF_NOSYM(prevlv, TRUE); /* undo bump by op_xnew */ + xnewvar_next = xnewvar->next; + xnewvar->next = xnewvar_anchor; + xnewvar_anchor = xnewvar; + } + /* Step 5: See if anything on the xnew_ref_list needs to be handled */ + DBGRFCT((stderr, "als_check_xnew_var_aliases: Step 5: Process xnew_ref_list if any\n")); + for (xnewref = popdsymval->xnew_ref_list; xnewref; xnewref = xnewref_next) + { + prevlv = xnewref->lvval; + DBGRFCT((stderr, "als_check_xnew_var_aliases: xnewref-prevlv: 0x"lvaddr"\n", prevlv)); + DECR_CREFCNT(prevlv); /* Will remove the trefcnt in final desposition below */ + /* Only do the scan if the reference count is greater than 1 since we are going to remove the + * refcnts added by op_xnew as we finish here. So if the var is going away anyway, no need + * to scan. + */ + if (1 < prevlv->stats.trefcnt) + { /* Process the array */ + DBGRFCT((stderr, "als_check_xnew_var_aliases: potentially scanning lv 0x"lvaddr"\n", prevlv)); + RESOLV_ALIAS_CNTNRS_IN_TREE(prevlv, popdsymval, cursymval); + } else + DBGRFCT((stderr, "als_check_xnew_var_aliases: prevlv was deleted\n")); + /* Remove refcnt and we are done */ + DECR_BASE_REF_NOSYM(prevlv, TRUE); + xnewref_next = xnewref->next; + xnewref->next = xnewref_anchor; + xnewref_anchor = xnewref; + } + /* Step 6: Check if a pending alias return value exists and if so if it needs to be processed. + * This type of value is created by unw_retarg() as the result of a "QUIT *" statement. It is an alias container + * mval that lives in the compiler temps of the caller with a pointer in an mv_stent of the callee in the mvs_parm + * block allocated by push_parm. Since this mval-container is just an mval and not an lv_val, we have to largely + * do similar processing to the "als_prcs_xnew_alias_cntnr" with this block type difference in mind. + */ + if (NULL != alias_retarg) + { + assert(0 != (MV_ALIASCONT & alias_retarg->mvtype)); + oldlv = (lv_val *)alias_retarg->str.addr; + if (MV_LVCOPIED == oldlv->v.mvtype) + { /* This lv_val has been copied over already so use that pointer instead */ + newlv = oldlv->ptrs.copy_loc.newtablv; + alias_retarg->str.addr = (char *)newlv; /* Replace container ptr */ + DBGRFCT((stderr, "\nals_check_xnew_var_aliases: alias retarg var found - referenced array already copied" + " - Setting pointer in aliascont mval 0x"lvaddr" to lv 0x"lvaddr"\n", alias_retarg, newlv)); + } else + { + assert(LV_IS_BASE_VAR(oldlv)); + if (popdsymval == LV_SYMVAL(oldlv)) + { /* lv_val is owned by the popped symtab .. clone it to the new current tree */ + CLONE_LVVAL(oldlv, newlv, cursymval); + alias_retarg->str.addr = (char *)newlv; /* Replace container ptr */ + DBGRFCT((stderr, "\nals_check_xnew_var_aliases: alias retarg var found - aliascont mval 0x"lvaddr + " being reset to point to lv 0x"lvaddr" which is a clone of lv 0x"lvaddr"\n", + alias_retarg, newlv, oldlv)); + } else + { /* lv_val is owned by current or older symval .. just use it in the subsequent scan in case it + * leads us to other lv_vals owned by the popped symtab. + */ + DBGRFCT((stderr, "\nals_check_xnew_var_aliases: alias retarg var found - aliascont mval 0x"lvaddr + " just being (potentially) scanned for container vars\n", alias_retarg)); + newlv = oldlv; + } + RESOLV_ALIAS_CNTNRS_IN_TREE(newlv, popdsymval, cursymval); + } + } + DBGRFCT((stderr, "als_check_xnew_var_aliases: Completed xvar pop processing\n")); + suspend_lvgcol = FALSE; + REVERT; +} + +/* Local routine! + * This routine is basically a slightly lightweight lv_killarray() that goes through a given tree looking for container vars + * and performing the necessary reference count cleanup as well as freeing the lv tree nodes but wont go through the + * bother of hashtable maintenance since the hashtable is anyways going away as part of the symbol table pop. + */ +STATICFNDEF void als_xnew_killaliasarray(lvTree *lvt) +{ + lvTreeNode *node, *nextnode; + lvTree *tmplvt; + + DEBUG_ONLY( + lv_val *lv; + + assert(NULL != lvt); + lv = (lv_val *)LVT_PARENT(lvt); + assert(NULL == LV_CHILD(lv)); /* Owner lv's children pointer MUST be NULL! */ + /* See comment in lv_killarray for why this is necessary */ + ) + /* Iterate through the tree in post-order fashion. Doing it in-order or pre-order has issues since we would have + * freed up nodes in the tree but would need to access links in them to get at the NEXT node. + */ + for (node = lvAvlTreeFirstPostOrder(lvt); NULL != node; node = nextnode) + { + nextnode = lvAvlTreeNextPostOrder(node); /* determine "nextnode" before freeing "node" */ + assert(NULL != node); + tmplvt = LV_CHILD(node); + if (NULL != tmplvt) + { + LV_CHILD(node) = NULL; + als_xnew_killaliasarray(tmplvt); + } + DECR_AC_REF_LIGHT(((lv_val *)node)); /* Decrement alias contain ref and cleanup if necessary */ + /* If node points to an "lv_val", we need to do a heavyweight LV_FREESLOT call to free up the lv_val. + * But we instead do a simple "LVTREENODE_FREESLOT" call because we are guaranteed node points to a "lvTreeNode" + * (i.e. it is a subscripted lv and never the base lv). Assert that. + */ + assert(!LV_IS_BASE_VAR(node)); + LVTREENODE_FREESLOT(node); + } + LVTREE_FREESLOT(lvt); +} + +/* Local routine! + * Routine to process an alias container found in a node of a var being "returned" back through an exclusive new. + * We may have to move the data. + */ +STATICFNDEF void als_prcs_xnew_alias_cntnr(lvTree *lvt, symval *popdsymval, symval *cursymval) +{ + lvTree *lvt_child; + lvTreeNode *node; + lv_val *newlv, *oldlv; + + assert(NULL != lvt); /* caller should not call if no subtree */ + /* In the case of lv_killarray, the only option we have is to do post-order traversal since we are freeing + * nodes in the tree as we traverse. But in this case, we dont change the tree structure so we are free to + * choose any order. We choose in-order as that is faster than post-order. + */ + for (node = lvAvlTreeFirst(lvt); NULL != node; node = lvAvlTreeNext(node)) + { + if (node->v.mvtype & MV_ALIASCONT) + { + assert(lvt->base_lv->has_aliascont); + assert(!LV_IS_BASE_VAR(node)); + oldlv = (lv_val *)node->v.str.addr; + assert(NULL != oldlv); + assert(LV_IS_BASE_VAR(oldlv)); + if (MV_LVCOPIED == oldlv->v.mvtype) + { /* This lv_val has been copied over already so use that pointer instead */ + newlv = oldlv->ptrs.copy_loc.newtablv; + assert(LV_IS_BASE_VAR(newlv)); + node->v.str.addr = (char *)newlv; /* Replace container ptr */ + DBGRFCT((stderr, "\nals_prcs_xnew_alias_cntnr: aliascont var found - referenced array already " + "copied - Setting pointer in aliascont lv 0x"lvaddr" to lv 0x"lvaddr"\n", node, newlv)); + } else + { + assert(LV_IS_BASE_VAR(oldlv)); + if (popdsymval == LV_SYMVAL(oldlv)) + { /* lv_val is owned by the popped symtab .. clone it to the new current tree */ + CLONE_LVVAL(oldlv, newlv, cursymval); + assert(LV_IS_BASE_VAR(newlv)); + node->v.str.addr = (char *)newlv; /* Replace container ptr */ + DBGRFCT((stderr, "\nals_prcs_xnew_alias_cntnr: aliascont var found - aliascont lv 0x"lvaddr + " being reset to point to lv 0x"lvaddr" which is a clone of lv 0x"lvaddr"\n", node, + newlv, oldlv)); + } else + { /* lv_val is owned by current or older symval .. just use it in the subsequent scan in case + * it leads us to other lv_vals owned by the popped symtab. */ + DBGRFCT((stderr, "\nals_prcs_xnew_alias_cntnr: aliascont var found - aliascont lv 0x"lvaddr + " just being (potentially) scanned for container vars\n", node)); + newlv = oldlv; + } + RESOLV_ALIAS_CNTNRS_IN_TREE(newlv, popdsymval, cursymval); + } + } + lvt_child = LV_GET_CHILD(node); + if (NULL != lvt_child) /* Descend recursively down this tree as well */ + als_prcs_xnew_alias_cntnr(lvt_child, popdsymval, cursymval); + } +} + +/* Routine to process an alias container found in an array being "saved" by TSTART (op_tstart). We need to set this array up + * so it gets copied just like op_tstart does for the base variables that are specified in it. In addition, this new array + * itself needs to be scanned so if it points to anything, that too gets saved if modified (all handled by + * TP_SAVE_RESTART_VAR macro). + */ +void als_prcs_tpsav_cntnr(lvTree *lvt) +{ + lvTree *lvt_child; + lvTreeNode *node; + lv_val *cntnr_lv_base; + + assert(NULL != lvt); /* caller should not call if no subtree */ + /* In the case of lv_killarray, the only option we have is to do post-order traversal since we are freeing + * nodes in the tree as we traverse. But in this case, we dont change the tree structure so we are free to + * choose any order. We choose in-order as that is faster than post-order. + */ + for (node = lvAvlTreeFirst(lvt); NULL != node; node = lvAvlTreeNext(node)) + { + if (node->v.mvtype & MV_ALIASCONT) + { + assert(lvt->base_lv->has_aliascont); + assert(!LV_IS_BASE_VAR(node)); + cntnr_lv_base = (lv_val *)node->v.str.addr; /* Extract container pointer */ + assert(NULL != cntnr_lv_base); + assert(LV_IS_BASE_VAR(cntnr_lv_base)); + assert(1 <= cntnr_lv_base->stats.trefcnt); + assert(1 <= cntnr_lv_base->stats.crefcnt); + if (NULL == cntnr_lv_base->tp_var) + { /* Save this var if it hasn't already been saved */ + assert(cntnr_lv_base->stats.tstartcycle != tstartcycle); + DBGRFCT((stderr, "\ntpSAV_container: Container at 0x"lvaddr + " refers to lv 0x"lvaddr" -- Creating tpsav block\n", node, cntnr_lv_base)); + TP_SAVE_RESTART_VAR(cntnr_lv_base, tp_pointer, &null_mname_entry); + INCR_CREFCNT(cntnr_lv_base); /* 2nd increment for reference via a container node */ + INCR_TREFCNT(cntnr_lv_base); + if (LV_HAS_CHILD(cntnr_lv_base)) + TPSAV_CNTNRS_IN_TREE(cntnr_lv_base); + } else + { /* If not saving it, we still need to bump the ref count(s) for this reference and + * process any children if we have't already seen this node (taskcycle check will tell us this). + */ + DBGRFCT((stderr, "\ntpSAV_container: Container at 0x"lvaddr" refers to lv 0x"lvaddr + " -- Incrementing refcnts\n", node, cntnr_lv_base)); + INCR_CREFCNT(cntnr_lv_base); + INCR_TREFCNT(cntnr_lv_base); + assert(0 < cntnr_lv_base->stats.trefcnt); + assert(0 < cntnr_lv_base->stats.crefcnt); + if (cntnr_lv_base->stats.tstartcycle != tstartcycle) + { + DBGRFCT((stderr, "\ntpSAV_container: .. Container at 0x"lvaddr" refers to lv 0x"lvaddr + " -- processing tree\n", node, cntnr_lv_base)); + if (LV_HAS_CHILD(cntnr_lv_base)) + TPSAV_CNTNRS_IN_TREE(cntnr_lv_base); + } else + { + DBGRFCT((stderr, "\ntpSAV_container: .. Container at 0x"lvaddr" refers to lv 0x"lvaddr + " -- Already processed -- bypassing\n", node, cntnr_lv_base)); + } + } + } + lvt_child = LV_GET_CHILD(node); + if (NULL != lvt_child) /* Descend recursively down this tree as well */ + als_prcs_tpsav_cntnr(lvt_child); + } +} + +/* For a given container var found in the tree we need to re-establish the reference counts for the base var + * the container is pointing to. Used during a local var restore on a TP restart. + */ +void als_prcs_tprest_cntnr(lvTree *lvt) +{ + lvTree *lvt_child; + lvTreeNode *node; + lv_val *cntnr_lv_base; + + assert(NULL != lvt); /* caller should not call if no subtree */ + /* In the case of lv_killarray, the only option we have is to do post-order traversal since we are freeing + * nodes in the tree as we traverse. But in this case, we dont change the tree structure so we are free to + * choose any order. We choose in-order as that is faster than post-order. + */ + for (node = lvAvlTreeFirst(lvt); NULL != node; node = lvAvlTreeNext(node)) + { + if (node->v.mvtype & MV_ALIASCONT) + { + assert(lvt->base_lv->has_aliascont); + assert(!LV_IS_BASE_VAR(node)); + cntnr_lv_base = (lv_val *)node->v.str.addr; /* Extract container pointer */ + assert(NULL != cntnr_lv_base); + assert(LV_IS_BASE_VAR(cntnr_lv_base)); + assert(1 <= cntnr_lv_base->stats.trefcnt); + assert(1 <= cntnr_lv_base->stats.crefcnt); + assert(cntnr_lv_base->tp_var); + DBGRFCT((stderr, "\ntpREST_cntnr_node: Processing container at 0x"lvaddr"\n", node)); + INCR_CREFCNT(cntnr_lv_base); + INCR_TREFCNT(cntnr_lv_base); + assert(0 < (cntnr_lv_base)->stats.trefcnt); + assert(0 < (cntnr_lv_base)->stats.crefcnt); + } + lvt_child = LV_GET_CHILD(node); + if (NULL != lvt_child) /* Descend recursively down this tree as well */ + als_prcs_tprest_cntnr(lvt_child); + } +} + +/* For a given container, decrement the ref count of the creature it points to. Part of unwinding an unmodified + * tp saved variable. + */ +void als_prcs_tpunwnd_cntnr(lvTree *lvt) +{ + lvTree *lvt_child; + lvTreeNode *node; + lv_val *cntnr_lv_base; + + assert(NULL != lvt); /* caller should not call if no subtree */ + /* In the case of lv_killarray, the only option we have is to do post-order traversal since we are freeing + * nodes in the tree as we traverse. But in this case, we dont change the tree structure so we are free to + * choose any order. We choose in-order as that is faster than post-order. + */ + for (node = lvAvlTreeFirst(lvt); NULL != node; node = lvAvlTreeNext(node)) + { + if (node->v.mvtype & MV_ALIASCONT) + { + assert(lvt->base_lv->has_aliascont); + assert(!LV_IS_BASE_VAR(node)); + cntnr_lv_base = (lv_val *)node->v.str.addr; /* Extract container pointer */ + assert(NULL != cntnr_lv_base); + assert(LV_IS_BASE_VAR(cntnr_lv_base)); + assert(1 <= cntnr_lv_base->stats.trefcnt); + assert(1 <= cntnr_lv_base->stats.crefcnt); + /* Note we cannot assert cntnr_lv_base->tp_var here since the tp_var node may have already been freed and + * cleared by unwind processing of the base var itself. We just have to undo our counts here and keep going. + */ + DBGRFCT((stderr, "\ntpUNWND_cntnr_node: Processing container at 0x"lvaddr"\n", cntnr_lv_base)); + DECR_CREFCNT(cntnr_lv_base); + DECR_BASE_REF_NOSYM(cntnr_lv_base, FALSE); + } + lvt_child = LV_GET_CHILD(node); + if (NULL != lvt_child) /* Descend recursively down this tree as well */ + als_prcs_tpunwnd_cntnr(lvt_child); + } +} + +/* This routine deletes the data pointed to by the lv_val and removes the container flag from the value making it just + * a regular NULL/0 value. + */ +void als_prcs_kill_cntnr(lvTree *lvt) +{ + lvTree *lvt_child; + lvTreeNode *node; + lv_val *cntnr_lv_base; + + assert(NULL != lvt); /* caller should not call if no subtree */ + /* In the case of lv_killarray, the only option we have is to do post-order traversal since we are freeing + * nodes in the tree as we traverse. But in this case, we dont change the tree structure so we are free to + * choose any order. We choose in-order as that is faster than post-order. + */ + for (node = lvAvlTreeFirst(lvt); NULL != node; node = lvAvlTreeNext(node)) + { + if (node->v.mvtype & MV_ALIASCONT) + { + assert(lvt->base_lv->has_aliascont); + assert(!LV_IS_BASE_VAR(node)); + cntnr_lv_base = (lv_val *)node->v.str.addr; + assert(NULL != cntnr_lv_base); + assert(LV_IS_BASE_VAR(cntnr_lv_base)); + node->v.mvtype &= ~MV_ALIASCONT; + DECR_CREFCNT(cntnr_lv_base); + DECR_BASE_REF_NOSYM(cntnr_lv_base, TRUE); + } + lvt_child = LV_GET_CHILD(node); + if (NULL != lvt_child) /* Descend recursively down this tree as well */ + als_prcs_kill_cntnr(lvt_child); + } +} + +/* Local routine! + * This routine checks if the supplied container points to an lv_val that is already marked as having been processd in this pass. + * If not, the lv_val is marked and processed recursively. + */ +STATICFNDEF void als_prcs_markreached_cntnr(lvTree *lvt) +{ + lvTree *lvt_child; + lvTreeNode *node; + lv_val *cntnr_lv_base; + + assert(NULL != lvt); /* caller should not call if no subtree */ + /* In the case of lv_killarray, the only option we have is to do post-order traversal since we are freeing + * nodes in the tree as we traverse. But in this case, we dont change the tree structure so we are free to + * choose any order. We choose in-order as that is faster than post-order. + */ + for (node = lvAvlTreeFirst(lvt); NULL != node; node = lvAvlTreeNext(node)) + { + if (node->v.mvtype & MV_ALIASCONT) + { + assert(lvt->base_lv->has_aliascont); + assert(!LV_IS_BASE_VAR(node)); + cntnr_lv_base = (lv_val *)node->v.str.addr; + assert(NULL != cntnr_lv_base); + assert(LV_IS_BASE_VAR(cntnr_lv_base)); + MARK_REACHABLE(cntnr_lv_base); + } + lvt_child = LV_GET_CHILD(node); + if (NULL != lvt_child) /* Descend recursively down this tree as well */ + als_prcs_markreached_cntnr(lvt_child); + } +} + +/* Function to scan an lvval for containers pointing to other structures that need to be scanned in xnew pop processing. + * This goes through the entire tree of lv nodes looking for containers and if it finds any, it finds the base var pointed + * to by the container if it has not already been processed in this pass (as determined by lvtaskcycle). Processing includes + * incrementing refcnts and creating an lv_xnew_ref entry for the base var so we can check it again when the symtab pops to + * see if any containers were created in them that point to the symtab being popped. + */ +void als_prcs_xnewref_cntnr(lvTree *lvt) +{ + lvTree *lvt_child; + lvTreeNode *node; + lv_val *cntnr_lv_base; + lv_xnew_ref *xnewref; + + assert(NULL != lvt); /* caller should not call if no subtree */ + /* In the case of lv_killarray, the only option we have is to do post-order traversal since we are freeing + * nodes in the tree as we traverse. But in this case, we dont change the tree structure so we are free to + * choose any order. We choose in-order as that is faster than post-order. + */ + for (node = lvAvlTreeFirst(lvt); NULL != node; node = lvAvlTreeNext(node)) + { + if (node->v.mvtype & MV_ALIASCONT) + { + assert(lvt->base_lv->has_aliascont); + assert(!LV_IS_BASE_VAR(node)); + cntnr_lv_base = (lv_val *)node->v.str.addr; + assert(NULL != cntnr_lv_base); + assert(LV_IS_BASE_VAR(cntnr_lv_base)); + if (cntnr_lv_base->stats.lvtaskcycle != lvtaskcycle) + { + INCR_CREFCNT(cntnr_lv_base); + INCR_TREFCNT(cntnr_lv_base); + cntnr_lv_base->stats.lvtaskcycle = lvtaskcycle; + if (NULL != xnewref_anchor) + { /* Reuse entry from list */ + xnewref = xnewref_anchor; + xnewref_anchor = xnewref->next; + } else + { /* Malloc an entry. Note that these blocks are put back on the chain anchored at the + * xnewref_anchor global in function "als_check_xnew_var_aliases". They are not freed + * since xnews typically happen in subroutines for temporary periods making the + * likelihood of block reuse high. Also they are small and typically few in number. + */ + xnewref = (lv_xnew_ref *)malloc(SIZEOF(lv_xnew_ref)); + } + xnewref->lvval = cntnr_lv_base; + xnewref->next = curr_symval->xnew_ref_list; + curr_symval->xnew_ref_list = xnewref; + if (LV_HAS_CHILD(cntnr_lv_base)) + XNEWREF_CNTNRS_IN_TREE(cntnr_lv_base); + } + } + lvt_child = LV_GET_CHILD(node); + if (NULL != lvt_child) /* Descend recursively down this tree as well */ + als_prcs_xnewref_cntnr(lvt_child); + } +} + +/* Initialize ZWRite hash table structures used when ZWRiting in an aliased variable environment */ +void als_zwrhtab_init(void) +{ + zwr_zav_blk *zavb, *zavb_next; + + if (zwrhtab && zwrhtab->cleaned) + return; + if (NULL == zwrhtab) + { /* none yet .. allocate and init one */ + zwrhtab = (zwr_hash_table *)malloc(SIZEOF(zwr_hash_table)); + zwrhtab->first_zwrzavb = NULL; + init_hashtab_addr(&zwrhtab->h_zwrtab, ZWR_HTAB_INIT_SIZE, HASHTAB_COMPACT, HASHTAB_SPARE_TABLE); + } else + { /* Have one, reinitialize it */ + assert(zwrhtab->first_zwrzavb); + zavb = zwrhtab->first_zwrzavb; + if (zavb) + { + for (zavb_next = zavb->next; zavb_next; zavb = zavb_next, zavb_next = zavb->next) + { /* Leave one block on queue if it exists .. get rid of others */ + free(zavb); + } + assert(zavb); + zwrhtab->first_zwrzavb = zavb; + } + reinitialize_hashtab_addr(&zwrhtab->h_zwrtab); + } + if (NULL == zwrhtab->first_zwrzavb) + zwrhtab->first_zwrzavb = (zwr_zav_blk *)malloc(SIZEOF(zwr_zav_blk) + (SIZEOF(zwr_alias_var) * ZWR_ZAV_BLK_CNT)); + ZAV_BLK_INIT(zwrhtab->first_zwrzavb, NULL); + zwrhtab->cleaned = TRUE; +} + +/* Obtain a zwr_alias_var slot for the zalias hash table */ +zwr_alias_var *als_getzavslot(void) +{ + zwr_alias_var *zav; + zwr_zav_blk *zavb; + + assert(zwrhtab); + assert(zwrhtab->first_zwrzavb); + + zwrhtab->cleaned = FALSE; /* No longer in a clean/initialized state */ + /* Check if a block can be allocated out of a zavb super block */ + zavb = zwrhtab->first_zwrzavb; + assert(zavb); + if (zavb->zav_free >= zavb->zav_top) + { /* This block is full too .. need a new one */ + zavb = (zwr_zav_blk *)malloc(SIZEOF(zwr_zav_blk) + (SIZEOF(zwr_alias_var) * ZWR_ZAV_BLK_CNT)); + ZAV_BLK_INIT(zavb, zwrhtab->first_zwrzavb); + zwrhtab->first_zwrzavb = zavb; + } + assert(zavb->zav_free < zavb->zav_top); + zav = zavb->zav_free++; + zav->value_printed = FALSE; + return zav; +} + +/* See if lv_val is associated with a base named var in the current symval. + * Scan the hash table for valid entries and see if any of them point to supplied lv_val. + * If so, return the hash table entry which contains the var's name that is "lowest" which + * necessitates a full scan of the hash table (which ZWrite processing is going to do several + * times anyway. + */ +ht_ent_mname *als_lookup_base_lvval(lv_val *lvp) +{ + ht_ent_mname *htep, *htep_top, *htep_loweq; + lv_val *lvhtval; + + assert(LV_IS_BASE_VAR(lvp)); + htep_loweq = NULL; + htep = curr_symval->h_symtab.base; + htep_top = curr_symval->h_symtab.base + curr_symval->h_symtab.size; + assert(htep_top == curr_symval->h_symtab.top); + for (; htep < htep_top; htep++) + { + if (HTENT_VALID_MNAME(htep, lv_val, lvhtval) && '$' != *htep->key.var_name.addr && lvp == lvhtval) + { /* HT entry is valid and has a key that is not a $ZWRTAC type key and the lval matches + * so we have a candidate to check for "lowest" alias name for given lv_val addr. + */ + if (htep_loweq) + { /* See current champ higher than candidate, then get new champ */ + if (0 < memvcmp(htep_loweq->key.var_name.addr, htep_loweq->key.var_name.len, + htep->key.var_name.addr, htep->key.var_name.len)) + htep_loweq = htep; + } else + /* First time thru, free-ride assignment */ + htep_loweq = htep; + } + } + return htep_loweq; +} + +/* Routine to do a garbage collection on the lv_vals in the current symbol table in order to detect if + * there is any "lost data" which are 2 or more base lv_vals that point to each other thus keeping their + * reference counts non-zero and prevent them from being deleted but otherwise have no entry in the hash table + * themselves nor are linked to by any combination of container var linked arrays that do have an entry in + * the hash table -- in other words, they are totally orphaned with no way to retrieve them so are effectively + * dead but are not able to be killed in an automated fashion. This routine will find and kill those blocks + * returning a count of the lv_vals thus found and killed. + * + * Operation: + * + * 1) Run lv_blks which contain all lv_val structures in use for this symbol table. + * 2) Record each base lv_val in our version of the array used by stp_gcol. Base lv_vals can be identified + * by having a non-zero parent.sym field pointing to a block with type MV_SYM. There are 3 exceptions to + this: In UNIX, the zsearch_var, zsearch_dir1, and zsearch_dir2 fields contain lv_vals that should not be + released. Check for and avoid them. + * 3) Increment lvtaskcycle with which we will mark lv_vals as having been marked accessible as we discover them. + * 4) Go through the hashtable. Set the lvtaskcycle field to mark the lv_val "reachable". + * 5) If the lv_val has descendants, run the decendant chain to look for container vars. + * 6) The base lv_vals that container vars point to, if not already marked "reachable" will be so marked and + * the search recursively invoked on the new var. + * 7) Do the same by running the mv_stent chain marking the temporarily displaced vars for parameters and NEW's as + * "reachable". + * 8) Do the same by running the TP stack marking local variable copies made for this symbol table as reachable. + * 9) Mark any pending return argument as "reachable". + * 10) Once the "reachable" search is complete, run through the created list of lv_vals and locate any that were + * not reachable. If they remain undeleted (deleted will have parent.sym field zeroed), delete them. + * + * Note this routine uses the same buffer structure that "stp_gcol" uses except it loads its array with lv_val* + * instead of mstr*. An address is an address.. + */ +int als_lvval_gc(void) +{ + int killcnt; + lv_blk *lv_blk_ptr; + ht_ent_mname *htep, *htep_top; + lv_val *lvp, *lvlimit; + lv_val **lvarraycur, **lvarray, **lvarraytop, **lvptr; + mv_stent *mv_st_ent; + tp_frame *tf; + tp_var *restore_ent; + lvTree *lvt_child; + symval *sym; + DEBUG_ONLY(uint4 savelvtaskcycle;) + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + assert(!suspend_lvgcol); + DBGRFCT((stderr, "als_lvval_gc: Beginning lv_val garbage collection\n")); + if (NULL == stp_array) + /* Same initialization as is in stp_gcol_src.h */ + stp_array = (mstr **)malloc((stp_array_size = STP_MAXITEMS) * SIZEOF(mstr *)); + + lvarraycur = lvarray = (lv_val **)stp_array; + lvarraytop = lvarraycur + stp_array_size; + + /* Steps 1,2 - find all the base lv_vals and put in list */ + for (lv_blk_ptr = curr_symval->lv_first_block; lv_blk_ptr; lv_blk_ptr = lv_blk_ptr->next) + { + for (lvp = (lv_val *)LV_BLK_GET_BASE(lv_blk_ptr), lvlimit = LV_BLK_GET_FREE(lv_blk_ptr, lvp); + lvp < lvlimit; lvp++) + { + sym = LV_SYMVAL(lvp); + assert((NULL == sym) || SYM_IS_SYMVAL(sym)); + if ((NULL != sym) UNIX_ONLY(&& (TREF(zsearch_var) != lvp)) + UNIX_ONLY(&& (TREF(zsearch_dir1) != lvp) && (TREF(zsearch_dir2) != lvp))) + { /* Put it in the list */ + assert(0 < lvp->stats.trefcnt); + if (lvarraycur >= lvarraytop) + { /* Need more room -- expand */ + stp_expand_array(); + lvarraycur = lvarray = (lv_val **)stp_array; + lvarraytop = lvarraycur + stp_array_size; + } + *lvarraycur++ = lvp; + } + } + } + /* Step 3, increment lvtaskcycle to mark "reachable" lv_vals */ + INCR_LVTASKCYCLE; + DEBUG_ONLY(savelvtaskcycle = lvtaskcycle); + /* Steps 4,5,6 - Find and mark reachable lv_vals */ + DBGRFCT((stderr, "als_lvval_gc: Starting symtab scan\n")); + htep = curr_symval->h_symtab.base; + htep_top = curr_symval->h_symtab.base + curr_symval->h_symtab.size; + assert(htep_top == curr_symval->h_symtab.top); + for (; htep < htep_top; htep++) + { + if (HTENT_VALID_MNAME(htep, lv_val, lvp)) + { /* HT entry is valid. Note for purposes of this loop, we do NOT bypass $ZWRTAC type keys since + * they are valid reachable variables even if hidden */ + MARK_REACHABLE(lvp); + } + } + /* Step 7 - Run the mv_stent chain marking those vars as reachable. */ + DBGRFCT((stderr, "als_lvval_gc: Starting mv_stent scan\n")); + for (mv_st_ent = mv_chain; mv_st_ent; mv_st_ent = (mv_stent *)(mv_st_ent->mv_st_next + (char *)mv_st_ent)) + { + switch (mv_st_ent->mv_st_type) + { /* The types processed here contain lv_vals we want to mark */ + case MVST_NTAB: + lvp = mv_st_ent->mv_st_cont.mvs_ntab.save_value; + DBGRFCT((stderr, "als_lvval_gc: NTAB at 0x"lvaddr" has save value 0x"lvaddr"\n", + mv_st_ent, lvp)); + assert(NULL != lvp); + break; + case MVST_PVAL: + /* Note the save_value in the PVAL types below can be zero since they are created in + * op_bindparm with a NULL save_value value and later filled in by op_bindparm. It is + * possible to trigger this code in between the push_parm call of the caller and the + * op_bindparm call of the callee when tracing or with an outofband event. In that case, + * we don't want that NULL save_value pointer ending our loop prematurely. + */ + lvp = mv_st_ent->mv_st_cont.mvs_pval.mvs_ptab.save_value; + DBGRFCT((stderr, "als_lvval_gc: PVAL at 0x"lvaddr" has save value 0x"lvaddr"\n", + mv_st_ent, lvp)); + assert(NULL != mv_st_ent->mv_st_cont.mvs_pval.mvs_val); + /* Mark created lv_val to hold current value as reachable as it may not (yet) be in the + * hashtable if op_bindparm has not yet run. + */ + MARK_REACHABLE(mv_st_ent->mv_st_cont.mvs_pval.mvs_val); + if (NULL == lvp) + continue; /* Don't end loop prematurely */ + break; + case MVST_NVAL: + lvp = mv_st_ent->mv_st_cont.mvs_nval.mvs_ptab.save_value; + DBGRFCT((stderr, "als_lvval_gc: NVAL at 0x"lvaddr" has save value 0x"lvaddr"\n", + mv_st_ent, lvp)); + assert(NULL != mv_st_ent->mv_st_cont.mvs_nval.mvs_val); + /* Mark created lv_val to hold current value as reachable as it may not (yet) be in the + * hashtable if op_bindparm has not yet run. + */ + MARK_REACHABLE(mv_st_ent->mv_st_cont.mvs_nval.mvs_val); + assert(NULL != lvp); + break; + case MVST_STAB: + /* The symbol table is changing to be other than the table we are using so we can + * stop the loop now. Exiting the switch with lvp NULL indicates that. + */ + DBGRFCT((stderr, "als_lvval_gc: STAB mv_stent at 0x"lvaddr" stops mv_stent scan\n", + mv_st_ent)); + lvp = NULL; + break; + default: + DBGRFCT((stderr, "als_lvval_gc: Ignoring mv_stent type %d\n", mv_st_ent->mv_st_type)); + continue; + } + if (NULL == lvp) + break; + MARK_REACHABLE(lvp); + } + /* Step 8 - Run the TP stack to see if there is anything we can mark reachable */ + DBGRFCT((stderr, "als_lvval_gc: Starting TP stack scan\n")); + for (tf = tp_pointer; NULL != tf && tf->sym == curr_symval; tf = tf->old_tp_frame) + { + for (restore_ent = tf->vars; NULL != restore_ent; restore_ent = restore_ent->next) + { /* Since TP keeps its own use count on these sorts of variables, we will mark both the + * current and saved values in these blocks. This is because the "current value" could + * be detached from the hash table at this point but is still viable while we hold a use + * count on it. + */ + MARK_REACHABLE(restore_ent->current_value); + MARK_REACHABLE(restore_ent->save_value); + } + } + /* Step 9 - Mark any pending alias return argument as reachable */ + if (NULL != alias_retarg) + { /* There is a pending alias return arg (container). Find the lv it is pointing to and mark it and its progeny + * as reachable. + */ + assert(0 != (MV_ALIASCONT & alias_retarg->mvtype)); + lvp = (lv_val *)alias_retarg->str.addr; + assert(LV_IS_BASE_VAR(lvp)); + MARK_REACHABLE(lvp); + } + /* Step 10 - Run the list of base lv_vals we created earlier and see which ones were not marked with the current + * cycle. Note they may have already been deleted after the first one gets deleted so we can check for that + * by looking for a zeroed parent field. Note the object of this code is not to force-delete the vars as we + * encounter them but by performing a deletion of their arrays, we will kill the interlinking container vars that + * are keeping these vars alive. + */ + killcnt = 0; + DBGRFCT((stderr, "\nals_lvval_gc: final orphaned lvval scan\n")); + for (lvptr = lvarray; lvptr < lvarraycur; ++lvptr) + { + lvp = *lvptr; + if (lvp->stats.lvtaskcycle != lvtaskcycle) + { /* Have an orphaned lv_val */ + DBGRFCT((stderr, "\nals_lvval_gc: lvval 0x"lvaddr" has been identified as orphaned\n", lvp)); + ++killcnt; + if (LV_SYMVAL(lvp)) + { /* Var is still intact, kill it. Note that in this situation, since there are no hash table + * entries pointing to us, our container refs and total refs should be equal. We can't + * use the "regular" DECR macros because those get us into trouble. For example if this + * var has a container pointing to another var who has a container pointing to us and it + * is only those pointers keeping both vars alive, decrementing our counter causes it to + * become zero which messes up the deletion of the other var's container since the refcnts + * are already zero. What we will do instead is INCREASE the trefcnt to keep this var from + * being deleted, then drive the kill of any array it has to spur these vars to go away. + */ + assert(LV_IS_BASE_VAR(lvp)); + DBGRFCT((stderr, "\nals_lvval_gc: Working to release unreachable lvval 0x"lvaddr"\n", lvp)); + assert(lvp->stats.trefcnt == lvp->stats.crefcnt); + INCR_TREFCNT(lvp); + lvt_child = LV_GET_CHILD(lvp); + if (NULL != lvt_child) + { + assert(lvp == (lv_val *)LVT_PARENT(lvt_child)); + LV_CHILD(lvp) = NULL; + lv_killarray(lvt_child, FALSE); + } + DECR_BASE_REF_NOSYM(lvp, FALSE); /* Var might go away now, or later if need more deletes first */ + } else + DBGRFCT((stderr, "\nals_lvval_gc: Seems to have become released -- lvval 0x"lvaddr"\n", lvp)); + } + } + DBGRFCT((stderr, "\nals_lvval_gc: final orphaned lvval scan completed\n")); +# ifdef DEBUG + /* The removal of a reference for each lv_val should have done it but let's go back and verify they all + * went away. If not, then it is not a simple case of user silliness and we have a problem. + */ + for (lvptr = lvarray; lvptr < lvarraycur; ++lvptr) + { + lvp = *lvptr; + if (lvp->stats.lvtaskcycle != lvtaskcycle && LV_SYMVAL(lvp)) + { /* Var is still intact, kill it */ + assert(FALSE); + } + } +# endif + assert(lvtaskcycle == savelvtaskcycle); + DBGRFCT((stderr, "als_lvval_gc: GC complete -- recovered %d lv_vals\n", killcnt)); + SPGC_since_LVGC = 0; + return killcnt; +} + +# ifdef DEBUG_ALIAS +/* Routine to check lv_val monitoring. If any lv_vals that were created during the monitoring period still exist, emit a + * message to that effect with the lv_val address. In this manner we hope to find lv_val leaks (if any) in various tests. + * Note that this is a debugging (not debug build) only routine since its costs are non-trivial and is only enabled + * when necessary. Other tests check for memory leaks so if they find one, this monitoring can be used to discover the + * source so this is not needed for test coverage. + */ +void als_lvmon_output(void) +{ + symval *lvlsymtab; + lv_blk *lvbp; + lv_val *lvp, *lvp_top; + + flush_pio(); + for (lvlsymtab = curr_symval; lvlsymtab; lvlsymtab = lvlsymtab->last_tab) + { + for (lvbp = curr_symval->lv_first_block; lvbp; lvbp = lvbp->next) + { + for (lvp = (lv_val *)LV_BLK_GET_BASE(lvbp), lvp_top = LV_BLK_GET_FREE(lvbp, lvp); lvp < lvp_top; lvp++) + { + if (lvp->lvmon_mark) + { /* lv_val slot not used as an sbs and is marked. Report it */ + FPRINTF(stderr, "als_lvmon_output: lv_val at 0x"lvaddr" is still marked\n", lvp); + } + } + } + } + fflush(stderr); +} +# endif diff --git a/sr_port/alloc_reg.c b/sr_port/alloc_reg.c new file mode 100644 index 0000000..14b138a --- /dev/null +++ b/sr_port/alloc_reg.c @@ -0,0 +1,217 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" +#include "gtm_string.h" +#include + +#include "cmd_qlf.h" +#include "gtmdbglvl.h" +#include "compiler.h" +#include "mdq.h" +#include "opcode.h" +#include "alloc_reg.h" +#include "cdbg_dump.h" + +#define MAX_TEMP_COUNT 128 + +GBLDEF int4 sa_temps[VALUED_REF_TYPES]; +GBLDEF int4 sa_temps_offset[VALUED_REF_TYPES]; + +GBLREF int mvmax; +GBLREF triple t_orig; +GBLREF uint4 gtmDebugLevel; +GBLREF command_qualifier cmd_qlf; + +LITDEF int4 sa_class_sizes[VALUED_REF_TYPES] = +{ + 0 /* dummy for slot zero */ + ,SIZEOF(mval*) /* TVAR_REF */ + ,SIZEOF(mval) /* TVAL_REF */ + ,SIZEOF(mint) /* TINT_REF */ + ,SIZEOF(mval*) /* TVAD_REF */ + ,SIZEOF(char*) /* TCAD_REF */ +}; +LITREF octabstruct oc_tab[]; + +error_def(ERR_TMPSTOREMAX); + +void alloc_reg(void) +{ + triple *x, *y, *ref; + tbp *b; + oprtype *j; + opctype opc, opx; + char tempcont[VALUED_REF_TYPES][MAX_TEMP_COUNT], dest_type; + int r, c, temphigh[VALUED_REF_TYPES]; + unsigned int oct; + int4 size; + + memset(&tempcont[0][0], 0, SIZEOF(tempcont)); + memset(&temphigh[0], -1, SIZEOF(temphigh)); + temphigh[TVAR_REF] = mvmax - 1; + COMPDBG(PRINTF(" \n\n\n\n************************************ Begin alloc_reg scan *****************************\n");); + dqloop(&t_orig, exorder, x) + { + COMPDBG(PRINTF(" ************************ Triple Start **********************\n");); + COMPDBG(cdbg_dump_triple(x, 0);); + opc = x->opcode; + switch (opc) + { + case OC_NOOP: + case OC_PARAMETER: + continue; + case OC_LINESTART: + /* If the next triple is also a LINESTART, then this is a comment line. Therefore + eliminate this LINESTART */ + opx = x->exorder.fl->opcode; + if ((OC_LINESTART == opx) || (OC_LINEFETCH == opx) || (OC_ISFORMAL == opx)) + { + opc = x->opcode = OC_NOOP; + COMPDBG(PRINTF(" ** Converting triple to NOOP (rsn 1) **\n");); + continue; /* continue, because 'normal' NOOP continues from this switch */ + } + /* There is a special case in the case of NOLINE_ENTRY being specified. If a blank line is followed + by a line with a label and that label generates fetch information, the generated triple sequence + will be LINESTART (from blank line), ILIT (count from PREVIOUS fetch), LINEFETCH. We will detect + that sequence here and change the LINESTART to a NOOP. + */ + if (!(cmd_qlf.qlf & CQ_LINE_ENTRY) && (OC_ILIT == opx) && (NULL != x->exorder.fl->exorder.fl) + && (OC_LINEFETCH == x->exorder.fl->exorder.fl->opcode)) + { + opc = x->opcode = OC_NOOP; + COMPDBG(PRINTF(" ** Converting triple to NOOP (rsn 2) **\n");); + continue; /* continue, because 'normal' NOOP continues from this switch */ + } + break; + case OC_LINEFETCH: + case OC_FETCH: + assert((TRIP_REF == x->operand[0].oprclass) + && (OC_ILIT == x->operand[0].oprval.tref->opcode)); + if (x->operand[0].oprval.tref->operand[0].oprval.ilit == mvmax) + { + x->operand[0].oprval.tref->operand[0].oprval.ilit = 0; + x->operand[1].oprclass = 0; + } + break; + case OC_STO: + /* If we are storing a literal e.g. s x="hi", don't call op_sto, because we do not + * need to check if the literal is defined. OC_STOLIT will be an in-line copy. + * Bypass this if we have been requested to not do inline literals. + */ + if ((cmd_qlf.qlf & CQ_INLINE_LITERALS) && (TRIP_REF == x->operand[1].oprclass) + && (OC_LIT == x->operand[1].oprval.tref->opcode)) + opc = x->opcode = OC_STOLIT; + break; + case OC_EQU: + /* Check to see if the operation is a x="" or a ""=x, if so (and this is a very common case) + * use special opcode OC_EQUNUL, which takes one argument and just checks length for zero + */ + if ((TRIP_REF == x->operand[0].oprclass) && (OC_LIT == x->operand[0].oprval.tref->opcode) + && (0 == x->operand[0].oprval.tref->operand[0].oprval.mlit->v.str.len)) + { + x->operand[0] = x->operand[1]; + x->operand[1].oprclass = 0; + opc = x->opcode = OC_EQUNUL; + } else if ((TRIP_REF == x->operand[1].oprclass) && (OC_LIT == x->operand[1].oprval.tref->opcode) + && (0 == x->operand[1].oprval.tref->operand[0].oprval.mlit->v.str.len)) + { + x->operand[1].oprclass = 0; + opc = x->opcode = OC_EQUNUL; + } + break; + } + for (j = x->operand, y = x; j < ARRAYTOP(y->operand); ) + { + if (TRIP_REF == j->oprclass) + { + ref = j->oprval.tref; + if (OC_PARAMETER == ref->opcode) + { + y = ref; + j = y->operand; + continue; + } + if (r = ref->destination.oprclass) /* Note assignment */ + { + dqloop(&ref->backptr, que, b) + { + if (b->bpt == y) + { + dqdel(b, que); + break; + } + } + if ((ref->backptr.que.fl == &ref->backptr) && (TVAR_REF != r)) + tempcont[r][j->oprval.tref->destination.oprval.temp] = 0; + } + } + j++; + } + if (OC_PASSTHRU == x->opcode) + { + COMPDBG(PRINTF(" *** OC_PASSTHRU opcode being NOOP'd\n");); + x->opcode = OC_NOOP; + continue; + } + if (!(dest_type = x->destination.oprclass)) /* Note assignment */ + { + oct = oc_tab[opc].octype; + if ((oct & OCT_VALUE) && (x->backptr.que.fl != &x->backptr) && !(oct & OCT_CGSKIP)) + { + if (!(oct & OCT_MVADDR) && (x->backptr.que.fl->que.fl == &x->backptr) + && (OC_STO == (y = x->backptr.que.fl->bpt)->opcode) && (y->operand[1].oprval.tref == x) + && (OC_VAR == y->operand[0].oprval.tref->opcode)) + { + x->destination = y->operand[0]; + y->opcode = OC_NOOP; + y->operand[0].oprclass = y->operand[1].oprclass = 0; + } else + { + oct &= OCT_VALUE | OCT_MVADDR; + assert ((OCT_MVAL == oct) || (OCT_MINT == oct) || ((OCT_MVADDR | OCT_MVAL) == oct) + || (OCT_CDADDR == oct)); + r = (OCT_MVAL == oct) ? TVAL_REF : (((OCT_MVADDR | OCT_MVAL) == oct) ? TVAD_REF + : ((OCT_MINT == oct) ? TINT_REF : TCAD_REF)); + for (c = 0; tempcont[r][c] && (MAX_TEMP_COUNT > c); c++) + ; + if (MAX_TEMP_COUNT <= c) + rts_error(VARLSTCNT(1) ERR_TMPSTOREMAX); + tempcont[r][c] = 1; + x->destination.oprclass = r; + x->destination.oprval.temp = c; + if (c > temphigh[r]) + temphigh[r] = c; + if (OC_CDADDR == x->opcode) + x->opcode = OC_NOOP; + } + } + } else if (TRIP_REF == dest_type) + { + assert(x->destination.oprval.tref->destination.oprclass); + x->destination = x->destination.oprval.tref->destination; + } + } + for (r = 0; VALUED_REF_TYPES > r ;r++) + sa_temps[r] = temphigh[r] + 1; + sa_temps_offset[TVAR_REF] = sa_temps[TVAR_REF] * sa_class_sizes[TVAR_REF]; + size = sa_temps[TVAL_REF] * sa_class_sizes[TVAL_REF]; + sa_temps_offset[TVAL_REF] = size; + /* Since we need to align the temp region to the largest types, align even int temps to SIZEOF(char*) */ + size += ROUND_UP2(sa_temps[TINT_REF] * sa_class_sizes[TINT_REF], SIZEOF(char *)); + sa_temps_offset[TINT_REF] = size; + size += sa_temps[TVAD_REF] * sa_class_sizes[TVAD_REF]; + sa_temps_offset[TVAD_REF] = size; + size += sa_temps[TCAD_REF] * sa_class_sizes[TCAD_REF]; + sa_temps_offset[TCAD_REF] = size; +} diff --git a/sr_port/alloc_reg.h b/sr_port/alloc_reg.h new file mode 100644 index 0000000..7f5fce9 --- /dev/null +++ b/sr_port/alloc_reg.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef ALLOC_REG_INCLUDED +#define ALLOC_REG_INCLUDED + +void alloc_reg(void); + +#endif /* ALLOC_REG_INCLUDED */ diff --git a/sr_port/arit.h b/sr_port/arit.h new file mode 100644 index 0000000..22e7540 --- /dev/null +++ b/sr_port/arit.h @@ -0,0 +1,54 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* + arit.h : defines constants for arithmetic operations. + gdsroot.h has a definition for the MAX_NUM_SUBSC_LEN that + depends on the number of significant digits available. If + it changes from 18, the define must be updated +*/ +#define NUM_DEC_DG_1L 9 /* Number of decimal digits in a int4 */ +#define NUM_DEC_DG_2L 18 /* Number of decimal digits in two longs */ + +#define EXP_IDX_BIAL (9+MV_XBIAS) /* Max. biased exponent index within a int4 */ +#define EXP_IDX_BIAQ (18+MV_XBIAS) /* Max. biased exponent index within two longs */ +#define EXPLO (-42+MV_XBIAS) /* Min. biased exponent */ +#define EXPHI (48+MV_XBIAS) /* Max. biased exponent */ +/* Note: The above values for EXPLO and EXPHI set up a range of 20 thru 109 which still leaves room for expansion + * in the 7-bit exponent whose full range is 0 thru 127. One might be tempted to increase EXPLO/EXPHI to accommodate + * the available range but should not because these values also affect how numeric subscripts are stored in the database. + * + * The entire numeric range currently supported by GT.M along with how it represents them internally as well as + * inside the database (the first byte of the numeric subscript) is captured in the table below. + * + * -------------------------|---------------------------------------------------------------------------------------- + * Numeric value | ... [-1E46, -1E-43] ... [0] ... [1E-43, 1E46] ... + * mval.e representation | ... [ 109, 20] ... [0] ... [ 20, 109] ... + * mval.sgn representation | ... [ 1, 1] ... [0] ... [ 0, 0] ... + * | | | | | | | | + * | | | | | | | | + * | | | | | | | | + * | v v v v v v v + * subscript representation | [0x00, 0x11] [0x12, 0x6b] [0x6c,0x7F] [0x80] [0x81, 0x93] [0x94, 0xED] [0xEE, 0xFF] + * in database | + * -------------------------|---------------------------------------------------------------------------------------- + * + * Any increase in EXPHI will encroach the currently unused interval [0x00,0x11] and has to be done with caution + * as a few of those are used for different purposes (0x01 to represent a null subscript in standard null collation, + * 0x02 to be used for spanning node subscripts etc.) + */ + +#define EXP_INT_OVERF (7+MV_XBIAS) /* Upper bound on MV_INT numbers */ +#define EXP_INT_UNDERF (-3+MV_XBIAS) /* Lower bound on MV_INT numbers (includes + * integers & fractions upto 3 decimal places */ +#define MANT_LO 100000000 /* mantissa.1 >= MANT_LO */ +#define MANT_HI 1000000000 /* mantissa.1 < MANT_HI , mantissa.0 < MANT_HI */ +#define INT_HI 1000000 /* -INT_HI < mv_int < INT_HI */ diff --git a/sr_port/asc2i.c b/sr_port/asc2i.c new file mode 100644 index 0000000..5d9e1f5 --- /dev/null +++ b/sr_port/asc2i.c @@ -0,0 +1,45 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +GBLREF seq_num seq_num_zero; +GBLREF seq_num seq_num_minus_one; + +int4 asc2i(uchar_ptr_t p, int4 len) +{ + uchar_ptr_t c; + int4 ret; + + ret = 0; + for (c = p + len; c > p; p++) + { if (*p > '9' || *p < '0') + return -1; + ret = ret * 10; + ret += *p - '0'; + } + return ret; +} + +qw_num asc2l(uchar_ptr_t p, int4 len) +{ + uchar_ptr_t c; + qw_num ret; + + QWASSIGN(ret, seq_num_zero); + for (c = p + len; c > p; p++) + { if (*p > '9' || *p < '0') + return seq_num_minus_one; + QWMULBYDW(ret, ret, 10); + QWINCRBYDW(ret, *p - '0'); + } + return ret; +} diff --git a/sr_port/asc_hex2i.c b/sr_port/asc_hex2i.c new file mode 100644 index 0000000..23577db --- /dev/null +++ b/sr_port/asc_hex2i.c @@ -0,0 +1,34 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +unsigned int asc_hex2i(p,len) +char *p; +int len; +{ + char *c; + int ret; + + ret = 0; + for (c = p + len; c > p; p++) + { + if (*p >= '0' && *p <= '9') + ret = ret * 16 + *p - '0'; + else if (*p >= 'a' && *p <= 'f') + ret = ret * 16 + *p - 'a' + 10; + else if (*p >= 'A' && *p <= 'F') + ret = ret * 16 + *p - 'A' + 10; + else + return (uint4)-1; + } + return ret; +} diff --git a/sr_port/aswp.h b/sr_port/aswp.h new file mode 100644 index 0000000..ed05cbb --- /dev/null +++ b/sr_port/aswp.h @@ -0,0 +1,30 @@ +/**************************************************************** + * * + * Copyright 2005 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef ASWP_H_INCLUDED +#define ASWP_H_INCLUDED + +#ifdef __hppa + +int aswp3(sm_int_ptr_t /* location */, int4 /* value */, sm_global_latch_ptr_t /* latch */); +#define ASWP(A,B,C) aswp3((sm_int_ptr_t)(A), B, C) + + +#else + +int aswp(sm_int_ptr_t /* location */, int4 /* value */); +int aswp_secshr(sm_int_ptr_t /* location */, int4 /* value */); +#define ASWP(A,B,C) aswp((sm_int_ptr_t)(A), B) + +#endif + +#endif + diff --git a/sr_port/azl_geturxlab.c b/sr_port/azl_geturxlab.c new file mode 100644 index 0000000..3def7ff --- /dev/null +++ b/sr_port/azl_geturxlab.c @@ -0,0 +1,33 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "urx.h" + +bool azl_geturxlab (addr, rp) +char *addr; +urx_rtnref *rp; +{ + urx_labref *lp; + + assert (rp->lab); + for (lp = rp->lab; lp; lp = lp->next) + { + urx_addr *ap; + + for (ap = lp->addr; ap; ap = ap->next) + if (addr == (char *)ap->addr) + break; + if (ap) + return TRUE; + } + return FALSE; +} diff --git a/sr_port/azl_geturxrtn.c b/sr_port/azl_geturxrtn.c new file mode 100644 index 0000000..779d3f8 --- /dev/null +++ b/sr_port/azl_geturxrtn.c @@ -0,0 +1,35 @@ +/**************************************************************** + * * + * Copyright 2001, 2004 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "urx.h" + +GBLREF urx_rtnref urx_anchor; + +bool azl_geturxrtn(char *addr, mstr *rname, urx_rtnref **rp) +{ + assert(urx_anchor.len == 0); + for (*rp = urx_anchor.next; *rp; *rp = (*rp)->next) + { + urx_addr *ap; + + for (ap = (*rp)->addr; ap; ap = ap->next) + if (addr == (char *)ap->addr) + break; + if (ap) + { + rname->len = (*rp)->len; + rname->addr = (char *)&(*rp)->name[0]; + return TRUE; + } + } + return FALSE; +} diff --git a/sr_port/backup_block.c b/sr_port/backup_block.c new file mode 100644 index 0000000..d838f99 --- /dev/null +++ b/sr_port/backup_block.c @@ -0,0 +1,99 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gtm_stdio.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gdsblk.h" +#include "shmpool.h" +#include "memcoherency.h" +#include "mupipbckup.h" + +GBLREF uint4 process_id; + +boolean_t backup_block(sgmnt_addrs *csa, block_id blk, cache_rec_ptr_t backup_cr, sm_uc_ptr_t backup_blk_p) +{ + uint4 bsiz; + int4 required; + shmpool_buff_hdr_ptr_t sbufh_p; + shmpool_blk_hdr_ptr_t sblkh_p; + boolean_t ret = TRUE, is_bg; + sgmnt_data_ptr_t csd; + sm_uc_ptr_t bkp_src_blk; + + + csd = csa->hdr; + is_bg = (dba_bg == csd->acc_meth); + assert(is_bg || (dba_mm == csd->acc_meth)); + /* Should have EITHER backup cr (BG mode) or buffer pointer (MM mode) */ + assert((is_bg && (NULL != backup_cr) && (NULL == backup_blk_p)) || + (!is_bg && (NULL == backup_cr) && (NULL != backup_blk_p))); + if (is_bg) + { /* Get buffer address from the cache record */ + VMS_ONLY(assert(0 == backup_cr->shmpool_blk_off)); + assert(backup_cr->in_cw_set);/* ensure the buffer has been pinned (from preemption in db_csh_getn) */ + backup_blk_p = GDS_ANY_REL2ABS(csa, backup_cr->buffaddr); + } + bsiz = ((blk_hdr_ptr_t)(backup_blk_p))->bsiz; + sbufh_p = csa->shmpool_buffer; + assert(bsiz <= sbufh_p->blk_size); + + /* Obtain block from shared memory pool. If we can't get the block, then backup will be effectively terminated. */ + sblkh_p = shmpool_blk_alloc(csa->region, SHMBLK_BACKUP); + if (((shmpool_blk_hdr_ptr_t)-1L) == sblkh_p) + return FALSE; /* Backup died for whatever reason. Backup failure already dealt with in shmpool_blk_alloc() */ + + /* Fill the block we have been assigned in before marking it valid */ + sblkh_p->blkid = blk; + if (is_bg) + { + assert(NULL != backup_cr); + sblkh_p->use.bkup.ondsk_blkver = backup_cr->ondsk_blkver; + assert(((blk_hdr_ptr_t)backup_blk_p)->bsiz >= SIZEOF(blk_hdr)); + } else /* For MM version, no dynamic conversions take place so just record block as we know it is */ + sblkh_p->use.bkup.ondsk_blkver = csd->desired_db_format; + bkp_src_blk = backup_blk_p; +# ifdef GTM_CRYPT + /* If the database is encrypted, the old_block will be in the encrypted twin buffer. Fetch it from the encrypted + * twin counter part and write that to the backup buffer instead. + */ + if (csd->is_encrypted) + { + DBG_ENSURE_PTR_IS_VALID_GLOBUFF(csa, csd, backup_blk_p); + bkp_src_blk = GDS_ANY_ENCRYPTGLOBUF(backup_blk_p, csa); + DBG_ENSURE_PTR_IS_VALID_ENCTWINGLOBUFF(csa, csd, bkp_src_blk); + } +# endif + /* Adjust bsiz to be within database block size range. Asserts above will ensure this IS the case for DBG. */ + if (bsiz < SIZEOF(blk_hdr)) + bsiz = SIZEOF(blk_hdr); + else if (bsiz > sbufh_p->blk_size) + bsiz = sbufh_p->blk_size; + /* Copy block information to data portion of shmpool block just following header */ + memcpy((sblkh_p + 1), bkp_src_blk, bsiz); + /* Need a write coherency fence here as we want to make sure the above info is stored and + * reflected to other processors before we mark the block valid. + */ + SHM_WRITE_MEMORY_BARRIER; + sblkh_p->valid_data = TRUE; + /* And another write barrier to advertise its cleanliness to other processors */ + SHM_WRITE_MEMORY_BARRIER; + + return TRUE; +} diff --git a/sr_port/base_frame.c b/sr_port/base_frame.c new file mode 100644 index 0000000..51dc726 --- /dev/null +++ b/sr_port/base_frame.c @@ -0,0 +1,62 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "rtnhdr.h" +#include "stack_frame.h" + +GBLREF unsigned char *stacktop, *stackwarn, *msp; +GBLREF stack_frame *frame_pointer; + +void base_frame(rhdtyp *base_address) +{ + void gtm_ret_code(); /* This is an external which points to code without an entry mask */ + unsigned char *msp_save; + stack_frame *fp; + + error_def(ERR_STACKOFLOW); + error_def(ERR_STACKCRIT); + + if ((INTPTR_T)msp & 1) /* synchronize mumps stack on even boundary */ + msp--; + if ((INTPTR_T)msp & 2) + msp -= 2; +#ifdef GTM64 + if ((INTPTR_T)msp & 4) + msp -= 4; +#endif /* GTM64 */ + + msp_save = msp; + msp -= SIZEOF(stack_frame) + SIZEOF(stack_frame *); + if (msp <= stackwarn) + { + if (msp <= stacktop) + { + msp = msp_save; + rts_error(VARLSTCNT(1) ERR_STACKOFLOW); + } + else + rts_error(VARLSTCNT(1) ERR_STACKCRIT); + } + *(stack_frame **)((stack_frame *)msp + 1) = frame_pointer; + frame_pointer = fp = (stack_frame *)msp; + memset(fp, 0, SIZEOF(stack_frame)); + fp->ctxt = GTM_CONTEXT(gtm_ret_code); + fp->mpc = CODE_ADDRESS(gtm_ret_code); + fp->rvector = base_address; + fp->temps_ptr = (unsigned char *)fp; + fp->vartab_len = 0; + fp->vartab_ptr = (char *)fp; + fp->type = SFT_COUNT; +} diff --git a/sr_port/bg_update.h b/sr_port/bg_update.h new file mode 100644 index 0000000..3cc62e6 --- /dev/null +++ b/sr_port/bg_update.h @@ -0,0 +1,20 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __BG_UPDATE_H__ +#define __BG_UPDATE_H__ + +enum cdb_sc bg_update(cw_set_element *cs, trans_num ctn, trans_num effective_tn, sgm_info *si); + +enum cdb_sc bg_update_phase1(cw_set_element *cs, trans_num ctn, sgm_info *si); +enum cdb_sc bg_update_phase2(cw_set_element *cs, trans_num ctn, trans_num effective_tn, sgm_info *si); + +#endif diff --git a/sr_port/bit_clear.c b/sr_port/bit_clear.c new file mode 100644 index 0000000..74fd5ff --- /dev/null +++ b/sr_port/bit_clear.c @@ -0,0 +1,26 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "bit_clear.h" + +static unsigned char bit_clear_mask[8] = {127, 191, 223, 239, 247, 251, 253, 254}; + +uint4 bit_clear (uint4 bit, sm_uc_ptr_t base) +{ + uint4 retval; + sm_uc_ptr_t ptr; + + ptr = base + bit / 8; + retval = (1 << (bit & 7)) & *ptr; + *ptr &= ~(1 << (bit & 7)); + return retval != 0; +} diff --git a/sr_port/bit_clear.h b/sr_port/bit_clear.h new file mode 100644 index 0000000..0b57766 --- /dev/null +++ b/sr_port/bit_clear.h @@ -0,0 +1,20 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef BIT_CLEAR_DEFINED + +/* Declare parms for bit_clear.c */ + +uint4 bit_clear (uint4 bit, sm_uc_ptr_t base); + +#define BIT_CLEAR_DEFINED + +#endif diff --git a/sr_port/bit_set.c b/sr_port/bit_set.c new file mode 100644 index 0000000..5d02648 --- /dev/null +++ b/sr_port/bit_set.c @@ -0,0 +1,24 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "bit_set.h" + +uint4 bit_set (uint4 bit, sm_uc_ptr_t base) +{ + int4 retval; + sm_uc_ptr_t ptr; + + ptr = base + bit / 8; + retval = (1 << (bit & 7)) & *ptr; + *ptr |= 1 << (bit & 7); + return retval != 0; +} diff --git a/sr_port/bit_set.h b/sr_port/bit_set.h new file mode 100644 index 0000000..7793021 --- /dev/null +++ b/sr_port/bit_set.h @@ -0,0 +1,20 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef BIT_SET_DEFINED + +/* Declare parms for bit_set.c */ + +uint4 bit_set (uint4 bit, sm_uc_ptr_t base); + +#define BIT_SET_DEFINED + +#endif diff --git a/sr_port/bm_find_blk.c b/sr_port/bm_find_blk.c new file mode 100644 index 0000000..7e8f944 --- /dev/null +++ b/sr_port/bm_find_blk.c @@ -0,0 +1,186 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gdsbml.h" + +#define MAX_FFS_SIZE 32 + +/* Returns the location of the first set bit in the field. */ +/* The search starts at the hint and does not wrap */ + +int4 bm_find_blk(int4 hint, sm_uc_ptr_t base_addr, int4 total_bits, boolean_t *used) +{ + int4 bits; + sm_uc_ptr_t ptr, top; + unsigned char valid; + + assert(hint < total_bits); + total_bits *= BML_BITS_PER_BLK; + /* Currently bm_find_blk has been coded to assume that if "hint" is + * non-zero then it is less than total_bits. We better assert that. + */ + if (hint) + { + ptr = base_addr + (hint * BML_BITS_PER_BLK) / 8; + top = base_addr + (total_bits + 7) / 8 - 1; + if (ptr == top) + { + bits = total_bits % 8; + if (bits == 6) + valid = *ptr & 63; + else if (bits == 4) + valid = *ptr & 15; + else if (bits == 2) + valid = *ptr & 3; + else + valid = *ptr; + } + else + valid = *ptr; + switch (hint % (8 / BML_BITS_PER_BLK)) + { + case 0: break; + case 1: valid = valid & 252; + break; + case 2: valid = valid & 240; + break; + case 3: valid = valid & 192; + break; + } + if (valid) + { + if (valid & 1) + { + if (valid & 2) + *used = TRUE; + else + *used = FALSE; + return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK)); + } + else if (valid & 4) + { + if (valid & 8) + *used = TRUE; + else + *used = FALSE; + return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 1); + } + else if (valid & 16) + { + if (valid & 32) + *used = TRUE; + else + *used = FALSE; + return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 2); + } + else + { + if (valid & 128) + *used = TRUE; + else + *used = FALSE; + return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 3); + } + } + ptr++; + } + else + ptr = base_addr; + + for (top = base_addr + (total_bits +7) / 8 - 1; ptr < top; ptr++) + { + if (*ptr) + { + if (*ptr & 1) + { + if (*ptr & 2) + *used = TRUE; + else + *used = FALSE; + return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK)); + } + else if (*ptr & 4) + { + if (*ptr & 8) + *used = TRUE; + else + *used = FALSE; + return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 1); + } + else if (*ptr & 16) + { + if (*ptr & 32) + *used = TRUE; + else + *used = FALSE; + return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 2); + } + else + { if (*ptr & 128) + *used = TRUE; + else + *used = FALSE; + return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 3); + } + } + } + if ((ptr == top) && *ptr) /* Special processing for last byte--may be only partially valid , if had hint may */ + { + bits = total_bits % 8; /* have already done last byte, then ptr will be greater than top */ + if (bits == 6) + valid = *ptr & 63; + else if (bits == 4) + valid = *ptr & 15; + else if (bits == 2) + valid = *ptr & 3; + else + valid = *ptr; + if (valid) + { + if (valid & 1) + { + if (valid & 2) + *used = TRUE; + else + *used = FALSE; + return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK)); + } + else if (valid & 4) + { + if (valid & 8) + *used = TRUE; + else + *used = FALSE; + return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 1); + } + else if (valid & 16) + { + if (valid & 32) + *used = TRUE; + else + *used = FALSE; + return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 2); + } + else + { + if (valid & 128) + *used = TRUE; + else + *used = FALSE; + return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 3); + } + } + } + return -1; +} diff --git a/sr_port/bm_getfree.c b/sr_port/bm_getfree.c new file mode 100644 index 0000000..63176d9 --- /dev/null +++ b/sr_port/bm_getfree.c @@ -0,0 +1,289 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/************************************************************************************** +* +* MODULE NAME: BM_GETFREE.C +* +* CALLING SEQUENCE: block_id bm_getfree(hint, blk_used, cw_work, cs, cw_depth_ptr) +* +* DESCRIPTION: Takes a block id as a hint and tries to find a +* free block, looking first at the hint, then in the same local +* bitmap, and finally in every local map. If it finds a free block, +* it looks for the bitmap in the working set, puts it in the working +* set if it is not there, and updates the map used, and marks it with +* the transaction number, and updates the boolean +* pointed to by blk_used to indicate if the free block had been used previously. +* +* HISTORY: +* +***************************************************************************************/ +#include "mdef.h" + +#include "gtm_string.h" + +#include "cdb_sc.h" +#include "gdsroot.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsbml.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsfhead.h" +#include "min_max.h" +#include "gdsblkops.h" +#include "filestruct.h" +#include "gdscc.h" +#include "gdskill.h" /* needed for tp.h */ +#include "jnl.h" /* needed for tp.h */ +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" /* needed for ua_list for ENSURE_UPDATE_ARRAY_SPACE macro */ +#include "iosp.h" +#include "bmm_find_free.h" + +/* Include prototypes */ +#include "t_qread.h" +#include "t_write_map.h" +#include "bit_clear.h" +#include "send_msg.h" +#include "bm_getfree.h" +#include "gdsfilext.h" + +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF char *update_array, *update_array_ptr; +GBLREF gd_region *gv_cur_region; +GBLREF unsigned char rdfail_detail; +GBLREF uint4 dollar_tlevel; +GBLREF uint4 update_array_size, cumul_update_array_size; +GBLREF unsigned int t_tries; + +block_id bm_getfree(block_id orig_hint, boolean_t *blk_used, unsigned int cw_work, cw_set_element *cs, int *cw_depth_ptr) +{ + cw_set_element *cs1; + sm_uc_ptr_t bmp; + block_id bml, hint, hint_cycled, hint_limit; + block_id_ptr_t b_ptr; + int cw_set_top, depth, lcnt; + unsigned int local_maps, map_size, n_decrements = 0, total_blks; + trans_num ctn; + int4 free_bit, offset; + uint4 space_needed; + uint4 status; + srch_blk_status blkhist; + + total_blks = (dba_mm == cs_data->acc_meth) ? cs_addrs->total_blks : cs_addrs->ti->total_blks; + if (orig_hint >= total_blks) /* for TP, hint can be > total_blks */ + orig_hint = 1; + hint = orig_hint; + hint_cycled = DIVIDE_ROUND_UP(total_blks, BLKS_PER_LMAP); + hint_limit = DIVIDE_ROUND_DOWN(orig_hint, BLKS_PER_LMAP); + local_maps = hint_cycled + 2; /* for (up to) 2 wraps */ + for (lcnt = 0; lcnt <= local_maps; lcnt++) + { + bml = bmm_find_free(hint / BLKS_PER_LMAP, (sm_uc_ptr_t)MM_ADDR(cs_data), local_maps); + if ((NO_FREE_SPACE == bml) || (bml >= hint_cycled)) + { /* if no free space or might have looped to original map, extend */ + if ((NO_FREE_SPACE != bml) && (hint_limit < hint_cycled)) + { + hint_cycled = hint_limit; + hint = 1; + continue; + } + if (SS_NORMAL != (status = gdsfilext(cs_data->extension_size, total_blks))) + return (status); + if (dba_mm == cs_data->acc_meth) + return (FILE_EXTENDED); + hint = total_blks; + total_blks = cs_addrs->ti->total_blks; + hint_cycled = DIVIDE_ROUND_UP(total_blks, BLKS_PER_LMAP); + local_maps = hint_cycled + 2; /* for (up to) 2 wraps */ + /* + * note that you can make an optimization of not going back over the whole database and going over + * only the extended section. but since it is very unlikely that a free block won't be found + * in the extended section and the fact that we are starting from the extended section in either + * approach and the fact that we have a GTMASSERT to check that we don't have a lot of + * free blocks while doing an extend and the fact that it is very easy to make the change to do + * a full-pass, the full-pass solution is currently being implemented + */ + lcnt = -1; /* allow it one extra pass to ensure that it can take advantage of the entension */ + n_decrements++; /* used only for debugging purposes */ + continue; + } + bml *= BLKS_PER_LMAP; + if (ROUND_DOWN2(hint, BLKS_PER_LMAP) != bml) + { /* not within requested map */ + if ((bml < hint) && (hint_cycled)) /* wrap? - second one should force an extend for sure */ + hint_cycled = (hint_limit < hint_cycled) ? hint_limit: 0; + hint = bml + 1; /* start at beginning */ + } + if (ROUND_DOWN2(total_blks, BLKS_PER_LMAP) == bml) + map_size = (total_blks - bml); + else + map_size = BLKS_PER_LMAP; + if (dollar_tlevel) + { + depth = cw_work; + cw_set_top = *cw_depth_ptr; + if (depth < cw_set_top) + tp_get_cw(cs, cw_work, &cs1); + for (; depth < cw_set_top; depth++, cs1 = cs1->next_cw_set) + { /* do tp front to back because list is more efficient than tp_get_cw and forward pointers exist */ + if (bml == cs1->blk) + { + TRAVERSE_TO_LATEST_CSE(cs1); + break; + } + } + if (depth >= cw_set_top) + { + assert(cw_set_top == depth); + depth = 0; + } + } else + { + for (depth = *cw_depth_ptr - 1; depth >= cw_work; depth--) + { /* do non-tp back to front, because of adjacency */ + if (bml == (cs + depth)->blk) + { + cs1 = cs + depth; + break; + } + } + if (depth < cw_work) + { + assert(cw_work - 1 == depth); + depth = 0; + } + } + if (0 == depth) + { + ctn = cs_addrs->ti->curr_tn; + if (!(bmp = t_qread(bml, (sm_int_ptr_t)&blkhist.cycle, &blkhist.cr))) + return MAP_RD_FAIL; + if ((BM_SIZE(BLKS_PER_LMAP) != ((blk_hdr_ptr_t)bmp)->bsiz) || (LCL_MAP_LEVL != ((blk_hdr_ptr_t)bmp)->levl)) + { + assert(CDB_STAGNATE > t_tries); + rdfail_detail = cdb_sc_badbitmap; + return MAP_RD_FAIL; + } + offset = 0; + } else + { + bmp = cs1->old_block; + b_ptr = (block_id_ptr_t)(cs1->upd_addr); + b_ptr += cs1->reference_cnt - 1; + offset = *b_ptr + 1; + } + if (offset < map_size) + { + free_bit = bm_find_blk(offset, (sm_uc_ptr_t)bmp + SIZEOF(blk_hdr), map_size, blk_used); + if (MAP_RD_FAIL == free_bit) + return MAP_RD_FAIL; + } else + free_bit = NO_FREE_SPACE; + if (NO_FREE_SPACE != free_bit) + break; + if ((hint = bml + BLKS_PER_LMAP) >= total_blks) /* if map is full, start at 1st blk in next map */ + { /* wrap - second one should force an extend for sure */ + hint = 1; + if (hint_cycled) + hint_cycled = (hint_limit < hint_cycled) ? hint_limit: 0; + } + if ((0 == depth) && (FALSE != cs_addrs->now_crit)) /* if it's from the cw_set, its state is murky */ + bit_clear(bml / BLKS_PER_LMAP, MM_ADDR(cs_data)); /* if crit, repair master map error */ + } + /* If not in the final retry, it is possible that free_bit is >= map_size (e.g. if bitmap block gets recycled). */ + if (map_size <= (uint4)free_bit && CDB_STAGNATE <= t_tries) + { /* bad free bit */ + assert((NO_FREE_SPACE == free_bit) && (lcnt > local_maps)); /* All maps full, should have extended */ + GTMASSERT; + } + if (0 != depth) + { + b_ptr = (block_id_ptr_t)(cs1->upd_addr); + b_ptr += cs1->reference_cnt++; + *b_ptr = free_bit; + } else + { + space_needed = (BLKS_PER_LMAP + 1) * SIZEOF(block_id); + if (dollar_tlevel) + { + ENSURE_UPDATE_ARRAY_SPACE(space_needed); /* have brackets for "if" for macros */ + } + BLK_ADDR(b_ptr, space_needed, block_id); + memset(b_ptr, 0, space_needed); + *b_ptr = free_bit; + blkhist.blk_num = bml; + blkhist.buffaddr = bmp; /* cycle and cr have already been assigned from t_qread */ + t_write_map(&blkhist, (uchar_ptr_t)b_ptr, ctn, 1); /* last parameter 1 is what cs->reference_cnt gets set to */ + } + return bml + free_bit; +} + +/* This routine returns whether the free_blocks counter in the file-header is ok (TRUE) or not (FALSE). + * If not, it corrects it. This assumes cs_addrs, cs_data and gv_cur_region to point to the region of interest. + * It also assumes that the master-map is correct and finds out non-full local bitmaps and counts the number of + * free blocks in each of them and sums them up to determine the perceived correct free_blocks count. + * The reason why this is ok is that even if the master-map incorrectly reports a local bitmap as full, our new free_blocks + * count will effectively make the free space in that local-bitmap invisible and make a gdsfilext necessary and valid. + * A later mupip integ will scavenge that invisible space for us. The worst that can therefore happen is that we will transiently + * not be using up existing space. But we will always ensure that the free_blocks counter goes in sync with the master-map. + */ +boolean_t is_free_blks_ctr_ok(void) +{ + boolean_t blk_used; + block_id bml, free_bit, free_bml, maxbitsthismap; + cache_rec_ptr_t cr; + int cycle; + sm_uc_ptr_t bmp; + unsigned int local_maps, total_blks, free_blocks; + + error_def(ERR_DBBADFREEBLKCTR); + + assert(&FILE_INFO(gv_cur_region)->s_addrs == cs_addrs && cs_addrs->hdr == cs_data && cs_addrs->now_crit); + total_blks = (dba_mm == cs_data->acc_meth) ? cs_addrs->total_blks : cs_addrs->ti->total_blks; + local_maps = DIVIDE_ROUND_UP(total_blks, BLKS_PER_LMAP); + for (free_blocks = 0, free_bml = 0; free_bml < local_maps; free_bml++) + { + bml = bmm_find_free((uint4)free_bml, (sm_uc_ptr_t)MM_ADDR(cs_data), local_maps); + if (bml < free_bml) + break; + free_bml = bml; + bml *= BLKS_PER_LMAP; + if (!(bmp = t_qread(bml, (sm_int_ptr_t)&cycle, &cr)) + || (BM_SIZE(BLKS_PER_LMAP) != ((blk_hdr_ptr_t)bmp)->bsiz) + || (LCL_MAP_LEVL != ((blk_hdr_ptr_t)bmp)->levl)) + { + assert(FALSE); /* In pro, we will simply skip counting this local bitmap. */ + continue; + } + assert(free_bml <= (local_maps - 1)); + maxbitsthismap = (free_bml != (local_maps - 1)) ? BLKS_PER_LMAP : total_blks - bml; + for (free_bit = 0; free_bit < maxbitsthismap; free_bit++) + { + free_bit = bm_find_blk(free_bit, (sm_uc_ptr_t)bmp + SIZEOF(blk_hdr), maxbitsthismap, &blk_used); + assert(NO_FREE_SPACE <= free_bit); + if (0 > free_bit) + break; + free_blocks++; + } + } + assert(cs_addrs->ti->free_blocks == free_blocks); + if (cs_addrs->ti->free_blocks != free_blocks) + { + send_msg(VARLSTCNT(6) ERR_DBBADFREEBLKCTR, 4, DB_LEN_STR(gv_cur_region), cs_addrs->ti->free_blocks, free_blocks); + cs_addrs->ti->free_blocks = free_blocks; + return FALSE; + } + return TRUE; +} diff --git a/sr_port/bm_getfree.h b/sr_port/bm_getfree.h new file mode 100644 index 0000000..d3e6e34 --- /dev/null +++ b/sr_port/bm_getfree.h @@ -0,0 +1,18 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef BM_GETFREE_INCLUDED +#define BM_GETFREE_INCLUDED + +block_id bm_getfree(block_id orig_hint, boolean_t *blk_used, unsigned int cw_work, cw_set_element *cs, int *cw_depth_ptr); +boolean_t is_free_blks_ctr_ok(void); + +#endif /* BM_GETFREE_INCLUDED */ diff --git a/sr_port/bm_setmap.c b/sr_port/bm_setmap.c new file mode 100644 index 0000000..9d61b9b --- /dev/null +++ b/sr_port/bm_setmap.c @@ -0,0 +1,108 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "cdb_sc.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gdsbml.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdscc.h" +#include "filestruct.h" +#include "jnl.h" +#include "gdsblkops.h" /* for CHECK_AND_RESET_UPDATE_ARRAY macro */ + +/* Include prototypes */ +#include "t_qread.h" +#include "t_end.h" +#include "t_retry.h" +#include "t_begin_crit.h" +#include "t_write_map.h" +#include "gvcst_map_build.h" +#include "mm_read.h" + +GBLREF gd_region *gv_cur_region; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF char *update_array, *update_array_ptr; +GBLREF cw_set_element cw_set[]; +GBLREF unsigned char rdfail_detail; +GBLREF unsigned char *non_tp_jfb_buff_ptr; + +void bm_setmap(block_id bml, block_id blk, int4 busy) +{ + sm_uc_ptr_t bmp; + trans_num ctn; + srch_hist alt_hist; + srch_blk_status blkhist; /* block-history to fill in for t_write_map which uses "blk_num", "buffaddr", "cr", "cycle" */ + cw_set_element *cse; + int lbm_status; /* local bitmap status of input "blk" i.e. BUSY or FREE or RECYCLED */ + int4 reference_cnt; + uint4 bitnum; + + error_def(ERR_DSEFAIL); + + t_begin_crit(ERR_DSEFAIL); + ctn = cs_addrs->ti->curr_tn; + if (!(bmp = t_qread(bml, &blkhist.cycle, &blkhist.cr))) + t_retry((enum cdb_sc)rdfail_detail); + blkhist.blk_num = bml; + blkhist.buffaddr = bmp; + alt_hist.h[0].blk_num = 0; /* Need for calls to T_END for bitmaps */ + CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ + bitnum = blk - bml; + /* Find out current status in order to determine if there is going to be a state transition */ + assert(ROUND_DOWN2(blk, cs_data->bplmap) == bml); + GET_BM_STATUS(bmp, bitnum, lbm_status); + switch(lbm_status) + { + case BLK_BUSY: + reference_cnt = busy ? 0 : -1; + break; + case BLK_FREE: + case BLK_MAPINVALID: + case BLK_RECYCLED: + assert(BLK_MAPINVALID != lbm_status); + reference_cnt = busy ? 1 : 0; + break; + default: + assert(FALSE); + break; + } + if (reference_cnt) + { /* Initialize update array with non-zero bitnum only if reference_cnt is non-zero. */ + assert(bitnum); + *((block_id_ptr_t)update_array_ptr) = bitnum; + update_array_ptr += SIZEOF(block_id); + } + /* Terminate update array unconditionally with zero bitnum. */ + *((block_id_ptr_t)update_array_ptr) = 0; + update_array_ptr += SIZEOF(block_id); + t_write_map(&blkhist, (uchar_ptr_t)update_array, ctn, reference_cnt); + if (JNL_ENABLED(cs_data)) + { + cse = (cw_set_element *)(&cw_set[0]); + cse->new_buff = non_tp_jfb_buff_ptr; + memcpy(non_tp_jfb_buff_ptr, bmp, ((blk_hdr_ptr_t)bmp)->bsiz); + gvcst_map_build((uint4 *)cse->upd_addr, (uchar_ptr_t)cse->new_buff, cse, cs_addrs->ti->curr_tn); + cse->done = TRUE; + } + /* Call t_end till it succeeds or aborts (error will be reported) */ + while ((trans_num)0 == t_end(&alt_hist, NULL, TN_NOT_SPECIFIED)) + ; + return; +} diff --git a/sr_port/bm_update.h b/sr_port/bm_update.h new file mode 100644 index 0000000..f7ff7e9 --- /dev/null +++ b/sr_port/bm_update.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __BM_UPDATE_H__ +#define __BM_UPDATE_H__ + +void bm_update(cw_set_element *cs, sm_uc_ptr_t lclmap, boolean_t is_mm); + +#endif diff --git a/sr_port/bml_busy.c b/sr_port/bml_busy.c new file mode 100644 index 0000000..71a2217 --- /dev/null +++ b/sr_port/bml_busy.c @@ -0,0 +1,35 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsbml.h" + +/* Include prototypes */ +#include "bit_clear.h" +#include "wbox_test_init.h" + +GBLREF boolean_t dse_running; + +uint4 bml_busy(uint4 setbusy, sm_uc_ptr_t map) +{ + uint4 ret, ret1; + + setbusy *= BML_BITS_PER_BLK; + ret = bit_clear(setbusy, map); + ret1 = bit_clear(setbusy + 1, map); + /* In case of a valid snapshot, assert that only a RECYCLED or FREE block gets marked as BUSY (dse is an exception). */ + assert((ret && ret1) || (ret && !ret1) || dse_running + || (WBTEST_INVALID_SNAPSHOT_EXPECTED == gtm_white_box_test_case_number)); + return ret; +} diff --git a/sr_port/bml_find_busy.c b/sr_port/bml_find_busy.c new file mode 100644 index 0000000..c66d078 --- /dev/null +++ b/sr_port/bml_find_busy.c @@ -0,0 +1,138 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gdsbml.h" +#include "bml_find_busy.h" + +#define MAX_FFS_SIZE 32 + +/* Returns the location of the first clear bit in the field. */ +/* The search starts at the hint and wraps if necessary. */ + +int4 bml_find_busy(int4 hint, uchar_ptr_t base_addr, int4 total_blks) +{ + uchar_ptr_t ptr, top; + unsigned char valid; + int4 bits, hint_pos, total_bits, ret_val; + + hint_pos = hint * BML_BITS_PER_BLK; + total_bits = total_blks * BML_BITS_PER_BLK; + if (hint_pos >= total_bits) + hint_pos = 0; + hint_pos = hint_pos / 8; + + for (ptr = base_addr + hint_pos, top = base_addr + (total_bits + 7) / 8 - 1; ptr < top; ptr++) + { + if ((*ptr & 0x3) == 0) + { + ret_val = (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK)); + if (ret_val >= hint) + return ret_val; + } + if ((*ptr & 0xc) == 0) + { + ret_val = (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 1); + if (ret_val >= hint) + return ret_val; + } + if ((*ptr & 0x30) == 0) + { + ret_val = (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 2); + if (ret_val >= hint) + return ret_val; + } + if ((*ptr & 0xc0) == 0) + { + ret_val = (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 3); + if (ret_val >= hint) + return ret_val; + } + } + + bits = total_bits % 8; + if (bits == 6) + valid = *ptr | 0xc0; + else if (bits == 4) + valid = *ptr | 0xf0; + else if (bits == 2) + valid = *ptr | 0xfc; + else + valid = *ptr; + + if ((valid & 0x3) == 0) + { + ret_val = (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK)); + if (ret_val >= hint) + return ret_val; + } + if ((valid & 0xc) == 0) + { + ret_val = (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 1); + if (ret_val >= hint) + return ret_val; + } + if ((valid & 0x30) == 0) + { + ret_val = (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 2); + if (ret_val >= hint) + return ret_val; + } + if ((valid & 0xc0) == 0) + { + ret_val = (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 3); + if (ret_val >= hint && ret_val <= total_blks) + return ret_val; + } + + for (ptr = base_addr, top = base_addr + hint_pos; ptr < top; ptr++) + { + if ((*ptr & 0x3) == 0) + { + return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK)); + } + else if ((*ptr & 0xc) == 0) + { + return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 1); + } + else if ((*ptr & 0x30) == 0) + { + return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 2); + } + else if ((*ptr & 0xc0) == 0) + { + return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 3); + } + } + + bits = hint % 4; + if (bits == 3) + valid = *ptr | 0xc0; + else if (bits == 2) + valid = *ptr | 0xf0; + else if (bits == 1) + valid = *ptr | 0xfc; + else + valid = *ptr; + + if ((valid & 0x3) == 0) + return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK)); + if ((valid & 0xc) == 0) + return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 1); + if ((valid & 0x30) == 0) + return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 2); + if ((valid & 0xc0) == 0) + return (int4)((ptr - base_addr) * (8 / BML_BITS_PER_BLK) + 3); + + return -1; +} diff --git a/sr_port/bml_find_busy.h b/sr_port/bml_find_busy.h new file mode 100644 index 0000000..60ce519 --- /dev/null +++ b/sr_port/bml_find_busy.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __BML_FIND_BUSY_H__ +#define __BML_FIND_BUSY_H__ + +int4 bml_find_busy(int4 hint, uchar_ptr_t base_addr, int4 total_blks); + +#endif diff --git a/sr_port/bml_find_free.c b/sr_port/bml_find_free.c new file mode 100644 index 0000000..fa140cc --- /dev/null +++ b/sr_port/bml_find_free.c @@ -0,0 +1,77 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gdsbml.h" + +#define RETURN_IF_FREE(valid, ptr, base_addr) \ +{ \ + int4 bits; \ + \ + if (valid) \ + { \ + if (valid > THREE_BLKS_BITMASK) \ + bits = 3; \ + else if (valid > TWO_BLKS_BITMASK) \ + bits = 2; \ + else if (valid > ONE_BLK_BITMASK) \ + bits = 1; \ + else \ + bits = 0; \ + return (int4)((ptr - base_addr) * (BITS_PER_UCHAR / BML_BITS_PER_BLK) + bits); \ + } \ +} + +/* Returns the location of the first set bit in the field. The search starts at the hint and wraps if necessary. + * If a non-null update array is passed, that is also taken into account while figuring out if any free block is available. + */ +int4 bml_find_free(int4 hint, uchar_ptr_t base_addr, int4 total_bits) +{ + uchar_ptr_t ptr, top; + unsigned char valid; + int4 bits; + + hint *= BML_BITS_PER_BLK; + hint = hint / BITS_PER_UCHAR; + total_bits *= BML_BITS_PER_BLK; + + if (hint > total_bits) + hint = 0; + for (ptr = base_addr + hint, top = base_addr + DIVIDE_ROUND_UP(total_bits, BITS_PER_UCHAR) - 1; ptr < top; ptr++) + { + valid = *ptr; + RETURN_IF_FREE(valid, ptr, base_addr); + } + if (*ptr) /* Special processing for last byte as may be only partially valid */ + { + bits = total_bits % BITS_PER_UCHAR; + if (bits == 6) + valid = *ptr & THREE_BLKS_BITMASK; + else if (bits == 4) + valid = *ptr & TWO_BLKS_BITMASK; + else if (bits == 2) + valid = *ptr & ONE_BLK_BITMASK; + else + valid = *ptr; + RETURN_IF_FREE(valid, ptr, base_addr); + } + if (hint) + { + for (ptr = base_addr, top = base_addr + hint; ptr < top; ptr++) + { + valid = *ptr; + RETURN_IF_FREE(valid, ptr, base_addr); + } + } + return NO_FREE_SPACE; +} diff --git a/sr_port/bml_free.c b/sr_port/bml_free.c new file mode 100644 index 0000000..14ee8ea --- /dev/null +++ b/sr_port/bml_free.c @@ -0,0 +1,34 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsbml.h" + +/* Include prototypes */ +#include "bit_set.h" +#include "bit_clear.h" + +GBLREF boolean_t dse_running; + +uint4 bml_free(uint4 setfree, sm_uc_ptr_t map) +{ + uint4 ret, ret1; + + setfree *= BML_BITS_PER_BLK; + ret = bit_set(setfree, map); + ret1 = bit_clear(setfree + 1, map); + /* Assert that only a BUSY or RECYCLED block gets marked as FREE (dse is an exception) */ + assert((!ret && !ret1) || (ret && ret1) || dse_running); + return ret; +} diff --git a/sr_port/bml_init.c b/sr_port/bml_init.c new file mode 100644 index 0000000..6e61c12 --- /dev/null +++ b/sr_port/bml_init.c @@ -0,0 +1,70 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#ifdef UNIX +#include /* for DSK_WRITE macro */ +#elif defined(VMS) +#include "efn.h" /* for DSK_WRITE macro */ +#else +#error UNSUPPORTED PLATFORM +#endif + +#include "gdsroot.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsbml.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "jnl.h" + +GBLREF gd_region *gv_cur_region; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF jnl_gbls_t jgbl; + +int4 bml_init(block_id bml) +{ + blk_hdr_ptr_t ptr; + uint4 size; + uint4 status; + trans_num blktn; + + size = BM_SIZE(cs_data->bplmap); + /* Allocate full block .. bml_newmap will set the write size, dsk_write will write part or all of it as appropriate. */ + ptr = (blk_hdr_ptr_t)malloc(cs_addrs->hdr->blk_size); + /* We are about to create a local bitmap. Setting its block transaction number to the current database transaction + * number gives us a clear history of when this bitmap got created. There are two exceptions. + * 1) If before-image journaling, it implies the possibility of using backward recovery/rollback both of which can + * take the database to a transaction number MUCH BEFORE the current database transaction number. In that case, the + * recovered database will have DBTNTOOLG integrity errors since the bitmap block's transaction number will be greater + * than the post-recovery database transaction number. Since we have no control over what transaction number backward + * recovery can take the database to, we set the bitmap block transaction number to 0 (the least possible) for the + * before-image journaling case. + * 2) If in forward recovery, then the database current transaction number is not incremented in gdsfilext (the caller + * of this function) so we have to create the local bitmap blocks with curr_tn-1 in order to avoid a DBTNTOOLG error. + */ + if (JNL_ENABLED(cs_data) && cs_addrs->jnl && cs_addrs->jnl->jnl_buff && cs_addrs->jnl->jnl_buff->before_images) + blktn = 0; + else if (jgbl.forw_phase_recovery && !JNL_ENABLED(cs_data)) /* forward recovery */ + blktn = cs_data->trans_hist.curr_tn - 1; + else + blktn = cs_data->trans_hist.curr_tn; + + bml_newmap(ptr, size, blktn); + /* status holds the status of any error return from dsk_write */ + DSK_WRITE_NOCACHE(gv_cur_region, bml, (sm_uc_ptr_t)ptr, cs_data->desired_db_format, status); + free(ptr); + return status; +} diff --git a/sr_port/bml_newmap.c b/sr_port/bml_newmap.c new file mode 100644 index 0000000..1c40301 --- /dev/null +++ b/sr_port/bml_newmap.c @@ -0,0 +1,35 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsbml.h" +#include "gdsdbver.h" + +void bml_newmap(blk_hdr_ptr_t ptr, uint4 size, trans_num curr_tn) +{ + sm_uc_ptr_t bptr; + + /* --- similar logic exists in mupip_restore.c, which need to pick up any new updates here --- */ + ptr->bver = GDSVCURR; + ptr->bsiz = size; + ptr->levl = LCL_MAP_LEVL; + ptr->tn = curr_tn; + bptr = (sm_uc_ptr_t)ptr + SIZEOF(blk_hdr); + size -= SIZEOF(blk_hdr); + *bptr++ = THREE_BLKS_FREE; + memset(bptr, FOUR_BLKS_FREE, size - 1); +} diff --git a/sr_port/bml_recycled.c b/sr_port/bml_recycled.c new file mode 100644 index 0000000..8b489ce --- /dev/null +++ b/sr_port/bml_recycled.c @@ -0,0 +1,32 @@ +/**************************************************************** + * * + * Copyright 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsbml.h" + +/* Include prototypes */ +#include "bit_set.h" + +GBLREF boolean_t dse_running; + +uint4 bml_recycled(uint4 setfree, sm_uc_ptr_t map) +{ + uint4 ret, ret1; + + setfree *= BML_BITS_PER_BLK; + ret = bit_set(setfree, map); + ret1 = bit_set(setfree + 1, map); + assert((!ret && !ret1) || dse_running); + return ret; +} diff --git a/sr_port/bml_status_check.c b/sr_port/bml_status_check.c new file mode 100644 index 0000000..ef2e511 --- /dev/null +++ b/sr_port/bml_status_check.c @@ -0,0 +1,78 @@ +/**************************************************************** + * * + * Copyright 2007, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gdsroot.h" +#include "gdskill.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gdsblk.h" +#include "gdsbml.h" +#include "gdscc.h" +#include "mm_read.h" +#include "bml_status_check.h" + +#define MAX_FFS_SIZE 32 + +GBLREF sgmnt_addrs *cs_addrs; + +/* Checks that a block that we have acquired is marked RECYCLED/FREE in the database + * and that an existing block that we are updating is marked BUSY in the database. + * For BG, the check is done only if the bitmap block is available in the cache AND is not being concurrently updated. + */ +void bml_status_check(cw_set_element *cs) +{ + block_id bmlblk, blk; + cache_rec_ptr_t bmlcr; + blk_hdr_ptr_t bmlbuff; + boolean_t is_mm; + int4 bml_status; + + is_mm = (dba_mm == cs_addrs->hdr->acc_meth); + assert(gds_t_create != cs->mode); + if ((gds_t_acquired == cs->mode) || (gds_t_write == cs->mode)) + { + bmlblk = ROUND_DOWN2(cs->blk, BLKS_PER_LMAP); + assert(IS_BITMAP_BLK(bmlblk)); + blk = cs->blk - bmlblk; + assert(blk); + if (!is_mm) + { + bmlcr = db_csh_get(bmlblk); + bmlbuff = ((NULL != bmlcr) && (CR_NOTVALID != (sm_long_t)bmlcr) + && (0 > bmlcr->read_in_progress) && !bmlcr->in_tend) + ? (blk_hdr_ptr_t)GDS_REL2ABS(bmlcr->buffaddr) + : NULL; + } else + { + bmlbuff = (blk_hdr_ptr_t)mm_read(bmlblk); + /* mm_read would have incremented the GVSTATS n_dsk_read counter. But we dont want that to happen + * because this function is invoked only in debug builds and yet we want the same counter + * value for both pro and dbg builds. So undo that action immediately. + */ + INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_dsk_read, (gtm_uint64_t)-1);/* note the -1 causes the undo */ + assert(NULL != bmlbuff); + } + if (NULL != bmlbuff) + { + assert(LCL_MAP_LEVL == bmlbuff->levl); + assert(BM_SIZE(cs_addrs->hdr->bplmap) == bmlbuff->bsiz); + GET_BM_STATUS(bmlbuff, blk, bml_status); + assert(BLK_MAPINVALID != bml_status); + assert((gds_t_acquired != cs->mode) || (BLK_BUSY != bml_status)); + assert((gds_t_acquired == cs->mode) || (BLK_BUSY == bml_status)); + } + } +} diff --git a/sr_port/bml_status_check.h b/sr_port/bml_status_check.h new file mode 100644 index 0000000..606da56 --- /dev/null +++ b/sr_port/bml_status_check.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __BML_STATUS_CHECK_H__ +#define __BML_STATUS_CHECK_H__ + +void bml_status_check(cw_set_element *cs); + +#endif diff --git a/sr_port/bmm_find_free.c b/sr_port/bmm_find_free.c new file mode 100644 index 0000000..c79e537 --- /dev/null +++ b/sr_port/bmm_find_free.c @@ -0,0 +1,44 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gdsbml.h" +#include "min_max.h" +#include "gtm_ffs.h" +#include "bmm_find_free.h" + +#define MAX_FFS_SIZE 32 + +/* Returns the location of the first set bit in the field. */ +/* The search starts at the hint and wraps if necessary. */ + +int4 bmm_find_free(uint4 hint, uchar_ptr_t base_addr, uint4 total_bits) +{ + int4 answer, width; + uint4 start, top; + + if (hint >= total_bits) + hint = 0; + for (start = hint, top = total_bits; top; start = 0, top = hint, hint = 0) + { /* one or two passes through outer loop; second is a wrap to the beginning */ + for (width = MIN(top, ROUND_DOWN2(start, MAX_FFS_SIZE) + MAX_FFS_SIZE) - start; width > 0; + start += width, width = MIN(top - start, MAX_FFS_SIZE)) + { + answer = gtm_ffs(start, base_addr, width); + if (NO_FREE_SPACE != answer) + return answer; + } + } + assert(NO_FREE_SPACE == answer); + return NO_FREE_SPACE; +} diff --git a/sr_port/bmm_find_free.h b/sr_port/bmm_find_free.h new file mode 100644 index 0000000..fe8a454 --- /dev/null +++ b/sr_port/bmm_find_free.h @@ -0,0 +1,16 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#ifndef __BMM_FIND_FREE_H__ +#define __BMM_FIND_FREE_H__ + +int4 bmm_find_free(uint4 hint, uchar_ptr_t base_addr, uint4 total_bits); + +#endif diff --git a/sr_port/bmm_init.c b/sr_port/bmm_init.c new file mode 100644 index 0000000..7b21173 --- /dev/null +++ b/sr_port/bmm_init.c @@ -0,0 +1,31 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gdsbt.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsfhead.h" +#include "gdsbml.h" + + +GBLREF sgmnt_data *cs_data; + +void bmm_init(void) +{ + assert(cs_data && cs_data->master_map_len); + memset(MM_ADDR(cs_data), BMP_EIGHT_BLKS_FREE, MASTER_MAP_SIZE(cs_data)); + return; +} diff --git a/sr_port/bool_expr.c b/sr_port/bool_expr.c new file mode 100644 index 0000000..f59d22f --- /dev/null +++ b/sr_port/bool_expr.c @@ -0,0 +1,35 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" + +int bool_expr(bool op,oprtype *addr) +{ + oprtype x; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + if (!(TREF(expr_depth))++) + TREF(expr_start) = TREF(expr_start_orig) = NULL; + if (!eval_expr(&x)) + { + TREF(expr_depth) = 0; + return FALSE; + } + coerce(&x, OCT_BOOL); + if (!(--(TREF(expr_depth)))) + TREF(shift_side_effects) = FALSE; + assert(x.oprclass == TRIP_REF); + bx_tail(x.oprval.tref, op, addr); + return TRUE; +} diff --git a/sr_port/break.h b/sr_port/break.h new file mode 100644 index 0000000..e5e1bd6 --- /dev/null +++ b/sr_port/break.h @@ -0,0 +1,16 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#define BREAK_MASK (1 << 0) +#define ZBREAK_MASK (1 << 1) +#define DEVBREAK_MASK (1 << 2) +#define ZSTBREAK_MASK (1 << 3) +#define TRIGGER_ZBREAK_REMOVED_MASK (1 << 4) diff --git a/sr_port/bt_get.c b/sr_port/bt_get.c new file mode 100644 index 0000000..f956870 --- /dev/null +++ b/sr_port/bt_get.c @@ -0,0 +1,46 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* This function returns a pointer to the bt_rec entry or 0 if not found. */ + +#include "mdef.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsbgtr.h" /* for the BG_TRACE_PRO macros */ + +GBLREF sgmnt_addrs *cs_addrs; + +bt_rec_ptr_t bt_get(int4 block) /* block = block # to get */ +{ + register sgmnt_addrs *csa; + bt_rec_ptr_t bt; + int lcnt; + + csa = cs_addrs; + assert(csa->now_crit); + bt = csa->bt_header + (block % csa->hdr->bt_buckets); + assert(bt->blk == BT_QUEHEAD); + for (lcnt = csa->hdr->n_bts; lcnt > 0; lcnt--) + { + bt = (bt_rec_ptr_t)((sm_uc_ptr_t) bt + bt->blkque.fl); + if (bt->blk == block) + return bt; + if (bt->blk == BT_QUEHEAD) + return NULL; + } + SET_TRACEABLE_VAR(csa->hdr->wc_blocked, TRUE); + BG_TRACE_PRO_ANY(csa, wc_blocked_bt_get); + return NULL; /* actually should return BT_INVALID or some such value but callers check only for NULL */ +} diff --git a/sr_port/bt_init.c b/sr_port/bt_init.c new file mode 100644 index 0000000..d86ea85 --- /dev/null +++ b/sr_port/bt_init.c @@ -0,0 +1,29 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" + +void bt_init(sgmnt_addrs *cs) +{ + sgmnt_data_ptr_t base; + + base = cs->hdr; + cs->ti = &base->trans_hist; + cs->bt_header = (bt_rec_ptr_t)((sm_uc_ptr_t) base + cs->nl->bt_header_off); + cs->bt_base = (bt_rec_ptr_t)((sm_uc_ptr_t) base + cs->nl->bt_base_off); + cs->th_base = (th_rec_ptr_t)((sm_uc_ptr_t) base + cs->nl->th_base_off); + return; +} diff --git a/sr_port/bt_malloc.c b/sr_port/bt_malloc.c new file mode 100644 index 0000000..5fa13cd --- /dev/null +++ b/sr_port/bt_malloc.c @@ -0,0 +1,38 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" + +void bt_malloc(sgmnt_addrs *csa) +{ + unsigned int n; + sgmnt_data_ptr_t csd; + + csd = csa->hdr; + /* check that the file header is quad word aligned */ + if ((-(SIZEOF(uint4) * 2) & (sm_long_t)csd) != (sm_long_t)csd) + GTMASSERT; + if ((-(SIZEOF(uint4) * 2) & SIZEOF_FILE_HDR(csd)) != SIZEOF_FILE_HDR(csd)) + GTMASSERT; + csa->nl->bt_header_off = (n = (uint4)(SIZEOF_FILE_HDR(csd))); + csa->nl->th_base_off = (n += csd->bt_buckets * SIZEOF(bt_rec)); /* hash table */ + csa->nl->th_base_off += SIZEOF(que_ent); /* tnque comes after fl and bl of blkque */ + csa->nl->bt_base_off = (n += SIZEOF(bt_rec)); /* th_queue anchor referenced above */ + assert((n += (csd->n_bts * SIZEOF(bt_rec))) == (SIZEOF_FILE_HDR(csd)) + (BT_SIZE(csd))); /* DON'T use n after this */ + bt_init(csa); + bt_refresh(csa); + return; +} diff --git a/sr_port/bt_put.c b/sr_port/bt_put.c new file mode 100644 index 0000000..235f7a8 --- /dev/null +++ b/sr_port/bt_put.c @@ -0,0 +1,129 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gdsroot.h" +#include "gdskill.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdscc.h" +#include "filestruct.h" +#include "gdsbgtr.h" +#include "send_msg.h" +#include "relqop.h" +#include "wcs_recover.h" +#include "wcs_get_space.h" +#include "jnl.h" +#include "wbox_test_init.h" + +#ifdef DEBUG +static int4 entry_count = 0; +#endif + +GBLREF volatile boolean_t in_wcs_recover; /* TRUE if in "wcs_recover" */ +GBLREF uint4 process_id; +GBLREF jnl_gbls_t jgbl; + +bt_rec_ptr_t bt_put(gd_region *reg, int4 block) +{ + bt_rec_ptr_t bt, q0, q1, hdr; + sgmnt_addrs *csa; + sgmnt_data_ptr_t csd; + cache_rec_ptr_t cr; + th_rec_ptr_t th; + trans_num lcl_tn; + uint4 lcnt; + + error_def(ERR_BTFAIL); + error_def(ERR_WCFAIL); + error_def(ERR_WCBLOCKED); + + csa = (sgmnt_addrs *)&FILE_INFO(reg)->s_addrs; + csd = csa->hdr; + assert(csa->now_crit || csd->clustered); + assert(dba_mm != csa->hdr->acc_meth); + lcl_tn = csa->ti->curr_tn; + hdr = csa->bt_header + (block % csd->bt_buckets); + assert(BT_QUEHEAD == hdr->blk); + for (lcnt = 0, bt = (bt_rec_ptr_t)((sm_uc_ptr_t)hdr + hdr->blkque.fl); ; + bt = (bt_rec_ptr_t)((sm_uc_ptr_t)bt + bt->blkque.fl), lcnt++) + { + if (BT_QUEHEAD == bt->blk) + { /* there is no matching bt */ + assert(bt == hdr); + bt = (bt_rec_ptr_t)((sm_uc_ptr_t)(csa->th_base) + csa->th_base->tnque.fl - SIZEOF(th->tnque)); + if (CR_NOTVALID != bt->cache_index) + { /* the oldest bt is still valid */ + assert(!in_wcs_recover); + cr = (cache_rec_ptr_t)GDS_ANY_REL2ABS(csa, bt->cache_index); + if (cr->dirty) + { /* get it written so it can be reused */ + BG_TRACE_PRO_ANY(csa, bt_put_flush_dirty); + if (FALSE == wcs_get_space(reg, 0, cr)) + { + assert(csd->wc_blocked); /* only reason we currently know + * why wcs_get_space could fail */ + assert(gtm_white_box_test_case_enabled); + BG_TRACE_PRO_ANY(csa, wcb_bt_put); + send_msg(VARLSTCNT(8) ERR_WCBLOCKED, 6, LEN_AND_LIT("wcb_bt_put"), + process_id, &lcl_tn, DB_LEN_STR(reg)); + return NULL; + } + } + bt->cache_index = CR_NOTVALID; + cr->bt_index = 0; + } + q0 = (bt_rec_ptr_t)((sm_uc_ptr_t)bt + bt->blkque.fl); + q1 = (bt_rec_ptr_t)remqt((que_ent_ptr_t)q0); + if (EMPTY_QUEUE == (sm_long_t)q1) + rts_error(VARLSTCNT(3) ERR_BTFAIL, 1, 1); + bt->blk = block; + bt->killtn = lcl_tn; + insqt((que_ent_ptr_t)bt, (que_ent_ptr_t)hdr); + th = (th_rec_ptr_t)remqh((que_ent_ptr_t)csa->th_base); + if (EMPTY_QUEUE == (sm_long_t)th) + GTMASSERT; + break; + } + if (bt->blk == block) + { /* bt_put should never be called twice for the same block with the same lcl_tn. This is because + * t_end/tp_tend update every block only once as part of each update transaction. Assert this. + * The two exceptions are + * a) Forward journal recovery which simulates a 2-phase M-kill where the same block + * could get updated in both phases (example bitmap block gets updated for blocks created + * within the TP transaction as well as for blocks that are freed up in the 2nd phase of + * the M-kill) with the same transaction number. This is because although GT.M would have + * updated the same block with different transaction numbers in the two phases, forward + * recovery will update it with the same tn and instead increment the db tn on seeing the + * following INCTN journal record(s). + * b) Cache recovery (wcs_recover). It could call bt_put more than once for the same block + * and potentially with the same tn. This is because the state of the queues is questionable + * and there could be more than one cache record for a given block number. + */ + assert(in_wcs_recover || (bt->tn < lcl_tn) || (jgbl.forw_phase_recovery && !JNL_ENABLED(csa))); + q0 = (bt_rec_ptr_t)((sm_uc_ptr_t)bt + bt->tnque.fl); + th = (th_rec_ptr_t)remqt((que_ent_ptr_t)((sm_uc_ptr_t)q0 + SIZEOF(th->tnque))); + if (EMPTY_QUEUE == (sm_long_t)th) + GTMASSERT; + break; + } + if (0 == bt->blkque.fl) + rts_error(VARLSTCNT(3) ERR_BTFAIL, 1, 2); + if (lcnt >= csd->n_bts) + rts_error(VARLSTCNT(3) ERR_BTFAIL, 1, 3); + } + insqt((que_ent_ptr_t)th, (que_ent_ptr_t)csa->th_base); + bt->tn = lcl_tn; + return bt; +} diff --git a/sr_port/bt_refresh.c b/sr_port/bt_refresh.c new file mode 100644 index 0000000..e1566b0 --- /dev/null +++ b/sr_port/bt_refresh.c @@ -0,0 +1,45 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "longset.h" +#include "relqop.h" + +void bt_refresh(sgmnt_addrs *csa) +{ + sgmnt_data_ptr_t csd; + bt_rec_ptr_t ptr, top, bt1; + + error_def(ERR_BTFAIL); + + csd = csa->hdr; + assert(dba_bg == csd->acc_meth); + longset((uchar_ptr_t)csa->bt_header, (csd->bt_buckets + csd->n_bts + 1) * SIZEOF(bt_rec), 0); + + for (ptr = csa->bt_header, top = ptr + csd->bt_buckets + 1; ptr < top; ptr++) + ptr->blk = BT_QUEHEAD; + + for (ptr = csa->bt_base, bt1 = csa->bt_header, top = ptr + csd->n_bts; ptr < top ; ptr++, bt1++) + { + ptr->blk = BT_NOTVALID; + ptr->cache_index = CR_NOTVALID; + insqt((que_ent_ptr_t)ptr, (que_ent_ptr_t)bt1); + insqt((que_ent_ptr_t)((sm_uc_ptr_t)ptr + (2 * SIZEOF(sm_off_t))), (que_ent_ptr_t)csa->th_base); + } + ((th_rec *)((uchar_ptr_t)csa->th_base + csa->th_base->tnque.fl))->tn = csa->ti->curr_tn - 1; + csa->ti->mm_tn = 0; + return; +} diff --git a/sr_port/buddy_list.c b/sr_port/buddy_list.c new file mode 100644 index 0000000..d2ca856 --- /dev/null +++ b/sr_port/buddy_list.c @@ -0,0 +1,204 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "buddy_list.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "relqop.h" + +#define MAX_MEM_SIZE_IN_BITS 63 /* the unreachable maximum size of memory (in bits) */ + +void initialize_list(buddy_list *list, int4 elemSize, int4 initAlloc) +{ + int4 i, maxPtrs; + int4 temp; + + if (!initAlloc) + initAlloc = 1; + + assert(0 < elemSize); + assert(0 < initAlloc); + + elemSize = ROUND_UP2(elemSize, 8) ; /* make it 8 byte aligned */ + temp = initAlloc; + while (temp & (temp - 1)) /* floor it to a perfect power of 2 */ + temp = temp & (temp - 1); + if (initAlloc != temp) + initAlloc = temp << 1; + for (i = 0; initAlloc >> i; i++); + + list->initAllocBits = i - 1; + list->elemSize = elemSize; + list->initAlloc = initAlloc; + list->cumulMaxElems = initAlloc; + list->nElems = 0; + /* Note: size_t typecast done below to enable 64-bit arithmetic so sizes > 4GB can be allocated */ + list->ptrArray = (char **)malloc((size_t)SIZEOF(char *) * (MAX_MEM_SIZE_IN_BITS + 2)); + /* +2 = +1 for holding the NULL pointer and +1 for ptrArray[0] */ + memset(list->ptrArray, 0, SIZEOF(char *) * (MAX_MEM_SIZE_IN_BITS + 2)); + list->ptrArrayCurr = list->ptrArray; + /* Note: size_t typecast done below to enable 64-bit arithmetic so sizes > 4GB can be allocated */ + list->nextFreePtr = list->ptrArray[0] = (char *)malloc((size_t)initAlloc * elemSize); + list->free_que = NULL; /* initialize the list to have no free element queue */ + DEBUG_ONLY(list->used_free_last_n_elements = FALSE;) + DEBUG_ONLY(list->used_free_element = FALSE;) +} + +/* Any changes to this routine need corresponding changes to the VERIFY_LIST_IS_REINITIALIZED macro (defined in buddy_list.h) */ +void reinitialize_list(buddy_list *list) +{ + assert(list); + list->nElems = 0; + list->cumulMaxElems = list->initAlloc; + list->ptrArrayCurr = list->ptrArray; + list->nextFreePtr = list->ptrArray[0]; + list->free_que = NULL; /* reset the list to have no free element queue */ + DEBUG_ONLY(list->used_free_last_n_elements = FALSE;) + DEBUG_ONLY(list->used_free_element = FALSE;) +} + +boolean_t free_last_n_elements(buddy_list *list, int4 num) +{ + int4 rowElemsMax, rowElemsLeft, numLeft, nElems; + char **ptrArrayCurr; + + assert(list); + assert(!list->used_free_element); + DEBUG_ONLY(list->used_free_last_n_elements = TRUE;) + nElems = list->nElems; + if (nElems >= num) + { + ptrArrayCurr = list->ptrArrayCurr; + numLeft = num; + rowElemsLeft = (int4)(list->nextFreePtr - ptrArrayCurr[0]) / list->elemSize; + rowElemsMax = nElems - rowElemsLeft; + while (numLeft >= rowElemsLeft && ptrArrayCurr != list->ptrArray) + { + assert(0 == rowElemsMax % list->initAlloc); + numLeft -= rowElemsLeft; + list->cumulMaxElems -= rowElemsMax; + if (rowElemsMax != list->initAlloc) + rowElemsMax = rowElemsMax >> 1; + rowElemsLeft = rowElemsMax; + ptrArrayCurr--; + assert(rowElemsMax >= list->initAlloc); + } + list->nElems = nElems - num; + list->ptrArrayCurr = ptrArrayCurr; + list->nextFreePtr = ptrArrayCurr[0] + list->elemSize * (rowElemsLeft - numLeft); + assert(list->ptrArrayCurr >= list->ptrArray); + return TRUE; + } else + return FALSE; +} + +char *get_new_element(buddy_list *list, int4 nElements) +{ + char *retPtr; + char **ptrArrayCurr; + int4 cumulMaxElems, nElems, elemSize; + + if (0 >= nElements) + { + assert(FALSE); + return NULL; + } + nElems = list->nElems; + cumulMaxElems = list->cumulMaxElems; + elemSize = list->elemSize; + if (nElems + nElements <= cumulMaxElems) + { + retPtr = list->nextFreePtr; + list->nextFreePtr += nElements * elemSize; + list->nElems = nElems + nElements; + } else + { + do + { + ptrArrayCurr = ++list->ptrArrayCurr; + /* Note: size_t typecast done below to enable 64-bit arithmetic so sizes > 4GB can be allocated */ + if (!(retPtr = *ptrArrayCurr)) + retPtr = *ptrArrayCurr = (char *)malloc((size_t)cumulMaxElems * elemSize); + nElems = cumulMaxElems; + cumulMaxElems *= 2; + } while (nElems + nElements > cumulMaxElems); + list->nElems = nElems + nElements; + list->nextFreePtr = retPtr + elemSize * nElements; + list->cumulMaxElems = cumulMaxElems; + } + return retPtr; +} + +char *get_new_free_element(buddy_list *list) +{ + char *elem; + + assert(!list->used_free_last_n_elements); + DEBUG_ONLY(list->used_free_element = TRUE;) + /* Assert that each element has enough space to store a pointer. This will be used to maintain the singly linked list + * of freed up elements in the buddy list. The head of this list will be list->free_que. + */ + assert(SIZEOF(char *) <= list->elemSize); + elem = list->free_que; + if (NULL != elem) + { + list->free_que = *(char **)elem; + assert(elem != list->free_que); + return elem; + } + return get_new_element(list, 1); +} + +void free_element(buddy_list *list, char *elem) +{ + assert(!list->used_free_last_n_elements); + DEBUG_ONLY(list->used_free_element = TRUE;) + assert(elem); + assert(elem != list->free_que); + /* Add it to the singly linked list of freed up elements in the buddy_list */ + *(char **)elem = list->free_que; + list->free_que = elem; +} + +char *find_element(buddy_list *list, int4 index) +{ + char **ptrArrayCurr; + int4 i, initAllocBits; + + if (index > list->nElems) + return NULL; + initAllocBits = list->initAllocBits; + for (i = initAllocBits; index >> i; i++); + i = i - initAllocBits; + return list->ptrArray[i] + list->elemSize * (index - list->initAlloc * (i ? 1 << (i-1) : 0)); +} + +void cleanup_list(buddy_list *list) +{ + char **curr; + + assert(list); + if (!list || !(curr = list->ptrArray)) + return; + while(*curr) + { + free(*curr); + curr++; + } + free(list->ptrArray); +} diff --git a/sr_port/buddy_list.h b/sr_port/buddy_list.h new file mode 100644 index 0000000..fa5b374 --- /dev/null +++ b/sr_port/buddy_list.h @@ -0,0 +1,98 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __BUDDY_LIST_H__ +#define __BUDDY_LIST_H__ + +/* This is a general purpose structure used for storing a list of similar-sized structure-elements with the need for + * efficiently accessing the element once we know its index. + * It maintains an array of pointers "ptrArray". + * The first and second pointers i.e. ptrArray[0] and ptrArray[1] point to an array containing "initAlloc" elements. + * Every other pointer ptrArray[i] points to an array containing double-the-number-of-elements + * as the array pointed to by ptrArray[i-1]. + * This also provides for iterating through the list of elements. + */ + +typedef struct buddy_list_struct +{ + char **ptrArray; /* the array of pointers */ + int4 elemSize; /* the size of each structure-element */ + int4 initAlloc; /* the number of structures in the ptrArray[0] and ptrArray[1] */ + int4 initAllocBits; /* number of bits corresponding to initAlloc */ + int4 nElems; /* the current number of elements used up in the total array */ + int4 cumulMaxElems; /* the maximum number of elements that can be held from the first upto the current array */ + char **ptrArrayCurr; /* = &ptrArray[i] where i is the current array index in use for allocation */ + char *nextFreePtr; /* pointer to the next free element in the current array. Initially = ptrArray[0] */ + char *free_que; /* pointer to the singly linked list of freed-up elements */ +# ifdef DEBUG + /* The following two debug-only variables are present to avoid mixing of usage of the functions "free_last_n_elements" + * and "get_new_free_element"/"free_element()" in the same buddy list. This is because they both use completely + * different schemes to free up elements (one reduces the # of elements in the buddy list whereas the other does not + * touch the # of elements but maintains an internal singly linked list of freed up elements) and they cannot coexist. + */ + boolean_t used_free_last_n_elements; /* TRUE if "free_last_n_elements" was called in this buddy_list */ + boolean_t used_free_element; /* TRUE if "get_new_free_element" or "free_element" was called */ +# endif +} buddy_list; + +void initialize_list(buddy_list *list, int4 elemSize, int4 initAlloc); +char *get_new_element(buddy_list *list, int4 nElements); +char *find_element(buddy_list *list, int4 index); +void cleanup_list(buddy_list *list); +void reinitialize_list(buddy_list *list); /* used for reusing already allocated storage */ +boolean_t free_last_n_elements(buddy_list *list, int4 num); /* to free up the last contiguous "num" elements */ +void free_element(buddy_list *list, char *elem); /* to free up an element and reuse it later */ +char *get_new_free_element(buddy_list *list); /* gets a freed-up element if available otherwise gets a new one */ + +#define CAREFUL_FREEUP_BUDDY_LIST(list) \ +{ \ + if (NULL != list) \ + FREEUP_BUDDY_LIST(list); \ +} + +#define FREEUP_BUDDY_LIST(list) \ +{ \ + assert(list); \ + if (NULL != list) \ + { \ + cleanup_list(list); \ + free(list); \ + } \ +} + +#define VERIFY_LIST_IS_REINITIALIZED(list) \ +{ /* The following code verifies the same fields initialized by the \ + * function "reinitialize_list". Any changes to one should be reflected \ + * in the other. \ + */ \ + assert((0 == list->nElems) || process_exiting); \ + assert((list->cumulMaxElems == list->initAlloc) || process_exiting); \ + assert((list->ptrArrayCurr == list->ptrArray) || process_exiting); \ + assert((list->nextFreePtr == list->ptrArray[0])|| process_exiting); \ + assert((NULL == list->free_que) || process_exiting); \ +} + +#define REINITIALIZE_LIST(LST) \ +{ \ + buddy_list *lcllist; \ + \ + lcllist = LST; \ + assert((NULL == lcllist->free_que) || lcllist->nElems); \ + if (lcllist->nElems) \ + reinitialize_list(lcllist); \ + else \ + { /* No need to reinitialize. Verify \ + * that list is already initialized */ \ + VERIFY_LIST_IS_REINITIALIZED(lcllist); \ + } \ +} + +#endif diff --git a/sr_port/bx_boolop.c b/sr_port/bx_boolop.c new file mode 100644 index 0000000..02204db --- /dev/null +++ b/sr_port/bx_boolop.c @@ -0,0 +1,35 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "mmemory.h" + +void bx_boolop(triple *t, bool jmp_type_one, bool jmp_to_next, bool sense, oprtype *addr) +{ + oprtype *p; + + if (jmp_to_next) + { + p = (oprtype *) mcalloc(SIZEOF(oprtype)); + *p = put_tjmp(t); + } + else + { + p = addr; + } + bx_tail(t->operand[0].oprval.tref, jmp_type_one, p); + bx_tail(t->operand[1].oprval.tref, sense, addr); + t->opcode = OC_NOOP; + t->operand[0].oprclass = t->operand[1].oprclass = 0; + return; +} diff --git a/sr_port/bx_relop.c b/sr_port/bx_relop.c new file mode 100644 index 0000000..260eab3 --- /dev/null +++ b/sr_port/bx_relop.c @@ -0,0 +1,25 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "mdq.h" + +void bx_relop(triple *t, opctype cmp, opctype tst, oprtype *addr) +{ + triple *ref; + + ref = maketriple(tst); + ref->operand[0] = put_indr(addr); + dqins(t, exorder, ref); + t->opcode = cmp; + return; +} diff --git a/sr_port/bx_tail.c b/sr_port/bx_tail.c new file mode 100644 index 0000000..db0de83 --- /dev/null +++ b/sr_port/bx_tail.c @@ -0,0 +1,130 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "mdq.h" +#include "mmemory.h" + +LITREF octabstruct oc_tab[]; + +/* structure of jmps is as follows: + + sense OC_AND OC_OR + + TRUE op1 op1 + jmpf next jmpt addr + op2 op2 + jmpt addr jmpt addr + + FALSE op1 op1 + jmpf addr jmpt next + op2 op2 + jmpf addr jmpf addr +*/ + +void bx_tail(triple *t, bool sense, oprtype *addr) +/* +triple *t; triple to be processed +bool sense; code to be generated is jmpt or jmpf +oprtype *addr; address to jmp +*/ +{ + triple *ref; + oprtype *p; + + assert(sense == TRUE || sense == FALSE); + assert(oc_tab[t->opcode].octype & OCT_BOOL); + assert(t->operand[0].oprclass == TRIP_REF); + assert(t->operand[1].oprclass == TRIP_REF || t->operand[1].oprclass == 0); + switch (t->opcode) + { + case OC_COBOOL: + ex_tail(&t->operand[0]); + if (t->operand[0].oprval.tref->opcode == OC_GETTRUTH) + { + dqdel(t->operand[0].oprval.tref, exorder); + t->opcode = sense ? OC_JMPTSET : OC_JMPTCLR; + t->operand[0] = put_indr(addr); + return; + } + ref = maketriple(sense ? OC_JMPNEQ : OC_JMPEQU); + ref->operand[0] = put_indr(addr); + dqins(t, exorder, ref); + return; + case OC_COM: + bx_tail(t->operand[0].oprval.tref , !sense, addr); + t->opcode = OC_NOOP; + t->operand[0].oprclass = 0; + return; + case OC_NEQU: + sense = ! sense; + /* caution: fall through */ + case OC_EQU: + bx_relop(t, OC_EQU, sense ? OC_JMPNEQ : OC_JMPEQU, addr); + break; + case OC_NPATTERN: + sense = ! sense; + /* caution: fall through */ + case OC_PATTERN: + bx_relop(t, OC_PATTERN, sense ? OC_JMPNEQ : OC_JMPEQU, addr); + break; + case OC_NFOLLOW: + sense = ! sense; + /* caution: fall through */ + case OC_FOLLOW: + bx_relop(t, OC_FOLLOW, sense ? OC_JMPGTR : OC_JMPLEQ, addr); + break; + case OC_NSORTS_AFTER: + sense = ! sense; + /* caution: fall through */ + case OC_SORTS_AFTER: + bx_relop(t, OC_SORTS_AFTER, sense ? OC_JMPGTR : OC_JMPLEQ, addr); + break; + case OC_NCONTAIN: + sense = ! sense; + /* caution: fall through */ + case OC_CONTAIN: + bx_relop(t, OC_CONTAIN, sense ? OC_JMPNEQ : OC_JMPEQU, addr); + break; + case OC_NGT: + sense = ! sense; + /* caution: fall through */ + case OC_GT: + bx_relop(t, OC_NUMCMP, sense ? OC_JMPGTR : OC_JMPLEQ, addr); + break; + case OC_NLT: + sense = ! sense; + /* caution: fall through */ + case OC_LT: + bx_relop(t, OC_NUMCMP, sense ? OC_JMPLSS : OC_JMPGEQ, addr); + break; + case OC_NAND: + sense = ! sense; + /* caution: fall through */ + case OC_AND: + bx_boolop(t, FALSE, sense, sense, addr); + return; + case OC_NOR: + sense = !sense; + /* caution: fall through */ + case OC_OR: + bx_boolop(t, TRUE, !sense, sense, addr); + return; + default: + GTMASSERT; + } + for (p = t->operand ; p < ARRAYTOP(t->operand); p++) + if (p->oprclass == TRIP_REF) + ex_tail(p); + return; +} diff --git a/sr_port/cache.h b/sr_port/cache.h new file mode 100644 index 0000000..459808a --- /dev/null +++ b/sr_port/cache.h @@ -0,0 +1,65 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef CACHE_H +#define CACHE_H + +/* Macros to add debugging to objcode cache for indirects. To enable, uncomment line below */ +/*#define DEBUG_CACHE */ +#ifdef DEBUG_CACHE +# define DBGCACHE(x) DBGFPF(x) +#else +# define DBGCACHE(x) +#endif + +typedef struct { + mstr str; + uint4 code; +} icode_str; /* For indirect code source. */ + +typedef struct cache_ent +{ + mstr obj; + icode_str src; + int refcnt; /* Number of indirect source code pointing to same cache entry */ + int zb_refcnt; /* Number of zbreak action entry pointing to same cache entry */ +} cache_entry; + +/* Following is the indirect routine header build as part of an indirect code object */ +typedef struct ihead_struct +{ + cache_entry *indce; + int4 vartab_off; + int4 vartab_len; + int4 temp_mvals; + int4 temp_size; + int4 fixup_vals_off; /* literal mval table offset */ + int4 fixup_vals_num; /* literal mval table's mval count */ +} ihdtyp; + +#define ICACHE_TABLE_INIT_SIZE 64 /* Use 1K memory initially */ +#define ICACHE_SIZE ROUND_UP2(SIZEOF(cache_entry), NATIVE_WSIZE) + +/* We allow cache_table to grow till we hit memory or entry maximums. If more memory is needed, we do compaction. + * Current default limits (overrideable by environment variable): 128 entries, 128KB of object code on all platforms + * except IA64 architecture which gets 256KB due to its less compact instruction forms. + */ +#define MAX_CACHE_MEMSIZE (IA64_ONLY(256) NON_IA64_ONLY(128)) +#define MAX_CACHE_ENTRIES 128 + +void indir_lits(ihdtyp *ihead); +void cache_init(void); +mstr *cache_get(icode_str *indir_src); +void cache_put(icode_str *src, mstr *object); +void cache_table_rebuild(void); +void cache_stats(void); + +#endif diff --git a/sr_port/cache_cleanup.c b/sr_port/cache_cleanup.c new file mode 100644 index 0000000..d136130 --- /dev/null +++ b/sr_port/cache_cleanup.c @@ -0,0 +1,47 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "objlabel.h" +#include "cache.h" +#include "hashtab_objcode.h" +#include "rtnhdr.h" +#include "stack_frame.h" +#include "cache_cleanup.h" + +GBLREF hash_table_objcode cache_table; +GBLREF int indir_cache_mem_size; + +void cache_cleanup(stack_frame *sf) +{ + ihdtyp *irtnhdr; + cache_entry *csp; + INTPTR_T *vp; + boolean_t deleted; + + assert(sf->ctxt); + vp = (INTPTR_T *)sf->ctxt; + vp--; + if ((GTM_OMAGIC << 16) + OBJ_LABEL == *vp) /* Validate backward linkage */ + { /* Frame is one of ours */ + vp--; + irtnhdr = (ihdtyp *)((char *)vp + *vp); + csp = irtnhdr->indce; + assert(NULL != csp); + assert(0 < csp->refcnt); + csp->refcnt--; /* This usage of this cache entry is done */ + /* We want to keep the entry around with the hope that it will be accessed again. + * When we keep too many entries or entries are using too much memory cache_put will call cache_table_rebuild() + * to make space removing elements with csp->refcnt == 0 and csp->zb_refcnt == 0 + */ + } else + GTMASSERT; /* Not sure when this could happen */ +} diff --git a/sr_port/cache_cleanup.h b/sr_port/cache_cleanup.h new file mode 100644 index 0000000..72f50c1 --- /dev/null +++ b/sr_port/cache_cleanup.h @@ -0,0 +1,34 @@ +/**************************************************************** + * * + * Copyright 2001, 2004 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef CACHE_CLEANUP_DEFINED +#define CACHE_CLEANUP_DEFINED + +#define IF_INDR_FRAME_CLEANUP_CACHE_ENTRY(frame_pointer) \ +{ \ + /* See if unwinding an indirect frame*/ \ + if (frame_pointer->flags & SFF_INDCE) \ + cache_cleanup(frame_pointer); \ +} + +#define IF_INDR_FRAME_CLEANUP_CACHE_ENTRY_AND_UNMARK(frame_pointer) \ +{ \ + /* See if unwinding an indirect frame*/ \ + if (frame_pointer->flags & SFF_INDCE) \ + { \ + cache_cleanup(frame_pointer); \ + frame_pointer->flags &= SFF_INDCE_OFF; \ + } \ +} + +void cache_cleanup(stack_frame *sf); + +#endif diff --git a/sr_port/cache_get.c b/sr_port/cache_get.c new file mode 100644 index 0000000..bd14d65 --- /dev/null +++ b/sr_port/cache_get.c @@ -0,0 +1,39 @@ +/**************************************************************** + * * + * Copyright 2001, 2004 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "cache.h" +#include "hashtab_objcode.h" + +GBLREF int cache_hits, cache_fails; +GBLREF hash_table_objcode cache_table; + +/* cache_get - get cached indirect object code corresponding to input source and code from cache_table. + * + * If object code exists in cache, return pointer to object code mstr + * otherwise, return NULL. + */ +mstr *cache_get(icode_str *indir_src) +{ + cache_entry *csp; + ht_ent_objcode *tabent; + + if (NULL != (tabent = lookup_hashtab_objcode(&cache_table, indir_src))) + { + cache_hits++; + return &(((cache_entry *)tabent->value)->obj); + } else + { + cache_fails++; + return NULL; + } +} diff --git a/sr_port/cache_init.c b/sr_port/cache_init.c new file mode 100644 index 0000000..8c1fc68 --- /dev/null +++ b/sr_port/cache_init.c @@ -0,0 +1,24 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "cache.h" +#include "hashtab_objcode.h" +#include "hashtab.h" + +GBLREF hash_table_objcode cache_table; +GBLREF int indir_cache_mem_size; + +void cache_init(void) +{ + init_hashtab_objcode(&cache_table, ICACHE_TABLE_INIT_SIZE, HASHTAB_COMPACT, HASHTAB_SPARE_TABLE); + indir_cache_mem_size = 0; +} diff --git a/sr_port/cache_put.c b/sr_port/cache_put.c new file mode 100644 index 0000000..95da88b --- /dev/null +++ b/sr_port/cache_put.c @@ -0,0 +1,85 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_stdio.h" +#include "gtm_string.h" +#include "cache.h" +#include "hashtab_objcode.h" +#include "cachectl.h" +#include "cacheflush.h" +#include "rtnhdr.h" +#include "gtm_text_alloc.h" +#include "io.h" + +GBLREF hash_table_objcode cache_table; +GBLREF int indir_cache_mem_size; +GBLREF uint4 max_cache_memsize; /* Maximum bytes used for indirect cache object code */ +GBLREF uint4 max_cache_entries; /* Maximum number of cached indirect compilations */ + +void cache_put(icode_str *src, mstr *object) +{ + cache_entry *csp; + int i, fixup_cnt; + mval *fix_base, *fix; + var_tabent *var_base, *varent; + ht_ent_objcode *tabent; + boolean_t added; + + indir_cache_mem_size += (ICACHE_SIZE + object->len); + if (indir_cache_mem_size > max_cache_memsize || cache_table.count > max_cache_entries) + cache_table_rebuild(); + csp = (cache_entry *)GTM_TEXT_ALLOC(ICACHE_SIZE + object->len); + csp->obj.addr = (char *)csp + ICACHE_SIZE; + csp->refcnt = csp->zb_refcnt = 0; + csp->src = *src; + csp->obj.len = object->len; + memcpy(csp->obj.addr, object->addr, object->len); + ((ihdtyp *)(csp->obj.addr))->indce = csp; /* Set backward link to this cache entry */ + added = add_hashtab_objcode(&cache_table, &csp->src, csp, &tabent); + assert(added); + DBGCACHE((stdout, "cache_put: Added to cache lookaside %d bytes - (%d/%d/%d %d/%d) code: %d src: %.*s\n", + ICACHE_SIZE + object->len, cache_table.count, cache_table.size, max_cache_entries, + indir_cache_mem_size, max_cache_memsize, src->code, src->str.len, src->str.addr)); + DBGCACHE((stdout, "cache_put: *** updated entry: 0x"lvaddr" value: 0x"lvaddr"\n\n", tabent, tabent->value)); + /* Do address fixup on the literals that preceed the code */ + fixup_cnt = ((ihdtyp *)(csp->obj.addr))->fixup_vals_num; + if (fixup_cnt) + { + /* Do address fixups for literals in indirect code. This is done by making them point + * to the literals that are still resident in the stringpool. The rest of the old object + * code will be garbage collected but these literals will be salvaged. The reason to point + * to the stringpool version instead of in the copy we just created is that if an assignment + * to a local variable from an indirect string literal were to occur, only the mval is copied. + * So then there would be a local variable mval pointing into our malloc'd storage instead of + * the stringpool. If the cache entry were recycled to hold a different object, the local + * mval would then be pointing at garbage. By pointing these literals to their stringpool + * counterparts, we save having to (re)copy them to the stringpool where they will be handled + * safely and correctly. + */ + fix_base = (mval *)((unsigned char *)csp->obj.addr + ((ihdtyp *)(csp->obj.addr))->fixup_vals_off); + for (fix = fix_base, i = 0 ; i < fixup_cnt ; i++, fix++) + { + if (MV_IS_STRING(fix)) /* if string, place in string pool */ + fix->str.addr = (INTPTR_T)fix->str.addr + object->addr; + } + } + fixup_cnt = ((ihdtyp *)(csp->obj.addr))->vartab_len; + if (fixup_cnt) + { + /* Do address fix up of local variable name which is in stringpool */ + var_base = (var_tabent *)((unsigned char *)csp->obj.addr + ((ihdtyp *)(csp->obj.addr))->vartab_off); + for (varent = var_base, i = 0; i < fixup_cnt; i++, varent++) + varent->var_name.addr = (INTPTR_T) varent->var_name.addr + object->addr; + } + *object = csp->obj; /* Update location of object code for comp_indr */ + cacheflush(csp->obj.addr, csp->obj.len, BCACHE); +} diff --git a/sr_port/cache_stats.c b/sr_port/cache_stats.c new file mode 100644 index 0000000..c258bee --- /dev/null +++ b/sr_port/cache_stats.c @@ -0,0 +1,42 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* Print cacheing stats for indirect code */ +#include "mdef.h" + +#include "gtm_stdio.h" +#include "cache.h" +#include "hashtab_objcode.h" + +GBLREF int cache_hits, cache_fails; +GBLREF hash_table_objcode cache_table; + +void cache_stats(void) +{ + int total_attempts, ace; + ht_ent_objcode *tabent, *topent; + cache_entry *csp; + + total_attempts = cache_hits + cache_fails; + FPRINTF(stderr,"\nIndirect code cache performance -- Hits: %d, Fails: %d, Hit Ratio: %d%%\n", + cache_hits, cache_fails, total_attempts ? ((100 * cache_hits) / (cache_hits + cache_fails)) : 0); + ace = 0; /* active cache entries */ + for (tabent = cache_table.base, topent = cache_table.top; tabent < topent; tabent++) + { + if (HTENT_VALID_OBJCODE(tabent, cache_entry, csp)) + { + if (csp->refcnt || csp->zb_refcnt) + ++ace; + } + } + FPRINTF(stderr,"Indirect cache entries currently marked active: %d\n", ace); +} + diff --git a/sr_port/cache_table_rebuild.c b/sr_port/cache_table_rebuild.c new file mode 100644 index 0000000..7ea39e2 --- /dev/null +++ b/sr_port/cache_table_rebuild.c @@ -0,0 +1,54 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gtm_stdio.h" +#include "io.h" +#include "min_max.h" +#include "cache.h" +#include "hashtab_objcode.h" +#include "cachectl.h" +#include "gtm_text_alloc.h" +#include "error.h" + +GBLREF hash_table_objcode cache_table; +GBLREF int indir_cache_mem_size; + +error_def(ERR_MEMORY); +error_def(ERR_VMSMEMORY); + +void cache_table_rebuild() +{ + ht_ent_objcode *tabent, *topent; + cache_entry *csp; + + DBGCACHE((stdout, "cache_table_rebuild: Rebuilding indirect lookaside cache\n")); + for (tabent = cache_table.base, topent = cache_table.top; tabent < topent; tabent++) + { + if (HTENT_VALID_OBJCODE(tabent, cache_entry, csp)) + { + if ((0 == csp->refcnt) && (0 == csp->zb_refcnt)) + { + ((ihdtyp *)(csp->obj.addr))->indce = NULL; + indir_cache_mem_size -= (ICACHE_SIZE + csp->obj.len); + GTM_TEXT_FREE(csp); + delete_hashtab_ent_objcode(&cache_table, tabent); + } + } + } + /* Only do compaction processing if we are not processing a memory type error (which + * involves allocating a smaller table with storage we don't have. + */ + if (COMPACT_NEEDED(&cache_table) && error_condition != UNIX_ONLY(ERR_MEMORY) VMS_ONLY(ERR_VMSMEMORY)) + compact_hashtab_objcode(&cache_table); +} diff --git a/sr_port/cachectl.h b/sr_port/cachectl.h new file mode 100644 index 0000000..3b4e8de --- /dev/null +++ b/sr_port/cachectl.h @@ -0,0 +1,22 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* cachectl.h - values used as parameters to cacheflush + * + * ICACHE - flush instruction cache + * DCACHE - flush data cache + * BCACHE - flush both caches + */ + + +#define ICACHE 0x1 +#define DCACHE 0x2 +#define BCACHE (ICACHE|DCACHE) diff --git a/sr_port/cacheflush.c b/sr_port/cacheflush.c new file mode 100644 index 0000000..686c48e --- /dev/null +++ b/sr_port/cacheflush.c @@ -0,0 +1,36 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* cacheflush stub + * + * Most hardware platforms on which GT.M is implemented use separate + * instruction and data caches. It is necessary to flush these caches + * whenever we generate code in a data region in order to make sure + * the generated code gets written from the data cache to memory and + * subsequently loaded from memory into the instruction cache. + * + * Input: addr starting address of region to flush + * nbytes size, in bytes, of region to flush + * cache_select flag indicating whether to flus + * I-cache, D-cache, or both + * + * This stub is for those platforms that don't use separate data and + * instruction caches. + */ + +#include "mdef.h" +#include "cacheflush.h" + + +int cacheflush (void *addr, long nbytes, int cache_select) +{ + return 0; /* incr_link requires a zero return value for success */ +} diff --git a/sr_port/cacheflush.h b/sr_port/cacheflush.h new file mode 100644 index 0000000..b35d32f --- /dev/null +++ b/sr_port/cacheflush.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __CACHEFLUSH_H__ +#define __CACHEFLUSH_H__ + +int cacheflush (void *addr, long nbytes, int cache_select); + +#endif diff --git a/sr_port/caller_id.c b/sr_port/caller_id.c new file mode 100644 index 0000000..8210437 --- /dev/null +++ b/sr_port/caller_id.c @@ -0,0 +1,28 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* Stub for caller_id routine called from CRIT_TRACE macro to + return the return address of our caller allowing CRIT_TRACE + (used in various semaphore routines) to determine who was + calling those semaphore routines and for what purpose and + when. This is a system dependent type of operation and is + generally implemented in assembly language. This stub is + so we continue to function on those platforms we have not + yet implemented this function. */ +#include "mdef.h" +#include "caller_id.h" + + +caddr_t caller_id(void) +{ + return NULL; +} + diff --git a/sr_port/caller_id.h b/sr_port/caller_id.h new file mode 100644 index 0000000..c97f86d --- /dev/null +++ b/sr_port/caller_id.h @@ -0,0 +1,19 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef _CALLER_ID_H__ +#define _CALLER_ID_H__ + +#include +caddr_t caller_id(void); + +#endif + diff --git a/sr_port/callg.h b/sr_port/callg.h new file mode 100644 index 0000000..5ccdfda --- /dev/null +++ b/sr_port/callg.h @@ -0,0 +1,25 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#ifndef CALLG_H +#define CALLG_H +#include "fgncalsp.h" + +typedef struct gparam_list_struct +{ + intszofptr_t n; + void *arg[MAXIMUM_PARAMETERS]; +} gparam_list; + +typedef INTPTR_T (*callgfnptr)(intszofptr_t cnt, ...); +INTPTR_T callg(callgfnptr, gparam_list *); +void callg_signal(void *); + +#endif diff --git a/sr_port/callg_signal.c b/sr_port/callg_signal.c new file mode 100644 index 0000000..2bbc1d9 --- /dev/null +++ b/sr_port/callg_signal.c @@ -0,0 +1,21 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "lv_val.h" /* needed by "callg.h" */ +#include "callg.h" + +void callg_signal(void *arg) +{ + + (void)callg((callgfnptr) rts_error, (void *)arg); +} diff --git a/sr_port/ccp.h b/sr_port/ccp.h new file mode 100644 index 0000000..13df896 --- /dev/null +++ b/sr_port/ccp.h @@ -0,0 +1,249 @@ +/**************************************************************** + * * + * Copyright 2001, 2005 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef CCP_H +#define CCP_H + +/* requires gdsroot */ + +enum +{ + CCP_VALBLK_TRANS_HIST, + CCP_VALBLK_JNL_ADDR, + CCP_VALBLK_EPOCH_TN, + CCP_VALBLK_LST_ADDR +}; + +typedef struct ccp_wait_struct +{ + struct ccp_wait_struct *next; + uint4 pid; +} ccp_wait; + +typedef struct +{ + ccp_wait *first,**last; +} ccp_wait_head; + +typedef struct +{ + short cond; + unsigned short length; + int4 lockid; +} ccp_iosb; + +typedef struct mem_list_struct { + char *addr; + uint4 pages; + struct mem_list_struct *next; + struct mem_list_struct *prev; + bool free; +} mem_list; + +typedef struct ccp_db_header_struct +{ + struct sgmnt_data_struct *glob_sec; /* address of file header */ + struct gd_region_struct *greg; /* address like gv_cur_region */ + struct sgmnt_addrs_struct *segment; /* like cs_addrs */ + struct ccp_db_header_struct *next; /* pointer to next db_header in list */ + mem_list *mem_ptr; /* memory control structure */ + vms_lock_sb wm_iosb; /* iosb for write mode $enq*/ + vms_lock_sb flush_iosb; /* iosb for 'buffers flushed' $enq*/ + vms_lock_sb lock_iosb; /* iosb for MUMPS LOCK $enq*/ + vms_lock_sb refcnt_iosb; /* iosb for reference count of gtm process in database $enq*/ + ccp_iosb qio_iosb; /* iosb for $QIO used during file opens and misc operations*/ + ccp_wait_head write_wait; /* list of pid's to wake upon entering write mode */ + ccp_wait_head flu_wait; /* list of pid's to wake upon prior machine finishing flush */ + ccp_wait_head exitwm_wait; /* list of pid's to wake after finishing exitwm */ + ccp_wait_head reopen_wait; /* list of pid's to wake upon finishing close */ + trans_num last_write_tn; /* t.n. for last write on this node */ + trans_num master_map_start_tn; /* t.n. for last master map update*/ + trans_num tick_tn; /* t.n. as of last clock tick*/ + uint4 last_lk_sequence; /* sequence for last lock update */ + short unsigned wc_rover; /* used when flushing dirty buffers so that we don't have to start at the beginning + each time */ + struct ccp_db_header_struct *tick_timer_id; /* unique values to use as timer id and pointer to db */ + struct ccp_db_header_struct *quantum_timer_id; + struct ccp_db_header_struct *stale_timer_id; + struct ccp_db_header_struct *wmcrit_timer_id; + struct ccp_db_header_struct *close_timer_id; + struct ccp_db_header_struct *exitwm_timer_id; + struct ccp_db_header_struct *extra_tick_id; + date_time start_wm_time; /* Time entered write mode */ + unsigned char drop_lvl; + unsigned int quantum_expired : 1; /* quantum is up*/ + unsigned int tick_in_progress : 1; /* the tick timer is running*/ + unsigned int wmexit_requested: 1; /* we have received a request to relinquish write mode*/ + unsigned int write_mode_requested: 1; /* processing to get write mode has commenced*/ + unsigned int dirty_buffers_acquired : 1; /* set to zero when write mode is entered, + and to one when ENQ on dirty buffers is acquired*/ + unsigned int filler : 1; + unsigned int stale_in_progress : 1; /* stale timer active */ + unsigned int blocking_ast_received : 1; /* an exitwm blocking ast has been received */ + unsigned int remote_wakeup : 1; /* wakeup needed for processes blocked on mumps locks */ + unsigned int extra_tick_started : 1; /* started timer for extra tick prior to write mode release */ + unsigned int extra_tick_done : 1; /* extra tick done */ + unsigned int close_region : 1; /* closing region */ +} ccp_db_header; + +typedef union +{ + gds_file_id file_id; + struct gd_region_struct *reg; + ccp_db_header *h; + struct FAB *fab; + struct + { + unsigned char len; + unsigned char txt[23]; + } str; + struct + { + gds_file_id fid; + unsigned short cycle; + } exreq; /* exit write mode request */ + struct + { + unsigned short channel; + unsigned char *context; + } mbx; +} ccp_action_aux_value; + +/* types used to discriminate ccp_action_aux_value union members */ +#define CCTVNUL 0 /* no value */ +#define CCTVSTR 1 /* string */ +#define CCTVMBX 2 /* mailbox id */ +#define CCTVFIL 3 /* file id */ +#define CCTVDBP 4 /* data_base header pointer */ +#define CCTVFAB 5 /* pointer to a FAB */ + +#define CCP_TABLE_ENTRY(A,B,C,D) A, +typedef enum +{ +#include "ccpact_tab.h" + CCPACTION_COUNT +} ccp_action_code; +#undef CCP_TABLE_ENTRY + +typedef struct +{ + ccp_action_code action; + uint4 pid; + ccp_action_aux_value v; +} ccp_action_record; + +typedef struct +{ + int4 fl; + int4 bl; +} ccp_relque; + +typedef struct +{ + ccp_relque q; + date_time request_time, process_time; + ccp_action_record value; +}ccp_que_entry; + +#define CCP_MBX_NAME "GTM$CCP$MBX" +#define CCP_PRC_NAME "GT.CX_CONTROL" +#define CCP_LOG_NAME "GTM$CCPLOG:CCPERR.LOG" + +enum ccp_state_code +{ + CCST_CLOSED, /* Data base is closed */ + CCST_OPNREQ, /* A request to open the data base is being processed */ + CCST_RDMODE, /* The data base is in READ mode, all buffers have been written to disk */ + CCST_WRTREQ, /* A Write Mode request on our machine is being processed */ + CCST_WRTGNT, /* Write mode has been granted, but the last machine's dirty buffers are not available */ + CCST_DRTGNT, /* Write mode has been granted and the dirty buffers are available */ + CCST_WMXREQ, /* Another machine has requested write mode */ + CCST_WMXGNT /* We have relinquished write mode, and are writing dirty buffers */ +}; + +#define CCST_MASK_CLOSED (1 << CCST_CLOSED) +#define CCST_MASK_OPNREQ (1 << CCST_OPNREQ) +#define CCST_MASK_RDMODE (1 << CCST_RDMODE) +#define CCST_MASK_WRTREQ (1 << CCST_WRTREQ) +#define CCST_MASK_WRTGNT (1 << CCST_WRTGNT) +#define CCST_MASK_DRTGNT (1 << CCST_DRTGNT) +#define CCST_MASK_WMXREQ (1 << CCST_WMXREQ) +#define CCST_MASK_WMXGNT (1 << CCST_WMXGNT) + +#define CCST_MASK_OPEN (~(CCST_MASK_CLOSED | CCST_MASK_OPNREQ)) +#define CCST_MASK_WRITE_MODE (CCST_MASK_WRTGNT | CCST_MASK_DRTGNT) +#define CCST_MASK_HAVE_DIRTY_BUFFERS (CCST_MASK_DRTGNT | CCST_MASK_WMXREQ | CCST_MASK_WMXGNT | CCST_MASK_RDMODE) +#define CCP_SEGMENT_STATE(X,Y) (((1 << ((X)->ccp_state)) & (Y)) != 0) + +#define CCP_CLOSE_REGION 0 +#define CCP_OPEN_REGION 1 + +#ifdef VMS +#define CCP_FID_MSG(X,Y) (ccp_sendmsg(Y, &FILE_INFO(X)->file_id)) +#else +#define CCP_FID_MSG(X,Y) +#endif + +bool ccp_act_request(ccp_action_record *rec); +bool ccp_priority_request(ccp_action_record *rec); +short ccp_sendmsg(ccp_action_code action, ccp_action_aux_value *aux_value); +ccp_action_record *ccp_act_select(void); +ccp_db_header *ccp_get_reg_by_fab(struct FAB *fb); +ccp_db_header *ccp_get_reg(gds_file_id *name); +void ccp_exit(void); +void ccp_init(void); +void ccp_quemin_adjust(char oper); +void ccp_staleness(ccp_db_header **p); +short ccp_sendmsg(ccp_action_code action, ccp_action_aux_value *aux_value); +unsigned char *ccp_format_querec(ccp_que_entry *inrec, unsigned char *outbuf, unsigned short outbuflen); +void cce_ccp_ch(); +void cce_get_return_channel(ccp_action_aux_value *p); +void cce_dbdump(void); +void ccp_act_complete(void); +void ccp_act_init(void); +void ccp_add_reg(ccp_db_header *d); +void ccp_close1(ccp_db_header *db); +void ccp_dump(void); +void ccp_ewmwtbf_interrupt(ccp_db_header **p); +void ccp_exitwm1a(ccp_db_header *db); +void ccp_exitwm1(ccp_db_header *db); +void ccp_exitwm2a(ccp_db_header *db); +void ccp_exitwm2(ccp_db_header *db); +void ccp_exitwm3(ccp_db_header *db); +void ccp_exitwm_attempt(ccp_db_header *db); +void ccp_exitwm_blkast(ccp_db_header **pdb); +void ccp_extra_tick(ccp_db_header **p); +void ccp_gotdrt_tick(ccp_db_header *db); +void ccp_lkdowake_blkast(ccp_db_header *db); +void ccp_lkrqwake1(ccp_db_header *db); +void ccp_mbx_start(void); +void ccp_opendb1a(struct FAB *fb); +void ccp_opendb1(ccp_db_header *db); +void ccp_opendb1e(struct FAB *fb); +void ccp_opendb2(ccp_db_header *db); +void ccp_opendb3b(ccp_db_header *db); +void ccp_opendb3c(ccp_db_header *db); +void ccp_opendb3(ccp_db_header *db); +void ccp_opendb(ccp_action_record *rec); +void ccp_pndg_proc_add(ccp_wait_head *list, int4 pid); +void ccp_pndg_proc_wake(ccp_wait_head *list); +void ccp_request_write_mode(ccp_db_header *db); +void ccp_reqwm_interrupt(ccp_db_header **pdb); +void ccp_rundown(void); +void ccp_signal_cont(uint4 arg1, ...); +void ccp_tick_interrupt(ccp_db_header **p); +void ccp_tick_start(ccp_db_header *db); +void ccp_tr_checkdb(void); +void ccp_writedb4a(ccp_db_header *db); +void ccp_writedb4(ccp_db_header *db); +void ccp_writedb5(ccp_db_header *db); + +#endif diff --git a/sr_port/ccp_cluster_lock_wake.h b/sr_port/ccp_cluster_lock_wake.h new file mode 100644 index 0000000..f9c49ea --- /dev/null +++ b/sr_port/ccp_cluster_lock_wake.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __CCP_CLUSTER_LOCK_WAKE_H__ +#define __CCP_CLUSTER_LOCK_WAKE_H__ + +void ccp_cluster_lock_wake(gd_region *reg); + +#endif diff --git a/sr_port/ccpact.h b/sr_port/ccpact.h new file mode 100644 index 0000000..4380109 --- /dev/null +++ b/sr_port/ccpact.h @@ -0,0 +1,37 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* + * CCPACT.H is now obsolete; the code has been moved into CCP.H. + * The file can be NIXed once all inclusions of it have been removed. + */ + +#if 0 + +#define CCP_TABLE_ENTRY(A,B,C,D) A, + +enum action_code_enum +{ +#include "ccpact_tab.h" + CCPACTION_COUNT +}; + +#undef CCP_TABLE_ENTRY + +/* ccpact_tab auxvalue types to discriminate unions */ +#define CCTVNUL 0 /* no value */ +#define CCTVSTR 1 /* string */ +#define CCTVMBX 2 /* mailbox id */ +#define CCTVFIL 3 /* file id */ +#define CCTVDBP 4 /* data_base header pointer */ +#define CCTVFAB 5 /* pointer to a FAB */ + +#endif diff --git a/sr_port/ccpact_tab.h b/sr_port/ccpact_tab.h new file mode 100644 index 0000000..4fcbe53 --- /dev/null +++ b/sr_port/ccpact_tab.h @@ -0,0 +1,36 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* +CCP_TABLE_ENTRY (transaction routine auxvalue transaction + enum, name, type, priority ) +*/ +CCP_TABLE_ENTRY (CCTR_CHECKDB, ccp_tr_checkdb, CCTVNUL, CCPR_NOR) /* check files to see if still being accessed */ +CCP_TABLE_ENTRY (CCTR_CLOSE, ccp_tr_close, CCTVFIL, CCPR_NOR) /* notify that process no longer has interest in region */ +CCP_TABLE_ENTRY (CCTR_CLOSEJNL, ccp_tr_closejnl, CCTVDBP, CCPR_HI) /* journal file close due to oper ast */ +CCP_TABLE_ENTRY (CCTR_DEBUG, ccp_tr_debug, CCTVSTR, CCPR_HI) /* request that ccp go into debug mode */ +CCP_TABLE_ENTRY (CCTR_EWMWTBF, ccp_tr_ewmwtbf, CCTVDBP, CCPR_HI) /* exit write mode, wait for buffers to be written out */ +CCP_TABLE_ENTRY (CCTR_EXITWM, ccp_tr_exitwm, CCTVFIL, CCPR_NOR) /* relinquish write mode */ +CCP_TABLE_ENTRY (CCTR_EXWMREQ, ccp_tr_exwmreq, CCTVDBP, CCPR_NOR) /* request to relinquish write mode */ +CCP_TABLE_ENTRY (CCTR_FLUSHLK, ccp_tr_flushlk, CCTVFIL, CCPR_NOR) /* wait for prior machine's flush to finish */ +CCP_TABLE_ENTRY (CCTR_GOTDRT, ccp_tr_gotdrt, CCTVDBP, CCPR_HI) /* got enq for dirty buffers acquired */ +CCP_TABLE_ENTRY (CCTR_LKRQWAKE, ccp_tr_lkrqwake, CCTVFIL, CCPR_NOR) /* request wake up lock wait procs whole cluster*/ +CCP_TABLE_ENTRY (CCTR_NULL, ccp_tr_null, CCTVNUL, CCPR_NOR) /* noop */ +CCP_TABLE_ENTRY (CCTR_OPENDB1, ccp_tr_opendb1, CCTVFAB, CCPR_HI) /* data base open, second phase */ +CCP_TABLE_ENTRY (CCTR_OPENDB1A, ccp_tr_opendb1a, CCTVFAB, CCPR_HI) /* step two of first phase of opening */ +CCP_TABLE_ENTRY (CCTR_OPENDB1B, ccp_tr_opendb1b, CCTVDBP, CCPR_HI) /* deadlock detected getting locks, retry */ +CCP_TABLE_ENTRY (CCTR_OPENDB1E, ccp_tr_opendb1e, CCTVFAB, CCPR_HI) /* error opening database */ +CCP_TABLE_ENTRY (CCTR_OPENDB3, ccp_tr_opendb3, CCTVDBP, CCPR_HI) /* data base open, third phase */ +CCP_TABLE_ENTRY (CCTR_OPENDB3A, ccp_tr_opendb3a, CCTVDBP, CCPR_HI) /* journal file opening */ +CCP_TABLE_ENTRY (CCTR_QUEDUMP, ccp_tr_quedump, CCTVSTR, CCPR_HI) /* request dump of queues */ +CCP_TABLE_ENTRY (CCTR_STOP, ccp_tr_stop, CCTVNUL, CCPR_NOR) /* operator stop */ +CCP_TABLE_ENTRY (CCTR_WRITEDB, ccp_tr_writedb, CCTVFIL, CCPR_NOR) /* request write mode */ +CCP_TABLE_ENTRY (CCTR_WRITEDB1, ccp_tr_writedb1, CCTVDBP, CCPR_HI) /* request write mode - ENQ was granted */ diff --git a/sr_port/cdb_sc.h b/sr_port/cdb_sc.h new file mode 100644 index 0000000..bf05b39 --- /dev/null +++ b/sr_port/cdb_sc.h @@ -0,0 +1,34 @@ +/**************************************************************** + * * + * Copyright 2001, 2003 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef CDB_SC +#define CDB_SC + +/********************************* WARNING: *********************************** +* Several of these codes are concurrently defined in GVCST_BLK_SEARCH.MAR, * +* GVCST_SEARCH.MAR, MUTEX.MAR, and MUTEX_STOPREL.MAR. If their positions * +* are changed here, their definitions must be modified there as well! * +********************************************************************************/ + +enum cdb_sc +{ +#define CDB_SC_NUM_ENTRY(code, value) code = value, +#define CDB_SC_UCHAR_ENTRY(code, is_wcs_code, value) code = value, +#define CDB_SC_LCHAR_ENTRY(code, is_wcs_code, value) code = value, +#include "cdb_sc_table.h" +#undef CDB_SC_NUM_ENTRY +#undef CDB_SC_UCHAR_ENTRY +#undef CDB_SC_LCHAR_ENTRY +}; + +#define FUTURE_READ 0 /* used by dsk_read and t_qread */ + +#endif diff --git a/sr_port/cdb_sc_table.h b/sr_port/cdb_sc_table.h new file mode 100644 index 0000000..da77c17 --- /dev/null +++ b/sr_port/cdb_sc_table.h @@ -0,0 +1,94 @@ +/**************************************************************** + * * + * Copyright 2003, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* New entries should be added at the end to maintain backward compatibility */ +/* Note: This is an exception where we have 132+ characters in a line */ + +/* + * CDB_SC_NUM_ENTRY(code, value) + * CDB_SC_UCHAR_ENTRY(code, is_wcs_code, value) + * CDB_SC_LCHAR_ENTRY(code, is_wcs_code, value) + * + * is_wcs_code is TRUE if the cdb_sc code is a cache-related failure code. + * + * cdb_sc codes with numeric values are internally generated codes which is never displayed to the user and hence + * can never imply a database cache related problem. hence the macro CDB_SC_NUM_ENTRY has no is_wcs_code parameter. + * + * Currently the failure codes considered as cache failure codes are as follows. + * cdb_sc_losthist, + * cdb_sc_lostcr, + * cdb_sc_cacheprob, + * cdb_sc_lostbefor + * cdb_sc_cyclefail, + * cdb_sc_lostbmlhist, + * cdb_sc_lostbmlcr, + * cdb_sc_crbtmismatch, + * cdb_sc_bmlmod and cdb_sc_blkmod need to be added here, but they present an interesting problem. This is because + * if the database has an integrity error, we will get a cdb_sc_blkmod/bmlmod error for every transaction that reads + * the block with the BLKTNTOOLG integrity error in which case we do not want to set wc_blocked and cause indefinite + * cache-recoveries. But to do that we need to do a wcs_verify() after setting wc_blocked and if no problems are + * detected, we should unset wc_blocked. that is a little tricky and is deferred until it is considered worthy. + */ + +CDB_SC_NUM_ENTRY( cdb_sc_normal, 0) /* 0 success */ +CDB_SC_NUM_ENTRY( cdb_sc_endtree, 1) /* 1 gvcst_lftsib or gvcst_rtsib searched past end of tree */ +CDB_SC_NUM_ENTRY( cdb_sc_delete_parent, 2) /* 2 gvcst_kill_blk succeeded, but signals gvcst_kill that block was completely deleted */ +CDB_SC_NUM_ENTRY( cdb_sc_nolock, 3) /* 3 mutex_lockwim was unable to obtain a lock */ +CDB_SC_NUM_ENTRY( cdb_sc_needcrit, 4) /* 4 on 4th attempt and need crit for this region -- restart transaction no penalty */ +CDB_SC_NUM_ENTRY( cdb_sc_helpedout, 5) /* 5 wcs_blocked when t_tries >= CDB_STAGNATE */ +CDB_SC_NUM_ENTRY( cdb_sc_gbloflow, 6) /* 6 t_end or tp_tend found the database full and could not be extended */ +CDB_SC_NUM_ENTRY( cdb_sc_oprnotneeded, 7) /* 7 reorg operation was not required */ +CDB_SC_NUM_ENTRY( cdb_sc_starrecord, 8) /* 8 star record was found while reading the block */ +CDB_SC_NUM_ENTRY( cdb_sc_extend, 9) /* 9 extend requested when none seemed needed - from gdsfilext */ +CDB_SC_NUM_ENTRY( cdb_sc_jnlclose, 10) /* 10 journal file has been closed */ + +CDB_SC_UCHAR_ENTRY(cdb_sc_rmisalign1, FALSE, 'A') /* 'A' gvcst_get found record misaligned */ +CDB_SC_UCHAR_ENTRY(cdb_sc_keyoflow, FALSE, 'B') /* 'B' gvcst_expand_key or gvcst_search (3) found key overflow */ +CDB_SC_UCHAR_ENTRY(cdb_sc_rmisalign, FALSE, 'C') /* 'C' Record misaligned from nearly everyone */ +CDB_SC_UCHAR_ENTRY(cdb_sc_r2small, FALSE, 'D') /* 'D' gvcst_expand_key found record too small */ +CDB_SC_UCHAR_ENTRY(cdb_sc_losthist, TRUE, 'E') /* 'E' t_end or tp_tend (both mm or bg) - tn could not be verified from history */ +CDB_SC_UCHAR_ENTRY(cdb_sc_mapfail, FALSE, 'F') /* 'F' t_end or op_tcommit (from bm_getfree) failed to acquire new block */ +CDB_SC_UCHAR_ENTRY(cdb_sc_lostcr, TRUE, 'G') /* 'G' gvcst_...sib, t_end, tp_tend, tp_check_hist - found cache buffer modified */ +CDB_SC_UCHAR_ENTRY(cdb_sc_mkblk, FALSE, 'H') /* 'H' Composing a local block failed, from gvcst_kill (3) gvcst_put (14) */ +CDB_SC_UCHAR_ENTRY(cdb_sc_rdfail, FALSE, 'I') /* 'I' t_qread found block number requested is outside size of file as described by fileheader */ +CDB_SC_UCHAR_ENTRY(cdb_sc_badlvl, FALSE, 'J') /* 'J' gvcst_search found a child block didn't have the next block level below its parent */ +CDB_SC_UCHAR_ENTRY(cdb_sc_cacheprob, TRUE, 'K') /* 'K' db_csh_get, ... found a cache control problem */ +CDB_SC_UCHAR_ENTRY(cdb_sc_blkmod, FALSE, 'L') /* 'L' t_end, or tp_tend found block modified */ +CDB_SC_UCHAR_ENTRY(cdb_sc_uperr, FALSE, 'M') /* 'M' t_ch received an unpredicatable error */ +CDB_SC_UCHAR_ENTRY(cdb_sc_comfail, FALSE, 'N') /* 'N' Commit failed used in t_end_sysops (8) by (?) */ +CDB_SC_UCHAR_ENTRY(cdb_sc_lostbefor, TRUE, 'O') /* 'O' t_end or tp_tend found the before image needed for journaling was removed from the cache */ +CDB_SC_UCHAR_ENTRY(cdb_sc_committfail, FALSE, 'P') /* 'P' t_commit_cleanup found a partially committed block split */ +CDB_SC_UCHAR_ENTRY(cdb_sc_dbccerr, FALSE, 'Q') /* 'Q' mutex found (in 1 of 3 places) an interlock instruction failure in critical mechanism */ +CDB_SC_UCHAR_ENTRY(cdb_sc_critreset, FALSE, 'R') /* 'R' mutex found (in 1 of 6 places) that the segment crit crash count has been incremented */ +CDB_SC_UCHAR_ENTRY(cdb_sc_maxlvl, FALSE, 'S') /* 'S' t_write_root or gvcst_search found maximum legal block level for database exceeded */ +CDB_SC_UCHAR_ENTRY(cdb_sc_blockflush, FALSE, 'T') /* 'T' t_end (hist, or bitmap) found an to update a buffer that is being flushed (GT.CX) */ +CDB_SC_UCHAR_ENTRY(cdb_sc_cyclefail, TRUE, 'U') /* 'U' t_end or tp_tend found a buffer in read(only) set was overwritten though tn static */ +CDB_SC_UCHAR_ENTRY(cdb_sc_optrestart, FALSE, 'V') /* 'V' TP restart explicitly signaled by the TRESTART command */ +CDB_SC_UCHAR_ENTRY(cdb_sc_future_read, FALSE, 'W') /* 'W' dsk_read return to t_qread indicated block transaction exceeds curr_tn (GT.CX) */ +CDB_SC_UCHAR_ENTRY(cdb_sc_badbitmap, FALSE, 'X') /* 'X' bm_getfree found bitmap had bad size or level */ +CDB_SC_UCHAR_ENTRY(cdb_sc_badoffset, FALSE, 'Y') /* 'Y' gvcst_blk_search (in gvcst_search_blk or gvcst_search_tail) found a bad record offset */ +CDB_SC_UCHAR_ENTRY(cdb_sc_blklenerr, FALSE, 'Z') /* 'Z' gvcst_blk_search (in gvcst_search_blk or gvcst_search_tail) reached the end with no match */ + +CDB_SC_LCHAR_ENTRY(cdb_sc_bmlmod, FALSE, 'a') /* 'a' t_end or tp_tend (mm or bg) found bit_map modified */ +CDB_SC_LCHAR_ENTRY(cdb_sc_lostbmlhist, TRUE, 'b') /* 'b' t_end or tp_tend (bg) - tn could not be verified from history */ +CDB_SC_LCHAR_ENTRY(cdb_sc_lostbmlcr, TRUE, 'c') /* 'c' t_end or tp_tend (bg) - found cache buffer modified */ +CDB_SC_LCHAR_ENTRY(cdb_sc_lostoldblk, FALSE, 'd') /* 'd' t_qread or op_tcommit (tp and before image) - old_block of a used block is NULL */ +CDB_SC_LCHAR_ENTRY(cdb_sc_blknumerr, FALSE, 'e') /* 'e' t_qread or op_tcommit - block number is impossible */ +CDB_SC_LCHAR_ENTRY(cdb_sc_blksplit, FALSE, 'f') /* 'f' recompute_upd_array recognized that the block needs to be split */ +CDB_SC_LCHAR_ENTRY(cdb_sc_toomanyrecompute, FALSE, 'g') /* 'g' more than 25% of the blocks in read-set need to be recomputed */ +CDB_SC_LCHAR_ENTRY(cdb_sc_jnlstatemod, FALSE, 'h') /* 'h' csd->jnl_state changed or csd->jnl_before_image changed since start of the transaction */ +CDB_SC_LCHAR_ENTRY(cdb_sc_needlock, FALSE, 'i') /* 'i' on final retry and need to wait for M-lock - restart transaction - allow for max of 16 such restarts */ +CDB_SC_LCHAR_ENTRY(cdb_sc_bkupss_statemod, FALSE, 'j') /* 'j' t_end/tp_tend found that either online-backup-in-progress or snapshot + state changed since start of transaction */ +CDB_SC_LCHAR_ENTRY(cdb_sc_crbtmismatch, TRUE, 'k') /* 'k' cr->blk and bt->blk does not match */ +CDB_SC_LCHAR_ENTRY(cdb_sc_phase2waitfail, TRUE, 'l') /* 'l' wcs_phase2_commit_wait timed out when called from t_qread */ +CDB_SC_LCHAR_ENTRY(cdb_sc_inhibitkills, FALSE, 'm') /* 'm' t_end/tp_tend found inhibit_kills counter greater than zero */ +CDB_SC_LCHAR_ENTRY(cdb_sc_triggermod, FALSE, 'n') /* 'n' csd->db_trigger_cycle changed since start of of transaction */ diff --git a/sr_port/cdbg_dump.c b/sr_port/cdbg_dump.c new file mode 100644 index 0000000..6336832 --- /dev/null +++ b/sr_port/cdbg_dump.c @@ -0,0 +1,341 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" +#include "gtm_string.h" + +#include "gtmdbglvl.h" +#include "compiler.h" +#include "opcode.h" +#include "mvalconv.h" +#include "cdbg_dump.h" +#include "stringpool.h" +#include "cache.h" + +LITDEF char *oprtype_names[] = +{ + "Operand[0]", + "Operand[1]", + "Destination" +}; +LITDEF char *oprtype_type_names[] = +{ + "NIL", + "TVAR_REF", + "TVAL_REF", + "TINT_REF", + "TVAD_REF", + "TCAD_REF", + "VREG_REF", + "MLIT_REF", + "MVAR_REF", + "TRIP_REF", + "TNXT_REF", + "TJMP_REF", + "INDR_REF", + "MLAB_REF", + "ILIT_REF", + "CDLT_REF", + "TEMP_REF", + "MFUN_REF", + "MNXL_REF", + "TSIZ_REF", + "OCNT_REF" +}; + +LITDEF char *indents[11] = +{ + "", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " " +}; + +GBLREF char *oc_tab_graphic[]; +GBLREF spdesc indr_stringpool; +GBLREF int4 sa_temps_offset[]; +GBLREF int4 sa_temps[]; + +LITREF int4 sa_class_sizes[]; + +#define MAX_INDENT (32 * 1024) +STATICDEF char *indent_str; +STATICDEF int last_indent = 0; + +void cdbg_dump_triple(triple *dtrip, int indent) +{ + int len; + + PRINTF("%s Triple %s [0x"lvaddr"] fwdptr: 0x"lvaddr" bkwdptr: 0x"lvaddr" srcline: %d colmn: %d rtaddr: %d\n", + cdbg_indent(indent), oc_tab_graphic[dtrip->opcode], (long unsigned int) dtrip, + (long unsigned int) dtrip->exorder.fl, (long unsigned int) dtrip->exorder.bl, dtrip->src.line, + dtrip->src.column, dtrip->rtaddr); + /*switch(dtrip->opcode) + { + case OC_CDLIT: + PRINTF("%s OC_CDLT ptr: 0x"lvaddr" len: 0x"lvaddr"\n", + cdbg_indent(indent), opr->oprval.cdlt->addr, opr->oprval.cdlt->len); + if (opr->oprval.cdlt->len) + cdbg_dump_mstr(cdbg_indent(indent), opr->oprval.cdlt); + break; + } */ + cdbg_dump_operand(indent + 1, &dtrip->operand[0], OP_0); + cdbg_dump_operand(indent + 1, &dtrip->operand[1], OP_1); + if (dtrip->destination.oprclass) + cdbg_dump_operand(indent + 1, &dtrip->destination, OP_DEST); + fflush(stdout); +} + +void cdbg_dump_shrunk_triple(triple *dtrip, int old_size, int new_size) +{ + PRINTF("Shrunken triple %s [0x"lvaddr"] fwdptr: 0x"lvaddr" bkwdptr: 0x"lvaddr" srcline: %d colmn: %d rtaddr: %d\n", + oc_tab_graphic[dtrip->opcode], (long unsigned int) dtrip, (long unsigned int) dtrip->exorder.fl, + (long unsigned int) dtrip->exorder.bl, dtrip->src.line, dtrip->src.column, dtrip->rtaddr); + PRINTF(" old size: %d new size: %d shrinkage: %d\n", old_size, new_size, (old_size - new_size)); + fflush(stdout); +} + +void cdbg_dump_operand(int indent, oprtype *opr, int opnum) +{ + triple *rtrip; + int offset; + int len; + char *buff; + char mid[(SIZEOF(mident_fixed) * 2) + 1]; /* Sized to hold an labels name rtn.lbl */ + + if (opr) + PRINTF("%s %s [0x"lvaddr"] Type: %s\n", cdbg_indent(indent), oprtype_names[opnum], (long unsigned int) opr, + oprtype_type_names[opr->oprclass]); + else + PRINTF("%s ** Warning ** Null opr passed as operand\n", cdbg_indent(indent)); + if (!opr->oprclass) + { + fflush(stdout); + return; + } + /* We have a real oprclass, dump it's info */ + switch(opr->oprclass) + { + case TVAR_REF: + PRINTF("%s Temporary variable index %d\n", cdbg_indent(indent), opr->oprval.temp); + break; + case TCAD_REF: + case TVAD_REF: + PRINTF("%s %s reference - whatever it means: value is %d\n", cdbg_indent(indent), + ((TCAD_REF == opr->oprclass) ? "TCAD_REF" : "TVAD_REF"), opr->oprval.temp); + break; + case MVAR_REF: + if (opr->oprval.vref) + { + PRINTF("%s LS vref: 0x"lvaddr" RS vref: 0x"lvaddr" index: %d varname: %s last triple: " + "0x"lvaddr"\n", + cdbg_indent(indent),(long unsigned int) opr->oprval.vref->lson, + (long unsigned int) opr->oprval.vref->rson, opr->oprval.vref->mvidx, + cdbg_makstr(opr->oprval.vref->mvname.addr, &buff, opr->oprval.vref->mvname.len), + (long unsigned int) opr->oprval.vref->last_fetch); + free(buff); + } + else + PRINTF("%s ** Warning ** oprval.vref is NULL\n", cdbg_indent(indent)); + break; + case TINT_REF: + case TVAL_REF: + offset = sa_temps_offset[opr->oprclass]; + offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; + PRINTF("%s temp index: %d offset: 0x%08x\n", cdbg_indent(indent), opr->oprval.temp, offset); + break; + case ILIT_REF: + PRINTF("%s ilit value: %d [0x%08x]\n", cdbg_indent(indent), opr->oprval.ilit, opr->oprval.ilit); + break; + case MLIT_REF: + if (opr->oprval.mlit) + PRINTF("%s lit-ref fwdptr: 0x"lvaddr" bkwdptr: 0x"lvaddr" rtaddr: 0x"lvaddr"\n", + cdbg_indent(indent), (long unsigned int) opr->oprval.mlit->que.fl, + (long unsigned int) opr->oprval.mlit->que.bl, opr->oprval.mlit->rt_addr); + else + PRINTF("%s ** Warning ** oprval.mlit is NULL\n", cdbg_indent(indent)); + cdbg_dump_mval(indent, &opr->oprval.mlit->v); + break; + case TJMP_REF: + if (opr->oprval.tref) + PRINTF("%s tjmp-ref jump list ptr: 0x"lvaddr"\n", cdbg_indent(indent), + (long unsigned int) &opr->oprval.tref->jmplist); + else + PRINTF("%s ** Warning ** oprval.tref is NULL\n", cdbg_indent(indent)); + break; + case TRIP_REF: + rtrip = opr->oprval.tref; + PRINTF("%s Trip reference:\n", cdbg_indent(indent)); + cdbg_dump_triple(rtrip, indent + 1); + break; + case INDR_REF: + cdbg_dump_operand(indent, opr->oprval.indr, opnum); + break; + case TSIZ_REF: + if (opr->oprval.tsize) + PRINTF("%s triple at 0x"lvaddr" has size %d\n", cdbg_indent(indent), + (long unsigned int) opr->oprval.tsize->ct, opr->oprval.tsize->size); + else + PRINTF("%s ** Warning ** oprval.tsize is NULL\n", cdbg_indent(indent)); + break; + case OCNT_REF: + PRINTF("%s offset from call to next triple: %d\n", cdbg_indent(indent), opr->oprval.offset); + break; + case MLAB_REF: + case MFUN_REF: + if (opr->oprval.lab) + { + len = opr->oprval.lab->mvname.len; + memcpy(mid, opr->oprval.lab->mvname.addr, len); + mid[len] = 0; + PRINTF("%s ref type: %s mlabel name: %s\n", cdbg_indent(indent), + oprtype_type_names[opr->oprclass], mid); + } else + PRINTF("%s ref type: %s ** Warning ** oprval.lab is NULL\n", cdbg_indent(indent), + oprtype_type_names[opr->oprclass]); + break; + case CDLT_REF: + if (opr->oprval.cdlt) + { + len = opr->oprval.cdlt->len; + memcpy(mid, opr->oprval.cdlt->addr, len); + mid[len] = 0; + PRINTF("%s cdlt-ref mstr->%s", cdbg_indent(indent), mid); + } else + PRINTF("%s ref type: %s ** Warning ** oprval.cdlt is NULL\n", cdbg_indent(indent), + oprtype_type_names[opr->oprclass]); + break; + default: + PRINTF("%s %s bogus reference\n", cdbg_indent(indent), oprtype_type_names[opr->oprclass]); + } + fflush(stdout); +} + +void cdbg_dump_mval(int indent, mval *mv) +{ + boolean_t first; + double mvf; + int4 mvd; + + PRINTF("%s Type: 0x%1x (", cdbg_indent(indent), mv->mvtype); + first = TRUE; + if (mv->mvtype & MV_NM) + { + PRINTF("Number"); + first = FALSE; + } + if (mv->mvtype & MV_INT) + { + if (!first) + PRINTF(", "); + PRINTF("Integer"); + first = FALSE; + } + if (mv->mvtype & MV_STR) + { + if (!first) + PRINTF(", "); + PRINTF("String"); + fflush(stdout); + first = FALSE; + } + if (first) + { + PRINTF("Undefined MVAL)\n"); + return; + } + PRINTF(") Sign: %d Exp: %d\n", mv->sgn, mv->e); + if (mv->mvtype & MV_NUM_MASK) + { + if (mv->mvtype & MV_INT) + { + mvd = mval2i(mv); + PRINTF("%s Integer value: %d\n", cdbg_indent(indent), mvd); + } else + { + mvf = mval2double(mv); + PRINTF("%s Double value: %f\n", cdbg_indent(indent), mvf); + } + } + if (mv->mvtype & MV_STR) + { + if (!mv->str.len) + PRINTF("%s String value: \n", cdbg_indent(indent)); + else + cdbg_dump_mstr(indent, &mv->str); + } + fflush(stdout); +} + +/* Dump value of a given mstr. Assumes length is non-zero */ +void cdbg_dump_mstr(int indent, mstr *ms) +{ + unsigned char *buffer, *strp; + int len; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + len = ms->len; + strp = (unsigned char *)ms->addr; +# if defined(USHBIN_SUPPORTED) || defined(VMS) + /* In shared binary mode, shrink_trips is called after indir_lits() changes the addresses + * in the mvals to offsets. De-offset them if they don't point into the (indirect) + * stringpool. This *ONLY* happens during an indirect compilation. + */ + assert(TREF(compile_time) || indr_stringpool.base != indr_stringpool.free); + if (!TREF(compile_time) && strp < indr_stringpool.base) + strp += (UINTPTR_T)(indr_stringpool.base - SIZEOF(ihdtyp) - PADLEN(SIZEOF(ihdtyp), NATIVE_WSIZE)); +# endif + buffer = malloc(len + 1); + memcpy(buffer, strp, len); + buffer[len] = 0; + PRINTF("%s String value: %s\n", cdbg_indent(indent), buffer); + fflush(stdout); + free(buffer); +} + +/* Provide string to do indenting of formatted output */ +char *cdbg_indent(int indent) +{ + if (10 >= indent) + return (char *)indents[indent]; + + if (NULL == indent_str) + indent_str = malloc(MAX_INDENT); + if (MAX_INDENT < indent * 2) + { + fflush(stdout); + GTMASSERT; + } + if (indent > last_indent) + memset(indent_str, ' ', indent * 2); + indent_str[indent * 2] = 0; + last_indent = indent; + return indent_str; +} + +/* Make a given addr/len string into a null terminate string */ +char *cdbg_makstr(char *str, char **buf, int len) +{ + *buf = malloc(len + 1); + memcpy(*buf, str, len); + (*buf)[len] = 0; + return *buf; +} diff --git a/sr_port/cdbg_dump.h b/sr_port/cdbg_dump.h new file mode 100644 index 0000000..8255836 --- /dev/null +++ b/sr_port/cdbg_dump.h @@ -0,0 +1,27 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef CDBG_DUMP_H +#define CDBG_DUMP_H + +/* Values for 2nd arg of cdbg_dump_operand */ +#define OP_0 0 /* operand[0] is passed */ +#define OP_1 1 /* operand[1] is passed */ +#define OP_DEST 2 /* destination is passed */ + +void cdbg_dump_triple(triple *dtrip, int indent); +void cdbg_dump_shrunk_triple(triple *dtrip, int old_size, int new_size); +void cdbg_dump_operand(int indent, oprtype *opr, int opnum); +void cdbg_dump_mval(int indent, mval *mv); +void cdbg_dump_mstr(int indent, mstr *ms); +char *cdbg_indent(int indent); +char *cdbg_makstr(char *str, char **buf, mstr_len_t len); +#endif diff --git a/sr_port/ceprep_file.c b/sr_port/ceprep_file.c new file mode 100644 index 0000000..7323a54 --- /dev/null +++ b/sr_port/ceprep_file.c @@ -0,0 +1,144 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#ifdef VMS +#include +static struct FAB ceprep_fab; /* file access block for compiler escape preprocessor output */ +static struct RAB ceprep_rab; /* record access block for compiler escape preprocessor output */ +#endif + +#include "gtm_string.h" +#include "cmd_qlf.h" +#include "compiler.h" +#include "io.h" +#include "io_params.h" +#include "op.h" +#include "comp_esc.h" + +#define CEPREP_OPEN_TIMEOUT 30 + +GBLREF unsigned char source_buffer[]; +GBLREF mident module_name; +GBLREF io_pair io_curr_device; +GBLREF command_qualifier cmd_qlf; + +static io_pair dev_in_use; + +void open_ceprep_file(void) +{ +#ifdef VMS +/* stub except for VMS */ +#ifdef __ALPHA +# pragma member_alignment save +# pragma nomember_alignment +#endif + static readonly struct + { + unsigned char newversion; + unsigned char wrap; + unsigned char width; + int4 v_width; + unsigned char eol; + } open_params_list = + { + (unsigned char) iop_newversion, + (unsigned char) iop_wrap, + (unsigned char) iop_recordsize, + (int4) MAX_SRCLINE, + (unsigned char) iop_eol + }; +#ifdef __ALPHA +# pragma member_alignment restore +#endif + int mname_len; + uint4 status; + char charspace, ceprep_name_buff[MAX_MIDENT_LEN + SIZEOF(".MCI") - 1], fname[255]; + mval file, params; + struct NAM ceprep_nam; /* name block for file access block for compiler escape preprocessor output */ + + /* Create cepreprocessor output file. */ + ceprep_fab = cc$rms_fab; + ceprep_fab.fab$l_dna = ceprep_name_buff; + mname_len = module_name.len; + assert(mname_len <= MAX_MIDENT_LEN); + if (0 == mname_len) + { + MEMCPY_LIT(ceprep_name_buff, "MDEFAULT.MCI"); + ceprep_fab.fab$b_dns = SIZEOF("MDEFAULT.MCI") - 1; + } else + { + memcpy(ceprep_name_buff, module_name.addr, mname_len); + MEMCPY_LIT(&ceprep_name_buff[mname_len], ".MCI"); + ceprep_fab.fab$b_dns = mname_len + SIZEOF(".MCI") - 1; + } + if (MV_DEFINED(&cmd_qlf.ceprep_file)) + { + ceprep_fab.fab$b_fns = cmd_qlf.ceprep_file.str.len; + ceprep_fab.fab$l_fna = cmd_qlf.ceprep_file.str.addr; + } + ceprep_nam = cc$rms_nam; + ceprep_nam.nam$l_esa = fname; + ceprep_nam.nam$b_ess = SIZEOF(fname); + ceprep_nam.nam$b_nop = (NAM$M_SYNCHK); + ceprep_fab.fab$l_nam = &ceprep_nam; + ceprep_fab.fab$l_fop = FAB$M_NAM; + if (RMS$_NORMAL != (status = sys$parse(&ceprep_fab, 0, 0))) + rts_error(VARLSTCNT(1) status); + file.mvtype = params.mvtype = MV_STR; + file.str.len = ceprep_nam.nam$b_esl; + file.str.addr = fname; + params.str.len = SIZEOF(open_params_list); + params.str.addr = &open_params_list; + op_open(&file, ¶ms, CEPREP_OPEN_TIMEOUT, 0); + params.str.len = 1; + charspace = (char)iop_eol; + params.str.addr = &charspace; + dev_in_use = io_curr_device; + op_use(&file, ¶ms); +#endif + return; +} + +void close_ceprep_file(void) +{ +#ifdef VMS +/* stub except for VMS */ + unsigned char charspace; + mval param, ceprep_file; + + param.str.len = 1; + charspace = (char)iop_eol; + param.str.addr = &charspace; + ceprep_file.mvtype = param.mvtype = MV_STR; + ceprep_file.str.len = io_curr_device.in->trans_name->len; + ceprep_file.str.addr = io_curr_device.in->trans_name->dollar_io; + op_close(&ceprep_file, ¶m); + io_curr_device = dev_in_use; +#endif + return; +} + +void put_ceprep_line(void) +{ +#ifdef VMS +/* stub except for VMS */ + mval out; + + out.mvtype = MV_STR; + out.str.len = strlen((char *)source_buffer); + out.str.addr = source_buffer; + op_write(&out); + op_wteol(1); +#endif + return; +} diff --git a/sr_port/cert_blk.c b/sr_port/cert_blk.c new file mode 100644 index 0000000..264177c --- /dev/null +++ b/sr_port/cert_blk.c @@ -0,0 +1,462 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsbml.h" +#include "copy.h" +#include "subscript.h" +#include "filestruct.h" +#include "gdscc.h" +#include "gdskill.h" /* needed for tp.h */ +#include "jnl.h" /* needed for tp.h */ +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" +#include "error.h" +#include "mmemory.h" +#include "gtm_ffs.h" +#include "cert_blk.h" +#include "gtm_ctype.h" +#ifdef GTM_TRIGGER +#include "rtnhdr.h" +#include "gv_trigger.h" +#endif + +GBLREF uint4 dollar_tlevel; +GBLREF boolean_t dse_running; + +#define BITS_PER_UCHAR 8 +#define BLKS_PER_UINT4 ((SIZEOF(uint4) / SIZEOF(unsigned char)) * BITS_PER_UCHAR) / BML_BITS_PER_BLK +#define BLOCK_WINDOW 8 +#define LEVEL_WINDOW 3 +#define OFFSET_WINDOW 4 + +#define TEXT1 ": " +#define TEXT2 " " +#define TEXT3 ":" +#define TEXT4 ", " + +#define MAX_UTIL_LEN 40 +#define RTS_ERROR_FUNC(err, buff) \ +{ \ + if (gtmassert_on_error) \ + GTMASSERT; \ + rts_error_func(err, buff); \ +} + +void rts_error_func(int err, uchar_ptr_t buff); + +int cert_blk (gd_region *reg, block_id blk, blk_hdr_ptr_t bp, block_id root, boolean_t gtmassert_on_error) +{ + block_id child, prev_child; + rec_hdr_ptr_t rp, r_top; + int num_subscripts; + uint4 bplmap, mask1, offset; + sm_uint_ptr_t chunk_p; /* Value is unaligned so will be assigned to chunk */ + uint4 chunk; + sm_uc_ptr_t blk_top, blk_id_ptr, next_tp_child_ptr, key_base, mp, b_ptr; + unsigned char rec_cmpc, min_cmpc; /* the minimum cmpc expected in any record (except star-key) in a gvt */ + unsigned char ch, prior_expkey[MAX_KEY_SZ + 1]; + unsigned int prior_expkeylen; + unsigned short temp_ushort; + int blk_levl; + int blk_size, rec_size, comp_length, rec_offset, key_size; + unsigned char util_buff[MAX_UTIL_LEN]; + int util_len; + off_chain chain; + sgmnt_addrs *csa; + sgmnt_data_ptr_t csd; + boolean_t is_gvt, is_directory, first_key, full, prev_char_is_delimiter; + + error_def(ERR_DBBLEVMX); + error_def(ERR_DBBLEVMN); + error_def(ERR_DBBSIZMN); + error_def(ERR_DBBSIZMX); + error_def(ERR_DBRSIZMN); + error_def(ERR_DBRSIZMX); + error_def(ERR_DBCMPNZRO); + error_def(ERR_DBSTARSIZ); + error_def(ERR_DBSTARCMP); + error_def(ERR_DBCMPMX); + error_def(ERR_DBKEYMX); + error_def(ERR_DBKEYMN); + error_def(ERR_DBCMPBAD); + error_def(ERR_DBKEYORD); + error_def(ERR_DBPTRNOTPOS); + error_def(ERR_DBPTRMX); + error_def(ERR_DBPTRMAP); + error_def(ERR_DBLVLINC); + error_def(ERR_DBBMSIZE); + error_def(ERR_DBBMBARE); + error_def(ERR_DBBMINV); + error_def(ERR_DBBMMSTR); + error_def(ERR_DBROOTBURN); + error_def(ERR_DBDIRTSUBSC); + error_def(ERR_DBMAXNRSUBS); /* same error as ERR_MAXNRSUBSCRIPTS, but has a string output as well */ + error_def(ERR_DBINVGBL); + error_def(ERR_DBBDBALLOC); + + csa = &FILE_INFO(reg)->s_addrs; + csd = csa->hdr; + bplmap = csd->bplmap; + assert(bplmap == BLKS_PER_LMAP); + blk_levl = bp->levl; + blk_size = bp->bsiz; + offset = (uint4)blk / bplmap; + + util_len=0; + i2hex_blkfill(blk,&util_buff[util_len], BLOCK_WINDOW); + util_len += BLOCK_WINDOW; + MEMCPY_LIT(&util_buff[util_len], TEXT1); /* OFFSET_WINDOW + 1 spaces */ + util_len += SIZEOF(TEXT3) - 1; + util_len += OFFSET_WINDOW +1; + i2hex_blkfill(blk_levl, &util_buff[util_len], LEVEL_WINDOW); + util_len += LEVEL_WINDOW; + MEMCPY_LIT(&util_buff[util_len], TEXT2); + util_len += SIZEOF(TEXT2) - 1; + util_buff[util_len] = 0; + + chain = *(off_chain *)&blk; + /* Assert that if at all chain.flag is non-zero (i.e. a created block), we are in TP and not yet in the commit logic. + * The only exception to this rule is if we are in TP and inside phase1 of the commit logic and trying to certify a + * block that was killed inside of the transaction (possible if cert_blk is called directly from tp_tend). In this case, + * the block number passed is a special value GDS_CREATE_BLK_MAX so check that. + */ + assert(!chain.flag || dollar_tlevel && (!csa->t_commit_crit + || ((T_COMMIT_CRIT_PHASE1 == csa->t_commit_crit) && (GDS_CREATE_BLK_MAX == blk)))); + if (!chain.flag && ((offset * bplmap) == (uint4)blk)) /* it's a bitmap */ + { + if ((unsigned char)blk_levl != LCL_MAP_LEVL) + { + RTS_ERROR_FUNC(MAKE_MSG_INFO(ERR_DBLVLINC), util_buff); + return FALSE; + } + if (blk_size != BM_SIZE(bplmap)) + { + RTS_ERROR_FUNC(ERR_DBBMSIZE, util_buff); + return FALSE; + } + mp = (sm_uc_ptr_t)bp + SIZEOF(blk_hdr); + if ((*mp & 1) != 0) + { /* bitmap doesn't protect itself */ + RTS_ERROR_FUNC(ERR_DBBMBARE, util_buff); + return FALSE; + } + full = TRUE; + offset = ((csa->ti->total_blks - blk) >= bplmap) ? bplmap : (csa->ti->total_blks - blk); + blk_top = (sm_uc_ptr_t)bp + BM_SIZE(offset + (BITS_PER_UCHAR / BML_BITS_PER_BLK) - 1); + for (chunk_p = (sm_uint_ptr_t)mp ; (sm_uc_ptr_t)chunk_p - blk_top < 0 ; chunk_p++) + { + GET_LONG(chunk, chunk_p); /* Obtain unalinged unit4 value */ + /* The following code is NOT independent of the bitmap layout: */ + mask1 = chunk & SIXTEEN_BLKS_FREE; /* mask 'recycled' blocks to 'free' blocks */ + if ((mask1 != 0) && full) /* check for free blocks */ + { /* if (full bitmap || full chunk || regular scan of a "short" bitmap) */ + if ((offset == bplmap) || ((blk_top - (sm_uc_ptr_t)chunk_p) > SIZEOF(chunk)) + || (NO_FREE_SPACE != bml_find_free((int4)((sm_uc_ptr_t)chunk_p - mp), mp, offset))) + { + full = FALSE; + } + } + mask1 ^= SIXTEEN_BLKS_FREE; /* complement to busy */ + mask1 <<= 1; /* shift to reused position */ + mask1 &= chunk; /* check against the original contents */ + if (mask1 != 0) /* busy and reused should never appear together */ + { + RTS_ERROR_FUNC(ERR_DBBMINV, util_buff); + return FALSE; + } + + } + if (full == (NO_FREE_SPACE != gtm_ffs(blk / bplmap, MM_ADDR(csd), MASTER_MAP_BITS_PER_LMAP))) + { + RTS_ERROR_FUNC(ERR_DBBMMSTR, util_buff); + /* DSE CACHE -VERIFY used to fail occasionally with the DBBMMSTR error because of passing + * an older twin global buffer that contained stale bitmap information. That is now fixed. + * So we dont expect any more such failures. Assert accordingly. + */ + assert(!dse_running); + return FALSE; + } + return TRUE; + } + if (blk_levl > MAX_BT_DEPTH) + { + RTS_ERROR_FUNC(ERR_DBBLEVMX, util_buff); + return FALSE; + } + if (blk_levl < 0) + { + RTS_ERROR_FUNC(ERR_DBBLEVMN, util_buff); + return FALSE; + } + if (blk_levl == 0) + { /* data block */ + if ((DIR_ROOT == blk) || (blk == root)) + { /* headed for where an index block should be */ + RTS_ERROR_FUNC(ERR_DBROOTBURN, util_buff); + return FALSE; + } + if (blk_size < SIZEOF(blk_hdr)) + { + RTS_ERROR_FUNC(ERR_DBBSIZMN, util_buff); + return FALSE; + } + } else + { /* index block */ + if (blk_size < (SIZEOF(blk_hdr) + SIZEOF(rec_hdr) + SIZEOF(block_id))) + { /* must have at least one record */ + RTS_ERROR_FUNC(ERR_DBBSIZMN, util_buff); + return FALSE; + } + } + if (blk_size > csd->blk_size) + { + RTS_ERROR_FUNC(ERR_DBBSIZMX, util_buff); + return FALSE; + } + if (0 == root) + { + is_directory = FALSE; + is_gvt = FALSE; + /* if both "is_directory" and "is_gvt" are FALSE, then we dont know YET if the given block is a directory or gvt */ + } else if (DIR_ROOT == root) + { + is_directory = TRUE; + is_gvt = FALSE; + } else + { + is_directory = FALSE; + is_gvt = TRUE; + } + blk_top = (sm_uc_ptr_t)bp + blk_size; + first_key = TRUE; + min_cmpc = 0; + prior_expkeylen = 0; + comp_length = 2 * SIZEOF(char); /* for double NUL to indicate no prior key */ + prior_expkey[0] = prior_expkey[1] = 0; /* double NUL also works for memvcmp test for key order */ + next_tp_child_ptr = NULL; + prev_child = 0; + + for (rp = (rec_hdr_ptr_t)((sm_uc_ptr_t)bp + SIZEOF(blk_hdr)) ; rp < (rec_hdr_ptr_t)blk_top ; rp = r_top) + { + GET_RSIZ(rec_size, rp); + rec_offset = (int)((sm_ulong_t)rp - (sm_ulong_t)bp); + /*add util_buff here*/ + + util_len=0; + i2hex_blkfill(blk,&util_buff[util_len], BLOCK_WINDOW); + util_len += BLOCK_WINDOW; + MEMCPY_LIT(&util_buff[util_len], TEXT1); /* OFFSET_WINDOW + 1 spaces */ + util_len += SIZEOF(TEXT3) - 1; + i2hex_nofill(rec_offset, &util_buff[util_len], OFFSET_WINDOW); + util_len += OFFSET_WINDOW + 1; + i2hex_blkfill(blk_levl, &util_buff[util_len], LEVEL_WINDOW); + util_len += LEVEL_WINDOW; + MEMCPY_LIT(&util_buff[util_len], TEXT2); + util_len += SIZEOF(TEXT2) - 1; + util_buff[util_len] = 0; + + if (rec_size <= SIZEOF(rec_hdr)) + { + RTS_ERROR_FUNC(ERR_DBRSIZMN, util_buff); + return FALSE; + } + if (rec_size > (short)((sm_ulong_t)blk_top - (sm_ulong_t)rp)) + { + RTS_ERROR_FUNC(ERR_DBRSIZMX, util_buff); + return FALSE; + } + r_top = (rec_hdr_ptr_t)((sm_ulong_t)rp + rec_size); + rec_cmpc = rp->cmpc; + if (first_key) + { + if (rec_cmpc) + { + RTS_ERROR_FUNC(ERR_DBCMPNZRO, util_buff); + return FALSE; + } + if (0 == blk_levl) + { + ch = *((sm_uc_ptr_t)rp + SIZEOF(rec_hdr)); + if (!(VALFIRSTCHAR_WITH_TRIG(ch))) + GTMASSERT; + } + } + if (r_top == (rec_hdr_ptr_t)blk_top && blk_levl) + { /* star key */ + if (rec_size != SIZEOF(rec_hdr) + SIZEOF(block_id)) + { + RTS_ERROR_FUNC(ERR_DBSTARSIZ, util_buff); + return FALSE; + } + if (rec_cmpc) + { + RTS_ERROR_FUNC(ERR_DBSTARCMP, util_buff); + return FALSE; + } + blk_id_ptr = (sm_uc_ptr_t)rp + SIZEOF(rec_hdr); + } else + { /* non-star key */ + key_base = (sm_uc_ptr_t)rp + SIZEOF(rec_hdr); + /* num_subscripts = number of full subscripts found in the key (including the compressed part) */ + num_subscripts = 0; + prev_char_is_delimiter = FALSE; + if (rec_cmpc) + { + if (rec_cmpc >= prior_expkeylen) + { + RTS_ERROR_FUNC(ERR_DBCMPMX, util_buff); + return FALSE; + } + for (b_ptr = prior_expkey; b_ptr < (prior_expkey + rec_cmpc); b_ptr++) + { + if (KEY_DELIMITER == *b_ptr) + num_subscripts++; + } + prev_char_is_delimiter = (KEY_DELIMITER == prior_expkey[rec_cmpc - 1]); + } + assert(key_base < (sm_uc_ptr_t)r_top); /* otherwise we would have signalled ERR_DBRSIZMN error */ + for (blk_id_ptr = key_base ; ; ) + { + if (KEY_DELIMITER == *blk_id_ptr++) + { + if (!min_cmpc) + { /* note down the length of the global-variable (without subscripts) from the + * first key in the block. every other record in this block (except the star-key + * in case of an index block) should have a rec_cmpc that is atleast this much + * (of course this is TRUE only if we are in a GVT). + */ + min_cmpc = blk_id_ptr - key_base; + } + if (prev_char_is_delimiter) + break; /* found key terminator */ + prev_char_is_delimiter = TRUE; + num_subscripts++; + } else + prev_char_is_delimiter = FALSE; + if (blk_id_ptr >= (sm_uc_ptr_t)r_top) + { + RTS_ERROR_FUNC(ERR_DBKEYMX, util_buff); + return FALSE; + } + } + num_subscripts--; /* the global variable name was counted above as a subscript. adjust that */ + key_size = (int4)(blk_id_ptr - key_base); + if (!first_key && (rec_cmpc < min_cmpc)) + { /* name-level change between consecutive records in the block, this should be a directory block */ + if (is_gvt) + { /* this is a contradiction. a block cannot be a directory and gvt at the same time. + * gvt should contain all keys with the same global name */ + RTS_ERROR_FUNC(ERR_DBINVGBL, util_buff); + return FALSE; + } + is_directory = TRUE; /* no need to do this if it was already TRUE but we save an if check */ + } + if (num_subscripts) + { /* key has subscripts, should therefore be a GVT block */ + if (is_directory) + { /* this is a contradiction. a block cannot be a directory and gvt at the same time. + * the directory tree should contain only name-level (i.e. unsubscripted) globals */ + RTS_ERROR_FUNC(ERR_DBDIRTSUBSC, util_buff); + return FALSE; + } + is_gvt = TRUE; /* no need to do this if it was already TRUE but we save an if check */ + } + if (MAX_GVSUBSCRIPTS <= num_subscripts) + { + RTS_ERROR_FUNC(ERR_DBMAXNRSUBS, util_buff); + return FALSE; + } + if (blk_levl && (key_size != (rec_size - SIZEOF(block_id) - SIZEOF(rec_hdr)))) + { + RTS_ERROR_FUNC(ERR_DBKEYMN, util_buff); + return FALSE; + } + assert(first_key || (rec_cmpc < prior_expkeylen)); + if (!first_key) + { + if (prior_expkey[rec_cmpc] == key_base[0]) + { + RTS_ERROR_FUNC(ERR_DBCMPBAD, util_buff); + return FALSE; + } + if (((unsigned int)prior_expkey[rec_cmpc] >= (unsigned int)key_base[0])) + { + RTS_ERROR_FUNC(ERR_DBKEYORD, util_buff); + return FALSE; + } + } + memcpy(prior_expkey + rec_cmpc, key_base, key_size); + prior_expkeylen = rec_cmpc + key_size; + } + /* Check for proper child block numbers */ + if ((0 != blk_levl) || (0 != is_directory)) + { + GET_LONG(child, blk_id_ptr); + chain = *(off_chain *)&child; + /* In TP, child block number can be greater than the total_blks for blocks created within TP. + * Dont do any checks on such blocks. + */ + if (!dollar_tlevel || !chain.flag) + { + if (child <= 0) + { + RTS_ERROR_FUNC(ERR_DBPTRNOTPOS, util_buff); + return FALSE; + } + if (child > csa->ti->total_blks) + { + RTS_ERROR_FUNC(ERR_DBPTRMX, util_buff); + return FALSE; + } + if (!(child % bplmap)) + { + RTS_ERROR_FUNC(ERR_DBPTRMAP, util_buff); + return FALSE; + } + if (child == prev_child) + { + RTS_ERROR_FUNC(ERR_DBBDBALLOC, util_buff); + return FALSE; + } + prev_child = child; + } else + { + if ((blk_id_ptr != next_tp_child_ptr) && (NULL != next_tp_child_ptr)) + { + RTS_ERROR_FUNC(ERR_DBPTRNOTPOS, util_buff); + return FALSE; + } + next_tp_child_ptr = blk_id_ptr + chain.next_off; + } + } + first_key = FALSE; + comp_length = prior_expkeylen; + } + assert(!is_directory || !is_gvt); /* the block cannot be a directory AND gvt at the same time */ + return TRUE; + +} + +void rts_error_func(int err, uchar_ptr_t buff) +{ + rts_error(VARLSTCNT(4) MAKE_MSG_INFO(err), 2, LEN_AND_STR((char_ptr_t)buff)); +} diff --git a/sr_port/cert_blk.h b/sr_port/cert_blk.h new file mode 100644 index 0000000..29bd8fd --- /dev/null +++ b/sr_port/cert_blk.h @@ -0,0 +1,36 @@ +/**************************************************************** + * * + * Copyright 2003, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef CERT_BLK_DEFINED + +#define CERT_BLK_DEFINED + +int cert_blk(gd_region *reg, block_id blk, blk_hdr_ptr_t bp, block_id root, boolean_t gtmassert_on_error); + +#define CERT_BLK_IF_NEEDED(certify_all_blocks, gv_cur_region, cs, blk_ptr, gv_target) \ +{ \ + assert(gds_t_create != cs->mode); /* should have morphed into gds_t_acquired before getting here */ \ + if (certify_all_blocks) \ + { /* GTMASSERT on integ error */ \ + /* If cs->mode is of type kill_t_create, then it would have been assigned an arbitrary block# \ + * inside of the transaction which could match a real block # in the database at this point. \ + * For example, it could be identical to the root block of the global in which case cert_blk \ + * will assume this is the root block when actually this is a block that is no longer part of \ + * the tree. So pass an invalid block# that indicates it is a created block. \ + */ \ + cert_blk(gv_cur_region, \ + ((kill_t_create != cs->mode) ? cs->blk : GDS_CREATE_BLK_MAX), (blk_hdr_ptr_t)blk_ptr, \ + dollar_tlevel ? ((NULL != cs->blk_target) ? cs->blk_target->root : 0) \ + : ((NULL != gv_target) ? gv_target->root : 0), TRUE); \ + } \ +} + +#endif diff --git a/sr_port/cg_var.c b/sr_port/cg_var.c new file mode 100644 index 0000000..043dfad --- /dev/null +++ b/sr_port/cg_var.c @@ -0,0 +1,30 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "compiler.h" +#include "rtnhdr.h" +#include "obj_file.h" +#include "cg_var.h" +#include "stringpool.h" +#include "hashtab_mname.h" + +GBLREF spdesc stringpool; + +void cg_var(mvar *v, var_tabent **p) +{ /* Copy mident with variable name to variable table entry */ + assert(stringpool.base <= (unsigned char *)v->mvname.addr && (unsigned char *)v->mvname.addr < stringpool.top); + (*p)[v->mvidx].var_name = v->mvname; + COMPUTE_HASH_MNAME(&((*p)[v->mvidx])); + (*p)[v->mvidx].var_name.addr = (char *)(v->mvname.addr - (char *)stringpool.base); + (*p)[v->mvidx].marked = FALSE; +} diff --git a/sr_port/cg_var.h b/sr_port/cg_var.h new file mode 100644 index 0000000..3c1ee05 --- /dev/null +++ b/sr_port/cg_var.h @@ -0,0 +1,18 @@ +/**************************************************************** + * * + * Copyright 2001, 2004 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef CG_VAR_H_INCLUDED +#define CG_VAR_H_INCLUDED + +void cg_var(mvar *v, var_tabent **p); +void ind_cg_var(mvar *v, var_tabent **p); + +#endif diff --git a/sr_port/cgp.h b/sr_port/cgp.h new file mode 100644 index 0000000..1fba200 --- /dev/null +++ b/sr_port/cgp.h @@ -0,0 +1,19 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#define CGP_NOSTATE (0) +#define CGP_PARSE (1) +#define CGP_RESOLVE (2) +#define CGP_APPROX_ADDR (3) +#define CGP_ADDR_OPT (4) +#define CGP_ASSEMBLY (5) +#define CGP_MACHINE (6) +#define CGP_FINI (7) diff --git a/sr_port/change_reg.c b/sr_port/change_reg.c new file mode 100644 index 0000000..f53320c --- /dev/null +++ b/sr_port/change_reg.c @@ -0,0 +1,54 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "change_reg.h" +#include "tp_set_sgm.h" + +GBLREF gd_region *gv_cur_region; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF uint4 dollar_tlevel; + +void change_reg(void) +{ + if (!gv_cur_region) + { + cs_addrs = (sgmnt_addrs *)0; + cs_data = (sgmnt_data_ptr_t)0; + return; + } + + switch (gv_cur_region->dyn.addr->acc_meth) + { + case dba_usr: + case dba_cm: + cs_addrs = (sgmnt_addrs *)0; + cs_data = (sgmnt_data_ptr_t)0; + break; + case dba_mm: + case dba_bg: + cs_addrs = &FILE_INFO(gv_cur_region)->s_addrs; + cs_data = cs_addrs->hdr; + if (dollar_tlevel) + tp_set_sgm(); + break; + default: + GTMASSERT; + } +} + diff --git a/sr_port/change_reg.h b/sr_port/change_reg.h new file mode 100644 index 0000000..5062631 --- /dev/null +++ b/sr_port/change_reg.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef CHANGE_REG_INCLUDED +#define CHANGE_REG_INCLUDED + +void change_reg(void); + +#endif /* CHANGE_REG_INCLUDED */ diff --git a/sr_port/chk2lev.m b/sr_port/chk2lev.m new file mode 100644 index 0000000..f1b1a11 --- /dev/null +++ b/sr_port/chk2lev.m @@ -0,0 +1,21 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +chk2lev ;check for op codes which are missing slots + New x,i + Set x="" + For Set x=$o(work(x)) Quit:x="" Do + . If x#1 Do Quit + . . Set x=x\1 + . . If "OC_CO"'=$Extract(opx(x),1,5) Do + . . . For i=x+.1:.1:x+.3 If '$Data(work(i)) Write !,"subcode ",i-x*10," is missing for op code ",x," (",opx(x),")",! + . . Set x=x+.9 + . If $Order(work(x))\1=x Write !,"triple code ",x," (",opx(x),") is present with subcodes",! + Quit diff --git a/sr_port/chkop.m b/sr_port/chkop.m new file mode 100644 index 0000000..9b56350 --- /dev/null +++ b/sr_port/chkop.m @@ -0,0 +1,19 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +chkop ;display undefined triple codes + u "" + n x + w !,"missing triple op codes: ",! + s x="" +chkop1 s x=$o(opx(x)) i x="" g chkop9 + i '$d(work(x)),'$d(work(x+.1)) w x,?7,opx(x),! + g chkop1 +chkop9 q diff --git a/sr_port/chktchain.c b/sr_port/chktchain.c new file mode 100644 index 0000000..416146f --- /dev/null +++ b/sr_port/chktchain.c @@ -0,0 +1,32 @@ +/**************************************************************** + * * + * Copyright 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "compiler.h" + +/* Check that the triple chain is a well formed doubly linked list */ + +void chktchain(triple *head) +{ + triple *tp, *tp2; + + /* tp2 is needed to detect cycles in the list (so we avoid looping indefinitely) that dont return to the head */ + for (tp = head->exorder.fl, tp2 = tp->exorder.fl; tp != head; tp = tp->exorder.fl, tp2 = tp2->exorder.fl->exorder.fl) + { + if (tp->exorder.bl->exorder.fl != tp) + GTMASSERT; /* if this assert fails, then recompile with DEBUG_TRIPLES to catch the issue sooner */ + if (tp->exorder.fl->exorder.bl != tp) + GTMASSERT; /* if this assert fails, then recompile with DEBUG_TRIPLES to catch the issue sooner */ + if (tp == tp2) /* cycle found, but without returning to the head of the linked list */ + GTMASSERT; /* if this assert fails, then recompile with DEBUG_TRIPLES to catch the issue sooner */ + } +} diff --git a/sr_port/cli_get_str_ele.c b/sr_port/cli_get_str_ele.c new file mode 100644 index 0000000..7409b7a --- /dev/null +++ b/sr_port/cli_get_str_ele.c @@ -0,0 +1,30 @@ +/**************************************************************** + * * + * Copyright 2003 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "cliif.h" + +LITREF unsigned char lower_to_upper_table[]; + +bool cli_get_str_ele(char *inbuff, char *dst, unsigned short *dst_len, boolean_t upper_case) +{ + *dst_len = 0; + while (*inbuff && ',' != *inbuff) + { + if (upper_case) + *dst++ = lower_to_upper_table[*inbuff++]; + else + *dst++ = *inbuff++; + (*dst_len)++; + } + *dst = 0; + return (*dst_len ? TRUE : FALSE); +} diff --git a/sr_port/cli_port.c b/sr_port/cli_port.c new file mode 100644 index 0000000..4b9ebdd --- /dev/null +++ b/sr_port/cli_port.c @@ -0,0 +1,344 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_ctype.h" +#include "gtm_stdio.h" +#include "gtm_stdlib.h" +#include "gtm_string.h" + +#include "gtm_limits.h" +#include + +#include "cli.h" +#include "util.h" + +/* A lot of stuff that can be made portable across unix and vvms cli.c needs to be moved into this module. + * For a start, cli_str_to_hex() is moved in. At least cli_get_str(), cli_get_int(), cli_get_num() can be moved in later. + */ + +/* +*------------------------------------------------------- +* Check if string is a decimal number +* +* Return: +* TRUE - only decimal digits +* FALSE - otherwise +* ------------------------------------------------------- +*/ +int cli_is_dcm(char *p) +{ + while (*p && ISDIGIT_ASCII(*p)) + p++; + + if (*p) return (FALSE); + else return (TRUE); +} + +/* + * -------------------------------------------------- + * Convert string to hex. + * + * Return: + * TRUE - OK + * FALSE - Could not convert to hex + * -------------------------------------------------- + */ +boolean_t cli_str_to_hex(char *str, uint4 *dst) +{ + unsigned long result; + int save_errno; + + save_errno = errno; + errno = 0; + result = STRTOUL(str, NULL, 16); + if ( +#if INT_MAX < LONG_MAX + (UINT_MAX < result) || /* outside UINT range */ +#endif + (ERANGE == errno && ULONG_MAX == result) || (0 == result && 0 != errno)) + { /* out of range or other error */ + *dst = 0; + errno = save_errno; + return FALSE; + } else + { + *dst = (uint4)result; + errno = save_errno; + return TRUE; + } +} + +/* + * -------------------------------------------------- + * Convert string to 64 bit hex. + * + * Return: + * TRUE - OK + * FALSE - Could not convert to hex + * -------------------------------------------------- + */ +boolean_t cli_str_to_hex64(char *str, gtm_uint64_t *dst) +{ + gtm_uint64_t result; + int save_errno; + + save_errno = errno; + errno = 0; + result = STRTOU64L(str, NULL, 16); + if ((ERANGE == errno && GTM_UINT64_MAX == result) || (0 == result && 0 != errno)) + { /* out of range or other error */ + *dst = 0; + errno = save_errno; + return FALSE; + } else + { + *dst = result; + errno = save_errno; + return TRUE; + } +} + +/* + * -------------------------------------------------- + * Convert string to 64 bit unsigned int. + * + * Return: + * TRUE - OK + * FALSE - Could not convert to int + * -------------------------------------------------- + */ +boolean_t cli_str_to_uint64(char *str, gtm_uint64_t *dst) +{ + gtm_uint64_t result; + int save_errno; + + save_errno = errno; + errno = 0; + result = STRTOU64L(str, NULL, 10); + if ((ERANGE == errno && GTM_UINT64_MAX == result) || (0 == result && 0 != errno)) + { /* out of range or other error */ + *dst = 0; + errno = save_errno; + return FALSE; + } else + { + *dst = result; + errno = save_errno; + return TRUE; + } +} + +/* + * -------------------------------------------------- + * Convert string to int. + * + * Return: + * TRUE - OK + * FALSE - Could not convert to int + * -------------------------------------------------- + */ +boolean_t cli_str_to_int(char *str, int4 *dst) +{ + long result; + int save_errno; + + save_errno = errno; + errno = 0; + result = STRTOL(str, NULL, 10); + if ( +#if INT_MAX < LONG_MAX + (INT_MIN > result || INT_MAX < result) || /* outside INT range */ +#endif + (ERANGE == errno && (LONG_MIN == result || LONG_MAX == result)) || (0 == result && 0 != errno)) + { /* out of range or other error */ + *dst = 0; + errno = save_errno; + return FALSE; + } else + { + *dst = (int4)result; + errno = save_errno; + return TRUE; + } +} + +/* + * -------------------------------------------------- + * Convert string to 64 bit int. + * + * Return: + * TRUE - OK + * FALSE - Could not convert to int + * -------------------------------------------------- + */ +boolean_t cli_str_to_int64(char *str, gtm_int64_t *dst) +{ + gtm_int64_t result; + int save_errno; + + save_errno = errno; + errno = 0; + result = STRTO64L(str, NULL, 10); + if ((ERANGE == errno && (GTM_INT64_MIN == result || GTM_INT64_MAX == result)) || (0 == result && 0 != errno)) + { /* out of range or other error */ + *dst = 0; + errno = save_errno; + return FALSE; + } else + { + *dst = result; + errno = save_errno; + return TRUE; + } +} + +/* + * -------------------------------------------------- + * Convert string to number. + * + * Return: + * TRUE - OK + * FALSE - Could not convert to number + * -------------------------------------------------- + */ +boolean_t cli_str_to_num(char *str, int4 *dst) +{ + long result; + int save_errno, base; + + save_errno = errno; + errno = 0; + if (cli_is_dcm(str)) + base = 10; + else + base = 16; + result = STRTOL(str, NULL, base); + if ( +#if INT_MAX < LONG_MAX + (INT_MIN > result || INT_MAX < result) || /* outside INT range */ +#endif + (ERANGE == errno && (LONG_MIN == result || LONG_MAX == result)) || (0 == result && 0 != errno)) + { /* out of range or other error */ + *dst = 0; + errno = save_errno; + return FALSE; + } else + { + *dst = (int4)result; + errno = save_errno; + return TRUE; + } +} + +/* + * -------------------------------------------------- + * Convert string to 64 bit number. + * + * Return: + * TRUE - OK + * FALSE - Could not convert to number + * -------------------------------------------------- + */ +boolean_t cli_str_to_num64(char *str, gtm_int64_t *dst) +{ + gtm_int64_t result; + int save_errno, base; + + save_errno = errno; + errno = 0; + if (cli_is_dcm(str)) + base = 10; + else + base = 16; + result = STRTO64L(str, NULL, base); + if ((ERANGE == errno && (GTM_INT64_MIN == result || GTM_INT64_MAX == result)) || (0 == result && 0 != errno)) + { /* out of range or other error */ + *dst = 0; + errno = save_errno; + return FALSE; + } else + { + *dst = result; + errno = save_errno; + return TRUE; + } +} + +int cli_parse_two_numbers(char *qual_name, const char delimiter, uint4 *first_num, uint4 *second_num) +{ /* Parse two unsigned base 10 numbers separated by the given delimiter. Eg. -LOG_INTERVAL=10,20 (on VMS, -LOG_INTERVAL="10,20"). + * Both Unix and VMS accept the qualifier as a string. NOTE: On VMS, such qualifiers are quoted strings. + * Both numbers are optional (eg. -LOG_INTERVAL=10, or -LOG_INTERVAL=",20", or -LOG_INTERVAL=,). + * Return values: + * CLI_2NUM_FIRST_SPECIFIED (binary 10), first number specified, second not + * CLI_2NUM_SECOND_SPECIFIED (binary 01), first number not specified, second is + * CLI_2NUM_BOTH_SPECIFIED (binary 11) (CLI_2NUM_FIRST_SPECIFIED | CLI_2NUM_SECOND_SPECIFIED), both specified + * 0 (binary 00), error in parsing either number + */ + char *first_num_str, *second_num_str, *two_num_str_top, *num_endptr; + char two_num_qual_str[128]; + unsigned short two_num_qual_len; + uint4 num; + int retval = 0; + + two_num_qual_len = SIZEOF(two_num_qual_str); + if (!cli_get_str(qual_name, two_num_qual_str, &two_num_qual_len)) + { + util_out_print("Error parsing !AZ qualifier", TRUE, qual_name); + return 0; + } +#ifdef VMS + /* DCL does not strip quotes included in the command line. However, the DEFAULT value (see mupip_cmd.cld) is stripped + * of quotes. */ + if ('"' == two_num_qual_str[0]) + { + assert('"' == two_num_qual_str[two_num_qual_len - 1]); /* end quote should exist */ + first_num_str = &two_num_qual_str[1]; /* Skip begin quote */ + two_num_qual_str[two_num_qual_len - 1] = '\0'; /* Zap end quote */ + two_num_qual_len -= 2; /* Quotes gone */ + } else +#endif + first_num_str = two_num_qual_str; + for (second_num_str = first_num_str, two_num_str_top = first_num_str + two_num_qual_len; + second_num_str < two_num_str_top && delimiter != *second_num_str; + second_num_str++) + ; + if (delimiter == *second_num_str) + *second_num_str++ = '\0'; + if (*first_num_str != '\0') /* VMS issues EINVAL if strtoul is passed null string */ + { + errno = 0; + num = (uint4)STRTOUL(first_num_str, &num_endptr, 10); + if ((0 == num && (0 != errno || (num_endptr == first_num_str && *first_num_str != '\0'))) || + (0 != errno && GTM64_ONLY(UINT_MAX == num) NON_GTM64_ONLY(ULONG_MAX == num))) + { + util_out_print("Error parsing or invalid parameter for !AZ", TRUE, qual_name); + return 0; + } + *first_num = num; + retval |= CLI_2NUM_FIRST_SPECIFIED; + } /* else, first number not specified */ + if (second_num_str < two_num_str_top && *second_num_str != '\0') + { + errno = 0; + num = (uint4)STRTOUL(second_num_str, &num_endptr, 10); + if ((0 == num && (0 != errno || (num_endptr == second_num_str && *second_num_str != '\0'))) || + (0 != errno && GTM64_ONLY(UINT_MAX == num) NON_GTM64_ONLY(ULONG_MAX == num))) + { + util_out_print("Error parsing or invalid parameter for LOG_INTERVAL", TRUE); + return 0; + } + *second_num = num; + retval |= CLI_2NUM_SECOND_SPECIFIED; + } /* else, second number not specified */ + return retval; +} diff --git a/sr_port/cliif.h b/sr_port/cliif.h new file mode 100644 index 0000000..305f885 --- /dev/null +++ b/sr_port/cliif.h @@ -0,0 +1,61 @@ +/**************************************************************** + * * + * Copyright 2001, 2005 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef CLIIF_included +#define CLIIF_included + +boolean_t cli_get_hex(char *entry, uint4 *dst); +boolean_t cli_get_int(char *entry, int4 *dst); +boolean_t cli_get_hex64(char *entry, gtm_uint64_t *dst); +boolean_t cli_get_uint64(char *entry, gtm_uint64_t *dst); +boolean_t cli_get_int64(char *entry, gtm_int64_t *dst); +boolean_t cli_get_num(char *entry, int4 *dst); +boolean_t cli_get_num64(char *entry, gtm_int64_t *dst); +boolean_t cli_get_str(char *entry, char *dst, unsigned short *max_len); +bool cli_get_str_ele(char *inbuff, char *dst, unsigned short *dst_len, boolean_t upper_case); +boolean_t cli_get_time(char *entry, uint4 *dst); +bool cli_get_value(char *entry, char val_buf[]); +boolean_t cli_negated(char *entry); +boolean_t cli_str_to_hex(char *str, uint4 *dst); +boolean_t cli_str_to_hex64(char *str, gtm_uint64_t *dst); +boolean_t cli_str_to_uint64(char *str, gtm_uint64_t *dst); +boolean_t cli_str_to_int(char *str, int4 *dst); +boolean_t cli_str_to_int64(char *str, gtm_int64_t *dst); +boolean_t cli_str_to_num(char *str, int4 *dst); +boolean_t cli_str_to_num64(char *str, gtm_int64_t *dst); +int4 cli_t_f_n(char *entry); +int4 cli_n_a_e(char *entry); +int cli_get_string_token(int *eof); +int cli_gettoken(int *eof); +int cli_is_assign(char *p); +int cli_is_dcm(char *p); +int cli_is_hex(char *p); +int cli_is_qualif(char *p); +int cli_look_next_string_token(int *eof); +int cli_look_next_token(int *eof); +int cli_present(char *entry); /***type int added***/ +void cli_str_setup(int length, char *addr); +void cli_strupper(char *sp); +int cli_parse_two_numbers(char *qual_name, const char delimiter, uint4 *first_num, uint4 *second_num); +#ifdef __osf__ + /* N.B. argv is passed in from main (in gtm.c) almost straight from the operating system. */ +#pragma pointer_size (save) +#pragma pointer_size (long) +#endif +void cli_lex_setup(int argc, char *argv[]); +#ifdef __osf__ +#pragma pointer_size (restore) +#endif + +#define CLI_2NUM_FIRST_SPECIFIED 0x2 +#define CLI_2NUM_SECOND_SPECIFIED 0x1 + +#endif /* CLIIF_included */ diff --git a/sr_port/cmd.c b/sr_port/cmd.c new file mode 100644 index 0000000..94f9a0a --- /dev/null +++ b/sr_port/cmd.c @@ -0,0 +1,294 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "mdq.h" +#include "opcode.h" +#include "toktyp.h" +#include "nametabtyp.h" +#include "mmemory.h" +#include "advancewindow.h" +#include "cmd.h" +#include "namelook.h" + +#define VMS_OS 01 +#define UNIX_OS 02 +#define ALL_SYS (VMS_OS | UNIX_OS) +#ifdef UNIX /* command validation is a function of the OS */ +# define VALID_CMD(i) (cmd_data[i].os_syst & UNIX_OS) +# ifdef __hppa +# define TRIGGER_OS 0 +# else +# define TRIGGER_OS UNIX_OS +# endif +#elif defined VMS +# define VALID_CMD(i) (cmd_data[i].os_syst & VMS_OS) +# define TRIGGER_OS 0 +#else +# error UNSUPPORTED PLATFORM +#endif + +GBLREF char window_token; +GBLREF mident window_ident; +GBLREF triple *curtchain, *curr_fetch_trip; + +int cmd(void) +{ /* All the commands are listed here. Two pairs of entries in general. + * One for full command and one for short-hand notation. + * For example, B and and BREAK. + */ +LITDEF nametabent cmd_names[] = + { + {1, "B"}, {5, "BREAK"} + ,{1, "C"}, {5, "CLOSE"} + ,{1, "D"}, {2, "DO"} + ,{1, "E"}, {4, "ELSE"} + ,{1, "F"}, {3, "FOR"} + ,{1, "G"}, {4, "GOTO"} + ,{1, "H"}, {4, "HALT"}, {4, "HANG"} + ,{1, "I"}, {2, "IF"} + ,{1, "J"}, {3, "JOB"} + ,{1, "K"}, {4, "KILL"} + ,{1, "L"}, {4, "LOCK"} + ,{1, "M"}, {5, "MERGE"} + ,{1, "N"}, {3, "NEW"} + ,{1, "O"}, {4, "OPEN"} + ,{1, "Q"}, {4, "QUIT"} + ,{1, "R"}, {4, "READ"} + ,{1, "S"}, {3, "SET"} + ,{2, "TC"}, {7, "TCOMMIT"} + ,{3, "TRE"}, {8, "TRESTART"} + ,{3, "TRO"}, {8, "TROLLBAC*"} + ,{2, "TS"}, {6, "TSTART"} + ,{1, "U"}, {3, "USE"} + ,{1, "V"}, {4, "VIEW"} + ,{1, "W"}, {5, "WRITE"} + ,{1, "X"}, {6, "XECUTE"} + ,{2, "ZA"}, {8, "ZALLOCAT*"} + ,{3, "ZAT"}, {7, "ZATTACH"} + ,{2, "ZB"}, {6, "ZBREAK"} + ,{2, "ZC"} + ,{4, "ZCOM"} + ,{8, "ZCONTINU*"} + ,{8, "ZCOMPILE"} + ,{2, "ZD"}, {8, "ZDEALLOC*"} + ,{3, "ZED"}, {5, "ZEDIT"} + ,{2, "ZG"}, {5, "ZGOTO"} + ,{2, "ZH"} + ,{5, "ZHALT"} + ,{5, "ZHELP"} + ,{2, "ZK"}, {5, "ZKILL"} + ,{2, "ZL"}, {5, "ZLINK"} + ,{2, "ZM"}, {8, "ZMESSAGE"} + ,{2, "ZP"}, {6, "ZPRINT"} + ,{3, "ZSH"}, {5, "ZSHOW"} + ,{3, "ZST"}, {5, "ZSTEP"} + ,{3, "ZSY"}, {7, "ZSYSTEM"} + ,{3, "ZTC"}, {8, "ZTCOMMIT"} +# ifdef GTM_TRIGGER + ,{3, "ZTR"}, {8, "ZTRIGGER"} +# endif + ,{3, "ZTS"}, {7, "ZTSTART"} + ,{3, "ZWA"}, {6, "ZWATCH"} + ,{3, "ZWI*"} + ,{3, "ZWR"}, {6, "ZWRITE"} + }; + /* + * cmd_index is an array indexed by the first alphabet of the command-name + * cmd_index[0] and cmd_index[1] elements are for a command beginning with 'A'. + * cmd_index[25] and cmd_index[26] element for a command beginning with 'Z'. + * The cmd_index[n] holds the index of the first element in cmd_names + * whose command-name begins with the same 'n'th letter of the alphabet (A is 1st letter). + * Example: + * Say, [B]REAK is the command. + * 'B'-'A' = 1. and cmd_index[1] = 0 and cmd_index[1+1]=2. So it is in cmd_names[1] and cmd_names[2] + * Say, [C]LOSE is the command. + * 'C'-'A' = 2. and cmd_index[2] = 2 and cmd_index[2+1]=4. So it is in cmd_names[2] and cmd_names[4] + * Say, [D]O is the command. + * 'D'-'A' = 3. and cmd_index[3] = 4 and cmd_index[3+1]=6. So it is in cmd_names[4] and cmd_names[6] + * Say, [I]F is the command. + * 'I'-'A' = 8. and cmd_index[8] = 15 and cmd_index[8+1]=17. So it is in cmd_names[15] and cmd_names[17] + * Say, [M]ERGE the command. + * 'M'-'A' = 12. and cmd_index[12] = 23 and cmd_index[12+1]=25. So it is in cmd_names[23] and cmd_names[25] + */ +LITDEF unsigned char cmd_index[27] = + { + 0, 0, 2, 4, 6, 8, 10, 12, 15, 17, 19, 21, 23 + ,25, 27, 29, 29, 31, 33, 35, 43, 45, 47, 49 + ,51, 51, GTMTRIG_ONLY(95) NON_GTMTRIG_ONLY(93) + }; +LITDEF struct + { + int (*fcn)(); + unsigned int eol_ok:1; + unsigned int pcnd_ok:1; + char os_syst; + } cmd_data[] = + { + {m_break, 1, 1, ALL_SYS}, {m_break, 1, 1, ALL_SYS} + ,{m_close, 0, 1, ALL_SYS}, {m_close, 0, 1, ALL_SYS} + ,{m_do, 1, 1, ALL_SYS}, {m_do, 1, 1, ALL_SYS} + ,{m_else, 1, 0, ALL_SYS}, {m_else, 1, 0, ALL_SYS} + ,{m_for, 0, 0, ALL_SYS}, {m_for, 0, 0, ALL_SYS} + ,{m_goto, 0, 1, ALL_SYS}, {m_goto, 0, 1, ALL_SYS} + ,{m_hcmd, 1, 1, ALL_SYS} + ,{m_halt, 1, 1, ALL_SYS} + ,{m_hang, 0, 1, ALL_SYS} + ,{m_if, 1, 0, ALL_SYS}, {m_if, 1, 0, ALL_SYS} + ,{m_job, 0, 1, ALL_SYS}, {m_job, 0, 1, ALL_SYS} + ,{m_kill, 1, 1, ALL_SYS}, {m_kill, 1, 1, ALL_SYS} + ,{m_lock, 1, 1, ALL_SYS}, {m_lock, 1, 1, ALL_SYS} + ,{m_merge, 0, 1, ALL_SYS}, {m_merge, 0, 1, ALL_SYS} + ,{m_new, 1, 1, ALL_SYS}, {m_new, 1, 1, ALL_SYS} + ,{m_open, 0, 1, ALL_SYS}, {m_open, 0, 1, ALL_SYS} + ,{m_quit, 1, 1, ALL_SYS}, {m_quit, 1, 1, ALL_SYS} + ,{m_read, 0, 1, ALL_SYS}, {m_read, 0, 1, ALL_SYS} + ,{m_set, 0, 1, ALL_SYS}, {m_set, 0, 1, ALL_SYS} + ,{m_tcommit, 1, 1, ALL_SYS}, {m_tcommit, 1, 1, ALL_SYS} + ,{m_trestart, 1, 1, ALL_SYS}, {m_trestart, 1, 1, ALL_SYS} + ,{m_trollback, 1, 1, ALL_SYS}, {m_trollback, 1, 1, ALL_SYS} + ,{m_tstart, 1, 1, ALL_SYS}, {m_tstart, 1, 1, ALL_SYS} + ,{m_use, 0, 1, ALL_SYS}, {m_use, 0, 1, ALL_SYS} + ,{m_view, 0, 1, ALL_SYS}, {m_view, 0, 1, ALL_SYS} + ,{m_write, 0, 1, ALL_SYS}, {m_write, 0, 1, ALL_SYS} + ,{m_xecute, 0, 1, ALL_SYS}, {m_xecute, 0, 1, ALL_SYS} + ,{m_zallocate, 0, 1, ALL_SYS}, {m_zallocate, 0, 1, ALL_SYS} + ,{m_zattach, 1, 1, ALL_SYS}, {m_zattach, 1, 1, ALL_SYS} + ,{m_zbreak, 0, 1, ALL_SYS}, {m_zbreak, 0, 1, ALL_SYS} + ,{m_zcontinue, 1, 1, ALL_SYS} + ,{m_zcompile, 0, 1, ALL_SYS} + ,{m_zcontinue, 1, 1, ALL_SYS} + ,{m_zcompile, 0, 1, ALL_SYS} + ,{m_zdeallocate, 1, 1, ALL_SYS}, {m_zdeallocate, 1, 1, ALL_SYS} + ,{m_zedit, 1, 1, ALL_SYS}, {m_zedit, 1, 1, ALL_SYS} + ,{m_zgoto, 1, 1, ALL_SYS}, {m_zgoto, 1, 1, ALL_SYS} + ,{m_zhelp, 1, 1, ALL_SYS} + ,{m_zhalt, 1, 1, ALL_SYS} + ,{m_zhelp, 1, 1, ALL_SYS} + ,{m_zwithdraw, 0, 1, ALL_SYS}, {m_zwithdraw, 0, 1, ALL_SYS} + ,{m_zlink, 1, 1, ALL_SYS}, {m_zlink, 1, 1, ALL_SYS} + ,{m_zmessage, 0, 1, ALL_SYS}, {m_zmessage, 0, 1, ALL_SYS} + ,{m_zprint, 1, 1, ALL_SYS}, {m_zprint, 1, 1, ALL_SYS} + ,{m_zshow, 1, 1, ALL_SYS}, {m_zshow, 1, 1, ALL_SYS} + ,{m_zstep, 1, 1, ALL_SYS}, {m_zstep, 1, 1, ALL_SYS} + ,{m_zsystem, 1, 1, ALL_SYS}, {m_zsystem, 1, 1, ALL_SYS} + ,{m_ztcommit, 1, 1, ALL_SYS}, {m_ztcommit, 1, 1, ALL_SYS} +# ifdef GTM_TRIGGER + ,{m_ztrigger, 0, 1, TRIGGER_OS}, {m_ztrigger, 0, 1, TRIGGER_OS} +# endif + ,{m_ztstart, 1, 1, ALL_SYS}, {m_ztstart, 1, 1, ALL_SYS} + ,{m_zwatch, 0, 1, 0}, {m_zwatch, 0, 1, 0} + ,{m_zwithdraw, 0, 1, ALL_SYS} + ,{m_zwrite, 1, 1, ALL_SYS}, {m_zwrite, 1, 1, ALL_SYS} + }; + + triple *temp_expr_start, *ref0, *ref1, *fetch0, *triptr; + char *c; + int x; + oprtype *cr; + boolean_t rval, shifting; + error_def(ERR_CMD); + error_def(ERR_INVCMD); + error_def(ERR_PCONDEXPECTED); + error_def(ERR_SPOREOL); + error_def(ERR_EXPR); + error_def(ERR_CNOTONSYS); + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + assert(cmd_index[26] == (SIZEOF(cmd_names)/SIZEOF(nametabent))); + CHKTCHAIN(curtchain); + TREF(pos_in_chain) = *curtchain; + if (window_token != TK_IDENT) + { + stx_error(ERR_CMD); + return FALSE; + } + assert(0 != window_ident.len); + c = window_ident.addr; + if (*c == '%') + { + stx_error(ERR_CMD); + return FALSE; + } + if ((x = namelook(cmd_index, cmd_names, c, window_ident.len)) < 0) + { + stx_error(ERR_INVCMD); + return FALSE; + } + if (!VALID_CMD(x) ) + { + stx_error(ERR_CNOTONSYS); + return FALSE; + } + advancewindow(); + if (window_token != TK_COLON || !cmd_data[x].pcnd_ok) + cr = NULL; + else + { + advancewindow(); + cr = (oprtype *)mcalloc(SIZEOF(oprtype)); + if (!bool_expr((bool) FALSE,cr)) + { + stx_error(ERR_PCONDEXPECTED); + return FALSE; + } + if (shifting = (TREF(expr_start) != TREF(expr_start_orig))) /* WARNING - assignent */ + { + temp_expr_start = TREF(expr_start); + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(temp_expr_start); + } + } + if (window_token == TK_SPACE) + advancewindow(); + else if (window_token != TK_EOL || !cmd_data[x].eol_ok) + { + stx_error(ERR_SPOREOL); + return FALSE; + } + fetch0 = curr_fetch_trip; + for (;;) + { + rval = (*cmd_data[x].fcn)(); + if (!rval || window_token != TK_COMMA) + break; + else + { advancewindow(); + if (window_token == TK_SPACE || window_token == TK_EOL) + { + stx_error(ERR_EXPR); + return FALSE; + } + } + } + if (rval && cr) + { + if (fetch0 != curr_fetch_trip) + { + assert (curr_fetch_trip->opcode == OC_FETCH); + *cr = put_tjmp(curtchain->exorder.bl); + } else + { + if (shifting) + { + ref0 = newtriple(OC_JMP); + ref1 = newtriple(OC_GVRECTARG); + ref1->operand[0] = put_tref(temp_expr_start); + *cr = put_tjmp(ref1); + tnxtarg(&ref0->operand[0]); + } else + tnxtarg(cr); + } + } + return rval; +} diff --git a/sr_port/cmd.h b/sr_port/cmd.h new file mode 100644 index 0000000..ae25274 --- /dev/null +++ b/sr_port/cmd.h @@ -0,0 +1,69 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef CMD_INCLUDED +#define CMD_INCLUDED + +int cmd(void); +int m_break(void); +int m_close(void); +int m_do(void); +int m_else(void); +int m_xecute(void); +int m_for(void); +int m_goto(void); +int m_halt(void); +int m_hang(void); +int m_hcmd(void); +int m_if(void); +int m_job(void); +int m_kill(void); +int m_lock(void); +int m_merge(void); +int m_new(void); +int m_open(void); +int m_quit(void); +int m_read(void); +int m_set(void); +int m_tcommit(void); +int m_trestart(void); +int m_trollback(void); +int m_tstart(void); +int m_use(void); +int m_view(void); +int m_write(void); +int m_xecute(void); +int m_zallocate(void); +int m_zattach(void); +int m_zbreak(void); +int m_zcompile(void); +int m_zcontinue(void); +int m_zdeallocate(void); +int m_zedit(void); +int m_zgoto(void); +int m_zhalt(void); +int m_zhelp(void); +int m_zlink(void); +int m_zmessage(void); +int m_zprint(void); +int m_zshow(void); +int m_zstep(void); +int m_zsystem(void); +int m_ztcommit(void); +#ifdef GTM_TRIGGER +int m_ztrigger(void); +#endif +int m_ztstart(void); +int m_zwatch(void); +int m_zwithdraw(void); +int m_zwrite(void); + +#endif diff --git a/sr_port/cmd_qlf.h b/sr_port/cmd_qlf.h new file mode 100644 index 0000000..7203c9c --- /dev/null +++ b/sr_port/cmd_qlf.h @@ -0,0 +1,65 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#ifndef CMD_QLF_H_INCLUDED +#define CMD_QLF_H_INCLUDED + +typedef struct +{ + uint4 qlf; + mval object_file; + mval list_file; + mval ceprep_file; + mval rtnname; +} command_qualifier; + +typedef struct +{ unsigned short page; /* page number */ + unsigned short list_line; /* listing line number */ + unsigned short lines_per_page; + unsigned short space; /* spacing */ +} list_params; + +/* command qualifer bit masks */ +#define CQ_LIST (1 << 0) /* 0x0001 */ +#define CQ_MACHINE_CODE (1 << 1) /* 0x0002 */ +#define CQ_CROSS_REFERENCE (1 << 2) /* 0x0004 */ +#define CQ_DEBUG (1 << 3) /* 0x0008 */ +#define CQ_OBJECT (1 << 4) /* 0x0010 */ +#define CQ_WARNINGS (1 << 5) /* 0x0020 */ +#define CQ_IGNORE (1 << 6) /* 0x0040 */ +#define CQ_LOWER_LABELS (1 << 7) /* 0x0080 */ +#define CQ_LINE_ENTRY (1 << 8) /* 0x0100 */ +#define CQ_CE_PREPROCESS (1 << 9) /* 0x0200 */ +#define CQ_INLINE_LITERALS (1 << 10) /* 0x0400 */ +#define CQ_ALIGN_STRINGS (1 << 11) /* 0x0800 */ +#define CQ_UTF8 (1 << 12) /* 0x1000 */ +#define CQ_NAMEOFRTN (1 << 13) /* 0x2000 */ + +/* TODO: add CQ_ALIGN_STRINGS to the default list below when alignment is supported */ +#define CQ_DEFAULT (CQ_WARNINGS | CQ_OBJECT | CQ_IGNORE | CQ_LOWER_LABELS | CQ_LINE_ENTRY | CQ_INLINE_LITERALS) + +#define LISTTAB 10 +#define PG_WID 132 + +typedef struct src_line_type +{ + struct + { + struct src_line_type *fl,*bl; + } que; + char *addr; + int4 line; +} src_line_struct; + +void zl_cmd_qlf(mstr *quals, command_qualifier *qualif); +void get_cmd_qlf(command_qualifier *qualif); + +#endif /* CMD_QLF_H_INCLUDED */ diff --git a/sr_port/cmerrors.msg b/sr_port/cmerrors.msg new file mode 100644 index 0000000..2647605 --- /dev/null +++ b/sr_port/cmerrors.msg @@ -0,0 +1,19 @@ +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! ! +! Copyright 2001 Sanchez Computer Associates, Inc. ! +! ! +! This source code contains the intellectual property ! +! of its copyright holder(s), and is made available ! +! under a license. If you do not know the terms of ! +! the license, please stop and do not read further. ! +! ! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + .FACILITY GTCM,249/PREFIX=CMERR_ + .TITLE CMERRORS Error Messages for GTCM, VAX/VMS EDITION +INVPROT /error/fao=0 +REGNTFND /error/fao=0 +CMINTQUE /fatal/fao=0 +INVINTMSG /error/fao=0 +CMEXCDASTLM /error/fao=0 +CMSYSSRV /error/fao=0 + .end diff --git a/sr_port/cmi.h b/sr_port/cmi.h new file mode 100644 index 0000000..bd52376 --- /dev/null +++ b/sr_port/cmi.h @@ -0,0 +1,18 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef CMI_INCLUDED +#define CMI__INCLUDED + +cmi_status_t cmi_read(struct CLB *lnk); +cmi_status_t cmi_write(struct CLB *lnk); + +#endif /* CMI_INCLUDED */ diff --git a/sr_port/cmidef.h b/sr_port/cmidef.h new file mode 100644 index 0000000..4d4124a --- /dev/null +++ b/sr_port/cmidef.h @@ -0,0 +1,45 @@ +/**************************************************************** + * * + * Copyright 2001, 2004 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef CMIPORT_H_INCLUDED +#define CMIPORT_H_INCLUDED + +#define CM_MSG_BUF_SIZE 512 /* Message buffer size */ +#define CM_MAX_BUF_LEN ((unsigned short)0xFFFF) /* lnk->cbl is of type unsigned short, hence 64K-1 is the max currently */ + +/* + * Connection States + */ +#define CM_CLB_IDLE 0 +#define CM_CLB_READ 1 +#define CM_CLB_WRITE 2 +#define CM_CLB_CONNECT 3 +#define CM_CLB_DISCONNECT 4 +#define CM_CLB_WRITE_URG 5 +#define CM_CLB_READ_URG 6 + +/* get platform specific stuff */ +#include "cmidefsp.h" + +cmi_status_t cmi_read(struct CLB *c); +cmi_status_t cmi_write(struct CLB *c); +cmi_status_t cmi_open(struct CLB *c); +cmi_status_t cmi_close(struct CLB *c); +struct CLB *cmu_getclb(cmi_descriptor *node, cmi_descriptor *task); +struct NTD *cmu_ntdroot(void); + +#ifndef RELQUE2PTR +#define RELQUE2PTR(X) (((unsigned char *) &(X)) + ((int4) (X))) +#endif +#define PTR2RELQUE(DESTINATION,TARGET) (DESTINATION = (((unsigned char *) &(TARGET)) - ((unsigned char *) &(DESTINATION)))) +#define QUEENT2CLB(QP, QH) (struct CLB *)((char *)(QP) - (char *)&(((struct CLB *)(0))->QH)) + +#endif /* CMI_INCLUDED */ diff --git a/sr_port/cmmdef.h b/sr_port/cmmdef.h new file mode 100644 index 0000000..aec77ff --- /dev/null +++ b/sr_port/cmmdef.h @@ -0,0 +1,355 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#define ZAREQUEST_SENT 16 +#define LREQUEST_SENT 8 +#define REQUEST_PENDING 4 +#define REMOTE_ZALLOCATES 2 +#define REMOTE_LOCKS 1 +#define REMOTE_CLR_MASK (ZAREQUEST_SENT + LREQUEST_SENT + REMOTE_ZALLOCATES + REMOTE_LOCKS) +#define CM_BUFFER_OVERHEAD 20 +#define CM_BLKPASS 40 + +#define CMM_PROTOCOL_TYPE "GCM" + +#define S_PROTOCOL "VAXVMSGTM023GCM010 " +#define S_HDRSIZE 1 +#define S_PROTSIZE 33 +#define S_REGINSIZE 6 +#define S_LAFLAGSIZE 1 +#define S_SUBLISTSIZE 1 +#define CM_MINBUFSIZE 512 + CM_BUFFER_OVERHEAD + +#define CMLCK_REQUEUE 0 +#define CM_LOCKS 0 +#define CM_ZALLOCATES 0x80 +#define CM_NOLKCANCEL 256 + +#define CM_WRITE 1 +#define CM_READ 0 +#define CM_NOOP 2 + +#define CMMS_E_ERROR 1 /* [0x01] */ +#define CMMS_L_LKCANALL 2 /* [0x02] */ +#define CMMS_L_LKCANCEL 3 /* [0x03] */ +#define CMMS_L_LKDELETE 4 /* [0x04] */ +#define CMMS_L_LKREQIMMED 5 /* [0x05] */ +#define CMMS_L_LKREQNODE 6 /* [0x06] */ +#define CMMS_L_LKREQUEST 7 /* [0x07] */ +#define CMMS_L_LKRESUME 8 /* [0x08] */ +#define CMMS_L_LKACQUIRE 9 /* [0x09] */ +#define CMMS_L_LKSUSPEND 10 /* [0x0A] */ +#define CMMS_M_LKABORT 11 /* [0x0B] */ +#define CMMS_M_LKBLOCKED 12 /* [0x0C] */ +#define CMMS_M_LKGRANTED 13 /* [0x0D] */ +#define CMMS_M_LKDELETED 14 /* [0x0E] */ +#define CMMS_M_LKSUSPENDED 15 /* [0x0F] */ +#define CMMS_Q_DATA 16 /* [0x10] */ +#define CMMS_Q_GET 17 /* [0x11] */ +#define CMMS_Q_KILL 18 /* [0x12] */ +#define CMMS_Q_ORDER 19 /* [0x13] */ +#define CMMS_Q_PREV 20 /* [0x14] */ +#define CMMS_Q_PUT 21 /* [0x15] */ +#define CMMS_Q_QUERY 22 /* [0x16] */ +#define CMMS_Q_ZWITHDRAW 23 /* [0x17] */ +#define CMMS_R_DATA 24 /* [0x18] */ +#define CMMS_R_GET 25 /* [0x19] */ +#define CMMS_R_KILL 26 /* [0x1A] */ +#define CMMS_R_ORDER 27 /* [0x1B] */ +#define CMMS_R_PREV 28 /* [0x1C] */ +#define CMMS_R_PUT 29 /* [0x1D] */ +#define CMMS_R_QUERY 30 /* [0x1E] */ +#define CMMS_R_ZWITHDRAW 31 /* [0x1F] */ +#define CMMS_R_UNDEF 32 /* [0x20] */ +#define CMMS_S_INITPROC 33 /* [0x21] */ +#define CMMS_S_INITREG 34 /* [0x22] */ +#define CMMS_S_TERMINATE 35 /* [0x23] */ +#define CMMS_S_INTERRUPT 36 /* [0x24] */ +#define CMMS_T_INITPROC 37 /* [0x25] */ +#define CMMS_T_REGNUM 38 /* [0x26] */ +#define CMMS_X_INQPROC 39 /* [0x27] */ +#define CMMS_X_INQPRRG 40 /* [0x28] */ +#define CMMS_X_INQREG 41 /* [0x29] */ +#define CMMS_Y_STATPROCREC 42 /* [0x2A] */ +#define CMMS_Y_STATPRRGREC 43 /* [0x2B] */ +#define CMMS_Y_STATREGREC 44 /* [0x2C] */ +#define CMMS_U_LKEDELETE 45 /* [0x2D] */ +#define CMMS_U_LKESHOW 46 /* [0x2E] */ +#define CMMS_V_LKESHOW 47 /* [0x2F] */ +#define CMMS_E_TERMINATE 48 /* [0x30] */ +#define CMMS_B_BUFRESIZE 49 /* [0x31] */ +#define CMMS_B_BUFFLUSH 50 /* [0x32] */ +#define CMMS_C_BUFRESIZE 51 /* [0x33] */ +#define CMMS_C_BUFFLUSH 52 /* [0x34] */ +#define CMMS_Q_INCREMENT 53 /* [0x35] */ /* Opcode for message type sent from client (to server) */ +#define CMMS_R_INCREMENT 54 /* [0x36] */ /* Opcode for message type received by client (from server) */ + +#define CMM_QUERYGET_MIN_LEVEL "200" /* $query works as queryget only from version "V200" onwards */ +#define CMM_INCREMENT_MIN_LEVEL "210" /* $INCREMENT works only from version "V210" onwards */ +#define CMM_STDNULLCOLL_MIN_LEVEL "210" /* Standard null collation works only from version "V210" onwards */ +#define CMM_LONGNAMES_MIN_LEVEL "210" /* long name works only from protocol "V210" onwards */ + +typedef struct cm_region_list_struct + { + que_ent regque; + struct cm_region_list_struct *next; + unsigned char regnum; + unsigned char oper; + unsigned short lks_this_cmd; + bool reqnode; + char filler[3]; + struct cm_region_head_struct *reghead; + struct cs_struct *cs; + struct mlk_pvtblk_struct *blkd; + struct mlk_pvtblk_struct *lockdata; + uint4 pini_addr; + } cm_region_list; + +typedef struct cs_struct + { + que_ent qent; + cm_region_list *region_root; + cm_region_list *current_region; + struct CLB *clb_ptr; + unsigned char state; + unsigned char new_msg; + unsigned char maxregnum; + bool waiting_in_queue; +#ifdef UNIX + struct timeval connect; /* Debugging tool -- time connection was established */ + time_t lastact; /* Debugging tool -- time of last server action */ +#else + uint4 connect[2]; /* Debugging tool -- time connection was established */ + uint4 lastact[2]; /* Debugging tool -- time of last server action */ +#endif + uint4 stats; + unsigned short procnum; + unsigned short transnum; + unsigned short lk_cancel; + unsigned short last_cancelled; /* hold transnum of last cancelled lock request */ + struct /* hold info from interrupt cancel msg */ + { /* laflag can be 0, x40, x80 */ + unsigned char laflag; /* + 1 if valid */ + unsigned char transnum; /* for lk_cancel */ + } int_cancel; + struct jnl_process_vector_struct *pvec; + boolean_t query_is_queryget; /* based on client/server protocol levels, query == queryget */ + boolean_t err_compat; /* based on client/server protocol levels (and platform type), + * rts_error mechanism b/n client and server might be different */ + boolean_t cli_supp_allowexisting_stdnullcoll;/* decided based on client's protocol levels */ + boolean_t client_supports_long_names; /* based on client's levels */ + cm_region_list *region_array[256]; /* [UCHAR_MAX + 1] speed up gtcm_find_region */ + } connection_struct; + +typedef struct cm_region_head_struct + { + relque head; + struct cm_region_head_struct *next; + struct cm_region_head_struct *last; + connection_struct *connect_ptr; + struct gd_region_struct *reg; + uint4 refcnt; + uint4 wakeup; + hash_table_mname *reg_hash; + } cm_region_head; + +typedef struct cm_lk_response_struct + { + struct cm_lk_response_struct *next; + struct CLB *response; + } cm_lk_response; + +typedef struct link_info_struct + { + unsigned char neterr; + unsigned char lck_info; + unsigned char lnk_active; + char filler; + struct mlk_pvtblk_struct *netlocks; + unsigned short procnum; + unsigned short buffered_count; + unsigned short buffer_size; + unsigned short buffer_used; + unsigned char *buffer; + boolean_t convert_byteorder; + boolean_t query_is_queryget; /* based on client/server protocol levels, query == queryget */ + boolean_t err_compat; /* based on client/server protocol levels (and platform type), + * rts_error mechanism b/n client and server might be different */ + cm_lk_response lk_response; + boolean_t server_supports_dollar_incr; /* decided based on server protocol levels */ + boolean_t server_supports_std_null_coll; /* decided based on server protocol levels */ + boolean_t server_supports_long_names; /* decided based on server protocol levels */ + } link_info; + +typedef struct + { + char code; + char rnum; + bool all; + bool interactive; + int4 pid; + char nodelength; + char node[32]; + } clear_request; + +typedef struct + { + char code; + char filler[3]; + int4 status; + int4 locknamelength; + char lockname[256]; + } clear_reply; + +typedef struct + { + char code; + bool clear; + } clear_confirm; + +typedef struct + { + char code; + char rnum; + bool all; + bool wait; + int4 pid; + char nodelength; + char node[32]; + } show_request; + +typedef struct + { + char code; + char line[256]; + } show_reply; + +#define CM_CPU_OFFSET 0 +#define CM_OS_OFFSET 3 +#define CM_IMPLEMENTATION_OFFSET 6 +#define CM_VERSION_OFFSET 9 +#define CM_TYPE_OFFSET 12 +#define CM_LEVEL_OFFSET 15 +#define CM_ENDIAN_OFFSET 18 + +#define CM_FILLER_SIZE 14 + +typedef struct + { + char msg[S_PROTSIZE]; + } protocol_msg; + +#define CM_PUT_USHORT(PTR, USVAL, CONVFLAG) \ + { \ + if (CONVFLAG) \ + { \ + unsigned short val = GTM_BYTESWAP_16(USVAL); \ + PUT_USHORT(PTR, val); \ + } \ + else \ + PUT_USHORT(PTR, USVAL); \ + } + +#define CM_PUT_SHORT(PTR, SVAL, CONVFLAG) \ + { \ + if (CONVFLAG) \ + { \ + short val = GTM_BYTESWAP_16(SVAL); \ + PUT_SHORT(PTR, val); \ + } \ + else \ + PUT_SHORT(PTR, SVAL); \ + } + +#define CM_PUT_ULONG(PTR, ULVAL, CONVFLAG) \ + { \ + if (CONVFLAG) \ + { \ + uint4 val = GTM_BYTESWAP_32(ULVAL); \ + PUT_ULONG(PTR, val); \ + } \ + else \ + PUT_ULONG(PTR, ULVAL); \ + } + +#define CM_PUT_LONG(PTR, LVAL, CONVFLAG) \ + { \ + if (CONVFLAG) \ + { \ + int4 val = GTM_BYTESWAP_32(LVAL); \ + PUT_LONG(PTR, val); \ + } \ + else \ + PUT_LONG(PTR, LVAL); \ + } + +#define CM_GET_USHORT(USVAR, PTR, CONVFLAG) \ + { \ + if (CONVFLAG) \ + { \ + unsigned short val; \ + GET_USHORT(val, (PTR)); \ + USVAR = GTM_BYTESWAP_16(val); \ + } \ + else \ + GET_USHORT((USVAR), (PTR)); \ + } + +#define CM_GET_SHORT(SVAR, PTR, CONVFLAG) \ + { \ + if (CONVFLAG) \ + { \ + short val; \ + GET_SHORT(val, (PTR)); \ + SVAR = GTM_BYTESWAP_16(val); \ + } \ + else \ + GET_SHORT((SVAR), (PTR)); \ + } + +#define CM_GET_ULONG(ULVAR, PTR, CONVFLAG) \ + { \ + if (CONVFLAG) \ + { \ + uint4 val; \ + GET_ULONG(val, (PTR)); \ + ULVAR = GTM_BYTESWAP_32(val); \ + } \ + else \ + GET_ULONG((ULVAR), (PTR)); \ + } + +#define CM_GET_LONG(LVAR, PTR, CONVFLAG) \ + { \ + if (CONVFLAG) \ + { \ + int4 val; \ + GET_LONG(val, (PTR)); \ + LVAR = GTM_BYTESWAP_32(val); \ + } \ + else \ + GET_LONG((LVAR), (PTR)); \ + } + +#define CM_GET_GVCURRKEY(PTR, LEN) \ + /* fetch gvcurrkey fields from message buffer; side effect : PTR is modified \ + * to point to the byte after gv_currkey */ \ + /* if we want to keep gv_currkey->top, why bother changing it; vinu Jul 17, 2000 */ \ + /* top = gv_currkey->top; */ \ + /* GET_USHORT(gv_currkey->top, ptr); */ \ + (PTR) += SIZEOF(unsigned short); \ + GET_USHORT(gv_currkey->end, (PTR)); \ + (PTR) += SIZEOF(unsigned short); \ + GET_USHORT(gv_currkey->prev, (PTR)); \ + (PTR) += SIZEOF(unsigned short); \ + memcpy(gv_currkey->base, (PTR), (LEN) - 6); \ + (PTR) += ((LEN) - 6); \ + /* gv_currkey->top = top; */ diff --git a/sr_port/code_address_type.h b/sr_port/code_address_type.h new file mode 100644 index 0000000..8ca6775 --- /dev/null +++ b/sr_port/code_address_type.h @@ -0,0 +1,30 @@ +/**************************************************************** + * * + * Copyright 2007, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef CODE_ADDRESS_TYPE_INCLUDE +#define CODE_ADDRESS_TYPE_INCLUDE + +#include "mdef.h" + +#ifdef __ia64 +GBLREF int function_type(char*); +#endif /* __ia64 */ + +#define GTM_C_RTN 1 +#define GTM_ASM_RTN 2 + +#ifndef __ia64 +#define CODE_ADDRESS_TYPE(func) &func +#else /* __ia64 */ +#define CODE_ADDRESS_TYPE(func) CODE_ADDRESS(func) +#endif /* __ia64 */ + +#endif /* CODE_ADDRESS_TYPE_INCLUDE */ diff --git a/sr_port/code_gen.c b/sr_port/code_gen.c new file mode 100644 index 0000000..68a7c49 --- /dev/null +++ b/sr_port/code_gen.c @@ -0,0 +1,97 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "mdq.h" +#include "cgp.h" +#include "cmd_qlf.h" +#include "rtnhdr.h" +#include "obj_file.h" +#include "list_file.h" +#include "emit_code.h" +#include "dumptable.h" + +GBLDEF int4 codegen_padlen; /* pad code to section alignment */ + +LITREF octabstruct oc_tab[]; /* op-code table */ +GBLREF triple t_orig; /* head of triples */ +GBLREF char cg_phase; /* code generation phase */ +GBLREF int4 curr_addr; /* current address */ +GBLREF src_line_struct src_head; +GBLREF short source_column,source_line; +GBLREF int4 pending_errtriplecode; /* if non-zero contains the error code to invoke ins_errtriple with */ +GBLREF triple *curtchain; + +void code_gen(void) +{ + int4 old_line, pad_len; + triple *ct; /* current triple */ + src_line_struct *sl; + + if (cg_phase == CGP_ASSEMBLY) + { + curr_addr = t_orig.exorder.fl->rtaddr; + old_line = -1; + } + assert(0 == pending_errtriplecode); /* we should never have a pending ins_errtriple at this point */ + assert(curtchain == &t_orig); /* curtchain should still be pointing to what it was originally */ + DEBUG_ONLY(chktchain(&t_orig)); /* if this assert fails, then recompile with DEBUG_TRIPLES to catch the issue sooner */ + dqloop(&t_orig, exorder, ct) + { + if (cg_phase == CGP_APPROX_ADDR) + ct->rtaddr = curr_addr; + else if (cg_phase == CGP_ASSEMBLY) + { + if (ct->src.line != old_line) + { + list_line(""); + for (sl = src_head.que.bl; sl->line <= ct->src.line && sl != &src_head; ) + { + list_line_number(); + dqdel(sl,que); + list_line(sl->addr); + sl = src_head.que.bl; + } + old_line = ct->src.line; + } + } + source_line = ct->src.line; + source_column = ct->src.column; + + if (!(oc_tab[ct->opcode].octype & OCT_CGSKIP)) + trip_gen(ct); + }/* dqloop */ + +#ifdef _AIX + emit_epilog(); +#endif + + /* The code section needs to be padded so the next section (variable name table) can be optimally aligned + for use by the hashing functions (ex use 8 byte loads on alpha requires 8 byte alignment). We compute the + pad length and record it and add it to the code size. Later when the code is optimized, the pad length + will be subtracted back out, rechecked for padding and an appropriate pad length recomputed. + */ + if (CGP_APPROX_ADDR == cg_phase) + codegen_padlen = PADLEN(curr_addr, SECTION_ALIGN_BOUNDARY); /* Length to pad to align next section */ + if (codegen_padlen) + { + assert(STR_LIT_LEN(PADCHARS) >= codegen_padlen); + if (CGP_MACHINE == cg_phase) + emit_immed(PADCHARS, codegen_padlen); /* Pad out with extraneous info */ + else + curr_addr += codegen_padlen; + } + + if (cg_phase == CGP_ASSEMBLY) + dumptable(); +}/* code_gen */ diff --git a/sr_port/coerce.c b/sr_port/coerce.c new file mode 100644 index 0000000..95000cc --- /dev/null +++ b/sr_port/coerce.c @@ -0,0 +1,79 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "compiler.h" +#include "opcode.h" +#include "mdq.h" +#include "mvalconv.h" +#include "hashtab_str.h" + +GBLREF hash_table_str *complits_hashtab; + +LITREF octabstruct oc_tab[]; + +void coerce(oprtype *a,unsigned short new_type) +{ + + mliteral *lit; + opctype conv, old_op; + triple *ref, *coerc; + stringkey litkey; + ht_ent_str *litent; + boolean_t litdltd; + + assert (new_type == OCT_MVAL || new_type == OCT_MINT || new_type == OCT_BOOL); + assert (a->oprclass == TRIP_REF); + ref = a->oprval.tref; + old_op = ref->opcode; + if (new_type & oc_tab[old_op].octype) + return; + if (old_op == OC_COMVAL || old_op == OC_COMINT) + { + dqdel(ref,exorder); + ref = ref->operand[0].oprval.tref; + old_op = ref->opcode; + if (new_type & oc_tab[old_op].octype) + return; + } else if (OC_LIT == old_op && OCT_MINT == new_type) + { + lit = ref->operand[0].oprval.mlit; + if (!(++lit->rt_addr)) + { /* completely removing this otherwise unused literal as needs to be an ILIT instead */ + if (NULL != complits_hashtab && NULL != complits_hashtab->base) + { /* Deleted entry is in the hash table .. remove it */ + litkey.str = lit->v.str; + COMPUTE_HASH_STR(&litkey); + DEBUG_ONLY(litent = lookup_hashtab_str(complits_hashtab, &litkey)); + assert(litent); /* Literal is there .. better be found */ + assert(litent->value == (void *)lit); + litdltd = delete_hashtab_str(complits_hashtab, &litkey); + assert(litdltd); + } + dqdel(lit, que); + } + ref->opcode = OC_ILIT; + ref->operand[0].oprclass = ILIT_REF; + ref->operand[0].oprval.ilit = MV_FORCE_INTD(&(lit->v)); + return; + } + if (new_type == OCT_BOOL) + conv = OC_COBOOL; + else if (new_type == OCT_MINT) + conv = OC_COMINT; + else + conv = OC_COMVAL; + coerc = newtriple(conv); + coerc->operand[0] = put_tref(ref); + *a = put_tref(coerc); + return; +} diff --git a/sr_port/collseq.c b/sr_port/collseq.c new file mode 100644 index 0000000..297718a --- /dev/null +++ b/sr_port/collseq.c @@ -0,0 +1,70 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "io.h" +#include "iosp.h" +#include "collseq.h" +#include "error.h" +#include "trans_log_name.h" +#include "gtm_logicals.h" + +int find_local_colltype(void) +{ + int lct, status; + char transbuf[MAX_TRANS_NAME_LEN]; + mstr lognam, transnam; + + lognam.len = SIZEOF(LCT_PREFIX) - 1; + lognam.addr = LCT_PREFIX; + status = TRANS_LOG_NAME(&lognam, &transnam, transbuf, SIZEOF(transbuf), do_sendmsg_on_log2long); + if (SS_NORMAL != status) + return 0; + lct = asc2i((uchar_ptr_t)transnam.addr, transnam.len); + return lct >= MIN_COLLTYPE && lct <= MAX_COLLTYPE ? lct : 0; +} + +collseq *ready_collseq(int act) +{ + unsigned char filespec[SIZEOF(CT_PREFIX) + 4]; /* '4' to hold the chars in the max allowable + * collation sequence (255) plus the terminating null */ + unsigned char *fsp; + collseq temp_csp, *csp; + mstr fspec; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + /* Validate the alternative collating type (act) */ + if (!(act >= 1 && act <= MAX_COLLTYPE)) + return (collseq*)NULL; + /* Search for record of the collating type already being mapped in. */ + for (csp = TREF(collseq_list); csp != NULL && act != csp->act; csp = csp->flink) + ; + if (NULL == csp) + { + /* If not found, create a structure and attempt to map in the collating support package.*/ + temp_csp.act = act; + temp_csp.flink = TREF(collseq_list); + memcpy(filespec, CT_PREFIX, SIZEOF(CT_PREFIX)); + fsp = i2asc(&filespec[SIZEOF(CT_PREFIX) - 1], act); + *fsp = 0; + fspec.len = INTCAST(fsp - filespec); + fspec.addr = (char *)filespec; + if (!map_collseq(&fspec, &temp_csp)) + return NULL; + csp = (collseq *) malloc(SIZEOF(collseq)); + *csp = temp_csp; + TREF(collseq_list) = csp; + } + return csp; +} diff --git a/sr_port/collseq.h b/sr_port/collseq.h new file mode 100644 index 0000000..3a64d28 --- /dev/null +++ b/sr_port/collseq.h @@ -0,0 +1,101 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef COLLSEQ_H_INCLUDED +#define COLLSEQ_H_INCLUDED + +#include "min_max.h" + +#define MAX_COLLTYPE 255 +#define MIN_COLLTYPE 0 +#define XFORM 0 +#define XBACK 1 + +#define ALLOC_XFORM_BUFF(STR1LEN) \ +{ \ + mstr_len_t lcl_len; \ + \ + if (0 == TREF(max_lcl_coll_xform_bufsiz)) \ + { \ + assert(NULL == TREF(lcl_coll_xform_buff)); \ + TREF(max_lcl_coll_xform_bufsiz) = MAX_STRBUFF_INIT; \ + TREF(lcl_coll_xform_buff) = (char *)malloc(TREF(max_lcl_coll_xform_bufsiz)); \ + } \ + lcl_len = STR1LEN; \ + assert(MAX_STRLEN >= lcl_len); \ + if (lcl_len > TREF(max_lcl_coll_xform_bufsiz)) \ + { \ + assert(NULL != TREF(lcl_coll_xform_buff)); \ + free(TREF(lcl_coll_xform_buff)); \ + assert(MAX_STRLEN >= TREF(max_lcl_coll_xform_bufsiz)); \ + while (lcl_len > TREF(max_lcl_coll_xform_bufsiz)) \ + TREF(max_lcl_coll_xform_bufsiz) *= 2; \ + TREF(max_lcl_coll_xform_bufsiz) = MIN(MAX_STRLEN, TREF(max_lcl_coll_xform_bufsiz)); \ + TREF(lcl_coll_xform_buff) = (char *)malloc(TREF(max_lcl_coll_xform_bufsiz)); \ + } \ +} +/* + Following two macros are currently used in replication filters, merge command and binary load to transform + GTM null subscripts collation to standard null subscript collation and vice versa +*/ + +#define GTM2STDNULLCOLL(key, len) \ +{ \ + unsigned char *currptr, *ptrtop; \ + \ + currptr = (unsigned char *)(key); \ + ptrtop = (currptr + (len)); \ + assert(currptr < ptrtop); \ + do { \ + if ((STR_SUB_PREFIX == *currptr++) && (KEY_DELIMITER == *currptr)) \ + *(currptr - 1) = SUBSCRIPT_STDCOL_NULL; \ + assert(currptr <= ptrtop); \ + while ((currptr < ptrtop) && (KEY_DELIMITER != *currptr++)) \ + ; \ + } while ((currptr < ptrtop) && (KEY_DELIMITER != *currptr)); \ + assert(currptr <= ptrtop); \ +} + +#define STD2GTMNULLCOLL(key, len) \ +{ \ + unsigned char *currptr, *ptrtop; \ + \ + currptr = (unsigned char *)(key); \ + ptrtop = (currptr + (len)); \ + assert(currptr < ptrtop); \ + do { \ + if ((SUBSCRIPT_STDCOL_NULL == *currptr++) && (KEY_DELIMITER == *currptr)) \ + *(currptr - 1) = STR_SUB_PREFIX; \ + assert(currptr <= ptrtop); \ + while ((currptr < ptrtop) && (KEY_DELIMITER != *currptr++)) \ + ; \ + } while ((currptr < ptrtop) && (KEY_DELIMITER != *currptr)); \ + assert(currptr <= ptrtop); \ +} + + +typedef struct collseq_struct { + struct collseq_struct *flink; + int act; + int4 (*xform)(); + int4 (*xback)(); + int4 (*version)(); + int4 (*verify)(); + int argtype; +} collseq; + +boolean_t map_collseq(mstr *fspec, collseq *ret_collseq); +collseq *ready_collseq(int act); +int4 do_verify(collseq *csp, unsigned char type, unsigned char ver); +int find_local_colltype(void); +void act_in_gvt(void); + +#endif /* COLLSEQ_H_INCLUDED */ diff --git a/sr_port/comline.h b/sr_port/comline.h new file mode 100644 index 0000000..1509d55 --- /dev/null +++ b/sr_port/comline.h @@ -0,0 +1,14 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#define MAX_RECALL 99 +#define MAX_RECALL_NUMBER_LENGTH 2 /* i.e. maximum recallable strings 99 */ +#define clmod(x) ((x + MAX_RECALL) % MAX_RECALL) diff --git a/sr_port/comp_esc.h b/sr_port/comp_esc.h new file mode 100644 index 0000000..7782057 --- /dev/null +++ b/sr_port/comp_esc.h @@ -0,0 +1,25 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +struct ce_sentinel_desc +{ + char *escape_sentinel; + int4 escape_length; + int4 (*user_routine)(); + struct ce_sentinel_desc *next; +}; + +int ce_init(void); +void ce_substitute(struct ce_sentinel_desc *shp, int4 source_col, int4 *skip_ct); +void close_ceprep_file(void); +void open_ceprep_file(void); +void put_ceprep_line(void); + diff --git a/sr_port/comp_fini.c b/sr_port/comp_fini.c new file mode 100644 index 0000000..3c6861b --- /dev/null +++ b/sr_port/comp_fini.c @@ -0,0 +1,93 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "stringpool.h" +#include "rtnhdr.h" +#include "mv_stent.h" +#include "cgp.h" +#include "alloc_reg.h" +#include "advancewindow.h" +#include "hashtab_str.h" + +/* WARNING: comp_fini restores the currently-active stringpool from the + * indirection stringpool (indr_stringpool) to the runtime stringpool + * (rts_stringpool). It depends on comp_init having changed it from + * rts_stringpool to indr_stringpool during compilation setup. + */ +GBLREF spdesc stringpool, rts_stringpool, indr_stringpool; +GBLREF short int source_column; +GBLREF char cg_phase; +GBLREF unsigned char *source_buffer; +GBLREF char window_token; + +error_def(ERR_INDEXTRACHARS); + +int comp_fini(bool status, mstr *obj, opctype retcode, oprtype *retopr, mstr_len_t src_len) +{ + triple *ref; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + if (status) + { + while (TK_SPACE == window_token) /* Eat up trailing white space */ + advancewindow(); + if (source_column != src_len + 2 && source_buffer[source_column] != '\0') + { + status = FALSE; + stx_error(ERR_INDEXTRACHARS); + } else + { + cg_phase = CGP_RESOLVE; + assert(TREF(for_stack_ptr) == TADR(for_stack)); + if (*TREF(for_stack_ptr)) + tnxtarg(*TREF(for_stack_ptr)); + ref = newtriple(retcode); + if (retopr) + ref->operand[0] = *retopr; + start_fetches(OC_NOOP); + resolve_ref(0); /* cannot fail because there are no MLAB_REF's in indirect code */ + alloc_reg(); + INVOKE_STP_GCOL(0); + /* The above invocation of stp_gcol with a parameter of 0 is a critical part of compilation + * (both routine compilations and indirect dynamic compilations). This collapses the indirect + * (compilation) stringpool so that only the literals are left. This stringpool is then written + * out to the compiled object as the literal pool for that compilation. Temporary stringpool + * use for conversions or whatever are eliminated. Note the path is different in stp_gcol for + * the indirect stringpool which is only used during compilations. + */ + assert(indr_stringpool.base == stringpool.base); + indr_stringpool = stringpool; + stringpool = rts_stringpool; + TREF(compile_time) = FALSE; + ind_code(obj); + indr_stringpool.free = indr_stringpool.base; + } + } + if (!status) + { + assert(indr_stringpool.base == stringpool.base); + indr_stringpool = stringpool; + stringpool = rts_stringpool; + indr_stringpool.free = indr_stringpool.base; + TREF(compile_time) = FALSE; + cg_phase = CGP_NOSTATE; + } + TREF(transform) = TRUE; + COMPILE_HASHTAB_CLEANUP; + mcfree(); + return status; +} diff --git a/sr_port/comp_indr.c b/sr_port/comp_indr.c new file mode 100644 index 0000000..f074c29 --- /dev/null +++ b/sr_port/comp_indr.c @@ -0,0 +1,105 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" +#include "gtm_string.h" + +#include "rtnhdr.h" +#include "stack_frame.h" +#include "mv_stent.h" +#include "copy.h" +#include "cache.h" +#include "objlabel.h" +#include "mprof.h" +#include "cacheflush.h" +#include "compiler.h" +#include "obj_file.h" +#include "error.h" + +GBLREF mv_stent *mv_chain; +GBLREF unsigned char *stackbase, *stacktop, *stackwarn, *msp; +GBLREF stack_frame *frame_pointer; +GBLREF boolean_t is_tracing_on; + +error_def(ERR_STACKCRIT); +error_def(ERR_STACKOFLOW); + +void comp_indr (mstr *obj) +{ + stack_frame *sf; + unsigned char *fix, *fix_base, *tmps, *syms, *save_msp; + int tempsz, vartabsz, fixup_cnt, zapsz; + INTPTR_T *vp; + ihdtyp *rtnhdr; + + assert((frame_pointer < frame_pointer->old_frame_pointer) || (NULL == frame_pointer->old_frame_pointer)); + save_msp = msp; + sf = (stack_frame *)(msp -= SIZEOF(stack_frame)); + rtnhdr = (ihdtyp *)obj->addr; + + /* Check that our cache_entry pointer is in proper alignment with us */ + assert(rtnhdr->indce->obj.addr == (char *)rtnhdr); + + tempsz = ROUND_UP2(rtnhdr->temp_size, SIZEOF(char *)); + tmps = msp -= tempsz; + vartabsz = rtnhdr->vartab_len; + vartabsz *= SIZEOF(ht_ent_mname *); + /* Check that our vars and friends can fit on this stack */ + if ((msp -= vartabsz) <= stackwarn) + { + if (msp <= stacktop) + { + msp = save_msp; + rts_error(VARLSTCNT(1) ERR_STACKOFLOW); + } else + rts_error(VARLSTCNT(1) ERR_STACKCRIT); + } + syms = msp; + + *sf = *frame_pointer; + sf->old_frame_pointer = frame_pointer; + sf->type = 0; + sf->temps_ptr = tmps; + sf->l_symtab = (ht_ent_mname **)syms; + sf->vartab_len = rtnhdr->vartab_len; + if (zapsz = (vartabsz + tempsz)) /* Note assignment */ + memset(syms, 0, zapsz); /* Zap temps and symtab together */ + + sf->vartab_ptr = (char *)rtnhdr + rtnhdr->vartab_off; + sf->temp_mvals = rtnhdr->temp_mvals; + /* Code starts just past the literals that were fixed up and past the validation and hdr offset fields */ + sf->mpc = (unsigned char *)rtnhdr + rtnhdr->fixup_vals_off + (rtnhdr->fixup_vals_num * SIZEOF(mval)); + /* IA64 required SECTION_ALIGN_BOUNDARY alignment (16 bytes). ABS 2008/12 + This has been carried forward to other 64bit platfoms without problems */ + GTM64_ONLY(sf->mpc = (unsigned char *)ROUND_UP2((UINTPTR_T)sf->mpc, SECTION_ALIGN_BOUNDARY)); + sf->mpc = sf->mpc + (2 * SIZEOF(INTPTR_T)); /* Account for hdroffset and MAGIC_VALUE */ + sf->flags = SFF_INDCE; /* We will be needing cleanup for this frame */ + DEBUG_ONLY( + vp = (INTPTR_T *)sf->mpc; + assert(NULL != vp); + vp--; + assert((GTM_OMAGIC << 16) + OBJ_LABEL == *vp); + vp--; + assert((unsigned char*)rtnhdr == (unsigned char *)vp + *vp); + ); + rtnhdr->indce->refcnt++; /* This entry is now in use on M stack */ + + if (is_tracing_on) + new_prof_frame(FALSE); + sf->ctxt = sf->mpc; + assert(msp < stackbase); + frame_pointer = sf; + DBGEHND((stderr, "comp_indr: Added indirect stack frame at addr 0x"lvaddr" - New msp: 0x"lvaddr"\n", sf, msp)); + assert((frame_pointer < frame_pointer->old_frame_pointer) || (NULL == frame_pointer->old_frame_pointer)); + return; +} diff --git a/sr_port/comp_init.c b/sr_port/comp_init.c new file mode 100644 index 0000000..292eb3c --- /dev/null +++ b/sr_port/comp_init.c @@ -0,0 +1,68 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "stp_parms.h" +#include "compiler.h" +#include "stringpool.h" +#include "rtnhdr.h" +#include "mv_stent.h" +#include "opcode.h" +#include "cgp.h" +#include "lb_init.h" + +/* WARNING: comp_init changes the currently-active stringpool from the + * the runtime stringpool (rts_stringpool) to the indirection stringpool + * (indr_stringpool). comp_fini changes it back from indr_stringpool to + * rts_stringpool when the compilation is finished. + */ +GBLREF spdesc stringpool,rts_stringpool; +GBLREF spdesc indr_stringpool; +GBLREF unsigned char *source_buffer; +GBLREF int4 curr_fetch_count; +GBLREF triple *curr_fetch_trip; +GBLREF char cg_phase; + +error_def(ERR_INDRMAXLEN); + +void comp_init(mstr *src) +{ + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + if ((unsigned)src->len >= MAX_SRCLINE) + rts_error(VARLSTCNT(3) ERR_INDRMAXLEN, 1, MAX_SRCLINE); + memcpy(source_buffer,src->addr,src->len); + source_buffer[src->len + 1] = source_buffer[src->len] = 0; + TREF(compile_time) = TRUE; + TREF(transform) = FALSE; + cg_phase = CGP_PARSE; + TREF(source_error_found) = 0; + TREF(last_source_column) = 0; + assert(rts_stringpool.base == stringpool.base); + rts_stringpool = stringpool; + if (!indr_stringpool.base) + { + stp_init(STP_INITSIZE); + indr_stringpool = stringpool; + } else + stringpool = indr_stringpool; + tripinit(); + lb_init(); + assert(TREF(for_stack_ptr) == TADR(for_stack)); + *TREF(for_stack_ptr) = NULL; + curr_fetch_trip = newtriple(OC_FETCH); + curr_fetch_count = 0; + start_fetches(OC_FETCH); + return; +} diff --git a/sr_port/compile_pattern.c b/sr_port/compile_pattern.c new file mode 100644 index 0000000..1a056a2 --- /dev/null +++ b/sr_port/compile_pattern.c @@ -0,0 +1,89 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "compiler.h" +#include "stringpool.h" +#include "opcode.h" +#include "mdq.h" +#include "advancewindow.h" +#include "compile_pattern.h" +#include "patcode.h" + +GBLREF spdesc stringpool; +GBLREF char *lexical_ptr; +GBLREF unsigned char *source_buffer; +GBLREF short int source_column; + +int compile_pattern(oprtype *opr, bool is_indirect) +{ + ptstr retstr; + mval retmval; + mstr instr; + int status; + triple *oldchain, tmpchain, *ref, *triptr; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + if (is_indirect) + { + if (TREF(shift_side_effects)) + { + dqinit(&tmpchain, exorder); + oldchain = setcurtchain(&tmpchain); + if (!indirection(opr)) + { + setcurtchain(oldchain); + return FALSE; + } + ref = newtriple(OC_INDPAT); + newtriple(OC_GVSAVTARG); + setcurtchain(oldchain); + dqadd(TREF(expr_start), &tmpchain, exorder); + TREF(expr_start) = tmpchain.exorder.bl; + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(TREF(expr_start)); + } else + { + if (!indirection(opr)) + return FALSE; + ref = newtriple(OC_INDPAT); + } + ref->operand[0] = *opr; + *opr = put_tref(ref); + return TRUE; + } else + { + instr.addr = (char *)&source_buffer[source_column - 1]; + instr.len = STRLEN(instr.addr); + status = patstr(&instr, &retstr, NULL); + TREF(last_source_column) = (short int)(instr.addr - (char *)source_buffer); + assert(TREF(last_source_column)); + if (status) + { /* status == syntax error when non-zero */ + stx_error(status); + return FALSE; + } + retmval.mvtype = MV_STR; + retmval.str.len = retstr.len * SIZEOF(uint4); + retmval.str.addr = (char *)stringpool.free; + ENSURE_STP_FREE_SPACE(retmval.str.len); + memcpy(stringpool.free, &retstr.buff[0], retmval.str.len); + stringpool.free += retmval.str.len; + *opr = put_lit(&retmval); + lexical_ptr = instr.addr; + advancewindow(); + advancewindow(); + return TRUE; + } +} diff --git a/sr_port/compile_pattern.h b/sr_port/compile_pattern.h new file mode 100644 index 0000000..5bbb894 --- /dev/null +++ b/sr_port/compile_pattern.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001, 2002 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __COMPILE_PATTERN_H__ +#define __COMPILE_PATTERN_H__ + +int compile_pattern(oprtype *z, bool is_indirect); + +#endif diff --git a/sr_port/compiler.h b/sr_port/compiler.h new file mode 100644 index 0000000..e07958f --- /dev/null +++ b/sr_port/compiler.h @@ -0,0 +1,491 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#ifndef COMPILER_H_INCLUDED +#define COMPILER_H_INCLUDED + +typedef unsigned int opctype; + +typedef struct mvarstruct +{ + struct mvarstruct *lson, + *rson; + int4 mvidx; + mident mvname; + struct tripletype *last_fetch; +} mvar; + +typedef struct mvaxstruct +{ + struct mvaxstruct *last, + *next; + mvar *var; + int4 mvidx; +} mvax; + +typedef struct mlinestruct +{ + struct mlinestruct *parent, + *sibling, + *child; + struct tripletype *externalentry; + uint4 line_number; /* ...operation on this line */ + boolean_t table; /* put in table or not */ +} mline; + +typedef struct mlabstruct +{ + struct mlabstruct *lson, + *rson; + mline *ml; + mident mvname; + int formalcnt; + boolean_t gbl; +} mlabel; + +typedef struct mliteralstruct +{ + struct + { + struct mliteralstruct *fl, + *bl; + } que; + INTPTR_T rt_addr; + mval v; +} mliteral; + +typedef struct triplesize +{ + struct tripletype *ct; + int4 size; +} tripsize; + +typedef struct oprtypestruct +{ + char oprclass; + union + { + struct oprtypestruct *indr; + struct tripletype *tref; + struct triplesize *tsize; + mlabel *lab; + mline *mlin; + mliteral *mlit; + mstr *cdlt; + mvar *vref; + int4 temp; + int4 ilit; + int4 offset; + unsigned char vreg; + } oprval; +} oprtype; + +/* Values for oprclass */ +#define NOCLASS 0 +#define TVAR_REF 1 +#define TVAL_REF 2 +#define TINT_REF 3 +#define TVAD_REF 4 +#define TCAD_REF 5 +#define VALUED_REF_TYPES 6 +#define VREG_REF 6 +#define MLIT_REF 7 +#define MVAR_REF 8 +#define TRIP_REF 9 +#define TNXT_REF 10 +#define TJMP_REF 11 +#define INDR_REF 12 +#define MLAB_REF 13 +#define ILIT_REF 14 +#define CDLT_REF 15 +#define TEMP_REF 16 +#define MFUN_REF 17 +#define MNXL_REF 18 /* refer to internalentry of child line */ +#define TSIZ_REF 19 /* ilit refering to size of given triple codegen */ +#define OCNT_REF 20 /* Offset from Call to Next Triple */ + + +typedef struct tbptype +{ + struct + { + struct tbptype *fl, + *bl; + } que; + struct tripletype *bpt; +} tbp; + +typedef struct +{ + uint4 line; + uint4 column; +} source_address; + +typedef struct tripletype +{ + opctype opcode; + struct + { + struct tripletype *fl, + *bl; + } exorder; + tbp backptr, /* triples which reference this triple's value */ + jmplist; /* triples which jump to this one */ + source_address src; + int rtaddr; /* relative run time address of triple */ + oprtype operand[2], + destination; +} triple; + +typedef struct +{ + unsigned short octype; +} octabstruct; + +/* Values for octype */ +#define OCT_NULL 0 +#define OCT_MVAL 1 +#define OCT_MINT 2 +#define OCT_MVADDR 4 +#define OCT_CDADDR 8 +#define OCT_VALUE (OCT_MVAL | OCT_MINT | OCT_CDADDR) +#define OCT_BOOL 16 +#define OCT_JUMP 32 +#define OCT_EXPRLEAF 64 +#define OCT_CGSKIP 128 +#define OCT_COERCE 256 + + +typedef struct +{ + char name[20]; + opctype bo_type; + char uo_type; + unsigned short opr_type; +} toktabtype; + +#define VMS_OS 01 +#define UNIX_OS 02 +#define ALL_SYS (VMS_OS | UNIX_OS) +#ifdef UNIX /* function and svn validation are a function of the OS */ +# define VALID_FUN(i) (fun_data[i].os_syst & UNIX_OS) +# define VALID_SVN(i) (svn_data[i].os_syst & UNIX_OS) +# ifdef __hppa +# define TRIGGER_OS 0 +# else +# define TRIGGER_OS UNIX_OS +# endif +#elif defined VMS +# define VALID_FUN(i) (fun_data[i].os_syst & VMS_OS) +# define VALID_SVN(i) (svn_data[i].os_syst & VMS_OS) +# define TRIGGER_OS 0 +#else +# error UNSUPPORTED PLATFORM +#endif + +#define EXPR_FAIL 0 /* expression had syntax error */ +#define EXPR_GOOD 1 /* expression ok, no indirection at root */ +#define EXPR_INDR 2 /* expression ok, indirection at root */ +#define EXPR_SHFT 4 /* expression ok, involved shifted GV references */ + +#define CHARMAXARGS 256 +#define MAX_ACTUALS 32 +#define MAX_FORARGS 127 +#define MAX_SRCLINE 8192 /* maximum length of a program source or indirection line */ +#define NO_FORMALLIST (-1) + +/* Some errors should not cause stx_error to issue an rts_error. These are the errors related to + * a) Invalid Intrinsic Special Variables + * b) Invalid Intrinsic Function Names + * c) Invalid Deviceparameters for IO commands + * These should cause an error at runtime if and only if that codepath is reached. + * PostConditionals can cause this path to be avoided in which case we do not want to issue an error at compile time. + * Therefore issue only a warning at compile-time and proceed with compilation as if this codepath will not be reached at runtime. + */ +error_def(ERR_DEVPARINAP); +error_def(ERR_DEVPARUNK); +error_def(ERR_DEVPARVALREQ); +error_def(ERR_FNOTONSYS); +error_def(ERR_INVFCN); +error_def(ERR_INVSVN); +error_def(ERR_SVNONEW); +error_def(ERR_SVNOSET); + +#define IS_STX_WARN(errcode) \ + ((ERR_INVFCN == errcode) || (ERR_FNOTONSYS == errcode) || (ERR_INVSVN == errcode) \ + || (ERR_SVNONEW == errcode) || (ERR_SVNOSET == errcode) || (ERR_DEVPARUNK == errcode) \ + || (ERR_DEVPARINAP == errcode) || (ERR_DEVPARVALREQ == errcode)) + +/* This macro does an "stx_error" of the input errcode but before that it asserts that the input errcode is one + * of the known error codes that are to be handled as a compile-time warning (instead of an error). It also set + * the variable "parse_warn" to TRUE which is relied upon by the functions that invoke this macro. Note that when + * triggers are included, warnings become errors so bypass the warning stuff. + */ +#ifdef GTM_TRIGGER +# define STX_ERROR_WARN(errcode) \ +{ \ + if (!TREF(trigger_compile)) \ + parse_warn = TRUE; \ + assert(IS_STX_WARN(errcode)); \ + stx_error(errcode); \ + if (TREF(trigger_compile)) \ + return FALSE; \ +} +#else +# define STX_ERROR_WARN(errcode) \ +{ \ + parse_warn = TRUE; \ + assert(IS_STX_WARN(errcode)); \ + stx_error(errcode); \ +} +#endif + +#ifdef DEBUG +# define COMPDBG(x) if (gtmDebugLevel & GDL_DebugCompiler) {x} +#else +# define COMPDBG(x) +#endif + +/* Cutover from simple lists to hash table access - tuned by testing compilation + of 24K+ Vista M source routines. +*/ +#include "copy.h" +#define LIT_HASH_CUTOVER DEBUG_ONLY(4) PRO_ONLY(32) +#define SYM_HASH_CUTOVER DEBUG_ONLY(4) PRO_ONLY(16) +#define COMPLITS_HASHTAB_CLEANUP \ + { \ + GBLREF hash_table_str *complits_hashtab; \ + if (complits_hashtab && complits_hashtab->base) \ + { /* Release hash table itself but leave hash table descriptor if exists */ \ + free_hashtab_str(complits_hashtab); \ + } \ + } +#define COMPSYMS_HASHTAB_CLEANUP \ + { \ + GBLREF hash_table_str *compsyms_hashtab; \ + if (compsyms_hashtab && compsyms_hashtab->base) \ + { /* Release hash table itself but leave hash table descriptor if exists */ \ + free_hashtab_str(compsyms_hashtab); \ + compsyms_hashtab->base = NULL; \ + } \ + } +#define COMPILE_HASHTAB_CLEANUP \ + COMPLITS_HASHTAB_CLEANUP; \ + COMPSYMS_HASHTAB_CLEANUP; + +/* Macro to compute running checksum of a routine one line at a time. */ +#define RTN_SRC_CHKSUM(srcptr, srclen, chksum) \ +{ \ + char *chkcalc, *cptr; \ + uint4 srcint; \ + for (chkcalc = srcptr, cptr = srcptr + srclen; chkcalc < cptr; ) \ + { \ + srcint = 0; \ + if (INTCAST(cptr - chkcalc) < SIZEOF(uint4)) \ + { \ + memcpy(&srcint, chkcalc, cptr - chkcalc); \ + chkcalc = cptr; /* Stops loop after this iteration is complete */ \ + } else \ + { \ + GET_ULONG(srcint, chkcalc); \ + chkcalc += SIZEOF(uint4); \ + } \ + chksum ^= srcint; \ + chksum >>= 1; \ + } \ +} + +/* the macro below tucks a code reference into the for_stack so a FOR that's done can move on correctly when done */ +#define FOR_END_OF_SCOPE(DEPTH, RESULT) \ +{ \ + oprtype **Ptr; \ + \ + assert(0 <= DEPTH); \ + assert(TREF(for_stack_ptr) < (oprtype **)TADR(for_stack) + MAX_FOR_STACK); \ + Ptr = (oprtype **)TREF(for_stack_ptr) - DEPTH; \ + assert(Ptr >= (oprtype **)TADR(for_stack)); \ + if (NULL == *Ptr) \ + *Ptr = (oprtype *)mcalloc(SIZEOF(oprtype)); \ + RESULT = put_indr(*Ptr); \ +} + +#define GOOD_FOR FALSE /* single level */ +#define BLOWN_FOR TRUE /* all levels */ + +/* Marco to decrement or clear the for_stack, clear the for_temp array + * and generate code to release run-time malloc'd mvals anchored in the for_saved_indx array + * The corresponding FOR_PUSH macro is in m_for.c but this one is used in stx_error.c for error cases + */ +#define FOR_POP(ALL) \ +{ \ + unsigned int For_stack_level; \ + boolean_t Seen_indx; \ + \ + assert(TREF(for_stack_ptr) >= (oprtype **)TADR(for_stack)); \ + assert(TREF(for_stack_ptr) <= (oprtype **)TADR(for_stack) + MAX_FOR_STACK); \ + if (TREF(for_stack_ptr) > (oprtype **)TADR(for_stack)) \ + --(TREF(for_stack_ptr)); \ + if (ALL) \ + { \ + while (TREF(for_stack_ptr) > (oprtype **)TADR(for_stack)) \ + (TREF(for_stack_ptr))--; \ + *(TREF(for_stack_ptr)) = NULL; \ + } \ + if (TREF(for_stack_ptr) == (oprtype **)TADR(for_stack)) \ + { \ + for (Seen_indx = FALSE, For_stack_level = MAX_FOR_STACK; --For_stack_level; ) \ + { \ + if (!Seen_indx && (TRUE_WITH_INDX == TAREF1(for_temps, For_stack_level))) \ + { \ + (void)newtriple(OC_FORFREEINDX); \ + Seen_indx = TRUE; \ + } \ + TAREF1(for_temps, For_stack_level) = FALSE; \ + } \ + } else \ + assert(TREF(for_stack_ptr) > (oprtype **)TADR(for_stack)); \ +} + +/* value used to make for_temps entries a little more than boolean */ +#define TRUE_WITH_INDX 2 + +int actuallist(oprtype *opr); +int bool_expr(bool op, oprtype *addr); +void bx_boolop(triple *t, bool jmp_type_one, bool jmp_to_next, bool sense, oprtype *addr); +void bx_relop(triple *t, opctype cmp, opctype tst, oprtype *addr); +void bx_tail(triple *t, bool sense, oprtype *addr); +void chktchain(triple *head); +void code_gen(void); +void coerce(oprtype *a, unsigned short new_type); +int comp_fini(bool status, mstr *obj, opctype retcode, oprtype *retopr, mstr_len_t src_len); +void comp_init(mstr *src); +void comp_indr(mstr *obj); +boolean_t compiler_startup(void); +triple *entryref(opctype op1, opctype op2, mint commargcode, boolean_t can_commarg, boolean_t labref, boolean_t textname); +int eval_expr(oprtype *a); +int expratom(oprtype *a); +int exfunc(oprtype *a, boolean_t alias_target); +int expritem(oprtype *a); +int expr(oprtype *a); +void ex_tail(oprtype *opr); +int extern_func(oprtype *a); +int f_ascii(oprtype *a, opctype op); +int f_char(oprtype *a, opctype op); +int f_data(oprtype *a, opctype op); +int f_extract(oprtype *a, opctype op); +int f_find(oprtype *a, opctype op); +int f_fnumber(oprtype *a, opctype op); +int f_fnzbitfind(oprtype *a, opctype op); +int f_fnzbitget(oprtype *a, opctype op); +int f_fnzbitset(oprtype *a, opctype op); +int f_fnzbitstr(oprtype *a, opctype op); +int f_get(oprtype *a, opctype op); +int f_incr(oprtype *a, opctype op); +int f_justify(oprtype *a, opctype op); +int f_length(oprtype *a, opctype op); +int f_mint(oprtype *a, opctype op); +int f_mint_mstr(oprtype *a, opctype op); +int f_mstr(oprtype *a, opctype op); +int f_name(oprtype *a, opctype op); +int f_next(oprtype *a, opctype op); +int f_one_mval(oprtype *a, opctype op); +int f_order(oprtype *a, opctype op); +int f_order1(oprtype *a, opctype op); +int f_piece(oprtype *a, opctype op); +int f_qlength(oprtype *a, opctype op); +int f_qsubscript(oprtype *a, opctype op); +int f_query (oprtype *a, opctype op); +int f_reverse(oprtype *a, opctype op); +int f_select(oprtype *a, opctype op); +int f_stack(oprtype *a, opctype op); +int f_text(oprtype *a, opctype op); +int f_translate(oprtype *a, opctype op); +int f_two_mstrs(oprtype *a, opctype op); +int f_two_mval(oprtype *a, opctype op); +int f_view(oprtype *a, opctype op); +int f_zahandle(oprtype *a, opctype op); +int f_zcall(oprtype *a, opctype op); +int f_zchar(oprtype *a, opctype op); +int f_zconvert(oprtype *a, opctype op); +int f_zdate(oprtype *a, opctype op); +int f_zdebug(oprtype *a, opctype op); +int f_zechar(oprtype *a, opctype op); +int f_zgetsyi(oprtype *a, opctype op); +int f_zjobexam(oprtype *a, opctype op); +int f_zparse(oprtype *a, opctype op); +int f_zprevious(oprtype *a, opctype op); +int f_zqgblmod(oprtype *a, opctype op); +int f_zsearch(oprtype *a, opctype op); +int f_zsigproc(oprtype *a, opctype op); +int f_zsqlexpr (oprtype *a, opctype op); +int f_zsqlfield (oprtype *a, opctype op); +int f_zsubstr(oprtype *a, opctype op); +int f_ztrigger(oprtype *a, opctype op); +int f_ztrnlnm(oprtype *a, opctype op); +int f_zwidth(oprtype *a, opctype op); +mlabel *get_mladdr(mident *c); +mvar *get_mvaddr(mident *c); +int glvn(oprtype *a); +int gvn(void); +void ind_code(mstr *obj); +int indirection(oprtype *a); +void ins_triple(triple *x); +int intexpr(oprtype *a); +void int_label(void); +int jobparameters (oprtype *c); +boolean_t line(uint4 *lnc); +int linetail(void); +int lkglvn(bool gblvn); +int lref(oprtype *label, oprtype *offset, bool no_lab_ok, mint commarg_code, bool commarg_ok, bool *got_some); +int lvn(oprtype *a,opctype index_op,triple *parent); +void make_commarg(oprtype *x, mint ind); +oprtype make_gvsubsc(mval *v); +triple *maketriple(opctype op); +int name_glvn(bool gblvn, oprtype *a); +triple *newtriple(opctype op); +int nref(void); +int numexpr(oprtype *a); +void obj_code(uint4 src_lines, uint4 checksum); +int one_job_param(char **parptr); +int parse_until_rparen_or_space(void); +oprtype put_ocnt(void); +oprtype put_tsiz(void); +oprtype put_cdlt(mstr *x); +oprtype put_ilit(mint x); +oprtype put_indr(oprtype *x); +oprtype put_lit(mval *x); +oprtype put_mfun(mident *l); +oprtype put_mlab(mident *l); +oprtype put_mnxl(void); +oprtype put_mvar(mident *x); +oprtype put_str(char *pt, mstr_len_t n); +oprtype put_tjmp(triple *x); +oprtype put_tnxt(triple *x); +oprtype put_tref(triple *x); +int resolve_ref(int errknt); +void resolve_tref(triple *, oprtype *); +triple *setcurtchain(triple *x); +/* VMS uses same code generator as USHBIN so treat as USHBIN for these compiler routines */ +# if defined(USHBIN_SUPPORTED) || defined(VMS) +void shrink_trips(void); +boolean_t litref_triple_oprcheck(oprtype *operand); +# else +void shrink_jmps(void); +# endif +void start_fetches(opctype op); +void start_for_fetches(void); +int strexpr(oprtype *a); +void tnxtarg(oprtype *a); +void tripinit(void); +void walktree(mvar *n,void (*f)(),char *arg); +void wrtcatopt(triple *r, triple ***lpx, triple **lptop); +int zlcompile(unsigned char len, unsigned char *addr); /***type int added***/ + +#endif /* COMPILER_H_INCLUDED */ diff --git a/sr_port/compiler_ch.c b/sr_port/compiler_ch.c new file mode 100644 index 0000000..fdf8f3e --- /dev/null +++ b/sr_port/compiler_ch.c @@ -0,0 +1,67 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "error.h" +#include "cgp.h" +#include "cmd_qlf.h" +#include "list_file.h" +#include "source_file.h" +#include "rtnhdr.h" +#include "obj_file.h" +#include "reinit_externs.h" +#include "compiler.h" +#include "util.h" +#include "hashtab_str.h" + +GBLREF command_qualifier cmd_qlf; +GBLREF char cg_phase; +GBLREF boolean_t mstr_native_align, save_mstr_native_align; + +CONDITION_HANDLER(compiler_ch) +{ + error_def(ERR_ASSERT); + error_def(ERR_FORCEDHALT); + error_def(ERR_GTMASSERT); + error_def(ERR_GTMCHECK); + error_def(ERR_MEMORY); + error_def(ERR_VMSMEMORY); + error_def(ERR_STACKOFLOW); + error_def(ERR_OUTOFSPACE); + + START_CH; + if (DUMPABLE) + { + NEXTCH; + } + + if (cmd_qlf.qlf & CQ_WARNINGS) + PRN_ERROR; + + COMPILE_HASHTAB_CLEANUP; + reinit_externs(); + mstr_native_align = save_mstr_native_align; + + if (cg_phase == CGP_MACHINE) + drop_object_file(); + + if (cg_phase > CGP_NOSTATE) + { + if (cg_phase < CGP_RESOLVE) + close_source_file(); + if (cg_phase < CGP_FINI && (cmd_qlf.qlf & CQ_LIST || cmd_qlf.qlf & CQ_CROSS_REFERENCE)) + { + close_list_file(); + } + } + UNWIND(NULL, NULL); +} diff --git a/sr_port/compiler_startup.c b/sr_port/compiler_startup.c new file mode 100644 index 0000000..a956d6a --- /dev/null +++ b/sr_port/compiler_startup.c @@ -0,0 +1,195 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "compiler.h" +#include "opcode.h" +#include "cmd_qlf.h" +#include "mdq.h" +#include "cgp.h" +#include "error.h" +#include "mmemory.h" +#include "stringpool.h" +#include "list_file.h" +#include "source_file.h" +#include "lb_init.h" +#include "reinit_externs.h" +#include "comp_esc.h" +#include "resolve_blocks.h" +#include "hashtab_str.h" + +#define HOPELESS_COMPILE 128 + +GBLREF short int source_column, source_line; + +/* ensure source_buffer is aligned on a int4 word boundary so that + * we can calculate the checksum a longword at a time. + */ +GBLREF int4 aligned_source_buffer[MAX_SRCLINE / SIZEOF(int4) + 1]; +GBLREF unsigned char *source_buffer; +GBLREF src_line_struct src_head; +GBLREF triple t_orig, *curr_fetch_trip, *curr_fetch_opr; +GBLREF int4 curr_fetch_count; +GBLREF command_qualifier cmd_qlf; +GBLREF int mlmax; +GBLREF mline mline_root; +GBLREF char cg_phase; /* code generation phase */ +GBLREF boolean_t mstr_native_align, save_mstr_native_align; +GBLREF hash_table_str *complits_hashtab; + +LITDEF char compile_terminated[] = "COMPILATION TERMINATED DUE TO EXCESS ERRORS"; + +boolean_t compiler_startup(void) +{ +#ifdef DEBUG + void dumpall(); +#endif + boolean_t compile_w_err; + unsigned char err_buf[45]; + unsigned char *cp, *cp2; + int errknt; + int4 n; + uint4 checksum, line_count; + mlabel *null_lab; + src_line_struct *sl; + mident null_mident; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + assert(NULL == complits_hashtab || NULL == complits_hashtab->base); + memset(&null_mident, 0, SIZEOF(null_mident)); + ESTABLISH_RET(compiler_ch, FALSE); + /* Since the stringpool alignment is solely based on mstr_native_align, we need to initialize it based + * on the ALIGN_STRINGS qualifier so that all strings in the literal text pool are aligned. + * However, when a module is compiled at runtime, we need to preserve the existing runtime setting + * (that was initialized at GT.M startup) once the compilation is done. save_mstr_native_align is used for + * this purpose. */ + save_mstr_native_align = mstr_native_align; + /* mstr_native_align = (cmd_qlf.qlf & CQ_ALIGN_STRINGS) ? TRUE : FALSE; */ + mstr_native_align = FALSE; /* TODO: remove this line and uncomment the above line */ + cg_phase = CGP_NOSTATE; + TREF(source_error_found) = errknt = 0; + if(!open_source_file()) + { + mstr_native_align = save_mstr_native_align; + REVERT; + return FALSE; + } + cg_phase = CGP_PARSE; + if (cmd_qlf.qlf & CQ_LIST || cmd_qlf.qlf & CQ_CROSS_REFERENCE) + { + if (cmd_qlf.qlf & CQ_MACHINE_CODE) + dqinit(&src_head, que); + open_list_file(); + } + if (cmd_qlf.qlf & CQ_CE_PREPROCESS) + open_ceprep_file(); + tripinit(); + null_lab = get_mladdr(&null_mident); + null_lab->ml = &mline_root; + mlmax++; + curr_fetch_trip = curr_fetch_opr = newtriple(OC_LINEFETCH); + curr_fetch_count = 0; + TREF(code_generated) = FALSE; + checksum = 0; + line_count = 1; + for (source_line = 1; errknt <= HOPELESS_COMPILE; source_line++) + { + if (-1 == (n = read_source_file())) + break; + if (cmd_qlf.qlf & CQ_LIST || cmd_qlf.qlf & CQ_CROSS_REFERENCE) + { + if (cmd_qlf.qlf & CQ_MACHINE_CODE) + { + sl = (src_line_struct *)mcalloc(SIZEOF(src_line_struct)); + dqins(&src_head, que, sl); + sl->addr = mcalloc(n + 1); /* +1 for zero termination */ + sl->line = source_line; + memcpy(sl->addr, source_buffer, n + 1); + } else + { + list_line_number(); + list_line((char *)source_buffer); + } + } + /* calculate checksum */ + RTN_SRC_CHKSUM((char *)source_buffer, n, checksum); + TREF(source_error_found) = 0; + lb_init(); + if (cmd_qlf.qlf & CQ_CE_PREPROCESS) + put_ceprep_line(); + if (!line(&line_count)) + { + assert(TREF(source_error_found)); + errknt++; + } + } + close_source_file(); + if (cmd_qlf.qlf & CQ_CE_PREPROCESS) + close_ceprep_file(); + cg_phase = CGP_RESOLVE; + if (t_orig.exorder.fl == &t_orig) /* if no lines in routine, set up line 0 */ + newtriple(OC_LINESTART); + newtriple(OC_RET); /* always provide a default QUIT */ + mline_root.externalentry = t_orig.exorder.fl; + INVOKE_STP_GCOL(0); + /* The above invocation of stp_gcol with a parameter of 0 is a critical part of compilation + * (both routine compilations and indirect dynamic compilations). This collapses the indirect + * (compilation) stringpool so that only the literals are left. This stringpool is then written + * out to the compiled object as the literal pool for that compilation. Temporary stringpool + * use for conversions or whatever are eliminated. Note the path is different in stp_gcol for + * the indirect stringpool which is only used during compilations. + */ + start_fetches(OC_NOOP); + resolve_blocks(); + errknt = resolve_ref(errknt); + compile_w_err = (errknt <= HOPELESS_COMPILE && (cmd_qlf.qlf & CQ_IGNORE)); + if (cmd_qlf.qlf & CQ_LIST || cmd_qlf.qlf & CQ_CROSS_REFERENCE) + { + list_line(""); + if (errknt) + cp = i2asc(err_buf, errknt); + else + { + cp = err_buf; + *cp++ = 'n'; + *cp++ = 'o'; + } + memcpy(cp, " error", SIZEOF(" error")); + cp += SIZEOF(" error") - 1; + if (1 != errknt) + *cp++ = 's'; + *cp = 0; + list_line((char *)err_buf); + if (errknt > HOPELESS_COMPILE) + list_line((char *)compile_terminated); + if (cmd_qlf.qlf & CQ_MACHINE_CODE && compile_w_err) + list_head(1); + } + if ((!errknt || compile_w_err) && (cmd_qlf.qlf & CQ_OBJECT || cmd_qlf.qlf & CQ_MACHINE_CODE)) + { + obj_code(line_count, checksum); + cg_phase = CGP_FINI; + } + if (cmd_qlf.qlf & CQ_LIST || cmd_qlf.qlf & CQ_CROSS_REFERENCE) + { + list_cmd(); + close_list_file(); + } + COMPILE_HASHTAB_CLEANUP; + reinit_externs(); + mstr_native_align = save_mstr_native_align; + REVERT; + return errknt ? TRUE : FALSE; +} diff --git a/sr_port/compswap.h b/sr_port/compswap.h new file mode 100644 index 0000000..6a998ae --- /dev/null +++ b/sr_port/compswap.h @@ -0,0 +1,57 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef COMPSWAP_H_INCLUDE +#define COMPSWAP_H_INCLUDE + +/* COMPSWAP_LOCK/UNLOCK are the same for all platform except for __ia64, which needs slightly different versions to handle + * memory consistency isues + */ +#ifdef UNIX + boolean_t compswap_secshr(sm_global_latch_ptr_t lock, int compval, int newval1); +# if (defined(_AIX) || (defined(__ia64) && defined(__linux__))) /* AIX or Linux Itanium */ + boolean_t compswap_lock(sm_global_latch_ptr_t lock, int compval, int newval1); + boolean_t compswap_unlock(sm_global_latch_ptr_t lock, int compval, int newval1); +# define COMPSWAP_LOCK(LCK, CMPVAL1, CMPVAL2, NEWVAL1, NEWVAL2) compswap_lock(LCK, CMPVAL1, NEWVAL1) +# define COMPSWAP_UNLOCK(LCK, CMPVAL1, CMPVAL2, NEWVAL1, NEWVAL2) compswap_unlock(LCK, CMPVAL1, NEWVAL1) +# elif !defined(__ia64) + boolean_t compswap(sm_global_latch_ptr_t lock, int compval, int newval1); +# define COMPSWAP_LOCK(LCK, CMPVAL1, CMPVAL2, NEWVAL1, NEWVAL2) compswap(LCK, CMPVAL1, NEWVAL1) +# define COMPSWAP_UNLOCK(LCK, CMPVAL1, CMPVAL2, NEWVAL1, NEWVAL2) compswap(LCK, CMPVAL1, NEWVAL1) +# elif defined(__HP_cc) + /* Use compiler inline assembly macros for HP-UX/HP C */ + /* This is assuming 32 bit lock storage, which right now seems to be PIDs + * most of the time. PIDs are currently 32 bit values, but that could change + * someday, so beware + */ +# include +# define FENCE (_Asm_fence) (_UP_CALL_FENCE | _UP_SYS_FENCE | _DOWN_CALL_FENCE | _DOWN_SYS_FENCE) +# define COMPSWAP_LOCK(LCK, CMPVAL1, CMPVAL2, NEWVAL1, NEWVAL2) \ + ( \ + _Asm_mov_to_ar((_Asm_app_reg)_AREG_CCV, (uint64_t) CMPVAL1,FENCE), \ + _Asm_cmpxchg((_Asm_sz)_SZ_W, (_Asm_sem)_SEM_ACQ,(uint32_t *)LCK, \ + (uint64_t)NEWVAL1, (_Asm_ldhint)_LDHINT_NONE) == (uint64_t)CMPVAL1 ? 1 : 0 \ + ) +# define COMPSWAP_UNLOCK(LCK, CMPVAL1, CMPVAL2, NEWVAL1, NEWVAL2) \ + ( \ + _Asm_mov_to_ar((_Asm_app_reg)_AREG_CCV,(uint64_t) CMPVAL1,FENCE), \ + _Asm_cmpxchg((_Asm_sz)_SZ_W,(_Asm_sem)_SEM_REL,(uint32_t *)LCK, \ + (uint64_t)NEWVAL1, (_Asm_ldhint)_LDHINT_NONE) == (uint64_t)CMPVAL1 ? 1 : 0 \ + ) +# endif /* __ia64 */ +#else + boolean_t compswap(sm_global_latch_ptr_t lock, int compval1, int compval2, int newval1, int newval2); + boolean_t compswap_secshr(sm_global_latch_ptr_t lock, int compval1, int compval2, int newval1, int newval2); +# define COMPSWAP_LOCK(LCK, CMPVAL1, CMPVAL2, NEWVAL1, NEWVAL2) compswap(LCK, CMPVAL1, CMPVAL2, NEWVAL1, NEWVAL2) +# define COMPSWAP_UNLOCK(LCK, CMPVAL1, CMPVAL2, NEWVAL1, NEWVAL2) compswap(LCK, CMPVAL1, CMPVAL2, NEWVAL1, NEWVAL2) +#endif + +#endif diff --git a/sr_port/copy.h b/sr_port/copy.h new file mode 100644 index 0000000..a22f9e2 --- /dev/null +++ b/sr_port/copy.h @@ -0,0 +1,70 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* If unaligned access is supported UNALIGNED_ACCESS_SUPPORTED has to be defined in the appropriate mdefsp.h + * On platforms where unaligned data is not legal, + * the macros are defined using a series of character moves */ + +#ifdef UNALIGNED_ACCESS_SUPPORTED +/* Unsigned versions are different from signed ones as we may get sign extension problems when promotion is needed */ +#define GET_LONGP(X,Y) (*(int4 *)(X) = *(int4 *)(Y)) +#define GET_SHORTP(X,Y) (*(short *)(X) = *(short *)(Y)) +#define GET_LONG(X,Y) ((X) = *(int4 *)(Y)) +#define GET_ULONG(X,Y) ((X) = *(uint4 *)(Y)) +#define GET_SHORT(X,Y) ((X) = *(short *)(Y)) +#define GET_USHORT(X,Y) ((X) = *(unsigned short *)(Y)) +#define GET_CHAR(X,Y) ((X) = *(unsigned char *)(Y)) +#define REF_CHAR(Y) (*(unsigned char *)Y) +#define PUT_ZERO(X) ((X) = 0) +#define PUT_LONG(X,Y) (*(int4*)(X) = (Y)) +#define PUT_ULONG(X,Y) (*(uint4*)(X) = (Y)) +#define PUT_SHORT(X,Y) (*(short*)(X) = (Y)) +#define PUT_USHORT(X,Y) (*(unsigned short*)(X) = (Y)) +#define PUT_CHAR(X,Y) (*(unsigned char *)(X) = (Y)) +#else +#include +#define GET_LONGP(X,Y) (*(caddr_t)(X) = *(caddr_t)(Y), \ + *((caddr_t)(X)+1) = *((caddr_t)(Y)+1), \ + *((caddr_t)(X)+2) = *((caddr_t)(Y)+2), \ + *((caddr_t)(X)+3) = *((caddr_t)(Y)+3)) + +#define GET_SHORTP(X,Y) (*(caddr_t)(X) = *(caddr_t)(Y), *((caddr_t)(X)+1) = *((caddr_t)(Y)+1)) + +/* Unsigned versions are same as the signed ones as we do char by char */ +#define GET_LONG(X,Y) (*(caddr_t)(&X) = *(caddr_t)(Y), \ + *((caddr_t)(&X)+1) = *((caddr_t)(Y)+1), \ + *((caddr_t)(&X)+2) = *((caddr_t)(Y)+2), \ + *((caddr_t)(&X)+3) = *((caddr_t)(Y)+3)) + +#define GET_ULONG GET_LONG + +#define GET_SHORT(X,Y) (*(caddr_t)(&X) = *(caddr_t)(Y), *((caddr_t)(&X)+1) = *((caddr_t)(Y)+1)) + +#define GET_USHORT GET_SHORT + +#define GET_CHAR(X,Y) (*(caddr_t)(&X) = *(caddr_t)(Y)) +#define REF_CHAR(Y) (*(caddr_t)(Y)) + +#define PUT_ZERO(X) (memset((caddr_t)&(X), 0, SIZEOF(X))) + +#define PUT_LONG(X,Y) (*(caddr_t)(X) = *(caddr_t)(&Y), \ + *((caddr_t)(X)+1) = *((caddr_t)(&Y)+1), \ + *((caddr_t)(X)+2) = *((caddr_t)(&Y)+2), \ + *((caddr_t)(X)+3) = *((caddr_t)(&Y)+3)) + +#define PUT_ULONG PUT_LONG + +#define PUT_SHORT(X,Y) (*(caddr_t)(X) = *(caddr_t)(&Y), *((caddr_t)(X)+1) = *((caddr_t)(&Y)+1)) + +#define PUT_USHORT PUT_SHORT + +#define PUT_CHAR(X,Y) (*(caddr_t)(X) = *(caddr_t)(&Y)) +#endif /*UNALIGNED_ACCESS_SUPPORTED*/ diff --git a/sr_port/copy_stack_frame.c b/sr_port/copy_stack_frame.c new file mode 100644 index 0000000..3a16dfc --- /dev/null +++ b/sr_port/copy_stack_frame.c @@ -0,0 +1,59 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" + +#include "rtnhdr.h" +#include "stack_frame.h" +#include "mprof.h" +#include "error.h" + +GBLREF stack_frame *frame_pointer; +GBLREF unsigned char *stackbase ,*stacktop, *msp, *stackwarn; + +error_def(ERR_STACKCRIT); +error_def(ERR_STACKOFLOW); + +void copy_stack_frame(void) +{ + register stack_frame *sf; + unsigned char *msp_save; + + msp_save = msp; + sf = (stack_frame *) (msp -= SIZEOF(stack_frame)); + if (msp <= stackwarn) + { + if (msp <= stacktop) + { + msp = msp_save; + rts_error(VARLSTCNT(1) ERR_STACKOFLOW); + } else + rts_error(VARLSTCNT(1) ERR_STACKCRIT); + } + assert(msp < stackbase); + assert((frame_pointer < frame_pointer->old_frame_pointer) || (NULL == frame_pointer->old_frame_pointer)); + *sf = *frame_pointer; + sf->old_frame_pointer = frame_pointer; + sf->flags = 0; /* Don't propagate special flags */ + sf->for_ctrl_stack = NULL; + frame_pointer = sf; + DBGEHND((stderr, "copy_stack_frame: Added stackframe at addr 0x"lvaddr" old-msp: 0x"lvaddr" new-msp: 0x"lvaddr"\n", + sf, msp_save, msp)); + assert((frame_pointer < frame_pointer->old_frame_pointer) || (NULL == frame_pointer->old_frame_pointer)); +} + +void copy_stack_frame_sp(void) +{ + copy_stack_frame(); + new_prof_frame(TRUE); +} diff --git a/sr_port/cre_jnl_file.c b/sr_port/cre_jnl_file.c new file mode 100644 index 0000000..281d239 --- /dev/null +++ b/sr_port/cre_jnl_file.c @@ -0,0 +1,472 @@ +/**************************************************************** + * * + * Copyright 2003, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gtm_stat.h" +#if defined(UNIX) +#include "gtm_fcntl.h" +#include "gtm_unistd.h" +#include "eintr_wrappers.h" +#include "gtm_permissions.h" +#if defined(__MVS__) +#include "gtm_zos_io.h" +#endif +#elif defined(VMS) +#include +#include +#include +#include "iosb_disk.h" +#endif + +#include "gtm_file_stat.h" +#include "gtm_rename.h" +#include "error.h" +#include "gdsroot.h" +#include "gdsbt.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "jnl.h" +#include "gtmio.h" +#include "util.h" +#include "gtmmsg.h" +#include "send_msg.h" +#include "iosp.h" +#include "repl_sp.h" +#include "is_file_identical.h" +#include "jnl_get_checksum.h" +#include "gtmimagename.h" +#include "get_fs_block_size.h" +#include "wbox_test_init.h" +#include "gt_timer.h" + +/* Note : Now all system error messages are issued here. So callers do not need to issue them again */ +#define STATUS_MSG(info) \ +{ \ + if (SS_NORMAL != info->status2) \ + { \ + if (IS_GTM_IMAGE) \ + send_msg(VARLSTCNT(12) ERR_JNLCRESTATUS, 7, CALLFROM, info->jnl_len, info->jnl, \ + info->fn_len, info->fn, info->status, 0, info->status2); \ + else \ + gtm_putmsg(VARLSTCNT1(11) ERR_JNLCRESTATUS, 7, CALLFROM, info->jnl_len, info->jnl,\ + info->fn_len, info->fn, info->status, PUT_SYS_ERRNO(info->status2)); \ + } else if (SS_NORMAL != info->status) \ + { \ + if (IS_GTM_IMAGE) \ + send_msg(VARLSTCNT(10) ERR_JNLCRESTATUS, 7, CALLFROM, info->jnl_len, \ + info->jnl, info->fn_len, info->fn, info->status); \ + else \ + gtm_putmsg(VARLSTCNT(10) ERR_JNLCRESTATUS, 7, CALLFROM, info->jnl_len, \ + info->jnl, info->fn_len, info->fn, info->status); \ + } \ +} +#define RETURN_ON_ERROR(info) \ +if (SYSCALL_ERROR(info->status) || SYSCALL_ERROR(info->status2)) \ +{ \ + int status; \ + F_CLOSE(channel, status);/* resets "channel" to FD_INVALID */ \ + if (NULL != jrecbuf_base) \ + { \ + free(jrecbuf_base); \ + jrecbuf_base = NULL; \ + } \ + return EXIT_ERR; \ +} + +#if defined(VMS) +#define ZERO_SIZE_IN_BLOCKS 127 /* 127 is RMS maximum blocks / write */ +#define ZERO_SIZE (ZERO_SIZE_IN_BLOCKS * DISK_BLOCK_SIZE) +#endif + +GBLREF jnl_gbls_t jgbl; +GBLREF boolean_t mupip_jnl_recover; +GBLREF jnl_process_vector *prc_vec; + +/* Create a journal file from info. + * If necessary, it renames journal file of same name. + * Note: jgbl.gbl_jrec_time must be set by callers + */ +uint4 cre_jnl_file(jnl_create_info *info) +{ + mstr filestr; + int org_fn_len, rename_fn_len, fstat; + uint4 ustatus; + char *org_fn, rename_fn[MAX_FN_LEN]; + error_def (ERR_JNLCRESTATUS); + error_def (ERR_JNLFNF); + + if (!info->no_rename) /* ***MAY*** be rename is required */ + { + if (SS_NORMAL != (info->status = prepare_unique_name((char *)info->jnl, info->jnl_len, "", "", + rename_fn, &rename_fn_len, &info->status2))) + { /* prepare_unique_name calls append_time_stamp which needs to open the info->jnl file. + * We are here because append_time_stamp failed to open info->jnl or something else. + * So check if info->jnl is present in the system */ + filestr.addr = (char *)info->jnl; + filestr.len = info->jnl_len; + if (FILE_NOT_FOUND != (fstat = gtm_file_stat(&filestr, NULL, NULL, FALSE, &ustatus))) + { + if (FILE_STAT_ERROR == fstat) + { + STATUS_MSG(info); /* for prepare_unique_name call */ + info->status = ustatus; + info->status2 = SS_NORMAL; + } + STATUS_MSG(info); + return EXIT_ERR; + } + if (IS_GTM_IMAGE) + send_msg(VARLSTCNT(4) ERR_JNLFNF, 2, filestr.len, filestr.addr); + else + gtm_putmsg(VARLSTCNT(4) ERR_JNLFNF, 2, filestr.len, filestr.addr); + STATUS_MSG(info); + info->status = info->status2 = SS_NORMAL; + info->no_rename = TRUE; /* We wanted to rename, but not required */ + info->no_prev_link = TRUE; /* No rename => no prev_link */ + } else + { + /* Note if info->no_prev_link == TRUE, we do not keep previous link, though rename can happen */ + if (JNL_ENABLED(info) && !info->no_prev_link) + { + memcpy(info->prev_jnl, rename_fn, rename_fn_len + 1); + info->prev_jnl_len = rename_fn_len; + } else + assert(info->no_prev_link); + } + } /* else we know for sure rename is not required */ + return (cre_jnl_file_common(info, rename_fn, rename_fn_len)); +} + +/* This creates info->jnl and if (!info->no_rename) then it renames existing info->jnl to be rename_fn */ +uint4 cre_jnl_file_common(jnl_create_info *info, char *rename_fn, int rename_fn_len) +{ + jnl_file_header *header; + unsigned char hdr_base[JNL_HDR_LEN + MAX_IO_BLOCK_SIZE]; + struct_jrec_pfin *pfin_record; + struct_jrec_pini *pini_record; + struct_jrec_epoch *epoch_record; + struct_jrec_eof *eof_record; + unsigned char *create_fn, fn_buff[MAX_FN_LEN]; + int create_fn_len, cre_jnl_rec_size, status, write_size, jrecbufbase_size; + fd_type channel; + char *jrecbuf, *jrecbuf_base; + gd_id jnlfile_id; +#if defined(VMS) + struct FAB fab; + struct NAM nam; + char es_buffer[MAX_FN_LEN], name_buffer[MAX_FN_LEN]; + uint4 blk, block, zero_size; + io_status_block_disk iosb; +#elif defined(UNIX) + struct stat stat_buf; + int fstat_res; + ZOS_ONLY(int realfiletag;) + int stat_res; + int group_id; + struct stat sb; + int perm; +#endif + trans_num db_tn; + uint4 temp_offset, temp_checksum; + uint4 jnl_fs_block_size; + + error_def(ERR_FILERENAME); + error_def(ERR_RENAMEFAIL); + error_def(ERR_JNLCRESTATUS); + error_def(ERR_PREMATEOF); + error_def(ERR_TEXT); + ZOS_ONLY(error_def(ERR_BADTAG);) + + jrecbuf = NULL; + if (info->no_rename) + { /* The only cases where no-renaming is possible are as follows + * (i) MUPIP SET JOURNAL where the new journal file name is different from the current journal file name + * (ii) For MUPIP BACKUP, MUPIP SET JOURNAL, GT.M Runtime and forw_phase_recovery, + * in case the current journal file does not exist (due to some abnormal condition). + * But in this case we cut the link and hence info->no_prev_link should be TRUE. + * The assert below tries to capture this as much as possible without introducing any new global variables. + */ + assert((IS_MUPIP_IMAGE && !jgbl.forw_phase_recovery) || (IS_GTM_IMAGE && info->no_prev_link)); + create_fn_len = info->jnl_len; + create_fn = info->jnl; + assert(0 == create_fn[create_fn_len]); + } else + { + create_fn = &fn_buff[0]; + if (SS_NORMAL != (info->status = prepare_unique_name((char *)info->jnl, (int)info->jnl_len, "", EXT_NEW, + (char *)create_fn, &create_fn_len, &info->status2))) + { + STATUS_MSG(info); + return EXIT_ERR; + } + } +#if defined(UNIX) + OPENFILE3((char *)create_fn, O_CREAT | O_EXCL | O_RDWR, 0600, channel); + if (-1 == channel) + { + info->status = errno; + STATUS_MSG(info); + return EXIT_ERR; + } +#if defined(__MVS__) + if (-1 == gtm_zos_set_tag(channel, TAG_BINARY, TAG_NOTTEXT, TAG_FORCE, &realfiletag)) + TAG_POLICY_SEND_MSG((char *)create_fn, errno, realfiletag, TAG_BINARY); +#endif + FSTAT_FILE(channel, &stat_buf, fstat_res); + if (-1 == fstat_res) + { + info->status = errno; + STATUS_MSG(info); + F_CLOSE(channel, status); + return EXIT_ERR; + } + /* check database file again. It was checked earlier so should still be ok. */ + STAT_FILE((sm_c_ptr_t)info->fn, &sb, stat_res); + if (-1 == stat_res) + { + info->status = errno; + STATUS_MSG(info); + F_CLOSE(channel, status); + return EXIT_ERR; + } + /* setup new group and permissions if indicated by the security rules. + * Use 0770 anded with current mode for the new mode if it is world writeable. + */ + gtm_set_group_and_perm(&sb, &group_id, &perm, (0770 & sb.st_mode)); + /* if group not the same then change group of temporary file */ + if ((-1 != group_id) && (group_id != stat_buf.st_gid) && (-1 == fchown(channel, -1, group_id))) + { + info->status = errno; + STATUS_MSG(info); + F_CLOSE(channel, status); + return EXIT_ERR; + } + if (-1 == FCHMOD(channel, perm)) + { + info->status = errno; + STATUS_MSG(info); + F_CLOSE(channel, status); /* resets "channel" to FD_INVALID */ + return EXIT_ERR; + } + jnl_fs_block_size = get_fs_block_size(channel); + /* We need to write the journal file header, followed by pini/epoch/pfin/eof records followed by 0-padding + * to ensure the final size of the journal file is filesystem-block-size aligned. + * To have a "jnl_fs_block_size" aligned buffer that is also a multiple of jnl_fs_block_size long, + * we need to allocate the needed size + 2 * jnl_fs_block_size. This will leave up to jnl_fs_block_size + * before the actual "used" part for alignment plus enough left after the "used" part to make up a buffer + * to write that is a multiple of jnl_fs_block_size. + */ + jrecbufbase_size = PINI_RECLEN + EPOCH_RECLEN + PFIN_RECLEN + EOF_RECLEN + (2 * jnl_fs_block_size); + jrecbuf_base = malloc(jrecbufbase_size); + jrecbuf = (char *)ROUND_UP2((uintszofptr_t)jrecbuf_base, jnl_fs_block_size); + memset(jrecbuf, 0, jnl_fs_block_size); + set_gdid_from_stat(&jnlfile_id, &stat_buf); +#elif defined(VMS) + nam = cc$rms_nam; + nam.nam$l_rsa = name_buffer; + nam.nam$b_rss = SIZEOF(name_buffer); + nam.nam$l_esa = es_buffer; + nam.nam$b_ess = SIZEOF(es_buffer); + nam.nam$b_nop = NAM$M_NOCONCEAL; + fab = cc$rms_fab; + fab.fab$l_nam = &nam; + fab.fab$b_org = FAB$C_SEQ; + fab.fab$b_rfm = FAB$C_FIX; + fab.fab$l_fop = FAB$M_UFO | FAB$M_MXV | FAB$M_CBT; + fab.fab$b_fac = FAB$M_GET | FAB$M_PUT | FAB$M_BIO; + fab.fab$b_shr = FAB$M_SHRGET | FAB$M_SHRPUT | FAB$M_UPI | FAB$M_NIL; + fab.fab$w_mrs = DISK_BLOCK_SIZE; + fab.fab$l_alq = info->alloc; + fab.fab$w_deq = info->extend; + fab.fab$l_fna = create_fn; + fab.fab$b_fns = create_fn_len; + info->status = sys$create(&fab); + if (0 == (info->status & 1)) + { + info->status2 = fab.fab$l_stv; /* store secondary status information */ + STATUS_MSG(info); + return EXIT_ERR; + } + channel = fab.fab$l_stv; + jrecbufbase_size = ZERO_SIZE; + jrecbuf_base = jrecbuf = malloc(ZERO_SIZE); + memset(jrecbuf, 0, ZERO_SIZE); + block = (JNL_HDR_LEN >> LOG2_DISK_BLOCK_SIZE); + assert(block * DISK_BLOCK_SIZE == JNL_HDR_LEN); + for (blk = block; blk < info->alloc; blk += ZERO_SIZE_IN_BLOCKS) + { + zero_size = (blk + ZERO_SIZE_IN_BLOCKS <= info->alloc) ? + ZERO_SIZE : (info->alloc - blk) * DISK_BLOCK_SIZE; + DO_FILE_WRITE(channel, blk * DISK_BLOCK_SIZE, jrecbuf, zero_size, info->status, info->status2); + STATUS_MSG(info); + RETURN_ON_ERROR(info); + } + memcpy(jnlfile_id.dvi, &nam.nam$t_dvi, SIZEOF(jnlfile_id.dvi)); + memcpy(jnlfile_id.did, &nam.nam$w_did, SIZEOF(jnlfile_id.did)); + memcpy(jnlfile_id.fid, &nam.nam$w_fid, SIZEOF(jnlfile_id.fid)); + jnl_fs_block_size = get_fs_block_size(channel); +#endif + info->checksum = jnl_get_checksum_entire((uint4 *)&jnlfile_id, SIZEOF(gd_id)); + /* Journal file header size relies on this assert */ + assert(256 == GTMCRYPT_RESERVED_HASH_LEN); + header = (jnl_file_header *)(ROUND_UP2((uintszofptr_t)hdr_base, jnl_fs_block_size)); + /* We have already saved previous journal file name in info */ + jfh_from_jnl_info(info, header); + header->recover_interrupted = mupip_jnl_recover; + assert(ROUND_UP2(JNL_HDR_LEN, jnl_fs_block_size) == JNL_HDR_LEN); + assert((unsigned char *)header + JNL_HDR_LEN <= ARRAYTOP(hdr_base)); + /* Although the real journal file header is REAL_JNL_HDR_LEN, we write the entire journal file header + * including padding (64K in Unix) so we write 0-padding instead of some garbage. Not necessary + * but makes analysis of journal file using dump utilities (like od) much easier. Also 0-initialization + * might help us in the future when we add some fields in the file header. The cost of the extra padding + * write is not so much since it is only once per journal file at creation time. All future writes of the + * file header write only the real file header and not the 0-padding. + */ + DO_FILE_WRITE(channel, 0, header, JNL_HDR_LEN, info->status, info->status2); + STATUS_MSG(info); + RETURN_ON_ERROR(info); + assert(DISK_BLOCK_SIZE >= EPOCH_RECLEN + EOF_RECLEN + PFIN_RECLEN + PINI_RECLEN); + pini_record = (struct_jrec_pini *)&jrecbuf[0]; + pini_record->prefix.jrec_type = JRT_PINI; + pini_record->prefix.forwptr = pini_record->suffix.backptr = PINI_RECLEN; + db_tn = info->csd->trans_hist.curr_tn; + pini_record->prefix.tn = db_tn; + pini_record->prefix.pini_addr = JNL_HDR_LEN; + pini_record->prefix.time = jgbl.gbl_jrec_time; /* callers must set it */ + temp_offset = JNL_HDR_LEN; + temp_checksum = ADJUST_CHECKSUM(INIT_CHECKSUM_SEED, temp_offset); + pini_record->prefix.checksum = ADJUST_CHECKSUM(temp_checksum, info->checksum); + pini_record->suffix.suffix_code = JNL_REC_SUFFIX_CODE; + assert(prc_vec); + memcpy((unsigned char*)&pini_record->process_vector[CURR_JPV], (unsigned char*)prc_vec, SIZEOF(jnl_process_vector)); + /* Already process_vector[ORIG_JPV] is memset 0 */ + pini_record->prefix.pini_addr = JNL_HDR_LEN; + /* EPOCHs are written unconditionally in Unix while they are written only for BEFORE_IMAGE in VMS */ + if (JNL_HAS_EPOCH(info)) + { + epoch_record = (struct_jrec_epoch *)&jrecbuf[PINI_RECLEN]; + epoch_record->prefix.jrec_type = JRT_EPOCH; + epoch_record->prefix.forwptr = epoch_record->suffix.backptr = EPOCH_RECLEN; + epoch_record->prefix.tn = db_tn; + epoch_record->prefix.pini_addr = JNL_HDR_LEN; + epoch_record->prefix.time = jgbl.gbl_jrec_time; + epoch_record->blks_to_upgrd = info->blks_to_upgrd; + epoch_record->free_blocks = info->free_blocks; + epoch_record->total_blks = info->total_blks; + temp_offset = JNL_HDR_LEN + PINI_RECLEN; + temp_checksum = ADJUST_CHECKSUM(INIT_CHECKSUM_SEED, temp_offset); + epoch_record->prefix.checksum = ADJUST_CHECKSUM(temp_checksum, info->checksum); + epoch_record->suffix.suffix_code = JNL_REC_SUFFIX_CODE; + QWASSIGN(epoch_record->jnl_seqno, info->reg_seqno); + pfin_record = (struct_jrec_pfin *)&jrecbuf[PINI_RECLEN + EPOCH_RECLEN]; + temp_offset = JNL_HDR_LEN + PINI_RECLEN + EPOCH_RECLEN; + temp_checksum = ADJUST_CHECKSUM(INIT_CHECKSUM_SEED, temp_offset); + pfin_record->prefix.checksum = ADJUST_CHECKSUM(temp_checksum, info->checksum); + eof_record = (struct_jrec_eof *)&jrecbuf[PINI_RECLEN + EPOCH_RECLEN + PFIN_RECLEN]; + temp_offset = JNL_HDR_LEN + PINI_RECLEN + EPOCH_RECLEN + PFIN_RECLEN; + temp_checksum = ADJUST_CHECKSUM(INIT_CHECKSUM_SEED, temp_offset); + eof_record->prefix.checksum = ADJUST_CHECKSUM(temp_checksum, info->checksum); + cre_jnl_rec_size = PINI_RECLEN + EPOCH_RECLEN + PFIN_RECLEN + EOF_RECLEN; + } else + { + pfin_record = (struct_jrec_pfin *)&jrecbuf[PINI_RECLEN]; + temp_offset = JNL_HDR_LEN + PINI_RECLEN; + temp_checksum = ADJUST_CHECKSUM(INIT_CHECKSUM_SEED, temp_offset); + pfin_record->prefix.checksum = ADJUST_CHECKSUM(temp_checksum, info->checksum); + eof_record = (struct_jrec_eof *)&jrecbuf[PINI_RECLEN + PFIN_RECLEN]; + temp_offset = JNL_HDR_LEN + PINI_RECLEN + PFIN_RECLEN; + temp_checksum = ADJUST_CHECKSUM(INIT_CHECKSUM_SEED, temp_offset); + eof_record->prefix.checksum = ADJUST_CHECKSUM(temp_checksum, info->checksum); + cre_jnl_rec_size = PINI_RECLEN + PFIN_RECLEN + EOF_RECLEN; + } + pfin_record->prefix.jrec_type = JRT_PFIN; + pfin_record->prefix.forwptr = pfin_record->suffix.backptr = PFIN_RECLEN; + pfin_record->prefix.tn = db_tn; + pfin_record->prefix.pini_addr = JNL_HDR_LEN; + pfin_record->prefix.time = jgbl.gbl_jrec_time; + pfin_record->suffix.suffix_code = JNL_REC_SUFFIX_CODE; + eof_record->prefix.jrec_type = JRT_EOF; + eof_record->prefix.forwptr = eof_record->suffix.backptr = EOF_RECLEN; + eof_record->prefix.tn = db_tn; + eof_record->prefix.pini_addr = JNL_HDR_LEN; + eof_record->prefix.time = jgbl.gbl_jrec_time; + QWASSIGN(eof_record->jnl_seqno, info->reg_seqno); + eof_record->suffix.suffix_code = JNL_REC_SUFFIX_CODE; + /* Assert that the journal file header and journal records are all in sync with respect to the db tn. */ + assert(header->bov_tn == db_tn); + assert(header->eov_tn == db_tn); + /* Write the journal records, but also 0-fill the tail of the journal file enough to fill + * an aligned filesystem-block-size boundary. Take into account that the file header is already written. + */ + write_size = ROUND_UP2((JNL_HDR_LEN + cre_jnl_rec_size), jnl_fs_block_size) - JNL_HDR_LEN; + assert((jrecbuf + write_size) <= (jrecbuf_base + jrecbufbase_size)); + /* Assert that initial virtual-size of the journal file (which is nothing but the journal allocation) + * gives us enough space to keep the journal file header + padding + PINI/PFIN/EPOCH/EOF records. + * Do the assertion in units of 512-byte blocks as that keeps the values well under 4G. Keeping the + * units in bytes causes the highest autoswitchlimit value to round up to 4G effectively becoming 0 + * on certain platforms and therefore failing the assert. + */ + assert(ROUND_UP2(header->virtual_size, jnl_fs_block_size/DISK_BLOCK_SIZE) + > DIVIDE_ROUND_UP(JNL_HDR_LEN + write_size, DISK_BLOCK_SIZE)); + DO_FILE_WRITE(channel, JNL_HDR_LEN, jrecbuf, write_size, info->status, info->status2); + STATUS_MSG(info); + RETURN_ON_ERROR(info); + UNIX_ONLY(GTM_FSYNC(channel, status);) + F_CLOSE(channel, status); /* resets "channel" to FD_INVALID */ + free(jrecbuf_base); + jrecbuf_base = NULL; + if (info->no_rename) + return EXIT_NRM; + /* Say, info->jnl = a.mjl + * So system will have a.mjl and a.mjl_new for a crash before the following call + * Following does rename of a.mjl to a.mjl_timestamp. + * So system will have a.mjl_timestamp and a.mjl_new for a crash after this call + */ + if (SS_NORMAL != (info->status = gtm_rename((char *)info->jnl, (int)info->jnl_len, + (char *)rename_fn, rename_fn_len, &info->status2))) + { + if (IS_GTM_IMAGE) + send_msg(VARLSTCNT(6) ERR_RENAMEFAIL, 4, info->jnl_len, info->jnl, rename_fn_len, rename_fn); + else + gtm_putmsg(VARLSTCNT(6) ERR_RENAMEFAIL, 4, info->jnl_len, info->jnl, rename_fn_len, rename_fn); + STATUS_MSG(info); + return EXIT_ERR; + } + /* Following does rename of a.mjl_new to a.mjl. + * So system will have a.mjl_timestamp as previous generation and a.mjl as new/current journal file + */ + if (SS_NORMAL != (info->status = gtm_rename((char *)create_fn, create_fn_len, + (char *)info->jnl, (int)info->jnl_len, &info->status2))) + { + if (IS_GTM_IMAGE) + send_msg(VARLSTCNT(6) ERR_RENAMEFAIL, 4, info->jnl_len, info->jnl, rename_fn_len, rename_fn); + else + gtm_putmsg(VARLSTCNT(6) ERR_RENAMEFAIL, 4, info->jnl_len, info->jnl, rename_fn_len, rename_fn); + STATUS_MSG(info); + return EXIT_ERR; + } + if (IS_GTM_IMAGE) + send_msg(VARLSTCNT (6) ERR_FILERENAME, 4, info->jnl_len, info->jnl, rename_fn_len, rename_fn); + else + gtm_putmsg(VARLSTCNT (6) ERR_FILERENAME, 4, info->jnl_len, info->jnl, rename_fn_len, rename_fn); + DEBUG_ONLY( + if (gtm_white_box_test_case_enabled && (WBTEST_JNL_CREATE_INTERRUPT == gtm_white_box_test_case_number)) + { + LONG_SLEEP(600); + assert(FALSE); /* Should be killed before that */ + } + ) + return EXIT_NRM; +} diff --git a/sr_port/cre_jnl_file_intrpt_rename.c b/sr_port/cre_jnl_file_intrpt_rename.c new file mode 100644 index 0000000..34089be --- /dev/null +++ b/sr_port/cre_jnl_file_intrpt_rename.c @@ -0,0 +1,120 @@ +/**************************************************************** + * * + * Copyright 2003, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_rename.h" +#include "gtm_file_remove.h" +#include "gtm_file_stat.h" +#include "gtmmsg.h" +#include "iosp.h" +#include "gdsroot.h" +#include "gdsbt.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "jnl.h" +#include "send_msg.h" +#include "gtmimagename.h" + +void cre_jnl_file_intrpt_rename(int fn_len, sm_uc_ptr_t fn) +{ + mstr filestr; + int status1, status2, ext_new_jnl_fn_len; + uint4 status, ustatus; + unsigned char ext_new_jnl_fn[MAX_FN_LEN]; + error_def(ERR_FILEPARSE); + error_def(ERR_RENAMEFAIL); + error_def(ERR_FILEDELFAIL); + error_def(ERR_FILEDEL); + error_def(ERR_FILERENAME); + + filestr.addr = (char *)fn; + filestr.len = fn_len; + prepare_unique_name((char *)fn, fn_len, "", EXT_NEW, (char *)ext_new_jnl_fn, &ext_new_jnl_fn_len, &ustatus); + assert(SS_NORMAL == ustatus); + status1 = gtm_file_stat(&filestr, NULL, NULL, FALSE, &ustatus); + if (FILE_STAT_ERROR == status1) + { + if (IS_GTM_IMAGE) + send_msg(VARLSTCNT(5) ERR_FILEPARSE, 2, filestr.len, filestr.addr, ustatus); + else + gtm_putmsg(VARLSTCNT(5) ERR_FILEPARSE, 2, filestr.len, filestr.addr, ustatus); + return; + } + filestr.addr = (char *)ext_new_jnl_fn; + filestr.len = ext_new_jnl_fn_len; + status2 = gtm_file_stat(&filestr, NULL, NULL, FALSE, &ustatus); + if (FILE_STAT_ERROR == status2) + { + if (IS_GTM_IMAGE) + send_msg(VARLSTCNT(5) ERR_FILEPARSE, 2, filestr.len, filestr.addr, ustatus); + else + gtm_putmsg(VARLSTCNT(5) ERR_FILEPARSE, 2, filestr.len, filestr.addr, ustatus); + return; + } + if (FILE_NOT_FOUND == status1) + { + if (FILE_PRESENT == status2) + { + status = gtm_rename(filestr.addr, (int)filestr.len, (char *)fn, fn_len, &ustatus); + if (SYSCALL_ERROR(status)) + { + if (IS_GTM_IMAGE) + { + VMS_ONLY(send_msg(VARLSTCNT(8) ERR_RENAMEFAIL, 4, filestr.len, filestr.addr, + fn_len, fn, status, ustatus);) + UNIX_ONLY(send_msg(VARLSTCNT(7) ERR_RENAMEFAIL, 4, filestr.len, filestr.addr, + fn_len, fn, status);) + } else + { + VMS_ONLY(gtm_putmsg(VARLSTCNT(8) ERR_RENAMEFAIL, 4, filestr.len, filestr.addr, + fn_len, fn, status, ustatus);) + UNIX_ONLY(gtm_putmsg(VARLSTCNT(7) ERR_RENAMEFAIL, 4, filestr.len, filestr.addr, + fn_len, fn, status);) + } + } else + { + if (IS_GTM_IMAGE) + send_msg(VARLSTCNT(6) ERR_FILERENAME, 4, (int)filestr.len, filestr.addr, fn_len, fn); + else + gtm_putmsg(VARLSTCNT(6) ERR_FILERENAME, 4, filestr.len, filestr.addr, fn_len, fn); + } + } + } else + { + if (FILE_PRESENT == status2) + { + status = gtm_file_remove(filestr.addr, (int)filestr.len, &ustatus); + if (SYSCALL_ERROR(status)) + { + if (IS_GTM_IMAGE) + { + VMS_ONLY(send_msg(VARLSTCNT(6) ERR_FILEDELFAIL, 2, filestr.len, filestr.addr, + status, ustatus);) + UNIX_ONLY(send_msg(VARLSTCNT(5) ERR_FILEDELFAIL, 2, filestr.len, filestr.addr, status);) + } else + { + VMS_ONLY(gtm_putmsg(VARLSTCNT(6) ERR_FILEDELFAIL, 2, filestr.len, filestr.addr, + status, ustatus);) + UNIX_ONLY(gtm_putmsg(VARLSTCNT(5) ERR_FILEDELFAIL, 2, filestr.len, filestr.addr, status);) + } + } else + { + if (IS_GTM_IMAGE) + send_msg(VARLSTCNT(4) ERR_FILEDEL, 2, filestr.len, filestr.addr); + else + gtm_putmsg(VARLSTCNT(4) ERR_FILEDEL, 2, filestr.len, filestr.addr); + } + } + } +} diff --git a/sr_port/cre_private_code_copy.c b/sr_port/cre_private_code_copy.c new file mode 100644 index 0000000..a335b2a --- /dev/null +++ b/sr_port/cre_private_code_copy.c @@ -0,0 +1,57 @@ +/**************************************************************** + * * + * Copyright 2002, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "iosp.h" +#include "error.h" +#include "rtnhdr.h" +#include "inst_flush.h" +#include "private_code_copy.h" +#include "stack_frame.h" +#include "gtm_text_alloc.h" +#include "gtm_string.h" + +CONDITION_HANDLER(cre_priv_ch) +{ + error_def(UNIX_ONLY(ERR_MEMORY) VMS_ONLY(ERR_VMSMEMORY);) + + START_CH; + if (SIGNAL == UNIX_ONLY(ERR_MEMORY) VMS_ONLY(ERR_VMSMEMORY)) + { + UNWIND(NULL, NULL); /* ignore "lack-of-memory" error, rather not set breakpoint than error out in such a case */ + } + NEXTCH; +} + +uint4 cre_private_code_copy(rhdtyp *rtn) +{ + unsigned char *new_ptext; + int code_size; + + error_def(UNIX_ONLY(ERR_MEMORY) VMS_ONLY(ERR_VMSMEMORY);) + +#ifdef USHBIN_SUPPORTED + assert(NULL != rtn->shlib_handle); /* don't need private copy if not shared */ + assert(NULL == rtn->shared_ptext_adr); /* if already private, we shouldn't be calling this routine */ + code_size = (int)(rtn->ptext_end_adr - rtn->ptext_adr) ; + ESTABLISH_RET(cre_priv_ch, UNIX_ONLY(ERR_MEMORY) VMS_ONLY(ERR_VMSMEMORY)); + new_ptext = GTM_TEXT_ALLOC(code_size); + REVERT; + memcpy(new_ptext, rtn->ptext_adr, code_size); + adjust_frames(rtn->ptext_adr, rtn->ptext_end_adr, new_ptext); + rtn->shared_ptext_adr = rtn->ptext_adr; + rtn->ptext_adr = new_ptext; + rtn->ptext_end_adr = new_ptext + code_size; + inst_flush(new_ptext, code_size); +#endif + return SS_NORMAL; +} diff --git a/sr_port/create_dummy_gbldir.c b/sr_port/create_dummy_gbldir.c new file mode 100644 index 0000000..4741588 --- /dev/null +++ b/sr_port/create_dummy_gbldir.c @@ -0,0 +1,138 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include +#include "gtm_string.h" +#include "gtm_stdio.h" +#include "gtm_fcntl.h" +#include "gtm_unistd.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "mlkdef.h" +#include "filestruct.h" +#include "gbldirnam.h" +#include "hashtab_mname.h" +#include "hashtab.h" + +#ifdef GTM64 +#define SAVE_ADDR_REGION \ +{ \ + int4 *tmp = (int *)&(addr->regions); \ + *int4_ptr++ = *tmp; \ + int4_ptr++; \ +} +#else /* GTM64 */ +#define SAVE_ADDR_REGION \ + *int4_ptr++ = (int4)addr->regions; +#endif /* GTM64 */ + +static gdr_name *gdr_name_head; + +gd_addr *create_dummy_gbldir(void) +{ + header_struct *header; + gd_addr *addr; + gdr_name *name; + gd_binding *map, *map_top; + gd_region *region; + gd_region *region_top; + gd_segment *segment; + int4 *int4_ptr; + uint4 t_offset, size; + + size = SIZEOF(header_struct) + SIZEOF(gd_addr) + 3 * SIZEOF(gd_binding) + 1 * SIZEOF(gd_region) + 1 * SIZEOF(gd_segment); + header = (header_struct *)malloc(ROUND_UP(size, DISK_BLOCK_SIZE)); + memset(header, 0, ROUND_UP(size, DISK_BLOCK_SIZE)); + header->filesize = size; + size = ROUND_UP(size, DISK_BLOCK_SIZE); + memcpy(header->label, GDE_LABEL_LITERAL, SIZEOF(GDE_LABEL_LITERAL)); + addr = (gd_addr *)((char *)header + SIZEOF(header_struct)); + addr->max_rec_size = 256; + addr->maps = (gd_binding*)((UINTPTR_T)addr + SIZEOF(gd_addr)); + addr->n_maps = 3; + addr->regions = (gd_region*)((INTPTR_T)(addr->maps) + 3 * SIZEOF(gd_binding)); + addr->n_regions = 1; + addr->segments = (gd_segment*)((INTPTR_T)(addr->regions) + SIZEOF(gd_region)); + addr->n_segments = 1; + addr->link = 0; + addr->tab_ptr = 0; + addr->id = 0; + addr->local_locks = 0; + addr->end = (UINTPTR_T)(addr->segments + 1 * SIZEOF(gd_segment)); + int4_ptr = (int4*)(addr->maps); + *int4_ptr++ = 0x232FFFFF; + *int4_ptr++ = 0xFFFFFFFF; + *int4_ptr++ = 0xFFFFFFFF; + *int4_ptr++ = 0xFFFFFFFF; + *int4_ptr++ = 0xFFFFFFFF; + *int4_ptr++ = 0xFFFFFFFF; + *int4_ptr++ = 0xFFFFFFFF; + *int4_ptr++ = 0xFFFFFFFF; + SAVE_ADDR_REGION + *int4_ptr++ = 0x24FFFFFF; + *int4_ptr++ = 0xFFFFFFFF; + *int4_ptr++ = 0xFFFFFFFF; + *int4_ptr++ = 0xFFFFFFFF; + *int4_ptr++ = 0xFFFFFFFF; + *int4_ptr++ = 0xFFFFFFFF; + *int4_ptr++ = 0xFFFFFFFF; + *int4_ptr++ = 0xFFFFFFFF; + SAVE_ADDR_REGION + *int4_ptr++ = 0xFFFFFFFF; + *int4_ptr++ = 0xFFFFFFFF; + *int4_ptr++ = 0xFFFFFFFF; + *int4_ptr++ = 0xFFFFFFFF; + *int4_ptr++ = 0xFFFFFFFF; + *int4_ptr++ = 0xFFFFFFFF; + *int4_ptr++ = 0xFFFFFFFF; + *int4_ptr++ = 0xFFFFFFFF; + SAVE_ADDR_REGION + region = (gd_region*)(addr->regions); + segment = (gd_segment*)(addr->segments); + region->rname_len = 7; + memcpy(region->rname,"DEFAULT",7); + + for (map = addr->maps, map_top = map + addr->n_maps; map < map_top ; map++) + { + t_offset = map->reg.offset; + map->reg.addr = (gd_region *)((char *)addr + t_offset); + } + for (region = addr->regions, region_top = region + addr->n_regions; region < region_top ; region++) + { + t_offset = region->dyn.offset; + region->dyn.addr = (gd_segment *)((char *)addr + t_offset); + } + /* Should be using gd_id_ptr_t below, but ok for now since malloc won't return > 4G + * and since addr->id is a 4-byte pointer only until we change the format of the global directory. + */ + addr->id = (gd_id *)malloc(SIZEOF(gd_id)); + memset(addr->id, 0, SIZEOF(gd_id)); + + addr->tab_ptr = (hash_table_mname *)malloc(SIZEOF(hash_table_mname)); + init_hashtab_mname((hash_table_mname *)addr->tab_ptr, 0, HASHTAB_NO_COMPACT, HASHTAB_NO_SPARE_TABLE ); + + name = (gdr_name *)malloc(SIZEOF(gdr_name)); + MALLOC_CPY_LIT(name->name.addr, "DUMMY.GLD"); + if (gdr_name_head) + name->link = (gdr_name *)gdr_name_head; + else + name->link = 0; + gdr_name_head = name; + gdr_name_head->gd_ptr = addr; + return addr; +} diff --git a/sr_port/create_fatal_error_zshow_dmp.c b/sr_port/create_fatal_error_zshow_dmp.c new file mode 100644 index 0000000..24a3a15 --- /dev/null +++ b/sr_port/create_fatal_error_zshow_dmp.c @@ -0,0 +1,50 @@ +/**************************************************************** + * * + * Copyright 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#ifdef VMS +#include +#endif +#ifdef UNIX +#include +#endif + +#include "error.h" +#include "jobexam_process.h" +#include "gtmdbglvl.h" +#include "create_fatal_error_zshow_dmp.h" + +LITDEF mval gtmfatal_error_filename = DEFINE_MVAL_LITERAL(MV_STR, 0, 0, SIZEOF(GTMFATAL_ERROR_DUMP_FILENAME) - 1, + GTMFATAL_ERROR_DUMP_FILENAME, 0, 0); + +GBLREF int4 exi_condition; +GBLREF uint4 gtmDebugLevel; +GBLREF volatile int4 gtmMallocDepth; + +/* On VMS, SIGNAL is sig->chf$l_sig_name (a parameter to mdb_condition_handler) so needs to be passed in */ +void create_fatal_error_zshow_dmp(int4 signal, boolean_t repeat_error) +{ + mval dummy_mval; + int4 save_SIGNAL; /* On UNIX this is exi_condition */ + + UNIX_ONLY(PRN_ERROR); + if (UNIX_ONLY(0 == gtmMallocDepth && ((SIGBUS != exi_condition && SIGSEGV != exi_condition) || + (GDL_ZSHOWDumpOnSignal & gtmDebugLevel))) + VMS_ONLY((SS$_ACCVIO != signal) || (GDL_ZSHOWDumpOnSignal & gtmDebugLevel))) + { /* If dumpable condition, create traceback file of M stack info and such */ + /* On Unix, we need to push out our error now before we potentially overlay it in jobexam_process() */ + /* Create dump file */ + UNIX_ONLY(save_SIGNAL = SIGNAL); /* Signal might be modified by jobexam_process() */ + jobexam_process((mval *)>mfatal_error_filename, &dummy_mval); + UNIX_ONLY(SIGNAL = save_SIGNAL); + } +} diff --git a/sr_port/create_fatal_error_zshow_dmp.h b/sr_port/create_fatal_error_zshow_dmp.h new file mode 100644 index 0000000..4572267 --- /dev/null +++ b/sr_port/create_fatal_error_zshow_dmp.h @@ -0,0 +1,19 @@ +/**************************************************************** + * * + * Copyright 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef CREATE_FATAL_ERROR_ZSHOW_DMP_H_ +# define CREATE_FATAL_ERROR_ZSHOW_DMP_H_ + +#define GTMFATAL_ERROR_DUMP_FILENAME "GTM_FATAL_ERROR" + +void create_fatal_error_zshow_dmp(int4 signal, boolean_t repeat_error); + +#endif diff --git a/sr_port/crit_wake.h b/sr_port/crit_wake.h new file mode 100644 index 0000000..f1fe01a --- /dev/null +++ b/sr_port/crit_wake.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __CRIT_WAKE_H__ +#define __CRIT_WAKE_H__ + +int crit_wake(sm_uint_ptr_t pid); + +#endif diff --git a/sr_port/crit_wake_alarm.c b/sr_port/crit_wake_alarm.c new file mode 100644 index 0000000..64266e2 --- /dev/null +++ b/sr_port/crit_wake_alarm.c @@ -0,0 +1,22 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gt_timer.h" +#include "crit_wake_alarm.h" + +GBLDEF bool crit_timer_expired; + +void crit_wake_alarm(void) +{ + crit_timer_expired = TRUE; + GT_WAKE; +} diff --git a/sr_port/crit_wake_alarm.h b/sr_port/crit_wake_alarm.h new file mode 100644 index 0000000..25a7ef2 --- /dev/null +++ b/sr_port/crit_wake_alarm.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __CRIT_WAKE_ALARM_H__ +#define __CRIT_WAKE_ALARM_H__ + +void crit_wake_alarm(void); + +#endif diff --git a/sr_port/cryptdef.h b/sr_port/cryptdef.h new file mode 100644 index 0000000..3110f0e --- /dev/null +++ b/sr_port/cryptdef.h @@ -0,0 +1,47 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +typedef struct +{ + uint4 sid[16]; + uint4 exp_date; + unsigned char gtm_serial[8]; +} gtm_id_block; + +typedef struct +{ + gtm_id_block plaintext; + unsigned char key[SIZEOF(gtm_id_block)]; + gtm_id_block cryptext; +} gtm_id_struct; + +#define CRYPT_CHKSYSTEM { if (!licensed) \ + { \ + if ((lkid & 4) == 4) \ + { \ + GTMASSERT; \ + } \ + lkid++; \ + } \ + } +#if defined (DEBUG) || defined (NOLICENSE) +#define LP_LICENSED(a,b,c,d,e,f,g,h,i,j) 1 /* equivalent to SS$_NORMAL */ +#if defined (VMS) +#define LP_ACQUIRE(a,b,c,d) lp_id(d) +#else +#define LP_ACQUIRE(a,b,c,d) 1 +#endif +#define LP_CONFIRM(a,b) 1 +#else +#define LP_LICENSED(a,b,c,d,e,f,g,h,i,j) lp_licensed(a,b,c,d,e,f,g,h,i,j) +#define LP_ACQUIRE(a,b,c,d) lp_acquire(a,b,c,d) +#define LP_CONFIRM(a,b) lp_confirm(a,b) +#endif diff --git a/sr_port/ctrlc_handler_dummy.c b/sr_port/ctrlc_handler_dummy.c new file mode 100644 index 0000000..64157e5 --- /dev/null +++ b/sr_port/ctrlc_handler_dummy.c @@ -0,0 +1,15 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#include "ctrlc_handler_dummy.h" + +void ctrlc_handler_dummy(void) +{ +} diff --git a/sr_port/ctrlc_handler_dummy.h b/sr_port/ctrlc_handler_dummy.h new file mode 100644 index 0000000..99ee2aa --- /dev/null +++ b/sr_port/ctrlc_handler_dummy.h @@ -0,0 +1,16 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#ifndef __CTRLC_HANDLER_DUMMY_H__ +#define __CTRLC_HANDLER_DUMMY_H__ + +void ctrlc_handler_dummy(void); + +#endif diff --git a/sr_port/cvtparm.c b/sr_port/cvtparm.c new file mode 100644 index 0000000..76ed5cb --- /dev/null +++ b/sr_port/cvtparm.c @@ -0,0 +1,126 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "compiler.h" +#include "stringpool.h" +#include "io_params.h" +#include "io.h" +#include "iottdef.h" +#include "cvtparm.h" +#include "mvalconv.h" +#include "cvttime.h" +#include "cvtprot.h" + +GBLREF spdesc stringpool; +LITREF unsigned char io_params_size[]; + +#define IOP_DESC(a,b,c,d,e) {d, e} +LITDEF dev_ctl_struct dev_param_control[] = +{ +#include "iop.h" +}; +#undef IOP_DESC + +int4 cvtparm(int iocode, mval *src, mval *dst) +{ + error_def(ERR_DEVPARTOOBIG); + error_def(ERR_DEVPARPROT); + + int4 status, nl, tim[2]; + int siz, cnt, strlen; + short ns; + unsigned char *cp, msk; + io_termmask lngmsk; + + assert(MV_DEFINED(src)); + strlen = -1; + siz = io_params_size[iocode]; + ENSURE_STP_FREE_SPACE(siz + 1); + switch(dev_param_control[iocode].source_type) + { + case IOP_SRC_INT: + assert(siz == SIZEOF(int4) || siz == SIZEOF(short)); + MV_FORCE_NUM(src); + nl = MV_FORCE_INT(src); + if (siz == SIZEOF(int4)) + cp = (unsigned char *)&nl; + else + { + assert (siz == SIZEOF(short)); + + ns = (short) nl; + cp = (unsigned char *) &ns; + } + break; + case IOP_SRC_STR: + assert(siz == IOP_VAR_SIZE); + MV_FORCE_STR(src); + if (src->str.len > 255) /*one byte string lengths within a parameter string*/ + return (int4) ERR_DEVPARTOOBIG; + strlen = src->str.len; + siz = strlen + SIZEOF(unsigned char); + cp = (unsigned char *) src->str.addr; + break; + case IOP_SRC_MSK: + MV_FORCE_STR(src); + assert(siz == SIZEOF(int4)); + nl = 0; + for (cp = (unsigned char *) src->str.addr, cnt = src->str.len ; cnt > 0 ; cnt--) + nl |= (1 << *cp++); + cp = (unsigned char *) &nl; + break; + case IOP_SRC_LNGMSK: + MV_FORCE_STR(src); + assert(siz == SIZEOF(io_termmask)); + memset(&lngmsk, 0, SIZEOF(io_termmask)); + for (cp = (unsigned char *) src->str.addr, cnt = src->str.len ; cnt > 0 ; cnt--) + { + msk = *cp++; + nl = msk / 32; + lngmsk.mask[nl] |= (1 << (msk - (nl * 32))); + } + cp = (unsigned char *) &lngmsk; + break; + case IOP_SRC_PRO: + assert(siz == SIZEOF(unsigned char)); + MV_FORCE_STR(src); + nl = cvtprot(src->str.addr, src->str.len); + if (nl == -1) + return (int4) ERR_DEVPARPROT; + msk = nl; + cp = &msk; + break; + case IOP_SRC_TIME: + status = cvttime(src, tim); + if ((status & 1) == 0) + return status; + siz = SIZEOF(tim) ; + cp = (unsigned char *) tim ; + break; + default: + assert(FALSE); + } + dst->mvtype = MV_STR; + dst->str.addr = (char *) stringpool.free; + dst->str.len = siz; + if (strlen >= 0) + { + *stringpool.free++ = strlen; + siz -= SIZEOF(char); + } + memcpy(stringpool.free, cp, siz); + stringpool.free += siz; + return 0; +} diff --git a/sr_port/cvtparm.h b/sr_port/cvtparm.h new file mode 100644 index 0000000..b5e6da6 --- /dev/null +++ b/sr_port/cvtparm.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef CVTPARM_INCLUDED +#define CVTPARM_INCLUDED + +int4 cvtparm(int iocode, mval *src, mval *dst); + +#endif /* CVTPARM_INCLUDED */ diff --git a/sr_port/cvtprot.h b/sr_port/cvtprot.h new file mode 100644 index 0000000..241d4b9 --- /dev/null +++ b/sr_port/cvtprot.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __CVTPROT_H__ +#define __CVTPROT_H__ + + int cvtprot(char *cp, short cnt); + +#endif diff --git a/sr_port/cvttime.h b/sr_port/cvttime.h new file mode 100644 index 0000000..d08991e --- /dev/null +++ b/sr_port/cvttime.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef CVTTIME_INCLUDED +#define CVTTIME_INCLUDED + +int4 cvttime(mval *src, int4 *tim); + +#endif /* CVTTIME_INCLUDED */ diff --git a/sr_port/cws_insert.h b/sr_port/cws_insert.h new file mode 100644 index 0000000..5c24d80 --- /dev/null +++ b/sr_port/cws_insert.h @@ -0,0 +1,82 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __CWS_INSERT_H__ +#define __CWS_INSERT_H__ + +GBLREF hash_table_int4 cw_stagnate; +GBLREF boolean_t cw_stagnate_reinitialized; + +/* Usually a process does not need the cw_stagnate hash table as it is used only in the final retry. + * But CWS_RESET (to ensure the hashtable is reinitialized) is done in a lot of places and almost every transaction. + * To avoid unnecessary overhead in the reset, we start from a small initial value for CWS_INITIAL_SIZE. + * When necessary hash rounties will expand automatically. + */ +#define CWS_INITIAL_SIZE 4 + +/* Initialize the cw_stagnate hash-table */ +#define CWS_INIT \ +{ \ + init_hashtab_int4(&cw_stagnate, CWS_INITIAL_SIZE, HASHTAB_COMPACT, HASHTAB_SPARE_TABLE); \ + cw_stagnate_reinitialized = TRUE; \ +} + +/* macro CWS_INSERT appends a block_id onto an hashtable of block_id's + * cw_stagnate is the address of the hashtable + * it is initially NULL + * CWS_INITIAL_SIZE is the initial size of the hash table + * table is enlarged each time it fills up + * The hashtable is allocated the first time this routine is* called, and the contents of it are reset by calling cws_reset + * in t_begin, t_begin_crit and tp_hist. The hashtable expands by itself. + */ + +GBLREF boolean_t mu_reorg_in_swap_blk; +GBLREF int4 cws_reorg_remove_index; +GBLREF block_id cws_reorg_remove_array[]; + +#define CWS_INSERT(block) \ +{ \ + ht_ent_int4 *dummy; \ + boolean_t new_entry; \ + \ + cw_stagnate_reinitialized = FALSE; \ + new_entry = add_hashtab_int4(&cw_stagnate, (uint4 *)(&block), HT_VALUE_DUMMY, &dummy); \ + /* If in mu_swap_blk and a new entry was added to the hashtable, note this block number \ + * in the cws_reorg_remove_array for later removal in the next iteration of the for \ + * loop in "mu_swap_blk" (see mu_swap_blk.c for more detail). \ + */ \ + if (mu_reorg_in_swap_blk && new_entry) \ + { \ + CWS_REORG_INSERT(block); \ + } \ +} + +#define CWS_REORG_REMOVE_ARRAYSIZE ((4 * MAX_BT_DEPTH) + 2) /* see mu_swap_blk.c for detail on this calculation */ +#define CWS_REORG_REMOVE_MAXINDEX (CWS_REORG_REMOVE_ARRAYSIZE - 1) + +#define CWS_REORG_INSERT(blk) \ +{ \ + assert(cws_reorg_remove_index < CWS_REORG_REMOVE_MAXINDEX); \ + cws_reorg_remove_array[++cws_reorg_remove_index] = (blk); \ +} + +/* the use of the variable cw_stagnate_reinitialized to optimize CWS_RESET assumes that all calls to + * add_hashtab_int4() are done through CWS_INSERT macro. + */ +#define CWS_RESET \ +{ /* if a transaction did not use cw_stagnate hash table, there is no need to reset it */ \ + if (!cw_stagnate_reinitialized) \ + { \ + reinitialize_hashtab_int4(&cw_stagnate); \ + cw_stagnate_reinitialized = TRUE; \ + } \ +} +#endif diff --git a/sr_port/d.mpt b/sr_port/d.mpt new file mode 100644 index 0000000..a9e9a2e --- /dev/null +++ b/sr_port/d.mpt @@ -0,0 +1,24 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 1987, 2003 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +%D ;GT.M %D utility - write current date as [d]d-mmm-yy + ;invoke at INT to set %DAT to the current date as [d]d-mmm-yy + ;invoke at FUNC as an extrinsic special variable + ;supplied for illustration and compatibility + ;use of inline code is easy and efficient + ; + w $$FUNC() + q +INT s %DAT=$$FUNC() + q +FUNC() n h,monyear + s monyear=$S($ZDATEFORM:"-MON-YEAR",1:"-MON-YY") + s h=$h + q +$zd(h,"DD")_$zd(h,monyear) diff --git a/sr_port/date.mpt b/sr_port/date.mpt new file mode 100644 index 0000000..0fd3c37 --- /dev/null +++ b/sr_port/date.mpt @@ -0,0 +1,83 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 1987, 2003 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +%DATE ;GT.M %DATE utility - returns $H format date in %DN + ;invoke at INT with %DS to by pass interactive prompt + ;formats accepted include: + ; days as 1 or 2 digits + ; months as 1 or 2 digits or as unique alpha prefixes + ; years as 2 or 4 digits + ; "T" or "t" for today with an optional +/- offset + ; except for the +/- offset any non alphameric character(s) + ; are accepted as delimiters + ; numeric months must precede days + ; alpha months may precede or follow days + ; a missing year is defaulted to this year + ; null input is defaulted to today + ; + n %DS + f r !,"Date: ",%DS s %DN=$$FUNC(%DS) q:%DN'="" w " - invalid date" + q +INT s %DN=$$FUNC($g(%DS)) + q +FUNC(dt) + n cp,dd,dir,ilen,mm,tok,tp,yy,dh,zd + i $g(dt)="" q +$H + s ilen=$l(dt)+1,cp=1 d advance + i $e("TODAY",1,$l(tok))=$tr(tok,"today","TODAY") q $$incr(+$H) + i $e("TOMORROW",1,$l(tok))=$tr(tok,"tomrw","TOMRW") q $$incr($H+1) + i $e("YESTERDAY",1,$l(tok))=$tr(tok,"yestrday","YESTRDAY") q $$incr($H-1) + i dir?1A s mm=$$amon(tok) q:mm=0 "" d advance s dd=tok q $$date + i tok<1 q "" + s mm=tok d advance + i dir?1A s dd=mm,mm=$$amon(tok) q:mm=0 "" q $$date + i mm<13 s dd=tok q $$date + q "" + ; +advance f cp=cp:1:ilen q:$e(dt,cp)?1AN + s dir=$e(dt,cp) + i dir?1A f tp=cp+1:1:ilen q:$e(dt,tp)'?1A + e i dir?1N f tp=cp+1:1:ilen q:$e(dt,tp)'?1N + e s tok="" q + s tok=$e(dt,cp,tp-1) + s cp=tp + q +incr(h) + f cp=cp:1:ilen q:"+-"[$e(dt,cp) + i cp'=ilen s dd=$e(dt,cp) d advance i dir?.1N,cp=ilen q h+(dd_tok) + q h + ; +amon(mm) + s mm=$tr(mm,"abcdefghijklmnopqrstuvwxyz","ABCDEFGHIJKLMNOPQRSTUVWXYZ") + i $l(mm)<3,"AJM"[tok,"JAPAU"'[mm q 0 + n mon + s mon="\JANUARY \FEBRUARY \MARCH \APRIL \MAY \JUNE \JULY \AUGUST \SEPTEMBER\OCTOBER \NOVEMBER \DECEMBER" + q $f(mon,("\"_mm))+8\10 + ; +date() + i dd<1 q "" + d advance + i dir'?.1N q "" + s zd=$ZDATEFORM + s yy=tok + i yy="" s yy=$zd($h,"YEAR") + i cp'=ilen q "" + i $l(yy)<3 d + . s dh=$H + . s yy=yy+(100*$S('zd:19,(zd>1840)&($L(zd)=4):($E(zd,1,2)+$S($E(zd,3,4)'>yy:0,1:1)),1:$E($ZDATE(dh,"YEAR"),1,2))) + ; 20th rolling current century + i dd>$s(+mm'=2:$e(303232332323,mm)+28,yy#4:28,yy#100:29,yy#400:28,1:29) q "" + n cc,dat + s dat=yy-1841,mm=mm-1,cc=1 + i dat<0 s dd=dd-1,cc=-1 + s dat=dat\4*1461+(dat#4-$s(dat'<0:0,1:4)*365)+(mm*30)+$e(10112234455,mm)+dd-(yy-1800\100-(yy-1600\400)) + i yy#4,mm>1 s dat=dat-cc + i yy#100=0,mm<2,yy#400 s dat=dat+cc + q dat diff --git a/sr_port/db_auto_upgrade.c b/sr_port/db_auto_upgrade.c new file mode 100644 index 0000000..d14a4b1 --- /dev/null +++ b/sr_port/db_auto_upgrade.c @@ -0,0 +1,133 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_string.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "gdscc.h" +#include "gdsblkops.h" +#include "filestruct.h" +#include "io.h" +#include "jnl.h" +#include "mutex.h" +#include "wcs_phase2_commit_wait.h" +#include "gvcst_protos.h" /* for gvcst_init_sysops prototype */ + +GBLREF boolean_t dse_running; + +error_def(ERR_DBBADUPGRDSTATE); + +void db_auto_upgrade(gd_region *reg) +{ + /* detect unitialized file header fields for this version of GT.M and do a mini auto-upgrade, initializing such fields + * to default values in the new GT.M version + */ + + sgmnt_addrs *csa; + sgmnt_data_ptr_t csd; + + assert(NULL != reg); + if (NULL == reg) + return; + csa = &FILE_INFO(reg)->s_addrs; + csd = csa->hdr; + assert(NULL != csd); + if (NULL == csd) + return; + + if (0 == csd->mutex_spin_parms.mutex_hard_spin_count) + csd->mutex_spin_parms.mutex_hard_spin_count = MUTEX_HARD_SPIN_COUNT; + if (0 == csd->mutex_spin_parms.mutex_sleep_spin_count) + csd->mutex_spin_parms.mutex_sleep_spin_count = MUTEX_SLEEP_SPIN_COUNT; + /* zero is a legitimate value for csd->mutex_spin_parms.mutex_spin_sleep_mask; so can't detect if need re-initialization */ + + /* Auto upgrade based on minor database version number. This code currently only does auto upgrade and does not + do auto downgrade although that certainly is possible to implement if necessary. For now, if the current version + is at a lower level than the minor db version, we do nothing. + + Note the purpose of the minor_dbver field is so that some part of gtm (either runtime, or conversion utility) some + time and several versions down the road from now knows by looking at this field what fields in the fileheader are + valid so it is important that the minor db version be updated each time the fileheader is updated and this routine + correspondingly updated. SE 5/2006. + */ + if (csd->minor_dbver < GDSMVCURR) + { /* In general, the method for adding new versions is: + 1) If there are no automatic updates for this version, it is optional to add the version to the switch + statement below. Those there are more for example at this time (through V53000). + 2) Update (or add) a case for the previous version to update any necessary fields. + */ + if (!csd->opened_by_gtmv53 && !csd->db_got_to_v5_once) + { + csd->opened_by_gtmv53 = TRUE; + /* This is a case of a database that has been used by a pre-V53 version of GT.M that did not contain + * the fix (C9H07-002873). At this point, the database might contain RECYCLED blocks that are a mix of + * a) Those blocks that were RECYCLED at the time of the MUPIP UPGRADE from V4 to V5. + * b) Those blocks that became RECYCLED due to M-kills in V5. + * It is only (a) that we have to mark as FREE as it might contain too-full v4 format blocks. But there + * is no way to distinguish the two. So we mark both (a) and (b) as FREE. This will mean no PBLKs written + * for (b) and hence no backward journal recovery possible to a point before the start of the REORG UPGRADE. + * We force a MUPIP REORG UPGRADE rerun (to mark RECYCLED blocks FREE) by setting fully_upgraded to FALSE. + * Note that this does not need to be done for databases created by a V5 version (C9I05-002987). + */ + if (MASTER_MAP_SIZE_V4 == csd->master_map_len) + { + csd->fully_upgraded = FALSE; + csd->reorg_upgrd_dwngrd_restart_block = 0; /* reorg upgrade should restart from block 0 */ + /* Ensure reorg_db_fmt_start_tn and desired_db_format_tn are set to different + * values so fresh reorg upgrade can set fully_upgraded to TRUE once it is done. + */ + csd->reorg_db_fmt_start_tn = 0; + csd->desired_db_format_tn = 1; + } else + csd->db_got_to_v5_once = TRUE; /* db was created by V5 so safe to set this */ + } + /* When adding a new minor version, the following template should be maintained + * a) Remove the penultimate 'break' + * b) Remove the assert(FALSE) in the last case (most recent minor version) + * c) If there are any file header fields added in the new minor version, initialize the fields to default values + * in the last case + * d) Add a new case with the new minor version + * e) Add assert(FALSE) and break (like it was before) + */ + switch(csd->minor_dbver) + { + case GDSMV51000: /* Multi-site replication available */ + case GDSMV52000: /* Unicode */ + case GDSMV53000: /* M-Itanium release */ + gvstats_rec_upgrade(csa); /* Move GVSTATS information to new place in file header */ + case GDSMV54002: /* Backward recovery related fields */ + csd->jnl_eovtn = csd->trans_hist.curr_tn; + break; + case GDSMV54003: + /* Nothing to do for this version since it is GDSMVCURR for now. */ + assert(FALSE); /* When this assert fails, it means a new GDSMV* was created, */ + break; /* so a new "case" needs to be added BEFORE the assert. */ + } + csd->minor_dbver = GDSMVCURR; + if (0 == csd->wcs_phase2_commit_wait_spincnt) + csd->wcs_phase2_commit_wait_spincnt = WCS_PHASE2_COMMIT_DEFAULT_SPINCNT; + } + csd->last_mdb_ver = GDSMVCURR; + if (csd->fully_upgraded && !csd->db_got_to_v5_once) + { /* Database is fully upgraded but the db_got_to_v5_once field says different. + * Don't know how that could happen, except with DSE which can change both the database file header fields + */ + assert(!dse_running); + csd->db_got_to_v5_once = TRUE; /* fix it in PRO */ + send_msg(VARLSTCNT(6) ERR_DBBADUPGRDSTATE, 4, REG_LEN_STR(reg), DB_LEN_STR(reg)); + } + return; +} diff --git a/sr_port/db_common_init.c b/sr_port/db_common_init.c new file mode 100644 index 0000000..3175c85 --- /dev/null +++ b/sr_port/db_common_init.c @@ -0,0 +1,52 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* This is the routine used by both db_init() and mu_rndwn_file() for initializing appropriate structures. */ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "iosp.h" +#include "jnl.h" + +GBLREF jnl_process_vector *prc_vec; + +void db_common_init(gd_region *reg, sgmnt_addrs *csa, sgmnt_data_ptr_t csd) +{ + csa->bmm = MM_ADDR(csd); + csa->reorg_last_dest = 0; /* For mupip reorg swap operation */ + csa->region = reg; /* initialize the back-link */ + reg->max_rec_size = csd->max_rec_size; + reg->max_key_size = csd->max_key_size; + reg->null_subs = csd->null_subs; + reg->std_null_coll = csd->std_null_coll; + reg->jnl_state = csd->jnl_state; + reg->jnl_file_len = csd->jnl_file_len; /* journal file name length */ + memcpy(reg->jnl_file_name, csd->jnl_file_name, reg->jnl_file_len); /* journal file name */ + reg->jnl_alq = csd->jnl_alq; + reg->jnl_deq = csd->jnl_deq; + reg->jnl_buffer_size = csd->jnl_buffer_size; + reg->jnl_before_image = csd->jnl_before_image; + bt_init(csa); + /* Initialization of prc_vec is done even for no journaling. gtcm uses this always. Others might need it too. */ + if (NULL == prc_vec) + { + prc_vec = (jnl_process_vector *)malloc(SIZEOF(jnl_process_vector)); + jnl_prc_vector(prc_vec); + } +} diff --git a/sr_port/db_csh_get.c b/sr_port/db_csh_get.c new file mode 100644 index 0000000..cc4417a --- /dev/null +++ b/sr_port/db_csh_get.c @@ -0,0 +1,115 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsbgtr.h" +#include "cache.h" +#include "hashtab.h" /* needed for cws_insert.h */ +#include "hashtab_int4.h" /* needed for cws_insert.h */ +#include "longset.h" /* needed for cws_insert.h */ +#include "cws_insert.h" + +GBLREF sgmnt_addrs *cs_addrs; +GBLREF unsigned int t_tries; +GBLREF boolean_t mu_reorg_process; + +#define ENOUGH_TRIES_TO_FALL_BACK 17 + +/* This function returns a pointer to the cache_rec entry, NULL if not found, or CR_INVALID if the hash table is corrupt */ + +cache_rec_ptr_t db_csh_get(block_id block) /* block number to look up */ +{ + register sgmnt_addrs *csa; + sgmnt_data_ptr_t csd; + cache_rec_ptr_t cr, cr_hash_base; + int blk_hash, lcnt, ocnt, hmax; + bool is_mm; +#ifdef DEBUG + cache_rec_ptr_t cr_low, cr_high; +#endif + + csa = cs_addrs; + csd = csa->hdr; + is_mm = (dba_mm == csd->acc_meth); + assert((FALSE == is_mm) || (csa->now_crit)); /* if MM you should be in crit */ + VMS_ONLY(assert(FALSE == is_mm);) /* in VMS, MM should never call db_csh_get */ + hmax = csd->bt_buckets; + blk_hash = (block % hmax); + if (!is_mm) + { + DEBUG_ONLY(cr_low = &csa->acc_meth.bg.cache_state->cache_array[0];) + DEBUG_ONLY(cr_high = cr_low + csd->bt_buckets + csd->n_bts;) + cr_hash_base = csa->acc_meth.bg.cache_state->cache_array + blk_hash; + } else + { + DEBUG_ONLY(cr_low = (cache_rec_ptr_t)(csa->acc_meth.mm.mmblk_state->mmblk_array);) + DEBUG_ONLY(cr_high = (cache_rec_ptr_t)(csa->acc_meth.mm.mmblk_state->mmblk_array + csd->bt_buckets + csd->n_bts);) + cr_hash_base = (cache_rec_ptr_t)(csa->acc_meth.mm.mmblk_state->mmblk_array + blk_hash); + } + ocnt = 0; + csa->wbuf_dqd++; /* Tell rundown we have an orphaned block in case of interrupt */ + do + { + cr = cr_hash_base; + assert(cr->blk == BT_QUEHEAD); + lcnt = hmax; + do + { + cr = (cache_rec_ptr_t)((sm_uc_ptr_t)cr + cr->blkque.fl); + assert(!CR_NOT_ALIGNED(cr, cr_low) && !CR_NOT_IN_RANGE(cr, cr_low, cr_high) + UNIX_ONLY(|| is_mm)); + if (BT_QUEHEAD == cr->blk) + { /* We have reached the end of the queue, validate we have run the queue + * back around to the same queue header or we'll need to retry because the + * queue changed on us. + */ + if (cr == cr_hash_base) + { + csa->wbuf_dqd--; + return (cache_rec_ptr_t)NULL; + } + break; /* Retry - something changed */ + } + if ((CR_BLKEMPTY != cr->blk) && ((cr->blk % hmax) != blk_hash)) + break; /* Retry - something changed */ + assert(!csa->now_crit || (0 != cr->blkque.fl) && (0 != cr->blkque.bl)); + if (cr->blk == block) + { + if (!is_mm) + { + if (CDB_STAGNATE <= t_tries || mu_reorg_process) + CWS_INSERT(block); + /* setting refer outside of crit may not prevent its replacement, but that's an + * inefficiency, not a tragedy because of concurrency checks in t_end or tp_tend; + * the real problem is to ensure that the cache_rec layout is such that this + * assignment does not damage other fields. + */ + cr->refer = TRUE; + } + csa->wbuf_dqd--; + return cr; + } + lcnt--; + } while (lcnt); + ocnt++; + /* We rarely expect to come here, hence it is considered better to recompute the maximum value of ocnt (for the + * termination check) instead of storing it in a local variable at the beginning of the do loop */ + } while (ocnt < (csa->now_crit ? 1 : ENOUGH_TRIES_TO_FALL_BACK)); + csa->wbuf_dqd--; + + BG_TRACE_PRO_ANY(csa, db_csh_get_too_many_loops); + return (TRUE == csa->now_crit ? (cache_rec_ptr_t)CR_NOTVALID : (cache_rec_ptr_t) NULL); +} diff --git a/sr_port/db_csh_getn.c b/sr_port/db_csh_getn.c new file mode 100644 index 0000000..052d1c3 --- /dev/null +++ b/sr_port/db_csh_getn.c @@ -0,0 +1,358 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include /* needed for VSIG_ATOMIC_T */ + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdskill.h" +#include "gdscc.h" +#include "filestruct.h" +#include "interlock.h" +#include "jnl.h" +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab.h" /* needed for cws_insert.h */ +#include "hashtab_int4.h" /* needed for tp.h and cws_insert.h */ +#include "tp.h" +#include "gdsbgtr.h" +#include "min_max.h" +#include "sleep_cnt.h" +#include "send_msg.h" +#include "relqop.h" +#include "is_proc_alive.h" +#include "cache.h" +#include "longset.h" /* needed for cws_insert.h */ +#include "cws_insert.h" +#include "wcs_sleep.h" +#include "wcs_get_space.h" +#include "wcs_timer_start.h" +#include "add_inter.h" +#include "wbox_test_init.h" +#include "have_crit.h" +#include "memcoherency.h" +#include "gtm_c_stack_trace.h" + +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gd_region *gv_cur_region; +GBLREF uint4 process_id; +GBLREF uint4 image_count; +GBLREF unsigned int t_tries; +GBLREF uint4 dollar_tlevel; +GBLREF sgm_info *sgm_info_ptr; +GBLREF boolean_t mu_reorg_process; + +#define TRACE_AND_SLEEP(ocnt) \ +{ \ + if (1 == ocnt) \ + { \ + BG_TRACE_PRO(db_csh_getn_rip_wait); \ + first_r_epid = latest_r_epid; \ + } \ + wcs_sleep(ocnt); \ +} + +error_def(ERR_BUFRDTIMEOUT); +error_def(ERR_INVALIDRIP); + +cache_rec_ptr_t db_csh_getn(block_id block) +{ + cache_rec_ptr_t hdr, q0, start_cr, cr; + bt_rec_ptr_t bt; + unsigned int lcnt, ocnt; + int rip, max_ent, pass1, pass2, pass3; + int4 flsh_trigger; + uint4 first_r_epid, latest_r_epid; + sgmnt_addrs *csa; + sgmnt_data_ptr_t csd; + srch_blk_status *tp_srch_status; + ht_ent_int4 *tabent; + + csa = cs_addrs; + csd = csa->hdr; + assert(csa->now_crit); + assert(csa == &FILE_INFO(gv_cur_region)->s_addrs); + max_ent = csd->n_bts; + cr = (cache_rec_ptr_t)GDS_REL2ABS(csa->nl->cur_lru_cache_rec_off); + hdr = csa->acc_meth.bg.cache_state->cache_array + (block % csd->bt_buckets); + start_cr = csa->acc_meth.bg.cache_state->cache_array + csd->bt_buckets; + pass1 = max_ent; /* skip referred or dirty or read-into cache records */ + pass2 = 2 * max_ent; /* skip referred cache records */ + pass3 = 3 * max_ent; /* skip nothing */ + INCR_DB_CSH_COUNTER(csa, n_db_csh_getns, 1); + DEFER_INTERRUPTS(INTRPT_IN_DB_CSH_GETN); + for (lcnt = 0; ; lcnt++) + { + if (lcnt > pass3) + { + BG_TRACE_PRO(wc_blocked_db_csh_getn_loopexceed); + assert(FALSE); + break; + } + cr++; + if (cr == start_cr + max_ent) + cr = start_cr; + VMS_ONLY( + if ((lcnt == pass1) || (lcnt == pass2)) + wcs_wtfini(gv_cur_region); + ) + if (cr->refer && (lcnt < pass2)) + { /* in passes 1 & 2, set refer to FALSE and skip; in the third pass attempt reuse even if TRUE == refer */ + cr->refer = FALSE; + continue; + } + if (cr->in_cw_set || cr->in_tend) + { /* some process already has this pinned for reading and/or updating. skip it. */ + cr->refer = TRUE; + continue; + } + if (CDB_STAGNATE <= t_tries || mu_reorg_process) + { + /* Prevent stepping on self when crit for entire transaction. + * This is done by looking up in sgm_info_ptr->blk_in_use and cw_stagnate for presence of the block. + * The following two hashtable lookups are not similar, since in TP, sgm_info_ptr->blks_in_use + * is updated to the latest cw_stagnate list of blocks only in "tp_hist". + * Also note that the lookup in sgm_info_ptr->blks_in_use reuses blocks that don't have cse's. + * This is to allow big-read TP transactions which may use up more than the available global buffers. + * There is one issue here in that a block that has been only read till now may be stepped upon here + * but may later be needed for update. It is handled by updating the block's corresponding + * entry in the set of histories (sgm_info_ptr->first_tp_hist[index] structure) to hold the + * "cr" and "cycle" of the t_qread done for the block when it was intended to be changed for the + * first time within the transaction since otherwise the transaction would restart due to a + * cdb_sc_lostcr status. Note that "tn" (read_tn of the block) in the first_tp_hist will still + * remain the "tn" when the block was first read within this transaction to ensure the block + * hasn't been modified since the start of the transaction. Once we intend on changing the + * block i.e. srch_blk_status->cse is non-NULL, we ensure in the code below not to step on it. + * ["tp_hist" is the routine that updates the "cr", "cycle" and "tn" of the block]. + * Note that usually in a transaction the first_tp_hist[] structure holds the "cr", "cycle", and "tn" + * of the first t_qread of the block within that transaction. The above is the only exception. + * Also note that for blocks in cw_stagnate (i.e. current TP mini-action), we don't reuse any of + * them even if they don't have a cse. This is to ensure that the current action doesn't + * encounter a restart due to cdb_sc_lostcr in "tp_hist" even in the fourth-retry. + */ + tp_srch_status = NULL; + if (dollar_tlevel && (NULL != (tabent = lookup_hashtab_int4(sgm_info_ptr->blks_in_use, (uint4 *)&cr->blk))) + && (tp_srch_status = (srch_blk_status *)tabent->value) && (tp_srch_status->cse)) + { /* this process is already using the block - skip it */ + cr->refer = TRUE; + continue; + } + if (NULL != lookup_hashtab_int4(&cw_stagnate, (uint4 *)&cr->blk)) + { /* this process is already using the block for the current gvcst_search - skip it */ + cr->refer = TRUE; + continue; + } + if (NULL != tp_srch_status) + { /* About to reuse a buffer that is part of the read-set of the current TP transaction. + * Reset clue as otherwise the next global reference of that global will use an outofdate clue. + * Even though tp_srch_status is available after the sgm_info_ptr->blks_in_use hashtable check, + * we dont want to reset the clue in case the cw_stagnate hashtable check causes the same cr + * to be skipped from reuse. Hence the placement of this reset logic AFTER the cw_stagnate check. + */ + tp_srch_status->blk_target->clue.end = 0; + } + } + if (cr->dirty) + { /* Note that in Unix, it is possible that we see a stale value of cr->dirty (possible if a + * concurrent "wcs_wtstart" has reset dirty to 0 but that update did not reach us yet). In this + * case the call to "wcs_get_space" below will do the necessary memory barrier instructions + * (through calls to "aswp") which will allow us to see the non-stale value of cr->dirty. + * + * It is also possible that cr->dirty is non-zero but < cr->flushed_dirty_tn. In this case, wcs_get_space + * done below will return FALSE forcing a cache-rebuild which will fix this situation. + * + * In VMS, another process cannot be concurrently resetting cr->dirty to 0 as the resetting routine + * is "wcs_wtfini" which is executed in crit which another process cannot be in as we are in crit now. + */ + if (gv_cur_region->read_only) + continue; + if (lcnt < pass1) + { + if (!csa->timer && (csa->nl->wcs_timers < 1)) + wcs_timer_start(gv_cur_region, FALSE); + continue; + } + BG_TRACE_PRO(db_csh_getn_flush_dirty); + if (FALSE == wcs_get_space(gv_cur_region, 0, cr)) + { /* failed to flush it out - force a rebuild */ + BG_TRACE_PRO(wc_blocked_db_csh_getn_wcsstarvewrt); + assert(csd->wc_blocked); /* only reason we currently know why wcs_get_space could fail */ + assert(gtm_white_box_test_case_enabled); + break; + } + assert(0 == cr->dirty); + } + UNIX_ONLY( + /* the cache-record is not free for reuse until the write-latch value becomes LATCH_CLEAR. + * In VMS, resetting the write-latch value occurs in "wcs_wtfini" which is in CRIT, we are fine. + * In Unix, this resetting is done by "wcs_wtstart" which is out-of-crit. Therefore, we need to + * wait for this value to be LATCH_CLEAR before reusing this cache-record. + * Note that we are examining the write-latch-value without holding the interlock. It is ok to do + * this because the only two routines that modify the latch value are "bg_update" and + * "wcs_wtstart". The former cannot be concurrently executing because we are in crit. + * The latter will not update the latch value unless this cache-record is dirty. But in this + * case we would have most likely gone through the if (cr->dirty) check above. Most likely + * because there is one rare possibility where a concurrent "wcs_wtstart" has set cr->dirty + * to 0 but not yet cleared the latch. In that case we wait for the latch to be cleared. + * In all other cases, nobody is modifying the latch since when we got crit and therefore + * it is safe to observe the value of the latch without holding the interlock. + */ + if (LATCH_CLEAR != WRITE_LATCH_VAL(cr)) + { /* possible if a concurrent "wcs_wtstart" has set cr->dirty to 0 but not yet + * cleared the latch. this should be very rare though. + */ + if (lcnt < pass2) + continue; /* try to find some other cache-record to reuse until the 3rd pass */ + for (ocnt = 1; (MAXWRTLATCHWAIT >= ocnt) && (LATCH_CLEAR != WRITE_LATCH_VAL(cr)); ocnt++) + wcs_sleep(SLEEP_WRTLATCHWAIT); /* since it is a short lock, sleep the minimum */ + if (MAXWRTLATCHWAIT <= ocnt) + { + BG_TRACE_PRO(db_csh_getn_wrt_latch_stuck); + assert(FALSE); + continue; + } + } + ) + /* Note that before setting up a buffer for the requested block, we should make sure the cache-record's + * read_in_progress is set. This is so that noone else in t_qread gets access to this empty buffer. + * By setting up a buffer, it is meant assigning cr->blk in addition to inserting the cr in the blkques + * through "shuffqth" below. + * Note that "t_qread" has special code to handle read_in_progress */ + LOCK_BUFF_FOR_READ(cr, rip); + if (0 != rip) + { + if (lcnt < pass2) + { /* someone is reading into this cache record. leave it for two passes. + * this is because if somebody is reading it, it is most likely to be referred to very soon. + * if we replace this, we will definitely be causing a restart for the reader. + * instead of that, see if some other cache record fits in for us. + */ + RELEASE_BUFF_READ_LOCK(cr); + continue; + } + for (ocnt = 1; 0 != rip && BUF_OWNER_STUCK >= ocnt; ocnt++) + { + RELEASE_BUFF_READ_LOCK(cr); + /* The owner has been unable to complete the read - check for some things before going to sleep. + * Since cr->r_epid can be changing concurrently, take a local copy before using it below, + * particularly before calling is_proc_alive as we dont want to call it with a 0 r_epid. + */ + latest_r_epid = cr->r_epid; + if (cr->read_in_progress < -1) + { + BG_TRACE_PRO(db_csh_getn_out_of_design); /* outside of design; clear to known state */ + send_msg(VARLSTCNT(4) ERR_INVALIDRIP, 2, DB_LEN_STR(gv_cur_region)); + assert(cr->r_epid == 0); + cr->r_epid = 0; + INTERLOCK_INIT(cr); + } else if (0 != latest_r_epid) + { + if (is_proc_alive(latest_r_epid, cr->image_count)) + { +# ifdef DEBUG + if ((BUF_OWNER_STUCK / 2) == ocnt) + GET_C_STACK_FROM_SCRIPT("BUFRDTIMEOUT", process_id, latest_r_epid, ONCE); +# endif + TRACE_AND_SLEEP(ocnt); + } else + { + cr->r_epid = 0; + INTERLOCK_INIT(cr); /* Process gone, release that process's lock */ + } + } else + { + TRACE_AND_SLEEP(ocnt); + } + LOCK_BUFF_FOR_READ(cr, rip); + } + if ((BUF_OWNER_STUCK < ocnt) && (0 != rip)) + { + BG_TRACE_PRO(db_csh_getn_buf_owner_stuck); + if (0 != latest_r_epid) + { + if (first_r_epid != latest_r_epid) + GTMASSERT; + GET_C_STACK_FROM_SCRIPT("BUFRDTIMEOUT", process_id, latest_r_epid, + DEBUG_ONLY(TWICE) PRO_ONLY(ONCE)); + RELEASE_BUFF_READ_LOCK(cr); + send_msg(VARLSTCNT(8) ERR_BUFRDTIMEOUT, 6, process_id, + cr->blk, cr, first_r_epid, DB_LEN_STR(gv_cur_region)); + continue; + } + cr->r_epid = 0; + INTERLOCK_INIT(cr); + LOCK_BUFF_FOR_READ(cr, rip); + assert(0 == rip); /* Since holding crit, we expect to get lock */ + if (0 != rip) + continue; + /* We successfully obtained the lock so can fall out of this block */ + } + } + assert(0 == rip); + /* no other process "owns" the block */ + if (CDB_STAGNATE <= t_tries || mu_reorg_process) + { /* this should probably use cr->in_cw_set with a condition handler to cleanup */ + CWS_INSERT(block); + } + assert(LATCH_CLEAR == WRITE_LATCH_VAL(cr)); + /* got a block - set it up */ + assert(0 == cr->epid); + assert(0 == cr->r_epid); + cr->r_epid = process_id; /* establish ownership */ + cr->image_count = image_count; + cr->blk = block; + /* We want cr->read_in_progress to be locked BEFORE cr->cycle is incremented. t_qread relies on this order. + * Enforce this order with a write memory barrier. Not doing so might cause the incremented cr->cycle to be + * seen by another process even though it sees the unlocked state of cr->read_in_progress. This could cause + * t_qread to incorrectly return with an uptodate cr->cycle even though the buffer is still being read in + * from disk and this could cause db integ errors as validation (in t_end/tp_tend which relies on cr->cycle) + * will detect no problems even though there is one. Note this memory barrier is still needed even though + * there is a memory barrier connotation in the LOCK_BUFF_FOR_READ() macro above. LOCK_BUFF_FOR_READ() does + * a read type memory barrier whereas here, we need a write barrier. + */ + SHM_WRITE_MEMORY_BARRIER; + cr->cycle++; + cr->jnl_addr = 0; + cr->refer = TRUE; + if (cr->bt_index != 0) + { + bt = (bt_rec_ptr_t)GDS_REL2ABS(cr->bt_index); + bt->cache_index = CR_NOTVALID; + cr->bt_index = 0; + } + q0 = (cache_rec_ptr_t)((sm_uc_ptr_t)cr + cr->blkque.fl); + shuffqth((que_ent_ptr_t)q0, (que_ent_ptr_t)hdr); + assert(0 == cr->dirty); + csa->nl->cur_lru_cache_rec_off = GDS_ABS2REL(cr); + if (lcnt > pass1) + csa->nl->cache_hits = 0; + csa->nl->cache_hits++; + if (csa->nl->cache_hits > csd->n_bts) + { + flsh_trigger = csd->flush_trigger; + csd->flush_trigger = MIN(flsh_trigger + MAX(flsh_trigger / STEP_FACTOR, 1), MAX_FLUSH_TRIGGER(csd->n_bts)); + csa->nl->cache_hits = 0; + } + INCR_DB_CSH_COUNTER(csa, n_db_csh_getn_lcnt, lcnt); + ENABLE_INTERRUPTS(INTRPT_IN_DB_CSH_GETN); + return cr; + } + /* force a recover */ + INCR_DB_CSH_COUNTER(csa, n_db_csh_getn_lcnt, lcnt); + csa->nl->cur_lru_cache_rec_off = GDS_ABS2REL(cr); + ENABLE_INTERRUPTS(INTRPT_IN_DB_CSH_GETN); + return (cache_rec_ptr_t)CR_NOTVALID; +} diff --git a/sr_port/db_csh_ini.c b/sr_port/db_csh_ini.c new file mode 100644 index 0000000..dba19bc --- /dev/null +++ b/sr_port/db_csh_ini.c @@ -0,0 +1,27 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" + + +void db_csh_ini(sgmnt_addrs *cs) +{ + if ((INTPTR_T)cs->hdr & 7) + GTMASSERT; + cs->acc_meth.bg.cache_state = (cache_que_heads_ptr_t)((sm_uc_ptr_t)cs->hdr + cs->nl->cache_off); + assert(cs->acc_meth.bg.cache_state); + return; +} diff --git a/sr_port/db_csh_ref.c b/sr_port/db_csh_ref.c new file mode 100644 index 0000000..9c8e8f4 --- /dev/null +++ b/sr_port/db_csh_ref.c @@ -0,0 +1,112 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "interlock.h" +#include "lockconst.h" +#include "longset.h" +#include "relqop.h" + +error_def(ERR_WCFAIL); + +GBLREF int4 process_id; + +void db_csh_ref(sgmnt_addrs *csa) +{ + sgmnt_data_ptr_t csd; + node_local_ptr_t cnl; + sm_uc_ptr_t bp, bp_top; + cache_rec_ptr_t cr, cr_top, cr1; + int4 buffer_size, rec_size; + boolean_t is_mm; + + csd = csa->hdr; + /* Note the cr setups for MM should realistically be under a TARGETED_MSYNC_ONLY macro since the MM + * cache recs are only used in that mode. We don't currently use that mode but since this is one-time + * open-code, we aren't bothering. Note if targeted msyncs ever do come back into fashion, we should + * revisit the INTERLOCK_INIT_MM vs INTERLOCK_INIT usage, here and everywhere else too. + */ + is_mm = (dba_mm == csd->acc_meth); + if (!is_mm) + { + longset((uchar_ptr_t)csa->acc_meth.bg.cache_state, + SIZEOF(cache_que_heads) + (csd->bt_buckets + csd->n_bts - 1) * SIZEOF(cache_rec), + 0); /* -1 since there is a cache_rec in cache_que_heads */ + cr = cr1 = csa->acc_meth.bg.cache_state->cache_array; + buffer_size = csd->blk_size; + assert(buffer_size > 0); + assert(0 == buffer_size % DISK_BLOCK_SIZE); + SET_LATCH_GLOBAL(&csa->acc_meth.bg.cache_state->cacheq_active.latch, LOCK_AVAILABLE); + SET_LATCH_GLOBAL(&csa->acc_meth.bg.cache_state->cacheq_wip.latch, LOCK_AVAILABLE); + rec_size = SIZEOF(cache_rec); + } else + { + longset((uchar_ptr_t)csa->acc_meth.mm.mmblk_state, + SIZEOF(mmblk_que_heads) + (csd->bt_buckets + csd->n_bts - 1) * SIZEOF(mmblk_rec), + 0); /* -1 since there is a mmblk_rec in mmblk_que_heads */ + cr = cr1 = (cache_rec_ptr_t)csa->acc_meth.mm.mmblk_state->mmblk_array; + SET_LATCH_GLOBAL(&csa->acc_meth.mm.mmblk_state->mmblkq_active.latch, LOCK_AVAILABLE); + SET_LATCH_GLOBAL(&csa->acc_meth.mm.mmblk_state->mmblkq_wip.latch, LOCK_AVAILABLE); + rec_size = SIZEOF(mmblk_rec); + } + cnl = csa->nl; + SET_LATCH_GLOBAL(&cnl->wc_var_lock, LOCK_AVAILABLE); + SET_LATCH_GLOBAL(&cnl->db_latch, LOCK_AVAILABLE); + for (cr_top = (cache_rec_ptr_t)((sm_uc_ptr_t)cr + rec_size * csd->bt_buckets); + cr < cr_top; cr = (cache_rec_ptr_t)((sm_uc_ptr_t)cr + rec_size)) + cr->blk = BT_QUEHEAD; + cr_top = (cache_rec_ptr_t)((sm_uc_ptr_t)cr + rec_size * csd->n_bts); + cnl->cur_lru_cache_rec_off = GDS_ANY_ABS2REL(csa, cr); + cnl->cache_hits = 0; + if (!is_mm) + { + bp = (sm_uc_ptr_t)ROUND_UP((sm_ulong_t)cr_top, OS_PAGE_SIZE); + bp_top = bp + (gtm_uint64_t)csd->n_bts * buffer_size; + GTMCRYPT_ONLY( + if (csd->is_encrypted) + { /* In case of an encrypted database, bp_top is actually the beginning of the encrypted global buffer + * array (an array maintained parallely with the regular unencrypted global buffer array. + */ + cnl->encrypt_glo_buff_off = (sm_off_t)((sm_uc_ptr_t)bp_top - (sm_uc_ptr_t)bp); + } + ) + } + for (; cr < cr_top; cr = (cache_rec_ptr_t)((sm_uc_ptr_t)cr + rec_size), + cr1 = (cache_rec_ptr_t)((sm_uc_ptr_t)cr1 + rec_size)) + { + if (!is_mm) + { + INTERLOCK_INIT(cr); + } else + { + INTERLOCK_INIT_MM(cr); + } + + cr->cycle++; /* increment cycle whenever buffer's blk number changes (for tp_hist) */ + cr->blk = CR_BLKEMPTY; + assert(0 == cr->bt_index); /* when cr->blk is empty, ensure no bt points to this cache-record */ + if (!is_mm) + { + assert(bp <= bp_top); + cr->buffaddr = GDS_ANY_ABS2REL(csa, bp); + bp += buffer_size; + } + insqt((que_ent_ptr_t)cr, (que_ent_ptr_t)cr1); + cr->refer = FALSE; + } + cnl->wc_in_free = csd->n_bts; + return; +} diff --git a/sr_port/dbcertify.c b/sr_port/dbcertify.c new file mode 100644 index 0000000..9f982c1 --- /dev/null +++ b/sr_port/dbcertify.c @@ -0,0 +1,98 @@ +/**************************************************************** + * * + * Copyright 2005, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/**************************************************************** + DBCERTIFY - main driver + + Parse arguments, invoke required phase routine. + + Note: Most routines in this utility are self-contained + meaning they do not reference GT.M library routines + (with some notable exceptions). This is because + phase-1 is going to run against live V4 databases + and the V5 compilation will be using V5 database + structures. + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_ctype.h" +#include "gtm_stdio.h" +#include "gtm_string.h" +#include "gtm_unistd.h" +#include "gtm_stdlib.h" +#include + +#ifdef UNIX +#include "sig_init.h" +#else +#include "desblk.h" /* for desblk structure */ +#endif +#include "gdsroot.h" +#include "v15_gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "v15_gdsbt.h" +#include "gdsblk.h" +#include "gdsfhead.h" +#include "v15_gdsfhead.h" +#include "gdsblkops.h" +#include "mupip_exit.h" +#include "gtmimagename.h" +#include "error.h" +#include "iosp.h" +#include "gtm_env_init.h" +#include "dbcertify.h" +#include "cli.h" +#include "gtm_imagetype_init.h" +#include "gtm_threadgbl_init.h" + +GBLREF uint4 process_id; +GBLREF boolean_t gtm_utf8_mode; +#ifdef VMS +GBLREF desblk exi_blk; +GBLREF int4 exi_condition; +#endif +#ifdef UNIX +GBLREF CLI_ENTRY dbcertify_cmd_ary[]; +#endif + +GBLDEF phase_static_area *psa_gbl; /* Global anchor for static area */ +#ifdef UNIX +GBLDEF CLI_ENTRY *cmd_ary = &dbcertify_cmd_ary[0]; /* Define cmd_ary to be the DBCERTIFY specific cmd table */ +#endif + +int UNIX_ONLY(main)VMS_ONLY(dbcertify)(int argc, char **argv) +{ + DCL_THREADGBL_ACCESS; + + /* Initialization of scaffolding we run on */ + GTM_THREADGBL_INIT; + gtm_imagetype_init(DBCERTIFY_IMAGE); + gtm_env_init(); + gtm_utf8_mode = FALSE; /* Only ever runs in V4 database so NO utf8 mode -- ever */ + psa_gbl = malloc(SIZEOF(*psa_gbl)); + memset(psa_gbl, 0, SIZEOF(*psa_gbl)); + UNIX_ONLY(err_init(dbcertify_base_ch)); + UNIX_ONLY(sig_init(dbcertify_signal_handler, dbcertify_signal_handler, NULL)); + VMS_ONLY(util_out_open(0)); + VMS_ONLY(SET_EXIT_HANDLER(exi_blk, dbcertify_exit_handler, exi_condition)); /* Establish exit handler */ + VMS_ONLY(ESTABLISH(dbcertify_base_ch)); + process_id = getpid(); + + /* Structure checks .. */ + assert((24 * 1024) == SIZEOF(v15_sgmnt_data)); /* Verify V4 file header hasn't suddenly increased for some odd reason */ + + /* Platform dependent method to get the option scan going and invoke necessary driver routine */ + dbcertify_parse_and_dispatch(argc, argv); + return SS_NORMAL; +} diff --git a/sr_port/dbcertify.h b/sr_port/dbcertify.h new file mode 100644 index 0000000..5012727 --- /dev/null +++ b/sr_port/dbcertify.h @@ -0,0 +1,354 @@ +/**************************************************************** + * * + * Copyright 2005, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#ifndef DBCERTIFY_H_INCLUDED +#define DBCERTIFY_H_INCLUDED + +#include "gtm_stdio.h" +#ifdef UNIX +# include "mu_all_version_standalone.h" +#endif + +#ifdef VMS +# define OPTDELIM "/" +# define DEFAULT_OUTFILE_SUFFIX "_DBCERTSCAN" +# define TMPCMDFILSFX ".com" +# define SETDISTLOGENV "$ DEFINE/NOLOG "GTM_DIST" " +# define DSE_START "$ RUN "GTM_DIST":DSE.EXE" +# define DSE_FIND_REG_ALL "FIND /REG" +# define MUPIP_START "$ RUN "GTM_DIST":MUPIP.EXE" +# define RUN_CMD "@" +# define RESULT_ASGN "$ DEFINE SYS$OUTPUT " +# define DUMPRSLTFILE "TYPE " +# define RMS_OPEN_BIN ,"rfm=fix","mrs=512","ctx=bin" +#else +# define SHELL_START "#!/bin/sh" +# define SETDISTLOGENV GTM_DIST"=" +# define DSE_START_PIPE_RSLT1 "$"GTM_DIST"/dse << EOF > " +# define DSE_START_PIPE_RSLT2 " 2>&1" +# define OPTDELIM "-" +# define DEFAULT_OUTFILE_SUFFIX ".dbcertscan" +# define TMPCMDFILSFX ".sh" +# define DSE_FIND_REG_ALL "find -reg" +# define MUPIP_START "$"GTM_DIST"/mupip << EOF" +# define RUN_CMD "./" +# define DUMPRSLTFILE "cat " +# define RMS_OPEN_BIN +#endif +#define DSE_BFLUSH "buffer_flush" +#define DSE_QUIT "quit" +#define DSE_OPEN_RSLT "OPEN "OPTDELIM"FILE="TMPRSLTFILE +#define DSE_CLOSE_RSLT "CLOSE" +#define DSE_PROMPT "DSE> " +#define DSE_REG_LIST_START UNIX_ONLY(DSE_PROMPT)"List of global directory:" +#define MUPIP_EXTEND "EXTEND " +#define MUPIP_BLOCKS " "OPTDELIM"BLOCKS=" +#define TMPFILEPFX "dbcmdx" +#define TMPRSLTFILSFX ".out" +#define MAX_IEB_CNT 10 + +/* The "maximum" key that we use to search for righthand children of gvtroot blocks + is simply 0xFF, 0x00, 0x00. So we don't need much here.. +*/ +#define MAX_DBC_KEY_SZ 3 + +/* A restart is triggered when, in the processing of a given block, we discover that + some other block in its path needs to be dealt with first. Since the splitting of + the higher (tree) level block will take care of anything else higher in the tree + (that call may restart but it does not matter how deeply we nest, each level has + its own restart counter), we should never need more than 1 restart but it is set + to 3 for "cushion". SE 5/2005. +*/ +#define MAX_RESTART_CNT 3 +/* The version should be updated anytime the format of the header or underlying records + is changed.. + + **NOTE ** Any change here require changes in v5cbsu.m. +*/ +#define P1HDR_TAG "GTMDBC01" + +/* Worst case of max level of DT tree + max level of GVT tree plus if every block in a tree gets split plus + each split block creates a block in a different bitmap. +*/ +#define MAX_BLOCK_INFO_DEPTH (MAX_BT_DEPTH * 4) + +#define PREMATURE_EOF "Premature EOF on temporary results file. Command may have failed" + +enum gdsblk_usage {gdsblk_read = 1, gdsblk_update, gdsblk_create}; + +/* Note gdsblk_gvtleaf is used in v5cbsu.m so changes here must be reflected there ! */ +enum gdsblk_type {gdsblk_gvtgeneric = 1, gdsblk_dtgeneric, + gdsblk_gvtroot, gdsblk_gvtindex, gdsblk_gvtleaf, + gdsblk_dtroot, gdsblk_dtindex, gdsblk_dtleaf, + gdsblk_bitmap}; + +/* Structure defining the header of the phase 1 output file. Note the char fields in this + structure use absolute lengths. This is because of the requirements that this structure + be able to be read by the M program vb5cbsu. + + **NOTE ** Any changes here require changes in v5cbsu.m. + */ +typedef struct +{ + unsigned char p1hdr_tag[8]; /* We are what we are */ + v15_trans_num tn; /* TN at point we started reading blocks */ + int4 blk_count; /* number of blocks recorded in this file (records) */ + int4 tot_blocks; /* Total blocks we processed (used blocks) */ + int4 dt_leaf_cnt; /* Block type counters */ + int4 dt_index_cnt; + int4 gvt_leaf_cnt; + int4 gvt_index_cnt; + unsigned char regname[V4_MAX_RN_LEN + 1]; /* Region name for DSE */ + unsigned char dbfn[V4_MAX_FN_LEN + 1]; /* Database file name */ + int4 uid_len; /* Length of following unique id field (varies across platforms, used by v5cbsu) */ + unique_file_id unique_id; /* Make sure this DB doesn't move */ + char fillx[32 - SIZEOF(unique_file_id)]; /* Fill out for variable size of unique_id */ + char fill512[152]; /* Pad out to 512 for VMS oddities with fixed record IO and hdr rewrite */ +} p1hdr; + +/* Structure defining the header of each record in the phase 1 output file. + + **NOTE ** Any changes here require changes in v5cbsu.m. +*/ +typedef struct +{ + v15_trans_num tn; /* TN of this block */ + block_id blk_num; /* block id (number) for this block */ + enum gdsblk_type blk_type; /* Block type */ + int4 blk_levl; /* block level for type */ + int4 akey_len; /* ASCII key length */ +} p1rec; + + +typedef struct +{ + unsigned int top; /* Offset to top of buffer allocated for the key */ + unsigned int end; /* End of the current key. Offset to the second null */ + unsigned int gvn_len; /* Length of part of key that makes up the GVN */ + unsigned char base[1]; /* Base of the key */ +} dbc_gv_key; + +typedef struct +{ + dbc_gv_key *ins_key; /* Describes key part of record */ + block_id blk_id; /* Block id to use for value of key */ +} dbc_inserted_rec; + +/* Structure to hold block numbers where an integrity error caused a problem the first time around */ +typedef struct integ_error_blk_list_struct +{ + struct integ_error_blk_list_struct *next; /* Chain of such structures if necessary */ + int blk_cnt; /* Count of blccks in structure */ + block_id blk_list[MAX_IEB_CNT]; /* List of block ids with integ errors */ +} integ_error_blk_list; + +/* Structure we use for pseudo cw_set_element/cache entry - allocated in an array */ +typedef struct block_info_struct +{ + v15_trans_num tn; /* transaction number for bit maps */ + block_id blk_num; /* Block number or a hint block number for creates */ + enum gdsblk_usage usage; /* Read, Update, or Create */ + enum gdsblk_type blk_type; /* Type of block */ + blk_segment *upd_addr; /* Address of the block segment array containing update info + * for this block */ + boolean_t found_in_cache; /* This block was found in the cache instead of being read */ + uchar_ptr_t old_buff; /* Original buffer before changes */ + uchar_ptr_t new_buff; /* New buffer after changes/create */ + uchar_ptr_t prev_rec; /* Pointer to previous record in block (in old_buff) */ + unsigned int prev_match; /* Match of previous record to search key */ + uchar_ptr_t curr_rec; /* Current record being looked at in block (in old_buff) */ + unsigned int curr_match; /* Match of current record to search key */ + dbc_gv_key *curr_blk_key; /* Key in use to lookup records in this block (last key used) */ + dbc_gv_key *prev_blk_key; /* Key as known at previous record (copy of curr_blk_key before it is updated) */ + dbc_inserted_rec ins_rec; /* Record to be inserted in this block (if .ins_key.end is not 0) */ + int blk_len; /* (old) size of this block */ + int blk_levl; /* block's level */ + /* When a block splits a new block must be created and the parent must be updated to + * to have a record pointing to the new block. The created block number will not be + * known until the last possible moment. Thus it is not possible to completely modify + * the parent. The following field is used in such a case. When non-zero (and it should only + * be non-zero for created blocks), it points to the referring block's ins_key.blk_id field. The + * location this field targets is inside the update array for that block. When a block is assigned + * at commit time, this field is used to "fixup" the update array so that when the block is + * (re)created during commit, it contains the proper block number. + */ + block_id *ins_blk_id_p; +} block_info; + +/* Single malloc'd structure to hold static fields while we shuffle between our processing routines */ +typedef struct +{ + int hint_lcl; /* Hint offset in hint_blk's local bitmap */ + int outfd; /* File descriptor for input file */ + int blks_processed; /* Counter - blocks from phase-1 we ended up splitting */ + int blks_bypassed; /* Counter - blocks from phase-1 no longer needed splitting */ + int blks_too_big; /* Counter -- blocks needing to be split */ + int blks_read; /* Counter - blocks we read during processing */ + int blks_cached; /* Counter - block reads satisfied from cache */ + int blks_updated; /* Counter - blocks we updated during processing */ + int blks_created; /* Counter - blocks we created during processing */ + int dtlvl0; /* Counter - Directory tree level 0 blocks */ + int dtlvln0; /* Counter - Directory tree not level 0 blocks */ + int gvtlvl0; /* Counter - Global variable tree level 0 blocks */ + int gvtlvln0; /* Counter - Global variable tree not level 0 blocks */ + int gvtrchildren; /* Counter - Right sibling children of gvtroot processed */ + int blk_process_errors; /* Counter - Errors encountered which keep us from certifying DB */ + int gvtroot_rchildren_cnt; /* Count of the inhabitants of gvtroot_rchildren[] */ + int local_bit_map_cnt; /* Total local bit maps in database */ + uint4 blocks_to_process; /* Number of blocks (records) phase two will process */ + int tmpcmdfile_len; /* Length of tmpcmdfile */ + int tmprsltfile_len; /* Length of tmprsltfile */ + unsigned int max_blk_len; /* Max block length (including blk header) */ + unsigned int max_rec_len; /* Maximum record length (including rec header) */ + boolean_t report_only; /* Copy of input report_only flag */ + boolean_t detail; /* Copy of input detail flag */ + boolean_t bsu_keys; /* Copy of input bsu_keys flag */ + boolean_t final; /* This is the final pass for error blocks */ + boolean_t phase_one; /* When we are in phase 1 (aka scan phase)... */ + boolean_t dbc_debug; /* The -debug flag was specified */ + boolean_t tmp_file_names_gend; /* Whether we have figured out what our temp file names are */ + boolean_t keep_temp_files; /* Leave temp files instead of delete on exit */ + UNIX_ONLY(sem_info sem_inf[FTOK_ID_CNT];) /* Ftok keys for id 43 and 1 (both used by older versions) */ + volatile boolean_t dbc_critical; /* Critical section indicator for condition/signal/exit handlers */ + volatile boolean_t dbc_fhdr_dirty; /* If fileheader has been modified, flag it */ + uchar_ptr_t curr_lbmap_buff; /* Buffer holding current local bit map block */ + uchar_ptr_t block_buff; /* Buffer holding current database block */ + unsigned char util_cmd_buff[256]; /* Buffer for DSE and/or MUPIP command creation */ + block_info *blk_set; /* Max number of blocks we need per query/update (array) */ + int block_depth; /* How many blocks we have ref'd/used this query */ + int block_depth_hwm; /* High water mark for block depth */ + FILE *tcfp; /* File pointer for temporary command file */ + FILE *trfp; /* File pointer for temporary result file */ + block_id hint_blk; /* Hint where to start search for next free block */ + p1hdr ofhdr; /* Phase-1 output file header (phase-2 input file) */ + p1rec rhdr; /* Record in output file */ + p1rec gvtroot_rchildren[MAX_BT_DEPTH + 1]; /* List of right hand children for a GVT root block to process */ + gd_region *dbc_gv_cur_region; /* our version of gv_cur_region */ + v15_sgmnt_data_ptr_t dbc_cs_data; /* our version of cs_data */ + dbc_gv_key *first_rec_key; /* Statically allocazted key buffer used for initial search */ + file_control *fc; + integ_error_blk_list *iebl; /* Chain of blocks describing integ errors we encountered in 1st pass */ + dbc_gv_key *gvn_key; /* Used to look up GVN in directory tree */ + dbc_gv_key *max_key; /* Maximum possible key value */ + unsigned char outfn[MAX_FN_LEN + 1]; /* File name argument (may be db or outfile depending on phase) */ + unsigned char regname[MAX_RN_LEN + 1]; /* Buffer for region name */ + unsigned char rslt_buff[MAX_ZWR_KEY_SZ + 1]; /* Buffer for reading from result file */ + unsigned char tmpcmdfile[MAX_FN_LEN + 1]; /* Temporary command file name */ + unsigned char tmprsltfile[MAX_FN_LEN + 1]; /* Temporary command result file name */ + unsigned char tmpfiledir[MAX_FN_LEN + 1]; /* Directory where temp files created (defaults to current dir */ +} phase_static_area; + +/* Debug macro. This macro is compiled into even the pro-build. It emits the enclosed message if a debug + option has been specified.*/ +#define DBC_DEBUG(x) if (psa->dbc_debug) {PRINTF x; fflush(stdout);} + +/* We need to redefine 2 macros from gdsblkops.h (BLK_INIT and BLK_FINI) because they contain references + to the type blk_hdr which for V5 is a different size than the v15_blk_hdr type we are using for V4 databases. +*/ +#ifndef BLK_INIT +# error gdsblkops.h must be included prior to dbcertify.h +#endif +#undef BLK_INIT +#undef BLK_FINI + +/* *************************************************************************** + * BLK_INIT(BNUM, ARRAY) allocates: + * blk_segment ARRAY[BLK_SEG_ARRAY_SIZE] + * at the next octaword-aligned location in the update array and sets + * BNUM = &ARRAY[1] + */ +#define BLK_INIT(BNUM, ARRAY) \ +{ \ + update_array_ptr = (char_ptr_t)ROUND_UP2((INTPTR_T)update_array_ptr, UPDATE_ELEMENT_ALIGN_SIZE); \ + (ARRAY) = (blk_segment *)update_array_ptr; \ + update_array_ptr += BLK_SEG_ARRAY_SIZE * SIZEOF(blk_segment); \ + assert(((update_array + update_array_size) - update_array_ptr) >= 0); \ + (BNUM) = (ARRAY + 1); \ + blk_seg_cnt = SIZEOF(v15_blk_hdr); \ +} + +/* *************************************************************************** + * BLK_FINI(BNUM,ARRAY) finishes the update array by + * BNUM->addr = 0 + * BNUM-- + * if the blk_seg_cnt is within range, then + * ARRAY[0].addr = BNUM (address of last entry containing data) + * ARRAY[0].len = blk_seg_cnt (total size of all block segments) + * and it returns the value of blk_seg_cnt, + * otherwise, it returns zero and the caller should invoke t_retry + */ +#define BLK_FINI(BNUM,ARRAY) \ +( \ + (BNUM--)->addr = (uchar_ptr_t)0, \ + (blk_seg_cnt <= blk_size && blk_seg_cnt >= SIZEOF(v15_blk_hdr)) \ + ? (ARRAY)[0].addr = (uchar_ptr_t)(BNUM), (ARRAY)[0].len = blk_seg_cnt \ + : 0 \ +) + +/* Need to redefine the IS_BML macro in gdsblk.h to use the older header */ +#ifndef IS_BML +# error gdsblk.h must be included prior to dbcertify.h +#endif +#undef IS_BML +#define IS_BML(b) (BML_LEVL == ((v15_blk_hdr_ptr_t)(b))->levl) + +CONDITION_HANDLER(dbcertify_base_ch); + +#ifdef __osf__ +#pragma pointer_size (save) +#pragma pointer_size (long) +#endif +void dbcertify_parse_and_dispatch(int argc, char **argv); +#ifdef __osf__ +#pragma pointer_size (restore) +#endif + +void dbcertify_scan_phase(void); +void dbcertify_certify_phase(void); +void dbcertify_dbfilop(phase_static_area *psa); +#ifdef UNIX +#include +void dbcertify_signal_handler(int sig, siginfo_t *info, void *context); +void dbcertify_deferred_signal_handler(void); +#else +void dbcertify_exit_handler(void); +#endif +/* Routines in dbcertify_funcs.c */ +void dbc_gen_temp_file_names(phase_static_area *psa); +void dbc_open_command_file(phase_static_area *psa); +void dbc_write_command_file(phase_static_area *psa, char_ptr_t cmd); +void dbc_close_command_file(phase_static_area *psa); +void dbc_run_command_file(phase_static_area *psa, char_ptr_t cmdname, char_ptr_t cmdargs, boolean_t piped_result); +void dbc_remove_command_file(phase_static_area *psa); +void dbc_open_result_file(phase_static_area *psa); +void dbc_find_database_filename(phase_static_area *psa, uchar_ptr_t regname, uchar_ptr_t dbfn); +uchar_ptr_t dbc_read_result_file(phase_static_area *psa, int errmsg, uchar_ptr_t eofmsg); +void dbc_close_result_file(phase_static_area *psa); +void dbc_remove_result_file(phase_static_area *psa); +int dbc_syscmd(char_ptr_t cmd); +int dbc_read_dbblk(phase_static_area *psa, int blk_num, enum gdsblk_type blk_type); +void dbc_init_key(phase_static_area *psa, dbc_gv_key **key); +void dbc_find_key(phase_static_area *psa, dbc_gv_key *key, uchar_ptr_t rec_p, int blk_levl); +boolean_t dbc_match_key(dbc_gv_key *key1, int blk_levl1, dbc_gv_key *key2, unsigned int *matchc); +int dbc_find_dtblk(phase_static_area *psa, dbc_gv_key *key, int min_levl); +int dbc_find_record(phase_static_area *psa, dbc_gv_key *key, int blk_index, int min_levl, enum gdsblk_type newblk_type, + boolean_t fail_ok); +void dbc_init_blk(phase_static_area *psa, block_info *blk_set_p, int blk_num, enum gdsblk_usage blk_usage, int blk_len, + int blk_levl); +void dbc_init_db(phase_static_area *psa); +void dbc_close_db(phase_static_area *psa); +void dbc_scan_phase_cleanup(void); +void dbc_certify_phase_cleanup(void); +#ifdef UNIX +void dbc_aquire_standalone_access(phase_static_area *psa); +void dbc_release_standalone_access(phase_static_area *psa); +#endif + +#endif diff --git a/sr_port/dbcertify_base_ch.c b/sr_port/dbcertify_base_ch.c new file mode 100644 index 0000000..c53d07c --- /dev/null +++ b/sr_port/dbcertify_base_ch.c @@ -0,0 +1,99 @@ +/**************************************************************** + * * + * Copyright 2005, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdlib.h" +#ifdef VMS +#include +#endif + +#include "gdsroot.h" +#include "v15_gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "v15_gdsbt.h" +#include "gdsblk.h" +#include "gdsfhead.h" +#include "v15_gdsfhead.h" +#include "gdsblkops.h" +#include "error.h" +#include "gtmimagename.h" +#include "filestruct.h" +#include "v15_filestruct.h" +#include "dbcertify.h" + +GBLREF boolean_t need_core; +GBLREF boolean_t created_core; +GBLREF boolean_t dont_want_core; +GBLREF int4 exi_condition; +GBLREF int4 error_condition; +GBLREF enum gtmImageTypes image_type; + +CONDITION_HANDLER(dbcertify_base_ch) +{ + VMS_ONLY( + unsigned short msglen; + uint4 status; + unsigned char msginfo[4]; + unsigned char msg_buff[MAX_MSG_SIZE + 1]; + $DESCRIPTOR(msgbuf, msg_buff); + ) + error_def(ERR_GTMCHECK); + error_def(ERR_ASSERT); + error_def(ERR_GTMASSERT); + error_def(ERR_MEMORY); + error_def(ERR_VMSMEMORY); + error_def(ERR_STACKOFLOW); + error_def(ERR_OUTOFSPACE); + VMS_ONLY(error_def(ERR_DBCNOFINISH);) + + START_CH; + PRN_ERROR; + if (SUCCESS == SEVERITY || INFO == SEVERITY) + { + CONTINUE; + } else + { + UNIX_ONLY( + if ((DUMPABLE) && !SUPPRESS_DUMP) + { + need_core = TRUE; + gtm_fork_n_core(); + } + /* rts_error sets error_condition, and dbcertify_base_ch is called only if + * exiting thru rts_error. Setup exi_condition to reflect error + * exit status. Note, if the last eight bits (the only relevant bits + * for Unix exit status) of error_condition is non-zero in case of + * errors, we make sure that an error exit status (non-zero value -1) + * is setup. This is a hack. + */ + if (0 == exi_condition) + exi_condition = (((error_condition & UNIX_EXIT_STATUS_MASK) != 0) ? error_condition : -1); + ) + VMS_ONLY( + if ((DUMPABLE) && !SUPPRESS_DUMP) + { + gtm_dump(); + TERMINATE; + } + exi_condition = SIGNAL; + /* following is a hack to avoid FAO directives getting printed without expanding + * in the error message during EXIT() + */ + if (IS_GTM_ERROR(SIGNAL)) + exi_condition = ERR_DBCNOFINISH; + ) + UNSUPPORTED_PLATFORM_CHECK; + EXIT(exi_condition); + } +} diff --git a/sr_port/dbcertify_certify_phase.c b/sr_port/dbcertify_certify_phase.c new file mode 100644 index 0000000..836c877 --- /dev/null +++ b/sr_port/dbcertify_certify_phase.c @@ -0,0 +1,1483 @@ +/**************************************************************** + * * + * Copyright 2005, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/**************************************************************** + dbcertify_certify_phase2.c - Database certification phase 2 + + - Verify phase 1 input file. + - Locate and open database after getting standalong access. + - Read the identified blocks in and if they are still too + large, split them. + - Certify the database as "clean" if no errors encountered. + + Note: Most routines in this utility are self-contained + meaning they do not reference GT.M library routines + (with some notable exceptions). This is because + phase-2 is going to run against V4 format databases + but any linked routines would be compiled for V5 + databases. +****************************************************************/ + +#include "mdef.h" + +#ifdef VMS +#include +#include +#include +#endif + +#include +#include "gtm_stat.h" +#include "gtm_ctype.h" +#include "gtm_stdio.h" +#include "gtm_string.h" +#include "gtm_unistd.h" +#include "gtm_stdlib.h" +#include "gtm_fcntl.h" + +#ifdef __MVS__ +#include "gtm_zos_io.h" +#endif +#include "gtmio.h" +#include "cli.h" +#include "copy.h" +#include "iosp.h" +#include "gdsroot.h" +#include "v15_gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "v15_gdsbt.h" +#include "gdsfhead.h" +#include "v15_gdsfhead.h" +#include "filestruct.h" +#include "v15_filestruct.h" +#include "gdsblk.h" +#include "gdsbml.h" +#include "gdscc.h" +#include "bmm_find_free.h" +#include "gdsblkops.h" +#include "bit_set.h" +#include "bit_clear.h" +#include "min_max.h" +#include "gtmmsg.h" +#ifdef VMS +# include "is_file_identical.h" +#endif +#include "error.h" +#include "mupip_exit.h" +#include "util.h" +#include "dbcertify.h" + +GBLREF char_ptr_t update_array, update_array_ptr; +GBLREF uint4 update_array_size; +GBLREF VSIG_ATOMIC_T forced_exit; /* Signal came in while we were in critical section */ +GBLREF int4 exi_condition; +GBLREF phase_static_area *psa_gbl; + +boolean_t dbc_split_blk(phase_static_area *psa, block_id blk_num, enum gdsblk_type blk_type, v15_trans_num tn, int blk_levl); +void dbc_flush_fhead(phase_static_area *psa); +void dbc_read_p1out(phase_static_area *psa, void *obuf, int olen); + +error_def(ERR_DEVOPENFAIL); +error_def(ERR_FILENOTFND); +error_def(ERR_DBCSCNNOTCMPLT); +error_def(ERR_DBCBADFILE); +error_def(ERR_DBCMODBLK2BIG); +error_def(ERR_DBCINTEGERR); +error_def(ERR_DBCNOEXTND); +error_def(ERR_DBCDBCERTIFIED); +error_def(ERR_DBCNOTSAMEDB); +error_def(ERR_DBCDBNOCERTIFY); +error_def(ERR_DBCREC2BIGINBLK); +error_def(ERR_SYSCALL); +error_def(ERR_TEXT); +error_def(ERR_BITMAPSBAD); +error_def(ERR_MUPCLIERR); +ZOS_ONLY(error_def(ERR_BADTAG);) + +/* The final certify phase of certification process */ +void dbcertify_certify_phase(void) +{ + int save_errno, len, rc, restart_cnt, maxkeystructsize; + uint4 rec_num; + char_ptr_t errmsg; + boolean_t restart_transaction, p1rec_read; + unsigned short buff_len; + char ans[2]; + unsigned char dbfn[MAX_FN_LEN + 1]; + file_control *fc; + phase_static_area *psa; + ZOS_ONLY(int realfiletag;) + + psa = psa_gbl; + DBC_DEBUG(("DBC_DEBUG: Beginning certification phase\n")); + psa->phase_one = FALSE; + UNIX_ONLY(atexit(dbc_certify_phase_cleanup)); + psa->block_depth = psa->block_depth_hwm = -1; /* Initialize no cache */ + + /* Check parsing results */ + if (CLI_PRESENT == cli_present("BLOCKS")) + { + if (!cli_get_hex("BLOCKS", &psa->blocks_to_process)) + exit(1); /* Error message already raised */ + } else + psa->blocks_to_process = MAXTOTALBLKS_V4; + if (CLI_PRESENT == cli_present("TEMPFILE_DIR")) + { /* Want to put temp files in this directory */ + buff_len = SIZEOF(psa->tmpfiledir) - 1; + if (0 == cli_get_str("TEMPFILE_DIR", (char_ptr_t)psa->tmpfiledir, &buff_len)) + mupip_exit(ERR_MUPCLIERR); + } + psa->keep_temp_files = (CLI_PRESENT == cli_present("KEEP_TEMPS")); + buff_len = SIZEOF(psa->outfn) - 1; + if (0 == cli_get_str("P1OUTFILE", (char_ptr_t)psa->outfn, &buff_len)) + mupip_exit(ERR_MUPCLIERR); + + /* Open phase-1 output file (our input file) */ + psa->outfd = OPEN((char_ptr_t)psa->outfn, O_RDONLY RMS_OPEN_BIN); + if (FD_INVALID == psa->outfd) + { + save_errno = errno; + if (save_errno == ENOENT) + rts_error(VARLSTCNT(4) ERR_FILENOTFND, 2, RTS_ERROR_STRING((char_ptr_t)psa->outfn)); + else + { + errmsg = STRERROR(save_errno); + rts_error(VARLSTCNT(8) ERR_DEVOPENFAIL, 2, RTS_ERROR_STRING((char_ptr_t)psa->outfn), + ERR_TEXT, 2, RTS_ERROR_STRING(errmsg)); + } + } +#ifdef __MVS__ + if (-1 == gtm_zos_tag_to_policy(psa->outfd, TAG_BINARY, &realfiletag)) + TAG_POLICY_GTM_PUTMSG((char_ptr_t)psa->outfn, errno, realfiletag, TAG_BINARY); +#endif + dbc_read_p1out(psa, &psa->ofhdr, SIZEOF(p1hdr)); /* Read phase 1 output file header */ + if (0 != memcmp(psa->ofhdr.p1hdr_tag, P1HDR_TAG, SIZEOF(psa->ofhdr.p1hdr_tag))) + rts_error(VARLSTCNT(4) ERR_DBCBADFILE, 2, RTS_ERROR_STRING((char_ptr_t)psa->outfn)); + if (0 == psa->ofhdr.tot_blocks) + /* Sanity check that the output file was finished and completed */ + rts_error(VARLSTCNT(4) ERR_DBCSCNNOTCMPLT, 2, RTS_ERROR_STRING((char_ptr_t)psa->outfn)); + assert(0 != psa->ofhdr.tn); + + /* Check if region name still associates to the same file */ + dbc_find_database_filename(psa, psa->ofhdr.regname, dbfn); + + /* Notify user this is a critical change and give them the opportunity to abort */ + util_out_print("--------------------------------------------------------------------------------", FLUSH); + util_out_print("You must have a backup of database !AD before you proceed!!", FLUSH, + RTS_ERROR_STRING((char_ptr_t)psa->ofhdr.dbfn)); + util_out_print("An abnormal termination can damage the database while doing the certification !!", FLUSH); + util_out_print("Proceeding will also turn off replication and/or journaling if enabled", FLUSH); + util_out_print("--------------------------------------------------------------------------------", FLUSH); + util_out_print("Proceed? [y/n]:", FLUSH); + SCANF("%1s", ans); /* We only need one char, any more would overflow our buffer */ + if ('y' != ans[0] && 'Y' != ans[0]) + { + util_out_print("Certification phase aborted\n", FLUSH); + return; + } + util_out_print("Certification phase for database !AD beginning", FLUSH, RTS_ERROR_STRING((char_ptr_t)psa->ofhdr.dbfn)); + + /* Build database structures */ + MALLOC_INIT(psa->dbc_gv_cur_region, SIZEOF(gd_region)); + MALLOC_INIT(psa->dbc_gv_cur_region->dyn.addr, SIZEOF(gd_segment)); + psa->dbc_gv_cur_region->dyn.addr->acc_meth = dba_bg; + len = STRLEN((char_ptr_t)psa->ofhdr.dbfn); + strcpy((char_ptr_t)psa->dbc_gv_cur_region->dyn.addr->fname, (char_ptr_t)dbfn); + psa->dbc_gv_cur_region->dyn.addr->fname_len = len; + + FILE_CNTL_INIT(psa->dbc_gv_cur_region->dyn.addr); + psa->dbc_gv_cur_region->dyn.addr->file_cntl->file_type = dba_bg; + + psa->dbc_cs_data = malloc(SIZEOF(*psa->dbc_cs_data)); + fc = psa->fc = psa->dbc_gv_cur_region->dyn.addr->file_cntl; + fc->file_type = psa->dbc_gv_cur_region->dyn.addr->acc_meth = dba_bg; /* Always treat as BG mode */ + + /* Initialize for db processing - open and read in file-header, get "real" filename for comparison */ + dbc_init_db(psa); + if (0 != strcmp((char_ptr_t)psa->dbc_gv_cur_region->dyn.addr->fname, (char_ptr_t)psa->ofhdr.dbfn)) + /* File name change means db was moved or at least is not as it was when it was scanned */ + rts_error(VARLSTCNT(1) ERR_DBCNOTSAMEDB); + if (psa->ofhdr.tn > psa->dbc_cs_data->trans_hist.curr_tn) + rts_error(VARLSTCNT(1) ERR_DBCNOTSAMEDB); + psa->max_blk_len = psa->dbc_cs_data->blk_size - psa->dbc_cs_data->reserved_bytes; + + /* Initialize maximum key we may need later if we encounter gvtroot blocks */ + maxkeystructsize = SIZEOF(dbc_gv_key) + MAX_DBC_KEY_SZ - 1; + MALLOC_INIT(psa->max_key, maxkeystructsize); + psa->max_key->top = maxkeystructsize; + psa->max_key->gvn_len = 1; + *psa->max_key->base = (unsigned char)0xFF; + /* Key format: 0xFF, 0x00, 0x00 : This is higher than any valid key would be */ + psa->max_key->end = MAX_DBC_KEY_SZ - 1; + + /* Allocate update array based on fileheader values */ + psa->dbc_cs_data->max_update_array_size = psa->dbc_cs_data->max_non_bm_update_array_size = + (uint4)ROUND_UP2(MAX_NON_BITMAP_UPDATE_ARRAY_SIZE(psa->dbc_cs_data), UPDATE_ARRAY_ALIGN_SIZE); + psa->dbc_cs_data->max_update_array_size += (int4)(ROUND_UP2(MAX_BITMAP_UPDATE_ARRAY_SIZE, UPDATE_ARRAY_ALIGN_SIZE)); + update_array = malloc(psa->dbc_cs_data->max_update_array_size); + update_array_size = psa->dbc_cs_data->max_update_array_size; + + /* Now to the real work -- Read and split each block the phase 1 file recorded that still needs + to be split (concurrent updates may have "fixed" some blocks). + */ + psa->hint_blk = psa->hint_lcl = 1; + restart_transaction = p1rec_read = FALSE; + restart_cnt = 0; + for (rec_num = 0; rec_num < psa->ofhdr.blk_count || 0 < psa->gvtroot_rchildren_cnt;) + { /* There is the possibility that we are restarting the processing of a given record. In + that case we will not read the next record in but process what is already in the buffer. + This can occur if we have extended the database. */ + if (!restart_transaction) + { /* First to check is if we have any queued gvtroot_rchildren to process (described elsewhere). If we have + these, we process them now without bumping the record count. + */ + p1rec_read = FALSE; /* Assume we did NOT read from the file */ + if (0 < psa->gvtroot_rchildren_cnt) + { + psa->gvtroot_rchildren_cnt--; + memcpy((char *)&psa->rhdr, (char *)&psa->gvtroot_rchildren[psa->gvtroot_rchildren_cnt], + SIZEOF(p1rec)); + psa->gvtrchildren++; /* counter */ + DBC_DEBUG(("DBC_DEBUG: Pulling p1rec from queued gvtroot_rchildren array (%d)\n", + psa->gvtroot_rchildren_cnt)); + } else + { /* Normal processing - read record from phase one file */ + if (rec_num == psa->blocks_to_process) + { /* Maximum records processed */ + DBC_DEBUG(("DBC_DEBUG: Maximum records to process limit reached " + "- premature exit to main loop\n")); + break; + } + DBC_DEBUG(("DBC_DEBUG: ****************** Reading new p1out record (%d) *****************\n", \ + (rec_num + 1))); + dbc_read_p1out(psa, &psa->rhdr, SIZEOF(p1rec)); + if (0 != psa->rhdr.akey_len) + { /* This module does not need the ascii key so just bypass it if it exists */ + if (0 != psa->rhdr.blk_levl || SIZEOF(psa->rslt_buff) < psa->rhdr.akey_len ) + GTMASSERT; /* Must be corrupted file? */ + dbc_read_p1out(psa, (char_ptr_t)psa->rslt_buff, psa->rhdr.akey_len); + } + p1rec_read = TRUE; /* Note, not reset by restarted transaction */ + } + /* Don't want to reset the high water mark on a restarted transaction */ + if (psa->block_depth > psa->block_depth_hwm) + psa->block_depth_hwm = psa->block_depth; /* Keep track of maximum indexes we have used */ + restart_cnt = 0; + } else + { + ++restart_cnt; + if (MAX_RESTART_CNT < restart_cnt) + GTMASSERT; /* No idea what could cause this.. */ + DBC_DEBUG(("DBC_DEBUG: ****************** Restarted transaction (%d) *****************\n", \ + (rec_num + 1))); + /* "restart_transaction" is either set or cleared by dbc_split_blk() below */ + } + assert((int)psa->rhdr.blk_type); + /* Note assignment in "if" below */ + if (restart_transaction = dbc_split_blk(psa, psa->rhdr.blk_num, psa->rhdr.blk_type, + psa->rhdr.tn, psa->rhdr.blk_levl)) + psa->block_depth_hwm = -1; /* Zaps cache so all blocks are re-read */ + else if (p1rec_read) /* If rec processed was from scan phase, bump record counter */ + rec_num++; + } /* for each record in phase-1 output file or each restart or each queued rh child */ + + /* Reaching this point, the database has been updated, with no errors. We can now certify + this database as ready for the current version of GT.M + */ + util_out_print("", FLUSH); /* New line for below message in case MUPIP extension leaves prompt */ + if (0 == psa->blk_process_errors) + { + if (psa->blocks_to_process != rec_num) + { + ((sgmnt_data_ptr_t)psa->dbc_cs_data)->certified_for_upgrade_to = GDSV5; + psa->dbc_fhdr_dirty = TRUE; + gtm_putmsg(VARLSTCNT(6) ERR_DBCDBCERTIFIED, 4, RTS_ERROR_STRING((char_ptr_t)psa->ofhdr.dbfn), + RTS_ERROR_LITERAL("GT.M V5")); + } else + { + DBC_DEBUG(("DBC_DEBUG: Database certification bypassed due to records to process limit being reached\n")); + } + } else + gtm_putmsg(VARLSTCNT(4) ERR_DBCDBNOCERTIFY, 2, RTS_ERROR_STRING((char_ptr_t)psa->ofhdr.dbfn)); + + dbc_flush_fhead(psa); + dbc_close_db(psa); + CLOSEFILE_RESET(psa->outfd, rc); /* resets "psa->outfd" to FD_INVALID */ + + PRINTF("\n"); + PRINTF("Total blocks in scan phase file -- %12d [0x%08x]\n", psa->ofhdr.blk_count, psa->ofhdr.blk_count); + PRINTF("Blocks bypassed ------------------ %12d [0x%08x]\n", psa->blks_bypassed, psa->blks_bypassed); + PRINTF("Blocks processed ----------------- %12d [0x%08x]\n", psa->blks_processed, psa->blks_processed); + PRINTF("Blocks read ---------------------- %12d [0x%08x]\n", psa->blks_read, psa->blks_read); + PRINTF("Blocks read from cache ----------- %12d [0x%08x]\n", psa->blks_cached, psa->blks_cached); + PRINTF("Blocks updated ------------------- %12d [0x%08x]\n", psa->blks_updated, psa->blks_updated); + PRINTF("Blocks created ------------------- %12d [0x%08x]\n", psa->blks_created, psa->blks_created); + PRINTF("GVTROOT right children processed - %12d [0x%08x]\n", psa->gvtrchildren, psa->gvtrchildren); + + /* Release resources */ + free(update_array); + free(psa->dbc_cs_data); +#ifdef VMS + /* Some extra freeing of control blocks on VMS */ + if (NULL != FILE_INFO(psa->dbc_gv_cur_region)->fab) + free(FILE_INFO(psa->dbc_gv_cur_region)->fab); + if (NULL != FILE_INFO(psa->dbc_gv_cur_region)->nam) + { + if (NULL != FILE_INFO(psa->dbc_gv_cur_region)->nam->nam$l_esa) + free(FILE_INFO(psa->dbc_gv_cur_region)->nam->nam$l_esa); + free(FILE_INFO(psa->dbc_gv_cur_region)->nam); + } + if (NULL != FILE_INFO(psa->dbc_gv_cur_region)->xabfhc) + free(FILE_INFO(psa->dbc_gv_cur_region)->xabfhc); + if (NULL != FILE_INFO(psa->dbc_gv_cur_region)->xabpro) + free(FILE_INFO(psa->dbc_gv_cur_region)->xabpro); +#endif + free(psa->dbc_gv_cur_region->dyn.addr->file_cntl->file_info); + free(psa->dbc_gv_cur_region->dyn.addr->file_cntl); + free(psa->dbc_gv_cur_region->dyn.addr); + free(psa->dbc_gv_cur_region); + psa->dbc_gv_cur_region = NULL; + if (psa->first_rec_key) + free(psa->first_rec_key); + free(psa); +} + +/* Routine to handle the processing (splitting) of a given database block. If the current block process needs + to be restarted, this function returns TRUE. else if processing completed normally, returns FALSE. + + Routine notes: + + This routine implements a "simplistic" mini database engine. It is "simplistic" in the regards to fact that it + doesn't need to worry about concurrency issues. It also has one design assumption that we will NEVER add a + record to a level 0 block (either DT or GVT). Because of this assumption, many complications from gvcst_put(), on + which it is largely based, were non-issues and were removed (e.g. no TP). This routine has its own concepts of + "cache", cw_set elements, update arrays, etc. Following is a brief description of how these things are implemented + in this routine: + + The primary control block in this scheme is the block_info block which serves as a cache record, change array anchor, + gv_target, and so on. In short, everything that is known about a given database block is contained in this one + structure. There is an array of these structures with the name "blk_set" which is a global variable array dimensioned + at a thoroughly outrageous amount for the worst case scenario. + + There are areas within the blk_set array that are worth describing: + + - The block_depth global variable always holds the top in use index into blk_set. + - blk_set[0] describes the block that was fed to us from the phase 1 scan. It is the primary block that needs to + be split. If nothing needs to happen to it, we go to the next record and blk_set[0] get a new block in it. + - Starting with blk_set[1] through blk_set[bottom_tree_index] are first the directory tree (DT) blocks and then + (if primary was a GVT block) the global variable tree (GVT) blocks. + - Starting with blk_set[bottom_tree_index + 1] through blk_set[bottom_created_index] are newly created blocks during + split processing. + - Starting with blk_set[bottom_created_index + 1] through blk_set[block_depth] are local bit map blocks that are being + modified for the "transaction". + + This engine has a very simple cache mechanism. If a block we need is somewhere in the blk_set array (a global variable + block_depth_hwm maintains a high water mark), the cache version is used rather than forcing a re-read from disk. It is + fairly simple but seems to save a lot of reads, especially of the directory tree and the local bit_maps. + + Like gvcst_put(), once we have the blocks from the tree loaded, they are processed in reverse order as a split in one + block requires a record to be inserted into the parent block. We start with the primary block (blk_set[0]) and then + move to blk_set[bottom_tree_index] and work backwards from there until either we get to a block for which there are + no updates or we hit a root block (GVT or DT depending) at which time we are done with the primary update loop. + + After performing the block splits and creating new blocks, we double check that we have room to hold them all. If not, + we make a call to MUPIP EXTEND to extend the database for us. Since this means we have to close the file and give up + our locks on it, we also restart the transaction and force all blocks to be re-read from disk. + + Once assured we have sufficient free blocks, we start at blk_set[bottom_created_index] and work down to + blk_set[bottom_tree_index + 1] allocating and assigning block numbers to the created blocks. Part of this process also + puts the block numbers into places where the update arrays will pick them up when the referencing blocks are built. + + Once all the new blocks have been assigned, we loop through blk_set[bottom_tree_index] to blk_set[0] and create the + new versions of the blocks (for those blocks marked as being updated). A note here is that this engine does not build + update array entries for bitmap blocks, preferring instead to just update the local bitmap block buffers directly. + + The last major loop is to write to disk all the new and changed blocks to disk. There is no processing but simple IO + in this loop to minimize the potential of something going wrong. There is no recovery at this point. If this loop fails + in mid-stream, the database is toast. + +*/ +boolean_t dbc_split_blk(phase_static_area *psa, block_id blk_num, enum gdsblk_type blk_type, v15_trans_num tn, int blk_levl) +{ + int blk_len, blk_size, restart_cnt, save_block_depth; + int gvtblk_index, dtblk_index, blk_index, bottom_tree_index, bottom_created_index; + int curr_blk_len, curr_blk_levl, curr_rec_len, ins_key_len, ins_rec_len; + int curr_rec_shrink, curr_rec_offset, blks_this_lmap; + int prev_rec_offset, new_blk_len, new_rec_len, remain_offset, remain_len, blk_seg_cnt; + int new_lh_blk_len, new_rh_blk_len, created_blocks, extent_size; + int local_map_max, lbm_blk_index, lcl_blk, curr_rec_cmpc, cmpc; + int4 lclmap_not_full; + uint4 total_blks; + boolean_t dummy_bool; + boolean_t got_root, level_0, completed, insert_point, restart_transaction; + blk_segment *bs_ptr, *bs1, *blk_sega_p, *blk_array_top; + rec_hdr_ptr_t ins_rec_hdr, next_rec_hdr, new_star_hdr; + dbc_gv_key *last_rec_key; + uchar_ptr_t rec_p, next_rec_p, mid_point, cp1, lcl_map_p, new_blk_p, blk_p, blk_endp, chr_p; + unsigned short us_rec_len; + v15_trans_num curr_tn; + block_id blk_ptr; + block_id bitmap_blk_num, *lhs_block_id_p, *rhs_block_id_p, allocated_blk_num; + block_info *blk_set_p, *blk_set_new_p, *blk_set_prnt_p, *blk_set_bm_p, *blk_set_rhs_p; + block_info restart_blk_set; + + DEBUG_ONLY( + boolean_t first_time = FALSE; + ) + + /* First order of business is to read the required block in */ + psa->block_depth = -1; + blk_size = psa->dbc_cs_data->blk_size; /* BLK_FINI macro needs a local copy */ + dbc_read_dbblk(psa, blk_num, blk_type); + + /* Now that we have read the block in, let us see if it is still a "problem" block. If its + TN has changed, that is an indicator that is should NOT be a problem block any longer + with the sole exception of a TN RESET having been done on the DB since phase 1. In that + case, we will still insist on a phase 1 rerun as some of our sanity checks have disappeared. + */ + assert(0 == psa->block_depth); + blk_p = psa->blk_set[0].old_buff; + assert(blk_p); + blk_len = psa->blk_set[0].blk_len; + + /* If the block is still too large, sanity check on TN at phase 1 and now. Note that it is + possible in an index block for the TN to have changed yet the block is otherwise unmodified + if (1) this is an index block and (2) a record is being inserted before the first record in + the block. In this case, the new record is put into the new (LH) sibling and the entire existing + block is put unmodified into the RH side in the existing block. The net result is that only + the TN changes in this block and if the block is too full it is not split. This will never + happen for a created block though. It can only hapen for existing index blocks. Note if the + block is not (still) too full that we cannot yet say this block has nothing to happen to it + because if it is a gvtroot block, we need to record its right side children further down. + */ + GET_ULONG(curr_tn, &((v15_blk_hdr_ptr_t)blk_p)->tn); + if ((UNIX_ONLY(8) VMS_ONLY(9) > blk_size - blk_len) && (curr_tn != tn) && (gdsblk_gvtleaf == blk_type)) + { + /* Block has been modified: Three possible reasons it is not fixed: + 1) The user was playing with reserved bytes and set it too low allowing some + large blocks to be created we did not know about (but thankfully just caught). + 2) User ran a recover after running phase 1 that re-introduced some too-large + blocks. This is a documented no-no but we have no way to enforce it on V4. + 3) There was a TN reset done. + All three of these causes require a rerun of the scan phase. + */ + rts_error(VARLSTCNT(3) ERR_DBCMODBLK2BIG, 1, blk_num); + } + + /* Isolate the full key in the first record of the block */ + dbc_init_key(psa, &psa->first_rec_key); + dbc_find_key(psa, psa->first_rec_key, blk_p + SIZEOF(v15_blk_hdr), psa->blk_set[0].blk_levl); + psa->first_rec_key->gvn_len = USTRLEN((char_ptr_t)psa->first_rec_key->base); /* The GVN we need to lookup in the DT */ + + /* Possibilities at this point: + 1) We are looking for a DT (directory tree) block. + 2) We are looking for a GVT (global variable tree) block. + + We lookup first_rec_key in the directory tree. If (1) we pass the block level we are searching for + as a parameter. If (2), we pass -1 as the block level we are searching for as we need a complete + search of the leaf level DT in order to find the GVN. + + If (1) then the lookup is complete and verification and (later) block splitting can begin. If (2), we need to + take the pointer from the found DT record which points to the GVT root block and start our search again + from there using the level from the original block as a stopping point. One special case here is if our + target block was a gvtroot block, we don't need to traverse the GVT tree to find it. We get it from the + directory tree and stop our search there. + */ + switch(blk_type) + { + case gdsblk_dtindex: + case gdsblk_dtleaf: + case gdsblk_dtroot: + /* Since our search is to end in the dt tree, stop when we get to the requisite level */ + blk_index = dbc_find_dtblk(psa, psa->first_rec_key, blk_levl); + if (0 > blk_index) + { /* Integrity error encountered or record not found. We cannot proceed */ + assert(FALSE); + rts_error(VARLSTCNT(8) ERR_DBCINTEGERR, 2, RTS_ERROR_STRING((char_ptr_t)psa->ofhdr.dbfn), + ERR_TEXT, 2, + RTS_ERROR_LITERAL("Unable to find index (DT) record for an existing global")); + } + break; + case gdsblk_gvtindex: + case gdsblk_gvtleaf: + /* Search all the way down to lvl 0 to get a dtleaf block */ + dtblk_index = dbc_find_dtblk(psa, psa->first_rec_key, 0); + if (0 > dtblk_index) + { /* Integrity error encountered or record not found. We cannot proceed */ + assert(FALSE); + rts_error(VARLSTCNT(8) ERR_DBCINTEGERR, 2, RTS_ERROR_STRING((char_ptr_t)psa->ofhdr.dbfn), + ERR_TEXT, 2, RTS_ERROR_LITERAL("Unable to locate DT leaf (root) block")); + } + assert(0 == ((v15_blk_hdr_ptr_t)psa->blk_set[dtblk_index].old_buff)->levl); + /* Note level 0 directory blocks can have collation data in them but it would be AFTER + the block pointer which is the first thing in the record after the key. + */ + GET_ULONG(blk_ptr, (psa->blk_set[dtblk_index].curr_rec + SIZEOF(rec_hdr) + + psa->blk_set[dtblk_index].curr_blk_key->end + 1 + - ((rec_hdr *)psa->blk_set[dtblk_index].curr_rec)->cmpc)); + gvtblk_index = dbc_read_dbblk(psa, blk_ptr, gdsblk_gvtroot); + assert(-1 != gvtblk_index); + /* If our target block was not the gvtroot block we just read in then we keep scanning for our + target record. Otherwise, the scan stops here. + */ + if (0 != gvtblk_index) + { + blk_index = dbc_find_record(psa, psa->first_rec_key, gvtblk_index, blk_levl, gdsblk_gvtroot, FALSE); + if (0 > blk_index) + { + if (-1 == blk_index) + { /* Integrity error encountered. We cannot proceed */ + assert(FALSE); + rts_error(VARLSTCNT(8) ERR_DBCINTEGERR, 2, + RTS_ERROR_STRING((char_ptr_t)psa->ofhdr.dbfn), + ERR_TEXT, 2, + RTS_ERROR_LITERAL("Unable to find index record for an existing global")); + } else if (-2 == blk_index) + { /* Record was not found. Record has been deleted since we last + found it. Elicits a warning message in DEBUG mode but is otherwise ignored. + */ + assert(FALSE); + DBC_DEBUG(("DBC_DEBUG: Block split of blk 0x%x bypassed because its " + "key could not be located in the GVT\n", blk_num)); + psa->blks_bypassed++; + psa->blks_read += psa->block_depth; + /* Only way to properly update the count of cached records is to run the list + and check them. + */ + for (blk_index = psa->block_depth, blk_set_p = &psa->blk_set[blk_index]; + 0 <= blk_index; + --blk_index, --blk_set_p) + { /* Check each block we read */ + if (gdsblk_create != blk_set_p->usage && blk_set_p->found_in_cache) + psa->blks_cached++; + } + return FALSE; /* No restart necessary */ + } else + GTMASSERT; + } + } else + { /* This is a gvtroot block and is the subject of our search */ + blk_index = gvtblk_index; + assert(gdsblk_gvtroot == psa->blk_set[0].blk_type); + } + break; + default: + GTMASSERT; + } + /* The most recently accessed block (that terminated the search) should be the block + we are looking for (which should have been found in the cache as block 0. If not, + there is an integrity error and we should not continue. + */ + if (0 != blk_index) + { /* Integrity error encountered. We cannot proceed */ + assert(FALSE); + rts_error(VARLSTCNT(8) ERR_DBCINTEGERR, 2, RTS_ERROR_STRING((char_ptr_t)psa->ofhdr.dbfn), + ERR_TEXT, 2, + RTS_ERROR_LITERAL("Did not locate record in same block as we started searching for")); + } + + /* If this is a gvtroot type block, we have some extra processing to do. Following is a description of + the issue we are addressing here. If a gvtroot block is "too large" and was too large at the time + the scan was run, it will of course be identified by the scan as too large. Prior to running the scan, + the reserved bytes field was set so no more too-full blocks can be created. But if a gvtroot block is + identified by the scan and subsequently has to be split by normal GTM processing before the certify + can be done, the too-full part of the block can (in totality) end up in the right hand child of the + gvtroot block (not obeying the reserved bytes rule). But the gvtroot block is the only one that was + identified by the scan and certify may now miss the too-full block in the right child. Theoretically, + the entire right child chain of the gvtroot block can be too full. Our purpose here is that when we + have identified a gvtblock as being too full, we pause here to read the right child chain coming off + of that block all the way down to (but not including) block level 0. Each of these blocks will be + processed to check for being too full. The way we do this is to run the chain and build p1rec entries + in the gvtroot_rchildren[] array. When we are at the top of the processing loop, we will take these + array entries over records from the phase one input file. We only load up the array if it is empty. + Otherwise, the assumption is that we are re-processing and the issue has already been handled. + */ + blk_set_p = &psa->blk_set[0]; + if (gdsblk_gvtroot == blk_set_p->blk_type && 0 == psa->gvtroot_rchildren_cnt) + { + DBC_DEBUG(("DBC_DEBUG: Encountered gvtroot block (block %d [0x%08x]), finding/queueing children\n", + blk_set_p->blk_num, blk_set_p->blk_num)); + save_block_depth = psa->block_depth; /* These reads are temporary and should not remain in cache so + we will restore block_depth after we are done. + */ + /* Attempting to locate the maximum possible key for this database should read the list of right + children into the cache. Pretty much any returncode from dbc_find_record is possible. We usually + aren't going to find the global which may come up as not found or an integrity error or it could + possibly even be found. Just go with what it gives us. Not much verification we can do on it. + */ + blk_index = dbc_find_record(psa, psa->max_key, 0, 0, gdsblk_gvtroot, TRUE); + /* Pull children (if any) out of cache and put into queue for later processing */ + for (blk_index = save_block_depth + 1; + blk_index <= psa->block_depth && gdsblk_gvtleaf != psa->blk_set[blk_index].blk_type; + ++blk_index, ++psa->gvtroot_rchildren_cnt) + { /* Fill in p1rec type entry in gvtroot_rchildren[] for later */ + DBC_DEBUG(("DBC_DEBUG: Right child block: blk_index: %d blk_num: %d [0x%08x] blk_levl: %d\n", + blk_index, psa->blk_set[blk_index].blk_num, psa->blk_set[blk_index].blk_num, + psa->blk_set[blk_index].blk_levl)); + psa->gvtroot_rchildren[psa->gvtroot_rchildren_cnt].tn = psa->blk_set[blk_index].tn; + psa->gvtroot_rchildren[psa->gvtroot_rchildren_cnt].blk_num = psa->blk_set[blk_index].blk_num; + psa->gvtroot_rchildren[psa->gvtroot_rchildren_cnt].blk_type = psa->blk_set[blk_index].blk_type; + psa->gvtroot_rchildren[psa->gvtroot_rchildren_cnt].blk_levl = psa->blk_set[blk_index].blk_levl; + psa->gvtroot_rchildren[psa->gvtroot_rchildren_cnt].akey_len = 0; + } + psa->block_depth = save_block_depth; + blk_index = 0; /* reset to start *our* work in the very first block */ + } + + /* Now we have done the gvtroot check if we were going to. If this particular block has sufficient room in it + we don't need to split it of course. + */ + if (UNIX_ONLY(8) VMS_ONLY(9) <= blk_size - blk_len) + { /* This block has room now - no longer need to split it */ + DBC_DEBUG(("DBC_DEBUG: Block not processed as it now has sufficient room\n")); + psa->blks_bypassed++; + psa->blks_read++; + if (psa->blk_set[0].found_in_cache) + psa->blks_cached++; + return FALSE; /* No restart needed */ + } + + /* Beginning of block update/split logic. We need to process the blocks in the reverse order from the + tree path. This means blk_set[0] which is actually the block we want to split must be the first + in our path. We then need to process the block array backwards in case the changes made to those + records cause subsequent splits. + + First order of business is to find a suitable place to split this block .. Run through + the records in the block until we are "halfway" through the block. Split so that the first record + (after the first) whose end point is in the "second half" of the block will be the first record of + the second half or right hand side block after the split. This makes sure that the left side has at + least one record in it. We already know that this block has at least 2 records in it or it would not + need splitting. + */ + rec_p = blk_p + SIZEOF(v15_blk_hdr); + blk_set_p->curr_rec = rec_p; + dbc_find_key(psa, blk_set_p->curr_blk_key, rec_p, blk_set_p->blk_levl); + GET_USHORT(us_rec_len, &((rec_hdr *)rec_p)->rsiz); + curr_rec_len = us_rec_len; + next_rec_p = rec_p + curr_rec_len; + blk_set_p->curr_match = 0; /* First record of block always cmpc 0 */ + blk_len = ((v15_blk_hdr_ptr_t)blk_p)->bsiz; + blk_endp = blk_p + blk_len; + mid_point = blk_p + blk_size / 2; + do + { /* Keep scanning the next record until you find the split point which is the first record that straddles the + * mid-point of the block. This loop makes sure the prev_key and curr_key fields are correctly set when we + * enter the processing loop below. + */ + blk_set_p->prev_match = blk_set_p->curr_match; + memcpy(blk_set_p->prev_blk_key, blk_set_p->curr_blk_key, (SIZEOF(dbc_gv_key) + blk_set_p->curr_blk_key->end)); + rec_p = next_rec_p; /* Must be at least one record in LHS and one in RHS */ + blk_set_p->prev_rec = blk_set_p->curr_rec; + blk_set_p->curr_rec = rec_p; + GET_USHORT(us_rec_len, &((rec_hdr *)rec_p)->rsiz); + curr_rec_len = us_rec_len; + dbc_find_key(psa, blk_set_p->curr_blk_key, rec_p, blk_set_p->blk_levl); + blk_set_p->curr_match = ((rec_hdr *)rec_p)->cmpc; + next_rec_p = rec_p + curr_rec_len; + if (next_rec_p >= blk_endp) /* We have reached the last record in the block. Cannot skip anymore. */ + break; + if (next_rec_p >= mid_point) + { /* The current record straddles the mid-point of the almost-full block. This is most likely going + * to be the split point. If splitting at the current record causes the RHS block to continue to + * be too-full and there is still room in the LHS block we will scan one more record in this loop. + * Scanning this one more record should make the RHS block no longer too-full. This is asserted below. + */ + /* Compute the sizes of the LHS and RHS blocks assuming the current record moves into each of them */ + if (blk_set_p->blk_levl) + { /* Index block. The current record is changed into a *-key (a simple star key rec) */ + new_lh_blk_len = (int)((rec_p - blk_p) + BSTAR_REC_SIZE); + } else + { /* Data block. Always simple split (no inserted record) */ + new_lh_blk_len = (int)(next_rec_p - blk_p); + assert(gdsblk_gvtleaf == blk_set_p->blk_type || gdsblk_dtleaf == blk_set_p->blk_type); + } + assert(0 < new_lh_blk_len); + /* assert that the LHS block without the current record is guaranteed not to be too-full */ + assert((new_lh_blk_len - (next_rec_p - rec_p)) <= psa->max_blk_len); + /* Right hand side has key of curr_rec expanded since is first key of blcok */ + new_rh_blk_len = (int)(SIZEOF(v15_blk_hdr) + blk_set_p->curr_match + blk_len - (rec_p - blk_p) ); + assert(0 < new_rh_blk_len); + if ((new_rh_blk_len <= psa->max_blk_len) || (new_lh_blk_len > psa->max_blk_len)) + break; + assert(FALSE == first_time); /* assert we never scan more than one record past mid-point of the block */ + DEBUG_ONLY(first_time = TRUE;) + } + } while (TRUE); + assert((rec_p - blk_p) < ((v15_blk_hdr_ptr_t)blk_p)->bsiz); + + /* Block processing loop */ + bottom_tree_index = psa->block_depth; /* Record end of the tree in case need bit map blocks later */ + update_array_ptr = update_array; /* Reset udpate array */ + DBC_DEBUG(("DBC_DEBUG: Beginning split processing loop\n")); + for (completed = FALSE; !completed;) + { /* Backwards process until we hit a block with no changes to it */ + DBC_DEBUG(("DBC_DEBUG: ******** Top of blk process loop for block index %d\n", blk_index)); + assert(0 <= blk_index); + blk_set_p = &psa->blk_set[blk_index]; + assert(blk_set_p->blk_len == ((v15_blk_hdr_ptr_t)blk_set_p->old_buff)->bsiz); + assert(blk_set_p->blk_levl == ((v15_blk_hdr_ptr_t)blk_set_p->old_buff)->levl); + curr_blk_len = blk_set_p->blk_len; + curr_blk_levl = blk_set_p->blk_levl; + if (0 != blk_set_p->ins_rec.ins_key->end) + { + ins_key_len = blk_set_p->ins_rec.ins_key->end + 1; + ins_rec_len = ins_key_len + SIZEOF(block_id); /* We only ever insert index records */ + } else + ins_key_len = ins_rec_len = 0; + blk_p = blk_set_p->old_buff; + /* If ins_rec_len has a non-zero value, then we need to reset the values for prev_match and + key_match. These values were computed using the original scan key as their basis. Now we + are using these fields to insert a new key. The positioning is still correct but the + number of matching characters is potentially different. + */ + if (ins_rec_len) + { + if (0 != blk_set_p->prev_blk_key->end) + { /* There is a "previous record" */ + insert_point = dbc_match_key(blk_set_p->prev_blk_key, blk_set_p->blk_levl, + blk_set_p->ins_rec.ins_key, &blk_set_p->prev_match); + assert(!insert_point); /* This is prior to insert point (sanity check) */ + } + insert_point = dbc_match_key(blk_set_p->curr_blk_key, blk_set_p->blk_levl, + blk_set_p->ins_rec.ins_key, &blk_set_p->curr_match); + assert(insert_point); /* This is supposed to *be* the insert point */ + } + /* Make convenient copies of some commonly used record fields */ + curr_rec_cmpc = ((rec_hdr *)blk_set_p->curr_rec)->cmpc; + curr_rec_shrink = blk_set_p->curr_match - curr_rec_cmpc; + curr_rec_offset = (int)(blk_set_p->curr_rec - blk_set_p->old_buff); + GET_USHORT(us_rec_len, &((rec_hdr *)blk_set_p->curr_rec)->rsiz); + curr_rec_len = us_rec_len; + prev_rec_offset = (int)(blk_set_p->prev_rec - blk_set_p->old_buff); + got_root = (gdsblk_dtroot == blk_set_p->blk_type) || (gdsblk_gvtroot == blk_set_p->blk_type); + /* Decide if this record insert (if an insert exists) will cause a block split or not. If this + is the first block in the tree (the one we got from the phase 1 file), there will be no insert. + If we find a block that does not need to change, we are done and can exit the loop. + This differs from the regular GT.M runtime which must keep checking even the split blocks + but since we never add data to a level 0 block being split, we will never create split-off + blocks that themselves are (still) too full. + */ + assert(gdsblk_read == blk_set_p->usage); + new_blk_len = (int)(ins_rec_len ? (curr_blk_len + curr_rec_cmpc + SIZEOF(rec_hdr) + ins_rec_len + - blk_set_p->prev_match - blk_set_p->curr_match) + : curr_blk_len); /* No inserted rec, size does not change */ + if (new_blk_len <= psa->max_blk_len) + { /* "Simple" case .. we do not need a block split - only (possibly) a record added. Note + that this is the only path where there may not be a "previous" record so we + have to watch for that possibility. + */ + assert(0 != blk_index); /* Never insert a record into target blk so should never be here */ + /* In this path we should always have an inserted record length. We should have detected we + were done in an earlier loop iteration. + */ + assert(ins_rec_len); + DBC_DEBUG(("DBC_DEBUG: Block index %d is a simple update\n", blk_index)); + /* We must have an insert at this point and since we only ever insert records into + index blocks, we must be in that situation */ + assert(0 != curr_blk_levl); + blk_set_p->usage = gdsblk_update; /* It's official .. blk is being modified */ + /* We have a record to insert into this block but no split is needed */ + BLK_INIT(bs_ptr, bs1); + blk_set_p->upd_addr = bs1; /* Save address of our update array */ + if (0 != blk_set_p->prev_blk_key->end) + { /* First piece is block prior to the record + key + value */ + BLK_SEG(bs_ptr, + blk_set_p->old_buff + SIZEOF(v15_blk_hdr), + (curr_rec_offset - SIZEOF(v15_blk_hdr))); + } + BLK_ADDR(ins_rec_hdr, SIZEOF(rec_hdr), rec_hdr); + /* Setup new record header */ + new_rec_len = (int)(SIZEOF(rec_hdr) + ins_rec_len - blk_set_p->prev_match); + ins_rec_hdr->rsiz = new_rec_len; + ins_rec_hdr->cmpc = blk_set_p->prev_match; + BLK_SEG(bs_ptr, (sm_uc_ptr_t)ins_rec_hdr, SIZEOF(rec_hdr)); + /* Setup key */ + BLK_ADDR(cp1, + blk_set_p->ins_rec.ins_key->end + 1 - blk_set_p->prev_match, + unsigned char); + memcpy(cp1, blk_set_p->ins_rec.ins_key->base + blk_set_p->prev_match, + blk_set_p->ins_rec.ins_key->end + 1 - blk_set_p->prev_match); + BLK_SEG(bs_ptr, cp1, blk_set_p->ins_rec.ins_key->end + 1 - blk_set_p->prev_match); + /* Setup value (all index records have value of size "block_id". The proper value is + either there already or will be when we go to commit these changes. */ + BLK_SEG(bs_ptr, (sm_uc_ptr_t)&blk_set_p->ins_rec.blk_id, SIZEOF(block_id)); + /* For index blocks, we know that since a star key is the last record in the block + (which is the last record that can be curr_rec) that there is a trailing portion + of the block we need to output. + */ + BLK_ADDR(next_rec_hdr, SIZEOF(rec_hdr), rec_hdr); /* Replacement rec header */ + next_rec_hdr->rsiz = curr_rec_len - curr_rec_shrink; + next_rec_hdr->cmpc = blk_set_p->curr_match; + BLK_SEG(bs_ptr, (sm_uc_ptr_t)next_rec_hdr, SIZEOF(rec_hdr)); + remain_offset = curr_rec_shrink + SIZEOF(rec_hdr); /* Where rest of record plus any + further records begin */ + remain_len = curr_blk_len - curr_rec_offset; + BLK_SEG(bs_ptr, + blk_set_p->curr_rec + remain_offset, + remain_len - remain_offset); + if (0 == BLK_FINI(bs_ptr, bs1)) + GTMASSERT; + assert(blk_seg_cnt == new_blk_len); + DBC_DEBUG(("DBC_DEBUG: Stopping block scan after simple update (no further inserts to previous lvls)\n")); + completed = TRUE; + break; + } else + { /* The block is either already too large or would be too large when the record is inserted + and so it must be split. + + There are two different ways a block can be split. It can either be split so that: + + (1) the inserted record is at the end of the left block or, + + (2) the record is the first record in the right half. + + Compute the left/right block sizes for these two options and see which one does not + force a secondary block split (one of them must be true here unlike in GT.M code because + here we are NEVER adding a record to a level 0 block, we only split lvl 0 blocks as + needed). Note that the case where we are splitting a level 0 block with no record insert + is treated as an unremarkable variant of option (1) as described above. + + Follow the conventions of gvcst_put (LHS to new block, RHS to old block): + + (1) If we are inserting the record into the lefthand side then a new split-off block will + receive the first part of the block including the record. The remainder of the block is + placed into the current (existing) block. + + (2) If we are putting the record into the righthand side, then a new split-off block will + receive the first part of the block. The new record plus the remainder of the block is + placed into the current block. + + The sole exception to the above is if a root block (either DT or GVT) is being split. In + that case, BOTH the LHS and RHS become NEW blocks and the root block is (a) increased in + level and (b) contains only entries for the two created blocks. + + Note that gvcst_put has several additional checks and balances here that we are forgoing + such as making sure the blocks are as balanced as possible, concurrency concerns, etc. They + add un-needed complications to this one-time code. Any inefficiencies here can be undone + with a pass of MUPIP REORG. + */ + DBC_DEBUG(("DBC_DEBUG: Block index %d needs to be split\n", blk_index)); + /* First up is split so that the inserted record (if any) is the last record in the left + hand block. Note if this is an index block, the last record must be a star key rec as per + option (1) above. + */ + if (curr_blk_levl) + /* Index block. Two cases: (a) We are adding a key to the end in which case it is just + a simple star key rec or (b) No record is being added so the previous record is + changed into a star key rec. + */ + new_lh_blk_len = (int)(curr_rec_offset + BSTAR_REC_SIZE + - (ins_rec_len ? 0 : (blk_set_p->curr_rec - blk_set_p->prev_rec))); + else + { + /* Data block. Always simple split (no inserted record) */ + new_lh_blk_len = curr_rec_offset; + assert(gdsblk_gvtleaf == blk_set_p->blk_type || gdsblk_dtleaf == blk_set_p->blk_type); + } + assert(0 < new_lh_blk_len); + /* Right hand side has key of curr_rec expanded since is first key of blcok */ + new_rh_blk_len = (int)(SIZEOF(v15_blk_hdr) + ((rec_hdr *)blk_set_p->curr_rec)->cmpc + + (curr_blk_len - curr_rec_offset)); + assert(0 < new_rh_blk_len); + /* Common initialization */ + ++psa->block_depth; /* Need a new block to split into */ + if (MAX_BLOCK_INFO_DEPTH <= psa->block_depth) + GTMASSERT; + DBC_DEBUG(("DBC_DEBUG: Block index %d used for newly created split (lhs) block\n", psa->block_depth)); + blk_set_new_p = &psa->blk_set[psa->block_depth]; + dbc_init_blk(psa, blk_set_new_p, -1, gdsblk_create, new_lh_blk_len, curr_blk_levl); + if (got_root) + /* If root, the LHS sub-block is a different type */ + blk_set_new_p->blk_type = (gdsblk_gvtroot == blk_set_p->blk_type) + ? gdsblk_gvtindex : gdsblk_dtindex; + else + blk_set_new_p->blk_type = blk_set_p->blk_type; + /* Complete our LHS block */ + BLK_INIT(bs_ptr, bs1); /* Our new block to create */ + blk_set_new_p->upd_addr = bs1; + level_0 = (0 == curr_blk_levl); + /* See if they fit in their respective blocks */ + if (level_0 || (new_lh_blk_len <= psa->max_blk_len) && (new_rh_blk_len <= psa->max_blk_len)) + { /* Method 1 - record goes to left-hand side */ + DBC_DEBUG(("DBC_DEBUG: Method 1 block lengths: lh: %d rh: %d max_blk_len: %d\n", \ + new_lh_blk_len, new_rh_blk_len, psa->max_blk_len)); + /* New update array for new block */ + if (level_0) + { /* Level 0 block, we are only splitting it -- never adding a record */ + assert(curr_rec_offset <= psa->max_blk_len); + BLK_SEG(bs_ptr, blk_set_p->old_buff + SIZEOF(v15_blk_hdr), + curr_rec_offset - SIZEOF(v15_blk_hdr)); + assert(0 == ins_rec_len); /* Never insert records to lvl0 */ + if (new_rh_blk_len > psa->max_blk_len) + { /* Case of a data block that has a DBCREC2BIG error unnoticed by DBCERTIFY SCAN. + * Should not happen normally. But in case it does in production, we will handle + * it by NOT certifying the database and requiring a rerun of the SCAN + */ + assert(FALSE); + gtm_putmsg(VARLSTCNT(6) ERR_DBCREC2BIGINBLK, 4, + blk_num, psa->dbc_cs_data->max_rec_size, + psa->dbc_gv_cur_region->dyn.addr->fname_len, + psa->dbc_gv_cur_region->dyn.addr->fname); + psa->blk_process_errors++; /* must be zero to certify db at end */ + } + } else + { /* Index block -- may or may not be adding a record. + If adding a record, the inserted record becomes a star key record. + If not adding a record the last record is morphed into a star key record. + */ + BLK_SEG(bs_ptr, blk_set_p->old_buff + SIZEOF(v15_blk_hdr), + (ins_rec_len ? curr_rec_offset : prev_rec_offset) + - SIZEOF(v15_blk_hdr)); + BLK_ADDR(new_star_hdr, SIZEOF(rec_hdr), rec_hdr); + new_star_hdr->rsiz = BSTAR_REC_SIZE; + new_star_hdr->cmpc = 0; + BLK_SEG(bs_ptr, (uchar_ptr_t)new_star_hdr, SIZEOF(rec_hdr)); + BLK_SEG(bs_ptr, (ins_rec_len ? (uchar_ptr_t)&blk_set_p->ins_rec.blk_id + : (blk_set_p->prev_rec + SIZEOF(rec_hdr) + + blk_set_p->prev_blk_key->end + 1 + - ((rec_hdr *)blk_set_p->prev_rec)->cmpc)), + SIZEOF(block_id)); + } + /* Complete our LHS block */ + if (0 == BLK_FINI(bs_ptr, bs1)) + GTMASSERT; + assert(blk_seg_cnt == new_lh_blk_len); + /* Remember key of last record in this block */ + if (0 == ins_rec_len) + last_rec_key = blk_set_p->prev_blk_key; + else + last_rec_key = blk_set_p->ins_rec.ins_key; + if (!got_root) + { /* New block created, insert record to it in parent block. To do this we create + a record with the last key in this LH block to be inserted between curr_rec + and prev_rec of the parent block. + */ + if (0 == blk_index) + blk_set_prnt_p = &psa->blk_set[bottom_tree_index]; /* Cycle back up to parent */ + else + blk_set_prnt_p = blk_set_p - 1; + assert(blk_set_prnt_p != &psa->blk_set[0]); + assert(NULL != last_rec_key); + /* Note: We do not need the "+ 1" on the key length since SIZEOF(dbc_gv_key) contains + the first character of the key so the "+ 1" to get the last byte of the key is + already integrated into the length + */ + memcpy(blk_set_prnt_p->ins_rec.ins_key, last_rec_key, + SIZEOF(dbc_gv_key) + last_rec_key->end); + /* Setup so that creation of the blk_set_new_p block can then set its block id into + our parent block's insert rec buffer which will be made part of the inserted + record at block build time + */ + blk_set_new_p->ins_blk_id_p = &blk_set_prnt_p->ins_rec.blk_id; + blk_set_rhs_p = blk_set_p; /* Use original block for rhs */ + blk_set_rhs_p->usage = gdsblk_update; + } else + { /* Have root block: need to put the RHS into a new block too */ + DBC_DEBUG(("DBC_DEBUG: Splitting root block, extra block to be created\n")); + ++psa->block_depth; /* Need a new block to split into */ + if (MAX_BLOCK_INFO_DEPTH <= psa->block_depth) + GTMASSERT; + blk_set_rhs_p = &psa->blk_set[psa->block_depth]; + dbc_init_blk(psa, blk_set_rhs_p, -1, gdsblk_create, new_rh_blk_len, curr_blk_levl); + /* We will put the pointers to both this block and the RHS we build next + into the original root block -- done later when RHS is complete */ + /* If root, the RHS sub-block is a different type */ + blk_set_rhs_p->blk_type = (gdsblk_gvtroot == blk_set_p->blk_type) + ? gdsblk_gvtindex : gdsblk_dtindex; + } + + /**** Now build RHS into either current or new block ****/ + BLK_INIT(bs_ptr, bs1); + blk_set_rhs_p->upd_addr = bs1; /* Block building roadmap.. */ + BLK_ADDR(next_rec_hdr, SIZEOF(rec_hdr), rec_hdr); + next_rec_hdr->rsiz = curr_rec_len + curr_rec_cmpc; + next_rec_hdr->cmpc = 0; + BLK_SEG(bs_ptr, (uchar_ptr_t)next_rec_hdr, SIZEOF(rec_hdr)); + /* Copy the previously compressed part of the key out of curr_rec. Note, if this + key is a star rec key, nothing is written because cmpc is zero */ + if (curr_rec_cmpc) + { + BLK_ADDR(cp1, curr_rec_cmpc, unsigned char); + memcpy(cp1, blk_set_p->curr_blk_key->base, curr_rec_cmpc); + BLK_SEG(bs_ptr, cp1, curr_rec_cmpc); + } + /* Remainder of existing block */ + BLK_SEG(bs_ptr, + blk_set_p->curr_rec + SIZEOF(rec_hdr), + curr_blk_len - curr_rec_offset - SIZEOF(rec_hdr)); + /* Complete update array */ + if (0 == BLK_FINI(bs_ptr, bs1)) + GTMASSERT; + assert(blk_seg_cnt == new_rh_blk_len); + } else + { /* Recompute sizes for inserted record being in righthand block as per + method (2) */ + DBC_DEBUG(("DBC_DEBUG: Method 1 created invalid blocks: lh: %d rh: %d " \ + "max_blk_len: %d -- trying method 2\n", new_lh_blk_len, new_rh_blk_len, \ + psa->max_blk_len)); + /* By definition we *must* have an inserted record in this path */ + assert(0 != ins_rec_len); + /* New block sizes - note because we *must* be inserting a record in this method, + the only case considered here is when we are operating on an index block. + */ + assert(!level_0); + /* Last record turns into star key record */ + new_lh_blk_len = (int)(curr_rec_offset + BSTAR_REC_SIZE - + (blk_set_p->curr_rec - blk_set_p->prev_rec) ); + assert(0 < new_lh_blk_len); + new_rh_blk_len = (int)(SIZEOF(v15_blk_hdr) + SIZEOF(rec_hdr) + + ins_rec_len + curr_blk_len - (curr_rec_offset) - curr_rec_shrink); + assert(0 < new_rh_blk_len); + if (new_lh_blk_len > psa->max_blk_len || new_rh_blk_len > psa->max_blk_len) + { /* This is possible if we are inserting a record into a block (and thus we are + not picking the insertion point) and the insertion point is either the first or + next-to-last record in the block such that neither method 1 nor 2 can create blocks + of acceptable size. In this case, although this problem block is likely on the + list of blocks to process, we cannot wait and thus must perform the split now. + To do that, we call this same routine recursively with the necessary parms to + process *THIS* block. Since this will destroy all the structures we had built + up, signal a transaction restart which will re-read everything and should allow + the transaction we were processing to proceed. + */ + if (curr_blk_len <= psa->max_blk_len) + /* Well, that wasn't the problem, something else is wrong */ + rts_error(VARLSTCNT(8) ERR_DBCINTEGERR, 2, + RTS_ERROR_STRING((char_ptr_t)psa->ofhdr.dbfn), + ERR_TEXT, 2, RTS_ERROR_LITERAL("Unable to split block appropriately")); + /* If we do have to restart, we won't be able to reinvoke dbc_split_blk() with the + parms taken from the current blk_set_p as that array will be overwritten by the + recursion. Save the current blk_set_p so we can use it in a restartable context. + */ + restart_blk_set = *blk_set_p; + for (restart_cnt = 0, restart_transaction = TRUE; + restart_transaction; + ++restart_cnt) + { + if (MAX_RESTART_CNT < restart_cnt) + GTMASSERT; /* No idea what could cause this */ + DBC_DEBUG(("DBC_DEBUG: *** *** Recursive call to handle too large block 0x%x\n", \ + restart_blk_set.blk_num)); + psa->block_depth_hwm = -1; /* Zaps cache so all blocks are re-read */ + restart_transaction = dbc_split_blk(psa, restart_blk_set.blk_num, + restart_blk_set.blk_type, restart_blk_set.tn, + restart_blk_set.blk_levl); + } + return TRUE; /* This transaction must restart */ + } + DBC_DEBUG(("DBC_DEBUG: Method 2 block lengths: lh: %d rh: %d max_blk_len: %d\n", \ + new_lh_blk_len, new_rh_blk_len, psa->max_blk_len)); + + /* Start building (new) LHS block - for this index record, the record before the split + becomes a new *-key. + + Note: If the block split was caused by our appending the new record + to the end of the block, this code causes the record PRIOR to the + current *-key to become the new *-key. + */ + BLK_SEG(bs_ptr, + blk_set_p->old_buff + SIZEOF(v15_blk_hdr), + prev_rec_offset - SIZEOF(v15_blk_hdr)); + /* Replace last record with star key rec */ + BLK_ADDR(new_star_hdr, SIZEOF(rec_hdr), rec_hdr); + new_star_hdr->rsiz = BSTAR_REC_SIZE; + new_star_hdr->cmpc = 0; + BLK_SEG(bs_ptr, (uchar_ptr_t)new_star_hdr, SIZEOF(rec_hdr)); + /* Output pointer from prev_rec as star key record's value */ + BLK_SEG(bs_ptr, blk_set_p->curr_rec - SIZEOF(block_id), SIZEOF(block_id)); + /* Complete our LHS block */ + if (0 == BLK_FINI(bs_ptr, bs1)) + GTMASSERT; + assert(blk_seg_cnt == new_lh_blk_len); + if (!got_root) + { + /* New block created, insert record to it in parent block. To do this we create + a record with the last key in this LH block to be inserted between curr_rec + and prev_rec of the parent block. + */ + if (0 == blk_index) + blk_set_prnt_p = &psa->blk_set[bottom_tree_index]; /* Cycle back up to parent */ + else + blk_set_prnt_p = blk_set_p - 1; + assert(blk_set_prnt_p != &psa->blk_set[0]); + assert(NULL != blk_set_p->prev_blk_key); + /* Note: We do not need the "+ 1" on the key length since SIZEOF(dbc_gv_key) contains + the first character of the key so the "+ 1" to get the last byte of the key is + already integrated into the length + */ + memcpy(blk_set_prnt_p->ins_rec.ins_key, blk_set_p->prev_blk_key, + SIZEOF(dbc_gv_key) + blk_set_p->prev_blk_key->end); + /* Setup so that creation of the blk_set_new_p block can then set its block id into + our parent block's insert rec buffer which will be made part of the inserted + record at block build time + */ + blk_set_new_p->ins_blk_id_p = &blk_set_prnt_p->ins_rec.blk_id; + blk_set_rhs_p = blk_set_p; /* Use original block for rhs */ + blk_set_rhs_p->usage = gdsblk_update; + } else + { /* Have root block: need to put the RHS into a new block too */ + DBC_DEBUG(("DBC_DEBUG: Splitting root block, extra block to be created\n")); + ++psa->block_depth; /* Need a new block to split into */ + if (MAX_BLOCK_INFO_DEPTH <= psa->block_depth) + GTMASSERT; + blk_set_rhs_p = &psa->blk_set[psa->block_depth]; + /* Key for last record in the LHS block used to (re)construct root block */ + last_rec_key = blk_set_p->curr_blk_key; + dbc_init_blk(psa, blk_set_rhs_p, -1, gdsblk_create, new_rh_blk_len, curr_blk_levl); + /* We will put the pointers to both this block and the RHS we build next + into the original root block -- done later when RHS is complete */ + /* If root, the RHS sub-block is a different type */ + blk_set_rhs_p->blk_type = (gdsblk_gvtroot == blk_set_p->blk_type) + ? gdsblk_gvtindex : gdsblk_dtindex; + } + + /**** Now build RHS into current block ****/ + BLK_INIT(bs_ptr, bs1); + blk_set_rhs_p->upd_addr = bs1; /* Block building roadmap.. */ + /* Build record header for inserted record. Inserted record is always for index + type blocks + */ + BLK_ADDR(ins_rec_hdr, SIZEOF(rec_hdr), rec_hdr); + ins_rec_hdr->rsiz = SIZEOF(rec_hdr) + blk_set_p->ins_rec.ins_key->end + 1 + + SIZEOF(block_id); + ins_rec_hdr->cmpc = 0; + BLK_SEG(bs_ptr, (uchar_ptr_t)ins_rec_hdr, SIZEOF(rec_hdr)); + /* Now for the inserted record key */ + BLK_SEG(bs_ptr, + blk_set_p->ins_rec.ins_key->base, + blk_set_p->ins_rec.ins_key->end + 1); + /* Finally the inserted record value always comes from the block_id field. It is + not filled in now but will be when the block it refers to is created. */ + BLK_SEG(bs_ptr, (uchar_ptr_t)&blk_set_p->ins_rec.blk_id, SIZEOF(block_id)); + /* Record that was first in RH side now needs its cmpc (and length) reset since + it is now the second record in the new block. */ + BLK_ADDR(next_rec_hdr, SIZEOF(rec_hdr), rec_hdr); + next_rec_hdr->rsiz = curr_rec_len - curr_rec_shrink; + next_rec_hdr->cmpc = blk_set_p->curr_match; + BLK_SEG(bs_ptr, (uchar_ptr_t)next_rec_hdr, SIZEOF(rec_hdr)); + remain_offset = curr_rec_shrink + SIZEOF(rec_hdr); /* Where rest of record plus any + further records begin */ + remain_len = curr_blk_len - curr_rec_offset; + BLK_SEG(bs_ptr, + blk_set_p->curr_rec + remain_offset, + remain_len - remain_offset); + if (0 == BLK_FINI(bs_ptr, bs1)) + GTMASSERT; + assert(blk_seg_cnt == new_rh_blk_len); + } /* else method (2) */ + if (got_root) + { /* If we have split a root block, we need to now set the pointers to the new LHS + and RHS blocks into the root block as the only records. Note this requires a + level increase of the tree. Hopefully we will not come across a database that is + already at maximum level. If so, the only way to reduce the level is to run + MUPIP REORG with a fairly recent vintage of GT.M + */ + BLK_INIT(bs_ptr, bs1); + blk_set_p->usage = gdsblk_update; /* It's official .. blk is being modified */ + blk_set_p->upd_addr = bs1; /* Block building roadmap.. */ + blk_set_p->blk_levl++; /* Needs to be at a new level */ + if (MAX_BT_DEPTH <= blk_set_p->blk_levl) + /* Tree is too high */ + GTMASSERT; + /* First record will have last key in LHS block */ + BLK_ADDR(next_rec_hdr, SIZEOF(rec_hdr), rec_hdr); + next_rec_hdr->rsiz = SIZEOF(rec_hdr) + last_rec_key->end + 1 + SIZEOF(block_id);; + next_rec_hdr->cmpc = 0; + BLK_SEG(bs_ptr, (uchar_ptr_t)next_rec_hdr, SIZEOF(rec_hdr)); + BLK_SEG(bs_ptr, last_rec_key->base, (last_rec_key->end + 1)); + BLK_ADDR(lhs_block_id_p, SIZEOF(block_id), block_id); /* First record's value */ + BLK_SEG(bs_ptr, (uchar_ptr_t)lhs_block_id_p, SIZEOF(block_id)); + blk_set_new_p->ins_blk_id_p = lhs_block_id_p; /* Receives block id when created */ + /* Second record is a star key record pointing to the RHS block */ + BLK_ADDR(new_star_hdr, SIZEOF(rec_hdr), rec_hdr); + new_star_hdr->rsiz = BSTAR_REC_SIZE; + new_star_hdr->cmpc = 0; + BLK_SEG(bs_ptr, (uchar_ptr_t)new_star_hdr, SIZEOF(rec_hdr)); + BLK_ADDR(rhs_block_id_p, SIZEOF(block_id), block_id); /* First record's value */ + BLK_SEG(bs_ptr, (uchar_ptr_t)rhs_block_id_p, SIZEOF(block_id)); + blk_set_rhs_p->ins_blk_id_p = rhs_block_id_p; /* Receives block id when created */ + /* Complete update array */ + if (0 == BLK_FINI(bs_ptr, bs1)) + GTMASSERT; + /* The root block is the last one we need to change */ + DBC_DEBUG(("DBC_DEBUG: Stopping block scan as blk_index %d is a root block\n", blk_index)); + completed = TRUE; + break; + } + } /* else need block split */ + if (0 != blk_index) + blk_index--; /* working backwards.. */ + else + blk_index = bottom_tree_index; + } /* while !completed */ + assert(completed); + + /* Check that we have sufficient free blocks to create the blccks we need (if any) */ + created_blocks = psa->block_depth - bottom_tree_index; + if (created_blocks > psa->dbc_cs_data->trans_hist.free_blocks) + { /* We have a slight problem in that this transaction requires more free blocks than are + available. Our recourse is to flush the current file-header preserving any changes we + have already made, close the file and execute a mupip command to perform an extension before + re-opening the db for further processing. + */ + DBC_DEBUG(("DBC_DEBUG: Insufficient free blocks for this transaction - calling MUPIP EXTEND\n")); + dbc_flush_fhead(psa); + dbc_close_db(psa); + extent_size = MAX(psa->dbc_cs_data->extension_size, created_blocks); + /* Now build command file to perform the MUPIP EXTEND for the region */ + dbc_open_command_file(psa); + dbc_write_command_file(psa, MUPIP_START); + strcpy((char_ptr_t)psa->util_cmd_buff, MUPIP_EXTEND); + strcat((char_ptr_t)psa->util_cmd_buff, (char_ptr_t)psa->ofhdr.regname); + strcat((char_ptr_t)psa->util_cmd_buff, " "OPTDELIM"B="); + chr_p = psa->util_cmd_buff + strlen((char_ptr_t)psa->util_cmd_buff); + chr_p = i2asc(chr_p, extent_size); + *chr_p = 0; + dbc_write_command_file(psa, (char_ptr_t)psa->util_cmd_buff); + UNIX_ONLY(dbc_write_command_file(psa, "EOF")); + dbc_close_command_file(psa); + dbc_run_command_file(psa, "MUPIP", (char_ptr_t)psa->util_cmd_buff, FALSE); + /* Seeing as how it is very difficult to (in portable code) get a coherent error code back from + an executed command, we will just assume it worked, open the database back in and see if in + fact it did actually extend sufficiently. If not, this is a perm error and we stop here. + */ + dbc_init_db(psa); + if (created_blocks > psa->dbc_cs_data->trans_hist.free_blocks) + rts_error(VARLSTCNT(4) ERR_DBCNOEXTND, 2, RTS_ERROR_STRING((char_ptr_t)psa->ofhdr.dbfn)); + /* Database is now extended -- safest bet is to restart this particular update so that it is certain + nothing else got in besides the extention. + */ + DBC_DEBUG(("DBC_DEBUG: Restarting processing of this p1rec due to DB extension\n")); + return TRUE; + + } + /* The update arrarys are complete, we know there are sufficient free blocks in the database to accomodate + the splitting we have to do. + */ + bottom_created_index = psa->block_depth; /* From here on out are bit map blocks */ + if (0 != created_blocks) + { /* Run through the created blocks assigning block numbers and filling the numbers into the buffers + that need them. If we didn't create any blocks, we know we didn't split any and there is nothing + to do for this p1 record. + */ + total_blks = psa->dbc_cs_data->trans_hist.total_blks; + local_map_max = DIVIDE_ROUND_UP(total_blks, psa->dbc_cs_data->bplmap); + DBC_DEBUG(("DBC_DEBUG: Assigning block numbers to created DB blocks\n")); + for (blk_index = psa->block_depth, blk_set_p = &psa->blk_set[blk_index]; + bottom_tree_index < blk_index; --blk_index, --blk_set_p) + { + assert(gdsblk_create == blk_set_p->usage); + assert(NULL != blk_set_p->upd_addr); + /* Find and allocate a database block for this created block */ + assert(NULL != blk_set_p->ins_blk_id_p); /* Must be a place to put the block id */ + /* First find local bit map with some room in it */ + lclmap_not_full = bmm_find_free(psa->hint_blk / psa->dbc_cs_data->bplmap, + (sm_uc_ptr_t)psa->dbc_cs_data->master_map, + local_map_max); + if (NO_FREE_SPACE == lclmap_not_full) + { + assert(FALSE); + rts_error(VARLSTCNT(5) ERR_DBCINTEGERR, 2, RTS_ERROR_STRING((char_ptr_t)psa->ofhdr.dbfn), + ERR_BITMAPSBAD); + } + if (ROUND_DOWN2(psa->hint_blk, psa->dbc_cs_data->bplmap) != lclmap_not_full) + psa->hint_lcl = 1; + bitmap_blk_num = lclmap_not_full * psa->dbc_cs_data->bplmap; + /* Read this bitmap in. Note it may already exist in the cache (likely for multiple creates) */ + lbm_blk_index = dbc_read_dbblk(psa, bitmap_blk_num, gdsblk_bitmap); + blk_set_bm_p = &psa->blk_set[lbm_blk_index]; + assert(IS_BML(blk_set_bm_p->old_buff)); /* Verify we have a bit map block */ + assert(ROUND_DOWN2(blk_set_bm_p->blk_num, psa->dbc_cs_data->bplmap) == blk_set_bm_p->blk_num); + if (ROUND_DOWN2(psa->dbc_cs_data->trans_hist.total_blks, psa->dbc_cs_data->bplmap) == bitmap_blk_num) + /* This bitmap is the last one .. compute total blks in partial this bitmap */ + blks_this_lmap = (psa->dbc_cs_data->trans_hist.total_blks - bitmap_blk_num); + else + /* Regular bitmap (not last one) */ + blks_this_lmap = psa->dbc_cs_data->bplmap; + lcl_map_p = blk_set_bm_p->old_buff + SIZEOF(v15_blk_hdr); + lcl_blk = psa->hint_lcl = bm_find_blk(psa->hint_lcl, lcl_map_p, blks_this_lmap, &dummy_bool); + if (NO_FREE_SPACE == lcl_blk) + { + assert(FALSE); + rts_error(VARLSTCNT(5) ERR_DBCINTEGERR, 2, RTS_ERROR_STRING((char_ptr_t)psa->ofhdr.dbfn), + ERR_BITMAPSBAD); + } + /* Found a free block, mark it busy. Note that bitmap blocks are treated somewhat differently + than other blocks. We do not create an update array for them but just change the copy in + old_buff as appropriate. + */ + bml_busy(lcl_blk, lcl_map_p); + blk_set_bm_p->usage = gdsblk_update; + /* See if entire block is full - if yes, we need to mark master map too */ + psa->hint_lcl = bml_find_free(psa->hint_lcl, lcl_map_p, blks_this_lmap); + if (NO_FREE_SPACE == psa->hint_lcl) + { /* Local map was filled .. clear appropriate master map bit */ + DBC_DEBUG(("DBC_DEBUG: -- Local map now full - marking master map\n")); + bit_clear(bitmap_blk_num / psa->dbc_cs_data->bplmap, psa->dbc_cs_data->master_map); + } + assert(lcl_blk); /* Shouldn't be zero as that is for the lcl bitmap itself */ + allocated_blk_num = psa->hint_blk = bitmap_blk_num + lcl_blk; + DBC_DEBUG(("DBC_DEBUG: -- The newly allocated block for block index %d is 0x%x\n", \ + blk_index, allocated_blk_num)); + /* Fill this block number in the places it needs to be filled */ + assert(-1 == blk_set_p->blk_num); + blk_set_p->blk_num = allocated_blk_num; + *blk_set_p->ins_blk_id_p = allocated_blk_num; + psa->dbc_cs_data->trans_hist.free_blocks--; /* There is one fewer free blocks tonite */ + assert(0 <= (int)psa->dbc_cs_data->trans_hist.free_blocks); + psa->dbc_fhdr_dirty = TRUE; + } + /* Now that all the block insertions have been filled in, run the entire chain looking for + both created and updated blocks. Build the new versions of their blocks in new_buff. + */ + DBC_DEBUG(("DBC_DEBUG: Create new and changed blocks via their update arrays\n")); + for (blk_index = psa->block_depth, blk_set_p = &psa->blk_set[blk_index]; 0 <= blk_index; --blk_index, --blk_set_p) + { /* Run through the update array for this block */ + blk_sega_p = (blk_segment *)blk_set_p->upd_addr; + if (gdsblk_bitmap == blk_set_p->blk_type || gdsblk_read == blk_set_p->usage) + { /* Bitmap blocks are updated in place and of course read blocks have no updates */ + DBC_DEBUG(("DBC_DEBUG: -- Block index %d bypassed for type (%d) or usage (%d)\n", \ + blk_index, blk_set_p->blk_type, blk_set_p->usage)); + assert(NULL == blk_sega_p); + continue; + } + DBC_DEBUG(("DBC_DEBUG: -- Block index %d being (re)built\n", blk_index)); + assert(NULL != blk_sega_p); + new_blk_len = INTCAST(blk_sega_p->len); + new_blk_p = blk_set_p->new_buff; + ((v15_blk_hdr_ptr_t)new_blk_p)->bsiz = blk_set_p->blk_len = new_blk_len; + ((v15_blk_hdr_ptr_t)new_blk_p)->levl = blk_set_p->blk_levl; + /* VMS has an unalighed tn. All UNIX variants have an aligned TN */ + VMS_ONLY(PUT_ULONG(&((v15_blk_hdr_ptr_t)new_blk_p)->tn, psa->dbc_cs_data->trans_hist.curr_tn)); + UNIX_ONLY(((v15_blk_hdr_ptr_t)new_blk_p)->tn = psa->dbc_cs_data->trans_hist.curr_tn); + new_blk_p += SIZEOF(v15_blk_hdr); + for (blk_array_top = (blk_segment *)blk_sega_p->addr, blk_sega_p++; + blk_sega_p <= blk_array_top; blk_sega_p++) + { /* Start with first subtantive array entry ([1]) and do the segment move thing */ + memcpy(new_blk_p, blk_sega_p->addr, blk_sega_p->len); + new_blk_p += blk_sega_p->len; + } + assert((new_blk_p - blk_set_p->new_buff) == new_blk_len); + } + /* One last pass through the block list to do the physical IO on the database */ + psa->fc->op = FC_WRITE; + psa->fc->op_len = blk_size; /* Just write the full block out regardless */ + DBC_DEBUG(("DBC_DEBUG: Flush all modified blocks out to disk for this transaction\n")); + psa->dbc_critical = TRUE; + for (blk_index = psa->block_depth, blk_set_p = &psa->blk_set[blk_index]; 0 <= blk_index; --blk_index, --blk_set_p) + { /* Output all modified/created blocks */ + if (gdsblk_create != blk_set_p->usage) + { /* We read everything but created blocks and some of them were found in cache */ + psa->blks_read++; + if (blk_set_p->found_in_cache) + psa->blks_cached++; + } + if (gdsblk_read == blk_set_p->usage) + { + DBC_DEBUG(("DBC_DEBUG: -- Block index %d bypassed for usage (read)\n", blk_index)); + continue; /* Nothing to do for read-only block */ + } + if (gdsblk_bitmap == blk_set_p->blk_type) + { /* Bitmap blocks are built in old_buff, swap with new_buff. This also lets the + buffer be reused correctly (by dbc_read_dbblk) if we read this block into + the same place later. + */ + blk_p = blk_set_p->new_buff; + blk_set_p->new_buff = blk_set_p->old_buff; + blk_set_p->old_buff = blk_p; + } + DBC_DEBUG(("DBC_DEBUG: -- Block index %d being written as block 0x%x\n", blk_index, \ + blk_set_p->blk_num)); + psa->fc->op_buff = blk_set_p->new_buff; + psa->fc->op_pos = psa->dbc_cs_data->start_vbn + (blk_size / DISK_BLOCK_SIZE) * blk_set_p->blk_num; + dbcertify_dbfilop(psa); + if (gdsblk_create == blk_set_p->usage) + psa->blks_created++; + else + psa->blks_updated++; + } + psa->dbc_critical = FALSE; + if (forced_exit) + { /* Our exit was deferred until we cleared the critical section area */ + UNIX_ONLY(dbcertify_deferred_signal_handler()); + VMS_ONLY(sys$exit(exi_condition)); + } + + /* Update the transaction number in the fileheader for the next transaction */ + psa->dbc_cs_data->trans_hist.curr_tn++; + psa->dbc_fhdr_dirty = TRUE; + } else + GTMASSERT; /* If we got this far we should have split a block which would create a block */ + DBC_DEBUG(("DBC_DEBUG: Block processing completed\n")); + psa->blks_processed++; + + return FALSE; /* No transaction restart necessary */ +} + +/* Flush the file-header back out to the database */ +void dbc_flush_fhead(phase_static_area *psa) +{ + if (!psa->dbc_fhdr_dirty) + return; /* Nothing to do if it wasn't dirtied */ + psa->fc->op = FC_WRITE; + psa->fc->op_buff = (uchar_ptr_t)psa->dbc_cs_data; + psa->fc->op_pos = 1; + psa->fc->op_len = SIZEOF(v15_sgmnt_data); + dbcertify_dbfilop(psa); + psa->dbc_fhdr_dirty = FALSE; + return; +} + +/* Read the next output record into the buffer provided */ +void dbc_read_p1out(phase_static_area *psa, void *obuf, int olen) +{ + int rc, save_errno; + char_ptr_t errmsg; + + DOREADRC(psa->outfd, obuf, olen, rc); + if (-1 == rc) + { + save_errno = errno; + errmsg = STRERROR(save_errno); + assert(FALSE); + rts_error(VARLSTCNT(11) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("read()"), CALLFROM, + ERR_TEXT, 2, RTS_ERROR_STRING(errmsg)); + } +} + +/* Exit/cleanup routine */ +void dbc_certify_phase_cleanup(void) +{ + phase_static_area *psa; + + psa = psa_gbl; + if (psa->dbc_gv_cur_region && psa->dbc_gv_cur_region->dyn.addr && psa->dbc_gv_cur_region->dyn.addr->file_cntl) + { + dbc_flush_fhead(psa); + dbc_close_db(psa); + if (psa->dbc_critical) + rts_error(VARLSTCNT(4) ERR_TEXT, + 2, RTS_ERROR_LITERAL("Failure while in critical section -- database damage likely")); + } + UNIX_ONLY(dbc_release_standalone_access(psa)); + if (psa_gbl->tmp_file_names_gend) + { /* Only close/delete if we know what they are */ + if (psa->tcfp) + dbc_close_command_file(psa); + if (!psa->keep_temp_files) + dbc_remove_command_file(psa); + if (psa->trfp) + dbc_close_result_file(psa); + if (!psa->keep_temp_files) + dbc_remove_result_file(psa); + } +} diff --git a/sr_port/dbcertify_funcs.c b/sr_port/dbcertify_funcs.c new file mode 100644 index 0000000..df73496 --- /dev/null +++ b/sr_port/dbcertify_funcs.c @@ -0,0 +1,995 @@ +/**************************************************************** + * * + * Copyright 2005, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#define _POSIX_EXIT /* Needed for VMS system() call */ +#include "mdef.h" + +#ifdef VMS +#include +#include +#include +#endif + +#include +#include "sys/wait.h" +#include "gtm_stat.h" +#include "gtm_ctype.h" +#include "gtm_stdio.h" +#include "gtm_string.h" +#include "gtm_unistd.h" +#include "gtm_stdlib.h" +#include "gtm_fcntl.h" + +#include "gtmio.h" +#include "copy.h" +#include "iosp.h" +#include "gdsroot.h" +#include "v15_gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "v15_gdsbt.h" +#include "gdsfhead.h" +#include "v15_gdsfhead.h" +#include "gdsblk.h" +#include "patcode.h" +#include "gdsblkops.h" +#include "filestruct.h" +#include "v15_filestruct.h" +#include "gtmmsg.h" +#include "eintr_wrappers.h" +#include "min_max.h" +#include "error.h" +#include "jnl.h" +#include "trans_log_name.h" +#include "dbcertify.h" + +#define FILETAB "File " +#define REGIONTAB "Region " + +error_def(ERR_DEVOPENFAIL); +error_def(ERR_SYSCALL); +error_def(ERR_DBRDONLY); +error_def(ERR_DBOPNERR); +error_def(ERR_DBCCMDFAIL); +error_def(ERR_DBCINTEGERR); +error_def(ERR_PREMATEOF); +error_def(ERR_TEXT); +error_def(ERR_TRNLOGFAIL); +error_def(ERR_NOREGION); +error_def(ERR_DBNOTGDS); +error_def(ERR_BADDBVER); +error_def(ERR_DBMINRESBYTES); +error_def(ERR_DBMAXREC2BIG); +error_def(ERR_DBCKILLIP); +error_def(ERR_DBCNOTSAMEDB); +error_def(ERR_LOGTOOLONG); +error_def(ERR_GTMDISTUNDEF); + +/* Open the temporary file that hold the command(s) we are going to execute */ +void dbc_open_command_file(phase_static_area *psa) +{ + char_ptr_t errmsg; + int rc, save_errno; + int4 status; + char *dist_ptr; + mstr gtm_dist_m, gtm_dist_path; + char gtm_dist_path_buff[MAX_FN_LEN + 1]; + + assert(NULL != psa && NULL == psa->tcfp); + if (!psa->tmp_file_names_gend) + dbc_gen_temp_file_names(psa); + gtm_dist_m.addr = UNIX_ONLY("$")GTM_DIST; + gtm_dist_m.len = SIZEOF(UNIX_ONLY("$")GTM_DIST) - 1; + status = TRANS_LOG_NAME(>m_dist_m, >m_dist_path, gtm_dist_path_buff, SIZEOF(gtm_dist_path_buff), + dont_sendmsg_on_log2long); +#ifdef UNIX + if (SS_LOG2LONG == status) + rts_error(VARLSTCNT(5) ERR_LOGTOOLONG, 3, gtm_dist_m.len, gtm_dist_m.addr, SIZEOF(gtm_dist_path_buff) - 1); + else +#endif + if (SS_NORMAL != status) + rts_error(VARLSTCNT(1) ERR_GTMDISTUNDEF); + assert(0 < gtm_dist_path.len); + VMS_ONLY(dbc_remove_command_file(psa)); /* If we don't do this, the command files versions pile up fast */ + psa->tcfp = Fopen((char_ptr_t)psa->tmpcmdfile, "w"); + if (NULL == psa->tcfp) + { + save_errno = errno; + errmsg = STRERROR(save_errno); + rts_error(VARLSTCNT(8) ERR_DEVOPENFAIL, 2, psa->tmpcmdfile_len, psa->tmpcmdfile, + ERR_TEXT, 2, RTS_ERROR_TEXT(errmsg)); + } + UNIX_ONLY(dbc_write_command_file(psa, SHELL_START)); + MEMCPY_LIT(psa->util_cmd_buff, SETDISTLOGENV); + memcpy(psa->util_cmd_buff + SIZEOF(SETDISTLOGENV) - 1, gtm_dist_path.addr, gtm_dist_path.len); + psa->util_cmd_buff[SIZEOF(SETDISTLOGENV) - 1 + gtm_dist_path.len] = 0; /* Null temrinator */ + dbc_write_command_file(psa, (char_ptr_t)psa->util_cmd_buff); +} + +/* Write a record to temporary command file */ +void dbc_write_command_file(phase_static_area *psa, char_ptr_t cmd) +{ + char_ptr_t errmsg; + int rc, save_errno; + + assert(NULL != psa && NULL != psa->tcfp); + assert(psa->tmp_file_names_gend); + rc = FPRINTF(psa->tcfp, "%s\n", cmd); + if (-1 == rc) + { + save_errno = errno; + errmsg = STRERROR(save_errno); + rts_error(VARLSTCNT(11) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fprintf()"), CALLFROM, + ERR_TEXT, 2, RTS_ERROR_TEXT(errmsg)); + } + rc = CHMOD((char_ptr_t)psa->tmpcmdfile, S_IRUSR + S_IWUSR + S_IXUSR + S_IRGRP + S_IROTH); /* Change to 744 */ + if (-1 == rc) + { + save_errno = errno; + errmsg = STRERROR(save_errno); + rts_error(VARLSTCNT(15) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("chmod()"), CALLFROM, + ERR_TEXT, 2, psa->tmpcmdfile_len, psa->tmpcmdfile, + ERR_TEXT, 2, RTS_ERROR_TEXT(errmsg)); + } +} + +/* Close the temporary command file */ +void dbc_close_command_file(phase_static_area *psa) +{ + assert(NULL != psa && NULL != psa->tcfp); + assert(psa->tmp_file_names_gend); + fclose(psa->tcfp); + psa->tcfp = NULL; +} + +/* Execute the temporary command file */ +void dbc_run_command_file(phase_static_area *psa, char_ptr_t cmdname, char_ptr_t cmdargs, boolean_t piped_result) +{ + int rc, cmd_len; + unsigned char cmdbuf1[MAX_FN_LEN + 256], cmdbuf2[MAX_FN_LEN + 1], *cp; + + assert(NULL != psa && NULL == psa->tcfp); + assert(psa->tmp_file_names_gend); + MEMCPY_LIT(cmdbuf1, RUN_CMD); + cp = cmdbuf1 + SIZEOF(RUN_CMD) - 1; + memcpy(cp, psa->tmpcmdfile, psa->tmpcmdfile_len); + cp += psa->tmpcmdfile_len; + cmd_len = (int)(cp - cmdbuf1); + *cp = '\0'; + + rc = dbc_syscmd((char_ptr_t)cmdbuf1); + if (0 != rc) + { /* If piped_result, they can't see what went wrong (error messages likely in result file */ + if (piped_result) + { /* Output result file so they can see what happened -- this may or may not work but it is + the best we can do at this point */ + MEMCPY_LIT(cmdbuf2, DUMPRSLTFILE); + cp = cmdbuf2 + SIZEOF(DUMPRSLTFILE) - 1; + memcpy(cp, psa->tmprsltfile, psa->tmprsltfile_len); + cp += psa->tmprsltfile_len; + *cp = '\0'; + dbc_syscmd((char_ptr_t)cmdbuf2); + } + rts_error(VARLSTCNT(13) ERR_DBCCMDFAIL, 7, rc, cmd_len, cmdbuf1, RTS_ERROR_TEXT(cmdname), + RTS_ERROR_TEXT(cmdargs), ERR_TEXT, 2, + RTS_ERROR_LITERAL("Note that the "UNIX_ONLY("environment variable $")VMS_ONLY("logical ")GTM_DIST + " must point to the current GT.M V4 installation")); + } +} + +/* Remove/dalete command file - normally only needed at cleanup since open with "W" will delete any existing file. */ +void dbc_remove_command_file(phase_static_area *psa) +{ + char_ptr_t errmsg; + int rc, save_errno; + + assert(NULL != psa && NULL == psa->tcfp); /* Must be closed */ + if (!psa->tmp_file_names_gend) + dbc_gen_temp_file_names(psa); + rc = remove((char_ptr_t)psa->tmpcmdfile); + if (-1 == rc && ENOENT != errno) + { + save_errno = errno; + errmsg = STRERROR(save_errno); + rts_error(VARLSTCNT(15) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("remove()"), CALLFROM, + ERR_TEXT, 2, psa->tmpcmdfile_len, psa->tmpcmdfile, + ERR_TEXT, 2, RTS_ERROR_TEXT(errmsg)); + } +} + +/* Open result file */ +void dbc_open_result_file(phase_static_area *psa) +{ + char_ptr_t errmsg; + int rc, save_errno; + + assert(NULL != psa && NULL == psa->trfp); + assert(psa->tmp_file_names_gend); + psa->trfp = Fopen((char_ptr_t)psa->tmprsltfile, "r"); + if (0 == psa->trfp) + { + save_errno = errno; + errmsg = STRERROR(save_errno); + rts_error(VARLSTCNT(8) ERR_DEVOPENFAIL, 2, psa->tmprsltfile_len, psa->tmprsltfile, + ERR_TEXT, 2, RTS_ERROR_TEXT(errmsg)); + } +} + +/* Read a record from temporary result file */ +uchar_ptr_t dbc_read_result_file(phase_static_area *psa, int rderrmsg, uchar_ptr_t arg) +{ + int save_errno; + char_ptr_t errmsg; + char_ptr_t fgs; + char emsg[MAX_FN_LEN + 256]; + + assert(NULL != psa && NULL != psa->trfp); + FGETS((char_ptr_t)psa->rslt_buff, MAX_ZWR_KEY_SZ, psa->trfp, fgs); + if (NULL == fgs) + { + if (!feof(psa->trfp)) + { /* Non-EOF message */ + save_errno = errno; + errmsg = STRERROR(save_errno); + rts_error(VARLSTCNT(11) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fgets()"), CALLFROM, + ERR_TEXT, 2, RTS_ERROR_TEXT(errmsg)); + } else + { /* We have EOF */ + if (0 != rderrmsg) + rts_error(VARLSTCNT(4) rderrmsg, 2, RTS_ERROR_TEXT((char_ptr_t)arg)); + else + { + strcpy(emsg, "Temporary results file ("); + strcat(emsg, (char_ptr_t)psa->tmprsltfile); + strcat(emsg, " had unexpected values"); + rts_error(VARLSTCNT(6) ERR_PREMATEOF, 0, ERR_TEXT, 2, + RTS_ERROR_TEXT(emsg)); + } + } + exit(1); /* We shouldn't come here but in case... */ + } + return (uchar_ptr_t)fgs; +} + +/* Close the temporary command file */ +void dbc_close_result_file(phase_static_area *psa) +{ + assert(NULL != psa && NULL != psa->trfp); + fclose(psa->trfp); + psa->trfp = NULL; +} + +/* Remove/dalete result file */ +void dbc_remove_result_file(phase_static_area *psa) +{ + int rc, save_errno; + char_ptr_t errmsg; + + assert(NULL != psa && NULL == psa->trfp); /* Must be closed */ + if (!psa->tmp_file_names_gend) + dbc_gen_temp_file_names(psa); + rc = remove((char_ptr_t)psa->tmprsltfile); + if (-1 == rc && ENOENT != errno) + { + save_errno = errno; + errmsg = STRERROR(save_errno); + rts_error(VARLSTCNT(15) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("remove()"), CALLFROM, + ERR_TEXT, 2, psa->tmprsltfile_len, psa->tmprsltfile, + ERR_TEXT, 2, RTS_ERROR_TEXT(errmsg)); + } +} + +/* Create the temporary file names we need. This is a hardcoded prefix following by the region name + of the file we are processing. This should create a unique temporary command script and command + result file for a given invocation and allows multiple copies of DBCERTIFY to run against databases + in the same directory. +*/ +void dbc_gen_temp_file_names(phase_static_area *psa) +{ + unsigned char *cp, *regname_p; + int len, dir_len; + + assert(NULL != psa && !psa->tmp_file_names_gend); + assert(0 == psa->tmprsltfile[0]); + assert(0 == psa->tmpcmdfile[0]); + /* See if we have region name information yet. Where this region name information is + kept depends on the phase we are in. Scan phase it is in psa->regname (is an argument + to the phase). In certify phase the region name is in the scan phase outfile file header. + */ + if (psa->phase_one) + /* Scan phase, region name in regname */ + regname_p = psa->regname; + else + regname_p = (uchar_ptr_t)psa->ofhdr.regname; + assert(0 != *regname_p); /* We should have a regname of substance */ + + /* Temp command file name: TMPFILEPFX_.TMPFILESFX. Note that + tempfiledir has no default if not specified therefore defaults to the current directory. + */ + cp = psa->tmpcmdfile; + if (0 != *psa->tmpfiledir) + { /* A temporary file directory was specified .. use it */ + len = STRLEN((char_ptr_t)psa->tmpfiledir); + memcpy(cp, psa->tmpfiledir, len); + cp += len; +#ifdef VMS + if (']' != *(cp - 1) && '>' != *(cp - 1) && ':' != *(cp - 1)) + *cp++ = ':'; +#else + if ('/' != *(cp - 1)) + *cp++ = '/'; +#endif + dir_len = (int)(cp - psa->tmpcmdfile); + } else + dir_len = 0; + MEMCPY_LIT(cp, TMPFILEPFX); + cp = psa->tmpcmdfile + SIZEOF(TMPFILEPFX) - 1; + *cp++ = '_'; + len = STRLEN((char_ptr_t)regname_p); + memcpy(cp, regname_p, len); + cp += len; + MEMCPY_LIT(cp, TMPCMDFILSFX); + cp += SIZEOF(TMPCMDFILSFX) - 1; + psa->tmpcmdfile_len = (int)(cp - psa->tmpcmdfile); + *cp = '\0'; /* Null terminate */ + + /* Now same thing for temporary results file */ + cp = psa->tmprsltfile; + if (0 != dir_len) + { /* Dir will be same as for command file so use that.. */ + memcpy(cp, psa->tmpcmdfile, dir_len); + cp += dir_len; + } + MEMCPY_LIT(cp, TMPFILEPFX); + cp = psa->tmprsltfile + SIZEOF(TMPFILEPFX) - 1; + *cp++ = '_'; + len = STRLEN((char_ptr_t)regname_p); + memcpy(cp, regname_p, len); + cp += len; + MEMCPY_LIT(cp, TMPRSLTFILSFX); + cp += SIZEOF(TMPRSLTFILSFX) - 1; + psa->tmprsltfile_len = (int)(cp - psa->tmprsltfile); + *cp = '\0'; /* Null terminate */ + + + psa->tmp_file_names_gend = TRUE; +} + +/* Execute the given system command. Note even in VMS we are getting a POSIX style return code, not the + VMS style return code due to the _POSIX_EXIT macro defined at the top of this module. See the C library + reference manual for details on VMS. + */ +int dbc_syscmd(char_ptr_t cmdparm) +{ + int rc; +#ifdef _BSD + union wait wait_stat; +#else + int4 wait_stat; +#endif + +#ifdef VMS + /* Verify system() is supported */ + if (0 == SYSTEM(NULL)) + GTMASSERT; +#endif + rc = SYSTEM(cmdparm); + if (-1 == rc) + rc = errno; + else + { +#ifdef _BSD + wait_stat.w_status = rc; +#else + wait_stat = rc; +#endif + rc = WEXITSTATUS(wait_stat); + } + return rc; +} + +/* Find the region name associated with the given database. + + Parse the output from "DSE /REG" to determine. +*/ +void dbc_find_database_filename(phase_static_area *psa, uchar_ptr_t regname, uchar_ptr_t dbfn) +{ + int len; + uchar_ptr_t rptr; + + dbc_open_command_file(psa); +#ifdef VMS + strcpy((char_ptr_t)psa->util_cmd_buff, RESULT_ASGN); + strcat((char_ptr_t)psa->util_cmd_buff, (char_ptr_t)psa->tmprsltfile); + dbc_write_command_file(psa, (char_ptr_t)psa->util_cmd_buff); + dbc_write_command_file(psa, DSE_START); +#else + strcpy((char_ptr_t)psa->util_cmd_buff, DSE_START_PIPE_RSLT1); + strcat((char_ptr_t)psa->util_cmd_buff, (char_ptr_t)psa->tmprsltfile); + strcat((char_ptr_t)psa->util_cmd_buff, DSE_START_PIPE_RSLT2); + dbc_write_command_file(psa, (char_ptr_t)psa->util_cmd_buff); +#endif + dbc_write_command_file(psa, DSE_FIND_REG_ALL); + dbc_write_command_file(psa, DSE_QUIT); + UNIX_ONLY(dbc_write_command_file(psa, "EOF")); + dbc_close_command_file(psa); + dbc_remove_result_file(psa); + dbc_run_command_file(psa, "DSE", DSE_FIND_REG_ALL, TRUE); + + dbc_open_result_file(psa); + /* There should be 7 lines of uninteresting stuff before we get to the file/region pairs but since other errors may + pop up at the head of this list (notorious example -- waiting for ftok semaphore from DSE) we will just eat lines + until we get to the first DSE prompt at which point our command should be outputing. If we run out of lines (hit + EOF), the read routine will return an appropriate error. + */ + do + { + rptr = dbc_read_result_file(psa, 0, NULL); + } while (0 != MEMCMP_LIT(rptr, DSE_REG_LIST_START)); + /* And two more lines after that are also bogus */ + dbc_read_result_file(psa, 0, NULL); + dbc_read_result_file(psa, 0, NULL); + + /* Now we get to the triplets of Filename, Regionname, and a blank line for each region in GLD */ + while(1) + { + rptr = dbc_read_result_file(psa, ERR_NOREGION, regname); /* Should have filename */ + if (0 != MEMCMP_LIT(rptr, FILETAB)) + GTMASSERT; + len = STRLEN(((char_ptr_t)rptr + SIZEOF(FILETAB) - 1)) - 1; + assert(MAX_FN_LEN >= len); + assert(len); + memcpy(dbfn, (rptr + SIZEOF(FILETAB) - 1), len); + dbfn[len] = 0; + rptr = dbc_read_result_file(psa, ERR_NOREGION, regname); + if (0 != MEMCMP_LIT(rptr, REGIONTAB)) + GTMASSERT; + len = STRLEN((char_ptr_t)rptr + SIZEOF(REGIONTAB) - 1) - 1; + assert(len); + *(rptr + SIZEOF(REGIONTAB) - 1 + len) = 0; /* Get rid of trailing \n */ + if (0 == strcmp(((char_ptr_t)rptr + SIZEOF(REGIONTAB) - 1), (char_ptr_t)regname)) + break; /* Found match */ + rptr = dbc_read_result_file(psa, ERR_NOREGION, regname); /* Ingored blank line */ + len = STRLEN((char_ptr_t)rptr); + if ('\n' == rptr[len - 1]) --len; /* Note last record of output file does not have '\n' so test + before adjusting 'len' */ + if ('\r' == rptr[len - 1]) --len; /* Same for carriage return (mostly for VMS) */ + if (0 != len && ((SIZEOF("DSE> ") - 1) != len || 0 != memcmp(rptr, "DSE> ", len))) + GTMASSERT; + } + dbc_close_result_file(psa); + return; +} + +/* Read a given database block into the next block_set (or return existing one) */ +int dbc_read_dbblk(phase_static_area *psa, int blk_num, enum gdsblk_type blk_type) +{ + uchar_ptr_t tucp, src_blk_p; + int blk_index; + block_info *blk_set_p, *blk_set_new_p; + + assert(0 <= blk_num); + if (NULL == psa->blk_set) + { /* Need a few of these.. */ + psa->blk_set = malloc(SIZEOF(block_info) * MAX_BLOCK_INFO_DEPTH); + memset(psa->blk_set, 0, SIZEOF(block_info) * MAX_BLOCK_INFO_DEPTH); + assert(-1 == psa->block_depth); + } + + DBC_DEBUG(("DBC_DEBUG: Requesting database block 0x%x (type = %d)\n", blk_num, blk_type)); + /* Scan the blocks we have thus far to make sure this block is not amongst them. + Block duplication is possible in two instances: (1) We have reached the target + block or (2) this is a bitmap block we are reading in to modify. These conditions + are also "asserted". + */ + assert(blk_num < psa->dbc_cs_data->trans_hist.total_blks); + for (blk_index = 0, blk_set_p = &psa->blk_set[0]; blk_index <= psa->block_depth; ++blk_index, ++blk_set_p) + { + if (blk_set_p->blk_num == blk_num) + { /* This block already exists in our cache */ + assert(0xff == ((v15_blk_hdr_ptr_t)blk_set_p->old_buff)->levl || 0 == blk_index); + if (gdsblk_gvtroot == blk_type) + { + assert(gdsblk_gvtindex == blk_set_p->blk_type); + /* Override preexisting type to say this is the GVT root block which we + did not know before when we read it in. + */ + blk_set_p->blk_type = gdsblk_gvtroot; + } + DBC_DEBUG(("DBC_DEBUG: Found block in active cache - blk_index %d\n", blk_index)); + blk_set_p->found_in_cache = TRUE; + return blk_index; + } + } + + ++psa->block_depth; /* Going to another level of block usage */ + if (MAX_BLOCK_INFO_DEPTH <= psa->block_depth) + GTMASSERT; + blk_set_new_p = &psa->blk_set[psa->block_depth]; + + /* See if this block already occupies this or another slot in the "inactive cache" which is any block + that was read into a slot >= block_depth but <= block_depth_hwm (our high water mark). + */ + for (blk_index = psa->block_depth, blk_set_p = &psa->blk_set[psa->block_depth]; + blk_index <= psa->block_depth_hwm; + blk_index++, blk_set_p++) + { /* Note the blk_set_p->blk_num != 0 represents a minor inefficiency in that it forces us to always + reload block 0 the first time it is used in a transaction but does not cause any correctness + issues. After this initial load, it will be found on subsequent searches by the loop above. The + check against 0 prevents us from picking up a block that was not in use before. An alternative + method to this check would be to initialize the block numbers of the entire cache to some value + (preferrably other than -1 as that value has another meaning [created block]). This is something + that could be done in the future if this processing turns out to be burdensome which we fully + do not expect to be the case with v5cbsu.m handling the bulk of the ocnversion workload. SE 5/2005. + */ + if (blk_num == blk_set_p->blk_num && 0 != blk_set_p->blk_num) + { /* Block already exists in this slot so we can avoid I/O. If this is the slot we were going + to put the new block in (blk_index == block_depth) then everything is in the right place + and we only need minor resets. Else, copy information from found block to current block + as necessary + */ + if (blk_index > psa->block_depth) + { /* Block exists in an older slot. Copy info to new slot */ + dbc_init_blk(psa, blk_set_new_p, blk_set_p->blk_num, gdsblk_read, blk_set_p->blk_len, + blk_set_p->blk_levl); + blk_set_new_p->blk_type = blk_set_p->blk_type; + memcpy(blk_set_new_p->old_buff, + ((gdsblk_read == blk_set_p->usage) ? blk_set_p->old_buff : blk_set_p->new_buff), + psa->dbc_cs_data->blk_size); + blk_set_p->blk_num = -1; /* Effectively invalidate this (now) older cache entry */ + DBC_DEBUG(("DBC_DEBUG: Found block in inactive cache differemt slot (%d) for blk_index %d\n", \ + blk_index, psa->block_depth)); + } else + { + assert(blk_index == psa->block_depth); + switch(blk_set_new_p->usage) + { + case gdsblk_create: + case gdsblk_update: + /* Both of these have the current value for the buffer in new_buff. + Swap new_buff and old_buff to get things into proper order */ + assert(blk_set_p->old_buff); + assert(blk_set_p->new_buff); + tucp = blk_set_p->old_buff; + blk_set_p->old_buff = blk_set_p->new_buff; + blk_set_p->new_buff = tucp; + /* Fall into code for block that was only read (it is all setup already) */ + case gdsblk_read: + /* If block was not disturbed, the buffer pointers are already in the + correct configuration */ + dbc_init_blk(psa, blk_set_p, blk_set_p->blk_num, gdsblk_read, blk_set_p->blk_len, + blk_set_p->blk_levl); + DBC_DEBUG(("DBC_DEBUG: Found block in inactive cache same slot for blk_index" \ + " %d\n", psa->block_depth)); + break; + default: + GTMASSERT; + } + } + VMS_ONLY(GET_ULONG(blk_set_new_p->tn, &((v15_blk_hdr_ptr_t)blk_set_new_p->old_buff)->tn)); + UNIX_ONLY(blk_set_new_p->tn = ((v15_blk_hdr_ptr_t)blk_set_new_p->old_buff)->tn); + blk_set_new_p->found_in_cache = TRUE; + return psa->block_depth; + } + } + + /* Initialize block we are about to use. Some values not yet known until after read so this serves mainly + to make sure all the structures (such as read buffer) are allocated. + */ + dbc_init_blk(psa, blk_set_new_p, blk_num, gdsblk_read, 0, 0); + /* Now read the block */ + DBC_DEBUG(("DBC_DEBUG: Reading in database block 0x%x into blk_index %d\n", blk_num, psa->block_depth)); + psa->fc->op = FC_READ; + psa->fc->op_buff = (sm_uc_ptr_t)blk_set_new_p->old_buff; + psa->fc->op_pos = psa->dbc_cs_data->start_vbn + (psa->dbc_cs_data->blk_size / DISK_BLOCK_SIZE) * blk_num; + psa->fc->op_len = psa->dbc_cs_data->blk_size; /* In case length field was modified during a file-extension */ + dbcertify_dbfilop(psa); /* Read data/index block (no return if error) */ + /* Now that we know some value, call initialize again to set the values the way we want */ + dbc_init_blk(psa, blk_set_new_p, blk_num, gdsblk_read, ((v15_blk_hdr_ptr_t)blk_set_new_p->old_buff)->bsiz, + ((v15_blk_hdr_ptr_t)blk_set_new_p->old_buff)->levl); + VMS_ONLY(GET_ULONG(blk_set_new_p->tn, &((v15_blk_hdr_ptr_t)blk_set_new_p->old_buff)->tn)); + UNIX_ONLY(blk_set_new_p->tn = ((v15_blk_hdr_ptr_t)blk_set_new_p->old_buff)->tn); + /* recalculate block type if necessary */ + switch(blk_type) + { + case gdsblk_gvtroot: + case gdsblk_gvtindex: + case gdsblk_gvtleaf: + case gdsblk_dtroot: + case gdsblk_dtindex: + case gdsblk_dtleaf: + case gdsblk_bitmap: + blk_set_new_p->blk_type = blk_type; + break; + case gdsblk_gvtgeneric: + if (0 == ((v15_blk_hdr_ptr_t)blk_set_new_p->old_buff)->levl) + blk_set_new_p->blk_type = gdsblk_gvtleaf; + else + blk_set_new_p->blk_type = gdsblk_gvtindex; + break; + case gdsblk_dtgeneric: + if (0 == ((v15_blk_hdr_ptr_t)blk_set_new_p->old_buff)->levl) + blk_set_new_p->blk_type = gdsblk_dtleaf; + else + blk_set_new_p->blk_type = gdsblk_dtindex; + break; + default: + GTMASSERT; + } + return psa->block_depth; +} + +/* Find the end of a key using the current value of the key as a base */ +void dbc_find_key(phase_static_area *psa, dbc_gv_key *key, uchar_ptr_t rec_p, int blk_levl) +{ + int cmpc, rec_len; + unsigned short us_rec_len; + uchar_ptr_t key_targ_p, key_src_p; + + cmpc = ((rec_hdr_ptr_t)rec_p)->cmpc; + GET_USHORT(us_rec_len, &((rec_hdr_ptr_t)rec_p)->rsiz); + rec_len = us_rec_len; + if (BSTAR_REC_SIZE == rec_len && 0 < blk_levl) + { /* This is a star key record .. there is no key */ + key->end = 0; + DBC_DEBUG(("DBC_DEBUG: Found star key record in dbc_find_key\n")); + return; + } + /* Loop till we find key termination */ + key_targ_p = key->base + cmpc; + key_src_p = rec_p + SIZEOF(rec_hdr); + while (1) + { + for (; *key_src_p; ++key_targ_p, ++key_src_p) + *key_targ_p = *key_src_p; + if (0 == *(key_src_p + 1)) + { /* Just found the end of the key */ + *key_targ_p++ = 0; /* Create key ending null */ + *key_targ_p = 0; /* Install 2nd 0x00 as part of key but not part of length */ + key->end = (uint4)(key_targ_p - key->base); + break; + } + /* Else, copy subscript separator char and keep scanning */ + *key_targ_p++ = *key_src_p++; + assert((key_src_p - rec_p) < rec_len); /* Sanity check */ + } + assert(cmpc <= key->end); /* Overrun sanity check */ + return; +} + +/* Find the gvt root block by looking up the GVN in the directory tree */ +int dbc_find_dtblk(phase_static_area *psa, dbc_gv_key *key, int min_levl) +{ + uchar_ptr_t rec_p; + int blk_index; + + assert(MAX_MIDENT_LEN >= key->gvn_len); + dbc_init_key(psa, &psa->gvn_key); + memcpy(psa->gvn_key, key, SIZEOF(dbc_gv_key) + key->gvn_len); /* Make key with GVN only (including trailing null) */ + psa->gvn_key->end = key->gvn_len; + /* Look up GVN in directory tree */ + blk_index = dbc_find_record(psa, psa->gvn_key, (psa->phase_one ? 0 : 1), min_levl, gdsblk_dtroot, FALSE); + return blk_index; +} + +/* Find the record in a given block + rc = -1 : integrity error detected + = -2 : record not found + = else : the index of the block in the cache where record was found (curr_blk_key is set for matching record). + Note since this routine is used in certify phase to lookup all the right hand siblings of a gvtroot block given a + maximum key, this flag tells us that failure is ok .. we just wanted to populate the cache with siblings. +*/ +int dbc_find_record(phase_static_area *psa, dbc_gv_key *key, int blk_index, int min_levl, enum gdsblk_type newblk_type, + boolean_t fail_ok) +{ + uchar_ptr_t rec_p, blk_p, blk_top, key1, key2; + unsigned short us_rec_len; + int blk_ptr, blk_levl, key_len, key_len1, key_len2, rec_len; + enum gdsblk_type blk_type; + block_info *blk_set_p; + + DBC_DEBUG(("DBC_DEBUG: dbc_find_record: Beginning scan of block index %d\n", blk_index)); + /* If blk_index is 0, there is no starting block in the cache/set so we read block 1 (root) */ + assert(0 <= min_levl); + blk_type = newblk_type; + if (gdsblk_dtroot == blk_type) + { + assert((psa->phase_one && 0 == blk_index) || (!psa->phase_one && 1 == blk_index)); + assert((psa->phase_one && -1 == psa->block_depth) || (!psa->phase_one && 0 == psa->block_depth)); + blk_index = dbc_read_dbblk(psa, 1, gdsblk_dtroot); + blk_type = gdsblk_dtgeneric; /* Type of future blocks */ + } else if (gdsblk_gvtroot == blk_type) + blk_type = gdsblk_gvtgeneric; /* Type of future read blocks */ + blk_set_p = &psa->blk_set[blk_index]; + blk_levl = ((v15_blk_hdr_ptr_t)blk_set_p->old_buff)->levl; + + /* If we have reached the minimum level, we are done but ONLY if this is also our target block (blk_index == 0). + If we are not at blk_index 0 then we need to find where our key fits into this block. This is typcially + in the first search of the directory tree where min_levl is 0 but when we hit the dtleaf block we still + want to search it to find the pointer to the gvt root block. + */ + if (min_levl == blk_levl && 0 == blk_index) + { /* This is the level we were looking for and record is found */ + DBC_DEBUG(("DBC_DEBUG: dbc_find_record: Reached minimum block level and found block we were looking for\n")); + return blk_index; + } + /* Find first block that is greater than or equal to our gvn or if we find a star-key */ + blk_p = blk_set_p->old_buff; + rec_p = blk_p + SIZEOF(v15_blk_hdr); + blk_top = blk_p + ((v15_blk_hdr_ptr_t)blk_set_p->old_buff)->bsiz; + while (rec_p < blk_top) + { + blk_set_p->prev_match = blk_set_p->curr_match; + blk_set_p->curr_match = 0; + GET_USHORT(us_rec_len, &((rec_hdr *)rec_p)->rsiz); + rec_len = us_rec_len; + if (0 >= rec_len || rec_len > psa->dbc_cs_data->max_rec_size) + /* Something messed up integrity wise - matters for phase-2 but not for phase-1 */ + return -1; + if (0 != blk_levl && BSTAR_REC_SIZE == rec_len) + { /* We have a star record - This is the record we are looking for in this block */ + blk_set_p->curr_blk_key->end = 0; /* Key length is zero for this type */ + if (min_levl == blk_levl) + { /* Block record and key information set up. We can return now */ + DBC_DEBUG(("DBC_DEBUG: dbc_find_record: Reached minimum block level -- matching scan was a star" + " key record\n")); + return blk_index; + } + DBC_DEBUG(("DBC_DEBUG: dbc_find_record: Recursing down a level via star key record at offset 0x%lx\n", \ + (rec_p - blk_p))); + GET_ULONG(blk_ptr, rec_p + VMS_ONLY(3) UNIX_ONLY(4)); + blk_index = dbc_read_dbblk(psa, blk_ptr, blk_type); + /* Keep looking next level down */ + return dbc_find_record(psa, key, blk_index, min_levl, blk_type, fail_ok); + } + /* Determine key for this record */ + dbc_find_key(psa, blk_set_p->curr_blk_key, rec_p, blk_set_p->blk_levl); + /* Perform key comparison keeping track of how many chars match (compression count) */ + if (dbc_match_key(blk_set_p->curr_blk_key, blk_set_p->blk_levl, + key, &blk_set_p->curr_match)) + { /* Found our record - If the record is in an index block, recurse. Else return the record we found */ + if (gdsblk_gvtleaf == blk_set_p->blk_type || min_levl == blk_levl) + { /* This is a terminal block. It is the end of the road */ + DBC_DEBUG(("DBC_DEBUG: dbc_find_record: Reached minimum block level (or leaf level) -- matching" \ + " scan was a normal keyed record at offset 0x%lx\n", (rec_p - blk_p))); + return blk_index; + } + /* We already know that the current block is not the one we are interested in and therefore + this record is known to contain a pointer to another block. Read the block in and + recurse to continue the search for the key. + */ + DBC_DEBUG(("DBC_DEBUG: dbc_find_record: Recursing down a level via keyed index record at offset 0x%lx\n", \ + (rec_p - blk_p))); + GET_ULONG(blk_ptr, (rec_p + SIZEOF(rec_hdr) + blk_set_p->curr_blk_key->end + - ((rec_hdr *)rec_p)->cmpc + 1)); + blk_index = dbc_read_dbblk(psa, blk_ptr, blk_type); + return dbc_find_record(psa, key, blk_index, min_levl, blk_type, fail_ok); + } + /* We want to be able to find the previous record */ + blk_set_p->prev_rec = rec_p; + memcpy(blk_set_p->prev_blk_key, blk_set_p->curr_blk_key, + (SIZEOF(dbc_gv_key) + blk_set_p->curr_blk_key->end)); + rec_p += rec_len; /* Point to next record in block */ + blk_set_p->curr_rec = rec_p; + } + /* If we don't find the record (or one greater), the block with the key we are looking for is no + longer existing in the GVT (assert that the search is for a level 0 GVT block). + */ + if (gdsblk_gvtleaf == psa->blk_set[0].blk_type) + { /* Key not found */ + DBC_DEBUG(("DBC_DEBUG: dbc_find_record: Searched for key was not found\n")); + return -2; + } + /* Else we should have found the record or a star key. Globals are NOT removed once created so we + should always be able to find an appropriate record if this is not a GVT leaf block. Exception to this + is when we are just wanting to populate the right siblings of a gvtroot block in the cache. + */ + if (!fail_ok) + { + assert(FALSE); + rts_error(VARLSTCNT(8) ERR_DBCINTEGERR, 2, RTS_ERROR_TEXT((char_ptr_t)psa->ofhdr.dbfn), + ERR_TEXT, 2, RTS_ERROR_TEXT("Unable to find index record for an existing global")); + } + return -1; +} + +/* Compare two keys. If key1 is logically greater than or equal to key2, return TRUE, else FALSE. + Also, set output parameter matchc with the number of chars that matched */ +boolean_t dbc_match_key(dbc_gv_key *key1, int blk_levl1, dbc_gv_key *key2, unsigned int *matchc) +{ + uchar_ptr_t key_val1, key_val2; + int key_len1, key_len2, key_len, lcl_matchc; + + lcl_matchc = 0; + key_val1 = key1->base; + key_val2 = key2->base; + key_len1 = key1->end + 1; + if (1 == key_len1 && 0 < blk_levl1) + { /* This is a star key record. It is always greater than the second key but + has no matching characters. + */ + *matchc = 0; + return TRUE; + } + assert(1 < key_len1); /* Otherwise we expect to see a key here */ + key_len2 = key2->end + 1; + assert(1 < key_len2); /* Not expecting a star key record in second position */ + key_len = MIN(key_len1, key_len2); + for (; key_len; key_val1++, key_val2++, key_len--) + { + if (*key_val1 == *key_val2) + ++lcl_matchc; + else + break; + } + *matchc = lcl_matchc; + if ((0 == key_len && key_len1 >= key_len2) || (0 != key_len && *key_val1 > *key_val2)) + return TRUE; + return FALSE; +} + +/* Initialize the given gv_key to a null status so dbc_find_key() start off with a new key */ +void dbc_init_key(phase_static_area *psa, dbc_gv_key **key) +{ + if (NULL == *key) + { /* Need a key allocated for this block */ + *key = malloc(SIZEOF(dbc_gv_key) + psa->dbc_cs_data->max_key_size); + (*key)->top = (uint4)(SIZEOF(dbc_gv_key) + psa->dbc_cs_data->max_key_size); + } + (*key)->end = (*key)->gvn_len = 0; + return; +} + +/* Routine to initialize a blk_set block */ +void dbc_init_blk(phase_static_area *psa, block_info *blk_set_p, int blk_num, enum gdsblk_usage blk_usage, int blk_len, + int blk_levl) +{ + blk_set_p->blk_num = blk_num; + blk_set_p->usage = blk_usage; + blk_set_p->ins_rec.blk_id = 0; + blk_set_p->found_in_cache = FALSE; + dbc_init_key(psa, &blk_set_p->curr_blk_key); + dbc_init_key(psa, &blk_set_p->prev_blk_key); + dbc_init_key(psa, &blk_set_p->ins_rec.ins_key); + blk_set_p->blk_len = blk_len; + blk_set_p->blk_levl = blk_levl; + blk_set_p->curr_match = blk_set_p->prev_match = 0; + if (NULL == blk_set_p->old_buff) + blk_set_p->old_buff = malloc(psa->dbc_cs_data->blk_size); + if (NULL == blk_set_p->new_buff) + blk_set_p->new_buff = malloc(psa->dbc_cs_data->blk_size); + blk_set_p->curr_rec = blk_set_p->prev_rec = blk_set_p->old_buff + SIZEOF(v15_blk_hdr); + blk_set_p->upd_addr = NULL; + blk_set_p->ins_blk_id_p = NULL; + return; +} + +/* Initialize database usage - open it and read in the file-header */ +void dbc_init_db(phase_static_area *psa) +{ /* This routine does the database open and initialization for both scan (phase1) and certify + phases however the requirements for these phases differ somewhat. For the scan phase we + just want to open the database, read the file-header into dbc_cs_data and verify this is a + database. For the certify phase, we also have to obtain standalone access, make sure the file + hasn't moved around since the scan phase and add reserved_bytes and max_rec_size checks. But + standalone access is somewhat tricky as part of the standalone verification process involves + (for later V4 versions) knowing the key of the shared memory segment which is kept in the + file-header meaning we have to read the file-header before we can lock it on UNIX. Because + of this, on UNIX we will RE-READ the file-header after obtaining the lock to make sure there + are no stale values. + */ + + /* We must have RW access in scan phase in order to do the DSE buffer flush to assure we are looking + at the correct database blocks and of course the certify phase needs R/W access to do its job. + */ + if (0 != ACCESS((char_ptr_t)psa->dbc_gv_cur_region->dyn.addr->fname, (R_OK | W_OK))) + { + if (EACCES == errno) + rts_error(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(psa->dbc_gv_cur_region)); + else + rts_error(VARLSTCNT(5) ERR_DBOPNERR, 2, DB_LEN_STR(psa->dbc_gv_cur_region), errno); + } + /* Open the database which on VMS gives standalone access (for phase 2) */ + psa->fc->op = FC_OPEN; + dbcertify_dbfilop(psa); /* Knows this is a phase 2 open so gives standalone access on VMS */ + psa->dbc_gv_cur_region->open = TRUE; + + if (!psa->phase_one) + { /* Verify the fileid has not changed */ + if (!is_gdid_gdid_identical(&psa->ofhdr.unique_id.uid, + &FILE_INFO(psa->dbc_gv_cur_region)->UNIX_ONLY(fileid)VMS_ONLY(file_id))) + rts_error(VARLSTCNT(1) ERR_DBCNOTSAMEDB); + } + + /* Read in database file header */ + psa->fc->op = FC_READ; + psa->fc->op_buff = (sm_uc_ptr_t)psa->dbc_cs_data; + psa->fc->op_len = SIZEOF(*psa->dbc_cs_data); + psa->fc->op_pos = 1; + dbcertify_dbfilop(psa); + + /* Verify we (still) have a GT.M V4 database */ + if (0 != memcmp(psa->dbc_cs_data->label, V15_GDS_LABEL, GDS_LABEL_SZ - 1)) + { + if (memcmp(psa->dbc_cs_data->label, V15_GDS_LABEL, GDS_LABEL_SZ - 3)) + rts_error(VARLSTCNT(4) ERR_DBNOTGDS, 2, RTS_ERROR_TEXT((char_ptr_t)psa->ofhdr.dbfn)); + else + rts_error(VARLSTCNT(4) ERR_BADDBVER, 2, RTS_ERROR_TEXT((char_ptr_t)psa->ofhdr.regname)); + } + + if (!psa->phase_one) + { +#ifdef UNIX + /* Obtain standalone access to database. + + In UNIX, this requires creation/access to the "ftok" database semaphore. This is + the main semaphore in some V4 releases and the "startup/rundown" semaphore in other + releases. But in all cases, its creation and locking will govern standalone access + to the database in question. + + On VMS, we just don't open the file as shared and we are guarranteed standalone + access to it. + */ + dbc_aquire_standalone_access(psa); + dbcertify_dbfilop(psa); /* Re-read file header */ +#endif + /* Verify reserved_bytes and max_rec_len again and verify kill_in_prog is not set */ + if (VMS_ONLY(9) UNIX_ONLY(8) > psa->dbc_cs_data->reserved_bytes) + rts_error(VARLSTCNT(4) ERR_DBMINRESBYTES, 2, VMS_ONLY(9) UNIX_ONLY(8), psa->dbc_cs_data->reserved_bytes); + if (SIZEOF(blk_hdr) > (psa->dbc_cs_data->blk_size - psa->dbc_cs_data->max_rec_size)) + rts_error(VARLSTCNT(5) ERR_DBMAXREC2BIG, 3, psa->dbc_cs_data->max_rec_size, psa->dbc_cs_data->blk_size, + (psa->dbc_cs_data->blk_size - SIZEOF(blk_hdr))); + if (0 != psa->dbc_cs_data->kill_in_prog) + rts_error(VARLSTCNT(4) ERR_DBCKILLIP, 2, RTS_ERROR_TEXT((char_ptr_t)psa->ofhdr.dbfn)); + /* Turn off replication and/or journaling for our trip here */ + if (jnl_open == psa->dbc_cs_data->jnl_state) + { + psa->dbc_cs_data->jnl_state = jnl_closed; + psa->dbc_cs_data->repl_state = repl_closed; + } + } + return; +} + +/* Close the database and remove any semaphores we had protecting it */ +void dbc_close_db(phase_static_area *psa) +{ + if (psa->dbc_gv_cur_region->open) + { /* If region is open.. close it and release the semaphores.. */ + psa->fc->op = FC_CLOSE; + dbcertify_dbfilop(psa); + psa->dbc_gv_cur_region->open = FALSE; + UNIX_ONLY(if (!psa->phase_one) dbc_release_standalone_access(psa)); + } + return; +} + +#ifdef UNIX +/* Aquire semaphores that on on all V4.x releases are the access control semaphores. In pre V4.2 releases + they were based on an FTOK of the database name with an ID of '1'. In V4.2 and later, they are based on + the FTOK of the database name with an ID of '43'. Since we do not know which flavor of database we are + dealing with, we must create and acquire both flavors of semaphore and hold them for the duration of + the phase 2 run. But just holding these semaphore is not sufficient to guarrantee standalone access. We + also must attempt to attach to the shared memory for the segment. If it is found, standalone access + is not achieved. Early V4 versions (prior to V4.2) created the shared memory with the same FTOK id as the + semaphore. Later versions would have had the key of the created private section in the file-header. Use + both approaches and fail our attempt if either succeeds. +*/ +void dbc_aquire_standalone_access(phase_static_area *psa) +{ + mu_all_version_get_standalone((char_ptr_t)psa->dbc_gv_cur_region->dyn.addr->fname, &psa->sem_inf[0]); +} + +void dbc_release_standalone_access(phase_static_area *psa) +{ + mu_all_version_release_standalone(&psa->sem_inf[0]); +} +#endif diff --git a/sr_port/dbcertify_scan_phase.c b/sr_port/dbcertify_scan_phase.c new file mode 100644 index 0000000..0d303d4 --- /dev/null +++ b/sr_port/dbcertify_scan_phase.c @@ -0,0 +1,862 @@ +/**************************************************************** + * * + * Copyright 2005, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/**************************************************************** + DBCERTIFY_SCAN_PHASE - Phase 1 scan utility + + - Flush database buffers (call to DSE BUFFER_FLUSH) + - Open database + - Verify either report_only or max_lrecl and reserved_bytes within bounds. + - If not report_only, open output file. + - Locate too-full blocks and identify type. + - Write descriptive records to output file if required + - rewrite header of output file to include total information. + - print statistics of run to console. + + Note: Most routines in this utility are self-contained + meaning they do not reference GT.M library routines + (with some notable exceptions). This is because + phase-1 is going to run against live V4 databases + and the V5 compilation will be using V5 database + structures. + ****************************************************************/ + +#include "mdef.h" + +#ifdef VMS +#include +#include +#include +#endif + +#include +#include "gtm_stat.h" +#include "gtm_ctype.h" +#include "gtm_stdio.h" +#include "gtm_string.h" +#include "gtm_unistd.h" +#include "gtm_stdlib.h" +#include "gtm_fcntl.h" + +#if defined(__MVS__) +#include "gtm_zos_io.h" +#endif +#include "cli.h" +#include "copy.h" +#include "iosp.h" +#include "gdsroot.h" +#include "v15_gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "v15_gdsbt.h" +#include "gdsfhead.h" +#include "v15_gdsfhead.h" +#include "filestruct.h" +#include "v15_filestruct.h" +#include "gdsblk.h" +#include "gdsbml.h" +#include "gdsblkops.h" +#include "gtmmsg.h" +#include "gtmio.h" +#include "spec_type.h" +#include "get_spec.h" +#include "collseq.h" +#include "format_targ_key.h" +#include "patcode.h" +#include "error.h" +#include "mupip_exit.h" +#include "gvcst_lbm_check.h" +#include "dbcertify.h" + +GBLREF gv_key *gv_altkey; +GBLREF gv_namehead *gv_target; +GBLREF pattern *pattern_list; +GBLREF pattern *curr_pattern; +GBLREF pattern mumps_pattern; +GBLREF uint4 *pattern_typemask; +GBLREF phase_static_area *psa_gbl; + +error_def(ERR_FILENAMETOOLONG); +error_def(ERR_DBNOTGDS); +error_def(ERR_BADDBVER); +error_def(ERR_DBMINRESBYTES); +error_def(ERR_DBMAXREC2BIG); +error_def(ERR_DEVOPENFAIL); +error_def(ERR_DBCREC2BIG); +error_def(ERR_SYSCALL); +error_def(ERR_TEXT); +error_def(ERR_DBCINTEGERR); +error_def(ERR_COLLATIONUNDEF); +error_def(ERR_COLLTYPVERSION); +error_def(ERR_GVIS); +error_def(ERR_MUPCLIERR); +ZOS_ONLY(error_def(ERR_BADTAG);) + +void dbc_write_p1out(phase_static_area *psa, void *obuf, int olen); +void dbc_requeue_block(phase_static_area *psa, block_id blk_num); +void dbc_integ_error(phase_static_area *psa, block_id blk_num, char_ptr_t emsg); +void dbc_process_block(phase_static_area *psa, int blk_num, gtm_off_t dbptr); +uchar_ptr_t dbc_format_key(phase_static_area *psa, uchar_ptr_t rec_p); + +/* Phase 1 certification process scans the dstabase */ +void dbcertify_scan_phase(void) +{ + int max_max_rec_size; /* Maximum value for max record size for given block size */ + int lm_offset; /* Local bit map offset */ + int mm_offset; /* Master map offset */ + int save_errno, blk_index; + ssize_t rc; + size_t len; + uchar_ptr_t badfn; + char_ptr_t errmsg; + unsigned char dbfn[MAX_FN_LEN + 1]; + unsigned short buff_len; + gtm_off_t dbptr; + boolean_t outfile_present; + enum gdsblk_type blk_type; + block_id bitmap_blk_num, last_bitmap_blk_num, blk_num; + integ_error_blk_list *iebl; + phase_static_area *psa; + ZOS_ONLY(int realfiletag;) + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + psa = psa_gbl; + DBC_DEBUG(("DBC_DEBUG: Beginning scan phase\n")); + psa->bsu_keys = TRUE; + UNIX_ONLY(atexit(dbc_scan_phase_cleanup)); + TREF(transform) = TRUE; + psa->block_depth = psa->block_depth_hwm = -1; /* Initialize no cache */ + initialize_pattern_table(); + /* On VMS, file operations are in 512 byte chunks which seems to give VMS some fidgies + when we rewrite the header later. Make sure the header is in one single 512 byte + block. + */ + assert(DISK_BLOCK_SIZE == SIZEOF(p1hdr)); + /* Check results of option parse */ + psa->phase_one = TRUE; + psa->report_only = (CLI_PRESENT == cli_present("REPORT_ONLY")); + psa->detail = (CLI_PRESENT == cli_present("DETAIL")); + psa->bsu_keys = !cli_negated("BSU_KEYS"); + if (outfile_present = (CLI_PRESENT == cli_present("OUTFILE"))) + { + buff_len = SIZEOF(psa->outfn) - 1; + if (FALSE == cli_get_str("OUTFILE", (char_ptr_t)psa->outfn, &buff_len)) + mupip_exit(ERR_MUPCLIERR); + psa->outfn[buff_len] = '\0'; /* Not null terminated if max string on UNIX, not at all on VMS */ + } + if (CLI_PRESENT == cli_present("TEMPFILE_DIR")) + { /* Want to put temp files in this directory */ + buff_len = SIZEOF(psa->tmpfiledir) - 1; + if (FALSE == cli_get_str("TEMPFILE_DIR", (char_ptr_t)psa->tmpfiledir, &buff_len)) + mupip_exit(ERR_MUPCLIERR); + psa->tmpfiledir[buff_len] = '\0'; + } + psa->keep_temp_files = (CLI_PRESENT == cli_present("KEEP_TEMPS")); + buff_len = SIZEOF(psa->regname) - 1; + if (FALSE == cli_get_str("REGION", (char_ptr_t)psa->regname, &buff_len)) + mupip_exit(ERR_MUPCLIERR); + psa->regname[buff_len] = '\0'; + /* First order of business is to flush the database in the cache so we start with + as current a version as possible. + */ + dbc_open_command_file(psa); +# ifdef VMS + strcpy((char_ptr_t)psa->util_cmd_buff, RESULT_ASGN); + strcat((char_ptr_t)psa->util_cmd_buff, (char_ptr_t)psa->tmprsltfile); + dbc_write_command_file(psa, (char_ptr_t)psa->util_cmd_buff); + dbc_write_command_file(psa, DSE_START); +# else + strcpy((char_ptr_t)psa->util_cmd_buff, DSE_START_PIPE_RSLT1); + strcat((char_ptr_t)psa->util_cmd_buff, (char_ptr_t)psa->tmprsltfile); + strcat((char_ptr_t)psa->util_cmd_buff, DSE_START_PIPE_RSLT2); + dbc_write_command_file(psa, (char_ptr_t)psa->util_cmd_buff); +# endif + strcpy((char_ptr_t)psa->util_cmd_buff, DSE_FIND_REG_ALL); + strcat((char_ptr_t)psa->util_cmd_buff, "="); + strcat((char_ptr_t)psa->util_cmd_buff, (char_ptr_t)psa->regname); + dbc_write_command_file(psa, (char_ptr_t)psa->util_cmd_buff); + dbc_write_command_file(psa, DSE_BFLUSH); + dbc_write_command_file(psa, DSE_QUIT); + UNIX_ONLY(dbc_write_command_file(psa, "EOF")); + dbc_close_command_file(psa); + dbc_remove_result_file(psa); + dbc_run_command_file(psa, "DSE", DSE_BFLUSH, TRUE); + /* Also need to find out what the database name for this region is */ + dbc_find_database_filename(psa, psa->regname, dbfn); + /* See if a phase-1 output filename was specified. If not, create a default name */ + if (!outfile_present) + { /* No output file name specified -- supply a default */ + len = strlen((char_ptr_t)dbfn); + if (MAX_FN_LEN < (len + SIZEOF(DEFAULT_OUTFILE_SUFFIX) - 1)) + { + badfn = malloc(len + SIZEOF(DEFAULT_OUTFILE_SUFFIX)); + memcpy(badfn, dbfn, len); + badfn[len] = 0; + strcat((char_ptr_t)badfn, DEFAULT_OUTFILE_SUFFIX); + rts_error(VARLSTCNT(6) ERR_FILENAMETOOLONG, 0, ERR_TEXT, 2, RTS_ERROR_STRING((char_ptr_t)badfn)); + } + strcpy((char_ptr_t)psa->outfn, (char_ptr_t)dbfn); + strcat((char_ptr_t)psa->outfn, DEFAULT_OUTFILE_SUFFIX); + } + /* Build data structures and open database */ + MALLOC_INIT(psa->dbc_gv_cur_region, SIZEOF(gd_region)); + MALLOC_INIT(psa->dbc_gv_cur_region->dyn.addr, SIZEOF(gd_segment)); + psa->dbc_gv_cur_region->dyn.addr->acc_meth = dba_bg; + len = strlen((char_ptr_t)dbfn); + strcpy((char_ptr_t)psa->dbc_gv_cur_region->dyn.addr->fname, (char_ptr_t)dbfn); + psa->dbc_gv_cur_region->dyn.addr->fname_len = (unsigned short)len; + FILE_CNTL_INIT(psa->dbc_gv_cur_region->dyn.addr); + psa->dbc_gv_cur_region->dyn.addr->file_cntl->file_type = dba_bg; + psa->dbc_cs_data = malloc(SIZEOF(*psa->dbc_cs_data)); + /* Initialize for db processing - open and read in file-header */ + psa->fc = psa->dbc_gv_cur_region->dyn.addr->file_cntl; + dbc_init_db(psa); + /* If REPORT_ONLY was *NOT* specified, then we require two things: + * 1) The reserved bytes value must be at least 8 (UNIX) or 9 (VMS). + * 2) The maximum record size must be < blk_size - 16 to allow for new V5 block header. + */ + max_max_rec_size = psa->dbc_cs_data->blk_size - SIZEOF(blk_hdr); + if (VMS_ONLY(9) UNIX_ONLY(8) > psa->dbc_cs_data->reserved_bytes) + { + gtm_putmsg(VARLSTCNT(4) ERR_DBMINRESBYTES, 2, VMS_ONLY(9) UNIX_ONLY(8), psa->dbc_cs_data->reserved_bytes); + if (!psa->report_only) + exit(SS_NORMAL - 1); /* Gives -1 on UNIX (failure) and 0 on VMS (failure) */ + } + if (psa->dbc_cs_data->max_rec_size > max_max_rec_size) + { + gtm_putmsg(VARLSTCNT(5) ERR_DBMAXREC2BIG, 3, psa->dbc_cs_data->max_rec_size, psa->dbc_cs_data->blk_size, + max_max_rec_size); + if (!psa->report_only) + exit(SS_NORMAL - 1); + } + /* If not REPORT_ONLY, open the phase-1 output file and write header info. Note this will be + * re-written at the completion of the process. + */ + if (!psa->report_only) + { /* Recreate the file entirely if it exists */ + psa->outfd = OPEN3((char_ptr_t)psa->outfn, O_WRONLY + O_CREAT + O_TRUNC, S_IRUSR + S_IWUSR RMS_OPEN_BIN); + if (FD_INVALID == psa->outfd) + { /* The following STRERROR() extraction necessary for VMS portability */ + save_errno = errno; + errmsg = STRERROR(save_errno); + rts_error(VARLSTCNT(8) ERR_DEVOPENFAIL, 2, RTS_ERROR_STRING((char_ptr_t)psa->outfn), + ERR_TEXT, 2, RTS_ERROR_STRING(errmsg)); + } +# ifdef __MVS__ + if (-1 == gtm_zos_set_tag(psa->outfd, TAG_BINARY, TAG_NOTTEXT, TAG_FORCE, &realfiletag)) + TAG_POLICY_GTM_PUTMSG((char_ptr_t)psa->outfn, errno, realfiletag, TAG_BINARY); +# endif + memset((void *)&psa->ofhdr, 0, SIZEOF(p1hdr)); + memcpy(psa->ofhdr.p1hdr_tag, P1HDR_TAG, SIZEOF(psa->ofhdr.p1hdr_tag)); + dbc_write_p1out(psa, &psa->ofhdr, SIZEOF(p1hdr)); /* Initial hdr is all zeroes */ + } + /* Initialize */ + psa->block_buff = malloc(psa->dbc_cs_data->blk_size); /* Current data/index block we are looking at */ + psa->curr_lbmap_buff= malloc(psa->dbc_cs_data->blk_size); /* Current local bit map cache */ + psa->local_bit_map_cnt = (psa->dbc_cs_data->trans_hist.total_blks + psa->dbc_cs_data->bplmap - 1) + / psa->dbc_cs_data->bplmap; + dbptr = (psa->dbc_cs_data->start_vbn - 1) * DISK_BLOCK_SIZE; + blk_num = 0; + /* Loop to process every local bit map in the database. Since the flag tells us only + * (0) it is full or (1) it is not full, we have to completely process each local bit map. + */ + psa->fc->op_len = psa->dbc_cs_data->blk_size; + for (mm_offset = 0; + (mm_offset < psa->local_bit_map_cnt) && (blk_num < psa->dbc_cs_data->trans_hist.total_blks); + ++mm_offset) + { /* Once through for each master map bit in our database */ + psa->fc->op_buff = psa->curr_lbmap_buff; + psa->fc->op_pos = (dbptr / DISK_BLOCK_SIZE) + 1; + dbcertify_dbfilop(psa); /* Read local bitmap block (no return if error) */ + /* Verification we haven't gotten lost */ + assert(0 == (blk_num % psa->dbc_cs_data->bplmap)); + /* Loop through each local bit map processing (checking) allocated blocks */ + for (lm_offset = 0; + (lm_offset < (psa->dbc_cs_data->bplmap * BML_BITS_PER_BLK)) + && (blk_num < psa->dbc_cs_data->trans_hist.total_blks); + lm_offset += BML_BITS_PER_BLK, dbptr += psa->dbc_cs_data->blk_size, blk_num++) + { + if (gvcst_blk_is_allocated(psa->curr_lbmap_buff + SIZEOF(v15_blk_hdr), lm_offset)) + /* This block is in use -- process it */ + dbc_process_block(psa, blk_num, dbptr); + else + { + DBC_DEBUG(("DBC_DEBUG: Block 0x%x is NOT allocated -- bypassing\n", blk_num)); + psa->blks_bypassed++; + } + } + if ((BLKS_PER_LMAP * BML_BITS_PER_BLK) > lm_offset) + DBC_DEBUG(("DBC_DEBUG: Partial lmap processed - blk 0x%x - lm_offset 0x%x\n", \ + (mm_offset * BLKS_PER_LMAP), lm_offset)); + } + /* If there were any blocks we had trouble processing the first time through, perform a second buffer flush and + * retry them. If they are still broken, it is an error this time. Note all integ errors are fatal but record-too-long + * errors only cause the run to turn into a "report_only" type run and thus do not create the output file but + * scanning continues. + */ + if (NULL != psa->iebl) + { + DBC_DEBUG(("DBC_DEBUG: Entering block re-processing loop\n")); + psa->final = TRUE; + last_bitmap_blk_num = -1; + for (iebl = psa->iebl; iebl; iebl = iebl->next) + for (blk_index = 0; blk_index < iebl->blk_cnt; blk_index++) + { /* For each previously broken block. First see if they are still allocated */ + blk_num = iebl->blk_list[blk_index]; + assert(blk_num); + bitmap_blk_num = ROUND_DOWN2(blk_num, psa->dbc_cs_data->bplmap); + /* Read bitmap in if it isn't already in our buffer */ + if (bitmap_blk_num != last_bitmap_blk_num) + { + psa->fc->op_buff = psa->curr_lbmap_buff; + dbptr = ((psa->dbc_cs_data->start_vbn - 1) * DISK_BLOCK_SIZE) + + psa->dbc_cs_data->free_space + + ((gtm_off_t)psa->dbc_cs_data->blk_size * bitmap_blk_num); + psa->fc->op_pos = (dbptr / DISK_BLOCK_SIZE) + 1; + dbcertify_dbfilop(psa); /* Read local bitmap block (no return if error) */ + last_bitmap_blk_num = bitmap_blk_num; + } + lm_offset = (blk_num - bitmap_blk_num) * 2; + dbptr = ((psa->dbc_cs_data->start_vbn - 1) * DISK_BLOCK_SIZE) + psa->dbc_cs_data->free_space + + ((gtm_off_t)psa->dbc_cs_data->blk_size * blk_num); + if (gvcst_blk_is_allocated(psa->curr_lbmap_buff + SIZEOF(v15_blk_hdr), lm_offset)) + /* This block is in use -- process it */ + dbc_process_block(psa, blk_num, dbptr); + else + { + DBC_DEBUG(("DBC_DEBUG: Bypassing block 0x%x because no longer allocated\n", blk_num)); + psa->blks_bypassed++; + } + } + DBC_DEBUG(("DBC_DEBUG: Block reprocessing complete\n")); + } + /* Now, update the fields in the output file's fileheader if we are writing it */ + if (!psa->report_only) + { + rc = lseek(psa->outfd, (ssize_t)0, SEEK_SET); + if (-1 == rc) + { + save_errno = errno; + errmsg = STRERROR(save_errno); + rts_error(VARLSTCNT(11) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("lseek()"), CALLFROM, + ERR_TEXT, 2, RTS_ERROR_STRING(errmsg)); + } + psa->ofhdr.tn = psa->dbc_cs_data->trans_hist.curr_tn; + psa->ofhdr.blk_count = psa->blks_too_big; + psa->ofhdr.tot_blocks = psa->blks_processed; + psa->ofhdr.dt_leaf_cnt = psa->dtlvl0; + psa->ofhdr.dt_index_cnt = psa->dtlvln0; + psa->ofhdr.gvt_leaf_cnt = psa->gvtlvl0; + psa->ofhdr.gvt_index_cnt = psa->gvtlvln0; + psa->ofhdr.uid_len = SIZEOF(unique_file_id); /* Size used by v5cbsu since this field len varies by platform */ + memcpy(&psa->ofhdr.unique_id, &FILE_INFO(psa->dbc_gv_cur_region)->UNIX_ONLY(fileid)VMS_ONLY(file_id), + SIZEOF(unique_file_id)); + assert(SIZEOF(psa->ofhdr.regname) > strlen((char_ptr_t)psa->regname)); + strcpy((char_ptr_t)psa->ofhdr.regname, (char_ptr_t)psa->regname); + assert(SIZEOF(psa->ofhdr.dbfn) > strlen((char_ptr_t)psa->dbc_gv_cur_region->dyn.addr->fname)); + strcpy((char_ptr_t)psa->ofhdr.dbfn, (char_ptr_t)psa->dbc_gv_cur_region->dyn.addr->fname); + dbc_write_p1out(psa, &psa->ofhdr, SIZEOF(p1hdr)); /* Rewrite populated output file header */ + CLOSEFILE_RESET(psa->outfd, rc); /* Close output file; Resets "psa->outfd" to FD_INVALID */ + if (-1 == rc) + { + save_errno = errno; + errmsg = STRERROR(save_errno); + rts_error(VARLSTCNT(11) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("close()"), CALLFROM, + ERR_TEXT, 2, RTS_ERROR_STRING(errmsg)); + } + } + psa->fc->op = FC_CLOSE; + dbcertify_dbfilop(psa); /* Close database */ + PRINTF("\n\n"); + PRINTF("Total blocks in database ------- %12d [0x%08x]\n", psa->dbc_cs_data->trans_hist.total_blks, + psa->dbc_cs_data->trans_hist.total_blks); + PRINTF("Total local bitmap blocks ------- %12d [0x%08x]\n", psa->local_bit_map_cnt, psa->local_bit_map_cnt); + PRINTF("Blocks bypassed ----------------- %12d [0x%08x]\n", psa->blks_bypassed, psa->blks_bypassed); + PRINTF("Blocks processed ---------------- %12d [0x%08x]\n", psa->blks_processed, psa->blks_processed); + PRINTF("Blocks needing to be split ------ %12d [0x%08x]\n", psa->blks_too_big, psa->blks_too_big); + PRINTF("- DT leaf (data) blocks --------- %12d [0x%08x]\n", psa->dtlvl0, psa->dtlvl0); + PRINTF("- DT index blocks --------------- %12d [0x%08x]\n", psa->dtlvln0, psa->dtlvln0); + PRINTF("- GVT leaf (data) blocks -------- %12d [0x%08x]\n", psa->gvtlvl0, psa->gvtlvl0); + PRINTF("- GVT index blocks -------------- %12d [0x%08x]\n", psa->gvtlvln0, psa->gvtlvln0); + /* Release resources */ + free(psa->dbc_cs_data); +# ifdef VMS + /* Some extra freeing of control blocks on VMS */ + if (NULL != FILE_INFO(psa->dbc_gv_cur_region)->fab) + free(FILE_INFO(psa->dbc_gv_cur_region)->fab); + if (NULL != FILE_INFO(psa->dbc_gv_cur_region)->nam) + { + if (NULL != FILE_INFO(psa->dbc_gv_cur_region)->nam->nam$l_esa) + free(FILE_INFO(psa->dbc_gv_cur_region)->nam->nam$l_esa); + free(FILE_INFO(psa->dbc_gv_cur_region)->nam); + } + if (NULL != FILE_INFO(psa->dbc_gv_cur_region)->xabfhc) + free(FILE_INFO(psa->dbc_gv_cur_region)->xabfhc); + if (NULL != FILE_INFO(psa->dbc_gv_cur_region)->xabpro) + free(FILE_INFO(psa->dbc_gv_cur_region)->xabpro); +# endif + free(psa->dbc_gv_cur_region->dyn.addr->file_cntl->file_info); + free(psa->dbc_gv_cur_region->dyn.addr->file_cntl); + free(psa->dbc_gv_cur_region->dyn.addr); + free(psa->dbc_gv_cur_region); + free(psa->curr_lbmap_buff); + free(psa->block_buff); + for (; psa->iebl; ) + { + iebl = psa->iebl->next; + free(psa->iebl); + psa->iebl = iebl; + } + free(psa); +} + +/* Write the given output record but keep the global variable "chksum" updated with the tally. + * Assumes that all writes are from aligned buffers (regardless of how they end up on disk). + */ +void dbc_write_p1out(phase_static_area *psa, void *obuf, int olen) +{ + int save_errno; + ssize_t rc; + char_ptr_t errmsg; + + DBC_DEBUG(("DBC_DEBUG: Output %d bytes to dbcertscan output file\n", olen)); + rc = write(psa->outfd, obuf, olen); + if (-1 == rc) + { + save_errno = errno; + errmsg = STRERROR(save_errno); + rts_error(VARLSTCNT(11) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("close()"), CALLFROM, + ERR_TEXT, 2, RTS_ERROR_STRING(errmsg)); + } +} + +/* Routine to process a database block */ +void dbc_process_block(phase_static_area *psa, int blk_num, gtm_off_t dbptr) +{ + int rec_len, rec1_cmpc, rec2_cmpc, key_len, blk_levl, rec1_len, rec2_len, rec2_rlen; + int free_bytes, blk_len; + int save_errno, mm_offset; + ssize_t rc; + size_t len, rec1_gvn_len; + boolean_t have_dt_blk; + unsigned short us_rec_len; + char_ptr_t errmsg, key_pfx; + uchar_ptr_t rec_ptr, rec1_ptr, rec2_ptr, key_ptr; + enum gdsblk_type blk_type; + + DBC_DEBUG(("DBC_DEBUG: Block 0x%x is allocated -- processing\n", blk_num)); + psa->fc->op_buff = psa->block_buff; + psa->fc->op_pos = (dbptr / DISK_BLOCK_SIZE) + 1; + dbcertify_dbfilop(psa); /* Read data/index block (no return if error) */ + /* Check free space in block */ + blk_len = ((v15_blk_hdr_ptr_t)psa->block_buff)->bsiz; + free_bytes = psa->dbc_cs_data->blk_size - blk_len; + if (UNIX_ONLY(8) VMS_ONLY(9) > free_bytes) + { + blk_levl = ((v15_blk_hdr_ptr_t)psa->block_buff)->levl; + if (MAX_BT_DEPTH <= blk_levl) + dbc_integ_error(psa, blk_num, "Bad block level"); + /* Isolate first record for length check */ + rec1_ptr = psa->block_buff + SIZEOF(v15_blk_hdr); + rec1_cmpc = ((rec_hdr_ptr_t)rec1_ptr)->cmpc; + if (0 != rec1_cmpc) + dbc_integ_error(psa, blk_num, "Bad compression count"); + GET_USHORT(us_rec_len, &((rec_hdr_ptr_t)rec1_ptr)->rsiz); + rec1_len = us_rec_len; + if ((rec1_len + SIZEOF(v15_blk_hdr)) < blk_len) + { /* There is a 2nd record. It must also be checked as it is possible for a + * too-long record to exist as the 2nd record if it is a near clone of the + * first record (differing only in the last byte of the key) and the first + * record has no value (null value). + */ + rec2_ptr = rec1_ptr + rec1_len; + rec2_cmpc = ((rec_hdr_ptr_t)rec2_ptr)->cmpc; + if (rec2_cmpc > rec1_len) + dbc_integ_error(psa, blk_num, "Compression count too large"); + GET_USHORT(us_rec_len, &((rec_hdr_ptr_t)rec2_ptr)->rsiz); + rec2_len = us_rec_len; + rec2_rlen = rec2_len + rec2_cmpc; + } else + { + rec2_len = rec2_rlen = 0; /* There is no second record */ + assert(0 == blk_levl); /* And thus this is supposed to be lvl0 data block */ + } + if (rec1_len > psa->dbc_cs_data->max_rec_size) + { /* First record exceeds maximum size */ + rec_len = rec1_len; + rec_ptr = rec1_ptr; + } else if (rec2_rlen > psa->dbc_cs_data->max_rec_size) + { /* Second record exceeds maximum size */ + rec_len = rec2_rlen; + rec_ptr = rec2_ptr; + } else + /* All is well .. set flag record length is ok */ + rec_len = 0; + if (rec_len) + { /* One of these records exceeds the max size - might be a transitory integ on + * 1st pass or permanent on 2nd pass. + */ + assert(rec_ptr); + if (psa->final) + { /* Should be a data block with a too-long record in it */ + assert(0 == ((v15_blk_hdr_ptr_t)psa->block_buff)->levl); + psa->gvtlvl0++; + key_ptr = dbc_format_key(psa, rec_ptr); /* Text representation of the key */ + gtm_putmsg(VARLSTCNT(9) ERR_DBCREC2BIG, 7, + RTS_ERROR_STRING((char_ptr_t)key_ptr), rec_len, + blk_num, psa->dbc_cs_data->max_rec_size, + psa->dbc_gv_cur_region->dyn.addr->fname_len, + psa->dbc_gv_cur_region->dyn.addr->fname); + if (!psa->report_only) + { + CLOSEFILE_RESET(psa->outfd, rc); /* Close output file; Resets "psa->outfd" to FD_INVALID */ + if (-1 == rc) + { + save_errno = errno; + errmsg = STRERROR(save_errno); + rts_error(VARLSTCNT(11) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("close()"), CALLFROM, + ERR_TEXT, 2, RTS_ERROR_STRING(errmsg)); + } + if (-1 == remove((char_ptr_t)psa->outfn)) + { /* Delete bogus output file */ + save_errno = errno; + errmsg = STRERROR(save_errno); + rts_error(VARLSTCNT(11) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("remove()"), CALLFROM, + ERR_TEXT, 2, RTS_ERROR_STRING(errmsg)); + } + psa->report_only = TRUE; /* No more writing to output file */ + } + } else /* Not our final trip through, cause the block to be requeued for later processing */ + dbc_requeue_block(psa, blk_num); + return; + } + /* Determine type of block (DT lvl 0, DT lvl !0, GVT lvl 0, GVT lvl !0) + * Rules for checking: + * 1) If compression count of 2nd record is zero, it *must* be a directory tree block. This is a fast path + * check to avoid doing the strlen in the second check. + * 2) If compression count of second record is less than or equal to the length of the global variable name, + * then this must be a directory tree block. The reason this check works is a GVT index or data block + * would have same GVN in the 2nd record as the first so the compression count would be a minimum of + * (length(GVN) + 1). The "+ 1" is for the terminating null of the GVN. + * 3) If there is no second record, this must be a lvl0 data tree block as no other block could exceed + * the maximum block size with a single record. + */ + have_dt_blk = FALSE; + if (0 != rec2_len) + { /* There is a second record .. */ + if (0 == rec2_cmpc) + have_dt_blk = TRUE; /* Only DT records have non-zero cmpc in 2nd record */ + else + { + rec1_gvn_len = strlen((char_ptr_t)rec1_ptr + SIZEOF(rec_hdr)); + if (rec2_cmpc <= rec1_gvn_len) + have_dt_blk = TRUE; + } + if (have_dt_blk) + { + if (0 == blk_levl) + { + psa->dtlvl0++; + blk_type = gdsblk_dtleaf; + } else + { + psa->dtlvln0++; + if (1 != blk_num) /* Quick root block check */ + blk_type = gdsblk_dtindex; + else + blk_type = gdsblk_dtroot; + } + } else + { + if (0 == blk_levl) + { + psa->gvtlvl0++; + blk_type = gdsblk_gvtleaf; + } else + { + psa->gvtlvln0++; + /* Note from the master map, we cannot tell if this is a gvtroot + block or not so just mark it as an index block for now. It will + be noted as a root block when read in by stage 2. + */ + blk_type = gdsblk_gvtindex; + } + } + } else + { /* There was no second record. This record must be a lvl 0 data block */ + psa->gvtlvl0++; + blk_type = gdsblk_gvtleaf; + } + if (psa->bsu_keys && gdsblk_gvtleaf == blk_type) + { /* Get text representation of the key. Because we are not using the DT in the cache, there is + * a possibility that we cannot find this global however that chance should be slight. Not finding + * it only means that this record cannot be processed by v5cbsu.m and will instead have to be + * processed by phase-2. This is an acceptable alternative. + * + * The key we format is the second record in the block if it is available. This is because of the + * way an update in place takes place. If the block is too big, the block is split at the point + * of the update. If the (somewhat long) keys of the first and second records differ by only 1 + * byte and the first record has no value, we could get into a situation where the second record + * is highly compressed and a split at that point will give back insufficient space for the + * new block to meet the block size requirements. If instead we split the record after the 2nd + * record in this situation, the resulting blocks will be sized sufficiently to meet the upgrade + * requirements. + */ + if (rec2_len) + { /* There is a 2nd record */ + rec_ptr = rec2_ptr; + key_pfx = "[2] "; + } else + { /* No 2nd record, format 1st record instead */ + rec_ptr = rec1_ptr; + key_pfx = "[1] "; + } + key_ptr = dbc_format_key(psa, (rec2_len ? rec2_ptr : rec1_ptr)); + } else + { + key_ptr = (uchar_ptr_t)""; + key_pfx = ""; + } + if (psa->detail) + { + if (0 == psa->blks_too_big) + { /* Print header */ + PRINTF("\nProblem blocks (max block size = %d):\n\n", psa->dbc_cs_data->blk_size); + PRINTF(" Blknum Offset Blktype BlkLvl Blksize Free" + " Key\n"); + } +# ifndef __osf__ + GTM64_ONLY(PRINTF(" 0x%08x %16lx %s %5d %9d %6d %s%s\n", blk_num, dbptr, + (have_dt_blk ? " DT " : " GVT "), + ((v15_blk_hdr_ptr_t)psa->block_buff)->levl, + ((v15_blk_hdr_ptr_t)psa->block_buff)->bsiz, free_bytes, key_pfx, key_ptr)); + NON_GTM64_ONLY(PRINTF(" 0x%08x %16llx %s %5d %9d %6d %s%s\n", blk_num, dbptr, + (have_dt_blk ? " DT " : " GVT "), + ((v15_blk_hdr_ptr_t)psa->block_buff)->levl, + ((v15_blk_hdr_ptr_t)psa->block_buff)->bsiz, free_bytes, key_pfx, key_ptr)); + VMS_ONLY(PRINTF(" 0x%08x %16llx %s %5d %9d %6d %s%s\n", blk_num, dbptr, + (have_dt_blk ? " DT " : " GVT "), + ((v15_blk_hdr_ptr_t)psa->block_buff)->levl, + ((v15_blk_hdr_ptr_t)psa->block_buff)->bsiz, free_bytes, key_pfx, key_ptr)); +# else + PRINTF(" 0x%08x %16lx %s %5d %9d %6d\n %s%s", blk_num, dbptr, + (have_dt_blk ? " DT " : " GVT "), + ((v15_blk_hdr_ptr_t)psa->block_buff)->levl, + ((v15_blk_hdr_ptr_t)psa->block_buff)->bsiz, free_bytes, key_pfx, key_ptr); +# endif + } + psa->blks_too_big++; + /* Prepare the record to put to the output file */ + if (!psa->report_only) + { + GET_LONG(psa->rhdr.tn, &((v15_blk_hdr_ptr_t)psa->block_buff)->tn); + psa->rhdr.blk_num = blk_num; + psa->rhdr.blk_type = blk_type; + psa->rhdr.blk_levl = blk_levl; + if (psa->bsu_keys && gdsblk_gvtleaf == blk_type) + psa->rhdr.akey_len = key_len = STRLEN((char_ptr_t)key_ptr); + else + psa->rhdr.akey_len = 0; + dbc_write_p1out(psa, &psa->rhdr, SIZEOF(p1rec)); + if (psa->bsu_keys && gdsblk_gvtleaf == blk_type) + dbc_write_p1out(psa, key_ptr, key_len); + } + } + psa->blks_processed++; +} + +/* Requeue a block for later processing after a flush to verify we have a problem or not */ +void dbc_requeue_block(phase_static_area *psa, block_id blk_num) +{ + integ_error_blk_list *iebl; + + if (NULL == psa->iebl || MAX_IEB_CNT <= (psa->iebl->blk_cnt + 1)) + { /* Need a new/another block to hold error blocks */ + DBC_DEBUG(("DBC_DEBUG: Allocating new iebl struct\n")); + iebl = malloc(SIZEOF(integ_error_blk_list)); + iebl->next = psa->iebl; + iebl->blk_cnt = 0; + psa->iebl = iebl; + } + DBC_DEBUG(("DBC_DEBUG: Requeuing block 0x%x for later processing\n", blk_num)) + psa->iebl->blk_list[psa->iebl->blk_cnt++] = blk_num; +} + +/* Routine to handle integrity errors. If first pass (not final), requeue the block. If on + * final pass, give integrity rts_error. + */ +void dbc_integ_error(phase_static_area *psa, block_id blk_num, char_ptr_t emsg) +{ + char_ptr_t errmsg; + unsigned char intgerrmsg[256]; + int save_errno; + ssize_t rc; + size_t len; + + if (!psa->final) + dbc_requeue_block(psa, blk_num); + else + { /* Give integrity error message */ + if (!psa->report_only) + { + CLOSEFILE_RESET(psa->outfd, rc); /* Close output file; Resets "psa->outfd" to FD_INVALID */ + if (-1 == rc) + { + save_errno = errno; + errmsg = STRERROR(save_errno); + rts_error(VARLSTCNT(11) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("close()"), CALLFROM, + ERR_TEXT, 2, RTS_ERROR_STRING(errmsg)); + } + if (-1 == remove((char_ptr_t)psa->outfn)) + { /* Delete bogus output file */ + save_errno = errno; + errmsg = STRERROR(save_errno); + rts_error(VARLSTCNT(11) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("remove()"), CALLFROM, + ERR_TEXT, 2, RTS_ERROR_STRING(errmsg)); + } + psa->report_only = TRUE; /* No more writing to output file */ + } + intgerrmsg[0] = 0; + strcpy((char_ptr_t)intgerrmsg, emsg); + strcat((char_ptr_t)intgerrmsg, " in block 0x"); + len = strlen((char_ptr_t)intgerrmsg); + i2hex(blk_num, &intgerrmsg[len], 8); + intgerrmsg[len + 8] = 0; + rts_error(VARLSTCNT(8) ERR_DBCINTEGERR, 2, RTS_ERROR_STRING((char_ptr_t)psa->ofhdr.dbfn), + ERR_TEXT, 2, RTS_ERROR_STRING((char_ptr_t)intgerrmsg)); + } +} + +/* Generate an ascii representation of the given key in the current block buffer. + * This is accomplished (mainly) by: + * 1) Locating the key within the record. + * 2) Calling the dbc_find_dtblk() routine to locate the directory entry for us. + * 3) Setting up gv_target and friends to point to the entry. + * 4) Checking the located directory entry for collation information. + * 5) Calling format_targ_key() to do the formatting into our buffer. + * Note: usage of "first_rec_key" is somewhat overloaded in this routine. Under most + * circumstances, it is most likely the second key that is being formatted but + * this is a defined area that is available for use in this (scan phase) routine + * so we use it. + */ +uchar_ptr_t dbc_format_key(phase_static_area *psa, uchar_ptr_t trec_p) +{ + int dtblk_index, hdr_len, rec_value_len, rec_len, rec_cmpc; + size_t len; + uchar_ptr_t blk_p, rec_value_p, subrec_p, key_end_p, rec_p; + block_info *blk_set_p; + unsigned short us_rec_len; + + dbc_init_key(psa, &psa->first_rec_key); + /* We have to parse the block down to the supplied key to make sure the compressed portions + * of the key are available. + */ + rec_p = psa->block_buff + SIZEOF(v15_blk_hdr); + while (rec_p < trec_p) + { + dbc_find_key(psa, psa->first_rec_key, rec_p, 0); + GET_USHORT(us_rec_len, &((rec_hdr_ptr_t)rec_p)->rsiz); + rec_p += us_rec_len; + } + assert(rec_p == trec_p); + dbc_find_key(psa, psa->first_rec_key, trec_p, 0); + psa->first_rec_key->gvn_len = USTRLEN((char_ptr_t)psa->first_rec_key->base); /* The GVN we need to lookup in the DT */ + assert(0 < psa->first_rec_key->gvn_len); + psa->block_depth = -1; /* Reset to beginning each pass */ + dtblk_index = dbc_find_dtblk(psa, psa->first_rec_key, 0); + if (0 > dtblk_index) + { /* Couldn't find the GVN in the DT. Tiz possible but rare (concurrency issues) and of no major consequence. */ + assert(FALSE); + return NULL; + } + blk_set_p = &psa->blk_set[dtblk_index]; + blk_p = blk_set_p->old_buff; + assert(0 == ((v15_blk_hdr_ptr_t)blk_p)->levl); + rec_cmpc = ((rec_hdr *)blk_set_p->curr_rec)->cmpc; + rec_value_p = (blk_set_p->curr_rec + SIZEOF(rec_hdr) + blk_set_p->curr_blk_key->end + 1 - rec_cmpc); + /* Verify that the dt record we found is the exact one we were looking for */ + if ((psa->first_rec_key->gvn_len + 1) != blk_set_p->curr_blk_key->end) + /* Some concurrency issues no doubt.. */ + return NULL; + if (0 != memcmp(psa->first_rec_key->base, blk_set_p->curr_blk_key->base, blk_set_p->curr_blk_key->end)) + return NULL; + /* Create gv_target if necessary */ + if (NULL == gv_target) + { + gv_target = malloc(SIZEOF(gv_namehead) + psa->dbc_cs_data->max_key_size); + gv_target->clue.prev = 0; + gv_target->clue.top = psa->first_rec_key->top; + } + /* Copy our key to gv_target->clue since dbc_gv_key is somewhat different */ + gv_target->clue.end = psa->first_rec_key->end; + memcpy(gv_target->clue.base, psa->first_rec_key->base, psa->first_rec_key->end + 1); + /* Figure out collation for this global */ + GET_USHORT(us_rec_len, &((rec_hdr *)blk_set_p->curr_rec)->rsiz); + rec_len = us_rec_len; + rec_value_len = (int)(rec_len - (rec_value_p - blk_set_p->curr_rec)); + if (SIZEOF(block_id) < rec_value_len) + { /* This global potentially has collation data in its record (taken from gvcst_root_search()) */ + subrec_p = get_spec(rec_value_p + SIZEOF(block_id), (int)(rec_value_len - SIZEOF(block_id)), COLL_SPEC); + if (subrec_p) + { + gv_target->nct = *(subrec_p + COLL_NCT_OFFSET); + gv_target->act = *(subrec_p + COLL_ACT_OFFSET); + gv_target->ver = *(subrec_p + COLL_VER_OFFSET); + } else + { + gv_target->nct = 0; + gv_target->act = 0; + gv_target->ver = 0; + } + } else + { + gv_target->nct = 0; + gv_target->act = psa->dbc_cs_data->def_coll; + gv_target->ver = psa->dbc_cs_data->def_coll_ver; + } + /* If there was any collation data involved, make sure the routines are available */ + if (gv_target->act) + { /* Need to setup gv_altkey in case of errors (contains gvn) */ + if (NULL == gv_altkey) + { + gv_altkey = malloc(SIZEOF(gv_key) + psa->dbc_cs_data->max_key_size); + gv_altkey->prev = 0; + gv_altkey->top = psa->first_rec_key->top; + } + gv_altkey->end = psa->first_rec_key->gvn_len + 1; + memcpy(gv_altkey->base, psa->first_rec_key->base, psa->first_rec_key->gvn_len + 1); + act_in_gvt(); + } + assert(gv_target->act || NULL == gv_target->collseq); + /* Format the resulting key into the result buffer which is sized appropriately for this task */ + key_end_p = format_targ_key(psa->rslt_buff, SIZEOF(psa->rslt_buff), &gv_target->clue, TRUE); + *key_end_p = 0; + return psa->rslt_buff; +} + +/* Simple cleanup routine for this scan phaase */ +void dbc_scan_phase_cleanup(void) +{ + /* Cleanup our temporary files */ + if (psa_gbl->tmp_file_names_gend) + { /* only close/delete if we know what they are */ + if (psa_gbl->tcfp) + dbc_close_command_file(psa_gbl); + if (!psa_gbl->keep_temp_files) + dbc_remove_command_file(psa_gbl); + if (psa_gbl->trfp) + dbc_close_result_file(psa_gbl); + if (!psa_gbl->keep_temp_files) + dbc_remove_result_file(psa_gbl); + } +} diff --git a/sr_port/dbfilop.h b/sr_port/dbfilop.h new file mode 100644 index 0000000..179dc7c --- /dev/null +++ b/sr_port/dbfilop.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef DBFILOP_INCLUDED +#define DBFILOP_INCLUDED + +uint4 dbfilop(file_control *fc); + +#endif /* DBFILOP_INCLUDED */ diff --git a/sr_port/ddphdr.h b/sr_port/ddphdr.h new file mode 100644 index 0000000..86a7b1d --- /dev/null +++ b/sr_port/ddphdr.h @@ -0,0 +1,209 @@ +/**************************************************************** + * * + * Copyright 2001, 2003 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef DDPHDR_H_INCLUDED +#define DDPHDR_H_INCLUDED + +typedef int4 condition_code; + +#define MAX_ETHER_DATA_SIZE 1500 +#define ETHERNET_HEADER_SIZE 14 /* Is size of Destination address + source address + protocol # */ +#define ETHERADDR_LENGTH 6 +#define MIN_ETH_RECV_BUFCNT 1 /* from OpenVMS I/O User's Reference Manual, system default */ +#define MAX_ETH_RECV_BUFCNT 255 /* from OpenVMS I/O User's Reference Manual */ +#define ETH_RCV_BUFCNT 64 /* GT.CM DDP default */ +#define DDP_ETH_PROTO_TYPE ((unsigned short)0x3980) /* will be seen as 8039 on the wire */ + +#define DDP_MIN_MSG_LEN 64 +#define DDP_PROTO_VERSION 0x02 /* MSM, DSM-11 use version 2; DSM 6.0 & 6.1, DDP-DOS V1.00.8+ use version 4 */ +#define DDP_MSG_TERMINATOR 0xff + +#define DDP_CIRCUIT_NAME_LEN 3 +#define DDP_VOLUME_NAME_LEN 3 +#define DDP_UCI_NAME_LEN 3 + +/* Request codes */ +#define DDPTR_USEREXIT 0x00 /* [00] */ /* Individual user sends message to agent to clean up for exit. Per DSM doc, + * this code is for an ERROR ON RRB (read request buffer) */ +#define DDPTR_LOCK 0x02 /* [02] */ /* Not implemented */ +#define DDPTR_UNLOCK 0x04 /* [04] */ /* Not implemented */ +#define DDPTR_ZALLOC 0x06 /* [06] */ +#define DDPTR_ZDEALLOC 0x08 /* [08] */ +#define DDPTR_GET 0x0a /* [10] */ +#define DDPTR_PUT 0x0c /* [12] */ +#define DDPTR_KILL 0x0e /* [14] */ +#define DDPTR_ORDER 0x10 /* [16] */ +#define DDPTR_QUERY 0x12 /* [18] */ +#define DDPTR_DEFINE 0x14 /* [20] */ +#define DDPTR_PREVIOUS 0x16 /* [22] */ +#define DDPTR_JOB 0x18 /* [24] */ /* Not implemented */ +#define DDPTR_RESPONSE 0x1a /* [26] */ +#define DDPTR_ERRESPONSE 0x1c /* [28] */ +#define DDPTR_RDRQBUF 0x1e /* [30] */ /* READ REQUEST BUFFER, not implemented */ +#define DDPTR_ANNOUNCE 0x20 /* [32] */ + +/* Response codes other than DDPTR_RESPONSE and DDPTR_ERRESPONSE */ +#define DDPTRX_ANNOUNCE 0x80 /* [128] */ /* Announcement that we are connecting */ +#define DDPTRX_NOCONNECT 0x81 /* [129] */ /* Named volume set not connected */ +#define DDPTRX_CONGESTION 0x82 /* [130] */ /* Agent congestion */ +#define DDPTRX_RUNDOWN 0x83 /* [131] */ /* Client wishes to disconnect */ +#define DDPTRX_SHUTDOWN 0x84 /* [132] */ /* Shutdown the server */ +#define DDPTRX_DUMP 0x85 /* [133] */ /* Start trace */ + +/* GT.M extended reference form is ^|"globaldirectory"|global, DSM is ^["UCI","VOL"]global */ +#define GTM_EXTREF_PREFIX "^|\"" +#define GTM_EXTREF_SUFFIX "\"|" +#define DSM_EXTREF_PREFIX "^[\"" +#define DSM_UCI_VOL_SEPARATOR "\",\"" +#define DSM_EXTREF_SUFFIX "\"]" +#define DSM_EXTREF_FORM_LEN (STR_LIT_LEN(DSM_EXTREF_PREFIX) + \ + DDP_UCI_NAME_LEN + \ + STR_LIT_LEN(DSM_UCI_VOL_SEPARATOR) + \ + DDP_VOLUME_NAME_LEN + \ + STR_LIT_LEN(DSM_EXTREF_SUFFIX)) + +#define DDP_VOLSET_CONF_LOGICAL_PREFIX "GTMDDP_VOLCONF_" +#define DDP_GROUP_LOGICAL_PREFIX "GTMDDP_GROUPS_" +#define DDP_ETHER_DEV_PREFIX "GTMDDP_CONTROLLER_" +#define DDP_MAXRECSIZE_PREFIX "GTMDDP_MAXRECSIZE_" +#define DDP_ETH_RCV_BUFCNT_PREFIX "GTMDDP_ETHRCVBUFCNT_" +#define DDP_MAXREQCREDITS_PREFIX "GTMDDP_MAXREQCREDITS_" +#define DDP_CLIENT_CKTNAM_LOGI "GTMDDP_CIRCUIT_NAME" + +#define DDP_MAX_VOLSETS 16 +#define DDP_TEXT_ID_LENGTH 3 +#define DDP_ANNOUNCE_CODE_LEN 2 /* "WI" or "II" */ +#define MAXIMUM_PROCESSES 256 +#define DDP_MAX_GROUP 16 +#define DDP_DEFAULT_GROUP_MASK 0x0001 /* member of group 0 */ +#define DDP_MIN_RECSIZE 1024 +#define DDP_LEAST_MAXREQCREDITS 1 +#define DDP_LARGEST_MAXREQCREDITS 0xFF + +/* some constants that were derived from DSM packets */ +#define DDP_GROUP_MASK 0x0101 +#define DDP_ADVERTISE_INTERVAL 0x00 +#define DDP_MAX_REQUEST_CREDITS 0x04 +#define DDP_CPU_TYPE 0x01 +#define DDP_SOFTWARE_VERSION 0x00 +#define DDP_CPU_LOAD_RATING 0x00 +#define DDP_AUTOCONFIGURE_VERSION 0x02 +#define DDP_ANNOUNCE_FILLER3_LITERAL 0x01ff00ff +#define DDP_GLOBAL_TYPE 0x02 + +/* DDP node status bit masks */ +#define DDP_NODE_STATUS_READ 0x01 /* bit is 1 if read locked, 0 if read enabled */ +#define DDP_NODE_STATUS_WRITE 0x02 /* bit is 1 if write locked, 0 if write enabled */ +#define DDP_NODE_STATUS_STATE 0x04 /* bit is 1 if unreachable, 0 if reachable */ +#define DDP_NODE_STATUS_CHANGE 0x10 /* bit is 1 if state change occurred on circuit */ +#define DDP_NODE_STATUS_DISABLED 0x20 /* bit is 1 if disabled, 0 if enabled */ + +#define DDP_NODE_STATUS_ALL_CLEAR 0x00 /* read enabled + write enabled + reachable + no state change + circuit enabled */ + +#define DDP_LOG_ERROR(err_len, err_string) \ +{ \ + now_t now; /* for GET_CUR_TIME macro */ \ + char time_str[CTIME_BEFORE_NL + 2]; /* for GET_CUR_TIME macro*/ \ + char *time_ptr; /* for GET_CUR_TIME macro*/ \ + bool save_dec_nofac; \ + \ + GET_CUR_TIME; \ + save_dec_nofac = dec_nofac; /* save for later restore */ \ + dec_nofac = TRUE; /* don't need error mnemonic prefix, just print the message contents */ \ + dec_err(VARLSTCNT(6) ERR_DDPLOGERR, 4, CTIME_BEFORE_NL, time_ptr, (err_len), (err_string)); \ + dec_nofac = save_dec_nofac; /* back to what it was */ \ +} + +/* All structures that are DDP messages should be packed; do not let the compiler pad for structure member alignment */ +#if defined(__alpha) +# pragma member_alignment save +# pragma nomember_alignment +#endif + +typedef struct +{ + unsigned short uci; + unsigned short volset; +} ddp_info; + +typedef struct +{ + unsigned char trancode; + unsigned char proto; + unsigned short source_circuit_name; /* 5-bit format */ + unsigned short source_job_number; + unsigned short remote_circuit_name; /* 5-bit format */ + unsigned short remote_job_number; /* 0000 if this is a request */ + unsigned char message_number; + unsigned char filler1; /* literal 00 */ + unsigned short message_length; + unsigned char hdrlen; + unsigned char txt[1]; +} ddp_hdr_t; + +#define DDP_MSG_HDRLEN 0x0f /* excluding txt field of ddp_hdr_t */ + +struct frame_hdr +{ + unsigned short frame_length; + unsigned char destination_address[ETHERADDR_LENGTH]; + unsigned char source_address[ETHERADDR_LENGTH]; + unsigned char protocol_type[2]; +}; + +struct in_buffer_struct +{ + struct frame_hdr fh; + ddp_hdr_t dh; +}; + +typedef struct +{ /* byte position in announce packet - 15 is the first position past the header, position starts from 0, header size is 15 bytes */ + unsigned short filler0; /* position 15: literal 0x0000 */ + unsigned char code[DDP_ANNOUNCE_CODE_LEN]; /* position 17: "WI", or "II" */ + unsigned char ether_addr[ETHERADDR_LENGTH]; /* position 19: Ethernet physical address for this node */ + unsigned short circuit_name; /* position 25: DDP Node (circuit) name */ + unsigned short filler1; /* position 27: reserved for possible name extension */ + unsigned short filler2; /* position 29: reserved for possible name extension */ + unsigned short max_job_no; /* position 31: max job # */ + unsigned short group_mask; /* position 33: DDP group number mask */ + unsigned char advertise_interval; /* position 35: Advertise interval in seconds */ + unsigned char max_request_credits; /* position 36: Maximum request credits */ + unsigned char cpu_type; /* position 37: Remote CPU type */ + unsigned char version; /* position 38: Version of software */ + unsigned char cpu_load_rating; /* position 39: CPU load rating */ + unsigned char proto_version; /* position 40: DDP protocol version in use */ + unsigned char node_status; /* position 41: see comments re: DDP_NODE_STATUS_* */ + unsigned char autoconfigure_version; /* position 42: DDP autoconfigure version */ + unsigned short volset[DDP_MAX_VOLSETS]; /* position 43: 5 bit formal volset names */ + /******************************************* begin unknown **************************************************/ + unsigned char filler3[32 + 4 + 8]; /* position 75: at position 107 we write literal 0x01ff00ff */ + /******************************************* end unknown **************************************************/ + unsigned char terminator; /* position 119: DDP_MSG_TERMINATOR */ +} ddp_announce_msg_t; + +#define DDP_ANNOUNCE_MSG_LEN 105 /* from ddp_announce_msg_t */ + +typedef struct +{ + unsigned char naked_size; + unsigned short uci; + unsigned short vol; + unsigned char global_type; + unsigned char global_len; + unsigned char global[1]; /* actually, global_len bytes of formatted global reference */ +} ddp_global_request_t; /* immediately follows the message header (ddp_hdr) for global request. For SET, ASCII value follows */ + +#if defined(__alpha) +# pragma member_alignment restore +#endif + +#endif /* DDPHDR_H_INCLUDED */ diff --git a/sr_port/deferred_events.c b/sr_port/deferred_events.c new file mode 100644 index 0000000..7fa5b13 --- /dev/null +++ b/sr_port/deferred_events.c @@ -0,0 +1,518 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#ifdef VMS +#include "efn.h" +#include +#endif + +#include "xfer_enum.h" +#include "tp_timeout.h" +#include "deferred_events.h" +#include "outofband.h" +#include "interlock.h" +#include "lockconst.h" +#include "add_inter.h" +#include "op.h" +#include "iott_wrterr.h" + +#ifdef DEBUG_DEFERRED +#include "gtm_stdio.h" +#endif + +#include "fix_xfer_entry.h" + +/* ============================================================================= + * EXTERNAL VARIABLES + * ============================================================================= + */ + +/* The transfer table */ +GBLREF xfer_entry_t xfer_table[]; + +/* M Profiling active */ +GBLREF boolean_t is_tracing_on; + +/* Marks sensitive database operations */ +GBLREF volatile int4 fast_lock_count; + +#if defined(VMS) +GBLREF volatile short num_deferred; +#else +GBLREF volatile int4 num_deferred; +#endif +GBLREF volatile int4 ctrap_action_is, outofband; + +/* ============================================================================= + * FILE-SCOPE VARIABLES + * ============================================================================= + */ + +/* ------------------------------------------------------------------ + * Declared volatile because accesses occur both in main thread + * and (possibly multiple) interrupts levels. + * ------------------------------------------------------------------ + */ + +/* Holds count of events logged of each type. + * Cleared when table is reset. + * Location zero (== no_event) is not used. + */ + +/* INCR_CNT on VMS doesn't return the post-incremented value as is the + * case in Unix. But we don't need an interlocked add in VMS since we + * should be in an AST and AST's can't be nested (we assert to that effect + * in xfer_set_handlers). The macro INCR_CNT_SP accomplishes this task for us. + */ + +#if defined(UNIX) +volatile int4 xfer_table_events[DEFERRED_EVENTS]; +#define INCR_CNT_SP(X,Y) INCR_CNT(X,Y) +#elif defined(VMS) +volatile short xfer_table_events[DEFERRED_EVENTS]; +#define INCR_CNT_SP(X,Y) (++*X) +#else +# error "Unsupported Platform" +#endif +GBLREF global_latch_t defer_latch; + +/* ------------------------------------------------------- + * Act only on first recieved. + * ------------------------------------------------------- + */ +GBLDEF volatile int4 first_event = no_event; + +error_def(ERR_DEFEREVENT); + +/* ============================================================================= + * EXPORTED FUNCTIONS + * ============================================================================= + */ +/* ------------------------------------------------------------------ + * *** INTERRUPT HANDLER *** + * Sets up transfer table changes needed for: + * - Synchronous handling of asynchronous events. + * - Single-stepping and breakpoints + * Note: + * - Call here from a routine specific to each event type. + * - Pass in a single value to pass on to xfer_table set function + * for that type. Calling routine should record any other event + * info, if needed, in volatile global variables. + * - If this is first event logged, will call back to the function + * provided and pass along parameter. + * Future: + * - mdb_condition_handler does not call here -- should change it. + * - Ditto for routines related to zbreak and zstep. + * - Should put handler prototypes in a header file & include it here, + * if can use with some way to ensure type checking. + * - A higher-level interface (e.g. change sets) might be better. + * ------------------------------------------------------------------ + */ +boolean_t xfer_set_handlers(int4 event_type, void (*set_fn)(int4 param), int4 param_val) +{ + boolean_t is_first_event = FALSE; + + /* ------------------------------------------------------------ + * Keep track of what event types have come in. + * - Get and set value atomically in case of concurrent + * events and/or resetting while setting. + * ------------------------------------------------------------------ + * Use interlocked operations to prevent races between set and reset, + * and to avoid missing overlapping sets. + * On HP: + * OK only if there's no a risk a conflicting operation is + * in progress (can deadlock). + * On all platforms: + * Don't want I/O from a sensitive area. + * Avoid both by testing fast_lock_count, and doing interlocks and + * I/O only if it is non-zero. Can't be resetting then, so worst + * risk is missing an event when there's already one happening. + * ------------------------------------------------------------------ + */ + + VMS_ONLY(assert(lib$ast_in_prog())); + if (fast_lock_count == 0) + { +#ifdef DEBUG_DEFERRED + (void) FPRINTF(stderr, + "\nBefore interlocked operations: " + "xfer_table_events[%d]=%d, " + "first_event=%s, " + "num_deferred=%d\n", + event_type, + xfer_table_events[event_type], + (is_first_event?"TRUE":"FALSE"), + num_deferred); +#endif + if (1 == INCR_CNT_SP(&xfer_table_events[event_type], &defer_latch)) + { + /* Concurrent events can collide here, too */ + is_first_event = (1 == INCR_CNT_SP(&num_deferred, &defer_latch)); + } + +#ifdef DEBUG_DEFERRED + (void) FPRINTF(stderr, + "\nAfter interlocked operations: " + "xfer_table_events[%d]=%d, " + "first_event=%s, " + "num_deferred=%d\n", + event_type,xfer_table_events[event_type], + (is_first_event?"TRUE":"FALSE"), + num_deferred); +#endif + } else if (1 == ++xfer_table_events[event_type]) + is_first_event = (1 == ++num_deferred); + if (is_first_event) + { + first_event = event_type; + +#ifdef DEBUG_DEFERRED + if (0 != fast_lock_count) + { + (void) FPRINTF(stderr, + "Setting xfer_table for event type %d.\n", + event_type); + } +#endif + /* ------------------------------------------------------- + * If table changed, it was not synchronized. + * (Assumes these entries are all that would be changed) + * Note asserts bypassed for Itanium due to nature of the + * fixed up addresses making direct comparisions non-trivial. + * -------------------------------------------------------- + */ +#ifndef __ia64 + assert((xfer_table[xf_linefetch] == op_linefetch) || + (xfer_table[xf_linefetch] == op_zstepfetch) || + (xfer_table[xf_linefetch] == op_zst_fet_over) || + (xfer_table[xf_linefetch] == op_mproflinefetch)); + assert((xfer_table[xf_linestart] == op_linestart) || + (xfer_table[xf_linestart] == op_zstepstart) || + (xfer_table[xf_linestart] == op_zst_st_over) || + (xfer_table[xf_linestart] == op_mproflinestart)); + assert((xfer_table[xf_zbfetch] == op_zbfetch) || + (xfer_table[xf_zbfetch] == op_zstzb_fet_over) || + (xfer_table[xf_zbfetch] == op_zstzbfetch)); + assert((xfer_table[xf_zbstart] == op_zbstart) || + (xfer_table[xf_zbstart] == op_zstzb_st_over) || + (xfer_table[xf_zbstart] == op_zstzbstart)); + assert((xfer_table[xf_forchk1] == op_forchk1) || + (xfer_table[xf_forchk1] == op_mprofforchk1)); + assert((xfer_table[xf_forloop] == op_forloop)); + assert(xfer_table[xf_ret] == opp_ret || + xfer_table[xf_ret] == opp_zst_over_ret || + xfer_table[xf_ret] == opp_zstepret); + assert(xfer_table[xf_retarg] == op_retarg || + xfer_table[xf_retarg] == opp_zst_over_retarg || + xfer_table[xf_retarg] == opp_zstepretarg); +#endif /* !ia64 */ + /* ----------------------------------------------- + * Now call the specified set function to swap in + * the desired handlers (and set flags or whatever). + * ----------------------------------------------- + */ + set_fn(param_val); + } +#ifdef DEBUG_DEFERRED + else if (0 != fast_lock_count) + { + (void) FPRINTF(stderr, + "\n---Multiple deferred events---\n" + "Event type %d occurred while type %d was pending\n", + event_type, + first_event); + } +#endif + assert(no_event != first_event); + return is_first_event; +} + +/* ------------------------------------------------------------------ + * Reset the transfer table only if current event type + * - Needed for abort before action, e.g., a tp timeout + * that happened just before commit was too late to be aborted and + * so must be cleared. In a case like this, reset should happen + * only if the event type attempting the clear is the type + * that caused the change. + * - Other resets should be unconditional, in case there are + * unidentified control paths (e.g. exit paths) that cause + * resets when they did not set. + * ------------------------------------------------------------------ + */ +boolean_t xfer_reset_if_setter(int4 event_type) +{ + if (event_type == first_event) + { + /* Still have to synchronize the same way... */ + if (TRUE == xfer_reset_handlers(event_type)) + { /* ********************************* + * Check for and activate any + * other pending events before + * returning success: + * ********************************* + */ + /* (Not implemented) */ + return TRUE; + } else + { /* --------------------------------- + * Would require interleaved resets + * to get here, e.g. due to + * rts_error from interrupt level. + * --------------------------------- + */ + assert(FALSE); + return FALSE; + } + } else + return FALSE; +} + +/* ------------------------------------------------------------------ + * Reset transfer table to normal settings. + * + * - Intent: Put back all state that was or could have been changed + * due to prior deferral(s). + * - Would be easier to implement this assumption if this routine + * were changed to delegate responsibility as does the + * corresponding set routine. + * - Note that all events are reenabled before user's handler + * would be executed (assuming one is appropriate for this event + * and has been specified) + * => It's possible to have handler-in-handler execution. + * => If no handler executed, would lose other deferred events due + * to reset of all pending. + * - If M profiling is active, some entries should be set to the + * op_mprof* routines. + * - Return value indicates whether reset type matches set type. + * If it does not, this indicates an "abnormal" path. + * - Should still reset the table in this case. + * - BUT: Consider also calling a reset routine for all setters + * that have been logged, to allow them to reset themselves, + * (for example, to reset TP timer & flags, or anything else + * that could cause unintended effects if left set after + * deferred events have been cleared). + * - May need to update behavior to ensure it doesn't miss a + * critical event between registration of first event + * and clearing of all events. This seems problematic only if + * the following are true: + * - Two events are deferred at one time (call them A and B). + * - An M exception handler (ZTRAP or device) is required to + * execute due to B and perform action X. + * - Either no handler executes due to A, or the handler that + * does execute does not perform action X in response to B + * (this includes the possibility of performing X but not + * as needed by B, e.g. perhaps it should happen for both + * A and B but only happens for A). + * Seems like most or all of these can be addressed by carefully + * specifying coding requirements on M handlers. + * ------------------------------------------------------------------ + */ +boolean_t xfer_reset_handlers(int4 event_type) +{ + int4 e_type; + boolean_t reset_type_is_set_type; + int4 status; + int e, ei, e_tot=0; + + + /* ------------------------------------------------------------------ + * Note: If reset routine can preempt path from handler to + * set routine (e.g. clearing event before acting on it), + * these assertions can fail. + * Should not happen in current design. + * ------------------------------------------------------------------ + */ + assert(0 < num_deferred); + assert(0 < xfer_table_events[event_type]); + + if (is_tracing_on) + { + FIX_XFER_ENTRY(xf_linefetch, op_mproflinefetch); + FIX_XFER_ENTRY(xf_linestart, op_mproflinestart); + FIX_XFER_ENTRY(xf_forchk1, op_mprofforchk1); + } else + { + FIX_XFER_ENTRY(xf_linefetch, op_linefetch); + FIX_XFER_ENTRY(xf_linestart, op_linestart); + FIX_XFER_ENTRY(xf_forchk1, op_forchk1); + } + FIX_XFER_ENTRY(xf_forloop, op_forloop); + FIX_XFER_ENTRY(xf_zbfetch, op_zbfetch); + FIX_XFER_ENTRY(xf_zbstart, op_zbstart); + FIX_XFER_ENTRY(xf_ret, opp_ret); + FIX_XFER_ENTRY(xf_retarg, op_retarg); + +#ifdef DEBUG_DEFERRED + (void) FPRINTF(stderr,"Reset xfer_table for event type %d.\n",event_type); +#endif + + reset_type_is_set_type = (event_type == first_event); + +#ifdef DEBUG + if (!reset_type_is_set_type) + { + rts_error(VARLSTCNT(4) ERR_DEFEREVENT, + 2, + event_type, + first_event); + } +#endif + +#ifdef DEBUG_DEFERRED +/*--------------------------------------------=--------------------------------- + * Note: concurrent modification of array elements means events that occur + * during this section will cause inconsistent totals. + *----------------------------------------------------------------------------- + */ + for (ei=no_event; ei1) + { + (void) FPRINTF(stderr,"Event Log:\n"); + for (ei=no_event; ei e_type; e_type++) + { + xfer_table_events[e_type] = 1; + } + + /* ------------------------------------------------------------------------- + * Reset external event modules that need it. + * (Should do this in a more modular fashion.) + * None + * ------------------------------------------------------------------------- + */ + + /* -------------------------------------------- + * Reset private variables. + * -------------------------------------------- + */ + first_event = no_event; + num_deferred = 0; + ctrap_action_is = 0; + outofband = 0; + VMS_ONLY( + status = sys$clref(efn_outofband); + assert(SS$_WASSET == status); + if ((SS$_WASSET != status) && (SS$_WASCLR != status)) + GTMASSERT; + ) + /* ****************************************************************** + * There is a race here: + * If a new event interrupts after previous line and before + * corresponding assignment in next loop, it will be missed. + * For most events, we're going to an M handler anyway, so it won't + * matter (assuming the handler would handle all pending events). + * But if not going to an M handler (e.g. if resetting zbreak/zstep), + * could miss another event. + * + * Better (to avoid missing any events): + * aswp xfer_table_events elements (as described above), and + * check here if still zero. If not, must have missed that event + * since aswp, possibly before num_deferred was reset => never set + * xfer_table => should do that now. + * If more than one is nonzero, choose first arbitrarily + * unless first_event is now set -- unless it is, we've lost track of + * which event was first. + * ****************************************************************** + */ + /* Clear to allow new events to be reset only after we're all done. */ + for (e_type = 1; DEFERRED_EVENTS > e_type; e_type++) + { + xfer_table_events[e_type] = FALSE; + } + return reset_type_is_set_type; +} + +/* ------------------------------------------------------------------ + * Perform action corresponding to the first async event that + * was logged. + * ------------------------------------------------------------------ + */ +void async_action(bool lnfetch_or_start) +{ + /* Double-check that we should be here: */ + assert(0 < num_deferred); + + switch(first_event) + { + case (outofband_event): + if (0 == outofband) + { /* This function can be invoked only by a op_*intrrpt* transfer table function. Those transfer table + * functions should be active only for a short duration between the occurrence of an outofband event + * and the handling of it at a logical boundary (next M-line). We dont expect to be running with + * those transfer table functions for more than one M-line. If "outofband" is set to 0, the call to + * "outofband_action" below will do nothing and we will end up running with the op_*intrrpt* transfer + * table functions indefinitely. In this case M-FOR loops are known to return incorrect results which + * might lead to application integrity issues. It is therefore considered safer to GTMASSERT as we + * will at least have the core for analysis. + */ + GTMASSERT; + } + outofband_action(lnfetch_or_start); + break; + case (tt_write_error_event): + UNIX_ONLY( + xfer_reset_if_setter(tt_write_error_event); + iott_wrterr(); + ) + /* VMS tt error processing is done in op_*intrrpt */ + break; + case (network_error_event): + /* ------------------------------------------------------- + * Network error not implemented here yet. Need to move + * from mdb_condition_handler after review. + * ------------------------------------------------------- + */ + case (zstp_or_zbrk_event): + /* ------------------------------------------------------- + * ZStep/Zbreak events not implemented here yet. Need to + * move here after review. + * ------------------------------------------------------- + */ + default: + GTMASSERT; /* see above GTMASSERT for comment as to why this is needed */ + } +} + + +/* ------------------------------------------------------------------ + * Indicate whether an xfer_table change is pending. + * Only works for changes made using routines in this module + * (need to rework others, too). + * + * Might not be needed. + * ------------------------------------------------------------------ + */ +boolean_t xfer_table_changed(void) +{ + return (0 != num_deferred); +} diff --git a/sr_port/deferred_events.h b/sr_port/deferred_events.h new file mode 100644 index 0000000..79bed3f --- /dev/null +++ b/sr_port/deferred_events.h @@ -0,0 +1,108 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* -------------------------------------------- + * Async. events that can be deferred + * -------------------------------------------- + */ +enum deferred_event +{ + no_event = 0, + outofband_event, + network_error_event, + zstp_or_zbrk_event, + tt_write_error_event, + DEFERRED_EVENTS +}; + +/* ============================================================================= + * EXPORTED VARIABLES + * ============================================================================= + */ + +/* ------------------------------------------------------- + * - Used by the substitute xfer_table functions. + * - Keeping_count => should change name to num_logged. + * - Should be made static and wrapped with a function for weaker + * intermodule coupling; however, it's accessed from assembly + * language => not worth the trouble to add a layer. + * ------------------------------------------------------- + */ +#if defined(UNIX) +GBLREF volatile int4 num_deferred; +#elif defined(VMS) +GBLREF volatile short num_deferred; +#else +# error "Unsupported Platform" +#endif + + +/* ------------------------------------------------------------------ + * Sets up transfer table changes needed for: + * - Synchronous handling of asynchronous events. + * - Single-stepping and breakpoints + * Return value indicates success (e.g. if first to attempt). + * + * Notes: + * - mdb_condition_handler is different. + * Should change it to use this function (CAREFULLY!). + * - So are routines related to zbreak and zstep. + * ==> Need to update them too (also carefully -- needs + * a thorough redesign or rethinking). + * ------------------------------------------------------------------ + */ +boolean_t xfer_set_handlers(int4, void (*callback)(int4), int4 param); + +/* ------------------------------------------------------------------ + * Reset transfer table to normal settings. + * + * Puts back everything that could/would have been changed, assuming + * that no xfer_table changes have been added since it was written. + * + * Return value indicates success/failure. Succeeds only if event + * type of reset is the same as event type of set. + * ------------------------------------------------------------------ + */ +boolean_t xfer_reset_handlers(int4 event_type); + +/* ------------------------------------------------------------------ + * This version resets the handlers only if they were set by the + * same event type. + * ------------------------------------------------------------------ + */ +boolean_t xfer_reset_if_setter(int4 event_type); + +/* ------------------------------------------------------- + * Prototypes for transfer table callback functions. + * Only called by routine that manages xfer_table. + * + * Should use these (with enums?) to ensure type checking + * is done. + * ------------------------------------------------------- + */ + +void ctrap_set(int4); + +void ctrlc_set(int4); + +void ctrly_set(int4); + +void tp_timeout_defer(int4); + +void tt_write_error_set(int4); + +/* ------------------------------------------------------------------ + * Perform action corresponding to the first async event that + * was logged. + * ------------------------------------------------------------------ + */ +void async_action(bool); +boolean_t xfer_table_changed(void); diff --git a/sr_port/desired_db_format_set.c b/sr_port/desired_db_format_set.c new file mode 100644 index 0000000..ead191c --- /dev/null +++ b/sr_port/desired_db_format_set.c @@ -0,0 +1,190 @@ +/**************************************************************** + * * + * Copyright 2005, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_time.h" +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gdscc.h" +#include "gdskill.h" +#include "jnl.h" +#include "iosp.h" /* for SS_NORMAL */ +#include "util.h" + +/* Prototypes */ +#include "gtmmsg.h" /* for gtm_putmsg prototype */ +#include "desired_db_format_set.h" +#include "send_msg.h" /* for send_msg */ +#include "wcs_phase2_commit_wait.h" + +#define WCS_PHASE2_COMMIT_WAIT_LIT "wcb_phase2_commit_wait" + +LITREF char *gtm_dbversion_table[]; + +GBLREF inctn_opcode_t inctn_opcode; +GBLREF jnl_gbls_t jgbl; +GBLREF uint4 process_id; +GBLREF inctn_detail_t inctn_detail; /* holds detail to fill in to inctn jnl record */ + +/* input parameter "command_name" is a string that is either "MUPIP REORG UPGRADE/DOWNGRADE" or "MUPIP SET VERSION" */ +int4 desired_db_format_set(gd_region *reg, enum db_ver new_db_format, char *command_name) +{ + boolean_t was_crit; + char *db_fmt_str; + char *wcblocked_ptr; + int4 status; + uint4 jnl_status; + inctn_opcode_t save_inctn_opcode; + sgmnt_addrs *csa; + sgmnt_data_ptr_t csd; + trans_num curr_tn; + jnl_private_control *jpc; + jnl_buffer_ptr_t jbp; + + error_def(ERR_COMMITWAITSTUCK); + error_def(ERR_DBDSRDFMTCHNG); + error_def(ERR_MMNODYNDWNGRD); + error_def(ERR_MUDWNGRDTN); + error_def(ERR_MUNOACTION); + error_def(ERR_WCBLOCKED); + error_def(ERR_CRYPTNOV4); + error_def(ERR_SNAPSHOTNOV4); + assert(reg->open); + csa = &FILE_INFO(reg)->s_addrs; + csd = csa->hdr; + GTMCRYPT_ONLY( + /* We don't allow databases to be encrypted if the version is V4 */ + if (csd->is_encrypted && (GDSV4 == new_db_format)) + { + gtm_putmsg(VARLSTCNT(4) ERR_CRYPTNOV4, 2, DB_LEN_STR(reg)); + return ERR_CRYPTNOV4; + } + ) + GTM_SNAPSHOT_ONLY( + /* We don't allow databases to be downgraded when snapshots are in progress */ + if (SNAPSHOTS_IN_PROG(csa->nl) && (GDSV4 == new_db_format)) + { + gtm_putmsg(VARLSTCNT(5) ERR_SNAPSHOTNOV4, 3, csa->nl->num_snapshots_in_effect, DB_LEN_STR(reg)); + return ERR_SNAPSHOTNOV4; + } + ) + was_crit = csa->now_crit; + if (FALSE == was_crit) + grab_crit(reg); + /* if MM and desired_db_format is not V5, gvcst_init would have issued MMNODYNDWNGRD error. assert that. */ + assert(dba_bg == csd->acc_meth || (dba_mm == csd->acc_meth) && (GDSV5 == csd->desired_db_format)); + if (csd->desired_db_format == new_db_format) + { /* no change in db_format. fix max_tn_warn if necessary and return right away. */ + status = ERR_MUNOACTION; + assert(csd->trans_hist.curr_tn <= csd->max_tn); + if ((GDSV4 == new_db_format) && (MAX_TN_V4 < csd->max_tn)) + { /* reset max_tn to MAX_TN_V4 only if V4 format and the new value will still be greater than curr_tn */ + if (MAX_TN_V4 >= csd->trans_hist.curr_tn) + { + csd->max_tn = MAX_TN_V4; + /* since max_tn changed above, max_tn_warn might also need to correspondingly change */ + SET_TN_WARN(csd, csd->max_tn_warn); + } else + GTMASSERT; /* out-of-design state where curr_tn > MAX_TN_V4 in GDSV4 */ + } + if (FALSE == was_crit) + rel_crit(reg); + return status; + } + if (dba_mm == csd->acc_meth) + { + status = ERR_MMNODYNDWNGRD; + gtm_putmsg(VARLSTCNT(4) status, 2, REG_LEN_STR(reg)); + if (FALSE == was_crit) + rel_crit(reg); + return status; + } + /* check if curr_tn is too high to downgrade */ + curr_tn = csd->trans_hist.curr_tn; + if ((GDSV4 == new_db_format) && (MAX_TN_V4 <= curr_tn)) + { + status = ERR_MUDWNGRDTN; + gtm_putmsg(VARLSTCNT(5) status, 3, &curr_tn, DB_LEN_STR(reg)); + if (FALSE == was_crit) + rel_crit(reg); + return status; + } + /* Wait for concurrent phase2 commits to complete before switching the desired db format */ + if (csa->nl->wcs_phase2_commit_pidcnt && !wcs_phase2_commit_wait(csa, NULL)) + { /* Set wc_blocked so next process to get crit will trigger cache-recovery */ + SET_TRACEABLE_VAR(csd->wc_blocked, TRUE); + wcblocked_ptr = WCS_PHASE2_COMMIT_WAIT_LIT; + send_msg(VARLSTCNT(8) ERR_WCBLOCKED, 6, LEN_AND_STR(wcblocked_ptr), + process_id, &csd->trans_hist.curr_tn, DB_LEN_STR(reg)); + status = ERR_COMMITWAITSTUCK; + gtm_putmsg(VARLSTCNT(7) status, 5, process_id, 1, csa->nl->wcs_phase2_commit_pidcnt, DB_LEN_STR(reg)); + if (FALSE == was_crit) + rel_crit(reg); + return status; + } + if (JNL_ENABLED(csd)) + { + SET_GBL_JREC_TIME; /* needed for jnl_ensure_open, jnl_put_jrt_pini and jnl_write_aimg_rec */ + jpc = csa->jnl; + jbp = jpc->jnl_buff; + /* Before writing to jnlfile, adjust jgbl.gbl_jrec_time if needed to maintain time order of jnl records. + * This needs to be done BEFORE the jnl_ensure_open as that could write journal records + * (if it decides to switch to a new journal file) + */ + ADJUST_GBL_JREC_TIME(jgbl, jbp); + jnl_status = jnl_ensure_open(); + if (0 == jnl_status) + { + save_inctn_opcode = inctn_opcode; + inctn_opcode = inctn_db_format_change; + inctn_detail.blks2upgrd_struct.blks_to_upgrd_delta = csd->blks_to_upgrd; + if (0 == jpc->pini_addr) + jnl_put_jrt_pini(csa); + jnl_write_inctn_rec(csa); + inctn_opcode = save_inctn_opcode; + } else + gtm_putmsg(VARLSTCNT(6) jnl_status, 4, JNL_LEN_STR(csd), DB_LEN_STR(reg)); + } + csd->desired_db_format = new_db_format; + csd->fully_upgraded = FALSE; + csd->desired_db_format_tn = curr_tn; + switch(new_db_format) + { + case GDSV4: + csd->max_tn = MAX_TN_V4; + break; + case GDSV5: + csd->max_tn = MAX_TN_V5; + break; + default: + GTMASSERT; + } + SET_TN_WARN(csd, csd->max_tn_warn); /* if max_tn changed above, max_tn_warn also needs a corresponding change */ + assert(curr_tn < csd->max_tn); /* ensure CHECK_TN macro below will not issue TNTOOLARGE rts_error */ + CHECK_TN(csa, csd, curr_tn); /* can issue rts_error TNTOOLARGE */ + /* increment csd->trans_hist.curr_tn */ + assert(csd->trans_hist.early_tn == csd->trans_hist.curr_tn); + csd->trans_hist.early_tn = csd->trans_hist.curr_tn + 1; + INCREMENT_CURR_TN(csd); + if (FALSE == was_crit) + rel_crit(reg); + status = SS_NORMAL; + send_msg(VARLSTCNT(11) ERR_DBDSRDFMTCHNG, 9, DB_LEN_STR(reg), LEN_AND_STR(gtm_dbversion_table[new_db_format]), + LEN_AND_STR(command_name), process_id, process_id, &curr_tn); + return status; +} diff --git a/sr_port/desired_db_format_set.h b/sr_port/desired_db_format_set.h new file mode 100644 index 0000000..b2a5c6f --- /dev/null +++ b/sr_port/desired_db_format_set.h @@ -0,0 +1,20 @@ +/**************************************************************** + * * + * Copyright 2005 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef DESIRED_DB_FORMAT_SET_DEFINED + +/* prototypes */ + +int4 desired_db_format_set(gd_region *reg, enum db_ver new_db_format, char *command_name); + +#define DESIRED_DB_FORMAT_SET_DEFINED + +#endif diff --git a/sr_port/deviceparameters.c b/sr_port/deviceparameters.c new file mode 100644 index 0000000..447102b --- /dev/null +++ b/sr_port/deviceparameters.c @@ -0,0 +1,634 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "nametabtyp.h" +#include "io_params.h" +#include "zshow_params.h" +#include "advancewindow.h" +#include "namelook.h" +#include "cvtparm.h" +#include "deviceparameters.h" + +GBLREF char window_token; +GBLREF mident window_ident; +GBLREF triple *curtchain; + +error_def(ERR_RPARENMISSING); +error_def(ERR_DEVPARUNK); +error_def(ERR_DEVPARINAP); +error_def(ERR_DEVPARVALREQ); + +LITREF unsigned char io_params_size[]; +LITREF dev_ctl_struct dev_param_control[]; +LITDEF nametabent dev_param_names[] = +{ + {2,"AF*"} + ,{2,"AL*"} ,{4,"ALLO"} + ,{2,"AP*"} + ,{2,"AT*"} + + ,{4,"BIGR*"} + ,{2,"BL*"} ,{4,"BLOC"} + ,{4,"BU*"} + + ,{2,"CA"}, {4,"CANT*"} + ,{4,"CANO*"} + ,{2,"CE*"} + ,{3,"CHA*"} + ,{3,"CHS*"} + ,{3,"CLE*"} + ,{3,"CLI"} + ,{4,"COMM*"}, {7,"COMMAND"} + ,{4,"CONN*"} + ,{4,"CONT*"} + ,{4,"CONV*"} + ,{3,"COP*"} + ,{2,"CP*"} + ,{2,"CT*"} ,{4,"CTRA"} + + ,{4,"DELE*"} ,{4,"DELE"} + ,{4,"DELI*"} + ,{3,"DET*"} + ,{3,"DOU*"} + ,{3,"DOW*"} + + ,{2,"EB*"} ,{4,"EBCD"} + ,{2,"EC*"} + ,{2,"ED*"} ,{4,"EDIT"} + ,{6,"ERASEL*"} + ,{6,"ERASET*"} + ,{2,"ES*"} + ,{3,"EXC*"} ,{4,"EXCE"} + ,{3,"EXT*"} ,{4,"EXTE"} + ,{4,"EXTG*"} + + ,{1,"F"} ,{3,"FIE*"} ,{5,"FIELD"} + ,{3,"FIF*"} ,{4,"FIFO"} + ,{3,"FIL*"} + ,{3,"FIR*"} + ,{3,"FIX*"} ,{5,"FIXED"} + ,{3,"FLA*"} + ,{3,"FLU*"} + ,{2,"FO*"} + + ,{1,"G"} ,{2,"GR*"} + + ,{2,"HE*"} + ,{3,"HOL*"} + ,{3,"HOS*"} ,{4,"HOST"} + + ,{6,"ICHSET"} + ,{4,"INDE*"} ,{11,"INDEPENDENT"} + ,{2,"IN*"} ,{4,"INSE"} + ,{3,"IOE*"} + + ,{3,"LAB*"} + ,{3,"LAS*"} + ,{2,"LE*"} ,{4,"LENG"} + ,{2,"LI*"} + ,{4,"LOGF*"} + ,{4,"LOGQ*"} + ,{3,"LOW*"} + + ,{1,"M"} + ,{8,"MOREREAD*"} + ,{3,"MOU*"} + + ,{2,"NA*"} + ,{3,"NEW*"} + ,{3,"NEX*"} + ,{6,"NOBIGR*"} + ,{4,"NOBU*"} + ,{4,"NOCA*"} + ,{4,"NOCE*"} ,{6,"NOCENE"} + ,{6,"NOCONV*"} + ,{6,"NODELI*"} + ,{5,"NODOU*"} + ,{4,"NOEB*"} + ,{4,"NOEC*"} ,{6,"NOECHO"} + ,{4,"NOED*"} ,{6,"NOEDIT"} + ,{4,"NOES*"} ,{6,"NOESCA"} + ,{5,"NOEXT*"} + ,{5,"NOFIL*"} + ,{5,"NOFIX*"} + ,{5,"NOFLA*"} + ,{4,"NOHE*"} + ,{5,"NOHOL*"} + ,{5,"NOHOS*"} ,{6,"NOHOST"} + ,{4,"NOIN*"} ,{6,"NOINSE"} + ,{5,"NOLAB*"} + ,{5,"NOLOW*"} + ,{6,"NONOTI*"} + ,{5,"NOPAG*"} + ,{6,"NOPASS*"} + ,{6,"NOPAST*"} + ,{6,"NOPRIN*"} + ,{4,"NORC*"} + ,{6,"NOREAD"} ,{7,"NOREADO*"} + ,{7,"NOREADS*"} + ,{5,"NORES*"} + ,{5,"NORET*"} + ,{4,"NOSE*"} + ,{4,"NOST*"} + ,{4,"NOTE"} + ,{5,"NOTER*"} + ,{4,"NOTI*"} + ,{5,"NOTRA*"} + ,{5,"NOTRU*"} + ,{4,"NOTT*"} ,{6,"NOTTSY"} + ,{4,"NOTY*"} ,{6,"NOTYPE"} + ,{4,"NOUR*"} + ,{4,"NOWA*"} + ,{4,"NOWC*"} + ,{4,"NOWR"} ,{5,"NOWRA*"} ,{6,"NOWRAP"} + ,{7,"NOWRITE*"} + ,{2,"NU*"} + + ,{1,"O"} + ,{6,"OCHSET"} + ,{2,"OP*"} + ,{2,"OV*"} + ,{2,"OW*"} + + ,{2,"P1"} + ,{2,"P2"} + ,{2,"P3"} + ,{2,"P4"} + ,{2,"P5"} + ,{2,"P6"} + ,{2,"P7"} + ,{2,"P8"} + ,{3,"PAD"} + ,{3,"PAG*"} + ,{4,"PARS*"} ,{5,"PARSE"} + ,{4,"PASS*"} + ,{4,"PAST*"} + ,{4,"PRIO*"} + ,{4,"PRIN*"} + ,{3,"PRM*"} ,{6,"PRMMBX"} + ,{3,"PRO*"} + + ,{3,"QUE*"} + + ,{2,"RC*"} ,{4,"RCHK"} + ,{4,"READ"} ,{5,"READO*"} + ,{5,"READS*"} + ,{3,"REC*"} + ,{3,"REM*"} + ,{3,"REN*"} + ,{3,"RES*"} + ,{3,"RET*"} + ,{3,"REW*"} + ,{3,"RFA"} + ,{3,"RFM"} + + ,{1,"S"} + ,{3,"SEQ*"} + ,{3,"SET*"} + ,{2,"SH"} ,{3,"SHA*"} ,{4,"SHAR"} + ,{4,"SHEL*"} ,{5,"SHELL"} + ,{2,"SK*"} + ,{2,"SO*"} + ,{3,"SPA*"} + ,{3,"SPO*"} + ,{2,"ST"} ,{3,"STR*"} + ,{4,"STDE*"} ,{6,"STDERR"} + ,{2,"SU*"} + ,{2,"SY*"} + + ,{2,"TE*"} ,{4,"TERM"} + ,{2,"TM*"} + ,{3,"TRA*"} + ,{3,"TRU*"} + ,{2,"TT*"} ,{4,"TTSY"} + ,{2,"TY*"} ,{4,"TYPE"} + + ,{2,"UI*"} ,{3,"UIC"} + ,{2,"UN*"} + ,{2,"UP*"} + ,{2,"UR*"} + ,{2,"US*"} + + ,{2,"VA*"} + + ,{1,"W"} + ,{2,"WA*"} ,{4,"WAIT"} + ,{2,"WC*"} ,{4,"WCHK"} + ,{2,"WI*"} ,{5,"WIDTH"} + ,{2,"WO*"} + ,{2,"WR"} ,{3,"WRA*"} + ,{5,"WRITE"} + ,{7,"WRITELB"} + ,{7,"WRITEOF"} + ,{7,"WRITEON*"} + ,{7,"WRITETM"} + + ,{1,"X"} + + ,{1,"Y"} + + ,{2,"ZB*"} + ,{2,"ZD*"} + ,{3,"ZEX*"} + ,{4,"ZFIL*"} + ,{3,"ZFF"} + ,{2,"ZI*"} + ,{4,"ZLEN*"} + ,{4,"ZLIS*"} + + ,{8,"ZNODELAY"} /* ZNO* have to be spelled out fully */ + ,{9,"ZNOFILTER"} + ,{5,"ZNOFF"} + ,{7,"ZNOWRAP"} + + ,{4,"ZWID*"} + ,{4,"ZWRA*"} +}; +/* Offset of letter in dev_param_names */ +LITDEF unsigned char dev_param_index[27] = +{ +/* A B C D E F G H I J K L M N */ + 0, 5, 9, 26, 32, 45, 57, 59, 63, 69, 69, 69, 77, 80, +/* O P Q R S T U V W X Y Z end */ + 140, 145, 164, 165, 178, 196, 205, 211, 212, 227, 228, 229, 243 +}; +/* Offset of string within letter in dev_param_names */ +/* maintained in conjunction with zshow_params.h = offset in letter, letter */ +LITDEF zshow_index zshow_param_index[] = +{ +/* ALLO BLOC COMMAND CONV CTRA DELE EBCD EDIT EXCE EXTE FIELD FIL FIXED HOST */ + {2,0}, {2,1}, {9,2}, {12,2}, {16,2}, {1,3}, {1,4}, {4,4}, {9,4}, {11,4}, {2,5}, {5,5}, {8,5}, {3,7}, +/* ICHSET INDEPENDENT INSE LAB */ + {0,8}, {2,8}, {4,8}, {1,11}, +/* LENG NOCENE NOECHO NOEDIT NOESCA NOHOST NOINSE */ + {3,11}, {7,13}, {13,13}, {15,13}, {17,13}, {25,13}, {27,13}, +/* NOPAST NOREADS NOTTSY NOTYPE NOWRAP OCHSET PAD PARSE PAST PRMMBX RCHK */ + {33,13}, {38,13}, {49,13}, {51,13}, {57,13}, {1,14}, {8,15}, {11,15}, {13,15}, {17,15}, {1,17}, +/* READ READS REC SHAR SHELL STDERR TERM TTSY TYPE UIC WAIT WCHK WIDTH WRITE */ + {2,17}, {4,17}, {5,17}, {5,18}, {7,18}, {15,18}, {1,19}, {6,19}, {8,19}, {1,20}, {2,22}, {4,22}, {6,22}, {10,22} +}; + +int deviceparameters(oprtype *c, char who_calls) +{ + oprtype x; + oprtype cat_list[n_iops]; + int cat_cnt; + mval tmpmval; + triple *ref, *parm; + int n; + int status; + char parstr[MAXDEVPARLEN]; + char *parptr; + boolean_t is_parm_list; + boolean_t parse_warn; + + static readonly unsigned char dev_param_data[] = + { + iop_after + ,iop_allocation ,iop_allocation + ,iop_append + ,iop_attach + + ,iop_bigrecord + ,iop_blocksize ,iop_blocksize + ,iop_burst + + ,iop_canctlo, iop_canctlo + ,iop_canonical + ,iop_cenable + ,iop_characteristic + ,iop_chset + ,iop_clearscreen + ,iop_cli + ,iop_command ,iop_command + ,iop_connect + ,iop_contiguous + ,iop_convert + ,iop_copies + ,iop_cpulimit + ,iop_ctrap ,iop_ctrap + + ,iop_delete ,iop_delete + ,iop_delimiter + ,iop_detach + ,iop_doublespace + ,iop_downscroll + + ,iop_ebcdic ,iop_ebcdic + ,iop_echo + ,iop_editing ,iop_editing + ,iop_eraseline + ,iop_erasetape + ,iop_escape + ,iop_exception ,iop_exception + ,iop_extension ,iop_extension + ,iop_extgap + + ,iop_field ,iop_field ,iop_field + ,iop_fifo, iop_fifo + ,iop_filter + ,iop_firstpage + ,iop_fixed ,iop_fixed + ,iop_flag + ,iop_flush + ,iop_form + + ,iop_g_protection, iop_g_protection + + ,iop_header + ,iop_hold + ,iop_hostsync, iop_hostsync + + ,iop_ipchset + ,iop_independent ,iop_independent + ,iop_insert ,iop_insert + ,iop_ioerror + + ,iop_label + ,iop_lastpage + ,iop_length ,iop_length + ,iop_listen + ,iop_logfile + ,iop_logqueue + ,iop_lowercase + + ,iop_m + ,iop_morereadtime + ,iop_mount + + ,iop_name + ,iop_newversion + ,iop_next + ,iop_nobigrecord + ,iop_noburst + ,iop_nocanonical + ,iop_nocenable ,iop_nocenable + ,iop_noconvert + ,iop_nodelimiter + ,iop_nodoublespace + ,iop_noebcdic + ,iop_noecho ,iop_noecho + ,iop_noediting ,iop_noediting + ,iop_noescape ,iop_noescape + ,iop_inhextgap + ,iop_nofilter + ,iop_nofixed + ,iop_noflag + ,iop_noheader + ,iop_nohold + ,iop_nohostsync ,iop_nohostsync + ,iop_noinsert ,iop_noinsert + ,iop_nolabel + ,iop_nolowercase + ,iop_nonotify + ,iop_page + ,iop_nopassall + ,iop_nopasthru + ,iop_noprint + ,iop_nordcheckdata + ,iop_noreadonly ,iop_noreadonly + ,iop_noreadsync + ,iop_norestart + ,iop_inhretry + ,iop_nosequential + ,iop_nostream + ,iop_note + ,iop_noterminator + ,iop_notify + ,iop_notrailer + ,iop_notruncate + ,iop_nottsync ,iop_nottsync + ,iop_notypeahead ,iop_notypeahead + ,iop_nourgent + ,iop_nowait + ,iop_nowtcheckdata + ,iop_nowrap ,iop_nowrap ,iop_nowrap + ,iop_nowriteonly + ,iop_nl + + ,iop_o_protection + ,iop_opchset + ,iop_operator + ,iop_noinsert + ,iop_o_protection + + ,iop_p1 + ,iop_p2 + ,iop_p3 + ,iop_p4 + ,iop_p5 + ,iop_p6 + ,iop_p7 + ,iop_p8 + ,iop_pad + ,iop_page + ,iop_parse ,iop_parse + ,iop_passall + ,iop_pasthru + ,iop_priority + ,iop_print + ,iop_prmmbx ,iop_prmmbx + ,iop_o_protection + + ,iop_queue + + ,iop_rdcheckdata ,iop_rdcheckdata + ,iop_readonly ,iop_readonly + ,iop_readsync + ,iop_recordsize + ,iop_remote + ,iop_rename + ,iop_restart + ,iop_retry + ,iop_rewind + ,iop_rfa + ,iop_rfm + + ,iop_s_protection + ,iop_sequential + ,iop_setup + ,iop_shared ,iop_shared, iop_shared + ,iop_shell ,iop_shell + ,iop_skipfile + ,iop_socket + ,iop_space + ,iop_spool + ,iop_stream, iop_stream + ,iop_stderr, iop_stderr + ,iop_submit + ,iop_s_protection + + ,iop_terminator, iop_terminator + ,iop_tmpmbx + ,iop_trailer + ,iop_truncate + ,iop_ttsync ,iop_ttsync + ,iop_typeahead ,iop_typeahead + + ,iop_uic ,iop_uic + ,iop_unload + ,iop_upscroll + ,iop_urgent + ,iop_user + + ,iop_nofixed + + ,iop_w_protection + ,iop_wait ,iop_wait + ,iop_wtcheckdata ,iop_wtcheckdata + ,iop_width ,iop_width + ,iop_w_protection + ,iop_wrap ,iop_wrap + ,iop_writeonly + ,iop_writelb + ,iop_writeof + ,iop_writeonly + ,iop_writetm + + ,iop_x + + ,iop_y + + ,iop_zbfsize + ,iop_zdelay + ,iop_exception /* for ZEXCEPTION; ZEXC* is a synonym for EXC* */ + ,iop_filter /* for ZFILTER; ZFIL* is a synonym for FIL* */ + ,iop_zff + ,iop_zibfsize + ,iop_length /* for ZLENGTH; ZLEN* is a synonym for LE* and LENG */ + ,iop_zlisten + + ,iop_znodelay + ,iop_nofilter /* for ZNOFILTER; ZNOFILTER is a synonym for NOFIL* */ + ,iop_znoff + ,iop_nowrap /* for ZNOWRAP; ZNOWRAP is a synonym for NOWR, NOWRA*, NOWRAP */ + ,iop_width /* for ZWIDTH; ZWID* is a synonym for WI*, WIDTH */ + ,iop_wrap /* for ZWRAP; ZWRA* is a synonym for WR, and WRA* */ + } ; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + assert(dev_param_index[26] == (SIZEOF(dev_param_names)/SIZEOF(nametabent))); + assert(dev_param_index[26] == (SIZEOF(dev_param_data)/SIZEOF(unsigned char))); + is_parm_list = (window_token == TK_LPAREN); + if (is_parm_list) + advancewindow(); + cat_cnt = 0; + parptr = parstr; + parse_warn = FALSE; + for (;;) + { + if ((window_token != TK_IDENT) + || ((n = namelook(dev_param_index, dev_param_names, window_ident.addr, window_ident.len)) < 0)) + { + STX_ERROR_WARN(ERR_DEVPARUNK); /* sets "parse_warn" to TRUE */ + break; + } + n = dev_param_data[n]; + if (!(dev_param_control[n].valid_with & who_calls)) + { + STX_ERROR_WARN(ERR_DEVPARINAP); /* sets "parse_warn" to TRUE */ + break; + } + advancewindow(); + *parptr++ = n; + if (io_params_size[n]) + { + if (window_token != TK_EQUAL) + { + STX_ERROR_WARN(ERR_DEVPARVALREQ); /* sets "parse_warn" to TRUE */ + break; + } + advancewindow(); + if (!expr(&x)) + return FALSE; + assert(x.oprclass == TRIP_REF); + if (x.oprval.tref->opcode == OC_LIT) + { + /* check to see if this string could overflow (5 is a int4 word plus a parameter code for + safety) Must check before cvtparm, due to the fact that tmpmval could otherwise + be garbage collected by a later putstr + */ + if (parptr - parstr + x.oprval.tref->operand[0].oprval.mlit->v.str.len + 5 > SIZEOF(parstr)) + { + cat_list[cat_cnt++] = put_str(parstr, INTCAST(parptr - parstr)); + parptr = parstr; + } + assert(x.oprval.tref->operand[0].oprclass == MLIT_REF); + status = cvtparm(n, &x.oprval.tref->operand[0].oprval.mlit->v, &tmpmval); + if (status) + { + stx_error(status); + return FALSE; + } + memcpy(parptr, tmpmval.str.addr, tmpmval.str.len); + parptr += tmpmval.str.len; + } else + { + if (parptr > parstr) + { + cat_list[cat_cnt++] = put_str(parstr, INTCAST(parptr - parstr)); + parptr = parstr; + } + ref = newtriple(OC_CVTPARM); + ref->operand[0] = put_ilit(n); + ref->operand[1] = x; + cat_list[cat_cnt++] = put_tref(ref); + } + } + if (!is_parm_list) + break; + if (window_token == TK_COLON) + { + advancewindow(); + continue; + } + else if (window_token == TK_RPAREN) + { + advancewindow(); + break; + } + stx_error(ERR_RPARENMISSING); + return FALSE; + } + if (parse_warn) + { /* Parse the remaining arguments until the corresponding RIGHT-PAREN or SPACE or EOL is reached */ + if (!parse_until_rparen_or_space()) + return FALSE; + if (window_token == TK_RPAREN) + advancewindow(); + } + *parptr++ = iop_eol; + cat_list[cat_cnt++] = put_str(parstr,INTCAST(parptr - parstr)); + if (cat_cnt <= 1) + *c = cat_list[0]; + else + { + ref = newtriple(OC_CAT); + ref->operand[0] = put_ilit(cat_cnt + 1); + *c = put_tref(ref); + for (n = 0 ; n < cat_cnt ; n++) + { + parm = newtriple(OC_PARAMETER); + ref->operand[1] = put_tref(parm); + ref = parm; + ref->operand[0] = cat_list[n]; + } + } + return TRUE; +} diff --git a/sr_port/deviceparameters.h b/sr_port/deviceparameters.h new file mode 100644 index 0000000..6ef872b --- /dev/null +++ b/sr_port/deviceparameters.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef DEVICEPARAMETERS_INCLUDED +#define DEVICEPARAMETERS_INCLUDED + +int deviceparameters(oprtype *c, char who_calls); /***type int added***/ + +#endif /* DEVICEPARAMETERS_INCLUDED */ diff --git a/sr_port/dfa_calc.c b/sr_port/dfa_calc.c new file mode 100644 index 0000000..e1addc1 --- /dev/null +++ b/sr_port/dfa_calc.c @@ -0,0 +1,478 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "copy.h" +#include "patcode.h" +#include "compiler.h" +#include "gtm_string.h" + +/* the following macro checks that a 1 dimensional array reference is valid i.e. array[index] is within defined limits */ +#define check_1dim_array_bound(array, index) assert((index) < (SIZEOF(array) / SIZEOF(array[0]))) + +/* the following macro checks that a 2 dimensional array reference is valid i.e. array[row][col] is within defined limits */ +#define check_2dim_array_bound(array, row, col) \ +{ \ + assert((row) < (SIZEOF(array) / SIZEOF(array[0]))); \ + assert((col) < (SIZEOF(array[0]) / SIZEOF(array[0][0]))); \ +} + +/* Note: in various places, dfa_calc() makes a reference to the array 'typemask'. dfa_calc() is executed at compile-time. + * The content of the array typemask is static, but, at run-time, the pointer that is used to access the typemask array + * (pattern_typemask) may change whenever a program executes the command View "PATCODE":tablename. + * As a result, the pattern masks that the GT.M compiler uses may differ from the ones that are in operation at run-time. + */ +LITREF uint4 typemask[PATENTS]; + +static uint4 classmask[CHAR_CLASSES] = +{ + PATM_N, PATM_P, PATM_L, PATM_U, PATM_C, PATM_B, PATM_D, PATM_F, PATM_G, PATM_H, PATM_I, + PATM_J, PATM_K, PATM_M, PATM_O, PATM_Q, PATM_R, PATM_S, PATM_T, PATM_V, PATM_W, PATM_X, + PATM_UTF8_ALPHABET, PATM_UTF8_NONBASIC +}; + +/* This procedure is part of the MUMPS compiler. The function of this procedure is to build the data structures that + * will be used to drive the DFA engine that can evaluate certain pattern matches. Note that this routine operates + * at compile-time, and that all data structures built in this procedure are compiled at the end into a terse string + * of values that will be passed to do_pattern (through patstr). do_pattern(), which operates at run-time will + * interpret this string of values and do the actual DFA work (DFA = Discrete Finite Automaton). + */ +int dfa_calc(struct leaf *leaves, int leaf_num, struct e_table *expand, uint4 **fstchar_ptr, uint4 **outchar_ptr) +{ + uint4 *locoutchar; + uint4 pattern_mask; + unsigned char *textstring; + int offset[2 * (MAX_SYM + 1)]; + int pos_offset[CHAR_CLASSES]; + int fst[2][2], lst[2][2]; + int4 charcls, maskcls, numexpand, count, clsnum, maxcls, clsposlis; + int4 state_num, node_num, sym_num, expseq, seq; + struct node nodes; + /* EdM: comment for reviewers: + * 'states' is currently defined as a boolean_t. + * In the original version it was a bool (== char). + * Since comparisons on this array are done using + * memcmp, and the only values assigned to elements + * in this array are TRUE and FALSE (1 and 0), + * we might consider declaring states as a 'char' + * array after all... + */ + boolean_t states[2 * MAX_SYM][CHAR_CLASSES]; + boolean_t fpos[2 * MAX_SYM][CHAR_CLASSES]; + int d_trans[2 * MAX_SYM][CHAR_CLASSES]; + int pos_lis[2 * MAX_SYM][CHAR_CLASSES]; + struct c_trns_tb c_trans; + + /* Note: in various places, this procedure makes a reference to the array 'typemask'. + * This procedure is executed at compile-time. The contents of the array typemask is static, but, at + * run-time, the pointer that is used to access the array pattern_typemask may change whenever a program + * executes the command View "PATCODE":tablename. As a result, the pattern masks that the GT.M compiler + * uses may differ from the ones that are in operation at run-time. + */ + locoutchar = *outchar_ptr; + if (leaf_num > 1) + { + pattern_mask = PATM_DFA; + state_num = 1; + check_1dim_array_bound(leaves->nullable, leaf_num); + leaves->nullable[leaf_num] = FALSE; + leaves->letter[leaf_num][0] = DFABIT; + leaves->letter[leaf_num][1] = -1; + pos_offset[0] = 0; + for (seq = 1; seq < CHAR_CLASSES; seq++) + pos_offset[seq] = pos_offset[seq - 1] + expand->num_e[seq - 1]; + memset(&nodes, 0, SIZEOF(nodes)); + memset(&fpos[0][0], 0, SIZEOF(fpos)); + memset(&states[0][0], 0, SIZEOF(states)); + memset(&d_trans[0][0], 128, SIZEOF(d_trans)); + memset(&pos_lis[0][0], 128, SIZEOF(pos_lis)); + memset(&c_trans.c[0], 0, SIZEOF(c_trans.c)); + memset(offset, 0, SIZEOF(offset)); + memset(fst, 0, SIZEOF(fst)); + memset(lst, 0, SIZEOF(lst)); + charcls = 0; + clsnum = 0; + maxcls = 0; + nodes.nullable[0] = leaves->nullable[0] & leaves->nullable[1]; + states[state_num][charcls] = TRUE; + for (maskcls = 0; leaves->letter[0][maskcls] >= 0; maskcls++) + { + check_1dim_array_bound(leaves->letter[0], maskcls); + if (!(leaves->letter[0][maskcls] & DFABIT)) + { + check_2dim_array_bound(fpos, charcls, charcls + 1); + fpos[charcls][charcls + 1] = TRUE; + lst[FST][FST] = charcls; + lst[FST][LST] = charcls; + assert(leaves->letter[0][maskcls] >= 0 && leaves->letter[0][maskcls] < SIZEOF(typemask)); + seq = patmaskseq(typemask[leaves->letter[0][maskcls]]); + if (seq < 0) + seq = 0; + for (numexpand = 1; expand->meta_c[seq][numexpand] != leaves->letter[0][maskcls]; numexpand++) + ; + check_1dim_array_bound(pos_lis, (pos_offset[seq] + numexpand)); + for (count = 0; pos_lis[pos_offset[seq] + numexpand][count] >= 0; count++) + ; + check_2dim_array_bound(pos_lis, (pos_offset[seq] + numexpand), count); + pos_lis[pos_offset[seq] + numexpand][count] = charcls; + charcls++; + } else + { + seq = patmaskseq(leaves->letter[0][maskcls]); + if (seq < 0) + { + seq = 0; + expseq = 0; + } else + expseq = expand->num_e[seq]; + for (numexpand = 0; numexpand < expseq; numexpand++) + { + states[state_num][charcls] = TRUE; + fst[FST][LST] = charcls; + lst[FST][LST] = charcls; + for (count = 0; pos_lis[pos_offset[seq] + numexpand][count] >= 0; count++) + ; + check_2dim_array_bound(pos_lis, (pos_offset[seq] + numexpand), count); + pos_lis[pos_offset[seq] + numexpand][count] = charcls; + charcls++; + } + } + } + fst[LST][FST] = charcls; + fst[LST][LST] = charcls; + lst[LST][FST] = charcls; + if(!leaves->nullable[1]) + { + nodes.last[0][charcls] = TRUE; + maxcls = charcls; + } + for (maskcls = 0; leaves->letter[1][maskcls] >= 0; maskcls++) + { + check_1dim_array_bound(leaves->letter[1], maskcls); + if (!(leaves->letter[1][maskcls] & DFABIT)) + { + check_2dim_array_bound(fpos, charcls, charcls + 1); + fpos[charcls][charcls + 1] = TRUE; + lst[LST][FST] = charcls; + lst[LST][LST] = charcls; + assert(leaves->letter[1][maskcls] >= 0 && leaves->letter[1][maskcls] < SIZEOF(typemask)); + seq = patmaskseq(typemask[leaves->letter[1][maskcls]]); + if (seq < 0) + seq = 0; + for (numexpand = 1; expand->meta_c[seq][numexpand] != leaves->letter[1][maskcls]; numexpand++) + ; + check_1dim_array_bound(pos_lis, (pos_offset[seq] + numexpand)); + for (count = 0; pos_lis[pos_offset[seq] + numexpand][count] >= 0; count++) + ; + check_2dim_array_bound(pos_lis, (pos_offset[seq] + numexpand), count); + pos_lis[pos_offset[seq] + numexpand][count] = charcls; + charcls++; + } else + { + seq = patmaskseq(leaves->letter[1][maskcls]); + if (seq < 0) + { + seq = 0; + expseq = 0; + } else + expseq = expand->num_e[seq]; + for (numexpand = 0; numexpand < expseq; numexpand++) + { + nodes.last[0][charcls] = TRUE; + fst[LST][LST] = charcls; + lst[LST][LST] = charcls; + check_1dim_array_bound(pos_lis, (pos_offset[seq] + numexpand)); + for (count = 0; pos_lis[pos_offset[seq] + numexpand][count] >= 0; count++) + ; + check_2dim_array_bound(pos_lis, (pos_offset[seq] + numexpand), count); + pos_lis[pos_offset[seq] + numexpand][count] = charcls; + charcls++; + } + } + } + if (leaves->nullable[0]) + { + assert((2 * MAX_SYM) > lst[FST][LST]); + assert(CHAR_CLASSES > fst[LST][LST]); + for (numexpand = lst[FST][FST]; numexpand <= lst[FST][LST]; numexpand++) + { + for (count = fst[FST][FST]; count <= fst[FST][LST]; count++) + { + check_2dim_array_bound(fpos, numexpand, count); + fpos[numexpand][count] = TRUE; + } + } + for (numexpand = fst[LST][FST]; numexpand <= fst[LST][LST]; numexpand++) + states[state_num][numexpand] = TRUE; + } + if (leaves->nullable[1]) + { + nodes.last[0][charcls - 1] = TRUE; + for (numexpand = lst[LST][FST]; numexpand <= lst[LST][LST]; numexpand++) + { + for (count = fst[LST][FST]; count <= fst[LST][LST]; count++) + { + check_2dim_array_bound(fpos, numexpand, count); + fpos[numexpand][count] = TRUE; + } + } + for (numexpand = lst[FST][FST]; numexpand <= lst[FST][LST]; numexpand++) + nodes.last[0][numexpand] = TRUE; + maxcls = charcls; + } + for (numexpand = lst[FST][FST]; numexpand <= lst[FST][LST]; numexpand++) + { + for (count = fst[LST][FST]; count <= fst[LST][LST]; count++) + { + check_2dim_array_bound(fpos, numexpand, count); + fpos[numexpand][count] = TRUE; + } + } + if (!leaves->nullable[1]) + clsnum = lst[LST][FST]; + for (node_num = 1; node_num < leaf_num; node_num++) + { + nodes.nullable[node_num] = nodes.nullable[node_num - 1] & + leaves->nullable[node_num + 1]; + if (leaves->nullable[node_num + 1]) + { + for (maskcls = 0; maskcls < charcls; maskcls++) + { + check_2dim_array_bound(nodes.last, node_num, maskcls); + nodes.last[node_num][maskcls] = nodes.last[node_num - 1][maskcls]; + } + } else + { + nodes.last[node_num][charcls] = TRUE; + maxcls = charcls; + } + fst[LST][FST] = charcls; + fst[LST][LST] = charcls; + lst[LST][FST] = charcls; + for (maskcls = 0; leaves->letter[node_num + 1][maskcls] >= 0; maskcls++) + { + check_1dim_array_bound(leaves->letter[node_num + 1], maskcls); + if (!(leaves->letter[node_num + 1][maskcls] & DFABIT)) + { + check_2dim_array_bound(fpos, charcls, charcls + 1); + fpos[charcls][charcls + 1] = TRUE; + lst[LST][FST] = charcls; + lst[LST][LST] = charcls; + assert(leaves->letter[node_num + 1][maskcls] >= 0 && + leaves->letter[node_num + 1][maskcls] < SIZEOF(typemask)); + seq = patmaskseq(typemask[leaves->letter[node_num + 1][maskcls]]); + if (seq < 0) + seq = 0; + for (numexpand = 1; + expand->meta_c[seq][numexpand] != leaves->letter[node_num + 1][maskcls]; + numexpand++) + ; + check_1dim_array_bound(pos_lis, (pos_offset[seq] + numexpand)); + for (count = 0; pos_lis[pos_offset[seq] + numexpand][count] >= 0; count++) + ; + check_2dim_array_bound(pos_lis, (pos_offset[seq] + numexpand), count); + pos_lis[pos_offset[seq] + numexpand][count] = charcls; + charcls++; + } else + { + seq = patmaskseq(leaves->letter[node_num + 1][maskcls]); + if (seq < 0) + { + seq = 0; + expseq = 0; + } else + expseq = expand->num_e[seq]; + for (numexpand = 0; numexpand < expseq; numexpand++) + { + nodes.last[node_num][charcls] = TRUE; + if (nodes.nullable[node_num - 1]) + states[state_num][charcls] = TRUE; + fst[LST][LST] = charcls; + lst[LST][LST] = charcls; + for (count = 0; pos_lis[pos_offset[seq] + numexpand][count] >= 0; count++) + ; + check_2dim_array_bound(pos_lis, (pos_offset[seq] + numexpand), count); + pos_lis[pos_offset[seq] + numexpand][count] = charcls; + charcls++; + } + } + } + if (nodes.nullable[node_num - 1]) + { + for (numexpand = fst[LST][FST]; numexpand <= fst[LST][LST]; numexpand++) + { + check_1dim_array_bound(states[state_num], numexpand); + states[state_num][numexpand] = TRUE; + } + } + if (leaves->nullable[node_num + 1]) + { + nodes.last[node_num][charcls - 1] = TRUE; + for (numexpand = lst[LST][FST]; numexpand <= lst[LST][LST]; numexpand++) + { + for (count = fst[LST][FST]; count <= fst[LST][LST]; count++) + { + check_2dim_array_bound(fpos, numexpand, count); + fpos[numexpand][count] = TRUE; + } + } + maxcls = charcls; + } + for (numexpand = clsnum; numexpand < maxcls; numexpand++) + for (count = fst[LST][FST]; count <= fst[LST][LST]; count++) + if (nodes.last[node_num - 1][numexpand]) + fpos[numexpand][count] = TRUE; + if (!leaves->nullable[node_num + 1]) + clsnum = lst[LST][FST]; + } + sym_num = charcls; + state_num++; + check_1dim_array_bound(offset, state_num + 1); + for (seq = 1; seq < state_num; seq++) + { + charcls = 0; + offset[seq + 1]++; + offset[seq + 1] += offset[seq]; + for (maskcls = 0; maskcls < CHAR_CLASSES; maskcls++) + { + if (expand->num_e[maskcls] > 0) + { + for (numexpand = 0; numexpand < expand->num_e[maskcls]; numexpand++) + { + for (maxcls = 0; pos_lis[charcls + numexpand][maxcls] >= 0; maxcls++) + { + clsposlis = pos_lis[charcls + numexpand][maxcls]; + if (states[seq][clsposlis]) + { + for (clsnum = 0; clsnum <= sym_num; clsnum++) + { + check_1dim_array_bound(states[state_num], clsnum); + states[state_num][clsnum] |= fpos[clsposlis][clsnum]; + } + } + } + check_1dim_array_bound(states, state_num); + for (count = 0; + memcmp(states[count], states[state_num], (sym_num + 1) * SIZEOF(boolean_t)) + && (count < state_num); + count++) + ; + if (count > 0) + { + if (0 == numexpand) + { + d_trans[seq][charcls] = count; + for (clsnum = 0; + (clsnum < c_trans.c[seq]) + && (c_trans.trns[seq][clsnum] != count); + clsnum++) + ; + check_1dim_array_bound(c_trans.p_msk[seq], clsnum); + if (clsnum == c_trans.c[seq]) + { + c_trans.p_msk[seq][clsnum] = classmask[maskcls]; + check_1dim_array_bound(c_trans.trns[seq], clsnum); + c_trans.trns[seq][clsnum] = count; + offset[seq + 1] += 2; + c_trans.c[seq]++; + } else + c_trans.p_msk[seq][clsnum] |= classmask[maskcls]; + } else if (d_trans[seq][charcls] != count) + { + d_trans[seq][charcls + numexpand] = count; + offset[seq + 1] += 3; + } + if (count == state_num) + state_num++; + else + memset(states[state_num], 0, (sym_num + 1) * SIZEOF(states[0][0])); + } + } + charcls += expand->num_e[maskcls]; + } + } + } + *outchar_ptr += offset[state_num] + 2; + if ((*outchar_ptr - *fstchar_ptr > MAX_DFA_SPACE) || + ((offset[state_num] + 1) > (MAX_PATTERN_LENGTH / 2))) + return -1; + *locoutchar++ = PATM_DFA; + *locoutchar++ = offset[state_num]; + for (seq = 1; seq < state_num; seq++) + { + charcls = 0; + for (numexpand = 0; numexpand < CHAR_CLASSES; numexpand++) + { + if (expand->num_e[numexpand] > 1) + { + for (count = 1; count < expand->num_e[numexpand]; count++) + { + check_2dim_array_bound(d_trans, seq, charcls + count); + if (d_trans[seq][charcls + count] >= 0) + { + *locoutchar++ = PATM_STRLIT; + *locoutchar++ = expand->meta_c[numexpand][count]; + *locoutchar++ = offset[d_trans[seq][charcls + count]]; + } + } + } + charcls += expand->num_e[numexpand]; + } + for (numexpand = 0; numexpand < c_trans.c[seq]; numexpand++) + { + check_2dim_array_bound(c_trans.p_msk, seq, numexpand); + *locoutchar++ = c_trans.p_msk[seq][numexpand]; + check_2dim_array_bound(c_trans.trns, seq, numexpand); + check_1dim_array_bound(offset, c_trans.trns[seq][numexpand]); + *locoutchar++ = offset[c_trans.trns[seq][numexpand]]; + } + *locoutchar++ = (states[seq][sym_num]) ? PATM_ACS : PATM_DFA; + } + assert(MAX_DFA_SPACE >= (locoutchar - *fstchar_ptr)); + return 1; + } else + { + pattern_mask = 0; + *outchar_ptr += 1; + maskcls = 1; + if (!(leaves->letter[0][0] & DFABIT)) + { + pattern_mask = PATM_STRLIT; + for (maskcls = 0; leaves->letter[0][maskcls] >= 0; maskcls++) + ; + check_1dim_array_bound(leaves->letter[0], maskcls); + *outchar_ptr += PAT_STRLIT_PADDING + ((maskcls + SIZEOF(uint4) - 1) / SIZEOF(uint4)); + } else + { + for (numexpand = 0; leaves->letter[0][numexpand] >= 0; numexpand++) + pattern_mask |= leaves->letter[0][numexpand]; + check_1dim_array_bound(leaves->letter[0], numexpand); + } + if (*outchar_ptr - *fstchar_ptr > MAX_PATTERN_LENGTH) + return -1; + *locoutchar++ = pattern_mask; + if (PATM_STRLIT & pattern_mask) + { + *locoutchar++ = maskcls; /* bytelen */ + *locoutchar++ = maskcls; /* charlen */ + + *locoutchar++ = 0; /* both NONASCII and BADCHAR flags are absent since this is + indeed a valid ASCII string or else we would not have come + to dfa_calc (and hence there are no bad chars) */ + assert(3 == PAT_STRLIT_PADDING); + textstring = (unsigned char *)locoutchar; /* change pointer type */ + for (numexpand = 0; numexpand < maskcls; numexpand++) + *textstring++ = leaves->letter[0][numexpand]; + } + return maskcls; + } +} diff --git a/sr_port/dh.mpt b/sr_port/dh.mpt new file mode 100644 index 0000000..e2bb7d3 --- /dev/null +++ b/sr_port/dh.mpt @@ -0,0 +1,29 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 1987, 2003 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +%DH ;GT.M %DH utility - decimal to hexadecimal conversion program + ;invoke with %DH in decimal and %DL digits to return %DH in hexadecimal + ;invoke at INT to execute interactively + ;invoke at FUNC as an extrinsic function + ;if you make heavy use of this routine, consider $ZCALL + ; + s %DH=$$FUNC(%DH,$G(%DL)) + q +INT n %DL + r !,"Decimal: ",%DH r !,"Digits: ",%DL s %DH=$$FUNC(%DH,%DL) + q +FUNC(d,l) + s:'$l($g(l)) l=8 + q:d=0 $e("00000000",1,l) + n h + s h="" + s:d<0 d=$$FUNC^%EXP(16,l)+d + f q:'d s h=$e("0123456789ABCDEF",d#16+1)_h,d=d\16 + q $e("00000000",1,l-$l(h))_h diff --git a/sr_port/dm_read.h b/sr_port/dm_read.h new file mode 100644 index 0000000..b4a506f --- /dev/null +++ b/sr_port/dm_read.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef DM_READ_INCLUDED +#define DM_READ_INCLUDED + +void dm_read(mval *v); + +#endif /* DM_READ_INCLUDED */ diff --git a/sr_port/dm_setup.c b/sr_port/dm_setup.c new file mode 100644 index 0000000..3c7057c --- /dev/null +++ b/sr_port/dm_setup.c @@ -0,0 +1,32 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "rtnhdr.h" +#include "stack_frame.h" +#include "dm_setup.h" +#ifdef UNIX +#include "io.h" +GBLREF io_desc *gtm_err_dev; +#endif + +GBLREF stack_frame *frame_pointer; + +void dm_setup(void) +{ +#ifdef UNIX + /* zero the error device just to be safe */ + assert(NULL == gtm_err_dev); + gtm_err_dev = NULL; +#endif + new_stack_frame(frame_pointer->rvector, GTM_CONTEXT(call_dm), CODE_ADDRESS(call_dm)); + frame_pointer->type = SFT_DM; +} diff --git a/sr_port/dm_setup.h b/sr_port/dm_setup.h new file mode 100644 index 0000000..73725a7 --- /dev/null +++ b/sr_port/dm_setup.h @@ -0,0 +1,19 @@ +/**************************************************************** + * * + * Copyright 2001, 2003 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __DM_SETUP_H_ +#define __DM_SETUP_H_ + +#define GTM_DMOD "GTM$DMOD" + +void dm_setup(void); + +#endif diff --git a/sr_port/do.mpt b/sr_port/do.mpt new file mode 100644 index 0000000..af8c04b --- /dev/null +++ b/sr_port/do.mpt @@ -0,0 +1,29 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 1987,2001 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +%DO ;GT.M %DO utility - decimal to octal conversion program + ;invoke with %DO in decimal and %DL digits to return %DO in octal + ;invoke at INT to execute interactively + ;invoke at FUNC as an extrinsic function + ;if you make heavy use of this routine, consider $ZCALL + ; + s %DO=$$FUNC(%DO,$g(%DL)) + q +INT n %DL + r !,"Decimal: ",%DO r !,"Digits: ",%DL s %DO=$$FUNC(%DO,%DL) + q +FUNC(d,l) + s:'$l($g(l)) l=12 + q:d=0 $e("000000000000",1,l) + n o + s o="" + s:d<0 d=$$FUNC^%EXP(8,l)+d + f q:'d s o=d#8_o,d=d\8 + q $e("000000000000",1,l-$l(o))_o diff --git a/sr_port/do_indir_do.c b/sr_port/do_indir_do.c new file mode 100644 index 0000000..4ad7307 --- /dev/null +++ b/sr_port/do_indir_do.c @@ -0,0 +1,62 @@ +/**************************************************************** + * * + * Copyright 2001, 2004 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "toktyp.h" +#include "rtnhdr.h" +#include "stack_frame.h" +#include "indir_enum.h" +#include "cmd_qlf.h" +#include "gtm_caseconv.h" +#include "op.h" +#include "do_indir_do.h" +#include "valid_mname.h" + +GBLREF stack_frame *frame_pointer; +GBLREF command_qualifier cmd_qlf; +GBLREF boolean_t is_tracing_on; + +int do_indir_do(mval *v, unsigned char argcode) +{ + mval label; + lnr_tabent USHBIN_ONLY(*)*addr; + mident_fixed ident; + rhdtyp *current_rhead; + + if (valid_labname(&v->str)) + { + memcpy(ident.c, v->str.addr, v->str.len); + if (!(cmd_qlf.qlf & CQ_LOWER_LABELS)) + lower_to_upper((uchar_ptr_t)ident.c, (uchar_ptr_t)ident.c, v->str.len); + label.mvtype = MV_STR; + label.str.len = v->str.len; + label.str.addr = &ident.c[0]; + addr = op_labaddr(frame_pointer->rvector, &label, 0); + if (argcode == indir_do) + { + if (is_tracing_on) + exfun_frame_sp(); + else + exfun_frame(); + } + current_rhead = CURRENT_RHEAD_ADR(frame_pointer->rvector); + frame_pointer->mpc = LINE_NUMBER_ADDR(current_rhead, USHBIN_ONLY(*)addr); +#ifdef HAS_LITERAL_SECT + frame_pointer->ctxt = (unsigned char *)LINKAGE_ADR(current_rhead); +#else + frame_pointer->ctxt = PTEXT_ADR(current_rhead); +#endif + return TRUE; + } else + return FALSE; +} diff --git a/sr_port/do_indir_do.h b/sr_port/do_indir_do.h new file mode 100644 index 0000000..04aebcb --- /dev/null +++ b/sr_port/do_indir_do.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef DO_INDIR_DO_INCLUDED +#define DO_INDIR_DO_INCLUDED + +int do_indir_do(mval *v, unsigned char argcode); /***type int added***/ + +#endif /* DO_INDIR_DO_INCLUDED */ diff --git a/sr_port/do_patalt.c b/sr_port/do_patalt.c new file mode 100644 index 0000000..58945a6 --- /dev/null +++ b/sr_port/do_patalt.c @@ -0,0 +1,339 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" /* for memset */ + +#include "copy.h" +#include "patcode.h" + +#ifdef UNICODE_SUPPORTED +#include "gtm_utf8.h" +#endif + +/* see corresponding GBLDEFs in gbldefs.c for comments on the caching mechanism */ +GBLREF int4 curalt_depth; /* depth of alternation nesting */ +GBLREF int4 do_patalt_calls[PTE_MAX_CURALT_DEPTH]; /* number of calls to do_patalt() */ +GBLREF int4 do_patalt_hits[PTE_MAX_CURALT_DEPTH]; /* number of pte_csh hits in do_patalt() */ +GBLREF int4 do_patalt_maxed_out[PTE_MAX_CURALT_DEPTH]; /* no. of pte_csh misses after maxing on allocation size */ +GBLREF pte_csh *pte_csh_array[PTE_MAX_CURALT_DEPTH]; /* pte_csh array (per curalt_depth) */ +GBLREF int4 pte_csh_cur_size[PTE_MAX_CURALT_DEPTH]; /* current pte_csh size (per curalt_depth) */ +GBLREF int4 pte_csh_alloc_size[PTE_MAX_CURALT_DEPTH]; /* current allocated pte_csh size (per curalt_depth) */ +GBLREF int4 pte_csh_entries_per_len[PTE_MAX_CURALT_DEPTH]; /* current number of entries per len */ +GBLREF int4 pte_csh_tail_count[PTE_MAX_CURALT_DEPTH]; /* count of non 1-1 corresponding pte_csh_array members */ +GBLREF pte_csh *cur_pte_csh_array; /* copy of pte_csh_array corresponding to curalt_depth */ +GBLREF int4 cur_pte_csh_size; /* copy of pte_csh_cur_size corresponding to curalt_depth */ +GBLREF int4 cur_pte_csh_entries_per_len; /* copy of pte_csh_entries_per_len corresponding to curalt_depth */ +GBLREF int4 cur_pte_csh_tail_count; /* copy of pte_csh_tail_count corresponding to curalt_depth */ +GBLREF boolean_t gtm_utf8_mode; + +/* Example compiled pattern for an alternation pattern + * Pattern = P0_P1 + * ---------------- + * P0 = 1.3(.N,2"-",.2A) + * P1 = 1" " + * + * Compiled Pattern + * ----------------- + * 0x00000000 <-- fixed (1 if fixed length, 0 if not fixed length) + * 0x00000027 <-- length of pattern stream (inclusive of itself) + * + * 0x02000000 P0 <-- pattern_mask[0] => alternation + * 0x00000001 P0 <-- alt_rep_min[0] + * 0x00000003 P0 <-- alt_rep_max[0] + * 0x00000009 P0 <-- length of alternation pattern's choice[0] pattern (exclusive of itself) + * 0x00000000 P0 <-- fixed + * 0x00000002 P0 <-- length of pattern stream (inclusive of itself) + * 0x40000001 P0 <-- pattern_mask[0] => DFABIT | PATM_N + * 0x00000001 P0 <-- count + * 0x00000000 P0 <-- tot_min + * 0x00007fff P0 <-- tot_max + * 0x00000000 P0 <-- min[0] + * 0x00007fff P0 <-- max[0] + * 0x00000001 P0 <-- size[0] + * 0x0000000a P0 <-- length of alternation pattern's choice[1] pattern (exclusive of itself) + * 0x00000001 P0 <-- fixed + * 0x00000004 P0 <-- length of pattern stream (inclusive of itself) + * 0x00000082 P0 <-- pattern_mask[0] = PATM_STR | PATM_P + * 0x00000001 P0 <-- length of PATM_STR (exclusive of itself) + * 0x0000002d P0 <-- PATM_STR[0] = '-' + * 0x00000001 P0 <-- count + * 0x00000002 P0 <-- tot_min + * 0x00000002 P0 <-- tot_max + * 0x00000002 P0 <-- min[0] // Note for fixed length, max[] array is absent // + * 0x00000001 P0 <-- size[0] + * 0x00000000 P0 <-- End of alternation pattern's choices ('\0') + * + * 0x00000082 P1 <-- pattern_mask[1] => PATM_STR | PATM_P (' ') + * 0x00000001 P1 <-- length of PATM_STR (exclusive of itself) + * 0x00000020 P1 <-- PATM_STR[0] = ' ' + * + * 0x00000002 <-- count + * 0x00000001 <-- total_min + * 0x00007fff <-- total_max + * 0x00000000 <-- min[0] <-- Begin of min[2] array + * 0x00000001 <-- min[1] + * 0x00007fff <-- max[0] <-- Begin of max[2] array + * 0x00000001 <-- max[1] + * 0x00000001 <-- size[0] <-- Begin of size[2] array + * 0x00000001 <-- size[1] + */ + +/* returns index in cur_pte_csh_array that holds the desired tuple.. + * return PTE_NOT_FOUND otherwise. + */ +static int pte_csh_present(char *patptr, char *strptr, int4 charlen, int repcnt) +{ + int4 index; + pte_csh *tmp_pte, *pte_top; + + assert(PTE_MAX_CURALT_DEPTH > curalt_depth); + index = ((PTE_STRLEN_CUTOFF > charlen) ? charlen : PTE_STRLEN_CUTOFF) * cur_pte_csh_entries_per_len; + assert(cur_pte_csh_size > index); + tmp_pte = cur_pte_csh_array + index; + pte_top = tmp_pte + ((PTE_STRLEN_CUTOFF > charlen) ? cur_pte_csh_entries_per_len : cur_pte_csh_tail_count); + assert(pte_top <= (cur_pte_csh_array + cur_pte_csh_size)); + for (; tmp_pte < pte_top; tmp_pte++) + { + if ((tmp_pte->strptr != strptr) || (tmp_pte->patptr != patptr) + || (tmp_pte->charlen != charlen) || (tmp_pte->repcnt != repcnt)) + { + if (NULL != tmp_pte->strptr) + continue; + else + break; /* the first NULL value means all further entries for this "charlen" are NULL */ + } + tmp_pte->count++; + return (int)tmp_pte->match; + } + return (int)PTE_NOT_FOUND; +} + +static void pte_csh_insert(char *patptr, char *strptr, int4 charlen, int repcnt, boolean_t match) +{ + int4 index; + pte_csh *tmp_pte, *pte_top, *min_pte, *free_pte; + + assert(PTE_MAX_CURALT_DEPTH > curalt_depth); + assert(PTE_NOT_FOUND == pte_csh_present(patptr, strptr, charlen, repcnt)); + index = ((PTE_STRLEN_CUTOFF > charlen) ? charlen : PTE_STRLEN_CUTOFF) * cur_pte_csh_entries_per_len; + assert(cur_pte_csh_size > index); + tmp_pte = cur_pte_csh_array + index; + pte_top = tmp_pte + ((PTE_STRLEN_CUTOFF > charlen) ? cur_pte_csh_entries_per_len : cur_pte_csh_tail_count); + assert(pte_top <= (cur_pte_csh_array + cur_pte_csh_size)); + min_pte = tmp_pte; + free_pte = NULL; + + for (; tmp_pte < pte_top; tmp_pte++) + { + if (NULL == tmp_pte->patptr) + { + min_pte = free_pte = tmp_pte; + break; + } else if (min_pte->count > tmp_pte->count) + min_pte = tmp_pte; + } + if (NULL == free_pte) + { + for (tmp_pte = cur_pte_csh_array + index; tmp_pte < pte_top; tmp_pte++) + tmp_pte->count = 1; /* reset count whenever new entry is made thereby causing history refresh. + * i.e. permitting formerly busy but currently inactive patterns to be reused + */ + } + min_pte->count = 0; /* give little priority to the rest by setting count to 1 less than the others */ + min_pte->patptr = patptr; + min_pte->strptr = strptr; + min_pte->charlen = charlen; + min_pte->repcnt = repcnt; + min_pte->match = match; +} + +int do_patalt(uint4 *firstalt, unsigned char *strptr, unsigned char *strtop, int4 repmin, int4 repmax, int totchar, int repcnt, + int4 min_incr, int4 max_incr) +{ + boolean_t fixed; + int4 alt_tot_min, alt_tot_max, new_pte_csh_size, tmp_do_patalt_calls; + uint4 *cur_alt, tempuint; + uint4 *patptr; + int match, alt_size, charlen, bytelen, pat_found; + mval alt_pat, alt_str; + pte_csh *tmp_pte; + unsigned char *strtmp, *strnext; + + if (PTE_MAX_CURALT_DEPTH > curalt_depth) + { /* try to find it in the current pattern evaluation cache (cur_pte_csh_array) itself */ + tmp_do_patalt_calls = ++do_patalt_calls[curalt_depth]; + pat_found = pte_csh_present((char *)firstalt, (char *)strptr, totchar, repcnt); + if (PTE_NOT_FOUND != pat_found) + { + do_patalt_hits[curalt_depth]++; + return pat_found; + } else if ((tmp_do_patalt_calls > cur_pte_csh_size) + && ((tmp_do_patalt_calls - do_patalt_hits[curalt_depth]) > (tmp_do_patalt_calls / PTE_CSH_MISS_FACTOR))) + { /* lots of cache miss happening. try to increase pt_csh_array size */ + do_patalt_hits[curalt_depth] = do_patalt_calls[curalt_depth] = 1; + new_pte_csh_size = cur_pte_csh_size; + if (cur_pte_csh_size < pte_csh_alloc_size[curalt_depth]) + { + new_pte_csh_size = (cur_pte_csh_size << 1); + assert(cur_pte_csh_size <= pte_csh_alloc_size[curalt_depth]); + } else if (PTE_MAX_ENTRIES > pte_csh_alloc_size[curalt_depth]) + { + new_pte_csh_size = (cur_pte_csh_size << 1); + tmp_pte = malloc(SIZEOF(pte_csh) * new_pte_csh_size); + free(cur_pte_csh_array); + pte_csh_alloc_size[curalt_depth] = new_pte_csh_size; + pte_csh_array[curalt_depth] = tmp_pte; + cur_pte_csh_array = pte_csh_array[curalt_depth]; + } else + do_patalt_maxed_out[curalt_depth]++; + if (new_pte_csh_size != cur_pte_csh_size) + { + memset(pte_csh_array[curalt_depth], 0, SIZEOF(pte_csh) * new_pte_csh_size); + pte_csh_cur_size[curalt_depth] *= 2; + pte_csh_entries_per_len[curalt_depth] *= 2; + pte_csh_tail_count[curalt_depth] *= 2; + UPDATE_CUR_PTE_CSH_MINUS_ARRAY(cur_pte_csh_size, + cur_pte_csh_entries_per_len, cur_pte_csh_tail_count); + } + } + } + alt_pat.mvtype = MV_STR; + alt_str.mvtype = MV_STR; + alt_str.str.addr = (char *)strptr; + patptr = firstalt; + GET_LONG(alt_size, patptr); + patptr++; + for (match = FALSE; !match && alt_size; patptr++) + { + cur_alt = patptr; + cur_alt++; + GET_ULONG(tempuint, cur_alt); + cur_alt++; + cur_alt += tempuint; + GET_LONG(alt_tot_min, cur_alt); + cur_alt++; + if (alt_tot_min <= totchar) + { + GET_LONG(tempuint, cur_alt); + GET_LONG(fixed, patptr); + /* Note that some patterns whose minimum and maximum length are the same need not have + * "fixed" field 1. This is because alternations which have choices that all evaluate + * to the same length (e.g. 5(2l,2e,"ab")) are currently not recognizable by do_patfixed + * and hence go through do_pattern. + */ + assert(!fixed || (alt_tot_min == tempuint)); + alt_tot_max = (tempuint < totchar) ? tempuint : totchar; + alt_pat.str.addr = (char *)patptr; + alt_pat.str.len = alt_size * SIZEOF(uint4); + /* Note that the below zero min length avoiding code is actually an optimization. + * This is because if we start from length 0, we will end up matching the input string and in case + * the alternation pattern's max count is huge (e.g. PAT_MAX) we will end up recursing + * in do_patalt() as many times each time matching a length of 0, without realizing we are + * not progressing anywhere in the match by matching a huge number of empty strings. + * This will effectively cause a combinatorial explosion to occur in case there are at least 2 choices + * in the alternation pattern (which usually will be the case) since the choices that need to be + * examined are 2 ** PAT_MAX. + * Instead, if we start from length 1, every level of recursion we decrease the size of the problem + * by matching a non-zero length of the input string and hence we can't progress much in the + * recursion levels before starting to backtrack, thereby avoiding the explosion. + * Note that we do have to consider zero length in case we haven't yet exhausted our minimum count of + * the alternation pattern and we have a null input string remaining to be matched. + * Hence the if check below. + */ + if (totchar && (0 == alt_tot_min)) + alt_tot_min = 1; /* avoid zero min length when non-zero string still needs to be matched */ + if (!gtm_utf8_mode) + { /* each character is 1 byte so charlen and bytelen is same */ + charlen = alt_tot_min; + bytelen = alt_tot_min; + } + UNICODE_ONLY( + else + { /* skip alt_tot_min characters */ + strtmp = strptr; + for (charlen = 0; charlen < alt_tot_min; charlen++) + { + assert(strtmp < strtop); + strtmp = UTF8_MBNEXT(strtmp, strtop); + } + bytelen = (int)(strtmp - strptr); + } + ) + UNICODE_ONLY( + if (gtm_utf8_mode) + alt_str.mvtype |= MV_UTF_LEN; /* avoid recomputing "char_len" in do_pattern/do_patfixed */ + ) + for ( ; !match && (charlen <= alt_tot_max); charlen++) + { + alt_str.str.len = bytelen; + UNICODE_ONLY( + if (gtm_utf8_mode) + { + assert(utf8_len(&alt_str.str) == charlen); + alt_str.str.char_len = charlen; /* set "char_len" */ + } + ) + match = charlen ? (fixed ? do_patfixed(&alt_str, &alt_pat) + : do_pattern(&alt_str, &alt_pat)) + : TRUE; + /* max_incr and min_incr aid us in an earlier backtracking optimization. + * for example, let us consider "abcdefghijklmnopqrstuvwxyz"?.13(1l,1e,1n,1u,1p,2l) + * say the first do_patalt() call matches a substring (the beginning of the input string) "a" + * with the first alternation choice 1l + * say the recursive second do_patalt() call then matches a substring of the now beginning + * input string "b" with the first alternation choice 1l again + * the recursively called third do_patalt() now can rest assured that the remaining string + * can't be matched by the alternation. This is because it has only 11 chances left + * (note the maximum is .13) and each time the maximum length it can match is 2 (the + * maximum length of all the alternation choices which is 2l) which leaves it with a + * maximum of 22 characters while there are still 24 characters left in the input-string. + * this optimization can cause a backtracking to occur at the 3rd level of call to do_patalt() + * instead of going through the call trace 13 times and then determining at the leaf level. + * since at each level, the choices examined are 6, we are saving nearly (6 to the power of 11) + * choice examinations (11 for the levels that we avoid with the optimization) + */ + if (match && ((charlen < totchar) || (repcnt < repmin))) + match &= ((repcnt < repmax) + && ((totchar - charlen) <= (repmax - repcnt) * max_incr) + && ((totchar - charlen) >= (repmin - repcnt) * min_incr)) + ? do_patalt(firstalt, &strptr[bytelen], strtop, repmin, repmax, + totchar - charlen, repcnt + 1, min_incr, max_incr) + : FALSE; + if (!match) + { /* update "bytelen" to correspond to "charlen + 1" */ + if (!gtm_utf8_mode) + bytelen++; + UNICODE_ONLY( + else + { + assert((strtmp < strtop) || (charlen == alt_tot_max)); + if (strtmp < strtop) + { + strnext = UTF8_MBNEXT(strtmp, strtop); + assert(strnext > strtmp); + bytelen += (int)(strnext - strtmp); + strtmp = strnext; + } + } + ) + } + } + } + patptr += alt_size; + GET_LONG(alt_size, patptr); + } + if (PTE_MAX_CURALT_DEPTH > curalt_depth) + pte_csh_insert((char *)firstalt, (char *)strptr, totchar, repcnt, match); + return match; +} + diff --git a/sr_port/do_patfixed.c b/sr_port/do_patfixed.c new file mode 100644 index 0000000..31acc1f --- /dev/null +++ b/sr_port/do_patfixed.c @@ -0,0 +1,210 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "patcode.h" +#include "copy.h" + +#ifdef UNICODE_SUPPORTED +#include "gtm_icu_api.h" /* needed by *TYPEMASK* macros defined in gtm_utf8.h */ +#include "gtm_utf8.h" +#endif + +GBLDEF char codelist[] = PATM_CODELIST; + +GBLREF uint4 pat_allmaskbits; +GBLREF uint4 *pattern_typemask; +GBLREF boolean_t gtm_utf8_mode; + +/* This procedure executes at "run-time". After a pattern in a MUMPS program has been compiled (by patstr and its + * helper-procedures), this procedure is called to evaluate "fixed-length" patterns. + * i.e. for each pattern atom, the lower-bound is equal to the upper-bound such as 3N2A5N. + * For patterns with a variable length, procedure do_pattern() is called to do the evaluation. + */ + +int do_patfixed(mval *str, mval *pat) +{ + int4 count, tempint; + int4 *min, *reptr, *rtop; + int4 repeat; + int bit; + int letter; + int repcnt; + int bytelen, charlen, pbytelen, strbytelen; + unsigned char *strptr, *strtop, *strnext, *pstr, *ptop, *pnext; + uint4 code, tempuint, patstream_len; + uint4 *patptr; + uint4 mbit; + char buf[CHAR_CLASSES]; + boolean_t flags, pvalid, strvalid; + UNICODE_ONLY( + wint_t utf8_codepoint; + ) + + error_def(ERR_PATNOTFOUND); + + /* set up information */ + MV_FORCE_STR(str); + patptr = (uint4 *)pat->str.addr; + DEBUG_ONLY( + GET_ULONG(tempuint, patptr); + assert(tempuint); /* ensure first uint4 is non-zero indicating fixed length pattern string */ + ) + patptr++; + GET_ULONG(tempuint, patptr); + DEBUG_ONLY(patstream_len = tempuint); + patptr += tempuint; + GET_LONG(count, patptr); + assert(MAX_PATTERN_ATOMS > count); + patptr++; + GET_ULONG(tempuint, patptr); + patptr++; + if (!gtm_utf8_mode) + charlen = str->str.len; + UNICODE_ONLY( + else + { + MV_FORCE_LEN(str); /* to set str.char_len if not already done; also issues BADCHAR error if appropriate */ + charlen = str->str.char_len; + } + ) + if (tempuint != charlen) + return FALSE; + patptr++; + min = (int4 *)patptr; + rtop = min + count; /* Note: the compiler generates: rtop = min + SIZEOF(int4) * count */ + + /* attempt a match */ + strptr = (unsigned char *)str->str.addr; + strtop = &strptr[str->str.len]; + patptr = (uint4 *)pat->str.addr; + patptr += 2; + for (reptr = min; reptr < rtop ; reptr++) + { + GET_LONG(repeat, reptr); + GET_ULONG(code, patptr); + assert(code); + patptr++; + if (!(code & PATM_STRLIT)) + { /* meta character pat atom */ + if (!(code & pat_allmaskbits)) + { /* current table has no characters with this pattern code */ + bytelen = 0; + for (bit = 0; bit < PAT_MAX_BITS; bit++) + { + mbit = (1 << bit); + if ((mbit & code & PATM_LONGFLAGS) && !(mbit & pat_allmaskbits)) + buf[bytelen++] = codelist[patmaskseq(mbit)]; + } + rts_error(VARLSTCNT(4) ERR_PATNOTFOUND, 2, bytelen, buf); + } + if (!gtm_utf8_mode) + { + for (repcnt = 0; repcnt < repeat; repcnt++) + { + if (!(code & pattern_typemask[*strptr++])) + return FALSE; + } + } + UNICODE_ONLY( + else + { + for (repcnt = 0; repcnt < repeat; repcnt++) + { + assert(strptr < strtop); /* PATTERN_TYPEMASK macro relies on this */ + if (!(code & PATTERN_TYPEMASK(strptr, strtop, strnext, utf8_codepoint))) + return FALSE; + strptr = strnext; + } + } + ) + } else + { /* STRLIT pat atom */ + assert(3 == PAT_STRLIT_PADDING); + GET_LONG(bytelen, patptr); /* get bytelen */ + patptr++; + GET_LONG(charlen, patptr); /* get charlen */ + patptr++; + GET_ULONG(flags, patptr); /* get falgs */ + patptr++; + assert(!(flags & PATM_STRLIT_BADCHAR)); + /* ensure pattern atom length is within limits of the complete pattern stream */ + assert((0 <= bytelen) + && ((patptr + DIVIDE_ROUND_UP(bytelen, SIZEOF(*patptr))) + <= ((uint4 *)(pat->str.addr) + patstream_len + 2))); + pstr = (unsigned char *)patptr; + if (1 == bytelen) + { + if (!gtm_utf8_mode) + { + for (repcnt = 0; repcnt < repeat; repcnt++) + if (*pstr != *strptr++) + return FALSE; + patptr++; + } + UNICODE_ONLY( + else + { + for (repcnt = 0; repcnt < repeat; repcnt++) + { + if ((1 != (UTF8_VALID(strptr, strtop, bytelen), bytelen)) || (*pstr != *strptr++)) + return FALSE; + } + patptr++; + } + ) + } else if (bytelen > 0) + { + if (!gtm_utf8_mode) + { + for (repcnt = 0; repcnt < repeat; repcnt++) + for (letter = 0, pstr = (unsigned char *)patptr; letter < bytelen; letter++) + if (*pstr++ != *strptr++) + return FALSE; + patptr += DIVIDE_ROUND_UP(bytelen, SIZEOF(*patptr)); + } + UNICODE_ONLY( + else + { + pstr = (unsigned char *)patptr; + ptop = pstr + bytelen; + for (repcnt = 0; repcnt < repeat; repcnt++) + { + pstr = (unsigned char *)patptr; + for ( ; pstr < ptop; ) + { + pvalid = UTF8_VALID(pstr, ptop, pbytelen); /* sets pbytelen */ + assert(pvalid); + strvalid = UTF8_VALID(strptr, strtop, strbytelen); /* sets strbytelen */ + if (pbytelen != strbytelen) + return FALSE; + else + { + DEBUG_ONLY(strnext = strptr + pbytelen); + pnext = pstr + pbytelen; + do + { + if (*pstr++ != *strptr++) + return FALSE; + } while (pstr < pnext); + assert(strptr == strnext); + } + } + } + patptr += DIVIDE_ROUND_UP(bytelen, SIZEOF(*patptr)); + } + ) + } + } + } + return TRUE; +} diff --git a/sr_port/do_patsplit.c b/sr_port/do_patsplit.c new file mode 100644 index 0000000..5eda234 --- /dev/null +++ b/sr_port/do_patsplit.c @@ -0,0 +1,362 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "patcode.h" +#include "copy.h" +#include "min_max.h" +#include "gtm_string.h" + +#ifdef UNICODE_SUPPORTED +#include "gtm_utf8.h" +#endif + +GBLREF boolean_t gtm_utf8_mode; + +/* This routine tries to split the pattern-string P into substrings LFR where F is a fixed length pattern-atom of P + * and L and R are the pattern substrings in P on the left and right side of F. + * For pattern-strings that have more than one fixed-length pattern-atom, the one closest to the median is selected. + * Once F is determined, do_patfixed() is invoked to determine all possible matches of F with the input string. + * do_patsplit() has additional optimizations to try match the fixed length pattern in a restricted subset of the + * input string taking into account the min and max of the left and the right pattern. + * For each such match, an attempt is made to match L and R with the left and right side of the input string. + * This is done using do_patfixed() or do_pattern() appropriately. + * If any such attempt succeeds, we return TRUE. + * If no attempt succeeds, we return FALSE. + * If the pattern-string P doesn't contain any fixed-length pattern-atom, we return DO_PATSPLIT_FAIL + */ + +int do_patsplit(mval *str, mval *pat) +{ + int4 count, total_min, total_max; + int4 min[MAX_PATTERN_ATOMS], max[MAX_PATTERN_ATOMS], size[MAX_PATTERN_ATOMS]; + int4 bytelen, charlen, charstoskip, fixedcharlen, leftcharlen, rightcharlen, deltalen, numchars; + int4 strbytelen, strcharlen; + int4 alt_rep_min, alt_rep_max; + int4 alt; + uint4 tempuint; + uint4 code, flags; + uint4 *patptr, *patptr_start, *patptr_end, *fixed_patptr, *right_patptr, *tmp_patptr; + ptstr left_ptstr, right_ptstr, fixed_ptstr; + mval left_pat, right_pat, fixed_pat, left_str, right_str, fixed_str; + int4 index, fixed_index; /* index of our current fixed-length pattern-atom */ + boolean_t right; /* 0 indicates we are processing left side, 1 indicates right side */ + boolean_t fixed[2]; /* fixed[0] is for the left, fixed[1] is for the right */ + int4 tot_min[2], tot_max[2], cnt[2]; /* index 0 is for left, index 1 is for right */ + int4 offset; + unsigned char *strptr, *strtop, *rightptr, *rightnext, *fixedptr, *fixednext, *maxfixedptr; + boolean_t match; /* match status of input pattern with input string */ + gtm_uint64_t bound; + + MV_FORCE_STR(str); + patptr = (uint4 *)pat->str.addr; + DEBUG_ONLY( + GET_ULONG(tempuint, patptr); + assert(!tempuint); + ) + patptr++; + patptr_start = patptr + 1; + GET_ULONG(tempuint, patptr); + patptr += tempuint; + patptr_end = patptr; + GET_LONG(count, patptr); + patptr++; + GET_LONG(total_min, patptr); + patptr++; + GET_LONG(total_max, patptr); + patptr++; + UNICODE_ONLY( + if (gtm_utf8_mode) + { + MV_FORCE_LEN(str); /* to set str.char_len if not already done */ + assert(str->str.char_len >= total_min && str->str.char_len <= total_max); + } + ) + assert(count <= MAX_PATTERN_ATOMS); + memcpy(&min[0], patptr, SIZEOF(*patptr) * count); + patptr += count; + memcpy(&max[0], patptr, SIZEOF(*patptr) * count); + patptr += count; + memcpy(&size[0], patptr, SIZEOF(*patptr) * count); + patptr = patptr_start; + right = FALSE; /* start with left side */ + fixed[right] = TRUE; + tot_min[right] = tot_max[right] = 0; + fixed_patptr = right_patptr = NULL; + fixed_index = -1; + + for (index = 0; index < count; index++) + { + GET_ULONG(code, patptr); + tmp_patptr = patptr; + patptr++; + if (code & PATM_ALT) + { /* skip to the next pattern-atom */ + GET_LONG(alt_rep_min, patptr); + patptr++; + GET_LONG(alt_rep_max, patptr); + patptr++; + GET_LONG(alt, patptr); + patptr++; + while(alt) + { + patptr += alt; + GET_LONG(alt, patptr); + patptr++; + } + fixed[right] = FALSE; + /* patptr now points to the next patcode after the alternation */ + } else if (code == PATM_DFA) + { /* Discrete Finite Automaton pat atom */ + assert(min[index] != max[index]); /* DFA should never be fixed length */ + GET_LONG(bytelen, patptr); + patptr++; + patptr += bytelen; + fixed[right] = FALSE; + } else + { + if (code & PATM_STRLIT) + { /* STRLIT pat atom */ + assert(3 == PAT_STRLIT_PADDING); + GET_LONG(bytelen, patptr); + patptr++; + GET_LONG(charlen, patptr); + patptr++; + GET_ULONG(flags, patptr); + patptr++; + patptr += DIVIDE_ROUND_UP(bytelen, SIZEOF(*patptr)); + } + if ((min[index] == max[index]) && (bound = (gtm_uint64_t)min[index] * size[index])) + { /* fixed_length */ + if (ABS(index - (count / 2)) < ABS(fixed_index - (count / 2))) + { /* non-zero fixed length pattern with a fixed_index closer to the median of the array */ + if (right) + { /* update left's tot_min and tot_max to reflect the new fixed_index */ + tot_min[0] += tot_min[right] + + BOUND_MULTIPLY(min[fixed_index], size[fixed_index], bound); + if (tot_min[0] > PAT_MAX_REPEAT) + tot_min[0] = PAT_MAX_REPEAT; + tot_max[0] += tot_max[right] + + BOUND_MULTIPLY(max[fixed_index], size[fixed_index], bound); + if (tot_max[0] > PAT_MAX_REPEAT) + tot_max[0] = PAT_MAX_REPEAT; + fixed[0] &= fixed[right]; + } + fixed_index = index; + right = TRUE; + fixed[right] = TRUE; + tot_min[right] = tot_max[right] = 0; + fixed_patptr = tmp_patptr; + right_patptr = patptr; + continue; + } + } else + fixed[right] = FALSE; + } + tot_min[right] += BOUND_MULTIPLY(min[index], size[index], bound); + if (tot_min[right] > PAT_MAX_REPEAT) + tot_min[right] = PAT_MAX_REPEAT; + tot_max[right] += BOUND_MULTIPLY(max[index], size[index], bound); + if (tot_max[right] > PAT_MAX_REPEAT) + tot_max[right] = PAT_MAX_REPEAT; + } + assert(index == count); + if (-1 == fixed_index) + return DO_PATSPLIT_FAIL; + assert(fixed_index < count); + assert((total_min == (tot_min[0] + tot_min[1] + BOUND_MULTIPLY(min[fixed_index], size[fixed_index], bound))) || + (PAT_MAX_REPEAT == total_min)); + assert((total_max == (tot_max[0] + tot_max[1] + BOUND_MULTIPLY(max[fixed_index], size[fixed_index], bound))) || + (PAT_MAX_REPEAT == total_max)); + cnt[0] = fixed_index; + if (cnt[0]) + { /* left section has at least one pattern atom. create its compilation string */ + patptr = left_ptstr.buff; + *patptr++ = fixed[0]; + *patptr++ = (uint4)(fixed_patptr - patptr_start + 1); + memcpy(patptr, patptr_start, (char *)fixed_patptr - (char *)patptr_start); + patptr += fixed_patptr - patptr_start; + *patptr++ = cnt[0]; + *patptr++ = tot_min[0]; + *patptr++ = tot_max[0]; + for (index = 0; index < cnt[0]; index++) + *patptr++ = min[index]; + if (!fixed[0]) + { + for (index = 0; index < cnt[0]; index++) + *patptr++ = max[index]; + } + for (index = 0; index < cnt[0]; index++) + *patptr++ = size[index]; + left_pat.mvtype = MV_STR; + left_pat.str.len = INTCAST((char *)patptr - (char *)&left_ptstr.buff[0]); + left_pat.str.addr = (char *)&left_ptstr.buff[0]; + } + + /* create fixed length pattern atom's compilation string */ + patptr = fixed_ptstr.buff; + *patptr++ = TRUE; /* fixed length pattern */ + *patptr++ = (uint4)(right_patptr - fixed_patptr + 1); + memcpy(patptr, fixed_patptr, (char *)right_patptr - (char *)fixed_patptr); + patptr += right_patptr - fixed_patptr; + *patptr++ = 1; /* count */ + fixedcharlen = min[fixed_index] * size[fixed_index]; /* tot_min and tot_max */ + *patptr++ = fixedcharlen; + *patptr++ = fixedcharlen; + *patptr++ = min[fixed_index]; /* min[0] */ + *patptr++ = size[fixed_index]; /* size[0] */ + fixed_pat.mvtype = MV_STR; + fixed_pat.str.len = INTCAST((char *)patptr - (char *)&fixed_ptstr.buff[0]); + fixed_pat.str.addr = (char *)&fixed_ptstr.buff[0]; + + cnt[1] = count - fixed_index - 1; + if (cnt[1]) + { /* right section has at least one pattern atom. create its compilation string */ + patptr = right_ptstr.buff; + *patptr++ = fixed[1]; + *patptr++ = (uint4)(patptr_end - right_patptr + 1); + memcpy(patptr, right_patptr, (char *)patptr_end - (char *)right_patptr); + patptr += patptr_end - right_patptr; + *patptr++ = cnt[1]; + *patptr++ = tot_min[1]; + *patptr++ = tot_max[1]; + for (index = fixed_index + 1; index < count; index++) + *patptr++ = min[index]; + if (!fixed[1]) + { + for (index = fixed_index + 1; index < count; index++) + *patptr++ = max[index]; + } + for (index = fixed_index + 1; index < count; index++) + *patptr++ = size[index]; + right_pat.mvtype = MV_STR; + right_pat.str.len = INTCAST((char *)patptr - (char *)&right_ptstr.buff[0]); + right_pat.str.addr = (char *)&right_ptstr.buff[0]; + } + strbytelen = str->str.len; + strptr = (unsigned char *)str->str.addr; + strtop = strptr + strbytelen; + if (!gtm_utf8_mode) + strcharlen = str->str.len; + UNICODE_ONLY( + else + strcharlen = str->str.char_len; + ) + /* Determine "maxfixedptr" */ + if (strcharlen > (tot_min[1] + tot_max[0] + fixedcharlen)) + charstoskip = tot_max[0]; + else + charstoskip = strcharlen - tot_min[1] - fixedcharlen; + if (!gtm_utf8_mode) + strptr += charstoskip; + UNICODE_ONLY( + else + { + for ( ; 0 < charstoskip; charstoskip--) + { + assert(strptr < strtop); /* below macro relies on this */ + strptr = UTF8_MBNEXT(strptr, strtop); + } + } + ) + maxfixedptr = strptr; + /* Determine "fixedptr" */ + strptr = (unsigned char *)str->str.addr; + if (strcharlen > (tot_min[0] + tot_max[1] + fixedcharlen)) + charstoskip = strcharlen - tot_max[1] - fixedcharlen; + else + charstoskip = tot_min[0]; + leftcharlen = charstoskip; + if (!gtm_utf8_mode) + strptr += charstoskip; + UNICODE_ONLY( + else + { + for ( ; 0 < charstoskip; charstoskip--) + { + assert(strptr < strtop); /* below macro relies on this */ + strptr = UTF8_MBNEXT(strptr, strtop); + } + } + ) + fixedptr = strptr; + /* Set "left_str" */ + left_str.mvtype = MV_STR; + left_str.str.addr = str->str.addr; + /* Set "right_str" */ + right_str.mvtype = MV_STR; + /* Set "fixed_str" */ + fixed_str.mvtype = MV_STR; + if (!gtm_utf8_mode) + { + fixed_str.str.len = fixedcharlen; + rightptr = fixedptr + fixedcharlen; + } + UNICODE_ONLY( + else + { + left_str.mvtype |= MV_UTF_LEN; /* avoid recomputing "char_len" in do_patfixed below */ + right_str.mvtype |= MV_UTF_LEN; /* avoid recomputing "char_len" in do_patfixed below */ + fixed_str.mvtype |= MV_UTF_LEN; /* avoid recomputing "char_len" in do_patfixed below */ + fixed_str.str.char_len = fixedcharlen; + /* skip fixedcharlen characters */ + assert(fixedptr == strptr); + for (numchars = 0; numchars < fixedcharlen; numchars++) + { + assert(strptr < strtop); + strptr = UTF8_MBNEXT(strptr, strtop); + } + rightptr = strptr; + } + ) + deltalen = 0; + /* Try to match the fixed pattern string and for each match, try matching the left and right input strings */ + for (match = FALSE; !match && (fixedptr <= maxfixedptr); fixedptr = fixednext, rightptr = rightnext, leftcharlen++) + { + fixed_str.str.addr = (char *)fixedptr; + fixed_str.str.len = INTCAST(rightptr - fixedptr); + if (!gtm_utf8_mode) + { + fixednext = fixedptr + 1; + rightnext = rightptr + 1; + } + UNICODE_ONLY( + else + { + assert(fixedptr < strtop); + fixednext = UTF8_MBNEXT(fixedptr, strtop); + assert((rightptr < strtop) || (fixedptr == maxfixedptr) && (fixednext > maxfixedptr)); + if (rightptr < strtop) + rightnext = UTF8_MBNEXT(rightptr, strtop); + } + ) + if (!do_patfixed(&fixed_str, &fixed_pat)) + continue; + assert(cnt[0] || cnt[1]); /* fixed_pat takes only one pattern atom and non-zero rest are in cnt[0] and cnt[1] */ + if (cnt[0]) + { + left_str.str.len = INTCAST(fixedptr - (unsigned char *)left_str.str.addr); + UNICODE_ONLY(left_str.str.char_len = leftcharlen;) + match = fixed[0] ? do_patfixed(&left_str, &left_pat) : do_pattern(&left_str, &left_pat); + if (!match) + continue; + } + if (cnt[1]) + { + right_str.str.addr = (char *)rightptr; + UNICODE_ONLY(right_str.str.char_len = strcharlen - leftcharlen - fixedcharlen;) + right_str.str.len = INTCAST(strtop - rightptr); + match = (fixed[1] ? do_patfixed(&right_str, &right_pat) : do_pattern(&right_str, &right_pat)); + } + } + return match; +} diff --git a/sr_port/do_pattern.c b/sr_port/do_pattern.c new file mode 100644 index 0000000..8065b11 --- /dev/null +++ b/sr_port/do_pattern.c @@ -0,0 +1,469 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "patcode.h" +#include "copy.h" +#include "min_max.h" + +#ifdef UNICODE_SUPPORTED +#include "gtm_icu_api.h" /* needed by *TYPEMASK* macros defined in gtm_utf8.h */ +#include "gtm_utf8.h" +#endif + +GBLREF uint4 pat_allmaskbits; +GBLREF uint4 *pattern_typemask; +GBLREF char codelist[]; +GBLREF int4 curalt_depth; /* depth of alternation nesting */ +GBLREF int4 do_patalt_calls[PTE_MAX_CURALT_DEPTH]; /* number of calls to do_patalt() */ +GBLREF int4 do_patalt_hits[PTE_MAX_CURALT_DEPTH]; /* number of pte_csh hits in do_patalt() */ +GBLREF pte_csh *pte_csh_array[PTE_MAX_CURALT_DEPTH]; /* pte_csh array (per curalt_depth) */ +GBLREF int4 pte_csh_cur_size[PTE_MAX_CURALT_DEPTH]; /* current pte_csh size (per curalt_depth) */ +GBLREF int4 pte_csh_alloc_size[PTE_MAX_CURALT_DEPTH]; /* current allocated pte_csh size (per curalt_depth) */ +GBLREF int4 pte_csh_entries_per_len[PTE_MAX_CURALT_DEPTH]; /* current number of entries per len */ +GBLREF int4 pte_csh_tail_count[PTE_MAX_CURALT_DEPTH]; /* count of non 1-1 corresponding pte_csh_array members */ +GBLREF pte_csh *cur_pte_csh_array; /* copy of pte_csh_array corresponding to curalt_depth */ +GBLREF int4 cur_pte_csh_size; /* copy of pte_csh_cur_size corresponding to curalt_depth */ +GBLREF int4 cur_pte_csh_entries_per_len; /* copy of pte_csh_entries_per_len corresponding to curalt_depth */ +GBLREF int4 cur_pte_csh_tail_count; /* copy of pte_csh_tail_count corresponding to curalt_depth */ +GBLREF boolean_t gtm_utf8_mode; + +/* This procedure executes at "run-time". After a pattern in a MUMPS program has been compiled (by patstr and + * its helper-procedures), this procedure can be called to evaluate "variable-length" patterns. + * Variable-length patterns are of the kind 3.5N2.A.5N i.e. for at least one pattern atom, + * the lower-bound is different from the upper-bound. + * For patterns with a fixed length, procedure do_patfixed() will be called to do the evaluation. + * Variable length input patterns will be scanned at runtime to see if they have at least one fixed length pattern atom. + * If yes, routine do_patsplit() will be invoked to determine the fixed length sub pattern atom that is closest to the + * median of the pattern atoms. The input pattern would then be split into three, left, fixed and right. + * The fixed pattern can be matched by a linear scan in the input string. Once that is done, the left and right + * pattern atom positions are pivoted relative to the input string and do_pattern() is invoked on each of them recursively. + * If no fixed length pattern atoms can be found, then do_pattern() calculates all possible permutations (it has certain + * optimizations to prune the combinatorial search tree) and tries to see for each permutation if a match occurs. + */ + +int do_pattern(mval *str, mval *pat) +{ + int4 count, total_min, total_max; + int4 alt_rep_min, alt_rep_max, min_incr, max_incr; + int4 bytelen, charlen, length, pbytelen, strbytelen; + boolean_t success, attempt, pvalid, strvalid; + int atom, unit, idx, index, hasfixed; + uint4 z_diff, *rpt, *rtop, rept; + uint4 repeat[MAX_PATTERN_ATOMS]; + uint4 *patidx[MAX_PATTERN_ATOMS]; + unsigned char *stridx[MAX_PATTERN_ATOMS]; + unsigned char *strptr, *strtop, *strnext, *pstr, *ptop, *pnext; + uint4 code, tempuint; + uint4 *dfa_ptr, dfa_val; + uint4 *patptr; + uint4 mbit, flags; + int4 *min, *max, *size; + int4 mintmp, maxtmp, sizetmp; + int alt, bit; + char buf[CHAR_CLASSES]; + boolean_t pte_csh_init; + boolean_t match; + UNICODE_ONLY( + wint_t utf8_codepoint; + ) + + error_def(ERR_PATNOTFOUND); + + /* set up information */ + MV_FORCE_STR(str); + patptr = (uint4 *) pat->str.addr; + GET_ULONG(tempuint, patptr); + if (tempuint) + { /* tempuint non-zero implies fixed length pattern string. this in turn implies we are not called from op_pattern.s + * but instead called from op_fnzsearch(), gvzwr_fini(), gvzwr_var(), lvzwr_fini(), lvzwr_var() etc. + * in this case, call do_patfixed() as the code below and code in do_patsplit() assumes we are dealing with a + * variable length pattern string. changing all the callers to call do_patfixed() directly instead of this extra + * redirection was considered, but not felt worth it since the call to do_pattern() is not easily macroizable + * due to the expression-like usage in those places. + */ + return do_patfixed(str, pat); + } + patptr++; + patidx[0] = patptr + 1; + stridx[0] = (unsigned char *)str->str.addr; + strtop = stridx[0] + str->str.len; + GET_ULONG(tempuint, patptr); + patptr += tempuint; + GET_LONG(count, patptr); + patptr++; + GET_LONG(total_min, patptr); + patptr++; + GET_LONG(total_max, patptr); + patptr++; + /* "length" actually denotes character length; Get it from the appropriate field in the mstr */ + if (!gtm_utf8_mode) + length = str->str.len; + UNICODE_ONLY( + else + { + MV_FORCE_LEN(str); /* to set str.char_len if not already done; also issues BADCHAR error if appropriate */ + length = str->str.char_len; + } + ) + if (length < total_min || length > total_max) + return FALSE; + min = (int4 *)patptr; + patptr += count; + max = (int4 *)patptr; + patptr += count; + if (MIN_SPLIT_N_MATCH_COUNT <= count) + { + hasfixed = FALSE; + for (index = 0; index < count; index++) + { + GET_LONG(maxtmp, max + index); + GET_LONG(mintmp, min + index); + if (maxtmp == mintmp) + { + hasfixed = TRUE; + break; + } + } + if (hasfixed && (DO_PATSPLIT_FAIL != (match = do_patsplit(str, pat)))) + return match; + } + size = (int4 *)patptr; + memcpy(repeat, min, count * SIZEOF(*min)); + rtop = &repeat[0] + count; + count--; + attempt = FALSE; + idx = 0; + pte_csh_init = FALSE; + /* proceed to check string */ + for (;;) + { + if (total_min == length) + { /* attempt a match */ + attempt = TRUE; + strptr = stridx[idx]; + patptr = patidx[idx]; + rpt = &repeat[idx]; + for (; rpt < rtop; rpt++) + { + GET_ULONG(code, patptr); + patptr++; + rept = *rpt; + if (code & PATM_ALT) + { /* pattern alternation */ + GET_LONG(alt_rep_min, patptr); + patptr++; + GET_LONG(alt_rep_max, patptr); + patptr++; + GET_LONG(maxtmp, max + idx); + GET_LONG(mintmp, min + idx); + assert(alt_rep_min || !mintmp); + assert(alt_rep_max || !maxtmp); + min_incr = mintmp ? mintmp / alt_rep_min : 0; + max_incr = maxtmp ? maxtmp / alt_rep_max : 0; + if (rept) + { + if (FALSE == pte_csh_init) + { + PTE_CSH_INCR_CURALT_DEPTH(curalt_depth); + pte_csh_init = TRUE; + } + if (do_patalt(patptr, strptr, strtop, alt_rep_min, alt_rep_max, rept, 1, + min_incr, max_incr)) + { + if (!gtm_utf8_mode) + strptr += rept; + UNICODE_ONLY( + else + { + for (unit = 0; unit < rept; unit++) + { + assert(strptr < strtop); /* below macro relies on this */ + strptr = UTF8_MBNEXT(strptr, strtop); + } + } + ) + } else + goto CALC; + } + /* make sure that patptr points to the next patcode after the alternation */ + GET_LONG(alt, patptr); + patptr++; + while(alt) + { + patptr += alt; + GET_LONG(alt, patptr); + patptr++; + } + } else if (!(code & PATM_STRLIT)) + { /* meta character pat atom */ + if (!(code & pat_allmaskbits)) + { /* current table has no characters with this pattern code */ + bytelen = 0; + for (bit = 0; bit < PAT_MAX_BITS; bit++) + { + mbit = (1 << bit); + if ((mbit & code & PATM_LONGFLAGS) && !(mbit & pat_allmaskbits)) + buf[bytelen++] = codelist[patmaskseq(mbit)]; + } + rts_error(VARLSTCNT(4) ERR_PATNOTFOUND, 2, bytelen, buf); + } + if (!gtm_utf8_mode) + { + for (unit = 0; unit < rept; unit++) + { + if (!(code & pattern_typemask[*strptr++])) + goto CALC; + } + } + UNICODE_ONLY( + else + { + for (unit = 0; unit < rept; unit++) + { + assert(strptr < strtop); /* PATTERN_TYPEMASK macro relies on this */ + if (!(code & PATTERN_TYPEMASK(strptr, strtop, strnext, utf8_codepoint))) + goto CALC; + strptr = strnext; + } + } + ) + } else if (code == PATM_DFA) + { /* Discrete Finite Automaton pat atom */ + GET_LONG(bytelen, patptr); + patptr++; + dfa_ptr = patptr; + for (unit = 0; unit < rept; ) + { + GET_ULONG(dfa_val, dfa_ptr); + if (!(dfa_val & PATM_STRLIT)) + { + if (!gtm_utf8_mode) + { + success = (dfa_val & pattern_typemask[*strptr]); + strnext = strptr + 1; + } + UNICODE_ONLY( + else + { + success = (dfa_val & + PATTERN_TYPEMASK(strptr, strtop, strnext, utf8_codepoint)); + } + ) + } else + { + dfa_ptr++; + GET_ULONG(dfa_val, dfa_ptr); + /* Only ASCII characters are currently allowed for DFA STRLITs. + * Assert that below. + */ + assert(IS_ASCII(dfa_val)); + if (!gtm_utf8_mode) + { + success = (dfa_val == *strptr); + strnext = strptr + 1; + } + UNICODE_ONLY( + else + { + UTF8_VALID(strptr, strtop, strbytelen); + success = ((1 == strbytelen) && (dfa_val == *strptr)); + strnext = strptr + strbytelen; + } + ) + } + dfa_ptr++; + if (success) + { + GET_ULONG(dfa_val, dfa_ptr); + dfa_ptr = patptr + dfa_val; + strptr = strnext; + unit++; + GET_ULONG(dfa_val, dfa_ptr); + if (dfa_val == PATM_ACS) + break; + } else + { + dfa_ptr++; + GET_ULONG(dfa_val, dfa_ptr); + if ((dfa_val & PATM_DFA) == PATM_DFA) + break; + } + } + if (unit < rept) + goto CALC; + else + { + GET_ULONG(dfa_val, dfa_ptr); + while (dfa_val < PATM_DFA) + { + if (dfa_val & PATM_STRLIT) + dfa_ptr += 3; + else + dfa_ptr += 2; + GET_ULONG(dfa_val, dfa_ptr); + } + if (dfa_val != PATM_ACS) + goto CALC; + } + patptr += bytelen; + } else + { /* STRLIT pat atom */ + assert(3 == PAT_STRLIT_PADDING); + GET_LONG(bytelen, patptr); /* get bytelen */ + patptr++; + GET_LONG(charlen, patptr); /* get charlen */ + patptr++; + GET_ULONG(flags, patptr); /* get flags */ + patptr++; + if (bytelen == 1) + { + if (!gtm_utf8_mode) + { + for (unit = 0; unit < rept; unit++) + { + if (*(unsigned char *)patptr != *strptr++) + goto CALC; + } + } + UNICODE_ONLY( + else + { + for (unit = 0; unit < rept; unit++) + { + if ((1 != (UTF8_VALID(strptr, strtop, strbytelen), strbytelen)) + || (*(unsigned char *)patptr != *strptr++)) + goto CALC; + } + } + ) + patptr++; + } else if (bytelen > 0) + { + if (!gtm_utf8_mode) + { + ptop = (unsigned char *)patptr + bytelen; + for (unit = 0; unit < rept; unit++) + { + pstr = (unsigned char *)patptr; + while (pstr < ptop) + { + if (*pstr++ != *strptr++) + goto CALC; + } + } + } + UNICODE_ONLY( + else + { + pstr = (unsigned char *)patptr; + ptop = pstr + bytelen; + for (unit = 0; unit < rept; unit++) + { + pstr = (unsigned char *)patptr; + for ( ; pstr < ptop; ) + { + pvalid = UTF8_VALID(pstr, ptop, pbytelen); + /* sets pbytelen */ + assert(pvalid); + strvalid = UTF8_VALID(strptr, strtop, strbytelen); + /* sets strbytelen */ + if (pbytelen != strbytelen) + goto CALC; + DEBUG_ONLY(strnext = strptr + pbytelen); + pnext = pstr + pbytelen; + do + { + if (*pstr++ != *strptr++) + goto CALC; + } while (pstr < pnext); + assert(strptr == strnext); + } + } + } + ) + patptr += DIVIDE_ROUND_UP(bytelen, SIZEOF(*patptr)); + } + } + idx++; + stridx[idx] = strptr; + patidx[idx] = patptr; + } + if (pte_csh_init) + { /* surrounded by braces since the following is a multi-line macro */ + PTE_CSH_DECR_CURALT_DEPTH(curalt_depth); + } + return TRUE; + } else + { /* calculate permutations */ + attempt = FALSE; + GET_LONG(maxtmp, max + count); + GET_LONG(mintmp, min + count); + GET_LONG(sizetmp, size + count); + if (repeat[count] < maxtmp) + { + atom = unit = length - total_min; + z_diff = maxtmp - mintmp; + if (sizetmp > 1) + { + unit /= sizetmp; + atom = unit * sizetmp; + z_diff *= sizetmp; + } + if (atom > 0) + { + total_min += MIN(atom, z_diff); + repeat[count] = MIN(repeat[count] + unit, maxtmp); + if (total_min == length) + continue; + } + } + } + +CALC: unit = count; + GET_LONG(sizetmp, size + unit); + for ( ; ; ) + { + GET_LONG(mintmp, min + unit); + total_min -= (repeat[unit] - mintmp) * sizetmp; + repeat[unit] = mintmp; + unit--; + if (unit < 0) + { + if (pte_csh_init) + { /* surrounded by braces since the following is a multi-line macro */ + PTE_CSH_DECR_CURALT_DEPTH(curalt_depth); + } + return FALSE; + } + GET_LONG(maxtmp, max + unit); + GET_LONG(sizetmp, size + unit); + if (repeat[unit] < maxtmp) + { + total_min += sizetmp; + repeat[unit]++; + if (total_min <= length) + { + if (unit <= idx) + { + idx = unit; + break; + } + if (!attempt) + break; + } + } + } + } +} diff --git a/sr_port/do_xform.h b/sr_port/do_xform.h new file mode 100644 index 0000000..345bac2 --- /dev/null +++ b/sr_port/do_xform.h @@ -0,0 +1,34 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef DO_XFORM_INCLUDED +#define DO_XFORM_INCLUDED + +#define DO_XFORM_RETURN_IF_NULL_STRING(INPUT, OUTPUT, LENGTH) \ +{ \ + if (!INPUT->len) \ + { /* If input string is the null subscript, we want the output (of the collation transformation function) \ + * to be a null subscript as well. This is because the null subscript has special meaning as far as \ + * GT.M subscript collation is concerned. In case the collation routine decides to map it to a non-null \ + * subscript the user might start seeing undesirable collation orders. To be safe and avoid such \ + * issues, the null subscript is handled specially by mapping it to a null subscript internally by GT.M \ + * (without passing this to the collation routine) and returning right away. \ + */ \ + OUTPUT->len = 0; \ + *LENGTH = 0; \ + } \ +} + +void do_xform(collseq *csp, int fc_type, mstr *input, mstr *output, int *length); +/* + * fc_type would be either XFORM (0) or XBACK (1) + */ +#endif /* DO_XFORM_INCLUDED */ diff --git a/sr_port/dollar_quit.c b/sr_port/dollar_quit.c new file mode 100644 index 0000000..fddfd0e --- /dev/null +++ b/sr_port/dollar_quit.c @@ -0,0 +1,271 @@ +/**************************************************************** + * * + * Copyright 2010, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "rtnhdr.h" +#include "stack_frame.h" +#include "op.h" +#include "get_ret_targ.h" +#include "xfer_enum.h" +#include "dollar_quit.h" +#if defined(__sparc) +# include "sparc.h" +#elif defined(__s390__) || defined(__MVS__) +# include "s390.h" +#elif defined(__hppa) +# include "hppa.h" +#elif defined(__ia64) +# include "ia64.h" +#endif + +GBLREF int process_exiting; + +/* Determine value to return for $QUIT: + * + * 0 - no return value requested + * 1 - non-alias return value requested + * 3 - alias return value requrested + * + * Determination of parm/no-parm is made by calling get_ret_targ() which searches the mv_stents + * on the stack back to a counted frame to see of an MVST_PARM block containg a return mval was pushed + * onto the stack signifying a return value is required. If a return value is required, determination of + * the type of return value is made by examining the generated instruction stream at the return point + * and checking for an OC_EXFUNRET or OC_EXFUNRETALS (non-alias and alias type return var processor + * respectively) opcode following the return point. This is done by isolating the instruction that + * indexes into the transfer table, extracting the xfer-table index and checking against known values + * for op_exfunret and op_exfunretals to determine type of return. No match means no return value. + * + * Because this routine looks at the generated code stream at the return point, it is highly platform + * dependent. + * + * Note: If generated code changes for a platform, this module needs to be revisited. + */ +int dollar_quit(void) +{ + stack_frame *sf; + mval *parm_blk; + int retval; + int xfer_index; + + union + { + unsigned char *instr; + unsigned short *instr_type; + unsigned char *instr_type_8; + unsigned char *xfer_offset_8; + short *xfer_offset_16; + int *xfer_offset_32; + } ptrs; + + parm_blk = get_ret_targ(&sf); + if (NULL == parm_blk) + /* There was no parm block - return 0 */ + retval = 0; + else + { /* There is a parm block - see if they want a "regular" or alias type return argument */ + sf = sf->old_frame_pointer; /* Caller's frame */ +# ifdef __i386 + { + ptrs.instr = sf->mpc; + /* First figure out the potential length of the lea* instruction loading compiler temp offset */ + if (0x078d == *ptrs.instr_type) + ptrs.instr += 3; /* Past the 2 byte lea plus 1 byte push */ + else if (0x478d == *ptrs.instr_type) + ptrs.instr += 4; /* Past the 3 byte lea plus 1 byte push */ + else if (0x878d == *ptrs.instr_type) + ptrs.instr += 7; /* Past the 6 byte lea plus 1 byte push */ + else + ptrs.instr = NULL; + /* Note the "long format call opcode" check below assumes that both of the EXFUNRET[ALS] calls remain at a + * greater-than-128 byte offset in the transfer table (which they currently are). + */ + if ((NULL != ptrs.instr) && (0x93FF == *ptrs.instr_type)) + { + ptrs.instr += SIZEOF(*ptrs.instr_type); + xfer_index = *ptrs.xfer_offset_32 / SIZEOF(void *); + } else + xfer_index = -1; + } +# elif defined(__x86_64__) + { + ptrs.instr = sf->mpc; + if (0x8d49 == *ptrs.instr_type) + { + ptrs.instr += 2; /* Past first part of instruction type */ + if (0x7e == *ptrs.instr_type_8) + ptrs.instr += 2; /* past last byte of instruction type plus 1 byte offset */ + else if (0xbe == *ptrs.instr_type_8) + ptrs.instr += 5; /* past last byte of instruction type plus 4 byte offset */ + else + ptrs.instr = NULL; + } else + ptrs.instr_type = NULL; + if ((NULL != ptrs.instr) && (0x93FF == *ptrs.instr_type)) + { /* Long format CALL */ + ptrs.instr += SIZEOF(*ptrs.instr_type); + xfer_index = *ptrs.xfer_offset_32 / SIZEOF(void *); + } else + xfer_index = -1; /* Not an xfer index */ + } +# elif defined(_AIX) + { + ptrs.instr = sf->mpc + 4; /* Past address load of compiler temp arg */ + if (0xE97C == *ptrs.instr_type) + { /* ld of descriptor address from xfer table */ + ptrs.instr += SIZEOF(*ptrs.instr_type); + xfer_index = *ptrs.xfer_offset_16 / SIZEOF(void *); + } else + xfer_index = -1; + } +# elif defined(__alpha) /* Applies to both VMS and Tru64 as have same codegen */ + { + ptrs.instr = sf->mpc + 4; /* Past address load of compiler temp arg */ + if (UNIX_ONLY(0xA36C) VMS_ONLY(0xA36B) == *(ptrs.instr_type + 1)) /* Different code for reg diff */ + /* ldl of descriptor address from xfer table - little endian - offset prior to opcode */ + xfer_index = *ptrs.xfer_offset_16 / SIZEOF(void *); + else + xfer_index = -1; + } +# elif defined(__sparc) + { + ptrs.instr = sf->mpc + 4; /* Past address load of compiler temp arg */ + if (0xC85C == *ptrs.instr_type) + { /* ldx of rtn address from xfer table */ + ptrs.instr += SIZEOF(*ptrs.instr_type); + xfer_index = (*ptrs.xfer_offset_16 & SPARC_MASK_OFFSET) / SIZEOF(void *); + } else + xfer_index = -1; + } +# elif defined(__s390__) || defined(__MVS__) + { + format_RXY instr_LG; + ZOS_ONLY(format_RR instr_RR;) + union + { + int offset; + struct + { /* Used to reassemble the offset in the LG instruction */ + int offset_unused:12; + int offset_hi:8; + int offset_low:12; + } instr_LG_bits; + } RXY; + /* Need to forward space past address load of compiler temp arg. On zOS, the position of the mpc can + * differ. If the origin point is an external call, we have to forward space past the BCR following + * the call point. If the origin point is an internal call, the call point is a branch with no + * following BCR. So zOS needs to determine if it has to jump over a BCR call first. + */ + ZOS_ONLY(memcpy(&instr_RR, sf->mpc, SIZEOF(instr_RR))); + ptrs.instr = sf->mpc; + ZOS_ONLY(if ((S390_OPCODE_RR_BCR == instr_RR.opcode) + && (0 == instr_RR.r1) && (0 == instr_RR.r2)) + ptrs.instr += 2); /* Past BCR 0,0 from external call */ + ptrs.instr += 6; /* Past address load of compiler temp arg */ + memcpy(&instr_LG, ptrs.instr, SIZEOF(instr_LG)); + if ((S390_OPCODE_RXY_LG == instr_LG.opcode) && (S390_SUBCOD_RXY_LG == instr_LG.opcode2) + && (GTM_REG_SAVE_RTN_ADDR == instr_LG.r1) && (GTM_REG_XFER_TABLE == instr_LG.b2)) + { /* LG of rtn address from xfer table */ + RXY.offset = 0; + RXY.instr_LG_bits.offset_hi = instr_LG.dh2; + RXY.instr_LG_bits.offset_low = instr_LG.dl2; + xfer_index = RXY.offset / SIZEOF(void *); + } else + xfer_index = -1; + } +# elif defined(__hppa) + { + hppa_fmt_1 instr_LDX; + union + { + int offset; + struct + { + signed int high:19; + unsigned int low:13; + } instr_offset; + } fmt_1; + + ptrs.instr = sf->mpc + 8; /* Past address load of compiler temp arg plus rtn call to load of xfer + * table call with offset in delay slot */ + memcpy(&instr_LDX, ptrs.instr, SIZEOF(instr_LDX)); + if (((HPPA_INS_LDW >> HPPA_SHIFT_OP) == instr_LDX.pop) && (GTM_REG_XFER_TABLE == instr_LDX.b) + && (R22 == instr_LDX.t)) + { /* ldx of rtn address from xfer table */ + fmt_1.instr_offset.low = instr_LDX.im14a; + fmt_1.instr_offset.high = instr_LDX.im14b; + xfer_index = fmt_1.offset / SIZEOF(void *); + } else + xfer_index = -1; + } +# elif defined(__ia64) + { + ia64_bundle xfer_ref_inst; /* Buffer to put built instruction into */ + ia64_fmt_A4 adds_inst; /* The actual adds instruction computing xfer reference */ + union + { + int offset; + struct + { +# ifdef BIGENDIAN + signed int sign:19; + unsigned int imm6d:6; + unsigned int imm7b:7; +# else + unsigned int imm7b:7; + unsigned int imm6d:6; + signed int sign:19; +# endif + } instr_offset; + } imm14; + + ptrs.instr = sf->mpc + 16; /* Past address load of compiler temp arg */ +# ifdef BIGENDIAN + xfer_ref_inst.hexValue.aValue = GTM_BYTESWAP_64(((ia64_bundle *)ptrs.instr)->hexValue.aValue); + xfer_ref_inst.hexValue.bValue = GTM_BYTESWAP_64(((ia64_bundle *)ptrs.instr)->hexValue.bValue); +# else + xfer_ref_inst.hexValue.aValue = ((ia64_bundle *)ptrs.instr)->hexValue.aValue; + xfer_ref_inst.hexValue.bValue = ((ia64_bundle *)ptrs.instr)->hexValue.bValue; +# endif + adds_inst.hexValue = xfer_ref_inst.format.inst3; /* Extract instruction from bundle */ + if ((8 == adds_inst.format.pop) && (2 == adds_inst.format.x2a) + && (GTM_REG_XFER_TABLE == adds_inst.format.r3) && (IA64_REG_SCRATCH1 == adds_inst.format.r1)) + { /* We have an xfer computation instruction. Find the offset to find which opcode */ + imm14.instr_offset.imm7b = adds_inst.format.imm7b; /* Low order bits */ + imm14.instr_offset.imm6d = adds_inst.format.imm6d; /* upper bits minus sign */ + imm14.instr_offset.sign = adds_inst.format.sb; /* Sign bit propagated */ + xfer_index = imm14.offset / SIZEOF(void *); + } else + xfer_index = -1; + } +# else +# error Unsupported Platform +# endif + if (xf_exfunret == xfer_index) + /* Need a QUIT with a non-alias return value */ + retval = 1; + else if (xf_exfunretals == xfer_index) + /* Need a QUIT with an alias return value */ + retval = 11; + else + { /* Something weird afoot - had parm block can can't locate EXFUNRET[ALS] opcode. This can happen if + * a fatal error occurs during a call before the callee stack frame is actually pushed and we are + * called during GTM_FATAL_ERROR.* file creation. Assert that this is the case, else, we just pretend + * we didn't find a parm block.. + */ + assert(process_exiting); + retval = 0; + } + } + return retval; +} diff --git a/sr_port/dollar_quit.h b/sr_port/dollar_quit.h new file mode 100644 index 0000000..ecb68e5 --- /dev/null +++ b/sr_port/dollar_quit.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef DOLLAR_QUIT_INCLUDE +#define DOLLAR_QUIT_INCLUDE + +int dollar_quit(void); + +#endif diff --git a/sr_port/dollar_system_init.c b/sr_port/dollar_system_init.c new file mode 100644 index 0000000..69b36d4 --- /dev/null +++ b/sr_port/dollar_system_init.c @@ -0,0 +1,62 @@ +/**************************************************************** + * * + * Copyright 2002, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "startup.h" +#include "dollar_system_init.h" +#include "gtm_logicals.h" +#include "io.h" +#include "iosp.h" +#include "stringpool.h" +#include "trans_log_name.h" + +GBLREF mval dollar_system; +GBLREF spdesc stringpool; + +void dollar_system_init(struct startup_vector *svec) +{ + int4 status; + mstr val, tn; + char buf[MAX_TRANS_NAME_LEN]; + + error_def(ERR_LOGTOOLONG); + error_def(ERR_TRNLOGFAIL); + + dollar_system.mvtype = MV_STR; + dollar_system.str.addr = (char *)stringpool.free; + dollar_system.str.len = STR_LIT_LEN("47,"); + memcpy(stringpool.free, "47,", dollar_system.str.len); + stringpool.free += dollar_system.str.len; + val.addr = SYSID; + val.len = STR_LIT_LEN(SYSID); + if (SS_NORMAL == (status = TRANS_LOG_NAME(&val, &tn, buf, SIZEOF(buf), dont_sendmsg_on_log2long))) + { + dollar_system.str.len += tn.len; + memcpy(stringpool.free, tn.addr, tn.len); + stringpool.free += tn.len; + } else if (SS_NOLOGNAM == status) + { + dollar_system.str.len += svec->sysid_ptr->len; + memcpy(stringpool.free, svec->sysid_ptr->addr, svec->sysid_ptr->len); + stringpool.free += svec->sysid_ptr->len ; + } +# ifdef UNIX + else if (SS_LOG2LONG == status) + rts_error(VARLSTCNT(5) ERR_LOGTOOLONG, 3, LEN_AND_LIT(SYSID), SIZEOF(buf) - 1); +# endif + else + rts_error(VARLSTCNT(5) ERR_TRNLOGFAIL, 2, LEN_AND_LIT(SYSID), status); + assert(stringpool.free < stringpool.top); /* it's process initialization after all */ + return; +} diff --git a/sr_port/dollar_system_init.h b/sr_port/dollar_system_init.h new file mode 100644 index 0000000..4ee7514 --- /dev/null +++ b/sr_port/dollar_system_init.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2002 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef DOLLAR_SYSTEM_INIT_H_INCLUDED +#define DOLLAR_SYSTEM_INIT_H_INCLUDED + +void dollar_system_init(struct startup_vector *svec); + +#endif /* DOLLAR_SYSTEM_INIT_H_INCLUDED */ diff --git a/sr_port/dollar_zlevel.c b/sr_port/dollar_zlevel.c new file mode 100644 index 0000000..a512bdd --- /dev/null +++ b/sr_port/dollar_zlevel.c @@ -0,0 +1,44 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "rtnhdr.h" +#include "stack_frame.h" +#include "dollar_zlevel.h" + +GBLREF stack_frame *frame_pointer; + +int dollar_zlevel() +{ + int count; + stack_frame *fp, *fpprev; + + for (count = 0, fp = frame_pointer; NULL != fp; fp = fpprev) + { + assert((fp < fp->old_frame_pointer) || (NULL == fp->old_frame_pointer)); + fpprev = fp->old_frame_pointer; + if (!(fp->type & SFT_COUNT)) + continue; + if (NULL == fpprev) + { /* Next frame is some sort of base frame */ +# ifdef GTM_TRIGGER + if (fp->type & SFT_TRIGR) + { /* Have a trigger baseframe, pick up stack continuation frame_pointer stored by base_frame() */ + fpprev = *(stack_frame **)(fp + 1); + continue; + } else +# endif + break; /* Some other base frame that stops us */ + } + count++; + } + return (count); +} diff --git a/sr_port/dollar_zlevel.h b/sr_port/dollar_zlevel.h new file mode 100644 index 0000000..150a782 --- /dev/null +++ b/sr_port/dollar_zlevel.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __DOLLAR_ZLEVEL_H__ +#define __DOLLAR_ZLEVEL_H__ + +int dollar_zlevel(void); + +#endif diff --git a/sr_port/dollarx.c b/sr_port/dollarx.c new file mode 100644 index 0000000..18b5427 --- /dev/null +++ b/sr_port/dollarx.c @@ -0,0 +1,217 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include + +#include "io.h" +#include "iottdef.h" +#include "dollarx.h" +#include "patcode.h" + +#ifdef UNICODE_SUPPORTED +#include "gtm_icu_api.h" /* needed by *TYPEMASK* macros defined in gtm_utf8.h */ +#include "gtm_utf8.h" + +LITREF UChar32 u32_line_term[]; +#endif + +GBLREF uint4 *pattern_typemask; +GBLREF boolean_t gtm_utf8_mode; + +void dollarx(io_desc *io_ptr, unsigned char *str, unsigned char *strtop) +{ + unsigned char *str1, *strnext, *strstart, *strcursor, *strprev; + int4 esc_level, char_width, total; + boolean_t utf8_term, utf8_active, utf8_crlast = FALSE; + wint_t curr_char; + + utf8_active = (gtm_utf8_mode UNICODE_ONLY(&& CHSET_M != io_ptr->ochset)) ? TRUE : FALSE; + utf8_term = (utf8_active && tt == io_ptr->type) ? TRUE : FALSE; + strstart = strcursor = str; + if (io_ptr->write_filter) + { + esc_level = (io_ptr->write_filter & ESC_MASK); + while (str < strtop) + { + if (START != io_ptr->esc_state) + { + assert (esc_level); + str1 = iott_escape(str, strtop, io_ptr); + str = str1; + if ((FINI == io_ptr->esc_state) || ( BADESC == io_ptr->esc_state)) + io_ptr->esc_state = START; + continue; + } + if (!utf8_active) + { + curr_char = *str; + strnext = str + 1; + } +#ifdef UNICODE_SUPPORTED + else + strnext = UTF8_MBTOWC(str, strtop, curr_char); +#endif + if (io_ptr->write_filter & CHAR_FILTER) + { + switch(curr_char) + { + case NATIVE_LF: + if (!utf8_crlast) + { /* otherwise CR case will have handled */ + io_ptr->dollar.y++; + if (io_ptr->length) + io_ptr->dollar.y %= io_ptr->length; + } else + utf8_crlast = FALSE; + str = strnext; + break; + case NATIVE_CR: + io_ptr->dollar.x = 0; + if (utf8_active && gtmsocket != io_ptr->type) + { /* CR implies LF for Unicode except for socket which recongizes + only NATIVE_LF as a terminator unicode or not. + */ + utf8_crlast = TRUE; + io_ptr->dollar.y++; + if (io_ptr->length) + io_ptr->dollar.y %= io_ptr->length; + } + str = strstart = strcursor = strnext; + break; + case NATIVE_BS: + /* if bs at beginning of string but x > 0 need image of line */ + if (io_ptr->dollar.x > 0) +#ifdef UNICODE_SUPPORTED + if (utf8_term) + { + /* get previous character relative to strcursor and back it up */ + if (strstart < strcursor) + { + for ( ; strstart < strcursor; strcursor = strprev) + { + UTF8_LEADING_BYTE((strcursor - 1), strstart, + strprev); + UTF8_MBTOWC(strprev, strtop, curr_char); + if (U_ISPRINT(curr_char)) + break; + } + strcursor = strprev; /* back up cursor */ + GTM_IO_WCWIDTH(curr_char, char_width); + io_ptr->dollar.x -= char_width; + } + } else +#endif + io_ptr->dollar.x--; + str = strnext; + utf8_crlast = FALSE; + break; + case NATIVE_FF: + io_ptr->dollar.x = io_ptr->dollar.y = 0; + str = strstart = strcursor = strnext; + utf8_crlast = FALSE; + break; + case NATIVE_ESC: + utf8_crlast = FALSE; + if (esc_level) + { + str1 = iott_escape(str, strtop, io_ptr); + str = str1; + if ((FINI == io_ptr->esc_state) || ( BADESC == io_ptr->esc_state)) + io_ptr->esc_state = START; + continue; + } + /*** Caution: FALL THROUGH ***/ + default: + utf8_crlast = FALSE; + if (!gtm_utf8_mode) + { + if (!(pattern_typemask[*str] & PATM_C)) + io_ptr->dollar.x++; + str++; + } + UNICODE_ONLY( + else + { + assert(str < strtop); /* PATTERN_TYPEMASK macro relies on this */ + if (utf8_term) + { + if (curr_char == u32_line_term[U32_LT_NL] || + curr_char == u32_line_term[U32_LT_LS] || + curr_char == u32_line_term[U32_LT_PS]) + { /* a line terminator not handled above */ + io_ptr->dollar.y++; + if (io_ptr->length) + io_ptr->dollar.y %= io_ptr->length; + io_ptr->dollar.x = 0; + strstart = strcursor = strnext; + char_width = 0; + } else + GTM_IO_WCWIDTH(curr_char, char_width); + if (0 < char_width) + { + io_ptr->dollar.x += char_width; + strcursor = strnext; + } + + } else if (U_ISPRINT(curr_char)) + io_ptr->dollar.x++; + str = strnext; + } + ) /* UNICODE_ONLY */ + break; + } + } else if (NATIVE_ESC == *str) + { + assert(esc_level); + str1 = iott_escape(str, strtop, io_ptr); + str = str1; + if ((FINI == io_ptr->esc_state) || (BADESC == io_ptr->esc_state)) + io_ptr->esc_state = START; + } else + { +#ifdef UNICODE_SUPPORTED + if (utf8_term) + { + GTM_IO_WCWIDTH(curr_char, char_width); + io_ptr->dollar.x += char_width; + } else +#endif + io_ptr->dollar.x++; + str = strnext; + } + } +#ifdef UNICODE_SUPPORTED + } else if (utf8_active) + { + for (total = 0; str < strtop; str = strnext) + { + strnext = UTF8_MBTOWC(str, strtop, curr_char); + if (utf8_term) + { /* count display width */ + GTM_IO_WCWIDTH(curr_char, char_width); + total += char_width; + } else + total++; /* count number of Unicode characters */ + } + io_ptr->dollar.x += total; +#endif + } else + io_ptr->dollar.x += (unsigned int)(strtop - str); + if (io_ptr->dollar.x > io_ptr->width && io_ptr->wrap) + { + io_ptr->dollar.y += (io_ptr->dollar.x / io_ptr->width); + if (io_ptr->length) + io_ptr->dollar.y %= io_ptr->length; + io_ptr->dollar.x %= io_ptr->width; + } +} diff --git a/sr_port/dollarx.h b/sr_port/dollarx.h new file mode 100644 index 0000000..95217fd --- /dev/null +++ b/sr_port/dollarx.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef DOLLARX_INCLUDED +#define DOLLARX_INCLUDED + +void dollarx(io_desc *io_ptr, unsigned char *str, unsigned char *strtop); + +#endif /* DOLLARX_INCLUDED */ diff --git a/sr_port/dpgbldir.c b/sr_port/dpgbldir.c new file mode 100644 index 0000000..a8dfd51 --- /dev/null +++ b/sr_port/dpgbldir.c @@ -0,0 +1,333 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gbldirnam.h" +#include "hashtab_mname.h" +#include "iosize.h" +#include "probe.h" +#include "dpgbldir.h" +#ifdef UNIX +#include "gtmio.h" +#elif defined(VMS) +#include +#else +#error unsupported platform +#endif +#include "dpgbldir_sysops.h" +#include "targ_alloc.h" +#include "gtm_logicals.h" + +GBLREF gd_addr *gd_header; +GBLREF gv_namehead *gv_target_list; + +LITREF char gde_labels[GDE_LABEL_NUM][GDE_LABEL_SIZE]; + +STATICDEF gdr_name *gdr_name_head; +STATICDEF gd_addr *gd_addr_head; + +/*+ +Function: ZGBLDIR + + This function searches the list of global directory names for + the specified names. If not found, it adds the new name to the + list and calls GD_LOAD. A pointer to the global directory + structure is returned, and the name entry is pointed at it. + The global directory pointer is then returned to the caller. + +Syntax: gd_addr *zgbldir(mval *v) + +Prototype: ? + +Return: *gd_addr -- a pointer to the global directory structure + +Arguments: mval *v -- an mval that contains the name of the global + directory to be accessed. The name may require translation. + +Side Effects: NONE + +Notes: NONE +-*/ +gd_addr *zgbldir(mval *v) +{ + gd_addr *gd_ptr; + gdr_name *name; + mstr temp_mstr, *tran_name; + + for (name = gdr_name_head; name; name = (gdr_name *)name->link) + if (v->str.len == name->name.len && !memcmp(v->str.addr, name->name.addr, v->str.len)) + return name->gd_ptr; + if (!v->str.len) + { + temp_mstr.addr = GTM_GBLDIR; + temp_mstr.len = SIZEOF(GTM_GBLDIR) - 1; + tran_name = get_name(&temp_mstr); + } else + tran_name = get_name(&v->str); + gd_ptr = gd_load(tran_name); + name = (gdr_name *)malloc(SIZEOF(gdr_name)); + if (name->name.len = v->str.len) /* Note embedded assignment */ + { + name->name.addr = (char *)malloc(v->str.len); + memcpy(name->name.addr, v->str.addr, v->str.len); + } + /* Store translated global directory name as well */ + assert(tran_name->len); + name->exp_name = *tran_name; + /* free up memory allocated for mstr field in get_name. + * memory allocated for addr field of the mstr is needed as it has been copied over to "name->exp_name" */ + free(tran_name); + + if (gdr_name_head) + name->link = (gdr_name *)gdr_name_head; + else + name->link = 0; + gdr_name_head = name; + gdr_name_head->gd_ptr = gd_ptr; + return gd_ptr; +} + +/*+ +Function: GD_LOAD + +Syntax: gd_addr *gd_load(mstr *gd_name) + + Open a global directory file and verify that it is a valid GD. + Determine if it has already been opened. If not, setup and + initialize the GT.M structures used to access the GD based on + the information in the file, enter in the linked list of global + directories and return a pointer to it. If already opened, return + a pointer to it. + +Prototype: ? + +Return: gd_addr * (all errors are signalled) + +Arguments: gd_name is the name of the file to be opened + +Side Effects: None +Notes: A) While checking may be done earlier for duplicate names, + unique identification of files can require OS specific + operations useable only after the file is open, so checks + must be done within this function for duplicate files. +-*/ +gd_addr *gd_load(mstr *v) +{ + void *file_ptr; /* This is a temporary structure as the file open and manipulations are currently stubs */ + header_struct *header, temp_head; + gd_addr *table, *gd_addr_ptr; + gd_binding *map, *map_top; + gd_region *reg, *reg_top; + uint4 t_offset, size; + short i; + error_def(ERR_GDINVALID); + + file_ptr = open_gd_file(v); + + for (gd_addr_ptr = gd_addr_head; gd_addr_ptr; gd_addr_ptr = gd_addr_ptr->link) + { /* if already open then return old structure */ + if (comp_gd_addr(gd_addr_ptr, file_ptr)) + { + close_gd_file(file_ptr); + return gd_addr_ptr; + } + } + file_read(file_ptr, SIZEOF(header_struct), (uchar_ptr_t)&temp_head, 1); /* Read in header and verify is valid GD */ + for (i = 0; i < GDE_LABEL_NUM; i++) + { + if (!memcmp(temp_head.label, gde_labels[i], GDE_LABEL_SIZE - 1)) + break; + } + if (GDE_LABEL_NUM == i) + { + close_gd_file(file_ptr); + rts_error(VARLSTCNT(8) ERR_GDINVALID, 6, v->len, v->addr, LEN_AND_LIT(GDE_LABEL_LITERAL), + SIZEOF(temp_head.label), temp_head.label); + } + size = LEGAL_IO_SIZE(temp_head.filesize); + header = (header_struct *)malloc(size); + file_read(file_ptr, size, (uchar_ptr_t)header, 1); /* Read in body of file */ + table = (gd_addr *)((char *)header + SIZEOF(header_struct)); + table->local_locks = (struct gd_region_struct *)((UINTPTR_T)table->local_locks + (UINTPTR_T)table); + table->maps = (struct gd_binding_struct *)((UINTPTR_T)table->maps + (UINTPTR_T)table); + table->regions = (struct gd_region_struct *)((UINTPTR_T)table->regions + (UINTPTR_T)table); + table->segments = (struct gd_segment_struct *)((UINTPTR_T)table->segments + (UINTPTR_T)table); + table->end = (table->end + (UINTPTR_T)table); + + for (map = table->maps, map_top = map + table->n_maps; map < map_top; map++) + { + t_offset = map->reg.offset; + map->reg.addr = (gd_region *)((char *)table + t_offset); + } + + for (reg = table->regions, reg_top = reg + table->n_regions; reg < reg_top; reg++) + { + t_offset = reg->dyn.offset; + reg->dyn.addr = (gd_segment *)((char *)table + t_offset); + } + table->link = gd_addr_head; + gd_addr_head = table; + fill_gd_addr_id(gd_addr_head, file_ptr); + close_gd_file(file_ptr); + table->tab_ptr = (hash_table_mname *)malloc(SIZEOF(hash_table_mname)); + init_hashtab_mname(table->tab_ptr, 0, HASHTAB_NO_COMPACT, HASHTAB_NO_SPARE_TABLE); + return table; +} + +/*+ +Function: GET_NEXT_GDR + + This function returns the next entry in the list of open + global directories. If the input parameter is zero, the + first entry is returned, otherwise the next entry in the + list is returned. If the input parameter is not a member + of the list, then zero will be returned. + +Syntax: gd_addr *get_next_gdr(gd_addr *prev) + +Prototype: ? + +Return: *gd_addr -- a pointer to the global directory structure + +Arguments: The previous global directory accessed; + +Side Effects: NONE + +Notes: NONE +-*/ +gd_addr *get_next_gdr(gd_addr *prev) +{ + gd_addr *ptr; + + if (!prev) + return gd_addr_head; + + for (ptr = gd_addr_head; ptr && ptr != prev; ptr = ptr->link) + if (!GTM_PROBE(SIZEOF(*ptr), ptr, READ)) /* Called from secshr, have to check access to memory */ + return NULL; + if (ptr && GTM_PROBE(SIZEOF(*ptr), ptr, READ)) + return ptr->link; + return NULL; +} + +/* Maintain list of regions for GTCM_SERVER */ +void cm_add_gdr_ptr(gd_region *greg) +{ + gd_addr *ga; + + ga = (gd_addr *)malloc(SIZEOF(gd_addr)); + ga->end = 0; /* signifies a GT.CM gd_addr */ + ga->regions = greg; + ga->n_regions = 1; + ga->link = gd_addr_head; + gd_addr_head = ga; + return; +} + +void cm_del_gdr_ptr(gd_region *greg) +{ + gd_addr *ga1, *ga2; + + for (ga1 = ga2 = gd_addr_head; ga1; ga1 = ga1->link) + { + if (ga1->regions == greg) + { + if (ga1 == gd_addr_head) + gd_addr_head = ga1->link; + else + ga2->link = ga1->link; + free(ga1); + break; + } + ga2 = ga1; + } + return; +} + +boolean_t get_first_gdr_name(gd_addr *current_gd_header, mstr *log_nam) +{ + gdr_name *name; + + for (name = gdr_name_head; name; name = (gdr_name *)name->link) + { + if (name->gd_ptr == current_gd_header) + { + *log_nam = name->exp_name; + return (TRUE); + } + } + return FALSE; +} + +void gd_rundown(void) /* Wipe out the global directory structures */ +{ + gd_addr *gda_cur, *gda_next; + gdr_name *gdn_cur, *gdn_next; + + for (gda_cur = gd_addr_head; NULL != gda_cur; gda_cur = gda_next) + { + gda_next = gda_cur->link; + if (gda_cur->end) + { + gd_ht_kill(gda_cur->tab_ptr, TRUE); + free(gda_cur->tab_ptr); /* free up hashtable malloced in gd_load() */ + free(gda_cur->id); /* free up gd_id malloced in gd_load()/fill_gd_addr_id() */ + free((char *)gda_cur - SIZEOF(header_struct)); /* free up global directory itself */ + } else + free(gda_cur); /* GT.CM gd_addr and hence header_struct wasn't malloced in cm_add_gdr_ptr */ + } + assert(NULL == gv_target_list); + gd_header = gd_addr_head = (gd_addr *)NULL; + for (gdn_cur = gdr_name_head; NULL != gdn_cur; gdn_cur = gdn_next) + { + gdn_next = (gdr_name *)gdn_cur->link; + if (gdn_cur->name.len) + free(gdn_cur->name.addr); + free(gdn_cur); + } + gdr_name_head = (gdr_name *)NULL; +} + +void gd_ht_kill(hash_table_mname *table, boolean_t contents) /* wipe out the hash table corresponding to a gld */ +{ + ht_ent_mname *tabent, *topent; + gvnh_reg_t *gvnh_reg; + gv_namehead *gvt; + + if (contents) + { + for (tabent = table->base, topent = tabent + table->size; tabent < topent; tabent++) + { + if (HTENT_VALID_MNAME(tabent, gvnh_reg_t, gvnh_reg)) + { + gvt = gvnh_reg->gvt; + gvt->regcnt--; + if (!gvt->regcnt) + targ_free(gvt); + free(gvnh_reg); + } + } + } + free_hashtab_mname(table); + /* We don't do a free(table) in this generic routine because it is called both by GT.M and GT.CM + * and GT.CM retains the table for reuse while GT.M doesn't. GT.M fgncal_rundown() takes care of + * this by freeing it up explicitly (after a call to ht_kill) in gd_rundown() [dpgbldir.c] + */ + return; +} diff --git a/sr_port/dpgbldir.h b/sr_port/dpgbldir.h new file mode 100644 index 0000000..3a426e7 --- /dev/null +++ b/sr_port/dpgbldir.h @@ -0,0 +1,32 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __DBGBLDIR_H__ +#define __DBGBLDIR_H__ + +typedef struct gvt_container_struct +{ + gvnh_reg_t *gvnh_reg; + struct gvt_container_struct *next_gvtc; +} gvt_container; + +boolean_t get_first_gdr_name(gd_addr *current_gd_header, mstr *log_nam); +gd_addr *zgbldir(mval *v); +gd_addr *gd_load(mstr *v); +gd_addr *get_next_gdr(gd_addr *prev); +mstr *get_name(mstr *ms); +void cm_add_gdr_ptr(gd_region *greg); +void cm_del_gdr_ptr(gd_region *greg); +void *open_gd_file(mstr *v); +void gd_rundown(void); +void gd_ht_kill(struct hash_table_mname_struct *table, boolean_t contents); + +#endif diff --git a/sr_port/dse.h b/sr_port/dse.h new file mode 100644 index 0000000..a102656 --- /dev/null +++ b/sr_port/dse.h @@ -0,0 +1,138 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __DSE_H__ +#define __DSE_H__ + +#define PATCH_SAVE_SIZE 128 +#define DSE_DMP_TIME_FMT "DD-MON-YEAR 24:60:SS" + +#define GET_CURR_TIME_IN_DOLLARH_AND_ZDATE(dollarh_mval, dollarh_buffer, zdate_mval, zdate_buffer) \ +{ /* gets current time in the mval "dollarh_mval" in dollarh format and in the mval "zdate_mval" in ZDATE format \ + * the ZDATE format string used is DSE_DMP_TIME_FMT \ + * the dollarh_buffer and zdate_buffer are buffers which the corresponding mvals use to store the actual time string \ + */ \ + GBLREF mval dse_dmp_time_fmt; \ + GBLREF spdesc stringpool; \ + LITREF mval literal_null; \ + \ + op_horolog(&dollarh_mval); /* returns $H value in stringpool */ \ + assert(SIZEOF(dollarh_buffer) >= dollarh_mval.str.len); \ + /* if op_fnzdate (called below) calls stp_gcol, dollarh_mval might get corrupt because it is not known to stp_gcol. \ + * To prevent problems, copy from stringpool to local buffer */ \ + memcpy(dollarh_buffer, dollarh_mval.str.addr, dollarh_mval.str.len); \ + dollarh_mval.str.addr = (char *)dollarh_buffer; \ + stringpool.free -= dollarh_mval.str.len; /* now that we've made a copy, we don't need dollarh_mval in stringpool */ \ + op_fnzdate(&dollarh_mval, &dse_dmp_time_fmt, (mval *)&literal_null, (mval *)&literal_null, &zdate_mval); \ + /* op_fnzdate() returns zdate formatted string in stringpool */ \ + assert(SIZEOF(zdate_buffer) >= zdate_mval.str.len); \ + /* copy over stringpool string into local buffer to ensure zdate_mval will not get corrupt */ \ + memcpy(zdate_buffer, zdate_mval.str.addr, zdate_mval.str.len); \ + zdate_mval.str.addr = (char *)zdate_buffer; \ + stringpool.free -= zdate_mval.str.len; /* now that we've made a copy, we don't need zdate_mval in stringpool anymore */ \ +} + +typedef struct +{ + block_id blk; + char *bp; + gd_region *region; + char *comment; + short int ver; +} save_strct; + +enum dse_fmt +{ + CLOSED_FMT = 0, + GLO_FMT, + ZWR_FMT, + OPEN_FMT +}; + +/* Grab crit for dse* functions taking into account -nocrit if specified */ +#define DSE_GRAB_CRIT_AS_APPROPRIATE(WAS_CRIT, NOCRIT_PRESENT, CS_ADDRS, GV_CUR_REGION) \ +{ \ + if (!WAS_CRIT) \ + { \ + if (NOCRIT_PRESENT) \ + CS_ADDRS->now_crit = TRUE; \ + else \ + grab_crit(GV_CUR_REGION); \ + } \ +} + +/* Rel crit for dse* functions taking into account -nocrit if specified */ +#define DSE_REL_CRIT_AS_APPROPRIATE(WAS_CRIT, NOCRIT_PRESENT, CS_ADDRS, GV_CUR_REGION) \ +{ \ + if (!WAS_CRIT) \ + { \ + if (NOCRIT_PRESENT) \ + CS_ADDRS->now_crit = FALSE; \ + else \ + rel_crit(GV_CUR_REGION); \ + } \ +} + +void dse_ctrlc_setup(void); +int dse_data(char *dst, int *len); +int dse_getki(char *dst, int *len, char *qual, int qual_len); +int dse_is_blk_in(sm_uc_ptr_t rp, sm_uc_ptr_t r_top, short size); +int dse_ksrch(block_id srch, block_id_ptr_t pp, int4 *off, char *targ_key, int targ_len); +int dse_order(block_id srch, block_id_ptr_t pp, int4 *op, char *targ_key, short int targ_len, + bool dir_data_blk); +void dse_rmsb(void); +void dse_ctrlc_handler(int sig); +void dse_exhaus(int4 pp, int4 op); +void dse_m_rest(block_id blk, unsigned char *bml_list, int4 bml_size, sm_vuint_ptr_t blks_ptr, + bool in_dir_tree); +void dse_rmrec(void); +void dse_find_roots(block_id index); +boolean_t dse_fdmp(sm_uc_ptr_t data, int len); +boolean_t dse_fdmp_output(void *addr, int4 len); +void dse_adrec(void); +void dse_adstar(void); +void dse_all(void); +boolean_t dse_b_dmp(void); +void dse_cache(void); +void dse_chng_bhead(void); +void dse_chng_fhead(void); +void dse_chng_rhead(void); +void dse_crit(void); +void dse_dmp(void); +void dse_eval(void); +void dse_f_blk(void); +void dse_f_free(void); +void dse_f_key(void); +void dse_f_reg(void); +void dse_flush(void); +int parse_dlr_char(char *src, char *top, char *dlr_subsc); +void dse_help(void); +void dse_version(void); +void dse_integ(void); +bool dse_is_blk_free (block_id blk, sm_int_ptr_t cycle, cache_rec_ptr_ptr_t cr); +int4 dse_lm_blk_free(int4 blk, sm_uc_ptr_t base_addr); +void dse_maps(void); +void dse_open (void); +void dse_close(void); +void dse_over(void); +void dse_page(void); +boolean_t dse_r_dmp(void); +void dse_range(void); +void dse_rest(void); +void dse_save(void); +void dse_shift(void); +void dse_wcreinit (void); +sm_uc_ptr_t dump_record(sm_uc_ptr_t rp, block_id blk, sm_uc_ptr_t bp, sm_uc_ptr_t b_top); +void dse_dmp_fhead (void); +void dse_ctrlc_handler(int sig); +void dse_remove(void); + +#endif diff --git a/sr_port/dse.hlp b/sr_port/dse.hlp new file mode 100644 index 0000000..e4da1e7 --- /dev/null +++ b/sr_port/dse.hlp @@ -0,0 +1,1086 @@ + +1 Overview + Overview + The GT.M Database Structure Editor, DSE, examines and repairs + Greystone Technology Database Structure (GDS) database(s). GT.M uses + Buffered Global (BG) and Mapped Memory (MM) access methods for GDS + files. For more information on GDS, refer to the "Greystone Database + Structure" chapter in the GT.M Administration and Operations Guide. + MUPIP INTEG provides comprehensive error checking, which serves to + verify the results of repairs undertaken with DSE. For more + information on MUPIP INTEG, refer to the "MUMPS Peripheral Interchange + Program" chapter in the GT.M Administration and Operations Guide. For + more information on the use of DSE, refer to the "Maintaining Database + Integrity" chapter in the GT.M Administration and Operations Guide. + +1 DSE_Functions + Functions of DSE + DSE is primarily a database repair utility. + + Use DSE to: + + o Dump parts of the database for troubleshooting database errors + + o Add or delete a record in a block + + o Update file, block or record header information + + o Update bit maps + + o Save copies of database fragments for analysis, audit or + restoration. + + Use the DSE EXIT command to leave DSE. + +1 Command_Syntax + Command Syntax + The format for DSE commands is: + + Command qualifier + + DSE interprets all numeric input as hexadecimal, except for time + values, the -BLK_SIZE=, -KEY_MAX_SIZE=, -RECORD_MAX_SIZE=, + -REFERENCE_COUNT=, -TIMERS_PENDING= and -WRITES_PER_FLUSH= on CHANGE + -FILEHEADER, and -VERSION= on the REMOVE and RESTORE commands. This + convention corresponds to the displays provided by DSE and by MUPIP + INTEG. + +1 ADD + AD[D] + The ADD command adds a record to a block. + + The format of the ADD command is: + + AD[D] [-B[LOCK]=block] + + For greater than level 0 blocks add: + + -STAR -POINTER=block + + or + + -OFFSET=offset|-RECORD=record -KEY=key -POINTER=block + + For level 0 blocks add: + + -OFFSET=offset|-RECORD=record -KEY=key -D[ATA]=string + + The ADD command requires either the -OFFSET or -RECORD qualifier to + position the record in the block and either the -KEY or the -STAR + qualifier to define the key for the block. + + The -STAR qualifier is not valid at level 0 (i.e., for a data block). + The ADD command requires the -DATA qualifier at level 0 or the + -POINTER qualifier at any other level to provide the content of the + record. + +2 Qualifiers +-BLOCK + -B[LOCK]=block_number + Specifies the block to receive the new record. + + On commands with no -BLOCK= qualifier, DSE uses the last block + handled by a DSE operation. In this case, when no block has been + accessed, .i.e., on the first block oriented command, DSE uses + block one (1). + +-DATA + -D[ATA]=string + Specifies the data field for records added to a data block. Use + quotes around string and escape codes of the form \a or \ab, + where a and b are hexadecimal digits representing non-printing + characters. \\ translates to a single backslash. The -DATA + qualifier only applies at level 0 and is incompatible with the + -STAR and -POINTER qualifiers. + +-KEY + -K[EY]=key + Specifies the key of the new record. Enclose MUMPS-style global + references in quotes (""). The -KEY qualifier is incompatible + with the -STAR qualifier. + +-OFFSET + -O[FFSET]=offset + Adds the new record at the next record boundary after the + specified offset. The -OFFSET qualifier is incompatible with the + -RECORD and -STAR qualifiers. + +-POINTER + -P[OINTER]=pointer + Specifies the block pointer field for records added to an index + block. The -POINTER qualifier is incompatible with the -DATA + qualifier and cannot be used at level 0. + +-RECORD + -R[ECORD]=record_number + Specifies a record number of the new record. The -RECORD + qualifier is incompatible with the -OFFSET and -STAR qualifiers. + +-STAR + -S[TAR] + Adds a star record (i.e., a record that identifies the last + record in an indexed block) at the end of the specified block. + The -STAR qualifier is incompatible with all qualifiers except + -BLOCK and -POINTER and cannot be used at level 0. + +1 ALL + AL[L] + The ALL command applies action(s) specified by a qualifier to all GDS + regions defined by the current Global Directory. This is a very + powerful command; use caution. Be especially careful if you have an + overlapping database structure (e.g., overlapping regions accessed + from separate application global directories). + +2 Qualifiers +-BUFFER_FLUSH + -B[UFFER_FLUSH] + Flushes to disk buffers all regions specified by the current + Global Directory. The -BUFFER_FLUSH qualifier is incompatible + with the -RENEW qualifier. + +-CRITINIT + -C[RITINIT] + Initializes critical sections for all regions specified by the + current Global Directory. The -CRITINIT qualifier is + incompatible with the -RENEW, -RELEASE and -SEIZE qualifiers. + +-FREEZE + -[NO]F[REEZE] + Allows a user to freeze, or prevent updates to, GDS regions + specified by the current Global Directory. The -FREEZE qualifier + freezes all such GDS regions except those previously frozen by + another user. Regions frozen by a particular user are associated + with that user. A frozen region may be updated in one of two + ways: The user who froze the region may unfreeze it with the + -NOFREEZE qualifier; or another user may override the freeze + injunction with the -OVERRIDE qualifier. + + The -NOFREEZE qualifier unfreezes only those GDS regions that + were previously frozen by the operator. Once a region is + unfrozen, it may be updated by any user. To unfreeze all GDS + regions, use the -OVERRIDE qualifier. + + The -FREEZE qualifier is incompatible with the -RENEW qualifier. + + DSE releases -FREEZE when it EXITs. To hold all databases, enter + ALL -FREEZE and then SPAWN to perform other operations. + +-OVERRIDE + -O[VERRIDE] + The -OVERRIDE qualifier is meaningful only with the -FREEZE or + -NOFREEZE it unfreezes all GDS regions, including those frozen + by other users. When used with -FREEZE, the -OVERRIDE qualifier + freezes all GDS regions, including those frozen by other users, + associating all such freezes with the current user. The current + user must then use the -NOFREEZE qualifier to unfreeze the + database; any other user attempting a -UNFREEZE would also have + to include the -OVERRIDE qualifier. + +-REFERENCE + -REF[ERENCE] + Resets reference counts to 1 for all regions specified by the + current Global Directory. The -REFERENCE qualifier is + incompatible with the -RENEW qualifier. + +-RELEASE + -REL[EASE] + Releases critical sections for all regions specified by the + current Global Directory. The -RELEASE qualifier is incompatible + with the -CRITINIT, -RENEW and -SEIZE qualifiers. + +-RENEW + -REN[EW] + Reinitializes critical sections (-CRITICAL) and buffers + (-WCINIT), resets reference counts to 1 (-REFERENCE_COUNT) and + clears freeze flags for all regions specified by the current + Global Directory (-NOFREEZE). -RENEW requires confirmation. The + -RENEW qualifier is incompatible with all other qualifiers. + +-SEIZE + -S[EIZE] + Seizes the critical section for all regions specified by the + current Global Directory. The -SEIZE qualifier is incompatible + with the -CRITINIT, -RELEASE and -RENEW qualifiers. + + The SEIZE qualifier can be useful when you encounter a + DSEBLKRDFAIL error, generated when DSE is unable to read a block + from the database. + +-WCINIT + -W[CINIT] + Reinitializes buffers for all regions specified by the current + Global Directory. -WCINIT requires confirmation. The -WCINIT + qualifier is incompatible with the -RENEW qualifier. + +1 BUFFER_FLUSH + B[UFFER_FLUSH] + The BUFFER_FLUSH command flushes the current region's buffers to disk. + + + The format of the BUFFER_FLUSH command is: + + B[UFFER_FLUSH] + + The BUFFER_FLUSH command has no qualifiers. + +1 CHANGE + CH[ANGE] + The CHANGE command changes fields of a file, block, or record header + and the bit map. + + The CHANGE command either has a -FILEHEADER qualifier or an implicit + or explicit -BLOCK qualifier plus one or more of their associated + qualifiers to define the target of the change. + +2 Block_Qualifiers + +-BLOCK + -BL[OCK]=block_number + Specifies the block to modify. The -BLOCK qualifier is + incompatible with the -FILEHEADER qualifier and all qualifiers + related to -FILEHEADER. + + -BLOCK is the default qualifier. On commands with neither a + -BLOCK nor a -FILEHEADER qualifier, DSE uses the last block + handled by a DSE operation. In this case, when no block has been + accessed, that is, on the first block-oriented command, DSE uses + block one (1). + +-BSIZ + -BS[IZ]=block_size + Changes the block size field of the specified block. Decreasing + the block size can result in loss of existing data. The -BSIZ + qualifier is incompatible with all qualifiers except -BLOCK, + -LEVEL and -TN. + +-LEVEL + -L[EVEL]=level + Changes the level field for the specified block. The -LEVEL + qualifier is incompatible with all qualifiers except -BLOCK, + -BSIZ and -TN. + +-TN + -TN[=transaction_number] + Changes the transaction number for the current block. When a + CHANGE command does not include a -TN=, DSE sets the transaction + number to the current transaction number. Manipulation of the + block transaction number affects MUPIP BACKUP -INCREMENTAL. The + -TN qualifier is incompatible with all qualifiers except -BLOCK, + -BSIZ and -LEVEL. + +-OFFSET + -OF[FSET]=offset + Specifies the offset within the block of the target record. The + -OFFSET qualifier is incompatible with all qualifiers except + -BLOCK, -CMPC and -RSIZ. + +-RECORD + -RE[CORD]=record_number + Specifies the record number of the target record. The -RECORD + qualifier is incompatible with all qualifiers except -BLOCK, + -CMPC and -RSIZ. + +-CMPC + -CM[PC]=compression_count + Changes the compression count field of the specified record. The + -CMPC qualifier is incompatible with all qualifiers except + -BLOCK, -OFFSET, and -RECORD. + +-RSIZ + -RS[IZ]=record_size + Changes the record size field of the specified record. The -RSIZ + qualifier is incompatible with all qualifiers except -OFFSET and + -RECORD. + +2 File_header_qualifiers + +-FILEHEADER + -FI[LEHEADER] + Enables modification of specific fields in the file header. The + -FILEHEADER qualifier is incompatible with the -BLOCK and all + qualifiers related to -BLOCK (i.e., -BSIZ, -CMPC, -LEVEL, + -OFFSET, -RECORD, -RSIZ and -TN qualifiers). + +-BLK_SIZE + -BLK[_SIZE]=block_size + Changes the decimal block size field of the current file. Use + the -BLK_SIZE qualifier only in conjunction with the -FILEHEADER + qualifier. Do not use this CHANGE qualifier except on + instructions from Greystone. + +-BLOCKS_FREE + -BLO[CKS_FREE]=free blocks + Changes the free blocks field of the current file. Use the + -BLOCK_FREE qualifier only in conjunction with the -FILEHEADER + qualifier. Database operations maintain this field for the + user's convenience. The field does not control any database + operations. + +-B_COMPREHENSIVE + -B_C[OMPREHENSIVE]=transaction_number + Changes the transaction number in the fileheader of the last + comprehensive backup to the value specified. Use this qualifier + only in conjunction with the -FILEHEADER qualifier. + +-B_INCREMENTAL + -B_I[NCREMENTAL]=transaction_number + Changes the transaction number in the fileheader of the last + incremental backup to the value specified. Use this qualifier + only in conjunction with the -FILEHEADER qualifier. + +-B_RECORD + -B_R[ECORD]=transaction_number + Changes the transaction number in the fileheader of the last + -RECORD backup to the value specified. Use this qualifier only + in conjunction with the -FILEHEADER qualifier. + +-CORRUPT_FILE + -CO[RRUPT_FILE]=value + Sets the file_corrupt field in the file header. Possible values + are: TRUE, FALSE and NOCHANGE. Use the -CORRUPT_FILE qualifier + only in conjunction with the -FILEHEADER qualifier. + + WARNING: when DSE EXITs after a CHANGE -FILEHEADER -CORRUPT=TRUE + without a matching CHANGE -FILEHEADER -CORRUPT=FALSE, the file + becomes unavailable to all future access. + +-CURRENT_TN + -CU[RRENT_TN]=transaction_number + Changes the current transaction number for the current region. + Use the -CURRENT_TN qualifier only in conjunction with the + -FILEHEADER qualifier. This qualifier has implications only for + MUPIP BACKUP -INCREMENTAL. Raising the -CURRENT_TN corrects + block transaction number too large errors. + +-FLUSH_TIME + -FL[USH_TIME][=delta_time] + Changes the flush_time default interval (in delta_time). The + time entered must be between 0 and 1 hour. + + Use the -FLUSH_TIME qualifier only in conjunction with the + -FILEHEADER qualifier. Do not use this CHANGE qualifier except + on instructions from Greystone. A -FLUSH_TIME with no value + resets the -FLUSH_TIME to the default value. Input is + interpreted as decimal. + +-FREEZE + -FR[EEZE]=value + Sets availability of the region for update. Possible values are: + TRUE, FALSE and NOCHANGE. Use to "freeze" (disable database + writes) or "unfreeze" the database. Use the -FREEZE qualifier + only in conjunction with the -FILEHEADER qualifier. + + DSE releases -FREEZE when it EXITs. To hold the database(s), + CHANGE -FILEHEADER -FREEZE=TRUE and then SPAWN to perform other + operations. + +-KEY_MAX_SIZE + -K[EY_MAX_SIZE]=key_max_size + Changes the decimal value for the maximum allowable key size. + Use the -KEY_MAX_SIZE qualifier only in conjunction with the + -FILEHEADER qualifier. Reducing KEY_MAX_SIZE can restrict access + to existing data and cause GT.M-generated errors. Do not create + incompatible key and record sizes. If you make a permanent + change to the key size using DSE, use GDE to check that the + appropriate Global Directory contains the same key size for the + region. For more information on key and record sizes, refer to + the "Global Directory Editor" chapter in the GT.M Administration + and Operations Guide. + +-NULL_SUBSCRIPTS + -N[ULL_SUBSCRIPTS]=value + Sets the acceptability of null subscripts in database keys. + Possible values are: TRUE, FALSE and NOCHANGE. Use the + -NULL_SUBSCRIPTS qualifier only in conjunction with the + -FILEHEADER qualifier. Prohibiting null-subscripts can restrict + access to existing data and cause GT.M generated errors. + +-RECORD_MAX_SIZE + -REC[ORD_MAX_SIZE]=record_max_size + Changes the decimal value for the maximum allowable record size. + Use the -RECORD_MAX_SIZE qualifier only in conjunction with the + -FILEHEADER qualifier. Reducing the RECORD_MAX_SIZE can restrict + access to existing data and cause GT.M-generated errors. Do not + create incompatible key and record sizes. If you make a + permanent change to the record size using DSE, make sure GDE + contains the same record size for the appropriate Global + Directory. For more information on key and record sizes, refer + to "Global Directory Editor" chapter in GT.M Administration and + Operations Guide. + +-REFERENCE_COUNT + -REF[ERENCE_COUNT]=reference_count + Sets a field that tracks how many processes are accessing the + database from the current node. MUPIP INTEG and DSE use decimal + numbers for -REFERENCE_COUNT. Use the -REFERENCE_COUNT qualifier + only in conjunction with the -FILEHEADER qualifier. Restrict + CHANGE -FILEHEADER -REFERENCE_COUNT to the case where the + process running DSE has exclusive (stand-alone) access to the + database file. When DSE has sole access to a database file the + -REFERENCE_COUNT should be 1. This is an informational field and + does not have any effect on processing. + +-TIMERS_PENDING + -TI[MERS_PENDING]=timers_pending + Sets field that tracks the number of processes considering a + timed flush. Use the -TIMERS_PENDING qualifier only in + conjunction with the -FILEHEADER qualifier. Proper values are 0, + 1, and 2. Do not use this CHANGE qualifier except on + instructions from Greystone. + +-TOTAL_BLKS + -TO[TAL_BLKS]=total_blocks + Changes the total blocks field of the current file. Use the + -TOTAL_BLKS qualifier only in conjunction with the -FILEHEADER + qualifier. + + WARNING: The total blocks field should always reflect the actual + size of the database. Change this field only if it no longer + reflects the size of the database. + +-TRIGGER_FLUSH + -TR[IGGER_FLUSH]=trigger_flush + Sets the decimal value for the triggering threshold, in buffers, + for flushing the cache modified queue. Use the -TRIGGER_FLUSH + qualifier only in conjunction with the -FILEHEADER qualifier. Do + not use this CHANGE qualifier except on instructions from + Greystone. + +-WRITES_PER_FLUSH + -WR[ITES_PER_FLUSH]=writes_per_flush + Sets the decimal number of blocks to write in each flush. Use + the -WRITES_PER_FLUSH qualifier only in conjunction with the + -FILEHEADER qualifier. Do not use this CHANGE qualifier except + on instructions from Greystone. + +1 CLOSE + CL[OSE] + The CLOSE command closes the currently open output file. Use to close + the opened dump file. + + The format of the CLOSE command is: + + CL[OSE] + + The CLOSE command has no qualifiers. + +1 CRITICAL + CR[ITICAL] + The CRITICAL command along with its qualifiers displays and/or + modifies the status and contents of the critical section for the + current region. The critical section provides a control mechanism. + This field identifies, by its PID, the process presently managing + updates to database. + + The format of the CRITICAL command is: + + CR[ITICAL] -I[NIT] + -O[WNER] + -REL[EASE] + -REM[OVE] + -S[EIZE] + + By default, the CRITICAL command assumes the -OWNER qualifier, which + displays the status of the critical section. + +2 Qualifiers +-INIT + -I[NIT] + Reinitializes the critical section. The -RESET qualifier causes + all processes actively accessing that database file to signal an + error. Do not use -INIT without the -RESET parameter when other + processes are accessing the region. + + CAUTION: Using CRITICAL -INIT when the write owner of a critical + section is an active GT.M process may cause structural database + damage. + +-OWNER + -O[WNER] + Displays the ID of the process at the head of the critical + section, the ID of the process running DSE and the count of + critical read owners. When the current process owns the critical + section, DSE displays a warning message. The -OWNER qualifier is + incompatible with other qualifiers. + + Example: + + DSE> critical-owner + + Write critical section is currently unowned + +-RELEASE + -REL[EASE] + Releases the critical section if the process running DSE owns + the section. The -RELEASE qualifier is incompatible with other + qualifiers. + +-REMOVE + -REM[OVE] + Terminates any write ownership of the critical section. Use this + when the critical section is owned by a process that is + nonexistent or is known to no longer be running a GT.M image. + The -REMOVE qualifier is incompatible with other qualifiers. + + CAUTION: Using CRITICAL-REMOVE when the write owner of a + critical section is an active GT.M process may cause structural + database damage. + +-SEIZE + -S[EIZE] + Seizes the critical section if the section is available. The + -SEIZE qualifier is incompatible with other qualifiers. + +1 DUMP + D[UMP] + The DUMP command displays blocks, records or file headers. DUMP serves + as one of the primary DSE examination commands. Use the error messages + reported by MUPIP INTEG to determine what to DUMP and examine from the + database. DUMP also transfers records to a sequential file for future + study and/or for input to MUPIP LOAD. + + The DUMP command requires specification of either -BLOCK, -HEADER, + -RECORD or -FILEHEADER. + +2 Qualifiers +-BLOCK + -B[LOCK]=block_number + Specifies the starting block of the dump. The -BLOCK qualifier + is incompatible with the -FILEHEADER qualifier. + + On commands with no -BLOCK= qualifier, DSE uses the last block + handled by a DSE operation. In this case, when no block has been + accessed, .i.e., on the first block oriented command, DSE uses + block one (1). + + Example: + + DSE> dump -block=2 + + Block 2 Size 1B Level 0 TN 2 + Rec:1 Blk 2 Off 7 Size A Cmpc 0 Ptr 8 Key ^a + 7 : | A 0 0 61 0 0 8 0 0 0 + | . . . a . . . . . . + + Rec:2 Blk 2 Off 11 Size A Cmpc 0 Ptr B Key ^b + 11 : | A 0 0 62 0 0 B 0 0 0 + | . . . b . . . . . . + + +-COUNT + -C[OUNT]=count + Specifies the number of block headers or records to DUMP. The + -COUNT qualifier is incompatible with the -FILEHEADER qualifier. + +-FILEHEADER + -F[ILEHEADER] + Dumps file header information. The -FILEHEADER qualifier is + incompatible with all other qualifiers. + +-GLO + -G[LO] + Dumps the specified record or blocks into the current output + file in Global Output (GO) format. The -GLO qualifier is + incompatible with the -HEADER and -FILEHEADER qualifiers. + +-HEADER + -[NO]H[EADER] + Specifies whether the dump of the specified blocks or records is + restricted to, or excludes, headers. The -HEADER qualifier is + incompatible with the -GLO and -FILEHEADER qualifiers. + + By default, DUMP displays all information in a block or record. + +-OFFSET + -O[FFSET]=offset + Specifies the offset of the starting record for the dump. If the + offset does not point to the beginning of a record, DSE rounds + down to the last valid record start (e.g., DUMP -OFF=10 starts + at -OFF=A if that was the last record). The -OFFSET qualifier is + incompatible with the -RECORD and -FILEHEADER qualifiers. + +-RECORD + -R[ECORD]=record_number + Specifies the record number of the starting record of the dump. + The -RECORD qualifier is incompatible with the -OFFSET and + -FILEHEADER qualifiers. + +1 EVALUATE + EV[ALUATE] + The EVALUATE command displays a number in both hexadecimal and + decimal. Use it to translate a hexadecimal number to decimal and vice + versa. The -DECIMAL and -HEXADECIMAL qualifiers specify the input base + for the number. + + The format of the EVALUATE command is: + + EV[ALUATE] -D[ECIMAL] + -H[EXADECIMAL] + -N[UMBER]=number + + The -NUMBER qualifier is required. + + By default, EVALUATE treats the number as having a hexadecimal base. + +2 Qualifiers +-DECIMAL + -D[ECIMAL] + Specifies that the input number has a decimal base. The -DECIMAL + qualifier is incompatible with the -HEXADECIMAL qualifier + +-HEXADECIMAL + -H[EXADECIMAL] + Specifies that the input number has a hexadecimal base. The + -HEXADECIMAL qualifier is incompatible with the -DECIMAL + qualifier. + +-NUMBER + -N[UMBER]=number + Specifies the number to evaluate. This qualifier is required. + + Example: + + DSE> evaluate-number=61 + + Hex: 61 Dec: 97 + + +1 EXIT + EX[IT] + The EXIT command ends a DSE session. + + The format of the EXIT command is: + + EX[IT] + + The EXIT command has no qualifiers. + +1 FIND + F[IND] + The FIND command directs DSE to a given block or region. At the + beginning of a DSE session, use the FIND -REGION command to select the + target region. + + The FIND command, except with the -FREEBLOCK and -REGION qualifiers, + uses the index tree to locate blocks. FIND can locate blocks only + within the index tree structure. If you need to locate keys + independent of their attachment to the tree, use the RANGE command. + +2 Qualifiers +-BLOCK + -B[LOCK]=block_number + Specifies the block to find. The -BLOCK qualifier is + incompatible with the -KEY and -REGION qualifiers. + + On commands with no -BLOCK= qualifier, DSE uses the last block + handled by a DSE operation. In this case, when no block has been + accessed, .i.e., on the first block oriented command, DSE uses + block one (1). + +-EXHAUSTIVE + -E[XHAUSTIVE] + Instructs DSE to search the entire index structure for the + desired path or siblings. FIND -EXHAUSTIVE is useful in locating + blocks that are in the tree but not indexed correctly. The + -EXHAUSTIVE qualifier is incompatible with the -FREEBLOCK, -KEY + and -REGION qualifiers. + +-FREEBLOCK + -F[REEBLOCK] + Finds the nearest free block to the block specified by -HINT. + The -FREEBLOCK qualifier is incompatible with all other + qualifiers except -BLOCK and -HINT. The -HINT qualifier is + required with the -FREEBLOCK qualifier. + +-HINT + -H[INT]=block_number + Designates the starting point of a -FREEBLOCK search. The -HINT + qualifier can be used only in conjunction with the -FREEBLOCK + qualifier. + +-KEY + -K[EY]=key + Searches the database for the block containing the specified + key. Enclose a MUMPS style key in quotes (""). The -KEY + qualifier is incompatible with all other qualifiers. + +-REGION + -R[EGION][=region] + Switches to the named Global Directory region. The -REGION + qualifier is incompatible with all other qualifiers. + + -REGION without a specified region, or -REGION=*, displays all + existing regions in the database. + +-SIBLINGS + -S[IBLINGS] + Displays the block numbers of the logical siblings of the + specified block. The logical siblings are the blocks that + logically exist to the right and left of the given block in the + database tree structure. The -SIBLINGS qualifier is incompatible + with the -FREEBLOCK, -KEY and -REGION qualifiers. + +1 HELP + H[ELP] + The HELP command explains DSE commands. The format of the HELP command + is: + + H[ELP] [item] + + Item tells HELP which information to display. Enter the DSE command + (item) after the HELP command or at the Topic prompt. Use or + to return to the DSE prompt. + +1 INTEGRIT + I[NTEGRIT] + The INTEGRIT command checks the internal consistency of a non-bitmap + block. INTEG reports errors in hexadecimal notation. + + The format of the INTEGRIT command is: + + I[NTEGRIT] -B[LOCK]=block_number + +2 Qualifiers +-BLOCK + -B[LOCK]=block_number + Specifies the block for DSE to check. + + On commands with no -BLOCK= qualifier, DSE uses the last block + handled by a DSE operation. In this case, when no block has been + accessed, .i.e., on the first block oriented command, DSE uses + block one (1). + +1 MAPS + M[APS] + The MAPS command examines or updates bit maps. + + MAPS forces blocks either -BUSY or -FREE. The -MASTER qualifier + reflects the current status of a local bit map back into the master + map. The -RESTORE qualifier rebuilds all maps and should be used with + a great deal of caution as it can destroy important information. + + By default, MAPS shows the status of the bit map for the specified + block. + +2 Qualifiers +-BLOCK + -BL[OCK]=block_number + Specifies the target block for MAPS. The -BLOCK qualifier is + incompatible with the -RESTORE_ALL qualifier. + + On commands with no -BLOCK= qualifier, DSE uses the last block + handled by a DSE operation. In this case, when no block has been + accessed, .i.e., on the first block-oriented command, DSE uses + block one (1). + +-BUSY + -BU[SY] + Marks the current block busy in the block's local map and + appropriately updates the master bit map. The -BUSY qualifier is + incompatible with all qualifiers except -BLOCK. + +-FREE + -F[REE] + Marks the current block free in the block's local map and + appropriately updates the master bit map. The -FREE qualifier is + incompatible with all qualifiers except -BLOCK. + +-MASTER + -M[ASTER] + Sets the master bit map bit associated with the current block's + local map according to whether that local map is full or not. + The -MASTER qualifier is incompatible with all qualifiers except + -BLOCK. + +-RESTORE_ALL + -R[ESTORE_ALL] + Sets all local bit maps and the master bit map to reflect the + blocks used in the database file. Use RESTORE_ALL only if the + database contents are known to be correct, but a large number of + the bit maps require correction. The -RESTORE_ALL qualifier is + incompatible with all other qualifiers. + +1 OPEN + OP[EN] + The OPEN command opens a file for sequential output of global variable + data. OPEN a file to which you want to "dump" information. + + The format of the OPEN command is: + + OP[EN] -F[ILE]=file + + If an OPEN command does not have a -FILE qualifier, DSE reports the + name of the current output file. + +2 Qualifiers +-F[ILE] + -F[ILE]=file + Specifies the file to open. + +1 OVERWRITE + OV[ERWRITE] + The OVERWRITE command overwrites the specified string onto the given + offset in the current block. Use extreme caution when using this + command. + + The format of the OVERWRITE command is: + + OV[ERWRITE] -D[ATA]=string + -O[FFSET]=offset + +2 Qualifiers +-D[ATA] + -D[ATA]=string + Specifies the data to be written. Use quotes around string and + escape codes of the form \a or \ab, where a and b are + hexadecimal digits, for non-printing characters. \\ translates + to a single backslash. + +-O[FFSET] + -O[FFSET]=offset + Specifies the offset in the current block where the overwrite + should begin. + +1 PAGE + P[AGE] + The PAGE command sends one form feed to the output device. Use PAGE to + add form feeds to a dump file, making the hardcopy file easier to + read. If you plan to use the dump file with MUPIP LOAD, do not use + PAGE. + + The format of the PAGE command is: + + P[AGE] + + The PAGE command has no qualifiers. + +1 RANGE + RA[NGE] + The RANGE command finds all blocks in the database whose first key + falls in the specified range of keys. The RANGE command may take a + very long time unless the range specified by -FROM and -TO is close + together. Use FIND -KEY first to determine whether the key appears in + the tree. + + The format of the RANGE command is: + + RA[NGE] -F[ROM]=block + -T[O]=block + -L[OWER]=key + -U[PPER]=key + +2 Qualifiers +-FROM + -F[ROM]=block_number + Specifies a starting block number for the range search. + + By default, RANGE starts processing at the beginning of the + file. + +-TO + -T[O]=block_number + Specifies an ending block number for the range search. + + By default, RANGE stops processing at the end of the file. + +-LOWER + -L[OWER]=key + Specifies the lower bound for the key range. + +-UPPER + -U[PPER]=key + Specifies the upper bound for the key range. + +1 REMOVE + REM[OVE] + The REMOVE command removes one or more records or a save buffer. + + The format of the REMOVE command is: + + REM[OVE] -B[LOCK]=block_number + -C[OUNT]=count + -O[FFSET] + -R[ECORD]=record_number + -V[ERSION]=version_number + + The version number is specified in decimal. + +2 Qualifiers +-BLOCK + -B[LOCK]=block_number + Specifies the block associated with the record or buffer being + deleted. + + On commands with no -BLOCK= qualifier, DSE uses the last block + handled by a DSE operation. In this case, when no block has been + accessed, that is, on the first block-oriented command, DSE uses + block one (1). + +-COUNT + -C[OUNT]=count + Specifies the number of records to remove. The -COUNT qualifier + is incompatible with the -VERSION qualifier. + + By default, REMOVE deletes a single record. + +-OFFSET + -O[FFSET]=offset + Specifies the offset of the record to remove. The -OFFSET + qualifier is incompatible with the -RECORD and -VERSION + qualifiers. + +-RECORD + -R[ECORD]=record_number + Specifies the record number of the record to remove. The -RECORD + qualifier is incompatible with the -OFFSET and -VERSION + qualifiers. + +-VERSION + -V[ERSION]=version_number + Specifies the decimal version number in decimal of the save + buffer to remove. -VERSION is required to REMOVE a SAVE buffer. + -VERSION is incompatible with all qualifiers except -BLOCK. + +1 RESTORE + RES[TORE] + The RESTORE command restores saved versions of blocks. + + The format of the RESTORE command is: + + RES[TORE] -B[LOCK]=block_number + -F[ROM]=from + -R[EGION]=region + -V[ERSION]=version_number + + The version number is specified in decimal. + +2 Qualifiers +-BLOCK + -B[LOCK]=block_number + Specifies the block to restore. + + On commands with no -BLOCK= qualifier, DSE uses the last block + handled by a DSE operation. In this case, when no block has been + accessed, .i.e., on the first block oriented command, DSE uses + block one (1). + +-FROM + -F[ROM]=block_number + Specifies the block number of the save buffer to restore. + + By default, RESTORE uses the target block number as the SAVE + block number. + +-REGION + -R[EGION]=region_number + Specifies the region of the saved buffer to restore. + + By default, RESTORE uses SAVE buffers from the current region. + +-VERSION + -V[ERSION]=version_number + Specifies the decimal version number of the block to restore. + The version number is required. + +1 SAVE + SA[VE] + The SAVE command saves versions of blocks or displays a listing of + saved versions. Saved information is lost when DSE EXITs. Use with the + RESTORE command to move blocks. As a safety feature, use SAVE to + retain fallback copies of database blocks before changing them. + + The format of the SAVE command is: + + SA[VE] -B[LOCK]=block_number + -C[OMMENT]=string + -L[IST] + +2 Qualifiers +-BLOCK + -B[LOCK]=block_number + Specifies the block to save. + + On commands with no -BLOCK= qualifier, DSE uses the last block + handled by a DSE operation. In this case, when no block has been + accessed, .i.e., on the first block-oriented command, DSE uses + block one (1). + +-COMMENT + -C[OMMENT]=string + Specifies a comment to save with the block. Enclose the comment + in quotes (""). The -COMMENT qualifier is incompatible with the + -LIST qualifier. + +-LIST + -L[IST] + Lists saved versions of specified blocks. The -LIST qualifier is + incompatible with the -COMMENT qualifier. + + By default, SAVE -LIST provides a directory of all SAVEd blocks. + +1 SHIFT + SH[IFT] + The SHIFT command shifts data in a block, filling the block with zeros + or shortening the block. The format of the SHIFT command is: + + SH[IFT] -B[ACKWARD]=shift + -F[ORWARD]=shift + -O[FFSET]=offset + +2 Qualifiers +-BACKWARD + -B[ACKWARD]=shift + Specifies the extent to which DSE should shift data backwards + towards the block header. The -BACKWARD qualifier is + incompatible with the -FORWARD qualifier. + +-FORWARD + -F[ORWARD]=shift + Specifies the extent to which DSE should shift data forward + towards the end of the block. The -FORWARD qualifier is + incompatible with the -BACKWARD qualifier. + +-OFFSET + -O[FFSET]=offset + Specifies the starting offset of the portion of the block to + shift. + +1 SPAWN + SP[AWN] + The SPAWN command forks a child process for access to the shell + without terminating the current DSE environment. Use the SPAWN command + to suspend a session and issue shell commands such as MUPIP INTEG + -REGION or GDE. The SPAWN command leaves your terminal at the input + prompt of the shell of the spawned process. + + The format of the SPAWN command is: + + SP[AWN] [command] + + The SPAWN command has no qualifiers. + +1 WCINIT + W[CINIT] + The WCINIT command reinitializes the global buffers of the current + region. Because it cleans out the cache, WCINIT is a very dangerous + command and therefore should not be used except under Greystone + supervision. + + WARNING: A WCINIT command issued while normal database operations are + in progress can cause catastrophic damage to the database. + + The format of the WCINIT command is: + + W[CINIT] + + The WCINIT command has no qualifiers. + + When you issue the WCINIT command, DSE issues the CONFIRMATION: + prompt. You must verify the WCINIT command by responding with a "YES." + + If you do not confirm the WCINIT, DSE issues the message: + + No action taken, enter yes at the CONFIRMATION prompt to initialize + global buffers. + + diff --git a/sr_port/dse_adrec.c b/sr_port/dse_adrec.c new file mode 100644 index 0000000..50c7b09 --- /dev/null +++ b/sr_port/dse_adrec.c @@ -0,0 +1,254 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "min_max.h" /* needed for gdsblkops.h */ +#include "gdsblkops.h" +#include "gdscc.h" +#include "cli.h" +#include "copy.h" +#include "filestruct.h" +#include "jnl.h" +#include "skan_offset.h" +#include "skan_rnum.h" +#include "dse.h" + +/* Include prototypes */ +#include "t_qread.h" +#include "t_write.h" +#include "t_end.h" +#include "t_begin_crit.h" +#include "gvcst_blk_build.h" +#include "util.h" +#include "t_abort.h" + +GBLREF char *update_array, *update_array_ptr; +GBLREF uint4 update_array_size; +GBLREF srch_hist dummy_hist; +GBLREF block_id patch_curr_blk; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF gd_addr *gd_header; +GBLREF char patch_comp_key[MAX_KEY_SZ + 1]; +GBLREF unsigned char patch_comp_count; +GBLREF gd_region *gv_cur_region; +GBLREF cw_set_element cw_set[]; +GBLREF unsigned char *non_tp_jfb_buff_ptr; + + +void dse_adrec(void) +{ + char data[MAX_LINE], key[MAX_KEY_SZ + 1]; + unsigned char cc; + sm_uc_ptr_t new_bp, lbp, b_top, rp, r_top, key_top; + short int size, new_len, rsize; + int data_len, key_len; + int4 blk_seg_cnt, blk_size; + block_id blk; + blk_segment *bs1, *bs_ptr; + srch_blk_status blkhist; + + error_def(ERR_CPBEYALLOC); + error_def(ERR_DBRDONLY); + error_def(ERR_DSEBLKRDFAIL); + error_def(ERR_DSEFAIL); + error_def(ERR_GVIS); + error_def(ERR_REC2BIG); + + if (gv_cur_region->read_only) + rts_error(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); + CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ + if (cli_present("BLOCK") == CLI_PRESENT) + { + if(!cli_get_hex("BLOCK", (uint4 *)&blk)) + return; + patch_curr_blk = blk; + } + if (patch_curr_blk < 0 || patch_curr_blk >= cs_addrs->ti->total_blks || !(patch_curr_blk % cs_addrs->hdr->bplmap)) + { + util_out_print("Error: invalid block number.", TRUE); + return; + } + if (cli_present("KEY") != CLI_PRESENT) + { + util_out_print("Error: key must be specified.", TRUE); + return; + } + if (!dse_getki(&key[0], &key_len, LIT_AND_LEN("KEY"))) + return; + t_begin_crit(ERR_DSEFAIL); + blk_size = cs_addrs->hdr->blk_size; + blkhist.blk_num = patch_curr_blk; + if (!(blkhist.buffaddr = t_qread(blkhist.blk_num, &blkhist.cycle, &blkhist.cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + + lbp = (uchar_ptr_t)malloc(blk_size); + memcpy(lbp, blkhist.buffaddr, blk_size); + + if (((blk_hdr_ptr_t)lbp)->bsiz > blk_size) + ((blk_hdr_ptr_t)lbp)->bsiz = blk_size; + else if (((blk_hdr_ptr_t)lbp)->bsiz < SIZEOF(blk_hdr)) + ((blk_hdr_ptr_t)lbp)->bsiz = SIZEOF(blk_hdr); + + b_top = lbp + ((blk_hdr_ptr_t)lbp)->bsiz; + if (((blk_hdr_ptr_t)lbp)->levl) + { + if (cli_present("POINTER") != CLI_PRESENT) + { + util_out_print("Error: block pointer must be specified for this index block record.", TRUE); + free(lbp); + t_abort(gv_cur_region, cs_addrs); + return; + } + if (!cli_get_hex("POINTER", (uint4 *)&blk)) + { + t_abort(gv_cur_region, cs_addrs); + free(lbp); + return; + } + if (blk < 0 || blk >= cs_addrs->ti->total_blks || !(blk % cs_addrs->hdr->bplmap)) + { + util_out_print("Error: pointer is an invalid block number.", TRUE); + free(lbp); + t_abort(gv_cur_region, cs_addrs); + return; + } + MEMCP(&data[0], (char *)&blk, 0, SIZEOF(block_id), SIZEOF(block_id)); + data_len = SIZEOF(block_id); + } else + { + if (cli_present("DATA") != CLI_PRESENT) + { + util_out_print("Error: data must be specified for this data block record.", TRUE); + free(lbp); + t_abort(gv_cur_region, cs_addrs); + return; + } + if (FALSE == dse_data(&data[0], &data_len)) + { + free(lbp); + t_abort(gv_cur_region, cs_addrs); + return; + } + if (key_len + data_len > cs_addrs->hdr->max_rec_size) + { + rts_error(VARLSTCNT(10) ERR_REC2BIG, 4, key_len + data_len, (int4)cs_addrs->hdr->max_rec_size, + REG_LEN_STR(gv_cur_region), ERR_GVIS, 2, LEN_AND_STR(key)); + } + + } + if (cli_present("RECORD") == CLI_PRESENT) + { + if (!(rp = skan_rnum(lbp, TRUE))) + { + free(lbp); + t_abort(gv_cur_region, cs_addrs); + return; + } + } else if (cli_present("OFFSET") == CLI_PRESENT) + { + if (!(rp = skan_offset(lbp, TRUE))) + { + free(lbp); + t_abort(gv_cur_region, cs_addrs); + return; + } + } else + { + util_out_print("Error: must specify a record number or offset for the record to be added.", TRUE); + free(lbp); + t_abort(gv_cur_region, cs_addrs); + return; + } + + new_bp = (uchar_ptr_t)malloc(blk_size); + size = (key_len < patch_comp_count) ? key_len : patch_comp_count; + for (cc = 0; cc < size && patch_comp_key[cc] == key[cc]; cc++) + ; + ((rec_hdr_ptr_t)new_bp)->cmpc = cc; + new_len = key_len - cc + data_len + SIZEOF(rec_hdr); + PUT_SHORT(&((rec_hdr_ptr_t)new_bp)->rsiz, new_len); + MEMCP(new_bp, &key[cc], SIZEOF(rec_hdr), key_len - cc, blk_size); + MEMCP(new_bp, &data[0], SIZEOF(rec_hdr) + key_len - cc, data_len, blk_size); + if (rp < b_top) + { + GET_SHORT(rsize, &((rec_hdr_ptr_t)rp)->rsiz); + if (rsize < SIZEOF(rec_hdr)) + r_top = rp + SIZEOF(rec_hdr); + else + r_top = rp + rsize; + if (r_top >= b_top) + r_top = b_top; + if (((blk_hdr_ptr_t)lbp)->levl) + key_top = r_top - SIZEOF(block_id); + else + { + for (key_top = rp + SIZEOF(rec_hdr); key_top < r_top; ) + if (!*key_top++ && !*key_top++) + break; + } + if (((rec_hdr_ptr_t)rp)->cmpc > patch_comp_count) + cc = patch_comp_count; + else + cc = ((rec_hdr_ptr_t)rp)->cmpc; + size = key_top - rp - SIZEOF(rec_hdr); + if (size > SIZEOF(patch_comp_key) - 2 - cc) + size = SIZEOF(patch_comp_key) - 2 - cc; + if (size < 0) + size = 0; + memcpy(&patch_comp_key[cc], rp + SIZEOF(rec_hdr), size); + patch_comp_count = cc + size; + size = (key_len < patch_comp_count) ? key_len : patch_comp_count; + for (cc = 0; cc < size && patch_comp_key[cc] == key[cc]; cc++) + ; + ((rec_hdr_ptr_t)(new_bp + new_len))->cmpc = cc; + rsize = patch_comp_count - cc + r_top - key_top + SIZEOF(rec_hdr); + PUT_SHORT(&((rec_hdr_ptr_t)(new_bp + new_len))->rsiz, rsize); + MEMCP(new_bp, &patch_comp_key[cc], new_len + SIZEOF(rec_hdr), patch_comp_count - cc, blk_size); + MEMCP(new_bp, key_top, new_len + SIZEOF(rec_hdr) + patch_comp_count - cc, b_top - key_top, blk_size); + new_len += patch_comp_count - cc + SIZEOF(rec_hdr) + b_top - key_top; + } + if (rp - lbp + new_len > blk_size) + { + util_out_print("Error: record too large for remaining space in block.", TRUE); + free(lbp); + free(new_bp); + t_abort(gv_cur_region, cs_addrs); + return; + } + memcpy(rp, new_bp, new_len); + free(new_bp); + ((blk_hdr_ptr_t)lbp)->bsiz += new_len + (unsigned int)(rp - b_top); + + BLK_INIT(bs_ptr, bs1); + BLK_SEG(bs_ptr, (uchar_ptr_t)lbp + SIZEOF(blk_hdr), (int)((blk_hdr_ptr_t)lbp)->bsiz - SIZEOF(blk_hdr)); + if (!BLK_FINI(bs_ptr, bs1)) + { + util_out_print("Error: bad blk build.", TRUE); + free(lbp); + t_abort(gv_cur_region, cs_addrs); + return; + } + t_write(&blkhist, (unsigned char *)bs1, 0, 0, ((blk_hdr_ptr_t)lbp)->levl, TRUE, FALSE, GDS_WRITE_KILLTN); + BUILD_AIMG_IF_JNL_ENABLED(cs_data, non_tp_jfb_buff_ptr, cs_addrs->ti->curr_tn); + t_end(&dummy_hist, NULL, TN_NOT_SPECIFIED); + + free(lbp); + return; +} diff --git a/sr_port/dse_adstar.c b/sr_port/dse_adstar.c new file mode 100644 index 0000000..705902f --- /dev/null +++ b/sr_port/dse_adstar.c @@ -0,0 +1,134 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "min_max.h" /* needed for gdsblkops.h */ +#include "gdsblkops.h" +#include "gdscc.h" +#include "cli.h" +#include "copy.h" +#include "filestruct.h" +#include "jnl.h" +#include "dse.h" + +/* Include prototypes */ +#include "t_qread.h" +#include "t_write.h" +#include "t_end.h" +#include "t_begin_crit.h" +#include "gvcst_blk_build.h" +#include "util.h" +#include "t_abort.h" + +GBLREF char *update_array, *update_array_ptr; +GBLREF gd_region *gv_cur_region; +GBLREF uint4 update_array_size; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF srch_hist dummy_hist; +GBLREF gd_addr *gd_header; +GBLREF block_id patch_curr_blk; +GBLREF cw_set_element cw_set[]; +GBLREF unsigned char *non_tp_jfb_buff_ptr; + +void dse_adstar(void) +{ + uchar_ptr_t lbp, b_top; + block_id blk; + blk_segment *bs1, *bs_ptr; + int4 blk_seg_cnt, blk_size; + short rsize; + srch_blk_status blkhist; + + error_def(ERR_DBRDONLY); + error_def(ERR_DSEBLKRDFAIL); + error_def(ERR_DSEFAIL); + + if (gv_cur_region->read_only) + rts_error(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); + CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ + if (cli_present("BLOCK") == CLI_PRESENT) + { + if (!cli_get_hex("BLOCK", (uint4 *)&blk)) + return; + patch_curr_blk = blk; + } + if (patch_curr_blk < 0 || patch_curr_blk >= cs_addrs->ti->total_blks || !(patch_curr_blk % cs_addrs->hdr->bplmap)) + { + util_out_print("Error: invalid block number.", TRUE); + return; + } + if (cli_present("POINTER") != CLI_PRESENT) + { + util_out_print("Error: block pointer must be specified.", TRUE); + return; + } + if (!cli_get_hex("POINTER", (uint4 *)&blk)) + return; + t_begin_crit(ERR_DSEFAIL); + blk_size = cs_addrs->hdr->blk_size; + blkhist.blk_num = patch_curr_blk; + if (!(blkhist.buffaddr = t_qread(blkhist.blk_num, &blkhist.cycle, &blkhist.cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + lbp = (uchar_ptr_t)malloc(blk_size); + memcpy(lbp, blkhist.buffaddr, blk_size); + + if (!((blk_hdr_ptr_t)lbp)->levl) + { + util_out_print("Error: cannot add a star record to a data block.", TRUE); + free(lbp); + t_abort(gv_cur_region, cs_addrs); + return; + } + if (((blk_hdr_ptr_t) lbp)->bsiz > blk_size) + b_top = lbp + blk_size; + else if (((blk_hdr_ptr_t) lbp)->bsiz < SIZEOF(blk_hdr)) + b_top = lbp + SIZEOF(blk_hdr); + else + b_top = lbp + ((blk_hdr_ptr_t) lbp)->bsiz; + if (b_top - lbp > blk_size - SIZEOF(rec_hdr) - SIZEOF(block_id)) + { + util_out_print("Error: not enough free space in block for a star record.", TRUE); + free(lbp); + t_abort(gv_cur_region, cs_addrs); + return; + } + rsize = SIZEOF(rec_hdr) + SIZEOF(block_id); + PUT_SHORT(&((rec_hdr_ptr_t)b_top)->rsiz, rsize); + ((rec_hdr_ptr_t) b_top)->cmpc = 0; + PUT_LONG((block_id_ptr_t)(b_top + SIZEOF(rec_hdr)), blk); + ((blk_hdr_ptr_t)lbp)->bsiz += (unsigned int)(SIZEOF(rec_hdr) + SIZEOF(block_id)); + + BLK_INIT(bs_ptr, bs1); + BLK_SEG(bs_ptr, (uchar_ptr_t)lbp + SIZEOF(blk_hdr), (int)((blk_hdr_ptr_t)lbp)->bsiz - SIZEOF(blk_hdr)); + if (!BLK_FINI(bs_ptr, bs1)) + { + util_out_print("Error: bad blk build.", TRUE); + free(lbp); + t_abort(gv_cur_region, cs_addrs); + return; + } + t_write(&blkhist, (unsigned char *)bs1, 0, 0, ((blk_hdr_ptr_t)lbp)->levl, TRUE, FALSE, GDS_WRITE_KILLTN); + BUILD_AIMG_IF_JNL_ENABLED(cs_data, non_tp_jfb_buff_ptr, cs_addrs->ti->curr_tn); + t_end(&dummy_hist, NULL, TN_NOT_SPECIFIED); + + free(lbp); + return; +} diff --git a/sr_port/dse_all.c b/sr_port/dse_all.c new file mode 100644 index 0000000..2ca494f --- /dev/null +++ b/sr_port/dse_all.c @@ -0,0 +1,271 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gtm_stdio.h" +#include "util.h" +#include "cli.h" +#include "gdsroot.h" +#include "gt_timer.h" +#include "gtmmsg.h" + +#if defined(UNIX) +#define GET_CONFIRM(X,Y) {PRINTF("CONFIRMATION: ");FGETS((X), (Y), stdin, fgets_res);Y = strlen(X);} + +#include "gtm_ipc.h" + +GBLREF uint4 user_id; +#elif defined(VMS) +#define GET_CONFIRM(X,Y) {if(!cli_get_str("CONFIRMATION",(X),&(Y))) {rts_error(VARLSTCNT(1) ERR_DSEWCINITCON); \ + return;}} +#else +#error UNSUPPORTED PLATFORM +#endif + +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "jnl.h" +#include "gdskill.h" +#include "gdscc.h" +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" +#include "min_max.h" /* needed for init_root_gv.h */ +#include "init_root_gv.h" +#include "dse.h" + +#ifdef UNIX +#include "mutex.h" +#endif +#include "wcs_flu.h" +#include /* for VSIG_ATOMIC_T */ + +GBLREF VSIG_ATOMIC_T util_interrupt; +GBLREF block_id patch_curr_blk; +GBLREF gd_addr *gd_header; +GBLREF gd_region *gv_cur_region; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF short crash_count; +GBLREF uint4 process_id; +GBLREF gd_addr *original_header; +GBLREF boolean_t dse_all_dump; /* TRUE if DSE ALL -DUMP is specified */ + +void dse_all(void) +{ + gd_region *ptr; + tp_region *region_list, *rg, *rg_last, *rg_new; /* A handy structure for maintaining a list of regions */ + int i, j; + sgmnt_addrs *old_addrs, *csa; + gd_region *old_region; + block_id old_block; + int4 stat; + char confirm[256]; + unsigned short len; + boolean_t ref = FALSE; + boolean_t crit = FALSE; + boolean_t wc = FALSE; + boolean_t flush = FALSE; + boolean_t freeze = FALSE; + boolean_t nofreeze = FALSE; + boolean_t seize = FALSE; + boolean_t release = FALSE; + boolean_t dump = FALSE; + boolean_t override = FALSE; + boolean_t was_crit; + gd_addr *temp_gdaddr; + gd_binding *map; +#ifdef UNIX + char *fgets_res; +#endif + + error_def(ERR_DSEWCINITCON); + error_def(ERR_FREEZE); + error_def(ERR_DBRDONLY); + error_def(ERR_FREEZECTRL); + + old_addrs = cs_addrs; + old_region = gv_cur_region; + old_block = patch_curr_blk; + temp_gdaddr = gd_header; + gd_header = original_header; + if (cli_present("RENEW") == CLI_PRESENT) + { + crit = ref = wc = nofreeze = TRUE; + len = SIZEOF(confirm); + GET_CONFIRM(confirm,len); + if (confirm[0] != 'Y' && confirm[0] != 'y') + { + rts_error(VARLSTCNT(1) ERR_DSEWCINITCON); + return; + } + } else + { + if (cli_present("CRITINIT") == CLI_PRESENT) + crit = TRUE; + if (cli_present("REFERENCE") == CLI_PRESENT) + ref = TRUE; + if (cli_present("WCINIT") == CLI_PRESENT) + { + wc = TRUE; + len = SIZEOF(confirm); + GET_CONFIRM(confirm,len); + if (confirm[0] != 'Y' && confirm[0] != 'y') + { + rts_error(VARLSTCNT(1) ERR_DSEWCINITCON); + return; + } + } + if (cli_present("BUFFER_FLUSH") == CLI_PRESENT) + flush = TRUE; + if (cli_present("SEIZE") == CLI_PRESENT) + seize = TRUE; + if (cli_present("RELEASE") == CLI_PRESENT) + release = TRUE; + stat = cli_present("FREEZE"); + if (stat == CLI_NEGATED) + nofreeze = TRUE; + else if (stat == CLI_PRESENT) + { + freeze = TRUE; + nofreeze = FALSE; + } + if (cli_present("OVERRIDE") == CLI_PRESENT) + override = TRUE; + if (cli_present("DUMP") == CLI_PRESENT) + dump = TRUE; + } + if (!dump && gv_cur_region->read_only) + rts_error(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); + region_list = NULL; + for (i = 0, ptr = gd_header->regions; i < gd_header->n_regions; i++, ptr++) + { + if (ptr->dyn.addr->acc_meth != dba_bg && ptr->dyn.addr->acc_meth != dba_mm) + { + util_out_print("Skipping region !AD: not BG or MM access",TRUE,ptr->rname_len,&ptr->rname[0]); + continue; + } + if (!ptr->open) + { + util_out_print("Skipping region !AD as it is not bound to any namespace.", TRUE, + ptr->rname_len, &ptr->rname[0]); + continue; + } + if (dump) + { + gv_cur_region = ptr; + cs_addrs = &FILE_INFO(gv_cur_region)->s_addrs; + dse_all_dump = TRUE; + dse_dmp_fhead(); + assert(!dse_all_dump); /* should have been reset by "dse_dmp_fhead" */ + } else + { + /* put on region list in order of ftok value so processed in same order that crits are obtained */ + csa = &FILE_INFO(ptr)->s_addrs; + insert_region(ptr, &(region_list), NULL, SIZEOF(tp_region)); + } + } + if (!dump) + { /* Now run the list of regions in the sorted ftok order to execute the desired commands */ + for (rg = region_list; NULL != rg; rg = rg->fPtr) + { + gv_cur_region = rg->reg; + switch(gv_cur_region->dyn.addr->acc_meth) + { + case dba_mm: + case dba_bg: + cs_addrs = &FILE_INFO(gv_cur_region)->s_addrs; + break; + default: + GTMASSERT; + } + patch_curr_blk = get_dir_root(); + + if (crit) + { + UNIX_ONLY(gtm_mutex_init(gv_cur_region, NUM_CRIT_ENTRY, TRUE);) + VMS_ONLY(mutex_init(cs_addrs->critical, NUM_CRIT_ENTRY, TRUE);) + cs_addrs->nl->in_crit = 0; + cs_addrs->hold_onto_crit = FALSE; /* reset this just before cs_addrs->now_crit is reset */ + cs_addrs->now_crit = FALSE; + } + if (cs_addrs->critical) + crash_count = cs_addrs->critical->crashcnt; + if (freeze) + { + while (REG_ALREADY_FROZEN == region_freeze(gv_cur_region, TRUE, override, FALSE)) + { + hiber_start(1000); + if (util_interrupt) + { + gtm_putmsg(VARLSTCNT(1) ERR_FREEZECTRL); + break; + } + } + if (freeze != !(cs_addrs->hdr->freeze)) + util_out_print("Region !AD is now FROZEN", TRUE, REG_LEN_STR(gv_cur_region)); + } + was_crit = cs_addrs->now_crit; + if (seize) + { + if (!was_crit) + grab_crit(gv_cur_region); /* no point seizing crit if WE already have it held */ + cs_addrs->hold_onto_crit = TRUE; /* need to do this AFTER grab_crit */ + } + if (wc) + { + if (!was_crit && !seize) + grab_crit(gv_cur_region); + bt_init(cs_addrs); + if (cs_addrs->hdr->acc_meth == dba_bg) + { + bt_refresh(cs_addrs); + db_csh_ini(cs_addrs); + db_csh_ref(cs_addrs); + } + if (!was_crit && (!seize || release)) + rel_crit(gv_cur_region); + } + if (flush) + wcs_flu(WCSFLU_FLUSH_HDR | WCSFLU_WRITE_EPOCH | WCSFLU_SYNC_EPOCH); + if (release) + { /* user wants crit to be released unconditionally so "was_crit" not checked like everywhere else */ + cs_addrs->hold_onto_crit = FALSE; /* need to do this BEFORE rel_crit */ + if (cs_addrs->now_crit) + rel_crit(gv_cur_region); + else + util_out_print("Current process does not own the Region: !AD.", + TRUE, REG_LEN_STR(gv_cur_region)); + } + if (nofreeze) + { + if (REG_ALREADY_FROZEN == region_freeze(gv_cur_region,FALSE, override, FALSE)) + util_out_print("Region: !AD is frozen by another user, not releasing freeze",TRUE, + REG_LEN_STR(gv_cur_region)); + else + util_out_print("Region !AD is now UNFROZEN", TRUE, REG_LEN_STR(gv_cur_region)); + } + if (ref) + cs_addrs->nl->ref_cnt = 1; + } + } + cs_addrs = old_addrs; + gv_cur_region = old_region; + patch_curr_blk = old_block; + GET_SAVED_GDADDR(gd_header, temp_gdaddr, map, gv_cur_region); + return; +} diff --git a/sr_port/dse_b_dmp.c b/sr_port/dse_b_dmp.c new file mode 100644 index 0000000..dfa7f41 --- /dev/null +++ b/sr_port/dse_b_dmp.c @@ -0,0 +1,261 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "gdsbml.h" +#include "cli.h" +#include "dse.h" +#include "util.h" + +/* Include prototypes */ +#include "t_qread.h" + +#define REUSABLE_CHAR ":" +#define FREE_CHAR "." +#define BUSY_CHAR "X" +#define CORRUPT_CHAR "?" +#define MAX_UTIL_LEN 80 + +GBLREF VSIG_ATOMIC_T util_interrupt; +GBLREF block_id patch_curr_blk; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gd_region *gv_cur_region; +GBLREF int patch_is_fdmp; +GBLREF int patch_fdmp_recs; +GBLREF int patch_rec_counter; +LITREF char *gtm_dbversion_table[]; + +boolean_t dse_b_dmp(void) +{ + int4 util_len, head, lmap_num, iter1, iter2, mapsize, bplmap, nocrit_present, dummy_int, len, count; + unsigned char util_buff[MAX_UTIL_LEN], mask; + boolean_t free, was_crit, invalid_bitmap = FALSE, is_mm; + block_id blk; + sm_uc_ptr_t bp, b_top, rp, mb, dump_record(sm_uc_ptr_t rp, block_id blk, sm_uc_ptr_t bp, sm_uc_ptr_t b_top); + cache_rec_ptr_t cr; + enum db_ver ondsk_blkver; + + error_def(ERR_DSEBLKRDFAIL); + error_def(ERR_CTRLC); + error_def(ERR_BITMAPSBAD); + + head = cli_present("HEADER"); + if (CLI_PRESENT == cli_present("BLOCK")) + { + if (!cli_get_hex("BLOCK", (uint4 *)&blk)) + return FALSE; + if (blk < 0 || blk >= cs_addrs->ti->total_blks) + { + util_out_print("Error: invalid block number.", TRUE); + return FALSE; + } + patch_curr_blk = blk; + } else + blk = patch_curr_blk; + if (CLI_PRESENT == cli_present("COUNT")) + { + if (!cli_get_hex("COUNT", (uint4 *)&count)) + return FALSE; + if (count < 1) + return FALSE; + } else + count = 1; + + util_out_print(0, TRUE); + bplmap = cs_addrs->hdr->bplmap; + is_mm = (dba_mm == cs_addrs->hdr->acc_meth); + mapsize = BM_SIZE(bplmap); + patch_rec_counter = 1; + was_crit = cs_addrs->now_crit; + nocrit_present = (CLI_NEGATED == cli_present("CRIT")); + DSE_GRAB_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + for ( ; ; ) + { + if (blk / bplmap * bplmap != blk) + { + if (!(bp = t_qread(blk, &dummy_int, &cr))) + { + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + } + if (((blk_hdr_ptr_t) bp)->levl && patch_is_fdmp) + { + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + util_out_print("Error: cannot perform GLO/ZWR dump on index block.", TRUE); + return FALSE; + } + if (((blk_hdr_ptr_t) bp)->bsiz > cs_addrs->hdr->blk_size) + b_top = bp + cs_addrs->hdr->blk_size; + else if (((blk_hdr_ptr_t) bp)->bsiz < SIZEOF(blk_hdr)) + b_top = bp + SIZEOF(blk_hdr); + else + b_top = bp + ((blk_hdr_ptr_t) bp)->bsiz; + if (CLI_NEGATED != head && !patch_is_fdmp) + { memcpy(util_buff, "Block ", 6); + util_len = 6; + util_len += i2hex_nofill(blk, &util_buff[util_len], 8); + memcpy(&util_buff[util_len], " Size ", 8); + util_len += 8; + util_len += i2hex_nofill(((blk_hdr_ptr_t)bp)->bsiz, &util_buff[util_len], 8); + memcpy(&util_buff[util_len], " Level !UL TN ", 18); + util_len += 18; + util_len += i2hexl_nofill(((blk_hdr_ptr_t)bp)->tn, &util_buff[util_len], 16); + memcpy(&util_buff[util_len], " ", 1); + util_len++; + ondsk_blkver = (!is_mm ? cr->ondsk_blkver : GDSV5); + len = STRLEN(gtm_dbversion_table[ondsk_blkver]); + memcpy(&util_buff[util_len], gtm_dbversion_table[ondsk_blkver], len); + util_len += len; + memcpy(&util_buff[util_len], "!/", 2); + util_len += 2; + util_buff[util_len] = 0; + util_out_print((caddr_t)util_buff, TRUE, ((blk_hdr_ptr_t) bp)->levl ); + } + rp = bp + SIZEOF(blk_hdr); + if (CLI_PRESENT != head && (!patch_is_fdmp || ((blk_hdr_ptr_t) bp)->levl == 0)) + { + while (!util_interrupt && (rp = dump_record(rp, blk, bp, b_top))) + patch_rec_counter += 1; + } + if (util_interrupt) + { + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + rts_error(VARLSTCNT(1) ERR_CTRLC); + break; + } + if (CLI_NEGATED == head) + util_out_print(0, TRUE); + } else if (!patch_is_fdmp) + { + if (!(bp = t_qread(blk, &dummy_int, &cr))) + { + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + } + if (CLI_NEGATED != head) + { + + if (bplmap == 0) + { + memcpy(util_buff, "Block ", 6); + util_len = 6; + util_len += i2hex_nofill(blk, &util_buff[util_len], 8); + memcpy(&util_buff[util_len], " Size ", 8); + util_len += 8; + util_len += i2hex_nofill(mapsize, &util_buff[util_len], 4); + memcpy(&util_buff[util_len], " Master Status: Cannot Determine (bplmap == 0)!/", 50); + util_len += 50; + util_buff[util_len] = 0; + util_out_print((caddr_t)util_buff, TRUE ); + } else + { + mb = cs_addrs->bmm + blk / (8 * bplmap); + lmap_num = blk / bplmap; + mask = 1 << ( lmap_num - lmap_num / 8 * 8); + free = mask & *mb; + memcpy(util_buff, "Block ", 6); + util_len = 6; + util_len += i2hex_nofill(blk, &util_buff[util_len], 8); + memcpy(&util_buff[util_len], " Size ", 7); + util_len += 7; + util_len += i2hex_nofill(((blk_hdr_ptr_t)bp)->bsiz, &util_buff[util_len], 8); + memcpy(&util_buff[util_len], " Level !SB TN ", 16); + util_len += 16; + util_len += i2hexl_nofill(((blk_hdr_ptr_t)bp)->tn, &util_buff[util_len], 16); + memcpy(&util_buff[util_len], " ", 1); + util_len++; + ondsk_blkver = (!is_mm ? cr->ondsk_blkver : GDSV5); + len = STRLEN(gtm_dbversion_table[ondsk_blkver]); + memcpy(&util_buff[util_len], gtm_dbversion_table[ondsk_blkver], len); + util_len += len; + util_buff[util_len] = 0; + util_out_print((caddr_t)util_buff, FALSE, ((blk_hdr_ptr_t) bp)->levl ); + util_len = 0; + memcpy(&util_buff[util_len], " Master Status: !AD!/",23); + util_len = 23; + util_buff[util_len] = 0; + util_out_print((caddr_t)util_buff, TRUE, free ? 10 : 4, free ? "Free Space" : "Full"); + } + } + if (CLI_PRESENT != head) + { + util_out_print(" !_Low order High order", TRUE); + + lmap_num = 0; + while (lmap_num < bplmap) + { memcpy(util_buff, "Block ", 6); + util_len = 6; + i2hex_blkfill(blk + lmap_num, &util_buff[util_len], 8); + util_len += 8; + memcpy(&util_buff[util_len], ":!_| ", 6); + util_len += 6; + util_buff[util_len] = 0; + util_out_print((caddr_t)util_buff, FALSE); + for (iter1 = 0; iter1 < 4; iter1++) + { + for (iter2 = 0; iter2 < 8; iter2++) + { + mask = dse_lm_blk_free(lmap_num * BML_BITS_PER_BLK, bp + SIZEOF(blk_hdr)); + if (!mask) + util_out_print("!AD", FALSE, 1, BUSY_CHAR); + else if (BLK_FREE == mask) + util_out_print("!AD", FALSE, 1, FREE_CHAR); + else if (BLK_RECYCLED == mask) + util_out_print("!AD", FALSE, 1, REUSABLE_CHAR); + else { + invalid_bitmap = TRUE; + util_out_print("!AD", FALSE, 1, CORRUPT_CHAR); + } + if (++lmap_num >= bplmap) + break; + } + util_out_print(" ", FALSE); + if (lmap_num >= bplmap) + break; + } + util_out_print("|", TRUE); + if (util_interrupt) + { + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + rts_error(VARLSTCNT(1) ERR_CTRLC); + } + } + util_out_print("!/'!AD' == BUSY '!AD' == FREE '!AD' == REUSABLE '!AD' == CORRUPT!/", + TRUE,1, BUSY_CHAR, 1, FREE_CHAR, 1, REUSABLE_CHAR, 1, CORRUPT_CHAR); + if (invalid_bitmap) + { + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + rts_error(VARLSTCNT(1) ERR_BITMAPSBAD); + } + } + } + count--; + if (count <= 0 || util_interrupt) + break; + blk++; + if (blk >= cs_addrs->ti->total_blks) + blk = 0; + } + patch_curr_blk = blk; + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + return TRUE; +} diff --git a/sr_port/dse_cache.c b/sr_port/dse_cache.c new file mode 100644 index 0000000..52fec7c --- /dev/null +++ b/sr_port/dse_cache.c @@ -0,0 +1,233 @@ +/**************************************************************** + * * + * Copyright 2003, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "jnl.h" +#include "cli.h" +#include "gdsblk.h" +#include "util.h" +#include "dse.h" +#include "stringpool.h" /* for GET_CURR_TIME_IN_DOLLARH_AND_ZDATE macro */ +#include "op.h" /* for op_fnzdate and op_horolog prototype */ +#include "wcs_recover.h" /* for wcs_recover prototype */ +#include "wcs_phase2_commit_wait.h" + +GBLREF gd_region *gv_cur_region; +GBLREF gd_addr *original_header; + +#define DB_ABS2REL(X) ((sm_uc_ptr_t)(X) - (sm_uc_ptr_t)csa->nl) +#define MAX_UTIL_LEN 40 +#define CLEAN_VERIFY "verification is clean" +#define UNCLEAN_VERIFY "verification is NOT clean (see operator log for details)" +#define RECOVER_DONE "recovery complete (see operator log for details)" +#define RECOVER_NOT_APPLIC "recovery not applicable with MM access method" + +void dse_cache(void) +{ + boolean_t all_present, change_present, recover_present, show_present, verify_present, was_crit, is_clean; + boolean_t nocrit_present, offset_present, size_present, value_present; + gd_region *reg, *r_top; + sgmnt_addrs *csa; + mval dollarh_mval, zdate_mval; + int4 size; + uint4 offset, value, old_value; + char dollarh_buffer[MAXNUMLEN], zdate_buffer[SIZEOF(DSE_DMP_TIME_FMT)]; + char temp_str[256], temp_str1[256]; + sm_uc_ptr_t chng_ptr; + cache_rec_ptr_t cr_que_lo; + mmblk_rec_ptr_t mr_que_lo; + boolean_t is_mm; + + error_def(ERR_SIZENOTVALID4); + + all_present = (CLI_PRESENT == cli_present("ALL")); + + recover_present = (CLI_PRESENT == cli_present("RECOVER")); + verify_present = (CLI_PRESENT == cli_present("VERIFY")); + change_present = (CLI_PRESENT == cli_present("CHANGE")); + show_present = (CLI_PRESENT == cli_present("SHOW")); + + offset_present = (CLI_PRESENT == cli_present("OFFSET")); + size_present = (CLI_PRESENT == cli_present("SIZE")); + value_present = (CLI_PRESENT == cli_present("VALUE")); + + nocrit_present = (CLI_NEGATED == cli_present("CRIT")); + assert(!nocrit_present || show_present); /* NOCRIT is only applicable for SHOW */ + if (offset_present && !cli_get_hex("OFFSET", &offset)) + return; + if (size_present) + { + if (!cli_get_int("SIZE", &size)) + return; + if (!((SIZEOF(char) == size) || (SIZEOF(short) == size) || (SIZEOF(int4) == size))) + rts_error(VARLSTCNT(1) ERR_SIZENOTVALID4); + } + if (value_present && !cli_get_hex("VALUE", &value)) + return; + assert(change_present || recover_present || show_present || verify_present); + for (reg = original_header->regions, r_top = reg + original_header->n_regions; reg < r_top; reg++) + { + if (!all_present && (reg != gv_cur_region)) + continue; + if (!reg->open || reg->was_open) + continue; + is_mm = (dba_mm == reg->dyn.addr->acc_meth); + csa = &FILE_INFO(reg)->s_addrs; + assert(is_mm || (csa->db_addrs[0] == (sm_uc_ptr_t)csa->nl)); + was_crit = csa->now_crit; + if (!was_crit && !nocrit_present) + grab_crit(reg); + if (verify_present || recover_present) + { + GET_CURR_TIME_IN_DOLLARH_AND_ZDATE(dollarh_mval, dollarh_buffer, zdate_mval, zdate_buffer); + if (verify_present) + { /* Before invoking wcs_verify, wait for any pending phase2 commits to finish. + * Need to wait as otherwise ongoing phase2 commits can result in cache verification + * returning FALSE (e.g. due to DBCRERR message indicating that cr->in_tend is non-zero). + */ + if (csa->nl->wcs_phase2_commit_pidcnt && !is_mm) + { /* No need to check return value since even if it fails, we want to do cache verification */ + wcs_phase2_commit_wait(csa, NULL); + } + is_clean = wcs_verify(reg, TRUE, FALSE); /* expect_damage is TRUE, caller_is_wcs_recover is FALSE */ + } else + { + if (UNIX_ONLY(TRUE)VMS_ONLY(!is_mm)) + { + SET_TRACEABLE_VAR(csa->hdr->wc_blocked, TRUE); + /* No need to invoke function "wcs_phase2_commit_wait" as "wcs_recover" does that anyways */ + wcs_recover(reg); + assert(FALSE == csa->hdr->wc_blocked); /* wcs_recover() should have cleared this */ + } + } + assert(20 == STR_LIT_LEN(DSE_DMP_TIME_FMT)); /* if they are not the same, the !20AD below should change */ + util_out_print("Time !20AD : Region !12AD : Cache !AZ", TRUE, zdate_mval.str.len, zdate_mval.str.addr, + REG_LEN_STR(reg), verify_present ? (is_clean ? CLEAN_VERIFY : UNCLEAN_VERIFY) + : UNIX_ONLY(RECOVER_DONE) + VMS_ONLY(is_mm ? RECOVER_NOT_APPLIC : RECOVER_DONE)); + } else if (offset_present) + { + if ((csa->nl->sec_size VMS_ONLY(* OS_PAGELET_SIZE)) < (offset + size)) + util_out_print("Region !12AD : Error: offset + size is greater than region's max_offset = 0x!XL", + TRUE, REG_LEN_STR(reg), (csa->nl->sec_size VMS_ONLY(* OS_PAGELET_SIZE))); + else + { + chng_ptr = (sm_uc_ptr_t)csa->nl + offset; + if (SIZEOF(char) == size) + { + SPRINTF(temp_str, "!UB [0x!XB]"); + old_value = *(sm_uc_ptr_t)chng_ptr; + } else if (SIZEOF(short) == size) + { + SPRINTF(temp_str, "!UW [0x!XW]"); + old_value = *(sm_ushort_ptr_t)chng_ptr; + } else if (SIZEOF(int4) == size) + { + SPRINTF(temp_str, "!UL [0x!XL]"); + old_value = *(sm_uint_ptr_t)chng_ptr; + } + if (value_present) + { + if (SIZEOF(char) == size) + *(sm_uc_ptr_t)chng_ptr = value; + else if (SIZEOF(short) == size) + *(sm_ushort_ptr_t)chng_ptr = value; + else if (SIZEOF(int4) == size) + *(sm_uint_ptr_t)chng_ptr = value; + } else + value = old_value; + if (show_present) + { + SPRINTF(temp_str1, "Region !12AD : Location !UL [0x!XL] : Value = %s :" + " Size = !UB [0x!XB]", temp_str); + util_out_print(temp_str1, TRUE, REG_LEN_STR(reg), offset, offset, value, value, size, size); + } else + { + SPRINTF(temp_str1, "Region !12AD : Location !UL [0x!XL] : Old Value = %s : " + "New Value = %s : Size = !UB [0x!XB]", temp_str, temp_str); + util_out_print(temp_str1, TRUE, REG_LEN_STR(reg), offset, offset, + old_value, old_value, value, value, size, size); + } + } + } else + { + assert(show_present); /* this should be a DSE CACHE -SHOW command with no other qualifiers */ + util_out_print("Region !AD : Shared_memory = 0x!XJ", + TRUE, REG_LEN_STR(reg), csa->nl); + util_out_print("Region !AD : node_local = 0x!XJ", + TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->nl)); + util_out_print("Region !AD : critical = 0x!XJ", + TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->critical)); + if (JNL_ALLOWED(csa)) + { + util_out_print("Region !AD : jnl_buffer_struct = 0x!XJ", + TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->jnl->jnl_buff)); + util_out_print("Region !AD : jnl_buffer_data = 0x!XJ", TRUE, REG_LEN_STR(reg), + DB_ABS2REL(&csa->jnl->jnl_buff->buff[csa->jnl->jnl_buff->buff_off])); + } + util_out_print("Region !AD : shmpool_buffer = 0x!XJ", + TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->shmpool_buffer)); + util_out_print("Region !AD : lock_space = 0x!XJ", + TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->lock_addrs[0])); + if (!is_mm) + { + util_out_print("Region !AD : cache_queues_state = 0x!XJ", + TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->acc_meth.bg.cache_state)); + cr_que_lo = &csa->acc_meth.bg.cache_state->cache_array[0]; + util_out_print("Region !AD : cache_que_header = 0x!XJ : Numelems = 0x!XL : Elemsize = 0x!XL", + TRUE, REG_LEN_STR(reg), DB_ABS2REL(cr_que_lo), csa->hdr->bt_buckets, SIZEOF(cache_rec)); + util_out_print("Region !AD : cache_record = 0x!XJ : Numelems = 0x!XL : Elemsize = 0x!XL", + TRUE, REG_LEN_STR(reg), DB_ABS2REL(cr_que_lo + csa->hdr->bt_buckets), csa->hdr->n_bts, + SIZEOF(cache_rec)); + util_out_print("Region !AD : global_buffer = 0x!XJ : Numelems = 0x!XL : Elemsize = 0x!XL", + TRUE, REG_LEN_STR(reg), + ROUND_UP2(DB_ABS2REL(cr_que_lo + csa->hdr->bt_buckets + csa->hdr->n_bts), OS_PAGE_SIZE), + csa->hdr->n_bts, csa->hdr->blk_size); + util_out_print("Region !AD : db_file_header = 0x!XJ", TRUE, + REG_LEN_STR(reg), DB_ABS2REL(csa->hdr)); + util_out_print("Region !AD : bt_que_header = 0x!XJ : Numelems = 0x!XL : Elemsize = 0x!XL", + TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->bt_header), csa->hdr->bt_buckets, SIZEOF(bt_rec)); + util_out_print("Region !AD : th_base = 0x!XJ", + TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->th_base)); + util_out_print("Region !AD : bt_record = 0x!XJ : Numelems = 0x!XL : Elemsize = 0x!XL", + TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->bt_base), csa->hdr->n_bts, SIZEOF(bt_rec)); + util_out_print("Region !AD : shared_memory_size = 0x!XL", + TRUE, REG_LEN_STR(reg), csa->nl->sec_size VMS_ONLY(* OS_PAGELET_SIZE)); + } else + { + util_out_print("Region !AD : mmblk_state = 0x!XJ", + TRUE, REG_LEN_STR(reg), DB_ABS2REL(csa->acc_meth.mm.mmblk_state)); + mr_que_lo = &csa->acc_meth.mm.mmblk_state->mmblk_array[0]; + util_out_print("Region !AD : mmblk_que_header = 0x!XJ : Numelems = 0x!XL : Elemsize = 0x!XL", + TRUE, REG_LEN_STR(reg), DB_ABS2REL(mr_que_lo), csa->hdr->bt_buckets, SIZEOF(mmblk_rec)); + util_out_print("Region !AD : mm_cache_record = 0x!XJ : Numelems = 0x!XL : Elemsize = 0x!XL", + TRUE, REG_LEN_STR(reg), DB_ABS2REL(mr_que_lo + csa->hdr->bt_buckets), csa->hdr->n_bts, + SIZEOF(mmblk_rec)); + util_out_print("Region !AD : shared_memory_size = 0x!XL", + TRUE, REG_LEN_STR(reg), csa->nl->sec_size VMS_ONLY(* OS_PAGELET_SIZE)); + util_out_print("Region !AD : db_file_header = 0x!XJ", TRUE, REG_LEN_STR(reg), csa->hdr); + } + } + if (!was_crit && !nocrit_present) + rel_crit(reg); + } + return; +} diff --git a/sr_port/dse_chng_bhead.c b/sr_port/dse_chng_bhead.c new file mode 100644 index 0000000..e01f616 --- /dev/null +++ b/sr_port/dse_chng_bhead.c @@ -0,0 +1,255 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_time.h" + +#include "gdsroot.h" +#include "gdskill.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "gdsbml.h" +#include "min_max.h" /* needed for gdsblkops.h */ +#include "gdsblkops.h" +#include "gdscc.h" +#include "filestruct.h" +#include "jnl.h" +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" +#include "cli.h" +#include "send_msg.h" +#include "dse.h" + +/* Include prototypes */ +#include "t_qread.h" +#include "t_write.h" +#include "t_end.h" +#include "t_begin_crit.h" +#include "process_deferred_stale.h" +#include "util.h" +#include "t_abort.h" +#include "gvcst_blk_build.h" /* for the BUILD_AIMG_IF_JNL_ENABLED macro */ + +GBLREF char *update_array, *update_array_ptr; +GBLREF uint4 update_array_size; +GBLREF srch_hist dummy_hist; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF block_id patch_curr_blk; +GBLREF gd_region *gv_cur_region; +GBLREF gd_addr *gd_header; +GBLREF cache_rec *cr_array[((MAX_BT_DEPTH * 2) - 1) * 2]; /* Maximum number of blocks that can be in transaction */ +GBLREF boolean_t unhandled_stale_timer_pop; +GBLREF unsigned char *non_tp_jfb_buff_ptr; +GBLREF cw_set_element cw_set[]; + +void dse_chng_bhead(void) +{ + block_id blk; + int4 x; + trans_num tn; + cache_rec_ptr_t cr; + blk_hdr new_hdr; + blk_segment *bs1, *bs_ptr; + int4 blk_seg_cnt, blk_size; /* needed for BLK_INIT,BLK_SEG and BLK_FINI macros */ + boolean_t ismap; + boolean_t chng_blk; + boolean_t was_crit; + boolean_t was_hold_onto_crit; + uint4 mapsize; + srch_blk_status blkhist; + sgmnt_addrs *csa; + sgmnt_data_ptr_t csd; +# ifdef GTM_CRYPT + int req_enc_blk_size; + int crypt_status; + blk_hdr_ptr_t bp, save_bp, save_old_block; +# endif + + error_def(ERR_DSEBLKRDFAIL); + error_def(ERR_DSEFAIL); + error_def(ERR_DBRDONLY); + + if (gv_cur_region->read_only) + rts_error(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); + CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ + chng_blk = FALSE; + csa = cs_addrs; + if (cli_present("BLOCK") == CLI_PRESENT) + { + if (!cli_get_hex("BLOCK", (uint4 *)&blk)) + return; + if (blk < 0 || blk > csa->ti->total_blks) + { + util_out_print("Error: invalid block number.", TRUE); + return; + } + patch_curr_blk = blk; + } + csd = csa->hdr; + assert(csd == cs_data); + blk_size = csd->blk_size; + ismap = (patch_curr_blk / csd->bplmap * csd->bplmap == patch_curr_blk); + mapsize = BM_SIZE(csd->bplmap); + + t_begin_crit(ERR_DSEFAIL); + blkhist.blk_num = patch_curr_blk; + if (!(blkhist.buffaddr = t_qread(blkhist.blk_num, &blkhist.cycle, &blkhist.cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + new_hdr = *(blk_hdr_ptr_t)blkhist.buffaddr; + + if (cli_present("LEVEL") == CLI_PRESENT) + { + if (!cli_get_hex("LEVEL", (uint4 *)&x)) + { + t_abort(gv_cur_region, csa); + return; + } + if (ismap && (unsigned char)x != LCL_MAP_LEVL) + { + util_out_print("Error: invalid level for a bit map block.", TRUE); + t_abort(gv_cur_region, csa); + return; + } + if (!ismap && (x < 0 || x > MAX_BT_DEPTH + 1)) + { + util_out_print("Error: invalid level.", TRUE); + t_abort(gv_cur_region, csa); + return; + } + new_hdr.levl = (unsigned char)x; + + chng_blk = TRUE; + if (new_hdr.bsiz < SIZEOF(blk_hdr)) + new_hdr.bsiz = SIZEOF(blk_hdr); + if (new_hdr.bsiz > blk_size) + new_hdr.bsiz = blk_size; + } + if (cli_present("BSIZ") == CLI_PRESENT) + { + if (!cli_get_hex("BSIZ", (uint4 *)&x)) + { + t_abort(gv_cur_region, csa); + return; + } + if (ismap && x != mapsize) + { + util_out_print("Error: invalid bsiz.", TRUE); + t_abort(gv_cur_region, csa); + return; + } else if (x < SIZEOF(blk_hdr) || x > blk_size) + { + util_out_print("Error: invalid bsiz.", TRUE); + t_abort(gv_cur_region, csa); + return; + } + chng_blk = TRUE; + new_hdr.bsiz = x; + } + if (!chng_blk) + t_abort(gv_cur_region, csa); + else + { + BLK_INIT(bs_ptr, bs1); + BLK_SEG(bs_ptr, blkhist.buffaddr + SIZEOF(new_hdr), new_hdr.bsiz - SIZEOF(new_hdr)); + if (!BLK_FINI(bs_ptr, bs1)) + { + util_out_print("Error: bad block build.", TRUE); + t_abort(gv_cur_region, csa); + return; + } + t_write(&blkhist, (unsigned char *)bs1, 0, 0, new_hdr.levl, TRUE, FALSE, GDS_WRITE_KILLTN); + BUILD_AIMG_IF_JNL_ENABLED(csd, non_tp_jfb_buff_ptr, csa->ti->curr_tn); + t_end(&dummy_hist, NULL, TN_NOT_SPECIFIED); + } + if (cli_present("TN") == CLI_PRESENT) + { + if (!cli_get_hex64("TN", &tn)) + return; + was_crit = csa->now_crit; + t_begin_crit(ERR_DSEFAIL); + CHECK_TN(csa, csd, csd->trans_hist.curr_tn); /* can issue rts_error TNTOOLARGE */ + assert(csa->ti->early_tn == csa->ti->curr_tn); + if (NULL == (blkhist.buffaddr = t_qread(blkhist.blk_num, &blkhist.cycle, &blkhist.cr))) + { + util_out_print("Error: Unable to read buffer.", TRUE); + t_abort(gv_cur_region, csa); + return; + } + if (new_hdr.bsiz < SIZEOF(blk_hdr)) + new_hdr.bsiz = SIZEOF(blk_hdr); + if (new_hdr.bsiz > blk_size) + new_hdr.bsiz = blk_size; + BLK_INIT(bs_ptr, bs1); + BLK_SEG(bs_ptr, blkhist.buffaddr + SIZEOF(new_hdr), new_hdr.bsiz - SIZEOF(new_hdr)); + BLK_FINI(bs_ptr, bs1); + t_write(&blkhist, (unsigned char *)bs1, 0, 0, + ((blk_hdr_ptr_t)blkhist.buffaddr)->levl, TRUE, FALSE, GDS_WRITE_KILLTN); + /* Pass the desired tn as argument to bg_update/mm_update below */ + BUILD_AIMG_IF_JNL_ENABLED(csd, non_tp_jfb_buff_ptr, tn); + was_hold_onto_crit = csa->hold_onto_crit; + csa->hold_onto_crit = TRUE; + t_end(&dummy_hist, NULL, tn); +# ifdef GTM_CRYPT + if (csd->is_encrypted && (tn < csa->ti->curr_tn)) + { /* BG and db encryption is enabled and the DSE update caused the block-header to potentially have a tn + * that is LESS than what it had before. At this point, the global buffer (corresponding to blkhist.blk_num) + * reflects the contents of the block AFTER the dse update (bg_update would have touched this) whereas + * the corresponding encryption global buffer reflects the contents of the block BEFORE the update. + * Normally wcs_wtstart takes care of propagating the tn update from the regular global buffer to the + * corresponding encryption buffer. But if before it gets a chance, let us say a process goes to t_end + * as part of a subsequent transaction and updates this same block. Since the blk-hdr-tn potentially + * decreased, it is possible that the PBLK writing check (comparing blk-hdr-tn with the epoch_tn) decides + * to write a PBLK for this block (even though a PBLK was already written for this block as part of a + * previous DSE CHANGE -BL -TN in the same epoch). In this case, since the db is encrypted, the logic + * will assume there were no updates to this block since the last time wcs_wtstart updated the encryption + * buffer and therefore use that to write the pblk, which is incorrect since it does not yet contain the + * tn update. The consequence of this is would be writing an older before-image PBLK) record to the + * journal file. To prevent this situation, we update the encryption buffer here (before releasing crit) + * using logic like that in wcs_wtstart to ensure it is in sync with the regular global buffer. + * Note: + * Although we use cw_set[0] to access the global buffer corresponding to the block number being updated, + * cw_set_depth at this point is 0 because t_end resets it. This is considered safe since cw_set is a + * static array (as opposed to malloc'ed memory) and hence is always available and valid until it gets + * overwritten by subsequent updates. + */ + bp = (blk_hdr_ptr_t)GDS_ANY_REL2ABS(csa, cw_set[0].cr->buffaddr); + DBG_ENSURE_PTR_IS_VALID_GLOBUFF(csa, csd, (sm_uc_ptr_t)bp); + save_bp = (blk_hdr_ptr_t)GDS_ANY_ENCRYPTGLOBUF(bp, csa); + DBG_ENSURE_PTR_IS_VALID_ENCTWINGLOBUFF(csa, csd, (sm_uc_ptr_t)save_bp); + assert((bp->bsiz <= csd->blk_size) && (bp->bsiz >= SIZEOF(*bp))); + req_enc_blk_size = MIN(csd->blk_size, bp->bsiz) - SIZEOF(*bp); + if (BLK_NEEDS_ENCRYPTION(bp->levl, req_enc_blk_size)) + { + ASSERT_ENCRYPTION_INITIALIZED; + memcpy(save_bp, bp, SIZEOF(blk_hdr)); + GTMCRYPT_ENCODE_FAST(csa->encr_key_handle, (char *)(bp + 1), req_enc_blk_size, + (char *)(save_bp + 1), crypt_status); + if (0 != crypt_status) + GC_GTM_PUTMSG(crypt_status, gv_cur_region->dyn.addr->fname); + } else + memcpy(save_bp, bp, bp->bsiz); + } +# endif + if (!was_hold_onto_crit) + csa->hold_onto_crit = FALSE; + if (!was_crit) + rel_crit(gv_cur_region); + if (unhandled_stale_timer_pop) + process_deferred_stale(); + } + return; +} diff --git a/sr_port/dse_chng_fhead.c b/sr_port/dse_chng_fhead.c new file mode 100644 index 0000000..d26e68e --- /dev/null +++ b/sr_port/dse_chng_fhead.c @@ -0,0 +1,769 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/******************************************************************************* +* +* MODULE NAME: CHANGE_FHEAD +* +* CALLING SEQUENCE: void change_fhead() +* +* DESCRIPTION: This module changes values of certain fields +* of the file header. The only range-checking +* takes place on input, not in this routine, allowing +* the user maximum control. +* +* HISTORY: +* +*******************************************************************************/ + +#include "gtm_string.h" +#include "mdef.h" +#include "gtm_stdlib.h" +#include "gtm_unistd.h" +#include "gtm_stdio.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "cli.h" +#include "timersp.h" +#include "jnl.h" +#include "util.h" +#include "gtm_caseconv.h" +#include "gt_timer.h" +#include "timers.h" +#include "send_msg.h" +#include "dse.h" +#include "gtmmsg.h" +#ifdef GTM_CRYPT +#include "gtmcrypt.h" +#endif + +GBLREF VSIG_ATOMIC_T util_interrupt; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gd_region *gv_cur_region; +GBLREF uint4 process_id; +GBLREF uint4 image_count; +LITREF char *gtm_dbversion_table[]; + +error_def(ERR_FREEZE); +error_def(ERR_BLKSIZ512); +error_def(ERR_DBRDONLY); +error_def(ERR_SIZENOTVALID8); +error_def(ERR_FREEZECTRL); + +#define CLNUP_CRIT \ +{ \ + if (!was_crit) \ + { \ + if (nocrit_present) \ + cs_addrs->now_crit = FALSE; \ + else \ + rel_crit(gv_cur_region); \ + } \ +} + +void dse_chng_fhead(void) +{ + int4 x, index_x, save_x; + unsigned short buf_len; + bool was_crit; + boolean_t override = FALSE; + int4 nocrit_present; + int4 location_present, value_present, size_present, size; + uint4 location; + boolean_t max_tn_present, max_tn_warn_present, curr_tn_present, change_tn; + gtm_uint64_t value, old_value; + seq_num seq_no; + trans_num tn, prev_tn, max_tn_old, max_tn_warn_old, curr_tn_old, max_tn_new, max_tn_warn_new, curr_tn_new; + char temp_str[256], temp_str1[256], buf[MAX_LINE]; + int gethostname_res; + sm_uc_ptr_t chng_ptr; + const char *freeze_msg[] = { "UNFROZEN", "FROZEN" } ; + GTMCRYPT_ONLY( + char hash_buff[GTMCRYPT_HASH_LEN]; + int crypt_status; + ) + + if (gv_cur_region->read_only) + rts_error(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); + + memset(temp_str, 0, 256); + memset(temp_str1, 0, 256); + memset(buf, 0, MAX_LINE); + was_crit = cs_addrs->now_crit; + nocrit_present = (CLI_NEGATED == cli_present("CRIT")); + if (!was_crit) + { + if (nocrit_present) + cs_addrs->now_crit = TRUE; + else + grab_crit(gv_cur_region); + } + if (CLI_PRESENT == cli_present("OVERRIDE")) + override = TRUE; +#ifdef VMS + if (cs_addrs->hdr->freeze && (cs_addrs->hdr->freeze != process_id || + cs_addrs->hdr->image_count != image_count) && !override) +#endif +#ifdef UNIX + if (cs_addrs->hdr->freeze && (cs_addrs->hdr->image_count != process_id) + && !override) +#endif + { + CLNUP_CRIT; + util_out_print("Region: !AD is frozen by another user, not releasing freeze.", + TRUE, REG_LEN_STR(gv_cur_region)); + rts_error(VARLSTCNT(4) ERR_FREEZE, 2, REG_LEN_STR(gv_cur_region)); + return; + + } + prev_tn = cs_addrs->ti->curr_tn; + location_present = FALSE; + if (CLI_PRESENT == cli_present("LOCATION")) + { + location_present = TRUE; + if (!cli_get_hex("LOCATION", &location)) + { + CLNUP_CRIT; + return; + } + } + if (CLI_PRESENT == cli_present("HEXLOCATION")) + { + location_present = TRUE; + if (!cli_get_hex("HEXLOCATION", &location)) + { + CLNUP_CRIT; + return; + } + } + if (CLI_PRESENT == cli_present("DECLOCATION")) + { + location_present = TRUE; + if (!cli_get_int("DECLOCATION", (int4 *)&location)) + { + CLNUP_CRIT; + return; + } + } + size_present = FALSE; + if (CLI_PRESENT == cli_present("SIZE")) + { + size_present = TRUE; + if (!cli_get_int("SIZE", &size)) + { + CLNUP_CRIT; + return; + } + } + value_present = FALSE; + if (CLI_PRESENT == cli_present("VALUE")) + { + value_present = TRUE; + if (!cli_get_hex64("VALUE", (gtm_uint64_t *)&value)) + { + CLNUP_CRIT; + return; + } + } + if (CLI_PRESENT == cli_present("HEXVALUE")) + { + value_present = TRUE; + if (!cli_get_hex64("HEXVALUE", &value)) + { + CLNUP_CRIT; + return; + } + } + if (CLI_PRESENT == cli_present("DECVALUE")) + { + value_present = TRUE; + if (!cli_get_uint64("DECVALUE", (gtm_uint64_t *)&value)) + { + CLNUP_CRIT; + return; + } + } + if (TRUE == location_present) + { + if (FALSE == size_present) + size = SIZEOF(int4); + if (!((SIZEOF(char) == size) || (SIZEOF(short) == size) || (SIZEOF(int4) == size) || + (SIZEOF(gtm_int64_t) == size))) + { + CLNUP_CRIT; + rts_error(VARLSTCNT(1) ERR_SIZENOTVALID8); + } + if ((0 > (int4)size) || ((uint4)SGMNT_HDR_LEN < (uint4)location) + || ((uint4)SGMNT_HDR_LEN < ((uint4)location + (uint4)size))) + util_out_print("Error: Cannot modify any location outside the file-header", TRUE); + else if (0 != location % size) + util_out_print("Error: Location !UL [0x!XL] should be a multiple of Size !UL", + TRUE, location, location, size, size); + else + { + chng_ptr = (sm_uc_ptr_t)cs_addrs->hdr + location; + if (SIZEOF(char) == size) + { + SPRINTF(temp_str, "!UB [0x!XB]"); + old_value = *(sm_uc_ptr_t)chng_ptr; + } + else if (SIZEOF(short) == size) + { + SPRINTF(temp_str, "!UW [0x!XW]"); + old_value = *(sm_ushort_ptr_t)chng_ptr; + } + else if (SIZEOF(int4) == size) + { + SPRINTF(temp_str, "!UL [0x!XL]"); + old_value = *(sm_uint_ptr_t)chng_ptr; + } + else if (SIZEOF(gtm_int64_t) == size) + { + SPRINTF(temp_str, "!@UQ [0x!@XQ]"); + old_value = *(qw_num_ptr_t)chng_ptr; + } + if (value_present) + { + if (SIZEOF(char) == size) + *(sm_uc_ptr_t)chng_ptr = (unsigned char) value; + else if (SIZEOF(short) == size) + *(sm_ushort_ptr_t)chng_ptr = (unsigned short) value; + else if (SIZEOF(int4) == size) + *(sm_uint_ptr_t)chng_ptr = (unsigned int) value; + else if (SIZEOF(gtm_int64_t) == size) + *(qw_num_ptr_t)chng_ptr = value; + } else + value = old_value; + SPRINTF(temp_str1, "Location !UL [0x!XL] : Old Value = %s : New Value = %s : Size = !UB [0x!XB]", + temp_str, temp_str); + if (SIZEOF(int4) >= size) + util_out_print(temp_str1, TRUE, location, location, (uint4)old_value, (uint4)old_value, + (uint4)value, (uint4)value, size, size); + else + util_out_print(temp_str1, TRUE, location, location, &old_value, &old_value, + &value, &value, size, size); + } + } + if ((CLI_PRESENT == cli_present("TOTAL_BLKS")) && (cli_get_hex("TOTAL_BLKS", (uint4 *)&x))) + cs_addrs->ti->total_blks = x; + if ((CLI_PRESENT == cli_present("BLOCKS_FREE")) && (cli_get_hex("BLOCKS_FREE", (uint4 *)&x))) + cs_addrs->ti->free_blocks = x; + if ((CLI_PRESENT == cli_present("BLK_SIZE")) && (cli_get_int("BLK_SIZE", &x))) + { + if (!(x % DISK_BLOCK_SIZE) && (0 != x)) + cs_addrs->hdr->blk_size = x; + else + { + cs_addrs->hdr->blk_size = ((x/DISK_BLOCK_SIZE) + 1) * DISK_BLOCK_SIZE; + CLNUP_CRIT; + rts_error(VARLSTCNT(4) ERR_BLKSIZ512, 2, x, cs_addrs->hdr->blk_size); + } + } + if ((CLI_PRESENT == cli_present("RECORD_MAX_SIZE")) && (cli_get_int("RECORD_MAX_SIZE", &x))) + { + cs_addrs->hdr->max_rec_size = x; + gv_cur_region->max_rec_size = x; + } + if ((CLI_PRESENT == cli_present("KEY_MAX_SIZE")) && (cli_get_int("KEY_MAX_SIZE", &x))) + { + cs_addrs->hdr->max_key_size = x; + gv_cur_region->max_key_size = x; + } + if ((CLI_PRESENT == cli_present("INHIBIT_KILLS")) && (cli_get_int("INHIBIT_KILLS", &x))) + { + cs_addrs->nl->inhibit_kills = x; + } + if (CLI_PRESENT == cli_present("INTERRUPTED_RECOV")) + { + x = cli_t_f_n("INTERRUPTED_RECOV"); + if (1 == x) + cs_addrs->hdr->recov_interrupted = TRUE; + else if (0 == x) + cs_addrs->hdr->recov_interrupted = FALSE; + } + if ((CLI_PRESENT == cli_present("REFERENCE_COUNT")) && (cli_get_int("REFERENCE_COUNT", &x))) + cs_addrs->nl->ref_cnt = x; + if ((CLI_PRESENT == cli_present("RESERVED_BYTES")) && (cli_get_int("RESERVED_BYTES", &x))) + cs_addrs->hdr->reserved_bytes = x; + if ((CLI_PRESENT == cli_present("DEF_COLLATION")) && (cli_get_int("DEF_COLLATION", &x))) + cs_addrs->hdr->def_coll = x; + if (CLI_PRESENT == cli_present("NULL_SUBSCRIPTS")) + { + x = cli_n_a_e("NULL_SUBSCRIPTS"); + if (-1 != x) + gv_cur_region->null_subs = cs_addrs->hdr->null_subs = (unsigned char)x; + } + if (CLI_PRESENT == cli_present("CERT_DB_VER")) + { + buf_len = SIZEOF(buf); + if (cli_get_str("CERT_DB_VER", buf, &buf_len)) + { + lower_to_upper((uchar_ptr_t)buf, (uchar_ptr_t)buf, buf_len); + for (index_x=0; index_x < GDSVLAST ; index_x++) + if (0 == STRCMP(buf, gtm_dbversion_table[index_x])) + { + cs_addrs->hdr->certified_for_upgrade_to = (enum db_ver)index_x; + break; + } + if (GDSVLAST <= index_x) + util_out_print("Invalid value for CERT_DB_VER qualifier", TRUE); + } + } + if (CLI_PRESENT == cli_present("DB_WRITE_FMT")) + { + buf_len = SIZEOF(buf); + if (cli_get_str("DB_WRITE_FMT", buf, &buf_len)) + { + lower_to_upper((uchar_ptr_t)buf, (uchar_ptr_t)buf, buf_len); + for (index_x=0; index_x < GDSVLAST ; index_x++) + if (0 == STRCMP(buf, gtm_dbversion_table[index_x])) + { + cs_addrs->hdr->desired_db_format = (enum db_ver)index_x; + cs_addrs->hdr->fully_upgraded = FALSE; + break; + } + if (GDSVLAST <= index_x) + util_out_print("Invalid value for DB_WRITE_FMT qualifier", TRUE); + } + } + /* ---------- Begin ------ CURRENT_TN/MAX_TN/WARN_MAX_TN processing -------- */ + max_tn_old = cs_addrs->hdr->max_tn; + if ((CLI_PRESENT == cli_present("MAX_TN")) && (cli_get_hex64("MAX_TN", &max_tn_new))) + max_tn_present = TRUE; + else + { + max_tn_present = FALSE; + max_tn_new = max_tn_old; + } + max_tn_warn_old = cs_addrs->hdr->max_tn_warn; + if ((CLI_PRESENT == cli_present("WARN_MAX_TN")) && (cli_get_hex64("WARN_MAX_TN", &max_tn_warn_new))) + max_tn_warn_present = TRUE; + else + { + max_tn_warn_present = FALSE; + max_tn_warn_new = max_tn_warn_old; + } + curr_tn_old = cs_addrs->ti->curr_tn; + if ((CLI_PRESENT == cli_present("CURRENT_TN")) && (cli_get_hex64("CURRENT_TN", &curr_tn_new))) + curr_tn_present = TRUE; + else + { + curr_tn_present = FALSE; + curr_tn_new = curr_tn_old; + } + change_tn = TRUE; + if (max_tn_present) + { + if (max_tn_new < max_tn_warn_new) + { + change_tn = FALSE; + util_out_print("MAX_TN value cannot be less than the current/specified value of WARN_MAX_TN", TRUE); + } + } + if (max_tn_warn_present) + { + if (!max_tn_present && (max_tn_warn_new > max_tn_new)) + { + change_tn = FALSE; + util_out_print("WARN_MAX_TN value cannot be greater than the current/specified value of MAX_TN", TRUE); + } + if (max_tn_warn_new < curr_tn_new) + { + change_tn = FALSE; + util_out_print("WARN_MAX_TN value cannot be less than the current/specified value of CURRENT_TN", TRUE); + } + } + if (curr_tn_present) + { + if (!max_tn_warn_present && (curr_tn_new > max_tn_warn_new)) + { + change_tn = FALSE; + util_out_print("CURRENT_TN value cannot be greater than the current/specified value of WARN_MAX_TN", TRUE); + } + } + if (change_tn) + { + if (max_tn_present) + cs_addrs->hdr->max_tn = max_tn_new; + if (max_tn_warn_present) + cs_addrs->hdr->max_tn_warn = max_tn_warn_new; + if (curr_tn_present) + cs_addrs->ti->curr_tn = cs_addrs->ti->early_tn = curr_tn_new; + assert(max_tn_new == cs_addrs->hdr->max_tn); + assert(max_tn_warn_new == cs_addrs->hdr->max_tn_warn); + assert(curr_tn_new == cs_addrs->ti->curr_tn); + assert(max_tn_new >= max_tn_warn_new); + assert(max_tn_warn_new >= curr_tn_new); + } else + { + /* if (max_tn_present) + util_out_print("MAX_TN value not changed", TRUE); + if (max_tn_warn_present) + util_out_print("WARN_MAX_TN value not changed", TRUE); + if (curr_tn_present) + util_out_print("CURRENT_TN value not changed", TRUE); + */ + assert(max_tn_old == cs_addrs->hdr->max_tn); + assert(max_tn_warn_old == cs_addrs->hdr->max_tn_warn); + assert(curr_tn_old == cs_addrs->ti->curr_tn); + } + /* ---------- End ------ CURRENT_TN/MAX_TN/WARN_MAX_TN processing -------- */ + if (CLI_PRESENT == cli_present("REG_SEQNO") && cli_get_hex64("REG_SEQNO", (gtm_uint64_t *)&seq_no)) + cs_addrs->hdr->reg_seqno = seq_no; + VMS_ONLY( + if (CLI_PRESENT == cli_present("RESYNC_SEQNO") && cli_get_hex64("RESYNC_SEQNO", (gtm_uint64_t *)&seq_no)) + cs_addrs->hdr->resync_seqno = seq_no; + if (CLI_PRESENT == cli_present("RESYNC_TN") && cli_get_hex64("RESYNC_TN", &tn)) + cs_addrs->hdr->resync_tn = tn; + ) + UNIX_ONLY( + if (CLI_PRESENT == cli_present("ZQGBLMOD_SEQNO") && cli_get_hex64("ZQGBLMOD_SEQNO", (gtm_uint64_t *)&seq_no)) + cs_addrs->hdr->zqgblmod_seqno = seq_no; + if (CLI_PRESENT == cli_present("ZQGBLMOD_TN") && cli_get_hex64("ZQGBLMOD_TN", &tn)) + cs_addrs->hdr->zqgblmod_tn = tn; + if (CLI_PRESENT == cli_present("DUALSITE_RESYNC_SEQNO") && cli_get_hex64("DUALSITE_RESYNC_SEQNO", &seq_no)) + cs_addrs->hdr->dualsite_resync_seqno = seq_no; + ) + if (CLI_PRESENT == cli_present("STDNULLCOLL")) + { + if ( -1 != (x = cli_t_f_n("STDNULLCOLL"))) + gv_cur_region->std_null_coll = cs_addrs->hdr->std_null_coll = x; + } + if (CLI_PRESENT == cli_present("CORRUPT_FILE")) + { + x = cli_t_f_n("CORRUPT_FILE"); + if (1 == x) + cs_addrs->hdr->file_corrupt = TRUE; + else if (0 == x) + cs_addrs->hdr->file_corrupt = FALSE; + } + if ((CLI_PRESENT == cli_present("TIMERS_PENDING")) && (cli_get_int("TIMERS_PENDING", &x))) + cs_addrs->nl->wcs_timers = x - 1; + change_fhead_timer("FLUSH_TIME", cs_addrs->hdr->flush_time, + (dba_bg == cs_addrs->hdr->acc_meth ? TIM_FLU_MOD_BG : TIM_FLU_MOD_MM), FALSE); + if ((CLI_PRESENT == cli_present("WRITES_PER_FLUSH")) && (cli_get_int("WRITES_PER_FLUSH", &x))) + cs_addrs->hdr->n_wrt_per_flu = x; + if ((CLI_PRESENT == cli_present("TRIGGER_FLUSH")) && (cli_get_int("TRIGGER_FLUSH", &x))) + cs_addrs->hdr->flush_trigger = x; + if ((CLI_PRESENT == cli_present("GOT2V5ONCE")) && (cli_get_int("GOT2V5ONCE", &x))) + cs_addrs->hdr->db_got_to_v5_once = (boolean_t)x; + change_fhead_timer("STALENESS_TIMER", cs_addrs->hdr->staleness, 5000, TRUE); + change_fhead_timer("TICK_INTERVAL", cs_addrs->hdr->ccp_tick_interval, 100, TRUE); + change_fhead_timer("QUANTUM_INTERVAL", cs_addrs->hdr->ccp_quantum_interval, 1000, FALSE); + change_fhead_timer("RESPONSE_INTERVAL", cs_addrs->hdr->ccp_response_interval, 60000, FALSE); + if ((CLI_PRESENT == cli_present("B_BYTESTREAM")) && (cli_get_hex64("B_BYTESTREAM", &tn))) + cs_addrs->hdr->last_inc_backup = tn; + if ((CLI_PRESENT == cli_present("B_COMPREHENSIVE")) && (cli_get_hex64("B_COMPREHENSIVE", &tn))) + cs_addrs->hdr->last_com_backup = tn; + if ((CLI_PRESENT == cli_present("B_DATABASE")) && (cli_get_hex64("B_DATABASE", &tn))) + cs_addrs->hdr->last_com_backup = tn; + if ((CLI_PRESENT == cli_present("B_INCREMENTAL")) && (cli_get_hex64("B_INCREMENTAL", &tn))) + cs_addrs->hdr->last_inc_backup = tn; + if ((CLI_PRESENT == cli_present("WAIT_DISK")) && (cli_get_int("WAIT_DISK", &x))) + cs_addrs->hdr->wait_disk_space = (x >= 0 ? x : 0); + if (((CLI_PRESENT == cli_present("HARD_SPIN_COUNT")) && cli_get_int("HARD_SPIN_COUNT", &x)) + UNIX_ONLY( || ((CLI_PRESENT == cli_present("MUTEX_HARD_SPIN_COUNT")) && cli_get_int("MUTEX_HARD_SPIN_COUNT", &x))) + ) /* Unix should be backward compatible, accept MUTEX_ prefix qualifiers as well */ + { + if (0 < x) + cs_addrs->hdr->mutex_spin_parms.mutex_hard_spin_count = x; + else + util_out_print("Error: HARD SPIN COUNT should be a non zero positive number", TRUE); + } + if (((CLI_PRESENT == cli_present("SLEEP_SPIN_COUNT")) && cli_get_int("SLEEP_SPIN_COUNT", &x)) + UNIX_ONLY( || ((CLI_PRESENT == cli_present("MUTEX_SLEEP_SPIN_COUNT")) && cli_get_int("MUTEX_SLEEP_SPIN_COUNT", &x))) + ) /* Unix should be backward compatible, accept MUTEX_ prefix qualifiers as well */ + { + if (0 < x) + cs_addrs->hdr->mutex_spin_parms.mutex_sleep_spin_count = x; + else + util_out_print("Error: SLEEP SPIN COUNT should be a non zero positive number", TRUE); + } + if (((CLI_PRESENT == cli_present("SPIN_SLEEP_TIME")) && cli_get_int("SPIN_SLEEP_TIME", &x)) + UNIX_ONLY( || ((CLI_PRESENT == cli_present("MUTEX_SPIN_SLEEP_TIME")) && cli_get_int("MUTEX_SPIN_SLEEP_TIME", &x))) + ) /* Unix should be backward compatible, accept MUTEX_ prefix qualifiers as well */ + { + if (x < 0) + util_out_print("Error: SPIN SLEEP TIME should be non negative", TRUE); + else + { + save_x = x; + for (index_x = 0; 0 != x; x >>= 1, index_x++); + if (index_x <= 1) + x = index_x; + else if ((1 << (index_x - 1)) == save_x) + x = save_x - 1; + else + x = (1 << index_x) - 1; + if (x > 999999) + util_out_print("Error: SPIN SLEEP TIME should be less than one million micro seconds", TRUE); + else + cs_addrs->hdr->mutex_spin_parms.mutex_spin_sleep_mask = x; + } + } + UNIX_ONLY( + if ((CLI_PRESENT == cli_present("COMMITWAIT_SPIN_COUNT")) && cli_get_int("COMMITWAIT_SPIN_COUNT", &x)) + { + if (0 <= x) + cs_addrs->hdr->wcs_phase2_commit_wait_spincnt = x; + else + util_out_print("Error: COMMITWAIT SPIN COUNT should be a positive number", TRUE); + } + ) + if ((CLI_PRESENT == cli_present("B_RECORD")) && (cli_get_hex64("B_RECORD", &tn))) + cs_addrs->hdr->last_rec_backup = tn; + if ((CLI_PRESENT == cli_present("BLKS_TO_UPGRADE")) && (cli_get_hex("BLKS_TO_UPGRADE", (uint4 *)&x))) + { + cs_addrs->hdr->blks_to_upgrd = x; + cs_addrs->hdr->fully_upgraded = FALSE; + } + if ((CLI_PRESENT == cli_present("MBM_SIZE")) && (cli_get_int("MBM_SIZE", &x))) + cs_addrs->hdr->master_map_len = x * DISK_BLOCK_SIZE; + if (cs_addrs->hdr->clustered) + { + if (cs_addrs->ti->curr_tn == prev_tn) + { + CHECK_TN(cs_addrs, cs_addrs->hdr, cs_addrs->ti->curr_tn);/* can issue rts_error TNTOOLARGE */ + cs_addrs->ti->early_tn++; + INCREMENT_CURR_TN(cs_addrs->hdr); + } + } + if ((CLI_PRESENT == cli_present("RC_SRV_COUNT")) && (cli_get_int("RC_SRV_COUNT", &x))) + cs_addrs->hdr->rc_srv_cnt = x; + if (CLI_PRESENT == cli_present("FREEZE")) + { + x = cli_t_f_n("FREEZE"); + if (1 == x) + { + while (REG_ALREADY_FROZEN == region_freeze(gv_cur_region, TRUE, override, FALSE)) + { + hiber_start(1000); + if (util_interrupt) + { + gtm_putmsg(VARLSTCNT(1) ERR_FREEZECTRL); + break; + } + } + } + else if (0 == x) + { + if (REG_ALREADY_FROZEN == region_freeze(gv_cur_region, FALSE, override, FALSE)) + { + util_out_print("Region: !AD is frozen by another user, not releasing freeze.", + TRUE, REG_LEN_STR(gv_cur_region)); + } + + } + if (x != !(cs_addrs->hdr->freeze)) + util_out_print("Region !AD is now !AD", TRUE, REG_LEN_STR(gv_cur_region), LEN_AND_STR(freeze_msg[x])); + cs_addrs->persistent_freeze = x; /* secshr_db_clnup() shouldn't clear the freeze up */ + } + if (CLI_PRESENT == cli_present("FULLY_UPGRADED") && cli_get_int("FULLY_UPGRADED", &x)) + { + cs_addrs->hdr->fully_upgraded = (boolean_t)x; + if (x) + cs_addrs->hdr->db_got_to_v5_once = TRUE; + } + if (CLI_PRESENT == cli_present("GVSTATSRESET")) + { + /* Clear statistics in NODE-LOCAL first */ +# define TAB_GVSTATS_REC(COUNTER,TEXT1,TEXT2) cs_addrs->nl->gvstats_rec.COUNTER = 0; +# include "tab_gvstats_rec.h" +# undef TAB_GVSTATS_REC + /* Do it in the file-header next */ + gvstats_rec_cnl2csd(cs_addrs); + } + if (CLI_PRESENT == cli_present("ONLINE_NBB")) + { + buf_len = SIZEOF(buf); + if (cli_get_str("ONLINE_NBB", buf, &buf_len)) + { + lower_to_upper((uchar_ptr_t)buf, (uchar_ptr_t)buf, buf_len); + if (0 == STRCMP(buf, "NOT_IN_PROGRESS")) + cs_addrs->nl->nbb = BACKUP_NOT_IN_PROGRESS; + else + { + if (('0' == buf[0]) && ('\0' == buf[1])) + x = 0; + else + { + x = ATOI(buf); + if (0 == x) + x = -2; + } + if (x < -1) + util_out_print("Invalid value for online_nbb qualifier", TRUE); + else + cs_addrs->nl->nbb = x; + } + } + } + if (CLI_PRESENT == cli_present("ABANDONED_KILLS")) + { + buf_len = SIZEOF(buf); + if (cli_get_str("ABANDONED_KILLS", buf, &buf_len)) + { + lower_to_upper((uchar_ptr_t)buf, (uchar_ptr_t)buf, buf_len); + if (0 == STRCMP(buf, "NONE")) + cs_addrs->hdr->abandoned_kills = 0; + else + { + if (('0' == buf[0]) && ('\0' == buf[1])) + x = 0; + else + { + x = ATOI(buf); + if (0 == x) + x = -1; + } + if (0 > x) + util_out_print("Invalid value for abandoned_kills qualifier", TRUE); + else + cs_addrs->hdr->abandoned_kills = x; + } + } + } + if (CLI_PRESENT == cli_present("KILL_IN_PROG")) + { + buf_len = SIZEOF(buf); + if (cli_get_str("KILL_IN_PROG", buf, &buf_len)) + { + lower_to_upper((uchar_ptr_t)buf, (uchar_ptr_t)buf, buf_len); + if (0 == STRCMP(buf, "NONE")) + cs_addrs->hdr->kill_in_prog = 0; + else + { + if (('0' == buf[0]) && ('\0' == buf[1])) + x = 0; + else + { + x = ATOI(buf); + if (0 == x) + x = -1; + } + if (0 > x) + util_out_print("Invalid value for kill_in_prog qualifier", TRUE); + else + cs_addrs->hdr->kill_in_prog = x; + } + } + } + if (CLI_PRESENT == cli_present("MACHINE_NAME")) + { + buf_len = SIZEOF(buf); + if (cli_get_str("MACHINE_NAME", buf, &buf_len)) + { + lower_to_upper((uchar_ptr_t)buf, (uchar_ptr_t)buf, buf_len); + if (0 == STRCMP(buf, "CURRENT")) + { + memset(cs_addrs->hdr->machine_name, 0, MAX_MCNAMELEN); + GETHOSTNAME(cs_addrs->hdr->machine_name, MAX_MCNAMELEN, gethostname_res); + } + else if (0 == STRCMP(buf, "CLEAR")) + memset(cs_addrs->hdr->machine_name, 0, MAX_MCNAMELEN); + else + util_out_print("Invalid value for the machine_name qualifier", TRUE); + } else + util_out_print("Error: cannot get value for !AD.", TRUE, LEN_AND_LIT("MACHINE_NAME")); + + } +# ifdef GTM_CRYPT + if (CLI_PRESENT == cli_present("ENCRYPTION_HASH")) + { + /* It could be possible that when the user is trying to change the encryption hash in the file header, + * more than one process is accessing the database. In such a case, changing the hash might affect the + * running processes. So warn the user about the potential consequence and return. */ + if (1 < cs_addrs->nl->ref_cnt) + { + util_out_print("Cannot reset encryption hash in file header while !XL other processes are \ + accessing the database.", + TRUE, + cs_addrs->nl->ref_cnt - 1); + return; + } + ASSERT_ENCRYPTION_INITIALIZED; /* assert that encryption is already initialized in db_init */ + + /* It is possible that the encryption hash in the database file header is corrupted and we are trying to + * reset it here. But for that to happen, GTMCRYPT_HASH_GEN should not worry about the error happened in + * db_init (unless the encryption library failed due to dlopen error as this would mean that the function + * pointers for the encryption APIs would not be initialized to a proper value and we would end up not + * reporting error). So, the below macro resets the error only if it was not caused due to a dlopen error. */ + GTMCRYPT_RESET_HASH_MISMATCH_ERR; + + /* Now generate the new hash to be placed in the database file header. */ + GTMCRYPT_HASH_GEN((char *)gv_cur_region->dyn.addr->fname, + gv_cur_region->dyn.addr->fname_len, + hash_buff, + crypt_status); + if (0 != crypt_status) + GC_GTM_PUTMSG(crypt_status, gv_cur_region->dyn.addr->fname); + memcpy(cs_addrs->hdr->encryption_hash, hash_buff, GTMCRYPT_HASH_LEN); + DEBUG_ONLY( + GTMCRYPT_HASH_CHK(cs_addrs->hdr->encryption_hash, crypt_status); + assert(0 == crypt_status); + ) + } +# endif + +#ifdef UNIX + if (CLI_PRESENT == cli_present("JNL_YIELD_LIMIT") && cli_get_int("JNL_YIELD_LIMIT", &x)) + { + if (0 > x) + util_out_print("YIELD_LIMIT cannot be NEGATIVE", TRUE); + else if (MAX_YIELD_LIMIT < x) + util_out_print("YIELD_LIMIT cannot be greater than !UL", TRUE, MAX_YIELD_LIMIT); + else + cs_addrs->hdr->yield_lmt = x; + } +#endif + if (CLI_PRESENT == cli_present(UNIX_ONLY("JNL_SYNCIO") VMS_ONLY("JNL_CACHE"))) + { + x = cli_t_f_n(UNIX_ONLY("JNL_SYNCIO") VMS_ONLY("JNL_CACHE")); + if (1 == x) + cs_addrs->hdr->jnl_sync_io = UNIX_ONLY(TRUE) VMS_ONLY(FALSE); + else if (0 == x) + cs_addrs->hdr->jnl_sync_io = UNIX_ONLY(FALSE) VMS_ONLY(TRUE); + } + if ((CLI_PRESENT == cli_present("AVG_BLKS_READ")) && (cli_get_int("AVG_BLKS_READ", &x))) + { + if (x <= 0) + util_out_print("Invalid value for AVG_BLKS_READ qualifier", TRUE); + else + cs_addrs->hdr->avg_blks_per_100gbl = x; + } + if ((CLI_PRESENT == cli_present("PRE_READ_TRIGGER_FACTOR")) && (cli_get_int("PRE_READ_TRIGGER_FACTOR", &x))) + { + if ((x < 0) || (x > 100)) + util_out_print("Invalid value for PRE_READ_TRIGGER_FACTOR qualifier", TRUE); + else + cs_addrs->hdr->pre_read_trigger_factor = x; + } + if ((CLI_PRESENT == cli_present("UPD_RESERVED_AREA")) && (cli_get_int("UPD_RESERVED_AREA", &x))) + { + if ((x < 0) || (x > 100)) + util_out_print("Invalid value for UPD_RESERVED_AREA qualifier", TRUE); + else + cs_addrs->hdr->reserved_for_upd = x; + } + if ((CLI_PRESENT == cli_present("UPD_WRITER_TRIGGER_FACTOR")) && (cli_get_int("UPD_WRITER_TRIGGER_FACTOR", &x))) + { + if ((x < 0) || (x > 100)) + util_out_print("Invalid value for UPD_WRITER_TRIGGER_FACTOR qualifier", TRUE); + else + cs_addrs->hdr->writer_trigger_factor = x; + } + CLNUP_CRIT; + return; +} diff --git a/sr_port/dse_chng_rhead.c b/sr_port/dse_chng_rhead.c new file mode 100644 index 0000000..7fea02c --- /dev/null +++ b/sr_port/dse_chng_rhead.c @@ -0,0 +1,164 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "min_max.h" /* needed for gdsblkops.h */ +#include "gdsblkops.h" +#include "gdscc.h" +#include "cli.h" +#include "copy.h" +#include "filestruct.h" +#include "jnl.h" +#include "skan_offset.h" +#include "skan_rnum.h" +#include "dse.h" + +/* Include prototypes */ +#include "t_qread.h" +#include "t_write.h" +#include "t_end.h" +#include "t_begin_crit.h" +#include "gvcst_blk_build.h" +#include "util.h" +#include "t_abort.h" + +GBLREF char *update_array, *update_array_ptr; +GBLREF gd_region *gv_cur_region; +GBLREF uint4 update_array_size; +GBLREF srch_hist dummy_hist; +GBLREF block_id patch_curr_blk; +GBLREF unsigned char patch_comp_count; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF gd_addr *gd_header; +GBLREF cw_set_element cw_set[]; +GBLREF unsigned char *non_tp_jfb_buff_ptr; + +void dse_chng_rhead(void) +{ + block_id blk; + sm_uc_ptr_t bp, b_top, cp, rp; + boolean_t chng_rec; + rec_hdr new_rec; + uint4 x; + blk_segment *bs1, *bs_ptr; + int4 blk_seg_cnt, blk_size; + srch_blk_status blkhist; + + error_def(ERR_DBRDONLY); + error_def(ERR_DSEBLKRDFAIL); + error_def(ERR_DSEFAIL); + + if (gv_cur_region->read_only) + rts_error(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); + CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ + if (cli_present("BLOCK") == CLI_PRESENT) + { + if(!cli_get_hex("BLOCK", (uint4 *)&blk)) + return; + patch_curr_blk = blk; + } + if (patch_curr_blk < 0 || patch_curr_blk >= cs_addrs->ti->total_blks || !(patch_curr_blk % cs_addrs->hdr->bplmap)) + { + util_out_print("Error: invalid block number.", TRUE); + return; + } + + t_begin_crit(ERR_DSEFAIL); + blkhist.blk_num = patch_curr_blk; + if (!(blkhist.buffaddr = t_qread(blkhist.blk_num, &blkhist.cycle, &blkhist.cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + bp = blkhist.buffaddr; + blk_size = cs_addrs->hdr->blk_size; + chng_rec = FALSE; + b_top = bp + ((blk_hdr_ptr_t)bp)->bsiz; + if (((blk_hdr_ptr_t)bp)->bsiz > blk_size || ((blk_hdr_ptr_t)bp)->bsiz < SIZEOF(blk_hdr)) + chng_rec = TRUE; /* force rewrite to correct size */ + if (cli_present("RECORD") == CLI_PRESENT) + { + if (!(rp = skan_rnum(bp, FALSE))) + { + t_abort(gv_cur_region, cs_addrs); + return; + } + } else if (!(rp = skan_offset(bp, FALSE))) + { + t_abort(gv_cur_region, cs_addrs); + return; + } + GET_SHORT(new_rec.rsiz, &((rec_hdr_ptr_t)rp)->rsiz); + new_rec.cmpc = ((rec_hdr_ptr_t)rp)->cmpc; + if (cli_present("CMPC") == CLI_PRESENT) + { + if (!cli_get_hex("CMPC", &x)) + { + t_abort(gv_cur_region, cs_addrs); + return; + } + if (x > 0x7f) + { + util_out_print("Error: invalid cmpc.",TRUE); + t_abort(gv_cur_region, cs_addrs); + return; + } + if (x > patch_comp_count) + util_out_print("Warning: specified compression count is larger than the current expanded key size.", TRUE); + new_rec.cmpc = x; + chng_rec = TRUE; + } + if (cli_present("RSIZ") == CLI_PRESENT) + { + if (!cli_get_hex("RSIZ", &x)) + { + t_abort(gv_cur_region, cs_addrs); + return; + } + if (x < SIZEOF(rec_hdr) || x > blk_size) + { + util_out_print("Error: invalid rsiz.", TRUE); + t_abort(gv_cur_region, cs_addrs); + return; + } + new_rec.rsiz = x; + chng_rec = TRUE; + } + if (chng_rec) + { + BLK_INIT(bs_ptr, bs1); + cp = bp; + cp += SIZEOF(blk_hdr); + if (chng_rec) + { + BLK_SEG(bs_ptr, cp, rp - cp); + BLK_SEG(bs_ptr, (uchar_ptr_t)&new_rec, SIZEOF(rec_hdr)); + cp = rp + SIZEOF(rec_hdr); + } + if (b_top - cp) + BLK_SEG(bs_ptr, cp, b_top - cp); + if (!BLK_FINI(bs_ptr, bs1)) + { + util_out_print("Error: bad blk build.", TRUE); + t_abort(gv_cur_region, cs_addrs); + return; + } + t_write(&blkhist, (unsigned char *)bs1, 0, 0, ((blk_hdr_ptr_t)bp)->levl, TRUE, FALSE, GDS_WRITE_KILLTN); + BUILD_AIMG_IF_JNL_ENABLED(cs_data, non_tp_jfb_buff_ptr, cs_addrs->ti->curr_tn); + t_end(&dummy_hist, NULL, TN_NOT_SPECIFIED); + } + return; +} diff --git a/sr_port/dse_crit.c b/sr_port/dse_crit.c new file mode 100644 index 0000000..311e745 --- /dev/null +++ b/sr_port/dse_crit.c @@ -0,0 +1,184 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gdsbt.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsfhead.h" +#include "cli.h" +#include "lockconst.h" +#include "wcs_recover.h" +#include "dse.h" +#include "tp_change_reg.h" /* for tp_change_reg() prototype */ + +#ifdef UNIX +#include "mutex.h" +#endif +#include "util.h" + +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gd_region *gv_cur_region; +GBLREF uint4 process_id; +GBLREF short crash_count; +GBLREF gd_addr *original_header; + +#define MAX_UTIL_LEN 80 + +void dse_crit(void) +{ + int util_len, dse_crit_count; + char util_buff[MAX_UTIL_LEN]; + boolean_t crash = FALSE, cycle = FALSE, owner = FALSE; + gd_region *save_region, *r_local, *r_top; + + error_def(ERR_DBRDONLY); + + crash = ((cli_present("CRASH") == CLI_PRESENT) || (cli_present("RESET") == CLI_PRESENT)); + cycle = (CLI_PRESENT == cli_present("CYCLE")); + if (cli_present("SEIZE") == CLI_PRESENT || cycle) + { + if (gv_cur_region->read_only && !cycle) + rts_error(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); + if (cs_addrs->now_crit) + { + util_out_print("!/Write critical section already seized.!/", TRUE); + return; + } + crash_count = cs_addrs->critical->crashcnt; + grab_crit(gv_cur_region); + cs_addrs->hold_onto_crit = TRUE; /* need to do this AFTER grab_crit */ + util_out_print("!/Seized write critical section.!/", TRUE); + if (!cycle) + return; + } + if (cli_present("RELEASE") == CLI_PRESENT || cycle) + { + if (gv_cur_region->read_only && !cycle) + rts_error(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); + if (!cs_addrs->now_crit) + { + util_out_print("!/Critical section already released.!/", TRUE); + return; + } + crash_count = cs_addrs->critical->crashcnt; + if (cs_addrs->now_crit) + { /* user wants crit to be released unconditionally so "was_crit" not checked like everywhere else */ + cs_addrs->hold_onto_crit = FALSE; /* need to do this before the rel_crit */ + rel_crit(gv_cur_region); + util_out_print("!/Released write critical section.!/", TRUE); + } + return; + } + if (cli_present("INIT") == CLI_PRESENT) + { + if (gv_cur_region->read_only) + rts_error(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); + cs_addrs->hdr->image_count = 0; + UNIX_ONLY(gtm_mutex_init(gv_cur_region, NUM_CRIT_ENTRY, crash);) + VMS_ONLY(mutex_init(cs_addrs->critical, NUM_CRIT_ENTRY, crash);) + cs_addrs->nl->in_crit = 0; + cs_addrs->now_crit = FALSE; + util_out_print("!/Reinitialized critical section.!/", TRUE); + return; + } + if (cli_present("REMOVE") == CLI_PRESENT) + { + if (gv_cur_region->read_only) + rts_error(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); + if (cs_addrs->nl->in_crit == 0) + { + util_out_print("!/The write critical section is unowned!/", TRUE); + return; + } + UNIX_ONLY(assert(LOCK_AVAILABLE != cs_addrs->critical->semaphore.u.parts.latch_pid);) + VMS_ONLY(assert(cs_addrs->critical->semaphore >= 0);) + cs_addrs->now_crit = TRUE; + cs_addrs->nl->in_crit = process_id; + crash_count = cs_addrs->critical->crashcnt; + /* user wants crit to be removed unconditionally so "was_crit" not checked (before rel_crit) like everywhere else */ + if (dba_bg == cs_addrs->hdr->acc_meth) + { + wcs_recover(gv_cur_region); + /* In case, this crit was obtained through a CRIT -SEIZE, csa->hold_onto_crit would have been set to + * TRUE. Set that back to FALSE now that we are going to release control of crit. + */ + cs_addrs->hold_onto_crit = FALSE; /* need to do this before the rel_crit */ + rel_crit(gv_cur_region); + util_out_print("!/Removed owner of write critical section!/", TRUE); + } else + { + /* In case, this crit was obtained through a CRIT -SEIZE, csa->hold_onto_crit would have been set to + * TRUE. Set that back to FALSE now that we are going to release control of crit. + */ + cs_addrs->hold_onto_crit = FALSE; /* need to do this before the rel_crit */ + rel_crit(gv_cur_region); + util_out_print("!/Removed owner of write critical section!/", TRUE); + util_out_print("!/WARNING: No recovery because database is MM.!/", TRUE); + } + return; + } + if (crash) + { + memcpy(util_buff, "!/Critical section crash count is ", 34); + util_len = 34; + util_len += i2hex_nofill(cs_addrs->critical->crashcnt, (uchar_ptr_t)&util_buff[util_len], 8); + memcpy(&util_buff[util_len], "!/", 2); + util_len += 2; + util_buff[util_len] = 0; + util_out_print(util_buff, TRUE); + return; + } + if (cli_present("ALL") == CLI_PRESENT) + { + dse_crit_count = 0; + save_region = gv_cur_region; + for (r_local = original_header->regions, r_top = r_local + original_header->n_regions; r_local < r_top; r_local++) + { + if (!r_local->open || r_local->was_open) + continue; + gv_cur_region = r_local; + tp_change_reg(); + if (cs_addrs->nl->in_crit) + { + dse_crit_count++; + UNIX_ONLY(util_out_print("Database !AD : CRIT Owned by pid [!UL]", TRUE, + DB_LEN_STR(gv_cur_region), cs_addrs->nl->in_crit);) + VMS_ONLY(util_out_print("Database !AD : CRIT owned by pid [0x!XL]", TRUE, + DB_LEN_STR(gv_cur_region), cs_addrs->nl->in_crit);) + } + } + if (0 == dse_crit_count) + util_out_print("CRIT is currently unowned on all regions", TRUE); + gv_cur_region = save_region; + tp_change_reg(); + return; + } + if (cs_addrs->nl->in_crit) + { +# if defined(UNIX) + util_out_print("!/Write critical section owner is process id !UL", TRUE, cs_addrs->nl->in_crit); + if (cs_addrs->now_crit) + util_out_print("DSE (process id: !UL) owns the write critical section", TRUE, process_id); +# elif defined(VMS) + util_out_print("!/Write critical section owner is process id !XL", TRUE, cs_addrs->nl->in_crit); + if (cs_addrs->now_crit) + util_out_print("DSE (process id: !XL) owns the write critical section", TRUE, process_id); +# endif + util_out_print(0, TRUE); + } else + util_out_print("!/Write critical section is currently unowned", TRUE); + return; +} diff --git a/sr_port/dse_data.c b/sr_port/dse_data.c new file mode 100644 index 0000000..0e30712 --- /dev/null +++ b/sr_port/dse_data.c @@ -0,0 +1,76 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "cli.h" +#include "dse.h" + +int dse_data(char *dst, int *len) +{ + + unsigned short cli_len; + char buf[MAX_LINE],*src,*bot,*top; + + cli_len = SIZEOF(buf); + if (!cli_get_str("DATA",buf,&cli_len)) + return FALSE; + bot = dst; + top = &buf[cli_len - 1]; + src = &buf[0]; + +#ifdef VMS + if (buf[0] == '"') + src = &buf[1]; +#endif + + for (; src <= top ;src++) + { +#ifdef VMS + if (src == top && *src == '"') + break; +#endif + if (*src == '\\') + { + src++; + if (*src == '\\') + { + *dst++ = '\\'; + continue; + } + if (*src >= '0' && *src <= '9') + *dst = *src - '0'; + else if (*src >= 'a' && *src <= 'f') + *dst = *src - 'a' + 10; + else if (*src >= 'A' && *src <= 'F') + *dst = *src - 'A' +10; + else + continue; + src++; + if (*src >= '0' && *src <= '9') + *dst = (*dst << 4) + *src - '0'; + else if (*src >= 'a' && *src <= 'f') + *dst = (*dst << 4) + *src - 'a' + 10; + else if (*src >= 'A' && *src <= 'F') + *dst = (*dst << 4) + *src - 'A' +10; + dst++; + } + else + *dst++ = *src; + } + *len = (int)(dst - bot); + return TRUE; + +} diff --git a/sr_port/dse_dmp.c b/sr_port/dse_dmp.c new file mode 100644 index 0000000..8797043 --- /dev/null +++ b/sr_port/dse_dmp.c @@ -0,0 +1,93 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "cli.h" +#include "dse.h" +#include "error.h" +#include "util.h" + +GBLDEF enum dse_fmt dse_dmp_format = CLOSED_FMT; + +GBLREF boolean_t patch_is_fdmp; +GBLREF int patch_fdmp_recs; + +#define MESS_OFF SIZEOF("; ") - 1 + +CONDITION_HANDLER(dse_dmp_handler) +{ + START_CH; + PRN_ERROR; + util_out_print("!/DSE is not able to complete the dump to file due to the above reason.!/", TRUE); + UNWIND(NULL,NULL); +} + +static char *format_label[] = {"; BAD", "; GLO", "; ZWR"}; /* CLOSE_FMT == 0, GLO_FMT == 1 and ZWR_FMT == 2 */ + +void dse_dmp(void) +{ + boolean_t dmp_res, glo_present, zwr_present; + + patch_fdmp_recs = 0; + glo_present = (CLI_PRESENT == cli_present("GLO")); + zwr_present = (CLI_PRESENT == cli_present("ZWR")); + if (glo_present || zwr_present) + { + if (CLOSED_FMT == dse_dmp_format) + { + util_out_print("Error: must open an output file before dump.", TRUE); + return; + } + if (gtm_utf8_mode && (GLO_FMT == glo_present)) + { + util_out_print("Error: GLO format is not supported in UTF-8 mode. Use ZWR format.", TRUE); + return; + } + if (OPEN_FMT == dse_dmp_format) + { + dse_dmp_format = (glo_present ? GLO_FMT : ZWR_FMT); + if (!gtm_utf8_mode) + dse_fdmp_output(LIT_AND_LEN("; DSE EXTRACT")); + else + dse_fdmp_output(LIT_AND_LEN("; DSE EXTRACT UTF-8")); + dse_fdmp_output(STR_AND_LEN(format_label[dse_dmp_format])); + } else if ((glo_present ? GLO_FMT : ZWR_FMT) != dse_dmp_format) + { + util_out_print("Error: current output file already contains !AD records.", TRUE, + LEN_AND_STR(&format_label[dse_dmp_format][MESS_OFF])); + return; + } + patch_is_fdmp = TRUE; + ESTABLISH(dse_dmp_handler); + } else + patch_is_fdmp = FALSE; + if (CLI_PRESENT == cli_present("RECORD") || CLI_PRESENT == cli_present("OFFSET")) + dmp_res = dse_r_dmp(); + else + dmp_res = dse_b_dmp(); + if (patch_is_fdmp) + { + REVERT; + if (dmp_res) + util_out_print("!UL !AD records written.!/", TRUE, patch_fdmp_recs, + LEN_AND_STR(&format_label[dse_dmp_format][MESS_OFF])); + } + return; +} diff --git a/sr_port/dse_dmp_fhead.c b/sr_port/dse_dmp_fhead.c new file mode 100644 index 0000000..4b5eabc --- /dev/null +++ b/sr_port/dse_dmp_fhead.c @@ -0,0 +1,495 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/******************************************************************************* +* +* MODULE NAME: DSE_DMP_FHEAD +* +* CALLING SEQUENCE: void dse_dmp_fhead () +* +* DESCRIPTION: This module dumps certain fields of current file +* header. +* +* HISTORY: +* +*******************************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include /* needed for handling of epoch_interval (EPOCH_SECOND2SECOND macro uses ceil) */ + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "jnl.h" +#include "cli.h" +#include "util.h" +#include "dse.h" +#include "dse_puttime.h" +#include "gtmmsg.h" +#include "stringpool.h" /* for GET_CURR_TIME_IN_DOLLARH_AND_ZDATE macro */ +#include "op.h" +#include "shmpool.h" /* Needed for the shmpool structures */ +#ifdef GTM_SNAPSHOT +#include "db_snapshot.h" +#endif + +#define MAX_UTIL_LEN 64 +#define NEXT_EPOCH_TIME_SPACES " " /* 19 spaces, we have 19 character field width to output Next Epoch Time */ + +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gd_region *gv_cur_region; +GBLREF boolean_t dse_all_dump; /* TRUE if DSE ALL -DUMP is specified */ + +GBLDEF mval dse_dmp_time_fmt = DEFINE_MVAL_STRING(MV_STR, 0, 0, STR_LIT_LEN(DSE_DMP_TIME_FMT), DSE_DMP_TIME_FMT, 0, 0); + +LITREF char *jrt_label[JRT_RECTYPES]; +LITREF char *gtm_dbversion_table[]; + + +#define SHOW_STAT(TEXT, VARIABLE) if (0 != csd->VARIABLE##_cntr) \ + util_out_print(TEXT" 0x!XL Transaction = 0x!16@XQ", TRUE, (csd->VARIABLE##_cntr), \ + (&csd->VARIABLE##_tn)); + +#define SHOW_DB_CSH_STAT(csd, COUNTER, TEXT1, TEXT2) \ + if (csd->COUNTER.curr_count || csd->COUNTER.cumul_count) \ + { \ + util_out_print(TEXT1" 0x!XL "TEXT2" 0x!XL", TRUE, (csd->COUNTER.curr_count), \ + (csd->COUNTER.cumul_count + csd->COUNTER.curr_count)); \ + } + +#define SHOW_GVSTATS_STAT(cnl, COUNTER, TEXT1, TEXT2) \ +{ \ + if (cnl->gvstats_rec.COUNTER) \ + util_out_print(" " TEXT1 " : " TEXT2" 0x!16@XQ", TRUE, (&cnl->gvstats_rec.COUNTER)); \ +} + +/* NEED_TO_DUMP is only for the qualifiers other than "BASIC" and "ALL". + file_header is not dumped only if "NOBASIC" is explicitly specified */ + +#define NEED_TO_DUMP(string) \ + (is_dse_all ? (CLI_PRESENT == cli_present("ALL")) \ + : (CLI_PRESENT == cli_present(string) || CLI_PRESENT == cli_present("ALL") && CLI_NEGATED != cli_present(string))) + +void dse_dmp_fhead (void) +{ + boolean_t jnl_buff_open; + unsigned char util_buff[MAX_UTIL_LEN], buffer[MAXNUMLEN]; + int util_len, rectype, time_len, index, idx; + uint4 jnl_status; + enum jnl_state_codes jnl_state; + gds_file_id zero_fid; + mval dollarh_mval, zdate_mval; + char dollarh_buffer[MAXNUMLEN], zdate_buffer[SIZEOF(DSE_DMP_TIME_FMT)]; + sgmnt_addrs *csa; + sgmnt_data_ptr_t csd; + node_local_ptr_t cnl; + jnl_private_control *jpc; + jnl_buffer_ptr_t jb; + shmpool_buff_hdr_ptr_t bptr; + boolean_t is_dse_all; + uint4 pid; + boolean_t new_line; + unsigned char outbuf[GTMCRYPT_HASH_HEX_LEN + 1]; + GTM_SNAPSHOT_ONLY( + shm_snapshot_t *ss_shm_ptr;) + + is_dse_all = dse_all_dump; + dse_all_dump = FALSE; + csa = cs_addrs; + csd = csa->hdr; + cnl = csa->nl; + jnl_state = (enum jnl_state_codes)csd->jnl_state; + VMS_ONLY( + memset(&zero_fid, 0, SIZEOF(zero_fid)); + jnl_buff_open = (0 != memcmp(cnl->jnl_file.jnl_file_id.fid, zero_fid.fid, SIZEOF(zero_fid.fid))); + ) + UNIX_ONLY( + jnl_buff_open = (0 != cnl->jnl_file.u.inode); + ) + if (is_dse_all || (CLI_NEGATED != cli_present("BASIC"))) + { + util_out_print("!/File !AD", TRUE, gv_cur_region->dyn.addr->fname_len, + &gv_cur_region->dyn.addr->fname[0]); + util_out_print("Region !AD", TRUE, gv_cur_region->rname_len, &gv_cur_region->rname[0]); + GET_CURR_TIME_IN_DOLLARH_AND_ZDATE(dollarh_mval, dollarh_buffer, zdate_mval, zdate_buffer); + util_out_print("Date/Time !AD [$H = !AD]", TRUE, zdate_mval.str.len, zdate_mval.str.addr, + dollarh_mval.str.len, dollarh_mval.str.addr); + util_out_print(" Access method !AD", FALSE, + 2, (csd->acc_meth == dba_mm) ? "MM" : "BG"); + util_out_print(" Global Buffers !12UL", TRUE, csd->n_bts); + util_out_print(" Reserved Bytes !19UL", FALSE, csd->reserved_bytes); + util_out_print(" Block size (in bytes) !12UL", TRUE, csd->blk_size); + util_out_print(" Maximum record size !19UL", FALSE, csd->max_rec_size); + util_out_print(" Starting VBN !12UL", TRUE, csd->start_vbn); + util_out_print(" Maximum key size !19UL", FALSE, csd->max_key_size); + util_out_print(" Total blocks 0x!XL", TRUE, csa->ti->total_blks); + util_out_print(" Null subscripts !AD", FALSE, 12, + (csd->null_subs == ALWAYS) ? " ALWAYS" : (csd->null_subs == ALLOWEXISTING) ? " EXISTING" : + " NEVER" ); + util_out_print(" Free blocks 0x!XL", TRUE, csa->ti->free_blocks); + + /* + NOTE: Currently Std Null Collation is the only entry in one line, + For 64bit TN project, when some other fields will be added, this can + be adjusted then - MM Oct 04 + */ + util_out_print(" Standard Null Collation !AD", FALSE, 11, + (csd->std_null_coll) ? " TRUE" : " FALSE"); + util_out_print(" Free space 0x!XL", TRUE, csd->free_space); + util_out_print(" Last Record Backup 0x!16@XQ", FALSE, &csd->last_rec_backup); + util_out_print (" Extension Count !12UL", TRUE, csd->extension_size); + util_out_print(" Last Database Backup 0x!16@XQ", FALSE, &csd->last_com_backup); + if (csd->bplmap > 0) + util_out_print(" Number of local maps !12UL", TRUE, + (csa->ti->total_blks + csd->bplmap - 1) / csd->bplmap); + else + util_out_print(" Number of local maps ??", TRUE); + util_out_print(" Last Bytestream Backup 0x!16@XQ", FALSE, &csd->last_inc_backup); + util_out_print(" Lock space 0x!XL", TRUE, csd->lock_space_size/OS_PAGELET_SIZE); + util_out_print(" In critical section 0x!XL", FALSE, cnl->in_crit); + util_out_print(" Timers pending !12UL", TRUE, cnl->wcs_timers + 1); + if (FROZEN_BY_ROOT == csd->freeze) + util_out_print(" Cache freeze id FROZEN BY ROOT", FALSE); + else + util_out_print(" Cache freeze id 0x!XL", FALSE, (csd->freeze)? csd->freeze : 0); + dse_puttime(csd->flush_time, " Flush timer !AD", TRUE); + util_out_print(" Freeze match 0x!XL", FALSE, csd->image_count ? csd->image_count : 0); + util_out_print(" Flush trigger !12UL", TRUE, csd->flush_trigger); + util_out_print(" Current transaction 0x!16@XQ", FALSE, &csa->ti->curr_tn); + util_out_print(" No. of writes/flush !12UL", TRUE, csd->n_wrt_per_flu); + util_out_print(" Maximum TN 0x!16@XQ", FALSE, &csd->max_tn); + if (GDSVLAST > csd->certified_for_upgrade_to) + util_out_print(" Certified for Upgrade to !AD", TRUE, + LEN_AND_STR(gtm_dbversion_table[csd->certified_for_upgrade_to])); + else /* out of range so print hex */ + util_out_print(" Certified for Upgrade to 0x!XL", TRUE, csd->certified_for_upgrade_to); + util_out_print(" Maximum TN Warn 0x!16@XQ", FALSE, &csd->max_tn_warn); + if (GDSVLAST > csd->desired_db_format) + util_out_print(" Desired DB Format !AD", TRUE, + LEN_AND_STR(gtm_dbversion_table[csd->desired_db_format])); + else /* out of range so print hex */ + util_out_print(" Desired DB Format 0x!XL", TRUE, csd->desired_db_format); + util_out_print(" Master Bitmap Size !12UL", FALSE, csd->master_map_len / DISK_BLOCK_SIZE); + util_out_print(" Blocks to Upgrade 0x!XL", TRUE, csd->blks_to_upgrd); + if (csd->def_coll) + { + util_out_print(" Default Collation !19UL", FALSE, csd->def_coll); + util_out_print(" Collation Version !12UL", TRUE, csd->def_coll_ver); + } + util_out_print(" Create in progress !AD", FALSE, 12, (csd->createinprogress) ? + " TRUE" : " FALSE"); + +# ifdef CNTR_WORD_32 + util_out_print(" Modified cache blocks !12UL", TRUE, cnl->wcs_active_lvl); +# else + util_out_print(" Modified cache blocks !12UW", TRUE, cnl->wcs_active_lvl); +# endif + + util_out_print(" Reference count !19UL", FALSE, cnl->ref_cnt); + util_out_print(" Wait Disk !12UL", TRUE, csd->wait_disk_space); + util_out_print(" Journal State !AD", (jnl_notallowed == jnl_state), 13, + (jnl_notallowed != jnl_state) ? + ((jnl_state == jnl_closed) ? " OFF" + : (jnl_buff_open ? " ON" : "[inactive] ON")) : " DISABLED"); + if (jnl_notallowed != jnl_state) + { + util_out_print(" Journal Before imaging !AD", TRUE, + 5, (csd->jnl_before_image) ? " TRUE" : "FALSE"); + util_out_print(" Journal Allocation !19UL", FALSE, csd->jnl_alq); + util_out_print(" Journal Extension !12UL", TRUE, csd->jnl_deq); + util_out_print(" Journal Buffer Size !19UL", FALSE, csd->jnl_buffer_size); + util_out_print(" Journal Alignsize !12UL", TRUE, csd->alignsize / DISK_BLOCK_SIZE); + util_out_print(" Journal AutoSwitchLimit !17UL", FALSE, csd->autoswitchlimit); + util_out_print(" Journal Epoch Interval!12UL", TRUE, EPOCH_SECOND2SECOND(csd->epoch_interval)); +# ifdef UNIX + util_out_print(" Journal Yield Limit !19UL", FALSE, csd->yield_lmt); + util_out_print(" Journal Sync IO !AD", TRUE, 5, + (csd->jnl_sync_io ? " TRUE" : "FALSE")); +# elif VMS + util_out_print(" Journal NOCACHE IO !AD", TRUE, 12, + (csd->jnl_sync_io ? " TRUE" : " FALSE")); +# endif + util_out_print(" Journal File: !AD", TRUE, JNL_LEN_STR(csd)); + } + if (BACKUP_NOT_IN_PROGRESS != cnl->nbb) + util_out_print(" Online Backup NBB !19UL", TRUE, cnl->nbb); + /* Mutex Stuff */ + util_out_print(" Mutex Hard Spin Count !19UL", FALSE, csd->mutex_spin_parms.mutex_hard_spin_count); + util_out_print(" Mutex Sleep Spin Count!12UL", TRUE, csd->mutex_spin_parms.mutex_sleep_spin_count); + util_out_print(" Mutex Spin Sleep Time !19UL", FALSE, + (csd->mutex_spin_parms.mutex_spin_sleep_mask == 0) ? + 0 : (csd->mutex_spin_parms.mutex_spin_sleep_mask + 1)); + util_out_print(" KILLs in progress !12UL", TRUE, (csd->kill_in_prog + csd->abandoned_kills)); + util_out_print(" Replication State !AD", FALSE, 13, + (csd->repl_state == repl_closed ? " OFF" + : (csd->repl_state == repl_open ? " ON" : " [WAS_ON] OFF"))); + util_out_print(" Region Seqno 0x!16@XQ", TRUE, &csd->reg_seqno); + VMS_ONLY( + util_out_print(" Resync Seqno 0x!16@XQ", FALSE, &csd->resync_seqno); + util_out_print(" Resync trans 0x!16@XQ", TRUE, &csd->resync_tn); + ) + UNIX_ONLY( + util_out_print(" Zqgblmod Seqno 0x!16@XQ", FALSE, &csd->zqgblmod_seqno); + util_out_print(" Zqgblmod Trans 0x!16@XQ", TRUE, &csd->zqgblmod_tn); + ) + util_out_print(" Endian Format !6AZ", UNIX_ONLY(FALSE) VMS_ONLY(TRUE), ENDIANTHISJUSTIFY); + UNIX_ONLY( + util_out_print(" Commit Wait Spin Count!12UL", TRUE, csd->wcs_phase2_commit_wait_spincnt); + ) + util_out_print(" Database file encrypted !AD", TRUE, 5, csd->is_encrypted ? " TRUE" : "FALSE"); + } + if (CLI_PRESENT == cli_present("ALL")) + { /* Only dump if -/ALL as if part of above display */ + util_out_print(0, TRUE); + UNIX_ONLY(util_out_print(" Dualsite Resync Seqno 0x!16@XQ", FALSE, &csd->dualsite_resync_seqno);) + VMS_ONLY(util_out_print(" ", FALSE);) + util_out_print(" DB Current Minor Version !4UL", TRUE, csd->minor_dbver); + util_out_print(" Blks Last Record Backup 0x!XL", FALSE, csd->last_rec_bkup_last_blk); + util_out_print(" Last GT.M Minor Version !4UL", TRUE, csd->last_mdb_ver); + util_out_print(" Blks Last Stream Backup 0x!XL", FALSE, csd->last_inc_bkup_last_blk); + util_out_print(" DB Creation Version !AD", TRUE, + LEN_AND_STR(gtm_dbversion_table[csd->creation_db_ver])); + util_out_print(" Blks Last Comprehensive Backup 0x!XL", FALSE, csd->last_com_bkup_last_blk); + util_out_print(" DB Creation Minor Version !4UL", TRUE, csd->creation_mdb_ver); + util_out_print(0, TRUE); + util_out_print(" Total Global Buffers 0x!XL", FALSE, csd->n_bts); + util_out_print(" Phase2 commit pid count 0x!XL", TRUE, cnl->wcs_phase2_commit_pidcnt); + util_out_print(" Dirty Global Buffers 0x!XL", FALSE, cnl->wcs_active_lvl); + util_out_print(" Write cache timer count 0x!XL", TRUE, cnl->wcs_timers); + util_out_print(" Free Global Buffers 0x!XL", FALSE, cnl->wc_in_free); + util_out_print(" wcs_wtstart pid count 0x!XL", TRUE, cnl->in_wtstart); + util_out_print(" Write Cache is Blocked !AD", FALSE, 5, (csd->wc_blocked ? " TRUE" : "FALSE")); + util_out_print(" wcs_wtstart intent cnt 0x!XL", TRUE, cnl->intent_wtstart); + new_line = FALSE; + for (index = 0; MAX_WTSTART_PID_SLOTS > index; index++) + { + pid = cnl->wtstart_pid[index]; + if (0 != pid) + { + util_out_print(" wcs_wtstart pid [!2UL] !AD !12UL", new_line, index, + new_line ? 0 : 7, new_line ? "" : " ", pid); + new_line = !new_line; + } + } + /* Additional information regarding kills that are in progress, abandoned and inhibited */ + util_out_print(0, TRUE); + util_out_print(" Actual kills in progress !12UL", FALSE, csd->kill_in_prog); + util_out_print(" Abandoned Kills !12UL", TRUE, csd->abandoned_kills); + util_out_print(" Process(es) inhibiting KILLs !5UL", TRUE, cnl->inhibit_kills); + + util_out_print(0, TRUE); + util_out_print(" DB Trigger cycle of ^#t !12UL", TRUE, csd->db_trigger_cycle); + util_out_print(0, TRUE); + util_out_print(" MM defer_time !5SL", TRUE, csd->defer_time); + /* Print the database encryption hash information */ + GET_HASH_IN_HEX(csd->encryption_hash, outbuf, GTMCRYPT_HASH_HEX_LEN); + util_out_print(" Database file encryption hash !AD", TRUE, GTMCRYPT_HASH_HEX_LEN, outbuf); + } + if (NEED_TO_DUMP("ENVIRONMENT")) + { + util_out_print(0, TRUE); + util_out_print(" Full Block Writes !AD", FALSE, 6, + (csa->do_fullblockwrites) ? " ON" : " OFF"); + util_out_print(" Full Block Write Len !12UL", TRUE, csa->fullblockwrite_len); + } + if (NEED_TO_DUMP("DB_CSH")) + { + util_out_print(0, TRUE); +# define TAB_DB_CSH_ACCT_REC(COUNTER,TEXT1,TEXT2) SHOW_DB_CSH_STAT(csd, COUNTER, TEXT1, TEXT2) +# include "tab_db_csh_acct_rec.h" +# undef TAB_DB_CSH_ACCT_REC + } + if (NEED_TO_DUMP("GVSTATS")) + { + util_out_print(0, TRUE); +# define TAB_GVSTATS_REC(COUNTER,TEXT1,TEXT2) SHOW_GVSTATS_STAT(cnl, COUNTER, TEXT1, TEXT2) +# include "tab_gvstats_rec.h" +# undef TAB_GVSTATS_REC + } + if (NEED_TO_DUMP("TPBLKMOD")) + { + util_out_print(0, TRUE); + assert(n_tp_blkmod_types < ARRAYSIZE(csd->tp_cdb_sc_blkmod)); + util_out_print(" TP blkmod nomod !12UL", TRUE, csd->tp_cdb_sc_blkmod[tp_blkmod_nomod]); + util_out_print(" TP blkmod gvcst_srch !12UL", TRUE, csd->tp_cdb_sc_blkmod[tp_blkmod_gvcst_srch]); + util_out_print(" TP blkmod t_qread !12UL", TRUE, csd->tp_cdb_sc_blkmod[tp_blkmod_t_qread]); + util_out_print(" TP blkmod tp_tend !12UL", TRUE, csd->tp_cdb_sc_blkmod[tp_blkmod_tp_tend]); + util_out_print(" TP blkmod tp_hist !12UL", TRUE, csd->tp_cdb_sc_blkmod[tp_blkmod_tp_hist]); + } + if (NEED_TO_DUMP("BG_TRC")) + { + util_out_print(0, TRUE); + /* print out all the BG_TRACE accounting fields */ +# define TAB_BG_TRC_REC(A,B) SHOW_STAT(A,B); +# include "tab_bg_trc_rec.h" +# undef TAB_BG_TRC_REC + } + jpc = csa->jnl; + if (NEED_TO_DUMP("JOURNAL") && (JNL_ENABLED(csd) && (NULL != jpc) && (NULL != jpc->jnl_buff))) + { + jb = jpc->jnl_buff; + util_out_print(0, TRUE); + /* --------------------------- journal buffer --------------------------------- */ + util_out_print(" Jnl Buffer Size 0x!XL", FALSE, jb->size); + util_out_print(" ", FALSE); + util_out_print(" Dskaddr 0x!XL", TRUE, jb->dskaddr); + util_out_print(" Free 0x!XL", FALSE, jb->free); + util_out_print(" ", FALSE); + util_out_print(" Freeaddr 0x!XL", TRUE, jb->freeaddr); + util_out_print(" Dsk 0x!XL", FALSE, jb->dsk); + util_out_print(" ", FALSE); + util_out_print(" Wrtsize 0x!XL", TRUE, jb->wrtsize); + util_out_print(" Journal checksum seed 0x!XL", FALSE, csd->jnl_checksum); + util_out_print(" ", FALSE); + util_out_print(" Min_write_size 0x!XL", TRUE, jb->min_write_size); + util_out_print(" bytcnt 0x!XL", FALSE, jb->bytcnt); + util_out_print(" ", FALSE); + util_out_print(" Max_write_size 0x!XL", TRUE, jb->max_write_size); + util_out_print(" Before image !AD", FALSE, 5, (jb->before_images ? " TRUE" : "FALSE")); + util_out_print(" ", FALSE); + util_out_print(" Filesize !12UL", TRUE, jb->filesize); + util_out_print(" Iosb.cond !12UW", FALSE, jb->iosb.cond); + util_out_print(" ", FALSE); + util_out_print(" qiocnt !12UL", TRUE, jb->qiocnt); + util_out_print(" Iosb.length 0x!4XW", FALSE, jb->iosb.length); + util_out_print(" ", FALSE); + util_out_print(" errcnt !12UL", TRUE, jb->errcnt); + util_out_print(" Iosb.dev_specific !12UL", FALSE, jb->iosb.dev_specific); + util_out_print(" ", FALSE); + time_len = exttime(jb->next_epoch_time, (char *)buffer, 0); + assert(STR_LIT_LEN(NEXT_EPOCH_TIME_SPACES) >= time_len); + util_out_print(" Next Epoch_Time!AD!AD", TRUE, STR_LIT_LEN(NEXT_EPOCH_TIME_SPACES) - time_len + 1, + NEXT_EPOCH_TIME_SPACES, time_len - 1, buffer); /* -1 to avoid printing \ at end of $H + * format time returned by exttime */ + util_out_print(" Blocked Process !12UL", FALSE, jb->blocked); + util_out_print(" ", FALSE); + util_out_print(" Epoch_tn 0x!16@XQ", TRUE, &jb->epoch_tn); + util_out_print(" Io_in_progress !AD", FALSE, 5, + (jb->UNIX_ONLY(io_in_prog_latch.u.parts.latch_pid)VMS_ONLY(io_in_prog) ? " TRUE" : "FALSE")); + util_out_print(" ", FALSE); + util_out_print(" Epoch_Interval !12UL", TRUE, EPOCH_SECOND2SECOND(jb->epoch_interval)); + util_out_print(" Now_writer !12UL", FALSE, + (jb->UNIX_ONLY(io_in_prog_latch.u.parts.latch_pid)VMS_ONLY(now_writer))); + util_out_print(" ", FALSE); + util_out_print(" Image_count !12UL", TRUE, jb->image_count); + util_out_print(" fsync_in_prog !AD", FALSE, 5, + (jb->fsync_in_prog_latch.u.parts.latch_pid ? " TRUE" : "FALSE")); + util_out_print(" ", FALSE); + util_out_print(" fsync pid !12SL", TRUE, (jb->fsync_in_prog_latch.u.parts.latch_pid)); + util_out_print(" fsync addrs 0x!XL", FALSE, jb->fsync_dskaddr); + util_out_print(" ", FALSE); + util_out_print(" Need_db_fsync !AD", TRUE, 5, (jb->need_db_fsync ? " TRUE" : "FALSE")); + util_out_print(" Filesystem block size 0x!XL", FALSE, jb->fs_block_size); + util_out_print(" ", FALSE); + util_out_print(" jnl solid tn 0x!16@XQ", TRUE, &csd->jnl_eovtn); + for (rectype = JRT_BAD + 1; rectype < JRT_RECTYPES - 1; rectype++) + { + util_out_print(" Jnl Rec Type !5AZ !7UL ", FALSE, jrt_label[rectype], + jb->reccnt[rectype]); + rectype++; + util_out_print(" Jnl Rec Type !5AZ !7UL", TRUE, jrt_label[rectype], jb->reccnt[rectype]); + } + if (rectype != JRT_RECTYPES) + util_out_print(" Jnl Rec Type !5AZ !7UL", TRUE, jrt_label[rectype], jb->reccnt[rectype]); + util_out_print(0, TRUE); + util_out_print(" Recover interrupted !AD", FALSE, 5, (csd->recov_interrupted ? " TRUE" : "FALSE")); + util_out_print(" ", FALSE); + util_out_print(" INTRPT resolve time !12UL", TRUE, csd->intrpt_recov_tp_resolve_time); + util_out_print(" INTRPT seqno 0x!16@XQ", FALSE, &csd->intrpt_recov_resync_seqno); + util_out_print(" ", FALSE); + util_out_print(" INTRPT jnl_state !12UL", TRUE, csd->intrpt_recov_jnl_state); + util_out_print(" INTRPT repl_state !12UL", FALSE, csd->intrpt_recov_repl_state); + util_out_print(0, TRUE); + } + if (NEED_TO_DUMP("BACKUP")) + { + bptr = csa->shmpool_buffer; + /* --------------------------- online backup buffer ---------------------------------- */ + util_out_print(0, TRUE); + util_out_print(" Free blocks !12UL", FALSE, bptr->free_cnt); + util_out_print(" ", FALSE); + util_out_print(" Backup blocks !12UL", TRUE, bptr->backup_cnt); + util_out_print(" Reformat blocks !12UL", FALSE, bptr->reformat_cnt); + util_out_print(" ", FALSE); + util_out_print(" Total blocks !12UL", TRUE, bptr->total_blks); + util_out_print(" Shmpool blocked !AD", FALSE, 5, (bptr->shmpool_blocked ? " TRUE" : "FALSE")); + util_out_print(" ", FALSE); + util_out_print(" File Offset 0x!16@XQ", TRUE, &bptr->dskaddr); + util_out_print(" Shmpool crit holder !12UL", FALSE, bptr->shmpool_crit_latch.u.parts.latch_pid); + util_out_print(" ", FALSE); + util_out_print(" Backup_errno !12UL", TRUE, bptr->backup_errno); +# ifdef VMS + util_out_print(" Shmpool crit imgcnt !12UL", TRUE, bptr->shmpool_crit_latch.u.parts.latch_image_count); +# endif + util_out_print(" Backup Process ID !12UL", FALSE, bptr->backup_pid); + util_out_print(" ", FALSE); + util_out_print(" Backup TN 0x!16@XQ", TRUE, &bptr->backup_tn); + util_out_print(" Inc Backup TN 0x!16@XQ", FALSE, &bptr->inc_backup_tn); + util_out_print(" ", FALSE); + util_out_print(" Process Failed !12UL", TRUE, bptr->failed); + util_out_print(" Allocs since check !12UL", FALSE, bptr->allocs_since_chk); + util_out_print(" ", FALSE); + util_out_print(" Backup Image Count !12UL", TRUE, bptr->backup_image_count); + util_out_print(" Temp File: !AD", TRUE, LEN_AND_STR(&bptr->tempfilename[0])); + } + if (NEED_TO_DUMP("MIXEDMODE")) + { + util_out_print(0, TRUE); + util_out_print(" Database is Fully Upgraded : !AD", + TRUE, 5, (csd->fully_upgraded ? " TRUE" : "FALSE")); + util_out_print(" Database WAS ONCE Fully Upgraded from V4 : !AD", + TRUE, 5, (csd->db_got_to_v5_once ? " TRUE" : "FALSE")); + util_out_print(" Blocks to Upgrade subzero(negative) error : 0x!XL", TRUE, csd->blks_to_upgrd_subzero_error); + util_out_print(" TN when Blocks to Upgrade last became 0 : 0x!16@XQ", TRUE, &csd->tn_upgrd_blks_0); + util_out_print(" TN when Desired DB Format last changed : 0x!16@XQ", TRUE, &csd->desired_db_format_tn); + util_out_print(" TN when REORG upgrd/dwngrd changed dbfmt : 0x!16@XQ", TRUE, &csd->reorg_db_fmt_start_tn); + util_out_print(0, TRUE); + util_out_print(" Block Number REORG upgrd/dwngrd will restart from : 0x!XL", + TRUE, csd->reorg_upgrd_dwngrd_restart_block); + } + if (NEED_TO_DUMP("UPDPROC")) + { + util_out_print(0, TRUE); + util_out_print(" Upd reserved area [% global buffers] !3UL", FALSE, csd->reserved_for_upd); + util_out_print(" Avg blks read per 100 records !4UL", TRUE, csd->avg_blks_per_100gbl); + util_out_print(" Pre read trigger factor [% upd rsrvd] !3UL", FALSE, csd->pre_read_trigger_factor); + util_out_print(" Upd writer trigger [%flshTrgr] !3UL", TRUE, csd->writer_trigger_factor); + } +# ifdef GTM_SNAPSHOT + if (NEED_TO_DUMP("SNAPSHOT")) + { + util_out_print(0, TRUE); + util_out_print(" Snapshot in progress !AD", FALSE, 5, + (cnl->snapshot_in_prog ? " TRUE" : "FALSE")); + util_out_print(" Number of active snapshots !12UL", TRUE, cnl->num_snapshots_in_effect); + util_out_print(" Snapshot cycle !12UL", FALSE, cnl->ss_shmcycle); + /* SS_MULTI: Note that if we have multiple snapshots, then we have to run through each active + * snapshot region and dump their informations respectively + */ + ss_shm_ptr = (shm_snapshot_ptr_t)(SS_GETSTARTPTR(csa)); + util_out_print(" Active snapshot PID !12UL", TRUE, ss_shm_ptr->ss_info.ss_pid); + util_out_print(" Snapshot TN !12UL", FALSE, ss_shm_ptr->ss_info.snapshot_tn); + util_out_print(" Total blocks !12UL", TRUE, ss_shm_ptr->ss_info.total_blks); + util_out_print(" Free blocks !12UL", FALSE, ss_shm_ptr->ss_info.free_blks); + util_out_print(" Process failed !12UL", TRUE, ss_shm_ptr->failed_pid); + util_out_print(" Failure errno !12UL", FALSE, ss_shm_ptr->failure_errno); + util_out_print(" Snapshot shared memory identifier !12SL", TRUE, ss_shm_ptr->ss_info.ss_shmid); + util_out_print(" Snapshot file name !AD", TRUE, LEN_AND_STR(ss_shm_ptr->ss_info.shadow_file)); + } +# endif + return; +} diff --git a/sr_port/dse_eval.c b/sr_port/dse_eval.c new file mode 100644 index 0000000..3fbd153 --- /dev/null +++ b/sr_port/dse_eval.c @@ -0,0 +1,49 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "cli.h" +#include "util.h" +#include "dse.h" + +#define MAX_UTIL_LEN 56 + +void dse_eval(void) +{ + int4 util_len; + gtm_uint64_t num; + char util_buff[MAX_UTIL_LEN]; + + if (cli_present("NUMBER") != CLI_PRESENT) + return; + if (cli_present("DECIMAL") == CLI_PRESENT) + { + if (!cli_get_uint64("NUMBER", (gtm_uint64_t *)&num)) + return; + } else if (!cli_get_hex64("NUMBER", &num)) + return; + memcpy(util_buff, "Hex: ", 6); + util_len = 6; + util_len += i2hexl_nofill(num, (uchar_ptr_t)&util_buff[util_len], 16); + memcpy(&util_buff[util_len]," Dec: !@UQ", 13); + util_len += 13; + util_buff[util_len] = 0; + util_out_print(util_buff, TRUE, &num); + return; +} diff --git a/sr_port/dse_exhaus.c b/sr_port/dse_exhaus.c new file mode 100644 index 0000000..f4ad059 --- /dev/null +++ b/sr_port/dse_exhaus.c @@ -0,0 +1,213 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "dsefind.h" +#include "copy.h" +#include "util.h" +#include "dse.h" + +/* Include prototypes */ +#include "t_qread.h" + +#define MAX_UTIL_LEN 32 + +GBLDEF short int patch_path_count; + +GBLREF global_root_list *global_roots_head; +GBLREF bool patch_find_sibs; +GBLREF bool patch_find_root_search; +GBLREF bool patch_exh_found; +GBLREF block_id patch_path[MAX_BT_DEPTH + 1]; +GBLREF int4 patch_offset[MAX_BT_DEPTH + 1]; +GBLREF block_id patch_left_sib,patch_right_sib,patch_find_blk; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF VSIG_ATOMIC_T util_interrupt; + +void dse_exhaus(int4 pp, int4 op) +{ + sm_uc_ptr_t bp, np, b_top, rp, r_top, nrp, nr_top, ptr; + char util_buff[MAX_UTIL_LEN]; + block_id last; + short temp_short; + int count, util_len; + int4 dummy_int; + cache_rec_ptr_t dummy_cr; + global_dir_path *d_ptr, *temp; + error_def(ERR_DSEBLKRDFAIL); + error_def(ERR_CTRLC); + + last = 0; + patch_path_count++; + if(!(bp = t_qread(patch_path[pp - 1],&dummy_int,&dummy_cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + if (((blk_hdr_ptr_t) bp)->bsiz > cs_addrs->hdr->blk_size) + b_top = bp + cs_addrs->hdr->blk_size; + else if (((blk_hdr_ptr_t) bp)->bsiz < SIZEOF(blk_hdr)) + b_top = bp + SIZEOF(blk_hdr); + else + b_top = bp + ((blk_hdr_ptr_t) bp)->bsiz; + for (rp = bp + SIZEOF(blk_hdr); rp < b_top ;rp = r_top) + { + if (util_interrupt) + { + rts_error(VARLSTCNT(1) ERR_CTRLC); + break; + } + if (!(np = t_qread(patch_path[pp - 1],&dummy_int,&dummy_cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + if (np != bp) + { + b_top = np + (b_top - bp); + rp = np + (rp - bp); + r_top = np + (r_top - bp); + bp = np; + } + GET_SHORT(temp_short,&((rec_hdr_ptr_t) rp)->rsiz); + r_top = rp + temp_short; + if (r_top > b_top) + r_top = b_top; + if (r_top - rp < SIZEOF(block_id)) + break; + if (((blk_hdr_ptr_t)bp)->levl) + GET_LONG(patch_path[pp],(r_top - SIZEOF(block_id))); + else + { + for (ptr = rp + SIZEOF(rec_hdr); ; ) + { + if (*ptr++ == 0 && *ptr++ ==0) + break; + } + GET_LONG(patch_path[pp],ptr); + } + patch_offset[op] = (int4)(rp - bp); + if (patch_path[pp] == patch_find_blk) + { + if (!patch_exh_found) + { + if (patch_find_sibs) + util_out_print("!/ Left siblings Right siblings",TRUE); + patch_exh_found = TRUE; + } + if (patch_find_sibs) + { + patch_left_sib = last; + if (r_top < b_top) + { + nrp = r_top; + GET_SHORT(temp_short,&((rec_hdr_ptr_t) rp)->rsiz); + nr_top = nrp + temp_short; + if (nr_top > b_top) + nr_top = b_top; + if (nr_top - nrp >= SIZEOF(block_id)) + { + if (((blk_hdr_ptr_t)bp)->levl) + GET_LONG(patch_right_sib,(nr_top - SIZEOF(block_id))); + else + { + for (ptr = rp + SIZEOF(rec_hdr); ;) + { + if (*ptr++ == 0 && *ptr++ == 0) + break; + } + GET_LONG(patch_right_sib,ptr); + } + } + } + else + patch_right_sib = 0; + if (patch_left_sib) + { + memcpy(util_buff," ",1); + util_len = 1; + util_len += i2hex_nofill(patch_left_sib,(uchar_ptr_t)&util_buff[util_len],8); + memcpy(&util_buff[util_len]," ",1); + util_len += 1; + util_buff[util_len] = 0; + util_out_print(util_buff,FALSE); + }else + util_out_print(" none ",FALSE); + if (patch_right_sib) + { + memcpy(util_buff," ",1); + util_len = 1; + util_len += i2hex_nofill(patch_right_sib,(uchar_ptr_t)&util_buff[util_len],8); + util_buff[util_len] = 0; + util_out_print(util_buff,FALSE); + } + else + util_out_print(" none",TRUE); + } + else /* !patch_find_sibs */ + { + patch_path_count--; + util_out_print(" Directory path!/ Path--blk:off",TRUE); + if (!patch_find_root_search) + { + d_ptr = global_roots_head->link->dir_path; + while(d_ptr) + { + memcpy(util_buff," ",1); + util_len = 1; + util_len += i2hex_nofill(d_ptr->block,(uchar_ptr_t)&util_buff[util_len],8); + memcpy(&util_buff[util_len],":",1); + util_len += 1; + util_len += i2hex_nofill(d_ptr->offset,(uchar_ptr_t)&util_buff[util_len],4); + util_buff[util_len] = 0; + util_out_print(util_buff,FALSE); + temp = d_ptr; + d_ptr = d_ptr->next; + free(temp); + } + global_roots_head->link->dir_path = 0; + util_out_print("!/!/ Global paths!/ Path--blk:off",TRUE); + } + for (count = 0; count < patch_path_count ;count++) + { + memcpy(util_buff," ",1); + util_len = 1; + util_len += i2hex_nofill(patch_path[count],(uchar_ptr_t)&util_buff[util_len],8); + memcpy(&util_buff[util_len],":",1); + util_len += 1; + util_len += i2hex_nofill(patch_offset[count],(uchar_ptr_t)&util_buff[util_len],4); + util_buff[util_len] = 0; + util_out_print(util_buff,FALSE); + } + memcpy(util_buff," ",1); + util_len = 1; + util_len += i2hex_nofill(patch_path[count],(uchar_ptr_t)&util_buff[util_len],8); + util_buff[util_len] = 0; + util_out_print(util_buff,TRUE); + patch_path_count++; + } + } + if (patch_path[pp] > 0 && patch_path[pp] < cs_addrs->ti->total_blks + && (patch_path[pp] % cs_addrs->hdr->bplmap)) + if (((blk_hdr_ptr_t) bp)->levl > 1) + dse_exhaus(pp + 1,op + 1); + else if (((blk_hdr_ptr_t)bp)->levl == 1 && patch_find_root_search) + dse_find_roots(patch_path[pp]); + last = patch_path[pp]; + } + patch_path_count--; + return; +} diff --git a/sr_port/dse_exit.c b/sr_port/dse_exit.c new file mode 100644 index 0000000..4e9edf5 --- /dev/null +++ b/sr_port/dse_exit.c @@ -0,0 +1,30 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdlib.h" /* for exit() */ + +#include "error.h" +#include "iosp.h" +#include "util.h" +#include "dse_exit.h" + +GBLREF unsigned int t_tries; + +void dse_exit(void) +{ + /* reset t_tries (from CDB_STAGNATE to 0) as we are exiting and no longer going to be running transactions + * and an assert in wcs_recover relies on this */ + t_tries = 0; + util_out_close(); + EXIT(SS_NORMAL); +} diff --git a/sr_port/dse_exit.h b/sr_port/dse_exit.h new file mode 100644 index 0000000..825cdf4 --- /dev/null +++ b/sr_port/dse_exit.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __DSE_EXIT_H__ +#define __DSE_EXIT_H__ + +void dse_exit(void); + +#endif diff --git a/sr_port/dse_f_blk.c b/sr_port/dse_f_blk.c new file mode 100644 index 0000000..ea79c5a --- /dev/null +++ b/sr_port/dse_f_blk.c @@ -0,0 +1,362 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include + +#include "error.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "dsefind.h" +#include "cli.h" +#include "copy.h" +#include "util.h" +#include "dse.h" + +/* Include prototypes*/ +#include "t_qread.h" + +GBLDEF global_root_list *global_roots_head, *global_roots_tail; +GBLDEF block_id patch_left_sib, patch_right_sib, patch_find_blk; +GBLDEF block_id patch_path[MAX_BT_DEPTH + 1]; +GBLDEF block_id patch_path1[MAX_BT_DEPTH + 1]; +GBLDEF int4 patch_offset[MAX_BT_DEPTH + 1]; +GBLDEF int4 patch_offset1[MAX_BT_DEPTH + 1]; +GBLDEF bool patch_find_sibs; +GBLDEF bool patch_exh_found; +GBLDEF bool patch_find_root_search; +GBLDEF short int patch_dir_path_count; + +GBLREF block_id patch_curr_blk; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gd_region *gv_cur_region; +GBLREF short int patch_path_count; + +#define MAX_UTIL_LEN 33 +static boolean_t was_crit; +static int4 nocrit_present; + +void dse_f_blk(void) +{ + global_root_list *temp; + global_dir_path *d_ptr, *dtemp; + block_id blk; + bool exhaust; + char targ_key[256], util_buff[MAX_UTIL_LEN]; + sm_uc_ptr_t bp, b_top, rp, r_top, key_top, blk_id; + short int size, count, rsize; + int util_len; + int4 dummy_int; + cache_rec_ptr_t dummy_cr; + + error_def(ERR_DSEBLKRDFAIL); + error_def(ERR_CTRLC); + + if (cli_present("BLOCK") == CLI_PRESENT) + { + if(!cli_get_hex("BLOCK", (uint4 *)&blk)) + return; + if (blk < 0 || blk >= cs_addrs->ti->total_blks + || !(blk % cs_addrs->hdr->bplmap)) + { + util_out_print("Error: invalid block number.", TRUE); + return; + } + patch_curr_blk = blk; + } + patch_find_sibs = (cli_present("SIBLINGS") == CLI_PRESENT); + patch_find_blk = patch_curr_blk; + was_crit = cs_addrs->now_crit; + nocrit_present = (CLI_NEGATED == cli_present("CRIT")); + DSE_GRAB_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + /* ESTABLISH is done here because dse_f_blk_ch() assumes we already + * have crit. + */ + ESTABLISH(dse_f_blk_ch); + + if(!(bp = t_qread(patch_find_blk, &dummy_int, &dummy_cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + if (((blk_hdr_ptr_t) bp)->bsiz > cs_addrs->hdr->blk_size) + b_top = bp + cs_addrs->hdr->blk_size; + else if (((blk_hdr_ptr_t) bp)->bsiz < SIZEOF(blk_hdr)) + b_top = bp + SIZEOF(blk_hdr); + else + b_top = bp + ((blk_hdr_ptr_t) bp)->bsiz; + rp = bp + SIZEOF(blk_hdr); + GET_SHORT(rsize, &((rec_hdr_ptr_t) rp)->rsiz); + if (rsize < SIZEOF(rec_hdr)) + r_top = rp + SIZEOF(rec_hdr); + else + r_top = rp + rsize; + if (r_top > b_top) + r_top = b_top; + for (key_top = rp + SIZEOF(rec_hdr); key_top < r_top ; ) + { + if (!*key_top++) + break; + } + if (((blk_hdr_ptr_t)bp)->levl && key_top > (blk_id = r_top - SIZEOF(block_id))) + key_top = blk_id; + patch_path_count = 1; + patch_path[0] = get_dir_root(); + patch_left_sib = patch_right_sib = 0; + size = key_top - rp - SIZEOF(rec_hdr); + if (size > SIZEOF(targ_key)) + size = SIZEOF(targ_key); + patch_find_root_search = TRUE; + if ((exhaust = (cli_present("EXHAUSTIVE") == CLI_PRESENT)) || size <= 0) + { + if (size < 0) + { + util_out_print("No keys in block, cannot perform ordered search.", TRUE); + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + REVERT; + return; + } + if (patch_exh_found = (patch_find_blk == patch_path[0])) + { + if (patch_find_sibs) + util_out_print("!/ Left siblings Right siblings!/ none none", TRUE); + else + { + memcpy(util_buff, "!/ Paths--blk:off!/ ", 24); + util_len = 24; + util_len += i2hex_nofill(patch_find_blk, (uchar_ptr_t)&util_buff[util_len], + 8); + util_buff[util_len] = 0; + util_out_print(util_buff, TRUE); + } + } + else + { + global_roots_head = (global_root_list *)malloc(SIZEOF(global_root_list)); + global_roots_tail = global_roots_head; + global_roots_head->link = (global_root_list *)0; + global_roots_head->dir_path = (global_dir_path *)0; + dse_exhaus(1, 0); + patch_find_root_search = FALSE; + while (!patch_exh_found && global_roots_head->link) + { + patch_path[0] = global_roots_head->link->root; + patch_path_count = 1; + patch_left_sib = patch_right_sib = 0; + if (patch_exh_found = (patch_find_blk == patch_path[0])) + { + if (patch_find_sibs) + util_out_print("!/ Left siblings Right siblings!/ none none", TRUE); + else + { + patch_path_count--; + util_out_print(" Directory path!/ Path--blk:off", TRUE); + if (!patch_find_root_search) + { + d_ptr = global_roots_head->link->dir_path; + while(d_ptr) + { + memcpy(util_buff, " ", 1); + util_len = 1; + util_len += i2hex_nofill(d_ptr->block, + (uchar_ptr_t)&util_buff[util_len], + 8); + memcpy(&util_buff[util_len], ":", 1); + util_len += 1; + util_len += i2hex_nofill(d_ptr->offset, + (uchar_ptr_t)&util_buff[util_len], + 4); + memcpy(&util_buff[util_len], ",", 1); + util_len += 1; + util_buff[util_len] = 0; + util_out_print(util_buff, FALSE); + temp = (global_root_list *)d_ptr; + d_ptr = d_ptr->next; + free(temp); + } + global_roots_head->link->dir_path = 0; + util_out_print("!/!/ Global paths!/ Path--blk:off", TRUE); + } + for (count = 0; count < patch_path_count ;count++) + { + memcpy(util_buff, " ", 1); + util_len = 1; + util_len += i2hex_nofill(patch_path[count], + (uchar_ptr_t)&util_buff[util_len], + 8); + memcpy(&util_buff[util_len], ":", 1); + util_len += 1; + util_len += i2hex_nofill(patch_offset[count], (uchar_ptr_t)&util_buff[util_len], 4); + memcpy(&util_buff[util_len], ",", 1); + util_len += 1; + util_buff[util_len] = 0; + util_out_print(util_buff, FALSE); + } + memcpy(util_buff, " ", 1); + util_len = 1; + util_len += i2hex_nofill(patch_path[count], + (uchar_ptr_t)&util_buff[util_len], 8); + util_buff[util_len] = 0; + util_out_print(util_buff, TRUE); + } + } + else + dse_exhaus(1, 0); + temp = global_roots_head; + d_ptr = global_roots_head->link->dir_path; + while(d_ptr) + { + dtemp = d_ptr; + d_ptr = d_ptr->next; + free(dtemp); + } + global_roots_head = global_roots_head->link; + free(temp); + } + while (global_roots_head->link) + { + temp = global_roots_head; + d_ptr = global_roots_head->link->dir_path; + while(d_ptr) + { + dtemp = d_ptr; + d_ptr = d_ptr->next; + free(dtemp); + } + global_roots_head = global_roots_head->link; + free(temp); + } + } + if (!patch_exh_found) + { + if (exhaust) + { + util_out_print("Error: exhaustive search fail.", TRUE); + } + else + { + util_out_print("Error: ordered search fail.", TRUE); + } + } + else + util_out_print(0, TRUE); + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + REVERT; + return; + } + else /* !exhaust && size > 0 */ + { + if (!dse_is_blk_in(rp, r_top, size)) + { + util_out_print("Error: ordered search fail.", TRUE); + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + REVERT; + return; + } + } + if (patch_find_sibs) + { + util_out_print("!/!_Left sibling!_Right sibling", TRUE); + if (patch_left_sib) + { + memcpy(util_buff, "!_", 2); + util_len = 2; + util_len += i2hex_nofill(patch_left_sib, (uchar_ptr_t)&util_buff[util_len], 8); + util_buff[util_len] = 0; + util_out_print(util_buff, FALSE); + } + else + util_out_print("!_none", FALSE); + if (patch_right_sib) + { + memcpy(util_buff, "!_!_", 4); + util_len = 4; + util_len += i2hex_nofill(patch_right_sib, (uchar_ptr_t)&util_buff[util_len], 8); + memcpy(&util_buff[util_len], "!/", 2); + util_len += 2; + util_buff[util_len] = 0; + util_out_print(util_buff, TRUE); + } else + util_out_print("!_!_none!/", TRUE); + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + REVERT; + return; + } + util_out_print("!/ Directory path!/ Path--blk:off", TRUE); + patch_dir_path_count--; + for (count = 0; count < patch_dir_path_count ;count++) + { + memcpy(util_buff, " ", 1); + util_len = 1; + util_len += i2hex_nofill(patch_path[count], (uchar_ptr_t)&util_buff[util_len], 8); + memcpy(&util_buff[util_len], ":", 1); + util_len += 1; + util_len += i2hex_nofill(patch_offset[count], (uchar_ptr_t)&util_buff[util_len], 4); + memcpy(&util_buff[util_len], ",", 1); + util_len += 1; + util_buff[util_len] = 0; + util_out_print(util_buff, FALSE); + } + util_out_print("!/ Global tree path!/ Path--blk:off", TRUE); + if (patch_path_count) + { + patch_path_count--; + for (count = 0; count < patch_path_count ;count++) + { + memcpy(util_buff, " ", 1); + util_len = 1; + util_len += i2hex_nofill(patch_path1[count], (uchar_ptr_t)&util_buff[util_len], 8); + memcpy(&util_buff[util_len], ":", 1); + util_len += 1; + util_len += i2hex_nofill(patch_offset1[count], (uchar_ptr_t)&util_buff[util_len], 4); + memcpy(&util_buff[util_len], ",", 1); + util_len += 1; + util_buff[util_len] = 0; + util_out_print(util_buff, FALSE); + } + memcpy(util_buff, " ", 1); + util_len = 1; + util_len += i2hex_nofill(patch_path1[count], (uchar_ptr_t)&util_buff[util_len], 8); + memcpy(&util_buff[util_len], "!/", 2); + util_len += 2; + util_buff[util_len] = 0; + util_out_print(util_buff, TRUE); + } + else + { + memcpy(util_buff, " ", 1); + util_len = 1; + util_len += i2hex_nofill(patch_path[count], (uchar_ptr_t)&util_buff[util_len], 8); + memcpy(&util_buff[util_len], "!/", 2); + util_len += 2; + util_buff[util_len] = 0; + util_out_print(util_buff, TRUE); + } + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + REVERT; + return; +} + +/* Control-C condition handler */ +CONDITION_HANDLER(dse_f_blk_ch) +{ + error_def(ERR_CTRLC); + START_CH; + + if (SIGNAL == ERR_CTRLC) + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + NEXTCH; +} + diff --git a/sr_port/dse_f_free.c b/sr_port/dse_f_free.c new file mode 100644 index 0000000..08bfa50 --- /dev/null +++ b/sr_port/dse_f_free.c @@ -0,0 +1,103 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "cli.h" +#include "util.h" +#include "gdsbml.h" +#include "bmm_find_free.h" +#include "dse.h" + +/* Include prototypes */ +#include "t_qread.h" + +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gd_region *gv_cur_region; + +#define MAX_UTIL_LEN 80 + +void dse_f_free(void) +{ + block_id blk; + bool in_last_bmap; + char util_buff[MAX_UTIL_LEN]; + sm_uc_ptr_t lmap_base; + int4 bplmap, total_blks; + int4 util_len, master_bit, lmap_bit, hint_over_bplmap, hint_mod_bplmap; + boolean_t was_crit; + int4 dummy_int, nocrit_present; + cache_rec_ptr_t dummy_cr; + error_def(ERR_DSEBLKRDFAIL); + + if (cs_addrs->hdr->bplmap == 0) + { util_out_print("Cannot perform free block search: bplmap field of file header is zero.", TRUE); + return; + } + bplmap = cs_addrs->hdr->bplmap; + + if(!cli_get_hex("HINT", (uint4 *)&blk)) + return; + if (blk < 0 || blk >= cs_addrs->ti->total_blks || (blk / bplmap * bplmap == blk)) + { util_out_print("Error: invalid block number.", TRUE); + return; + } + hint_over_bplmap = blk / bplmap; + master_bit = bmm_find_free(hint_over_bplmap, cs_addrs->bmm, + (cs_addrs->ti->total_blks + bplmap - 1)/ bplmap); + if (master_bit == -1) + { util_out_print("Error: database full.", TRUE); + return; + } + in_last_bmap = (master_bit == (cs_addrs->ti->total_blks / bplmap)); + was_crit = cs_addrs->now_crit; + nocrit_present = (CLI_NEGATED == cli_present("CRIT")); + DSE_GRAB_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + if(!(lmap_base = t_qread(master_bit * bplmap, &dummy_int, &dummy_cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + if (master_bit == hint_over_bplmap) + hint_mod_bplmap = blk - blk / bplmap * bplmap; + else + hint_mod_bplmap = 0; + if (in_last_bmap) + total_blks = (cs_addrs->ti->total_blks - master_bit); + else + total_blks = bplmap; + lmap_bit = bml_find_free(hint_mod_bplmap, lmap_base + SIZEOF(blk_hdr), total_blks); + if (lmap_bit == -1) + { memcpy(util_buff, "Error: bit map in block ", 24); + util_len = 24; + util_len += i2hex_nofill(master_bit * bplmap, (uchar_ptr_t)&util_buff[util_len], 8); + memcpy(&util_buff[util_len], " incorrectly marked free in master map.", 39); + util_len += 39; + util_buff[util_len] = 0; + util_out_print(util_buff, TRUE); + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + return; + } + memcpy(util_buff, "!/Next free block is ", 21); + util_len = 21; + util_len += i2hex_nofill(master_bit * bplmap + lmap_bit, (uchar_ptr_t)&util_buff[util_len], 8); + memcpy(&util_buff[util_len], ".!/", 3); + util_len += 3; + util_buff[util_len] = 0; + util_out_print(util_buff, TRUE); + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + return; +} diff --git a/sr_port/dse_f_key.c b/sr_port/dse_f_key.c new file mode 100644 index 0000000..ffb44e2 --- /dev/null +++ b/sr_port/dse_f_key.c @@ -0,0 +1,122 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "cli.h" +#include "util.h" +#include "dse.h" + +GBLREF short int patch_path_count; +GBLREF block_id ksrch_root; +GBLREF bool patch_find_root_search; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gd_region *gv_cur_region; + +#define MAX_UTIL_LEN 64 + +void dse_f_key(void) +{ + block_id path[MAX_BT_DEPTH + 1], root_path[MAX_BT_DEPTH + 1]; + int4 offset[MAX_BT_DEPTH + 1], root_offset[MAX_BT_DEPTH + 1], nocrit_present; + char targ_key[MAX_KEY_SZ + 1], targ_key_root[MAX_KEY_SZ + 1], *key_top, util_buff[MAX_UTIL_LEN]; + int size, size_root, root_path_count, count, util_len; + bool found, was_crit; + + if (!dse_getki(&targ_key[0],&size,LIT_AND_LEN("KEY"))) + return; + patch_path_count = 1; + root_path[0] = get_dir_root(); + for (key_top = &targ_key[0]; key_top < ARRAYTOP(targ_key); ) + if (!*key_top++) + break; + size_root = (int)(key_top - &targ_key[0] + 1); + memcpy(&targ_key_root[0],&targ_key[0],size_root); + targ_key_root[size_root - 1] = targ_key_root[size_root] = 0; + patch_find_root_search = TRUE; + was_crit = cs_addrs->now_crit; + nocrit_present = (CLI_NEGATED == cli_present("CRIT")); + DSE_GRAB_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + if (!dse_ksrch(root_path[0],&root_path[1],&root_offset[0],&targ_key_root[0],size_root)) + { + util_out_print("!/Key not found, no root present.!/",TRUE); + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + return; + } + root_path_count = patch_path_count; + patch_path_count = 1; + path[0] = ksrch_root; + patch_find_root_search = FALSE; + if (!dse_ksrch(path[0],&path[1],&offset[0],&targ_key[0],size)) + { memcpy(util_buff,"!/Key not found, would be in block ",36); + util_len = 36; + util_len += i2hex_nofill(path[patch_path_count - 2], (uchar_ptr_t)&util_buff[util_len], 8); + memcpy(&util_buff[util_len], ".",1); + util_len += 1; + util_buff[util_len] = 0; + util_out_print(util_buff,FALSE); + patch_path_count -= 1; + }else + { memcpy(util_buff,"!/Key found in block ",22); + util_len = 22; + util_len += i2hex_nofill(path[patch_path_count - 1], (uchar_ptr_t)&util_buff[util_len], 8); + memcpy(&util_buff[util_len], ".",1); + util_len += 1; + util_buff[util_len] = 0; + util_out_print(util_buff,FALSE); + } + util_out_print("!/ Directory path!/ Path--blk:off",TRUE); + for (count = 0; count < root_path_count ;count++) + { memcpy(util_buff," ",1); + util_len = 1; + util_len += i2hex_nofill(root_path[count],(uchar_ptr_t)&util_buff[util_len], 8); + memcpy(&util_buff[util_len],":",1); + util_len += 1; + util_len += i2hex_nofill(root_offset[count],(uchar_ptr_t)&util_buff[util_len], 4); + memcpy(&util_buff[util_len],",",1); + util_len += 1; + util_buff[util_len] = 0; + util_out_print(util_buff,FALSE); + } + util_out_print("!/ Global tree path!/ Path--blk:off",TRUE); + if (patch_path_count) + { for (count = 0; count < patch_path_count ;count++) + { memcpy(util_buff," ",1); + util_len = 1; + util_len += i2hex_nofill(path[count],(uchar_ptr_t)&util_buff[util_len], 8); + memcpy(&util_buff[util_len],":",1); + util_len += 1; + util_len += i2hex_nofill(offset[count],(uchar_ptr_t)&util_buff[util_len], 4); + memcpy(&util_buff[util_len],",",1); + util_len += 1; + util_buff[util_len] = 0; + util_out_print(util_buff,FALSE); + } + util_out_print(0,TRUE); + } else + { memcpy(util_buff," ",1); + util_len = 1; + util_len += i2hex_nofill(root_path[count],(uchar_ptr_t)&util_buff[util_len], 8); + memcpy(&util_buff[util_len],"!/",2); + util_len += 2; + util_buff[util_len] = 0; + util_out_print(util_buff,TRUE); + } + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + return; +} diff --git a/sr_port/dse_f_reg.c b/sr_port/dse_f_reg.c new file mode 100644 index 0000000..2b52473 --- /dev/null +++ b/sr_port/dse_f_reg.c @@ -0,0 +1,129 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "min_max.h" /* needed for init_root_gv.h */ +#include "init_root_gv.h" +#include "util.h" +#include "cli.h" +#include "dse.h" + +GBLREF block_id patch_curr_blk; +GBLREF gd_region *gv_cur_region; +GBLREF gd_addr *gd_header; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF short crash_count; +GBLREF mval dollar_zgbldir; +GBLREF gd_addr *original_header; + +void dse_f_reg(void) +{ + char rn[MAX_RN_LEN]; + unsigned short rnlen; + int i; + bool found; + gd_region *ptr; + gd_addr *temp_gdaddr; + gd_binding *map; + + temp_gdaddr = gd_header; + gd_header = original_header; + rnlen = SIZEOF(rn); + if (!cli_get_str("REGION",rn,&rnlen)) + { + gd_header = temp_gdaddr; + return; + } + if (rn[0] == '*' && rnlen == 1) + { + util_out_print("List of global directory:!_!AD!/",TRUE,dollar_zgbldir.str.len,dollar_zgbldir.str.addr); + for (i=0, ptr = gd_header->regions; i < gd_header->n_regions ;i++, ptr++) + { util_out_print("!/File !_!AD",TRUE, ptr->dyn.addr->fname_len,&ptr->dyn.addr->fname[0]); + util_out_print("Region!_!AD",TRUE, REG_LEN_STR(ptr)); + } + gd_header = temp_gdaddr; + return; + } + assert (rn[0]); + + found = FALSE; + for (i=0, ptr = gd_header->regions; i < gd_header->n_regions ;i++, ptr++) + if (found = !memcmp(&ptr->rname[0],&rn[0],MAX_RN_LEN)) + break; + if (!found) + { + util_out_print("Error: region not found.",TRUE); + gd_header = temp_gdaddr; + return; + } + + if (ptr == gv_cur_region) + { + util_out_print("Error: already in region: !AD",TRUE,REG_LEN_STR(gv_cur_region)); + gd_header = temp_gdaddr; + return; + } + if (ptr->dyn.addr->acc_meth == dba_cm) + { + util_out_print("Error: Cannot edit an GT.CM database file.",TRUE); + gd_header = temp_gdaddr; + return; + } + if (ptr->dyn.addr->acc_meth == dba_usr) + { + util_out_print("Error: Cannot edit a non-GDS format database file.",TRUE); + gd_header = temp_gdaddr; + return; + } + if (!ptr->open) + { + util_out_print("Error: that region was not opened because it is not bound to any namespace.",TRUE); + gd_header = temp_gdaddr; + return; + } + + if (cs_addrs->now_crit == TRUE) + util_out_print("Warning: now leaving region in critical section: !AD",TRUE, gv_cur_region->rname_len, + gv_cur_region->rname); + + gv_cur_region = ptr; + switch (gv_cur_region->dyn.addr->acc_meth) + { + case dba_mm: + case dba_bg: + cs_addrs = &FILE_INFO(gv_cur_region)->s_addrs; + cs_data = cs_addrs->hdr; + break; + default: + GTMASSERT; + } + + if (cs_addrs && cs_addrs->critical) + { crash_count = cs_addrs->critical->crashcnt; + } + util_out_print("!/File !_!AD",TRUE, DB_LEN_STR(gv_cur_region)); + util_out_print("Region!_!AD!/",TRUE, REG_LEN_STR(gv_cur_region)); + + patch_curr_blk = get_dir_root(); + gv_init_reg(gv_cur_region); + GET_SAVED_GDADDR(gd_header, temp_gdaddr, map, gv_cur_region); + return; +} diff --git a/sr_port/dse_fdmp.c b/sr_port/dse_fdmp.c new file mode 100644 index 0000000..19252eb --- /dev/null +++ b/sr_port/dse_fdmp.c @@ -0,0 +1,93 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_string.h" + +#include "mlkdef.h" +#include "gdsroot.h" +#include "gdsbt.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsfhead.h" +#include "gtmctype.h" +#include "cli.h" +#include "dse.h" +#include "gvsub2str.h" +#include "zshow.h" + +GBLREF enum dse_fmt dse_dmp_format; +GBLREF gd_region *gv_cur_region; +GBLREF char patch_comp_key[MAX_KEY_SZ + 1]; + +static unsigned char *work_buff; +static unsigned int work_buff_length; + +boolean_t dse_fdmp(sm_uc_ptr_t data, int len) +{ + unsigned char *key_char_ptr, *work_char_ptr; + int dest_len; + + if (work_buff_length < ZWR_EXP_RATIO(gv_cur_region->max_rec_size)) + { + work_buff_length = ZWR_EXP_RATIO(gv_cur_region->max_rec_size); + if (work_buff) + free (work_buff); + work_buff = (unsigned char *)malloc(work_buff_length); + } + work_char_ptr = work_buff; + *work_char_ptr++ = '^'; + for (key_char_ptr = (uchar_ptr_t)patch_comp_key; *key_char_ptr ; key_char_ptr++) + { + if (PRINTABLE(*key_char_ptr)) + *work_char_ptr++ = *key_char_ptr; + else + return FALSE; + } + key_char_ptr++; + if (*key_char_ptr) + { + *work_char_ptr++ = '('; + for (;;) + { + work_char_ptr = gvsub2str(key_char_ptr, work_char_ptr, TRUE); + /* Removed unnecessary checks for printable characters (PRINTABLE()) here + * since the data being written into files (OPENed files) would have been + * passed through ZWR translation which would have taken care of converting + * to $CHAR() or $ZCHAR() */ + + for (; *key_char_ptr ; key_char_ptr++) + ; + key_char_ptr++; + if (*key_char_ptr) + *work_char_ptr++ = ','; + else + break; + } + *work_char_ptr++ = ')'; + } + assert(MAX_ZWR_KEY_SZ >= work_char_ptr - work_buff); + if (GLO_FMT == dse_dmp_format) + { + if (!dse_fdmp_output(work_buff, (int4)(work_char_ptr - work_buff))) + return FALSE; + if (!dse_fdmp_output(data, len)) + return FALSE; + } else + { + assert(ZWR_FMT == dse_dmp_format); + *work_char_ptr++ = '='; + format2zwr(data, len, work_char_ptr, &dest_len); + if (!dse_fdmp_output(work_buff, (int4)(work_char_ptr + dest_len - work_buff))) + return FALSE; + } + return TRUE; +} diff --git a/sr_port/dse_find_roots.c b/sr_port/dse_find_roots.c new file mode 100644 index 0000000..82b2750 --- /dev/null +++ b/sr_port/dse_find_roots.c @@ -0,0 +1,80 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "dsefind.h" +#include "copy.h" +#include "dse.h" + +/* Include prototypes */ +#include "t_qread.h" + +GBLREF global_root_list *global_roots_tail; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF block_id patch_path[MAX_BT_DEPTH + 1]; +GBLREF int4 patch_offset[MAX_BT_DEPTH + 1]; +GBLREF short int patch_path_count; + +void dse_find_roots(block_id index) +{ + int count; + int4 dummy_int; + cache_rec_ptr_t dummy_cr; + short temp_short; + sm_uc_ptr_t bp, b_top, rp, r_top, key_top; + global_dir_path *d_ptr; + error_def(ERR_DSEBLKRDFAIL); + + if(!(bp = t_qread(index,&dummy_int,&dummy_cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + if (((blk_hdr_ptr_t) bp)->bsiz > cs_addrs->hdr->blk_size) + b_top = bp + cs_addrs->hdr->blk_size; + else if (((blk_hdr_ptr_t) bp)->bsiz < SIZEOF(blk_hdr)) + b_top = bp + SIZEOF(blk_hdr); + else + b_top = bp + ((blk_hdr_ptr_t) bp)->bsiz; + for (rp = bp + SIZEOF(blk_hdr); rp < b_top ;rp = r_top) + { GET_SHORT(temp_short,&((rec_hdr_ptr_t)rp)->rsiz); + r_top = rp + temp_short; + if (r_top > b_top) + r_top = b_top; + if (r_top - rp < SIZEOF(block_id)) + break; + global_roots_tail->link = (global_root_list *)malloc(SIZEOF(global_root_list)); + global_roots_tail = global_roots_tail->link; + global_roots_tail->link = 0; + for (key_top = rp + SIZEOF(rec_hdr); key_top < r_top; ) + if(!*key_top++ && !*key_top++) + break; + GET_LONG(global_roots_tail->root,key_top); + global_roots_tail->dir_path = (global_dir_path *)malloc(SIZEOF(global_dir_path)); + d_ptr = global_roots_tail->dir_path; + for (count = 0; ; count++) + { d_ptr->block = patch_path[count]; + d_ptr->offset = patch_offset[count]; + if (count < patch_path_count - 1) + d_ptr->next = (global_dir_path *)malloc(SIZEOF(global_dir_path)); + else + { d_ptr->next = 0; + d_ptr->offset = (int4)(rp - bp); + break; + } + d_ptr = d_ptr->next; + } + } + return; +} diff --git a/sr_port/dse_flush.c b/sr_port/dse_flush.c new file mode 100644 index 0000000..3d80170 --- /dev/null +++ b/sr_port/dse_flush.c @@ -0,0 +1,57 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" /* needed for jnl.h */ +#include "jnl.h" /* needed for the WCSFLU_* macros */ +#include "wcs_flu.h" +#include "dse.h" + +GBLREF gd_region *gv_cur_region; +GBLREF short crash_count; +GBLREF sgmnt_addrs *cs_addrs; + +void dse_flush(void) +{ + boolean_t was_crit; + + error_def(ERR_DSEONLYBGMM); + error_def(ERR_DBRDONLY); + + if (gv_cur_region->read_only) + rts_error(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); + + switch (gv_cur_region->dyn.addr->acc_meth) + { + case dba_bg: + case dba_mm: + if (cs_addrs->critical) + crash_count = cs_addrs->critical->crashcnt; + was_crit = cs_addrs->now_crit; + if (!was_crit) + grab_crit(gv_cur_region); + wcs_flu(WCSFLU_FLUSH_HDR | WCSFLU_WRITE_EPOCH | WCSFLU_SYNC_EPOCH); + if (!was_crit) + rel_crit(gv_cur_region); + break; + default: + rts_error(VARLSTCNT(4) ERR_DSEONLYBGMM, 2, LEN_AND_LIT("BUFFER_FLUSH")); + break; + } + return; +} diff --git a/sr_port/dse_getki.c b/sr_port/dse_getki.c new file mode 100644 index 0000000..2b916eb --- /dev/null +++ b/sr_port/dse_getki.c @@ -0,0 +1,255 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gtm_strings.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_ctype.h" +#include "gtm_facility.h" +#include "gtm_stdlib.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "min_max.h" /* needed for init_root_gv.h */ +#include "init_root_gv.h" +#include "util.h" +#include "cli.h" +#include "stringpool.h" +#include "dse.h" +#include "mvalconv.h" +#include "op.h" +#include "format_targ_key.h" + +#ifdef GTM_TRIGGER +#include "hashtab_mname.h" +#include "rtnhdr.h" +#include "gv_trigger.h" /* needed for INIT_ROOT_GVT */ +#include "targ_alloc.h" +#endif +#ifdef UNICODE_SUPPORTED +#include "gtm_utf8.h" +#endif + +GBLREF gv_key *gv_currkey; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gd_region *gv_cur_region; +GBLREF mval curr_gbl_root; +GBLREF gv_namehead *gv_target; + +LITREF mval literal_hasht; + +int dse_getki(char *dst, int *len, char *qual, int qual_len) +{ + char buf[MAX_ZWR_KEY_SZ], *src, *temp_dst, *bot, *top, *tmp, slit[MAX_KEY_SZ + 1], key_buf[MAX_KEY_SZ + 1]; + short int max_key; + unsigned short buf_len; + int key_len, dlr_num, dlr_len; + mval key_subsc; + sgmnt_addrs *csa; + + buf_len = SIZEOF(buf); + if (!cli_get_str(qual, buf, &buf_len)) + return FALSE; + bot = temp_dst = (char *)&key_buf[0]; + top = &buf[buf_len]; + src = &buf[0]; + if (*src++ != '^') + { + util_out_print("Error: invalid key.", TRUE); + return FALSE; + } + if ((*src >= 'A' && *src <= 'Z') || + (*src >= 'a' && *src <= 'z') || + (*src == '%')) /* first letter must be an alphabet or % */ + { + *temp_dst++ = *src++; + } else + { + util_out_print("Error: invalid key.", TRUE); + return FALSE; + } + for ( ; *src != '(' && src < top ;src++) + { + if ((*src >= 'A' && *src <= 'Z') || + (*src >= 'a' && *src <= 'z') || + (*src >= '0' && *src <= '9')) + *temp_dst = *src; + else + { + util_out_print("Error: invalid key.", TRUE); + return FALSE; + } + temp_dst++; + } + *temp_dst = '\0'; + + csa = cs_addrs; + key_len = (int )(temp_dst - bot); + INIT_ROOT_GVT(bot, key_len, curr_gbl_root); + bot = (char *)&gv_currkey->base[0]; + temp_dst = (char *)&gv_currkey->base[0] + gv_currkey->end; + max_key = gv_cur_region->max_key_size; + if (*src == '(') + { + src++; + for (;;) + { + key_subsc.mvtype = MV_STR; + if (*src == '$') /* may be a $char() */ + { + src++; + if ((dlr_len = parse_dlr_char(src, top, slit)) > 0) + { + key_subsc.str.addr = slit; + key_subsc.str.len = STRLEN(slit); + src += dlr_len; + } else + { + util_out_print("Error: invalid key.", TRUE); + return FALSE; + } + } else if (*src != '\"') /* numerical subscript */ + { + for (key_subsc.str.addr = src ; *src != ')' && *src != ','; src++) + { + if (src == top || (*src < '0' || *src > '9') && *src != '-' && *src != '.') + { + util_out_print("Error: invalid key.", TRUE); + return FALSE; + } + } + key_subsc.str.len = INTCAST(src - key_subsc.str.addr); + s2n(&key_subsc); + key_subsc.mvtype &= MV_NUM_MASK; + } else + { + src++; + tmp = slit; + for (;;) + { + if (src == top) + { + util_out_print("Error: invalid key.", TRUE); + return FALSE; + } + if (*src == '\"') + if (*++src != '\"') + break; + *tmp++ = *src++; + } + key_subsc.str.addr = slit; + key_subsc.str.len = INTCAST(tmp - slit); + } + if ( 0 == key_subsc.str.len && NEVER == cs_addrs->hdr->null_subs) + { + util_out_print("Error: Null subscripts not allowed", TRUE); + return FALSE; + } + mval2subsc(&key_subsc, gv_currkey); + if (gv_currkey->end >= max_key) + ISSUE_GVSUBOFLOW_ERROR(gv_currkey); + if (*src != ',') + break; + src++; + } + if (*src++ != ')') + { + util_out_print("Error: invalid key.", TRUE); + return FALSE; + } + temp_dst = (char *)&gv_currkey->base[0] + gv_currkey->end; + } + if (src != top) + { + util_out_print("Error: invalid key.", TRUE); + return FALSE; + } + *len = (int)(temp_dst - bot + 1); + memcpy(dst, &gv_currkey->base[0], *len); + return TRUE; +} + +int parse_dlr_char(char *src, char *top, char *dlr_subsc) +{ + int indx = 0, dlr_len, dlr_val, harlen; + char lcl_buf[MAX_KEY_SZ + 1]; + char *tmp_buf, *strnext; + boolean_t dlrzchar = FALSE; + + tmp_buf = src; + + if ('Z' == TOUPPER(*tmp_buf)) + { + dlrzchar = TRUE; + tmp_buf++; + } + if ('C' != TOUPPER(*tmp_buf++)) + return 0; + if ('H' == TOUPPER(*tmp_buf)) + { + if (top - tmp_buf <= STR_LIT_LEN("har") || STRNCASECMP(tmp_buf, "har", STR_LIT_LEN("har"))) + { + if (!dlrzchar) + return 0; + tmp_buf++; + } + else + tmp_buf += STR_LIT_LEN("har"); + + } else if (dlrzchar) + return 0; + + if (*tmp_buf++ != '(') + return 0; + if (!ISDIGIT_ASCII(*tmp_buf)) + return 0; + while (tmp_buf != top) + { + if (ISDIGIT_ASCII(*tmp_buf)) + lcl_buf[indx++] = *tmp_buf; + else if (',' == *tmp_buf || ')' == *tmp_buf) + { + lcl_buf[indx] = '\0'; + dlr_val = ATOI(lcl_buf); + if (0 > dlr_val) + return 0; + if (!gtm_utf8_mode || dlrzchar) + { + if (255 < dlr_val) + return 0; + *dlr_subsc++ = dlr_val; + } +#ifdef UNICODE_SUPPORTED + else { + strnext = (char *)UTF8_WCTOMB(dlr_val, dlr_subsc); + if (strnext == dlr_subsc) + return 0; + dlr_subsc = strnext; + } +#endif + indx = 0; + if (')' == *tmp_buf) + { + *dlr_subsc = '\0'; + break; + } + } else + return 0; + tmp_buf++; + } + if (tmp_buf == top) + return 0; + tmp_buf++; + return (int)(tmp_buf - src); +} diff --git a/sr_port/dse_integ.c b/sr_port/dse_integ.c new file mode 100644 index 0000000..8346bba --- /dev/null +++ b/sr_port/dse_integ.c @@ -0,0 +1,77 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsfhead.h" +#include "cli.h" +#include "util.h" +#include "dse.h" + +/* Include prototypes */ +#include "t_qread.h" +#include "cert_blk.h" + +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gd_region *gv_cur_region; +GBLREF block_id patch_curr_blk; + +#define MAX_UTIL_LEN 40 + +void dse_integ(void) +{ + block_id blk; + char util_buff[MAX_UTIL_LEN]; + sm_uc_ptr_t bp; + int4 dummy_int, nocrit_present; + cache_rec_ptr_t dummy_cr; + int util_len; + boolean_t was_crit; + + error_def(ERR_DSEBLKRDFAIL); + + if (CLI_PRESENT == cli_present("BLOCK")) + { + if (!cli_get_hex("BLOCK", (uint4 *)&blk)) + return; + if (blk < 0 || blk >= cs_addrs->ti->total_blks) + { + util_out_print("Error: invalid block number.", TRUE); + return; + } + patch_curr_blk = blk; + } + memcpy(util_buff, "!/Checking integrity of block ", 30); + util_len = 30; + util_len += i2hex_nofill(patch_curr_blk, (uchar_ptr_t)&util_buff[util_len], 8); + memcpy(&util_buff[util_len], ":", 1); + util_len += 1; + util_buff[util_len] = 0; + util_out_print(util_buff, TRUE); + was_crit = cs_addrs->now_crit; + nocrit_present = (CLI_NEGATED == cli_present("CRIT")); + DSE_GRAB_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + if (!(bp = t_qread(patch_curr_blk, &dummy_int, &dummy_cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + if (TRUE == cert_blk(gv_cur_region, patch_curr_blk, (blk_hdr_ptr_t)bp, 0, FALSE)) + util_out_print("!/ No errors detected.!/", TRUE); + else + util_out_print(NULL, TRUE); + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + return; +} diff --git a/sr_port/dse_is_blk_free.c b/sr_port/dse_is_blk_free.c new file mode 100644 index 0000000..617ed1e --- /dev/null +++ b/sr_port/dse_is_blk_free.c @@ -0,0 +1,47 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gdsbml.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "dse.h" + +/* Include prototypes */ +#include "dse_is_blk_free.h" +#include "t_qread.h" + +GBLREF sgmnt_addrs *cs_addrs; + +bool dse_is_blk_free (block_id blk, sm_int_ptr_t cycle, cache_rec_ptr_ptr_t cr) +{ + uint4 status; + uint4 index, offset; + sm_uc_ptr_t bp; + error_def(ERR_DSEBLKRDFAIL); + + index = (blk / cs_addrs->hdr->bplmap) * cs_addrs->hdr->bplmap; + offset = blk - index; + + if(!(bp = t_qread (index, cycle, cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + + status = dse_lm_blk_free(offset * BML_BITS_PER_BLK, bp + SIZEOF(blk_hdr)); + if (0 == status) /* status == 00 => blk is busy */ + return FALSE; + else /* status == 01 and 11 => blk is free. + Also illegal value is considered as free */ + return TRUE; +} diff --git a/sr_port/dse_is_blk_free.h b/sr_port/dse_is_blk_free.h new file mode 100644 index 0000000..23b7777 --- /dev/null +++ b/sr_port/dse_is_blk_free.h @@ -0,0 +1,20 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef DSE_IS_BLK_FREE_DEFINED + +/* Declare parms for dse_is_blk_free.c */ + +bool dse_is_blk_free (block_id blk, sm_int_ptr_t cycle, cache_rec_ptr_ptr_t cr); + +#define DSE_IS_BLK_FREE_DEFINED + +#endif diff --git a/sr_port/dse_is_blk_in.c b/sr_port/dse_is_blk_in.c new file mode 100644 index 0000000..220300b --- /dev/null +++ b/sr_port/dse_is_blk_in.c @@ -0,0 +1,71 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "dsefind.h" +#include "dse.h" + +GBLREF sgmnt_addrs *cs_addrs; +GBLREF block_id patch_find_blk; +GBLREF block_id patch_path[MAX_BT_DEPTH + 1]; +GBLREF block_id patch_path1[MAX_BT_DEPTH + 1]; +GBLREF int4 patch_offset[MAX_BT_DEPTH + 1]; +GBLREF int4 patch_offset1[MAX_BT_DEPTH + 1]; +GBLREF short int patch_path_count; +GBLREF short int patch_dir_path_count; +GBLREF bool patch_find_root_search; + +int dse_is_blk_in(sm_uc_ptr_t rp, sm_uc_ptr_t r_top, short size) +{ + sm_uc_ptr_t key_top; + char targ_key[256]; + + memcpy(&targ_key[0], rp + SIZEOF(rec_hdr), size); + if ( patch_find_blk != patch_path[0] + && !dse_order(patch_path[0], &patch_path[1], &patch_offset[0], + &targ_key[0], size, 0)) + return FALSE; + patch_dir_path_count = patch_path_count; + if (patch_find_blk != patch_path[patch_path_count - 1]) + { + if (patch_path[patch_path_count - 1] <= 0 + || patch_path[patch_path_count] > cs_addrs->ti->total_blks) + return FALSE; + patch_find_root_search = FALSE; + for (key_top = rp + SIZEOF(rec_hdr); key_top < r_top ; ) + if (!*key_top++ && !*key_top++) + break; + size = key_top - rp - SIZEOF(rec_hdr); + if (size < 0) + size = 0; + else if (size > SIZEOF(targ_key)) + size = SIZEOF(targ_key); + memcpy(&targ_key[0], rp + SIZEOF(rec_hdr), size); + patch_path1[0] = patch_path[patch_path_count - 1]; + patch_path[patch_path_count - 1] = 0; + patch_path_count = 1; + if (patch_find_blk != patch_path1[0] + && !dse_order(patch_path1[0],&patch_path1[1],&patch_offset1[0], + &targ_key[0], size, 0)) + return FALSE; + } else + patch_path_count = 0; + return TRUE; +} diff --git a/sr_port/dse_ksrch.c b/sr_port/dse_ksrch.c new file mode 100644 index 0000000..b0a290c --- /dev/null +++ b/sr_port/dse_ksrch.c @@ -0,0 +1,122 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "copy.h" +#include "dse.h" + +/* Include prototypes */ +#include "t_qread.h" +#include "mmemory.h" + +GBLREF short int patch_path_count; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF char patch_comp_key[MAX_KEY_SZ + 1]; +GBLREF unsigned char patch_comp_count; +GBLREF bool patch_find_root_search; +GBLDEF block_id ksrch_root; + +int dse_ksrch(block_id srch, + block_id_ptr_t pp, + int4 *off, + char *targ_key, + int targ_len) +{ + sm_uc_ptr_t bp, b_top, rp, r_top, key_top, blk_id; + unsigned char cc; + int rsize; + ssize_t size; + int4 cmp; + short dummy_short; + int4 dummy_int; + cache_rec_ptr_t dummy_cr; + error_def(ERR_DSEBLKRDFAIL); + + if(!(bp = t_qread(srch, &dummy_int, &dummy_cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + if (((blk_hdr_ptr_t) bp)->bsiz > cs_addrs->hdr->blk_size) + b_top = bp + cs_addrs->hdr->blk_size; + else if (((blk_hdr_ptr_t) bp)->bsiz < SIZEOF(blk_hdr)) + b_top = bp + SIZEOF(blk_hdr); + else + b_top = bp + ((blk_hdr_ptr_t) bp)->bsiz; + patch_comp_count = 0; + patch_comp_key[0] = patch_comp_key[1] = 0; + *off = 0; + for (rp = bp + SIZEOF(blk_hdr); rp < b_top; rp = r_top) + { + *off = (int4)(rp - bp); + GET_SHORT(dummy_short, &((rec_hdr_ptr_t)rp)->rsiz); + rsize = dummy_short; + if (rsize < SIZEOF(rec_hdr)) + r_top = rp + SIZEOF(rec_hdr); + else + r_top = rp + rsize; + if (r_top > b_top) + r_top = b_top; + + + if (r_top - rp < (((blk_hdr_ptr_t)bp)->levl ? SIZEOF(block_id) : MIN_DATA_SIZE) + SIZEOF(rec_hdr)) + { + *pp = 0; + break; + } + for (key_top = rp + SIZEOF(rec_hdr); key_top < r_top ; ) + if (!*key_top++ && !*key_top++) + break; + + if (((blk_hdr_ptr_t)bp)->levl && key_top > (blk_id = r_top - SIZEOF(block_id))) + key_top = blk_id; + if (((rec_hdr_ptr_t) rp)->cmpc > patch_comp_count) + cc = patch_comp_count; + else + cc = ((rec_hdr_ptr_t) rp)->cmpc; + size = (ssize_t)(key_top - rp - SIZEOF(rec_hdr)); + if (size > SIZEOF(patch_comp_key) - 2 - cc) + size = SIZEOF(patch_comp_key) - 2 - cc; + if (size < 0) + size = 0; + memcpy(&patch_comp_key[cc], rp + SIZEOF(rec_hdr), size); + patch_comp_count = (int)(cc + size); + GET_LONGP(pp, key_top); + cmp = memvcmp(targ_key, targ_len, &patch_comp_key[0], patch_comp_count); + if (0 > cmp) + break; + if (!cmp) + { + if (0 != ((blk_hdr_ptr_t)bp)->levl) + break; + if (patch_find_root_search) + { + for (key_top = rp + SIZEOF(rec_hdr); key_top < r_top; ) + if (!*key_top++ && !*key_top++) + break; + GET_LONG(ksrch_root, key_top); + } + return TRUE; + } + } + patch_path_count++; + if (((blk_hdr_ptr_t) bp)->levl && *pp > 0 && *pp < cs_addrs->ti->total_blks && (*pp % cs_addrs->hdr->bplmap) + && dse_ksrch(*pp, pp + 1, off + 1, targ_key, targ_len)) + return TRUE; + return FALSE; + +} diff --git a/sr_port/dse_lm_blk_free.c b/sr_port/dse_lm_blk_free.c new file mode 100644 index 0000000..929cb71 --- /dev/null +++ b/sr_port/dse_lm_blk_free.c @@ -0,0 +1,46 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "gdsbml.h" +#include "cli.h" +#include "dse.h" + +int4 dse_lm_blk_free(int4 blk, sm_uc_ptr_t base_addr) +{ + sm_uc_ptr_t ptr; + unsigned char valid; + int4 bits; + + blk /= BML_BITS_PER_BLK; + ptr = base_addr + (blk * BML_BITS_PER_BLK) / 8; + valid = *ptr; + switch (blk % (8 / BML_BITS_PER_BLK)) + { case 0: break; + case 1: + valid = valid >> BML_BITS_PER_BLK; + break; + case 2: + valid = valid >> 2 * BML_BITS_PER_BLK; + break; + case 3: + valid = valid >> 3 * BML_BITS_PER_BLK; + break; + } + bits = valid & 3; + return bits; +} diff --git a/sr_port/dse_m_rest.c b/sr_port/dse_m_rest.c new file mode 100644 index 0000000..a252564 --- /dev/null +++ b/sr_port/dse_m_rest.c @@ -0,0 +1,134 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/******************************************************************************* +* +* MODULE NAME: PATCH_M_REST +* +* CALLING SEQUENCE: void dse_m_rest (blk, bml_list, bml_size) +* block_id blk; +* unsigned char *bml_list; +* int4 bml_size; +* +* DESCRIPTION: This is a recursive routine kicked off by PATCH_MAPS +* in the RESTORE_ALL function. It reconstructs a +* a local copy of all the local bit maps. +* +* HISTORY: +* +*******************************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "copy.h" +#include "util.h" +#include "gdsbml.h" +#include "dse.h" + +/* Include prototypes */ +#include "t_qread.h" + +GBLREF sgmnt_addrs *cs_addrs; + +#define MAX_UTIL_LEN 64 + +void dse_m_rest ( + block_id blk, /* block number */ + unsigned char *bml_list, /* start of local list of local bit maps */ + int4 bml_size, /* size of each entry in *bml_list */ + sm_vuint_ptr_t blks_ptr, /* total free blocks */ + bool in_dir_tree) +{ + sm_uc_ptr_t bp, b_top, rp, r_top, bml_ptr, np, ptr; + unsigned char util_buff[MAX_UTIL_LEN]; + block_id next; + int util_len; + int4 dummy_int; + cache_rec_ptr_t dummy_cr; + int4 bml_index; + short level, rsize; + int4 bplmap; + error_def(ERR_DSEBLKRDFAIL); + if(!(bp = t_qread (blk, &dummy_int, &dummy_cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + if (((blk_hdr_ptr_t) bp)->bsiz > cs_addrs->hdr->blk_size) + b_top = bp + cs_addrs->hdr->blk_size; + else if (((blk_hdr_ptr_t) bp)->bsiz < SIZEOF(blk_hdr)) + b_top = bp + SIZEOF(blk_hdr); + else + b_top = bp + ((blk_hdr_ptr_t) bp)->bsiz; + + level = ((blk_hdr_ptr_t)bp)->levl; + bplmap = cs_addrs->hdr->bplmap; + + for (rp = bp + SIZEOF(blk_hdr); rp < b_top ;rp = r_top) + { if (in_dir_tree || level > 1) /* reread block because it may have been flushed from read */ + { if (!(np = t_qread(blk,&dummy_int,&dummy_cr))) /* cache due to LRU buffer scheme and reads in recursive */ + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); /* calls to dse_m_rest. */ + if (np != bp) + { b_top = np + (b_top - bp); + rp = np + (rp - bp); + r_top = np + (r_top - bp); + bp = np; + } + } + GET_SHORT(rsize,&((rec_hdr_ptr_t)rp)->rsiz); + r_top = rp + rsize; + if (r_top > b_top) + r_top = b_top; + if (r_top - rp < (SIZEOF(rec_hdr) + SIZEOF(block_id))) + break; + if (in_dir_tree && level == 0) + { + for (ptr = rp + SIZEOF(rec_hdr); ; ) + { + if (*ptr++ == 0 && *ptr++ == 0) + break; + } + GET_LONG(next,ptr); + } + else + GET_LONG(next,r_top - SIZEOF(block_id)); + if (next < 0 || next >= cs_addrs->ti->total_blks || + (next / bplmap * bplmap == next)) + { memcpy(util_buff,"Invalid pointer in block ",25); + util_len = 25; + util_len += i2hex_nofill(blk, &util_buff[util_len], 8); + memcpy(&util_buff[util_len], " record offset ",15); + util_len += 15; + util_len += i2hex_nofill((int)(rp - bp), &util_buff[util_len], 4); + util_buff[util_len] = 0; + util_out_print((char*)util_buff,TRUE); + continue; + } + bml_index = next / bplmap; + bml_ptr = bml_list + bml_index * bml_size; + if (bml_busy(next - next / bplmap * bplmap, bml_ptr + SIZEOF(blk_hdr))) + { *blks_ptr = *blks_ptr - 1; + if (((blk_hdr_ptr_t) bp)->levl > 1) + { dse_m_rest (next, bml_list, bml_size, blks_ptr, in_dir_tree); + } + else if (in_dir_tree) + { assert(((blk_hdr_ptr_t) bp)->levl == 0 || ((blk_hdr_ptr_t) bp)->levl == 1); + dse_m_rest (next, bml_list, bml_size, blks_ptr, ((blk_hdr_ptr_t)bp)->levl); + } + } + } + return; +} diff --git a/sr_port/dse_maps.c b/sr_port/dse_maps.c new file mode 100644 index 0000000..80f604d --- /dev/null +++ b/sr_port/dse_maps.c @@ -0,0 +1,262 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gtm_time.h" + +#include "gdsroot.h" +#include "gdskill.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "gdsbml.h" +#include "min_max.h" /* needed for gdsblkops.h */ +#include "gdsblkops.h" +#include "gdscc.h" +#include "filestruct.h" +#include "jnl.h" +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" +#include "cli.h" +#include "util.h" +#include "send_msg.h" +#include "dse.h" + +/* Include prototypes */ +#include "t_qread.h" +#include "dse_is_blk_free.h" +#include "bit_set.h" +#include "bit_clear.h" +#include "t_begin_crit.h" +#include "t_write.h" +#include "t_end.h" +#include "longset.h" /* needed for cws_insert.h */ +#include "cws_insert.h" +#include "process_deferred_stale.h" +#include "gvcst_blk_build.h" + +#define MAX_UTIL_LEN 80 + +GBLREF char *update_array, *update_array_ptr; +GBLREF uint4 update_array_size; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF block_id patch_curr_blk; +GBLREF gd_region *gv_cur_region; +GBLREF short crash_count; +GBLREF boolean_t unhandled_stale_timer_pop; +GBLREF srch_hist dummy_hist; +GBLREF unsigned char *non_tp_jfb_buff_ptr; + +void dse_maps(void) +{ + block_id blk, bml_blk; + blk_segment *bs1, *bs_ptr; + int4 blk_seg_cnt, blk_size; /* needed for BLK_INIT, BLK_SEG and BLK_FINI macros */ + sm_uc_ptr_t bp; + char util_buff[MAX_UTIL_LEN]; + int4 bml_size, bml_list_size, blk_index, bml_index; + int4 total_blks, blks_in_bitmap; + int4 bplmap, dummy_int; + unsigned char *bml_list; + cache_rec_ptr_t cr, dummy_cr; + bt_rec_ptr_t btr; + int util_len; + uchar_ptr_t blk_ptr; + boolean_t was_crit; + uint4 jnl_status; + srch_blk_status blkhist; + jnl_private_control *jpc; + jnl_buffer_ptr_t jbp; + sgmnt_addrs *csa; + sgmnt_data_ptr_t csd; + + error_def(ERR_DSEBLKRDFAIL); + error_def(ERR_DBRDONLY); + error_def(ERR_DSEFAIL); + + if (CLI_PRESENT == cli_present("BUSY") || CLI_PRESENT == cli_present("FREE") || + CLI_PRESENT == cli_present("MASTER") || CLI_PRESENT == cli_present("RESTORE_ALL")) + { + if (gv_cur_region->read_only) + rts_error(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); + } + CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ + csa = cs_addrs; + assert(&FILE_INFO(gv_cur_region)->s_addrs == csa); + was_crit = csa->now_crit; + if (csa->critical) + crash_count = csa->critical->crashcnt; + csd = csa->hdr; + bplmap = csd->bplmap; + if (CLI_PRESENT == cli_present("BLOCK")) + { + if (!cli_get_hex("BLOCK", (uint4 *)&blk)) + return; + if (blk < 0 || blk >= csa->ti->total_blks) + { + util_out_print("Error: invalid block number.", TRUE); + return; + } + patch_curr_blk = blk; + } + else + blk = patch_curr_blk; + if (CLI_PRESENT == cli_present("FREE")) + { + if (0 == bplmap) + { + util_out_print("Cannot perform map updates: bplmap field of file header is zero.", TRUE); + return; + } + if (blk / bplmap * bplmap == blk) + { + util_out_print("Cannot perform action on a map block.", TRUE); + return; + } + bml_blk = blk / bplmap * bplmap; + bm_setmap(bml_blk, blk, FALSE); + return; + } + if (CLI_PRESENT == cli_present("BUSY")) + { + if (0 == bplmap) + { + util_out_print("Cannot perform map updates: bplmap field of file header is zero.", TRUE); + return; + } + if (blk / bplmap * bplmap == blk) + { + util_out_print("Cannot perform action on a map block.", TRUE); + return; + } + bml_blk = blk / bplmap * bplmap; + bm_setmap(bml_blk, blk, TRUE); + return; + } + blk_size = csd->blk_size; + if (CLI_PRESENT == cli_present("MASTER")) + { + if (0 == bplmap) + { + util_out_print("Cannot perform maps updates: bplmap field of file header is zero.", TRUE); + return; + } + if (!was_crit) + grab_crit(gv_cur_region); + bml_blk = blk / bplmap * bplmap; + if (dba_mm == csd->acc_meth) + bp = (sm_uc_ptr_t)csa->acc_meth.mm.base_addr + (off_t)bml_blk * blk_size; + else + { + assert(dba_bg == csd->acc_meth); + if (!(bp = t_qread(bml_blk, &dummy_int, &dummy_cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + } + if ((csa->ti->total_blks / bplmap) * bplmap == bml_blk) + total_blks = (csa->ti->total_blks - bml_blk); + else + total_blks = bplmap; + if (NO_FREE_SPACE == bml_find_free(0, bp + SIZEOF(blk_hdr), total_blks)) + bit_clear(bml_blk / bplmap, csa->bmm); + else + bit_set(bml_blk / bplmap, csa->bmm); + if (bml_blk > csa->nl->highest_lbm_blk_changed) + csa->nl->highest_lbm_blk_changed = bml_blk; + if (!was_crit) + rel_crit(gv_cur_region); + return; + } + if (CLI_PRESENT == cli_present("RESTORE_ALL")) + { + if (0 == bplmap) + { + util_out_print("Cannot perform maps updates: bplmap field of file header is zero.", TRUE); + return; + } + total_blks = csa->ti->total_blks; + assert(ROUND_DOWN2(blk_size, 2 * SIZEOF(int4)) == blk_size); + bml_size = BM_SIZE(bplmap); + bml_list_size = (total_blks + bplmap - 1) / bplmap * bml_size; + bml_list = (unsigned char *)malloc(bml_list_size); + for (blk_index = 0, bml_index = 0; blk_index < total_blks; blk_index += bplmap, bml_index++) + bml_newmap((blk_hdr_ptr_t)(bml_list + bml_index * bml_size), bml_size, csa->ti->curr_tn); + if (!was_crit) + { + grab_crit(gv_cur_region); + csa->hold_onto_crit = TRUE; /* need to do this AFTER grab_crit */ + } + blk = get_dir_root(); + assert(blk < bplmap); + csa->ti->free_blocks = total_blks - DIVIDE_ROUND_UP(total_blks, bplmap); + bml_busy(blk, bml_list + SIZEOF(blk_hdr)); + csa->ti->free_blocks = csa->ti->free_blocks - 1; + dse_m_rest(blk, bml_list, bml_size, &csa->ti->free_blocks, TRUE); + for (blk_index = 0, bml_index = 0; blk_index < total_blks; blk_index += bplmap, bml_index++) + { + t_begin_crit(ERR_DSEFAIL); + CHECK_TN(csa, csd, csd->trans_hist.curr_tn); /* can issue rts_error TNTOOLARGE */ + CWS_RESET; + CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ + assert(csa->ti->early_tn == csa->ti->curr_tn); + blk_ptr = bml_list + bml_index * bml_size; + blkhist.blk_num = blk_index; + if (!(blkhist.buffaddr = t_qread(blkhist.blk_num, &blkhist.cycle, &blkhist.cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + BLK_INIT(bs_ptr, bs1); + BLK_SEG(bs_ptr, blk_ptr + SIZEOF(blk_hdr), bml_size - SIZEOF(blk_hdr)); + BLK_FINI(bs_ptr, bs1); + t_write(&blkhist, (unsigned char *)bs1, 0, 0, LCL_MAP_LEVL, TRUE, FALSE, GDS_WRITE_KILLTN); + BUILD_AIMG_IF_JNL_ENABLED(csd, non_tp_jfb_buff_ptr, csa->ti->curr_tn); + t_end(&dummy_hist, NULL, csa->ti->curr_tn); + } + /* Fill in master map */ + for (blk_index = 0, bml_index = 0; blk_index < total_blks; blk_index += bplmap, bml_index++) + { + blks_in_bitmap = (blk_index + bplmap <= total_blks) ? bplmap : total_blks - blk_index; + assert(1 < blks_in_bitmap); /* the last valid block in the database should never be a bitmap block */ + if (NO_FREE_SPACE != bml_find_free(0, (bml_list + bml_index * bml_size) + SIZEOF(blk_hdr), blks_in_bitmap)) + bit_set(blk_index / bplmap, csa->bmm); + else + bit_clear(blk_index / bplmap, csa->bmm); + if (blk_index > csa->nl->highest_lbm_blk_changed) + csa->nl->highest_lbm_blk_changed = blk_index; + } + if (!was_crit) + { + csa->hold_onto_crit = FALSE; /* need to do this before the rel_crit */ + rel_crit(gv_cur_region); + } + if (unhandled_stale_timer_pop) + process_deferred_stale(); + free(bml_list); + csd->kill_in_prog = csd->abandoned_kills = 0; + return; + } + MEMCPY_LIT(util_buff, "!/Block "); + util_len = SIZEOF("!/Block ") - 1; + util_len += i2hex_nofill(blk, (uchar_ptr_t)&util_buff[util_len], 8); + memcpy(&util_buff[util_len], " is marked !AD in its local bit map.!/", + SIZEOF(" is marked !AD in its local bit map.!/") - 1); + util_len += SIZEOF(" is marked !AD in its local bit map.!/") - 1; + util_buff[util_len] = 0; + if (!was_crit) + grab_crit(gv_cur_region); + util_out_print(util_buff, TRUE, 4, dse_is_blk_free(blk, &dummy_int, &dummy_cr) ? "free" : "busy"); + if (!was_crit) + rel_crit(gv_cur_region); + return; +} diff --git a/sr_port/dse_order.c b/sr_port/dse_order.c new file mode 100644 index 0000000..b81f98a --- /dev/null +++ b/sr_port/dse_order.c @@ -0,0 +1,138 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "copy.h" +#include "dse.h" + +/* Include prototypes */ +#include "t_qread.h" +#include "mmemory.h" + +GBLREF short int patch_path_count; +GBLREF block_id patch_left_sib,patch_right_sib,patch_find_blk; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF char patch_comp_key[MAX_KEY_SZ + 1]; +GBLREF unsigned char patch_comp_count; +GBLREF bool patch_find_root_search; + +int dse_order(block_id srch, + block_id_ptr_t pp, + int4 *op, + char *targ_key, + short int targ_len, + bool dir_data_blk) +{ + sm_uc_ptr_t bp, b_top, rp, r_top, key_top, c1, ptr; + unsigned char cc; + block_id last; + short int size, rsize; + int4 dummy_int; + cache_rec_ptr_t dummy_cr; + error_def(ERR_DSEBLKRDFAIL); + + last = 0; + patch_path_count++; + if(!(bp = t_qread(srch, &dummy_int, &dummy_cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + if (((blk_hdr_ptr_t) bp)->bsiz > cs_addrs->hdr->blk_size) + b_top = bp + cs_addrs->hdr->blk_size; + else if (((blk_hdr_ptr_t) bp)->bsiz < SIZEOF(blk_hdr)) + b_top = bp + SIZEOF(blk_hdr); + else + b_top = bp + ((blk_hdr_ptr_t) bp)->bsiz; + patch_comp_count = 0; + patch_comp_key[0] = patch_comp_key[1] = 0; + for (rp = bp + SIZEOF(blk_hdr); rp < b_top ;rp = r_top, last = *pp) + { + GET_SHORT(rsize,&((rec_hdr_ptr_t)rp)->rsiz); + if (rsize < SIZEOF(rec_hdr)) + r_top = rp + SIZEOF(rec_hdr); + else + r_top = rp + rsize; + if (r_top > b_top || (r_top == b_top && ((blk_hdr*)bp)->levl)) + { + if (b_top - rp != SIZEOF(rec_hdr) + SIZEOF(block_id) || ((rec_hdr *) rp)->cmpc) + return FALSE; + if (dir_data_blk && !((blk_hdr_ptr_t)bp)->levl) + { + for (ptr = rp + SIZEOF(rec_hdr); ;) + if (*ptr++ == 0 && *ptr++ == 0) + break; + GET_LONGP(pp,ptr); + } else + GET_LONGP(pp,b_top - SIZEOF(block_id)); + break; + } else + { + if (r_top - rp < SIZEOF(block_id) + SIZEOF(rec_hdr)) + break; + if (dir_data_blk && !((blk_hdr_ptr_t)bp)->levl) + { + for (ptr = rp + SIZEOF(rec_hdr); ;) + if (*ptr++ == 0 && *ptr++ == 0) + break; + key_top = ptr; + } else + key_top = r_top - SIZEOF(block_id); + if (((rec_hdr_ptr_t) rp)->cmpc > patch_comp_count) + cc = patch_comp_count; + else + cc = ((rec_hdr_ptr_t) rp)->cmpc; + size = key_top - rp - SIZEOF(rec_hdr); + if (size > SIZEOF(patch_comp_key) - 2 - cc) + size = SIZEOF(patch_comp_key) - 2 - cc; + if (size < 0) + size = 0; + memcpy(&patch_comp_key[cc], rp + SIZEOF(rec_hdr), size); + patch_comp_count = cc + size; + GET_LONGP(pp,key_top); + if (memvcmp(targ_key,targ_len,&patch_comp_key[0],patch_comp_count) <= 0) + break; + } + } + *op = (int4)(rp - bp); + if (*pp == patch_find_blk && !dir_data_blk) + { + patch_left_sib = last; + if (r_top < b_top) + { + rp = r_top; + GET_SHORT(rsize,&((rec_hdr_ptr_t)r_top)->rsiz); + r_top = rp + rsize; + if (r_top > b_top) + r_top = b_top; + if (r_top - rp >= SIZEOF(block_id)) + GET_LONG(patch_right_sib,r_top - SIZEOF(block_id)); + } + return TRUE; + } + if ( *pp > 0 && *pp < cs_addrs->ti->total_blks && (*pp % cs_addrs->hdr->bplmap)) + { + if (((blk_hdr_ptr_t) bp)->levl > 1 && dse_order(*pp,pp + 1,op + 1,targ_key,targ_len, 0)) + return TRUE; + else if (((blk_hdr_ptr_t)bp)->levl == 1 && patch_find_root_search) + return dse_order( *pp,pp + 1,op + 1, targ_key, targ_len, 1); + else if (((blk_hdr_ptr_t)bp)->levl == 0 && patch_find_root_search) + return TRUE; + } + patch_path_count--; + return FALSE; +} diff --git a/sr_port/dse_over.c b/sr_port/dse_over.c new file mode 100644 index 0000000..d38fd38 --- /dev/null +++ b/sr_port/dse_over.c @@ -0,0 +1,225 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gtm_iconv.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "min_max.h" /* needed for gdsblkops.h */ +#include "gdsblkops.h" +#include "gdscc.h" +#include "cli.h" +#include "filestruct.h" +#include "jnl.h" +#include "io.h" +#include "iosp.h" +#include "util.h" +#include "dse.h" + +/* Include prototypes */ +#include "t_qread.h" +#include "t_write.h" +#include "t_end.h" +#include "t_begin_crit.h" +#include "gvcst_blk_build.h" +#include "ebc_xlat.h" +#include "t_abort.h" +#include "stringpool.h" +#include "gtm_conv.h" +#include "gtm_utf8.h" + +GBLREF char *update_array, *update_array_ptr; +GBLREF gd_region *gv_cur_region; +GBLREF uint4 update_array_size; +GBLREF gd_addr *gd_header; +GBLREF srch_hist dummy_hist; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF block_id patch_curr_blk; +GBLREF gtm_chset_t dse_over_chset; +#if defined(KEEP_zOS_EBCDIC) || defined(VMS) +GBLREF iconv_t dse_over_cvtcd; +#endif +GBLREF cw_set_element cw_set[]; +GBLREF unsigned char *non_tp_jfb_buff_ptr; +GBLREF UConverter *chset_desc[]; +LITREF mstr chset_names[]; +GBLREF spdesc stringpool; + +void dse_over(void) +{ + static char *data = NULL; + static int data_size; + block_id blk; + uchar_ptr_t lbp; + uint4 offset; + int data_len, size; + blk_segment *bs1, *bs_ptr; + int4 blk_seg_cnt, blk_size; + unsigned char *cvt_src_ptr, *cvt_dst_ptr; + unsigned int insize, outsize; + char chset_name[MAX_CHSET_NAME]; + mstr chset_mstr; + unsigned short name_len = 0; + srch_blk_status blkhist; + mstr cvt_src; + int cvt_len; + + error_def(ERR_DBRDONLY); + error_def(ERR_DSEBLKRDFAIL); + error_def(ERR_DSEFAIL); + + if (gv_cur_region->read_only) + rts_error(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); + CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ + blk_size = cs_addrs->hdr->blk_size; + if (cli_present("BLOCK") == CLI_PRESENT) + { + if (!cli_get_hex("BLOCK", (uint4 *)&blk)) + return; + if (blk < 0 || blk >= cs_addrs->ti->total_blks) + { + util_out_print("Error: invalid block number.",TRUE); + return; + } + patch_curr_blk = blk; + } else + blk = patch_curr_blk; + if (CLI_PRESENT == cli_present("OCHSET")) + { + name_len = SIZEOF(chset_name); + if (cli_get_str("OCHSET", chset_name, &name_len) && 0 != name_len) + { + chset_name[name_len] = 0; +#if defined(KEEP_zOS_EBCDIC) || defined(VMS) + if ( (iconv_t)0 != dse_over_cvtcd ) + { + ICONV_CLOSE_CD(dse_over_cvtcd); + } + if (!strcmp(chset_name, INSIDE_CH_SET)) + dse_over_cvtcd = (iconv_t)0; + else + ICONV_OPEN_CD(dse_over_cvtcd, INSIDE_CH_SET, chset_name); +#else + chset_mstr.addr = chset_name; + chset_mstr.len = name_len; + SET_ENCODING(dse_over_chset, &chset_mstr); + get_chset_desc(&chset_names[dse_over_chset]); +#endif + } + } else + { +#ifdef KEEP_zOS_EBCDIC + if ((iconv_t)0 != dse_over_cvtcd ) + { + ICONV_CLOSE_CD(dse_over_cvtcd); + dse_over_cvtcd = (iconv_t)0; /* default ASCII, no conversion */ + } +#else + dse_over_chset = CHSET_M; +#endif + } + if (cli_present("OFFSET") != CLI_PRESENT) + { + util_out_print("Error: offset must be specified.", TRUE); + return; + } + if (!cli_get_hex("OFFSET", &offset)) + return; + if (offset < SIZEOF(blk_hdr)) + { + util_out_print("Error: offset too small.", TRUE); + return; + } + if (cli_present("DATA") != CLI_PRESENT) + { + util_out_print("Error: data must be specified.", TRUE); + return; + } + t_begin_crit(ERR_DSEFAIL); + blkhist.blk_num = blk; + if (!(blkhist.buffaddr = t_qread(blkhist.blk_num, &blkhist.cycle, &blkhist.cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + + size = ((blk_hdr_ptr_t)blkhist.buffaddr)->bsiz; + if (size < SIZEOF(blk_hdr)) + size = SIZEOF(blk_hdr); + else if (size >= blk_size) + size = blk_size; + + if (offset >= size) + { + util_out_print("Error: offset too large.", TRUE); + t_abort(gv_cur_region, cs_addrs); + return; + } + if (NULL == data) + { + data = malloc(MAX_LINE); + data_size = MAX_LINE; + } + if (FALSE == dse_data(&data[0], &data_len)) + { + t_abort(gv_cur_region, cs_addrs); + return; + } +#if defined(KEEP_zOS_EBCDIC) || defined(VMS) + cvt_src_ptr = cvt_dst_ptr = (unsigned char *)data; + insize = outsize = (unsigned int)data_len; + if ((iconv_t)0 != dse_over_cvtcd) + ICONVERT(dse_over_cvtcd, &cvt_src_ptr, &insize, &cvt_dst_ptr, &outsize); /* in-place conversion */ +#else + cvt_src.len = (unsigned int)data_len; + cvt_src.addr = data; + if (CHSET_M != dse_over_chset) + { + cvt_len = gtm_conv(chset_desc[dse_over_chset], chset_desc[CHSET_UTF8], &cvt_src, NULL, NULL); + if (cvt_len > data_size) + { + free(data); + data = malloc(cvt_len); + data_size = cvt_len; + } + memcpy(data, stringpool.free, cvt_len); + data_len = cvt_len; + } +#endif + if (offset + data_len > size) + { + util_out_print("Error: data will not fit in block at given offset.", TRUE); + t_abort(gv_cur_region, cs_addrs); + return; + } + lbp = (uchar_ptr_t)malloc(blk_size); + memcpy (lbp, blkhist.buffaddr, blk_size); + memcpy(lbp + offset, &data[0], data_len); + + BLK_INIT(bs_ptr, bs1); + BLK_SEG(bs_ptr, (uchar_ptr_t)lbp + SIZEOF(blk_hdr), (int)((blk_hdr_ptr_t)lbp)->bsiz - SIZEOF(blk_hdr)); + if (!BLK_FINI(bs_ptr, bs1)) + { + util_out_print("Error: bad blk build.", TRUE); + free(lbp); + t_abort(gv_cur_region, cs_addrs); + return; + } + t_write(&blkhist, (unsigned char *)bs1, 0, 0, ((blk_hdr_ptr_t)lbp)->levl, TRUE, FALSE, GDS_WRITE_KILLTN); + BUILD_AIMG_IF_JNL_ENABLED(cs_data, non_tp_jfb_buff_ptr, cs_addrs->ti->curr_tn); + t_end(&dummy_hist, NULL, TN_NOT_SPECIFIED); + return; +} diff --git a/sr_port/dse_page.c b/sr_port/dse_page.c new file mode 100644 index 0000000..c1e6e97 --- /dev/null +++ b/sr_port/dse_page.c @@ -0,0 +1,25 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "util.h" +#include "dse.h" + +void dse_page(void) +{ + util_out_print("!^",TRUE); + return; +} diff --git a/sr_port/dse_puttime.h b/sr_port/dse_puttime.h new file mode 100644 index 0000000..db78bff --- /dev/null +++ b/sr_port/dse_puttime.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __DST_PUTTIME_H__ +#define __DST_PUTTIME_H__ + +void dse_puttime(int_ptr_t time, char *c, bool flush); + +#endif diff --git a/sr_port/dse_r_dmp.c b/sr_port/dse_r_dmp.c new file mode 100644 index 0000000..d54463f --- /dev/null +++ b/sr_port/dse_r_dmp.c @@ -0,0 +1,115 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "cli.h" +#include "dse.h" +#include "util.h" +#include "skan_offset.h" +#include "skan_rnum.h" + +/* Include prototypes */ +#include "t_qread.h" + +GBLREF block_id patch_curr_blk; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gd_region *gv_cur_region; +GBLREF int patch_is_fdmp; +GBLREF int patch_fdmp_recs; +GBLREF int patch_rec_counter; +GBLREF VSIG_ATOMIC_T util_interrupt; + +boolean_t dse_r_dmp(void) +{ + block_id blk; + sm_uc_ptr_t bp, b_top, rp; + int4 count; + int4 dummy_int; + cache_rec_ptr_t dummy_cr; + short record, size; + boolean_t was_crit; + int4 nocrit_present; + + error_def(ERR_DSEBLKRDFAIL); + error_def(ERR_CTRLC); + + if (cli_present("BLOCK") == CLI_PRESENT) + { + uint4 tmp_blk; + + if(!cli_get_hex("BLOCK", &tmp_blk)) + return FALSE; + blk = (block_id)tmp_blk; + if (blk < 0 || blk >= cs_addrs->ti->total_blks || !(blk % cs_addrs->hdr->bplmap)) + { + util_out_print("Error: invalid block number.", TRUE); + return FALSE; + } + patch_curr_blk = blk; + } + if (cli_present("COUNT") == CLI_PRESENT) + { + if (!cli_get_hex("COUNT", (uint4 *)&count)) + return FALSE; + } else + count = 1; + was_crit = cs_addrs->now_crit; + nocrit_present = (CLI_NEGATED == cli_present("CRIT")); + DSE_GRAB_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + if (!(bp = t_qread(patch_curr_blk, &dummy_int, &dummy_cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + if (((blk_hdr_ptr_t) bp)->bsiz > cs_addrs->hdr->blk_size) + b_top = bp + cs_addrs->hdr->blk_size; + else if (((blk_hdr_ptr_t) bp)->bsiz < SIZEOF(blk_hdr)) + b_top = bp + SIZEOF(blk_hdr); + else + b_top = bp + ((blk_hdr_ptr_t) bp)->bsiz; + if (((blk_hdr_ptr_t) bp)->levl && patch_is_fdmp) + { + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + util_out_print("Error: cannot perform GLO/ZWR dump on index block.", TRUE); + return FALSE; + } + if (cli_present("RECORD") == CLI_PRESENT) + { + if (!(rp = skan_rnum (bp, FALSE))) + { + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + return FALSE; + } + } else if (!(rp = skan_offset (bp, FALSE))) + { + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + return FALSE; + } + util_out_print(0, TRUE); + for ( ; 0 < count; count--) + { + if (util_interrupt || !(rp = dump_record(rp, patch_curr_blk, bp, b_top))) + break; + patch_rec_counter += 1; + } + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + if (util_interrupt) + rts_error(VARLSTCNT(1) ERR_CTRLC); + else if (cli_present("HEADER") == CLI_NEGATED) + util_out_print(0, TRUE); + return TRUE; +} diff --git a/sr_port/dse_range.c b/sr_port/dse_range.c new file mode 100644 index 0000000..9e9261d --- /dev/null +++ b/sr_port/dse_range.c @@ -0,0 +1,238 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "cli.h" +#include "copy.h" +#include "min_max.h" +#include "util.h" +#include "dse.h" + +/* Include prototypes */ +#include "t_qread.h" + +#define MAX_UTIL_LEN 20 + +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gd_region *gv_cur_region; +GBLREF VSIG_ATOMIC_T util_interrupt; +GBLREF block_id patch_find_blk; +GBLREF block_id patch_path[MAX_BT_DEPTH + 1]; +GBLREF short int patch_path_count; +GBLREF bool patch_find_root_search; + +void dse_range(void) +{ + char lower[256], targ_key[256], upper[256], util_buff[MAX_UTIL_LEN]; + block_id from, to, blk, blk_child; + sm_uc_ptr_t bp, b_top, key_bot, key_top, key_top1, rp, r_top; + char level; + int4 dummy_int, nocrit_present; + cache_rec_ptr_t dummy_cr; + short int rsize, size, size1; + int cnt, dummy, lower_len, util_len, upper_len; + boolean_t busy_matters, free, got_lonely_star, index, low, lost, star, up, was_crit; + + error_def(ERR_DSEBLKRDFAIL); + error_def(ERR_CTRLC); + + if (cli_present("FROM") == CLI_PRESENT) + { + if (!cli_get_hex("FROM", (uint4 *)&from)) + return; + if (from < 0 || from > cs_addrs->ti->total_blks + || !(from % cs_addrs->hdr->bplmap)) + { + util_out_print("Error: invalid block number.", TRUE); + return; + } + } + else + from = 1; + if (cli_present("TO") == CLI_PRESENT) + { + if(!cli_get_hex("TO", (uint4 *)&to)) + return; + if (to < 0 || to > cs_addrs->ti->total_blks + || !(to % cs_addrs->hdr->bplmap)) + { + util_out_print("Error: invalid block number.", TRUE); + return; + } + } + else + to = cs_addrs->ti->total_blks - 1; + if (low = (cli_present("LOWER") == CLI_PRESENT)) + { + if (!dse_getki(&lower[0], &lower_len, LIT_AND_LEN("LOWER"))) + return; + } + if (up = (cli_present("UPPER") == CLI_PRESENT)) + { + if (!dse_getki(&upper[0], &upper_len, LIT_AND_LEN("UPPER"))) + return; + } + star = (cli_present("STAR") == CLI_PRESENT); + if (!low && !up && !star) + { + util_out_print("Must specify star, or a lower or upper key limit.", TRUE); + return; + } + index = (cli_present("INDEX") == CLI_PRESENT); + lost = (cli_present("LOST") == CLI_PRESENT); + dummy = cli_present("BUSY"); + if (dummy == CLI_PRESENT) + { + busy_matters = TRUE; + free = FALSE; + } + else if (dummy == CLI_NEGATED) + busy_matters = free = TRUE; + else + busy_matters = free = FALSE; + patch_path[0] = get_dir_root(); + cnt = 0; + was_crit = cs_addrs->now_crit; + nocrit_present = (CLI_NEGATED == cli_present("CRIT")); + DSE_GRAB_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + for (blk = from; blk <= to ;blk++) + { + if (util_interrupt) + { + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + rts_error(VARLSTCNT(1) ERR_CTRLC); + break; + } + if (!(blk % cs_addrs->hdr->bplmap)) + continue; + if (!(bp = t_qread(blk, &dummy_int, &dummy_cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + level = ((blk_hdr_ptr_t)bp)->levl; + if (index && (level == 0)) + continue; + if (busy_matters && (free != dse_is_blk_free(blk, &dummy_int, &dummy_cr))) + continue; + if (((blk_hdr_ptr_t) bp)->bsiz > cs_addrs->hdr->blk_size) + b_top = bp + cs_addrs->hdr->blk_size; + else if (((blk_hdr_ptr_t) bp)->bsiz < SIZEOF(blk_hdr)) + b_top = bp + SIZEOF(blk_hdr); + else + b_top = bp + ((blk_hdr_ptr_t) bp)->bsiz; + rp = bp + SIZEOF(blk_hdr); + GET_SHORT(rsize, &((rec_hdr_ptr_t) rp)->rsiz); + if (rsize < SIZEOF(rec_hdr)) + r_top = rp + SIZEOF(rec_hdr); + else + r_top = rp + rsize; + if (r_top >= b_top) + r_top = b_top; + got_lonely_star = FALSE; + if (((blk_hdr_ptr_t) bp)->levl) + { + key_top = r_top - SIZEOF(block_id); + if (star && (r_top == b_top)) + got_lonely_star = TRUE; + } else + { + if (!up && !low) + continue; + for (key_top = rp + SIZEOF(rec_hdr); key_top < r_top ; ) + if (!*key_top++ && !*key_top++) + break; + } + if (!got_lonely_star) + { + key_bot = rp + SIZEOF(rec_hdr); + size = key_top - key_bot; + if (size <= 0) + continue; + if (size > SIZEOF(targ_key)) + size = SIZEOF(targ_key); + if (lost) + { + for (key_top1 = rp + SIZEOF(rec_hdr); key_top1 < r_top ; ) + if (!*key_top1++) + break; + size1 = key_top1 - rp - SIZEOF(rec_hdr); + if (size1 > SIZEOF(targ_key)) + size1 = SIZEOF(targ_key); + patch_find_root_search = TRUE; + patch_path_count = 1; + patch_find_blk = blk; + if (dse_is_blk_in(rp, r_top, size1)) + continue; + } + if (low && memcmp(lower, key_bot, MIN(lower_len, size)) > 0) + continue; + if (up && memcmp(upper, key_bot, MIN(upper_len, size)) < 0) + continue; + } else + { + got_lonely_star = FALSE; + if (lost) + { + blk_child = *(block_id_ptr_t)key_top; + if (!(bp = t_qread(blk_child, &dummy_int, &dummy_cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + if (((blk_hdr_ptr_t) bp)->bsiz > cs_addrs->hdr->blk_size) + b_top = bp + cs_addrs->hdr->blk_size; + else if (((blk_hdr_ptr_t) bp)->bsiz < SIZEOF(blk_hdr)) + b_top = bp + SIZEOF(blk_hdr); + else + b_top = bp + ((blk_hdr_ptr_t) bp)->bsiz; + rp = bp + SIZEOF(blk_hdr); + GET_SHORT(rsize, &((rec_hdr_ptr_t) rp)->rsiz); + if (rsize < SIZEOF(rec_hdr)) + r_top = rp + SIZEOF(rec_hdr); + else + r_top = rp + rsize; + if (r_top >= b_top) + r_top = b_top; + if (((blk_hdr_ptr_t) bp)->levl) + key_top = r_top - SIZEOF(block_id); + for (key_top1 = rp + SIZEOF(rec_hdr); key_top1 < r_top ; ) + if (!*key_top1++) + break; + size1 = key_top1 - rp - SIZEOF(rec_hdr); + if (size1 > 0) + { + if (size1 > SIZEOF(targ_key)) + size1 = SIZEOF(targ_key); + patch_find_root_search = TRUE; + patch_path_count = 1; + patch_find_blk = blk; + if (dse_is_blk_in(rp, r_top, size1)) + continue; + } + } + } + if (!cnt++) + util_out_print("!/Blocks in the specified key range:", TRUE); + util_out_print("Block: !8XL Level: !2UL", TRUE, blk, level); + } + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + if (cnt) + util_out_print("Found !UL blocks", TRUE, cnt); + else + util_out_print("None found.", TRUE); + return; +} diff --git a/sr_port/dse_rest.c b/sr_port/dse_rest.c new file mode 100644 index 0000000..a8a674e --- /dev/null +++ b/sr_port/dse_rest.c @@ -0,0 +1,186 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "gtm_string.h" +#include "mdef.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "min_max.h" /* needed for gdsblkops.h */ +#include "gdsblkops.h" +#include "gdscc.h" +#include "dse.h" +#include "cli.h" +#include "init_root_gv.h" +#include "filestruct.h" +#include "jnl.h" +#include "util.h" + +/* Include prototypes */ +#include "t_qread.h" +#include "t_write.h" +#include "t_end.h" +#include "t_begin_crit.h" +#include "gvcst_blk_build.h" +#include "t_abort.h" + +#define MAX_UTIL_LEN 80 + +GBLREF char *update_array, *update_array_ptr; +GBLREF uint4 update_array_size; +GBLREF srch_hist dummy_hist; +GBLREF gd_region *gv_cur_region; +GBLREF gd_addr *gd_header; +GBLREF block_id patch_curr_blk; +GBLREF save_strct patch_save_set[PATCH_SAVE_SIZE]; +GBLREF unsigned short int patch_save_count; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF gd_addr *original_header; +GBLREF cw_set_element cw_set[]; +GBLREF unsigned char *non_tp_jfb_buff_ptr; + +void dse_rest(void) +{ + block_id to, from; + gd_region *region; + int i, util_len; + uchar_ptr_t lbp; + char util_buff[MAX_UTIL_LEN], rn[MAX_RN_LEN + 1]; + blk_segment *bs1, *bs_ptr; + int4 blk_seg_cnt, blk_size; + unsigned short rn_len; + uint4 version; + gd_addr *temp_gdaddr; + gd_binding *map; + boolean_t found; + srch_blk_status blkhist; + + error_def(ERR_DBRDONLY); + error_def(ERR_DSEBLKRDFAIL); + error_def(ERR_DSEFAIL); + + if (gv_cur_region->read_only) + rts_error(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); + CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ + if (cli_present("VERSION") != CLI_PRESENT) + { + util_out_print("Error: save version number must be specified.", TRUE); + return; + } + if (!cli_get_int("VERSION", (int4 *)&version)) + return; + if (cli_present("BLOCK") == CLI_PRESENT) + { + if (!cli_get_hex("BLOCK", (uint4 *)&to)) + return; + if (to < 0 || to >= cs_addrs->ti->total_blks) + { + util_out_print("Error: invalid block number.", TRUE); + return; + } + patch_curr_blk = to; + } else + to = patch_curr_blk; + if (cli_present("FROM") == CLI_PRESENT) + { + if (!cli_get_hex("FROM", (uint4 *)&from)) + return; + if (from < 0 || from >= cs_addrs->ti->total_blks) + { + util_out_print("Error: invalid block number.", TRUE); + return; + } + } else + from = to; + if (cli_present("REGION") == CLI_PRESENT) + { + + rn_len = SIZEOF(rn); + if (!cli_get_str("REGION", rn, &rn_len)) + return; + for (i = rn_len; i < MAX_RN_LEN + 1; i++) + rn[i] = 0; + found = FALSE; + + temp_gdaddr = gd_header; + gd_header = original_header; + + for (i=0, region = gd_header->regions; i < gd_header->n_regions ;i++, region++) + if (found = !memcmp(®ion->rname[0], &rn[0], MAX_RN_LEN)) + break; + GET_SAVED_GDADDR(gd_header, temp_gdaddr, map, gv_cur_region); + if (!found) + { + util_out_print("Error: region not found.", TRUE); + return; + } + if (!region->open) + { + util_out_print("Error: that region was not opened because it is not bound to any namespace.", TRUE); + return; + } + } else + region = gv_cur_region; + found = FALSE; + for (i = 0; i < patch_save_count; i++) + if (patch_save_set[i].blk == from && patch_save_set[i].region == region + && (found = version == patch_save_set[i].ver)) + break; + if (!found) + { + util_out_print("Error: no such version.", TRUE); + return; + } + memcpy(util_buff, "!/Restoring block ", 18); + util_len = 18; + util_len += i2hex_nofill(to, (uchar_ptr_t)&util_buff[util_len], 8); + memcpy(&util_buff[util_len]," from version !UL", 17); + util_len += 17; + util_buff[util_len] = 0; + util_out_print(util_buff,FALSE,version); + if (to != from) + { + memcpy(util_buff, " of block ", 10); + util_len = 10; + util_len += i2hex_nofill(from, (uchar_ptr_t)&util_buff[util_len], 8); + util_buff[util_len] = 0; + util_out_print(util_buff, FALSE); + } + if (region != gv_cur_region) + util_out_print(" in region !AD", FALSE, LEN_AND_STR(rn)); + util_out_print("!/",TRUE); + if (to > cs_addrs->ti->total_blks) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + t_begin_crit(ERR_DSEFAIL); + blk_size = cs_addrs->hdr->blk_size; + blkhist.blk_num = to; + if (!(blkhist.buffaddr = t_qread(blkhist.blk_num, &blkhist.cycle, &blkhist.cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + lbp = (uchar_ptr_t)patch_save_set[i].bp; + + BLK_INIT(bs_ptr, bs1); + BLK_SEG(bs_ptr, (uchar_ptr_t)lbp + SIZEOF(blk_hdr), (int)((blk_hdr_ptr_t)lbp)->bsiz - SIZEOF(blk_hdr)); + if (!BLK_FINI(bs_ptr, bs1)) + { + util_out_print("Error: bad blk build.", TRUE); + t_abort(gv_cur_region, cs_addrs); + return; + } + t_write(&blkhist, (unsigned char *)bs1, 0, 0, ((blk_hdr_ptr_t)lbp)->levl, TRUE, FALSE, GDS_WRITE_KILLTN); + BUILD_AIMG_IF_JNL_ENABLED(cs_data, non_tp_jfb_buff_ptr, cs_addrs->ti->curr_tn); + t_end(&dummy_hist, NULL, TN_NOT_SPECIFIED); + return; +} diff --git a/sr_port/dse_rmrec.c b/sr_port/dse_rmrec.c new file mode 100644 index 0000000..ba6f0f2 --- /dev/null +++ b/sr_port/dse_rmrec.c @@ -0,0 +1,202 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "min_max.h" /* needed for gdsblkops.h */ +#include "gdsblkops.h" +#include "gdscc.h" +#include "cli.h" +#include "copy.h" +#include "filestruct.h" +#include "jnl.h" +#include "util.h" +#include "skan_offset.h" +#include "skan_rnum.h" +#include "dse.h" + +/* Include prototypes */ +#include "t_qread.h" +#include "t_write.h" +#include "t_end.h" +#include "t_begin_crit.h" +#include "gvcst_blk_build.h" +#include "t_abort.h" + +GBLREF char *update_array, *update_array_ptr; +GBLREF uint4 update_array_size; +GBLREF gd_addr *gd_header; +GBLREF gd_region *gv_cur_region; +GBLREF srch_hist dummy_hist; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF block_id patch_curr_blk; +GBLREF char patch_comp_key[MAX_KEY_SZ + 1]; +GBLREF unsigned char patch_comp_count; +GBLREF cw_set_element cw_set[]; +GBLREF unsigned char *non_tp_jfb_buff_ptr; + +void dse_rmrec(void) +{ + block_id blk; + blk_segment *bs1, *bs_ptr; + int4 blk_seg_cnt, blk_size; + int4 count; + uchar_ptr_t lbp, b_top, rp, r_top, key_top, rp_base; + char comp_key[MAX_KEY_SZ + 1]; + unsigned char cc, cc_base; + short int size, i, rsize; + srch_blk_status blkhist; + + error_def(ERR_DBRDONLY); + error_def(ERR_DSEBLKRDFAIL); + error_def(ERR_DSEFAIL); + + if (gv_cur_region->read_only) + rts_error(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); + CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ + if (cli_present("BLOCK") == CLI_PRESENT) + { + if(!cli_get_hex("BLOCK", (uint4 *)&blk)) + return; + if (blk < 0 || blk >= cs_addrs->ti->total_blks || !(blk % cs_addrs->hdr->bplmap)) + { + util_out_print("Error: invalid block number.", TRUE); + return; + } + patch_curr_blk = blk; + } + if (cli_present("COUNT") == CLI_PRESENT) + { + if (!cli_get_hex("COUNT", (uint4 *)&count) || count < 1) + return; + } else + count = 1; + t_begin_crit(ERR_DSEFAIL); + blk_size = cs_addrs->hdr->blk_size; + blkhist.blk_num = patch_curr_blk; + if (!(blkhist.buffaddr = t_qread(blkhist.blk_num, &blkhist.cycle, &blkhist.cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + lbp = (uchar_ptr_t)malloc(blk_size); + memcpy(lbp, blkhist.buffaddr, blk_size); + + if (((blk_hdr_ptr_t)lbp)->bsiz > cs_addrs->hdr->blk_size) + b_top = lbp + cs_addrs->hdr->blk_size; + else if (((blk_hdr_ptr_t)lbp)->bsiz < SIZEOF(blk_hdr)) + b_top = lbp + SIZEOF(blk_hdr); + else + b_top = lbp + ((blk_hdr_ptr_t)lbp)->bsiz; + if (cli_present("RECORD") == CLI_PRESENT) + { + if (!(rp = rp_base = skan_rnum(lbp, FALSE))) + { + free(lbp); + t_abort(gv_cur_region, cs_addrs); + return; + } + } else if (!(rp = rp_base = skan_offset(lbp, FALSE))) + { + free(lbp); + t_abort(gv_cur_region, cs_addrs); + return; + } + memcpy(&comp_key[0], &patch_comp_key[0], SIZEOF(patch_comp_key)); + cc_base = patch_comp_count; + for ( ; ; ) + { + GET_SHORT(rsize, &((rec_hdr_ptr_t)rp)->rsiz); + if (rsize < SIZEOF(rec_hdr)) + r_top = rp + SIZEOF(rec_hdr); + else + r_top = rp + rsize; + if (r_top >= b_top) + { + if (count) + { + if (((blk_hdr_ptr_t) lbp)->levl) + util_out_print("Warning: removed a star record from the end of this block.", TRUE); + ((blk_hdr_ptr_t)lbp)->bsiz = (unsigned int)(rp_base - lbp); + BLK_INIT(bs_ptr, bs1); + BLK_SEG(bs_ptr, (uchar_ptr_t)lbp + SIZEOF(blk_hdr), + (int)((blk_hdr_ptr_t)lbp)->bsiz - SIZEOF(blk_hdr)); + if (!BLK_FINI(bs_ptr, bs1)) + { + util_out_print("Error: bad blk build.",TRUE); + free(lbp); + t_abort(gv_cur_region, cs_addrs); + return; + } + t_write(&blkhist, (unsigned char *)bs1, 0, 0, + ((blk_hdr_ptr_t)lbp)->levl, TRUE, FALSE, GDS_WRITE_KILLTN); + BUILD_AIMG_IF_JNL_ENABLED(cs_data, non_tp_jfb_buff_ptr, cs_addrs->ti->curr_tn); + t_end(&dummy_hist, NULL, TN_NOT_SPECIFIED); + free(lbp); + return; + } + r_top = b_top; + } + if (((blk_hdr_ptr_t)lbp)->levl) + key_top = r_top - SIZEOF(block_id); + else + { + for (key_top = rp + SIZEOF(rec_hdr); key_top < r_top; ) + if (!*key_top++ && !*key_top++) + break; + } + if (((rec_hdr_ptr_t)rp)->cmpc > patch_comp_count) + cc = patch_comp_count; + else + cc = ((rec_hdr_ptr_t)rp)->cmpc; + size = key_top - rp - SIZEOF(rec_hdr); + if (size > SIZEOF(patch_comp_key) - 2 - cc) + size = SIZEOF(patch_comp_key) - 2 - cc; + if (size < 0) + size = 0; + memcpy(&patch_comp_key[cc], rp + SIZEOF(rec_hdr), size); + patch_comp_count = cc + size; + if (--count >= 0) + { + rp = r_top; + continue; + } + size = (patch_comp_count < cc_base) ? patch_comp_count : cc_base; + for (i = 0; i < size && patch_comp_key[i] == comp_key[i]; i++) + ; + ((rec_hdr_ptr_t)rp_base)->cmpc = i; + rsize = r_top - key_top + SIZEOF(rec_hdr) + patch_comp_count - i; + PUT_SHORT(&((rec_hdr_ptr_t)rp_base)->rsiz, rsize); + memcpy(rp_base + SIZEOF(rec_hdr), &patch_comp_key[i], patch_comp_count - i); + memcpy(rp_base + SIZEOF(rec_hdr) + patch_comp_count - i, key_top, b_top - key_top); + ((blk_hdr_ptr_t)lbp)->bsiz = (unsigned int)(rp_base + rsize - lbp + b_top - r_top); + BLK_INIT(bs_ptr, bs1); + BLK_SEG(bs_ptr, (uchar_ptr_t)lbp + SIZEOF(blk_hdr), ((blk_hdr_ptr_t)lbp)->bsiz - SIZEOF(blk_hdr)); + if (!BLK_FINI(bs_ptr, bs1)) + { + util_out_print("Error: bad blk build.", TRUE); + free(lbp); + t_abort(gv_cur_region, cs_addrs); + return; + } + t_write(&blkhist, (unsigned char *)bs1, 0, 0, ((blk_hdr_ptr_t)lbp)->levl, TRUE, FALSE, GDS_WRITE_KILLTN); + BUILD_AIMG_IF_JNL_ENABLED(cs_data, non_tp_jfb_buff_ptr, cs_addrs->ti->curr_tn); + t_end(&dummy_hist, NULL, TN_NOT_SPECIFIED); + free(lbp); + return; + } +} diff --git a/sr_port/dse_rmsb.c b/sr_port/dse_rmsb.c new file mode 100644 index 0000000..0857dd4 --- /dev/null +++ b/sr_port/dse_rmsb.c @@ -0,0 +1,71 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "dse.h" +#include "cli.h" +#include "util.h" + +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gd_region *gv_cur_region; +GBLREF block_id patch_curr_blk; +GBLREF save_strct patch_save_set[PATCH_SAVE_SIZE]; +GBLREF unsigned short patch_save_count; + +void dse_rmsb(void) +{ + block_id blk; + unsigned int i; + uint4 version; + bool found; + + if (cli_present("VERSION") != CLI_PRESENT) + { + util_out_print("Error: save version number must be specified.", TRUE); + return; + } + if (!cli_get_int("VERSION", (int4 *)&version)) + return; + if (cli_present("BLOCK") == CLI_PRESENT) + { + if (!cli_get_hex("BLOCK", (uint4 *)&blk)) + return; + if (blk < 0 || blk >= cs_addrs->ti->total_blks || !(blk % cs_addrs->hdr->bplmap)) + { + util_out_print("Error: invalid block number.", TRUE); + return; + } + patch_curr_blk = blk; + } + found = FALSE; + for (i = 0; i < patch_save_count; i++) + if (patch_save_set[i].blk == patch_curr_blk && patch_save_set[i].region == gv_cur_region + &&(found = version == patch_save_set[i].ver)) + break; + if (!found) + { + util_out_print("Error: no such version.", TRUE); + return; + } + patch_save_count--; + free(patch_save_set[i].bp); + memcpy(&patch_save_set[i], &patch_save_set[i + 1], + (patch_save_count - i) * SIZEOF(save_strct)); + return; +} diff --git a/sr_port/dse_save.c b/sr_port/dse_save.c new file mode 100644 index 0000000..070b010 --- /dev/null +++ b/sr_port/dse_save.c @@ -0,0 +1,149 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "gtm_string.h" +#include "mdef.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "dse.h" +#include "cli.h" +#include "util.h" + +/* Include prototypes */ +#include "t_qread.h" + +#define MAX_UTIL_LEN 80 + +GBLDEF save_strct patch_save_set[PATCH_SAVE_SIZE]; +GBLDEF unsigned short patch_save_count = 0; + +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gd_region *gv_cur_region; +GBLREF block_id patch_curr_blk; + +void dse_save(void) +{ + block_id blk; + unsigned i, j, util_len; + unsigned short buff_len; + boolean_t was_block, was_crit; + char buff[100], *ptr, util_buff[MAX_UTIL_LEN]; + sm_uc_ptr_t bp; + int4 dummy_int, nocrit_present; + cache_rec_ptr_t dummy_cr; + + error_def(ERR_DSEBLKRDFAIL); + + memset(util_buff, 0, MAX_UTIL_LEN); + + if (was_block = (cli_present("BLOCK") == CLI_PRESENT)) + { + if (!cli_get_hex("BLOCK", (uint4 *)&blk)) + return; + if (blk < 0 || blk >= cs_addrs->ti->total_blks) + { + util_out_print("Error: invalid block number.", TRUE); + return; + } + patch_curr_blk = blk; + } else + blk = patch_curr_blk; + if (cli_present("LIST") == CLI_PRESENT) + { + if (was_block) + { + util_len = SIZEOF("!/Saved versions of block "); + memcpy(util_buff, "!/Saved versions of block ", util_len); + util_len += i2hex_nofill(blk, (uchar_ptr_t)&util_buff[util_len-1], 8); + util_buff[util_len-1] = 0; + util_out_print(util_buff, TRUE); + for (i = j = 0; i < patch_save_count; i++) + if (patch_save_set[i].blk == blk) + { + j++; + + if (*patch_save_set[i].comment) + util_out_print("Version !UL Region !AD Comment: !AD!/", TRUE, + patch_save_set[i].ver, REG_LEN_STR(patch_save_set[i].region), + LEN_AND_STR(patch_save_set[i].comment)); + + else + util_out_print("Version !UL Region !AD!/", TRUE, patch_save_set[i].ver, + REG_LEN_STR(patch_save_set[i].region)); + } + if (!j) + util_out_print("None.!/", TRUE); + return; + } + util_out_print("!/Save history:!/", TRUE); + for (i = j = 0; i < patch_save_count; i++) + { + util_len = SIZEOF("Block "); + memcpy(util_buff, "Block ", util_len); + util_len += i2hex_nofill(patch_save_set[i].blk, (uchar_ptr_t)&util_buff[util_len-1], 8); + util_buff[util_len-1] = 0; + util_out_print(util_buff, TRUE); + j++; + if (*patch_save_set[i].comment) + { + util_out_print("Version !UL Region !AD Comment: !AD!/", TRUE, + patch_save_set[i].ver, REG_LEN_STR(patch_save_set[i].region), + LEN_AND_STR(patch_save_set[i].comment)); + + } else + { + util_out_print("Version !UL Region !AD!/", TRUE, patch_save_set[i].ver, + REG_LEN_STR(patch_save_set[i].region)); + } + } + if (!j) + util_out_print(" None.!/", TRUE); + return; + } + j = 1; + for (i = 0; i < patch_save_count; i++) + if (patch_save_set[i].blk == blk && patch_save_set[i].region == gv_cur_region + && patch_save_set[i].ver >= j) + j = patch_save_set[i].ver + 1; + util_len = SIZEOF("!/Saving version !UL of block "); + memcpy(util_buff, "!/Saving version !UL of block ", util_len); + util_len += i2hex_nofill(blk, (uchar_ptr_t)&util_buff[util_len-1], 8); + util_buff[util_len-1] = 0; + util_out_print(util_buff, TRUE, j); + patch_save_set[patch_save_count].ver = j; + patch_save_set[patch_save_count].blk = blk; + patch_save_set[patch_save_count].region = gv_cur_region; + patch_save_set[patch_save_count].bp = (char *)malloc(cs_addrs->hdr->blk_size); + if (blk >= cs_addrs->ti->total_blks) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + was_crit = cs_addrs->now_crit; + nocrit_present = (CLI_NEGATED == cli_present("CRIT")); + DSE_GRAB_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + if (!(bp = t_qread(blk, &dummy_int, &dummy_cr))) + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + memcpy(patch_save_set[patch_save_count].bp, bp, cs_addrs->hdr->blk_size); + DSE_REL_CRIT_AS_APPROPRIATE(was_crit, nocrit_present, cs_addrs, gv_cur_region); + buff_len = SIZEOF(buff); + if ((cli_present("COMMENT") == CLI_PRESENT) && cli_get_str("COMMENT", buff, &buff_len)) + { + ptr = &buff[buff_len]; + *ptr = 0; + j = (unsigned int)(ptr - &buff[0] + 1); + patch_save_set[patch_save_count].comment = (char *)malloc(j); + memcpy(patch_save_set[patch_save_count].comment, &buff[0], j); + } else + patch_save_set[patch_save_count].comment = ""; + patch_save_count++; + return; +} diff --git a/sr_port/dse_shift.c b/sr_port/dse_shift.c new file mode 100644 index 0000000..2f1deb5 --- /dev/null +++ b/sr_port/dse_shift.c @@ -0,0 +1,167 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "min_max.h" /* needed for gdsblkops.h */ +#include "gdsblkops.h" +#include "gdscc.h" +#include "dse.h" +#include "cli.h" +#include "filestruct.h" +#include "jnl.h" +#include "util.h" + +/* Include prototypes */ +#include "t_qread.h" +#include "t_write.h" +#include "t_end.h" +#include "t_begin_crit.h" +#include "gvcst_blk_build.h" +#include "t_abort.h" + +GBLREF char *update_array, *update_array_ptr; +GBLREF gd_region *gv_cur_region; +GBLREF uint4 update_array_size; +GBLREF srch_hist dummy_hist; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF gd_addr *gd_header; +GBLREF block_id patch_curr_blk; +GBLREF cw_set_element cw_set[]; +GBLREF unsigned char *non_tp_jfb_buff_ptr; + +void dse_shift(void) +{ + bool forward; + uint4 offset, shift; + int4 size; + sm_uc_ptr_t bp; + uchar_ptr_t lbp; + blk_segment *bs1, *bs_ptr; + int4 blk_seg_cnt, blk_size; + srch_blk_status blkhist; + + error_def(ERR_DBRDONLY); + error_def(ERR_DSEBLKRDFAIL); + error_def(ERR_DSEFAIL); + + if (gv_cur_region->read_only) + rts_error(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); + CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ + if (patch_curr_blk < 0 || patch_curr_blk >= cs_addrs->ti->total_blks || !(patch_curr_blk % cs_addrs->hdr->bplmap)) + { + util_out_print("Error: invalid block number.", TRUE); + return; + } + if (cli_present("OFFSET") != CLI_PRESENT) + { + util_out_print("Error: offset must be specified.", TRUE); + return; + } + if (!cli_get_hex("OFFSET", &offset)) + return; + shift = 0; + if (cli_present("FORWARD") == CLI_PRESENT) + { + if (!cli_get_hex("FORWARD", &shift)) + return; + forward = TRUE; + lbp = (unsigned char *)malloc((size_t)shift); + } else if (cli_present("BACKWARD") == CLI_PRESENT) + { + if (!cli_get_hex("BACKWARD", &shift)) + return; + if (shift > offset) + { + util_out_print("Error: shift greater than offset not allowed.", TRUE); + return; + } + forward = FALSE; + lbp = (unsigned char *)0; + } + if (!shift) + { + util_out_print("Error: must specify amount to shift.", TRUE); + if (lbp) + free(lbp); + return; + } + blk_size = cs_addrs->hdr->blk_size; + t_begin_crit(ERR_DSEFAIL); + blkhist.blk_num = patch_curr_blk; + if (!(blkhist.buffaddr = t_qread(blkhist.blk_num, &blkhist.cycle, &blkhist.cr))) + { + if (lbp) + free(lbp); + rts_error(VARLSTCNT(1) ERR_DSEBLKRDFAIL); + } + bp = blkhist.buffaddr; + size = ((blk_hdr *)bp)->bsiz; + if (size < 0) + size = 0; + else if (size > cs_addrs->hdr->blk_size) + size = cs_addrs->hdr->blk_size; + if (offset < SIZEOF(blk_hdr) || offset > size) + { + util_out_print("Error: offset not in range of block.", TRUE); + t_abort(gv_cur_region, cs_addrs); + if (lbp) + free(lbp); + return; + } + BLK_INIT(bs_ptr, bs1); + if (forward) + { + if (shift + size >= cs_addrs->hdr->blk_size) + { + util_out_print("Error: block not large enough to accomodate shift.", TRUE); + t_abort(gv_cur_region, cs_addrs); + if (lbp) + free(lbp); + return; + } + memset(lbp, 0, shift); + BLK_SEG(bs_ptr, bp + SIZEOF(blk_hdr), offset - SIZEOF(blk_hdr)); + BLK_SEG(bs_ptr, lbp, shift); + if (size - offset) + BLK_SEG(bs_ptr, bp + offset, size - offset); + } else + { + if (shift > offset) + shift = offset - SIZEOF(blk_hdr); + if (offset - shift > SIZEOF(blk_hdr)) + BLK_SEG(bs_ptr, bp + SIZEOF(blk_hdr), offset - shift - SIZEOF(blk_hdr)); + if (size - offset) + BLK_SEG(bs_ptr, bp + offset, size - offset); + } + if (!BLK_FINI(bs_ptr, bs1)) + { + util_out_print("Error: bad blk build.", TRUE); + t_abort(gv_cur_region, cs_addrs); + if (lbp) + free(lbp); + return; + } + t_write(&blkhist, (unsigned char *)bs1, 0, 0, ((blk_hdr_ptr_t)bp)->levl, TRUE, FALSE, GDS_WRITE_KILLTN); + BUILD_AIMG_IF_JNL_ENABLED(cs_data, non_tp_jfb_buff_ptr, cs_addrs->ti->curr_tn); + t_end(&dummy_hist, NULL, TN_NOT_SPECIFIED); + return; +} diff --git a/sr_port/dse_wcreinit.c b/sr_port/dse_wcreinit.c new file mode 100644 index 0000000..cb4a145 --- /dev/null +++ b/sr_port/dse_wcreinit.c @@ -0,0 +1,91 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "cli.h" +#include "dse.h" + +#ifdef VMS +#define GET_CONFIRM(X,Y) {if(!cli_get_str("CONFIRMATION",(X),&(Y))) {rts_error(VARLSTCNT(1) ERR_DSEWCINITCON); \ + return;}} +#endif +#ifdef UNIX +#include "gtm_stdio.h" +#define GET_CONFIRM(X,Y) {PRINTF("CONFIRMATION: ");FGETS((X), (Y), stdin, fgets_res);Y = strlen(X);} + +#endif + +GBLREF gd_region *gv_cur_region; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF short crash_count; + +void dse_wcreinit (void) +{ + unsigned char *c; + char confirm[256]; + uint4 large_block; + short len; + boolean_t was_crit; +# ifdef UNIX + char *fgets_res; +# endif + + error_def(ERR_DSEWCINITCON); + error_def(ERR_DSEINVLCLUSFN); + error_def(ERR_DSEONLYBGMM); + error_def(ERR_DBRDONLY); + + if (gv_cur_region->read_only) + rts_error(VARLSTCNT(4) ERR_DBRDONLY, 2, DB_LEN_STR(gv_cur_region)); + + if (cs_addrs->hdr->clustered) + { + rts_error(VARLSTCNT(1) ERR_DSEINVLCLUSFN); + return; + } + if (cs_addrs->critical) + crash_count = cs_addrs->critical->crashcnt; + len = SIZEOF(confirm); + GET_CONFIRM(confirm,len); + if (confirm[0] != 'Y' && confirm[0] != 'y') + { + rts_error(VARLSTCNT(1) ERR_DSEWCINITCON); + return; + } + if (cs_addrs->hdr->acc_meth != dba_bg && cs_addrs->hdr->acc_meth != dba_mm) + { + rts_error(VARLSTCNT(4) ERR_DSEONLYBGMM, 2, LEN_AND_LIT("WCINIT")); + return; + } + was_crit = cs_addrs->now_crit; + if (!was_crit) + grab_crit(gv_cur_region); + bt_init(cs_addrs); + if (cs_addrs->hdr->acc_meth == dba_bg) + { + bt_refresh(cs_addrs); + db_csh_ini(cs_addrs); + db_csh_ref(cs_addrs); + } + if (!was_crit) + rel_crit (gv_cur_region); + + return; +} diff --git a/sr_port/dsefind.h b/sr_port/dsefind.h new file mode 100644 index 0000000..68cd680 --- /dev/null +++ b/sr_port/dsefind.h @@ -0,0 +1,22 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +typedef struct global_dir_path_struct +{ block_id block; + int4 offset; + struct global_dir_path_struct *next; +}global_dir_path; + +typedef struct global_root_list_struct +{ block_id root; + global_dir_path *dir_path; + struct global_root_list_struct *link; +}global_root_list; diff --git a/sr_port/dump_lockhist.c b/sr_port/dump_lockhist.c new file mode 100644 index 0000000..5e4c662 --- /dev/null +++ b/sr_port/dump_lockhist.c @@ -0,0 +1,59 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsfhead.h" +#include "filestruct.h" + +GBLREF gd_region *gv_cur_region; +GBLREF int process_id; + +/* Routine to dump the lock history array on demand starting with most recent and working backwards */ + +void dump_lockhist(void) +{ +#ifdef DEBUG /* should only be called conditional on DEBUG, but play it safe */ + int4 lockIdx, lockIdx_first; + node_local_ptr_t locknl; + + locknl = FILE_INFO(gv_cur_region)->s_addrs.nl; + FPRINTF(stderr, "\nProcess lock history (in reverse order) -- Current pid: %d\n", process_id); /* Print headers */ + FPRINTF(stderr, "Func LockAddr Caller Pid Retry TrIdx\n"); + FPRINTF(stderr, "----------------------------------------------------------\n"); + for (lockIdx_first = lockIdx = locknl->lockhist_idx; ;) + { + if (NULL != locknl->lockhists[lockIdx].lock_addr) + { + FPRINTF(stderr, "%.4s %16lx %16lx %6d %6d %d\n", + locknl->lockhists[lockIdx].lock_op, + locknl->lockhists[lockIdx].lock_addr, + locknl->lockhists[lockIdx].lock_callr, + locknl->lockhists[lockIdx].lock_pid, + locknl->lockhists[lockIdx].loop_cnt, + lockIdx); + } + if (--lockIdx < 0) /* If we have fallen off the short end.. */ + lockIdx = LOCKHIST_ARRAY_SIZE - 1; /* .. move to the tall end */ + if (lockIdx == lockIdx_first) + break; /* Completed the loop */ + } + FPRINTF(stderr,"\0"); + fflush(stderr); +#endif +} diff --git a/sr_port/dump_record.c b/sr_port/dump_record.c new file mode 100644 index 0000000..b62237b --- /dev/null +++ b/sr_port/dump_record.c @@ -0,0 +1,271 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include + +#include "gtmctype.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "cli.h" +#include "copy.h" +#include "min_max.h" /* needed for init_root_gv.h */ +#include "init_root_gv.h" +#include "util.h" +#include "dse.h" +#include "print_target.h" +#include "op.h" + +#ifdef GTM_TRIGGER +#include "hashtab_mname.h" +#include "rtnhdr.h" /* needed for gv_trigger.h */ +#include "gv_trigger.h" /* needed for INIT_ROOT_GVT */ +#include "targ_alloc.h" +#endif +#ifdef UNICODE_SUPPORTED +#include "gtm_icu_api.h" +#include "gtm_utf8.h" +#endif + + +GBLDEF bool wide_out; +GBLDEF char patch_comp_key[MAX_KEY_SZ + 1]; +GBLDEF unsigned char patch_comp_count; +GBLDEF int patch_rec_counter; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF VSIG_ATOMIC_T util_interrupt; +GBLREF mval curr_gbl_root; +GBLREF int patch_is_fdmp; +GBLREF int patch_fdmp_recs; +GBLREF gv_key *gv_currkey; +GBLREF gv_namehead *gv_target; + +LITREF mval literal_hasht; + +#define MAX_UTIL_LEN 80 +#define NUM_BYTES_PER_LINE 20 + +sm_uc_ptr_t dump_record(sm_uc_ptr_t rp, block_id blk, sm_uc_ptr_t bp, sm_uc_ptr_t b_top) +{ + sm_uc_ptr_t r_top, key_top, cptr0, cptr1, cptr_top, cptr_base = NULL, cptr_next = NULL; + char key_buf[MAX_KEY_SZ + 1], *temp_ptr, *temp_key, util_buff[MAX_UTIL_LEN]; + char *prefix_str, *space_str, *dot_str, *format_str; + unsigned char cc; + short int size; + int4 util_len, head; + uint4 ch; + int buf_len, field_width,fastate, chwidth = 0; + ssize_t chlen; + block_id blk_id; + boolean_t rechdr_displayed = FALSE; + sgmnt_addrs *csa; + + if (rp >= b_top) + return NULL; + head = cli_present("HEADER"); + GET_SHORT(size, &((rec_hdr_ptr_t)rp)->rsiz); + cc = ((rec_hdr_ptr_t)rp)->cmpc; + if ((CLI_NEGATED != head) && !patch_is_fdmp) + { + MEMCPY_LIT(util_buff, "Rec:"); + util_len = SIZEOF("Rec:") - 1; + util_len += i2hex_nofill(patch_rec_counter, (uchar_ptr_t)&util_buff[util_len], 4); + MEMCPY_LIT(&util_buff[util_len], " Blk "); + util_len += SIZEOF(" Blk ") - 1; + util_len += i2hex_nofill(blk, (uchar_ptr_t)&util_buff[util_len], 8); + MEMCPY_LIT(&util_buff[util_len], " Off "); + util_len += SIZEOF(" Off ") - 1; + util_len += i2hex_nofill((int)(rp - bp), (uchar_ptr_t)&util_buff[util_len], 4); + MEMCPY_LIT(&util_buff[util_len], " Size "); + util_len += SIZEOF(" Size ") - 1; + util_len += i2hex_nofill(size, (uchar_ptr_t)&util_buff[util_len], 4); + MEMCPY_LIT(&util_buff[util_len], " Cmpc "); + util_len += SIZEOF(" Cmpc ") - 1; + util_len += i2hex_nofill(cc, (uchar_ptr_t)&util_buff[util_len], 2); + MEMCPY_LIT(&util_buff[util_len], " "); + util_len += SIZEOF(" ") - 1; + util_buff[util_len] = 0; + util_out_print(util_buff, FALSE); + } + r_top = rp + size; + if (r_top > b_top) + r_top = b_top; + else if (r_top < rp + SIZEOF(rec_hdr)) + r_top = rp + SIZEOF(rec_hdr); + if (cc > patch_comp_count) + cc = patch_comp_count; + if (((blk_hdr_ptr_t)bp)->levl) + key_top = r_top - SIZEOF(block_id); + else + { + for (key_top = rp + SIZEOF(rec_hdr); key_top < r_top;) + if (!*key_top++ && !*key_top++) + break; + } + size = key_top - rp - SIZEOF(rec_hdr); + if (size > SIZEOF(patch_comp_key) - 2 - cc) + size = SIZEOF(patch_comp_key) - 2 - cc; + if (size < 0) + size = 0; + memcpy(&patch_comp_key[cc], rp + SIZEOF(rec_hdr), size); + patch_comp_count = cc + size; + patch_comp_key[patch_comp_count] = patch_comp_key[patch_comp_count + 1] = 0; + if (patch_is_fdmp) + { + if (dse_fdmp(key_top, (int)(r_top - key_top))) + patch_fdmp_recs++; + } else + { + if (r_top - SIZEOF(block_id) >= key_top) + { + GET_LONG(blk_id, key_top); + if ((((blk_hdr_ptr_t)bp)->levl) || (blk_id <= cs_addrs->ti->total_blks)) + { + MEMCPY_LIT(util_buff, "Ptr "); + util_len = SIZEOF("Ptr ") - 1; + util_len += i2hex_nofill(blk_id, (uchar_ptr_t)&util_buff[util_len], SIZEOF(blk_id) * 2); + MEMCPY_LIT(&util_buff[util_len], " "); + util_len += SIZEOF(" ") - 1; + util_buff[util_len] = 0; + util_out_print(util_buff, FALSE); + } + } + util_out_print("Key ", FALSE); + if (r_top == b_top + && ((blk_hdr_ptr_t)bp)->levl && !((rec_hdr_ptr_t)rp)->cmpc + && r_top - rp == SIZEOF(rec_hdr) + SIZEOF(block_id)) + util_out_print("*", FALSE); + else if (patch_comp_key[0]) + { + util_out_print("^", FALSE); + csa = cs_addrs; + RETRIEVE_ROOT_VAL(patch_comp_key, key_buf, temp_ptr, temp_key, buf_len); + INIT_ROOT_GVT(key_buf, buf_len, curr_gbl_root); + } + print_target((uchar_ptr_t)patch_comp_key); + util_out_print(0, TRUE); + if (CLI_PRESENT != head) + { + prefix_str = " |"; + if (wide_out) + { + format_str = " !AD"; + dot_str = " ."; + space_str = " "; + field_width = 4; + } else + { + format_str = " !AD"; + dot_str = " ."; + space_str = " "; + field_width = 3; + } + fastate = 0; + for (cptr0 = rp; cptr0 < r_top; cptr0 += NUM_BYTES_PER_LINE) + { + if (util_interrupt) + { /* return, rather than signal ERR_CTRLC so that the calling routine + can deal with that signal and do the appropriate cleanup */ + return NULL; + } + util_len = 8; + i2hex_blkfill((int)(cptr0 - bp), (uchar_ptr_t)util_buff, 8); + MEMCPY_LIT(&util_buff[util_len], " : |"); + util_len += SIZEOF(" : |") - 1; + util_buff[util_len] = 0; + util_out_print(util_buff, FALSE); + /* Dump hexadecimal byte values */ + for (cptr1 = cptr0; cptr1 < (cptr0 + NUM_BYTES_PER_LINE); cptr1++) + { + if (cptr1 < r_top) + { + i2hex_blkfill(*(sm_uc_ptr_t)cptr1, (uchar_ptr_t)util_buff, field_width); + util_buff[field_width] = 0; + util_out_print(util_buff, FALSE); + } else + util_out_print(space_str, FALSE); + } + util_out_print("|", TRUE); + util_out_print(prefix_str, FALSE); + /* Display character/wide-character glyphs */ + for (cptr1 = cptr0, cptr_top = cptr0 + NUM_BYTES_PER_LINE; cptr1 < cptr_top; cptr1++) + { + if (!rechdr_displayed && (cptr1 == (rp + SIZEOF(rec_hdr)))) + rechdr_displayed = TRUE; + assert(rechdr_displayed || (cptr1 < (rp + SIZEOF(rec_hdr)))); + assert(!rechdr_displayed || (cptr1 >= (rp + SIZEOF(rec_hdr)))); + switch (fastate) + { + case 0: /* prints single-byte characters or intepret + multi-byte characters */ + if (cptr1 >= r_top) + util_out_print(space_str, FALSE); + else if (!gtm_utf8_mode || IS_ASCII(*cptr1) || !rechdr_displayed) + { /* single-byte characters */ + if (PRINTABLE(*(sm_uc_ptr_t)cptr1)) + util_out_print(format_str, FALSE, 1, cptr1); + else + util_out_print(dot_str, FALSE); + } +#ifdef UNICODE_SUPPORTED + else { /* multi-byte characters */ + cptr_next = UTF8_MBTOWC(cptr1, r_top, ch); + chlen = cptr_next - cptr1; + if (WEOF == ch || !U_ISPRINT(ch)) + { /* illegal or non-printable characters */ + cptr1--; + fastate = 1; + } else + { /* multi-byte printable characters */ + cptr_base = cptr1; + chwidth = UTF8_WCWIDTH(ch); + assert(chwidth >= 0 && chwidth <= 2); + cptr1--; + fastate = 2; + } + } +#endif + break; + + case 1: /* illegal or non-printable characters */ + util_out_print(dot_str, FALSE); + if (--chlen <= 0) + fastate = 0; + break; + + case 2: /* printable multi-byte characters */ + if (chlen-- > 1) /* fill leading bytes with spaces */ + util_out_print(space_str, FALSE); + else + { + util_out_print("!AD", FALSE, field_width - chwidth, space_str); + if (0 < chwidth) + util_out_print("!AD", FALSE, cptr_next - cptr_base, cptr_base); + fastate = 0; + } + break; + } + } + util_out_print("|", TRUE); + } + } + if (CLI_NEGATED != head) + util_out_print(0, TRUE); + } + return (r_top == b_top) ? NULL : r_top; +} diff --git a/sr_port/dumptable.c b/sr_port/dumptable.c new file mode 100644 index 0000000..bac5cd0 --- /dev/null +++ b/sr_port/dumptable.c @@ -0,0 +1,60 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "compiler.h" +#include "list_file.h" +#include "dumptable.h" + +static readonly char argtype[][6] = +{ + "","TVAR ","TVAL ","TINT ","TVAD " + ,"TCAD ","VREG ","MLIT ","MVAR " + ,"TRIP ","TNXT ","TJMP ","INDR " + ,"MLAB ","ILIT ","CDLT ","TEMP " +}; +static readonly char start8[] = " stored at r8 + "; +static readonly char start9[] = " stored at r9 + "; + +GBLREF int4 sa_temps[]; +GBLREF int4 sa_temps_offset[]; + +void dumptable(void) +{ + char outbuf[256]; + int i; + unsigned char *c; + + for (i=1; i <= TCAD_REF ; i++) + { c = (unsigned char *)&outbuf[0]; + memcpy(c,argtype[i],5); + c += 5; + *c++ = ' '; + c = i2asc(c,sa_temps[i]); + + if (i == TVAR_REF) + { memcpy(c, &start8[0], SIZEOF(start8) - 1); + c += SIZEOF(start8) - 1; + } + else + { memcpy(c, &start9[0], SIZEOF(start9) - 1); + c += SIZEOF(start9) - 1; + } + + c = i2asc(c,sa_temps_offset[i]); + *c++ = 0; + list_tab(); + list_line(outbuf); + } +} diff --git a/sr_port/dumptable.h b/sr_port/dumptable.h new file mode 100644 index 0000000..db7d75e --- /dev/null +++ b/sr_port/dumptable.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef DUMPTABLE_INCLUDED +#define DUMPTABLE_INCLUDED + +void dumptable(void); /***type int added***/ + +#endif /* DUMPTABLE_INCLUDED */ diff --git a/sr_port/eb_muldiv.c b/sr_port/eb_muldiv.c new file mode 100644 index 0000000..a805690 --- /dev/null +++ b/sr_port/eb_muldiv.c @@ -0,0 +1,316 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* eb_muldiv - emulate extended precision (18-digit) multiplication and division */ + +#include "mdef.h" + +#include "arit.h" +#include "eb_muldiv.h" + +#define FLO_HI 1e9 +#define FLO_LO 1e8 +#define FLO_BIAS 1e3 + +#define DFLOAT2MINT(X,DF) (X[1] = DF, X[0] = (DF - (double)X[1])*FLO_HI) + +#define RADIX 10000 + + +/* eb_int_mul - multiply two GT.M INT's + * + * input + * v1, u1 - GT.M INT's + * + * output + * if the product will fit into a GT.M INT format: + * function result = FALSE => no promotion necessary + * p[] = INT product of (v1*m1) + * else (implies overflow out of INT format): + * function result = TRUE => promotion to extended precision necessary + * p[] = undefined + */ + +bool eb_int_mul (int4 v1, int4 u1, int4 p[]) +{ + double pf; + int4 tp[2], promote; + + promote = TRUE; /* promote if overflow or too many significant fractional digits */ + pf = (double)u1*(double)v1/FLO_BIAS; + if ((pf < FLO_HI) && (pf > -FLO_HI)) + { + DFLOAT2MINT(tp, pf); + if (tp[0] == 0) /* don't need extra precision */ + { + promote = FALSE; + p[0] = tp[0]; p[1] = tp[1]; + } + } + return promote; +} + + +/* eb_mul - multiply two GT.M extended precision numeric values + * + * input + * v[], u[] - GT.M extended precision numeric value mantissas + * + * output + * function result = scale factor of result + * p[] = GT.M extended precision mantissa of (u*v) + */ + +int4 eb_mul (int4 v[], int4 u[], int4 p[]) /* p = u*v */ +{ + short i, j; + int4 acc, carry, m1[5], m2[5], prod[9], scale; + + /* Throughout, larger index => more significance. */ + + for (i = 0 ; i < 9 ; i++) + prod[i] = 0; + + /* Break up 2-4-(3/1)-4-4 */ + + m1[0] = v[0] % RADIX; + m2[0] = u[0] % RADIX; + + m1[1] = (v[0]/RADIX) % RADIX; + m2[1] = (u[0]/RADIX) % RADIX; + + m1[2] = (v[1] % (RADIX/10))*10 + v[0]/(RADIX*RADIX); + m2[2] = (u[1] % (RADIX/10))*10 + u[0]/(RADIX*RADIX); + + m1[3] = (v[1]/(RADIX/10)) % RADIX; + m2[3] = (u[1]/(RADIX/10)) % RADIX; + + m1[4] = v[1]/((RADIX/10)*RADIX); + m2[4] = u[1]/((RADIX/10)*RADIX); + + for (j = 0 ; j <= 4 ; j++) + { + if (m2[j] != 0) + { + for (i = 0, carry = 0 ; i <= 4 ; i++) + { + acc = m1[i]*m2[j] + prod[i+j] + carry; + prod[i+j] = acc % RADIX; + carry = acc / RADIX; + } + if ( 9 > i+j) + prod[i+j] = carry; + else + if (0 != carry) + assert(FALSE); + } + } + + if (prod[8] >= RADIX/10) + { + /* Assemble back 4-4-1/3-4-2 */ + scale = 0; /* no scaling needed */ + p[0] = ((prod[6]%1000)*RADIX + prod[5])*(RADIX/ 100) + (prod[4]/ 100); + p[1] = ( prod[8] *RADIX + prod[7])*(RADIX/1000) + (prod[6]/1000); + } + else /* prod[8] < RADIX/10 [means not normalized] */ + { + /* Assemble back 3-4-2/2-4-3 */ + scale = -1; /* to compensate for normalization */ + p[0] = ((prod[6]%100)*RADIX + prod[5])*(RADIX/ 10) + (prod[4]/ 10); + p[1] = ( prod[8] *RADIX + prod[7])*(RADIX/100) + (prod[6]/100); + } + + return scale; +} + + +/* eb_mvint_div - divide to GT.M INT's + * + * input + * v, u - INT's to be divided + * + * output + * if the quotient will fit into a GT.M INT: + * function value = FALSE => no promotion necessary + * q[] = INT quotient of (v/u) + * else (implies overflow out of GT.M INT forat): + * function value = TRUE => promotion to extended precision necessary + * q[] = undefined + */ + +bool eb_mvint_div (int4 v, int4 u, int4 q[]) +{ + double qf; + int4 tq[2], promote; + + promote = TRUE; /* promote if overflow or too many significant fractional digits */ + qf = (double)v*FLO_BIAS/(double)u; + if ((qf < FLO_HI) && (qf > -FLO_HI)) + { + DFLOAT2MINT(tq, qf); + if (tq[0] == 0) /* don't need extra word of precision */ + { + promote = FALSE; + q[0] = tq[0]; q[1] = tq[1]; + } + } + return promote; +} + + +/* eb_int_div - integer division of two GT.M INT's + * + * input + * v1, u1 - GT.M INT's to be divided + * + * output + * if result fits into a GT.M INT: + * function value = FALSE => no promotion necessary + * q[] = INT result of (v1\u1) + * else (implies some sort of overflow): + * function result = TRUE => promotion to extended precision necessary + * q[] = undefined + */ + +bool eb_int_div (int4 v1, int4 u1, int4 q[]) +{ + double qf; + qf= (double)v1*FLO_BIAS/(double)u1; + if (qf < FLO_HI && qf > -FLO_HI) + { + DFLOAT2MINT(q,qf); + q[1]= (q[1]/MV_BIAS)*MV_BIAS; + return FALSE; + } + else + { + return TRUE; + } +} + + +/* eb_div - divide two GT.M extended precision numeric values + * + * input + * x[], y[] - GT.M extended precision numeric value mantissas + * + * output + * function result = scale factor of result + * q[] = GT.M extended precision mantissa of (y/x) + */ + +int4 eb_div (int4 x[], int4 y[], int4 q[]) /* q = y/x */ +{ + int4 borrow, carry, i, j, scale, prod, qx[5], xx[5], yx[10]; + + for (i = 0 ; i < 5 ; i++) + yx[i] = 0; + if (x[1] < y[1] || (x[1] == y[1] && x[0] <= y[0])) /* i.e., if x <= y */ + { + /* Break y apart 3-4-2/2-4-3 */ + scale = 1; + yx[5] = (y[0]%(RADIX/10))*10; + yx[6] = (y[0]/(RADIX/10))%RADIX; + yx[7] = (y[1]%(RADIX/100))*(RADIX/100) + y[0]/((RADIX/10)*RADIX); + yx[8] = (y[1]/(RADIX/100))%RADIX; + yx[9] = y[1]/((RADIX/100)*RADIX); + } + else + { + /* Break y apart 4-4-1/3-4-2 */ + scale = 0; + yx[5] = (y[0]%(RADIX/100))*100; + yx[6] = (y[0]/(RADIX/100))%RADIX; + yx[7] = (y[1]%(RADIX/1000))*(RADIX/10) + y[0]/((RADIX/100)*RADIX); + yx[8] = (y[1]/(RADIX/1000))%RADIX; + yx[9] = y[1]/((RADIX/1000)*RADIX); + } + /* Break x apart 4-4-1/3-4-2 */ + xx[0] = (x[0]%(RADIX/100))*100; + xx[1] = (x[0]/(RADIX/100))%RADIX; + xx[2] = (x[1]%(RADIX/1000))*(RADIX/10) + x[0]/((RADIX/100)*RADIX); + xx[3] = (x[1]/(RADIX/1000))%RADIX; + xx[4] = x[1]/((RADIX/1000)*RADIX); + + assert (yx[9] <= xx[4]); + for (i = 4 ; i >= 0 ; i--) + { + qx[i] = (yx[i+5]*RADIX + yx[i+4]) / xx[4]; + if (qx[i] != 0) + { + /* Multiply x by qx[i] and subtract from remainder. */ + for (j = 0, borrow = 0 ; j <= 4 ; j++) + { + prod = qx[i]*xx[j] + borrow; + borrow = prod/RADIX; + yx[i+j] -= (prod%RADIX); + if (yx[i+j] < 0) + { + yx[i+j] += RADIX; + borrow ++; + } + } + yx[i+5] -= borrow; + + while (yx[i+5] < 0) + { + qx[i] --; /* estimate too high */ + for (j = 0, carry = 0 ; j <= 4 ; j++) + { + yx[i+j] += (xx[j] + carry); + carry = yx[i+j]/RADIX; + yx[i+j] %= RADIX; + } + yx[i+5] += carry; + } + } + assert (0 <= qx[i] && qx[i] < RADIX); /* make sure in range */ + assert (yx[i+5] == 0); /* check that remainder doesn't overflow */ + } + + /* Assemble q 4-4-1/3-4-2 */ + q[0] = ((qx[2]%1000)*RADIX + qx[1])*100 + (qx[0]/ 100); + q[1] = ( qx[4] *RADIX + qx[3])* 10 + (qx[2]/1000); + + assert ( (FLO_LO <= q[1] && q[1] < FLO_HI) + || (q[1] == 0 && q[0] == 0 && y[1] == 0 && y[0] == 0) ); + + return scale; +} + + +/* eb_int_mod - INT modulus of two GT.M INT's + * + * input + * v1, u1 - GT.M INT's + * + * output + * p[] = INT value of (v1 mod u1) == (v1 - (u1*floor(v1/u1))) + */ + +void eb_int_mod (int4 v1, int4 u1, int4 p[]) +{ + int4 quo, rat, neg; + + if (u1 == 0 || v1 == 0) + { + p[1]= 0; + } + else + { + quo = v1/u1; + rat = v1 != quo*u1; + neg = (v1 < 0 && u1 > 0) || (v1 > 0 && u1 < 0); + p[1] = v1 - u1*(quo - (neg && rat)); + } + return; +} diff --git a/sr_port/eb_muldiv.h b/sr_port/eb_muldiv.h new file mode 100644 index 0000000..98ca462 --- /dev/null +++ b/sr_port/eb_muldiv.h @@ -0,0 +1,22 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __EB_MULDIV_H__ +#define __EB_MULDIV_H__ + +bool eb_int_mul (int4 v1, int4 u1, int4 p[]); +int4 eb_mul (int4 v[], int4 u[], int4 p[]); /* p = u*v */ +bool eb_mvint_div (int4 v, int4 u, int4 q[]); +bool eb_int_div (int4 v1, int4 u1, int4 q[]); +int4 eb_div (int4 x[], int4 y[], int4 q[]); /* q = y/x */ +void eb_int_mod (int4 v1, int4 u1, int4 p[]); + +#endif diff --git a/sr_port/ebc_xlat.c b/sr_port/ebc_xlat.c new file mode 100644 index 0000000..a7ebf36 --- /dev/null +++ b/sr_port/ebc_xlat.c @@ -0,0 +1,125 @@ +/**************************************************************** + * * + * Copyright 2001, 2003 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/**************************************************************** +* * +* This module provides translation functions between the * +* ASCII and EBCDIC code sets. * +* * +****************************************************************/ + + + +#include "mdef.h" +#include "ebc_xlat.h" + + +/* Translation tables */ +/* These were generated using iconv between "ISO8859-1" (ASCII) */ +/* and "IBM-1047" (EBCDIC)); */ + +/* EBCDIC to ASCII */ + +LITDEF unsigned char e2a[256] = +{ + /* 00 - 07: */ 0x0, 0x1, 0x2, 0x3, 0x9c, 0x9, 0x86, 0x7f, + /* 08 - 0f: */ 0x97, 0x8d, 0x8e, 0xb, 0xc, 0xd, 0xe, 0xf, + /* 10 - 17: */ 0x10, 0x11, 0x12, 0x13, 0x9d, 0xa, 0x8, 0x87, + /* 18 - 1f: */ 0x18, 0x19, 0x92, 0x8f, 0x1c, 0x1d, 0x1e, 0x1f, + /* 20 - 27: */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x17, 0x1b, + /* 28 - 2f: */ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x5, 0x6, 0x7, + /* 30 - 37: */ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x4, + /* 38 - 3f: */ 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, + /* 40 - 47: */ 0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5, + /* 48 - 4f: */ 0xe7, 0xf1, 0xa2, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, + /* 50 - 57: */ 0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef, + /* 58 - 5f: */ 0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x5e, + /* 60 - 67: */ 0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5, + /* 68 - 6f: */ 0xc7, 0xd1, 0xa6, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, + /* 70 - 77: */ 0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf, + /* 78 - 7f: */ 0xcc, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, + /* 80 - 87: */ 0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + /* 88 - 8f: */ 0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, + /* 90 - 97: */ 0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, + /* 98 - 9f: */ 0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, + /* a0 - a7: */ 0xb5, 0x7e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + /* a8 - af: */ 0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0x5b, 0xde, 0xae, + /* b0 - b7: */ 0xac, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc, + /* b8 - bf: */ 0xbd, 0xbe, 0xdd, 0xa8, 0xaf, 0x5d, 0xb4, 0xd7, + /* c0 - c7: */ 0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + /* c8 - cf: */ 0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, + /* d0 - d7: */ 0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, + /* d8 - df: */ 0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xf9, 0xfa, 0xff, + /* e0 - e7: */ 0x5c, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + /* e8 - ef: */ 0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, + /* f0 - f7: */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + /* f8 - ff: */ 0x38, 0x39, 0xb3, 0xdb, 0xdc, 0xd9, 0xda, 0x9f +}; + + + +/* ASCII to EBCDIC */ + +LITDEF unsigned char a2e[256] = +{ + /* 00 - 07: */ 0x0, 0x1, 0x2, 0x3, 0x37, 0x2d, 0x2e, 0x2f, + /* 08 - 0f: */ 0x16, 0x5, 0x15, 0xb, 0xc, 0xd, 0xe, 0xf, + /* 10 - 17: */ 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, + /* 18 - 1f: */ 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, + /* 20 - 27: */ 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, + /* 28 - 2f: */ 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, + /* 30 - 37: */ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + /* 38 - 3f: */ 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, + /* 40 - 47: */ 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + /* 48 - 4f: */ 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + /* 50 - 57: */ 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, + /* 58 - 5f: */ 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x5f, 0x6d, + /* 60 - 67: */ 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + /* 68 - 6f: */ 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + /* 70 - 77: */ 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, + /* 78 - 7f: */ 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0xa1, 0x7, + /* 80 - 87: */ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x6, 0x17, + /* 88 - 8f: */ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x9, 0xa, 0x1b, + /* 90 - 97: */ 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x8, + /* 98 - 9f: */ 0x38, 0x39, 0x3a, 0x3b, 0x4, 0x14, 0x3e, 0xff, + /* a0 - a7: */ 0x41, 0xaa, 0x4a, 0xb1, 0x9f, 0xb2, 0x6a, 0xb5, + /* a8 - af: */ 0xbb, 0xb4, 0x9a, 0x8a, 0xb0, 0xca, 0xaf, 0xbc, + /* b0 - b7: */ 0x90, 0x8f, 0xea, 0xfa, 0xbe, 0xa0, 0xb6, 0xb3, + /* b8 - bf: */ 0x9d, 0xda, 0x9b, 0x8b, 0xb7, 0xb8, 0xb9, 0xab, + /* c0 - c7: */ 0x64, 0x65, 0x62, 0x66, 0x63, 0x67, 0x9e, 0x68, + /* c8 - cf: */ 0x74, 0x71, 0x72, 0x73, 0x78, 0x75, 0x76, 0x77, + /* d0 - d7: */ 0xac, 0x69, 0xed, 0xee, 0xeb, 0xef, 0xec, 0xbf, + /* d8 - df: */ 0x80, 0xfd, 0xfe, 0xfb, 0xfc, 0xba, 0xae, 0x59, + /* e0 - e7: */ 0x44, 0x45, 0x42, 0x46, 0x43, 0x47, 0x9c, 0x48, + /* e8 - ef: */ 0x54, 0x51, 0x52, 0x53, 0x58, 0x55, 0x56, 0x57, + /* f0 - f7: */ 0x8c, 0x49, 0xcd, 0xce, 0xcb, 0xcf, 0xcc, 0xe1, + /* f8 - ff: */ 0x70, 0xdd, 0xde, 0xdb, 0xdc, 0x8d, 0x8e, 0xdf +}; + + + +void asc_to_ebc(unsigned char *estring_out, unsigned char *astring_in, int len) +{ + int i; + unsigned char *in_ptr, *out_ptr; + + for (i = 0, in_ptr = astring_in, out_ptr = estring_out; i < len; i++, in_ptr++, out_ptr++) + *out_ptr = a2e[*in_ptr]; +} + +void ebc_to_asc(unsigned char *astring_out, unsigned char *estring_in, int len) +{ + int i; + unsigned char *in_ptr, *out_ptr; + + for (i = 0, in_ptr = estring_in, out_ptr = astring_out; i < len; i++, in_ptr++, out_ptr++) + *out_ptr = e2a[*in_ptr]; +} diff --git a/sr_port/ebc_xlat.h b/sr_port/ebc_xlat.h new file mode 100644 index 0000000..a93144d --- /dev/null +++ b/sr_port/ebc_xlat.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#ifndef __EBC_XLAT_H__ +#define __EBC_XLAT_H__ + +void asc_to_ebc(unsigned char *estring_out, unsigned char *astring_in, int len); +void ebc_to_asc(unsigned char *astring_out, unsigned char *estring_in, int len); + +#endif diff --git a/sr_port/ecode_add.c b/sr_port/ecode_add.c new file mode 100644 index 0000000..24e805f --- /dev/null +++ b/sr_port/ecode_add.c @@ -0,0 +1,202 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" /* for memcpy() */ + +#include "min_max.h" /* for MIN macro */ +#include "rtnhdr.h" /* for stack_frame.h */ +#include "stack_frame.h" /* for stack_frame type */ +#include "error_trap.h" + +#include "get_command_line.h" /* for get_command_line() prototype */ +#include "dollar_zlevel.h" /* for dollar_zlevel() prototype */ + +GBLREF dollar_ecode_type dollar_ecode; /* structure containing $ECODE related information */ +GBLREF dollar_stack_type dollar_stack; /* structure containing $STACK related information */ +GBLREF stack_frame *error_frame; /* "frame_pointer" at the time of adding the current ECODE */ +GBLREF stack_frame *frame_pointer; + +#define INCR_ECODE_INDEX(ecode_index, str, strlen) \ +{ \ + memcpy(dollar_ecode.end, str, strlen); \ + dollar_ecode.array[ecode_index].ecode_str.addr = dollar_ecode.end; \ + dollar_ecode.array[ecode_index].ecode_str.len = strlen; \ + /* -1 below for not calculating the terminating ',' as part of this ECODE, \ + * but instead calculate that as part of the beginning of the next ECODE */ \ + dollar_ecode.end += strlen - 1; \ + ecode_index++; \ +} + +#define DECR_ECODE_INDEX(ecode_index) \ +{ \ + ecode_index--; \ + space_left += dollar_ecode.array[ecode_index].ecode_str.len - 1; \ +} + +/* returns TRUE if able to fit in string held by tmpmval into dollar_stack + * returns FALSE otherwise */ +static boolean_t fill_dollar_stack_info(mval *mvalptr, mstr *mstrptr) +{ + ssize_t space_left; + + if (mvalptr->str.len) + { + space_left = dollar_stack.top - dollar_stack.end; + if (mvalptr->str.len > space_left) + { + dollar_stack.incomplete = TRUE; /* we stop storing $STACK(level) info once we reach a frame level + * that can't be fitted in the available space */ + return FALSE; + } + memcpy(dollar_stack.end, mvalptr->str.addr, mvalptr->str.len); + mstrptr->addr = dollar_stack.end; + mstrptr->len = mvalptr->str.len; + dollar_stack.end += mstrptr->len; + assert(dollar_stack.end <= dollar_stack.top); + } else + mstrptr->len = 0; + return TRUE; +} + +/* returns TRUE if able to fit in one $STACK(level,...) of information in global variable structure "dollar_stack". + * returns FALSE otherwise */ +static boolean_t fill_dollar_stack_level(int array_level, int frame_level, int cur_zlevel) +{ + mstr *mstrptr; + mval tmpmval; + dollar_stack_struct *dstack; + + assert(FALSE == dollar_stack.incomplete); /* we should not have come here if previous $STACK levels were incomplete */ + dstack = &dollar_stack.array[array_level]; + /* fill in $STACK(level) */ + if (frame_level) + get_frame_creation_info(frame_level, cur_zlevel, &tmpmval); + else + get_command_line(&tmpmval, FALSE); /* FALSE to indicate we want actual (not processed) command line */ + /* note that tmpmval at this point will most likely point to the stringpool. but we rely on stp_gcol to free it up */ + mstrptr = &dstack->mode_str; + if (FALSE == fill_dollar_stack_info(&tmpmval, mstrptr)) + return FALSE; + /* fill in $STACK(level,"ECODE") */ + dstack->ecode_ptr = (frame_level == (cur_zlevel - 1)) ? &dollar_ecode.array[dollar_ecode.index - 1] : NULL; + + /* fill in $STACK(level,"PLACE") */ + get_frame_place_mcode(frame_level, DOLLAR_STACK_PLACE, cur_zlevel, &tmpmval); + mstrptr = &dstack->place_str; + if (FALSE == fill_dollar_stack_info(&tmpmval, mstrptr)) + return FALSE; + /* fill in $STACK(level,"MCODE") */ + get_frame_place_mcode(frame_level, DOLLAR_STACK_MCODE, cur_zlevel, &tmpmval); + mstrptr = &dstack->mcode_str; + if (FALSE == fill_dollar_stack_info(&tmpmval, mstrptr)) + return FALSE; + return TRUE; +} + +boolean_t ecode_add(mstr *str) /* add "str" to $ECODE and return whether SUCCESS or FAILURE as TRUE/FALSE */ +{ + int ecode_index, stack_index; + boolean_t shrink; + int cur_zlevel, level; + char eclostmid_buf[MAX_DIGITS_IN_INT + STR_LIT_LEN(",Z,")], *dest; + ssize_t space_left, eclostmid_len; + + error_def(ERR_ECLOSTMID); + + dest = &eclostmid_buf[0]; + *dest++ = ','; + *dest++ = 'Z'; + dest = (char *)i2asc((unsigned char *)dest, ERR_ECLOSTMID); + *dest++ = ','; + eclostmid_len = dest - &eclostmid_buf[0]; + assert(SIZEOF(eclostmid_buf) >= eclostmid_len); + + assert(str->len < DOLLAR_ECODE_ALLOC); + space_left = dollar_ecode.top - dollar_ecode.end; + ecode_index = dollar_ecode.index; + shrink = FALSE; + if (space_left < str->len) + { + shrink = TRUE; + assert(1 == shrink); /* since we need a value of 1 (instead of any non-zero) for usage below */ + space_left -= eclostmid_len - 1;/* note : space_left can become negative but code below handles that */ + } + if (ecode_index >= (DOLLAR_ECODE_MAXINDEX - shrink)) + { + assert(DOLLAR_ECODE_MAXINDEX >= ecode_index); + if (DOLLAR_ECODE_MAXINDEX == ecode_index) + { + DECR_ECODE_INDEX(ecode_index); + shrink = TRUE; + } + if (shrink) + { + DECR_ECODE_INDEX(ecode_index); + assert((DOLLAR_ECODE_MAXINDEX - 2) == ecode_index); + } + } + assert(ecode_index < DOLLAR_ECODE_MAXINDEX); + for ( ; space_left < (int)str->len; ) /* note explicit typecasting to make sure it is a signed comparison */ + { + ecode_index--; + if (1 > ecode_index) /* if ecode_index == -1 ==> str->len > DOLLAR_ECODE_ALLOC so nothing can be done in PRO */ + return FALSE; /* if ecode_index == 0 ==> first ECODE needs to be overlaid. we do not want to do that. */ + space_left += dollar_ecode.array[ecode_index].ecode_str.len - 1; + } + for (stack_index = 0; stack_index < dollar_stack.index; stack_index++) + { + if (dollar_stack.array[stack_index].ecode_ptr > &dollar_ecode.array[ecode_index]) + return FALSE; /* do not want to overlay any ECODE that $STACK(level,"ECODE") is pointing to */ + } + assert(0 <= ecode_index); + if (dollar_ecode.index != ecode_index) + { + dollar_ecode.end = dollar_ecode.array[ecode_index].ecode_str.addr; + dollar_ecode.index = ecode_index; + } + if (shrink) + { + INCR_ECODE_INDEX(dollar_ecode.index, &eclostmid_buf[0], (mstr_len_t)eclostmid_len); + } + INCR_ECODE_INDEX(dollar_ecode.index, str->addr, str->len); + if ((1 == dollar_ecode.index) + || ((!dollar_stack.incomplete) && (2 == dollar_ecode.index) + && (dollar_ecode.first_ecode_error_frame == error_frame))) + { /* need to fill in $STACK entries if either the first ECODE or if an error in the first ECODE error-handler. + * do not fill in nested error $STACK info if the first ECODE's $STACK info itself was incompletely filled in */ + if (1 == dollar_ecode.index) + { /* first ECODE. note down error_frame info in "first_ecode_error_frame" as well as $STACK(level) info */ + dollar_ecode.first_ecode_error_frame = frame_pointer; + assert(0 == dollar_stack.index); + } + cur_zlevel = dollar_zlevel(); + assert(dollar_stack.index <= cur_zlevel); + for (level = dollar_stack.index; level < MIN(cur_zlevel, DOLLAR_STACK_MAXINDEX); ) + { /* we do not store $STACK(level) info for levels > 256 */ + if (fill_dollar_stack_level(level, level, cur_zlevel)) + level++; /* update array_level only if we had enough space to fill in all of above */ + else + break; + } + if ((2 == dollar_ecode.index) && (cur_zlevel == dollar_stack.index) && (DOLLAR_STACK_MAXINDEX > cur_zlevel)) + { /* if nested error occurred at the same frame_level as the first error, + * store $STACK information for the nested error in $STACK(frame_level+1) + */ + assert(level == dollar_stack.index); + if (fill_dollar_stack_level(level, cur_zlevel - 1, cur_zlevel)) + level++; + } + dollar_stack.index = level; + } + return TRUE; +} diff --git a/sr_port/ecode_get.c b/sr_port/ecode_get.c new file mode 100644 index 0000000..f032c8b --- /dev/null +++ b/sr_port/ecode_get.c @@ -0,0 +1,37 @@ +/**************************************************************** + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "stringpool.h" +#include "error_trap.h" + +GBLREF dollar_ecode_type dollar_ecode; /* structure containing $ECODE related information */ + +void ecode_get(int level, mval *val) +{ + mstr tmpmstr; + + val->mvtype = MV_STR; + assert(-1 == level); /* currently -1 is the only valid negative level argument to ecode_get() */ + if (dollar_ecode.index) + { + assert(dollar_ecode.end > dollar_ecode.begin); + val->str.addr = dollar_ecode.begin; + val->str.len = INTCAST(dollar_ecode.end - dollar_ecode.begin + 1); /* to account for terminating ',' */ + s2pool(&val->str); + } else + { + assert(dollar_ecode.end == dollar_ecode.begin); + val->str.len = 0; + } +} diff --git a/sr_port/ecode_init.c b/sr_port/ecode_init.c new file mode 100644 index 0000000..4c218a7 --- /dev/null +++ b/sr_port/ecode_init.c @@ -0,0 +1,39 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "rtnhdr.h" +#include "stack_frame.h" +#include "error.h" /* for ERROR_RTN */ +#include "error_trap.h" + +GBLREF dollar_ecode_type dollar_ecode; /* structure containing $ECODE related information */ +GBLREF dollar_stack_type dollar_stack; /* structure containing $STACK related information */ + +/* NOTE: every malloc'd storage here should be free'd in a nested call-in environment. + * gtmci_isv_restore (in gtmci_isv.c) needs to be reflected for any future mallocs added here. */ +void ecode_init(void) +{ + dollar_ecode.begin = (char *)malloc(DOLLAR_ECODE_ALLOC); + dollar_ecode.top = dollar_ecode.begin + DOLLAR_ECODE_ALLOC; + dollar_ecode.array = (dollar_ecode_struct *)malloc(SIZEOF(dollar_ecode_struct) * DOLLAR_ECODE_MAXINDEX); + dollar_ecode.error_rtn_addr = NON_IA64_ONLY(CODE_ADDRESS(ERROR_RTN)) IA64_ONLY(CODE_ADDRESS_C(ERROR_RTN)); + dollar_ecode.error_rtn_ctxt = GTM_CONTEXT(ERROR_RTN); + dollar_ecode.error_return_addr = (error_ret_fnptr)ERROR_RETURN; + + dollar_stack.begin = (char *)malloc(DOLLAR_STACK_ALLOC); + dollar_stack.top = dollar_stack.begin + DOLLAR_STACK_ALLOC; + dollar_stack.array = (dollar_stack_struct *)malloc(SIZEOF(dollar_stack_struct) * DOLLAR_STACK_MAXINDEX); + + NULLIFY_DOLLAR_ECODE; /* this macro resets dollar_ecode.{end,index}, dollar_stack.{begin,index,incomplete} and + * first_ecode_error_frame to point as if no error occurred at all */ +} diff --git a/sr_port/ecode_set.c b/sr_port/ecode_set.c new file mode 100644 index 0000000..1a5dff3 --- /dev/null +++ b/sr_port/ecode_set.c @@ -0,0 +1,80 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "error.h" +#include "error_trap.h" +#include "merrors_ansi.h" + +/* ECODE_MAX_LEN is the maximum length of the string representation of "errnum"'s ECODE. + * This is arrived at as follows : ",M,Z,". Each number can be at most MAX_NUM_SIZE. + * The three ","s and the letters "M" and "Z" add up to 5 characters. + * In addition we give a buffer for overflow in the production version (just in case). + */ +#define BUFFER_FOR_OVERFLOW 15 /* give some buffer in case overflow happens in PRO */ +#define ECODE_MAX_LEN ((2 * MAX_DIGITS_IN_INT) + STR_LIT_LEN(",M,Z,")) +#define ECODE_MAX_LEN_WITH_BUFFER ((ECODE_MAX_LEN) + (BUFFER_FOR_OVERFLOW)) + +void ecode_set(int errnum) +{ + mval tmpmval; + const err_ctl *ectl; + mstr ecode_mstr; + char ecode_buff[ECODE_MAX_LEN_WITH_BUFFER]; + char *ecode_ptr; + int ansi_error; + int severity; + + error_def(ERR_SETECODE); + + /* If this routine was called with error code SETECODE, + * an end-user just put a correct value into $ECODE, + * and it shouldn't be replaced by this routine. + */ + if (ERR_SETECODE == errnum) + return; + /* When the value of $ECODE is non-empty, error trapping + * will be invoked. When the severity level does not warrant + * error trapping, no value should be copied into $ECODE + */ + severity = errnum & SEV_MSK; + if ((INFO == severity) || (SUCCESS == severity)) + return; + /* Get ECODE string from error-number. If the error has an ANSI standard code, return ,Mnnn, (nnn is ANSI code). + * Always return ,Zxxx, (xxx is GT.M code). Note that the value of $ECODE must start and end with a comma + */ + ecode_ptr = &ecode_buff[0]; + *ecode_ptr++ = ','; + if (ectl = err_check(errnum)) + { + ansi_error = ((errnum & FACMASK(ectl->facnum)) && (MSGMASK(errnum, ectl->facnum) <= ectl->msg_cnt)) + ? error_ansi[MSGMASK(errnum, ectl->facnum) - 1] + : 0; + if (ansi_error > 0) + { + *ecode_ptr++ = 'M'; + ecode_ptr = (char *)i2asc((unsigned char *)ecode_ptr, ansi_error); + *ecode_ptr++ = ','; + } + } + *ecode_ptr++ = 'Z'; + ecode_ptr = (char *)i2asc((unsigned char *)ecode_ptr, errnum); + *ecode_ptr++ = ','; + ecode_mstr.addr = &ecode_buff[0]; + ecode_mstr.len = INTCAST(ecode_ptr - ecode_mstr.addr); + assert(ecode_mstr.len <= ECODE_MAX_LEN); + if (ecode_mstr.len > ECODE_MAX_LEN_WITH_BUFFER) + GTMASSERT; + ecode_add(&ecode_mstr); +} diff --git a/sr_port/eintr_wrappers.h b/sr_port/eintr_wrappers.h new file mode 100644 index 0000000..30ebe42 --- /dev/null +++ b/sr_port/eintr_wrappers.h @@ -0,0 +1,281 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* Define macros to do system calls and restart as appropriate + * + * FCNTL, FCNTL3 Loop until fcntl call succeeds or fails with other than EINTR. + * TCFLUSH Loop until tcflush call succeeds or fails with other than EINTR. + * TCSETATTR Loop until tcsetattr call succeeds or fails with other than EINTR. + */ + +#ifndef EINTR_WRP_Included +#define EINTR_WRP_Included + +#include +#include + +#define ACCEPT_SOCKET(SOCKET, ADDR, LEN, RC) \ +{ \ + do \ + { \ + RC = ACCEPT(SOCKET, ADDR, LEN); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define CHG_OWNER(PATH, OWNER, GRP, RC) \ +{ \ + do \ + { \ + RC = CHOWN(PATH, OWNER, GRP); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define CLOSEDIR(DIR, RC) \ +{ \ + do \ + { \ + RC = closedir(DIR); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define CONNECT_SOCKET(SOCKET, ADDR, LEN, RC) \ +{ \ + do \ + { \ + RC = CONNECT(SOCKET, ADDR, LEN); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define CREATE_FILE(PATHNAME, MODE, RC) \ +{ \ + do \ + { \ + RC = CREAT(PATHNAME, MODE); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define DOREAD_A_NOINT(FD, BUF, SIZE, RC) \ +{ \ + do \ + { \ + RC = DOREAD_A(FD, BUF, SIZE); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define DUP2(FDESC1, FDESC2, RC) \ +{ \ + do \ + { \ + RC = dup2(FDESC1, FDESC2); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define FCLOSE(STREAM, RC) \ +{ \ + do \ + { \ + RC = fclose(STREAM); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define FCNTL2(FDESC, ACTION, RC) \ +{ \ + do \ + { \ + RC = fcntl(FDESC, ACTION); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define FCNTL3(FDESC, ACTION, ARG, RC) \ +{ \ + do \ + { \ + RC = fcntl(FDESC, ACTION, ARG); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define FGETS_FILE(BUF, LEN, FP, RC) \ +{ \ + do \ + { \ + FGETS(BUF, LEN, FP, RC); \ + } while(NULL == RC && !feof(FP) && ferror(FP) && EINTR == errno); \ +} + +#define FSTAT_FILE(FDESC, INFO, RC) \ +{ \ + do \ + { \ + RC = fstat(FDESC, INFO); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define FSTATVFS_FILE(FDESC, FSINFO, RC) \ +{ \ + do \ + { \ + FSTATVFS(FDESC, FSINFO, RC); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define FTRUNCATE(FDESC, LENGTH, RC) \ +{ \ + do \ + { \ + RC = ftruncate(FDESC, LENGTH); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define MSGSND(MSGID, MSGP, MSGSZ, FLG, RC) \ +{ \ + do \ + { \ + RC = msgsnd(MSGID, MSGP, MSGSZ, FLG);\ + } while(-1 == RC && EINTR == errno); \ +} + +#define OPEN_PIPE(FDESC, RC) \ +{ \ + do \ + { \ + RC = pipe(FDESC); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define READ_FILE(FD, BUF, SIZE, RC) \ +{ \ + do \ + { \ + RC = read(FD, BUF, SIZE); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define RECVFROM_SOCK(SOCKET, BUF, LEN, FLAGS, \ + ADDR, ADDR_LEN, RC) \ +{ \ + do \ + { \ + RC = RECVFROM(SOCKET, BUF, LEN, \ + FLAGS, ADDR, ADDR_LEN);\ + } while(-1 == RC && EINTR == errno); \ +} + +#define SELECT(FDS, INLIST, OUTLIST, XLIST, \ + TIMEOUT, RC) \ +{ \ + struct timeval eintr_select_timeval; \ + do \ + { \ + eintr_select_timeval = *(TIMEOUT); \ + RC = select(FDS, INLIST, OUTLIST, \ + XLIST, \ + &eintr_select_timeval);\ + } while(-1 == RC && EINTR == errno); \ +} + + +#define SEND(SOCKET, BUF, LEN, FLAGS, RC) \ +{ \ + do \ + { \ + RC = send(SOCKET, BUF, LEN, FLAGS); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define SENDTO_SOCK(SOCKET, BUF, LEN, FLAGS, \ + ADDR, ADDR_LEN, RC) \ +{ \ + do \ + { \ + RC = SENDTO(SOCKET, BUF, LEN, FLAGS, \ + ADDR, ADDR_LEN); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define STAT_FILE(PATH, INFO, RC) \ +{ \ + do \ + { \ + RC = Stat(PATH, INFO); \ + } while((uint4)-1 == RC && EINTR == errno); \ +} + +#define TCFLUSH(FDESC, REQUEST, RC) \ +{ \ + do \ + { \ + RC = tcflush(FDESC, REQUEST); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define Tcsetattr(FDESC, WHEN, TERMPTR, RC) \ +{ \ + do \ + { \ + RC = tcsetattr(FDESC, WHEN, TERMPTR);\ + } while(-1 == RC && EINTR == errno); \ +} + +#define TRUNCATE_FILE(PATH, LENGTH, RC) \ +{ \ + do \ + { \ + RC = TRUNCATE(PATH, LENGTH); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define WAIT(STATUS, RC) \ +{ \ + do \ + { \ + RC = wait(STATUS); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define WAITPID(PID, STATUS, OPTS, RC) \ +{ \ + /* Ensure that the incoming PID is non-zero. We currently don't know of any places where we want to invoke \ + * waitpid with child PID being 0 as that would block us till any of the child spawned by this parent process \ + * changes its state unless invoked with WNOHANG bit set. Make sure not waiting on current pid \ + */ \ + assert(0 != PID); \ + assert(getpid() != PID); \ + do \ + { \ + RC = waitpid(PID, STATUS, OPTS); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define WRITE_FILE(FD, BUF, SIZE, RC) \ +{ \ + do \ + { \ + RC = write(FD, BUF, SIZE); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define GTM_FSYNC(FD, RC) \ +{ \ + do \ + { \ + RC = fsync(FD); \ + } while(-1 == RC && EINTR == errno); \ +} + +#define SIGPROCMASK(FUNC, NEWSET, OLDSET, RC) \ +{ \ + do \ + { \ + RC = sigprocmask(FUNC, NEWSET, OLDSET); \ + } while (-1 == RC && EINTR == errno); \ +} + +#endif diff --git a/sr_port/emit_code.c b/sr_port/emit_code.c new file mode 100644 index 0000000..8ef5797 --- /dev/null +++ b/sr_port/emit_code.c @@ -0,0 +1,1922 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include +#include "gtm_string.h" +#include +#include "gtm_fcntl.h" +#include "gtm_stat.h" +#include "gtm_stdio.h" + +#include "compiler.h" +#include "opcode.h" +#include "mdq.h" +#include "vxi.h" +#include "vxt.h" +#include "cgp.h" +#include "obj_gen.h" +#include "rtnhdr.h" +#include "obj_file.h" +#include "list_file.h" +#include "min_max.h" +#include "emit_code.h" +#ifdef UNIX +#include "xfer_enum.h" +#endif +#include "hashtab_mname.h" +#include "stddef.h" + +/* Required to find out variable length argument runtime function calls*/ +#if defined(__x86_64__) || defined(__ia64) +# include "code_address_type.h" +# ifdef XFER +# undef XFER +# endif /* XFER */ +# define XFER(a,b) #b +# include "xfer_desc.i" +DEFINE_XFER_TABLE_DESC; +GBLDEF int call_4lcldo_variant; /* used in emit_jmp for call[sp] and forlcldo */ +#endif /* __x86_64__ || __ia64 */ + +#define MVAL_INT_SIZE DIVIDE_ROUND_UP(SIZEOF(mval), SIZEOF(UINTPTR_T)) + +#ifdef DEBUG +# include "vdatsize.h" +/* VAX DISASSEMBLER TEXT */ +static const char vdat_bdisp[VDAT_BDISP_SIZE + 1] = "B^"; +static const char vdat_wdisp[VDAT_WDISP_SIZE + 1] = "W^"; +static const char vdat_r9[VDAT_R9_SIZE + 1] = "(r9)"; +static const char vdat_r8[VDAT_R8_SIZE + 1] = "(r8)"; +static const char vdat_gr[VDAT_GR_SIZE + 1] = "G^"; +static const char vdat_immed[VDAT_IMMED_SIZE + 1] = "I^#"; +static const char vdat_r11[VDAT_R11_SIZE + 1] = "(R11)"; +static const char vdat_gtmliteral[VDAT_GTMLITERAL_SIZE + 1] = "GTM$LITERAL"; +static const char vdat_def[VDAT_DEF_SIZE + 1] = "@"; + +IA64_ONLY(GBLDEF char asm_mode = 0; /* 0 - disassembly mode. 1 - decode mode */) +GBLDEF unsigned char *obpt; /* output buffer index */ +GBLDEF unsigned char outbuf[ASM_OUT_BUFF]; /* assembly language output buffer */ +static int vaxi_cnt = 1; /* Vax instruction count */ + +/* Disassembler text: */ +LITREF char *xfer_name[]; +LITREF char vxi_opcode[][6]; +GBLREF char *oc_tab_graphic[]; +#endif + +LITREF octabstruct oc_tab[]; /* op-code table */ +LITREF short ttt[]; /* triple templates */ + +GBLREF boolean_t run_time; +GBLREF int sa_temps_offset[]; +GBLREF int sa_temps[]; +LITREF int sa_class_sizes[]; + +GBLDEF CODE_TYPE code_buf[NUM_BUFFERRED_INSTRUCTIONS]; +GBLDEF int code_idx; +#ifdef DEBUG +GBLDEF struct inst_count generated_details[MAX_CODE_COUNT], calculated_details[MAX_CODE_COUNT]; +GBLDEF int4 generated_count, calculated_count; +#endif /* DEBUG */ +GBLDEF int calculated_code_size, generated_code_size; +GBLDEF int jmp_offset; /* Offset to jump target */ +GBLDEF int code_reference; /* Offset from pgm start to current loc */ + +DEBUG_ONLY(static boolean_t opcode_emitted;) + +static int stack_depth = 0; + +/* On x86_64, the smaller offsets are encoded in 1 byte (4 bytes otherwise). But for some cases, + the offsets may be different during APPROX_ADDR and MACHINE phases, hence generating different size instruction. + to solve this even the smaller offsets need to be encoded in 4 bytes so that same size instructions are generated + in both APPROX_ADDR and MACHINE phase. the variable force_32 is used for this purpose*/ +X86_64_ONLY(GBLDEF boolean_t force_32 = FALSE;) + +GBLREF int curr_addr; +GBLREF char cg_phase; /* code generation phase */ +GBLREF char cg_phase_last; /* the previous code generation phase */ + + +/*variables for counting the arguments*/ +static int vax_pushes_seen, vax_number_of_arguments; + +static struct push_list +{ + struct push_list *next; + unsigned char value[PUSH_LIST_SIZE]; +} *current_push_list_ptr, *push_list_start_ptr; + +static int push_list_index; +static boolean_t ocnt_ref_seen = FALSE; +static oprtype *ocnt_ref_opr; +static triple *current_triple; + +void trip_gen (triple *ct) +{ + oprtype **sopr, *opr; /* triple operand */ + oprtype *saved_opr[MAX_ARGS]; + unsigned short oct; + short tp; /* template pointer */ + const short *tsp; /* template short pointer */ + triple *ttp; /* temp triple pointer */ + int irep_index; + oprtype *irep_opr; + const short *repl; /* temp irep ptr */ + short repcnt; + int off; + + error_def (ERR_UNIMPLOP); + error_def (ERR_MAXARGCNT); + +# if !defined(TRUTH_IN_REG) && (!(defined(__osf__) || defined(__x86_64__) || defined(Linux390))) + GTMASSERT; +# endif + + DEBUG_ONLY(opcode_emitted = FALSE); + current_triple = ct; /* save for possible use by internal rtns */ + tp = ttt[ct->opcode]; + if (tp <= 0) + { + stx_error(ERR_UNIMPLOP); + return; + } + + code_idx = 0; + vax_pushes_seen = 0; + vax_number_of_arguments = 0; + + if (cg_phase_last != cg_phase) + { + cg_phase_last = cg_phase; + if (cg_phase == CGP_APPROX_ADDR) + push_list_init(); + else + reset_push_list_ptr(); + } + + code_reference = ct->rtaddr; + oct = oc_tab[ct->opcode].octype; + sopr = &saved_opr[0]; + *sopr++ = &ct->destination; + for (ttp = ct, opr = ttp->operand ; opr < ARRAYTOP(ttp->operand); ) + { + if (opr->oprclass) + { + if (opr->oprclass == TRIP_REF && opr->oprval.tref->opcode == OC_PARAMETER) + { + ttp = opr->oprval.tref; + opr = ttp->operand; + continue; + } + *sopr++ = opr; + if (sopr >= ARRAYTOP(saved_opr)) + rts_error(VARLSTCNT(3) ERR_MAXARGCNT, 1, MAX_ARGS); + } + opr++; + } + *sopr = 0; + + jmp_offset = 0; + if (oct & OCT_JUMP || ct->opcode == OC_LDADDR || ct->opcode == OC_FORLOOP) + { + if (ct->operand[0].oprval.tref->rtaddr == 0) /* forward reference */ + { + jmp_offset = LONG_JUMP_OFFSET; + assert(cg_phase == CGP_APPROX_ADDR); + } else + jmp_offset = ct->operand[0].oprval.tref->rtaddr - ct->rtaddr; + + switch(ct->opcode) + { + case OC_CALL: + case OC_FORLCLDO: + case OC_CALLSP: +# ifdef __x86_64__ + tsp = (short *)&ttt[ttt[tp]]; + if (-128 <= tsp[CALL_4LCLDO_XFER] && 127 >= tsp[CALL_4LCLDO_XFER]) + off = jmp_offset - XFER_BYTE_INST_SIZE; + else + off = jmp_offset - XFER_LONG_INST_SIZE; + if (-128 <= (off - BRB_INST_SIZE) && 127 >= (off - BRB_INST_SIZE)) + call_4lcldo_variant = BRB_INST_SIZE; /* used by emit_jmp */ + else + { + call_4lcldo_variant = JMP_LONG_INST_SIZE; /* used by emit_jmp */ + tsp = (short *)&ttt[ttt[tp + 1]]; + if (-128 <= tsp[CALL_4LCLDO_XFER] && 127 >= tsp[CALL_4LCLDO_XFER]) + off = jmp_offset - XFER_BYTE_INST_SIZE; + else + off = jmp_offset - XFER_LONG_INST_SIZE; + if (-32768 > (off - JMP_LONG_INST_SIZE) && + 32767 < (off - JMP_LONG_INST_SIZE)) + tsp = (short *)&ttt[ttt[tp + 2]]; + } + break; +# else + off = (jmp_offset - CALL_INST_SIZE)/INST_SIZE; /* [kmk] */ + if (off >= -128 && off <= 127) + tsp = &ttt[ttt[tp]]; + else if (off >= -32768 && off <= 32767) + tsp = &ttt[ttt[tp + 1]]; + else + tsp = &ttt[ttt[tp + 2]]; + break; +# endif /* __x86_64__ */ + case OC_JMP: + case OC_JMPEQU: + case OC_JMPGEQ: + case OC_JMPGTR: + case OC_JMPLEQ: + case OC_JMPNEQ: + case OC_JMPLSS: + case OC_JMPTSET: + case OC_JMPTCLR: + case OC_LDADDR: + case OC_FORLOOP: + tsp = &ttt[ttt[tp]]; + break; + default: + GTMASSERT; + } + } else if (oct & OCT_COERCE) + { + switch (oc_tab[ct->operand[0].oprval.tref->opcode].octype & (OCT_VALUE | OCT_BOOL)) + { + case OCT_MVAL: + tp = ttt[tp]; + break; + case OCT_MINT: + tp = ttt[tp + 3]; + break; + case OCT_BOOL: + tp = ttt[tp + 4]; + break; + default: + GTMASSERT; + break; + } + tsp = &ttt[tp]; + } else + tsp = &ttt[tp]; + + for (; *tsp != VXT_END;) + { + if (*tsp == VXT_IREPAB || *tsp == VXT_IREPL) + { + repl = tsp; + repl += 2; + repcnt = *repl++; + assert(repcnt != 1); + for (irep_index = repcnt, irep_opr = &ct->operand[1]; irep_index > 2; --irep_index) + { + assert(irep_opr->oprclass == TRIP_REF); + irep_opr = &irep_opr->oprval.tref->operand[1]; + } + + if (irep_opr->oprclass == TRIP_REF) + { + repl = tsp; + do + { + tsp = repl; + tsp = emit_vax_inst((short *)tsp, &saved_opr[0], --sopr); +# ifdef DEBUG + if (cg_phase == CGP_ASSEMBLY) + emit_asmlist(ct); +# endif + } while (sopr > &saved_opr[repcnt]); + } else + { + sopr = &saved_opr[repcnt]; + tsp = repl; + } + } else + { + assert(*tsp > 0 && *tsp <= 511); + tsp = emit_vax_inst((short *)tsp, &saved_opr[0], sopr); +# ifdef DEBUG + if (cg_phase == CGP_ASSEMBLY) + emit_asmlist(ct); +# endif + }/* else */ + }/* for */ + + if (cg_phase == CGP_APPROX_ADDR) + if (vax_pushes_seen > 0) + add_to_vax_push_list(vax_pushes_seen); +} + +#ifdef DEBUG +void emit_asmlist(triple *ct) +{ + int offset; + unsigned char *c; + + obpt -= 2; + *obpt = ' '; /* erase trailing comma */ + if (!opcode_emitted) + { + opcode_emitted = TRUE; + offset = (int)(&outbuf[0] + 60 - obpt); + if (offset >= 1) + { /* tab to position 60 */ + memset(obpt, ' ', offset); + obpt += offset; + } else + { /* leave at least 2 spaces */ + memset(obpt, ' ', 2); + obpt += 2; + } + *obpt++ = ';'; + for (c = (unsigned char*)oc_tab_graphic[ct->opcode]; *c;) + *obpt++ = *c++; + } + emit_eoi(); + format_machine_inst(); +} + +void emit_eoi (void) +{ + IA64_ONLY(if (asm_mode == 0) {) + *obpt++ = '\0'; + list_tab(); + list_line((char *)outbuf); + IA64_ONLY(}) + return; +} +#endif + + +short *emit_vax_inst (short *inst, oprtype **fst_opr, oprtype **lst_opr) +{ + static short last_vax_inst = 0; + short sav_in, save_inst; + boolean_t oc_int; + oprtype *opr; + triple *ct; + int cnt, cnttop, reg, words_to_move, reg_offset, save_reg_offset, targ_reg; + int branch_idx, branch_offset, loop_top_idx, instr; + + code_idx = 0; + + switch (cg_phase) + { + case CGP_ASSEMBLY: +# ifdef DEBUG + list_chkpage(); + obpt = &outbuf[0]; + memset(obpt, SP, SIZEOF(outbuf)); + i2asc((uchar_ptr_t)obpt, vaxi_cnt++); + obpt += 7; + if (VXT_IREPAB != *inst && VXT_IREPL != *inst) + instr = *inst; + else + instr = (*inst == VXT_IREPAB) ? VXI_PUSHAB : VXI_PUSHL; + memcpy(obpt, &vxi_opcode[instr][0], 6); + obpt += 10; + *obpt++ = SP; + *obpt++ = SP; + /***** WARNING - FALL THRU *****/ +# endif + case CGP_ADDR_OPT: + case CGP_APPROX_ADDR: + case CGP_MACHINE: + switch ((sav_in = *inst++)) + { + case VXI_BEQL: + emit_jmp(GENERIC_OPCODE_BEQ, &inst, GTM_REG_COND_CODE); + break; + case VXI_BGEQ: + emit_jmp(GENERIC_OPCODE_BGE, &inst, GTM_REG_COND_CODE); + break; + case VXI_BGTR: + emit_jmp(GENERIC_OPCODE_BGT, &inst, GTM_REG_COND_CODE); + break; + case VXI_BLEQ: + emit_jmp(GENERIC_OPCODE_BLE, &inst, GTM_REG_COND_CODE); + break; + case VXI_BLSS: + emit_jmp(GENERIC_OPCODE_BLT, &inst, GTM_REG_COND_CODE); + break; + case VXI_BNEQ: + emit_jmp(GENERIC_OPCODE_BNE, &inst, GTM_REG_COND_CODE); + break; + case VXI_BLBC: + case VXI_BLBS: + assert(*inst == VXT_REG); + inst++; + +# ifdef TRUTH_IN_REG + reg = GTM_REG_CODEGEN_TEMP; + NON_GTM64_ONLY(GEN_LOAD_WORD(reg, gtm_reg(*inst++), 0);) + GTM64_ONLY( GEN_LOAD_WORD_4(reg, gtm_reg(*inst++), 0);) +# else + /* For platforms, where the $TRUTH value is not carried in a register and + must be fetched from a global variable by subroutine call. */ + assert(*inst == 0x5a); /* VAX r10 or $TEST register */ + inst++; + emit_call_xfer(SIZEOF(intszofptr_t) * xf_dt_get); + reg = GTM_REG_R0; /* function return value */ +# endif + /* Generate a cmp instruction using the return value of the previous call, + which will be in EAX */ + X86_64_ONLY(GEN_CMP_EAX_IMM32(0);) + + if (sav_in == VXI_BLBC) + X86_64_ONLY(emit_jmp(GENERIC_OPCODE_BEQ, &inst, 0);) + NON_X86_64_ONLY(emit_jmp(GENERIC_OPCODE_BLBC, &inst, reg);) + else + { + assert(sav_in == VXI_BLBS); + X86_64_ONLY(emit_jmp(GENERIC_OPCODE_BNE, &inst, 0);) + NON_X86_64_ONLY(emit_jmp(GENERIC_OPCODE_BLBS, &inst, reg);) + } + break; + case VXI_BRB: + emit_jmp(GENERIC_OPCODE_BR, &inst, 0); + break; + case VXI_BRW: + emit_jmp(GENERIC_OPCODE_BR, &inst, 0); + break; + + case VXI_BICB2: +# ifdef TRUTH_IN_REG + GEN_CLEAR_TRUTH; +# endif + assert(*inst == VXT_LIT); + inst++; + assert(*inst == 1); + inst++; + assert(*inst == VXT_REG); + inst++; + inst++; + break; + case VXI_BISB2: +# ifdef TRUTH_IN_REG + GEN_SET_TRUTH; +# endif + assert(*inst == VXT_LIT); + inst++; + assert(*inst == 1); + inst++; + assert(*inst == VXT_REG); + inst++; + inst++; + break; + case VXI_CALLS: + oc_int = TRUE; + if (*inst == VXT_LIT) + { + inst++; + cnt = (int4)*inst++; + } else + { + assert(*inst == VXT_VAL); + inst++; + opr = *(fst_opr + *inst); + assert(opr->oprclass == TRIP_REF); + ct = opr->oprval.tref; + if (ct->destination.oprclass) + opr = &ct->destination; +# ifdef __vms + /* This is a case where VMS puts the argument count in a special register so + handle that differently here. + */ + if (opr->oprclass == TRIP_REF) + { + assert(ct->opcode == OC_ILIT); + cnt = ct->operand[0].oprval.ilit; + code_buf[code_idx++] = ALPHA_INS_LDA + | ALPHA_REG_AI << ALPHA_SHIFT_RA + | ALPHA_REG_ZERO << ALPHA_SHIFT_RB + | (cnt & ALPHA_MASK_DISP) << ALPHA_SHIFT_DISP; + inst++; + } else + { + assert(opr->oprclass == TINT_REF); + oc_int = FALSE; + opr = *(fst_opr + *inst++); + reg = get_arg_reg(); + emit_trip(opr, TRUE, ALPHA_INS_LDL, reg); + emit_push(reg); + } +# else + /* All other platforms put argument counts in normal parameter + registers and go through this path instead. + */ + if (opr->oprclass == TRIP_REF) + { + assert(ct->opcode == OC_ILIT); + cnt = ct->operand[0].oprval.ilit; + reg = get_arg_reg(); + IA64_ONLY(LOAD_IMM14(reg, cnt);) + NON_IA64_ONLY(GEN_LOAD_IMMED(reg, cnt);) + cnt++; + inst++; + } else + { + assert(opr->oprclass == TINT_REF); + oc_int = FALSE; + opr = *(fst_opr + *inst++); + reg = get_arg_reg(); + emit_trip(opr, TRUE, GENERIC_OPCODE_LOAD, reg); + } + emit_push(reg); +# endif + } + assert(*inst == VXT_XFER); + inst++; + emit_call_xfer((int)*inst++); + if (oc_int) + { + if (cnt != 0) + emit_pop(cnt); + } else + { /* During the commonization of emit_code.c I discovered that TINT_REF is + not currently used in the compiler so this may be dead code but I'm + leaving this path in here anyway because I don't want to put it back + in if we find we need it. (4/2003 SE) + */ + emit_trip(opr, TRUE, GENERIC_OPCODE_LOAD, CALLS_TINT_TEMP_REG); + emit_pop(1); + } + break; + case VXI_CLRL: + assert(*inst == VXT_VAL); + inst++; + GEN_CLEAR_WORD_EMIT(CLRL_REG); + break; + case VXI_CMPL: + assert(*inst == VXT_VAL); + inst++; + GEN_LOAD_WORD_EMIT(CMPL_TEMP_REG); + assert(*inst == VXT_VAL); + inst++; + + X86_64_ONLY(GEN_LOAD_WORD_EMIT(GTM_REG_CODEGEN_TEMP);) + NON_X86_64_ONLY(GEN_LOAD_WORD_EMIT(GTM_REG_COND_CODE);) + + X86_64_ONLY(GEN_CMP_REGS(CMPL_TEMP_REG, GTM_REG_CODEGEN_TEMP)) + NON_X86_64_ONLY(GEN_SUBTRACT_REGS(CMPL_TEMP_REG, GTM_REG_COND_CODE, GTM_REG_COND_CODE);) + break; + case VXI_INCL: + assert(*inst == VXT_VAL); + inst++; + save_inst = *inst++; + emit_trip(*(fst_opr + save_inst), TRUE, GENERIC_OPCODE_LOAD, GTM_REG_ACCUM); + GEN_ADD_IMMED(GTM_REG_ACCUM, 1); + emit_trip(*(fst_opr + save_inst), TRUE, GENERIC_OPCODE_STORE, GTM_REG_ACCUM); + break; + case VXI_JMP: + if (*inst == VXT_VAL) + { + inst++; + emit_trip(*(fst_opr + *inst++), FALSE, GENERIC_OPCODE_LOAD, GTM_REG_CODEGEN_TEMP); + GEN_JUMP_REG(GTM_REG_CODEGEN_TEMP); + } else + emit_jmp(GENERIC_OPCODE_BR, &inst, 0); + break; + case VXI_JSB: + assert(*inst == VXT_XFER); + inst++; + emit_call_xfer((int)*inst++); + /* Callee may have popped some values so we can't count on anything left on the stack. */ + stack_depth = 0; + break; + case VXI_MOVAB: + if (*inst == VXT_JMP) + { + inst += 2; + emit_pcrel(); + NON_RISC_ONLY(IGEN_LOAD_ADDR_REG(GTM_REG_ACCUM)) + RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_ADDR_REG(GTM_REG_ACCUM);) + assert(*inst == VXT_ADDR); + inst++; + emit_trip(*(fst_opr + *inst++), FALSE, GENERIC_OPCODE_STORE, GTM_REG_ACCUM); + } else if (*inst == VXT_ADDR || *inst == VXT_VAL) + { + boolean_t addr; + + addr = (*inst == VXT_VAL); + inst++; + save_inst = *inst++; + assert(*inst == VXT_REG); + inst++; + emit_trip(*(fst_opr + save_inst), addr, GENERIC_OPCODE_LDA, gtm_reg(*inst++)); + } else + GTMASSERT; + break; + case VXI_MOVC3: + /* The MOVC3 instruction is only used to copy an mval from one place to another + so that is the expansion we will generate. The most efficient expansion is to + generate a series of load and store instructions. Do the loads first then the + stores to keep the pipelines flowing and not stall waiting for any given load + or store to complete. Because some platforms (notably HPPA) do not have enough + argument registers to contain an entire MVAL and because an mval may grow from + its present size and affect other platforms some day, We put the whole code gen + thing in a loop so we can do this regardless of how big it gets. + */ + + assert(*inst == VXT_LIT); + inst += 2; + assert(*inst == VXT_VAL); + inst++; + emit_trip(*(fst_opr + *inst++), TRUE, GENERIC_OPCODE_LDA, MOVC3_SRC_REG); + assert(*inst == VXT_VAL); + inst++; + emit_trip(*(fst_opr + *inst++), TRUE, GENERIC_OPCODE_LDA, MOVC3_TRG_REG); + +# if defined(__MVS__) || defined(Linux390) + /* The MVC instruction on zSeries facilitates memory copy(mval in this case) in a single + * instruction instead of multiple 8/4 byte copies. + * + * TODO: Revisit other platforms using generic emit_code and verify if the below + * logic of multiple copies can be replaced with more efficient instruction(s) + * available on that particular platform. + */ + GEN_MVAL_COPY(MOVC3_SRC_REG, MOVC3_TRG_REG, SIZEOF(mval)); +# else + for (words_to_move = MVAL_INT_SIZE, reg_offset = 0; words_to_move;) + { + reg = MACHINE_FIRST_ARG_REG; + save_reg_offset = reg_offset; + for (cnt = 0, cnttop = MIN(words_to_move, MACHINE_REG_ARGS) ; cnt < cnttop; + cnt++, reg_offset += SIZEOF(UINTPTR_T)) + { + X86_64_ONLY(targ_reg = GET_ARG_REG(cnt);) + NON_X86_64_ONLY(targ_reg = reg + cnt;) + NON_GTM64_ONLY(GEN_LOAD_WORD(targ_reg, MOVC3_SRC_REG, reg_offset);) + GTM64_ONLY(GEN_LOAD_WORD_8(targ_reg, MOVC3_SRC_REG, reg_offset);) + } + reg = MACHINE_FIRST_ARG_REG; + for (cnt = 0; + cnt < cnttop; + cnt++, save_reg_offset += SIZEOF(UINTPTR_T), words_to_move--) + { + X86_64_ONLY(targ_reg = GET_ARG_REG(cnt);) + NON_X86_64_ONLY(targ_reg = reg + cnt;) + NON_GTM64_ONLY(GEN_STORE_WORD(targ_reg, MOVC3_TRG_REG, save_reg_offset);) + GTM64_ONLY(GEN_STORE_WORD_8(targ_reg, MOVC3_TRG_REG, save_reg_offset);) + } + } +# endif + break; + case VXI_MOVL: + if (*inst == VXT_REG) + { + inst++; + if (*inst > 0x5f) /* OC_CURRHD: any mode >= 6 (deferred), any register */ + { + inst++; + NON_GTM64_ONLY(GEN_LOAD_WORD(GTM_REG_ACCUM, GTM_REG_FRAME_POINTER, 0);) + GTM64_ONLY(GEN_LOAD_WORD_8(GTM_REG_ACCUM, GTM_REG_FRAME_POINTER, 0);) + assert(*inst == VXT_ADDR); + inst++; + emit_trip(*(fst_opr + *inst++), FALSE, GENERIC_OPCODE_STORE, GTM_REG_ACCUM); + } else + { + boolean_t addr; + + assert(*inst == 0x50); /* register mode: (from) r0 */ + inst++; + if (*inst == VXT_VAL || *inst == VXT_ADDR) + { + addr = (*inst == VXT_VAL); + inst++; + emit_trip(*(fst_opr + *inst++), addr, GENERIC_OPCODE_STORE, + MOVL_RETVAL_REG); + } else if (*inst == VXT_REG) + { + inst++; + +# ifdef TRUTH_IN_REG + if (*inst == 0x5a) /* to VAX r10 or $TEST */ + { + NON_GTM64_ONLY(GEN_STORE_WORD(MOVL_RETVAL_REG, + GTM_REG_DOLLAR_TRUTH, 0);) + GTM64_ONLY(GEN_STORE_WORD_4(MOVL_RETVAL_REG, + GTM_REG_DOLLAR_TRUTH, 0);) + } else + { + GEN_MOVE_REG(gtm_reg(*inst), MOVL_RETVAL_REG); + } +# else + if (*inst == 0x5a) /* to VAX r10 or $TEST */ + { + reg = get_arg_reg(); + GEN_MOVE_REG(reg, MOVL_RETVAL_REG); + emit_push(reg); + emit_call_xfer(SIZEOF(intszofptr_t) * xf_dt_store); + } else + { + GEN_MOVE_REG(gtm_reg(*inst), MOVL_RETVAL_REG); + } +# endif + inst++; + } else + GTMASSERT; + } + } else if (*inst == VXT_VAL) + { + inst++; + save_inst = *inst++; + assert(*inst == VXT_REG); + inst++; + assert(*inst == 0x51); /* register mode: R1 */ + inst++; + emit_trip(*(fst_opr + save_inst), TRUE, GENERIC_OPCODE_LOAD, MOVL_REG_R1); + } else + GTMASSERT; + break; + case VXT_IREPAB: + assert(*inst == VXT_VAL); + inst += 2; + reg = get_arg_reg(); + emit_trip(*lst_opr, TRUE, GENERIC_OPCODE_LDA, reg); + emit_push(reg); + break; + case VXI_PUSHAB: + reg = get_arg_reg(); + if (*inst == VXT_JMP) + { + inst += 2; + emit_pcrel(); + NON_RISC_ONLY(IGEN_LOAD_ADDR_REG(reg)) + RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_ADDR_REG(reg);) + } else if (*inst == VXT_VAL || *inst == VXT_GREF) + { + inst++; + emit_trip(*(fst_opr + *inst++), TRUE, GENERIC_OPCODE_LDA, reg); + } else + GTMASSERT; + emit_push(reg); + break; + case VXT_IREPL: + assert(*inst == VXT_VAL); + inst += 2; + reg = get_arg_reg(); + emit_trip(*lst_opr, TRUE, GENERIC_OPCODE_LOAD, reg); + emit_push(reg); + break; + case VXI_PUSHL: + reg = get_arg_reg(); + if (*inst == VXT_LIT) + { + inst++; + GEN_LOAD_IMMED(reg, *inst); + inst++; + } else if (*inst == VXT_ADDR) + { + inst++; + emit_trip(*(fst_opr + *inst++), FALSE, GENERIC_OPCODE_LOAD, reg); + } else if (*inst == VXT_VAL) + { + inst++; + emit_trip(*(fst_opr + *inst++), TRUE, GENERIC_OPCODE_LOAD, reg); + } else + GTMASSERT; + emit_push(reg); + break; + case VXI_TSTL: + assert(*inst == VXT_VAL || *inst == VXT_REG); + if (*inst == VXT_VAL) + { + inst++; + emit_trip( + *(fst_opr + *inst++), TRUE, GENERIC_OPCODE_LOAD, + X86_64_ONLY(GTM_REG_CODEGEN_TEMP) NON_X86_64_ONLY(GTM_REG_COND_CODE) + ); + X86_64_ONLY(GEN_CMP_IMM32(GTM_REG_CODEGEN_TEMP, 0)) + } else if (*inst == VXT_REG) + { + inst++; + X86_64_ONLY(assert(gtm_reg(*inst) == I386_REG_RAX); /* Same as R0 */) + X86_64_ONLY(GEN_CMP_EAX_IMM32(0);) + NON_X86_64_ONLY(GEN_MOVE_REG(GTM_REG_COND_CODE, gtm_reg(*inst));) + inst++; + } + break; + default: + GTMASSERT; + break; + } + break; + default: + GTMASSERT; + break; + } + assert(code_idx < NUM_BUFFERRED_INSTRUCTIONS); + if (cg_phase == CGP_MACHINE) + { + generated_code_size += code_idx; +# ifdef DEBUG + if (generated_count < MAX_CODE_COUNT) + { + generated_details[generated_count].size = code_idx; + generated_details[generated_count++].sav_in = sav_in; + } +# endif /* DEBUG */ + emit_immed ((char *)&code_buf[0], (uint4)(INST_SIZE * code_idx)); + } else if (cg_phase != CGP_ASSEMBLY) + { + if (cg_phase == CGP_APPROX_ADDR) + { + calculated_code_size += code_idx; +# ifdef DEBUG + if (calculated_count < MAX_CODE_COUNT) + { + calculated_details[calculated_count].size = code_idx; + calculated_details[calculated_count++].sav_in = sav_in; + } +# endif /* DEBUG */ + } + curr_addr += (INST_SIZE * code_idx); + } + code_reference += (INST_SIZE * code_idx); + jmp_offset -= (INST_SIZE * code_idx); + last_vax_inst = sav_in; + return inst; +} + +#ifndef __x86_64__ /* For x86_64, this is defined in emit_code_sp.c */ +void emit_jmp (uint4 branchop, short **instp, int reg) +{ + uint4 branchop_opposite; + int src_reg; + int skip_idx; + NON_RISC_ONLY(int tmp_code_idx;) + int branch_offset; + + /* assert(jmp_offset != 0); */ + /* assert commented since jmp_offset could be zero in CGP_ADDR_OPT phase after a jump to the immediately following + * instruction is nullified (as described below) */ + + /* size of this particular instruction */ + jmp_offset -= (int)((char *)&code_buf[code_idx] - (char *)&code_buf[0]); +# if !(defined(__MVS__) || defined(Linux390)) + /* The code_buff on zSeries is filled with 2 byte chunks */ + assert((jmp_offset & 3) == 0); +# endif + branch_offset = jmp_offset / INST_SIZE; + + /* Some platforms have a different origin for the offset */ + EMIT_JMP_ADJUST_BRANCH_OFFSET; + + switch (cg_phase) + { +# ifdef DEBUG + case CGP_ASSEMBLY: + *obpt++ = 'x'; + *obpt++ = '^'; + *obpt++ = '0'; + *obpt++ = 'x'; + obpt += i2hex_nofill(INST_SIZE * branch_offset, (uchar_ptr_t)obpt, 8); + *obpt++ = ','; + *obpt++ = ' '; + /***** WARNING - FALL THRU *****/ +# endif + case CGP_ADDR_OPT: + case CGP_APPROX_ADDR: + case CGP_MACHINE: + assert(VXT_JMP == **instp); + *instp += 1; + assert(1 == **instp); + (*instp)++; + if (0 == branch_offset) + { /* This is a jump to the immediately following instruction. Nullify the jump + and don't generate any instruction (not even a NOP) */ + /* code_buf[code_idx++] = GENERIC_OPCODE_NOP; */ + } else if (EMIT_JMP_SHORT_CODE_CHECK) + { /* Short jump immediate operand - some platforms also do a compare */ + EMIT_JMP_SHORT_CODE_GEN; + } else + { /* Potentially longer jump sequence */ + skip_idx = -1; + if (EMIT_JMP_OPPOSITE_BR_CHECK) + { /* This jump sequence is longer and is not conditional so if we need a conditional + jump, create the opposite conditional jump to jump around the longer jump to + the target thereby preserving the original semantics. + */ + EMIT_JMP_GEN_COMPARE; + switch (branchop) + { + case GENERIC_OPCODE_BEQ: + branchop_opposite = GENERIC_OPCODE_BNE; + break; + case GENERIC_OPCODE_BGE: + branchop_opposite = GENERIC_OPCODE_BLT; + break; + case GENERIC_OPCODE_BGT: + branchop_opposite = GENERIC_OPCODE_BLE; + break; + case GENERIC_OPCODE_BLE: + branchop_opposite = GENERIC_OPCODE_BGT; + break; + case GENERIC_OPCODE_BLT: + branchop_opposite = GENERIC_OPCODE_BGE; + break; + case GENERIC_OPCODE_BNE: + branchop_opposite = GENERIC_OPCODE_BEQ; + break; +# ifdef __alpha + case GENERIC_OPCODE_BLBC: + branchop_opposite = GENERIC_OPCODE_BLBS; + break; + case GENERIC_OPCODE_BLBS: + branchop_opposite = GENERIC_OPCODE_BLBC; + break; +# endif + default: + GTMASSERT; + break; + } + RISC_ONLY( + skip_idx = code_idx++; /* Save index of branch inst. Set target offset later */ + code_buf[skip_idx] = IGEN_COND_BRANCH_REG_OFFSET(branchop_opposite, reg, 0); + branch_offset--; + ) + NON_RISC_ONLY( + skip_idx = code_idx; /* Save index of branch inst. Set target offset later */ + IGEN_COND_BRANCH_REG_OFFSET(branchop_opposite, reg, 0) + branch_offset -= NUM_INST_IGEN_COND_BRANCH_REG_OFFSET; + ) +# ifdef DELAYED_BRANCH + code_buf[code_idx++] = GENERIC_OPCODE_NOP; + branch_offset--; +# endif + + } + if (EMIT_JMP_LONG_CODE_CHECK) + { /* This is more common unconditional branch generation and should be mutually + exclusive to EMIT_JMP_OPPOSITE_BR_CHECK. Some platforms will have the "short" + branch generation up top be more common but that form does not cover unconditional + jumps (Examples: AIX and HP-UX) */ + assert(!(EMIT_JMP_OPPOSITE_BR_CHECK)); + NON_RISC_ONLY(IGEN_UCOND_BRANCH_REG_OFFSET(branchop, branch_offset)) + RISC_ONLY( + code_buf[code_idx++] = IGEN_UCOND_BRANCH_REG_OFFSET(branchop, 0, branch_offset); + ) +# ifdef DELAYED_BRANCH + code_buf[code_idx++] = GENERIC_OPCODE_NOP; +# endif + } else + { + if (EMIT_JMP_OPPOSITE_BR_CHECK) + { /* VAX conditional long jump generates two native branch instructions - + one conditional branch (above) and one PC relative branch (below). + The second branch instruction also needs adjustment of the origin. */ + EMIT_JMP_ADJUST_BRANCH_OFFSET; + } + GEN_PCREL; + emit_base_offset(GTM_REG_CODEGEN_TEMP, (INST_SIZE * branch_offset)); + RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_ADDR_REG(GTM_REG_CODEGEN_TEMP);) + NON_RISC_ONLY(IGEN_LOAD_ADDR_REG(GTM_REG_CODEGEN_TEMP)) + GEN_JUMP_REG(GTM_REG_CODEGEN_TEMP); + } + if (skip_idx != -1) + { /* Fill in the offset from our opposite jump instruction to here .. the + place to bypass the jump. + */ + branch_offset = BRANCH_OFFSET_FROM_IDX(skip_idx, code_idx); + RISC_ONLY(code_buf[skip_idx] |= IGEN_COND_BRANCH_OFFSET(branch_offset);) + + NON_RISC_ONLY( + tmp_code_idx = code_idx; + code_idx = skip_idx; + IGEN_COND_BRANCH_REG_OFFSET(branchop_opposite, reg, branch_offset) + code_idx = tmp_code_idx; + ) + } + } + break; + default: + GTMASSERT; + break; + } +} + +#endif /* !__x86_64__ */ + +/* Emit code that generates a relative pc based jump target. The last instruction is not + complete so the caller may finish it with whatever instruction is necessary. +*/ +void emit_pcrel(void) +{ + int branch_offset; + + jmp_offset -= INTCAST((char *)&code_buf[code_idx] - (char *)&code_buf[0]); + + switch (cg_phase) + { +# ifdef DEBUG + case CGP_ASSEMBLY: + *obpt++ = 'x'; + *obpt++ = '^'; + *obpt++ = '0'; + *obpt++ = 'x'; + obpt += i2hex_nofill(jmp_offset + code_reference, (uchar_ptr_t)obpt, 8); + *obpt++ = ','; + *obpt++ = ' '; + /***** WARNING - FALL THRU *****/ +# endif + case CGP_ADDR_OPT: + case CGP_APPROX_ADDR: + case CGP_MACHINE: + branch_offset = jmp_offset / INST_SIZE; + GEN_PCREL; + EMIT_JMP_ADJUST_BRANCH_OFFSET; /* Account for different branch origins on different platforms */ + emit_base_offset(GTM_REG_CODEGEN_TEMP, INST_SIZE * branch_offset); + break; + default: + GTMASSERT; + break; + } +} + + +/* Emit the code for a given triple */ +void emit_trip(oprtype *opr, boolean_t val_output, uint4 generic_inst, int trg_reg) +{ + boolean_t inst_emitted; + unsigned char reg, op_mod, op_reg; + int offset, immediate; + int upper_idx, lower_idx; + triple *ct; + int low, extra, high; + GTM64_ONLY(int next_ptr_offset = 8;) + + error_def(ERR_SRCNAM); + + if (opr->oprclass == TRIP_REF) + { + ct = opr->oprval.tref; + if (ct->destination.oprclass) + opr = &ct->destination; + /* else lit or error */ + } + + inst_emitted = FALSE; + switch (cg_phase) + { + case CGP_ADDR_OPT: + case CGP_APPROX_ADDR: + switch (opr->oprclass) + { + case TRIP_REF: + assert(ct->destination.oprclass == 0); + assert(val_output); + switch (ct->opcode) + { + case OC_LIT: + assert(ct->operand[0].oprclass == MLIT_REF); + + if (run_time) + reg = GTM_REG_PV; + else + reg = GTM_REG_LITERAL_BASE; + + if (CGP_ADDR_OPT == cg_phase) + { + /* We want the expansion to be proper sized this time. Note + that this won't be true so much on the initial CGP_ADDR_OPT + pass but will be true on the shrink_trips() pass after the + literals are compiled. + */ + offset = literal_offset(ct->operand[0].oprval.mlit->rt_addr); + /* Need non-zero base reg for AIX */ + X86_64_ONLY(force_32 = TRUE;) + emit_base_offset(reg, offset); + X86_64_ONLY(force_32 = FALSE;) + } else + { + /* Gross expansion ok first time through */ + /* Non-0 base reg for AIX */ + X86_64_ONLY(force_32 = TRUE;) + emit_base_offset(reg, LONG_JUMP_OFFSET); + X86_64_ONLY(force_32 = FALSE;) + } + + X86_64_ONLY(IGEN_LOAD_ADDR_REG(trg_reg)) +# if !(defined(__MVS__) || defined(Linux390)) + NON_X86_64_ONLY(code_idx++;) +# else + IGEN_LOAD_ADDR_REG(trg_reg); +# endif + inst_emitted = TRUE; + break; + case OC_CDLIT: + emit_base_offset(GTM_REG_PV, find_linkage(ct->operand[0].oprval.cdlt)); + if (GENERIC_OPCODE_LDA == generic_inst) + { + RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_LINKAGE(trg_reg);) + NON_RISC_ONLY(IGEN_LOAD_LINKAGE(trg_reg);) + inst_emitted = TRUE; + } else + { + RISC_ONLY(code_buf[code_idx++] + |= IGEN_LOAD_LINKAGE(GTM_REG_CODEGEN_TEMP);) + NON_RISC_ONLY(IGEN_LOAD_LINKAGE(GTM_REG_CODEGEN_TEMP);) + emit_base_offset(GTM_REG_CODEGEN_TEMP, 0); + } + break; + case OC_ILIT: + assert(GENERIC_OPCODE_LOAD == generic_inst); + immediate = ct->operand[0].oprval.ilit; + EMIT_TRIP_ILIT_GEN; + inst_emitted = TRUE; + break; + case OC_TRIPSIZE: + /* This tiples value is calculated in the shrink_jmp/shrink_trips + phase. It is a parameter to (currently only) op_exfun and is the + length of the generated jump instruction. op_exfun needs this + length to adjust the return address in the created stackframe + so it does not have to parse instructions at the return address + to see what return signature was created. We will add asserts to + this generation in later phases after the true value has been + calculated. At this point, it is zero. + */ + immediate = ct->operand[0].oprval.tsize->size; + EMIT_TRIP_ILIT_GEN; + inst_emitted = TRUE; + break; + default: + GTMASSERT; + break; + } + break; + case TINT_REF: + case TVAL_REF: + assert(val_output); + offset = sa_temps_offset[opr->oprclass]; + offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; + GTM64_ONLY( + if ( sa_class_sizes[opr->oprclass] == 4 ) + { + next_ptr_offset = 4; + REVERT_GENERICINST_TO_WORD(generic_inst); + } + ) + NON_GTM64_ONLY( + if (offset < 0 || offset > MAX_OFFSET) + GTMASSERT; + ) + emit_base_offset(GTM_REG_FRAME_TMP_PTR, offset); + break; + + case TCAD_REF: + case TVAD_REF: + case TVAR_REF: + offset = sa_temps_offset[opr->oprclass]; + offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; + GTM64_ONLY( + if ( sa_class_sizes[opr->oprclass] == 4 ) + { + next_ptr_offset = 4; + REVERT_GENERICINST_TO_WORD(generic_inst); + } + ) + NON_GTM64_ONLY( + if (offset < 0 || offset > MAX_OFFSET) + GTMASSERT; + ) + if (opr->oprclass == TVAR_REF) + reg = GTM_REG_FRAME_VAR_PTR; + else + reg = GTM_REG_FRAME_TMP_PTR; + emit_base_offset(reg, offset); + if (val_output) + { + if (GENERIC_OPCODE_LDA == generic_inst) + { + NON_RISC_ONLY(IGEN_LOAD_NATIVE_REG(trg_reg)) + RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_NATIVE_REG(trg_reg);) + if (opr->oprclass == TVAR_REF) + { + emit_base_offset(trg_reg, offsetof(ht_ent_mname, value)); + NON_RISC_ONLY(IGEN_LOAD_NATIVE_REG(trg_reg)) + RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_NATIVE_REG(trg_reg);) + } + inst_emitted = TRUE; + } else + { + NON_RISC_ONLY(IGEN_LOAD_NATIVE_REG(GTM_REG_CODEGEN_TEMP)) + RISC_ONLY(code_buf[code_idx++] + |= IGEN_LOAD_NATIVE_REG(GTM_REG_CODEGEN_TEMP);) + emit_base_offset(GTM_REG_CODEGEN_TEMP, 0); + } + } + break; + + case OCNT_REF: + /* This ref's value is calculated in emit_call_xfer(). This value is related to TSIZ_REF + in that it is used for the same reason to different calls (op_call, op_callsp, + op_forlcldo, and their mprof counterparts). It is the offset needed to be + added to the return address from the calls to these routines to bypass a + generated jump sequence. In this case however, the jump sequence is being + generated as part of the OC_CALL, OC_CALLSP or OC_FORLCLDO triple itself. + There is no separate jump triple so the TSIZ_REF triple cannot be used. + So this operand is the OFFSET from the CALL to the NEXT TRIPLE. The operation + is that when this routine sees this type of reference, it will set a flag + and record the operand address and go ahead and generate the value that it has. The + next transfer table generation that occurs will see the set flag and will compute + the address from the return address of that transfer table call to the next triple + and update this triple's value. Since our originating triple has a JUMP type, + it will be updated in shrink_jmp/shirnk_trips() until all necessary shrinkage + is done so the final phase will have the correct value and we only have to + generate an immediate value. + */ + immediate = opr->oprval.offset; + EMIT_TRIP_ILIT_GEN; + inst_emitted = TRUE; + ocnt_ref_seen = TRUE; + ocnt_ref_opr = opr; + break; + } + if (!inst_emitted) { + NON_RISC_ONLY(IGEN_GENERIC_REG(generic_inst, trg_reg)) + RISC_ONLY(code_buf[code_idx++] |= IGEN_GENERIC_REG(generic_inst, trg_reg);) + } + break; +# ifdef DEBUG + case CGP_ASSEMBLY: + offset = 0; + switch (opr->oprclass) + { + case TRIP_REF: + assert(ct->destination.oprclass == 0); + assert(val_output); + switch (ct->opcode) + { + case OC_LIT: + assert(ct->operand[0].oprclass == MLIT_REF); + offset = literal_offset(ct->operand[0].oprval.mlit->rt_addr); + memcpy(obpt, &vdat_def[0], VDAT_DEF_SIZE); + obpt += VDAT_DEF_SIZE; + memcpy(obpt, &vdat_gtmliteral[0], VDAT_GTMLITERAL_SIZE); + obpt += VDAT_GTMLITERAL_SIZE; + *obpt++ = '+'; + *obpt++ = '0'; + *obpt++ = 'x'; + obpt += i2hex_nofill(offset, (uchar_ptr_t)obpt, 8); + if (run_time) + reg = GTM_REG_PV; + else + reg = GTM_REG_LITERAL_BASE; + X86_64_ONLY(force_32 = TRUE;) + emit_base_offset(reg, offset); + X86_64_ONLY(force_32 = FALSE;) + NON_RISC_ONLY(IGEN_LOAD_ADDR_REG(trg_reg)) + RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_ADDR_REG(trg_reg);) + inst_emitted = TRUE; + break; + case OC_CDLIT: + if (val_output) + { + memcpy(obpt, &vdat_gr[0], VDAT_GR_SIZE); + obpt += VDAT_GR_SIZE; + } + memcpy(obpt, ct->operand[0].oprval.cdlt->addr, + ct->operand[0].oprval.cdlt->len); + obpt += ct->operand[0].oprval.cdlt->len; + emit_base_offset(GTM_REG_PV, find_linkage(ct->operand[0].oprval.cdlt)); + if (GENERIC_OPCODE_LDA == generic_inst) + { + RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_LINKAGE(trg_reg)); + NON_RISC_ONLY(IGEN_LOAD_LINKAGE(trg_reg)); + inst_emitted = TRUE; + } else + { + RISC_ONLY(code_buf[code_idx++] + |= IGEN_LOAD_LINKAGE(GTM_REG_CODEGEN_TEMP);) + NON_RISC_ONLY(IGEN_LOAD_LINKAGE(GTM_REG_CODEGEN_TEMP);) + emit_base_offset(GTM_REG_CODEGEN_TEMP, 0); + } + break; + case OC_ILIT: + assert(generic_inst == GENERIC_OPCODE_LOAD); + immediate = ct->operand[0].oprval.ilit; + memcpy(obpt, &vdat_immed[0], VDAT_IMMED_SIZE); + obpt += VDAT_IMMED_SIZE; + obpt = i2asc((uchar_ptr_t)obpt, ct->operand[0].oprval.ilit); + EMIT_TRIP_ILIT_GEN; + inst_emitted = TRUE; + break; + case OC_TRIPSIZE: + immediate = ct->operand[0].oprval.tsize->size; + assert(0 < immediate); + assert(MAX_BRANCH_CODEGEN_SIZE > immediate); + EMIT_TRIP_ILIT_GEN; + inst_emitted = TRUE; + break; + default: + GTMASSERT; + break; + } + break; + + case TINT_REF: + case TVAL_REF: + assert(val_output); + offset = sa_temps_offset[opr->oprclass]; + offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; + NON_GTM64_ONLY( + if (offset < 0 || offset > MAX_OFFSET) + GTMASSERT; + ) + if (offset < 127) + { + memcpy(obpt, &vdat_bdisp[0], VDAT_BDISP_SIZE); + obpt += VDAT_BDISP_SIZE; + } else + { + memcpy(obpt, &vdat_wdisp[0], VDAT_WDISP_SIZE); + obpt += VDAT_WDISP_SIZE; + } + obpt = i2asc((uchar_ptr_t)obpt, offset); + memcpy(obpt, &vdat_r9[0], VDAT_R9_SIZE); + obpt += VDAT_R9_SIZE; + /* + * for 64 bit platforms, By default the loads/stores + * are of 8 bytes, but if the value being dealt with + * is a word, then the opcode in generic_inst is + * changed to ldw/stw(4 byte load/stores) + */ + GTM64_ONLY( + if (sa_class_sizes[opr->oprclass] == 4) + { + next_ptr_offset = 4; + REVERT_GENERICINST_TO_WORD(generic_inst); + } + ) + emit_base_offset(GTM_REG_FRAME_TMP_PTR, offset); + break; + + case TCAD_REF: + case TVAD_REF: + case TVAR_REF: + offset = sa_temps_offset[opr->oprclass]; + offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; + if (val_output) + { + memcpy(obpt, &vdat_def[0], VDAT_DEF_SIZE); + obpt += VDAT_DEF_SIZE; + } + if (offset < 127) + { + memcpy(obpt, &vdat_bdisp[0], VDAT_BDISP_SIZE); + obpt += VDAT_BDISP_SIZE; + } else + { + memcpy(obpt, &vdat_wdisp[0], VDAT_WDISP_SIZE); + obpt += VDAT_WDISP_SIZE; + } + obpt = i2asc((uchar_ptr_t)obpt, offset); + if (opr->oprclass == TVAR_REF) + { + memcpy(obpt, &vdat_r8[0], VDAT_R8_SIZE); + obpt += VDAT_R8_SIZE; + } else + { + memcpy(obpt, &vdat_r9[0], VDAT_R9_SIZE); + obpt += VDAT_R9_SIZE; + } + NON_GTM64_ONLY( + if (offset < 0 || offset > MAX_OFFSET) + GTMASSERT; + ) + if (opr->oprclass == TVAR_REF) + reg = GTM_REG_FRAME_VAR_PTR; + else + reg = GTM_REG_FRAME_TMP_PTR; + GTM64_ONLY( + if (sa_class_sizes[opr->oprclass] == 4) + { + next_ptr_offset = 4; + REVERT_GENERICINST_TO_WORD(generic_inst); + } + ) + + emit_base_offset(reg, offset); + if (val_output) /* indirection */ + { + + if (GENERIC_OPCODE_LDA == generic_inst) + { + RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_NATIVE_REG(trg_reg);) + NON_RISC_ONLY(IGEN_LOAD_NATIVE_REG(trg_reg)) + if (opr->oprclass == TVAR_REF) + { + emit_base_offset(trg_reg, offsetof(ht_ent_mname, value)); + NON_RISC_ONLY(IGEN_LOAD_NATIVE_REG(trg_reg)) + RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_NATIVE_REG(trg_reg);) + } + inst_emitted = TRUE; + } else + { + RISC_ONLY(code_buf[code_idx++] + |= IGEN_LOAD_NATIVE_REG(GTM_REG_CODEGEN_TEMP);) + NON_RISC_ONLY(IGEN_LOAD_NATIVE_REG(GTM_REG_CODEGEN_TEMP)) + emit_base_offset(GTM_REG_CODEGEN_TEMP, 0); + } + } + break; + + case OCNT_REF: + immediate = opr->oprval.offset; + assert(0 < immediate); + assert(MAX_BRANCH_CODEGEN_SIZE > immediate); + EMIT_TRIP_ILIT_GEN; + inst_emitted = TRUE; + ocnt_ref_seen = TRUE; + ocnt_ref_opr = opr; + break; + + default: + GTMASSERT; + break; + } + if (!inst_emitted) { + RISC_ONLY(code_buf[code_idx++] |= IGEN_GENERIC_REG(generic_inst, trg_reg);) + NON_RISC_ONLY(IGEN_GENERIC_REG(generic_inst, trg_reg)) + } + *obpt++ = ','; + *obpt++ = ' '; + break; +# endif + case CGP_MACHINE: + switch (opr->oprclass) + { + case TRIP_REF: + assert(ct->destination.oprclass == 0); + assert(val_output); + switch (ct->opcode) + { + case OC_LIT: + assert(ct->operand[0].oprclass == MLIT_REF); + offset = literal_offset(ct->operand[0].oprval.mlit->rt_addr); + if (run_time) + reg = GTM_REG_PV; + else + reg = GTM_REG_LITERAL_BASE; + X86_64_ONLY(force_32 = TRUE;) + emit_base_offset(reg, offset); + X86_64_ONLY(force_32 = FALSE;) + RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_ADDR_REG(trg_reg);) + NON_RISC_ONLY(IGEN_LOAD_ADDR_REG(trg_reg);) + inst_emitted = TRUE; + break; + case OC_CDLIT: + emit_base_offset(GTM_REG_PV, find_linkage(ct->operand[0].oprval.cdlt)); + if (GENERIC_OPCODE_LDA == generic_inst) + { + RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_LINKAGE(trg_reg);) + NON_RISC_ONLY(IGEN_LOAD_LINKAGE(trg_reg);) + inst_emitted = TRUE; + } else + { + RISC_ONLY(code_buf[code_idx++] + |= IGEN_LOAD_LINKAGE(GTM_REG_CODEGEN_TEMP);) + NON_RISC_ONLY(IGEN_LOAD_LINKAGE(GTM_REG_CODEGEN_TEMP);) + emit_base_offset(GTM_REG_CODEGEN_TEMP, 0); + } + break; + case OC_ILIT: + assert(GENERIC_OPCODE_LOAD == generic_inst); + immediate = ct->operand[0].oprval.ilit; + EMIT_TRIP_ILIT_GEN; + inst_emitted = TRUE; + break; + case OC_TRIPSIZE: + immediate = ct->operand[0].oprval.tsize->size; + assert(0 < immediate); + assert(MAX_BRANCH_CODEGEN_SIZE > immediate); + EMIT_TRIP_ILIT_GEN; + inst_emitted = TRUE; + break; + default: + GTMASSERT; + break; + } + break; + + case TINT_REF: + case TVAL_REF: + assert(val_output); + offset = sa_temps_offset[opr->oprclass]; + offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; + GTM64_ONLY( + if ( sa_class_sizes[opr->oprclass] == 4 ) + { + next_ptr_offset = 4; + REVERT_GENERICINST_TO_WORD(generic_inst); + } + ) + + NON_GTM64_ONLY( + if (offset < 0 || offset > MAX_OFFSET) + GTMASSERT; + ) + emit_base_offset(GTM_REG_FRAME_TMP_PTR, offset); + break; + + case TCAD_REF: + case TVAD_REF: + case TVAR_REF: + offset = sa_temps_offset[opr->oprclass]; + offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; + GTM64_ONLY( + if (sa_class_sizes[opr->oprclass] == 4) + { + next_ptr_offset = 4; + REVERT_GENERICINST_TO_WORD(generic_inst); + } + ) + + NON_GTM64_ONLY( + if (offset < 0 || offset > MAX_OFFSET) + GTMASSERT; + ) + if (opr->oprclass == TVAR_REF) + reg = GTM_REG_FRAME_VAR_PTR; + else + reg = GTM_REG_FRAME_TMP_PTR; + emit_base_offset(reg, offset); + if (val_output) /* indirection */ + { + if (GENERIC_OPCODE_LDA == generic_inst) + { + RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_NATIVE_REG(trg_reg);) + NON_RISC_ONLY(IGEN_LOAD_NATIVE_REG(trg_reg)) + if (opr->oprclass == TVAR_REF) + { + emit_base_offset(trg_reg, offsetof(ht_ent_mname, value)); + NON_RISC_ONLY(IGEN_LOAD_NATIVE_REG(trg_reg)) + RISC_ONLY(code_buf[code_idx++] |= IGEN_LOAD_NATIVE_REG(trg_reg);) + } + inst_emitted = TRUE; + } else + { + RISC_ONLY(code_buf[code_idx++] + |= IGEN_LOAD_NATIVE_REG(GTM_REG_CODEGEN_TEMP);) + NON_RISC_ONLY(IGEN_LOAD_NATIVE_REG(GTM_REG_CODEGEN_TEMP);) + emit_base_offset(GTM_REG_CODEGEN_TEMP, 0); + } + } + break; + + case OCNT_REF: + immediate = opr->oprval.offset; + assert(0 <= immediate); + assert(MAX_BRANCH_CODEGEN_SIZE > immediate); + EMIT_TRIP_ILIT_GEN; + inst_emitted = TRUE; + ocnt_ref_seen = TRUE; + ocnt_ref_opr = opr; + break; + + default: + GTMASSERT; + break; + } + /* If we haven't emitted a finished instruction already, finish it now */ + if (!inst_emitted) { + RISC_ONLY(code_buf[code_idx++] |= IGEN_GENERIC_REG(generic_inst, trg_reg);) + NON_RISC_ONLY(IGEN_GENERIC_REG(generic_inst, trg_reg)) + } + break; + default: + GTMASSERT; + break; + } +} + + +/* get_arg_reg + * + * Determines the argument position of the current argument and returns the number of the register to use for the + * value of the argument. If it's not one of the arguments passed in machine registers, get_arg_reg defaults to the + * accumulator emulator register. + * + * NOTE: because shrink_jmps does not always process emulated VAX instructions that generate arguments, it is crucial + * that get_arg_reg() and emit_push() predict the same number of instructions during the CGP_APPROX_ADDR phase as are + * actually generated during subsequent phases. In order to ensure this, they emulate instruction generation backwards + * during the CGP_APPROX_ADDR and CGP_ADDR_OPT phases relative to the other phases. For example: + * + * CGP_APPROX_ADDR and CGP_ADDR_OPT phases: + * arg1 <- first argument, . . ., argN <- N th argument + * if more than N, series of: + * LOAD GTM_REG_ACCUM, next argument + * STORE GTM_REG_ACCUM, STACK_WORD_SIZE*(i-N)(sp) + * + * other phases: + * if more than N, series of: + * LOAD GTM_REG_ACCUM, next argument + * STORE GTM_REG_ACCUM, STACK_WORD_SIZE*(i-N)(sp) + * argN <- N th argument, . . ., arg1 <- first argument + * where STACK_WORD_SIZE is 8(Alpha) or 4(other platforms). + * + * While this technique correctly predicts the number of arguments, it does not guarantee to start any of the + * individual argument instruction sequences, except the first, during the CGP_APPROX_ADDR phase at the same + * code_reference address as it will for subsequent phases. This is because, although it predicts (or should) + * the same number of instructions during the CGP_APPROX_ADDR phase for an overall sequence of argument pushes, + * it does not do so in the same order as subsequent phases. Because we do not use PC-relative addressing for + * data on this platform, this difference should be benign (the subsequent xfer table call should be synchronized + * with respect to code_reference address across all phases). + */ + +int get_arg_reg(void) +{ + int arg_reg_i; + + switch (cg_phase) + { + case CGP_APPROX_ADDR: + case CGP_ADDR_OPT: + if (vax_pushes_seen < MACHINE_REG_ARGS) + arg_reg_i = GET_ARG_REG(vax_pushes_seen); + else + arg_reg_i = GTM_REG_ACCUM; + break; + + case CGP_ASSEMBLY: + case CGP_MACHINE: + if (vax_pushes_seen == 0) /* first push of a series */ + vax_number_of_arguments = next_vax_push_list(); + + if (vax_number_of_arguments <= MACHINE_REG_ARGS) + arg_reg_i = GET_ARG_REG(vax_number_of_arguments - 1); + else + arg_reg_i = GTM_REG_ACCUM; + break; + + default: + GTMASSERT; + break; + } + return arg_reg_i; +} + + +/* VAX reg to local machine reg */ +int gtm_reg(int vax_reg) +{ + int reg; + + switch (vax_reg & 0x0f) /* mask out VAX register mode field */ + { + case 0: + reg = GTM_REG_R0; + break; + case 1: + reg = GTM_REG_R1; + break; + case 8: + reg = GTM_REG_FRAME_VAR_PTR; + break; + case 9: + reg = GTM_REG_FRAME_TMP_PTR; + break; + +# ifdef TRUTH_IN_REG + case 10: + /* The value of $TEST is maintained in r10 for the VAX GT.M + * implementation. On platforms with an insufficient number of + * non-volatile (saved) registers, the value of $TEST is maintained + * only in memory; when sufficient registers are available, though, + * we keep $TEST in one of them. + */ + reg = GTM_REG_DOLLAR_TRUTH; + break; +# endif + + case 11: + reg = GTM_REG_XFER_TABLE; + break; + case 12: + reg = GTM_REG_FRAME_POINTER; + break; /* VMS ap */ + default: + GTMASSERT; + break; + } + + return reg; +} + + +void emit_push(int reg) +{ + int arg_reg_i; + int stack_offset; + + switch (cg_phase) + { + case CGP_APPROX_ADDR: + case CGP_ADDR_OPT: + if (vax_pushes_seen >= MACHINE_REG_ARGS) + { + RISC_ONLY(code_idx++;) /* for STORE instruction */ + NON_RISC_ONLY( + assert(reg == GTM_REG_ACCUM); + stack_offset = STACK_ARG_OFFSET((vax_number_of_arguments - MACHINE_REG_ARGS - 1)); + GEN_STORE_ARG(reg, stack_offset); /* Store arg on stack */ + ) + } + break; + + case CGP_ASSEMBLY: + case CGP_MACHINE: + if (vax_number_of_arguments <= MACHINE_REG_ARGS) + assert(reg == GET_ARG_REG(vax_number_of_arguments - 1)); + else + { + assert(reg == GTM_REG_ACCUM); + stack_offset = STACK_ARG_OFFSET((vax_number_of_arguments - MACHINE_REG_ARGS - 1)); + GEN_STORE_ARG(reg, stack_offset); /* Store arg on stack */ + } + break; + + default: + GTMASSERT; + break; + } + + if (cg_phase == CGP_MACHINE || cg_phase == CGP_ASSEMBLY) + { + vax_number_of_arguments--; /* actually, it's the number of arguments remaining */ + assert(vax_number_of_arguments >= 0); + } + vax_pushes_seen++; + stack_depth++; + return; +} + + +void emit_pop(int count) +{ + int stack_adjust; + + assert(stack_depth >= count); + stack_depth -= count; + /* It's possible we lost count after a jsb (see VXI_JSB). */ + if (stack_depth < 0) + stack_depth = 0; + return; +} + + +void add_to_vax_push_list(int pushes_seen) +{ + error_def(ERR_MAXARGCNT); + + /* Make sure there's enough room */ + if (pushes_seen > MAX_ARGS) + rts_error(VARLSTCNT(3) ERR_MAXARGCNT, 1, MAX_ARGS); + + push_list_index++; + if (push_list_index >= PUSH_LIST_SIZE) + { + push_list_index = 0; + if (current_push_list_ptr->next == 0 ) + { + current_push_list_ptr->next = (struct push_list *)malloc(SIZEOF(*current_push_list_ptr)); + current_push_list_ptr->next->next = 0; + } + current_push_list_ptr = current_push_list_ptr->next; + } + current_push_list_ptr->value[push_list_index] = pushes_seen; +} + + +int next_vax_push_list(void) +{ + push_list_index++; + if (push_list_index >= PUSH_LIST_SIZE) + { + push_list_index=0; + if (current_push_list_ptr->next == 0 ) + GTMASSERT; + current_push_list_ptr = current_push_list_ptr->next; + } + + return (current_push_list_ptr->value[push_list_index]); +} + + +void push_list_init(void) +{ + push_list_index = -1; + if (push_list_start_ptr == 0) + { + push_list_start_ptr = (struct push_list *)malloc(SIZEOF(*current_push_list_ptr)); + push_list_start_ptr->next = 0; + } + current_push_list_ptr = push_list_start_ptr; +} + + +void reset_push_list_ptr(void) +{ + push_list_index = -1; + current_push_list_ptr = push_list_start_ptr; +} + + +void emit_call_xfer(int xfer) +{ + int offset; + unsigned char *c; + +# ifdef DEBUG + if (CGP_ASSEMBLY == cg_phase) + { + memcpy(obpt, &vdat_def[0], VDAT_DEF_SIZE); + obpt += VDAT_DEF_SIZE; + if (xfer < 127) + { + memcpy(obpt, &vdat_bdisp[0], VDAT_BDISP_SIZE); + obpt += VDAT_BDISP_SIZE; + } else + { + memcpy(obpt, &vdat_wdisp[0], VDAT_WDISP_SIZE); + obpt += VDAT_WDISP_SIZE; + } + offset = (int)(xfer / SIZEOF(char *)); + for (c = (unsigned char *)xfer_name[offset]; *c ; ) + *obpt++ = *c++; + memcpy(obpt, &vdat_r11[0], VDAT_R11_SIZE); + obpt += VDAT_R11_SIZE; + *obpt++ = ','; + *obpt++ = ' '; + } +# endif + + assert(0 == (xfer & 0x3)); + offset = (int)(xfer / SIZEOF(char *)); +# ifdef __x86_64__ + /* Set RAX to 0 for variable argument function calls. This is part of the ABI. + * The RAX represents the # of floating of values being passed + */ + if (GTM_C_VAR_ARGS_RTN == xfer_table_desc[offset]) + { + GEN_LOAD_IMMED(I386_REG_RAX, 0); + } +# endif /* __x86_64__ */ + +# ifdef __ia64 + if (GTM_ASM_RTN == xfer_table_desc[offset]) + { + GEN_XFER_TBL_CALL_FAKE(xfer); + } else + { + GEN_XFER_TBL_CALL_DIRECT(xfer); + + } +# else + GEN_XFER_TBL_CALL(xfer); +# endif /* __ia64 */ + + /* In the normal case we will return */ + if (!ocnt_ref_seen) + return; /* fast test for return .. we hope */ + + /* If ocnt_ref_seen is set, then we need to compute the value to be used by a recent + OCNT_REF parameter. This parameter is (currently as of 6/2003) used by op_call, op_callsp, + op_forlcldo, and their mprof counterparts and is the number of bytes those entry points + should add to the return address that they will store as the return point in the new stack + frame that they create. This parameter is basically the size of the generated code for the + jump that follows the call to the above routines that is generates by the associated + triples OC_CALL, OC_CALLSP, and OC_FORLCLDO respectively. Since this jump can be variable in + size and the only other way for these routines to know what form the jump takes is to parse + the instructions at run time, this routine in the compiler will calculate that information and + allow it to be passed in as a parameter. The OCNT_REF handler in emit_trip() has set the + ocnt_ref_seen flag to bring us here. We now calculate the current PC address and subtract it + from the PC address of the next triple. + */ + assert(OC_CALL == current_triple->opcode || OC_CALLSP == current_triple->opcode || + OC_FORLCLDO == current_triple->opcode); + offset = current_triple->exorder.fl->rtaddr - (code_reference + (code_idx * INST_SIZE)); + /* If in assembly or machine (final) phases, make sure have reasonable offset. The offset may be + negative in the early phases so don't check during them. For other phases, put a govenor on + the values so we don't affect the codegen sizes which can mess up shrink_trips. During the + triple shrink phase, the triple distances can vary widely and cause the codegen to change + sizes. Note this still allows an assert fail for 0 if a negative number was being produced. + */ + if (CGP_MACHINE == cg_phase || CGP_ASSEMBLY == cg_phase) + { + assert(0 <= offset && MAX_BRANCH_CODEGEN_SIZE > offset); + } else + offset = MAX(0, MIN(128, offset)); + ocnt_ref_opr->oprval.offset = offset; + ocnt_ref_seen = FALSE; +} diff --git a/sr_port/emit_code.h b/sr_port/emit_code.h new file mode 100644 index 0000000..3bf0c2d --- /dev/null +++ b/sr_port/emit_code.h @@ -0,0 +1,63 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef EMIT_CODE_INCLUDED +#define EMIT_CODE_INCLUDED + +#include "emit_code_sp.h" + +#ifdef DEBUG +void emit_asmlist(triple *ct); +void emit_eoi(void); +#endif +void trip_gen(triple *ct); +short *emit_vax_inst(short *inst, oprtype **fst_opr, oprtype **lst_opr); +void emit_jmp(uint4 branchop, short **instp, int reg); +void emit_pcrel(void); +void emit_trip(oprtype *opr, boolean_t val_output, uint4 alpha_inst, int ra_reg); +void emit_push(int reg); +void emit_pop(int count); +void add_to_vax_push_list(int pushes_seen); +int next_vax_push_list(void); +void push_list_init(void); +void reset_push_list_ptr(void); +void emit_call_xfer(int xfer); + +int get_arg_reg(void); +int gtm_reg(int vax_reg); + +#ifdef __x86_64__ +# define NUM_BUFFERRED_INSTRUCTIONS 100 +# define CODE_TYPE char +#elif defined(__ia64) +# define CODE_TYPE ia64_bundle +# define NUM_BUFFERRED_INSTRUCTIONS 25 +#elif defined(__MVS__) || defined(Linux390) +# define CODE_TYPE uint2 +# define NUM_BUFFERRED_INSTRUCTIONS 100 +#else +# define CODE_TYPE uint4 +# define NUM_BUFFERRED_INSTRUCTIONS 25 +#endif + +#define ASM_OUT_BUFF 256 +#define PUSH_LIST_SIZE 500 + +#if defined(__vms) || defined(_AIX) || defined(__sparc) || defined(__hpux) || (defined(__linux__) && defined(__ia64)) \ + || defined(__MVS__) +# define TRUTH_IN_REG +#elif defined(__osf__) || (defined(__linux__) && defined(__x86_64__)) || defined(Linux390) +# undef TRUTH_IN_REG +#else +# error UNSUPPORTED PLATFORM +#endif + +#endif diff --git a/sr_port/entryref.c b/sr_port/entryref.c new file mode 100644 index 0000000..7e336ba --- /dev/null +++ b/sr_port/entryref.c @@ -0,0 +1,220 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" +#include "mlabel2xtern.h" +#include "mrout2xtern.h" +#include "gtmimagename.h" +#include "rtnhdr.h" +#include "stack_frame.h" + +GBLREF stack_frame *frame_pointer; +GBLREF mident routine_name; +GBLREF char window_token; +GBLREF mident window_ident; + +error_def(ERR_LABELEXPECTED); +error_def(ERR_RTNNAME); + +triple *entryref(opctype op1, opctype op2, mint commargcode, boolean_t can_commarg, boolean_t labref, boolean_t textname) +{ + oprtype offset, label, routine, rte1; + char rtn_text[SIZEOF(mident_fixed)], lab_text[SIZEOF(mident_fixed)]; + mident rtnname, labname; + mstr rtn_str, lbl_str; + triple *ref, *next, *rettrip; + boolean_t same_rout; + + rtnname.len = labname.len = 0; + rtnname.addr = &rtn_text[0]; + labname.addr = &lab_text[0]; + /* These cases don't currently exist but if they start to exist, the code in this + * routine needs to be revisited for proper operation as the textname conditions + * were assumed not to happen if can_commarg was FALSE (which it is in the one + * known use of textname TRUE - in m_zgoto). + */ + assert(!(can_commarg && textname)); + switch (window_token) + { + case TK_INTLIT: + int_label(); + /* caution: fall through */ + case TK_IDENT: + memcpy(labname.addr, window_ident.addr, window_ident.len); + labname.len = window_ident.len; + advancewindow(); + if ((TK_PLUS != window_token) && (TK_CIRCUMFLEX != window_token) && !IS_MCODE_RUNNING && can_commarg) + { + rettrip = newtriple(op1); + rettrip->operand[0] = put_mlab(&labname); + return rettrip; + } + label.oprclass = 0; + break; + case TK_ATSIGN: + if(!indirection(&label)) + return NULL; + if ((TK_PLUS != window_token) && (TK_CIRCUMFLEX != window_token) && (TK_COLON != window_token) + && can_commarg) + { + rettrip = ref = maketriple(OC_COMMARG); + ref->operand[0] = label; + ref->operand[1] = put_ilit(commargcode); + ins_triple(ref); + return rettrip; + } + labname.len = 0; + break; + case TK_PLUS: + stx_error(ERR_LABELEXPECTED); + return NULL; + default: + labname.len = 0; + label.oprclass = 0; + break; + } + if (!labref && (TK_PLUS == window_token)) + { /* Have line offset specified */ + advancewindow(); + if (!intexpr(&offset)) + return NULL; + } else + offset.oprclass = 0; + if (TK_CIRCUMFLEX == window_token) + { /* Have a routine name specified */ + advancewindow(); + switch(window_token) + { + case TK_IDENT: + MROUT2XTERN(window_ident.addr, rtnname.addr, window_ident.len); + rtn_str.len = rtnname.len = window_ident.len; + rtn_str.addr = rtnname.addr; + advancewindow(); + if (!IS_MCODE_RUNNING) + { /* Triples for indirect code */ + same_rout = (MIDENT_EQ(&rtnname, &routine_name) && can_commarg); + if (!textname) + { /* Resolve routine and label names to addresses for most calls */ + if (!label.oprclass && !offset.oprclass) + { /* Routine only (no label or offset) */ + if (same_rout) + { + rettrip = newtriple(op1); + rettrip->operand[0] = put_mlab(&labname); + } else + { + rettrip = maketriple(op2); + if (rtnname.addr[0] == '%') + rtnname.addr[0] = '_'; + rettrip->operand[0] = put_cdlt(&rtn_str); + mlabel2xtern(&lbl_str, &rtnname, &labname); + rettrip->operand[1] = put_cdlt(&lbl_str); + ins_triple(rettrip); + } + return rettrip; + } else if (!same_rout) + { + rte1 = put_str(rtn_str.addr, rtn_str.len); + if (rtnname.addr[0] == '%') + rtnname.addr[0] = '_'; + routine = put_cdlt(&rtn_str); + ref = newtriple(OC_RHDADDR); + ref->operand[0] = rte1; + ref->operand[1] = routine; + routine = put_tref(ref); + } else + routine = put_tref(newtriple(OC_CURRHD)); + } else + { /* Return the actual names used */ + if (!label.oprclass && !offset.oprclass) + { /* Routine only (no label or offset) */ + rettrip = maketriple(op2); + rettrip->operand[0] = put_str(rtn_str.addr, rtn_str.len); + ref = newtriple(OC_PARAMETER); + ref->operand[0] = put_str(labname.addr, labname.len); + ref->operand[1] = put_ilit(0); + rettrip->operand[1] = put_tref(ref); + ins_triple(rettrip); + return rettrip; + } else + routine = put_str(rtn_str.addr, rtn_str.len); + } + + } else + { /* Triples for normal compiled code */ + routine = put_str(rtn_str.addr, rtn_str.len); + if (!textname) + { /* If not returning text name, convert text name to routine header address */ + ref = newtriple(OC_RHDADDR1); + ref->operand[0] = routine; + routine = put_tref(ref); + } + } + break; + case TK_ATSIGN: + if (!indirection(&routine)) + return NULL; + if (!textname) + { /* If not returning text name, convert text name to routine header address */ + ref = newtriple(OC_RHDADDR1); + ref->operand[0] = routine; + routine = put_tref(ref); + } + break; + default: + stx_error(ERR_RTNNAME); + return NULL; + } + } else + { + if (!label.oprclass && (0 == labname.len)) + { + stx_error(ERR_LABELEXPECTED); + return NULL; + } + if (!textname) + routine = put_tref(newtriple(OC_CURRHD)); + else + { /* If we need a name, the mechanism to retrieve it differs between normal and indirect compilation */ + if (!IS_MCODE_RUNNING) + /* For normal compile, use routine name set when started compile */ + routine = put_str(routine_name.addr, routine_name.len); + else + /* For an indirect compile, obtain the currently running routine header and pull the routine + * name out of that. + */ + routine = put_str(frame_pointer->rvector->routine_name.addr, + frame_pointer->rvector->routine_name.len); + } + } + if (!offset.oprclass) + offset = put_ilit(0); + if (!label.oprclass) + label = put_str(labname.addr, labname.len); + ref = textname ? newtriple(OC_PARAMETER) : newtriple(OC_LABADDR); + ref->operand[0] = label; + next = newtriple(OC_PARAMETER); + ref->operand[1] = put_tref(next); + next->operand[0] = offset; + if (!textname) + next->operand[1] = routine; /* Not needed if giving text names */ + rettrip = next = newtriple(op2); + next->operand[0] = routine; + next->operand[1] = put_tref(ref); + return rettrip; +} diff --git a/sr_port/err_check.c b/sr_port/err_check.c new file mode 100644 index 0000000..d21ba4f --- /dev/null +++ b/sr_port/err_check.c @@ -0,0 +1,45 @@ +/**************************************************************** + * * + * Copyright 2001, 2003 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "error.h" + +VMS_ONLY(LITREF) UNIX_ONLY(GBLREF) err_ctl merrors_ctl; +VMS_ONLY(LITREF) UNIX_ONLY(GBLREF) err_ctl cmerrors_ctl; +VMS_ONLY(LITREF) UNIX_ONLY(GBLREF) err_ctl cmierrors_ctl; +VMS_ONLY(LITREF) UNIX_ONLY(GBLREF) err_ctl gdeerrors_ctl; +#ifdef VMS +LITREF err_ctl laerrors_ctl; /* Roger thinks that this one is obsolete */ +LITREF err_ctl lperrors_ctl; /* Roger thinks that this one may be obsolete */ +#endif + +const err_ctl *err_check(int errnum) +{ + const err_ctl *all_errors[] = {&merrors_ctl, &gdeerrors_ctl, &cmierrors_ctl, &cmerrors_ctl, +#ifdef VMS + &laerrors_ctl, &lperrors_ctl, +#endif + NULL}; + const err_ctl *fac; + int errtype; + + if (0 > errnum) + return 0; + + for (errtype = 0; all_errors[errtype]; errtype++) + { + fac = all_errors[errtype]; + if ((errnum & FACMASK(fac->facnum)) && + ((MSGMASK(errnum, fac->facnum)) <= fac->msg_cnt)) + return fac; + } + return NULL; +} diff --git a/sr_port/error.h b/sr_port/error.h new file mode 100644 index 0000000..d12fb9b --- /dev/null +++ b/sr_port/error.h @@ -0,0 +1,115 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __ERROR_H__ +#define __ERROR_H__ + +typedef struct err_msg_struct +{ + char *tag; + char *msg; + int parm_count; +} err_msg; + +typedef struct err_ctl_struct +{ + int facnum; + char *facname; + const err_msg *fst_msg; /* For VMS, this pointer is not used, and its value will typically be NULL */ + int msg_cnt; +} err_ctl; + +#include "errorsp.h" + +#define ERROR_RETURN error_return + +#define FCNTL 1 +#define MSGCNTL 27 +#define MSGFAC 16 +#define MSGNBIT 15 +#define MSGSEVERITY 3 +#define MSGNUM 3 + +#define FACMASK(fac) (FCNTL << MSGCNTL | 1 << MSGNBIT | (fac) << MSGFAC) +#define MSGMASK(msg,fac) (((msg) & ~FACMASK(fac)) >> MSGSEVERITY) +#define SEVMASK(msg) ((msg) & 7) + +/* to change default severity of msg to type */ +#define MAKE_MSG_TYPE(msg, type) ((msg) & ~SEV_MSK | (type)) + +/* Macro used intermittently to trace various error handling invocations */ +/* #define DEBUG_ERRHND */ +#ifdef DEBUG_ERRHND +# define DBGEHND(x) DBGFPF(x) +# define DBGEHND_ONLY(x) x +#else +# define DBGEHND(x) +# define DBGEHND_ONLY(x) +#endif + +const err_ctl *err_check(int err); + +CONDITION_HANDLER(ccp_ch); +CONDITION_HANDLER(ccp_exi_ch); +CONDITION_HANDLER(compiler_ch); +CONDITION_HANDLER(cre_priv_ch); +CONDITION_HANDLER(dbinit_ch); +CONDITION_HANDLER(dse_dmp_handler); +CONDITION_HANDLER(dse_f_blk_ch); +CONDITION_HANDLER(exi_ch); +CONDITION_HANDLER(fgncal_ch); +CONDITION_HANDLER(fntext_ch); +CONDITION_HANDLER(gds_rundown_ch); +CONDITION_HANDLER(gtcm_ch); +CONDITION_HANDLER(gtcm_exi_ch); +CONDITION_HANDLER(gtm_env_xlate_ch); +CONDITION_HANDLER(gtmrecv_ch); +CONDITION_HANDLER(gtmrecv_fetchresync_ch); +CONDITION_HANDLER(gtmsource_ch); +CONDITION_HANDLER(gvcmy_open_ch); +CONDITION_HANDLER(gvcmz_netopen_ch); +CONDITION_HANDLER(gvzwrite_ch); +CONDITION_HANDLER(hashtab_rehash_ch); +CONDITION_HANDLER(jobexam_dump_ch); +CONDITION_HANDLER(iob_io_error); +CONDITION_HANDLER(io_init_ch); +CONDITION_HANDLER(iomt_ch); +CONDITION_HANDLER(job_init_ch); +CONDITION_HANDLER(lastchance1); +CONDITION_HANDLER(lastchance2); +CONDITION_HANDLER(lastchance3); +CONDITION_HANDLER(mdb_condition_handler); +CONDITION_HANDLER(mu_freeze_ch); +CONDITION_HANDLER(mu_int_ch); +CONDITION_HANDLER(mu_int_reg_ch); +CONDITION_HANDLER(mu_rndwn_file_ch); +CONDITION_HANDLER(mupip_load_ch); +CONDITION_HANDLER(mupip_recover_ch); +CONDITION_HANDLER(mupip_set_jnl_ch); +CONDITION_HANDLER(mur_multi_rehash_ch); +CONDITION_HANDLER(ojch); +CONDITION_HANDLER(region_init_ch); +CONDITION_HANDLER(replication_ch); +CONDITION_HANDLER(stp_gcol_ch); +CONDITION_HANDLER(t_ch); +CONDITION_HANDLER(terminate_ch); +CONDITION_HANDLER(tp_restart_ch); +CONDITION_HANDLER(trans_code_ch); +CONDITION_HANDLER(updproc_ch); +CONDITION_HANDLER(util_base_ch); +CONDITION_HANDLER(util_ch); +CONDITION_HANDLER(gtm_maxstr_ch); +CONDITION_HANDLER(zshow_ch); +CONDITION_HANDLER(zyerr_ch); + +void mum_tstart(); + +#endif diff --git a/sr_port/error_trap.h b/sr_port/error_trap.h new file mode 100644 index 0000000..752e4e4 --- /dev/null +++ b/sr_port/error_trap.h @@ -0,0 +1,153 @@ +/**************************************************************** + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef ERROR_TRAP_H +#define ERROR_TRAP_H + +#define IS_ETRAP (err_act == &dollar_etrap.str) + +#define DOLLAR_ECODE_MAXINDEX 32 /* maximum of 32 ecodes in $ECODE */ +#define DOLLAR_STACK_MAXINDEX 256 /* maximum of 256 levels will be stored for $STACK(level) */ + +#define DOLLAR_ECODE_ALLOC (1 << 15) /* 32K chunk memory malloced for $ECODE */ +#define DOLLAR_STACK_ALLOC (1 << 15) /* 32K chunk memory malloced for $STACK(level) */ + +#define STACK_ZTRAP_EXPLICIT_NULL -1 /* used to indicate an explicit SET $ZTRAP = "" */ + +typedef void (*error_ret_fnptr)(void); + +typedef struct dollar_ecode +{ + mstr ecode_str; +} dollar_ecode_struct; + +typedef struct dollar_stack /* contents of a single $STACK(level) entry */ +{ + mstr mode_str; /* $STACK(level) */ + dollar_ecode_struct *ecode_ptr; /* $STACK(level,"ECODE") */ + mstr mcode_str; /* $STACK(level,"MCODE") */ + mstr place_str; /* $STACK(level,"PLACE") */ +} dollar_stack_struct; + +typedef enum stack_mode +{ + DOLLAR_STACK_INVALID, + DOLLAR_STACK_ECODE, /* $STACK(level,"ECODE") */ + DOLLAR_STACK_PLACE, /* $STACK(level,"PLACE") */ + DOLLAR_STACK_MCODE, /* $STACK(level,"MCODE") */ + DOLLAR_STACK_MODE /* $STACK(level) i.e. how this frame got created */ +} stack_mode_t; + +typedef struct +{ + char *begin; /* beginning of malloced memory holding the complete $ECODE */ + char *end; /* pointer to where next $ECODE can be added */ + char *top; /* allocated end of malloced memory holding the complete $ECODE */ + dollar_ecode_struct *array; /* array of DOLLAR_ECODE_MAXINDEX dollar_ecode_struct structures */ + uint4 index; /* current count of number of filled structures in array */ + int4 error_last_ecode; /* last error code number */ + unsigned char *error_last_b_line; /* ptr to beginning of line where error occurred */ + struct stack_frame_struct *first_ecode_error_frame; /* "frame_pointer" at the time of adding the first ECODE */ + unsigned char *error_rtn_addr; /* CODE_ADDRESS(ERROR_RTN) */ + unsigned char *error_rtn_ctxt; /* GTM_CONTEXT(ERROR_RTN) */ + error_ret_fnptr error_return_addr; /* CODE_ADDRESS(ERROR_RETURN) */ +} dollar_ecode_type; + +typedef struct +{ + char *begin; /* beginning of malloced memory holding all $STACK(level) detail */ + char *end; /* pointer to where next $STACK(level) detail can be added */ + char *top; /* allocated end of malloced memory holding all $STACK(level) detail */ + dollar_stack_struct *array; /* array of DOLLAR_STACK_MAXINDEX dollar_stack_struct structures */ + uint4 index; /* current count of number of filled structures in array */ + boolean_t incomplete; /* TRUE if we were not able to fit in all $STACK info */ +} dollar_stack_type; + +/* reset all $ECODE related variables to correspond to $ECODE = NULL state */ +#define NULLIFY_DOLLAR_ECODE \ +{ \ + GBLREF dollar_ecode_type dollar_ecode; \ + GBLREF dollar_stack_type dollar_stack; \ + \ + dollar_ecode.end = dollar_ecode.begin; \ + dollar_ecode.index = 0; \ + dollar_stack.end = dollar_stack.begin; \ + dollar_stack.index = 0; \ + dollar_stack.incomplete = FALSE; \ + dollar_ecode.first_ecode_error_frame = NULL; \ +} + +/* nullify "error_frame" */ +#define NULLIFY_ERROR_FRAME \ +{ \ + GBLREF stack_frame *error_frame; \ + \ + error_frame = NULL; \ +} + +/* Set "error_frame" to point to "frame_pointer" and mark it as an error frame type. This is an indication that + * whenever we unwind back to this frame, we need to transfer control to error_rtn_addr/ctxt (taken care of by getframe). + */ +#define SET_ERROR_FRAME(fp) \ +{ \ + GBLREF stack_frame *error_frame; \ + \ + fp->flags |= SFF_ETRAP_ERR; \ + error_frame = fp; \ +} + +/* invoke the function error_return() if the necessity of error-rethrow is detected */ +#define INVOKE_ERROR_RET_IF_NEEDED \ +{ \ + GBLREF dollar_ecode_type dollar_ecode; \ + GBLREF stack_frame *error_frame; \ + \ + if (NULL != error_frame) \ + { \ + if (error_frame == frame_pointer) \ + { \ + if (dollar_ecode.index) /* non-zero implies non-NULL $ECODE */ \ + { /* this is an error frame and $ECODE is non-NULL during QUIT out of this frame. \ + * rethrow the error at lower level */ \ + (*dollar_ecode.error_return_addr)(); \ + /* We dont expect the above call to return in Unix since we either rethrow \ + * the error or do a MUM_TSTART which unwinds the C-stack. But in VMS, we dont \ + * do the latter so it is possible if the current frame is of type SFT_DM that \ + * we dont rethrow and dont do a MUM_TSTART as well. Assert accordingly. \ + */ \ + UNIX_ONLY(assert(FALSE);) /* this should not return */ \ + VMS_ONLY(assert(SFT_DM & frame_pointer->type);) \ + } else \ + { \ + assert(FALSE); \ + NULLIFY_ERROR_FRAME; /* don't know how we reached here. reset it in PRO */ \ + } \ + } else if (error_frame < frame_pointer) \ + { \ + assert(FALSE); \ + NULLIFY_ERROR_FRAME; /* don't know how we reached here. reset it in PRO */ \ + } \ + } \ +} + +void ecode_init(void); +void ecode_get(int level, mval *result); /* return $ECODE (if "level" < 0) or $STACK(level,"ECODE") in "result" */ +void ecode_set(int errnum); /* convert "errnum" to error-string and call ecode_add() */ +boolean_t ecode_add(mstr *str); /* add "str" to $ECODE */ +void error_return(void); +#ifdef VMS +void error_return_vms(void); +#endif + +void get_dollar_stack_info(int level, stack_mode_t mode, mval *result); +void get_frame_creation_info(int level, int cur_zlevel, mval *result); +void get_frame_place_mcode(int level, stack_mode_t mode, int cur_zlevel, mval *result); + +#endif /* ERROR_TRAP_H */ diff --git a/sr_port/eval_expr.c b/sr_port/eval_expr.c new file mode 100644 index 0000000..6195710 --- /dev/null +++ b/sr_port/eval_expr.c @@ -0,0 +1,124 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" +#include "compile_pattern.h" + +GBLREF triple *curtchain; +GBLREF char director_token, window_token; + +error_def(ERR_EXPR); +error_def(ERR_RHMISSING); + +LITREF octabstruct oc_tab[]; +LITREF toktabtype tokentable[]; + +int eval_expr(oprtype *a) +{ + triple *ref, *parm, *ref1; + int op_count; + oprtype x1, x2; + opctype i; + unsigned short type; + bool ind_pat; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + if (!expratom(&x1)) + { /* If didn't already add an error of our own, do so now with catch all expression error */ + if (OC_RTERROR != curtchain->exorder.bl->exorder.bl->exorder.bl->opcode) + stx_error(ERR_EXPR); + return EXPR_FAIL; + } + while (i = tokentable[window_token].bo_type) + { + type = tokentable[window_token].opr_type; + if (oc_tab[i].octype & OCT_BOOL) + { + if (!TREF(shift_side_effects)) + { + for (ref = curtchain->exorder.bl; oc_tab[ref->opcode].octype & OCT_BOOL; ref = ref->exorder.bl) + ; + TREF(expr_start) = TREF(expr_start_orig) = ref; + } + switch (i) + { + case OC_NAND: + case OC_AND: + case OC_NOR: + case OC_OR: + TREF(shift_side_effects) = -TRUE; /* "special" TRUE triggers warning in expritem */ + break; + default: + if (!TREF(shift_side_effects)) + TREF(shift_side_effects) = TRUE; + } + } + coerce(&x1, type); + if (OC_CAT == i) + { + ref1 = ref = maketriple(OC_CAT); + for (op_count = 2; ; op_count++) /* op_count = first operand plus destination */ + { + parm = newtriple(OC_PARAMETER); + ref1->operand[1] = put_tref(parm); + ref1 = parm; + ref1->operand[0] = x1; + if (TK_UNDERSCORE != window_token) + { + assert(op_count > 1); + ref->operand[0] = put_ilit(op_count); + ins_triple(ref); + break; + } + advancewindow(); + if (!expratom(&x1)) + { + stx_error(ERR_RHMISSING); + return EXPR_FAIL; + } + coerce(&x1, type); + } + } else + { + if ((TK_QUESTION == window_token) || (TK_NQUESTION == window_token)) + { + ind_pat = FALSE; + if (TK_ATSIGN == director_token) + { + ind_pat = TRUE; + advancewindow(); + } + if (!compile_pattern(&x2, ind_pat)) + return EXPR_FAIL; + } else + { + advancewindow(); + if (!expratom(&x2)) + { + stx_error(ERR_RHMISSING); + return EXPR_FAIL; + } + } + coerce(&x2, type); + ref = newtriple(i); + ref->operand[0] = x1; + ref->operand[1] = x2; + } + x1 = put_tref(ref); + } + *a = x1; + return (OC_INDGLVN == curtchain->exorder.bl->opcode) ? EXPR_INDR : EXPR_GOOD; +} diff --git a/sr_port/ex_tail.c b/sr_port/ex_tail.c new file mode 100644 index 0000000..1bf0877 --- /dev/null +++ b/sr_port/ex_tail.c @@ -0,0 +1,67 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "mdq.h" +#include "opcode.h" +#include "toktyp.h" +#include "mmemory.h" + +void ex_tail(oprtype *opr) +{ + LITREF octabstruct oc_tab[]; + triple *t, *t1, *t2, *bitrip; + oprtype *i; + opctype c; + unsigned short w; + + assert(opr->oprclass == TRIP_REF); + t = opr->oprval.tref; + c = t->opcode; + w = oc_tab[c].octype; + if (w & OCT_EXPRLEAF) + return; + assert(t->operand[0].oprclass == TRIP_REF); + assert(t->operand[1].oprclass == TRIP_REF || t->operand[1].oprclass == 0); + if (!(w & OCT_BOOL)) + { + for (i = t->operand ; i < ARRAYTOP(t->operand); i++) + if (i->oprclass == TRIP_REF) + ex_tail(i); + if (c == OC_COMINT && (t1 = t->operand[0].oprval.tref)->opcode == OC_BOOLINIT) + opr->oprval.tref = t1; + } else + { + for (t1 = t ; ; t1 = t2) + { + assert(t1->operand[0].oprclass == TRIP_REF); + t2 = t1->operand[0].oprval.tref; + if (!(oc_tab[t2->opcode].octype & OCT_BOOL)) + break; + } + bitrip = maketriple(OC_BOOLINIT); + dqins(t1->exorder.bl, exorder, bitrip); + t2 = t->exorder.fl; + assert(&t2->operand[0] == opr); + assert(t2->opcode == OC_COMVAL || t2->opcode == OC_COMINT); + if (t2->opcode == OC_COMINT) + dqdel(t2,exorder); + t1 = maketriple(OC_BOOLFINI); + t1->operand[0] = put_tref(bitrip); + opr->oprval.tref = bitrip; + dqins(t, exorder, t1); + i = (oprtype *) mcalloc(SIZEOF(oprtype)); + bx_tail(t,(bool) FALSE, i); + *i = put_tnxt(t1); + } + return; +} diff --git a/sr_port/exfun_frame.c b/sr_port/exfun_frame.c new file mode 100644 index 0000000..b862ef9 --- /dev/null +++ b/sr_port/exfun_frame.c @@ -0,0 +1,80 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" +#include "gtm_string.h" + +#include "rtnhdr.h" +#include "stack_frame.h" +#include "mprof.h" +#include "error.h" + +GBLREF stack_frame *frame_pointer; +GBLREF unsigned char *msp, *stackbase, *stackwarn, *stacktop; + +error_def(ERR_STACKCRIT); +error_def(ERR_STACKOFLOW); + +void exfun_frame (void) +{ + register stack_frame *sf; + unsigned char *msp_save; + + msp_save = msp; + sf = (stack_frame *)(msp -= SIZEOF(stack_frame)); /* Note imbedded assignment */ + assert(sf < frame_pointer); + if (msp <= stackwarn) + { + if (msp <= stacktop) + { + msp = msp_save; + rts_error(VARLSTCNT(1) ERR_STACKOFLOW); + } else + rts_error(VARLSTCNT(1) ERR_STACKCRIT); + } + assert (msp < stackbase); + assert((frame_pointer < frame_pointer->old_frame_pointer) || (NULL == frame_pointer->old_frame_pointer)); + *sf = *frame_pointer; + msp -= sf->rvector->temp_size; + if (msp <= stackwarn) + { + if (msp <= stacktop) + { + msp = msp_save; + rts_error(VARLSTCNT(1) ERR_STACKOFLOW); + } else + rts_error(VARLSTCNT(1) ERR_STACKCRIT); + } + sf->temps_ptr = msp; + assert (msp < stackbase); + memset (msp, 0, sf->rvector->temp_size); + sf->for_ctrl_stack = NULL; + sf->old_frame_pointer = frame_pointer; + frame_pointer = sf; + assert((frame_pointer < frame_pointer->old_frame_pointer) || (NULL == frame_pointer->old_frame_pointer)); + DBGEHND((stderr, "exfun_frame: Added stackframe at addr 0x"lvaddr" old-msp: 0x"lvaddr" new-msp: 0x"lvaddr"\n", + sf, msp_save, msp)); + return; +} + +void exfun_frame_sp(void) +{ + exfun_frame(); + new_prof_frame (TRUE); +} + +void exfun_frame_push_dummy_frame(void) +{ + exfun_frame(); + new_prof_frame (FALSE); +} diff --git a/sr_port/exfunc.c b/sr_port/exfunc.c new file mode 100644 index 0000000..c3a2e80 --- /dev/null +++ b/sr_port/exfunc.c @@ -0,0 +1,130 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "mdq.h" +#include "advancewindow.h" + +#define INDIR_DUMMY -1 + +GBLREF char window_token; + +error_def(ERR_ACTOFFSET); + +int exfunc(oprtype *a, boolean_t alias_target) +{ + triple *calltrip, *calltrip_opr1_tref, *counttrip, *funret, *labelref, *masktrip; + triple *oldchain, *ref0, *routineref, tmpchain, *triptr; +# if defined(USHBIN_SUPPORTED) || defined(VMS) + triple *tripsize; +# endif + + assert(TK_DOLLAR == window_token); + advancewindow(); + dqinit(&tmpchain, exorder); + oldchain = setcurtchain(&tmpchain); + calltrip = entryref(OC_EXFUN, OC_EXTEXFUN, INDIR_DUMMY, TRUE, TRUE, FALSE); + setcurtchain(oldchain); + if (!calltrip) + return FALSE; + if (OC_EXFUN == calltrip->opcode) + { + assert(MLAB_REF == calltrip->operand[0].oprclass); +# if defined(USHBIN_SUPPORTED) || defined(VMS) + ref0 = newtriple(OC_PARAMETER); + ref0->operand[0] = put_tsiz(); /* Need size of following code gen triple here */ + calltrip->operand[1] = put_tref(ref0); + tripsize = ref0->operand[0].oprval.tref; + assert(OC_TRIPSIZE == tripsize->opcode); +# else + ref0 = calltrip; +# endif + } else + { + calltrip_opr1_tref = calltrip->operand[1].oprval.tref; + if (OC_EXTEXFUN == calltrip->opcode) + { + assert(TRIP_REF == calltrip->operand[1].oprclass); + if (OC_CDLIT == calltrip_opr1_tref->opcode) + assert(CDLT_REF == calltrip_opr1_tref->operand[0].oprclass); + else + { + assert(OC_LABADDR == calltrip_opr1_tref->opcode); + assert(TRIP_REF == calltrip_opr1_tref->operand[1].oprclass); + assert(OC_PARAMETER == calltrip_opr1_tref->operand[1].oprval.tref->opcode); + assert(TRIP_REF == calltrip_opr1_tref->operand[1].oprval.tref->operand[0].oprclass); + assert(OC_ILIT == calltrip_opr1_tref->operand[1].oprval.tref->operand[0].oprval.tref->opcode); + assert(ILIT_REF + == calltrip_opr1_tref->operand[1].oprval.tref->operand[0].oprval.tref->operand[0].oprclass); + if (0 != calltrip_opr1_tref->operand[1].oprval.tref->operand[0].oprval.tref->operand[0].oprval.ilit) + { + stx_error(ERR_ACTOFFSET); + return FALSE; + } + } + } else /* $$ @dlabel [actuallist] */ + { + assert(OC_COMMARG == calltrip->opcode); + assert(TRIP_REF == calltrip->operand[1].oprclass); + assert(OC_ILIT == calltrip_opr1_tref->opcode); + assert(ILIT_REF == calltrip_opr1_tref->operand[0].oprclass); + assert(INDIR_DUMMY == calltrip_opr1_tref->operand[0].oprval.ilit); + assert(calltrip->exorder.fl == &tmpchain); + routineref = maketriple(OC_CURRHD); + labelref = maketriple(OC_LABADDR); + ref0 = maketriple(OC_PARAMETER); + dqins(calltrip->exorder.bl, exorder, routineref); + dqins(calltrip->exorder.bl, exorder, labelref); + dqins(calltrip->exorder.bl, exorder, ref0); + labelref->operand[0] = calltrip->operand[0]; + labelref->operand[1] = put_tref(ref0); + ref0->operand[0] = calltrip->operand[1]; + ref0->operand[0].oprval.tref->operand[0].oprval.ilit = 0; + ref0->operand[1] = put_tref(routineref); + calltrip->operand[0] = put_tref(routineref); + calltrip->operand[1] = put_tref(labelref); + calltrip->opcode = OC_EXTEXFUN; + } + ref0 = newtriple(OC_PARAMETER); + ref0->operand[0] = calltrip->operand[1]; + calltrip->operand[1] = put_tref(ref0); + } + if (TK_LPAREN != window_token) + { + masktrip = newtriple(OC_PARAMETER); + counttrip = newtriple(OC_PARAMETER); + masktrip->operand[0] = put_ilit(0); + counttrip->operand[0] = put_ilit(0); + masktrip->operand[1] = put_tref(counttrip); + ref0->operand[1] = put_tref(masktrip); + } else + if (!actuallist(&ref0->operand[1])) + return FALSE; + triptr = oldchain->exorder.bl; + dqadd(triptr, &tmpchain, exorder); /*this is a violation of info hiding*/ + if (OC_EXFUN == calltrip->opcode) + { + assert(MLAB_REF == calltrip->operand[0].oprclass); + triptr = newtriple(OC_JMP); + triptr->operand[0] = put_mfun(&calltrip->operand[0].oprval.lab->mvname); + calltrip->operand[0].oprclass = ILIT_REF; /* dummy placeholder */ +# if defined(USHBIN_SUPPORTED) || defined(VMS) + tripsize->operand[0].oprval.tsize->ct = triptr; +# endif + } + /* If target is an alias, use special container-expecting routine OC_EXFUNRETALS, else regular OC_EXFUNRET */ + funret = newtriple((alias_target ? OC_EXFUNRETALS : OC_EXFUNRET)); + funret->operand[0] = *a = put_tref(calltrip); + return TRUE; +} diff --git a/sr_port/exp.mpt b/sr_port/exp.mpt new file mode 100644 index 0000000..acea1cb --- /dev/null +++ b/sr_port/exp.mpt @@ -0,0 +1,24 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 1989,2001 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +%EXP ;GT.M %EXP utility - raise 1st argument to the power of the 2nd + ;invoke with %I and %J (%I**%J) to obtain result in %I + ;invoke at INT to execute interactively + ;invoke at FUNC as an extrinsic function + ; + s %I=$$FUNC(%I,%J) + q +INT n %I,%J + r !,"Power: ",%J r !,"Number: ",%I w !,%I," raised to ",%J," is ",$$FUNC(%I,%J),! + q +FUNC(i,j) + n f,w + i i<0,j#1 q "" + q i**j diff --git a/sr_port/expr.c b/sr_port/expr.c new file mode 100644 index 0000000..b6f861f --- /dev/null +++ b/sr_port/expr.c @@ -0,0 +1,41 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" + +int expr(oprtype *a) +{ + triple *triptr; + int4 rval; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + if (!(TREF(expr_depth))++) + TREF(expr_start) = TREF(expr_start_orig) = NULL; + if (!(rval = eval_expr(a))) + { + TREF(expr_depth) = 0; + return FALSE; + } + coerce(a,OCT_MVAL); + ex_tail(a); + if (!(--(TREF(expr_depth)))) + TREF(shift_side_effects) = FALSE; + if (TREF(expr_start) != TREF(expr_start_orig)) + { + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(TREF(expr_start)); + } + return rval; + +} diff --git a/sr_port/expratom.c b/sr_port/expratom.c new file mode 100644 index 0000000..939e497 --- /dev/null +++ b/sr_port/expratom.c @@ -0,0 +1,31 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" + +GBLREF char window_token; + +int expratom(oprtype *a) +{ + + switch(window_token) + { + case TK_IDENT: + case TK_CIRCUMFLEX: + case TK_ATSIGN: + return glvn(a); + default: + return expritem(a); + } +} diff --git a/sr_port/expritem.c b/sr_port/expritem.c new file mode 100644 index 0000000..6350b62 --- /dev/null +++ b/sr_port/expritem.c @@ -0,0 +1,633 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "compiler.h" +#include "svnames.h" +#include "opcode.h" +#include "toktyp.h" +#include "nametabtyp.h" +#include "funsvn.h" +#include "advancewindow.h" +#include "mdq.h" +#include "stringpool.h" +#include "namelook.h" +#include "fullbool.h" +#include "show_source_line.h" + +GBLREF bool devctlexp; +GBLREF char director_token; +GBLREF triple *curtchain; +GBLREF boolean_t run_time; +GBLREF mident window_ident; +GBLREF mval window_mval; +GBLREF char window_token; + +error_def(ERR_BOOLSIDEFFECT); +error_def(ERR_EXPR); +error_def(ERR_FCNSVNEXPECTED); +error_def(ERR_FNOTONSYS); +error_def(ERR_INVFCN); +error_def(ERR_INVSVN); +error_def(ERR_RPARENMISSING); +error_def(ERR_VAREXPECTED); + +LITREF toktabtype tokentable[]; +LITREF mval literal_null; + +#ifndef UNICODE_SUPPORTED +#define f_char f_zchar +#endif + +/* note that svn_index array provides indexes into this array for each letter of the + * alphabet so changes here should be reflected there. + */ +LITDEF nametabent svn_names[] = +{ + { 1, "D" }, { 6, "DEVICE" } + ,{ 2, "EC" }, { 5, "ECODE" } + ,{ 2, "ES" }, { 6, "ESTACK" } + ,{ 2, "ET" }, { 5, "ETRAP" } + ,{ 1, "H" }, { 7, "HOROLOG" } + ,{ 1, "I" }, { 2, "IO" } + ,{ 1, "J" }, { 3, "JOB" } + ,{ 1, "K" }, { 3, "KEY" } + ,{ 1, "P" }, { 8, "PRINCIPA*" } + ,{ 1, "Q" }, { 4, "QUIT" } + ,{ 1, "R" }, { 8, "REFERENC*" } + ,{ 1, "S" }, { 7, "STORAGE" } + ,{ 2, "ST" }, { 5, "STACK" } + ,{ 2, "SY" }, { 6, "SYSTEM" } + ,{ 1, "T" }, { 4, "TEST" } + ,{ 2, "TL"}, { 6, "TLEVEL"} + ,{ 2, "TR"}, { 8, "TRESTART"} + ,{ 1, "X" } + ,{ 1, "Y" } + ,{ 2, "ZA" } + ,{ 3, "ZAL*"} + ,{ 2, "ZB" } + ,{ 2, "ZC" } + ,{ 3, "ZCH" }, { 6, "ZCHSET" } + ,{ 3, "ZCM*" } + ,{ 3, "ZCO*" } + ,{ 3, "ZCS*" } + ,{ 3, "ZDA*" } + ,{ 2, "ZD*" } + ,{ 2, "ZE" } + ,{ 3, "ZED*" } + ,{ 3, "ZEO*" } + ,{ 3, "ZER*" } + ,{ 2, "ZG*" } + ,{ 4, "ZINI*"} + ,{ 4, "ZINT*"} + ,{ 3, "ZIO" } + ,{ 2, "ZJ" }, { 4, "ZJOB" } + ,{ 2, "ZL*" } + ,{ 8, "ZMAXTPTI*" } + ,{ 3, "ZMO*" } + ,{ 5, "ZPATN" }, {8, "ZPATNUME*" } + ,{ 4, "ZPOS*" } + ,{ 5, "ZPROC*" } + ,{ 5, "ZPROM*" } + ,{ 2, "ZQ*" } + ,{ 3, "ZRE*" } + ,{ 3, "ZRO*" } + ,{ 3, "ZSO*" } + ,{ 2, "ZS" }, { 4, "ZSTA*" } + ,{ 5, "ZSTEP"} + ,{ 3, "ZSY*"} + ,{ 4, "ZTCO*"} + ,{ 4, "ZTDA*"} + ,{ 3, "ZTE" }, { 4, "ZTEX*"} + ,{ 4, "ZTLE*"} + ,{ 4, "ZTNA*"} + ,{ 4, "ZTOL*"} + ,{ 4, "ZTRI*"} + ,{ 4, "ZTSL*"} + ,{ 4, "ZTUP*"} + ,{ 4, "ZTVA*"} + ,{ 4, "ZTWO*"} + ,{ 2, "ZT*" } + ,{ 3, "ZUS*" } + ,{ 2, "ZV*" } + ,{ 4, "ZYER*" } +}; + +/* Indexes into svn_names array for each letter of the alphabet */ +LITDEF unsigned char svn_index[27] = { + 0, 0, 0, 0, 2, 8, 8, 8, 10, /* a b c d e f g h i */ + 12, 14 ,16, 16, 16, 16, 16, 18, 20, /* j k l m n o p q r */ + 22, 28, 34 ,34, 34, 34, 35, 36, 89 /* s t u v w x y z ~ */ +}; + +/* These entries correspond to the entries in the svn_names array */ +LITDEF svn_data_type svn_data[] = +{ + { SV_DEVICE, FALSE, ALL_SYS }, { SV_DEVICE, FALSE, ALL_SYS } + ,{ SV_ECODE, TRUE, ALL_SYS }, { SV_ECODE, TRUE, ALL_SYS } + ,{ SV_ESTACK, FALSE, ALL_SYS }, { SV_ESTACK, FALSE, ALL_SYS } + ,{ SV_ETRAP, TRUE, ALL_SYS }, { SV_ETRAP, TRUE, ALL_SYS } + ,{ SV_HOROLOG, FALSE, ALL_SYS }, { SV_HOROLOG, FALSE, ALL_SYS } + ,{ SV_IO, FALSE, ALL_SYS }, { SV_IO, FALSE, ALL_SYS } + ,{ SV_JOB, FALSE, ALL_SYS }, { SV_JOB, FALSE, ALL_SYS } + ,{ SV_KEY, FALSE, ALL_SYS }, { SV_KEY, FALSE, ALL_SYS } + ,{ SV_PRINCIPAL, FALSE, ALL_SYS }, { SV_PRINCIPAL, FALSE, ALL_SYS } + ,{ SV_QUIT, FALSE, ALL_SYS }, { SV_QUIT, FALSE, ALL_SYS } + ,{ SV_REFERENCE, FALSE, ALL_SYS }, { SV_REFERENCE, FALSE, ALL_SYS } + ,{ SV_STORAGE, FALSE, ALL_SYS }, { SV_STORAGE, FALSE, ALL_SYS } + ,{ SV_STACK, FALSE, ALL_SYS }, { SV_STACK, FALSE, ALL_SYS } + ,{ SV_SYSTEM, FALSE, ALL_SYS }, { SV_SYSTEM, FALSE, ALL_SYS } + ,{ SV_TEST, FALSE, ALL_SYS }, { SV_TEST, FALSE, ALL_SYS } + ,{ SV_TLEVEL, FALSE, ALL_SYS }, { SV_TLEVEL, FALSE, ALL_SYS } + ,{ SV_TRESTART, FALSE, ALL_SYS }, { SV_TRESTART, FALSE, ALL_SYS } + ,{ SV_X, TRUE, ALL_SYS } + ,{ SV_Y, TRUE, ALL_SYS } + ,{ SV_ZA, FALSE, ALL_SYS } + ,{ SV_ZALLOCSTOR, FALSE, ALL_SYS } + ,{ SV_ZB, FALSE, ALL_SYS } + ,{ SV_ZC, FALSE, ALL_SYS } + ,{ SV_ZCHSET, FALSE, ALL_SYS }, { SV_ZCHSET, FALSE, ALL_SYS } + ,{ SV_ZCMDLINE, FALSE, ALL_SYS } + ,{ SV_ZCOMPILE, TRUE, ALL_SYS } + ,{ SV_ZCSTATUS, FALSE, ALL_SYS} + ,{ SV_ZDATE_FORM, TRUE, ALL_SYS } + ,{ SV_ZDIR, TRUE, ALL_SYS } + ,{ SV_ZERROR, TRUE, ALL_SYS } + ,{ SV_ZEDITOR, FALSE, ALL_SYS } + ,{ SV_ZEOF, FALSE, ALL_SYS } + ,{ SV_ZERROR, TRUE, ALL_SYS } + ,{ SV_ZGBLDIR, TRUE, ALL_SYS } + ,{ SV_ZININTERRUPT, FALSE, ALL_SYS} + ,{ SV_ZINTERRUPT, TRUE, ALL_SYS} + ,{ SV_ZIO, FALSE, ALL_SYS } + ,{ SV_ZJOB, FALSE, ALL_SYS }, { SV_ZJOB, FALSE, ALL_SYS } + ,{ SV_ZLEVEL, FALSE, ALL_SYS } + ,{ SV_ZMAXTPTIME, TRUE, ALL_SYS } + ,{ SV_ZMODE, FALSE, ALL_SYS } + ,{ SV_ZPATNUMERIC, FALSE, ALL_SYS }, { SV_ZPATNUMERIC, FALSE, ALL_SYS } + ,{ SV_ZPOS, FALSE, ALL_SYS } + ,{ SV_ZPROC, FALSE, ALL_SYS } + ,{ SV_PROMPT, TRUE, ALL_SYS } + ,{ SV_ZQUIT, TRUE, ALL_SYS } + ,{ SV_ZREALSTOR, FALSE, ALL_SYS } + ,{ SV_ZROUTINES, TRUE, ALL_SYS } + ,{ SV_ZSOURCE, TRUE, ALL_SYS } + ,{ SV_ZSTATUS, TRUE, ALL_SYS }, { SV_ZSTATUS, TRUE, ALL_SYS } + ,{ SV_ZSTEP, TRUE, ALL_SYS } + ,{ SV_ZSYSTEM, FALSE, ALL_SYS } + ,{ SV_ZTCODE, FALSE, TRIGGER_OS } + ,{ SV_ZTDATA, FALSE, TRIGGER_OS } + ,{ SV_ZTEXIT, TRUE, ALL_SYS }, { SV_ZTEXIT, TRUE, ALL_SYS } + ,{ SV_ZTLEVEL, FALSE, TRIGGER_OS} + ,{ SV_ZTNAME, FALSE, TRIGGER_OS } + ,{ SV_ZTOLDVAL, FALSE, TRIGGER_OS } + ,{ SV_ZTRIGGEROP, FALSE, TRIGGER_OS} + ,{ SV_ZTSLATE, TRUE, TRIGGER_OS} + ,{ SV_ZTUPDATE, FALSE, TRIGGER_OS } + ,{ SV_ZTVALUE, TRUE, TRIGGER_OS } + ,{ SV_ZTWORMHOLE, TRUE, TRIGGER_OS } + ,{ SV_ZTRAP, TRUE, ALL_SYS } + ,{ SV_ZUSEDSTOR, FALSE, ALL_SYS } + ,{ SV_ZVERSION, FALSE, ALL_SYS } + ,{ SV_ZYERROR, TRUE, ALL_SYS } +}; + +/* note that fun_index array provides indexes into this array for each letter of the + * alphabet so changes here should be reflected there. + * "*" is used below only after 8 characters. + */ +LITDEF nametabent fun_names[] = +{ + {1, "A"}, {5, "ASCII"} + ,{1, "C"}, {4, "CHAR"} + ,{1, "D"}, {4, "DATA"} + ,{1, "E"}, {7, "EXTRACT"} + ,{1, "F"}, {4, "FIND"} + ,{2, "FN"}, {7, "FNUMBER"} + ,{1, "G"}, {3, "GET"} + ,{1, "I"}, {4, "INCR"}, {8, "INCREMEN*"} + ,{1, "J"}, {7, "JUSTIFY"} + ,{1, "L"}, {6, "LENGTH"} + ,{1, "N"} + ,{2, "NA"}, {4, "NAME"} + ,{4, "NEXT"} + ,{1, "O"}, {5, "ORDER"} + ,{1, "P"}, {5, "PIECE"} + ,{2, "QL"}, {7, "QLENGTH"} + ,{2, "QS"}, {8, "QSUBSCRI*"} + ,{1, "Q"}, {5, "QUERY"} + ,{1, "R"}, {6, "RANDOM"} + ,{2, "RE"}, {7, "REVERSE"} + ,{1, "S"}, {6, "SELECT"} + ,{2, "ST"}, {5, "STACK"} + ,{1, "T"}, {4, "TEXT"} + ,{2, "TR"}, {8, "TRANSLAT*"} + ,{1, "V*"} + ,{2, "ZA"}, {6, "ZASCII"} + ,{3, "ZAH"}, {8, "ZAHANDLE"} + ,{7, "ZBITAND"} + ,{8, "ZBITCOUN*"} + ,{8, "ZBITFIND"} + ,{7, "ZBITGET"} + ,{7, "ZBITLEN"} + ,{7, "ZBITNOT"} + ,{6, "ZBITOR"} + ,{7, "ZBITSET"} + ,{7, "ZBITSTR"} + ,{7, "ZBITXOR"} + ,{2, "ZC"}, {5, "ZCALL"} + ,{3, "ZCH"}, {5, "ZCHAR"} + ,{3, "ZCO"}, {8, "ZCONVERT"} + ,{2, "ZD"} + ,{5, "ZDATA"} + ,{5, "ZDATE"} + ,{2, "ZE"}, {8, "ZEXTRACT"} + ,{2, "ZF"}, {5, "ZFIND"} + ,{5, "ZFILE"}, {8, "ZFILEATT*"} + ,{7, "ZGETDVI"} + ,{7, "ZGETJPI"} + ,{7, "ZGETLKI"} + ,{7, "ZGETSYI"} + ,{5, "ZINCR"}, {8, "ZINCREME*"} + ,{2, "ZJ"}, {8, "ZJUSTIFY"} + ,{8, "ZJOBEXAM"} + ,{2, "ZL"}, {7, "ZLENGTH"} + ,{5, "ZLKID"} + ,{2, "ZM"}, {8, "ZMESSAGE"} + ,{2, "ZP"}, {8, "ZPREVIOU*"} + ,{6, "ZPARSE"} + ,{3, "ZPI"}, {6, "ZPIECE"} + ,{4, "ZPID"} + ,{5, "ZPRIV"}, {8, "ZPRIVILE*"} + ,{2, "ZQ"}, {8, "ZQGBLMOD"} + ,{7, "ZSEARCH"} + ,{7, "ZSETPRV"} + ,{8, "ZSIGPROC"} + ,{4, "ZSUB"}, {7, "ZSUBSTR"} + ,{3, "ZTR"}, {8, "ZTRANSLA*"} + ,{4, "ZTRI"}, {8, "ZTRIGGER"} + ,{7, "ZTRNLNM"} + ,{2, "ZW"}, {6, "ZWIDTH"} +}; + +/* Index into fun_names array where entries that start with each letter of the alphabet begin. */ +LITDEF unsigned char fun_index[27] = +{ + 0, 2, 2, 4, 6, 8, 12, 14, 14, /* a b c d e f g h i */ + 17, 19, 19, 21, 21, 25, 27, 29, 35, /* j k l m n o p q r */ + 39, 43, 47, 47, 48, 48, 48, 48, 113 /* s t u v w x y z ~ */ +}; + +/* Each entry corresponds to an entry in fun_names */ +LITDEF fun_data_type fun_data[] = +{ + { OC_FNASCII, ALL_SYS }, { OC_FNASCII, ALL_SYS } + ,{ OC_FNCHAR, ALL_SYS }, { OC_FNCHAR, ALL_SYS } + ,{ OC_FNDATA, ALL_SYS }, { OC_FNDATA, ALL_SYS } + ,{ OC_FNEXTRACT, ALL_SYS }, { OC_FNEXTRACT, ALL_SYS } + ,{ OC_FNFIND, ALL_SYS }, { OC_FNFIND, ALL_SYS } + ,{ OC_FNFNUMBER, ALL_SYS }, { OC_FNFNUMBER, ALL_SYS } + ,{ OC_FNGET, ALL_SYS }, { OC_FNGET, ALL_SYS } + ,{ OC_FNINCR, ALL_SYS }, { OC_FNINCR, ALL_SYS }, { OC_FNINCR, ALL_SYS } + ,{ OC_FNJ2, ALL_SYS }, { OC_FNJ2, ALL_SYS } + ,{ OC_FNLENGTH, ALL_SYS }, { OC_FNLENGTH, ALL_SYS } + ,{ OC_FNNEXT, ALL_SYS } + ,{ OC_FNNAME, ALL_SYS }, { OC_FNNAME, ALL_SYS } + ,{ OC_FNNEXT, ALL_SYS } + ,{ OC_FNORDER, ALL_SYS }, {OC_FNORDER, ALL_SYS } + ,{ OC_FNPIECE, ALL_SYS }, { OC_FNPIECE, ALL_SYS } + ,{ OC_FNQLENGTH, ALL_SYS }, { OC_FNQLENGTH, ALL_SYS } + ,{ OC_FNQSUBSCR, ALL_SYS }, { OC_FNQSUBSCR, ALL_SYS } + ,{ OC_FNQUERY, ALL_SYS }, { OC_FNQUERY, ALL_SYS } + ,{ OC_FNRANDOM, ALL_SYS }, { OC_FNRANDOM, ALL_SYS } + ,{ OC_FNREVERSE, ALL_SYS }, { OC_FNREVERSE, ALL_SYS } + ,{ OC_PASSTHRU, ALL_SYS }, { OC_PASSTHRU, ALL_SYS } + ,{ OC_FNSTACK1, ALL_SYS }, { OC_FNSTACK1, ALL_SYS } + ,{ OC_FNTEXT, ALL_SYS }, { OC_FNTEXT, ALL_SYS } + ,{ OC_FNTRANSLATE, ALL_SYS }, { OC_FNTRANSLATE, ALL_SYS } + ,{ OC_FNVIEW, ALL_SYS } + ,{ OC_FNZASCII, ALL_SYS }, { OC_FNZASCII, ALL_SYS } + ,{ OC_FNZAHANDLE, ALL_SYS }, { OC_FNZAHANDLE, ALL_SYS } + ,{ OC_FNZBITAND, ALL_SYS } + ,{ OC_FNZBITCOUN, ALL_SYS } + ,{ OC_FNZBITFIND, ALL_SYS } + ,{ OC_FNZBITGET, ALL_SYS } + ,{ OC_FNZBITLEN, ALL_SYS } + ,{ OC_FNZBITNOT, ALL_SYS } + ,{ OC_FNZBITOR, ALL_SYS } + ,{ OC_FNZBITSET, ALL_SYS } + ,{ OC_FNZBITSTR, ALL_SYS } + ,{ OC_FNZBITXOR, ALL_SYS } +# ifdef __sun + ,{ OC_FNZCALL,UNIX_OS}, { OC_FNZCALL,UNIX_OS} +# else + ,{ OC_FNZCALL, VMS_OS }, { OC_FNZCALL, VMS_OS } +# endif + ,{ OC_FNZCHAR, ALL_SYS }, { OC_FNZCHAR, ALL_SYS } + ,{ OC_FNZCONVERT2, UNIX_OS }, { OC_FNZCONVERT2, UNIX_OS } + ,{ OC_FNZDATE, ALL_SYS } + ,{ OC_FNZDATA, ALL_SYS } + ,{ OC_FNZDATE, ALL_SYS } + ,{ OC_FNZEXTRACT, ALL_SYS }, { OC_FNZEXTRACT, ALL_SYS } + ,{ OC_FNZFIND, ALL_SYS }, { OC_FNZFIND, ALL_SYS } + ,{ OC_FNZFILE, VMS_OS }, { OC_FNZFILE, VMS_OS } + ,{ OC_FNZGETDVI, VMS_OS } + ,{ OC_FNZGETJPI, ALL_SYS } + ,{ OC_FNZGETLKI, VMS_OS } + ,{ OC_FNZGETSYI, VMS_OS } + ,{ OC_FNINCR, ALL_SYS }, { OC_FNINCR, ALL_SYS } + ,{ OC_FNZJ2, ALL_SYS }, { OC_FNZJ2, ALL_SYS } + ,{ OC_FNZJOBEXAM, ALL_SYS } + ,{ OC_FNZLENGTH, ALL_SYS }, { OC_FNZLENGTH, ALL_SYS } + ,{ OC_FNZLKID, VMS_OS} + ,{ OC_FNZM, ALL_SYS }, { OC_FNZM, ALL_SYS } + ,{ OC_FNZPREVIOUS, ALL_SYS }, { OC_FNZPREVIOUS, ALL_SYS } + ,{ OC_FNZPARSE, ALL_SYS } + ,{ OC_FNZPIECE, ALL_SYS }, { OC_FNZPIECE, ALL_SYS } + ,{ OC_FNZPID, VMS_OS } + ,{ OC_FNZPRIV, VMS_OS }, { OC_FNZPRIV, VMS_OS } + ,{ OC_FNZQGBLMOD, ALL_SYS }, { OC_FNZQGBLMOD, ALL_SYS } + ,{ OC_FNZSEA, ALL_SYS } + ,{ OC_FNZSETPRV, VMS_OS } + ,{ OC_FNZSIGPROC, ALL_SYS } + ,{ OC_FNZSUBSTR, ALL_SYS }, { OC_FNZSUBSTR, ALL_SYS } + ,{ OC_FNZTRANSLATE, ALL_SYS }, { OC_FNZTRANSLATE, ALL_SYS } + ,{ OC_FNZTRIGGER, TRIGGER_OS }, { OC_FNZTRIGGER, TRIGGER_OS } + ,{ OC_FNZTRNLNM, ALL_SYS } + ,{ OC_FNZWIDTH, ALL_SYS }, { OC_FNZWIDTH, ALL_SYS } +}; + +/* Each entry corresponds to an entry in fun_names */ +GBLDEF int (*fun_parse[])(oprtype *, opctype) = /* contains addresses so can't be a LITDEF */ +{ + f_ascii, f_ascii, + f_char, f_char, + f_data, f_data, + f_extract, f_extract, + f_find, f_find, + f_fnumber, f_fnumber, + f_get, f_get, + f_incr, f_incr, f_incr, + f_justify, f_justify, + f_length, f_length, + f_next, + f_name, f_name, + f_next, + f_order, f_order, + f_piece, f_piece, + f_qlength, f_qlength, + f_qsubscript, f_qsubscript, + f_query, f_query, + f_mint, f_mint, + f_reverse, f_reverse, + f_select, f_select, + f_stack, f_stack, + f_text, f_text, + f_translate, f_translate, + f_view, + f_ascii, f_ascii, + f_zahandle, f_zahandle, + f_two_mval, + f_one_mval, + f_fnzbitfind, + f_fnzbitget, + f_one_mval, + f_one_mval, + f_two_mval, + f_fnzbitset, + f_fnzbitstr, + f_two_mval, + f_zcall, f_zcall, + f_zchar, f_zchar, + f_zconvert, f_zconvert, + f_zdate, + f_data, /* $ZDATA reuses parser for $DATA since only runtime execution differs */ + f_zdate, + f_extract, f_extract, + f_find, f_find, + f_two_mstrs, f_two_mstrs, + f_two_mstrs, + f_mint_mstr, + f_two_mstrs, + f_zgetsyi, + f_incr, f_incr, + f_justify, f_justify, + f_zjobexam, + f_length, f_length, + f_mint, + f_mint, f_mint, + f_zprevious, f_zprevious, + f_zparse, + f_piece, f_piece, + f_mint, + f_mstr, f_mstr, + f_zqgblmod, f_zqgblmod, + f_zsearch, + f_mstr, + f_zsigproc, + f_extract, f_extract, /* $ZSUBSTR */ + f_translate, f_translate, + f_ztrigger, f_ztrigger, + f_ztrnlnm, + f_zwidth, f_zwidth +}; + +int expritem(oprtype *a) +{ + int i, index, sv_opcode; + triple *oldchain, *ref, tmpchain, *triptr; + boolean_t parse_warn, save_shift; + oprtype x1; + char source_line_buff[MAX_SRCLINE + SIZEOF(ARROW)]; + unsigned char type; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + assert(svn_index[26] == (SIZEOF(svn_names)/SIZEOF(nametabent))); + assert(SIZEOF(svn_names)/SIZEOF(nametabent) == SIZEOF(svn_data)/SIZEOF(svn_data_type)); /* are all SVNs covered? */ + assert(fun_index[26] == (SIZEOF(fun_names)/SIZEOF(nametabent))); + assert(SIZEOF(fun_names)/SIZEOF(nametabent) == SIZEOF(fun_data)/SIZEOF(fun_data_type)); /* are all functions covered? */ + if (i = tokentable[window_token].uo_type) /* Note assignment */ + { + type = tokentable[window_token].opr_type; + advancewindow(); + if ((OC_NEG == i) && ((TK_NUMLIT == window_token) || (TK_INTLIT == window_token))) + { + assert(MV_IS_NUMERIC(&window_mval)); + if (window_mval.mvtype & MV_INT) + window_mval.m[1] = -window_mval.m[1]; + else + window_mval.sgn = 1; + if (TK_NUMLIT == window_token) + n2s(&window_mval); + } else + { + if (!expratom(&x1)) + return FALSE; + coerce(&x1,type); + ref = newtriple((opctype) i); + ref->operand[0] = x1; + *a = put_tref(ref); + return TRUE; + } + } + switch(i = window_token) /* Note assignment */ + { + case TK_INTLIT: + n2s(&window_mval); + case TK_NUMLIT: + case TK_STRLIT: + *a = put_lit(&window_mval); + advancewindow(); + return TRUE; + case TK_LPAREN: + advancewindow(); + if (eval_expr(a) && TK_RPAREN == window_token) + { + advancewindow(); + return TRUE; + } + stx_error(ERR_RPARENMISSING); + return FALSE; + case TK_DOLLAR: + if ((TK_DOLLAR == director_token) || (TK_AMPERSAND == director_token)) + { + if ((-TRUE == TREF(shift_side_effects)) && !run_time && (FULL_BOOL_WARN == TREF(gtm_fullbool))) + { /* warnings requested by by gtm_fullbool and enabled by -TRUE from eval_expr */ + show_source_line(source_line_buff, SIZEOF(source_line_buff), TRUE); + dec_err(VARLSTCNT(1) ERR_BOOLSIDEFFECT); + } + advancewindow(); + dqinit(&tmpchain, exorder); /* a new chain in case we need to juggle things */ + oldchain = setcurtchain(&tmpchain); + save_shift = TREF(shift_side_effects); + if (GTM_BOOL != TREF(gtm_fullbool)) /* if no short circuit, only the outermost should juggle */ + TREF(shift_side_effects) = FALSE; + TREF(temp_subs) = TRUE; + if ((TK_DOLLAR == window_token) ? !exfunc(a, FALSE) : !extern_func(a)) + { + setcurtchain(oldchain); + return FALSE; + } + TREF(shift_side_effects) = save_shift; + if (!save_shift || (GTM_BOOL == TREF(gtm_fullbool))) + { /* put it on the end of the main chain as there's no reason to play with the ordering */ + setcurtchain(oldchain); + triptr = curtchain->exorder.bl; + dqadd(triptr, &tmpchain, exorder); /* this is a violation of info hiding */ + } else /* put on the shift chain to get side effects in boolean expression */ + { /* add the chain after "expr_start" which may be much before "curtchain" */ + ref = newtriple(OC_GVSAVTARG); + dqadd(TREF(expr_start), &tmpchain, exorder); /* this is a violation of info hiding */ + TREF(expr_start) = tmpchain.exorder.bl; + setcurtchain(oldchain); + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(ref); + } + } else + { + advancewindow(); + if (TK_IDENT != window_token) + { + stx_error(ERR_FCNSVNEXPECTED); + return FALSE; + } + parse_warn = FALSE; + if (TK_LPAREN == director_token) + { + index = namelook(fun_index, fun_names, window_ident.addr, window_ident.len); + if (index < 0) + { + STX_ERROR_WARN(ERR_INVFCN); /* sets "parse_warn" to TRUE */ + } else + { + assert(SIZEOF(fun_names) / SIZEOF(fun_data_type) > index); + if (!VALID_FUN(index)) + { + STX_ERROR_WARN(ERR_FNOTONSYS); /* sets "parse_warn" to TRUE */ + } else if (OC_FNINCR == fun_data[index].opcode) + { /* $INCR is used. This can operate on undefined local variables + * and make them defined. If used in a SET where the left and right + * side of the = operator use this variable (as a subscript on the left + * and as input to the $INCR function on the right), we want an UNDEF + * error to show up which means we need to set "temp_subs" to TRUE. + */ + TREF(temp_subs) = TRUE; + if ((-TRUE == TREF(shift_side_effects)) && !run_time + && (FULL_BOOL_WARN == TREF(gtm_fullbool))) /* warnings requested */ + { /* by gtm_fullbool and enabled by -TRUE from eval_expr */ + show_source_line(source_line_buff, SIZEOF(source_line_buff), TRUE); + dec_err(VARLSTCNT(1) ERR_BOOLSIDEFFECT); + } + } + } + advancewindow(); + advancewindow(); + if (!parse_warn) + { + assert(OPCODE_COUNT > fun_data[index].opcode); + if (!(boolean_t)((*fun_parse[index])(a, fun_data[index].opcode))) + return FALSE; + } else + { + *a = put_lit((mval *)&literal_null); + /* Parse the remaining arguments until the corresponding RIGHT-PAREN/SPACE/EOL + is reached */ + if (!parse_until_rparen_or_space()) + return FALSE; + } + if (TK_RPAREN != window_token) + { + stx_error(ERR_RPARENMISSING); + return FALSE; + } + advancewindow(); + } else + { + index = namelook(svn_index, svn_names, window_ident.addr, window_ident.len); + if (0 > index) + { + STX_ERROR_WARN(ERR_INVSVN); /* sets "parse_warn" to TRUE */ + } else + { + assert(SIZEOF(svn_names) / SIZEOF(svn_data_type) > index); + if (!VALID_SVN(index)) + { + STX_ERROR_WARN(ERR_FNOTONSYS); /* sets "parse_warn" to TRUE */ + } + } + advancewindow(); + if (!parse_warn) + { + sv_opcode = svn_data[index].opcode; + assert(SV_NUM_SV > sv_opcode); + if (SV_TEST == sv_opcode) + *a = put_tref(newtriple(OC_GETTRUTH)); + else + { + if (sv_opcode == SV_X || sv_opcode == SV_Y) + devctlexp = TRUE; + ref = newtriple(OC_SVGET); + ref->operand[0] = put_ilit(sv_opcode); + *a = put_tref(ref); + } + } else + *a = put_lit((mval *)&literal_null); + } + } + return TRUE; + case TK_COLON: + stx_error(ERR_EXPR); + return FALSE; + } + return FALSE; +} diff --git a/sr_port/ext2jnl.c b/sr_port/ext2jnl.c new file mode 100644 index 0000000..98d90af --- /dev/null +++ b/sr_port/ext2jnl.c @@ -0,0 +1,288 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include /* for offsetof() macro */ +#include "gtm_ctype.h" + +#include "mlkdef.h" +#include "gtm_string.h" +#include "subscript.h" +#include "gdsroot.h" /* for filestruct.h */ +#include "gdsbt.h" /* for gdsfhead.h */ +#include "gtm_facility.h" /* for fileinfo.h */ +#include "fileinfo.h" /* for gdsfhead.h */ +#include "gdsfhead.h" /* for filestruct.h */ +#include "filestruct.h" /* for jnl.h */ +#include "jnl.h" +#include "repl_dbg.h" +#include "copy.h" +#include "zshow.h" +#include "mvalconv.h" +#include "str2gvkey.h" + +GBLREF char *ext_stop; +GBLREF gv_key *gv_currkey; +static boolean_t in_tp; +static int4 num_records; + +/* callers please set up the proper condition-handlers */ +/* expects a null-terminated ext_buff. does the equivalent but inverse of jnl2ext */ + +char *ext2jnlcvt(char *ext_buff, int4 ext_len, jnl_record *rec) +{ + char *ext_next; + jnl_record *temp_rec; + + temp_rec = rec; + for ( ; (NULL != (ext_next = strchr(ext_buff, '\n'))); ) + { + *ext_next++ = '\0'; + rec = (jnl_record *)ext2jnl(ext_buff, rec); + assert(0 == (INTPTR_T)rec % JNL_REC_START_BNDRY); + if (ext_stop == ext_buff) + break; + ext_buff = ext_next; + } + + assert(rec != temp_rec); + ext_stop = ext_buff; + return (char *)rec; +} + + +/* expects a single null-terminated ptr (equivalent to one line in the extract-file) */ + +char *ext2jnl(char *ptr, jnl_record *rec) +{ + unsigned char *pool_save, ch, chtmp; + int keylength, keystate, len, i, reclen, temp_reclen; + bool keepgoing; + mstr src, des; + jnl_record *temp_rec; + muextract_type exttype; + enum jnl_record_type rectype; + jrec_suffix *suffix; + uint4 nodeflags; + DEBUG_ONLY(uint4 tcom_num = 0;) + + ext_stop = ptr + strlen(ptr) + 1; + temp_rec = rec; + + exttype = (muextract_type)MUEXTRACT_TYPE(ptr); + assert((exttype >= 0) && (exttype < MUEXT_MAX_TYPES)); + + switch(exttype) + { + case MUEXT_SET: + if (in_tp) + { + if (0 == num_records) + { + num_records++; + rec->prefix.jrec_type = JRT_TSET; + } else + rec->prefix.jrec_type = JRT_USET; + } else + rec->prefix.jrec_type = JRT_SET; + break; + + case MUEXT_KILL: + if (in_tp) + { + if (0 == num_records) + { + num_records++; + rec->prefix.jrec_type = JRT_TKILL; + } else + rec->prefix.jrec_type = JRT_UKILL; + } else + rec->prefix.jrec_type = JRT_KILL; + break; + + case MUEXT_ZKILL: + if (in_tp) + { + if (0 == num_records) + { + num_records++; + rec->prefix.jrec_type = JRT_TZKILL; + } else + rec->prefix.jrec_type = JRT_UZKILL; + } else + rec->prefix.jrec_type = JRT_ZKILL; + break; + +# ifdef GTM_TRIGGER + case MUEXT_ZTWORM: + if (in_tp) + { + if (0 == num_records) + { + num_records++; + rec->prefix.jrec_type = JRT_TZTWORM; + } else + rec->prefix.jrec_type = JRT_UZTWORM; + } else + GTMASSERT; /* ZTWORMHOLE should always been seen only inside a TP fence */ + break; + case MUEXT_ZTRIG: + if (in_tp) + { + if (0 == num_records) + { + num_records++; + rec->prefix.jrec_type = JRT_TZTRIG; + } else + rec->prefix.jrec_type = JRT_UZTRIG; + } else + GTMASSERT; /* ZTRIGGER should always been seen only inside a TP fence */ + break; +# endif + + case MUEXT_TSTART: + in_tp = TRUE; + num_records = 0; + return (char *)rec; + break; + + case MUEXT_TCOMMIT: + rec->prefix.jrec_type = JRT_TCOM; + DEBUG_ONLY( + /* External filter format has only ONE TSTART..TCOM. The journal record received from the external filter + * SHOULD also have only ONE TSTART..TCOM + */ + tcom_num++; + assert(1 == tcom_num); + ) + rec->jrec_tcom.num_participants = 1; /* Only ONE TSTART..TCOM in the external filter format */ + in_tp = FALSE; + break; + + case MUEXT_PINI: + case MUEXT_PFIN: + case MUEXT_EOF: + case MUEXT_ZTSTART: + case MUEXT_ZTCOMMIT: + assert(FALSE); + ext_stop = ptr; + return (char *)rec; + break; + + case MUEXT_NULL: + rec->prefix.jrec_type = JRT_NULL; + break; + + default: + assert(FALSE); + ext_stop = ptr; + return (char *)rec; + break; + } + rectype = (enum jnl_record_type)rec->prefix.jrec_type; + ptr = strtok(ptr, "\\"); /* get the rec-type field */ + assert(NULL != ptr); + ptr = strtok(NULL, "\\"); /* get the time field */ + assert(NULL != ptr); + ptr = strtok(NULL, "\\"); /* get the tn field */ + assert(NULL != ptr); + rec->prefix.tn = asc2i((uchar_ptr_t)ptr, STRLEN(ptr)); + ptr = strtok(NULL, "\\"); /* get the pid field */ + assert(NULL != ptr); + ptr = strtok(NULL, "\\"); /* get the client pid field */ + assert(NULL != ptr); + ptr = strtok(NULL, "\\"); /* get the token or jnl_seqno */ + assert(NULL != ptr); + rec->jrec_null.jnl_seqno = asc2l((uchar_ptr_t)ptr,STRLEN(ptr)); + + switch(exttype) + { + case MUEXT_NULL: + rec->jrec_null.prefix.forwptr = rec->jrec_null.suffix.backptr = NULL_RECLEN; + rec->jrec_null.suffix.suffix_code = JNL_REC_SUFFIX_CODE; + return ((char_ptr_t)rec) + NULL_RECLEN; + case MUEXT_TCOMMIT: + ptr = strtok(NULL, "\\"); /* get the participants */ + ptr = strtok(NULL, "\\"); /* get the jnl_tid */ + rec->jrec_tcom.jnl_tid[0] = 0; + if (NULL != ptr) + strcpy(rec->jrec_tcom.jnl_tid, ptr); + num_records = 0; + rec->jrec_tcom.prefix.forwptr = rec->jrec_tcom.suffix.backptr = TCOM_RECLEN; + rec->jrec_tcom.suffix.suffix_code = JNL_REC_SUFFIX_CODE; + return ((char_ptr_t)rec) + TCOM_RECLEN; + } + assert(IS_SET_KILL_ZKILL_ZTRIG_ZTWORM(rectype)); + ptr = strtok(NULL, "\\"); /* get the update_num field */ + assert(NULL != ptr); + assert(OFFSETOF(struct_jrec_upd, update_num) == OFFSETOF(struct_jrec_ztworm, update_num)); + rec->jrec_set_kill.update_num = asc2i((uchar_ptr_t)ptr, STRLEN(ptr)); + if (MUEXT_ZTWORM != exttype) + { + ptr = strtok(NULL, "\\"); /* get the nodeflags field */ + assert(NULL != ptr); + rec->jrec_set_kill.mumps_node.nodeflags = asc2i((uchar_ptr_t)ptr, STRLEN(ptr)); + } + ptr += (strlen(ptr) + 1); /* get the key-value and data also; can't use strtok since there might be '\\' in the subscript */ + assert(NULL != ptr); + if (MUEXT_ZTWORM != exttype) + { + assert(IS_SET_KILL_ZKILL_ZTRIG(rectype)); + len = STRLEN(ptr); + keylength = zwrkeylength(ptr, len); /* determine length of key */ + + REPL_DPRINT2("ext2jnl source:KEY=DATA:%s\n", ptr); + assert(keylength <= len); + str2gvkey_nogvfunc(ptr, keylength, gv_currkey); + rec->jrec_set_kill.mumps_node.length = gv_currkey->end; + memcpy(rec->jrec_set_kill.mumps_node.text, gv_currkey->base, gv_currkey->end); + temp_reclen = (int)(FIXED_UPD_RECLEN + rec->jrec_set_kill.mumps_node.length + SIZEOF(jnl_str_len_t)); + if (IS_KILL_ZKILL(rectype)) + { + temp_reclen += JREC_SUFFIX_SIZE; + reclen = ROUND_UP2(temp_reclen, JNL_REC_START_BNDRY); + memset((char_ptr_t)rec + temp_reclen - JREC_SUFFIX_SIZE, 0, reclen - temp_reclen); + suffix = (jrec_suffix *)((char_ptr_t)rec + reclen - JREC_SUFFIX_SIZE); + rec->prefix.forwptr = suffix->backptr = reclen; + suffix->suffix_code = JNL_REC_SUFFIX_CODE; + return (char_ptr_t)rec + reclen; + } + /* we have to get the data value now */ + src.len = len - keylength - 1; + src.addr = ptr + (keylength + 1); + } else + { /* ZTWORMHOLE */ + assert(IS_ZTWORM(rectype)); + src.addr = ptr; + src.len = STRLEN(ptr); + temp_reclen = (int)(FIXED_ZTWORM_RECLEN); + } + des.len = 0; + des.addr = (char_ptr_t)rec + temp_reclen + SIZEOF(jnl_str_len_t); + REPL_DPRINT3("ext2jnl JNL Format (before zwr2format): src : Len %d :: DATA:%s\n", src.len, src.addr); + REPL_DPRINT3("ext2jnl JNL Format (before zwr2format): des : Len %d :: DATA:%s\n", des.len, des.addr); + if (!zwr2format(&src, &des)) + { + assert(FALSE); + return (char_ptr_t)rec; + } + REPL_DPRINT3("ext2jnl JNL Format : src : Len %d :: DATA:%s\n", src.len, src.addr); + REPL_DPRINT3("ext2jnl JNL Format : des : Len %d :: DATA:%s\n", des.len, des.addr); + PUT_MSTR_LEN((char_ptr_t)rec + temp_reclen, des.len); + temp_reclen += SIZEOF(jnl_str_len_t) + des.len + JREC_SUFFIX_SIZE; + reclen = ROUND_UP2(temp_reclen, JNL_REC_START_BNDRY); + memset((char_ptr_t)rec + temp_reclen - JREC_SUFFIX_SIZE, 0, reclen - temp_reclen); + suffix = (jrec_suffix *)((char_ptr_t)rec + reclen - JREC_SUFFIX_SIZE); + rec->prefix.forwptr = suffix->backptr = reclen; + suffix->suffix_code = JNL_REC_SUFFIX_CODE; + return (char_ptr_t)rec + reclen; +} diff --git a/sr_port/extern_func.c b/sr_port/extern_func.c new file mode 100644 index 0000000..1ca0ed5 --- /dev/null +++ b/sr_port/extern_func.c @@ -0,0 +1,159 @@ +/**************************************************************** + * * + * Copyright 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" +#ifdef VMS +#include "vaxsym.h" +#include "mmemory.h" +#endif + +GBLREF char director_token; +GBLREF char *lexical_ptr; +GBLREF unsigned char *source_buffer; +GBLREF char window_token; + +error_def(ERR_RTNNAME); + +/* Maximum size of external routine reference of the form label^routine */ +#ifdef UNIX +#define MAX_EXTREF (2 * MAX_MIDENT_LEN + STR_LIT_LEN("^")) +#endif + +/* compiler parse to AVT module for external functions ($&) */ +int extern_func(oprtype *a) +{ + char *extref; + mstr extentry, package; + oprtype *nxtopr; + triple *calltrip, *ref; + boolean_t have_ident; + int cnt, actcnt; +# ifdef VMS + char *extsym, *extern_symbol; + oprtype tabent; +# endif + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + assert (TK_AMPERSAND == window_token); + advancewindow(); + cnt = 0; + extref = (char *)&source_buffer[TREF(last_source_column) - 1]; + package.len = 0; + package.addr = NULL; + if (have_ident = (window_token == TK_IDENT)) /* assignment */ + { + if (TK_PERIOD == director_token) + { /* if ident is a package reference, then take it off */ + package.addr = extref; + package.len = INTCAST(lexical_ptr - extref - 1); + VMS_ONLY(package.len = ((package.len > MAX_EXTREF) ? MAX_EXTREF : package.len)); + extref = lexical_ptr; + advancewindow(); /* get to . */ + advancewindow(); /* to next token */ + if (have_ident = (TK_IDENT == window_token)) /* assignment */ + advancewindow(); + } else + advancewindow(); + } + if (TK_CIRCUMFLEX == window_token) + { + advancewindow(); + if (TK_IDENT == window_token) + { + have_ident = TRUE; + advancewindow(); + } + } + if (!have_ident) + { + stx_error(ERR_RTNNAME); + return FALSE; + } + extentry.len = INTCAST((char *)&source_buffer[TREF(last_source_column) - 1] - extref); + extentry.len = INTCAST(extentry.len > MAX_EXTREF ? MAX_EXTREF : extentry.len); + extentry.addr = extref; +#ifdef VMS_CASE_SENSITIVE_MACROS + if (!run_time) + { /* this code is disabled because the + * external call table macros are not case sensitive + */ + extern_symbol = mcalloc(MAX_SYMREF); + extsym = extern_symbol; + MEMCPY_LIT(extsym, ZCSYM_PREFIX); + extsym += SIZEOF(ZCSYM_PREFIX) - 1; + memcpy(extsym, package.addr, package.len); + if ('%' == *extsym) + *extsym = '_'; + extsym += package.len; + *extsym++ = '.'; + memcpy(extsym, extentry.addr, extentry.len); + if ('%' == *extsym) + *extsym = '_'; + extsym += extentry.len; + extentry.addr = extern_symbol; + extentry.len = extsym - extern_symbol; + tabent = put_cdlt(&extentry); + } else + { +#endif +# ifdef VMS + ref = newtriple(OC_FGNLOOKUP); + ref->operand[0] = put_str(package.addr, package.len); + ref->operand[1] = put_str(extentry.addr, extentry.len); + tabent = put_tref(ref); +# endif +#ifdef VMS_CASE_SENSITIVE_MACROS + } +#endif + calltrip = maketriple(a ? OC_FNFGNCAL : OC_FGNCAL); + nxtopr = &calltrip->operand[1]; + ref = newtriple(OC_PARAMETER); + ref->operand[0] = UNIX_ONLY(put_str(package.addr, package.len)) VMS_ONLY(tabent); + *nxtopr = put_tref(ref); + nxtopr = &ref->operand[1]; + cnt++; +# ifdef UNIX + ref = newtriple(OC_PARAMETER); + ref->operand[0] = put_str(extentry.addr, extentry.len); + *nxtopr = put_tref(ref); + nxtopr = &ref->operand[1]; + cnt++; +# endif + if (TK_LPAREN != window_token) + { + ref = newtriple(OC_PARAMETER); + ref->operand[0] = put_ilit(0); + *nxtopr = put_tref(ref); + nxtopr = &ref->operand[1]; + cnt++; + ref = newtriple(OC_PARAMETER); + ref->operand[0] = put_ilit(0); + *nxtopr = put_tref(ref); + nxtopr = &ref->operand[1]; + cnt++; + } else + { + if (!(actcnt = actuallist(nxtopr))) + return FALSE; + cnt += actcnt; + } + cnt++; /* dst mval, or 0 */ + calltrip->operand[0] = put_ilit(cnt); + ins_triple(calltrip); + if (a) + *a = put_tref(calltrip); + return TRUE; +} diff --git a/sr_port/f_ascii.c b/sr_port/f_ascii.c new file mode 100644 index 0000000..d1ebe32 --- /dev/null +++ b/sr_port/f_ascii.c @@ -0,0 +1,37 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int f_ascii(oprtype *a, opctype op) +{ + triple *r; + + r = maketriple(op); + if (!strexpr(&(r->operand[0]))) + return FALSE; + if (window_token != TK_COMMA) + r->operand[1] = put_ilit(1); + else + { + advancewindow(); + if (!intexpr(&(r->operand[1]))) + return FALSE; + } + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_data.c b/sr_port/f_data.c new file mode 100644 index 0000000..0b893b2 --- /dev/null +++ b/sr_port/f_data.c @@ -0,0 +1,76 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "mdq.h" + +GBLREF char window_token; + +int f_data(oprtype *a, opctype op) +{ + triple *oldchain, tmpchain, *r, *triptr; + error_def(ERR_VAREXPECTED); + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + assert(OC_FNDATA == op || OC_FNZDATA == op); + r = maketriple(op); + switch (window_token) + { + case TK_IDENT: + if (!lvn(&(r->operand[0]), OC_SRCHINDX, 0)) + return FALSE; + ins_triple(r); + break; + case TK_CIRCUMFLEX: + if (!gvn()) + return FALSE; + r->opcode = OC_GVDATA; + ins_triple(r); + break; + case TK_ATSIGN: + if (TREF(shift_side_effects)) + { + dqinit(&tmpchain, exorder); + oldchain = setcurtchain(&tmpchain); + if (!indirection(&(r->operand[0]))) + { + setcurtchain(oldchain); + return FALSE; + } + r->operand[1] = put_ilit((mint)(OC_FNDATA == op ? indir_fndata : indir_fnzdata)); + ins_triple(r); + newtriple(OC_GVSAVTARG); + setcurtchain(oldchain); + dqadd(TREF(expr_start), &tmpchain, exorder); + TREF(expr_start) = tmpchain.exorder.bl; + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(TREF(expr_start)); + } else + { + if (!indirection(&(r->operand[0]))) + return FALSE; + r->operand[1] = put_ilit((mint)(OC_FNDATA == op ? indir_fndata : indir_fnzdata)); + ins_triple(r); + } + r->opcode = OC_INDFUN; + break; + default: + stx_error(ERR_VAREXPECTED); + return FALSE; + } + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_extract.c b/sr_port/f_extract.c new file mode 100644 index 0000000..9fa2c13 --- /dev/null +++ b/sr_port/f_extract.c @@ -0,0 +1,56 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; + +/* Note this function compiler routine is used for all of $EXTRACT, + $ZEXTRACT, and $ZSUBSTR since the format of the call is identical + in all instances as the function is similar. +*/ +int f_extract(oprtype *a, opctype op) +{ + triple *first, *last, *r; + + r = maketriple(op); + if (!strexpr(&(r->operand[0]))) + return FALSE; + first = newtriple(OC_PARAMETER); + last = newtriple(OC_PARAMETER); + r->operand[1] = put_tref(first); + first->operand[1] = put_tref(last); + if (window_token != TK_COMMA) + { + first->operand[0] = put_ilit(1); + last->operand[0] = put_ilit((OC_FNZSUBSTR == op) ? MAXPOSINT4 : 1); + } else + { + advancewindow(); + if (!intexpr(&(first->operand[0]))) + return FALSE; + if (window_token != TK_COMMA) + last->operand[0] = (OC_FNZSUBSTR == op) ? put_ilit(MAXPOSINT4) : first->operand[0]; + else + { + advancewindow(); + if (!intexpr(&(last->operand[0]))) + return FALSE; + } + } + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_find.c b/sr_port/f_find.c new file mode 100644 index 0000000..e82659c --- /dev/null +++ b/sr_port/f_find.c @@ -0,0 +1,51 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int f_find(oprtype *a, opctype op) +{ + triple *delimiter, *start, *r; + error_def(ERR_COMMA); + + r = maketriple(op); + if (!strexpr(&(r->operand[0]))) + return FALSE; + if (window_token != TK_COMMA) + { + stx_error(ERR_COMMA); + return FALSE; + } + advancewindow(); + delimiter = newtriple(OC_PARAMETER); + start = newtriple(OC_PARAMETER); + r->operand[1] = put_tref(delimiter); + delimiter->operand[1] = put_tref(start); + if (!strexpr(&(delimiter->operand[0]))) + return FALSE; + if (window_token != TK_COMMA) + start->operand[0] = put_ilit(1); + else + { + advancewindow(); + if (!intexpr(&(start->operand[0]))) + return FALSE; + } + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_fnumber.c b/sr_port/f_fnumber.c new file mode 100644 index 0000000..202081c --- /dev/null +++ b/sr_port/f_fnumber.c @@ -0,0 +1,59 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "mdq.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int f_fnumber( oprtype *a, opctype op) +{ + triple *ref, *next, *r; + oprtype z; + error_def(ERR_COMMA); + + r = maketriple(op); + if (!numexpr(&r->operand[0])) + return FALSE; + if (window_token != TK_COMMA) + { + stx_error(ERR_COMMA); + return FALSE; + } + advancewindow(); + if (!strexpr(&r->operand[1])) + return FALSE; + if (window_token != TK_COMMA) + { + ref = newtriple(OC_FORCENUM); + ref->operand[0] = r->operand[0]; + r->operand[0] = put_tref(ref); + } else + { + advancewindow(); + if (!intexpr(&z)) + return FALSE; + ref = newtriple(OC_FNJ3); + ref->operand[0] = r->operand[0]; + r->operand[0] = put_tref(ref); + next = newtriple(OC_PARAMETER); + ref->operand[1] = put_tref(next); + next->operand[0] = put_ilit((mint) 0); + next->operand[1] = z; + } + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_fnzbitfind.c b/sr_port/f_fnzbitfind.c new file mode 100644 index 0000000..d2e1add --- /dev/null +++ b/sr_port/f_fnzbitfind.c @@ -0,0 +1,50 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int f_fnzbitfind( oprtype *a, opctype op) +{ + triple *r, *parm; + error_def(ERR_COMMA); + + r = maketriple(op); + if (!expr(&(r->operand[0]))) /* bitstring */ + return FALSE; + if (window_token != TK_COMMA) + { + stx_error(ERR_COMMA); + return FALSE; + } + parm = newtriple(OC_PARAMETER); + r->operand[1] = put_tref(parm); + advancewindow(); + if (!intexpr(&(parm->operand[0]))) /* truthval */ + return FALSE; + if (window_token != TK_COMMA) + parm->operand[1] = put_ilit(1); + else + { + advancewindow(); + if (!intexpr(&(parm->operand[1]))) /* position */ + return FALSE; + } + + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_fnzbitget.c b/sr_port/f_fnzbitget.c new file mode 100644 index 0000000..7f93b72 --- /dev/null +++ b/sr_port/f_fnzbitget.c @@ -0,0 +1,39 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int f_fnzbitget( oprtype *a, opctype op) +{ + triple *r; + error_def(ERR_COMMA); + + r = maketriple(op); + if (!expr(&(r->operand[0]))) /* bitstring */ + return FALSE; + if (window_token != TK_COMMA) + { + stx_error(ERR_COMMA); + return FALSE; + } + advancewindow(); + if (!intexpr(&(r->operand[1]))) /* position */ + return FALSE; + + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_fnzbitset.c b/sr_port/f_fnzbitset.c new file mode 100644 index 0000000..729fd46 --- /dev/null +++ b/sr_port/f_fnzbitset.c @@ -0,0 +1,50 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int f_fnzbitset( oprtype *a, opctype op) +{ + triple *r, *parm; + error_def(ERR_COMMA); + + r = maketriple(op); + if (!expr(&(r->operand[0]))) /* bitstring */ + return FALSE; + if (window_token != TK_COMMA) + { + stx_error(ERR_COMMA); + return FALSE; + } + parm = newtriple(OC_PARAMETER); + r->operand[1] = put_tref(parm); + advancewindow(); + if (!intexpr(&(parm->operand[0]))) /* position */ + return FALSE; + if (window_token != TK_COMMA) + { + stx_error(ERR_COMMA); + return FALSE; + } + advancewindow(); + if (!intexpr(&(parm->operand[1]))) /* truthval */ + return FALSE; + + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_fnzbitstr.c b/sr_port/f_fnzbitstr.c new file mode 100644 index 0000000..0b1272e --- /dev/null +++ b/sr_port/f_fnzbitstr.c @@ -0,0 +1,38 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int f_fnzbitstr( oprtype *a, opctype op ) +{ + triple *r, *parm; + + r = maketriple(op); + if (!intexpr(&(r->operand[0]))) /* size */ + return FALSE; + if (window_token != TK_COMMA) + r->operand[1] = put_ilit(0); + else + { + advancewindow(); + if (!intexpr(&(r->operand[1]))) /* position */ + return FALSE; + } + + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_get.c b/sr_port/f_get.c new file mode 100644 index 0000000..9212c7c --- /dev/null +++ b/sr_port/f_get.c @@ -0,0 +1,117 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "indir_enum.h" +#include "mdq.h" +#include "mmemory.h" +#include "advancewindow.h" + +GBLREF char window_token; +GBLREF triple *curtchain; + +int f_get(oprtype *a, opctype op) +{ + triple tmpchain, *oldchain, *r, *triptr; + oprtype result, *result_ptr; + error_def(ERR_VAREXPECTED); + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + result_ptr = (oprtype *)mcalloc(SIZEOF(oprtype)); + result = put_indr(result_ptr); + r = maketriple(op); + switch (window_token) + { + case TK_IDENT: + if (!lvn(&r->operand[0], OC_SRCHINDX, 0)) + return FALSE; + if (window_token != TK_COMMA) + { + ins_triple(r); + *a = put_tref(r); + return TRUE; + } + r->opcode = OC_FNGET2; + r->operand[1] = result; + break; + case TK_CIRCUMFLEX: + if (!gvn()) + return FALSE; + if (window_token == TK_COMMA) + { /* 2-argument $GET with global-variable as first argument. In this case generate the following + * sequence of opcodes. OC_FNGVGET1, opcodes-to-evaluate-second-argument-expression, OC_FNGVGET2 + */ + r->opcode = OC_FNGVGET1; + ins_triple(r); + triptr = r; + /* Prepare triple for OC_FNGVGET2 */ + r = maketriple(op); + r->opcode = OC_FNGVGET2; + r->operand[0] = put_tref(triptr); + r->operand[1] = result; + } else + { + r->opcode = OC_FNGVGET; + r->operand[0] = result; + } + break; + case TK_ATSIGN: + r->opcode = OC_INDGET; + if (TREF(shift_side_effects)) + { + dqinit(&tmpchain, exorder); + oldchain = setcurtchain(&tmpchain); + if (!indirection(&r->operand[0])) + { + setcurtchain(oldchain); + return FALSE; + } + r->operand[1] = result; + if (window_token == TK_COMMA) + { + advancewindow(); + if (!expr(result_ptr)) + return FALSE; + } else + *result_ptr = put_str(0, 0); + ins_triple(r); + newtriple(OC_GVSAVTARG); + setcurtchain(oldchain); + dqadd(TREF(expr_start), &tmpchain, exorder); + TREF(expr_start) = tmpchain.exorder.bl; + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(TREF(expr_start)); + *a = put_tref(r); + return TRUE; + } + if (!indirection(&r->operand[0])) + return FALSE; + r->operand[1] = result; + break; + default: + stx_error(ERR_VAREXPECTED); + return FALSE; + } + if (window_token == TK_COMMA) + { + advancewindow(); + if (!expr(result_ptr)) + return FALSE; + } else + *result_ptr = put_str(0, 0); + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_incr.c b/sr_port/f_incr.c new file mode 100644 index 0000000..0ea23b4 --- /dev/null +++ b/sr_port/f_incr.c @@ -0,0 +1,121 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#include "mdef.h" + +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "mdq.h" +#include "advancewindow.h" +#include "fullbool.h" +#include "show_source_line.h" + +GBLREF triple *curtchain; +GBLREF char window_token; + +int f_incr(oprtype *a, opctype op) +{ + boolean_t ok, save_shift; + char source_line_buff[MAX_SRCLINE + SIZEOF(ARROW)]; + oprtype *increment; + triple incrchain, *oldchain, *r, *savptr, targchain, tmpexpr, *triptr; + error_def(ERR_VAREXPECTED); + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + r = maketriple(op); + /* may need to evaluate the increment (2nd arg) early and use result later: prepare to juggle triple chains */ + save_shift = TREF(shift_side_effects); + if (GTM_BOOL != TREF(gtm_fullbool)) + TREF(shift_side_effects) = FALSE; /* if no short circuit, only the outermost should juggle */ + dqinit(&targchain, exorder); /* a place for the operation and the target */ + dqinit(&incrchain, exorder); /* a place for the increment */ + dqinit(&tmpexpr, exorder); /* a place to juggle the shifted chain in case it's active */ + triptr = TREF(expr_start); + savptr = TREF(expr_start_orig); /* but make sure expr_start_orig == expr_start since this is a new chain */ + TREF(expr_start_orig) = TREF(expr_start) = &tmpexpr; + oldchain = setcurtchain(&targchain); /* save the result of the first argument 'cause it evaluates 2nd */ + switch (window_token) + { + case TK_IDENT: + /* $INCREMENT() performs an implicit $GET() on a first argument lvn so we use OC_PUTINDX because + * we know only at runtime whether to signal an UNDEF error (depending on whether we have + * VIEW "NOUNDEF" or "UNDEF" state; op_putindx creates the local variable unconditionally, even if + * we have "UNDEF" state, in which case any error in op_fnincr causes an op_kill of that local variable + */ + ok = (lvn(&(r->operand[0]), OC_PUTINDX, 0)); + break; + case TK_CIRCUMFLEX: + ok = gvn(); + r->opcode = OC_GVINCR; + r->operand[0] = put_ilit(0); /* dummy fill since emit_code does not like empty operand[0] */ + break; + case TK_ATSIGN: + ok = indirection(&r->operand[0]); + r->opcode = OC_INDINCR; + break; + default: + ok = FALSE; + break; + } + if (!ok) + { + setcurtchain(oldchain); + return FALSE; + } + assert(TREF(expr_start) == tmpexpr.exorder.bl); /* maks sure nothing else did something fancy */ + TREF(expr_start) = triptr; /* restore original shift chain */ + TREF(expr_start_orig) = savptr; + increment = &r->operand[1]; + setcurtchain(&incrchain); /* now to the increment expr, which must evaluate before the glvn in $INCR(glvn,expr) */ + if (window_token != TK_COMMA) + *increment = put_ilit(1); /* default optional increment to 1 */ + else + { + advancewindow(); + if (!strexpr(increment)) + { + setcurtchain(oldchain); + return FALSE; + } + } + triptr = incrchain.exorder.bl; /* prepare to park the target after the increment */ + dqadd(triptr, &targchain, exorder); /* this is a violation of info hiding */ + coerce(increment, OCT_MVAL); + ins_triple(r); + if (&tmpexpr != tmpexpr.exorder.bl) + { /* one or more OC_GVNAME may have shifted so add to the end of the shift chain */ + assert(TREF(shift_side_effects)); + dqadd(TREF(expr_start), &tmpexpr, exorder); /* this is a violation of info hiding */ + TREF(expr_start) = tmpexpr.exorder.bl; + triptr = newtriple(OC_GVRECTARG); /* restore the result of the last gvn to preserve $referece (the naked) */ + triptr->operand[0] = put_tref(TREF(expr_start)); + } + TREF(shift_side_effects) = save_shift; + if (!save_shift || ((GTM_BOOL == TREF(gtm_fullbool)) && (OC_INDINCR != r->opcode))) + { /* put it on the end of the main chain as there's no reason to play more with the ordering */ + setcurtchain(oldchain); + triptr = curtchain->exorder.bl; + dqadd(triptr, &incrchain, exorder); /* this is a violation of info hiding */ + } else /* need full side effects or indirect 1st argument so put everything on the shift chain */ + { /* add the chain after "expr_start" which may be much before "curtchain" */ + newtriple(OC_GVSAVTARG); + setcurtchain(oldchain); + assert(NULL != TREF(expr_start)); + dqadd(TREF(expr_start), &incrchain, exorder); /* this is a violation of info hiding */ + TREF(expr_start) = incrchain.exorder.bl; + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(TREF(expr_start)); + } + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_justify.c b/sr_port/f_justify.c new file mode 100644 index 0000000..c893dba --- /dev/null +++ b/sr_port/f_justify.c @@ -0,0 +1,49 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int f_justify( oprtype *a, opctype op) +{ + triple *ref, *r; + error_def(ERR_COMMA); + + r = maketriple(op); + if (!strexpr(&r->operand[0])) + return FALSE; + if (window_token != TK_COMMA) + { + stx_error(ERR_COMMA); + return FALSE; + } + advancewindow(); + if (!intexpr(&r->operand[1])) + return FALSE; + if (window_token == TK_COMMA) + { + r->opcode = OC_FNJ3; + ref = newtriple(OC_PARAMETER); + ref->operand[0] = r->operand[1]; + r->operand[1] = put_tref(ref); + advancewindow(); + if (!intexpr(&ref->operand[1])) + return FALSE; + } + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_length.c b/sr_port/f_length.c new file mode 100644 index 0000000..dd2af73 --- /dev/null +++ b/sr_port/f_length.c @@ -0,0 +1,41 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int f_length( oprtype *a, opctype op) +{ + triple *r; + + assert((OC_FNLENGTH == op) || (OC_FNZLENGTH == op)); + r = maketriple(op); + if (!strexpr(&(r->operand[0]))) + return FALSE; + if (window_token == TK_COMMA) + { + advancewindow(); + if (OC_FNLENGTH == op) + r->opcode = OC_FNPOPULATION; /* This isn't very go information hiding */ + else + r->opcode = OC_FNZPOPULATION; /* This isn't very go information hiding */ + if (!strexpr(&(r->operand[1]))) + return FALSE; + } + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_mint.c b/sr_port/f_mint.c new file mode 100644 index 0000000..4a96639 --- /dev/null +++ b/sr_port/f_mint.c @@ -0,0 +1,26 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" + +int f_mint( oprtype *a, opctype op) +{ + triple *r; + + r = maketriple(op); + if (!intexpr(&r->operand[0])) + { return FALSE; + } + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_mint_mstr.c b/sr_port/f_mint_mstr.c new file mode 100644 index 0000000..73adefc --- /dev/null +++ b/sr_port/f_mint_mstr.c @@ -0,0 +1,40 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int f_mint_mstr( oprtype *a, opctype op) +{ + triple *r; + error_def(ERR_COMMA); + + r = maketriple(op); + if (!intexpr(&(r->operand[0]))) + return FALSE; + if (window_token != TK_COMMA) + { + stx_error(ERR_COMMA); + return FALSE; + } + advancewindow(); + if (!strexpr(&r->operand[1])) + { return FALSE; + } + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_mstr.c b/sr_port/f_mstr.c new file mode 100644 index 0000000..874c5cd --- /dev/null +++ b/sr_port/f_mstr.c @@ -0,0 +1,26 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" + +int f_mstr( oprtype *a, opctype op) +{ + triple *r; + + r = maketriple(op); + if (!strexpr(&r->operand[0])) + { return FALSE; + } + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_name.c b/sr_port/f_name.c new file mode 100644 index 0000000..790611e --- /dev/null +++ b/sr_port/f_name.c @@ -0,0 +1,69 @@ +/**************************************************************** + * * + * Copyright 2001, 2004 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "indir_enum.h" +#include "advancewindow.h" +#include "subscript.h" + +GBLREF char window_token; + +int f_name(oprtype *a, opctype op) +{ + triple *r; + oprtype *depth; + bool gbl; + error_def(ERR_VAREXPECTED); + + r = maketriple(op); + gbl = FALSE; + switch (window_token) + { + case TK_CIRCUMFLEX: + gbl = TRUE; + advancewindow(); + /* caution fall through */ + case TK_IDENT: + if (!name_glvn(gbl, &r->operand[1])) + return FALSE; + depth = &r->operand[0]; + break; + case TK_ATSIGN: + r->opcode = OC_INDFNNAME; + if (!indirection(&(r->operand[0]))) + return FALSE; + depth = &r->operand[1]; + break; + default: + stx_error(ERR_VAREXPECTED); + return FALSE; + } + /* allow for optional default value */ + if (window_token != TK_COMMA) + { + *depth = put_ilit(MAX_LVSUBSCRIPTS + 1); /* default to maximum number of subscripts allowed by law */ + /* ideally this should be MAX(MAX_LVSUBSCRIPTS, MAX_GVSUBSCRIPTS) but they are the same so take the easy path */ + assert(MAX_LVSUBSCRIPTS == MAX_GVSUBSCRIPTS); /* add assert to ensure our assumption is valid */ + } else + { + advancewindow(); + if (!strexpr(depth)) + return FALSE; + } + coerce(depth, OCT_MVAL); + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_next.c b/sr_port/f_next.c new file mode 100644 index 0000000..640c252 --- /dev/null +++ b/sr_port/f_next.c @@ -0,0 +1,91 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "mdq.h" + +GBLREF char window_token, director_token; +GBLREF triple *curtchain; + +int f_next( oprtype *a, opctype op) +{ + triple *oldchain, tmpchain, *ref, *r, *triptr; + error_def(ERR_VAREXPECTED); + error_def(ERR_LVORDERARG); + error_def(ERR_GVNEXTARG); + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + r = maketriple(op); + switch (window_token) + { + case TK_IDENT: + if (director_token != TK_LPAREN) + { + stx_error(ERR_LVORDERARG); + return FALSE; + } + if (!lvn(&(r->operand[0]),OC_SRCHINDX,r)) + return FALSE; + ins_triple(r); + break; + case TK_CIRCUMFLEX: + ref = TREF(shift_side_effects) ? TREF(expr_start) : curtchain->exorder.bl; + if (!gvn()) + return FALSE; + /* the following assumes OC_LIT and OC_GVNAME are all one + * gets for an unsubscripted global variable reference */ + if ((TREF(shift_side_effects) ? TREF(expr_start) : curtchain)->exorder.bl->exorder.bl->exorder.bl == ref) + { + stx_error(ERR_GVNEXTARG); + return FALSE; + } + r->opcode = OC_GVNEXT; + ins_triple(r); + break; + case TK_ATSIGN: + if (TREF(shift_side_effects)) + { + dqinit(&tmpchain, exorder); + oldchain = setcurtchain(&tmpchain); + if (!indirection(&(r->operand[0]))) + { + setcurtchain(oldchain); + return FALSE; + } + r->operand[1] = put_ilit((mint)indir_fnnext); + ins_triple(r); + newtriple(OC_GVSAVTARG); + setcurtchain(oldchain); + dqadd(TREF(expr_start), &tmpchain, exorder); + TREF(expr_start) = tmpchain.exorder.bl; + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(TREF(expr_start)); + } else + { + if (!indirection(&(r->operand[0]))) + return FALSE; + r->operand[1] = put_ilit((mint)indir_fnnext); + ins_triple(r); + } + r->opcode = OC_INDFUN; + break; + default: + stx_error(ERR_VAREXPECTED); + return FALSE; + } + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_one_mval.c b/sr_port/f_one_mval.c new file mode 100644 index 0000000..5198ba1 --- /dev/null +++ b/sr_port/f_one_mval.c @@ -0,0 +1,29 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "toktyp.h" + +GBLREF char window_token; + +int f_one_mval( oprtype *a, opctype op) +{ + triple *r; + + r = maketriple(op); + if (!expr(&(r->operand[0]))) + return FALSE; + + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_order.c b/sr_port/f_order.c new file mode 100644 index 0000000..67b2078 --- /dev/null +++ b/sr_port/f_order.c @@ -0,0 +1,302 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "fnorder.h" +#include "mdq.h" +#include "mmemory.h" +#include "advancewindow.h" +#include "mvalconv.h" + +GBLREF char window_token, director_token; +GBLREF mident window_ident; +GBLREF triple *curtchain; + +/* The following are static triples used to pass information between functions "f_order" and "set_opcode" */ +STATICDEF triple *gvo2_savtarg1; /* Save gv_currkey after processing gvn1 in $ORDER(gvn1,expr) */ +STATICDEF triple *gvo2_savtarg2; /* Save gv_currkey after processing gvn1 and expr in $ORDER(gvn1,expr) but before executing + * the runtime function for $ORDER (OC_GVO2) */ +STATICDEF triple *gvo2_pre_srchindx_triple; /* the end of the triple chain before OC_SRCHINDX got inserted */ + +error_def(ERR_VAREXPECTED); +error_def(ERR_ORDER2); + +LITDEF opctype order_opc[last_obj][last_dir] = +{ + /* forward backward undecided */ + { OC_GVORDER, OC_ZPREVIOUS, OC_GVO2 }, /* global */ + { OC_FNLVNAME, OC_FNLVPRVNAME, OC_FNLVNAMEO2 }, /* local_name */ + { OC_FNORDER, OC_FNZPREVIOUS, OC_FNO2 }, /* local_sub */ + { OC_INDFUN, OC_INDFUN, OC_INDO2 } /* indir */ +}; + +STATICFNDEF boolean_t set_opcode(triple *r, oprtype *result, oprtype *result_ptr, oprtype *second_opr, enum order_obj object) +{ + enum order_dir direction; + triple *s; + triple tmpchain, *oldchain, *x, *tp, *tmptriple, *gvo2_post_srchindx_triple, *t1, *t2; + int4 dummy_intval; + + if (window_token == TK_COMMA) + { + advancewindow(); + if (local_sub == object) + gvo2_post_srchindx_triple = curtchain->exorder.bl; + if (global == object) + { /* Prepare for OC_GVSAVTARG/OC_GVRECTARG processing in case second argument has global references. + * If the first argument to $ORDER is a global variable and the second argument is a literal, + * then the opcodes generated are (in that order) + * + * OC_GVNAME, EXPR, OC_GVO2 + * + * But if the first argument is a global variable and the second argument is an expression that is + * not a literal, then the opcodes generated are (in that order) + * + * OC_GVNAME, OC_SAVTARG1, EXPR, OC_SAVTARG2, OC_RECTARG1, OC_GVO2, OC_RECTARG2 + * + * Note that OC_SAVTARG1 and OC_SAVTARG2 are the same opcode OC_SAVTARG but are placeholder indicators. + * Similarly OC_RECTARG1 and OC_RECTARG2. + * + * This opcode order ensures that OC_GVO2 is presented the right gv_currkey on entry into function + * "op_gvo2" as well as ensure that after the $ORDER returns, the naked indicator is set correctly. + */ + dqinit(&tmpchain, exorder); + oldchain = setcurtchain(&tmpchain); + } + if (!expr(result_ptr)) + { + if (global == object) + setcurtchain(oldchain); + return FALSE; + } + assert(TRIP_REF == result_ptr->oprclass); + s = result_ptr->oprval.tref; + if (OC_LIT == s->opcode) + { + if (MV_IS_TRUEINT(&s->operand[0].oprval.mlit->v, &dummy_intval) + && ((MV_BIAS == s->operand[0].oprval.mlit->v.m[1]) + || (-MV_BIAS == s->operand[0].oprval.mlit->v.m[1]))) + direction = (MV_BIAS == s->operand[0].oprval.mlit->v.m[1]) ? forward : backward; + else + { + if (global == object) + setcurtchain(oldchain); + stx_error(ERR_ORDER2); + return FALSE; + } + if (global == object) + { /* No need for OC_GVSAVTARG/OC_GVRECTARG processing as expr is a constant (no global references) */ + setcurtchain(oldchain); + tmptriple = curtchain->exorder.bl; + dqadd(tmptriple, &tmpchain, exorder); + } + } else + { + direction = undecided; + if (global == object) + { /* Need to do OC_GVSAVTARG/OC_GVRECTARG processing as expr could contain global references */ + assert(OC_GVO2 == order_opc[object][direction]); + setcurtchain(oldchain); + /* Note down the value of gv_currkey at this point */ + newtriple(OC_GVSAVTARG); + /* Add second argument triples */ + gvo2_savtarg1 = curtchain->exorder.bl; + tmptriple = curtchain->exorder.bl; + dqadd(tmptriple, &tmpchain, exorder); + /* Note down the value of gv_currkey at this point */ + newtriple(OC_GVSAVTARG); + gvo2_savtarg2 = curtchain->exorder.bl; + } + } + } else + direction = forward; + + switch (object) + { + case global: + if (direction == undecided) + *second_opr = *result; + break; + case local_name: + if (direction == undecided) + *second_opr = *result; + else if (direction == forward) + { /* The op_fnlvname rtn needs an extra parm - insert it now */ + assert(OC_FNLVNAME == order_opc[object][direction]); + *second_opr = put_ilit(0); /* Flag not to return aliases with no value */ + } + break; + + case local_sub: + if (direction == undecided) + { /* This is $ORDER(subscripted-local-variable, expr). The normal order of evaluation would be + * + * 1) Evaluate subscripts of local variable + * 2) Do OC_SRCHINDX + * 3) Evaluate expr + * 4) Do OC_FNORDER + * + * But it is possible that the subscripted local-variable is defined only by an extrinsic function + * that is part of "expr". In that case, we should NOT do the OC_SRCHINDX before "expr" gets + * evaluated (as otherwise OC_SRCHINDX will not return the right lv_val structure). That is, the + * order of evaluation should be + * + * 1) Evaluate subscripts of local variable + * 2) Evaluate expr + * 3) Do OC_SRCHINDX + * 4) Do OC_FNORDER + * + * The triples need to be reordered accordingly to implement the above evaluation order. + * This reordering of triples is implemented below by recording the end of the triple chain + * just BEFORE (variable "gvo2_pre_srchindx_triple") and just AFTER (variable + * "gvo2_post_srchindx_triple") parsing the subscripted-local-variable first argument to + * $ORDER. This is done partly in the function "f_order" and partly in "set_opcode". Once these + * are recorded, the second argument "expr" is parsed and the triples generated. After this, we + * start from gvo2_post_srchindx_triple and go back the triple chain until we find the OC_SRCHINDX + * opcode or gvo2_pre_srchindx_triple whichever is earlier (e.g. for unsubscripted names + * OC_SRCHINDX triple is not generated). This portion of the triple chain (that does the + * OC_SRCHINDX computation) is deleted and added at the end of the current triple chain. This + * accomplishes the desired evaluation reordering. Note that the value of the naked indicator is + * not affected by this reordering (since OC_SRCHINDX does not do global references). + */ + for (tmptriple = gvo2_post_srchindx_triple; (OC_SRCHINDX != tmptriple->opcode); + tmptriple = tmptriple->exorder.bl) + { + if (tmptriple == gvo2_pre_srchindx_triple) + break; + } + if (OC_SRCHINDX == tmptriple->opcode) + { + t1 = tmptriple->exorder.bl; + t2 = gvo2_post_srchindx_triple->exorder.fl; + dqdelchain(t1,t2,exorder); + dqinit(&tmpchain, exorder); + tmpchain.exorder.fl = tmptriple; + tmpchain.exorder.bl = gvo2_post_srchindx_triple; + gvo2_post_srchindx_triple->exorder.fl = &tmpchain; + tmptriple->exorder.bl = &tmpchain; + tmptriple = curtchain->exorder.bl; + dqadd(tmptriple, &tmpchain, exorder); + } + s = newtriple(OC_PARAMETER); + s->operand[0] = *second_opr; + s->operand[1] = *result; + *second_opr = put_tref(s); + } + break; + + case indir: + if (direction == forward) + *second_opr = put_ilit((mint)indir_fnorder1); + else + if (direction == backward) + *second_opr = put_ilit((mint)indir_fnzprevious); + else + *second_opr = *result; + break; + + default: + assert(FALSE); + } + r->opcode = order_opc[object][direction]; + return TRUE; +} + +int f_order(oprtype *a, opctype op) +{ + enum order_obj object; + oprtype result, *result_ptr, *second_opr; + triple tmpchain, *oldchain, *r, *triptr; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + result_ptr = (oprtype *)mcalloc(SIZEOF(oprtype)); + result = put_indr(result_ptr); + r = maketriple(OC_NOOP); /* We'll fill in the opcode later, when we figure out what it is */ + switch (window_token) + { + case TK_IDENT: + if (director_token == TK_LPAREN) + { /* See comment in "set_opcode" for why we maintain "gvo2_pre_srchindx_triple" here */ + gvo2_pre_srchindx_triple = curtchain->exorder.bl; + if (!lvn(&r->operand[0], OC_SRCHINDX, r)) + return FALSE; + object = local_sub; + } else + { + r->operand[0] = put_str(window_ident.addr, window_ident.len); + advancewindow(); + object = local_name; + } + second_opr = &r->operand[1]; + break; + case TK_CIRCUMFLEX: + if (!gvn()) + return FALSE; + object = global; + second_opr = &r->operand[0]; + break; + case TK_ATSIGN: + if (TREF(shift_side_effects)) + { + dqinit(&tmpchain, exorder); + oldchain = setcurtchain(&tmpchain); + if (!indirection(&r->operand[0])) + { + setcurtchain(oldchain); + return FALSE; + } + + if (!set_opcode(r, &result, result_ptr, &r->operand[1], indir)) + return FALSE; + ins_triple(r); + + newtriple(OC_GVSAVTARG); + setcurtchain(oldchain); + dqadd(TREF(expr_start), &tmpchain, exorder); + TREF(expr_start) = tmpchain.exorder.bl; + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(TREF(expr_start)); + *a = put_tref(r); + return TRUE; + } + if (!indirection(&r->operand[0])) + return FALSE; + object = indir; + second_opr = &r->operand[1]; + break; + default: + stx_error(ERR_VAREXPECTED); + return FALSE; + } + if (set_opcode(r, &result, result_ptr, second_opr, object)) + { /* Restore gv_currkey of the first argument (in case the second expression contained a global reference). + * This will ensure op_gvo2 has gv_currkey set properly on entry */ + if (OC_GVO2 == r->opcode) + { + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(gvo2_savtarg1); + ins_triple(r); + /* Restore gv_currkey to what it was after evaluating the second argument (to preserved naked indicator) */ + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(gvo2_savtarg2); + } else + ins_triple(r); + *a = put_tref(r); + return TRUE; + } + return FALSE; +} diff --git a/sr_port/f_order1.c b/sr_port/f_order1.c new file mode 100644 index 0000000..e5ac577 --- /dev/null +++ b/sr_port/f_order1.c @@ -0,0 +1,86 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "mdq.h" +#include "advancewindow.h" + +GBLREF char window_token, director_token; +GBLREF mident window_ident; + +int f_order1( oprtype *a, opctype op) +{ + triple *oldchain, tmpchain, *r, *triptr; + error_def(ERR_VAREXPECTED); + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + r = maketriple(op); + switch (window_token) + { + case TK_IDENT: + if (director_token != TK_LPAREN) + { + r->opcode = OC_FNLVNAME; + r->operand[0] = put_str(window_ident.addr, window_ident.len); + r->operand[1] = put_ilit(0); /* FALSE - do not return aliased vars with no value */ + ins_triple(r); + advancewindow(); + break; + } + if (!lvn(&(r->operand[0]), OC_SRCHINDX, r)) + return FALSE; + ins_triple(r); + break; + case TK_CIRCUMFLEX: + if (!gvn()) + return FALSE; + r->opcode = OC_GVORDER; + ins_triple(r); + break; + case TK_ATSIGN: + if (TREF(shift_side_effects)) + { + dqinit(&tmpchain, exorder); + oldchain = setcurtchain(&tmpchain); + if (!indirection(&(r->operand[0]))) + { + setcurtchain(oldchain); + return FALSE; + } + r->operand[1] = put_ilit((mint)indir_fnorder1); + ins_triple(r); + newtriple(OC_GVSAVTARG); + setcurtchain(oldchain); + dqadd(TREF(expr_start), &tmpchain, exorder); + TREF(expr_start) = tmpchain.exorder.bl; + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(TREF(expr_start)); + } else + { + if (!indirection(&(r->operand[0]))) + return FALSE; + r->operand[1] = put_ilit((mint)indir_fnorder1); + ins_triple(r); + } + r->opcode = OC_INDFUN; + break; + default: + stx_error(ERR_VAREXPECTED); + return FALSE; + } + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_qlength.c b/sr_port/f_qlength.c new file mode 100644 index 0000000..44414eb --- /dev/null +++ b/sr_port/f_qlength.c @@ -0,0 +1,29 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" + +GBLREF char window_token; + +int f_qlength(oprtype *a, opctype op) +{ + triple *r; + + r = maketriple(op); + if (!strexpr(&(r->operand[0]))) + return FALSE; + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_qsubscript.c b/sr_port/f_qsubscript.c new file mode 100644 index 0000000..43c0b6d --- /dev/null +++ b/sr_port/f_qsubscript.c @@ -0,0 +1,35 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int f_qsubscript(oprtype *a, opctype op) +{ + triple *r; + + r = maketriple(op); + if (!strexpr(&(r->operand[0]))) + return FALSE; + if (window_token != TK_COMMA) + return FALSE; + advancewindow(); + if (!intexpr(&(r->operand[1]))) + return FALSE; + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_query.c b/sr_port/f_query.c new file mode 100644 index 0000000..ed4d974 --- /dev/null +++ b/sr_port/f_query.c @@ -0,0 +1,109 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "mdq.h" + +GBLREF char window_token; + +int f_query ( oprtype *a, opctype op) +{ + triple *oldchain, tmpchain, *r, *r0, *r1, *triptr; + error_def (ERR_VAREXPECTED); + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + if (window_token == TK_IDENT) + { + if (!lvn (a, OC_FNQUERY, 0)) + return FALSE; + assert (a->oprclass == TRIP_REF); + if (a->oprval.tref->opcode == OC_FNQUERY) + { + assert (a->oprval.tref->opcode == OC_FNQUERY); + assert (a->oprval.tref->operand[0].oprclass == TRIP_REF); + assert (a->oprval.tref->operand[0].oprval.tref->opcode == OC_ILIT); + assert (a->oprval.tref->operand[0].oprval.tref->operand[0].oprclass == ILIT_REF); + assert (a->oprval.tref->operand[0].oprval.tref->operand[0].oprval.ilit > 0); + a->oprval.tref->operand[0].oprval.tref->operand[0].oprval.ilit += 2; + assert (a->oprval.tref->operand[1].oprclass == TRIP_REF); + assert (a->oprval.tref->operand[1].oprval.tref->opcode == OC_PARAMETER); + assert (a->oprval.tref->operand[1].oprval.tref->operand[0].oprclass == TRIP_REF); + r0 = a->oprval.tref->operand[1].oprval.tref->operand[0].oprval.tref; + assert (r0->opcode == OC_VAR); + assert (r0->operand[0].oprclass == MVAR_REF); + r1 = maketriple (OC_PARAMETER); + r1->operand[0] = put_str(r0->operand[0].oprval.vref->mvname.addr, r0->operand[0].oprval.vref->mvname.len); + r1->operand[1] = a->oprval.tref->operand[1]; + a->oprval.tref->operand[1] = put_tref (r1); + dqins (a->oprval.tref->exorder.fl, exorder, r1); + } else + { + assert (a->oprval.tref->opcode == OC_VAR); + r0 = newtriple (OC_FNQUERY); + r0->operand[0] = put_ilit (3); + r0->operand[1] = put_tref (newtriple (OC_PARAMETER)); + r0->operand[1].oprval.tref->operand[0] = put_str(a->oprval.tref->operand[0].oprval.vref->mvname.addr, + a->oprval.tref->operand[0].oprval.vref->mvname.len); + r1 = r0->operand[1].oprval.tref; + r1->operand[1] = *a; + *a = put_tref (r0); + } + } else + { + r = maketriple(op); + switch (window_token) + { + case TK_CIRCUMFLEX: + if (!gvn()) + return FALSE; + r->opcode = OC_GVQUERY; + ins_triple(r); + break; + case TK_ATSIGN: + if (TREF(shift_side_effects)) + { + dqinit(&tmpchain, exorder); + oldchain = setcurtchain(&tmpchain); + if (!indirection(&(r->operand[0]))) + { + setcurtchain(oldchain); + return FALSE; + } + r->operand[1] = put_ilit((mint)indir_fnquery); + ins_triple(r); + newtriple(OC_GVSAVTARG); + setcurtchain(oldchain); + dqadd(TREF(expr_start), &tmpchain, exorder); + TREF(expr_start) = tmpchain.exorder.bl; + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(TREF(expr_start)); + } else + { + if (!indirection(&(r->operand[0]))) + return FALSE; + r->operand[1] = put_ilit((mint)indir_fnquery); + ins_triple(r); + } + r->opcode = OC_INDFUN; + break; + default: + stx_error(ERR_VAREXPECTED); + return FALSE; + } + *a = put_tref(r); + } + return TRUE; +} diff --git a/sr_port/f_reverse.c b/sr_port/f_reverse.c new file mode 100644 index 0000000..111bc83 --- /dev/null +++ b/sr_port/f_reverse.c @@ -0,0 +1,29 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +/* #include "opcode.h" */ +#include "toktyp.h" + +GBLREF char window_token; + +int f_reverse(oprtype *a, opctype op) +{ + triple *r; + + r = maketriple(op); + if (!strexpr(&(r->operand[0]))) + return FALSE; + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_select.c b/sr_port/f_select.c new file mode 100644 index 0000000..add15b8 --- /dev/null +++ b/sr_port/f_select.c @@ -0,0 +1,126 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "mdq.h" +#include "mmemory.h" +#include "advancewindow.h" + +GBLREF char window_token; +LITREF octabstruct oc_tab[]; + +int f_select( oprtype *a, opctype op ) +{ + triple tmpchain, *oldchain, *ref, *r, *save_start, *save_start_orig, *triptr; + oprtype *cnd, tmparg, endtrip, target; + opctype old_op; + unsigned int save_depth; + boolean_t first_time, save_shift; + error_def(ERR_COLON); + error_def(ERR_SELECTFALSE); + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + save_shift = TREF(shift_side_effects); + save_depth = TREF(expr_depth); + save_start = TREF(expr_start); + save_start_orig = TREF(expr_start_orig); + TREF(shift_side_effects) = FALSE; + TREF(expr_depth) = 0; + TREF(expr_start) = TREF(expr_start_orig) = NULL; + if (save_shift) + { + dqinit(&tmpchain, exorder); + oldchain = setcurtchain(&tmpchain); + } + r = maketriple(op); + first_time = TRUE; + endtrip = put_tjmp(r); + for (;;) + { + cnd = (oprtype *)mcalloc(SIZEOF(oprtype)); + if (!bool_expr((bool) FALSE, cnd)) + { + if (save_shift) + setcurtchain(oldchain); + return FALSE; + } + if (TK_COLON != window_token) + { + if (save_shift) + setcurtchain(oldchain); + stx_error(ERR_COLON); + return FALSE; + } + advancewindow(); + if (!expr(&tmparg)) + { + if (save_shift) + setcurtchain(oldchain); + return FALSE; + } + assert(TRIP_REF == tmparg.oprclass); + old_op = tmparg.oprval.tref->opcode; + if (first_time) + { + if ((OC_LIT == old_op) || (oc_tab[old_op].octype & OCT_MVADDR)) + { + ref = newtriple(OC_STOTEMP); + ref->operand[0] = tmparg; + tmparg = put_tref(ref); + } + r->operand[0] = target = tmparg; + first_time = FALSE; + } else + { + ref = newtriple(OC_STO); + ref->operand[0] = target; + ref->operand[1] = tmparg; + if (OC_PASSTHRU == tmparg.oprval.tref->opcode) + { + assert(TRIP_REF == tmparg.oprval.tref->operand[0].oprclass); + ref = newtriple(OC_STO); + ref->operand[0] = target; + ref->operand[1] = put_tref(tmparg.oprval.tref->operand[0].oprval.tref); + } + } + ref = newtriple(OC_JMP); + ref->operand[0] = endtrip; + tnxtarg(cnd); + if (TK_COMMA != window_token) + break; + advancewindow(); + } + tmparg = put_ilit(ERR_SELECTFALSE); + ref = newtriple(OC_RTERROR); + ref->operand[0] = tmparg; + ref->operand[1] = put_ilit(FALSE); /* Not a subroutine reference */ + ins_triple(r); + assert(!TREF(expr_depth)); + TREF(shift_side_effects) = save_shift; + TREF(expr_depth) = save_depth; + TREF(expr_start) = save_start; + TREF(expr_start_orig) = save_start_orig; + if (save_shift) + { + newtriple(OC_GVSAVTARG); + setcurtchain(oldchain); + dqadd(TREF(expr_start), &tmpchain, exorder); + TREF(expr_start) = tmpchain.exorder.bl; + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(TREF(expr_start)); + } + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_stack.c b/sr_port/f_stack.c new file mode 100644 index 0000000..c31f0d2 --- /dev/null +++ b/sr_port/f_stack.c @@ -0,0 +1,37 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int f_stack(oprtype *a, opctype op) +{ + triple *r; + + r = maketriple(op); + if (!intexpr(&(r->operand[0]))) + return FALSE; + if (window_token == TK_COMMA) + { + advancewindow(); + r->opcode = OC_FNSTACK2; /*This isn't very good information hiding*/ + if (!strexpr(&(r->operand[1]))) + return FALSE; + } + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_text.c b/sr_port/f_text.c new file mode 100644 index 0000000..c78c4ee --- /dev/null +++ b/sr_port/f_text.c @@ -0,0 +1,124 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "cmd_qlf.h" +#include "advancewindow.h" +#include "gtm_caseconv.h" + +static readonly mstr zero_mstr; + +GBLREF char window_token, director_token; +GBLREF mident window_ident; +GBLREF boolean_t run_time; +GBLREF mident routine_name; +GBLREF command_qualifier cmd_qlf; + +int f_text(oprtype *a, opctype op) +{ + int implicit_offset = 0; + triple *r, *label; + + error_def(ERR_TEXTARG); + error_def(ERR_RTNNAME); + + r = maketriple(op); + switch (window_token) + { + case TK_CIRCUMFLEX: + implicit_offset = 1; + /* CAUTION - fall-through */ + case TK_PLUS: + r->operand[0] = put_str(zero_mstr.addr, 0); /* Null label - top of routine */ + break; + case TK_INTLIT: + int_label(); + /* CAUTION - fall through */ + case TK_IDENT: + if (!(cmd_qlf.qlf & CQ_LOWER_LABELS)) + lower_to_upper((uchar_ptr_t)window_ident.addr, (uchar_ptr_t)window_ident.addr, window_ident.len); + r->operand[0] = put_str(window_ident.addr, window_ident.len); + advancewindow(); + break; + case TK_ATSIGN: + if (!indirection(&(r->operand[0]))) + return FALSE; + r->opcode = OC_INDTEXT; + break; + default: + stx_error(ERR_TEXTARG); + return FALSE; + } + assert(TK_PLUS == window_token || TK_CIRCUMFLEX == window_token || TK_RPAREN == window_token || TK_EOL == window_token); + if (OC_INDTEXT != r->opcode || TK_PLUS == window_token || TK_CIRCUMFLEX == window_token) + { /* Need another parm chained in to deal with offset and routine name except for the case where an + * indirect specifies the entire argument. + */ + label = newtriple(OC_PARAMETER); + r->operand[1] = put_tref(label); + } + if (TK_PLUS != window_token) + { + if (OC_INDTEXT != r->opcode || TK_CIRCUMFLEX == window_token) + /* Set default offset (0 or 1 as computed above) when offset not specified */ + label->operand[0] = put_ilit(implicit_offset); + else + { /* Fill in indirect text for case where indirect specifies entire operand */ + r->opcode = OC_INDFUN; + r->operand[1] = put_ilit((mint)indir_fntext); + } + } else + { /* Process offset */ + advancewindow(); + if (!intexpr(&(label->operand[0]))) + return FALSE; + } + if (TK_CIRCUMFLEX != window_token) + { /* No routine specified - default to current routine */ + if (OC_INDFUN != r->opcode) + { + if (!run_time) + label->operand[1] = put_str(routine_name.addr, routine_name.len); + else + label->operand[1] = put_tref(newtriple(OC_CURRTN)); + } + } else + { /* Routine has been specified - pull it */ + advancewindow(); + switch(window_token) + { + case TK_IDENT: +# ifdef GTM_TRIGGER + if (TK_HASH == director_token) + /* Coagulate tokens as necessary (and available) to allow '#' in the routine name */ + advwindw_hash_in_mname_allowed(); +# endif + label->operand[1] = put_str(window_ident.addr, window_ident.len); + advancewindow(); + break; + case TK_ATSIGN: + if (!indirection(&label->operand[1])) + return FALSE; + r->opcode = OC_INDTEXT; + break; + default: + stx_error(ERR_RTNNAME); + return FALSE; + } + } + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_translate.c b/sr_port/f_translate.c new file mode 100644 index 0000000..df93a84 --- /dev/null +++ b/sr_port/f_translate.c @@ -0,0 +1,51 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; +LITREF mval literal_null ; + +int f_translate( oprtype *a, opctype op) +{ + triple *args[3]; + int i; + bool more_args; + + args[0] = maketriple(op); + if (!expr(&(args[0]->operand[0]))) + return FALSE; + for (i = 1 , more_args = TRUE ; i < 3 ; i++) + { + args[i] = newtriple(OC_PARAMETER); + if (more_args) + { + if (window_token != TK_COMMA) + more_args = FALSE; + else + { + advancewindow(); + if (!expr(&(args[i]->operand[0]))) + return FALSE; + } + } + if (!more_args) + args[i]->operand[0] = put_lit((mval *)&literal_null); + args[i - 1]->operand[1] = put_tref(args[i]); + } + ins_triple(args[0]); + *a = put_tref(args[0]); + return TRUE; +} diff --git a/sr_port/f_two_mstrs.c b/sr_port/f_two_mstrs.c new file mode 100644 index 0000000..7c7a75e --- /dev/null +++ b/sr_port/f_two_mstrs.c @@ -0,0 +1,40 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int f_two_mstrs( oprtype *a, opctype op) +{ + triple *r; + error_def(ERR_COMMA); + + r = maketriple(op); + if (!strexpr(&(r->operand[0]))) + return FALSE; + if (window_token != TK_COMMA) + { + stx_error(ERR_COMMA); + return FALSE; + } + advancewindow(); + if (!strexpr(&r->operand[1])) + { return FALSE; + } + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_two_mval.c b/sr_port/f_two_mval.c new file mode 100644 index 0000000..dbb7512 --- /dev/null +++ b/sr_port/f_two_mval.c @@ -0,0 +1,39 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int f_two_mval( oprtype *a, opctype op ) +{ + triple *r; + error_def(ERR_COMMA); + + r = maketriple(op); + if (!expr(&(r->operand[0]))) + return FALSE; + if (window_token != TK_COMMA) + { + stx_error(ERR_COMMA); + return FALSE; + } + advancewindow(); + if (!expr(&(r->operand[1]))) + return FALSE; + + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_view.c b/sr_port/f_view.c new file mode 100644 index 0000000..86a2e22 --- /dev/null +++ b/sr_port/f_view.c @@ -0,0 +1,62 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int f_view( oprtype *a, opctype op ) +{ + triple *root, *last, *curr; + oprtype argv[CHARMAXARGS], *argp; + int argc; + error_def(ERR_FCHARMAXARGS); + + argp = &argv[0]; + argc = 0; + if (!expr(argp)) + return FALSE; + assert(argp->oprclass == TRIP_REF); + argc++; + argp++; + for (;;) + { + if (window_token != TK_COMMA) + break; + advancewindow(); + if (!expr(argp)) + return FALSE; + assert(argp->oprclass == TRIP_REF); + argc++; + argp++; + if (argc >= CHARMAXARGS - 1) + { stx_error(ERR_FCHARMAXARGS); + return FALSE; + } + } + root = last = maketriple(op); + root->operand[0] = put_ilit(argc + 1); + argp = &argv[0]; + for (; argc > 0 ;argc--, argp++) + { + curr = newtriple(OC_PARAMETER); + curr->operand[0] = *argp; + last->operand[1] = put_tref(curr); + last = curr; + } + ins_triple(root); + *a = put_tref(root); + return TRUE; +} diff --git a/sr_port/f_zahandle.c b/sr_port/f_zahandle.c new file mode 100644 index 0000000..cef3471 --- /dev/null +++ b/sr_port/f_zahandle.c @@ -0,0 +1,73 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "mdq.h" + +GBLREF char window_token; + +int f_zahandle(oprtype *a, opctype op) +{ + triple *oldchain, tmpchain, *r, *triptr; + error_def(ERR_VAREXPECTED); + error_def(ERR_NAMEEXPECTED); + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + r = maketriple(op); + switch (window_token) + { + case TK_IDENT: + if (!lvn(&(r->operand[0]), OC_GETINDX, 0)) + return FALSE; + ins_triple(r); + break; + case TK_CIRCUMFLEX: + stx_error(ERR_NAMEEXPECTED); + return FALSE; + case TK_ATSIGN: + if (TREF(shift_side_effects)) + { + dqinit(&tmpchain, exorder); + oldchain = setcurtchain(&tmpchain); + if (!indirection(&(r->operand[0]))) + { + setcurtchain(oldchain); + return FALSE; + } + r->operand[1] = put_ilit((mint)indir_fnzahandle); + ins_triple(r); + newtriple(OC_GVSAVTARG); + setcurtchain(oldchain); + dqadd(TREF(expr_start), &tmpchain, exorder); + TREF(expr_start) = tmpchain.exorder.bl; + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(TREF(expr_start)); + } else + { + if (!indirection(&(r->operand[0]))) + return FALSE; + r->operand[1] = put_ilit((mint)indir_fnzahandle); + ins_triple(r); + } + r->opcode = OC_INDFUN; + break; + default: + stx_error(ERR_VAREXPECTED); + return FALSE; + } + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_zcall.c b/sr_port/f_zcall.c new file mode 100644 index 0000000..04f20e9 --- /dev/null +++ b/sr_port/f_zcall.c @@ -0,0 +1,70 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int f_zcall( oprtype *a, opctype op) +{ + triple *root, *last, *curr,*ref; + oprtype argv[CHARMAXARGS], *argp; + int argc; + error_def(ERR_FCHARMAXARGS); + + argp = &argv[0]; + argc = 0; + if (!expr(argp)) + return FALSE; + assert(argp->oprclass == TRIP_REF); + argc++; + argp++; + for (;;) + { + if (window_token != TK_COMMA) + break; + advancewindow(); + if (window_token == TK_COMMA || window_token == TK_RPAREN) + { + ref = newtriple(OC_NULLEXP); + *argp = put_tref(ref); + } + else + { + if (!expr(argp)) + return FALSE; + assert(argp->oprclass == TRIP_REF); + } + argc++; + argp++; + if (argc >= CHARMAXARGS) + { stx_error(ERR_FCHARMAXARGS); + return FALSE; + } + } + root = last = maketriple(op); + root->operand[0] = put_ilit(argc + 1); + argp = &argv[0]; + for (; argc > 0 ;argc--, argp++) + { + curr = newtriple(OC_PARAMETER); + curr->operand[0] = *argp; + last->operand[1] = put_tref(curr); + last = curr; + } + ins_triple(root); + *a = put_tref(root); + return TRUE; +} diff --git a/sr_port/f_zchar.c b/sr_port/f_zchar.c new file mode 100644 index 0000000..0eb2106 --- /dev/null +++ b/sr_port/f_zchar.c @@ -0,0 +1,99 @@ +/**************************************************************** + * * + * Copyright 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "stringpool.h" +#include "gtm_iconv.h" +#include "io.h" +#include "iosp.h" +#ifdef __MVS__ +#include "gtm_unistd.h" +#endif +#include "advancewindow.h" + +GBLREF char window_token; +GBLREF spdesc stringpool; + +int f_zchar(oprtype *a, opctype op) +{ + triple *root, *last, *curr; + oprtype argv[CHARMAXARGS], *argp; + mval v; + boolean_t all_lits; + char *c; + int argc, i; + unsigned char *tmp_ptr; + unsigned int tmp_len; + + error_def(ERR_FCHARMAXARGS); + error_def(ERR_TEXT); + + all_lits = TRUE; + argp = &argv[0]; + argc = 0; + for (;;) + { + if (!intexpr(argp)) + return FALSE; + assert(argp->oprclass == TRIP_REF); + if (argp->oprval.tref->opcode != OC_ILIT) + all_lits = FALSE; + argc++; + argp++; + if (window_token != TK_COMMA) + break; + advancewindow(); + if (argc >= CHARMAXARGS) + { + stx_error(ERR_FCHARMAXARGS); + return FALSE; + } + } + if (all_lits) + { + ENSURE_STP_FREE_SPACE(argc + 1); + v.mvtype = MV_STR; + v.str.addr = c = (char *)stringpool.free; + argp = &argv[0]; + for (; argc > 0 ;argc--, argp++) + { + i = argp->oprval.tref->operand[0].oprval.ilit; + if ((i >= 0) && (i < 256)) /* only true for single byte character set */ + *c++ = i; + } + *c = '\0'; + v.str.len = INTCAST(c - v.str.addr); + stringpool.free = (unsigned char *)c; + s2n(&v); + *a = put_lit(&v); + return TRUE; + } + root = maketriple(op); + root->operand[0] = put_ilit(argc + 1); + last = root; + argp = &argv[0]; + for (; argc > 0; argc--, argp++) + { + curr = newtriple(OC_PARAMETER); + curr->operand[0] = *argp; + last->operand[1] = put_tref(curr); + last = curr; + } + ins_triple(root); + *a = put_tref(root); + return TRUE; +} diff --git a/sr_port/f_zconvert.c b/sr_port/f_zconvert.c new file mode 100644 index 0000000..8babca0 --- /dev/null +++ b/sr_port/f_zconvert.c @@ -0,0 +1,99 @@ +/**************************************************************** + * * + * Copyright 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "compiler.h" + +#ifdef UNICODE_SUPPORTED +#include "toktyp.h" +#include "opcode.h" +#include "advancewindow.h" +#include "gtm_conv.h" + +GBLREF char window_token; + +error_def(ERR_BADCASECODE); +error_def(ERR_BADCHSET); +error_def(ERR_COMMA); + +/* $ZCONVERT(): 3 parameters (3rd optional) - all are string expressions. + + For 2 argument $ZCONVERT, if 2nd argument is a literal, must be one of + "U", "L", or "T" (case independent) or else raise BADCASECODE error. + + For 3 argument $ZCONVERT, if 2nd or 3rd arguments are literals, they + must be one of "UTF-8", "UTF-16LE", or "UTF-16BE" (case independent) + or else raise BADCHSET error. +*/ +int f_zconvert(oprtype *a, opctype op) +{ + triple *r, *mode, *mode2; + mstr *tmpstr; + + r = maketriple(op); + if (!strexpr(&(r->operand[0]))) + return FALSE; + if (TK_COMMA != window_token) + { + stx_error(ERR_COMMA); + return FALSE; + } + advancewindow(); + + /* 2nd parameter (required) */ + mode = newtriple(OC_PARAMETER); + r->operand[1] = put_tref(mode); + if (!strexpr(&(mode->operand[0]))) + return FALSE; + + /* Check for 3rd parameter */ + if (TK_COMMA != window_token) + { /* 3rd parameter does not exist. Do checks for 2 arument $zconvert */ + if (mode->operand[0].oprval.tref->opcode == OC_LIT && + -1 == verify_case((tmpstr = &mode->operand[0].oprval.tref->operand[0].oprval.mlit->v.str))) + { + stx_error(ERR_BADCASECODE, 2, tmpstr->len, tmpstr->addr); + return FALSE; + } + } else + { /* 3rd parameter exists .. reel it in after error checking 2nd parm */ + r->opcode = OC_FNZCONVERT3; + if (mode->operand[0].oprval.tref->opcode == OC_LIT && + 0 >= verify_chset((tmpstr = &mode->operand[0].oprval.tref->operand[0].oprval.mlit->v.str))) + { + stx_error(ERR_BADCHSET, 2, tmpstr->len, tmpstr->addr); + return FALSE; + } + advancewindow(); + mode2 = newtriple(OC_PARAMETER); + mode->operand[1] = put_tref(mode2); + if (!strexpr(&(mode2->operand[0]))) + return FALSE; + if (mode2->operand[0].oprval.tref->opcode == OC_LIT && + 0 >= verify_chset((tmpstr = &mode2->operand[0].oprval.tref->operand[0].oprval.mlit->v.str))) + { + stx_error(ERR_BADCHSET, 2, tmpstr->len, tmpstr->addr); + return FALSE; + } + } + + ins_triple(r); + *a = put_tref(r); + return TRUE; +} + +#else /* Unicode is not supported */ +int f_zconvert(oprtype *a, opctype op) +{ + GTMASSERT; +} +#endif diff --git a/sr_port/f_zdate.c b/sr_port/f_zdate.c new file mode 100644 index 0000000..e71c9de --- /dev/null +++ b/sr_port/f_zdate.c @@ -0,0 +1,51 @@ +/**************************************************************** + * * + * Copyright 2001, 2003 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; +LITREF mval literal_null ; + +int f_zdate( oprtype *a, opctype op ) /* op is not used */ +{ + triple *args[4]; + int i; + bool more_args; + + args[0] = maketriple(OC_FNZDATE); + if (!expr(&(args[0]->operand[0]))) + return FALSE; + for (i = 1 , more_args = TRUE ; i < 4 ; i++) + { + args[i] = newtriple(OC_PARAMETER); + if (more_args) + { + if (window_token != TK_COMMA) + more_args = FALSE; + else + { + advancewindow(); + if (!expr(&(args[i]->operand[0]))) + return FALSE; + } + } + if (!more_args) + args[i]->operand[0] = put_lit((mval *)&literal_null); + args[i - 1]->operand[1] = put_tref(args[i]); + } + ins_triple(args[0]); + *a = put_tref(args[0]); + return TRUE; +} diff --git a/sr_port/f_zgetsyi.c b/sr_port/f_zgetsyi.c new file mode 100644 index 0000000..220d578 --- /dev/null +++ b/sr_port/f_zgetsyi.c @@ -0,0 +1,38 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int f_zgetsyi( oprtype *a, opctype op) +{ + triple *r; + + r = maketriple(op); + if (!strexpr(&(r->operand[0]))) + return FALSE; + if (window_token != TK_COMMA) + r->operand[1] = put_str("",0); + else + { + advancewindow(); + if (!strexpr(&r->operand[1])) + return FALSE; + } + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_zjobexam.c b/sr_port/f_zjobexam.c new file mode 100644 index 0000000..47011c5 --- /dev/null +++ b/sr_port/f_zjobexam.c @@ -0,0 +1,33 @@ +/**************************************************************** + * * + * Copyright 2001, 2002 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int f_zjobexam(oprtype *a, opctype op) +{ + triple *r; + + r = maketriple(op); + if (TK_RPAREN == window_token) + { /* No argument specified - default to null */ + r->operand[0] = put_str("",0); + } else if (!strexpr(&(r->operand[0]))) + return FALSE; /* Improper string argument */ + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_zparse.c b/sr_port/f_zparse.c new file mode 100644 index 0000000..3d3e6eb --- /dev/null +++ b/sr_port/f_zparse.c @@ -0,0 +1,53 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int f_zparse( oprtype *a, opctype op) +{ + triple *ref, *last, *r; + bool again; + int i; + + last = r = maketriple(op); + if (!strexpr(&(r->operand[0]))) + return FALSE; + again = TRUE; + for(i = 0; i < 4 ;i++) + { + ref = newtriple(OC_PARAMETER); + last->operand[1] = put_tref(ref); + if(again && window_token == TK_COMMA) + { + advancewindow(); + if (window_token == TK_COMMA) + { ref->operand[0] = put_str("",0); + } + else if(!strexpr(&ref->operand[0])) + return FALSE; + } + else + { + again = FALSE; + ref->operand[0] = put_str("",0); + } + last = ref; + } + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_zprevious.c b/sr_port/f_zprevious.c new file mode 100644 index 0000000..ff9d27b --- /dev/null +++ b/sr_port/f_zprevious.c @@ -0,0 +1,85 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "mdq.h" +#include "advancewindow.h" + +GBLREF char window_token, director_token; +GBLREF mident window_ident; + +int f_zprevious( oprtype *a, opctype op) +{ + triple *oldchain, tmpchain, *r, *triptr; + error_def(ERR_VAREXPECTED); + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + r = maketriple(op); + switch (window_token) + { + case TK_IDENT: + if (director_token != TK_LPAREN) + { + r->opcode = OC_FNLVPRVNAME; + r->operand[0] = put_str(window_ident.addr, window_ident.len); + ins_triple(r); + advancewindow(); + break; + } + if (!lvn(&(r->operand[0]), OC_SRCHINDX, r)) + return FALSE; + ins_triple(r); + break; + case TK_CIRCUMFLEX: + if (!gvn()) + return FALSE; + r->opcode = OC_ZPREVIOUS; + ins_triple(r); + break; + case TK_ATSIGN: + if (TREF(shift_side_effects)) + { + dqinit(&tmpchain, exorder); + oldchain = setcurtchain(&tmpchain); + if (!indirection(&(r->operand[0]))) + { + setcurtchain(oldchain); + return FALSE; + } + r->operand[1] = put_ilit((mint)indir_fnzprevious); + ins_triple(r); + newtriple(OC_GVSAVTARG); + setcurtchain(oldchain); + dqadd(TREF(expr_start), &tmpchain, exorder); + TREF(expr_start) = tmpchain.exorder.bl; + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(TREF(expr_start)); + } else + { + if (!indirection(&(r->operand[0]))) + return FALSE; + r->operand[1] = put_ilit((mint)indir_fnzprevious); + ins_triple(r); + } + r->opcode = OC_INDFUN; + break; + default: + stx_error(ERR_VAREXPECTED); + return FALSE; + } + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_zqgblmod.c b/sr_port/f_zqgblmod.c new file mode 100644 index 0000000..4be8fed --- /dev/null +++ b/sr_port/f_zqgblmod.c @@ -0,0 +1,70 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "mdq.h" + +GBLREF char window_token; + +int f_zqgblmod(oprtype *a, opctype op) +{ + triple *oldchain, tmpchain, *r, *triptr; + error_def(ERR_VAREXPECTED); + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + r = maketriple(op); + switch (window_token) + { + case TK_CIRCUMFLEX: + if (!gvn()) + return FALSE; + r->opcode = OC_FNZQGBLMOD; + ins_triple(r); + break; + case TK_ATSIGN: + r->opcode = OC_INDFUN; + if (TREF(shift_side_effects)) + { + dqinit(&tmpchain, exorder); + oldchain = setcurtchain(&tmpchain); + if (!indirection(&(r->operand[0]))) + { + setcurtchain(oldchain); + return FALSE; + } + r->operand[1] = put_ilit((mint)indir_fnzqgblmod); + ins_triple(r); + newtriple(OC_GVSAVTARG); + setcurtchain(oldchain); + dqadd(TREF(expr_start), &tmpchain, exorder); + TREF(expr_start) = tmpchain.exorder.bl; + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(TREF(expr_start)); + } else + { + if (!indirection(&(r->operand[0]))) + return FALSE; + r->operand[1] = put_ilit((mint)indir_fnzqgblmod); + ins_triple(r); + } + break; + default: + stx_error(ERR_VAREXPECTED); + return FALSE; + } + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_zsearch.c b/sr_port/f_zsearch.c new file mode 100644 index 0000000..50f7953 --- /dev/null +++ b/sr_port/f_zsearch.c @@ -0,0 +1,38 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int f_zsearch( oprtype *a, opctype op ) +{ + triple *r; + + r = maketriple(op); + if (!strexpr(&(r->operand[0]))) + return FALSE; + if (window_token != TK_COMMA) + r->operand[1] = put_ilit(0); + else + { + advancewindow(); + if (!intexpr(&(r->operand[1]))) + return FALSE; + } + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_zsigproc.c b/sr_port/f_zsigproc.c new file mode 100644 index 0000000..01ad3f2 --- /dev/null +++ b/sr_port/f_zsigproc.c @@ -0,0 +1,41 @@ +/**************************************************************** + * * + * Copyright 2001, 2002 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int f_zsigproc(oprtype *a, opctype op) +{ + triple *r; + error_def(ERR_COMMA); + + r = maketriple(op); + /* First argument is integer process id */ + if (!intexpr(&(r->operand[0]))) + return FALSE; /* Improper process id argument */ + if (window_token != TK_COMMA) + { /* 2nd argument (for now) required */ + stx_error(ERR_COMMA); + return FALSE; + } + advancewindow(); + /* 2nd argument is the signal number to send */ + if (!intexpr(&(r->operand[1]))) + return FALSE; /* Improper signal number argument */ + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_ztrigger.c b/sr_port/f_ztrigger.c new file mode 100644 index 0000000..54e61b3 --- /dev/null +++ b/sr_port/f_ztrigger.c @@ -0,0 +1,54 @@ +/**************************************************************** + * * + * Copyright 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "compiler.h" +#include "toktyp.h" +#include "opcode.h" +#include "advancewindow.h" + +GBLREF char window_token; +LITREF mval literal_null ; + +int f_ztrigger(oprtype *a, opctype op) +{ + triple *r, *arg1, *arg2; + + r = maketriple(op); + arg1 = newtriple(OC_PARAMETER); + arg2 = newtriple(OC_PARAMETER); + if (!strexpr(&(r->operand[0]))) + return FALSE; + if (TK_COMMA == window_token) + { /* Looking for a 2nd argument */ + advancewindow(); + if (!strexpr(&(arg1->operand[0]))) + return FALSE; + if (TK_COMMA == window_token) + { + advancewindow(); + if (!strexpr(&(arg2->operand[0]))) + return FALSE; + + } else + arg2->operand[0] = put_lit((mval *)&literal_null); + } else + { + arg1->operand[0] = put_lit((mval *)&literal_null); + arg2->operand[0] = put_lit((mval *)&literal_null); + } + r->operand[1] = put_tref(arg1); + arg1->operand[1] = put_tref(arg2); + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_ztrnlnm.c b/sr_port/f_ztrnlnm.c new file mode 100644 index 0000000..39b6bd3 --- /dev/null +++ b/sr_port/f_ztrnlnm.c @@ -0,0 +1,78 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int f_ztrnlnm( oprtype *a, opctype op ) +{ + triple *r, *last, *ref; + int i; + bool again; + + last = r = maketriple(op); + if (!strexpr(&r->operand[0])) + return FALSE; + ref = newtriple(OC_PARAMETER); + last->operand[1] = put_tref(ref); + if (window_token == TK_COMMA) + { advancewindow(); + if (window_token == TK_COMMA || window_token == TK_RPAREN) + { ref->operand[0] = put_str("",0); + }else + { if (!strexpr(&ref->operand[0])) + return FALSE; + } + }else + { ref->operand[0] = put_str("",0); + } + last = ref; + ref = newtriple(OC_PARAMETER); + last->operand[1] = put_tref(ref); + if (window_token == TK_COMMA) + { advancewindow(); + if (window_token == TK_COMMA || window_token == TK_RPAREN) + { ref->operand[0] = put_ilit(0); + }else + { if (!intexpr(&ref->operand[0])) + return FALSE; + } + }else + { ref->operand[0] = put_ilit(0); + } + last = ref; + again = TRUE; + for (i = 0; i < 3; i++) + { ref = newtriple(OC_PARAMETER); + last->operand[1] = put_tref(ref); + if (again && window_token == TK_COMMA) + { advancewindow(); + if (window_token == TK_COMMA || window_token == TK_RPAREN) + { ref->operand[0] = put_str("",0); + }else + { if (!strexpr(&ref->operand[0])) + return FALSE; + } + }else + { again = FALSE; + ref->operand[0] = put_str("",0); + } + last = ref; + } + ins_triple(r); + *a = put_tref(r); + return TRUE; +} diff --git a/sr_port/f_zwidth.c b/sr_port/f_zwidth.c new file mode 100644 index 0000000..d4ef680 --- /dev/null +++ b/sr_port/f_zwidth.c @@ -0,0 +1,30 @@ +/**************************************************************** + * * + * Copyright 2006, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "advancewindow.h" + +GBLREF char window_token; + +/* $ZWIDTH(): Single parameter - string expression */ +int f_zwidth(oprtype *a, opctype op) +{ + triple *r; + + r = maketriple(op); + if (!strexpr(&(r->operand[0]))) + return FALSE; + ins_triple(r); + *a = put_tref(r); + return TRUE; +} + diff --git a/sr_port/fao_parm.h b/sr_port/fao_parm.h new file mode 100644 index 0000000..9544227 --- /dev/null +++ b/sr_port/fao_parm.h @@ -0,0 +1,20 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +typedef struct { +unsigned short len; +unsigned char fill1; +unsigned char fill2; +char *addr; +}desc_struct; + + +#define MAX_FAO_PARMS 20 diff --git a/sr_port/fgn_glopref.c b/sr_port/fgn_glopref.c new file mode 100644 index 0000000..5dd87bf --- /dev/null +++ b/sr_port/fgn_glopref.c @@ -0,0 +1,37 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* fgn_glopref : prefixes the global variable name with ^ + for locks. +*/ +#include "mdef.h" + +#include "gtm_string.h" + +#include "stringpool.h" +#include "rtnhdr.h" +#include "lv_val.h" /* needed by "fgncal.h" */ +#include "fgncal.h" + +GBLREF spdesc stringpool ; + +void fgn_glopref(mval *v) +{ + unsigned char *p; + + ENSURE_STP_FREE_SPACE(v->str.len + 1); + p = stringpool.free; + *stringpool.free++ = '^'; + memcpy(stringpool.free,v->str.addr,v->str.len); + stringpool.free += v->str.len ; + v->str.addr = (char *)p; + v->str.len++; +} diff --git a/sr_port/fgncal.h b/sr_port/fgncal.h new file mode 100644 index 0000000..a615f01 --- /dev/null +++ b/sr_port/fgncal.h @@ -0,0 +1,21 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __FGNCAL_H__ +#define __FGNCAL_H__ + +mval *fgncal_lookup(mval *x); +void fgncal_unwind(void); +void fgncal_rundown(void); + +#include "fgncalsp.h" + +#endif diff --git a/sr_port/fgncal_lookup.c b/sr_port/fgncal_lookup.c new file mode 100644 index 0000000..0985eb7 --- /dev/null +++ b/sr_port/fgncal_lookup.c @@ -0,0 +1,45 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "toktyp.h" +#include "lv_val.h" /* needed by "fgncal.h" */ +#include "fgncal.h" +#include "valid_mname.h" +#include "rtnhdr.h" + +GBLREF symval *curr_symval; + +mval *fgncal_lookup(mval *x) +{ + mval *ret_val; + ht_ent_mname *tabent; + var_tabent targ_key; + mident ident; + + MV_FORCE_DEFINED(x); + assert(MV_IS_STRING(x)); + ret_val = NULL; + ident = x->str; + if (ident.len > MAX_MIDENT_LEN) + ident.len = MAX_MIDENT_LEN; + if (valid_mname(&ident)) + { + targ_key.var_name = ident; + COMPUTE_HASH_MNAME(&targ_key); + targ_key.marked = FALSE; + if (add_hashtab_mname_symval(&curr_symval->h_symtab, &targ_key, NULL, &tabent)) + lv_newname(tabent, curr_symval); + ret_val = (mval *) tabent->value; + } + return ret_val; +} diff --git a/sr_port/fgncal_unwind.c b/sr_port/fgncal_unwind.c new file mode 100644 index 0000000..3f51303 --- /dev/null +++ b/sr_port/fgncal_unwind.c @@ -0,0 +1,65 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "rtnhdr.h" +#include "stack_frame.h" +#include "tp_frame.h" +#include "error.h" +#include "error_trap.h" +#include "mv_stent.h" +#include "op.h" +#include "fgncal.h" +#ifdef GTM_TRIGGER +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gv_trigger.h" +#include "gtm_trigger.h" +#endif + +GBLDEF unsigned char *fgncal_stack; +GBLREF unsigned char *stackbase, *stacktop, *stackwarn, *msp; +GBLREF mv_stent *mv_chain; +GBLREF stack_frame *frame_pointer; + +void fgncal_unwind(void) +{ + mv_stent *mvc; + + error_def(ERR_STACKUNDERFLO); + + assert(msp <= stackbase && msp > stacktop); + assert(mv_chain <= (mv_stent *)stackbase && mv_chain > (mv_stent *)stacktop); + assert(frame_pointer <= (stack_frame*)stackbase && frame_pointer > (stack_frame *)stacktop); + + while (frame_pointer && frame_pointer < (stack_frame *)fgncal_stack) + { +# ifdef GTM_TRIGGER + if (SFT_TRIGR & frame_pointer->type) + gtm_trigger_fini(TRUE, FALSE); + else +# endif + op_unwind(); + } + for (mvc = mv_chain ; mvc < (mv_stent *) fgncal_stack ; ) + { + unw_mv_ent(mvc); + mvc = (mv_stent *)(mvc->mv_st_next + (char *) mvc); + } + mv_chain = mvc; + msp = fgncal_stack; + if (msp > stackbase) + rts_error(VARLSTCNT(1) ERR_STACKUNDERFLO); + +} diff --git a/sr_port/file_head_read.h b/sr_port/file_head_read.h new file mode 100644 index 0000000..63dbdf4 --- /dev/null +++ b/sr_port/file_head_read.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2003, 2005 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef FILE_HEAD_READ_INCLUDED +#define FILE_HEAD_READ_INCLUDED + +boolean_t file_head_read(char *, sgmnt_data_ptr_t, int4); + +#endif /* FILE_HEAD_READ_INCLUDED */ diff --git a/sr_port/file_head_write.h b/sr_port/file_head_write.h new file mode 100644 index 0000000..29d3f93 --- /dev/null +++ b/sr_port/file_head_write.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2003, 2005 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef FILE_HEAD_WRITE_INCLUDED +#define FILE_HEAD_WRITE_INCLUDED + +boolean_t file_head_write(char *, sgmnt_data_ptr_t, int4); + +#endif /* FILE_HEAD_WRITE_INCLUDED */ diff --git a/sr_port/fileinfo.h b/sr_port/fileinfo.h new file mode 100644 index 0000000..2afddb6 --- /dev/null +++ b/sr_port/fileinfo.h @@ -0,0 +1,38 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __FILE_INFO_H__ +#define __FILE_INFO_H__ + +#define FI_USR_SZ 31 +#define FI_TRM_SZ 7 + +typedef struct +{ + gtm_facility fac; /* facility */ + short dat[4]; /* date (quadword) */ + char usr[FI_USR_SZ]; /* user name */ + char trm[FI_TRM_SZ]; /* terminal identification */ + char filler[2]; /* used for longword alignment */ +}file_info; + +#define FI_NUM_ENT 5 +typedef struct +{ + int4 cnt; /* number of entries inserted into ent. + * ent is a circular queue so + * ent[ cnt % FI_NUM_ENT] + * is always the next location to insert. + */ + file_info ent[FI_NUM_ENT]; /* entries */ +}file_log; + +#endif diff --git a/sr_port/find_line_addr.c b/sr_port/find_line_addr.c new file mode 100644 index 0000000..df74f7f --- /dev/null +++ b/sr_port/find_line_addr.c @@ -0,0 +1,86 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "rtnhdr.h" +#include "cmd_qlf.h" +#include "gtm_caseconv.h" +#include "min_max.h" + +GBLREF command_qualifier cmd_qlf; + +int4* find_line_addr (rhdtyp *routine, mstr *label, int4 offset, mident **lent_name) +{ + lab_tabent *base, *top, *ptr; + rhdtyp *real_routine; + mident_fixed target_label; + mident lname; + lnr_tabent *line_table, *first_line; + int stat, n; + error_def(ERR_LABELONLY); + + if (!routine) + return NULL; + real_routine = CURRENT_RHEAD_ADR(routine); + first_line = LNRTAB_ADR(real_routine); + + if (!label->len || !*label->addr) + { /* No label specified. Return the first line */ + base = LABTAB_ADR(real_routine); + assert(0 == base->lab_name.len); + if (lent_name) + *lent_name = &base->lab_name; + line_table = first_line; + } + else + { + lname.len = (label->len <= MAX_MIDENT_LEN) ? label->len : MAX_MIDENT_LEN; + if (cmd_qlf.qlf & CQ_LOWER_LABELS) + lname.addr = label->addr; + else + { + lower_to_upper((uchar_ptr_t)&target_label.c[0], (uchar_ptr_t)label->addr, lname.len); + lname.addr = &target_label.c[0]; + } + + ptr = base = LABTAB_ADR(real_routine); + top = base + real_routine->labtab_len; + for ( ; ; ) + { + n = (int)(top - base) / 2; + ptr = base + n; + MIDENT_CMP(&lname, &ptr->lab_name, stat); + if (0 == stat) + { + if (lent_name) + *lent_name = &ptr->lab_name; + line_table = LABENT_LNR_ENTRY(real_routine, ptr); + break; + } + else if (stat > 0) + base = ptr; + else + top = ptr; + + if (n < 1) + return NULL; + } + } + + line_table += offset; + if (line_table < first_line || line_table >= first_line + real_routine->lnrtab_len) + return NULL; + + return line_table; +} diff --git a/sr_port/find_line_start.c b/sr_port/find_line_start.c new file mode 100644 index 0000000..be7f093 --- /dev/null +++ b/sr_port/find_line_start.c @@ -0,0 +1,69 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "rtnhdr.h" + +GBLREF unsigned char *stackbase, *stacktop; + +unsigned char *find_line_start(unsigned char *in_addr, rhdtyp *routine) +{ + unsigned char *result; + lab_tabent *max_label, *label_table, *last_label; + lnr_tabent *line_table, *last_line; + int4 in_addr_offset; + + result = (unsigned char *)0; + + if (!ADDR_IN_CODE(in_addr, routine)) + return result; + routine = CURRENT_RHEAD_ADR(routine); + USHBIN_ONLY( + assert(routine->labtab_adr); + assert(routine->lnrtab_adr); + ); + NON_USHBIN_ONLY( + assert(routine->labtab_ptr >= 0); + assert(routine->lnrtab_ptr >= 0); + ); + assert(routine->labtab_len >= 0); + assert(routine->lnrtab_len >= 0); + label_table = LABTAB_ADR(routine); + last_label = label_table + routine->labtab_len; + max_label = label_table++; + while (label_table < last_label) + { /* Find first label that goes past the input addr. The previous label is then the target line */ + if (in_addr > LABEL_ADDR(routine, label_table)) + { + if (max_label->LABENT_LNR_OFFSET <= label_table->LABENT_LNR_OFFSET) + max_label = label_table; + } + label_table++; + } + line_table = LABENT_LNR_ENTRY(routine, max_label); +/* Used as offset !! */ + in_addr_offset = (int4)(in_addr - CODE_BASE_ADDR(routine)); + last_line = LNRTAB_ADR(routine); + last_line += routine->lnrtab_len; + for( ; ++line_table < last_line ;) + { /* Find first line that is > input addr. The previous line is the target line */ + if (in_addr_offset <= *line_table) + { + result = LINE_NUMBER_ADDR(routine, (line_table - 1)); + break; + } + } + if (line_table >= last_line) + result = LINE_NUMBER_ADDR(routine, (line_table - 1)); + + return result; +} + diff --git a/sr_port/find_rtn_hdr.c b/sr_port/find_rtn_hdr.c new file mode 100644 index 0000000..81c56ff --- /dev/null +++ b/sr_port/find_rtn_hdr.c @@ -0,0 +1,70 @@ +/**************************************************************** + * * + * Copyright 2001, 2004 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "rtnhdr.h" +#include "ident.h" +#include "min_max.h" + +#define S_CUTOFF 7 +GBLREF rtn_tabent *rtn_names, *rtn_names_end; + +rhdtyp *find_rtn_hdr(mstr *name) +{ + rtn_tabent *bot, *top, *mid; + int4 comp; + mident rtn_name; + mident_fixed rtn_name_buff; + + assert (name->len <= MAX_MIDENT_LEN); + rtn_name.len = name->len; +#ifdef VMS + rtn_name.addr = &rtn_name_buff.c[0]; + CONVERT_IDENT(rtn_name.addr, name->addr, name->len); +#else + rtn_name.addr = name->addr; +#endif + bot = rtn_names; + top = rtn_names_end; + for (;;) + { + if (top < bot) + return 0; + else if ((top - bot) < S_CUTOFF) + { + comp = -1; + for (mid = bot; comp < 0 && mid <= top; mid++) + { + MIDENT_CMP(&mid->rt_name, &rtn_name, comp); + if (0 == comp) + return mid->rt_adr; + } + return 0; + } else + { + mid = bot + (top - bot) / 2; + MIDENT_CMP(&mid->rt_name, &rtn_name, comp); + if (0 == comp) + return mid->rt_adr; + else if (comp < 0) + { + bot = mid + 1; + continue; + } else + { + top = mid - 1; + continue; + } + } + } +} diff --git a/sr_port/five_2_ascii.c b/sr_port/five_2_ascii.c new file mode 100644 index 0000000..e6f5d65 --- /dev/null +++ b/sr_port/five_2_ascii.c @@ -0,0 +1,24 @@ +/**************************************************************** + * * + * Copyright 2002 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "five_2_ascii.h" + +unsigned char *five_2_ascii(unsigned short *inval, unsigned char *cp) +{ + int4 val; + + val = *inval; + *cp++ = (val >> 11) + '@'; + *cp++ = ((val >> 6) & 0x1f) + '@'; + *cp++ = ((val >> 1) & 0x1f) + '@'; + return cp; +} diff --git a/sr_port/five_2_ascii.h b/sr_port/five_2_ascii.h new file mode 100644 index 0000000..53357fc --- /dev/null +++ b/sr_port/five_2_ascii.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2002 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef FIVE_2_ASCII_H_INCLUDED +#define FIVE_2_ASCII_H_INCLUDED + +unsigned char *five_2_ascii(unsigned short *inval, unsigned char *cp); + +#endif /* FIVE_2_ASCII_H_INCLUDED */ diff --git a/sr_port/five_bit.c b/sr_port/five_bit.c new file mode 100644 index 0000000..3df8844 --- /dev/null +++ b/sr_port/five_bit.c @@ -0,0 +1,28 @@ +/**************************************************************** + * * + * Copyright 2002 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "five_bit.h" + +/* five_bit - convert 3-character string into 5-bit character representation */ + +unsigned short five_bit(unsigned char *src) /* src is pointer to 3-character string to be converted to 5-bit format */ +{ + int index; + unsigned short result; + + /* Or low-order 5 bits of each character together into high-order 15 bits of result. */ + for (index = 0, result = 0; index < 3; index++, src++) + result = (result << 5) | (*src & 0x1f); + result <<= 1; + + return result; +} diff --git a/sr_port/five_bit.h b/sr_port/five_bit.h new file mode 100644 index 0000000..70c0827 --- /dev/null +++ b/sr_port/five_bit.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2002 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef FIVE_BIT_H_INCLUDED +#define FIVE_BIT_H_INCLUDED + +unsigned short five_bit(unsigned char *src); + +#endif /* FIVE_BIT_H_INCLUDED */ diff --git a/sr_port/fix_pages.h b/sr_port/fix_pages.h new file mode 100644 index 0000000..a65bc09 --- /dev/null +++ b/sr_port/fix_pages.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef FIX_PAGES_INCLUDED +#define FIX_PAGES_INCLUDED + +void fix_pages(unsigned char * bot, unsigned char * top); + +#endif /* FIX_PAGES_INCLUDED */ diff --git a/sr_port/fix_xfer_entry.h b/sr_port/fix_xfer_entry.h new file mode 100644 index 0000000..66baec6 --- /dev/null +++ b/sr_port/fix_xfer_entry.h @@ -0,0 +1,39 @@ +/**************************************************************** + * * + * Copyright 2007, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef FIX_XFER_ENTRY_INCLUDED +#define FIX_XFER_ENTRY_INCLUDED + +GBLREF xfer_entry_t xfer_table[]; + +#ifdef __ia64 +GBLREF char xfer_table_desc[]; +#endif /* __ia64 */ + +#if defined(__ia64) || defined(__x86_64__) +#include "xfer_desc.i" +#endif + +#ifndef __ia64 +#define FIX_XFER_ENTRY(indx, func) \ +{ \ + xfer_table[indx] = (xfer_entry_t)&func; \ +} +#else /* __ia64 */ +#define FIX_XFER_ENTRY(indx, func) \ +{ \ + xfer_table[indx] = (xfer_entry_t)CODE_ADDRESS(func); \ + xfer_table_desc[indx] = func##_FUNCTYPE; \ +} + +#endif /* __ia64 */ + +#endif /* FIX_XFER_ENTRY_INCLUDED */ diff --git a/sr_port/fl.mpt b/sr_port/fl.mpt new file mode 100644 index 0000000..fb9c74d --- /dev/null +++ b/sr_port/fl.mpt @@ -0,0 +1,15 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 1992,2001 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +%FL ;GT.M %FL utility - first lines lister + ;invoke ^%FL to get interaction + ; + d FL^%RO + q diff --git a/sr_port/flt_mod.c b/sr_port/flt_mod.c new file mode 100644 index 0000000..d4d2469 --- /dev/null +++ b/sr_port/flt_mod.c @@ -0,0 +1,131 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* flt_mod.(u, v) = u - (v*floor.(u/v)) where x-1 < floor.x <= x ^ int.x */ + +#include "mdef.h" + +#include "arit.h" +#include "op.h" +#include "eb_muldiv.h" +#include "promodemo.h" +#include "flt_mod.h" + +LITREF mval literal_zero; +LITREF int4 ten_pwr[]; + +void flt_mod (mval *u, mval *v, mval *q) +{ + int exp; + int4 z, x; + mval w; /* temporary mval for division result */ + mval y; /* temporary mval for extended precision promotion + to prevent modifying caller's data */ + mval *u_orig; /* original (caller's) value of u */ + error_def(ERR_DIVZERO); + + u_orig = u; + MV_FORCE_NUM(u); + MV_FORCE_NUM(v); + + if ((v->mvtype & MV_INT) != 0 && v->m[1] == 0) + rts_error(VARLSTCNT(1) ERR_DIVZERO); + + if ((u->mvtype & MV_INT & v->mvtype) != 0) + { + /* Both are INT's; use shortcut. */ + q->mvtype = MV_NM | MV_INT; + eb_int_mod(u->m[1], v->m[1], q->m); + return; + } + else if ((u->mvtype & MV_INT) != 0) + { + /* u is INT; promote to extended precision for compatibility with v. */ + y = *u; + promote(&y); /* y will be normalized, but not in canonical form */ + u = &y; /* this is why we need u_orig */ + } + else if ((v->mvtype & MV_INT) != 0) + { + /* v is INT; promote to extended precision for compatibility with u. */ + y = *v; + promote(&y); + v = &y; + } + + /* At this point, both u and v are in extended precision format. */ + + /* Set w = floor(u/v). */ + op_div (u, v, &w); + if ((w.mvtype & MV_INT) != 0) + promote(&w); + exp = w.e; + if (exp <= MV_XBIAS) + { + /* Magnitude of w, floor(u/v), is < 1. */ + if (u->sgn != v->sgn && w.m[1] != 0 && exp >= EXPLO) + { + /* Signs differ (=> floor(u/v) < 0) and (w != 0) and (no underflow) => floor(u/v) == -1 */ + w.sgn = 1; + w.e = MV_XBIAS + 1; + w.m[1] = MANT_LO; + w.m[0] = 0; + } + else + { + /* Signs same (=> floor(u/v) >= 0) or (w == 0) or (underflow) => floor(u/v) == 0 */ + *q = *u_orig; /* u - floor(u/v)*v == u - 0*v == u */ + return; + } + } + else if (exp < EXP_IDX_BIAL) + { + z = ten_pwr[EXP_IDX_BIAL - exp]; + x = (w.m[1]/z)*z; + if (u->sgn != v->sgn && (w.m[1] != x || w.m[0] != 0)) + { + w.m[0] = 0; + w.m[1] = x + z; + if (w.m[1] >= MANT_HI) + { + w.m[0] = w.m[0]/10 + (w.m[1]%10)*MANT_LO; + w.m[1] /= 10; + w.e++; + } + } + else + { + w.m[0] = 0; + w.m[1] = x; + } + } + else if (exp < EXP_IDX_BIAQ) + { + z = ten_pwr[EXP_IDX_BIAQ - exp]; + x = (w.m[0]/z)*z; + if (u->sgn != v->sgn && w.m[0] != x) + { + w.m[0] = x + z; + if (w.m[0] >= MANT_HI) + { + w.m[0] -= MANT_HI; + w.m[1]++; + } + } + else + { + w.m[0] = x; + } + } + + op_mul (&w, v, &w); /* w = w*v = floor(u/v)*v */ + op_sub (u_orig, &w, q); /* q = u - w = u - floor(u/v)*v */ +} diff --git a/sr_port/flt_mod.h b/sr_port/flt_mod.h new file mode 100644 index 0000000..1a006f7 --- /dev/null +++ b/sr_port/flt_mod.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __FLT_MOD_H__ +#define __FLT_MOD_H__ + +void flt_mod (mval *u, mval *v, mval *q); + +#endif diff --git a/sr_port/flush_jmp.c b/sr_port/flush_jmp.c new file mode 100644 index 0000000..68898ce --- /dev/null +++ b/sr_port/flush_jmp.c @@ -0,0 +1,197 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" +#include "gtm_string.h" + +#include "rtnhdr.h" +#include "mv_stent.h" +#include "objlabel.h" +#include "cache.h" +#include "stack_frame.h" +#include "cache_cleanup.h" +#include "op.h" +#include "unwind_nocounts.h" +#include "flush_jmp.h" +#include "error.h" +#include "tp_frame.h" +#ifdef GTM_TRIGGER +# include "gtm_trigger_trc.h" +#endif + +GBLREF symval *curr_symval; +GBLREF stack_frame *error_frame; +GBLREF stack_frame *frame_pointer; +GBLREF mv_stent *mv_chain; +GBLREF unsigned char *stackbase,*stacktop,*msp,*stackwarn; +GBLREF tp_frame *tp_pointer; + +LITREF boolean_t mvs_save[]; + +STATICFNDCL void fix_tphold_mvc(char *target, char *srcstart, char *srcend); + +error_def(ERR_STACKCRIT); +error_def(ERR_STACKOFLOW); + +void flush_jmp (rhdtyp *rtn_base, unsigned char *context, unsigned char *transfer_addr) +{ + mv_stent *mv_st_ent, *mv_st_prev; + char *top; + unsigned char *msp_save; + int4 shift, size, mv_st_type; + + unwind_nocounts(); + /* We are going to mutate the current frame from the program it was running to the program we want it to run. + * If the current frame is marked for indr cache cleanup, do that cleanup now and unmark the frame. + */ + IF_INDR_FRAME_CLEANUP_CACHE_ENTRY_AND_UNMARK(frame_pointer); + + DBGEHND((stderr, "flush_jmp: Retargetting stack frame 0x"lvaddr" for transfer address 0x"lvaddr"\n", frame_pointer, + transfer_addr)); + /* Also unmark the SFF_ETRAP_ERR bit in case it is set. This way we ensure control gets transferred to + * the mpc below instead of "error_return" (which is what getframe will do in case the bit is set). + * It is ok to clear this bit because the global variable "error_frame" will still be set to point to + * this frame so whenever we unwind out of this, we will rethrow the error at the parent frame. + */ + assert(!(frame_pointer->flags & SFF_ETRAP_ERR) || (NULL == error_frame) || (error_frame == frame_pointer)); + assert(!(SFT_TRIGR & frame_pointer->type)); + frame_pointer->flags &= SFF_ETRAP_ERR_OFF; /* clear the SFF_ETRAP_ERR bit */ + frame_pointer->flags &= SFF_TRIGR_CALLD_OFF; /* clear the SFF_TRIGR_CALLD bit since this frame is being rewritten */ + GTMTRIG_ONLY(DBGTRIGR((stderr, "flush_jmp: Turrning off SFF_TRIGR_CALLD in frame 0x"lvaddr"\n", frame_pointer))); + frame_pointer->rvector = rtn_base; + frame_pointer->vartab_ptr = (char *)VARTAB_ADR(rtn_base); + frame_pointer->vartab_len = frame_pointer->rvector->vartab_len; + frame_pointer->mpc = transfer_addr; + frame_pointer->ctxt = context; +#ifdef HAS_LITERAL_SECT + frame_pointer->literal_ptr = (int4 *)LITERAL_ADR(rtn_base); +#endif + frame_pointer->temp_mvals = frame_pointer->rvector->temp_mvals; + size = rtn_base->temp_size; + frame_pointer->temps_ptr = (unsigned char *)frame_pointer - size; + size += rtn_base->vartab_len * SIZEOF(ht_ent_mname *); + frame_pointer->l_symtab = (ht_ent_mname **)((char *)frame_pointer - size); + assert(frame_pointer->type & SFT_COUNT); + assert((unsigned char *)mv_chain > stacktop && (unsigned char *)mv_chain <= stackbase); + while (((char *)mv_chain < (char *)frame_pointer) && !mvs_save[mv_chain->mv_st_type]) + { + assert(MVST_TRIGR != mv_chain->mv_st_type); /* Should never unwind a trigger frame here */ + msp = (unsigned char *)mv_chain; + op_oldvar(); + } + if ((char *)mv_chain > (char *)frame_pointer) + { + msp_save = msp; + msp = (unsigned char *)frame_pointer->l_symtab; + if (msp <= stackwarn) + { + if (msp <= stacktop) + { + msp = msp_save; + rts_error(VARLSTCNT(1) ERR_STACKOFLOW); + } else + rts_error(VARLSTCNT(1) ERR_STACKCRIT); + } + memset(msp, 0, size); + DBGEHND((stderr, "flush_jmp: Old msp: 0x"lvaddr" New msp: 0x"lvaddr"\n", msp_save, msp)); + return; + } + /* We kept one or more mv_stents for this frame. We may need to shift the stack to get room to create an l_symtab + * for this re-purposed frame. In the above loop, we stopped searching the mv_stent chain at the first mv_stent we + * knew we had to keep. Since we are moving things around anyway, see if there are any mv_stents associated + * with this frame which don't need to be kept and can reclaim. + */ + mv_st_ent = mv_chain; + mv_st_prev = (mv_stent *)((char *)mv_st_ent + mv_st_ent->mv_st_next); + top = (char *)mv_st_ent + mvs_size[mv_st_ent->mv_st_type]; + while ((char *)mv_st_prev < (char *)frame_pointer) + { + mv_st_type = mv_st_prev->mv_st_type; + assert(MVST_TRIGR != mv_st_type); /* Should never unwind a trigger frame here */ + if (!mvs_save[mv_st_type]) + { /* Don't need to keep this mv_stent. Remove it from the chain */ + DBGEHND((stderr, "flush_jmp: Removing no-save mv_stent addr 0x"lvaddr" and type %d\n", + mv_st_prev, mv_st_type)); + unw_mv_ent(mv_st_prev); + mv_st_ent->mv_st_next += mv_st_prev->mv_st_next; + mv_st_prev = (mv_stent *)((char *)mv_st_prev + mv_st_prev->mv_st_next); + continue; + } + /* We found a previous mv_stent we need to keep. If we had an interveening mv_stent we don't need to + * keep, migrate the new keeper mv_stent adjacent to the previous keeper. */ + if (mv_st_prev != (mv_stent *)top) + { + DBGEHND((stderr, "flush_jmp: Migrating keeper mv_stent from 0x"lvaddr" to 0x"lvaddr" type %d\n", + mv_st_prev, top, mv_st_type)); + if (MVST_TPHOLD == mv_st_type) + { /* If we are moving an MVST_TPHOLD mv_stent, find it in the tpstack and fix its + * address there too. Else we won't unwind to the correct place on a restart. */ + fix_tphold_mvc(top, (char *)mv_st_prev, ((char *)mv_st_prev + mvs_size[MVST_TPHOLD])); + } + memmove(top, mv_st_prev, mvs_size[mv_st_type]); + } + DBGEHND((stderr, "flush_jmp: Updating offsets for mv_stent at addr 0x"lvaddr" type %d\n", + mv_st_ent, mv_st_ent->mv_st_type)); + mv_st_ent->mv_st_next = mvs_size[mv_st_ent->mv_st_type]; + mv_st_ent = (mv_stent *)top; + mv_st_ent->mv_st_next += (unsigned int)((char *)mv_st_prev - top); + top += mvs_size[mv_st_ent->mv_st_type]; + mv_st_prev = (mv_stent *)((char *)mv_st_ent + mv_st_ent->mv_st_next); + } + shift = (int4)((char *)frame_pointer - top - size); + DBGEHND_ONLY(msp_save = msp); + if (shift) + { + if ((unsigned char *)mv_chain + shift <= stackwarn) + { + if ((unsigned char *)mv_chain + shift <= stacktop) + rts_error(VARLSTCNT(1) ERR_STACKOFLOW); + else + rts_error(VARLSTCNT(1) ERR_STACKCRIT); + } + DBGEHND((stderr, "flush_jmp: Shifting %d bytes of stack from 0x"lvaddr" to 0x"lvaddr" by %d bytes\n", + INTCAST(top - (char *)mv_chain), mv_chain, (mv_chain + shift), shift)); + /* Since we are moving one or more mv_stents, it is no more difficult to check the range against the + * tp_frame chain than it is to loop through the mv_stents checking each one since the tp_frame stack + * is usually no more than 1-3 deep. + */ + fix_tphold_mvc(((char *)mv_chain + shift), (char *)mv_chain, ((char *)mv_chain + (top - (char *)mv_chain))); + memmove((char *)mv_chain + shift, mv_chain, top - (char *)mv_chain); + mv_chain = (mv_stent *)((char *)mv_chain + shift); + mv_st_ent = (mv_stent *)((char *)mv_st_ent + shift); + mv_st_ent->mv_st_next -= shift; + msp = (unsigned char *)mv_chain; + } + memset(frame_pointer->l_symtab, 0, size); + DBGEHND((stderr, "flush_jmp: Old msp: 0x"lvaddr" New msp: 0x"lvaddr"\n", msp_save, msp)); + return; +} + +/* Routine to fix up the TPHOLD mv_stent address in the tp_stack when flush_jmp shifts the stack */ +STATICFNDEF void fix_tphold_mvc(char *target, char *srcstart, char *srcend) +{ + tp_frame *tf; + + DBGEHND((stderr, "fix_tphold_mvc: entered with target: 0x"lvaddr" srcstart: 0x"lvaddr" srcend: 0x"lvaddr"\n", + target, srcstart, srcend)); + for (tf = tp_pointer; ((NULL != tf) && ((char *)tf->fp > srcstart)); tf = tf->old_tp_frame) + { + if (((char *)tf->mvc >= srcstart) && ((char *)tf->mvc < srcend)) + { + DBGEHND((stderr, "fix_tphold_mvc: Modifying tp_frame mv_stent value from 0x"lvaddr" to 0x"lvaddr + " level %d\n", tf->mvc, ((char *)tf->mvc + (target - srcstart)), + tf->mvc->mv_st_cont.mvs_tp_holder.tphold_tlevel)); + tf->mvc = (mv_stent *)((char *)tf->mvc + (target - srcstart)); + } + } +} diff --git a/sr_port/flush_jmp.h b/sr_port/flush_jmp.h new file mode 100644 index 0000000..4f332dc --- /dev/null +++ b/sr_port/flush_jmp.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __FLUSH_JMP_H__ +#define __FLUSH_JMP_H__ + +void flush_jmp (rhdtyp *rtn_base, unsigned char *context, unsigned char *transfer_addr); + +#endif diff --git a/sr_port/flush_pio.c b/sr_port/flush_pio.c new file mode 100644 index 0000000..1a6dd6b --- /dev/null +++ b/sr_port/flush_pio.c @@ -0,0 +1,31 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" +#include "io_params.h" + +GBLREF io_pair io_std_device; +GBLREF bool prin_out_dev_failure; + +void flush_pio(void) +{ + /* there is no eol character in the flush prototype */ + /*unsigned char p;*/ + + if (io_std_device.out && !prin_out_dev_failure) /* Some utility pgms don't have devices to flush */ + { /* do not flush if we've encountered an error with io_std_device already so that we give user $ZT, or EXCEPTION + * a chance to execute */ + /*p = (unsigned char)iop_eol; + (io_std_device.out->disp_ptr->flush)(io_std_device.out, &p);*/ + (io_std_device.out->disp_ptr->flush)(io_std_device.out); + } +} diff --git a/sr_port/fnname.h b/sr_port/fnname.h new file mode 100644 index 0000000..e7a5aad --- /dev/null +++ b/sr_port/fnname.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#define FNLCL 0 +#define FNGBL 1 +#define FNEXTGBL1 2 +#define FNEXTGBL2 4 +#define FNVBAR 8 +#define FNNAKGBL 32 diff --git a/sr_port/fnorder.h b/sr_port/fnorder.h new file mode 100644 index 0000000..0298955 --- /dev/null +++ b/sr_port/fnorder.h @@ -0,0 +1,32 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef _FNORDER_H_INC_ +#define _FNORDER_H_INC_ + +enum order_obj { + global = 0, + local_name, + local_sub, + indir, + last_obj +}; + +enum order_dir { + forward = 0, + backward, + undecided, + last_dir +}; + +STATICFNDCL boolean_t set_opcode(triple *r, oprtype *result, oprtype *result_ptr, oprtype *second_opr, enum order_obj object); + +#endif diff --git a/sr_port/fnpc.h b/sr_port/fnpc.h new file mode 100644 index 0000000..fbc71b2 --- /dev/null +++ b/sr_port/fnpc.h @@ -0,0 +1,54 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef FNPC_INCLUDED +#define FNPC_INCLUDED + +/* Note, FNPC_MAX should never exceed 254 since the value 255 is used to flag "invalid entry" */ +#define FNPC_STRLEN_MIN 15 +#define FNPC_MAX 50 +#define FNPC_ELEM_MAX 80 + +/* The delimiter argument to op_fnp1, opfnzp1, op_setp1, and op_setzp1 is + passed as an integer but contains 1-4 chars (zero filled). The unicode + versions are interested in all of them but the non-unicode versions are + only interested in the first char. +*/ +typedef union +{ + int unichar_val; + unsigned char unibytes_val[4]; +} delimfmt; + +typedef struct fnpc_struct +{ + mstr last_str; /* The last string (addr/len) we used in cache */ + unsigned int *pcoffmax; /* Address of last element in pstart array */ + int delim; /* delimiter used in $[z]piece */ + int npcs; /* Number of pieces for which values are filled in */ + int indx; /* The index of this piece */ + boolean_t byte_oriented; /* True if byte oriented; False if (unicode) char oriented */ + unsigned int pstart[FNPC_ELEM_MAX + 1]; /* Where each piece starts (last elem holds end of last piece) */ +} fnpc; + +typedef struct +{ + fnpc *fnpcsteal; /* Last stolen cache element */ + fnpc *fnpcmax; /* (use addrs to avoid array indexing) */ + fnpc fnpcs[FNPC_MAX]; +} fnpc_area; + +#ifdef DEBUG +void fnpc_stats(void); +#endif + + +#endif diff --git a/sr_port/fnpc_stats.c b/sr_port/fnpc_stats.c new file mode 100644 index 0000000..9a3b9cc --- /dev/null +++ b/sr_port/fnpc_stats.c @@ -0,0 +1,56 @@ +/**************************************************************** + * * + * Copyright 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "fnpc.h" +#include "gtm_stdio.h" + +#ifdef DEBUG +GBLREF uint4 process_id; +GBLREF int c_miss; /* cache misses (debug) */ +GBLREF int c_hit; /* cache hits (debug) */ +GBLREF int c_small; /* scanned small string brute force */ +GBLREF int c_small_pcs; /* chars scanned by small scan */ +GBLREF int c_pskip; /* number of pieces "skipped" */ +GBLREF int c_pscan; /* number of pieces "scanned" */ +GBLREF int c_parscan; /* number of partial scans (partial cache hits) */ +GBLREF int cs_miss; /* cache misses (debug) */ +GBLREF int cs_hit; /* cache hits (debug) */ +GBLREF int cs_small; /* scanned small string brute force */ +GBLREF int cs_small_pcs; /* chars scanned by small scan */ +GBLREF int cs_pskip; /* number of pieces "skipped" */ +GBLREF int cs_pscan; /* number of pieces "scanned" */ +GBLREF int cs_parscan; /* number of partial scans (partial cache hits) */ +GBLREF int c_clear; /* cleared due to (possible) value change */ + +void fnpc_stats(void) +{ + FPRINTF(stderr, "process id: %d\n", process_id); + FPRINTF(stderr, "fnpc cache clears: %d\n", c_clear); + FPRINTF(stderr, "Reference Piece:\n"); + FPRINTF(stderr, " fnpc cache miss: %d\n", c_miss); + FPRINTF(stderr, " fnpc cache hit: %d\n", c_hit); + FPRINTF(stderr, " fnpc pieces skipped: %d\n", c_pskip); + FPRINTF(stderr, " fnpc pieces scanned: %d\n", c_pscan); + FPRINTF(stderr, " fnpc partial scans: %d\n", c_parscan); + FPRINTF(stderr, " small string scans: %d\n", c_small); + FPRINTF(stderr, " small str pcs scnd: %d\n", c_small_pcs); + FPRINTF(stderr, "Set Piece:\n"); + FPRINTF(stderr, " fnpc cache miss: %d\n", cs_miss); + FPRINTF(stderr, " fnpc cache hit: %d\n", cs_hit); + FPRINTF(stderr, " fnpc pieces skipped: %d\n", cs_pskip); + FPRINTF(stderr, " fnpc pieces scanned: %d\n", cs_pscan); + FPRINTF(stderr, " fnpc partial scans: %d\n", cs_parscan); + FPRINTF(stderr, " small string scans: %d\n", cs_small); + FPRINTF(stderr, " small str pcs scnd: %d\n", cs_small_pcs); +} +#endif diff --git a/sr_port/fntext_ch.c b/sr_port/fntext_ch.c new file mode 100644 index 0000000..3b01fd9 --- /dev/null +++ b/sr_port/fntext_ch.c @@ -0,0 +1,37 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "error.h" + +error_def(ERR_ASSERT); +error_def(ERR_GTMCHECK); +error_def(ERR_GTMASSERT); +error_def(ERR_MEMORY); +error_def(ERR_TPRETRY); +error_def(ERR_VMSMEMORY); +error_def(ERR_STACKOFLOW); +error_def(ERR_OUTOFSPACE); + +CONDITION_HANDLER(fntext_ch) +{ + START_CH; + if (!DUMPABLE && (SIGNAL != ERR_TPRETRY)) + { + UNWIND(NULL, NULL); /* As per the standard, $TEXT returns null string if there are errors while */ + /* loading/linking with the entryref. So, we ignore non-fatal errors. */ + } else + { + NEXTCH; /* But, we don't want to ignore fatal errors as these may be indicative of serious */ + /* issues that may need investigation. Also, TP restarts need to be handled properly. */ + } +} diff --git a/sr_port/follow.h b/sr_port/follow.h new file mode 100644 index 0000000..f25cdb5 --- /dev/null +++ b/sr_port/follow.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __FOLLOW_H__ +#define __FOLLOW_H__ + +int follow(mval *, mval *); + +#endif diff --git a/sr_port/format2zwr.c b/sr_port/format2zwr.c new file mode 100644 index 0000000..b81c975 --- /dev/null +++ b/sr_port/format2zwr.c @@ -0,0 +1,212 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#include "mdef.h" + +#include "gtm_string.h" + +#include "mlkdef.h" +#include "zshow.h" +#include "patcode.h" +#include "compiler.h" /* for CHARMAXARGS */ + +#ifdef UNICODE_SUPPORTED +#include "gtm_icu_api.h" /* U_ISPRINT() needs this header */ +#include "gtm_utf8.h" +#endif + +GBLREF uint4 *pattern_typemask; +GBLREF boolean_t gtm_utf8_mode; + +/* Routine to convert a string to ZWRITE format. Used by the utiltities. + * NOTE: this routine does almost the same formatting as mval_write(). The reason + * for not using mval_write() is because it is much more complex than we need + * here. Moreover, this version is more efficient due to the availability of + * pre-allocated destination buffer */ +int format2zwr(sm_uc_ptr_t src, int src_len, unsigned char *des, int *des_len) +{ + sm_uc_ptr_t cp; + uint4 ch; + int fastate = 0, ncommas, dstlen, chlen; + boolean_t isctl, isill; + uchar_ptr_t srctop, strnext, tmpptr; + + dstlen = *des_len = 0; + + if (src_len > 0) + { + srctop = src + src_len; + fastate = 0; + /* deals with the other characters */ + for (cp = src; cp < srctop; cp += chlen) + { + if (!gtm_utf8_mode) + { + ch = *cp; + isctl = ((pattern_typemask[ch] & PATM_C) != 0); + isill = FALSE; + chlen = 1; + } +#ifdef UNICODE_SUPPORTED + else { + strnext = UTF8_MBTOWC(cp, srctop, ch); + isill = (WEOF == ch) ? (ch = *cp, TRUE) : FALSE; + if (!isill) + isctl = !U_ISPRINT(ch); + chlen = (int)(strnext - cp); + } +#endif + switch(fastate) + { + case 0: /* beginning of the string */ + case 1: /* beginning of a new substring followed by a graphic character */ + if (isill) + { + if (dstlen > 0) + { + des[dstlen++] = '"'; + des[dstlen++] = '_'; + } + MEMCPY_LIT(des + dstlen, DOLLARZCH); + dstlen += STR_LIT_LEN(DOLLARZCH); + I2A(des, dstlen, ch); + fastate = 3; + ncommas = 0; + } else if (isctl) + { + if (dstlen > 0) + { /* close previous string with quote and prepare for concatenation */ + des[dstlen++] = '"'; + des[dstlen++] = '_'; + } + MEMCPY_LIT(des + dstlen, DOLLARCH); + dstlen += STR_LIT_LEN(DOLLARCH); + I2A(des, dstlen, ch); + fastate = 2; + ncommas = 0; + } else + { /* graphic characters */ + if (0 == fastate) /* the initial quote in the beginning */ + { + des[dstlen++] = '"'; + fastate = 1; + } + if ('"' == ch) + des[dstlen++] = '"'; + if (!gtm_utf8_mode) + des[dstlen++] = ch; + else { + memcpy(&des[dstlen], cp, chlen); + dstlen += chlen; + } + } + break; + case 2: /* subsequent characters following a non-graphic character in the + form of $CHAR(x,) */ + if (isill) + { + MEMCPY_LIT(des + dstlen, CLOSE_PAREN_DOLLARZCH); + dstlen += STR_LIT_LEN(CLOSE_PAREN_DOLLARZCH); + I2A(des, dstlen, ch); + fastate = 3; + } else if(isctl) + { + ncommas++; + if (CHARMAXARGS == ncommas) + { + ncommas = 0; + MEMCPY_LIT(des + dstlen, CLOSE_PAREN_DOLLARCH); + dstlen += STR_LIT_LEN(CLOSE_PAREN_DOLLARCH); + } else + { + MEMCPY_LIT(des + dstlen, COMMA); + dstlen += STR_LIT_LEN(COMMA); + } + I2A(des, dstlen, ch); + } else + { + MEMCPY_LIT(des + dstlen, CLOSE_PAREN_QUOTE); + dstlen += STR_LIT_LEN(CLOSE_PAREN_QUOTE); + if (!gtm_utf8_mode) + des[dstlen++] = ch; + else { + memcpy(&des[dstlen], cp, chlen); + dstlen += chlen; + } + if ('"' == ch) + des[dstlen++] = '"'; + fastate = 1; + } + break; + case 3: /* subsequent characters following an illegal character in the form of $ZCHAR(x,) */ + if(isill) + { + ncommas++; + if (CHARMAXARGS == ncommas) + { + ncommas = 0; + MEMCPY_LIT(des + dstlen, CLOSE_PAREN_DOLLARZCH); + dstlen += STR_LIT_LEN(CLOSE_PAREN_DOLLARZCH); + } else + { + MEMCPY_LIT(des + dstlen, COMMA); + ++dstlen; + } + I2A(des, dstlen, ch); + } else if (isctl) + { + MEMCPY_LIT(des + dstlen, CLOSE_PAREN_DOLLARCH); + dstlen += STR_LIT_LEN(CLOSE_PAREN_DOLLARCH); + I2A(des, dstlen, ch); + fastate = 2; + } else + { + MEMCPY_LIT(des + dstlen, CLOSE_PAREN_QUOTE); + dstlen += STR_LIT_LEN(CLOSE_PAREN_QUOTE); + if (!gtm_utf8_mode) + des[dstlen++] = ch; + else { + memcpy(&des[dstlen], cp, chlen); + dstlen += chlen; + } + if ('"' == ch) + des[dstlen++] = '"'; + fastate = 1; + } + break; + default: + assert(FALSE); + break; + } + } + + /* close up */ + switch(fastate) + { + case 1: + des[dstlen++] = '"'; + break; + case 2: + case 3: + des[dstlen++] = ')'; + break; + default: + assert(FALSE); + break; + } + } else + { + des[0] = des[1] = '"'; + dstlen = 2; + } + *des_len = dstlen; + return 0; +} + diff --git a/sr_port/format_key_lv_val.c b/sr_port/format_key_lv_val.c new file mode 100644 index 0000000..b05c8bb --- /dev/null +++ b/sr_port/format_key_lv_val.c @@ -0,0 +1,100 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* + *-------------------------------------------------------------------------- + * Descriptipn: + * Given a non-null lv_val *, this will format the subscripted local variable key + * starting from the base variable lv_val *. + * Input: + * lvpin: Pointer to the last subscript's lv_val + * buff : Buffer where key will be formatted + * size : Size of buff + * Return Value: + * End address upto which buffer was used to format the key + * (Needed for length calculation in caller) + *-------------------------------------------------------------------------- + */ + +#include "mdef.h" + +#include "lv_val.h" +#include "gtm_string.h" +#include "mvalconv.h" +#include "promodemo.h" /* for "demote" prototype used in LV_NODE_GET_KEY */ + +unsigned char *format_key_lv_val(lv_val *lvpin, unsigned char *buff, int size) +{ + boolean_t is_base_var; + int cnt, cntfmt; + lv_val *lv, *base_lv; + mval tempmv; + lvTree *lvt; + lvTreeNode *node, *nodep[MAX_LVSUBSCRIPTS]; + unsigned char *endbuff; + + if (NULL == lvpin) + return buff; + lv = lvpin; + is_base_var = LV_IS_BASE_VAR(lv); + base_lv = !is_base_var ? LV_GET_BASE_VAR(lv) : lv; + cntfmt = 0; + while (lv != base_lv) + { + assert(!LV_IS_BASE_VAR(lv)); + nodep[cntfmt++] = (lvTreeNode *)lv; + lvt = LV_GET_PARENT_TREE(lv); + assert(NULL != lvt); + assert(lvt->base_lv == base_lv); + lv = (lv_val *)LVT_PARENT(lvt); + assert(NULL != lv); + } + endbuff = format_lvname(base_lv, buff, size); + size -= (int)(endbuff - buff); + buff = endbuff; + if (cntfmt) + { + if (size < 1) + return buff; + *buff++ = '('; + size--; + } + for (cnt = cntfmt - 1; cnt >= 0; cnt--) + { + node = nodep[cnt]; + LV_NODE_GET_KEY(node, &tempmv); /* Get node key into "tempmv" depending on the structure type of "node" */ + MV_FORCE_STRD(&tempmv); + if (size < tempmv.str.len) + { /* copy as much space as we have */ + memcpy(buff, tempmv.str.addr, size); + buff += size; + return buff; + } + memcpy(buff, tempmv.str.addr, tempmv.str.len); + size -= tempmv.str.len; + buff += tempmv.str.len; + if (cnt) + { + if (size < 1) + return buff; + *buff++ = ','; + size--; + } + } + if (cntfmt) + { + if (size < 1) + return buff; + *buff++ = ')'; + size--; + } + return buff; +} diff --git a/sr_port/format_key_mvals.c b/sr_port/format_key_mvals.c new file mode 100644 index 0000000..8d2ab4c --- /dev/null +++ b/sr_port/format_key_mvals.c @@ -0,0 +1,85 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* + *-------------------------------------------------------------------------- + * Descriptipn: + * Given the lvname_info *, which contains all the mvals of all + * subscripts and the name itself, of a local variable node, + * this will format the entire local variable key + * Input: + * lvnp: Pointer to the structure of a local variable + * buff: Buffer where key will be formatted + * size: Size of buff + * Return Value: + * End address upto which buffer was used to format the key + * (Needed for the length calculation in caller) + *-------------------------------------------------------------------------- + */ + +#include "mdef.h" + +#include "gtm_string.h" +#include "lv_val.h" /* needed by "lv_nameinfo.h" */ + +unsigned char *format_key_mvals(unsigned char *buff, int size, lvname_info *lvnp) +{ + int cnt; + mval *keys; + int n, subcnt; + unsigned char *endbuff; + + cnt = (int)lvnp->total_lv_subs - 1; + endbuff = format_lvname(lvnp->start_lvp, buff, size); + size -= (int)(endbuff - buff); + buff = endbuff; + + if (cnt > 0 && size > 0) + { + *buff++ = '('; + size--; + subcnt = 0; + for (n = 0; ; ) + { + keys = lvnp->lv_subs[subcnt++]; + MV_FORCE_STR(keys); + if (size > (keys)->str.len) + { + memcpy(buff, (keys)->str.addr, (keys)->str.len); + buff += (keys)->str.len; + size -= (keys)->str.len; + } + else + { + /* copy as much space as we have */ + memcpy(buff, (keys)->str.addr, size); + buff += size; + break; + } + + if (++n < cnt && size > 0) + { + *buff++ = ','; + size--; + } + else + { + if (size > 0) + { + *buff++ = ')'; + size--; + } + break; + } + } + } + return buff; +} diff --git a/sr_port/format_lvname.c b/sr_port/format_lvname.c new file mode 100644 index 0000000..158411a --- /dev/null +++ b/sr_port/format_lvname.c @@ -0,0 +1,62 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* + *--------------------------------------------------------------------------------- + * Description: + * Given lv_val * of the local variable name, format the local variable name string + * + * Input Parameter: + * start: Pointer to the local variable name + * buff: Buffer where key will be formatted + * size: Size of buff + * + * Return Value: + * End address upto which buffer was used to format the key + * (Needed for the length calculation in caller) + *--------------------------------------------------------------------------------- + */ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "rtnhdr.h" +#include "stack_frame.h" +#include "lv_val.h" +#include "min_max.h" + +GBLREF stack_frame *frame_pointer; + +unsigned char *format_lvname(lv_val *startlv, unsigned char *buff, int size) +{ + int i, len; + ht_ent_mname **j; + mident *vent; + + if (!startlv) + return buff; + if ((startlv >= (lv_val *)frame_pointer->temps_ptr) + && (startlv <= (lv_val *)(frame_pointer->temps_ptr + frame_pointer->rvector->temp_size))) + return buff; + for (i = 0, j = frame_pointer->l_symtab; i < frame_pointer->vartab_len; i++, j++) + { + if (*j && (lv_val *)((*j)->value) == startlv) + break; + } + if (i >= frame_pointer->vartab_len) + return buff; + vent = &(((var_tabent *)frame_pointer->vartab_ptr)[i].var_name); + assert(vent->len <= MAX_MIDENT_LEN); + len = MIN(size, vent->len); + memcpy(buff, vent->addr, len); + return buff + len; +} diff --git a/sr_port/format_targ_key.c b/sr_port/format_targ_key.c new file mode 100644 index 0000000..24fa371 --- /dev/null +++ b/sr_port/format_targ_key.c @@ -0,0 +1,94 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "format_targ_key.h" +#include "gvsub2str.h" + +/* return a pointer that points after the last char added */ +unsigned char *format_targ_key(unsigned char *out_char_ptr, int4 max_size, gv_key *key, boolean_t dollarc) +{ + unsigned char *gvkey_char_ptr, *out_top, *work_char_ptr, work_buff[MAX_ZWR_KEY_SZ], *work_top; + boolean_t is_string; + DEBUG_ONLY(unsigned char *gvkey_top_ptr;) + + assert(12 < max_size); + out_top = out_char_ptr + max_size - 2; /* - 2, as could add comma left-paren or TWO double quotes between checks */ + gvkey_char_ptr = key->base; + DEBUG_ONLY(gvkey_top_ptr = gvkey_char_ptr + key->end;) + /* Ensure input key is well-formed (i.e. double null terminated) */ + assert(KEY_DELIMITER == *(gvkey_top_ptr - 1)); + assert(KEY_DELIMITER == *gvkey_top_ptr); + /* The following assert (in the for loop) assumes that a global name will be able to fit in completely into any key. + * But that is not true. For exmaple I can have a maxkeysize of 10 and try to set a global variable name of length 20. + * That will have issues below. Until C9J10-003204 is fixed to handle long global names and small maxkeysizes, we + * let the below code stay as it is (asserts only) to avoid overheads (of if checks for whether end is reached) in pro. + * When that is fixed, it is possible, we see the key terminate before even the global name is finished. In that case, + * we should return without '(' or ')' in the formatted buffer. The caller will know this is a case of too long global name. + */ + for (*out_char_ptr++ = '^'; (*out_char_ptr = *gvkey_char_ptr++); out_char_ptr++) + assert(gvkey_char_ptr <= gvkey_top_ptr); + assert(gvkey_char_ptr <= gvkey_top_ptr); + if (0 == *gvkey_char_ptr) /* no subscipts */ + return (out_char_ptr); + *out_char_ptr++ = '('; + for ( ; ; ) + { + assert(gvkey_char_ptr <= gvkey_top_ptr); + if (0x01 == *gvkey_char_ptr) /* this must be a null string which was adjusted by op_gvorder */ + { + *out_char_ptr++ = '"'; + *out_char_ptr++ = '"'; + } else + { + is_string = FALSE; + if ((STR_SUB_PREFIX == *gvkey_char_ptr) && !dollarc) + { + is_string = TRUE; + *out_char_ptr++ = '"'; + } + work_top = gvsub2str(gvkey_char_ptr, work_buff, dollarc); + for (work_char_ptr = work_buff; work_char_ptr < work_top;) + { + if (out_char_ptr >= out_top) + { + assert(FALSE); + return (NULL); + } + *out_char_ptr++ = *work_char_ptr++; + } + if (is_string) + *out_char_ptr++ = '"'; + } + if (out_char_ptr >= out_top) + { + assert(FALSE); + return (NULL); + } + for ( ; *gvkey_char_ptr++; ) + assert(gvkey_char_ptr <= gvkey_top_ptr); + assert(gvkey_char_ptr <= gvkey_top_ptr); + if (*gvkey_char_ptr) + *out_char_ptr++ = ','; + else + break; + } + *out_char_ptr++ = ')'; + assert(gvkey_char_ptr <= gvkey_top_ptr); + return (out_char_ptr); +} diff --git a/sr_port/format_targ_key.h b/sr_port/format_targ_key.h new file mode 100644 index 0000000..31698cf --- /dev/null +++ b/sr_port/format_targ_key.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef FORMAT_TARG_KEY_INCLUDED +#define FORMAT_TARG_KEY_INCLUDED + +unsigned char *format_targ_key(unsigned char *out_char_ptr, int4 max_size, gv_key *key, boolean_t dollarc); + +#endif /* FORMAT_TARG_KEY_INCLUDED */ diff --git a/sr_port/freecnt.mpt b/sr_port/freecnt.mpt new file mode 100644 index 0000000..ac80ea6 --- /dev/null +++ b/sr_port/freecnt.mpt @@ -0,0 +1,31 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 1989,2001 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +%FREECNT;GT.M %FREECNT utility - display database free blocks + ; + n rn,fn,fb,tb,%ZL + i '$d(%zdebug) n $et s $et="zg "_$zl_":ERR^%FREECNT" u $p:(ctrap=$c(3):exc="zg "_$zl_":EXIT^%FREECNT") + s rn=$view("GVFIRST") + d head,show + f s rn=$view("gvnext",rn) q:rn="" d show + d EXIT + q +head ; + w "Region",?16,"Free",?25,"Total",?40,"Database file",!,"------",?16,"----",?25,"-----",?40,"-------------",! + q +show ; + s fn=$v("GVFILE",rn),fb=$v("FREEBLOCKS",rn),tb=$v("TOTALBLOCKS",rn) + w rn,?12,$j(fb,8),?22,$j(tb,8)," (",$j(fb/tb*100.0,5,1),"%)",?40,fn,! + q +ERR w !,$p($zs,",",2,99),! + s $ec="" + ; Warning: Fall-through +EXIT u $p:(ctrap="":exc="") + q diff --git a/sr_port/fullbool.h b/sr_port/fullbool.h new file mode 100644 index 0000000..06f3a15 --- /dev/null +++ b/sr_port/fullbool.h @@ -0,0 +1,21 @@ +/**************************************************************** + * * + * Copyright 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#ifndef FULLBOOL_H_INCLUDED +#define FULLBOOL_H_INCLUDED + +enum gtm_bool_type +{ + GTM_BOOL = 0, + FULL_BOOL, + FULL_BOOL_WARN +}; + +#endif /* FULLBOOL_H_INCLUDED */ diff --git a/sr_port/funsvn.h b/sr_port/funsvn.h new file mode 100644 index 0000000..71e12a6 --- /dev/null +++ b/sr_port/funsvn.h @@ -0,0 +1,23 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + + +typedef struct { + opctype opcode; + bool can_set; + char os_syst; +} svn_data_type; + +typedef struct{ + opctype opcode; + char os_syst; +} fun_data_type; + diff --git a/sr_port/g.mpt b/sr_port/g.mpt new file mode 100644 index 0000000..94453b3 --- /dev/null +++ b/sr_port/g.mpt @@ -0,0 +1,64 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 1987, 2009 Fidelity Information Services, Inc ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +%G ;GT.M %G utility - global lister + ; + n %in,%ZL,%ZD + i '$d(%zdebug) n $et s $et="zg "_$zl_":ERR^%G" u $p:(ctrap=$c(3):exc="zg "_$zl_":LOOP^%G") + f d q:$l(%ZD) + . r !,"Output device: : ",%ZD,! + . i '$l(%ZD) s %ZD=$p q + . i %ZD="^" q + . i %ZD="?" d q + . . w !!,"Select the device you want for output" + . . w !,"If you wish to exit enter a carat (^)",! + . . s %ZD="" + . i $zparse(%ZD)="" w " no such device" s %ZD="" q + . o %ZD:(newversion:block=2048:record=2044:exception="g noopen"):0 + . i '$t w !,%ZD," is not available" s %ZD="" q + . q +noopen . w !,$p($ZS,",",2,999),! c %ZD s %ZD="" + q:%ZD="^" + d base + q +base f r !,"List ^",%in,! q:%in="" d + . i $e(%in)="?",$l(%in)=1 d help q + . i (%in="?D")!(%in="?d") d ^%GD u $p:(ctrap=$c(3):exc="zg "_($zl-2)_":LOOP^%G") q + . s:%in="*" %in="?.E(*)" + . s:$p(%in,"(")="*" $p(%in,"(")="?.E" + . s:$e(%in)'="^" %in="^"_%in + . n $et s $et="ZG "_$ZL_":badzwr" + . u %ZD zwr @%in u $p + . q +badzwr . u $p w !,$p($zs,",",3,99),! + . s $ec="" + d EXIT + q +help w !,"VALID INPUT",!! + w !,?3,"",?16,"to leave the %G utility ",! + w !,?4,"?D",?16,"to display existing globals in your directory ",! + w !,"[global name]",?16,"the MUMPS name for the global e.g. ABC, or" + w !?16,"a MUMPS pattern to match selected globals e.g. ?1""A"".E, or" + w !?16,"""*"" as a wildcard for all globals" + w !?16,"the global name may be followed by: " + w !?16,"subscript(s) in parentheses" + w !?16,"a subscript is a MUMPS expression e.g. ""joe"",10,$e(a,1)," + w !?16,"a ""*"" as a subscript causes all descendents to be included," + w !?16,"or by a range of subscripts in parentheses" + w !?16,"expressed as [expr]:[expr] e.g 1:10 ""a"":""d""",! + q +ERR u $p w !,$p($zs,",",2,99),! + s $ecode="" + ; Warning - Fall-through +EXIT i $d(%ZD),%ZD'=$p c %ZD + u $p:(ctrap="":exc="") + q +LOOP if 1'=$zeof d base + q diff --git a/sr_port/gbldef.mpt b/sr_port/gbldef.mpt new file mode 100644 index 0000000..5bde6a9 --- /dev/null +++ b/sr_port/gbldef.mpt @@ -0,0 +1,56 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001, 2004 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +%gbldef ; ; ;Global Collation Control + ; +kill(gname) + n $et + s $et="g error" + i '$$edit(.gname) q 0 + i "BGMM"'[$v("GVACCESS_METHOD",$v("REGION",gname)) zm 150376418:$v("REGION",gname); DBREMOTE + i $d(@gname) zm 150373626 ;Error if there is data in the global + s @gname="" k @gname ;make sure that the global is defined + s gname=$e(gname,2,32) ;remove circumflex, take at most 31 chars + v "YDIRTVAL":$e($v("YDIRTREE",gname),1,4),"YDIRTREE":gname + q 1 + ; +set(gname,nct,act) + n ver,$et + s $et="g error" + i '$$edit(.gname) q 0 + i "BGMM"'[$v("GVACCESS_METHOD",$v("REGION",gname)) zm 150376418:$v("REGION",gname); DBREMOTE + i $d(@gname) zm 150373626 ;Error if there is data in the global + s act=+$g(act),nct=+$g(nct) s:nct nct=1 + i (act>255)!(act<0) zm 150374290:act ; collation type specified is illegal + i act s ver=$V("YCOLLATE",act) + e s ver=0 + i ver<0 zm 150376282:act ; doesn't find coll type, or can't get version + s @gname="" k @gname ;make sure that the global is defined + s gname=$e(gname,2,32) ;remove circumflex, take at most 31 chars + v "YDIRTVAL":$e($v("YDIRTREE",gname),1,4)_$c(1,nct,act,ver),"YDIRTREE":gname + q 1 + ; +get(gname) + n t,tl,$et + s $et="g error" + i '$$edit(.gname) q 0 + i "BGMM"'[$v("GVACCESS_METHOD",$v("REGION",gname)) zm 150376418:$v("REGION",gname); DBREMOTE + s t=$e($v("YDIRTREE",$e(gname,2,32)),5,999),tl=$l(t) ;remove circumflex, take at most 31 chars + i tl,tl>4!($a(t,1)'=1) zm 150374058 + q $s(tl:$a(t,2)_","_$a(t,3)_","_$a(t,4),1:0) + ; +edit(gname) + i $e(gname)'="^" s gname="^"_gname + i $e(gname,2)'="%",$e(gname,2)'?1A zm 150373218 ; LKNAMEXPECTED + i gname'?1"^"1E.AN zm 150373218 + q 1 + ; +error s $ec="" + q 0 diff --git a/sr_port/gbldefs.c b/sr_port/gbldefs.c new file mode 100644 index 0000000..d184f21 --- /dev/null +++ b/sr_port/gbldefs.c @@ -0,0 +1,1119 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + + /* General repository for global variable definitions. This keeps us from + pulling in modules and all their references when all we wanted was the + global data def.. */ + +#include "mdef.h" + +#include "gtm_inet.h" +#include "gtm_iconv.h" +#include "gtm_socket.h" +#include "gtm_unistd.h" +#include "gtm_limits.h" + +#include +#include +#ifdef UNIX +# include +#endif +#ifdef VMS +# include /* Required for gtmsource.h */ +# include +# include +# include "desblk.h" +#endif +#include "cache.h" +#include "hashtab_addr.h" +#include "hashtab_int4.h" +#include "hashtab_int8.h" +#include "hashtab_mname.h" +#include "hashtab_str.h" +#include "hashtab_objcode.h" +/* The define of CHEXPAND below causes error.h to create GBLDEFs */ +#define CHEXPAND +#include "error.h" +#include "rtnhdr.h" +#include "gdsroot.h" +#include "gdskill.h" +#include "ccp.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gdscc.h" +#include "comline.h" +#include "compiler.h" +#include "cmd_qlf.h" +#include "io.h" +#include "iosp.h" +#include "jnl.h" +#include "lv_val.h" +#include "mdq.h" +#include "mprof.h" +#include "mv_stent.h" +#include "stack_frame.h" +#include "stp_parms.h" +#include "stringpool.h" +#include "buddy_list.h" /* needed for tp.h */ +#include "tp.h" +#include "tp_frame.h" +#include "mlkdef.h" +#include "zshow.h" +#include "zwrite.h" +#include "zbreak.h" +#include "mmseg.h" +#ifndef VMS +# include "gtmsiginfo.h" +#endif +#include "gtmimagename.h" +#include "iotcpdef.h" +#include "gt_timer.h" +#include "iosocketdef.h" /* needed for socket_pool and MAX_N_SOCKETS */ +#include "ctrlc_handler_dummy.h" +#include "unw_prof_frame_dummy.h" +#include "op.h" +#include "gtmsecshr.h" +#include "error_trap.h" +#include "patcode.h" /* for pat_everything and sizeof_pat_everything */ +#include "source_file.h" /* for REV_TIME_BUFF_LEN */ +#include "mupipbckup.h" +#include "dpgbldir.h" +#include "mmemory.h" +#include "have_crit.h" +#include "alias.h" + +/* FOR REPLICATION RELATED GLOBALS */ +#include "repl_msg.h" +#include "gtmsource.h" +#include "gtmrecv.h" + +/* FOR MERGE RELATED GLOBALS */ +#include "gvname_info.h" +#include "op_merge.h" + +#ifdef UNIX +#include "cli.h" +#include "invocation_mode.h" +#include "fgncal.h" +#include "parse_file.h" /* for MAX_FBUFF */ +#include "repl_sem.h" +#include "gtm_zlib.h" +#endif + +#include "jnl_typedef.h" + +#ifdef VMS +#include "gtm_logicals.h" /* for GTM_MEMORY_NOACCESS_COUNT */ +#endif + +#include "gds_blk_upgrade.h" /* for UPGRADE_IF_NEEDED flag */ +#include "cws_insert.h" /* for CWS_REORG_ARRAYSIZE */ + +#ifdef UNICODE_SUPPORTED +#include "gtm_icu_api.h" +#include "gtm_utf8.h" +#endif + +# ifdef GTM_CRYPT +# include "gtmcrypt.h" +# include "gdsblk.h" +# include "muextr.h" +# endif + +#ifdef GTM_TRIGGER +#include "gv_trigger.h" +#include "gtm_trigger.h" +#endif + +#define DEFAULT_ZERROR_STR "Unprocessed $ZERROR, see $ZSTATUS" +#define DEFAULT_ZERROR_LEN (SIZEOF(DEFAULT_ZERROR_STR) - 1) + +GBLDEF gd_region *db_init_region; +GBLDEF sgmnt_data_ptr_t cs_data; +GBLDEF sgmnt_addrs *cs_addrs; +GBLDEF sgmnt_addrs *cs_addrs_list; /* linked list of csa corresponding to all currently open databases */ + +GBLDEF unsigned short proc_act_type; +GBLDEF volatile bool ctrlc_pending; +GBLDEF volatile int4 ctrap_action_is; +GBLDEF bool out_of_time; +GBLDEF io_pair io_curr_device; /* current device */ +GBLDEF io_pair io_std_device; /* standard device */ +GBLDEF io_log_name *dollar_principal; /* pointer to log name GTM$PRINCIPAL if defined */ +GBLDEF bool prin_in_dev_failure = FALSE; +GBLDEF bool prin_out_dev_failure = FALSE; +GBLDEF io_desc *active_device; + +GBLDEF bool error_mupip = FALSE, + file_backed_up = FALSE, + gv_replopen_error = FALSE, + gv_replication_error = FALSE, + incremental = FALSE, + jobpid = FALSE, + online = FALSE, + record = FALSE, + std_dev_outbnd = FALSE, + in_mupip_freeze = FALSE, + in_backup = FALSE, + view_debug1 = FALSE, + view_debug2 = FALSE, + view_debug3 = FALSE, + view_debug4 = FALSE, + mupip_error_occurred, + dec_nofac; + +GBLDEF boolean_t is_updproc = FALSE, + is_updhelper = FALSE, + mupip_jnl_recover = FALSE, + repl_allowed = FALSE, + suspend_lvgcol = FALSE, + run_time = FALSE, + unhandled_stale_timer_pop = FALSE, + gtcm_connection = FALSE, + is_replicator = FALSE, /* TRUE => this process can write jnl records to the jnlpool for replicated db */ + tp_in_use = FALSE, /* TRUE => TP has been used by this process and is thus initialized */ + dollar_truth = TRUE, + have_standalone_access = FALSE, + gtm_stdxkill = FALSE; /* TRUE => Use M Standard X-KILL - FALSE use historical GTM X-KILL (default) */ + +GBLDEF VSIG_ATOMIC_T forced_exit = FALSE; /* Asynchronous signal/interrupt handler sets this variable to TRUE, + * hence the VSIG_ATOMIC_T type in the definition. + */ +GBLDEF intrpt_state_t intrpt_ok_state = INTRPT_OK_TO_INTERRUPT; /* any other value implies it is not ok to interrupt */ + +GBLDEF unsigned char *msp, + *mubbuf, + *restart_ctxt, + *stackbase, + *stacktop, + *stackwarn, + *restart_pc; +GBLDEF int4 backup_close_errno, + backup_write_errno, + mubmaxblk, + forced_exit_err, + exit_state, + restore_read_errno; +GBLDEF volatile int4 outofband, crit_count = 0; +GBLDEF int mumps_status = SS_NORMAL, + stp_array_size = 0; +GBLDEF gvzwrite_datablk *gvzwrite_block; +GBLDEF lvzwrite_datablk *lvzwrite_block; +GBLDEF io_log_name *io_root_log_name; +GBLDEF mliteral literal_chain; +GBLDEF mstr *comline_base, + *err_act, + **stp_array, + extnam_str, + env_gtm_env_xlate; +GBLDEF MSTR_CONST(default_sysid, "gtm_sysid"); +GBLDEF mval dollar_zgbldir, + dollar_zsource = DEFINE_MVAL_STRING(MV_STR, 0, 0, 0, NULL, 0, 0), + dollar_zstatus, + dollar_zstep = DEFINE_MVAL_STRING(MV_STR | MV_NM | MV_INT | MV_NUM_APPROX, 0, 0, 1, "B", 0, 0), + dollar_ztrap, + ztrap_pop2level = DEFINE_MVAL_STRING(MV_NM | MV_INT, 0, 0, 0, 0, 0, 0), + zstep_action, + dollar_system, + dollar_estack_delta = DEFINE_MVAL_STRING(MV_STR, 0, 0, 0, NULL, 0, 0), + dollar_etrap, + dollar_zerror = DEFINE_MVAL_STRING(MV_STR, 0, 0, DEFAULT_ZERROR_LEN, DEFAULT_ZERROR_STR, 0, 0), + dollar_zyerror, + dollar_ztexit = DEFINE_MVAL_STRING(MV_STR, 0, 0, 0, NULL, 0, 0); +GBLDEF uint4 dollar_zjob; +GBLDEF mval dollar_zinterrupt; +GBLDEF boolean_t dollar_zininterrupt; +GBLDEF boolean_t dollar_ztexit_bool; /* Truth value of dollar_ztexit when coerced to boolean */ +GBLDEF boolean_t dollar_zquit_anyway; + +GBLDEF mv_stent *mv_chain; +GBLDEF sgm_info *first_sgm_info; /* List of participating regions in the TP transaction with NO ftok ordering */ +GBLDEF sgm_info *first_tp_si_by_ftok; /* List of participating regions in the TP transaction sorted on ftok order */ +GBLDEF spdesc indr_stringpool, + rts_stringpool, + stringpool; +GBLDEF stack_frame *frame_pointer; +GBLDEF stack_frame *zyerr_frame = NULL; +GBLDEF symval *curr_symval; +GBLDEF tp_frame *tp_pointer; +GBLDEF tp_region *halt_ptr, + *grlist; +GBLDEF trans_num local_tn; /* transaction number for THIS PROCESS (starts at 0 each time) */ +GBLDEF trans_num tstart_local_tn; /* copy of global variable "local_tn" at op_tstart time */ +GBLDEF gv_namehead *gv_target; +GBLDEF gv_namehead *gv_target_list; /* List of ALL gvts that were allocated (in targ_alloc) by this process */ +GBLDEF gv_namehead *gvt_tp_list; /* List of gvts that were referenced in the current TP transaction */ +GBLDEF gvt_container *gvt_pending_list; /* list of gvts that need to be re-examined/re-allocated when region is opened */ +GBLDEF buddy_list *gvt_pending_buddy_list;/* buddy_list for maintaining memory for gv_targets to be re-examined/allocated */ + +GBLDEF int4 exi_condition; +GBLDEF uint4 gtmDebugLevel; +GBLDEF caddr_t smCallerId; /* Caller of top level malloc/free */ +GBLDEF int process_exiting; +GBLDEF int4 dollar_zsystem; +GBLDEF int4 dollar_zeditor; +GBLDEF boolean_t sem_incremented = FALSE; +GBLDEF boolean_t new_dbinit_ipc = FALSE; +GBLDEF mval **ind_result_array, **ind_result_sp, **ind_result_top; +GBLDEF mval **ind_source_array, **ind_source_sp, **ind_source_top; +GBLDEF rtn_tabent *rtn_fst_table, *rtn_names, *rtn_names_top, *rtn_names_end; +GBLDEF int4 break_message_mask; +GBLDEF bool rc_locked = FALSE; +GBLDEF boolean_t certify_all_blocks = FALSE; /* If flag is set all blocks are checked after they are + * written to the database. Upon error we stay critical + * and report. This flag can be set via the MUMPS command + * VIEW "GDSCERT":1. */ +GBLDEF mval curr_gbl_root; +GBLDEF gd_addr *original_header; +GBLDEF hash_table_str *complits_hashtab = NULL; +GBLDEF hash_table_str *compsyms_hashtab = NULL; +GBLDEF mem_list *mem_list_head; +GBLDEF boolean_t debug_mupip; +GBLDEF unsigned char t_fail_hist[CDB_MAX_TRIES]; /* type has to be unsigned char and not enum cdb_sc to ensure single byte */ +GBLDEF cache_rec_ptr_t cr_array[((MAX_BT_DEPTH * 2) - 1) * 2]; /* Maximum number of blocks that can be in transaction */ +GBLDEF unsigned int cr_array_index; +GBLDEF boolean_t need_core; /* Core file should be created */ +GBLDEF boolean_t created_core; /* core file was created */ +GBLDEF boolean_t core_in_progress; /* creating core NOW */ +GBLDEF boolean_t dont_want_core; /* Higher level flag overrides need_core set by lower level rtns */ +GBLDEF boolean_t exit_handler_active; /* recursion prevention */ +GBLDEF boolean_t block_saved; +#if defined(KEEP_zOS_EBCDIC) || defined(VMS) +GBLDEF iconv_t dse_over_cvtcd = (iconv_t)0; +#endif +GBLDEF gtm_chset_t dse_over_chset = CHSET_M; +GBLDEF char window_token; +GBLDEF mval window_mval; +GBLDEF char director_token; +GBLDEF mval director_mval; +LITDEF MIDENT_DEF(zero_ident, 0, NULL); /* the null mident */ +static char ident_buff1[SIZEOF(mident_fixed)]; +static char ident_buff2[SIZEOF(mident_fixed)]; +GBLDEF MIDENT_DEF(window_ident, 0, &ident_buff1[0]); /* the current identifier */ +GBLDEF MIDENT_DEF(director_ident, 0, &ident_buff2[0]); /* the look-ahead identifier */ +GBLDEF char *lexical_ptr; +GBLDEF int4 aligned_source_buffer[MAX_SRCLINE / SIZEOF(int4) + 1]; +GBLDEF unsigned char *source_buffer = (unsigned char *)aligned_source_buffer; +GBLDEF src_line_struct src_head; +GBLDEF short int source_column, source_line; +GBLDEF bool devctlexp; +GBLDEF char cg_phase; /* code generation phase */ +/* Previous code generation phase: Only used by emit_code.c to initialize the push list at the + * beginning of each phase (bug fix: C9D12-002478) */ +GBLDEF char cg_phase_last; + +GBLDEF int cmd_cnt; + +GBLDEF command_qualifier glb_cmd_qlf = { CQ_DEFAULT }, + cmd_qlf = { CQ_DEFAULT }; +#ifdef __osf__ +#pragma pointer_size (save) +#pragma pointer_size (long) +#endif +GBLDEF char **cmd_arg; +#ifdef __osf__ +#pragma pointer_size (restore) +#endif + +#ifdef UNIX +GBLDEF volatile uint4 heartbeat_counter = 0; +#endif + +/* DEFERRED EVENTS */ +GBLDEF int dollar_zmaxtptime = 0; +GBLDEF bool licensed = TRUE; + +#if defined(UNIX) +GBLDEF volatile int4 num_deferred; +#elif defined(VMS) +GBLDEF volatile short num_deferred; +GBLDEF int4 lkid, lid; +GBLDEF desblk exi_blk; +GBLDEF struct chf$signal_array *tp_restart_fail_sig; +GBLDEF boolean_t tp_restart_fail_sig_used; +#else +# error "Unsupported Platform" +#endif + +GBLDEF volatile int4 fast_lock_count = 0; /* Used in wcs_stale */ + +/* REPLICATION RELATED GLOBALS */ +GBLDEF gtmsource_options_t gtmsource_options; +GBLDEF gtmrecv_options_t gtmrecv_options; + +GBLDEF boolean_t is_tracing_on; +GBLDEF void (*tp_timeout_start_timer_ptr)(int4 tmout_sec) = tp_start_timer_dummy; +GBLDEF void (*tp_timeout_clear_ptr)(void) = tp_clear_timeout_dummy; +GBLDEF void (*tp_timeout_action_ptr)(void) = tp_timeout_action_dummy; +GBLDEF void (*ctrlc_handler_ptr)() = ctrlc_handler_dummy; +GBLDEF int (*op_open_ptr)(mval *v, mval *p, int t, mval *mspace) = op_open_dummy; +GBLDEF void (*unw_prof_frame_ptr)(void) = unw_prof_frame_dummy; +GBLDEF boolean_t mu_reorg_process = FALSE; /* set to TRUE by MUPIP REORG */ +GBLDEF boolean_t mu_reorg_in_swap_blk = FALSE; /* set to TRUE for the duration of the call to "mu_swap_blk" */ +GBLDEF boolean_t mu_rndwn_process = FALSE; +GBLDEF gv_key *gv_currkey_next_reorg; +GBLDEF gv_namehead *reorg_gv_target; + +#ifdef UNIX +GBLDEF struct sockaddr_un gtmsecshr_sock_name; +GBLDEF struct sockaddr_un gtmsecshr_cli_sock_name; +GBLDEF key_t gtmsecshr_key; +#endif +GBLDEF int gtmsecshr_sockpath_len; +GBLDEF int gtmsecshr_cli_sockpath_len; +GBLDEF mstr gtmsecshr_pathname; +GBLDEF int server_start_tries; +GBLDEF int gtmsecshr_log_file; +GBLDEF int gtmsecshr_sockfd = FD_INVALID; +GBLDEF boolean_t gtmsecshr_sock_init_done = FALSE; +GBLDEF char muext_code[MUEXT_MAX_TYPES][2] = +{ +# define MUEXT_TABLE_ENTRY(muext_rectype, code0, code1) {code0, code1}, +# include "muext_rec_table.h" +# undef MUEXT_TABLE_ENTRY +}; +GBLDEF int patch_is_fdmp; +GBLDEF int patch_fdmp_recs; +GBLDEF boolean_t horiz_growth = FALSE; +GBLDEF int4 prev_first_off, prev_next_off; + /* these two globals store the values of first_off and next_off in cse, + * when there is a blk split at index level. This is to permit rollback + * to intermediate states */ +GBLDEF sm_uc_ptr_t min_mmseg; +GBLDEF sm_uc_ptr_t max_mmseg; +GBLDEF mmseg *mmseg_head; +GBLDEF ua_list *first_ua, *curr_ua; +GBLDEF char *update_array, *update_array_ptr; +GBLDEF int gv_fillfactor = 100, + rc_set_fragment; /* Contains offset within data at which data fragment starts */ +GBLDEF uint4 update_array_size = 0, + cumul_update_array_size = 0; /* the current total size of the update array */ +GBLDEF kill_set *kill_set_tail; +GBLDEF boolean_t pool_init = FALSE; +GBLDEF boolean_t is_src_server = FALSE; +GBLDEF boolean_t is_rcvr_server = FALSE; +GBLDEF jnl_format_buffer *non_tp_jfb_ptr = NULL; +GBLDEF unsigned char *non_tp_jfb_buff_ptr; +GBLDEF boolean_t dse_running = FALSE; +GBLDEF jnlpool_addrs jnlpool; +GBLDEF jnlpool_ctl_ptr_t jnlpool_ctl; +GBLDEF jnlpool_ctl_struct temp_jnlpool_ctl_struct; +GBLDEF jnlpool_ctl_ptr_t temp_jnlpool_ctl = &temp_jnlpool_ctl_struct; +GBLDEF sm_uc_ptr_t jnldata_base; +GBLDEF int4 jnlpool_shmid = INVALID_SHMID; +GBLDEF recvpool_addrs recvpool; +GBLDEF int recvpool_shmid = INVALID_SHMID; +GBLDEF int gtmsource_srv_count = 0; +GBLDEF int gtmrecv_srv_count = 0; + +/* The following _in_prog counters are needed to prevent deadlocks while doing jnl-qio (timer & non-timer). */ +GBLDEF volatile int4 db_fsync_in_prog; +GBLDEF volatile int4 jnl_qio_in_prog; +#ifdef UNIX +GBLDEF void (*op_write_ptr)(mval *); +GBLDEF void (*op_wteol_ptr)(int4 n); +GBLDEF gtmsiginfo_t signal_info; +#ifndef MUTEX_MSEM_WAKE +GBLDEF int mutex_sock_fd = FD_INVALID; +GBLDEF struct sockaddr_un mutex_sock_address; +GBLDEF struct sockaddr_un mutex_wake_this_proc; +GBLDEF int mutex_wake_this_proc_len; +GBLDEF int mutex_wake_this_proc_prefix_len; +GBLDEF fd_set mutex_wait_on_descs; +#endif +#endif +GBLDEF void (*call_on_signal)(); +GBLDEF enum gtmImageTypes image_type; /* initialized at startup i.e. in dse.c, lke.c, gtm.c, mupip.c, gtmsecshr.c etc. */ + +#ifdef UNIX +GBLDEF parmblk_struct *param_list; /* call-in parameters block (defined in unix/fgncalsp.h)*/ +GBLDEF unsigned int invocation_mode = MUMPS_COMPILE; /* how mumps has been invoked */ +GBLDEF char cli_err_str[MAX_CLI_ERR_STR] = ""; /* Parse Error message buffer */ +GBLDEF char *cli_err_str_ptr = NULL; +GBLDEF io_desc *gtm_err_dev = NULL; +GBLDEF boolean_t gtm_pipe_child = FALSE; +#endif + +/* this array is indexed by file descriptor */ +GBLDEF boolean_t *lseekIoInProgress_flags = (boolean_t *)0; + +#if defined(UNIX) +/* Latch variable for Unix implementations. Used in SUN and HP */ +GBLDEF global_latch_t defer_latch; +#endif + +GBLDEF int num_additional_processors; +GBLDEF int gtm_errno = -1; /* holds the errno (unix) in case of an rts_error */ +GBLDEF int4 error_condition = 0; +GBLDEF global_tlvl_info *global_tlvl_info_head; +GBLDEF buddy_list *global_tlvl_info_list; +GBLDEF boolean_t job_try_again; +GBLDEF volatile int4 gtmMallocDepth; /* Recursion indicator */ +GBLDEF d_socket_struct *socket_pool; +GBLDEF boolean_t mu_star_specified; +GBLDEF backup_reg_list *mu_repl_inst_reg_list; + +#ifndef VMS +GBLDEF volatile int suspend_status = NO_SUSPEND; +#endif + +GBLDEF gv_namehead *reset_gv_target = INVALID_GV_TARGET; +GBLDEF VSIG_ATOMIC_T util_interrupt = 0; +GBLDEF sgmnt_addrs *kip_csa; +GBLDEF boolean_t need_kip_incr; +GBLDEF int merge_args = 0; +GBLDEF merge_glvn_ptr mglvnp = NULL; +GBLDEF int ztrap_form; +GBLDEF boolean_t ztrap_new; +GBLDEF int4 wtfini_in_prog; +/* items for $piece stats */ +#ifdef DEBUG +GBLDEF int c_miss; /* cache misses (debug) */ +GBLDEF int c_hit; /* cache hits (debug) */ +GBLDEF int c_small; /* scanned small string brute force */ +GBLDEF int c_small_pcs; /* chars scanned by small scan */ +GBLDEF int c_pskip; /* number of pieces "skipped" */ +GBLDEF int c_pscan; /* number of pieces "scanned" */ +GBLDEF int c_parscan; /* number of partial scans (partial cache hits) */ +GBLDEF int cs_miss; /* cache misses (debug) */ +GBLDEF int cs_hit; /* cache hits (debug) */ +GBLDEF int cs_small; /* scanned small string brute force */ +GBLDEF int cs_small_pcs; /* chars scanned by small scan */ +GBLDEF int cs_pskip; /* number of pieces "skipped" */ +GBLDEF int cs_pscan; /* number of pieces "scanned" */ +GBLDEF int cs_parscan; /* number of partial scans (partial cache hits) */ +GBLDEF int c_clear; /* cleared due to (possible) value change */ +GBLDEF boolean_t setp_work; +#endif +GBLDEF z_records zbrk_recs = {NULL, NULL, NULL}; + +#ifdef UNIX +GBLDEF ipcs_mesg db_ipcs; /* For requesting gtmsecshr to update ipc fields */ +GBLDEF gd_region *ftok_sem_reg = NULL; /* Last region for which ftok semaphore is grabbed */ +GBLDEF gd_region *standalone_reg = NULL; /* We have standalone access for this region */ +GBLDEF int gtm_non_blocked_write_retries; /* number of retries for non-blocked write to pipe */ +#endif + +#ifdef VMS +/* Following global variables store the state of an erroring sys$qio just before a GTMASSERT in the CHECK_CHANNEL_STATUS macro */ +GBLDEF uint4 check_channel_status = 0; /* stores the qio return status */ +GBLDEF uint4 check_channel_id = 0; /* stores the qio channel id */ +#endif + +GBLDEF boolean_t write_after_image = FALSE; /* true for after-image jnlrecord writing by recover/rollback */ +GBLDEF int iott_write_error; +GBLDEF int4 write_filter; +GBLDEF boolean_t need_no_standalone = FALSE; + +GBLDEF int4 zdir_form = ZDIR_FORM_FULLPATH; /* $ZDIR shows full path including DEVICE and DIRECTORY */ +GBLDEF mval dollar_zdir = DEFINE_MVAL_STRING(MV_STR, 0, 0, 0, NULL, 0, 0); + +GBLDEF int * volatile var_on_cstack_ptr = NULL; /* volatile pointer to int; volatile so that nothing gets optimized out */ +GBLDEF hash_table_int4 cw_stagnate; +GBLDEF boolean_t cw_stagnate_reinitialized = FALSE; + +GBLDEF uint4 pat_everything[] = { 0, 2, PATM_E, 1, 0, PAT_MAX_REPEAT, 0, PAT_MAX_REPEAT, 1 }; /* pattern = ".e" */ +GBLDEF mstr_len_t sizeof_pat_everything = SIZEOF(pat_everything); + +GBLDEF uint4 *pattern_typemask; +GBLDEF pattern *pattern_list; +GBLDEF pattern *curr_pattern; + +/* Unicode related data */ +GBLDEF boolean_t gtm_utf8_mode; /* Is GT.M running with Unicode Character Set; Set only after ICU initialization */ +GBLDEF boolean_t is_gtm_chset_utf8; /* Is gtm_chset environment variable set to UTF8 */ +GBLDEF boolean_t utf8_patnumeric; /* Should patcode N match non-ASCII numbers in pattern match ? */ +GBLDEF boolean_t badchar_inhibit = FALSE;/* Suppress malformed UTF-8 characters by default */ +GBLDEF MSTR_DEF(dollar_zchset, 1, "M"); +GBLDEF MSTR_DEF(dollar_zpatnumeric, 1, "M"); + +/* Standard MUMPS pattern-match table. + * This table holds the current pattern-matching attributes of each ASCII character. + * Bits 0..23 of each entry correspond with the pattern-match characters, A..X. + */ +GBLDEF pattern mumps_pattern = { + (void *) 0, /* flink */ + (void *) 0, /* typemask */ + (void *) 0, /* pat YZ name array */ + (void *) 0, /* pat YZ name-length array */ + -1, /* number of YZ patcodes */ + 1, /* namlen */ + {'M', '\0'} /* name */ +}; + +/* mapbit is used by pattab.c and patstr.c. Note that patstr.c uses only entries until PATM_X */ +GBLDEF readonly uint4 mapbit[] = +{ + PATM_A, PATM_B, PATM_C, PATM_D, PATM_E, PATM_F, PATM_G, PATM_H, + PATM_I, PATM_J, PATM_K, PATM_L, PATM_M, PATM_N, PATM_O, PATM_P, + PATM_Q, PATM_R, PATM_S, PATM_T, PATM_U, PATM_V, PATM_W, PATM_X, + PATM_YZ1, PATM_YZ2, PATM_YZ3, PATM_YZ4 +}; + +LITDEF uint4 typemask[PATENTS] = +{ + PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, /* hex 00-07 : ASCII characters */ + PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, /* hex 08-0F : ASCII characters */ + PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, /* hex 10-17 : ASCII characters */ + PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, /* hex 18-1F : ASCII characters */ + PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, /* hex 20-27 : ASCII characters */ + PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, /* hex 28-2F : ASCII characters */ + PATM_N, PATM_N, PATM_N, PATM_N, PATM_N, PATM_N, PATM_N, PATM_N, /* hex 30-37 : ASCII characters */ + PATM_N, PATM_N, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, /* hex 38-3F : ASCII characters */ + PATM_P, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, /* hex 40-47 : ASCII characters */ + PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, /* hex 48-4F : ASCII characters */ + PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, /* hex 50-57 : ASCII characters */ + PATM_U, PATM_U, PATM_U, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, /* hex 58-5F : ASCII characters */ + PATM_P, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, /* hex 60-67 : ASCII characters */ + PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, /* hex 68-6F : ASCII characters */ + PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, /* hex 70-77 : ASCII characters */ + PATM_L, PATM_L, PATM_L, PATM_P, PATM_P, PATM_P, PATM_P, PATM_C, /* hex 78-7F : ASCII characters */ + PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, /* hex 80-87 : non-ASCII characters */ + PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, /* hex 88-8F : non-ASCII characters */ + PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, /* hex 90-97 : non-ASCII characters */ + PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, PATM_C, /* hex 98-9F : non-ASCII characters */ + PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, /* hex A0-A7 : non-ASCII characters */ + PATM_P, PATM_P, PATM_L, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, /* hex A8-AF : non-ASCII characters */ + PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, /* hex B0-B7 : non-ASCII characters */ + PATM_P, PATM_P, PATM_L, PATM_P, PATM_P, PATM_P, PATM_P, PATM_P, /* hex B8-BF : non-ASCII characters */ + PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, /* hex C0-C7 : non-ASCII characters */ + PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, /* hex C8-CF : non-ASCII characters */ + PATM_P, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, /* hex D0-D7 : non-ASCII characters */ + PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_U, PATM_P, PATM_L, /* hex D8-DF : non-ASCII characters */ + PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, /* hex E0-E7 : non-ASCII characters */ + PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, /* hex E8-EF : non-ASCII characters */ + PATM_P, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, /* hex F0-F7 : non-ASCII characters */ + PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_L, PATM_P, PATM_C /* hex F8-FF : non-ASCII characters */ +}; + +GBLDEF uint4 pat_allmaskbits; /* universal set of valid pattern bit codes for currently active pattern table */ + +/* globals related to caching of pattern evaluation match result. + * for a given tuple, we store the evaluation result. + * in the above, + * uniquely identifies a substring of the input string. + * identifies the pattern atom that we are matching with + * identifies the recursion depth of do_patalt() for this pattern atom ("repcnt" in code) + * note that is a necessity in the above because the same alternation pattern atom can have different + * match or not-match status for the same input string depending on the repetition count usage of the pattern atom + * after a series of thoughts on an efficient structure for storing pattern evaluation, finally arrived at a simple + * array of structures wherein for a given length (strlen) we have a fixed number of structures available. + * we allocate an array of structures, say, 1024 structures. + * this is a simple 1-1 mapping, wherein + * for length 0, the available structures are the first 32 structures of the array, + * for length 1, the available structures are the second 32 structures of the array. + * ... + * for length 47, the available structures are the 47th 32 structures of the array. + * for length 48 and above, the available structures are all the remaining structures of the array. + * whenever any new entry needs to be cached and there is no room among the available structures, we preempt the + * most unfrequently used cache entry (to do this we do keep a count of every entry's frequency of usage) + * the assumption is that substrings of length > 48 (an arbitrary reasonable small number) won't be used + * so frequently so that they have lesser entries to fight for among themselves than lower values of length. + * with the above caching in place, the program segment below took 15 seconds. + * it was found that if the array size is increased to 16384 (as opposed to 1024 as above) and the available + * structures for each length increased proportionally (i.e. 16 times = 16*32 structures instead of 32 as above) + * the performance improved to the extent of taking 3 seconds. + * but this raised an interesting question, that of "size" vs. "time" tradeoff. + * with increasing array size, we get better "time" performance due to better caching. + * but that has an overhead of increased "size" (memory) usage. + * to arrive at a compromise, a dynamic algorithm emerged. the process will allocate a small array + * beginning at 1024 entries and grow to a max of 16384 entries as and when it deems the hit ratio is not good. + * the array only grows, i.e. there is no downsizing algorithm at play. + * the dynamic algorithm addresses to an extent both the "size" and "time" issues and finishes the below in 1 second. + * #defines for the dynamic algorithm growth can be found in patcode.h + */ +GBLDEF int4 curalt_depth = -1; /* depth of alternation nesting */ +GBLDEF int4 do_patalt_calls[PTE_MAX_CURALT_DEPTH]; /* number of calls to do_patalt() */ +GBLDEF int4 do_patalt_hits[PTE_MAX_CURALT_DEPTH]; /* number of pte_csh hits in do_patalt() */ +GBLDEF int4 do_patalt_maxed_out[PTE_MAX_CURALT_DEPTH]; /* no. of pte_csh misses after maxing on allocation size */ + +GBLDEF pte_csh *pte_csh_array[PTE_MAX_CURALT_DEPTH]; /* pte_csh array (per curalt_depth) */ +GBLDEF int4 pte_csh_cur_size[PTE_MAX_CURALT_DEPTH]; /* current pte_csh size (per curalt_depth) */ +GBLDEF int4 pte_csh_alloc_size[PTE_MAX_CURALT_DEPTH]; /* current allocated pte_csh size (per curalt_depth) */ +GBLDEF int4 pte_csh_entries_per_len[PTE_MAX_CURALT_DEPTH]; /* current number of entries per len */ +GBLDEF int4 pte_csh_tail_count[PTE_MAX_CURALT_DEPTH]; /* count of non 1-1 corresponding pte_csh_array members */ + +GBLDEF pte_csh *cur_pte_csh_array; /* copy of pte_csh_array corresponding to curalt_depth */ +GBLDEF int4 cur_pte_csh_size; /* copy of pte_csh_cur_size corresponding to curalt_depth */ +GBLDEF int4 cur_pte_csh_entries_per_len; /* copy of pte_csh_entries_per_len corresponding to curalt_depth */ +GBLDEF int4 cur_pte_csh_tail_count; /* copy of pte_csh_tail_count corresponding to curalt_depth */ + +GBLDEF readonly char *before_image_lit[] = {"NOBEFORE_IMAGES", "BEFORE_IMAGES"}; +GBLDEF readonly char *jnl_state_lit[] = {"DISABLED", "OFF", "ON"}; +GBLDEF readonly char *repl_state_lit[] = {"OFF", "ON", "WAS_ON"}; + +GBLDEF boolean_t crit_sleep_expired; /* mutex.mar: signals that a timer waiting for crit has expired */ +GBLDEF uint4 crit_deadlock_check_cycle; /* compared to csa->crit_check_cycle to determine if a given region + in a transaction legitimately has crit or not */ +GBLDEF node_local_ptr_t locknl; /* if non-NULL, indicates node-local of interest to the LOCK_HIST macro */ +GBLDEF boolean_t in_mutex_deadlock_check; /* if TRUE, mutex_deadlock_check() is part of our current C-stack trace */ +/* $ECODE and $STACK related variables. + * error_frame and skip_error_ret should ideally be part of dollar_ecode structure. since sr_avms/opp_ret.m64 uses these + * global variables and it was felt risky changing it to access a member of a structure, they are kept as separate globals */ +GBLDEF stack_frame *error_frame; /* ptr to frame where last error occurred or was rethrown */ +GBLDEF boolean_t skip_error_ret; /* set to TRUE by golevel(), used and reset by op_unwind() */ +GBLDEF dollar_ecode_type dollar_ecode; /* structure containing $ECODE related information */ +GBLDEF dollar_stack_type dollar_stack; /* structure containing $STACK related information */ +GBLDEF boolean_t ztrap_explicit_null; /* whether $ZTRAP was explicitly set to NULL in the current frame */ +GBLDEF int4 gtm_object_size; /* Size of entire gtm object for compiler use */ +GBLDEF int4 linkage_size; /* Size of linkage section during compile */ +GBLDEF uint4 lnkrel_cnt; /* number of entries in linkage Psect to relocate */ +GBLDEF int4 sym_table_size; /* size of the symbol table during compilation */ +GBLDEF boolean_t stop_non_mandatory_expansion, non_mandatory_expansion; /* Used in stringpool managment */ +GBLDEF jnl_fence_control jnl_fence_ctl; +GBLDEF jnl_process_vector *prc_vec = NULL; /* for current process */ +GBLDEF jnl_process_vector *originator_prc_vec = NULL; /* for client/originator */ +LITDEF char *jrt_label[JRT_RECTYPES] = +{ +#define JNL_TABLE_ENTRY(rectype, extract_rtn, label, update, fixed_size, is_replicated) label, +#include "jnl_rec_table.h" /* BYPASSOK */ +#undef JNL_TABLE_ENTRY +}; +LITDEF int jrt_update[JRT_RECTYPES] = +{ +#define JNL_TABLE_ENTRY(rectype, extract_rtn, label, update, fixed_size, is_replicated) update, +#include "jnl_rec_table.h" /* BYPASSOK */ +#undef JNL_TABLE_ENTRY +}; +LITDEF boolean_t jrt_fixed_size[JRT_RECTYPES] = +{ +#define JNL_TABLE_ENTRY(rectype, extract_rtn, label, update, fixed_size, is_replicated) fixed_size, +#include "jnl_rec_table.h" /* BYPASSOK */ +#undef JNL_TABLE_ENTRY +}; +LITDEF boolean_t jrt_is_replicated[JRT_RECTYPES] = +{ +#define JNL_TABLE_ENTRY(rectype, extract_rtn, label, update, fixed_size, is_replicated) is_replicated, +#include "jnl_rec_table.h" /* BYPASSOK */ +#undef JNL_TABLE_ENTRY +}; +/* Change the initialization if struct_jrec_tcom in jnl.h changes */ +GBLDEF struct_jrec_tcom tcom_record = {{JRT_TCOM, TCOM_RECLEN, 0, 0, 0}, + 0, 0, 0, "", {TCOM_RECLEN, JNL_REC_SUFFIX_CODE}}; +GBLDEF jnl_gbls_t jgbl; +GBLDEF short crash_count; +GBLDEF trans_num start_tn; +GBLDEF cw_set_element cw_set[CDB_CW_SET_SIZE]; +GBLDEF unsigned char cw_set_depth, cw_map_depth; +GBLDEF unsigned int t_tries; +GBLDEF uint4 t_err; +GBLDEF uint4 update_trans; /* Bitmask indicating among other things whether this region was updated; + * See gdsfhead.h for UPDTRNS_* bitmasks + * Bit-0 is 1 if cw_set_depth is non-zero or if it is a duplicate set + * (cw_set_depth is zero in that case). + * Bit-1 is unused for non-TP. + * Bit-2 is 1 if transaction commit in this region is beyond point of rollback. + */ + +GBLDEF boolean_t mu_rndwn_file_dbjnl_flush; /* to indicate standalone access is available to shared memory so + * wcs_recover() need not increment db curr_tn or write inctn record */ + +GBLDEF boolean_t is_uchar_wcs_code[] = /* uppercase failure codes that imply database cache related problem */ +{ /* if any of the following failure codes are seen in the final retry, wc_blocked will be set to trigger cache recovery */ +#define CDB_SC_NUM_ENTRY(code, value) +#define CDB_SC_UCHAR_ENTRY(code, is_wcs_code, value) is_wcs_code, +#define CDB_SC_LCHAR_ENTRY(code, is_wcs_code, value) +#include "cdb_sc_table.h" /* BYPASSOK */ +#undef CDB_SC_NUM_ENTRY +#undef CDB_SC_UCHAR_ENTRY +#undef CDB_SC_LCHAR_ENTRY +}; + +GBLDEF boolean_t is_lchar_wcs_code[] = /* lowercase failure codes that imply database cache related problem */ +{ /* if any of the following failure codes are seen in the final retry, wc_blocked will be set to trigger cache recovery */ +#define CDB_SC_NUM_ENTRY(code, value) +#define CDB_SC_UCHAR_ENTRY(code, is_wcs_code, value) +#define CDB_SC_LCHAR_ENTRY(code, is_wcs_code, value) is_wcs_code, +#include "cdb_sc_table.h" /* BYPASSOK */ +#undef CDB_SC_NUM_ENTRY +#undef CDB_SC_UCHAR_ENTRY +#undef CDB_SC_LCHAR_ENTRY +}; + +GBLDEF boolean_t gvdupsetnoop = FALSE; /* if TRUE, duplicate SETs do not change GDS block (and therefore no PBLK journal + * records will be written) although the database transaction number will be + * incremented and logical SET journal records will be written. + */ +GBLDEF boolean_t gtm_fullblockwrites; /* Do full (not partial) database block writes T/F */ +UNIX_ONLY(GBLDEF int4 gtm_shmflags;) /* Extra flags for shmat */ +#ifdef VMS +GBLDEF uint4 gtm_memory_noaccess_defined; /* count of the number of GTM_MEMORY_NOACCESS_ADDR logicals which are defined */ +GBLDEF uint4 gtm_memory_noaccess[GTM_MEMORY_NOACCESS_COUNT]; /* see VMS gtm_env_init_sp.c */ +#endif + +GBLDEF volatile boolean_t in_wcs_recover = FALSE; /* TRUE if in "wcs_recover", used by "bt_put" and "generic_exit_handler" */ + + +GBLDEF boolean_t in_gvcst_incr = FALSE; /* set to TRUE by gvcst_incr, set to FALSE by gvcst_put + * distinguishes to gvcst_put, if the current db operation is a SET or $INCR */ +GBLDEF mval *post_incr_mval; /* mval pointing to the post-$INCR value */ +GBLDEF mval increment_delta_mval; /* mval holding the INTEGER increment value, set by gvcst_incr, + * used by gvcst_put/gvincr_recompute_upd_array which is invoked by t_end */ +GBLDEF boolean_t is_dollar_incr = FALSE; /* valid only if gvcst_put is in the call-stack (i.e. t_err == ERR_GVPUTFAIL); + * is a copy of "in_gvcst_incr" just before it got reset to FALSE */ +GBLDEF int indir_cache_mem_size; /* Amount of memory currently in use by indirect cache */ +GBLDEF hash_table_objcode cache_table; +GBLDEF int cache_hits, cache_fails; + +/* The alignment feature is disabled due to some issues in stringpool garbage collection. + * TODO: When we sort out stringpool issues, change mstr_native_align to TRUE below */ +GBLDEF boolean_t mstr_native_align = FALSE; +GBLDEF boolean_t save_mstr_native_align; + +GBLDEF mvar *mvartab; +GBLDEF mvax *mvaxtab,*mvaxtab_end; +GBLDEF mlabel *mlabtab; +GBLDEF mline mline_root; +GBLDEF mline *mline_tail; +GBLDEF short int block_level; +GBLDEF triple t_orig; +GBLDEF int mvmax, mlmax, mlitmax; +static char routine_name_buff[SIZEOF(mident_fixed)], module_name_buff[SIZEOF(mident_fixed)]; +static char int_module_name_buff[SIZEOF(mident_fixed)]; +GBLDEF MIDENT_DEF(routine_name, 0, &routine_name_buff[0]); +GBLDEF MIDENT_DEF(module_name, 0, &module_name_buff[0]); +GBLDEF MIDENT_DEF(int_module_name, 0, &int_module_name_buff[0]); +GBLDEF char rev_time_buf[REV_TIME_BUFF_LEN]; +GBLDEF unsigned short source_name_len; +GBLDEF short object_name_len; +UNIX_ONLY( + GBLDEF unsigned char source_file_name[MAX_FBUFF + 1]; + GBLDEF unsigned char object_file_name[MAX_FBUFF + 1]; + GBLDEF int object_file_des; +) +VMS_ONLY( + GBLDEF char source_file_name[PATH_MAX]; + GBLDEF char object_file_name[256]; + GBLDEF struct FAB obj_fab; /* file access block for the object file */ +) + +GBLDEF int4 curr_addr, code_size; +GBLDEF mident_fixed zlink_mname; + +GBLDEF sm_uc_ptr_t reformat_buffer; +GBLDEF int reformat_buffer_len; +GBLDEF volatile int reformat_buffer_in_use; /* used only in DEBUG mode */ + +GBLDEF boolean_t mu_reorg_upgrd_dwngrd_in_prog; /* TRUE if MUPIP REORG UPGRADE/DOWNGRADE is in progress */ +GBLDEF boolean_t mu_reorg_nosafejnl; /* TRUE if NOSAFEJNL explicitly specified */ +GBLDEF trans_num mu_reorg_upgrd_dwngrd_blktn; /* tn in blkhdr of current block processed by MUPIP REORG {UP,DOWN}GRADE */ + +GBLDEF inctn_opcode_t inctn_opcode = inctn_invalid_op; +GBLDEF inctn_detail_t inctn_detail; /* holds detail to fill in to inctn jnl record */ +GBLDEF uint4 region_open_count; /* Number of region "opens" we have executed */ + +GBLDEF uint4 gtm_blkupgrade_flag = UPGRADE_IF_NEEDED; /* by default upgrade only if necessary */ +GBLDEF boolean_t disk_blk_read; + +GBLDEF boolean_t gtm_dbfilext_syslog_disable = FALSE; /* by default, log every file extension message */ + +GBLDEF int4 cws_reorg_remove_index; /* see mu_swap_blk.c for comments on the need for these two */ +GBLDEF block_id cws_reorg_remove_array[CWS_REORG_REMOVE_ARRAYSIZE]; + +GBLDEF uint4 log_interval; + +#ifdef UNIX +GBLDEF uint4 gtm_principal_editing_defaults; /* ext_cap flags if tt */ +GBLDEF boolean_t in_repl_inst_edit = FALSE; /* used by an assert in repl_inst_read/repl_inst_write */ +GBLDEF boolean_t in_repl_inst_create; /* used by repl_inst_read/repl_inst_write */ +GBLDEF boolean_t holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS]; /* whether a particular replication semaphore is being held + * by the current process or not. */ +GBLDEF boolean_t print_offset; /* Set to TRUE if -DETAIL is specified in MUPIP REPLIC -JNLPOOL or -EDITINST */ +GBLDEF boolean_t in_mupip_ftok; /* Used by an assert in repl_inst_read */ +GBLDEF uint4 section_offset; /* Used by PRINT_OFFSET_PREFIX macro in repl_inst_dump.c */ + +GBLDEF uint4 mutex_per_process_init_pid; /* pid that invoked "mutex_per_process_init" */ +GBLDEF boolean_t gtm_quiet_halt; /* Suppress FORCEDHALT message */ +#endif + +#ifdef UNICODE_SUPPORTED +/* Unicode line terminators. In addition to the following + * codepoints, the sequence CR LF is considered a single + * line terminator. + */ +LITDEF UChar32 u32_line_term[] = +{ + 0x000A, /* Line Feed */ + 0x000D, /* Carraige Return */ + 0x0085, /* Next Line - EBCDIC mostly */ + 0x000C, /* Form Feed */ + UTF_LINE_SEPARATOR, /* Line Separator */ + UTF_PARA_SEPARATOR, /* Paragraph Separator */ + 0x0000 +}; + +/* Given the first byte in a UTF-8 representation, the following array returns the total number of bytes in the encoding + * 00-7F : 1 byte + * C2-DF : 2 bytes + * E0-EF : 3 bytes + * F0-F4 : 4 bytes + * All others: 1 byte + * For details on the UTF-8 encoding see gtm_utf8.h + */ +LITDEF unsigned int utf8_bytelen[] = +{ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00-1F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20-3F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40-5F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60-7F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80-9F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A0-BF */ + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* C0-DF */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* E0-FF */ +}; + +/* Given the first byte in a UTF-8 representation, the following array returns the number of bytes to follow + * 00-7F : 0 byte + * C2-DF : 1 byte + * E0-EF : 2 bytes + * F0-F4 : 3 bytes + * All others: -1 bytes + * For details on the UTF-8 encoding see gtm_utf8.h + */ +LITDEF signed int utf8_followlen[] = +{ + 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, /* 00-1F */ + 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, /* 20-3F */ + 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, /* 40-5F */ + 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, /* 60-7F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-9F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-BF */ + -1,-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* C0-DF */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-FF */ +}; + +GBLDEF gtm_wcswidth_fnptr_t gtm_wcswidth_fnptr; /* see comment in gtm_utf8.h about this typedef */ +#endif + +GBLDEF uint4 gtm_max_sockets; /* Maximum sockets per socket device supported by this process */ +GBLDEF d_socket_struct *newdsocket; /* Commonly used temp socket area */ + +GBLDEF boolean_t dse_all_dump; /* TRUE if DSE ALL -DUMP is specified */ +GBLDEF int socketus_interruptus; /* How many times socket reads have been interrutped */ + +GBLDEF int4 pending_errtriplecode; /* if non-zero contains the error code to invoke ins_errtriple with */ + +GBLDEF uint4 process_id; +GBLDEF uint4 image_count; /* not used in UNIX but defined to preserve VMS compatibility */ + +GBLDEF size_t totalRmalloc; /* Total storage currently (real) malloc'd (includes extent blocks) */ +GBLDEF size_t totalAlloc; /* Total allocated (includes allocation overhead but not free space */ +GBLDEF size_t totalUsed; /* Sum of user allocated portions (totalAlloc - overhead) */ + +GBLDEF size_t totalRallocGta; /* Total storage currently (real) mmap alloc'd */ +GBLDEF size_t totalAllocGta; /* Total mmap allocated (includes allocation overhead but not free space */ +GBLDEF size_t totalUsedGta; /* Sum of "in-use" portions (totalAllocGta - overhead) */ + +GBLDEF volatile char *outOfMemoryMitigation; /* Cache that we will freed to help cleanup if run out of memory */ +GBLDEF uint4 outOfMemoryMitigateSize;/* Size of above cache (in Kbytes) */ + +GBLDEF int mcavail; +GBLDEF mcalloc_hdr *mcavailptr, *mcavailbase; + +GBLDEF uint4 max_cache_memsize; /* Maximum bytes used for indirect cache object code */ +GBLDEF uint4 max_cache_entries; /* Maximum number of cached indirect compilations */ + +GBLDEF void (*cache_table_relobjs)(void); /* Function pointer to call cache_table_rebuild() */ +UNIX_ONLY(GBLDEF ch_ret_type (*ht_rhash_ch)()); /* Function pointer to hashtab_rehash_ch */ +UNIX_ONLY(GBLDEF ch_ret_type (*jbxm_dump_ch)()); /* Function pointer to jobexam_dump_ch */ + +#ifdef VMS +GBLDEF boolean_t tp_has_kill_t_cse; /* cse->mode of kill_t_write or kill_t_create got created in this transaction */ +#endif + +GBLDEF cache_rec_ptr_t pin_fail_cr; /* Pointer to the cache-record that we failed while pinning */ +GBLDEF cache_rec pin_fail_cr_contents; /* Contents of the cache-record that we failed while pinning */ +GBLDEF cache_rec_ptr_t pin_fail_twin_cr; /* Pointer to twin of the cache-record that we failed to pin */ +GBLDEF cache_rec pin_fail_twin_cr_contents; /* Contents of twin of the cache-record that we failed to pin */ +GBLDEF bt_rec_ptr_t pin_fail_bt; /* Pointer to bt of the cache-record that we failed to pin */ +GBLDEF bt_rec pin_fail_bt_contents; /* Contents of bt of the cache-record that we failed to pin */ +GBLDEF int4 pin_fail_in_crit; /* Holder of crit at the time we failed to pin */ +GBLDEF int4 pin_fail_wc_in_free; /* Number of write cache records in free queue when we failed to pin */ +GBLDEF int4 pin_fail_wcs_active_lvl; /* Number of entries in active queue when we failed to pin */ +GBLDEF int4 pin_fail_ref_cnt; /* Reference count when we failed to pin */ +GBLDEF int4 pin_fail_in_wtstart; /* Count of processes in wcs_wtstart when we failed to pin */ +GBLDEF int4 pin_fail_phase2_commit_pidcnt; /* Number of processes in phase2 commit when we failed to pin */ + +GBLDEF zwr_hash_table *zwrhtab; /* How we track aliases during zwrites */ +GBLDEF uint4 zwrtacindx; /* When creating $ZWRTACxxx vars for ZWRite, this holds xxx */ +GBLDEF uint4 tstartcycle; /* lv_val cycle for tstart operations */ +GBLDEF uint4 lvtaskcycle; /* lv_val cycle for misc lv_val related tasks */ +GBLDEF int4 SPGC_since_LVGC; /* stringpool GCs since the last lv_val GC */ +GBLDEF int4 LVGC_interval = MIN_SPGC_PER_LVGC; /* dead data GC is done every LVGC_interval stringpool GCs */ +GBLDEF lv_xnew_var *xnewvar_anchor; /* Anchor for unused lv_xnew_var blocks */ +GBLDEF lv_xnew_ref *xnewref_anchor; /* Anchor for unused lv_xnew_ref blocks */ +GBLDEF mval *alias_retarg; /* Points to an alias return arg created by a "QUIT *" recorded here so + * symtab-unwind logic can find it and modify it if necessary if a + * symtab popped during the return and the retarg points to an lv_val + * that is going to be destroyed. + */ +#ifdef DEBUG_ALIAS +GBLDEF boolean_t lvmon_enabled; /* Enable lv_val monitoring */ +#endif + +GBLDEF block_id gtm_tp_allocation_clue; /* block# hint to start allocation for created blocks in TP */ + +#ifdef UNIX +GBLDEF int4 gtm_zlib_cmp_level; /* zlib compression level specified at process startup */ +GBLDEF int4 repl_zlib_cmp_level; /* zlib compression level currently in use in replication pipe. + * This is a source-server specific variable and is non-zero only + * if compression is enabled and works in the receiver server as well. + */ +GBLDEF zlib_cmp_func_t zlib_compress_fnptr; +GBLDEF zlib_uncmp_func_t zlib_uncompress_fnptr; +#endif + +GBLDEF mlk_stats_t mlk_stats; /* Process-private M-lock statistics */ + +#ifdef UNIX +/* Initialized blockalrm and block_sigsent can be used by all */ +GBLDEF boolean_t blocksig_initialized = FALSE; /* set to TRUE when blockalrm and block_sigsent are initialized */ +GBLDEF sigset_t blockalrm; +GBLDEF sigset_t block_sigsent; /* block all signals that can be sent externally + (SIGINT, SIGQUIT, SIGTERM, SIGTSTP, SIGCONT) */ +GBLDEF char *gtm_core_file; +GBLDEF char *gtm_core_putenv; +#endif + +#ifdef __MVS__ +GBLDEF char *gtm_utf8_locale_object; +GBLDEF boolean_t gtm_tag_utf8_as_ascii = TRUE; +#endif +#ifdef GTM_CRYPT +GBLDEF int4 gtmcrypt_init_state; /* Represents the various states of encryption library */ +GBLDEF int4 gbl_encryption_ecode; +GBLDEF char dl_err[MAX_ERRSTR_LEN]; + +GBLDEF gtmcrypt_init_t gtmcrypt_init_fnptr; +GBLDEF gtmcrypt_close_t gtmcrypt_close_fnptr; +GBLDEF gtmcrypt_hash_gen_t gtmcrypt_hash_gen_fnptr; +GBLDEF gtmcrypt_encode_t gtmcrypt_encode_fnptr; +GBLDEF gtmcrypt_decode_t gtmcrypt_decode_fnptr; +GBLDEF gtmcrypt_getkey_by_hash_t gtmcrypt_getkey_by_hash_fnptr; +GBLDEF gtmcrypt_getkey_by_name_t gtmcrypt_getkey_by_name_fnptr; +GBLDEF gtmcrypt_strerror_t gtmcrypt_strerror_fnptr; + +#endif /* GTM_CRYPT */ + +#ifdef DEBUG +/* Following definitions are related to white_box testing */ +GBLDEF boolean_t gtm_white_box_test_case_enabled = FALSE; +GBLDEF int gtm_white_box_test_case_number = 0; +GBLDEF int gtm_white_box_test_case_count = 0; +GBLDEF int gtm_wbox_input_test_case_count = 0; /* VMS allows maximum 31 characters for external identifer */ +GBLDEF boolean_t stringpool_unusable = FALSE; /* Set to TRUE by any function that does not expect any of its function + * callgraph to use/expand the stringpool. */ +GBLDEF boolean_t stringpool_unexpandable = FALSE;/* Set to TRUE by any function for a small period when it has ensured + * enough space in the stringpool so it does not expect any more garbage + * collections or expansions. + */ +GBLDEF boolean_t donot_INVOKE_MUMTSTART = FALSE; /* Set to TRUE whenever an implicit TSTART is done in gvcst_put/kill as + * part of an explicit + trigger update. In this situation, we dont expect + * MUM_TSTART macro to be invoked at all (see skip_INVOKE_RESTART below + * for description on why this is needed). So we keep this debug-only + * flag turned on throughout the gvcst_put/kill. An assert in + * mdb_condition_handler (invoked by INVOKE_RESTART macro when it does + * an rts_error) checks it never gets invoked while this is set. + */ +#endif + +GBLDEF boolean_t block_is_free; /* Set to TRUE if the caller wants to let t_qread know that the block it is + * attempting to read is actually a FREE block + */ +GBLDEF int4 gv_keysize; +GBLDEF gd_addr *gd_header; +GBLDEF gd_binding *gd_map; +GBLDEF gd_binding *gd_map_top; + +#ifdef GTM_TRIGGER +GBLDEF int4 gtm_trigger_depth; /* 0 if no trigger, 1 if inside trigger; 2 if inside nested trigger etc. */ +GBLDEF int4 tstart_trigger_depth; /* gtm_trigger_depth at the time of the outermost "op_tstart" + * (i.e. explicit update). This should be used only if dollar_tlevel + * is non-zero as it is not otherwise maintained. + */ +GBLDEF uint4 trigger_name_cntr; /* Counter from which trigger names are constructed */ +GBLDEF boolean_t *ztvalue_changed_ptr; /* -> boolean in current gtm_trigger_parms signaling if ztvalue has + been updated */ +GBLDEF boolean_t ztwormhole_used; /* TRUE if $ztwormhole was used by trigger code */ +GBLDEF mstr *dollar_ztname; +GBLDEF mval *dollar_ztdata, + *dollar_ztoldval, + *dollar_ztriggerop, + *dollar_ztupdate, + *dollar_ztvalue, + dollar_ztwormhole = DEFINE_MVAL_STRING(MV_STR, 0, 0, 0, NULL, 0, 0), + dollar_ztslate = DEFINE_MVAL_STRING(MV_STR, 0, 0, 0, NULL, 0, 0), + gtm_trigger_etrap; /* Holds $ETRAP value for inside trigger */ +GBLDEF int tprestart_state; /* When triggers restart, multiple states possible. See tp_restart.h */ +GBLDEF boolean_t skip_INVOKE_RESTART = FALSE; /* set to TRUE if caller of op_tcommit/t_retry does not want it to + * use the INVOKE_RESTART macro (which uses an rts_error to trigger + * the restart) instead return code. The reason we dont want to do + * rts_error is that this is an implicit tstart situation where we + * did not do opp_tstart.s so we dont want control to be transferred + * using the MUM_TSTART macro by mdb_condition_handler (which assumes + * opp_tstart.s invocation). + */ +GBLDEF boolean_t goframes_unwound_trigger; /* goframes() unwound a trigger base frame during its unwinds */ +GBLDEF symval *trigr_symval_list; /* List of availalable symvals for use in (nested) triggers */ +GBLDEF boolean_t dollar_ztrigger_invoked; /* $ZTRIGGER() was invoked on at least one region in this transaction */ +# ifdef DEBUG + GBLDEF gv_trigger_t *gtm_trigdsc_last; /* For debugging purposes - parms gtm_trigger called with */ + GBLDEF gtm_trigger_parms *gtm_trigprm_last; + GBLDEF ch_ret_type (*ch_at_trigger_init)(); /* Condition handler in effect when gtm_trigger called */ +# endif +GBLDEF boolean_t explicit_update_repl_state; /* Initialized just before an explicit update invokes any triggers. + * Set to 1 if triggering update is to a replicated database. + * Set to 0 if triggering update is to a non-replicated database. + * Value stays untouched across nested trigger invocations. + */ +#endif + +GBLDEF boolean_t skip_dbtriggers = FALSE; /* Set to FALSE by default (i.e. triggers are invoked). Set to TRUE + * unconditionally by MUPIP LOAD as it always skips triggers. Also set + * to TRUE by journal recovery/update-process only when they encounter + * updates done by MUPIP LOAD so they too skip trigger processing. In the + * case of update process, this keeps primary/secondary in sync. In the + * case of journal recovery, this keeps the db and jnl in sync. + */ + +GBLDEF boolean_t expansion_failed, retry_if_expansion_fails; /* used by string pool when trying to expand */ + +GBLDEF boolean_t mupip_exit_status_displayed; /* TRUE if mupip_exit has already displayed a message for non-zero status. + * Used by mur_close_files to ensure some message gets printed in case + * of abnormal exit status (in some cases mupip_exit might not have been + * invoked but we will still go through mur_close_files e.g. if exit is + * done directly without invoking mupip_exit). + */ +GBLDEF boolean_t implicit_trollback = FALSE; /* Set to TRUE by OP_TROLLBACK macro before calling op_trollback. Set + * to FALSE by op_trollback. Used to indicate op_trollback as to + * whether it is being called from generated code (opp_trollback.s) + * or from C runtime code. + */ +GBLDEF boolean_t hold_onto_locks; /* Currently TRUE for the lifetime of an online rollback. This means + * a variety of locks (db crit locks, jnlpool crit locks, instance file + * ftok locks, db access control semaphore etc.) are held for an extended + * period of time by rollback until it terminates. To the database runtime + * code, this global primarily signals it NOT to release the crit lock + * on ANY region after each transaction commit. Since online rollback is + * currently supported only in Unix, this variable is set to FALSE in VMS. + * A field "hold_onto_crit" is similarly defined in the "sgmnt_addrs" + * structure. This one pertains specifically to the db/jnlpool crit lock. + * See comment there on how to decide which one to use in the runtime code. + */ +#ifdef DEBUG +GBLDEF boolean_t ok_to_UNWIND_in_exit_handling; /* see gtm_exit_handler.c for comments */ +GBLDEF boolean_t skip_block_chain_tail_check; +#endif + +GBLDEF char gvcst_search_clue; diff --git a/sr_port/gbldefs_usr_share.c b/sr_port/gbldefs_usr_share.c new file mode 100644 index 0000000..7c3964f --- /dev/null +++ b/sr_port/gbldefs_usr_share.c @@ -0,0 +1,42 @@ +/**************************************************************** + * * + * Copyright 2002 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* General repository for global variable definitions used both in DDPGVUSR and GTMSHR. gvusr_init should setup these globals */ +/* for use in DDPGVUSR.*/ + +/***************** DO NOT MOVE THESE GLOBALS INTO GBLDEFS.C; IT WILL CAUSE DDPGVUSR.EXE BLOAT *****************/ + +#include "mdef.h" + +#include "gdsroot.h" +#include "gdskill.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" + +GBLDEF gd_region *gv_cur_region; +GBLDEF gv_key *gv_altkey, *gv_currkey; +GBLDEF bool caller_id_flag = TRUE; + +#ifdef INT8_SUPPORTED + GBLDEF const seq_num seq_num_zero = 0; + GBLDEF const seq_num seq_num_one = 1; + GBLDEF const seq_num seq_num_minus_one = (seq_num)-1; +#else + GBLDEF const seq_num seq_num_zero = {0, 0}; + GBLDEF const seq_num seq_num_minus_one = {(uint4)-1, (uint4)-1}; +# ifdef BIGENDIAN + GBLDEF const seq_num seq_num_one = {0, 1}; +# else + GBLDEF const seq_num seq_num_one = {1, 0}; +# endif +#endif diff --git a/sr_port/gc.mpt b/sr_port/gc.mpt new file mode 100644 index 0000000..8d58435 --- /dev/null +++ b/sr_port/gc.mpt @@ -0,0 +1,47 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 1989, 2006 Fidelity Information Services, Inc ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +%GC ;GT.M %GC utility - global copy + ; + n %GI,%GO,%SC,$et s %ZL=$zl,$et="zg "_$ZL_":ERR^%GC" + u 0:(ctrap=$c(3):exc="zg "_$ZL_":RESTART^%GC") + w !,"Global copy",! +RESTART r !,"Show copied nodes ? ",%SC s %SC=($tr(%SC,"yes","YES")=$e("YES",1,$l(%SC))) + f r !,"From global ^",%GI q:%GI="" d COPY + u 0:(ctrap="":exc="") + q +COPY n c,ix + i $e(%GI)="?" s ix=%GI d help q + i $e(%GI)'="^" s %GI="^"_%GI + i '$d(@%GI) w !,"Global "_%GI_" does not exist." q + f r !,"To global ^",%GO q:$e(%GO)'="?" s ix=%GO d help + i %GO="" q + i $e(%GO)'="^" s %GO="^"_%GO + i $d(@%GO) w !,"Global "_%GO_" already exists." q + s c=0 + i '$d(%SC) n %SC s %SC=0 + i $d(@%GI)'[0 s @%GO=@%GI s c=c+1 i %SC w !,%GO,"=",@%GI + f s %GI=$q(@%GI) q:%GI="" s c=c+1,ix="("_$p(%GI,"(",2,999),@(%GO_ix)=@%GI i %SC w !,%GO_ix,"=",@%GI + w !,"Total ",c," nodes copied.",! + q +help i $l(ix)=2,"Dd"[$e(ix,2) d ^%GD u 0:flush q + W !!,"This routine copies a node and all its descendents" + w !,"from one global variable to another" + w !,"""From global"" requests the source for the copy," + w !,"""To global"" requests the destination" + w !,"Use standard MUMPS gvn syntax to specify the node names" + w !,"?D invokes %GD to get a global directory" + w !," drops you back to the prior prompt or out of %GC" + w ! + q +ERR w !,$p($zs,",",2,99),! + u 0:(ctrap="":exc="") + s $ec="" + q diff --git a/sr_port/gce.mpt b/sr_port/gce.mpt new file mode 100644 index 0000000..317c15e --- /dev/null +++ b/sr_port/gce.mpt @@ -0,0 +1,75 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 1989, 2003 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +%GCE ;GT.M %GCE utility - global change every occurrence + ; + n c,g,gn,m,n,ns,os,s,sc,sn,%ZD,%ZG,x + i '$d(%zdebug) n $et s $et="zg "_$zl_":ERR^%GCE" u $p:(ctrap=$c(3):exc="zg "_$zl_":LOOP^%GCE") + w !,"Global Change Every occurrence",! + d base + q +base f d main q:'%ZG + i $d(%ZD),%ZD'=$p c %ZD + u $p:(ctrap="":exc="") + q +main k %ZG d CALL^%GSEL i '%ZG q + s gn="" + r !,"Old string: ",os + i os="" w !,"No string to find - no search done.",! s %ZG=0 q + i os?.E1C.E w !,"The Old string contains control characters" + r !,"New string: ",ns + i ns?.E1C.E w !,"The New string contains control characters" + f r !,"Show changed nodes ? ",x,! q:$e(x)'="?" d help + i $l(x) s sc=$e("NO",1,$l(x))'=$e($tr(x,"no","NO"),1,2) + e s sc=1 + i sc f d q:$l(%ZD) + . r !,"Output device: : ",%ZD,! + . i '$l(%ZD) s %ZD=$p q + . i %ZD="^" q + . i %ZD="?" d q + . . w !!,"Select the device you want for output" + . . w !,"If you wish to exit enter a carat (^)",! + . . s %ZD="" + . i $zparse(%ZD)="" w " no such device" s %ZD="" q + . o %ZD:(newversion:block=2048:record=2044:exception="g noopen"):0 + . i '$t w !,%ZD," is not available" s %ZD="" q + . q +noopen . w !,$p($ZS,",",2,999),! c %ZD s %ZD="" + i sc q:%ZD="^" + e s %ZD=$p + i %ZD'=$p d + . u %ZD w $zd($h,"DD-MON-YEAR 24:60:SS"),!,"Global Change Every occurrence of:",!,">",os,"<",!,"To:",!,">",ns,"<",! u $p + f s gn=$o(%ZG(gn)) q:gn="" d search + q +search w:$x>70 ! w gn,?$x\10+1*10 + s g=gn,(m,n)=0 u %ZD + i ($d(@g)#10=1) s n=1 d:@g[os change + f s g=$q(@g) q:g="" s n=n+1 d:@g[os change + i m w !!,m," changes made in total ",n," nodes.",! + e w !!,"No changes made in total ",n," nodes.",! + u $p + q +change w:sc !,g,!,"Was : ",@g + s s=@g,sn="",c=$l(s,os) + f i=1:1:c-1 s sn=sn_$p(s,os,i)_ns + s @g=sn_$p(s,os,c),m=m+i + w:sc !,"Now : ",@g,! + q +help w !,"Answer No to this prompt if you do not wish a trail of the changes" + q + q +ERR i $d(%ZD),%ZD'=$p c %ZD + u $p w !,$p($zs,",",2,99),! + u $p:(ctrap="":exc="") + s $ec="" + q +LOOP i $d(%ZD),%ZD'=$p c %ZD + d base + q diff --git a/sr_port/gd.mpt b/sr_port/gd.mpt new file mode 100644 index 0000000..f120d10 --- /dev/null +++ b/sr_port/gd.mpt @@ -0,0 +1,24 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 1987,2001 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +%GD ;GT.M %GD utility - global directory + ; + n %ZG,%ZL + i '$d(%zdebug) n $et s $et="zg "_$zl_":ERR^%GD" u $p:(ctrap=$c(3):exc="k %ZG zg "_$zl_":LOOP^%GD") + w !,"Global Directory",! + d GD^%GSEL,EXIT + q +ERR u $p w !,$p($zs,",",2,99),! + s $ec="" + ; Warning: Fall-through! +EXIT u $p:(ctrap="":exc="") + q +LOOP d GD^%GSEL,EXIT + q diff --git a/sr_port/gde.hlp b/sr_port/gde.hlp new file mode 100644 index 0000000..50a1a96 --- /dev/null +++ b/sr_port/gde.hlp @@ -0,0 +1,880 @@ + +1 Overview + GDE Overview + The GT.M Global Directory Editor, GDE, is a tool for creating, + examining, and modifying Global Directories (GDs). A Global Directory + is a file that identifies: + + o What global variables go to what database files + + o The size limits for names and values of global variables + + o Other database characteristics + + o If and what type of journaling should take place + + All MUMPS programs that use the same Global Directory share all the + same global variables, unless the Global Directory uses environment + variables for which users have varying definitions. The local + variables are accessible only among programs executed within a single + process scope. + +2 Functions + GDE Functions + The main functions of the Global Directory Editor are to: + + o Define the mapping of global variables to database files + + o Define the character limitations of global variable names and + records + + o Provide the MUMPS Peripheral Interchange Program (MUPIP) with + characteristics (e.g., -ALLOCATION size) used in creating and + extending a database file + + o Define whether a database file should be journaled and how + + o Define the -ACCESS_METHOD used to access the database files and + other database characteristics + +2 Mapping + Mapping + Defining a "map," i.e., where GT.M stores a global variable, + requires defining, not only the NAME of the global variable and the + database FILE in the Global Directory, but also the REGION and + SEGMENT. + + A REGION is a logical structure that holds information such as key- + and record-size about a portion of a database. GT.M and the + operating system handle data stored in a REGION together by storing + it in the same place or places, backing it up as a unit, etc. A + REGION must map to a SEGMENT. + + A SEGMENT defines additional database storage characteristics not + required for UNIX files. A SEGMENT must map to a FILE. The SEGMENT + exists primarily for future design considerations, when a + one-to-one correspondence between the REGION and SEGMENT will no + longer be required. + + The connection in the map between a name and a file is very + important and is used by the run-time system and most GT.M + utilities. Specifying a map requires entering (a) NAME(S) with a + connection to a REGION, a REGION with a connection to a SEGMENT, + and a SEGMENT with a connection to a database file. The commands + may be issued in any order, but the final result must be a complete + logical path from name to file. + + NAME(s) ---> REGION ---> SEGMENT ---> FILE + +2 Default_GD + Creating a Default Global Directory + The Global Directory Editor creates a quick default Global + Directory for purposes such as development and testing work. A + default Global Directory also serves as a starting point or + template for a custom Global Directory. + + To create a default Global Directory structure, invoke and + immediately EXIT GDE. GDE creates a Global Directory mapping of all + NAMEs to the REGION DEFAULT, the default REGION to the SEGMENT + DEFAULT and the default SEGMENT to the default file-name MUMPS with + the default file extension of .DAT. + +2 Custom_GD + Creating a Custom Global Directory + When a default Global Directory does not meet your needs, you need + to customize a Global Directory. Usually you customize the Global + Directory when you define your production database file. This + enables you to optimize the sharing and location of your data. + + To create a custom Global Directory, invoke GDE and issue the + commands necessary to build the desired Global Directory. For more + information about mapping, refer to the "How to Map Global + Variables" section. + +2 GTM$GBLDIR + Defining the gtmgbldir Environment Variable + GT.M identifies the current Global Directory by the environment + variable gtmgbldir. GDE, MUPIP, LKE, DSE and the GT.M run-time + system use this environment variable to identify the file used as + the Global Directory. The run-time system normally uses this + environment variable, but may also access a Global Directory by + setting $ZGBLDIR or the extended global reference ([]) syntax. + + If you maintain multiple Global Directories, define gtmgbldir to + the Global Directory you currently want to use. You may want to + define gtmgbldir in your login file. + + Example + + $ gtmgbldir=prod.gld + $ export gtmgbldir + +1 Command_syntax + Command Syntax + The general format for GDE commands is: + + command [-object-type] [object-name] [-qualifier...] + ex: -NAME Name-space -region-qualifier... + -REGION Region-name -region-qualifier... + -SEGMENT Segment-name -segment-qualifier... + + where: + + Object-type Indicates whether the command operates on a + -N[AME], + - R[EGION] or -S[EGMENT]. + + Object-name Specifies the name of a N[AME] space, R[EGION] or + S[EGMENT]. Object-names of different types may + have the same name. + + Name-space Specifies a name or name prefix that maps to a + REGION. Names may include the wildcard operator * + as a suffix. + + Region-name Specifies a REGION name. + + Segment-name Specifies a SEGMENT name. + + Qualifier Indicates a command or object qualifier. + + The format for each command specifies required qualifiers for the + command. + + The @, EXIT, HELP, LOG, QUIT and SPAWN commands do not use this + general format. For the applicable format, refer to the section + explaining each of these commands. + + Comments on the command line may be delimited by an exclamation mark + (!). An exclamation mark not enclosed in quotes (") causes GDE to + ignore the rest of the input line. + +1 at-sign + @ + The @ command executes a GDE command file. Use the @ command to run + stored GDE command sequences from an interactive session. + + The format of the @ command is: + + @ file-name + + The file-name specifies the command file to execute. GDE provides the + default file extension ".COM" in creating the file-name. + + GDE executes each line of the command file as if the line had been + typed at the terminal. + + Example + + GDE> @standard + + This command transfers the GDE input to STANDARD.COM in the current + default directory. STANDARD.COM should contain GDE commands; any + comments should start with an exclamation mark (!). + +1 ADD + A[DD] + The ADD command inserts a new NAME, REGION, or SEGMENT into the Global + Directory. + + The format of the ADD command is: + + A[DD]-N[AME] name-space -R[EGION]=region-name + A[DD]-R[EGION] region-name -D[YNAMIC]=segment-name [-region-qual...] + A[DD]-S[EGMENT] segment-name -F[ILE_NAME]=file-name [-segment-qual...] + + The ADD command requires specification of an object-type and + object-name. GDE supplies default values for qualifiers not supplied. + + Name-spaces and file namesare case-sensitive while other objects are + not. + +1 CHANGE + C[HANGE] + The CHANGE command alters the NAME to REGION or REGION to SEGMENT + mapping and the environment for a REGION or SEGMENT. + + The format of the CHANGE command is: + + C[HANGE]-N[AME] name-space -R[EGION]=new-region + C[HANGE]-R[EGION] region-name [-region-qualifier...] + C[HANGE]-S[EGMENT] segment-name [-segment-qualifier...] + + The CHANGE command requires specification of an object-type and + object-name. + + Changes to the database environment characteristics take effect the + next time you create a new file with the MUPIP CREATE command. Mapping + changes take effect for subsequent image activation, for example + following the next RUN or MUMPS-DIRECT command. + +1 DELETE + D[ELETE] + The DELETE command removes a NAME, REGION, or SEGMENT from the Global + Directory. The DELETE command does not delete the actual data from the + database but can make the data inaccessible to MUMPS images using the + resulting directory. + + The format of the DELETE command is: + + D[ELETE]-N[AME] name-space + D[ELETE]-R[EGION] region-name + D[ELETE]-S[EGMENT] segment-name + + The DELETE command requires specification of an object-type and + object-name. + + Deleting a NAME removes the NAME to REGION mapping. Deleting a REGION + unmaps all NAMES mapped to the REGION. Deleting a SEGMENT unmaps the + REGION mapped to the SEGMENT. + + Map the deleted names to another REGION or the deleted REGION to + another SEGMENT using the CHANGE command. + The default name-space (*) can not be deleted. + +1 EXIT + E[XIT] + The EXIT command writes all changes made in the current GDE editing + session to the Global Directory. + + The format of the EXIT command is: + + E[XIT] + + GDE performs a full verification test (VERIFY) on the data. If the + verification succeeds, GDE writes the new Global Directory to disk and + issues a verification message. + + If the verification fails, GDE displays a listing of all unverifiable + mappings and waits for corrections. Make appropriate corrections or + leave the Global Directory in its original, unedited state by using + the QUIT command. + + If you have not made any changes to the Global Directory, GDE does not + create a new Global Directory. + +1 HELP + H[ELP] + The HELP command explains the GDE commands. The format of the HELP + command is: + + HE[LP] [keyword...] + + Specify the GDE command for which you want information at the Topic + prompt. The help facility also provides an "Overview." + + Use or to return to the GDE prompt. + +1 LOCKS + LOC[KS] + The LOCKS command specifies the REGION into which GT.M maps locks on + names not starting with ^. GDE maps locks on global names (starting + with ^) to the region of the database specified for that name. + + The format of the LOCKS command is: + + LOC[KS] -R[EGION]=region-name + + The LOCK -REGION= qualifier allows specification of a region for local + locks. By default, GDE maps local locks to the default region DEFAULT. + + + Example + + GDE> LOCK-REGION=MAIN + + This command maps all locks on resource names that don't start with + "^" to region MAIN. + +1 LOG + LOG + The LOG command creates a log file of all GDE commands and displays + for the current editing session. Because the system places an + exclamation point (i.e., the comment symbol) before all display lines + in the log, a log can be used with the @ sign as a command procedure. + + The format of the LOG command is: + + LOG + LOG -ON[=file-name] + LOG -OF[F] + + The LOG command without a qualifier reports the current status of GDE + logging. The LOG command displays a message showing whether logging is + in effect and the specification of the current log file for the GDE + session. + + The log facility can be turned on and off using the -ON or -OFF + qualifiers any time during a GDE session. However, GDE closes the log + files only when the GDE session ends. + + The -ON qualifier has an optional argument of a file-name, which must + identify a legal UNIX file. GDE uses the default file extension + ".LOG". If LOG -ON has no file-name argument, GDE uses the previous + log file for the editing session. If no log file has previously been + specified during this editing session, GDE uses the default log file + GDELOG.LOG. + +1 QUIT + Q[UIT] + The QUIT command ends the current editing session without saving any + changes to the Global Directory. GDE does not create an updated Global + Directory file. + + The format of the QUIT command is: + + Q[UIT] + + If the session made changes to the Global Directory, GDE issues a + message warning that the Global Directory has not been updated. + +1 RENAME + R[ENAME] + The RENAME command allows changes of a NAME, the name of a REGION or + the name of a SEGMENT. + + The format of the RENAME command is: + + R[ENAME]-N[AME] old-name new-name + R[ENAME]-R[EGION] old-reg-name new-reg-name + R[ENAME]-S[EGMENT] old-seg-name new-seg-name + + The RENAME command requires specification of an object-type and two + object-names. + + When renaming a REGION, GDE transfers all NAME mappings to the new + REGION. When renaming a SEGMENT, GDE transfers the REGION mappings to + the new SEGMENT. + +1 SETGD + SE[TGD] + The SETGD command closes out edits on one Global Directory and opens + edits on another. + + The format of the SETGD command is: + + SE[TGD] -F[ILE]=file-name [-Q[UIT]] + + The -FILE=file-name qualifier identifies the new Global Directory. + When you provide a partial file-name, GDE uses the current default + directory and defaults the type to .GLD. + + The -QUIT qualifier specifies that any changes that have been made to + the current Global Directory are not written, i.e., are lost, during + the change of Global Directory. + + A SETGD changes the Global Directory on which the GDE edits act. If + the current Global Directory has not been modified or the -QUIT + qualifier appears in the command, the change simply occurs. However, + if the current Global Directory has been modified, GDE verifies the + Global Directory, and if the verification is successful, writes that + Global Directory. If the verification is not successful, the SETGD + fails. + +1 SHOW + SH[OW] + The SHOW command displays information about NAMEs, REGIONs and + SEGMENTs. + + The format of the SHOW command is: + + SH[OW] -N[AME] [name-space] + SH[OW] -R[EGION] [region-name] + SH[OW] -S[EGMENT] [segment-name] + SH[OW] -M[AP] [R[EGION]=region-name] + SH[OW] -T[EMPLATE] + SH[OW] -A[LL] + + The object-type is optional. -MAP, -TEMPLATE, and -ALL are special + qualifiers used as follows: + + o -MAP - displays the mapping of all NAMES, REGIONs, SEGMENTs, and + files + + o -TEMPLATE - displays the current REGION and SEGMENT templates + + o -ALL - displays all templates, the map, and information about each + defined NAME, REGION, and SEGMENT + + By default, SHOW displays -ALL. + + +1 SPAWN + SP[AWN] + The SPAWN command creates a child process for access to the shell + without terminating the current GDE environment. Use the SPAWN command + to suspend a session and issue shell commands such as ls or printenv. + The SPAWN command spawns (forks) a child process with an optional + command string. If SPAWN has no command string parameter, the GDE + command leaves the terminal at the prompt for the shell of the spawned + process. + + The format of the SPAWN command is: + + SP[AWN] [shell command] + + Example + + GDE> SPAWN "ls *.DAT" + + This command invokes a directory listing of all files in the current + default directory with a .DAT extension. + +1 TEMPLATE + T[EMPLATE] + The TEMPLATE command maintains a set of REGION and SEGMENT qualifier + values for use as templates when ADDing regions and segments. When an + ADD command omits qualifiers, GDE uses the template values as + defaults. GDE maintains a separate set of SEGMENT qualifier values for + each ACCESS_METHOD. When GDE modifies the ACCESS_METHOD, it activates + the appropriate set of TEMPLATEs and sets all unspecified qualifiers + to the template defaults for the new ACCESS_METHOD. Use the GDE SHOW + command to display qualifier values for all ACCESS_METHODs. + + The format of the TEMPLATE command is: + + T[EMPLATE]-R[EGION] [-region-qualifier...] + T[EMPLATE]-S[EGMENT] [-segment-qualifier...] + + The TEMPLATE command requires specification of an object-type. + +1 VERIFY + V[ERIFY] + The VERIFY command checks the NAME to REGION mappings to insure all + NAMES map to a REGION. The VERIFY command checks REGION to SEGMENT + mappings to insure each REGION maps to a SEGMENT, each SEGMENT maps to + only one REGION and the SEGMENT maps to a UNIX file. The EXIT command + implicitly performs a VERIFY -ALL. + + The format of the VERIFY command is: + + V[ERIFY] + V[ERIFY] -N[AME] [name-space] + V[ERIFY] -R[EGION] [region-name] + V[ERIFY] -S[EGMENT] [segment-name] + V[ERIFY] -M[AP] + V[ERIFY] -T[EMPLATE] + V[ERIFY] -A[LL] + + The object-type is optional. -MAP, -TEMPLATE, and -ALL are special + qualifiers used as follows: + + o -MAP - checks that all NAMES map to a REGION, all REGIONs map to a + SEGMENT, and all SEGMENTs map to a FILE + + o -TEMPLATE - checks that all templates currently are consistent and + useable + + o -ALL - checks all map and template data + + VERIFY with no qualifier, VERIFY -MAP and VERIFY -ALL each check all + current information. + +1 Qualifiers + GDE Command Qualifiers + The -NAME, -REGION, and -SEGMENT qualifiers each have additional + qualifiers used to further define or specify characteristics of a + NAME, REGION, or SEGMENT. This section discusses these additional + qualifiers. + +2 Name_Qualifiers + Name Qualifiers + The only -NAME qualifier, used with the commands ADD or CHANGE, is + the -REGION qualifier. + +3 -REGION + -R[EGION]=region-name + Specifies the name of a REGION. + + The maximum length is 16 alphanumeric characters. + + Example + + GDE> add-Name a*-Region=areg + + This command creates the name "a," if it does not exist and maps + it to the region "areg." + + +2 Region_Qualifiers + Region Qualifiers + The following -REGION qualifiers can be used with the ADD, CHANGE + or TEMPLATE commands. + +3 -DYNAMIC_SEGMENT + -D[YNAMIC_SEGMENT]=segment-name + Specifies the name of a dynamic SEGMENT. A dynamic segment + allows read-write access. + + The minimum length is 1 alpha character. + + The maximum length is 16 alphanumeric characters. + +3 -KEY_SIZE + -K[EY_SIZE]=size in bytes + Specifies the maximum size of keys, in bytes, which can be + stored in the region. + + CAUTION: The key size must be less than the record size. GDE + rejects the command if the key size is greater than the record + size. + + The minimum key size is 3 bytes. + + The maximum key size is 255 bytes. + + By default, GDE uses a key size of 64 bytes. + +3 -RECORD_SIZE + -R[ECORD_SIZE]=size in bytes + Specifies the maximum record size, in bytes, which can be stored + in the region. + + CAUTION: The key size must be less than the record size. GDE + rejects the command if the key size exceeds the record size. + + The record size must be less than half the block size of the + segment to which the region maps. If the record size is not less + than half the block size minus 7 bytes, GDE issues an error + message. To VERIFY or EXIT, you must change the record size. + + The minimum record size is 7 bytes. + + The maximum record size is 32,508 bytes. + + By default, GDE uses a record size of 256 bytes. + +3 -NULL_SUBSCRIPTS + -[NO]N[ULL_SUBSCRIPTS] + Indicates whether GT.M allows null subscripts for global + variables stored in the region, i.e., whether GT.M permits + reference such as ^aaa("",1). + + By default, REGIONS have -NONULL_SUBSCRIPTS. + +3 -JOURNAL + -[NO]J[OURNAL][=journal-option-list] + Specifies whether the database file allows journaling and, if it + does, establishes characteristics for the journal file. + + -NOJOURNAL specifies that the database file does not allow + journaling. -NOJOURNAL does not accept an argument assignment. + + -JOURNAL specifies that journaling is allowed. -JOURNAL takes + one or more arguments in a journal-option-list. The + journal-option-list contains keywords separated with commas (,) + enclosed in parentheses (). When the list contains only one + keyword, the parentheses are optional. + + For more information about journaling, refer to the GT.M + Journaling chapter of the GT.M Administration and Operations + Guide. + +4 BEFORE_IMAGE + [NO]BE[FORE_IMAGE] + [NO]BEFORE_IMAGE controls whether the journal should capture + before images of information that an update is about to + modify. + + A BEFORE_IMAGE journal permits the possibility of performing + "roll-back" recovery (i.e., Backward Recovery) of the + associated database file. BEFORE_IMAGE increases the load on + I/O and CPU resources and therefore may affect performance. + +4 FILE_NAME + F[ILE_NAME]=file-name + FILE_NAME=file-name specifies the name of the journal file. + + Journal file-name are limited to 255 characters. + + By default, GDE derives the file-name from the database file + name. + + By default, GDE uses a journal file type of .MJL. + +4 ALLOCATION + A[LLOCATION]=blocks + ALLOCATION=blocks specifies the initial size of the journal + file in blocks. Because frequent journal file extensions + degrade run-time performance, make journal file allocation + ample for a production database file. + + When you change the ALLOCATION and do not also specify + EXTENSION, the EXTENSION automatically changes to equal the + ALLOCATION. + + The minimum allocation is 10 blocks. + + The maximum allocation is 16777216 blocks. + + By default, GDE uses an allocation of 100 blocks. + +4 EXTENSION + E[XTENSION]=blocks + EXTENSION=blocks specifies the size by which a journal file + extends when it becomes full. EXTENSION=0 prevents automatic + journal file extension. Because frequent journal file + extensions degrade run-time performance, make the journal + file extension ample for a production database file. + + When you change the ALLOCATION and do not also specify + EXTENSION, the EXTENSION automatically changes to equal the + ALLOCATION. + + The minimum EXTENSION is 0 blocks. + + The maximum EXTENSION is 65536 blocks. + + By default, GDE uses an EXTENSION of 100 blocks. + +4 BUFFER_SIZE + BU[FFER_SIZE]=pages + BUFFER_SIZE=pages specifies the amount of memory used to + buffer journal file output. A larger BUFFER_SIZE usually + smooths and improves run-time performance. + + A larger BUFFER_SIZE requires more memory resources, which + may be scarce. A larger BUFFER_SIZE provides more room for + journal records in memory on their way to the disk and + therefore increases the number of update records that may be + lost in a system failure. + + The minimum BUFFER_SIZE is enough 512-byte pages to hold 2 + GDS database blocks. + + The maximum BUFFER_SIZE is 2000 pages. + + By default, GDE uses a BUFFER_SIZE of 128 pages. + +2 Segment_Qualifiers + Segment Qualifiers + The following -SEGMENT qualifiers can be used with the ADD, CHANGE, + or TEMPLATE commands. + +3 -FILE_NAME + -F[ILE_NAME]="filename" + Specifies the file name for a SEGMENT. The filename must appear + in quotes to distinguish its slashes from those of the command + qualifiers. GT.M allows the use of environment variables in the + filename. + + The maximum filename length is 255 characters. + + By default, GDE uses a file name of MUMPS. + + By default, GDE uses a file extension of .DAT. + +3 -ACCESS_METHOD + -AC[CESS_METHOD]=code + Specifies the access method GT.M uses to store and retrieve data + from the global database file. The two methods are Buffered + Global (BG) and Mapped Memory (MM). + + GDE maintains a separate set of SEGMENT qualifier values for + each ACCESS_METHOD. When GDE modifies the ACCESS_METHOD, it + activates the appropriate set of TEMPLATEs and sets all + unspecified qualifiers to the template defaults for the new + ACCESS_METHOD. + + By default, GDE uses an access method of BG. + + +3 -BLOCK_SIZE + -BL[OCK_SIZE]=size + Specifies the size, in bytes, of each database block on disk. + The block-size must be a multiple of 512. If the block-size is + not a multiple of 512, GDE rounds off the block-size to the next + highest multiple of 512 and issues a warning message. + + If the specified block-size is less than the minimum, GDE uses + the minimum block-size. If the specified block-size is greater + than the maximum, GDE issues an error message. + + A 1024 byte or 2048 byte block-size serves well for most + applications. + + + The minimum block-size is 512 bytes. + + The maximum block-size is 65,024 bytes. + + By default, GDE uses a block-size of 1024 bytes for BG and MM + files. + +3 -ALLOCATION + -AL[LOCATION]=size + Specifies the number of blocks GT.M allocates to a disk file + when MUPIP creates the file. For GDS files, the number of bytes + allocated is ALLOCATION size times the BLOCK_SIZE. + + The default allocation was chosen for small development + projects. Use larger allocations for production files and large + projects. Because file fragmentation impairs performance, make + the initial allocation large enough to hold the anticipated + contents of the file for a length of time consistent with your + UNIX file reorganization schedule. + + The minimum ALLOCATION is 10 blocks. + + The maximum ALLOCATION is 16777216 blocks. + + By default, GDE uses an ALLOCATION of 100 blocks. + +3 -EXTENSION_COUNT + -E[XTENSION_COUNT]=size + Specifies the number of extra GDS blocks of disk space by which + the file should extend. The extend amount is interpreted as the + number of usable GDS blocks to create with the extension. To + calculate the number of host operating system blocks added with + each extension, multiply the number of GDS blocks added by (GDS + block size/host block size); to this amount, add one local bit + map block for each 512-byte block added in each extension, plus + one for any remaining bytes. + + The default extension amount was chosen for small development + projects. Use larger extensions for larger files. Because many + file extensions adversely affect performance, set up extensions + appropriate to the file allocation. + + BG files may extend automatically when the file is full. A zero + extension size prevents a BG file from automatically extending. + + BG files may be, and MM files must be, extended with MUPIP + EXTEND. When a MUPIP EXTEND command does not include a -BLOCKS= + qualifier, EXTEND uses the extension size in the database + header. The extension amount may be changed with the MUPIP SET + command. To require explicit expansion for BG files with MUPIP + EXTEND, set -EXTENSION_COUNT to zero. + + The minimum EXTENSION is 0 blocks. + + The maximum EXTENSION is 65,535 blocks. + + By default, GDE uses an EXTENSION of 100 blocks. + +3 -GLOBAL_BUFFER_COUNT + -G[LOBAL_BUFFER_COUNT]=size + Specifies the number of global buffers for a file. Global + buffers serve as part of the database caching mechanisms. + + Avoid inadequate settings of this factor. However, if your + system is memory constrained and the database file traffic is + not heavy enough to hold the cache in memory, increasing + GLOBAL_BUFFER_COUNT may induce paging. Therefore, do not + increase this factor to a large value without careful + observation. + + The proper number of GLOBAL_BUFFERs depends on the application + and the amount of primary memory available on the system. Most + production databases exhibit a direct relationship between the + number of GLOBAL_BUFFERs and performance. However, the + relationship is not linear, but rather more parabolic, so that + increases past some point have progressively less benefit. This + point of diminishing returns depends on the application. For + most applications, Greystone expects the optimum number of + GLOBAL_BUFFERs to be between 512 and 2048. + + Generally, you should increase the number of GLOBAL_BUFFERs for + production GDS databases. This is because GT.M uses the shared + memory database cache associated with each GDS file for the vast + majority of caching. + + The minimum for BG is 64 blocks. + + The maximum for BG is 4096 blocks. + + By default, GDE uses a GLOBAL_BUFFER_COUNT of 1024 blocks. + +3 -DEFER + -[NO]D[EFER] + Instructs GT.M whether or not to store updates on the disk + immediately. + + DEFER has a significant performance benefit for heavily updated + database files. However, DEFER should only be used for files, + such as those containing temporary storage for reports, which + can be recreated if the system crashes. + + By default, GDE makes MM database files -DEFER. + +3 -LOCK_SPACE + -LOC[K_SPACE]=size + Specifies the number of pages of space to use for the lock + database stored with this segment. If GT.M runs out of space to + store locks, the system becomes slightly less efficient. The + default amount is generous for most MUMPS applications. + + The minimum LOCK_SPACE is 10 pages. + + The maximum LOCK_SPACE is 1000 pages. + + By default, GDE uses a LOCK_SPACE of 20 pages. + +1 Guidelines + Mapping Guidelines + Global Directory maps consist of a hierarchy of NAMEs, REGIONs, + SEGMENTs and FILEs. The following section provides guidelines for + defining and using these mapping components. + +2 Names + NAME Guidelines + GT.M uses a NAME to place global variables in a physical database + file. + A NAME: + + o Maps to only one REGION in the Global Directory + + o Can be a discrete "global" name, e.g., aaa is a discrete global + + o Can be a partial name ending with a wild card ("*") + + o Must begin with an alphabetic character or a % sign + + o Can be 1 to 8 alphanumeric characters + + o Is case sensitive + + A wild card defines all names starting with the characters of the + partial name. For example, abc*, defines the range of all global + names beginning with the three characters "abc." + +2 Regions + REGION Guidelines + GT.M uses a REGION to logically define a portion of the database + with the same characteristics, such as key and record-size. A + REGION maps to a SEGMENT. More than one NAME may map to a REGION. A + Global Directory must have at least one REGION. + + A region-name: + + o Must begin with an alphabetic character, except for DEFAULT + + o Can include alphanumerics, a dollar sign and an underscore + + o Can be 1 to 15 characters + + GDE automatically converts region-names to upper-case. + + By default, GDE uses DEFAULT for the default region-name. + +2 Segments + SEGMENT Guidelines + GT.M uses a SEGMENT to define a physical file and access method for + the database stored in that file. A SEGMENT maps to only one UNIX + file. A SEGMENT can be mapped by only one REGION. + + A segment-name: + + o Must begin with an alphabetic character + + o Can include alphanumerics, a dollar sign and an underscore + + o Can be 1 to 15 characters + + GDE automatically converts segment-names to upper-case. + + By default, GDE uses the file name MUMPS for the DEFAULT default + segment. By default, GDE uses the file extension .DAT for database + files. + + diff --git a/sr_port/gde.m b/sr_port/gde.m new file mode 100644 index 0000000..4d2dbdd --- /dev/null +++ b/sr_port/gde.m @@ -0,0 +1,78 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001, 2010 Fidelity Information Services, Inc ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +gde: ;base module - d DEBUG^GDE to debug + i $$set^%LCLCOL(0) + s (debug,runtime)=0 +DBG: ;transfer point for DEBUG and "runtime" %gde + s $et=$s(debug:"b:$zs'[""%GDE""!allerrs ",1:"")_"g:(""%GDE%NONAME""[$p($p($zs,"","",3),""-"")) SHOERR^GDE d ABORT^GDE" + s io=$io,useio="io",comlevel=0,combase=$zl,resume(0)=$zl_":INTERACT" + i $$set^%PATCODE("M") + d GDEINIT^GDEINIT,GDEMSGIN^GDEMSGIN,GDFIND^GDESETGD,CREATE^GDEGET:create,LOAD^GDEGET:'create + i debug s prompt="DEBUGDE>",uself="logfile" + e s prompt="GDE>",uself="logfile:(ctrap=$c(3,25,26):exception=""d CTRL^GDE"")" + e s useio="io:(ctrap=$c(3,25,26):exception=""d CTRL^GDE"")" + u @useio + i $l(comline) d comline,EXIT^GDEEXIT + i runtime s prompt="GD_SHOW>",verb="SHOW",x="" f s x=$o(syntab(x)) q:'$l(x) i x'="SHOW" k syntab(x) +INTERACT + f u io:ctrap=$c(25,26) w !,prompt," " r comline u @useio d comline:$l(comline) + q +comline: s cp=1,ntoken="",ntoktype="TKEOL" s:runtime comline="/"_comline d GETTOK^GDESCAN + i log u @uself w comline,! u @useio + i runtime n NAME,REGION,SEGMENT,gqual,lquals zg:"/QUIT"[$tr(comline,lower,upper) combase-1 d SHOW^GDEPARSE q + i ntoktype="TKAMPER" s resume(comlevel+1)=$zl d comfile q + d GDEPARSE^GDEPARSE + q +CTRL + i $p($zs,",",3,999)["%GTM-E-CTRAP, Character trap $C(3) encountered" do zg @resume(comlevel) + . i comlevel>0 d comeof; if we take a ctrl-c in a command file then get out of that command file + i $p($zs,",",3,999)["%GTM-E-CTRAP, Character trap $C(25) encountered" h + i $p($zs,",",3,999)["%GTM-E-CTRAP, Character trap $C(26) encountered" d EXIT^GDEEXIT + i $p($zs,",",3,999)="%GTM-E-IOEOF, Attempt to read past an end-of-file" d comexit + i $zeof d EXIT^GDEEXIT + d ABORT + ; +comexit: i 'update d QUIT^GDEQUIT + i $$ALL^GDEVERIF,$$GDEPUT^GDEPUT h +DBGCOMX u $i:exception="" s $et="" zm (gdeerr("VERIFY")\2*2):"FAILED" + h +comfile: + d GETTOK^GDESCAN,TFSPEC^GDEPARSE + s (comfile,comfile(comlevel+1))=$zparse(value,"","",".COM") + i '$l($zsearch(comfile)),'$l($zsearch(comfile)) zm gdeerr("FNF"):comfile + e o comfile:(read:exc="zg "_$zl_":comeof") zm gdeerr("EXECOM"):comfile d SCRIPT +comeof c comfile s comlevel=$select(comlevel>1:comlevel-1,1:0) + i comlevel>0 s comfile=comfile(comlevel) zm gdeerr("EXECOM"):comfile + e u @useio + i $p($zs,",",3)'["%GTM-E-IOEOF",$p($zs,",",3)'["FNF" w !,$p($zs,",",3,9999),! + q +SCRIPT: + s comlevel=comlevel+1 + f u comfile r comline i $e(comline,1)'="!" u @useio d comline:$l(comline) + ;this loop is terminated by the comfile exception at eof +SHOERR + w !,$p($zs,",",3,999),! + s comlevel=$s(comlevel>1:comlevel-1,1:0) + s $ecode="" + zg @resume(comlevel) + q +ABORT + s abortzs=$zs,abort="GDEDUMP.DMP",$et="" + o abort:(newversion:noreadonly) u abort zsh "*" c abort + u @useio + ; make GDECHECK error fatal except native UNIX + i $d(gdeerr) zm gdeerr("GDECHECK") Write $ZMessage($Select($ZVersion'["VMS"&(256>abortzs):+abortzs,1:+abortzs\8*8+4)),! + e w $zs + h +DEBUG ;entry point to debug gde + i $$set^%LCLCOL(0) + s allerrs=0,debug=1,runtime=0 u 0:(ctrap="":exception="") zb DBGCOMX,ABORT + g DBG diff --git a/sr_port/gdeadd.m b/sr_port/gdeadd.m new file mode 100644 index 0000000..cb339e5 --- /dev/null +++ b/sr_port/gdeadd.m @@ -0,0 +1,47 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001, 2008 Fidelity Information Services, Inc ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +add: ;implement the verb: ADD +NAME + i $d(nams(NAME)) zm gdeerr("OBJDUP"):"Name":NAME + i '$d(lquals("REGION")) zm gdeerr("QUALREQD"):"Region" + s update=1,nams=nams+1 + s nams(NAME)=lquals("REGION") + q +REGION + n nullsub + i $d(regs(REGION)) zm gdeerr("OBJDUP"):"Region":REGION + i '$d(lquals("DYNAMIC_SEGMENT")) zm gdeerr("QUALREQD"):"Dynamic_segment" + i $d(lquals("JOURNAL")),lquals("JOURNAL"),'$d(lquals("BEFORE_IMAGE")) zm gdeerr("QUALREQD"):"Before_image" + i $d(lquals("NULL_SUBSCRIPTS")) d + . s nullsub=lquals("NULL_SUBSCRIPTS") + . s lquals("NULL_SUBSCRIPTS")=$s((nullsub="ALWAYS")!(nullsub="TRUE"):1,nullsub="EXISTING":2,1:0) + i '$$RQUALS^GDEVERIF(.lquals) zm gdeerr("OBJNOTADD"):"Region":REGION + s update=1,s="",regs=regs+1 + f s s=$o(tmpreg(s)) q:'$l(s) s regs(REGION,s)=tmpreg(s) + f s s=$o(lquals(s)) q:'$l(s) s regs(REGION,s)=lquals(s) i s="ALLOCATION" s regs(REGION,"EXTENSION")=lquals(s) + i $d(segs),$d(regs(REGION,"DYNAMIC_SEGMENT")),$d(segs(regs(REGION,"DYNAMIC_SEGMENT"),"ACCESS_METHOD")) d + . i "MM"=segs(regs(REGION,"DYNAMIC_SEGMENT"),"ACCESS_METHOD"),'$d(lquals("BEFORE_IMAGE")) s regs(REGION,"BEFORE_IMAGE")=0 + q +SEGMENT + i $d(segs(SEGMENT)) zm gdeerr("OBJDUP"):"Segment":SEGMENT + i '$d(lquals("FILE_NAME")) zm gdeerr("QUALREQD"):"File" + s am=$s($d(lquals("ACCESS_METHOD")):lquals("ACCESS_METHOD"),1:tmpacc) + i '$$SQUALS^GDEVERIF(am,.lquals) zm gdeerr("OBJNOTADD"):"Segment":SEGMENT + s update=1,s="",segs=segs+1 + s segs(SEGMENT,"ACCESS_METHOD")=am + i "MM"=am s s="" f s s=$o(regs(s)) q:'$l(s) d + . i regs(s,"DYNAMIC_SEGMENT")=SEGMENT,'$d(lquals("BEFORE_IMAGE")) s regs(s,"BEFORE_IMAGE")=0 + d seg + f s s=$o(lquals(s)) q:'$l(s) s segs(SEGMENT,s)=lquals(s) + q +seg: s segs(SEGMENT,"FILE_NAME")=lquals("FILE_NAME") + f s s=$o(tmpseg(am,s)) q:'$l(s) s segs(SEGMENT,s)=tmpseg(am,s) + q diff --git a/sr_port/gdechang.m b/sr_port/gdechang.m new file mode 100644 index 0000000..b722302 --- /dev/null +++ b/sr_port/gdechang.m @@ -0,0 +1,42 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001, 2008 Fidelity Information Services, Inc ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +change: ;implement the verb: CHANGE +NAME + i '$d(nams(NAME)) zm gdeerr("OBJNOTFND"):"Name":NAME + i '$d(lquals("REGION")) zm gdeerr("QUALREQD"):"Region" + s update=1 + s nams(NAME)=lquals("REGION") + q +REGION + n nullsub + i '$d(regs(REGION)) zm gdeerr("OBJNOTFND"):"Region":REGION + i $d(lquals("JOURNAL")),lquals("JOURNAL"),'regs(REGION,"JOURNAL"),'$d(lquals("BEFORE_IMAGE")) d + . zm gdeerr("QUALREQD"):"Before_image" + i $d(lquals("NULL_SUBSCRIPTS")) d + . s nullsub=lquals("NULL_SUBSCRIPTS") + . s lquals("NULL_SUBSCRIPTS")=$s((nullsub="ALWAYS")!(nullsub="TRUE"):1,nullsub="EXISTING":2,1:0) + i '$$RQUALS^GDEVERIF(.lquals) zm gdeerr("OBJNOTCHG"):"region":REGION + s update=1,s="" + f s s=$o(lquals(s)) q:'$l(s) s regs(REGION,s)=lquals(s) i s="ALLOCATION" s regs(REGION,"EXTENSION")=lquals(s) + q +SEGMENT + i '$d(segs(SEGMENT)) zm gdeerr("OBJNOTFND"):"Segment":SEGMENT + s am=$s($d(lquals("ACCESS_METHOD")):lquals("ACCESS_METHOD"),1:segs(SEGMENT,"ACCESS_METHOD")) + i '$$SQUALS^GDEVERIF(am,.lquals) zm gdeerr("OBJNOTCHG"):"segment":SEGMENT + s update=1,s="" + s segs(SEGMENT,"ACCESS_METHOD")=am + f s s=$o(lquals(s)) q:'$l(s) s segs(SEGMENT,s)=lquals(s) + f s s=$o(tmpseg(am,s)) q:'$l(s) d + . i '$l(tmpseg(am,s)) s segs(SEGMENT,s)="" q + . i '$l(segs(SEGMENT,s)) s segs(SEGMENT,s)=tmpseg(am,s) + i "MM"=am,"MM"'=tmpacc s s="" f s s=$o(regs(s)) q:'$l(s) d + . i regs(s,"DYNAMIC_SEGMENT")=SEGMENT,'$d(lquals("BEFORE_IMAGE")) s regs(s,"BEFORE_IMAGE")=0 + q diff --git a/sr_port/gdedelet.m b/sr_port/gdedelet.m new file mode 100644 index 0000000..a4883e7 --- /dev/null +++ b/sr_port/gdedelet.m @@ -0,0 +1,24 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +delete: ;implement the verb: DELETE +NAME + i '$d(nams(NAME)) zm gdeerr("OBJNOTFND"):"Name":NAME + i NAME="*" zm gdeerr("LVSTARALON") + s update=1 k nams(NAME) s nams=nams-1 + q +REGION + i '$d(regs(REGION)) zm gdeerr("OBJNOTFND"):"Region":REGION + s update=1 k regs(REGION) s regs=regs-1 + q +SEGMENT + i '$d(segs(SEGMENT)) zm gdeerr("OBJNOTFND"):"Segment":SEGMENT + s update=1 k segs(SEGMENT) s segs=segs-1 + q diff --git a/sr_port/gdeerrors.msg b/sr_port/gdeerrors.msg new file mode 100644 index 0000000..0feb0a0 --- /dev/null +++ b/sr_port/gdeerrors.msg @@ -0,0 +1,71 @@ +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! ! +! Copyright 2001, 2009 Fidelity Information Services, Inc ! +! ! +! This source code contains the intellectual property ! +! of its copyright holder(s), and is made available ! +! under a license. If you do not know the terms of ! +! the license, please stop and do not read further. ! +! ! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + .FACILITY GDE, 248 /PREFIX = GDE_ + .TITLE GDEERRORS Error Messages for GDE + +BLKSIZ512 /info/fao=4 +EXECOM /info/fao=2 +FNF /info/fao=2 +GDCREATE /info/fao=2 +GDECHECK /info/fao=0 +GDUNKNFMT /info/fao=2 +GDUPDATE /info/fao=2 +GDUSEDEFS /info/fao=2 +ILLCHAR /error/fao=2 +INPINTEG /fatal/fao=0 +KEYTOOBIG /info/fao=4 +KEYSIZIS /info/fao=2 +KEYWRDAMB /error/fao=4 +KEYWRDBAD /error/fao=4 +LOADGD /info/fao=2 +LOGOFF /info/fao=2 +LOGON /info/fao=2 +LVSTARALON /error/fao=0 +MAPBAD /info/fao=8 +MAPDUP /info/fao=10 +NAMSTARTBAD /error/fao=2 +NOACTION /info/fao=2 +RPAREN /error/fao=0 +NOEXIT /info/fao=0 +NOLOG /info/fao=2 +NOVALUE /error/fao=2 +NONEGATE /error/fao=2 +OBJDUP /error/fao=4 +OBJNOTADD /error/fao=4 +OBJNOTCHG /error/fao=4 +OBJNOTFND /error/fao=4 +OBJREQD /error/fao=2 +PREFIXBAD /error/fao=4 +QUALBAD /error/fao=2 +QUALDUP /error/fao=2 +QUALREQD /error/fao=2 +RECTOOBIG /info/fao=6 +RECSIZIS /info/fao=2 +REGIS /info/fao=2 +SEGIS /info/fao=4 +VALTOOBIG /info/fao=6 +VALTOOLONG /error/fao=6 +VALTOOSMALL /info/fao=6 +VALUEBAD /error/fao=4 +VALUEREQD /error/fao=2 +VERIFY /info/fao=2 +BUFSIZIS /info/fao=2 +BUFTOOSMALL /info/fao=4 +MMNOBEFORIMG /info/fao=0 +NOJNL /info/fao=2 +GDREADERR /info/fao=2 +GDNOTSET /info/fao=0 +INVGBLDIR /info/fao=4 +WRITEERROR /info/fao=2 +NONASCII /error/fao=4 +ENCNOMM /error/fao=2 + .end diff --git a/sr_port/gdeexit.m b/sr_port/gdeexit.m new file mode 100644 index 0000000..cdf0a3e --- /dev/null +++ b/sr_port/gdeexit.m @@ -0,0 +1,16 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +exit: ;implement the verb: EXIT +EXIT + i 'update d QUIT^GDEQUIT + i '$$ALL^GDEVERIF zm gdeerr("NOEXIT") q + i '$$GDEPUT^GDEPUT q ; zm is issued in GDEPUT.m + h diff --git a/sr_port/gdehelp.m b/sr_port/gdehelp.m new file mode 100644 index 0000000..899ad35 --- /dev/null +++ b/sr_port/gdehelp.m @@ -0,0 +1,18 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +help: ;implement the verb: HELP +HELP + n helpline + s helpline=$e(comline,cp-$l(ntoken),999) + u io:ctrap=$c(3,25) + zh helpline:helpfile + u @useio + q diff --git a/sr_port/gdeinit.m b/sr_port/gdeinit.m new file mode 100644 index 0000000..856c142 --- /dev/null +++ b/sr_port/gdeinit.m @@ -0,0 +1,354 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001, 2010 Fidelity Information Services, Inc ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +gdeinit: ;set up local variables and arrays +GDEINIT + i $ZVER'["VMS" view "BADCHAR" + s renpref="" + s log=0,logfile="GDELOG.LOG",BOL="" + s ZERO=$c(0),ONE=$c(1),TRUE=ONE,FALSE=ZERO,TAB=$c(9) + s endian("VAX","VMS")=FALSE,glo("VMS")=1024 + s endian("AXP","VMS")=FALSE,endian("AXP","OSF1")=FALSE,glo("VMS")=1024,glo("OSF1")=1024 + s endian("x86","SCO")=FALSE,endian("x86","UWIN")=FALSE,endian("x86","Linux")=FALSE,endian("x86","CYGWIN")=FALSE + s endian("x86_64","Linux")=FALSE + s glo("SCO")=384,glo("UWIN")=1024,glo("Linux")=1024,glo("CYGWIN")=1024 + s endian("SEQUOIA_SERIES_400","VAX")=TRUE,glo("VAX")=1024 + s endian("HP-PA","HP-UX")=TRUE,glo("HP-UX")=1024 + s endian("IA64","HP-UX")=TRUE,glo("HP-UX")=1024 + s endian("IA64","Linux")=FALSE,glo("Linux")=1024 + s endian("SPARC","SUN/OS_V4.x")=TRUE,endian("SPARC","Solaris")=TRUE,glo("SUN/OS_V4.x")=800,glo("Solaris")=1024 + s endian("MIPS","A25")=TRUE,glo("A25")=1024 + s endian("B30","NONSTOP-UX")=TRUE,glo("NONSTOP-UX")=1024 + s endian("B32","NONSTOP-UX")=TRUE,glo("NONSTOP-UX")=1024 + s endian("MC-680x0","SYS_V/68_R3V6")=TRUE,endian("MC-680x0","TOPIX")=TRUE,glo("SYS_V/68_R3V6")=1024,glo("TOPIX")=1024 + s endian("RS6000","AIX")=TRUE,glo("AIX")=1024 + s endian("S390","OS390")=TRUE,endian("S390X","Linux")=TRUE,glo("OS390")=1024 + ; The following line is for support of AIX V3.2.5 only and can (and should) + ; be removed (along with this comment) as soon as we drop support for + ; AIX V3.2.5. This change is needed to correspond to the change in + ; release_name.h. C9801-000344 + s glo("AIX325")=glo("AIX") + s HEX(0)=1 + s gtm64=$p($zver," ",4) + i "/IA64/RS6000/SPARC/x86_64/x86/S390/S390X"[("/"_gtm64) s encsupportedplat=TRUE,gtm64=$s("x86"=gtm64:FALSE,1:TRUE) + e s (encsupportedplat,gtm64)=FALSE + i (gtm64=TRUE) f x=1:1:16 s HEX(x)=HEX(x-1)*16 i x#2=0 s TWO(x*4)=HEX(x) + e f x=1:1:8 s HEX(x)=HEX(x-1)*16 i x#2=0 s TWO(x*4)=HEX(x) + f i=25:1:28 s TWO(i)=TWO(i-1)*2 + s TWO(31)=TWO(32)*.5 + s lower="abcdefghijklmnopqrstuvwxyz",upper="ABCDEFGHIJKLMNOPQRSTUVWXYZ" + s endian=endian($p($zver," ",4),$p($zver," ",3)) + s ver=$p($zver," ",3) + s defglo=glo(ver) + s comline=$zcmdline + s nullsubs="\NEVER\FALSE\ALWAYS\TRUE\EXISTING" + s nommbi=1 ; this is used in gdeverif and should be removed along with the code when support is added + d UNIX:ver'="VMS" + d VMS:ver="VMS" + d syntabi +; + i (gtm64=FALSE) d + . s SIZEOF("am_offset")=324 + . s SIZEOF("file_spec")=256 + . s SIZEOF("gd_header")=16 + . s SIZEOF("gd_contents")=44 + . s SIZEOF("gd_map")=36 + . s SIZEOF("gd_region")=332 + . if ver'="VMS" s SIZEOF("gd_segment")=340 + . e s SIZEOF("gd_segment")=336 + e d + . s SIZEOF("am_offset")=332 + . s SIZEOF("file_spec")=256 + . s SIZEOF("gd_header")=16 + . s SIZEOF("gd_contents")=80 + . s SIZEOF("gd_map")=40 + . s SIZEOF("gd_region")=344 + . s SIZEOF("gd_segment")=360 + s SIZEOF("mident")=32 + s SIZEOF("blk_hdr")=16 + s SIZEOF("rec_hdr")=3 + s SIZEOF("dsk_blk")=512 + s SIZEOF("max_str")=32767 + s MAXNAMLN=SIZEOF("mident")-1,MAXREGLN=32,MAXSEGLN=32 ; maximum name length allowed is 31 characters + s PARNAMLN=31,PARREGLN=31,PARSEGLN=31 +; +; tokens are used for error reporting only + s tokens("TKIDENT")="identifier" + s tokens("TKNUMLIT")="number" + s tokens("TKEOL")="end-of-line" + s tokens("""")="TKSTRLIT",tokens("TKSTRLIT")="string literal" + s tokens("@")="TKAMPER",tokens("TKAMPER")="ampersand" + s tokens("*")="TKASTER",tokens("TKASTER")="asterisk" + s tokens(":")="TKCOLON",tokens("TKCOLON")="colon" + s tokens(",")="TKCOMMA",tokens("TKCOMMA")="comma" + s tokens("$")="TKDOLLAR",tokens("TKDOLLAR")="dollar sign" + s tokens("=")="TKEQUAL",tokens("TKEQUAL")="equal sign" + s tokens("<")="TKLANGLE",tokens("TKLANGLE")="left angle bracket" + s tokens("[")="TKLBRACK",tokens("TKLBRACK")="left bracket" + s tokens("(")="TKLPAREN",tokens("TKLPAREN")="left parenthesis" + s tokens("-")="TKDASH",tokens("TKDASH")="dash" + s tokens("%")="TKPCT",tokens("TKPCT")="percent sign" + s tokens(".")="TKPERIOD",tokens("TKPERIOD")="period" + s tokens(")")="TKRPAREN",tokens("TKRPAREN")="right parenthesis" + s tokens("]")="TKRBRACK",tokens("TKRBRACK")="right bracket" + s tokens(">")="TKRANGLE",tokens("TKRANGLE")="right angle bracket" + s tokens(";")="TKSCOLON",tokens("TKSCOLON")="semicolon" + s tokens("/")="TKSLASH",tokens("TKSLASH")="slash" + s tokens("_")="TKUSCORE",tokens("TKUSCORE")="underscore" + s tokens("!")="TKEXCLAM",tokens("TKEXCLAM")="exclamation point" + s tokens("TKOTHER")="other" +; maximums and mimimums +; region + s minreg("ALLOCATION")=$s(ver'="VMS":200,1:10) + s minreg("BEFORE_IMAGE")=0,minreg("COLLATION_DEFAULT")=0,minreg("STDNULLCOLL")=0 + s minreg("EXTENSION")=0 + s minreg("JOURNAL")=0,minreg("KEY_SIZE")=3,minreg("NULL_SUBSCRIPTS")=0 + s minreg("RECORD_SIZE")=SIZEOF("rec_hdr")+4 + s maxreg("ALLOCATION")=TWO(24),maxreg("BEFORE_IMAGE")=1,maxreg("BUFFER_SIZE")=2000 + s maxreg("COLLATION_DEFAULT")=255,maxreg("STDNULLCOLL")=1,maxreg("EXTENSION")=HEX(4)-1 + s maxreg("JOURNAL")=1,maxreg("KEY_SIZE")=255,maxreg("NULL_SUBSCRIPTS")=2 + s maxreg("RECORD_SIZE")=SIZEOF("max_str") +; segments +; bg + s minseg("BG","ALLOCATION")=10,minseg("BG","BLOCK_SIZE")=SIZEOF("dsk_blk"),minseg("BG","EXTENSION_COUNT")=0 + s minseg("BG","GLOBAL_BUFFER_COUNT")=64,minseg("BG","LOCK_SPACE")=10,minseg("BG","RESERVED_BYTES")=0 + s maxseg("BG","ALLOCATION")=TWO(27),(maxseg("BG","BLOCK_SIZE"),maxseg("BG","RESERVED_BYTES"))=HEX(4)-SIZEOF("dsk_blk") + i ver'="VMS" s maxseg("BG","ALLOCATION")=TWO(28)-TWO(25) ; supports 224M blocks for UNIX only + s maxseg("BG","EXTENSION_COUNT")=HEX(4)-1,maxseg("BG","LOCK_SPACE")=65536 + i (gtm64=TRUE) s maxseg("BG","GLOBAL_BUFFER_COUNT")=2147483647 ; 2G-1 + e s maxseg("BG","GLOBAL_BUFFER_COUNT")=65536 +; mm + s minseg("MM","ALLOCATION")=10,minseg("MM","BLOCK_SIZE")=SIZEOF("dsk_blk"),minseg("MM","DEFER")=0 + s minseg("MM","LOCK_SPACE")=10,minseg("MM","EXTENSION_COUNT")=0,minseg("MM","RESERVED_BYTES")=0 + s maxseg("MM","ALLOCATION")=TWO(27),(maxseg("MM","BLOCK_SIZE"),maxseg("BG","RESERVED_BYTES"))=HEX(4)-SIZEOF("dsk_blk") + i ver'="VMS" s maxseg("MM","ALLOCATION")=TWO(28)-TWO(25) ; supports 224M blocks for UNIX only + s maxseg("MM","DEFER")=86400,maxseg("MM","LOCK_SPACE")=1000,maxseg("MM","EXTENSION_COUNT")=HEX(4)-1 + q + +;----------------------------------------------------------------------------------------------------------------------------------- + +; gde command language syntax table +syntabi: + s syntab("ADD","NAME")="" + s syntab("ADD","NAME","REGION")="REQUIRED" + s syntab("ADD","NAME","REGION","TYPE")="TREGION" + s syntab("ADD","REGION")="" + s syntab("ADD","REGION","COLLATION_DEFAULT")="REQUIRED" + s syntab("ADD","REGION","COLLATION_DEFAULT","TYPE")="TNUMBER" + s syntab("ADD","REGION","STDNULLCOLL")="NEGATABLE" + s syntab("ADD","REGION","DYNAMIC_SEGMENT")="REQUIRED" + s syntab("ADD","REGION","DYNAMIC_SEGMENT","TYPE")="TSEGMENT" + s syntab("ADD","REGION","JOURNAL")="NEGATABLE,REQUIRED,LIST" + s syntab("ADD","REGION","JOURNAL","ALLOCATION")="REQUIRED" + s syntab("ADD","REGION","JOURNAL","ALLOCATION","TYPE")="TNUMBER" + s syntab("ADD","REGION","JOURNAL","BUFFER_SIZE")="REQUIRED" + s syntab("ADD","REGION","JOURNAL","BUFFER_SIZE","TYPE")="TNUMBER" + s syntab("ADD","REGION","JOURNAL","BEFORE_IMAGE")="NEGATABLE" + s syntab("ADD","REGION","JOURNAL","EXTENSION")="REQUIRED" + s syntab("ADD","REGION","JOURNAL","EXTENSION","TYPE")="TNUMBER" + s syntab("ADD","REGION","JOURNAL","FILE_NAME")="REQUIRED" + s syntab("ADD","REGION","JOURNAL","FILE_NAME","TYPE")="TFSPEC" + ;s syntab("ADD","REGION","JOURNAL","STOP_ENABLED")="NEGATABLE" + s syntab("ADD","REGION","KEY_SIZE")="REQUIRED" + s syntab("ADD","REGION","KEY_SIZE","TYPE")="TNUMBER" + s syntab("ADD","REGION","NULL_SUBSCRIPTS")="NEGATABLE,REQUIRED" + s syntab("ADD","REGION","NULL_SUBSCRIPTS","TYPE")="TNULLSUB" + s syntab("ADD","REGION","NULL_SUBSCRIPTS","TYPE","VALUES")=nullsubs + s syntab("ADD","REGION","RECORD_SIZE")="REQUIRED" + s syntab("ADD","REGION","RECORD_SIZE","TYPE")="TNUMBER" + s syntab("ADD","SEGMENT")="" + s syntab("ADD","SEGMENT","ACCESS_METHOD")="REQUIRED" + s syntab("ADD","SEGMENT","ACCESS_METHOD","TYPE")="TACCMETH" + s syntab("ADD","SEGMENT","ACCESS_METHOD","TYPE","VALUES")=accmeth + s syntab("ADD","SEGMENT","ALLOCATION")="REQUIRED" + s syntab("ADD","SEGMENT","ALLOCATION","TYPE")="TNUMBER" + s syntab("ADD","SEGMENT","BLOCK_SIZE")="REQUIRED" + s syntab("ADD","SEGMENT","BLOCK_SIZE","TYPE")="TNUMBER" + s syntab("ADD","SEGMENT","BUCKET_SIZE")="REQUIRED" + s syntab("ADD","SEGMENT","BUCKET_SIZE","TYPE")="TNUMBER" + s syntab("ADD","SEGMENT","DEFER")="NEGATABLE" + i ver'="VMS" s syntab("ADD","SEGMENT","ENCRYPTION_FLAG")="NEGATABLE" + s syntab("ADD","SEGMENT","EXTENSION_COUNT")="REQUIRED" + s syntab("ADD","SEGMENT","EXTENSION_COUNT","TYPE")="TNUMBER" + s syntab("ADD","SEGMENT","FILE_NAME")="REQUIRED" + s syntab("ADD","SEGMENT","FILE_NAME","TYPE")="TFSPEC" + s syntab("ADD","SEGMENT","GLOBAL_BUFFER_COUNT")="REQUIRED" + s syntab("ADD","SEGMENT","GLOBAL_BUFFER_COUNT","TYPE")="TNUMBER" + s syntab("ADD","SEGMENT","LOCK_SPACE")="REQUIRED" + s syntab("ADD","SEGMENT","LOCK_SPACE","TYPE")="TNUMBER" + s syntab("ADD","SEGMENT","RESERVED_BYTES")="REQUIRED" + s syntab("ADD","SEGMENT","RESERVED_BYTES","TYPE")="TNUMBER" + s syntab("ADD","SEGMENT","WINDOW_SIZE")="REQUIRED" + s syntab("ADD","SEGMENT","WINDOW_SIZE","TYPE")="TNUMBER" + s syntab("CHANGE","NAME")="" + s syntab("CHANGE","NAME","REGION")="REQUIRED" + s syntab("CHANGE","NAME","REGION","TYPE")="TREGION" + s syntab("CHANGE","REGION")="" + s syntab("CHANGE","REGION","COLLATION_DEFAULT")="REQUIRED" + s syntab("CHANGE","REGION","COLLATION_DEFAULT","TYPE")="TNUMBER" + s syntab("CHANGE","REGION","STDNULLCOLL")="NEGATABLE" + s syntab("CHANGE","REGION","DYNAMIC_SEGMENT")="REQUIRED" + s syntab("CHANGE","REGION","DYNAMIC_SEGMENT","TYPE")="TSEGMENT" + s syntab("CHANGE","REGION","JOURNAL")="NEGATABLE,REQUIRED,LIST" + s syntab("CHANGE","REGION","JOURNAL","ALLOCATION")="REQUIRED" + s syntab("CHANGE","REGION","JOURNAL","ALLOCATION","TYPE")="TNUMBER" + s syntab("CHANGE","REGION","JOURNAL","BUFFER_SIZE")="REQUIRED" + s syntab("CHANGE","REGION","JOURNAL","BUFFER_SIZE","TYPE")="TNUMBER" + s syntab("CHANGE","REGION","JOURNAL","BEFORE_IMAGE")="NEGATABLE" + s syntab("CHANGE","REGION","JOURNAL","EXTENSION")="REQUIRED" + s syntab("CHANGE","REGION","JOURNAL","EXTENSION","TYPE")="TNUMBER" + s syntab("CHANGE","REGION","JOURNAL","FILE_NAME")="REQUIRED" + s syntab("CHANGE","REGION","JOURNAL","FILE_NAME","TYPE")="TFSPEC" + ;s syntab("CHANGE","REGION","JOURNAL","STOP_ENABLED")="NEGATABLE" + s syntab("CHANGE","REGION","KEY_SIZE")="REQUIRED" + s syntab("CHANGE","REGION","KEY_SIZE","TYPE")="TNUMBER" + s syntab("CHANGE","REGION","NULL_SUBSCRIPTS")="NEGATABLE,REQUIRED" + s syntab("CHANGE","REGION","NULL_SUBSCRIPTS","TYPE")="TNULLSUB" + s syntab("CHANGE","REGION","NULL_SUBSCRIPTS","TYPE","VALUES")=nullsubs + s syntab("CHANGE","REGION","RECORD_SIZE")="REQUIRED" + s syntab("CHANGE","REGION","RECORD_SIZE","TYPE")="TNUMBER" + s syntab("CHANGE","SEGMENT")="" + s syntab("CHANGE","SEGMENT","ACCESS_METHOD")="REQUIRED" + s syntab("CHANGE","SEGMENT","ACCESS_METHOD","TYPE")="TACCMETH" + s syntab("CHANGE","SEGMENT","ACCESS_METHOD","TYPE","VALUES")=accmeth + s syntab("CHANGE","SEGMENT","ALLOCATION")="REQUIRED" + s syntab("CHANGE","SEGMENT","ALLOCATION","TYPE")="TNUMBER" + s syntab("CHANGE","SEGMENT","BLOCK_SIZE")="REQUIRED" + s syntab("CHANGE","SEGMENT","BLOCK_SIZE","TYPE")="TNUMBER" + s syntab("CHANGE","SEGMENT","BUCKET_SIZE")="REQUIRED" + s syntab("CHANGE","SEGMENT","BUCKET_SIZE","TYPE")="TNUMBER" + s syntab("CHANGE","SEGMENT","DEFER")="NEGATABLE" + i ver'="VMS" s syntab("CHANGE","SEGMENT","ENCRYPTION_FLAG")="NEGATABLE" + s syntab("CHANGE","SEGMENT","EXTENSION_COUNT")="REQUIRED" + s syntab("CHANGE","SEGMENT","EXTENSION_COUNT","TYPE")="TNUMBER" + s syntab("CHANGE","SEGMENT","FILE_NAME")="REQUIRED" + s syntab("CHANGE","SEGMENT","FILE_NAME","TYPE")="TFSPEC" + s syntab("CHANGE","SEGMENT","GLOBAL_BUFFER_COUNT")="REQUIRED" + s syntab("CHANGE","SEGMENT","GLOBAL_BUFFER_COUNT","TYPE")="TNUMBER" + s syntab("CHANGE","SEGMENT","LOCK_SPACE")="REQUIRED" + s syntab("CHANGE","SEGMENT","LOCK_SPACE","TYPE")="TNUMBER" + s syntab("CHANGE","SEGMENT","RESERVED_BYTES")="REQUIRED" + s syntab("CHANGE","SEGMENT","RESERVED_BYTES","TYPE")="TNUMBER" + s syntab("CHANGE","SEGMENT","WINDOW_SIZE")="REQUIRED" + s syntab("CHANGE","SEGMENT","WINDOW_SIZE","TYPE")="TNUMBER" + s syntab("TEMPLATE","REGION")="" + s syntab("TEMPLATE","REGION","COLLATION_DEFAULT")="REQUIRED" + s syntab("TEMPLATE","REGION","COLLATION_DEFAULT","TYPE")="TNUMBER" + s syntab("TEMPLATE","REGION","STDNULLCOLL")="NEGATABLE" + s syntab("TEMPLATE","REGION","DYNAMIC_SEGMENT")="REQUIRED" + s syntab("TEMPLATE","REGION","DYNAMIC_SEGMENT","TYPE")="TSEGMENT" + s syntab("TEMPLATE","REGION","JOURNAL")="NEGATABLE,REQUIRED,LIST" + s syntab("TEMPLATE","REGION","JOURNAL","ALLOCATION")="REQUIRED" + s syntab("TEMPLATE","REGION","JOURNAL","ALLOCATION","TYPE")="TNUMBER" + s syntab("TEMPLATE","REGION","JOURNAL","BUFFER_SIZE")="REQUIRED" + s syntab("TEMPLATE","REGION","JOURNAL","BUFFER_SIZE","TYPE")="TNUMBER" + s syntab("TEMPLATE","REGION","JOURNAL","BEFORE_IMAGE")="NEGATABLE" + s syntab("TEMPLATE","REGION","JOURNAL","EXTENSION")="REQUIRED" + s syntab("TEMPLATE","REGION","JOURNAL","EXTENSION","TYPE")="TNUMBER" + s syntab("TEMPLATE","REGION","JOURNAL","FILE_NAME")="REQUIRED" + s syntab("TEMPLATE","REGION","JOURNAL","FILE_NAME","TYPE")="TFSPEC" + ;s syntab("TEMPLATE","REGION","JOURNAL","STOP_ENABLED")="NEGATABLE" + s syntab("TEMPLATE","REGION","KEY_SIZE")="REQUIRED" + s syntab("TEMPLATE","REGION","KEY_SIZE","TYPE")="TNUMBER" + s syntab("TEMPLATE","REGION","NULL_SUBSCRIPTS")="NEGATABLE,REQUIRED" + s syntab("TEMPLATE","REGION","NULL_SUBSCRIPTS","TYPE")="TNULLSUB" + s syntab("TEMPLATE","REGION","NULL_SUBSCRIPTS","TYPE","VALUES")=nullsubs + s syntab("TEMPLATE","REGION","RECORD_SIZE")="REQUIRED" + s syntab("TEMPLATE","REGION","RECORD_SIZE","TYPE")="TNUMBER" + s syntab("TEMPLATE","SEGMENT")="" + s syntab("TEMPLATE","SEGMENT","ACCESS_METHOD")="REQUIRED" + s syntab("TEMPLATE","SEGMENT","ACCESS_METHOD","TYPE")="TACCMETH" + s syntab("TEMPLATE","SEGMENT","ACCESS_METHOD","TYPE","VALUES")=accmeth + s syntab("TEMPLATE","SEGMENT","ALLOCATION")="REQUIRED" + s syntab("TEMPLATE","SEGMENT","ALLOCATION","TYPE")="TNUMBER" + s syntab("TEMPLATE","SEGMENT","BLOCK_SIZE")="REQUIRED" + s syntab("TEMPLATE","SEGMENT","BLOCK_SIZE","TYPE")="TNUMBER" + s syntab("TEMPLATE","SEGMENT","BUCKET_SIZE")="REQUIRED" + s syntab("TEMPLATE","SEGMENT","BUCKET_SIZE","TYPE")="TNUMBER" + s syntab("TEMPLATE","SEGMENT","DEFER")="NEGATABLE" + i ver'="VMS" s syntab("TEMPLATE","SEGMENT","ENCRYPTION_FLAG")="NEGATABLE" + s syntab("TEMPLATE","SEGMENT","EXTENSION_COUNT")="REQUIRED" + s syntab("TEMPLATE","SEGMENT","EXTENSION_COUNT","TYPE")="TNUMBER" + s syntab("TEMPLATE","SEGMENT","FILE_NAME")="REQUIRED" + s syntab("TEMPLATE","SEGMENT","FILE_NAME","TYPE")="TFSPEC" + s syntab("TEMPLATE","SEGMENT","GLOBAL_BUFFER_COUNT")="REQUIRED" + s syntab("TEMPLATE","SEGMENT","GLOBAL_BUFFER_COUNT","TYPE")="TNUMBER" + s syntab("TEMPLATE","SEGMENT","LOCK_SPACE")="REQUIRED" + s syntab("TEMPLATE","SEGMENT","LOCK_SPACE","TYPE")="TNUMBER" + s syntab("TEMPLATE","SEGMENT","RESERVED_BYTES")="REQUIRED" + s syntab("TEMPLATE","SEGMENT","RESERVED_BYTES","TYPE")="TNUMBER" + s syntab("TEMPLATE","SEGMENT","WINDOW_SIZE")="REQUIRED" + s syntab("TEMPLATE","SEGMENT","WINDOW_SIZE","TYPE")="TNUMBER" + s syntab("DELETE","NAME")="" + s syntab("DELETE","REGION")="" + s syntab("DELETE","SEGMENT")="" + s syntab("EXIT")="" + s syntab("HELP")="" + s syntab("LOCKS","REGION")="REQUIRED" + s syntab("LOCKS","REGION","TYPE")="TREGION" + s syntab("LOG","OFF")="" + s syntab("LOG","ON")="OPTIONAL" + s syntab("LOG","ON","TYPE")="TFSPEC" + s syntab("SETGD","FILE")="REQUIRED" + s syntab("SETGD","FILE","TYPE")="TFSPEC" + s syntab("SETGD","QUIT")="" + s syntab("QUIT")="" + s syntab("RENAME","NAME")="" + s syntab("RENAME","REGION")="" + s syntab("RENAME","SEGMENT")="" + s syntab("SHOW")="" + s syntab("SHOW","ALL")="" + s syntab("SHOW","TEMPLATE")="" + s syntab("SHOW","MAP")="" + s syntab("SHOW","MAP","REGION")="REQUIRED" + s syntab("SHOW","MAP","REGION","TYPE")="TREGION" + s syntab("SHOW","NAME")="" + s syntab("SHOW","REGION")="" + s syntab("SHOW","SEGMENT")="" + s syntab("SHOW","COMMANDS")="" + s syntab("SHOW","COMMANDS","FILE")="OPTIONAL" + s syntab("SHOW","COMMANDS","FILE","TYPE")="TFSPEC" + s syntab("SPAWN")="" + s syntab("VERIFY","ALL")="" + s syntab("VERIFY","MAP")="" + s syntab("VERIFY","NAME")="" + s syntab("VERIFY","REGION")="" + s syntab("VERIFY","SEGMENT")="" + s syntab("VERIFY","TEMPLATE")="" + q +VMS + s endian=FALSE + s hdrlab="GTCGBLDIR009" ; must be concurrently maintained in gbldirnam.h!!! + s tfile="GTM$GBLDIR" + s accmeth="\BG\MM\USER" + s helpfile="GTM$HELP:GDE.HLB" + s defdb="MUMPS" + s defgld="MUMPS.GLD",defgldext=".GLD" + s defreg="$DEFAULT" + s defseg="$DEFAULT" + s dbfilpar=".1AN.1""-"".1""_"".1"":"".1""$"".1""["".1""]"".1""<"".1"">"".1""."".1"";""" + s filexfm="$tr(filespec,lower,upper)" + s sep="TKSLASH" + q + +UNIX: + s hdrlab="GTCGBDUNX006" ; must be concurrently maintained in gbldirnam.h!!! + i (gtm64=TRUE) s hdrlab="GTCGBDUNX106" ; the high order digit is a 64-bit flag + s tfile="$gtmgbldir" + s accmeth="\BG\MM" + s helpfile="$gtm_dist/gdehelp.gld" + s defdb="mumps.dat" + s defgld="mumps.gld",defgldext="*.gld" + s defreg="DEFAULT" + s defseg="DEFAULT" + s dbfilpar="1E" + s filexfm="filespec" + s sep="TKDASH" + q diff --git a/sr_port/gdelocks.m b/sr_port/gdelocks.m new file mode 100644 index 0000000..4d3156f --- /dev/null +++ b/sr_port/gdelocks.m @@ -0,0 +1,15 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +locks: ;implement the verb: LOCKS +LOCKS + s update=1 + s nams("#")=gqual("value") + q diff --git a/sr_port/gdelog.m b/sr_port/gdelog.m new file mode 100644 index 0000000..3d95682 --- /dev/null +++ b/sr_port/gdelog.m @@ -0,0 +1,21 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +log: ;implement the verb: LOG +LOG + i gqual="OFF" s log=0 zm gdeerr("LOGOFF"):logfile q + s log=1 + i $d(gqual("value")) s logfile=$zparse(gqual("value"),"","",".LOG") + o logfile:(newversion:noreadonly) zm gdeerr("LOGON"):logfile + q +INQUIRE + i 'log zm gdeerr("NOLOG"):logfile + e zm gdeerr("LOGON"):logfile + q diff --git a/sr_port/gdemap.m b/sr_port/gdemap.m new file mode 100644 index 0000000..46deb78 --- /dev/null +++ b/sr_port/gdemap.m @@ -0,0 +1,132 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2010 Fidelity Information Services, Inc ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +map: ;create maps for put and show, names for get and show +PUTMAKE + k lexnams n t1 + d SHOWMAKE + s s1=$ztr($zj("",SIZEOF("mident"))," ",$zch(255)),t1=$ztr($zj("",SIZEOF("mident"))," ",$zch(0)) + f s s2=s1,s1=$o(map(s1),-1),map(s2_$ze(t1,$zl(s2)+1,SIZEOF("mident")))=map(s1) q:s1="$" k map(s1) + s map("#)"_$ze(t1,3,SIZEOF("mident")))=map("#)"),map("%"_$ze(t1,2,SIZEOF("mident")))=map("$") + f s2="#","#)","$" k map(s2) + q +;---------------------------------------------------------------------------------------------------------------------------------- +SHOWMAKE + n lexnams s s="" + f s s=$o(nams(s)) q:'$zl(s) d lexins(s) + s map("$")=nams("*"),map("#")=nams("#") + s i=1 + f s i=$o(lexnams(i)) q:'$zl(i) d showstar(i) + s s="" + f s s=$o(lexnams(0,s),-1) q:'$zl(s) d pointins(s,lexnams(0,s)) + s s1=$o(map(""),-1) + f s s2=s1,s1=$o(map(s1),-1) q:s2="$" i map(s1)=map(s2) k map(s2) + q +;---------------------------------------------------------------------------------------------------------------------------------- +SHOWNAM + n lexnams,t1,map + d SHOWMAKE + s s1=$ztr($zj("",SIZEOF("mident"))," ",$zch(255)),t1=$ztr($zj("",SIZEOF("mident"))," ",$zch(0)) + f s s2=s1,s1=$o(map(s1),-1),map(s2)=map(s1) q:s1="$" k map(s1) + k map("#") i '$$MAP2NAM(.map) zm gdeerr("GDECHECK")\2*2 + q +;---------------------------------------------------------------------------------------------------------------------------------- +MAP2NAM(list) + n ar,l,s,x,xl,xn,xp,xt,xv + s x=$o(list("")) q:x'="#)" 0 + s x=$o(list(x)) + i x="%" s list("$")=list("%") k list("%") + e q:x'="$" 0 + s s=$ztr($zj("",SIZEOF("mident"))," ",$zch(255)),x=$o(list(""),-1) q:x'=s 0 + k ar,nams s nams=0,ar(0,s)="",x=$o(list(x),-1) + i x'="$",list(x)'=list(s) s xp="z" f q:xp="" s xn=xp_"*",(nams(xn),ar(1,xn))="",xp=$$lexprev(xp) + f q:x="$" d s x=$o(list(x),-1) + . i $d(list(x_")")) q + . s xl=$zl(x) + . i $ze(x,xl)=")" s xn=$ze(x,1,xl-1),(nams(xn))="" q + . i xl+1'xl&($d(list(xn))!$d(list(xn_")"))) + . i ip,ip'>in!($zl($ztr(x,"z"))&(xl+1=SIZEOF("mident")!'in)) do + . . s xp=x f ip=1:1:ip s xp=$$lexprev(xp),xn=xp_star,(nams(xn),ar(xl,xn))="" + . e s:'in&'$zl($ztr(x,"z")) in=1 s xn=x f in=1:1:in d q:$d(nams(xn))!$d(list(xn_")")) + . . s xp=xn_star,(nams(xp),ar($zl(xn),xp))="",xn=$$lexnext(xn) + s x="",ok=1 + f xl=1:1:SIZEOF("mident") d q:'ok + . f s x=$o(ar(xl,x)) q:x="" d q:'ok + . . s xt=$o(list(x)) + . . i '$zl(xt) s ok=0 q + . . s xv=list(xt) + . . i '$zl(xv) s ok=0 q + . . s xn=$$lexnext($ze(x,1,xl)) + . . f l=xl-1:-1:0 d chkprojection q:$zl(xp) + i 'ok q 0 + f s x=$o(nams(x)) q:x="" s xn=$o(list(x)),nams(x)=list(xn),nams=nams+1 + s xl=SIZEOF("mident")+1,x=$o(ar(xl,"")) + i $zl(x) f s xt=$o(ar(xl,x)) s xv=nams(x),xn=$$lexnext(x) d q:""=xt s x=xt + . f l=xl-3:-1:0 d chkprojection q:$zl(xp) + . i $zl(xt),xt'=xn,$d(nams(x)) f s nams(xn)=nams(x),nams=nams+1,xn=$$lexnext(xn) q:xt']xn + s nams("#")=list("#)"),nams("*")=list("$"),nams=nams+2 + q 1 +;---------------------------------------------------------------------------------------------------------------------------------- +lexins:(s) + n x,l + i s["*" s l=$zl(s),x=$ze(s,1,l),lexnams(l,x)=nams(s) + e s lexnams(0,s)=nams(s) + q +showstar:(i) + s j="" + f s j=$o(lexnams(i,j),-1) q:'$zl(j) d starins($ze(j,1,$zl(j)-1),lexnams(i,j)) + q +starins:(s,reg) + n next + s next=$$lexnext(s) + i $zl(next),'$d(map(next)) s map(next)=map($o(map(next),-1)) + s map(s)=reg + q +pointins:(s,reg) + n next + s next=$$alphnext(s) + i $zl(next),'$d(map(next)) s map(next)=map($o(map(next),-1)) + s map(s)=reg + q +alphnext:(s) + i $zl(s)=MAXNAMLN q $$lexnext(s) + e q s_")" + ; +lexnext:(s) + n len,last,succ + s len=$zl(s),last=$ze(s,len) + i last="z" f s len=len-1,last=$ze(s,len) q:last'="z"!'len + i 'len q "" + s s=$ze(s,1,len-1),succ=$zch($za(last)+1) + i succ?1AN q s_succ + i "A"]succ q s_"A" + i "a"]succ q s_"a" + q "" +lexprev:(s) + n len,last,prio + s len=$zl(s),last=$ze(s,len) + s s=$ze(s,1,len-1),prio=$zch($za(last)-1) + i prio?1AN q s_prio + i prio]"Z" q s_"Z" + i prio]"9" q:len=1 "%" q s_"9" + q "" +chkprojection: ; assumes l,x,xt,xv and may change ok & xp + n xr,xs + s (xp,xs)=$ze(x,1,l) + f s xp=$o(ar(l,xp)) s xr=$s('$zl(xp):"",'l:"$",$ze(xp,1,l)'=xs:xt,1:$o(list(xp))) i xs']xr!'$zl(xr)!'l q + s:'$zl(xr) xp="" q:'$zl(xp) + i xt=xr&(xl>1) s xp=" " q + s xp=list(xr) + i '$zl(xp) s ok=0 q + i xv=xp k nams(x) + q diff --git a/sr_port/gdemsgin.m b/sr_port/gdemsgin.m new file mode 100644 index 0000000..cbdfc02 --- /dev/null +++ b/sr_port/gdemsgin.m @@ -0,0 +1,69 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001, 2009 Fidelity Information Services, Inc ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +gdemsgin: ;message initialization - equate global symbols to numeric values +GDEMSGIN + s gdeerr("BLKSIZ512")=150503435 + s gdeerr("EXECOM")=150503443 + s gdeerr("FNF")=150503451 + s gdeerr("GDCREATE")=150503459 + s gdeerr("GDECHECK")=150503467 + s gdeerr("GDUNKNFMT")=150503475 + s gdeerr("GDUPDATE")=150503483 + s gdeerr("GDUSEDEFS")=150503491 + s gdeerr("ILLCHAR")=150503498 + s gdeerr("INPINTEG")=150503506 + s gdeerr("KEYSIZIS")=150503523 + s gdeerr("KEYTOOBIG")=150503515 + s gdeerr("KEYWRDAMB")=150503530 + s gdeerr("KEYWRDBAD")=150503538 + s gdeerr("LOADGD")=150503547 + s gdeerr("LOGOFF")=150503555 + s gdeerr("LOGON")=150503563 + s gdeerr("LVSTARALON")=150503570 + s gdeerr("MAPBAD")=150503579 + s gdeerr("MAPDUP")=150503587 + s gdeerr("NAMSTARTBAD")=150503594 + s gdeerr("NOACTION")=150503603 + s gdeerr("RPAREN")=150503610 + s gdeerr("NOEXIT")=150503619 + s gdeerr("NOLOG")=150503627 + s gdeerr("NONEGATE")=150503642 + s gdeerr("NOVALUE")=150503634 + s gdeerr("OBJDUP")=150503650 + s gdeerr("OBJNOTADD")=150503658 + s gdeerr("OBJNOTCHG")=150503666 + s gdeerr("OBJNOTFND")=150503674 + s gdeerr("OBJREQD")=150503682 + s gdeerr("PREFIXBAD")=150503690 + s gdeerr("QUALBAD")=150503698 + s gdeerr("QUALDUP")=150503706 + s gdeerr("QUALREQD")=150503714 + s gdeerr("RECSIZIS")=150503731 + s gdeerr("RECTOOBIG")=150503723 + s gdeerr("REGIS")=150503739 + s gdeerr("SEGIS")=150503747 + s gdeerr("VALTOOBIG")=150503755 + s gdeerr("VALTOOLONG")=150503762 + s gdeerr("VALTOOSMALL")=150503771 + s gdeerr("VALUEBAD")=150503778 + s gdeerr("VALUEREQD")=150503786 + s gdeerr("VERIFY")=150503795 + s gdeerr("BUFSIZIS")=150503803 + s gdeerr("BUFTOOSMALL")=150503811 + s gdeerr("MMNOBEFORIMG")=150503819 + s gdeerr("NOJNL")=150503827 + s gdeerr("GDREADERR")=150503835 + s gdeerr("GDNOTSET")=150503843 + s gdeerr("INVGBLDIR")=150503851 + s gdeerr("WRITEERROR")=150503859 + s gdeerr("NONASCII")=150503866 + s gdeerr("ENCNOMM")=150503874 + q diff --git a/sr_port/gdeparse.m b/sr_port/gdeparse.m new file mode 100644 index 0000000..9e8668c --- /dev/null +++ b/sr_port/gdeparse.m @@ -0,0 +1,210 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2010 Fidelity Information Services, Inc ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +gdeparse: ;command parser +GDEPARSE + n verb,NAME,REGION,SEGMENT,gqual,lquals + d matchtok("TKIDENT","Verb") s verb=token + d checkkw(.verb,"verb","syntab") + d @verb + q +qual(qual,ent,s) + i ntoktype="TKEOL" zm gdeerr("QUALREQD"):ent + d matchtok(sep,ent),matchtok("TKIDENT",ent) + s qual=token d checkkw(.qual,ent,s) s t=@s@(qual) + i negated,t'["NEGATABLE" zm gdeerr("NONEGATE"):qual + i t["REQUIRED",ntoktype'="TKEQUAL",'negated zm gdeerr("VALUEREQD"):qual + i "NEGATABLE"[t!negated,ntoktype="TKEQUAL" zm gdeerr("NOVALUE"):qual + i t["NEGATABLE" s qual("value")='negated + i ntoktype="TKEQUAL",t'["LIST" s qual("value")=$$getvalue(s,qual) q + i ntoktype="TKEQUAL" d list(qual) + q +getvalue:(s,qual) + d matchtok("TKEQUAL","Value") + i ntoktype="TKEOL" zm gdeerr("VALUEREQD"):qual + d @@s@(qual,"TYPE") + q value + ; +list:(lhead) + n sep + s tmp=lqual,v=lqual("value") + i $e(comline,cp)="(" d GETTOK^GDESCAN + s sep=ntoktype d getlitm + i sep="TKLPAREN" s sep=ntoktype f q:ntoktype="TKRPAREN" zm:"TKRPAREN|TKCOMMA"'[ntoktype gdeerr("RPAREN") d getlitm + i ntoktype="TKRPAREN" d GETTOK^GDESCAN + s lqual=tmp,lqual("value")=v + q +TNUMBER + d GETTOK^GDESCAN + i toktype'="TKNUMLIT" zm gdeerr("VALUEBAD"):token:"number" + i $l(token)'=$zl(token) zm gdeerr("NONASCII"):token:"number" ; error if the token has non-ascii numbers + s value=token + q +TFSPEC + k filespec + i ntoktype="TKEOL" zm gdeerr("QUALREQD"):"file specification" + i ntoktype="TKSTRLIT" s filespec=$ze(ntoken,2,$zl(ntoken)-1) + e d TFSPECP + d GETTOK^GDESCAN ; put the scanner back on track + i $zl(filespec)>(SIZEOF("file_spec")-1) zm gdeerr("VALUEBAD"):filespec:"file specification" + i '$l($zparse(filespec,"","","","SYNTAX_ONLY")) zm gdeerr("VALUEBAD"):filespec:"file specification" + s @("value="_$s($l(filexfm):filexfm,1:filespec)) ; do system specific file name translation + q +TFSPECP ; scan filespec token by token + n c,cp1 ; unix filenames must be quoted to avoid / conflicts with qualifiers + s cp1=cp-$l(ntoken) + f i=0:1 s c=$e(comline,cp1+i) q:c'?@dbfilpar!'$l(c) + s filespec=$e(comline,cp1,cp1+i-1),cp=cp1+i + q +TACCMETH + d GETTOK^GDESCAN + i toktype'="TKIDENT" zm gdeerr("VALUEBAD"):token:qual + s value=$tr(token,lower,upper) + i @s@(qual,"TYPE","VALUES")'[("\"_value) zm gdeerr("VALUEBAD"):token:qual + q +TNULLSUB + d GETTOK^GDESCAN + i toktype'="TKIDENT" zm gdeerr("VALUEBAD"):token:qual + s value=$tr(token,lower,upper) + i @s@(qual,"TYPE","VALUES")'[("\"_value) zm gdeerr("VALUEBAD"):token:qual + q +TREGION + n REGION d REGION s value=REGION + q +TSEGMENT + n SEGMENT d SEGMENT s value=SEGMENT + q +NAME + k NAME + i ntoktype="TKEOL" zm gdeerr("OBJREQD"):"name" + n c,cp1 + s cp1=cp-$l(ntoken) + f i=0:1 s c=$e(comline,cp1+i) q:c'?.1"%".1AN.1"*"!'$l(c) + s NAME=$e(comline,cp1,cp1+i-1),cp=cp1+i d GETTOK^GDESCAN ; put the scanner back on track + i '$l(NAME) zm gdeerr("VALUEBAD"):token:"name" + i $l(NAME)'=$zl(NAME) zm gdeerr("NONASCII"):NAME:"name" ; error if the name is non-ascii + i NAME'="*" s x=$e(NAME) i x'="%",x'?1A zm gdeerr("NAMSTARTBAD"):NAME + i $e(NAME,2,999)'?.AN.1"*" zm gdeerr("VALUEBAD"):NAME:"name" + i $l(NAME)>PARNAMLN zm gdeerr("VALTOOLONG"):NAME:PARNAMLN:"name" + q +REGION + k REGION + i ntoktype="TKEOL" zm gdeerr("OBJREQD"):renpref_"region" + n c,cp1 + s cp1=cp-$l(ntoken) + f i=0:1 s c=$e(comline,cp1+i) q:c'?.1AN.1"$".1"_"!'$l(c) + s REGION=$tr($e(comline,cp1,cp1+i-1),lower,upper),cp=cp1+i d GETTOK^GDESCAN ; put the scanner back on track + i '$l(REGION) zm gdeerr("VALUEBAD"):token:renpref_"region" + i $l(REGION)'=$zl(REGION) zm gdeerr("NONASCII"):REGION:"region" ; error if the name of the region is non-ascii + i REGION=defreg q + s x=$e(REGION) i x'?1A zm gdeerr("PREFIXBAD"):REGION:renpref_"region" + i $l(REGION)>PARREGLN zm gdeerr("VALTOOLONG"):REGION:PARREGLN:renpref_"region" + q +SEGMENT + k SEGMENT + i ntoktype="TKEOL" zm gdeerr("OBJREQD"):renpref_"segment" + n c,cp1 + s cp1=cp-$l(ntoken) + f i=0:1 s c=$e(comline,cp1+i) q:c'?.1AN.1"$".1"_"!'$l(c) + s SEGMENT=$tr($e(comline,cp1,cp1+i-1),lower,upper),cp=cp1+i d GETTOK^GDESCAN ; put the scanner back on track + i '$l(SEGMENT) zm gdeerr("VALUEBAD"):token:renpref_"segment" + i $l(SEGMENT)'=$zl(SEGMENT) zm gdeerr("NONASCII"):SEGMENT:"segment" ; error if the name of the segment is non-ascii + i SEGMENT=defseg q + s x=$e(SEGMENT) i x'?1A zm gdeerr("PREFIXBAD"):SEGMENT:renpref_"segment" + i $l(SEGMENT)>PARSEGLN zm gdeerr("VALTOOLONG"):SEGMENT:PARSEGLN:renpref_"segment" + q +matchtok:(tok,ent) + d GETTOK^GDESCAN + i toktype=tok q + zm gdeerr("VALUEBAD"):token:ent + q +checkkw:(kw,ent,kwlist) + n x1,x2 + s kw=$tr(kw,lower,upper) + i $e(kw,1,2)="NO" s negated=1,kw=$e(kw,3,999) + e s negated=0 + s x1="" f s x1=$o(@kwlist@(x1)) q:kw=$e(x1,1,$l(kw))!'$l(x1) + i '$l(x1) zm gdeerr("KEYWRDBAD"):kw:ent + s x2=x1 f s x2=$o(@kwlist@(x2)) q:kw=$e(x2,1,$l(kw))!'$l(x2) + i $l(x2) zm gdeerr("KEYWRDAMB"):kw:ent + s kw=x1 + q +getqual: d qual(.lqual,"Local qualifier","syntab("""_verb_""","""_gqual_""")") + i '$d(lquals(lqual)) s lquals(lqual)=$g(lqual("value")) + e zm gdeerr("QUALDUP"):lqual + q +getlitm: d qual(.lqual,"Local qualifier","syntab("""_verb_""","""_gqual_""","""_lhead_""")") + i '$d(lquals(lqual)) s lquals(lqual)=$g(lqual("value")) + e zm gdeerr("QUALDUP"):lqual + q + +;----------------------------------------------------------------------------------------------------------------------------------- + +ADD +CHANGE + d qual(.gqual,"Global qualifier","syntab("""_verb_""")"),@gqual + f q:ntoktype="TKEOL" d getqual + d @gqual^@("GDE"_$e(verb,1,5)) + q +RENAME + d qual(.gqual,"Global qualifier","syntab("""_verb_""")") + n renpref + s renpref="old " d @gqual s old=@gqual + s renpref="new " d @gqual s new=@gqual + s renpref="" d matchtok("TKEOL","End of line") + d @gqual^GDERENAM(old,new) + q +TEMPLATE + d qual(.gqual,"Global qualifier","syntab("""_verb_""")") + f q:ntoktype="TKEOL" d getqual + d @gqual^GDETEMPL + q +DELETE + d qual(.gqual,"Global qualifier","syntab("""_verb_""")"),@gqual,matchtok("TKEOL","End of line"),@gqual^GDEDELET + q +LOCKS + d qual(.gqual,"Global qualifier","syntab("""_verb_""")"),matchtok("TKEOL","End of line"),LOCKS^GDELOCKS + q +LOG + i ntoktype="TKEOL" d INQUIRE^GDELOG q + d qual(.gqual,"Global qualifier","syntab("""_verb_""")"),matchtok("TKEOL","End of line"),LOG^GDELOG + q +SHOW + i ntoktype="TKEOL" d ALL^GDESHOW q + d qual(.gqual,"Global qualifier","syntab("""_verb_""")") s t="NAMEREGIONSEGMENT"[gqual + i t,ntoktype="TKEOL" d @("ALL"_$e(gqual,1,5))^GDESHOW q + n mapreg + i gqual="MAP",ntoktype'="TKEOL" d getqual s mapreg=$g(lquals("REGION")) + i 't,"COMMANDS"=gqual,ntoktype'="TKEOL" d getqual s cfile=$g(lquals("FILE")) + d @gqual:t,matchtok("TKEOL","End of line"),@gqual^GDESHOW + q +VERIFY + i ntoktype="TKEOL" s x=$$ALL^GDEVERIF q + d qual(.gqual,"Global qualifier","syntab("""_verb_""")") + i "ALL|MAP"[gqual s x=$$ALL^GDEVERIF q + n verified s verified=1 + i ntoktype="TKEOL" d @("ALL"_$e(gqual,1,3))^GDEVERIF i 1 + e i "NAMEREGIONSEGMENT"[gqual d @gqual,@gqual^GDEVERIF i 1 + e zm gdeerr("NOVALUE"):gqual + i $d(verified) zm gdeerr("VERIFY"):$s(verified:"OK",1:"FAILED") w ! + q +EXIT +QUIT + d matchtok("TKEOL","End of line") + d ^@("GDE"_$tr(verb,lower,upper)) + q +SETGD f d q:ntoktype="TKEOL" + . d qual(.gqual,"Global qualifier","syntab("""_verb_""")") s:gqual="FILE" tfile=gqual("value") s:gqual="QUIT" update=0 + d GDESETGD^GDESETGD + q +HELP +SPAWN + d ^@("GDE"_$tr(verb,lower,upper)) + q diff --git a/sr_port/gdequit.m b/sr_port/gdequit.m new file mode 100644 index 0000000..88414fd --- /dev/null +++ b/sr_port/gdequit.m @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +quit: ;implement the verb: QUIT +QUIT + zm gdeerr("NOACTION"):$zparse(tfile,"",defgldext) + h diff --git a/sr_port/gderenam.m b/sr_port/gderenam.m new file mode 100644 index 0000000..4ed648c --- /dev/null +++ b/sr_port/gderenam.m @@ -0,0 +1,45 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +rename: ;implement the verb: RENAME +NAME(old,new) + i old=new q + i old="*" zm gdeerr("LVSTARALON") + i '$d(nams(old)) d error1 + i $d(nams(new)) d error2 + s update=1,nams(new)=nams(old),s="" + f s s=$o(nams(old,s)) q:'$l(s) s nams(new,s)=nams(old,s) + k nams(old) + q +REGION(old,new) + i old=new q + i '$d(regs(old)) d error1 + i $d(regs(new)) d error2 + s update=1,s="" + f s s=$o(regs(old,s)) q:'$l(s) s regs(new,s)=regs(old,s) + k regs(old) + f s s=$o(nams(s)) q:'$l(s) i nams(s)=old s nams(s)=new + q +SEGMENT(old,new) + i old=new q + i '$d(segs(old)) d error1 + i $d(segs(new)) d error2 + n lquals s update=1,am=segs(old,"ACCESS_METHOD"),s="" + f s s=$o(segs(old,s)) q:'$l(s) s segs(new,s)=segs(old,s),lquals(s)=segs(old,s) + i '$$SQUALS^GDEVERIF(am,.lquals) k segs(new) zm gdeerr("OBJNOTCHG"):"segment":old + s segs(new,"ACCESS_METHOD")=am k segs(old) + f s s=$o(regs(s)) q:'$l(s) i regs(s,"DYNAMIC_SEGMENT")=old s regs(s,"DYNAMIC_SEGMENT")=new + q +error1: + zm gdeerr("OBJNOTFND"):"Old "_$tr(gqual,upper,lower):old + q +error2: + zm gdeerr("OBJDUP"):"New "_$tr(gqual,upper,lower):new + q diff --git a/sr_port/gdescan.m b/sr_port/gdescan.m new file mode 100644 index 0000000..36c702a --- /dev/null +++ b/sr_port/gdescan.m @@ -0,0 +1,73 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001, 2010 Fidelity Information Services, Inc ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +gdescan: ;scanner used by gdeparse +GETTOK + n c + s token=ntoken,toktype=ntoktype + f cp=cp:1 s c=$e(comline,cp) q:(c'=" ")&(c'=TAB) + i $c(10,13)[c,cp=$l(comline) s c="" + s ntoktype=$s(c?1A:"TKIDENT",c?1N:"TKNUMLIT",c="":"TKEOL",$d(tokens(c)):tokens(c),1:"TKOTHER") + d @ntoktype + q +shotoks: ; for debugging only + w !," toktype: ",toktype,?24," token: '",token,"'" + w ?48," ntoktype: ",ntoktype,?72,"ntoken: '",ntoken,"'" + q +TKIDENT + n i + f i=1:1 s c=$e(comline,cp+i) q:(c'?1A)&(c'="_") + s ntoken=$e(comline,cp,cp+i-1),cp=cp+i + q +TKNUMLIT + n i + f i=1:1 q:$e(comline,cp+i)'?1N + s ntoken=$e(comline,cp,cp+i-1),cp=cp+i + q +TKSTRLIT + n i + f i=1:1:$l(comline)-cp q:$e(comline,cp+i)="""" + s ntoken=$e(comline,cp,cp+i),cp=cp+i+1 + q +TKAMPER +TKASTER +TKCOLON +TKCOMMA +TKDASH ; see below for more UNIXy alternative +TKDOLLAR +TKEQUAL +TKLANGLE +TKLBRACK +TKLPAREN +TKPCT +TKPERIOD +TKRANGLE +TKRBRACK +TKRPAREN +TKSCOLON +TKSLASH +TKUSCORE + s ntoken=c,cp=cp+1 + q +TKEXCLAM + s ntoktype="TKEOL" + s ntoken="" + s cp=$l(comline) + q +;TKDASH - more UNIXy handling disabled for compatibility with other utilities + s ntoken=c,cp=cp+1 + i sep="TKDASH",$e(comline,cp)?1A s c=$e(comline,cp-2) i c=" "!(c=TAB) q + zm gdeerr("ILLCHAR"):"-" + q +TKEOL + s ntoken="" + q +TKOTHER + zm gdeerr("ILLCHAR"):c diff --git a/sr_port/gdesetgd.m b/sr_port/gdesetgd.m new file mode 100644 index 0000000..b51f4de --- /dev/null +++ b/sr_port/gdesetgd.m @@ -0,0 +1,23 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +gdesetgd: ;implement the verb: SETGD +GDESETGD + i update,'$$ALL^GDEVERIF zm gdeerr("GDNOTSET") q + i update,'$$GDEPUT^GDEPUT zm gdeerr("GDNOTSET") q + d GDFIND,CREATE^GDEGET:create,LOAD^GDEGET:'create + q +GDFIND s file=$zparse(tfile,"",defgldext) + i file="" s file=$ztrnlnm(tfile) s:file="" file=tfile zm gdeerr("INVGBLDIR"):file:defgld s tfile=defgld + s file=$zsearch($zparse(tfile,"",defgldext)) + i file="" s file=$zsearch($zparse(tfile,"",defgldext)) + i file="" s file=$zparse(tfile,"",defgldext),create=1 zm gdeerr("GDUSEDEFS"):file + e s create=0 zm gdeerr("LOADGD"):file + q diff --git a/sr_port/gdeshow.m b/sr_port/gdeshow.m new file mode 100644 index 0000000..af538a0 --- /dev/null +++ b/sr_port/gdeshow.m @@ -0,0 +1,279 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001, 2010 Fidelity Information Services, Inc ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +show: ;implement the verb: SHOW +ALL + d TEMPLATE,ALLNAME,ALLREGIO,ALLSEGME,MAP + q +COMMANDS + s BOL="!" + set delim=$select("VMS"=ver:"/",1:"-") + set defconst=$select("VMS"=ver:"$DEFAULT",1:"DEFAULT") + i $l($get(cfile)) o cfile:(newversion:exc="w !,$ztatus c cfile zgoto $zl:cfilefail") u cfile d + . d namec,segmentc,regionc,templatec + . c cfile +cfilefail: + f i="@useio",$s(log:"@uself",1:"") q:'$l(i) u @i d templatec,namec,regionc,segmentc + s BOL="" + q +NAME + i '$d(nams(NAME)) zm gdeerr("OBJNOTFND"):"Name":NAME q + d n2 + i log s BOL="!" u @uself w BOL d n2 w ! u @useio s BOL="" + q +n2: d namehd w !,BOL,?x(1),NAME,?x(2),nams(NAME) + q +ALLNAME + d SHOWNAM^GDEMAP + d n1 + i log s BOL="!" u @uself w BOL d n1 w ! u @useio s BOL="" + q +n1: d namehd s s="#" + f s s=$o(nams(s)) q:'$l(s) w !,BOL,?x(1),s,?x(2),nams(s) + q +namec: + d SHOWNAM^GDEMAP + s s="#" + w !,"LOCKS "_delim_"REGION=",nams(s) + f s s=$o(nams(s)) q:'$l(s) d + . i "*"'=s w !,"ADD "_delim_"NAME ",s," "_delim_"REGION=",nams(s) q + . s defreg=nams(s) + . i defconst'=defreg w !,"RENAME "_delim_"REGION "_defconst_" ",defreg + w ! + q +REGION + i '$d(regs(REGION)) zm gdeerr("OBJNOTFND"):"Region":REGION q + d r2 + i log s BOL="!" u @uself w BOL d r2 w ! u @useio s BOL="" + q +r2: d regionhd s s=REGION d onereg + i regs(s,"JOURNAL") d jnlhd d onejnl + q +ALLREGIO + d r1 + i log s BOL="!" u @uself w BOL d r1 w ! u @useio s BOL="" + q +r1: d regionhd s jnl=0,s="" + f s s=$o(regs(s)) q:'$l(s) d onereg i regs(s,"JOURNAL") s jnl=1 + i jnl d jnlhd s s="" f s s=$o(regs(s)) q:'$l(s) i regs(s,"JOURNAL") d onejnl + q +onereg: + w !,BOL,?x(1),s,?x(2),regs(s,"DYNAMIC_SEGMENT"),?x(3),$j(regs(s,"COLLATION_DEFAULT"),4) + w ?x(4),$j(regs(s,"RECORD_SIZE"),5),?x(5),$j(regs(s,"KEY_SIZE"),5) + w ?x(6),$s(regs(s,"NULL_SUBSCRIPTS")=1:"ALWAYS",regs(s,"NULL_SUBSCRIPTS")=2:"EXISTING",1:"NEVER") + w ?x(7),$s(regs(s,"STDNULLCOLL"):"Y",1:"N") + w ?x(8),$s(regs(s,"JOURNAL"):"Y",1:"N") + q +onejnl: + w !,BOL,?x(1),s,?x(2),$s($l(regs(s,"FILE_NAME")):regs(s,"FILE_NAME"),1:"") + i $x'<(x(3)-1) w !,BOL + w ?x(3),$s(regs(s,"BEFORE_IMAGE"):"Y",1:"N"),?x(4),$j(regs(s,"BUFFER_SIZE"),5) + w ?x(5),$j(regs(s,"ALLOCATION"),10),?x(6),$j(regs(s,"EXTENSION"),5) + w !,BOL + q +regionc: + s s="" + f s s=$o(regs(s)) q:'$l(s) d + . i s=defreg s defseg=regs(s,"DYNAMIC_SEGMENT") + . w !,$s(s=defreg:"CHANGE",1:"ADD")," "_delim_"REGION ",s," "_delim_"DYNAMIC=",regs(s,"DYNAMIC_SEGMENT") + . f q="COLLATION_DEFAULT","RECORD_SIZE","KEY_SIZE" w " "_delim,q,"=",regs(s,q) + . w " "_delim_"NULL_SUBSCRIPTS=",$s(regs(s,"NULL_SUBSCRIPTS")=1:"ALWAYS",tmpreg("NULL_SUBSCRIPTS")=2:"EXISTING",1:"NEVER") + . i regs(s,"STDNULLCOLL") w " "_delim_"STDNULLCOLL" + . i regs(s,"JOURNAL") d + .. w " "_delim_"JOURNAL=(",$s(regs(s,"BEFORE_IMAGE"):"",1:"NO"),"BEFORE_IMAGE",",BUFFER_SIZE=",tmpreg("BUFFER_SIZE") + .. w ",ALLOCATION=",regs(s,"ALLOCATION"),",EXTENSION=",regs(s,"EXTENSION") + .. i $l(regs(s,"FILE_NAME")) w ",FILE=""",regs(s,"FILE_NAME"),"""" + .. w ")" + . else w " "_delim_"NOJOURNAL" + i defconst'=defseg w !,"DELETE "_delim_"SEGMENT "_defconst + w !,BOL + q +SEGMENT + i '$d(segs(SEGMENT)) zm gdeerr("OBJNOTFND"):"Segment":SEGMENT q + d s2 + i log s BOL="!" u @uself w BOL d s2 w ! u @useio s BOL="" + q +s2: d seghd s s=SEGMENT s am=segs(s,"ACCESS_METHOD") d oneseg + q +ALLSEGME + d s1 + i log s BOL="!" u @uself w BOL d s1 w ! u @useio s BOL="" + q +s1: d seghd s s="" + f s s=$o(segs(s)) q:'$l(s) s am=segs(s,"ACCESS_METHOD") d oneseg + q +oneseg: + w !,BOL,?x(1),s,?x(2),segs(s,"FILE_NAME") + i $x'<(x(3)-1) w !,BOL + w ?x(3),segs(s,"ACCESS_METHOD") + i am="USER" q + w ?x(4),$s(segs(s,"FILE_TYPE")="DYNAMIC":"DYN",1:"STA") + w ?x(5),$j(segs(s,"BLOCK_SIZE"),5),?x(6),$j(segs(s,"ALLOCATION"),10),?x(7),$j(segs(s,"EXTENSION_COUNT"),5) + d @am + q +BG w ?x(8),"GLOB=",$j(segs(s,"GLOBAL_BUFFER_COUNT"),4) + w !,BOL,?x(8),"LOCK=",$j(segs(s,"LOCK_SPACE"),4) + w !,BOL,?x(8),"RES =",$j(segs(s,"RESERVED_BYTES"),4) + ; For non-encryption platforms, always show FLAG as OFF. For VMS dont even display this line + i $ZVersion'["VMS" w !,BOL,?x(8),"ENCR=",$S((encsupportedplat=TRUE&segs(s,"ENCRYPTION_FLAG")):"ON",1:"OFF") + q +MM w ?x(8),$s(segs(s,"DEFER"):"DEFER",1:"NODEFER") + w !,BOL,?x(8),"LOCK=",$j(segs(s,"LOCK_SPACE"),4) + w !,BOL,?x(8),"RES = ",$j(segs(s,"RESERVED_BYTES"),4) + i $ZVersion'["VMS" w !,BOL,?x(8),"ENCR=OFF" + q +segmentc: + s s="" + f s s=$o(segs(s)) q:'$l(s) s am=segs(s,"ACCESS_METHOD") d + . w !,$s(s=defseg:"CHANGE",1:"ADD")," "_delim_"SEGMENT ",s," "_delim_"ACCESS_METHOD=",segs(s,"ACCESS_METHOD") + . i am="USER" q + . f q="BLOCK_SIZE","ALLOCATION","EXTENSION_COUNT","LOCK_SPACE","RESERVED_BYTES" w " "_delim,q,"=",segs(s,q) + . i "BG"=am d + .. w " "_delim_"GLOBAL_BUFFER_COUNT=",segs(s,"GLOBAL_BUFFER_COUNT") + .. i $zver'["VMS",encsupportedplat=TRUE,segs(s,"ENCRYPTION_FLAG") w " "_delim_"ENCRYPT" + . i "MM"=am w " "_delim,$s(segs(s,"DEFER"):"DEFER",1:"NODEFER") + . w " "_delim_"FILE=",segs(s,"FILE_NAME") + w !,BOL + q +MAP + n map + i '$d(mapreg) n mapreg s mapreg="" + e i '$d(regs(mapreg)) zm gdeerr("OBJNOTFND"):"Region":mapreg q + d SHOWMAKE^GDEMAP + d m1 + i log s BOL="!" u @uself w BOL d m1 w ! u @useio s BOL="" + q +m1: n l1,s1,s2 + d maphd + s s1=$o(map("$")) + i s1'="%" s map("%")=map("$"),s1="%" + f s s2=s1,s1=$o(map(s2)) q:'$l(s1) d onemap(s1,s2) + d onemap("...",s2) + i $d(nams("#")) s s2="LOCAL LOCKS",map(s2)=nams("#") d onemap("",s2) k map(s2) + q +onemap:(s1,s2) + i $l(mapreg),mapreg'=map(s2) q + s l1=$l(s1) + i $l(s2)=l1,$e(s1,l1)=0,$e(s2,l1)=")",$e(s1,1,l1-1)=$e(s2,1,l1-1) q + w !,BOL,?x(1),$tr(s2,")","0"),?x(2),$tr(s1,")","0"),?x(3),"REG = ",map(s2) + i '$d(regs(map(s2),"DYNAMIC_SEGMENT")) d q + . w !,BOL,?x(3),"SEG = NONE",!,BOL,?x(3),"FILE = NONE" + s j=regs(map(s2),"DYNAMIC_SEGMENT") w !,BOL,?x(3),"SEG = ",j + i '$d(segs(j,"ACCESS_METHOD")) w !,BOL,?x(3),"FILE = NONE" + e s s=segs(j,"FILE_NAME") w !,BOL,?x(3),"FILE = ",s + q +TEMPLATE + d t1 + i log s BOL="!" u @uself w BOL d t1 w ! u @useio s BOL="" + q +t1: d tmpreghd + w !,BOL,?x(1),"",?x(3),$j(tmpreg("COLLATION_DEFAULT"),4) + w ?x(4),$j(tmpreg("RECORD_SIZE"),5),?x(5),$j(tmpreg("KEY_SIZE"),5) + w ?x(6),$s(tmpreg("NULL_SUBSCRIPTS")=1:"ALWAYS",tmpreg("NULL_SUBSCRIPTS")=2:"EXISTING",1:"NEVER") + w ?x(7),$s(tmpreg("STDNULLCOLL"):"Y",1:"N") + w ?x(8),$s(tmpreg("JOURNAL"):"Y",1:"N") + i tmpreg("JOURNAL") d tmpjnlhd,tmpjnlbd + d tmpseghd + w !,BOL,?x(1),"",?x(2),$s(tmpacc="BG":" *",1:""),?x(3),"BG" + w ?x(4),$s(tmpseg("BG","FILE_TYPE")="DYNAMIC":"DYN",1:"STA"),?x(5),$j(tmpseg("BG","BLOCK_SIZE"),5) + w ?x(6),$j(tmpseg("BG","ALLOCATION"),10),?x(7),$j(tmpseg("BG","EXTENSION_COUNT"),5) + w ?x(8),"GLOB =",$j(tmpseg("BG","GLOBAL_BUFFER_COUNT"),3) + w !,BOL,?x(8),"LOCK =",$j(tmpseg("BG","LOCK_SPACE"),3) + w !,BOL,?x(8),"RES =",$j(tmpseg("BG","RESERVED_BYTES"),4) + i $ZVersion'["VMS" w !,BOL,?x(8),"ENCR = ",$s((encsupportedplat=TRUE&tmpseg("BG","ENCRYPTION_FLAG")):"ON",1:"OFF") + w !,BOL,?x(1),"",?x(2),$s(tmpacc="MM":" *",1:""),?x(3),"MM" + w ?x(4),$s(tmpseg("MM","FILE_TYPE")="DYNAMIC":"DYN",1:"STA"),?x(5),$j(tmpseg("MM","BLOCK_SIZE"),5) + w ?x(6),$j(tmpseg("MM","ALLOCATION"),10),?x(7),$j(tmpseg("MM","EXTENSION_COUNT"),5) + w ?x(8),$s(tmpseg("MM","DEFER"):"DEFER",1:"NODEFER") + w !,BOL,?x(8),"LOCK =",$j(tmpseg("MM","LOCK_SPACE"),3) + q +tmpjnlbd: + w !,BOL,?x(1),"",?x(2),$s($l(tmpreg("FILE_NAME")):tmpreg("FILE_NAME"),1:"") + i $x'<(x(3)-1) w !,BOL + w ?x(3),$s(tmpreg("BEFORE_IMAGE"):"Y",1:"N"),?x(4),$j(tmpreg("BUFFER_SIZE"),5) + w ?x(5),$j(tmpreg("ALLOCATION"),10),?x(6),$j(tmpreg("EXTENSION"),5) + w !,BOL + q +templatec: + f am="MM","BG" w !,"TEMPLATE "_delim_"SEGMENT "_delim_"ACCESS_METHOD=",am d + . f q="BLOCK_SIZE","ALLOCATION","EXTENSION_COUNT","LOCK_SPACE","RESERVED_BYTES" w " "_delim,q,"=",tmpseg(am,q) + . i "BG"=am d + .. w " "_delim_"GLOBAL_BUFFER_COUNT=",tmpseg("BG","GLOBAL_BUFFER_COUNT") + .. i $zver'["VMS",encsupportedplat=TRUE,tmpseg("BG","ENCRYPTION_FLAG") w " "_delim_"ENCRYPT" + . i "MM"=am w $s(tmpseg("MM","DEFER"):delim,1:delim_"NO"),"DEFER" + w !,"TEMPLATE "_delim_"REGION" + f q="RECORD_SIZE","KEY_SIZE" w " "_delim,q,"=",tmpreg(q) + w " "_delim_"NULL_SUBSCRIPTS=",$s(tmpreg("NULL_SUBSCRIPTS")=1:"ALWAYS",tmpreg("NULL_SUBSCRIPTS")=2:"EXISTING",1:"NEVER") + i tmpreg("STDNULLCOLL") w " "_delim_"STDNULLCOLL" + i tmpreg("JOURNAL") d + . w !,"TEMPLATE "_delim_"REGION "_delim_"JOURNAL=(" + . w $s(tmpreg("BEFORE_IMAGE"):"",1:"NO"),"BEFORE_IMAGE,BUFFER_SIZE=",tmpreg("BUFFER_SIZE") + . w ",ALLOCATION=",tmpreg("ALLOCATION"),",EXTENSION=",tmpreg("EXTENSION"),")" + i $l(tmpreg("FILE_NAME")) w ",FILE=",tmpreg("FILE_NAME") + w !,BOL + q + +;----------------------------------------------------------------------------------------------------------------------------------- + +namehd: + s x(0)=9,x(1)=1,x(2)=36 + w !,BOL,!,BOL,?x(0),"*** NAMES ***",!,BOL,?x(1),"Global",?x(2),"Region" + w !,BOL,?x(1),$tr($j("",78)," ","-") + q +regionhd: + s x(0)=32,x(1)=1,x(2)=33,x(3)=65,x(4)=71,x(5)=77,x(6)=83,x(7)=94,x(8)=104 + w !,BOL,!,BOL,?x(0),"*** REGIONS ***" + w !,BOL,?x(2),"Dynamic",?x(3),$j("Def",4),?x(4),$j("Rec",5),?x(5),$j("Key",5),?x(6),"Null",?x(7),"Standard" + w !,BOL,?x(1),"Region",?x(2),"Segment",?x(3),$j("Coll",4),?x(4),$j("Size",5),?x(5),$j("Size",5) + w ?x(6),"Subs",?x(7),"NullColl",?x(8),"Journaling" + w !,BOL,?x(1),$tr($j("",114)," ","-") + q +jnlhd: + s x(0)=26,x(1)=1,x(2)=33,x(3)=59,x(4)=65,x(5)=71,x(6)=82,x(7)=88 + w !,BOL,!,BOL,?x(0),"*** JOURNALING INFORMATION ***" + w !,BOL,?x(1),"Region",?x(2),"Jnl File (def ext: .mjl)" + w ?x(3),"Before",?x(4),$j("Buff",5),?x(5),$j("Alloc",10),?x(6),"Exten" ;?x(7),"Stop" + w !,BOL,?x(1),$tr($j("",87)," ","-") + q +seghd: + s x(0)=32,x(1)=1,x(2)=33,x(3)=53,x(4)=57,x(5)=61,x(6)=67,x(7)=78,x(8)=84 + w !,BOL,!,BOL,?x(0),"*** SEGMENTS ***" + w !,BOL,?x(1),"Segment",?x(2),"File (def ext: .dat)",?x(3),"Acc",?x(4),"Typ",?x(5),"Block",?x(6),$j("Alloc",10) + w ?x(7),"Exten",?x(8),"Options" + w !,BOL,?x(1),$tr($j("",91)," ","-") + q +maphd: + s x="*** MAP"_$s($l(mapreg):" for region "_mapreg,1:"")_" ***" + s x(0)=80-$l(x)*.5,x(1)=1,x(2)=33,x(3)=66,x(4)=98,x(5)=130 + w !,BOL,!,BOL,?x(0),x + w !,BOL,?x(1)," - - - - - - - - - - Names - - - - - - - - - -" + w !,BOL,?x(1),"From",?x(2),"Up to",?x(3),"Region / Segment / File(def ext: .dat)" + w !,BOL,?x(1),$tr($j("",131)," ","-") + q +tmpreghd: + s x(0)=31,x(1)=1,x(2)=19,x(3)=44,x(4)=49,x(5)=55,x(6)=61,x(7)=72,x(8)=82 + w !,BOL,!,BOL,?x(0),"*** TEMPLATES ***" + w !,BOL,?x(3),$j("Def",4),?x(4),$j("Rec",5),?x(5),$j("Key",5),?x(6),"Null",?x(7),"Standard" + w !,BOL,?x(1),"Region",?x(3),$j("Coll",4),?x(4),$j("Size",5),?x(5),$j("Size",5) + w ?x(6),"Subs",?x(7),"NullColl",?x(8),"Journaling" + w !,BOL,?x(1),$tr($j("",92)," ","-") + q +tmpjnlhd: + s x(0)=26,x(1)=1,x(2)=18,x(3)=44,x(4)=51,x(5)=57,x(6)=68,x(7)=74 + w !,BOL,?x(2),"Jnl File (def ext: .mjl)" + w ?x(3),"Before",?x(4),$j("Buff",5),?x(5),$j("Alloc",10),?x(6),"Exten" ;?x(7),"Stop" + w !,BOL,?x(1),$tr($j("",78)," ","-") + q +tmpseghd: + s x(0)=32,x(1)=1,x(2)=18,x(3)=38,x(4)=42,x(5)=46,x(6)=52,x(7)=63,x(8)=69 + w !,BOL,!,BOL,?x(1),"Segment",?x(2),"Active",?x(3),"Acc",?x(4),"Typ",?x(5),"Block",?x(6),$j("Alloc",10) + w ?x(7),"Exten",?x(8),"Options" + w !,BOL,?x(1),$tr($j("",78)," ","-") + q diff --git a/sr_port/gdespawn.m b/sr_port/gdespawn.m new file mode 100644 index 0000000..fc9ee79 --- /dev/null +++ b/sr_port/gdespawn.m @@ -0,0 +1,18 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +spawn: ;implement the verb: SPAWN +SPAWN + n spawnline + s spawnline=$e(comline,cp-$l(ntoken),999) + u io:ctrap=$c(3,25) + zsy spawnline + u @useio + q diff --git a/sr_port/gdetempl.m b/sr_port/gdetempl.m new file mode 100644 index 0000000..4331d54 --- /dev/null +++ b/sr_port/gdetempl.m @@ -0,0 +1,28 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001, 2005 Fidelity Information Services, Inc ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +template: ;implement the verb: TEMPLATE +REGION + n nullsub + i $d(lquals("JOURNAL")),lquals("JOURNAL"),'tmpreg("JOURNAL"),'$d(lquals("BEFORE_IMAGE")) zm gdeerr("QUALREQD"):"Before_image" + i $d(lquals("NULL_SUBSCRIPTS")) d + . s nullsub=lquals("NULL_SUBSCRIPTS") + . s lquals("NULL_SUBSCRIPTS")=$s((nullsub="ALWAYS")!(nullsub="TRUE"):1,nullsub="EXISTING":2,1:0) + i '$$TRQUALS^GDEVERIF(.lquals) zm gdeerr("OBJNOTCHG"):"region":"template" + s update=1,s="" + f s s=$o(lquals(s)) q:'$l(s) s tmpreg(s)=lquals(s) i s="ALLOCATION" s tmpreg("EXTENSION")=lquals(s)\10 + q +SEGMENT + i $d(lquals("ACCESS_METHOD")) s am=lquals("ACCESS_METHOD") + e s am=tmpacc + i '$$TSQUALS^GDEVERIF(am,.lquals) zm gdeerr("OBJNOTCHG"):"segment":"template" + s update=1,s="",tmpacc=am + f s s=$o(lquals(s)) q:'$l(s) s tmpseg(tmpacc,s)=lquals(s) + q diff --git a/sr_port/gds_blk_downgrade.c b/sr_port/gds_blk_downgrade.c new file mode 100644 index 0000000..43b2b7a --- /dev/null +++ b/sr_port/gds_blk_downgrade.c @@ -0,0 +1,83 @@ +/**************************************************************** + * * + * Copyright 2005, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdlib.h" +#include "gtm_string.h" + +#include "gdsroot.h" +#include "v15_gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsblk.h" +#include "gdsbt.h" +#include "v15_gdsbt.h" +#include "gdsfhead.h" +#include "v15_gdsfhead.h" +#include "io.h" +#include "iottdef.h" +#include "iomtdef.h" +#include "gds_blk_downgrade.h" +#ifdef VMS +#include "copy.h" +#endif + +#define SPACE_NEEDED (SIZEOF(blk_hdr) - SIZEOF(v15_blk_hdr)) + +GBLREF boolean_t dse_running; + +void gds_blk_downgrade(v15_blk_hdr_ptr_t gds_blk_trg, blk_hdr_ptr_t gds_blk_src) +{ + sm_uc_ptr_t trg_p, src_p; + v15_trans_num v15tn; + trans_num tn; + uint4 bsiz, levl; + int movesize; + + /* Note that this routine is written in such a fashion that it is possible for the + source and target blocks to point to the same area. + */ + assert(gds_blk_trg); + assert(gds_blk_src); + assert(SIZEOF(v15_blk_hdr) > gds_blk_src->bver); /* Check it is a GDSVCURR blk to begin with */ + assert(0 == ((long)gds_blk_trg & 0x7)); /* Buffer alignment checks (8 byte) */ + assert(0 == ((long)gds_blk_src & 0x7)); + trg_p = (sm_uc_ptr_t)gds_blk_trg + SIZEOF(v15_blk_hdr); + src_p = (sm_uc_ptr_t)gds_blk_src + SIZEOF(blk_hdr); + bsiz = gds_blk_src->bsiz; + assert(MAX_BLK_SZ >= bsiz); + assert(SIZEOF(blk_hdr) <= bsiz); + tn = gds_blk_src->tn; + assert((MAX_TN_V4 >= tn) || dse_running); + levl = gds_blk_src->levl; + movesize = bsiz - SIZEOF(blk_hdr); + if ((sm_uc_ptr_t)gds_blk_trg != (sm_uc_ptr_t)gds_blk_src) + { /* Normal case, downgrade is to a new buffer. Our simple check is quicker + than just always doing memmove() would be. But assert they are at least + one block away just in case... + */ + DEBUG_ONLY( + if ((sm_uc_ptr_t)gds_blk_trg > (sm_uc_ptr_t)gds_blk_src) + assert((sm_uc_ptr_t)gds_blk_trg >= ((sm_uc_ptr_t)gds_blk_src + bsiz)); + else + assert((sm_uc_ptr_t)gds_blk_src >= ((sm_uc_ptr_t)gds_blk_trg + bsiz)); + ) + memcpy(trg_p, src_p, movesize); + } else + memmove(trg_p, src_p, movesize); + + gds_blk_trg->bsiz = bsiz - SPACE_NEEDED; + gds_blk_trg->levl = levl; + v15tn = (v15_trans_num) tn; + UNIX_ONLY(gds_blk_trg->tn = v15tn); + VMS_ONLY(PUT_ULONG(&gds_blk_trg->tn, v15tn)); +} diff --git a/sr_port/gds_blk_downgrade.h b/sr_port/gds_blk_downgrade.h new file mode 100644 index 0000000..9eae9d7 --- /dev/null +++ b/sr_port/gds_blk_downgrade.h @@ -0,0 +1,19 @@ +/**************************************************************** + * * + * Copyright 2005 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GDS_BLK_UPGRADE_INCLUDED +#define GDS_BLK_UPGRADE_INCLUDED + +void gds_blk_downgrade(v15_blk_hdr_ptr_t gds_blk_trg, blk_hdr_ptr_t gds_blk_src); + +#define IS_GDS_BLK_DOWNGRADE_NEEDED(ondskblkver) (GDSV4 == (ondskblkver)) + +#endif diff --git a/sr_port/gds_blk_upgrade.c b/sr_port/gds_blk_upgrade.c new file mode 100644 index 0000000..9e8745d --- /dev/null +++ b/sr_port/gds_blk_upgrade.c @@ -0,0 +1,81 @@ +/**************************************************************** + * * + * Copyright 2005, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "v15_gdsroot.h" +#include "gdsblk.h" +#include "gdsdbver.h" +#include "gds_blk_upgrade.h" +#include "iosp.h" +#include "copy.h" + +#define SPACE_NEEDED (SIZEOF(blk_hdr) - SIZEOF(v15_blk_hdr)) + +GBLREF boolean_t gtm_blkupgrade_override; +GBLREF uint4 gtm_blkupgrade_flag; /* control whether dynamic upgrade is attempted or not */ + +int4 gds_blk_upgrade(sm_uc_ptr_t gds_blk_src, sm_uc_ptr_t gds_blk_trg, int4 blksize, enum db_ver *ondsk_blkver) +{ + blk_hdr_ptr_t bp; + v15_blk_hdr_ptr_t v15bp; + v15_trans_num v15tn; + uint4 v15bsiz, v15levl; + + error_def(ERR_DYNUPGRDFAIL); + + assert(gds_blk_src); + assert(gds_blk_trg); + /* Assert that the input buffer is 8-byte aligned for us to freely de-reference 8-byte tn fields from the buffer header. + * If not, we should have had to use GET/PUT_xxxx macros from copy. + * Note that in GDSV4 format in VMS, the "tn" field in the block-header is not 4-byte offset aligned so we + * need to use the GET_ULONG macro to fetch the field from the block header. But since "tn" is 8-byte + * offset aligned in the new format, there is no need of any such macro while assigning bp->tn. + */ + assert(0 == ((long)gds_blk_src & 0x7)); /* Assume 8 byte alignment */ + assert(0 == ((long)gds_blk_trg & 0x7)); + v15bp = (v15_blk_hdr_ptr_t)gds_blk_src; + bp = (blk_hdr_ptr_t)gds_blk_trg; + assert((SIZEOF(v15_blk_hdr) <= v15bp->bsiz) || (UPGRADE_ALWAYS == gtm_blkupgrade_flag)); + UNIX_ONLY(v15tn = v15bp->tn); + VMS_ONLY(GET_ULONG(v15tn, &v15bp->tn)); + v15bsiz = v15bp->bsiz; + if (v15bsiz > blksize) /* Exceeds maximum block size. Not a valid V4 block. Return without upgrading */ + { + assert(UPGRADE_NEVER != gtm_blkupgrade_flag); + if (UPGRADE_IF_NEEDED == gtm_blkupgrade_flag) + { + if (NULL != ondsk_blkver) + *ondsk_blkver = GDSV5; + return SS_NORMAL; + } else + { + if (NULL != ondsk_blkver) + *ondsk_blkver = GDSV4; + return ERR_DYNUPGRDFAIL; + } + } + if (NULL != ondsk_blkver) + *ondsk_blkver = GDSV4; + v15bsiz += SPACE_NEEDED; + if (v15bsiz > blksize) /* Exceeds maximum block size */ + return ERR_DYNUPGRDFAIL; + v15levl = v15bp->levl; + memmove((gds_blk_trg + SPACE_NEEDED), gds_blk_src, v15bp->bsiz); /* Shift/copy block requisite amount */ + bp->tn = v15tn; + bp->bsiz = v15bsiz; + bp->levl = v15levl; + bp->bver = GDSV5; + return SS_NORMAL; +} diff --git a/sr_port/gds_blk_upgrade.h b/sr_port/gds_blk_upgrade.h new file mode 100644 index 0000000..98a22a0 --- /dev/null +++ b/sr_port/gds_blk_upgrade.h @@ -0,0 +1,130 @@ +/**************************************************************** + * * + * Copyright 2005, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GDS_BLK_UPGRADE_INCLUDED +#define GDS_BLK_UPGRADE_INCLUDED + +#define UPGRADE_IF_NEEDED 0 /* default */ +#define UPGRADE_NEVER 1 +#define UPGRADE_ALWAYS 2 + +int4 gds_blk_upgrade(sm_uc_ptr_t gds_blk_src, sm_uc_ptr_t gds_blk_trg, int4 bsiz, enum db_ver *ondsk_blkver); + +GBLREF uint4 gtm_blkupgrade_flag; /* control whether dynamic upgrade is attempted or not */ +GBLREF boolean_t dse_running; + +/* See if block needs to be converted to current version. Assume buffer is at least short aligned. + * Note: csd->fully_upgraded is not derived within the macro but instead passed in as a parameter to ensure whichever + * function (dsk_read currently) references this does that once and copies the value into a local variable that is used + * in all further usages. This way multiple usages are guaranteed to see the same value. Using csd->fully_upgraded in + * each of those cases could cause different values to be seen (since csd can be concurrently updated). + */ +#define GDS_BLK_UPGRADE_IF_NEEDED(blknum, srcbuffptr, trgbuffptr, curcsd, ondskblkver, upgrdstatus, fully_upgraded) \ +{ \ + /* In order to detect if a block needs to be upgraded or not, we do the following series of tests. \ + * If DSE, the variable "gtm_blkupgrade_flag" controls whether upgrade is attempted or not. \ + * If it is UPGRADE_NEVER, we never attempt upgrade. \ + * Likewise, if it is UPGRADE_ALWAYS, we unconditionally upgrade. \ + * If it is UPGRADE_IF_NEEDED, then the following checks are done. \ + * 1) If the file-header has "fully_upgraded" set to TRUE, we know for sure no block needs to be upgraded. \ + * This check is performed in this macro itself as it is a quick check and avoids a function call. \ + * 2) Else, the block might or might not need an upgrade. To decide, we do some more checks. \ + * V5 onwards, the first 2 bytes of the block header is the version indicator. It is 1 in V5. \ + * In V4, the first 2 bytes of the block header was the block-size which is guaranteed to be \ + * at least SIZEOF(v15_blk_hdr) (the size of the V4 blk_hdr structure) which is 8 bytes in Unix \ + * and 7 bytes in VMS. We use the first 2 bytes in the block header as a first level check. \ + * If they are >= SIZEOF(v15_blk_hdr), we decide it is a V4 format block and try to upgrade. \ + * This check is performed in this macro itself as it is a quick check and avoids a function call. \ + * 3) It is quite possible that we might conclude the format incorrectly based on just the above checks. \ + * This can be due to any one of the following. \ + * => A V4 format block might have a corrupt block-header where the first 2 bytes are exactly 1. \ + * => A V5 format block might have a corrupt block-header where the first 2 bytes are not 1. \ + * => We might be reading an unused block (marked free/recycled in the bitmap) from disk. \ + * This is possible due to a variety of reasons including concurrency issues while \ + * traversing the B-tree that cause us to end up in a restartable situation. \ + * If we read such a block, then the block contents (including the header) is not valid. \ + * In VMS it contains uninitialized data. In Unix it contains 0s. \ + * In all the above cases, we have no definitive way of determining exactly which format the block is. \ + * For the case where we incorrectly conclude it is V5 format, we dont do much. \ + * But for the other case, we do better by checking if the V4 block has enough room to accommodate \ + * the extra space needed for the upgrade. \ + * If yes, we go ahead with the upgrade. \ + * If not, we check if the V4 block size is less than the database block size. \ + * If yes, then we believe it is a valid V4 block that is just too big to be upgraded. \ + * We therefore issue a DYNUPGRDFAIL error. \ + * If no, this is a corrupt block and we decide not to upgrade. \ + * Something to consider for the future is to invoke block certification on this block. \ + * This check is involved and hence is done in the function "gds_blk_upgrade" (invoked from this macro). \ + * This is not a theoretically foolproof solution but for all practical purposes should be good enough. \ + * Note that for a database that has been completely upgraded to V5 format, we do not have any inconclusiveness. \ + * \ + * Note the clearing of srcbuffptr is done as a flag that gds_blk_upgrd was run (used by dsk_read). \ + */ \ + if (!dse_running || (UPGRADE_IF_NEEDED == gtm_blkupgrade_flag)) \ + { \ + if ((fully_upgraded) || (SIZEOF(v15_blk_hdr) > ((v15_blk_hdr_ptr_t)(srcbuffptr))->bsiz)) \ + { \ + upgrdstatus = SS_NORMAL; \ + if (NULL != (ondskblkver)) \ + *(ondskblkver) = GDSV5; \ + } else \ + { \ + upgrdstatus = gds_blk_upgrade((sm_uc_ptr_t)(srcbuffptr), (sm_uc_ptr_t)(trgbuffptr), \ + (curcsd)->blk_size, (ondskblkver)); \ + if (srcbuffptr != trgbuffptr) \ + srcbuffptr = NULL; \ + } \ + } else if (UPGRADE_NEVER == gtm_blkupgrade_flag) \ + { \ + upgrdstatus = SS_NORMAL; \ + if (NULL != (ondskblkver)) \ + *(ondskblkver) = GDSV5; \ + } else if (UPGRADE_ALWAYS == gtm_blkupgrade_flag) \ + { \ + upgrdstatus = gds_blk_upgrade((sm_uc_ptr_t)(srcbuffptr), (sm_uc_ptr_t)(trgbuffptr), \ + (curcsd)->blk_size, (ondskblkver)); \ + if (NULL != (ondskblkver)) \ + *(ondskblkver) = GDSV4; \ + if (srcbuffptr != trgbuffptr) \ + srcbuffptr = NULL; \ + } \ +} + +/* This macro is invoked by dsk_read.c when we know for sure we are reading a valid block. This checks that the block + * we read from disk contains a valid block header. It also checks that we CANNOT have read a V4 format reused block + * if the database has been fully upgraded. It uses checks similar to those used in the GDS_BLK_UPGRADE_IF_NEEDED + * macro to determine if it is a V4 or V5 format block header. + * Note: csd->fully_upgraded is not derived within the macro but instead passed in as a parameter to ensure whichever + * function (dsk_read currently) references this does that once and copies the value into a local variable that is used + * in all further usages. This way multiple usages are guaranteed to see the same value. Using csd->fully_upgraded in + * each of those cases could cause different values to be seen (since csd can be concurrently updated). + */ +#define GDS_BLK_HDR_CHECK(csd, v5_blk_hdr, fully_upgraded) \ +{ \ + v15_blk_hdr_ptr_t v4_blk_hdr; \ + \ + v4_blk_hdr = (v15_blk_hdr_ptr_t)v5_blk_hdr; \ + if (!(fully_upgraded) || (SIZEOF(v15_blk_hdr) > v4_blk_hdr->bsiz)) \ + { /* V5 formatted buffer in shared memory (even though might be V4 format in disk) */ \ + assert((unsigned)GDSVLAST > (unsigned)v5_blk_hdr->bver); \ + assert((LCL_MAP_LEVL == v5_blk_hdr->levl) || ((unsigned)MAX_BT_DEPTH > (unsigned)v5_blk_hdr->levl)); \ + assert((unsigned)size >= (unsigned)v5_blk_hdr->bsiz); \ + assert(csd->trans_hist.curr_tn >= v5_blk_hdr->tn); \ + } else \ + { /* V4 formatted buffer in shared memory (not converted because fully_upgraded is TRUE). \ + * Possible if we are reading a recycled block that is in V4 format from a fully upgraded database. \ + * But all recycled blocks are now upgraded by MUPIP REORG UPGRADE so this should be impossible. \ + */ \ + assert(FALSE); \ + } \ +} + +#endif diff --git a/sr_port/gds_map_moved.c b/sr_port/gds_map_moved.c new file mode 100644 index 0000000..ea0b117 --- /dev/null +++ b/sr_port/gds_map_moved.c @@ -0,0 +1,134 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gdsroot.h" +#include "gdskill.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdscc.h" +#include "filestruct.h" +#include "jnl.h" +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" +#include "gds_map_moved.h" +#include "hashtab_mname.h" +#include "dpgbldir.h" +#include "gtmimagename.h" + +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF gd_addr *gd_header; + +void gds_map_moved(sm_uc_ptr_t new_base, sm_uc_ptr_t old_base, sm_uc_ptr_t old_top, off_t new_eof) +{ + int hist_index; + sm_long_t adj; + srch_hist *hist, *dir_hist, *dir_alt_hist, *hist1, *hist2; + ht_ent_mname *tabent, *topent; + gv_namehead *gvt; + gvnh_reg_t *gvnh_reg; + hash_table_mname *tbl; + gd_addr *addr_ptr; + + assert(cs_addrs->now_crit); + /* It's possible to arrive here via mupip_backup --> wcs_flu --> wcs_mm_recover --> gds_map_moved and have + * cs_addrs->dir_tree be NULL. To distinguish that case, we can also check if this this is the GTM image + * in the following assert and then return because there's nothing to do here. + */ + assert(!IS_GTM_IMAGE || ((NULL != cs_addrs->dir_tree) && (NULL != &cs_addrs->dir_tree->hist))); + /* This initialization has to be done irrespective of whether new_base is different from old_base or not. */ + cs_data = cs_addrs->hdr = (sgmnt_data_ptr_t)new_base; + cs_addrs->db_addrs[1] = new_base + new_eof - 1; + cs_addrs->bmm = MM_ADDR(cs_data); + cs_addrs->acc_meth.mm.base_addr = (sm_uc_ptr_t)((sm_uc_ptr_t)cs_data + (cs_data->start_vbn - 1) * DISK_BLOCK_SIZE); + if (NULL != cs_addrs->sgm_info_ptr) + cs_addrs->sgm_info_ptr->tp_csd = cs_addrs->hdr; + bt_init(cs_addrs); + if (NULL == cs_addrs->dir_tree) + return; + /* The following adjustment needs to be done only if new_base is different from old_base */ + if (new_base == old_base) + return; + adj = (sm_long_t)(new_base - old_base); + assert(0 != adj); + dir_hist = &cs_addrs->dir_tree->hist; + dir_alt_hist = cs_addrs->dir_tree->alt_hist; + for (hist = dir_hist; (NULL != hist); hist = (hist == dir_hist) ? dir_alt_hist : NULL) + { + for (hist_index = 0; HIST_TERMINATOR != hist->h[hist_index].blk_num; hist_index++) + { + assert(MAX_BT_DEPTH >= hist_index); + if ((old_base <= hist->h[hist_index].buffaddr) && + (old_top > hist->h[hist_index].buffaddr)) + { + hist->h[hist_index].buffaddr += adj; + assert(new_base <= hist->h[hist_index].buffaddr); + } else + { + /* It has to be a private copy */ + assert((hist->h[hist_index].first_tp_srch_status != 0) || + (((off_chain *)&(hist->h[hist_index].blk_num))->flag != 0)); + } + } + } + /* It is possible that more than one global directory has regions mapping to the same physical database file. + * In this case, the search histories in the gv_targets hash tables of all those glds should be fixed. Hence, + * we go through all open glds (instead of just the currently active gd_header). + */ + assert(NULL != get_next_gdr(NULL)); /* assert that we have at least one open global directory */ + for (addr_ptr = get_next_gdr(NULL); NULL != addr_ptr; addr_ptr = get_next_gdr(addr_ptr)) + { + tbl = addr_ptr->tab_ptr; + assert(NULL != tbl); + for (tabent = tbl->base, topent = tbl->top; tabent < topent; tabent++) + { + if ((HTENT_VALID_MNAME(tabent, gvnh_reg_t, gvnh_reg)) && (NULL != (gvt = gvnh_reg->gvt)) + && (cs_addrs == gvt->gd_csa) && (0 < gvt->clue.end)) + { + hist1 = &gvt->hist; + hist2 = gvt->alt_hist; + for (hist = hist1; (NULL != hist); hist = (hist == hist1) ? hist2 : NULL) + { + if (((hist == hist1) && (hist == dir_hist)) + || ((hist == hist2) && (hist == dir_alt_hist))) + continue; + for (hist_index = 0; HIST_TERMINATOR != hist->h[hist_index].blk_num; hist_index++) + { + assert(MAX_BT_DEPTH >= hist_index); + if ((old_base <= hist->h[hist_index].buffaddr) + && (old_top > hist->h[hist_index].buffaddr)) + { + hist->h[hist_index].buffaddr += adj; + assert(new_base <= hist->h[hist_index].buffaddr); + } else if ((hist == hist2) && (0 < gvt->clue.end)) + { /* alt_hist is not updated when clue is set so the buffaddr can + * point to a prior instance of the file's mapping. So, reset alt_hist. + */ + hist->h[hist_index].blk_num = HIST_TERMINATOR; + } else + { /* It's already been adjusted or it has to be a private copy */ + assert(((new_base <= hist->h[hist_index].buffaddr) + && (hist->h[hist_index].buffaddr < new_base + (old_top - old_base))) + || (0 != hist->h[hist_index].first_tp_srch_status) + || (0 != ((off_chain *)&(hist->h[hist_index].blk_num))->flag)); + } + } + } + } + } + } + return; +} diff --git a/sr_port/gds_map_moved.h b/sr_port/gds_map_moved.h new file mode 100644 index 0000000..7a37e5f --- /dev/null +++ b/sr_port/gds_map_moved.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GDS_MAP_MOVED_INCLUDED +#define GDS_MAP_MOVED_INCLUDED + +void gds_map_moved(sm_uc_ptr_t new_base, sm_uc_ptr_t old_base, sm_uc_ptr_t old_top, off_t new_eof); + +#endif /* GDS_MAP_MOVED_INCLUDED */ diff --git a/sr_port/gds_rundown.h b/sr_port/gds_rundown.h new file mode 100644 index 0000000..15ab79f --- /dev/null +++ b/sr_port/gds_rundown.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GDS_RUNDOWN_INCLUDED +#define GDS_RUNDOWN_INCLUDED + +void gds_rundown(void); + +#endif /* GDS_RUNDOWN_INCLUDED */ diff --git a/sr_port/gdsbgtr.h b/sr_port/gdsbgtr.h new file mode 100644 index 0000000..949d6a3 --- /dev/null +++ b/sr_port/gdsbgtr.h @@ -0,0 +1,31 @@ +/**************************************************************** + * * + * Copyright 2005 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* Define macros to keep statistics of certain parts of code we pass + * through, some only if we are in debug mode. + * + * Although the incremented counters are generally in shared storage, + * we will not do interlock adds to them because even though there + * may be some interference, most will succeed and we are only looking + * for trends from these numbers anyway, not exact counts. + */ + + +#define BG_TRACE_PRO_ANY(C, X) {C->hdr->X##_cntr++; C->hdr->X##_tn = C->ti->curr_tn;} +#define BG_TRACE_PRO(Q) BG_TRACE_PRO_ANY(cs_addrs, Q) + +#ifdef DEBUG +#define BG_TRACE_ANY(C, X) BG_TRACE_PRO_ANY(C, X) +#define BG_TRACE(Q) BG_TRACE_ANY(cs_addrs, Q) +#else +#define BG_TRACE_ANY(C, X) +#define BG_TRACE(Q) +#endif diff --git a/sr_port/gdsblk.h b/sr_port/gdsblk.h new file mode 100644 index 0000000..4cecaeb --- /dev/null +++ b/sr_port/gdsblk.h @@ -0,0 +1,115 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __GDSBLK_H__ +#define __GDSBLK_H__ + +/* gdsblk.h */ + +#include + +#define BML_LEVL ((unsigned char)-1) + +#define CST_BSIZ(b) ((b)->bsiz - SIZEOF(blk_hdr)) +#define CST_RSIZ(r) ((r)->rsiz - SIZEOF(rec_hdr)) +#define CST_TOR(r) ((uchar_ptr_t)(r) + (r)->rsiz) +#define CST_TOB(b) ((uchar_ptr_t)(b) + (b)->bsiz) +#define CST_RIB(r,b) ((uchar_ptr_t)(r) + (r)->rsiz <= CST_TOB(b)) +#define CST_1ST_REC(b) ((rec_hdr_ptr_t)((uchar_ptr_t)(b) + SIZEOF(blk_hdr))) +#define CST_NXT_REC(r) ((uchar_ptr_t)(r) + (r)->rsiz) +#define CST_BOK(r) ((uchar_ptr_t)(r) + SIZEOF(rec_hdr)) +#define CST_USAR(b, r) ((b)->bsiz - ((uchar_ptr_t(r) + (r)->rsiz - (uchar_ptr_t)(b))) +#define CST_KSIZ (gv_curr_key->end - gv_curr_key->base + 1) +#define BSTAR_REC_SIZE INTCAST((SIZEOF(rec_hdr) + SIZEOF(block_id))) +#define BSTAR_REC(r) ((rec_hdr_ptr_t)(r))->rsiz = BSTAR_REC_SIZE; ((rec_hdr_ptr_t)(r))->cmpc = 0; +#define IS_LEAF(b) (0 == ((blk_hdr_ptr_t)(b))->levl) +#define IS_BML(b) (BML_LEVL == ((blk_hdr_ptr_t)(b))->levl) +#define IS_BSTAR_REC(r) ((r)->rsiz == BSTAR_REC_SIZE) +#define GAC_RSIZE(rsize,r,tob) if ((uchar_ptr_t)(r) + ((rsize) = ((rec_hdr_ptr_t)(r))->rsiz) > tob) return(-1) +#define MIN_DATA_SIZE 1 + 2 /* 1 byte of key + 2 nulls for terminator */ +#define MAX_EXTN_COUNT 65535 +#define MIN_EXTN_COUNT 0 +#define MAX_DB_BLK_SIZE ((1 << 16) - 512) /* 64Kb - 512 (- 512 to take care of VMS's max I/O capabilities) */ + +#if defined(__alpha) && defined(__vms) +# pragma member_alignment save +# pragma nomember_alignment +#endif + +/* Version 4 block header */ +typedef struct +{ + unsigned short bsiz; /* block size */ + unsigned char levl; /* block level. level 0 is data level. level 1 is + * first index level. etc. + */ + uint4 tn; /* transaction number when block was written */ +} v15_blk_hdr; + +/* Current block header */ +typedef struct +{ + unsigned short bver; /* block version - overlays V4 block size */ + unsigned char filler; + unsigned char levl; /* block level. level 0 is data level. level 1 is + * first index level. etc. + */ + unsigned int bsiz; /* block size */ + trans_num tn; /* transaction number when block was written */ +} blk_hdr; + +typedef struct +{ + unsigned short rsiz; + unsigned char cmpc; +} rec_hdr; + +#if defined(__alpha) && defined(__vms) +# pragma member_alignment restore +#endif + +/* Define pointer types to above structures */ +#ifdef DB64 +# ifdef __osf__ +# pragma pointer_size(save) +# pragma pointer_size(long) +# else +# error UNSUPPORTED PLATFORM +# endif +#endif + +typedef v15_blk_hdr *v15_blk_hdr_ptr_t; /* From jnl format 15 used in last GT.M V4 version */ +typedef blk_hdr *blk_hdr_ptr_t; +typedef rec_hdr *rec_hdr_ptr_t; + +#ifdef DB64 +# ifdef __osf__ +# pragma pointer_size(restore) +# endif +#endif + +#define MAX_RESERVE_B(X) ((X)->blk_size - (X)->max_rec_size - SIZEOF(blk_hdr)) +#define CHKRECLEN(r,b,n) ((unsigned int)((n) + (uchar_ptr_t)(r) - (uchar_ptr_t)(b)) <= (unsigned int)((blk_hdr_ptr_t)(b))->bsiz) + +/********************************************************************* + read record size from REC_BASE (temp_ushort must be defined) + *********************************************************************/ +#define GET_RSIZ(REC_SIZE, REC_BASE) \ + GET_USHORT(temp_ushort, &(((rec_hdr_ptr_t)(REC_BASE))->rsiz)); \ + REC_SIZE = temp_ushort + +int4 bm_find_blk(int4 hint, sm_uc_ptr_t base_addr, int4 total_bits, boolean_t *used); +void bm_setmap(block_id bml, block_id blk, int4 busy); +void bml_newmap(blk_hdr_ptr_t ptr, uint4 size, trans_num curr_tn); + +/* End of gdsblk.h */ + +#endif diff --git a/sr_port/gdsblkops.h b/sr_port/gdsblkops.h new file mode 100644 index 0000000..9ec760e --- /dev/null +++ b/sr_port/gdsblkops.h @@ -0,0 +1,252 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __GDSBLK_OPS_H__ +#define __GDSBLK_OPS_H__ + +/* Block segment array holds the list of memcpys needed to build the updated block. The first entry in the array holds the + length of the updated block and the address of the last entry in the array that holds valid data. + The arrays are processed at t_end time, and the copies are done starting with the last element in the array +*/ + +/* HEADER-FILE-DEPENDENCIES : min_max.h */ + +typedef struct +{ + sm_uc_ptr_t addr; + sm_ulong_t len; +} blk_segment; + +#define BLK_SEG_ARRAY_SIZE 20 + +/* although kills may have MAX_BT_DEPTH * 2 - 1 elements, each element is limited to key_size and gives a smaller max than puts */ + +/* *************************************************************************** + * The following is the splitup of the calculation of maximum-update-array-size for one non-TP action (for a PUT) + * + * BLK_INIT, BLK_FINI space ---> CDB_CW_SET_SIZE * (BLK_SEG_ARRAY_SIZE * SIZEOF(blk_segment)) + * BLK_ADDR leaf-level space---> 2 * cs_data->blk_size (for current and new sibling) + * BLK_ADDR index-level space--> (MAX_BT_DEPTH - 1) * (2 * (MAX_KEY_SZ + SIZEOF(rec_hdr) + SIZEOF(block_id)) + SIZEOF(rec_hdr)) + * 2 extra space ---> cs_data->blk_size + BSTAR_REC_SIZE (needed in case of global-variable creation) + * Bitmap BLK_ADDR space ---> (MAX_BT_DEPTH + 1) * (SIZEOF(block_id) * (BLKS_PER_LMAP + 1)) + * + */ + +#define UPDATE_ELEMENT_ALIGN_SIZE 8 +#define UPDATE_ARRAY_ALIGN_SIZE (1 << 14) /* round up the update array to a 16K boundary */ +#define MAX_BITMAP_UPDATE_ARRAY_SIZE ((MAX_BT_DEPTH + 1) * ROUND_UP2(SIZEOF(block_id) * (BLKS_PER_LMAP + 1), \ + UPDATE_ELEMENT_ALIGN_SIZE)) +#define MAX_NON_BITMAP_UPDATE_ARRAY_SIZE(csd) \ + (CDB_CW_SET_SIZE * ROUND_UP2(BLK_SEG_ARRAY_SIZE * SIZEOF(blk_segment), UPDATE_ELEMENT_ALIGN_SIZE) \ + + 2 * ROUND_UP2(csd->blk_size, UPDATE_ELEMENT_ALIGN_SIZE) \ + + (MAX_BT_DEPTH - 1) * \ + (3 * ROUND_UP2(SIZEOF(rec_hdr), UPDATE_ELEMENT_ALIGN_SIZE) \ + + 2 * ROUND_UP2(SIZEOF(block_id), UPDATE_ELEMENT_ALIGN_SIZE) + 2 * ROUND_UP2(MAX_KEY_SZ, 8)) \ + + ROUND_UP2(csd->blk_size, UPDATE_ELEMENT_ALIGN_SIZE) + ROUND_UP2(BSTAR_REC_SIZE, UPDATE_ELEMENT_ALIGN_SIZE)) + +#define UA_SIZE(X) (uint4)(X->max_update_array_size) +#define UA_NON_BM_SIZE(X) (uint4)(X->max_non_bm_update_array_size) + +#define ENSURE_UPDATE_ARRAY_SPACE(space_needed) \ +{ \ + GBLREF ua_list *first_ua, *curr_ua; \ + GBLREF char *update_array, *update_array_ptr; \ + GBLREF uint4 update_array_size, cumul_update_array_size; \ + GBLREF uint4 dollar_tlevel; \ + ua_list *tmpua; \ + \ + assert((0 != update_array_size) && (NULL != update_array)); \ + if (ROUND_DOWN2(update_array + update_array_size - update_array_ptr, UPDATE_ELEMENT_ALIGN_SIZE) < (space_needed)) \ + { /* the space remaining is too small for safety - chain on a new array */ \ + assert((NULL != first_ua) && (NULL != curr_ua)); \ + if ((NULL == curr_ua) || (NULL == curr_ua->next_ua)) \ + { \ + /* care should be taken to ensure things will work right even if malloc() errors out with ERR_MEMORY. \ + * that is why multiple assignments are not done in a single line that has a malloc() call in it. \ + * in addition, the field taking in the result of malloc() should be initialized to NULL before the \ + * call. This is because tp_clean_up() (invoked in case of error handling) relies on the integrity of \ + * this update array linked list in order to do its cleanup. Not following the above rules will cause \ + * difficult-to-debug memory related problems (even corruption) */ \ + tmpua = (ua_list *)malloc(SIZEOF(ua_list)); \ + memset(tmpua, 0, SIZEOF(ua_list)); /* initialize tmpua->update_array and tmpua->next_ua to NULL */ \ + /* it is important that all parameters in the MIN-MAX calculation below be unsigned numbers */ \ + tmpua->update_array_size = MIN(MAX(cumul_update_array_size, (space_needed)), BIG_UA); \ + tmpua->update_array = (char *)malloc(tmpua->update_array_size); \ + /* update globals only after above mallocs succeed */ \ + cumul_update_array_size += tmpua->update_array_size; \ + if (NULL == curr_ua) /* in PRO, don't take chances, reset first_ua/curr_ua to newly created upd array */\ + { \ + if (dollar_tlevel && (NULL != first_ua)) /* if already in TP, we will lose all updates until now\ + if we reset first_ua. do not proceed in this case */\ + GTMASSERT; \ + first_ua = curr_ua = tmpua; \ + } else \ + curr_ua = curr_ua->next_ua = tmpua; \ + } else \ + { /* No need to do malloc as curr_ua->next_ua could be REUSED */ \ + curr_ua = curr_ua->next_ua; \ + /* No need to reset cumul_update_array_size as no ua_list is deleted/added from/to the first_ua \ + * linked list */ \ + } \ + assert((NULL != first_ua) && (NULL != curr_ua)); \ + update_array_size = curr_ua->update_array_size; \ + update_array = update_array_ptr = curr_ua->update_array; \ + } \ +} + +/* The following macro resets update_array_ptr to point to update_array. + * This needs to be done before using any BLK_* macros as part of a non-TP transaction. + */ +#define RESET_UPDATE_ARRAY \ +{ \ + GBLREF char *update_array, *update_array_ptr; \ + \ + assert(NULL != update_array); \ + /* reset update_array to the start */ \ + update_array_ptr = update_array; \ +} + +/* the following macro does what RESET_UPDATE_ARRAY does and additionally does some integrity checks */ +#define CHECK_AND_RESET_UPDATE_ARRAY \ +{ \ + GBLREF uint4 dollar_tlevel; \ + GBLREF unsigned char cw_set_depth; \ + \ + assert(!dollar_tlevel); /* TP should never use this update_array */ \ + assert(0 == cw_set_depth); /* ensure we never reset an active update_array */ \ + RESET_UPDATE_ARRAY; \ +} + +/* *************************************************************************** + * BLK_INIT(BNUM, ARRAY) allocates: + * blk_segment ARRAY[BLK_SEG_ARRAY_SIZE] + * at the next octaword-aligned location in the update array and sets + * BNUM = &ARRAY[1] + */ + +#define BLK_INIT(BNUM, ARRAY) \ +{ \ + GBLREF char *update_array, *update_array_ptr; \ + \ + update_array_ptr = (char*)ROUND_UP2((INTPTR_T)update_array_ptr, UPDATE_ELEMENT_ALIGN_SIZE); \ + (ARRAY) = (blk_segment*)update_array_ptr; \ + update_array_ptr += (BLK_SEG_ARRAY_SIZE * SIZEOF(blk_segment)); \ + assert((update_array + update_array_size) - update_array_ptr >= 0); \ + (BNUM) = (ARRAY + 1); \ + blk_seg_cnt = SIZEOF(blk_hdr); \ +} + +/* *************************************************************************** + * BLK_SEG(BNUM, ADDR, LEN) adds a new entry to the blk_segment array + */ + +#define BLK_SEG(BNUM, ADDR, LEN) \ +{ \ + sm_ulong_t lcl_len1; \ + GBLREF uint4 dollar_tlevel; \ + \ + /* Note that name of len variable "lcl_len1" should be different \ + * from the name used in the REORG_BLK_SEG macro as otherwise \ + * the below assignment will leave lcl_len1 uninitialized. \ + */ \ + lcl_len1 = (LEN); \ + /* The function "gvcst_blk_build" (which uses this update array to build the block) relies on "len" to \ + * be positive. This macro is called from both non-TP and TP code. In non-TP, we know of a few callers \ + * (dse etc.) that could pass in a negative length to the BLK_SEG macro. Those are okay since we are \ + * guaranteed the validation (in t_end inside of crit) would catch this case and force a restart thus \ + * avoiding a call to gvcst_blk_build. But in TP, validations happen before commit time in tp_hist and \ + * it has a few optimizations where for globals with NOISOLATION turned ON, it allows validation to \ + * succeed even if the block contents changed concurrently. This means update arrays with negative \ + * lengths could find their way to gvcst_blk_build even after tp_hist. Since those lengths are passed \ + * directly to memmove which treats it as an unsigned quantity, it means huge memmoves that are likely \ + * to cause memory corruption and/or SIG-11. Therefore it is absolutely necessary that if we are in TP \ + * the caller does not pass in a negative length. Assert that. \ + */ \ + assert(!dollar_tlevel || (0 <= (sm_long_t)lcl_len1)); \ + (BNUM)->addr = (ADDR); \ + (BNUM)->len = lcl_len1; \ + blk_seg_cnt += (int)lcl_len1; \ + assert((char *)BNUM - update_array_ptr < 0); \ + (BNUM)++; \ +} + +/* *************************************************************************** + * BLK_FINI(BNUM,ARRAY) finishes the update array by + * BNUM->addr = 0 + * BNUM-- + * if the blk_seg_cnt is within range, then + * ARRAY[0].addr = BNUM (address of last entry containing data) + * ARRAY[0].len = blk_seg_cnt (total size of all block segments) + * and it returns the value of blk_seg_cnt, + * otherwise, it returns zero and the caller should invoke t_retry + */ + +#define BLK_FINI(BNUM,ARRAY) \ +( \ + (BNUM--)->addr = (uchar_ptr_t)0, \ + (blk_seg_cnt <= blk_size && blk_seg_cnt >= SIZEOF(blk_hdr)) \ + ? (ARRAY)[0].addr = (uchar_ptr_t)(BNUM), (ARRAY)[0].len = blk_seg_cnt \ + : 0 \ +) + +/* *************************************************************************** + * BLK_ADDR(X,Y,Z) allocates a space of length Y in the update array + * and sets pointer X (of type Z) to the beginning of that space + */ + +#ifdef DEBUG +#define BLK_ADDR(X,Y,Z) \ +( \ + update_array_ptr = (char*)(((INTPTR_T)update_array_ptr + 7) & ~7), \ + assert((update_array + update_array_size - Y) - update_array_ptr >= 0), \ + (X) = (Z*)update_array_ptr, update_array_ptr += (INTPTR_T)Y \ +) +#else +#define BLK_ADDR(X,Y,Z) \ +( \ + update_array_ptr = (char*)(((INTPTR_T)update_array_ptr + 7) & ~7), \ + (X) = (Z*)update_array_ptr, update_array_ptr += (INTPTR_T)Y \ +) +#endif + +/* ******************************************************************************** + * REORG_BLK_SEG(BNUM, ADDR, LEN) is the same as the BLK_SEG macro + * except that it takes a private copy of the input memory and adds that + * to the update array. This is necessary in case of MUPIP REORG for two + * operations (Coalesce and Swap). In either cases, the contents of one block + * will rely on the contents of itself and another block. This is not currently + * supported by t_end where the assumption is that all contents needed to build + * a buffer are available in that buffer itself (this greatly simplifies the + * process of pinning of buffers in shared memory). To avoid cross-links to other + * buffers, we need to take a copy of the other buffer's contents from shared + * memory into private memory before adding it to the update array. This macro + * should be called only by MUPIP REORG. An assert has been added to that effect. + */ +#define REORG_BLK_SEG(BNUM, ADDR, LEN) \ +{ \ + char *lcl_ptr; \ + sm_ulong_t lcl_len; \ + \ + GBLREF boolean_t mu_reorg_process; \ + \ + assert(mu_reorg_process); \ + lcl_len = (LEN); \ + if ((0 > (sm_long_t)lcl_len) || ((blk_seg_cnt + lcl_len) > blk_size)) \ + { \ + assert(CDB_STAGNATE > t_tries); \ + return cdb_sc_blkmod; \ + } \ + BLK_ADDR(lcl_ptr, lcl_len, char); \ + memcpy(lcl_ptr, (ADDR), lcl_len); \ + BLK_SEG((BNUM), (sm_uc_ptr_t)lcl_ptr, lcl_len); \ +} + +#endif diff --git a/sr_port/gdsbml.h b/sr_port/gdsbml.h new file mode 100644 index 0000000..98be521 --- /dev/null +++ b/sr_port/gdsbml.h @@ -0,0 +1,91 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#define SIXTEEN_BLKS_FREE 0x55555555 +#define FOUR_BLKS_FREE 0x55 +#define THREE_BLKS_FREE 0x54 +#define LCL_MAP_LEVL 0xFF +#define BLK_BUSY 0x00 +#define BLK_FREE 0x01 +#define BLK_MAPINVALID 0x02 +#define BLK_RECYCLED 0x03 +#define BML_BITS_PER_BLK 2 +#define THREE_BLKS_BITMASK 0x3F +#define TWO_BLKS_BITMASK 0x0F +#define ONE_BLK_BITMASK 0x03 +#define BMP_EIGHT_BLKS_FREE 255 + +/* returns the bitmap status (BLK_BUSY|BLK_FREE|etc.) of the "blknum"th block within the local bitmap block "bp" in bml_status */ +#define GET_BM_STATUS(bp, blknum, bml_status) \ +{ \ + sm_uc_ptr_t ptr; \ + \ + ptr = ((sm_uc_ptr_t)(bp) + SIZEOF(blk_hdr) + ((blknum * BML_BITS_PER_BLK) / BITS_PER_UCHAR)); \ + bml_status = (*ptr >> ((blknum * BML_BITS_PER_BLK) % BITS_PER_UCHAR)) & ((1 << BML_BITS_PER_BLK) - 1); \ +} + +/* sets bitmap status (BLK_BUSY|BLK_FREE etc.) for the "blknum"th block within the local bitmap block "bp" from new_bml_status */ +#define SET_BM_STATUS(bp, blknum, new_bml_status) \ +{ \ + sm_uc_ptr_t ptr; \ + \ + assert(2 == BML_BITS_PER_BLK); \ + ptr = ((sm_uc_ptr_t)(bp) + SIZEOF(blk_hdr) + ((blknum * BML_BITS_PER_BLK) / BITS_PER_UCHAR)); \ + *ptr = (*ptr & ~(0x03 << ((blknum * BML_BITS_PER_BLK) % BITS_PER_UCHAR))) \ + | ((new_bml_status & 0x03) << ((blknum * BML_BITS_PER_BLK) % BITS_PER_UCHAR)); \ +} + +#define BM_MINUS_BLKHDR_SIZE(bplm) ((bplm) / (BITS_PER_UCHAR / BML_BITS_PER_BLK)) +#define BM_SIZE(bplm) (SIZEOF(blk_hdr) + BM_MINUS_BLKHDR_SIZE(bplm)) + +#define VALIDATE_BM_BLK(blk, bp, csa, region, status) \ +{ \ + error_def(ERR_DBBMLCORRUPT); \ + \ + assert(BITS_PER_UCHAR % BML_BITS_PER_BLK == 0); /* assert this for the BM_MINUS_BLKHDR_SIZE macro */ \ + if (IS_BITMAP_BLK(blk) && ((LCL_MAP_LEVL != (bp)->levl) || (BM_SIZE(csa->hdr->bplmap) != (bp)->bsiz))) \ + { \ + send_msg(VARLSTCNT(9) ERR_DBBMLCORRUPT, 7, DB_LEN_STR(region), \ + blk, (bp)->bsiz, (bp)->levl, &(bp)->tn, &csa->ti->curr_tn); \ + status = FALSE; \ + assert(FALSE); \ + } else \ + status = TRUE; \ +} + +#define NO_FREE_SPACE -1 + +/* MAP_RD_FAIL is hard coded into the file BML_GET_FREE.MAR */ +#define MAP_RD_FAIL -2 +#define EXTEND_SUSPECT -3 +#define FILE_EXTENDED -4 +#define FINAL_RETRY_FREEZE_PROG -5 + +#define GET_CDB_SC_CODE(gdsfilext_code, status) \ +{ \ + if (MAP_RD_FAIL == gdsfilext_code) \ + status = (enum cdb_sc)rdfail_detail; \ + else if (EXTEND_SUSPECT == gdsfilext_code) \ + status = (enum cdb_sc)cdb_sc_extend; \ + else if (NO_FREE_SPACE == gdsfilext_code) \ + status = cdb_sc_gbloflow; \ + else if (FINAL_RETRY_FREEZE_PROG == gdsfilext_code) \ + status = cdb_sc_needcrit; \ +} + +#define MASTER_MAP_BITS_PER_LMAP 1 + +int4 bml_find_free(int4 hint, uchar_ptr_t base_addr, int4 total_bits); +int4 bml_init(block_id bml); +uint4 bml_busy(uint4 setbusy, sm_uc_ptr_t map); +uint4 bml_free(uint4 setfree, sm_uc_ptr_t map); +uint4 bml_recycled(uint4 setfree, sm_uc_ptr_t map); + diff --git a/sr_port/gdsbt.h b/sr_port/gdsbt.h new file mode 100644 index 0000000..eaecedd --- /dev/null +++ b/sr_port/gdsbt.h @@ -0,0 +1,595 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GDSBT_H +#define GDSBT_H +/* this requires gdsroot.h */ + +#include +#ifdef MUTEX_MSEM_WAKE +#ifdef POSIX_MSEM +# include +#else +# include +#endif +#endif + +#ifdef VMS +# include /* for SS$_WASSET */ +# include "ast.h" /* for ENABLE/DISABLE */ +#elif defined(UNIX) +# include /* for _POSIX_HOST_NAME_MAX */ +# if defined(__osf__) +# include /* for _POSIX_HOST_NAME_MAX */ +# elif defined(SUNOS) && !defined(_POSIX_HOST_NAME_MAX) +# include /* for MAXHOSTNAMELEN (Solaris 9) */ +# endif +#endif + +#include "gvstats_rec.h" + +#define CR_NOTVALID (-1L) + +#define GTM64_WC_MAX_BUFFS (2*1024*1024)-1 /* to fit in an int4 */ +#define WC_MAX_BUFFS 64*1024 + +#define WC_DEF_BUFFS 128 +#define WC_MIN_BUFFS 64 + +#define MAX_LOCK_SPACE 65536 /* need to change these whenever global directory defaults change */ +#define MIN_LOCK_SPACE 10 + +#define MAX_REL_NAME 36 +#define MAX_MCNAMELEN 256 /* We do not support hostname truncation */ +#if defined(UNIX) +# if defined(_POSIX_HOST_NAME_MAX) +# if MAX_MCNAMELEN <= _POSIX_HOST_NAME_MAX /* _POSIX_HOST_NAME_MAX excludes terminating NULL */ +# error MAX_MCNAMELEN is not greater than _POSIX_HOST_NAME_MAX. +# endif +# elif defined(MAXHOSTNAMELEN) +# if MAX_MCNAMELEN < MAXHOSTNAMELEN /* MAXHOSTNAMELEN includes terminating NULL */ +# error MAX_MCNAMELEN is less than MAXHOSTNAMELEN. +# endif +# else +# error _POSIX_HOST_NAME_MAX or MAXHOSTNAMELEN not defined. +# endif +#endif + +#define GDS_LABEL_SZ 12 + +#define MAX_DB_WTSTARTS 2 /* Max number of "flush-timer driven" simultaneous writers in wcs_wtstart */ +#define MAX_WTSTART_PID_SLOTS 4 * MAX_DB_WTSTARTS /* Max number of PIDs for wcs_wtstart to save */ +#define MAX_KIP_PID_SLOTS 8 + +#define BT_FACTOR(X) (X) +#define FLUSH_FACTOR(X) ((X)-(X)/16) +#define BT_QUEHEAD (-2) +#define BT_NOTVALID (-1) +#define BT_MAXRETRY 3 +#define BT_SIZE(X) ((((sgmnt_data_ptr_t)X)->bt_buckets + 1 + ((sgmnt_data_ptr_t)X)->n_bts) * SIZEOF(bt_rec)) + /* parameter is *sgmnt_data*/ + /* note that the + 1 above is for the th_queue head which falls between the hash table and the */ + /* actual bts */ +#define HEADER_UPDATE_COUNT 1024 +#define LAST_WBOX_SEQ_NUM 1000 + +typedef struct +{ + trans_num curr_tn; + trans_num early_tn; + trans_num last_mm_sync; /* Last tn where a full mm sync was done */ + char filler_8byte[8]; /* previously header_open_tn but no longer used. + * cannot remove as this is part of database file header */ + trans_num mm_tn; /* Used to see if CCP must update master map */ + uint4 lock_sequence; /* Used to see if CCP must update lock section */ + uint4 ccp_jnl_filesize; /* Passes size of journal file if extended */ + volatile uint4 total_blks; /* Placed here so can be passed to other machines on cluster */ + volatile uint4 free_blocks; +} th_index; + +typedef struct +{ + struct + { + sm_off_t fl; + sm_off_t bl; /* self-relative queue entry */ + } blkque, tnque; /* for block number hash, lru queue */ + trans_num tn; /* transaction # #*/ + trans_num killtn; /* last transaction when this block was updated as part of an M-kill */ + block_id blk; /* block #*/ + int4 cache_index; + bool flushing; /* buffer is being flushed after a machine switch on a cluster */ + char filler[3]; + int4 filler_int4; /* maintain 8 byte alignment */ +} bt_rec; /* block table record */ + +/* This structure is used to access the transaction queue. It points at all but the + first two longwords of a bt_rec. CAUTION: there is no such thing as a queue of + th_recs, they are always bt_recs, and the extra two longwords are always there */ +typedef struct +{ + struct + { + sm_off_t fl; + sm_off_t bl; + } tnque; + trans_num tn; + trans_num killtn; /* last transaction when this block was updated as part of an M-kill */ + block_id blk; + int4 cache_index; + bool flushing; + char filler[3]; + int4 filler_int4; /* maintain 8 byte alignment */ +} th_rec; + +/* This structure is used to maintain all cache records. The BT queue contains + a history of those blocks that have been updated. */ + +/* + * Definitions for GT.M Mutex Control + * + * See MUTEX.MAR for VMS functional implementation + */ +typedef struct +{ + struct + { + sm_off_t fl, + bl; + } que; + int4 pid; + void *super_crit; +#if defined(UNIX) + /* + * If the following fields are to be made part of VMS too, change + * size of mutex_que_entry in mutex.mar (lines 118, 152). + * Make sure that the size of mutex_que_entry is a multiple of 8 bytes + * for quadword alignment requirements of remqhi and insqti. + */ + int4 mutex_wake_instance; + int4 filler1; /* for dword alignment */ +#ifdef MUTEX_MSEM_WAKE +#ifdef POSIX_MSEM + sem_t mutex_wake_msem; /* Not two ints .. somewhat larger */ +#else + msemaphore mutex_wake_msem; /* Two ints (incidentally two int4s) */ +#endif + +#endif +#endif +} mutex_que_entry; + +typedef struct +{ + struct + { + sm_off_t fl, + bl; + } que; + global_latch_t latch; +} mutex_que_head; + +typedef struct +{ +#if defined(UNIX) + global_latch_t semaphore; + CACHELINE_PAD(SIZEOF(global_latch_t), 1) + latch_t crashcnt; + int4 filler1; /* for alignment */ + global_latch_t crashcnt_latch; + CACHELINE_PAD(SIZEOF(latch_t) + SIZEOF(latch_t) + SIZEOF(global_latch_t), 2) + latch_t queslots; + int4 filler2; /* for alignment */ + CACHELINE_PAD(SIZEOF(latch_t) + SIZEOF(latch_t), 3) + mutex_que_head prochead; + CACHELINE_PAD(SIZEOF(mutex_que_head), 4) + mutex_que_head freehead; +#elif defined(VMS) + short semaphore; + unsigned short wrtpnd; + short crashcnt, + queslots; +#ifdef __alpha +/* use constant instead of defining CACHELINE_SIZE since we do not want to affect other structures + the 64 must match padding in mutex.mar and mutex_stoprel.mar */ + char filler1[64 - SIZEOF(short)*4]; +#endif + mutex_que_entry prochead; +#ifdef __alpha + char filler2[64 - SIZEOF(mutex_que_entry)]; +#endif + mutex_que_entry freehead; +#else +#error UNSUPPORTED PLATFORM +#endif +} mutex_struct; + +typedef struct { /* keep this structure and member offsets defined in sr_avms/mutex.mar in sync */ + int4 mutex_hard_spin_count; + int4 mutex_sleep_spin_count; + int4 mutex_spin_sleep_mask; + int4 filler1; +} mutex_spin_parms_struct; + +enum crit_ops +{ crit_ops_gw = 1, /* grab [write] crit */ + crit_ops_rw, /* rel [write] crit */ + crit_ops_nocrit /* did a rel_crit when now_crit flag was off */ +}; + +typedef struct +{ + caddr_t call_from; + enum crit_ops crit_act; + int4 epid; +} crit_trace; + +typedef struct +{ + sm_off_t cr_off; + trans_num cr_tn; + uint4 process_id; + block_id blk; + uint4 cycle; +} dskread_trace; + +#define OP_LOCK_SIZE 4 + +/* Structure to hold lock history */ +typedef struct +{ + sm_int_ptr_t lock_addr; /* Address of actual lock */ + caddr_t lock_callr; /* Address of (un)locker */ + int4 lock_pid; /* Process id of (un)locker */ + int4 loop_cnt; /* iteration count of lock retry loop */ + char lock_op[OP_LOCK_SIZE]; /* Operation performed (either OBTN or RLSE) */ +} lockhist; + +enum ftok_ops +{ + ftok_ops_lock = 1, + ftok_ops_release +}; + +typedef struct +{ + enum ftok_ops ftok_oper; + uint4 process_id; + trans_num cr_tn; +} ftokhist; + +#define DSKREAD_OPS_ARRAY_SIZE 512 +#define CRIT_OPS_ARRAY_SIZE 512 +#define LOCKHIST_ARRAY_SIZE 512 +#define SECSHR_OPS_ARRAY_SIZE 1023 /* 1 less than 1K to accommodate the variable secshr_ops_index */ +#define FTOK_OPS_ARRAY_SIZE 512 + +/* SECSHR_ACCOUNTING macro assumes csa->nl is dereferencible and does accounting if variable "do_accounting" is set to TRUE */ +#define SECSHR_ACCOUNTING(value) \ +{ \ + if (do_accounting) \ + { \ + if (csa->nl->secshr_ops_index < SECSHR_OPS_ARRAY_SIZE) \ + csa->nl->secshr_ops_array[csa->nl->secshr_ops_index] = (gtm_uint64_t)(value); \ + csa->nl->secshr_ops_index++; \ + } \ +} + +/* Mapped space local to each node on the cluster */ +typedef struct node_local_struct +{ + unsigned char label[GDS_LABEL_SZ]; /* 12 signature for GDS shared memory */ + unsigned char fname[MAX_FN_LEN + 1]; /* 256 filename of corresponding database */ + char now_running[MAX_REL_NAME]; /* 36 current active GT.M version stamp */ + char machine_name[MAX_MCNAMELEN]; /* 256 machine name for clustering */ + sm_off_t bt_header_off; /* (QW alignment) offset to hash table */ + sm_off_t bt_base_off; /* bt first entry */ + sm_off_t th_base_off; + sm_off_t cache_off; + sm_off_t cur_lru_cache_rec_off; /* current LRU cache_rec pointer offset */ + sm_off_t critical; + sm_off_t jnl_buff; + sm_off_t shmpool_buffer; /* Shared memory buffer pool area */ + sm_off_t lock_addrs; + sm_off_t hdr; /* Offset to file-header (BG mode ONLY!) */ + volatile int4 in_crit; + int4 in_reinit; + unsigned short ccp_cycle; + unsigned short filler; /* Align for ccp_cycle. Not changing to int + as that would perturb to many things at this point */ + boolean_t ccp_crit_blocked; + int4 ccp_state; + boolean_t ccp_jnl_closed; + boolean_t glob_sec_init; + uint4 wtstart_pid[MAX_WTSTART_PID_SLOTS]; /* Maintain pids of wcs_wtstart processes */ + int4 filler8_int; /* 8-byte alignment filler */ + global_latch_t wc_var_lock; /* latch used for access to various wc_* ref counters */ + CACHELINE_PAD(SIZEOF(global_latch_t), 1) /* Keep these two latches in separate cache lines */ + global_latch_t db_latch; /* latch for interlocking on hppa and tandem */ + CACHELINE_PAD(SIZEOF(global_latch_t), 2) + int4 cache_hits; + int4 wc_in_free; /* number of write cache records in free queue */ + /* All the counters below (declared using CNTR4DCL are 4-byte counters. We would like to keep them in separate + * cachelines on load-lock/store-conditional platforms particularly and other platforms too just to be safe. + */ + volatile CNTR4DCL(wcs_timers, 1); /* number of write cache timers in use - 1 */ + CACHELINE_PAD(4, 3) + volatile CNTR4DCL(wcs_active_lvl, 2); /* number of entries in active queue */ + CACHELINE_PAD(4, 4) + volatile CNTR4DCL(wcs_staleness, 3); + CACHELINE_PAD(4, 5) + volatile CNTR4DCL(ref_cnt, 4); /* reference count. How many people are using the database */ + CACHELINE_PAD(4, 6) + volatile CNTR4DCL(intent_wtstart, 5); /* Count of processes that INTEND to enter wcs_wtstart code */ + CACHELINE_PAD(4, 7) + volatile CNTR4DCL(in_wtstart, 6); /* Count of processes that are INSIDE wcs_wtstart code */ + CACHELINE_PAD(4, 8) + volatile CNTR4DCL(wcs_phase2_commit_pidcnt, 7); /* number of processes actively finishing phase2 commit */ + CACHELINE_PAD(4, 9) + int4 mm_extender_pid; /* pid of the process executing gdsfilext in MM mode */ + int4 highest_lbm_blk_changed; /* Records highest local bit map block that + changed so we know how much of master bit + map to write out. Modified only under crit */ + int4 nbb; /* Next backup block -- for online backup */ + int4 lockhist_idx; /* (DW alignment) "circular" index into lockhists array */ + int4 crit_ops_index; /* "circular" index into crit_ops_array */ + int4 dskread_ops_index; /* "circular" index into dskread_ops_array */ + int4 ftok_ops_index; /* "circular" index into ftok_ops_array */ + lockhist lockhists[LOCKHIST_ARRAY_SIZE]; /* Keep lock histories here */ + crit_trace crit_ops_array[CRIT_OPS_ARRAY_SIZE]; /* space for CRIT_TRACE macro to record info */ + dskread_trace dskread_ops_array[DSKREAD_OPS_ARRAY_SIZE]; /* space for DSKREAD_TRACE macro to record info */ + unique_file_id unique_id; + uint4 owner_node; + volatile int4 wcsflu_pid; /* pid of the process executing wcs_flu in BG mode */ + int4 creation_date_time4; /* Lower order 4-bytes of database's creation time to be + * compared at sm attach time */ + int4 inhibit_kills; /* inhibit new KILLs while MUPIP BACKUP, INTEG or FREEZE are + * waiting for kill-in-progress to become zero + */ + boolean_t remove_shm; /* can this shm be removed by the last process to rundown */ + union + { + gds_file_id jnl_file_id; /* needed on UNIX to hold space */ + unix_file_id u; /* from gdsroot.h even for VMS */ + } jnl_file; /* Note that in versions before V4.3-001B, "jnl_file" used to be a member of sgmnt_data. + * Now it is a filler there and rightly used here since it is non-zero only when shared memory is active. + */ + boolean_t donotflush_dbjnl; /* whether database and journal can be flushed to disk or not (TRUE for mupip recover) */ + int4 n_pre_read; + char replinstfilename[MAX_FN_LEN + 1];/* 256 : Name of the replication instance file corresponding to this db. + * In VMS, this is the name of the corresponding global directory. + */ + int4 secshr_ops_index; + gtm_uint64_t secshr_ops_array[SECSHR_OPS_ARRAY_SIZE]; /* taking up 8K */ + gvstats_rec_t gvstats_rec; + trans_num last_wcsflu_tn; /* curr_tn when last wcs_flu was done on this database */ + sm_off_t encrypt_glo_buff_off; /* offset from unencrypted global buffer to its encrypted counterpart */ + global_latch_t snapshot_crit_latch; /* To be acquired by any process that wants to clean up an orphaned snapshot or + * initiate a new snapshot + */ + long ss_shmid; /* Identifier of the shared memory for the snapshot that started + * recently. + */ + uint4 ss_shmcycle; /* incremented everytime a new snapshot creates a new shared memory identifier */ + boolean_t snapshot_in_prog; /* Tells GT.M if any snapshots are in progress */ + uint4 num_snapshots_in_effect; /* how many snapshots are currently in place for this region */ + uint4 wbox_test_seq_num; /* used to coordinate with sequential testing steps */ + uint4 kip_pid_array[MAX_KIP_PID_SLOTS]; /* Processes actively doing kill (0 denotes empty slots) */ + + gtm_uint64_t sec_size; /* Upon going to larger shared memory sizes, we realized that this does not */ + /* need to be in the file header but the node local since it can be calculated */ + /* from info in the file header. */ + uint4 filler_8byte_align; +# if defined(UNIX) + ftokhist ftok_ops_array[FTOK_OPS_ARRAY_SIZE]; +# endif +} node_local; + +#define DSKREAD_TRACE(CSA, CR_OFF, CR_TN, PID, BLK, CYCLE) \ +{ \ + int4 doidx; \ + node_local_ptr_t cnl; \ + assert((NULL != CSA)&& (NULL != (CSA->nl))); \ + cnl = CSA->nl; \ + doidx = ++cnl->dskread_ops_index; \ + if (DSKREAD_OPS_ARRAY_SIZE <= doidx) \ + doidx = cnl->dskread_ops_index = 0; \ + cnl->dskread_ops_array[doidx].cr_off = CR_OFF; \ + cnl->dskread_ops_array[doidx].cr_tn = CR_TN; \ + cnl->dskread_ops_array[doidx].process_id = PID; \ + cnl->dskread_ops_array[doidx].blk = BLK; \ + cnl->dskread_ops_array[doidx].cycle = CYCLE; \ +} + +#ifdef DEBUG +/* The following macro does not use a separate semaphore to protect its maintenance of the shared memory + * value crit_ops_index (which would complicate precisely the situation it was created to examine) therefore, + * in order to to maximize the chances of gathering meaningful data, it seems better placed after grab_crit + * and before rel_crit. Also we will increment the index first and cache it so we can shorten our exposure window. + */ +#define CRIT_TRACE(X) \ +{ \ + int4 coidx; \ + node_local_ptr_t cnl; \ + boolean_t in_ast; \ + unsigned int ast_status; \ + assert((NULL != csa) && (NULL !=(csa->nl))); \ + cnl = csa->nl; \ + coidx = ++cnl->crit_ops_index; \ + if (CRIT_OPS_ARRAY_SIZE <= coidx) \ + coidx = cnl->crit_ops_index = 0; \ + VMS_ONLY( \ + in_ast = lib$ast_in_prog(); \ + if (!in_ast) \ + ast_status = sys$setast(DISABLE); \ + ) \ + cnl->crit_ops_array[coidx].call_from = (caddr_t)caller_id(); \ + VMS_ONLY( \ + if ((!in_ast) && (SS$_WASSET == ast_status)) \ + sys$setast(ENABLE); \ + ) \ + cnl->crit_ops_array[coidx].epid = process_id; \ + cnl->crit_ops_array[coidx].crit_act = (X); \ +} + +/* The following macro checks that curr_tn and early_tn are equal right before beginning a transaction commit. + * The only exception we know of is if a process in the midst of commit had been killed (kill -9 or STOP/ID) + * after having incremented early_tn but before it finished the commit (and therefore incremented curr_tn). + * In that case another process that did a rundown (and executed secshr_db_clnup) at around the same time + * could have cleaned up the CRIT lock (sensing that the crit holder pid is no longer alive) making the crit + * lock available for other processes. To check if that is the case, we need to go back the crit_ops_array and check that + * a) the most recent crit operation was a grab crit done by the current pid (crit_act == crit_ops_gw) AND + * b) the immediately previous crit operation should NOT be a release crit crit_ops_rw but instead should be a crit_ops_gw + * c) there are two exceptions to this and they are + * (i) that there could be one or more crit_ops_nocrit actions from processes that tried releasing crit + * even though they dont own it (cases we know of are in gds_rundown and in t_end/tp_tend if + * t_commit_cleanup completes the transaction after a mid-commit error). + * (ii) there could be one or more crit_ops_gw/crit_ops_rw pair of operations by a pid in between. + */ +#define ASSERT_CURR_TN_EQUALS_EARLY_TN(csa, currtn) \ +{ \ + GBLREF uint4 process_id; \ + \ + assert((currtn) == csa->ti->curr_tn); \ + if (csa->ti->early_tn != (currtn)) \ + { \ + int4 coidx, lcnt; \ + node_local_ptr_t cnl; \ + uint4 expect_gw_pid = 0; \ + \ + cnl = csa->nl; \ + assert(NULL != (node_local_ptr_t)cnl); \ + coidx = cnl->crit_ops_index; \ + assert(CRIT_OPS_ARRAY_SIZE > coidx); \ + assert(crit_ops_gw == cnl->crit_ops_array[coidx].crit_act); \ + assert(process_id == cnl->crit_ops_array[coidx].epid); \ + for (lcnt = 0; CRIT_OPS_ARRAY_SIZE > lcnt; lcnt++) \ + { \ + if (coidx) \ + coidx--; \ + else \ + coidx = CRIT_OPS_ARRAY_SIZE - 1; \ + if (crit_ops_nocrit == cnl->crit_ops_array[coidx].crit_act) \ + continue; \ + if (crit_ops_rw == cnl->crit_ops_array[coidx].crit_act) \ + { \ + assert(0 == expect_gw_pid); \ + expect_gw_pid = cnl->crit_ops_array[coidx].epid; \ + } else if (crit_ops_gw == cnl->crit_ops_array[coidx].crit_act) \ + { \ + if (!expect_gw_pid) \ + break; /* found lone grab-crit */ \ + assert(expect_gw_pid == cnl->crit_ops_array[coidx].epid); \ + expect_gw_pid = 0;/* found paired grab-crit. continue search */ \ + } \ + } \ + assert(CRIT_OPS_ARRAY_SIZE > lcnt); /* assert if did not find lone grab-crit */ \ + } \ +} + +/* + * The following macro places lock history entries in an array for debugging. + * NOTE: Users of this macro, set either of the following prior to using this macro. + * (i) gv_cur_region to the region whose history we are storing. + * (ii) global variable "locknl" to correspond to the node-local of the region whose history we are storing. + * If "locknl" is non-NULL, it is used to store the lock history. If not only then is gv_cur_region used. + */ +#define LOCK_HIST(OP, LOC, ID, CNT) \ +{ \ + GBLREF node_local_ptr_t locknl; \ + \ + int lockidx; \ + node_local_ptr_t lcknl; \ + \ + if (NULL == locknl) \ + { \ + assert(NULL != gv_cur_region); \ + lcknl = FILE_INFO(gv_cur_region)->s_addrs.nl; \ + assert(NULL != lcknl); \ + } else \ + lcknl = locknl; \ + lockidx = ++lcknl->lockhist_idx; \ + if (LOCKHIST_ARRAY_SIZE <= lockidx) \ + lockidx = lcknl->lockhist_idx = 0; \ + GET_LONGP(&lcknl->lockhists[lockidx].lock_op[0], (OP)); \ + lcknl->lockhists[lockidx].lock_addr = (sm_int_ptr_t)(LOC); \ + lcknl->lockhists[lockidx].lock_callr = (caddr_t)caller_id(); \ + lcknl->lockhists[lockidx].lock_pid = (int4)(ID); \ + lcknl->lockhists[lockidx].loop_cnt = (int4)(CNT); \ +} + + +#define DUMP_LOCKHIST() dump_lockhist() +#else +#define CRIT_TRACE(X) +#define ASSERT_CURR_TN_EQUALS_EARLY_TN(csa, currtn) +#define LOCK_HIST(OP, LOC, ID, CNT) +#define DUMP_LOCKHIST() +#endif + +#define FTOK_TRACE(CSA, CR_TN, FTOK_OPER, PID) \ +{ \ + node_local_ptr_t cnl; \ + int4 foindx; \ + assert(NULL != CSA); \ + if (cnl = (CSA->nl)) \ + { \ + foindx = ++cnl->ftok_ops_index; \ + if (FTOK_OPS_ARRAY_SIZE <= cnl->ftok_ops_index) \ + foindx = cnl->ftok_ops_index = 0; \ + cnl->ftok_ops_array[foindx].process_id = PID; \ + cnl->ftok_ops_array[foindx].cr_tn = CR_TN; \ + cnl->ftok_ops_array[foindx].ftok_oper = FTOK_OPER; \ + } \ +} + +#define BT_NOT_ALIGNED(bt, bt_base) (!IS_PTR_ALIGNED((bt), (bt_base), SIZEOF(bt_rec))) +#define BT_NOT_IN_RANGE(bt, bt_lo, bt_hi) (!IS_PTR_IN_RANGE((bt), (bt_lo), (bt_hi))) + +#define NUM_CRIT_ENTRY 1024 +#define CRIT_SPACE (NUM_CRIT_ENTRY * SIZEOF(mutex_que_entry) + SIZEOF(mutex_struct)) +#define NODE_LOCAL_SIZE (ROUND_UP(SIZEOF(node_local), OS_PAGE_SIZE)) +#define NODE_LOCAL_SPACE (ROUND_UP(CRIT_SPACE + NODE_LOCAL_SIZE, OS_PAGE_SIZE)) +/* In order for gtmsecshr not to pull in OTS library, NODE_LOCAL_SIZE_DBS is used in secshr_db_clnup instead of NODE_LOCAL_SIZE */ +#define NODE_LOCAL_SIZE_DBS (ROUND_UP(SIZEOF(node_local), DISK_BLOCK_SIZE)) + +/* Define pointer types for above structures that may be in shared memory and need 64 + bit pointers. */ +#ifdef DB64 +# ifdef __osf__ +# pragma pointer_size(save) +# pragma pointer_size(long) +# else +# error UNSUPPORTED PLATFORM +# endif +#endif + +typedef bt_rec *bt_rec_ptr_t; +typedef th_rec *th_rec_ptr_t; +typedef th_index *th_index_ptr_t; +typedef mutex_struct *mutex_struct_ptr_t; +typedef mutex_spin_parms_struct *mutex_spin_parms_ptr_t; +typedef mutex_que_entry *mutex_que_entry_ptr_t; +typedef node_local *node_local_ptr_t; + +#ifdef DB64 +# ifdef __osf__ +# pragma pointer_size(restore) +# endif +#endif + +#include "cdb_sc.h" + +bt_rec_ptr_t bt_get(int4 block); +void dump_lockhist(void); +void wait_for_block_flush(bt_rec *bt, block_id block); + +#endif diff --git a/sr_port/gdscc.h b/sr_port/gdscc.h new file mode 100644 index 0000000..73f7bc4 --- /dev/null +++ b/sr_port/gdscc.h @@ -0,0 +1,194 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __GDSCC_H__ +#define __GDSCC_H__ + +/* this requires gdsroot.h gtm_facilit.h fileinfo.h gdsbt.h gdsfhead.h gdir.h gdskey.h */ + +#include + +/* BIG_UA is the maximum size of a single update array specified as an unsigned quantity (usages rely on this). It is 16MB. */ +#define BIG_UA (uint4)16777216 + +#define CDB_R_SET_SIZE 32 +#define CDB_CW_SET_SIZE (MAX_BT_DEPTH * 3 + 1 + 2) +#define CDB_W_SET_SIZE 16 + +/* CDB_CW_SET_SIZE = 24 + * 3 for all the levels (including updated block, newly created sibling and possible bitmap update) + * 1 extra for the root level (to take care of gds_t_write_root case) + * 2 in the case of creation of a new global variable (1 index block with a * key and 1 data block + * containing the key) + */ + +#define CDB_T_CREATE 0 +#define CDB_T_WRITE 1 +#define CDB_T_WRITE_ROOT 2 + +/* The following defines the write_type for a block that is going to be updated. + * GDS_WRITE_PLAIN is the default type for most updates. + * GDS_WRITE_BLOCK_SPLIT is set in case of a block update due to a block split. It is currently not used anywhere in the code. + * GDS_WRITE_KILLTN requires a little more explanation. + * + * The TP commit logic ("tp_tend") makes use of an optimization referred to as the "indexmod" optimization. + * This optimization tries to avoid a restart in the case where a TP transaction does a SET to a data block and later finds + * at TCOMMIT time that the index block which was part of the SET had been updated by a concurrent SET (or a REORG split + * operation) to a different data block (that also had the same index block as an ancestor) which resulted in a block split + * causing the index block to be updated. In this case there is no reason to restart. The index block could have been + * modified by other operations as well (e.g. M-kill, REORG coalesce or swap operations or any DSE command or a block split + * operation that caused the height of the global variable tree to increase [C9B11-001813]). In these cases, we dont want + * this optimization to take effect as we cant be sure everything that was relied upon for the TP transaction was still valid. + * These disallowed operations are generically referred to as "kill" type of operations. This optimization is implemented by + * having a field "killtn" (name derived from "kill" type of operations) in the bt (block-table) structure for each block. + * This field is assigned the same value as the "tn" whenever an index block gets updated due to one of the disallowed operations. + * Otherwise it stays untouched (i.e. killtn <= tn at all times). It is the "killtn" (and not "tn") that is used in the + * cdb_sc_blkmod validation check in tp_tend if we are validating a index block. Since KILLs, MUPIP REORG and/or DSE operations + * are usually rare compared to SET activity, most of the cases we expect the indexmod optimization to be in effect and + * therefore help reduce the # of TP restarts due to index block changes. Note that each SET/GET/KILL operation in TP goes + * through an intermediate validation routine tp_hist which does the cdb_sc_blkmod validation using "tn" (not "killtn"). + * Only if that passed, do we relax the commit time validation for index blocks. + * + * The operations that are not allowed to use this optimization (M-kill, REORG or DSE) are supposed to make sure they + * set the write_type of the cw-set-element to GDS_WRITE_KILLTN. Failing to do so cause "killtn" in the bt to NOT be uptodate + * which in turn can cause false validation passes (in the cdb_sc_blkmod check) causing GT.M processes to incorrectly commit + * when they should not. This can lead to GT.M/application level data integrity errors. + */ +#define GDS_WRITE_PLAIN 0 +#define GDS_WRITE_KILLTN 1 +#define GDS_WRITE_BLOCK_SPLIT 2 + +/* macro to traverse to the end of an horizontal cw_set_element list */ + +#define TRAVERSE_TO_LATEST_CSE(x) \ +{ \ + GBLREF uint4 dollar_tlevel; \ + \ + assert(dollar_tlevel); \ + if (x) \ + for ( ; (x)->high_tlevel; x = (x)->high_tlevel) \ + ; \ +} + +typedef uint4 block_offset; +typedef int4 block_index; + +/* If a new mode is added to the table below, make sure pre-existing mode usages in the current codebase are examined to see + * if the new mode needs to be added there as well. For example, there is code in tp_incr_commit.c and tp_incr_clean_up.c + * where gds_t_create and kill_t_create are used explicitly. If the new mode is yet another *create* type, then it might need + * to be added in those places as well. + */ +enum gds_t_mode +{ + gds_t_noop = 0, /* there is code that initializes stuff to 0 relying on it being equal to gds_t_noop */ + gds_t_create, + gds_t_write, + gds_t_write_recycled, /* modify a recycled block (currently only done by MUPIP REORG UPGRADE/DOWNGRADE) */ + gds_t_acquired, + gds_t_writemap, + gds_t_committed, /* t_end relies on this particular placement */ + gds_t_write_root, /* t_end relies on this being AFTER gds_t_committed */ + gds_t_busy2free, /* t_end relies on this being AFTER gds_t_committed */ + n_gds_t_op, /* tp_tend and other routines rely on this being BEFORE kill_t* modes and AFTER all gds_t_* modes */ + kill_t_create, /* tp_tend relies on this being AFTER n_gds_t_op */ + kill_t_write, /* tp_tend relies on this being AFTER n_gds_t_op */ +}; + +typedef struct key_value_struct +{ + gv_key key; /* note that the following array holds the actual key contents */ + char key_contents[DBKEYSIZE(MAX_KEY_SZ)]; + mstr value; + struct key_value_struct *next; +} key_cum_value; + +/* Create/write set element. This is used to describe modification of a database block */ + +typedef struct cw_set_element_struct +{ + trans_num tn; /* transaction number for bit maps */ + sm_uc_ptr_t old_block; /* Address of 'before-image' of block to be over-written */ + cache_rec_ptr_t cr; + struct cw_set_element_struct *next_cw_set; + struct cw_set_element_struct *prev_cw_set; /* linked list (vertical) of cw_set_elements with one link per block */ + + struct cw_set_element_struct *high_tlevel; + struct cw_set_element_struct *low_tlevel; /* linked list (horizontal) of cw_set elements for a given block with + * different transaction levels. Latest cw_set_elements (for a given block) + * are inserted at the beginning of the horizontal list */ + off_jnl_t jnl_freeaddr; /* journal update address */ + uint4 write_type; /* can be GDS_WRITE_PLAIN or GDS_WRITE_KILLTN or GDS_WRITE_BLOCK_SPLIT + * or bit-wise-or of both */ + key_cum_value *recompute_list_head; /* pointer to a list of keys (with values) that need to be recomputed */ + key_cum_value *recompute_list_tail; /* pointer to a list of keys (with values) that need to be recomputed */ + enum gds_t_mode mode; /* Create, write, or write root */ + block_id blk; /* Block number or a hint block number for creates */ + unsigned char *upd_addr; /* Address of the block segment array containing update info + * for this block */ + unsigned char *new_buff; /* Address of a buffer created for each global mentioned inside of a + * transaction more then once (for tp) */ + gv_namehead *blk_target; /* address of the "gv_target" associated with a new_buff + * used to invalidate clues that point to malloc'ed copies */ + int4 cycle; + + /* When a block splits a new block must be created and the parent must be updated to + * to have a record pointing to the new block. The created block number will not be + * known until the last possible moment. Thus it is not possible to completely modify + * the parent. The following 2 fields are used in such a case. "ins_off" tells where + * the created block's number should be put in the parent block. "index" tells which + * element of the create/write set is being created. + */ + + block_offset first_off; + block_offset ins_off; /* Insert block number offset */ + block_offset next_off; + block_index index; /* Insert block number index */ + int4 reference_cnt; /* Relevant only for a bitmap block. + * > 0 => # of non-bitmap blocks to be allocated in this bitmap; + * < 0 => # of non-bitmap blocks to be freed up in this bitmap; + * == 0 => change to bitmap block without any non-bitmap block change + * Used to update csd->free_blocks when the bitmap block is built + */ + int4 level; /* Block level for newly created blocks */ + boolean_t done; /* Has this update been done already? */ + boolean_t first_copy; /* If overlaying same buffer, set if first copy needed */ + /* just an optimisation - avoids copying first few bytes, if anyway + * we are just overlaying the new_buff in the same transaction */ + boolean_t forward_process; /* Need to process update array from front when doing kills */ + uint4 t_level; /* transaction level associated with cw element, for incremental rollback */ + enum db_ver ondsk_blkver; /* Actual block version from block header as it exists on disk. + * If "cse->mode" is gds_t_write_root, this is uninitialized. + * If "cse->mode" is gds_t_create/gds_t_acquired, this is GDSVCURR. + * Otherwise, this is set to cr->ondsk_blkver (cr is got from the history). + * Whenever "cse->old_block" is reset, this needs to be reset too (except + * in the case of gds_t_create/gds_t_acquired). + */ + int4 old_mode; /* Saved copy of "cse->mode" before being reset to gds_t_committed. + * Is negated at end of bg_update_phase1 to indicate (to secshr_db_clnup) + * that phase1 is complete. Is negated back to the postive value at end + * of bg_update_phase2. Since this can take on negative values, its type + * is int4 (signed) and not enum gds_t_mode (which is unsigned). + */ + /* The following two fields aid in rolling back the transactions. 'undo_next_off' holds the + * original next_off in the blk buffer that would be if another nested transaction was not + * started. 'undo_offset' holds the offset at which 'undo_next_off' should be applied in case + * of an undo due to trollback. + * A 'kill' might change the next_off field at most in two places in the blk buffer. So, is + * an array of size two. + */ + + block_offset undo_next_off[2]; + block_offset undo_offset[2]; + uint4 blk_checksum; + uint4 was_free; +} cw_set_element; + +#endif diff --git a/sr_port/gdsdbver.h b/sr_port/gdsdbver.h new file mode 100644 index 0000000..44aec93 --- /dev/null +++ b/sr_port/gdsdbver.h @@ -0,0 +1,67 @@ +/**************************************************************** + * * + * Copyright 2005, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GDSDBVER_H_INCLUDED +#define GDSDBVER_H_INCLUDED +/* Database version related definitions */ + +/* Values for bytes 10 and 11 of the GDS Label (first field in the database file header) */ +#ifdef VMS +# define GDS_V20 "02" +# define GDS_X21 "03" +# define GDS_V23 "04" +# define GDS_V24 "05" +# define GDS_V25 "06" +# define GDS_V254 "07" +# define GDS_V255 "08" +# define GDS_V30 "09" +# define GDS_V40 "10" +# define GDS_V50 "11" +#else +# define GDS_V40 "02" +# define GDS_V50 "03" +#endif +#define GDS_CURR GDS_V50 + +/* Database major version as an enum quantity. Used to index the dbversion table in mtables.c */ +enum db_ver +{ + GDSNOVER = -1, + GDSV4 = 0, + GDSV5, + GDSVLAST +}; +#define GDSVCURR ((enum db_ver)(GDSVLAST - 1)) + +/* Database minor version as an enum quantity. This is an ever increasing number that may skip actual + releases as it is only added to when a file-header field is added or changed or if there is a + significant API difference in the version. This number can be incremented only by adding an entry + at the end, just before GDSMVLAST. Note these entries need corresponding updates in + db_auto_upgrade.c. +*/ +enum mdb_ver +{ + GDSMV4, /* Applies to all V4 versions (no minor versions defined) */ + GDSMV50000, + GDSMV51000, /* Multi-site available (for databases created by V51000 - see V51000ALT */ + GDSMV51000ALT, /* Upgrade from a previous version upgraded to this value for V51000 due to bug */ + GDSMV52000, /* Unicode .. no real header changes but db contents could be unusable by previous versions */ + GDSMV53000, /* M-Itanium release. secshr_ops_array and index is been copied from sgmnt_data to node_local. */ + GDSMV53003, /* ZSHOW "G" release: Db Statistics rearranged in file header */ + GDSMV53004, /* New fields(is_encrypted, encryption_hash) for encryption */ + GDSMV54000, /* New fields(db_trigger_cycle) for triggers */ + GDSMV54002, /* New statistical counter field for ZTRIGGER command */ + GDSMV54003, /* New fields(turn_around_point, jnl_eovtn) for backward recovery */ + GDSMVLAST +}; +#define GDSMVCURR ((enum mdb_ver)(GDSMVLAST - 1)) + +#endif diff --git a/sr_port/gdsfhead.h b/sr_port/gdsfhead.h new file mode 100644 index 0000000..5fb467a --- /dev/null +++ b/sr_port/gdsfhead.h @@ -0,0 +1,3198 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GDSFHEAD_H_INCLUDED +#define GDSFHEAD_H_INCLUDED + +/* gdsfhead.h */ +/* this requires gdsroot.h gtm_facility.h fileinfo.h gdsbt.h */ + +#include +#include "gdsdbver.h" +#include "gtm_unistd.h" +#include "gtm_limits.h" +#include "gtm_stdlib.h" +#include "gtm_string.h" +#include "send_msg.h" +#include "iosp.h" +#ifdef VMS +#include "iosb_disk.h" +#endif +#ifdef GTM_CRYPT +#include "gtmcrypt.h" /* for gtmcrypt_key_t */ +#endif + +#define CACHE_STATE_OFF SIZEOF(que_ent) + +error_def(ERR_DBCRERR); +error_def(ERR_DBENDIAN); +error_def(ERR_GVIS); +error_def(ERR_GVSUBOFLOW); +error_def(ERR_REPLINSTMISMTCH); +error_def(ERR_SCNDDBNOUPD); +error_def(ERR_SSATTACHSHM); +error_def(ERR_SSFILOPERR); +error_def(ERR_STACKCRIT); +error_def(ERR_STACKOFLOW); +error_def(ERR_TNTOOLARGE); +error_def(ERR_TNWARN); + \ +/* all this record's fields should exactly be the first members of the cache_rec in the same order */ +typedef struct mmblk_rec_struct +{ + struct + { + sm_off_t fl; + sm_off_t bl; + } + blkque, /* cache records whose block numbers hash to the same location */ + state_que; /* cache records in same state (either wip or active) */ + union + { + short semaphore; + volatile int4 latch; /* int required for atomic swap on Unix */ + /* volatile required as this value is referenced outside of the lock in db_csh_getn() */ + } interlock; + block_id blk; + uint4 refer; + enum db_ver ondsk_blkver; /* Actual block version from block header as it exists on disk + (prior to any dynamic conversion that may have occurred when read in). + */ + trans_num dirty; +} mmblk_rec; + +/* all the fields of this record should exactly be the first members of the cache_state_rec in the same order */ +typedef struct mmblk_state_rec_struct +{ + struct + { + sm_off_t fl; + sm_off_t bl; + } + state_que; /* WARNING -- from this point onwards this should be identical to a mmblk_rec */ + union + { + short semaphore; + volatile int4 latch; /* int required for atomic swap on Unix */ + /* volatile required as this value is referenced outside of the lock in db_csh_getn() */ + } interlock; + block_id blk; + uint4 refer; + enum db_ver ondsk_blkver; /* Actual block version from block header as it exists on disk + (prior to any dynamic conversion that may have occurred when read in). + */ + trans_num dirty; +} mmblk_state_rec; + +typedef struct +{ + mmblk_que_head mmblkq_wip, /* write-in-progress queue -- unused in Unix */ + mmblkq_active; /* active queue */ + mmblk_rec mmblk_array[1]; /* the first mmblk record */ +} mmblk_que_heads; + + + /* need to keep quadword aligned */ + +/* Cache record -- NOTE: the head portion of this should exactly match with mmblk_rec */ +typedef struct cache_rec_struct +{ + struct + { + sm_off_t fl; + sm_off_t bl; + } + blkque, /* cache records whose block numbers hash to the same location */ + state_que; /* cache records in same state (either wip or active) */ + union + { + short semaphore; + volatile int4 latch; /* int required for atomic swap on Unix */ + /* volatile required as this value is referenced outside of the lock in db_csh_getn() */ + } interlock; + block_id blk; + uint4 refer; /* reference bit for the clock algorithm */ + enum db_ver ondsk_blkver; /* Actual block version from block header as it exists on disk + (prior to any dynamic conversion that may have occurred when read in). + */ + /* Keep our 64 bit fields up front */ + /* this point should be quad-word aligned */ + trans_num dirty; /* block has been modified since last written to disk; used by bt_put, db_csh_getn + * mu_rndwn_file wcs_recover, secshr_db_clnup, wr_wrtfin_all and extensively by the ccp */ + trans_num flushed_dirty_tn; /* value of dirty at the time of flushing */ + trans_num tn; + sm_off_t bt_index; /* offset to bt_rec */ + sm_off_t buffaddr; /* offset to buffer holding actual data*/ + sm_off_t twin; /* (VMS) offset to cache_rec of another copy of the same block from bg_update & wcs_wt_all + * (Unix & VMS) offset to cache_rec holding before-image for wcs_recover to backup */ +#ifdef VMS + sm_off_t shmpool_blk_off; /* Offset to shmpool block containing the reformat buffer for this CR */ + int4 backup_cr_off; /* Offset to backup_cr (set/used by bg_update_phase1/2 routines) */ +#endif + off_jnl_t jnl_addr; /* offset from bg_update to prevent wcs_wtstart from writing a block ahead of the journal */ + global_latch_t rip_latch; /* for read_in_progress - note contains extra 16 bytes for HPPA. Usage note: this + latch is used on those platforms where read_in_progress is not directly updated + by atomic routines/instructions. As such there needs be no cache line padding between + this field and read_in_progress. + */ + /* and now the rest */ + int4 image_count; /* maintained with r_epid in vms to ensure that the process has stayed in gt.m */ + int4 epid; /* set by wcs_wtstart to id the write initiator; cleared by wcs_wtfini + * used by t_commit_cleanup, secshr_db_clnup and wcs_recover */ + int4 cycle; /* relative stamp indicates changing versions of the block for concurrency checking */ + int4 r_epid; /* set by db_csh_getn, cleared by t_qread, bg_update, wcs_recover or secshr_db_clnup + * used to check for process leaving without releasing the buffer + * must be word aligned on the VAX */ +#ifdef VMS + io_status_block_disk iosb; /* used on VMS write */ +#endif + CNTR4DCL(read_in_progress, 10); /* -1 for normal and 0 for rip used by t_qread and checked by others */ + uint4 in_tend; /* non-zero pid from bg_update indicates secshr_db_clnup should finish update */ + uint4 in_cw_set; /* non-zero pid from t_end, tp_tend or bg_update protects block from db_csh_getn; + * returned to 0 by t_end, tp_tend or t_commit_cleanup */ + uint4 data_invalid; /* non-zero pid from bg_update indicates t_commit_cleanup/wcs_recover should invalidate */ + boolean_t stopped; /* TRUE indicates to wcs_recover that secshr_db_clnup built the block */ + boolean_t wip_stopped; /* TRUE indicates to wcs_recover, wcs_wtfini, wcs_get_blk and gds_rundown + * that secshr_db_clnup cancelled the qio */ +} cache_rec; + +/* A note about cache line separation of the latches contained in these blocks. Because this block is duplicated + many (ptentially tens+ of) thousands of times in a running system, we have decided against providing cacheline + padding so as to force each cache record into a separate cacheline (due to it containing a latch and/or atomic + counter field) to prevent processes from causing interference with each other. We decided that the probability + of two processes working on adjacent cache records simultaneously was low enough that the interference was + minimal whereas increasing the cache record size to prevent that interference could cause storage problems + on some platforms where processes are already running near the edge. +*/ + +/* cache_state record -- NOTE: the first few fields of this should be identical to that of mmblk_state_rec */ +typedef struct +{ + struct + { + sm_off_t fl; + sm_off_t bl; + } + state_que; /* WARNING from this point, this structure must be identical to a cache_rec */ + union + { + short semaphore; + volatile int4 latch; /* int required for atomic swap on Unix */ + /* volatile required as this value is referenced outside of the lock in db_csh_getn() */ + } interlock; + block_id blk; + uint4 refer; /* reference bit for the LRU algorithm */ + enum db_ver ondsk_blkver; /* Actual block version from block header as it exists on disk + (prior to any dynamic conversion that may have occurred when read in). + */ + /* Keep our 64 bit fields up front */ + /* this point should be quad-word aligned */ + trans_num dirty; /* block has been modified since last written to disk; used by bt_put, db_csh_getn + * mu_rndwn_file wcs_recover, secshr_db_clnup, wr_wrtfin_all and extensively by the ccp */ + trans_num flushed_dirty_tn; /* value of dirty at the time of flushing */ + trans_num tn; + sm_off_t bt_index; /* offset to bt_rec */ + sm_off_t buffaddr; /* offset to buffer holding actual data*/ + sm_off_t twin; /* (VMS) offset to cache_rec of another copy of the same block from bg_update & wcs_wt_all + * (Unix & VMS) offset to cache_rec holding before-image for wcs_recover to backup */ +#ifdef VMS + sm_off_t shmpool_blk_off; /* Offset to shmpool block containing the reformat buffer for this CR */ + int4 backup_cr_off; /* Offset to backup_cr (set/used by bg_update_phase1/2 routines) */ +#endif + off_jnl_t jnl_addr; /* offset from bg_update to prevent wcs_wtstart from writing a block ahead of the journal */ + global_latch_t rip_latch; /* for read_in_progress - note contains extra 16 bytes for HPPA. Usage note: this + latch is used on those platforms where read_in_progress is not directly updated + by atomic routines/instructions. As such there needs be no cache line padding between + this field and read_in_progress. + */ + /* and now the rest */ + int4 image_count; /* maintained with r_epid in vms to ensure that the process has stayed in gt.m */ + int4 epid; /* set by wcs_start to id the write initiator; cleared by wcs_wtfini + * used by t_commit_cleanup, secshr_db_clnup and wcs_recover */ + int4 cycle; /* relative stamp indicates changing versions of the block for concurrency checking */ + int4 r_epid; /* set by db_csh_getn, cleared by t_qread, bg_update, wcs_recover or secshr_db_clnup + * used to check for process leaving without releasing the buffer + * must be word aligned on the VAX */ +#ifdef VMS + io_status_block_disk iosb; /* used on VMS write */ +#endif + CNTR4DCL(read_in_progress, 10); /* -1 for normal and 0 for rip used by t_qread and checked by others */ + uint4 in_tend; /* non-zero pid from bg_update indicates secshr_db_clnup should finish update */ + uint4 in_cw_set; /* non-zero pid from t_end, tp_tend or bg_update protects block from db_csh_getn; + * returned to 0 by t_end, tp_tend or t_commit_cleanup */ + uint4 data_invalid; /* non-zero pid from bg_update indicates t_commit_cleanup/wcs_recover should invalidate */ + boolean_t stopped; /* TRUE indicates to wcs_recover that secshr_db_clnup built the block */ + boolean_t wip_stopped; /* TRUE indicates to wcs_recover, wcs_wtfini, wcs_get_blk and gds_rundown + * that secshr_db_clnup cancelled the qio */ +} cache_state_rec; + +#define CR_BLKEMPTY -1 +#define MBR_BLKEMPTY -1 +#define FROZEN_BY_ROOT (uint4)(0xFFFFFFFF) +#define BACKUP_NOT_IN_PROGRESS 0x7FFFFFFF + +typedef struct +{ + cache_que_head cacheq_wip, /* write-in-progress queue -- unused in Unix */ + cacheq_active; /* active queue */ + cache_rec cache_array[1]; /*the first cache record*/ +} cache_que_heads; + +/* Define pointer types to some previously defined structures */ + +#ifdef DB64 +# ifdef __osf__ +# pragma pointer_size(save) +# pragma pointer_size(long) +# else +# error UNSUPPORTED PLATFORM +# endif +#endif + +typedef cache_que_head *cache_que_head_ptr_t; +typedef cache_rec *cache_rec_ptr_t; +typedef cache_rec **cache_rec_ptr_ptr_t; +typedef cache_state_rec *cache_state_rec_ptr_t; +typedef cache_que_heads *cache_que_heads_ptr_t; + +typedef mmblk_que_head *mmblk_que_head_ptr_t; +typedef mmblk_rec *mmblk_rec_ptr_t; +typedef mmblk_rec **mmblk_rec_ptr_ptr_t; +typedef mmblk_state_rec *mmblk_state_rec_ptr_t; +typedef mmblk_que_heads *mmblk_que_heads_ptr_t; + +void verify_queue_lock(que_head_ptr_t qhdr); +void verify_queue(que_head_ptr_t qhdr); + +#ifdef DB64 +# ifdef __osf__ +# pragma pointer_size(restore) +# endif +#endif + +#ifdef DEBUG_QUEUE +#define VERIFY_QUEUE(base) verify_queue(base) +#define VERIFY_QUEUE_LOCK(base,latch) verify_queue_lock(base,latch) +#else +#define VERIFY_QUEUE(base) +#define VERIFY_QUEUE_LOCK(base,latch) +#endif + +/* The following 3 macros were introduced while solving a problem with $view where a call to $view in */ +/* mumps right after a change to $zgbldir gave the old global directory - not the new one. On VMS it */ +/* caused a core dump. If one were to access a global variable via $data right after the change, however, */ +/* the $view worked correctly. The solution was to make sure the gd_map information matched the current */ +/* gd_header in op_fnview.c. The code used as a template for this change was in gvinit.c. The first */ +/* macro gets the gd_header using an mval. The second macro establishes the gd_map from the gd_header. */ +/* The third macro is an assert (when DEBUG_ONLY is defined) for those cases where the gd_header is already */ +/* set to make sure the mapping is correct. The first 2 macros are executed when the gd_header is null, */ +/* and the 3rd macro is associated with an else clause if it is not. Therefore, they should be maintained */ +/* as a group. */ + +#define SET_GD_HEADER(inmval) \ +{ \ + inmval.mvtype = MV_STR; \ + inmval.str.len = 0; \ + gd_header = zgbldir(&inmval); \ +} + +#define SET_GD_MAP \ +{ \ + GBLREF gd_binding *gd_map, *gd_map_top; \ + \ + gd_map = gd_header->maps; \ + gd_map_top = gd_map + gd_header->n_maps; \ + TREF(gd_targ_addr) = gd_header; \ +} + +#define GD_HEADER_ASSERT \ +{ \ + GBLREF gd_binding *gd_map, *gd_map_top; \ + \ + assert(gd_map == gd_header->maps); \ + assert(gd_map_top == gd_map + gd_header->n_maps); \ + assert(TREF(gd_targ_addr) == gd_header); \ +} + +/* If reallocating gv_currkey/gv_altkey, preserve pre-existing values */ +#define GVKEY_INIT(GVKEY, KEYSIZE) \ +{ \ + gv_key *new_KEY, *old_KEY; \ + int4 keySZ; \ + \ + old_KEY = GVKEY; \ + keySZ = KEYSIZE; \ + /* KEYSIZE should have been the output of a DBKEYSIZE command so \ + * should be a multiple of 4. Assert that. \ + */ \ + assert(ROUND_UP2(keySZ, 4) == keySZ); \ + new_KEY = (gv_key *)malloc(SIZEOF(gv_key) - 1 + keySZ); \ + if (NULL != old_KEY) \ + { \ + assert(KEYSIZE >= old_KEY->top); \ + assert(old_KEY->top > old_KEY->end); \ + memcpy(new_KEY, old_KEY, SIZEOF(gv_key) + old_KEY->end); \ + free(old_KEY); \ + } else \ + { \ + new_KEY->base[0] = '\0'; \ + new_KEY->end = 0; \ + new_KEY->prev = 0; \ + } \ + new_KEY->top = keySZ; \ + GVKEY = new_KEY; \ +} + +#define GVKEY_FREE_IF_NEEDED(GVKEY) \ +{ \ + if (NULL != GVKEY) \ + { \ + free(GVKEY); \ + GVKEY = NULL; \ + } \ +} + +#define GVKEYSIZE_INCREASE_IF_NEEDED(KEYSIZE) \ +{ \ + int keySIZE; \ + \ + GBLREF int4 gv_keysize; \ + GBLREF gv_key *gv_altkey; \ + GBLREF gv_key *gv_currkey; \ + \ + keySIZE = KEYSIZE; \ + assert(keySIZE); \ + if (keySIZE > gv_keysize) \ + { \ + gv_keysize = keySIZE; \ + GVKEY_INIT(gv_currkey, keySIZE); \ + GVKEY_INIT(gv_altkey, keySIZE); \ + } else \ + assert((NULL != gv_currkey) && (NULL != gv_altkey) && gv_keysize \ + && (gv_keysize == gv_currkey->top) && (gv_keysize == gv_altkey->top)); \ +} + +#define SET_CSA_DIR_TREE(csa, keysize, reg) \ +{ \ + if (NULL == csa->dir_tree) \ + { \ + csa->dir_tree = targ_alloc(keysize, NULL, reg); \ + GTMTRIG_ONLY(assert(NULL == csa->hasht_tree)); \ + } else \ + assert((csa->dir_tree->gd_csa == csa) && (DIR_ROOT == csa->dir_tree->root)); \ +} + +#define FREE_CSA_DIR_TREE(csa) \ +{ \ + sgmnt_addrs *lcl_csa; \ + gv_namehead *dir_tree, *hasht_tree; \ + \ + lcl_csa = csa; \ + GTMTRIG_ONLY( \ + hasht_tree = lcl_csa->hasht_tree; \ + if (NULL != hasht_tree) \ + { \ + assert(hasht_tree->gd_csa == csa); \ + hasht_tree->regcnt--; /* targ_free relies on this */ \ + targ_free(hasht_tree); \ + lcl_csa->hasht_tree = NULL; \ + } \ + ) \ + dir_tree = lcl_csa->dir_tree; \ + assert(NULL != dir_tree); \ + dir_tree->regcnt--; /* targ_free relies on this */ \ + targ_free(dir_tree); \ + lcl_csa->dir_tree = NULL; \ +} + +#define PROCESS_GVT_PENDING_LIST(GREG, CSA, GVT_PENDING_LIST) \ +{ \ + if (NULL != GVT_PENDING_LIST) \ + { /* Now that the region has been opened, check if there are any gv_targets that were \ + * allocated for this region BEFORE the open. If so, re-allocate them if necessary. \ + */ \ + process_gvt_pending_list(GREG, CSA); \ + } \ +} + +#define T_COMMIT_CRIT_PHASE1 1 /* csa->t_commit_crit gets set to this in during bg_update_phase1 */ +#define T_COMMIT_CRIT_PHASE2 2 /* csa->t_commit_crit gets set to this in during bg_update_phase2 */ + +/* macro to check if we hold crit or are committing (with or without crit) */ +#define T_IN_CRIT_OR_COMMIT(CSA) ((CSA)->now_crit || (CSA)->t_commit_crit) + +/* Macro to check if we hold crit or are committing (with or without crit) or are in wcs_wtstart for this region. + * This is used in timer handling code to determine if it is ok to interrupt. We do not want to interrupt if holding + * crit or in the midst of commit or in wcs_wtstart (in the last case, we could be causing another process HOLDING CRIT + * on the region to wait in bg_update_phase1 if we hold the write interlock). + */ +#define T_IN_CRIT_OR_COMMIT_OR_WRITE(CSA) (T_IN_CRIT_OR_COMMIT(CSA) || (CSA)->in_wtstart) + +/* macro to check if a database commit is past the point where it can be successfully rolled back */ +#define T_UPDATE_UNDERWAY(CSA) ((CSA)->t_commit_crit) + +/* the file header has relative pointers to its data structures so each process will malloc + * one of these and fill it in with absolute pointers upon file initialization. + */ +#define GDS_REL2ABS(x) (((sm_uc_ptr_t)cs_addrs->lock_addrs[0] + (sm_off_t)(x))) +#define GDS_ABS2REL(x) (sm_off_t)(((sm_uc_ptr_t)(x) - (sm_uc_ptr_t)cs_addrs->lock_addrs[0])) +#define GDS_ANY_REL2ABS(w,x) (((sm_uc_ptr_t)(w->lock_addrs[0]) + (sm_off_t)(x))) +#define GDS_ANY_ABS2REL(w,x) (sm_off_t)(((sm_uc_ptr_t)(x) - (sm_uc_ptr_t)w->lock_addrs[0])) +#ifdef GTM_CRYPT +#define GDS_ANY_ENCRYPTGLOBUF(w,x) ((sm_uc_ptr_t)(w) + (sm_off_t)(x->nl->encrypt_glo_buff_off)) +#endif +#define ASSERT_IS_WITHIN_SHM_BOUNDS(ptr, csa) \ + assert((NULL == (ptr)) || (((ptr) >= csa->db_addrs[0]) && ((0 == csa->db_addrs[1]) || ((ptr) < csa->db_addrs[1])))) + +#ifdef DEBUG +#define DBG_ENSURE_PTR_IS_VALID_GLOBUFF(CSA, CSD, PTR) \ +{ \ + cache_rec_ptr_t cache_start; \ + long bufindx; \ + sm_uc_ptr_t bufstart; \ + \ + cache_start = &(CSA)->acc_meth.bg.cache_state->cache_array[0]; \ + cache_start += CSD->bt_buckets; \ + bufstart = (sm_uc_ptr_t)GDS_ANY_REL2ABS((CSA), cache_start->buffaddr); \ + assert((PTR) >= bufstart); \ + bufindx = (PTR - bufstart) / CSD->blk_size; \ + assert(bufindx < CSD->n_bts); \ + assert((bufstart + (bufindx * CSD->blk_size)) == (PTR)); \ +} +#define DBG_ENSURE_PTR_IS_VALID_ENCTWINGLOBUFF(CSA, CSD, PTR) \ +{ \ + cache_rec_ptr_t cache_start; \ + long bufindx; \ + sm_uc_ptr_t bufstart; \ + \ + cache_start = &(CSA)->acc_meth.bg.cache_state->cache_array[0]; \ + cache_start += CSD->bt_buckets; \ + bufstart = (sm_uc_ptr_t)GDS_ANY_REL2ABS((CSA), cache_start->buffaddr); \ + bufstart += (gtm_uint64_t)CSD->blk_size * CSD->n_bts; \ + assert((PTR) >= bufstart); \ + bufindx = (PTR - bufstart) / CSD->blk_size; \ + assert(bufindx < CSD->n_bts); \ + assert((bufstart + (bufindx * (gtm_uint64_t)CSD->blk_size)) == (PTR)); \ +} + +#define DBG_ENSURE_OLD_BLOCK_IS_VALID(cse, is_mm, csa, csd) \ +{ \ + cache_rec_ptr_t cache_start; \ + long bufindx; \ + sm_uc_ptr_t bufstart; \ + GBLREF boolean_t dse_running, write_after_image; \ + \ + assert((gds_t_write != cse->mode) && (gds_t_write_recycled != cse->mode) && gds_t_writemap != cse->mode \ + || (NULL != cse->old_block)); /* don't miss writing a PBLK */ \ + if (NULL != cse->old_block) \ + { \ + if (!is_mm) \ + { \ + cache_start = &csa->acc_meth.bg.cache_state->cache_array[0]; \ + cache_start += csd->bt_buckets; \ + bufstart = (sm_uc_ptr_t)GDS_ANY_REL2ABS(csa, cache_start->buffaddr); \ + bufindx = (cse->old_block - bufstart) / csd->blk_size; \ + assert(bufindx < csd->n_bts); \ + assert(cse->blk == cache_start[bufindx].blk); \ + assert(dse_running || write_after_image || (process_id == cache_start[bufindx].in_cw_set)); \ + } else \ + { \ + assert(cse->old_block == csa->db_addrs[0] + cse->blk * csd->blk_size \ + + (csd->start_vbn - 1) * DISK_BLOCK_SIZE); \ + } \ + } \ +} + +/* Check if a given address corresponds to a global buffer (BG) in database shared memory AND if + * we are in phase2 of commit. If so check whether the corresponding cache-record is pinned. + * Used by gvcst_blk_build to ensure the update array points to valid contents even though we dont hold crit. + */ +#define DBG_BG_PHASE2_CHECK_CR_IS_PINNED(csa, seg) \ +{ \ + cache_rec_ptr_t cache_start; \ + long bufindx; \ + sm_uc_ptr_t bufstart, bufend, bufaddr; \ + \ + GBLREF uint4 process_id; \ + \ + if ((seg)->len && (T_COMMIT_CRIT_PHASE2 == csa->t_commit_crit) && (dba_bg == csa->hdr->acc_meth)) \ + { \ + cache_start = &csa->acc_meth.bg.cache_state->cache_array[0]; \ + cache_start += csa->hdr->bt_buckets; \ + bufstart = (sm_uc_ptr_t)GDS_ANY_REL2ABS(csa, cache_start->buffaddr); \ + bufend = bufstart + ((gtm_uint64_t)csa->hdr->n_bts * csa->hdr->blk_size); \ + bufaddr = (sm_uc_ptr_t)(seg)->addr; \ + /* Check if given address is within database shared memory range */ \ + if ((bufaddr >= bufstart) && (bufaddr < bufend)) \ + { \ + bufindx = (bufaddr - bufstart) / csa->hdr->blk_size; \ + assert(bufindx < csa->hdr->n_bts); \ + /* Assert that we have the cache-record pinned */ \ + assert(process_id == cache_start[bufindx].in_cw_set); \ + } \ + } \ +} + +/* Macro to check that we have not pinned any more buffers than we are updating. + * This check is done only for BG access method and in dbg mode. + * This is invoked by t_end/tp_tend just before beginning phase2 of commit. + */ +#define DBG_CHECK_PINNED_CR_ARRAY_CONTENTS(is_mm, crarray, crarrayindex, bplmap) \ +{ \ + GBLREF boolean_t write_after_image; \ + \ + if (!is_mm) \ + { \ + int4 crindex; \ + \ + for (crindex = 0; crindex < crarrayindex; crindex++) \ + { \ + if (process_id == crarray[crindex]->in_cw_set) \ + { /* We have pinned that cache-record implies we are planning on updating it \ + * (so should have set in_tend). \ + * \ + * Since bitmap blocks are done with phase2 inside of crit, they should not \ + * show up in the pinned array list at end of phase1 for GT.M. But DSE is an \ + * exception as it could operate on a bitmap block as if it is updating a \ + * non-bitmap block (i.e. without invoking gvcst_map_build). MUPIP JOURNAL \ + * RECOVER also could do the same thing while applying an AIMG record. \ + * \ + * In addition, VMS has an exception in case this is a twinned cache-record. \ + * In that case, for the older twin in_cw_set will be set to non-zero, but \ + * in_tend will be set to FALSE. Since we are outside of crit at this point, \ + * it is possible cr->twin field might be 0 (could have gotten cleared by \ + * wcs_wtfini concurrently) so we cannot assert on the twin field but \ + * cr->bt_index should still be 0 since we have not yet finished the \ + * update on the newer twin so we can check on that. \ + */ \ + assert(crarray[crindex]->in_tend \ + && ((0 != crarray[crindex]->blk % bplmap) || write_after_image) \ + VMS_ONLY(|| !crarray[crindex]->bt_index)); \ + } \ + } \ + } \ +} + +#else +#define DBG_ENSURE_PTR_IS_VALID_GLOBUFF(CSA, CSD, PTR) +#define DBG_ENSURE_PTR_IS_VALID_ENCTWINGLOBUFF(CSA, CSD, PTR) +#define DBG_ENSURE_OLD_BLOCK_IS_VALID(cse, is_mm, csa, csd) +#define DBG_BG_PHASE2_CHECK_CR_IS_PINNED(csa, bufaddr) +#define DBG_CHECK_PINNED_CR_ARRAY_CONTENTS(is_mm, crarray, crarrayindex, bplmap) +#endif + +/* The TP_CHANGE_REG macro is a replica of the tp_change_reg() routine to be used for performance considerations. + * The TP_CHANGE_REG_IF_NEEDED macro tries to optimize on processing if reg is same as gv_cur_region. But it can be + * used only if the region passed is not NULL and if gv_cur_region, cs_addrs and cs_data are known to be in sync. + * Note that timers can interrupt the syncing and hence any routines that are called by timers should be safe + * and use the TP_CHANGE_REG macro only. + */ +#define TP_CHANGE_REG(reg) \ +{ \ + gv_cur_region = reg; \ + if (NULL == gv_cur_region || FALSE == gv_cur_region->open) \ + { \ + cs_addrs = (sgmnt_addrs *)0; \ + cs_data = (sgmnt_data_ptr_t)0; \ + } else \ + { \ + switch (reg->dyn.addr->acc_meth) \ + { \ + case dba_mm: \ + case dba_bg: \ + cs_addrs = &FILE_INFO(gv_cur_region)->s_addrs; \ + cs_data = cs_addrs->hdr; \ + break; \ + case dba_usr: \ + case dba_cm: \ + cs_addrs = (sgmnt_addrs *)0; \ + cs_data = (sgmnt_data_ptr_t)0; \ + break; \ + default: \ + GTMASSERT; \ + break; \ + } \ + } \ +} + +#define TP_CHANGE_REG_IF_NEEDED(reg) \ +{ \ + assert(reg); \ + if (reg != gv_cur_region) \ + { \ + gv_cur_region = reg; \ + switch (reg->dyn.addr->acc_meth) \ + { \ + case dba_mm: \ + case dba_bg: \ + assert(reg->open); \ + cs_addrs = &FILE_INFO(gv_cur_region)->s_addrs; \ + cs_data = cs_addrs->hdr; \ + break; \ + case dba_usr: \ + case dba_cm: \ + cs_addrs = (sgmnt_addrs *)0; \ + cs_data = (sgmnt_data_ptr_t)0; \ + break; \ + default: \ + GTMASSERT; \ + break; \ + } \ + } \ + assert(&FILE_INFO(gv_cur_region)->s_addrs == cs_addrs && cs_addrs->hdr == cs_data); \ +} + +/* The TP_TEND_CHANGE_REG macro is a special macro used in tp_tend.c to optimize out the unnecessary checks in + * the TP_CHANGE_REG_IF_NEEDED macro. Also it sets cs_addrs and cs_data to precomputed values instead of recomputing + * them from the region by dereferencing through a multitude of pointers. It does not check if gv_cur_region is + * different from the input region. It assumes it is different enough % of times that the cost of the if check + * is not worth the additional unconditional sets. + */ +#define TP_TEND_CHANGE_REG(si) \ +{ \ + gv_cur_region = si->gv_cur_region; \ + cs_addrs = si->tp_csa; \ + cs_data = si->tp_csd; \ +} + +#define GTCM_CHANGE_REG(reghead) \ +{ \ + GBLREF cm_region_head *curr_cm_reg_head; \ + GBLREF gd_region *gv_cur_region; \ + GBLREF sgmnt_data *cs_data; \ + GBLREF sgmnt_addrs *cs_addrs; \ + \ + curr_cm_reg_head = (reghead); \ + gv_cur_region = curr_cm_reg_head->reg; \ + if ((dba_bg == gv_cur_region->dyn.addr->acc_meth) || (dba_mm == gv_cur_region->dyn.addr->acc_meth)) \ + { \ + cs_addrs = &FILE_INFO(gv_cur_region)->s_addrs; \ + cs_data = cs_addrs->hdr; \ + } else \ + GTMASSERT; \ +} + +/* Macro to be used whenever cr->data_invalid needs to be set */ +#define SET_DATA_INVALID(cr) \ +{ \ + uint4 in_tend, data_invalid; \ + \ + DEBUG_ONLY(in_tend = cr->in_tend); \ + DEBUG_ONLY(data_invalid = cr->data_invalid); \ + assert((process_id == in_tend) || (0 == in_tend) && (0 == data_invalid)); \ + assert((0 == in_tend) \ + || (process_id == in_tend) && ((0 == data_invalid) || (process_id == data_invalid))); \ + cr->data_invalid = process_id; \ +} + +/* Macro to be used whenever cr->data_invalid needs to be re-set */ +#define RESET_DATA_INVALID(cr) \ +{ \ + uint4 data_invalid; \ + \ + DEBUG_ONLY(data_invalid = cr->data_invalid); \ + assert(process_id == data_invalid); \ + cr->data_invalid = 0; \ +} + +/* Macro to be used whenever cr->in_cw_set needs to be set (PIN) inside a TP transaction */ +#define TP_PIN_CACHE_RECORD(cr, si) \ +{ \ + assert(0 <= si->cr_array_index); \ + assert(si->cr_array_index < si->cr_array_size); \ + PIN_CACHE_RECORD(cr, si->cr_array, si->cr_array_index); \ +} + +GBLREF cache_rec_ptr_t pin_fail_cr; /* Pointer to the cache-record that we failed while pinning */ +GBLREF cache_rec pin_fail_cr_contents; /* Contents of the cache-record that we failed while pinning */ +GBLREF cache_rec_ptr_t pin_fail_twin_cr; /* Pointer to twin of the cache-record that we failed to pin */ +GBLREF cache_rec pin_fail_twin_cr_contents; /* Contents of twin of the cache-record that we failed to pin */ +GBLREF bt_rec_ptr_t pin_fail_bt; /* Pointer to bt of the cache-record that we failed to pin */ +GBLREF bt_rec pin_fail_bt_contents; /* Contents of bt of the cache-record that we failed to pin */ +GBLREF int4 pin_fail_in_crit; /* Holder of crit at the time we failed to pin */ +GBLREF int4 pin_fail_wc_in_free; /* Number of write cache records in free queue when we failed to pin */ +GBLREF int4 pin_fail_wcs_active_lvl; /* Number of entries in active queue when we failed to pin */ +GBLREF int4 pin_fail_ref_cnt; /* Reference count when we failed to pin */ +GBLREF int4 pin_fail_in_wtstart; /* Count of processes in wcs_wtstart when we failed to pin */ +GBLREF int4 pin_fail_phase2_commit_pidcnt; /* Number of processes in phase2 commit when we failed to pin */ +/* Macro to be used whenever cr->in_cw_set needs to be set (PIN) outside of a TP transaction */ +#define PIN_CACHE_RECORD(cr, crarray, crarrayindex) \ +{ \ + uint4 in_tend, data_invalid, in_cw_set; \ + \ + DEBUG_ONLY(in_tend = cr->in_tend); \ + DEBUG_ONLY(data_invalid = cr->data_invalid); \ + assert((process_id == in_tend) || (0 == in_tend)); \ + assert((process_id == data_invalid) || (0 == data_invalid)); \ + in_cw_set = cr->in_cw_set; \ + if (0 != in_cw_set) \ + { \ + pin_fail_cr = cr; \ + pin_fail_cr_contents = *cr; \ + if (cr->bt_index) \ + { \ + pin_fail_bt = (bt_rec_ptr_t)GDS_ANY_REL2ABS(cs_addrs, cr->bt_index); \ + pin_fail_bt_contents = *pin_fail_bt; \ + } \ + if (cr->twin) \ + { \ + pin_fail_twin_cr = (cache_rec_ptr_t)GDS_ANY_REL2ABS(cs_addrs, cr->twin); \ + pin_fail_twin_cr_contents = *pin_fail_twin_cr; \ + } \ + pin_fail_in_crit = cs_addrs->nl->in_crit; \ + pin_fail_wc_in_free = cs_addrs->nl->wc_in_free; \ + pin_fail_wcs_active_lvl = cs_addrs->nl->wcs_active_lvl; \ + pin_fail_ref_cnt = cs_addrs->nl->ref_cnt; \ + pin_fail_in_wtstart = cs_addrs->nl->in_wtstart; \ + pin_fail_phase2_commit_pidcnt = cs_addrs->nl->wcs_phase2_commit_pidcnt; \ + GTMASSERT; \ + } \ + /* In VMS we should never set in_cw_set on an OLDER twin. */ \ + VMS_ONLY(assert(!cr->twin || cr->bt_index)); \ + /* stuff it in the array before setting in_cw_set */ \ + crarray[crarrayindex] = cr; \ + crarrayindex++; \ + cr->in_cw_set = process_id; \ +} + +/* Macro to be used whenever cr->in_cw_set needs to be re-set (UNPIN) in TP or non-TP) */ +#define UNPIN_CACHE_RECORD(cr) \ +{ \ + uint4 in_tend, data_invalid, in_cw_set; \ + \ + in_cw_set = cr->in_cw_set; \ + if (process_id == cr->in_cw_set) /* reset in_cw_set only if we hold it */ \ + { \ + DEBUG_ONLY(in_tend = cr->in_tend); \ + DEBUG_ONLY(data_invalid = cr->data_invalid); \ + assert((process_id == in_tend) || (0 == in_tend)); \ + assert((process_id == data_invalid) || (0 == data_invalid)); \ + cr->in_cw_set = 0; \ + } \ +} + +/* Macro to reset cr->in_cw_set for the entire cr_array in case of a retry (TP or non-TP) */ +#define UNPIN_CR_ARRAY_ON_RETRY(crarray, crarrayindex) \ +{ \ + int4 lcl_crarrayindex; \ + cache_rec_ptr_ptr_t cr_ptr; \ + cache_rec_ptr_t cr; \ + uint4 in_tend, data_invalid, in_cw_set; \ + \ + lcl_crarrayindex = crarrayindex; \ + if (lcl_crarrayindex) \ + { \ + cr_ptr = (cache_rec_ptr_ptr_t)&crarray[lcl_crarrayindex-1]; \ + while (lcl_crarrayindex--) \ + { \ + cr = *cr_ptr; \ + DEBUG_ONLY(in_tend = cr->in_tend); \ + DEBUG_ONLY(data_invalid = cr->data_invalid); \ + DEBUG_ONLY(in_cw_set = cr->in_cw_set); \ + assert(!data_invalid); \ + assert(!in_tend); \ + assert(process_id == in_cw_set); \ + UNPIN_CACHE_RECORD(cr); \ + cr_ptr--; \ + } \ + crarrayindex = 0; \ + } \ +} + +/* Macro to reset cr->in_cw_set (UNPIN) for the entire cr_array in case of a commit (TP or non-TP). + * Usually in_cw_set is set for all cache-records that we are planning on updating before we start phase1. + * After updating each cse in phase2, we reset the corresponding cse->cr->in_cw_set. + * Therefore on a successful commit, after completing all cses in phase2, we dont expect any pinned cr->in_cw_set at all. + * This is true for Unix but in VMS where we could have twins, both the older and newer twins have the in_cw_set set in + * phase1 while only the newer twin's in_cw_set gets reset in phase2 (since only this cr will be stored in cse->cr). + * Therefore there could be a few cache-records which need to be unpinned even after all cses are done in phase2. + * The following macro unpins those. It is structured such a way that in Unix, it only checks that all have been reset + * while it actually does the reset only in VMS. + */ +#if defined(VMS) +#define UNPIN_CR_ARRAY_ON_COMMIT(crarray, crarrayindex) \ +{ \ + int4 lcl_crarrayindex; \ + cache_rec_ptr_ptr_t cr_ptr; \ + cache_rec_ptr_t cr; \ + \ + lcl_crarrayindex = crarrayindex; \ + if (lcl_crarrayindex) \ + { \ + cr_ptr = (cache_rec_ptr_ptr_t)&crarray[lcl_crarrayindex-1]; \ + while (lcl_crarrayindex--) \ + { \ + cr = *cr_ptr; \ + UNPIN_CACHE_RECORD(cr); \ + cr_ptr--; \ + } \ + crarrayindex = 0; \ + } \ +} +#elif defined(UNIX) +# ifdef DEBUG +# define UNPIN_CR_ARRAY_ON_COMMIT(crarray, crarrayindex) \ + { \ + int4 lcl_crarrayindex; \ + cache_rec_ptr_ptr_t cr_ptr; \ + cache_rec_ptr_t cr; \ + \ + lcl_crarrayindex = crarrayindex; \ + if (lcl_crarrayindex) \ + { \ + cr_ptr = (cache_rec_ptr_ptr_t)&crarray[lcl_crarrayindex-1]; \ + while (lcl_crarrayindex--) \ + { \ + cr = *cr_ptr; \ + assert(process_id != cr->in_cw_set); \ + cr_ptr--; \ + } \ + crarrayindex = 0; \ + } \ + } +# else +# define UNPIN_CR_ARRAY_ON_COMMIT(crarray, crarrayindex) \ + crarrayindex = 0; +# endif +#endif + +#define JNLPOOL_INIT_IF_NEEDED(CSA, CSD, CNL) \ +{ \ + unsigned char instfilename_copy[MAX_FN_LEN + 1]; \ + sm_uc_ptr_t jnlpool_instfilename; \ + \ + GBLREF jnlpool_ctl_ptr_t jnlpool_ctl; \ + GBLREF boolean_t is_replicator; \ + GBLREF boolean_t pool_init; \ + GBLREF boolean_t is_updproc; \ + \ + if (REPL_ALLOWED(CSD) && is_replicator) \ + { \ + if (FALSE == pool_init) \ + jnlpool_init((jnlpool_user)GTMPROC, (boolean_t)FALSE, (boolean_t *)NULL); \ + assert(pool_init); \ + if (!CSA->replinst_matches_db) \ + { \ + if (jnlpool_ctl->upd_disabled && !is_updproc) \ + { /* Updates are disabled in this journal pool. Detach from journal pool and issue error. */ \ + assert(NULL != jnlpool.jnlpool_ctl); \ + jnlpool_detach(); \ + assert(NULL == jnlpool.jnlpool_ctl); \ + assert(FALSE == pool_init); \ + rts_error(VARLSTCNT(1) ERR_SCNDDBNOUPD); \ + } \ + UNIX_ONLY(jnlpool_instfilename = (sm_uc_ptr_t)jnlpool_ctl->jnlpool_id.instfilename;) \ + VMS_ONLY(jnlpool_instfilename = (sm_uc_ptr_t)jnlpool_ctl->jnlpool_id.gtmgbldir;) \ + if (STRCMP(CNL->replinstfilename, jnlpool_instfilename)) \ + { /* Replication instance file mismatch. Issue error. But before that detach from journal pool. \ + * Copy replication instance file name in journal pool to temporary memory before detaching. \ + */ \ + UNIX_ONLY(assert(SIZEOF(instfilename_copy) == SIZEOF(jnlpool_ctl->jnlpool_id.instfilename))); \ + VMS_ONLY(assert(SIZEOF(instfilename_copy) == SIZEOF(jnlpool_ctl->jnlpool_id.gtmgbldir))); \ + memcpy(&instfilename_copy[0], jnlpool_instfilename, SIZEOF(instfilename_copy)); \ + assert(NULL != jnlpool.jnlpool_ctl); \ + jnlpool_detach(); \ + assert(NULL == jnlpool.jnlpool_ctl); \ + assert(FALSE == pool_init); \ + rts_error(VARLSTCNT(8) ERR_REPLINSTMISMTCH, 6, LEN_AND_STR(instfilename_copy), \ + DB_LEN_STR(gv_cur_region), LEN_AND_STR(CNL->replinstfilename)); \ + } \ + CSA->replinst_matches_db = TRUE; \ + } \ + } \ +} + +/* Explanation for why we need the following macro. + * + * Normally a cdb_sc_blkmod check is done using the "bt". This is done in t_end and tp_tend. + * But that is possible only if we hold crit. There are a few routines (TP only) that need + * to do this check outside of crit (e.g. tp_hist, gvcst_search). For those, the following macro + * is defined. This macro compares transaction numbers directly from the buffer instead of + * going through the bt or blk queues. This is done to speed up processing. One consequence + * is that we might encounter a situation where the buffer's contents hasn't been modified, + * but the block might actually have been changed i.e. in VMS a twin buffer might have been + * created or the "blk" field in the cache-record corresponding to this buffer might have + * been made CR_BLKEMPTY etc. In these cases, we rely on the fact that the cycle for the + * buffer would have been incremented thereby saving us in the cdb_sc_lostcr check which will + * always FOLLOW (never PRECEDE) this check. + * + * Note that in case of BG, it is possible that the buffer could be in the process of being updated + * (phase2 outside of crit). In this case we have to restart as otherwise we could incorrectly + * validate an inconsistent state of the database as okay. For example, say our search path + * contains a level-1 root-block and a level-0 data block. If both of these blocks were + * concurrently being updated in phase2 (outside of crit) by another process, it is possible + * (because of the order in which blocks are committed) that the data block contents get + * modified first but the index block is still unchanged. If we traversed down the tree at + * this instant, we are looking at a search path that contains a mix of pre-update and post-update + * blocks and should never validate this traversal as okay. In this case, the cache record + * corresponding to the index block would have its "in_tend" flag non-zero indicating update is pending. + * + * The order of the check should be cr->in_tend BEFORE the buffaddr->tn check. Doing it otherwise + * would mean it is posible for the buffaddr->tn check to succeed and before the cr->in_tend + * check is done the buffer gets rebuilt (from start to finish in phase2). This would result + * in us falsely validating this transaction as okay when in fact we should have restarted. + * + * Because we rely on the fact that cr->in_tend is reset to 0 AFTER t1->buffaddr->tn is updated, and + * since these could be updated concurrently, and since this macro is used outside of crit, we need to + * ensure a read memory barrier is done. Currently, the only two places which use this macro are tp_hist.c + * and gvcst_search.c. Out of this, the latter uses this only as a performance measure and not for correctness. + * But the former uses this for correctness. In fact tp_tend.c relies on tp_hist.c doing a proper validation. + * Therefore the read memory barrier is essential in tp_hist.c and not needed in gvcst_search.c. See tp_hist.c + * for use of the read memory barrier and a comment describing why it is ok to do it only once per function + * invocation (instead of using it once per block that gets validated). + * + * There are two variants of this macro. + * TP_IS_CDB_SC_BLKMOD : That calculates the blktn by doing t1->buffaddr->tn explicitly. + * TP_IS_CDB_SC_BLKMOD3 : This is provided the blktn as input so can avoid the explicit calculation. + */ +#define TP_IS_CDB_SC_BLKMOD(cr, t1) (((NULL != (cr)) && (cr)->in_tend) || ((t1)->tn <= ((blk_hdr_ptr_t)(t1)->buffaddr)->tn)) +#define TP_IS_CDB_SC_BLKMOD3(cr, t1, blktn) (((NULL != (cr)) && (cr)->in_tend) || ((t1)->tn <= blktn)) + +#define MM_ADDR(SGD) ((sm_uc_ptr_t)(((sgmnt_data_ptr_t)SGD) + 1)) +#ifdef VMS +#define MASTER_MAP_BLOCKS_DFLT 64 /* 64 gives 128M possible blocks */ +#else +#define MASTER_MAP_BLOCKS_DFLT 112 /* 112 gives 224M possible blocks */ +#endif +#define MASTER_MAP_BLOCKS_V4 32 /* 32 gives 64M possible blocks */ +#define MASTER_MAP_BLOCKS_MAX 112 /* 112 gives 224M possible blocks */ +#define MASTER_MAP_BLOCKS_V5_OLD 64 /* V5 database previous master map block size */ +#define MASTER_MAP_SIZE_V5_OLD (MASTER_MAP_BLOCKS_V5_OLD * DISK_BLOCK_SIZE) +#define MASTER_MAP_SIZE_V4 (MASTER_MAP_BLOCKS_V4 * DISK_BLOCK_SIZE) /* MUST be a multiple of DISK_BLOCK_SIZE */ +#define MASTER_MAP_SIZE_MAX (MASTER_MAP_BLOCKS_MAX * DISK_BLOCK_SIZE) /* MUST be a multiple of DISK_BLOCK_SIZE */ +#define MASTER_MAP_SIZE_DFLT (MASTER_MAP_BLOCKS_DFLT * DISK_BLOCK_SIZE) /* MUST be a multiple of DISK_BLOCK_SIZE */ +#define MASTER_MAP_SIZE(SGD) (((sgmnt_data_ptr_t)SGD)->master_map_len) +#define SGMNT_HDR_LEN SIZEOF(sgmnt_data) +#define SIZEOF_FILE_HDR(SGD) (SGMNT_HDR_LEN + MASTER_MAP_SIZE(SGD)) +#define SIZEOF_FILE_HDR_DFLT (SGMNT_HDR_LEN + MASTER_MAP_SIZE_DFLT) +#define SIZEOF_FILE_HDR_MIN (SGMNT_HDR_LEN + MASTER_MAP_SIZE_V4) +#define SIZEOF_FILE_HDR_MAX (SGMNT_HDR_LEN + MASTER_MAP_SIZE_MAX) +#define MM_BLOCK (SGMNT_HDR_LEN / DISK_BLOCK_SIZE + 1) /* gt.m numbers blocks from 1 */ +#define TH_BLOCK 1 + +#define JNL_NAME_SIZE 256 /* possibly expanded when opened */ +#define JNL_NAME_EXP_SIZE 1024 /* MAXPATHLEN, before jnl_buffer in shared memory */ + +#define BLKS_PER_LMAP 512 +#define MAXTOTALBLKS_V4 (MASTER_MAP_SIZE_V4 * 8 * BLKS_PER_LMAP) +#define MAXTOTALBLKS_V5 (MASTER_MAP_SIZE_MAX * 8 * BLKS_PER_LMAP) +#define MAXTOTALBLKS_MAX (MASTER_MAP_SIZE_MAX * 8 * BLKS_PER_LMAP) +#define MAXTOTALBLKS(SGD) (MASTER_MAP_SIZE(SGD) * 8 * BLKS_PER_LMAP) +#define IS_BITMAP_BLK(blk) (ROUND_DOWN2(blk, BLKS_PER_LMAP) == blk) /* TRUE if blk is a bitmap */ +/* + * UNIX - 8K fileheader (= 16 blocks) + 56K mastermap (= 112 blocks) + 1 + * VMS - 8K fileheader (= 16 blocks) + 32K mastermap (= 64 blocks) + 24K padding (= 48 blocks) + 1 */ +#define START_VBN_V5 129 +#define START_VBN_V4 49 /* 8K fileheader (= 16 blocks) + 16K mastermap (= 32 blocks) + 1 */ +#define START_VBN_CURRENT START_VBN_V5 + +#define STEP_FACTOR 64 /* the factor by which flush_trigger is incremented/decremented */ +#define MIN_FLUSH_TRIGGER(n_bts) ((n_bts)/4) /* the minimum flush_trigger as a function of n_bts */ +#define MAX_FLUSH_TRIGGER(n_bts) ((n_bts)*15/16) /* the maximum flush_trigger as a function of n_bts */ + +#define MIN_FILLFACTOR 30 +#define MAX_FILLFACTOR 100 + +#ifdef DEBUG_DYNGRD +# define DEBUG_DYNGRD_ONLY(X) X +#else +# define DEBUG_DYNGRD_ONLY(X) +#endif + +#ifdef VMS +/* RET is a dummy that is not really used on VMS */ +#define DCLAST_WCS_WTSTART(reg, num_bufs, RET) \ +{ \ + unsigned int status; \ + \ + if (SS$_NORMAL != (status = sys$dclast(wcs_wtstart, reg, 0))) \ + { \ + assert(FALSE); \ + status = DISABLE_AST; \ + wcs_wtstart(reg); \ + if (SS$_WASSET == status) \ + ENABLE_AST; \ + } \ +} +#elif defined(UNIX) +#define DCLAST_WCS_WTSTART(reg, num_bufs, RET) RET = wcs_wtstart(reg, num_bufs); +#else +#error UNSUPPORTED PLATFORM +#endif + +#define SAVE_WTSTART_PID(cnl, pid, index) \ +{ \ + for (index = 0; index < MAX_WTSTART_PID_SLOTS; index++) \ + if (0 == cnl->wtstart_pid[index]) \ + break; \ + if (MAX_WTSTART_PID_SLOTS > index) \ + cnl->wtstart_pid[index] = pid; \ +} + +#define CLEAR_WTSTART_PID(cnl, index) \ +{ \ + if (MAX_WTSTART_PID_SLOTS > index) \ + cnl->wtstart_pid[index] = 0; \ +} + +#define WRITERS_ACTIVE(cnl) ((0 < cnl->intent_wtstart) || (0 < cnl->in_wtstart)) + +#define SIGNAL_WRITERS_TO_STOP(csd) \ +{ \ + SET_TRACEABLE_VAR((csd)->wc_blocked, TRUE); /* to stop all active writers */ \ + /* memory barrier needed to broadcast this information to other processors */ \ + SHM_WRITE_MEMORY_BARRIER; \ +} + +#define WAIT_FOR_WRITERS_TO_STOP(cnl, lcnt, maxiters) \ +{ /* We need to ensure that an uptodate value of cnl->intent_wtstart is read in the \ + * WRITERS_ACTIVE macro every iteration of the loop hence the read memory barrier. \ + */ \ + SHM_READ_MEMORY_BARRIER; \ + for (lcnt=1; WRITERS_ACTIVE(cnl) && (lcnt <= maxiters); lcnt++) \ + { /* wait for any processes INSIDE or at ENTRY of wcs_wtstart to finish */ \ + wcs_sleep(lcnt); \ + SHM_READ_MEMORY_BARRIER; \ + } \ +} + +#define SIGNAL_WRITERS_TO_RESUME(csd) \ +{ \ + SET_TRACEABLE_VAR((csd)->wc_blocked, FALSE); /* to let active writers resume */ \ + /* memory barrier needed to broadcast this information to other processors */ \ + SHM_WRITE_MEMORY_BARRIER; \ +} + +#define INCR_INTENT_WTSTART(cnl) \ +{ \ + INCR_CNT(&cnl->intent_wtstart, &cnl->wc_var_lock); /* signal intent to enter wcs_wtstart */ \ + if (0 >= cnl->intent_wtstart) \ + { /* possible if wcs_verify had reset this flag */ \ + INCR_CNT(&cnl->intent_wtstart, &cnl->wc_var_lock); \ + /* wcs_verify cannot possibly have reset this flag again because it does this only \ + * after wcs_recover waits for a maximum of 1 minute (for this flag to become zero) \ + * before giving up. Therefore for that to happen, we should have been context \ + * switched out for 1 minute after the second INCR_CNT but before the below assert) \ + * We believe that is an extremely unlikely condition so dont do anything about it. \ + * In the worst case this will get reset to 0 by the next wcs_verify or INCR_CNT \ + * (may need multiple INCR_CNTs depending on how negative a value this is) whichever \ + * happens sooner. \ + */ \ + assert(0 < cnl->intent_wtstart); \ + } \ +} + +#define DECR_INTENT_WTSTART(cnl) \ +{ \ + if (0 < cnl->intent_wtstart) \ + DECR_CNT(&cnl->intent_wtstart, &cnl->wc_var_lock); \ + /* else possible if wcs_verify had reset this flag */ \ +} + +#define ENSURE_JNL_OPEN(csa, reg) \ +{ \ + boolean_t was_crit; \ + jnl_private_control *jpc; \ + sgmnt_data_ptr_t csd; \ + uint4 jnl_status; \ + \ + assert(cs_addrs == csa); \ + assert(gv_cur_region == reg); \ + assert(FALSE == reg->read_only); \ + csd = csa->hdr; \ + if (JNL_ENABLED(csd)) \ + { \ + was_crit = csa->now_crit; \ + if (!was_crit) \ + grab_crit(reg); \ + jnl_status = JNL_ENABLED(csd) ? jnl_ensure_open() : 0; \ + if (!was_crit) \ + rel_crit(reg); \ + if (0 != jnl_status) \ + { \ + jpc = csa->jnl; \ + assert(NULL != jpc); \ + if (SS_NORMAL != jpc->status) \ + rts_error(VARLSTCNT(7) jnl_status, 4, JNL_LEN_STR(csd), DB_LEN_STR(gv_cur_region), \ + jpc->status); \ + else \ + rts_error(VARLSTCNT(6) jnl_status, 4, JNL_LEN_STR(csd), DB_LEN_STR(gv_cur_region)); \ + } \ + } \ +} + +/* the RET is meaningful only on UNIX */ +#define JNL_ENSURE_OPEN_WCS_WTSTART(csa, reg, num_bufs, RET) \ +{ \ + ENSURE_JNL_OPEN(csa, reg); \ + DCLAST_WCS_WTSTART(reg, num_bufs, RET); \ +} + +/* Macros to effect changes in the blks_to_upgrd field of the file-header. + * We should hold crit on the region in all cases except for one when we are in MUPIP CREATE (but we are still standalone here). + * Therefore we need not use any interlocks to update this field. This is asserted below. + * Although we can derive "csd" from "csa", we pass them as two separate arguments for performance reasons. + * Use local variables to record shared memory information doe debugging purposes in case of an assert failure. + */ +#define INCR_BLKS_TO_UPGRD(csa, csd, delta) \ +{ \ + int4 new_blks_to_upgrd; \ + int4 cur_blks_to_upgrd; \ + int4 cur_delta; \ + \ + assert((csd)->createinprogress || (csa)->now_crit); \ + cur_delta = (delta); \ + assert((csa)->hdr == (csd)); \ + assert(0 != cur_delta); \ + cur_blks_to_upgrd = (csd)->blks_to_upgrd; \ + assert(0 <= (csd)->blks_to_upgrd); \ + new_blks_to_upgrd = cur_delta + cur_blks_to_upgrd; \ + assert(0 <= new_blks_to_upgrd); \ + (csd)->blks_to_upgrd = new_blks_to_upgrd; \ + if (0 >= new_blks_to_upgrd) \ + { \ + if (0 == new_blks_to_upgrd) \ + (csd)->tn_upgrd_blks_0 = (csd)->trans_hist.curr_tn; \ + else \ + { /* blks_to_upgrd counter in the fileheader should never hold a \ + * negative value. Note down the negative value in a separate \ + * field for debugging and set the counter to 0. \ + */ \ + (csd)->blks_to_upgrd = 0; \ + (csd)->blks_to_upgrd_subzero_error -= (new_blks_to_upgrd); \ + } \ + } else \ + (csd)->fully_upgraded = FALSE; \ +} +#define DECR_BLKS_TO_UPGRD(csa, csd, delta) INCR_BLKS_TO_UPGRD((csa), (csd), -(delta)) + +/* Interlocked queue instruction constants ... */ + +#define QI_STARVATION 3 +#define EMPTY_QUEUE 0L +#define QUEUE_WAS_EMPTY 1 +#define INTERLOCK_FAIL -1L +#define QUEUE_INSERT_SUCCESS 1 + +typedef trans_num bg_trc_rec_tn; +typedef int4 bg_trc_rec_cntr; + +typedef struct +{ + int4 curr_count; /* count for this invocation of shared memory */ + int4 cumul_count; /* count from the creation of database (not including this invocation) */ +} db_csh_acct_rec; + +#define TAB_DB_CSH_ACCT_REC(A,B,C) A, +enum db_csh_acct_rec_type +{ +#include "tab_db_csh_acct_rec.h" +n_db_csh_acct_rec_types +}; +#undef TAB_DB_CSH_ACCT_REC + +#include "gvstats_rec.h" + +#define GVSTATS_SET_CSA_STATISTIC(csa, counter, value) \ +{ \ + csa->gvstats_rec.counter = value; \ +} + +#define INCR_GVSTATS_COUNTER(csa, cnl, counter, increment) \ +{ \ + csa->gvstats_rec.counter += increment; \ + cnl->gvstats_rec.counter += increment; \ +} + +#if defined(DEBUG) || defined(DEBUG_DB_CSH_COUNTER) +# define INCR_DB_CSH_COUNTER(csa, counter, increment) \ + if (csa->read_write || dba_bg == csa->hdr->acc_meth) \ + csa->hdr->counter.curr_count += increment; +#else +# define INCR_DB_CSH_COUNTER(csa, counter, increment) +#endif + +enum tp_blkmod_type /* used for accounting in cs_data->tp_cdb_sc_blkmod[] */ +{ + tp_blkmod_nomod = 0, + tp_blkmod_gvcst_srch, + tp_blkmod_t_qread, + tp_blkmod_tp_tend, + tp_blkmod_tp_hist, + n_tp_blkmod_types +}; + +/* Below is a list of macro bitmasks used to set the global variable "donot_commit". This variable should normally be 0. + * But in rare cases, we could end up in situations where we know it is a restartable situation but decide not to + * restart right away (because of interface issues that the function where this is detected cannot signal a restart + * or because we dont want to take a performance hit to check this restartable situation in highly frequented code if + * the restart will anyway be detected before commit. In this cases, this variable will take on non-zero values. + * The commit logic will assert that this variable is indeed zero after validation but before proceeding with commit. + */ +#define DONOTCOMMIT_TPHIST_BLKTARGET_MISMATCH (1 << 0) /* Restartable situation encountered in tp_hist */ +#define DONOTCOMMIT_GVCST_DELETE_BLK_CSE_TLEVEL (1 << 1) /* Restartable situation encountered in gvcst_delete_blk */ +#define DONOTCOMMIT_JNLGETCHECKSUM_NULL_CR (1 << 2) /* Restartable situation encountered in jnl_get_checksum.h */ +#define DONOTCOMMIT_GVCST_KILL_ZERO_TRIGGERS (1 << 3) /* Restartable situation encountered in gvcst_kill */ +#define DONOTCOMMIT_GVCST_BLK_BUILD_TPCHAIN (1 << 4) /* Restartable situation encountered in gvcst_blk_build */ + +#define TAB_BG_TRC_REC(A,B) B, +enum bg_trc_rec_type +{ +#include "tab_bg_trc_rec.h" +n_bg_trc_rec_types +}; +#undef TAB_BG_TRC_REC + +#define UPGRD_WARN_INTERVAL (60 * 60 * 24) /* Once every 24 hrs */ +typedef struct compswap_time_field_struct +{ /* This structure is used where we want to do a compare-n-swap (CAS) on a time value. The CAS interfaces + need an instance of global_latch_t to operate on. We will utilize the "latch_pid" field to hold the + time and the latch_word is unused except on VMS where it will hold 0. Since this structure must be of + a constant size (size of global_latch_t varies), pad the latch with sufficient space to match the + size of global_latch_t's largest size (on HPUX). + */ + global_latch_t time_latch; +#ifndef __hppa + int4 hp_latch_space[4]; /* padding only on non-hpux systems */ +#endif +} compswap_time_field; +/* takes value of time() but needs to be 4 byte so can use compswap on it. Not + using time_t as that is an indeterminate size on various platforms. + Value is time (in seconds) in a compare/swap updated field so only one + process performs a given task in a given interval +*/ +#define cas_time time_latch.u.parts.latch_pid + +/* The following structure is used to determine + the endianess of a database header. +*/ + +typedef union +{ + struct { + unsigned short little_endian; + unsigned short big_endian; + } shorts; + uint4 word32; +} endian32_struct; + +#ifdef BIGENDIAN +# define ENDIANCHECKTHIS big_endian +#else +# define ENDIANCHECKTHIS little_endian +#endif + +#define CHECK_DB_ENDIAN(CSD,FNLEN,FNNAME) \ +{ \ + endian32_struct check_endian; \ + check_endian.word32 = (CSD)->minor_dbver; \ + if (!check_endian.shorts.ENDIANCHECKTHIS) \ + rts_error(VARLSTCNT(6) ERR_DBENDIAN, 4, FNLEN, FNNAME, ENDIANOTHER, ENDIANTHIS); \ +} + +/* This is the structure describing a segment. It is used as a database file header (for MM or BG access methods). + * The overloaded fields for MM and BG are n_bts, bt_buckets. */ + +/* ***NOTE*** If the field minor_dbver is updated, please also update gdsdbver.h and db_auto_upgrade.c appropriately + (see db_auto_upgrade for reasons and description). SE 5/2006 +*/ +typedef struct sgmnt_data_struct +{ + /************* MOSTLY STATIC DATABASE STATE FIELDS **************************/ + unsigned char label[GDS_LABEL_SZ]; + int4 blk_size; /* Block size for the file. Static data defined at db creation time */ + int4 master_map_len; /* Length of master map */ + int4 bplmap; /* Blocks per local map (bitmap). static data defined at db creation time */ + int4 start_vbn; /* starting virtual block number. */ + enum db_acc_method acc_meth; /* Access method (BG or MM) */ + uint4 max_bts; /* Maximum number of bt records allowed in file */ + int4 n_bts; /* number of cache record/blocks */ + int4 bt_buckets; /* Number of buckets in bt table */ + int4 reserved_bytes; /* Database blocks will always leave this many bytes unused */ + int4 max_rec_size; /* maximum record size allowed for this file */ + int4 max_key_size; /* maximum key size allowed for this file */ + uint4 lock_space_size; /* Number of bytes to be used for locks (in database for bg) */ + uint4 extension_size; /* Number of gds data blocks to extend by */ + uint4 def_coll; /* Default collation type for new globals */ + uint4 def_coll_ver; /* Default collation type version */ + boolean_t std_null_coll; /* 0 -> GT.M null collation,i,e, null subs collate between numeric and string + * 1-> standard null collation i.e. null subs collate before numeric and string */ + boolean_t null_subs; + uint4 free_space; /* Space in file header not being used */ + mutex_spin_parms_struct mutex_spin_parms; + int4 max_update_array_size; /* maximum size of update array needed for one non-TP set/kill */ + int4 max_non_bm_update_array_size;/* maximum size of update array excepting bitmaps */ + boolean_t file_corrupt; /* If set, it shuts the file down. No process (except DSE) can + * successfully map this section after the flag is set to TRUE. Processes + * that already have it mapped should produce an error the next time that + * they use the file. The flag can only be reset by the DSE utility. + */ + enum mdb_ver minor_dbver; /* Minor DB version field that is incremented when minor changes to this + * file-header or API changes occur. See note at top of sgmnt_data. + */ + uint4 jnl_checksum; + uint4 wcs_phase2_commit_wait_spincnt; /* # of spin iterations before sleeping while waiting for phase2 commits */ + enum mdb_ver last_mdb_ver; /* Minor DB version of the GT.M version that last accessed this database. + * Maintained only by GT.M versions V5.3-003 and greater. + */ + /* The structure is 128-bytes in size at this point */ + /************* FIELDS SET AT CREATION TIME ********************************/ + char filler_created[52]; /* Now unused .. was "file_info created" */ + boolean_t createinprogress; /* TRUE only if MUPIP CREATE is in progress. FALSE otherwise */ + int4 creation_time4; /* Lower order 4-bytes of time when the database file was created */ + int4 creation_filler_8byte; + + /************* FIELDS USED BY TN WARN PROCESSING *************************/ + trans_num max_tn; /* Hardstop TN for this database */ + trans_num max_tn_warn; /* TN for next TN_RESET warning for this database */ + + /************* FIELDS SET BY MUPIP BACKUP/REORG *************************/ + trans_num last_inc_backup; + trans_num last_com_backup; + trans_num last_rec_backup; + block_id last_inc_bkup_last_blk; /* Last block in the database at time of last incremental backup */ + block_id last_com_bkup_last_blk; /* Last block in the database at time of last comprehensive backup */ + block_id last_rec_bkup_last_blk; /* Last block in the database at time of last record-ed backup */ + block_id reorg_restart_block; + char filler_256[8]; + /************* FIELDS SET WHEN DB IS OPEN ********************************/ + char now_running[MAX_REL_NAME];/* for active version stamp */ + uint4 owner_node; /* Node on cluster that "owns" the file */ + uint4 image_count; /* for db freezing. Set to "process_id" on Unix and "image_count" on VMS */ + uint4 freeze; /* for db freezing. Set to "getuid" on Unix and "process_id" on VMS */ + int4 kill_in_prog; /* counter for multi-crit kills that are not done yet */ + int4 abandoned_kills; + char filler_320[8]; + /************* FIELDS USED IN V4 <==> V5 COMPATIBILITY MODE ****************/ + trans_num tn_upgrd_blks_0; /* TN when blks_to_upgrd becomes 0. + * Never set = 0 => we have not achieved this yet, + * Always set = 1 => database was created as V5 (or current version) + */ + trans_num desired_db_format_tn; /* Database tn when last db format change occurred */ + trans_num reorg_db_fmt_start_tn; /* Copy of desired_db_format_tn when MUPIP REORG UPGRADE/DOWNGRADE started */ + block_id reorg_upgrd_dwngrd_restart_block; /* Block numbers lesser than this were last upgraded/downgraded by + * MUPIP REORG UPGRADE|DOWNGRADE before being interrupted */ + int4 blks_to_upgrd; /* Blocks not at current block version level */ + int4 blks_to_upgrd_subzero_error; /* number of times "blks_to_upgrd" potentially became negative */ + enum db_ver desired_db_format; /* Output version for database blocks (normally current version) */ + boolean_t fully_upgraded; /* Set to TRUE by MUPIP REORG UPGRADE when ALL blocks (including RECYCLED blocks) + * have been examined and upgraded (if necessary) and blks_to_upgrd is set to 0; + * If set to TRUE, this guarantees all blocks in the database are upgraded. + * "blks_to_upgrd" being 0 does not necessarily guarantee the same since the + * counter might have become incorrect (due to presently unknown reasons). + * set to FALSE whenever desired_db_format changes or the database is + * updated with V4 format blocks (by MUPIP JOURNAL). + */ + boolean_t db_got_to_v5_once; /* Set to TRUE by the FIRST MUPIP REORG UPGRADE (since MUPIP UPGRADE was run + * to upgrade the file header to V5 format) when it completes successfully. + * The FIRST reorg upgrade marks all RECYCLED blocks as FREE. Successive reorg + * upgrades keep RECYCLED blocks as they are while still trying to upgrade them. + * This is because ONLY the FIRST reorg upgrade could see RECYCLED blocks in V4 + * format that are too full (lack the additional space needed by the V5 block + * header) to be upgraded to V5 format. Once these are marked FREE, all future + * block updates happen in V5 format in the database buffers so even if they + * are written in V4 format to disk, they are guaranteed to be upgradeable. + * This field marks that transition in the db and is never updated thereafter. + */ + boolean_t opened_by_gtmv53; /* Set to TRUE the first time this database is opened by GT.M V5.3-000 and higher */ + char filler_384[12]; + /************* FIELDS RELATED TO DB TRANSACTION HISTORY *****************************/ + th_index trans_hist; /* transaction history - if moved from 1st filehdr block, change TH_BLOCK */ + char filler_trans_hist[8]; + /************* FIELDS RELATED TO WRITE CACHE FLUSHING *******************************/ + int4 flush_time[2]; + int4 flush_trigger; + int4 n_wrt_per_flu; /* Number of writes per flush call. Overloaded for BG and MM */ + int4 wait_disk_space; /* seconds to wait for diskspace before giving up on a db block write */ + int4 defer_time; /* defer write + * 0 => immediate, + * -1 => infinite defer, + * >0 => defer_time * flush_time[0] is actual defer time + * default value = 1 => a write-timer every csd->flush_time[0] seconds + */ + volatile boolean_t wc_blocked; /* Set to TRUE by process that knows it is leaving the cache in a possibly + * inconsistent state. Next process grabbing crit will do cache recovery. + * This setting also stops all concurrent writers from working on the cache. + * In MM mode, it is used to call wcs_recover during a file extension + */ + char filler_512[20]; + /************* FIELDS Used for update process performance improvement. Some may go away in later releases ********/ + uint4 reserved_for_upd; /* Percentage (%) of blocks reserved for update process disk read */ + uint4 avg_blks_per_100gbl; /* Number of blocks read on average for 100 global key read */ + uint4 pre_read_trigger_factor;/* Percentage (%) of blocks reserved for prereader disk read */ + uint4 writer_trigger_factor; /* For update process writers flush trigger */ + /************* FIELDS USED ONLY BY UNIX ********************************/ + int4 semid; /* Since int may not be of fixed size, int4 is used */ + int4 shmid; /* Since int may not be of fixed size, int4 is used */ + gtm_time8 gt_sem_ctime; /* time of creation of semaphore */ + gtm_time8 gt_shm_ctime; /* time of creation of shared memory */ + char filler_unixonly[40]; /* to ensure this section has 64-byte multiple size */ + /************* ACCOUNTING INFORMATION ********************************/ + int4 filler_n_retries[CDB_MAX_TRIES];/* Now moved to TAB_GVSTATS_REC section */ + int4 filler_n_puts; /* Now moved to TAB_GVSTATS_REC section */ + int4 filler_n_kills; /* Now moved to TAB_GVSTATS_REC section */ + int4 filler_n_queries; /* Now moved to TAB_GVSTATS_REC section */ + int4 filler_n_gets; /* Now moved to TAB_GVSTATS_REC section */ + int4 filler_n_order; /* Now moved to TAB_GVSTATS_REC section */ + int4 filler_n_zprevs; /* Now moved to TAB_GVSTATS_REC section */ + int4 filler_n_data; /* Now moved to TAB_GVSTATS_REC section */ + uint4 filler_n_puts_duplicate; /* Now moved to TAB_GVSTATS_REC section */ + uint4 filler_n_tp_updates; /* Now moved to TAB_GVSTATS_REC section */ + uint4 filler_n_tp_updates_duplicate; /* Now moved to TAB_GVSTATS_REC section */ + char filler_accounting_64_align[4]; /* to ensure this section has 64-byte multiple size */ + /************* CCP/RC RELATED FIELDS (CCP STUFF IS NOT USED CURRENTLY BY GT.M) *************/ + int4 staleness[2]; /* timer value */ + int4 ccp_tick_interval[2]; /* quantum to release write mode if no write occurs and others are queued + * These three values are all set at creation by mupip_create + */ + int4 ccp_quantum_interval[2];/* delta timer for ccp quantum */ + int4 ccp_response_interval[2];/* delta timer for ccp mailbox response */ + boolean_t ccp_jnl_before; /* used for clustered to pass if jnl file has before images */ + boolean_t clustered; /* FALSE (clustering is currently unsupported) */ + boolean_t unbacked_cache; /* FALSE for clustering. TRUE otherwise */ + + int4 rc_srv_cnt; /* Count of RC servers accessing database */ + int4 dsid; /* DSID value, non-zero when being accessed by RC */ + int4 rc_node; + char filler_ccp_rc[8]; /* to ensure this section has 64-byte multiple size */ + /************* REPLICATION RELATED FIELDS ****************/ + /* VMS does not yet have multi-site replication functionality. Hence the two sets of fields in this section. */ +#ifdef VMS + seq_num reg_seqno; /* the jnl seqno of the last update to this region -- 8-byte aligned */ + seq_num resync_seqno; /* the resync-seqno to be sent to the secondary */ + trans_num resync_tn; /* db tn corresponding to resync_seqno - used in losttrans handling */ + seq_num old_resync_seqno; /* to find out if transactions were sent from primary to secondary */ + int4 repl_state; /* state of replication whether open/closed/was_open */ + char filler_repl[28]; /* to ensure this section has 64-byte multiple size */ +#else + seq_num reg_seqno; /* the jnl seqno of the last update to this region -- 8-byte aligned */ + seq_num pre_multisite_resync_seqno; /* previous resync-seqno field now moved to the replication instance file */ + trans_num zqgblmod_tn; /* db tn corresponding to zqgblmod_seqno - used in losttrans handling */ + seq_num zqgblmod_seqno; /* minimum resync seqno of ALL -fetchresync rollbacks that happened on a secondary + * (that was formerly a root primary) AFTER the most recent + * MUPIP REPLIC -LOSTTNCOMPLETE command */ + int4 repl_state; /* state of replication whether open/closed/was_open */ + boolean_t multi_site_open; /* Set to TRUE the first time a process opens the database using + * a GT.M version that supports multi-site replication. FALSE until then */ + seq_num dualsite_resync_seqno; /* Last known seqno communicated with the other side of the replication pipe. + * This field is maintained as long as the other side is still running a + * dual-site GT.M version. Once all replication instances in a configuration + * are upgraded to the multi-site GT.M version, this field is no longer used. + */ + char filler_repl[16]; /* to ensure this section has 64-byte multiple size */ +#endif + /************* TP RELATED FIELDS ********************/ + int4 filler_n_tp_retries[12]; /* Now moved to TAB_GVSTATS_REC section */ + int4 filler_n_tp_retries_conflicts[12]; /* Now moved to TAB_GVSTATS_REC section */ + int4 tp_cdb_sc_blkmod[8]; /* Notes down the number of times each place got a cdb_sc_blkmod in tp. + * Only first 4 array entries are updated now, but space is allocated + * for 4 more if needed in the future. */ + /************* JOURNALLING RELATED FIELDS ****************/ + uint4 jnl_alq; + uint4 jnl_deq; + int4 jnl_buffer_size; /* in 512-byte pages */ + boolean_t jnl_before_image; + int4 jnl_state; /* journaling state: same as enum jnl_state_codes in jnl.h */ + uint4 jnl_file_len; /* journal file name length */ + uint4 autoswitchlimit; /* limit in disk blocks (max 4GB) when jnl should be auto switched */ + int4 epoch_interval; /* Time between successive epochs in epoch-seconds */ + uint4 alignsize; /* alignment size for JRT_ALIGN */ + int4 jnl_sync_io; /* drives sync I/O ('direct' if applicable) for journals, if set (UNIX) */ + /* writers open NOCACHING to bypass XFC cache, if set (VMS) */ + int4 yield_lmt; /* maximum number of times a process yields to get optimal jnl writes */ + boolean_t turn_around_point; + trans_num jnl_eovtn; /* last tn for a closed jnl; otherwise epoch tn from the epoch before last */ + char filler_jnl[8]; /* to ensure this section has 64-byte multiple size */ + /************* INTERRUPTED RECOVERY RELATED FIELDS ****************/ + seq_num intrpt_recov_resync_seqno;/* resync/fetchresync jnl_seqno of interrupted rollback */ + jnl_tm_t intrpt_recov_tp_resolve_time;/* since-time for the interrupted recover */ + boolean_t recov_interrupted; /* whether a MUPIP JOURNAL RECOVER/ROLLBACK on this db got interrupted */ + int4 intrpt_recov_jnl_state; /* journaling state at start of interrupted recover/rollback */ + int4 intrpt_recov_repl_state;/* replication state at start of interrupted recover/rollback */ + char filler_1k[40]; + /************* HUGE CHARACTER ARRAYS **************/ + unsigned char jnl_file_name[JNL_NAME_SIZE]; /* journal file name */ + unsigned char reorg_restart_key[256]; /* 1st key of a leaf block where reorg was done last time */ + char machine_name[MAX_MCNAMELEN]; + char encryption_hash[GTMCRYPT_RESERVED_HASH_LEN]; + /* char filler_2k[256] was here before adding the encryption_hash. Since the GTMCRYPT_RESERVED_HASH_LEN + * consumes 256 bytes, filler_2k has been removed. */ + /************* BG_TRC_REC RELATED FIELDS ***********/ +# define TAB_BG_TRC_REC(A,B) bg_trc_rec_tn B##_tn; +# include "tab_bg_trc_rec.h" +# undef TAB_BG_TRC_REC + char bg_trc_rec_tn_filler [1200 - (SIZEOF(bg_trc_rec_tn) * n_bg_trc_rec_types)]; + +# define TAB_BG_TRC_REC(A,B) bg_trc_rec_cntr B##_cntr; +# include "tab_bg_trc_rec.h" +# undef TAB_BG_TRC_REC + char bg_trc_rec_cntr_filler[ 600 - (SIZEOF(bg_trc_rec_cntr) * n_bg_trc_rec_types)]; + + /************* DB_CSH_ACCT_REC RELATED FIELDS ***********/ +# define TAB_DB_CSH_ACCT_REC(A,B,C) db_csh_acct_rec A; +# include "tab_db_csh_acct_rec.h" +# undef TAB_DB_CSH_ACCT_REC + char db_csh_acct_rec_filler_4k[ 248 - (SIZEOF(db_csh_acct_rec) * n_db_csh_acct_rec_types)]; + + /************* GVSTATS_REC RELATED FIELDS ***********/ + gvstats_rec_t gvstats_rec; + char gvstats_rec_filler_4k_plus_512[ 512 - SIZEOF(gvstats_rec_t)]; + char filler_5k_minus_16[512 - 16]; + /************* DB CREATION AND UPGRADE CERTIFICATION FIELDS ***********/ + enum db_ver creation_db_ver; /* Major DB version at time of creation */ + enum mdb_ver creation_mdb_ver; /* Minor DB version at time of creation */ + enum db_ver certified_for_upgrade_to; /* Version the database is certified for upgrade to */ + int filler_5k; + /************* SECSHR_DB_CLNUP RELATED FIELDS (now moved to node_local) ***********/ + int4 secshr_ops_index_filler; + int4 secshr_ops_array_filler[255]; /* taking up 1k */ + /********************************************************/ + compswap_time_field next_upgrd_warn; /* Time when we can send the next upgrade warning to the operator log */ + boolean_t is_encrypted; + uint4 db_trigger_cycle; /* incremented every MUPIP TRIGGER command that changes ^#t global contents */ + char filler_7k[992]; + char filler_8k[1024]; + /********************************************************/ + /* Master bitmap immediately follows. Tells whether the local bitmaps have any free blocks or not. */ +} sgmnt_data; + +#ifdef DB64 +# ifdef __osf__ +# pragma pointer_size(save) +# pragma pointer_size(long) +# else +# error UNSUPPORTED PLATFORM +# endif +#endif + +typedef sgmnt_data *sgmnt_data_ptr_t; + +#ifdef DB64 +# ifdef __osf__ +# pragma pointer_size(restore) +# endif +#endif + +typedef struct +{ + FILL8DCL(cache_que_heads_ptr_t, cache_state, 1); /* pointer to beginnings of state queues */ +} sgbg_addrs; + +typedef struct +{ + FILL8DCL(sm_uc_ptr_t, base_addr, 1); + FILL8DCL(mmblk_que_heads_ptr_t, mmblk_state, 2); /* pointer to beginnings of state and blk queues */ +} sgmm_addrs; + +#define MAX_NM_LEN MAX_MIDENT_LEN +#define MIN_RN_LEN 1 +#define MAX_RN_LEN MAX_MIDENT_LEN +#define V4_MAX_RN_LEN 31 /* required for dbcertify.h */ +#define MIN_SN_LEN 1 +#define MAX_SN_LEN MAX_MIDENT_LEN +#define STR_SUB_PREFIX 0x0FF +#define SUBSCRIPT_STDCOL_NULL 0x01 +#define STR_SUB_ESCAPE 0X01 +#define STR_SUB_MAXVAL 0xFF +#define SUBSCRIPT_ZERO 0x080 +#define SUBSCRIPT_BIAS 0x0BE +#define NEG_MNTSSA_END 0x0FF +#define KEY_DELIMITER 0X00 +#define MIN_DB_BLOCKS 10 /* this should be maintained in conjunction with the mimimum allocation in GDEINIT.M */ + +/* definition for NULL_SUBSCRIPTS */ +#define NEVER 0 +#define ALWAYS 1 +#define ALLOWEXISTING 2 + +#define OFFSET(x,y) ((uchar_ptr_t)x - (uchar_ptr_t)y) + +#define FC_READ 0 +#define FC_WRITE 1 +#define FC_OPEN 2 +#define FC_CLOSE 3 + +typedef struct file_control_struct +{ + sm_uc_ptr_t op_buff; + UNIX_ONLY(gtm_int64_t) VMS_ONLY(int4) op_pos; + int op_len; + void *file_info; /* Pointer for OS specific struct */ + char file_type; + char op; +} file_control; + +typedef struct header_struct_struct +{ + char label[12]; + unsigned filesize; /* size of file excluding GDE info */ + + /* removed unused file_log struct */ +} header_struct; + +typedef struct gdr_name_struct +{ + mstr name; + mstr exp_name; + struct gdr_name_struct *link; + struct gd_addr_struct *gd_ptr; +} gdr_name; + +typedef struct gd_addr_struct +{ + struct gd_region_struct *local_locks; + int4 max_rec_size; + short n_maps; + short n_regions; + short n_segments; + short filler; + struct gd_binding_struct *maps; + struct gd_region_struct *regions; + struct gd_segment_struct *segments; + struct gd_addr_struct *link; + struct hash_table_mname_struct *tab_ptr; + gd_id *id; + UINTPTR_T end; +} gd_addr; +typedef gd_addr *(*gd_addr_fn_ptr)(); + +typedef struct gd_segment_struct +{ + unsigned short sname_len; + unsigned char sname[MAX_SN_LEN + 1]; + unsigned short fname_len; + unsigned char fname[MAX_FN_LEN + 1]; + unsigned short blk_size; + unsigned short ext_blk_count; + uint4 allocation; + struct CLB *cm_blk; + unsigned char defext[4]; + char defer_time; /* Was passed in cs_addrs */ + unsigned char file_type; + unsigned char buckets; /* Was passed in FAB */ + unsigned char windows; /* Was passed in FAB */ + uint4 lock_space; + uint4 global_buffers; /* Was passed in FAB */ + uint4 reserved_bytes; /* number of bytes to be left in every database block */ + enum db_acc_method acc_meth; + file_control *file_cntl; + struct gd_region_struct *repl_list; + UNIX_ONLY(boolean_t is_encrypted;) +} gd_segment; + +typedef union +{ + int4 offset; /* relative offset to segment */ + gd_segment *addr; /* absolute address of segment */ +} gd_seg_addr; + +typedef struct gd_region_struct +{ + unsigned short rname_len; + unsigned char rname[MAX_RN_LEN + 1]; + unsigned short max_key_size; + uint4 max_rec_size; + gd_seg_addr dyn; + gd_seg_addr stat; + bool open; + bool lock_write; /* Field is not currently used by GT.M */ + char null_subs; /* 0 ->NEVER(previous NO), 1->ALWAYS(previous YES), 2->ALLOWEXISTING + * i.e. will allow read null subs but prohibit set */ + unsigned char jnl_state; + + /* deleted gbl_lk_root and lcl_lk_root, obsolete fields */ + + uint4 jnl_alq; + unsigned short jnl_deq; + short jnl_buffer_size; + bool jnl_before_image; + bool opening; + bool read_only; + bool was_open; + unsigned char cmx_regnum; + unsigned char def_coll; + bool std_null_coll; /* 0 -> GT.M null collation,i,e, null subs collate between numeric and string + * 1-> standard null collation i.e. null subs collate before numeric and string */ + unsigned char jnl_file_len; + unsigned char jnl_file_name[JNL_NAME_SIZE]; + + /* VMS file id struct goes to OS specific struct */ + /* VMS lock structure for reference goes to OS specific struct */ + + int4 node; + int4 sec_size; +} gd_region; + +typedef struct sgmnt_addrs_struct +{ + sgmnt_data_ptr_t hdr; + sm_uc_ptr_t bmm; + sm_uc_ptr_t wc; + bt_rec_ptr_t bt_header; + bt_rec_ptr_t bt_base; + th_rec_ptr_t th_base; + th_index_ptr_t ti; + node_local_ptr_t nl; + mutex_struct_ptr_t critical; + struct shmpool_buff_hdr_struct *shmpool_buffer; /* 1MB chunk of shared memory that we micro manage */ + sm_uc_ptr_t db_addrs[2]; + sm_uc_ptr_t lock_addrs[2]; + struct gv_namehead_struct *dir_tree; +# ifdef GTM_TRIGGER + struct gv_namehead_struct *hasht_tree; +# endif + struct sgmnt_addrs_struct *next_fenced; /* NULL if db has journaling turned off (or disabled) + * Otherwise (db has journaling turned on), it is + * NULL if this db was not updated in this TP/ZTP + * non-NULL if this db was updated in this TP/ZTP + * The non-NULL value points to the next csa that + * has a non-NULL next_fenced value i.e. a linked list + * of csas. The end of the list is JNL_FENCE_LIST_END + * (cannot use NULL due to special meaning described + * above and hence using a macro which evaluates to -1). + */ + struct jnl_private_control_struct *jnl; + struct sgm_info_struct *sgm_info_ptr; + gd_region *region; /* the region corresponding to this csa */ + struct hash_table_mname_struct *gvt_hashtab; /* NON-NULL only if regcnt > 1; + * Maintains all gv_targets mapped to this db file */ + struct reg_ctl_list_struct *rctl; /* pointer to rctl for this region (used only if jgbl.forw_phase_recovery) */ + struct sgmnt_addrs_struct *next_csa; /* points to csa of NEXT database that has been opened by this process */ +# ifdef GTM_CRYPT + char *encrypted_blk_contents; + gtmcrypt_key_t encr_key_handle; + int4 encrypt_init_status; +# endif +# ifdef GTM_SNAPSHOT + struct snapshot_context_struct *ss_ctx; +# endif + union + { + sgmm_addrs mm; + sgbg_addrs bg; + /* May add new pointers here for other methods or change to void ptr */ + } acc_meth; + gvstats_rec_t gvstats_rec; + /* 8-byte aligned at this point on all platforms (32-bit, 64-bit or Tru64 which is a mix of 32-bit and 64-bit pointers) */ + sgmnt_data_ptr_t mm_core_hdr; /* Most OSs don't include memory mapped files in the core dump. For MM access + * mode, this is a pointer to a copy of the header that will be in the corefile. + * The pointer is only used for MM and that too only in Unix. + */ + size_t fullblockwrite_len; /* Length of a full block write */ + uint4 total_blks; /* Last we knew, file was this big. Used to + * signal MM processing file was extended and + * needs to be remapped. */ + uint4 prev_free_blks; + /* The following uint4's are treated as bools but must be 4 bytes to avoid interaction between + bools in interrupted routines and possibly lost data */ + volatile uint4 timer; /* This process has a timer for this region */ + volatile uint4 in_wtstart; /* flag we are busy writing */ + volatile uint4 now_crit; /* This process has the critical write lock */ + volatile uint4 wbuf_dqd; /* A write buffer has been dequeued - signals that + extra cleanup required if die while on */ + uint4 stale_defer; /* Stale processing deferred this region */ + boolean_t freeze; + volatile boolean_t dbsync_timer; /* whether a timer to sync the filehdr (and write epoch) is active */ + block_id reorg_last_dest; /* last destinition block used for swap */ + boolean_t jnl_before_image; + boolean_t read_write; + boolean_t extending; + boolean_t persistent_freeze; /* if true secshr_db_clnup() won't unfreeze this region */ + /* The following 3 fields are in cs_addrs instead of in the file-header since they are a function + * of the journal-record sizes that can change with journal-version-numbers (for the same database). + */ + int4 pblk_align_jrecsize; /* maximum size of a PBLK record with corresponding ALIGN record */ + int4 min_total_tpjnl_rec_size; /* minimum journal space requirement for a TP transaction */ + int4 min_total_nontpjnl_rec_size; /* minimum journal space requirement for a non-TP transaction */ + int4 jnl_state; /* journaling state: it can be 0, 1 or 2 (same as enum jnl_state_codes in jnl.h) */ + int4 repl_state; /* state of replication whether open/closed/was_open */ + uint4 crit_check_cycle; /* Used to mark which regions in a transaction legiticamtely have crit */ + int4 backup_in_prog; /* true if online backup in progress for this region (used in op_tcommit/tp_tend) */ + boolean_t snapshot_in_prog; /* true if snapshots are in progress for this region */ + int4 ref_cnt; /* count of number of times csa->nl->ref_cnt was incremented by this process */ + int4 fid_index; /* index for region ordering based on unique_id */ + boolean_t do_fullblockwrites; /* This region enabled for full block writes */ + int4 regnum; /* Region number (region open counter) used by journaling so all tokens + have a unique prefix per region (and all regions have same prefix) + */ + int4 n_pre_read_trigger; /* For update process to keep track of progress and when to trigger pre-read */ + boolean_t replinst_matches_db; /* TRUE if replication instance file name stored in db shared memory matches the + * instance file name stored in the journal pool that this process has attached to. + * Updates are allowed to this replicated database only if this is TRUE. + */ + int4 regcnt; /* # of regions that have this as their csa */ + boolean_t t_commit_crit; /* set to FALSE by default. set to TRUE if in the middle of database commit. + * if access method is BG, this assumes a multi-state value. + * FALSE -> T_COMMIT_CRIT_PHASE1 -> T_COMMIT_CRIT_PHASE2 -> FALSE + * (bg_update_phase1) (bg_update_phase2) (finish commit) + */ + boolean_t wcs_pidcnt_incremented; /* set to TRUE if we incremented cnl->wcs_phase2_commit_pidcnt. + * used by secshr_db_clnup to decrement the shared counter. */ + boolean_t incr_db_trigger_cycle; /* set to FALSE by default. set to TRUE if trigger state change (in ^#t) occurs for + * any global in this database which means an increment to csa->db_trigger_cycle and + * csd->db_trigger_cycle. Currently used by MUPIP TRIGGER/$ZTRIGGER(), MUPIP RECOVER + * and UPDATE PROCESS + */ + uint4 db_trigger_cycle; /* mirror of csd->db_trigger_cycle; used to detect concurrent ^#t global changes */ + uint4 db_dztrigger_cycle; /* incremented on every $ZTRIGGER() operation. Due to the presence of $ZTRIGGER() + * and ZTRIGGER command the 'd' prefix for ztrigger in db_dztrigger_cycle is used + * to denote the '$' in $ZTRIGGER() */ + boolean_t hold_onto_crit; /* TRUE currently for dse if a CRIT -SEIZE has been done on this region. + * Set to FALSE by a DSE CRIT -RELEASE done on this region. Will also be TRUE in + * case of ONLINE ROLLBACK when that is implemented. Any code that can be invoked + * by both DSE and ROLLBACK should use csa->hold_onto_crit. Any code that can be + * invoked only by ROLLBACK can use the global variable "hold_onto_locks" as that + * is faster (avoids the csa-> dereference) and encompasses all database regions. + */ +} sgmnt_addrs; + +typedef struct gd_binding_struct +{ + unsigned char name[MAX_NM_LEN + 1]; + union + { + gd_region *addr; + int4 offset; + } reg; +} gd_binding; + +typedef struct +{ + unsigned short offset; + unsigned short match; +} srch_rec_status; + +typedef struct srch_blk_status_struct +{ + cache_rec_ptr_t cr; + sm_uc_ptr_t buffaddr; + block_id blk_num; + trans_num tn; + srch_rec_status prev_rec, + curr_rec; + int4 cycle; + int4 level; + struct cw_set_element_struct *cse; + struct srch_blk_status_struct *first_tp_srch_status; + struct gv_namehead_struct *blk_target; +} srch_blk_status; + +/* Defines for "cycle" member in srch_blk_status. + * For histories pointing to shared-memory buffers, + * "cycle" will be CYCLE_SHRD_COPY in MM and some positive number in BG. + * For histories pointing to privately-built blocks, + * "cycle" will be CYCLE_PVT_COPY for both BG and MM. + */ +#define CYCLE_PVT_COPY -1 +#define CYCLE_SHRD_COPY -2 + +typedef struct +{ + int4 depth; + int4 filler; + srch_blk_status h[MAX_BT_DEPTH + 1]; +} srch_hist; + +typedef struct gv_key_struct +{ + unsigned short top; /* Offset to top of buffer allocated for the key */ + unsigned short end; /* End of the current key. Offset to the second null */ + unsigned short prev; /* Offset to the start of the previous subscript. + * This is used for global nakeds. + */ + unsigned char base[1]; /* Base of the key */ +} gv_key; + +/* The direction that the newly added record went after a block split at a given level */ +enum split_dir +{ + NEWREC_DIR_FORCED, /* direction forced due to one of the sides being too-full i.e. no choice */ + NEWREC_DIR_LEFT, /* new record went into the end of the left block after the split */ + NEWREC_DIR_RIGHT, /* new record went into the beginning of the right block after the split */ +}; + +/* Any change to this structure should also have a corresponding [re]initialization in mupip_recover.c + * in the code where we play the records in the forward phase i.e. go through each of the jnl_files + * and within if (mur_options.update), initialize necessary fields of gv_target before proceeding with mur_forward(). + */ +typedef struct gv_namehead_struct +{ + gv_key *first_rec, *last_rec; /* Boundary recs of clue's data block */ + struct gv_namehead_struct *next_gvnh; /* Used to chain gv_target's together */ + struct gv_namehead_struct *prev_gvnh; /* Used to chain gv_target's together */ + struct gv_namehead_struct *next_tp_gvnh; /* Used to chain gv_targets participating in THIS TP transaction */ + sgmnt_addrs *gd_csa; /* Pointer to Segment corresponding to this key */ + srch_hist *alt_hist; /* alternate history. initialized once per gv_target */ + struct collseq_struct *collseq; /* pointer to a linked list of user supplied routine addresses + for internationalization */ + trans_num read_local_tn; /* local_tn of last reference for this global */ + GTMTRIG_ONLY(trans_num trig_local_tn;) /* local_tn of last trigger driven for this global */ + boolean_t noisolation; /* whether isolation is turned on or off for this global */ + block_id root; /* Root of global variable tree */ + mname_entry gvname; /* the name of the global */ + NON_GTM64_ONLY(uint4 filler_8byte_align0;) /* for 8-byte alignment of "hist" member */ + srch_hist hist; /* block history array */ + int4 regcnt; /* number of global directories whose hash-tables point to this gv_target. + * 1 by default. > 1 if the same name in TWO DIFFERENT global directories + * maps to the same physical file (i.e. two regions in different global + * directories have the same physical file). + */ + unsigned char nct; /* numerical collation type for internalization */ + unsigned char act; /* alternative collation type for internalization */ + unsigned char ver; + bool split_cleanup_needed; + char last_split_direction[MAX_BT_DEPTH - 1]; /* maintain last split direction for each level in the GVT */ + char filler_8byte_align1[2]; + block_id last_split_blk_num[MAX_BT_DEPTH - 1]; +# ifdef GTM_TRIGGER + struct gvt_trigger_struct *gvt_trigger; /* pointer to trigger info for this global + * (is non-NULL only if db_trigger_cycle is non-zero) */ + uint4 db_trigger_cycle; /* copy of csd->db_trigger_cycle when triggers for this global were + * last read/initialized from ^#t global (in gvtr_init) */ + uint4 db_dztrigger_cycle; /* copy of csa->db_dztrigger_cycle when triggers for this global were + * last read/initialized from ^#t global (in gvtr_init) */ + boolean_t trig_mismatch_test_done; /* whether update process has checked once if there is a mismatch + * in trigger definitions between originating and replicating instance */ + GTM64_ONLY(uint4 filler_8byte_align2;) /* for 8-byte alignment of "clue" member. (targ_alloc relies on this) */ +# endif + gv_key clue; /* Clue key, must be last in namehead struct because of hung buffer. */ +} gv_namehead; + +typedef struct gvnh_reg_struct +{ + gv_namehead *gvt; + gd_region *gd_reg; /* Region of key */ +} gvnh_reg_t; + +#define INVALID_GV_TARGET (gv_namehead *)-1L + +typedef struct gvsavtarg_struct +{ + gd_addr *gd_targ_addr; + gd_binding *gd_map; + gd_region *gv_cur_region; + gv_namehead *gv_target; + bool gv_last_subsc_null; + bool gv_some_subsc_null; + short prev; + short end; + short filler_8byte_align; +} gvsavtarg_t; + +#define GVSAVTARG_ALIGN_BNDRY 8 +#define GVSAVTARG_FIXED_SIZE (SIZEOF(gvsavtarg_t)) + +/* Following three macros define the mechanism to restore gv_target under normal and error conditions. + * RESET_GV_TARGET should be used to restore gv_target from the global, reset_gv_target, only when we + * are sure that this function is the first one in the call stack to have saved gv_target. + * If the module that needs the restoration mechanism is not the first one to save gv_target in the call + * stack, then one of the last two macros should be used. + * RESET_GV_TARGET_LCL is used to restore gv_target from the local variable used to save gv_target. + * RESET_GV_TARGET_LCL_AND_CLR_GBL is used at the end of the module, when there are no more gv_target + * restorations needed. This resets gv_target and invalidates reset_gv_target. + * + * This mechanism ensures that, when there are multiple functions in a given call stack that save and + * restore gv_target, only the bottom most function gets to store its value in the global, reset_gv_target. + * In case of rts errors, if the error is not SUCCESS or INFO, then gv_target gets restored to reset_gv_target + * (in preemptive_ch()). For SUCCESS or INFO, no restoration is necessary because CONTINUE from the condition + * handlers would take us through the normal path for gv_target restoration. + */ + +#define SKIP_GVT_GVKEY_CHECK FALSE +#define DO_GVT_GVKEY_CHECK TRUE + +#define RESET_GV_TARGET(GVT_GVKEY_CHECK) \ +{ \ + assert(INVALID_GV_TARGET != reset_gv_target); \ + gv_target = reset_gv_target; \ + reset_gv_target = INVALID_GV_TARGET; \ + DEBUG_ONLY( \ + if (GVT_GVKEY_CHECK) \ + { \ + DBG_CHECK_GVTARGET_CSADDRS_IN_SYNC; \ + DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC; \ + } \ + ) \ +} + +#define RESET_GV_TARGET_LCL(SAVE_TARG) gv_target = SAVE_TARG; + +#define RESET_GV_TARGET_LCL_AND_CLR_GBL(SAVE_TARG) \ +{ \ + gv_target = SAVE_TARG; \ + if (!gbl_target_was_set) \ + { \ + assert(SAVE_TARG == reset_gv_target || INVALID_GV_TARGET == reset_gv_target); \ + DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC; \ + reset_gv_target = INVALID_GV_TARGET; \ + } \ +} + + +/* No point doing the gvtarget-gvcurrkey in-sync check or the gvtarget-csaddrs in-sync check if we are anyways going to exit. + * There is no way op_gvname (which is where these design assumptions get actually used) is going to be called from now onwards. + */ +GBLREF int process_exiting; +GBLREF trans_num local_tn; +GBLREF gv_namehead *gvt_tp_list; + +#define ADD_TO_GVT_TP_LIST(GVT) \ +{ \ + if (GVT->read_local_tn != local_tn) \ + { /* Set read_local_tn to local_tn; Also add GVT to list of gvtargets referenced in this TP transaction. */ \ + GVT->read_local_tn = local_tn; \ + GVT->next_tp_gvnh = gvt_tp_list; \ + gvt_tp_list = GVT; \ + } else \ + { /* Check that GVT is already part of the list of gvtargets referenced in this TP transaction */ \ + DBG_CHECK_IN_GVT_TP_LIST(GVT, TRUE); /* TRUE => we check that GVT IS present in the gvt_tp_list */ \ + } \ +} + +#ifdef DEBUG +#define DBG_CHECK_IN_GVT_TP_LIST(gvt, present) \ +{ \ + gv_namehead *gvtarg; \ + \ + GBLREF gv_namehead *gvt_tp_list; \ + GBLREF uint4 dollar_tlevel; \ + \ + for (gvtarg = gvt_tp_list; NULL != gvtarg; gvtarg = gvtarg->next_tp_gvnh) \ + { \ + if (gvtarg == gvt) \ + break; \ + } \ + assert(!present || (NULL != gvtarg)); \ + assert(present || (NULL == gvtarg) || (process_exiting && !dollar_tlevel)); \ +} + +#define DBG_CHECK_GVT_IN_GVTARGETLIST(gvt) \ +{ \ + gv_namehead *gvtarg; \ + \ + GBLREF gd_region *gv_cur_region; \ + GBLREF gv_namehead *gv_target_list; \ + \ + for (gvtarg = gv_target_list; NULL != gvtarg; gvtarg = gvtarg->next_gvnh) \ + { \ + if (gvtarg == gvt) \ + break; \ + } \ + /* For dba_cm or dba_usr type of regions, gv_target_list is not maintained so \ + * if gv_target is not part of gv_target_list, assert region is not BG or MM. \ + */ \ + assert((NULL != gvtarg) || (dba_cm == gv_cur_region->dyn.addr->acc_meth) \ + || (dba_usr == gv_cur_region->dyn.addr->acc_meth)); \ +} + +#define DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC \ +{ \ + mname_entry *gvent; \ + mstr *varname; \ + int varlen; \ + unsigned short keyend; \ + unsigned char *keybase; \ + \ + GBLREF int4 gv_keysize; \ + \ + GBLREF gv_key *gv_currkey; \ + GBLREF gv_namehead *reset_gv_target; \ + \ + assert((NULL != gv_currkey) || (NULL == gv_target)); \ + /* make sure gv_currkey->top always reflects the maximum keysize across all dbs that we opened until now */ \ + assert((NULL == gv_currkey) || (gv_currkey->top == gv_keysize)); \ + keybase = &gv_currkey->base[0]; \ + if (!process_exiting && (NULL != gv_currkey) && (0 != keybase[0]) && (INVALID_GV_TARGET == reset_gv_target)) \ + { \ + assert(NULL != gv_target); \ + gvent = &gv_target->gvname; \ + varname = &gvent->var_name; \ + varlen = varname->len; \ + assert(varlen); \ + assert((0 != keybase[varlen]) || !memcmp(keybase, varname->addr, varlen)); \ + keyend = gv_currkey->end; \ + assert(!keyend || (KEY_DELIMITER == keybase[keyend])); \ + assert(!keyend || (KEY_DELIMITER == keybase[keyend - 1])); \ + /* Check that gv_target is part of the gv_target_list */ \ + DBG_CHECK_GVT_IN_GVTARGETLIST(gv_target); \ + } \ + /* Do gv_target sanity check as well; Do not do this if it is NULL or if it is GT.CM GNP client (gd_csa is NULL) */ \ + if ((NULL != gv_target) && (NULL != gv_target->gd_csa)) \ + DBG_CHECK_GVTARGET_INTEGRITY(gv_target); \ +} + +/* Do checks on the integrity of various fields in gv_target. targ_alloc initializes these and they are supposed to + * stay that way. The following code is very similar to that in targ_alloc so needs to be maintained in sync. This + * macro expects that gv_target->gd_csa is non-NULL (could be NULL for GT.CM GNP client) so any callers of this macro + * should ensure they do not invoke it in case of NULL gd_csa. + */ +#define DBG_CHECK_GVTARGET_INTEGRITY(GVT) \ +{ \ + int keysize, partial_size; \ + GBLREF boolean_t dse_running; \ + GBLREF jnl_gbls_t jgbl; \ + \ + /* Forward recovery does targ_alloc of MAX_KEY_SZ (independent of db max_key_size) for csa->dir_tree. \ + * Take that into account while computing keysize. \ + */ \ + keysize = (!jgbl.forw_phase_recovery || (GVT->gd_csa->dir_tree != GVT)) ? GVT->gd_csa->hdr->max_key_size : MAX_KEY_SZ; \ + keysize = DBKEYSIZE(keysize); \ + partial_size = SIZEOF(gv_namehead) + 2 * SIZEOF(gv_key) + 3 * keysize; \ + /* DSE could change the max_key_size dynamically so account for it in the below assert */ \ + if (!dse_running) \ + { \ + assert(GVT->gvname.var_name.addr == (char *)GVT + partial_size); \ + assert((char *)GVT->first_rec == ((char *)&GVT->clue + SIZEOF(gv_key) + keysize)); \ + assert((char *)GVT->last_rec == ((char *)GVT->first_rec + SIZEOF(gv_key) + keysize)); \ + assert(GVT->clue.top == keysize); \ + } \ + assert(GVT->clue.top == GVT->first_rec->top); \ + assert(GVT->clue.top == GVT->last_rec->top); \ +} +#else +# define DBG_CHECK_IN_GVT_TP_LIST(gvt, present) +# define DBG_CHECK_GVT_IN_GVTARGETLIST(gvt) +# define DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC +# define DBG_CHECK_GVTARGET_INTEGRITY(GVT) +#endif + +/* The below GBLREFs are for the following macro */ +GBLREF gv_namehead *gv_target; +GBLREF sgmnt_addrs *cs_addrs; +#define DBG_CHECK_GVTARGET_CSADDRS_IN_SYNC assert(process_exiting || (NULL == gv_target) || (gv_target->gd_csa == cs_addrs)) + +/* Indicate incompleteness of (potentially subscripted) global name by adding a "*" (without closing ")") at the end */ +#define GV_SET_LAST_SUBSCRIPT_INCOMPLETE(BUFF, END) \ +{ \ + if (NULL == (char *)(END)) \ + { /* The buffer passed to format_targ_key was not enough \ + * for the transformation. We don't expect this. Handle \ + * it nevertheless by adding ",*" at end. \ + */ \ + assert(FALSE); \ + END = ((unsigned char *)ARRAYTOP(BUFF)) - 1; \ + assert((char *)(END) > (char *)(BUFF)); \ + *(END)++ = '*'; \ + } else \ + { /* Overflow occurred while adding the global name OR \ + * after adding the last subscript OR in the middle of \ + * adding a subscript (not necessarily last). In all \ + * cases, add a '*' at end to indicate incompleteness. \ + */ \ + if (')' == END[-1]) \ + (END)--; \ + /* ensure we have space to write 1 byte */ \ + assert((char *)(END) + 1 <= ((char *)ARRAYTOP(BUFF))); \ + *(END)++ = '*'; \ + } \ +} + +#define ISSUE_GVSUBOFLOW_ERROR(GVKEY) \ +{ \ + unsigned char *endBuff, fmtBuff[MAX_ZWR_KEY_SZ]; \ + \ + /* Assert that input key to format_targ_key is double null terminated */ \ + assert(KEY_DELIMITER == GVKEY->base[GVKEY->end]); \ + endBuff = format_targ_key(fmtBuff, ARRAYSIZE(fmtBuff), GVKEY, TRUE); \ + GV_SET_LAST_SUBSCRIPT_INCOMPLETE(fmtBuff, endBuff); /* Note: might update "endBuff" */ \ + rts_error(VARLSTCNT(6) ERR_GVSUBOFLOW, 0, ERR_GVIS, 2, endBuff - fmtBuff, fmtBuff); \ +} + +#define COPY_SUBS_TO_GVCURRKEY(mvarg, max_key, gv_currkey, was_null, is_null) \ +{ \ + GBLREF mv_stent *mv_chain; \ + GBLREF unsigned char *msp, *stackwarn, *stacktop; \ + mval temp; \ + unsigned char buff[MAX_ZWR_KEY_SZ], *end; \ + int len; \ + \ + was_null |= is_null; \ + if (mvarg->mvtype & MV_SUBLIT) \ + { \ + is_null = ((STR_SUB_PREFIX == *(unsigned char *)mvarg->str.addr) \ + && (KEY_DELIMITER == *(mvarg->str.addr + 1))); \ + if (gv_target->collseq || gv_target->nct) \ + { \ + /* collation transformation should be done at the server's end for CM regions */ \ + assert(dba_cm != gv_cur_region->dyn.addr->acc_meth); \ + TREF(transform) = FALSE; \ + end = gvsub2str((uchar_ptr_t)mvarg->str.addr, buff, FALSE); \ + TREF(transform) = TRUE; \ + temp.mvtype = MV_STR; \ + temp.str.addr = (char *)buff; \ + temp.str.len = (mstr_len_t)(end - buff); \ + mval2subsc(&temp, gv_currkey); \ + } else \ + { \ + len = mvarg->str.len; \ + if (gv_currkey->end + len - 1 >= max_key) \ + ISSUE_GVSUBOFLOW_ERROR(gv_currkey); \ + memcpy((gv_currkey->base + gv_currkey->end), mvarg->str.addr, len); \ + if (is_null && 0 != gv_cur_region->std_null_coll) \ + gv_currkey->base[gv_currkey->end] = SUBSCRIPT_STDCOL_NULL; \ + gv_currkey->prev = gv_currkey->end; \ + gv_currkey->end += len - 1; \ + } \ + } else \ + { \ + MV_FORCE_DEFINED(mvarg); \ + mval2subsc(mvarg, gv_currkey); \ + if (gv_currkey->end >= max_key) \ + ISSUE_GVSUBOFLOW_ERROR(gv_currkey); \ + is_null = (MV_IS_STRING(mvarg) && (0 == mvarg->str.len)); \ + } \ +} + +/* Copy GVKEY to GVT->CLUE. Take care NOT to copy cluekey->top to GVKEY->top as they correspond + * to the allocation sizes of two different memory locations and should stay untouched. + */ +#define COPY_CURRKEY_TO_GVTARGET_CLUE(GVT, GVKEY) \ +{ \ + gv_key *cluekey; \ + \ + if (GVT->clue.top <= GVKEY->end) \ + GTMASSERT; \ + assert(KEY_DELIMITER == GVKEY->base[GVKEY->end]); \ + assert(KEY_DELIMITER == GVKEY->base[GVKEY->end - 1]); \ + cluekey = &GVT->clue; \ + memcpy(cluekey->base, GVKEY->base, GVKEY->end + 1); \ + cluekey->end = GVKEY->end; \ + cluekey->prev = GVKEY->prev; \ + DBG_CHECK_GVTARGET_INTEGRITY(GVT); \ +} + +/* Macro to denote special value of first_rec when it is no longer reliable */ +#define GVT_CLUE_FIRST_REC_UNRELIABLE (short)0xffff + +/* Macro to denote special value of last_rec when it is the absolute maximum (in case of *-keys all the way down) */ +#define GVT_CLUE_LAST_REC_MAXKEY (short)0xffff + +/* Macro to reset first_rec to a special value to indicate it is no longer reliable + * (i.e. the keyrange [first_rec, clue] should not be used by gvcst_search. + * Note that [clue, last_rec] is still a valid keyrange and can be used by gvcst_search. + */ +#define GVT_CLUE_INVALIDATE_FIRST_REC(GVT) \ +{ \ + assert(GVT->clue.end); \ + *((short *)GVT->first_rec->base) = GVT_CLUE_FIRST_REC_UNRELIABLE; \ +} + +#ifdef DEBUG +/* Macro to check that the clue is valid. Basically check that first_rec <= clue <= last_rec. Also check that + * all of them start with the same global name in case of a GVT. A clue that does not satisfy these validity + * checks implies the possibility of DBKEYORD errors (e.g. C9905-001119 in VMS). + */ +#define DEBUG_GVT_CLUE_VALIDATE(GVT) \ +{ \ + mname_entry *gvent; \ + unsigned short klen; \ + gv_namehead *gvt; \ + \ + /* Verify that clue->first_rec <= clue.base <= clue->last_rec. \ + * The only exception is if first_rec has been reset to an unreliable value. \ + */ \ + gvt = GVT; /* copy into local variable to avoid evaluating input multiple times */ \ + klen = MIN(gvt->clue.end, gvt->first_rec->end); \ + assert(klen); \ + assert((0 <= memcmp(gvt->clue.base, gvt->first_rec->base, klen)) \ + || (GVT_CLUE_FIRST_REC_UNRELIABLE == *((short *)gvt->first_rec->base))); \ + klen = MIN(gvt->clue.end, gvt->last_rec->end); \ + assert(klen); \ + assert(0 <= memcmp(gvt->last_rec->base, gvt->clue.base, klen)); \ + if (DIR_ROOT != gvt->root) \ + { /* Not a directory tree => a GVT tree, check that first_rec/last_rec have at least gvname in it */ \ + gvent = &gvt->gvname; \ + if (GVT_CLUE_FIRST_REC_UNRELIABLE != *((short *)gvt->first_rec->base)) \ + { \ + assert((0 == memcmp(gvent->var_name.addr, gvt->first_rec->base, gvent->var_name.len)) \ + && (KEY_DELIMITER == gvt->first_rec->base[gvent->var_name.len])); \ + } \ + if (GVT_CLUE_LAST_REC_MAXKEY != *((short *)gvt->last_rec->base)) \ + { \ + assert((0 == memcmp(gvent->var_name.addr, gvt->last_rec->base, gvent->var_name.len)) \ + && (KEY_DELIMITER == gvt->last_rec->base[gvent->var_name.len])); \ + } \ + } \ +} +#else +#define DEBUG_GVT_CLUE_VALIDATE(GVT) +#endif + +/* Macro used by $ZPREVIOUS to replace a NULL subscript at the end with the maximum possible subscript + * that could exist in the database for this global name. + */ +#define GVZPREVIOUS_APPEND_MAX_SUBS_KEY(GVKEY, GVT) \ +{ \ + int lastsubslen, keysize; \ + unsigned char *ptr; \ + \ + assert(GVT->clue.top || (NULL == GVT->gd_csa)); \ + assert(!GVT->clue.top || (NULL != GVT->gd_csa) && (GVT->gd_csa == cs_addrs)); \ + /* keysize can be obtained from GVT->clue.top in case of GT.M. \ + * But for GT.CM client, clue will be uninitialized. So we would need to \ + * compute keysize from gv_cur_region->max_key_size. Since this is true for \ + * GT.M as well, we use the same approach for both to avoid an if check and a \ + * break in the pipeline. \ + */ \ + keysize = DBKEYSIZE(gv_cur_region->max_key_size); \ + assert(!GVT->clue.top || (keysize == GVT->clue.top)); \ + lastsubslen = keysize - GVKEY->prev - 2; \ + if ((0 < lastsubslen) && (GVKEY->top >= keysize) && (GVKEY->end > GVKEY->prev)) \ + { \ + ptr = &GVKEY->base[GVKEY->prev]; \ + memset(ptr, STR_SUB_MAXVAL, lastsubslen); \ + ptr += lastsubslen; \ + *ptr++ = KEY_DELIMITER; /* terminator for last subscript */ \ + *ptr = KEY_DELIMITER; /* terminator for entire key */ \ + GVKEY->end = GVKEY->prev + lastsubslen + 1; \ + assert(GVKEY->end == (ptr - &GVKEY->base[0])); \ + } else \ + GTMASSERT; \ + if (NULL != gv_target->gd_csa) \ + DBG_CHECK_GVTARGET_INTEGRITY(GVT); \ +} + +/* Bit masks for the update_trans & si->update_trans variables */ +#define UPDTRNS_DB_UPDATED_MASK (1 << 0) /* 1 if this region was updated by this non-TP/TP transaction */ +#define UPDTRNS_JNL_LOGICAL_MASK (1 << 1) /* 1 if logical jnl record was written in this region's + * journal file by this TP transaction. Maintained only for TP. + */ +#define UPDTRNS_JNL_REPLICATED_MASK (1 << 2) /* 1 if there is at least one logical jnl record written in this + * region's journal file by this TP transaction that needs to be + * replicated across. 0 if all updates done to this region was + * inside of a trigger. Maintained only for TP. + */ +#define UPDTRNS_TCOMMIT_STARTED_MASK (1 << 3) /* 1 if non-TP or TP transaction is beyond the point of rolling + * back by "t_commit_cleanup" and can only be rolled forward by + * "secshr_db_clnup". + */ +#define UPDTRNS_ZTRIGGER_MASK (1 << 4) /* 1 if ZTRIGGER command was done in this transaction. This allows + * the transaction to be committed even if it had no updates. + * Maintained only for TP. + */ +#define UPDTRNS_VALID_MASK (UPDTRNS_DB_UPDATED_MASK | UPDTRNS_JNL_LOGICAL_MASK \ + | UPDTRNS_JNL_REPLICATED_MASK | UPDTRNS_TCOMMIT_STARTED_MASK \ + | UPDTRNS_ZTRIGGER_MASK) + +/* The enum codes below correspond to code-paths that can increment the database curr_tn + * without having a logical update. Journaling currently needs to know all such code-paths */ +typedef enum +{ + inctn_invalid_op = 0, /* 0 : */ + /* the following opcodes do NOT populate the global variable "inctn_detail" */ + inctn_gvcstput_extra_blk_split, /* 1 : */ + inctn_mu_reorg, /* 2 : */ + inctn_wcs_recover, /* 3 : */ + /* the following opcodes populate "inctn_detail.blks2upgrd_struct" */ + inctn_gdsfilext_gtm, /* 4 : */ + inctn_gdsfilext_mu_reorg, /* 5 : */ + inctn_db_format_change, /* 6 : written when cs_data->desired_db_format changes */ + /* the following opcodes populate "inctn_detail.blknum_struct" */ + inctn_bmp_mark_free_gtm, /* 7 : */ + inctn_bmp_mark_free_mu_reorg, /* 8 : */ + inctn_blkmarkfree, /* 9 : a RECYCLED block being marked free by MUPIP REORG UPGRADE/DOWNGRADE */ + inctn_blkupgrd, /* 10 : written whenever a GDS block is upgraded by MUPIP REORG UPGRADE if + * a) SAFEJNL is specified OR + * b) NOSAFEJNL is specified and the block is not undergoing a fmt change + */ + inctn_blkupgrd_fmtchng, /* 11 : written whenever a GDS block is upgraded by MUPIP REORG UPGRADE -NOSAFEJNL + * and if that block is undergoing a fmt change i.e. (V4 -> V5) OR (V5 -> V4). + * This differentiation (inctn_blkupgrd vs inctn_blkupgrd_fmtch) is necessary + * because in the latter case we will not be writing a PBLK record and hence + * have no record otherwise of a block fmt change if it occurs (note that a + * PBLK journal record's "ondsk_blkver" field normally helps recovery + * determine if a fmt change occurred or not). + */ + inctn_blkdwngrd, /* 12 : similar to inctn_blkupgrd except that this is for DOWNGRADE */ + inctn_blkdwngrd_fmtchng, /* 13 : similar to inctn_blkupgrd_fmtchng except that this is for DOWNGRADE */ + /* the following opcodes do NOT populate the global variable "inctn_detail" */ + inctn_opcode_total /* 15 : MAX. All additions of inctn opcodes should be done BEFORE this line */ +} inctn_opcode_t; + +/* macros to check curr_tn */ +#define MAX_TN_V4 ((trans_num)(MAXUINT4 - TN_HEADROOM_V4)) +#define MAX_TN_V5 (MAXUINT8 - TN_HEADROOM_V5) +#define TN_HEADROOM_V4 (2 * MAXTOTALBLKS_V4) +#define TN_HEADROOM_V5 (2 * MAXTOTALBLKS_V5) +#define HEADROOM_FACTOR 4 + +/* the following macro checks that curr_tn < max_tn_warn <= max_tn. + * if not, it adjusts max_tn_warn accordingly to ensure the above. + * if not possible, it issues TNTOOLARGE error. + */ +#define CHECK_TN(CSA, CSD, TN) \ +{ \ + assert((CSA)->hdr == (CSD)); \ + assert((TN) <= (CSD)->max_tn_warn); \ + assert((CSD)->max_tn_warn <= (CSD)->max_tn); \ + assert((CSA)->now_crit); /* Must be crit to mess with stuff */ \ + if ((TN) >= (CSD)->max_tn_warn) \ + { \ + trans_num trans_left; \ + \ + if ((CSA)->hdr->max_tn <= (TN)) \ + { \ + rts_error(VARLSTCNT(5) ERR_TNTOOLARGE, 3, DB_LEN_STR((CSA)->region), &(CSA)->hdr->max_tn); \ + assert(FALSE); /* should not come here */ \ + } \ + assert((CSD)->max_tn > (TN)); \ + trans_left = (CSD)->max_tn - (TN); \ + send_msg(VARLSTCNT(6) ERR_TNWARN, 4, DB_LEN_STR((CSA)->region), &trans_left, &(CSD)->max_tn); \ + (CSD)->max_tn_warn = (TN) + 1 + ((trans_left - 1) >> 1); \ + assert((TN) < (CSD)->max_tn_warn); \ + assert((CSD)->max_tn_warn <= (CSD)->max_tn); \ + } \ +} + +#define INCREMENT_CURR_TN(CSD) \ +{ \ + assert((CSD)->trans_hist.curr_tn < (CSD)->max_tn_warn); \ + assert((CSD)->max_tn_warn <= (CSD)->max_tn); \ + (CSD)->trans_hist.curr_tn++; \ + assert((CSD)->trans_hist.curr_tn == (CSD)->trans_hist.early_tn); \ +} + +#define SET_TN_WARN(CSD, ret_warn_tn) \ +{ \ + trans_num headroom; \ + \ + headroom = (gtm_uint64_t)(GDSV4 == (CSD)->desired_db_format ? TN_HEADROOM_V4 : TN_HEADROOM_V5); \ + headroom *= HEADROOM_FACTOR; \ + (ret_warn_tn) = (CSD)->trans_hist.curr_tn; \ + if ((headroom < (CSD)->max_tn) && ((ret_warn_tn) < ((CSD)->max_tn - headroom))) \ + (ret_warn_tn) = (CSD)->max_tn - headroom; \ + assert((CSD)->trans_hist.curr_tn <= (ret_warn_tn)); \ + assert((ret_warn_tn) <= (CSD)->max_tn); \ +} + +#define HIST_TERMINATOR 0 +#define HIST_SIZE(h) ( (SIZEOF(int4) * 2) + (SIZEOF(srch_blk_status) * ((h).depth + 1)) ) + +/* Start of lock space in a bg file, therefore also doubles as overhead size for header, bt and wc queues F = # of wc blocks */ +#define LOCK_BLOCK(X) (DIVIDE_ROUND_UP(SIZEOF_FILE_HDR(X) + BT_SIZE(X), DISK_BLOCK_SIZE)) +#define LOCK_BLOCK_SIZE(X) (DIVIDE_ROUND_UP(SIZEOF_FILE_HDR(X) + BT_SIZE(X), OS_PAGE_SIZE)) +#define LOCK_SPACE_SIZE(X) (ROUND_UP2(((sgmnt_data_ptr_t)X)->lock_space_size, OS_PAGE_SIZE)) +/* In case of an encrypted database, we maintain both encrypted and decrypted versions of the block in shared memory + * in parallel arrays of global buffers hence the doubling calculation below. Although this doubles the shared memory + * size requirements for encrypted databases (when compared to the same unencrypted database), it helps in other ways. + * By ensuring that this encrypted global buffer array contents are identical to the encrypted on-disk block contents + * of database blocks at all times, we can avoid allocating process private memory to store encrypted before-images + * (to write to a journal file). Instead processes can use the encrypted global buffer directly for this purpose. + * In user environments where process-private memory is very costly compared to database shared memory (e.g. where + * 1000s of GT.M processes run against the same database) the above approach is expected to use lesser total memory. + */ +#define CACHE_CONTROL_SIZE(X) \ + (ROUND_UP((ROUND_UP((X->bt_buckets + X->n_bts) * SIZEOF(cache_rec) + SIZEOF(cache_que_heads), OS_PAGE_SIZE) \ + + ((gtm_uint64_t)X->n_bts * X->blk_size * (X->is_encrypted ? 2 : 1))), OS_PAGE_SIZE)) +#define MMBLK_CONTROL_SIZE(X) (ROUND_UP((((sgmnt_data_ptr_t)X)->bt_buckets + ((sgmnt_data_ptr_t)X)->n_bts) * SIZEOF(mmblk_rec) \ + + SIZEOF(mmblk_que_heads), OS_PAGE_SIZE)) + +OS_PAGE_SIZE_DECLARE + +#ifdef VMS +#define MAX_NAME_LEN 31 /* Size of a repl resource name on vvms */ +#endif +/* structure to identify a given system wide shared section to be ours (replic section) */ +typedef struct +{ + unsigned char label[GDS_LABEL_SZ]; + char pool_type; + char now_running[MAX_REL_NAME]; +#ifdef VMS + char repl_pool_key[MAX_NAME_LEN + 1]; /* resource name for the section */ + char filler[7]; /* makes sure the size of the structure is a multiple of 8 */ + char gtmgbldir[MAX_FN_LEN + 1]; /* Identify which instance of this shared pool corresponds to */ +#else + int4 repl_pool_key_filler; /* makes sure the size of the structure is a multiple of 8 */ + char instfilename[MAX_FN_LEN + 1]; /* Identify which instance file this shared pool corresponds to */ +#endif +} replpool_identifier; + +#if defined(__osf__) && defined(__alpha) +# pragma pointer_size(save) +# pragma pointer_size(long) +#endif + +typedef replpool_identifier *replpool_id_ptr_t; + +#if defined(__osf__) && defined(__alpha) +# pragma pointer_size(restore) +#endif + +/* Macro to increment the count of processes that are doing two phase commit. + * This is invoked just BEFORE starting phase1 of the commit. + */ +#define INCR_WCS_PHASE2_COMMIT_PIDCNT(csa, cnl) \ +{ \ + assert(!csa->wcs_pidcnt_incremented); \ + INCR_CNT(&cnl->wcs_phase2_commit_pidcnt, &cnl->wc_var_lock); \ + csa->wcs_pidcnt_incremented = TRUE; \ +} + +/* Macro to decrement the count of processes that are doing two phase commit. + * This is invoked just AFTER finishing phase2 of the commit. + */ +#define DECR_WCS_PHASE2_COMMIT_PIDCNT(csa, cnl) \ +{ \ + assert(csa->wcs_pidcnt_incremented); \ + csa->wcs_pidcnt_incremented = FALSE; \ + DECR_CNT(&cnl->wcs_phase2_commit_pidcnt, &cnl->wc_var_lock); \ +} + +/* The CAREFUL_DECR_WCS_PHASE2_COMMIT_PIDCNT macro is the same as the DECR_WCS_PHASE2_COMMIT_PIDCNT macro + * except that it uses CAREFUL_DECR_CNT instead of DECR_CNT. This does alignment checks and is needed by + * secshr_db_clnup as it runs in kernel mode in VMS. The two macros should be maintained in parallel. + */ +#define CAREFUL_DECR_WCS_PHASE2_COMMIT_PIDCNT(csa, cnl) \ +{ \ + assert(csa->wcs_pidcnt_incremented); \ + csa->wcs_pidcnt_incremented = FALSE; \ + CAREFUL_DECR_CNT(&cnl->wcs_phase2_commit_pidcnt, &cnl->wc_var_lock); \ +} + +#ifdef UNIX +/* Insert the process_id into the list of process ids actively doing a kill */ +#define INSERT_KIP_PID(local_csa) \ +{ \ + int idx; \ + uint4 pid; \ + uint4 *kip_pid_arr_ptr; \ + GBLREF uint4 process_id; \ + \ + kip_pid_arr_ptr = local_csa->nl->kip_pid_array; \ + assert(local_csa->now_crit); \ + for (idx = 0; idx < MAX_KIP_PID_SLOTS; idx++) \ + { \ + pid = kip_pid_arr_ptr[idx]; \ + if ((0 == pid) || (process_id == pid)) \ + { \ + kip_pid_arr_ptr[idx] = process_id; \ + break; \ + } \ + } \ +} +/* Remove the process_id from the list of process ids actively doing a kill */ +#define REMOVE_KIP_PID(local_csa) \ +{ \ + int idx; \ + uint4 *kip_pid_arr_ptr; \ + GBLREF uint4 process_id; \ + \ + kip_pid_arr_ptr = local_csa->nl->kip_pid_array; \ + for (idx = 0; idx < MAX_KIP_PID_SLOTS; idx++) \ + { \ + if (process_id == kip_pid_arr_ptr[idx]) \ + { \ + kip_pid_arr_ptr[idx] = 0; \ + break; \ + } \ + } \ +} +#else +#define INSERT_KIP_PID(local_csa) +#define REMOVE_KIP_PID(local_csa) +#endif + +#define DECR_KIP(CSD, CSA, KIP_CSA) \ +{ \ + sgmnt_data_ptr_t local_csd; \ + sgmnt_addrs *local_csa; \ + \ + /* Instead of using CSA and CSD directly in DECR_CNT, assign it to \ + * local variables as the caller can potentially pass the global \ + * kip_csa as the second argument(which also happens to be the \ + * the third argument which will be reset to NULL below) thereby \ + * leading to SEG faults in the calls to DECR_CNT. Similar \ + * modifications are in INCR_KIP and their CAREFUL counterparts */ \ + local_csd = CSD; \ + local_csa = CSA; \ + assert(NULL != KIP_CSA); \ + KIP_CSA = NULL; \ + DECR_CNT(&local_csd->kill_in_prog, &local_csa->nl->wc_var_lock); \ + REMOVE_KIP_PID(local_csa); \ +} +/* Note that the INCR_KIP and CAREFUL_INCR_KIP macros should be maintained in parallel */ +#define INCR_KIP(CSD, CSA, KIP_CSA) \ +{ \ + sgmnt_data_ptr_t local_csd; \ + sgmnt_addrs *local_csa; \ + \ + local_csd = CSD; \ + local_csa = CSA; \ + assert(NULL == KIP_CSA); \ + INCR_CNT(&local_csd->kill_in_prog, &local_csa->nl->wc_var_lock); \ + INSERT_KIP_PID(local_csa); \ + KIP_CSA = CSA; \ +} +/* The CAREFUL_INCR_KIP macro is the same as the INCR_KIP macro except that it uses CAREFUL_INCR_CNT instead of INCR_CNT. + * This does alignment checks and is needed by secshr_db_clnup as it runs in kernel mode in VMS. + * The INCR_KIP and CAREFUL_INCR_KIP macros should be maintained in parallel. + */ +#define CAREFUL_INCR_KIP(CSD, CSA, KIP_CSA) \ +{ \ + sgmnt_data_ptr_t local_csd; \ + sgmnt_addrs *local_csa; \ + \ + local_csd = CSD; \ + local_csa = CSA; \ + assert(NULL == KIP_CSA); \ + CAREFUL_INCR_CNT(&local_csd->kill_in_prog, &local_csa->nl->wc_var_lock); \ + INSERT_KIP_PID(local_csa); \ + KIP_CSA = CSA; \ +} +#define CAREFUL_DECR_KIP(CSD, CSA, KIP_CSA) \ +{ \ + sgmnt_data_ptr_t local_csd; \ + sgmnt_addrs *local_csa; \ + \ + local_csd = CSD; \ + local_csa = CSA; \ + assert(NULL != KIP_CSA); \ + KIP_CSA = NULL; \ + CAREFUL_DECR_CNT(&local_csd->kill_in_prog, &local_csa->nl->wc_var_lock); \ + REMOVE_KIP_PID(local_csa); \ +} +/* Since abandoned_kills counter is only incremented in secshr_db_clnup it does not have its equivalent DECR_ABANDONED_KILLS */ +#define CAREFUL_INCR_ABANDONED_KILLS(CSD, CSA) \ +{ \ + CAREFUL_INCR_CNT(&CSD->abandoned_kills, &CSA->nl->wc_var_lock); \ +} + +#define INCR_INHIBIT_KILLS(CNL) \ +{ \ + INCR_CNT(&CNL->inhibit_kills, &CNL->wc_var_lock); \ +} + +#define DECR_INHIBIT_KILLS(CNL) \ +{ \ + if (0 < CNL->inhibit_kills) \ + DECR_CNT(&CNL->inhibit_kills, &CNL->wc_var_lock); \ +} + +/* Commands like MUPIP BACKUP, MUPIP INTEG -REG or MUPIP FREEZE wait for kills-in-prog flag to become zero. + * While these process wait for ongoing block-freeing KILLs (or reorg actions that free up blocks) to complete, + * new block-freeing KILLs (or reorg actions that free up blocks) are deferred using inhibit_kills counter. + * New block-freeing KILLs/REORG will wait for a maximum period of 1 minute until inhibit_kills counter is 0. + * In case of timeout, they will proceed after resetting the inhibit_kills to 0. The reset is done in case + * the inhibit_kills was orphaned (i.e. the process that set it got killed before it got a chance to reset). + */ +#define WAIT_ON_INHIBIT_KILLS(CNL, MAXKILLINHIBITWAIT) \ +{ \ + int4 sleep_counter; \ + \ + GBLREF boolean_t need_kip_incr; \ + GBLREF uint4 dollar_tlevel; \ + \ + assert(dollar_tlevel || need_kip_incr); \ + for (sleep_counter = 1; (0 < CNL->inhibit_kills); ++sleep_counter) \ + { \ + if (MAXKILLINHIBITWAIT <= sleep_counter) \ + { \ + CNL->inhibit_kills = 0; \ + SHM_WRITE_MEMORY_BARRIER; \ + break; \ + } \ + wcs_sleep(sleep_counter); \ + } \ +} + +/* Wait for a region freeze to be turned off. Note that we dont hold CRIT at this point. Ideally we would have + * READ memory barriers between each iterations of sleep to try and get the latest value of the "freeze" field from + * the concurrently updated database shared memory. But since region-freeze is a perceivably rare event, we choose + * not to do the memory barriers. The consequence of this decision is that it might take more iterations for us to + * see updates to the "freeze" field than it would have if we did the memory barrier each iteration. But since we + * dont hold crit at this point AND since freeze is a rare event, we dont mind the extra wait. + */ +#define MAXHARDCRITS 31 + +#define WAIT_FOR_REGION_TO_UNFREEZE(CSA, CSD) \ +{ \ + int lcnt1; \ + \ + assert(CSA->hdr == CSD); \ + assert(!CSA->now_crit); \ + for (lcnt1 = 1; ; lcnt1++) \ + { \ + if (!CSD->freeze) \ + break; \ + if (MAXHARDCRITS < lcnt1) \ + wcs_backoff(lcnt1); \ + } \ +} + +#define GRAB_UNFROZEN_CRIT(reg, csa, csd) \ +{ \ + int lcnt; \ + \ + assert(&FILE_INFO(reg)->s_addrs == csa && csa->hdr == csd); \ + assert(csa->now_crit); \ + for (lcnt = 0; ; lcnt++) \ + { \ + if (!csd->freeze) \ + break; \ + rel_crit(reg); \ + WAIT_FOR_REGION_TO_UNFREEZE(csa, csd); \ + grab_crit(reg); \ + } \ + assert(!csd->freeze && csa->now_crit); \ +} +/* remove "csa" from list of open regions (cs_addrs_list) */ +#define REMOVE_CSA_FROM_CSADDRSLIST(CSA) \ +{ \ + GBLREF sgmnt_addrs *cs_addrs_list; \ + \ + sgmnt_addrs *tmpcsa, *prevcsa; \ + \ + assert(NULL != CSA); \ + assert(NULL == CSA->nl); \ + prevcsa = NULL; \ + for (tmpcsa = cs_addrs_list; NULL != tmpcsa; tmpcsa = tmpcsa->next_csa) \ + { \ + if (CSA == tmpcsa) \ + break; \ + prevcsa = tmpcsa; \ + } \ + /* tmpcsa could not be equal to CSA in case CSA was never added to this list \ + * (possible in case of errors during gvcst_init). In dbg, the only case we \ + * know of this is if an external signal causes exit processing before db_init \ + * completes. Assert accordingly. \ + */ \ + assert((tmpcsa == CSA) || process_exiting); \ + if (tmpcsa == CSA) \ + { \ + if (NULL != prevcsa) \ + prevcsa->next_csa = CSA->next_csa; \ + else \ + cs_addrs_list = CSA->next_csa; \ + } \ +} + +#define INVALID_SEMID -1 +#define INVALID_SHMID -1L + +#if defined(UNIX) +#define DB_FSYNC(reg, udi, csa, db_fsync_in_prog, save_errno) \ +{ \ + BG_TRACE_PRO_ANY(csa, n_db_fsyncs); \ + if (csa->now_crit) \ + BG_TRACE_PRO_ANY(csa, n_db_fsyncs_in_crit); \ + db_fsync_in_prog++; \ + save_errno = 0; \ + if (-1 == fsync(udi->fd)) \ + { \ + db_fsync_in_prog--; \ + save_errno = errno; \ + } \ + db_fsync_in_prog--; \ + assert(0 <= db_fsync_in_prog); \ +} + +#define STANDALONE(x) mu_rndwn_file(x, TRUE) +#define DBFILOP_FAIL_MSG(status, msg) gtm_putmsg(VARLSTCNT(5) msg, 2, DB_LEN_STR(gv_cur_region), status); +#elif defined(VMS) +#define STANDALONE(x) mu_rndwn_file(TRUE) /* gv_cur_region needs to be equal to "x" */ +#define DBFILOP_FAIL_MSG(status, msg) gtm_putmsg(VARLSTCNT(6) msg, 2, DB_LEN_STR(gv_cur_region), status, \ + FILE_INFO(gv_cur_region)->fab->fab$l_stv); +#else +#error unsupported platform +#endif + +#define CR_NOT_ALIGNED(cr, cr_base) (!IS_PTR_ALIGNED((cr), (cr_base), SIZEOF(cache_rec))) +#define CR_NOT_IN_RANGE(cr, cr_lo, cr_hi) (!IS_PTR_IN_RANGE((cr), (cr_lo), (cr_hi))) +#define MBR_NOT_ALIGNED(mbr, mbr_base) (!IS_PTR_ALIGNED((mbr), (mbr_base), SIZEOF(mmblk_rec))) +#define MBR_NOT_IN_RANGE(mbr, mbr_lo, mbr_hi) (!IS_PTR_IN_RANGE((mbr), (mbr_lo), (mbr_hi))) + +/* Examine that cr->buffaddr is indeed what it should be. If not, this macro fixes its value by + * recomputing from the cache_array. + * NOTE: We rely on bt_buckets, n_bts and blk_size fields of file header being correct/not corrupt */ +#define CR_BUFFER_CHECK(reg, csa, csd, cr) \ +{ \ + cache_rec_ptr_t cr_lo, cr_hi; \ + \ + cr_lo = (cache_rec_ptr_t)csa->acc_meth.bg.cache_state->cache_array + csd->bt_buckets; \ + cr_hi = cr_lo + csd->n_bts; \ + CR_BUFFER_CHECK1(reg, csa, csd, cr, cr_lo, cr_hi); \ +} + +/* A more efficient macro than CR_BUFFER_CHECK when we have cr_lo and cr_hi already available */ +#define CR_BUFFER_CHECK1(reg, csa, csd, cr, cr_lo, cr_hi) \ +{ \ + INTPTR_T bp, bp_lo, bp_top, cr_top; \ + \ + cr_top = GDS_ANY_ABS2REL(csa, cr_hi); \ + bp_lo = ROUND_UP(cr_top, OS_PAGE_SIZE); \ + bp = bp_lo + ((cr) - (cr_lo)) * csd->blk_size; \ + if (bp != cr->buffaddr) \ + { \ + send_msg(VARLSTCNT(13) ERR_DBCRERR, 11, DB_LEN_STR(reg), cr, cr->blk, \ + RTS_ERROR_TEXT("cr->buffaddr"), cr->buffaddr, bp, CALLFROM); \ + cr->buffaddr = bp; \ + } \ + DEBUG_ONLY(bp_top = bp_lo + (gtm_uint64_t)csd->n_bts * csd->blk_size;) \ + assert(IS_PTR_IN_RANGE(bp, bp_lo, bp_top) && IS_PTR_ALIGNED(bp, bp_lo, csd->blk_size)); \ +} + +#define DB_INFO UNIX_ONLY(unix_db_info)VMS_ONLY(vms_gds_info) + +#define FILE_CNTL_INIT_IF_NULL(SEG) \ +{ \ + file_control *lcl_fc; \ + \ + lcl_fc = SEG->file_cntl; \ + if (NULL == lcl_fc) \ + { \ + MALLOC_INIT(lcl_fc, SIZEOF(file_control)); \ + SEG->file_cntl = lcl_fc; \ + } \ + if (NULL == lcl_fc->file_info) \ + { \ + MALLOC_INIT(lcl_fc->file_info, SIZEOF(DB_INFO)); \ + SEG->file_cntl->file_info = lcl_fc->file_info; \ + } \ +} +#define FILE_CNTL_INIT(SEG) \ +{ \ + file_control *lcl_fc; \ + \ + MALLOC_INIT(lcl_fc, SIZEOF(file_control)); \ + MALLOC_INIT(lcl_fc->file_info, SIZEOF(DB_INFO)); \ + SEG->file_cntl = lcl_fc; \ +} + +#define IS_DOLLAR_INCREMENT ((is_dollar_incr) && (ERR_GVPUTFAIL == t_err)) + +#define AVG_BLKS_PER_100_GBL 200 +#define PRE_READ_TRIGGER_FACTOR 50 +#define UPD_RESERVED_AREA 50 +#define UPD_WRITER_TRIGGER_FACTOR 33 + +#define SNAPSHOT_ALONE_IN_PROG(CSA) (CSA->snapshot_in_prog && !CSA->backup_in_prog && !(JNL_ENABLED(CSA) && CSA->jnl_before_image)) + +#ifdef GTM_SNAPSHOT +# define SNAPSHOTS_IN_PROG(X) ((X)->snapshot_in_prog) +/* Creates a new snapshot context. Called by GT.M (or utilities like update process, MUPIP LOAD which uses + * GT.M runtime. As a side effect sets csa->snapshot_in_prog to TRUE if the context creation went fine. + */ +# define SS_INIT_IF_NEEDED(CSA, CNL) \ +{ \ + int ss_shmcycle; \ + boolean_t status; \ + snapshot_context_ptr_t lcl_ss_ctx; \ + \ + lcl_ss_ctx = SS_CTX_CAST(CSA->ss_ctx); \ + assert(NULL != lcl_ss_ctx); \ + ss_shmcycle = CNL->ss_shmcycle; \ + CSA->snapshot_in_prog = TRUE; \ + assert(lcl_ss_ctx->ss_shmcycle <= ss_shmcycle); \ + if (lcl_ss_ctx->ss_shmcycle != ss_shmcycle) \ + { /* Process' view of snapshot is stale. Create/Update snapshot context */ \ + status = ss_create_context(lcl_ss_ctx, ss_shmcycle); \ + if (!status) \ + { /* snapshot context creation failed. Reset private copy of snapshot_in_prog so that we don't \ + * read the before images in t_end or op_tcommit */ \ + CSA->snapshot_in_prog = FALSE; \ + } \ + assert(!status || (SNAPSHOT_INIT_DONE == lcl_ss_ctx->cur_state)); \ + assert(status || (SHADOW_FIL_OPEN_FAIL == lcl_ss_ctx->cur_state) \ + || (SNAPSHOT_SHM_ATTACH_FAIL == lcl_ss_ctx->cur_state) \ + || (SNAPSHOT_NOT_INITED == lcl_ss_ctx->cur_state)); \ + } else if ((SHADOW_FIL_OPEN_FAIL == lcl_ss_ctx->cur_state) \ + || (SNAPSHOT_SHM_ATTACH_FAIL == lcl_ss_ctx->cur_state)) \ + { /* Previous attempt at snapshot context creation failed (say, snapshot file open failed) and the error \ + * has been reported in the shared memory. However, the snapshot is not yet complete. So, set \ + * snapshot_in_prog to FALSE since the ongoing snapshot is not valid (as indicated by us in the prior \ + * transaction/retry inside crit) \ + * Note that we will be doing this 'if' check unconditionally until MUPIP INTEG detects the error in \ + * shared memory which can be avoided by making GT.M itself set CNL->snapshot_in_prog to FALSE when it \ + * detects inside crit that snapshot initialization failed for this process and hence the ongoing \ + * snapshot is no longer valid. This way we don't wait for MUPIP INTEG to detect and terminate the \ + * snapshots \ + */ \ + CSA->snapshot_in_prog = FALSE; \ + } \ +} + +#ifdef DEBUG +# define DBG_ENSURE_SNAPSHOT_GOOD_TO_GO(LCL_SS_CTX, CNL) \ +{ \ + shm_snapshot_ptr_t ss_shm_ptr; \ + \ + assert(SNAPSHOTS_IN_PROG(CNL)); \ + assert(NULL != LCL_SS_CTX); \ + ss_shm_ptr = LCL_SS_CTX->ss_shm_ptr; \ + assert(NULL != ss_shm_ptr); \ + assert(SNAPSHOT_INIT_DONE == LCL_SS_CTX->cur_state); \ + assert(0 == LCL_SS_CTX->failure_errno); \ + assert((-1 != CNL->ss_shmid) && \ + (LCL_SS_CTX->attach_shmid == CNL->ss_shmid)); \ + assert(NULL != LCL_SS_CTX->start_shmaddr); \ + assert(0 == strcmp(LCL_SS_CTX->shadow_file, ss_shm_ptr->ss_info.shadow_file)); \ + assert(-1 != LCL_SS_CTX->shdw_fd); \ +} +#else +# define DBG_ENSURE_SNAPSHOT_GOOD_TO_GO(LCL_SS_CTX, CNL) +#endif +/* Destroy an existing snapshot. Called by GT.M (or utilities like update process, MUPIP LOAD which uses + * GT.M runtime. Assumes that csa->snapshot_in_prog is TRUE and as a side effect sets csa->snapshot_in_prog + * to FALSE if the context is destroyed + */ +# define SS_RELEASE_IF_NEEDED(CSA, CNL) \ +{ \ + int ss_shmcycle; \ + snapshot_context_ptr_t lcl_ss_ctx; \ + \ + lcl_ss_ctx = SS_CTX_CAST(CSA->ss_ctx); \ + assert(SNAPSHOTS_IN_PROG(CSA) && (NULL != lcl_ss_ctx)); \ + ss_shmcycle = CNL->ss_shmcycle; \ + if (!SNAPSHOTS_IN_PROG(cnl) || (lcl_ss_ctx->ss_shmcycle != ss_shmcycle)) \ + { \ + ss_destroy_context(lcl_ss_ctx); \ + CSA->snapshot_in_prog = FALSE; \ + } \ +} + +/* No need to write before-image in case the block is FREE. In case the database had never been fully upgraded from V4 to V5 format + * (after the MUPIP UPGRADE), all RECYCLED blocks can basically be considered FREE (i.e. no need to write before-images since + * backward journal recovery will never be expected to take the database to a point BEFORE the mupip upgrade). + * Logic to check if before image of a given block has to be read or not are slightly complicated if snapshots are present + * For snapshots, we might want to read the before images of FREE blocks. Also, if the block that we are reading + * is already before imaged by some other GT.M process then we do not needed to read the before image of such a block. But, such + * a condition is applicable ONLY if snapshots alone are in progress as we might want the same block for BACKUP if it is in + * progress. + * Note: The below condition, to before image FREE blocks, is needed only if INTEG is the snapshot initiator. When we add + * bitmasks or some alternate mechanism to optionalize the before image'ing of FREE blocks, this condition must be tweaked + * accordingly. For now, INTEG is the only snapshot initiator. + */ +# define BEFORE_IMAGE_NEEDED(read_before_image, CS, csa, csd, blk_no, retval) \ +{ \ + retval = (read_before_image && csd->db_got_to_v5_once && (!CS->was_free || SNAPSHOTS_IN_PROG(csa))); \ + retval = retval && (!SNAPSHOT_ALONE_IN_PROG(csa) || !ss_chk_shdw_bitmap(csa, SS_CTX_CAST(csa->ss_ctx), blk_no));\ +} + +# define CHK_AND_UPDATE_SNAPSHOT_STATE_IF_NEEDED(CSA, CNL, SS_NEED_TO_RESTART) \ +{ \ + GBLREF uint4 process_id; \ + \ + uint4 lcl_failure_errno; \ + ss_proc_status cur_state; \ + shm_snapshot_ptr_t ss_shm_ptr; \ + snapshot_context_ptr_t lcl_ss_ctx; \ + boolean_t csa_snapshot_in_prog, cnl_snapshot_in_prog; \ + \ + assert(CSA->now_crit); \ + csa_snapshot_in_prog = SNAPSHOTS_IN_PROG(CSA); \ + cnl_snapshot_in_prog = SNAPSHOTS_IN_PROG(CNL); \ + if (csa_snapshot_in_prog || cnl_snapshot_in_prog) \ + { \ + lcl_ss_ctx = SS_CTX_CAST(CSA->ss_ctx); \ + ss_shm_ptr = (shm_snapshot_ptr_t)(SS_GETSTARTPTR(CSA)); \ + assert(lcl_ss_ctx->ss_shmcycle <= CNL->ss_shmcycle); \ + if (!cnl_snapshot_in_prog || ss_shm_ptr->failure_errno) \ + { /* No on going snapshots or on going snapshot is invalid. Even if we encountered error during snapshot \ + * context creation outside crit, we ignore it as the snapshot is no more active/valid. \ + */ \ + CSA->snapshot_in_prog = FALSE; \ + } else if (lcl_ss_ctx->ss_shmcycle == CNL->ss_shmcycle) \ + { /* Neither new snapshots started nor existing ones completed. However, it's possible that we might have \ + * encountered error during snapshot context creation outside crit. If the values noted outside crit \ + * matches with the global values, then the error is genuine. If not, then we might have done operations\ + * (shm attach and file open) when things in the shared memory were in flux in which case we need to \ + * restart \ + */ \ + lcl_failure_errno = lcl_ss_ctx->failure_errno; \ + assert(!ss_shm_ptr->failure_errno); \ + SS_NEED_TO_RESTART = FALSE; \ + cur_state = lcl_ss_ctx->cur_state; \ + switch(cur_state) \ + { \ + case SNAPSHOT_INIT_DONE: \ + /* Most common case. Ensure the local values of snapshot context matches with the \ + * values stored in shared memory */ \ + assert(csa_snapshot_in_prog); \ + DBG_ENSURE_SNAPSHOT_GOOD_TO_GO(lcl_ss_ctx, CNL); \ + break; \ + case SNAPSHOT_SHM_ATTACH_FAIL: \ + assert(0 != lcl_failure_errno); \ + assert(FALSE == CSA->snapshot_in_prog); \ + if (lcl_ss_ctx->nl_shmid == CNL->ss_shmid) \ + { /* Error encountered outside crit is genuine. Indicate MUPIP INTEG that the \ + * snapshot is no more valid \ + */ \ + send_msg(VARLSTCNT(4) ERR_SSATTACHSHM, 1, lcl_ss_ctx->nl_shmid, \ + lcl_failure_errno); \ + ss_shm_ptr->failure_errno = lcl_failure_errno; \ + ss_shm_ptr->failed_pid = process_id; \ + } else /* snapshot context creation done while things were in flux */ \ + SS_NEED_TO_RESTART = TRUE; \ + break; \ + case SHADOW_FIL_OPEN_FAIL: \ + assert(0 != lcl_failure_errno); \ + assert(FALSE == CSA->snapshot_in_prog); \ + if (0 == STRCMP(lcl_ss_ctx->shadow_file, ss_shm_ptr->ss_info.shadow_file)) \ + { /* Error encountered outside crit is genuine. Indicate MUPIP INTEG that the \ + * snapshot is no more valid \ + */ \ + send_msg(VARLSTCNT(7) ERR_SSFILOPERR, 4, LEN_AND_LIT("open"), \ + LEN_AND_STR(lcl_ss_ctx->shadow_file), lcl_failure_errno); \ + ss_shm_ptr->failure_errno = lcl_failure_errno; \ + ss_shm_ptr->failed_pid = process_id; \ + } else /* snapshot context creation done while things were in flux */ \ + SS_NEED_TO_RESTART = TRUE; \ + break; \ + default: \ + assert(FALSE); \ + } \ + } else /* A new snapshot has started after we grabbed crit in t_end. Need to restart */ \ + SS_NEED_TO_RESTART = TRUE; \ + } \ +} + +# define WRITE_SNAPSHOT_BLOCK(csa, cr, mm_blk_ptr, blkid, lcl_ss_ctx) \ +{ \ + assert(NULL != lcl_ss_ctx); \ + /* write this block to the snapshot shadow file only if this was not already \ + * before imaged. If error happens while writing to the snapshot file, then \ + * ss_write_block will mark the appropriate error in the shared memory \ + * which INTEG will later query and report accordingly. So, just continue \ + * as if nothing happened. \ + */ \ + if (!ss_chk_shdw_bitmap(csa, lcl_ss_ctx, blkid)) \ + if (!ss_write_block(csa, blkid, cr, mm_blk_ptr, lcl_ss_ctx)) \ + assert(FALSE); \ +} +#else +# define SNAPSHOTS_IN_PROG(X) (FALSE) +# define WRITE_SNAPSHOT_BLOCK(csa, cr, mm_blk_ptr, blkid, lcl_ss_ctx) +# define SS_INIT_IF_NEEDED(CSA, CNL) +# define SS_RELEASE_IF_NEEDED(CSA, CNL) +/* No need to write before-image in case the block is FREE. In case the database had never been fully upgraded from V4 to V5 format + * (after the MUPIP UPGRADE), all RECYCLED blocks can basically be considered FREE (i.e. no need to write before-images since + * backward journal recovery will never be expected to take the database to a point BEFORE the mupip upgrade). + */ +# define BEFORE_IMAGE_NEEDED(read_before_image, CS, csa, csd, blk_no, retval) \ + retval = (read_before_image && csd->db_got_to_v5_once && !CS->was_free); +#endif + +/* Determine if the state of 'backup in progress' has changed since we grabbed crit in t_end.c/tp_tend.c */ +#define CHK_AND_UPDATE_BKUP_STATE_IF_NEEDED(CNL, CSA, NEW_BKUP_STARTED) \ +{ \ + if (CSA->backup_in_prog != (BACKUP_NOT_IN_PROGRESS != CNL->nbb)) \ + { \ + if (!CSA->backup_in_prog) \ + NEW_BKUP_STARTED = TRUE; \ + CSA->backup_in_prog = !CSA->backup_in_prog; \ + } \ +} + +#define BLK_HDR_EMPTY(bp) ((0 == (bp)->bsiz) && (0 == (bp)->tn)) + +typedef enum +{ + REG_FREEZE_SUCCESS, + REG_ALREADY_FROZEN, + REG_HAS_KIP +}freeze_status; + +#define GV_BIND_NAME_ONLY(ADDR, TARG) gv_bind_name(ADDR, TARG) + +#define GV_BIND_NAME_AND_ROOT_SEARCH(ADDR, TARG) \ +{ \ + enum db_acc_method acc_meth; \ + GBLREF gd_region *gv_cur_region; \ + GBLREF gv_namehead *gv_target; \ + \ + GV_BIND_NAME_ONLY(ADDR, TARG); \ + acc_meth = gv_cur_region->dyn.addr->acc_meth; \ + if ((dba_bg == acc_meth) || (dba_mm == acc_meth)) \ + { \ + if ((0 == gv_target->root) || (DIR_ROOT == gv_target->root)) \ + gvcst_root_search(); \ + } \ +} + +void assert_jrec_member_offsets(void); +bt_rec_ptr_t bt_put(gd_region *r, int4 block); +void bt_que_refresh(gd_region *greg); +void bt_init(sgmnt_addrs *cs); +void bt_malloc(sgmnt_addrs *csa); +void bt_refresh(sgmnt_addrs *csa); +void db_common_init(gd_region *reg, sgmnt_addrs *csa, sgmnt_data_ptr_t csd); +void grab_crit(gd_region *reg); +void grab_lock(gd_region *reg); +void gv_init_reg(gd_region *reg); +void gvcst_init(gd_region *greg); +enum cdb_sc gvincr_compute_post_incr(srch_blk_status *bh); +enum cdb_sc gvincr_recompute_upd_array(srch_blk_status *bh, struct cw_set_element_struct *cse, cache_rec_ptr_t cr); +boolean_t mupfndfil(gd_region *reg, mstr *mstr_addr); +boolean_t region_init(bool cm_regions); +freeze_status region_freeze(gd_region *region, boolean_t freeze, boolean_t override, boolean_t wait_for_kip); +void rel_crit(gd_region *reg); +void rel_lock(gd_region *reg); +boolean_t wcs_verify(gd_region *reg, boolean_t expect_damage, boolean_t caller_is_wcs_recover); +bool wcs_wtfini(gd_region *reg); + +#ifdef VMS +int4 wcs_wtstart(gd_region *region); +#elif defined(UNIX) +int4 wcs_wtstart(gd_region *region, int4 writes); +#else +#error Undefined Platform +#endif + +void bmm_init(void); +int4 bmm_find_free(uint4 hint, uchar_ptr_t base_addr, uint4 total_bits); + +bool reg_cmcheck(gd_region *reg); + +void gv_bind_name(gd_addr *addr, mstr *targ); + +void db_csh_ini(sgmnt_addrs *cs); +void db_csh_ref(sgmnt_addrs *cs_addrs); +cache_rec_ptr_t db_csh_get(block_id block); +cache_rec_ptr_t db_csh_getn(block_id block); + +enum cdb_sc tp_hist(srch_hist *hist1); + +sm_uc_ptr_t get_lmap(block_id blk, unsigned char *bits, sm_int_ptr_t cycle, cache_rec_ptr_ptr_t cr); + +bool ccp_userwait(struct gd_region_struct *reg, uint4 state, int4 *timadr, unsigned short cycle); +void ccp_closejnl_ast(struct gd_region_struct *reg); +bt_rec *ccp_bt_get(sgmnt_addrs *cs_addrs, int4 block); +unsigned char *mval2subsc(mval *in_val, gv_key *out_key); + +int4 dsk_read(block_id blk, sm_uc_ptr_t buff, enum db_ver *ondisk_blkver, boolean_t blk_free); + +uint4 jnl_flush(gd_region *reg); +void jnl_fsync(gd_region *reg, uint4 fsync_addr); +void jnl_mm_timer(sgmnt_addrs *csa, gd_region *reg); +void jnl_oper_user_ast(gd_region *reg); +void jnl_wait(gd_region *reg); +void view_jnlfile(mval *dst, gd_region *reg); +void jnl_put_jrt_pfin(sgmnt_addrs *csa); +void jnl_put_jrt_pini(sgmnt_addrs *csa); +void jnl_write_epoch_rec(sgmnt_addrs *csa); +void jnl_write_inctn_rec(sgmnt_addrs *csa); +void fileheader_sync(gd_region *reg); + +gd_addr *create_dummy_gbldir(void); + +/* These prototypes should ideally be included in gvstats_rec.h but they require "sgmnt_addrs" type + * to be defined which is done in this header file, hence the prototyping is done here instead. + */ +void gvstats_rec_csd2cnl(sgmnt_addrs *csa); +void gvstats_rec_cnl2csd(sgmnt_addrs *csa); +void gvstats_rec_upgrade(sgmnt_addrs *csa); + +#include "gdsfheadsp.h" + +/* End of gdsfhead.h */ + +#endif diff --git a/sr_port/gdsfilext.h b/sr_port/gdsfilext.h new file mode 100644 index 0000000..b6a7d79 --- /dev/null +++ b/sr_port/gdsfilext.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __GDSFILEXT_H__ +#define __GDSFILEXT_H__ + +uint4 gdsfilext(uint4 blocks, uint4 filesize); + +#endif diff --git a/sr_port/gdskill.h b/sr_port/gdskill.h new file mode 100644 index 0000000..d38be79 --- /dev/null +++ b/sr_port/gdskill.h @@ -0,0 +1,48 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __GDSKILL_H__ +#define __GDSKILL_H__ + +/* Since small memory is allocated in powers of two, keep the kill_set + structure size about 8 bytes under 1k mark (current size of + header used by memory management system) */ +#define BLKS_IN_KILL_SET 251 + +/* Note the currently GDS_MAX_BLK_BITS is 28. This 28 bit block field allows for a 256M GDS block database. + * The three byte level field is the minimum required to contain the level. However, if more addressability + * is needed, the level field can likely be shrunk to a single bit to indicate a non-zero level and then the + * actual level obtained when the blocks are read in. Note that if this changes, a comment in gvcst_init() + * also needs adjustment. + */ +typedef struct +{ +#ifdef BIGENDIAN + unsigned int flag : 1; /* Block was created by this TP transaction (not real block yet) */ + unsigned int level : 3; /* Block level (0 to 6) */ + unsigned int block : GDS_MAX_BLK_BITS; /* Block number */ +#else + unsigned int block : GDS_MAX_BLK_BITS; /* Block number */ + unsigned int level : 3; /* Block level (0 to 6) */ + unsigned int flag : 1; /* Block was created by this TP transaction (not real block yet) */ +#endif +} blk_ident; + +typedef struct kill_set_struct +{ + struct kill_set_struct + *next_kill_set; + int4 used; + blk_ident blk[BLKS_IN_KILL_SET]; +} kill_set; + +#endif + diff --git a/sr_port/gdsroot.h b/sr_port/gdsroot.h new file mode 100644 index 0000000..175dc6b --- /dev/null +++ b/sr_port/gdsroot.h @@ -0,0 +1,169 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GDSROOT_H +#define GDSROOT_H + +#include + +#define DIR_ROOT 1 +#define CDB_STAGNATE 3 +#define CDB_MAX_TRIES (CDB_STAGNATE + 2) /* used in defining arrays, usage requires it must be at least 1 more than CSB_STAGNATE*/ +#define MAX_BT_DEPTH 7 +#define GLO_NAME_MAXLEN 33 /* 1 for length, 4 for prefix, 15 for dvi, 1 for $, 12 for fid */ +#define MAX_NUM_SUBSC_LEN 10 /* one for exponent, nine for the 18 significant digits */ +#define EXTEND_WARNING_FACTOR 3 +/* Define macro to compute the maximum key size required in the gv_key structure based on the database's maximum key size. + * Align it to 4-byte boundary as this macro is mostly used by targ_alloc which allocates 3 keys one for gv_target->clue, + * one for gv_target->first_rec and one for gv_target->last_rec. The alignment ensures all 3 fields start at aligned boundary. + * In the following macro, we can ideally use the ROUND_UP2 macro but since this is used in a typedef (of "key_cum_value") + * in gdscc.h, we cannot use that macro as it contains GTMASSERT expressions to do the 2-power check. To avoid this, + * we use the ROUND_UP macro (which has no checks). It is ok to do that instead of the more-efficient ROUND_UP2 macro + * as the second parameter is a constant so all this should get evaluated at compile-time itself. + */ +#define DBKEYSIZE(KSIZE) (ROUND_UP((KSIZE + MAX_NUM_SUBSC_LEN), 4)) + +typedef gtm_uint64_t trans_num; +typedef uint4 trans_num_4byte; + +typedef int4 block_id; /* allows for GDS block #s to have 32 bits but see GDS_MAX_BLK_BITS below */ + +#define GDS_MAX_BLK_BITS 28 /* see blk_ident structure in gdskill.h for why this cannot be any greater */ +#define GDS_MAX_VALID_BLK (1<inode != (B)->inode) \ + ? ((A)->inode > (B)->inode ? 1 : -1) \ + : ((A)->device != (B)->device) \ + ? ((A)->device > (B)->device ? 1 : -1) \ + : ((A)->st_gen != (B)->st_gen) \ + ? ((A)->st_gen > (B)->st_gen ? 1 : -1) \ + : 0) +#else +#define gdid_cmp(A, B) \ + (((A)->inode != (B)->inode) \ + ? ((A)->inode > (B)->inode ? 1 : -1) \ + : ((A)->device != (B)->device) \ + ? ((A)->device > (B)->device ? 1 : -1) \ + : 0) +#endif +#define is_gdid_gdid_identical(A, B) (0 == gdid_cmp(A, B) ? TRUE: FALSE) +#endif + +#define VALFIRSTCHAR(X) (ISALPHA_ASCII(X) || ('%' == X)) +#define VALFIRSTCHAR_WITH_TRIG(X) (ISALPHA_ASCII(X) || ('%' == X) GTMTRIG_ONLY(|| (HASHT_GBL_CHAR1 == X))) +#define VALKEY(X) (ISALPHA_ASCII(X) || ISDIGIT_ASCII(X)) + +/* Prototypes below */ +block_id get_dir_root(void); +boolean_t get_full_path(char *orig_fn, unsigned int orig_len, char *full_fn, unsigned int *full_len, + int max_len, uint4 *status); +void gvinit(void); +#ifdef VMS +void global_name(unsigned char prefix[], gds_file_id *fil, + unsigned char *buff); +#endif +#endif diff --git a/sr_port/ged.mpt b/sr_port/ged.mpt new file mode 100644 index 0000000..c90b006 --- /dev/null +++ b/sr_port/ged.mpt @@ -0,0 +1,212 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 1989,2001 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +%ged ;%ged - global editor + n (%zdebug) + s %("lo")="abcdefghijklmnopqrstuvwxyz" + s %("up")="ABCDEFGHIJKLMNOPQRSTUVWXYZ" + s %("$zso")=$zso + s %("$io")=$io + s $zso=$select($zv'["VMS":".",1:"[]")_"_"_$j_".ged" + s %("zlbeg")=$zl +beg ; + n $et s $et=$s($d(%zdebug):"b",1:"s $ec="""" zg "_$zl_":beg") + u 0:(ctrap=$c(3)) + f r !,"Edit ^",%("global"),! q:%("global")="" d + . i $e(%("global"))'="^" s %("global")="^"_%("global") + . if (($e(%("global"),2)="%")&($l(%("global"))=2)) d q + .. w !,"%ged will not edit: ",%("global") + .. q + . if ($e(%("global"),2)="?") d help q + . d convert + . d main + . k @%("local"),@(%("a")_$e(%("global"),3,%("glbl lngth"))) + . zwi %("add"),%("change"),%("kill"),%("s") + . quit + s $zso=%("$zso") u %("$io"):(ctrap="":exception="") + q + ; +main ; + s %("glbl lngth")=$f(%("global"),"(")-2 + if %("glbl lngth")=-2 s %("glbl lngth")=$l(%("global")) + s %("local")=$e(%("global"),2,%("glbl lngth")) + d zwr + s %=%("global") + o $zso u $zso + f r % q:$zeof s @(%("a")_$e(%,3,9999)) s %("s")=$g(%("s"))+1 + c $zso + zed $zso + o $zso:(read:rewind) + d stor + c $zso:delete + o $zso c $zso:delete + w !,"node : ",%("global") + w !,"selected : ",+$g(%("s")) + w !,"changed : ",+$g(%("change")) + w !,"added : ",+$g(%("add")) + w !,"killed : ",+$g(%("kill")) + q + ; +zwr ; + n $et s $et=$s($d(%zdebug):"b",1:"s $ec="""" goto continue") + o $zso:(newversion:exc="":noreadonly) + u $zso zwr @%("global") +continue + c $zso + q +badzwr ; + c $zso:delete + u 0 w !,"invalid global description: ",%("global") + q +stor ; + s %("storlvl")=$zl + u $zso + f r % quit:$zeof d istor + do work + q +istor ; + n $et + s $et="do istorerr" + i '$l($tr(%," ")) q + i $e(%)'="^" s %="^"_% + i $e(%,1,%("glbl lngth"))=$e(%("global"),1,%("glbl lngth")) do + . s %=$e(%,2,99999) + . s @% + . quit + else do + . set %("cur io")=$io + . u 0 w !,"%ged will not edit: ",% + . use %("cur io") + . quit + q + ; +istorerr; + close $zso + use 0 + w !,"invalid syntax: ",%,!,"return to continue: " r %:30,! + zed $zso + open $zso:(read:rewind) + s $ec="" + zg %("storlvl"):stor +work ; + s %=%("local") + if $d(@%)'[0 d check ; Set top node + f s %=$q(@%) q:%="" d check + d kill + q +check ; + if ($d(@("^"_%))[0)&($d(@(%("a")_$e(%,2,9999)))[0) d add q + if ($d(@("^"_%))[0)&($d(@(%("a")_$e(%,2,9999)))'[0) d addcheck q + if ($d(@("^"_%))'[0)&($d(@(%("a")_$e(%,2,9999)))[0) d conflict q + if @("^"_%)=@% d withdraw q + if @(%("a")_$e(%,2,9999))=@("^"_%) d change q + d checkerr + q + ; +add ; + s @("^"_%)=@% + s %("add")=$g(%("add"))+1 + d withdraw + q + ; +change ; + s @("^"_%)=@% + s %("change")=$g(%("change"))+1 + d withdraw + q + ; +withdraw; + if $d(@(%("a")_$e(%,2,9999)))'[0 zwithdraw @(%("a")_$e(%,2,9999)) + q + ; +conflict; + u 0 + w !,"WARNING: The original value for node ^",%," was not stored before" + w !,"the edit session and may have changed while in the editor.",! + s %("%local")=%("a")_$e(%,2,9999) + w !,"old value : ",$s($d(@%("%local"))'[0:@%("%local"),1:"") + w !,"current value : ",@("^"_%) + w !,"edit value : ",@(%),!! + w !,"Do you still wish to use the edit value? [n] " + r %("ans"):30 s %("ans")=$tr(%("ans"),%("lo"),%("up")) + if "\NO"[("\"_%("ans")) d withdraw q + if "\YES"[("\"_%("ans")) d change q + d withdraw + q +addcheck; + u 0 + w !,"WARNING: Node ^",%," was deleted while in the editor",! + s %("%local")=%("a")_$e(%,2,9999) + w !,"old value : ",$s($d(@%("%local"))'[0:@%("%local"),1:"") + w !,"current value : " + w !,"edit value : ",@(%),!! + w !,"Do you still wish to use the edit value? [n] " + r %("ans"):30 s %("ans")=$tr(%("ans"),%("lo"),%("up")) + if "\NO"[("\"_%("ans")) d withdraw q + if "\YES"[("\"_%("ans")) d add q + d withdraw + q +checkerr; + u 0 + w !,"WARNING: Node ^",%," was modified while in editor",! + s %("%local")=%("a")_$e(%,2,9999) + w !,"old value : ",$s($d(@%("%local"))'[0:@%("%local"),1:"") + w !,"current value : ",@("^"_%) + w !,"edit value : ",@(%),!! + w !,"Do you still wish to use the edit value? [n] " + r %("ans"):30 s %("ans")=$tr(%("ans"),%("lo"),%("up")) + if "\NO"[("\"_%("ans")) d withdraw q + if "\YES"[("\"_%("ans")) d add q + d withdraw + q + ; +kill ; + n $et + s $et="s $ec="""" q" + s %=(%("a")_$e(%("local"),2,9999)) + if $d(@%)'[0 d killcheck + f s %=$q(@%) q:%="" d killcheck + q + ; +killcheck; + if $d(@("^"_%("b")_$e(%,2,9999)))[0 s %("kill")=$g(%("kill"))+1 q + if @("^"_%("b")_$e(%,2,9999))=@% zwi @("^"_%("b")_$e(%,2,9999)) s %("kill")=$g(%("kill"))+1 q + u 0 + w !,"killed node has changed: " + w !,"node : ^",%("b")_$e(%,2,9999) + w !,"old value : ",@(%("a")_$e(%,2,9999)) + w !,"current value : ",@("^"_%("b")_$e(%,2,9999)) + q + ; +nofile c $zso:delete + u 0 w !,"No changes made",! + q + ; +convert; + s %("b")=$e(%("global"),2) + s %("a")=$a(%("global"),2) + s %("a")=%("a")+1 + s %("a")=$s(%("a")=91:65,%("a")=123:97,%("a")=38:65,1:%("a")) + s %("a")=$c(%("a")) + q + ; +help i "dD"[$e(%("global"),3),$l(%("global"))=3 d ^%GD q + w !,"VALID INPUT",!! + w !,?3,"",?16,"to leave the %GED utility ",! + w !,?4,"?D",?16,"to display existing globals in your directory ",! + w !,"[global name]",?16,"the MUMPS name for the global e.g. ABC" + w !?16,"the global name may be followed by: " + w !?16,"subscript(s) in parentheses" + w !?16,"a subscript is a MUMPS expression e.g. ""joe"",10,$e(a,1)," + w !?16,"a ""*"" as a subscript causes all descendents to be included," + w !?16,"or by a range of subscripts in parentheses" + w !?16,"expressed as [expr]:[expr] e.g 1:10 ""a"":""d""" + w !?16,"a MUMPS pattern to match selected subscripts: ^TEST(?1.3N)" + q diff --git a/sr_port/gendash.m b/sr_port/gendash.m new file mode 100644 index 0000000..26ba51a --- /dev/null +++ b/sr_port/gendash.m @@ -0,0 +1,23 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +gendash ;generate sub-indices for dash type ttt's + n (work,ttt,opcdcnt) + f x=0:1:opcdcnt-1 d gd1 + q +gd1 i $d(work(x)) s ttt(x)=work(x) q + i $o(work(x))\1'=x s ttt(x)=0 q + s ttt(x)=ttt + f i=x+.1:.1 d proc q:$o(work(i))\1'=x + q +proc i $d(work(i)) s ttt(ttt)=work(i) + e s ttt(ttt)=0 + s ttt=ttt+1 + q diff --git a/sr_port/genout.m b/sr_port/genout.m new file mode 100644 index 0000000..4b4f589 --- /dev/null +++ b/sr_port/genout.m @@ -0,0 +1,35 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001, 2007 Fidelity Information Services, Inc ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +genout ;output the results + o outfile:newv u outfile + n knt + s knt=0 + w "/****************************************************************",! + w " *",$c(9),$c(9),$c(9),$c(9),$c(9),$c(9),$c(9),$c(9),"*",! + w " *",$c(9),"Copyright 2001, ",$Zdate($H,"YEAR")," Fidelity Information Services, Inc",$c(9),"*",! + w " *",$c(9),$c(9),$c(9),$c(9),$c(9),$c(9),$c(9),$c(9),"*",! + w " *",$c(9),"This source code contains the intellectual property",$c(9),"*",! + w " *",$c(9),"of its copyright holder(s), and is made available",$c(9),"*",! + w " *",$c(9),"under a license. If you do not know the terms of",$c(9),"*",! + w " *",$c(9),"the license, please stop and do not read further.",$c(9),"*",! + w " *",$c(9),$c(9),$c(9),$c(9),$c(9),$c(9),$c(9),$c(9),"*",! + w " ****************************************************************/",!! + f i="mdef.h","vxi.h","vxt.h","xfer_enum.h" w "#include """,i,"""",! + w "LITDEF short ttt[",ttt,"] = {",! + f i=0:1:ttt-2 d prnt + w ttt(ttt-1),"};",! + c outfile + q +prnt i knt=0 w !,"/*",$j(i,5)," */",$c(9) + w ttt(i),"," + s knt=knt+1 + i knt>7!(ttt(i)="VXT_END") s knt=0 + q diff --git a/sr_port/get_cmd_qlf.c b/sr_port/get_cmd_qlf.c new file mode 100644 index 0000000..07af250 --- /dev/null +++ b/sr_port/get_cmd_qlf.c @@ -0,0 +1,160 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_string.h" +#include "cmd_qlf.h" +#include "cli.h" + +GBLDEF list_params lst_param; + +GBLREF command_qualifier glb_cmd_qlf; +GBLREF boolean_t gtm_utf8_mode; + +void get_cmd_qlf(command_qualifier *qualif) +{ + static readonly char upper[] = "UPPER"; + static readonly char lower[] = "LOWER"; + mstr *s; + int4 temp_int; + unsigned short len; + unsigned char inbuf[255]; + + qualif->qlf = glb_cmd_qlf.qlf; + qualif->object_file.mvtype = qualif->list_file.mvtype = qualif->ceprep_file.mvtype = 0; + if (gtm_utf8_mode) + qualif->qlf |= CQ_UTF8; /* Mark as being compiled in UTF8 mode */ + if (cli_present("OBJECT") == CLI_PRESENT) + { + qualif->qlf |= CQ_OBJECT; + qualif->object_file.mvtype = MV_STR; + s = &qualif->object_file.str; + len = s->len; + if (cli_get_str("OBJECT", s->addr, &len) == FALSE) + { + s->len = 0; + if (glb_cmd_qlf.object_file.mvtype == MV_STR && glb_cmd_qlf.object_file.str.len > 0) + { + s->len = glb_cmd_qlf.object_file.str.len; + memcpy(s->addr, glb_cmd_qlf.object_file.str.addr, + s->len); + } + } else + s->len = len; + } else if (cli_negated("OBJECT") == TRUE) + qualif->qlf &= ~CQ_OBJECT; + + + if (cli_present("CROSS_REFERENCE") == CLI_PRESENT) + qualif->qlf |= CQ_CROSS_REFERENCE; + else if (cli_negated("CROSS_REFERENCE") == TRUE) + qualif->qlf &= ~CQ_CROSS_REFERENCE; + + if (cli_negated("IGNORE") == TRUE) + qualif->qlf &= ~CQ_IGNORE; + else + qualif->qlf |= CQ_IGNORE; + + if (cli_present("DEBUG") == CLI_PRESENT) + qualif->qlf |= CQ_DEBUG; + else if (cli_negated("DEBUG") == TRUE) + qualif->qlf &= ~CQ_DEBUG; + + if (cli_negated("LINE_ENTRY") == TRUE) + qualif->qlf &= ~CQ_LINE_ENTRY; + + if (cli_negated("INLINE_LITERALS") == TRUE) + qualif->qlf &= ~CQ_INLINE_LITERALS; + + if (cli_negated("ALIGN_STRINGS") == TRUE) + qualif->qlf &= ~CQ_ALIGN_STRINGS; + +#ifdef DEBUG + if (cli_present("MACHINE_CODE") == CLI_PRESENT) + qualif->qlf |= CQ_MACHINE_CODE; + else if (cli_negated("MACHINE_CODE") == TRUE) + qualif->qlf &= ~CQ_MACHINE_CODE; +#else + qualif->qlf &= ~CQ_MACHINE_CODE; +#endif + + if (cli_negated("WARNINGS") == TRUE) + qualif->qlf &= ~CQ_WARNINGS; + else + qualif->qlf |= CQ_WARNINGS; + + + if (cli_negated("LIST") == TRUE) + qualif->qlf &= (~CQ_LIST & ~CQ_MACHINE_CODE); + else if (cli_present("LIST") == CLI_PRESENT) + { + qualif->qlf |= CQ_LIST; + qualif->list_file.mvtype = MV_STR; + s = &qualif->list_file.str; + len = s->len; + if (cli_get_str("LIST", s->addr, &len) == FALSE) + { + s->len = 0; + if (glb_cmd_qlf.list_file.mvtype == MV_STR && glb_cmd_qlf.list_file.str.len > 0) + { + s->len = glb_cmd_qlf.list_file.str.len; + memcpy(s->addr, glb_cmd_qlf.list_file.str.addr, + s->len); + } + } else + s->len = len; + } else if (!(qualif->qlf & CQ_LIST)) + qualif->qlf &= ~CQ_MACHINE_CODE; + + if (cli_get_int("LENGTH",&temp_int) == FALSE) + temp_int = 66; + lst_param.lines_per_page = temp_int; + if (cli_get_int("SPACE",&temp_int) == FALSE || temp_int <= 0 || temp_int >= lst_param.lines_per_page) + temp_int = 1; + lst_param.space = temp_int; + + if (cli_present("LABELS") == CLI_PRESENT) + { + len = SIZEOF(inbuf); + if (cli_get_str("LABELS", (char *)&inbuf[0], &len)) + { + if (len == SIZEOF(upper) - 1) + { + if (!memcmp(upper, &inbuf[0], len)) + qualif->qlf &= ~CQ_LOWER_LABELS; + else if (!memcmp(lower, &inbuf[0], len)) + qualif->qlf |= CQ_LOWER_LABELS; + } + } + } +# ifdef UNIX + if (CLI_PRESENT == cli_present("NAMEOFRTN")) + qualif->qlf |= CQ_NAMEOFRTN; +# endif + if (cli_present("CE_PREPROCESS") == CLI_PRESENT) + { + qualif->qlf |= CQ_CE_PREPROCESS; + qualif->ceprep_file.mvtype = MV_STR; + s = &qualif->ceprep_file.str; + len = s->len; + if (cli_get_str("CE_PREPROCESS", s->addr, &len) == FALSE) + { + s->len = 0; + if (glb_cmd_qlf.ceprep_file.mvtype == MV_STR && glb_cmd_qlf.ceprep_file.str.len > 0) + { + s->len = glb_cmd_qlf.ceprep_file.str.len; + memcpy(s->addr, glb_cmd_qlf.ceprep_file.str.addr, s->len); + } + } else + s->len = len; + } else if (cli_negated("CE_PREPROCESS") == TRUE) + qualif->qlf &= ~CQ_CE_PREPROCESS; +} diff --git a/sr_port/get_command_line.h b/sr_port/get_command_line.h new file mode 100644 index 0000000..6c6816e --- /dev/null +++ b/sr_port/get_command_line.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001, 2002 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GET_COMMAND_LINE_INCLUDED +#define GET_COMMAND_LINE_INCLUDED + +void get_command_line(mval *result, boolean_t zcmd_line); + +#endif /* GET_COMMAND_LINE_INCLUDED */ diff --git a/sr_port/get_dir_root.c b/sr_port/get_dir_root.c new file mode 100644 index 0000000..9f8464c --- /dev/null +++ b/sr_port/get_dir_root.c @@ -0,0 +1,20 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" + +block_id get_dir_root(void) +{ + + return 1; + +} diff --git a/sr_port/get_dlr_device.c b/sr_port/get_dlr_device.c new file mode 100644 index 0000000..1c103d7 --- /dev/null +++ b/sr_port/get_dlr_device.c @@ -0,0 +1,33 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" +#include "stringpool.h" + +GBLREF io_pair io_curr_device; + +void get_dlr_device(mval *v) +{ + mstr x; + char buff[128], *cp, *cend; + + x.len = SIZEOF(buff); + x.addr = buff; + (io_curr_device.in->disp_ptr->dlr_device)(&x); + v->mvtype = MV_STR; + v->str.addr = cp = x.addr; + cend = cp + x.len; + for ( ; cp < cend && *cp ; cp++) + ; + v->str.len = INTCAST(cp - v->str.addr); + s2pool(&v->str); +} diff --git a/sr_port/get_dlr_key.c b/sr_port/get_dlr_key.c new file mode 100644 index 0000000..66d4b02 --- /dev/null +++ b/sr_port/get_dlr_key.c @@ -0,0 +1,33 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" +#include "stringpool.h" + +GBLREF io_pair io_curr_device; + +void get_dlr_key(mval *v) +{ + mstr x; + char buff[128], *cp, *cend; + + x.len = SIZEOF(buff); + x.addr = buff; + (io_curr_device.in->disp_ptr->dlr_key)(&x); + v->mvtype = MV_STR; + v->str.addr = cp = x.addr; + cend = cp + x.len; + for ( ; cp < cend && *cp ; cp++) + ; + v->str.len = INTCAST(cp - v->str.addr); + s2pool(&v->str); +} diff --git a/sr_port/get_dollar_stack_info.c b/sr_port/get_dollar_stack_info.c new file mode 100644 index 0000000..12c9286 --- /dev/null +++ b/sr_port/get_dollar_stack_info.c @@ -0,0 +1,50 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "stringpool.h" +#include "error_trap.h" + +GBLREF spdesc stringpool; +GBLREF dollar_stack_type dollar_stack; /* structure containing $STACK related information */ + +void get_dollar_stack_info(int level, stack_mode_t mode, mval *result) +{ + assert(0 <= level); + assert(level < dollar_stack.index); + switch(mode) + { + case DOLLAR_STACK_MODE: + result->str = dollar_stack.array[level].mode_str; + break; + case DOLLAR_STACK_MCODE: + result->str = dollar_stack.array[level].mcode_str; + break; + case DOLLAR_STACK_PLACE: + result->str = dollar_stack.array[level].place_str; + break; + case DOLLAR_STACK_ECODE: + if (NULL != dollar_stack.array[level].ecode_ptr) + result->str = dollar_stack.array[level].ecode_ptr->ecode_str; + else + { + result->str.len = 0; + return; + } + break; + default: + GTMASSERT; + } + s2pool(&result->str); + assert(!result->str.len || ((unsigned char *)result->str.addr + result->str.len) == stringpool.free); + return; +} diff --git a/sr_port/get_frame_creation_info.c b/sr_port/get_frame_creation_info.c new file mode 100644 index 0000000..bb80af5 --- /dev/null +++ b/sr_port/get_frame_creation_info.c @@ -0,0 +1,90 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "rtnhdr.h" +#include "mv_stent.h" +#include "stack_frame.h" +#include "stringpool.h" +#include "error_trap.h" + +#define CREATEDBY_DO 0 +#define CREATEDBY_XECUTE 1 +#define CREATEDBY_FUNCTION 2 + +GBLREF stack_frame *frame_pointer; +GBLREF spdesc stringpool; +GBLREF mv_stent *mv_chain; + +#ifdef UNIX +LITDEF mstr createdby_text[3] = {{0, LEN_AND_LIT("DO")}, {0, LEN_AND_LIT("XECUTE")}, {0, LEN_AND_LIT("$$")}}; +#endif + +#ifdef VMS +LITDEF mstr createdby_text[3] = {{LEN_AND_LIT("DO")}, {LEN_AND_LIT("XECUTE")}, {LEN_AND_LIT("$$")}}; +#endif + +void get_frame_creation_info(int level, int cur_zlevel, mval *result) +{ + int count; + stack_frame *fp, *previous; + mv_stent *mvc; + + assert(0 < level); + assert(level < cur_zlevel); + count = cur_zlevel; + for (previous = NULL, fp = frame_pointer; ; previous = fp, fp = fp->old_frame_pointer) + { + if (NULL == fp->old_frame_pointer) + { + if (fp->type & SFT_TRIGR) + /* Have a trigger baseframe, pick up stack continuation frame_pointer stored by base_frame() */ + fp = *(stack_frame **)(fp + 1); + else + { /* Something wrong, just return null or assert if debug mode */ + assert(FALSE); + result->str.len = 0; + return; + } + } + assert(NULL != fp); + if (!(fp->type & SFT_COUNT)) + continue; + count--; + if (count == level) + break; + } + if (fp->flags & SFF_INDCE) + result->str = createdby_text[CREATEDBY_XECUTE]; + else + { + for (mvc = mv_chain; ; mvc = (mv_stent *) (mvc->mv_st_next + (char *) mvc)) + { + if ((mvc >= (mv_stent *)fp) || (!mvc->mv_st_next)) + { + assert(mvc->mv_st_next); + result->str = createdby_text[CREATEDBY_DO]; + break; + } + if (mvc <= (mv_stent *)previous) + continue; + if ((MVST_PARM == mvc->mv_st_type) && mvc->mv_st_cont.mvs_parm.ret_value) + { + assert(mvc->mv_st_next); + result->str = createdby_text[CREATEDBY_FUNCTION]; + break; + } + } + } + s2pool(&result->str); + assert(((unsigned char *)result->str.addr + result->str.len) == stringpool.free); +} diff --git a/sr_port/get_frame_place_mcode.c b/sr_port/get_frame_place_mcode.c new file mode 100644 index 0000000..1966867 --- /dev/null +++ b/sr_port/get_frame_place_mcode.c @@ -0,0 +1,160 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "rtnhdr.h" +#include "stack_frame.h" +#include "stringpool.h" +#include "error_trap.h" +#include "objlabel.h" +#include "cache.h" +#include "cache_cleanup.h" +#include "mu_gv_stack_init.h" +#include "gtmimagename.h" + +#include "op.h" /* for op_fntext() prototype */ + +GBLREF stack_frame *frame_pointer; +GBLREF stack_frame *error_frame; +GBLREF spdesc stringpool; +GBLREF dollar_ecode_type dollar_ecode; /* structure containing $ECODE related information */ +GBLREF enum gtmImageTypes image_type; + +void get_frame_place_mcode(int level, stack_mode_t mode, int cur_zlevel, mval *result) +{ + int count; + stack_frame *fp; + unsigned char pos_str[MAX_ENTRYREF_LEN]; + mval label; + mval routine; + int ips; + int offset; + int s1, s2; + boolean_t indirect_frame; + ihdtyp *irtnhdr; + cache_entry *indce; + INTPTR_T *vp; + unsigned char *fpmpc; + + assert(DOLLAR_STACK_PLACE == mode || DOLLAR_STACK_MCODE == mode); + assert(0 <= level); + assert(level < cur_zlevel); + count = cur_zlevel; + for (fp = frame_pointer; ; fp = fp->old_frame_pointer) + { + if (NULL == fp->old_frame_pointer) + { + if (fp->type & SFT_TRIGR) + /* Have a trigger baseframe, pick up stack continuation frame_pointer stored by base_frame() */ + fp = *(stack_frame **)(fp + 1); + else + { /* Something wrong, just return null or assert if debug mode */ + assert(FALSE); + result->str.len = 0; + return; + } + } + assert(NULL != fp); + if (!(fp->type & SFT_COUNT)) + continue; + count--; + if (count == level) + break; + } + fpmpc = fp->mpc; + if (ADDR_IN_CODE(fpmpc, fp->rvector)) + { + result->str.addr = (char *)&pos_str[0]; + result->str.len = INTCAST(symb_line(fpmpc, &pos_str[0], 0, fp->rvector) - &pos_str[0]); + indirect_frame = FALSE; + } else + { + indirect_frame = TRUE; + pos_str[0] = '@'; + result->str.addr = (char *)&pos_str[0]; + result->str.len = 1; + } + if (DOLLAR_STACK_PLACE == mode) + { + if (result->str.len) + { + s2pool(&result->str); + assert(((unsigned char *)result->str.addr + result->str.len) == stringpool.free); + } + } + if (DOLLAR_STACK_MCODE == mode) + { + if (!indirect_frame) + { + if (IS_GTM_IMAGE || (0 < level)) + { + label.mvtype = MV_STR; + routine.mvtype = MV_STR; + result->mvtype = MV_STR; + label.str.len = result->str.len; + label.str.addr = (char *)&pos_str[0]; + routine.str.len = 0; + for (ips = 0, s1 = s2 = -1; ips < result->str.len; ips++) + { + if ('+' == pos_str[ips]) + { + assert((-1 == s1) && (-1 == s2)); + s1 = ips; + } + if ('^' == pos_str[ips]) + { + s2 = ips; + break; + } + } + if (s2 >= 0) + { + routine.str.addr = (char *)&pos_str[s2 + 1]; + routine.str.len = result->str.len - s2 - 1; + label.str.len = s2; + } + offset = 0; + if (s1 >= 0) + { + label.str.len = s1; + if (s2 < 0) + s2 = result->str.len; + for (ips = s1 + 1; ips < s2; ips++) + offset = offset * 10 + pos_str[ips] - '0'; + } + op_fntext(&label, offset, &routine, result); + } else + { /* Utility base frame does not have source code */ + result->str.addr = UTIL_BASE_FRAME_CODE; + result->str.len = STRLEN(UTIL_BASE_FRAME_CODE); + } + } else + { /* code picked up from cache_cleanup(). any changes here might need to be reflected there */ + vp = (INTPTR_T *)fp->ctxt; + assert(NULL != vp); + vp--; + if ((GTM_OMAGIC << 16) + OBJ_LABEL != *vp) /* Validate backward linkage */ + GTMASSERT; + vp--; + irtnhdr = (ihdtyp *)((char *)vp + *vp); + indce = irtnhdr->indce; + assert(NULL != indce); + assert(0 < indce->refcnt); /* currently used in the M stack better have a non-zero refcnt */ + result->str = indce->src.str; + s2pool(&result->str); + assert(((unsigned char *)result->str.addr + result->str.len) == stringpool.free); + } + } + return; +} diff --git a/sr_port/get_fs_block_size.c b/sr_port/get_fs_block_size.c new file mode 100644 index 0000000..1d2c7ec --- /dev/null +++ b/sr_port/get_fs_block_size.c @@ -0,0 +1,50 @@ +/**************************************************************** + * * + * Copyright 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "get_fs_block_size.h" + +#ifndef VMS +#include "gtm_statvfs.h" +#endif + +uint4 get_fs_block_size(int fd) +{ +# ifndef VMS + struct statvfs bufvfs; + int status; +# endif + uint4 gtm_fs_block_size; + unsigned long sys_fs_block_size; + +# if defined(__vms) + sys_fs_block_size = DISK_BLOCK_SIZE; +# else + FSTATVFS(fd, &bufvfs, status); + assert(-1 != status); + assert(SIZEOF(sys_fs_block_size) == SIZEOF(bufvfs.f_frsize)); + /* If fstatvfs call fails, we dont know what the underlying filesystem size is. + * Instead of erroring out at this point, we assume a safe value and continue as much as we can. + * The higher the value, the more safe it is since it is more likely a multiple of the underlying + * filesystem size. We therefore pick 4K. + */ + sys_fs_block_size = (-1 == status) ? 4096 : bufvfs.f_frsize; +# endif + /* Fit file system block size in a 4-byte unsigned integer as that is the size in jnl_buffer. + * Assert that we never get a block size > what can be held in a 4-byte unsigned integer. + */ + gtm_fs_block_size = (uint4)sys_fs_block_size; + assert(gtm_fs_block_size == sys_fs_block_size); + assert(MAX_IO_BLOCK_SIZE >= gtm_fs_block_size); + assert(MAX_IO_BLOCK_SIZE % gtm_fs_block_size == 0); + return gtm_fs_block_size; +} diff --git a/sr_port/get_fs_block_size.h b/sr_port/get_fs_block_size.h new file mode 100644 index 0000000..b9a0177 --- /dev/null +++ b/sr_port/get_fs_block_size.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GET_FS_BLOCK_SIZE_INCLUDED +#define GET_FS_BLOCK_SIZE_INCLUDED + +uint4 get_fs_block_size(int fd); + +#endif /* GET_FS_BLOCK_SIZE_INCLUDED */ diff --git a/sr_port/get_lmap.c b/sr_port/get_lmap.c new file mode 100644 index 0000000..71c0dc3 --- /dev/null +++ b/sr_port/get_lmap.c @@ -0,0 +1,67 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gdsbml.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" + +/* Include prototypes */ +#include "t_qread.h" + + +/* +get_lmap.c: + Reads local bit map and returns buffer address, + two bit local bit-map value corresponding to the block, cycle and cr +Input Parameter: + blk: block id of the block whose bit map this routine is to fetch +Output Parameter: + bits: two bit local bit map + cycle: Cycle value found in t_qread + cr: Cache Record value found in t_qread +Returns: + buffer address of local bitmap block + Null: if t_qread fails +*/ +sm_uc_ptr_t get_lmap (block_id blk, unsigned char *bits, sm_int_ptr_t cycle, cache_rec_ptr_ptr_t cr) +{ + sm_uc_ptr_t ptr, bp; + block_id index, offset; + error_def(ERR_DSEBLKRDFAIL); + + index = ROUND_DOWN2(blk, BLKS_PER_LMAP); + offset = blk - index; + bp = t_qread (index, cycle, cr); + if (bp) + { + ptr = bp + SIZEOF(blk_hdr) + (offset * BML_BITS_PER_BLK) / 8; + *bits = *ptr; + switch (blk % (8 / BML_BITS_PER_BLK)) + { case 0: break; + case 1: + *bits = *bits >> BML_BITS_PER_BLK; + break; + case 2: + *bits = *bits >> 2 * BML_BITS_PER_BLK; + break; + case 3: + *bits = *bits >> 3 * BML_BITS_PER_BLK; + break; + } + *bits = *bits & 3; + } + return bp; +} diff --git a/sr_port/get_log_name.c b/sr_port/get_log_name.c new file mode 100644 index 0000000..c93ac57 --- /dev/null +++ b/sr_port/get_log_name.c @@ -0,0 +1,62 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "io.h" +#include "ident.h" +#include "mmemory.h" + +GBLREF io_log_name *io_root_log_name; + +#define LOGNAME_LEN 255 + +io_log_name *get_log_name(mstr *v, bool insert) +{ + io_log_name *l, *prev, *new; + int4 stat; + short index, v_len; + unsigned char buf[LOGNAME_LEN]; + error_def (ERR_INVSTRLEN); + + assert (io_root_log_name != 0); + assert(io_root_log_name->len == 0); + v_len = v->len; + if (v_len == 0) + return io_root_log_name; + if (v_len > LOGNAME_LEN) + rts_error(VARLSTCNT(4) ERR_INVSTRLEN, 2, v_len, LOGNAME_LEN); + CONVERT_IDENT(buf, v->addr, v_len); + for (prev = io_root_log_name, l = prev->next; l != 0; prev = l, l = l->next) + { + stat = memvcmp(l->dollar_io, l->len, buf, v_len); + if (stat == 0) + return l; + if (stat > 0) + break; + } + if (insert == INSERT) + { + assert(prev != 0); + new =(io_log_name *) malloc(SIZEOF(*new) + v_len); + memset(new, 0, SIZEOF(*new) - 1); + new->len = v_len; + memcpy(new->dollar_io, buf, v_len); + new->dollar_io[v_len] = 0; + prev->next = new; + new->next = l; + return new; + } + assert(insert == NO_INSERT); + return 0; +} diff --git a/sr_port/get_mladdr.c b/sr_port/get_mladdr.c new file mode 100644 index 0000000..7752488 --- /dev/null +++ b/sr_port/get_mladdr.c @@ -0,0 +1,60 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_string.h" +#include "compiler.h" +#include "cmd_qlf.h" +#include "mmemory.h" +#include "gtm_caseconv.h" +#include "min_max.h" +#include "stringpool.h" + +GBLREF mlabel *mlabtab; +GBLREF command_qualifier cmd_qlf; + +mlabel *get_mladdr(mident *lab_name) +{ + mident_fixed upper_ident; + mident *lname, upper_lname; + mlabel **p; + int4 x; + mstr lab_str; + + lname = lab_name; + if (!(cmd_qlf.qlf & CQ_LOWER_LABELS)) + { + lower_to_upper((uchar_ptr_t)&upper_ident.c[0], (uchar_ptr_t)lab_name->addr, lab_name->len); + upper_lname.len = lab_name->len; + upper_lname.addr = &upper_ident.c[0]; + lname = &upper_lname; + } + for (p = &mlabtab; *p; ) + { + MIDENT_CMP(&(*p)->mvname, lname, x); + if (x < 0) + p = &((*p)->rson); + else if (x > 0) + p = &((*p)->lson); + else + return *p; + } + lab_str.len = lname->len; + lab_str.addr = lname->addr; + s2pool_align(&lab_str); + *p = (mlabel *) mcalloc(SIZEOF(mlabel)); + (*p)->mvname.len = lab_str.len; + (*p)->mvname.addr = lab_str.addr; + assert(!(*p)->lson && !(*p)->rson); + (*p)->formalcnt = NO_FORMALLIST; + (*p)->gbl = TRUE; + return *p; +} diff --git a/sr_port/get_mmseg.c b/sr_port/get_mmseg.c new file mode 100644 index 0000000..1b1eda1 --- /dev/null +++ b/sr_port/get_mmseg.c @@ -0,0 +1,65 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include + +#include "mmseg.h" + +GBLREF sm_uc_ptr_t min_mmseg; +GBLREF sm_uc_ptr_t max_mmseg; +GBLREF mmseg *mmseg_head; + +OS_PAGE_SIZE_DECLARE + +/* === get virtual address if available === */ +caddr_t get_mmseg(size_t size) +{ + mmseg *curr, *newone; + sm_uc_ptr_t begin_hint, end_hint; + +#if defined(__osf__) && defined(__alpha) + /* caddr_t should be 64 bits */ + assert(8 == SIZEOF(caddr_t)); +#endif + + if (!mmseg_head) + { +#if defined(__osf__) && defined(__alpha) + /* Some simple initial value for Tru64 UNIX, these numbers + * really should be from testing + */ + min_mmseg = (sm_uc_ptr_t)0x4000000000L; + max_mmseg = (sm_uc_ptr_t)0x3E000000000L; +#endif + + return (caddr_t)min_mmseg; + } + + if ((mmseg_head->begin > min_mmseg) && (size < mmseg_head->begin - min_mmseg)) + return (caddr_t)min_mmseg; + + curr = mmseg_head; + while (curr) + { + end_hint = curr->next ? (sm_uc_ptr_t)curr->next->begin : max_mmseg; + end_hint = (sm_uc_ptr_t)(ROUND_DOWN2((long)end_hint, OS_PAGE_SIZE)); + begin_hint = (sm_uc_ptr_t)(ROUND_UP2((long)(curr->end), OS_PAGE_SIZE)); + if (((unsigned long)begin_hint > (unsigned long)min_mmseg) && + ((unsigned long)size < (unsigned long)end_hint - (unsigned long)begin_hint)) + return (caddr_t)begin_hint; + curr = curr->next; + } + + /* Our managable virtual address space is used up, let system decide */ + return (caddr_t)NULL; +} diff --git a/sr_port/get_mumps_code.h b/sr_port/get_mumps_code.h new file mode 100644 index 0000000..5ae17f8 --- /dev/null +++ b/sr_port/get_mumps_code.h @@ -0,0 +1,11 @@ +/**************************************************************** + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +void get_mumps_code(stack_frame *fp, mval *place, mval *mumpscode); diff --git a/sr_port/get_mvaddr.c b/sr_port/get_mvaddr.c new file mode 100644 index 0000000..daec33c --- /dev/null +++ b/sr_port/get_mvaddr.c @@ -0,0 +1,66 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "compiler.h" +#include "mmemory.h" +#include "min_max.h" +#include "stringpool.h" + +GBLREF mvar *mvartab; +GBLREF mvax *mvaxtab, *mvaxtab_end; +GBLREF int mvmax; + +mvar *get_mvaddr(mident *var_name) +{ + mvar **p; + mvax *px; + mstr vname; + int x; + + p = &mvartab; + while (*p) + { + MIDENT_CMP(&(*p)->mvname, var_name, x); + if (x < 0) + p = &((*p)->rson); + else if (x > 0) + p = &((*p)->lson); + else + return *p; + } + /* variable doesn't exist - create a new mvar in mvartab */ + vname.len = var_name->len; + vname.addr = var_name->addr; + s2pool_align(&vname); + *p = (mvar *)mcalloc(SIZEOF(mvar)); + (*p)->mvname.len = vname.len; + (*p)->mvname.addr = vname.addr; + (*p)->mvidx = mvmax++; + (*p)->lson = (*p)->rson = NULL; + (*p)->last_fetch = NULL; + px = (mvax *)mcalloc(SIZEOF(mvax)); + px->var = *p; + px->last = px->next = 0; + px->mvidx = (*p)->mvidx; + if (mvaxtab_end) + { + px->last = mvaxtab_end; + mvaxtab_end->next = px; + mvaxtab_end = px; + } + else + mvaxtab = mvaxtab_end = px; + return *p; +} diff --git a/sr_port/get_page_size.h b/sr_port/get_page_size.h new file mode 100644 index 0000000..cf2b745 --- /dev/null +++ b/sr_port/get_page_size.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GET_PAGE_SIZE_INCLUDED +#define GET_PAGE_SIZE_INCLUDED + +void get_page_size(void); + +#endif /* GET_PAGE_SIZE_INCLUDED */ diff --git a/sr_port/get_reference.c b/sr_port/get_reference.c new file mode 100644 index 0000000..761afea --- /dev/null +++ b/sr_port/get_reference.c @@ -0,0 +1,66 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gdsroot.h" /* needed for gdsfhead.h */ +#include "gtm_facility.h" /* needed for gdsfhead.h */ +#include "fileinfo.h" /* needed for gdsfhead.h */ +#include "gdsbt.h" /* needed for gdsfhead.h */ +#include "gdsfhead.h" +#include "stringpool.h" +#include "format_targ_key.h" +#include "get_reference.h" /* for get_reference() prototype */ + +GBLREF gv_key *gv_currkey; +GBLREF spdesc stringpool; +GBLREF mstr extnam_str; + +void get_reference(mval *var) +{ + char *end, *start; + char extnamdelim[] = "^|\"\"|"; + char *extnamsrc, *extnamtop; + int maxlen; + + /* you need to return a double-quote for every single-quote. assume worst case. */ + maxlen = MAX_ZWR_KEY_SZ + (!extnam_str.len ? 0 : ((extnam_str.len * 2) + SIZEOF(extnamdelim))); + ENSURE_STP_FREE_SPACE(maxlen); + var->mvtype = MV_STR; + start = var->str.addr = (char *)stringpool.free; + var->str.len = 0; + if (gv_currkey && gv_currkey->end) + { + if (extnam_str.len) + { + *start++ = extnamdelim[0]; + *start++ = extnamdelim[1]; + *start++ = extnamdelim[2]; + extnamsrc = &extnam_str.addr[0]; + extnamtop = extnamsrc + extnam_str.len; + for ( ; extnamsrc < extnamtop; ) + { + *start++ = *extnamsrc; + if ('"' == *extnamsrc++) /* caution : pointer increment side-effect */ + *start++ = '"'; + } + *start++ = extnamdelim[3]; + } + end = (char *)format_targ_key((unsigned char *)start, MAX_ZWR_KEY_SZ, gv_currkey, TRUE); + if (extnam_str.len) + /* Note: the next vertical bar overwrites the caret that + * was part of he original name of the global variable + */ + *start = extnamdelim[4]; + var->str.len = INTCAST(end - var->str.addr); + stringpool.free += var->str.len; + } +} diff --git a/sr_port/get_reference.h b/sr_port/get_reference.h new file mode 100644 index 0000000..b78c292 --- /dev/null +++ b/sr_port/get_reference.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GET_REFERENCE_H_INCLUDED +#define GET_REFERENCE_H_INCLUDED + +void get_reference(mval *var); + +#endif /* GET_REFERENCE_H_INCLUDED */ diff --git a/sr_port/get_ret_targ.c b/sr_port/get_ret_targ.c new file mode 100644 index 0000000..20d8945 --- /dev/null +++ b/sr_port/get_ret_targ.c @@ -0,0 +1,77 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "rtnhdr.h" +#include "stack_frame.h" +#include "mv_stent.h" +#include "tp_frame.h" +#include "get_ret_targ.h" + +GBLREF stack_frame *frame_pointer; +GBLREF unsigned char *msp, *stackbase, *stacktop; +GBLREF mv_stent *mv_chain; +GBLREF int process_exiting; + +mval *get_ret_targ(stack_frame **retsf) +{ + /* return the target of the return for this frame; return NULL if not called as extrinsic */ + + mv_stent *mvc; + stack_frame *sf; + + assert(msp <= stackbase && msp > stacktop); + assert(mv_chain <= (mv_stent *)stackbase && mv_chain > (mv_stent *)stacktop); + assert(frame_pointer <= (stack_frame *)stackbase && frame_pointer > (stack_frame *)stacktop); + + for (mvc = mv_chain, sf = frame_pointer; NULL != sf; sf = sf->old_frame_pointer) + { + for ( ; mvc < (mv_stent *)sf; mvc = (mv_stent *)(mvc->mv_st_next + (char *)mvc)) + { + switch (mvc->mv_st_type) + { + case MVST_PARM: + if (NULL != retsf) + *retsf = sf; + return mvc->mv_st_cont.mvs_parm.ret_value; + case MVST_MSAV: + case MVST_MVAL: + case MVST_IARR: + case MVST_STAB: + case MVST_NTAB: + case MVST_PVAL: + case MVST_NVAL: + case MVST_STCK: + case MVST_STCK_SP: + case MVST_TVAL: + case MVST_TPHOLD: + case MVST_ZINTR: + case MVST_ZINTDEV: + case MVST_TRIGR: + case MVST_RSTRTPC: + case MVST_STORIG: + case MVST_MRGZWRSV: + break; + default: + assert(FALSE); + if (!process_exiting) + GTMASSERT; + break; + } + if (!mvc->mv_st_next) + GTMASSERT; /* Avoid endless loop if next field got zapped */ + } + if (SFT_COUNT & sf->type) + break; /* Counted frame was checked, done */ + } + return NULL; +} diff --git a/sr_port/get_ret_targ.h b/sr_port/get_ret_targ.h new file mode 100644 index 0000000..c07b6d1 --- /dev/null +++ b/sr_port/get_ret_targ.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __GET_RET_TARG_H__ +#define __GET_RET_TARG_H__ + +mval *get_ret_targ(stack_frame **retsf); + +#endif diff --git a/sr_port/get_root.c b/sr_port/get_root.c new file mode 100644 index 0000000..92cd4a3 --- /dev/null +++ b/sr_port/get_root.c @@ -0,0 +1,24 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "get_root.h" + +GBLREF gv_namehead *gv_target; + +int get_root(void) +{ return(gv_target->root); +} diff --git a/sr_port/get_root.h b/sr_port/get_root.h new file mode 100644 index 0000000..070a038 --- /dev/null +++ b/sr_port/get_root.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __GET_ROOT_H__ +#define __GET_ROOT_H__ + +int get_root(void); + +#endif diff --git a/sr_port/get_spec.c b/sr_port/get_spec.c new file mode 100644 index 0000000..55af966 --- /dev/null +++ b/sr_port/get_spec.c @@ -0,0 +1,44 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "spec_type.h" +#include "get_spec.h" + +GBLREF gv_key *gv_altkey; +GBLREF gv_namehead *gv_target; +static readonly int spec_len[LAST_TYPE_DEFINED]={4,4}; + +uchar_ptr_t get_spec(uchar_ptr_t spec_rec_addr, int spec_rec_len, unsigned char spec_type) +{ + uchar_ptr_t ptr, top; + error_def(ERR_GVIS); + error_def(ERR_INVSPECREC); + + for (ptr = spec_rec_addr, top = ptr + spec_rec_len; ptr < top; ptr += spec_len[*ptr]) + { + if (*ptr == spec_type) + return ptr; + else + if (*ptr > LAST_TYPE_DEFINED) + { + gv_target->root = 0; + rts_error(VARLSTCNT(6) ERR_INVSPECREC, 0, ERR_GVIS, 2, gv_altkey->end - 1, gv_altkey->base); + } + } + return (uchar_ptr_t)0; +} diff --git a/sr_port/get_spec.h b/sr_port/get_spec.h new file mode 100644 index 0000000..dfdd4bb --- /dev/null +++ b/sr_port/get_spec.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GET_SPEC_INCLUDED +#define GET_SPEC_INCLUDED + +uchar_ptr_t get_spec(uchar_ptr_t spec_rec_addr, int spec_rec_len, unsigned char spec_type); + +#endif /* GET_SPEC_INCLUDED */ diff --git a/sr_port/get_symb_line.c b/sr_port/get_symb_line.c new file mode 100644 index 0000000..e17a4eb --- /dev/null +++ b/sr_port/get_symb_line.c @@ -0,0 +1,60 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "rtnhdr.h" +#include "stack_frame.h" +#include "error_trap.h" /* for error_ret()/error_ret_vms() declaration */ + +GBLREF stack_frame *frame_pointer; +GBLREF stack_frame *error_frame; +GBLREF dollar_ecode_type dollar_ecode; /* structure containing $ECODE related information */ + +unsigned char *get_symb_line(unsigned char *out, unsigned char **b_line, unsigned char **ctxt) +{ + boolean_t line_reset; + stack_frame *fp; + unsigned char *addr, *out_addr; + unsigned char *fpmpc, *fpctxt; + + line_reset = FALSE; + for (fp = frame_pointer; fp; fp = fp->old_frame_pointer) + { +# ifdef GTM_TRIGGER + if (NULL == fp->old_frame_pointer && (fp->type & SFT_TRIGR)) + /* Have a trigger baseframe, pick up stack continuation frame_pointer stored by base_frame() */ + fp = *(stack_frame **)(fp + 1); +# endif + fpmpc = fp->mpc; + fpctxt = fp->ctxt; + if (ADDR_IN_CODE(fpmpc, fp->rvector)) + { + if (ctxt != 0) + *ctxt = fpctxt; + if (line_reset) + addr = fpmpc + 1; + else + addr = fpmpc; + out_addr = symb_line(addr, out, b_line, fp->rvector); + assert (out < out_addr); + return out_addr; + } else + { + if (fp->type & SFT_ZTRAP || fp->type & SFT_DEV_ACT) + line_reset = TRUE; + } + } + GTMASSERT; + return NULL; +} diff --git a/sr_port/getjobname.c b/sr_port/getjobname.c new file mode 100644 index 0000000..202d57e --- /dev/null +++ b/sr_port/getjobname.c @@ -0,0 +1,33 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "stringpool.h" +#include "mvalconv.h" +#include "getjobnum.h" +#include "getjobname.h" + +GBLREF uint4 process_id; +GBLDEF mval dollar_job; +static char djbuff[10]; /* storage for dollar job's string form */ + +void getjobname(void) +{ + getjobnum(); + i2usmval(&dollar_job, process_id); + n2s(&dollar_job); + assert(dollar_job.str.len <= SIZEOF(djbuff)); + memcpy(djbuff,dollar_job.str.addr,dollar_job.str.len); + dollar_job.str.addr = djbuff; +} diff --git a/sr_port/getjobname.h b/sr_port/getjobname.h new file mode 100644 index 0000000..3d872a8 --- /dev/null +++ b/sr_port/getjobname.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __GETJOBNAME_H__ +#define __GETJOBNAME_H__ + +void getjobname(void); + +#endif diff --git a/sr_port/getjobnum.h b/sr_port/getjobnum.h new file mode 100644 index 0000000..2876213 --- /dev/null +++ b/sr_port/getjobnum.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __GETJOBNUM_H__ +#define __GETJOBNUM_H__ + +void getjobnum(void); + +#endif diff --git a/sr_port/getprime.c b/sr_port/getprime.c new file mode 100644 index 0000000..c670023 --- /dev/null +++ b/sr_port/getprime.c @@ -0,0 +1,29 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +int4 getprime (int4 n) +/* Returns first prime # >= n */ +{ + int m, p; + + for (m = n | 1 ; ; m += 2) + { + for (p = 3 ; ; p += 2) + { + if (p * p > m) + return m; + if (((m / p) * p) == m) + break; + } + } +} diff --git a/sr_port/getstorage.h b/sr_port/getstorage.h new file mode 100644 index 0000000..c802e0d --- /dev/null +++ b/sr_port/getstorage.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GETSTORAGE_INCLUDED +#define GETSTORAGE_INCLUDED + +int4 getstorage(void); + +#endif /* GETSTORAGE_INCLUDED */ diff --git a/sr_port/getzdir.c b/sr_port/getzdir.c new file mode 100644 index 0000000..409001f --- /dev/null +++ b/sr_port/getzdir.c @@ -0,0 +1,36 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_unistd.h" +#include "gtm_string.h" + +#include "getzdir.h" +#include "setzdir.h" + +GBLREF mval dollar_zdir; + +void getzdir(void) +{ + mval cwd; + + setzdir(NULL, &cwd); + if (cwd.str.len > dollar_zdir.str.len) + { + if (NULL != dollar_zdir.str.addr) + free(dollar_zdir.str.addr); + dollar_zdir.str.addr = malloc(cwd.str.len); + } + dollar_zdir.str.len = cwd.str.len; + memcpy(dollar_zdir.str.addr, cwd.str.addr, cwd.str.len); + return; +} diff --git a/sr_port/getzdir.h b/sr_port/getzdir.h new file mode 100644 index 0000000..6f5345e --- /dev/null +++ b/sr_port/getzdir.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GETZDIR_INCLUDED +#define GETZDIR_INCLUDED + +void getzdir(void); + +#endif /* GETZDIR_INCLUDED */ diff --git a/sr_port/getzmode.h b/sr_port/getzmode.h new file mode 100644 index 0000000..51079a3 --- /dev/null +++ b/sr_port/getzmode.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GETZMODE_INCLUDED +#define GETZMODE_INCLUDED + +void getzmode(void); + +#endif /* GETZMODE_INCLUDED */ diff --git a/sr_port/getzposition.c b/sr_port/getzposition.c new file mode 100644 index 0000000..0f8af95 --- /dev/null +++ b/sr_port/getzposition.c @@ -0,0 +1,28 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "stringpool.h" +#include "rtnhdr.h" +#include "stack_frame.h" +#include "getzposition.h" + +GBLREF spdesc stringpool; + +void getzposition (mval *v) +{ + ENSURE_STP_FREE_SPACE(MAX_ENTRYREF_LEN); + v->mvtype = MV_STR; + v->str.addr = (char *) stringpool.free; + stringpool.free = get_symb_line (stringpool.free, 0, 0); + v->str.len = INTCAST((char *)stringpool.free - v->str.addr); + return; +} diff --git a/sr_port/getzposition.h b/sr_port/getzposition.h new file mode 100644 index 0000000..86ae879 --- /dev/null +++ b/sr_port/getzposition.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GETZPOSITION_INCLUDED +#define GETZPOSITION_INCLUDED + +void getzposition(mval *v); + +#endif /* GETZPOSITION_INCLUDED */ diff --git a/sr_port/getzprocess.h b/sr_port/getzprocess.h new file mode 100644 index 0000000..631eda5 --- /dev/null +++ b/sr_port/getzprocess.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GETZPROCESS_INCLUDED +#define GETZPROCESS_INCLUDED + +void getzprocess(void); + +#endif /* GETZPROCESS_INCLUDED */ diff --git a/sr_port/gi.mpt b/sr_port/gi.mpt new file mode 100644 index 0000000..4e5a3da --- /dev/null +++ b/sr_port/gi.mpt @@ -0,0 +1,69 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 1991, 2006 Fidelity Information Services, Inc ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +%GI ;service@greystone.com %GO;19920722 21:35;global input + ;Load globals into database + ;Possible enhancements: + ;selection and/or exclusion by key list, range and/or wildcard + ;optional confirmation by global name + ;callable entry point + ; + w !,"Global Input Utility",! + i '$d(%zdebug) n $et s $et="zg "_$zl_":ERR^%GI" u $p:(ctrap=$c(3):exc="zg "_$zl_":EXIT^%GI") + n d,g,n,sav,x,y,%ZD,fmt,ctls + s ctls="" f d=1:1:31,127 s ctls=ctls_$c(d) + f d q:$l(%ZD) + . r !,"Input device: : ",%ZD,! + . i '$l(%ZD) s %ZD=$p q + . i %ZD="^" q + . i %ZD="?" d q + . . w !!,"Select the device you want for input" + . . w !,"If you wish to exit enter a caret (^)",! + . . s %ZD="" + . i $zparse(%ZD)="" w " no such device" s %ZD="" q + . o %ZD:(readonly:block=2048:record=2044:exception="g noopen"):0 + . i '$t w !,%ZD," is not available" s %ZD="" q + . q +noopen . w !,$p($ZS,",",2,999),! c %ZD s %ZD="" + q:%ZD="^" + w !! + s sav="",(g,n)=0 + u %ZD:exception="g eof" + r x,y u $p w !,x,!,y,!! + u $p r !,"OK ? ",x,!! + i $l(x),$e("NO",1,$l(x))=$tr(x,"no","NO") c:%ZD'=$p %ZD u $p:(ctrap="":exc="") q + s fmt=y["ZWR" + i (fmt) f u %ZD r x q:x="" d + . s @x,n=n+1,x=$p($p(x,"="),"(") i x'=sav,x'="^" d + . . s g=g+1,sav=x + . . u $p w:$x>70 ! w x,?$x\10+1*10 + i ('fmt) f u %ZD r x,y i "*"'[$e(x) d + . i $tr(x,ctls,"")'=x d ;convert control chars to $C(x) exprs + . . n c,cp,nx s nx="" + . . f cp=1:1:$l(x) d + . . . s c=$e(x,cp),nx=nx_$s(ctls[c:"""_$c("_$a(c)_")_""",1:c) + . . s x=nx ;use fixed 'x' + . s @x=y + . s n=n+1,x=$p(x,"(") + . i x'=sav,x'="^" d + . . s g=g+1,sav=x + . . u $p w:$x>70 ! w x,?$x\10+1*10 +eof u $p + w !!,"Restored ",n," node",$s(n=1:"",1:"s") + w " in ",g," global",$s(g=1:".",1:"s.") + c:%ZD'=$p %ZD u $p:(ctrap="":exc="") + q + ; +ERR u $p w !,$p($zs,",",2,99),! + ; Warning - Fall-though + s $ec="" +EXIT i $d(%ZD),%ZD'=$p c %ZD + u $p:(ctrap="":exc="") + q diff --git a/sr_port/global_map.c b/sr_port/global_map.c new file mode 100644 index 0000000..ab789d5 --- /dev/null +++ b/sr_port/global_map.c @@ -0,0 +1,92 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "global_map.h" +#include "min_max.h" + +/* "map[]" is assumed to be an array of mstrs whose last mstr has a NULL value for its "addr" field. + * "map[]" contains even number of mstrs, each consecutive pair of mstrs (starting from map[0]) point to a range of names. + * Also "map[]" is an array of strings arranged in non-decreasing alphabetical order. + * Thus "map[]" is an array representing a set of ranges starting from the smallest to the largest. + * "beg" and "end" are the end-points of the range (both inclusive) to be inserted into this sorted range set. + * Note that a point is represented as a range whose begin and end are the same. + */ + +void global_map(mstr map[], mstr *beg, mstr *end) +{ + mstr *left, *right, rangestart, rangeend, tmpmstr; + int rslt; + + DEBUG_ONLY( + MSTRP_CMP(beg, end, rslt); + assert(0 >= rslt); + ) + for (left = map; left->addr; left++) + { + MSTRP_CMP(left, beg, rslt); + if (0 <= rslt) + break; + } + /* left now points to the first mstr in the array that is >= beg */ + for (right = left; right->addr; right++) + { + MSTRP_CMP(right, end, rslt); + if (0 < rslt) + break; + } + /* right now points to the first mstr in the array that is > end */ + if (left == right) + { /* the usual case where {beg, end} has no or complete intersections with any existing range in map[]. + * in the case of complete intersection return. + * in case of no intersection, insert {beg, end} maintaining sorted order by shifting all higher existing ranges + * two positions to the right */ + if ((right - map) & 1) + return; + rangestart = *beg; + rangeend = *end; + while (left->addr) + { /* {rangestart, rangeend} is the current range to be inserted */ + tmpmstr = *left; + *left++ = rangestart; + rangestart = tmpmstr; + tmpmstr = *left; + *left++ = rangeend; + rangeend = tmpmstr; + } + *left++ = rangestart; + *left++ = rangeend; + left->addr = 0; + return; + } + /* case where {beg, end} has partial intersections with existing ranges in map[]. + * replace intersecting ranges with one union range e.g. replace {1, 10} {5, 15} {12, 20} with {1, 20} + */ + if (0 == ((left - map) & 1)) + *left++ = *beg; + if (0 == ((right - map) & 1)) + *(--right) = *end; + if (left == right) /* possible if {beg, end} is exactly equal to an existing range in map[] */ + return; + do + { + *left++ = *right; + } while ((right++)->addr); + /* note that replacing atleast 2 existing ranges with {begin, end} into one union range will cause a reduction + * in the number of ranges in map[] effectively causing higher-valued ranges on the right to shift left. + * In that case, we have to be also left-shift the null-valued mstr.addr, hence the ++ is done in the check + * for (right++)->addr in the "while" above instead of having "*left++ = *right++" in the loop. + */ + return; +} diff --git a/sr_port/global_map.h b/sr_port/global_map.h new file mode 100644 index 0000000..ed02326 --- /dev/null +++ b/sr_port/global_map.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GLOBAL_MAP_INCLUDED +#define GLOBAL_MAP_INCLUDED + +void global_map(mstr map[], mstr *beg, mstr *end); + +#endif /* GLOBAL_MAP_INCLUDED */ diff --git a/sr_port/glvn.c b/sr_port/glvn.c new file mode 100644 index 0000000..62880de --- /dev/null +++ b/sr_port/glvn.c @@ -0,0 +1,70 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "mdq.h" + +GBLREF char window_token; + +int glvn(oprtype *a) +{ + + triple *ref, *oldchain, tmpchain, *triptr; + oprtype x1; + error_def(ERR_VAREXPECTED); + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + switch(window_token) + { + case TK_IDENT: + if (!lvn(a,OC_GETINDX,0)) + return FALSE; + return TRUE; + case TK_CIRCUMFLEX: + if (!gvn()) + return FALSE; + *a = put_tref(newtriple(OC_GVGET)); + return TRUE; + case TK_ATSIGN: + if (TREF(shift_side_effects)) + { + dqinit(&tmpchain, exorder); + oldchain = setcurtchain(&tmpchain); + if (!indirection(&x1)) + { + setcurtchain(oldchain); + return FALSE; + } + ref = newtriple(OC_INDGLVN); + newtriple(OC_GVSAVTARG); + setcurtchain(oldchain); + dqadd(TREF(expr_start), &tmpchain, exorder); + TREF(expr_start) = tmpchain.exorder.bl; + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(TREF(expr_start)); + } else + { + if (!indirection(&x1)) + return FALSE; + ref = newtriple(OC_INDGLVN); + } + ref->operand[0] = x1; + *a = put_tref(ref); + return TRUE; + default: + stx_error(ERR_VAREXPECTED); + return FALSE; + } +} diff --git a/sr_port/go.mpt b/sr_port/go.mpt new file mode 100644 index 0000000..2fbac85 --- /dev/null +++ b/sr_port/go.mpt @@ -0,0 +1,88 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 1987, 2008 Fidelity Information Services, Inc ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +%GO ;service@greystone.com %GO;19920722 21:15;global output + ;Extracts global data to standard global output (GO) file + ;Invoke ^%GO to get interaction + ;possible enhancements: + ;selection by key list, range and/or wildcard, rather than global name + ;callable entry point + ; + w !,"Global Output Utility",! + i '$d(%zdebug) n $et s $et="zg "_$zl_":ERR^%GO" u $p:(ctrap=$c(3):exc="zg "_$zl_":EXIT^%GO") + n g,gn,m,n,%ZD,%ZG,%ZH,fmt,fmtdone + d ^%GSEL + i %ZG=0 w !,"No globals selected" q + r !,"Header Label: ",%ZH,! + r !,"Output Format: GO or ZWR: ",fmt,! + i (fmt="")!($e("ZWR",1,$l(fmt))=$tr(fmt,"zwr","ZWR")) s fmt=1 + e s fmt=0 + f d q:$l(%ZD) + . r !,"Output device: : ",%ZD,! + . i '$l(%ZD) s %ZD=$p q + . i %ZD="^" q + . i %ZD="?" d q + . . w !!,"Select the device you want for output" + . . w !,"If you wish to exit enter a carat (^)",! + . . s %ZD="" + . i $zparse(%ZD)="" w " no such device" s %ZD="" q + . o %ZD:(newversion:block=2048:record=2044:exception="g noopen"):0 + . i '$t w !,%ZD," is not available" s %ZD="" q + . q +noopen . w !,$p($ZS,",",2,999),! c %ZD s %ZD="" + q:%ZD="^" + w !! + i '$l(%ZH) s %ZH="%GO Global Output Utility" + u %ZD w %ZH w:"M"'=$ZCHSET " ",$ZCHSET w !,"GT.M ",$zd($h,"DD-MON-YEAR 24:60:SS") w:fmt " ZWR" w ! + s gn="",(m,n)=0 + f s gn=$o(%ZG(gn)) q:gn="" s g=gn d + . s fmtdone=0 + . u $p w:$x>70 ! w gn,?$x\10+1*10 u %ZD i $p=%ZD w ! + . s m=m+1 + . i $d(@g)'[0 d s n=n+1 + . . i fmt zwr @g s fmtdone=1 + . . e w g,!,@g,! + . f s g=$q(@g) q:g="" d + . . i fmt zwr:'fmtdone @g ; don't zwr if already done for unsubscripted global. It takes care for subscripts too + . . e w g,!,@g,! + . . s n=n+1 + u %ZD w !! + u $p + w !!,"Total of ",n," node",$s(n=1:"",1:"s") + w " in ",m," global",$s(m=1:".",1:"s."),!! + c:%ZD'=$p %ZD u $p:(ctrap="":exc="") + q +fw(s) ; variables used in this function are: fwlen, s, cc, fastate, isctl, i, thistime + ; initialize this procedure + s fwlen=$l(s) + i fwlen=0 w ! q + i s=+s w s,! q + s cc=$e(s) + i cc?1C w "$C(",$a(cc) s fastate=2 + e w """",cc w:cc="""" cc s fastate=1 + ; start the loop to deal with the whole string. + f i=2:1:fwlen s cc=$e(s,i,i),isctl=cc?1C d + . s thistime=1 + . if fastate=1 d + . . if (isctl) w """_$C(",$a(cc) s fastate=2,thistime=0 + . . else w cc w:cc="""" cc + . if (fastate=2)&thistime d + . . if (isctl)!(cc="""") w ",",$a(cc) + . . else w ")_""",cc s fastate=1 + if fastate=1 w """",! + else w ")",! + q + ; +ERR u $p w !,$p($zs,",",2,99),! + ; Warning - Fall-though + s $ec="" +EXIT i $d(%ZD),%ZD'=$p c %ZD + u $p:(ctrap="":exc="") + q diff --git a/sr_port/goerrorframe.c b/sr_port/goerrorframe.c new file mode 100644 index 0000000..5e90510 --- /dev/null +++ b/sr_port/goerrorframe.c @@ -0,0 +1,63 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" + +#include "rtnhdr.h" +#include "stack_frame.h" +#include "tp_frame.h" +#include "golevel.h" +#include "error.h" +#include "error_trap.h" +#ifdef GTM_TRIGGER +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gv_trigger.h" +#include "gtm_trigger.h" +#endif + +GBLREF stack_frame *frame_pointer; +GBLREF stack_frame *error_frame; + +void goerrorframe() +{ + stack_frame *fp, *fpprev; + int4 unwind; + + for (unwind = 0, fp = frame_pointer; fp < error_frame; fp = fpprev) + { + fpprev = fp->old_frame_pointer; +# ifdef GTM_TRIGGER + if (SFT_TRIGR & fpprev->type) + { + fpprev = *(stack_frame **)(fpprev + 1); + unwind++; /* Skipping over trigger frame but it needs unwinding too */ + } +# endif + unwind++; + assert(fpprev); + } + assert(fp == error_frame); + DBGEHND((stderr, "goerrorframe: Unwinding %d frames\n", unwind)); + GOFRAMES(unwind, FALSE, FALSE); + assert(error_frame == frame_pointer); + /* Now that we (the caller mdb_condition_handler) are going to rethrow an error, ensure that the + * SFF_ETRAP_ERR bit is set in "error_frame" in case it got reset by flush_jmp. + */ + SET_ERROR_FRAME(error_frame); + assert(error_frame->flags & SFF_ETRAP_ERR); + return; +} diff --git a/sr_port/goframes.c b/sr_port/goframes.c new file mode 100644 index 0000000..52a5c6c --- /dev/null +++ b/sr_port/goframes.c @@ -0,0 +1,105 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" + +#include "rtnhdr.h" /* needed for golevel.h */ +#include "error.h" +#include "op.h" +#include "stack_frame.h" /* needed for golevel.h */ +#include "tp_frame.h" /* needed for golevel.h */ +#include "golevel.h" +#ifdef GTM_TRIGGER +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gdscc.h" +#include "gdskill.h" +#include "jnl.h" +#include "gv_trigger.h" +#include "gtm_trigger.h" +#include "get_ret_targ.h" + +GBLREF boolean_t goframes_unwound_trigger; +#endif +GBLREF boolean_t skip_error_ret; +GBLREF tp_frame *tp_pointer; +GBLREF stack_frame *frame_pointer; + +LITREF mval literal_null; + +#ifdef GTM_TRIGGER +void goframes(int4 frames, boolean_t unwtrigrframe, boolean_t fromzgoto) +#else +void goframes(int4 frames) +#endif +{ + mval *ret_targ; + + GTMTRIG_ONLY(goframes_unwound_trigger = FALSE); + for (ret_targ = NULL; frames--; ) + { + while (tp_pointer && tp_pointer->fp <= frame_pointer) + { + OP_TROLLBACK(-1); + } + if (0 == frames) + { + ret_targ = (mval *)get_ret_targ(NULL); + if (NULL != ret_targ) + { + *ret_targ = literal_null; + ret_targ->mvtype |= MV_RETARG; + } + } + skip_error_ret = TRUE; +# ifdef GTM_TRIGGER + if (!(SFT_TRIGR & frame_pointer->type)) + { /* Normal frame unwind */ + DBGTRIGR((stderr, "goframes: unwinding regular frame at %016lx\n", frame_pointer)); + op_unwind(); + DBGTRIGR((stderr, "goframes: after regular frame unwind: frame_pointer 0x%016lx ctxt value: 0x%016lx\n", + frame_pointer, ctxt)); + } else + { /* Trigger base frame unwind (special case) */ + DBGTRIGR((stderr, "goframes: unwinding trigger base frame at %016lx\n", frame_pointer)); + gtm_trigger_fini(TRUE, fromzgoto); + goframes_unwound_trigger = TRUE; + } +# else + /* If triggers are not enabled, just a normal unwind */ + DBGEHND((stderr, "goframes: unwinding regular frame at %016lx\n", frame_pointer)); + op_unwind(); +# endif + assert(FALSE == skip_error_ret); /* op_unwind() should have read and reset this */ + skip_error_ret = FALSE; /* be safe in PRO versions */ + } +# ifdef GTM_TRIGGER + if (unwtrigrframe && (SFT_TRIGR & frame_pointer->type)) + { /* If we landed on a trigger base frame after unwinding everything, we are in the same boat as if we had run into + * one while we were unwinding. We cannot return this frame to (for example) zgoto which is going to morph it into + * something else (unwtrigrframe only set when ZGOTO with entryref specified). So if the flag says we should never + * land on a trigger frame, go ahead and unwind that one too. + */ + DBGTRIGR((stderr, "goframes: unwinding trailing trigger base frame at %016lx\n", frame_pointer)); + gtm_trigger_fini(TRUE, fromzgoto); + goframes_unwound_trigger = TRUE; + } +# endif + + return; +} diff --git a/sr_port/golevel.h b/sr_port/golevel.h new file mode 100644 index 0000000..9d47e76 --- /dev/null +++ b/sr_port/golevel.h @@ -0,0 +1,34 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __GOLEVEL_H__ +#define __GOLEVEL_H__ + + +/* golevel() will unwind up to the counted frame corresponding to level specified by the parm. In a trigger + * environment, an additional flag that determines if landing on a trigger frame means the unwind should continue + * or not is supplied. This flag is passed to goframes() which does the actual unwind of a specific number + * of frames (counted and uncounted). Note goframes is used by both golevel and goerrorframe. + */ +#ifdef GTM_TRIGGER +#define GOLEVEL(level, unwtrigrframe) golevel(level, unwtrigrframe) +#define GOFRAMES(frames, unwtrigrframe, fromzgoto) goframes(frames, unwtrigrframe, fromzgoto) +void golevel(int4 level, boolean_t unwtrigrframe); +void goframes(int4 frames, boolean_t unwtrigrframe, boolean_t fromzgoto); +#else +#define GOLEVEL(level, unwtrigrframe) golevel(level) +#define GOFRAMES(frames, unwtrigrframe, fromzgoto) goframes(frames) +void golevel(int4 level); /* unwind upto the counted frame corresponding to frame level "level" */ +void goframes(int4 frames); /* unwind "frames" number of frames */ +#endif +void goerrorframe(void); /* unwind upto (but not including) the frame pointed to by the "error_frame" global */ + +#endif diff --git a/sr_port/gsel.mpt b/sr_port/gsel.mpt new file mode 100644 index 0000000..e058241 --- /dev/null +++ b/sr_port/gsel.mpt @@ -0,0 +1,105 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 1987, 2006 Fidelity Information Services, Inc ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +%GSEL ;GT.M %GSEL utility - global select into a local array + ;invoke ^%GSEL to create %ZG - a local array of existing globals, interactively + ; + n add,beg,cnt,end,g,gd,gdf,k,out,pat,stp,i,c,g1 + i '$d(%zdebug) n $et s $et="zg "_$zl_":ERR^%GSEL" u $p:(ctrap=$c(3):exc="zg "_$zl_":LOOP^%GSEL") + d init,main + u $p:(ctrap="":exc="") + q +GD n add,beg,cnt,end,g,gd,gdf,k,out,pat,stp + s cnt=0,(out,gd,gdf)=1 + d main + i gdf s %ZG="*" d setup,it w !,"Total of ",cnt," global",$s(cnt=1:".",1:"s."),! + q +CALL n add,beg,cnt,end,g,gd,gdf,k,out,pat,stp,i,c,g1 + s (cnt,gd)=0 + i $d(%ZG)>1 s g="" f s g=$o(%ZG(g)) q:'$l(g) s cnt=cnt+1 + i $g(%ZG)'?.N s out=0 d setup,it s %ZG=cnt q + s out=1 + d main + q +init k %ZG + s (cnt,gd)=0,out=1 + q +main f d inter q:'$l(%ZG) + s %ZG=cnt + q +inter r !,"Global ^",%ZG,! q:'$l(%ZG) + i $e(%ZG)="?",$l(%ZG)=1 d help q + i (%ZG="?D")!(%ZG="?d") d cur q + d setup,it + w !,$s(gd:"T",1:"Current t"),"otal of ",cnt," global",$s(cnt=1:".",1:"s."),! + q +setup i gd s add=1,cnt=0,g=%ZG k %ZG s %ZG=g + e i "'-"[$e(%ZG) s add=0,g=$e(%ZG,2,999) + e s add=1,g=%ZG + s g=$tr(g,"? !""#$&'()+'-./;<=>@[]\^_`{}|~","%") + s g=$tr(g,$c(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,127)) + s g1="" f i=1:1:$l(g) s c=$e(g,i) if $a(c)<128 s g1=g1_c ; Filter out "all" non-ascii characters (be it M or UTF-8) + s g=g1 + s end=$p(g,":",2),beg=$p(g,":") + i end=beg s end="" + q +it s gdf=0 + i end'?."*",end']beg q + s g=beg d pat + i pat["""" d start f d search q:'$l(g) d save + i pat["""",'$l(end) q + s beg=stp + i '$l(g) s g=stp + s pat=".E",stp="^"_$e(end)_$tr($e(end,2,9999),"%","z") d start f d search q:'$l(g) d save + s g=end d pat + i pat["""" s:beg]g g=beg d start f d search q:'$l(g) d save + q +pat + n tmpstp + i $e(g)="%" s g="!"_$e(g,2,9999) + s pat=g + f q:$l(g,"%")<2 s g=$p(g,"%",1)_"#"_$p(g,"%",2,999),pat=$p(pat,"%",1)_"""1E1"""_$p(pat,"%",2,999) + f q:$l(g,"*")<2 s g=$p(g,"*",1)_"$"_$p(g,"*",2,999),pat=$p(pat,"*",1)_""".E1"""_$p(pat,"*",2,999) + i $e(g)="!" s g="%"_$e(g,2,9999),pat="%"_$e(pat,2,9999) + i pat["""" s pat="1""^"_pat_"""" + s tmpstp="z",$p(tmpstp,"z",30)="z" + s g="^"_$p($p(g,"#"),"$"),stp=g_$e(tmpstp,$l(g)-1,31) + q +start i g="^" s g="^%" + i g?@pat,$d(@g) d save + q +search f s g=$o(@g) s:g]stp g="" q:g?@pat!'$l(g) + q +save i add,'$d(%ZG(g)) s %ZG(g)="",cnt=cnt+1 d prt:out + i 'add,$d(%ZG(g)) k %ZG(g) s cnt=cnt-1 d prt:out + q +prt w:$x>70 ! w g,?$x\10+1*10 + q +help ; + w !,?2,"",?25,"to leave",!,?2,"""*""",?25,"for all" + w !,?2,"global",?25,"for 1 global" + w !,?2,"global1:global2",?25,"for a range" + w !,?2,"""*"" as a wildcard",?25,"permitting any number of characters" + w !,?2,"""%"" as a wildcard",?25,"for a single character in positions other than the first" + w !,?2,"""?"" as a wildcard",?25,"for a single character in positions other than the first" + i gd q + w !,?2,"""'"" as the 1st character",!,?25,"to remove globals from the list" + w !,?2,"?D",?25,"for the currently selected globals",! + q +cur w ! s g="" + f s g=$o(%ZG(g)) q:'$l(g) w:$x>70 ! w g,?($x\10+1*10) + q +ERR u $p w !,$p($ZS,",",2,999),! + u $p:(ctrap="":exc="") + s $ec="" + q +LOOP d main + u $p:(ctrap="":exc="") + q diff --git a/sr_port/gtm_assert.c b/sr_port/gtm_assert.c new file mode 100644 index 0000000..85db5fa --- /dev/null +++ b/sr_port/gtm_assert.c @@ -0,0 +1,46 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* gtm_assert - invoked via the "GTMASSERT" macro. + * + * gtm_assert raises the ERR_GTMASSERT error condition which is + * intended to be a replacement for the ubiquitous ERR_GTMCHECK. + * It differs from ERR_GTMCHECK in that it indicates the module + * name and line number of its invocation so one can determine + * exactly which ERR_GTMASSERT caused the termination. + * + * The "GTMASSERT" macro differs from the "assert" macro in that + * it is significant regardless of the definition or lack thereof + * of the macro "DEBUG" and is therefore valid for PRO images as + * well as for DBG and BTA images. + */ + +#include "mdef.h" +#include "send_msg.h" + +LITREF char gtm_release_name[]; +LITREF int4 gtm_release_name_len; + +error_def(ERR_GTMASSERT); + + + +void gtm_assert ( int file_name_len, char file_name[], int line_no) +{ + send_msg (VARLSTCNT(7) ERR_GTMASSERT, 5, + gtm_release_name_len, gtm_release_name, + file_name_len, file_name, + line_no); + rts_error (VARLSTCNT(7) ERR_GTMASSERT, 5, + gtm_release_name_len, gtm_release_name, + file_name_len, file_name, + line_no); +} diff --git a/sr_port/gtm_bintim.h b/sr_port/gtm_bintim.h new file mode 100644 index 0000000..bdfc834 --- /dev/null +++ b/sr_port/gtm_bintim.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2003 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GTM_BINTIM_INCLUDED +#define GTM_BINTIM_INCLUDED + +int gtm_bintim (char *s, jnl_proc_time *timep); + +#endif /* GTM_BINTIM_INCLUDED */ diff --git a/sr_port/gtm_byteswap_64.c b/sr_port/gtm_byteswap_64.c new file mode 100644 index 0000000..77e0371 --- /dev/null +++ b/sr_port/gtm_byteswap_64.c @@ -0,0 +1,29 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +qw_num gtm_byteswap_64(qw_num num64) +{ +#ifndef INT8_SUPPORTED + qw_num swap_qw; + uint32_t swap_uint32; + + QWASSIGN(swap_qw, num64); + swap_uint32 = GTM_BYTESWAP_32(swap_qw.value[lsb_index]); + swap_qw.value[lsb_index] = GTM_BYTESWAP_32(swap_qw.value[msb_index]); + swap_qw.value[msb_index] = swap_uint32; + return (swap_qw); +#else + GTMASSERT; /* should use GTM_BYTESWAP_64 macro, not gtm_byteswap_64 function */ + return 0; +#endif +} diff --git a/sr_port/gtm_c_stack_trace.h b/sr_port/gtm_c_stack_trace.h new file mode 100644 index 0000000..80f7dca --- /dev/null +++ b/sr_port/gtm_c_stack_trace.h @@ -0,0 +1,136 @@ +/**************************************************************** + * * + * Copyright 2009, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#ifndef GTM_C_STACK_TRACE_H +#define GTM_C_STACK_TRACE_H + +#include "gtm_stdio.h" /* For SPRINTF */ +#define ONCE 1 +#define TWICE 2 + +#ifdef VMS +#define GET_C_STACK_MULTIPLE_PIDS(MESSAGE, CNL_PID_ARRAY, MAX_PID_SLOTS, STUCK_CNT) +#define GET_C_STACK_FROM_SCRIPT(MESSAGE, WAITINGPID, BLOCKINGPID, COUNT) +#define GET_C_STACK_FOR_KIP(KIP_PIDS_ARR_PTR, TRYNUM, MAX_TRY, STUCK_CNT, MAX_PID_SLOTS) +#elif defined(UNIX) +#include +#include "send_msg.h" +#include "wbox_test_init.h" +#include "gt_timer.h" +#include "gtm_logicals.h" +#include "trans_log_name.h" +#ifndef GDSFHEAD_H_INCLUDED +#include "gdsroot.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#endif + +error_def(ERR_STUCKACT); +error_def(ERR_SYSCALL); +error_def(ERR_TEXT); + +#define GET_C_STACK_MULTIPLE_PIDS(MESSAGE, CNL_PID_ARRAY, MAX_PID_SLOTS, STUCK_CNT) \ +{ \ + uint4 index; \ + uint4 pid; \ + GBLREF uint4 process_id; \ + \ + for (index = 0; MAX_PID_SLOTS > index; index++) \ + { \ + pid = CNL_PID_ARRAY[index]; \ + if (0 != pid) \ + GET_C_STACK_FROM_SCRIPT(MESSAGE, process_id, pid, STUCK_CNT); \ + } \ +} +#define GET_C_STACK_FROM_SCRIPT(MESSAGE, WAITINGPID, BLOCKINGPID, COUNT) \ +{ \ + int4 messagelen, arr_len; \ + char *command; \ + char *errptr, *currpos; \ + int rs, save_errno; \ + char *gtm_waitstuck_script; \ + mstr envvar_logical, trans; \ + char buf[MAX_TRANS_NAME_LEN]; \ + int status; \ + \ + DCL_THREADGBL_ACCESS; \ + \ + SETUP_THREADGBL_ACCESS; \ + if (!(TREF(gtm_waitstuck_script)).len) \ + { \ + envvar_logical.addr = GTM_PROCSTUCKEXEC; \ + envvar_logical.len = SIZEOF(GTM_PROCSTUCKEXEC)- 1; \ + if (SS_NORMAL == (status = TRANS_LOG_NAME(&envvar_logical, &trans, buf, SIZEOF(buf), \ + do_sendmsg_on_log2long))) \ + { \ + assert(SIZEOF(buf) > trans.len); \ + if (0 != trans.len) \ + { \ + (TREF(gtm_waitstuck_script)).len = trans.len; \ + (TREF(gtm_waitstuck_script)).addr = (char *)malloc(trans.len); \ + memcpy((TREF(gtm_waitstuck_script)).addr, trans.addr, trans.len); \ + } \ + } \ + } \ + if (0 != (TREF(gtm_waitstuck_script)).len) \ + { \ + messagelen = STRLEN(MESSAGE); \ + arr_len = GTM_MAX_DIR_LEN + messagelen + 3 * SIZEOF(uint4) + 5; \ + command = (char *)malloc (arr_len); \ + memcpy(command, (TREF(gtm_waitstuck_script)).addr, (TREF(gtm_waitstuck_script)).len); \ + currpos = (char *)command + (TREF(gtm_waitstuck_script)).len; \ + *currpos++ = ' '; \ + memcpy(currpos, MESSAGE, messagelen); \ + currpos += messagelen; \ + *currpos++ = ' '; \ + SPRINTF(currpos, "%u %u %u", WAITINGPID, BLOCKINGPID, COUNT); \ + assert (STRLEN(command) < arr_len); \ + rs = SYSTEM((char *)command); \ + send_msg(VARLSTCNT(6) ERR_STUCKACT, 4, STRLEN("SUCCESS"), rs ? "FAILURE" : "SUCCESS", \ + STRLEN(command), (char *)command); \ + if (0 != rs) \ + { \ + save_errno = errno; \ + send_msg(VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("system"), CALLFROM, save_errno); \ + } \ + free((void *)command); \ + } \ +} +#define GET_C_STACK_FOR_KIP(KIP_PIDS_ARR_PTR, TRYNUM, MAX_TRY, STUCK_CNT, MAX_PID_SLOTS) \ +{ \ + boolean_t invoke_c_stack = FALSE; \ + const char *kip_wait_string = NULL; \ + \ + DEBUG_ONLY( \ + /* If we had waited for half the max time, get a C stack trace on the processes currently \ + * doing the kill \ + */ \ + if ((MAX_TRY / 2) == TRYNUM) \ + { \ + invoke_c_stack = TRUE; \ + kip_wait_string = "KILL_IN_PROG_HALFWAIT"; \ + } \ + ) \ + /* If we had waited for max time, get a C stack trace on the processes currently doing the kill \ + * irrespective of whether it's pro or dbg \ + */ \ + if (MAX_TRY <= TRYNUM) \ + { \ + invoke_c_stack = TRUE; \ + kip_wait_string = "KILL_IN_PROG_WAIT"; \ + } \ + if (invoke_c_stack) \ + GET_C_STACK_MULTIPLE_PIDS(kip_wait_string, KIP_PIDS_ARR_PTR, MAX_PID_SLOTS, STUCK_CNT); \ +} + +#else +#error UNSUPPORTED PLATFORM +#endif +#endif diff --git a/sr_port/gtm_caseconv.h b/sr_port/gtm_caseconv.h new file mode 100644 index 0000000..1160e7e --- /dev/null +++ b/sr_port/gtm_caseconv.h @@ -0,0 +1,18 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GTM_CASECONV_included +#define GTM_CASECONV_included + +void lower_to_upper(uchar_ptr_t d, uchar_ptr_t s, int4 len); +void upper_to_lower(uchar_ptr_t d, uchar_ptr_t s, int4 len); + +#endif /*GTM_CASECONV_included*/ diff --git a/sr_port/gtm_ctype.h b/sr_port/gtm_ctype.h new file mode 100644 index 0000000..55a5924 --- /dev/null +++ b/sr_port/gtm_ctype.h @@ -0,0 +1,125 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* gtm_ctype.h - interlude to system header file. */ + +#ifndef GTM_CTYPEH +#define GTM_CTYPEH + +#if defined(__osf__) && defined(__alpha) + +/* On Tru64, contains declarations of arrays of 64-bit pointers + * in system library routines. The following pragma's are necessary + * to ensure that references to those arrays declare them as 64-bit + * pointer arrays, even if the including C program is compiled with + * 32-bit pointer options. +*/ + +#pragma pointer_size (save) +#pragma pointer_size (long) + +#endif + +#include + +/* The behavior of the system-defined ISxxxxx macros can vary based on the current locale and platform. + * In the "C" locale, characters are classified according to the rules of the US-ASCII 7-bit coded character set. + * In non-"C" locales (for example, UTF-8 mode) the result of ISXXXXX might not be what is expected + * For example, ISALNUM(240) will return TRUE in UTF8 mode and FALSE in M mode (C locale) on Solaris. + * Therefore define ISxxxxx_ASCII variant macros that additionally do check for ASCII. + * Callers that need to check for a ISxxxxx property within the ASCII range should use the ISxxxxx_ASCII variants. + * This makes the return value consistent across all platforms and independent of the locale. + * We do not expect any callers of the non-ASCII macros in the GT.M codebase. + */ + +#ifdef ISALNUM +#undef ISALNUM +#endif +#define ISALNUM isalnum +#define ISALNUM_ASCII(CH) (IS_ASCII(CH) && ISALNUM(CH)) + +#ifdef ISALPHA +#undef ISALPHA +#endif +#define ISALPHA isalpha +#define ISALPHA_ASCII(CH) (IS_ASCII(CH) && ISALPHA(CH)) + +#ifdef ISCNTRL +#undef ISCNTRL +#endif +#define ISCNTRL iscntrl +#define ISCNTRL_ASCII(CH) (IS_ASCII(CH) && ISCNTRL(CH)) + +#ifdef ISDIGIT +#undef ISDIGIT +#endif +#define ISDIGIT isdigit +#define ISDIGIT_ASCII(CH) (IS_ASCII(CH) && ISDIGIT(CH)) + +#ifdef ISGRAPH +#undef ISGRAPH +#endif +#define ISGRAPH isgraph +#define ISGRAPH_ASCII(CH) (IS_ASCII(CH) && ISGRAPH(CH)) + +#ifdef ISLOWER +#undef ISLOWER +#endif +#define ISLOWER islower +#define ISLOWER_ASCII(CH) (IS_ASCII(CH) && ISLOWER(CH)) + +#ifdef ISPRINT +#undef ISPRINT +#endif +#define ISPRINT isprint +#define ISPRINT_ASCII(CH) (IS_ASCII(CH) && ISPRINT(CH)) + +#ifdef ISPUNCT +#undef ISPUNCT +#endif +#define ISPUNCT ispunct +#define ISPUNCT_ASCII(CH) (IS_ASCII(CH) && ISPUNCT(CH)) + +#ifdef ISSPACE +#undef ISSPACE +#endif +#define ISSPACE isspace +#define ISSPACE_ASCII(CH) (IS_ASCII(CH) && ISSPACE(CH)) + +#ifdef ISUPPER +#undef ISUPPER +#endif +#define ISUPPER isupper +#define ISUPPER_ASCII(CH) (IS_ASCII(CH) && ISUPPER(CH)) + +#ifdef ISXDIGIT +#undef ISXDIGIT +#endif +#define ISXDIGIT isxdigit +#define ISXDIGIT_ASCII(CH) (IS_ASCII(CH) && ISXDIGIT(CH)) + +#ifdef TOLOWER +#undef TOLOWER +#endif +#define TOLOWER tolower + +#ifdef TOUPPER +#undef TOUPPER +#endif +#define TOUPPER toupper + +#if defined(__osf__) && defined(__alpha) + +#pragma pointer_size (restore) + +#endif + +#endif diff --git a/sr_port/gtm_dirent.h b/sr_port/gtm_dirent.h new file mode 100644 index 0000000..eb18bcb --- /dev/null +++ b/sr_port/gtm_dirent.h @@ -0,0 +1,22 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* gtm_dirent.h - interlude to system header file. */ +#ifndef GTM_DIRENTH +#define GTM_DIRENTH + +#include + +#define OPENDIR opendir + +#define READDIR(dir, rddr_res) (rddr_res = readdir(dir)) + +#endif diff --git a/sr_port/gtm_env_init.c b/sr_port/gtm_env_init.c new file mode 100644 index 0000000..9a285f0 --- /dev/null +++ b/sr_port/gtm_env_init.c @@ -0,0 +1,217 @@ +/**************************************************************** + * * + * Copyright 2004, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gtm_stdlib.h" +#include /* For offsetof macro */ + +#include "gtm_logicals.h" +#include "logical_truth_value.h" +#include "trans_numeric.h" +#include "trans_log_name.h" +#include "gtmdbglvl.h" +#include "iosp.h" +#include "wbox_test_init.h" +#include "gtm_env_init.h" /* for gtm_env_init() and gtm_env_init_sp() prototype */ +#include "gt_timer.h" +#include "io.h" +#include "iotcpdef.h" +#include "iosocketdef.h" +#include "gtm_malloc.h" +#include "cache.h" +#include "gdsroot.h" /* needed for gdsfhead.h */ +#include "gdskill.h" /* needed for gdsfhead.h */ +#include "gdsbt.h" /* needed for gdsfhead.h */ +#include "gdsfhead.h" /* needed for MAXTOTALBLKS_MAX macro */ +#include "mvalconv.h" +#include "fullbool.h" + +#ifdef DEBUG +# define INITIAL_DEBUG_LEVEL GDL_Simple +#else +# define INITIAL_DEBUG_LEVEL GDL_None +#endif + +#ifdef FULLBLOCKWRITES +# define DEFAULT_FBW_FLAG TRUE +#else +# define DEFAULT_FBW_FLAG FALSE +#endif +#define SIZEOF_prombuf ggl_prombuf + +GBLREF boolean_t dollar_zquit_anyway; /* if TRUE compile QUITs to not care whether or not they're from an extrinsic */ +GBLREF boolean_t gvdupsetnoop; /* if TRUE, duplicate SETs update journal but not database (except + for curr_tn++) */ +GBLREF uint4 gtmDebugLevel; /* Debug level (0 = using default sm module so with + a DEBUG build, even level 0 implies basic debugging) */ +GBLREF boolean_t gtm_fullblockwrites; /* Do full (not partial) database block writes T/F */ +GBLREF boolean_t certify_all_blocks; +GBLREF uint4 gtm_blkupgrade_flag; /* controls whether dynamic block upgrade is attempted or not */ +GBLREF boolean_t gtm_dbfilext_syslog_disable; /* control whether db file extension message is logged or not */ +GBLREF uint4 gtm_max_sockets; /* Maximum sockets in a socket device that can be created by this process */ +GBLREF bool undef_inhibit; +GBLREF uint4 outOfMemoryMitigateSize; /* Reserve that we will freed to help cleanup if run out of memory */ +GBLREF uint4 max_cache_memsize; /* Maximum bytes used for indirect cache object code */ +GBLREF uint4 max_cache_entries; /* Maximum number of cached indirect compilations */ +GBLREF block_id gtm_tp_allocation_clue; /* block# hint to start allocation for created blocks in TP */ +GBLREF boolean_t gtm_stdxkill; /* Use M Standard exclusive kill instead of historical GTM */ + +void gtm_env_init(void) +{ + mstr val, trans; + boolean_t ret, is_defined; + uint4 tdbglvl, tmsock, reservesize, memsize, cachent; + int4 status; + char buf[MAX_TRANS_NAME_LEN]; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + if (!TREF(gtm_env_init_done)) + { + /* See if a debug level has been specified. Do this first since gtmDebugLevel needs + to be initialized before any mallocs are done in the system. + */ + gtmDebugLevel = INITIAL_DEBUG_LEVEL; + val.addr = GTM_DEBUG_LEVEL_ENVLOG; + val.len = SIZEOF(GTM_DEBUG_LEVEL_ENVLOG) - 1; + if (tdbglvl = trans_numeric(&val, &is_defined, TRUE)) /* Note assignment!! */ + { /* Some kind of debugging was asked for.. */ + tdbglvl |= GDL_Simple; /* Make sure simple debugging turned on if any is */ + if ((GDL_SmChkFreeBackfill | GDL_SmChkAllocBackfill) & tdbglvl) + tdbglvl |= GDL_SmBackfill; /* Can't check it unless it's filled in */ + if (GDL_SmStorHog & tdbglvl) + tdbglvl |= GDL_SmBackfill | GDL_SmChkAllocBackfill; + gtmDebugLevel |= tdbglvl; + } + /* Duplicate Set Noop environment/logical */ + val.addr = GTM_GVDUPSETNOOP; + val.len = SIZEOF(GTM_GVDUPSETNOOP) - 1; + assert(FALSE == gvdupsetnoop); /* should have been set to FALSE in gbldefs.c */ + ret = logical_truth_value(&val, FALSE, &is_defined); + if (is_defined) + gvdupsetnoop = ret; /* if the logical is not defined, we want gvdupsetnoop to take its default value */ + /* gtm_boolean environment/logical */ + val.addr = GTM_BOOLEAN; + val.len = SIZEOF(GTM_BOOLEAN) - 1; + TREF(gtm_fullbool) = trans_numeric(&val, &is_defined, TRUE); + /* NOUNDEF environment/logical */ + val.addr = GTM_NOUNDEF; + val.len = SIZEOF(GTM_NOUNDEF) - 1; + assert(FALSE == undef_inhibit); /* should have been set to FALSE at global variable definition time */ + ret = logical_truth_value(&val, FALSE, &is_defined); + if (is_defined) + undef_inhibit = ret; /* if the logical is not defined, we want undef_inhibit to take its default value */ +# ifdef DEBUG + /* GTM_GVUNDEF_FATAL environment/logical */ + val.addr = GTM_GVUNDEF_FATAL; + val.len = SIZEOF(GTM_GVUNDEF_FATAL) - 1; + assert(FALSE == TREF(gtm_gvundef_fatal)); /* should have been set to FALSE by gtm_threadgbl_defs */ + ret = logical_truth_value(&val, FALSE, &is_defined); + if (is_defined) + TREF(gtm_gvundef_fatal) = ret; /* if logical is not defined, gtm_gvundef_fatal takes the default value */ +# endif + /* Initialize variable that controls TP allocation clue (for created blocks) */ + val.addr = GTM_TP_ALLOCATION_CLUE; + val.len = SIZEOF(GTM_TP_ALLOCATION_CLUE) - 1; + gtm_tp_allocation_clue = (block_id)trans_numeric(&val, &is_defined, TRUE); + if (!is_defined) + gtm_tp_allocation_clue = (block_id)MAXTOTALBLKS_MAX; + /* Full Database-block Write mode */ + val.addr = GTM_FULLBLOCKWRITES; + val.len = SIZEOF(GTM_FULLBLOCKWRITES) - 1; + gtm_fullblockwrites = logical_truth_value(&val, FALSE, &is_defined); + if (!is_defined) + gtm_fullblockwrites = DEFAULT_FBW_FLAG; + /* GDS Block certification */ + val.addr = GTM_GDSCERT; + val.len = SIZEOF(GTM_GDSCERT) - 1; + ret = logical_truth_value(&val, FALSE, &is_defined); + if (is_defined) + certify_all_blocks = ret; /* if the logical is not defined, we want to take default value */ + /* Initialize null subscript's collation order */ + val.addr = LCT_STDNULL; + val.len = SIZEOF(LCT_STDNULL) - 1; + ret = logical_truth_value(&val, FALSE, &is_defined); + if (is_defined) + TREF(local_collseq_stdnull) = ret; + /* Initialize eXclusive Kill variety (GTM vs M Standard) */ + val.addr = GTM_STDXKILL; + val.len = SIZEOF(GTM_STDXKILL) - 1; + ret = logical_truth_value(&val, FALSE, &is_defined); + if (is_defined) + gtm_stdxkill = ret; + /* Initialize variables for white box testing. Even though these white-box test variables only control the + * flow of the DBG builds, the PRO builds check on these variables (for example, in tp_restart.c to decide + * whether to fork_n_core or not) so need to do this initialization for PRO builds as well. + */ + wbox_test_init(); + /* Initialize variable that controls dynamic GT.M block upgrade */ + val.addr = GTM_BLKUPGRADE_FLAG; + val.len = SIZEOF(GTM_BLKUPGRADE_FLAG) - 1; + gtm_blkupgrade_flag = trans_numeric(&val, &is_defined, TRUE); + /* Initialize whether database file extensions need to be logged in the operator log */ + val.addr = GTM_DBFILEXT_SYSLOG_DISABLE; + val.len = SIZEOF(GTM_DBFILEXT_SYSLOG_DISABLE) - 1; + ret = logical_truth_value(&val, FALSE, &is_defined); + if (is_defined) + gtm_dbfilext_syslog_disable = ret; /* if the logical is not defined, we want to take default value */ + /* Initialize maximum sockets in a single socket device createable by this process */ + gtm_max_sockets = MAX_N_SOCKET; + val.addr = GTM_MAX_SOCKETS; + val.len = SIZEOF(GTM_MAX_SOCKETS) - 1; + if ((tmsock = trans_numeric(&val, &is_defined, TRUE)) && MAX_MAX_N_SOCKET > tmsock) /* Note assignment!! */ + gtm_max_sockets = tmsock; + /* Initialize storage to allocate and keep in our back pocket in case run out of memory */ + outOfMemoryMitigateSize = GTM_MEMORY_RESERVE_DEFAULT; + val.addr = GTM_MEMORY_RESERVE; + val.len = SIZEOF(GTM_MEMORY_RESERVE) - 1; + if (reservesize = trans_numeric(&val, &is_defined, TRUE)) /* Note assignment!! */ + outOfMemoryMitigateSize = reservesize; + /* Initialize indirect cache limits (max memory, max entries) */ + max_cache_memsize = MAX_CACHE_MEMSIZE * 1024; + val.addr = GTM_MAX_INDRCACHE_MEMORY; + val.len = SIZEOF(GTM_MAX_INDRCACHE_MEMORY) - 1; + if (memsize = trans_numeric(&val, &is_defined, TRUE)) /* Note assignment!! */ + max_cache_memsize = memsize * 1024; + max_cache_entries = MAX_CACHE_ENTRIES; + val.addr = GTM_MAX_INDRCACHE_COUNT; + val.len = SIZEOF(GTM_MAX_INDRCACHE_COUNT) - 1; + if (cachent = trans_numeric(&val, &is_defined, TRUE)) /* Note assignment!! */ + max_cache_entries = cachent; + /* Initialize ZQUIT to control funky QUIT compilation */ + val.addr = GTM_ZQUIT_ANYWAY; + val.len = SIZEOF(GTM_ZQUIT_ANYWAY) - 1; + ret = logical_truth_value(&val, FALSE, &is_defined); + if (is_defined) + dollar_zquit_anyway = ret; + /* Initialize ZPROMPT to desired GTM prompt or default */ + val.addr = GTM_PROMPT; + val.len = SIZEOF(GTM_PROMPT) - 1; + if (SS_NORMAL == (status = TRANS_LOG_NAME(&val, &trans, buf, SIZEOF(buf), do_sendmsg_on_log2long))) + { /* Non-standard prompt requested */ + assert(SIZEOF(buf) > trans.len); + if (SIZEOF_prombuf >= trans.len) + { + (TREF(gtmprompt)).len = trans.len; + memcpy((TREF(gtmprompt)).addr, trans.addr, trans.len); + } + } + if (GETENV("gtm_environment_init")) + { + TREF(gtm_environment_init) = TRUE; + } + /* Platform specific initializations */ + gtm_env_init_sp(); + TREF(gtm_env_init_done) = TRUE; + } +} diff --git a/sr_port/gtm_env_init.h b/sr_port/gtm_env_init.h new file mode 100644 index 0000000..96666f9 --- /dev/null +++ b/sr_port/gtm_env_init.h @@ -0,0 +1,19 @@ +/**************************************************************** + * * + * Copyright 2004 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __GTM_ENV_INIT_DEFINED + +void gtm_env_init(void); +void gtm_env_init_sp(void); + +#define __GTM_ENV_INIT_DEFINED + +#endif diff --git a/sr_port/gtm_env_xlate_init.c b/sr_port/gtm_env_xlate_init.c new file mode 100644 index 0000000..43d19fd --- /dev/null +++ b/sr_port/gtm_env_xlate_init.c @@ -0,0 +1,60 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gtm_limits.h" + +#include "iosp.h" +#include "trans_log_name.h" +#include "gtm_logicals.h" +#include "error.h" +#include "gtm_env_xlate_init.h" +#include "stringpool.h" + +GBLREF mstr env_gtm_env_xlate; + +void gtm_env_xlate_init(void) +{ + int4 status; + mstr val, tn; + char buf[GTM_PATH_MAX]; + + error_def(ERR_TRNLOGFAIL); + error_def(ERR_LOGTOOLONG); + + val.addr = GTM_ENV_XLATE; + val.len = STR_LIT_LEN(GTM_ENV_XLATE); + if (SS_NORMAL == (status = TRANS_LOG_NAME(&val, &tn, buf, SIZEOF(buf), dont_sendmsg_on_log2long))) + { + UNIX_ONLY( + env_gtm_env_xlate.len = tn.len; + env_gtm_env_xlate.addr = (char *)malloc(tn.len); + memcpy(env_gtm_env_xlate.addr, buf, tn.len); + ) + VMS_ONLY( + /* In op_gvextnam, the logical name is used in VMS, rather than its value (by lib$find_image_symbol), + * so only whether the logical name translates is checked here. + */ + env_gtm_env_xlate.len = val.len; + env_gtm_env_xlate.addr = val.addr; + ) + } else if (SS_NOLOGNAM == status) + env_gtm_env_xlate.len = 0; +# ifdef UNIX + else if (SS_LOG2LONG == status) + rts_error(VARLSTCNT(5) ERR_LOGTOOLONG, 3, LEN_AND_LIT(GTM_ENV_XLATE), SIZEOF(buf) - 1); +# endif + else + rts_error(VARLSTCNT(5) ERR_TRNLOGFAIL, 2, LEN_AND_LIT(GTM_ENV_XLATE), status); + return; +} diff --git a/sr_port/gtm_env_xlate_init.h b/sr_port/gtm_env_xlate_init.h new file mode 100644 index 0000000..896486d --- /dev/null +++ b/sr_port/gtm_env_xlate_init.h @@ -0,0 +1,19 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#ifndef GTM_ENV_XLATE_INIT_H +#define GTM_ENV_XLATE_INIT_H + +#define GTM_ENV_XLATE_ROUTINE_NAME "gtm_env_xlate" + +void gtm_env_xlate_init(void); +mval* gtm_env_translate(mval* val1, mval* val2, mval* val_xlated); + +#endif /* GTM_ENV_XLATE_INIT_H */ diff --git a/sr_port/gtm_event_log.h b/sr_port/gtm_event_log.h new file mode 100644 index 0000000..3f244e3 --- /dev/null +++ b/sr_port/gtm_event_log.h @@ -0,0 +1,29 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __GTM_EVENT_LOG_H__ +#define __GTM_EVENT_LOG_H__ +/* GTM external logging */ + +#define GTM_EVENT_LOG_HARDCODE_RTN_NAME /* undef this to use env variable */ + +#ifdef GTM_EVENT_LOG_HARDCODE_RTN_NAME +# define GTM_EVENT_LOG_RTN "GtmEventLog" +extern int GtmEventLog(int argc, char *category, char *code, char *msg); +#endif + +#define GTM_EVENT_LOG_ARGC 3 /* excluding the argument count - category, code and msg */ + +int gtm_event_log_init(void); +int gtm_event_log_close(void); +int gtm_event_log(int argc, char *category, char *code, char *msg); + +#endif diff --git a/sr_port/gtm_facility.h b/sr_port/gtm_facility.h new file mode 100644 index 0000000..a84a3eb --- /dev/null +++ b/sr_port/gtm_facility.h @@ -0,0 +1,20 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __GTM_FACILITY_H__ +#define __GTM_FACILITY_H__ + +typedef enum +{ mcompile_v1y0, + mupip_create_v1y0 +} gtm_facility; + +#endif diff --git a/sr_port/gtm_fcntl.h b/sr_port/gtm_fcntl.h new file mode 100644 index 0000000..adf6010 --- /dev/null +++ b/sr_port/gtm_fcntl.h @@ -0,0 +1,36 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* gtm_fcntl.h - interlude to system header file. */ +#ifndef GTM_FCNTLH +#define GTM_FCNTLH + +#include + +#ifndef GTM_FD_TRACE +# define CREAT creat +# define OPEN open +# define OPEN3 open +#else +# define CREAT gtm_creat +# define OPEN gtm_open +# define OPEN3 gtm_open3 +# undef open /* in case this is already defined by (at least AIX and HPUX seem to do this) */ +# undef creat /* in case this is already defined by (at least AIX and HPUX seem to do this) */ +# define open gtm_open +# define creat gtm_creat +#endif + +int gtm_open(const char *pathname, int flags); +int gtm_open3(const char *pathname, int flags, mode_t mode); +int gtm_creat(const char *pathname, mode_t mode); + +#endif diff --git a/sr_port/gtm_fetch.c b/sr_port/gtm_fetch.c new file mode 100644 index 0000000..6a15713 --- /dev/null +++ b/sr_port/gtm_fetch.c @@ -0,0 +1,81 @@ +/**************************************************************** + * * + * Copyright 2009, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include + +#include "rtnhdr.h" +#include "stack_frame.h" +#include "lookup_variable_htent.h" +#include "op.h" +#include "lv_val.h" + +GBLREF stack_frame *frame_pointer; +GBLREF symval *curr_symval; +GBLREF int process_exiting; + +#ifdef UNIX +void gtm_fetch(unsigned int cnt_arg, unsigned int indxarg, ...) +#elif defined(VMS) +void gtm_fetch(unsigned int indxarg, ...) +#else +#error unsupported platform +#endif +{ + va_list var; + unsigned int indx; + unsigned int cnt; + stack_frame *fp; + ht_ent_mname **htepp; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + assert(!process_exiting); /* Verify that no process unwound the exit frame and continued */ + assert(!TREF(in_zwrite)); /* Verify in_zwrite was not left on */ + VAR_START(var, indxarg); + VMS_ONLY(va_count(cnt);) + UNIX_ONLY(cnt = cnt_arg;) /* need to preserve stack copy on i386 */ + fp = frame_pointer; + if (0 < cnt) + { /* All generated code comes here to verify instantiation + of a given set of variables from the local variable table */ + indx = indxarg; + for ( ; ; ) + { + htepp = &fp->l_symtab[indx]; + if (NULL == *htepp) + *htepp = lookup_variable_htent(indx); + assert(NULL != (*htepp)->value); + assert(LV_IS_BASE_VAR((*htepp)->value)); + assert(NULL != LV_SYMVAL((lv_val *)((*htepp)->value))); + if (0 < --cnt) + indx = va_arg(var, int4); + else + break; + } + } else + { /* GT.M calls come here to verify instantiation of the + entire local variable table */ + indx = fp->vartab_len; + htepp = &fp->l_symtab[indx]; + for (; indx > 0;) + { + --indx; + --htepp; + if (NULL == *htepp) + *htepp = lookup_variable_htent(indx); + else if (NULL == (*htepp)->value) + lv_newname(*htepp, curr_symval); /* Alias processing may have removed the lv_val */ + } + } + va_end(var); +} diff --git a/sr_port/gtm_ffs.c b/sr_port/gtm_ffs.c new file mode 100644 index 0000000..1ca7133 --- /dev/null +++ b/sr_port/gtm_ffs.c @@ -0,0 +1,52 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_ffs.h" + +#define BITS_PER_UCHAR 8 + +int gtm_ffs (uint4 offset, uchar_ptr_t addr, uint4 size) +{ + uchar_ptr_t c; + int i, j, top; + + c = addr + (offset / BITS_PER_UCHAR); + if (i = (offset & (BITS_PER_UCHAR - 1))) + { /* partial byte starting at offset */ + for (j = 0; (i < BITS_PER_UCHAR) && (j < size); j++, i++) + { + if (*c & (1 << i)) + return (offset + j); + } + c++; + } + assert(c == (addr + (offset + BITS_PER_UCHAR - 1) / BITS_PER_UCHAR)); + for (i = ROUND_UP2(offset, BITS_PER_UCHAR), top = ROUND_DOWN2(size + offset, BITS_PER_UCHAR); i < top; + c++, i += BITS_PER_UCHAR) + { /* full bytes offset to end */ + if (*c) + { + for (j = 0; j < BITS_PER_UCHAR; j++) + { + if (*c & (1 << j)) + return (i + j); + } + } + } + for (j = 0, top = size + offset; i < top; j++, i++) + { /* partial byte at end */ + assert(j < BITS_PER_UCHAR); + if (*c & (1 << j)) + return i; + } + return -1; +} diff --git a/sr_port/gtm_ffs.h b/sr_port/gtm_ffs.h new file mode 100644 index 0000000..bf05557 --- /dev/null +++ b/sr_port/gtm_ffs.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GTM_FFS_INCLUDED +#define GTM_FFS_INCLUDED + +int gtm_ffs(uint4 offset, uchar_ptr_t addr, uint4 size); + +#endif /* GTM_FFS_INCLUDED */ diff --git a/sr_port/gtm_file_remove.h b/sr_port/gtm_file_remove.h new file mode 100644 index 0000000..a3014a0 --- /dev/null +++ b/sr_port/gtm_file_remove.h @@ -0,0 +1,16 @@ +/**************************************************************** + * * + * Copyright 2003 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __GTM_FILE_REMOVE_ +#define __GTM_FILE_REMOVE_ +int4 gtm_file_remove(char *fn, int fn_len, uint4 *ustatus); + +#endif diff --git a/sr_port/gtm_file_stat.h b/sr_port/gtm_file_stat.h new file mode 100644 index 0000000..097a0ac --- /dev/null +++ b/sr_port/gtm_file_stat.h @@ -0,0 +1,60 @@ +/**************************************************************** + * * + * Copyright 2001, 2002 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GTM_FILE_STAT_INCLUDED +#define GTM_FILE_STAT_INCLUDED + +/* Note that FILE_READONLY also implies FILE_PRESENT and the callers can use this information if necessary */ +#define FILE_NOT_FOUND 0 +#define FILE_PRESENT 1 +#define FILE_READONLY 2 +#define FILE_STAT_ERROR 4 + +/* Copy the filename from "src" to "dest" using the following rules. + * (i) removing the version information + * (ii) doing compression of contiguous directory delimiters (']' or '>' followed by '<' or '['). + * (iii) transform every '<' to a '[' and every '>' to a ']' + * e.g. + * src = user:[library.]gtmshr.exe;23 + * dst = user:[library.v990.pro]gtmshr.exe + * + * Note that without (iii) we would have got the following mixed notation for "dst" which is incorrect + * as the '[' is balanced by a '>' (at the end of ".pro>") instead of a corresponding ']'. + * + * e.g. src = user:[library.]gtmshr.exe;23 + * dst = user:[library.v990.pro>gtmshr.exe */ +#ifdef VMS +#define fncpy_nover(src, src_len, dest, dest_len) \ +{ \ + unsigned char *sptr, *dptr; \ + for (sptr = (unsigned char *)src, dptr = dest; sptr < (src + src_len) && (';' != *sptr); ) \ + { \ + if (('>' == *sptr || ']' == *sptr) && ('<' == *(sptr + 1) || '[' == *(sptr + 1))) \ + sptr += 2; \ + else if ('<' == *sptr) \ + { \ + *dptr++ = '['; \ + sptr++; \ + } else if ('>' == *sptr) \ + { \ + *dptr++ = ']'; \ + sptr++; \ + } else \ + *dptr++ = *sptr++; \ + } \ + dest_len = dptr - (unsigned char *)(dest); \ + *(dptr) = 0; \ +} +#endif + +int gtm_file_stat(mstr *file, mstr *def, mstr *ret, boolean_t check_prv, uint4 *status); + +#endif /* GTM_FILE_STAT_INCLUDED */ diff --git a/sr_port/gtm_iconv.h b/sr_port/gtm_iconv.h new file mode 100644 index 0000000..9fa1f6e --- /dev/null +++ b/sr_port/gtm_iconv.h @@ -0,0 +1,26 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* gtm_iconv.h - interlude to system header file. */ +#ifndef GTM_ICONVH +#define GTM_ICONVH + +#ifdef USING_ICONV + +#define _OSF_SOURCE +#include +#undef _OSF_SOURCE + +#define ICONV_OPEN iconv_open + +#endif + +#endif diff --git a/sr_port/gtm_imagetype_init.c b/sr_port/gtm_imagetype_init.c new file mode 100644 index 0000000..f88ae08 --- /dev/null +++ b/sr_port/gtm_imagetype_init.c @@ -0,0 +1,49 @@ +/**************************************************************** + * * + * Copyright 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtmimagename.h" +#include "gtm_imagetype_init.h" + +GBLREF boolean_t skip_dbtriggers; +GBLREF boolean_t is_replicator; +GBLREF boolean_t run_time; +GBLREF boolean_t write_after_image; +GBLREF boolean_t dse_running; +GBLREF enum gtmImageTypes image_type; + +void gtm_imagetype_init(enum gtmImageTypes img_type) +{ + boolean_t is_svc_or_gtcm; + + NON_GTMTRIG_ONLY(skip_dbtriggers = TRUE;) /* Do not invoke triggers for trigger non-supporting platforms. */ + is_svc_or_gtcm = ((GTM_SVC_DAL_IMAGE == img_type) + || (GTCM_GNP_SERVER_IMAGE == img_type) + || (GTCM_SERVER_IMAGE == img_type)); + if (is_svc_or_gtcm) + skip_dbtriggers = TRUE; /* SUN RPC DAL server and GT.CM OMI and GNP servers do not invoke triggers */ + if (is_svc_or_gtcm || (GTM_IMAGE == img_type)) + { + is_replicator = TRUE; /* can go through t_end() and write jnl records to the jnlpool for replicated db */ + run_time = TRUE; + } else if (DSE_IMAGE == img_type) + { + dse_running = TRUE; + write_after_image = TRUE; /* if block change is done, after image of the block needs to be written */ + } +# ifdef UNIX + else if (MUPIP_IMAGE == img_type) + run_time = FALSE; +# endif + image_type = img_type; + return; +} diff --git a/sr_port/gtm_imagetype_init.h b/sr_port/gtm_imagetype_init.h new file mode 100644 index 0000000..a46766a --- /dev/null +++ b/sr_port/gtm_imagetype_init.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GTM_IMAGETYPE_INIT_INCLUDED +#define GTM_IMAGETYPE_INIT_INCLUDED + +void gtm_imagetype_init(enum gtmImageTypes img_type); + +#endif diff --git a/sr_port/gtm_inet.h b/sr_port/gtm_inet.h new file mode 100644 index 0000000..60fd19c --- /dev/null +++ b/sr_port/gtm_inet.h @@ -0,0 +1,31 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* gtm_inet.h - interlude to system header file. */ +#ifndef GTM_INETH +#define GTM_INETH +#ifdef VMS +#include +#endif +#if defined(_AIX) || defined (__MVS__) +#include +#endif + +#include + +#ifdef NeedInAddrPort +typedef uint32_t in_addr_t; +#endif + +#define INET_ADDR inet_addr +#define INET_NTOA inet_ntoa + +#endif diff --git a/sr_port/gtm_limits.h b/sr_port/gtm_limits.h new file mode 100644 index 0000000..0cb1c7c --- /dev/null +++ b/sr_port/gtm_limits.h @@ -0,0 +1,64 @@ +/**************************************************************** + * * + * Copyright 2002, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* Interlude to */ + +#ifndef GTM_LIMITSH +#define GTM_LIMITSH + +#include +#ifdef __hpux +#include +#endif + +/* The value 1023 for PATH_MAX is derived using pathconf("path", _PC_PATH_MAX) on z/OS. + * Since we cant afford calling a function on each use of PATH_MAX/GTM_PATH_MAX, + * this value is hardcoded here. + */ +#if defined (__MVS__) +#ifndef PATH_MAX +#define PATH_MAX 1023 +#endif +#define GTM_PATH_MAX PATH_MAX + 1 +#else +#define GTM_PATH_MAX 1024 /* includes terminating NULL */ +#endif + +#if defined(LLONG_MAX) /* C99 and others */ +#define GTM_INT64_MIN LLONG_MIN +#define GTM_INT64_MAX LLONG_MAX +#define GTM_UINT64_MAX ULLONG_MAX +#elif defined(LONG_LONG_MAX) +#define GTM_INT64_MIN LONG_LONG_MIN +#define GTM_INT64_MAX LONG_LONG_MAX +#define GTM_UINT64_MAX ULONG_LONG_MAX +#elif defined(LONGLONG_MAX) +#define GTM_INT64_MIN LONGLONG_MIN +#define GTM_INT64_MAX LONGLONG_MAX +#define GTM_UINT64_MAX ULONGLONG_MAX +#elif defined(__INT64_MAX) /* OpenVMS Alpha */ +#define GTM_INT64_MIN __INT64_MIN +#define GTM_INT64_MAX __INT64_MAX +#define GTM_UINT64_MAX __UINT64_MAX +#elif defined(INTMAX_MAX) /* HP-UX */ +#define GTM_INT64_MIN INTMAX_MIN +#define GTM_INT64_MAX INTMAX_MAX +#define GTM_UINT64_MAX UINTMAX_MAX +#elif LONG_MAX != INT_MAX /* Tru64 */ +#define GTM_INT64_MIN LONG_MIN +#define GTM_INT64_MAX LONG_MAX +#define GTM_UINT64_MAX ULONG_MAX +#else +#error Unable to determine 64 bit MAX in gtm_limits.h +#endif + +#endif + diff --git a/sr_port/gtm_malloc.c b/sr_port/gtm_malloc.c new file mode 100644 index 0000000..d7db8a2 --- /dev/null +++ b/sr_port/gtm_malloc.c @@ -0,0 +1,36 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* gtm_malloc -- the default version + + The bulk of the GTM storage manager code now sits in gtm_malloc_src.h and + is included twice: once in gtm_malloc.c and again in gtm_malloc_dbg.c. + The reason for this is that the production modules built for distribution + in the field can have both forms of storage mgmt available in the event + that it becomes necessary to chase a corruption issue. It can now be done + without resorting to a "debug" version. Several different levels of + debugging will also be made available to catch various problems. + + If the DEBUG flag is not defined (indicating a pro build), the gtm_malloc + module will expand without all the asserts and special checking making for + a compact and efficient storage manager. The gtm_malloc_dbg module will + expand AS IF DEBUG had been specified supplying an alternate assert filled + version of storage mgmnt with several different levels of storage validation + available. + + If the DEBUG flag is defined (debug or beta build), the gtm_malloc module + will expand with all debugging information intact and the gtm_malloc_dbg + module will expand as call backs to the gtm_malloc module since it makes + little sense to expand the identical module twice. +*/ + +#include "caller_id.h" +#include "gtm_malloc_src.h" diff --git a/sr_port/gtm_malloc.h b/sr_port/gtm_malloc.h new file mode 100644 index 0000000..62ff7fd --- /dev/null +++ b/sr_port/gtm_malloc.h @@ -0,0 +1,86 @@ +/**************************************************************** + * * + * Copyright 2003, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GTM_MALLOC_H__included +#define GTM_MALLOC_H__included + +#define GTM_MEMORY_RESERVE_DEFAULT 64 /* 64K reserve "backpocket-cache" released on out-of-memory error */ + +typedef GTM64_ONLY(gtm_uint8) NON_GTM64_ONLY(unsigned int) gtm_msize_t; + +/* Each allocated block has the following structure. The actual address + returned to the user for 'malloc' and supplied by the user for 'free' + is actually the storage beginning at the 'userStorage.userStart' area. + This holds true even for storage that is truely malloc'd. Note that true + allocated length is kept even in the pro header. +*/ +typedef struct storElemStruct +{ /* While the following chars and short are not the best for performance, they enable us + to keep the header size to 8 bytes in a pro build. This is important since our minimum + allocation size is 16 bytes leaving 8 bytes for data. Also I have not researched what + they are, there are a bunch of 8 byte allocates in GT.M that if we were to go to a 16 + byte header would make the minimum block size 32 bytes thus doubling the storage + requirements for these small blocks. SE 03/2002 [Note 16 byte header is the norm in 64 bit] + */ + signed char queueIndex; /* Index into TwoTable for this size of element */ + unsigned char state; /* State of this block */ + unsigned short extHdrOffset; /* For MAXTWO sized elements: offset to the + header that describes the extent */ + GTM64_ONLY(char filler[4];) /* Explicit filler to align the length - may be repurposed */ + gtm_msize_t realLen; /* Real (total) length of allocation */ +#ifdef DEBUG + struct storElemStruct *fPtr; /* Next storage element on free/allocated queue */ + struct storElemStruct *bPtr; /* Previous storage element on free/allocated queue */ + unsigned char *allocatedBy; /* Who allocated storage */ + gtm_msize_t allocLen; /* Requested length of allocation */ + gtm_msize_t smTn; /* Storage management transaction number allocated at */ + unsigned char headMarker[GTM64_ONLY(8)NON_GTM64_ONLY(4)]; /* Header that should not be modified during usage */ + union + { + struct storElemStruct *deferFreeNext; /* Pointer to next deferred free block */ + unsigned char userStart; /* First byte of user useable storage */ + } userStorage; +#else + union /* In production mode, the links are used only when element is free */ + { + struct storElemStruct *deferFreeNext; /* Pointer to next deferred free block */ + struct /* Free block information */ + { + struct storElemStruct *fPtr; /* Next storage element on free queue */ + struct storElemStruct *bPtr; /* Previous storage element on free queue */ + } links; + unsigned char userStart; /* First byte of user useable storage */ + } userStorage; +#endif +} storElem; + +size_t gtm_bestfitsize(size_t); +void verifyFreeStorage(void); +void verifyAllocatedStorage(void); +void raise_gtmmemory_error(void); +void printMallocInfo(void); +void printMallocDump(void); + +/* When verifying the storage chains, check the allocated chain first in case overruns for allocated storage + * have damaged the free chains. This way we find the culprit rather than the symptom. Of course, in the case + * where free'd storage is continued to be used, this method breaks down but it is hoped the chosen way finds + * the greater number of issues rather than their symptoms. + */ +#define VERIFY_STORAGE_CHAINS \ +{ \ + GBLREF uint4 gtmDebugLevel; \ + if (GDL_SmAllocVerf & gtmDebugLevel) \ + verifyAllocatedStorage(); \ + if (GDL_SmFreeVerf & gtmDebugLevel) \ + verifyFreeStorage(); \ +} + +#endif /* GTM_MALLOC_H__included */ diff --git a/sr_port/gtm_malloc_dbg.c b/sr_port/gtm_malloc_dbg.c new file mode 100644 index 0000000..93d399a --- /dev/null +++ b/sr_port/gtm_malloc_dbg.c @@ -0,0 +1,80 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* gtm_malloc_dbg -- the debugging version + + The bulk of the GTM storage manager code now sits in gtm_malloc_src.h and + is included twice: once in gtm_malloc.c and again in gtm_malloc_dbg.c. + The reason for this is that the production modules built for distribution + in the field can have both forms of storage mgmt available in the event + that it becomes necessary to chase a corruption issue. It can now be done + without resorting to a "debug" version. Several different levels of + debugging will also be made available to catch various problems. + + If the DEBUG flag is not defined (indicating a pro build), the gtm_malloc + module will expand without all the asserts and special checking making for + a compact and efficient storage manager. The gtm_malloc_dbg module will + expand AS IF DEBUG had been specified supplying an alternate assert filled + version of storage mgmnt with several different levels of storage validation + available. + + If the DEBUG flag is defined (debug or beta build), the gtm_malloc module + will expand with all debugging information intact and the gtm_malloc_dbg + module will expand as call backs to the gtm_malloc module since it makes + little sense to expand the identical module twice. +*/ + +#ifndef DEBUG + +/* We have a PRO build -- generate a full debug version with debug versions of + our global names */ + +# define gtmSmInit gtmSmInit_dbg +# define gtm_malloc gtm_malloc_dbg +# define gtm_free gtm_free_dbg +# define findStorElem findStorElem_dbg +# define processDeferredFrees processDeferredFrees_dbg +# define release_unused_storage release_unused_storage_dbg +# define raise_gtmmemory_error raise_gtmmemory_error_dbg +# define gtm_bestfitsize gtm_bestfitsize_dbg +# define DEBUG +# define PRO_BUILD +# define GTM_MALLOC_DEBUG +# include "caller_id.h" +# include "gtm_malloc_src.h" + +#else + +/* We have a DEBUG build -- Nobody should call gtm_malloc_dbg directly */ +# include "mdef.h" +# include "gtm_malloc.h" +/* Include some defs for these rtns to keep the compiler quiet for this routine. + Nobody should be calling these directly so we don't want them where they can + get included anywhere else. + + Note the real versions of these routines are defined and only used/callable from + the gtm_malloc_src.h include so when we define them here for completeness in a dbg + build, we change the return signature to not return anything (saves us from having + to put a "return" after the GTMASSERTS). These are just "catchalls" in case the + expansion functioned incorrectly. +*/ +void gtm_malloc_dbg(size_t size); +void gtm_free_dbg(void *addr); +void gtm_malloc_dbg(size_t size) +{ + GTMASSERT; +} + +void gtm_free_dbg(void *addr) +{ + GTMASSERT; +} +#endif diff --git a/sr_port/gtm_malloc_src.h b/sr_port/gtm_malloc_src.h new file mode 100644 index 0000000..415b633 --- /dev/null +++ b/sr_port/gtm_malloc_src.h @@ -0,0 +1,1494 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* Storage manager for "smaller" pieces of storage. Uses power-of-two + "buddy" system as described by Knuth. Currently manages pieces of + size 2K - SIZEOF(header). + + This include file is included in both gtm_malloc.c and gtm_malloc_dbg.c. + See the headers of those modules for explanations of how the storage + manager build is actually accomplished. + + Debugging is controlled via the "gtmdbglvl" environment variable in + the Unix environment and the GTM$DBGLVL logical in the VMS environment. + If this variable is set to a non-zero value, the debugging environment + is enabled. The debugging features turned on will correspond to the bit + values defined gtmdbglvl.h. Note that this mechanism is versatile enough + that non-storage-managment debugging is also hooked in here. The + debugging desired is a mask for the features desired. For example, if the + value 4 is set, then tracing is enabled. If the value is set to 6, then + both tracing and statistics are enabled. Because the code is expanded + twice in a "Pro" build, these debugging features are available even + in a pro build and can thus be enabled in the field without the need for + a "debug" version to be installed in order to chase a corruption or other + problem. +*/ + +#include "mdef.h" + +/* We are the redefined versions so use real versions in this module */ +#undef malloc +#undef free + +#include +#include +#include +#include +#include +#include +#if !defined(VMS) && !defined(__MVS__) +#include +#endif +#include "gtm_stdio.h" +#include "gtm_string.h" +#include "gtm_stdlib.h" + +#include "eintr_wrappers.h" +#include "gtmdbglvl.h" +#include "io.h" +#include "iosp.h" +#include "min_max.h" +#include "mdq.h" +#include "error.h" +#include "trans_log_name.h" +#include "gtmmsg.h" +#include "print_exit_stats.h" +#include "mmemory.h" +#include "gtm_logicals.h" +#include "cache.h" +#include "gtm_malloc.h" +#include "have_crit.h" +#ifdef UNIX +#include "deferred_signal_handler.h" +#endif + +/* This routine is compiled twice, once as debug and once as pro and put into the same pro build. The alternative + * memory manager is selected with the debug flags (any non-zero gtmdbglvl setting invokes debug memory manager in + * a pro build). So the global variables (defined using the STATICD macro) have to be two different fields. + * One for pro, one for dbg. The fields have different values and different sizes between the two compiles but + * exist in the same build. They cannot coexist. That is why STATICD is defined to be static for PRO and GBLDEF for DBG. + * This is the reason why we cannot use the STATICDEF macro here because that is defined to be a GBLDEF for PRO and DBG. + * + * To debug this routine effectively, normally static routines are turned into GBLDEFs. Also, for vars that + * need one copy, define GBLRDEF to GBLDEF for debug and GBLREF for pro. This is because the pro builds always + * have a debug version in them satisfiying the GBLREF but the debug builds won't have any pro code in them so + * the define must be in the debug version. Also note that we cannot use the STATICDEF macro (instead of the + * STATICD below) since that evaluates to a GBLDEF in both PRO and DBG which + */ +#ifdef DEBUG +# define STATICD GBLDEF +# define STATICR extern +# define GBLRDEF GBLDEF +#else +# define STATICD static +# define STATICR static +# define GBLRDEF GBLREF +#endif + +#ifdef GTM64 +# define gmaAdr "%016lx" +# define gmaFill " " +# define gmaLine "--------" +#else +# define gmaAdr "%08lx" +# define gmaFill " " +# define gmaLine " " +#endif + +#ifdef VMS +/* These routines for VMS are AST-safe */ +# define MALLOC(size, addr) \ +{ \ + int msize, errnum; \ + void *maddr; \ + msize = size; \ + errnum = lib$get_vm(&msize, &maddr); \ + if (SS$_NORMAL != errnum) \ + { \ + gtmMallocErrorSize = size; \ + gtmMallocErrorCallerid = CALLERID; \ + gtmMallocErrorErrno = errnum; \ + raise_gtmmemory_error(); \ + } \ + addr = (void *)maddr; \ +} +# define FREE(size, addr) \ +{ \ + int msize, errnum; \ + void *maddr; \ + msize = size; \ + maddr = addr; \ + errnum = lib$free_vm(&msize, &maddr); \ + if (SS$_NORMAL != errnum) \ + { \ + --gtmMallocDepth; \ + assert(FALSE); \ + rts_error(VARLSTCNT(4) ERR_FREEMEMORY, 1, CALLERID, errnum); \ + } \ +} +# define GTM_MALLOC_REENT +#else +/* These routines for Unix are NOT thread-safe */ +# define MALLOC(size, addr) \ +{ \ + addr = (void *)malloc(size); \ + if (NULL == (void *)addr) \ + { \ + gtmMallocErrorSize = size; \ + gtmMallocErrorCallerid = CALLERID; \ + gtmMallocErrorErrno = errno; \ + raise_gtmmemory_error(); \ + } \ +} +# define FREE(size, addr) free(addr); +#endif +#ifdef GTM_MALLOC_REENT +# define GMR_ONLY(statement) statement +# define NON_GMR_ONLY(statement) +#else +# define GMR_ONLY(statement) +# define NON_GMR_ONLY(statement) statement +#endif + +#ifdef DEBUG +/* States a storage element may be in. Debug version has values less likely to occur naturally + although the possibilities are limited with only one byte of information. */ +enum ElemState {Allocated = 0x42, Free = 0x24}; +#else +enum ElemState {Allocated = 1, Free}; /* 0 is just "too commonly occurring" */ +#endif + +/* At the end of each block is this header which is used to track when all of the elements that + a block of real allocated storage was broken into have become free. At that point, we can return + the chunk to the OS. +*/ +typedef struct storExtHdrStruct +{ + struct + { + struct storExtHdrStruct *fl, *bl; /* In case we need to visit the entire list */ + } links; + unsigned char *extentStart; /* First byte of real extent (not aligned) */ + storElem *elemStart; /* Start of array of MAXTWO elements */ + int elemsAllocd; /* MAXTWO sized element count. When 0 this block is free */ +} storExtHdr; + +#define MAXTWO 2048 +/* How many "MAXTWO" elements to allocate at one time. This minimizes the waste since our subblocks must + be aligned on a suitable power of two boundary for the buddy-system to work properly +*/ +#define ELEMS_PER_EXTENT 16 + +#define MAXDEFERQUEUES 10 +#ifdef DEBUG +# define STOR_EXTENTS_KEEP 1 /* Keep only one extent in debug for maximum testing */ +# define MINTWO NON_GTM64_ONLY(64) GTM64_ONLY(128) +# define MAXINDEX NON_GTM64_ONLY(5) GTM64_ONLY(4) +# define STE_FP(p) p->fPtr +# define STE_BP(p) p->bPtr +#else +# define STOR_EXTENTS_KEEP 5 +# define MINTWO NON_GTM64_ONLY(16) GTM64_ONLY(32) +# define MAXINDEX NON_GTM64_ONLY(7) GTM64_ONLY(6) +# define STE_FP(p) p->userStorage.links.fPtr +# define STE_BP(p) p->userStorage.links.bPtr +#endif +/* Our extent must be aligned on a MAXTWO byte boundary hence we allocate one more extent than + we actually want so we can be guarranteed usable storage. However if that allocation actually + starts on a MAXTWO boundary (on guarranteed 8 byte boundary), then we get an extra element. + Here we define our extent size and provide an initial sanity value for "extent_used". If the + allocator ever gets this extra block, this field will be increased by the size of one element + to compensate. +*/ +#define EXTENT_SIZE ((MAXTWO * (ELEMS_PER_EXTENT + 1)) + SIZEOF(storExtHdr)) +static unsigned int extent_used = (MAXTWO * ELEMS_PER_EXTENT + SIZEOF(storExtHdr)); +/* Following are values used in queueIndex in a storage element. Note that both + values must be less than zero for the current code to function correctly. */ +#define QUEUE_ANCHOR -1 +#define REAL_MALLOC -2 + +/* Define number of malloc and free calls we will keep track of */ +#define MAXSMTRACE 128 + +/* Structure where malloc and free call trace information is kept */ +typedef struct +{ + unsigned char *smAddr; /* Addr allocated or released */ + unsigned char *smCaller; /* Who called malloc/free */ + gtm_msize_t smSize; /* Size allocated or freed */ + gtm_msize_t smTn; /* What transaction it was */ +} smTraceItem; + +#ifdef DEBUG +# define INCR_CNTR(x) ++x +# define INCR_SUM(x, y) x += y +# define DECR_CNTR(x) --x +# define DECR_SUM(x, y) x -= y +# define SET_MAX(max, tst) {max = MAX(max, tst);} +# define SET_ELEM_MAX(qtype, idx) SET_MAX(qtype##ElemMax[idx], qtype##ElemCnt[idx]) +# define TRACE_MALLOC(addr, len, tn) \ +{ \ + if (GDL_SmTrace & gtmDebugLevel) \ + DBGFPF((stdout, "Malloc at 0x%lx of %ld bytes from 0x%lx (tn=%ld)\n", addr, len, CALLERID, tn)); \ +} +# define TRACE_FREE(addr, len, tn) \ +{ \ + if (GDL_SmTrace & gtmDebugLevel) \ + DBGFPF((stdout,"Free at 0x%lx of %d bytes from 0x%lx (tn=%ld)\n", addr, len, CALLERID, tn)); \ +} +#else +# define INCR_CNTR(x) +# define INCR_SUM(x, y) +# define DECR_CNTR(x) +# define DECR_SUM(x, y) +# define SET_MAX(max, tst) +# define SET_ELEM_MAX(qtype, idx) +# define TRACE_MALLOC(addr, len, tn) +# define TRACE_FREE(addr, len, tn) +#endif +#ifdef DEBUG_SM +# define DEBUGSM(x) (PRINTF x, fflush(stdout)) +# else +# define DEBUGSM(x) +#endif +/* Note we use unsigned char * instead of caddr_t for all references to caller_id so the caller id + is always 4 bytes. On Tru64, caddr_t is 8 bytes which will throw off the size of our + storage header in debug mode */ +#ifdef GTM_MALLOC_DEBUG +# define CALLERID (smCallerId) +#else +# define CALLERID ((unsigned char *)caller_id()) +#endif + +/* Define "routines" to enqueue and dequeue storage elements. Use define so we don't + have to depend on each implementation's compiler inlining to get efficient code here */ +#define ENQUEUE_STOR_ELEM(qtype, idx, elem) \ +{ \ + storElem *qHdr, *fElem; \ + qHdr = &qtype##StorElemQs[idx]; \ + STE_FP(elem) = fElem = STE_FP(qHdr); \ + STE_BP(elem) = qHdr; \ + STE_FP(qHdr) = STE_BP(fElem) = elem; \ + INCR_CNTR(qtype##ElemCnt[idx]); \ + SET_ELEM_MAX(qtype, idx); \ +} + +#define DEQUEUE_STOR_ELEM(qtype, elem) \ +{ \ + STE_FP(STE_BP(elem)) = STE_FP(elem); \ + STE_BP(STE_FP(elem)) = STE_BP(elem); \ + DECR_CNTR(qtype##ElemCnt[elem->queueIndex]); \ +} + +#define GET_QUEUED_ELEMENT(sizeIndex, uStor, qHdr, sEHdr) \ +{ \ + qHdr = &freeStorElemQs[sizeIndex]; \ + uStor = STE_FP(qHdr); /* First element on queue */ \ + if (QUEUE_ANCHOR != uStor->queueIndex) /* Does element exist? (Does queue point to itself?) */ \ + { \ + DEQUEUE_STOR_ELEM(free, uStor); /* It exists, dequeue it for use */ \ + if (MAXINDEX == sizeIndex) \ + { /* Allocating a MAXTWO block. Increment use counter for this subblock's block */ \ + sEHdr = (storExtHdr *)((char *)uStor + uStor->extHdrOffset); \ + ++sEHdr->elemsAllocd; \ + } \ + } else \ + uStor = findStorElem(sizeIndex); \ + assert(0 == ((unsigned long)uStor & (TwoTable[sizeIndex] - 1))); /* Verify alignment */ \ +} + +#ifdef INT8_SUPPORTED +# define ChunkSize 8 +# define ChunkType gtm_int64_t +# define ChunkValue 0xdeadbeefdeadbeefLL +#else +# define ChunkSize 4 +# define ChunkType int4 +# define ChunkValue 0xdeadbeef +#endif +#define AddrMask (ChunkSize - 1) + +#ifdef DEBUG +/* For debug builds, keep track of the last MAXSMTRACE mallocs and frees. */ +GBLDEF volatile int smLastMallocIndex; /* Index to entry of last malloc-er */ +GBLDEF volatile int smLastFreeIndex; /* Index to entry of last free-er */ +GBLDEF smTraceItem smMallocs[MAXSMTRACE]; /* Array of recent allocators */ +GBLDEF smTraceItem smFrees[MAXSMTRACE]; /* Array of recent releasers */ +GBLDEF volatile unsigned int smTn; /* Storage management (wrappable) transaction number */ +GBLDEF unsigned int outOfMemorySmTn; /* smTN when ran out of memory */ +#endif + +GBLREF uint4 gtmDebugLevel; /* Debug level (0 = using default sm module so with + a DEBUG build, even level 0 implies basic debugging) */ +GBLREF int process_exiting; /* Process is on it's way out */ +GBLREF volatile int4 gtmMallocDepth; /* Recursion indicator. Volatile so it gets stored immediately */ +GBLREF volatile void *outOfMemoryMitigation; /* Reserve that we will freed to help cleanup if run out of memory */ +GBLREF uint4 outOfMemoryMitigateSize; /* Size of above reserve in Kbytes */ +GBLREF int mcavail; +GBLREF mcalloc_hdr *mcavailptr, *mcavailbase; +GBLREF void (*cache_table_relobjs)(void); /* Function pointer to call cache_table_rebuild() */ +UNIX_ONLY(GBLREF ch_ret_type (*ht_rhash_ch)();) /* Function pointer to hashtab_rehash_ch */ +UNIX_ONLY(GBLREF ch_ret_type (*jbxm_dump_ch)();) /* Function pointer to jobexam_dump_ch */ +/* This var allows us to call ourselves but still have callerid info */ +GBLREF unsigned char *smCallerId; /* Caller of top level malloc/free */ +GBLREF volatile int4 fast_lock_count; /* Stop stale/epoch processing while we have our parts exposed */ + +OS_PAGE_SIZE_DECLARE + +#define SIZETABLEDIM MAXTWO/MINTWO +STATICD int size2Index[SIZETABLEDIM]; + +GBLRDEF boolean_t gtmSmInitialized; /* Initialized indicator */ +GBLRDEF size_t gtmMallocErrorSize; /* Size of last failed malloc */ +GBLRDEF unsigned char *gtmMallocErrorCallerid; /* Callerid of last failed malloc */ +GBLRDEF int gtmMallocErrorErrno; /* Errno at point of last failure */ + +GBLRDEF readonly struct +{ + unsigned char nullHMark[4]; + unsigned char nullStr[1]; + unsigned char nullTMark[4]; +} NullStruct +#ifdef DEBUG + /* Note, tiz important the first 4 bytes of this are same as markerChar defined below as that is the value both nullHMark + * and nullTMark are asserted against to validate against corruption. + */ + = {0xde, 0xad, 0xbe, 0xef, 0x00, 0xde, 0xad, 0xbe, 0xef} +#endif + ; + +#ifdef DEBUG +/* Arrays allocated with size of MAXINDEX + 2 are sized to hold an extra + * entry for "real malloc" type allocations. Note that the arrays start with + * the next larger element with GTM64 due to increased overhead from the + * 8 byte pointers. + */ + +STATICD readonly uint4 TwoTable[MAXINDEX + 2] = { +# ifndef GTM64 + 64, +# endif + 128, 256, 512, 1024, 2048, 0xFFFFFFFF}; /* Powers of two element sizes */ + +# ifdef GTM64 +STATICD readonly unsigned char markerChar[8] = {0xde, 0xad, 0xbe, 0xef, 0xef, 0xbe, 0xad, 0xde}; +# else +STATICD readonly unsigned char markerChar[4] = {0xde, 0xad, 0xbe, 0xef}; +# endif +#else +STATICD readonly uint4 TwoTable[MAXINDEX + 2] = { +# ifndef GTM64 + 16, +# endif + 32, 64, 128, 256, 512, 1024, 2048, 0xFFFFFFFF}; +#endif + +STATICD storElem freeStorElemQs[MAXINDEX + 1]; /* Need full element as queue anchor for dbl-linked + list since ptrs not at top of element */ +STATICD storExtHdr storExtHdrQ; /* List of storage blocks we allocate here */ +STATICD uint4 curExtents; /* Number of current extents */ + +#ifdef GTM_MALLOC_REENT +STATICD storElem *deferFreeQueues[MAXDEFERQUEUES]; /* Where deferred (nested) frees are queued for later processing */ +STATICD boolean_t deferFreeExists; /* A deferred free is pending on a queue */ +#endif + +#ifdef DEBUG +STATICD storElem allocStorElemQs[MAXINDEX + 2]; /* The extra element is for queueing "real" malloc'd entries */ +# ifdef INT8_SUPPORTED +STATICD readonly unsigned char backfillMarkC[8] = {0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef}; +# else +STATICD readonly unsigned char backfillMarkC[4] = {0xde, 0xad, 0xbe, 0xef}; +# endif +#endif + +GBLREF size_t totalRmalloc; /* Total storage currently (real) malloc'd (includes extent blocks) */ +GBLREF size_t totalAlloc; /* Total allocated (includes allocation overhead but not free space */ +GBLREF size_t totalUsed; /* Sum of user allocated portions (totalAlloc - overhead) */ + +#ifdef DEBUG +/* Define variables used to instrument how our algorithm works */ +STATICD uint4 totalMallocs; /* Total malloc requests */ +STATICD uint4 totalFrees; /* Total free requests */ +STATICD uint4 totalExtents; /* Times we allocated more storage */ +STATICD uint4 maxExtents; /* Highwater mark of extents */ +STATICD size_t rmallocMax; /* Maximum value of totalRmalloc */ +STATICD uint4 mallocCnt[MAXINDEX + 2]; /* Malloc count satisfied by each queue size */ +STATICD uint4 freeCnt[MAXINDEX + 2]; /* Free count for element in each queue size */ +STATICD uint4 elemSplits[MAXINDEX + 2]; /* Times a given queue size block was split */ +STATICD uint4 elemCombines[MAXINDEX + 2]; /* Times a given queue block was formed by buddies being recombined */ +STATICD uint4 freeElemCnt[MAXINDEX + 2]; /* Current count of elements on the free queue */ +STATICD uint4 allocElemCnt[MAXINDEX + 2]; /* Current count of elements on the allocated queue */ +STATICD uint4 freeElemMax[MAXINDEX + 2]; /* Maximum number of blocks on the free queue */ +STATICD uint4 allocElemMax[MAXINDEX + 2]; /* Maximum number of blocks on the allocated queue */ +GMR_ONLY(STATICD uint4 reentMallocs;) /* Total number of reentrant mallocs made */ +GMR_ONLY(STATICD uint4 deferFreePending;) /* Total number of frees that were deferred */ +#endif + +/* Macro to return an index into the TwoTable for a given size (round up to next power of two) + Use the size2Index table to get the proper index. This table is indexed by the number of + storage "blocks" being requested. A storage block is the size of the smallest power of two + block we can allocate (size MINTWO) */ +#ifdef DEBUG +# define GetSizeIndex(size) (size ? size2Index[(size - 1) / MINTWO] : assert(FALSE)) +#else +# define GetSizeIndex(size) (size2Index[(size - 1) / MINTWO]) +#endif + +/* Internal prototypes */ +void gtmSmInit(void); +storElem *findStorElem(int sizeIndex); +void release_unused_storage(void); +#ifdef DEBUG +void backfill(unsigned char *ptr, gtm_msize_t len); +boolean_t backfillChk(unsigned char *ptr, gtm_msize_t len); +#else +void *gtm_malloc_dbg(size_t); +void gtm_free_dbg(void *); +void raise_gtmmemory_error_dbg(void); +size_t gtm_bestfitsize_dbg(size_t); +#endif + +/* Initialize the storage manangement system. Things to initialize: + + - Initialize size2Index table. This table is used to convert a malloc request size + to a storage queue index. + - Initialize queue anchor fwd/bkwd pointers to point to queue anchors so we + build a circular queue. This allows elements to be added and removed without + end-of-queue special casing. The queue anchor element is easily recognized because + it's queue index size will be set to a special value. + - Initialize debug mode. See if gtm_debug_level environment variable is set and + retrieve it's value if yes. +*/ +void gtmSmInit(void) /* Note renamed to gtmSmInit_dbg when included in gtm_malloc_dbg.c */ +{ + char *ascNum; + storElem *uStor; + int i, sizeIndex, testSize, blockSize, save_errno; + + error_def(ERR_INVMEMRESRV); + + /* WARNING!! Since this is early initialization, the following asserts are not well behaved if they do + indeed trip. The best that can be hoped for is they give a condition handler exhausted error on + GTM startup. Unfortunately, more intelligent responses are somewhat elusive since no output devices + are setup nor (potentially) most of the GTM runtime. + */ + assert(MINTWO == TwoTable[0]); +# if defined(__linux__) && !defined(__i386) + /* This will make sure that all the memory allocated using 'malloc' will be in heap and no 'mmap' is used. + * This is needed to make sure that the offset calculation that we do at places(que_ent, chache_que, etc..) + * using 2 'malloc'ed memory can be hold in an integer. Though this will work without any problem as the + * current GT.M will not allocate memory more than 4GB, we should find a permanant solution by migrating those + * offset fields to long and make sure all other related application logic works fine. + */ + mallopt(M_MMAP_MAX, 0); +# endif /* __linux__ && !__i386 */ + /* Check that the storage queue offset in a storage element has sufficient reach + to cover an extent. + */ + assert(((extent_used - SIZEOF(storExtHdr)) <= ((1 << (SIZEOF(uStor->extHdrOffset) * 8)) - 1))); + + /* Initialize size table used to get a storage queue index */ + sizeIndex = 0; + testSize = blockSize = MINTWO; + for (i = 0; i < SIZETABLEDIM; i++, testSize += blockSize) + { + if (testSize > TwoTable[sizeIndex]) + ++sizeIndex; + size2Index[i] = sizeIndex; + } + + /* Need to initialize the fwd/bck ptrs in the anchors to point to themselves */ + for (uStor = &freeStorElemQs[0], i = 0; i <= MAXINDEX; ++i, ++uStor) + { + STE_FP(uStor) = STE_BP(uStor) = uStor; + uStor->queueIndex = QUEUE_ANCHOR; + } + DEBUG_ONLY( + for (uStor = &allocStorElemQs[0], i = 0; i <= (MAXINDEX + 1); ++i, ++uStor) + { + STE_FP(uStor) = STE_BP(uStor) = uStor; + uStor->queueIndex = QUEUE_ANCHOR; + } + ); + dqinit(&storExtHdrQ, links); + + /* One last task before we consider ourselves initialized. Allocate the out-of-memory mitigation storage + that we will hold onto but not use. If we get an out-of-memory error, this storage will be released back + to the OS for it or GTM to use as necessary while we try to go about an orderly shutdown of our process. + The term "release" here means a literal release. The thinking is we don't know whether GTM's small storage + manager will make use of this storage (32K at a time) or if a larger malloc() will be done by libc for + buffers or what not so we will just give this chunk back to the OS to use as it needs it. + */ + if (0 < outOfMemoryMitigateSize) + { + assert(NULL == outOfMemoryMitigation); + outOfMemoryMitigation = malloc(outOfMemoryMitigateSize * 1024); + if (NULL == outOfMemoryMitigation) + { + save_errno = errno; + gtm_putmsg(VARLSTCNT(5) ERR_INVMEMRESRV, 2, + RTS_ERROR_LITERAL(UNIX_ONLY("$gtm_memory_reserve")VMS_ONLY("GTM_MEMORY_RESERVE")), + save_errno); + exit(save_errno); + } + } + gtmSmInitialized = TRUE; +} + +/* Recursive routine used to obtain an element on a given size queue. If no + elements of that size are available, we recursively call ourselves to get + an element of the next larger queue which we will then split in half to + get the one we need and place the remainder back on the free queue of its + new smaller size. If we run out of queues, we obtain a fresh new 'hunk' of + storage, carve it up into the largest block size we handle and process as + before. */ +storElem *findStorElem(int sizeIndex) /* Note renamed to findStorElem_dbg when included in gtm_malloc_dbg.c */ +{ + unsigned char *uStorAlloc; + storElem *uStor, *uStor2, *qHdr; + storExtHdr *sEHdr; + int hdrSize; + unsigned int i; + + UNIX_ONLY(error_def(ERR_SYSCALL);) + + ++sizeIndex; + DEBUG_ONLY(hdrSize = OFFSETOF(storElem, userStorage)); /* Size of storElem header */ + if (MAXINDEX >= sizeIndex) + { /* We have more queues to search */ + GET_QUEUED_ELEMENT(sizeIndex, uStor, qHdr, sEHdr); + + /* We have a larger than necessary element now so break it in half and put + the second half on the queue one size smaller than us */ + INCR_CNTR(elemSplits[sizeIndex]); + --sizeIndex; /* Dealing now with smaller element queue */ + assert(sizeIndex >= 0 && sizeIndex < MAXINDEX); + uStor2 = (storElem *)((unsigned long)uStor + TwoTable[sizeIndex]); + uStor2->state = Free; + uStor2->queueIndex = sizeIndex; + assert(0 == ((unsigned long)uStor2 & (TwoTable[sizeIndex] - 1))); /* Verify alignment */ + DEBUG_ONLY( + memcpy(uStor2->headMarker, markerChar, SIZEOF(uStor2->headMarker)); /* Put header tag in place */ + /* Backfill entire block being freed so usage of it will cause problems */ + if (GDL_SmBackfill & gtmDebugLevel) + backfill((unsigned char *)uStor2 + hdrSize, TwoTable[sizeIndex] - hdrSize); + ); + ENQUEUE_STOR_ELEM(free, sizeIndex, uStor2); /* Place on free queue */ + } else + { /* Nothing left to search, [real]malloc a new ALIGNED block of storage and put it on our queues */ + ++curExtents; + SET_MAX(maxExtents, curExtents); + INCR_CNTR(totalExtents); + /* Allocate size for one more subblock than we want. This guarrantees us that we can put our subblocks + on a power of two boundary necessary for buddy alignment + */ + MALLOC(EXTENT_SIZE, uStorAlloc); + uStor2 = (storElem *)uStorAlloc; + /* Make addr "MAXTWO" byte aligned */ + uStor = (storElem *)(((unsigned long)(uStor2) + MAXTWO - 1) & (unsigned long) -MAXTWO); + totalRmalloc += EXTENT_SIZE; + SET_MAX(rmallocMax, totalRmalloc); + sEHdr = (storExtHdr *)((char *)uStor + (ELEMS_PER_EXTENT * MAXTWO)); + DEBUGSM(("debugsm: Allocating extent at 0x%08lx\n", uStor)); + + /* If the storage given to us was aligned, we have ELEMS_PER_EXTENT+1 blocks, else we have + ELEMS_PER_EXTENT blocks. We won't put the first element on the queue since that block is + being returned to be split. + */ + if (uStor == uStor2) + { + i = 0; /* The storage was suitably aligned, we get an extra block free */ + sEHdr = (storExtHdr *)((char *)sEHdr + MAXTWO); + extent_used = EXTENT_SIZE; /* New max for sanity checks */ + } else + i = 1; /* The storage was not aligned. Have planned number of blocks with some waste */ + assert(((char *)sEHdr + SIZEOF(*sEHdr)) <= ((char *)uStorAlloc + EXTENT_SIZE)); + for (uStor2 = uStor; ELEMS_PER_EXTENT > i; ++i) + { /* Place all but first entry on the queue */ + uStor2 = (storElem *)((unsigned long)uStor2 + MAXTWO); + assert(0 == ((unsigned long)uStor2 & (TwoTable[MAXINDEX] - 1))); /* Verify alignment */ + uStor2->state = Free; + uStor2->queueIndex = MAXINDEX; + uStor2->extHdrOffset = (char *)sEHdr - (char *)uStor2; + assert(extent_used > uStor2->extHdrOffset); + DEBUG_ONLY( + memcpy(uStor2->headMarker, markerChar, SIZEOF(uStor2->headMarker)); + /* Backfill entire block on free queue so we can detect trouble + * with premature usage or overflow from something else + */ + if (GDL_SmBackfill & gtmDebugLevel) + backfill((unsigned char *)uStor2 + hdrSize, TwoTable[MAXINDEX] - hdrSize); + ); + ENQUEUE_STOR_ELEM(free, MAXINDEX, uStor2); /* Place on free queue */ + } + uStor->extHdrOffset = (char *)sEHdr - (char *)uStor; + uStor->state = Free; + sizeIndex = MAXINDEX; + /* Set up storage block header */ + sEHdr->extentStart = uStorAlloc; + sEHdr->elemStart = uStor; + sEHdr->elemsAllocd = 1; + dqins(&storExtHdrQ, links, sEHdr); + } + assert(sizeIndex >= 0 && sizeIndex <= MAXINDEX); + uStor->queueIndex = sizeIndex; /* This is now a smaller block */ + return uStor; +} + +#ifdef GTM_MALLOC_REENT +/* Routine to process deferred frees in the deferred free queues */ +void processDeferredFrees() /* Note renamed to processDeferredFrees_dbg when included in gtm_malloc_dbg.c */ +{ + int dqIndex; + storElem *uStor, *uStorNext; + VMS_ONLY(error_def(ERR_FREEMEMORY);) + + assert(0 == gtmMallocDepth); + do + { + deferFreeExists = FALSE; + /* Run queue in reverse order so we can process the highest index queues first freeing them + up that much sooner. This eliminates the problem of index creep. */ + for (dqIndex = MAXDEFERQUEUES - 1; 0 <= dqIndex; --dqIndex) + { + /* Check if queue is empty or not once outside of the gtmMallocDepth lock 'cause + we don't want to get the lock unless we really need to */ + if (deferFreeQueues[dqIndex]) + { + gtmMallocDepth = dqIndex + 2; + uStor = deferFreeQueues[dqIndex]; /* Dequeue entire chain at this location */ + deferFreeQueues[dqIndex] = NULL; + gtmMallocDepth = 0; + for (; uStor; uStor = uStorNext) /* Release all elements on this queue */ + { + uStorNext = uStor->userStorage.deferFreeNext; + gtm_free(&uStor->userStorage.userStart); + } + } + } + } while (deferFreeExists); +} +#endif + + +/* Note, if the below declaration changes, corresponding changes in gtmxc_types.h needs to be done. */ +/* Obtain free storage of the given size */ +void *gtm_malloc(size_t size) /* Note renamed to gtm_malloc_dbg when included in gtm_malloc_dbg.c */ +{ + unsigned char *retVal; + storElem *uStor, *qHdr; + storExtHdr *sEHdr; + gtm_msize_t tSize; + int sizeIndex, i, hdrSize; + unsigned char *trailerMarker; + boolean_t reentered; + + UNIX_ONLY(error_def(ERR_SYSCALL);) + error_def(ERR_MEMORYRECURSIVE); + +# ifndef DEBUG + /* If we are not expanding for DEBUG, check now if DEBUG has been turned on. + If it has, we are in the wrong module Jack. This IF is structured so that + if this is the normal (default/optimized) case we will fall into the code + and handle the rerouting at the end. */ + if (GDL_None == gtmDebugLevel) + { +# endif + /* Note that this if is also structured for maximum fallthru. The else will + be near the end of this entry point */ + if (gtmSmInitialized) + { + hdrSize = OFFSETOF(storElem, userStorage); /* Size of storElem header */ + NON_GTM64_ONLY(if ((size + hdrSize) < size) GTMASSERT); /* Check for wrap in 32 bit platforms */ + assert((hdrSize + SIZEOF(markerChar)) < MINTWO); + + NON_GMR_ONLY(fast_lock_count++); + ++gtmMallocDepth; /* Nesting depth of memory calls */ + reentered = (1 < gtmMallocDepth); + NON_GMR_ONLY( + if (reentered) + { + --gtmMallocDepth; + assert(FALSE); + rts_error(VARLSTCNT(1) ERR_MEMORYRECURSIVE); + } + ); + INCR_CNTR(totalMallocs); + INCR_CNTR(smTn); + /* Validate null string not overwritten */ + assert(0 == memcmp(&NullStruct.nullHMark[0], markerChar, SIZEOF(NullStruct.nullHMark))); + assert(0 == memcmp(&NullStruct.nullTMark[0], markerChar, SIZEOF(NullStruct.nullHMark))); + DEBUG_ONLY( + GMR_ONLY(if (!reentered)) + { /* Verify the storage chains before we play */ + VERIFY_STORAGE_CHAINS; + } + ); + if (0 != size) + { + GMR_ONLY(size = MAX(SIZEOF(char *), size);) /* Need room for deferred free next pointer */ + tSize = size + hdrSize; /* Add in header size */ + DEBUG_ONLY( + tSize += SIZEOF(markerChar); /* Add in room for trailer label */ + /* If being a storage hog, we want to make sure we have plenty of room for + * filler. For strings up to MAXTWO in length, we pad with an additional 50% + * of storage with a minimum of 32 bytes and a maximum of 256 bytes. For larger + * strings, we pad with 256 bytes. Since selecting GDL_SmStorHog also turns on + * GDL_SmBackfill and GDL_SmChkAllocBackfill, this padding will be backfilled and + * checked during allocate storage validation calls. + */ + if (GDL_SmStorHog & gtmDebugLevel) + { + if (MAXTWO >= size) + tSize += (MIN(MAX(size / 2, 32), 256)); + else + tSize += 256; + } + ); + /* The difference between $ZALLOCSTOR and $ZUSEDSTOR (totalAlloc and totalUsed global vars) is + * that when you allocate, say 16 bytes, that comes out of a 32 byte chunk (with the pro storage + * mgr) with the rest being unusable. In a debug build (or a pro build with $gtmdbglvl set to + * something non-zero), $ZUSEDSTOR is incremented by 16 bytes (the requested allocation) while + * $ZALLOCSTOR is incremented by 32 bytes (the actual allocation). But, in a pro build using + * the pro memory manager, we do not track the user-allocated size anywhere. We know it when + * we do the allocation of course, but when it comes time to free it, we no longer know what + * the user requested size was. We only know that it came out of a 32 byte block. In order for + * the free to be consistent with the allocation, we have to use the one value we know at both + * malloc and free times - 32 bytes. The net result is that $ZALLOCSTOR and $ZUSEDSTOR report + * the same value in a pro build with the pro stmgr while they will be quite different in a + * debug build or a pro build with $gtmdbglvl engaged. The difference between them shows the + * allocation overhead of gtm_malloc itself. + */ + if (MAXTWO >= tSize GMR_ONLY(&& !reentered)) + { /* Use our memory manager for smaller pieces */ + sizeIndex = GetSizeIndex(tSize); /* Get index to size we need */ + assert(sizeIndex >= 0 && sizeIndex <= MAXINDEX); + GET_QUEUED_ELEMENT(sizeIndex, uStor, qHdr, sEHdr); + tSize = TwoTable[sizeIndex]; + uStor->realLen = tSize; + } else + { /* Use regular malloc to obtain the piece */ + MALLOC(tSize, uStor); + totalRmalloc += tSize; + SET_MAX(rmallocMax, totalRmalloc); + uStor->queueIndex = REAL_MALLOC; + uStor->realLen = tSize; + DEBUG_ONLY(sizeIndex = MAXINDEX + 1); /* Just so the ENQUEUE below has a queue since + * we use -1 as the "real" queueindex for + * malloc'd storage and we don't record allocated + * storage in other than debug mode. */ + } + totalUsed += DEBUG_ONLY(size) PRO_ONLY(tSize); + totalAlloc += tSize; + INCR_CNTR(mallocCnt[sizeIndex]); + uStor->state = Allocated; +# ifdef DEBUG + /* Fill in extra debugging fields in header */ + uStor->allocatedBy = CALLERID; /* Who allocated us */ + uStor->allocLen = size; /* User requested size */ + memcpy(uStor->headMarker, markerChar, SIZEOF(uStor->headMarker)); + trailerMarker = (unsigned char *)&uStor->userStorage.userStart + size; /* Where to put trailer */ + memcpy(trailerMarker, markerChar, SIZEOF(markerChar)); /* Small trailer */ + if (GDL_SmInitAlloc & gtmDebugLevel) + /* Initialize the space we are allocating */ + backfill((unsigned char *)&uStor->userStorage.userStart, size); + if (GDL_SmBackfill & gtmDebugLevel) + { /* Use backfill method of after-allocation metadata */ + backfill(trailerMarker + SIZEOF(markerChar), + (uStor->realLen - size - hdrSize - SIZEOF(markerChar))); + } + + uStor->smTn = smTn; /* transaction number */ + GMR_ONLY(if (!reentered)) + { + ENQUEUE_STOR_ELEM(alloc, sizeIndex, uStor); + } +# ifdef GTM_MALLOC_REENT + else + { /* Reentrant allocates cannot be put on our allocated queue -- sorry too dangerous */ + uStor->fPtr = uStor->bPtr = NULL; + INCR_CNTR(allocElemCnt[sizeIndex]); + INCR_CNTR(reentMallocs); + } +# endif +# endif + retVal = &uStor->userStorage.userStart; + assert(((long)retVal & (long)-8) == (long)retVal); /* Assert we have an 8 byte boundary */ + } else /* size was 0 */ + retVal = &NullStruct.nullStr[0]; + DEBUG_ONLY( + /* Record this transaction in debugging history */ + ++smLastMallocIndex; + if (MAXSMTRACE <= smLastMallocIndex) + smLastMallocIndex = 0; + smMallocs[smLastMallocIndex].smAddr = retVal; + smMallocs[smLastMallocIndex].smSize = size; + smMallocs[smLastMallocIndex].smCaller = CALLERID; + smMallocs[smLastMallocIndex].smTn = smTn; + ); + TRACE_MALLOC(retVal, size, smTn); + + --gtmMallocDepth; + GMR_ONLY( + /* Check on deferred frees */ + if (0 == gtmMallocDepth && deferFreeExists) + processDeferredFrees(); + ); + NON_GMR_ONLY(--fast_lock_count); + DEFERRED_EXIT_HANDLING_CHECK; + return retVal; + } else /* Storage mgmt has not been initialized */ + { + gtmSmInit(); + /* Reinvoke gtm_malloc now that we are initialized. Note that this one time (the first + call to malloc), we will not record the proper caller id in the storage header or in + the traceback table. The caller will show up as gtm_malloc(). However, all subsequent + calls will be correct. + */ + return (void *)gtm_malloc(size); + } +# ifndef DEBUG + } else + { /* We have a non-DEBUG module but debugging is turned on so redirect the call to the appropriate module */ + smCallerId = (unsigned char *)caller_id(); + return (void *)gtm_malloc_dbg(size); + } +# endif +} + +/* Note, if the below declaration changes, corresponding changes in gtmxc_types.h needs to be done. */ +/* Release the free storage at the given address */ +void gtm_free(void *addr) /* Note renamed to gtm_free_dbg when included in gtm_malloc_dbg.c */ +{ + storElem *uStor, *buddyElem; + storExtHdr *sEHdr; + unsigned char *trailerMarker; + int sizeIndex, hdrSize, saveIndex, dqIndex, freedElemCnt; + gtm_msize_t saveSize, allocSize; + + error_def(ERR_MEMORYRECURSIVE); + UNIX_ONLY(error_def(ERR_MEMORY);) + VMS_ONLY(error_def(ERR_FREEMEMORY);) + VMS_ONLY(error_def(ERR_VMSMEMORY);) + +# ifndef DEBUG + /* If we are not expanding for DEBUG, check now if DEBUG has been turned on. + If it has, we are in the wrong module Jack. This IF is structured so that + if this is the normal (optimized) case we will fall into the code and + handle the rerouting at the end. */ + if (GDL_None == gtmDebugLevel) + { +# endif + if (!gtmSmInitialized) /* Storage must be init'd before can free anything */ + GTMASSERT; + /* If we are exiting, don't bother with frees. Process destruction can do it *UNLESS* we are handling an + out of memory condition with the proviso that we can't return memory if we are already nested */ + if (process_exiting && (0 != gtmMallocDepth || error_condition != UNIX_ONLY(ERR_MEMORY) VMS_ONLY(ERR_VMSMEMORY))) + return; + NON_GMR_ONLY(++fast_lock_count); + ++gtmMallocDepth; /* Recursion indicator */ + +# ifdef GTM_MALLOC_REENT + /* If we are attempting to do a reentrant free, we will instead put the free on a queue to be released + at a later time. Ironically, since we cannot be sure of any queues of available blocks, we have to + malloc a small block to carry this info which we will free with the main storage */ + if (1 < gtmMallocDepth) + { + if ((unsigned char *)addr != &NullStruct.nullStr[0]) + { + dqIndex = gtmMallocDepth - 2; /* 0 origin index into defer queues */ + if (MAXDEFERQUEUES <= dqIndex) /* Can't run out of queues */ + GTMASSERT; + hdrSize = offsetof(storElem, userStorage); + uStor = (storElem *)((unsigned long)addr - hdrSize); /* Backup ptr to element header */ + uStor->userStorage.deferFreeNext = deferFreeQueues[dqIndex]; + deferFreeQueues[dqIndex] = uStor; + deferFreeExists = TRUE; + INCR_CNTR(deferFreePending); + } + --gtmMallocDepth; + return; + } +# else + if (1 < gtmMallocDepth) + { + --gtmMallocDepth; + assert(FALSE); + rts_error(VARLSTCNT(1) ERR_MEMORYRECURSIVE); + } +# endif + INCR_CNTR(smTn); /* Bump the transaction number */ + + /* Validate null string not overwritten */ + assert(0 == memcmp(&NullStruct.nullHMark[0], markerChar, SIZEOF(NullStruct.nullHMark))); + assert(0 == memcmp(&NullStruct.nullTMark[0], markerChar, SIZEOF(NullStruct.nullHMark))); + /* verify chains before we attempt dequeue */ + DEBUG_ONLY(VERIFY_STORAGE_CHAINS); + INCR_CNTR(totalFrees); + if ((unsigned char *)addr != &NullStruct.nullStr[0]) + { + hdrSize = OFFSETOF(storElem, userStorage); + uStor = (storElem *)((unsigned long)addr - hdrSize); /* Backup ptr to element header */ + sizeIndex = uStor->queueIndex; +# ifdef DEBUG + if (GDL_SmInitAlloc & gtmDebugLevel) + /* Initialize the space we are de-allocating */ + backfill((unsigned char *)&uStor->userStorage.userStart, uStor->allocLen); + TRACE_FREE(addr, uStor->allocLen, smTn); + saveSize = uStor->allocLen; + /* Extra checking for debugging. Note that these sanity checks are only done in debug + mode. The thinking is that we will bypass the checks in the general case for speed but + if we really need to chase a storage related problem, we should switch to the debug version + in the field to turn on these and other checks. + */ + assert(Allocated == uStor->state); + assert(0 == memcmp(uStor->headMarker, markerChar, SIZEOF(uStor->headMarker))); + trailerMarker = (unsigned char *)&uStor->userStorage.userStart + uStor->allocLen;/* Where trailer was put */ + assert(0 == memcmp(trailerMarker, markerChar, SIZEOF(markerChar))); + if (GDL_SmChkAllocBackfill & gtmDebugLevel) + { /* Use backfill check method for after-allocation metadata */ + assert(backfillChk(trailerMarker + SIZEOF(markerChar), + (uStor->realLen - uStor->allocLen - hdrSize - SIZEOF(markerChar)))); + } + + /* Remove element from allocated queue unless element is from a reentered malloc call. In that case, just + * manipulate the counters. + */ + if (NULL != uStor->fPtr) + { + if (0 <= uStor->queueIndex) + { + DEQUEUE_STOR_ELEM(alloc, uStor); + } else + { /* shenanigans so that counts are maintained properly in debug mode */ + saveIndex = uStor->queueIndex; + uStor->queueIndex = MAXINDEX + 1; + DEQUEUE_STOR_ELEM(alloc, uStor); + uStor->queueIndex = saveIndex; + } + } else + DECR_CNTR(allocElemCnt[((0 <= uStor->queueIndex) ? uStor->queueIndex : MAXINDEX + 1)]); +# endif + totalUsed -= DEBUG_ONLY(uStor->allocLen) PRO_ONLY(uStor->realLen); + if (sizeIndex >= 0) + { /* We can put the storage back on one of our simple queues */ + assert(0 == ((unsigned long)uStor & (TwoTable[sizeIndex] - 1))); /* Verify alignment */ + assert(sizeIndex >= 0 && sizeIndex <= MAXINDEX); + uStor->state = Free; + INCR_CNTR(freeCnt[sizeIndex]); + assert(uStor->realLen == TwoTable[sizeIndex]); + totalAlloc -= TwoTable[sizeIndex]; + /* First, if there are larger queues than this one, see if it has a buddy that it can + combine with */ + while (sizeIndex < MAXINDEX) + { + buddyElem = (storElem *)((unsigned long)uStor ^ TwoTable[sizeIndex]);/* Address of buddy */ + assert(0 == ((unsigned long)buddyElem & (TwoTable[sizeIndex] - 1)));/* Verify alignment */ + assert(buddyElem->state == Allocated || buddyElem->state == Free); + assert(buddyElem->queueIndex >= 0 && buddyElem->queueIndex <= sizeIndex); + if (buddyElem->state == Allocated || buddyElem->queueIndex != sizeIndex) + /* All possible combines done */ + break; + + /* Remove buddy from its queue and make a larger element for a larger queue */ + DEQUEUE_STOR_ELEM(free, buddyElem); + if (buddyElem < uStor) /* Pick lower address buddy for top of new bigger block */ + uStor = buddyElem; + ++sizeIndex; + assert(sizeIndex >= 0 && sizeIndex <= MAXINDEX); + INCR_CNTR(elemCombines[sizeIndex]); + uStor->queueIndex = sizeIndex; + } + DEBUG_ONLY( + /* Backfill entire block being freed so usage of it will cause problems */ + if (GDL_SmBackfill & gtmDebugLevel) + backfill((unsigned char *)uStor + hdrSize, TwoTable[sizeIndex] - hdrSize); + ); + ENQUEUE_STOR_ELEM(free, sizeIndex, uStor); + if (MAXINDEX == sizeIndex) + { /* Freeing/Coagulating a MAXTWO block. Decrement use counter for this element's block */ + sEHdr = (storExtHdr *)((char *)uStor + uStor->extHdrOffset); + --sEHdr->elemsAllocd; + assert(0 <= sEHdr->elemsAllocd); + /* Check for an extent being ripe for return to the system. Requirements are: + 1) All subblocks must be free (elemsAllocd == 0). + 2) There must be more than STOR_EXTENTS_KEEP extents already allocated. + If these conditions are met, we will dequeue each individual element from + it's queue and release the entire extent in a (real) free. + */ + if (STOR_EXTENTS_KEEP < curExtents && 0 == sEHdr->elemsAllocd) + { /* Release this extent */ + DEBUGSM(("debugsm: Extent being freed from 0x%08lx\n", sEHdr->elemStart)); + DEBUG_ONLY(freedElemCnt = 0); + for (uStor = sEHdr->elemStart; + (char *)uStor < (char *)sEHdr; + uStor = (storElem *)((char *)uStor + MAXTWO)) + { + DEBUG_ONLY(++freedElemCnt); + assert(Free == uStor->state); + assert(MAXINDEX == uStor->queueIndex); + DEQUEUE_STOR_ELEM(free, uStor); + DEBUGSM(("debugsm: ... element removed from free q 0x%08lx\n", uStor)); + } + assert(ELEMS_PER_EXTENT <= freedElemCnt); /* one loop to free them all */ + assert((char *)uStor == (char *)sEHdr); + dqdel(sEHdr, links); + FREE(EXTENT_SIZE, sEHdr->extentStart); + totalRmalloc -= EXTENT_SIZE; + --curExtents; + assert(curExtents); + } + } + + } else + { + assert(REAL_MALLOC == sizeIndex); /* Better be a real malloc type block */ + INCR_CNTR(freeCnt[MAXINDEX + 1]); /* Count free of malloc */ + allocSize = saveSize = uStor->realLen; + DEBUG_ONLY( + /* Backfill entire block being freed so usage of it will cause problems */ + if (GDL_SmBackfill & gtmDebugLevel) + backfill((unsigned char *)uStor, allocSize); + ); + FREE(allocSize, uStor); + totalRmalloc -= allocSize; + totalAlloc -= allocSize; + } + } + DEBUG_ONLY( + /* Make trace entry for this free */ + ++smLastFreeIndex; + if (MAXSMTRACE <= smLastFreeIndex) + smLastFreeIndex = 0; + smFrees[smLastFreeIndex].smAddr = addr; + smFrees[smLastFreeIndex].smSize = saveSize; + smFrees[smLastFreeIndex].smCaller = CALLERID; + smFrees[smLastFreeIndex].smTn = smTn; + ); + --gtmMallocDepth; + GMR_ONLY( + /* Check on deferred frees */ + if (0 == gtmMallocDepth && deferFreeExists) + processDeferredFrees(); + ); + NON_GMR_ONLY(--fast_lock_count); +# ifndef DEBUG + } else + { /* If not a debug module and debugging is enabled, reroute call to + the debugging version. + */ + smCallerId = (unsigned char *)caller_id(); + gtm_free_dbg(addr); + } +# endif + DEFERRED_EXIT_HANDLING_CHECK; +} + + +/* When an out-of-storage type error is encountered, besides releasing our memory reserve, we also + want to release as much unused storage within various GTM queues that we can find. +*/ +void release_unused_storage(void) /* Note renamed to release_unused_storage_dbg when included in gtm_malloc_dbg.c */ +{ + mcalloc_hdr *curhdr, *nxthdr; + + /* Release compiler storage if we aren't in the compiling business currently */ + if (NULL != mcavailbase && mcavailptr == mcavailbase && mcavail == mcavailptr->size) + { /* Buffers are unused and subject to release */ + for (curhdr = mcavailbase; curhdr; curhdr = nxthdr) + { + nxthdr = curhdr->link; + gtm_free(curhdr); + } + mcavail = 0; + mcavailptr = mcavailbase = NULL; + } + /* If the cache_table_rebuild() routine is available in this executable, call it through its + function pointer. */ + if (NULL != cache_table_relobjs) + (*cache_table_relobjs)(); /* Release object code in indirect cache */ +} + + +/* Raise ERR_MEMORY or ERR_VMSMEMORY. Separate routine since is called from hashtable logic in place of the + previous HTEXPFAIL error message. As such, it checks and properly deals with which flavor is running + (debug or non-debug). +*/ +void raise_gtmmemory_error(void) /* Note renamed to raise_gtmmemory_error_dbg when included in gtm_malloc_dbg.c */ +{ + void *addr; + + VMS_ONLY(error_def(ERR_VMSMEMORY);) + UNIX_ONLY(error_def(ERR_MEMORY);) + +# ifndef DEBUG + /* If we are not expanding for DEBUG, check now if DEBUG has been turned on. + If it has, we are in the wrong module Jack. This IF is structured so that + if this is the normal (optimized) case we will fall into the code and + handle the rerouting at the end. + + Note: The DEBUG expansion of this code in a pro build actually has a different + entry point name (raise_gtmmeory_error_dbg) and if malloc debugging options are + on in pro, we need to call that version, but since efficiency in pro trumps + clarity, we put the redirecting call at the bottom of the if-else block to + avoid disrupting the instruction pipeline. + */ + if (GDL_None == gtmDebugLevel) + { +# endif + if (NULL != (addr = (void *)outOfMemoryMitigation) + UNIX_ONLY(&& !(ht_rhash_ch == active_ch->ch || jbxm_dump_ch == active_ch->ch || stp_gcol_ch == active_ch->ch))) + { /* Free our reserve only if not in certain condition handlers (on UNIX) since it is */ + /* going to unwind this error and ignore it. On VMS the error will not be trapped */ + outOfMemoryMitigation = NULL; + UNIX_ONLY(free(addr)); + VMS_ONLY(lib$free_vm(addr)); + DEBUG_ONLY(if (0 == outOfMemorySmTn) outOfMemorySmTn = smTn); + /* Must decr gtmMallocDepth after release above but before the */ + /* call to release_unused_storage() below. */ + --gtmMallocDepth; + release_unused_storage(); + } else + --gtmMallocDepth; + UNIX_ONLY(--fast_lock_count); + DEFERRED_EXIT_HANDLING_CHECK; + UNIX_ONLY(rts_error(VARLSTCNT(5) ERR_MEMORY, 2, gtmMallocErrorSize, gtmMallocErrorCallerid, gtmMallocErrorErrno)); + VMS_ONLY(rts_error(VARLSTCNT(4) ERR_VMSMEMORY, 2, gtmMallocErrorSize, gtmMallocErrorCallerid)); +# ifndef DEBUG + } else + /* If not a debug module and debugging is enabled, reroute call to + the debugging version. + */ + raise_gtmmemory_error_dbg(); +# endif +} + + +/* Return the maximum size that would fully utilize a storage block given the input size. If the size will not + fit in one of the buddy list queue elems, it is returned unchanged. Otherwise, the size of the buddy list queue + element minus the overhead will be returned as the best fit size. +*/ +size_t gtm_bestfitsize(size_t size) +{ + size_t tSize; + int hdrSize, sizeIndex; + +# ifndef DEBUG + /* If we are not expanding for DEBUG, check now if DEBUG has been turned on. + If it has, we are in the wrong module Jack. This IF is structured so that + if this is the normal (optimized) case we will fall into the code and + handle the rerouting at the end. + + Note: The DEBUG expansion of this code in a pro build actually has a different + entry point name (gtm_bestfitsize_dbg) and if malloc debugging options are + on in pro, we need to call that version, but since efficiency in pro trumps + clarity, we put the redirecting call at the bottom of the if-else block to + avoid disrupting the instruction pipeline. + */ + if (GDL_None == gtmDebugLevel) + { +# endif + hdrSize = OFFSETOF(storElem, userStorage); /* Size of storElem header */ + tSize = size + hdrSize DEBUG_ONLY(+ SIZEOF(markerChar)); + if (MAXTWO >= tSize) + { /* Allocation would fit in a buddy list queue */ + sizeIndex = GetSizeIndex(tSize); + tSize = TwoTable[sizeIndex]; + return (tSize - hdrSize DEBUG_ONLY(- SIZEOF(markerChar))); + } + return size; +# ifndef DEBUG + } + /* If not a debug module and debugging is enabled, reroute call to + the debugging version. + */ + return gtm_bestfitsize_dbg(size); +# endif +} + + +/* Note that the DEBUG define takes on an additional meaning in this module. Not only are routines defined within + intended for DEBUG builds but they are also only generated ONCE rather than twice like most of the routines are + in this module. +*/ +#ifdef DEBUG +/* Backfill the requested area with marker text. We do this by doing single byte + stores up to the point where we can do aligned stores of the native register + length. Then fill the area as much as possible and finish up potentially with + a few single byte unaligned bytes at the end. +*/ +void backfill(unsigned char *ptr, gtm_msize_t len) +{ + unsigned char *c; + ChunkType *chunkPtr; + gtm_msize_t unalgnLen, chunkCnt; + + if (0 != len) + { + /* Process unaligned portion first */ + unalgnLen = (gtm_msize_t)ptr & AddrMask; /* Past an alignment point */ + if (unalgnLen) + { + unalgnLen = ChunkSize - unalgnLen; /* How far to go to get to alignment point */ + unalgnLen = MIN(unalgnLen, len); /* Make sure not going too far */ + c = backfillMarkC; + len -= unalgnLen; + do + { + *ptr++ = *c++; + --unalgnLen; + } while(unalgnLen); + } + + /* Now, do aligned portion */ + assert(0 == ((gtm_msize_t)ptr & AddrMask)); /* Verify aligned */ + chunkCnt = len / ChunkSize; + chunkPtr = (ChunkType *)ptr; + while (chunkCnt--) + { + *chunkPtr++ = ChunkValue; + len -= SIZEOF(ChunkType); + } + + /* Do remaining unaligned portion if any */ + if (len) + { + ptr = (unsigned char *)chunkPtr; + c = backfillMarkC; + do + { + *ptr++ = *c++; + --len; + } while(len); + } + } +} + +/* ** still under ifdef DEBUG ** */ +/* Check the given backfilled area that it was filled in exactly as + the above backfill routine would have filled it in. Again, do any + unaligned single chars first, then aligned native length areas, + then any stragler unaligned chars */ +boolean_t backfillChk(unsigned char *ptr, gtm_msize_t len) +{ + unsigned char *c; + ChunkType *chunkPtr; + gtm_msize_t unalgnLen, chunkCnt; + + if (0 != len) + { + /* Process unaligned portion first */ + unalgnLen = (gtm_msize_t)ptr & AddrMask; /* Past an alignment point */ + if (unalgnLen) + { + unalgnLen = ChunkSize - unalgnLen; /* How far to go to get to alignment point */ + unalgnLen = MIN(unalgnLen, len); /* Make sure not going too far */ + c = backfillMarkC; + len -= unalgnLen; + do + { + if (*ptr++ == *c++) + --unalgnLen; + else + return FALSE; + } while(unalgnLen); + } + + /* Now, do aligned portion */ + assert(0 == ((gtm_msize_t)ptr & AddrMask)); /* Verify aligned */ + chunkCnt = len / ChunkSize; + chunkPtr = (ChunkType *)ptr; + while (chunkCnt--) + { + if (*chunkPtr++ == ChunkValue) + len -= SIZEOF(ChunkType); + else + return FALSE; + } + + /* Do remaining unaligned portion if any */ + if (len) + { + ptr = (unsigned char *)chunkPtr; + c = backfillMarkC; + do + { + if (*ptr++ == *c++) + --len; + else + return FALSE; + } while(len); + } + } + return TRUE; +} + + +/* ** still under ifdef DEBUG ** */ +/* Routine to run the free storage chains to verify that everything is in the correct place */ +void verifyFreeStorage(void) +{ + storElem *eHdr, *uStor; + uint4 i; + int hdrSize; + + hdrSize = OFFSETOF(storElem, userStorage); + /* Looping for each free queue */ + for (eHdr = &freeStorElemQs[0], i = 0; i <= MAXINDEX; ++i, ++eHdr) + { + for (uStor = STE_FP(eHdr); uStor->queueIndex != QUEUE_ANCHOR; uStor = STE_FP(uStor)) + { + assert(((MAXINDEX + 1) >= i)); /* Verify loop limits */ + assert(((i == uStor->queueIndex) && (MAXINDEX <= MAXINDEX)) + || (((MAXINDEX + 1) == i) && (REAL_MALLOC == uStor->queueIndex))); /* Verify queue index */ + assert(0 == ((unsigned long)uStor & (TwoTable[i] - 1))); /* Verify alignment */ + assert(Free == uStor->state); /* Verify state */ + assert(0 == memcmp(uStor->headMarker, markerChar, SIZEOF(uStor->headMarker))); /* Vfy metadata marker */ + assert(MAXINDEX != i || extent_used > uStor->extHdrOffset); + if (GDL_SmChkFreeBackfill & gtmDebugLevel) + { /* Use backfill check method for verifying freed storage is untouched */ + assert(backfillChk((unsigned char *)uStor + hdrSize, TwoTable[i] - hdrSize)); + } + } + } +} + + +/* ** still under ifdef DEBUG ** */ +/* Routine to run the allocated chains to verify that the markers are all still in place */ +void verifyAllocatedStorage(void) +{ + storElem *eHdr, *uStor; + unsigned char *trailerMarker; + uint4 i; + int hdrSize; + + hdrSize = OFFSETOF(storElem, userStorage); + /* Looping for MAXINDEX+1 will check the real-malloc'd chains too */ + for (eHdr = &allocStorElemQs[0], i = 0; i <= (MAXINDEX + 1); ++i, ++eHdr) + { + for (uStor = STE_FP(eHdr); uStor->queueIndex != QUEUE_ANCHOR; uStor = STE_FP(uStor)) + { + assert(((MAXINDEX + 1) >= i)); /* Verify loop not going nutz */ + assert(((i == uStor->queueIndex) && (MAXINDEX <= MAXINDEX)) + || (((MAXINDEX + 1) == i) && (REAL_MALLOC == uStor->queueIndex))); /* Verify queue index */ + if (i != MAXINDEX + 1) /* If not verifying real mallocs,*/ + assert(0 == ((unsigned long)uStor & (TwoTable[i] - 1))); /* .. verify alignment */ + assert(Allocated == uStor->state); /* Verify state */ + assert(0 == memcmp(uStor->headMarker, markerChar, SIZEOF(uStor->headMarker))); /* Vfy metadata markers */ + trailerMarker = (unsigned char *)&uStor->userStorage.userStart+uStor->allocLen;/* Where trailer was put */ + assert(0 == memcmp(trailerMarker, markerChar, SIZEOF(markerChar))); + assert(MAXINDEX != i || extent_used > uStor->extHdrOffset); + if (GDL_SmChkAllocBackfill & gtmDebugLevel) + { /* Use backfill check method for after-allocation metadata */ + assert(backfillChk(trailerMarker + SIZEOF(markerChar), + (uStor->realLen - uStor->allocLen - hdrSize - SIZEOF(markerChar)))); + } + } + } +} + + +/* ** still under ifdef DEBUG ** */ +/* Routine to print the end-of-process info -- either allocation statistics or malloc trace dump. + Note that the use of FPRINTF here instead of util_out_print is historical. The output was at one + time going to stdout and util_out_print goes to stderr. If necessary or desired, these could easily + be changed to use util_out_print instead of FPRINTF +*/ +void printMallocInfo(void) +{ + int i, j; + + if (GDL_SmStats & gtmDebugLevel) + { + FPRINTF(stderr,"\nMalloc small storage performance:\n"); + FPRINTF(stderr, + "Total mallocs: %d, total frees: %d, total extents: %d, total rmalloc bytes: %ld," + " max rmalloc bytes: %ld\n", + totalMallocs, totalFrees, totalExtents, totalRmalloc, rmallocMax); + FPRINTF(stderr, + "Total (currently) allocated (includes overhead): %ld, Total (currently) used (no overhead): %ld\n", + totalAlloc, totalUsed); + FPRINTF(stderr, + "Maximum extents: %d, Current extents: %d, Released extents: %d\n", maxExtents, curExtents, + (totalExtents - curExtents)); + GMR_ONLY(FPRINTF(stderr,"Total reentrant mallocs: %d, total deferred frees: %d\n", reentMallocs, deferFreePending);) + FPRINTF(stderr,"\nQueueSize Mallocs Frees Splits Combines CurCnt MaxCnt CurCnt MaxCnt\n"); + FPRINTF(stderr, " Free Free Alloc Alloc\n"); + FPRINTF(stderr, "-----------------------------------------------------------------------------------------\n"); + { + for (i = 0; i <= MAXINDEX + 1; ++i) + { + FPRINTF(stderr, + "%9d %9d %9d %9d %9d %9d %9d %9d %9d\n", TwoTable[i], mallocCnt[i], freeCnt[i], + elemSplits[i], elemCombines[i], freeElemCnt[i], freeElemMax[i], + allocElemCnt[i], allocElemMax[i]); + } + } + } + if (GDL_SmDumpTrace & gtmDebugLevel) + { + FPRINTF(stderr,"\nMalloc Storage Traceback: gtm_malloc() addr: 0x"gmaAdr"\n", >m_malloc); + FPRINTF(stderr,"TransNumber "gmaFill" AllocAddr Size "gmaFill" CallerAddr\n"); + FPRINTF(stderr,"------------------------------------------------"gmaLine gmaLine"\n"); + for (i = 0,j = smLastMallocIndex; i < MAXSMTRACE; ++i,--j)/* Loop through entire table, start with last elem used */ + { + if (0 > j) /* Wrap as necessary */ + j = MAXSMTRACE - 1; + if (0 != smMallocs[j].smTn) + FPRINTF(stderr,"%9d 0x"gmaAdr" %10d 0x"gmaAdr"\n", + smMallocs[j].smTn, smMallocs[j].smAddr, smMallocs[j].smSize, smMallocs[j].smCaller); + } + FPRINTF(stderr,"\n\nFree Storage Traceback:\n"); + FPRINTF(stderr,"TransNumber "gmaFill" FreeAddr Size "gmaFill" CallerAddr\n"); + FPRINTF(stderr,"------------------------------------------------"gmaLine gmaLine"\n"); + for (i = 0, j = smLastFreeIndex; i < MAXSMTRACE; ++i, --j)/* Loop through entire table, start with last elem used */ + { + if (0 > j) /* Wrap as necessary */ + j = MAXSMTRACE - 1; + if (0 != smFrees[j].smTn) + FPRINTF(stderr,"%9d 0x"gmaAdr" %10d 0x"gmaAdr"\n", + smFrees[j].smTn, smFrees[j].smAddr, smFrees[j].smSize, smFrees[j].smCaller); + } + FPRINTF(stderr,"\n"); + fflush(stderr); + } + printMallocDump(); +} + + +/* Routine to print storage dump. This is called as part of print_malloc_info but is also potentially separately called from + op_view so is a separate routine. +*/ +void printMallocDump(void) +{ + storElem *eHdr, *uStor; + int i; + + if (GDL_SmDump & gtmDebugLevel) + { + FPRINTF(stderr,"\nMalloc Storage Dump: gtm_malloc() addr: 0x"gmaAdr"\n", >m_malloc); + FPRINTF(stderr,gmaFill"Malloc Addr "gmaFill" Alloc From Malloc Size Trans Number\n"); + FPRINTF(stderr," ----------------------------------------------------------"gmaLine gmaLine"\n"); + /* Looping for each allocated queue */ + for (eHdr = &allocStorElemQs[0], i = 0; i <= (MAXINDEX + 1); ++i, ++eHdr) + { + for (uStor = STE_FP(eHdr); uStor->queueIndex != QUEUE_ANCHOR; uStor = STE_FP(uStor)) + { + FPRINTF(stderr, " 0x"gmaAdr" 0x"gmaAdr" %10d %10d\n", + &uStor->userStorage.userStart, uStor->allocatedBy, uStor->allocLen, uStor->smTn); + } + } + fflush(stderr); + } +} +#endif diff --git a/sr_port/gtm_maxstr.c b/sr_port/gtm_maxstr.c new file mode 100644 index 0000000..32624ae --- /dev/null +++ b/sr_port/gtm_maxstr.c @@ -0,0 +1,77 @@ +/**************************************************************** + * * + * Copyright 2001, 2003 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_string.h" +#include "error.h" +#include "gtm_maxstr.h" + +/* GT.M string buffer stack where each entry is allocated geometrically on the need basis */ +GBLDEF mstr maxstr_buff[MAXSTR_STACK_SIZE]; +GBLDEF int maxstr_stack_level = -1; + +/* This handler is to release the heap storage allocated within a function in case of errors. + * The suggested protocol is that after a function is done with using the string buffer, the macro + * MAXSTR_BUFF_FINI used in the function epilog releases the malloc'd storage. But if an error + * occurs within the function body, the function epilog is not executed so this condition handler + * should do the same so that the storage is not kept dangling. */ +CONDITION_HANDLER(gtm_maxstr_ch) +{ + START_CH; + if (maxstr_buff[maxstr_stack_level].addr) + { + free(maxstr_buff[maxstr_stack_level].addr); + maxstr_buff[maxstr_stack_level].addr = NULL; + } + maxstr_buff[maxstr_stack_level].len = 0; + maxstr_stack_level--; + NEXTCH; +} + +/* The routine checks if the currently available buffer (either 32K automatic or >32K malloc'd) + * is sufficient for the new space requirement. If not sufficient, it keeps doubling the buffer size + * and checks until a new buffer size is large enough to accommodate the incoming string. It then + * allocates the new buffer and releases the previously malloc'd buffer. + * Note that if strings fit within 32K, the automating buffer is used and no heap storage is used. + * Parameters: + * + * space_needed - buffer space needed to accommodate the string that is about to be written + * buff - address of the pointer to the beginning of the buffer. If reallocation occurs, buff will be + * modified to point to the reallocated buffer. + * space_occupied - how full is the buffer? + * + * Returns the new allocated buffer size. + */ +int gtm_maxstr_alloc(int space_needed, char** buff, int space_occupied) +{ + int new_buff_size; + + if (space_needed > (maxstr_buff[maxstr_stack_level].len - space_occupied)) + { /* Existing buffer is not sufficient. reallocate the buffer with the double the size */ + new_buff_size = maxstr_buff[maxstr_stack_level].len; + while (space_needed > new_buff_size - space_occupied) + new_buff_size += new_buff_size; + if (NULL != maxstr_buff[maxstr_stack_level].addr) + { + assert(maxstr_buff[maxstr_stack_level].addr == *buff); + maxstr_buff[maxstr_stack_level].addr = (char *)malloc(new_buff_size); + memcpy(maxstr_buff[maxstr_stack_level].addr, *buff, space_occupied); + free(*buff); + } else + { + maxstr_buff[maxstr_stack_level].addr = (char *)malloc(new_buff_size); + memcpy(maxstr_buff[maxstr_stack_level].addr, *buff, space_occupied); + } + *buff = maxstr_buff[maxstr_stack_level].addr; + maxstr_buff[maxstr_stack_level].len = new_buff_size; + } + return maxstr_buff[maxstr_stack_level].len; +} diff --git a/sr_port/gtm_maxstr.h b/sr_port/gtm_maxstr.h new file mode 100644 index 0000000..aba2801 --- /dev/null +++ b/sr_port/gtm_maxstr.h @@ -0,0 +1,121 @@ +/**************************************************************** + * * + * Copyright 2003 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GTM_MAXSTR_INCLUDED +#define GTM_MAXSTR_INCLUDED + +/* The following macros should be used whenever an automatic string buffer + * of size MAX_STRLEN needs to be declared in a function. Given that MAX_STRLEN + * is enhanced to 1MB, allocating 1MB of local buffer on stack is not viable and + * may lead to C stack overflows. The following macros employ an adaptive + * scheme where an inital buffer of size 32K is allocated on stack and whenever + * the buffer is found to be insufficient, the algorithm reallocates the buffer in + * malloc'd space by doubling the previous size. + * + * If the string fits within 32K, there is [almost] no additional penalty since + * the buffer is on stack as before. + * + * For any future requirement of MAX_STRLEN automatic buffers, the following macros + * should be used. An example below: + * + * func() + * { + * char buffer[MAX_STRLEN]; + * mstr src, dst; + * + * ... + * dst.addr = &buffer[0]; + * memcpy(dst.addr, src.addr, len) + * ... + * + * return + * } + * + * should be replaced something like + * + * #include "gtm_maxstr.h" + * func() + * { + * MAXSTR_BUFF_DECL(buffer); + * mstr src, dst; + * + * MAXSTR_BUFF_INIT; + * ... + * ... + * dst.addr = &buffer[0]; + * MAXSTR_BUFF_ALLOC(src.len, &dst.addr, 0); + * ... + * ... + * MAXSTR_BUFF_FINI; + * return; + * } + * + */ + +/* The maximum nested depth of MAXSTR_BUFF_INIT/MAXSTR_BUFF_FINI allowed. We do not expect + * many nesting levels. When the buffer stack overflows, GT.M asserts. */ +#define MAXSTR_STACK_SIZE 10 + +GBLREF mstr maxstr_buff[]; /* Buffer stack for nested MAXSTR_BUFF_INIT/MAXSTR_BUFF_FINI */ +/* Each entry in the buffer stack where each entry points to the buffer and it's size. Note that + * although mstr is chosen as the entry type, it does not represent a GT.M string in the + * traditional sense. The addr field point to the malloc'd buffer and is NULL if no + * reallocation occured, i.e. buffer lies on the stack. The len field stores the current + * buffer size which can grow geometrically. + */ +GBLREF int maxstr_stack_level; /* Current (0-index based) depth of nested MAXSTR_BUFF_INIT/MAXSTR_BUFF_FINI */ + +#define MAXSTR_BUFF_DECL(var) char var[MAX_STRBUFF_INIT]; + +#define MAXSTR_BUFF_INIT \ +{ \ + ESTABLISH(gtm_maxstr_ch); \ + maxstr_stack_level++; \ + assert(maxstr_stack_level < MAXSTR_STACK_SIZE); \ + maxstr_buff[maxstr_stack_level].len = MAX_STRBUFF_INIT; \ + maxstr_buff[maxstr_stack_level].addr = NULL; \ +} + +#define MAXSTR_BUFF_INIT_RET \ +{ \ + ESTABLISH_RET(gtm_maxstr_ch, -1); \ + maxstr_stack_level++; \ + assert(maxstr_stack_level < MAXSTR_STACK_SIZE); \ + maxstr_buff[maxstr_stack_level].len = MAX_STRBUFF_INIT; \ + maxstr_buff[maxstr_stack_level].addr = NULL; \ +} + +/* The following macro checks whether the existing available buffer is sufficient + * and if not, it reallocates the buffer to the sufficient size. + * space_needed - buffer space needed to accommodate the string that is about to be written. + * buff - pointer to the beginning of the buffer. If reallocation occurs, buff will be + * modified to point to the reallocated buffer. + * space_occupied - how full is the buffer? + * returns - size of the allocated buffer (whether reallocated or not). + */ +#define MAXSTR_BUFF_ALLOC(space_needed, buff, space_occupied) \ + gtm_maxstr_alloc((space_needed), &(buff), (space_occupied)) + +#define MAXSTR_BUFF_FINI \ +{ \ + if (maxstr_buff[maxstr_stack_level].addr) \ + { \ + free(maxstr_buff[maxstr_stack_level].addr); \ + maxstr_buff[maxstr_stack_level].addr = NULL; \ + } \ + maxstr_buff[maxstr_stack_level].len = 0; \ + maxstr_stack_level--; \ + REVERT; /* gtm_maxstr_ch() */ \ +} + +int gtm_maxstr_alloc(int space_needed, char** buff, int space_occupied); + +#endif /* GTM_MAXSTR_INCLUDED */ diff --git a/sr_port/gtm_memcmp.c b/sr_port/gtm_memcmp.c new file mode 100644 index 0000000..9c2e49d --- /dev/null +++ b/sr_port/gtm_memcmp.c @@ -0,0 +1,28 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* gtm_memcmp - GT.M interlude to C library memcmp function. + * + * gtm_memcmp is an interlude to the supplied C library memcmp that + * prevents memory addressing errors that can occur when the length + * of the items to be compared is zero and the supplied function + * does not first validate its input parameters. + */ + +#include "mdef.h" + +#include "gtm_string.h" + +#undef memcmp +int gtm_memcmp (const void *a, const void *b, size_t len) +{ + return (int)(len == 0 ? len : memcmp(a, b, len)); +} diff --git a/sr_port/gtm_netdb.h b/sr_port/gtm_netdb.h new file mode 100644 index 0000000..3e6212e --- /dev/null +++ b/sr_port/gtm_netdb.h @@ -0,0 +1,23 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* gtm_netdb.h - interlude to system header file. */ +#ifndef GTM_NETDBH +#define GTM_NETDBH + +#include + +#define MAX_GETHOST_TRIES 8 +#define GETHOSTBYNAME gethostbyname +#define GETHOSTBYADDR gethostbyaddr +#define HSTRERROR hstrerror + +#endif diff --git a/sr_port/gtm_newintrinsic.c b/sr_port/gtm_newintrinsic.c new file mode 100644 index 0000000..2cdd356 --- /dev/null +++ b/sr_port/gtm_newintrinsic.c @@ -0,0 +1,159 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "rtnhdr.h" +#include "mv_stent.h" +#include "stack_frame.h" +#include "tp_frame.h" +#include "gtm_string.h" +#include "gtm_newintrinsic.h" +#include "op.h" + +GBLREF mv_stent *mv_chain; +GBLREF unsigned char *stackbase, *stacktop, *msp, *stackwarn; +GBLREF stack_frame *frame_pointer; +GBLREF tp_frame *tp_pointer; +GBLREF symval *curr_symval; +GBLREF uint4 dollar_tlevel; +#ifdef GTM_TRIGGER +GBLREF mval dollar_ztwormhole; +#endif + +/* Note this module follows the basic pattern of op_newvar which handles the same + function except for local vars instead of intrinsic vars. */ +void gtm_newintrinsic(mval *intrinsic) +{ + mv_stent *mv_st_ent, *mvst_tmp, *mvst_prev; + stack_frame *fp, *fp_prev, *fp_fix; + tp_frame *tpp; + unsigned char *old_sp, *top; + int indx; + int4 shift_size; + + error_def(ERR_STACKOFLOW); + error_def(ERR_STACKCRIT); + + assert(intrinsic); + if (frame_pointer->type & SFT_COUNT) + { /* Current (youngest) frame is NOT an indirect frame. + Create restore entry for intrinsic var. + */ + PUSH_MV_STENT(MVST_MSAV); + mv_chain->mv_st_cont.mvs_msav.v = *intrinsic; + mv_chain->mv_st_cont.mvs_msav.addr = intrinsic; + } else + { /* Current (youngest) frame IS an indirect frame. + The situation is more complex because this is not a true stackframe. + It has full access to the base "counted" frame's vars and any new + done here must behave as if it were done in the base/counted frame. + To accomplish this, we actually find the base frame we are executing + in, then shift all frames younger than that up by the size of the mvstent + entry we need to save/restore the value being new'd and then go into + each frame modified and fixup all the addresses. + */ + fp = frame_pointer; + fp_prev = fp->old_frame_pointer; + assert(fp_prev); + /* Find relevant base (counted) frame */ + while (!(fp_prev->type & SFT_COUNT)) + { + fp = fp_prev; + fp_prev = fp->old_frame_pointer; + assert(fp_prev); + } + /* top is beginning of earliest indirect stackframe before counted base frame. + It is the point where we will shift to make room to insert an mv_stent into + the base frame. + */ + top = (unsigned char *)(fp + 1); + old_sp = msp; + shift_size = mvs_size[MVST_MSAV]; + msp -= shift_size; + if (msp <= stackwarn) + { + if (msp <= stacktop) + { + msp = old_sp; + rts_error(VARLSTCNT(1) ERR_STACKOFLOW); + } + else + rts_error(VARLSTCNT(1) ERR_STACKCRIT); + } + /* Ready, set, shift the younger indirect frames to make room for mv_stent */ + memmove(msp, old_sp, top - (unsigned char *)old_sp); + mv_st_ent = (mv_stent *)(top - shift_size); + mv_st_ent->mv_st_type = MVST_MSAV; + ADJUST_FRAME_POINTER(frame_pointer, shift_size); + /* adjust all the pointers in all the stackframes that were moved */ + for (fp_fix = frame_pointer; fp_fix != fp_prev; fp_fix = fp_fix->old_frame_pointer) + { + if ((unsigned char *)fp_fix->l_symtab < top && (unsigned char *)fp_fix->l_symtab > stacktop) + fp_fix->l_symtab = (ht_ent_mname **)((char *)fp_fix->l_symtab - shift_size); + if (fp_fix->temps_ptr < top && fp_fix->temps_ptr > stacktop) + fp_fix->temps_ptr -= shift_size; + if (fp_fix->vartab_ptr < (char *)top && fp_fix->vartab_ptr > (char *)stacktop) + fp_fix->vartab_ptr -= shift_size; + if ((unsigned char *)fp_fix->old_frame_pointer < top && (char *)fp_fix->old_frame_pointer + > (char *)stacktop) + { + ADJUST_FRAME_POINTER(fp_fix->old_frame_pointer, shift_size); + } + } + /* Adjust stackframe and mvstent pointers in relevant tp_frame blocks */ + assert(((NULL == tp_pointer) && !dollar_tlevel) || ((NULL != tp_pointer) && dollar_tlevel)); + for (tpp = tp_pointer; (tpp && ((unsigned char *)tpp->fp < top)); tpp = tpp->old_tp_frame) + { + if ((unsigned char *)tpp->fp > stacktop) + tpp->fp = (struct stack_frame_struct *)((char *)tpp->fp - shift_size); + /* Note low check for < top may be superfluous here but without a test case to verify, I + feel better leaving it in. SE 8/2001 */ + if ((unsigned char *)tpp->mvc < top && (unsigned char *)tpp->mvc > stacktop) + tpp->mvc = (struct mv_stent_struct *)((char *)tpp->mvc - shift_size); + } + /* Put new mvstent entry on (into) the mvstent chain */ + if ((unsigned char *)mv_chain >= top) + { /* Just put new entry on end of chain which preceeds our base frame */ + mv_st_ent->mv_st_next = (unsigned int)((char *)mv_chain - (char *)mv_st_ent); + mv_chain = mv_st_ent; + } else + { /* One of the indirect frames has mv_stents associated with it so we have to find + the appropriate insertion point for this frame. + */ + fp = (stack_frame *)((char *)fp - shift_size); + mv_chain = (mv_stent *)((char *)mv_chain - shift_size); + mvst_tmp = mv_chain; + mvst_prev = (mv_stent *)((char *)mvst_tmp + mvst_tmp->mv_st_next); + while (mvst_prev < (mv_stent *)fp) + { + mvst_tmp = mvst_prev; + mvst_prev = (mv_stent *)((char *)mvst_tmp + mvst_tmp->mv_st_next); + } + mvst_tmp->mv_st_next = (unsigned int)((char *)mv_st_ent - (char *)mvst_tmp); + mv_st_ent->mv_st_next = (unsigned int)((char *)mvst_prev - (char *)mv_st_ent + shift_size); + } + /* Save current values of intrinsic var in the inserted mv_stent */ + mv_st_ent->mv_st_cont.mvs_msav.v = *intrinsic; + mv_st_ent->mv_st_cont.mvs_msav.addr = intrinsic; + } + /* New the intrinsic var's current value if not $ZTWORMHOLE */ +# ifdef GTM_TRIGGER + if (&dollar_ztwormhole != intrinsic) + { +# endif + intrinsic->mvtype = MV_STR; + intrinsic->str.len = 0; +# ifdef GTM_TRIGGER + } +# endif + return; +} diff --git a/sr_port/gtm_newintrinsic.h b/sr_port/gtm_newintrinsic.h new file mode 100644 index 0000000..e30f226 --- /dev/null +++ b/sr_port/gtm_newintrinsic.h @@ -0,0 +1,12 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +void gtm_newintrinsic(mval *intrinsic); diff --git a/sr_port/gtm_putmsg_list.h b/sr_port/gtm_putmsg_list.h new file mode 100644 index 0000000..4e7dc06 --- /dev/null +++ b/sr_port/gtm_putmsg_list.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001, 2005 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GTM_PUTMSG_LIST_H +#define GTM_PUTMSG_LIST_H + +void gtm_putmsg_list(int arg_count, va_list var); + +#endif diff --git a/sr_port/gtm_pwd.h b/sr_port/gtm_pwd.h new file mode 100644 index 0000000..bee55ff --- /dev/null +++ b/sr_port/gtm_pwd.h @@ -0,0 +1,24 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* gtm_pwd.h - interlude to system header file. */ +#ifndef GTM_PWDH +#define GTM_PWDH + +#include + +#define GETPWUID(uid,getpwuid_res) (getpwuid_res = getpwuid(uid)) + +/* #define to be gtm_getpwuid to serve as a wrapper for blocking signals since getpwuid is not signal-safe. */ +#define getpwuid gtm_getpwuid +struct passwd *gtm_getpwuid(uid_t uid); /* Define prototype of "gtm_getpwuid" here */ + +#endif diff --git a/sr_port/gtm_relqueopi.c b/sr_port/gtm_relqueopi.c new file mode 100644 index 0000000..b1fd5cc --- /dev/null +++ b/sr_port/gtm_relqueopi.c @@ -0,0 +1,98 @@ +/**************************************************************** + * * + * Copyright 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* gtm_relqueopi - C-callable relative queue interlocked routines + * + * These routines perform interlocked operations on doubly-linked + * relative queues. They are designed to emulate the VAX machine + * instructions (and corresponding VAX C library routines) after + * which they are named. + * + * insqhi - insert entry into queue at head, interlocked + * insqti - insert entry into queue at tail, interlocked + * remqhi - remove entry from queue at head, interlocked + * remqti - remove entry from queue at tail, interlocked + */ + +#include "mdef.h" + +#include "relqueopi.h" + +#ifdef DEBUG + +/* An element that is not present in the queue should have its fl,bl fields zero. + * An element that is present in the queue should have its fl,bl fields non-zero. + * Ensure/Check this property for all queue operations. + * This will help catch a case when an element that is already present in the queue is re-added into the same queue. + * Such operations will cause queue corruption and since the queue is in shared memory (given that the interlocked + * queue operations are being done), could cause shared memory corruption and database damage (D9H03-002644). + */ +int gtm_insqhi(que_ent_ptr_t new, que_head_ptr_t base) +{ + int4 status; + + assert(0 == new->fl); + assert(0 == new->bl); + status = SYS_INSQHI(new, base); + /* We cannot assert that new->fl and new->bl are non-zero at this point since they + * could be concurrently removed from the queue right after we inserted it above. + */ + return status; +} + +int gtm_insqti(que_ent_ptr_t new, que_head_ptr_t base) +{ + int4 status; + + assert(0 == new->fl); + assert(0 == new->bl); + status = SYS_INSQTI(new, base); + /* We cannot assert that new->fl and new->bl are non-zero at this point since they + * could be concurrently removed from the queue right after we inserted it above. + */ + return status; +} + +void_ptr_t gtm_remqhi(que_head_ptr_t base) +{ + que_ent_ptr_t ret; + + ret = SYS_REMQHI(base); + if (NULL != ret) + { +# ifndef UNIX /* in Unix, relqueopi.c already does the rest of fl,bl to 0 just before releasing the swap lock */ + ret->fl = 0; + ret->bl = 0; +# endif + assert(0 == ret->fl); + assert(0 == ret->bl); + } + return (void_ptr_t)ret; +} + +void_ptr_t gtm_remqti(que_head_ptr_t base) +{ + que_ent_ptr_t ret; + + ret = SYS_REMQTI(base); + if (NULL != ret) + { +# ifndef UNIX /* in Unix, relqueopi.c already does the rest of fl,bl to 0 just before releasing the swap lock */ + ret->fl = 0; + ret->bl = 0; +# endif + assert(0 == ret->fl); + assert(0 == ret->bl); + } + return (void_ptr_t)ret; +} + +#endif diff --git a/sr_port/gtm_rename.h b/sr_port/gtm_rename.h new file mode 100644 index 0000000..811bd7d --- /dev/null +++ b/sr_port/gtm_rename.h @@ -0,0 +1,27 @@ +/**************************************************************** + * * + * Copyright 2003 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __GTM_RENAME_ +#define __GTM_RENAME_ +/* gtm_rename.h */ + +#define RENAME_SUCCESS 0 +#define RENAME_NOT_REQD 1 +#define RENAME_FAILED 2 + +int rename_file_if_exists(char *org_fn, int org_fn_len, char *rename_fn, int *rename_fn_len, uint4 *ustatus); +uint4 gtm_rename(char *org_fn, int org_fn_len, char *rename_fn, int rename_len, uint4 *ustatus); +uint4 prepare_unique_name(char *org_fn, int org_fn_len, char *prefix, char *suffix, char *rename_fn, + int *rename_fn_len, uint4 *ustatus); +uint4 append_time_stamp(char *fn, int fn_len, int *app_len, uint4 *ustatus); +void cre_jnl_file_intrpt_rename(int fn_len, sm_uc_ptr_t fn); + +#endif diff --git a/sr_port/gtm_savetraps.c b/sr_port/gtm_savetraps.c new file mode 100644 index 0000000..2440bde --- /dev/null +++ b/sr_port/gtm_savetraps.c @@ -0,0 +1,32 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_savetraps.h" +#include "gtm_newintrinsic.h" + +GBLREF mval dollar_ztrap; +GBLREF mval dollar_etrap; + +/* Routine called when we need to save the current Xtrap (etrap or ztrap) but + don't know which to save. +*/ +void gtm_savetraps(void) +{ + mval *intrinsic; + + if (dollar_ztrap.str.len) + intrinsic = &dollar_ztrap; + else + intrinsic = &dollar_etrap; + gtm_newintrinsic(intrinsic); + return; +} diff --git a/sr_port/gtm_savetraps.h b/sr_port/gtm_savetraps.h new file mode 100644 index 0000000..0b8d466 --- /dev/null +++ b/sr_port/gtm_savetraps.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GTM_SAVETRAPS_H +#define GTM_SAVETRAPS_H + +void gtm_savetraps(void); + +#endif diff --git a/sr_port/gtm_sizeof.h b/sr_port/gtm_sizeof.h new file mode 100644 index 0000000..892e589 --- /dev/null +++ b/sr_port/gtm_sizeof.h @@ -0,0 +1,42 @@ +/**************************************************************** + * * + * Copyright 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GTM_SIZEOF_H_INCLUDED +#define GTM_SIZEOF_H_INCLUDED + +/* Note: sizeof returns a "UNSIGNED long" type by default. When one of the operand in a relational operation + * (<, <= etc.) is a SIGNED type and the other is UNSIGNED, both operands are typecast to UNSIGNED (NOT SIGNED) + * before performing the comparison. This has unexpected consequences. For example, if a variable XYZ is signed + * and has a negative value of -1, one would expect an "if (X < sizeof(char))" to return TRUE (because X is + * negative and sizeof of anything is positive) but it actually returns FALSE. We have gotten bit by this at least + * twice (C9E09-002635 and C9J10-003208). Therefore we define a SIZEOF macro that returns a "SIGNED long" type and + * use it throughout the code base. This way "if (X < SIZEOF(char))" will evaluate correctly irrespective of + * whether X is signed or unsigned (if X is signed, since SIZEOF is also signed, a signed comparison occurs; + * if X is unsigned, since SIZEOF is signed, C's typecasting rules cause a unsigned comparison to occur). + * + * In a platform where long is 64-bits and SIZEOF is used in comparisons involving 32-bit variables, a compiler + * warning is issued for every 64->32 bit auto cast (zOS compiler for now). To avoid this warning, we define the + * SIZEOF macro to return a "SIGNED int" type on zOS. This will take care of the most common sizeof usages. + * Whenever SIZEOF needs to be used in expressions involving 64-bit pointer quantities, use ((INTPTR_T)SIZEOF(...)). + * Whenever SIZEOF needs to be used in expressions involving 64-bit integer quantities, use ((long)SIZEOF(...)). + * + * Some modules (like getcaps.c, gtm_tparm.c etc) do not include mdef.h for reasons stated in the corresponding + * files but yet use sizeof. To enable those modules to use the safer SIZEOF macro as well, we create a separate + * header file gtm_sizeof.h that is included explicitly by them as well as by mdef.h. + */ + +#if defined(__MVS__) +# define SIZEOF(X) ((int)(sizeof(X))) +#else +# define SIZEOF(X) ((long)sizeof(X)) +#endif + +#endif diff --git a/sr_port/gtm_socket.h b/sr_port/gtm_socket.h new file mode 100644 index 0000000..64b6d52 --- /dev/null +++ b/sr_port/gtm_socket.h @@ -0,0 +1,47 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* gtm_socket.h - interlude to system header file. */ +#ifndef GTM_SOCKETH +#define GTM_SOCKETH + +#include + +#define BIND bind +#define CONNECT connect +#define ACCEPT accept +#define RECVFROM recvfrom +#define SENDTO sendto + +#if defined(__osf__) && defined(__alpha) +#define GTM_SOCKLEN_TYPE size_t +#elif defined(VMS) +#define GTM_SOCKLEN_TYPE size_t +#elif defined(__sparc) +#define GTM_SOCKLEN_TYPE int +#else +#define GTM_SOCKLEN_TYPE socklen_t +#endif + +#ifdef GTM_FD_TRACE +/* Just like open and close were noted down in gtm_fcntl.h, note down all macros which we are redefining here and could + * potentially have been conflictingly defined by the system header file "socket.h". The system define will be used + * in gtm_fd_trace.c within the implementation of the GT.M interlude function. Currently none of these functions (socket) + * are defined by the system so it is not theoretically necessary but they could be defined in the future. + */ +# undef socket /* in case this is already defined by */ +# define socket gtm_socket +#endif + +int gtm_socket(int domain, int type, int protocol); +int gtm_connect(int socket, struct sockaddr *address, size_t address_len); + +#endif diff --git a/sr_port/gtm_stat.h b/sr_port/gtm_stat.h new file mode 100644 index 0000000..66080dc --- /dev/null +++ b/sr_port/gtm_stat.h @@ -0,0 +1,31 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* gtm_stat.h - interlude to system header file. */ +#ifndef GTM_STATH +#define GTM_STATH + +#include + + +#define CHMOD chmod + +#define FCHMOD fchmod + +#define MKDIR mkdir + +#define Stat stat + +#define MKNOD mknod + +#define LSTAT lstat + +#endif diff --git a/sr_port/gtm_stdlib.h b/sr_port/gtm_stdlib.h new file mode 100644 index 0000000..209b911 --- /dev/null +++ b/sr_port/gtm_stdlib.h @@ -0,0 +1,50 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* gtm_stdlib.h - interlude to system header file. */ +#ifndef GTM_STDLIBH +#define GTM_STDLIBH + +#include + +#ifndef __CYGWIN__ +#define GETENV getenv +#else +char *gtm_getenv(char *varname); +#define GETENV gtm_getenv +#endif +#define ATOI atoi +#define ATOL atol +#define ATOF atof +#define PUTENV putenv +#define STRTOL strtol +#define STRTOLL strtoll +#define STRTOUL strtoul +#if INT_MAX < LONG_MAX /* like Tru64 */ +#define STRTO64L strtol +#define STRTOU64L strtoul +#elif defined(__hpux) +#include +#define STRTO64L strtoimax +#define STRTOU64L strtoumax +#else +#define STRTO64L strtoll +#define STRTOU64L strtoull +#endif +#define MKSTEMP(template,mkstemp_res) (mkstemp_res = mkstemp(template)) +#ifndef __CYGWIN__ +#define SYSTEM system +#else +#define SYSTEM gtm_system +int gtm_system(const char *line); +#endif + +#endif diff --git a/sr_port/gtm_string.h b/sr_port/gtm_string.h new file mode 100644 index 0000000..69cbe22 --- /dev/null +++ b/sr_port/gtm_string.h @@ -0,0 +1,32 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* If this is not the vax, define string.h. This is because the Vax + has its own built-in instructions for string manipulation. +*/ +#ifndef GTM_STRINGH +#define GTM_STRINGH + +#ifndef __vax +# include +#endif + +#define STRERROR strerror + +#define STRCPY(SOURCE, DEST) strcpy((char *)(SOURCE), (char *)(DEST)) +#define STRNCPY_LIT(SOURCE, LITERAL) strncpy((char *)(SOURCE), (char *)(LITERAL), SIZEOF(LITERAL) - 1) /* BYPASSOK */ +#define STRNCPY_STR(SOURCE, STRING, LEN) strncpy((char *)(SOURCE), (char *)(STRING), LEN) + +#define STRCMP(SOURCE, DEST) strcmp((char *)(SOURCE), (char *)(DEST)) +#define STRNCMP_LIT(SOURCE, LITERAL) strncmp(SOURCE, LITERAL, SIZEOF(LITERAL) - 1) /* BYPASSOK */ +#define STRNCMP_STR(SOURCE, STRING, LEN) strncmp(SOURCE, STRING, LEN) + +#endif diff --git a/sr_port/gtm_strings.h b/sr_port/gtm_strings.h new file mode 100644 index 0000000..da433c5 --- /dev/null +++ b/sr_port/gtm_strings.h @@ -0,0 +1,20 @@ +/**************************************************************** + * * + * Copyright 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GTM_STRINGSH +#define GTM_STRINGSH + +#include + +#define STRCASECMP strcasecmp +#define STRNCASECMP strncasecmp + +#endif diff --git a/sr_port/gtm_tempnam.c b/sr_port/gtm_tempnam.c new file mode 100644 index 0000000..02de490 --- /dev/null +++ b/sr_port/gtm_tempnam.c @@ -0,0 +1,54 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gtm_stdio.h" +#include "gtm_tempnam.h" + +/************************************************************** + * gtm_tempnam() + * Generates a temporary file name and places it in fullname + * Note that fullname should be malloced by the caller + * It is assumed to be of proper size (MAX_FN_LEN + 1) + * + **************************************************************/ + +void gtm_tempnam(char *dir, char *prefix, char *fullname) +{ + static int temp_file_counter = 0; + int len; + char *ptr, def_prefix[] = "GTM_TEMP_"; + + if (NULL == dir) + { + len = SIZEOF(SCRATCH_DIR) - 1; + memcpy(fullname, SCRATCH_DIR, len); + } else + { + len = STRLEN(dir); + memcpy(fullname, dir, len); + } + ptr = fullname + len; + if (NULL == prefix) + { + prefix = def_prefix; + len = SIZEOF(def_prefix) - 1; + memcpy(ptr, def_prefix, len); + } else + { + len = STRLEN(prefix); + memcpy(ptr, prefix, len); + } + ptr += len; + SPRINTF(ptr, "_%d.tmp", temp_file_counter++); +} diff --git a/sr_port/gtm_tempnam.h b/sr_port/gtm_tempnam.h new file mode 100644 index 0000000..0f54613 --- /dev/null +++ b/sr_port/gtm_tempnam.h @@ -0,0 +1,24 @@ +/**************************************************************** + * * + * Copyright 2001, 2002 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __GTM_TEMPNAM_H__ +#define __GTM_TEMPNAM_H__ + +void gtm_tempnam(char *dir, char *prefix, char *fullname); + +#if defined(UNIX) +# define SCRATCH_DIR "/tmp/" +#elif defined(VMS) +# define SCRATCH_DIR "SYS$SCRATCH:" +#else +# error Unsupported Platform +#endif +#endif diff --git a/sr_port/gtm_text_alloc.h b/sr_port/gtm_text_alloc.h new file mode 100644 index 0000000..2e1380d --- /dev/null +++ b/sr_port/gtm_text_alloc.h @@ -0,0 +1,70 @@ +/**************************************************************** + * * + * Copyright 2007, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GTM_TEXT_ALLOC_H +#define GTM_TEXT_ALLOC_H + +/* States a storage element may be in (which need to be different from malloc states). */ +enum TextState {TextAllocated = 0x43, TextFree = 0x34}; + +/* Each allocated block (in the mmap build) has the following structure. The actual address + returned to the user for allocation and supplied by the user for release + is actually the storage beginning at the 'userStorage.userStart' area. + This holds true even for storage that is truely mmap'd. +*/ +typedef struct textElemStruct +{ /* This flavor of header is 16 bytes. This is required on IA64 and we have just adopted it for + the other platforms as well as it is a minor expense given the sizes of chunks we are using + */ + int queueIndex; /* Index into TwoTable for this size of element */ + enum TextState state; /* State of this block */ + unsigned int realLen; /* Real (total) length of allocation */ + int filler; + union /* The links are used only when element is free */ + { + struct /* Free block information */ + { + struct textElemStruct *fPtr; /* Next storage element on free queue */ + struct textElemStruct *bPtr; /* Previous storage element on free queue */ + } links; + unsigned char userStart; /* First byte of user useable storage */ + } userStorage; +} textElem; + +/* The below macros are used to allocate and release areas of storage that will be used + to contain executable code. Traditionally, GTM has just used malloc() and free() and + placed this code on the heap but some systems now protect against that (noteably + Linux). On those platforms (and any others we choose to separate the executable storage + pools from the regular heap storage), we use gtm_text_alloc() and gtm_text_free() to + allocate and free executable storage. These modules use mmap() and munmap() for this + purpose. + + Note that while use of the mmap() interface for executable storage does have some + potential advantages for security (and as noted above, is required for some platforms + to even function, because of the page aligned granularity of its requests, storage usage + with gtm_text_alloc() is not as efficient as it is with regular heap based storage. For + this reason, we only use this method on the required platforms rather than all. Replaceing + the algorithms in gtm_text_alloc.c with ones not based on the buddy system could + potentially alleviate these efficiency differences. +*/ +#if defined(__linux__) || defined(__osf__) || defined(__MVS__) || defined(__CYGWIN__) +# define GTM_TEXT_ALLOC(x) gtm_text_alloc(x) +# define GTM_TEXT_FREE(x) gtm_text_free(x) +void *gtm_text_alloc(size_t size); +void gtm_text_free(void *addr); +void printAllocInfo(void); +# define COMP_GTA /* Build gtm_text_alloc() module */ +# else +# define GTM_TEXT_ALLOC(x) gtm_malloc(x) +# define GTM_TEXT_FREE(x) gtm_free(x) +#endif + +#endif /* GTM_TEXT_ALLOC_H */ diff --git a/sr_port/gtm_threadgbl.h b/sr_port/gtm_threadgbl.h new file mode 100644 index 0000000..f87ab53 --- /dev/null +++ b/sr_port/gtm_threadgbl.h @@ -0,0 +1,112 @@ +/**************************************************************** + * * + * Copyright 2010, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + + +#ifndef GTM_THREADGBL_included +#define GTM_THREADGBL_included + +#ifndef NO_THREADGBL_DEFTYPES +# include "gtm_threadgbl_deftypes.h" /* Avoided for gtm_threadgbl_deftypes.c which builds this header file */ +#endif + +/* The following three macros allow access to global variables located in the thread-global array. The ultimate + * goal of this array is to permit GTM to become thread-safe. That is not yet true but this framework is the first + * step in taming our global variable use. In the future, these macros will change to their thread-safe counterparts + * such that we can have different arrays per thread. + * + * The macros can be classified as declaration (DCL_THREADGBL_ACCESS), definition (SETUP_THREADGBL_ACCESS), and + * usage (TADR, TREF, TAREF1, TAREF2, RFPTR, SFPTR, IVFPTR). + * + * DCL_THREADGBL_ACCESS - simple job is to declare the local variable used as an anchor to the thread global + * array. It should be located in the declaration section of an entry point. + * + * SETUP_THREAD_GBL_ACCESS - Sets the value of the local variable used as an anchor. Today, this is just a + * global var de-reference. In the future, it will use thread-logic to access a + * thread related base var. This should be placed near the top of the executable + * code for the routine - certainly before the first TREF macro. + * TADR - Used to obtain the address of the element. + * TREF - Used to dereference a global var (see notes at TREF macro for usage). + * TAREF1/TAREF2 - Used to access array elements - TAREF1 for vectors, TAREF2 for 2 subscript access. + * RFPTR - Used to reference a function pointer in an expression or conditional. + * SFPTR - Used to set a new value into a function pointer. + * IVFPTR - Used to invoke a function pointer. + */ + +/* Declare local thread global anchor */ +#define DCL_THREADGBL_ACCESS void *lcl_gtm_threadgbl + +/* Setup the local thread global anchor giving a value (and purpose in life) */ +#define SETUP_THREADGBL_ACCESS lcl_gtm_threadgbl = gtm_threadgbl + +/* Reference a given global in the thread global array. There are a couple of different ways this macro can be + * used. Note that its first term is a "de-reference" (aka "*"). As an example, say we have a global name defined + * as follows: + * + * somestruct *gblname; + * + * and we wish to access gblname->subfield. If we code "TREF(gblname)->subfield", that would generate + * + * *((somestruct **)((char *)lcl_gtm_threadgbl + nnn))->subfield + * + * Note that the first dereference covers the entire expression instead of just the TREF expression. This is an incorrect + * reference that hopefully produces a compiler error instead of a hard-to-find bug. What needs to be coded instead is + * (TREF(gblname))->subfield which generates: + * + * (*((somestruct **)((char *)lcl_gtm_threadgbl + nnn)))->subfield + * + * This isolates the dereference to the expression it needs to refer to and produces an expression to correctly access the + * desired field. However, if the global is not a pointer, but say, a boolean, then you CANNOT surround the TREF macro with + * parens if the expression is on the left hand side of the "=" operator because that is not a valid LREF expression. So if + * the field were a boolean, you could modify it as "TREF(somebool) = TRUE;". + * + * The general rule of thumb is if the global is a pointer being used to access a subfield, or if the global is a structure + * itself and you are accessing subfields with a "." operator (e.g. (TREF(fnpca)).fnpcs[i].indx = i;) surround the TREF with (). + * Otherwise* you can leave it unadorned. + * + * Note that function pointers present some odd constraints. You can cast something to a function pointer type but only + * when you are going to actually invoke it (as the IVFPTR() macro does below). But this means that non-invoking references + * (or assignments) cannot use casts to make types match. So we need 2 additional macros for reference in an expression (RFPTR) + * or setting a function pointer (SFPTR) so we can control the necessary aspects of the expressions involved. See + * $sr_unix/generic_signal_handler.c for uses of most of these macros used with function pointers. + */ +#define TREF(name) *((ggt_##name *)((char *)lcl_gtm_threadgbl + ggo_##name)) + +/* For address of item use TADR(name) macro */ +#define TADR(name) ((ggt_##name *)((char *)lcl_gtm_threadgbl + ggo_##name)) + +/* For access to single dimension array (vector), use TAREF1 macro */ +#define TAREF1(name, indx1) (TADR(name))[indx1] + +/* For access to 2 dimension array, use TAREF2 macro */ +#define TAREF2(name, indx1, indx2) ((ggt_##name *)((char *)lcl_gtm_threadgbl + ggo_##name + /* Base addr of array */ \ + (((indx1) - 1) * SIZEOF(ggt_##name) * ggd_##name)))[indx2] /* Row offset */ + +/* To set a function pointer, use SFPTR(name) macro - Used to copy an address INTO a function pointer */ +#define SFPTR(name, value) *((char **)((char *)lcl_gtm_threadgbl + ggo_##name)) = (char *)(value) + +/* To reference a function pointer's value, use RFPTR(name) macro. Use only in rvalue RHS expressions. Does not produce + * an lvalue suitable for LHS usage. See SFPTR() above if function pointer needs a new value. + */ +#define RFPTR(name) (ggt_##name (*)gga_##name)(*(char **)((char *)lcl_gtm_threadgbl + ggo_##name)) + +/* To invoke a function pointer, use IVFPTR(name) macro */ +#define IVFPTR(name) ((ggf_##name)(*(char **)((char *)lcl_gtm_threadgbl + ggo_##name))) + +/* In the main routines for GTM and the utilities that need to initialize the thread global structure all other routines access, + * this macro should be used in lieu of SETUP_THREADGBL_ACCESS. + */ +#define GTM_THREADGBL_INIT \ +{ \ + gtm_threadgbl_init(); \ + SETUP_THREADGBL_ACCESS; \ +} + +#endif diff --git a/sr_port/gtm_threadgbl_defs.h b/sr_port/gtm_threadgbl_defs.h new file mode 100644 index 0000000..b1c2916 --- /dev/null +++ b/sr_port/gtm_threadgbl_defs.h @@ -0,0 +1,195 @@ +/**************************************************************** + * * + * Copyright 2010, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* Note since this table is included multiple times, it should not have "previously included" protection. + * + * When adding items to this table, please be aware of alignment issues. + * + * Notes on where to put added items: + * + * 1. Make organization defensible. Possible organizations: + * a. Group like-types alphabetically. + * b. Gorup related items (such as compiler, replication, etc. + * 2. Because references will use an offset into this structure and since the "immediate offset" in + * compiler generated instructions is usually limited to "smaller" values like less than 16K or + * 32K or whatever (platform dependent), items near the top of the table should be reserved used for + * the most commonly used items (e.g. cs_addrs, cs_data, gv_target, etc). + * 3. Larger arrays/structures should go nearer the end. + * 4. There are no other runtime dependencies on this order. The order of fields can be switched around + * any way desired with just a rebuild. + */ + +/* Priority access fields - commonly used fields in performance situations */ +THREADGBLDEF(grabbing_crit, gd_region *) /* Region currently grabbing crit in (if any) */ + +/* Compiler */ +THREADGBLDEF(code_generated, boolean_t) /* flag that the compiler generated an object */ +THREADGBLDEF(compile_time, boolean_t) /* flag that the compiler's at work */ +THREADGBLDEF(dollar_zcstatus, int4) /* return status for zcompile and others */ +THREADGBLDEF(expr_depth, unsigned int) /* expression nesting level */ +THREADGBLDEF(expr_start, triple *) /* chain anchor for side effect early evaluation */ +THREADGBLDEF(expr_start_orig, triple *) /* anchor used to test if there's anything hung on + * expr_start */ +THREADGBLDEF(for_nest_level, uint4) /* kludge feeds extra (non-lvn) arg to FOR rt ops */ +THREADGBLDEF(for_stack_ptr, oprtype **) /* part of FOR compilation nesting mechanism */ +THREADGBLDEF(gtm_fullbool, unsigned int) /* controls boolean side-effect behavior defaults + * to 0 (GTM_BOOL) */ +THREADGBLDEF(last_source_column, short int) /* parser tracker */ +THREADGBLDEF(pos_in_chain, triple) /* anchor used to restart after a parsing error */ +THREADGBLDEF(s2n_intlit, boolean_t) /* type info from s2n for advancewindow */ +THREADGBLDEF(shift_side_effects, int) /* flag shifting of side-effects ahead of boolean + * evalation */ +THREADGBLDEF(source_error_found, int4) /* ?? */ +THREADGBLDEF(temp_subs, boolean_t) /* flag temp storing of subscripts to preserve + * current evaluation */ +THREADGBLDEF(trigger_compile, boolean_t) /* A trigger compilation is active */ + +/* Database */ +THREADGBLDEF(donot_commit, boolean_t) /* debug-only - see gdsfhead.h for purpose */ +THREADGBLDEF(gd_targ_addr, gd_addr *) /* current global directory reference */ +THREADGBLDEF(gtm_gvundef_fatal, boolean_t) /* core and die intended for testing */ +THREADGBLDEF(gv_extname_size, int4) /* part op_gvextname working memory mechanism */ +THREADGBLDEF(gv_last_subsc_null, boolean_t) /* indicates whether the last subscript of + * gv_currkey (aka $reference) is a NULL string */ +THREADGBLDEF(gv_mergekey2, gv_key *) /* op_merge working memory */ +THREADGBLDEF(gv_reorgkey, gv_key *) /* mu_swap_blk working memory */ +THREADGBLDEF(gv_some_subsc_null, boolean_t) /* TRUE if SOME subscript other than the last is + * NULL in gv_currkey (aka $REFERENCE). Note that + * while "some" in var name might typically include + * the last subscript, it does NOT in this case and + * allows name to be kept shorter. */ +THREADGBLDEF(gv_sparekey, gv_key *) /* gv_xform_key working memory */ +THREADGBLDEF(gv_sparekey_mval, mval) /* gv_xform_key working memory */ +THREADGBLDEF(gv_sparekey_size, int4) /* part gv_xform_key working memory mechanism */ +THREADGBLDEF(gv_tporigkey_ptr, gv_orig_key_array *) /* op_tstart tp nesting anchor */ +THREADGBLDEF(in_op_gvget, boolean_t) /* TRUE if op_gvget() is a C-stack call ancestor */ +THREADGBLDEF(last_fnquery_return_subcnt, int) /* count subscript in last_fnquery_return_sub */ +THREADGBLDEF(last_fnquery_return_varname, mval) /* returned varname of last $QUERY() */ +THREADGBLDEF(ok_to_call_wcs_recover, boolean_t) /* Set to TRUE before a few wcs_recover callers. + * Any call to wcs_recover in the final retry + * assert to prevent cache recovery while in a + * transaction and confuse things enough to cause + * further restarts (which is out-of-design while + * in the final retry). */ +THREADGBLDEF(prev_gv_target, gv_namehead *) /* saves the last gv_target for debugging */ +THREADGBLDEF(ready2signal_gvundef, boolean_t) /* TRUE if GET operation about to signal GVUNDEF */ +THREADGBLDEF(semop2long, volatile boolean_t) /* Waited too long for a semaphore operation */ +THREADGBLDEF(semwait2long, volatile boolean_t) /* Waited too long for a semaphore */ +THREADGBLDEF(tp_restart_count, uint4) /* tp_restart counter */ +THREADGBLDEF(tp_restart_dont_counts, uint4) /* tp_restart count adjustment */ +THREADGBLDEF(tp_restart_entryref, mval) /* tp_restart position for reporting */ +THREADGBLDEF(tp_restart_failhist_indx, int4) /* tp_restart dbg restart history index */ +THREADGBLDEF(tp_restart_needlock_tn, trans_num) /* tp_restart final try tn */ +THREADGBLDEF(tp_restart_needlock_cnt, uint4) /* tp_restart final try counter */ +THREADGBLDEF(transform, boolean_t) /* flag collation transform eligible */ + +/* Local variables */ +THREADGBLDEF(in_op_fnnext, boolean_t) /* set TRUE by op_fnnext; FALSE by op_fnorder */ +THREADGBLDEF(local_collseq, collseq *) /* pointer to collation algorithm for lvns */ +THREADGBLDEF(local_collseq_stdnull, boolean_t) /* flag temp controlling empty-string subscript + * handling - if true, use standard null subscript + * collation for local variables */ +THREADGBLDEF(lv_null_subs, int) /* UNIX: set in gtm_env_init_sp(), + * VMS: set in gtm$startup() */ +THREADGBLDEF(max_lcl_coll_xform_bufsiz, int) /* max size of local collation buffer,which extends + * from 32K each time the buffer overflows */ + +/* Replication variables */ +THREADGBLDEF(replgbl, replgbl_t) /* set of global variables needed by the source + * server */ +/* Miscellaneous */ +THREADGBLDEF(collseq_list, collseq *) /* list of pointers to currently mapped collation + * algorithms - since this seems only used in + * collseq.c -seems more like a STATICDEF */ +THREADGBLFPTR(create_fatal_error_zshow_dmp_fptr, void, ()) /* Fptr for gtm_fatal_error* zshow dmp routine */ +THREADGBLDEF(disable_sigcont, boolean_t) /* indicates whether the SIGCONT signal + * is allowed internally */ +THREADGBLDEF(dollar_zcompile, mstr) /* compiler qualifiers */ +THREADGBLDEF(dollar_zmode, mval) /* run mode indicator */ +THREADGBLDEF(dollar_zroutines, mstr) /* routine search list */ +THREADGBLDEF(error_on_jnl_file_lost, unsigned int) /* controls error handling done by jnl_file_lost. + * 0 (default) : Turn off journaling and continue. + * 1 : Keep journaling on, throw rts_error. + * VMS does not supports this and requires it to + * be 0. */ +#ifdef UNIX +THREADGBLDEF(fnzsearch_lv_vars, lv_val *) /* UNIX op_fnzsearch lv tree anchor */ +THREADGBLDEF(fnzsearch_sub_mval, mval) /* UNIX op_fnzsearch subscript constuctor */ +THREADGBLDEF(fnzsearch_nullsubs_sav, int) /* UNIX op_fnzsearch temp for null subs control */ +#endif +THREADGBLDEF(gtm_env_init_done, boolean_t) /* gtm_env_init flag for completion */ +THREADGBLFPTR(gtm_env_xlate_entry, int, ()) /* gtm_env_xlate() function pointer */ +THREADGBLDEF(gtm_environment_init, boolean_t) /* indicates that this is GT.M rather than + * production environment */ +THREADGBLFPTR(gtm_sigusr1_handler, void, ()) /* SIGUSR1 signal handler function ptr */ +THREADGBLDEF(gtm_waitstuck_script, mstr) /* Path to the script to be executed during waits*/ +THREADGBLDEF(gtmprompt, mstr) /* mstr pointing to prombuf containing the GTM + * prompt */ +THREADGBLDEF(in_zwrite, boolean_t) /* ZWrite is active */ +THREADGBLDEF(mprof_chunk_avail_size, int) /* Number of mprof stack frames that can fit in + * the current chunk */ +THREADGBLDEF(mprof_ptr, mprof_wrapper *) /* Object containing key mprof references */ +THREADGBLDEF(mprof_stack_curr_frame, mprof_stack_frame *) /* Pointer to the last frame on the mprof stack */ +THREADGBLDEF(mprof_stack_next_frame, mprof_stack_frame *) /* Pointer to the next frame to be put on the + * mprof stack */ +#ifdef UNIX +THREADGBLDEF(open_shlib_root, open_shlib *) /* Anchor for open shared library list */ +#endif +THREADGBLDEF(parms_cnt, unsigned int) /* Parameters count */ +#ifdef UNIX +THREADGBLDEF(pipefifo_interrupt, int) /* count of number of times a pipe or fifo device is + * interrupted */ +#endif +THREADGBLDEF(prof_fp, mprof_stack_frame *) /* Stack frame that mprof currently operates on */ +THREADGBLDEF(trans_code_pop, mval *) /* trans_code holder for $ZTRAP popping */ +THREADGBLDEF(view_ydirt_str, char *) /* op_view working storage for ydir* ops */ +THREADGBLDEF(view_ydirt_str_len, int4) /* part of op_view working storage for ydir* ops */ +THREADGBLDEF(zdate_form, int4) /* control for default $zdate() format */ +THREADGBLDEF(zro_root, zro_ent *) /* Anchor for zroutines structure entry array */ +#ifdef UNIX +THREADGBLDEF(zsearch_var, lv_val *) /* UNIX $zsearch() lookup variable */ +THREADGBLDEF(zsearch_dir1, lv_val *) /* UNIX $zsearch() directory 1 */ +THREADGBLDEF(zsearch_dir2, lv_val *) /* UNIX $zsearch() directory 2 */ +#endif + +/* Larger structures and char strings */ +THREADGBLDEF(fnpca, fnpc_area) /* $Piece cache structure area */ +THREADGBLAR1DEF(for_stack, oprtype *, MAX_FOR_STACK) /* stacks FOR scope complete (compilation) addrs */ +THREADGBLAR1DEF(for_temps, boolean_t, MAX_FOR_STACK) /* stacked flags of FOR control value temps */ +THREADGBLAR1DEF(last_fnquery_return_sub, mval, MAX_LVSUBSCRIPTS)/* Returned subscripts of last $QUERY() */ +THREADGBLDEF(lcl_coll_xform_buff, char *) /* This buffer is for local collation + * transformations, which must not nest - i.e. + * a transformation routine must not call another, + * or itself. This kind of nesting would cause + * overwriting of the buffer */ +#ifdef UNIX +THREADGBLAR1DEF(parm_ary, char *, MAX_PARMS) /* parameter strings buffer */ +THREADGBLAR1DEF(parm_ary_len, int, MAX_PARMS) /* array element allocation length */ +THREADGBLAR1DEF(parm_str_len, int, MAX_PARMS) /* parameter strings lengths */ +#endif +THREADGBLAR1DEF(prombuf, char, (MAX_MIDENT_LEN + 1)) /* The prompt buffer size (32) would allow at + * least 8 Unicode characters, but since most + * commonly used Unicode characters only occupy up + * to 3 bytes, the buffer would at least + * accommodate 10 Unicode characters in a prompt */ +THREADGBLDEF(rt_name_tbl, hash_table_mname) /* Routine hash table for finding $TEXT() info */ +THREADGBLAR1DEF(tp_restart_failhist_arry, char, FAIL_HIST_ARRAY_SIZE) /* tp_restart dbg storage of restart history */ + +/* GTM Call-in related globals */ +#ifdef UNIX +THREADGBLDEF(callin_hashtab, hash_table_str *) /* Callin hash table */ +THREADGBLDEF(ci_table, callin_entry_list *) /* Callin table in the form of a linked list */ +#endif +THREADGBLDEF(extcall_package_root, struct extcall_package_list *) /* External call table package list */ +#ifdef UNIX +THREADGBLDEF(gtmci_nested_level, unsigned int) /* Current nested depth of callin environments */ +#endif + diff --git a/sr_port/gtm_threadgbl_deftypes.c b/sr_port/gtm_threadgbl_deftypes.c new file mode 100644 index 0000000..d0d99dc --- /dev/null +++ b/sr_port/gtm_threadgbl_deftypes.c @@ -0,0 +1,213 @@ +/**************************************************************** + * * + * Copyright 2010, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* Since we are about to create gtm_threadgbl_deftypes.h, signal gtm_threadgbl.h to avoid including it */ +#define NO_THREADGBL_DEFTYPES +#include "mdef.h" + +#include +#include "gtm_inet.h" +#include "gtm_iconv.h" +#include "gtm_socket.h" +#include "gtm_unistd.h" +#include "gtm_limits.h" + +#include +#include +#ifdef UNIX +# include +#endif +#ifdef VMS +# include /* Required for gtmsource.h */ +# include +# include +# include "desblk.h" +#endif +#include "cache.h" +#include "hashtab_addr.h" +#include "hashtab_int4.h" +#include "hashtab_int8.h" +#include "hashtab_mname.h" +#include "hashtab_str.h" +#include "hashtab_objcode.h" +#include "error.h" +#include "rtnhdr.h" +#include "gdsroot.h" +#include "gdskill.h" +#include "ccp.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gdscc.h" +#include "comline.h" +#include "compiler.h" +#include "cmd_qlf.h" +#include "io.h" +#include "iosp.h" +#include "jnl.h" +#include "lv_val.h" +#include "collseq.h" +#include "mdq.h" +#include "mprof.h" +#include "mv_stent.h" +#include "stack_frame.h" +#include "stp_parms.h" +#include "stringpool.h" +#include "buddy_list.h" /* needed for tp.h */ +#include "tp.h" +#include "tp_frame.h" +#include "mlkdef.h" +#include "zshow.h" +#include "zwrite.h" +#include "zbreak.h" +#include "fnpc.h" +#include "mmseg.h" +#ifndef VMS +# include "gtmsiginfo.h" +#endif +#include "gtmimagename.h" +#include "iotcpdef.h" +#include "gt_timer.h" +#include "iosocketdef.h" /* needed for socket_pool and MAX_N_SOCKETS*/ +#include "ctrlc_handler_dummy.h" +#include "unw_prof_frame_dummy.h" +#include "op.h" +#include "gtmsecshr.h" +#include "error_trap.h" +#include "patcode.h" /* for pat_everything and sizeof_pat_everything */ +#include "source_file.h" /* for REV_TIME_BUFF_LEN */ +#include "mupipbckup.h" +#include "dpgbldir.h" +#include "mmemory.h" +#include "have_crit.h" +#include "alias.h" +#include "zroutines.h" + +/* FOR REPLICATION RELATED GLOBALS */ +#include "repl_msg.h" +#include "gtmsource.h" +#include "gtmrecv.h" +#include "replgbl.h" + +/* FOR MERGE RELATED GLOBALS */ +#include "gvname_info.h" +#include "op_merge.h" + +#ifdef UNIX +# include "cli.h" +# include "invocation_mode.h" +# include "fgncal.h" +# include "parse_file.h" /* for MAX_FBUFF */ +# include "repl_sem.h" +# include "gtm_zlib.h" +# include "zro_shlibs.h" +#endif + +#include "jnl_typedef.h" + +#ifdef VMS +# include "gtm_logicals.h" /* for GTM_MEMORY_NOACCESS_COUNT */ +#endif + +#include "gds_blk_upgrade.h" /* for UPGRADE_IF_NEEDED flag */ +#include "cws_insert.h" /* for CWS_REORG_ARRAYSIZE */ + +#ifdef UNICODE_SUPPORTED +# include "gtm_icu_api.h" +# include "gtm_utf8.h" +#endif + +#ifdef GTM_CRYPT +# include "gtmcrypt.h" +# include "gdsblk.h" +# include "muextr.h" +#endif + +#ifdef GTM_TRIGGER +# include "gv_trigger.h" +# include "gtm_trigger.h" +#endif + +/* This module's purpose is to generate gtm_threadgbl_deftypes.h for a given platform. This header file + * contains all the type and offset informatin needed for the TREF macro in gtm_threadgbl.h to access + * any field in the global structure without having to have all the types known in every module. Only the + * types used need be known. + * + * This is acomplished by creating the structure in this module with all types and offsets known and outputting + * those values in the form of #define statements that can be used by subsequent compiles. + */ + +/* First step, create the structure */ +#define THREADGBLDEF(name, type) type name; +#define THREADGBLFPTR(name, type, args) type (*name)args; +#define THREADGBLAR1DEF(name, type, dim1) type name[dim1]; +#define THREADGBLAR2DEF(name, type, dim1, dim2) type name[dim1][dim2]; +typedef struct +{ +#include "gtm_threadgbl_defs.h" +} gtm_threadgbl_def_t; +#undef THREADGBLDEF +#undef THREADGBLFPTR +#undef THREADGBLAR1DEF +#undef THREADGBLAR2DEF + +/* Note this module uses regular (lower case) printf because using PRINTF calls gtm_printf which is inappropriate + * since this module is not part of the GTM runtime but a standalone text generator. + */ + +/* Define macros that will generate the type and offset #defines */ +#define PRINT_TYPE_OFFSET(name, type) \ + printf("# define ggo_%s %d\n", #name, (int)OFFSETOF(gtm_threadgbl_def_t, name)); \ + printf("# define ggt_%s %s\n", #name, #type); + +/* For function pointers, we need the offset and type (which is a return type in this case since the actual type of + * the item is "function pointer") but also need the argument declarations for the function declaration to be complete + * Lastly, we need a function pointer typedef to make invocations work correctly. + */ +#define PRINT_TYPE_OFFSET_FPTR(name, type, args) \ + printf("# define ggo_%s %d\n", #name, (int)OFFSETOF(gtm_threadgbl_def_t, name)); \ + printf("# define ggt_%s %s\n", #name, #type); /* In this case, return type */ \ + printf("# define gga_%s %s\n", #name, #args); \ + printf("typedef %s (*ggf_%s)%s;\n", #type, #name, #args); + +/* For single dimension arrays, include the length of the entire array as it is likely needed, especially + * for character types. + */ +#define PRINT_TYPE_OFFSET_ARY1(name, type, dim1) \ + printf("# define ggo_%s %d\n", #name, (int)OFFSETOF(gtm_threadgbl_def_t, name)); \ + printf("# define ggt_%s %s\n", #name, #type); \ + printf("# define ggl_%s %d\n", #name, (int)SIZEOF(gtd.name)); + +/* For two dimensional arrays, we need to record the 2nd dimension as it is needed in the address computations */ +#define PRINT_TYPE_OFFSET_ARY2(name, type, dim1, dim2) \ + printf("# define ggo_%s %d\n", #name, (int)OFFSETOF(gtm_threadgbl_def_t, name)); \ + printf("# define ggt_%s %s\n", #name, #type); \ + printf("# define ggl_%s %d\n", #name, (int)SIZEOF(gtm_threadgbl_def_t.name)); \ + printf("# define ggd_%s %d\n", #name, (int)dim2); + +int main() +{ + gtm_threadgbl_def_t gtd; + + /* Now run through each var in the structure generating defines for the type and offset within the structure */ +# define THREADGBLDEF(name, type) PRINT_TYPE_OFFSET(name, type) +# define THREADGBLFPTR(name, type, args) PRINT_TYPE_OFFSET_FPTR(name, type, args) +# define THREADGBLAR1DEF(name, type, dim1) PRINT_TYPE_OFFSET_ARY1(name, type, dim1) +# define THREADGBLAR2DEF(name, type, dim1, dim2) PRINT_TYPE_OFFSET_ARY1(name, type, dim1, dim2) +# include "gtm_threadgbl_defs.h" +# undef THREADGBLDEF +# undef THREADGBLFPTR +# undef THREADGBLAR1DEF +# undef THREADGBLAR2DEF + printf("# define size_gtm_threadgbl_struct %d\n", (int)SIZEOF(gtm_threadgbl_def_t)); +} diff --git a/sr_port/gtm_threadgbl_init.c b/sr_port/gtm_threadgbl_init.c new file mode 100644 index 0000000..7b31da2 --- /dev/null +++ b/sr_port/gtm_threadgbl_init.c @@ -0,0 +1,217 @@ +/**************************************************************** + * * + * Copyright 2010, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + + +#include "mdef.h" +/* Note that since this routine is called prior to reading environment vars or pretty much any + * other initialization, we cannot use gtm_malloc() yet so care is taken to use the real system + * malloc. + */ +#undef malloc +#undef free + +#include +#include "gtm_stdio.h" +#include "gtm_stdlib.h" +#include "gtm_string.h" +#include "gtm_inet.h" +#include "gtm_iconv.h" +#include "gtm_socket.h" +#include "gtm_unistd.h" +#include "gtm_limits.h" + +#include +#include +#ifdef UNIX +# include +#endif +#ifdef VMS +# include /* Required for gtmsource.h */ +# include +# include +# include "desblk.h" +#endif +#include "cache.h" +#include "hashtab_addr.h" +#include "hashtab_int4.h" +#include "hashtab_int8.h" +#include "hashtab_mname.h" +#include "hashtab_str.h" +#include "hashtab_objcode.h" +#include "error.h" +#include "rtnhdr.h" +#include "gdsroot.h" +#include "gdskill.h" +#include "ccp.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gdscc.h" +#include "comline.h" +#include "compiler.h" +#include "cmd_qlf.h" +#include "io.h" +#include "iosp.h" +#include "jnl.h" +#include "lv_val.h" +#include "collseq.h" +#include "mdq.h" +#include "mprof.h" +#include "mv_stent.h" +#include "stack_frame.h" +#include "stp_parms.h" +#include "stringpool.h" +#include "buddy_list.h" /* needed for tp.h */ +#include "tp.h" +#include "tp_frame.h" +#include "mlkdef.h" +#include "zshow.h" +#include "zwrite.h" +#include "zbreak.h" +#include "fnpc.h" +#include "mmseg.h" +#ifndef VMS +# include "gtmsiginfo.h" +#endif +#include "gtmimagename.h" +#include "iotcpdef.h" +#include "gt_timer.h" +#include "iosocketdef.h" /* needed for socket_pool and MAX_N_SOCKETS*/ +#include "ctrlc_handler_dummy.h" +#include "unw_prof_frame_dummy.h" +#include "op.h" +#include "gtmsecshr.h" +#include "error_trap.h" +#include "patcode.h" /* for pat_everything and sizeof_pat_everything */ +#include "source_file.h" /* for REV_TIME_BUFF_LEN */ +#include "mupipbckup.h" +#include "dpgbldir.h" +#include "mmemory.h" +#include "have_crit.h" +#include "alias.h" +#include "zroutines.h" + +/* FOR REPLICATION RELATED GLOBALS */ +#include "repl_msg.h" +#include "gtmsource.h" +#include "gtmrecv.h" +#include "replgbl.h" + +/* FOR MERGE RELATED GLOBALS */ +#include "gvname_info.h" +#include "op_merge.h" + +#ifdef UNIX +# include "cli.h" +# include "invocation_mode.h" +# include "fgncal.h" +# include "parse_file.h" /* for MAX_FBUFF */ +# include "repl_sem.h" +# include "gtm_zlib.h" +# include "zro_shlibs.h" +#endif + +#include "jnl_typedef.h" + +#ifdef VMS +# include "gtm_logicals.h" /* for GTM_MEMORY_NOACCESS_COUNT */ +#endif + +#include "gds_blk_upgrade.h" /* for UPGRADE_IF_NEEDED flag */ +#include "cws_insert.h" /* for CWS_REORG_ARRAYSIZE */ + +#ifdef UNICODE_SUPPORTED +# include "gtm_icu_api.h" +# include "gtm_utf8.h" +#endif + +#ifdef GTM_CRYPT +# include "gtmcrypt.h" +# include "gdsblk.h" +# include "muextr.h" +#endif + +#ifdef GTM_TRIGGER +# include "gv_trigger.h" +# include "gtm_trigger.h" +#endif + +#include "gtm_threadgbl_init.h" + +#define DEFAULT_PROMPT "GTM>" + +GBLDEF void *gtm_threadgbl; /* Anchor for thread global for this thread */ + +/* Since gtm_threadgbl is type-neutral, define a structure mapping just for this routine that *does* + * contain the types. This structure can be accessed through the debugger for easier type dumping. + * Note we define this structure even in pro since it could be useful even in pro debugging (by gtmpcat). + */ +#define THREADGBLDEF(name,type) type name; +#define THREADGBLFPTR(name, type, args) type (*name)args; +#define THREADGBLAR1DEF(name, type, dim1) type name[dim1]; +#define THREADGBLAR2DEF(name, type, dim1, dim2) type name[dim1][dim2]; +typedef struct +{ +# include "gtm_threadgbl_defs.h" +} gtm_threadgbl_true_t; +#undef THREADGBLDEF +#undef THREADGBLFPTR +#undef THREADGBLAR1DEF +#undef THREADGBLAR2DEF + +GBLDEF gtm_threadgbl_true_t *gtm_threadgbl_true; + +/* This routine allocates the thread global structure and for now, since GTM is not yet threaded, + * anchors it in a global variable. This still improves access to global variables even in this + * paradym because the 3 step global dereference only need happen once per module. + */ + +error_def(ERR_MEMORY); +error_def(ERR_VMSMEMORY); +error_def(ERR_GTMASSERT); + +void gtm_threadgbl_init(void) +{ + void *lcl_gtm_threadgbl; + + if (SIZEOF(gtm_threadgbl_true_t) != size_gtm_threadgbl_struct) + { /* Size mismatch with gtm_threadgbl_deftypes.h - no error handling yet available so do + * the best we can. + */ + FPRINTF(stderr, "GTM-F-GTMASSERT gtm_threadgbl_true_t and gtm_threadgbl_t are different sizes\n"); + exit(ERR_GTMASSERT); + } + if (NULL != gtm_threadgbl) + { /* has already been initialized - don't re-init */ + FPRINTF(stderr, "GTM-F-GTMASSERT gtm_threadgbl is already initialized\n"); + exit(ERR_GTMASSERT); + } + gtm_threadgbl = lcl_gtm_threadgbl = malloc(size_gtm_threadgbl_struct); + if (NULL == gtm_threadgbl) + { /* Storage was not allocated for some reason - no error handling yet still */ + perror("GTM-F-MEMORY Unable to allocate startup thread structure"); + exit(UNIX_ONLY(ERR_MEMORY) VMS_ONLY(ERR_VMSMEMORY)); + } + memset(gtm_threadgbl, 0, size_gtm_threadgbl_struct); + gtm_threadgbl_true = (gtm_threadgbl_true_t *)gtm_threadgbl; + + /* Add specific initializations if other than 0s here using the TREF() family of macros: */ + MEMCPY_LIT(TADR(prombuf), DEFAULT_PROMPT); + (TREF(gtmprompt)).addr = TADR(prombuf); + (TREF(gtmprompt)).len = SIZEOF(DEFAULT_PROMPT) - 1; + TREF(lv_null_subs) = LVNULLSUBS_OK; /* UNIX: set in gtm_env_init_sp(), VMS: set in gtm$startup() - init'd here + * in case alternative invocation methods bypass gtm_startup() + */ + TREF(for_stack_ptr) = TADR(for_stack); + (TREF(replgbl)).jnl_release_timeout = DEFAULT_JNL_RELEASE_TIMEOUT; +} diff --git a/sr_port/gtm_threadgbl_init.h b/sr_port/gtm_threadgbl_init.h new file mode 100644 index 0000000..dcadcaa --- /dev/null +++ b/sr_port/gtm_threadgbl_init.h @@ -0,0 +1,18 @@ +/**************************************************************** + * * + * Copyright 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + + +#ifndef GTM_THREADGBL_INIT_included +#define GTM_THREADGBL_INIT_included + +void gtm_threadgbl_init(void); + +#endif diff --git a/sr_port/gtm_time.h b/sr_port/gtm_time.h new file mode 100644 index 0000000..13a694d --- /dev/null +++ b/sr_port/gtm_time.h @@ -0,0 +1,73 @@ +/**************************************************************** + * * + * Copyright 2001, 2003 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* gtm_time.h - interlude to system header file. */ +#ifndef GTM_TIMEH +#define GTM_TIMEH + +#include + +/* CTIME collides with linux define in termios */ + +#define GTM_CTIME ctime +#define GTM_CTIME_R ctime_r + +#define STRFTIME(dest, maxsize, format, timeptr, res) \ + res = strftime(dest, maxsize, format, timeptr) + +/* To use GET_CUR_TIME macro these definitions are required + * now_t now; char *time_ptr; char time_str[CTIME_BEFORE_NL + 2]; + */ +#if defined(VMS) + +typedef struct { + unsigned int buff1; + unsigned int buff2; +} now_t; + +#define CTIME_BEFORE_NL 20 + +#define GET_CUR_TIME \ +{ \ + uint4 time_status; \ + $DESCRIPTOR(atimenow, time_str); \ + \ + time_status = sys$asctim(0, &atimenow, 0, 0); \ + if (0 != (time_status & 1)) \ + { \ + time_str[CTIME_BEFORE_NL] = '\n'; \ + time_str[CTIME_BEFORE_NL + 1] = '\0'; \ + time_ptr = time_str; \ + } else \ + time_ptr = "* sys$asctim failed*\n"; /* keep string len same as CTIME_BEFORE_NL */ \ +} + +#elif defined(UNIX) + +typedef time_t now_t; + +#define CTIME_BEFORE_NL 24 +#define GET_CUR_TIME \ +{ \ + if ((time_t)-1 == (now = time(NULL))) \ + time_ptr = "****** time failed *****\n"; /* keep string len same as CTIME_BEFORE_NL */ \ + else if (NULL == (time_ptr = (char *)GTM_CTIME(&now))) \ + time_ptr = "***** ctime failed *****\n"; /* keep string len same as CTIME_BEFORE_NL */ \ + else \ + { \ + memcpy(time_str, time_ptr, CTIME_BEFORE_NL + 2); \ + time_ptr = time_str; \ + } \ +} + +#endif /* UNIX, VMS */ + +#endif diff --git a/sr_port/gtm_unistd.h b/sr_port/gtm_unistd.h new file mode 100644 index 0000000..fb2de8d --- /dev/null +++ b/sr_port/gtm_unistd.h @@ -0,0 +1,91 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* gtm_unistd.h - interlude to system header file. */ +#ifndef GTM_UNISTDH +#define GTM_UNISTDH + +#include + +#define CHDIR chdir + +#define CHOWN chown + +#define GETUID getuid + +#define GETGID getgid + +#if defined(VMS) + +#define GTM_MAX_DIR_LEN (PATH_MAX + PATH_MAX) /* DEVICE + DIRECTORY */ +#define GTM_VMS_STYLE_CWD 1 +#define GTM_UNIX_STYLE_CWD 0 + +#define GETCWD(buffer, size, getcwd_res) \ + (getcwd_res = getcwd(buffer, size, GTM_VMS_STYLE_CWD)) /* force VMS style always 'cos many other parts of GT.M always + * do it the VMS way */ +#else /* !VMS => UNIX */ + +#define GTM_MAX_DIR_LEN (PATH_MAX + 1) /* DIRECTORY + terminating '\0' */ + +#define GETCWD(buffer, size, getcwd_res) \ + (getcwd_res = getcwd(buffer, size)) + +#endif + +#ifndef UNICODE_SUPPORTED +#define GETHOSTNAME(name,namelen,gethostname_res) \ + (gethostname_res = gethostname(name, namelen)) +#else +#include "gtm_utf8.h" +GBLREF boolean_t gtm_utf8_mode; +#define GETHOSTNAME(name,namelen,gethostname_res) \ + (gethostname_res = gethostname(name, namelen), \ + gtm_utf8_mode ? gtm_utf8_trim_invalid_tail((unsigned char *)name, namelen) : 0, \ + gethostname_res) +#endif + +#define LINK link + +#define UNLINK unlink + +#define TTYNAME ttyname + +#define ACCESS access + +#define EXECL execl +#define EXECV execv +#define EXECVE execve + +#define TRUNCATE truncate + +#ifdef GTM_FD_TRACE +/* Just like open and close were noted down in gtm_fcntl.h, note down all macros which we are redefining here and could + * potentially have been conflictingly defined by the system header file "unistd.h". The system define will be used + * in gtm_fd_trace.c within the implementation of the GT.M interlude function. Currently none of these functions (close, + * pipe, dup, dup2) are defined by the system so it is not theoretically necessary but they could be defined in the future. + */ +# undef close /* in case this is already defined by */ +# undef pipe /* in case this is already defined by */ +# undef dup /* in case this is already defined by */ +# undef dup2 /* in case this is already defined by */ +# define close gtm_close +# define pipe gtm_pipe1 /* gtm_pipe is already used so using pipe1 */ +# define dup gtm_dup +# define dup2 gtm_dup2 +#endif + +int gtm_close(int fd); +int gtm_pipe1(int pipefd[2]); +int gtm_dup(int oldfd); +int gtm_dup2(int oldfd, int newfd); + +#endif diff --git a/sr_port/gtm_utsname.h b/sr_port/gtm_utsname.h new file mode 100644 index 0000000..444236c --- /dev/null +++ b/sr_port/gtm_utsname.h @@ -0,0 +1,20 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* gtm_utsname.h - interlude to system header file. */ +#ifndef GTM_UTSNAMEH +#define GTM_UTSNAMEH + +#include + +#define UNAME(name,uname_res) (uname_res = uname(name)) + +#endif diff --git a/sr_port/gtm_wake.h b/sr_port/gtm_wake.h new file mode 100644 index 0000000..fbc4899 --- /dev/null +++ b/sr_port/gtm_wake.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#ifndef __GTM_WAKE_H__ +#define __GTM_WAKE_H__ + +void gtm_wake(int4 *pidadr,char *prcnam); + +#endif + diff --git a/sr_port/gtmctype.h b/sr_port/gtmctype.h new file mode 100644 index 0000000..68437b0 --- /dev/null +++ b/sr_port/gtmctype.h @@ -0,0 +1,12 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#define PRINTABLE(X) ((X) < 127 && (X) > 31) diff --git a/sr_port/gtmdbglvl.h b/sr_port/gtmdbglvl.h new file mode 100644 index 0000000..a4a2343 --- /dev/null +++ b/sr_port/gtmdbglvl.h @@ -0,0 +1,43 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GTMDBGLVL_H_INCLUDED +#define GTMDBGLVL_H_INCLUDED + +/* Define GT.M debug levels. These values can be added together to turn on multiple + features at the same time. Note that the cumulative value specified in the logical + or environment variable must currently be specified in decimal. */ +#define GDL_None 0x00000000 /* (000) No debugging is happening today */ +#define GDL_Simple 0x00000001 /* (001) Regular assert checking, no special checks */ +#define GDL_SmStats 0x00000002 /* (002) Print usage statistics at end of process */ +#define GDL_SmTrace 0x00000004 /* (004) Trace each malloc/free (output to stderr) */ +#define GDL_SmDumpTrace 0x00000008 /* (008) Dump malloc/free trace information on exit */ +#define GDL_SmAllocVerf 0x00000010 /* (016) Perform verification of allocated storage chain for each call */ +#define GDL_SmFreeVerf 0x00000020 /* (032) Perform simple verification of free storage chain for each call */ +#define GDL_SmBackfill 0x00000040 /* (064) Backfill unused storage (cause exceptions if released storage is used */ +#define GDL_SmChkAllocBackfill 0x00000080 /* (128) Verify backfilled storage in GDL_AllocVerf while verifying \ + each individual queue entry */ +#define GDL_SmChkFreeBackfill 0x00000100 /* (256) Verify backfilled storage in GDL_FreeVerf while verifying \ + each individual queue entry */ +#define GDL_SmStorHog 0x00000200 /* (512) Each piece of storage allocated is allocated in an element twice \ + the desired size to provide glorious amounts of backfill for \ + overrun checking. */ +#define GDL_DumpOnStackOFlow 0x00000400 /* (1024) When get a stack overflow, generate a core */ +#define GDL_ZSHOWDumpOnSignal 0x00000800 /* (2048) Don't supress GTM_FATAL file creation when get a signal */ +#define GDL_PrintIndCacheStats 0x00001000 /* (4096) Print indirect cacheing stats */ +#define GDL_PrintPieceStats 0x00002000 /* (8192) Print stats on $Piece cacheing (debug only) */ +#define GDL_DebugCompiler 0x00004000 /* (16384) Turn on compiler debugging */ +#define GDL_SmDump 0x00008000 /* (32768) Do full blown storage dump -- only useful in debug mode */ +#define GDL_PrintEntryPoints 0x00010000 /* (65536) Print address of entry points when they are loaded/resolved */ +#define GDL_PrintSockIntStats 0x00020000 /* (131072) Print Socket interrupt stats on exit */ +#define GDL_SmInitAlloc 0x00040000 /* (262144) Initialize all storage allocated or deallocated with 0xdeadbeef */ +#define GDL_PrintPipeIntStats 0x00080000 /* (524288) Print Pipe/Fifo(rm) interrupt stats on exit */ +#endif diff --git a/sr_port/gtmimagename.h b/sr_port/gtmimagename.h new file mode 100644 index 0000000..f13b5b0 --- /dev/null +++ b/sr_port/gtmimagename.h @@ -0,0 +1,46 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GTMIMAGENAME_DEF +#define GTMIMAGENAME_DEF + +typedef struct +{ + char *imageName; + int imageNameLen; +} gtmImageName; + +enum gtmImageTypes +{ +#define IMAGE_TABLE_ENTRY(A,B) A, +#include "gtmimagetable.h" +#undef IMAGE_TABLE_ENTRY + n_image_types +}; + +#define GTMIMAGENAMETXT(x) gtmImageNames[x].imageNameLen, gtmImageNames[x].imageName + +GBLREF enum gtmImageTypes image_type; /* needed by IS_MUMPS_IMAGE and IS_GTM_IMAGE macros */ +GBLREF boolean_t run_time; /* needed by IS_MCODE_RUNNING macro */ + +#define IS_MCODE_RUNNING (run_time) + +#define IS_DSE_IMAGE (DSE_IMAGE == image_type) +#define IS_GTCM_GNP_SERVER_IMAGE (GTCM_GNP_SERVER_IMAGE == image_type) +#define IS_GTMSECSHR_IMAGE (GTMSECSHR_IMAGE == image_type) +#define IS_GTM_IMAGE IS_MUMPS_IMAGE +#define IS_GTM_SVC_DAL_IMAGE (GTM_SVC_DAL_IMAGE == image_type) +#define IS_LKE_IMAGE (LKE_IMAGE == image_type) +#define IS_MUMPS_IMAGE (GTM_IMAGE == image_type) +#define IS_MUPIP_IMAGE (MUPIP_IMAGE == image_type) +#define IS_VALID_IMAGE (INVALID_IMAGE != image_type) + +#endif diff --git a/sr_port/gtmimagetable.h b/sr_port/gtmimagetable.h new file mode 100644 index 0000000..1554b81 --- /dev/null +++ b/sr_port/gtmimagetable.h @@ -0,0 +1,24 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* + * IMAGE_TABLE_ENTRY(image_type, image-name) + */ +IMAGE_TABLE_ENTRY (INVALID_IMAGE, "INVALID_IMAGE") +IMAGE_TABLE_ENTRY (GTM_IMAGE, "GT.M") +IMAGE_TABLE_ENTRY (MUPIP_IMAGE, "MUPIP") +IMAGE_TABLE_ENTRY (DSE_IMAGE, "DSE") +IMAGE_TABLE_ENTRY (LKE_IMAGE, "LKE") +IMAGE_TABLE_ENTRY (GTMSECSHR_IMAGE, "GTMSECSHR") +IMAGE_TABLE_ENTRY (GTCM_SERVER_IMAGE, "GTCM_SERVER") +IMAGE_TABLE_ENTRY (GTCM_GNP_SERVER_IMAGE, "GTCM_GNP_SERVER") +IMAGE_TABLE_ENTRY (DBCERTIFY_IMAGE, "DBCERTIFY") +IMAGE_TABLE_ENTRY (GTM_SVC_DAL_IMAGE, "GTM_SVC_DAL") diff --git a/sr_port/gtmmsg.h b/sr_port/gtmmsg.h new file mode 100644 index 0000000..d4fcaff --- /dev/null +++ b/sr_port/gtmmsg.h @@ -0,0 +1,27 @@ +/**************************************************************** + * * + * Copyright 2001, 2005 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GTMMSG_H_INCLUDED +#define GTMMSG_H_INCLUDED + + +#ifdef VMS +void gtm_getmsg(uint4 msgnum, mstr *msgbuf); +void gtm_putmsg(int4 msgid, ...); +#elif defined(UNIX) +void gtm_getmsg(int4 msgnum, mstr *msgbuf); +void gtm_putmsg(int argcnt, ...); +void gtm_putmsg_noflush(int argcnt, ...); +#else +#error Unsupported platform +#endif + +#endif /* GTMMSG_H_INCLUDED */ diff --git a/sr_port/gtmrecv_ch.c b/sr_port/gtmrecv_ch.c new file mode 100644 index 0000000..3281d5a --- /dev/null +++ b/sr_port/gtmrecv_ch.c @@ -0,0 +1,35 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "error.h" + +CONDITION_HANDLER(gtmrecv_ch) +{ + error_def(ERR_ASSERT); + error_def(ERR_CTRLC); + error_def(ERR_FORCEDHALT); + error_def(ERR_GTMCHECK); + error_def(ERR_GTMASSERT); + error_def(ERR_MEMORY); + error_def(ERR_VMSMEMORY); + error_def(ERR_STACKOFLOW); + error_def(ERR_OUTOFSPACE); + + START_CH; + if (!(IS_GTM_ERROR(SIGNAL)) || DUMPABLE || SEVERITY == ERROR) + { + NEXTCH; + } + /* warning, info, or success */ + CONTINUE; +} diff --git a/sr_port/gtmrecv_changelog.c b/sr_port/gtmrecv_changelog.c new file mode 100644 index 0000000..e091e8a --- /dev/null +++ b/sr_port/gtmrecv_changelog.c @@ -0,0 +1,99 @@ +/**************************************************************** + * * + * Copyright 2001, 2005 Fidelity Information Services, Inc.* + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_time.h" +#include "gtm_string.h" +#include "gtm_inet.h" + +#include +#ifdef VMS +#include /* Required for gtmrecv.h */ +#endif + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gtmrecv.h" +#include "repl_dbg.h" +#include "repl_shutdcode.h" +#include "repl_sem.h" +#include "util.h" +#include "repl_log.h" + +GBLREF recvpool_addrs recvpool; +GBLREF gtmrecv_options_t gtmrecv_options; + +int gtmrecv_changelog(void) +{ + uint4 changelog_desired = 0, changelog_accepted = 0; + + /* Grab the recvpool jnlpool option write lock */ + if (0 > grab_sem(RECV, RECV_SERV_OPTIONS_SEM)) + { + util_out_print("Error grabbing recvpool option write lock. Could not initiate change log", TRUE); + return (ABNORMAL_SHUTDOWN); + } + if (0 != recvpool.gtmrecv_local->changelog || 0 != recvpool.upd_proc_local->changelog) + { + util_out_print("Change log is already in progress. Not initiating change in log file or log interval", TRUE); + rel_sem(RECV, RECV_SERV_OPTIONS_SEM); + return (ABNORMAL_SHUTDOWN); + } + if ('\0' != gtmrecv_options.log_file[0]) /* trigger change in log file (for both receiver and update process) */ + { + changelog_desired |= REPLIC_CHANGE_LOGFILE; + if (0 != strcmp(recvpool.gtmrecv_local->log_file, gtmrecv_options.log_file)) + { + changelog_accepted |= REPLIC_CHANGE_LOGFILE; + strcpy(recvpool.gtmrecv_local->log_file, gtmrecv_options.log_file); + util_out_print("Change log initiated with file !AD", TRUE, LEN_AND_STR(gtmrecv_options.log_file)); + } else + util_out_print("Log file is already !AD. Not initiating change in log file", TRUE, + LEN_AND_STR(gtmrecv_options.log_file)); + } + if (0 != gtmrecv_options.rcvr_log_interval) /* trigger change in receiver log interval */ + { + changelog_desired |= REPLIC_CHANGE_LOGINTERVAL; + if (gtmrecv_options.rcvr_log_interval != recvpool.gtmrecv_local->log_interval) + { + changelog_accepted |= REPLIC_CHANGE_LOGINTERVAL; + recvpool.gtmrecv_local->log_interval = gtmrecv_options.rcvr_log_interval; + util_out_print("Change initiated with receiver log interval !UL", TRUE, gtmrecv_options.rcvr_log_interval); + } else + util_out_print("Receiver log interval is already !UL. Not initiating change in log interval", TRUE, + gtmrecv_options.rcvr_log_interval); + } + if (0 != gtmrecv_options.upd_log_interval) /* trigger change in update process log interval */ + { + changelog_desired |= REPLIC_CHANGE_UPD_LOGINTERVAL; + if (gtmrecv_options.upd_log_interval != recvpool.upd_proc_local->log_interval) + { + changelog_accepted |= REPLIC_CHANGE_UPD_LOGINTERVAL; + recvpool.upd_proc_local->log_interval = gtmrecv_options.upd_log_interval; + util_out_print("Change initiated with update process log interval !UL", TRUE, + gtmrecv_options.upd_log_interval); + } else + util_out_print("Update process log interval is already !UL. Not initiating change in log interval", TRUE, + gtmrecv_options.upd_log_interval); + } + if (0 != changelog_accepted) + recvpool.gtmrecv_local->changelog = changelog_accepted; + else + util_out_print("No change to log file or log interval", TRUE); + rel_sem(RECV, RECV_SERV_OPTIONS_SEM); + return ((0 != changelog_accepted && changelog_accepted == changelog_desired) ? NORMAL_SHUTDOWN : ABNORMAL_SHUTDOWN); +} diff --git a/sr_port/gtmrecv_checkhealth.c b/sr_port/gtmrecv_checkhealth.c new file mode 100644 index 0000000..2236098 --- /dev/null +++ b/sr_port/gtmrecv_checkhealth.c @@ -0,0 +1,178 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_time.h" +#include +#include "gtm_inet.h" +#include "gtm_string.h" +#ifdef UNIX +#include +#endif +#ifdef VMS +#include /* Required for gtmrecv.h */ +#endif + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gtmrecv.h" +#include "repl_dbg.h" +#include "gtm_stdio.h" +#include "repl_shutdcode.h" +#include "repl_sem.h" +#include "repl_sp.h" +#include "repl_log.h" +#include "is_proc_alive.h" + +GBLREF recvpool_addrs recvpool; +GBLREF gtmrecv_options_t gtmrecv_options; + +int is_srv_alive(int srv_type) +{ + int status, semval; + boolean_t start_wait_logged; + uint4 srv_pid; + boolean_t srv_alive; + + srv_pid = (GTMRECV == srv_type) ? recvpool.gtmrecv_local->recv_serv_pid : recvpool.upd_proc_local->upd_proc_pid; + if (0 < srv_pid) + { + if (srv_alive = is_proc_alive(srv_pid, 0)) + semval = get_sem_info(RECV, (GTMRECV == srv_type) ? RECV_SERV_COUNT_SEM : UPD_PROC_COUNT_SEM, SEM_INFO_VAL); + if (srv_alive && 1 == semval) + status = SRV_ALIVE; + else if (!srv_alive || 0 == semval) + status = SRV_DEAD; + else + status = SRV_ERR; + } else + status = SRV_DEAD; + return (status); +} + +int is_updproc_alive(void) +{ + return (is_srv_alive(UPDPROC)); +} + +int is_recv_srv_alive(void) +{ + return (is_srv_alive(GTMRECV)); +} + +int gtmrecv_checkhealth(void) +{ + int rcv_status, upd_status, helper_status; + uint4 gtmrecv_pid, updproc_pid, updproc_pid_prev, helper_pid; + boolean_t helper_alive; + upd_helper_ctl_ptr_t upd_helper_ctl; + upd_helper_entry_ptr_t helper, helper_top; + recvpool_user helper_type; + + /* Grab the recvpool option write lock */ + + if (0 > grab_sem(RECV, RECV_SERV_OPTIONS_SEM)) + { + repl_log(stderr, FALSE, TRUE, "Error grabbing recvpool option write lock : %s. Could not check health of Receiver" + "Server/Update Process\n", REPL_SEM_ERROR); + return (((SRV_ERR << 2) | SRV_ERR) + NORMAL_SHUTDOWN); + } + + gtmrecv_pid = recvpool.gtmrecv_local->recv_serv_pid; + updproc_pid = recvpool.upd_proc_local->upd_proc_pid; + updproc_pid_prev = recvpool.upd_proc_local->upd_proc_pid_prev; + + REPL_DPRINT1("Checking health of Receiver Server/Update Process\n"); + + rcv_status = is_recv_srv_alive(); + upd_status = is_updproc_alive(); + + switch(rcv_status) + { + case SRV_ALIVE: + repl_log(stderr, FALSE, TRUE, FORMAT_STR, gtmrecv_pid, "Receiver server", ""); + break; + + case SRV_DEAD: + repl_log(stderr, FALSE, TRUE, FORMAT_STR, gtmrecv_pid, "Receiver server", " NOT"); + if (0 == gtmrecv_pid) + { + if (NO_SHUTDOWN == recvpool.gtmrecv_local->shutdown) + repl_log(stderr, FALSE, TRUE, + "Receiver Server crashed during receive pool initialization\n"); + else + repl_log(stderr, FALSE, TRUE, "Receiver server crashed during shutdown\n"); + } + break; + + case SRV_ERR: + repl_log(stderr, FALSE, TRUE, "Error finding health of receiver server\n"); + break; + } + + switch(upd_status) + { + case SRV_ALIVE: + repl_log(stderr, FALSE, TRUE, FORMAT_STR, updproc_pid, "Update process", ""); + break; + + case SRV_DEAD: + repl_log(stderr, FALSE, TRUE, FORMAT_STR, updproc_pid ? updproc_pid : updproc_pid_prev, + "Update process", " NOT"); + if (0 == updproc_pid) + { + if (NO_SHUTDOWN == recvpool.upd_proc_local->upd_proc_shutdown) + repl_log(stderr, FALSE, TRUE, "Update Process crashed during initialization\n"); + else + repl_log(stderr, FALSE, TRUE, "Update Process crashed during shutdown\n"); + } + break; + + case SRV_ERR: + repl_log(stdout, FALSE, TRUE, "Error in finding health of update process\n"); + break; + } + + helper_status = SRV_ALIVE; + if (gtmrecv_options.helpers) + { + upd_helper_ctl = recvpool.upd_helper_ctl; + for (helper = upd_helper_ctl->helper_list, helper_top = helper + MAX_UPD_HELPERS; helper < helper_top; helper++) + { + if (0 != (helper_pid = helper->helper_pid_prev)) + { + helper_type = (recvpool_user)helper->helper_type; + helper_alive = is_proc_alive(helper_pid, 0); + if (helper_alive && 0 == helper->helper_pid) /* process has vacated its slot, but the rcvr hasn't */ + helper_alive = FALSE; /* salvaged it yet. Unix zombies are alive,* er, half dead */ + helper_status = (SRV_ALIVE == helper_status && helper_alive) ? SRV_ALIVE : SRV_DEAD; + repl_log(stderr, FALSE, TRUE, FORMAT_STR, helper_pid, + (UPD_HELPER_READER == helper_type) ? "Helper reader" : "Helper writer", + helper_alive ? "" : " NOT"); + } + } + if (SRV_DEAD == helper_status) + { /* indicate to the receiver that it has to reap helpers */ + upd_helper_ctl->reap_helpers = HELPER_REAP_NOWAIT; + while (HELPER_REAP_NONE != upd_helper_ctl->reap_helpers && SRV_ALIVE == is_recv_srv_alive()) + SHORT_SLEEP(GTMRECV_WAIT_FOR_UPD_SHUTDOWN); + upd_helper_ctl->reap_helpers = HELPER_REAP_NONE; /* just in case recvr died */ + } + } + rel_sem(RECV, RECV_SERV_OPTIONS_SEM); + return ((rcv_status | (upd_status << 2) | (helper_status << 4)) + NORMAL_SHUTDOWN); +} diff --git a/sr_port/gtmrecv_comm_init.c b/sr_port/gtmrecv_comm_init.c new file mode 100644 index 0000000..adf596f --- /dev/null +++ b/sr_port/gtmrecv_comm_init.c @@ -0,0 +1,104 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gtm_socket.h" +#include "gtm_inet.h" +#include "gtm_fcntl.h" + +#include +#include +#include "gtm_unistd.h" +#ifdef VMS +#include /* Required for gtmrecv.h */ +#endif + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gtmrecv.h" +#include "repl_sp.h" +#include "gtmio.h" + +GBLDEF int gtmrecv_listen_sock_fd = FD_INVALID; + +/* Initialize communication stuff */ +int gtmrecv_comm_init(in_port_t port) +{ + struct sockaddr_in secondary_addr; + const int enable_reuseaddr = 1; + const int disable_keepalive = 1; + struct linger disable_linger = {0, 0}; + int rc; + + error_def(ERR_REPLCOMM); + error_def(ERR_TEXT); + + if (FD_INVALID != gtmrecv_listen_sock_fd) /* Initialization done already */ + return (0); + + /* Create the socket used for communicating with primary */ + if (FD_INVALID == (gtmrecv_listen_sock_fd = socket(AF_INET, SOCK_STREAM, 0))) + { + rts_error(VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, + RTS_ERROR_LITERAL("Error with receiver server socket create"), ERRNO); + return (-1); + } + + if (0 > setsockopt(gtmrecv_listen_sock_fd, SOL_SOCKET, SO_LINGER, (const void *)&disable_linger, SIZEOF(disable_linger))) + rts_error(VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, + RTS_ERROR_LITERAL("Error with receiver server listen socket disable linger"), ERRNO); + +#ifdef REPL_DISABLE_KEEPALIVE + if (0 > setsockopt(gtmrecv_listen_sock_fd, SOL_SOCKET, SO_KEEPALIVE, (const void *)&disable_keepalive, + SIZEOF(disable_keepalive))) + { + /* Till SIGPIPE is handled properly */ + rts_error(VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, + RTS_ERROR_LITERAL("Error with receiver server listen socket disable keepalive"), ERRNO); + } +#endif + + if (0 > setsockopt(gtmrecv_listen_sock_fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&enable_reuseaddr, + SIZEOF(enable_reuseaddr))) + { + rts_error(VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, + RTS_ERROR_LITERAL("Error with receiver server listen socket enable reuseaddr"), ERRNO); + } + + /* Make it known to the world that you are ready for a Source Server */ + memset((char *)&secondary_addr, 0, SIZEOF(secondary_addr)); + secondary_addr.sin_family = AF_INET; + secondary_addr.sin_addr.s_addr = htonl(INADDR_ANY); + secondary_addr.sin_port = htons(port); + + if (0 > BIND(gtmrecv_listen_sock_fd, (struct sockaddr *)&secondary_addr, SIZEOF(secondary_addr))) + { + rts_error(VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Could not bind local address"), ERRNO); + CLOSEFILE_RESET(gtmrecv_listen_sock_fd, rc); /* resets "gtmrecv_listen_sock_fd" to FD_INVALID */ + return (-1); + } + + if (0 > listen(gtmrecv_listen_sock_fd, 5)) + { + rts_error(VARLSTCNT(7) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Could not listen"), ERRNO); + CLOSEFILE_RESET(gtmrecv_listen_sock_fd, rc); /* resets "gtmrecv_listen_sock_fd" to FD_INVALID */ + return (-1); + } + + return (0); +} diff --git a/sr_port/gtmrecv_end_helpers.c b/sr_port/gtmrecv_end_helpers.c new file mode 100644 index 0000000..80364b3 --- /dev/null +++ b/sr_port/gtmrecv_end_helpers.c @@ -0,0 +1,59 @@ +/**************************************************************** + * * + * Copyright 2005 Fidelity Information Services, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" + +#ifdef UNIX +#include +#elif defined(VMS) +#include /* Required for gtmrecv.h */ +#endif + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "iosp.h" +#include "repl_shutdcode.h" +#include "gtmrecv.h" +#include "is_proc_alive.h" +#include "eintr_wrappers.h" +#include "repl_log.h" + +GBLREF recvpool_addrs recvpool; + +int gtmrecv_end_helpers(boolean_t is_rcvr_srvr) +{ /* Set flag in recvpool telling the receiver server to stop all reader and writer helpers. + * Wait for receiver server to complete the processs - all processes shut down, or some error occurred + */ + upd_helper_ctl_ptr_t upd_helper_ctl; + upd_helper_entry_ptr_t helper, helper_top; + + repl_log(stdout, TRUE, TRUE, "Initiating shut down of Helpers\n"); + upd_helper_ctl = recvpool.upd_helper_ctl; + for (helper = upd_helper_ctl->helper_list, helper_top = helper + MAX_UPD_HELPERS; helper < helper_top; helper++) + helper->helper_shutdown = SHUTDOWN; /* indicate to the helper to shut down */ + if (is_rcvr_srvr) + gtmrecv_reap_helpers(TRUE); + else + { + upd_helper_ctl->reap_helpers = HELPER_REAP_WAIT; + while (HELPER_REAP_NONE != upd_helper_ctl->reap_helpers && SRV_ALIVE == is_recv_srv_alive()) + SHORT_SLEEP(GTMRECV_WAIT_FOR_UPD_SHUTDOWN); + upd_helper_ctl->reap_helpers = HELPER_REAP_NONE; + } + return NORMAL_SHUTDOWN; +} diff --git a/sr_port/gtmrecv_exit.c b/sr_port/gtmrecv_exit.c new file mode 100644 index 0000000..61b4c9e --- /dev/null +++ b/sr_port/gtmrecv_exit.c @@ -0,0 +1,42 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdlib.h" /* for exit() */ + +#ifdef VMS +#include +#include +#endif + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "iosp.h" +#include "repl_shutdcode.h" +#include "gtmrecv.h" +#include "repl_log.h" +#include "repl_dbg.h" + +void gtmrecv_exit(int exit_status) +{ + error_def(ERR_REPLEXITERR); +#ifdef VMS + sys$exit((0 == exit_status) ? SS$_NORMAL : ERR_REPLEXITERR); +#else + exit(exit_status); +#endif +} diff --git a/sr_port/gtmrecv_get_opt.c b/sr_port/gtmrecv_get_opt.c new file mode 100644 index 0000000..edc3e0c --- /dev/null +++ b/sr_port/gtmrecv_get_opt.c @@ -0,0 +1,212 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc.* + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#include "mdef.h" + +#include "gtm_limits.h" +#include "gtm_stdlib.h" +#include "gtm_string.h" +#include "gtm_unistd.h" +#include "gtm_ctype.h" +#include "gtm_inet.h" +#include +#ifdef VMS +#include /* Required for gtmrecv.h */ +#endif + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gtmrecv.h" +#include "cli.h" +#include "gtm_stdio.h" +#include "util.h" +#include "repl_log.h" + +#ifdef UNIX +#include "gtm_zlib.h" +#endif + +GBLREF gtmrecv_options_t gtmrecv_options; + +int gtmrecv_get_opt(void) +{ + + boolean_t log, log_interval_specified; + unsigned short log_file_len, filter_cmd_len; + boolean_t buffsize_status; + boolean_t filter; + int status; + unsigned short statslog_val_len; + char statslog_val[4]; /* "ON" or "OFF" */ + uint4 n_readers, n_helpers; + boolean_t cmplvl_status; + + gtmrecv_options.start = (CLI_PRESENT == cli_present("START")); + gtmrecv_options.shut_down = (CLI_PRESENT == cli_present("SHUTDOWN")); + gtmrecv_options.checkhealth = (CLI_PRESENT == cli_present("CHECKHEALTH")); + gtmrecv_options.statslog = (CLI_PRESENT == cli_present("STATSLOG")); + gtmrecv_options.showbacklog = (CLI_PRESENT == cli_present("SHOWBACKLOG")); + gtmrecv_options.changelog = (CLI_PRESENT == cli_present("CHANGELOG")); + gtmrecv_options.updateonly = (CLI_PRESENT == cli_present("UPDATEONLY")); + gtmrecv_options.updateresync = (CLI_PRESENT == cli_present("UPDATERESYNC")); + gtmrecv_options.helpers = (CLI_PRESENT == cli_present("HELPERS")); + + gtmrecv_options.listen_port = 0; /* invalid port; indicates listenport not specified */ + if (gtmrecv_options.start && CLI_PRESENT == cli_present("LISTENPORT")) + { + if (!cli_get_int("LISTENPORT", >mrecv_options.listen_port)) + { + util_out_print("Error parsing LISTENPORT qualifier", TRUE); + return (-1); + } + if (buffsize_status = (CLI_PRESENT == cli_present("BUFFSIZE"))) + { + if (!cli_get_int("BUFFSIZE", >mrecv_options.buffsize)) + { + util_out_print("Error parsing BUFFSIZE qualifier", TRUE); + return (-1); + } + if (MIN_RECVPOOL_SIZE > gtmrecv_options.buffsize) + gtmrecv_options.buffsize = MIN_RECVPOOL_SIZE; + } else + gtmrecv_options.buffsize = DEFAULT_RECVPOOL_SIZE; +# ifdef UNIX + /* Check if compression level is specified */ + if (cmplvl_status = (CLI_PRESENT == cli_present("CMPLVL"))) + { + if (!cli_get_int("CMPLVL", >mrecv_options.cmplvl)) + { + util_out_print("Error parsing CMPLVL qualifier", TRUE); + return(-1); + } + if (GTM_CMPLVL_OUT_OF_RANGE(gtmrecv_options.cmplvl)) + gtmrecv_options.cmplvl = ZLIB_CMPLVL_MIN; /* no compression in this case */ + /* CMPLVL qualifier should override any value specified in the environment variable gtm_zlib_cmp_level */ + gtm_zlib_cmp_level = gtmrecv_options.cmplvl; + } else + gtmrecv_options.cmplvl = ZLIB_CMPLVL_MIN; /* no compression in this case */ +# endif + /* Round up or down buffsize */ + + if (filter = (CLI_PRESENT == cli_present("FILTER"))) + { + filter_cmd_len = MAX_FILTER_CMD_LEN; + if (!cli_get_str("FILTER", gtmrecv_options.filter_cmd, &filter_cmd_len)) + { + util_out_print("Error parsing FILTER qualifier", TRUE); + return (-1); + } + } else + gtmrecv_options.filter_cmd[0] = '\0'; + + gtmrecv_options.stopsourcefilter = (CLI_PRESENT == cli_present("STOPSOURCEFILTER")); + } + + if ((gtmrecv_options.start && 0 != gtmrecv_options.listen_port) || gtmrecv_options.statslog || gtmrecv_options.changelog) + { + log = (CLI_PRESENT == cli_present("LOG")); + log_interval_specified = (CLI_PRESENT == cli_present("LOG_INTERVAL")); + if (log) + { + log_file_len = MAX_FN_LEN + 1; + if (!cli_get_str("LOG", gtmrecv_options.log_file, &log_file_len)) + { + util_out_print("Error parsing LOG qualifier", TRUE); + return (-1); + } + } else + gtmrecv_options.log_file[0] = '\0'; + gtmrecv_options.rcvr_log_interval = gtmrecv_options.upd_log_interval = 0; + if (log_interval_specified + && 0 == cli_parse_two_numbers("LOG_INTERVAL", GTMRECV_LOGINTERVAL_DELIM, >mrecv_options.rcvr_log_interval, + >mrecv_options.upd_log_interval)) + return (-1); + if (gtmrecv_options.start) + { + if (0 == gtmrecv_options.rcvr_log_interval) + gtmrecv_options.rcvr_log_interval = LOGTRNUM_INTERVAL; + if (0 == gtmrecv_options.upd_log_interval) + gtmrecv_options.upd_log_interval = LOGTRNUM_INTERVAL; + } /* For changelog, interval == 0 implies don't change log interval already established */ + /* We ignore interval specification for statslog, Vinaya 2005/02/07 */ + } + + if (gtmrecv_options.shut_down) + { + if (CLI_PRESENT == (status = cli_present("TIMEOUT"))) + { + if (!cli_get_int("TIMEOUT", >mrecv_options.shutdown_time)) + { + util_out_print("Error parsing TIMEOUT qualifier", TRUE); + return (-1); + } + if (DEFAULT_SHUTDOWN_TIMEOUT < gtmrecv_options.shutdown_time || 0 > gtmrecv_options.shutdown_time) + { + gtmrecv_options.shutdown_time = DEFAULT_SHUTDOWN_TIMEOUT; + util_out_print("shutdown TIMEOUT changed to !UL", TRUE, gtmrecv_options.shutdown_time); + } + } else if (CLI_NEGATED == status) + gtmrecv_options.shutdown_time = -1; + else /* TIMEOUT not specified */ + gtmrecv_options.shutdown_time = DEFAULT_SHUTDOWN_TIMEOUT; + } + + if (gtmrecv_options.statslog) + { + statslog_val_len = 4; /* max(strlen("ON"), strlen("OFF")) + 1 */ + if (!cli_get_str("STATSLOG", statslog_val, &statslog_val_len)) + { + util_out_print("Error parsing STATSLOG qualifier", TRUE); + return (-1); + } +#ifdef UNIX + cli_strupper(statslog_val); +#endif + if (0 == strcmp(statslog_val, "ON")) + gtmrecv_options.statslog = TRUE; + else if (0 == strcmp(statslog_val, "OFF")) + gtmrecv_options.statslog = FALSE; + else + { + util_out_print("Invalid value for STATSLOG qualifier, should be either ON or OFF", TRUE); + return (-1); + } + } + gtmrecv_options.n_readers = gtmrecv_options.n_writers = 0; + if (gtmrecv_options.helpers && gtmrecv_options.start) + { /* parse the helpers qualifier to find out how many readers and writes have to be started */ + if (0 == (status = cli_parse_two_numbers("HELPERS", UPD_HELPERS_DELIM, &n_helpers, &n_readers))) + return (-1); + if (!(status & CLI_2NUM_FIRST_SPECIFIED)) + n_helpers = DEFAULT_UPD_HELPERS; + if (MIN_UPD_HELPERS > n_helpers || MAX_UPD_HELPERS < n_helpers) + { + util_out_print("Invalid number of helpers; must be in the range [!UL,!UL]", TRUE, MIN_UPD_HELPERS, + MAX_UPD_HELPERS); + return (-1); + } + if (!(status & CLI_2NUM_SECOND_SPECIFIED)) + n_readers = (int)(n_helpers * ((float)DEFAULT_UPD_HELP_READERS)/DEFAULT_UPD_HELPERS); /* may round down */ + if (n_readers > n_helpers) + { + n_readers = n_helpers; + util_out_print("Number of readers exceeds number of helpers, reducing number of readers to number of " + "helpers", TRUE); + } + gtmrecv_options.n_readers = n_readers; + gtmrecv_options.n_writers = n_helpers - n_readers; + } + return (0); +} diff --git a/sr_port/gtmrecv_helpers_init.c b/sr_port/gtmrecv_helpers_init.c new file mode 100644 index 0000000..8090cb2 --- /dev/null +++ b/sr_port/gtmrecv_helpers_init.c @@ -0,0 +1,212 @@ +/**************************************************************** + * * + * Copyright 2005, 2009 Fidelity Information Services, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#ifdef UNIX +#include +#include "gtm_unistd.h" +#include +#elif defined(VMS) +#include /* Required for gtmrecv.h */ +#endif +#include + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "iosp.h" +#include "repl_shutdcode.h" +#include "gtmrecv.h" +#include "repl_dbg.h" +#include "repl_errno.h" +#include "gtm_stdio.h" +#include "repl_sem.h" +#include "io.h" +#include "is_proc_alive.h" +#include "gtmmsg.h" +#include "trans_log_name.h" +#include "repl_log.h" +#include "eintr_wrappers.h" +#include "memcoherency.h" +#include "getjobnum.h" + +#define UPDHELPER_CMD_MAXLEN 1024 +#define UPDHELPER_CMD "$gtm_dist/mupip" +#define UPDHELPER_CMD_FILE "mupip" +#define UPDHELPER_CMD_ARG1 "replicate" +#define UPDHELPER_CMD_ARG2 "-updhelper" +#define UPDHELPER_READER_CMD_ARG3 "-reader" +#define UPDHELPER_WRITER_CMD_ARG3 "-writer" +#define UPDHELPER_READER_CMD_STR "REPLICATE/UPDHELPER/READER" +#define UPDHELPER_WRITER_CMD_STR "REPLICATE/UPDHELPER/WRITER" +#define UPDHELPER_MBX_PREFIX "GTMH" /* first three must be GTM, and only character left for uniqueness */ + /* U for update process, R for receiver, S for source, H for helper */ + +GBLREF recvpool_addrs recvpool; +GBLREF FILE *gtmrecv_log_fp; +GBLREF uint4 process_id; + +static int helper_init(upd_helper_entry_ptr_t helper, recvpool_user helper_type) +{ + int save_errno, save_shutdown; + char helper_cmd[UPDHELPER_CMD_MAXLEN]; + int status; + int4 i4status; + mstr helper_log_cmd, helper_trans_cmd; + upd_helper_ctl_ptr_t upd_helper_ctl; +#ifdef UNIX + pid_t helper_pid, waitpid_res; +#elif defined(VMS) + uint4 helper_pid, cmd_channel; + char mbx_suffix[2 + 1]; /* hex representation of numbers 0 through MAX_UPD_HELPERS-1, +1 for '\0' */ + $DESCRIPTOR(cmd_desc_reader, UPDHELPER_READER_CMD_STR); + $DESCRIPTOR(cmd_desc_writer, UPDHELPER_WRITER_CMD_STR); +#endif + error_def(ERR_LOGTOOLONG); + error_def(ERR_RECVPOOLSETUP); + error_def(ERR_TEXT); + + upd_helper_ctl = recvpool.upd_helper_ctl; + save_shutdown = helper->helper_shutdown; + helper->helper_shutdown = NO_SHUTDOWN; +#ifdef UNIX + if (0 > (helper_pid = fork())) + { + save_errno = errno; + helper->helper_shutdown = save_shutdown; + gtm_putmsg(VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, + LEN_AND_LIT("Could not fork update process"), save_errno); + repl_errno = EREPL_UPDSTART_FORK; + return UPDPROC_START_ERR; + } + if (0 == helper_pid) + { /* helper */ + getjobnum(); + helper->helper_pid_prev = process_id; /* identify owner of slot */ + helper_log_cmd.len = STR_LIT_LEN(UPDHELPER_CMD); + helper_log_cmd.addr = UPDHELPER_CMD; + if (SS_NORMAL != (i4status = TRANS_LOG_NAME(&helper_log_cmd, &helper_trans_cmd, helper_cmd, SIZEOF(helper_cmd), + dont_sendmsg_on_log2long))) + { + helper->helper_shutdown = save_shutdown; + gtm_putmsg(VARLSTCNT(6) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, + LEN_AND_LIT("Could not find path of Helper Process. Check value of $gtm_dist")); + if (SS_LOG2LONG == i4status) + gtm_putmsg(VARLSTCNT(5) ERR_LOGTOOLONG, 3, LEN_AND_LIT(UPDHELPER_CMD), SIZEOF(helper_cmd) - 1); + repl_errno = EREPL_UPDSTART_BADPATH; + return UPDPROC_START_ERR; + } + helper_cmd[helper_trans_cmd.len] = '\0'; + if (-1 == EXECL(helper_cmd, helper_cmd, UPDHELPER_CMD_ARG1, UPDHELPER_CMD_ARG2, + (UPD_HELPER_READER == helper_type) ? UPDHELPER_READER_CMD_ARG3 : UPDHELPER_WRITER_CMD_ARG3, NULL)) + { + save_errno = errno; + helper->helper_shutdown = save_shutdown; + gtm_putmsg(VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, + LEN_AND_LIT("Could not exec Helper Process"), save_errno); + repl_errno = EREPL_UPDSTART_EXEC; + return UPDPROC_START_ERR; + } + } +#elif defined(VMS) + /* Create detached server and write startup commands to it */ + i2hex(helper - upd_helper_ctl->helper_list, mbx_suffix, SIZEOF(mbx_suffix) - 1); + mbx_suffix[SIZEOF(mbx_suffix) - 1] = '\0'; + /* A mailbox is created per helper, and the mailbox name is assigned to a logical. This logical will persist until the + * helper terminates. So, we need to assign a unique logical per helper. Hence the suffix. */ + if (SS_NORMAL != (status = repl_create_server((UPD_HELPER_READER == helper_type) ? &cmd_desc_reader : &cmd_desc_writer, + UPDHELPER_MBX_PREFIX, mbx_suffix, &cmd_channel, &helper->helper_pid_prev, + ERR_RECVPOOLSETUP))) + { + gtm_putmsg(VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, LEN_AND_LIT("Unable to spawn Helper process"), status); + helper->helper_shutdown = save_shutdown; + repl_errno = EREPL_UPDSTART_FORK; + return UPDPROC_START_ERR; + } + helper_pid = helper->helper_pid_prev; +#endif + /* Wait for helper to startup */ + while (helper_pid != helper->helper_pid && is_proc_alive(helper_pid, 0)) + { + SHORT_SLEEP(GTMRECV_WAIT_FOR_SRV_START); + UNIX_ONLY(WAITPID(helper_pid, &status, WNOHANG, waitpid_res);) /* Release defunct helper process if dead */ + } + /* The helper has now gone far enough in the initialization, or died before initialization. Consider startup completed. */ +#if defined(VMS) + /* Deassign the send-cmd mailbox channel */ + if (SS_NORMAL != (status = sys$dassgn(cmd_channel))) + { + gtm_putmsg(VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, + RTS_ERROR_LITERAL("Unable to close upd-send-cmd mbox channel"), status); + helper->helper_shutdown = save_shutdown; + repl_errno = EREPL_UPDSTART_BADPATH; /* Just to make an auto-shutdown */ + return UPDPROC_START_ERR; + } +#endif + repl_log(gtmrecv_log_fp, TRUE, TRUE, "Helper %s started. PID %d [0x%X]\n", + (UPD_HELPER_READER == helper_type) ? "reader" : "writer", helper_pid, helper_pid); + return UPDPROC_STARTED; +} + +int gtmrecv_helpers_init(int n_readers, int n_writers) +{ /* Receiver server interface to start n_readers and n_writers helper processes */ + upd_helper_ctl_ptr_t upd_helper_ctl; + upd_helper_entry_ptr_t helper, helper_top; + int reader_count, writer_count, error_count, avail_slots, status; + + assert(0 != n_readers || 0 != n_writers); + upd_helper_ctl = recvpool.upd_helper_ctl; + for (avail_slots = 0, helper = upd_helper_ctl->helper_list, helper_top = helper + MAX_UPD_HELPERS; + helper < helper_top; helper++) + { + if (0 == helper->helper_pid) + avail_slots++; + } + if (n_readers + n_writers > avail_slots) + { /* adjust reader/writer count for available slots according to the percentage specified by user */ + n_writers = (int)(((float)n_writers/(n_readers + n_writers)) * (float)avail_slots); /* may round down */ + n_readers = avail_slots - n_writers; /* preference to readers, writer count may round down */ + } + /* Start helpers, readers first */ + for (helper = upd_helper_ctl->helper_list, helper_top = helper + MAX_UPD_HELPERS, + reader_count = 0, writer_count = 0, error_count = 0; + (reader_count + writer_count + error_count) < (n_readers + n_writers); ) + { + for (; 0 != helper->helper_pid && helper < helper_top; helper++) /* find next vacant slot */ + ; + if (helper == helper_top) + GTMASSERT; + status = helper_init(helper, ((reader_count + error_count) < n_readers) ? UPD_HELPER_READER : UPD_HELPER_WRITER); + if (UPDPROC_STARTED == status) + { + if ((reader_count + error_count) < n_readers) + reader_count++; + else + writer_count++; + } else /* UPDPROC_START_ERR == status */ + { + if ((EREPL_UPDSTART_BADPATH == repl_errno) /* receiver server lost gtm_dist environment, bad situation */ + || (EREPL_UPDSTART_EXEC == repl_errno)) /* in forked child, could not exec, should exit */ + gtmrecv_exit(ABNORMAL_SHUTDOWN); + error_count++; + } + } + upd_helper_ctl->start_n_readers = reader_count; + upd_helper_ctl->start_n_writers = writer_count; + SHM_WRITE_MEMORY_BARRIER; + upd_helper_ctl->start_helpers = FALSE; + return ((0 == error_count) ? NORMAL_SHUTDOWN : ABNORMAL_SHUTDOWN); +} diff --git a/sr_port/gtmrecv_reap_helpers.c b/sr_port/gtmrecv_reap_helpers.c new file mode 100644 index 0000000..63996ec --- /dev/null +++ b/sr_port/gtmrecv_reap_helpers.c @@ -0,0 +1,68 @@ +/**************************************************************** + * * + * Copyright 2005 Fidelity Information Services, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" + +#ifdef UNIX +#include +#elif defined(VMS) +#include /* Required for gtmrecv.h */ +#endif + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "iosp.h" +#include "repl_shutdcode.h" +#include "gtmrecv.h" +#include "is_proc_alive.h" +#include "eintr_wrappers.h" +#include "repl_log.h" + +GBLREF recvpool_addrs recvpool; + +void gtmrecv_reap_helpers(boolean_t wait) +{ + upd_helper_ctl_ptr_t upd_helper_ctl; + upd_helper_entry_ptr_t helper, helper_top; + uint4 helper_pid; + int exit_status; + UNIX_ONLY(pid_t waitpid_res;) + + upd_helper_ctl = recvpool.upd_helper_ctl; + for (helper = upd_helper_ctl->helper_list, helper_top = helper + MAX_UPD_HELPERS; helper < helper_top; helper++) + { + while (0 != (helper_pid = helper->helper_pid_prev)) + { + UNIX_ONLY(WAITPID(helper_pid, &exit_status, WNOHANG, waitpid_res);) /* release defunct helper if dead */ + if (UNIX_ONLY(waitpid_res == helper_pid || ) !is_proc_alive(helper_pid, 0)) + { + helper->helper_pid = 0; /* release entry */ + if (NORMAL_SHUTDOWN == helper->helper_shutdown) + { /* zombie has been released (on Unix only). Clean-up the slot */ + helper->helper_pid_prev = 0; + helper->helper_shutdown = NO_SHUTDOWN; + } /* else helper shutdown abnormal, continue to report in checkhealth */ + break; + } + if (!wait) + break; + SHORT_SLEEP(GTMRECV_WAIT_FOR_UPD_SHUTDOWN); + } + } + upd_helper_ctl->reap_helpers = HELPER_REAP_NONE; +} diff --git a/sr_port/gtmrecv_reinit_logseqno.c b/sr_port/gtmrecv_reinit_logseqno.c new file mode 100644 index 0000000..5cfdda0 --- /dev/null +++ b/sr_port/gtmrecv_reinit_logseqno.c @@ -0,0 +1,38 @@ +/**************************************************************** + * * + * Copyright 2005 Fidelity Information Services, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_inet.h" +#ifdef VMS +#include +#endif + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gtmrecv.h" + +GBLREF recvpool_addrs recvpool; +GBLREF seq_num lastlog_seqno; +GBLREF uint4 log_interval; +GBLREF qw_num trans_recvd_cnt, last_log_tr_recvd_cnt; + +void gtmrecv_reinit_logseqno(void) +{ + lastlog_seqno = recvpool.recvpool_ctl->jnl_seqno - log_interval; + trans_recvd_cnt = -(qw_num)(log_interval - 1); + last_log_tr_recvd_cnt = 0; +} diff --git a/sr_port/gtmrecv_showbacklog.c b/sr_port/gtmrecv_showbacklog.c new file mode 100644 index 0000000..600607f --- /dev/null +++ b/sr_port/gtmrecv_showbacklog.c @@ -0,0 +1,67 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#if !defined(__MVS__) && !defined(VMS) +#include +#endif +#include +#include +#include "gtm_string.h" +#include "gtm_inet.h" +#ifdef VMS +#include /* Required for gtmrecv.h */ +#endif + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gtmrecv.h" +#include "repl_dbg.h" +#include "repl_shutdcode.h" +#include "util.h" + +GBLREF recvpool_addrs recvpool; +GBLREF gtmrecv_options_t gtmrecv_options; +GBLREF seq_num seq_num_zero; +GBLREF seq_num seq_num_one; + +int gtmrecv_showbacklog(void) +{ + seq_num seq_num, read_jnl_seqno, jnl_seqno; + + QWASSIGN(jnl_seqno, recvpool.recvpool_ctl->jnl_seqno); + if (QWEQ(jnl_seqno, seq_num_zero)) + QWASSIGN(jnl_seqno, recvpool.recvpool_ctl->old_jnl_seqno); + QWASSIGN(read_jnl_seqno, recvpool.upd_proc_local->read_jnl_seqno); + + QWSUB(seq_num, jnl_seqno, read_jnl_seqno); + util_out_print("!@UQ : number of backlog transactions received by receiver server and yet to be processed " + "by update process", TRUE, &seq_num); + + QWASSIGN(seq_num, jnl_seqno); + if (QWNE(seq_num, seq_num_zero)) + QWDECRBY(seq_num, seq_num_one); + util_out_print("!@UQ : sequence number of last transaction received from Source Server and written to receive pool", + TRUE, &seq_num); + + QWASSIGN(seq_num, read_jnl_seqno); + if (QWNE(seq_num, seq_num_zero)) + QWDECRBY(seq_num, seq_num_one); + util_out_print("!@UQ : sequence number of last transaction processed by update process", TRUE, &seq_num); + + return (NORMAL_SHUTDOWN); +} diff --git a/sr_port/gtmrecv_start_helpers.c b/sr_port/gtmrecv_start_helpers.c new file mode 100644 index 0000000..af3382a --- /dev/null +++ b/sr_port/gtmrecv_start_helpers.c @@ -0,0 +1,107 @@ +/**************************************************************** + * * + * Copyright 2005, 2009 Fidelity Information Services, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#if defined(__MVS__) && !defined(_ISOC99_SOURCE) +#define _ISOC99_SOURCE +#endif + +#include "mdef.h" + +#include "gtm_stdio.h" +#include "gtm_string.h" + +#ifdef VMS +#include /* Required for gtmrecv.h */ +#endif + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "iosp.h" +#include "repl_shutdcode.h" +#include "gtmrecv.h" +#include "gtmmsg.h" +#include "is_proc_alive.h" +#include "memcoherency.h" + +GBLREF recvpool_addrs recvpool; + +int gtmrecv_start_helpers(int n_readers, int n_writers) +{ /* Set flag in recvpool telling the receiver server to start n_readers and n_writers helper processes. + * Wait for receiver server to complete the process - completed successfully, or terminated with error + */ + upd_helper_ctl_ptr_t upd_helper_ctl; + upd_helper_entry_ptr_t helper, helper_top; + char err_str[BUFSIZ]; + int avail_slots, started_readers, started_writers; + + error_def(ERR_REPLERR); + error_def(ERR_REPLINFO); + error_def(ERR_REPLWARN); + + assert(0 != n_readers || 0 != n_writers); + upd_helper_ctl = recvpool.upd_helper_ctl; + + /* let's clean up dead helpers first so we get an accurate count of available slots */ + upd_helper_ctl->reap_helpers = HELPER_REAP_NOWAIT; + while (HELPER_REAP_NONE != upd_helper_ctl->reap_helpers && SRV_ALIVE == is_recv_srv_alive()) + SHORT_SLEEP(GTMRECV_WAIT_FOR_UPD_SHUTDOWN); + upd_helper_ctl->reap_helpers = HELPER_REAP_NONE; /* just in case recvr died */ + + /* count available slots so receiver doesn't have to */ + for (avail_slots = 0, helper = upd_helper_ctl->helper_list, helper_top = helper + MAX_UPD_HELPERS; + helper < helper_top; helper++) + { + if (0 == helper->helper_pid) + { + avail_slots++; + helper->helper_pid_prev = 0; /* force out abnormally terminated helpers as well */ + helper->helper_shutdown = NO_SHUTDOWN; /* clean state */ + } + } + if (avail_slots < n_readers + n_writers) + { + SNPRINTF(err_str, SIZEOF(err_str), + "%d helpers will exceed the maximum allowed (%d), limit the helpers to %d\n", + n_readers + n_writers, MAX_UPD_HELPERS, avail_slots); + gtm_putmsg(VARLSTCNT(4) ERR_REPLERR, 2, LEN_AND_STR(err_str)); + return ABNORMAL_SHUTDOWN; + } + + upd_helper_ctl->start_n_readers = n_readers; + upd_helper_ctl->start_n_writers = n_writers; + SHM_WRITE_MEMORY_BARRIER; + upd_helper_ctl->start_helpers = TRUE; /* hey receiver, let's go, start 'em up */ + while (upd_helper_ctl->start_helpers && SRV_ALIVE == is_recv_srv_alive()) + SHORT_SLEEP(GTMRECV_WAIT_FOR_SRV_START); + if (!upd_helper_ctl->start_helpers) + { + started_readers = upd_helper_ctl->start_n_readers; + started_writers = upd_helper_ctl->start_n_writers; + SNPRINTF(err_str, SIZEOF(err_str), "%s %d out of %d readers and %d out of %d writers started", + ((started_readers + started_writers) == (n_readers + n_writers)) ? "All" : "Only", + started_readers, n_readers, started_writers, n_writers); + if ((started_readers + started_writers) == (n_readers + n_writers)) + { + gtm_putmsg(VARLSTCNT(4) ERR_REPLINFO, 2, LEN_AND_STR(err_str)); + return NORMAL_SHUTDOWN; + } + gtm_putmsg(VARLSTCNT(4) ERR_REPLWARN, 2, LEN_AND_STR(err_str)); + return ABNORMAL_SHUTDOWN; + } + gtm_putmsg(VARLSTCNT(4) ERR_REPLERR, 2, + LEN_AND_LIT("Receiver server is not alive to start helpers. Start receiver server first")); + return ABNORMAL_SHUTDOWN; +} diff --git a/sr_port/gtmrecv_statslog.c b/sr_port/gtmrecv_statslog.c new file mode 100644 index 0000000..92a5039 --- /dev/null +++ b/sr_port/gtmrecv_statslog.c @@ -0,0 +1,98 @@ +/**************************************************************** + * * + * Copyright 2001, 2002 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#if !defined(__MVS__) && !defined(VMS) +#include +#endif +#include +#include +#include "gtm_string.h" +#include +#ifdef UNIX +#include +#endif +#ifdef VMS +#include /* Required for gtmrecv.h */ +#endif + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gtmrecv.h" +#include "repl_dbg.h" +#include "repl_shutdcode.h" +#include "repl_sem.h" +#include "util.h" + +GBLREF recvpool_addrs recvpool; +GBLREF gtmrecv_options_t gtmrecv_options; + +int gtmrecv_statslog(void) +{ + +#ifdef VMS + error_def(ERR_UNIMPLOP); + error_def(ERR_TEXT); + + rts_error(VARLSTCNT(6) ERR_UNIMPLOP, 0, ERR_TEXT, 2, LEN_AND_LIT("Statistics logging not supported on VMS")); +#endif + /* Grab the recvpool option write lock */ + if (0 > grab_sem(RECV, RECV_SERV_OPTIONS_SEM)) + { + util_out_print("Error grabbing recvpool option write lock. Could not initiate stats log", TRUE); + return (ABNORMAL_SHUTDOWN); + } + + if (gtmrecv_options.statslog == recvpool.gtmrecv_local->statslog) + { + util_out_print("STATSLOG is already !AD. Not initiating change in stats log", TRUE, gtmrecv_options.statslog ? + strlen("ON") : strlen("OFF"), gtmrecv_options.statslog ? "ON" : "OFF"); + rel_sem_immediate(RECV, RECV_SERV_OPTIONS_SEM); + return (ABNORMAL_SHUTDOWN); + } + + if (!gtmrecv_options.statslog) + { + recvpool.gtmrecv_local->statslog = FALSE; + recvpool.gtmrecv_local->statslog_file[0] = '\0'; + util_out_print("STATSLOG turned OFF", TRUE); + rel_sem_immediate(RECV, RECV_SERV_OPTIONS_SEM); + return (NORMAL_SHUTDOWN); + } + + if ('\0' == gtmrecv_options.log_file[0]) /* Stats log file not specified, use general log file */ + { + util_out_print("No file specified for stats log. Using general log file !AD\n", TRUE, + LEN_AND_STR(recvpool.gtmrecv_local->log_file)); + strcpy(gtmrecv_options.log_file, recvpool.gtmrecv_local->log_file); + } else if (0 == strcmp(recvpool.gtmrecv_local->log_file, gtmrecv_options.log_file)) + { + util_out_print("Stats log file is already !AD. Not initiating change in log file", TRUE, + LEN_AND_STR(gtmrecv_options.log_file)); + rel_sem_immediate(RECV, RECV_SERV_OPTIONS_SEM); + return (ABNORMAL_SHUTDOWN); + } + + strcpy(recvpool.gtmrecv_local->statslog_file, gtmrecv_options.log_file); + recvpool.gtmrecv_local->statslog = TRUE; + + util_out_print("Stats log turned on with file !AD", TRUE, strlen(recvpool.gtmrecv_local->statslog_file), + recvpool.gtmrecv_local->statslog_file); + + rel_sem_immediate(RECV, RECV_SERV_OPTIONS_SEM); + return (NORMAL_SHUTDOWN); +} diff --git a/sr_port/gtmrecv_upd_proc_init.c b/sr_port/gtmrecv_upd_proc_init.c new file mode 100644 index 0000000..14f05c0 --- /dev/null +++ b/sr_port/gtmrecv_upd_proc_init.c @@ -0,0 +1,227 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc.* + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_time.h" +#include "gtm_fcntl.h" +#include "gtm_unistd.h" +#include "gtm_inet.h" +#ifdef UNIX +#include +#elif defined(VMS) +#include +#endif + +#include + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gtmrecv.h" +#include "repl_dbg.h" +#include "repl_errno.h" +#include "iosp.h" +#include "gtm_stdio.h" +#include "repl_shutdcode.h" +#include "repl_sem.h" +#include "io.h" +#include "is_proc_alive.h" +#include "gtmmsg.h" +#include "trans_log_name.h" +#include "repl_log.h" +#include "eintr_wrappers.h" + +#define UPDPROC_CMD_MAXLEN 1024 +#define UPDPROC_CMD "$gtm_dist/mupip" +#define UPDPROC_CMD_FILE "mupip" +#define UPDPROC_CMD_ARG1 "replicate" +#define UPDPROC_CMD_ARG2 "-updateproc" +#define UPDPROC_CMD_STR "REPLICATE/UPDATEPROC" + +GBLREF recvpool_addrs recvpool; +GBLREF int recvpool_shmid; + +GBLREF int gtmrecv_log_fd; +GBLREF FILE *gtmrecv_log_fp; +GBLREF int updproc_log_fd; + +error_def(ERR_LOGTOOLONG); +error_def(ERR_RECVPOOLSETUP); +error_def(ERR_REPLINFO); +error_def(ERR_TEXT); + +int gtmrecv_upd_proc_init(boolean_t fresh_start) +{ + /* Update Process initialization */ + + mstr upd_proc_log_cmd, upd_proc_trans_cmd; + char upd_proc_cmd[UPDPROC_CMD_MAXLEN]; + int status; + int upd_status, save_upd_status; +#ifdef UNIX + pid_t upd_pid, waitpid_res; +#elif defined(VMS) + uint4 upd_pid; + uint4 cmd_channel; + $DESCRIPTOR(cmd_desc, UPDPROC_CMD_STR); +#endif + + /* Check if the update process is alive */ + + if ((upd_status = is_updproc_alive()) == SRV_ERR) + { + gtm_putmsg(VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, + RTS_ERROR_LITERAL("Receive pool semctl failure"), REPL_SEM_ERRNO); + repl_errno = EREPL_UPDSTART_SEMCTL; + return(UPDPROC_START_ERR); + } else if (upd_status == SRV_ALIVE && !fresh_start) + { + gtm_putmsg(VARLSTCNT(4) ERR_TEXT, 2, RTS_ERROR_LITERAL("Update process already exists. Not starting it")); + return(UPDPROC_EXISTS); + } else if (upd_status == SRV_ALIVE) + { + gtm_putmsg(VARLSTCNT(6) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, + RTS_ERROR_LITERAL("Update process already exists. Please kill it before a fresh start")); + return(UPDPROC_EXISTS); + } + + save_upd_status = recvpool.upd_proc_local->upd_proc_shutdown; + recvpool.upd_proc_local->upd_proc_shutdown = NO_SHUTDOWN; + +#ifdef UNIX + if (0 > (upd_pid = fork())) + { + recvpool.upd_proc_local->upd_proc_shutdown = save_upd_status; + gtm_putmsg(VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, + RTS_ERROR_LITERAL("Could not fork update process"), errno); + repl_errno = EREPL_UPDSTART_FORK; + return(UPDPROC_START_ERR); + } + if (0 == upd_pid) + { + /* Update Process */ + upd_proc_log_cmd.len = SIZEOF(UPDPROC_CMD) - 1; + upd_proc_log_cmd.addr = UPDPROC_CMD; + status = TRANS_LOG_NAME(&upd_proc_log_cmd, &upd_proc_trans_cmd, upd_proc_cmd, SIZEOF(upd_proc_cmd), + dont_sendmsg_on_log2long); + if (status != SS_NORMAL) + { + gtm_putmsg(VARLSTCNT(6) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, + RTS_ERROR_LITERAL("Could not find path of Update Process. Check value of $gtm_dist")); + if (SS_LOG2LONG == status) + gtm_putmsg(VARLSTCNT(5) ERR_LOGTOOLONG, 3, LEN_AND_LIT(UPDPROC_CMD), SIZEOF(upd_proc_cmd) - 1); + repl_errno = EREPL_UPDSTART_BADPATH; + return(UPDPROC_START_ERR); + } + upd_proc_cmd[upd_proc_trans_cmd.len] = '\0'; + if (EXECL(upd_proc_cmd, upd_proc_cmd, UPDPROC_CMD_ARG1, UPDPROC_CMD_ARG2, NULL) < 0) + { + gtm_putmsg(VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, + RTS_ERROR_LITERAL("Could not exec Update Process"), errno); + repl_errno = EREPL_UPDSTART_EXEC; + return(UPDPROC_START_ERR); + } + } +#elif defined(VMS) + /* Create detached server and write startup commands to it */ + status = repl_create_server(&cmd_desc, "GTMU", "", &cmd_channel, &upd_pid, ERR_RECVPOOLSETUP); + if (SS_NORMAL != status) + { + gtm_putmsg(VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, + RTS_ERROR_LITERAL("Unable to spawn Update process"), status); + recvpool.upd_proc_local->upd_proc_shutdown = save_upd_status; + repl_errno = EREPL_UPDSTART_FORK; + return(UPDPROC_START_ERR); + } +#endif + if (recvpool.upd_proc_local->upd_proc_pid) + recvpool.upd_proc_local->upd_proc_pid_prev = recvpool.upd_proc_local->upd_proc_pid; + else + recvpool.upd_proc_local->upd_proc_pid_prev = upd_pid; + recvpool.upd_proc_local->upd_proc_pid = upd_pid; + /* Receiver Server; wait for the update process to startup */ + REPL_DPRINT2("Waiting for update process %d to startup\n", upd_pid); + while (get_sem_info(RECV, UPD_PROC_COUNT_SEM, SEM_INFO_VAL) == 0 && is_proc_alive(upd_pid, 0)) + { + /* To take care of reassignment of PIDs, the while condition should be && with the + * condition (PPID of pid == process_id) + */ + REPL_DPRINT2("Waiting for update process %d to startup\n", upd_pid); + UNIX_ONLY(WAITPID(upd_pid, &status, WNOHANG, waitpid_res);) /* Release defunct update process if dead */ + SHORT_SLEEP(GTMRECV_WAIT_FOR_SRV_START); + } +#ifdef VMS + /* Deassign the send-cmd mailbox channel */ + if (SS_NORMAL != (status = sys$dassgn(cmd_channel))) + { + gtm_putmsg(VARLSTCNT(7) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, + RTS_ERROR_LITERAL("Unable to close upd-send-cmd mbox channel"), status); + recvpool.upd_proc_local->upd_proc_shutdown = save_upd_status; + repl_errno = EREPL_UPDSTART_BADPATH; /* Just to make an auto-shutdown */ + return(UPDPROC_START_ERR); + } +#endif + repl_log(gtmrecv_log_fp, TRUE, FALSE, "Update Process started. PID %d [0x%X]\n", upd_pid, upd_pid); + return(UPDPROC_STARTED); +} + +int gtmrecv_start_updonly(void) +{ + int start_status, recvr_status, upd_status; + + if ((upd_status = is_updproc_alive()) == SRV_ALIVE) + { + gtm_putmsg(VARLSTCNT(6) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, + RTS_ERROR_LITERAL("Update Process exists already. New process not started")); + return(UPDPROC_START_ERR); + } else if (upd_status == SRV_ERR) + { + gtm_putmsg(VARLSTCNT(6) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, + RTS_ERROR_LITERAL("Error in starting up update process")); + return(UPDPROC_START_ERR); + } + + assert(upd_status == SRV_DEAD); +#ifdef VMS + recvpool.upd_proc_local->changelog |= REPLIC_CHANGE_LOGFILE; +#endif + recvpool.upd_proc_local->start_upd = UPDPROC_START; + while ((start_status = recvpool.upd_proc_local->start_upd) == UPDPROC_START && + (recvr_status = is_recv_srv_alive()) == SRV_ALIVE) + SHORT_SLEEP(GTMRECV_WAIT_FOR_SRV_START); + + if (start_status == UPDPROC_STARTED) + { + gtm_putmsg(VARLSTCNT(4) ERR_REPLINFO, 2, RTS_ERROR_LITERAL("Update Process started successfully")); + return(UPDPROC_STARTED); + } +#ifdef VMS + recvpool.upd_proc_local->changelog &= ~REPLIC_CHANGE_LOGFILE; +#endif + if (start_status == UPDPROC_START) + { + gtm_putmsg(VARLSTCNT(6) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, + RTS_ERROR_LITERAL("Receiver server is not alive to start update process. Please start receiver server")); + } else if (start_status == UPDPROC_START_ERR) + { + gtm_putmsg(VARLSTCNT(6) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, RTS_ERROR_LITERAL("Error starting update process")); + } else if (start_status == UPDPROC_EXISTS) + { + gtm_putmsg(VARLSTCNT(6) ERR_RECVPOOLSETUP, 0, ERR_TEXT, 2, + RTS_ERROR_LITERAL("Update Process exists already. New process not started")); + } + return(UPDPROC_START_ERR); +} diff --git a/sr_port/gtmsource_ch.c b/sr_port/gtmsource_ch.c new file mode 100644 index 0000000..b7281c5 --- /dev/null +++ b/sr_port/gtmsource_ch.c @@ -0,0 +1,105 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include + +#include "gtm_unistd.h" +#include "gtm_fcntl.h" +#include "gtm_inet.h" + +#ifdef UNIX +#include "gtm_ipc.h" +#include +#elif defined(VMS) +#include +#include /* Required for gtmrecv.h */ +#endif + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "iosp.h" +#include "repl_shutdcode.h" +#include "repl_msg.h" +#include "gtmsource.h" +#include "error.h" +#include "dpgbldir.h" + +#ifdef UNIX +#include "ftok_sems.h" +#endif + +GBLREF gd_addr *gd_header; +GBLREF jnlpool_addrs jnlpool; + +CONDITION_HANDLER(gtmsource_ch) +{ + gd_addr *addr_ptr; + gd_region *reg_local, *reg_top; + sgmnt_addrs *csa; + +#ifdef UNIX + unix_db_info *udi; +#endif + + error_def(ERR_ASSERT); + error_def(ERR_CTRLC); + error_def(ERR_FORCEDHALT); + error_def(ERR_GTMCHECK); + error_def(ERR_GTMASSERT); + error_def(ERR_MEMORY); + error_def(ERR_VMSMEMORY); + error_def(ERR_STACKOFLOW); + error_def(ERR_OUTOFSPACE); + + START_CH; + if (!(IS_GTM_ERROR(SIGNAL)) || DUMPABLE || SEVERITY == ERROR) + { + for (addr_ptr = get_next_gdr(NULL); addr_ptr; addr_ptr = get_next_gdr(addr_ptr)) + { + for (reg_local = addr_ptr->regions, reg_top = reg_local + addr_ptr->n_regions; + reg_local < reg_top; reg_local++) + { + if (reg_local->open && !reg_local->was_open) + { + csa = (sgmnt_addrs *)&FILE_INFO(reg_local)->s_addrs; + if (csa && (csa->now_crit)) + rel_crit(reg_local); + } + } + } + if (jnlpool.jnlpool_ctl) + { + csa = (sgmnt_addrs *)&FILE_INFO(jnlpool.jnlpool_dummy_reg)->s_addrs; + if (csa && csa->now_crit) + rel_lock(jnlpool.jnlpool_dummy_reg); + } + UNIX_ONLY( + /* Release FTOK lock on the replication instance file if holding it */ + if (NULL != jnlpool.jnlpool_dummy_reg) + { + udi = FILE_INFO(jnlpool.jnlpool_dummy_reg); + if (udi->grabbed_ftok_sem) + ftok_sem_release(jnlpool.jnlpool_dummy_reg, FALSE, FALSE); + assert(!udi->grabbed_ftok_sem); + } + ) + NEXTCH; + } + /* warning, info, or success */ + CONTINUE; +} diff --git a/sr_port/gtmsource_comm_init.c b/sr_port/gtmsource_comm_init.c new file mode 100644 index 0000000..2fd311f --- /dev/null +++ b/sr_port/gtmsource_comm_init.c @@ -0,0 +1,91 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#if defined(__MVS__) && !defined(_ISOC99_SOURCE) +#define _ISOC99_SOURCE +#endif + +#include "mdef.h" + +#include +#include + +#include "gtm_stdio.h" +#include "gtm_string.h" +#include "gtm_socket.h" +#include "gtm_inet.h" +#include "gtm_fcntl.h" +#include "gtm_unistd.h" +#ifdef VMS +#include /* Required for gtmsource.h */ +#endif + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "repl_msg.h" +#include "gtmsource.h" +#include "repl_sp.h" +#include "repl_comm.h" + +GBLDEF int gtmsource_sock_fd = FD_INVALID; +GBLREF jnlpool_addrs jnlpool; + +int gtmsource_comm_init(void) +{ + /* Initialize communication stuff */ + + const int disable_keepalive = 0; + struct linger disable_linger = {0, 0}; + char error_string[1024]; + int err_status; + + error_def(ERR_REPLCOMM); + error_def(ERR_TEXT); + + if (FD_INVALID != gtmsource_sock_fd) /* Initialization done already */ + return(0); + + /* Create the socket used for communicating with secondary */ + if (FD_INVALID == (gtmsource_sock_fd = socket(AF_INET, SOCK_STREAM, 0))) + { + err_status = ERRNO; + SNPRINTF(error_string, SIZEOF(error_string), "Error with source server socket create : %s", STRERROR(err_status)); + rts_error(VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_STRING(error_string)); + return(-1); + } + + /* A connection breakage should get rid of the socket */ + + if (-1 == setsockopt(gtmsource_sock_fd, SOL_SOCKET, SO_LINGER, (const void *)&disable_linger, SIZEOF(disable_linger))) + { + err_status = ERRNO; + SNPRINTF(error_string, SIZEOF(error_string), "Error with source server socket disable linger : %s", + STRERROR(err_status)); + rts_error(VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_STRING(error_string)); + } + +#ifdef REPL_DISABLE_KEEPALIVE + if (-1 == setsockopt(gtmsource_sock_fd, SOL_SOCKET, SO_KEEPALIVE, (const void *)&disable_keepalive, + SIZEOF(disable_keepalive))) + { /* Till SIGPIPE is handled properly */ + err_status = ERRNO; + SNPRINTF(error_string, SIZEOF(error_string), "Error with source server socket disable keepalive : %s", + STRERROR(err_status)); + rts_error(VARLSTCNT(6) ERR_REPLCOMM, 0, ERR_TEXT, 2, RTS_ERROR_STRING(error_string)); + } +#endif + return(0); +} diff --git a/sr_port/gtmsource_ctl_init.c b/sr_port/gtmsource_ctl_init.c new file mode 100644 index 0000000..f8e93af --- /dev/null +++ b/sr_port/gtmsource_ctl_init.c @@ -0,0 +1,461 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#ifdef UNIX +#include "gtm_stat.h" +#elif defined(VMS) +#include +#include +#include +#include +#include +#include /* Required for gtmsource.h */ +#include +#else +#error Unsupported platform +#endif +#include +#include "gtm_fcntl.h" +#include "gtm_unistd.h" +#include "gtm_inet.h" +#include "gtm_stdio.h" + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "jnl.h" +#include "repl_msg.h" +#include "gtmsource.h" +#include "repl_ctl.h" +#include "repl_errno.h" +#include "repl_dbg.h" +#ifdef UNIX +#include "gtmio.h" +#endif +#include "iosp.h" +#include "eintr_wrappers.h" +#include "repl_sp.h" +#include "tp_change_reg.h" +#include "is_file_identical.h" +#include "get_fs_block_size.h" +#ifdef GTM_CRYPT +#include "gtmcrypt.h" +#endif +#ifdef __MVS__ +#include "gtm_zos_io.h" +#endif + +GBLDEF repl_ctl_element *repl_ctl_list = NULL; + +GBLREF jnlpool_addrs jnlpool; +GBLREF seq_num seq_num_zero; + +GBLREF gd_addr *gd_header; +GBLREF gd_region *gv_cur_region; +GBLREF sgmnt_addrs *cs_addrs; + +GBLREF int gtmsource_log_fd; +GBLREF FILE *gtmsource_log_fp; +GBLREF int gtmsource_statslog_fd; +GBLREF FILE *gtmsource_statslog_fp; + +repl_buff_t *repl_buff_create(uint4 buffsize, uint4 jnl_fs_block_size); + +repl_buff_t *repl_buff_create(uint4 buffsize, uint4 jnl_fs_block_size) +{ + repl_buff_t *tmp_rb; + int index; + unsigned char *buff_ptr; + + tmp_rb = (repl_buff_t *)malloc(SIZEOF(repl_buff_t)); + tmp_rb->buffindex = REPL_MAINBUFF; + for (index = REPL_MAINBUFF; REPL_NUMBUFF > index; index++) + { + tmp_rb->buff[index].reclen = 0; + tmp_rb->buff[index].recaddr = JNL_FILE_FIRST_RECORD; + tmp_rb->buff[index].readaddr = JNL_FILE_FIRST_RECORD; + tmp_rb->buff[index].buffremaining = buffsize; + buff_ptr = (unsigned char *)malloc(buffsize + jnl_fs_block_size); + tmp_rb->buff[index].base_buff = buff_ptr; + tmp_rb->buff[index].base = (unsigned char *)ROUND_UP2((uintszofptr_t)buff_ptr, jnl_fs_block_size); + tmp_rb->buff[index].recbuff = tmp_rb->buff[index].base; + } + tmp_rb->fc = (repl_file_control_t *)malloc(SIZEOF(repl_file_control_t)); + return (tmp_rb); +} + +/* Given a journal file name, this function opens that file explicitly without going through "jnl_ensure_open" */ +int repl_open_jnl_file_by_name(repl_ctl_element *tmp_ctl, int jnl_fn_len, char *jnl_fn, int *fd_ptr, void *stat_buf_ptr) +{ + int tmp_fd; + int status; +#ifdef UNIX + struct stat stat_buf; +#elif defined(VMS) + struct FAB fab; + struct NAM stat_buf; +#else +#error Unsupported platform +#endif + + tmp_ctl->jnl_fn_len = jnl_fn_len; + memcpy(tmp_ctl->jnl_fn, jnl_fn, jnl_fn_len); + tmp_ctl->jnl_fn[jnl_fn_len] = '\0'; + status = SS_NORMAL; + + /* Open Journal File */ +# ifdef UNIX + OPENFILE(tmp_ctl->jnl_fn, O_RDONLY, tmp_fd); + if (0 > tmp_fd) + { + status = errno; + assert(FALSE); + } + if (SS_NORMAL == status) + { + FSTAT_FILE(tmp_fd, &stat_buf, status); + if (0 > status) + { + status = errno; + assert(FALSE); + } +# ifdef __MVS + else if (-1 == gtm_zos_tag_to_policy(tmp_fd, TAG_BINARY)) + { + status = errno; + assert(FALSE); + } +# endif + } + *((struct stat *)stat_buf_ptr) = stat_buf; +# elif defined(VMS) + fab = cc$rms_fab; + fab.fab$l_fna = tmp_ctl->jnl_fn; + fab.fab$b_fns = tmp_ctl->jnl_fn_len; + fab.fab$l_fop = FAB$M_UFO; + fab.fab$b_fac = FAB$M_GET | FAB$M_PUT | FAB$M_BIO; + fab.fab$b_shr = FAB$M_SHRPUT | FAB$M_SHRGET | FAB$M_UPI; + stat_buf = cc$rms_nam; + fab.fab$l_nam = &stat_buf; + fab.fab$l_dna = JNL_EXT_DEF; + fab.fab$b_dns = SIZEOF(JNL_EXT_DEF) - 1; + status = sys$open(&fab); + if (RMS$_NORMAL == status) + { + status = SS_NORMAL; + tmp_fd = fab.fab$l_stv; + } + assert(SS_NORMAL == status); + *((struct NAM *)stat_buf_ptr) = stat_buf; +# endif + REPL_DPRINT2("CTL INIT : Direct open of file %s\n", tmp_ctl->jnl_fn); + *fd_ptr = tmp_fd; + return status; +} + +int repl_ctl_create(repl_ctl_element **ctl, gd_region *reg, int jnl_fn_len, char *jnl_fn, boolean_t init) +{ + gd_region *r_save; + sgmnt_addrs *csa; + sgmnt_data_ptr_t csd; + repl_ctl_element *tmp_ctl = NULL; + jnl_file_header *tmp_jfh = NULL; + jnl_file_header *tmp_jfh_base = NULL; + jnl_private_control *jpc; + int tmp_fd = NOJNL; + int status; + GTMCRYPT_ONLY( + int crypt_status; + ) + uint4 jnl_status; + boolean_t did_jnl_ensure_open = FALSE, was_crit; + int4 lcl_jnl_fn_len; + char lcl_jnl_fn[JNL_NAME_SIZE]; +#ifdef UNIX + struct stat stat_buf; +#elif defined(VMS) + struct NAM stat_buf; + short iosb[4]; /* needed by the F_READ_BLK_ALIGNED macro */ +#else +#error Unsupported platform +#endif + uint4 jnl_fs_block_size; + + error_def(ERR_JNLFILOPN); + + status = SS_NORMAL; + jnl_status = 0; + + tmp_ctl = (repl_ctl_element *)malloc(SIZEOF(repl_ctl_element)); + tmp_ctl->reg = reg; + csa = &FILE_INFO(reg)->s_addrs; + jpc = csa->jnl; + if (init) + { + assert((0 == jnl_fn_len) && ((NULL == jnl_fn) || ('\0' == jnl_fn[0]))); + r_save = gv_cur_region; + gv_cur_region = reg; + tp_change_reg(); + assert(csa == cs_addrs); + jpc->channel = NOJNL; /* Not to close the prev gener file */ + was_crit = csa->now_crit; + if (!was_crit) + grab_crit(reg); + /* Although replication may be WAS_ON, it is possible that source server has not yet sent records + * that were generated when replication was ON. We have to open and read this journal file to + * cover such a case. But in the WAS_ON case, do not ask for a jnl_ensure_open to be done since + * it will return an error (it will try to open the latest generation journal file and that will + * fail because of a lot of reasons e.g. "jpc->cycle" vs "jpc->jnl_buff->cycle" mismatch or db/jnl + * tn mismatch JNLTRANSLSS error etc.). This will even cause a journal file switch which the source + * server should never do (it is only supposed to READ from journal files). Open the journal file + * stored in the database file header (which will be non-NULL even though journaling is currently OFF) + * thereby can send as many seqnos as possible until the repl=WAS_ON/jnl=OFF state was reached. + */ + csd = csa->hdr; + assert(REPL_ALLOWED(csd)); + if (!REPL_WAS_ENABLED(csd)) + { + /* replication is allowed and has not gone into the WAS_ON state so journaling is expected to be ON*/ + assert(JNL_ENABLED(csd)); + did_jnl_ensure_open = TRUE; + jnl_status = jnl_ensure_open(); + if (0 != jnl_status) + { + if (!was_crit) + rel_crit(reg); + if (SS_NORMAL != jpc->status) + rts_error(VARLSTCNT(7) jnl_status, 4, JNL_LEN_STR(csd), DB_LEN_STR(reg), jpc->status); + else + rts_error(VARLSTCNT(6) jnl_status, 4, JNL_LEN_STR(csd), DB_LEN_STR(reg)); + } else + { + tmp_ctl->jnl_fn_len = csd->jnl_file_len; + memcpy(tmp_ctl->jnl_fn, csd->jnl_file_name, tmp_ctl->jnl_fn_len); + tmp_ctl->jnl_fn[tmp_ctl->jnl_fn_len] = '\0'; + } + /* stash the shared fileid into private storage before rel_crit as it is used in JNL_GDID_PVT macro below */ + VMS_ONLY (jpc->fileid = csa->nl->jnl_file.jnl_file_id;) + UNIX_ONLY(jpc->fileid = csa->nl->jnl_file.u;) + REPL_DPRINT2("CTL INIT : Open of file %s thru jnl_ensure_open\n", tmp_ctl->jnl_fn); + tmp_fd = jpc->channel; + } else + { /* Note that we hold crit so it is safe to pass csd->jnl_file_name (no one else will be changing it) */ + status = repl_open_jnl_file_by_name(tmp_ctl, csd->jnl_file_len, (char *)csd->jnl_file_name, + &tmp_fd, &stat_buf); + } + if (!was_crit) + rel_crit(reg); + gv_cur_region = r_save; + tp_change_reg(); + assert(NOJNL != tmp_fd); + } else + status = repl_open_jnl_file_by_name(tmp_ctl, jnl_fn_len, jnl_fn, &tmp_fd, &stat_buf); + if (status == SS_NORMAL) + { + jnl_fs_block_size = get_fs_block_size(tmp_fd); + /* Because the read below will be to the aligned buffer, and it will read an aligned size, we need + * to allocate jnl_file_header + 2 * jnl_fs_block_size. There will be some throw-away before the + * buffer to get the alignment in place, and then up to jnl_fs_block_size after the header in order + * for the size to be a multiple of jnl_fs_block_size. + */ + tmp_jfh_base = (jnl_file_header *)malloc(SIZEOF(jnl_file_header) + (2 * jnl_fs_block_size)); + tmp_jfh = (jnl_file_header *)(ROUND_UP2((uintszofptr_t)tmp_jfh_base, jnl_fs_block_size)); + F_READ_BLK_ALIGNED(tmp_fd, 0, tmp_jfh, ROUND_UP2(REAL_JNL_HDR_LEN, jnl_fs_block_size), status); + assert(SS_NORMAL == status); + if (SS_NORMAL == status) + CHECK_JNL_FILE_IS_USABLE(tmp_jfh, status, FALSE, 0, NULL); /* FALSE => NO gtm_putmsg even if errors */ + assert(SS_NORMAL == status); + } + if (SS_NORMAL != status) + { + /* We need tmp_ctl->jnl_fn to issue the error but want to free up tmp_ctl before the error. + * So copy jnl_fn into local buffer before the error. + */ + lcl_jnl_fn_len = tmp_ctl->jnl_fn_len; + assert(ARRAYSIZE(lcl_jnl_fn) >= ARRAYSIZE(tmp_ctl->jnl_fn)); + assert(lcl_jnl_fn_len < ARRAYSIZE(lcl_jnl_fn)); + memcpy(lcl_jnl_fn, tmp_ctl->jnl_fn, lcl_jnl_fn_len); + lcl_jnl_fn[lcl_jnl_fn_len] = '\0'; + assert(FALSE); + free(tmp_ctl); + tmp_ctl = NULL; + if (NULL != tmp_jfh_base) + free(tmp_jfh_base); + tmp_jfh = NULL; + tmp_jfh_base = NULL; + rts_error(VARLSTCNT(7) ERR_JNLFILOPN, 4, lcl_jnl_fn_len, lcl_jnl_fn, DB_LEN_STR(reg), status); + } + assert(SS_NORMAL == status); /* so jnl_fs_block_size is guaranteed to have been initialized */ + tmp_ctl->repl_buff = repl_buff_create(tmp_jfh->alignsize, jnl_fs_block_size); + tmp_ctl->repl_buff->backctl = tmp_ctl; + tmp_ctl->repl_buff->fc->eof_addr = JNL_FILE_FIRST_RECORD; + tmp_ctl->repl_buff->fc->fs_block_size = jnl_fs_block_size; + tmp_ctl->repl_buff->fc->jfh_base = tmp_jfh_base; + tmp_ctl->repl_buff->fc->jfh = tmp_jfh; + tmp_ctl->repl_buff->fc->fd = tmp_fd; +# ifdef GTM_CRYPT + if (tmp_jfh->is_encrypted) + { + INIT_PROC_ENCRYPTION(crypt_status); + /* If the encryption init failed in db_init, the below MACRO should return an error. + * Depending on the error returned, report the error.*/ + GTMCRYPT_GETKEY(tmp_jfh->encryption_hash, tmp_ctl->encr_key_handle, crypt_status); + if (0 != crypt_status) + GC_RTS_ERROR(crypt_status, tmp_ctl->jnl_fn); + } +# endif + if (did_jnl_ensure_open) + { + F_COPY_GDID(tmp_ctl->repl_buff->fc->id, JNL_GDID_PVT(csa)); + /* reset jpc->channel (would have been updated by jnl_ensure_open) as that corresponds to an + * actively updated journal file and is only for GT.M and never for source server which only + * READS from journal files. Source server anyways has a copy of the fd in tmp_ctl->repl_buff->fc->fd. + */ + jpc->channel = NOJNL; + } else + { + F_COPY_GDID_FROM_STAT(tmp_ctl->repl_buff->fc->id, stat_buf); /* For VMS stat_buf is a NAM structure */ + } + + QWASSIGN(tmp_ctl->min_seqno, seq_num_zero); + QWASSIGN(tmp_ctl->max_seqno, seq_num_zero); + QWASSIGN(tmp_ctl->seqno, seq_num_zero); + tmp_ctl->tn = 1; + tmp_ctl->file_state = JNL_FILE_UNREAD; + tmp_ctl->lookback = FALSE; + tmp_ctl->first_read_done = FALSE; + tmp_ctl->eof_addr_final = FALSE; + tmp_ctl->max_seqno_final = FALSE; + tmp_ctl->read_complete = FALSE; + tmp_ctl->min_seqno_dskaddr = 0; + tmp_ctl->max_seqno_dskaddr = 0; + tmp_ctl->next = tmp_ctl->prev = NULL; + *ctl = tmp_ctl; + + return (SS_NORMAL); +} + +int gtmsource_ctl_init(void) +{ + /* Setup ctl for reading from journal files */ + + gd_region *region_top, *reg; + sgmnt_addrs *csa; + sgmnt_data_ptr_t csd; + repl_ctl_element *tmp_ctl, *prev_ctl; + int jnl_file_len, status; + + repl_ctl_list = (repl_ctl_element *)malloc(SIZEOF(repl_ctl_element)); + memset((char_ptr_t)repl_ctl_list, 0, SIZEOF(*repl_ctl_list)); + prev_ctl = repl_ctl_list; + + region_top = gd_header->regions + gd_header->n_regions; + for (reg = gd_header->regions; reg < region_top; reg++) + { + assert(reg->open); + csa = &FILE_INFO(reg)->s_addrs; + csd = csa->hdr; + if (REPL_ALLOWED(csd)) + { + status = repl_ctl_create(&tmp_ctl, reg, 0, NULL, TRUE); + assert(SS_NORMAL == status); + prev_ctl->next = tmp_ctl; + tmp_ctl->prev = prev_ctl; + tmp_ctl->next = NULL; + prev_ctl = tmp_ctl; + } + } + if (NULL == repl_ctl_list->next) /* No replicated region */ + GTMASSERT; + return (SS_NORMAL); +} + +int repl_ctl_close(repl_ctl_element *ctl) +{ + int index; + int status; + + if (NULL != ctl) + { + if (NULL != ctl->repl_buff) + { + for (index = REPL_MAINBUFF; REPL_NUMBUFF > index; index++) + if (NULL != ctl->repl_buff->buff[index].base_buff) + free(ctl->repl_buff->buff[index].base_buff); + if (NULL != ctl->repl_buff->fc) + { + if (NULL != ctl->repl_buff->fc->jfh_base) + free(ctl->repl_buff->fc->jfh_base); + if (NOJNL != ctl->repl_buff->fc->fd) + F_CLOSE(ctl->repl_buff->fc->fd, status); /* resets "ctl->repl_buff->fc->fd" to FD_INVALID */ + free(ctl->repl_buff->fc); + } + free(ctl->repl_buff); + } + free(ctl); + } + return (SS_NORMAL); +} + +int gtmsource_ctl_close(void) +{ + repl_ctl_element *ctl; + sgmnt_addrs *csa; + int status; + + if (repl_ctl_list) + { + for (ctl = repl_ctl_list->next; NULL != ctl; ctl = repl_ctl_list->next) + { + repl_ctl_list->next = ctl->next; /* next element becomes head thereby removing this element, + the current head from the list; if there is an error path that returns + us to this function before all elements were freed, we won't try to + free elements that have been freed already */ + DEBUG_ONLY( + csa = &FILE_INFO(ctl->reg)->s_addrs; + /* jpc->channel should never be set to a valid value outside of repl_ctl_create */ + assert((NULL != csa->jnl) && (NOJNL == csa->jnl->channel)); + ) + repl_ctl_close(ctl); + } + ctl = repl_ctl_list; + repl_ctl_list = NULL; + free(ctl); + } + return (SS_NORMAL); +} + +int gtmsource_set_lookback(void) +{ + /* Scan all the region ctl's and set lookback to TRUE if ctl has to be + * repositioned for a transaction read from the past */ + + repl_ctl_element *ctl; + + for (ctl = repl_ctl_list->next; NULL != ctl; ctl = ctl->next) + { + if ((JNL_FILE_OPEN == ctl->file_state || + JNL_FILE_CLOSED == ctl->file_state) && + QWLE(jnlpool.gtmsource_local->read_jnl_seqno, ctl->seqno)) + ctl->lookback = TRUE; + else + ctl->lookback = FALSE; + } + return (SS_NORMAL); +} diff --git a/sr_port/gtmsource_exit.c b/sr_port/gtmsource_exit.c new file mode 100644 index 0000000..18857da --- /dev/null +++ b/sr_port/gtmsource_exit.c @@ -0,0 +1,42 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" /* for FILE * in repl_comm.h */ +#include "gtm_stdlib.h" /* for exit() */ + +#ifdef VMS +#include +#include +#endif + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "jnl.h" +#include "repl_msg.h" +#include "gtmsource.h" +#include "repl_comm.h" + +void gtmsource_exit(int exit_status) +{ + error_def(ERR_REPLEXITERR); +#ifdef VMS + sys$exit((0 == exit_status) ? SS$_NORMAL : ERR_REPLEXITERR); +#else + exit(exit_status); +#endif +} diff --git a/sr_port/gtmsource_heartbeat.h b/sr_port/gtmsource_heartbeat.h new file mode 100644 index 0000000..ed4291e --- /dev/null +++ b/sr_port/gtmsource_heartbeat.h @@ -0,0 +1,34 @@ +/**************************************************************** + * * + * Copyright 2001, 2003 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GTMSOURCE_HEARTBEAT_H +#define GTMSOURCE_HEARTBEAT_H + +#define gtmsource_stall_heartbeat heartbeat_stalled = TRUE; +#define gtmsource_restart_heartbeat heartbeat_stalled = FALSE; +#define gtmsource_is_heartbeat_stalled (heartbeat_stalled) +#ifndef REPL_DISABLE_HEARTBEAT +#define gtmsource_is_heartbeat_due(now) \ + (0 != last_sent_time \ + && difftime(*(now), last_sent_time) >= (double)jnlpool.gtmsource_local->connect_parms[GTMSOURCE_CONN_HEARTBEAT_PERIOD]) +#else +#define gtmsource_is_heartbeat_due(now) FALSE +#endif + +GBLREF boolean_t heartbeat_stalled; +GBLREF repl_heartbeat_que_entry_t *repl_heartbeat_que_head; +GBLREF repl_heartbeat_que_entry_t *repl_heartbeat_free_head; +GBLREF time_t last_sent_time; +GBLREF time_t earliest_sent_time; + +void gtmsource_heartbeat_timer(TID tid, int4 interval_len, int *interval_ptr); + +#endif /* GTMSOURCE_HEARTBEAT_H */ diff --git a/sr_port/gtmsource_poll_actions.c b/sr_port/gtmsource_poll_actions.c new file mode 100644 index 0000000..e2093f8 --- /dev/null +++ b/sr_port/gtmsource_poll_actions.c @@ -0,0 +1,191 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc.* + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" +#include "gtm_time.h" +#include "gtm_string.h" +#include "gtm_unistd.h" + +#include "gtm_inet.h" +#ifdef VMS +#include /* Required for gtmsource.h */ +#endif + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "repl_msg.h" +#include "gtmsource.h" +#include "repl_dbg.h" +#include "repl_log.h" +#include "iosp.h" +#include "repl_shutdcode.h" +#include "gt_timer.h" +#include "gtmsource_heartbeat.h" +#include "jnl.h" +#include "repl_filter.h" +#include "util.h" +#include "repl_comm.h" +#include "eintr_wrappers.h" +#include "gtmio.h" +#include "sgtm_putmsg.h" +#include "copy.h" + +GBLREF jnlpool_addrs jnlpool; +GBLREF int gtmsource_sock_fd; +GBLREF gtmsource_state_t gtmsource_state; +GBLREF boolean_t gtmsource_logstats; +GBLREF int gtmsource_log_fd; +GBLREF FILE *gtmsource_log_fp; +GBLREF int gtmsource_statslog_fd; +GBLREF FILE *gtmsource_statslog_fp; +GBLREF int gtmsource_filter; +GBLREF volatile time_t gtmsource_now; +GBLREF uint4 log_interval; + +int gtmsource_poll_actions(boolean_t poll_secondary) +{ + /* This function should be called only in active mode, but cannot assert for it */ + + gtmsource_local_ptr_t gtmsource_local; + time_t now; + repl_heartbeat_msg_t overdue_heartbeat; + char *time_ptr; + char time_str[CTIME_BEFORE_NL + 1]; + char print_msg[1024], msg_str[1024]; + boolean_t log_switched = FALSE; + int status; + error_def(ERR_REPLWARN); + time_t temp_time; + gtm_time4_t time4; + + gtmsource_local = jnlpool.gtmsource_local; + if (SHUTDOWN == gtmsource_local->shutdown) + { + repl_log(gtmsource_log_fp, TRUE, TRUE, "Shutdown signalled\n"); + gtmsource_end(); /* Won't return */ + } + if (GTMSOURCE_CHANGING_MODE != gtmsource_state && GTMSOURCE_MODE_PASSIVE == gtmsource_local->mode) + { + repl_log(gtmsource_log_fp, TRUE, TRUE, "Changing mode from ACTIVE to PASSIVE\n"); + gtmsource_state = GTMSOURCE_CHANGING_MODE; + UNIX_ONLY(gtmsource_local->gtmsource_state = gtmsource_state;) + return (SS_NORMAL); + } + if (poll_secondary && GTMSOURCE_CHANGING_MODE != gtmsource_state && GTMSOURCE_WAITING_FOR_CONNECTION != gtmsource_state) + { + now = gtmsource_now; + if (gtmsource_is_heartbeat_overdue(&now, &overdue_heartbeat)) + { + /* Few platforms don't allow unaligned memory access. Passing ack_time to GTM_CTIME(ctime) may + * cause sig. time4 and temp_time are used as temporary variable for converting time to string.*/ + GET_LONG(time4, &overdue_heartbeat.ack_time[0]); + temp_time = time4; + time_ptr = GTM_CTIME(&temp_time); + memcpy(time_str, time_ptr, CTIME_BEFORE_NL); + time_str[CTIME_BEFORE_NL] = '\0'; + VMS_ONLY(SPRINTF(msg_str, "No response received for heartbeat sent at %s with SEQNO %llu in %0.f seconds. " + "Closing connection\n", time_str, *(seq_num *)&overdue_heartbeat.ack_seqno[0], + difftime(now, temp_time))); + NON_GTM64_ONLY(SPRINTF(msg_str, + "No response received for heartbeat sent at %s with SEQNO %llu in %0.f seconds. " + "Closing connection\n", time_str, *(seq_num *)&overdue_heartbeat.ack_seqno[0], + difftime(now, temp_time))); + GTM64_ONLY(SPRINTF(msg_str, "No response received for heartbeat sent at %s with SEQNO %lu in %0.f seconds. " + "Closing connection\n", time_str, *(seq_num *)&overdue_heartbeat.ack_seqno[0], + difftime(now, temp_time))); + sgtm_putmsg(print_msg, VARLSTCNT(4) ERR_REPLWARN, 2, LEN_AND_STR(msg_str)); + repl_log(gtmsource_log_fp, TRUE, TRUE, print_msg); + repl_close(>msource_sock_fd); + SHORT_SLEEP(GTMSOURCE_WAIT_FOR_RECEIVER_CLOSE_CONN); + gtmsource_state = GTMSOURCE_WAITING_FOR_CONNECTION; + UNIX_ONLY(gtmsource_local->gtmsource_state = gtmsource_state;) + return (SS_NORMAL); + } + + if (gtmsource_is_heartbeat_due(&now) && !gtmsource_is_heartbeat_stalled) + { + gtmsource_send_heartbeat(&now); + if (GTMSOURCE_WAITING_FOR_CONNECTION == gtmsource_state || + GTMSOURCE_CHANGING_MODE == gtmsource_state) + return (SS_NORMAL); + } + } + if (0 != gtmsource_local->changelog) + { + if (gtmsource_local->changelog & REPLIC_CHANGE_LOGINTERVAL) + { + repl_log(gtmsource_log_fp, TRUE, TRUE, "Changing log interval from %u to %u\n", + log_interval, gtmsource_local->log_interval); + log_interval = gtmsource_local->log_interval; + gtmsource_reinit_logseqno(); /* will force a LOG on the first send following the interval change */ + } + if (gtmsource_local->changelog & REPLIC_CHANGE_LOGFILE) + { + log_switched = TRUE; + repl_log(gtmsource_log_fp, TRUE, TRUE, "Changing log file to %s\n", gtmsource_local->log_file); +#ifdef UNIX + repl_log_init(REPL_GENERAL_LOG, >msource_log_fd, NULL, gtmsource_local->log_file, NULL); + repl_log_fd2fp(>msource_log_fp, gtmsource_log_fd); +#elif defined(VMS) + util_log_open(STR_AND_LEN(gtmsource_local->log_file)); +#else +#error unsupported platform +#endif + } + if ( log_switched == TRUE ) + repl_log(gtmsource_log_fp, TRUE, TRUE, "Change log to %s successful\n", gtmsource_local->log_file); + gtmsource_local->changelog = 0; + } + if (!gtmsource_logstats && gtmsource_local->statslog) + { +#ifdef UNIX + gtmsource_logstats = TRUE; + repl_log_init(REPL_STATISTICS_LOG, >msource_log_fd, >msource_statslog_fd, gtmsource_local->log_file, + gtmsource_local->statslog_file); + repl_log_fd2fp(>msource_statslog_fp, gtmsource_statslog_fd); + repl_log(gtmsource_log_fp, TRUE, TRUE, "Starting stats log to %s\n", gtmsource_local->statslog_file); + repl_log(gtmsource_statslog_fp, TRUE, TRUE, "Begin statistics logging\n"); +#else + repl_log(gtmsource_log_fp, TRUE, TRUE, "Stats logging not supported on VMS\n"); +#endif + + } else if (gtmsource_logstats && !gtmsource_local->statslog) + { + gtmsource_logstats = FALSE; + repl_log(gtmsource_log_fp, TRUE, TRUE, "Stopping stats log\n"); + /* Force all data out to the file before closing the file */ + repl_log(gtmsource_statslog_fp, TRUE, TRUE, "End statistics logging\n"); + CLOSEFILE_RESET(gtmsource_statslog_fd, status); /* resets "gtmsource_statslog_fd" to FD_INVALID */ + /* We need to FCLOSE because a later open() in repl_log_init() might return the same file descriptor as the one + * that we just closed. In that case, FCLOSE done in repl_log_fd2fp() affects the newly opened file and + * FDOPEN will fail returning NULL for the file pointer. So, we close both the file descriptor and file pointer. + * Note the same problem does not occur with GENERAL LOG because the current log is kept open while opening + * the new log and hence the new file descriptor will be different (we keep the old log file open in case there + * are errors during DUPing. In such a case, we do not switch the log file, but keep the current one). + * We can FCLOSE the old file pointer later in repl_log_fd2fp() */ + FCLOSE(gtmsource_statslog_fp, status); + gtmsource_statslog_fp = NULL; + } + if ((gtmsource_filter & EXTERNAL_FILTER) && ('\0' == gtmsource_local->filter_cmd[0])) + { + repl_log(gtmsource_log_fp, TRUE, TRUE, "Stopping filter\n"); + repl_stop_filter(); + gtmsource_filter &= ~EXTERNAL_FILTER; + } + return (SS_NORMAL); +} diff --git a/sr_port/gtmsource_reinit_logseqno.c b/sr_port/gtmsource_reinit_logseqno.c new file mode 100644 index 0000000..d373db8 --- /dev/null +++ b/sr_port/gtmsource_reinit_logseqno.c @@ -0,0 +1,39 @@ +/**************************************************************** + * * + * Copyright 2005 Fidelity Information Services, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_inet.h" +#ifdef VMS +#include +#endif + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "repl_msg.h" +#include "gtmsource.h" + +GBLREF jnlpool_addrs jnlpool; +GBLREF seq_num lastlog_seqno; +GBLREF uint4 log_interval; +GBLREF qw_num trans_sent_cnt, last_log_tr_sent_cnt; + +void gtmsource_reinit_logseqno(void) +{ + lastlog_seqno = jnlpool.gtmsource_local->read_jnl_seqno - log_interval; + trans_sent_cnt = -(qw_num)(log_interval - 1); + last_log_tr_sent_cnt = 0; +} diff --git a/sr_port/gv_bind_name.c b/sr_port/gv_bind_name.c new file mode 100644 index 0000000..6e5f8f5 --- /dev/null +++ b/sr_port/gv_bind_name.c @@ -0,0 +1,119 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gdsroot.h" +#include "gdskill.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "collseq.h" +#include "gdsfhead.h" +#include "gdscc.h" +#include "copy.h" +#include "filestruct.h" +#include "jnl.h" +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_mname.h" +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" +#include "change_reg.h" +#include "targ_alloc.h" +#include "gvcst_protos.h" /* for gvcst_root_search prototype */ +#include "min_max.h" + +GBLREF gv_namehead *gv_target; +GBLREF gv_key *gv_currkey; +GBLREF gd_region *gv_cur_region; +GBLREF gd_binding *gd_map, *gd_map_top; + +void gv_bind_name(gd_addr *addr, mstr *targ) +{ + gd_binding *map; + ht_ent_mname *tabent; + mname_entry gvent; + int res; + boolean_t added; + enum db_acc_method acc_meth; + gd_region *reg; + gvnh_reg_t *gvnh_reg; + + gd_map = addr->maps; + gd_map_top = gd_map + addr->n_maps; + gvent.var_name.addr = targ->addr; + gvent.var_name.len = MIN(targ->len, MAX_MIDENT_LEN); + COMPUTE_HASH_MNAME(&gvent); + if ((NULL != (tabent = lookup_hashtab_mname((hash_table_mname *)addr->tab_ptr, &gvent))) + && (NULL != (gvnh_reg = (gvnh_reg_t *)tabent->value))) + { + reg = gvnh_reg->gd_reg; + if (!reg->open) + { + gv_init_reg(reg); /* could modify gvnh_reg->gvt if multiple regions map to same db file */ + assert(0 == gvnh_reg->gvt->clue.end); + } + gv_target = gvnh_reg->gvt; + gv_cur_region = reg; + acc_meth = gv_cur_region->dyn.addr->acc_meth; + } else + { + map = gd_map + 1; /* get past local locks */ + for (; (res = memcmp(gvent.var_name.addr, &(map->name[0]), gvent.var_name.len)) >= 0; map++) + { + assert(map < gd_map_top); + if (0 == res && 0 != map->name[gvent.var_name.len]) + break; + } + if (!map->reg.addr->open) + gv_init_reg(map->reg.addr); + gv_cur_region = map->reg.addr; + acc_meth = gv_cur_region->dyn.addr->acc_meth; + if ((dba_cm == acc_meth) || (dba_usr == acc_meth)) + { + gv_target = malloc(SIZEOF(gv_namehead) + gvent.var_name.len); + memset(gv_target, 0, SIZEOF(gv_namehead) + gvent.var_name.len); + gv_target->gvname.var_name.addr = (char *)gv_target + SIZEOF(gv_namehead); + gv_target->nct = 0; + gv_target->collseq = NULL; + gv_target->regcnt = 1; + memcpy(gv_target->gvname.var_name.addr, gvent.var_name.addr, gvent.var_name.len); + gv_target->gvname.var_name.len = gvent.var_name.len; + gv_target->gvname.hash_code = gvent.hash_code; + } else + { + assert(gv_cur_region->max_key_size <= MAX_KEY_SZ); + gv_target = (gv_namehead *)targ_alloc(gv_cur_region->max_key_size, &gvent, gv_cur_region); + } + gvnh_reg = (gvnh_reg_t *)malloc(SIZEOF(gvnh_reg_t)); + gvnh_reg->gvt = gv_target; + gvnh_reg->gd_reg = gv_cur_region; + if (NULL != tabent) + { /* Since the global name was found but gv_target was null and now we created a new gv_target, + * the hash table key must point to the newly created gv_target->gvname. */ + tabent->key = gv_target->gvname; + tabent->value = (char *)gvnh_reg; + } else + { + added = add_hashtab_mname((hash_table_mname *)addr->tab_ptr, &gv_target->gvname, gvnh_reg, &tabent); + assert(added); + } + } + change_reg(); + memcpy(gv_currkey->base, gvent.var_name.addr, gvent.var_name.len); + gv_currkey->base[gvent.var_name.len] = 0; + gvent.var_name.len++; + gv_currkey->base[gvent.var_name.len] = 0; + gv_currkey->end = gvent.var_name.len; + gv_currkey->prev = 0; + return; +} diff --git a/sr_port/gv_init_reg.c b/sr_port/gv_init_reg.c new file mode 100644 index 0000000..9e9be16 --- /dev/null +++ b/sr_port/gv_init_reg.c @@ -0,0 +1,77 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "iosp.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "cryptdef.h" +#include "filestruct.h" +#include "targ_alloc.h" +#include "gvusr.h" +#include "gvcst_protos.h" /* for gvcst_init prototype */ + +GBLREF int4 lkid; +GBLREF bool licensed ; +GBLREF gv_key *gv_currkey; +GBLREF gv_key *gv_altkey; +GBLREF gd_region *gv_cur_region; + +void gv_init_reg (gd_region *reg) +{ + gv_namehead *g; + sgmnt_addrs *csa; +#ifdef NOLICENSE + licensed= TRUE ; +#else + CRYPT_CHKSYSTEM ; +#endif + switch (reg->dyn.addr->acc_meth) + { + case dba_usr: + gvusr_init (reg, &gv_cur_region, &gv_currkey, &gv_altkey); + break; + /* we may be left in dba_cm state for gt_cm, if we have rundown the db and again accessed + the db without quitting out of gtm */ + case dba_cm: + case dba_mm: + case dba_bg: + if (!reg->open) + gvcst_init(reg); + break; + default: + GTMASSERT; + } + assert(reg->open); + GVKEYSIZE_INCREASE_IF_NEEDED(DBKEYSIZE(reg->max_key_size)); + if (reg->dyn.addr->acc_meth == dba_bg || reg->dyn.addr->acc_meth == dba_mm) + { + if (!reg->was_open) + { + csa = (sgmnt_addrs*)&FILE_INFO(reg)->s_addrs; + g = csa->dir_tree; + if (NULL != g) + { /* It is possible that dir_tree has already been targ_alloc'ed. This is because GT.CM or VMS DAL + * calls can run down regions without the process halting out. We don't want to double malloc. + */ + g->clue.end = 0; + } + SET_CSA_DIR_TREE(csa, reg->max_key_size, reg); + } + } + return; +} diff --git a/sr_port/gv_match.c b/sr_port/gv_match.c new file mode 100644 index 0000000..e5c1ac3 --- /dev/null +++ b/sr_port/gv_match.c @@ -0,0 +1,43 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "dpgbldir.h" +#include "gv_match.h" + +/* Multiple regions from across multiple global directories might correspond to the same physical file. + * This routine detects if a given input region's database file matches with the corresponding file of an already open region. + * If yes it returns the FIRST matching region else it returns NULL. + */ +gd_region *gv_match(gd_region *reg) +{ + gd_region *r_top, *gv_region; + gd_addr *addr_ptr; + + for (addr_ptr = get_next_gdr(NULL); addr_ptr; addr_ptr = get_next_gdr(addr_ptr)) + { + for (gv_region = addr_ptr->regions, r_top = gv_region + addr_ptr->n_regions; gv_region < r_top; gv_region++) + { + if (!gv_region->open) + continue; + if (REG_EQUAL(FILE_INFO(reg), gv_region)) + return gv_region; + } + } + return NULL; +} diff --git a/sr_port/gv_match.h b/sr_port/gv_match.h new file mode 100644 index 0000000..203feac --- /dev/null +++ b/sr_port/gv_match.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GV_MATCH_INCLUDED +#define GV_MATCH_INCLUDED + +gd_region *gv_match(gd_region *reg); + +#endif /* GV_MATCH_INCLUDED */ diff --git a/sr_port/gv_rundown.c b/sr_port/gv_rundown.c new file mode 100644 index 0000000..8fe1450 --- /dev/null +++ b/sr_port/gv_rundown.c @@ -0,0 +1,179 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include +#ifdef VMS +#include /* Required for gtmsource.h */ +#include /* Required for the nam$l_esa members */ +#endif + +#include "gtm_inet.h" /* Required for gtmsource.h */ +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "gdskill.h" +#include "gdscc.h" +#include "min_max.h" /* needed for gdsblkops.h */ +#include "gdsblkops.h" +#include "filestruct.h" +#include "jnl.h" +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" +#include "ast.h" +#include "repl_msg.h" +#include "gtmsource.h" +#include "error.h" +#ifdef UNIX +#include "io.h" +#include "gtmsecshr.h" +#include "mutex.h" +#endif + +#include "tp_change_reg.h" +#include "gds_rundown.h" +#include "dpgbldir.h" +#include "gvcmy_rundown.h" +#include "rc_cpt_ops.h" +#include "gv_rundown.h" +#include "targ_alloc.h" +#ifdef GTM_CRYPT +#include "gtmcrypt.h" +#endif +GBLREF gd_region *gv_cur_region; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF boolean_t pool_init; +GBLREF jnlpool_addrs jnlpool; +GBLREF jnlpool_ctl_ptr_t jnlpool_ctl; + +void gv_rundown(void) +{ + gd_region *r_top, *r_save, *r_local; + gd_addr *addr_ptr; + sgm_info *si; +#ifdef VMS + vms_gds_info *gds_info; +#endif + + error_def(ERR_TEXT); + + r_save = gv_cur_region; /* Save for possible core dump */ + gvcmy_rundown(); + ENABLE_AST + + if (pool_init) + rel_lock(jnlpool.jnlpool_dummy_reg); + for (addr_ptr = get_next_gdr(NULL); addr_ptr; addr_ptr = get_next_gdr(addr_ptr)) + { + for (r_local = addr_ptr->regions, r_top = r_local + addr_ptr->n_regions; r_local < r_top; r_local++) + { + if (r_local->open && !r_local->was_open && dba_cm != r_local->dyn.addr->acc_meth) + { /* Rundown has already occurred for GT.CM client regions through gvcmy_rundown() above. + * Hence the (dba_cm != ...) check in the if above. Note that for GT.CM client regions, + * region->open is TRUE although cs_addrs is NULL. + */ + gv_cur_region = r_local; + tp_change_reg(); + gds_rundown(); + /* Now that gds_rundown is done, free up the memory associated with the region. + * Ideally the following memory freeing code should go to gds_rundown, but + * GT.CM calls gds_rundown() and we want to reuse memory for GT.CM. + */ + if (NULL != cs_addrs) + { + if (NULL != cs_addrs->dir_tree) + FREE_CSA_DIR_TREE(cs_addrs); + if (cs_addrs->sgm_info_ptr) + { + si = cs_addrs->sgm_info_ptr; + /* It is possible we got interrupted before initializing all fields of "si" + * completely so account for NULL values while freeing/releasing those fields. + */ + assert((si->tp_csa == cs_addrs) || (NULL == si->tp_csa)); + if (si->jnl_tail) + { + CAREFUL_FREEUP_BUDDY_LIST(si->format_buff_list); + CAREFUL_FREEUP_BUDDY_LIST(si->jnl_list); + } + CAREFUL_FREEUP_BUDDY_LIST(si->recompute_list); + CAREFUL_FREEUP_BUDDY_LIST(si->new_buff_list); + CAREFUL_FREEUP_BUDDY_LIST(si->tlvl_info_list); + CAREFUL_FREEUP_BUDDY_LIST(si->tlvl_cw_set_list); + CAREFUL_FREEUP_BUDDY_LIST(si->cw_set_list); + if (NULL != si->blks_in_use) + { + free_hashtab_int4(si->blks_in_use); + free(si->blks_in_use); + si->blks_in_use = NULL; + } + if (si->cr_array_size) + { + assert(NULL != si->cr_array); + if (NULL != si->cr_array) + free(si->cr_array); + } + if (NULL != si->first_tp_hist) + free(si->first_tp_hist); + free(si); + } + if (cs_addrs->jnl) + { + assert(&FILE_INFO(cs_addrs->jnl->region)->s_addrs == cs_addrs); + if (cs_addrs->jnl->jnllsb) + { + UNIX_ONLY(assert(FALSE)); + free(cs_addrs->jnl->jnllsb); + } + free(cs_addrs->jnl); + } + GTMCRYPT_ONLY( + if (cs_addrs->encrypted_blk_contents) + free(cs_addrs->encrypted_blk_contents); + ) + } + assert(gv_cur_region->dyn.addr->file_cntl->file_info); + VMS_ONLY( + gds_info = (vms_gds_info *)gv_cur_region->dyn.addr->file_cntl->file_info; + if (gds_info->xabpro) + free(gds_info->xabpro); + if (gds_info->xabfhc) + free(gds_info->xabfhc); + if (gds_info->nam) + { + free(gds_info->nam->nam$l_esa); + free(gds_info->nam); + } + if (gds_info->fab) + free(gds_info->fab); + ) + free(gv_cur_region->dyn.addr->file_cntl->file_info); + free(gv_cur_region->dyn.addr->file_cntl); + } + r_local->open = r_local->was_open = FALSE; + } + } + rc_close_section(); + gv_cur_region = r_save; /* Restore value for dumps but this region is now closed and is otherwise defunct */ + cs_addrs = NULL; + GTMCRYPT_ONLY(GTMCRYPT_CLOSE;) +#ifdef UNIX + gtmsecshr_sock_cleanup(CLIENT); +#ifndef MUTEX_MSEM_WAKE + mutex_sock_cleanup(); +#endif +#endif + jnlpool_detach(); +} diff --git a/sr_port/gv_rundown.h b/sr_port/gv_rundown.h new file mode 100644 index 0000000..8f6501e --- /dev/null +++ b/sr_port/gv_rundown.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GV_RUNDOWN_INCLUDED +#define GV_RUNDOWN_INCLUDED + +void gv_rundown(void); + +#endif /* GV_RUNDOWN_INCLUDED */ diff --git a/sr_port/gv_select.c b/sr_port/gv_select.c new file mode 100644 index 0000000..daab1c7 --- /dev/null +++ b/sr_port/gv_select.c @@ -0,0 +1,299 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "jnl.h" /* needed for WCSFLU_* macros */ +#include "muextr.h" +#include "iosp.h" +#include "cli.h" +#include "util.h" +#include "op.h" +#include "gt_timer.h" +#include "mupip_exit.h" +#include "gv_select.h" +#include "global_map.h" +#include "gtmmsg.h" +#include "wcs_flu.h" +#include "min_max.h" +#include "hashtab.h" /* needed for HT_VALUE_DUMMY */ +#ifdef GTM64 +#include "hashtab_int8.h" +#else +#include "hashtab_int4.h" +#endif /* GTM64 */ + +#define MAX_GMAP_ENTRIES_PER_ITER 2 /* maximum increase (could even be negative) in gmap array size per call to global_map */ + +GBLREF bool mu_ctrlc_occurred; +GBLREF bool mu_ctrly_occurred; +GBLREF gd_region *gv_cur_region; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF sgmnt_addrs *cs_addrs; + +static readonly unsigned char percent_lit = '%'; +static readonly unsigned char tilde_lit = '~'; + +void gv_select(char *cli_buff, int n_len, boolean_t freeze, char opname[], glist *gl_head, + int *reg_max_rec, int *reg_max_key, int *reg_max_blk) +{ + bool stashed = FALSE; + int num_quote, len, gmap_size, new_gmap_size, estimated_entries, count, rslt; + char *ptr, *ptr1, *c; + mstr gmap[512], *gmap_ptr, *gmap_ptr_base, gmap_beg, gmap_end; + mval val, curr_gbl_name; + glist *gl_tail, *gl_ptr; +#ifdef GTM64 + hash_table_int8 ext_hash; + ht_ent_int8 *tabent; +#else + hash_table_int4 ext_hash; + ht_ent_int4 *tabent; +#endif /* GTM64 */ + + error_def(ERR_FREEZE); + error_def(ERR_DBRDONLY); + error_def(ERR_SELECTSYNTAX); + error_def(ERR_MUNOFINISH); + error_def(ERR_MUNOACTION); + error_def(ERR_FREEZECTRL); + + memset(gmap, 0, SIZEOF(gmap)); + gmap_size = SIZEOF(gmap) / SIZEOF(gmap[0]); + gmap_ptr_base = &gmap[0]; + /* "estimated_entries" is a conservative estimate of the # of entries that could be used up in the gmap array */ + estimated_entries = 1; /* take into account the NULL gmap entry at the end of the array */ + for (ptr = cli_buff; *ptr; ptr = ptr1) + { + for (ptr1 = ptr; ; ptr1++) + { + if (',' == *ptr1) + { + len = (int)(ptr1 - ptr); + ptr1++; + break; + } else if (!*ptr1) + { + len = (int)(ptr1 - ptr); + break; + } + } + gmap_beg.addr = ptr; + c = gmap_beg.addr + len - 1; + num_quote = 0; + while ('"' == *c) + { + len--; + c--; + num_quote++; + } + if (0 >= len) + { + gtm_putmsg(VARLSTCNT(4) ERR_SELECTSYNTAX, 2, LEN_AND_STR(opname)); + mupip_exit(ERR_MUNOACTION); + } + c = gmap_beg.addr; + while (0 < num_quote) + { + if ('"' == *c) + { + c++; + len--; + } else + { + gtm_putmsg(VARLSTCNT(4) ERR_SELECTSYNTAX, 2, LEN_AND_STR(opname)); + mupip_exit(ERR_MUNOACTION); + } + num_quote--; + } + gmap_beg.addr = c; + if ('^' == *c) + { + gmap_beg.addr++; + len--; + } + gmap_beg.len = len; + c = mu_extr_ident(&gmap_beg); + len -= INTCAST(c - gmap_beg.addr); + assert(len >= 0); + if (0 == len) + gmap_end = gmap_beg; + else if (gmap_beg.len == 1 && '*' == *c) + { + gmap_beg.addr = (char*)&percent_lit; + gmap_beg.len = SIZEOF(percent_lit); + gmap_end.addr = (char*)&tilde_lit; + gmap_end.len = SIZEOF(tilde_lit); + } else if (1 == len && '*' == *c) + { + gmap_end = gmap_beg; + gmap_beg.len--; + *c = '~'; + } else if (':' != *c) + { + gtm_putmsg(VARLSTCNT(4) ERR_SELECTSYNTAX, 2, LEN_AND_STR(opname)); + mupip_exit(ERR_MUNOACTION); + } else + { + gmap_beg.len = INTCAST(c - gmap_beg.addr); + c++; + gmap_end.addr = c; + gmap_end.len = len - 1; + if ('^' == *c) + { + gmap_end.addr++; + gmap_end.len--; + } + c = mu_extr_ident(&gmap_end); + MSTR_CMP(gmap_beg, gmap_end, rslt); + if (((c - gmap_end.addr) != gmap_end.len) || (0 < rslt)) + { + gtm_putmsg(VARLSTCNT(4) ERR_SELECTSYNTAX, 2, LEN_AND_STR(opname)); + mupip_exit(ERR_MUNOACTION); + } + } + /* "estimated_entries" is the maximum number of entries that could be used up in the gmap array including the + * next global_map call. The actual number of used entries could be much lower than this. + * But since determining the actual number would mean scanning the gmap array for the first NULL pointer (a + * performance overhead), we do an approximate check instead. + */ + estimated_entries += MAX_GMAP_ENTRIES_PER_ITER; + if (estimated_entries >= gmap_size) + { /* Current gmap array does not have enough space. Double size before calling global_map */ + new_gmap_size = gmap_size * 2; /* double size of gmap array */ + gmap_ptr = (mstr *)malloc(SIZEOF(mstr) * new_gmap_size); + memcpy(gmap_ptr, gmap_ptr_base, SIZEOF(mstr) * gmap_size); + if (gmap_ptr_base != &gmap[0]) + free(gmap_ptr_base); + gmap_size = new_gmap_size; + gmap_ptr_base = gmap_ptr; + } + global_map(gmap_ptr_base, &gmap_beg, &gmap_end); + DEBUG_ONLY( + count = 1; + for (gmap_ptr = gmap_ptr_base; gmap_ptr->addr; gmap_ptr++) + count++; + assert(count < gmap_size); + ) + } + if (freeze) + { + GTM64_ONLY(init_hashtab_int8(&ext_hash, 0, HASHTAB_COMPACT, HASHTAB_SPARE_TABLE);) + NON_GTM64_ONLY(init_hashtab_int4(&ext_hash, 0, HASHTAB_COMPACT, HASHTAB_SPARE_TABLE);) + } + gl_head->next = NULL; + gl_tail = gl_head; + *reg_max_rec = 0; + *reg_max_key = 0; + *reg_max_blk = 0; + for (gmap_ptr = gmap_ptr_base; gmap_ptr->addr ; gmap_ptr++) + { + curr_gbl_name.mvtype = MV_STR; + curr_gbl_name.str = *gmap_ptr++; + op_gvname(VARLSTCNT(1) &curr_gbl_name); + if (dba_cm == gv_cur_region->dyn.addr->acc_meth) + { util_out_print("Can not select globals from region !AD across network",TRUE,gv_cur_region->rname_len, + gv_cur_region->rname); + mupip_exit(ERR_MUNOFINISH); + + } + if (dba_bg != gv_cur_region->dyn.addr->acc_meth && dba_mm != gv_cur_region->dyn.addr->acc_meth) + { + assert(gv_cur_region->dyn.addr->acc_meth == dba_usr); + util_out_print("Can not select globals from non-GDS format region !AD",TRUE,gv_cur_region->rname_len, + gv_cur_region->rname); + mupip_exit(ERR_MUNOFINISH); + } + op_gvdata(&val); + if (0 == val.m[1]) + { + op_gvname(VARLSTCNT(1) &curr_gbl_name); + op_gvorder(&curr_gbl_name); + if (!curr_gbl_name.str.len) + break; + assert('^' == *curr_gbl_name.str.addr); + curr_gbl_name.str.addr++; + curr_gbl_name.str.len--; + } + for (;;) + { + MSTRP_CMP(&curr_gbl_name.str, gmap_ptr, rslt); + if (0 < rslt) + break; + if (freeze) + { + /* Note: We cannot use int4 hash when we will have 64-bit address. + * In that case we may choose to hash the region name or use int8 hash */ + + GTM64_ONLY(if(add_hashtab_int8(&ext_hash,(gtm_uint64_t *)&gv_cur_region, HT_VALUE_DUMMY, &tabent))) + NON_GTM64_ONLY(if (add_hashtab_int4(&ext_hash, (uint4 *)&gv_cur_region, HT_VALUE_DUMMY, &tabent))) + { + if (cs_addrs->hdr->freeze) + { + gtm_putmsg(VARLSTCNT(4) ERR_FREEZE, 2, gv_cur_region->rname_len, + gv_cur_region->rname); + mupip_exit(ERR_MUNOFINISH); + } + /* Cannot proceed for read-only data files */ + if (gv_cur_region->read_only) + { + util_out_print("Cannot freeze the database",TRUE); + gtm_putmsg(VARLSTCNT(4) ERR_DBRDONLY, 2, + DB_LEN_STR(gv_cur_region)); + mupip_exit(ERR_MUNOFINISH); + } + while (REG_ALREADY_FROZEN == region_freeze(gv_cur_region, TRUE, FALSE, FALSE)) + { + hiber_start(1000); + if (mu_ctrly_occurred || mu_ctrlc_occurred) + { + gtm_putmsg(VARLSTCNT(1) ERR_FREEZECTRL); + mupip_exit(ERR_MUNOFINISH); + } + } + wcs_flu(WCSFLU_FLUSH_HDR | WCSFLU_WRITE_EPOCH | WCSFLU_SYNC_EPOCH); + } + } + assert(0 < curr_gbl_name.str.len); + gl_ptr = (glist*)malloc(SIZEOF(glist) - 1 + curr_gbl_name.str.len); + gl_ptr->name.mvtype = MV_STR; + gl_ptr->name.str.addr = (char*)gl_ptr->nbuf; + gl_ptr->name.str.len = curr_gbl_name.str.len; + memcpy(gl_ptr->nbuf, curr_gbl_name.str.addr, curr_gbl_name.str.len); + gl_ptr->next = 0; + gl_tail->next = gl_ptr; + gl_tail = gl_ptr; + if (*reg_max_rec < cs_data->max_rec_size) *reg_max_rec = cs_data->max_rec_size; + if (*reg_max_key < cs_data->max_key_size) *reg_max_key = cs_data->max_key_size; + if (*reg_max_blk < cs_data->blk_size) *reg_max_blk = cs_data->blk_size; + op_gvname(VARLSTCNT(1) &gl_tail->name); + op_gvorder(&curr_gbl_name); + if (0 == curr_gbl_name.str.len) + { + (gmap_ptr + 1)->addr = 0; + break; + } + assert('^' == *curr_gbl_name.str.addr); + curr_gbl_name.str.addr++; + curr_gbl_name.str.len--; + } + } + if (gmap_ptr_base != &gmap[0]) + free(gmap_ptr_base); +} diff --git a/sr_port/gv_select.h b/sr_port/gv_select.h new file mode 100644 index 0000000..77b7b64 --- /dev/null +++ b/sr_port/gv_select.h @@ -0,0 +1,18 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GV_SELECT_INCLUDED +#define GV_SELECT_INCLUDED + +void gv_select(char *cli_buff, int n_len, boolean_t freeze, char opname[], glist *gl_head, + int *reg_max_rec, int *reg_max_key, int *reg_max_blk); + +#endif /* GV_SELECT_INCLUDED */ diff --git a/sr_port/gv_trigger_common.h b/sr_port/gv_trigger_common.h new file mode 100644 index 0000000..0bf42d9 --- /dev/null +++ b/sr_port/gv_trigger_common.h @@ -0,0 +1,73 @@ +/**************************************************************** + * * + * Copyright 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef _GV_TRIGGER_COMMON_INCLUDED +#define _GV_TRIGGER_COMMON_INCLUDED +/* Following macros, though related to trigger global (^#t), are needed in trigger non-supported platforms. + * While gv_trigger.h lives placed in sr_unix, VMS needs the following macros. + */ + +#define HASHT_GBL_CHAR1 '#' +#define HASHT_GBL_CHAR2 't' +#define HASHT_GBLNAME "#t" +#define HASHT_FULL_GBLNAME "^#t" + +#define HASHT_GBLNAME_LEN STR_LIT_LEN(HASHT_GBLNAME) +#define HASHT_FULL_GBLNM_LEN STR_LIT_LEN(HASHT_FULL_GBLNAME) +#define HASHT_GBLNAME_FULL_LEN STR_LIT_LEN(HASHT_GBLNAME) + 1 /* including terminating '\0' subscript */ + +/* This macro assumes addr points to a gv_currkey like subscript representation (with potential subscripts) */ +#define IS_GVKEY_HASHT_GBLNAME(LEN, ADDR) ((HASHT_GBLNAME_LEN <= LEN) \ + && (KEY_DELIMITER == ADDR[HASHT_GBLNAME_LEN]) \ + && (HASHT_GBL_CHAR1 == ADDR[0]) \ + && (HASHT_GBL_CHAR2 == ADDR[1])) + +/* This macro assumes addr points to an mname (i.e. unsubscripted) */ +#define IS_MNAME_HASHT_GBLNAME(MNAME) ((HASHT_GBLNAME_LEN == MNAME.len) && !MEMCMP_LIT(MNAME.addr, HASHT_GBLNAME)) + +/* Similar to IS_GVKEY_HASHT_GBLNAME but used in places where ADDR points to ZWR formatted KEY (includes '^') */ +#define IS_GVKEY_HASHT_FULL_GBLNAME(LEN, ADDR) ((HASHT_FULL_GBLNM_LEN <= LEN) \ + && ('^' == ADDR[0]) \ + && (HASHT_GBL_CHAR1 == ADDR[1]) \ + && (HASHT_GBL_CHAR2 == ADDR[2])) + +#define HASHT_GBL_CURLABEL "2" /* Currently supported ^#t global format */ + +/* HASHT_GBL_CURLABEL values of prior trigger versions */ +#define V19_HASHT_GBL_LABEL "1" /* V5.4-000 to V5.4-001 */ + +#define LITERAL_HASHLABEL "#LABEL" +#define LITERAL_HASHCYCLE "#CYCLE" +#define LITERAL_HASHCOUNT "#COUNT" +#define LITERAL_CMD "CMD" +#define LITERAL_GVSUBS "GVSUBS" +#define LITERAL_OPTIONS "OPTIONS" +#define LITERAL_DELIM "DELIM" +#define LITERAL_ZDELIM "ZDELIM" +#define LITERAL_PIECES "PIECES" +#define LITERAL_TRIGNAME "TRIGNAME" +#define LITERAL_XECUTE "XECUTE" +#define LITERAL_CHSET "CHSET" + +#define LITERAL_HASHLABEL_LEN STR_LIT_LEN(LITERAL_HASHLABEL) +#define LITERAL_HASHCYCLE_LEN STR_LIT_LEN(LITERAL_HASHCYCLE) +#define LITERAL_HASHCOUNT_LEN STR_LIT_LEN(LITERAL_HASHCOUNT) +#define LITERAL_CMD_LEN STR_LIT_LEN(LITERAL_CMD) +#define LITERAL_GVSUBS_LEN STR_LIT_LEN(LITERAL_GVSUBS) +#define LITERAL_OPTIONS_LEN STR_LIT_LEN(LITERAL_OPTIONS) +#define LITERAL_DELIM_LEN STR_LIT_LEN(LITERAL_DELIM) +#define LITERAL_ZDELIM_LEN STR_LIT_LEN(LITERAL_ZDELIM) +#define LITERAL_PIECES_LEN STR_LIT_LEN(LITERAL_PIECES) +#define LITERAL_XECUTE_LEN STR_LIT_LEN(LITERAL_XECUTE) +#define LITERAL_TRIGNAME_LEN STR_LIT_LEN(LITERAL_TRIGNAME) +#define LITERAL_CHSET_LEN STR_LIT_LEN(LITERAL_CHSET) + +#endif diff --git a/sr_port/gv_xform_key.c b/sr_port/gv_xform_key.c new file mode 100644 index 0000000..fcc91da --- /dev/null +++ b/sr_port/gv_xform_key.c @@ -0,0 +1,91 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gvsub2str.h" +#include "mvalconv.h" +#include "gv_xform_key.h" + +GBLREF int4 gv_keysize; +GBLREF gv_namehead *gv_target; + +/* transform gv_currkey or gv_altkey based on collation sequence + * if XBACK is true then convert from internal to external format. + * if XBACK is false, convert from external to internal format + */ + +void gv_xform_key(gv_key *keyp, boolean_t xback) +{ + unsigned char *c0, *c1, *ctop; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + if (TREF(gv_sparekey_size) < gv_keysize) + { + if (TREF(gv_sparekey)) + free(TREF(gv_sparekey)); + else + { + (TREF(gv_sparekey_mval)).str.addr = (char *)malloc(MAX_ZWR_KEY_SZ); + (TREF(gv_sparekey_mval)).mvtype = MV_STR; + } + TREF(gv_sparekey) = (gv_key *)malloc(SIZEOF(gv_key) - 1 + gv_keysize); + TREF(gv_sparekey_size) = gv_keysize; + } + assert(keyp->top == gv_keysize); + assert(keyp->end < keyp->top); + memcpy(TREF(gv_sparekey), keyp, SIZEOF(gv_key) + keyp->end); + c1 = keyp->base; + while (*c1++) + ; + c0 = (TREF(gv_sparekey))->base + (c1 - keyp->base); + ctop = &((TREF(gv_sparekey))->base[(TREF(gv_sparekey))->end]); + if (!*c0) /* no subscipts */ + { + assert(c0 == ctop); + return; + } + assert(c0 < ctop); + keyp->prev = 0; + keyp->end = c1 - keyp->base; + for (; c0 < ctop; ) + { + if (STR_SUB_PREFIX != *c0) + { + assert(!gv_target->nct); + while (*c1++ = *c0++) + ; + keyp->prev = keyp->end; + keyp->end = c1 - keyp->base; + } else + { + TREF(transform) = xback; + (TREF(gv_sparekey_mval)).str.len + = gvsub2str(c0, (unsigned char *)((TREF(gv_sparekey_mval)).str.addr), FALSE) + - (unsigned char *)(TREF(gv_sparekey_mval)).str.addr; + TREF(transform) = !xback; + mval2subsc(TADR(gv_sparekey_mval), keyp); + c1 = &keyp->base[keyp->end]; + while (*c0++) + ; + } + assert(keyp->end < keyp->top); + } + return; +} diff --git a/sr_port/gv_xform_key.h b/sr_port/gv_xform_key.h new file mode 100644 index 0000000..c9b2195 --- /dev/null +++ b/sr_port/gv_xform_key.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __GV_XFORM_KEY_H__ +#define __GV_XFORM_KEY_H__ + +void gv_xform_key(gv_key *keyp, boolean_t xback); + +#endif diff --git a/sr_port/gvcmx.h b/sr_port/gvcmx.h new file mode 100644 index 0000000..f8a92be --- /dev/null +++ b/sr_port/gvcmx.h @@ -0,0 +1,29 @@ +/**************************************************************** + * * + * Copyright 2001, 2004 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GVCMX_INCLUDED +#define GVCMX_INCLUDED + +mint gvcmx_data(void); +bool gvcmx_get(mval *v); +bool gvcmx_order(void); +bool gvcmx_query(mval *val); +bool gvcmx_reqremlk(unsigned char laflag, int4 time); +bool gvcmx_resremlk(unsigned char c); +bool gvcmx_zprevious(void); +void gvcmx_canremlk(void); +void gvcmx_kill(bool so_subtree); +void gvcmx_put(mval *v); +void gvcmx_increment(mval *increment, mval *result); +void gvcmx_susremlk(unsigned char rmv_locks); +void gvcmx_unlock(unsigned char rmv_locks, bool specific, char incr); + +#endif /* GVCMX_INCLUDED */ diff --git a/sr_port/gvcmy_close.h b/sr_port/gvcmy_close.h new file mode 100644 index 0000000..f4839d3 --- /dev/null +++ b/sr_port/gvcmy_close.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GVCMY_CLOSE_H_INCLUDED +#define GVCMY_CLOSE_H_INCLUDED + +void gvcmy_close(struct CLB *); + +#endif diff --git a/sr_port/gvcmy_rundown.h b/sr_port/gvcmy_rundown.h new file mode 100644 index 0000000..9dff43b --- /dev/null +++ b/sr_port/gvcmy_rundown.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __GVCMY_RUNDOWN_H__ +#define __GVCMY_RUNDOWN_H__ + +void gvcmy_rundown(void); + +#endif diff --git a/sr_port/gvcst_blk_build.c b/sr_port/gvcst_blk_build.c new file mode 100644 index 0000000..bdda468 --- /dev/null +++ b/sr_port/gvcst_blk_build.c @@ -0,0 +1,245 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "min_max.h" /* needed for gdsblkops.h */ +#include "gdsblkops.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdscc.h" +#include "gdskill.h" +#include "filestruct.h" +#include "copy.h" +#include "jnl.h" +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" +#include "gvcst_blk_build.h" +#include "gtmimagename.h" + +#ifdef DEBUG +GBLREF boolean_t skip_block_chain_tail_check; +#endif + +GBLREF unsigned char cw_set_depth; +GBLREF uint4 dollar_tlevel; +GBLREF sgm_info *sgm_info_ptr; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF boolean_t write_after_image; +GBLREF unsigned int t_tries; + +void gvcst_blk_build(cw_set_element *cse, sm_uc_ptr_t base_addr, trans_num ctn) +{ + blk_segment *seg, *stop_ptr, *array; + off_chain chain; + sm_uc_ptr_t ptr, ptrtop; + sm_ulong_t n; + int4 offset; + trans_num blktn; +# ifdef DEBUG + boolean_t integ_error_found; + rec_hdr_ptr_t rp; + sm_uc_ptr_t chainptr, input_base_addr; + unsigned short nRecLen; +# endif + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + assert((dba_bg != cs_data->acc_meth) || dollar_tlevel || !cs_addrs->now_crit || write_after_image); + assert((dba_mm != cs_data->acc_meth) || dollar_tlevel || cs_addrs->now_crit); + assert(cse->mode != gds_t_writemap); + array = (blk_segment *)cse->upd_addr; + assert(array->len >= SIZEOF(blk_hdr)); + assert(array->len <= cs_data->blk_size); + assert((cse->ins_off + SIZEOF(block_id)) <= array->len); + assert((short)cse->index >= 0); + assert(!cse->undo_next_off[0] && !cse->undo_offset[0]); + assert(!cse->undo_next_off[1] && !cse->undo_offset[1]); + DEBUG_ONLY(input_base_addr = base_addr;) + + if (base_addr == NULL) + { /* it's the first private TP build */ + assert(dollar_tlevel); + assert(cse->blk_target); + base_addr = cse->new_buff = (unsigned char *)get_new_free_element(sgm_info_ptr->new_buff_list); + cse->first_copy = TRUE; + } else + assert(0 == ((sm_ulong_t)base_addr & 3)); /* word aligned at least */ + + /* The block-transaction-number is modified before the contents of the block are modified. This is + * done so as to allow a cdb_sc_blkmod check (done in t_qread, gvcst_search, gvcst_put and tp_hist) + * to be done out-of-crit by just checking for the transaction numbers. If the contents of the block + * were modified first, there is a possibility that the block-transaction number didn't get updated + * although the contents of the block may have changed and basing the decision of block-modified on + * just the transaction numbers may not always be correct. + * Note that in mm_update and bg_update there is an else block where instead of gvcst_blk_build(), + * a memcpy is done. To effect the above change, we also need to switch the order of memcpy and + * block-transaction-number-updation in those places. + * Note that a similar change is not needed in gvcst_map_build() because that will never be in the + * search history for any key. + */ + if (!ctn && dollar_tlevel) + { /* Subtract one so will pass concurrency control for mm databases. + * This block is guaranteed to be in an earlier history from when it was first read, + * so this history is superfluous for concurrency control. + * The correct tn is put in the block in mm_update or bg_update when the block is copied to the database. + */ + ctn = cs_addrs->ti->curr_tn - 1; + } + /* Assert that the block's transaction number is LESS than the transaction number corresponding to the blk build. + * i.e. no one else should have touched the block contents in shared memory from the time we locked this in phase1 + * to the time we build it in phase2. + * There are a few exceptions. + * a) With DSE, it is possible to change the block transaction number and then a DSE or MUPIP command can run + * on the above block with the above condition not true. + * b) tp_tend calls gvcst_blk_build for cse's with mode kill_t_write/kill_t_create. For them we build a private + * copy of the block for later use in phase2 of the M-kill. In this case, blktn could be + * uninitialized so cannot do any checks using this value. + * c) For MM, we dont have two phase commits so dont do any checks in that case. + * d) For acquired blocks, it is possible that some process had read in the uninitialized block from disk + * outside of crit (due to concurrency issues). Therefore the buffer could contain garbage. So we cannot + * rely on the buffer contents to determine the block's transaction number. + * e) For VMS, if a twin is created, we explicitly set its buffer tn to be equal to ctn in phase1. + * But since we are not passed the "cr" in this routine, it is not easily possible to check that. + * Hence in case of VMS, we relax the check so buffertn == ctn is allowed. + */ + DEBUG_ONLY(blktn = ((blk_hdr_ptr_t)base_addr)->tn); + assert(!IS_MCODE_RUNNING || !cs_addrs->t_commit_crit || (dba_bg != cs_data->acc_meth) || (n_gds_t_op < cse->mode) + || (cse->mode == gds_t_acquired) || (blktn UNIX_ONLY(<) VMS_ONLY(<=) ctn)); + assert((ctn < cs_addrs->ti->early_tn) || write_after_image); + ((blk_hdr_ptr_t)base_addr)->bver = GDSVCURR; + ((blk_hdr_ptr_t)base_addr)->tn = ctn; + ((blk_hdr_ptr_t)base_addr)->bsiz = UINTCAST(array->len); + ((blk_hdr_ptr_t)base_addr)->levl = cse->level; + + if (cse->forward_process) + { + stop_ptr = (blk_segment *)array->addr; + seg = cse->first_copy ? array + 1: array + 2; + ptr = base_addr + SIZEOF(blk_hdr); + if (!cse->first_copy) + ptr += ((blk_segment *)(array + 1))->len; + for ( ; seg <= stop_ptr; ) + { + assert(0L <= ((INTPTR_T)seg->len)); + DBG_BG_PHASE2_CHECK_CR_IS_PINNED(cs_addrs, seg); + memmove(ptr, seg->addr, seg->len); + ptr += seg->len; + seg++; + } + } else + { + stop_ptr = cse->first_copy ? array : array + 1; + seg = (blk_segment *)array->addr; + ptr = base_addr + array->len; + while (seg != stop_ptr) + { + assert(0L <= ((INTPTR_T)seg->len)); + DBG_BG_PHASE2_CHECK_CR_IS_PINNED(cs_addrs, seg); + ptr -= (n = seg->len); + memmove(ptr, seg->addr, n); + seg--; + } + } + if (dollar_tlevel) + { + if (cse->ins_off) + { /* if the cw set has a reference to resolve, move it to the block */ + assert(cse->index < sgm_info_ptr->cw_set_depth); + assert((int)cse->ins_off >= (int)(SIZEOF(blk_hdr) + SIZEOF(rec_hdr))); + assert((int)(cse->next_off + cse->ins_off + SIZEOF(block_id)) <= array->len); + if (cse->first_off == 0) + cse->first_off = cse->ins_off; + chain.flag = 1; + chain.cw_index = cse->index; + chain.next_off = cse->next_off; + ptr = base_addr + cse->ins_off; + GET_LONGP(ptr, &chain); + cse->index = 0; + cse->ins_off = 0; + cse->next_off = 0; + } +# ifdef DEBUG + if (offset = cse->first_off) + { /* Verify the integrity of the TP chains within a newly created block. + * If it is the first TP private build, the update array could have referenced + * shared memory global buffers which could have been concurrently updated. + * So the integrity of the chain cannot be easily verified. If ever we find + * an integ error in the chain, we check if this is the first private TP build + * and if so allow it but set a debug flag donot_commit so we never ever commit + * this transaction. The hope is that it will instead restart after validation. + */ + ptr = base_addr; + ptrtop = ptr + ((blk_hdr_ptr_t)ptr)->bsiz; + chainptr = ptr + offset; + ptr += SIZEOF(blk_hdr); + integ_error_found = FALSE; + for ( ; ptr < ptrtop; ) + { + do + { + GET_USHORT(nRecLen, &((rec_hdr_ptr_t)ptr)->rsiz); + if (0 == nRecLen) + { + assert(NULL == input_base_addr); + integ_error_found = TRUE; + break; + } + ptr += nRecLen; + if (ptr - SIZEOF(off_chain) == chainptr) + break; + if ((ptr - SIZEOF(off_chain)) > chainptr) + { + assert(NULL == input_base_addr); + integ_error_found = TRUE; + break; + } + GET_LONGP(&chain, ptr - SIZEOF(off_chain)); + if (chain.flag) + { + assert(NULL == input_base_addr); + integ_error_found = TRUE; + break; + } + } while (ptr < ptrtop); + if (integ_error_found) + break; + if (chainptr < ptrtop) + { + GET_LONGP(&chain, chainptr); + assert(1 == chain.flag || (skip_block_chain_tail_check && (0 == chain.next_off))); + assert(chain.cw_index < sgm_info_ptr->cw_set_depth); + offset = chain.next_off; + if (0 == offset) + chainptr = ptrtop; + else + { + chainptr = chainptr + offset; + assert(chainptr < ptrtop); /* ensure we have not overrun the buffer */ + } + } + } + if (integ_error_found) + TREF(donot_commit) |= DONOTCOMMIT_GVCST_BLK_BUILD_TPCHAIN; + else + assert(0 == offset); /* ensure the chain is NULL terminated */ + } +# endif + } else + assert(dollar_tlevel || (cse->index < (int)cw_set_depth)); +} diff --git a/sr_port/gvcst_blk_build.h b/sr_port/gvcst_blk_build.h new file mode 100644 index 0000000..948450d --- /dev/null +++ b/sr_port/gvcst_blk_build.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GVCST_BLK_BUILD_INCLUDED +#define GVCST_BLK_BUILD_INCLUDED + +void gvcst_blk_build(cw_set_element *cse, sm_uc_ptr_t base_addr, trans_num ctn); + +#endif /* GVCST_BLK_BUILD_INCLUDED */ diff --git a/sr_port/gvcst_blk_search.c b/sr_port/gvcst_blk_search.c new file mode 100644 index 0000000..39642e2 --- /dev/null +++ b/sr_port/gvcst_blk_search.c @@ -0,0 +1,434 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* + * NOTE: See also GVCST_BLK_SEARCH.MAR for the VAX platform. + * + * ------------------------------------------------------------------- + * Search a single gvcst block + * + * function definition + * enum cdb_sc gvcst_search_blk(pKey,pStat) + * gv_key *pKey; - target key + * srch_blk_status *pStat; - status block for this buffer + * + * function returns cdb_sc_normal if successful; otherwise, + * one of the failure codes cdb_sc_badoffset or cdb_sc_blklenerr. + * + * function definition + * enum cdb_sc gvcst_search_tail(pKey,pStat,pOldKey) + * gv_key *pKey; - target key + * srch_blk_status *pStat; - status block for this buffer + * gv_key *pOldKey; - key for status block + * + * gvcst_search_tail is identical to gvcst_search_blk, + * except instead of starting with the beginning + * of the block, it starts where the previous gvcst_search_blk + * left off, using the srch_blk_status to set-up. + * + * if successful, fills in srch_blk_status as follows: + * + * --------------------------------- + * | Block number (untouched) | + * --------------------------------- + * | Buffer address (input param) | + * --------------------------------- + * | Transaction number (untouched)| + * --------------------------------- + * | match | offset | previous record + * --------------------------------- + * | match | offset | current record + * --------------------------------- + * + * if the match is not found, or is found at the top or bottom of the + * block, then the values of the return fields are as follows: + * + * PREVIOUS REC CURRENT REC + * CONDITION MATCH OFFSET MATCH OFFSET + * --------- ---- ------ ----- ------ + * Buffer empty 0 0 0 7 + * Hit first key in block 0 0 a 7 + * Hit star key b c 0 x + * Hit last key (leaf) b c a x + * Went past last key (leaf) b x 0 y + * + * where: + * a = size of target key + * b = number of characters which match on the previous + * key (including those which have been compressed away) + * c = offset of previous record + * x = offset for last record in the block + * y = top of buffer (same as block bsize) + * + * + * Block structure + * block : + * blk_hdr : + * blk_data : record...record + * record : [rec_data] + * rec_hdr : + * rec_data : [byte...byte] + * ------------------------------------------------------------------- + */ + +#include "mdef.h" + +#include "gtm_stdio.h" +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "copy.h" +#include "cdb_sc.h" +#include "gvcst_protos.h" /* for gvcst_search_tail,gvcst_search_blk prototype */ +#include "send_msg.h" + +GBLREF unsigned int t_tries; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gd_region *gv_cur_region; + +#ifdef DEBUG +#include "gdscc.h" + +#define DBG_CHECK_SRCH_HIST_AND_CSE_BUFFER_MATCH(pStat) \ +{ \ + GBLREF uint4 dollar_tlevel; \ + \ + srch_blk_status *tp_srch_status; \ + cw_set_element *cse; \ + \ + if (dollar_tlevel) \ + { \ + tp_srch_status = pStat->first_tp_srch_status; \ + if (NULL != tp_srch_status) \ + { \ + cse = tp_srch_status->cse; \ + if (NULL != cse) \ + assert(cse->new_buff == pStat->buffaddr); \ + } \ + } \ +} +#else +#define DBG_CHECK_SRCH_HIST_AND_CSE_BUFFER_MATCH(pStat) +#endif + +#define INVOKE_GVCST_SEARCH_FAIL_IF_NEEDED(pStat) if (CDB_STAGNATE <= t_tries) gvcst_search_fail(pStat); + +static void gvcst_search_fail(srch_blk_status *pStat) +{ + char buff[1024], crbuff[256], regbuff[512]; + + error_def(ERR_TEXT); + + assert(CDB_STAGNATE <= t_tries); + assert((NULL != pStat) && ((NULL != pStat->cr) || (dba_mm == gv_cur_region->dyn.addr->acc_meth)) && (NULL != cs_addrs)); + if (NULL != pStat) + { + if (NULL != pStat->cr) + SPRINTF(crbuff, ": crbuff = 0x%lX", pStat->cr->buffaddr); + else + crbuff[0] = '\0'; + memcpy(regbuff, gv_cur_region->rname, gv_cur_region->rname_len); + regbuff[gv_cur_region->rname_len] = '\0'; + SPRINTF(buff, "Possible data corruption in region %s : blk = 0x%X : buff = 0x%lX : cr = 0x%lX %s : " + "csa = 0x%lX : csalock = 0x%lX", regbuff, pStat->blk_num, (long unsigned int) pStat->buffaddr, + (long unsigned int) pStat->cr, crbuff, (long unsigned int) cs_addrs, + (long unsigned int) cs_addrs->lock_addrs[0]); + send_msg(VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_STR(buff)); + } +} + +/* + * -------------------------------------------------- + * Search for a key in the block + * + * Return: + * cdb_sc_normal - success + * cdb_sc_badoffset - record with 0 length encountered, + * possibly a corrupt block + * cdb_sc_blklenerr - end of block reached without match + * -------------------------------------------------- + */ + +enum cdb_sc gvcst_search_blk (gv_key *pKey, srch_blk_status *pStat) +{ + /* register variables named in perceived order of declining impact */ + register int nFlg, nTargLen, nMatchCnt, nTmp; + sm_uc_ptr_t pBlkBase, pRecBase, pTop, pRec, pPrevRec; + unsigned char *pCurrTarg, *pTargKeyBase; + unsigned short nRecLen; + + /* the following load code (and code in a few other places) is coded in a "assember" style + * in an attempt to encourage the compiler to get it efficient; + * if instance, memory and non-memory instructions are interlaced to encourge pipelining. + * of course a great compiler doesn't need help, but this is portable code and ... + */ + DBG_CHECK_SRCH_HIST_AND_CSE_BUFFER_MATCH(pStat); + pBlkBase = pStat->buffaddr; + pRecBase = pBlkBase; + pTop = pBlkBase + ((blk_hdr_ptr_t)pBlkBase)->bsiz; + nRecLen = SIZEOF(blk_hdr); + pCurrTarg = pKey->base; + nMatchCnt = 0; + nTargLen = (int)pKey->end; + pTargKeyBase = pCurrTarg; + nTargLen++; /* for the terminating NUL on the key */ + + for (;;) + { + pRec = pRecBase + nRecLen; + + if (pRec >= pTop) + { /* Terminated at end of block */ + if (pRec > pTop) /* If record goes off the end, then block must be bad */ + { + INVOKE_GVCST_SEARCH_FAIL_IF_NEEDED(pStat); + assert(CDB_STAGNATE > t_tries); + return cdb_sc_blklenerr; + } + nTargLen = 0; + if (((blk_hdr_ptr_t)pBlkBase)->levl == 0) + { /* data block */ + pPrevRec = pRecBase; + pRecBase = pRec; + } + else + nMatchCnt = 0; /* star key */ + break; + } + GET_USHORT(nRecLen, &((rec_hdr_ptr_t)pRec)->rsiz); + if (nRecLen == 0) /* If record length is 0, then block must be bad */ + { + INVOKE_GVCST_SEARCH_FAIL_IF_NEEDED(pStat); + assert(CDB_STAGNATE > t_tries); + return cdb_sc_badoffset; + } + pPrevRec = pRecBase; + pRecBase = pRec; + + /* If current compression count > last match, then this record + also matches on 'last match' characters; keep looping */ + if ((nTmp = ((rec_hdr_ptr_t)pRec)->cmpc) > nMatchCnt) + continue; + + if (nTmp < nMatchCnt) + { /* Terminate on compression count < previous match, + this key is after the target */ + if (nRecLen == BSTAR_REC_SIZE && ((blk_hdr_ptr_t)pBlkBase)->levl != 0) + /* Star key has size of SIZEOF(rec_hdr) + SIZEOF(block_id), make match = 0 */ + nTargLen = 0; + else + /* Data block, make match = current compression count */ + nTargLen = nTmp; + break; + } + + /* Compression count == match count; Compare current target with current record */ + pRec += SIZEOF(rec_hdr); + + do + { + if ((nFlg = *pCurrTarg - *pRec++) != 0) + break; + pCurrTarg++; + } while ( --nTargLen); + + if (nFlg > 0) + nMatchCnt =(int)(pCurrTarg - pTargKeyBase); + else + { /* Key is after target*/ + if (nRecLen == BSTAR_REC_SIZE && (((blk_hdr_ptr_t)pBlkBase)->levl != 0)) + /* Star key has size of SIZEOF(rec_hdr) + SIZEOF(block_id), make match = 0 */ + nTargLen = 0; + else + nTargLen = (int)(pCurrTarg - pTargKeyBase); + break; + } + } + + pStat->prev_rec.offset = (short)(pPrevRec - pBlkBase); + pStat->prev_rec.match = (short)nMatchCnt; + pStat->curr_rec.offset = (short)(pRecBase - pBlkBase); + pStat->curr_rec.match = (short)nTargLen; + + return cdb_sc_normal; +} + + +/* search_tail is the "start anywhere" version of search_blk + getting started is a bit awkward, so excuse the gotos */ +enum cdb_sc gvcst_search_tail (gv_key *pKey, srch_blk_status *pStat, gv_key *pOldKey) +{ + /* register variables named in perceived order of declining impact */ + register int nFlg, nTargLen, nMatchCnt, nTmp; + sm_uc_ptr_t pBlkBase, pRecBase, pRec, pTop, pPrevRec; + unsigned char *pCurrTarg, *pTargKeyBase, *pOldKeyBase, *pCurrTargPos; + unsigned short nRecLen; + + /* see comment in gvcst_search_blk above on coding style */ + + if (pStat->prev_rec.offset == 0) + return gvcst_search_blk(pKey, pStat); /* nice clean start at the begining of a block */ + DBG_CHECK_SRCH_HIST_AND_CSE_BUFFER_MATCH(pStat); + pBlkBase = pStat->buffaddr; + pRecBase = pBlkBase + pStat->curr_rec.offset; + pRec = pRecBase; + pTop = pBlkBase + ((blk_hdr_ptr_t)pBlkBase)->bsiz; + nMatchCnt = pStat->prev_rec.match; + pCurrTarg = pKey->base; + pTargKeyBase = pCurrTarg; + pOldKeyBase = pOldKey->base; + pPrevRec = pBlkBase + pStat->prev_rec.offset; + nTargLen = pKey->end; + nTargLen++; /* for the NUL that terminates the key */ + if (pRec >= pTop) + { /* Terminated at end of block */ +/* eob_tail: */ if (pRec > pTop) + { + INVOKE_GVCST_SEARCH_FAIL_IF_NEEDED(pStat); + assert(CDB_STAGNATE > t_tries); + return cdb_sc_blklenerr; + } + if ((nTargLen = nMatchCnt) != 0) + { + do + { + if (*pCurrTarg++ != *pOldKeyBase++) + break; + } while (--nTargLen); + } + if (((blk_hdr_ptr_t)pBlkBase)->levl != 0) + nMatchCnt = 0; /* star key */ + else + nMatchCnt -= nTargLen; + nTargLen = 0; + } else + { + GET_USHORT(nRecLen, &((rec_hdr_ptr_t)pRec)->rsiz); + + if ((nFlg = nTmp = ((rec_hdr_ptr_t)pRec)->cmpc) != 0) + { + do + { + if ((nFlg = *pCurrTarg - *pOldKeyBase++) != 0) + break; + pCurrTarg++; + } while (--nTmp); + if (nFlg > 0) + { + nMatchCnt = (int)(pCurrTarg - pTargKeyBase); + nTargLen -= nMatchCnt; + } + if (nFlg < 0) + { + nTargLen += (int)(pTargKeyBase - pCurrTarg); + goto match_term; + } + } + if (nFlg == 0) + { + nTmp = nMatchCnt; + nMatchCnt = (int)(pCurrTarg - pTargKeyBase); + nTargLen -= nMatchCnt; + nTmp -= nMatchCnt; + + if (nTmp > 0) + { + pCurrTargPos = pCurrTarg; + + do + { + if (*pCurrTargPos++ != *pOldKeyBase++) + break; + nMatchCnt++; + } while (--nTmp); + } + goto alt_loop_entry; + } + for (;;) + { + pRec = pRecBase + nRecLen; + + if (pRec >= pTop) + { /* Terminated at end of block */ + if (pRec > pTop) /* If record goes off the end, then block must be bad */ + { + INVOKE_GVCST_SEARCH_FAIL_IF_NEEDED(pStat); + assert(CDB_STAGNATE > t_tries); + return cdb_sc_blklenerr; + } + nTargLen = 0; + + if (((blk_hdr_ptr_t)pBlkBase)->levl == 0) + { /* data block */ + pPrevRec = pRecBase; + pRecBase = pRec; + } + else + nMatchCnt = 0; /* star key */ + break; + } + GET_USHORT(nRecLen, &((rec_hdr_ptr_t)pRec)->rsiz); + if (nRecLen == 0) /* If record length is 0, then block must be bad */ + { + INVOKE_GVCST_SEARCH_FAIL_IF_NEEDED(pStat); + assert(CDB_STAGNATE > t_tries); + return cdb_sc_badoffset; + } + pPrevRec = pRecBase; + pRecBase = pRec; + /* If current compression count > last match, then this record + also matches on 'last match' characters; keep looping */ + if ((nTmp = ((rec_hdr_ptr_t)pRec)->cmpc) > nMatchCnt) + continue; + if (nTmp < nMatchCnt) +/* cc_term: */ { /* Terminated on compression count < previous match, + this key is after the target */ + if (nRecLen == BSTAR_REC_SIZE && ((blk_hdr_ptr_t)pBlkBase)->levl != 0) + /* Star key has size of SIZEOF(rec_hdr) + SIZEOF(block_id), make match = 0 */ + nTargLen = 0; + else + /* Data block, make match = current compression count */ + nTargLen = nTmp; + break; + } +alt_loop_entry: /* Compression count == match count; Compare current target with current record */ + pRec += SIZEOF(rec_hdr); + do + { + if ((nFlg = *pCurrTarg - *pRec++) != 0) + break; + pCurrTarg++; + } while (--nTargLen); + if (nFlg > 0) + nMatchCnt = (int)(pCurrTarg - pTargKeyBase); + else +match_term: { /* Key is after target*/ + if (nRecLen == BSTAR_REC_SIZE && (((blk_hdr_ptr_t)pBlkBase)->levl != 0)) + /* Star key has size of SIZEOF(rec_hdr) + SIZEOF(block_id), make match = 0 */ + nTargLen = 0; + else + nTargLen = (int)(pCurrTarg - pTargKeyBase); + break; + } + } + } +/* clean_up: */ + pStat->prev_rec.offset = (short)(pPrevRec - pBlkBase); + pStat->prev_rec.match = (short)nMatchCnt; + pStat->curr_rec.offset = (short)(pRecBase - pBlkBase); + pStat->curr_rec.match = (short)nTargLen; + return cdb_sc_normal; +} diff --git a/sr_port/gvcst_bmp_mark_free.c b/sr_port/gvcst_bmp_mark_free.c new file mode 100644 index 0000000..579d293 --- /dev/null +++ b/sr_port/gvcst_bmp_mark_free.c @@ -0,0 +1,243 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* gvcst_bmp_mark_free.c + This marks all the blocks in kill set list to be marked free. + Note ks must be already sorted +*/ +#include "mdef.h" + +#include "gtm_string.h" + +#include "cdb_sc.h" +#include "gdsroot.h" +#include "gdskill.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gdscc.h" +#include "jnl.h" +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" +#include "memcoherency.h" +#include "gdsblkops.h" /* for CHECK_AND_RESET_UPDATE_ARRAY macro */ + +/* Include prototypes */ +#include "t_qread.h" +#include "t_end.h" +#include "t_retry.h" +#include "t_begin.h" +#include "t_write_map.h" +#include "mm_read.h" +#include "add_inter.h" +#include "gvcst_bmp_mark_free.h" +#include "t_busy2free.h" + +GBLREF char *update_array, *update_array_ptr; +GBLREF cw_set_element cw_set[]; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF unsigned char rdfail_detail; +GBLREF sgm_info *sgm_info_ptr; +GBLREF boolean_t mu_reorg_process; +GBLREF inctn_opcode_t inctn_opcode; +GBLREF inctn_detail_t inctn_detail; /* holds detail to fill in to inctn jnl record */ +GBLREF uint4 dollar_tlevel; + +trans_num gvcst_bmp_mark_free(kill_set *ks) +{ + block_id bit_map, next_bm, *updptr; + blk_ident *blk, *blk_top, *nextblk; + trans_num ctn, start_db_fmt_tn; + unsigned int len; + int4 blk_prev_version; + srch_hist alt_hist; + trans_num ret_tn = 0; + boolean_t visit_blks; + srch_blk_status bmphist; + cache_rec_ptr_t cr; + enum db_ver ondsk_blkver; + + error_def(ERR_GVKILLFAIL); + + assert(inctn_bmp_mark_free_gtm == inctn_opcode || inctn_bmp_mark_free_mu_reorg == inctn_opcode); + /* Note down the desired_db_format_tn before you start relying on cs_data->fully_upgraded. + * If the db is fully_upgraded, take the optimal path that does not need to read each block being freed. + * But in order to detect concurrent desired_db_format changes, note down the tn (when the last format change occurred) + * before the fully_upgraded check and after having noted down the database current_tn. + * If they are the same, then we are guaranteed no concurrent desired_db_format change occurred. + * If they are not, then fall through to the non-optimal path where each to-be-killed block has to be visited. + * The reason we need to visit every block in case desired_db_format changes is to take care of the case where + * MUPIP REORG DOWNGRADE concurrently changes a block that we are about to free. + */ + start_db_fmt_tn = cs_data->desired_db_format_tn; + visit_blks = (!cs_data->fully_upgraded); /* Local evaluation */ + assert(!visit_blks || (visit_blks && dba_bg == cs_addrs->hdr->acc_meth)); /* must have blks_to_upgrd == 0 for non-BG */ + assert(!dollar_tlevel); /* Should NOT be in TP now */ + blk = &ks->blk[0]; + blk_top = &ks->blk[ks->used]; + if (!visit_blks) + { /* Database has been completely upgraded. Free all blocks in one bitmap as part of one transaction. */ + assert(cs_data->db_got_to_v5_once); /* assert all V4 fmt blocks (including RECYCLED) have space for V5 upgrade */ + inctn_detail.blknum_struct.blknum = 0; /* to indicate no adjustment to "blks_to_upgrd" necessary */ + for ( ; blk < blk_top; blk = nextblk) + { + if (0 != blk->flag) + { + nextblk = blk + 1; + continue; + } + assert(0 < blk->block); + assert((int4)blk->block < cs_addrs->ti->total_blks); + bit_map = ROUND_DOWN2((int)blk->block, BLKS_PER_LMAP); + next_bm = bit_map + BLKS_PER_LMAP; + CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ + /* Scan for the next local bitmap */ + updptr = (block_id *)update_array_ptr; + for (nextblk = blk; + (0 == nextblk->flag) && (nextblk < blk_top) && ((block_id)nextblk->block < next_bm); + ++nextblk) + { + assert((block_id)nextblk->block - bit_map); + *updptr++ = (block_id)nextblk->block - bit_map; + } + len = (unsigned int)((char *)nextblk - (char *)blk); + update_array_ptr = (char *)updptr; + alt_hist.h[0].blk_num = 0; /* need for calls to T_END for bitmaps */ + /* the following assumes SIZEOF(blk_ident) == SIZEOF(int) */ + assert(SIZEOF(blk_ident) == SIZEOF(int)); + *(int *)update_array_ptr = 0; + t_begin(ERR_GVKILLFAIL, UPDTRNS_DB_UPDATED_MASK); + for (;;) + { + ctn = cs_addrs->ti->curr_tn; + /* Need a read fence before reading fields from cs_data as we are reading outside + * of crit and relying on this value to detect desired db format state change. + */ + SHM_READ_MEMORY_BARRIER; + if (start_db_fmt_tn != cs_data->desired_db_format_tn) + { /* Concurrent db format change has occurred. Need to visit every block to be killed + * to determine its block format. Fall through to the non-optimal path below + */ + ret_tn = 0; + break; + } + bmphist.blk_num = bit_map; + if (NULL == (bmphist.buffaddr = t_qread(bmphist.blk_num, (sm_int_ptr_t)&bmphist.cycle, + &bmphist.cr))) + { + t_retry((enum cdb_sc)rdfail_detail); + continue; + } + t_write_map(&bmphist, (uchar_ptr_t)update_array, ctn, -(int4)(nextblk - blk)); + if ((trans_num)0 == (ret_tn = t_end(&alt_hist, NULL, TN_NOT_SPECIFIED))) + continue; + break; + } + if (0 == ret_tn) /* db format change occurred. Fall through to below for loop to visit each block */ + break; + } + } /* for all blocks in the kill_set */ + for ( ; blk < blk_top; blk++) + { /* Database has NOT been completely upgraded. Have to read every block that is going to be freed + * and determine whether it has been upgraded or not. Every block will be freed as part of one + * separate update to the bitmap. This will cause as many transactions as the blocks are being freed. + * But this overhead will be present only as long as the database is not completely upgraded. + * The reason why every block is updated separately is in order to accurately maintain the "blks_to_upgrd" + * counter in the database file-header when the block-freeup phase (2nd phase) of the M-kill proceeds + * concurrently with a MUPIP REORG UPGRADE/DOWNGRADE. If the bitmap is not updated for every block freeup + * then MUPIP REORG UPGRADE/DOWNGRADE should also upgrade/downgrade all blocks in one bitmap as part of + * one transaction (only then will we avoid double-decrement of "blks_to_upgrd" counter by the M-kill as + * well as the MUPIP REORG UPGRADE/DOWNGRADE). That is a non-trivial task as potentially 512 blocks need + * to be modified as part of one non-TP transaction which is unnecessarily making it heavyweight. Compared + * to that, incurring a per-block bitmap update overhead in the M-kill is considered acceptable since this + * will be the case only as long as we are in compatibility mode which should be hopefully not for long. + */ + if (0 != blk->flag) + continue; + assert(0 < blk->block); + assert((int4)blk->block < cs_addrs->ti->total_blks); + assert(!IS_BITMAP_BLK(blk->block)); + bit_map = ROUND_DOWN2((int)blk->block, BLKS_PER_LMAP); + assert(dba_bg == cs_addrs->hdr->acc_meth); + /* We need to check each block we are deleting to see if it is in the format of a previous version. + * If it is, then "csd->blks_to_upgrd" needs to be correspondingly adjusted. + */ + alt_hist.h[0].level = 0; /* Initialize for loop below */ + alt_hist.h[1].blk_num = 0; + CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ + assert((block_id)blk->block - bit_map); + assert(SIZEOF(block_id) == SIZEOF(blk_ident)); + *((block_id *)update_array_ptr) = ((block_id)blk->block - bit_map); + update_array_ptr += SIZEOF(blk_ident); + /* the following assumes SIZEOF(blk_ident) == SIZEOF(int) */ + assert(SIZEOF(blk_ident) == SIZEOF(int)); + *(int *)update_array_ptr = 0; + t_begin(ERR_GVKILLFAIL, UPDTRNS_DB_UPDATED_MASK); + for (;;) + { + ctn = cs_addrs->ti->curr_tn; + alt_hist.h[0].cse = NULL; + alt_hist.h[0].tn = ctn; + alt_hist.h[0].blk_num = blk->block; + if (NULL == (alt_hist.h[0].buffaddr = t_qread(alt_hist.h[0].blk_num, + (sm_int_ptr_t)&alt_hist.h[0].cycle, + &alt_hist.h[0].cr))) + { + t_retry((enum cdb_sc)rdfail_detail); + continue; + } + /* IF csd->db_got_to_v5_once is FALSE + * a) mark the block as FREE (not RECYCLED to avoid confusing MUPIP REORG UPGRADE with a + * block that was RECYCLED right at the time of MUPIP UPGRADE from a V4 to V5 version). + * MUPIP REORG UPGRADE will mark all existing RECYCLED blocks as FREE. + * b) need to write PBLK + * ELSE + * a) mark this block as RECYCLED + * b) no need to write PBLK (it will be written when the block later gets reused). + * ENDIF + * + * Create a cw-set-element with mode gds_t_busy2free that will cause a PBLK to be written in t_end + * (the value csd->db_got_to_v5_once will be checked while holding crit) only in the IF case above. + * At the same time bg_update will NOT be invoked for this cw-set-element so this block will not be + * touched. But the corresponding bitmap block will be updated as part of the same transaction (see + * t_write_map below) to mark this block as FREE or RECYCLED depending on whether csd->db_got_to_v5_once + * is FALSE or TRUE (actual check done in gvcst_map_build and sec_shr_map_build). + */ + t_busy2free(&alt_hist.h[0]); + cr = alt_hist.h[0].cr; + ondsk_blkver = cr->ondsk_blkver; /* Get local copy in case cr->ondsk_blkver changes between + * first and second part of the || + */ + assert((GDSV5 == ondsk_blkver) || (GDSV4 == ondsk_blkver)); + if (GDSVCURR != ondsk_blkver) + inctn_detail.blknum_struct.blknum = blk->block; + else + inctn_detail.blknum_struct.blknum = 0; /* i.e. no adjustment to "blks_to_upgrd" necessary */ + bmphist.blk_num = bit_map; + if (NULL == (bmphist.buffaddr = t_qread(bmphist.blk_num, (sm_int_ptr_t)&bmphist.cycle, + &bmphist.cr))) + { + t_retry((enum cdb_sc)rdfail_detail); + continue; + } + t_write_map(&bmphist, (uchar_ptr_t)update_array, ctn, -1); + if ((trans_num)0 == (ret_tn = t_end(&alt_hist, NULL, TN_NOT_SPECIFIED))) + continue; + break; + } + } /* for all blocks in the kill_set */ + return ret_tn; +} diff --git a/sr_port/gvcst_bmp_mark_free.h b/sr_port/gvcst_bmp_mark_free.h new file mode 100644 index 0000000..7d179ea --- /dev/null +++ b/sr_port/gvcst_bmp_mark_free.h @@ -0,0 +1,31 @@ +/**************************************************************** + * * + * Copyright 2001, 2002 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GVCST_BMP_MARK_FREE_INCLUDED +#define GVCST_BMP_MARK_FREE_INCLUDED + +trans_num gvcst_bmp_mark_free(kill_set *ks); + +#define GVCST_BMP_MARK_FREE(ks, ret_tn, cur_inctn_opcode, new_inctn_opcode, inctn_opcode, cs_addrs) \ +{ /* inctn_opcode is set already by callers (TP/non-TP/reorg) and is not expected to change. save it \ + * before modifying it. actually, the following save and reset of inctn_opcode (done before and after the \ + * call to gvcst_bmp_mark_free()) needs to be done only if JNL_ENABLED(cs_addrs), but since it is not \ + * easy to re-execute the save and reset of inctn_opcode in case t_end() detects a cdb_sc_jnlstatemod \ + * retry code, we choose the easier approach of doing the save and reset unconditionally even though this \ + * approach has an overhead of doing a few assignments even though inctn_opcode might not be used in t_end \ + * (in case JNL_ENABLED is not TRUE at t_end() time). \ + */ \ + assert(inctn_opcode == cur_inctn_opcode); \ + inctn_opcode = new_inctn_opcode; \ + ret_tn = gvcst_bmp_mark_free(ks); \ + inctn_opcode = cur_inctn_opcode; /* restore inctn_opcode */ \ +} +#endif /* GVCST_BMP_MARK_FREE_INCLUDED */ diff --git a/sr_port/gvcst_data.c b/sr_port/gvcst_data.c new file mode 100644 index 0000000..c701dae --- /dev/null +++ b/sr_port/gvcst_data.c @@ -0,0 +1,124 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "cdb_sc.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "copy.h" +#include "filestruct.h" /* needed for jnl.h */ +#include "gdscc.h" /* needed for tp.h */ +#include "jnl.h" /* needed for tp.h */ +#include "gdskill.h" /* needed for tp.h */ +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ + +#include "t_end.h" /* prototypes */ +#include "t_retry.h" +#include "t_begin.h" +#include "gvcst_protos.h" /* for gvcst_rtsib,gvcst_search,gvcst_search_blk,gvcst_data prototype */ + +GBLREF gv_key *gv_currkey; +GBLREF gv_namehead *gv_target; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gd_region *gv_cur_region; +GBLREF uint4 dollar_tlevel; +GBLREF unsigned int t_tries; + +mint gvcst_data(void) +{ + blk_hdr_ptr_t bp; + boolean_t do_rtsib; + enum cdb_sc status; + mint val; + rec_hdr_ptr_t rp; + unsigned short match, rsiz; + srch_blk_status *bh; + srch_hist *rt_history; + sm_uc_ptr_t b_top; + + assert((gv_target->root < cs_addrs->ti->total_blks) || dollar_tlevel); + T_BEGIN_READ_NONTP_OR_TP(ERR_GVDATAFAIL); + assert(t_tries < CDB_STAGNATE || cs_addrs->now_crit); /* we better hold crit in the final retry (TP & non-TP) */ + for (;;) + { + /* The following code is duplicated in gvcst_dataget. Any changes here might need to be reflected there as well */ + rt_history = gv_target->alt_hist; + rt_history->h[0].blk_num = 0; + if (cdb_sc_normal != (status = gvcst_search(gv_currkey, NULL))) + { + t_retry(status); + continue; + } + bh = gv_target->hist.h; + bp = (blk_hdr_ptr_t)bh->buffaddr; + rp = (rec_hdr_ptr_t)(bh->buffaddr + bh->curr_rec.offset); + b_top = bh->buffaddr + bp->bsiz; + match = bh->curr_rec.match; + do_rtsib = FALSE; + if (gv_currkey->end + 1 == match) + { + val = 1; + GET_USHORT(rsiz, &rp->rsiz); + rp = (rec_hdr_ptr_t)((sm_uc_ptr_t)rp + rsiz); + if ((sm_uc_ptr_t)rp > b_top) + { + t_retry(cdb_sc_rmisalign); + continue; + } else if ((sm_uc_ptr_t)rp == b_top) + do_rtsib = TRUE; + else if (rp->cmpc >= gv_currkey->end) + val += 10; + } else if (match >= gv_currkey->end) + val = 10; + else + { + val = 0; + if (rp == (rec_hdr_ptr_t)b_top) + do_rtsib = TRUE; + } + if (do_rtsib && (cdb_sc_endtree != (status = gvcst_rtsib(rt_history, 0)))) + { + if ((cdb_sc_normal != status) || (cdb_sc_normal != (status = gvcst_search_blk(gv_currkey, rt_history->h)))) + { + t_retry(status); + continue; + } + if (rt_history->h[0].curr_rec.match >= gv_currkey->end) + { + assert(1 >= val); + val += 10; + } + } + if (!dollar_tlevel) + { + if ((trans_num)0 == t_end(&gv_target->hist, 0 == rt_history->h[0].blk_num ? NULL : rt_history, + TN_NOT_SPECIFIED)) + continue; + } else + { + status = tp_hist(0 == rt_history->h[0].blk_num ? NULL : rt_history); + if (cdb_sc_normal != status) + { + t_retry(status); + continue; + } + } + INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_data, 1); + return val; + } +} diff --git a/sr_port/gvcst_dataget.c b/sr_port/gvcst_dataget.c new file mode 100644 index 0000000..a56e74b --- /dev/null +++ b/sr_port/gvcst_dataget.c @@ -0,0 +1,146 @@ +/**************************************************************** + * * + * Copyright 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "stringpool.h" +#include "cdb_sc.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "copy.h" +#include "filestruct.h" /* needed for jnl.h */ +#include "gdscc.h" /* needed for tp.h */ +#include "jnl.h" /* needed for tp.h */ +#include "gdskill.h" /* needed for tp.h */ +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ + +#include "t_end.h" /* prototypes */ +#include "t_retry.h" +#include "t_begin.h" +#include "gvcst_protos.h" /* for gvcst_rtsib,gvcst_search,gvcst_search_blk,gvcst_data prototype */ + +GBLREF gv_key *gv_currkey; +GBLREF gv_namehead *gv_target; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gd_region *gv_cur_region; +GBLREF uint4 dollar_tlevel; +GBLREF unsigned int t_tries; +GBLREF uint4 t_err; + +/* This function is the equivalent of invoking gvcst_data & gvcst_get at the same time. + * One crucial difference is that this function does NOT handle restarts by automatically invoking t_retry. + * Instead, it returns the restart code to the caller so that it can handle the restart accordingly. + * This is important in the case of triggers because we do NOT want to call t_retry in case of a implicit tstart + * wrapped gvcst_put or gvcst_kill trigger-invoking update transaction. Additionally, this function assumes + * that it is called always inside of TP (i.e. dollar_tlevel is non-zero). + */ +enum cdb_sc gvcst_dataget(mint *dollar_data, mval *val) +{ + blk_hdr_ptr_t bp; + boolean_t do_rtsib; + enum cdb_sc status; + mint dlr_data; + rec_hdr_ptr_t rp; + unsigned short match, rsiz; + srch_blk_status *bh; + srch_hist *rt_history; + sm_uc_ptr_t b_top; + int key_size, data_len; + uint4 save_t_err; + + error_def(ERR_GVDATAGETFAIL); + error_def(ERR_GVKILLFAIL); + + /* The following code is lifted from gvcst_data. Any changes here might need to be reflected there as well */ + assert(dollar_tlevel); + assert((CDB_STAGNATE > t_tries) || cs_addrs->now_crit); /* we better hold crit in the final retry (TP & non-TP) */ + save_t_err = t_err; + assert(ERR_GVKILLFAIL == save_t_err); /* this function should currently be called only from gvcst_kill */ + t_err = ERR_GVDATAGETFAIL; /* switch t_err to reflect dataget sub-operation (under the KILL operation) */ + /* In case of a failure return, it is ok to return with t_err set to ERR_GVDATAGETFAIL as that gives a better + * picture of exactly where in the transaction the failure occurred. + */ + rt_history = gv_target->alt_hist; + rt_history->h[0].blk_num = 0; + if (cdb_sc_normal != (status = gvcst_search(gv_currkey, NULL))) + return status; + bh = gv_target->hist.h; + bp = (blk_hdr_ptr_t)bh->buffaddr; + rp = (rec_hdr_ptr_t)(bh->buffaddr + bh->curr_rec.offset); + b_top = bh->buffaddr + bp->bsiz; + match = bh->curr_rec.match; + key_size = gv_currkey->end + 1; + do_rtsib = FALSE; + /* Even if key does not exist, return null string in "val". Caller can use dollar_data to distinguish + * whether the key is undefined or defined and set to the null string. + */ + val->mvtype = MV_STR; + val->str.len = 0; + if (key_size == match) + { + dlr_data = 1; + /* the following code is lifted from gvcst_get. any changes here might need to be reflected there as well */ + GET_USHORT(rsiz, &rp->rsiz); + data_len = rsiz + rp->cmpc - SIZEOF(rec_hdr) - key_size; + if ((0 > data_len) || ((sm_uc_ptr_t)rp + rsiz > b_top)) + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_rmisalign1; + return status; + } else + { + ENSURE_STP_FREE_SPACE(data_len); + memcpy(stringpool.free, (sm_uc_ptr_t)rp + rsiz - data_len, data_len); + val->str.addr = (char *)stringpool.free; + val->str.len = data_len; + stringpool.free += data_len; + } + /* --------------------- end code lifted from gvcst_get ---------------------------- */ + rp = (rec_hdr_ptr_t)((sm_uc_ptr_t)rp + rsiz); + if ((sm_uc_ptr_t)rp > b_top) + { + status = cdb_sc_rmisalign; + return status; + } else if ((sm_uc_ptr_t)rp == b_top) + do_rtsib = TRUE; + else if (rp->cmpc >= gv_currkey->end) + dlr_data += 10; + } else if (match >= gv_currkey->end) + dlr_data = 10; + else + { + dlr_data = 0; + if (rp == (rec_hdr_ptr_t)b_top) + do_rtsib = TRUE; + } + if (do_rtsib && (cdb_sc_endtree != (status = gvcst_rtsib(rt_history, 0)))) + { + if ((cdb_sc_normal != status) || (cdb_sc_normal != (status = gvcst_search_blk(gv_currkey, rt_history->h)))) + return status; + if (rt_history->h[0].curr_rec.match >= gv_currkey->end) + { + assert(1 >= dlr_data); + dlr_data += 10; + } + } + status = tp_hist(0 == rt_history->h[0].blk_num ? NULL : rt_history); + if (cdb_sc_normal != status) + return status; + *dollar_data = dlr_data; + t_err = save_t_err; /* restore t_err to what it was at function entry */ + return status; +} diff --git a/sr_port/gvcst_delete_blk.c b/sr_port/gvcst_delete_blk.c new file mode 100644 index 0000000..7a1c084 --- /dev/null +++ b/sr_port/gvcst_delete_blk.c @@ -0,0 +1,188 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gdskill.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdscc.h" +#include "copy.h" +#include "filestruct.h" +#include "jnl.h" +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" +#include "gvcst_blk_build.h" +#include "gvcst_delete_blk.h" + +GBLREF kill_set *kill_set_tail; +GBLREF sgm_info *sgm_info_ptr; +GBLREF uint4 dollar_tlevel; +GBLREF gv_namehead *gv_target; +GBLREF boolean_t horiz_growth; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF unsigned int t_tries; +#ifdef VMS +GBLREF boolean_t tp_has_kill_t_cse; /* cse->mode of kill_t_write or kill_t_create got created in this transaction */ +#endif + +void gvcst_delete_blk(block_id blk, int level, boolean_t committed) +{ + cw_set_element *cse, *old_cse; + kill_set *ks; + off_chain chain; + srch_blk_status *tp_srch_status; + uint4 iter; + ht_ent_int4 *tabent; + DEBUG_ONLY( + boolean_t block_already_in_hist = FALSE; + ) + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + horiz_growth = FALSE; + if (!dollar_tlevel) + ks = kill_set_tail; + else + { + PUT_LONG(&chain, blk); + tp_srch_status = NULL; + if (chain.flag == 1) + tp_get_cw(sgm_info_ptr->first_cw_set, (int)chain.cw_index, &cse); + else + { + if (NULL != (tabent = lookup_hashtab_int4(sgm_info_ptr->blks_in_use, (uint4 *)&blk))) + tp_srch_status = (srch_blk_status *)tabent->value; + cse = tp_srch_status ? tp_srch_status->cse : NULL; + } + if (cse) + { + if (!committed) + { + assert(dollar_tlevel >= cse->t_level); + if (NULL != cse->high_tlevel) + { /* this is possible only if this block is already part of either of the tree paths + * in gv_target->hist or alt_hist (see gvcst_kill.c) and got a newer cse created as + * part of a gvcst_kill_blk on this block in gvcst_kill.c a little before the call + * to gvcst_kill_blk of a parent block which in turn decided to delete this block + * as part of its records that are getting removed. this is a guaranteed restartable + * situation. since gvcst_delete_blk does not return any status, proceed with the + * newer cse and return. the restart will be later detected by tp_hist. + */ + cse = cse->high_tlevel; + DEBUG_ONLY(TREF(donot_commit) |= DONOTCOMMIT_GVCST_DELETE_BLK_CSE_TLEVEL;) + DEBUG_ONLY(block_already_in_hist = TRUE;) + } + assert(!cse->high_tlevel); + if (cse->t_level != dollar_tlevel) + { /* this part of the code is almost similar to that in t_write(), + * any changes in one should be reflected in the other */ + horiz_growth = TRUE; + old_cse = cse; + cse = (cw_set_element *)get_new_free_element(sgm_info_ptr->tlvl_cw_set_list); + memcpy(cse, old_cse, SIZEOF(cw_set_element)); + cse->low_tlevel = old_cse; + cse->high_tlevel = NULL; + old_cse->high_tlevel = cse; + cse->t_level = dollar_tlevel; + assert(2 == (SIZEOF(cse->undo_offset) / SIZEOF(cse->undo_offset[0]))); + assert(2 == (SIZEOF(cse->undo_next_off) / SIZEOF(cse->undo_next_off[0]))); + for (iter = 0; iter < 2; iter++) + cse->undo_next_off[iter] = cse->undo_offset[iter] = 0; + if (old_cse->done) + { + assert(NULL != old_cse->new_buff); + cse->new_buff = (unsigned char *)get_new_free_element(sgm_info_ptr->new_buff_list); + memcpy(cse->new_buff, old_cse->new_buff, ((blk_hdr_ptr_t)old_cse->new_buff)->bsiz); + } else + cse->new_buff = NULL; + assert(!block_already_in_hist); + /* tp_hist (called from gvcst_kill) updates "->cse" fields for all blocks that are + * part of the left or right histories of the M-kill. But this block is not one of + * those. Hence tp_srch_status->cse has to be updated here explicitly. + */ + if (tp_srch_status) + tp_srch_status->cse = (void *)cse; + } + switch (cse->mode) + { + case gds_t_create: + cse->mode = kill_t_create; + VMS_ONLY(tp_has_kill_t_cse = TRUE;) + if (level == 0) + return; + break; + case gds_t_write: + cse->mode = kill_t_write; + VMS_ONLY(tp_has_kill_t_cse = TRUE;) + break; + default: + ; + } + } else + { + switch(cse->mode) + { + case kill_t_create: + VMS_ONLY(assert(tp_has_kill_t_cse)); + if (level == 0) + return; + break; + default: + if (chain.flag) + { + chain.flag = 0; + blk = cse->blk; + } + break; + } + } + } + ks = sgm_info_ptr->kill_set_tail; + if (NULL == ks) /* Allocate first kill set to sgm_info_ptr block */ + { + ks = sgm_info_ptr->kill_set_tail = sgm_info_ptr->kill_set_head = (kill_set *)malloc(SIZEOF(kill_set)); + ks->used = 0; + ks->next_kill_set = NULL; + } + } + while (ks->used >= BLKS_IN_KILL_SET) + { + if (ks->next_kill_set == NULL) + { + ks->next_kill_set = (kill_set *)malloc(SIZEOF(kill_set)); + ks->next_kill_set->used = 0; + ks->next_kill_set->next_kill_set = NULL; + } + ks = kill_set_tail + = ks->next_kill_set; + } + ks->blk[ks->used].level = level; + if (!dollar_tlevel || !chain.flag) + { + assert((CDB_STAGNATE > t_tries) || (blk < cs_addrs->ti->total_blks)); + ks->blk[ks->used].block = blk; + ks->blk[ks->used].flag = 0; + } else + { + ks->blk[ks->used].block = chain.cw_index; + ks->blk[ks->used].flag = chain.flag; + } + ++ks->used; + assert(ks->used <= BLKS_IN_KILL_SET); +} diff --git a/sr_port/gvcst_delete_blk.h b/sr_port/gvcst_delete_blk.h new file mode 100644 index 0000000..bbd38e3 --- /dev/null +++ b/sr_port/gvcst_delete_blk.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GVCST_DELETE_BLK_INCLUDED +#define GVCST_DELETE_BLK_INCLUDED + +void gvcst_delete_blk(block_id blk, int level, boolean_t committed); + +#endif /* GVCST_DELETE_BLK_INCLUDED */ diff --git a/sr_port/gvcst_expand_any_key.c b/sr_port/gvcst_expand_any_key.c new file mode 100644 index 0000000..65ada75 --- /dev/null +++ b/sr_port/gvcst_expand_any_key.c @@ -0,0 +1,162 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* ------------------------------------------------------------------------- +gvcst_expand_any_key.c + Expands key in a block. It can expands a *-key too. + Given block base and record top of the key to be expanded, this will expand the key. + Result is placed in expanded_key. Can expand *=key too. +------------------------------------------------------------------------- */ +#include "mdef.h" + +#include "gtm_string.h" + +#include "cdb_sc.h" +#include "gdsroot.h" +#include "gdskill.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "copy.h" +#include "mu_reorg.h" +#include "filestruct.h" /* for struct RAB type recognition by C compiler before prototype usage in muextr.h */ +#include "muextr.h" + +/* Include prototypes */ +#include "t_qread.h" +#include "mupip_reorg.h" + +GBLREF gd_region *gv_cur_region; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF gv_namehead *gv_target; +GBLREF unsigned int t_tries; +GBLREF unsigned char rdfail_detail; + +/******************************************************************************************* +Input Parameter: + blk_base = Block's base which has the key + rec_top = record top of the record which will be expanded +Output Parameter: + expanded_key = expanded key + rec_size = last record size whic has the key + keylen = key size + keycmpc = key compression cound + hist_ptr = history of blocks read, while expanding a *-key + History excludes the working block from which key is expanded and + includes the blocks read below the current block to expand a *-key + NOTE: hist_ptr.depth will be unchanged +Return: + cdb_sc_normal on success + failure code on concurrency failure + *******************************************************************************************/ +enum cdb_sc gvcst_expand_any_key (sm_uc_ptr_t blk_base, sm_uc_ptr_t rec_top, sm_uc_ptr_t expanded_key, + int *rec_size, int *keylen, int *keycmpc, srch_hist *hist_ptr) +{ + enum cdb_sc status; + unsigned char expanded_star_key[MAX_KEY_SZ]; + unsigned short temp_ushort; + int cur_level; + int star_keycmpc; + int star_keylen; + int star_rec_size; + int tblk_size; + block_id tblk_num; + sm_uc_ptr_t rPtr1, rPtr2, curptr; + + + cur_level = ((blk_hdr_ptr_t)blk_base)->levl; + curptr = blk_base + SIZEOF(blk_hdr); + *rec_size = *keycmpc = *keylen = 0; + while (curptr < rec_top) + { + GET_RSIZ(*rec_size, curptr); + if (0 == cur_level || BSTAR_REC_SIZE != *rec_size) + { + READ_RECORD(cur_level, curptr, *keycmpc, *rec_size, expanded_key, *keylen, status); + if (cdb_sc_normal != status) + { + assert(t_tries < CDB_STAGNATE); + return status; + } + else + { + curptr += *rec_size; + if (curptr >= rec_top) + break; + } + } + else /* a star record in index block */ + { + if (curptr + *rec_size != rec_top || NULL == hist_ptr) + { + assert(t_tries < CDB_STAGNATE); + return cdb_sc_rmisalign; + } + while (0 != cur_level) + { + tblk_size = ((blk_hdr_ptr_t)blk_base)->bsiz; + GET_LONG(tblk_num, blk_base + tblk_size - SIZEOF(block_id)); + if (0 == tblk_num || cs_data->trans_hist.total_blks - 1 < tblk_num) + { + assert(t_tries < CDB_STAGNATE); + return cdb_sc_badlvl; + } + cur_level--; + hist_ptr->h[cur_level].tn = cs_addrs->ti->curr_tn; + if (!(blk_base = t_qread(tblk_num, (sm_int_ptr_t)(&(hist_ptr->h[cur_level].cycle)), + &(hist_ptr->h[cur_level].cr) ))) + { + assert(t_tries < CDB_STAGNATE); + return (enum cdb_sc)rdfail_detail; + } + if (((blk_hdr_ptr_t)blk_base)->levl != cur_level) + { + assert(t_tries < CDB_STAGNATE); + return cdb_sc_badlvl; + } + hist_ptr->h[cur_level].buffaddr = blk_base; + hist_ptr->h[cur_level].blk_num = tblk_num; + hist_ptr->h[cur_level].prev_rec.match = 0; + hist_ptr->h[cur_level].prev_rec.offset = 0; + hist_ptr->h[cur_level].curr_rec.match = 0; + hist_ptr->h[cur_level].curr_rec.offset = 0; + } + tblk_size = ((blk_hdr_ptr_t)blk_base)->bsiz; + /* expand *-key from right most leaf level block of the + sub-tree, of which, the original block is root */ + if (cdb_sc_normal != (status = (gvcst_expand_any_key(blk_base, blk_base + tblk_size, + expanded_star_key, &star_rec_size, &star_keylen, &star_keycmpc, hist_ptr)))) + return status; + if (*keylen + *keycmpc) /* Previous key exists */ + { + GET_CMPC(*keycmpc, expanded_key, &expanded_star_key[0]); + } + memcpy(expanded_key, expanded_star_key, star_keylen + star_keycmpc); + *keylen = star_keylen + star_keycmpc - *keycmpc; + *rec_size = *keylen + *keycmpc + BSTAR_REC_SIZE; + return cdb_sc_normal; + } /* end else if *-record */ + }/* end of "while" loop */ + if (curptr == rec_top) + { + return cdb_sc_normal; + } + else + { + assert(t_tries < CDB_STAGNATE); + return cdb_sc_rmisalign; + } +} + + diff --git a/sr_port/gvcst_expand_free_subtree.c b/sr_port/gvcst_expand_free_subtree.c new file mode 100644 index 0000000..ad27fbb --- /dev/null +++ b/sr_port/gvcst_expand_free_subtree.c @@ -0,0 +1,163 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "cdb_sc.h" /* atleast for cdb_sc_* codes */ +#include "copy.h" /* atleast for the GET_USHORT macros */ +#include "gdsroot.h" /* atleast for gds_file_id used by sgmnt_data in gdsfhead.h */ +#include "gdskill.h" /* atleast for the kill_set and blk_ident structures */ +#include "gdsblk.h" /* atleast for the blk_hdr and rec_hdr structures */ +#include "gtm_facility.h" /* atleast for gdsfhead.h */ +#include "fileinfo.h" /* atleast for gdsfhead.h */ +#include "gdsbt.h" /* atleast for gdsfhead.h */ +#include "gdsfhead.h" /* atleast for cs_addrs, cs_data etc. */ +#include "filestruct.h" /* atleast for the FILE_INFO macro */ +#include "gdscc.h" /* atleast for cw_set_element in tp.h */ +#include "jnl.h" /* atleast for tp.h */ +#include "buddy_list.h" /* atleast for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" /* atleast for off_chain */ +#include "t_qread.h" +#include "gvcst_bmp_mark_free.h" +#include "gvcst_delete_blk.h" +#include "gvcst_kill_sort.h" +#include "gvcst_expand_free_subtree.h" +#include "rc_cpt_ops.h" +#include "wcs_phase2_commit_wait.h" + +GBLREF gd_region *gv_cur_region; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF sgm_info *sgm_info_ptr; +GBLREF uint4 dollar_tlevel; +GBLREF unsigned char rdfail_detail; +GBLREF inctn_opcode_t inctn_opcode; + +void gvcst_expand_free_subtree(kill_set *ks_head) +{ + blk_hdr_ptr_t bp; + blk_ident *ksb; + block_id blk; + boolean_t flush_cache = FALSE, was_crit; + cache_rec_ptr_t cr; + int cnt, cycle; + int4 kill_error, temp_long; + kill_set *ks; + off_chain chain; + rec_hdr_ptr_t rp, rp1, rtop; + uint4 save_dollar_tlevel; + sm_uc_ptr_t temp_buff; + sgmnt_addrs *csa; + sgmnt_data_ptr_t csd; + unsigned short temp_ushort; + trans_num ret_tn; + inctn_opcode_t save_inctn_opcode; + bt_rec_ptr_t bt; + + error_def(ERR_GVKILLFAIL); + + csa = cs_addrs; + csd = cs_data; + /* If ever the following assert is removed, "flush_cache" shouldn't be set to FALSE unconditionally as it is now */ + assert(!csd->dsid); /* see related comment in gvcst_kill before the call to this routine */ + temp_buff = (unsigned char *)malloc(cs_data->blk_size); + for (ks = ks_head; NULL != ks; ks = ks->next_kill_set) + { + for (cnt = 0; cnt < ks->used; ++cnt) + { + ksb = &ks->blk[cnt]; + if (0 != ksb->level) + { + if (!(was_crit = csa->now_crit)) + grab_crit(gv_cur_region); + if (dollar_tlevel && ksb->flag) + { + chain.flag = 1; + chain.next_off = 0; + chain.cw_index = ksb->block; + assert(SIZEOF(chain) == SIZEOF(blk)); + blk = *(block_id *)&chain; + } else + blk = ksb->block; + if (!(bp = (blk_hdr_ptr_t)t_qread(blk, (sm_int_ptr_t)&cycle, &cr))) + { /* This should have worked because t_qread was done in crit */ + free(temp_buff); + rts_error(VARLSTCNT(4) ERR_GVKILLFAIL, 2, 1, &rdfail_detail); + } + if (NULL != cr) + { /* It is possible that t_qread returned a buffer from first_tp_srch_status. + * In that case, t_qread does not wait for cr->in_tend to be zero since + * there is no need to wait as long as all this is done inside of the TP + * transaction. But the gvcst_expand_free_subtree logic is special in that it + * is done AFTER the TP transaction is committed but with dollar_tlevel still + * set to non-zero. So it is possible that cr->in_tend is non-zero in this case. + * Hence we need to check if cr->in_tend is non-zero and if so wait for commit + * to complete before scanning the block for child-block #s to free. + */ + if (dollar_tlevel && cr->in_tend) + wcs_phase2_commit_wait(csa, cr); + assert(!cr->twin || cr->bt_index); + assert((NULL == (bt = bt_get(blk))) + || (CR_NOTVALID == bt->cache_index) + || (cr == (cache_rec_ptr_t)GDS_REL2ABS(bt->cache_index)) && (0 == cr->in_tend)); + } + memcpy(temp_buff, bp, bp->bsiz); + if (!was_crit) + rel_crit(gv_cur_region); + for (rp = (rec_hdr_ptr_t)(temp_buff + SIZEOF(blk_hdr)), + rtop = (rec_hdr_ptr_t)(temp_buff + ((blk_hdr_ptr_t)temp_buff)->bsiz); + rp < rtop; + rp = rp1) + { + GET_USHORT(temp_ushort, &rp->rsiz); + rp1 = (rec_hdr_ptr_t)((sm_uc_ptr_t)rp + temp_ushort); + if ((sm_uc_ptr_t)rp1 < (sm_uc_ptr_t)(rp + 1) + SIZEOF(block_id)) + { /* This should have worked because a local copy was made while crit */ + assert(FALSE); + kill_error = cdb_sc_rmisalign; + free(temp_buff); + rts_error(VARLSTCNT(4) ERR_GVKILLFAIL, 2, 1, &kill_error); + } + GET_LONG(temp_long, (block_id_ptr_t)((sm_uc_ptr_t)rp1 - SIZEOF(block_id))); + if (dollar_tlevel) + { + chain = *(off_chain *)&temp_long; + if ((1 == chain.flag) && ((int)chain.cw_index >= sgm_info_ptr->cw_set_depth)) + { + assert(sgm_info_ptr->tp_csa == cs_addrs); + GTMASSERT; + } + assert(chain.flag || temp_long < csa->ti->total_blks); + } + gvcst_delete_blk(temp_long, ksb->level - 1, TRUE); + if ((1 == ksb->level) && !dollar_tlevel && cs_data->dsid && !flush_cache) + rc_cpt_entry(temp_long); /* Invalidate single block */ + } + ksb->level = 0; + } else + { + if (!dollar_tlevel && cs_data->dsid && !flush_cache) + rc_cpt_entry(ksb->block); + } + } + gvcst_kill_sort(ks); + save_dollar_tlevel = dollar_tlevel; + assert(1 >= dollar_tlevel); + dollar_tlevel = 0; /* temporarily for gvcst_bmp_mark_free */ + GVCST_BMP_MARK_FREE(ks, ret_tn, inctn_invalid_op, inctn_bmp_mark_free_gtm, inctn_opcode, csa) + dollar_tlevel = save_dollar_tlevel; + } + free(temp_buff); +} + diff --git a/sr_port/gvcst_expand_free_subtree.h b/sr_port/gvcst_expand_free_subtree.h new file mode 100644 index 0000000..8c8c6f8 --- /dev/null +++ b/sr_port/gvcst_expand_free_subtree.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GVCST_EXPAND_FREE_SUBTREE_INCLUDED +#define GVCST_EXPAND_FREE_SUBTREE_INCLUDED + +void gvcst_expand_free_subtree(kill_set *ks_head); + +#endif /* GVCST_EXPAND_FREE_SUBTREE_INCLUDED */ diff --git a/sr_port/gvcst_expand_key.c b/sr_port/gvcst_expand_key.c new file mode 100644 index 0000000..5db0529 --- /dev/null +++ b/sr_port/gvcst_expand_key.c @@ -0,0 +1,80 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "cdb_sc.h" +#include "copy.h" +#include "gvcst_expand_key.h" + +GBLREF unsigned int t_tries; + +enum cdb_sc gvcst_expand_key(blk_hdr_ptr_t bp, int4 rec_top, gv_key *key) +{ + unsigned short temp_ushort; + int4 r_offset; + rec_hdr_ptr_t rp, rtop; + sm_uc_ptr_t p; + unsigned char *kbase, *kend, *kprv, *ktop, last, current; + + assert(SIZEOF(rec_hdr) <= SIZEOF(blk_hdr)); + kbase = kend = key->base; + ktop = &key->base[key->top]; + rp = (rec_hdr_ptr_t)bp; + rtop = (rec_hdr_ptr_t)((sm_uc_ptr_t)bp + rec_top); + for (r_offset = SIZEOF(blk_hdr); ; GET_USHORT(temp_ushort, &rp->rsiz), r_offset = temp_ushort) + { + /* WARNING: Assumes that SIZEOF(rec_hdr) <= SIZEOF(blk_hdr) */ + if (r_offset < SIZEOF(rec_hdr)) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_r2small; + } + rp = (rec_hdr_ptr_t)((sm_uc_ptr_t)rp + r_offset); + if (rp > rtop) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_rmisalign; + } + current = 1; + kend = kbase + rp->cmpc; + p = (sm_uc_ptr_t)(rp + 1); + for (;;) + { + if (kend >= ktop) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_keyoflow; + } + last = current; + *kend++ = current = *p++; + if (last == 0) + { + if (current == 0) + break; + else + kprv = kend - 1; /* start of last key */ + } + } + if (rp == rtop) + { + key->end = kend - kbase - 1; + key->prev = kprv - kbase; + return cdb_sc_normal; + } + kprv = kend - 1; /* start of last key */ + } +} diff --git a/sr_port/gvcst_expand_key.h b/sr_port/gvcst_expand_key.h new file mode 100644 index 0000000..1cc696c --- /dev/null +++ b/sr_port/gvcst_expand_key.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GVCST_EXPAND_KEY_INCLUDED +#define GVCST_EXPAND_KEY_INCLUDED + +enum cdb_sc gvcst_expand_key(blk_hdr_ptr_t bp, int4 rec_top, gv_key *key); + +#endif /* GVCST_EXPAND_KEY_INCLUDED */ diff --git a/sr_port/gvcst_gblmod.c b/sr_port/gvcst_gblmod.c new file mode 100644 index 0000000..0a6858f --- /dev/null +++ b/sr_port/gvcst_gblmod.c @@ -0,0 +1,78 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" /* needed for jnl.h */ +#include "gdscc.h" /* needed for tp.h */ +#include "jnl.h" /* needed for tp.h */ +#include "gdskill.h" /* needed for tp.h */ +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ + +#include "t_end.h" /* prototypes */ +#include "t_retry.h" +#include "t_begin.h" +#include "gvcst_protos.h" /* for gvcst_gblmod,gvcst_search prototype */ + +GBLREF gv_namehead *gv_target; +GBLREF gv_key *gv_currkey; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gd_region *gv_cur_region; +GBLREF uint4 dollar_tlevel; +GBLREF unsigned int t_tries; + +bool gvcst_gblmod(mval *v) +{ + bool gblmod; + enum cdb_sc status; + int key_size; + + T_BEGIN_READ_NONTP_OR_TP(ERR_GBLMODFAIL); + assert(t_tries < CDB_STAGNATE || cs_addrs->now_crit); /* we better hold crit in the final retry (TP & non-TP) */ + for (;;) + { + gblmod = TRUE; + if (cdb_sc_normal == (status = gvcst_search(gv_currkey, NULL))) + { + VMS_ONLY( + if (cs_addrs->hdr->resync_tn >= ((blk_hdr_ptr_t)gv_target->hist.h[0].buffaddr)->tn) + gblmod = FALSE; + ) + UNIX_ONLY( + if (cs_addrs->hdr->zqgblmod_tn > ((blk_hdr_ptr_t)gv_target->hist.h[0].buffaddr)->tn) + gblmod = FALSE; + ) + if (!dollar_tlevel) + { + if ((trans_num)0 == t_end(&gv_target->hist, NULL, TN_NOT_SPECIFIED)) + continue; + } else + { + status = tp_hist(NULL); + if (cdb_sc_normal != status) + { + t_retry(status); + continue; + } + } + return gblmod; + } + t_retry(status); + } +} diff --git a/sr_port/gvcst_get.c b/sr_port/gvcst_get.c new file mode 100644 index 0000000..f578803 --- /dev/null +++ b/sr_port/gvcst_get.c @@ -0,0 +1,141 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "stringpool.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "cdb_sc.h" +#include "copy.h" +#include "filestruct.h" /* needed for jnl.h */ +#include "gdscc.h" /* needed for tp.h */ +#include "jnl.h" /* needed for tp.h */ +#include "gdskill.h" /* needed for tp.h */ +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ + +#include "t_end.h" /* prototypes */ +#include "t_retry.h" +#include "t_begin.h" +#include "gvcst_protos.h" /* for gvcst_search,gvcst_get prototype */ + +GBLREF gv_namehead *gv_target; +GBLREF gv_key *gv_currkey; +GBLREF spdesc stringpool; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gd_region *gv_cur_region; +GBLREF uint4 dollar_tlevel; +GBLREF unsigned int t_tries; + +boolean_t gvcst_get(mval *v) +{ + blk_hdr_ptr_t bp; + enum cdb_sc status; + int key_size, data_len; + rec_hdr_ptr_t rp; + sm_uc_ptr_t b_top; + srch_blk_status *bh; + unsigned short rsiz; +# ifdef DEBUG + boolean_t in_op_gvget_lcl; +# endif + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + DEBUG_ONLY( + /* Store global variable in_op_gvget in a local variable and reset the global right away to ensure that the global + * value does not incorrectly get carried over to the next call of gvcst_get (e.g. it if was from "op_fngvget"). + */ + in_op_gvget_lcl = TREF(in_op_gvget); + TREF(in_op_gvget) = FALSE; + ) + T_BEGIN_READ_NONTP_OR_TP(ERR_GVGETFAIL); + assert(t_tries < CDB_STAGNATE || cs_addrs->now_crit); /* we better hold crit in the final retry (TP & non-TP) */ + for (;;) + { + if (cdb_sc_normal == (status = gvcst_search(gv_currkey, NULL))) + { + bh = gv_target->hist.h; + if ((key_size = gv_currkey->end + 1) == bh->curr_rec.match) + { + /* The following code is duplicated in gvcst_dataget. Any changes here might need + * to be reflected there as well. + */ + bp = (blk_hdr_ptr_t)bh->buffaddr; + b_top = bh->buffaddr + bp->bsiz; + rp = (rec_hdr_ptr_t)(bh->buffaddr + bh->curr_rec.offset); + GET_USHORT(rsiz, &rp->rsiz); + data_len = rsiz + rp->cmpc - SIZEOF(rec_hdr) - key_size; + if ((0 > data_len) || ((sm_uc_ptr_t)rp + rsiz > b_top)) + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_rmisalign1; + } else + { + ENSURE_STP_FREE_SPACE(data_len); + assert(stringpool.top - stringpool.free >= data_len); + memcpy(stringpool.free, (sm_uc_ptr_t)rp + rsiz - data_len, data_len); + if (!dollar_tlevel) + { + if ((trans_num)0 == t_end(&gv_target->hist, NULL, TN_NOT_SPECIFIED)) + continue; + } else + { + status = tp_hist(NULL); + if (cdb_sc_normal != status) + { + t_retry(status); + continue; + } + } + v->mvtype = MV_STR; + v->str.addr = (char *)stringpool.free; + v->str.len = data_len; + stringpool.free += data_len; + INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_get, 1); + return TRUE; + } + } else + { + DEBUG_ONLY(TREF(ready2signal_gvundef) = in_op_gvget_lcl;) + if (!dollar_tlevel) + { + if ((trans_num)0 == t_end(&gv_target->hist, NULL, TN_NOT_SPECIFIED)) + { + assert(FALSE == TREF(ready2signal_gvundef)); /* t_end should have reset this */ + continue; + } + } else + { + status = tp_hist(NULL); + if (cdb_sc_normal != status) + { + assert(FALSE == TREF(ready2signal_gvundef)); /* tp_hist should have reset this */ + t_retry(status); + continue; + } + } + assert(FALSE == TREF(ready2signal_gvundef)); /* t_end/tp_hist should have reset this up front */ + INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_get, 1); + return FALSE; + } + } + t_retry(status); + } +} diff --git a/sr_port/gvcst_incr.c b/sr_port/gvcst_incr.c new file mode 100644 index 0000000..c993abb --- /dev/null +++ b/sr_port/gvcst_incr.c @@ -0,0 +1,59 @@ +/**************************************************************** + * * + * Copyright 2004, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gdsroot.h" /* needed for gdsfhead.h */ +#include "gdsblk.h" /* needed for gdsfhead.h */ +#include "gtm_facility.h" /* needed for gdsfhead.h */ +#include "fileinfo.h" /* needed for gdsfhead.h */ +#include "gdsbt.h" /* needed for gdsfhead.h */ +#include "gdsfhead.h" /* needed for gvcst_protos.h */ +#include "gdsblkops.h" +#include "filestruct.h" +#include "jnl.h" +#include "gdscc.h" /* needed for tp.h */ +#include "gdskill.h" /* needed for tp.h */ +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" + +#include "mvalconv.h" /* for i2mval prototype for the MV_FORCE_MVAL macro */ +#include "gvcst_protos.h" /* for gvcst_incr prototype */ + +GBLREF boolean_t in_gvcst_incr; +GBLREF mval increment_delta_mval; +GBLREF mval *post_incr_mval; +GBLREF sgm_info *sgm_info_ptr; + +void gvcst_incr(mval *increment, mval *result) +{ + assert(!in_gvcst_incr); + assert(MV_IS_NUMERIC(increment)); /* op_gvincr or gtcmtr_increment should have done the MV_FORCE_NUM before calling */ + in_gvcst_incr = TRUE; + post_incr_mval = result; + /* it is possible (due to some optimizations in the compiler) that both the input parameters "increment" and "result" + * point to the same mval. if we pass "increment" and "result" as they are to gvcst_put, it is possible due to the code + * flow that gvcst_put needs to read the value of "increment" after it is done modifying "result" (it can do this by + * changing the global variable "post_incr_mval"). in this case it will read a bad value of "increment" (since it will + * now be reading the modified value of "result"). therefore we do not pass them as they are. Instead, since we are + * interested in only the numeric value of "increment", we take a copy of "increment" into an mval ("increment_delta_mval") + * and force it to be numeric. It is the address of this mval that is passed to gvcst_put. Any changes to "post_incr_mval" + * will not affect this mval so it is safe to read this anytime in gvcst_put. The mval "increment_delta_mval" is also + * used in gvincr_recompute_upd_array. + */ + increment_delta_mval = *increment; + /* Since we should be caring about just the numeric part, nullify the string part of the mval */ + increment_delta_mval.str.len = 0; + gvcst_put(&increment_delta_mval); + assert(!in_gvcst_incr); /* should have been reset by gvcst_put */ + in_gvcst_incr = FALSE; /* just in case it is not reset already by gvcst_put */ +} diff --git a/sr_port/gvcst_init.c b/sr_port/gvcst_init.c new file mode 100644 index 0000000..f7d6ee6 --- /dev/null +++ b/sr_port/gvcst_init.c @@ -0,0 +1,695 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include /* for offsetof macro */ + +#include "gtm_string.h" +#include "gtm_time.h" + +#include "cdb_sc.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsblk.h" +#include "gdskill.h" +#include "gdscc.h" +#include "min_max.h" /* needed for gdsblkops.h */ +#include "gdsblkops.h" +#include "filestruct.h" +#include "iosp.h" +#include "jnl.h" +#include "hashtab_int4.h" /* needed for tp.h */ +#include "buddy_list.h" /* needed for tp.h */ +#include "tp.h" +#include "gtm_stdlib.h" /* for ATOI */ +#include "cryptdef.h" +#include "mlkdef.h" +#include "error.h" +#include "gt_timer.h" +#include "gtmimagename.h" +#include "trans_log_name.h" +#include "gtm_logicals.h" +#include "dbfilop.h" +#include "set_num_additional_processors.h" +#include "have_crit.h" +#include "t_retry.h" +#include "dpgbldir.h" +#include "longset.h" /* needed for cws_insert.h */ +#include "cws_insert.h" /* for CWS_INIT macro */ +#include "gvcst_protos.h" /* for gvcst_init,gvcst_init_sysops,gvcst_tp_init prototype */ +#include "compswap.h" +#include "send_msg.h" +#include "targ_alloc.h" /* for "targ_free" prototype */ +#include "hashtab_mname.h" +#include "process_gvt_pending_list.h" +#include "gtmmsg.h" + +#ifdef GTM_FD_TRACE +#include "gtm_dbjnl_dupfd_check.h" +#endif + +GBLREF gd_region *gv_cur_region, *db_init_region; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_addrs *cs_addrs_list; +GBLREF boolean_t gtcm_connection; +GBLREF bool licensed; +GBLREF int4 lkid; +GBLREF char *update_array, *update_array_ptr; +GBLREF uint4 update_array_size, cumul_update_array_size; +GBLREF ua_list *first_ua, *curr_ua; +GBLREF short crash_count; +GBLREF uint4 dollar_tlevel; +GBLREF jnl_format_buffer *non_tp_jfb_ptr; +GBLREF unsigned char *non_tp_jfb_buff_ptr; +GBLREF boolean_t mupip_jnl_recover; +GBLREF buddy_list *global_tlvl_info_list; +GBLREF int4 tprestart_syslog_limit; +GBLREF tp_region *tp_reg_free_list; /* Ptr to list of tp_regions that are unused */ +GBLREF tp_region *tp_reg_list; /* Ptr to list of tp_regions for this transaction */ +GBLREF unsigned int t_tries; +GBLREF struct_jrec_tcom tcom_record; +GBLREF boolean_t tp_in_use; +GBLREF uint4 region_open_count; +GBLREF sm_uc_ptr_t reformat_buffer; +GBLREF int reformat_buffer_len; +GBLREF volatile int reformat_buffer_in_use; /* used only in DEBUG mode */ +GBLREF volatile int4 fast_lock_count; +GBLREF boolean_t new_dbinit_ipc; +GBLREF gvt_container *gvt_pending_list; +GBLREF boolean_t dse_running; +GBLREF jnl_gbls_t jgbl; + +LITREF char gtm_release_name[]; +LITREF int4 gtm_release_name_len; + +void assert_jrec_member_offsets(void) +{ + assert(REAL_JNL_HDR_LEN % DISK_BLOCK_SIZE == 0); + assert(JNL_HDR_LEN % DISK_BLOCK_SIZE == 0); + /* We currently assume that the journal file header size is aligned relative to the filesystem block size. + * which is currently assumed to be a 2-power (e.g. 512 bytes, 1K, 2K, 4K etc.) but never more than 64K + * (MAX_IO_BLOCK_SIZE). Given this, we keep the journal file header size at 64K for Unix and 512-byte aligned + * for VMS. This way any process updating the file header will hold crit and do aligned writes. Any process + * writing the journal file data (journal records) on disk will hold the qio lock and can safely do so without + * ever touching the journal file header area. If ever MAX_IO_BLOCK_SIZE changes (say because some filesystem + * block size changes to 128K) such that JNL_HDR_LEN is no longer aligned to that, we want to know hence this assert. + */ + assert(JNL_HDR_LEN % MAX_IO_BLOCK_SIZE == 0); + assert(REAL_JNL_HDR_LEN == SIZEOF(jnl_file_header)); + UNIX_ONLY(assert(REAL_JNL_HDR_LEN <= JNL_HDR_LEN);) + VMS_ONLY(assert(REAL_JNL_HDR_LEN == JNL_HDR_LEN);) + assert(JNL_HDR_LEN == JNL_FILE_FIRST_RECORD); + assert(DISK_BLOCK_SIZE >= PINI_RECLEN + EPOCH_RECLEN + PFIN_RECLEN + EOF_RECLEN); + assert((JNL_ALLOC_MIN * DISK_BLOCK_SIZE) > JNL_HDR_LEN); + /* Following assert is for JNL_FILE_TAIL_PRESERVE macro in tp.h */ + assert(PINI_RECLEN >= EPOCH_RECLEN && PINI_RECLEN >= PFIN_RECLEN && PINI_RECLEN >= EOF_RECLEN); + /* jnl_string structure has a 8-bit nodeflags field and a 24-bit length field. In some cases, this is + * used as a 32-bit length field (e.g. in the value part of the SET record or ZTWORMHOLE record). These + * usages treat the 32-bits as a jnl_str_len_t type and access it directly. Hence the requirement that + * jnl_str_len_t be the same size as 32-bits and also the same as the offset to the "text" member. + * If this assert fails, all places that reference jnl_str_len_t need to be revisited. + */ + assert(SIZEOF(jnl_str_len_t) == SIZEOF(uint4)); + assert(SIZEOF(jnl_str_len_t) == offsetof(jnl_string, text[0])); + /* since time in jnl record is a uint4, and since JNL_SHORT_TIME expects time_t, we better ensure they are same. + * A change in the size of time_t would mean a redesign of the fields. */ + + assert(SIZEOF(time_t) == GTM64_ONLY(SIZEOF(gtm_int8)) NON_GTM64_ONLY(SIZEOF(int4))); + + /* Make sure all jnl_seqno fields start at same offset. mur_output_record and others rely on this. */ + assert(offsetof(struct_jrec_null, jnl_seqno) == offsetof(struct_jrec_upd, token_seq.jnl_seqno)); + assert(offsetof(struct_jrec_null, jnl_seqno) == offsetof(struct_jrec_epoch, jnl_seqno)); + assert(offsetof(struct_jrec_null, jnl_seqno) == offsetof(struct_jrec_eof, jnl_seqno)); + assert(offsetof(struct_jrec_null, jnl_seqno) == offsetof(struct_jrec_tcom, token_seq.jnl_seqno)); + assert(offsetof(struct_jrec_null, jnl_seqno) == offsetof(struct_jrec_ztworm, token_seq.jnl_seqno)); + + assert(offsetof(struct_jrec_ztcom, token) == offsetof(struct_jrec_upd, token_seq)); + /* Make sure all jnl_seqno and token fields start at 8-byte boundary */ + assert(offsetof(struct_jrec_upd, token_seq.jnl_seqno) == + (ROUND_UP(offsetof(struct_jrec_upd, token_seq.jnl_seqno), SIZEOF(seq_num)))); + assert(offsetof(struct_jrec_tcom, token_seq.jnl_seqno) == + (ROUND_UP(offsetof(struct_jrec_tcom, token_seq.jnl_seqno), SIZEOF(seq_num)))); + assert(offsetof(struct_jrec_null, jnl_seqno) == + (ROUND_UP(offsetof(struct_jrec_null, jnl_seqno), SIZEOF(seq_num)))); + assert(offsetof(struct_jrec_epoch, jnl_seqno) == + (ROUND_UP(offsetof(struct_jrec_epoch, jnl_seqno), SIZEOF(seq_num)))); + assert(offsetof(struct_jrec_eof, jnl_seqno) == + (ROUND_UP(offsetof(struct_jrec_eof, jnl_seqno), SIZEOF(seq_num)))); + /* All fixed size records must be multiple of 8-byte */ + assert(TCOM_RECLEN == (ROUND_UP(SIZEOF(struct_jrec_tcom), JNL_REC_START_BNDRY))); + assert(ZTCOM_RECLEN == (ROUND_UP(SIZEOF(struct_jrec_ztcom), JNL_REC_START_BNDRY))); + assert(INCTN_RECLEN == (ROUND_UP(SIZEOF(struct_jrec_inctn), JNL_REC_START_BNDRY))); + assert(PINI_RECLEN == (ROUND_UP(SIZEOF(struct_jrec_pini), JNL_REC_START_BNDRY))); + assert(PFIN_RECLEN == (ROUND_UP(SIZEOF(struct_jrec_pfin), JNL_REC_START_BNDRY))); + assert(NULL_RECLEN == (ROUND_UP(SIZEOF(struct_jrec_null), JNL_REC_START_BNDRY))); + assert(EPOCH_RECLEN == (ROUND_UP(SIZEOF(struct_jrec_epoch), JNL_REC_START_BNDRY))); + assert(EOF_RECLEN == (ROUND_UP(SIZEOF(struct_jrec_eof), JNL_REC_START_BNDRY))); + /* Assumption about the structures in code */ + assert(0 == MIN_ALIGN_RECLEN % JNL_REC_START_BNDRY); + assert(SIZEOF(uint4) == SIZEOF(jrec_suffix)); + assert((MAX_JNL_REC_SIZE - MAX_LOGI_JNL_REC_SIZE) > MIN_PBLK_RECLEN); + assert((DISK_BLOCK_SIZE * JNL_DEF_ALIGNSIZE) >= MAX_JNL_REC_SIZE);/* default alignsize supports max jnl record length */ + assert(MAX_DB_BLK_SIZE < MAX_JNL_REC_SIZE); /* Ensure a PBLK record can accommodate a full GDS block */ + assert(MAX_JNL_REC_SIZE <= (1 << 24)); + /* Ensure that the 24-bit length field in the journal record can accommodate the maximum journal record size */ + assert(tcom_record.prefix.forwptr == tcom_record.suffix.backptr); + assert(TCOM_RECLEN == tcom_record.suffix.backptr); + assert(SIZEOF(token_split_t) == SIZEOF(token_build)); /* Required for TOKEN_SET macro */ +} + +void gvcst_init(gd_region *greg) +{ + sgmnt_addrs *csa, *prevcsa, *regcsa; + sgmnt_data_ptr_t csd, temp_cs_data; + char cs_data_buff[ROUND_UP(SGMNT_HDR_LEN, DISK_BLOCK_SIZE)]; + uint4 segment_update_array_size; + file_control *fc; + gd_region *prev_reg, *reg_top; +#ifdef DEBUG + cache_rec_ptr_t cr; + bt_rec_ptr_t bt; + blk_ident tmp_blk; +#endif + mstr log_nam, trans_log_nam; + char trans_buff[MAX_FN_LEN + 1]; + static int4 first_time = TRUE; + unique_file_id *greg_fid, *reg_fid; + gd_addr *addr_ptr; + tp_region *tr; + ua_list *tmp_ua; + time_t curr_time; + uint4 curr_time_uint4, next_warn_uint4; + unsigned int minus1 = (unsigned)-1; + enum db_acc_method greg_acc_meth; + ht_ent_mname *tabent, *topent, *stayent; + gv_namehead *gvt, *gvt_stay; + gvnh_reg_t *gvnh_reg; + hash_table_mname *table; + boolean_t added, first_wasopen; + intrpt_state_t save_intrpt_ok_state; + + error_def(ERR_BADDBVER); + error_def(ERR_DBCREINCOMP); + error_def(ERR_DBFLCORRP); + error_def(ERR_DBNOTGDS); + error_def(ERR_DBVERPERFWARN1); + error_def(ERR_DBVERPERFWARN2); + error_def(ERR_MMNODYNUPGRD); + + assert(!jgbl.forw_phase_recovery); + CWS_INIT; /* initialize the cw_stagnate hash-table */ + /* check the header design assumptions */ + assert(SIZEOF(th_rec) == (SIZEOF(bt_rec) - SIZEOF(bt->blkque))); + assert(SIZEOF(cache_rec) == (SIZEOF(cache_state_rec) + SIZEOF(cr->blkque))); + DEBUG_ONLY(assert_jrec_member_offsets();) + set_num_additional_processors(); + + DEBUG_ONLY( + /* Note that the "block" member in the blk_ident structure in gdskill.h has 28 bits. + * Currently, the maximum number of blocks is 2**28. If ever this increases, something + * has to be correspondingly done to the "block" member to increase its capacity. + * The following assert checks that we always have space in the "block" member + * to represent a GDS block number. + */ + tmp_blk.block = minus1; + assert(MAXTOTALBLKS_MAX - 1 <= tmp_blk.block); + ) + /* TH_BLOCK is currently a hardcoded constant as basing it on the offsetof macro does not work with the VMS compiler. + * Therefore assert that TH_BLOCK points to the 512-byte block where the "trans_hist" member lies in the fileheader. + */ + assert(DIVIDE_ROUND_UP(offsetof(sgmnt_data, trans_hist), DISK_BLOCK_SIZE) == TH_BLOCK); + if (TRUE == first_time) + { + log_nam.addr = GTM_TPRESTART_LOG_LIMIT; + log_nam.len = STR_LIT_LEN(GTM_TPRESTART_LOG_LIMIT); + if (SS_NORMAL == TRANS_LOG_NAME(&log_nam, &trans_log_nam, trans_buff, SIZEOF(trans_buff), do_sendmsg_on_log2long)) + { + tprestart_syslog_limit = ATOI(trans_log_nam.addr); + if (0 > tprestart_syslog_limit) + tprestart_syslog_limit = 0; + } + log_nam.addr = GTM_TPRESTART_LOG_DELTA; + log_nam.len = STR_LIT_LEN(GTM_TPRESTART_LOG_DELTA); + if (SS_NORMAL == TRANS_LOG_NAME(&log_nam, &trans_log_nam, trans_buff, SIZEOF(trans_buff), do_sendmsg_on_log2long)) + { + tprestart_syslog_delta = ATOI(trans_log_nam.addr); + if (0 > tprestart_syslog_delta) + tprestart_syslog_delta = MAXPOSINT4; + } + first_time = FALSE; + } + if ((prev_reg = dbfilopn(greg)) != greg) + { + if (NULL == prev_reg || (gd_region *)-1L == prev_reg) /* (gd_region *)-1 == prev_reg => cm region open attempted */ + return; + /* Found same database already open - prev_reg contains addr of originally openned region */ + greg->dyn.addr->file_cntl = prev_reg->dyn.addr->file_cntl; + memcpy(greg->dyn.addr->fname, prev_reg->dyn.addr->fname, prev_reg->dyn.addr->fname_len); + greg->dyn.addr->fname_len = prev_reg->dyn.addr->fname_len; + csa = (sgmnt_addrs *)&FILE_INFO(greg)->s_addrs; + PROCESS_GVT_PENDING_LIST(greg, csa, gvt_pending_list); + csd = csa->hdr; + if (NULL == csa->gvt_hashtab) + { /* Already have another region that points to the same physical database file as this one. + * Since two regions point to the same physical file, start maintaining a list of all global variable + * names whose gv_targets have already been allocated on behalf of the current database file. + * Future targ_allocs will check this list before they allocate (to avoid duplicate allocations). + */ + csa->gvt_hashtab = (hash_table_mname *)malloc(SIZEOF(hash_table_mname)); + init_hashtab_mname(csa->gvt_hashtab, 0, HASHTAB_NO_COMPACT, HASHTAB_NO_SPARE_TABLE); + assert(1 == csa->regcnt); + first_wasopen = TRUE; + } else + first_wasopen = FALSE; + for (addr_ptr = get_next_gdr(NULL); addr_ptr; addr_ptr = get_next_gdr(addr_ptr)) + { + table = addr_ptr->tab_ptr; + for (tabent = table->base, topent = tabent + table->size; tabent < topent; tabent++) + { + if (HTENT_VALID_MNAME(tabent, gvnh_reg_t, gvnh_reg)) + { /* Check if the gvt's region is the current region. + * If so add gvt's variable name into the csa hashtable. + */ + gvt = gvnh_reg->gvt; + assert((gvnh_reg->gd_reg != greg) || (csa == gvt->gd_csa)); + /* If this is the first time a was_open region is happening for this csa, then + * we want to merge gv_targets from both the regions into csa->gvt_hashtab. For + * all future was_open cases, we want only to add gv_targets from the was_open region. + */ + if (first_wasopen && (csa == gvt->gd_csa) || !first_wasopen && (gvnh_reg->gd_reg == greg)) + { /* Add gv_target into csa->gvt_hashtab */ + added = add_hashtab_mname(csa->gvt_hashtab, &gvt->gvname, gvt, &stayent); + assert(!added || (1 <= gvt->regcnt)); + if (!added) + { /* Entry already present. Increment gvt->regcnt. + * If NOISOLATION status differs between the two, + * choose the more pessimistic one. + */ + gvt_stay = (gv_namehead *)stayent->value; + assert(gvt_stay != gvt); + if (FALSE == gvt->noisolation) + gvt_stay->noisolation = FALSE; + assert(1 <= gvt_stay->regcnt); + /* Now make gvnh_reg->gvt point to gvt_stay (instead of gvt) */ + gvt_stay->regcnt++; + gvt->regcnt--; + gvnh_reg->gvt = gvt_stay; + targ_free(gvt); + } + } + } + } + } + greg->max_rec_size = csd->max_rec_size; + greg->max_key_size = csd->max_key_size; + greg->null_subs = csd->null_subs; + greg->std_null_coll = csd->std_null_coll; + greg->jnl_state = csd->jnl_state; + greg->jnl_file_len = csd->jnl_file_len; /* journal file name length */ + memcpy(greg->jnl_file_name, csd->jnl_file_name, greg->jnl_file_len); /* journal file name */ + greg->jnl_alq = csd->jnl_alq; + greg->jnl_deq = csd->jnl_deq; + greg->jnl_buffer_size = csd->jnl_buffer_size; + greg->jnl_before_image = csd->jnl_before_image; + greg->open = TRUE; + greg->opening = FALSE; + greg->was_open = TRUE; + assert(1 <= csa->regcnt); + csa->regcnt++; /* Increment # of regions that point to this csa */ + return; + } + GTM_FD_TRACE_ONLY(gtm_dbjnl_dupfd_check();) /* check if any of db or jnl fds collide (D9I11-002714) */ + greg->was_open = FALSE; + /* we shouldn't have crit on any region unless we are in TP and in the final retry or we are in + * mupip_set_journal trying to switch journals across all regions. Currently, there is no fine-granular + * checking for mupip_set_journal, hence a coarse MUPIP_IMAGE check for image_type + */ + assert(dollar_tlevel && (CDB_STAGNATE <= t_tries) || IS_MUPIP_IMAGE || (0 == have_crit(CRIT_HAVE_ANY_REG))); + if (dollar_tlevel && (0 != have_crit(CRIT_HAVE_ANY_REG))) + { /* To avoid deadlocks with currently holding crits and the DLM lock request to be done in db_init(), + * we should insert this region in the tp_reg_list and tp_restart should do the gvcst_init after + * having released crit on all regions. Note that this check should be done AFTER checking if the + * region has already been opened (i.e. greg->was_open = TRUE logic above) since in that case we dont + * do any heavyweight processing (like db_init which involves crit/DLM locks) and so dont need to restart. + */ + insert_region(greg, &tp_reg_list, &tp_reg_free_list, SIZEOF(tp_region)); + t_retry(cdb_sc_needcrit); + assert(FALSE); /* we should never reach here since t_retry should have unwound the M-stack and restarted the TP */ + } + csa = (sgmnt_addrs *)&FILE_INFO(greg)->s_addrs; + +#ifdef NOLICENSE + licensed= TRUE ; +#else + CRYPT_CHKSYSTEM ; +#endif + db_init_region = greg; /* initialized for dbinit_ch */ + csa->hdr = NULL; + csa->nl = NULL; + csa->jnl = NULL; + csa->persistent_freeze = FALSE; /* want secshr_db_clnup() to clear an incomplete freeze/unfreeze codepath */ + csa->regcnt = 1; /* At this point, only one region points to this csa */ + UNIX_ONLY( + FILE_INFO(greg)->semid = INVALID_SEMID; + FILE_INFO(greg)->shmid = INVALID_SHMID; + FILE_INFO(greg)->gt_sem_ctime = 0; + FILE_INFO(greg)->gt_shm_ctime = 0; + FILE_INFO(greg)->ftok_semid = INVALID_SEMID; + ) + VMS_ONLY( + csa->db_addrs[0] = csa->db_addrs[1] = NULL; + csa->lock_addrs[0] = csa->lock_addrs[1] = NULL; + ) + UNSUPPORTED_PLATFORM_CHECK; + new_dbinit_ipc = FALSE; /* dbinit_ch relies on this flag. Reset it before establishing condition handler + * just in case this was set to TRUE by a previous "gvcst_init". */ + ESTABLISH(dbinit_ch); + + greg_acc_meth = greg->dyn.addr->acc_meth; + temp_cs_data = (sgmnt_data_ptr_t)cs_data_buff; + fc = greg->dyn.addr->file_cntl; + fc->file_type = greg_acc_meth; + fc->op = FC_READ; + fc->op_buff = (sm_uc_ptr_t)temp_cs_data; + fc->op_len = SIZEOF(*temp_cs_data); + fc->op_pos = 1; + dbfilop(fc); + + if (MEMCMP_LIT(temp_cs_data->label, GDS_LABEL)) + { + if (memcmp(temp_cs_data->label, GDS_LABEL, GDS_LABEL_SZ - 3)) + rts_error(VARLSTCNT(4) ERR_DBNOTGDS, 2, DB_LEN_STR(greg)); + else + rts_error(VARLSTCNT(4) ERR_BADDBVER, 2, DB_LEN_STR(greg)); + } + /* Set the following values to sane values for recovery/rollback */ + if (mupip_jnl_recover) + { + temp_cs_data->createinprogress = FALSE; + temp_cs_data->freeze = 0; + temp_cs_data->image_count = 0; + temp_cs_data->owner_node = 0; + } + if (temp_cs_data->createinprogress) + rts_error(VARLSTCNT(4) ERR_DBCREINCOMP, 2, DB_LEN_STR(greg)); + /* Do not error out while journal recovery is running or dse is running */ + if (temp_cs_data->file_corrupt && !mupip_jnl_recover && !dse_running) + rts_error(VARLSTCNT(4) ERR_DBFLCORRP, 2, DB_LEN_STR(greg)); + /* Warn the user CORRUPT_FILE flag set*/ + if (temp_cs_data->file_corrupt && dse_running) + gtm_putmsg(VARLSTCNT(4) MAKE_MSG_WARNING(ERR_DBFLCORRP), 2, DB_LEN_STR(greg)); + if ((dba_mm == temp_cs_data->acc_meth) && temp_cs_data->blks_to_upgrd) + rts_error(VARLSTCNT(4) ERR_MMNODYNUPGRD, 2, DB_LEN_STR(greg)); + assert(greg_acc_meth != dba_cm); + if (greg_acc_meth != temp_cs_data->acc_meth) + { + greg_acc_meth = temp_cs_data->acc_meth; + greg->dyn.addr->acc_meth = greg_acc_meth; + } + /* Here's the shared memory layout: + * + * low address + * + * both + * segment_data + * (file_header) + * MM_BLOCK + * (master_map) + * TH_BLOCK + * BG + * bt_header + * (bt_buckets * bt_rec) + * th_base (SIZEOF(que_ent) into an odd bt_rec) + * bt_base + * (n_bts * bt_rec) + * LOCK_BLOCK (lock_space) + * (lock_space_size) + * cs_addrs->acc_meth.bg.cache_state + * (cache_que_heads) + * (bt_buckets * cache_rec) + * (n_bts * cache_rec) + * critical + * (mutex_struct) + * nl + * (node_local) + * [jnl_name + * jnl_buffer] + * MM + * file contents + * LOCK_BLOCK (lock_space) + * (lock_space_size) + * cs_addrs->acc_meth.mm.mmblk_state + * (mmblk_que_heads) + * (bt_buckets * mmblk_rec) + * (n_bts * mmblk_rec) + * critical + * (mutex_struct) + * nl + * (node_local) + * [jnl_name + * jnl_buffer] + * high address + */ + /* Ensure first 3 members (upto now_running) of node_local are at the same offset for any version. + * This is so that the VERMISMATCH error can be successfully detected in db_init/mu_rndwn_file + * and so that the db-file-name can be successfully obtained from orphaned shm by mu_rndwn_all. + */ + assert(offsetof(node_local, label[0]) == 0); + /* Unfortunately, GDS_LABEL_SZ can't be defined with STR_LIT_LEN because of header nesting issues. + * So, let's make sure it's correct. + */ + assert(GDS_LABEL_SZ == SIZEOF(GDS_LABEL)); + assert(offsetof(node_local, fname[0]) == GDS_LABEL_SZ); + assert(offsetof(node_local, now_running[0]) == GDS_LABEL_SZ + MAX_FN_LEN + 1); + assert(SIZEOF(csa->nl->now_running) == MAX_REL_NAME); + /* Protect the db_init and the code below until we set greg->open to TRUE. This is needed as otherwise, + * if a MUPIP STOP is issued to this process at a time-window when db_init is completed but greg->open + * is NOT set to TRUE, will cause gds_rundown NOT to clean up the shared memory created by db_init and + * thus would be left over in the system. + */ + DEFER_INTERRUPTS(INTRPT_IN_GVCST_INIT); + db_init(greg, temp_cs_data); + crash_count = csa->critical->crashcnt; + csa->regnum = ++region_open_count; + csd = csa->hdr; +# ifdef GTM_TRIGGER + /* Take copy of db trigger cycle into csa at db startup. Any concurrent changes to the + * db trigger cycle (by MUPIP TRIGGER) will be detected at tcommit (t_end/tp_tend) time. + */ + csa->db_trigger_cycle = csd->db_trigger_cycle; +# endif + /* set csd and fill in selected fields */ + assert(greg_acc_meth == greg->dyn.addr->acc_meth); + switch (greg_acc_meth) + { + case dba_mm: + csa->acc_meth.mm.base_addr = (sm_uc_ptr_t)((sm_ulong_t)csd + (int)(csd->start_vbn - 1) * DISK_BLOCK_SIZE); + break; + case dba_bg: + db_csh_ini(csa); + break; + default: + GTMASSERT; + } + /* It is necessary that we do the pending gv_target list reallocation BEFORE db_common_init as the latter resets + * greg->max_key_size to be equal to the csd->max_key_size and hence process_gvt_pending_list might wrongly conclude + * that NO reallocation (since it checks greg->max_key_size with csd->max_key_size) is needed when in fact a + * reallocation might be necessary (if the user changed max_key_size AFTER database creation) + */ + PROCESS_GVT_PENDING_LIST(greg, csa, gvt_pending_list); + db_common_init(greg, csa, csd); /* do initialization common to db_init() and mu_rndwn_file() */ + + /* If we are not fully upgraded, see if we need to send a warning to the operator console about + performance. Compatibility mode is a known performance drain. Actually, we can send one of two + messages. If the desired_db_format is for an earlier release than the current release, we send + a performance warning that this mode degrades performance. However, if the desired_db_format is + for the current version but there are blocks to convert still, we send a gengle reminder that + running mupip reorg upgrade would be a good idea to get the full performance benefit of V5. + */ + time(&curr_time); + assert(MAXUINT4 > curr_time); + curr_time_uint4 = (uint4)curr_time; + next_warn_uint4 = csd->next_upgrd_warn.cas_time; + if (!csd->fully_upgraded && curr_time_uint4 > next_warn_uint4 + && COMPSWAP_LOCK(&csd->next_upgrd_warn.time_latch, next_warn_uint4, 0, (curr_time_uint4 + UPGRD_WARN_INTERVAL), 0)) + { /* The msg is due and we have successfully updated the next time interval */ + if (GDSVCURR != csd->desired_db_format) + send_msg(VARLSTCNT(4) ERR_DBVERPERFWARN1, 2, DB_LEN_STR(greg)); + else + send_msg(VARLSTCNT(4) ERR_DBVERPERFWARN2, 2, DB_LEN_STR(greg)); + } + + /* Compute the maximum journal space requirements for a PBLK (including possible ALIGN record). + * Use this variable in the TOTAL_TPJNL_REC_SIZE and TOTAL_NONTP_JNL_REC_SIZE macros instead of recomputing. + */ + csa->pblk_align_jrecsize = (int4)MIN_PBLK_RECLEN + csd->blk_size + (int4)MIN_ALIGN_RECLEN; + segment_update_array_size = UA_SIZE(csd); + + if (first_ua == NULL) + { /* first open of first database - establish an update array system */ + assert(update_array == NULL); + assert(update_array_ptr == NULL); + assert(update_array_size == 0); + tmp_ua = (ua_list *)malloc(SIZEOF(ua_list)); + memset(tmp_ua, 0, SIZEOF(ua_list)); /* initialize tmp_ua->update_array and tmp_ua->next_ua to NULL */ + tmp_ua->update_array = (char *)malloc(segment_update_array_size); + tmp_ua->update_array_size = segment_update_array_size; + /* assign global variables only after malloc() succeeds */ + update_array_size = cumul_update_array_size = segment_update_array_size; + update_array = update_array_ptr = tmp_ua->update_array; + first_ua = curr_ua = tmp_ua; + } else + { /* there's already an update_array system in place */ + assert(update_array != NULL); + assert(update_array_size != 0); + if (!dollar_tlevel && segment_update_array_size > first_ua->update_array_size) + { + /* no transaction in progress and the current array is too small - replace it */ + assert(first_ua->update_array == update_array); + assert(first_ua->update_array_size == update_array_size); + assert(first_ua->next_ua == NULL); + tmp_ua = first_ua; + first_ua = curr_ua = NULL; + free(update_array); + tmp_ua->update_array = update_array = update_array_ptr = NULL; + tmp_ua->update_array = (char *)malloc(segment_update_array_size); + tmp_ua->update_array_size = segment_update_array_size; + /* assign global variables only after malloc() succeeds */ + update_array_size = cumul_update_array_size = segment_update_array_size; + update_array = update_array_ptr = tmp_ua->update_array; + first_ua = curr_ua = tmp_ua; + } + } + assert(global_tlvl_info_list || !csa->sgm_info_ptr); + if (JNL_ALLOWED(csa)) + { + if (NULL == non_tp_jfb_ptr) + { + non_tp_jfb_ptr = (jnl_format_buffer *)malloc(SIZEOF(jnl_format_buffer)); + non_tp_jfb_buff_ptr = (unsigned char *)malloc(MAX_JNL_REC_SIZE); + non_tp_jfb_ptr->buff = (char *) non_tp_jfb_buff_ptr; + /* If the journal records need to be encrypted in the journal file and if replication is in use, + * we will need access to both the encrypted (for the journal file) and unencrypted (for the + * journal pool) journal record contents. Since this code is executed only once (for the first + * journaled database opened) by this process, we will have to allocate an alternate buffer + * for this purpose (to hold the unencrypted data) as long as this GT.M version supports encryption. + */ + GTMCRYPT_ONLY( + non_tp_jfb_ptr->alt_buff = (char *)malloc(MAX_JNL_REC_SIZE); + ) + non_tp_jfb_ptr->record_size = 0; /* initialize it to 0 since TOTAL_NONTPJNL_REC_SIZE macro uses it */ + } + /* csa->min_total_tpjnl_rec_size represents the minimum journal buffer space needed for a TP transaction. + * It is a conservative estimate assuming that one ALIGN record and one PINI record will be written for + * one set of fixed size jnl records written. + * si->total_jnl_rec_size is initialized/reinitialized to this value here and in tp_clean_up(). + * The purpose of this field is to avoid recomputation of the variable in tp_clean_up(). + * In addition to this, space requirements for whatever journal records get formatted as part of + * jnl_format() need to be taken into account. + * This is done in jnl_format() where si->total_jnl_rec_size is appropriately incremented. + */ + csa->min_total_tpjnl_rec_size = PINI_RECLEN + TCOM_RECLEN + MIN_ALIGN_RECLEN; + /* Similarly csa->min_total_nontpjnl_rec_size represents the minimum journal buffer space needed + * for a non-TP transaction. + * It is a conservative estimate assuming that one ALIGN record and one PINI record will be written for + * one set of fixed size jnl records written. + */ + csa->min_total_nontpjnl_rec_size = PINI_RECLEN + MIN_ALIGN_RECLEN; + } + if (tp_in_use || !IS_GTM_IMAGE) + gvcst_tp_init(greg); /* Initialize TP structures, else postpone till TP is used (only if GTM) */ + if (!global_tlvl_info_list) + { + global_tlvl_info_list = (buddy_list *)malloc(SIZEOF(buddy_list)); + initialize_list(global_tlvl_info_list, SIZEOF(global_tlvl_info), GBL_TLVL_INFO_LIST_INIT_ALLOC); + } + greg->open = TRUE; + greg->opening = FALSE; + /* gds_rundown if invoked from now on will take care of cleaning up the shared memory segment */ + ENABLE_INTERRUPTS(INTRPT_IN_GVCST_INIT); + if (dba_bg == greg_acc_meth) + { /* Check if (a) this region has non-upgraded blocks and if so, (b) the reformat buffer exists and + (c) if it is big enough to deal with this region. If the region does not have any non-upgraded + block (blks_to_upgrd is 0) we will not allocate the buffer at this time. Note that this opens up + a small window for errors. If this buffer is not allocated and someone turns on compatibility + mode and before the process can discover this and allocate the buffer, it runs out of memory, + errors out and finds it is responsible for running down the database, it could fail on a recursive + memory error when it tries to allocate the block. This is (to me) an acceptable risk as it is + very low and compares favorably to the cost of every process allocating a database block sized + chunk of private storage that will be seldom if ever used (SE 3/2005). + */ + if (0 != csd->blks_to_upgrd && csd->blk_size > reformat_buffer_len) + { /* Buffer not big enough (or does not exist) .. get a new one releasing old if it exists */ + assert(0 == fast_lock_count); /* this is mainline (non-interrupt) code */ + ++fast_lock_count; /* No interrupts across this use of reformat_buffer */ + /* reformat_buffer_in_use should always be incremented only AFTER incrementing fast_lock_count + * as it is the latter that prevents interrupts from using the reformat buffer. Similarly + * the decrement of fast_lock_count should be done AFTER decrementing reformat_buffer_in_use. + */ + assert(0 == reformat_buffer_in_use); + DEBUG_ONLY(reformat_buffer_in_use++;) + if (reformat_buffer) + free(reformat_buffer); /* Different blksized databases in use .. keep only largest one */ + reformat_buffer = malloc(csd->blk_size); + reformat_buffer_len = csd->blk_size; + DEBUG_ONLY(reformat_buffer_in_use--;) + assert(0 == reformat_buffer_in_use); + --fast_lock_count; + } + + } + if ((dba_bg == greg_acc_meth) || (dba_mm == greg_acc_meth)) + { + /* Determine fid_index of current region's file_id across sorted file_ids of all regions open until now. + * All regions which have a file_id lesser than that of current region will have no change to their fid_index + * All regions which have a file_id greater than that of current region will have their fid_index incremented by 1 + * The fid_index determination algorithm below has an optimization in that if the current region's file_id is + * determined to be greater than that of a particular region, then all regions whose fid_index is lesser + * than that particular region's fid_index are guaranteed to have a lesser file_id than the current region + * so we do not compare those against the current region's file_id. + * Note that the sorting is done only on DB/MM regions. GT.CM/DDP regions should not be part of TP transactions, + * hence they will not be sorted. + */ + prevcsa = NULL; + greg_fid = &(csa->nl->unique_id); + for (regcsa = cs_addrs_list; NULL != regcsa; regcsa = regcsa->next_csa) + { + if ((NULL != prevcsa) && (regcsa->fid_index < prevcsa->fid_index)) + continue; + reg_fid = &((regcsa)->nl->unique_id); + VMS_ONLY(if (0 < memcmp(&(greg_fid->file_id), (char *)&(reg_fid->file_id), SIZEOF(gd_id)))) + UNIX_ONLY(if (0 < gdid_cmp(&(greg_fid->uid), &(reg_fid->uid)))) + { + if ((NULL == prevcsa) || (regcsa->fid_index > prevcsa->fid_index)) + prevcsa = regcsa; + } else + regcsa->fid_index++; + } + if (NULL == prevcsa) + csa->fid_index = 1; + else + csa->fid_index = prevcsa->fid_index + 1; + /* Add current csa into list of open csas */ + csa->next_csa = cs_addrs_list; + cs_addrs_list = csa; + /* Also update tp_reg_list fid_index's as insert_region relies on it */ + for (tr = tp_reg_list; NULL != tr; tr = tr->fPtr) + tr->file.fid_index = (&FILE_INFO(tr->reg)->s_addrs)->fid_index; + DBG_CHECK_TP_REG_LIST_SORTING(tp_reg_list); + } + REVERT; + return; +} diff --git a/sr_port/gvcst_kill.c b/sr_port/gvcst_kill.c new file mode 100644 index 0000000..f2cf47c --- /dev/null +++ b/sr_port/gvcst_kill.c @@ -0,0 +1,618 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include +#include "gtm_string.h" +#include "gtm_stdlib.h" +#include "gtm_stdio.h" +#include "gtm_inet.h" /* Required for gtmsource.h */ + +#ifdef VMS +#include /* Required for gtmsource.h */ +#endif + +#include "cdb_sc.h" +#include "gdsroot.h" +#include "gdskill.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gdscc.h" +#include "min_max.h" /* needed for gdsblkops.h */ +#include "gdsblkops.h" +#include "jnl.h" +#include "copy.h" +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" +#include "repl_msg.h" +#include "gtmsource.h" +#include "interlock.h" +#include "rtnhdr.h" +#include "stack_frame.h" +#ifdef GTM_TRIGGER +# include "gv_trigger.h" +# include "gtm_trigger.h" +# include "gv_trigger_protos.h" +# include "mv_stent.h" +# include "stringpool.h" +#endif +#include "tp_frame.h" +#include "tp_restart.h" + +/* Include prototypes */ +#include "gvcst_kill_blk.h" +#include "t_qread.h" +#include "t_end.h" +#include "t_retry.h" +#include "t_begin.h" +#include "gvcst_expand_free_subtree.h" +#include "gvcst_protos.h" /* for gvcst_kill,gvcst_search prototype */ +#include "rc_cpt_ops.h" +#include "add_inter.h" +#include "sleep_cnt.h" +#include "wcs_sleep.h" +#include "wbox_test_init.h" +#include "memcoherency.h" +#include "util.h" +#include "op.h" /* for op_tstart prototype */ +#include "format_targ_key.h" /* for format_targ_key prototype */ +#include "tp_set_sgm.h" /* for tp_set_sgm prototype */ +#include "op_tcommit.h" /* for op_tcommit prototype */ +#include "have_crit.h" + +GBLREF gd_region *gv_cur_region; +GBLREF gv_key *gv_currkey, *gv_altkey; +GBLREF int4 gv_keysize; +GBLREF gv_namehead *gv_target; +GBLREF jnl_fence_control jnl_fence_ctl; +GBLREF kill_set *kill_set_tail; +GBLREF uint4 dollar_tlevel; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF sgm_info *sgm_info_ptr; +GBLREF unsigned char cw_set_depth; +GBLREF unsigned int t_tries; +GBLREF boolean_t need_kip_incr; +GBLREF uint4 update_trans; +GBLREF jnlpool_addrs jnlpool; +GBLREF sgmnt_addrs *kip_csa; +GBLREF boolean_t skip_dbtriggers; /* see gbldefs.c for description of this global */ +GBLREF stack_frame *frame_pointer; +#ifdef GTM_TRIGGER +GBLREF int tprestart_state; +GBLREF int4 gtm_trigger_depth; +GBLREF int4 tstart_trigger_depth; +GBLREF boolean_t skip_INVOKE_RESTART; +GBLREF boolean_t ztwormhole_used; /* TRUE if $ztwormhole was used by trigger code */ +GBLREF mval dollar_ztwormhole; +#endif +#ifdef DEBUG +GBLREF char *update_array, *update_array_ptr; +GBLREF uint4 update_array_size; /* needed for the ENSURE_UPDATE_ARRAY_SPACE macro */ +#endif + +error_def(ERR_TPRETRY); +error_def(ERR_GVKILLFAIL); + +#ifdef GTM_TRIGGER +LITREF mval literal_null; +LITREF mval *fndata_table[2][2]; +#endif + +void gvcst_kill(bool do_subtree) +{ + bool clue, flush_cache; + boolean_t next_fenced_was_null, write_logical_jnlrecs, jnl_format_done; + boolean_t left_extra, right_extra; + cw_set_element *tp_cse; + enum cdb_sc cdb_status; + int lev, end; + uint4 prev_update_trans, actual_update; + jnl_format_buffer *jfb, *ztworm_jfb; + jnl_action_code operation; + kill_set kill_set_head, *ks, *temp_ks; + node_local_ptr_t cnl; + sgmnt_addrs *csa; + sgmnt_data_ptr_t csd; + srch_blk_status *left,*right; + srch_hist *alt_hist; + srch_rec_status *left_rec_stat, local_srch_rec; + uint4 segment_update_array_size; + unsigned char *base; + int lcl_dollar_tlevel, rc; + uint4 nodeflags; + sgm_info *si; +# ifdef GTM_TRIGGER + mint dlr_data; + boolean_t is_tpwrap; + boolean_t lcl_implicit_tstart; /* local copy of the global variable "implicit_tstart" */ + gtm_trigger_parms trigparms; + gvt_trigger_t *gvt_trigger; + gvtr_invoke_parms_t gvtr_parms; + int gtm_trig_status; + unsigned char *save_msp; + mv_stent *save_mv_chain; + mval *ztold_mval = NULL, ztvalue_new, ztworm_val; +# endif +# ifdef DEBUG + boolean_t is_mm; + uint4 dbg_research_cnt; +# endif + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + csa = cs_addrs; + csd = csa->hdr; + cnl = csa->nl; + DEBUG_ONLY(is_mm = (dba_mm == csd->acc_meth)); + GTMTRIG_ONLY( + TRIG_CHECK_REPLSTATE_MATCHES_EXPLICIT_UPDATE(gv_cur_region, csa); + assert(!dollar_tlevel || (tstart_trigger_depth <= gtm_trigger_depth)); + if (!dollar_tlevel || (gtm_trigger_depth == tstart_trigger_depth)) + { /* This is an explicit update. Set ztwormhole_used to FALSE. Note that we initialize this only at the + * beginning of the transaction and not at the beginning of each try/retry. If the application used + * $ztwormhole in any retsarting try of the transaction, we consider it necessary to write the + * TZTWORM/UZTWORM record even though it was not used in the succeeding/committing try. + */ + ztwormhole_used = FALSE; + } + ) + JNLPOOL_INIT_IF_NEEDED(csa, csd, cnl); + if (!dollar_tlevel) + { + kill_set_head.next_kill_set = NULL; + if (jnl_fence_ctl.level) /* next_fenced_was_null is reliable only if we are in ZTransaction */ + next_fenced_was_null = (NULL == csa->next_fenced) ? TRUE : FALSE; + /* In case of non-TP explicit updates that invoke triggers the kills happen inside of TP. If those kills + * dont cause any actual update, we need prev_update_trans set appropriately so update_trans can be reset. + */ + GTMTRIG_ONLY(prev_update_trans = 0;) + } else + prev_update_trans = sgm_info_ptr->update_trans; + assert(('\0' != gv_currkey->base[0]) && gv_currkey->end); + DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC; + T_BEGIN_SETORKILL_NONTP_OR_TP(ERR_GVKILLFAIL); + assert(NULL != update_array); + assert(NULL != update_array_ptr); + assert(0 != update_array_size); + assert(update_array + update_array_size >= update_array_ptr); + alt_hist = gv_target->alt_hist; + GTMTRIG_ONLY( + lcl_implicit_tstart = FALSE; + trigparms.ztvalue_new = NULL; + ) + operation = (do_subtree ? JNL_KILL : JNL_ZKILL); + for (;;) + { + actual_update = 0; + DEBUG_ONLY(dbg_research_cnt = 0;) + jnl_format_done = FALSE; + write_logical_jnlrecs = JNL_WRITE_LOGICAL_RECS(csa); +# ifdef GTM_TRIGGER + gvtr_parms.num_triggers_invoked = 0; /* clear any leftover value */ + dlr_data = 0; + is_tpwrap = FALSE; + if (!skip_dbtriggers) /* No trigger init needed if skip_dbtriggers is TRUE (e.g. mupip load etc.) */ + { + GVTR_INIT_AND_TPWRAP_IF_NEEDED(csa, csd, gv_target, gvt_trigger, lcl_implicit_tstart, is_tpwrap, + ERR_GVKILLFAIL); + assert(gvt_trigger == gv_target->gvt_trigger); + if (NULL != gvt_trigger) + { + PUSH_ZTOLDMVAL_ON_M_STACK(ztold_mval, save_msp, save_mv_chain); + /* Determine $ZTOLDVAL & $ZTDATA to fill in trigparms */ + cdb_status = gvcst_dataget(&dlr_data, ztold_mval); + if (cdb_sc_normal != cdb_status) + goto retry; + assert((11 >= dlr_data) && (1 >= (dlr_data % 10))); + /* Invoke triggers for KILL as long as $data is nonzero (1 or 10 or 11). + * Invoke triggers for ZKILL only if $data is 1 or 11 (for 10 case, ZKILL is a no-op). + */ + if (do_subtree ? dlr_data : (dlr_data & 1)) + { /* Either node or its descendants exists. Invoke KILL triggers for this node. + * But first write journal records (ZTWORM and/or KILL) for the triggering nupdate. + * "ztworm_jfb", "jfb" and "jnl_format_done" are set by the below macro. + */ + JNL_FORMAT_ZTWORM_IF_NEEDED(csa, write_logical_jnlrecs, + operation, gv_currkey, NULL, ztworm_jfb, jfb, jnl_format_done); + /* Initialize trigger parms that dont depend on the context of the matching trigger */ + trigparms.ztoldval_new = ztold_mval; + trigparms.ztdata_new = fndata_table[dlr_data / 10][dlr_data & 1]; + if (NULL == trigparms.ztvalue_new) + { /* Do not pass literal_null directly since $ztval can be modified inside trigger + * code and literal_null is in read-only segment so will not be modifiable. + * Hence the need for a dummy local variable mval "ztvalue_new" in the C stack. + */ + ztvalue_new = literal_null; + trigparms.ztvalue_new = &ztvalue_new; + } + gvtr_parms.gvtr_cmd = do_subtree ? GVTR_CMDTYPE_KILL : GVTR_CMDTYPE_ZKILL; + gvtr_parms.gvt_trigger = gvt_trigger; + /* gvtr_parms.duplicate_set : No need to initialize as it is SET-specific */ + /* Now that we have filled in minimal information, let "gvtr_match_n_invoke" do the rest */ + gtm_trig_status = gvtr_match_n_invoke(&trigparms, &gvtr_parms); + assert((0 == gtm_trig_status) || (ERR_TPRETRY == gtm_trig_status)); + if (ERR_TPRETRY == gtm_trig_status) + { /* A restart has been signaled that we need to handle or complete the handling of. + * This restart could have occurred reading the trigger in which case no + * tp_restart() has yet been done or it could have occurred in trigger code in + * which case we need to finish the incomplete tp_restart. In both cases this + * must be an implicitly TP wrapped transaction. Our action is to complete the + * necessary tp_restart() logic (t_retry is already completed so should be skipped) + * and then re-do the gvcst_kill logic. + */ + assert(lcl_implicit_tstart); + assert(CDB_STAGNATE >= t_tries); + cdb_status = cdb_sc_normal; /* signal "retry:" to avoid t_retry call */ + goto retry; + } + REMOVE_ZTWORM_JFB_IF_NEEDED(ztworm_jfb, jfb, sgm_info_ptr); + } + /* else : we dont invoke any KILL/ZTKILL type triggers for a node whose $data is 0 */ + POP_MVALS_FROM_M_STACK_IF_NEEDED(ztold_mval, save_msp, save_mv_chain); + } + } +# endif + assert(csd == cs_data); /* assert csd is in sync with cs_data even if there were MM db file extensions */ + assert(csd == csa->hdr); + si = sgm_info_ptr; /* Has to be AFTER the GVTR_INIT_AND_TPWRAP_IF_NEEDED macro in case that sets + * sgm_info_ptr to a non-NULL value (if a non-TP transaction is tp wrapped for triggers). + */ + assert(t_tries < CDB_STAGNATE || csa->now_crit); /* we better hold crit in the final retry (TP & non-TP) */ + if (!dollar_tlevel) + { + CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ + kill_set_tail = &kill_set_head; + for (ks = &kill_set_head; NULL != ks; ks = ks->next_kill_set) + ks->used = 0; + } else + { + segment_update_array_size = UA_NON_BM_SIZE(csd); + ENSURE_UPDATE_ARRAY_SPACE(segment_update_array_size); + } + clue = (0 != gv_target->clue.end); +research: + if (cdb_sc_normal != (cdb_status = gvcst_search(gv_currkey, NULL))) + goto retry; + assert(gv_altkey->top == gv_currkey->top); + assert(gv_altkey->top == gv_keysize); + end = gv_currkey->end; + assert(end < gv_currkey->top); + memcpy(gv_altkey, gv_currkey, SIZEOF(gv_key) + end); + base = &gv_altkey->base[0]; + if (do_subtree) + { + base[end - 1] = 1; + assert(KEY_DELIMITER == base[end]); + base[++end] = KEY_DELIMITER; + } else + { + base[end] = 1; + base[++end] = KEY_DELIMITER; + base[++end] = KEY_DELIMITER; + } + gv_altkey->end = end; + if (cdb_sc_normal != (cdb_status = gvcst_search(gv_altkey, alt_hist))) + goto retry; + if (alt_hist->depth != gv_target->hist.depth) + { + cdb_status = cdb_sc_badlvl; + goto retry; + } + right_extra = FALSE; + left_extra = TRUE; + for (lev = 0; 0 != gv_target->hist.h[lev].blk_num; ++lev) + { + left = &gv_target->hist.h[lev]; + right = &alt_hist->h[lev]; + assert(0 != right->blk_num); + left_rec_stat = left_extra ? &left->prev_rec : &left->curr_rec; + if (left->blk_num == right->blk_num) + { + cdb_status = gvcst_kill_blk(left, lev, gv_currkey, *left_rec_stat, right->curr_rec, + right_extra, &tp_cse); + assert(!dollar_tlevel || (NULL == tp_cse) || (left->cse == tp_cse)); + assert( dollar_tlevel || (NULL == tp_cse)); + if (tp_cse) + actual_update = UPDTRNS_DB_UPDATED_MASK; + if (cdb_sc_normal == cdb_status) + break; + gv_target->clue.end = 0;/* If need to go up from leaf (or higher), history will cease to be valid */ + if (clue) + { /* Clue history valid only for data block, need to re-search */ + clue = FALSE; + DEBUG_ONLY(dbg_research_cnt++;) + goto research; + } + if (cdb_sc_delete_parent != cdb_status) + goto retry; + left_extra = right_extra + = TRUE; + } else + { + gv_target->clue.end = 0; /* If more than one block involved, history will cease to be valid */ + if (clue) + { /* Clue history valid only for data block, need to re-search */ + clue = FALSE; + DEBUG_ONLY(dbg_research_cnt++;) + goto research; + } + local_srch_rec.offset = ((blk_hdr_ptr_t)left->buffaddr)->bsiz; + local_srch_rec.match = 0; + cdb_status = gvcst_kill_blk(left, lev, gv_currkey, *left_rec_stat, local_srch_rec, FALSE, &tp_cse); + assert(!dollar_tlevel || (NULL == tp_cse) || (left->cse == tp_cse)); + assert( dollar_tlevel || (NULL == tp_cse)); + if (tp_cse) + actual_update = UPDTRNS_DB_UPDATED_MASK; + if (cdb_sc_normal == cdb_status) + left_extra = FALSE; + else if (cdb_sc_delete_parent == cdb_status) + { + left_extra = TRUE; + cdb_status = cdb_sc_normal; + } else + goto retry; + local_srch_rec.offset = local_srch_rec.match + = 0; + cdb_status = gvcst_kill_blk(right, lev, gv_altkey, local_srch_rec, right->curr_rec, + right_extra, &tp_cse); + assert(!dollar_tlevel || (NULL == tp_cse) || (right->cse == tp_cse)); + assert( dollar_tlevel || (NULL == tp_cse)); + if (tp_cse) + actual_update = UPDTRNS_DB_UPDATED_MASK; + if (cdb_sc_normal == cdb_status) + right_extra = FALSE; + else if (cdb_sc_delete_parent == cdb_status) + { + right_extra = TRUE; + cdb_status = cdb_sc_normal; + } else + goto retry; + } + } + if (!dollar_tlevel) + { + assert(!jnl_format_done); + assert(0 == actual_update); /* for non-TP, tp_cse is NULL even if cw_set_depth is non-zero */ + if (0 != cw_set_depth) + actual_update = UPDTRNS_DB_UPDATED_MASK; + /* reset update_trans (to potentially non-zero value) in case it got set to 0 in previous retry */ + /* do not treat redundant KILL as an update-transaction */ + update_trans = actual_update; + } else + { + GTMTRIG_ONLY( + if (!actual_update) /* possible only if the node we are attempting to KILL does not exist now */ + { /* Note that it is possible that the node existed at the time of the "gvcst_dataget" but + * got killed later when we did the gvcst_search (right after the "research:" label). This + * is possible if any triggers invoked in between KILLed the node and/or all its + * descendants. We still want to consider this case as an actual update to the database + * as far as journaling is concerned (this is because we have already formatted the + * KILL journal record) so set actual_update to UPDTRNS_DB_UPDATED_MASK in this case. + * Note that it is possible that the node does not exist now due to a restartable + * situation (instead of due to a KILL inside trigger code). In that case, it is safe to + * set actual_update to UPDTRNS_DB_UPDATED_MASK (even though we did not do any update to + * the database) since we will be restarting anyways. For ZKILL, check if dlr_data was + * 1 or 11 and for KILL, check if it was 1, 10 or 11. + */ + DEBUG_ONLY( + if (!gvtr_parms.num_triggers_invoked && (do_subtree ? dlr_data : (dlr_data & 1))) + { /* Triggers were not invoked but still the node that existed a few + * steps above does not exist now. This is a restartable situation. + * Assert that. + */ + assert(!skip_dbtriggers); + TREF(donot_commit) |= DONOTCOMMIT_GVCST_KILL_ZERO_TRIGGERS; + } + ) + if (do_subtree ? dlr_data : (dlr_data & 1)) + actual_update = UPDTRNS_DB_UPDATED_MASK; + } + ) + NON_GTMTRIG_ONLY(assert(!jnl_format_done)); + assert(!actual_update || si->cw_set_depth + GTMTRIG_ONLY(|| gvtr_parms.num_triggers_invoked || TREF(donot_commit))); + assert(!(prev_update_trans & ~UPDTRNS_VALID_MASK)); + if (!actual_update) + si->update_trans = prev_update_trans; /* restore status prior to redundant KILL */ + } + if (write_logical_jnlrecs && actual_update) + { /* Maintain journal records only if the kill actually resulted in a database update. + * skip_dbtriggers is set to TRUE for trigger unsupporting platforms. So, nodeflags will be set to skip + * triggers on secondary. This ensures that updates happening in primary (trigger unsupporting platform) + * is treated in the same order in the secondary (trigger supporting platform) irrespective of whether + * the secondary has defined triggers or not for the global that is being updated. + */ + if (!dollar_tlevel) + { + nodeflags = 0; + if (skip_dbtriggers) + nodeflags |= JS_SKIP_TRIGGERS_MASK; + assert(!jnl_format_done); + jfb = jnl_format(operation, gv_currkey, NULL, nodeflags); + assert(NULL != jfb); + } else if (!jnl_format_done) + { + nodeflags = 0; + if (skip_dbtriggers) + nodeflags |= JS_SKIP_TRIGGERS_MASK; +# ifdef GTM_TRIGGER + /* Do not replicate implicit updates */ + assert(tstart_trigger_depth <= gtm_trigger_depth); + if (gtm_trigger_depth > tstart_trigger_depth) + { + /* Ensure that JS_SKIP_TRIGGERS_MASK and JS_NOT_REPLICATED_MASK are mutually exclusive. */ + assert(!(nodeflags & JS_SKIP_TRIGGERS_MASK)); + nodeflags |= JS_NOT_REPLICATED_MASK; + } +# endif + /* Write KILL journal record */ + jfb = jnl_format(operation, gv_currkey, NULL, nodeflags); + assert(NULL != jfb); + } + } + flush_cache = FALSE; + if (!dollar_tlevel) + { + if ((0 != csd->dsid) && (0 < kill_set_head.used) + && gv_target->hist.h[1].blk_num != alt_hist->h[1].blk_num) + { /* multi-level delete */ + rc_cpt_inval(); + flush_cache = TRUE; + } + if (0 < kill_set_head.used) /* increase kill_in_prog */ + { + need_kip_incr = TRUE; + if (!csa->now_crit) /* Do not sleep while holding crit */ + WAIT_ON_INHIBIT_KILLS(cnl, MAXWAIT2KILL); + } + if ((trans_num)0 == t_end(&gv_target->hist, alt_hist, TN_NOT_SPECIFIED)) + { /* In case this is MM and t_end caused a database extension, reset csd */ + assert(is_mm || (csd == cs_data)); + csd = cs_data; + if (jnl_fence_ctl.level && next_fenced_was_null && actual_update && write_logical_jnlrecs) + { /* If ZTransaction and first KILL and the kill resulted in an update + * Note that "write_logical_jnlrecs" is used above instead of JNL_WRITE_LOGICAL_RECS(csa) + * since the value of the latter macro might have changed inside the call to t_end() + * (since jnl state changes could change the JNL_ENABLED check which is part of the macro). + */ + assert(NULL != csa->next_fenced); + assert(jnl_fence_ctl.fence_list == csa); + jnl_fence_ctl.fence_list = csa->next_fenced; + csa->next_fenced = NULL; + } + need_kip_incr = FALSE; + assert(NULL == kip_csa); + /* We could have entered gvcst_kill trying to kill a global that does not exist but later due to a + * concurrent set, we are about to retry. In such a case, update_trans could have been set to 0 + * (from actual_update above). Reset update_trans to non-zero for the next retry as we expect the + * kill to happen in this retry. + */ + update_trans = UPDTRNS_DB_UPDATED_MASK; + continue; + } + /* In case this is MM and t_end caused a database extension, reset csd */ + assert(is_mm || (csd == cs_data)); + csd = cs_data; + } else + { + cdb_status = tp_hist(alt_hist); + if (cdb_sc_normal != cdb_status) + goto retry; + } + /* Note down $tlevel (used later) before it is potentially changed by op_tcommit below */ + lcl_dollar_tlevel = dollar_tlevel; +# ifdef GTM_TRIGGER + if (lcl_implicit_tstart) + { + GVTR_OP_TCOMMIT(cdb_status); + if (cdb_sc_normal != cdb_status) + goto retry; + } +# endif + INCR_GVSTATS_COUNTER(csa, cnl, n_kill, 1); + if (0 != gv_target->clue.end) + { /* If clue is still valid, then the deletion was confined to a single block */ + assert(gv_target->hist.h[0].blk_num == alt_hist->h[0].blk_num); + /* In this case, the "right hand" key (which was searched via gv_altkey) was the last search + * and should become the clue. Furthermore, the curr.match from this last search should be + * the history's curr.match. However, this record will have been shuffled to the position of + * the "left hand" key, and therefore, the original curr.offset should be left untouched. */ + gv_target->hist.h[0].curr_rec.match = alt_hist->h[0].curr_rec.match; + COPY_CURRKEY_TO_GVTARGET_CLUE(gv_target, gv_altkey); + } + NON_GTMTRIG_ONLY(assert(lcl_dollar_tlevel == dollar_tlevel)); + if (!lcl_dollar_tlevel) + { + assert(!dollar_tlevel); + assert(0 < kill_set_head.used || (NULL == kip_csa)); + if (0 < kill_set_head.used) /* free subtree, decrease kill_in_prog */ + { /* If csd->dsid is non-zero then some rc code was exercised before the changes + * to prevent pre-commit expansion of the kill subtree. Not clear on what to do now. + */ + assert(!csd->dsid); + ENABLE_WBTEST_ABANDONEDKILL; + gvcst_expand_free_subtree(&kill_set_head); + /* In case this is MM and gvcst_expand_free_subtree() called gvcst_bmp_mark_free() called t_retry() + * which remapped an extended database, reset csd */ + assert(is_mm || (csd == cs_data)); + csd = cs_data; + DECR_KIP(csd, csa, kip_csa); + } + assert(0 < kill_set_head.used || (NULL == kip_csa)); + for (ks = kill_set_head.next_kill_set; NULL != ks; ks = temp_ks) + { + temp_ks = ks->next_kill_set; + free(ks); + } + assert(0 < kill_set_head.used || (NULL == kip_csa)); + } + GTMTRIG_ONLY(assert(NULL == ztold_mval)); + return; +retry: +# ifdef GTM_TRIGGER + if (lcl_implicit_tstart) + { + assert(!skip_dbtriggers); + assert(!skip_INVOKE_RESTART); + assert((cdb_sc_normal != cdb_status) || (ERR_TPRETRY == gtm_trig_status)); + if (cdb_sc_normal != cdb_status) + skip_INVOKE_RESTART = TRUE; /* causes t_retry to invoke only tp_restart * without any rts_error */ + /* else: t_retry has already been done by gtm_trigger so no need to do it again for this try */ + } +# endif + assert((cdb_sc_normal != cdb_status) GTMTRIG_ONLY(|| lcl_implicit_tstart)); + if (cdb_sc_normal != cdb_status) + { + GTMTRIG_ONLY(POP_MVALS_FROM_M_STACK_IF_NEEDED(ztold_mval, save_msp, save_mv_chain)); + t_retry(cdb_status); + } else + { /* else: t_retry has already been done so no need to do that again but need to still invoke tp_restart + * to complete pending "tprestart_state" related work. + */ +# ifdef GTM_TRIGGER + assert(ERR_TPRETRY == gtm_trig_status); + TRIGGER_BASE_FRAME_UNWIND_IF_NOMANSLAND; + POP_MVALS_FROM_M_STACK_IF_NEEDED(ztold_mval, save_msp, save_mv_chain); +# endif + rc = tp_restart(1, !TP_RESTART_HANDLES_ERRORS); + assert(0 == rc GTMTRIG_ONLY(&& TPRESTART_STATE_NORMAL == tprestart_state)); + } + GTMTRIG_ONLY(assert(!skip_INVOKE_RESTART)); /* if set to TRUE above, should have been reset by t_retry */ + /* At this point, we can be in TP only if we implicitly did a tstart in gvcst_kill (as part of a trigger update). + * Assert that. Since the t_retry/tp_restart would have reset si->update_trans, we need to set it again. + * So reinvoke the T_BEGIN call only in case of TP. For non-TP, update_trans is unaffected by t_retry. + */ + assert(!dollar_tlevel GTMTRIG_ONLY(|| lcl_implicit_tstart)); + if (dollar_tlevel) + { + tp_set_sgm(); /* set sgm_info_ptr & first_sgm_info for TP start */ + T_BEGIN_SETORKILL_NONTP_OR_TP(ERR_GVKILLFAIL); /* set update_trans and t_err for wrapped TP */ + } else + { + /* We could have entered gvcst_kill trying to kill a global that does not exist but later due to a + * concurrent set, came here for retry. In such a case, update_trans could have been set to 0 + * (from actual_update). Reset update_trans to non-zero for the next retry as we expect the kill to + * happen in this retry. + */ + update_trans = UPDTRNS_DB_UPDATED_MASK; + } + /* In case this is MM and t_retry() remapped an extended database, reset csd */ + assert(is_mm || (csd == cs_data)); + csd = cs_data; + } +} diff --git a/sr_port/gvcst_kill_blk.c b/sr_port/gvcst_kill_blk.c new file mode 100644 index 0000000..f0b9c73 --- /dev/null +++ b/sr_port/gvcst_kill_blk.c @@ -0,0 +1,420 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gdskill.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "cdb_sc.h" +#include "min_max.h" /* needed for gdsblkops.h */ +#include "gdsblkops.h" +#include "copy.h" +#include "gdscc.h" +#include "filestruct.h" +#include "jnl.h" +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" + +/* Include prototypes */ +#include "t_write.h" +#include "t_create.h" +#include "gvcst_delete_blk.h" +#include "gvcst_kill_blk.h" + +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF gv_namehead *gv_target; +GBLREF char *update_array, *update_array_ptr; +GBLREF uint4 update_array_size; /* for the BLK_* macros */ +GBLREF sgmnt_addrs *cs_addrs; +GBLREF unsigned int t_tries; +GBLREF uint4 dollar_tlevel; +GBLREF sgm_info *sgm_info_ptr; +GBLREF boolean_t horiz_growth; + +/* delete all records greater than low and less than high in blkhist->blk_num */ + +enum cdb_sc gvcst_kill_blk(srch_blk_status *blkhist, + char level, + gv_key *search_key, + srch_rec_status low, + srch_rec_status high, + boolean_t right_extra, + cw_set_element **cseptr) +{ + typedef sm_uc_ptr_t bytptr; + + unsigned short temp_ushort; + int4 temp_long; + int blk_size, blk_seg_cnt, lmatch, rmatch, targ_len, prev_len, targ_base, next_rec_shrink, + temp_int, blkseglen; + bool kill_root, first_copy; + blk_hdr_ptr_t old_blk_hdr; + rec_hdr_ptr_t left_ptr; /*pointer to record before first record to delete*/ + rec_hdr_ptr_t del_ptr; /*pointer to first record to delete*/ + rec_hdr_ptr_t right_ptr; /*pointer to record after last record to delete*/ + rec_hdr_ptr_t right_prev_ptr; + rec_hdr_ptr_t rp, rp1; /*scratch record pointer*/ + rec_hdr_ptr_t first_in_blk, top_of_block, new_rec_hdr, star_rec_hdr; + blk_segment *bs1, *bs_ptr; + block_index new_block_index; + unsigned char *skb; + static readonly block_id zeroes = 0; + cw_set_element *cse, *old_cse; + bytptr curr, prev, right_bytptr; + off_chain chain1, curr_chain, prev_chain; + block_id blk; + sm_uc_ptr_t buffer; + + *cseptr = NULL; + if (low.offset == high.offset) + return cdb_sc_normal; + blk = blkhist->blk_num; + if (dollar_tlevel) + { + PUT_LONG(&chain1, blk); + if ((1 == chain1.flag) && ((int)chain1.cw_index >= sgm_info_ptr->cw_set_depth)) + { + assert(sgm_info_ptr->tp_csa == cs_addrs); + assert(FALSE == cs_addrs->now_crit); + return cdb_sc_blknumerr; + } + } + buffer = blkhist->buffaddr; + old_blk_hdr = (blk_hdr_ptr_t)buffer; + kill_root = FALSE; + blk_size = cs_data->blk_size; + first_in_blk = (rec_hdr_ptr_t)((bytptr)old_blk_hdr + SIZEOF(blk_hdr)); + top_of_block = (rec_hdr_ptr_t)((bytptr)old_blk_hdr + old_blk_hdr->bsiz); + left_ptr = (rec_hdr_ptr_t)((bytptr)old_blk_hdr + low.offset); + right_ptr = (rec_hdr_ptr_t)((bytptr)old_blk_hdr + high.offset); + if (right_extra && right_ptr < top_of_block) + { + right_prev_ptr = right_ptr; + GET_USHORT(temp_ushort, &right_ptr->rsiz); + right_ptr = (rec_hdr_ptr_t)((bytptr)right_ptr + temp_ushort); + } + if ((bytptr)left_ptr < (bytptr)old_blk_hdr || + (bytptr)right_ptr > (bytptr)top_of_block || + (bytptr)left_ptr >= (bytptr)right_ptr) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_rmisalign; + } + if ((bytptr)left_ptr == (bytptr)old_blk_hdr) + { + if ((bytptr)right_ptr == (bytptr)top_of_block) + { + if ((bytptr)first_in_blk == (bytptr)top_of_block) + { + if (0 != level) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_rmisalign; + } + return cdb_sc_normal; + } + if (!gv_target->hist.h[level + 1].blk_num) + kill_root = TRUE; + else + return cdb_sc_delete_parent; + } + del_ptr = first_in_blk; + } else + { + GET_USHORT(temp_ushort, &left_ptr->rsiz); + del_ptr = (rec_hdr_ptr_t)((bytptr)left_ptr + temp_ushort); + if ((bytptr)del_ptr <= (bytptr)(left_ptr + 1) || (bytptr)del_ptr > (bytptr)right_ptr) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_rmisalign; + } + } + if ((bytptr)del_ptr == (bytptr)right_ptr) + return cdb_sc_normal; + lmatch = low.match; + rmatch = high.match; + if (level) + { + for (rp = del_ptr ; rp < right_ptr ; rp = rp1) + { + GET_USHORT(temp_ushort, &rp->rsiz); + rp1 = (rec_hdr_ptr_t)((bytptr)rp + temp_ushort); + if (((bytptr)rp1 < (bytptr)(rp + 1) + SIZEOF(block_id)) || + ((bytptr)rp1 < buffer) || ((bytptr)rp1 > (buffer + blk_size))) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_rmisalign; + } + GET_LONG(temp_long, ((bytptr)rp1 - SIZEOF(block_id))); + if (dollar_tlevel) + { + chain1 = *(off_chain *)&temp_long; + if ((1 == chain1.flag) && ((int)chain1.cw_index >= sgm_info_ptr->cw_set_depth)) + { + assert(sgm_info_ptr->tp_csa == cs_addrs); + assert(FALSE == cs_addrs->now_crit); + return cdb_sc_blknumerr; + } + } + gvcst_delete_blk(temp_long, level - 1, FALSE); + } + } + if (kill_root) + { /* create an empty data block */ + BLK_INIT(bs_ptr, bs1); + if (!BLK_FINI(bs_ptr, bs1)) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_mkblk; + } + new_block_index = t_create(blk, (uchar_ptr_t)bs1, 0, 0, 0); + /* create index block */ + BLK_ADDR(new_rec_hdr, SIZEOF(rec_hdr), rec_hdr); + new_rec_hdr->rsiz = SIZEOF(rec_hdr) + SIZEOF(block_id); + new_rec_hdr->cmpc = 0; + BLK_INIT(bs_ptr, bs1); + BLK_SEG(bs_ptr, (bytptr)new_rec_hdr, SIZEOF(rec_hdr)); + BLK_SEG(bs_ptr, (bytptr)&zeroes, SIZEOF(block_id)); + if (!BLK_FINI(bs_ptr, bs1)) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_mkblk; + } + cse = t_write(blkhist, (unsigned char *)bs1, SIZEOF(blk_hdr) + SIZEOF(rec_hdr), new_block_index, 1, + TRUE, FALSE, GDS_WRITE_KILLTN); + assert(!dollar_tlevel || !cse->high_tlevel); + *cseptr = cse; + if (NULL != cse) + cse->first_off = 0; + return cdb_sc_normal; + } + next_rec_shrink = (int)(old_blk_hdr->bsiz + ((bytptr)del_ptr - (bytptr)right_ptr)); + if (SIZEOF(blk_hdr) >= next_rec_shrink) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_rmisalign; + } + if ((bytptr)right_ptr == (bytptr)top_of_block) + { + if (level) + { + GET_USHORT(temp_ushort, &left_ptr->rsiz); + next_rec_shrink += SIZEOF(rec_hdr) + SIZEOF(block_id) - temp_ushort; + } + } else + { + targ_base = (rmatch < lmatch) ? rmatch : lmatch; + targ_len = ((right_extra) ? right_prev_ptr->cmpc : right_ptr->cmpc) - targ_base; + if (targ_len < 0) + targ_len = 0; + prev_len = 0; + if (right_extra) + { + temp_int = right_prev_ptr->cmpc - right_ptr->cmpc; + if (0 >= temp_int) + prev_len = - temp_int; + else + { + if (temp_int < targ_len) + targ_len -= temp_int; + else + targ_len = 0; + } + } + next_rec_shrink += targ_len + prev_len; + } + BLK_INIT(bs_ptr, bs1); + first_copy = TRUE; + blkseglen = (int)((bytptr)del_ptr - (bytptr)first_in_blk); + if (0 < blkseglen) + { + if (((bytptr)right_ptr != (bytptr)top_of_block) || (0 == level)) + { + BLK_SEG(bs_ptr, (bytptr)first_in_blk, blkseglen); + first_copy = FALSE; + } else + { + blkseglen = (int)((bytptr)left_ptr - (bytptr)first_in_blk); + if (0 < blkseglen) + { + BLK_SEG(bs_ptr, (bytptr)first_in_blk, blkseglen); + first_copy = FALSE; + } + BLK_ADDR(star_rec_hdr, SIZEOF(rec_hdr), rec_hdr); + star_rec_hdr->cmpc = 0; + star_rec_hdr->rsiz = (unsigned short)(SIZEOF(rec_hdr) + SIZEOF(block_id)); + BLK_SEG(bs_ptr, (bytptr)star_rec_hdr, SIZEOF(rec_hdr)); + GET_USHORT(temp_ushort, &left_ptr->rsiz); + BLK_SEG(bs_ptr, ((bytptr)left_ptr + temp_ushort - SIZEOF(block_id)), SIZEOF(block_id)); + } + } + blkseglen = (int)((bytptr)top_of_block - (bytptr)right_ptr); + assert(0 <= blkseglen); + if (0 != blkseglen) + { + next_rec_shrink = targ_len + prev_len; + if (0 >= next_rec_shrink) + { + BLK_SEG(bs_ptr, (bytptr)right_ptr, blkseglen); + } else + { + BLK_ADDR(new_rec_hdr, SIZEOF(rec_hdr), rec_hdr); + new_rec_hdr->cmpc = right_ptr->cmpc - next_rec_shrink; + GET_USHORT(temp_ushort, &right_ptr->rsiz); + new_rec_hdr->rsiz = temp_ushort + next_rec_shrink; + BLK_SEG(bs_ptr, (bytptr)new_rec_hdr, SIZEOF(rec_hdr)); + if (targ_len) + { + BLK_ADDR(skb, targ_len, unsigned char); + memcpy(skb, &search_key->base[targ_base], targ_len); + BLK_SEG(bs_ptr, skb, targ_len); + } + if (prev_len) + BLK_SEG(bs_ptr, (bytptr)(right_prev_ptr + 1) , prev_len); + right_bytptr = (bytptr)(right_ptr + 1); + blkseglen = (int)((bytptr)top_of_block - right_bytptr); + if (0 < blkseglen) + { + BLK_SEG(bs_ptr, right_bytptr, blkseglen); + } else + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_rmisalign; + } + } + } + if (!BLK_FINI(bs_ptr, bs1)) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_mkblk; + } + cse = t_write(blkhist, (unsigned char *)bs1, 0, 0, level, first_copy, TRUE, GDS_WRITE_KILLTN); + assert(!dollar_tlevel || !cse->high_tlevel); + *cseptr = cse; + if (horiz_growth) + { + old_cse = cse->low_tlevel; + assert(old_cse && old_cse->done); + assert(2 == (SIZEOF(old_cse->undo_offset) / SIZEOF(old_cse->undo_offset[0]))); + assert(2 == (SIZEOF(old_cse->undo_next_off) / SIZEOF(old_cse->undo_next_off[0]))); + assert(!old_cse->undo_next_off[0] && !old_cse->undo_offset[0]); + assert(!old_cse->undo_next_off[1] && !old_cse->undo_offset[1]); + } + if ((NULL != cse) && (0 != cse->first_off)) + { /* fix up chains in the block to account for deleted records */ + prev = NULL; + curr = buffer + cse->first_off; + GET_LONGP(&curr_chain, curr); + while (curr < (bytptr)del_ptr) + { /* follow chain to first deleted record */ + if (0 == curr_chain.next_off) + break; + if (right_ptr == top_of_block && (bytptr)del_ptr - curr == SIZEOF(off_chain)) + break; /* special case described below: stop just before the first deleted record */ + prev = curr; + curr += curr_chain.next_off; + GET_LONGP(&curr_chain, curr); + } + if (right_ptr == top_of_block && (bytptr)del_ptr - curr == SIZEOF(off_chain)) + { + /* if the right side of the block is gone and our last chain is in the last record, + * terminate the chain and adjust the previous entry to point at the new *-key + * NOTE: this assumes there's NEVER a TP delete of records in the GVT + */ + assert(0 != level); + /* store next_off in old_cse before actually changing it in the buffer(for rolling back) */ + if (horiz_growth) + { + old_cse->undo_next_off[0] = curr_chain.next_off; + old_cse->undo_offset[0] = (block_offset)(curr - buffer); + assert(old_cse->undo_offset[0]); + } + curr_chain.next_off = 0; + GET_LONGP(curr, &curr_chain); + if (NULL != prev) + { /* adjust previous chain next_off to reflect the fact that the record it refers to is now a *-key */ + GET_LONGP(&prev_chain, prev); + /* store next_off in old_cse before actually changing it in the buffer(for rolling back) */ + if (horiz_growth) + { + old_cse->undo_next_off[1] = prev_chain.next_off; + old_cse->undo_offset[1] = (block_offset)(prev - buffer); + assert(old_cse->undo_offset[1]); + } + prev_chain.next_off = (unsigned int)((bytptr)left_ptr - prev + (unsigned int)(SIZEOF(rec_hdr))); + GET_LONGP(prev, &prev_chain); + } else /* it's the first (and only) one */ + cse->first_off = (block_offset)((bytptr)left_ptr - buffer + SIZEOF(rec_hdr)); + } else if (curr >= (bytptr)del_ptr) + { /* may be more records on the right that aren't deleted */ + while (curr < (bytptr)right_ptr) + { /* follow chain past last deleted record */ + if (0 == curr_chain.next_off) + break; + curr += curr_chain.next_off; + GET_LONGP(&curr_chain, curr); + } + /* prev : ptr to chain record immediately preceding the deleted area, + * or 0 if none. + * + * curr : ptr to chain record immediately following the deleted area, + * or to last chain record. + */ + if (curr < (bytptr)right_ptr) + { /* the former end of the chain is going, going, gone */ + if (NULL != prev) + { /* terminate the chain before the delete */ + GET_LONGP(&prev_chain, prev); + /* store next_off in old_cse before actually changing it in the buffer(for rolling back) */ + if (horiz_growth) + { + old_cse->undo_next_off[0] = prev_chain.next_off; + old_cse->undo_offset[0] = (block_offset)(prev - buffer); + assert(old_cse->undo_offset[0]); + } + prev_chain.next_off = 0; + GET_LONGP(prev, &prev_chain); + } else + cse->first_off = 0; /* the whole chain is gone */ + } else + { /* stitch up the left and right to account for the hole in the middle */ + /* next_rec_shrink is the change in record size due to the new compression count */ + if (NULL != prev) + { + GET_LONGP(&prev_chain, prev); + /* ??? new compression may be less (ie +) so why are negative shrinks ignored? */ + /* store next_off in old_cse before actually changing it in the buffer(for rolling back) */ + if (horiz_growth) + { + old_cse->undo_next_off[0] = prev_chain.next_off; + old_cse->undo_offset[0] = (block_offset)(prev - buffer); + assert(old_cse->undo_offset[0]); + } + prev_chain.next_off = (unsigned int)(curr - prev - ((bytptr)right_ptr - (bytptr)del_ptr) + + (next_rec_shrink > 0 ? next_rec_shrink : 0)); + GET_LONGP(prev, &prev_chain); + } else /* curr remains first: adjust the head */ + cse->first_off = (block_offset)(curr - buffer - ((bytptr)right_ptr - (bytptr)del_ptr) + + (next_rec_shrink > 0 ? next_rec_shrink : 0)); + } + } + } + horiz_growth = FALSE; + return cdb_sc_normal; +} diff --git a/sr_port/gvcst_kill_blk.h b/sr_port/gvcst_kill_blk.h new file mode 100644 index 0000000..1f89b7c --- /dev/null +++ b/sr_port/gvcst_kill_blk.h @@ -0,0 +1,26 @@ +/**************************************************************** + * * + * Copyright 2001, 2005 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GVCST_KILL_BLK_DEFINED + +/* Declare parms for gvcst_kill_blk.c */ + +enum cdb_sc gvcst_kill_blk (srch_blk_status *blkhist, + char level, + gv_key *search_key, + srch_rec_status low, + srch_rec_status high, + boolean_t right_extra, + cw_set_element **cseptr); + +#define GVCST_KILL_BLK_DEFINED + +#endif diff --git a/sr_port/gvcst_kill_sort.c b/sr_port/gvcst_kill_sort.c new file mode 100644 index 0000000..9746f6d --- /dev/null +++ b/sr_port/gvcst_kill_sort.c @@ -0,0 +1,90 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gdskill.h" +#include "gvcst_kill_sort.h" + +#define S_CUTOFF 15 + +void gvcst_kill_sort(kill_set *k) +{ + + block_id_ptr_t stack[50],*sp; + block_id v,t; + block_id_ptr_t l,r; + block_id_ptr_t ix,jx,kx; + + assert(k->used <= BLKS_IN_KILL_SET); + sp = stack; + l = (block_id_ptr_t)(k->blk); + r = l + k->used-1; + for (;;) + if (r - l < S_CUTOFF) + { + for (ix = l + 1 ; ix <= r ; ix++) + { + for (jx = ix , t = *ix; jx > l && *(jx-1) > t; jx--) + *jx = *(jx - 1); + *jx = t; + } + if (sp <= stack) + break; + else + { + l = *--sp; + r = *--sp; + } + } + else + { + ix = l; + jx = r; + kx = l + ((int)(r - l) / 2); + kx = (*ix > *jx) ? + ((*jx > *kx) ? + jx: + ((*ix > *kx) ? kx : ix)): + ((*jx < *kx) ? + jx: + ((*ix > *kx) ? ix : kx)); + t = *kx; + *kx = *jx; + *jx = t; + ix--; + do + { + do ix++; while (*ix < t); + do jx--; while (*jx > t); + v = *ix; + *ix = *jx; + *jx = v; + } while (jx > ix); + *jx = *ix; + *ix = *r; + *r = v; + if (ix - l > r - ix) + { + *sp++ = ix - 1; + *sp++ = l; + l = ix + 1; + } + else + { + *sp++ = r; + *sp++ = ix + 1; + r = ix - 1; + } + } + return; + +} diff --git a/sr_port/gvcst_kill_sort.h b/sr_port/gvcst_kill_sort.h new file mode 100644 index 0000000..766c491 --- /dev/null +++ b/sr_port/gvcst_kill_sort.h @@ -0,0 +1,18 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __GVCST_KILL_SORT_H__ +#define __GVCST_KILL_SORT_H__ + +void gvcst_kill_sort(kill_set *k); + +#endif + diff --git a/sr_port/gvcst_lbm_check.c b/sr_port/gvcst_lbm_check.c new file mode 100644 index 0000000..49eb11a --- /dev/null +++ b/sr_port/gvcst_lbm_check.c @@ -0,0 +1,90 @@ +/**************************************************************** + * * + * Copyright 2005, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gdskill.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdsbml.h" +#include "gvcst_lbm_check.h" + +/* Routines for checking status of database blocks using a given local bitmap */ + + + +/* There are two bits that denote the status of a block in the local bitmap: + + 0b00 busy + 0b01 free + 0b10 invalid + 0b11 recycled (but free) + + The low order bit can be used to denote busy or free so that is what + is actually tested. + + Inputs: 1) pointer to local bit map data (blk_ptr + SIZEOF(blk_hdr)). + 2) bit offset to test into bitmap. +*/ +boolean_t gvcst_blk_is_allocated(uchar_ptr_t lbmap, int lm_offset) +{ + int uchar_offset, uchar_offset_rem; + unsigned char result_byte; + uchar_ptr_t map_byte; + + assert(BLKS_PER_LMAP * BML_BITS_PER_BLK > lm_offset); + uchar_offset = lm_offset / BITS_PER_UCHAR; + uchar_offset_rem = lm_offset % BITS_PER_UCHAR; + map_byte = lbmap + uchar_offset; + result_byte = (*map_byte >> uchar_offset_rem) & 0x3; + switch(result_byte) + { + case 0x00: + return TRUE; + case 0x01: + case 0x03: + return FALSE; + default: + GTMASSERT; + } + return FALSE; /* Can't get here but keep compiler happy */ +} + +/* Similarly this routine tells if the block was EVER allocated (allocated or recycled) */ +boolean_t gvcst_blk_ever_allocated(uchar_ptr_t lbmap, int lm_offset) +{ + int uchar_offset, uchar_offset_rem; + unsigned char result_byte; + uchar_ptr_t map_byte; + + assert(BLKS_PER_LMAP * BML_BITS_PER_BLK > lm_offset); + uchar_offset = lm_offset / BITS_PER_UCHAR; + uchar_offset_rem = lm_offset % BITS_PER_UCHAR; + map_byte = lbmap + uchar_offset; + result_byte = (*map_byte >> uchar_offset_rem) & 0x3; + switch(result_byte) + { + case 0x00: + case 0x03: + return TRUE; + case 0x01: + return FALSE; + default: + GTMASSERT; + } + return FALSE; /* Can't get here but keep compiler happy */ +} diff --git a/sr_port/gvcst_lbm_check.h b/sr_port/gvcst_lbm_check.h new file mode 100644 index 0000000..cc034f8 --- /dev/null +++ b/sr_port/gvcst_lbm_check.h @@ -0,0 +1,19 @@ +/**************************************************************** + * * + * Copyright 2005 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + + +#ifndef GVCST_LBM_CHECK_H +#define GVCST_LBM_CHECK_H + +boolean_t gvcst_blk_is_allocated(uchar_ptr_t lbmap, int lm_offset); +boolean_t gvcst_blk_ever_allocated(uchar_ptr_t lbmap, int lm_offset); + +#endif diff --git a/sr_port/gvcst_lftsib.c b/sr_port/gvcst_lftsib.c new file mode 100644 index 0000000..e72320e --- /dev/null +++ b/sr_port/gvcst_lftsib.c @@ -0,0 +1,103 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "cdb_sc.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "copy.h" + +/* Include prototypes */ +#include "t_qread.h" +#include "gvcst_protos.h" /* for gvcst_search_blk,gvcst_lftsib prototype */ + +/* WARNING: assumes that the search history for the current target is in gv_target.hist */ + +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gv_namehead *gv_target; +GBLREF gv_key *gv_currkey; +GBLREF unsigned char rdfail_detail; +GBLREF srch_blk_status *first_tp_srch_status; /* overriding value of srch_blk_status given by t_qread in case of TP */ +GBLREF unsigned int t_tries; + +enum cdb_sc gvcst_lftsib(srch_hist *full_hist) +{ + srch_blk_status *old, *new, *old_base, *new_base; + rec_hdr_ptr_t rp; + unsigned short rec_size; + enum cdb_sc ret_val; + block_id blk; + unsigned short rtop, temp_short; + sm_uc_ptr_t buffer_address, bp; + int4 cycle; + + new_base = &full_hist->h[0]; + old = old_base = &gv_target->hist.h[0]; + for (;;) + { + buffer_address = old->buffaddr; + temp_short = old->prev_rec.offset; + if (temp_short > 0) + break; + old++; + if (0 == old->blk_num) + return cdb_sc_endtree; + if (old->cr && (old->blk_num != old->cr->blk)) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_lostcr; + } + if (cdb_sc_normal != (ret_val = gvcst_search_blk(gv_currkey, old))) + return ret_val; + } + /* old now points to the first block which had a non-zero prev_rec.offset */ + new = new_base + (old - old_base + 1); + full_hist->depth = (uint4)(old - old_base); + (new--)->blk_num = 0; + new->blk_num = old->blk_num; + new->tn = old->tn; + new->cse = NULL; + new->first_tp_srch_status = old->first_tp_srch_status; + assert(new->level == old->level); + assert(new->blk_target == old->blk_target); + new->buffaddr = old->buffaddr; + new->curr_rec = old->prev_rec; + new->cycle = old->cycle; + new->cr = old->cr; + temp_short = new->curr_rec.offset; + rp = (rec_hdr_ptr_t)(temp_short + new->buffaddr); + GET_USHORT(rec_size, &rp->rsiz); + rtop = temp_short + rec_size; + if (((blk_hdr_ptr_t)new->buffaddr)->bsiz < rtop) + return cdb_sc_rmisalign; + bp = new->buffaddr; + while (--new >= new_base) + { + --old; + GET_LONG(blk, ((sm_int_ptr_t)(bp + rtop - SIZEOF(block_id)))); + new->tn = cs_addrs->ti->curr_tn; + new->cse = NULL; + if (NULL == (buffer_address = t_qread(blk, &new->cycle, &new->cr))) + return((enum cdb_sc)rdfail_detail); + new->first_tp_srch_status = first_tp_srch_status; + assert(new->level == old->level); + assert(new->blk_target == old->blk_target); + new->blk_num = blk; + new->buffaddr = buffer_address; + bp = new->buffaddr; + rtop = ((blk_hdr_ptr_t)new->buffaddr)->bsiz; + } + return cdb_sc_normal; +} diff --git a/sr_port/gvcst_map_build.c b/sr_port/gvcst_map_build.c new file mode 100644 index 0000000..672a771 --- /dev/null +++ b/sr_port/gvcst_map_build.c @@ -0,0 +1,61 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsfhead.h" +#include "gdscc.h" +#include "gdsbml.h" + +#include "send_msg.h" /* prototypes */ +#include "gvcst_map_build.h" + +GBLREF gd_region *gv_cur_region; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF boolean_t dse_running; + +void gvcst_map_build(uint4 *array, sm_uc_ptr_t base_addr, cw_set_element *cs, trans_num ctn) +{ + boolean_t status; + uint4 (*bml_func)(); + uint4 bitnum, ret; +#ifdef DEBUG + int4 prev_bitnum, actual_cnt = 0; +#endif + + DEBUG_ONLY(VALIDATE_BM_BLK(cs->blk, (blk_hdr_ptr_t)base_addr, cs_addrs, gv_cur_region, status);) + assert(status); /* assert it is a valid bitmap block */ + ((blk_hdr_ptr_t)base_addr)->tn = ctn; + base_addr += SIZEOF(blk_hdr); + bml_func = (cs->reference_cnt > 0) ? bml_busy : (cs_addrs->hdr->db_got_to_v5_once ? bml_recycled : bml_free); + DEBUG_ONLY(prev_bitnum = -1;) + while (bitnum = *array) /* caution : intended assignment */ + { + assert((uint4)bitnum < cs_addrs->hdr->bplmap); /* check that bitnum is positive and within 0 to bplmap */ + assert((int4)bitnum > prev_bitnum); /* assert that blocks are sorted in the update array */ + ret = (* bml_func)(bitnum, base_addr); + DEBUG_ONLY( + if (cs->reference_cnt > 0) + actual_cnt++; /* block is being marked busy */ + else if (!ret) + actual_cnt--; /* block is transitioning from BUSY to either RECYCLED or FREE */ + /* all other state changes do not involve updates to the free_blocks count */ + ) + array++; + DEBUG_ONLY(prev_bitnum = (int4)bitnum); + } + assert(actual_cnt == cs->reference_cnt); +} diff --git a/sr_port/gvcst_map_build.h b/sr_port/gvcst_map_build.h new file mode 100644 index 0000000..bb10d2f --- /dev/null +++ b/sr_port/gvcst_map_build.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GVCST_MAP_BUILD_INCLUDED +#define GVCST_MAP_BUILD_INCLUDED + +void gvcst_map_build(uint4 *array, sm_uc_ptr_t base_addr, cw_set_element *cs, trans_num ctn); + +#endif /* GVCST_MAP_BUILD_INCLUDED */ diff --git a/sr_port/gvcst_order.c b/sr_port/gvcst_order.c new file mode 100644 index 0000000..c161be8 --- /dev/null +++ b/sr_port/gvcst_order.c @@ -0,0 +1,157 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "cdb_sc.h" +#include "copy.h" +#include "filestruct.h" /* needed for jnl.h */ +#include "gdscc.h" /* needed for tp.h */ +#include "jnl.h" /* needed for tp.h */ +#include "gdskill.h" /* needed for tp.h */ +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ + +#include "t_end.h" /* prototypes */ +#include "t_retry.h" +#include "t_begin.h" +#include "gvcst_protos.h" /* for gvcst_rtsib,gvcst_search,gvcst_search_blk,gvcst_order prototype */ + +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gd_region *gv_cur_region; +GBLREF gv_namehead *gv_target; +GBLREF gv_key *gv_currkey, *gv_altkey; +GBLREF int4 gv_keysize; +GBLREF uint4 dollar_tlevel; +GBLREF unsigned int t_tries; + +bool gvcst_order(void) +{ + blk_hdr_ptr_t bp; + bool found, two_histories; + enum cdb_sc status; + rec_hdr_ptr_t rp; + unsigned short rec_size; + srch_blk_status *bh; + srch_hist *rt_history; + sm_uc_ptr_t c1, c2, ctop, alt_top; + + T_BEGIN_READ_NONTP_OR_TP(ERR_GVORDERFAIL); + for (;;) + { + assert(t_tries < CDB_STAGNATE || cs_addrs->now_crit); /* we better hold crit in the final retry (TP & non-TP) */ + two_histories = FALSE; + if (cdb_sc_normal == (status = gvcst_search(gv_currkey, NULL))) + { + found = TRUE; + bh = gv_target->hist.h; + rp = (rec_hdr_ptr_t)(bh->buffaddr + bh->curr_rec.offset); + bp = (blk_hdr_ptr_t)bh->buffaddr; + if ((rec_hdr_ptr_t)CST_TOB(bp) <= rp) + { + two_histories = TRUE; + rt_history = gv_target->alt_hist; + status = gvcst_rtsib(rt_history, 0); + if (cdb_sc_normal == status) + { + bh = rt_history->h; + if (cdb_sc_normal != (status = gvcst_search_blk(gv_currkey, bh))) + { + t_retry(status); + continue; + } + rp = (rec_hdr_ptr_t)(bh->buffaddr + bh->curr_rec.offset); + bp = (blk_hdr_ptr_t)bh->buffaddr; + } else + { + if (cdb_sc_endtree == status) + { + found = FALSE; + two_histories = FALSE; /* second history not valid */ + } else + { + t_retry(status); + continue; + } + } + } + if (found) + { + assert(gv_altkey->top == gv_currkey->top); + assert(gv_altkey->top == gv_keysize); + assert(gv_altkey->end < gv_altkey->top); + /* store new subscipt */ + c1 = gv_altkey->base; + alt_top = gv_altkey->base + gv_altkey->top - 1; + /* Make alt_top one less than gv_altkey->top to allow double-null at end of a key-name */ + /* 4/17/96 + * HP compiler bug work-around. The original statement was + * c2 = (unsigned char *)CST_BOK(rp) + bh->curr_rec.match - rp->cmpc; + * + * ...but this was sometimes compiled incorrectly (the lower 4 bits + * of rp->cmpc, sign extended, were subtracted from bh->curr_rec.match). + * I separated out the subtraction of rp->cmpc. + * + * -VTF. + */ + c2 = (sm_uc_ptr_t)CST_BOK(rp) + bh->curr_rec.match; + memcpy(c1, gv_currkey->base, bh->curr_rec.match); + c1 += bh->curr_rec.match; + c2 -= rp->cmpc; + GET_USHORT(rec_size, &rp->rsiz); + ctop = (sm_uc_ptr_t)rp + rec_size; + for (;;) + { + if (c2 >= ctop || c1 >= alt_top) + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_rmisalign; + goto restart; /* goto needed because of nested FOR loop */ + } + if (0 == (*c1++ = *c2++)) + { + *c1 = 0; + break; + } + } + gv_altkey->end = c1 - gv_altkey->base; + assert(gv_altkey->end < gv_altkey->top); + } + if (!dollar_tlevel) + { + if ((trans_num)0 == t_end(&gv_target->hist, two_histories ? rt_history : NULL, TN_NOT_SPECIFIED)) + continue; + } else + { + status = tp_hist(two_histories ? rt_history : NULL); + if (cdb_sc_normal != status) + { + t_retry(status); + continue; + } + } + assert(cs_data == cs_addrs->hdr); + INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_order, 1); + return (found && (bh->curr_rec.match >= gv_currkey->prev)); + } +restart: t_retry(status); + } +} diff --git a/sr_port/gvcst_protos.h b/sr_port/gvcst_protos.h new file mode 100644 index 0000000..e20cba5 --- /dev/null +++ b/sr_port/gvcst_protos.h @@ -0,0 +1,41 @@ +/**************************************************************** + * * + * Copyright 2004, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* collection of prototypes of gvcst_* modules that need a bare minimum of mdef.h & gdsfhead.h to be already included */ + +#ifndef GVCST_PROTOS_H_INCLUDED +#define GVCST_PROTOS_H_INCLUDED + +void db_auto_upgrade(gd_region *reg); +void db_init(gd_region *reg, sgmnt_data_ptr_t tsd); +gd_region *dbfilopn (gd_region *reg); +void dbsecspc(gd_region *reg, sgmnt_data_ptr_t csd, gtm_uint64_t *sec_size); +mint gvcst_data(void); +enum cdb_sc gvcst_dataget(mint *dollar_data, mval *val); +bool gvcst_gblmod(mval *v); +boolean_t gvcst_get(mval *v); +void gvcst_incr(mval *increment, mval *result); +void gvcst_init(gd_region *greg); +void gvcst_kill(bool do_subtree); +enum cdb_sc gvcst_lftsib(srch_hist *full_hist); +bool gvcst_order(void); +void gvcst_put(mval *v); +bool gvcst_query(void); +boolean_t gvcst_queryget(mval *val); +void gvcst_root_search(void); +enum cdb_sc gvcst_rtsib(srch_hist *full_hist, int level); +enum cdb_sc gvcst_search(gv_key *pKey, srch_hist *pHist); +enum cdb_sc gvcst_search_blk(gv_key *pKey, srch_blk_status *pStat); +enum cdb_sc gvcst_search_tail(gv_key *pKey, srch_blk_status *pStat, gv_key *pOldKey); +void gvcst_tp_init(gd_region *); +bool gvcst_zprevious(void); + +#endif diff --git a/sr_port/gvcst_put.c b/sr_port/gvcst_put.c new file mode 100644 index 0000000..a54fadc --- /dev/null +++ b/sr_port/gvcst_put.c @@ -0,0 +1,2225 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include +#include "gtm_stdio.h" +#include "gtm_stdlib.h" +#include "gtm_string.h" +#include "gtm_inet.h" /* Required for gtmsource.h */ + +#ifdef VMS +#include /* Required for gtmsource.h */ +#endif + +#include "gdsroot.h" +#include "gdskill.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "cdb_sc.h" +#include "min_max.h" /* needed for gdsblkops.h */ +#include "gdsblkops.h" +#include "jnl.h" +#include "gdscc.h" +#include "copy.h" +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" +#include "rc_oflow.h" +#include "repl_msg.h" +#include "gtmsource.h" +#include "rtnhdr.h" +#include "stack_frame.h" +#ifdef GTM_TRIGGER +# include "gv_trigger.h" +# include "gtm_trigger.h" +# include "gv_trigger_protos.h" +# include "subscript.h" +# include "mv_stent.h" +# include "stringpool.h" +#endif +#include "tp_frame.h" +#include "tp_restart.h" + +/* Include prototypes */ +#include "t_write.h" +#include "t_write_root.h" +#include "t_end.h" +#include "t_retry.h" +#include "t_begin.h" +#include "t_create.h" +#include "gvcst_blk_build.h" +#include "gvcst_expand_key.h" +#include "gvcst_protos.h" /* for gvcst_search,gvcst_search_blk,gvcst_put prototype */ +#include "op.h" /* for op_add & op_tstart prototype */ +#include "format_targ_key.h" /* for format_targ_key prototype */ +#include "gvsub2str.h" /* for gvsub2str prototype */ +#include "tp_set_sgm.h" /* for tp_set_sgm prototype */ +#include "op_tcommit.h" /* for op_tcommit prototype */ +#include "have_crit.h" + +#ifdef GTM_TRIGGER +LITREF mval literal_null; +LITREF mval literal_one; +LITREF mval literal_zero; +#endif + +/* Globals that will not change in value across nested trigger calls of gvcst_put OR even if they might change in value, + * the change is such that they dont need save/restore logic surrounding the "gtm_trigger" call. Any new GBLREFs that are + * added in this module need to be examined for interference between gvcst_put and nested trigger call and any save/restore + * logic (if needed) should be appropriately added surrounding the "gtm_trigger" invocation. + */ +GBLREF boolean_t gvdupsetnoop; /* if TRUE, duplicate SETs update journal but not database (except for curr_tn++) */ +GBLREF boolean_t horiz_growth; +GBLREF boolean_t in_gvcst_incr; +GBLREF char *update_array, *update_array_ptr; +GBLREF gv_key *gv_altkey; +GBLREF gv_namehead *reset_gv_target; +GBLREF inctn_opcode_t inctn_opcode; +GBLREF int gv_fillfactor; +GBLREF int rc_set_fragment; /* Contains offset within data at which data fragment starts */ +GBLREF int4 gv_keysize; +GBLREF int4 prev_first_off, prev_next_off; +GBLREF uint4 update_trans; +GBLREF jnl_format_buffer *non_tp_jfb_ptr; +GBLREF jnl_gbls_t jgbl; +GBLREF jnlpool_addrs jnlpool; +GBLREF uint4 dollar_tlevel; +GBLREF uint4 process_id; +GBLREF uint4 update_array_size, cumul_update_array_size; /* the current total size of the update array */ +GBLREF unsigned char t_fail_hist[CDB_MAX_TRIES]; +GBLREF unsigned int t_tries; +GBLREF cw_set_element cw_set[CDB_CW_SET_SIZE];/* create write set. */ +GBLREF boolean_t skip_dbtriggers; /* see gbldefs.c for description of this global */ +GBLREF stack_frame *frame_pointer; +#ifdef GTM_TRIGGER +GBLREF int tprestart_state; +GBLREF int4 gtm_trigger_depth; +GBLREF int4 tstart_trigger_depth; +GBLREF boolean_t skip_INVOKE_RESTART; +GBLREF boolean_t ztwormhole_used; /* TRUE if $ztwormhole was used by trigger code */ +#endif +#ifdef DEBUG +GBLREF boolean_t skip_block_chain_tail_check; +#endif + +/* Globals that could change in value across nested trigger calls of gvcst_put AND need to be saved/restored */ +GBLREF boolean_t is_dollar_incr; +GBLREF gd_region *gv_cur_region; +GBLREF gv_key *gv_currkey; +GBLREF gv_namehead *gv_target; +GBLREF mval *post_incr_mval; +GBLREF mval increment_delta_mval; +GBLREF sgm_info *sgm_info_ptr; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; + +error_def(ERR_GVINCRISOLATION); +error_def(ERR_GVIS); +error_def(ERR_GVPUTFAIL); +error_def(ERR_REC2BIG); +error_def(ERR_RSVDBYTE2HIGH); +error_def(ERR_TPRETRY); + +/* Before issuing an error, add GVT to the list of known gvts in this TP transaction in case it is not already done. + * This GVT addition is usually done by "tp_hist" but that function has most likely not yet been invoked in gvcst_put. + * Doing this addition will ensure we remember to reset any non-zero clue in dir_tree as part of tp_clean_up when a TROLLBACK + * or TRESTART (implicit or explicit) occurs. Not doing so could cause transfer of control from the current gvcst_put action + * to a user-defined error trap which if it does further database references, it could end up using invalid clues from GVT + * and potentially incorrectly commit the transaction causing db integ errors as well. + */ +#define ENSURE_VALUE_WITHIN_MAX_REC_SIZE(value, GVT) \ +{ \ + if (dollar_tlevel) \ + ADD_TO_GVT_TP_LIST(GVT); /* note: macro also updates read_local_tn if necessary */ \ + if (gv_currkey->end + 1 + value.len + SIZEOF(rec_hdr) > gv_cur_region->max_rec_size) \ + { \ + if (0 == (end = format_targ_key(buff, MAX_ZWR_KEY_SZ, gv_currkey, TRUE))) \ + end = &buff[MAX_ZWR_KEY_SZ - 1]; \ + rts_error(VARLSTCNT(10) ERR_REC2BIG, 4, gv_currkey->end + 1 + value.len + SIZEOF(rec_hdr), \ + (int4)gv_cur_region->max_rec_size, \ + REG_LEN_STR(gv_cur_region), ERR_GVIS, 2, end - buff, buff); \ + } \ +} + +/* See comment before ENSURE_VALUE_WITHIN_MAX_REC_SIZE macro definition for why the ADD_TO_GVT_TP_LIST call below is necessary */ +#define ISSUE_RSVDBYTE2HIGH_ERROR(GVT) \ +{ \ + if (dollar_tlevel) \ + ADD_TO_GVT_TP_LIST(GVT); /* note: macro also updates read_local_tn if necessary */ \ + /* The record that is newly inserted/updated does not fit by itself in a separate block \ + * if the current reserved-bytes for this database is taken into account. Cannot go on. \ + */ \ + if (0 == (end = format_targ_key(buff, MAX_ZWR_KEY_SZ, gv_currkey, TRUE))) \ + end = &buff[MAX_ZWR_KEY_SZ - 1]; \ + rts_error(VARLSTCNT(11) ERR_RSVDBYTE2HIGH, 5, new_blk_size_single, \ + REG_LEN_STR(gv_cur_region), blk_size, blk_reserved_bytes, \ + ERR_GVIS, 2, end - buff, buff); \ +} + +#define RESTORE_ZERO_GVT_ROOT_ON_RETRY(LCL_ROOT, GV_TARGET, TP_ROOT, DIR_HIST, DIR_TREE) \ +{ \ + if (!LCL_ROOT) \ + { \ + assert(NULL != DIR_HIST); \ + assert(DIR_TREE == GV_TARGET->gd_csa->dir_tree); \ + /* t_retry only resets gv_target->clue and not the clue of the directory tree. \ + * But DIR_HIST non-null implies the directory tree was used in a gvcst_search and hence \ + * was validated (in t_end/tp_hist),so we need to reset its clue before the next try. \ + */ \ + DIR_TREE->clue.end = 0; \ + /* Check if LCL_ROOT & GV_TARGET->root are in sync. If not make them so. */ \ + if (GV_TARGET->root) \ + { /* We had reset the root block from zero to a non-zero value within \ + * this function, but since we are restarting, we can no longer be \ + * sure of the validity of the root block. Reset it to 0 so it will \ + * be re-determined in the next global reference. \ + */ \ + assert((TP_ROOT == GV_TARGET->root) \ + || ((0 == TP_ROOT) GTMTRIG_ONLY(&& (0 < gvtr_parms.num_triggers_invoked)))); \ + GV_TARGET->root = 0; \ + } \ + } \ +} + +#ifdef DEBUG +# define DBG_SAVE_VAL_AT_FUN_ENTRY \ +{ /* Save copy of "val" at function entry. \ + * Make sure this is not touched by any nested trigger code */ \ + dbg_lcl_val = val; \ + dbg_vallen = val->str.len; \ + memcpy(dbg_valbuff, val->str.addr, MIN(ARRAYSIZE(dbg_valbuff), dbg_vallen)); \ +} + +# define DBG_CHECK_VAL_AT_FUN_EXIT \ +{ /* Check "val" is same as what it was at function entry.(i.e. was not touched by nested trigger code). \ + * The only exception is if $ZTVAL changed "val" in which case gvcst_put would have been redone. */ \ + assert(dbg_vallen == dbg_lcl_val->str.len); \ + assert(0 == memcmp(dbg_valbuff, dbg_lcl_val->str.addr, MIN(ARRAYSIZE(dbg_valbuff), dbg_vallen))); \ +} +#else +# define DBG_SAVE_VAL_AT_FUN_ENTRY +# define DBG_CHECK_VAL_AT_FUN_EXIT +#endif + +#define GOTO_RETRY \ +{ \ + GTMTRIG_DBG_ONLY(dbg_trace_array[dbg_num_iters].retry_line = __LINE__); \ + goto retry; \ +} + +void gvcst_put(mval *val) +{ + sgmnt_addrs *csa; + sgmnt_data_ptr_t csd; + node_local_ptr_t cnl; + int4 blk_size, blk_fill_size, blk_reserved_bytes; + const int4 zeroes = 0; + boolean_t jnl_format_done; + blk_segment *bs1, *bs_ptr, *new_blk_bs; + block_id allocation_clue, tp_root, gvt_for_root, blk_num, last_split_blk_num[MAX_BT_DEPTH]; + block_index left_hand_index, ins_chain_index, root_blk_cw_index, next_blk_index; + block_offset next_offset, first_offset, ins_off1, ins_off2, old_curr_chain_next_off; + cw_set_element *cse, *cse_new, *old_cse; + gv_namehead *save_targ, *split_targ, *dir_tree; + enum cdb_sc status; + gv_key *temp_key; + mstr value; + off_chain chain1, curr_chain, prev_chain, chain2; + rec_hdr_ptr_t curr_rec_hdr, extra_rec_hdr, next_rec_hdr, new_star_hdr, rp; + srch_blk_status *bh, *bq, *tp_srch_status; + srch_hist *dir_hist; + int cur_blk_size, blk_seg_cnt, delta, i, j, left_hand_offset, n, ins_chain_offset, + new_blk_size_l, new_blk_size_r, new_blk_size_single, new_blk_size, blk_reserved_size, + last_possible_left_offset, new_rec_size, next_rec_shrink, next_rec_shrink1, + offset_sum, rec_cmpc, target_key_size, tp_lev, undo_index, cur_val_offset, curr_offset, bh_level; + uint4 segment_update_array_size, key_top, cp2_len, bs1_2_len, bs1_3_len; + char *va, last_split_direction[MAX_BT_DEPTH]; + sm_uc_ptr_t cp1, cp2, curr; + unsigned short extra_record_orig_size, rec_size, temp_short; + unsigned int prev_rec_offset, prev_rec_match, curr_rec_offset, curr_rec_match; + boolean_t copy_extra_record, level_0, new_rec, no_pointers, succeeded, key_exists; + boolean_t make_it_null, gbl_target_was_set, duplicate_set, new_rec_goes_to_right, need_extra_block_split; + key_cum_value *tempkv; + jnl_format_buffer *jfb, *ztworm_jfb; + jnl_action *ja; + mval *set_val; /* actual right-hand-side value of the SET or $INCR command */ + ht_ent_int4 *tabent; + unsigned char buff[MAX_ZWR_KEY_SZ], *end, old_ch, new_ch; + sm_uc_ptr_t buffaddr; + block_id lcl_root, last_split_bnum; + sgm_info *si; + uint4 nodeflags; + boolean_t write_logical_jnlrecs, can_write_logical_jnlrecs, blk_match, is_split_dir_left; + int split_depth; + mval *ja_val; + int rc; + int4 cse_first_off; + enum split_dir last_split_dir; +# ifdef GTM_TRIGGER + boolean_t is_tpwrap; + boolean_t ztval_gvcst_put_redo, skip_hasht_read; + gtm_trigger_parms trigparms; + gvt_trigger_t *gvt_trigger; + gvtr_invoke_parms_t gvtr_parms; + int gtm_trig_status; + int4 data_len; + unsigned char *save_msp; + mv_stent *save_mv_chain; + mval *ztold_mval = NULL; + mval *ztval_mval; + boolean_t lcl_implicit_tstart; /* local copy of the global variable "implicit_tstart" */ + mval lcl_increment_delta_mval; /* local copy of "increment_delta_mval" */ + boolean_t lcl_is_dollar_incr; /* local copy of is_dollar_incr taken at start of module. + * used to restore is_dollar_incr in case of TP restarts */ + mval *lcl_post_incr_mval; /* local copy of "post_incr_mval" at function entry. + * used to restore "post_incr_mval" in case of TP restarts */ + mval *lcl_val; /* local copy of "val" at function entry. + * used to restore "val" in case of TP restarts */ +# endif +# ifdef DEBUG + char dbg_valbuff[256]; + mstr_len_t dbg_vallen; + mval *dbg_lcl_val; + int dbg_num_iters = -1; /* number of iterations through gvcst_put */ + int lcl_dollar_tlevel, lcl_t_tries; + typedef struct + { + unsigned int t_tries; + int retry_line; + boolean_t is_fresh_tn_start; + boolean_t is_dollar_incr; + boolean_t ztval_gvcst_put_redo; + boolean_t is_extra_block_split; + mval *val; + boolean_t lcl_implicit_tstart; + } dbg_trace; + /* We want to capture all pertinent information across each iteration of gvcst_put. + * There are 3 things that can contribute to a new iteration. + * a) restarts from the primary set. + * Max of 4 iterations. + * b) extra_block_split from the primary set. It can have its own set of restarts too. + * Max of 4 iterations per extra_block_split. + * The # of extra block splits could be arbitrary in case of non-TP but cannot be more than 1 for TP + * because in TP, we would have grabbed crit in the final retry and prevent any more concurrent updates. + * c) ztval_gvcst_put_redo. This in turn can have its own set of restarts and extra_block_split iterations. + * Could take a max of (a) + (b) = 4 + 4 = 8 iterations. + * Total of 16 max iterations. If ever a transaction goes for more than this # of iterations (theoretically + * possible in non-TP if a lot of extra block splits occur), we assert fail. + */ + dbg_trace dbg_trace_array[16]; + boolean_t is_fresh_tn_start; + boolean_t is_mm; +# endif + + is_dollar_incr = in_gvcst_incr; + in_gvcst_incr = FALSE; + csa = cs_addrs; + csd = csa->hdr; + cnl = csa->nl; + assert(csd == cs_data); + DEBUG_ONLY(is_mm = (dba_mm == csd->acc_meth);) +# ifdef GTM_TRIGGER + TRIG_CHECK_REPLSTATE_MATCHES_EXPLICIT_UPDATE(gv_cur_region, csa); + assert(!dollar_tlevel || (tstart_trigger_depth <= gtm_trigger_depth)); + if (!dollar_tlevel || (gtm_trigger_depth == tstart_trigger_depth)) + { /* This is an explicit update. Set ztwormhole_used to FALSE. Note that we initialize this only at the + * beginning of the transaction and not at the beginning of each try/retry. If the application used + * $ztwormhole in any retsarting try of the transaction, we consider it necessary to write the + * TZTWORM/UZTWORM record even though it was not used in the succeeding/committing try. + */ + ztwormhole_used = FALSE; + } +# endif + JNLPOOL_INIT_IF_NEEDED(csa, csd, cnl); + blk_size = csd->blk_size; + blk_reserved_bytes = csd->reserved_bytes; + blk_fill_size = (blk_size * gv_fillfactor) / 100 - blk_reserved_bytes; + jnl_format_done = FALSE; /* do "jnl_format" only once per logical non-tp transaction irrespective of number of retries */ + GTMTRIG_ONLY( + ztval_gvcst_put_redo = FALSE; + skip_hasht_read = FALSE; + ) + assert(('\0' != gv_currkey->base[0]) && gv_currkey->end); + DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC; + /* this needs to be initialized before any code that does a "goto retry" since this gets used there */ + save_targ = gv_target; + gbl_target_was_set = (INVALID_GV_TARGET != reset_gv_target); + if (INVALID_GV_TARGET != reset_gv_target) + gbl_target_was_set = TRUE; + else + { + gbl_target_was_set = FALSE; + reset_gv_target = save_targ; + } + DBG_SAVE_VAL_AT_FUN_ENTRY; + GTMTRIG_ONLY( + lcl_implicit_tstart = FALSE; + DEBUG_ONLY(gvtr_parms.num_triggers_invoked = -1;) /* set to an out-of-design value; checked by an assert */ + ) + DEBUG_ONLY( + status = cdb_sc_normal; + lcl_dollar_tlevel = dollar_tlevel; + ) +fresh_tn_start: + DEBUG_ONLY(lcl_t_tries = -1;) + DEBUG_ONLY(is_fresh_tn_start = TRUE;) + assert(!jnl_format_done || (dollar_tlevel GTMTRIG_ONLY(&& ztval_gvcst_put_redo))); + T_BEGIN_SETORKILL_NONTP_OR_TP(ERR_GVPUTFAIL); +tn_restart: + /* t_tries should never decrease - it either increases or stays the same. If should decrease we could live-lock with + * an oscillating t_tries and never reach CDB_STAGNATE (go from optimistic to pessimistic concurrency). Since we + * typically do a normal increment and then, for certain conditions, do a complementary decrement, we assert that + * the net effect is never a decrease. + */ + assert(csa == cs_addrs); /* no amount of retries should change cs_addrs from what it was at entry into gvcst_put */ + assert((((int)t_tries) > lcl_t_tries) || (CDB_STAGNATE == t_tries)); + DEBUG_ONLY(lcl_t_tries = t_tries;) /* update lcl_t_tries */ + DEBUG_ONLY( + dbg_num_iters++; + assert(dbg_num_iters < ARRAYSIZE(dbg_trace_array)); + dbg_trace_array[dbg_num_iters].is_fresh_tn_start = is_fresh_tn_start; + dbg_trace_array[dbg_num_iters].t_tries = t_tries; + is_fresh_tn_start = FALSE; + dbg_trace_array[dbg_num_iters].is_dollar_incr = is_dollar_incr; + GTMTRIG_ONLY(dbg_trace_array[dbg_num_iters].ztval_gvcst_put_redo = ztval_gvcst_put_redo;) + dbg_trace_array[dbg_num_iters].val = val; + GTMTRIG_ONLY(dbg_trace_array[dbg_num_iters].lcl_implicit_tstart = lcl_implicit_tstart;) + dbg_trace_array[dbg_num_iters].is_extra_block_split = FALSE; + dbg_trace_array[dbg_num_iters].retry_line = 0; + split_targ = NULL; + ) + /* If MM and file extension occurred, reset csd to cs_data to avoid out-of-date value. If BG we dont need the reset + * but if checks are costlier than unconditional sets in a pipelined architecture so we choose not to do the if. + */ + assert(is_mm || (csd == cs_data)); + csd = cs_data; +# ifdef GTM_TRIGGER + gvtr_parms.num_triggers_invoked = 0; /* clear any leftover value */ + assert(!ztval_gvcst_put_redo || IS_PTR_INSIDE_M_STACK(val)); + is_tpwrap = FALSE; + if (!skip_dbtriggers && !skip_hasht_read) + { + GVTR_INIT_AND_TPWRAP_IF_NEEDED(csa, csd, gv_target, gvt_trigger, lcl_implicit_tstart, is_tpwrap, ERR_GVPUTFAIL); + assert(gvt_trigger == gv_target->gvt_trigger); + if (is_tpwrap) + { /* The above call to GVTR_INIT* macro created a TP transaction (by invoking op_tstart). + * Save all pertinent global variable information that needs to be restored in case of + * a restart. Note that the restart could happen in a nested trigger so these global + * variables could have changed in value from what they were at gvcst_put entry, hence + * the need to save/restore them. If this is not an implicitly tp wrapped transaction, + * there is no need to do this save/restore because a restart will transfer control + * back to the M code corresponding to the start of the transaction which would + * automatically initialize these global variables to the appropriate values. + */ + assert(lcl_implicit_tstart); + lcl_is_dollar_incr = is_dollar_incr; + lcl_val = val; + lcl_post_incr_mval = post_incr_mval; + lcl_increment_delta_mval = increment_delta_mval; + } + if (NULL != gvt_trigger) + PUSH_ZTOLDMVAL_ON_M_STACK(ztold_mval, save_msp, save_mv_chain); + } +# endif + assert(csd == cs_data); /* assert csd is in sync with cs_data even if there were MM db file extensions */ + si = sgm_info_ptr; /* Cannot be moved before GVTR_INIT_AND_TPWRAP_IF_NEEDED macro since we could enter gvcst_put + * with sgm_info_ptr NULL but could tpwrap a non-tp transaction due to triggers. In that case + * we want the updated sgm_info_ptr to be noted down in si and used later. + */ + assert((NULL == si) || (si->update_trans)); + assert(NULL != update_array); + assert(NULL != update_array_ptr); + assert(0 != update_array_size); + assert(update_array + update_array_size >= update_array_ptr); + /* When the following two asserts trip, we should change the data types of prev_first_off + * and prev_next_off, so they satisfy the assert. + */ + assert(SIZEOF(prev_first_off) >= SIZEOF(block_offset)); + assert(SIZEOF(prev_next_off) >= SIZEOF(block_offset)); + prev_first_off = prev_next_off = PREV_OFF_INVALID; + horiz_growth = FALSE; + assert(t_tries < CDB_STAGNATE || csa->now_crit); /* we better hold crit in the final retry (TP & non-TP) */ + /* level_0 == true and no_pointers == false means that this is a directory tree data block containing pointers to roots */ + level_0 = no_pointers = TRUE; + assert(gv_altkey->top == gv_currkey->top); + assert(gv_altkey->top == gv_keysize); + assert(gv_currkey->end < gv_currkey->top); + assert(gv_altkey->end < gv_altkey->top); + temp_key = gv_currkey; + dir_hist = NULL; + ins_chain_index = 0; + lcl_root = gv_target->root; + tp_root = lcl_root; + if (!dollar_tlevel) + { + CHECK_AND_RESET_UPDATE_ARRAY; /* reset update_array_ptr to update_array */ + } else + { + segment_update_array_size = UA_NON_BM_SIZE(csd); + ENSURE_UPDATE_ARRAY_SPACE(segment_update_array_size); + curr_chain = *(off_chain *)&lcl_root; + if (curr_chain.flag == 1) + { + tp_get_cw(si->first_cw_set, (int)curr_chain.cw_index, &cse); + tp_root = cse->blk; + } + } + if (0 == tp_root) + { /* Global does not exist as far as we know. Creating a new one requires validating the directory tree path which + * led us to this conclusion. So scan the directory tree here and validate its history at the end of this function. + * If we decide to restart due to a concurrency conflict, remember to reset gv_target->root to 0 before restarting. + */ + gv_target = dir_tree = csa->dir_tree; + for (cp1 = temp_key->base, cp2 = gv_altkey->base; 0 != *cp1;) + *cp2++ = *cp1++; + *cp2++ = 0; + *cp2 = 0; + gv_altkey->end = cp2 - gv_altkey->base; + assert(gv_altkey->end <= gv_altkey->top); + dir_hist = &gv_target->hist; + status = gvcst_search(gv_altkey, NULL); + RESET_GV_TARGET_LCL(save_targ); + if (cdb_sc_normal != status) + GOTO_RETRY; + if (gv_altkey->end + 1 == dir_hist->h[0].curr_rec.match) + { + GET_LONG(tp_root, (dir_hist->h[0].buffaddr + SIZEOF(rec_hdr) + + dir_hist->h[0].curr_rec.offset + gv_altkey->end + 1 + - ((rec_hdr_ptr_t)(dir_hist->h[0].buffaddr + dir_hist->h[0].curr_rec.offset))->cmpc)); + if (dollar_tlevel) + { + gvt_for_root = dir_hist->h[0].blk_num; + curr_chain = *(off_chain *)&gvt_for_root; + if (curr_chain.flag == 1) + tp_get_cw(si->first_cw_set, curr_chain.cw_index, &cse); + else + { + if (NULL != (tabent = lookup_hashtab_int4(si->blks_in_use, (uint4 *)&gvt_for_root))) + tp_srch_status = tabent->value; + else + tp_srch_status = NULL; + cse = tp_srch_status ? tp_srch_status->cse : NULL; + } + assert(!cse || !cse->high_tlevel); + } + assert(0 == gv_target->root); + gv_target->root = tp_root; + } + } + blk_reserved_size = blk_size - blk_reserved_bytes; + if (0 == tp_root) + { /* there is no entry in the GVT (and no root), so create a new empty tree and put the name in the GVT */ + /* Create the data block */ + key_exists = FALSE; + if (is_dollar_incr) + { /* The global variable that is being $INCREMENTed does not exist. + * $INCREMENT() should not signal UNDEF error but proceed with an implicit $GET(). + */ + assert(dollar_tlevel ? si->update_trans : update_trans); + *post_incr_mval = *val; + MV_FORCE_NUM(post_incr_mval); + post_incr_mval->mvtype &= ~MV_STR; /* needed to force any alphanumeric string to numeric */ + MV_FORCE_STR(post_incr_mval); + assert(post_incr_mval->str.len); + value = post_incr_mval->str; + /* The MAX_REC_SIZE check could not be done in op_gvincr (like is done in op_gvput) because + * the post-increment value is not known until here. so do the check here. + */ + ENSURE_VALUE_WITHIN_MAX_REC_SIZE(value, dir_tree); + } else + value = val->str; + /* Potential size of a GVT leaf block containing just the new/updated record */ + new_blk_size_single = SIZEOF(blk_hdr) + SIZEOF(rec_hdr) + temp_key->end + 1 + value.len; + if (new_blk_size_single > blk_reserved_size) + { /* The record that is newly inserted/updated does not fit by itself in a separate block + * if the current reserved-bytes for this database is taken into account. Cannot go on. + */ + ISSUE_RSVDBYTE2HIGH_ERROR(dir_tree); + } + BLK_ADDR(curr_rec_hdr, SIZEOF(rec_hdr), rec_hdr); + curr_rec_hdr->rsiz = SIZEOF(rec_hdr) + temp_key->end + 1 + value.len; + curr_rec_hdr->cmpc = 0; + BLK_INIT(bs_ptr, new_blk_bs); + BLK_SEG(bs_ptr, (sm_uc_ptr_t)curr_rec_hdr, SIZEOF(rec_hdr)); + BLK_ADDR(cp1, temp_key->end + 1, unsigned char); + memcpy(cp1, temp_key->base, temp_key->end + 1); + BLK_SEG(bs_ptr, cp1, temp_key->end + 1); + if (0 != value.len) + { + BLK_ADDR(va, value.len, char); + memcpy(va, value.addr, value.len); + BLK_SEG(bs_ptr, (unsigned char *)va, value.len); + } + if (0 == BLK_FINI(bs_ptr, new_blk_bs)) + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_mkblk; + GOTO_RETRY; + } + assert(new_blk_bs[0].len <= blk_reserved_size); /* Assert that new block has space for reserved bytes */ + /* Create the index block */ + BLK_ADDR(curr_rec_hdr, SIZEOF(rec_hdr), rec_hdr); + curr_rec_hdr->rsiz = BSTAR_REC_SIZE; + curr_rec_hdr->cmpc = 0; + BLK_INIT(bs_ptr, bs1); + BLK_SEG(bs_ptr, (sm_uc_ptr_t)curr_rec_hdr, SIZEOF(rec_hdr)); + BLK_SEG(bs_ptr, (unsigned char *)&zeroes, SIZEOF(block_id)); + if (0 == BLK_FINI(bs_ptr, bs1)) + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_mkblk; + GOTO_RETRY; + } + assert(bs1[0].len <= blk_reserved_size); /* Assert that new block has space for reserved bytes */ + allocation_clue = ALLOCATION_CLUE(csd->trans_hist.total_blks); + next_blk_index = t_create(allocation_clue, (uchar_ptr_t)new_blk_bs, 0, 0, 0); + ++allocation_clue; + ins_chain_index = t_create(allocation_clue, (uchar_ptr_t)bs1, SIZEOF(blk_hdr) + SIZEOF(rec_hdr), next_blk_index, 1); + root_blk_cw_index = ins_chain_index; + temp_key = gv_altkey; + gv_target->hist.h[0].blk_num = HIST_TERMINATOR; + gv_target = dir_tree; + bh = &gv_target->hist.h[0]; + value.len = SIZEOF(block_id); + value.addr = (char *)&zeroes; + no_pointers = FALSE; + } else + { + if (cdb_sc_normal != (status = gvcst_search(gv_currkey, NULL))) + GOTO_RETRY; + target_key_size = gv_currkey->end + 1; + bh = &gv_target->hist.h[0]; + key_exists = (target_key_size == bh->curr_rec.match); + if (is_dollar_incr) + { + if (key_exists) + { /* $INCR is being done on an existing global variable key in the database. + * the value to set the key to has to be determined by adding the existing value + * with the increment passed as the input parameter "val" (of type (mval *)) to gvcst_put + */ + if (cdb_sc_normal != (status = gvincr_compute_post_incr(bh))) + { + assert(CDB_STAGNATE > t_tries); + GOTO_RETRY; + } + } else + { /* The global variable that is being $INCREMENTed does not exist. $INCREMENT() should not + * signal UNDEF error but proceed with an implicit $GET() */ + *post_incr_mval = *val; + MV_FORCE_NUM(post_incr_mval); + post_incr_mval->mvtype &= ~MV_STR; /* needed to force any alphanumeric string to numeric */ + MV_FORCE_STR(post_incr_mval); + assert(post_incr_mval->str.len); + } + assert(MV_IS_STRING(post_incr_mval)); + assert(dollar_tlevel ? si->update_trans : update_trans); + value = post_incr_mval->str; + /* The MAX_REC_SIZE check could not be done in op_gvincr (like is done in op_gvput) because + * the post-increment value is not known until here. so do the check here. + */ + ENSURE_VALUE_WITHIN_MAX_REC_SIZE(value, gv_target); + + } else + value = val->str; + } + /* -------------------------------------------------------------------------------------------- + * The code for the non-block-split case is very similar to the code in recompute_upd_array. + * Any changes in either place should be reflected in the other. + * -------------------------------------------------------------------------------------------- + */ + need_extra_block_split = FALSE; /* Assume we don't require an additional block split (most common case) */ + duplicate_set = FALSE; /* Assume this is NOT a duplicate set (most common case) */ + split_depth = 0; + split_targ = gv_target; + for (succeeded = FALSE; !succeeded; no_pointers = level_0 = FALSE) + { + buffaddr = bh->buffaddr; + cur_blk_size = ((blk_hdr_ptr_t)buffaddr)->bsiz; + target_key_size = temp_key->end + 1; + /* Potential size of a block containing just the new/updated record */ + new_blk_size_single = SIZEOF(blk_hdr) + SIZEOF(rec_hdr) + target_key_size + value.len; + if (new_blk_size_single > blk_reserved_size) + { /* The record that is newly inserted/updated does not fit by itself in a separate block + * if the current reserved-bytes for this database is taken into account. If this is not a + * GVT leaf block, this situation is then possible if we are not in the final retry (and hence + * dont hold crit on the region) and "temp_key->end" (and in turn "target_key_size") was + * computed from a stale copy (due to concurrent updates or buffer reuse) of the global buffer + * (effectively a restartable situation). If so, restart. If not issue error. + */ + if (no_pointers || (CDB_STAGNATE <= t_tries)) + { + ISSUE_RSVDBYTE2HIGH_ERROR(gv_target); + } else + { + status = cdb_sc_mkblk; + GOTO_RETRY; + } + } + curr_rec_match = bh->curr_rec.match; + curr_rec_offset = bh->curr_rec.offset; + new_rec = (target_key_size != curr_rec_match); + if (!new_rec && !no_pointers) + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_lostcr; /* will a new cdb_sc status be better */ + GOTO_RETRY; + } + rp = (rec_hdr_ptr_t)(buffaddr + curr_rec_offset); + if (curr_rec_offset == cur_blk_size) + { + if ((FALSE == new_rec) && dollar_tlevel) + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_mkblk; + GOTO_RETRY; + } + rec_cmpc = 0; + rec_size = 0; + } else + { + GET_USHORT(rec_size, &rp->rsiz); + rec_cmpc = rp->cmpc; + if ((sm_uc_ptr_t)rp + rec_size > (sm_uc_ptr_t)buffaddr + cur_blk_size) + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_mkblk; + GOTO_RETRY; + } + } + prev_rec_match = bh->prev_rec.match; + if (new_rec) + { + new_rec_size = SIZEOF(rec_hdr) + target_key_size - prev_rec_match + value.len; + if (cur_blk_size <= (signed int)curr_rec_offset) /* typecast necessary to enforce "signed int" comparison */ + next_rec_shrink = 0; + else + next_rec_shrink = curr_rec_match - rec_cmpc; + delta = new_rec_size - next_rec_shrink; + } else + { + if (rec_cmpc != prev_rec_match) + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_mkblk; + GOTO_RETRY; + } + assert(target_key_size > rec_cmpc); + cur_val_offset = SIZEOF(rec_hdr) + (target_key_size - rec_cmpc); +# ifdef GTM_TRIGGER + if (no_pointers && (NULL != ztold_mval) && !skip_hasht_read) + { /* Complete initialization of ztold_mval */ + assert(!skip_dbtriggers); + data_len = rec_size - cur_val_offset; + if (0 > data_len) + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_rmisalign; + GOTO_RETRY; + } + if (data_len) + { + ENSURE_STP_FREE_SPACE(data_len); + ztold_mval->str.addr = (char *)stringpool.free; + memcpy(ztold_mval->str.addr, (sm_uc_ptr_t)rp + cur_val_offset, data_len); + stringpool.free += data_len; + } + ztold_mval->str.len = data_len; + ztold_mval->mvtype = MV_STR; /* ztold_mval is now completely initialized */ + } +# endif + new_rec_size = cur_val_offset + value.len; + delta = new_rec_size - rec_size; + if (!delta && gvdupsetnoop && value.len + && !memcmp(value.addr, (sm_uc_ptr_t)rp + new_rec_size - value.len, value.len)) + { + duplicate_set = TRUE; + succeeded = TRUE; + break; /* duplicate SET */ + } + next_rec_shrink = 0; + } + blk_num = bh->blk_num; + bh_level = bh->level; + if (dollar_tlevel) + { + if ((SIZEOF(rec_hdr) + target_key_size - prev_rec_match + value.len) != new_rec_size) + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_mkblk; + GOTO_RETRY; + } + chain1 = *(off_chain *)&blk_num; + if ((1 == chain1.flag) && ((int)chain1.cw_index >= si->cw_set_depth)) + { + assert(si->tp_csa == csa); + assert(FALSE == csa->now_crit); + status = cdb_sc_blknumerr; + GOTO_RETRY; + } + } + next_rec_shrink1 = next_rec_shrink; + /* Potential size of the current block including the new/updated record */ + new_blk_size = cur_blk_size + delta; + /* It is possible due to concurrency issues (for example if the buffer that we are planning on updating + * in shared memory got reused for a different block) that "new_blk_size" is lesser than "new_blk_size_single" + * In those cases, we will go into the non-block-split case but eventually we will restart. + */ + assert((new_blk_size >= new_blk_size_single) || (CDB_STAGNATE > t_tries)); + if ((new_blk_size <= blk_fill_size) || (new_blk_size <= new_blk_size_single)) + { /* Update can be done without overflowing the block's fillfactor OR the record to be updated + * is the only record in the new block. Do not split block in either case. This means we might + * not honour the desired FillFactor if the only record in a block exceeds the blk_fill_size, + * but in this case we are guaranteed the block has room for the current reserved bytes. + */ + if (no_pointers) /* level zero (normal) data block: no deferred pointer chains */ + ins_chain_offset = 0; + else /* index or directory level block */ + ins_chain_offset =(int)((sm_uc_ptr_t)rp - buffaddr + new_rec_size - SIZEOF(block_id)); + BLK_INIT(bs_ptr, bs1); + if (0 == rc_set_fragment) + { + BLK_SEG(bs_ptr, buffaddr + SIZEOF(blk_hdr), curr_rec_offset - SIZEOF(blk_hdr)); + BLK_ADDR(curr_rec_hdr, SIZEOF(rec_hdr), rec_hdr); + curr_rec_hdr->rsiz = new_rec_size; + curr_rec_hdr->cmpc = prev_rec_match; + BLK_SEG(bs_ptr, (sm_uc_ptr_t)curr_rec_hdr, SIZEOF(rec_hdr)); + BLK_ADDR(cp1, target_key_size - prev_rec_match, unsigned char); + memcpy(cp1, temp_key->base + prev_rec_match, target_key_size - prev_rec_match); + BLK_SEG(bs_ptr, cp1, target_key_size - prev_rec_match); + if (0 != value.len) + { + BLK_ADDR(va, value.len, char); + memcpy(va, value.addr, value.len); + BLK_SEG(bs_ptr, (unsigned char *)va, value.len); + } + if (!new_rec) + rp = (rec_hdr_ptr_t)((sm_uc_ptr_t)rp + rec_size); + n = (int)(cur_blk_size - ((sm_uc_ptr_t)rp - buffaddr)); + if (n > 0) + { + if (new_rec) + { + BLK_ADDR(next_rec_hdr, SIZEOF(rec_hdr), rec_hdr); + next_rec_hdr->rsiz = rec_size - next_rec_shrink; + next_rec_hdr->cmpc = curr_rec_match; + BLK_SEG(bs_ptr, (sm_uc_ptr_t)next_rec_hdr, SIZEOF(rec_hdr)); + next_rec_shrink += SIZEOF(rec_hdr); + } + if (n >= next_rec_shrink) + { + BLK_SEG(bs_ptr, (sm_uc_ptr_t)rp + next_rec_shrink, n - next_rec_shrink); + } else + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_mkblk; + GOTO_RETRY; + } + } + } else + { /* With GT.M TRIGGERS, it is not clear how the RC protocol will work. The below assert is to + * be informed whenever such usage happens (expected to be really rare) and handle it right + * then instead of worrying about it during the initial trigger implementation. + */ + assert(FALSE); + curr_rec_hdr = (rec_hdr_ptr_t)(buffaddr + curr_rec_offset); + /* First piece is block prior to record + key + data prior to fragment */ + BLK_SEG(bs_ptr, + buffaddr + SIZEOF(blk_hdr), + curr_rec_offset - SIZEOF(blk_hdr) + SIZEOF(rec_hdr) + rc_set_fragment + + gv_currkey->end + 1 - curr_rec_hdr->cmpc); + /* Second piece is fragment itself */ + BLK_ADDR(va, value.len, char); + memcpy(va, value.addr, value.len); + BLK_SEG(bs_ptr, (unsigned char *)va, value.len); + /* Third piece is data after fragment + rest of block after record */ + n = (int)(cur_blk_size - ((sm_uc_ptr_t)curr_rec_hdr - buffaddr) - SIZEOF(rec_hdr) + - (gv_currkey->end + 1 - curr_rec_hdr->cmpc) - rc_set_fragment - value.len); + if (0 < n) + BLK_SEG(bs_ptr, + (sm_uc_ptr_t)curr_rec_hdr + gv_currkey->end + 1 - curr_rec_hdr->cmpc + + rc_set_fragment + value.len, + n); + } + if (0 == BLK_FINI(bs_ptr, bs1)) + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_mkblk; + GOTO_RETRY; + } + assert(bs1[0].len <= blk_reserved_size); /* Assert that new block has space for reserved bytes */ + cse = t_write(bh, (unsigned char *)bs1, ins_chain_offset, ins_chain_index, bh_level, + FALSE, FALSE, GDS_WRITE_PLAIN); + assert(!dollar_tlevel || !cse->high_tlevel); + if ((0 != ins_chain_offset) && (NULL != cse) && (0 != cse->first_off)) + { /* formerly tp_offset_chain - inserts a new_entry in the chain */ + assert((NULL != cse->new_buff) || horiz_growth && cse->low_tlevel->new_buff + && (buffaddr == cse->low_tlevel->new_buff)); + assert(0 == cse->next_off); + assert(ins_chain_offset > (signed)SIZEOF(blk_hdr)); /* we want signed comparison */ + assert((curr_rec_offset - SIZEOF(off_chain)) == (ins_chain_offset - new_rec_size)); + offset_sum = cse->first_off; + curr = buffaddr + offset_sum; + /* The typecast is needed below to enforce a "signed int" (versus "unsigned int") comparison */ + if (offset_sum >= (signed int)curr_rec_offset) + { /* the new record is prior to the first existing chain record, id the new one as first */ + /* first_off-------------v--------------------v + * [blk_hdr]...[new rec ( )]...[existing rec ( )]... */ + cse->next_off = cse->first_off - (ins_chain_offset - new_rec_size) - next_rec_shrink1; + cse->first_off = ins_chain_offset; + } else + { + if (horiz_growth) + { + old_cse = cse->low_tlevel; + assert(old_cse->first_off); + assert(old_cse && old_cse->done); + assert(!old_cse->undo_next_off[0] && !old_cse->undo_offset[0]); + } + /* find chain records before and after the new one */ + for ( ; ; curr += curr_chain.next_off) + { /* try to make offset_sum identify the first chain entry after the new record */ + GET_LONGP(&curr_chain, curr); + assert(curr_chain.flag == 1); + if (0 == curr_chain.next_off) + break; + offset_sum += curr_chain.next_off; + /* The typecast is needed below to enforce a "signed int" comparison */ + if (offset_sum >= (signed int)curr_rec_offset) + break; + } + /* store the next_off in old_cse before changing it in the buffer (for rolling back) */ + if (horiz_growth) + { + old_cse->undo_next_off[0] = curr_chain.next_off; + old_cse->undo_offset[0] = (block_offset)(curr - buffaddr); + assert(old_cse->undo_offset[0]); + } + if (0 == curr_chain.next_off) + { /* the last chain record precedes the new record: just update it */ + /* ---|---------------v + * [blk_hdr]...[existing rec ( )]...[new rec ( )]... */ + curr_chain.next_off = ins_chain_offset - offset_sum; + GET_LONGP(curr, &curr_chain); + } else + { /* update the chain record before the new one */ + /* ---|---------------v--------------------v + * [blk_hdr]...[existing rec ( )]...[new rec ( )]...[existing rec ( )] */ + curr_chain.next_off = (unsigned int)(ins_chain_offset - (curr - buffaddr)); + GET_LONGP(curr, &curr_chain); + cse->next_off = offset_sum - (ins_chain_offset - new_rec_size) - next_rec_shrink1; + } + } + assert((ins_chain_offset + (int)cse->next_off) <= + (delta + (sm_long_t)cur_blk_size - SIZEOF(off_chain))); + } + succeeded = TRUE; + if (level_0) + { + if (new_rec) + { /* New record insertion at leaf level. gvcst_search would have already updated clue to + * reflect the new key, but we need to fix the search history to keep it in sync with clue. + * This search history (and clue) will be used by the NEXT call to gvcst_search. + * Note that clue.end could be 0 at this point (see "Clue less than first rec, invalidate" + * comment in gvcst_search) in which case the below assignment is unnecessary (though does + * not hurt) but we want to avoid the if check (since we expect clue to be non-zero mostly). + */ + assert((0 == gv_target->clue.end) || (gv_target->clue.end + 1 == target_key_size)); + assert(1 < target_key_size); + assert(bh->curr_rec.match != target_key_size); + bh->curr_rec.match = target_key_size; + } + /* ------------------------------------------------------------------------------------------------- + * We have to maintain information for future recomputation only if the following are satisfied + * 1) The block is a leaf-level block + * 2) We are in TP (indicated by non-null cse) + * 3) The global has NOISOLATION turned ON + * 4) The cw_set_element hasn't encountered a block-split or a kill + * 5) We don't need an extra_block_split + * + * We can also add an optimization that only cse's of mode gds_t_write need to have such updations, + * but because of the belief that for a nonisolated variable, we will very rarely encounter a + * situation where a created block (in TP) will have some new keys added to it, and that adding + * the check slows down the normal code, we don't do that check here. + * ------------------------------------------------------------------------------------------------- + */ + if (cse && gv_target->noisolation && !cse->write_type && !need_extra_block_split) + { + assert(dollar_tlevel); + if (is_dollar_incr) + { + ADD_TO_GVT_TP_LIST(gv_target); /* See comment in ENSURE_VALUE_WITHIN_MAX_REC_SIZE + * macro definition for why this macro call is necessary */ + rts_error(VARLSTCNT(4) ERR_GVINCRISOLATION, 2, + gv_target->gvname.var_name.len, gv_target->gvname.var_name.addr); + } + if (NULL == cse->recompute_list_tail || + 0 != memcmp(gv_currkey->base, cse->recompute_list_tail->key.base, + gv_currkey->top)) + { + tempkv = (key_cum_value *)get_new_element(si->recompute_list, 1); + tempkv->key = *gv_currkey; + tempkv->next = NULL; + memcpy(tempkv->key.base, gv_currkey->base, gv_currkey->end + 1); + if (NULL == cse->recompute_list_head) + { + assert(NULL == cse->recompute_list_tail); + cse->recompute_list_head = tempkv; + } else + cse->recompute_list_tail->next = tempkv; + cse->recompute_list_tail = tempkv; + } else + tempkv = cse->recompute_list_tail; + assert(0 == val->str.len + || ((val->str.len == bs1[4].len) + && 0 == memcmp(val->str.addr, bs1[4].addr, val->str.len))); + tempkv->value.len = val->str.len; /* bs1[4].addr is undefined if val->str.len is 0 */ + tempkv->value.addr = (char *)bs1[4].addr;/* but not used in that case, so ok */ + } + + } + } else + { /* Block split required */ + split_depth++; + gv_target->clue.end = 0; /* invalidate clue */ + /* Potential size of the left and right blocks, including the new record */ + new_blk_size_l = curr_rec_offset + new_rec_size; + new_blk_size_r = SIZEOF(blk_hdr) + SIZEOF(rec_hdr) + target_key_size + value.len + cur_blk_size + - curr_rec_offset - (new_rec ? next_rec_shrink : rec_size); + assert(new_blk_size_single <= blk_reserved_size); + assert(blk_reserved_size >= blk_fill_size); + extra_record_orig_size = 0; + prev_rec_offset = bh->prev_rec.offset; + assert(new_blk_size_single <= new_blk_size_r); + /* Decide which side (left or right) the new record goes. Ensure either side has at least one record. + * This means we might not honor the desired FillFactor if the only record in a block exceeds the + * blk_fill_size, but in this case we are guaranteed the block has room for the current reserved bytes. + * The typecast of curr_rec_offset is needed below to enforce a "signed int" comparison. + */ + if (new_blk_size_r > blk_fill_size) + { + new_rec_goes_to_right = (new_blk_size_r == new_blk_size_single); + last_split_dir = NEWREC_DIR_FORCED; /* no choice in split direction */ + } else if (new_blk_size_l > blk_fill_size) + { + new_rec_goes_to_right = TRUE; + last_split_dir = NEWREC_DIR_FORCED; /* no choice in split direction */ + } else + { /* new_rec can go in either direction without any issues of fitting in. + * This is where we need to use a few heuristics to ensure good block space utilization. + * We note down which direction (left or right) the new record went in after the split. + * We use that as the heuristic to identify the direction of data loading and do the + * splits accordingly for future updates. + */ + last_split_dir = (enum split_dir)gv_target->last_split_direction[bh_level]; + if (NEWREC_DIR_FORCED == last_split_dir) + { /* dont have prior information to use heuristic. choose whichever side is less full. + * if this turns out to not be the correct choice, we will correct ourselves at the + * time of the next block split at the same level. + */ + last_split_dir = (new_blk_size_l < new_blk_size_r) ? NEWREC_DIR_LEFT : NEWREC_DIR_RIGHT; + } else + { /* Last block split at this level chose a specific direction for new_rec. See if + * that heuristic worked. This is done by checking if the block # that new_rec went + * into previously is the same block that is being split now. If so, that means the + * previous choice of direction was actually not optimal. So try the other direction now. + */ + last_split_bnum = gv_target->last_split_blk_num[bh_level]; + if (dollar_tlevel) + { + chain2 = *(off_chain *)&last_split_bnum; + if (chain1.flag == chain2.flag) + { + if (!chain1.flag) + blk_match = (blk_num == last_split_bnum); + else + { + assert(chain1.cw_index < si->cw_set_depth); + blk_match = (chain1.cw_index == chain2.cw_index); + } + } else + blk_match = FALSE; + } else + { + DEBUG_ONLY(chain1 = *(off_chain *)&last_split_bnum;) + assert(!chain1.flag); + blk_match = (blk_num == last_split_bnum); + } + is_split_dir_left = (NEWREC_DIR_LEFT == last_split_dir); + if (blk_match) /* switch direction since last choice did not seem to have worked */ + last_split_dir = is_split_dir_left ? NEWREC_DIR_RIGHT : NEWREC_DIR_LEFT; + else + { /* blk# did not match means there is a high likelihood that the current split + * is happening in the OTHER sibling block from the previous block split operation + * at the same level. There is no easy way of confirming this so we assume the + * heuristic is doing its job, unless we see evidence otherwise. And that evidence + * is IF the block sizes of the left and right halves dont match the direction of + * choice (e.g. if we choose NEWREC_DIR_LEFT, we expect the right block to be + * almost full and the left block to be almost empty and vice versa). + * In this case too switch the direction. + */ + if (is_split_dir_left) + { + if (new_blk_size_l > new_blk_size_r) + last_split_dir = NEWREC_DIR_RIGHT; + } else + { + if (new_blk_size_l < new_blk_size_r) + last_split_dir = NEWREC_DIR_LEFT; + } + } + } + new_rec_goes_to_right = (NEWREC_DIR_RIGHT == last_split_dir); + } + last_split_direction[bh_level] = (char)last_split_dir; + if (new_rec_goes_to_right) + { /* Left side of this block will be split off into a new block. + * The new record and the right side of this block will remain in this block. + */ + /* prepare new block */ + BLK_INIT(bs_ptr, bs1); + if (level_0) + { + BLK_SEG(bs_ptr, buffaddr + SIZEOF(blk_hdr), curr_rec_offset - SIZEOF(blk_hdr)); + } else + { /* for index records, the record before the split becomes a new *-key */ + /* Note: If the block split was caused by our appending the new record + * to the end of the block, this code causes the record PRIOR to the + * current *-key to become the new *-key. + */ + BLK_SEG(bs_ptr, buffaddr + SIZEOF(blk_hdr), prev_rec_offset - SIZEOF(blk_hdr)); + BLK_ADDR(new_star_hdr, SIZEOF(rec_hdr), rec_hdr); + new_star_hdr->rsiz = BSTAR_REC_SIZE; + new_star_hdr->cmpc = 0; + BLK_SEG(bs_ptr, (sm_uc_ptr_t)new_star_hdr, SIZEOF(rec_hdr)); + BLK_SEG(bs_ptr, (sm_uc_ptr_t)rp - SIZEOF(block_id), SIZEOF(block_id)); + } + new_blk_bs = bs1; + if (0 == BLK_FINI(bs_ptr,bs1)) + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_mkblk; + GOTO_RETRY; + } + /* We want to assert that the left block has enough space for reserved bytes but + * it is possible that it DOES NOT have enough space for reserved bytes if the pre-split + * block was previously populated with a very low reserved bytes setting and if the current + * reserved bytes setting is much higher than what the chosen split point would free up. + * This is an issue waiting to be fixed by C9K01-003221. Until then the following assert + * has to remain commented out. + * + * assert(bs1[0].len <= blk_reserved_size); + */ + /* prepare the existing block */ + BLK_INIT(bs_ptr, bs1); + ins_chain_offset = no_pointers ? 0 : (int)(SIZEOF(blk_hdr) + SIZEOF(rec_hdr) + target_key_size); + left_hand_offset = left_hand_index + = 0; + if (!new_rec) + rp = (rec_hdr_ptr_t)((sm_uc_ptr_t)rp + rec_size); + BLK_ADDR(curr_rec_hdr, SIZEOF(rec_hdr), rec_hdr); + curr_rec_hdr->rsiz = target_key_size + SIZEOF(rec_hdr) + value.len; + curr_rec_hdr->cmpc = 0; + BLK_SEG(bs_ptr, (sm_uc_ptr_t)curr_rec_hdr, SIZEOF(rec_hdr)); + BLK_ADDR(cp1, target_key_size, unsigned char); + memcpy(cp1, temp_key->base, target_key_size); + BLK_SEG(bs_ptr, cp1, target_key_size); + if (0 != value.len) + { + BLK_ADDR(va, value.len, char); + memcpy(va, value.addr, value.len); + BLK_SEG(bs_ptr, (unsigned char *)va, value.len); + } + if (buffaddr + cur_blk_size > (sm_uc_ptr_t)rp) + { + BLK_ADDR(next_rec_hdr, SIZEOF(rec_hdr), rec_hdr); + GET_USHORT(next_rec_hdr->rsiz, &rp->rsiz); + next_rec_hdr->rsiz -= next_rec_shrink; + next_rec_hdr->cmpc = new_rec ? curr_rec_match : rp->cmpc; + BLK_SEG(bs_ptr, (sm_uc_ptr_t)next_rec_hdr, SIZEOF(rec_hdr)); + next_rec_shrink += SIZEOF(rec_hdr); + n = cur_blk_size - INTCAST(((sm_uc_ptr_t)rp - buffaddr)) - next_rec_shrink; + if (0 > n) /* want signed compare as 'n' can be negative */ + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_mkblk; + GOTO_RETRY; + } + BLK_SEG(bs_ptr, (sm_uc_ptr_t)rp + next_rec_shrink, n); + } + if (0 == BLK_FINI(bs_ptr, bs1)) + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_mkblk; + GOTO_RETRY; + } + assert(bs1[0].len <= blk_reserved_size); /* Assert that right block has space for reserved bytes */ + assert(gv_altkey->top == gv_currkey->top); + assert(gv_altkey->end < gv_altkey->top); + temp_key = gv_altkey; + if (cdb_sc_normal != (status = gvcst_expand_key((blk_hdr_ptr_t)buffaddr, prev_rec_offset, + temp_key))) + GOTO_RETRY; + } else + { /* Insert in left hand (new) block */ + if (!level_0) + { /* In case of an index block, as long as the current record is not a *-record + * (i.e. last record in the block) and copying an extra record into the left + * block does not cause it to exceed the fill factor, copy an additional record. + * Not doing the extra record copy for index blocks (was the case pre-V54002) has + * been seen to create suboptimally filled index blocks (as low as 15% fillfactor) + * depending on the patterns of updates. + */ + assert(new_rec); + copy_extra_record = ((BSTAR_REC_SIZE != rec_size) + && ((new_blk_size_l + BSTAR_REC_SIZE) <= blk_fill_size)); + } else + { + copy_extra_record = ((0 == prev_rec_offset) && (NEWREC_DIR_LEFT == last_split_dir) + && new_rec && (SIZEOF(blk_hdr) < cur_blk_size)); + } + BLK_INIT(bs_ptr, bs1); + if (no_pointers) + left_hand_offset = 0; + else + { + left_hand_offset = curr_rec_offset + SIZEOF(rec_hdr); + if (level_0 || copy_extra_record) + left_hand_offset += target_key_size - prev_rec_match; + } + left_hand_index = ins_chain_index; + ins_chain_index = ins_chain_offset = 0; + BLK_SEG(bs_ptr, buffaddr + SIZEOF(blk_hdr), curr_rec_offset - SIZEOF(blk_hdr)); + if (level_0) + { /* After the initial split, will this record fit into the new left block? + * If not, this pass will make room and we will do another block split on the next pass. + */ + assert((blk_seg_cnt + SIZEOF(rec_hdr) + target_key_size - prev_rec_match + value.len) + == new_blk_size_l); + assert((new_blk_size_single <= new_blk_size_l) || (CDB_STAGNATE > t_tries)); + assert((new_blk_size_single != new_blk_size_l) + || ((0 == prev_rec_offset) && (SIZEOF(blk_hdr) == curr_rec_offset))); + assert((new_blk_size_single >= new_blk_size_l) + || ((SIZEOF(blk_hdr) <= prev_rec_offset) && (SIZEOF(blk_hdr) < curr_rec_offset))); + if ((new_blk_size_l > blk_fill_size) && (new_blk_size_l > new_blk_size_single)) + { /* There is at least one existing record to the left of the split point. + * Do the initial split this pass and make an extra split next pass. + */ + need_extra_block_split = TRUE; + DEBUG_ONLY(dbg_trace_array[dbg_num_iters].is_extra_block_split = TRUE;) + } else + { + BLK_ADDR(curr_rec_hdr, SIZEOF(rec_hdr), rec_hdr); + curr_rec_hdr->rsiz = new_rec_size; + curr_rec_hdr->cmpc = prev_rec_match; + BLK_SEG(bs_ptr, (sm_uc_ptr_t)curr_rec_hdr, SIZEOF(rec_hdr)); + BLK_ADDR(cp1, target_key_size - prev_rec_match, unsigned char); + memcpy(cp1, temp_key->base + prev_rec_match, target_key_size - prev_rec_match); + BLK_SEG(bs_ptr, cp1, target_key_size - prev_rec_match); + if (0 != value.len) + { + BLK_ADDR(va, value.len, char); + memcpy(va, value.addr, value.len); + BLK_SEG(bs_ptr, (unsigned char *)va, value.len); + } + if (copy_extra_record) + { + n = rec_size - curr_rec_match; + /* typecast needed below to enforce a "signed int" comparison */ + if ((n + (signed int)curr_rec_offset + new_rec_size) > blk_fill_size) + copy_extra_record = FALSE; + else + { + BLK_ADDR(extra_rec_hdr, SIZEOF(rec_hdr), rec_hdr); + extra_rec_hdr->rsiz = n; + extra_rec_hdr->cmpc = curr_rec_match; + BLK_SEG(bs_ptr, (sm_uc_ptr_t)extra_rec_hdr, SIZEOF(rec_hdr)); + if (n < (signed)SIZEOF(rec_hdr)) /* want signed compare */ + { /* as 'n' can be negative */ + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_mkblk; + GOTO_RETRY; + } + BLK_SEG(bs_ptr, + buffaddr + SIZEOF(blk_hdr) + SIZEOF(rec_hdr) + + curr_rec_match, + n - SIZEOF(rec_hdr)); + new_blk_size_l += n; + } + } + } + } else + { + if (copy_extra_record) + { + BLK_ADDR(curr_rec_hdr, SIZEOF(rec_hdr), rec_hdr); + curr_rec_hdr->rsiz = new_rec_size; + curr_rec_hdr->cmpc = prev_rec_match; + BLK_SEG(bs_ptr, (sm_uc_ptr_t)curr_rec_hdr, SIZEOF(rec_hdr)); + BLK_ADDR(cp1, target_key_size - prev_rec_match, unsigned char); + memcpy(cp1, temp_key->base + prev_rec_match, target_key_size - prev_rec_match); + BLK_SEG(bs_ptr, cp1, target_key_size - prev_rec_match); + assert(value.len); + BLK_ADDR(va, value.len, char); + memcpy(va, value.addr, value.len); + BLK_SEG(bs_ptr, (unsigned char *)va, value.len); + new_blk_size_l += BSTAR_REC_SIZE; + } else + new_blk_size_l = curr_rec_offset + BSTAR_REC_SIZE; + BLK_ADDR(new_star_hdr, SIZEOF(rec_hdr), rec_hdr); + new_star_hdr->rsiz = BSTAR_REC_SIZE; + new_star_hdr->cmpc = 0; + BLK_SEG(bs_ptr, (sm_uc_ptr_t)new_star_hdr, SIZEOF(rec_hdr)); + if (!copy_extra_record) + { + BLK_SEG(bs_ptr, (unsigned char *)&zeroes, SIZEOF(block_id)); + } else + BLK_SEG(bs_ptr, (sm_uc_ptr_t)rp + rec_size - SIZEOF(block_id), SIZEOF(block_id)); + } + new_blk_bs = bs1; + if (0 == BLK_FINI(bs_ptr, bs1)) + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_mkblk; + GOTO_RETRY; + } + /* We want to assert that the left block has enough space for reserved bytes but + * it is possible that it DOES NOT have enough space for reserved bytes if the pre-split + * block was previously populated with a very low reserved bytes setting and if the current + * reserved bytes setting is much higher than what the chosen split point would free up. + * This is an issue waiting to be fixed by C9K01-003221. Until then the following assert + * has to remain commented out. + * + * assert(bs1[0].len <= blk_reserved_size); + */ + /* assert that both !new_rec and copy_extra_record can never be TRUE at the same time */ + assert(new_rec || !copy_extra_record); + if (!new_rec || copy_extra_record) + { /* Should guard for empty block??? */ + rp = (rec_hdr_ptr_t)((sm_uc_ptr_t)rp + rec_size); + rec_cmpc = rp->cmpc; + temp_short = rec_size; + GET_USHORT(rec_size, &rp->rsiz); + } + if (copy_extra_record) + { + extra_record_orig_size = temp_short; + assert(gv_altkey->top == gv_currkey->top); + assert(gv_altkey->end < gv_altkey->top); + temp_key = gv_altkey; + if (cdb_sc_normal != + (status = gvcst_expand_key((blk_hdr_ptr_t)buffaddr, curr_rec_offset, temp_key))) + GOTO_RETRY; + } else if (temp_key != gv_altkey) + { + memcpy(gv_altkey, temp_key, SIZEOF(gv_key) + temp_key->end); + temp_key = gv_altkey; + } + rec_size += rec_cmpc; + BLK_INIT(bs_ptr, bs1); + BLK_ADDR(next_rec_hdr, SIZEOF(rec_hdr), rec_hdr); + next_rec_hdr->rsiz = rec_size; + next_rec_hdr->cmpc = 0; + BLK_SEG(bs_ptr, (sm_uc_ptr_t)next_rec_hdr, SIZEOF(rec_hdr)); + BLK_ADDR(cp1, rec_cmpc, unsigned char); + memcpy(cp1, temp_key->base, rec_cmpc); + BLK_SEG(bs_ptr, cp1, rec_cmpc); + n = cur_blk_size - INTCAST(((sm_uc_ptr_t)rp - buffaddr)) - SIZEOF(rec_hdr); + if (0 > n) /* want signed compare as 'n' can be negative */ + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_mkblk; + GOTO_RETRY; + } + BLK_SEG(bs_ptr, (sm_uc_ptr_t)(rp + 1), n); + if (0 == BLK_FINI(bs_ptr, bs1)) + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_mkblk; + GOTO_RETRY; + } + /* We want to assert that the right block has enough space for reserved bytes but + * it is possible that it DOES NOT have enough space for reserved bytes if the pre-split + * block was previously populated with a very low reserved bytes setting and if the current + * reserved bytes setting is much higher than what the chosen split point would free up. + * This is an issue waiting to be fixed by C9K01-003221. Until then the following assert + * has to remain commented out. + * + * assert(bs1[0].len <= blk_reserved_size); + */ + } + next_blk_index = t_create(blk_num, (uchar_ptr_t)new_blk_bs, left_hand_offset, left_hand_index, bh_level); + if (!no_pointers && dollar_tlevel) + { /* there may be chains */ + assert(new_rec); + curr_chain = *(off_chain *)&blk_num; + if (curr_chain.flag == 1) + tp_get_cw(si->first_cw_set, curr_chain.cw_index, &cse); + else + { + if (NULL != (tabent = lookup_hashtab_int4(si->blks_in_use, (uint4 *)&blk_num))) + tp_srch_status = tabent->value; + else + tp_srch_status = NULL; + cse = tp_srch_status ? tp_srch_status->cse : NULL; + } + assert(!cse || !cse->high_tlevel); + if ((NULL != cse) && (0 != cse->first_off)) + { /* there is an existing chain: fix to account for the split */ + assert(NULL != cse->new_buff); + assert(cse->done); + assert(0 == cse->next_off); + cse_new = si->last_cw_set; + assert(!cse_new->high_tlevel); + assert(0 == cse_new->next_off); + assert(0 == cse_new->first_off); + assert(cse_new->ins_off == left_hand_offset); + assert(cse_new->index == left_hand_index); + assert(cse_new->level == cse->level); + cse_first_off = (int4)cse->first_off; + offset_sum = cse_first_off; + curr = buffaddr + offset_sum; + GET_LONGP(&curr_chain, curr); + assert(curr_chain.flag == 1); + last_possible_left_offset = curr_rec_offset + extra_record_orig_size - SIZEOF(off_chain); + /* some of the following logic used to be in tp_split_chain which was nixed */ + if (offset_sum <= last_possible_left_offset) + { /* the split falls within or after the chain; otherwise entire chain stays right */ + assert((cse_first_off < curr_rec_offset) + || (cse_first_off == last_possible_left_offset)); + if (left_hand_offset && (curr_rec_offset < cse_first_off)) + { /* We are inserting the new record (with the to-be-filled child block + * number) AND an extra record in the left block and the TP block + * chain of the block to be split starts AFTER the new record's offset + * in the current block. This means the left block (cse_new) will have a + * block chain starting with the newly inserted record's block pointer. + */ + cse_new->first_off = left_hand_offset; + } else + { + cse_new->first_off = cse_first_off; + assert(0 == cse_new->next_off); + } + if (level_0) /* if no *-key issue stop after, rather than at, a match */ + last_possible_left_offset += SIZEOF(off_chain); + if (offset_sum < last_possible_left_offset) + { /* it's not an immediate hit */ + for ( ; ; curr += curr_chain.next_off, GET_LONGP(&curr_chain, curr)) + { /* follow chain upto split point */ + assert(1 == curr_chain.flag); + if (0 == curr_chain.next_off) + break; + offset_sum += curr_chain.next_off; + if (offset_sum >= last_possible_left_offset) + break; + } /* end of search chain loop */ + } + assert(curr >= (buffaddr + cse_first_off)); + if (level_0) /* restore match point to "normal" */ + last_possible_left_offset -= SIZEOF(off_chain); + if ((offset_sum == last_possible_left_offset) && !level_0) + { /* The last record in the left side of the pre-split block is where + * the search stopped. If no extra record copy was done, then this + * record will end up BEFORE the inserted record in the post-split + * left block. Otherwise this will be AFTER the inserted record. + * + * In case of copy_extra_record, the extra record will become the *-key + * ---|------------v-----------------v + * [blk_hdr]...[curr rec( )][new rec ( )] [extra rec (*-key)] + * + * In case of no extra record copy, the new record will become the *-key + * ---|-------------------v + * [blk_hdr]...[curr rec( )][new rec (*-key)( )] + * + * Take this into account during the calculations below. + */ + assert(cse_first_off <= last_possible_left_offset); + if (left_hand_offset) + { + assert(!ins_chain_offset); + if (!extra_record_orig_size && (offset_sum != cse_first_off)) + { /* bring curr up to the match */ + curr += curr_chain.next_off; + GET_LONGP(&curr_chain, curr); + } + curr_offset = curr - buffaddr; + undo_index = 0; + if (curr_offset < curr_rec_offset) + { /* The chain starts before the curr_rec_offset. Fix + * next_off field from the last element in the chain + * before this offset. + */ + prev_chain = curr_chain; + assert(extra_record_orig_size + || (BSTAR_REC_SIZE + == (left_hand_offset - curr_offset))); + prev_chain.next_off = left_hand_offset - curr_offset; + assert((curr_offset + prev_chain.next_off) + <= (new_blk_size_l - SIZEOF(off_chain))); + if (dollar_tlevel != cse->t_level) + { + assert(dollar_tlevel > cse->t_level); + assert(!cse->undo_next_off[0] + && !cse->undo_offset[0]); + assert(!cse->undo_next_off[1] + && !cse->undo_offset[1]); + cse->undo_next_off[0] = curr_chain.next_off; + cse->undo_offset[0] = (block_offset)curr_offset; + undo_index = 1; + } + GET_LONGP(curr, &prev_chain); + } + if (extra_record_orig_size) + { + if (offset_sum != cse_first_off) + { /* bring curr up to the match */ + curr += curr_chain.next_off; + curr_offset += curr_chain.next_off; + GET_LONGP(&curr_chain, curr); + } + if (dollar_tlevel != cse->t_level) + { + assert(dollar_tlevel > cse->t_level); + assert(!cse->undo_next_off[undo_index] && + !cse->undo_offset[undo_index]); + cse->undo_next_off[undo_index] = + curr_chain.next_off; + cse->undo_offset[undo_index] = + (block_offset)curr_offset; + } + prev_chain = curr_chain; + prev_chain.next_off = 0; + GET_LONGP(curr, &prev_chain); + cse_new->next_off = BSTAR_REC_SIZE; + } + offset_sum += curr_chain.next_off; + } else + { + undo_index = 0; + /* the last record turns into the *-key */ + if (offset_sum == cse_first_off) + { /* it's all there is */ + /* first_off --------------------v + * [blk_hdr]...[curr rec (*-key)( )] */ + assert(prev_rec_offset >= SIZEOF(blk_hdr)); + cse_new->first_off = (block_offset)(prev_rec_offset + + SIZEOF(rec_hdr)); + } else + { /* update the next_off of the previous chain record */ + /* ---|--------------------v + * [blk_hdr]...[prev rec( )][curr rec (*-key)( )] */ + assert((buffaddr + prev_rec_offset) > curr); + prev_chain = curr_chain; + assert((offset_sum - prev_chain.next_off) /* check old */ + == (curr - buffaddr)); /* method equivalent */ + prev_chain.next_off = (unsigned int)( + (prev_rec_offset + (unsigned int)(SIZEOF(rec_hdr)) + - (curr - buffaddr))); + assert((curr - buffaddr + prev_chain.next_off) + <= ((new_blk_size_l < blk_reserved_size + ? new_blk_size_l : blk_reserved_size) + - SIZEOF(off_chain))); + if (dollar_tlevel != cse->t_level) + { + assert(dollar_tlevel > cse->t_level); + assert(!cse->undo_next_off[0] + && !cse->undo_offset[0]); + assert(!cse->undo_next_off[1] + && !cse->undo_offset[1]); + cse->undo_next_off[0] = curr_chain.next_off; + cse->undo_offset[0] = (block_offset)(curr - + buffaddr); + undo_index = 1; + } + GET_LONGP(curr, &prev_chain); + /* bring curr up to the match */ + curr += curr_chain.next_off; + GET_LONGP(&curr_chain, curr); + } + offset_sum += curr_chain.next_off; + if (dollar_tlevel != cse->t_level) + { + assert(dollar_tlevel > cse->t_level); + assert(!cse->undo_next_off[undo_index] && + !cse->undo_offset[undo_index]); + cse->undo_next_off[undo_index] = curr_chain.next_off; + cse->undo_offset[undo_index] = (block_offset)(curr - + buffaddr); + } + curr_chain.next_off = 0; + GET_LONGP(curr, &curr_chain); + } + } else + { /* found the split and no *-key issue: just terminate before the split */ + if (offset_sum == cse_first_off) + offset_sum += curr_chain.next_off; /* put it in the lead */ + old_curr_chain_next_off = curr_chain.next_off; + if (left_hand_offset) + { /* there's a new chain rec in left */ + curr_offset = curr - buffaddr; + if (extra_record_orig_size + && (curr_offset == last_possible_left_offset)) + { + assert(level_0); /* else *-key issues */ + cse_new->next_off = extra_record_orig_size + - next_rec_shrink1; + } + assert(!ins_chain_offset); + /* put the new one at the end of the chain */ + /* ---|---------------v + * [blk_hdr]...[curr rec( )]...[new rec ( )] */ + /* the new rec may or may not be a *-key */ + assert((offset_sum - curr_chain.next_off) == curr_offset); + assert(left_hand_offset > curr_offset); + curr_chain.next_off = (block_offset)(left_hand_offset + - curr_offset); + } else + curr_chain.next_off = 0; + assert((curr - buffaddr + curr_chain.next_off) + <= ((new_blk_size_l < blk_reserved_size + ? new_blk_size_l : blk_reserved_size) - SIZEOF(off_chain))); + if (dollar_tlevel != cse->t_level) + { + assert(dollar_tlevel > cse->t_level); + assert(!cse->undo_next_off[0] && !cse->undo_offset[0]); + assert(!cse->undo_next_off[1] && !cse->undo_offset[1]); + cse->undo_next_off[0] = old_curr_chain_next_off; + cse->undo_offset[0] = (block_offset)(curr - buffaddr); + } + GET_LONGP(curr, &curr_chain); + } /* end of *-key or not alternatives */ + assert((left_hand_offset + (int)cse_new->next_off) <= + ((new_blk_size_l < blk_reserved_size ? new_blk_size_l : blk_reserved_size) + - SIZEOF(off_chain))); + } /* end of buffer and cse_new adjustments */ + prev_first_off = cse_first_off; + if (ins_chain_offset) + { /* if there is a new chain rec in the old block, put it first */ + /* first_off---------v + * [blk_hdr][new rec( )]... */ + assert(!left_hand_offset); + assert(0 == extra_record_orig_size); + assert(ins_chain_offset >= (SIZEOF(blk_hdr) + SIZEOF(rec_hdr))); + cse->first_off = ins_chain_offset; + assert(0 == cse->next_off); + if (offset_sum > last_possible_left_offset) + { /* there are existing chain records after the split */ + /* first_off---------v--------------------v + * [blk_hdr][new rec( )]...[existing rec ( )] */ + prev_next_off = cse->next_off; + cse->next_off = offset_sum - last_possible_left_offset - next_rec_shrink1; + assert((int)(cse->next_off + ins_chain_offset) < new_blk_size_r); + } + } else if (offset_sum <= last_possible_left_offset) + { /* the last chain record went left with the split */ + cse->first_off = 0; + } else + { /* just adjust the anchor for the split */ + /* first_off------------------v + * [blk_hdr]...[existing rec ( )] */ + assert(offset_sum >= (int)cse_first_off); + cse->first_off = (block_offset)(offset_sum - last_possible_left_offset + rec_cmpc + + SIZEOF(blk_hdr) - SIZEOF(off_chain)); + assert(cse->first_off >= (SIZEOF(blk_hdr) + SIZEOF(rec_hdr))); + } + assert((ins_chain_offset + (int)cse->next_off) <= + ((new_blk_size_r < blk_reserved_size ? new_blk_size_r : blk_reserved_size) + - SIZEOF(off_chain))); + } /* end of of split processing */ + } /* end of tp only code */ + if (!dollar_tlevel) + cse = NULL; + else + { + cse_new = si->last_cw_set; + assert(!cse_new->high_tlevel); + gvcst_blk_build(cse_new, NULL, 0); + cse_new->done = TRUE; + } + /* Record block split heuristic info that will be used in next block split */ + if (!new_rec_goes_to_right) + { + chain1.flag = 1; + chain1.cw_index = next_blk_index; + chain1.next_off = 0; + assert(SIZEOF(gv_target->last_split_blk_num[bh_level]) == SIZEOF(off_chain)); + last_split_blk_num[bh_level] = *(block_id *)&chain1; + } else + last_split_blk_num[bh_level] = blk_num; + assert(temp_key == gv_altkey); + /* If new_rec_goes_to_right is TRUE, then it almost always implies that the left side of + * the block is almost full (i.e. adding the new record there caused it to exceed the fill + * factor) therefore direct all future updates to keys in between (which lie between the + * last key of the left block and the first key of the right block) to the right block. + * + * If not, direct those updates to the left block thereby preventing it from staying at a + * low capacity for a long period of time. + * + * This direction of future updates is implemented by controlling what key gets passed for + * record addition into the parent index block. For directing all in-between updates to the + * right block, pass in the last key of the left block to the parent index block. For directing + * all in-between updates to the left block, back off 1 spot from the first key of the right + * block and pass that to the parent index block. + * + * Doing this backoff accurately would imply finding the last non-zero byte in the key and taking + * 1 off from it. In case the length of the right key is less than the left key, it is possible + * that this backoff causes the new key to be less than even the left key (e.g. if left side has + * "C2 13 93 00" as key sequence corresponding to the number 1292 and right side has "C2 14 00" + * corresponding to the number 1300, taking one off the right side would give "C2 13 00" which corresponds + * to the number 12 and is lesser than the left side). In this case, we would have to start adding in + * FF bytes to the key as much as possible until we reached the left key length. In the above example, + * we would get "C2 13 FF 00". + * + * In the end, because of the complexities involved in getting an accurate backoff (see above paragraph), + * we instead implement a simplified backoff by examining just the first byte that differs and the + * immediately following byte (if needed). If it turns out that we cannot get a backoff with just + * those 2 bytes (should be rare), we then let the left key go unmodified. In such cases, we expect + * not many intervening possible keys and and therefore it does not matter that much whether we pass + * the left or (right-1) key to the parent. + * + * temp_key already holds the key corresponding to the last record of the left block. + * bs1[2] and bs1[3] hold the key corresponding to the first record of the right block. + */ + if (level_0) + { /* Determine key for record to pass on to parent index block */ + cp1 = temp_key->base; + cp2 = (unsigned char *)bs1[2].addr; + bs1_2_len = bs1[2].len; + for (i = 0; (i < bs1_2_len) && (*cp2 == *cp1); ++i) + { + ++cp2; + ++cp1; + } + if (i == bs1_2_len) + { + cp2 = (unsigned char *)bs1[3].addr; + bs1_3_len = bs1[3].len; + for (j = 0; (j < bs1_3_len) && (*cp2 == *cp1); ++j) + { + ++cp2; + ++cp1; + } + } + n = (int)((sm_long_t)*cp2 - (sm_long_t)*cp1); + if (0 > n) + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_mkblk; + GOTO_RETRY; + } else if (1 < n) + { + temp_key->end = cp1 - temp_key->base + 2; + if (temp_key->end < temp_key->top) + { + *cp1++ += (!new_rec_goes_to_right ? (n - 1) : 1); + *cp1++ = 0; + *cp1 = 0; + } else + { + temp_key->end = temp_key->prev; + assert(temp_key->end < temp_key->top); + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_mkblk; + GOTO_RETRY; + } + } else if (1 == n) + { + cp1++; + if ((cp1 - temp_key->base + 2) < temp_key->top) + { + if (i == (bs1_2_len - 1)) + cp2 = (unsigned char *)bs1[3].addr; + else + cp2++; + if ((STR_SUB_MAXVAL != *cp1) || (KEY_DELIMITER != *cp2)) + { + if (!new_rec_goes_to_right) + { + old_ch = *cp2; + new_ch = old_ch - 1; + *cp1 = new_ch; + if (KEY_DELIMITER != old_ch) + *(cp1 - 1) = *(cp2 - 1); + } else + { + old_ch = *cp1; + new_ch = old_ch + 1; + *cp1 = new_ch; + if (STR_SUB_MAXVAL == old_ch) + *(cp1 - 1) = *(cp2 - 1); + } + cp1++; + if (KEY_DELIMITER == new_ch) + temp_key->end--; + else + *cp1++ = KEY_DELIMITER; + *cp1 = KEY_DELIMITER; + temp_key->end = cp1 - temp_key->base; + } + } else + { + temp_key->end = temp_key->prev; + assert(temp_key->end < temp_key->top); + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_mkblk; + GOTO_RETRY; + } + } + } + assert(temp_key->end < temp_key->top); + assert(KEY_DELIMITER == temp_key->base[temp_key->end]); + assert(KEY_DELIMITER == temp_key->base[temp_key->end - 1]); + assert(KEY_DELIMITER != temp_key->base[temp_key->end - 2]); + bq = bh + 1; + if (HIST_TERMINATOR != bq->blk_num) + { /* Not root; write blocks and continue */ + if (cdb_sc_normal != (status = gvcst_search_blk(temp_key, bq))) + GOTO_RETRY; + cse = t_write(bh, (unsigned char *)bs1, ins_chain_offset, + ins_chain_index, bh_level, TRUE, FALSE, GDS_WRITE_PLAIN); + assert(!dollar_tlevel || !cse->high_tlevel); + if (cse) + { + assert(dollar_tlevel); + cse->write_type |= GDS_WRITE_BLOCK_SPLIT; + } + value.len = SIZEOF(block_id); + value.addr = (char *)&zeroes; + ++bh; + ins_chain_index = next_blk_index; + } else + { /* Create new root */ + if ((bh_level + 1) == MAX_BT_DEPTH) + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_maxlvl; + GOTO_RETRY; + } + ins_chain_index = t_create(blk_num, (uchar_ptr_t)bs1, ins_chain_offset, ins_chain_index, bh_level); + make_it_null = FALSE; + if (NULL != cse) + { /* adjust block to use the buffer and offsets worked out for the old root */ + assert(cse->done); + assert(NULL != cse->new_buff); + cse_new = si->last_cw_set; + assert(!cse_new->high_tlevel); + cse_new->blk_target = cse->blk_target; + cse_new->first_off = cse->first_off; + cse_new->next_off = cse->next_off; + /* to be able to incrementally rollback, we need another copy of new_buff, + * pointer copying wouldn't suffice + */ + cse_new->new_buff = (unsigned char *)get_new_free_element(si->new_buff_list); + memcpy(cse_new->new_buff, cse->new_buff, ((blk_hdr_ptr_t)cse->new_buff)->bsiz); + cse_new->old_block = NULL; + make_it_null = TRUE; + } + /* Build the right child of the new root right now since it is possible that before commit the + * root block may have been recycled in the global buffer which wouldn't cause a restart since + * it has been built already (see the gvcst_blk_build below). Otherwise, we may be relying + * on incorrect data in the root block when we build this right child finally in bg_update. + * Note that this needs to be done only in TP since only tp_tend allows for a block with a + * cse not to be in the global buffer if a new_buff already exists. + */ + if (dollar_tlevel) + { + DEBUG_ONLY(tp_get_cw(si->first_cw_set, ins_chain_index, &cse_new);) + assert(cse_new == si->last_cw_set); + cse_new = si->last_cw_set; + assert(FALSE == cse_new->done); + assert(!cse_new->high_tlevel); + gvcst_blk_build(cse_new, NULL, 0); + cse_new->done = TRUE; + } + target_key_size = temp_key->end + 1; + BLK_INIT(bs_ptr, bs1); + BLK_ADDR(curr_rec_hdr, SIZEOF(rec_hdr), rec_hdr); + curr_rec_hdr->rsiz = target_key_size + SIZEOF(rec_hdr) + SIZEOF(block_id); + curr_rec_hdr->cmpc = 0; + BLK_SEG(bs_ptr, (sm_uc_ptr_t)curr_rec_hdr, SIZEOF(rec_hdr)); + BLK_ADDR(cp1, target_key_size, unsigned char); + memcpy(cp1, temp_key->base, target_key_size); + BLK_SEG(bs_ptr, cp1, target_key_size); + BLK_SEG(bs_ptr, (unsigned char *)&zeroes, SIZEOF(block_id)); + BLK_ADDR(next_rec_hdr, SIZEOF(rec_hdr), rec_hdr); + next_rec_hdr->rsiz = BSTAR_REC_SIZE; + next_rec_hdr->cmpc = 0; + BLK_SEG(bs_ptr, (sm_uc_ptr_t)next_rec_hdr, SIZEOF(rec_hdr)); + BLK_SEG(bs_ptr, (unsigned char *)&zeroes, SIZEOF(block_id)); + if (0 == BLK_FINI(bs_ptr, bs1)) + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_mkblk; + GOTO_RETRY; + } + assert(bs1[0].len <= blk_reserved_size); /* Assert that new block has space for reserved bytes */ + ins_off1 = (block_offset)(SIZEOF(blk_hdr) + SIZEOF(rec_hdr) + target_key_size); + ins_off2 = (block_offset)(SIZEOF(blk_hdr) + (2 * SIZEOF(rec_hdr)) + SIZEOF(block_id) + + target_key_size); + assert(ins_off1 < ins_off2); + /* Since a new root block is not created but two new children are created, this update to the + * root block should disable the "indexmod" optimization (C9B11-001813). + */ + cse = t_write(bh, (unsigned char *)bs1, ins_off1, next_blk_index, + bh_level + 1, TRUE, FALSE, GDS_WRITE_KILLTN); + if (make_it_null) + cse->new_buff = NULL; + assert(!dollar_tlevel || !cse->high_tlevel); + if (!dollar_tlevel) + { /* create a sibling cw-set-element to store ins_off2/ins_chain_index */ + t_write_root(ins_off2, ins_chain_index); + } else + { + cse->write_type |= GDS_WRITE_BLOCK_SPLIT; + assert(NULL == cse->new_buff); + cse->first_off = 0; + cse->next_off = ins_off2 - ins_off1; + /* the following is the only place where the buffer is not completely built by + * gvcst_blk_build. this means that the block chain seen by gvcst_blk_build will + * have a bad value (that is fixed below) at the end of the list. therefore the + * block chain integrity checking code in gvcst_blk_build will error out normally + * in this case. signal that routine to skip checking just this tail element. + */ + DEBUG_ONLY(skip_block_chain_tail_check = TRUE;) + gvcst_blk_build(cse, NULL, 0); + DEBUG_ONLY(skip_block_chain_tail_check = FALSE;) + curr_chain.flag = 1; + curr_chain.cw_index = ins_chain_index; + curr_chain.next_off = 0; + curr = cse->new_buff + ins_off2; + GET_LONGP(curr, &curr_chain); + cse->done = TRUE; + gv_target->clue.end = 0; + } + succeeded = TRUE; + } + } + } + assert(succeeded); + horiz_growth = FALSE; + assert((csa->dir_tree == gv_target) || tp_root); + RESET_GV_TARGET_LCL_AND_CLR_GBL(save_targ); + /* The only case where gv_target is still csa->dir_tree after the above RESET macro is if op_gvput was invoked + * with gv_target being set to cs_addrs->dir_tree. In that case gbl_target_was_set would have been set to TRUE. Assert. + */ + assert((csa->dir_tree != gv_target) || gbl_target_was_set); + /* Format the journal records only once for non-TP (irrespective of number of restarts). + * We remember this through the variable "jnl_format_done". If TRUE, we do not redo the jnl_format. + * The only exception is if we are in $INCREMENT in which case we need to reformat since the + * current value (and hence the post-increment value) of the key might be different in different tries. + * In this case, the restart code checks and resets "jnl_format_done" to FALSE. + */ + if (!dollar_tlevel) + { + nodeflags = 0; + if (skip_dbtriggers) + nodeflags |= JS_SKIP_TRIGGERS_MASK; + assert(!jnl_format_done || !is_dollar_incr && (JNL_SET == non_tp_jfb_ptr->ja.operation)); + if (need_extra_block_split) + inctn_opcode = inctn_gvcstput_extra_blk_split; + else if (JNL_WRITE_LOGICAL_RECS(csa) && !jnl_format_done) + { + jfb = jnl_format(JNL_SET, gv_currkey, (!is_dollar_incr ? val : post_incr_mval), nodeflags); + assert(NULL != jfb); + jnl_format_done = TRUE; + } + succeeded = ((trans_num)0 != t_end(&gv_target->hist, dir_hist, TN_NOT_SPECIFIED)); + inctn_opcode = inctn_invalid_op; + if (succeeded) + { + if (NULL != dir_hist) + { /* The Global Variable Tree was created in this transaction. So clear its gv_target to be safe. + * The directory tree though will have a non-zero value and that can stay as it is since it + * was validated in this transaction and was found good enough for us to commit. + */ + assert(dir_tree != gv_target); + gv_target->clue.end = 0; + } + } else + { /* "t_retry" would have already been invoked by "t_end". + * So instead of going to "retry:", do only whatever steps from there are necessary here. + */ + RESTORE_ZERO_GVT_ROOT_ON_RETRY(lcl_root, gv_target, tp_root, dir_hist, dir_tree); + jnl_format_done = FALSE; /* need to reformat jnl records for $INCR even in case of non-TP */ + GTMTRIG_DBG_ONLY(dbg_trace_array[dbg_num_iters].retry_line = __LINE__); + goto tn_restart; + } + } else + { + status = tp_hist(dir_hist); + if (NULL != dir_hist) + { /* Note that although "tp_hist" processes the "dir_hist" history, it only adds "gv_target" to gvt_tp_list. + * But csa->dir_tree might have had clue, blk-split related info etc. modified as part of this + * gvcst_put invocation that might also need cleanup (just like any other gv_target) so add + * csa->dir_tree to gvt_tp_list (if not already done). + */ + assert(dir_tree == csa->dir_tree); + ADD_TO_GVT_TP_LIST(dir_tree); /* note: macro also updates read_local_tn if necessary */ + } + if (cdb_sc_normal != status) + GOTO_RETRY; + jnl_format_done = FALSE; + } + if (succeeded) + { + if (0 == tp_root) + { /* Fill in gv_target->root with newly created root block value. + * Previously, root remained at 0 at the end of the transaction and it was left to the + * NEXT transaction to do a gvcst_root_search and determine the new root block. + * This was fine until recently when op_gvrectarg was reworked to NOT do a gvcst_root_search + * (to avoid potential TP restarts while unwinding the M stack). This meant that gv_target->root + * needed to be kept uptodate as otherwise it was possible for gv_target->root to be stale + * after a op_gvrectarg causing incorrect behavior of following M code (see v52000/C9B10001765 + * subtest for example where $order(^gvn,$$extrinsic) is done and extrinsic CREATES <^gvn>). + */ + GTMTRIG_ONLY(assert(!ztval_gvcst_put_redo);) + assert(0 == gv_target->root); + if (!dollar_tlevel) + { + tp_root = cw_set[root_blk_cw_index].blk; + assert(gds_t_acquired == cw_set[root_blk_cw_index].old_mode); + assert(gds_t_committed == cw_set[root_blk_cw_index].mode); + assert(!IS_BITMAP_BLK(tp_root)); + } else + { + chain1.flag = 1; + chain1.cw_index = root_blk_cw_index; + chain1.next_off = 0; /* does not matter what value we set this field to */ + assert(SIZEOF(tp_root) == SIZEOF(chain1)); + tp_root = *(block_id *)&chain1; + } + gv_target->root = tp_root; + } + if (need_extra_block_split) + { /* The logical update required an extra block split operation first (which succeeded) so + * get back to doing the logical update before doing any trigger invocations etc. + */ + GTMTRIG_ONLY(skip_hasht_read = TRUE;) + goto fresh_tn_start; + } + for (bh_level = 0; bh_level < split_depth; bh_level++) + { + blk_num = last_split_blk_num[bh_level]; + assert(0 != blk_num); + split_targ->last_split_blk_num[bh_level] = blk_num; + assert((NEWREC_DIR_FORCED == last_split_direction[bh_level]) + || (NEWREC_DIR_LEFT == last_split_direction[bh_level]) + || (NEWREC_DIR_RIGHT == last_split_direction[bh_level])); + split_targ->last_split_direction[bh_level] = last_split_direction[bh_level]; + /* Fix blk_num if it was created in this transaction. In case of non-TP, we have the real block number + * corresponding to the created block. In case of TP, we can know that only at tp_clean_up time so defer. + */ + chain1 = *(off_chain *)&blk_num; + if (chain1.flag) + { + if (!dollar_tlevel) + { + assert(chain1.cw_index < ARRAYSIZE(cw_set)); + split_targ->last_split_blk_num[bh_level] = cw_set[chain1.cw_index].blk; + } else + split_targ->split_cleanup_needed = TRUE;/* phantom blk# will be fixed at tp_clean_up time */ + } + } + if (dollar_tlevel) + { + nodeflags = 0; + if (skip_dbtriggers) + nodeflags |= JS_SKIP_TRIGGERS_MASK; + ja_val = (!is_dollar_incr ? val : post_incr_mval); + write_logical_jnlrecs = JNL_WRITE_LOGICAL_RECS(csa); +# ifdef GTM_TRIGGER + if (!skip_dbtriggers) + { + /* Since we are about to invoke the trigger, we better have gv_target->gvt_trigger and + * the local variable gvt_trigger in sync. The only exception is when we are here because + * of a $ztvalue update and redoing the gvcst_put. In this case, it's possible that + * the trigger code that was previously executed deleted the trigger and did an update + * on the global which would have set gv_target->gvt_trigger to NULL. Assert accordingly. + */ + assert(ztval_gvcst_put_redo || (gvt_trigger == gv_target->gvt_trigger)); + if ((NULL != gvt_trigger) && !ztval_gvcst_put_redo) + { + assert(dollar_tlevel); + /* Format ZTWORM and SET journal records. + * "ztworm_jfb", "jfb" and "jnl_format_done" are set by the below macro. + */ + JNL_FORMAT_ZTWORM_IF_NEEDED(csa, write_logical_jnlrecs, + JNL_SET, gv_currkey, ja_val, ztworm_jfb, jfb, jnl_format_done); + /* Initialize trigger parms that dont depend on the context of the matching trigger */ + trigparms.ztoldval_new = key_exists ? ztold_mval : (mval *)&literal_null; + PUSH_MV_STENT(MVST_MVAL); /* protect $ztval from stp_gcol */ + ztval_mval = &mv_chain->mv_st_cont.mvs_mval; + if (!is_dollar_incr) + *ztval_mval = *val; + else + { + *ztval_mval = *post_incr_mval; + /* Since this is pointing to malloced buffer, we need to repoint it to stringpool + * to avoid a nested trigger call (that does a $INCR) from overwriting this buffer. + * This way buffers corresponding to $ztvals of nested triggers can coexist. + */ + s2pool(&ztval_mval->str); + } + trigparms.ztvalue_new = ztval_mval; + trigparms.ztdata_new = key_exists ? &literal_one : &literal_zero; + gvtr_parms.gvtr_cmd = GVTR_CMDTYPE_SET; + gvtr_parms.gvt_trigger = gvt_trigger; + gvtr_parms.duplicate_set = duplicate_set; + /* Now that we have filled in minimal information, let "gvtr_match_n_invoke" do the rest */ + gtm_trig_status = gvtr_match_n_invoke(&trigparms, &gvtr_parms); + assert((0 == gtm_trig_status) || (ERR_TPRETRY == gtm_trig_status)); + if (ERR_TPRETRY == gtm_trig_status) + { /* A restart has been signaled that we need to handle or complete the handling of. + * This restart could have occurred reading the trigger in which case no + * tp_restart() has yet been done or it could have occurred in trigger code in + * which case we need to finish the incomplete tp_restart. In both cases this + * must be an implicitly TP wrapped transaction. Our action is to complete the + * necessary tp_restart() logic (t_retry is already completed so should be skipped) + * and then re-do the gvcst_put logic. + */ + assert(lcl_implicit_tstart); + assert(CDB_STAGNATE >= t_tries); + status = cdb_sc_normal; /* signal "retry:" to avoid t_retry call */ + GOTO_RETRY; + } + REMOVE_ZTWORM_JFB_IF_NEEDED(ztworm_jfb, jfb, si); + if (trigparms.ztvalue_changed) + { /* At least one of the invoked triggers changed $ztval. + * Redo the gvcst_put with $ztval as the right side of the SET. + * Also make sure gtm_trigger calls are NOT done this time around. + */ + assert(0 < gvtr_parms.num_triggers_invoked); + val = trigparms.ztvalue_new; + MV_FORCE_STR(val); /* in case the updated value happens to be a numeric quantity */ + ztval_gvcst_put_redo = TRUE; + skip_hasht_read = TRUE; + /* In case, the current gvcst_put invocation was for $INCR, reset the corresponding + * global variable that indicates a $INCR is in progress since the redo of the + * gvcst_put is a SET command (no longer $INCR). + */ + is_dollar_incr = FALSE; + /* Dont pop the mvals as we want ztval_mval (which points to the mval containing + * "val" for the redo iteration) protected-from-stp_gcol/accessible until the + * redo is complete. + */ + goto fresh_tn_start; + } + } + POP_MVALS_FROM_M_STACK_IF_NEEDED(ztold_mval, save_msp, save_mv_chain); + /* pop any stacked mvals before op_tcommit as it does its own popping */ + } +# endif + if (write_logical_jnlrecs && !jnl_format_done) + { + assert(dollar_tlevel); +# ifdef GTM_TRIGGER + /* Do not replicate implicit update or $ztval redo update */ + assert(tstart_trigger_depth <= gtm_trigger_depth); + if ((gtm_trigger_depth > tstart_trigger_depth) || ztval_gvcst_put_redo) + { + /* Ensure that JS_SKIP_TRIGGERS_MASK and JS_NOT_REPLICATED_MASK are mutually exclusive. */ + assert(!(nodeflags & JS_SKIP_TRIGGERS_MASK)); + nodeflags |= JS_NOT_REPLICATED_MASK; + } +# endif + jfb = jnl_format(JNL_SET, gv_currkey, ja_val, nodeflags); + assert(NULL != jfb); + jnl_format_done = TRUE; + } +# ifdef GTM_TRIGGER + /* Go ahead with commit of any implicit TP wrapped transaction */ + if (lcl_implicit_tstart) + { + GVTR_OP_TCOMMIT(status); + if (cdb_sc_normal != status) + GOTO_RETRY; + } +# endif + } + assert(!JNL_WRITE_LOGICAL_RECS(csa) || jnl_format_done); + /* Now that the SET/$INCR is finally complete, increment the corresponding GVSTAT counter */ + INCR_GVSTATS_COUNTER(csa, cnl, n_set, 1); + DBG_CHECK_VAL_AT_FUN_EXIT; + assert(lcl_dollar_tlevel == dollar_tlevel); + return; + } +retry: + /* Note that it is possible cs_addrs is not equal to csa at this point in case we restarted due to trigger + * invocations and in case those triggers referenced globals in different regions. But this should be fixed + * by a call to t_retry/tp_restart below (it does a TP_CHANGE_REG(tp_pointer->gd_reg)). + */ + RESET_GV_TARGET_LCL_AND_CLR_GBL(save_targ); + /* Need to restart. If directory tree was used in this transaction, nullify its clue as well (not normally + * done by t_retry). The RESTORE_ZERO_GVT_ROOT_ON_RETRY macro call below takes care of that for us. + */ + RESTORE_ZERO_GVT_ROOT_ON_RETRY(lcl_root, gv_target, tp_root, dir_hist, dir_tree); +# ifdef GTM_TRIGGER + if (!skip_dbtriggers) + { + if (lcl_implicit_tstart) + { + assert(!skip_INVOKE_RESTART); + assert((cdb_sc_normal != status) || (ERR_TPRETRY == gtm_trig_status)); + if (cdb_sc_normal != status) + skip_INVOKE_RESTART = TRUE; /* causes t_retry to invoke only tp_restart without any rts_error */ + /* else: t_retry has already been done by gtm_trigger so no need to do it again for this try */ + /* If an implicitly TP wrapped transaction is restarting, restore things to what they were + * at entry into gvcst_put. Note that we could have done multiple iterations of gvcst_put for + * extra_block_split/retry/ztval_gvcst_put_redo. + */ + ztval_gvcst_put_redo = FALSE; + skip_hasht_read = FALSE; + val = lcl_val; + /* $increment related fields need to be restored */ + is_dollar_incr = lcl_is_dollar_incr; + post_incr_mval = lcl_post_incr_mval; + increment_delta_mval = lcl_increment_delta_mval; + } + } +# endif + assert((cdb_sc_normal != status) GTMTRIG_ONLY(|| lcl_implicit_tstart)); + if (cdb_sc_normal != status) + { + GTMTRIG_ONLY(POP_MVALS_FROM_M_STACK_IF_NEEDED(ztold_mval, save_msp, save_mv_chain)); + t_retry(status); + } else + { /* else: t_retry has already been done so no need to do that again but need to still invoke tp_restart + * to complete pending "tprestart_state" related work. + */ +# ifdef GTM_TRIGGER + assert(ERR_TPRETRY == gtm_trig_status); + TRIGGER_BASE_FRAME_UNWIND_IF_NOMANSLAND; + POP_MVALS_FROM_M_STACK_IF_NEEDED(ztold_mval, save_msp, save_mv_chain); +# endif + rc = tp_restart(1, !TP_RESTART_HANDLES_ERRORS); + assert(0 == rc GTMTRIG_ONLY(&& TPRESTART_STATE_NORMAL == tprestart_state)); + } + GTMTRIG_ONLY(assert(!skip_INVOKE_RESTART);) /* if set to TRUE a few statements above, should have been reset by t_retry */ + /* At this point, we can be in TP only if we implicitly did a tstart in gvcst_put (as part of a trigger update). + * Assert that. Since the t_retry/tp_restart would have reset si->update_trans, we need to set it again. + * So reinvoke the T_BEGIN call only in case of TP. For non-TP, update_trans is unaffected by t_retry. + */ + assert(!dollar_tlevel GTMTRIG_ONLY(|| lcl_implicit_tstart)); + if (dollar_tlevel) + { + jnl_format_done = FALSE; /* need to reformat jnl records unconditionally in case of TP */ + tp_set_sgm(); /* set sgm_info_ptr & first_sgm_info for TP start */ + T_BEGIN_SETORKILL_NONTP_OR_TP(ERR_GVPUTFAIL); /* set update_trans and t_err for wrapped TP */ + } else if (is_dollar_incr) + jnl_format_done = FALSE; /* need to reformat jnl records for $INCR even in case of non-TP */ + assert(dollar_tlevel || update_trans); + goto tn_restart; +} diff --git a/sr_port/gvcst_query.c b/sr_port/gvcst_query.c new file mode 100644 index 0000000..c79df26 --- /dev/null +++ b/sr_port/gvcst_query.c @@ -0,0 +1,134 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "cdb_sc.h" +#include "filestruct.h" /* needed for jnl.h */ +#include "gdscc.h" /* needed for tp.h */ +#include "jnl.h" /* needed for tp.h */ +#include "gdskill.h" /* needed for tp.h */ +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ + +#include "t_end.h" +#include "t_retry.h" +#include "t_begin.h" +#include "gvcst_expand_key.h" +#include "gvcst_protos.h" /* for gvcst_rtsib,gvcst_search,gvcst_search_blk,gvcst_query prototype */ + +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF gd_region *gv_cur_region; +GBLREF gv_namehead *gv_target; +GBLREF gv_key *gv_currkey, *gv_altkey; +GBLREF uint4 dollar_tlevel; +GBLREF unsigned int t_tries; + +bool gvcst_query(void) +{ + boolean_t found, two_histories; + enum cdb_sc status; + blk_hdr_ptr_t bp; + rec_hdr_ptr_t rp; + unsigned char *c1, *c2; + srch_blk_status *bh; + srch_hist *rt_history; + + T_BEGIN_READ_NONTP_OR_TP(ERR_GVQUERYFAIL); + assert(t_tries < CDB_STAGNATE || cs_addrs->now_crit); /* we better hold crit in the final retry (TP & non-TP) */ + for (;;) + { + two_histories = FALSE; + if (cdb_sc_normal == (status = gvcst_search(gv_currkey, 0))) + { + found = TRUE; + bh = &gv_target->hist.h[0]; + rp = (rec_hdr_ptr_t)(bh->buffaddr + bh->curr_rec.offset); + bp = (blk_hdr_ptr_t)bh->buffaddr; + if (rp >= (rec_hdr_ptr_t)CST_TOB(bp)) + { + two_histories = TRUE; + rt_history = gv_target->alt_hist; + status = gvcst_rtsib(rt_history, 0); + if (cdb_sc_endtree == status) /* end of tree */ + { + found = FALSE; + two_histories = FALSE; /* second history not valid */ + } else if (cdb_sc_normal != status) + { + t_retry(status); + continue; + } else + { + bh = &rt_history->h[0]; + if (cdb_sc_normal != (status = gvcst_search_blk(gv_currkey, bh))) + { + t_retry(status); + continue; + } + rp = (rec_hdr_ptr_t)(bh->buffaddr + bh->curr_rec.offset); + bp = (blk_hdr_ptr_t)bh->buffaddr; + } + } + if (found) + { /* !found indicates that the end of tree has been reached (see call to + * gvcst_rtsib). If there is no more tree, don't bother doing expansion. + */ + status = gvcst_expand_key((blk_hdr_ptr_t)bh->buffaddr, (int4)((sm_uc_ptr_t)rp - bh->buffaddr), + gv_altkey); + if (cdb_sc_normal != status) + { + t_retry(status); + continue; + } + } + if (!dollar_tlevel) + { + if ((trans_num)0 == t_end(&gv_target->hist, !two_histories ? NULL : rt_history, TN_NOT_SPECIFIED)) + continue; + } else + { + status = tp_hist(!two_histories ? NULL : rt_history); + if (cdb_sc_normal != status) + { + t_retry(status); + continue; + } + } + assert(cs_data == cs_addrs->hdr); + INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_query, 1); + if (found) + { + c1 = &gv_altkey->base[0]; + c2 = &gv_currkey->base[0]; + for (; *c2;) + { + if (*c2++ != *c1++) + break; + } + if (!*c2 && !*c1) + { + return TRUE; + } + } + return FALSE; + } + t_retry(status); + } +} diff --git a/sr_port/gvcst_queryget.c b/sr_port/gvcst_queryget.c new file mode 100644 index 0000000..60b8acf --- /dev/null +++ b/sr_port/gvcst_queryget.c @@ -0,0 +1,151 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "stringpool.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "cdb_sc.h" +#include "copy.h" +#include "filestruct.h" /* needed for jnl.h */ +#include "gdscc.h" /* needed for tp.h */ +#include "jnl.h" /* needed for tp.h */ +#include "gdskill.h" /* needed for tp.h */ +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ + +#include "gvcst_protos.h" /* for gvcst_queryget,gvcst_search,gvcst_rtsib,gvcst_search_blk prototype */ +#include "gvcst_expand_key.h" +#include "t_begin.h" +#include "t_retry.h" +#include "t_end.h" + +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF gd_region *gv_cur_region; +GBLREF gv_namehead *gv_target; +GBLREF gv_key *gv_currkey, *gv_altkey; +GBLREF uint4 dollar_tlevel; +GBLREF unsigned int t_tries; +GBLREF spdesc stringpool; + +boolean_t gvcst_queryget(mval *val) +{ + boolean_t found, two_histories; + enum cdb_sc status; + int rsiz, key_size, data_len; + unsigned short temp_ushort; + blk_hdr_ptr_t bp; + rec_hdr_ptr_t rp; + srch_blk_status *bh; + srch_hist *rt_history; + DEBUG_ONLY(unsigned char *save_strp = NULL); + + T_BEGIN_READ_NONTP_OR_TP(ERR_GVQUERYGETFAIL); + assert(t_tries < CDB_STAGNATE || cs_addrs->now_crit); /* we better hold crit in the final retry (TP & non-TP) */ + for (;;) + { + two_histories = FALSE; + if (cdb_sc_normal == (status = gvcst_search(gv_currkey, 0))) + { + found = TRUE; + bh = &gv_target->hist.h[0]; + rp = (rec_hdr_ptr_t)(bh->buffaddr + bh->curr_rec.offset); + bp = (blk_hdr_ptr_t)bh->buffaddr; + if (rp >= (rec_hdr_ptr_t)CST_TOB(bp)) + { + two_histories = TRUE; + rt_history = gv_target->alt_hist; + status = gvcst_rtsib(rt_history, 0); + if (cdb_sc_endtree == status) /* end of tree */ + { + found = FALSE; + two_histories = FALSE; /* second history not valid */ + } else if (cdb_sc_normal != status) + { + t_retry(status); + continue; + } else + { + bh = &rt_history->h[0]; + if (cdb_sc_normal != (status = gvcst_search_blk(gv_currkey, bh))) + { + t_retry(status); + continue; + } + rp = (rec_hdr_ptr_t)(bh->buffaddr + bh->curr_rec.offset); + bp = (blk_hdr_ptr_t)bh->buffaddr; + } + } + /* !found indicates that the end of tree has been reached (see call to + * gvcst_rtsib). If there is no more tree, don't bother doing expansion. + */ + if (found) + { + status = gvcst_expand_key((blk_hdr_ptr_t)bh->buffaddr, (int4)((sm_uc_ptr_t)rp - bh->buffaddr), + gv_altkey); + if (cdb_sc_normal != status) + { + t_retry(status); + continue; + } + key_size = gv_altkey->end + 1; + GET_RSIZ(rsiz, rp); + data_len = rsiz + rp->cmpc - SIZEOF(rec_hdr) - key_size; + if (data_len < 0 || (sm_uc_ptr_t)rp + rsiz > (sm_uc_ptr_t)bp + ((blk_hdr_ptr_t)bp)->bsiz) + { + assert(CDB_STAGNATE > t_tries); + t_retry(cdb_sc_rmisalign1); + continue; + } + ENSURE_STP_FREE_SPACE(data_len); + DEBUG_ONLY ( + if (!save_strp) + save_strp = stringpool.free); + assert(stringpool.top - stringpool.free >= data_len); + memcpy(stringpool.free, (sm_uc_ptr_t)rp + rsiz - data_len, data_len); + /* Assumption: t_end/tp_hist will never cause stp_gcol() call */ + } + if (!dollar_tlevel) + { + if ((trans_num)0 == t_end(&gv_target->hist, !two_histories ? NULL : rt_history, TN_NOT_SPECIFIED)) + continue; + } else + { + status = tp_hist(!two_histories ? NULL : rt_history); + if (cdb_sc_normal != status) + { + t_retry(status); + continue; + } + } + if (found) + { + DEBUG_ONLY(assert(save_strp == stringpool.free)); + /* Process val first. Already copied to string pool. */ + val->mvtype = MV_STR; + val->str.addr = (char *)stringpool.free; + val->str.len = data_len; + stringpool.free += data_len; + } + return found; + } + t_retry(status); + } +} diff --git a/sr_port/gvcst_root_search.c b/sr_port/gvcst_root_search.c new file mode 100644 index 0000000..2f832ae --- /dev/null +++ b/sr_port/gvcst_root_search.c @@ -0,0 +1,202 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "cdb_sc.h" +#include "copy.h" +#include "spec_type.h" +#include "stringpool.h" +#include "filestruct.h" /* needed for jnl.h */ +#include "gdscc.h" /* needed for tp.h */ +#include "jnl.h" /* needed for tp.h */ +#include "gdskill.h" /* needed for tp.h */ +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ + +#include "t_end.h" /* prototypes */ +#include "t_retry.h" +#include "t_begin.h" +#include "gvcst_protos.h" /* for gvcst_search,gvcst_root_search prototype */ +#include "get_spec.h" +#include "collseq.h" + +GBLREF gv_key *gv_currkey, *gv_altkey; +GBLREF int4 gv_keysize; +GBLREF gv_namehead *gv_target; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gd_region *gv_cur_region; +GBLREF uint4 dollar_tlevel; +GBLREF uint4 dollar_trestart; +GBLREF unsigned int t_tries; +GBLREF gv_namehead *reset_gv_target; +GBLREF boolean_t mupip_jnl_recover; + +static mstr global_collation_mstr; + +void gvcst_root_search(void) +{ + srch_blk_status *h0; + uchar_ptr_t c, c1; + sm_uc_ptr_t rp; + unsigned short rlen, hdr_len; + uchar_ptr_t subrec_ptr; + enum cdb_sc status; + boolean_t gbl_target_was_set; + gv_namehead *save_targ; + mname_entry *gvent; + int altkeylen; + block_id lcl_root; + + assert((dba_bg == gv_cur_region->dyn.addr->acc_meth) || (dba_mm == gv_cur_region->dyn.addr->acc_meth)); + assert(gv_altkey->top == gv_currkey->top); + assert(gv_altkey->top == gv_keysize); + assert(gv_currkey->end < gv_currkey->top); + for (c = gv_altkey->base, c1 = gv_currkey->base; *c1;) + *c++ = *c1++; + *c++ = 0; + *c = 0; + gv_altkey->end = c - gv_altkey->base; + assert(gv_altkey->end < gv_altkey->top); + assert(gv_target != cs_addrs->dir_tree); + save_targ = gv_target; + /* Check if "gv_target->gvname" matches "gv_altkey->base". If not, there is a name mismatch (out-of-design situation). + * This check is temporary until we catch the situation that caused D9H02-002641 */ + /* --- Check BEGIN --- */ + gvent = &save_targ->gvname; + altkeylen = gv_altkey->end - 1; + if (!altkeylen || (altkeylen != gvent->var_name.len) || memcmp(gv_altkey->base, gvent->var_name.addr, gvent->var_name.len)) + GTMASSERT; + /* --- Check END --- */ + if (INVALID_GV_TARGET != reset_gv_target) + gbl_target_was_set = TRUE; + else + { + gbl_target_was_set = FALSE; + reset_gv_target = save_targ; + } + gv_target = cs_addrs->dir_tree; + lcl_root = 0; + T_BEGIN_READ_NONTP_OR_TP(ERR_GVGETFAIL); + /* We better hold crit in the final retry (TP & non-TP). Only exception is journal recovery */ + assert((t_tries < CDB_STAGNATE) || cs_addrs->now_crit || mupip_jnl_recover); + for (;;) + { + hdr_len = rlen = 0; + gv_target = cs_addrs->dir_tree; + if (dollar_trestart) + gv_target->clue.end = 0; + assert(0 == save_targ->root); + if (cdb_sc_normal == (status = gvcst_search(gv_altkey, 0))) + { + if (gv_altkey->end + 1 == gv_target->hist.h[0].curr_rec.match) + { + h0 = gv_target->hist.h; + rp = (h0->buffaddr + h0->curr_rec.offset); + hdr_len = SIZEOF(rec_hdr) + gv_altkey->end + 1 - ((rec_hdr_ptr_t)rp)->cmpc; + GET_USHORT(rlen, rp); + if (FALSE == (CHKRECLEN(rp, h0->buffaddr, rlen)) || (rlen < hdr_len + SIZEOF(block_id))) + { + gv_target->clue.end = 0; + RESET_GV_TARGET_LCL_AND_CLR_GBL(save_targ); + t_retry(cdb_sc_rmisalign); + continue; + } + GET_LONG(lcl_root, (rp + hdr_len)); + if (rlen > hdr_len + SIZEOF(block_id)) + { + assert(NULL != global_collation_mstr.addr || 0 == global_collation_mstr.len); + if (global_collation_mstr.len < rlen - (hdr_len + SIZEOF(block_id))) + { + if (NULL != global_collation_mstr.addr) + free(global_collation_mstr.addr); + global_collation_mstr.len = rlen - (hdr_len + SIZEOF(block_id)); + global_collation_mstr.addr = (char *)malloc(global_collation_mstr.len); + } + /* the memcpy needs to be done here instead of out of for loop for + * concurrency consideration. We don't use s2pool because the pointer rp is 64 bits + */ + memcpy(global_collation_mstr.addr, rp + hdr_len + SIZEOF(block_id), + rlen - (hdr_len + SIZEOF(block_id))); + } + if (dollar_tlevel) + { + status = tp_hist(NULL); + if (cdb_sc_normal != status) + { + gv_target->clue.end = 0; + RESET_GV_TARGET_LCL_AND_CLR_GBL(save_targ); + t_retry(status); + continue; + } + break; + } + } + if (!dollar_tlevel) + { + if ((trans_num)0 != t_end(&gv_target->hist, NULL, TN_NOT_SPECIFIED)) + break; + } else + { + status = tp_hist(NULL); + if (cdb_sc_normal == status) + break; + gv_target->clue.end = 0; + RESET_GV_TARGET_LCL_AND_CLR_GBL(save_targ); + t_retry(status); + continue; + } + } else + { + gv_target->clue.end = 0; + RESET_GV_TARGET_LCL_AND_CLR_GBL(save_targ); + t_retry(status); + continue; + } + } + save_targ->root = lcl_root; /* now that we know the transaction validated fine, set root block in gv_target */ + RESET_GV_TARGET_LCL_AND_CLR_GBL(save_targ); + if (rlen > hdr_len + SIZEOF(block_id)) + { + assert(NULL != global_collation_mstr.addr); + subrec_ptr = get_spec((uchar_ptr_t)global_collation_mstr.addr, + (int)(rlen - (hdr_len + SIZEOF(block_id))), COLL_SPEC); + if (subrec_ptr) + { + gv_target->nct = *(subrec_ptr + COLL_NCT_OFFSET); + gv_target->act = *(subrec_ptr + COLL_ACT_OFFSET); + gv_target->ver = *(subrec_ptr + COLL_VER_OFFSET); + } else + { + gv_target->nct = 0; + gv_target->act = 0; + gv_target->ver = 0; + } + } else + { + gv_target->nct = 0; + gv_target->act = cs_addrs->hdr->def_coll; + gv_target->ver = cs_addrs->hdr->def_coll_ver; + } + if (gv_target->act) + act_in_gvt(); + assert(gv_target->act || NULL == gv_target->collseq); + return; +} diff --git a/sr_port/gvcst_rtsib.c b/sr_port/gvcst_rtsib.c new file mode 100644 index 0000000..ff5235a --- /dev/null +++ b/sr_port/gvcst_rtsib.c @@ -0,0 +1,128 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "cdb_sc.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "copy.h" + +/* Include prototypes */ +#include "t_qread.h" +#include "gvcst_protos.h" /* for gvcst_search_blk,gvcst_rtsib prototype */ + +/* construct a new array which is the path to the + right sibling of the leaf of the old array + note: the offset's will be correct, but NOT the 'match' members +*/ + +/* WARNING: assumes that the search history for the current target is in gv_target.hist */ + +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gv_namehead *gv_target; +GBLREF gv_key *gv_currkey; +GBLREF unsigned char rdfail_detail; +GBLREF unsigned int t_tries; +GBLREF srch_blk_status *first_tp_srch_status; /* overriding value of srch_blk_status given by t_qread in case of TP */ + +enum cdb_sc gvcst_rtsib(srch_hist *full_hist, int level) +{ + srch_blk_status *old, *new, *old_base, *new_base; + rec_hdr_ptr_t rp; + enum cdb_sc ret_val; + block_id blk; + unsigned short rec_size, temp_short; + sm_uc_ptr_t buffer_address; + int4 cycle; + + new_base = &full_hist->h[level]; + old = old_base = &gv_target->hist.h[level]; + for (;;) + { + old++; + if (0 == old->blk_num) + return cdb_sc_endtree; + if (old->cr && (old->blk_num != old->cr->blk)) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_lostcr; + } + if (cdb_sc_normal != (ret_val = gvcst_search_blk(gv_currkey, old))) + return ret_val; + buffer_address = old->buffaddr; + temp_short = old->curr_rec.offset; + rp = (rec_hdr_ptr_t)((sm_uc_ptr_t)buffer_address + temp_short); + GET_USHORT(rec_size, &rp->rsiz); + if ((sm_uc_ptr_t)rp + rec_size > buffer_address + (unsigned int)((blk_hdr_ptr_t)buffer_address)->bsiz) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_rmisalign; + } + if ((unsigned int)((blk_hdr_ptr_t)buffer_address)->bsiz > (temp_short + rec_size)) + break; + } + /* old now points to the first block which did not have a star key pointer*/ + new = new_base + (old - old_base + 1); + full_hist->depth = (int)(level + old - old_base); + (new--)->blk_num = 0; + new->tn = old->tn; + new->cse = NULL; + new->first_tp_srch_status = old->first_tp_srch_status; + assert(new->level == old->level); + assert(new->blk_target == old->blk_target); + new->blk_num = old->blk_num; + new->buffaddr = old->buffaddr; + new->prev_rec = old->curr_rec; + new->cycle = old->cycle; + new->cr = old->cr; + temp_short = new->prev_rec.offset; + rp = (rec_hdr_ptr_t)(temp_short + new->buffaddr); + GET_USHORT(rec_size, &rp->rsiz); + temp_short += rec_size; + new->curr_rec.offset = temp_short; + new->curr_rec.match = 0; + if (((blk_hdr_ptr_t)old->buffaddr)->bsiz < temp_short) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_rmisalign; + } + rp = (rec_hdr_ptr_t)(old->buffaddr + temp_short); + while (--new >= new_base) + { + --old; + GET_USHORT(rec_size, &rp->rsiz); + if ((sm_uc_ptr_t)rp + rec_size > buffer_address + (unsigned int)((blk_hdr_ptr_t)buffer_address)->bsiz) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_rmisalign; + } + GET_LONG(blk, ((sm_int_ptr_t)((sm_uc_ptr_t)rp + rec_size - SIZEOF(block_id)))); + new->tn = cs_addrs->ti->curr_tn; + new->cse = NULL; + if (NULL == (buffer_address = t_qread(blk, &new->cycle, &new->cr))) + return((enum cdb_sc)rdfail_detail); + new->first_tp_srch_status = first_tp_srch_status; + assert(new->level == old->level); + assert(new->blk_target == old->blk_target); + new->blk_num = blk; + new->buffaddr = buffer_address; + new->prev_rec.match = 0; + new->prev_rec.offset = 0; + new->curr_rec.match = 0; + new->curr_rec.offset = SIZEOF(blk_hdr); + rp = (rec_hdr_ptr_t)(buffer_address + SIZEOF(blk_hdr)); + } + return cdb_sc_normal; +} diff --git a/sr_port/gvcst_search.c b/sr_port/gvcst_search.c new file mode 100644 index 0000000..221820a --- /dev/null +++ b/sr_port/gvcst_search.c @@ -0,0 +1,502 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "cdb_sc.h" +#include "gdsroot.h" +#include "gdskill.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsfhead.h" +#include "gdscc.h" +#include "copy.h" +#include "filestruct.h" +#include "jnl.h" +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h and cws_insert.h */ +#include "tp.h" +#include "gvcst_blk_build.h" +#include "t_qread.h" +#include "longset.h" /* needed for cws_insert.h */ +#include "hashtab.h" /* needed for cws_insert.h */ +#include "cws_insert.h" +#include "gvcst_protos.h" /* for gvcst_search_blk,gvcst_search_tail,gvcst_search prototype */ +#include "min_max.h" + +GBLREF gd_region *gv_cur_region; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gv_namehead *gv_target; +GBLREF uint4 dollar_tlevel; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF unsigned char rdfail_detail; +GBLREF sgm_info *sgm_info_ptr; +GBLREF unsigned int t_tries; +GBLREF srch_blk_status *first_tp_srch_status; /* overriding value of srch_blk_status given by t_qread in case of TP */ +GBLREF trans_num local_tn; /* transaction number for THIS PROCESS */ +GBLREF boolean_t tp_restart_syslog; /* for the TP_TRACE_HIST_MOD macro */ +GBLREF boolean_t mu_reorg_process; +GBLREF char gvcst_search_clue; + +#define SET_GVCST_SEARCH_CLUE(X) gvcst_search_clue = X; + +enum cdb_sc gvcst_search(gv_key *pKey, /* Key to search for */ + srch_hist *pHist) /* History to fill in*/ +{ + unsigned char nLevl; + enum cdb_sc status; + register int n1; + register uchar_ptr_t c1, c2; + register sm_uc_ptr_t pRec, pBlkBase; + register gv_namehead *pTarg; /* Local copy of gv_target; hope it gets put into register */ + register srch_blk_status *pCurr; + register srch_blk_status *pNonStar; + register srch_hist *pTargHist; + block_id nBlkId; + cache_rec_ptr_t cr; + int cycle; + unsigned short n0, nKeyLen; + trans_num tn; + cw_set_element *cse; + off_chain chain1, chain2; + srch_blk_status *tp_srch_status, *srch_status, *leaf_blk_hist; + boolean_t already_built, is_mm; + ht_ent_int4 *tabent; + sm_uc_ptr_t buffaddr; + trans_num blkhdrtn; + int hist_size; + + pTarg = gv_target; + assert(NULL != pTarg); + assert(pTarg->root); + assert(pKey != &pTarg->clue); + nKeyLen = pKey->end + 1; + + assert(!dollar_tlevel || ((NULL != sgm_info_ptr) && (cs_addrs->sgm_info_ptr == sgm_info_ptr))); + SET_GVCST_SEARCH_CLUE(0); + INCR_DB_CSH_COUNTER(cs_addrs, n_gvcst_srches, 1); + pTargHist = (NULL == pHist ? &pTarg->hist : pHist); + /* If FINAL RETRY and TP then we can safely use clues of gv_targets that have been referenced in this + * TP transaction (read_local_tn == local_tn). While that is guaranteed to be true for all updates, it + * does not hold good for READs since we allow a lot more reads to be done inside a transaction compared + * to the # of updates allowed. We allow the same global to be read multiple times inside the same transaction + * using different global buffers for each read. This means that we need to validate any clues from the first + * read before using it for the second read even if it is in the final retry. This validation is done inside + * the below IF block. As for gv_targets which are referenced for the very first time in this TP transaction, + * we have no easy way of determining if their clues are still uptodate (i.e. using the clue will guarantee us + * no restart) and since we are in the final retry, we dont want to take a risk. So dont use the clue in that case. + * + * If FINAL RETRY and Non-TP, we will be dealing with only ONE gv_target so its clue would have been reset as + * part of the penultimate restart so we dont have any of the above issue in the non-tp case. The only exception + * is if we are in gvcst_kill in which case, gvcst_search will be called twice and the clue could be non-zero + * for the second invocation. In this case, the clue is guaranteed to be uptodate since it was set just now + * as part of the first invocation. So no need to do anything about clue in final retry for Non-TP. + */ + if ((0 != pTarg->clue.end) && ((CDB_STAGNATE > t_tries) || !dollar_tlevel || (pTarg->read_local_tn == local_tn))) + { /* Have non-zero clue. Check if it is usable for the current search key. If so validate clue then and use it. */ + /* In t_end, we skipped validating the clue in case of reorg due to the assumption that reorg never + * uses the clue i.e. it nullifies the clue before calling gvcst_search. Assert that here. + */ + assert(!mu_reorg_process); + INCR_DB_CSH_COUNTER(cs_addrs, n_gvcst_srch_clues, 1); + status = cdb_sc_normal; /* clue is usable unless proved otherwise */ + if (NULL != pHist) + { /* Copy the full srch_hist and set loop terminator flag in unused srch_blk_status entry. + * If in TP and if leaf block in history has cse, we are guaranteed that it is built by the + * immediately previous call to "gvcst_search" (called by gvcst_kill which does two calls to + * gvcst_search of which this invocation is the second) so no need to build the block like + * is done for the (NULL == pHist) case below. Assert that and some more. + */ + hist_size = HIST_SIZE(pTarg->hist); + memcpy(pHist, &pTarg->hist, hist_size); + ((srch_blk_status *)((char *)pHist + hist_size))->blk_num = 0; +# ifdef DEBUG + if (dollar_tlevel) + { + leaf_blk_hist = &pHist->h[0]; + assert(0 == leaf_blk_hist->level); + chain1 = *(off_chain *)&leaf_blk_hist->blk_num; + if (chain1.flag == 1) + { + assert((int)chain1.cw_index < sgm_info_ptr->cw_set_depth); + tp_get_cw(sgm_info_ptr->first_cw_set, (int)chain1.cw_index, &cse); + } else + { + tp_srch_status = leaf_blk_hist->first_tp_srch_status; + ASSERT_IS_WITHIN_TP_HIST_ARRAY_BOUNDS(tp_srch_status, sgm_info_ptr); + cse = (NULL != tp_srch_status) ? tp_srch_status->cse : NULL; + } + assert((NULL == cse) || cse->done); + } +# endif + } else if (dollar_tlevel) + { /* First nullify first_tp_srch_status member in gv_target history if out-of-date. This is logically done + * at tp_clean_up time but delayed until the time this gv_target is used next in a transaction. This way + * it saves some CPU cycles. pTarg->read_local_tn tells us whether this is the first usage of this + * gv_target in this TP transaction and if so we need to reset the out-of-date field. + */ + if (pTarg->read_local_tn != local_tn) + { + for (srch_status = &pTarg->hist.h[0]; HIST_TERMINATOR != srch_status->blk_num; srch_status++) + srch_status->first_tp_srch_status = NULL; + } + /* TP & going to use clue. check if clue path contains a leaf block with a corresponding unbuilt + * cse from the previous traversal. If so build it first before gvcst_search_blk/gvcst_search_tail. + */ + tp_srch_status = NULL; + leaf_blk_hist = &pTarg->hist.h[0]; + assert(0 == leaf_blk_hist->level); + chain1 = *(off_chain *)&leaf_blk_hist->blk_num; + if (chain1.flag == 1) + { + if ((int)chain1.cw_index >= sgm_info_ptr->cw_set_depth) + { + assert(sgm_info_ptr->tp_csa == cs_addrs); + assert(FALSE == cs_addrs->now_crit); + return cdb_sc_blknumerr; + } + tp_get_cw(sgm_info_ptr->first_cw_set, (int)chain1.cw_index, &cse); + } else + { + nBlkId = (block_id)leaf_blk_hist->blk_num; + tp_srch_status = leaf_blk_hist->first_tp_srch_status; + if ((NULL == tp_srch_status) + && (NULL != (tabent = lookup_hashtab_int4(sgm_info_ptr->blks_in_use, + (uint4 *)&leaf_blk_hist->blk_num)))) + tp_srch_status = tabent->value; + ASSERT_IS_WITHIN_TP_HIST_ARRAY_BOUNDS(tp_srch_status, sgm_info_ptr); + cse = (NULL != tp_srch_status) ? tp_srch_status->cse : NULL; + } + assert(!cse || !cse->high_tlevel); + if ((NULL == tp_srch_status) || (tp_srch_status->blk_target == leaf_blk_hist->blk_target)) + { /* Either the leaf level block in clue is not already present in the current TP transaction's + * hashtable OR it is already present and the corresponding globals match. If they dont match + * we know for sure the clue is out-of-date (i.e. using it will lead to a transaction restart) + * and hence needs to be discarded. + */ + leaf_blk_hist->first_tp_srch_status = tp_srch_status; + if (NULL != cse) + { + if (!cse->done) + { /* there's a private copy and it's not up to date */ + already_built = (NULL != cse->new_buff); + gvcst_blk_build(cse, cse->new_buff, 0); + /* Validate the block's search history right after building a private copy. + * This is not needed in case gvcst_search is going to reuse the clue's search + * history and return (because tp_hist will do the validation of this block). + * But if gvcst_search decides to do a fresh traversal (because the clue does not + * cover the path of the current input key etc.) the block build that happened now + * will not get validated in tp_hist since it will instead be given the current + * key's search history path (a totally new path) for validation. Since a private + * copy of the block has been built, tp_tend would also skip validating this block + * so it is necessary that we validate the block right here. Since it is tricky to + * accurately differentiate between the two cases, we do the validation + * unconditionally here (besides it is only a few if checks done per block build + * so it is considered okay performance-wise). + */ + if (!already_built && !chain1.flag) + { /* is_mm is calculated twice, but this is done so as to speed up the + * most-frequent path, i.e. when there is a clue and either no cse or + * cse->done is TRUE + */ + is_mm = (dba_mm == cs_data->acc_meth); + buffaddr = tp_srch_status->buffaddr; + cr = tp_srch_status->cr; + assert(tp_srch_status && (is_mm || cr) && buffaddr); + blkhdrtn = ((blk_hdr_ptr_t)buffaddr)->tn; + if (TP_IS_CDB_SC_BLKMOD3(cr, tp_srch_status, blkhdrtn)) + { + assert(CDB_STAGNATE > t_tries); + TP_TRACE_HIST_MOD(leaf_blk_hist->blk_num, gv_target, + tp_blkmod_gvcst_srch, cs_data, tp_srch_status->tn, + blkhdrtn, ((blk_hdr_ptr_t)buffaddr)->levl); + return cdb_sc_blkmod; + } + if (!is_mm && ((tp_srch_status->cycle != cr->cycle) + || (tp_srch_status->blk_num != cr->blk))) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_lostcr; + } + } + cse->done = TRUE; + leaf_blk_hist->cr = 0; + leaf_blk_hist->cycle = CYCLE_PVT_COPY; + leaf_blk_hist->buffaddr = cse->new_buff; + } else + { /* Keep leaf_blk_hist->buffaddr and cse->new_buff in sync. Dont know how they + * cannot be the same but it seems possible if the gvcst_blk_build happened as + * part of a t_qread call (which does not have enough information to update the + * search history buffer address) without going through gvcst_search. Since the + * consequences of these two not being in sync are database damage, we fix them + * in pro just in case they are different. + */ + assert(leaf_blk_hist->buffaddr == cse->new_buff); + leaf_blk_hist->buffaddr = cse->new_buff; + } + } + } else + status = cdb_sc_lostcr; /* two different gv_targets point to same block; discard out-of-date clue */ + } + /* Validate EVERY level in the clue before using it for ALL retries. This way we avoid unnecessary restarts. + * This is NECESSARY for the final retry (e.g. in a TP transaction that does LOTS of reads of different globals, + * it is possible that one global's clue is invalidated by a later read of another global) and is DESIRABLE (for + * performance reasons) in the other tries. The cost of a restart (particularly in TP) is very high that it is + * considered okay to take the hit of validating the entire clue before using it even if it is not the final retry. + */ + DEBUG_ONLY(is_mm = (dba_mm == cs_data->acc_meth);) + if (cdb_sc_normal == status) + { + for (srch_status = &pTargHist->h[0]; HIST_TERMINATOR != srch_status->blk_num; srch_status++) + { + assert(srch_status->level == srch_status - &pTargHist->h[0]); + assert(is_mm || (NULL == srch_status->cr) || (NULL != srch_status->buffaddr)); + cr = srch_status->cr; + assert(!is_mm || (NULL == cr)); + if (TP_IS_CDB_SC_BLKMOD(cr, srch_status)) + { + status = cdb_sc_blkmod; + break; + } + if (NULL != cr) + { + assert(NULL != srch_status->buffaddr); + if (srch_status->cycle != cr->cycle) + { + status = cdb_sc_lostcr; + break; + } + if (CDB_STAGNATE <= t_tries || mu_reorg_process) + CWS_INSERT(cr->blk); + cr->refer = TRUE; + } + } + } + if (cdb_sc_normal == status) + { /* Now that we are ready to use the clue, put more-likely case earlier in the if then else sequence. + * For sequential reads of globals, we expect the tail of the clue to be much more used than the head. + * For random reads, both are equally probable and hence it doesn't matter. + * The case (0 == n1) is not expected a lot (relatively) since the application may be able to optimize + * a number of reads of the same key into one read by using a local-variable to store the value. + */ + if (0 < (n1 = memcmp(pKey->base, pTarg->clue.base, nKeyLen))) + { + if (memcmp(pKey->base, pTarg->last_rec->base, nKeyLen) <= 0) + { + SET_GVCST_SEARCH_CLUE(1); + status = gvcst_search_tail(pKey, pTargHist->h, &pTarg->clue); + if (NULL == pHist) + { /* Implies the search history is being filled in pTarg->hist so we can + * safely update pTarg->clue to reflect the new search key. It is important + * that this clue update be done AFTER the gvcst_search_tail invocation + * (as that needs to pass the previous clue key). + */ + COPY_CURRKEY_TO_GVTARGET_CLUE(pTarg, pKey); + } + INCR_DB_CSH_COUNTER(cs_addrs, n_clue_used_tail, 1); + return status; + } + } else if (0 > n1) + { + if (memcmp(pKey->base, pTarg->first_rec->base, nKeyLen) >= 0) + { + SET_GVCST_SEARCH_CLUE(3); + status = gvcst_search_blk(pKey, pTargHist->h); + if (NULL == pHist) + { /* Implies the search history is being filled in pTarg->hist so we can + * safely update pTarg->clue to reflect the new search key. It does not + * matter if we update the clue BEFORE or AFTER the gvcst_search_blk + * invocation but for consistency with the gvcst_search_tail invocation + * we keep it AFTER. + */ + COPY_CURRKEY_TO_GVTARGET_CLUE(pTarg, pKey); + } + INCR_DB_CSH_COUNTER(cs_addrs, n_clue_used_head, 1); + return status; + } + } else + { + SET_GVCST_SEARCH_CLUE(2); + INCR_DB_CSH_COUNTER(cs_addrs, n_clue_used_same, 1); + return cdb_sc_normal; + } + } + } + nBlkId = pTarg->root; + tn = cs_addrs->ti->curr_tn; + if (NULL == (pBlkBase = t_qread(nBlkId, (sm_int_ptr_t)&cycle, &cr))) + return (enum cdb_sc)rdfail_detail; + nLevl = ((blk_hdr_ptr_t)pBlkBase)->levl; + if (MAX_BT_DEPTH < (int)nLevl) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_maxlvl; + } + if (0 == (int)nLevl) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_badlvl; + } + is_mm = (dba_mm == cs_data->acc_meth); + pTargHist->depth = (int)nLevl; + pCurr = &pTargHist->h[nLevl]; + (pCurr + 1)->blk_num = 0; + pCurr->tn = tn; + pCurr->first_tp_srch_status = first_tp_srch_status; + pCurr->cycle = cycle; + pCurr->cr = cr; + pNonStar = NULL; + for (;;) + { + assert(pCurr->level == nLevl); + pCurr->cse = NULL; + pCurr->blk_num = nBlkId; + pCurr->buffaddr = pBlkBase; + if (cdb_sc_normal != (status = gvcst_search_blk(pKey, pCurr))) + return status; + if (0 == nLevl) + break; + if ((n0 = pCurr->curr_rec.offset) >= ((blk_hdr_ptr_t)pBlkBase)->bsiz) + n0 = pCurr->prev_rec.offset; + pRec = pBlkBase + n0; + GET_USHORT(n0, &((rec_hdr_ptr_t)pRec)->rsiz); + if (FALSE == CHKRECLEN(pRec, pBlkBase, n0)) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_rmisalign; + } + GET_LONG(nBlkId, (pRec + n0 - SIZEOF(block_id))); + if (is_mm) + { + PUT_LONG(&chain2, nBlkId); + if ((0 == chain2.flag) && (nBlkId > cs_addrs->total_blks)) + { /* private copy should be taken care of by .flag */ + if (cs_addrs->total_blks < cs_addrs->ti->total_blks) + return cdb_sc_helpedout; + else + return cdb_sc_blknumerr; + } + } + if (BSTAR_REC_SIZE != n0) + pNonStar = pCurr; + pCurr--; + pCurr->tn = cs_addrs->ti->curr_tn; + if (NULL == (pBlkBase = t_qread(nBlkId, (sm_int_ptr_t)&pCurr->cycle, &pCurr->cr))) + return (enum cdb_sc)rdfail_detail; + pCurr->first_tp_srch_status = first_tp_srch_status; + if (((blk_hdr_ptr_t)pBlkBase)->levl != --nLevl) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_badlvl; + } + } + if (NULL == pHist) + { + if ((pCurr->curr_rec.offset < SIZEOF(blk_hdr)) || + ((pCurr->curr_rec.offset == SIZEOF(blk_hdr)) && (pCurr->curr_rec.match < nKeyLen))) + { /* Clue less than first rec, invalidate */ + pTarg->clue.end = 0; + return cdb_sc_normal; + } + pRec = pBlkBase + SIZEOF(blk_hdr); + GET_USHORT(n0, &((rec_hdr_ptr_t)pRec)->rsiz); + if (FALSE == CHKRECLEN(pRec, pBlkBase, n0)) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_rmisalign; + } + c1 = pRec + SIZEOF(rec_hdr); + c2 = pTarg->first_rec->base; + if (n0 > (pTarg->first_rec->top)) + { + n0 = pTarg->first_rec->top; + status = cdb_sc_keyoflow; + } else + status = cdb_sc_rmisalign; + if (0 != n0) + { + do + { + --n0; + if ((0 == (*c2++ = *c1++)) && (0 == *c1)) + break; + } while (n0); + } + if (0 == n0) + { + assert(CDB_STAGNATE > t_tries); + return status; + } + assert(c2 < &pTarg->first_rec->base[pTarg->first_rec->top]); /* make sure we don't exceed allocated bounds */ + *c2 = *c1; + DEBUG_ONLY(pTarg->first_rec->end = c2 - pTarg->first_rec->base;) + if (NULL == pNonStar) + { + *((short *)pTarg->last_rec->base) = GVT_CLUE_LAST_REC_MAXKEY; + DEBUG_ONLY(pTarg->last_rec->end = SIZEOF(short);) + } else + { + pRec = pNonStar->buffaddr + pNonStar->curr_rec.offset; + GET_USHORT(n0, &((rec_hdr_ptr_t)pRec)->rsiz); + c1 = pNonStar->buffaddr; + if (FALSE == CHKRECLEN(pRec, c1, n0)) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_rmisalign; + } + if (pNonStar->curr_rec.match < ((rec_hdr_ptr_t)pRec)->cmpc) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_rmisalign; + } + if ((n1 = ((rec_hdr_ptr_t)pRec)->cmpc) > (int)(pTarg->last_rec->top)) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_keyoflow; + } + c2 = pTarg->last_rec->base; + if (0 != n1) + memcpy(c2, pKey->base, n1); + c2 = (sm_uc_ptr_t)c2 + n1; + + c1 = pRec + SIZEOF(rec_hdr); + if ((int)n0 > (int)(pTarg->last_rec->top) - n1) + { + n0 = pTarg->last_rec->top - n1; + status = cdb_sc_keyoflow; + } else + status = cdb_sc_rmisalign; + if (0 != n0) + { + do + { + --n0; + if ((0 == (*c2++ = *c1++)) && (0 == *c1)) + break; + } while (n0); + } + if (0 == n0) + { + assert(CDB_STAGNATE > t_tries); + return status; + } + assert(c2 < &pTarg->last_rec->base[pTarg->last_rec->top]); /* make sure we don't exceed allocated bounds */ + *c2 = *c1; + DEBUG_ONLY(pTarg->last_rec->end = c2 - pTarg->last_rec->base;) + } + COPY_CURRKEY_TO_GVTARGET_CLUE(pTarg, pKey); + } + return cdb_sc_normal; +} diff --git a/sr_port/gvcst_tp_init.c b/sr_port/gvcst_tp_init.c new file mode 100644 index 0000000..59416a0 --- /dev/null +++ b/sr_port/gvcst_tp_init.c @@ -0,0 +1,108 @@ +/**************************************************************** + * * + * Copyright 2003, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gdscc.h" +#include "gdskill.h" +#include "jnl.h" +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "hashtab.h" +#include "tp.h" +#include "tp_timeout.h" +#include "gvcst_protos.h" /* for gvcst_tp_init prototype */ + +/* Initialize the TP structures we will be using for the successive TP operations */ +void gvcst_tp_init(gd_region *greg) +{ + sgm_info *si; + sgmnt_addrs *csa; + + csa = (sgmnt_addrs *)&FILE_INFO(greg)->s_addrs; + if (NULL == csa->sgm_info_ptr) + { + si = csa->sgm_info_ptr = (sgm_info *)malloc(SIZEOF(sgm_info)); + assert(32768 > SIZEOF(sgm_info)); + memset(si, 0, SIZEOF(sgm_info)); + si->tp_hist_size = TP_MAX_MM_TRANSIZE; + si->cur_tp_hist_size = INIT_CUR_TP_HIST_SIZE; /* should be very much less than si->tp_hist_size */ + assert(si->cur_tp_hist_size <= si->tp_hist_size); + si->blks_in_use = (hash_table_int4 *)malloc(SIZEOF(hash_table_int4)); + init_hashtab_int4(si->blks_in_use, BLKS_IN_USE_INIT_ELEMS, HASHTAB_COMPACT, HASHTAB_SPARE_TABLE); + /* See comment in tp.h about cur_tp_hist_size for details */ + si->first_tp_hist = si->last_tp_hist = + (srch_blk_status *)malloc(SIZEOF(srch_blk_status) * si->cur_tp_hist_size); + si->cw_set_list = (buddy_list *)malloc(SIZEOF(buddy_list)); + initialize_list(si->cw_set_list, SIZEOF(cw_set_element), CW_SET_LIST_INIT_ALLOC); + si->tlvl_cw_set_list = (buddy_list *)malloc(SIZEOF(buddy_list)); + initialize_list(si->tlvl_cw_set_list, SIZEOF(cw_set_element), TLVL_CW_SET_LIST_INIT_ALLOC); + si->tlvl_info_list = (buddy_list *)malloc(SIZEOF(buddy_list)); + initialize_list(si->tlvl_info_list, SIZEOF(tlevel_info), TLVL_INFO_LIST_INIT_ALLOC); + si->new_buff_list = (buddy_list *)malloc(SIZEOF(buddy_list)); + initialize_list(si->new_buff_list, SIZEOF(que_ent) + csa->hdr->blk_size, NEW_BUFF_LIST_INIT_ALLOC); + si->recompute_list = (buddy_list *)malloc(SIZEOF(buddy_list)); + initialize_list(si->recompute_list, SIZEOF(key_cum_value), RECOMPUTE_LIST_INIT_ALLOC); + /* The size of the si->cr_array can go up to TP_MAX_MM_TRANSIZE, but usually is quite less. + * Therefore, initially allocate a small array and expand as needed later. + */ + if (dba_bg == greg->dyn.addr->acc_meth) + { + si->cr_array_size = si->cur_tp_hist_size; + si->cr_array = (cache_rec_ptr_ptr_t)malloc(SIZEOF(cache_rec_ptr_t) * si->cr_array_size); + } else + { + si->cr_array_size = 0; + si->cr_array = NULL; + } + si->fresh_start = TRUE; + } else + si = csa->sgm_info_ptr; + si->gv_cur_region = greg; + si->tp_csa = csa; + si->tp_csd = csa->hdr; + si->start_tn = csa->ti->curr_tn; + if (JNL_ALLOWED(csa)) + { + si->total_jnl_rec_size = csa->min_total_tpjnl_rec_size; /* Reinitialize total_jnl_rec_size */ + /* Since the following jnl-mallocs are independent of any dynamically-changeable parameter of the + * database, we can as well use the existing malloced jnl structures if at all they exist. + */ + if (NULL == si->jnl_tail) + { + si->jnl_tail = &si->jnl_head; + si->jnl_list = (buddy_list *)malloc(SIZEOF(buddy_list)); + initialize_list(si->jnl_list, SIZEOF(jnl_format_buffer), JNL_LIST_INIT_ALLOC); + si->format_buff_list = (buddy_list *)malloc(SIZEOF(buddy_list)); + /* Minimum value of elemSize is 8 due to alignment requirements of the returned memory location. + * Therefore, we request an elemSize of 8 bytes for the format-buffer and will convert as much + * bytes as we need into as many 8-byte multiple segments (see code in jnl_format). + */ + initialize_list(si->format_buff_list, JFB_ELE_SIZE, + DIVIDE_ROUND_UP(JNL_FORMAT_BUFF_INIT_ALLOC, JFB_ELE_SIZE)); + } + } else if (NULL != si->jnl_tail) + { /* journaling is currently disallowed although it was allowed (non-zero si->jnl_tail) + * during the prior use of this region. Free up unnecessary region-specific structures now. + */ + FREEUP_BUDDY_LIST(si->jnl_list); + FREEUP_BUDDY_LIST(si->format_buff_list); + si->jnl_tail = NULL; + } +} diff --git a/sr_port/gvcst_zprevious.c b/sr_port/gvcst_zprevious.c new file mode 100644 index 0000000..1e0fb62 --- /dev/null +++ b/sr_port/gvcst_zprevious.c @@ -0,0 +1,156 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "cdb_sc.h" +#include "filestruct.h" /* needed for jnl.h */ +#include "gdscc.h" /* needed for tp.h */ +#include "jnl.h" /* needed for tp.h */ +#include "gdskill.h" /* needed for tp.h */ +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" /* needed for T_BEGIN_READ_NONTP_OR_TP macro */ + +#include "t_end.h" /* prototypes */ +#include "t_retry.h" +#include "t_begin.h" +#include "gvcst_expand_key.h" +#include "gvcst_protos.h" /* for gvcst_lftsib,gvcst_search,gvcst_search_blk,gvcst_zprevious prototype */ + +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gd_region *gv_cur_region; +GBLREF gv_namehead *gv_target; +GBLREF gv_key *gv_currkey, *gv_altkey; +GBLREF int4 gv_keysize; +GBLREF uint4 dollar_tlevel; +GBLREF unsigned int t_tries; + +bool gvcst_zprevious(void) +{ + static gv_key *zprev_temp_key; + static int4 zprev_temp_keysize = 0; + blk_hdr_ptr_t bp; + bool found, two_histories; + enum cdb_sc status; + rec_hdr_ptr_t rp; + unsigned char *c1, *c2, *ctop; + srch_blk_status *bh; + srch_hist *lft_history; + + T_BEGIN_READ_NONTP_OR_TP(ERR_GVORDERFAIL); + for (;;) + { + assert(t_tries < CDB_STAGNATE || cs_addrs->now_crit); /* we better hold crit in the final retry (TP & non-TP) */ + two_histories = FALSE; + if (cdb_sc_normal == (status = gvcst_search(gv_currkey, NULL))) + { + found = TRUE; + bh = gv_target->hist.h; + if (0 == bh->prev_rec.offset) + { + two_histories = TRUE; + lft_history = gv_target->alt_hist; + status = gvcst_lftsib(lft_history); + if (cdb_sc_normal == status) + { + bh = lft_history->h; + if (cdb_sc_normal != (status = gvcst_search_blk(gv_currkey, bh))) + { + t_retry(status); + continue; + } + } else if (cdb_sc_endtree == status) + { + found = FALSE; + two_histories = FALSE; /* second history not valid */ + } else + { + t_retry(status); + continue; + } + } + if (found) + { /* store new subscipt */ + assert(gv_altkey->top == gv_currkey->top); + assert(gv_altkey->top == gv_keysize); + assert(gv_currkey->end < gv_currkey->top); + rp = (rec_hdr_ptr_t)(bh->buffaddr + bh->prev_rec.offset); + bp = (blk_hdr_ptr_t)bh->buffaddr; + c1 = gv_altkey->base; + memcpy(c1, gv_currkey->base, bh->prev_rec.match); + c1 += bh->prev_rec.match; + assert(zprev_temp_keysize <= gv_keysize); + if (zprev_temp_keysize < gv_keysize) + { + zprev_temp_keysize = gv_keysize; + GVKEY_INIT(zprev_temp_key, zprev_temp_keysize); + } + assert(zprev_temp_key->top >= gv_currkey->top); + if (cdb_sc_normal != (status = gvcst_expand_key((blk_hdr_ptr_t)bh->buffaddr, bh->prev_rec.offset, + zprev_temp_key))) + { + t_retry(status); + continue; + } + if ((zprev_temp_key->end < gv_currkey->end) && (zprev_temp_key->end <= gv_currkey->prev)) + found = FALSE; + else + { + c2 = zprev_temp_key->base + bh->prev_rec.match; + ctop = zprev_temp_key->base + zprev_temp_key->end; + for (;;) + { + if (c2 >= ctop) + { + assert(CDB_STAGNATE > t_tries); + status = cdb_sc_rmisalign; + goto restart; /* goto needed because of nested FOR loop */ + } + if (0 == (*c1++ = *c2++)) + { + *c1 = 0; + break; + } + } + } + gv_altkey->end = c1 - gv_altkey->base; + assert(gv_altkey->end < gv_altkey->top); + } + if (!dollar_tlevel) + { + if ((trans_num)0 == t_end(&gv_target->hist, two_histories ? lft_history : NULL, TN_NOT_SPECIFIED)) + continue; + } else + { + status = tp_hist(two_histories ? lft_history : NULL); + if (cdb_sc_normal != status) + { + t_retry(status); + continue; + } + } + assert(cs_data == cs_addrs->hdr); + INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_zprev, 1); + return (found && (bh->prev_rec.match >= gv_currkey->prev)); + } +restart: t_retry(status); + } +} diff --git a/sr_port/gvincr_compute_post_incr.c b/sr_port/gvincr_compute_post_incr.c new file mode 100644 index 0000000..c00c294 --- /dev/null +++ b/sr_port/gvincr_compute_post_incr.c @@ -0,0 +1,91 @@ +/**************************************************************** + * * + * Copyright 2004, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "cdb_sc.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "gdskill.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gdscc.h" +#include "min_max.h" /* needed for gdsblkops.h */ +#include "gdsblkops.h" +#include "jnl.h" +#include "copy.h" +#include "op.h" /* for op_add prototype */ + +#define GVINCR_PRE_INCR_MIN_BUFFLEN MAX_NUM_SIZE /* starting size of the malloced buffer to store pre_increment value */ + /* since the increment will mostly be a number, start with MAX_NUM_SIZE */ + +static char *gvincr_pre_incr_buff; /* buffer to hold the pre-$INCR string value before converting to numeric */ +static int gvincr_pre_incr_bufflen = 0; /* length of the currently allocated buffer, updated if expansion occurs */ +GBLREF mval *post_incr_mval; +GBLREF gv_key *gv_currkey; +GBLREF mval increment_delta_mval; /* mval holding the increment value, set by op_gvincr */ +GBLREF unsigned int t_tries; + +/* compute post_incr_mval from the current value of gv_currkey that was just now searched down the tree */ +enum cdb_sc gvincr_compute_post_incr(srch_blk_status *bh) +{ + int4 cur_blk_size; + sm_uc_ptr_t buffaddr; + rec_hdr_ptr_t rp; + unsigned short rec_size; + int4 target_key_size, data_len; + uint4 gvincr_malloc_len; + mval pre_incr_mval; + + buffaddr = bh->buffaddr; + cur_blk_size = ((blk_hdr_ptr_t)buffaddr)->bsiz; + rp = (rec_hdr_ptr_t)(buffaddr + bh->curr_rec.offset); + GET_USHORT(rec_size, &rp->rsiz); + target_key_size = bh->curr_rec.match; + assert(target_key_size == gv_currkey->end + 1); + data_len = rec_size + rp->cmpc - SIZEOF(rec_hdr) - target_key_size; + if ((0 > data_len) || (((sm_uc_ptr_t)rp + rec_size) > ((sm_uc_ptr_t)buffaddr + cur_blk_size))) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_rmisalign; + } + if (data_len > gvincr_pre_incr_bufflen) + { + if (NULL != gvincr_pre_incr_buff) + free(gvincr_pre_incr_buff); + gvincr_malloc_len = (data_len > GVINCR_PRE_INCR_MIN_BUFFLEN) ? data_len + : GVINCR_PRE_INCR_MIN_BUFFLEN; + gvincr_pre_incr_buff = (char *)malloc(gvincr_malloc_len); + gvincr_pre_incr_bufflen = gvincr_malloc_len; + } + /* malloced buffer is used for pre_incr_mval instead of stringpool because this is memory that is + * inherently used only by $INCREMENT and is needed only during the lifetime of the increment. + * keeping it in the stringpool causes it to stay until the next garbage collection which adds + * to unnecessary overheads. + */ + pre_incr_mval.mvtype = MV_STR; + pre_incr_mval.str.addr = (char *)gvincr_pre_incr_buff; + pre_incr_mval.str.len = data_len; + memcpy(pre_incr_mval.str.addr, (sm_uc_ptr_t)rp + rec_size - data_len, data_len); + op_add(&pre_incr_mval, &increment_delta_mval, post_incr_mval); + assert(MV_IS_NUMERIC(post_incr_mval)); + /* "post_incr_mval" is of numeric type, convert it to a string type so it can be used by the caller to set "value" */ + MV_FORCE_STR(post_incr_mval); /* will use stringpool to store string representation */ + /* "post_incr_mval" is a copy of the mval pointer passed to "op_gvincr" and hence is on the M-stack + * and therefore is known to the garbage collector (stp_gcol). hence it is ok for it to use the stringpool + */ + return cdb_sc_normal; +} diff --git a/sr_port/gvincr_recompute_upd_array.c b/sr_port/gvincr_recompute_upd_array.c new file mode 100644 index 0000000..ed14184 --- /dev/null +++ b/sr_port/gvincr_recompute_upd_array.c @@ -0,0 +1,206 @@ +/**************************************************************** + * * + * Copyright 2004, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "cdb_sc.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "gdskill.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gdscc.h" +#include "min_max.h" /* needed for gdsblkops.h */ +#include "gdsblkops.h" +#include "jnl.h" +#include "copy.h" +#include "gvcst_protos.h" /* for gvcst_search_blk prototypes */ +#include "op.h" /* for add_mvals prototype */ +#include "jnl_get_checksum.h" + +GBLREF uint4 dollar_tlevel; +GBLREF gd_region *gv_cur_region; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF mval *post_incr_mval; /* mval pointing to the post-$INCR value */ +GBLREF jnl_format_buffer *non_tp_jfb_ptr; +GBLREF jnl_gbls_t jgbl; +GBLREF char *update_array, *update_array_ptr; +GBLREF int gv_fillfactor, rc_set_fragment; /* Contains offset within data at which data fragment starts */ +GBLREF unsigned char cw_set_depth; +GBLREF gv_key *gv_currkey; +GBLREF unsigned int t_tries; +GBLREF uint4 update_array_size; +GBLREF gv_namehead *gv_target; + +/* -------------------------------------------------------------------------------------------- + * This code is very similar to the code in gvcst_put for the non-block-split case as well as + * the code in recompute_upd_array in tp_tend.c. All of these need to be maintained in sync. + * -------------------------------------------------------------------------------------------- + */ + +enum cdb_sc gvincr_recompute_upd_array(srch_blk_status *bh, struct cw_set_element_struct *cse, cache_rec_ptr_t cr) +{ + blk_segment *bs1, *bs_ptr; + char *va; + enum cdb_sc status; + int4 blk_size, blk_fill_size, cur_blk_size, blk_seg_cnt, delta, tail_len, new_rec_size; + int4 target_key_size, data_len; + mstr value; + rec_hdr_ptr_t curr_rec_hdr, rp; + sm_uc_ptr_t cp1, buffaddr; + unsigned short rec_size; + jnl_format_buffer *jfb; + blk_hdr_ptr_t old_block; + sgmnt_addrs *csa; + + csa = cs_addrs; + assert(!dollar_tlevel); /* this recomputation is currently supported only for non-TP */ + /* To support this for TP would require addressing a lot more issues. Examples are + * a) Currently we format jnl records only for explicit updates and not for implicit updates (updates in trigger code). + * All such triggers updates currently happen inside of a TP (even if the explicit update is non-TP, there + * is an implicit TP wrapper). Therefore we need to record more information as to whether this update + * to the database needs a corresponding format of the logical journal record or not. + */ + assert(0 == cse->level); /* better be a leaf-level block */ + assert(csa->now_crit); + assert(!cse->level && (gds_t_write == cse->mode) && (NULL == cse->new_buff) && (GDS_WRITE_PLAIN == cse->write_type)); + blk_size = cs_data->blk_size; /* "blk_size" is also used by the BLK_FINI macro below */ + blk_fill_size = (blk_size * gv_fillfactor) / 100 - cs_data->reserved_bytes; + /* clues for gv_target involved in recomputation need not be nullified since only the value changes (not the key) */ + assert(CR_NOTVALID != (sm_long_t)cr); + if (NULL == cr || CR_NOTVALID == (sm_long_t)cr || (0 <= cr->read_in_progress)) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_lostcr; + } + if (cr->in_tend) + { /* Possible if this cache-record is being modified concurrently by another process in bg_update_phase2. + * Normally t_qread would have waited for this to complete before returning. But it is possible in some + * cases to bypass t_qread (e.g. gv_target->clue.end is non-zero). In this case we have two options. + * a) Signal a restart. This will cause clue.end to get reset to 0 and will now go through t_qread. + * b) Wait for in_tend to become non-zero and then proceed. This will save a restart. + * Since we are not in TP the overhead of restarting is not that bad. + * Since we hold crit at this point, we decide not to wait. We choose (a). + */ + assert(CDB_STAGNATE > t_tries); + return cdb_sc_blkmod; + } + buffaddr = bh->buffaddr; + target_key_size = gv_currkey->end + 1; + if (cdb_sc_normal != (status = gvcst_search_blk(gv_currkey, bh))) + { + assert(CDB_STAGNATE > t_tries); + return status; + } + if (target_key_size != bh->curr_rec.match) /* key does not exist, nothing doable here, restart transaction */ + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_blkmod; + } + cur_blk_size = ((blk_hdr_ptr_t)buffaddr)->bsiz; + rp = (rec_hdr_ptr_t)(buffaddr + bh->curr_rec.offset); + GET_USHORT(rec_size, &rp->rsiz); + data_len = rec_size + rp->cmpc - SIZEOF(rec_hdr) - target_key_size; + if (cdb_sc_normal != (status = gvincr_compute_post_incr(bh))) + { + assert(CDB_STAGNATE > t_tries); + return status; + } + assert(MV_IS_STRING(post_incr_mval)); /* gvincr_recompute_post_incr should have set it to be a of type MV_STR */ + value = post_incr_mval->str; + new_rec_size = rec_size - data_len + value.len; + delta = new_rec_size - rec_size; + if ((cur_blk_size + delta) > blk_fill_size) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_blksplit; + } + if (0 != rc_set_fragment) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_mkblk; /* let gvcst_put do the recomputation out of crit in case of rc_set */ + } + /* Note that a lot of the code below relies on the fact that we are in non-TP. For TP we need to do extra stuff */ + assert(NULL != update_array); + assert(NULL != update_array_ptr); + assert(0 != update_array_size); + assert(update_array + update_array_size >= update_array_ptr); + assert(1 == cw_set_depth); + /* since cw_set_depth is guaranteed to be 1 (by the above assert), we can be sure that the only update array space we would + * have used is for the current (and only) cw_set_element "cse" and hence can reuse the space by resetting update_array_ptr + */ + assert(ROUND_UP2((INTPTR_T)update_array, UPDATE_ELEMENT_ALIGN_SIZE) == (INTPTR_T)cse->upd_addr); + RESET_UPDATE_ARRAY; /* do not use CHECK_AND_RESET_UPDATE_ARRAY since we are knowingly resetting an active update array */ + BLK_INIT(bs_ptr, bs1); + BLK_SEG(bs_ptr, buffaddr + SIZEOF(blk_hdr), bh->curr_rec.offset - SIZEOF(blk_hdr)); + BLK_ADDR(curr_rec_hdr, SIZEOF(rec_hdr), rec_hdr); + curr_rec_hdr->rsiz = new_rec_size; + curr_rec_hdr->cmpc = bh->prev_rec.match; + BLK_SEG(bs_ptr, (sm_uc_ptr_t)curr_rec_hdr, SIZEOF(rec_hdr)); + BLK_ADDR(cp1, target_key_size - bh->prev_rec.match, unsigned char); + memcpy(cp1, gv_currkey->base + bh->prev_rec.match, target_key_size - bh->prev_rec.match); + BLK_SEG(bs_ptr, cp1, target_key_size - bh->prev_rec.match); + assert(0 != value.len); + BLK_ADDR(va, value.len, char); + memcpy(va, value.addr, value.len); + BLK_SEG(bs_ptr, (unsigned char *)va, value.len); + rp = (rec_hdr_ptr_t)((sm_uc_ptr_t)rp + rec_size); + tail_len = (int4)(cur_blk_size - ((sm_uc_ptr_t)rp - buffaddr)); + assert(tail_len >= 0); /* else gvincr_recompute_post_incr would have returned cdb_sc_rmisalign and we will not be here */ + if (tail_len > 0) + { + BLK_SEG(bs_ptr, (sm_uc_ptr_t)rp, tail_len); + } + if (0 == BLK_FINI(bs_ptr, bs1)) + { + assert(CDB_STAGNATE > t_tries); + return cdb_sc_mkblk; + } + cse->upd_addr = (unsigned char *)bs1; + /* assert that cse->old_block is indeed pointing to the buffer that the cache-record is pointing to. + * this is necessary to ensure that we are copying "ondsk_blkver" from the correct cache-record. + * there is a possibility that this assert might not hold true which is if we are in a restartable situation. + * but in that case do the same check that t_end will perform to determine this. + */ + assert((cse->old_block == (sm_uc_ptr_t)GDS_REL2ABS(cr->buffaddr)) || (bh->cycle != cr->cycle) || (bh->cr != cr)); + cse->ondsk_blkver = cr->ondsk_blkver; + cse->done = FALSE; + /* Reformat the logical SET jnl-record if we need to write logical records. But recompute checksums for PBLK record + * ONLY IF journaling is enabled. Do not need to do this in the case REPL_WAS_ENABLED(csa) is TRUE as replication + * only cares about logical records. Hence the separation of the code below into two "if" blocks. + */ + if (JNL_WRITE_LOGICAL_RECS(csa)) + jfb = jnl_format(JNL_SET, gv_currkey, post_incr_mval, 0); /* Re-format the logical SET jnl-record */ + if (JNL_ENABLED(csa)) + { /* Recompute checksums in case necessary */ + if (csa->jnl_before_image && (NULL != cse->old_block)) + { + old_block = (blk_hdr_ptr_t)cse->old_block; + if (old_block->tn < csa->jnl->jnl_buff->epoch_tn) + cse->blk_checksum = jnl_get_checksum((uint4 *)old_block, csa, old_block->bsiz); + else + cse->blk_checksum = 0; + } + } + assert(NULL != gv_target); + /* If clue is known to be non-zero, we have the potential for the first_rec part of it to be unreliable. + * Reset it to be safe. See comment in similar section in tp_hist for details on why. + */ + if (gv_target->clue.end) + GVT_CLUE_INVALIDATE_FIRST_REC(gv_target); + return cdb_sc_normal; +} diff --git a/sr_port/gvinit.c b/sr_port/gvinit.c new file mode 100644 index 0000000..2a3eb3e --- /dev/null +++ b/sr_port/gvinit.c @@ -0,0 +1,41 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "dpgbldir.h" + +GBLREF gd_addr *gd_header; + +void gvinit(void) +{ + mval v; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + /* if gd_header is null then get the current one, and update the gd_map */ + if (!gd_header) + { + SET_GD_HEADER(v); + SET_GD_MAP; + } + DEBUG_ONLY(else GD_HEADER_ASSERT); + /* May get in here after an extended ref call OR in mupip journal recover forward processing (with + * function call graph "mur_output_record/gvcst_put/gvtr_init/gvtr_db_tpwrap/op_tstart"). + * In either case it is possible that gv_currkey has already been set up, so dont lose any preexisting keys. + */ + GVKEYSIZE_INCREASE_IF_NEEDED(DBKEYSIZE(gd_header->regions->max_key_size)); +} diff --git a/sr_port/gvn.c b/sr_port/gvn.c new file mode 100644 index 0000000..af96598 --- /dev/null +++ b/sr_port/gvn.c @@ -0,0 +1,162 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "subscript.h" +#include "mdq.h" +#include "advancewindow.h" + +GBLREF char window_token; +GBLREF mident window_ident; + +int gvn(void) +{ + triple *ref, *t1, *oldchain, tmpchain, *triptr, *s; + oprtype subscripts[MAX_GVSUBSCRIPTS], *sb1, *sb2; + boolean_t shifting, vbar, parse_status; + opctype ox; + char x; + error_def(ERR_MAXNRSUBSCRIPTS); + error_def(ERR_RPARENMISSING); + error_def(ERR_GBLNAME); + error_def(ERR_EXTGBLDEL); + error_def(ERR_GVNAKEDEXTNM); + error_def(ERR_EXPR); + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + assert(window_token == TK_CIRCUMFLEX); + advancewindow(); + sb1 = sb2 = subscripts; + ox = 0; + if (shifting = TREF(shift_side_effects)) + { + dqinit(&tmpchain, exorder); + oldchain = setcurtchain(&tmpchain); + } + if (window_token == TK_LBRACKET || window_token == TK_VBAR) + { vbar = (window_token == TK_VBAR); + advancewindow(); + if (vbar) + parse_status = expr(sb1++); + else + parse_status = expratom(sb1++); + if (!parse_status) + { stx_error(ERR_EXPR); + if (shifting) + setcurtchain(oldchain); + return FALSE; + } + if (window_token == TK_COMMA) + { + advancewindow(); + if (vbar) + parse_status = expr(sb1++); + else + parse_status = expratom(sb1++); + if (!parse_status) + { stx_error(ERR_EXPR); + if (shifting) + setcurtchain(oldchain); + return FALSE; + } + } else + *sb1++ = put_str(0,0); + if ((!vbar && window_token != TK_RBRACKET) || (vbar && window_token != TK_VBAR)) + { stx_error(ERR_EXTGBLDEL); + if (shifting) + setcurtchain(oldchain); + return FALSE; + } + advancewindow(); + ox = OC_GVEXTNAM; + } + if (window_token == TK_IDENT) + { + if (!ox) + ox = OC_GVNAME; + *sb1++ = put_str(window_ident.addr, window_ident.len); + advancewindow(); + } else + { if (ox) + { + stx_error(ERR_GVNAKEDEXTNM); + if (shifting) + setcurtchain(oldchain); + return FALSE; + } + if (window_token != TK_LPAREN) + { + stx_error(ERR_GBLNAME); + if (shifting) + setcurtchain(oldchain); + return FALSE; + } + ox = OC_GVNAKED; + } + if (window_token == TK_LPAREN) + for (;;) + { + if (sb1 >= ARRAYTOP(subscripts)) + { + stx_error(ERR_MAXNRSUBSCRIPTS); + if (shifting) + setcurtchain(oldchain); + return FALSE; + } + advancewindow(); + if (!expr(sb1)) + { + if (shifting) + setcurtchain(oldchain); + return FALSE; + } + assert(sb1->oprclass == TRIP_REF); + s = sb1->oprval.tref; + if (s->opcode == OC_LIT) + *sb1 = make_gvsubsc(&s->operand[0].oprval.mlit->v); + sb1++; + if ((x = window_token) == TK_RPAREN) + { + advancewindow(); + break; + } + if (x != TK_COMMA) + { + stx_error(ERR_RPARENMISSING); + if (shifting) + setcurtchain(oldchain); + return FALSE; + } + } + ref = newtriple(ox); + ref->operand[0] = put_ilit((mint)(sb1 - sb2)); + for ( ; sb2 < sb1 ; sb2++) + { + t1 = newtriple(OC_PARAMETER); + ref->operand[1] = put_tref(t1); + ref = t1; + ref->operand[0] = *sb2; + } + if (shifting) + { + newtriple(OC_GVSAVTARG); + setcurtchain(oldchain); + dqadd(TREF(expr_start), &tmpchain, exorder); + TREF(expr_start) = tmpchain.exorder.bl; + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(TREF(expr_start)); + } + return TRUE; +} diff --git a/sr_port/gvname_env_restore.c b/sr_port/gvname_env_restore.c new file mode 100644 index 0000000..e297d4c --- /dev/null +++ b/sr_port/gvname_env_restore.c @@ -0,0 +1,59 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gdsroot.h" +#include "gdskill.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gdscc.h" +#include "copy.h" +#include "jnl.h" +#include "buddy_list.h" +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" +#include "gtm_string.h" +#include "gvname_info.h" + +GBLREF gv_key *gv_currkey; +GBLREF gd_region *gv_cur_region; +GBLREF gv_namehead *gv_target; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF uint4 dollar_tlevel; +GBLREF sgm_info *sgm_info_ptr; + + +void gvname_env_restore(gvname_info *curr_gvname_info) +{ + DEBUG_ONLY(boolean_t is_bg_or_mm;) + + gv_target = curr_gvname_info->s_gv_target; + gv_cur_region = curr_gvname_info->s_gv_cur_region; + DEBUG_ONLY(is_bg_or_mm = (dba_bg == gv_cur_region->dyn.addr->acc_meth || dba_mm == gv_cur_region->dyn.addr->acc_meth);) + cs_addrs = curr_gvname_info->s_cs_addrs; + assert((is_bg_or_mm && cs_addrs) || dba_cm == gv_cur_region->dyn.addr->acc_meth || + dba_usr == gv_cur_region->dyn.addr->acc_meth); + if (cs_addrs) /* cs_addrs might be NULL for dba_cm/dba_usr region */ + cs_data = cs_addrs->hdr; + assert(gv_currkey->top <= curr_gvname_info->s_gv_currkey->top); + gv_currkey->end = curr_gvname_info->s_gv_currkey->end; + gv_currkey->prev = curr_gvname_info->s_gv_currkey->prev; + memcpy(gv_currkey->base, curr_gvname_info->s_gv_currkey->base, curr_gvname_info->s_gv_currkey->end + 1); + sgm_info_ptr = curr_gvname_info->s_sgm_info_ptr; + assert((is_bg_or_mm && ((dollar_tlevel && sgm_info_ptr) || (!dollar_tlevel && !sgm_info_ptr))) || + dba_cm == gv_cur_region->dyn.addr->acc_meth || dba_usr == gv_cur_region->dyn.addr->acc_meth); + +} diff --git a/sr_port/gvname_env_save.c b/sr_port/gvname_env_save.c new file mode 100644 index 0000000..c1b0082 --- /dev/null +++ b/sr_port/gvname_env_save.c @@ -0,0 +1,56 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gdsroot.h" +#include "gdskill.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gdscc.h" +#include "copy.h" +#include "jnl.h" +#include "buddy_list.h" +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" +#include "gtm_string.h" +#include "gvname_info.h" + +GBLREF gv_key *gv_currkey; +GBLREF gd_region *gv_cur_region; +GBLREF gv_namehead *gv_target; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF uint4 dollar_tlevel; +GBLREF sgm_info *sgm_info_ptr; + +void gvname_env_save(gvname_info * curr_gvname_info) +{ + DEBUG_ONLY(boolean_t is_bg_or_mm;) + + DEBUG_ONLY(is_bg_or_mm = (dba_mm == gv_cur_region->dyn.addr->acc_meth || dba_bg == gv_cur_region->dyn.addr->acc_meth);) + curr_gvname_info->s_gv_target = gv_target; + curr_gvname_info->s_gv_cur_region = gv_cur_region; + assert((is_bg_or_mm && cs_addrs->hdr == cs_data) || dba_cm == gv_cur_region->dyn.addr->acc_meth || + dba_usr == gv_cur_region->dyn.addr->acc_meth); + curr_gvname_info->s_cs_addrs = cs_addrs; + DBG_CHECK_GVTARGET_CSADDRS_IN_SYNC; + assert(gv_currkey->top <= curr_gvname_info->s_gv_currkey->top); + curr_gvname_info->s_gv_currkey->end = gv_currkey->end; + curr_gvname_info->s_gv_currkey->prev = gv_currkey->prev; + memcpy(curr_gvname_info->s_gv_currkey->base, gv_currkey->base, gv_currkey->end + 1); + curr_gvname_info->s_sgm_info_ptr = sgm_info_ptr; + assert((is_bg_or_mm && ((dollar_tlevel && sgm_info_ptr) || (!dollar_tlevel && !sgm_info_ptr))) || + dba_cm == gv_cur_region->dyn.addr->acc_meth || dba_usr == gv_cur_region->dyn.addr->acc_meth); +} diff --git a/sr_port/gvname_info.h b/sr_port/gvname_info.h new file mode 100644 index 0000000..ce7dd5d --- /dev/null +++ b/sr_port/gvname_info.h @@ -0,0 +1,34 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* gvname_info.h + * ------------- + * + * Following structure is to save result of a call to op_gvname(). + * Specially in merge we do not need to call op_gvname again and again. + * Just call once and save result. + * Also note that it is not easy to call op_gvname with variable arguments again and again. + */ +#ifndef MERGE_GLOBAL_DEFINED +typedef struct gvname_info_struct { + gv_key *s_gv_currkey; + gv_namehead *s_gv_target; + gd_region *s_gv_cur_region; + sgmnt_addrs *s_cs_addrs; + sgm_info *s_sgm_info_ptr; +} gvname_info; +typedef gvname_info *gvname_info_ptr; + +/* Function Prototypes for M global variable functions of MERGE */ +void gvname_env_restore(gvname_info *curr_gvname_info); +void gvname_env_save(gvname_info * curr_gvname_info); +#define MERGE_GLOBAL_DEFINED +#endif diff --git a/sr_port/gvstats_rec.c b/sr_port/gvstats_rec.c new file mode 100644 index 0000000..c5e7ddf --- /dev/null +++ b/sr_port/gvstats_rec.c @@ -0,0 +1,75 @@ +/**************************************************************** + * * + * Copyright 2008, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gvstats_rec.h" + +void gvstats_rec_csd2cnl(sgmnt_addrs *csa) +{ + memcpy(&csa->nl->gvstats_rec, &csa->hdr->gvstats_rec, SIZEOF(gvstats_rec_t)); +} + +void gvstats_rec_cnl2csd(sgmnt_addrs *csa) +{ + memcpy(&csa->hdr->gvstats_rec, &csa->nl->gvstats_rec, SIZEOF(gvstats_rec_t)); +} + +void gvstats_rec_upgrade(sgmnt_addrs *csa) +{ + node_local_ptr_t cnl; + sgmnt_data_ptr_t csd; + int index; + + csd = csa->hdr; + cnl = csa->nl; + /* csd still contains gvstats info in old place. Copy over to new location */ + cnl->gvstats_rec.n_nontp_retries_0 = csd->filler_n_retries[0]; + cnl->gvstats_rec.n_nontp_retries_1 = csd->filler_n_retries[1]; + cnl->gvstats_rec.n_nontp_retries_2 = csd->filler_n_retries[2]; + cnl->gvstats_rec.n_nontp_retries_3 = csd->filler_n_retries[3]; + cnl->gvstats_rec.n_set = csd->filler_n_puts; + cnl->gvstats_rec.n_kill = csd->filler_n_kills; + cnl->gvstats_rec.n_query = csd->filler_n_queries; + cnl->gvstats_rec.n_get = csd->filler_n_gets; + cnl->gvstats_rec.n_order = csd->filler_n_order; + cnl->gvstats_rec.n_zprev = csd->filler_n_zprevs; + cnl->gvstats_rec.n_data = csd->filler_n_data; + /* No longer maintained : csd->filler_n_puts_duplicate */ + cnl->gvstats_rec.n_tp_readwrite = csd->filler_n_tp_updates; + /* No longer maintained : csd->filler_n_tp_updates_duplicate */ + cnl->gvstats_rec.n_tp_tot_retries_0 = csd->filler_n_tp_retries[0]; + cnl->gvstats_rec.n_tp_tot_retries_1 = csd->filler_n_tp_retries[1]; + cnl->gvstats_rec.n_tp_tot_retries_2 = csd->filler_n_tp_retries[2]; + cnl->gvstats_rec.n_tp_tot_retries_3 = csd->filler_n_tp_retries[3]; + cnl->gvstats_rec.n_tp_tot_retries_4 = csd->filler_n_tp_retries[4]; + for (index = 5; index < 12; index++) + cnl->gvstats_rec.n_tp_tot_retries_4 += csd->filler_n_tp_retries[index]; + cnl->gvstats_rec.n_tp_cnflct_retries_0 = csd->filler_n_tp_retries_conflicts[0]; + cnl->gvstats_rec.n_tp_cnflct_retries_1 = csd->filler_n_tp_retries_conflicts[1]; + cnl->gvstats_rec.n_tp_cnflct_retries_2 = csd->filler_n_tp_retries_conflicts[2]; + cnl->gvstats_rec.n_tp_cnflct_retries_3 = csd->filler_n_tp_retries_conflicts[3]; + cnl->gvstats_rec.n_tp_cnflct_retries_4 = csd->filler_n_tp_retries_conflicts[4]; + for (index = 5; index < 12; index++) + cnl->gvstats_rec.n_tp_cnflct_retries_4 += csd->filler_n_tp_retries_conflicts[index]; + /* Nullify statistics that were formerly in use but no longer so */ + csd->unused_dsk_reads.curr_count = 0; + csd->unused_dsk_reads.cumul_count = 0; + csd->unused_dsk_writes.curr_count = 0; + csd->unused_dsk_writes.cumul_count = 0; +} diff --git a/sr_port/gvstats_rec.h b/sr_port/gvstats_rec.h new file mode 100644 index 0000000..193c84f --- /dev/null +++ b/sr_port/gvstats_rec.h @@ -0,0 +1,37 @@ +/**************************************************************** + * * + * Copyright 2008, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GVSTATS_REC_H_INCLUDED +#define GVSTATS_REC_H_INCLUDED + +/* Note gvstats_rec exists in both sgmnt_data (file header) and in node_local. The reason + * for this is so that gvstats can be updated by read-only processes which would not be + * able to update the read-only file header. The gvstats in node_local are the ones that + * get updated and are peridically copied back to the fileheader and during fileheader + * flushes to keep them up to date. + */ + +#define TAB_GVSTATS_REC(A,B,C) A, +enum gvstats_rec_type +{ +#include "tab_gvstats_rec.h" +n_gvstats_rec_types +}; +#undef TAB_GVSTATS_REC + +typedef struct gvstats_rec_struct +{ +#define TAB_GVSTATS_REC(A,B,C) gtm_uint64_t A; +#include "tab_gvstats_rec.h" +} gvstats_rec_t; +#undef TAB_GVSTATS_REC + +#endif diff --git a/sr_port/gvstrsub.c b/sr_port/gvstrsub.c new file mode 100644 index 0000000..da36634 --- /dev/null +++ b/sr_port/gvstrsub.c @@ -0,0 +1,60 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "collseq.h" +#include "gdsfhead.h" +#include "do_xform.h" +#include "gvstrsub.h" +#include "zshow.h" + +GBLREF gv_namehead *gv_target; + +unsigned char *gvstrsub(unsigned char *src, unsigned char *target) +{ + int length, n, target_len; + char buf[MAX_KEY_SZ + 1], buf1[MAX_KEY_SZ + 1], *ptr; + unsigned char *str; + mstr mstr_x; + mstr mstr_tmp; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + ptr = buf; + for (n = 0, str = src; *str; ++n, ++str) + { + if (1 == *str) + { + str++; + *ptr++ = *str - 1; + } else + *ptr++ = *str; + } + if (TREF(transform) && gv_target && gv_target->collseq) + { + mstr_x.len = n; + mstr_x.addr = buf; + mstr_tmp.len = SIZEOF(buf1); + mstr_tmp.addr = buf1; + do_xform(gv_target->collseq, XBACK, &mstr_x, &mstr_tmp, &length); + n = length; + str = (unsigned char *)mstr_tmp.addr; /* mstr_tmp.addr is used just in case it is + reallocated in the XBACK routine */ + } else + str = (unsigned char *)buf; + format2zwr((sm_uc_ptr_t)str, n, target, &target_len); + return target + target_len; +} diff --git a/sr_port/gvstrsub.h b/sr_port/gvstrsub.h new file mode 100644 index 0000000..b2be161 --- /dev/null +++ b/sr_port/gvstrsub.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GVSTRSUB_INCLUDED +#define GVSTRSUB_INCLUDED + +unsigned char *gvstrsub(unsigned char *src, unsigned char *target); + +#endif /* GVSTRSUB_INCLUDED */ diff --git a/sr_port/gvsub2str.c b/sr_port/gvsub2str.c new file mode 100644 index 0000000..0b6468e --- /dev/null +++ b/sr_port/gvsub2str.c @@ -0,0 +1,160 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* + * ----------------------------------------------------- + * Convert a string subscript to MUMPS string + * ----------------------------------------------------- + */ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "copy.h" +#include "collseq.h" +#include "do_xform.h" +#include "gvstrsub.h" +#include "gvsub2str.h" + +#define LARGE_EXP 10000 + +GBLREF gv_namehead *gv_target; +LITREF unsigned short dpos[], dneg[]; + +/* + * ----------------------------------------------------- + * Convert a string subscript to MUMPS string + * Save result in a buffer pointed by targ. + * + * Entry: + * sub - input string in subscript format + * targ - output string buffer + * xlat_flg- translate flag. + * If true convert string to MUMPS format + * Return: + * (pointer to the last char. + * converted in the targ string) + 1. + * ----------------------------------------------------- + */ +unsigned char *gvsub2str(unsigned char *sub, unsigned char *targ, boolean_t xlat_flg) +{ + unsigned char buf1[MAX_KEY_SZ + 1], ch, *ptr, trail_ch; + unsigned short *tbl_ptr; + int expon, in_length, length, tmp; + mstr mstr_ch, mstr_targ; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + ch = *sub++; + if (STR_SUB_PREFIX == ch || (SUBSCRIPT_STDCOL_NULL == ch && KEY_DELIMITER == *sub)) + { /* If this is a string */ + if (xlat_flg) + return gvstrsub(sub, targ); + else + { + in_length = 0; + ptr = targ; + while ((ch = *sub++)) + { /* Copy string to targ, xlating each char */ + in_length++; + if (STR_SUB_ESCAPE == ch) + /* if this is an escape, demote next char */ + ch = (*sub++ - 1); + *targ++ = ch; + } + if (TREF(transform) && gv_target && gv_target->collseq) + { + mstr_ch.len = in_length; + mstr_ch.addr = (char *)ptr; + mstr_targ.len = SIZEOF(buf1); + mstr_targ.addr = (char *)buf1; + do_xform(gv_target->collseq, XBACK, &mstr_ch, &mstr_targ, &length); + memcpy(ptr, mstr_targ.addr, length); /* mstr_targ.addr is used just in case it is + * reallocated by the XBACK routine + */ + targ = ptr + length; + } + } + } else + { /* Number */ + if (SUBSCRIPT_ZERO == ch) + *targ++ = '0'; + else + { + tbl_ptr = (unsigned short *)&dpos[0] - 1; + trail_ch = KEY_DELIMITER; + if (0 <= (signed char)ch) + { /* Bit 7 of the exponent is set for positive numbers; must be negative */ + trail_ch = NEG_MNTSSA_END; + tbl_ptr = (unsigned short *)dneg; + ch = ~ch; + *targ++ = '-'; + } + ch -= (SUBSCRIPT_BIAS - 1); /* Unbias the exponent */ + expon = ch; + if (0 >= (signed char)ch) + { /* number is a fraction */ + ch = -(signed char)ch; + /* Save decimal point */ + *targ++ = '.'; + /* generate leading 0's */ + do *targ++ = '0'; + while ((signed char)ch-- > 0) + ; + /* make expon. really large to avoid + * generating extra dots */ + expon = LARGE_EXP; + } + while ((ch = *sub++) && ch != trail_ch) + { /* Convert digits loop */ + /* adjust dcm. point */ + if (0 >= (expon -= 2)) + { + if (0 != expon) + { + *targ++ = '.'; + expon = LARGE_EXP; + PUT_USHORT(targ, tbl_ptr[ch]); + targ += SIZEOF(short); + } else + { /* Insert dot between digits */ + PUT_USHORT(targ, tbl_ptr[ch]); + targ += SIZEOF(short); + *targ = *(targ - 1); + *(targ - 1) = '.'; + targ++; + expon = LARGE_EXP; + } + } else + { + PUT_USHORT(targ, tbl_ptr[ch]); + targ += SIZEOF(short); + } + } + if ((LARGE_EXP - 100) < expon) + { + if ('0' == *(targ - 1)) + targ--; + if ('.' == *(targ - 1)) + targ--; + } else + while (--expon > 0) + *targ++ = '0'; + } + } + return (targ); +} diff --git a/sr_port/gvsub2str.h b/sr_port/gvsub2str.h new file mode 100644 index 0000000..e5b6bb2 --- /dev/null +++ b/sr_port/gvsub2str.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GVSUB2STR_INCLUDED +#define GVSUB2STR_INCLUDED + +unsigned char *gvsub2str(unsigned char *sub, unsigned char *targ, boolean_t xlat_flg); + +#endif /* GVSUB2STR_INCLUDED */ diff --git a/sr_port/gvusr.h b/sr_port/gvusr.h new file mode 100644 index 0000000..6c51be1 --- /dev/null +++ b/sr_port/gvusr.h @@ -0,0 +1,27 @@ +/**************************************************************** + * * + * Copyright 2001, 2002 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GVUSR_INCLUDED +#define GVUSR_INCLUDED + +int gvusr_data(void); +int gvusr_get(mval *v); +int gvusr_lock(uint4 lock_len, unsigned char *lock_key, gd_region *reg); +int gvusr_order(void); +int gvusr_query(mval *v); +int gvusr_zprevious(void); +void gvusr_init(gd_region *reg, gd_region **creg, gv_key **ckey, gv_key **akey); +void gvusr_kill(bool do_subtree); +void gvusr_put(mval *v); +void gvusr_rundown(void); +void gvusr_unlock(uint4 lock_len, unsigned char *lock_key, gd_region *reg); + +#endif /* GVUSR_INCLUDED */ diff --git a/sr_port/gvusr_queryget.h b/sr_port/gvusr_queryget.h new file mode 100644 index 0000000..38c1be0 --- /dev/null +++ b/sr_port/gvusr_queryget.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2002 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef GVUSR_QUERYGET_H_INCLUDED +#define GVUSR_QUERYGET_H_INCLUDED + +boolean_t gvusr_queryget(mval *v); + +#endif /* GVUSR_QUERYGET_H_INCLUDED */ diff --git a/sr_port/gvzwr_arg.c b/sr_port/gvzwr_arg.c new file mode 100644 index 0000000..b3be7c3 --- /dev/null +++ b/sr_port/gvzwr_arg.c @@ -0,0 +1,57 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "zwrite.h" + +GBLREF gvzwrite_datablk *gvzwrite_block; + +void gvzwr_arg(int t, mval *a1, mval *a2) +{ + int i; + + i = gvzwrite_block->subsc_count++; + /* it would be good to guard the array i < sizeof... */ + if (a1) + { + MV_FORCE_DEFINED(a1); + if (MV_IS_CANONICAL(a1)) + MV_FORCE_NUMD(a1); + MV_FORCE_STRD(a1); + if ((ZWRITE_VAL != t) && (0 == a1->str.len)) /* value is real - leave it alone */ + a1 = NULL; + } + if (a2) + { + MV_FORCE_DEFINED(a2); + if (MV_IS_CANONICAL(a2)) + MV_FORCE_NUMD(a2); + MV_FORCE_STRD(a2); + if (0 == a2->str.len) /* can never be value */ + a2 = NULL; + } + ((zwr_sub_lst *)gvzwrite_block->sub)->subsc_list[i].subsc_type = t; + ((zwr_sub_lst *)gvzwrite_block->sub)->subsc_list[i].first = a1; + ((zwr_sub_lst *)gvzwrite_block->sub)->subsc_list[i].second = a2; + if ((ZWRITE_ASTERISK != t) && (ZWRITE_ALL != t)) + gvzwrite_block->mask |= 1 << i; + + if (ZWRITE_VAL != t) + gvzwrite_block->fixed = FALSE; + return; +} diff --git a/sr_port/gvzwr_fini.c b/sr_port/gvzwr_fini.c new file mode 100644 index 0000000..903c124 --- /dev/null +++ b/sr_port/gvzwr_fini.c @@ -0,0 +1,140 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "mlkdef.h" +#include "zshow.h" +#include "zwrite.h" +#include "error.h" +#include "op.h" +#include "change_reg.h" +#include "patcode.h" +#include "sgnl.h" +#include "gvzwrite_clnup.h" +#include "mvalconv.h" + +GBLDEF zshow_out *zwr_output; + +GBLREF gv_namehead *gv_target; +GBLREF gv_namehead *reset_gv_target; +GBLREF gv_key *gv_currkey; +GBLREF gd_region *gv_cur_region; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gvzwrite_datablk *gvzwrite_block; +GBLREF gd_binding *gd_map; +GBLREF gd_binding *gd_map_top; + +error_def(ERR_GVNAKED); + +void gvzwr_fini(zshow_out *out, int pat) +{ + char m[SIZEOF(mident_fixed)]; + mval local, data; + gv_key *old; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + if (!gv_currkey) + gvinit(); + + ESTABLISH(gvzwrite_ch); + + zwr_output = out; + assert(INVALID_GV_TARGET == reset_gv_target); + reset_gv_target = gv_target; + DBG_CHECK_GVTARGET_CSADDRS_IN_SYNC; + gvzwrite_block->gd_reg = gv_cur_region; + gvzwrite_block->old_targ = (unsigned char *)gv_target; + old = (gv_key *)malloc(SIZEOF(gv_key) + gv_currkey->end); + gvzwrite_block->old_key = (unsigned char *)old; + memcpy(gvzwrite_block->old_key, gv_currkey, SIZEOF(gv_key) + gv_currkey->end); + gvzwrite_block->old_map = gd_map; + gvzwrite_block->old_map_top = gd_map_top; + gvzwrite_block->gv_last_subsc_null = TREF(gv_last_subsc_null); + gvzwrite_block->gv_some_subsc_null = TREF(gv_some_subsc_null); + if (!pat) + { + local = *gvzwrite_block->pat; + if (local.str.len) /* New reference. Will get new gv_target.. */ + { + gv_target = NULL; + gv_currkey->base[0] = '\0'; + op_gvname(VARLSTCNT(1) &local); + op_gvdata(&data); + if (!(MV_FORCE_INTD(&data))) + sgnl_gvundef(); + else + { + gvzwrite_block->fixed = (gvzwrite_block->fixed ? TRUE : FALSE); + gvzwr_var(MV_FORCE_INTD(&data), 0); + } + } else /* Old (naked) reference. Keep previous gv_target reference */ + { + if (gv_currkey->prev == 0) + rts_error(VARLSTCNT(1) ERR_GVNAKED); + + gv_currkey->end = gv_currkey->prev; + gv_currkey->base[ gv_currkey->end ] = 0; + gv_currkey->prev = 0; + op_gvdata(&data); + if (!(MV_FORCE_INTD(&data))) + sgnl_gvundef(); + else + { + gvzwrite_block->fixed = (gvzwrite_block->fixed ? TRUE : FALSE); + gvzwr_var((int4)MV_FORCE_INTD(&data), 0); + } + } + } else + { + gv_target = NULL; + gv_currkey->base[0] = '\0'; + local.mvtype = MV_STR; + local.str.addr = &m[0]; + local.str.len = 1; + m[0] = '%'; + + gvzwrite_block->fixed = FALSE; + for (; ;) + { + op_gvname(VARLSTCNT(1) &local); + if (do_pattern(&local, gvzwrite_block->pat)) + { + op_gvdata(&data); + if ((MV_FORCE_INTD(&data))) + { + gvzwr_var((int4)MV_FORCE_INTD(&data), 0); + } + } + op_gvorder(&local); + if (local.str.len) + { + assert(local.str.len <= MAX_MIDENT_LEN + 1); + local.str.addr++; + local.str.len--; + memcpy(&m[0], local.str.addr, local.str.len); + local.str.addr = &m[0]; + } else + break; + } + } + gvzwrite_clnup(); /* this routine is called by gvzwrite_ch() too */ + REVERT; + return; +} diff --git a/sr_port/gvzwr_init.c b/sr_port/gvzwr_init.c new file mode 100644 index 0000000..9a2caa5 --- /dev/null +++ b/sr_port/gvzwr_init.c @@ -0,0 +1,38 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "zwrite.h" +#include "subscript.h" + +GBLREF gvzwrite_datablk *gvzwrite_block; + +void gvzwr_init(unsigned short t, mval *val, int4 pat) +{ + if (NULL == gvzwrite_block) + { + gvzwrite_block = malloc(SIZEOF(gvzwrite_datablk)); + memset(gvzwrite_block, 0, SIZEOF(gvzwrite_datablk)); + } + MV_FORCE_STR(val); + gvzwrite_block->type = pat; + if (NULL == gvzwrite_block->sub) + gvzwrite_block->sub = (zwr_sub_lst *)malloc(SIZEOF(zwr_sub_lst) * MAX_GVSUBSCRIPTS); + gvzwrite_block->pat = val; + gvzwrite_block->mask = gvzwrite_block->subsc_count = 0; + gvzwrite_block->fixed = TRUE; + return; +} diff --git a/sr_port/gvzwr_out.c b/sr_port/gvzwr_out.c new file mode 100644 index 0000000..67fc96a --- /dev/null +++ b/sr_port/gvzwr_out.c @@ -0,0 +1,51 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "zshow.h" +#include "op.h" +#include "format_targ_key.h" +#include "mlkdef.h" +#include "zwrite.h" + +GBLREF gv_key *gv_currkey; +GBLREF zshow_out *zwr_output; + +void gvzwr_out(void) +{ + int n; + mval val; + mval outdesc; + mstr one; + char buff[MAX_ZWR_KEY_SZ], *end; + + if ((end = (char *)format_targ_key((uchar_ptr_t)&buff[0], MAX_ZWR_KEY_SZ, gv_currkey, TRUE)) == 0) + end = &buff[MAX_ZWR_KEY_SZ - 1]; + op_gvget(&val); + if (!MV_DEFINED(&val)) + return; + MV_FORCE_STRD(&val); + outdesc.mvtype = MV_STR; + outdesc.str.addr = &buff[0]; + outdesc.str.len = INTCAST(end - outdesc.str.addr); + zshow_output(zwr_output,&outdesc.str); + buff[0] = '='; + one.addr = &buff[0]; + one.len = 1; + zshow_output(zwr_output,&one); + mval_write(zwr_output,&val,TRUE); +} diff --git a/sr_port/gvzwr_var.c b/sr_port/gvzwr_var.c new file mode 100644 index 0000000..4ee06b6 --- /dev/null +++ b/sr_port/gvzwr_var.c @@ -0,0 +1,225 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "zwrite.h" +#include "op.h" +#include "outofband.h" +#include "numcmp.h" +#include "patcode.h" +#include "sgnl.h" +#include "mvalconv.h" +#include "follow.h" +#include "gtm_string.h" + +#define eb_less(u, v) (numcmp(u, v) < 0) + +GBLREF gv_key *gv_currkey; +GBLREF gvzwrite_datablk *gvzwrite_block; +GBLREF int4 outofband; +GBLREF gd_region *gv_cur_region; +LITREF mval literal_null; + +void gvzwr_var(uint4 data, int4 n) +{ + mval mv, subdata; + unsigned short end, prev, end1, prev1; + bool save_gv_last_subsc_null; + boolean_t do_lev; + char seen_null; + zwr_sub_lst *zwr_sub; + int loop_condition = 1; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + if (outofband) + outofband_action(FALSE); + zwr_sub = (zwr_sub_lst *)gvzwrite_block->sub; + if ((0 == gvzwrite_block->subsc_count) && (0 == n)) + zwr_sub->subsc_list[n].subsc_type = ZWRITE_ASTERISK; + if ((1 == data || 11 == data) && + (!gvzwrite_block->subsc_count || (ZWRITE_ASTERISK == zwr_sub->subsc_list[n].subsc_type) || + (n && !(gvzwrite_block->mask >> n)))) + gvzwr_out(); + if ((1 >= data) || (gvzwrite_block->subsc_count && (n >= gvzwrite_block->subsc_count) + && (ZWRITE_ASTERISK != zwr_sub->subsc_list[gvzwrite_block->subsc_count - 1].subsc_type))) + return; + assert(1 < data); + end = gv_currkey->end; + prev = gv_currkey->prev; + if ((n < gvzwrite_block->subsc_count) && (ZWRITE_VAL == zwr_sub->subsc_list[n].subsc_type)) + { + mval2subsc(zwr_sub->subsc_list[n].first, gv_currkey); + op_gvdata(&subdata); + if (MV_FORCE_INTD(&subdata) && ((10 != (int4)MV_FORCE_INTD(&subdata)) || n < gvzwrite_block->subsc_count - 1)) + { + save_gv_last_subsc_null = TREF(gv_last_subsc_null); + gvzwr_var((int4)MV_FORCE_INTD(&subdata), n + 1); + TREF(gv_last_subsc_null) = save_gv_last_subsc_null; + } else if (gvzwrite_block->fixed) + sgnl_gvundef(); + } else + { + seen_null = 0; + if (n < gvzwrite_block->subsc_count + && zwr_sub->subsc_list[n].first + && ZWRITE_PATTERN != zwr_sub->subsc_list[n].subsc_type) + { + mv = *zwr_sub->subsc_list[n].first; + mval2subsc(&mv, gv_currkey); + if ((mv.mvtype & MV_STR) && !mv.str.len) + seen_null = 1; + op_gvdata(&subdata); + } else + { + mval2subsc((mval *)&literal_null, gv_currkey); + TREF(gv_last_subsc_null) = TRUE; + if (0 == gv_cur_region->std_null_coll) + { + op_gvorder(&mv); /* This will return the first subscript */ + if (0 == mv.str.len) + { + if (NEVER == gv_cur_region->null_subs || seen_null) + loop_condition = 0; + else + { + seen_null = 1; /* set flag to indicate processing null sub */ + op_gvnaked(VARLSTCNT(1) &mv); + op_gvdata(&subdata); + if (!MV_FORCE_INTD(&subdata)) + loop_condition = 0; + } + } else + { + op_gvnaked(VARLSTCNT(1) &mv); + op_gvdata(&subdata); + } + } else /* for standard null collation */ + { + /* determine whether $data(^gbl("") == 1 or 11, + if yes, first process that + */ + if (NEVER == gv_cur_region->null_subs) + { + op_gvorder(&mv); + assert(0 != mv.str.len); /* We are looking for the first subscript at a given level and so, + we do not expect to have hit at the end of the list */ + op_gvnaked(VARLSTCNT(1) &mv); + op_gvdata(&subdata); + } else + { + op_gvdata(&subdata); + if (MV_FORCE_INTD(&subdata)) + seen_null = 1; + } + } + } + while (loop_condition) + { + do_lev = (MV_FORCE_INTD(&subdata) ? TRUE : FALSE); + if (n < gvzwrite_block->subsc_count) + { + if (ZWRITE_PATTERN == zwr_sub->subsc_list[n].subsc_type) + { + if (!do_pattern(&mv, zwr_sub->subsc_list[n].first)) + do_lev = FALSE; + } else if (ZWRITE_ALL != zwr_sub->subsc_list[n].subsc_type) + { + if (do_lev && zwr_sub->subsc_list[n].first) + { + if (MV_IS_CANONICAL(&mv)) + { + if (!MV_IS_CANONICAL(zwr_sub->subsc_list[n].first) + || eb_less(&mv, zwr_sub->subsc_list[n].first)) + do_lev = FALSE; + } else + { + if (!MV_IS_CANONICAL(zwr_sub->subsc_list[n].first) + && (!follow(&mv, zwr_sub->subsc_list[n].first) && + (mv.str.len != zwr_sub->subsc_list[n].first->str.len || + memcmp(mv.str.addr, + zwr_sub->subsc_list[n].first->str.addr, + mv.str.len)))) + do_lev = FALSE; + } + } + + if (do_lev && zwr_sub->subsc_list[n].second) + { + if (MV_IS_CANONICAL(&mv)) + { + if (MV_IS_CANONICAL(zwr_sub->subsc_list[n].second) + && eb_less(zwr_sub->subsc_list[n].second, &mv)) + do_lev = FALSE; + } else + { + if (MV_IS_CANONICAL(zwr_sub->subsc_list[n].second) + || (!follow(zwr_sub->subsc_list[n].second, &mv) && + (mv.str.len != zwr_sub->subsc_list[n].second->str.len || + memcmp(mv.str.addr, + zwr_sub->subsc_list[n].second->str.addr, + mv.str.len)))) + do_lev = FALSE; + } + if (!do_lev) + break; + } + } + } + if (do_lev) + { + end1 = gv_currkey->end; + prev1 = gv_currkey->prev; + save_gv_last_subsc_null = TREF(gv_last_subsc_null); + gvzwr_var((int4)MV_FORCE_INTD(&subdata), n + 1); + TREF(gv_last_subsc_null) = save_gv_last_subsc_null; + gv_currkey->end = end1; + gv_currkey->prev = prev1; + gv_currkey->base[end1] = 0; + } + if (1 == seen_null) + { + assert(TREF(gv_last_subsc_null)); + TREF(gv_last_subsc_null) = FALSE; + seen_null = 2; /* set flag to indicate null sub processed */ + } + op_gvorder(&mv); + /* When null subscript is in the middle, + but with standard collation null subscripts can not be in the middle, so don't need to be worried + */ + if (0 == mv.str.len) + { + if (NEVER == gv_cur_region->null_subs || seen_null || gv_cur_region->std_null_coll) + break; + else + { + seen_null = 1; /* set flag to indicate processing null sub */ + op_gvnaked(VARLSTCNT(1) &mv); + op_gvdata(&subdata); + if (!MV_FORCE_INTD(&subdata)) + break; + } + } else + { + op_gvnaked(VARLSTCNT(1) &mv); + op_gvdata(&subdata); + } + } + } + gv_currkey->end = end; + gv_currkey->prev = prev; + gv_currkey->base[end] = 0; +} diff --git a/sr_port/gvzwrite_ch.c b/sr_port/gvzwrite_ch.c new file mode 100644 index 0000000..52d9ca4 --- /dev/null +++ b/sr_port/gvzwrite_ch.c @@ -0,0 +1,23 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "error.h" +#include "gvzwrite_clnup.h" + +CONDITION_HANDLER(gvzwrite_ch) +{ + START_CH; + gvzwrite_clnup(); /* this routine is called by gvzwr_fini() too */ + NEXTCH; +} + diff --git a/sr_port/gvzwrite_clnup.c b/sr_port/gvzwrite_clnup.c new file mode 100644 index 0000000..98b50a8 --- /dev/null +++ b/sr_port/gvzwrite_clnup.c @@ -0,0 +1,62 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "zwrite.h" +#include "error.h" +#include "change_reg.h" +#include "gvzwrite_clnup.h" +#include "filestruct.h" +#include "gdscc.h" +#include "jnl.h" + +GBLREF gv_namehead *gv_target; +GBLREF gv_namehead *reset_gv_target; +GBLREF gv_key *gv_currkey; +GBLREF gd_region *gv_cur_region; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF gvzwrite_datablk *gvzwrite_block; +GBLREF gd_binding *gd_map; +GBLREF gd_binding *gd_map_top; + +void gvzwrite_clnup(void) +{ + gv_key *old; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + gv_cur_region = gvzwrite_block->gd_reg; + change_reg(); + assert(reset_gv_target == ((gv_namehead *)gvzwrite_block->old_targ)); + if (NULL != gvzwrite_block->old_key) + { + old = (gv_key *)gvzwrite_block->old_key; + memcpy(&gv_currkey->base[0], &old->base[0], old->end + 1); + gv_currkey->end = old->end; + gv_currkey->prev = old->prev; + gd_map = gvzwrite_block->old_map; + gd_map_top = gvzwrite_block->old_map_top; + free(gvzwrite_block->old_key); + gvzwrite_block->old_key = gvzwrite_block->old_targ = (unsigned char *)NULL; + gvzwrite_block->subsc_count = 0; + TREF(gv_last_subsc_null) = gvzwrite_block->gv_last_subsc_null; + TREF(gv_some_subsc_null) = gvzwrite_block->gv_some_subsc_null; + } + RESET_GV_TARGET(DO_GVT_GVKEY_CHECK); +} diff --git a/sr_port/gvzwrite_clnup.h b/sr_port/gvzwrite_clnup.h new file mode 100644 index 0000000..26edb1f --- /dev/null +++ b/sr_port/gvzwrite_clnup.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __GVZWRITE_CLNUP_H__ +#define __GVZWRITE_CLNUP_H__ + +void gvzwrite_clnup(void); + +#endif diff --git a/sr_port/h.mpt b/sr_port/h.mpt new file mode 100644 index 0000000..c1d9c31 --- /dev/null +++ b/sr_port/h.mpt @@ -0,0 +1,57 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 1987, 2003 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +%H ;GT.M %H utility - date and time conversions to and from $H format + ;invoke with %DT in $H day format at %CDS to set %DAT mm/dd/yyyy + ;invoke with %TM in $H time format at %CTS to set %TIM hh:mm:ss + ;invoke with %DT in mm/dd/yy[yy] format at %CDN to set %DAT to $H form + ;invoke with %TM in hh:mm:ss format at %CTN to set %TIM to $H format + ;the labels without the % are corresponding functions + q +%CDS S %DAT=$$CDS(%DT) + q +CDS(dt) + i dt'<0,dt<94658 q +$zd(dt,"MM")_"/"_+$zd(dt,"DD")_$zd(dt,"/YEAR") + q "" + ; +%CTS s %TIM=$$CTS(%TM) + q +CTS(tm) + i tm'<0,tm<86400 q $zd(","_tm,"24:60:SS") + q "" + ; +%CDN s %DAT=$$CDN(%DT) + q +CDN(dt) + n cc,dat,dd,mm,yy,dh,zd + s mm=+dt,dd=$p(dt,"/",2),yy=$p(dt,"/",3),dat="" + i mm<1 q "" + i mm>13 q "" + i dd<1 q "" + s zd=$ZDATEFORM + i $l(yy)<3 d + . s dh=$H + . s yy=yy+(100*$S('zd:19,(zd>1840)&($L(zd)=4):($E(zd,1,2)+$S($E(zd,3,4)'>yy:0,1:1)),1:$E($ZDATE(dh,"YEAR"),1,2))) + ; 20th rolling current century + i dd>$s(+mm'=2:$e(303232332323,mm)+28,yy#4:28,yy#100:29,yy#400:28,1:29) q "" + s dat=yy-1841,mm=mm-1,cc=1 + i dat<0 s dd=dd-1,cc=-1 + s dat=dat\4*1461+(dat#4-$s(dat'<0:0,1:4)*365)+(mm*30)+$e(10112234455,mm)+dd-(yy-1800\100-(yy-1600\400)) + i yy#4,mm>1 s dat=dat-cc + i yy#100=0,mm<2,yy#400 s dat=dat+cc + q dat + ; +%CTN s %TIM=$$CTN(%TM) + q +CTN(tm) + n h,m,s + s h=+tm,m=$p(tm,":",2),s=$p(tm,":",3) + i h'<0,h<24,m'<0,m<60,s'<0,s<60 q h*60+m*60+s + q "" diff --git a/sr_port/hashtab.h b/sr_port/hashtab.h new file mode 100644 index 0000000..f4a8893 --- /dev/null +++ b/sr_port/hashtab.h @@ -0,0 +1,149 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#ifndef HASHTAB_COMMON_H +#define HASHTAB_COMMON_H + +#define HASHTAB_COMPACT FALSE +#define HASHTAB_NO_COMPACT TRUE + +#define HASHTAB_SPARE_TABLE FALSE +#define HASHTAB_NO_SPARE_TABLE TRUE + +#define HT_VALUE_DUMMY ((void *) 1L) + /* It is required not to have 0 or -1 for this macro. + * This is passed when hashtable callers want to use hash table just for the keys. + * Say, database code wants to create a list of blocks being read for a transactions. + * There is no corresponding value associated. For that case HT_VALUE_DUMMY will be passed as "value". */ +#define HT_DELETED_ENTRY ((void *) -1L) + /* Note: We may need to change the above during 64-bit port */ +#define HTENT_MARK_DELETED(tabent) (HT_DELETED_ENTRY == (tabent)->value) +#define HT_LOAD_FACTOR 50 +#define HT_REHASH_FACTOR (HT_LOAD_FACTOR/2) +#define HT_REHASH_TABLE_SIZE(table) MIN(table->size, table->count * 4) +#define INSERT_HTENT(table, tabent, hkey, value) \ +{ \ + if (HT_DELETED_ENTRY == (tabent)->value) \ + (table)->del_count--; \ + (tabent)->key = *hkey; \ + (tabent)->value = value; \ + (table)->count++; \ +} + +#define COMPACT_NEEDED(table) ((!(table)->dont_compact) && (((table)->del_count > (table)->cmp_trigger_size) || \ + (((table)->initial_size < (table)->size ) && ((table)->count < ((table)->cmp_trigger_size / 2))))) + +/* + * This macro is used by callers outside of the hash table implementation to indicate + * whether they will request the free of the hash table base at a later point in time or + * if it should be released by the hash table implementation during an expansion/compaction. + * They must call FREE_BASE_HASHTAB() later to release the base otherwise the memory + * will be leaked. + */ +#define DEFER_BASE_REL_HASHTAB(table, deferit) \ +{ \ + (table)->defer_base_release = deferit; \ +} + +/* + * This macro is used by callers outside of the hash table implementation to indicate + * that they are no longer using the hash table base. This function only provides a "hint" to the + * hash table implementation, i.e., the base can now be freed when appropriate. This can + * mean when this function is called if we are not keeping spare bases or at a potentially + * much later time if we are using a spare base. + */ +#define FREE_BASE_HASHTAB(table, base) \ +{ \ + if ((table)->dont_keep_spare_table) \ + free(base); \ +} + +/* +Different Hash Computation Macros for Strings: +All these were experminted and result is given in the design document of V5.0-000 longname project. +For now we decided to use ELF_HASH. +Do not remove the commented out section below which has all the hash functions. +We can remove them when we are certain that ELF_HASH is the best choice for us. +*/ + +#define STR_HASH ELF_HASH +#define ELF_HASH(sptr, len, hash, init_hashval) \ +{ \ + uint4 tempint; \ + char *curr, *top; \ + uint4 hcode; \ + for (hcode = init_hashval, curr = sptr, top = sptr + len; curr < top; curr++) \ + { \ + hcode = (hcode << 4) + *curr; \ + if (tempint = (hcode & 0xF0000000)) \ + hcode ^= tempint >> 24; \ + hcode &= ~tempint; \ + } \ + hash = hcode; \ +} + +/* +#define CHAR_BITS 8 +#define BITS_IN_int (SIZEOF(int) * CHAR_BITS) +#define THREE_QUARTERS (BITS_IN_int * 3 / 4) +#define ONE_EIGHTH (BITS_IN_int / 8) +#define HIGH_BITS (~((unsigned int)(~0) >> ONE_EIGHTH )) +#define PJW_HASH(sptr, len, hash, init_hashval) \ +{ \ + uint4 tempint; \ + char *curr, *top; \ + hash = init_hashval; \ + for (curr = sptr, top = sptr + len; curr < top; curr++) \ + { \ + hash = ( hash << ONE_EIGHTH ) + *curr; \ + if ((tempint = hash & HIGH_BITS ) != 0 ) \ + hash = ( hash ^ ( tempint >> THREE_QUARTERS )) & ~HIGH_BITS;\ + } \ +} + + +#define MISC1_HASH(sptr, len, hash, init_hashval) \ +{ \ + char *curr; \ + curr = sptr; \ + int indx; \ + hash = init_hashval; \ + for (indx = 0; indx < len; indx++, curr++) \ + hash += (*curr * (len - indx)); \ +} +#define MISC2_HASH(sptr, len, hash, init_hashval) \ + +{ \ + char *curr; \ + curr = sptr; \ + int indx; \ + hash = init_hashval; \ + for (indx = 0; indx < len; indx++, curr++) \ + hash = hash*31 + *curr; \ +} + +#define CURR_HASH(sptr, len, hash, init_hashval) \ +{ \ + char *ptr, tchar[MAXLEN]; \ + int indx; \ + uint4 temp1 = 0; \ + uint4 temp2 = 0; \ + memset(tchar, 0, MAXLEN); \ + memcpy(tchar, sptr, len); \ + ptr = &tchar[0]; \ + for ( indx = 0; indx < 4; indx++, ptr++) \ + temp1 = temp1 * 256 + *ptr ; \ + for ( ; indx < 8; indx++, ptr++) \ + temp2 = temp2 * 256 + *ptr ; \ + hash = (temp1 << 1) ^ (temp2) ; \ +} +*/ + +#endif diff --git a/sr_port/hashtab_addr.c b/sr_port/hashtab_addr.c new file mode 100644 index 0000000..9aa95c3 --- /dev/null +++ b/sr_port/hashtab_addr.c @@ -0,0 +1,23 @@ +/**************************************************************** + * * + * Copyright 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "min_max.h" +#include "gtm_string.h" +#include "error.h" +#include "send_msg.h" +#include "gtmmsg.h" +#include "hashtab.h" +#include "hashtab_addr.h" + +#define ADDR_HASH +/* The below include generates the hash table routines for the "addr" hash type */ +#include "hashtab_implementation.h" diff --git a/sr_port/hashtab_addr.h b/sr_port/hashtab_addr.h new file mode 100644 index 0000000..e7417a4 --- /dev/null +++ b/sr_port/hashtab_addr.h @@ -0,0 +1,69 @@ +/**************************************************************** + * * + * Copyright 2009, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#ifndef HASHTAB_ADDR_H +#define HASHTAB_ADDR_H + +typedef struct +{ + char *key; /* Note this is the actual key, not its address */ + void *value; +} ht_ent_addr; + +typedef struct hash_table_addr_struct +{ + ht_ent_addr *base; /* base of array of hent_* entries */ + ht_ent_addr *top; /* top of array of hent_* entries */ + unsigned int size; /* Hash table size */ + unsigned int initial_size; /* Hash table initial size */ + ht_ent_addr *spare_base; /* spare array of hent_* entries */ + unsigned int spare_base_size;/* size of spare array */ + boolean_t dont_compact; /* if set, never perform compaction */ + boolean_t dont_keep_spare_table; /* if set, don't keep a spare table */ + boolean_t defer_base_release; /* if set don't release base, caller will free_base...() later */ + unsigned int count; /* Number of valid entries */ + unsigned int del_count; /* Number of entries marked deleted. */ + unsigned int exp_trigger_size;/* When exp_trigger_size entried are used, expand table */ + unsigned int cmp_trigger_size;/* When cmp_trigger_size reached compact table */ + sm_uc_ptr_t entry_passed_thru;/* Bit vector used to determine whether a particular */ + /* ht_ent has been involved in a collision (meaning that */ + /* this value can't be marked empty on delete */ +} hash_table_addr; + +#define HTENT_EMPTY_ADDR(tabent, type, htvalue) (!(htvalue = (type *)(tabent)->value)) +#define HTENT_MARK_EMPTY_ADDR(tabent) (tabent)->value = (void *) 0L +#define HTENT_VALID_ADDR(tabent, type, htvalue) ((!HTENT_EMPTY_ADDR(tabent, type, htvalue)) && (HT_DELETED_ENTRY != htvalue)) + +/* Do not downcast when INT8_HASH, 8 byte int, is defined */ +#ifdef INT8_HASH +#define HASHTAB_UINTCAST(X) X +#else +#define HASHTAB_UINTCAST(X) UINTCAST(X) +#endif + +#ifdef GTM64 +# define COMPUTE_HASH_ADDR(hkey, hash) \ + hash = HASHTAB_UINTCAST(((((gtm_uint64_t)(*hkey)) & 0xFFFFFFFF) ^ (((gtm_uint64_t)(*hkey)) >> 31))) +#else +# define COMPUTE_HASH_ADDR(hkey, hash) hash = ((uint4)(*hkey)) +#endif + +/* Prototypes for addr hash routines. See hashtab_implementation.h for detail interface and implementation */ +void init_hashtab_addr(hash_table_addr *table, int minsize, boolean_t dont_compact, boolean_t dont_keep_spare_table); +void expand_hashtab_addr(hash_table_addr *table, int minsize); +boolean_t add_hashtab_addr(hash_table_addr *table, char **key, void *value, ht_ent_addr **tabentptr); +void *lookup_hashtab_addr(hash_table_addr *table, char **key); +void delete_hashtab_ent_addr(hash_table_addr *table, ht_ent_addr *tabent); +boolean_t delete_hashtab_addr(hash_table_addr *table, char **key); +void free_hashtab_addr(hash_table_addr *table); +void reinitialize_hashtab_addr(hash_table_addr *table); +void compact_hashtab_addr(hash_table_addr *table); + +#endif /* HASHTAB_ADDR_H */ diff --git a/sr_port/hashtab_implementation.h b/sr_port/hashtab_implementation.h new file mode 100644 index 0000000..6a93f0e --- /dev/null +++ b/sr_port/hashtab_implementation.h @@ -0,0 +1,673 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* Uniform Hash Implementation + +Hash table code supports the following data types as key: + a) int4 + b) int8 + c) UINTPTR_T + d) object code + e) variable name. + f) local process address (supported via define using either int4 or int8 as type + +Using pre-processor following four C files will expand five sets of routines. + a) hashtab_int4.c + b) hashtab_int8.c + c) hashtab_addr.c + d) hashtab_mname.c + e) hashtab_objcode.c + +Restrictions : + We assumed that no user of hash needs to add "key, value" pair where both are null. + We examined that GT.M does not need to have such cases. + We can add 0 as valid data for int4 and int8, however, it must always have non-zero value. + We can add 0 length string as "key" in objcode, however, it must always have non-zero length value. + We know object code cannot be 0 length, so even object source (key) is of 0 length, we are fine. + GT.M cannot have 0 length mname. So "key" for mname cannot be 0 length. + (If we want to remove above restriction, an extra field is needed for HT_ENT) +*/ + +#include "mdef.h" +#include "gtm_malloc.h" /* For raise_gtmmemory_error() definition */ +#include "bit_set.h" + +LITREF int ht_sizes[]; + +#define DEBUGHASHTABLE 0 + +#if defined(INT4_HASH) + +# define HT_KEY_T uint4 +# define HT_ENT ht_ent_int4 +# define HASH_TABLE hash_table_int4 +# define HTENT_KEY_MATCH(tabent, hkey) ((tabent)->key == (*hkey)) +# define FIND_HASH(hkey, hash) COMPUTE_HASH_INT4(hkey, hash) +# define HTENT_EMPTY HTENT_EMPTY_INT4 +# define HTENT_MARK_EMPTY HTENT_MARK_EMPTY_INT4 +# define HTENT_VALID HTENT_VALID_INT4 +# define INIT_HASHTAB init_hashtab_int4 +# define INIT_HASHTAB_INTL init_hashtab_intl_int4 +# define EXPAND_HASHTAB expand_hashtab_int4 +# define ADD_HASHTAB add_hashtab_int4 +# define ADD_HASHTAB_INTL add_hashtab_intl_int4 +# define LOOKUP_HASHTAB lookup_hashtab_int4 +# define DELETE_HASHTAB_ENT delete_hashtab_ent_int4 +# define DELETE_HASHTAB delete_hashtab_int4 +# define FREE_HASHTAB free_hashtab_int4 +# define REINITIALIZE_HASHTAB reinitialize_hashtab_int4 +# define COMPACT_HASHTAB compact_hashtab_int4 + +#elif defined(INT8_HASH) + +# define HT_KEY_T gtm_uint64_t +# define HT_ENT ht_ent_int8 +# define HASH_TABLE hash_table_int8 +# define HTENT_KEY_MATCH(tabent, hkey) ((tabent)->key == (*hkey)) +# define FIND_HASH(hkey, hash) COMPUTE_HASH_INT8(hkey, hash) +# define HTENT_EMPTY HTENT_EMPTY_INT8 +# define HTENT_MARK_EMPTY HTENT_MARK_EMPTY_INT8 +# define HTENT_VALID HTENT_VALID_INT8 +# define INIT_HASHTAB init_hashtab_int8 +# define INIT_HASHTAB_INTL init_hashtab_intl_int8 +# define EXPAND_HASHTAB expand_hashtab_int8 +# define ADD_HASHTAB add_hashtab_int8 +# define ADD_HASHTAB_INTL add_hashtab_intl_int8 +# define LOOKUP_HASHTAB lookup_hashtab_int8 +# define DELETE_HASHTAB_ENT delete_hashtab_ent_int8 +# define DELETE_HASHTAB delete_hashtab_int8 +# define FREE_HASHTAB free_hashtab_int8 +# define REINITIALIZE_HASHTAB reinitialize_hashtab_int8 +# define COMPACT_HASHTAB compact_hashtab_int8 + +#elif defined(ADDR_HASH) + +# define HT_KEY_T char * +# define HT_ENT ht_ent_addr +# define HASH_TABLE hash_table_addr +# define HTENT_KEY_MATCH(tabent, hkey) ((tabent)->key == (*hkey)) +# define FIND_HASH(hkey, hash) COMPUTE_HASH_ADDR(hkey, hash) +# define HTENT_EMPTY HTENT_EMPTY_ADDR +# define HTENT_MARK_EMPTY HTENT_MARK_EMPTY_ADDR +# define HTENT_VALID HTENT_VALID_ADDR +# define INIT_HASHTAB init_hashtab_addr +# define INIT_HASHTAB_INTL init_hashtab_intl_addr +# define EXPAND_HASHTAB expand_hashtab_addr +# define ADD_HASHTAB add_hashtab_addr +# define ADD_HASHTAB_INTL add_hashtab_intl_addr +# define LOOKUP_HASHTAB lookup_hashtab_addr +# define DELETE_HASHTAB_ENT delete_hashtab_ent_addr +# define DELETE_HASHTAB delete_hashtab_addr +# define FREE_HASHTAB free_hashtab_addr +# define REINITIALIZE_HASHTAB reinitialize_hashtab_addr +# define COMPACT_HASHTAB compact_hashtab_addr + +#elif defined(MNAME_HASH) + +# define HT_KEY_T mname_entry +# define HT_ENT ht_ent_mname +# define HASH_TABLE hash_table_mname +# define HTENT_KEY_MATCH(tabent, hkey) \ + ( ((tabent)->key.hash_code == (hkey)->hash_code) \ + && ((tabent)->key.var_name.len == (hkey)->var_name.len) \ + && (0 == memcmp((tabent)->key.var_name.addr, (hkey)->var_name.addr, (hkey)->var_name.len)) \ + ) +# define FIND_HASH(hkey, hash) {assert((hkey)->hash_code); hash = (hkey)->hash_code;} + /* Note: FIND_HASH for mname does not compute hash_code. Callers must make sure it is already computed. + * FIND_HASH for objcode or int4 or int8 computes hash code + * for every function call of add or lookup or delete. */ +# define HTENT_EMPTY HTENT_EMPTY_MNAME +# define HTENT_MARK_EMPTY HTENT_MARK_EMPTY_MNAME +# define HTENT_VALID HTENT_VALID_MNAME +# define INIT_HASHTAB init_hashtab_mname +# define INIT_HASHTAB_INTL init_hashtab_intl_mname +# define EXPAND_HASHTAB expand_hashtab_mname +# define ADD_HASHTAB add_hashtab_mname +# define ADD_HASHTAB_INTL add_hashtab_intl_mname +# define LOOKUP_HASHTAB lookup_hashtab_mname +# define DELETE_HASHTAB_ENT delete_hashtab_ent_mname +# define DELETE_HASHTAB delete_hashtab_mnamen +# define FREE_HASHTAB free_hashtab_mname +# define REINITIALIZE_HASHTAB reinitialize_hashtab_mname +# define COMPACT_HASHTAB compact_hashtab_mname + +#elif defined(STRING_HASH) + +# define HT_KEY_T stringkey +# define HT_ENT ht_ent_str +# define HASH_TABLE hash_table_str +# define HTENT_KEY_MATCH(tabent, hkey) \ + (((tabent)->key.hash_code == (hkey)->hash_code) \ + && ((tabent)->key.str.len == (hkey)->str.len) \ + && (0 == memcmp((tabent)->key.str.addr, (hkey)->str.addr, (hkey)->str.len)) \ + ) +# define FIND_HASH(hkey, hash) hash = (hkey)->hash_code +/* Note: FIND_HASH for str does not compute hash_code. Callers must make sure it is already computed */ +# define HTENT_EMPTY HTENT_EMPTY_STR +# define HTENT_MARK_EMPTY HTENT_MARK_EMPTY_STR +# define HTENT_VALID HTENT_VALID_STR +# define INIT_HASHTAB init_hashtab_str +# define INIT_HASHTAB_INTL init_hashtab_intl_str +# define EXPAND_HASHTAB expand_hashtab_str +# define ADD_HASHTAB add_hashtab_str +# define ADD_HASHTAB_INTL add_hashtab_intl_str +# define LOOKUP_HASHTAB lookup_hashtab_str +# define DELETE_HASHTAB_ENT delete_hashtab_ent_str +# define DELETE_HASHTAB delete_hashtab_str +# define FREE_HASHTAB free_hashtab_str +# define REINITIALIZE_HASHTAB reinitialize_hashtab_str +# define COMPACT_HASHTAB compact_hashtab_str + +#elif defined (OBJCODE_HASH) + +# define HT_KEY_T icode_str +# define HT_ENT ht_ent_objcode +# define HASH_TABLE hash_table_objcode +# define HTENT_KEY_MATCH(tabent, hkey) \ + (((tabent)->key.code == (hkey)->code) \ + && ((tabent)->key.str.len == (hkey)->str.len) \ + && (0 == memcmp((tabent)->key.str.addr, (hkey)->str.addr, (hkey)->str.len)) \ + ) +# define FIND_HASH(hkey, hash) COMPUTE_HASH_OBJCODE(hkey, hash) +# define HTENT_EMPTY HTENT_EMPTY_OBJCODE +# define HTENT_MARK_EMPTY HTENT_MARK_EMPTY_OBJCODE +# define HTENT_VALID HTENT_VALID_OBJCODE +# define INIT_HASHTAB init_hashtab_objcode +# define INIT_HASHTAB_INTL init_hashtab_intl_objcode +# define EXPAND_HASHTAB expand_hashtab_objcode +# define ADD_HASHTAB add_hashtab_objcode +# define ADD_HASHTAB_INTL add_hashtab_intl_objcode +# define LOOKUP_HASHTAB lookup_hashtab_objcode +# define DELETE_HASHTAB_ENT delete_hashtab_ent_objcode +# define DELETE_HASHTAB delete_hashtab_objcode +# define FREE_HASHTAB free_hashtab_objcode +# define REINITIALIZE_HASHTAB reinitialize_hashtab_objcode +# define COMPACT_HASHTAB compact_hashtab_objcode + +#else +#error undefined hash +#endif + +#if DEBUGHASHTABLE +/* Debug FPRINTF with pre and post requisite flushing of appropriate streams */ +#define DBGHASHTAB(x) {flush_pio(); FPRINTF x; fflush(stderr);} +#else +#define DBGHASHTAB(x) +#endif + +/* We use DOUBLE HASHING algorithm. After first collision we calculate + * rehash factor (rhfact) that is a function of the hash value (even though for + * correctness purposes any number from 1 to prime-1 should be fine) to + * avoid primary and secondary clustering. + * The SET_REHASH_INDEX is actually equivalent to ht_index = (ht_index + rhfact) % prime; + */ +#define SET_REHASH_FACTOR(rhfact, hash, prime) (rhfact) = (1 + ((hash) % ((prime) - 1))) +#define SET_REHASH_INDEX(ht_index, rhfact, prime) \ + assert((rhfact) < (prime)); \ + assert((ht_index) < (prime)); \ + (ht_index) += (rhfact); \ + if ((ht_index) >= (prime)) \ + (ht_index) -= (prime); + +#define RETURN_IF_ADDED(table, tabent, hkey, value) \ +{ \ + void *dummy; \ + if (HTENT_EMPTY(tabent, void, dummy)) \ + { \ + if (NULL != first_del_ent) \ + tabent = first_del_ent; \ + INSERT_HTENT(table, tabent, hkey, value); \ + return TRUE; \ + } \ + if (!HTENT_MARK_DELETED(tabent)) \ + { \ + if (HTENT_KEY_MATCH(tabent, hkey)) \ + return FALSE; /* valid and matched */ \ + } else if (NULL == first_del_ent) \ + first_del_ent = tabent; \ +} + +#define RETURN_IF_LOOKUP_DONE(tabent, hkey) \ +{ \ + void *dummy; \ + if (HTENT_EMPTY(tabent, void, dummy)) \ + return NULL; \ + if (!HTENT_MARK_DELETED(tabent) && HTENT_KEY_MATCH(tabent, hkey)) \ + return tabent; /* valid and matched */ \ +} + +#define DELETE_HTENT(table, tabent) \ +{ \ + uint4 entry_num; \ + sm_uc_ptr_t ptr; \ + \ + entry_num = (uint4)((tabent) - (table)->base); \ + /* Compute offset into bitmap for this entry */ \ + ptr = (table)->entry_passed_thru + (entry_num / BITS_PER_UCHAR); \ + if ((1 << (entry_num & 7)) & *ptr) \ + { \ + (tabent)->value = HT_DELETED_ENTRY; \ + (table)->del_count++; \ + } else \ + HTENT_MARK_EMPTY(tabent); \ + (table)->count--; \ + assert(((table)->count + (table)->del_count) <= (table)->size); \ +} + +#define RETURN_IF_DELETED(table, tabent, hkey) \ +{ \ + void *dummy; \ + if (HTENT_EMPTY(tabent, void, dummy)) \ + return FALSE; \ + if (!HTENT_MARK_DELETED(tabent) && HTENT_KEY_MATCH(tabent, hkey)) \ + { \ + DELETE_HTENT(table, tabent); \ + if (COMPACT_NEEDED(table)) \ + COMPACT_HASHTAB(table); \ + return TRUE; \ + } \ +} + +#define HT_FIELDS_COMMON_INIT(table) \ + table->exp_trigger_size = (double)table->size * HT_LOAD_FACTOR / 100.0; \ + table->cmp_trigger_size = (double)table->size * HT_REHASH_FACTOR / 100.0; \ + table->count = table->del_count = 0; + +/* Prototypes */ +void INIT_HASHTAB(HASH_TABLE *table, int minsize, boolean_t dont_compact, boolean_t dont_keep_spare_table); +STATICFNDCL void INIT_HASHTAB_INTL(HASH_TABLE *table, int minsize, HASH_TABLE *old_table); +void EXPAND_HASHTAB(HASH_TABLE *table, int minsize); +boolean_t ADD_HASHTAB(HASH_TABLE *table, HT_KEY_T *key, void *value, HT_ENT **tabentptr); +STATICFNDCL boolean_t ADD_HASHTAB_INTL(HASH_TABLE *table, HT_KEY_T *key, void *value, HT_ENT **tabentptr, + boolean_t changing_table_size); +void *LOOKUP_HASHTAB(HASH_TABLE *table, HT_KEY_T *key); +void DELETE_HASHTAB_ENT(HASH_TABLE *table, HT_ENT *tabent); +boolean_t DELETE_HASHTAB(HASH_TABLE *table, HT_KEY_T *key); +void FREE_HASHTAB(HASH_TABLE *table); +void REINITIALIZE_HASHTAB(HASH_TABLE *table); +void COMPACT_HASHTAB(HASH_TABLE *table); + +/* This is used by external callers to initially setup the hash table. */ +void INIT_HASHTAB(HASH_TABLE *table, int minsize, boolean_t dont_compact, boolean_t dont_keep_spare_table) +{ + int index; + + for (index = 0, table->initial_size = ht_sizes[index]; table->initial_size && table->initial_size < minsize; index++) + table->initial_size = ht_sizes[index]; + table->initial_size = table->initial_size ? table->initial_size : minsize; + table->dont_compact = dont_compact; + table->dont_keep_spare_table = dont_keep_spare_table; + table->defer_base_release = FALSE; + INIT_HASHTAB_INTL(table, minsize, NULL); +} + +/* This routine initializes hash table. It must be called once before hashing can be used. Note that + the ht_sizes array is defined in mtables.c. A NULL old_table pointer means that the table is being + setup for the first time. +*/ +STATICFNDEF void INIT_HASHTAB_INTL(HASH_TABLE *table, int minsize, HASH_TABLE *old_table) +{ + unsigned int cur_ht_size, prior_size; + int index; + boolean_t dont_keep_spare_table; + error_def(ERR_HTOFLOW); + DBGHASHTAB((stderr, "INIT_HASHTAB:table(%lx) minsize(%d) old_table(%lx)\n", table, minsize, old_table)); + + /* If this is the first time the hash table is being initialized (old_table == NULL), then look up the + * actual hash table size in ht_sizes based on the requested size (minsize). + * + * We dont want the hash table to shrink too fast so if we are changing the size of an existing hash table: + * 1) if the requested size is not smaller than half of the previous size: + * a) pick the actual size from ht_sizes based on the requested size. + * 2) if the requested size is smaller than half of the previous size: + * b) pick the previous entry (from the previous size (old_table->size) in ht_sizes. + */ + if ((NULL == old_table) || (minsize > (old_table->size / 2))) + { + for (index = 0, cur_ht_size = ht_sizes[index]; cur_ht_size && cur_ht_size < minsize; index++) + cur_ht_size = ht_sizes[index]; + } else /* don't shrink too fast ! */ + { + prior_size = ht_sizes[0]; + for (index = 1, cur_ht_size = ht_sizes[index]; cur_ht_size && cur_ht_size < old_table->size; index++) + { + cur_ht_size = ht_sizes[index]; + prior_size = ht_sizes[index-1]; + } + cur_ht_size = prior_size; + cur_ht_size = (cur_ht_size > old_table->initial_size) ? cur_ht_size : old_table->initial_size; + } + if (cur_ht_size) + { + DBGHASHTAB((stderr, "INIT_HASHTAB:table size will be (%d) for table(%lx)\n", + cur_ht_size, old_table?old_table:table)); + table->base = NULL; table->spare_base = NULL; table->spare_base_size = 0; /* a fresh new hash table */ + /* If this is is an initialization from a caller outside of the hash table implementation then + * old_table == NULL since there is no previous hash table. In this case the external versions of + * INIT_HASHTAB will setup table with values for dont_compact and dont_keep_spare_table. Otherwise, + * we can use them from the old_table. + */ + dont_keep_spare_table = old_table ? old_table->dont_keep_spare_table:table->dont_keep_spare_table; + if (!dont_keep_spare_table) + { + DBGHASHTAB((stderr, "INIT_HASHTAB: old_table(%lx)\n", old_table)); + if (NULL != old_table) + { + DBGHASHTAB((stderr, "INIT_HASHTAB: cur_ht_size(%d), spare_base_size(%d)\n", + cur_ht_size, old_table->spare_base_size)); + if (old_table->spare_base_size == cur_ht_size) + { /* We can use the spare table since it is the size we would have malloc'd */ + table->base = old_table->spare_base; + DBGHASHTAB((stderr, "INIT_HASHTAB: use spare table: base(%lx)\n", table->base)); + /* We no longer have a spare */ + old_table->spare_base = NULL; + old_table->spare_base_size = 0; + } else /* no luck on the reuse thing */ + if (NULL != old_table->spare_base) /* so free it if it exists */ + { + DBGHASHTAB((stderr, "INIT_HASHTAB: table(%lx): free spare_base(%lx)\n", + old_table, old_table->spare_base)); + free(old_table->spare_base); + old_table->spare_base = NULL; + old_table->spare_base_size = 0; + } + } + } + if (NULL == table->base) + { + /* Let's make sure we have a HT_ENT table. We are here either thru dont_keep_spare_table, + old_table == NULL, or wrong-sized spare */ + table->base = (void *)malloc((cur_ht_size * SIZEOF(HT_ENT)) + ROUND_UP(cur_ht_size, BITS_PER_UCHAR)); + DBGHASHTAB((stderr, "INIT_HASHTAB: malloc a new table: table(%lx) base(%lx)\n", + old_table?old_table:table, table->base)); + } + memset((char *)table->base, 0, (cur_ht_size * SIZEOF(HT_ENT)) + ROUND_UP(cur_ht_size, BITS_PER_UCHAR)); + table->size = cur_ht_size; + if (NULL != old_table) + { + table->initial_size = old_table->initial_size; + table->dont_compact = old_table->dont_compact; + table->dont_keep_spare_table = old_table->dont_keep_spare_table; + table->defer_base_release = old_table->defer_base_release; + } + table->top = table->base + cur_ht_size; + table->entry_passed_thru = (sm_uc_ptr_t) table->top; + DBGHASHTAB((stderr, "INIT_HASHTAB: entry_passed_thru points to (%lx)\n", table->entry_passed_thru)); + HT_FIELDS_COMMON_INIT(table); + } else + { + DBGHASHTAB((stderr, "INIT_HASHTAB:HTOFLOW: minsize(%d) cur_ht_size(%d)\n", minsize, cur_ht_size)); + send_msg(VARLSTCNT(3) ERR_HTOFLOW, 1, minsize); + rts_error(VARLSTCNT(3) ERR_HTOFLOW, 1, minsize); + } +} +/* Description: + Expand the hash table with at least minsize. + This can do either expansion or compaction, which depends on old table size and minsize passed. + It creates a new table and move old element to new table. + It deallocate old table entries +*/ +void EXPAND_HASHTAB(HASH_TABLE *table, int minsize) +{ + HASH_TABLE newtable, temptab; + HT_ENT *tabent, *topent, *dummy; + boolean_t added; + void *htval; + CONDITION_HANDLER(hashtab_rehash_ch); + ESTABLISH(hashtab_rehash_ch); + DBGHASHTAB((stderr, "EXPAND_HASHTAB:ENTER: table: table(%lx) base (%lx), spare_base(%lx), spare_base_size(%d), \n", + table, table->base, table->spare_base, table->spare_base_size)); + /* The next line keeps the HP-UX Itanium compiler in pro happy. This initialization is done is INIT_HASHTAB_INTL* + * but this line is placed here to appease the compiler. + */ + newtable.dont_keep_spare_table = table->dont_keep_spare_table; + INIT_HASHTAB_INTL(&newtable, minsize, table); + REVERT; + if (0 < table->count) /* if no active entries then nothing to move */ + for (tabent = table->base, topent = table->top; tabent < topent; tabent++) + { + if (HTENT_VALID(tabent, void, htval)) + { + /* Place location of new ht_ent entry into value location of existing ht entry */ + added = ADD_HASHTAB_INTL(&newtable, &tabent->key, htval, (HT_ENT **)&tabent->value, TRUE); + assert(added); + } + } + if (!table->defer_base_release && table->dont_keep_spare_table) + { + DBGHASHTAB((stderr, "EXPAND_HASHTAB:free base (%lx) \n", table->base)); + free(table->base); /* Deallocate old table entries */ + } + if (!table->dont_keep_spare_table) + { + temptab.spare_base = table->base; /* let's keep a spare in case we just have to clear the DELETED entries */ + temptab.spare_base_size = table->size; + } + + *table = newtable; + + if (table->dont_keep_spare_table) + { + table->spare_base = NULL; + table->spare_base_size = 0; + } else + { + table->spare_base = temptab.spare_base; /* let's keep a spare in case we just have to clear the DELETED entries */ + table->spare_base_size = temptab.spare_base_size; + } + DBGHASHTAB((stderr, "EXPAND_HASHTAB:EXIT: table: table(%lx ) base (%lx), spare_base(%lx), spare_base_size(%d) \n", + table, table->base, table->spare_base, table->spare_base_size)); +} + +/* Description: + Adds a key and corresponding value in hash table. + If key is already present, return false. + If key is not present, it adds the entry and returns true. + As a side-effect tabent points to the matched entry or added entry +*/ +/* This flavor is used by external caller (outside of the hash table implementation */ +boolean_t ADD_HASHTAB(HASH_TABLE *table, HT_KEY_T *key, void *value, HT_ENT **tabentptr) +{ + return ADD_HASHTAB_INTL(table, key, value, tabentptr, FALSE); +} + +/* This flavor is used by internal callers, for example when adding entries during a change of hash table size. */ +STATICFNDEF boolean_t ADD_HASHTAB_INTL(HASH_TABLE *table, HT_KEY_T *key, void *value, HT_ENT **tabentptr, + boolean_t changing_table_size) +{ +#ifdef INT8_HASH + gtm_uint64_t hash, ht_index, save_ht_index, prime, rhfact; +#else + uint4 hash, ht_index, save_ht_index, prime, rhfact; +#endif /* INT8_HASH */ + HT_ENT *oldbase, *first_del_ent, *tabbase; + if (!changing_table_size && (table->count >= table->exp_trigger_size)) + { + oldbase = table->base; + EXPAND_HASHTAB(table, table->size + 1); + if (oldbase == table->base) /* expansion failed */ + { + if (table->exp_trigger_size >= table->size) + /* Note this error routine will use the memory error parameters recorded when the + memory error was first raised by EXPAND_HASHTAB() above so the error will appear + as if it had occurred during that expansion attempt. + */ + raise_gtmmemory_error(); + table->exp_trigger_size = table->size; + } + } + first_del_ent = NULL; + tabbase = &table->base[0]; + prime = table->size; + FIND_HASH(key, hash); + ht_index = (int) (hash % prime); + *tabentptr = tabbase + ht_index; + RETURN_IF_ADDED(table, *tabentptr, key, value); + /* We are here because collision happened. Do collision resolution */ +# ifdef INT8_HASH + assert(MAXUINT4 > ht_index); +# endif + bit_set(ht_index, table->entry_passed_thru); + save_ht_index = ht_index; + SET_REHASH_FACTOR(rhfact, hash, prime); + SET_REHASH_INDEX(ht_index, rhfact, prime); + do + { + *tabentptr = tabbase + ht_index; + RETURN_IF_ADDED(table, *tabentptr, key, value); +# ifdef INT8_HASH + assert(MAXUINT4 > ht_index); +# endif + bit_set(ht_index, table->entry_passed_thru); + SET_REHASH_INDEX(ht_index, rhfact, prime); + } while(ht_index != save_ht_index); + /* All entries either deleted or used. No empty frame found */ + if (NULL != first_del_ent) + { /* There was a deleted one we could use - reuse the deleted frame */ + *tabentptr = first_del_ent; + INSERT_HTENT(table, *tabentptr, key, value); + return TRUE; + } + GTMASSERT; + return FALSE; /* to prevent warnings */ +} + +/* + * Returns pointer to the value corresponding to key, if found. + * Otherwise, it returns null. + */ +void *LOOKUP_HASHTAB(HASH_TABLE *table, HT_KEY_T *key) +{ +# ifdef INT8_HASH + gtm_uint64_t hash, ht_index, save_ht_index, prime, rhfact; +# else + uint4 hash, ht_index, save_ht_index, prime, rhfact; +# endif /* INT8_HASH */ + HT_ENT *tabent, *tabbase; + + tabbase = &table->base[0]; + prime = table->size; + FIND_HASH(key, hash); + ht_index = hash % prime; + tabent = tabbase + ht_index; + RETURN_IF_LOOKUP_DONE(tabent, key); + /* We are here because collision happened. Do collision resolution */ + save_ht_index = ht_index; + SET_REHASH_FACTOR(rhfact, hash, prime); + SET_REHASH_INDEX(ht_index, rhfact, prime); + do + { + tabent = tabbase + ht_index; + RETURN_IF_LOOKUP_DONE(tabent, key); + SET_REHASH_INDEX(ht_index, rhfact, prime); + } while(ht_index != save_ht_index); + return (void *)NULL; +} +/* Description: + Deletes hash table entry from hash table (whether it was active or not). + The function version is for callers outside of the hash table implementation. +*/ +void DELETE_HASHTAB_ENT(HASH_TABLE *table, HT_ENT *tabent) +{ + DELETE_HTENT(table, tabent); +} +/* + * Returns TRUE if + * 1) key is found and deleted successfully + * or + * 2) already key was marked deleted. + * Otherwise, it returns FALSE + * Deletion is done by marking value to HT_DELETED_ENTRY. + * If there are too many deleted entry, we call expand_hashtab() to do the + * compaction eliminating entries marked HT_DELETED_ENTRY + * Compaction will save memory and also cause LOOKUP_HASHTAB to run faster. + */ +boolean_t DELETE_HASHTAB(HASH_TABLE *table, HT_KEY_T *key) +{ +# ifdef INT8_HASH + gtm_uint64_t hash, ht_index, save_ht_index, prime, rhfact; +# else + uint4 hash, ht_index, save_ht_index, prime, rhfact; +# endif /* INT8_HASH */ + HT_ENT *tabent, *tabbase; + + tabbase = &table->base[0]; + prime = table->size; + FIND_HASH(key, hash); + ht_index = hash % prime; + tabent = tabbase + ht_index; + RETURN_IF_DELETED(table, tabent, key); + /* We are here because collision happened. Do collision resolution */ + save_ht_index = ht_index; + SET_REHASH_FACTOR(rhfact, hash, prime); + SET_REHASH_INDEX(ht_index, rhfact, prime); + do + { + tabent = tabbase + ht_index; + RETURN_IF_DELETED(table, tabent, key); + SET_REHASH_INDEX(ht_index, rhfact, prime); + } while(ht_index != save_ht_index); + return FALSE; +} + +/* + * free memory occupied by hash table. + */ +void FREE_HASHTAB(HASH_TABLE *table) +{ + if (table->base) + { + DBGHASHTAB((stderr, "FREE_HASHTAB:free table(%lx): base (%lx)\n", table, table->base)); + free(table->base); + } + table->base = NULL; + if (table->spare_base) + { + DBGHASHTAB((stderr, "FREE_HASHTAB:free table(%lx): spare_base (%lx)\n", table, table->spare_base)); + free(table->spare_base); + } + table->spare_base = NULL; + table->spare_base_size = 0; +} + +/* + * Returns TRUE, if key found and deleted successfully or already deleted. + */ +void REINITIALIZE_HASHTAB(HASH_TABLE *table) +{ + memset((char *)table->base, 0, (table->size * SIZEOF(HT_ENT)) + ((table->size / BITS_PER_UCHAR) + 1)); + HT_FIELDS_COMMON_INIT(table); +} + +/* + * Compact hashtable removing entries marked deleted. Note that this is necessary because + * of the search algorithm in ADD_HASHTAB which needs to find if a key exists before it can + * add a new entry. It keeps searching until it either finds the key, finds an empty (never + * used) entry or until it searches the entire table. So we need to replentish the supply of + * never used nodes. + */ +void COMPACT_HASHTAB(HASH_TABLE *table) +{ + error_def (ERR_HTSHRINKFAIL); + HT_ENT *oldbase; + + DBGHASHTAB((stderr, "COMPACT_HASHTAB: table(%lx)\n", table)); + if (!table->dont_compact) + { + oldbase = (table)->base; + EXPAND_HASHTAB(table, HT_REHASH_TABLE_SIZE(table)); + if (oldbase == (table)->base) /* rehash failed */ + { /* We will continue but performance will likely suffer */ + send_msg(VARLSTCNT(1) ERR_HTSHRINKFAIL); + (table)->cmp_trigger_size = (table)->size; + } + } +} diff --git a/sr_port/hashtab_int4.c b/sr_port/hashtab_int4.c new file mode 100644 index 0000000..d74ed59 --- /dev/null +++ b/sr_port/hashtab_int4.c @@ -0,0 +1,20 @@ +/**************************************************************** + * * + * Copyright 2001, 2004 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#include "mdef.h" +#include "min_max.h" +#include "gtm_string.h" +#include "error.h" +#include "send_msg.h" +#include "gtmmsg.h" +#include "hashtab.h" +#include "hashtab_int4.h" +#define INT4_HASH +#include "hashtab_implementation.h" diff --git a/sr_port/hashtab_int4.h b/sr_port/hashtab_int4.h new file mode 100644 index 0000000..ddd363e --- /dev/null +++ b/sr_port/hashtab_int4.h @@ -0,0 +1,60 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#ifndef HASHTAB_INT4_H +#define HASHTAB_INT4_H + +/* Note that hashtab_addr has #define references to all items in this header file. Changes/additions + should be reflected there as well. +*/ + +typedef struct +{ + uint4 key; + void *value; +} ht_ent_int4; + +typedef struct hash_table_int4_struct +{ + ht_ent_int4 *base; /* base of array of hent_* entries */ + ht_ent_int4 *top; /* top of array of hent_* entries */ + unsigned int size; /* Hash table size */ + unsigned int initial_size; /* Hash table initial size */ + ht_ent_int4 *spare_base; /* spare array of hent_* entries */ + unsigned int spare_base_size;/* size of spare array */ + boolean_t dont_compact; /* if set, never perform compaction */ + boolean_t dont_keep_spare_table; /* if set, don't keep a spare table */ + boolean_t defer_base_release; /* if set don't release base, caller will free_base...() later */ + unsigned int count; /* Number of valid entries */ + unsigned int del_count; /* Number of entries marked deleted. */ + unsigned int exp_trigger_size;/* When exp_trigger_size entried are used, expand table */ + unsigned int cmp_trigger_size;/* When cmp_trigger_size reached compact table */ + sm_uc_ptr_t entry_passed_thru;/* Bit vector used to determine whether a particular */ + /* ht_ent has been involved in a collision (meaning that */ + /* this value can't be marked empty on delete */ +} hash_table_int4; + +#define HTENT_EMPTY_INT4(tabent, type, htvalue) (!(htvalue = (type *)(tabent)->value)) +#define HTENT_MARK_EMPTY_INT4(tabent) (tabent)->value = (void *)0L +#define HTENT_VALID_INT4(tabent, type, htvalue) (!(HTENT_EMPTY_INT4(tabent, type, htvalue)) && (HT_DELETED_ENTRY != htvalue)) +#define COMPUTE_HASH_INT4(hkey, hash) hash = (*hkey) + +/* Prototypes for int4 hash routines. See hashtab_implementation.h for detail interface and implementation */ +void init_hashtab_int4(hash_table_int4 *table, int minsize, boolean_t dont_compact, boolean_t dont_keep_spare_table); +void expand_hashtab_int4(hash_table_int4 *table, int minsize); +boolean_t add_hashtab_int4(hash_table_int4 *table, uint4 *key, void *value, ht_ent_int4 **tabentptr); +void *lookup_hashtab_int4(hash_table_int4 *table, uint4 *key); +void delete_hashtab_ent_int4(hash_table_int4 *table, ht_ent_int4 *tabent); +boolean_t delete_hashtab_int4(hash_table_int4 *table, uint4 *key); +void free_hashtab_int4(hash_table_int4 *table); +void reinitialize_hashtab_int4(hash_table_int4 *table); +void compact_hashtab_int4(hash_table_int4 *table); + +#endif diff --git a/sr_port/hashtab_int8.c b/sr_port/hashtab_int8.c new file mode 100644 index 0000000..1d88f20 --- /dev/null +++ b/sr_port/hashtab_int8.c @@ -0,0 +1,20 @@ +/**************************************************************** + * * + * Copyright 2001, 2004 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#include "mdef.h" +#include "min_max.h" +#include "gtm_string.h" +#include "error.h" +#include "send_msg.h" +#include "gtmmsg.h" +#include "hashtab.h" +#include "hashtab_int8.h" +#define INT8_HASH +#include "hashtab_implementation.h" diff --git a/sr_port/hashtab_int8.h b/sr_port/hashtab_int8.h new file mode 100644 index 0000000..cf1a2da --- /dev/null +++ b/sr_port/hashtab_int8.h @@ -0,0 +1,61 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#ifndef HASHTAB_INT8_H +#define HASHTAB_INT8_H + +/* Note that hashtab_addr has #define references to all items in this header file. Changes/additions + should be reflected there as well. +*/ + +typedef struct +{ + gtm_uint64_t key; + void *value; + NON_GTM64_ONLY(uint4 filler;) /* To make it 16 byte for all platforms and aligned */ +} ht_ent_int8; + +typedef struct hash_table_int8_struct +{ + ht_ent_int8 *base; /* base of array of hent_* entries */ + ht_ent_int8 *top; /* top of array of hent_* entries */ + unsigned int size; /* Hash table size */ + unsigned int initial_size; /* Hash table initial size */ + ht_ent_int8 *spare_base; /* spare array of hent_* entries */ + unsigned int spare_base_size;/* size of spare array */ + boolean_t dont_compact; /* if set, never perform compaction */ + boolean_t dont_keep_spare_table; /* if set, don't keep a spare table */ + boolean_t defer_base_release; /* if set don't release base, caller will free_base...() later */ + unsigned int count; /* Number of valid entries */ + unsigned int del_count; /* Number of entries marked deleted. */ + unsigned int exp_trigger_size;/* When exp_trigger_size entried are used, expand table */ + unsigned int cmp_trigger_size;/* When cmp_trigger_size reached compact table */ + sm_uc_ptr_t entry_passed_thru;/* Bit vector used to determine whether a particular */ + /* ht_ent has been involved in a collision (meaning that */ + /* this value can't be marked empty on delete */ +} hash_table_int8; + +#define HTENT_EMPTY_INT8(tabent, type, htvalue) (!(htvalue = (type *)(tabent)->value)) +#define HTENT_MARK_EMPTY_INT8(tabent) (tabent)->value = (void *)0L +#define HTENT_VALID_INT8(tabent, type, htvalue) ((!HTENT_EMPTY_INT8(tabent, type, htvalue)) && (HT_DELETED_ENTRY != htvalue)) +#define COMPUTE_HASH_INT8(hkey, hash) hash = (((*hkey) & 0xFFFFFFFF) ^ (*hkey) >> 31) + +/* Prototypes for int8 hash routines. See hashtab_implementation.h for detail interface and implementation */ +void init_hashtab_int8(hash_table_int8 *table, int minsize, boolean_t dont_compact, boolean_t dont_keep_spare_table); +void expand_hashtab_int8(hash_table_int8 *table, int minsize); +boolean_t add_hashtab_int8(hash_table_int8 *table, gtm_uint64_t *key, void *value, ht_ent_int8 **tabentptr); +void *lookup_hashtab_int8(hash_table_int8 *table, gtm_uint64_t *key); +void delete_hashtab_ent_int8(hash_table_int8 *table, ht_ent_int8 *tabent); +boolean_t delete_hashtab_int8(hash_table_int8 *table, gtm_uint64_t *key); +void free_hashtab_int8(hash_table_int8 *table); +void reinitialize_hashtab_int8(hash_table_int8 *table); +void compact_hashtab_int8(hash_table_int8 *table); + +#endif diff --git a/sr_port/hashtab_mname.c b/sr_port/hashtab_mname.c new file mode 100644 index 0000000..38ca4bd --- /dev/null +++ b/sr_port/hashtab_mname.c @@ -0,0 +1,79 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#include "mdef.h" + +#include "min_max.h" +#include "gtm_string.h" +#include "error.h" +#include "send_msg.h" +#include "gtmmsg.h" +#include "rtnhdr.h" +#include "hashtab_mname.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "alias.h" + +GBLREF symval *curr_symval; + +#define MNAME_HASH + +/* The below include generates the hash table routines for the "mname" hash type */ +#include "hashtab_implementation.h" + +/* While the above include creates the bulk of the routines in this modules, for the mname hash type we + add one more routine that is a wrapper for the add_hashtab_mname created above. The situation this + wrapper (add_hashtab_mname_symval) covers is if the hash table was expanded, it goes back through the + stack and fixes the stack references kept in l_symval entries in each stackframe. + + Note that there are currently no direct callers of expand_hashtab_mname() (except in op_view) that we + don't already protect or of delete_hashtab_mname() or they would need similar wrappers here. +*/ +boolean_t add_hashtab_mname_symval(hash_table_mname *table, mname_entry *key, void *value, ht_ent_mname **tabentptr) +{ + boolean_t retval; + int table_size_orig; + ht_ent_mname *table_base_orig; + + /* Currently only two values we expect here shown below. If calls are added with other values, they need + to be taken care of here and in EXPAND_HASHTAB in hashtab_implementation.h + */ + assert(table == &curr_symval->h_symtab || table == &curr_symval->last_tab->h_symtab); + assert(FALSE == key->marked); + + /* remember table we started with */ + table_base_orig = table->base; + table_size_orig = table->size; + + /* We'll do the base release once we do the reparations */ + DEFER_BASE_REL_HASHTAB(table, TRUE); + + /* Call real table function */ + retval = add_hashtab_mname(table, key, value, tabentptr); + + /* If the hash table has not changed, we are done */ + if (table_base_orig == table->base) + { + DEFER_BASE_REL_HASHTAB(table, FALSE); + return retval; + } + + /* Otherwise we have some work to do to repair the l_symtab entries in the stack */ + als_lsymtab_repair(table, table_base_orig, table_size_orig); + + /* We're done with the previous base */ + FREE_BASE_HASHTAB(table, table_base_orig); + DEFER_BASE_REL_HASHTAB(table, FALSE); + + return retval; +} diff --git a/sr_port/hashtab_mname.h b/sr_port/hashtab_mname.h new file mode 100644 index 0000000..61b7fbe --- /dev/null +++ b/sr_port/hashtab_mname.h @@ -0,0 +1,63 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#ifndef HASHTAB_MNAME_H +#define HASHTAB_MNAME_H + +#include "hashtab.h" /* needed for STR_HASH (in COMPUTE_HASH_MNAME) */ + +typedef struct +{ + mname_entry key; + void *value; +} ht_ent_mname; + +typedef struct hash_table_mname_struct +{ + ht_ent_mname *base; /* base of array of hent_* entries */ + ht_ent_mname *top; /* top of array of hent_* entries */ + unsigned int size; /* Hash table size */ + unsigned int initial_size; /* Hash table initial size */ + ht_ent_mname *spare_base; /* spare array of hent_* entries */ + unsigned int spare_base_size;/* size of spare array */ + boolean_t dont_compact; /* if set, never perform compaction */ + boolean_t dont_keep_spare_table; /* if set, don't keep a spare table */ + boolean_t defer_base_release; /* if set don't release base, caller will free_base...() later */ + unsigned int count; /* Number of valid entries */ + unsigned int del_count; /* Number of entries marked deleted. */ + unsigned int exp_trigger_size;/* When exp_trigger_size entried are used, expand table */ + unsigned int cmp_trigger_size;/* When cmp_trigger_size reached compact table */ + sm_uc_ptr_t entry_passed_thru;/* Bit vector used to determine whether a particular */ + /* ht_ent has been involved in a collision (meaning that */ + /* this value can't be marked empty on delete */ +} hash_table_mname; + +#define HTENT_EMPTY_MNAME(tabent, type, htvalue) (!(tabent)->key.var_name.len) +#define HTENT_MARK_EMPTY_MNAME(tabent) (tabent)->key.var_name.len = 0 +#define HTENT_VALID_MNAME(tabent, type, htvalue) ((!HTENT_EMPTY_MNAME(tabent, type, htvalue)) && \ + (HT_DELETED_ENTRY != (htvalue = (type *)(tabent)->value))) +#define COMPUTE_HASH_MNAME(hkey) \ +{ \ + assert((0 < (hkey)->var_name.len) && (MAX_MIDENT_LEN >= (hkey)->var_name.len)); \ + STR_HASH((hkey)->var_name.addr, (hkey)->var_name.len, ((hkey)->hash_code), 0); \ +} + +/* Prototypes for mname hash routines. See hashtab_implementation.h for detail interface and implementation */ +void init_hashtab_mname(hash_table_mname *table, int minsize, boolean_t dont_compact, boolean_t dont_keep_spare_table); +void expand_hashtab_mname(hash_table_mname *table, int minsize); +boolean_t add_hashtab_mname(hash_table_mname *table, mname_entry *key, void *value, ht_ent_mname **tabentptr); +boolean_t add_hashtab_mname_symval(hash_table_mname *table, mname_entry *key, void *value, ht_ent_mname **tabentptr); +void *lookup_hashtab_mname(hash_table_mname *table, mname_entry *key); +void delete_hashtab_ent_mname(hash_table_mname *table, ht_ent_mname *tabent); +boolean_t delete_hashtab_mname(hash_table_mname *table, mname_entry *key); +void free_hashtab_mname(hash_table_mname *table); +void reinitialize_hashtab_mname(hash_table_mname *table); +void compact_hashtab_mname(hash_table_mname *table); +#endif diff --git a/sr_port/hashtab_objcode.c b/sr_port/hashtab_objcode.c new file mode 100644 index 0000000..679a8c8 --- /dev/null +++ b/sr_port/hashtab_objcode.c @@ -0,0 +1,20 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#include "mdef.h" +#include "min_max.h" +#include "gtm_string.h" +#include "error.h" +#include "send_msg.h" +#include "gtmmsg.h" +#include "cache.h" +#include "hashtab_objcode.h" +#define OBJCODE_HASH +#include "hashtab_implementation.h" diff --git a/sr_port/hashtab_objcode.h b/sr_port/hashtab_objcode.h new file mode 100644 index 0000000..c4fa9fb --- /dev/null +++ b/sr_port/hashtab_objcode.h @@ -0,0 +1,75 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#ifndef HASHTAB_OBJCODE_H +#define HASHTAB_OBJCODE_H + +#include "hashtab.h" /* needed for STR_HASH (in COMPUTE_HASH_MNAME) */ + +typedef struct +{ + icode_str key; + void *value; +} ht_ent_objcode; + +typedef struct hash_table_objcode_struct +{ + ht_ent_objcode *base; /* base of array of hent_* entries */ + ht_ent_objcode *top; /* top of array of hent_* entries */ + unsigned int size; /* Hash table size */ + unsigned int initial_size; /* Hash table initial size */ + ht_ent_objcode *spare_base; /* spare array of hent_* entries */ + unsigned int spare_base_size;/* size of spare array */ + boolean_t dont_compact; /* if set, never perform compaction */ + boolean_t dont_keep_spare_table; /* if set, don't keep a spare table */ + boolean_t defer_base_release; /* if set don't release base, caller will free_base...() later */ + unsigned int count; /* Number of valid entries */ + unsigned int del_count; /* Number of entries marked deleted. */ + unsigned int exp_trigger_size;/* When exp_trigger_size entried are used, expand table */ + unsigned int cmp_trigger_size;/* When cmp_trigger_size reached compact table */ + sm_uc_ptr_t entry_passed_thru;/* Bit vector used to determine whether a particular */ + /* ht_ent has been involved in a collision (meaning that */ + /* this value can't be marked empty on delete */ +} hash_table_objcode; + +#define HTENT_EMPTY_OBJCODE(tabent, type, htvalue) (!(htvalue = (type *)(tabent)->value) && !(tabent)->key.str.len) +#define HTENT_MARK_EMPTY_OBJCODE(tabent) \ +{ \ + (tabent)->value = NULL; \ + (tabent)->key.str.len = 0; \ +} +#define HTENT_VALID_OBJCODE(tabent, type, htvalue) ((!HTENT_EMPTY_OBJCODE(tabent, type, htvalue)) && (HT_DELETED_ENTRY != htvalue)) +#define COMPUTE_HASH_OBJCODE(hkey, hash) \ +{ \ + char *sptr; \ + sptr = (hkey)->str.addr; \ + if ((hkey)->str.len < SIZEOF(mident_fixed)) \ + { \ + STR_HASH((sptr), (hkey)->str.len, hash, 0); \ + } else \ + { \ + STR_HASH((sptr), (SIZEOF(mident_fixed) / 2), hash, 0); \ + STR_HASH(((sptr) + (hkey)->str.len - (SIZEOF(mident_fixed) / 2)), \ + (SIZEOF(mident_fixed) / 2), hash, hash); \ + } \ +} + +/* Prototypes for objcode hash routines. See hashtab_implementation.h for detail interface and implementation */ +void init_hashtab_objcode(hash_table_objcode *table, int minsize, boolean_t dont_compact, boolean_t dont_keep_spare_table); +void expand_hashtab_objcode(hash_table_objcode *table, int minsize); +boolean_t add_hashtab_objcode(hash_table_objcode *table, icode_str *key, void *value, ht_ent_objcode **tabentptr); +void *lookup_hashtab_objcode(hash_table_objcode *table, icode_str *key); +void delete_hashtab_ent_objcode(hash_table_objcode *table, ht_ent_objcode *tabent); +boolean_t delete_hashtab_objcode(hash_table_objcode *table, icode_str *key); +void free_hashtab_objcode(hash_table_objcode *table); +void reinitialize_hashtab_objcode(hash_table_objcode *table); +void compact_hashtab_objcode(hash_table_objcode *table); + +#endif diff --git a/sr_port/hashtab_rehash_ch.c b/sr_port/hashtab_rehash_ch.c new file mode 100644 index 0000000..96cf45f --- /dev/null +++ b/sr_port/hashtab_rehash_ch.c @@ -0,0 +1,38 @@ +/**************************************************************** + * * + * Copyright 2003, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "error.h" +#include "util.h" + +CONDITION_HANDLER(hashtab_rehash_ch) +{ + /* If we cannot alloc memory during rehashing, just continue in normal program flow */ + error_def(ERR_MEMORY); + error_def(ERR_MEMORYRECURSIVE); + error_def(ERR_HTOFLOW); + + START_CH; + /* If we cannot allocate memory or any error while doing rehash, just abort any more rehashing. + * We will continue with old table. Note that we do not ignore VMSMEMORY errors because if a + * VMS_MEMORY error occurred, gtm_malloc is going to have released the memory cache trying to get + * through process exit cleanly. We have no option to ignore the memory error on VMS + */ + if (ERR_HTOFLOW == SIGNAL || ERR_MEMORY == SIGNAL || ERR_MEMORYRECURSIVE == SIGNAL) + { + UNIX_ONLY(util_out_print("", RESET)); /* Prevents error message from being flushed later by rts_error() */ + UNWIND(NULL, NULL); + } else + { + NEXTCH; /* non memory related error */ + } +} diff --git a/sr_port/hashtab_str.c b/sr_port/hashtab_str.c new file mode 100644 index 0000000..a649edc --- /dev/null +++ b/sr_port/hashtab_str.c @@ -0,0 +1,22 @@ +/**************************************************************** + * * + * Copyright 2009, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "min_max.h" +#include "gtm_string.h" +#include "error.h" +#include "send_msg.h" +#include "gtmmsg.h" +#include "hashtab_str.h" + +#define STRING_HASH +/* The below include generates the hash table routines for the literal hash type */ +#include "hashtab_implementation.h" diff --git a/sr_port/hashtab_str.h b/sr_port/hashtab_str.h new file mode 100644 index 0000000..071a03d --- /dev/null +++ b/sr_port/hashtab_str.h @@ -0,0 +1,78 @@ +/**************************************************************** + * * + * Copyright 2009, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#ifndef HASHTAB_STR_H +#define HASHTAB_STR_H + +#include "hashtab.h" /* needed for STR_HASH (in COMPUTE_HASH_MNAME) */ + +typedef struct +{ + mstr str; + uint4 hash_code; + GTM64_ONLY(int4 filler;) +} stringkey; + +typedef struct +{ + stringkey key; + void *value; +} ht_ent_str; + +typedef struct hash_table_str_struct +{ + ht_ent_str *base; /* base of array of hent_* entries */ + ht_ent_str *top; /* top of array of hent_* entries */ + unsigned int size; /* Hash table size */ + unsigned int initial_size; /* Hash table initial size */ + ht_ent_str *spare_base; /* spare array of hent_* entries */ + unsigned int spare_base_size;/* size of spare array */ + boolean_t dont_compact; /* if set, never perform compaction */ + boolean_t dont_keep_spare_table; /* if set, don't keep a spare table */ + boolean_t defer_base_release; /* if set don't release base, caller will free_base...() later */ + unsigned int count; /* Number of valid entries */ + unsigned int del_count; /* Number of entries marked deleted. */ + unsigned int exp_trigger_size;/* When exp_trigger_size entried are used, expand table */ + unsigned int cmp_trigger_size;/* When cmp_trigger_size reached compact table */ + sm_uc_ptr_t entry_passed_thru;/* Bit vector used to determine whether a particular */ + /* ht_ent has been involved in a collision (meaning that */ + /* this value can't be marked empty on delete */ +} hash_table_str; + +#define HTENT_EMPTY_STR(tabent, type, htvalue) (!(tabent)->key.hash_code && !(tabent)->key.str.len) +#define HTENT_MARK_EMPTY_STR(tabent) \ +{ \ + (tabent)->key.hash_code = 0; \ + (tabent)->key.str.len = 0; \ +} +#define HTENT_VALID_STR(tabent, type, htvalue) ((!HTENT_EMPTY_STR(tabent, type, htvalue)) && \ + (HT_DELETED_ENTRY != (htvalue = (type *)(tabent)->value))) +#define COMPUTE_HASH_STR(hkey) \ +{ \ + assert((0 <= (hkey)->str.len) && (MAX_STRLEN >= (hkey)->str.len)); \ + if (0 == (hkey)->str.len) \ + /* Likely null string -- need nonzero hash code */ \ + (hkey)->hash_code = 1; \ + else \ + STR_HASH((hkey)->str.addr, (hkey)->str.len, ((hkey)->hash_code), 0); \ +} + +/* Prototypes for addr hash routines. See hashtab_implementation.h for detail interface and implementation */ +void init_hashtab_str(hash_table_str *table, int minsize, boolean_t dont_compact, boolean_t dont_keep_spare_table); +void expand_hashtab_str(hash_table_str *table, int minsize); +boolean_t add_hashtab_str(hash_table_str *table, stringkey *key, void *value, ht_ent_str **tabentptr); +void *lookup_hashtab_str(hash_table_str *table, stringkey *key); +void delete_hashtab_ent_str(hash_table_str *table, ht_ent_str *tabent); +boolean_t delete_hashtab_str(hash_table_str *table, stringkey *key); +void free_hashtab_str(hash_table_str *table); +void reinitialize_hashtab_str(hash_table_str *table); +void compact_hashtab_str(hash_table_str *table); + +#endif /* HASHTAB_STR_H */ diff --git a/sr_port/have_crit.c b/sr_port/have_crit.c new file mode 100644 index 0000000..c80c38e --- /dev/null +++ b/sr_port/have_crit.c @@ -0,0 +1,140 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_inet.h" +#ifdef VMS +#include /* Required for gtmsource.h */ +#endif + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "repl_msg.h" +#include "gtmsource.h" +#include "filestruct.h" +#include "jnl.h" +#include "dpgbldir.h" +#include "have_crit.h" +#include "send_msg.h" + +GBLREF volatile int4 crit_count; +GBLREF jnlpool_addrs jnlpool; +GBLREF uint4 process_id; +GBLREF uint4 crit_deadlock_check_cycle; +GBLREF uint4 dollar_tlevel; +GBLREF unsigned int t_tries; +GBLREF boolean_t hold_onto_locks; + +/* Return number of regions (including jnlpool dummy region) if have or are aquiring crit or in_wtstart + * ** NOTE ** This routine is called from signal handlers and is thus called asynchronously. + * If CRIT_IN_COMMIT bit is set, we check if in middle of commit (PHASE1 inside crit or PHASE2 outside crit) on some region. + * If CRIT_RELEASE bit is set, we release crit on region(s) that: + * 1) we hold crit on (neither CRIT_IN_COMMIT NOR CRIT_TRANS_NO_REG is specified) + * 2) are part of the current transactions except those regions that are marked as being valid + * to have crit in by virtue of their crit_check_cycle value is the same as crit_deadlock_check_cycle. + * Note: CRIT_RELEASE implies CRIT_ALL_REGIONS + * If CRIT_ALL_REGIONS bit is set, go through the entire list of regions + */ +uint4 have_crit(uint4 crit_state) +{ + gd_region *r_top, *r_local; + gd_addr *addr_ptr; + sgmnt_addrs *csa; + uint4 crit_reg_cnt = 0; + + error_def(ERR_MUTEXRELEASED); + + /* in order to proper release the necessary regions, CRIT_RELEASE implies going through all the regions */ + if (crit_state & CRIT_RELEASE) + { + assert(!hold_onto_locks); /* should not request crit to be released with this variable set to TRUE */ + crit_state |= CRIT_ALL_REGIONS; + } + if (0 != crit_count) + { + crit_reg_cnt++; + if (0 == (crit_state & CRIT_ALL_REGIONS)) + return crit_reg_cnt; + } + for (addr_ptr = get_next_gdr(NULL); addr_ptr; addr_ptr = get_next_gdr(addr_ptr)) + { + for (r_local = addr_ptr->regions, r_top = r_local + addr_ptr->n_regions; r_local < r_top; r_local++) + { + if (r_local->open && !r_local->was_open) + { + csa = &FILE_INFO(r_local)->s_addrs; + if (NULL != csa) + { + if (csa->now_crit) + { + crit_reg_cnt++; + /* It is possible that if DSE has done a CRIT REMOVE and stolen our crit, it + could be given to someone else which would cause this test to fail. The + current thinking is that the state DSE put this process is no longer viable + and it should die at the earliest opportunity, there being no way to know if + that is what happened anyway. + */ + if (csa->nl->in_crit != process_id) + GTMASSERT; + /* If we are releasing (all) regions with critical section or if special + TP case, release if the cycle number doesn't match meaning this is a + region we should not hold crit in (even if it is part of tp_reg_list). + */ + if ((0 != (crit_state & CRIT_RELEASE)) && + (0 == (crit_state & CRIT_NOT_TRANS_REG) || + crit_deadlock_check_cycle != csa->crit_check_cycle)) + { + assert(FALSE); + rel_crit(r_local); + send_msg(VARLSTCNT(8) ERR_MUTEXRELEASED, 6, + process_id, process_id, DB_LEN_STR(r_local), + dollar_tlevel, t_tries); + } + if (0 == (crit_state & CRIT_ALL_REGIONS)) + return crit_reg_cnt; + } + /* In Commit-crit is defined as the time since when early_tn is 1 + curr_tn upto when + * t_commit_crit is set to FALSE. Note that the first check should be done only if we + * hold crit as otherwise we could see inconsistent values. + */ + if ((crit_state & CRIT_IN_COMMIT) + && (csa->now_crit && (csa->ti->early_tn != csa->ti->curr_tn) || csa->t_commit_crit)) + { + crit_reg_cnt++; + if (0 == (crit_state & CRIT_ALL_REGIONS)) + return crit_reg_cnt; + } + if ((crit_state & CRIT_IN_WTSTART) && csa->in_wtstart) + { + crit_reg_cnt++; + if (0 == (crit_state & CRIT_ALL_REGIONS)) + return crit_reg_cnt; + } + } + } + } + } + if (NULL != jnlpool.jnlpool_ctl) + { + csa = &FILE_INFO(jnlpool.jnlpool_dummy_reg)->s_addrs; + if (NULL != csa && csa->now_crit) + { + crit_reg_cnt++; + if (0 != (crit_state & CRIT_RELEASE)) + rel_lock(jnlpool.jnlpool_dummy_reg); + } + } + return crit_reg_cnt; +} diff --git a/sr_port/have_crit.h b/sr_port/have_crit.h new file mode 100644 index 0000000..eb1bf77 --- /dev/null +++ b/sr_port/have_crit.h @@ -0,0 +1,124 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef HAVE_CRIT_H_INCLUDED +#define HAVE_CRIT_H_INCLUDED + +/* states of CRIT passed as argument to have_crit() */ +#define CRIT_IN_COMMIT 0x00000001 +#define CRIT_NOT_TRANS_REG 0x00000002 +#define CRIT_RELEASE 0x00000004 +#define CRIT_ALL_REGIONS 0x00000008 + +#define CRIT_IN_WTSTART 0x00000010 /* check if csa->in_wtstart is true */ + +/* Note absence of any flags is default value which finds if any region + or the replication pool have crit or are getting crit. It returns + when one is found without checking further. +*/ +#define CRIT_HAVE_ANY_REG 0x00000000 + +typedef enum +{ + INTRPT_OK_TO_INTERRUPT = 0, + INTRPT_IN_GTCMTR_TERMINATE, + INTRPT_IN_TP_UNWIND, + INTRPT_IN_TP_CLEAN_UP, + INTRPT_IN_CRYPT_SECTION, + INTRPT_IN_DB_CSH_GETN, + INTRPT_IN_GVCST_INIT, + INTRPT_IN_GDS_RUNDOWN, + INTRPT_IN_SS_INITIATE, + INTRPT_IN_ZLIB_CMP_UNCMP, + INTRPT_IN_TRIGGER_NOMANS_LAND, /* State where have trigger base frame but no trigger (exec) frame */ + INTRPT_IN_MUR_OPEN_FILES, + INTRPT_NUM_STATES, +} intrpt_state_t; + +GBLREF intrpt_state_t intrpt_ok_state; + +/* Macro to check if we are in a state that is ok to interrupt (or to do deferred signal handling). + * We do not want to interrupt if the global variable intrpt_ok_state indicates it is not ok to interrupt, + * if we are in the midst of a malloc, if we are holding crit, if we are in the midst of commit, or in + * wcs_wtstart. In the last case, we could be causing another process HOLDING CRIT on the region to wait + * in bg_update_phase1 if we hold the write interlock. Hence it is important for us to finish that as soon + * as possible and not interrupt it. + */ +#define OK_TO_INTERRUPT ((INTRPT_OK_TO_INTERRUPT == intrpt_ok_state) && (0 == gtmMallocDepth) \ + && (0 == have_crit(CRIT_HAVE_ANY_REG | CRIT_IN_COMMIT | CRIT_IN_WTSTART))) + +/* Macro to be used whenever we want to handle any signals that we deferred handling and exit in the process. + * In VMS, we dont do any signal handling, only exit handling. + */ +#define DEFERRED_EXIT_HANDLING_CHECK \ +{ \ + VMS_ONLY(GBLREF int4 exi_condition;) \ + GBLREF int process_exiting; \ + GBLREF VSIG_ATOMIC_T forced_exit; \ + GBLREF volatile int4 gtmMallocDepth; \ + \ + if (forced_exit && !process_exiting && OK_TO_INTERRUPT) \ + UNIX_ONLY(deferred_signal_handler();) \ + VMS_ONLY(sys$exit(exi_condition);) \ +} + +/* Macro to cause deferrable interrupts to be deferred recording the cause. + * If interrupt is already deferred, state is not changed. + * + * The normal usage of the below macros is + * DEFER_INTERRUPTS + * non-interruptible code + * ENABLE_INTERRUPTS + * We want the non-interruptible code to be executed AFTER the SAVE_INTRPT_OK_STATE macro. + * To enforce this ordering, one would think a read memory barrier is needed in between. + * But it is not needed. This is because we expect the non-interruptible code to have + * a) pointer dereferences OR + * b) function calls + * Either of these will prevent the compiler from reordering the non-interruptible code. + * Any non-interruptible code that does not have either of the above usages (for e.g. uses C global + * variables) might be affected by compiler reordering. As of now, there is no known case of such + * usage and no such usage is anticipated in the future. + * + * We dont need to worry about machine reordering as well since there is no shared memory variable + * involved here (intrpt_ok_state is a process private variable) and even if any reordering occurs + * they will all be in-flight instructions when the interrupt occurs so the hardware will guarantee + * all such instructions are completely done or completely discarded before servicing the interrupt + * which means the interrupt service routine will never see a reordered state of the above code. + */ +#define DEFER_INTERRUPTS(NEWSTATE) \ +{ \ + if (INTRPT_OK_TO_INTERRUPT == intrpt_ok_state) \ + /* Only reset state if we are in "OK" state */ \ + intrpt_ok_state = NEWSTATE; \ + else \ + assert((NEWSTATE) != intrpt_ok_state); /* Make sure not nesting same code */ \ +} + +/* Re-enable deferrable interrupts if the expected state is found. If expected state is not found, then + * we must have nested interrupt types. Avoid state changes in that case. When the nested state pops, + * interrupts will be restored. + */ +#define ENABLE_INTERRUPTS(OLDSTATE) \ +{ \ + assert(((OLDSTATE) == intrpt_ok_state) || (INTRPT_OK_TO_INTERRUPT != intrpt_ok_state)); \ + if ((OLDSTATE) == intrpt_ok_state) \ + { /* Only reset state if in expected state - othwise state must be non-zero which is \ + * asserted above. \ + */ \ + intrpt_ok_state = INTRPT_OK_TO_INTERRUPT; \ + DEFERRED_EXIT_HANDLING_CHECK; /* check if signals were deferred while held lock */ \ + } \ +} + +uint4 have_crit(uint4 crit_state); + +#endif /* HAVE_CRIT_H_INCLUDED */ + diff --git a/sr_port/hd.mpt b/sr_port/hd.mpt new file mode 100644 index 0000000..f693af2 --- /dev/null +++ b/sr_port/hd.mpt @@ -0,0 +1,25 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 1987,2001 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +%HD ;GT.M %HD utility - hexadecimal to decimal conversion program + ;invoke at INT with %HD in hexadecimal to return %HD in decimal + ;invoke at FUNC as an extrinsic function + ;if you make heavy use of this routine, consider $ZCALL + ; + s %HD=$$FUNC(%HD) + q +INT r !,"Hexidecimal: ",%HD s %HD=$$FUNC(%HD) + q +FUNC(h) + q:$tr(h,"E","e")<0 "" + n c,d,dg + s d=0,h=$tr(h,"abcdef","ABCDEF") + f c=1:1:$l(h) s dg=$f("0123456789ABCDEF",$e(h,c)) q:'dg s d=(d*16)+(dg-2) + q d diff --git a/sr_port/ho.mpt b/sr_port/ho.mpt new file mode 100644 index 0000000..a6a0fe4 --- /dev/null +++ b/sr_port/ho.mpt @@ -0,0 +1,28 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 1987, 2003 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +%HO ;GT.M %HO utility - hexadecimal to octal conversion program + ;invoke at INT with %HO in hexadecimal to return %HO in octal + ;invoke at FUNC as an extrinsic function + ;if you make heavy use of this routine, consider $ZCALL + ; + s %HO=$$FUNC(%HO) + q +INT r !,"Hexadecimal: ",%HO s %HO=$$FUNC(%HO) + q +FUNC(h) + q:$tr(h,"E","e")<0 "" + n c,d,dg,o + s d=0,h=$tr(h,"abcdef","ABCDEF"),o="" + f c=1:1:$l(h) s dg=$f("0123456789ABCDEF",$e(h,c)) q:'dg s d=(d*16)+(dg-2) + f q:'d s o=d#8_o,d=d\8 + q:0= (uintszofptr_t)ar); + len = (unsigned int)(ar + SIZEOF(ar) - q); + memcpy(p, q, len); + return p + len; +} + +uchar_ptr_t i2ascl(uchar_ptr_t p, qw_num n) +{ + unsigned char ar[MAX_DIGITS_IN_INT8], *q; + uint4 len; + long r; + + q = ar + SIZEOF(ar); + if (QWEQ(n, seq_num_zero)) + *--q = '0'; + else + { + while (QWNE(n, seq_num_zero)) + { + QWDIVIDEBYDW(n, 10, n, r); + *--q = r + '0'; + } + } + assert((uintszofptr_t)q >= (uintszofptr_t)ar); + len = (uint4)(ar + SIZEOF(ar) - q); + memcpy(p, q, len); + return (unsigned char *)(p + len) ; +} +#ifdef INT8_SUPPORTED +uchar_ptr_t i2asclx(uchar_ptr_t p, qw_num n) +{ + unsigned char ar[MAX_HEX_DIGITS_IN_INT8], *q; + uint4 len; + qw_num m; + + q = ar + SIZEOF(ar); + if (!n) + *--q = '0'; + else + { + while (n) + { + m = n & 0xF; + if (m <= 9) + *--q = m + '0'; + else + *--q = m - 0xa + 'A'; + n = n >> 4; + } + } + assert((uintszofptr_t)q >= (uintszofptr_t)ar); + len = (uint4)(ar + SIZEOF(ar) - q); + memcpy(p, q, len); + return p + len; +} +#else +uchar_ptr_t i2asclx(uchar_ptr_t p, qw_num n) +{ + unsigned char ar[24], *q; + uint4 msb, lsb, len, nibble; + int i; + + q = ar + SIZEOF(ar); + lsb = n.value[lsb_index]; + msb = n.value[msb_index]; + if (msb) + { + for (i = 0; i < 8; i++) /* 8 to denote 8 nibbles per 4-byte value */ + { + nibble = lsb & 0xF; + if (nibble <= 9) + *--q = nibble + '0'; + else + *--q = nibble - 0xa + 'A'; + lsb = lsb >> 4; + } + lsb = msb; + } + if (!lsb) + *--q = '0'; + else + { + while (lsb) + { + nibble = lsb & 0xF; + if (nibble <= 9) + *--q = nibble + '0'; + else + *--q = nibble - 0xa + 'A'; + lsb = lsb >> 4; + } + } + assert((uintszofptr_t)q >= (uintszofptr_t)ar); + len = ar + SIZEOF(ar) - q; + memcpy(p, q, len); + return p + len; +} +#endif diff --git a/sr_port/i2hex.c b/sr_port/i2hex.c new file mode 100644 index 0000000..0f9f7e6 --- /dev/null +++ b/sr_port/i2hex.c @@ -0,0 +1,61 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +void i2hex(UINTPTR_T val, uchar_ptr_t dest, int len) +{ + register uchar_ptr_t cp; + register UINTPTR_T n; + char x; + n = val; + cp = &dest[len]; + while (cp > dest) + { + x = n & 0xF; + n >>= 4; + *--cp = x + ((x > 9) ? 'A' - 10 : '0'); + } + return; +} + +#ifdef INT8_SUPPORTED +void i2hexl(qw_num val, uchar_ptr_t dest, int len) +{ + uchar_ptr_t cp; + qw_num n; + char x; + + n = val; + cp = &dest[len]; + + while (cp > dest) + { + x = n & 0xF; + n >>= 4; + *--cp = x + ((x > 9) ? 'A' - 10 : '0'); + } +} +#else +void i2hexl(qw_num val, uchar_ptr_t dest, int len) +{ + if (4 > len) + { /* We only process some bytes of the low order word */ + i2hex(val.value[msb_index], dest, len); + return; + } + i2hex(val.value[msb_index], dest, 8); /* Process low order word */ + dest += 8; + len -= 8; + if (len) + i2hex(val.value[lsb_index], dest, len); +} +#endif diff --git a/sr_port/i2hex_blkfill.c b/sr_port/i2hex_blkfill.c new file mode 100644 index 0000000..c905595 --- /dev/null +++ b/sr_port/i2hex_blkfill.c @@ -0,0 +1,46 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +void i2hex_blkfill(int num, uchar_ptr_t addr, int len) +{ + unsigned char buff[8]; + int i; + + assert(SIZEOF(buff) >= len); + i2hex(num, buff, len); + for ( i = 0; i < len - 1 ; i++) + { + if (buff[i] != '0') + break; + buff[i] = ' '; + } + memcpy(addr, buff, len); +} + +void i2hexl_blkfill(qw_num num, uchar_ptr_t addr, int len) +{ + unsigned char buff[16]; + int i; + + assert(SIZEOF(buff) >= len); + i2hexl(num, buff, len); + for ( i = 0; i < len - 1 ; i++) + { + if (buff[i] != '0') + break; + buff[i] = ' '; + } + memcpy(addr, buff, len); +} diff --git a/sr_port/i2hex_nofill.c b/sr_port/i2hex_nofill.c new file mode 100644 index 0000000..dc789b8 --- /dev/null +++ b/sr_port/i2hex_nofill.c @@ -0,0 +1,44 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +int i2hex_nofill(int num, uchar_ptr_t addr, int len) +{ + unsigned char buff[8]; + int i; + + assert(SIZEOF(buff) >= len); + i2hex(num, buff, len); + for (i = 0; i < len - 1 ; i++) + { if (buff[i] != '0') + break; + } + memcpy(addr, &buff[i], len - i); + return len - i; +} + +int i2hexl_nofill(qw_num num, uchar_ptr_t addr, int len) +{ + unsigned char buff[16]; + int i; + + assert(SIZEOF(buff) >= len); + i2hexl(num, buff, len); + for (i = 0; i < len - 1 ; i++) + { if (buff[i] != '0') + break; + } + memcpy(addr, &buff[i], len - i); + return len - i; +} diff --git a/sr_port/ind_cg_var.c b/sr_port/ind_cg_var.c new file mode 100644 index 0000000..a8884be --- /dev/null +++ b/sr_port/ind_cg_var.c @@ -0,0 +1,31 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "rtnhdr.h" +#include "cache.h" +#include "obj_file.h" +#include "cg_var.h" +#include "stringpool.h" +#include "hashtab_mname.h" + +GBLREF spdesc indr_stringpool; + +void ind_cg_var(mvar *v, var_tabent **p) +{ /* Copy mident with variable name to variable table entry */ + assert((char *)indr_stringpool.base <= v->mvname.addr && v->mvname.addr < (char *)indr_stringpool.top); + (*p)[v->mvidx].var_name = v->mvname; + COMPUTE_HASH_MNAME(&((*p)[v->mvidx])); + (*p)[v->mvidx].var_name.addr = (char *)((v->mvname.addr - (char *)indr_stringpool.base) + + ROUND_UP2(SIZEOF(ihdtyp), NATIVE_WSIZE)); + (*p)[v->mvidx].marked = FALSE; +} diff --git a/sr_port/ind_code.c b/sr_port/ind_code.c new file mode 100644 index 0000000..e0b1926 --- /dev/null +++ b/sr_port/ind_code.c @@ -0,0 +1,152 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "objlabel.h" +#include "rtnhdr.h" +#include "cache.h" +#include "cgp.h" +#include "stringpool.h" +#include "copy.h" +#include "mmemory.h" +#include "obj_file.h" +#include "cg_var.h" + +GBLREF boolean_t run_time; +GBLREF int4 mvmax, mlitmax; +GBLREF mvar *mvartab; +GBLREF int4 curr_addr, code_size; +GBLREF int4 sa_temps[]; +GBLREF int4 sa_temps_offset[]; +GBLREF char cg_phase; /* code generation phase */ +GBLREF char cg_phase_last; /* previous code generation phase */ +GBLREF spdesc stringpool, indr_stringpool; +#ifdef __ia64 +GBLREF int4 generated_code_size, calculated_code_size; +#endif + +GBLDEF unsigned char *runtime_base; + +/************** Indirect Object Code Format ************* + * + * +-------------------------+> aligned section boundary + * | ihdtyp | + * +-------------------------> aligned section boundary + * | lit text pool | + * +-------------------------- + * | lit mval table | + * +-------------------------- + * | hdr_offset (4-byte) | (8-byte on GTM64) + * +-------------------------- + * | validation (4-byte) | (8-byte on GTM64) + * +-------------------------- + * | Executable Code | + * +-------------------------- + * | variable Table | + * +-------------------------+ + * + ***************************************************/ + +void ind_code(mstr *obj) +{ + var_tabent *vptr; + ihdtyp *itext; + uint4 indir_code_size; + IA64_ONLY(int old_code_size;) + INTPTR_T validation, hdr_offset, long_temp; + unsigned char *indr_base_addr; + + assert(run_time); + curr_addr = SIZEOF(ihdtyp); + cg_phase = CGP_APPROX_ADDR; + cg_phase_last = CGP_NOSTATE; + IA64_ONLY(calculated_code_size = 0); + code_gen(); + code_size = curr_addr; + cg_phase = CGP_ADDR_OPT; +# if (!defined(USHBIN_SUPPORTED) && !defined(VMS)) /* non-shared binary UNIX platforms */ + shrink_jmps(); +# endif + /* GTM64: assuming indir_code_size wont exceed MAX_INT */ + indir_code_size = + (uint4)((SECTION_ALIGN_BOUNDARY - 1) /* extra padding to align the beginning of the entire indirect object */ + + SIZEOF(ihdtyp) + PADLEN(SIZEOF(ihdtyp), NATIVE_WSIZE) /* extra padding to align the beginning of lit text pool */ + + ROUND_UP2(indr_stringpool.free - indr_stringpool.base, NATIVE_WSIZE) /* base literal strings */ + + mlitmax * SIZEOF(mval) /* literal mval table aligned at NATIVE_WSIZE boundary */ + + (SIZEOF(INTPTR_T) * 2) /* validation word and (neg) offset to ihdtyp */ + /* SIZEOF(INTPTR_T) is used for alignment reasons */ + + GTM64_ONLY((SECTION_ALIGN_BOUNDARY - 1)) /* extra padding to align the beginning of the code address */ + + code_size /* code already aligned at SECTION_ALIGN_BOUNDARY boundary */ + + mvmax * SIZEOF(var_tabent)) /* variable table ents */ + + SIZEOF(mval) + SIZEOF(mname_entry) + SIZEOF(mident_fixed) + (SIZEOF(uint4) * 2); /* in case of/see op_indlvadr */ + ENSURE_STP_FREE_SPACE(indir_code_size); + /* Align the beginning of the indirect object so that ihdtyp fields can be accessed normally */ + stringpool.free = (unsigned char *)ROUND_UP2((UINTPTR_T)stringpool.free, SECTION_ALIGN_BOUNDARY); + itext = (ihdtyp *)stringpool.free; + indr_base_addr = stringpool.free; + stringpool.free += SIZEOF(ihdtyp); + indir_lits(itext); + /* Runtime base (fp->ctxt) needs to be set to the beginning of the Executable code so that + * the literal references are generated with appropriate (-ve) offsets from the base + * register (fp->ctxt). On USHBIN_SUPPORTED platforms, runtime_base should be computed + * before shrink_trips since it could be used in codegen of literals + */ + runtime_base = stringpool.free + SIZEOF(hdr_offset) + SIZEOF(validation); + /* Align the begining of the code so that it can be access properly. */ + GTM64_ONLY(runtime_base = (unsigned char *)ROUND_UP2((UINTPTR_T)runtime_base, SECTION_ALIGN_BOUNDARY);) + IA64_ONLY(old_code_size = code_size;) +# if defined(USHBIN_SUPPORTED) || defined(VMS) + shrink_trips(); +# endif + IA64_ONLY( + if (old_code_size != code_size) + calculated_code_size -= ((old_code_size - code_size)/16); + ) + /* Alignment for the starting of code address before code_gen.*/ + GTM64_ONLY(stringpool.free = (unsigned char *)ROUND_UP2((UINTPTR_T)stringpool.free, SECTION_ALIGN_BOUNDARY);) + assert(0 == GTM64_ONLY(PADLEN((UINTPTR_T)stringpool.free, SECTION_ALIGN_BOUNDARY)) + NON_GTM64_ONLY(PADLEN((UINTPTR_T)stringpool.free, SIZEOF(int4)))); + /* Since we know stringpool is aligned atleast at 4-byte boundary, copy both offset and validation + * words with integer assignments instead of copying them by emit_immed(). */ + hdr_offset = indr_base_addr - stringpool.free; /* -ve offset to ihdtyp */ + *(INTPTR_T *)stringpool.free = hdr_offset; + stringpool.free += SIZEOF(hdr_offset); + validation = MAGIC_COOKIE; /* Word to validate we are in right place */ + *(UINTPTR_T *)stringpool.free = validation; + stringpool.free += SIZEOF(validation); + cg_phase = CGP_MACHINE; + IA64_ONLY(generated_code_size = 0); + code_gen(); + IA64_ONLY(assert(calculated_code_size == generated_code_size)); + long_temp = stringpool.free - indr_base_addr; + assert(0 == PADLEN(long_temp, SIZEOF(INTPTR_T))); /* Just to make sure things are aligned for the vartab that follows */ + /* variable table */ + itext->vartab_off = (int4)long_temp; + itext->vartab_len = mvmax; + vptr = (var_tabent*)mcalloc(mvmax * SIZEOF(var_tabent)); + if (mvartab) + walktree(mvartab, ind_cg_var, (char *)&vptr); + else + assert(0 == mvmax); + emit_immed((char *) vptr, mvmax * SIZEOF(var_tabent)); + itext->temp_mvals = sa_temps[TVAL_REF]; + itext->temp_size = sa_temps_offset[TCAD_REF]; + /* indir_code_size may be greater than the actual resultant code size because expression coersion may cause some literals + * to be optimized away, leaving mlitmax greater than actual. + */ + assert(indir_code_size >= stringpool.free - indr_base_addr); + /* Return object code pointers on stack. A later cache_put will move + * the code to its new home and do the necessary cleanup on it. + */ + obj->addr = (char *)indr_base_addr; + obj->len = INTCAST(stringpool.free - indr_base_addr); +} diff --git a/sr_port/indir.h b/sr_port/indir.h new file mode 100644 index 0000000..c6186de --- /dev/null +++ b/sr_port/indir.h @@ -0,0 +1,81 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* The third column represents the opcodes for functions to be used by op_indfun(). + * The one parameter version of $name can probably be folded into op_indfun, but at a later time. + * Note: *** Please add new entries to end of list so as not to cause execution problems for + * compilations from previous versions. Yes, they should have recompiled but we can + * avoid exploding by simply adding entries to end of list. *** + */ +INDIR(indir_fndata, f_data, OC_FNDATA) +,INDIR(indir_fnnext, f_next, OC_FNNEXT) +,INDIR(indir_fnorder1, f_order1, OC_FNORDER) +,INDIR(indir_get, f_get, OC_FNGET) +,INDIR(indir_close, m_close, 0) +,INDIR(indir_hang, m_hang, 0) +,INDIR(indir_if, m_if, 0) +,INDIR(indir_kill, m_kill, 0) +,INDIR(indir_open, m_open, 0) +,INDIR(indir_read, m_read, 0) +,INDIR(indir_set, m_set, 0) +,INDIR(indir_use, m_use, 0) +,INDIR(indir_write, m_write, 0) +,INDIR(indir_xecute, m_xecute, 0) +,INDIR(indir_nref, nref, 0) +,INDIR(indir_lock, m_lock, 0) +,INDIR(indir_do, m_do, 0) +,INDIR(indir_goto, m_goto, 0) +,INDIR(indir_job, m_job, 0) +,INDIR(indir_linetail, linetail, 0) +,INDIR(indir_new, m_new, 0) +,INDIR(indir_zlink, m_zlink, 0) +,INDIR(indir_zbreak, m_zbreak, 0) +,INDIR(indir_zsystem, m_zsystem, 0) +,INDIR(indir_zedit, m_zedit, 0) +,INDIR(indir_zmess, m_zmessage, 0) +,INDIR(indir_zwatch, m_zwatch, 0) +,INDIR(indir_zgoto, m_zgoto, 0) +,INDIR(indir_zprint, m_zprint, 0) +,INDIR(indir_zwrite, m_zwrite, 0) +,INDIR(indir_glvn, indirection, 0) +,INDIR(indir_lvadr, indirection, 0) +,INDIR(indir_pattern, indirection, 0) +,INDIR(indir_iset, indirection, 0) +,INDIR(indir_text, indirection, 0) +,INDIR(indir_view, m_view, 0) +,INDIR(indir_zattach, m_zattach, 0) +,INDIR(indir_zallocate, m_zallocate, 0) +,INDIR(indir_zdeallocate, m_zdeallocate, 0) +,INDIR(indir_lvarg, indirection, 0) +,INDIR(indir_fnzprevious, f_zprevious, OC_FNZPREVIOUS) +,INDIR(indir_fnquery, f_query, OC_FNQUERY) +,INDIR(indir_zhelp, m_zhelp, 0) +,INDIR(indir_zshow, m_zshow, 0) +,INDIR(indir_lvnamadr, indirection, 0) +,INDIR(indir_zwithdraw, m_zwithdraw, 0) +,INDIR(indir_tstart, m_tstart, 0) +,INDIR(indir_fnname, f_name, 0) +,INDIR(indir_fnorder2, f_order, 0) +,INDIR(indir_fnzqgblmod, f_zqgblmod, OC_FNZQGBLMOD) +,INDIR(indir_trollback, m_trollback, 0) +,INDIR(indir_devparms, indirection, 0) +,INDIR(indir_merge, m_merge, 0) +,INDIR(indir_merge1, m_merge, 0) +,INDIR(indir_merge2, m_merge, 0) +,INDIR(indir_fntext, f_text, OC_FNTEXT) +,INDIR(indir_quit, m_quit, 0) +,INDIR(indir_increment, f_incr, 0) +,INDIR(indir_fnzahandle, f_zahandle, OC_FNZAHANDLE) +,INDIR(indir_fnzdata, f_data, OC_FNZDATA) +#ifdef GTM_TRIGGER +,INDIR(indir_ztrigger, m_ztrigger, 0) +#endif +,INDIR(indir_zhalt, m_zhalt, 0) diff --git a/sr_port/indir_enum.h b/sr_port/indir_enum.h new file mode 100644 index 0000000..2dce8fd --- /dev/null +++ b/sr_port/indir_enum.h @@ -0,0 +1,16 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#define INDIR(a,b,c) a +enum indir_enum { +#include "indir.h" +}; +#undef INDIR diff --git a/sr_port/indir_lits.c b/sr_port/indir_lits.c new file mode 100644 index 0000000..7b89b4e --- /dev/null +++ b/sr_port/indir_lits.c @@ -0,0 +1,53 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_string.h" +#include "compiler.h" +#include "mdq.h" +#include "stringpool.h" +#include "rtnhdr.h" +#include "copy.h" +#include "obj_file.h" +#include "cache.h" + +GBLREF mliteral literal_chain; +GBLREF spdesc stringpool, indr_stringpool; + +void indir_lits(ihdtyp *ihead) +{ + ssize_t size, lits_pad_len, hdr_pad_len; + int4 long_temp; + mliteral *lit; + + hdr_pad_len = PADLEN(SIZEOF(ihdtyp), NATIVE_WSIZE); + if (hdr_pad_len) /* additional padding to ihdtyp so that the literal text pool begins at the aligned boundary */ + emit_immed(PADCHARS, (uint4)hdr_pad_len); + size = indr_stringpool.free - indr_stringpool.base; + emit_immed((char *)indr_stringpool.base, (uint4)size); + lits_pad_len = PADLEN(size, NATIVE_WSIZE); /* Length to pad to put instructions on even bdy */ + if (lits_pad_len) + emit_immed(PADCHARS, (uint4)lits_pad_len); /* Pad out with extraneous info */ + size += (SIZEOF(ihdtyp) + hdr_pad_len + lits_pad_len); + ihead->fixup_vals_off = (int4)(ROUND_UP2(size, NATIVE_WSIZE)); + ihead->fixup_vals_num = 0; + dqloop(&literal_chain, que, lit) + { + if (lit->rt_addr < 0) + { + ihead->fixup_vals_num++; + lit->rt_addr = (INTPTR_T)stringpool.free; + lit->v.str.addr = (char *)((lit->v.str.addr - (char *)indr_stringpool.base) + + SIZEOF(ihdtyp) + hdr_pad_len); + emit_immed((char *)&lit->v, SIZEOF(mval)); + } + } +} diff --git a/sr_port/indirection.c b/sr_port/indirection.c new file mode 100644 index 0000000..fae82ee --- /dev/null +++ b/sr_port/indirection.c @@ -0,0 +1,94 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "subscript.h" +#include "advancewindow.h" + +GBLREF char window_token; + +int indirection(oprtype *a) +{ + triple *ref,*next; + oprtype subs[MAX_INDSUBSCRIPTS]; + oprtype x,*sb1,*sb2; + char c; + error_def(ERR_MAXNRSUBSCRIPTS); + error_def(ERR_RPARENMISSING); + error_def(ERR_LPARENMISSING); + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + assert(window_token == TK_ATSIGN); + if (!(TREF(expr_depth))++) + TREF(expr_start) = TREF(expr_start_orig) = NULL; + advancewindow(); + if (!expratom(a)) + { + TREF(expr_depth) = 0; + return FALSE; + } + coerce(a,OCT_MVAL); + ex_tail(a); + if (!(--(TREF(expr_depth)))) + TREF(shift_side_effects) = FALSE; + if (window_token == TK_ATSIGN) + { + advancewindow(); + if (window_token != TK_LPAREN) + { + stx_error(ERR_LPARENMISSING); + return FALSE; + } + ref = maketriple(OC_INDNAME); + sb1 = sb2 = subs; + for (;;) + { + if (sb1 >= ARRAYTOP(subs)) + { + stx_error(ERR_MAXNRSUBSCRIPTS); + return FALSE; + } + advancewindow(); + if (!expr(sb1++)) + return FALSE; + if ((c = window_token) == TK_RPAREN) + { + advancewindow(); + break; + } + if (c != TK_COMMA) + { + stx_error(ERR_RPARENMISSING); + return FALSE; + } + } + /* store argument count...n args plus the name plus the dst*/ + ref->operand[0] = put_ilit((mint)(sb1 - sb2) + 2); + ins_triple(ref); + next = newtriple(OC_PARAMETER); + next->operand[0] = *a; + ref->operand[1] = put_tref(next); + *a = put_tref(ref); + while (sb2 < sb1) + { + ref = newtriple(OC_PARAMETER); + next->operand[1] = put_tref(ref); + ref->operand[0] = *sb2++; + next = ref; + } + } + return TRUE; + +} diff --git a/sr_port/init_root_gv.h b/sr_port/init_root_gv.h new file mode 100644 index 0000000..0784b9d --- /dev/null +++ b/sr_port/init_root_gv.h @@ -0,0 +1,71 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + + +#define INIT_GBL_ROOT() { curr_gbl_root.str.addr = (char *)malloc(SIZEOF(mident_fixed)); } + +#define CREATE_DUMMY_GBLDIR(header, saved, region, map, map_top) { \ + mval v; \ + gd_binding *map_temp; \ +\ + saved = header; \ + header = 0; \ + v.mvtype = MV_STR; \ + header = (gd_addr *)create_dummy_gbldir(); \ + map = header->maps; \ + map_top = map + header->n_maps; \ + map_temp = map + 1; \ + map_temp->reg.addr = region; \ + map_temp = map_temp + 1;\ + map_temp->reg.addr = region; \ + } + +#define GET_SAVED_GDADDR(header, saved, map, region) {\ + header = saved; \ + map = header->maps; \ + map = map + 1; /* get past local locks */ \ + map->reg.addr = region; \ + map = map + 1; \ + map->reg.addr = region; \ +} + +#define RESTORE_ORIGINAL_GDADDR (header, saved) { header = saved; } + + +#define RETRIEVE_ROOT_VAL(gbl_name, root_val, temp_gbl_name, temp_root_val, root_val_len) \ +{ \ + temp_gbl_name = gbl_name; temp_root_val = root_val; \ + for (root_val_len = 0; (*temp_root_val++ = *temp_gbl_name++); root_val_len++); \ + *temp_root_val = '\0'; \ +} + +#define INIT_ROOT_GVT(curr_gbl_root, root_val_len, curr_gbl_root_mval) \ +{ \ + unsigned char *key; \ + gv_namehead *hasht_tree; \ + mname_entry gvent; \ + \ + GTMTRIG_ONLY(if ((HASHT_GBLNAME_LEN != root_val_len) || (0 != memcmp(HASHT_GBLNAME, curr_gbl_root, root_val_len))))\ + { \ + curr_gbl_root_mval.str.len = MIN(root_val_len, MAX_MIDENT_LEN); \ + memcpy(curr_gbl_root_mval.str.addr, curr_gbl_root, curr_gbl_root_mval.str.len); \ + op_gvname(VARLSTCNT(1) &curr_gbl_root_mval); \ + } GTMTRIG_ONLY(else \ + { \ + SETUP_TRIGGER_GLOBAL; \ + key = &gv_currkey->base[0]; \ + memcpy(key, HASHT_GBLNAME, HASHT_GBLNAME_FULL_LEN); \ + key += HASHT_GBLNAME_FULL_LEN; \ + *key++ = '\0'; \ + gv_currkey->end = HASHT_GBLNAME_FULL_LEN; \ + } \ + ) \ +} diff --git a/sr_port/init_secshr_addrs.c b/sr_port/init_secshr_addrs.c new file mode 100644 index 0000000..38d0555 --- /dev/null +++ b/sr_port/init_secshr_addrs.c @@ -0,0 +1,83 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#ifdef VMS +#include /* Required for gtmsource.h */ +#endif + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdscc.h" +#include "gdskill.h" +#include "filestruct.h" +#include "repl_msg.h" +#include "gtmsource.h" +#include "jnl.h" +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" +#include "init_secshr_addrs.h" + +GBLREF gd_addr_fn_ptr get_next_gdr_addrs; +GBLREF cw_set_element *cw_set_addrs; +GBLREF sgm_info **first_sgm_info_addrs; +GBLREF sgm_info **first_tp_si_by_ftok_addrs; +GBLREF unsigned char *cw_depth_addrs; +GBLREF uint4 rundown_process_id; +GBLREF uint4 rundown_image_count; +GBLREF int4 rundown_os_page_size; +GBLREF gd_region **jnlpool_reg_addrs; +GBLREF inctn_opcode_t *inctn_opcode_addrs; +GBLREF inctn_detail_t *inctn_detail_addrs; +GBLREF uint4 *dollar_tlevel_addrs; +GBLREF uint4 *update_trans_addrs; +GBLREF sgmnt_addrs **cs_addrs_addrs; +GBLREF sgmnt_addrs **kip_csa_addrs; +GBLREF boolean_t *need_kip_incr_addrs; +GBLREF trans_num *start_tn_addrs; + +#define DEF_PGSZ 512 + +void init_secshr_addrs(gd_addr_fn_ptr getnxtgdr, cw_set_element *cwsetaddrs, + sgm_info **firstsiaddrs, sgm_info **firstsibyftokaddrs, + unsigned char *cwsetdepthaddrs, uint4 epid, + uint4 icnt, int4 gtmospagesize, gd_region **jpool_reg_address, + inctn_opcode_t *inctn_opcode_address, + inctn_detail_t *inctn_detail_address, uint4 *dollar_tlevel_address, + uint4 *update_trans_address, sgmnt_addrs **cs_addrs_address, + sgmnt_addrs **kip_csa_address, boolean_t *need_kip_incr_address, + trans_num *start_tn_address) +{ + get_next_gdr_addrs = getnxtgdr; + cw_set_addrs = cwsetaddrs; + first_sgm_info_addrs = firstsiaddrs; + first_tp_si_by_ftok_addrs = firstsibyftokaddrs; + cw_depth_addrs = cwsetdepthaddrs; + rundown_process_id = epid; + assert(rundown_process_id); + rundown_image_count = icnt; + rundown_os_page_size = ((0 != gtmospagesize) && ((gtmospagesize / DEF_PGSZ) * DEF_PGSZ) == gtmospagesize) ? gtmospagesize + : DEF_PGSZ; + jnlpool_reg_addrs = jpool_reg_address; + inctn_opcode_addrs = inctn_opcode_address; + inctn_detail_addrs = inctn_detail_address; + dollar_tlevel_addrs = dollar_tlevel_address; + update_trans_addrs = update_trans_address; + cs_addrs_addrs = cs_addrs_address; + kip_csa_addrs = kip_csa_address; + need_kip_incr_addrs = need_kip_incr_address; + start_tn_addrs = start_tn_address; +} diff --git a/sr_port/init_secshr_addrs.h b/sr_port/init_secshr_addrs.h new file mode 100644 index 0000000..084ce18 --- /dev/null +++ b/sr_port/init_secshr_addrs.h @@ -0,0 +1,50 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef INIT_SECSHR_ADDRS_INCLUDED +#define INIT_SECSHR_ADDRS_INCLUDED + +void init_secshr_addrs(gd_addr_fn_ptr getnxtgdr, cw_set_element *cwsetaddrs, + sgm_info **firstsiaddrs, sgm_info **firstsibyftokaddrs, + unsigned char *cwsetdepthaddrs, uint4 epid, + uint4 icnt, int4 gtmospagesize, gd_region **jpool_reg_address, + inctn_opcode_t *inctn_opcode_address, + inctn_detail_t *inctn_detail_address, uint4 *dollar_tlevel_address, + uint4 *update_trans_address, sgmnt_addrs **cs_addrs_address, + sgmnt_addrs **kip_csa_addrs, boolean_t *need_kip_incr_address, + trans_num *start_tn_address); + +#include "dpgbldir.h" /* for "get_next_gdr" used by INVOKE_INIT_SECSHR_ADDRS macro */ + +GBLREF sgm_info *first_sgm_info; /* List of all regions (unsorted) in TP transaction */ +GBLREF sgm_info *first_tp_si_by_ftok; /* List of READ or UPDATED regions sorted on ftok order */ +GBLREF cw_set_element cw_set[]; +GBLREF unsigned char cw_set_depth; +GBLREF uint4 process_id; +GBLREF uint4 image_count; +GBLREF jnlpool_addrs jnlpool; +GBLREF inctn_opcode_t inctn_opcode; +GBLREF inctn_detail_t inctn_detail; /* holds detail to fill in to inctn jnl record */ +GBLREF uint4 dollar_tlevel; +GBLREF uint4 update_trans; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_addrs *kip_csa; +GBLREF boolean_t need_kip_incr; +GBLREF trans_num start_tn; + +#define INVOKE_INIT_SECSHR_ADDRS \ + init_secshr_addrs(get_next_gdr, cw_set, &first_sgm_info, &first_tp_si_by_ftok, \ + &cw_set_depth, process_id, image_count, OS_PAGE_SIZE, \ + &jnlpool.jnlpool_dummy_reg, &inctn_opcode, &inctn_detail, \ + &dollar_tlevel, &update_trans, &cs_addrs, \ + &kip_csa, &need_kip_incr, &start_tn); + +#endif /* INIT_SECSHR_ADDRS_INCLUDED */ diff --git a/sr_port/ins_errtriple.c b/sr_port/ins_errtriple.c new file mode 100644 index 0000000..5ace12a --- /dev/null +++ b/sr_port/ins_errtriple.c @@ -0,0 +1,64 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "compiler.h" +#include "opcode.h" +#include "mdq.h" + +GBLREF int4 pending_errtriplecode; /* if non-zero contains the error code to invoke ins_errtriple with */ +GBLREF triple *curtchain, t_orig; + +void ins_errtriple(int4 in_error) +{ + triple *x, *triptr; + boolean_t add_rterror_triple; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + if (!IS_STX_WARN(in_error) GTMTRIG_ONLY( || TREF(trigger_compile))) + { /* Not a warning and not a trigger, we have a real error (warnings become errors in triggers) */ + if (curtchain != &t_orig) + { /* If working with more than 1 chain defer until back to 1 because dqdelchain cannot delete across + * multiple chains. Set global variable "pending_errtriplecode" and let "setcurtchain" call here again. + */ + if (!pending_errtriplecode) /* Give user only the first error on the line */ + pending_errtriplecode = in_error; /* Save error for later insert */ + return; + } + x = (TREF(pos_in_chain)).exorder.bl; + /* If first error in the current line/cmd, delete all triples and replace them with an OC_RTERROR triple. */ + add_rterror_triple = (OC_RTERROR != x->exorder.fl->opcode); + if (!add_rterror_triple) + { /* This is the second error in this line/cmd. Check for triples added after OC_RTERROR and remove them + * as there could be dangling references amongst them which could later cause GTMASSERT in emit_code. + */ + x = x->exorder.fl; + assert(OC_RTERROR == x->opcode);/* corresponds to newtriple(OC_RTERROR) in previous ins_errtriple */ + x = x->exorder.fl; + assert(OC_ILIT == x->opcode); /* corresponds to put_ilit(in_error) in previous ins_errtriple */ + x = x->exorder.fl; + assert(OC_ILIT == x->opcode); /* corresponds to put_ilit(FALSE) in previous ins_errtriple */ + } + dqdelchain(x, curtchain, exorder); + assert(!add_rterror_triple || ((TREF(pos_in_chain)).exorder.bl->exorder.fl == curtchain)); + assert(!add_rterror_triple || (curtchain->exorder.bl == (TREF(pos_in_chain)).exorder.bl)); + } else + /* For IS_STX_WARN errors (if not compiling a trigger), parsing continues, so dont strip the chain */ + add_rterror_triple = TRUE; + if (add_rterror_triple) + { + triptr = newtriple(OC_RTERROR); + triptr->operand[0] = put_ilit(in_error); + triptr->operand[1] = put_ilit(FALSE); /* not a subroutine reference */ + } +} diff --git a/sr_port/ins_triple.c b/sr_port/ins_triple.c new file mode 100644 index 0000000..275a427 --- /dev/null +++ b/sr_port/ins_triple.c @@ -0,0 +1,29 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "compiler.h" +#include "mdq.h" + +GBLREF triple *curtchain; + +void ins_triple(triple *x) +{ + triple *y; + + /* Need to pass a temporary variable "y" (instead of "curtchain->exorder.bl") to the dqins macro as it will + * otherwise result in incorrect queue insertion. + */ + y = curtchain->exorder.bl; + dqins(y,exorder,x); + CHKTCHAIN(curtchain); +} diff --git a/sr_port/insert_region.c b/sr_port/insert_region.c new file mode 100644 index 0000000..15eef93 --- /dev/null +++ b/sr_port/insert_region.c @@ -0,0 +1,220 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* insert_region.c + * requirement + * reg initialized (gvcst_init) + * parameters + * reg the region to be inserted + * reg_list pointer to the pointer to the list head + * reg_free_list pointer to the pointer to the free list + * size size of the structure of each item in the list + * return + * pointer to the item in the list that is corresponding to the region. + * *reg_list and *reg_free_list are also updated if needed. + * fid_index field in csa and tp_reg_list is maintained by gvcst_init. Maintaining tp_reg_list is + * important, since the regions might be re-sorted in between insert_region() calls (i.e. new + * regions opening). All callers of insert_region except for dse_all() either use tp_reg_list or do not + * have the regions open. dse_all() opens the regions before it calls insert_region(), so maintaining + * fid_index in tp_reg_list is sufficient. + */ + +#include "mdef.h" + +#ifdef VMS +#include +#endif + +#ifdef UNIX +#include "gtm_ipc.h" /* needed for FTOK */ +#endif + +#include "gtm_string.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "gdscc.h" +#include "gdskill.h" +#include "filestruct.h" +#include "jnl.h" +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" +#include "iosp.h" +#include "dbfilop.h" +#include "gtmmsg.h" +#include "is_file_identical.h" +#include "tp_grab_crit.h" +#include "t_retry.h" +#include "wcs_mm_recover.h" +#include "gtmimagename.h" + +GBLREF gd_region *gv_cur_region; +GBLREF uint4 dollar_tlevel; +GBLREF unsigned int t_tries; + +tp_region *insert_region( gd_region *reg, + tp_region **reg_list, + tp_region **reg_free_list, + int4 size) +{ + tp_region *tr, *tr_last, *tr_new; + unique_file_id local_id; +# ifdef VMS + char *local_id_fiptr; + file_control *fc; + uint4 status; + gd_region *temp_reg; +# endif + int4 local_fid_index; + sgmnt_addrs *csa; + int4 match; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + assert(SIZEOF(tp_region) <= size); + assert(!IS_GTM_IMAGE || dollar_tlevel); + if (reg->open) + csa = (sgmnt_addrs *)&FILE_INFO(reg)->s_addrs; +# if defined(VMS) + if (!reg->open) + { + temp_reg = gv_cur_region; + gv_cur_region = reg; + local_id_fiptr = local_id.file_id; + if (!mupfndfil(reg, NULL)) + { + gv_cur_region = temp_reg; + return NULL; + } + FILE_CNTL_INIT_IF_NULL(reg->dyn.addr); + fc = reg->dyn.addr->file_cntl; + fc->file_type = reg->dyn.addr->acc_meth; + fc->op = FC_OPEN; + status = dbfilop(fc); + if (status & 1) + { + local_id_fiptr = &(FILE_INFO(reg)->file_id); + sys$dassgn(FILE_INFO(reg)->fab->fab$l_stv); + } else + { + gtm_putmsg(VARLSTCNT(1) status); + gv_cur_region = temp_reg; + return NULL; + } + gv_cur_region = temp_reg; + } else + local_fid_index = csa->fid_index; +# elif defined(UNIX) + if (!reg->open) + { + if (!mupfndfil(reg, NULL)) + return NULL; + if (!filename_to_id(&local_id.uid, (char *)reg->dyn.addr->fname)) + return NULL; + } else + local_fid_index = csa->fid_index; +# endif + /* See if the region is already on the list or if we have to add it */ + for (tr = *reg_list, tr_last = NULL; NULL != tr; tr = tr->fPtr) + { + if (reg->open) + { /* gvcst_init must have sorted them and filled in the fid_index field of node_local */ + assert(tr->reg->open); + /* note that it is possible that "reg" and "tr->reg" are different although their "fid_index" is the same. + * this is possible if both regions point to the same physical file. + * in this case we return the existing "tr" instead of creating a new one. + */ + if (local_fid_index == tr->file.fid_index) /* Region is found */ + { /* assert we are not in final retry or we are in TP and have crit on the region already */ + assert((CDB_STAGNATE > t_tries) + || (dollar_tlevel && reg->open && csa->now_crit)); + return tr; + } + if ((tr->file.fid_index > local_fid_index)) + break; /* .. we have found our insertion point */ + } else + { + if (reg == tr->reg) /* Region is found */ + { /* assert we are not in final retry or we are in TP and have crit on the region already */ + assert((CDB_STAGNATE > t_tries) + || (dollar_tlevel && reg->open && csa->now_crit)); + return tr; + } + /* let's sort here */ + if (!tr->reg->open) + { /* all regions closed */ + VMS_ONLY(match = memcmp(&(tr->file.file_id), local_id_fiptr, SIZEOF(gd_id))); + UNIX_ONLY(match = gdid_cmp(&(tr->file.file_id), &(local_id.uid))); + } else + { /* the other regions are open, i.e. file is pointing to fid_index, use file_id + * from node_local */ + VMS_ONLY(match = memcmp( + &(((sgmnt_addrs *)&FILE_INFO(tr->reg)->s_addrs)->nl->unique_id.file_id), + local_id_fiptr, SIZEOF(gd_id))); + UNIX_ONLY(match = gdid_cmp( + &(((sgmnt_addrs *)&FILE_INFO(tr->reg)->s_addrs)->nl->unique_id.uid), &(local_id.uid))); + } + if (0 == match) + return tr; + if (0 < match) + break; /* .. we have found our insertion point */ + } + tr_last = tr; + } + if ((NULL != reg_free_list) && (NULL != *reg_free_list)) /* Get a used block off our unused queue */ + { + tr_new = *reg_free_list; /* Get element */ + *reg_free_list = tr_new->fPtr; /* Remove from queue */ + } else /* get a new one */ + { + tr_new = (tp_region *)malloc(size); + if (size > SIZEOF(tp_region)) + memset(tr_new, 0, size); + } + tr_new->reg = reg; /* Add this region to end of list */ + if (!reg->open) + { + VMS_ONLY(memcpy(&(tr_new->file.file_id), local_id_fiptr, SIZEOF(gd_id))); + UNIX_ONLY(tr_new->file.file_id = local_id.uid;) + } else + tr_new->file.fid_index = local_fid_index; + if (NULL == tr_last) + { /* First element on the list */ + tr_new->fPtr = *reg_list; + *reg_list = tr_new; + } else + { /* Insert into list */ + tr_new->fPtr = tr_last->fPtr; + tr_last->fPtr = tr_new; + } + if ((CDB_STAGNATE <= t_tries) && dollar_tlevel && reg->open && !csa->now_crit) + { /* Final retry in TP and this region not locked down. Get crit on it if it is open. + * reg->open needs to be checked above to take care of the case where we do an insert_region() from gvcst_init() + * in the 3rd retry in TP when we have not yet opened the region. In case region is not open, + * tp_restart() (invoked through t_retry from gvcst_init) will open "reg" as well as get crit on it for us. + */ + DEBUG_ONLY(TREF(ok_to_call_wcs_recover) = TRUE;) + if (FALSE == tp_grab_crit(reg)) /* Attempt lockdown now */ + { + DEBUG_ONLY(TREF(ok_to_call_wcs_recover) = FALSE;) + t_retry(cdb_sc_needcrit); /* avoid deadlock -- restart transaction */ + assert(FALSE); /* should not come here as t_retry() does not return */ + } + DEBUG_ONLY(TREF(ok_to_call_wcs_recover) = FALSE;) + assert(csa->now_crit); /* ensure we have crit now */ + CHECK_MM_DBFILEXT_REMAP_IF_NEEDED(csa, reg); + } + DBG_CHECK_TP_REG_LIST_SORTING(*reg_list); + return tr_new; +} diff --git a/sr_port/inst_flush.h b/sr_port/inst_flush.h new file mode 100644 index 0000000..c56b2d3 --- /dev/null +++ b/sr_port/inst_flush.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef INST_FLUSH_INCLUDED +#define INST_FLUSH_INCLUDED + +void inst_flush(void *start, int4 len); + +#endif /* INST_FLUSH_INCLUDED */ diff --git a/sr_port/int_label.c b/sr_port/int_label.c new file mode 100644 index 0000000..4be3a94 --- /dev/null +++ b/sr_port/int_label.c @@ -0,0 +1,32 @@ +/**************************************************************** + * * + * Copyright 2001, 2004 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "compiler.h" +#include "toktyp.h" + +GBLREF char window_token; +GBLREF mval window_mval; +GBLREF mident window_ident; + +void int_label(void) +{ + int len; + + window_token = TK_IDENT; + len = window_mval.str.len; + len = (len < MAX_MIDENT_LEN) ? len: MAX_MIDENT_LEN; + memcpy(window_ident.addr, window_mval.str.addr, len); + window_ident.len = len; +} diff --git a/sr_port/intexpr.c b/sr_port/intexpr.c new file mode 100644 index 0000000..3f6282b --- /dev/null +++ b/sr_port/intexpr.c @@ -0,0 +1,42 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" + +int intexpr(oprtype *a) +{ + + triple *triptr; + int4 rval; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + if (!(TREF(expr_depth))++) + TREF(expr_start) = TREF(expr_start_orig) = NULL; + if (!(rval = eval_expr(a))) + { + TREF(expr_depth) = 0; + return FALSE; + } + coerce(a,OCT_MINT); + ex_tail(a); + if (!(--(TREF(expr_depth)))) + TREF(shift_side_effects) = FALSE; + if (TREF(expr_start) != TREF(expr_start_orig)) + { + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(TREF(expr_start)); + } + return rval; + +} diff --git a/sr_port/io.h b/sr_port/io.h new file mode 100644 index 0000000..d26f0ac --- /dev/null +++ b/sr_port/io.h @@ -0,0 +1,410 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef IO_H +#define IO_H + +#ifdef USING_ICONV +#define _OSF_SOURCE +#include +#undef _OSF_SOURCE +#else /* no iconv.h - must define size_t on VMS platform */ +#ifdef VMS +#include +#endif +#endif + +#include "gt_timer.h" +#include "rtnhdr.h" +#include "stack_frame.h" +#include "mv_stent.h" +#ifdef __MVS__ +#include "gtm_zos_io.h" +#endif + +#define INSERT TRUE +#define NO_INSERT FALSE +#define IO_SEQ_WRT 1 +#define IO_RD_ONLY 2 +#define ESC_LEN 16 +#define MAX_DEVCTL_LENGTH 256 +#define IO_ESC 0x1b +#define MAX_DEV_TYPE_LEN 7 + +#define CHAR_FILTER 128 +#define ESC1 1 +#define ESC2 2 +#define ESC_MASK (ESC1+ESC2) + +#define START 0 +#define AFTESC 1 +#define SEQ1 2 +#define SEQ2 3 +#define SEQ3 4 +#define SEQ4 5 +#define FINI 6 +#define BADESC 7 + +#define DEFAULT_IOD_LENGTH 55 +#define DEFAULT_IOD_WIDTH 80 +#define DEFAULT_IOD_WRAP TRUE + +#define BADCHAR_DEVICE_MSG "BADCHAR error raised on input" +#define UNAVAILABLE_DEVICE_MSG "Resource temporarily unavailable" + +typedef unsigned char params; + +/* + * The enum nl below conflicts with curses.h on AIX. At some point + * These names should be expanded to less generic identifiers + * to avoid conflicts with prototype header files. + */ +enum io_dev_type +{ tt, /* terminal */ + mt, /* mag tape */ + rm, /* rms */ + us, /* user device driver */ + mb, /* mail box */ + nl, /* null device */ + ff, /* fifo device */ + tcp, /* TCP socket */ + gtmsocket, /* socket device, socket is already used by sys/socket.h */ +#ifdef UNIX + pi, /* pipe */ +#endif + n_io_dev_types /* terminator */ +}; + +enum io_dev_state +{ dev_never_opened, + dev_closed, + dev_open, + n_io_dev_states +}; + +#ifdef VMS +enum code_set_type +{ + ascii, + ebcdic +}; +#endif + +typedef struct +{ + struct io_desc_struct *in; + struct io_desc_struct *out; +}io_pair; + + +typedef struct io_desc_struct +{ + io_pair pair; + struct io_log_name_struct *trans_name; + struct io_log_name_struct *name; + mstr error_handler; + unsigned int length; + unsigned int width; + bool perm; /* permanent */ + bool wrap; /* if FALSE trunc */ + enum io_dev_type type; + enum io_dev_state state; + struct + { unsigned int x; + unsigned int y; + unsigned short zeof; + unsigned short za; + unsigned char zb[ESC_LEN]; + }dollar; + unsigned char esc_state; + void *dev_sp; + struct dev_dispatch_struct *disp_ptr; +#if defined(KEEP_zOS_EBCDIC) || defined(VMS) + iconv_t input_conv_cd; + iconv_t output_conv_cd; + enum code_set_type in_code_set; + enum code_set_type out_code_set; +#endif + boolean_t newly_created; +#ifdef __MVS__ + gtm_chset_t file_chset; /* from file tag */ + gtm_chset_t process_chset; /* how to do conversion */ + unsigned int file_tag; + boolean_t text_flag; + boolean_t is_ichset_default; + boolean_t is_ochset_default; +#endif + gtm_chset_t ichset; + gtm_chset_t ochset; + int4 write_filter; +} io_desc; + +/* + * ICHSET: UTF-16 + * First READ: BOM + * Transition to UTF-16BE or UTF-16LE based on BOM + * + * ICHSET: UTF-16 + * First READ: Not BOM + * Transition to UTF-16BE per Unicode standard + * + * ICHSET: UTF-16LE (or UTF-16BE) + * First READ: BOM or not BOM + * Do nothing, assume input is in specified endian format. Pass input to application (if BOM present, it is treated as ZWNBS) + * + * OCHSET: UTF-16 + * First WRITE: Transition to UTF-16BE, write BOM + * + * OCHSET: UTF-16LE (or UTF-16BE) + * First WRITE: Do not WRITE BOM. All output in specified endian format + */ + +typedef struct io_log_name_struct +{ + io_desc *iod; /* io descriptor */ + struct io_log_name_struct + *next; /* linked list */ + unsigned char len; /* name length */ + char dollar_io[1]; /* _$IO hidden variable */ +}io_log_name; + +io_log_name *get_log_name(mstr *v, bool insert); + +/* wttab is not used in the IO dispatch, but used in the user defined dispatch for ious*. Even though all the entries are NULL in + * the IO dispatch table are NULL in the IO dispatch tables, they have to remain. */ +typedef struct dev_dispatch_struct +{ + short (*open)(io_log_name *, mval *, int, mval *, int4); + void (*close)(io_desc *, mval *); + void (*use)(io_desc *, mval *); + int (*read)(mval *, int4); + int (*rdone)(mint *, int4); + void (*write)(mstr *); + void (*wtone)(int); + void (*wteol)(int4, io_desc *); + void (*wtff)(void); + void (*wttab)(int4); + void (*flush)(io_desc *); + int (*readfl)(mval *, int4, int4); + void (*iocontrol)(mstr *); + void (*dlr_device)(mstr *); + void (*dlr_key)(mstr *); +} dev_dispatch_struct; + +/* io_ prototypes */ +void io_rundown(int rundown_type); +void io_init(bool term_ctrl); +bool io_is_rm(mstr *name); +bool io_is_sn(mstr *tn); +struct mv_stent_struct *io_find_mvstent(io_desc *io_ptr, boolean_t clear_mvstent); +#ifdef UNIX +bool io_is_tt(char *name); +#endif +bool io_open_try(io_log_name *naml, io_log_name *tl, mval *pp, int4 timeout, mval *mspace); +enum io_dev_type io_type(mstr *tn); +void io_init_name(void); + +#define ioxx_open(X) short io##X##_open(io_log_name *dev_name, mval *pp, int fd, mval *mspace, int4 timeout) +#define ioxx_dummy(X) short io##X##_dummy(io_log_name *dev_name, mval *pp, int fd, mval *mspace, int4 timeout) +#define ioxx_close(X) void io##X##_close(io_desc *iod, mval *pp) +#define ioxx_use(X) void io##X##_use(io_desc *iod, mval *pp) +#define ioxx_read(X) int io##X##_read(mval *v, int4 t) +#define ioxx_rdone(X) int io##X##_rdone (mint *v, int4 timeout) +#define ioxx_write(X) void io##X##_write(mstr *v) +#define ioxx_wtone(X) void io##X##_wtone(int c) +#define ioxx_wteol(X) void io##X##_wteol(int4 cnt, io_desc *iod) +#define ioxx_wtff(X) void io##X##_wtff(void) +#define ioxx_wttab(X) void io##X##_wttab(int4 x) +#define ioxx_flush(X) void io##X##_flush(io_desc *iod) +#define ioxx_readfl(X) int io##X##_readfl(mval *v, int4 width, int4 timeout) +#define xx_iocontrol(X) void X##_iocontrol(mstr *d) +#define xx_dlr_device(X) void X##_dlr_device(mstr *d) +#define xx_dlr_key(X) void X##_dlr_key(mstr *d) + +/* Following definitions have a pattern that most of the routines follow. Only exceptions are: + * 1. ioff_open() is an extra routine + * 2. iopi_open() is an extra routine on unix + * 3. iopi_iocontrol() is an extra routine on unix to handle write /writeof + */ + +#define ioxx(X) ioxx_##X(tt); ioxx_##X(mt); ioxx_##X(rm); VMS_ONLY(ioxx_##X(mb);) ioxx_##X(nl); \ + ioxx_##X(us); ioxx_##X(tcp); ioxx_##X(socket) +#define xxdlr(X) xx_iocontrol(X); xx_dlr_device(X); xx_dlr_key(X) + +/* prototypes for dispatch functions */ + +ioxx(open); +ioxx(close); +ioxx(rdone); +ioxx(use); +ioxx(read); +ioxx(readfl); +ioxx(write); +ioxx(wtone); +ioxx(wteol); +ioxx(wtff); +ioxx(dummy); +ioxx(flush); +xxdlr(nil); +xxdlr(ious); +xxdlr(iotcp); +xxdlr(iosocket); +ioxx_open(ff); +#ifdef UNIX +ioxx_open(pi); +xxdlr(iopi); /* we need iopi_iocontrol(), iopi_dlr_device() and iopi_dlr_key() */ +#endif +ioxx_wttab(us); +/* iott_ prototypes */ +uchar_ptr_t iott_escape(uchar_ptr_t strin, uchar_ptr_t strtop, io_desc *io_ptr); + +/* iomt_ prototypes */ +void iomt_getrec(io_desc *dv); +void iomt_rdstream(uint4 len, void *str, io_desc *dv); +int iomt_readblk(io_desc *dv); +void iomt_vlflush(io_desc *dv); +void iomt_wrtblk(io_desc *dv); +int iomt_wrtinit(io_desc *dv); +void iomt_wtansilab(io_desc *dv, uint4 labs); +uint4 iomt_reopen(io_desc *dv, unsigned short mode, int rewind); +void iomt_closesp(int4 channel); +void iomt_eof(io_desc *dev); +void iomt_erase(io_desc *dev); +void iomt_qio(io_desc *iod, uint4 mask, uint4 parm); +void iomt_rddoslab(io_desc *dv); +void iomt_rdansiend(io_desc *dv); +void iomt_rdansistart(io_desc *dv); +void iomt_rewind(io_desc *dev); +void iomt_skipfile(io_desc *dev, int count); +void iomt_skiprecord(io_desc *dev, int count); +void iomt_tm(io_desc *dev); +void iomt_wtdoslab(io_desc *dv); + + +/* iosocket_ prototypes */ +boolean_t iosocket_listen(io_desc *iod, unsigned short len); +boolean_t iosocket_wait(io_desc *iod, int4 timepar); +void iosocket_poolinit(void); + +/* iotcp_ prototypes */ +char *iotcp_name2ip(char *name); +int iotcp_fillroutine(void); +int iotcp_getlsock(io_log_name *dev); +void iotcp_rmlsock(io_desc *iod); + +/* tcp_ prototypes */ +int tcp_open(char *host, unsigned short port, int4 timeout, boolean_t passive); + +/* iomb_ prototypes */ +#ifdef VMS +int iomb_dataread (int timeout); +#endif + +bool same_device_check(mstr tname, char *buf); + +#define iotype(O,X,Y) \ +{ \ + O##_open, X##_close, X##_use, X##_read, X##_rdone, X##_write, \ + X##_wtone, X##_wteol, X##_wtff, NULL, X##_flush, X##_readfl, \ + Y##_iocontrol, Y##_dlr_device, Y##_dlr_key \ +} +#define ionil_dev \ +{ \ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL \ +} + +#ifdef __sparc +int outc(char ch); +#else +int outc(int ch); +#endif + +void get_dlr_device(mval *v); +void get_dlr_key(mval *v); + + +void flush_pio(void); + +void remove_rms(io_desc *ciod); + +dev_dispatch_struct *io_get_fgn_driver(mstr *s); + +#define MAX_CHSET_NAME 64 +#define TAB_BUF_SZ 128 + +LITREF unsigned char spaces_block[]; + +#if defined(KEEP_zOS_EBCDIC) || defined(VMS) + +LITREF unsigned char ebcdic_spaces_block[]; + +#define SPACES_BLOCK ((ascii != io_curr_device.out->out_code_set) ? \ + ebcdic_spaces_block : spaces_block) +#define RM_SPACES_BLOCK ((ascii != iod->out_code_set) ? \ + ebcdic_spaces_block : spaces_block) + + +#ifdef __MVS__ + +#define SET_CODE_SET(CODE_SET, CODE_SET_STR) \ +{ \ + if (!strcmp(CODE_SET_STR, OUTSIDE_CH_SET)) \ + CODE_SET = ebcdic; \ + else \ + CODE_SET = ascii; \ +} +#else + +#define SET_CODE_SET(CODE_SET, CODE_SET_STR) + +#endif /* __MVS__ */ + +#else /* !KEEP_zOS_EBCDIC && !VMS*/ + +#define SPACES_BLOCK spaces_block +#define RM_SPACES_BLOCK spaces_block + +#endif /* KEEP_zOS_EBCDIC || VMS */ + +#define ICONV_OPEN_CD(DESC_CD, CODE_SRC, CODE_TARGET) \ +{ \ + if (!strcmp(CODE_TARGET, CODE_SRC)) \ + DESC_CD = NO_XLAT; \ + else if (!strcmp(CODE_TARGET, "ISO8859-1")) \ + DESC_CD = EBCDIC_TO_ASCII; \ + else \ + DESC_CD = ASCII_TO_EBCDIC; \ +} + +#define ICONV_CLOSE_CD(DESC_CD) (DESC_CD = NO_XLAT) + +#define ICONVERT(CD, SRC, IN_LEN_PTR, DEST, OUT_LEN_PTR) \ +{ \ + if (EBCDIC_TO_ASCII == CD) \ + ebc_to_asc(*(DEST), *(SRC), *(IN_LEN_PTR)); \ + else if (ASCII_TO_EBCDIC == CD) \ + asc_to_ebc(*(DEST), *(SRC), *(IN_LEN_PTR)); \ +} + +#define SET_ENCODING(CHSET, CHSET_MSTR) \ +{ \ + int chset_idx; \ + error_def(ERR_BADCHSET); \ + \ + chset_idx = verify_chset(CHSET_MSTR); \ + ; \ + if (0 <= chset_idx) \ + (CHSET) = (gtm_chset_t)chset_idx; \ + else \ + rts_error(VARLSTCNT(4) ERR_BADCHSET, 2, (CHSET_MSTR)->len, (CHSET_MSTR)->addr); \ +} + +#endif /* IO_H */ diff --git a/sr_port/io_dev_dispatch.h b/sr_port/io_dev_dispatch.h new file mode 100644 index 0000000..0892642 --- /dev/null +++ b/sr_port/io_dev_dispatch.h @@ -0,0 +1,44 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* mupip_io_dev_dispatch.h is a subset of this file which includes those needed only by MUPIP + * so, if you need to make a change here, please keep the other one in sync. + * + * VMS can have addresses in literal constants while most Unix platforms cannot + */ + +UNIX_ONLY(GBLDEF) VMS_ONLY(LITDEF) dev_dispatch_struct io_dev_dispatch[] = +{ + iotype(iott, iott, nil), + iotype(iomt, iomt, nil), +# ifdef UNIX + iotype(iorm, iorm, iopi), +# else + iotype(iorm, iorm, nil), +# endif + iotype(ious, ious, ious), +# ifdef UNIX + ionil_dev, +# else + iotype(iomb, iomb, nil), +# endif + iotype(ionl, ionl, nil), +# ifdef UNIX + iotype(ioff, iorm, iopi), +# else + iotype(ioff, iorm, nil), +# endif + iotype(iotcp, iotcp, iotcp), + iotype(iosocket, iosocket, iosocket) +# ifdef UNIX + ,iotype(iopi, iorm, iopi) +# endif +}; diff --git a/sr_port/io_find_mvstent.c b/sr_port/io_find_mvstent.c new file mode 100644 index 0000000..1495f47 --- /dev/null +++ b/sr_port/io_find_mvstent.c @@ -0,0 +1,57 @@ +/**************************************************************** + * * + * Copyright 2007, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gt_timer.h" +#include "io.h" +#include "iotimer.h" +#include "iotcpdef.h" +#include "iosocketdef.h" +#include "rtnhdr.h" +#include "mv_stent.h" +#include "stack_frame.h" + +GBLREF mv_stent *mv_chain; +GBLREF stack_frame *frame_pointer; +GBLREF unsigned char *stackbase, *stacktop, *msp, *stackwarn; + +/* Find and optionally clear the mv_stent keeping interrupted device information for us */ +mv_stent *io_find_mvstent(io_desc *io_ptr, boolean_t clear_mvstent) +{ + mv_stent *mvc, *mv_zintdev; + + assert(msp <= stackbase && msp > stacktop); + assert(mv_chain <= (mv_stent *)stackbase && mv_chain > (mv_stent *)stacktop); + assert(frame_pointer <= (stack_frame *)stackbase && frame_pointer > (stack_frame *)stacktop); + mv_zintdev = NULL; + for (mvc = mv_chain; mvc < (mv_stent *)frame_pointer ; mvc = (mv_stent *)(mvc->mv_st_next + (char *)mvc)) + { + if (MVST_ZINTDEV == mvc->mv_st_type && io_ptr == mvc->mv_st_cont.mvs_zintdev.io_ptr) + { + mv_zintdev = mvc; + break; + } + if (!mvc->mv_st_next) + GTMASSERT; + } + if (mv_zintdev && clear_mvstent) + { + if (mv_chain == mv_zintdev) + POP_MV_STENT(); /* just pop if top of stack */ + else + { + mv_zintdev->mv_st_cont.mvs_zintdev.io_ptr = NULL; + mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid = FALSE; + } + } + return mv_zintdev; +} diff --git a/sr_port/io_init.c b/sr_port/io_init.c new file mode 100644 index 0000000..0c60c15 --- /dev/null +++ b/sr_port/io_init.c @@ -0,0 +1,186 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "io.h" +#include "iosp.h" +#include "io_params.h" +#include "error.h" +#include "op.h" +#include "term_setup.h" +#include "trans_log_name.h" +/***************** GLOBAL DATA FOR THE MUMPS IO SYSTEM *******************/ + +GBLREF io_pair io_curr_device; /* current device */ +GBLREF io_pair io_std_device; /* standard device */ +GBLREF io_log_name *dollar_principal; /* pointer to log name GTM$PRINCIPAL if defined */ +GBLREF bool prin_in_dev_failure; +GBLREF bool prin_out_dev_failure; +GBLREF int (*op_open_ptr)(mval *v, mval *p, int t, mval *mspace); + +GBLREF io_log_name *io_root_log_name; /* root of linked list */ +GBLREF mstr sys_input; +GBLREF mstr sys_output; +GBLREF mstr gtm_principal; + + +/***************** END OF GLOBAL DATA ***************************************/ + + +void io_init(bool term_ctrl) +{ + static readonly unsigned char open_params_list[2] = + { + (unsigned char)iop_newversion, + (unsigned char)iop_eol + }; + static readonly unsigned char null_params_list[2] = + { + (unsigned char)iop_nl, + (unsigned char)iop_eol + }; + static readonly unsigned char no_params = (unsigned char)iop_eol; + static readonly unsigned char shr_params[3] = + { + (unsigned char)iop_shared, + (unsigned char)iop_readonly, + (unsigned char)iop_eol + }; + + int4 status; + mval val; + mstr tn; + MSTR_CONST (gtm_netout, "GTM_NETOUT"); + MSTR_CONST (sys_net, "SYS$NET"); + char buf1[MAX_TRANS_NAME_LEN]; /* buffer to hold translated name */ + mval pars; + io_log_name *inp, *outp; + io_log_name *ln; + + error_def(ERR_LOGTOOLONG); + + io_init_name(); + /* default logical names */ + io_root_log_name = (io_log_name *)malloc(SIZEOF(*io_root_log_name)); + memset(io_root_log_name, 0, SIZEOF(*io_root_log_name)); + val.mvtype = MV_STR; + val.str.addr = "0"; + val.str.len = 1; + ln = get_log_name(&val.str, INSERT); + assert(ln != 0); + val.str = gtm_principal; + status = TRANS_LOG_NAME(&val.str, &tn, buf1, SIZEOF(buf1), dont_sendmsg_on_log2long); + if (SS_NOLOGNAM == status) + dollar_principal = 0; + else if (SS_NORMAL == status) + dollar_principal = get_log_name(&tn, INSERT); +# ifdef UNIX + else if (SS_LOG2LONG == status) + rts_error(VARLSTCNT(5) ERR_LOGTOOLONG, 3, val.str.len, val.str.addr, SIZEOF(buf1) - 1); +# endif + else + rts_error(VARLSTCNT(1) status); + + /* open devices */ + val.str = sys_input; + inp = get_log_name(&val.str, INSERT); + pars.mvtype = MV_STR; + status = TRANS_LOG_NAME(&val.str, &tn, buf1, SIZEOF(buf1), dont_sendmsg_on_log2long); + if (SS_NOLOGNAM == status) + { + pars.str.len = SIZEOF(null_params_list); + pars.str.addr = (char *)null_params_list; + } else if (SS_NORMAL == status) + { + if (!io_is_rm(&val.str)) + { + pars.str.len = SIZEOF(no_params); + pars.str.addr = (char *)&no_params; + } else if (io_is_sn(&val.str)) + { + pars.str.len = SIZEOF(open_params_list); + pars.str.addr = (char *)open_params_list; + } else + { + pars.str.len = SIZEOF(shr_params); + pars.str.addr = (char *)shr_params; + } + } +# ifdef UNIX + else if (SS_LOG2LONG == status) + rts_error(VARLSTCNT(5) ERR_LOGTOOLONG, 3, val.str.len, val.str.addr, SIZEOF(buf1) - 1); +# endif + else + rts_error(VARLSTCNT(1) status); + ESTABLISH(io_init_ch); + (*op_open_ptr)(&val, &pars, 0, 0); + io_curr_device.in = io_std_device.in = inp->iod; + val.str = sys_output; + if ((SS_NORMAL == TRANS_LOG_NAME(>m_netout, &tn, buf1, SIZEOF(buf1), do_sendmsg_on_log2long)) + && (SS_NORMAL == TRANS_LOG_NAME(&sys_net, &tn, buf1, SIZEOF(buf1), do_sendmsg_on_log2long)) + && io_is_sn(&sys_net)) + val.str = sys_net; + outp = get_log_name(&val.str, INSERT); + status = TRANS_LOG_NAME(&val.str, &tn, buf1, SIZEOF(buf1), dont_sendmsg_on_log2long); + if ((SS_NORMAL != status) && (SS_NOLOGNAM != status)) + { +# ifdef UNIX + if (SS_LOG2LONG == status) + rts_error(VARLSTCNT(5) ERR_LOGTOOLONG, 3, val.str.len, val.str.addr, SIZEOF(buf1) - 1); + else +# endif + rts_error(VARLSTCNT(1) status); + } + if ((val.str.addr == sys_net.addr) && (pars.str.addr == (char *)open_params_list)) + /* sys$net is the only input thing that uses open_params_list */ + outp->iod = io_curr_device.in; + /* For terminals and mailboxes and sockets, SYS$INPUT and SYS$OUTPUT may point to + the same device. If input is one of those, then check translated + name for output against translated name for input; + in that case they should be joined by their logical names */ + if (((tt == io_curr_device.in->type) || (mb == io_curr_device.in->type) || + (gtmsocket == io_curr_device.in->type)) + && same_device_check(tn, buf1)) + outp->iod = io_curr_device.in; + if (!outp->iod) + { + if (status == SS_NOLOGNAM) + { + pars.str.len = SIZEOF(null_params_list); + pars.str.addr = (char *)null_params_list; + } else if (status == SS_NORMAL) + { + pars.str.len = SIZEOF(open_params_list); + pars.str.addr = (char *)open_params_list; + } + (*op_open_ptr)(&val, &pars, 0, 0); + } + io_curr_device.out = io_std_device.out = outp->iod; + term_setup(term_ctrl); + io_std_device.out->pair = io_std_device; + io_std_device.in->pair = io_std_device; + io_std_device.out->perm = io_std_device.in->perm = TRUE; + for (ln = io_root_log_name; ln; ln = ln->next) + ln->iod = io_std_device.in; + + if (dollar_principal) + dollar_principal->iod = io_std_device.in; + pars.str.len = SIZEOF(no_params); + pars.str.addr = (char *)&no_params; + val.str.len = io_curr_device.in->trans_name->len; + val.str.addr = io_std_device.in->trans_name->dollar_io; + op_use(&val, &pars); + REVERT; + return; +} diff --git a/sr_port/io_init_ch.c b/sr_port/io_init_ch.c new file mode 100644 index 0000000..9004225 --- /dev/null +++ b/sr_port/io_init_ch.c @@ -0,0 +1,40 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "io.h" +#include "iottdef.h" +#include "error.h" +#include "setterm.h" +#include "util.h" + +GBLREF io_log_name *io_root_log_name; + +CONDITION_HANDLER(io_init_ch) +{ + io_log_name *iol; + + START_CH; + if (INFO == SEVERITY) + { + PRN_ERROR; + CONTINUE; + } + for (iol = io_root_log_name; 0 != iol; iol = iol->next) + { + if (iol->iod && (iol->iod->type == tt) && iol->iod->dev_sp) + resetterm(iol->iod); + } + NEXTCH; +} diff --git a/sr_port/io_params.h b/sr_port/io_params.h new file mode 100644 index 0000000..a0d592b --- /dev/null +++ b/sr_port/io_params.h @@ -0,0 +1,39 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#define MAXDEVPARLEN 1024 +#define IOP_VAR_SIZE 255 + +#define IOP_OPEN_OK 1 +#define IOP_USE_OK 2 +#define IOP_CLOSE_OK 4 + +#define IOP_SRC_INT 1 /* source is integer */ +#define IOP_SRC_STR 2 /* source is string */ +#define IOP_SRC_MSK 3 /* source is character mask */ +#define IOP_SRC_PRO 4 /* source is protection mask */ +#define IOP_SRC_LNGMSK 5 /* source is int4 character mask */ +#define IOP_SRC_TIME 6 /* source is the date-time string */ + +typedef struct +{ + unsigned char valid_with; + unsigned char source_type; +} dev_ctl_struct; + +#define IOP_DESC(a,b,c,d,e) b + +enum io_params +{ +#include "iop.h" +}; + +#undef IOP_DESC diff --git a/sr_port/io_rundown.c b/sr_port/io_rundown.c new file mode 100644 index 0000000..2a5e544 --- /dev/null +++ b/sr_port/io_rundown.c @@ -0,0 +1,67 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "io.h" +#include "iosp.h" +#include "io_params.h" +#include "error.h" + +GBLREF io_log_name *io_root_log_name; + +GBLREF io_pair io_std_device; +GBLREF bool prin_in_dev_failure; +GBLREF bool prin_out_dev_failure; + +void io_dev_close (io_log_name *d); + +void io_rundown (int rundown_type) +{ + io_log_name *l; /* logical name pointer */ + + if (io_root_log_name == 0) + return; + for (l = io_root_log_name; l != 0; l = io_root_log_name) + { + io_root_log_name = l->next; + if (l->iod != 0) + { + if ((NORMAL_RUNDOWN == rundown_type) || + ((RUNDOWN_EXCEPT_STD == rundown_type) && + ((l->iod->pair.in != io_std_device.in) && (l->iod->pair.out != io_std_device.out)))) + io_dev_close(l); + } + } +} + + +void io_dev_close (io_log_name *d) +{ + static readonly unsigned char p[] = { iop_eol}; + mval pp; + + if (d->iod->pair.in == io_std_device.in && d->iod->pair.out == io_std_device.out) + { + if (prin_in_dev_failure || prin_out_dev_failure) + return; + } + + ESTABLISH(lastchance3); + pp.mvtype = MV_STR; + pp.str.addr = (char *) p; + pp.str.len = SIZEOF(p); + if (d->iod->pair.in && d->iod->pair.in->state == dev_open) + (d->iod->pair.in->disp_ptr->close)(d->iod->pair.in, &pp); + if (d->iod->pair.out && d->iod->pair.out->state == dev_open) + (d->iod->pair.out->disp_ptr->close)(d->iod->pair.out, &pp); + REVERT; +} diff --git a/sr_port/iomt_ansilab_manager.c b/sr_port/iomt_ansilab_manager.c new file mode 100644 index 0000000..e2477dd --- /dev/null +++ b/sr_port/iomt_ansilab_manager.c @@ -0,0 +1,349 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "io.h" +#include "iosp.h" +#include "iottdef.h" +#include "iomtdef.h" +#include "movtc.h" + +static readonly char vol1_lab[] = "VOL1MUMPS1 3"; +static readonly char hdr1_lab[] = "HDR1MUMPS.SRC MUMPS100010001000100 00000 00000 000000GTCMUMPS "; +static readonly char hdr2_lab[] = " 00 "; +static readonly char eof1_lab[] = "EOF1MUMPS.SRC MUMPS100010001000100 00000 00000 000000GTCMUMPS "; + +LITREF unsigned char LIB_AB_ASC_EBC[]; +LITREF unsigned char LIB_AB_EBC_ASC[]; + +void iomt_wtansilab(io_desc *dv, uint4 labs) +{ + iosb io_status_blk; + uint4 status, status1, mask; + unsigned char *outcp, buff[ANSI_LAB_LENGTH], *ptr, asc_buf[12]; + d_mt_struct *mt_ptr; + error_def(ERR_MTIS); + + mt_ptr = (d_mt_struct *) dv->dev_sp; + mask = 0; +#ifdef UNIX + if (mt_ptr->mode != MT_M_WRITE) + { + uint4 status; + status = iomt_reopen (dv, MT_M_WRITE, FALSE); + } +#endif + if (labs & MTL_VOL1) + { outcp = (unsigned char*) vol1_lab; + if (mt_ptr->ebcdic) + { movtc(ANSI_LAB_LENGTH, outcp, LIB_AB_ASC_EBC, buff); + status = iomt_wtlblk(mt_ptr->access_id, mask, &io_status_blk, + buff, ANSI_LAB_LENGTH); + } + else + { status = iomt_wtlblk(mt_ptr->access_id, mask, &io_status_blk, + outcp, ANSI_LAB_LENGTH); + } + if (status != SS_NORMAL || (status1 = io_status_blk.status) != SS_NORMAL) + { if ( status1 == SS_ENDOFTAPE) + { + dv->dollar.za = 1; + } + else + { + dv->dollar.za = 9; + } + rts_error(VARLSTCNT(4) ERR_MTIS, 2, dv->trans_name->len, dv->trans_name->dollar_io); + } + else + dv->dollar.za = 0; + } + if (labs & MTL_HDR1) + { outcp = (unsigned char *) hdr1_lab; + mask = 0; + if (mt_ptr->ebcdic) + { movtc(ANSI_LAB_LENGTH, outcp, LIB_AB_ASC_EBC, buff); + status = iomt_wtlblk(mt_ptr->access_id, mask, &io_status_blk, + buff, ANSI_LAB_LENGTH); + } + else + { status = iomt_wtlblk(mt_ptr->access_id, mask, &io_status_blk, + outcp, ANSI_LAB_LENGTH); + } + if (status != SS_NORMAL || (status1 = io_status_blk.status) != SS_NORMAL) + { if ( status1 == SS_ENDOFTAPE) + { + dv->dollar.za = 1; + } + else + { + dv->dollar.za = 9; + } + rts_error(VARLSTCNT(4) ERR_MTIS, 2, dv->trans_name->len, dv->trans_name->dollar_io); + } + else + dv->dollar.za = 0; + } + if (labs & MTL_HDR2) + { outcp = buff; + memcpy(outcp, mt_ptr->fixed ? "HDR2F" : "HDR2D" , 5); + *(outcp+5) = '0'; + i2asc(asc_buf,mt_ptr->block_sz); + *(outcp+6) = asc_buf[0]; + *(outcp+7) = asc_buf[1]; + *(outcp+8) = asc_buf[2]; + *(outcp+9) = asc_buf[3]; + *(outcp+10) = '0'; + i2asc(asc_buf,mt_ptr->record_sz); + *(outcp+11) = asc_buf[0]; + *(outcp+12) = asc_buf[1]; + *(outcp+13) = asc_buf[2]; + *(outcp+14) = asc_buf[3]; + memcpy(outcp+15, hdr2_lab, SIZEOF(hdr2_lab) - 1); + mask = 0; + if (mt_ptr->ebcdic) + { movtc(ANSI_LAB_LENGTH, outcp, LIB_AB_ASC_EBC, buff); + status = iomt_wtlblk(mt_ptr->access_id, mask, &io_status_blk, + buff, ANSI_LAB_LENGTH); + } + else + { status = iomt_wtlblk(mt_ptr->access_id, mask, &io_status_blk, + outcp, ANSI_LAB_LENGTH); + } + if (status != SS_NORMAL || (status1 = io_status_blk.status) != SS_NORMAL) + { if ( status1 == SS_ENDOFTAPE) + { + dv->dollar.za = 1; + } + else + { + dv->dollar.za = 9; + } + rts_error(VARLSTCNT(4) ERR_MTIS, 2, dv->trans_name->len, dv->trans_name->dollar_io); + } + else + dv->dollar.za = 0; + } + if (labs & MTL_EOF1) + { outcp = (unsigned char *) eof1_lab; + mask = 0; + if (mt_ptr->ebcdic) + { movtc(ANSI_LAB_LENGTH, outcp, LIB_AB_ASC_EBC, buff); + status = iomt_wtlblk(mt_ptr->access_id, mask, &io_status_blk, + buff, ANSI_LAB_LENGTH); + } + else + { status = iomt_wtlblk(mt_ptr->access_id, mask, &io_status_blk, + outcp, ANSI_LAB_LENGTH); + } + if (status != SS_NORMAL || (status1 = io_status_blk.status) != SS_NORMAL) + { if (status1 == SS_ENDOFTAPE) + { + dv->dollar.za = 1; + } + else + { + dv->dollar.za = 9; + } + rts_error(VARLSTCNT(4) ERR_MTIS, 2, dv->trans_name->len, dv->trans_name->dollar_io); + } + else + dv->dollar.za = 0; + } + if (labs & MTL_EOF2) + { outcp = buff; + memcpy(outcp, mt_ptr->fixed ? "EOF2F" : "EOF2D" , 5); + *(outcp+5) = '0'; + i2asc(asc_buf,mt_ptr->block_sz); + *(outcp+6) = asc_buf[0]; + *(outcp+7) = asc_buf[1]; + *(outcp+8) = asc_buf[2]; + *(outcp+9) = asc_buf[3]; + *(outcp+10) = '0'; + i2asc(asc_buf,mt_ptr->record_sz); + *(outcp+11) = asc_buf[0]; + *(outcp+12) = asc_buf[1]; + *(outcp+13) = asc_buf[2]; + *(outcp+14) = asc_buf[3]; + memcpy(outcp+15, hdr2_lab, SIZEOF(hdr2_lab) - 1); + if (mt_ptr->ebcdic) + { movtc(ANSI_LAB_LENGTH, outcp, LIB_AB_ASC_EBC, buff); + status = iomt_wtlblk(mt_ptr->access_id, mask, &io_status_blk, + buff, ANSI_LAB_LENGTH); + } + else + { status = iomt_wtlblk(mt_ptr->access_id, mask, &io_status_blk, + outcp, ANSI_LAB_LENGTH); + } + if (status != SS_NORMAL || (status1 = io_status_blk.status) != SS_NORMAL) + { if (status1 == SS_ENDOFTAPE) + { + dv->dollar.za = 1; + } + else + { + dv->dollar.za = 9; + } + rts_error(VARLSTCNT(4) ERR_MTIS, 2, dv->trans_name->len, dv->trans_name->dollar_io); + } + else + dv->dollar.za = 0; + } + return; +} + +void iomt_rdansistart(io_desc *dv) +{ + uint4 status, status1, mask; + iosb io_status_blk; + d_mt_struct *mt_ptr; + int inlen, i; + unsigned char *incp, *ptr; + error_def (ERR_MTIS); + error_def(ERR_MTANSILAB); + + mt_ptr = (d_mt_struct *) dv->dev_sp; + inlen = ANSI_LAB_LENGTH; + incp = (unsigned char*) malloc(mt_ptr->block_sz); + for (i = 0; i < 4; i++) + { io_status_blk.status = 0; + mask = 0; + status = iomt_rdlblk(mt_ptr, mask, &io_status_blk, + incp, mt_ptr->block_sz); + if (status != SS_NORMAL || (status1 = io_status_blk.status) != SS_NORMAL) + { if ( status1 == SS_ENDOFTAPE) + { + dv->dollar.za = 1; + } + else + { + dv->dollar.za = 9; + } + free(incp); + if (status == SS_NORMAL && status1 == SS_ENDOFFILE) + return; + rts_error(VARLSTCNT(4) ERR_MTIS, 2, dv->trans_name->len, dv->trans_name->dollar_io); + } + else + dv->dollar.za = 0; + + if (mt_ptr->ebcdic) + movtc(ANSI_LAB_LENGTH, incp, LIB_AB_EBC_ASC, incp); + + switch(i) + { + case 0: + if (io_status_blk.char_ct != ANSI_LAB_LENGTH || memcmp(incp, vol1_lab, 4)) + { + dv->dollar.za = 9; + free (incp); + rts_error(VARLSTCNT(6) ERR_MTANSILAB, 0, ERR_MTIS, 2, dv->trans_name->len, + dv->trans_name->dollar_io); + } + break; + case 1: + if (io_status_blk.char_ct != ANSI_LAB_LENGTH || memcmp(incp, hdr1_lab, 4)) + { + dv->dollar.za = 9; + free (incp); + rts_error(VARLSTCNT(6) ERR_MTANSILAB, 0, ERR_MTIS, 2, dv->trans_name->len, + dv->trans_name->dollar_io); + } + break; + case 2: + if (!(io_status_blk.char_ct != ANSI_LAB_LENGTH || memcmp(incp, "HDR2", 4))) + { ptr = incp + 4; + if (*ptr == 'D' || *ptr == 'F') + { break; + } + } + dv->dollar.za = 9; + free (incp); + rts_error(VARLSTCNT(6) ERR_MTANSILAB, 0, ERR_MTIS, 2, dv->trans_name->len, dv->trans_name->dollar_io); + default: + dv->dollar.za = 9; + free (incp); + rts_error(VARLSTCNT(6) ERR_MTANSILAB, 0, ERR_MTIS, 2, dv->trans_name->len, dv->trans_name->dollar_io); + } + } +} + +void iomt_rdansiend(io_desc *dv) +{ + uint4 status, status1, mask; + iosb io_status_blk; + int inlen, i; + d_mt_struct *mt_ptr; + unsigned char *incp, *ptr; + error_def (ERR_MTIS); + error_def(ERR_MTANSILAB); + + mt_ptr = (d_mt_struct *) dv->dev_sp; + inlen = ANSI_LAB_LENGTH; + incp = (unsigned char *) malloc(mt_ptr->block_sz); + for (i = 0; i < 3; i++) + { io_status_blk.status = 0; + mask = 0; + status = iomt_rdlblk(mt_ptr, mask, &io_status_blk, + incp, mt_ptr->block_sz); + if (status != SS_NORMAL || (status1 = io_status_blk.status) != SS_NORMAL) + { if (status1 == SS_ENDOFTAPE) + { + dv->dollar.za = 1; + } + else + { + dv->dollar.za = 9; + } + free (incp); + if (status == SS_NORMAL && (status1 == SS_ENDOFFILE)) + return; + rts_error(VARLSTCNT(4) ERR_MTIS, 2, dv->trans_name->len, dv->trans_name->dollar_io); + } + else + dv->dollar.za = 0; + + if (mt_ptr->ebcdic) + movtc(ANSI_LAB_LENGTH, incp, LIB_AB_EBC_ASC, incp); + + switch(i) + { + case 0: + if (io_status_blk.char_ct != ANSI_LAB_LENGTH || memcmp(incp, eof1_lab, 4)) + { + dv->dollar.za = 9; + free (incp); + rts_error(VARLSTCNT(6) ERR_MTANSILAB, 0, ERR_MTIS, 2, dv->trans_name->len, + dv->trans_name->dollar_io); + } + break; + case 1: + if (!(io_status_blk.char_ct != ANSI_LAB_LENGTH || memcmp(incp, "EOF2", 4))) + { ptr = incp + 4; + if (*ptr == 'D' || *ptr == 'F') + { break; + } + } + dv->dollar.za = 9; + free (incp); + rts_error(VARLSTCNT(6) ERR_MTANSILAB, 0, ERR_MTIS, 2, dv->trans_name->len, dv->trans_name->dollar_io); + default: + dv->dollar.za = 9; + free (incp); + rts_error(VARLSTCNT(6) ERR_MTANSILAB, 0, ERR_MTIS, 2, dv->trans_name->len, dv->trans_name->dollar_io); + } + } + free (incp); + return; +} diff --git a/sr_port/iomt_close.c b/sr_port/iomt_close.c new file mode 100644 index 0000000..989ed4e --- /dev/null +++ b/sr_port/iomt_close.c @@ -0,0 +1,127 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" +#include "io.h" +#include "iottdef.h" +#include "iomtdef.h" +#include "io_params.h" +#include "stringpool.h" +#include "copy.h" + +LITREF unsigned char io_params_size[]; + +void iomt_close(io_desc *dv, mval *pp) +{ + unsigned char ch; + d_mt_struct *mt_ptr; + int p_offset; + int4 skips; + + error_def(ERR_UNIMPLOP); + + p_offset = 0; + mt_ptr = (d_mt_struct *)dv->dev_sp; +#ifdef DP + FPRINTF(stderr, ">> iomt_close\n"); +#endif + if (dv->state == dev_open) + { + iomt_flush(dv); + while (*(pp->str.addr + p_offset) != iop_eol) + { + switch (ch = *(pp->str.addr + p_offset++)) + { + case iop_exception: + dv->error_handler.len = *(pp->str.addr + p_offset); + dv->error_handler.addr = (char *)(pp->str.addr + p_offset + 1); + s2pool(&dv->error_handler); + break; + case iop_skipfile: + GET_LONG(skips, (pp->str.addr + p_offset)); + iomt_skipfile(dv, skips); + break; + case iop_unload: +#ifdef UNIX + rts_error(VARLSTCNT(1) ERR_UNIMPLOP); +#else + assert(FALSE); +#endif + break; + case iop_rewind: + iomt_rewind(dv); + break; + case iop_erasetape: + iomt_erase(dv); + break; + case iop_space: + GET_LONG(skips, (pp->str.addr + p_offset)); + iomt_skiprecord(dv, skips); + break; + case iop_writeof: + iomt_eof(dv); + break; + default: + break; + } + p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ? + (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]); + } + if (mt_ptr->labeled == MTLAB_ANSI) + { + if (mt_ptr->last_op == mt_write) + { + iomt_tm(dv); + iomt_wtansilab(dv, MTL_EOF1 | MTL_EOF2); + iomt_tm(dv); + iomt_tm(dv); + } + } else + { + if (mt_ptr->last_op == mt_write) + iomt_eof(dv); + +#ifdef UNIX + if (mt_ptr->cap.req_extra_filemark + && mt_ptr->last_op == mt_eof) +#else + if (mt_ptr->last_op == mt_eof) +#endif + { + iomt_eof(dv); + iomt_skipfile(dv, -1); + } + } + if (mt_ptr->buffer) + { + /* + * If bufftoggle is zero, then there is one buffer. + * Otherwise, there are two buffers. If bufftoggle is + * less than zero, then the buffer pointer points at + * the second buffer, and we must adjust the pointer so + * that we get to the beginning of the data which has + * been malloc'ed. + */ + if (mt_ptr->bufftoggle < 0) + free(mt_ptr->buffer + mt_ptr->bufftoggle); + else + free(mt_ptr->buffer); + } +#ifdef DP + FPRINTF(stderr, "<< iomt_close\n"); +#endif + iomt_closesp(mt_ptr->access_id); + dv->state = dev_closed; + } + return; +} diff --git a/sr_port/iomt_dummy.c b/sr_port/iomt_dummy.c new file mode 100644 index 0000000..cff50e0 --- /dev/null +++ b/sr_port/iomt_dummy.c @@ -0,0 +1,18 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" + +short iomt_dummy(io_log_name *dev_name, mval *pp, int fd, mval *mspace, int4 timeout) +{ + return 0; +} diff --git a/sr_port/iomt_eof.c b/sr_port/iomt_eof.c new file mode 100644 index 0000000..4209f07 --- /dev/null +++ b/sr_port/iomt_eof.c @@ -0,0 +1,30 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iomt_eof.c - Write an EOF marker to tape. */ +#include "mdef.h" +#include "io.h" +#include "iottdef.h" +#include "iomtdef.h" +#include "io_params.h" + +void +iomt_eof (io_desc *dev) +{ + d_mt_struct *mt_ptr; + + iomt_flush (dev); + iomt_qio (dev, IO_WRITEOF, 0); + mt_ptr = (d_mt_struct *) dev->dev_sp; + mt_ptr->last_op = ((mt_ptr->last_op == mt_eof || mt_ptr->last_op == mt_eof2) + ? mt_eof2 : mt_eof); + return; +} diff --git a/sr_port/iomt_erase.c b/sr_port/iomt_erase.c new file mode 100644 index 0000000..32e58ab --- /dev/null +++ b/sr_port/iomt_erase.c @@ -0,0 +1,31 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iomt_erase.c - Erase tape. */ +#include "mdef.h" +#include "io.h" +#include "iottdef.h" +#include "iomtdef.h" + +void +iomt_erase (io_desc *dev) +{ + static readonly int4 mask = IO_WRITELBLK | IO_M_ERASE; + + d_mt_struct *mt_ptr; + + iomt_flush (dev); + iomt_rewind (dev); + iomt_qio (dev, mask, 0); + mt_ptr = (d_mt_struct *) dev->dev_sp; + mt_ptr->last_op = mt_rewind; + return; +} diff --git a/sr_port/iomt_flush.c b/sr_port/iomt_flush.c new file mode 100644 index 0000000..032ef8d --- /dev/null +++ b/sr_port/iomt_flush.c @@ -0,0 +1,52 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "io.h" +#include "iottdef.h" +#include "iomtdef.h" + +void +iomt_flush (io_desc *dv) +{ + unsigned char *cp; + d_mt_struct *mt_ptr; + + mt_ptr = (d_mt_struct *) dv->dev_sp; + if (mt_ptr->last_op == mt_write) + { + if (mt_ptr->rec.len) + { + iomt_wteol (1, dv); + assert (mt_ptr->rec.len == 0); + } + if (mt_ptr->stream) + cp = mt_ptr->buffptr; + else + cp = (unsigned char *) mt_ptr->rec.addr; + + if (!mt_ptr->fixed && !mt_ptr->stream) + cp -= MT_RECHDRSIZ; + if (cp > mt_ptr->buffer) + { + if (cp >= mt_ptr->bufftop) + assert (cp == mt_ptr->bufftop); + else + memset (cp, (mt_ptr->stream ? 0 : '^'), mt_ptr->bufftop - cp); + iomt_wrtblk (dv); + iomt_wrtinit (dv); + } + } + return; +} diff --git a/sr_port/iomt_getrec.c b/sr_port/iomt_getrec.c new file mode 100644 index 0000000..a780d34 --- /dev/null +++ b/sr_port/iomt_getrec.c @@ -0,0 +1,74 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" +#include "iottdef.h" +#include "iomtdef.h" + +GBLREF io_pair io_curr_device; + +void iomt_getrec (io_desc *dv) +{ +#ifdef __MVS__ +#pragma convlit(suspend) +#endif + error_def (ERR_MTRDTHENWRT); + error_def (ERR_MTRECTOOBIG); + error_def (ERR_MTRECTOOSM); + unsigned char *cp, *cx, fill_char; + d_mt_struct *mt_ptr; + int x, ln; + + mt_ptr = (d_mt_struct *) dv->dev_sp; + cp = mt_ptr->buffptr; + if (!mt_ptr->stream) + fill_char = '^'; + ln = (int)(mt_ptr->bufftop - cp); + if (ln < 0) + rts_error (VARLSTCNT (1) ERR_MTRECTOOBIG); + else if (ln == 0) + { + iomt_readblk (dv); + cp = mt_ptr->buffer; + ln = (int)(mt_ptr->bufftop - cp); + } else if (*cp == fill_char) + { + if (!mt_ptr->fixed || !skpc (fill_char, ln, (char *)cp)) + { + iomt_readblk (dv); + cp = mt_ptr->buffer; + ln = (int)(mt_ptr->bufftop - cp); + } + } + if (mt_ptr->fixed) + { + mt_ptr->rec.len = (ln < mt_ptr->record_sz) ? ln : mt_ptr->record_sz; + } else if (!mt_ptr->stream) + { + x = *cp++ - '0'; + x = x * 10 + (*cp++ - '0'); + x = x * 10 + (*cp++ - '0'); + x = x * 10 + (*cp++ - '0') - MT_RECHDRSIZ; + if (x < 0) + rts_error (VARLSTCNT (1) ERR_MTRECTOOSM); + if (x > mt_ptr->record_sz || x > ln) + rts_error (VARLSTCNT (1) ERR_MTRECTOOBIG); + mt_ptr->rec.len = x; + } + mt_ptr->rec.addr = (char *) cp; + mt_ptr->buffptr = (unsigned char *) (mt_ptr->rec.addr + mt_ptr->rec.len); + mt_ptr->last_op = mt_read; + return; +#ifdef __MVS__ +#pragma convlit(resume) +#endif +} diff --git a/sr_port/iomt_rddoslab.c b/sr_port/iomt_rddoslab.c new file mode 100644 index 0000000..6d75000 --- /dev/null +++ b/sr_port/iomt_rddoslab.c @@ -0,0 +1,66 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gtm_stdio.h" + +#include "io.h" +#include "iosp.h" +#include "iottdef.h" +#include "iomtdef.h" + +void +iomt_rddoslab (io_desc *dv) +{ + static readonly unsigned char label[] = {149, 84, 248, 102, 79, 192, 1, 1, 155, 0, 0, 0, 0, 0}; + uint4 status, status1; + iosb io_status_blk; + d_mt_struct *mt_ptr; + error_def (ERR_MTIS); + error_def (ERR_MTDOSLAB); + int inlen; + unsigned char *incp; + + inlen = sizeof (label); + mt_ptr = (d_mt_struct *) dv->dev_sp; + incp = (unsigned char *) malloc (mt_ptr->block_sz); + io_status_blk.status = 0; + +#ifdef UNIX + if (mt_ptr->mode != MT_M_READ) + { + status = iomt_reopen (dv, MT_M_READ, FALSE); + } +#endif + status = iomt_rdlblk (mt_ptr, IO_READLBLK, &io_status_blk, + incp, mt_ptr->block_sz); + if (status != SS_NORMAL || (status1 = io_status_blk.status) != SS_NORMAL) + { + if (status1 == SS_ENDOFTAPE) + dv->dollar.za = 1; + else + dv->dollar.za = 9; + free (incp); + rts_error (VARLSTCNT (4) ERR_MTIS, 2, dv->trans_name->len, dv->trans_name->dollar_io); + } else + dv->dollar.za = 0; + + if (io_status_blk.char_ct != sizeof (label) || memcmp (incp, label, sizeof (label))) + { + dv->dollar.za = 9; + free (incp); + rts_error (VARLSTCNT (6) ERR_MTDOSLAB, 0, ERR_MTIS, 2, dv->trans_name->len, dv->trans_name->dollar_io); + } + free (incp); + return; +} diff --git a/sr_port/iomt_rdone.c b/sr_port/iomt_rdone.c new file mode 100644 index 0000000..e42961f --- /dev/null +++ b/sr_port/iomt_rdone.c @@ -0,0 +1,101 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" +#include "iottdef.h" +#include "iomtdef.h" +#include "iosp.h" + +GBLREF io_pair io_curr_device; + +int iomt_rdone(mint *v, int4 t) +{ + error_def(ERR_MTRDTHENWRT); + unsigned char x; + io_desc *dv; + d_mt_struct *mt_ptr; + + dv = io_curr_device.in; + mt_ptr = (d_mt_struct *)dv->dev_sp; + +#ifdef UNIX + if (mt_ptr->mode != MT_M_READ) + { + uint4 status; + + status = iomt_reopen(dv, MT_M_READ, FALSE); + if (status != SS_NORMAL) + rts_error(VARLSTCNT (1) status); + } +#endif + switch (mt_ptr->last_op) + { + case mt_rewind: + if (mt_ptr->labeled == MTLAB_DOS11) + iomt_rddoslab(dv); + else if (mt_ptr->labeled == MTLAB_ANSI) + iomt_rdansistart (dv); + /* CAUTION: FALL THROUGH */ + case mt_null: + if (iomt_readblk(dv)) + { + if (!mt_ptr->stream) + iomt_getrec(dv); + } + break; + case mt_read: + break; + default: + rts_error(VARLSTCNT (1) ERR_MTRDTHENWRT); + } + if (io_curr_device.in->dollar.zeof) + { + *v = -1; + return TRUE; + } + if (mt_ptr->stream) + { + if (mt_ptr->buffptr >= mt_ptr->bufftop) + { + iomt_readblk (dv); + if (io_curr_device.in->dollar.zeof) + { + *v = -1; + return TRUE; + } + } + iomt_rdstream(1, &x, dv); + if (io_curr_device.in->dollar.zeof) + *v = -1; + else if (mt_ptr->rec.len == 0) + { + *v = 11; + io_curr_device.in->dollar.x = 0; + io_curr_device.in->dollar.y++; + } else + *v = (unsigned int)x; + } else + { + if (mt_ptr->rec.len == 0) + { + *v = 13; + io_curr_device.in->dollar.x = 0; + io_curr_device.in->dollar.y++; + mt_ptr->last_op = mt_null; + return TRUE; + } + *v = (unsigned int)(*mt_ptr->rec.addr++); + mt_ptr->rec.len--; + } + mt_ptr->last_op = mt_read; + return TRUE; +} diff --git a/sr_port/iomt_rdstream.c b/sr_port/iomt_rdstream.c new file mode 100644 index 0000000..666ebee --- /dev/null +++ b/sr_port/iomt_rdstream.c @@ -0,0 +1,68 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" +#include "iottdef.h" +#include "iomtdef.h" + +GBLREF io_pair io_curr_device; + +#define NL 0 + +void iomt_rdstream (uint4 len, void *str, io_desc *dv) +{ + unsigned char *cp, *cx; + uint4 maxreclen; + bool endrec; + d_mt_struct *mt_ptr; + + mt_ptr = (d_mt_struct *) dv->dev_sp; + maxreclen = (len < mt_ptr->record_sz) ? len : mt_ptr->record_sz; + cp = cx = (unsigned char *)str; + endrec = FALSE; + + while (!endrec) + { + for (; mt_ptr->buffptr < mt_ptr->bufftop && cx - cp < maxreclen;) + { + switch (*mt_ptr->buffptr) + { + case NATIVE_CR: + case NL: + mt_ptr->buffptr++; + continue; + case NATIVE_VT: + case NATIVE_FF: + *cx++ = *mt_ptr->buffptr; + /* CAUTION : FALL-THROUGH */ + case NATIVE_LF: + mt_ptr->buffptr++; + endrec = TRUE; + break; + default: + *cx++ = *mt_ptr->buffptr++; + continue; + } + break; + } + if (mt_ptr->buffptr >= mt_ptr->bufftop && !endrec) + { + iomt_readblk (dv); + if (dv->dollar.zeof) + endrec = TRUE; + } else + endrec = TRUE; + } + mt_ptr->rec.addr = (char *) cp; + mt_ptr->rec.len = INTCAST(cx - cp); + mt_ptr->last_op = mt_read; +} diff --git a/sr_port/iomt_read.c b/sr_port/iomt_read.c new file mode 100644 index 0000000..52c9b78 --- /dev/null +++ b/sr_port/iomt_read.c @@ -0,0 +1,97 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gtm_stdio.h" +#include "io.h" +#include "iottdef.h" +#include "iomtdef.h" +#include "stringpool.h" +#include "iosp.h" + +GBLREF spdesc stringpool; +GBLREF io_pair io_curr_device; + +int iomt_read(mval *v, int4 t) +{ + error_def(ERR_MTRDTHENWRT); + io_desc *dv; + d_mt_struct *mt_ptr; + +#ifdef DP + FPRINTF(stderr, ">> iomt_read\n"); +#endif + + assert(stringpool.free >= stringpool.base); + assert(stringpool.free <= stringpool.top); + dv = io_curr_device.in; + mt_ptr = (d_mt_struct *) dv->dev_sp; + ENSURE_STP_FREE_SPACE(mt_ptr->record_sz); + v->str.addr = (char *) stringpool.free; + +#ifdef UNIX + if (mt_ptr->mode != MT_M_READ) + { + uint4 status; + + status = iomt_reopen(dv, MT_M_READ, FALSE); + if (status != SS_NORMAL) + return (status); + } +#endif + switch (mt_ptr->last_op) + { + case mt_rewind: + if (mt_ptr->labeled == MTLAB_DOS11) + iomt_rddoslab (dv); + else if (mt_ptr->labeled == MTLAB_ANSI) + iomt_rdansistart (dv); + /* CAUTION: FALL THROUGH */ + case mt_null: + if (iomt_readblk(dv)) + { + if (mt_ptr->stream) + iomt_rdstream(mt_ptr->record_sz, v->str.addr, dv); + else + iomt_getrec(dv); + } + break; + case mt_read: + if (mt_ptr->stream) + iomt_rdstream(mt_ptr->record_sz, v->str.addr, dv); + else + iomt_getrec(dv); + break; + default: + rts_error(VARLSTCNT (1) ERR_MTRDTHENWRT); + } + if (io_curr_device.in->dollar.zeof) + { + v->str.len = 0; + return TRUE; + } + if (!mt_ptr->stream) + memcpy(v->str.addr, mt_ptr->rec.addr, mt_ptr->rec.len); + v->str.len = mt_ptr->rec.len; + io_curr_device.in->dollar.x = 0; + io_curr_device.in->dollar.y++; + mt_ptr->last_op = (mt_ptr->buffptr >= mt_ptr->bufftop) + ? mt_null : mt_read; + +#ifdef DP + FPRINTF(stderr, "<< iomt_read\n"); +#endif + + return TRUE; +} diff --git a/sr_port/iomt_readblk.c b/sr_port/iomt_readblk.c new file mode 100644 index 0000000..1605f31 --- /dev/null +++ b/sr_port/iomt_readblk.c @@ -0,0 +1,97 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_stdio.h" +#include "io_params.h" +#include "io.h" +#include "iosp.h" +#include "iottdef.h" +#include "iomtdef.h" +#include "movtc.h" + +LITREF unsigned char LIB_AB_EBC_ASC[]; + +int iomt_readblk (io_desc *dv) +{ + int4 status; + unsigned short ct; + iosb io_status_blk; + d_mt_struct *mt_ptr; + error_def (ERR_IOEOF); + error_def (ERR_MTRDBADBLK); + + mt_ptr = (d_mt_struct *) dv->dev_sp; + +#ifdef DP + FPRINTF(stderr, ">> iomt_read_blk\n"); +#endif + +#ifdef UNIX + if (mt_ptr->mode != MT_M_READ) + { + status = iomt_reopen (dv, MT_M_READ, FALSE); + if (status != SS_NORMAL) + { + return (status); + } + } +#endif + status = iomt_rdlblk (mt_ptr, mt_ptr->read_mask, + &io_status_blk, mt_ptr->buffer, + mt_ptr->block_sz); + if (status != SS_NORMAL) + rts_error (VARLSTCNT (1) status); + switch (io_status_blk.status) + { + case SS_NORMAL: + /****************** + * This test is nooped ... if not, it would raise an error when the input block size did not + * match the declared block size. + * if (io_status_blk.char_ct != mt_ptr->block_sz) + * rts_error(VARLSTCNT(4) ERR_MTRDBADBLK,2,io_status_blk.char_ct, mt_ptr->block_sz); + ******************/ + mt_ptr->bufftop = mt_ptr->buffer + io_status_blk.char_ct; + dv->dollar.za = 0; + break; + case SS_ENDOFFILE: + if (mt_ptr->labeled == MTLAB_ANSI && !dv->dollar.zeof) + iomt_rdansiend (dv); + dv->dollar.zeof = TRUE; + dv->dollar.za = 0; + if (dv->error_handler.len > 0) + rts_error (VARLSTCNT (1) ERR_IOEOF); + return FALSE; + case SS_ENDOFTAPE: + dv->dollar.za = 1; + rts_error (VARLSTCNT (1) io_status_blk.status); + default: + dv->dollar.za = 9; + rts_error (VARLSTCNT (1) io_status_blk.status); + } + if (dv->dollar.zeof) + { + dv->dollar.zeof = FALSE; + dv->dollar.x = 0; + dv->dollar.y = 0; + } + if (mt_ptr->ebcdic) + movtc (mt_ptr->block_sz, mt_ptr->buffer, LIB_AB_EBC_ASC, mt_ptr->buffer); + if (!dv->dollar.za) + mt_ptr->buffptr = mt_ptr->buffer; + + +#ifdef DP + FPRINTF(stderr, "<< iomt_readblk\n"); +#endif + + return TRUE; +} diff --git a/sr_port/iomt_readfl.c b/sr_port/iomt_readfl.c new file mode 100644 index 0000000..a529110 --- /dev/null +++ b/sr_port/iomt_readfl.c @@ -0,0 +1,101 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gtm_stdio.h" + +#include "io.h" +#include "iottdef.h" +#include "iomtdef.h" +#include "iosp.h" + +GBLREF io_pair io_curr_device; + +int iomt_readfl(mval *v, int4 l, int4 t) +{ + error_def (ERR_MTRDTHENWRT); + io_desc *dv; + d_mt_struct *mt_ptr; + + dv = io_curr_device.in; + mt_ptr = (d_mt_struct *) dv->dev_sp; + +#ifdef UNIX + if (mt_ptr->mode != MT_M_READ) + { + unsigned short status; + + status = iomt_reopen(dv, MT_M_READ, FALSE); + if (status != SS_NORMAL) + return (status); + } +#endif + switch (mt_ptr->last_op) + { + case mt_rewind: + if (mt_ptr->labeled == MTLAB_DOS11) + iomt_rddoslab(dv); + else if (mt_ptr->labeled == MTLAB_ANSI) + iomt_rdansistart(dv); + /* CAUTION: FALL THROUGH */ + case mt_null: + if (iomt_readblk(dv)) + { + if (mt_ptr->stream) + iomt_rdstream(l, v->str.addr, dv); + else + iomt_getrec(dv); + } + break; + case mt_read: + if (mt_ptr->stream) + iomt_rdstream(l, v->str.addr, dv); + break; + default: + rts_error(VARLSTCNT (1) ERR_MTRDTHENWRT); + } + if (io_curr_device.in->dollar.zeof) + { + v->str.len = 0; + return TRUE; + } + if (mt_ptr->stream) + { + v->str.len = mt_ptr->rec.len; + mt_ptr->last_op = (mt_ptr->buffptr >= mt_ptr->bufftop) + ? mt_null : mt_read; + } else + { + if (mt_ptr->rec.len <= l) + { + memcpy(v->str.addr, mt_ptr->rec.addr, mt_ptr->rec.len); + v->str.len = mt_ptr->rec.len; + io_curr_device.in->dollar.x = 0; + io_curr_device.in->dollar.y++; + mt_ptr->last_op = (mt_ptr->buffptr >= mt_ptr->bufftop) + ? mt_null : mt_read; + mt_ptr->rec.len = 0; + } else + { + memcpy(v->str.addr, mt_ptr->rec.addr, l); + v->str.len = l; + mt_ptr->rec.addr += l; + mt_ptr->rec.len -= l; + io_curr_device.in->dollar.x += l; + mt_ptr->last_op = mt_read; + } + if (mt_ptr->rec.len <= 0 && mt_ptr->last_op == mt_read) + iomt_getrec(dv); + } + return TRUE; +} diff --git a/sr_port/iomt_rewind.c b/sr_port/iomt_rewind.c new file mode 100644 index 0000000..4355f1c --- /dev/null +++ b/sr_port/iomt_rewind.c @@ -0,0 +1,55 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" +#include "iottdef.h" +#include "iomtdef.h" +#include "io_params.h" + +void +iomt_rewind (io_desc *dev) +{ + d_mt_struct *mt_ptr; + + mt_ptr = (d_mt_struct *) dev->dev_sp; + if (mt_ptr->labeled == MTLAB_ANSI) + { + if (mt_ptr->last_op == mt_write) + { + iomt_tm (dev); + iomt_wtansilab (dev, MTL_EOF1 | MTL_EOF2); + iomt_tm (dev); + iomt_tm (dev); + } + } else + { + if (mt_ptr->last_op == mt_write) + { + iomt_flush (dev); + iomt_eof (dev); + } +#ifdef VMS + if (mt_ptr->last_op == mt_eof) + iomt_eof (dev); +#else /* check to see if this device requires an extra filemark */ + if (mt_ptr->cap.req_extra_filemark + && mt_ptr->last_op == mt_eof) + iomt_eof (dev); +#endif + } + iomt_qio (dev, IO_REWIND, 0); + mt_ptr->last_op = mt_rewind; + dev->dollar.zeof = FALSE; + dev->dollar.x = 0; + dev->dollar.y = 0; + return; +} diff --git a/sr_port/iomt_skipfile.c b/sr_port/iomt_skipfile.c new file mode 100644 index 0000000..74d04ae --- /dev/null +++ b/sr_port/iomt_skipfile.c @@ -0,0 +1,59 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" +#include "iottdef.h" +#include "iomtdef.h" +#include "io_params.h" + +void +iomt_skipfile (io_desc *dev, int count) +{ + d_mt_struct *mt_ptr; + error_def (ERR_MTRDTHENWRT); + + mt_ptr = (d_mt_struct *) dev->dev_sp; + if (mt_ptr->last_op == mt_write) + { + if (count > 0) + rts_error (VARLSTCNT (1) ERR_MTRDTHENWRT); + iomt_flush (dev); + iomt_eof (dev); +#ifdef UNIX + if (mt_ptr->cap.req_extra_filemark) + { + iomt_eof (dev); + count -= 2; + } + else +#endif + count--; + + } else if (mt_ptr->last_op == mt_eof) + { + if (count > 0) + rts_error (VARLSTCNT (1) ERR_MTRDTHENWRT); +#ifdef UNIX + if (mt_ptr->cap.req_extra_filemark) + { + iomt_eof (dev); + count -= 1; + } +#endif + } else if (mt_ptr->last_op == mt_eof2 && count > 0) + { + rts_error (VARLSTCNT (1) ERR_MTRDTHENWRT); + } + iomt_qio (dev, IO_SKIPFILE, count); + mt_ptr->last_op = mt_null; + return; +} diff --git a/sr_port/iomt_skiprecord.c b/sr_port/iomt_skiprecord.c new file mode 100644 index 0000000..e978682 --- /dev/null +++ b/sr_port/iomt_skiprecord.c @@ -0,0 +1,61 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" +#include "iottdef.h" +#include "iomtdef.h" +#include "io_params.h" + +void +iomt_skiprecord (io_desc *dev, int count) +{ + d_mt_struct *mt_ptr; + error_def (ERR_MTRDTHENWRT); + + mt_ptr = (d_mt_struct *) dev->dev_sp; + if (mt_ptr->last_op == mt_write) + { + if (count > 0) + rts_error (VARLSTCNT (1) ERR_MTRDTHENWRT); + iomt_flush (dev); + iomt_eof (dev); + +#ifdef UNIX + if (mt_ptr->cap.req_extra_filemark) + { + iomt_eof (dev); + iomt_qio (dev, IO_SKIPFILE, (unsigned int)-2); + } + else +#endif + iomt_qio (dev, IO_SKIPFILE, (unsigned int)-1); + + } else if (mt_ptr->last_op == mt_eof) + { + if (count > 0) + rts_error (VARLSTCNT (1) ERR_MTRDTHENWRT); + +#ifdef UNIX + if (mt_ptr->cap.req_extra_filemark) + { + iomt_eof (dev); + iomt_qio (dev, IO_SKIPFILE, (unsigned int)-1); + } +#endif + } else if (mt_ptr->last_op == mt_eof2 && count > 0) + { + rts_error (VARLSTCNT (1) ERR_MTRDTHENWRT); + } + iomt_qio (dev, IO_SKIPRECORD, count); + mt_ptr->last_op = mt_null; + return; +} diff --git a/sr_port/iomt_use.c b/sr_port/iomt_use.c new file mode 100644 index 0000000..a8384d6 --- /dev/null +++ b/sr_port/iomt_use.c @@ -0,0 +1,221 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_iconv.h" +#include "gtm_string.h" + +#include "copy.h" +#include "io_params.h" +#include "io.h" +#include "iosp.h" +#include "iottdef.h" +#include "iomtdef.h" +#include "nametabtyp.h" +#include "stringpool.h" +#include "namelook.h" + +static readonly nametabent mtlab_names[] = +{ + {3, "ANS"}, {4,"ANSI"}, {3, "DOS"}, {5, "DOS11"} +}; +static readonly unsigned char mtlab_index[27] = +{ + 0, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4 + ,4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 + ,4, 4, 4 +}; + +static readonly char mtlab_type[]={MTLAB_ANSI, MTLAB_ANSI, MTLAB_DOS11, MTLAB_DOS11}; + +static readonly nametabent mtwtlab_names[] = +{ + {4, "EOF1"}, {4,"EOF2"}, {4, "HDR1"}, {4, "HDR2"}, {4, "VOL1"} +}; + +static readonly unsigned char mtwtlab_index[27] = +{ + 0, 0, 0, 0, 0, 2, 2, 2, 4, 4, 4, 4, 4 + ,4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5 + ,5, 5, 5 +}; + +static readonly char mtwtlab_type[]={MTL_EOF1, MTL_EOF2, MTL_HDR1, MTL_HDR2, MTL_VOL1}; + +LITREF unsigned char io_params_size[]; + +void iomt_use(io_desc *iod, mval *pp) +{ + unsigned char ch, len; + int lab_type; + int4 length, width; + int4 skips; + d_mt_struct *mt_ptr, *out_ptr; + io_desc *d_in, *d_out; + char *tab; + int p_offset; + + error_def(ERR_MTINVLAB); + error_def(ERR_DEVPARMNEG); + error_def(ERR_UNIMPLOP); + + p_offset = 0; + d_in = iod->pair.in; + d_out = iod->pair.out; + mt_ptr = (d_mt_struct *)iod->dev_sp; + out_ptr = (d_mt_struct *)d_out->dev_sp; + while (*(pp->str.addr + p_offset) != iop_eol) + { + switch (ch = *(pp->str.addr + p_offset++)) + { + case iop_ebcdic: + mt_ptr->ebcdic = TRUE; + break; + case iop_noebcdic: + mt_ptr->ebcdic = FALSE; + break; + case iop_newversion: + mt_ptr->newversion = TRUE; + break; + case iop_label: + len = *(pp->str.addr + p_offset); + tab = pp->str.addr + p_offset + 1; + if ((lab_type = namelook(mtlab_index, mtlab_names, tab, len)) < 0) + rts_error(VARLSTCNT(1) ERR_MTINVLAB); + mt_ptr->labeled = mtlab_type[lab_type]; + break; + case iop_nolabel: + mt_ptr->labeled = FALSE; + break; + case iop_rdcheckdata: + mt_ptr->read_mask |= IO_M_DATACHECK; + break; + case iop_nordcheckdata: + mt_ptr->read_mask &= (~(IO_M_DATACHECK)); + break; + case iop_wtcheckdata: + mt_ptr->write_mask |= IO_M_DATACHECK; + break; + case iop_nowtcheckdata: + mt_ptr->write_mask &= (~(IO_M_DATACHECK)); + break; + case iop_inhretry: + mt_ptr->write_mask |= IO_M_INHRETRY; + mt_ptr->read_mask |= IO_M_INHRETRY; + break; + case iop_retry: + mt_ptr->write_mask &= ~IO_M_INHRETRY; + mt_ptr->read_mask &= ~IO_M_INHRETRY; + break; + case iop_inhextgap: + mt_ptr->write_mask |= IO_M_INHEXTGAP; + break; + case iop_extgap: + mt_ptr->write_mask &= ~IO_M_INHEXTGAP; + break; + case iop_length: + GET_LONG(length, (pp->str.addr + p_offset)); + if (length < 0) + rts_error(VARLSTCNT(1) ERR_DEVPARMNEG); + iod->length = length; + break; + case iop_width: + GET_LONG(width, (pp->str.addr + p_offset)); + if (width < 0) + rts_error(VARLSTCNT(1) ERR_DEVPARMNEG); + if (width == 0) + { + iod->wrap = FALSE; + iod->width = mt_ptr->record_sz; + } else if (width <= mt_ptr->record_sz) + { + iomt_flush(iod); + iod->width = width; + iod->wrap = TRUE; + } + break; + case iop_wrap: + out_ptr->wrap = TRUE; + break; + case iop_nowrap: + out_ptr->wrap = FALSE; + break; + case iop_skipfile: + GET_LONG(skips, (pp->str.addr + p_offset)); + iomt_skipfile(iod, skips); + break; + case iop_unload: + assert(FALSE); + break; + case iop_rewind: + iomt_rewind(iod); + break; + case iop_erasetape: + iomt_erase(iod); + break; + case iop_space: + GET_LONG(skips, (pp->str.addr + p_offset)); + iomt_skiprecord(iod, skips); + break; + case iop_writeof: + iomt_eof(iod); + break; + case iop_writetm: + iomt_tm(iod); + break; + case iop_writelb: + len = *(pp->str.addr + p_offset); + tab = pp->str.addr + p_offset + 1; + if ((lab_type = namelook(mtwtlab_index, mtwtlab_names, tab, len)) < 0) + rts_error(VARLSTCNT(1) ERR_MTINVLAB); + iomt_wtansilab(iod, mtwtlab_type[lab_type]); + break; + case iop_next: + rts_error(VARLSTCNT(1) ERR_UNIMPLOP); + break; + case iop_exception: + iod->error_handler.len = *(pp->str.addr + p_offset); + iod->error_handler.addr = (char *)(pp->str.addr + p_offset + 1); + s2pool(&iod->error_handler); + break; + case iop_ipchset: + { +#if defined(KEEP_zOS_EBCDIC) || defined(VMS) + if ( (iconv_t)0 != iod->input_conv_cd ) + { + ICONV_CLOSE_CD(iod->input_conv_cd); + } + SET_CODE_SET(iod->in_code_set, (char *)(pp->str.addr + p_offset + 1)); + if (DEFAULT_CODE_SET != iod->in_code_set) + ICONV_OPEN_CD(iod->input_conv_cd, (char *)(pp->str.addr + p_offset + 1), INSIDE_CH_SET); +#endif + break; + } + case iop_opchset: + { +#if defined(KEEP_zOS_EBCDIC) || defined(VMS) + if ( (iconv_t) 0 != iod->output_conv_cd ) + { + ICONV_CLOSE_CD(iod->output_conv_cd); + } + SET_CODE_SET(iod->out_code_set, (char *)(pp->str.addr + p_offset + 1)); + if (DEFAULT_CODE_SET != iod->out_code_set) + ICONV_OPEN_CD(iod->output_conv_cd, INSIDE_CH_SET, (char *)(pp->str.addr + p_offset + 1)); +#endif + break; + } + default: + break; + } + p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ? + (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]); + } +} diff --git a/sr_port/iomt_vlflush.c b/sr_port/iomt_vlflush.c new file mode 100644 index 0000000..9633254 --- /dev/null +++ b/sr_port/iomt_vlflush.c @@ -0,0 +1,47 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "io.h" +#include "iottdef.h" +#include "iomtdef.h" + +GBLREF io_pair io_curr_device; + +void iomt_vlflush(io_desc *dv) +{ + int len; + unsigned char *cp, fill_char; + d_mt_struct *mt_ptr; + + mt_ptr = (d_mt_struct *) dv->dev_sp; + assert(mt_ptr->fixed == FALSE); + if (!mt_ptr->stream) + { + fill_char = '^'; + len = mt_ptr->rec.len + MT_RECHDRSIZ; + cp = (unsigned char *) ( mt_ptr->rec.addr - MT_RECHDRSIZ); + memcpy(mt_ptr->buffer + mt_ptr->bufftoggle, cp, len); + memset(cp, fill_char, mt_ptr->bufftop - cp); + } + iomt_wrtblk(dv); + mt_ptr->buffer += mt_ptr->bufftoggle; + mt_ptr->bufftop += mt_ptr->bufftoggle; + mt_ptr->bufftoggle = -mt_ptr->bufftoggle; + mt_ptr->rec.addr = (char *) mt_ptr->buffer; + mt_ptr->buffptr = mt_ptr->buffer; + if (!mt_ptr->stream) + mt_ptr->rec.addr += MT_RECHDRSIZ; + return; +} diff --git a/sr_port/iomt_write.c b/sr_port/iomt_write.c new file mode 100644 index 0000000..b582679 --- /dev/null +++ b/sr_port/iomt_write.c @@ -0,0 +1,102 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" +#include "gtm_string.h" + +#include "io.h" +#include "iottdef.h" +#include "iomtdef.h" +#include "iosp.h" + +GBLREF io_pair io_curr_device; + +void +iomt_write (mstr *v) +{ + error_def (ERR_MTRDONLY); + io_desc *io_ptr; + unsigned char *inpt; + int inlen, outlen, n; + unsigned char *outcp; + d_mt_struct *mt_ptr; + + io_ptr = io_curr_device.out; + mt_ptr = (d_mt_struct *) io_ptr->dev_sp; + if (mt_ptr->read_only) + rts_error (VARLSTCNT (1) ERR_MTRDONLY); + +#ifdef UNIX + if (mt_ptr->mode != MT_M_WRITE) + { + uint4 status; + status = iomt_reopen (io_ptr, MT_M_WRITE, FALSE); + } +#endif + if (mt_ptr->last_op != mt_write) + iomt_wrtinit (io_ptr); + inlen = v->len; + outlen = io_ptr->width - mt_ptr->rec.len; + if (!mt_ptr->wrap && inlen > outlen) + inlen = outlen; + if (!inlen) + return; + for (inpt = (unsigned char *) v->addr;; inpt += n) + { + if (mt_ptr->stream) + { + outcp = mt_ptr->buffptr; + n = (outcp + inlen > mt_ptr->bufftop) ? + (int)(mt_ptr->bufftop - outcp) : inlen; + if (n <= 0) + { + iomt_vlflush (io_ptr); + outcp = (unsigned char *) mt_ptr->rec.addr; + } + } else + { + outcp = (unsigned char *) mt_ptr->rec.addr + mt_ptr->rec.len; + n = (inlen > outlen) ? outlen : inlen; + if (mt_ptr->fixed) + assert (outcp + n <= mt_ptr->bufftop); + else + { + if (outcp + n >= mt_ptr->bufftop) + { + iomt_vlflush (io_ptr); + outcp = (unsigned char *) (mt_ptr->rec.addr + mt_ptr->rec.len); + } + } + } + memcpy (outcp, inpt, n); + mt_ptr->buffptr = outcp + n; + mt_ptr->rec.len += n; + io_ptr->dollar.x += n; + if ((inlen -= n) <= 0) + break; + if (!mt_ptr->stream) + { + iomt_wteol (1, io_ptr); + outlen = io_ptr->width - mt_ptr->rec.len; + } + } + if (mt_ptr->stream && io_ptr->dollar.x >= io_ptr->width && io_ptr->wrap) + { + io_ptr->dollar.y += (io_ptr->dollar.x / io_ptr->width); + if (io_ptr->length) + io_ptr->dollar.y %= io_ptr->length; + io_ptr->dollar.x %= io_ptr->width; + } + + return; +} diff --git a/sr_port/iomt_wrtblk.c b/sr_port/iomt_wrtblk.c new file mode 100644 index 0000000..976e042 --- /dev/null +++ b/sr_port/iomt_wrtblk.c @@ -0,0 +1,70 @@ +/**************************************************************** + * * + * Copyright 2001, 2004 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_stdio.h" +#include "io.h" +#include "iosp.h" +#include "iottdef.h" +#include "iomtdef.h" + +#ifdef UNIX +#include +#endif +#include "movtc.h" + +LITREF unsigned char LIB_AB_ASC_EBC[]; + +void iomt_wrtblk (io_desc *dv) +{ + uint4 status; + iosb io_status_blk; + d_mt_struct *mt_ptr; + error_def (ERR_MTIS); + +#ifdef DP + FPRINTF(stderr, ">> iomt_wrtblk\n"); +#endif + mt_ptr = (d_mt_struct *) dv->dev_sp; + if (mt_ptr->ebcdic) + movtc (mt_ptr->block_sz, mt_ptr->buffer, LIB_AB_ASC_EBC, mt_ptr->buffer); + io_status_blk.status = 0; + +#ifdef UNIX + if (mt_ptr->mode != MT_M_WRITE) + { + status = iomt_reopen (dv, MT_M_WRITE, FALSE); + if (status != SS_NORMAL) + return; + } +#endif + status = iomt_wtlblk (mt_ptr->access_id, mt_ptr->write_mask, &io_status_blk, + mt_ptr->buffer, mt_ptr->block_sz); + if ((status != SS_NORMAL) || ((status = io_status_blk.status) != SS_NORMAL)) + { + if (status == SS_ENDOFTAPE) + dv->dollar.za = 1; + else + { + dv->dollar.za = 9; +#ifdef UNIX + rts_error (VARLSTCNT (6) errno, 0, ERR_MTIS, 2, dv->trans_name->len, dv->trans_name->dollar_io); +#else /* VAX */ + rts_error(VARLSTCNT(4) ERR_MTIS, 2, dv->trans_name->len, dv->trans_name->dollar_io); +#endif + } + } else + dv->dollar.za = 0; +#ifdef DP + FPRINTF(stderr, "<< iomt_wrtblk\n"); +#endif + return; +} diff --git a/sr_port/iomt_wrtinit.c b/sr_port/iomt_wrtinit.c new file mode 100644 index 0000000..a6a54fc --- /dev/null +++ b/sr_port/iomt_wrtinit.c @@ -0,0 +1,69 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_stdio.h" +#include "io.h" +#include "iottdef.h" +#include "iomtdef.h" +#include "iosp.h" +#include "copy.h" + +int iomt_wrtinit (io_desc *dv) +{ + unsigned char *cp; + uint4 *quadbyteptr; + int x, y; + d_mt_struct *mt_ptr; + static readonly unsigned char litzeros[4] = {'0', '0', '0', '0'}; + + mt_ptr = (d_mt_struct *) dv->dev_sp; +#ifdef UNIX + if (mt_ptr->mode != MT_M_WRITE) + { + uint4 status; + status = iomt_reopen (dv, MT_M_WRITE, FALSE); + if (status != SS_NORMAL) + { + return (status); + } + } +#endif + + mt_ptr->bufftop = mt_ptr->buffer + mt_ptr->block_sz; + mt_ptr->buffptr = mt_ptr->buffer; + if (mt_ptr->fixed || mt_ptr->stream) + { + mt_ptr->rec.addr = (char *) mt_ptr->buffer; + mt_ptr->rec.len = 0; + } else + { + quadbyteptr = (uint4 *) mt_ptr->buffer; + GET_LONGP(quadbyteptr, &litzeros[0]); + quadbyteptr++; + mt_ptr->rec.addr = (char *) quadbyteptr; + mt_ptr->rec.len = 0; + } + if (mt_ptr->labeled && mt_ptr->last_op == mt_rewind) + { + if (mt_ptr->labeled == MTLAB_DOS11) + iomt_wtdoslab (dv); + else if (mt_ptr->labeled == MTLAB_ANSI) + { + iomt_wtansilab (dv, MTL_VOL1 | MTL_HDR1 | MTL_HDR2); + iomt_tm (dv); + } else + GTMASSERT; + } + dv->dollar.za = 0; + mt_ptr->last_op = mt_write; + return SS_NORMAL; +} diff --git a/sr_port/iomt_wtdoslab.c b/sr_port/iomt_wtdoslab.c new file mode 100644 index 0000000..d0faeb8 --- /dev/null +++ b/sr_port/iomt_wtdoslab.c @@ -0,0 +1,59 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" +#include "gtm_string.h" + +#include "io.h" +#include "iosp.h" +#include "iottdef.h" +#include "iomtdef.h" + +void +iomt_wtdoslab (io_desc *dv) +{ + static readonly unsigned char label[] = {149, 84, 248, 102, 79, 192, 1, 1, 155, 0, 0, 0, 0, 0}; + uint4 status; + iosb io_status_blk; + unsigned char outcp[SIZEOF(label)]; + d_mt_struct *mt_ptr; + error_def (ERR_MTIS); + error_def (ERR_MTRDONLY); + + mt_ptr = (d_mt_struct *) dv->dev_sp; + if (mt_ptr->read_only) + rts_error (VARLSTCNT (1) ERR_MTRDONLY); +#ifdef UNIX + if (mt_ptr->mode != MT_M_WRITE) + { + status = iomt_reopen (dv, MT_M_WRITE, FALSE); + if (status != SS_NORMAL) + return; + } +#endif + memcpy(&outcp[0], &label[0], SIZEOF(outcp)); + io_status_blk.status = 0; + + status = iomt_wtlblk(mt_ptr->access_id, IO_WRITELBLK, &io_status_blk, + outcp, SIZEOF(outcp)); + if ((status != SS_NORMAL) || ((status = io_status_blk.status) != SS_NORMAL)) + { + if (status == SS_ENDOFTAPE) + dv->dollar.za = 1; + else + dv->dollar.za = 9; + rts_error (VARLSTCNT (4) ERR_MTIS, 2, dv->trans_name->len, dv->trans_name->dollar_io); + } else + dv->dollar.za = 0; + return; +} diff --git a/sr_port/iomt_wteol.c b/sr_port/iomt_wteol.c new file mode 100644 index 0000000..ecda727 --- /dev/null +++ b/sr_port/iomt_wteol.c @@ -0,0 +1,131 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gtm_stdio.h" +#include "io.h" +#include "iottdef.h" +#include "iomtdef.h" +#include "iosp.h" +#include "copy.h" + +#define EBCDIC_LITZERO '\360' +#define ASCII_LITZERO '0' + +#ifdef __MVS__ +#define MT_LITZERO EBCDIC_LITZERO +#else +#define MT_LITZERO ASCII_LITZERO +#endif + +void iomt_wteol (int4 cnt, io_desc *dv) +{ + unsigned char *cp; + uint4 *quadbyteptr; + d_mt_struct *mt_ptr; + error_def (ERR_MTRDONLY); + int i, x, y; + static readonly unsigned char litzeros[4] = {MT_LITZERO, MT_LITZERO, MT_LITZERO, MT_LITZERO}; + + mt_ptr = (d_mt_struct *) dv->dev_sp; + if (mt_ptr->read_only) + rts_error (VARLSTCNT (1) ERR_MTRDONLY); + +#ifdef UNIX + if (mt_ptr->mode != MT_M_WRITE) + { + uint4 status; + status = iomt_reopen (dv, MT_M_WRITE, FALSE); + if (status != SS_NORMAL) + { + /*return(status);*/ + return; + } + } +#endif + + if (mt_ptr->last_op != mt_write) + iomt_wrtinit (dv); + for (i = 0; i < cnt; i++) + { + if (mt_ptr->fixed) + { + x = mt_ptr->record_sz - mt_ptr->rec.len; + if (x > 0) + memset (mt_ptr->rec.addr + mt_ptr->rec.len, SP, x); + mt_ptr->rec.addr += mt_ptr->record_sz; + mt_ptr->rec.len = 0; + if (mt_ptr->rec.addr >= (char *) mt_ptr->bufftop) + { + assert (mt_ptr->rec.addr == (char *) mt_ptr->bufftop); + iomt_wrtblk (dv); + mt_ptr->rec.addr = (char *) mt_ptr->buffer; + } + } else if (!mt_ptr->stream) + { + cp = (unsigned char *) mt_ptr->rec.addr; + x = mt_ptr->rec.len; + quadbyteptr = (uint4 *) (cp + x); + x += MT_RECHDRSIZ; + y = x / 10; + *--cp = x - y * 10 + MT_LITZERO; + if (x = y) + { + *--cp = x - (y /= 10) * 10 + MT_LITZERO; + if (x = y) + { + *--cp = x - (y /= 10) * 10 + MT_LITZERO; + if (x = y) + { + *--cp = x - (y / 10) * 10 + MT_LITZERO; + } + } + } + if (((unsigned char *) quadbyteptr) + + MT_RECHDRSIZ > mt_ptr->bufftop) + { + cp = (unsigned char *) (mt_ptr->rec.addr + mt_ptr->rec.len); + x = (int)(mt_ptr->bufftop - cp); + assert (x >= 0); + if (x > 0) + memset (cp, '^', x); + iomt_wrtblk (dv); + quadbyteptr = (uint4 *) mt_ptr->buffer; + } + GET_LONGP(quadbyteptr, &litzeros[0]); + quadbyteptr++; + mt_ptr->rec.addr = (char *) quadbyteptr; + mt_ptr->rec.len = 0; + } else + { + assert (mt_ptr->stream); + mt_ptr->rec.len = 0; + if (mt_ptr->buffptr >= mt_ptr->bufftop) + iomt_vlflush (dv); + + *mt_ptr->buffptr++ = NATIVE_CR; + if (mt_ptr->buffptr >= mt_ptr->bufftop) + iomt_vlflush (dv); + + *mt_ptr->buffptr++ = NATIVE_LF; + if (mt_ptr->buffptr >= mt_ptr->bufftop) + iomt_vlflush (dv); + } + } + dv->dollar.x = 0; + dv->dollar.y += cnt; + if (dv->length) + dv->dollar.y %= dv->length; + return; +} diff --git a/sr_port/iomt_wtff.c b/sr_port/iomt_wtff.c new file mode 100644 index 0000000..47bf698 --- /dev/null +++ b/sr_port/iomt_wtff.c @@ -0,0 +1,45 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" +#include "iottdef.h" +#include "iomtdef.h" +#include "iosp.h" + +GBLREF io_pair io_curr_device; + +void iomt_wtff (void) +{ + io_desc *dv; + d_mt_struct *mt_ptr; + + dv = io_curr_device.out; + mt_ptr = (d_mt_struct *) dv->dev_sp; + +#ifdef UNIX + if (mt_ptr->mode != MT_M_WRITE) + { + uint4 status; + + status = iomt_reopen (dv, MT_M_WRITE, FALSE); + if (status != SS_NORMAL) + return; + } +#endif + iomt_wtone (12); + if (!mt_ptr->stream) + iomt_wteol (1, dv); + + mt_ptr->rec.len = 0; + io_curr_device.out->dollar.x = 0; + io_curr_device.out->dollar.y = 0; +} diff --git a/sr_port/iomt_wtone.c b/sr_port/iomt_wtone.c new file mode 100644 index 0000000..894b3c4 --- /dev/null +++ b/sr_port/iomt_wtone.c @@ -0,0 +1,27 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" + +GBLREF boolean_t gtm_utf8_mode; + +void iomt_wtone(int ch) +{ + mstr temp; + char c; + + c = (int)ch; + temp.len = 1; + temp.addr = &c; + iomt_write(&temp); + return; +} diff --git a/sr_port/ionl_close.c b/sr_port/ionl_close.c new file mode 100644 index 0000000..d7730be --- /dev/null +++ b/sr_port/ionl_close.c @@ -0,0 +1,19 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" + +void ionl_close(io_desc *dv, mval *p) +{ + dv->state = dev_closed; + return; +} diff --git a/sr_port/ionl_dummy.c b/sr_port/ionl_dummy.c new file mode 100644 index 0000000..6d8a080 --- /dev/null +++ b/sr_port/ionl_dummy.c @@ -0,0 +1,18 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" + +short ionl_dummy(io_log_name *dev_name, mval *pp, int fd, mval *mspace, int4 timeout) +{ + return 0; +} diff --git a/sr_port/ionl_flush.c b/sr_port/ionl_flush.c new file mode 100644 index 0000000..3df59f0 --- /dev/null +++ b/sr_port/ionl_flush.c @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#include "mdef.h" +#include "io.h" + +void ionl_flush(io_desc *iod) +{ + return; +} diff --git a/sr_port/ionl_open.c b/sr_port/ionl_open.c new file mode 100644 index 0000000..9891bbd --- /dev/null +++ b/sr_port/ionl_open.c @@ -0,0 +1,71 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "errno.h" +#include "gtm_unistd.h" + +#include "io_params.h" +#include "io.h" +#include "stringpool.h" +#include "gtmio.h" + +#define DEF_NL_WIDTH 255 +#define DEF_NL_LENGTH 66 + +LITREF unsigned char io_params_size[]; + +short ionl_open(io_log_name *dev_name, mval *pp, int fd, mval *mspace, int4 timeout) +{ + unsigned char ch; + io_desc *d_in, *d_out, *ioptr; + int p_offset, status; + + p_offset = 0; + /* If UNIX, then /dev/null was actually opened by io_open_try so we have to close it + since we don't use the device, we just simulate it by doing nothing on writes except + maintaining the appropriate pointers. We test for fd >= 0 since the less than zero + values mean no device was opened. + */ + UNIX_ONLY( + if (0 <= fd) + CLOSEFILE_RESET(fd, status); /* resets "fd" to FD_INVALID */ + ); + ioptr = dev_name->iod; + ioptr->state = dev_open; + d_in = ioptr->pair.in; + d_out = ioptr->pair.out; + ioptr->length = DEF_NL_LENGTH; + ioptr->width = DEF_NL_WIDTH; + ioptr->wrap = TRUE; + ioptr->dollar.za = 0; + ioptr->dollar.zeof = FALSE; + ioptr->dollar.x = 0; + ioptr->dollar.y = 0; + while (*(pp->str.addr + p_offset) != iop_eol) + { + if ((ch = *(pp->str.addr + p_offset++)) == iop_wrap) + d_out->wrap = TRUE; + if ((ch = *(pp->str.addr + p_offset++)) == iop_nowrap) + d_out->wrap = FALSE; + if ((ch = *(pp->str.addr + p_offset++)) == iop_exception) + { + ioptr->error_handler.len = *(pp->str.addr + p_offset); + ioptr->error_handler.addr = (char *)(pp->str.addr + p_offset + 1); + s2pool(&ioptr->error_handler); + break; + } + p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ? + (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]); + } + return TRUE; +} diff --git a/sr_port/ionl_rdone.c b/sr_port/ionl_rdone.c new file mode 100644 index 0000000..0bb5945 --- /dev/null +++ b/sr_port/ionl_rdone.c @@ -0,0 +1,23 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* ionl_rdone.c */ + +#include "mdef.h" +#include "io.h" + +int ionl_rdone(mint *val, int4 timeout) +{ + mval tmp; + + *val = -1; + return ionl_readfl(&tmp, 1, timeout); +} diff --git a/sr_port/ionl_read.c b/sr_port/ionl_read.c new file mode 100644 index 0000000..1aebfe1 --- /dev/null +++ b/sr_port/ionl_read.c @@ -0,0 +1,18 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" + +int ionl_read(mval *val, int4 timeout) +{ + return ionl_readfl(val, 1, timeout); +} diff --git a/sr_port/ionl_readfl.c b/sr_port/ionl_readfl.c new file mode 100644 index 0000000..b332bda --- /dev/null +++ b/sr_port/ionl_readfl.c @@ -0,0 +1,35 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" + +GBLREF io_pair io_curr_device; + +int ionl_readfl(mval *val, int4 width, int4 timeout) +{ + io_desc *dv; + error_def(ERR_IOEOF); + + val->str.len = 0; + dv = io_curr_device.in; + dv->dollar.x = 0; + dv->dollar.y++; + if (dv->dollar.zeof || (dv->error_handler.len > 0)) + { + dv->dollar.zeof = TRUE; + dv->dollar.za = 9; + rts_error(VARLSTCNT(1) ERR_IOEOF); + } + dv->dollar.za = 0; + dv->dollar.zeof = TRUE; + return TRUE; +} diff --git a/sr_port/ionl_use.c b/sr_port/ionl_use.c new file mode 100644 index 0000000..e280a1e --- /dev/null +++ b/sr_port/ionl_use.c @@ -0,0 +1,165 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gtm_iconv.h" +#include "gtm_stdlib.h" + +#include "copy.h" +#include "io_params.h" +#include "io.h" +#include "iosp.h" +#include "iottdef.h" +#include "nametabtyp.h" +#include "stringpool.h" +#include "namelook.h" + +LITREF nametabent filter_names[]; +LITREF unsigned char filter_index[27]; +LITREF unsigned char io_params_size[]; + +void ionl_use(io_desc *iod, mval *pp) +{ + unsigned char ch, len; + int fil_type; + int4 width, length; + io_desc *d_in, *d_out; + char *tab; + int p_offset; + + error_def(ERR_TTINVFILTER); + error_def(ERR_DEVPARMNEG); + + p_offset = 0; + d_in = iod->pair.in; + d_out = iod->pair.out; + assert(iod->state == dev_open); + while (*(pp->str.addr + p_offset) != iop_eol) + { + switch (ch = *(pp->str.addr + p_offset++)) + { + case iop_exception: + iod->error_handler.len = *(pp->str.addr + p_offset); + iod->error_handler.addr = (char *)(pp->str.addr + p_offset + 1); + s2pool(&iod->error_handler); + break; + case iop_filter: + len = *(pp->str.addr + p_offset); + tab = pp->str.addr + p_offset + 1; + if ((fil_type = namelook(filter_index, filter_names, tab, len)) < 0) + { + rts_error(VARLSTCNT(1) ERR_TTINVFILTER); + return; + } + switch (fil_type) + { + case 0: + iod->write_filter |= CHAR_FILTER; + break; + case 1: + iod->write_filter |= ESC1; + break; + case 2: + iod->write_filter &= ~CHAR_FILTER; + break; + case 3: + iod->write_filter &= ~ESC1; + break; + } + break; + case iop_nofilter: + iod->write_filter = 0; + break; + case iop_length: + GET_LONG(length, pp->str.addr + p_offset); + if (length < 0) + rts_error(VARLSTCNT(1) ERR_DEVPARMNEG); + d_out->length = length; + break; + case iop_width: + GET_LONG(width, pp->str.addr + p_offset); + if (width < 0) + rts_error(VARLSTCNT(1) ERR_DEVPARMNEG); + if (width == 0) + { + d_out->wrap = FALSE; + d_out->width = TTDEF_PG_WIDTH; + } + else + { + d_out->width = width; + d_out->wrap = TRUE; + } + break; + case iop_wrap: + d_out->wrap = TRUE; + break; + case iop_nowrap: + d_out->wrap = FALSE; + break; + case iop_x: + { + int4 col; + + GET_LONG(col, pp->str.addr + p_offset); + d_out->dollar.x = col; + if ((int4)(d_out->dollar.x) < 0) + d_out->dollar.x = 0; + if (d_out->dollar.x > d_out->width && d_out->wrap) + d_out->dollar.x %= d_out->width; + break; + } + case iop_y: + { + int4 row; + + GET_LONG(row, (pp->str.addr + p_offset)); + d_out->dollar.y = row; + if ((int4)(d_out->dollar.y) < 0) + d_out->dollar.y = 0; + if (d_out->length) + d_out->dollar.y %= d_out->length; + break; + } + case iop_ipchset: + { +#if defined(KEEP_zOS_EBCDIC) || defined(VMS) + if ( (iconv_t)0 != iod->input_conv_cd ) + { + ICONV_CLOSE_CD(iod->input_conv_cd); + } + SET_CODE_SET(iod->in_code_set, (char *)(pp->str.addr + p_offset + 1)); + if (DEFAULT_CODE_SET != iod->in_code_set) + ICONV_OPEN_CD(iod->input_conv_cd, (char *)(pp->str.addr + p_offset + 1), INSIDE_CH_SET); +#endif + break; + } + case iop_opchset: + { +#if defined(KEEP_zOS_EBCDIC) || defined(VMS) + if ( (iconv_t) 0 != iod->output_conv_cd ) + { + ICONV_CLOSE_CD(iod->output_conv_cd); + } + SET_CODE_SET(iod->out_code_set, (char *)(pp->str.addr + p_offset + 1)); + if (DEFAULT_CODE_SET != iod->out_code_set) + ICONV_OPEN_CD(iod->output_conv_cd, INSIDE_CH_SET, (char *)(pp->str.addr + p_offset + 1)); +#endif + break; + } + } + p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ? + (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]); + } + return; +} diff --git a/sr_port/ionl_write.c b/sr_port/ionl_write.c new file mode 100644 index 0000000..a7845b2 --- /dev/null +++ b/sr_port/ionl_write.c @@ -0,0 +1,26 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" +#include "dollarx.h" + +GBLREF io_pair io_curr_device; + +void ionl_write(mstr *v) +{ + io_desc *io_ptr; + + io_ptr = io_curr_device.out; + io_ptr->dollar.zeof = FALSE; + dollarx(io_ptr, (uchar_ptr_t)v->addr, (uchar_ptr_t)v->addr + v->len); + return; +} diff --git a/sr_port/ionl_wteol.c b/sr_port/ionl_wteol.c new file mode 100644 index 0000000..a3775ee --- /dev/null +++ b/sr_port/ionl_wteol.c @@ -0,0 +1,48 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "io.h" +#include "iottdef.h" + +/* essentially the same as iott_wteol */ +void ionl_wteol(int4 val, io_desc *io_ptr) +{ + mstr eol; + int eol_cnt; + + assert(val); + io_ptr->esc_state = START; + eol.len = STRLEN(NATIVE_TTEOL); + eol.addr = (char *)NATIVE_TTEOL; + for (eol_cnt = val; eol_cnt--; ) + { + io_ptr->dollar.x = 0; /* so that ionl_write doesn't try to wrap (based on escape state and width) */ + ionl_write(&eol); + } + /* $X is maintained in VMS without the below assignment (resetting to 0) because the NATIVE_TTEOL is \015\012 + * and the (\015) triggers appropriate maintenance of $X. In UNIX, NATIVE_TTEOL is \012, so + * FILTER=CHARACTER effectively turns off all $X maintenance (except for WRAP logic). + * In VMS the below assignment is not necessary, but harmless; it is always logically correct. + */ + io_ptr->dollar.x = 0; + if (!(io_ptr->write_filter & CHAR_FILTER)) + { /* If no FILTER and EOL, also maintain $Y; + * If FILTER, dollarx() of the linefeed character \012 takes care of this maintenance. + */ + io_ptr->dollar.y += val; + if (io_ptr->length) + io_ptr->dollar.y %= io_ptr->length; + } + return; +} diff --git a/sr_port/ionl_wtff.c b/sr_port/ionl_wtff.c new file mode 100644 index 0000000..50d710c --- /dev/null +++ b/sr_port/ionl_wtff.c @@ -0,0 +1,25 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "io.h" + +GBLREF io_pair io_curr_device; + +void ionl_wtff(void) +{ + io_curr_device.out->esc_state = START; + io_curr_device.out->dollar.zeof = FALSE; + io_curr_device.out->dollar.x = 0; + io_curr_device.out->dollar.y = 0; + return; +} diff --git a/sr_port/ionl_wtone.c b/sr_port/ionl_wtone.c new file mode 100644 index 0000000..2329312 --- /dev/null +++ b/sr_port/ionl_wtone.c @@ -0,0 +1,25 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" + +void ionl_wtone(int v) +{ + mstr temp; + char p; + + p = (char)v; + temp.len = 1; + temp.addr = &p; + ionl_write(&temp); + return; +} diff --git a/sr_port/iop.h b/sr_port/iop.h new file mode 100644 index 0000000..09e81c2 --- /dev/null +++ b/sr_port/iop.h @@ -0,0 +1,210 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iop.h, to be included when io parameters need enumeration */ +/* structure: enum_value, enum_mnemonic, data_element_size, legal_operations + , source_value_type */ +/* last entry = n_iops is the size of the table */ +/* Must add entries at the end in order to avoid a recompile */ +IOP_DESC(0, iop_eol, 0, 0, 0), +IOP_DESC(1, iop_canctlo, 0, IOP_USE_OK, 0), +IOP_DESC(2, iop_cenable, 0, IOP_USE_OK, 0), +IOP_DESC(3, iop_nocenable, 0, IOP_USE_OK, 0), +IOP_DESC(4, iop_clearscreen, 0, IOP_USE_OK, 0), +IOP_DESC(5, iop_convert, 0, IOP_USE_OK, 0), +IOP_DESC(6, iop_noconvert, 0, IOP_USE_OK, 0), +IOP_DESC(7, iop_downscroll, 0, IOP_USE_OK, 0), +IOP_DESC(8, iop_echo, 0, IOP_USE_OK, 0), +IOP_DESC(9, iop_noecho, 0, IOP_USE_OK, 0), +IOP_DESC(10, iop_eraseline, 0, IOP_USE_OK, 0), +IOP_DESC(11, iop_field, SIZEOF(short), IOP_USE_OK, IOP_SRC_INT), +IOP_DESC(12, iop_terminator, SIZEOF(int4) * 8, IOP_USE_OK, IOP_SRC_LNGMSK), +IOP_DESC(13, iop_upscroll, 0, IOP_USE_OK, 0), +IOP_DESC(14, iop_width, SIZEOF(int4), IOP_USE_OK, IOP_SRC_INT), +IOP_DESC(15, iop_blocksize, SIZEOF(int4), IOP_OPEN_OK, IOP_SRC_INT), +IOP_DESC(16, iop_ctrap, SIZEOF(int4), IOP_USE_OK, IOP_SRC_MSK), +IOP_DESC(17, iop_x, SIZEOF(int4), IOP_USE_OK, IOP_SRC_INT), +IOP_DESC(18, iop_y, SIZEOF(int4), IOP_USE_OK, IOP_SRC_INT), +IOP_DESC(19, iop_escape, 0, IOP_USE_OK, 0), +IOP_DESC(20, iop_noescape, 0, IOP_USE_OK, 0), +IOP_DESC(21, iop_allocation, SIZEOF(int4), IOP_OPEN_OK, IOP_SRC_INT), +IOP_DESC(22, iop_contiguous, 0, IOP_OPEN_OK, 0), +IOP_DESC(23, iop_delete, 0, IOP_CLOSE_OK, 0), +IOP_DESC(24, iop_extension, SIZEOF(unsigned short), IOP_OPEN_OK, IOP_SRC_INT), +IOP_DESC(25, iop_newversion, 0, IOP_OPEN_OK, 0), +IOP_DESC(26, iop_nosequential, 0, IOP_OPEN_OK, 0), +IOP_DESC(27, iop_s_protection, SIZEOF(char), IOP_OPEN_OK|IOP_CLOSE_OK, IOP_SRC_PRO), +IOP_DESC(28, iop_w_protection, SIZEOF(char), IOP_OPEN_OK|IOP_CLOSE_OK, IOP_SRC_PRO), +IOP_DESC(29, iop_g_protection, SIZEOF(char), IOP_OPEN_OK|IOP_CLOSE_OK, IOP_SRC_PRO), +IOP_DESC(30, iop_o_protection, SIZEOF(char), IOP_OPEN_OK|IOP_CLOSE_OK, IOP_SRC_PRO), +IOP_DESC(31, iop_readonly, 0, IOP_OPEN_OK, 0), +IOP_DESC(32, iop_recordsize, SIZEOF(int4), IOP_OPEN_OK, IOP_SRC_INT), +IOP_DESC(33, iop_shared, 0, IOP_OPEN_OK, 0), +IOP_DESC(34, iop_spool, 0, IOP_CLOSE_OK, 0), +IOP_DESC(35, iop_submit, 0, IOP_CLOSE_OK, 0), +IOP_DESC(36, iop_rfa, 0, IOP_USE_OK, 0), +IOP_DESC(37, iop_space, SIZEOF(int4), IOP_USE_OK|IOP_CLOSE_OK, IOP_SRC_INT), +IOP_DESC(38, iop_queue, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), +IOP_DESC(39, iop_rename, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), +IOP_DESC(40, iop_uic, IOP_VAR_SIZE, IOP_OPEN_OK|IOP_CLOSE_OK, IOP_SRC_STR), +IOP_DESC(41, iop_wrap, 0, IOP_OPEN_OK|IOP_USE_OK, 0), +IOP_DESC(42, iop_nowrap, 0, IOP_OPEN_OK|IOP_USE_OK, 0), +IOP_DESC(43, iop_rewind, 0, IOP_OPEN_OK|IOP_USE_OK|IOP_CLOSE_OK, 0), +IOP_DESC(44, iop_exception, IOP_VAR_SIZE, IOP_OPEN_OK|IOP_USE_OK|IOP_CLOSE_OK, IOP_SRC_STR), +IOP_DESC(45, iop_ebcdic, 0, IOP_OPEN_OK|IOP_USE_OK, 0), +IOP_DESC(46, iop_label, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), +IOP_DESC(47, iop_nolabel, 0, IOP_OPEN_OK | IOP_USE_OK, 0), +IOP_DESC(48, iop_newtape, 0, 0, 0), +IOP_DESC(49, iop_mount, 0, IOP_OPEN_OK, 0), +IOP_DESC(50, iop_fixed, 0, IOP_OPEN_OK, 0), +IOP_DESC(51, iop_erasetape, 0, IOP_OPEN_OK | IOP_USE_OK | IOP_CLOSE_OK, 0), +IOP_DESC(52, iop_next, 0, IOP_USE_OK, 0), +IOP_DESC(53, iop_writeof, 0, IOP_USE_OK|IOP_CLOSE_OK, 0), +IOP_DESC(54, iop_noreadonly, 0, IOP_OPEN_OK, 0), +IOP_DESC(55, iop_rdcheckdata, 0, IOP_OPEN_OK|IOP_USE_OK, 0), +IOP_DESC(56, iop_nordcheckdata, 0, IOP_OPEN_OK|IOP_USE_OK, 0), +IOP_DESC(57, iop_wtcheckdata, 0, IOP_OPEN_OK|IOP_USE_OK, 0), +IOP_DESC(58, iop_nowtcheckdata, 0, IOP_OPEN_OK|IOP_USE_OK, 0), +IOP_DESC(59, iop_inhretry, 0, IOP_OPEN_OK|IOP_USE_OK, 0), +IOP_DESC(60, iop_inhextgap, 0, IOP_OPEN_OK|IOP_USE_OK, 0), +IOP_DESC(61, iop_unload, 0, IOP_CLOSE_OK, 0), +IOP_DESC(62, iop_skipfile, SIZEOF(int4), IOP_USE_OK, IOP_SRC_INT), +IOP_DESC(63, iop_writeonly, 0, IOP_OPEN_OK, 0), +IOP_DESC(64, iop_nowriteonly, 0, IOP_OPEN_OK, 0), +IOP_DESC(65, iop_wait, 0, IOP_USE_OK, 0), +IOP_DESC(66, iop_tmpmbx, 0, IOP_OPEN_OK, 0), +IOP_DESC(67, iop_prmmbx, 0, IOP_OPEN_OK, 0), +IOP_DESC(68, iop_append, 0, IOP_OPEN_OK, 0), +IOP_DESC(69, iop_nowait, 0, IOP_USE_OK, 0), +IOP_DESC(70, iop_nofixed, 0, IOP_OPEN_OK, 0), +IOP_DESC(71, iop_stream, 0, IOP_OPEN_OK, 0), +IOP_DESC(72, iop_nostream, 0, IOP_OPEN_OK, 0), +IOP_DESC(73, iop_flush, 0, IOP_USE_OK, 0), +IOP_DESC(74, iop_length, SIZEOF(int4), IOP_USE_OK, IOP_SRC_INT), +IOP_DESC(75, iop_typeahead, 0, IOP_USE_OK, 0), +IOP_DESC(76, iop_notypeahead, 0, IOP_USE_OK, 0), +IOP_DESC(77, iop_editing, 0, IOP_USE_OK, 0), +IOP_DESC(78, iop_noediting, 0, IOP_USE_OK, 0), +IOP_DESC(79, iop_hostsync, 0, IOP_USE_OK, 0), +IOP_DESC(80, iop_nohostsync, 0, IOP_USE_OK, 0), +IOP_DESC(81, iop_insert, 0, IOP_USE_OK, 0), +IOP_DESC(82, iop_noinsert, 0, IOP_USE_OK, 0), +IOP_DESC(83, iop_pasthru, 0, IOP_USE_OK, 0), +IOP_DESC(84, iop_nopasthru, 0, IOP_USE_OK, 0), +IOP_DESC(85, iop_readsync, 0, IOP_USE_OK, 0), +IOP_DESC(86, iop_noreadsync, 0, IOP_USE_OK, 0), +IOP_DESC(87, iop_ttsync, 0, IOP_USE_OK, 0), +IOP_DESC(88, iop_nottsync, 0, IOP_USE_OK, 0), +IOP_DESC(89, iop_after, SIZEOF(int4) * 2, IOP_CLOSE_OK, IOP_SRC_TIME), +IOP_DESC(90, iop_burst, 0, IOP_CLOSE_OK, 0), +IOP_DESC(91, iop_characteristic, SIZEOF(int4), IOP_CLOSE_OK, IOP_SRC_INT), +IOP_DESC(92, iop_copies, SIZEOF(int4), IOP_CLOSE_OK, IOP_SRC_INT), +IOP_DESC(93, iop_cli, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), +IOP_DESC(94, iop_flag, 0, IOP_CLOSE_OK, 0), +IOP_DESC(95, iop_form, SIZEOF(int4), IOP_CLOSE_OK, IOP_SRC_INT), +IOP_DESC(96, iop_header, 0, IOP_CLOSE_OK, 0), +IOP_DESC(97, iop_hold, 0, IOP_CLOSE_OK, 0), +IOP_DESC(98, iop_lowercase, 0, IOP_CLOSE_OK, 0), +IOP_DESC(99, iop_name, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), +IOP_DESC(100, iop_cpulimit, SIZEOF(int4), IOP_CLOSE_OK, IOP_SRC_INT), +IOP_DESC(101, iop_noburst, 0, IOP_CLOSE_OK, 0), +IOP_DESC(102, iop_print, 0, IOP_CLOSE_OK, 0), +IOP_DESC(103, iop_noprint, 0, IOP_CLOSE_OK, 0), +IOP_DESC(104, iop_noflag, 0, IOP_CLOSE_OK, 0), +IOP_DESC(105, iop_noheader, 0, IOP_CLOSE_OK, 0), +IOP_DESC(106, iop_nohold, 0, IOP_CLOSE_OK, 0), +IOP_DESC(107, iop_nolowercase, 0, IOP_CLOSE_OK, 0), +IOP_DESC(108, iop_nonotify, 0, IOP_CLOSE_OK, 0), +IOP_DESC(109, iop_nopassall, 0, IOP_CLOSE_OK, 0), +IOP_DESC(110, iop_norestart, 0, IOP_CLOSE_OK, 0), +IOP_DESC(111, iop_noused_1, 0, 0, 0), +IOP_DESC(112, iop_note, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), +IOP_DESC(113, iop_notify, 0, IOP_CLOSE_OK, 0), +IOP_DESC(114, iop_notrailer, 0, IOP_CLOSE_OK, 0), +IOP_DESC(115, iop_operator, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), +IOP_DESC(116, iop_firstpage, SIZEOF(int4), IOP_CLOSE_OK, IOP_SRC_INT), +IOP_DESC(117, iop_passall, 0, IOP_CLOSE_OK, 0), +IOP_DESC(118, iop_priority, SIZEOF(int4), IOP_CLOSE_OK, IOP_SRC_INT), +IOP_DESC(119, iop_remote, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), +IOP_DESC(120, iop_restart, 0, IOP_CLOSE_OK, 0), +IOP_DESC(121, iop_setup, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), +IOP_DESC(122, iop_trailer, 0, IOP_CLOSE_OK, 0), +IOP_DESC(123, iop_user, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), +IOP_DESC(124, iop_logfile, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), +IOP_DESC(125, iop_logqueue, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), +IOP_DESC(126, iop_lastpage, SIZEOF(int4), IOP_CLOSE_OK, IOP_SRC_INT), +IOP_DESC(127, iop_page, 0, IOP_CLOSE_OK, 0), +IOP_DESC(128, iop_nopage, 0, IOP_CLOSE_OK, 0), +IOP_DESC(129, iop_doublespace, 0, IOP_CLOSE_OK, 0), +IOP_DESC(130, iop_nodoublespace, 0, IOP_CLOSE_OK, 0), +IOP_DESC(131, iop_p1, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), +IOP_DESC(132, iop_p2, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), +IOP_DESC(133, iop_p3, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), +IOP_DESC(134, iop_p4, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), +IOP_DESC(135, iop_p5, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), +IOP_DESC(136, iop_p6, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), +IOP_DESC(137, iop_p7, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), +IOP_DESC(138, iop_p8, IOP_VAR_SIZE, IOP_CLOSE_OK, IOP_SRC_STR), +IOP_DESC(139, iop_filter, IOP_VAR_SIZE, IOP_USE_OK, IOP_SRC_STR), +IOP_DESC(140, iop_nofilter, 0, IOP_USE_OK, 0), +IOP_DESC(141, iop_writetm, 0, IOP_USE_OK | IOP_CLOSE_OK, 0), +IOP_DESC(142, iop_writelb, IOP_VAR_SIZE, IOP_USE_OK, IOP_SRC_STR), +IOP_DESC(143, iop_truncate, 0, IOP_USE_OK | IOP_OPEN_OK, 0), +IOP_DESC(144, iop_notruncate, 0, IOP_USE_OK | IOP_OPEN_OK, 0), +IOP_DESC(145, iop_extgap, 0, IOP_USE_OK | IOP_OPEN_OK, 0), +IOP_DESC(146, iop_noebcdic, 0, IOP_USE_OK | IOP_OPEN_OK, 0), +IOP_DESC(147, iop_retry, 0, IOP_USE_OK | IOP_OPEN_OK, 0), +IOP_DESC(148, iop_nl, 0, IOP_OPEN_OK, 0), +IOP_DESC(149, iop_noterminator, 0, IOP_USE_OK, 0), +IOP_DESC(150, iop_sequential, 0, IOP_OPEN_OK, 0), +IOP_DESC(151, iop_fifo, 0, IOP_OPEN_OK, 0), +IOP_DESC(152, iop_canonical, 0, IOP_OPEN_OK | IOP_USE_OK, 0), +IOP_DESC(153, iop_nocanonical, 0, IOP_OPEN_OK | IOP_USE_OK, 0), +IOP_DESC(154, iop_socket, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_CLOSE_OK | IOP_USE_OK, IOP_SRC_STR), +IOP_DESC(155, iop_listen, 0, IOP_OPEN_OK | IOP_CLOSE_OK, 0), +IOP_DESC(156, iop_urgent, 0, IOP_USE_OK, 0), +IOP_DESC(157, iop_nourgent, 0, IOP_USE_OK, 0), +IOP_DESC(158, iop_delimiter, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), +IOP_DESC(159, iop_connect, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), +IOP_DESC(160, iop_ioerror, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), +IOP_DESC(161, iop_attach, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), +IOP_DESC(162, iop_detach, IOP_VAR_SIZE, IOP_USE_OK, IOP_SRC_STR), +IOP_DESC(163, iop_zlisten, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_CLOSE_OK | IOP_USE_OK, IOP_SRC_STR), +IOP_DESC(164, iop_ipchset, IOP_VAR_SIZE, IOP_OPEN_OK, IOP_SRC_STR), +IOP_DESC(165, iop_opchset, IOP_VAR_SIZE, IOP_OPEN_OK, IOP_SRC_STR), +IOP_DESC(166, iop_nodelimiter, 0, IOP_OPEN_OK | IOP_USE_OK, 0), +IOP_DESC(167, iop_zdelay, 0, IOP_OPEN_OK | IOP_USE_OK, 0), +IOP_DESC(168, iop_znodelay, 0, IOP_OPEN_OK | IOP_USE_OK, 0), +IOP_DESC(169, iop_zbfsize, SIZEOF(int4), IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_INT), +IOP_DESC(170, iop_zibfsize, SIZEOF(int4), IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_INT), +IOP_DESC(171, iop_zff, IOP_VAR_SIZE, IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_STR), +IOP_DESC(172, iop_znoff, 0, IOP_OPEN_OK | IOP_USE_OK, 0), +IOP_DESC(173, iop_zlength, SIZEOF(int4), IOP_USE_OK, IOP_SRC_INT), +IOP_DESC(174, iop_zwidth, SIZEOF(int4), IOP_USE_OK, IOP_SRC_INT), +IOP_DESC(175, iop_zwrap, 0, IOP_OPEN_OK|IOP_USE_OK, 0), +IOP_DESC(176, iop_znowrap, 0, IOP_OPEN_OK|IOP_USE_OK, 0), +IOP_DESC(177, iop_bigrecord, 0, IOP_OPEN_OK, 0), +IOP_DESC(178, iop_nobigrecord, 0, IOP_OPEN_OK, 0), +IOP_DESC(179, iop_rfm, IOP_VAR_SIZE, IOP_OPEN_OK, IOP_SRC_STR), +IOP_DESC(180, iop_m, 0, IOP_OPEN_OK, 0), +IOP_DESC(181, iop_utf8, 0, IOP_OPEN_OK, 0), +IOP_DESC(182, iop_utf16, 0, IOP_OPEN_OK, 0), +IOP_DESC(183, iop_utf16be, 0, IOP_OPEN_OK, 0), +IOP_DESC(184, iop_utf16le, 0, IOP_OPEN_OK, 0), +IOP_DESC(185, iop_pad, SIZEOF(int4), IOP_OPEN_OK, IOP_SRC_INT), +IOP_DESC(186, iop_chset, IOP_VAR_SIZE, IOP_OPEN_OK, IOP_SRC_STR), +IOP_DESC(187, iop_morereadtime, SIZEOF(int4), IOP_OPEN_OK | IOP_USE_OK, IOP_SRC_INT), +IOP_DESC(188, iop_shell, IOP_VAR_SIZE, IOP_OPEN_OK, IOP_SRC_STR), +IOP_DESC(189, iop_command, IOP_VAR_SIZE, IOP_OPEN_OK, IOP_SRC_STR), +IOP_DESC(190, iop_stderr, IOP_VAR_SIZE, IOP_OPEN_OK, IOP_SRC_STR), +IOP_DESC(191, iop_independent, 0, IOP_OPEN_OK, 0), +IOP_DESC(192, iop_parse, 0, IOP_OPEN_OK, 0), +IOP_DESC(193, n_iops, 0, 0, 0) diff --git a/sr_port/iop_parms_size.c b/sr_port/iop_parms_size.c new file mode 100644 index 0000000..b1d14da --- /dev/null +++ b/sr_port/iop_parms_size.c @@ -0,0 +1,23 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io_params.h" + +/* io_params_size contains a table of argument sizes for each io_param */ +/* enumerated in io_params. */ + +#define IOP_DESC(a,b,c,d,e) c + +LITDEF unsigned char io_params_size[] = +{ +#include "iop.h" +}; diff --git a/sr_port/iorm_wtff.c b/sr_port/iorm_wtff.c new file mode 100644 index 0000000..7eef14a --- /dev/null +++ b/sr_port/iorm_wtff.c @@ -0,0 +1,31 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" + +#define FORM_FEED "\014" +GBLREF io_pair io_curr_device; + +void iorm_wtff(void) +{ + mstr temp; + io_desc *iod; + + iod = io_curr_device.out; + iorm_flush(iod); + temp.len = SIZEOF(FORM_FEED) - 1; + temp.addr = FORM_FEED; + iorm_write(&temp); + iorm_wteol(1,iod); + iod->dollar.x = 0; + iod->dollar.y = 0; +} diff --git a/sr_port/iorm_wtone.c b/sr_port/iorm_wtone.c new file mode 100644 index 0000000..aa358a3 --- /dev/null +++ b/sr_port/iorm_wtone.c @@ -0,0 +1,63 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" +#ifdef UNICODE_SUPPORTED +#include "gtm_utf8.h" +#endif + +GBLREF io_pair io_curr_device; +GBLREF boolean_t gtm_utf8_mode; + +void iorm_wtone(int ch) +{ + mstr temp; + char c; +#ifdef UNICODE_SUPPORTED + unsigned char uni_buf[GTM_MB_LEN_MAX], *endptr; +#endif + + if (!gtm_utf8_mode || !IS_UTF_CHSET(io_curr_device.out->ochset)) + { + c = (char)ch; + temp.len = 1; + temp.addr = &c; + } +#ifdef UNICODE_SUPPORTED + else + { + switch (io_curr_device.out->ochset) + { + case CHSET_UTF8: + endptr = UTF8_WCTOMB(ch, uni_buf); + break; + case CHSET_UTF16: + /* iorm_write will write BE BOM if first write */ + /* continue as if UTF16BE */ + case CHSET_UTF16BE: + endptr = UTF16BE_WCTOMB(ch, uni_buf); + break; + case CHSET_UTF16LE: + endptr = UTF16LE_WCTOMB(ch, uni_buf); + break; + default: + GTMASSERT; + } + temp.addr = (char *)uni_buf; + temp.len = INTCAST(endptr - uni_buf); + assert(0 < temp.len); /* we validated the code point already in op_wtone() */ + } +#endif + UNICODE_ONLY(temp.char_len = 1;) + iorm_write(&temp); + return; +} diff --git a/sr_port/iormdefsp.h b/sr_port/iormdefsp.h new file mode 100644 index 0000000..2219de2 --- /dev/null +++ b/sr_port/iormdefsp.h @@ -0,0 +1,24 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef IORMDEFSP_H +#define IORMDEFSP_H + +#define EBCDIC_RMEOL "\25" /* #pragma(suspend) not working in macros */ +#define ASCII_RMEOL "\n" + +#if defined(KEEP_zOS_EBCDIC) || defined(VMS) +#define RMEOL ((ascii != iod->out_code_set) ? EBCDIC_RMEOL : ASCII_RMEOL ) +#else +#define RMEOL ASCII_RMEOL +#endif + +#endif /* IORMDEFSP_H */ diff --git a/sr_port/iosocket_bind.c b/sr_port/iosocket_bind.c new file mode 100644 index 0000000..7daf73e --- /dev/null +++ b/sr_port/iosocket_bind.c @@ -0,0 +1,175 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iosocket_bind.c */ +#include "mdef.h" +#include +#include "gtm_time.h" +#include "gtm_socket.h" +#include "gtm_inet.h" +#include +#include "gtm_stdio.h" +#include "gtm_string.h" +#include "gt_timer.h" +#include "io.h" +#include "iotimer.h" +#include "iotcpdef.h" +#include "iosocketdef.h" +#include "iotcproutine.h" + +#define BOUND "BOUND" + +GBLREF tcp_library_struct tcp_routines; + +boolean_t iosocket_bind(socket_struct *socketptr, int4 timepar, boolean_t update_bufsiz) +{ + int temp_1 = 1; + char *errptr; + int4 errlen, msec_timeout, real_errno; + short len; + in_port_t actual_port; + boolean_t no_time_left = FALSE; + d_socket_struct *dsocketptr; + ABS_TIME cur_time, end_time; + GTM_SOCKLEN_TYPE addrlen; + GTM_SOCKLEN_TYPE sockbuflen; + + error_def(ERR_SOCKINIT); + error_def(ERR_GETSOCKOPTERR); + error_def(ERR_SETSOCKOPTERR); + error_def(ERR_GETSOCKNAMERR); + + dsocketptr = socketptr->dev; + assert(NULL != dsocketptr); + dsocketptr->dollar_key[0] = '\0'; + if (timepar != NO_M_TIMEOUT) + { + msec_timeout = timeout2msec(timepar); + sys_get_curr_time(&cur_time); + add_int_to_abs_time(&cur_time, msec_timeout, &end_time); + } + + do + { + if (1 != temp_1) + tcp_routines.aa_close(socketptr->sd); + if (-1 == (socketptr->sd = tcp_routines.aa_socket(AF_INET, SOCK_STREAM, 0))) + { + real_errno = errno; + errptr = (char *)STRERROR(real_errno); + errlen = STRLEN(errptr); + rts_error(VARLSTCNT(5) ERR_SOCKINIT, 3, real_errno, errlen, errptr); + return FALSE; + } + temp_1 = 1; + if (-1 == tcp_routines.aa_setsockopt(socketptr->sd, + SOL_SOCKET, SO_REUSEADDR, &temp_1, SIZEOF(temp_1))) + { + real_errno = errno; + errptr = (char *)STRERROR(real_errno); + errlen = STRLEN(errptr); + rts_error(VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, + RTS_ERROR_LITERAL("SO_REUSEADDR"), real_errno, errlen, errptr); + return FALSE; + } +#ifdef TCP_NODELAY + temp_1 = socketptr->nodelay ? 1 : 0; + if (-1 == tcp_routines.aa_setsockopt(socketptr->sd, + IPPROTO_TCP, TCP_NODELAY, &temp_1, SIZEOF(temp_1))) + { + real_errno = errno; + errptr = (char *)STRERROR(real_errno); + errlen = STRLEN(errptr); + rts_error(VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, + RTS_ERROR_LITERAL("TCP_NODELAY"), real_errno, errlen, errptr); + return FALSE; + } +#endif + if (update_bufsiz) + { + if (-1 == tcp_routines.aa_setsockopt(socketptr->sd, + SOL_SOCKET, SO_RCVBUF, &socketptr->bufsiz, SIZEOF(socketptr->bufsiz))) + { + real_errno = errno; + errptr = (char *)STRERROR(real_errno); + errlen = STRLEN(errptr); + rts_error(VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, + RTS_ERROR_LITERAL("SO_RCVBUF"), real_errno, errlen, errptr); + return FALSE; + } + } else + { + sockbuflen = SIZEOF(socketptr->bufsiz); + if (-1 == tcp_routines.aa_getsockopt(socketptr->sd, + SOL_SOCKET, SO_RCVBUF, &socketptr->bufsiz, &sockbuflen)) + { + real_errno = errno; + errptr = (char *)STRERROR(real_errno); + errlen = STRLEN(errptr); + rts_error(VARLSTCNT(7) ERR_GETSOCKOPTERR, 5, + RTS_ERROR_LITERAL("SO_RCVBUF"), real_errno, errlen, errptr); + return FALSE; + } + } + temp_1 = tcp_routines.aa_bind(socketptr->sd, + (struct sockaddr *)&socketptr->local.sin, SIZEOF(struct sockaddr)); + if (temp_1 < 0) + { + real_errno = errno; + no_time_left = TRUE; + switch (real_errno) + { + case EADDRINUSE: + if (NO_M_TIMEOUT != timepar) + { + sys_get_curr_time(&cur_time); + cur_time = sub_abs_time(&end_time, &cur_time); + if (cur_time.at_sec > 0) + no_time_left = FALSE; + } + break; + case EINTR: + break; + default: + errptr = (char *)STRERROR(real_errno); + errlen = STRLEN(errptr); + rts_error(VARLSTCNT(5) ERR_SOCKINIT, 3, real_errno, errlen, errptr); + break; + } + if (no_time_left) + return FALSE; + hiber_start(100); + } + } while (temp_1 < 0); + + addrlen = SIZEOF(socketptr->local.sin); + if (-1 == tcp_routines.aa_getsockname(socketptr->sd, (struct sockaddr *)&socketptr->local.sin, &addrlen)) + { + real_errno = errno; + errptr = (char *)STRERROR(real_errno); + errlen = STRLEN(errptr); + rts_error(VARLSTCNT(5) ERR_GETSOCKNAMERR, 3, real_errno, errlen, errptr); + return FALSE; + } + actual_port = GTM_NTOHS(socketptr->local.sin.sin_port); + if (0 == socketptr->local.port) + socketptr->local.port = actual_port; + assert(socketptr->local.port == actual_port); + socketptr->state = socket_bound; + len = SIZEOF(BOUND) - 1; + memcpy(&dsocketptr->dollar_key[0], BOUND, len); + dsocketptr->dollar_key[len++] = '|'; + memcpy(&dsocketptr->dollar_key[len], socketptr->handle, socketptr->handle_len); + len += socketptr->handle_len; + dsocketptr->dollar_key[len++] = '|'; + SPRINTF(&dsocketptr->dollar_key[len], "%d", socketptr->local.port); + return TRUE; +} diff --git a/sr_port/iosocket_close.c b/sr_port/iosocket_close.c new file mode 100644 index 0000000..f785ec8 --- /dev/null +++ b/sr_port/iosocket_close.c @@ -0,0 +1,126 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iosocket_close.c - close a socket connection + * Parameters- + * iod -- I/O descriptor for the current device. + * + * pp -- mval that carries the device parameters + * + */ +#include "mdef.h" + +#include "gtm_string.h" +#include "gtm_iconv.h" +#include "gtm_stdio.h" + +#include "gtm_socket.h" +#include "gtm_inet.h" + +#include "copy.h" +#include "io_params.h" +#include "io.h" +#include "iotcpdef.h" +#include "iotcproutine.h" +#include "gt_timer.h" +#include "iosocketdef.h" +#include "stringpool.h" + +GBLREF tcp_library_struct tcp_routines; +LITREF unsigned char io_params_size[]; +void iosocket_close(io_desc *iod, mval *pp) +{ + boolean_t socket_specified = FALSE; + unsigned char ch; + int handle_len; + d_socket_struct *dsocketptr; + socket_struct *socketptr; + char sock_handle[MAX_HANDLE_LEN]; + int4 ii, jj, start, end, index; + int p_offset = 0; + error_def(ERR_SOCKNOTFND); + assert(iod->type == gtmsocket); + dsocketptr = (d_socket_struct *)iod->dev_sp; + while (iop_eol != (ch = *(pp->str.addr + p_offset++))) + { + switch (ch) + { + case iop_exception: + iod->error_handler.len = *(pp->str.addr + p_offset); + iod->error_handler.addr = (char *)(pp->str.addr + p_offset + 1); + s2pool(&iod->error_handler); + break; + case iop_socket: + handle_len = (short)(*(pp->str.addr + p_offset)); + assert(handle_len > 0); + memcpy(sock_handle, (char *)(pp->str.addr + p_offset + 1), handle_len); + socket_specified = TRUE; + break; + case iop_ipchset: +#if defined(KEEP_zOS_EBCDIC) || defined(VMS) + if ( (iconv_t)0 != iod->input_conv_cd ) + { + ICONV_CLOSE_CD(iod->input_conv_cd); + } + SET_CODE_SET(iod->in_code_set, (char *)(pp->str.addr + p_offset + 1)); + if (DEFAULT_CODE_SET != iod->in_code_set) + ICONV_OPEN_CD(iod->input_conv_cd, INSIDE_CH_SET, (char *)(pp->str.addr + p_offset + 1)); +#endif + break; + case iop_opchset: +#if defined(KEEP_zOS_EBCDIC) || defined(VMS) + if ( (iconv_t)0 != iod->output_conv_cd ) + { + ICONV_CLOSE_CD(iod->output_conv_cd); + } + SET_CODE_SET(iod->out_code_set, (char *)(pp->str.addr + p_offset + 1)); + if (DEFAULT_CODE_SET != iod->out_code_set) + ICONV_OPEN_CD(iod->output_conv_cd, (char *)(pp->str.addr + p_offset + 1), INSIDE_CH_SET); +#endif + break; + default: + break; + } + p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ? + (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]); + } + if (socket_specified) + { + if (0 > (index = iosocket_handle(sock_handle, &handle_len, FALSE, dsocketptr))) + { + rts_error(VARLSTCNT(4) ERR_SOCKNOTFND, 2, handle_len, sock_handle); + return; + } + start = end = index; + } + else + { + start = dsocketptr->n_socket - 1; + end = 0; + } + for (ii = start; ii >= end; ii--) + { + socketptr = dsocketptr->socket[ii]; + tcp_routines.aa_close(socketptr->sd); + iosocket_delimiter((unsigned char *)NULL, 0, socketptr, TRUE); /* free the delimiter space */ + free(socketptr->buffer); + if (NULL != socketptr->zff.addr) + free(socketptr->zff.addr); + free(socketptr); + if (dsocketptr->current_socket >= ii) + dsocketptr->current_socket--; + for (jj = ii + 1; jj <= dsocketptr->n_socket - 1; jj++) + dsocketptr->socket[jj - 1] = dsocketptr->socket[jj]; + dsocketptr->n_socket--; + } + if (!socket_specified) + iod->state = dev_closed; +} diff --git a/sr_port/iosocket_connect.c b/sr_port/iosocket_connect.c new file mode 100644 index 0000000..6c556c3 --- /dev/null +++ b/sr_port/iosocket_connect.c @@ -0,0 +1,423 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iosocket_connect.c */ +#include "mdef.h" +#include +#include "gtm_time.h" +#include "gtm_socket.h" +#include "gtm_inet.h" +#include +#include "gtm_stdio.h" +#include "gtm_string.h" +#include "stringpool.h" +#include "gt_timer.h" +#include "io.h" +#include "iotimer.h" +#include "iotcpdef.h" +#include "iosocketdef.h" +#include "iotcproutine.h" +#include "rtnhdr.h" +#include "stack_frame.h" +#include "mv_stent.h" +#include "outofband.h" + +#define ESTABLISHED "ESTABLISHED" + +GBLREF tcp_library_struct tcp_routines; +GBLREF volatile int4 outofband; +GBLREF boolean_t dollar_zininterrupt; +GBLREF stack_frame *frame_pointer; +GBLREF unsigned char *stackbase, *stacktop, *msp, *stackwarn; +GBLREF mv_stent *mv_chain; +GBLREF int socketus_interruptus; +GBLREF d_socket_struct *newdsocket; /* in case jobinterrupt */ +GBLREF int4 gtm_max_sockets; + + +boolean_t iosocket_connect(socket_struct *socketptr, int4 timepar, boolean_t update_bufsiz) +{ + int temp_1; + char *errptr; + int4 errlen, msec_timeout, save_errno, last_errno; + int d_socket_struct_len, res, nfds, sockerror; + fd_set writefds; + boolean_t no_time_left = FALSE; + boolean_t need_connect, need_socket, need_select; + short len; + io_desc *iod; + d_socket_struct *dsocketptr, *real_dsocketptr; + socket_interrupt *sockintr, *real_sockintr; + ABS_TIME cur_time, end_time; + struct timeval *sel_time; + mv_stent *mv_zintdev; + GTM_SOCKLEN_TYPE sockbuflen; + + error_def(ERR_SOCKINIT); + error_def(ERR_OPENCONN); + error_def(ERR_TEXT); + error_def(ERR_GETSOCKOPTERR); + error_def(ERR_SETSOCKOPTERR); + error_def(ERR_ZINTRECURSEIO); + error_def(ERR_STACKCRIT); + error_def(ERR_STACKOFLOW); + + SOCKET_DEBUG(PRINTF("socconn: ************* Entering socconn - timepar: %d\n",timepar); DEBUGSOCKFLUSH); + /* check for validity */ + dsocketptr = socketptr->dev; + assert(NULL != dsocketptr); + sockintr = &dsocketptr->sock_save_state; + iod = dsocketptr->iod; + real_dsocketptr = (d_socket_struct *)iod->dev_sp; /* not newdsocket which is not saved on error */ + real_sockintr = &real_dsocketptr->sock_save_state; + + dsocketptr->dollar_key[0] = '\0'; + real_dsocketptr->dollar_key[0] = '\0'; + need_socket = need_connect = TRUE; + need_select = FALSE; + + /* Check for restart */ + if (dsocketptr->mupintr) + { /* We have a pending read restart of some sort - check we aren't recursing on this device */ + if (sockwhich_invalid == sockintr->who_saved) + GTMASSERT; /* Interrupt should never have an invalid save state */ + if (dollar_zininterrupt) + rts_error(VARLSTCNT(1) ERR_ZINTRECURSEIO); + if (sockwhich_connect != sockintr->who_saved) + GTMASSERT; /* ZINTRECURSEIO should have caught */ + SOCKET_DEBUG(PRINTF("socconn: *#*#*#*#*#*#*# Restarted interrupted connect\n"); DEBUGSOCKFLUSH); + mv_zintdev = io_find_mvstent(iod, FALSE); + if (mv_zintdev) + { + if (sockintr->end_time_valid) + /* Restore end_time for timeout */ + end_time = sockintr->end_time; + if (socket_connect_inprogress == socketptr->state && FD_INVALID != socketptr->sd) + { + need_select = TRUE; + need_socket = need_connect = FALSE; /* sd still good */ + } + + /* Done with this mv_stent. Pop it off if we can, else mark it inactive. */ + if (mv_chain == mv_zintdev) + POP_MV_STENT(); /* pop if top of stack */ + else + { /* else mark it unused, see iosocket_open for use */ + mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid = FALSE; + mv_zintdev->mv_st_cont.mvs_zintdev.io_ptr = NULL; + } + SOCKET_DEBUG(PRINTF("socconn: mv_stent found - endtime: %d/%d\n", end_time.at_sec, end_time.at_usec); + DEBUGSOCKFLUSH); + } else + SOCKET_DEBUG(PRINTF("socconn: no mv_stent found !!\n"); DEBUGSOCKFLUSH); + real_dsocketptr->mupintr = dsocketptr->mupintr = FALSE; + real_sockintr->who_saved = sockintr->who_saved = sockwhich_invalid; + } else if (timepar != NO_M_TIMEOUT) + { + msec_timeout = timeout2msec(timepar); + sys_get_curr_time(&cur_time); + add_int_to_abs_time(&cur_time, msec_timeout, &end_time); + } + real_sockintr->end_time_valid = sockintr->end_time_valid = FALSE; + last_errno = 0; + + do + { + if (need_socket && FD_INVALID != socketptr->sd) + { + tcp_routines.aa_close(socketptr->sd); + socketptr->sd = FD_INVALID; + } + assert(FD_INVALID == -1); + if (need_socket) + { + if (-1 == (socketptr->sd = tcp_routines.aa_socket(AF_INET, SOCK_STREAM, 0))) + { + errptr = (char *)STRERROR(errno); + errlen = STRLEN(errptr); + rts_error(VARLSTCNT(5) ERR_SOCKINIT, 3, errno, errlen, errptr); + return FALSE; + } + need_socket = FALSE; + temp_1 = 1; + if (-1 == tcp_routines.aa_setsockopt(socketptr->sd, SOL_SOCKET, SO_REUSEADDR, &temp_1, SIZEOF(temp_1))) + { + save_errno = errno; + errptr = (char *)STRERROR(save_errno); + errlen = STRLEN(errptr); + tcp_routines.aa_close(socketptr->sd); /* Don't leave a dangling socket around */ + socketptr->sd = FD_INVALID; + rts_error(VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, + RTS_ERROR_LITERAL("SO_REUSEADDR"), save_errno, errlen, errptr); + return FALSE; + } +#ifdef TCP_NODELAY + temp_1 = socketptr->nodelay ? 1 : 0; + if (-1 == tcp_routines.aa_setsockopt(socketptr->sd, + IPPROTO_TCP, TCP_NODELAY, &temp_1, SIZEOF(temp_1))) + { + save_errno = errno; + errptr = (char *)STRERROR(save_errno); + errlen = STRLEN(errptr); + tcp_routines.aa_close(socketptr->sd); /* Don't leave a dangling socket around */ + socketptr->sd = FD_INVALID; + rts_error(VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, + RTS_ERROR_LITERAL("TCP_NODELAY"), save_errno, errlen, errptr); + return FALSE; + } +#endif + if (update_bufsiz) + { + if (-1 == tcp_routines.aa_setsockopt(socketptr->sd, + SOL_SOCKET, SO_RCVBUF, &socketptr->bufsiz, SIZEOF(socketptr->bufsiz))) + { + save_errno = errno; + errptr = (char *)STRERROR(save_errno); + errlen = STRLEN(errptr); + tcp_routines.aa_close(socketptr->sd); /* Don't leave a dangling socket around */ + socketptr->sd = FD_INVALID; + rts_error(VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, + RTS_ERROR_LITERAL("SO_RCVBUF"), save_errno, errlen, errptr); + return FALSE; + } + } else + { + sockbuflen = SIZEOF(socketptr->bufsiz); + if (-1 == tcp_routines.aa_getsockopt(socketptr->sd, + SOL_SOCKET, SO_RCVBUF, &socketptr->bufsiz, &sockbuflen)) + { + save_errno = errno; + errptr = (char *)STRERROR(save_errno); + errlen = STRLEN(errptr); + tcp_routines.aa_close(socketptr->sd); /* Don't leave a dangling socket around */ + socketptr->sd = FD_INVALID; + rts_error(VARLSTCNT(7) ERR_GETSOCKOPTERR, 5, + RTS_ERROR_LITERAL("SO_RCVBUF"), save_errno, errlen, errptr); + return FALSE; + } + } + } + save_errno = res = 0; + if (need_connect) + { + /* Use plain connect to allow jobinterrupt */ + assert(FD_INVALID != socketptr->sd); + res = connect(socketptr->sd, (struct sockaddr *)&socketptr->remote.sin, SIZEOF(socketptr->remote.sin)); + if (res < 0) + { + save_errno = errno; + no_time_left = FALSE; + need_connect = TRUE; + switch (save_errno) + { + case EISCONN: + save_errno = 0; + res = 0; /* since it is connected already, treat as if success */ + need_connect = FALSE; + break; + case EINTR: + if (outofband && 0 != timepar) + { /* handle outofband unless zero timeout */ + save_errno = 0; + need_socket = need_connect = FALSE; + break; + } /* else fall through */ + case EINPROGRESS: + case EALREADY: +# if (defined(__osf__) && defined(__alpha)) || defined(__sun) || defined(__vms) + case EWOULDBLOCK: +# endif + need_socket = need_connect = FALSE; + if (0 != timepar) + need_select = TRUE; + /* fall through */ + case ETIMEDOUT: /* the other side bound but not listening */ + case ECONNREFUSED: + if (!no_time_left && 0 != timepar && NO_M_TIMEOUT != timepar) + { + sys_get_curr_time(&cur_time); + cur_time = sub_abs_time(&end_time, &cur_time); + if (cur_time.at_sec <= 0) + no_time_left = TRUE; + } + if (0 == timepar) + no_time_left = TRUE; + else if (!no_time_left) + { + if (ETIMEDOUT == save_errno || ECONNREFUSED == save_errno) + need_connect = need_socket = TRUE; + save_errno = 0; + res = -1; /* do the outer loop again */ + } + if (no_time_left) + save_errno = 0; + break; + default: + break; + } + } /* if connect failed */ + } + if (need_select) + { + sockerror = 0; + do + { /* unless outofband loop on select if connection continuing */ + if (NO_M_TIMEOUT == timepar) + sel_time = NULL; + else + { + sys_get_curr_time(&cur_time); + cur_time = sub_abs_time(&end_time, &cur_time); + if (cur_time.at_sec > 0) + sel_time = (struct timeval *)&cur_time; + else + { /* timed out so done */ + save_errno = res = 0; + no_time_left = TRUE; + break; + } + } + FD_ZERO(&writefds); + FD_SET(socketptr->sd, &writefds); + res = select(socketptr->sd + 1, NULL, &writefds, NULL, sel_time); + if (0 < res) + { /* check for socket error */ + sockbuflen = SIZEOF(sockerror); + res = getsockopt(socketptr->sd, SOL_SOCKET, SO_ERROR, + &sockerror, &sockbuflen); + if (0 == res && 0 == sockerror) + { /* got it */ + save_errno = 0; + break; + } else if (0 == res && 0 != sockerror) + { + if (EINTR == sockerror) + { /* loop on select */ + save_errno = 0; + continue; + } else + { /* return socket error */ + if (ECONNREFUSED == sockerror || ETIMEDOUT == sockerror) + { /* try until timeout */ + last_errno = sockerror; + save_errno = 0; + need_socket = need_connect = TRUE; + need_select = FALSE; + res = -1; + } else + save_errno = sockerror; + break; + } + } else + { + save_errno = errno; /* error on getsockopt */ + break; + } + } else if (0 == res) + { /* select timed out */ + save_errno = 0; + no_time_left = TRUE; + break; + } else if (EINTR != errno) + { + save_errno = errno; + break; + } else if (outofband) + { + save_errno = 0; + break; + } + } while (TRUE); /* do select */ + } + if (save_errno) + { + if (FD_INVALID != socketptr->sd) + { + tcp_routines.aa_close(socketptr->sd); /* Don't leave a dangling socket around */ + socketptr->sd = FD_INVALID; + } + errptr = (char *)STRERROR(save_errno); + errlen = STRLEN(errptr); + if (dev_open == iod->state) + { + iod->dollar.za = 9; + memcpy(real_dsocketptr->dollar_device, ONE_COMMA, SIZEOF(ONE_COMMA)); + memcpy(&real_dsocketptr->dollar_device[SIZEOF(ONE_COMMA) - 1], + errptr, errlen + 1); /* + 1 for null */ + } + if (socketptr->ioerror) + rts_error(VARLSTCNT(6) ERR_OPENCONN, 0, ERR_TEXT, 2, errlen, errptr); + errno = save_errno; + return FALSE; + } + if (no_time_left) + return FALSE; /* caller will close socket */ + if (res < 0 && outofband) /* if connected delay outofband */ + { + SOCKET_DEBUG(PRINTF("socconn: outofband interrupt received (%d) -- " + "queueing mv_stent for wait intr\n", outofband); DEBUGSOCKFLUSH); + if (need_connect) + { /* no connect in progress */ + tcp_routines.aa_close(socketptr->sd); /* Don't leave a dangling socket around */ + socketptr->sd = FD_INVALID; + socketptr->state = socket_created; + } else + socketptr->state = socket_connect_inprogress; + real_sockintr->who_saved = sockintr->who_saved = sockwhich_connect; + if (NO_M_TIMEOUT != timepar) + { + real_sockintr->end_time = sockintr->end_time = end_time; + real_sockintr->end_time_valid = sockintr->end_time_valid = TRUE; + } else + real_sockintr->end_time_valid = sockintr->end_time_valid = FALSE; + real_sockintr->newdsocket = sockintr->newdsocket = newdsocket; + real_dsocketptr->mupintr = dsocketptr->mupintr = TRUE; + d_socket_struct_len = SIZEOF(d_socket_struct) + + (SIZEOF(socket_struct) * (gtm_max_sockets - 1)); + ENSURE_STP_FREE_SPACE(d_socket_struct_len); + PUSH_MV_STENT(MVST_ZINTDEV); + mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = FALSE; + mv_chain->mv_st_cont.mvs_zintdev.io_ptr = NULL; + mv_chain->mv_st_cont.mvs_zintdev.socketptr = socketptr; /* for sd and to free structure */ + mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.len = d_socket_struct_len; + mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr = (char *)stringpool.free; + memcpy (stringpool.free, (unsigned char *)newdsocket, d_socket_struct_len); + stringpool.free += d_socket_struct_len; + mv_chain->mv_st_cont.mvs_zintdev.io_ptr = iod; + mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = TRUE; + socketus_interruptus++; + SOCKET_DEBUG(PRINTF("socconn: mv_stent queued - endtime: %d/%d interrupts: %d\n", + end_time.at_sec, end_time.at_usec, socketus_interruptus); + DEBUGSOCKFLUSH); + outofband_action(FALSE); + GTMASSERT; /* Should *never* return from outofband_action */ + return FALSE; /* For the compiler.. */ + } + hiber_start(100); + } while (res < 0); + + /* handle the local information later. + SPRINTF(socketptr->local.saddr_ip, "%s", tcp_routines.aa_inet_ntoa(socketptr->remote.sin.sin_addr)); + socketptr->local.port = GTM_NTOHS(socketptr->remote.sin.sin_port); + */ + socketptr->state = socket_connected; + socketptr->first_read = socketptr->first_write = TRUE; + /* update dollar_key */ + len = SIZEOF(ESTABLISHED) - 1; + memcpy(&dsocketptr->dollar_key[0], ESTABLISHED, len); + dsocketptr->dollar_key[len++] = '|'; + memcpy(&dsocketptr->dollar_key[len], socketptr->handle, socketptr->handle_len); + len += socketptr->handle_len; + dsocketptr->dollar_key[len++] = '|'; + strcpy(&dsocketptr->dollar_key[len], socketptr->remote.saddr_ip); /* Also copies in trailing null */ + + return TRUE; +} diff --git a/sr_port/iosocket_create.c b/sr_port/iosocket_create.c new file mode 100644 index 0000000..009b7d4 --- /dev/null +++ b/sr_port/iosocket_create.c @@ -0,0 +1,164 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iosocket_create.c */ +/* this module takes care of + * 1. allocate the space + * 2. for passive: local.sin.sin_addr.s_addr & local.sin.sin_port + * for active : remote.sin.sin_addr.s_addr & remote.sin.sin_port + * for $principal: via getsockname and getsockpeer + * 3. socketptr->protocol + * 4. socketptr->sd (initialized to -1) unless already open via inetd + * 5. socketptr->passive + * 6. socketptr->state (initialized to created) unless already open + */ +#include "mdef.h" + +#include +#include "gtm_ctype.h" +#include "gtm_stdio.h" +#include "gtm_string.h" +#include "gtm_netdb.h" +#include "gtm_socket.h" +#include "gtm_inet.h" + +#include "io.h" +#include "iotcproutine.h" +#include "iotcpdef.h" +#include "gt_timer.h" +#include "iosocketdef.h" +#include "min_max.h" +#include "gtm_caseconv.h" + +#ifdef __osf__ +/* Tru64 does not have the prototype for "hstrerror" even though the function is available in the library. + * Until we revamp the TCP communications setup stuff to use the new(er) POSIX definitions, we cannot move + * away from "hstrerror". Declare prototype for this function in Tru64 manually until then. + */ +const char *hstrerror(int err); +#endif + +GBLREF tcp_library_struct tcp_routines; + +socket_struct *iosocket_create(char *sockaddr, uint4 bfsize, int file_des) +{ + socket_struct *socketptr; + bool passive = FALSE; + unsigned short port; + int ii, save_errno, tmplen; + GTM_SOCKLEN_TYPE socknamelen; + char temp_addr[SA_MAXLITLEN], addr[SA_MAXLEN], tcp[4], *adptr; + const char *errptr; + + error_def(ERR_INVPORTSPEC); + error_def(ERR_INVADDRSPEC); + error_def(ERR_PROTNOTSUP); + error_def(ERR_TEXT); + error_def(ERR_GETSOCKNAMERR); + + socketptr = (socket_struct *)malloc(SIZEOF(socket_struct)); + memset(socketptr, 0, SIZEOF(socket_struct)); + if (0 > file_des) + { /* no socket descriptor yet */ + if (SSCANF(sockaddr, "%[^:]:%hu:%3[^:]", temp_addr, &port, tcp) < 3) + { + passive = TRUE; + socketptr->local.sin.sin_addr.s_addr = INADDR_ANY; + if(SSCANF(sockaddr, "%hu:%3[^:]", &port, tcp) < 2) + { + free(socketptr); + rts_error(VARLSTCNT(1) ERR_INVPORTSPEC); + return NULL; + } + socketptr->local.sin.sin_port = GTM_HTONS(port); + socketptr->local.sin.sin_family = AF_INET; + socketptr->local.port = port; + } else + { + for (ii = 0; ISDIGIT_ASCII(temp_addr[ii]) || '.' == temp_addr[ii]; ii++) /* NOTE: only ASCII digits */ + ; /* allowed for dotted notation address */ + if (temp_addr[ii] != '\0') + { + SPRINTF(socketptr->remote.saddr_lit, "%s", temp_addr); + adptr = iotcp_name2ip(temp_addr); + if (NULL == adptr) + { +#if !defined(__hpux) && !defined(__MVS__) + errptr = HSTRERROR(h_errno); + rts_error(VARLSTCNT(6) ERR_INVADDRSPEC, 0, ERR_TEXT, 2, LEN_AND_STR(errptr)); +#else + /* Grumble grumble HPUX and z/OS don't have hstrerror() */ + rts_error(VARLSTCNT(1) ERR_INVADDRSPEC); +#endif + free(socketptr); + return NULL; + } + + SPRINTF(addr, "%s", adptr); + } else + SPRINTF(addr, "%s", temp_addr); + if ((unsigned int)-1 == (socketptr->remote.sin.sin_addr.s_addr = tcp_routines.aa_inet_addr(addr))) + { /* Errno not set by inet_addr() */ + free(socketptr); + rts_error(VARLSTCNT(1) ERR_INVADDRSPEC); + return NULL; + } + socketptr->remote.sin.sin_port = GTM_HTONS(port); + socketptr->remote.sin.sin_family = AF_INET; + socketptr->remote.port = port; + SPRINTF(socketptr->remote.saddr_ip, "%s", addr); + } + lower_to_upper((uchar_ptr_t)tcp, (uchar_ptr_t)tcp, SIZEOF("TCP") - 1); + if (0 == MEMCMP_LIT(tcp, "TCP")) + socketptr->protocol = socket_tcpip; + else + { + free(socketptr); + rts_error(VARLSTCNT(4) ERR_PROTNOTSUP, 2, MIN(strlen(tcp), SIZEOF("TCP") - 1), tcp); + return NULL; + } + socketptr->sd = FD_INVALID; /* don't mess with 0 */ + socketptr->state = socket_created; /* Is this really useful? */ + } else + { /* socket already setup by inetd */ + socketptr->sd = file_des; + socknamelen = SIZEOF(socketptr->local.sin); + if (-1 == tcp_routines.aa_getsockname(socketptr->sd, (struct sockaddr *)&socketptr->local.sin, &socknamelen)) + { + save_errno = errno; + errptr = (char *)STRERROR(save_errno); + tmplen = STRLEN(errptr); + rts_error(VARLSTCNT(5) ERR_GETSOCKNAMERR, 3, save_errno, tmplen, errptr); + free(socketptr); + return NULL; + } + socketptr->local.port = GTM_NTOHS(socketptr->local.sin.sin_port); + socknamelen = SIZEOF(socketptr->remote.sin); + if (-1 == getpeername(socketptr->sd, (struct sockaddr *)&socketptr->remote.sin, (GTM_SOCKLEN_TYPE *)&socknamelen)) + { + save_errno = errno; + errptr = (char *)STRERROR(save_errno); + tmplen = STRLEN(errptr); + rts_error(VARLSTCNT(5) ERR_GETSOCKNAMERR, 3, save_errno, tmplen, errptr); /* need new error */ + free(socketptr); + return NULL; + } + socketptr->remote.port = GTM_NTOHS(socketptr->remote.sin.sin_port); + socketptr->state = socket_connected; + socketptr->protocol = socket_tcpip; + } + socketptr->buffer = (char *)malloc(bfsize); + socketptr->buffer_size = bfsize; + socketptr->buffered_length = socketptr->buffered_offset = 0; + socketptr->passive = passive; + socketptr->moreread_timeout = DEFAULT_MOREREAD_TIMEOUT; + return socketptr; +} diff --git a/sr_port/iosocket_delimiter.c b/sr_port/iosocket_delimiter.c new file mode 100644 index 0000000..eb74541 --- /dev/null +++ b/sr_port/iosocket_delimiter.c @@ -0,0 +1,180 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iosocket_delimiter.c */ + +#include "mdef.h" + +#include "gtm_socket.h" +#include "gtm_string.h" + +#include "gtm_inet.h" + +#include "io.h" +#include "iottdef.h" +#include "iotcpdef.h" +#include "gt_timer.h" +#include "iosocketdef.h" +#include "gtm_conv.h" +#include "gtm_utf8.h" + +GBLREF boolean_t gtm_utf8_mode; +GBLREF UConverter *chset_desc[]; + +error_def(ERR_DELIMSIZNA); + +boolean_t iosocket_delimiter(unsigned char *delimiter_buffer, int4 delimiter_len, socket_struct *socketptr, boolean_t rm) +{ + int counter, ii, c_len; + unsigned char *c, *top, delimiter[MAX_DELIM_LEN + 1]; + + /* free the previous delimiters if any */ + for (ii = 0; ii < socketptr->n_delimiter; ii++) + { + free(socketptr->delimiter[ii].addr); + if (socketptr->idelimiter[ii].addr != socketptr->delimiter[ii].addr) + free(socketptr->idelimiter[ii].addr); + } + if (0 < socketptr->n_delimiter && socketptr->odelimiter0.addr != socketptr->delimiter[0].addr) + free(socketptr->odelimiter0.addr); + socketptr->n_delimiter = 0; + socketptr->delim0containsLF = FALSE; + if (rm) + return TRUE; + /* fill in the new delimiters */ + counter = ii = 0; + c = &delimiter_buffer[0]; + top = c + delimiter_len; + while ((c < top) && (ii < MAX_N_DELIMITER)) + { + switch(delimiter[counter++] = *c++) + { + case ':' : + /* end of the previous delimiter and start the next one */ + if (1 < counter) + { + if (gtm_utf8_mode) + { /* Check if delimiter has any invalid UTF-8 characters */ + c_len = utf8_len_strict(delimiter, counter - 1); + } else + c_len = counter - 1; + socketptr->delimiter[ii].addr = (char *)malloc(counter - 1); + memcpy(socketptr->delimiter[ii].addr, delimiter, counter - 1); + socketptr->delimiter[ii].len = counter - 1; + UNICODE_ONLY(socketptr->delimiter[ii].char_len = c_len); + socketptr->idelimiter[ii] = socketptr->delimiter[ii]; + if (0 == ii) + socketptr->odelimiter0 = socketptr->delimiter[0]; + socketptr->n_delimiter++; + ii++; + } + counter = 0; + break; + case '/' : /* escape */ + delimiter[counter - 1] = *c++; /* Escaping a delim character doesn't appear to be documented + anywhere. Nonetheless, the assumption is that the only use is to + escape ':' which is a one byte character in UTF-8. So, this logic + will work. However, if there is a change in what can be escaped + (say, escape a character that is > 1 byte in length), this logic + has to change. Vinaya, 2007/09/07 + */ + break; + case NATIVE_LF : /* Only NATIVE_LF is accepted as line terminator although Unicode defines other + line terminators */ + if (0 == ii) + socketptr->delim0containsLF = TRUE; + break; + default: + /* look at the next character */ + break; + } + if ((c == top) && (0 < counter)) + { + if (gtm_utf8_mode) /* Check if delimiter has any invalid UTF-8 character */ + c_len = utf8_len_strict(delimiter, counter); /* triggers badchar error for invalid sequence */ + else + c_len = counter; + socketptr->delimiter[ii].addr = (char *)malloc(counter); + memcpy(socketptr->delimiter[ii].addr, delimiter, counter); + socketptr->delimiter[ii].len = counter; + UNICODE_ONLY(socketptr->delimiter[ii].char_len = c_len); + socketptr->idelimiter[ii] = socketptr->delimiter[ii]; + if (0 == ii) + socketptr->odelimiter0 = socketptr->delimiter[0]; + socketptr->n_delimiter++; + ii++; + } + if (counter > MAX_DELIM_LEN) + { + rts_error(VARLSTCNT(1) ERR_DELIMSIZNA); + return FALSE; + } + } + return TRUE; +} + +void iosocket_delim_conv(socket_struct *socketptr, gtm_chset_t to_chset) +{ + static char *conv_buff = NULL; + int conv_len, delim_index, new_delim_len; + + assert(0 != socketptr->n_delimiter); + assert(CHSET_UTF16BE == to_chset || CHSET_UTF16LE == to_chset); + assert(gtm_utf8_mode); + + if (NULL == conv_buff) + conv_buff = malloc(MAX_DELIM_LEN); + for (delim_index = 0; delim_index < socketptr->n_delimiter; delim_index++) + { + conv_len = MAX_DELIM_LEN; + new_delim_len = gtm_conv(chset_desc[CHSET_UTF8], chset_desc[to_chset], &socketptr->delimiter[delim_index], + conv_buff, &conv_len); + assert(MAX_DELIM_LEN > new_delim_len); + if (MAX_DELIM_LEN < new_delim_len) + { + rts_error(VARLSTCNT(1) ERR_DELIMSIZNA); + return; + } + socketptr->idelimiter[delim_index].len = new_delim_len; + UNICODE_ONLY(socketptr->idelimiter[delim_index].char_len = socketptr->delimiter[delim_index].char_len); + socketptr->idelimiter[delim_index].addr = malloc(new_delim_len); + memcpy(socketptr->idelimiter[delim_index].addr, conv_buff, new_delim_len); + } + return; +} + +void iosocket_delimiter_copy(socket_struct *from, socket_struct *to) +{ + int delim_index; + + if (0 == (to->n_delimiter = from->n_delimiter)) + return; + for (delim_index = 0; delim_index < from->n_delimiter; delim_index++) + { + to->delimiter[delim_index] = from->delimiter[delim_index]; /* copy all fields */ + to->delimiter[delim_index].addr = malloc(from->delimiter[delim_index].len); /* re-allocate buffer */ + memcpy(to->delimiter[delim_index].addr, from->delimiter[delim_index].addr, from->delimiter[delim_index].len); + to->idelimiter[delim_index] = to->delimiter[delim_index]; /* copy all fields */ + if (from->delimiter[delim_index].addr != from->idelimiter[delim_index].addr) + { /* has been converted */ + to->idelimiter[delim_index].addr = malloc(from->idelimiter[delim_index].len); /* re-allocate buffer */ + memcpy(to->idelimiter[delim_index].addr, from->idelimiter[delim_index].addr, + from->idelimiter[delim_index].len); + } + } + to->odelimiter0 = to->delimiter[0]; + if (from->odelimiter0.addr != from->delimiter[0].addr) + { /* has been converted */ + to->odelimiter0.addr = malloc(from->odelimiter0.len); + memcpy(to->odelimiter0.addr, from->odelimiter0.addr, from->odelimiter0.len); + } + return; +} diff --git a/sr_port/iosocket_flush.c b/sr_port/iosocket_flush.c new file mode 100644 index 0000000..edb78fd --- /dev/null +++ b/sr_port/iosocket_flush.c @@ -0,0 +1,72 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iosocket_flush.c */ + +#include "mdef.h" + +#include "gtm_socket.h" +#include "gtm_inet.h" +#include +#include +#include "gtm_stdio.h" +#include "gtm_string.h" + +#include "io.h" +#include "iotcpdef.h" +#include "iotcproutine.h" +#include "gt_timer.h" +#include "iosocketdef.h" + +GBLREF tcp_library_struct tcp_routines; + +void iosocket_flush(io_desc *iod) +{ +#ifdef C9A06001531 + /* pending change request C9A06001531 */ + + d_socket_struct *dsocketptr; + socket_struct *socketptr; + int on = 1, off = 0; + char *errptr; + int4 errlen; + + error_def(ERR_SOCKWRITE); + error_def(ERR_TEXT); + error_def(ERR_CURRSOCKOFR); + + assert(gtmsocket == iod->type); + + dsocketptr = (d_socket_struct *)iod->dev_sp; + socketptr = dsocketptr->socket[dsocketptr->current_socket]; + + if (dsocketptr->current_socket >= dsocketptr->n_socket) + { + rts_error(VARLSTCNT(4) ERR_CURRSOCKOFR, 2, dsocketptr->current_socket, dsocketptr->n_socket); + return; + } + memcpy(dsocketptr->dollar_device, "0", SIZEOF("0")); + if( -1 == tcp_routines.aa_setsockopt(socketptr->sd, SOL_SOCKET, TCP_NODELAY, &on, SIZEOF(on)) || + (-1 == tcp_routines.aa_setsockopt(socketptr->sd, SOL_SOCKET, TCP_NODELAY, &off, SIZEOF(off)))) + { + errptr = (char *)STRERROR(errno); + errlen = strlen(errptr); + iod->dollar.za = 9; + MEMCPY_LIT(dsocketptr->dollar_device, "1,"); + memcpy(&dsocketptr->dollar_device[SIZEOF("1,") - 1], errptr, errlen + 1); /* we want the null */ + if (socketptr->ioerror) + rts_error(VARLSTCNT(6) ERR_SOCKWRITE, 0, ERR_TEXT, 2, errlen, errptr); + return; + } + +#endif + return; +} diff --git a/sr_port/iosocket_handle.c b/sr_port/iosocket_handle.c new file mode 100644 index 0000000..754d7a5 --- /dev/null +++ b/sr_port/iosocket_handle.c @@ -0,0 +1,79 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iosocket_handle.c */ + +/* newhandle == TRUE + * + * create a new handle + * return the old dsocketptr->n_socket + * (i.e. number of socket) + * (i.e. index of the new socket) + * + * newhandle == FALSE + * + * check if the handle exist + * yes ==> return the index + * no ==> return -1 + * (return the number of sockets would provide more information, but can cause + * confliction with index = 0 + * 0 ==> socket exist and index is 0 + * 0 ==> there are 0 sockets exist) + */ + +#include "mdef.h" + +#include "gtm_time.h" +#include "gtm_socket.h" +#include "gtm_inet.h" +#include "gtm_stdio.h" +#include "gtm_string.h" + +#include "io_params.h" +#include "io.h" +#include "iotcproutine.h" +#include "iotcpdef.h" +#include "gt_timer.h" +#include "iosocketdef.h" + +int4 iosocket_handle(char *handle, int *len, boolean_t newhandle, d_socket_struct *dsocketptr) +{ + boolean_t unique; + int4 ii, counter = 0, loop_flag = 1; + + while(loop_flag) + { + if (newhandle) + { + SPRINTF(handle, "h%ld%d", time((time_t *)0), counter); + *len = (short)strlen(handle); + } + ii = 0; + unique = TRUE; + while(ii < dsocketptr->n_socket) + { + if ((*len == dsocketptr->socket[ii]->handle_len) && + (0 == memcmp(handle, dsocketptr->socket[ii]->handle, *len))) + { + unique = FALSE; + break; + } + ii++; + } + if (!newhandle) + return (unique ? -1 : ii); + if (unique) + return ii; + counter++; + } + /* it will never reach here */ + return -1; +} diff --git a/sr_port/iosocket_iocontrol.c b/sr_port/iosocket_iocontrol.c new file mode 100644 index 0000000..81d97b8 --- /dev/null +++ b/sr_port/iosocket_iocontrol.c @@ -0,0 +1,107 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iosocket_iocontrol.c */ + +#include "mdef.h" + +#include "gtm_socket.h" +#include "gtm_inet.h" +#include "gtm_stdio.h" +#include "gtm_string.h" + +#include "io.h" +#include "iotimer.h" +#include "iotcpdef.h" +#include "gt_timer.h" +#include "iosocketdef.h" +#include "gtm_caseconv.h" +#include "stringpool.h" + +GBLREF spdesc stringpool; +GBLREF io_pair io_curr_device; + +error_def(ERR_INVCTLMNE); + +void iosocket_iocontrol(mstr *d) +{ + char action[MAX_DEVCTL_LENGTH]; + unsigned short depth; /* serve as depth for LISTEN and timeout for WAIT */ + int length, n, timeout; + + if (0 == d->len) + return; + /* The contents of d->addr are passed to us from op_iocontrol. That routine sets up these parms + * but does not zero terminate the string we are passing to SSCANF. If this is not a complex parm + * with parens (as is the case with WRITE /WAIT type statements), SSCANF() will get into trouble + * if the stringpool contains extra junk. For that reason, we now add a null terminator and make + * sure the argument is not too big to parse into our buffer above. + */ + assert(d->addr == (char *)stringpool.free); /* Verify string is where we think it is so we don't corrupt something */ + assert(MAX_DEVCTL_LENGTH > d->len); + assert(IS_IN_STRINGPOOL(d->addr, d->len)); + *(d->addr + d->len) = '\0'; + if (0 == (n = SSCANF(d->addr, "%[^(](%hu)", &action[0], &depth))) + memcpy(&action[0], d->addr, d->len); + if (0 == (length = STRLEN(&action[0]))) + return; + lower_to_upper((uchar_ptr_t)&action[0], (uchar_ptr_t)&action[0], length); + if (0 == memcmp(&action[0], "LISTEN", length)) + { + if (2 > n) + depth = DEFAULT_LISTEN_DEPTH; + iosocket_listen(io_curr_device.out, depth); + } else if (0 == memcmp(&action[0], "WAIT", length)) + { + timeout = depth; + if (2 > n) + timeout = NO_M_TIMEOUT; + iosocket_wait(io_curr_device.out, timeout); /* depth really means timeout here. */ + } else + rts_error(VARLSTCNT(1) ERR_INVCTLMNE); + + return; +} + +void iosocket_dlr_device(mstr *d) +{ + io_desc *iod; + int len; + d_socket_struct *dsocketptr; + + iod = io_curr_device.out; + dsocketptr = (d_socket_struct *)iod->dev_sp; + + len = STRLEN(dsocketptr->dollar_device); + /* verify internal buffer has enough space for $DEVICE string value */ + assert((int)d->len > len); + memcpy(d->addr, dsocketptr->dollar_device, len); + d->len = len; + return; +} + +void iosocket_dlr_key(mstr *d) +{ + io_desc *iod; + int len; + d_socket_struct *dsocketptr; + + iod = io_curr_device.out; + dsocketptr = (d_socket_struct *)iod->dev_sp; + + len = STRLEN(dsocketptr->dollar_key); + /* verify internal buffer has enough space for $DEVICE string value */ + assert((int)d->len > len); + if (len > 0) + memcpy(d->addr, dsocketptr->dollar_key, len); + d->len = len; + return; +} diff --git a/sr_port/iosocket_listen.c b/sr_port/iosocket_listen.c new file mode 100644 index 0000000..247b867 --- /dev/null +++ b/sr_port/iosocket_listen.c @@ -0,0 +1,95 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iosocket_listen.c */ +/* checks the socket state -- socket_bind */ +/* checks the socket type -- passive */ +/* start listening */ + +#include "mdef.h" + +#include +#include "gtm_inet.h" +#include "gtm_socket.h" +#include "gtm_stdio.h" +#include "gtm_string.h" + +#include "io_params.h" +#include "io.h" +#include "iotcproutine.h" +#include "iotcpdef.h" +#include "gt_timer.h" +#include "iosocketdef.h" + +#define LISTENING "LISTENING" +#define MAX_LISTEN_QUEUE_LENGTH 5 + +GBLREF tcp_library_struct tcp_routines; + +boolean_t iosocket_listen(io_desc *iod, unsigned short len) +{ + d_socket_struct *dsocketptr; + socket_struct *socketptr; + char *errptr; + int4 errlen; + + error_def(ERR_SOCKLISTEN); + error_def(ERR_TEXT); + error_def(ERR_LQLENGTHNA); + error_def(ERR_SOCKACTNA); + error_def(ERR_CURRSOCKOFR); + error_def(ERR_LISTENPASSBND); + + if (MAX_LISTEN_QUEUE_LENGTH < len) + { + rts_error(VARLSTCNT(3) ERR_LQLENGTHNA, 1, len); + return FALSE; + } + + assert(iod->type == gtmsocket); + dsocketptr = (d_socket_struct *)iod->dev_sp; + socketptr = dsocketptr->socket[dsocketptr->current_socket]; + + if (dsocketptr->current_socket >= dsocketptr->n_socket) + { + rts_error(VARLSTCNT(4) ERR_CURRSOCKOFR, 2, dsocketptr->current_socket, dsocketptr->n_socket); + return FALSE; + } + + if ((socketptr->state != socket_bound) || (socketptr->passive != TRUE)) + { + rts_error(VARLSTCNT(1) ERR_LISTENPASSBND); + return FALSE; + } + + dsocketptr->dollar_key[0] = '\0'; + + /* establish a queue of length len for incoming connections */ + if (-1 == tcp_routines.aa_listen(socketptr->sd, len)) + { + errptr = (char *)STRERROR(errno); + errlen = STRLEN(errptr); + rts_error(VARLSTCNT(6) ERR_SOCKLISTEN, 0, ERR_TEXT, 2, errlen, errptr); + return FALSE; + } + + socketptr->state = socket_listening; + + len = SIZEOF(LISTENING) - 1; + memcpy(&dsocketptr->dollar_key[0], LISTENING, len); + dsocketptr->dollar_key[len++] = '|'; + memcpy(&dsocketptr->dollar_key[len], socketptr->handle, socketptr->handle_len); + len += socketptr->handle_len; + dsocketptr->dollar_key[len++] = '|'; + SPRINTF(&dsocketptr->dollar_key[len], "%d", socketptr->local.port); + + return TRUE; +} diff --git a/sr_port/iosocket_open.c b/sr_port/iosocket_open.c new file mode 100644 index 0000000..e4e2ac9 --- /dev/null +++ b/sr_port/iosocket_open.c @@ -0,0 +1,461 @@ +/**************************************************************** + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include + +#include "gtm_string.h" +#include "gtm_stdio.h" +#include "gtm_socket.h" +#include "gtm_inet.h" +#include "gtm_time.h" +#include "copy.h" +#include "gt_timer.h" +#include "io.h" +#include "iotimer.h" +#include "iotcp_select.h" +#include "iotcpdef.h" +#include "iotcproutine.h" +#include "io_params.h" +#include "iosocketdef.h" +#include "gtm_caseconv.h" +#include "stringpool.h" +#include "gtm_conv.h" +#include "gtm_utf8.h" + +GBLREF tcp_library_struct tcp_routines; +GBLREF d_socket_struct *socket_pool, *newdsocket; +GBLREF io_pair io_std_device; /* standard device */ +GBLREF boolean_t gtm_utf8_mode; +GBLREF int4 gtm_max_sockets; +GBLREF boolean_t dollar_zininterrupt; +LITREF unsigned char io_params_size[]; +LITREF mstr chset_names[]; + +#define FREE_SOCKPTR(sockptr) \ +{ \ + if (NULL != sockptr->buffer) free(sockptr->buffer); \ + free(sockptr); \ +} + +#define ESTABLISHED "ESTABLISHED" + +short iosocket_open(io_log_name *dev, mval *pp, int file_des, mval *mspace, int4 timepar) +{ + char addr[SA_MAXLITLEN], *errptr, sockaddr[SA_MAXLITLEN], + temp_addr[SA_MAXLITLEN], dev_type[MAX_DEV_TYPE_LEN]; + unsigned char ch, *c, *next, *top; + int handle_len, moreread_timeout, len; + unsigned short port; + int4 errlen, msec_timeout, real_errno, p_offset = 0, zff_len, delimiter_len; + int d_socket_struct_len; + ABS_TIME cur_time, end_time; + io_desc *ioptr; + struct sockaddr_in peer; /* socket address + port */ + fd_set tcp_fd; + uint4 bfsize = DEFAULT_SOCKET_BUFFER_SIZE, ibfsize; + d_socket_struct *dsocketptr; + socket_struct *socketptr; + mv_stent *mv_zintdev; + boolean_t zint_conn_restart = FALSE; + socket_interrupt *sockintr; + mstr chset_mstr; + boolean_t attach_specified = FALSE, + listen_specified = FALSE, + connect_specified = FALSE, + ioerror_specified = FALSE, + delay_specified = FALSE, + nodelay_specified = FALSE, + ibfsize_specified = FALSE, + moreread_specified = FALSE, + is_principal = FALSE, /* called from inetd */ + ichset_specified, + ochset_specified; + unsigned char delimiter_buffer[MAX_N_DELIMITER * (MAX_DELIM_LEN + 1)], zff_buffer[MAX_ZFF_LEN]; + char ioerror, ip[3], tcp[4], + sock_handle[MAX_HANDLE_LEN], delimiter[MAX_DELIM_LEN + 1]; + + error_def(ERR_DELIMSIZNA); + error_def(ERR_ADDRTOOLONG); + error_def(ERR_SOCKETEXIST); + error_def(ERR_ABNCOMPTINC); + error_def(ERR_DEVPARINAP); + error_def(ERR_DEVPARMNEG); + error_def(ERR_ILLESOCKBFSIZE); + error_def(ERR_ZFF2MANY); + error_def(ERR_DELIMWIDTH); + error_def(ERR_SOCKMAX); + error_def(ERR_ZINTRECURSEIO); + error_def(ERR_MRTMAXEXCEEDED); + + ioptr = dev->iod; + assert((params) *(pp->str.addr + p_offset) < (unsigned char)n_iops); + assert(ioptr != 0); + assert(ioptr->state >= 0 && ioptr->state < n_io_dev_states); + assert(ioptr->type == gtmsocket); + if ((ioptr->state == dev_closed) && mspace && mspace->str.len && mspace->str.addr) + { + lower_to_upper((uchar_ptr_t)dev_type, (uchar_ptr_t)mspace->str.addr, mspace->str.len); + if (STR_LIT_LEN("SOCKET") != mspace->str.len || 0 != memcmp(dev_type, "SOCKET", STR_LIT_LEN("SOCKET"))) + { + if (ioptr->dev_sp) + free(ioptr->dev_sp); + ioptr->state = dev_never_opened; + } + } + d_socket_struct_len = SIZEOF(d_socket_struct) + (SIZEOF(socket_struct) * (gtm_max_sockets - 1)); + if (ioptr->state == dev_never_opened) + { + dsocketptr = ioptr->dev_sp = (void *)malloc(d_socket_struct_len); + memset(dsocketptr, 0, d_socket_struct_len); + dsocketptr->iod = ioptr; + } else + dsocketptr = (d_socket_struct *)ioptr->dev_sp; + + if (ioptr->state == dev_never_opened) + { + ioptr->state = dev_closed; + ioptr->width = TCPDEF_WIDTH; + ioptr->length = TCPDEF_LENGTH; + ioptr->wrap = TRUE; + if (-1 == iotcp_fillroutine()) + assert(FALSE); + if (!io_std_device.in) + /* called from io_init */ + is_principal = TRUE; + } + if (dsocketptr->mupintr) + { /* check if connect was interrupted */ + sockintr = &dsocketptr->sock_save_state; + if (sockwhich_invalid == sockintr->who_saved) + GTMASSERT; /* Interrupt should never have an invalid save state */ + if (dollar_zininterrupt) + { + dsocketptr->mupintr = FALSE; + sockintr->who_saved = sockwhich_invalid; + rts_error(VARLSTCNT(1) ERR_ZINTRECURSEIO); + } + if (sockwhich_connect != sockintr->who_saved) + GTMASSERT; /* ZINTRECURSEIO should have caught */ + mv_zintdev = io_find_mvstent(dsocketptr->iod, FALSE); + if (mv_zintdev && mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid) + { /* mupintr will be reset and mvstent popped in iosocket_connect */ + connect_specified = TRUE; + ibfsize_specified = sockintr->ibfsize_specified; + assert(newdsocket); + assert(newdsocket == sockintr->newdsocket); + memcpy(newdsocket, (d_socket_struct *)mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr, + d_socket_struct_len); + socketptr = newdsocket->socket[newdsocket->current_socket]; + assert(socketptr == (socket_struct *)mv_zintdev->mv_st_cont.mvs_zintdev.socketptr); + zint_conn_restart = TRUE; /* skip what we already did, state == dev_closed */ + } + } else + { + ioptr->dollar.zeof = FALSE; + if (NULL == newdsocket) + newdsocket = (d_socket_struct *)malloc(d_socket_struct_len); + memcpy(newdsocket, dsocketptr, d_socket_struct_len); + memcpy(newdsocket->dollar_device, "0", SIZEOF("0")); + zff_len = -1; /* indicates neither ZFF nor ZNOFF specified */ + delimiter_len = -1; /* indicates neither DELIM nor NODELIM specified */ + ichset_specified = ochset_specified = FALSE; + while (iop_eol != (ch = *(pp->str.addr + p_offset++))) + { + switch(ch) + { + case iop_delimiter: + delimiter_len = (int4)(unsigned char)*(pp->str.addr + p_offset); + if (((MAX_DELIM_LEN + 1) * MAX_N_DELIMITER) >= delimiter_len) + memcpy(delimiter_buffer, (pp->str.addr + p_offset + 1), delimiter_len); + else + rts_error(VARLSTCNT(1) ERR_DELIMSIZNA); + break; + case iop_ipchset: + UNICODE_ONLY( + if (gtm_utf8_mode) + { /* Only change ipchset if in UTF8 mode */ + chset_mstr.addr = (char *)(pp->str.addr + p_offset + 1); + chset_mstr.len = *(pp->str.addr + p_offset); + SET_ENCODING(ioptr->ichset, &chset_mstr); + ichset_specified = TRUE; + } + ); + break; + case iop_opchset: + UNICODE_ONLY( + if (gtm_utf8_mode) + { /* Only change ipchset if in UTF8 mode */ + chset_mstr.addr = (char *)(pp->str.addr + p_offset + 1); + chset_mstr.len = *(pp->str.addr + p_offset); + SET_ENCODING(ioptr->ochset, &chset_mstr); + ochset_specified = TRUE; + } + ); + break; + case iop_chset: + UNICODE_ONLY( + if (gtm_utf8_mode) + { /* Only change ipchset/opchset if in UTF8 mode */ + chset_mstr.addr = (char *)(pp->str.addr + p_offset + 1); + chset_mstr.len = *(pp->str.addr + p_offset); + SET_ENCODING(ioptr->ichset, &chset_mstr); + ioptr->ochset = ioptr->ichset; + ichset_specified = ochset_specified = TRUE; + } + ); + break; + /* Note the following 4 cases (iop_m/utf16/utf16be/utf16le) have no corresponding device parameter + but are included here because they can be easily used in internal processing. + */ + case iop_m: + UNICODE_ONLY( + ioptr->ichset = ioptr->ochset = CHSET_M; + ichset_specified = ochset_specified = TRUE; + ); + break; + case iop_utf16: + UNICODE_ONLY( + if (gtm_utf8_mode) + { /* Only change chset if in UTF8 mode */ + ioptr->ichset = ioptr->ochset = CHSET_UTF16; + ichset_specified = ochset_specified = TRUE; + } + ); + break; + case iop_utf16be: + UNICODE_ONLY( + if (gtm_utf8_mode) + { /* Only change chset if in UTF8 mode */ + ioptr->ichset = ioptr->ochset = CHSET_UTF16BE; + ichset_specified = ochset_specified = TRUE; + } + ); + break; + case iop_utf16le: + UNICODE_ONLY( + if (gtm_utf8_mode) + { /* Only change chset if in UTF8 mode */ + ioptr->ichset = ioptr->ochset = CHSET_UTF16LE; + ichset_specified = ochset_specified = TRUE; + } + ); + break; + /**********************************/ + case iop_nodelimiter: + delimiter_len = 0; + break; + case iop_zdelay: + delay_specified = TRUE; + break; + case iop_znodelay: + nodelay_specified = TRUE; + break; + case iop_zbfsize: + GET_ULONG(bfsize, pp->str.addr + p_offset); + if ((0 == bfsize) || (MAX_SOCKET_BUFFER_SIZE < bfsize)) + rts_error(VARLSTCNT(3) ERR_ILLESOCKBFSIZE, 1, bfsize); + break; + case iop_zibfsize: + ibfsize_specified = TRUE; + GET_ULONG(ibfsize, pp->str.addr + p_offset); + if ((0 == ibfsize) || (MAX_INTERNAL_SOCBUF_SIZE < ibfsize)) + rts_error(VARLSTCNT(3) ERR_ILLESOCKBFSIZE, 1, bfsize); + break; + case iop_zlisten: + listen_specified = TRUE; + len = (int)(*(pp->str.addr + p_offset)); + if (len < SA_MAXLITLEN) + { + memset(sockaddr, 0, SIZEOF(sockaddr)); + memcpy(sockaddr, pp->str.addr + p_offset + 1, len); + } else + rts_error(VARLSTCNT(6) ERR_ADDRTOOLONG, 4, len, pp->str.addr + p_offset + 1, + len, SA_MAXLITLEN); + break; + case iop_connect: + connect_specified = TRUE; + len = (int)(*(pp->str.addr + p_offset)); + if (len < SA_MAXLITLEN) + { + memset(sockaddr, 0, SIZEOF(sockaddr)); + memcpy(sockaddr, pp->str.addr + p_offset + 1, len); + } else + rts_error(VARLSTCNT(6) ERR_ADDRTOOLONG, 4, len, pp->str.addr + p_offset + 1, + len, SA_MAXLITLEN); + break; + case iop_ioerror: + ioerror_specified = TRUE; + ioerror = *(pp->str.addr + p_offset + 1); /* the first char decides */ + break; + case iop_exception: + ioptr->error_handler.len = *(pp->str.addr + p_offset); + ioptr->error_handler.addr = (char *)(pp->str.addr + p_offset + 1); + s2pool(&ioptr->error_handler); + break; + case iop_attach: + attach_specified = TRUE; + handle_len = (int)(*(pp->str.addr + p_offset)); + if (handle_len > MAX_HANDLE_LEN) + handle_len = MAX_HANDLE_LEN; + memcpy(sock_handle, pp->str.addr + p_offset + 1, handle_len); + break; + case iop_socket: + rts_error(VARLSTCNT(1) ERR_DEVPARINAP); + break; + case iop_zff: + if (MAX_ZFF_LEN >= (zff_len = (int4)(unsigned char)*(pp->str.addr + p_offset))) + memcpy(zff_buffer, (char *)(pp->str.addr + p_offset + 1), zff_len); + else + rts_error(VARLSTCNT(4) ERR_ZFF2MANY, 2, zff_len, MAX_ZFF_LEN); + break; + case iop_znoff: + zff_len = 0; + break; + case iop_wrap: + ioptr->wrap = TRUE; + break; + case iop_nowrap: + ioptr->wrap = FALSE; + break; + case iop_morereadtime: + /* Time in milliseconds socket read will wait for more data before returning */ + GET_LONG(moreread_timeout, pp->str.addr + p_offset); + if (-1 == moreread_timeout) + moreread_timeout = DEFAULT_MOREREAD_TIMEOUT; + else if (-1 > moreread_timeout) + rts_error(VARLSTCNT(1) ERR_DEVPARMNEG); + else if (MAX_MOREREAD_TIMEOUT < moreread_timeout) + rts_error(VARLSTCNT(3) ERR_MRTMAXEXCEEDED, 1, MAX_MOREREAD_TIMEOUT); + moreread_specified = TRUE; + break; + default: + break; + } + p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ? + (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]); + } + if (!ichset_specified) + ioptr->ichset = (gtm_utf8_mode) ? CHSET_UTF8 : CHSET_M; + if (!ochset_specified) + ioptr->ochset = (gtm_utf8_mode) ? CHSET_UTF8 : CHSET_M; + if (CHSET_M != ioptr->ichset && CHSET_UTF16 != ioptr->ichset) + get_chset_desc(&chset_names[ioptr->ichset]); + if (CHSET_M != ioptr->ochset && CHSET_UTF16 != ioptr->ochset) + get_chset_desc(&chset_names[ioptr->ochset]); + if (listen_specified && connect_specified) + { + rts_error(VARLSTCNT(8) ERR_ABNCOMPTINC, 6, LEN_AND_LIT("CONNECT"), + LEN_AND_LIT("ZLISTEN"), LEN_AND_LIT("OPEN")); + return FALSE; + } + if (delay_specified && nodelay_specified) + { + rts_error(VARLSTCNT(8) ERR_ABNCOMPTINC, 6, LEN_AND_LIT("DELAY"), + LEN_AND_LIT("NODELAY"), LEN_AND_LIT("OPEN")); + return FALSE; + } + if (listen_specified || connect_specified || is_principal) + { + if (NULL == (socketptr = iosocket_create(sockaddr, bfsize, is_principal ? file_des : -1))) + return FALSE; + assert(listen_specified == socketptr->passive); + if (ioerror_specified) + socketptr->ioerror = ('T' == ioerror || 't' == ioerror); + socketptr->nodelay = nodelay_specified; /* defaults to DELAY */ + if (ibfsize_specified) + socketptr->bufsiz = ibfsize; + if (moreread_specified) + { + socketptr->moreread_timeout = moreread_timeout; + socketptr->def_moreread_timeout = TRUE; /* iosocket_readfl.c needs to know user specified */ + } + /* socket handle -- also check for duplication */ + if (attach_specified) + { + if (iosocket_handle(sock_handle, &handle_len, FALSE, newdsocket) >= 0) + { + FREE_SOCKPTR(socketptr); + rts_error(VARLSTCNT(4) ERR_SOCKETEXIST, 2, handle_len, sock_handle); + return FALSE; + } + } else + iosocket_handle(sock_handle, &handle_len, TRUE, dsocketptr); + socketptr->handle_len = handle_len; + memcpy(socketptr->handle, sock_handle, handle_len); + /* parse the delimiter: delimiter_buffer ==> socketptr->delimiter[...] */ + if (0 <= delimiter_len) + iosocket_delimiter(delimiter_buffer, delimiter_len, socketptr, (0 == delimiter_len)); + if (ioptr->wrap && 0 != socketptr->n_delimiter && ioptr->width < socketptr->delimiter[0].len) + { + iosocket_delimiter((unsigned char *)NULL, 0, socketptr, TRUE); + FREE_SOCKPTR(socketptr); + rts_error(VARLSTCNT(4) ERR_DELIMWIDTH, 2, ioptr->width, socketptr->delimiter[0].len); + assert(FALSE); + } + /* connects newdsocket and socketptr (the new socket) */ + if (gtm_max_sockets <= newdsocket->n_socket) + { + iosocket_delimiter((unsigned char *)NULL, 0, socketptr, TRUE); + FREE_SOCKPTR(socketptr); + rts_error(VARLSTCNT(3) ERR_SOCKMAX, 1, gtm_max_sockets); + return FALSE; + } + socketptr->dev = newdsocket; + newdsocket->socket[newdsocket->n_socket++] = socketptr; + newdsocket->current_socket = newdsocket->n_socket - 1; + } + if (0 <= zff_len && /* ZFF or ZNOFF specified */ + 0 < (socketptr->zff.len = zff_len)) /* assign the new ZFF len, might be 0 from ZNOFF, or ZFF="" */ + { /* ZFF="non-zero-len-string" specified */ + if (gtm_utf8_mode) /* Check if ZFF has any invalid UTF-8 character */ + { /* Note: the ZFF string originates from the source program, so is in UTF-8 mode or M mode regardless + * of OCHSET of this device. ZFF is output on WRITE # command, and MUST contain valid UTF-8 sequence. */ + utf8_len_strict(zff_buffer, zff_len); /* triggers badchar error for invalid sequence */ + } + if (NULL == socketptr->zff.addr) /* we rely on socketptr->zff.addr being set to 0 in iosocket_create() */ + socketptr->zff.addr = (char *)malloc(MAX_ZFF_LEN); + memcpy(socketptr->zff.addr, zff_buffer, zff_len); + } + } + /* action */ + if ((listen_specified && (!iosocket_bind(socketptr, timepar, ibfsize_specified))) || + (connect_specified && (!iosocket_connect(socketptr, timepar, ibfsize_specified)))) + { + if (socketptr->sd > 0) + (void)tcp_routines.aa_close(socketptr->sd); + iosocket_delimiter((unsigned char *)NULL, 0, socketptr, TRUE); + if (NULL != socketptr->zff.addr) + free(socketptr->zff.addr); + FREE_SOCKPTR(socketptr); + return FALSE; + } else if (is_principal) + { /* fill in what bind or connect would */ + strncpy(socketptr->local.saddr_ip, tcp_routines.aa_inet_ntoa(socketptr->local.sin.sin_addr), + SIZEOF(socketptr->local.saddr_ip)); + strncpy(socketptr->remote.saddr_ip, tcp_routines.aa_inet_ntoa(socketptr->remote.sin.sin_addr), + SIZEOF(socketptr->remote.saddr_ip)); + len = SIZEOF(ESTABLISHED) - 1; + memcpy(&newdsocket->dollar_key[0], ESTABLISHED, len); + newdsocket->dollar_key[len++] = '|'; + memcpy(&newdsocket->dollar_key[len], socketptr->handle, socketptr->handle_len); + len += socketptr->handle_len; + newdsocket->dollar_key[len++] = '|'; + strcpy(&newdsocket->dollar_key[len], socketptr->remote.saddr_ip); /* Copies in terminating NULL */ + } + /* commit the changes to the list */ + if (listen_specified || connect_specified || is_principal) + { + socketptr->dev = dsocketptr; + memcpy(dsocketptr, newdsocket, d_socket_struct_len); + } + ioptr->state = dev_open; + return TRUE; +} diff --git a/sr_port/iosocket_poolinit.c b/sr_port/iosocket_poolinit.c new file mode 100644 index 0000000..96de3f5 --- /dev/null +++ b/sr_port/iosocket_poolinit.c @@ -0,0 +1,65 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iosocket_poolinit.c */ + +#include "mdef.h" + +#include "gtm_string.h" + +#include +#include "gtm_socket.h" +#include "gtm_inet.h" + +#include "io.h" +#include "io_params.h" +#include "iotimer.h" +#include "iotcpdef.h" +#include "gt_timer.h" +#include "iosocketdef.h" +#include "op.h" + +GBLREF d_socket_struct *socket_pool; + +static char socketpoolv[] = "socketpool"; +static char socketpoolp = '\0'; +static char socketpoolm[] = "socket"; + +void iosocket_poolinit(void) +{ + mval sockv, sockp, sockm; + int t; + io_log_name *nl; + + memset(&sockv, 0, SIZEOF(mval)); + memset(&sockp, 0, SIZEOF(mval)); + memset(&sockm, 0, SIZEOF(mval)); + + sockv.mvtype = MV_STR; + sockv.str.len = SIZEOF(socketpoolv) - 1; + sockv.str.addr = &socketpoolv[0]; + + sockp.mvtype = MV_STR; + sockp.str.len = SIZEOF(socketpoolp); + sockp.str.addr = &socketpoolp; + + sockm.mvtype = MV_STR; + sockm.str.len = SIZEOF(socketpoolm) - 1; + sockm.str.addr = &socketpoolm[0]; + + t = NO_M_TIMEOUT; + + op_open(&sockv, &sockp, t, &sockm); + + nl = get_log_name(&sockv.str, NO_INSERT); + assert(NULL != nl); + socket_pool = (d_socket_struct *)(nl->iod->dev_sp); +} diff --git a/sr_port/iosocket_rdone.c b/sr_port/iosocket_rdone.c new file mode 100644 index 0000000..339acb0 --- /dev/null +++ b/sr_port/iosocket_rdone.c @@ -0,0 +1,64 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iosocket_rdone.c */ + +#include "mdef.h" +#include "gtm_socket.h" +#include "gtm_inet.h" +#include "io.h" +#include "gt_timer.h" +#include "iotcpdef.h" +#include "iosocketdef.h" +#include "gtm_utf8.h" + +GBLREF io_pair io_curr_device; + +int iosocket_rdone(mint *v, int4 timeout) +{ + mval tmp; + int ret; + uint4 codepoint; + io_desc *iod; + gtm_chset_t ichset; + + ret = iosocket_readfl(&tmp, 1, timeout); + if (ret) + { + if (0 < tmp.str.len) + { + ichset = io_curr_device.in->ichset; + switch(ichset) + { + case CHSET_M: + codepoint = (unsigned char)tmp.str.addr[0]; + break; + case CHSET_UTF8: + UTF8_MBTOWC(tmp.str.addr, tmp.str.addr + tmp.str.len, codepoint); + break; + case CHSET_UTF16BE: + UTF16BE_MBTOWC(tmp.str.addr, tmp.str.addr + tmp.str.len, codepoint); + break; + case CHSET_UTF16LE: + UTF16LE_MBTOWC(tmp.str.addr, tmp.str.addr + tmp.str.len, codepoint); + break; + default: + GTMASSERT; + } + UNICODE_ONLY(assert(WEOF != codepoint)); + } else + /* Null length string returns 0 */ + codepoint = 0; + *v = (mint)(codepoint); + } else + *v = -1; + return ret; +} diff --git a/sr_port/iosocket_read.c b/sr_port/iosocket_read.c new file mode 100644 index 0000000..05162ed --- /dev/null +++ b/sr_port/iosocket_read.c @@ -0,0 +1,25 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iosocket_read.c */ + +#include "mdef.h" +#include "gtm_socket.h" +#include "gtm_inet.h" +#include "io.h" +#include "gt_timer.h" +#include "iotcpdef.h" +#include "iosocketdef.h" + +int iosocket_read(mval *v, int4 timeout) +{ + return iosocket_readfl(v, 0, timeout); /* 0 means not fixed length */ +} diff --git a/sr_port/iosocket_readfl.c b/sr_port/iosocket_readfl.c new file mode 100644 index 0000000..506ea8e --- /dev/null +++ b/sr_port/iosocket_readfl.c @@ -0,0 +1,876 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iosocket_readfl.c */ +#include "mdef.h" +#include +#include "gtm_stdio.h" +#include "gtm_time.h" +#ifdef __MVS__ +#include +#endif +#include "gtm_socket.h" +#include "gtm_inet.h" +#include "gtm_string.h" +#ifdef UNIX +#include "gtm_fcntl.h" +#include "eintr_wrappers.h" +#endif +#include "gt_timer.h" +#include "io.h" +#include "iotimer.h" +#include "iotcpdef.h" +#include "iotcproutine.h" +#include "stringpool.h" +#include "iosocketdef.h" +#include "min_max.h" +#include "outofband.h" +#include "wake_alarm.h" +#include "gtm_conv.h" +#include "gtm_utf8.h" +#include "rtnhdr.h" +#include "stack_frame.h" +#include "mv_stent.h" +#include "send_msg.h" +#include "error.h" + +GBLREF stack_frame *frame_pointer; +GBLREF unsigned char *stackbase, *stacktop, *msp, *stackwarn; +GBLREF mv_stent *mv_chain; +GBLREF io_pair io_curr_device; +#ifdef UNIX +GBLREF io_pair io_std_device; +GBLREF bool prin_in_dev_failure; +#endif +GBLREF bool out_of_time; +GBLREF spdesc stringpool; +GBLREF volatile int4 outofband; +GBLREF mstr chset_names[]; +GBLREF UConverter *chset_desc[]; +GBLREF boolean_t dollar_zininterrupt; +GBLREF int socketus_interruptus; +GBLREF boolean_t gtm_utf8_mode; + +#ifdef UNICODE_SUPPORTED +/* Maintenance of $KEY, $DEVICE and $ZB on a badchar error */ +void iosocket_readfl_badchar(mval *vmvalptr, int datalen, int delimlen, unsigned char *delimptr, + unsigned char *strend) +{ + int tmplen, len; + unsigned char *delimend; + io_desc *iod; + d_socket_struct *dsocketptr; + + iod = io_curr_device.in; + dsocketptr = (d_socket_struct *)(iod->dev_sp); + vmvalptr->str.len = datalen; + vmvalptr->str.addr = (char *)stringpool.free; + if (0 < datalen) + { /* Return how much input we got */ + if (CHSET_M != iod->ichset && CHSET_UTF8 != iod->ichset) + { + SOCKET_DEBUG(PRINTF("socrflbc: Converting UTF16xx data back to UTF8 for internal use\n"); DEBUGSOCKFLUSH); + vmvalptr->str.len = gtm_conv(chset_desc[iod->ichset], chset_desc[CHSET_UTF8], &vmvalptr->str, NULL, NULL); + vmvalptr->str.addr = (char *)stringpool.free; + } + stringpool.free += vmvalptr->str.len; + } + if (NULL != strend && NULL != delimptr) + { /* First find the end of the delimiter (max of 4 bytes) */ + if (0 == delimlen) + { + for (delimend = delimptr; GTM_MB_LEN_MAX >= delimlen && delimend < strend; ++delimend, ++delimlen) + { + if (UTF8_VALID(delimend, strend, tmplen)) + break; + } + } + if (0 < delimlen) + { /* Set $KEY and $ZB with the failing badchar */ + memcpy(iod->dollar.zb, delimptr, MIN(delimlen, ESC_LEN - 1)); + iod->dollar.zb[MIN(delimlen, ESC_LEN - 1)] = '\0'; + memcpy(dsocketptr->dollar_key, delimptr, MIN(delimlen, DD_BUFLEN - 1)); + dsocketptr->dollar_key[MIN(delimlen, DD_BUFLEN - 1)] = '\0'; + } + } + len = SIZEOF(ONE_COMMA) - 1; + memcpy(dsocketptr->dollar_device, ONE_COMMA, len); + memcpy(&dsocketptr->dollar_device[len], BADCHAR_DEVICE_MSG, SIZEOF(BADCHAR_DEVICE_MSG)); +} +#endif + +/* VMS uses the UCX interface; should support others that emulate it */ +int iosocket_readfl(mval *v, int4 width, int4 timeout) + /* width == 0 is a flag for non-fixed length read */ + /* timeout in seconds */ +{ + int ret, byteperchar; + boolean_t timed, vari, more_data, terminator, has_delimiter, requeue_done; + boolean_t zint_restart, outofband_terminate, one_read_done, utf8_active; + int flags, len, real_errno, save_errno, fcntl_res, errlen, charlen, stp_need; + int bytes_read, orig_bytes_read, ii, max_bufflen, bufflen, chars_read, mb_len, match_delim; + io_desc *iod; + d_socket_struct *dsocketptr; + socket_struct *socketptr; + int4 msec_timeout; /* timeout in milliseconds */ + TID timer_id; + ABS_TIME cur_time, end_time, time_for_read, zero; + char *errptr; + unsigned char *buffptr, *c_ptr, *c_top, *inv_beg, *buffer_start; + ssize_t status; + gtm_chset_t ichset; + mv_stent *mv_zintdev; + socket_interrupt *sockintr; + + error_def(ERR_IOEOF); + error_def(ERR_TEXT); + error_def(ERR_CURRSOCKOFR); + error_def(ERR_NOSOCKETINDEV); + error_def(ERR_GETSOCKOPTERR); + error_def(ERR_SETSOCKOPTERR); + error_def(ERR_MAXSTRLEN); + error_def(ERR_BOMMISMATCH); + error_def(ERR_ZINTRECURSEIO); + error_def(ERR_STACKCRIT); + error_def(ERR_STACKOFLOW); + UNIX_ONLY(error_def(ERR_NOPRINCIO);) + + assert(stringpool.free >= stringpool.base); + assert(stringpool.free <= stringpool.top); + iod = io_curr_device.in; + ichset = iod->ichset; + assert(dev_open == iod->state); + assert(gtmsocket == iod->type); + dsocketptr = (d_socket_struct *)(iod->dev_sp); + if (0 >= dsocketptr->n_socket) + { + iod->dollar.za = 9; + rts_error(VARLSTCNT(1) ERR_NOSOCKETINDEV); + return 0; + } + if (dsocketptr->n_socket <= dsocketptr->current_socket) + { + iod->dollar.za = 9; + rts_error(VARLSTCNT(4) ERR_CURRSOCKOFR, 2, dsocketptr->current_socket, dsocketptr->n_socket); + return 0; + } + utf8_active = NON_UNICODE_ONLY(FALSE) UNICODE_ONLY(gtm_utf8_mode ? IS_UTF_CHSET(ichset) : FALSE); + byteperchar = UNICODE_ONLY(utf8_active ? GTM_MB_LEN_MAX :) 1; + if (0 == width) + { /* op_readfl won't do this; must be a call from iosocket_read */ + vari = TRUE; + width = MAX_STRLEN; + } else + { + vari = FALSE; + width = (width < MAX_STRLEN) ? width : MAX_STRLEN; + } + /* if width is set to MAX_STRLEN, we might be overly generous (assuming every char is just one byte) we must check if byte + * length crosses the MAX_STRLEN boundary */ + socketptr = dsocketptr->socket[dsocketptr->current_socket]; + assert(socketptr); + sockintr = &dsocketptr->sock_save_state; + assert(sockintr); + outofband_terminate = FALSE; + one_read_done = FALSE; + zint_restart = FALSE; + /* Check if new or resumed read */ + if (dsocketptr->mupintr) + { /* We have a pending read restart of some sort */ + if (sockwhich_invalid == sockintr->who_saved) + GTMASSERT; /* Interrupt should never have an invalid save state */ + /* check we aren't recursing on this device */ + if (dollar_zininterrupt) + rts_error(VARLSTCNT(1) ERR_ZINTRECURSEIO); + if (sockwhich_readfl != sockintr->who_saved) + GTMASSERT; /* ZINTRECURSEIO should have caught */ + SOCKET_DEBUG(PRINTF("socrfl: *#*#*#*#*#*#*# Restarted interrupted read\n"); DEBUGSOCKFLUSH); + dsocketptr->mupintr = FALSE; + sockintr->who_saved = sockwhich_invalid; + mv_zintdev = io_find_mvstent(iod, FALSE); + assert(mv_zintdev); + if (mv_zintdev) + { + if (mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid) + { + bytes_read = mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.len; + assert(bytes_read == sockintr->bytes_read); + if (bytes_read) + { + buffer_start = (unsigned char *)mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr; + zint_restart = TRUE; + } + } else + { + assert(FALSE); + SOCKET_DEBUG(PRINTF("Evidence of an interrupt, but it was invalid\n"); DEBUGSOCKFLUSH); + } + /* Done with this mv_stent. Pop it off if we can, else mark it inactive. */ + if (mv_chain == mv_zintdev) + POP_MV_STENT(); /* pop if top of stack */ + else + { /* else mark it unused */ + mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid = FALSE; + mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.len = 0; + mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr = NULL; + } + } else + { + sockintr->end_time_valid = FALSE; + SOCKET_DEBUG(PRINTF("Evidence of an interrupt, but no MV_STENT\n"); DEBUGSOCKFLUSH); + } + } else + sockintr->end_time_valid = FALSE; + if (!zint_restart) + { /* Simple path (not restart or nothing read,) no worries*/ + /* compute the worst case byte requirement knowing that width is in chars */ + max_bufflen = (vari) ? MAX_STRBUFF_INIT + : ((MAX_STRLEN > (width * byteperchar)) ? (width * byteperchar) : MAX_STRLEN); + ENSURE_STP_FREE_SPACE(max_bufflen); + bytes_read = 0; + chars_read = 0; + } else + { + max_bufflen = sockintr->max_bufflen; + chars_read = sockintr->chars_read; + assert(chars_read <= bytes_read); + SOCKET_DEBUG2(PRINTF("socrfl: .. mv_stent found - bytes_read: %d chars_read: %d max_bufflen: %d" + " interrupts: %d\n", bytes_read, chars_read, max_bufflen, socketus_interruptus); + DEBUGSOCKFLUSH); + SOCKET_DEBUG(PRINTF("socrfl: .. timeout: %d\n", timeout); DEBUGSOCKFLUSH); + SOCKET_DEBUG(if (sockintr->end_time_valid) PRINTF("socrfl: .. endtime: %d/%d\n", end_time.at_sec, + end_time.at_usec); DEBUGSOCKFLUSH); + SOCKET_DEBUG2(PRINTF("socrfl: .. buffer address: 0x%08lx stringpool: 0x%08lx\n", + buffer_start, stringpool.free); DEBUGSOCKFLUSH); + if (stringpool.free != (buffer_start + bytes_read)) /* BYPASSOK */ + { /* Not @ stringpool.free - must move what we have, so we need room for the whole anticipated message */ + SOCKET_DEBUG2(PRINTF("socrfl: .. Stuff put on string pool after our buffer\n"); DEBUGSOCKFLUSH); + stp_need = max_bufflen; + } else + { /* Previously read buffer piece is still last thing in stringpool, so we need room for the rest */ + SOCKET_DEBUG2(PRINTF("socrfl: .. Our buffer did not move in the stringpool\n"); DEBUGSOCKFLUSH); + stp_need = max_bufflen - bytes_read; + assert(stp_need < max_bufflen); + } + if (!IS_STP_SPACE_AVAILABLE(stp_need)) + { /* need more room */ + SOCKET_DEBUG2(PRINTF("socrfl: .. garbage collection done in starting after interrupt\n"); DEBUGSOCKFLUSH); + v->str.addr = (char *)buffer_start; /* Protect buffer from reclaim */ + v->str.len = bytes_read; + INVOKE_STP_GCOL(max_bufflen); + buffer_start = (unsigned char *)v->str.addr; + } + if ((buffer_start + bytes_read) < stringpool.free) /* BYPASSOK */ + { /* now need to move it to the top */ + assert(stp_need == max_bufflen); + memcpy(stringpool.free, buffer_start, bytes_read); + } else + { /* it should still be just under the used space */ + assert((buffer_start + bytes_read) == stringpool.free); /* BYPASSOK */ + stringpool.free = buffer_start; /* backup the free pointer */ + } + v->str.len = 0; /* Clear incase interrupt or error -- don't want to hold this buffer */ + /* At this point, our previous buffer is sitting at stringpool.free and can be + expanded if necessary. This is what the rest of the code is expecting. + */ + } + if (iod->dollar.x && (TCP_WRITE == socketptr->lastop)) + { /* switching from write to read */ + assert(!zint_restart); + if (!iod->dollar.za) + iosocket_flush(iod); + iod->dollar.x = 0; + } + socketptr->lastop = TCP_READ; + ret = TRUE; + has_delimiter = (0 < socketptr->n_delimiter); + time_for_read.at_sec = 0; + if (0 == timeout) + time_for_read.at_usec = 0; + else if (socketptr->def_moreread_timeout) + time_for_read.at_usec = socketptr->moreread_timeout * 1000; + else + time_for_read.at_usec = INITIAL_MOREREAD_TIMEOUT * 1000; + SOCKET_DEBUG(PRINTF("socrfl: moreread_timeout = %d def_moreread_timeout= %d time = %d \n", + socketptr->moreread_timeout,socketptr->def_moreread_timeout,time_for_read.at_usec); DEBUGSOCKFLUSH); + timer_id = (TID)iosocket_readfl; + out_of_time = FALSE; + if (NO_M_TIMEOUT == timeout) + { + timed = FALSE; + msec_timeout = NO_M_TIMEOUT; + assert(!sockintr->end_time_valid); + } else + { + timed = TRUE; + msec_timeout = timeout2msec(timeout); + if (msec_timeout > 0) + { /* there is time to wait */ +#ifdef UNIX + /* set blocking I/O */ + FCNTL2(socketptr->sd, F_GETFL, flags); + if (flags < 0) + { + iod->dollar.za = 9; + save_errno = errno; + errptr = (char *)STRERROR(errno); + rts_error(VARLSTCNT(7) ERR_GETSOCKOPTERR, 5, LEN_AND_LIT("F_GETFL FOR NON BLOCKING I/O"), + save_errno, LEN_AND_STR(errptr)); + } + FCNTL3(socketptr->sd, F_SETFL, flags & (~(O_NDELAY | O_NONBLOCK)), fcntl_res); + if (fcntl_res < 0) + { + iod->dollar.za = 9; + save_errno = errno; + errptr = (char *)STRERROR(errno); + rts_error(VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, LEN_AND_LIT("F_SETFL FOR NON BLOCKING I/O"), + save_errno, LEN_AND_STR(errptr)); + } +#endif + sys_get_curr_time(&cur_time); + if (!sockintr->end_time_valid) + add_int_to_abs_time(&cur_time, msec_timeout, &end_time); + else + { /* end_time taken from restart data. Compute what msec_timeout should be so timeout timer + gets set correctly below. + */ + end_time = sockintr->end_time; /* Restore end_time for timeout */ + cur_time = sub_abs_time(&end_time, &cur_time); + if (0 > cur_time.at_sec) + { + msec_timeout = -1; + out_of_time = TRUE; + } else + msec_timeout = (int4)(cur_time.at_sec * 1000 + cur_time.at_usec / 1000); + SOCKET_DEBUG(PRINTF("socrfl: Taking timeout end time from read restart data - " + "computed msec_timeout: %d\n", msec_timeout); DEBUGSOCKFLUSH); + } + if (0 < msec_timeout) + start_timer(timer_id, msec_timeout, wake_alarm, 0, NULL); + } else + { + out_of_time = TRUE; + SOCKET_DEBUG(PRINTF("socrfl: No timeout specified, assuming out_of_time\n")); + } + } + sockintr->end_time_valid = FALSE; + dsocketptr->dollar_key[0] = '\0'; + iod->dollar.zb[0] = '\0'; + more_data = TRUE; + real_errno = 0; + requeue_done = FALSE; + SOCKET_DEBUG(PRINTF("socrfl: ##################### Entering read loop ######################\n"); + DEBUGSOCKFLUSH); + for (status = 0; status >= 0;) + { + SOCKET_DEBUG2(PRINTF("socrfl: ********* Top of read loop - bytes_read: %d chars_read: %d " + "buffered_length: %d\n", bytes_read, chars_read, socketptr->buffered_length); DEBUGSOCKFLUSH); + SOCKET_DEBUG2(PRINTF("socrfl: ********* read-width: %d vari: %d status: %d\n", width, vari, status); + DEBUGSOCKFLUSH); + if (bytes_read >= max_bufflen) + { /* more buffer needed. Extend the stringpool buffer by doubling the size as much as we + extended previously */ + SOCKET_DEBUG2(PRINTF("socrfl: Buffer expand1 bytes_read(%d) max_bufflen(%d)\n", + bytes_read, max_bufflen); DEBUGSOCKFLUSH); + assert(vari); + max_bufflen += max_bufflen; + if (MAX_STRLEN < max_bufflen) + max_bufflen = MAX_STRLEN; + if (!IS_STP_SPACE_AVAILABLE(bytes_read + max_bufflen)) + { + v->str.len = bytes_read; /* to keep the data read so far from being garbage collected */ + v->str.addr = (char *)stringpool.free; + stringpool.free += v->str.len; + assert(stringpool.free <= stringpool.top); + INVOKE_STP_GCOL(max_bufflen); + memcpy(stringpool.free, v->str.addr, v->str.len); + v->str.len = 0; /* If interrupted, don't hold onto old space */ + } + } + if (has_delimiter || requeue_done || (socketptr->first_read && CHSET_M != ichset)) + { /* Delimiter scanning needs one char at a time. Question is how big is a char? + For the UTF character sets, we have a similar issue (with the same solution) in that + we need to make sure the entire BOM we may have is in the buffer. If the last read + caused chars to be requeued, we have to make sure that we read in one full character + (buffer likely contains only a partial char) in order to make forward progess. If we + didn't do this, we would just pull the partial char out of the buffer, notice its incompleteness + and requeue it again and again. Forcing a full char read lets us continue past this point. + */ + requeue_done = FALSE; /* housekeeping -- We don't want to come back here for this reason + until it happens again */ + SOCKET_DEBUG2(if (socketptr->first_read && CHSET_M != ichset) + PRINTF("socrfl: Prebuffering because ichset = UTF16\n"); + else PRINTF("socrfl: Prebuffering because we have delimiter or requeue\n"); DEBUGSOCKFLUSH); + if (CHSET_M == iod->ichset) + bufflen = 1; /* M mode, 1 char == 1 byte */ + else + { /* We need to make sure the read buffer contains a complete UTF character and to make sure + we know how long that character is so we can read it completely. The issue is that if we + read a partial utf character, the utf checking code below will notice this and rebuffer it + so that it gets re-read on the next iteration. However, this will then re-read the same + partial character and we have a loop. We make a call here which will take a peek at the + first byte of the next character (and read it in if necessary), determine how long the + character is and verify that many characters are available in the buffer and return the + character length to us to use for bufflen. + */ + charlen = (int)iosocket_snr_utf_prebuffer(iod, socketptr, 0, &time_for_read, + (!vari || has_delimiter || 0 == chars_read)); + SOCKET_DEBUG2(PRINTF("socrfl: charlen from iosocket_snr_utf_prebuffer = %d\n", charlen); + DEBUGSOCKFLUSH); + if (0 < charlen) + { /* We know how long the next char is. If it will fit in our buffer, then it is + the correct bufflen. If it won't, our buffer is full and we need to expand. + */ + if ((max_bufflen - bytes_read) < charlen) + { + SOCKET_DEBUG2(PRINTF("socrfl: Buffer expand2 - max_bufflen(%d) " + "bytes_read(%d)\n", + max_bufflen, bytes_read); DEBUGSOCKFLUSH); + assert(vari); + max_bufflen += max_bufflen; + if (MAX_STRLEN < max_bufflen) + max_bufflen = MAX_STRLEN; + if (!IS_STP_SPACE_AVAILABLE(bytes_read + max_bufflen)) + { + v->str.len = bytes_read; /* to keep the data read so far from + being garbage collected */ + v->str.addr = (char *)stringpool.free; + stringpool.free += bytes_read; + assert(stringpool.free <= stringpool.top); + INVOKE_STP_GCOL(max_bufflen); + memcpy(stringpool.free, v->str.addr, v->str.len); + v->str.len = 0; /* If interrupted, don't hold onto old space */ + } + } + bufflen = charlen; + } else if (0 == charlen) + { /* No data was available or there was a timeout. We set status to -3 here + so that we end up bypassing the call to iosocket_snr below which was to + do the actual IO. No need to repeat our lack of IO issue. + */ + SOCKET_DEBUG(PRINTF("socrfl: Timeout or end of data stream\n"); DEBUGSOCKFLUSH); + status = -3; /* To differentiate from status=0 if prebuffer bypassed */ + DEBUG_ONLY(bufflen = 0); /* Triggers assert in iosocket_snr if we end up there anyway */ + } else + { /* Something bad happened. Feed the error to the lower levels for proper handling */ + SOCKET_DEBUG2(PRINTF("socrfl: Read error: status: %d errno: %d\n", charlen, errno); + DEBUGSOCKFLUSH); + status = charlen; + DEBUG_ONLY(bufflen = 0); /* Triggers assert in iosocket_snr if we end up there anyway */ + } + } + } else + { + bufflen = max_bufflen - bytes_read; + SOCKET_DEBUG2(PRINTF("socrfl: Regular read .. bufflen = %d\n", bufflen); DEBUGSOCKFLUSH); + status = 0; + } + buffptr = (stringpool.free + bytes_read); + terminator = FALSE; + if (0 <= status) + /* An IO above in prebuffering may have failed in which case we do not want to reset status */ + status = iosocket_snr(socketptr, buffptr, bufflen, 0, &time_for_read); + else + { + SOCKET_DEBUG2(PRINTF("socrfl: Data read bypassed - status: %d\n", status); DEBUGSOCKFLUSH); + } + if (0 == status || -3 == status) /* -3 status can happen on EOB from prebuffering */ + { + SOCKET_DEBUG2(PRINTF("socrfl: No more data available\n"); DEBUGSOCKFLUSH); + more_data = FALSE; + status = 0; /* Consistent treatment of no more data */ + } else if (0 < status) + { + if (timeout && !socketptr->def_moreread_timeout && !one_read_done) + { + one_read_done = TRUE; + SOCKET_DEBUG(PRINTF("socrfl: before moreread_timeout = %d timeout = %d \n", + socketptr->moreread_timeout,time_for_read.at_usec); DEBUGSOCKFLUSH); + time_for_read.at_usec = DEFAULT_MOREREAD_TIMEOUT * 1000; + SOCKET_DEBUG(PRINTF("socrfl: after timeout = %d \n",time_for_read.at_usec); DEBUGSOCKFLUSH); + } + SOCKET_DEBUG2(PRINTF("socrfl: Bytes read: %d\n", status); DEBUGSOCKFLUSH); + bytes_read += (int)status; + UNIX_ONLY(if (iod == io_std_device.out) + prin_in_dev_failure = FALSE;) + if (socketptr->first_read && CHSET_M != ichset) /* May have a BOM to defuse */ + { + if (CHSET_UTF8 != ichset) + { /* When the type is UTF16xx, we need to check for a BOM at the beginning of the file. If + found it will tell us which of UTF-16BE (default if no BOM) or UTF-16LE mode the data + is being written with. The call to iosocket_snr_utf_prebuffer() above should have made + sure that there were at least two chars available in the buffer if the char is a BOM. + */ + if (UTF16BE_BOM_LEN <= bytes_read) /* All UTF16xx BOM lengths are the same */ + { + if (0 == memcmp(buffptr, UTF16BE_BOM, UTF16BE_BOM_LEN)) + { + if (CHSET_UTF16LE == ichset) + { + iod->dollar.za = 9; + rts_error(VARLSTCNT(6) ERR_BOMMISMATCH, 4, + chset_names[CHSET_UTF16BE].len, + chset_names[CHSET_UTF16BE].addr, + chset_names[CHSET_UTF16LE].len, + chset_names[CHSET_UTF16LE].addr); + } else + { + iod->ichset = ichset = CHSET_UTF16BE; + bytes_read -= UTF16BE_BOM_LEN; /* Throw way BOM */ + SOCKET_DEBUG2(PRINTF("socrfl: UTF16BE BOM detected\n"); + DEBUGSOCKFLUSH); + } + } else if (0 == memcmp(buffptr, UTF16LE_BOM, UTF16LE_BOM_LEN)) + { + if (CHSET_UTF16BE == ichset) + { + iod->dollar.za = 9; + rts_error(VARLSTCNT(6) ERR_BOMMISMATCH, 4, + chset_names[CHSET_UTF16LE].len, + chset_names[CHSET_UTF16LE].addr, + chset_names[CHSET_UTF16BE].len, + chset_names[CHSET_UTF16BE].addr); + } else + { + iod->ichset = ichset = CHSET_UTF16LE; + bytes_read -= UTF16BE_BOM_LEN; /* Throw away BOM */ + SOCKET_DEBUG2(PRINTF("socrfl: UTF16LE BOM detected\n"); + DEBUGSOCKFLUSH); + } + } else + { /* No BOM specified. If UTF16, default BOM to UTF16BE per Unicode + standard + */ + if (CHSET_UTF16 == ichset) + { + SOCKET_DEBUG2(PRINTF("socrfl: UTF16BE BOM assumed\n"); + DEBUGSOCKFLUSH); + iod->ichset = ichset = CHSET_UTF16BE; + } + } + } else + { /* Insufficient characters to form a BOM so no BOM present. Like above, if in + UTF16 mode, default to UTF16BE per the Unicode standard. + */ + if (CHSET_UTF16 == ichset) + { + SOCKET_DEBUG2(PRINTF("socrfl: UTF16BE BOM assumed\n"); + DEBUGSOCKFLUSH); + iod->ichset = ichset = CHSET_UTF16BE; + } + } + } else + { /* Check for UTF8 BOM. If found, just eliminate it. */ + if (UTF8_BOM_LEN <= bytes_read && (0 == memcmp(buffptr, UTF8_BOM, UTF8_BOM_LEN))) + { + bytes_read -= UTF8_BOM_LEN; /* Throw way BOM */ + SOCKET_DEBUG2(PRINTF("socrfl: UTF8 BOM detected/ignored\n"); + DEBUGSOCKFLUSH); + } + } + } + if (socketptr->first_read) + { + if (CHSET_UTF16BE == ichset || CHSET_UTF16LE == ichset) + { + get_chset_desc(&chset_names[ichset]); + if (has_delimiter) + iosocket_delim_conv(socketptr, ichset); + } + socketptr->first_read = FALSE; + } + if (bytes_read && has_delimiter) + { /* ------- check to see if it is a delimiter -------- */ + SOCKET_DEBUG2(PRINTF("socrfl: Searching for delimiter\n"); DEBUGSOCKFLUSH); + for (ii = 0; ii < socketptr->n_delimiter; ii++) + { + if (bytes_read < socketptr->idelimiter[ii].len) + continue; + if (0 == memcmp(socketptr->idelimiter[ii].addr, + stringpool.free + bytes_read - socketptr->idelimiter[ii].len, + socketptr->idelimiter[ii].len)) + { + terminator = TRUE; + match_delim = ii; + memcpy(iod->dollar.zb, socketptr->idelimiter[ii].addr, + MIN(socketptr->idelimiter[ii].len, ESC_LEN - 1)); + iod->dollar.zb[MIN(socketptr->idelimiter[ii].len, ESC_LEN - 1)] = '\0'; + memcpy(dsocketptr->dollar_key, socketptr->idelimiter[ii].addr, + MIN(socketptr->idelimiter[ii].len, DD_BUFLEN - 1)); + dsocketptr->dollar_key[MIN(socketptr->idelimiter[ii].len, DD_BUFLEN - 1)] = '\0'; + break; + } + } + SOCKET_DEBUG2( + if (terminator) + PRINTF("socrfl: Delimiter found - match_delim: %d\n", match_delim); + else + PRINTF("socrfl: Delimiter not found\n"); + DEBUGSOCKFLUSH; + ); + } + if (!terminator) + more_data = TRUE; + } else if (EINTR == errno && !out_of_time) /* unrelated timer popped */ + { + status = 0; + continue; + } else + { + real_errno = errno; + break; + } + if (bytes_read > MAX_STRLEN) + { + iod->dollar.za = 9; + rts_error(VARLSTCNT(1) ERR_MAXSTRLEN); + } + orig_bytes_read = bytes_read; + if (0 != bytes_read) + { /* find n chars read from [buffptr, buffptr + bytes_read) */ + SOCKET_DEBUG2(PRINTF("socrfl: Start char scan - c_ptr: 0x%08lx c_top: 0x%08lx\n", + buffptr, (buffptr + status)); DEBUGSOCKFLUSH); + for (c_ptr = buffptr, c_top = buffptr + status; + c_ptr < c_top && chars_read < width; + c_ptr += mb_len, chars_read++) + { + mb_len = 1; /* In case of CHSET_M */ + if (!((CHSET_M == ichset) ? 1 : + (CHSET_UTF8 == ichset) ? UTF8_VALID(c_ptr, c_top, mb_len) : + (CHSET_UTF16BE == ichset) ? UTF16BE_VALID(c_ptr, c_top, mb_len) : + UTF16LE_VALID(c_ptr, c_top, mb_len))) + { /* This char is not valid unicode but this is only an error if entire char is + in the buffer. Else we will ignore it and it will be rebuffered further down. + First, we need to find its (real) length as xx_VALID set it to one when it + was determined to be invalid. + */ + mb_len = (CHSET_M == ichset) ? 0 : + (CHSET_UTF8 == ichset) ? UTF8_MBFOLLOW(c_ptr) : + (CHSET_UTF16BE == ichset) ? UTF16BE_MBFOLLOW(c_ptr, c_top) : + UTF16LE_MBFOLLOW(c_ptr, c_top); + mb_len++; /* Account for first byte of char */ + if (0 == mb_len || c_ptr + mb_len <= c_top) + { /* The entire char is in the buffer.. badchar */ +#ifdef UNICODE_SUPPORTED + if (CHSET_UTF8 == ichset) + { + iosocket_readfl_badchar(v, (int)((unsigned char *)c_ptr - stringpool.free), + 0, c_ptr, c_top); + UTF8_BADCHAR(0, c_ptr, c_top, 0, NULL); + } else /* UTF16LE or UTF16BE */ + { + inv_beg = c_ptr; + if ((c_ptr += 2) >= c_top) + c_ptr = c_top; + iosocket_readfl_badchar(v, + (int)((unsigned char *)inv_beg - stringpool.free), + (int)(c_ptr - inv_beg), inv_beg, c_top); + UTF8_BADCHAR((int)(c_ptr - inv_beg), inv_beg, c_top, + chset_names[ichset].len, chset_names[ichset].addr); + } +#endif + } + } + if (c_ptr + mb_len > c_top) /* Verify entire char is in buffer */ + break; + } + SOCKET_DEBUG2(PRINTF("socrfl: End char scan - c_ptr: 0x%08lx c_top: 0x%08lx\n", + c_ptr, c_top); DEBUGSOCKFLUSH); + if (c_ptr < c_top) /* width size READ completed OR partial last char, push back bytes into input buffer */ + { + iosocket_unsnr(socketptr, c_ptr, c_top - c_ptr); + bytes_read -= (int)(c_top - c_ptr); /* We will be re-reading these bytes */ + requeue_done = TRUE; /* Force single (full) char read next time through */ + SOCKET_DEBUG2(PRINTF("socrfl: Requeue of %d bytes done - adjusted bytes_read: %d\n", + (c_top - c_ptr), bytes_read); DEBUGSOCKFLUSH); + } + } + if (terminator) + { + assert(0 != bytes_read); + bytes_read -= socketptr->idelimiter[match_delim].len; + c_ptr -= socketptr->idelimiter[match_delim].len; + UNICODE_ONLY(chars_read -= socketptr->idelimiter[match_delim].char_len); + NON_UNICODE_ONLY(chars_read = bytes_read); + SOCKET_DEBUG2(PRINTF("socrfl: Terminator found - bytes_read reduced by %d bytes to %d\n", + socketptr->idelimiter[match_delim].len, bytes_read); DEBUGSOCKFLUSH); + SOCKET_DEBUG2(PRINTF("socrfl: .. c_ptr also reduced to 0x%08lx\n", c_ptr); DEBUGSOCKFLUSH); + } + /* If we read as much as we needed or if the buffer was totally full (last char or 3 might be part of an + incomplete character than can never be completed in this buffer) or if variable length, no delim with + chars available and no more data or outofband or have data including a terminator, we are then done. Note + that we are explicitly not handling jobinterrupt outofband here because it is handled above where it needs + to be done to be able to cleanly requeue any input (before delimiter procesing). + */ + if ((chars_read >= width) || + (MAX_STRLEN <= orig_bytes_read) || + (vari && !has_delimiter && 0 != chars_read && !more_data) || + (status > 0 && terminator)) + break; + if (0 != outofband) + { + outofband_terminate = TRUE; + break; + } + if (timed) + { + if (msec_timeout > 0) + { + sys_get_curr_time(&cur_time); + cur_time = sub_abs_time(&end_time, &cur_time); + if (0 > cur_time.at_sec) + { + out_of_time = TRUE; + cancel_timer(timer_id); + SOCKET_DEBUG(PRINTF("socrfl: Out of time detected and set\n")); + break; + } + } else if (!more_data) + break; + } + } + if (EINTR == real_errno) + status = 0; /* don't treat a or timeout as an error */ + if (timed) + { + if (0 < msec_timeout) + { +#ifdef UNIX + FCNTL3(socketptr->sd, F_SETFL, flags, fcntl_res); + if (fcntl_res < 0) + { + iod->dollar.za = 9; + save_errno = errno; + errptr = (char *)STRERROR(errno); + rts_error(VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, LEN_AND_LIT("F_SETFL FOR RESTORING SOCKET OPTIONS"), + save_errno, LEN_AND_STR(errptr)); + } +#endif + if (out_of_time) + { + ret = FALSE; + SOCKET_DEBUG(PRINTF("socrfl: Out of time to be returned (1)\n")); + } else + cancel_timer(timer_id); + } else if ((chars_read < width) && !(has_delimiter && terminator)) + { + ret = FALSE; + SOCKET_DEBUG(PRINTF("socrfl: Out of time to be returned (2)\n")); + } + } + /* If we terminated due to outofband, set up restart info. We may or may not restart (any outofband is capable of + restart) but set it up for at least the more common reasons (^C and job interrupt). + + Some restart info is kept in our iodesc block, but the buffer address information is kept in an mv_stent so if + the stack is garbage collected during the interrupt we don't lose track of where our stuff is saved away. + */ + if (outofband_terminate) + { + SOCKET_DEBUG(PRINTF("socrfl: outofband interrupt received (%d) -- queueing mv_stent for read intr\n", outofband); + DEBUGSOCKFLUSH); + PUSH_MV_STENT(MVST_ZINTDEV); + mv_chain->mv_st_cont.mvs_zintdev.io_ptr = iod; + mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = TRUE; + mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr = (char *)stringpool.free; + mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.len = bytes_read; + sockintr->who_saved = sockwhich_readfl; + if (0 < msec_timeout && NO_M_TIMEOUT != msec_timeout) + { + sockintr->end_time = end_time; + sockintr->end_time_valid = TRUE; + cancel_timer(timer_id); /* Worry about timer if/when we come back */ + } + sockintr->max_bufflen = max_bufflen; + sockintr->bytes_read = bytes_read; + sockintr->chars_read = chars_read; + dsocketptr->mupintr = TRUE; + stringpool.free += bytes_read; /* Don't step on our parade in the interrupt */ + socketus_interruptus++; + SOCKET_DEBUG2(PRINTF("socrfl: .. mv_stent: bytes_read: %d chars_read: %d max_bufflen: %d " + "interrupts: %d buffer_start: 0x%08lx\n", + bytes_read, chars_read, max_bufflen, socketus_interruptus, stringpool.free); DEBUGSOCKFLUSH); + SOCKET_DEBUG(if (sockintr->end_time_valid) PRINTF("socrfl: .. endtime: %d/%d timeout: %d msec_timeout: %d\n", + end_time.at_sec, end_time.at_usec, timeout, msec_timeout); + DEBUGSOCKFLUSH); + outofband_action(FALSE); + GTMASSERT; /* Should *never* return from outofband_action */ + return FALSE; /* For the compiler.. */ + } + if (chars_read > 0) + { /* there's something to return */ + v->str.len = INTCAST(c_ptr - stringpool.free); + v->str.addr = (char *)stringpool.free; + UNICODE_ONLY(v->str.char_len = chars_read); + assert(v->str.len == bytes_read); + SOCKET_DEBUG(PRINTF("socrfl: String to return bytelen: %d charlen: %d iod-width: %d wrap: %d\n", + v->str.len, chars_read, iod->width, iod->wrap); DEBUGSOCKFLUSH); + SOCKET_DEBUG2(PRINTF("socrfl: x: %d y: %d\n", iod->dollar.x, iod->dollar.y); DEBUGSOCKFLUSH); + if (((iod->dollar.x += chars_read) >= iod->width) && iod->wrap) + { + iod->dollar.y += (iod->dollar.x / iod->width); + if (0 != iod->length) + iod->dollar.y %= iod->length; + iod->dollar.x %= iod->width; + } + if (CHSET_M != ichset && CHSET_UTF8 != ichset) + { + SOCKET_DEBUG2(PRINTF("socrfl: Converting UTF16xx data back to UTF8 for internal use\n"); DEBUGSOCKFLUSH); + v->str.len = gtm_conv(chset_desc[ichset], chset_desc[CHSET_UTF8], &v->str, NULL, NULL); + v->str.addr = (char *)stringpool.free; + stringpool.free += v->str.len; + } + + } else + { + v->str.len = 0; + v->str.addr = dsocketptr->dollar_key; + } + if (status >= 0) + { /* no real problems */ + iod->dollar.zeof = FALSE; + iod->dollar.za = 0; + memcpy(dsocketptr->dollar_device, "0", SIZEOF("0")); + } else + { /* there's a significant problem */ + SOCKET_DEBUG(PRINTF("socrfl: Error handling triggered - status: %d\n", status); DEBUGSOCKFLUSH); + if (0 == chars_read) + iod->dollar.x = 0; + iod->dollar.za = 9; + len = SIZEOF(ONE_COMMA) - 1; + memcpy(dsocketptr->dollar_device, ONE_COMMA, len); + errptr = (char *)STRERROR(real_errno); + errlen = STRLEN(errptr); + memcpy(&dsocketptr->dollar_device[len], errptr, errlen + 1); /* + 1 for null */ +#ifdef UNIX + if (io_curr_device.in == io_std_device.in) + { + if (!prin_in_dev_failure) + prin_in_dev_failure = TRUE; + else + { + send_msg(VARLSTCNT(1) ERR_NOPRINCIO); + stop_image_no_core(); + } + } +#endif + if (iod->dollar.zeof || -1 == status || 0 < iod->error_handler.len) + { + iod->dollar.zeof = TRUE; + if (socketptr->ioerror) + rts_error(VARLSTCNT(6) ERR_IOEOF, 0, ERR_TEXT, 2, errlen, errptr); + } else + iod->dollar.zeof = TRUE; + } + SOCKET_DEBUG(if (!ret && out_of_time) PRINTF("socrfl: Returning from read due to timeout\n"); + else PRINTF("socrfl: Returning from read with success indicator set to %d\n", ret)); + SOCKET_DEBUG(fflush(stdout)); + return (ret); +} diff --git a/sr_port/iosocket_snr.c b/sr_port/iosocket_snr.c new file mode 100644 index 0000000..698001c --- /dev/null +++ b/sr_port/iosocket_snr.c @@ -0,0 +1,372 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iosocket_snr.c + * description: + * -- takes care of the buffering of the recv for the socket device (and possible tcp device) + * parameters: + * -- socketptr pointer to a socket device, where we get socket descriptor, buffer and offset in buffer + * -- buffer pointer to the buffer where to return stuff + * -- maxlength maximum number of bytes to get + * -- flags flags to be passed to recv() + * -- time_for_read pointer to the timeout structure used by select() + * -- extra_status reports either a timeout or a lost-of-connection + * return: + * -- got some stuff to return return number of bytes received + * -- got nothing and timed out return 0 + * -- lost-of-connection return -2 + * -- error condition return -1, with errno set + * side note: + * -- use our own buffer if the requested size is smaller than our own buffer size + * -- use the caller's buffer if the requested size is bigger than our own buffer + * control flow: + * -- if we already have some leftover, use it and figure out how much is still needed + * -- if there is still need to read, figure out whether to use our buffer or the passed-in buffer + * -- select so that this operation is timed + * -- if select returns positive, recv, otherwise, return timeout + * -- if recv gets nothing, return lost-of-connection + * -- if the device buffer is used, move it over to the return buffer and update the device buffer pointer + */ +#include "mdef.h" +#include +#include +#include "gtm_stdio.h" +#include "gtm_time.h" +#include "gtm_inet.h" +#include "gtm_string.h" +#ifdef UNIX +#include "gtm_fcntl.h" +#include "eintr_wrappers.h" +static int fcntl_res; +#ifdef DEBUG +#include /* for gettimeofday */ +#endif +#ifdef GTM_USE_POLL_FOR_SUBSECOND_SELECT +#include +#endif +#endif +#include "gt_timer.h" +#include "io.h" +#include "iotimer.h" +#include "iotcpdef.h" +#include "iotcproutine.h" +#include "stringpool.h" +#include "iosocketdef.h" +#include "min_max.h" +#include "gtm_utf8.h" +#include "outofband.h" + +/* MAX_SNR_IO is for read loop in iosocket_snr_utf_prebuffer(). It is possible for a series of interrupts (one + from each active region) to interfere with this read so be generous here. +*/ +#define MAX_SNR_IO 50 + +GBLREF io_pair io_curr_device; +GBLREF bool out_of_time; +GBLREF spdesc stringpool; +GBLREF tcp_library_struct tcp_routines; +GBLREF int4 outofband; + +/* Local routine we aren't making static due to increased debugging difficult static routines make */ +ssize_t iosocket_snr_io(socket_struct *socketptr, void *buffer, size_t maxlength, int flags, ABS_TIME *time_for_read); + +/* Select aNd Receive */ +ssize_t iosocket_snr(socket_struct *socketptr, void *buffer, size_t maxlength, int flags, ABS_TIME *time_for_read) +{ + int status; + ssize_t bytesread, recvsize; + void *recvbuff; + + SOCKET_DEBUG2(PRINTF("socsnr: socketptr: %08lx buffer: %08lx maxlength: %d, flags: %d\n", + socketptr, buffer, maxlength, flags); DEBUGSOCKFLUSH); + /* ====================== use leftover from the previous read, if there is any ========================= */ + assert(maxlength > 0); + if (socketptr->buffered_length > 0) + { + SOCKET_DEBUG2(PRINTF("socsnr: read from buffer - buffered_length: %d\n", socketptr->buffered_length); + DEBUGSOCKFLUSH); + bytesread = MIN(socketptr->buffered_length, maxlength); + memcpy(buffer, (void *)(socketptr->buffer + socketptr->buffered_offset), bytesread); + socketptr->buffered_offset += bytesread; + socketptr->buffered_length -= bytesread; + SOCKET_DEBUG2(PRINTF("socsnr: after buffer read - buffered_offset: %d buffered_length: %d\n", + socketptr->buffered_offset, socketptr->buffered_length); DEBUGSOCKFLUSH); + return bytesread; + } + /* ===================== decide on which buffer to use and the size of the recv ======================== */ + if (socketptr->buffer_size > maxlength) + { + recvbuff = socketptr->buffer; + recvsize = socketptr->buffer_size; + } else + { + recvbuff = buffer; + recvsize = maxlength; + } + VMS_ONLY(recvsize = MIN(recvsize, VMS_MAX_TCP_IO_SIZE)); + SOCKET_DEBUG2(PRINTF("socsnr: recvsize set to %d\n", recvsize); DEBUGSOCKFLUSH); + + /* =================================== select and recv ================================================= */ + assert(0 == socketptr->buffered_length); + socketptr->buffered_length = 0; + bytesread = (int)iosocket_snr_io(socketptr, recvbuff, recvsize, flags, time_for_read); + SOCKET_DEBUG2(PRINTF("socsnr: bytes read from recv: %d timeout: %d\n", bytesread, out_of_time); DEBUGSOCKFLUSH); + if (0 < bytesread) + { /* -------- got something this time ----------- */ + if (recvbuff == socketptr->buffer) + { + if (bytesread <= maxlength) + memcpy(buffer, socketptr->buffer, bytesread); + else + { /* -------- got some stuff for future recv -------- */ + memcpy(buffer, socketptr->buffer, maxlength); + socketptr->buffered_length = bytesread - maxlength; + bytesread = socketptr->buffered_offset = maxlength; + SOCKET_DEBUG2(PRINTF("socsnr: Buffer updated post read - buffered_offset: %d " + "buffered_length: %d\n", + socketptr->buffered_offset, socketptr->buffered_length); DEBUGSOCKFLUSH); + } + } + } + return bytesread; +} + +#ifdef DEBUG +/* hold gettimeofday before and after select to debug AIX spin */ +static struct timeval tvbefore, tvafter; +#endif + +/* Do the IO dirty work. Note the return value can be from either select() or recv(). + This would be a static routine but that just makes it harder to debug. +*/ +ssize_t iosocket_snr_io(socket_struct *socketptr, void *buffer, size_t maxlength, int flags, ABS_TIME *time_for_read) +{ + int status, bytesread, real_errno; + fd_set tcp_fd; + ABS_TIME lcl_time_for_read; +#ifdef GTM_USE_POLL_FOR_SUBSECOND_SELECT + long poll_timeout; + unsigned long poll_nfds; + struct pollfd poll_fdlist[1]; +#endif + + SOCKET_DEBUG2(PRINTF("socsnrio: Socket read request - socketptr: %08lx buffer: %08lx maxlength: %d flags: %d ", + socketptr, buffer, maxlength, flags); DEBUGSOCKFLUSH); + SOCKET_DEBUG2(PRINTF("time_for_read->at_sec: %d usec: %d\n", time_for_read->at_sec, time_for_read->at_usec); + DEBUGSOCKFLUSH); + DEBUG_ONLY(gettimeofday(&tvbefore, NULL);) +#ifndef GTM_USE_POLL_FOR_SUBSECOND_SELECT + FD_ZERO(&tcp_fd); + FD_SET(socketptr->sd, &tcp_fd); + assert(0 != FD_ISSET(socketptr->sd, &tcp_fd)); + lcl_time_for_read = *time_for_read; + status = tcp_routines.aa_select(socketptr->sd + 1, (void *)(&tcp_fd), (void *)0, (void *)0, &lcl_time_for_read); +#else + poll_fdlist[0].fd = socketptr->sd; + poll_fdlist[0].events = POLLIN; + poll_nfds = 1; + poll_timeout = time_for_read->at_usec / 1000; /* convert to millisecs */ + status = poll(&poll_fdlist[0], poll_nfds, poll_timeout); +#endif + real_errno = errno; + DEBUG_ONLY(gettimeofday(&tvafter, NULL);) + SOCKET_DEBUG2(PRINTF("socsnrio: Select return code: %d :: errno: %d\n", status, real_errno); DEBUGSOCKFLUSH); + if (0 < status) + { + bytesread = tcp_routines.aa_recv(socketptr->sd, buffer, maxlength, flags); + real_errno = errno; + SOCKET_DEBUG2(PRINTF("socsnrio: aa_recv return code: %d :: errno: %d\n", bytesread, errno); DEBUGSOCKFLUSH); + if ((0 == bytesread) || + ((-1 == bytesread) && (ECONNRESET == real_errno || EPIPE == real_errno || EINVAL == real_errno))) + { /* ----- lost connection ------- */ + if (0 == bytesread) + errno = ECONNRESET; + return (ssize_t)(-2); + } + SOCKET_DEBUG2(errno = real_errno); + return bytesread; + } + SOCKET_DEBUG2(errno = real_errno); + return (ssize_t)status; +} + +/* When scanning for delimiters, we have to make sure that the next read can pull in at least one full utf char. + Failure to do this means that if a partial utf8 char is read, it will be rebuffered, reread, rebuffered, forever. + A return code of zero indicates a timeout error occured. A negative return code indicates an IO error of some sort. + A positive return code is the length in bytes of the next unicode char in the buffer. +*/ +ssize_t iosocket_snr_utf_prebuffer(io_desc *iod, socket_struct *socketptr, int flags, ABS_TIME *time_for_read, + boolean_t wait_for_input) +{ + int mblen, bytesread, real_errno; + ssize_t readlen; + char *readptr; + + assert(CHSET_M != iod->ichset); + SOCKET_DEBUG2(PRINTF("socsnrupb: Enter prebuffer: buffered_length: %d wait_for_input: %d\n", + socketptr->buffered_length, wait_for_input); DEBUGSOCKFLUSH); + /* See if there is *anything* in the buffer */ + if (0 == socketptr->buffered_length) + { /* Buffer is empty, read at least one char into it so we can check how many we need */ + do + { + bytesread = (int)iosocket_snr_io(socketptr, socketptr->buffer, socketptr->buffer_size, flags, + time_for_read); + SOCKET_DEBUG2(real_errno = errno); + SOCKET_DEBUG2(PRINTF("socsnrupb: Buffer empty - bytes read: %d errno: %d\n", bytesread, real_errno); + DEBUGSOCKFLUSH); + SOCKET_DEBUG2(errno = real_errno); + } while (((-1 == bytesread && EINTR == errno) || (0 == bytesread && wait_for_input)) && + !out_of_time && 0 == outofband); + if (out_of_time || 0 != outofband) + { + SOCKET_DEBUG(if (out_of_time) PRINTF("socsnrupb: Returning due to timeout\n"); + else PRINTF("socsnrupb: Returning due to outofband\n"); DEBUGSOCKFLUSH); + if (0 < bytesread) + { /* If we read anything, be sure to consider it buffered */ + socketptr->buffered_length = bytesread; + socketptr->buffered_offset = 0; + } + return 0; + } + if (0 >= bytesread) + { + SOCKET_DEBUG2(real_errno = errno); + SOCKET_DEBUG2(PRINTF("socsnrupb: Returning due to error code %d errno: %d\n", bytesread, real_errno); + DEBUGSOCKFLUSH); + SOCKET_DEBUG2(errno = real_errno); + return bytesread; + } + socketptr->buffered_length = bytesread; + socketptr->buffered_offset = 0; + } + /* Compute number of bytes we need for the first char in the buffer */ + readptr = socketptr->buffer + socketptr->buffered_offset; + switch(iod->ichset) + { + case CHSET_UTF8: + mblen = UTF8_MBFOLLOW(readptr); + if (0 > mblen) + mblen = 0; /* Invalid char, just assume one char needed */ + break; + case CHSET_UTF16BE: + mblen = UTF16BE_MBFOLLOW(readptr, readptr + socketptr->buffered_length); + if (0 > mblen) + mblen = 1; /* If buffer is too small we will get -1 here. Assume need 2 chars */ + break; + case CHSET_UTF16LE: + mblen = UTF16LE_MBFOLLOW(readptr, readptr + socketptr->buffered_length); + if (0 > mblen) + mblen = 1; /* If buffer is too small we will get -1 here. Assume need 2 chars */ + break; + case CHSET_UTF16: + /* Special case as we don't know which mode we are in. This should only be used when + checking for BOMs. Check if first char is 0xFF or 0xFE. If it is, return 1 as our + (follow) length. If neither, assume UTF16BE (default UTF16 codeset) and return the + length it gives. + */ + if (0xFF == (unsigned char)*readptr || 0xFE == (unsigned char)*readptr) + mblen = 1; + else + { + mblen = UTF16BE_MBFOLLOW(readptr, readptr + socketptr->buffered_length); + if (0 > mblen) + mblen = 1; /* If buffer is too small we will get -1 here. Assume need 2 chars */ + } + break; + default: + GTMASSERT; + } + mblen++; /* Include first char we were looking at in the required byte length */ + SOCKET_DEBUG2(PRINTF("socsnrupb: Length of char: %d\n", mblen); DEBUGSOCKFLUSH); + if (socketptr->buffered_length < mblen) + { /* Still insufficient chars in the buffer for our utf character. Read some more in. */ + if ((socketptr->buffered_offset + mblen) > socketptr->buffer_size) + { /* Our char won't fit in the buffer. This can only occur if the read point is + right at the end of the buffer since the minimum buffer size is 32K. Solution + is to slide the part of the char that we have down to the beginning of the + buffer so we have plenty of room. Since this is at most 3 bytes, this is not + a major performance concern. + */ + SOCKET_DEBUG2(PRINTF("socsnrupb: Char won't fit in buffer, slide it down\n"); DEBUGSOCKFLUSH); + assert(SIZEOF(int) > socketptr->buffered_length); + assert(socketptr->buffered_offset > socketptr->buffered_length); /* Assert no overlap */ + memcpy(socketptr->buffer, (socketptr->buffer + socketptr->buffered_offset), socketptr->buffered_length); + socketptr->buffered_offset = 0; + } + while (socketptr->buffered_length < mblen) + { + SOCKET_DEBUG2(PRINTF("socsnrupb: Top of read loop for char - buffered_length: %d\n", + socketptr->buffered_length); DEBUGSOCKFLUSH); + readptr = socketptr->buffer + socketptr->buffered_offset + socketptr->buffered_length; + readlen = socketptr->buffer_size - socketptr->buffered_offset - socketptr->buffered_length; + assert(0 < readlen); + bytesread = (int)iosocket_snr_io(socketptr, readptr, readlen, flags, time_for_read); + SOCKET_DEBUG2(PRINTF("socsnrupb: Read %d chars\n", bytesread); DEBUGSOCKFLUSH); + if (0 > bytesread) + { /* Some error occurred. Check for restartable condition. */ + if (EINTR == errno) + if (!out_of_time) + continue; + else + return 0; /* timeout indicator */ + return bytesread; + } + if (out_of_time) + return 0; + socketptr->buffered_length += bytesread; + } + } + SOCKET_DEBUG2(PRINTF("socsnrupb: Returning char length %d -- buffered_length: %d\n", mblen, socketptr->buffered_length); + DEBUGSOCKFLUSH); + return mblen; +} + + +/* Place len bytes pointed by buffer back into socketptr's internal buffer */ +/* Side effect: suppose the last snr was with a length > internal buffer size, we would not have used the internal buffer. For + * that case, unsnr might move data not in the internal buffer into the internal buffer and also might result in buffer + * expansion + */ +void iosocket_unsnr(socket_struct *socketptr, unsigned char *buffer, size_t len) +{ + char *new_buff; + + if (socketptr->buffered_length + len <= socketptr->buffer_size) + { + if (socketptr->buffered_length > 0) + { + if (socketptr->buffered_offset < len) + { + memmove(socketptr->buffer + len, socketptr->buffer + socketptr->buffered_offset, + socketptr->buffered_length); + memmove(socketptr->buffer, buffer, len); + } else + { + memmove(socketptr->buffer, buffer, len); + memmove(socketptr->buffer + len, socketptr->buffer + socketptr->buffered_offset, + socketptr->buffered_length); + } + } else + memmove(socketptr->buffer, buffer, len); + } else + { + new_buff = malloc(socketptr->buffered_length + len); + memcpy(new_buff, buffer, len); + if (socketptr->buffered_length > 0) + memcpy(new_buff + len, socketptr->buffer + socketptr->buffered_offset, socketptr->buffered_length); + free(socketptr->buffer); + socketptr->buffer = new_buff; + } + socketptr->buffered_offset = 0; + socketptr->buffered_length += len; + return; +} diff --git a/sr_port/iosocket_switch.c b/sr_port/iosocket_switch.c new file mode 100644 index 0000000..6811751 --- /dev/null +++ b/sr_port/iosocket_switch.c @@ -0,0 +1,75 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iosocket_switch.c */ + +#include "mdef.h" + +#include "gtm_socket.h" +#include "gtm_inet.h" + +#include "io.h" +#include "iosp.h" +#include "io_params.h" +#include "iotcpdef.h" +#include "gt_timer.h" +#include "iosocketdef.h" + +GBLREF int4 gtm_max_sockets; + +boolean_t iosocket_switch(char *handle, int handle_len, d_socket_struct *from, d_socket_struct *to) +{ + int4 index, ii; + socket_struct *socketptr; + + error_def(ERR_SOCKNOTFND); + error_def(ERR_SOCKETEXIST); + error_def(ERR_SOCKMAX); + + if ((NULL == from) || + (0 > (index = iosocket_handle(handle, &handle_len, FALSE, from)))) + { + rts_error(VARLSTCNT(4) ERR_SOCKNOTFND, 2, handle_len, handle); + return FALSE; + } else + { + /* attach the socket to "to" and set it to current */ + + assert(NULL != to); + if (0 <= iosocket_handle(handle, &handle_len, FALSE, to)) + { + rts_error(VARLSTCNT(4) ERR_SOCKETEXIST, 2, handle_len, handle); + return FALSE; + } + if (gtm_max_sockets <= to->n_socket) + { + rts_error(VARLSTCNT(3) ERR_SOCKMAX, 1, gtm_max_sockets); + return FALSE; + } + socketptr = from->socket[index]; + socketptr->dev = to; + to->socket[to->n_socket++] = socketptr; + to->current_socket = to->n_socket - 1; + + /* detach it from "from" */ + + if (from->current_socket >= index) + from->current_socket--; + + for(ii = index; ii < from->n_socket - 1; ii++) + { + from->socket[ii] = from->socket[ii + 1]; + } + from->n_socket--; + from->socket[from->n_socket] = NULL; + } + return TRUE; +} diff --git a/sr_port/iosocket_use.c b/sr_port/iosocket_use.c new file mode 100644 index 0000000..341eb48 --- /dev/null +++ b/sr_port/iosocket_use.c @@ -0,0 +1,575 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iosocket_use.c */ +#include "mdef.h" + +#include "gtm_socket.h" +#include "gtm_unistd.h" +#include "gtm_iconv.h" +#include "gtm_stdio.h" +#include "gtm_string.h" + +#include +#include "gtm_inet.h" +#include +#include "copy.h" +#include "io.h" +#include "io_params.h" +#include "iotimer.h" +#include "iotcpdef.h" +#include "iotcproutine.h" +#include "gt_timer.h" +#include "iosocketdef.h" +#include "nametabtyp.h" +#include "namelook.h" +#include "stringpool.h" +#include "gtm_conv.h" + +GBLREF io_pair io_curr_device; +GBLREF io_pair io_std_device; +GBLREF io_desc *active_device; +GBLREF tcp_library_struct tcp_routines; +GBLREF d_socket_struct *socket_pool; +GBLREF boolean_t gtm_utf8_mode; +GBLREF spdesc stringpool; +GBLREF UConverter *chset_desc[]; +GBLREF int4 gtm_max_sockets; +GBLREF d_socket_struct *newdsocket; +GBLREF boolean_t dollar_zininterrupt; + +LITREF nametabent filter_names[]; +LITREF unsigned char filter_index[27]; +LITREF unsigned char io_params_size[]; + +void iosocket_use(io_desc *iod, mval *pp) +{ + unsigned char ch, len; + int handled_len, handlea_len, handles_len; + int4 length, width, new_len; + d_socket_struct *dsocketptr; + socket_struct *socketptr, newsocket; + char handlea[MAX_HANDLE_LEN], handles[MAX_HANDLE_LEN], handled[MAX_HANDLE_LEN]; + char addr[SA_MAXLITLEN], *errptr, sockaddr[SA_MAXLITLEN], + temp_addr[SA_MAXLITLEN], ioerror; + unsigned char delimiter_buffer[MAX_N_DELIMITER * (MAX_DELIM_LEN + 1)]; + unsigned char zff_buffer[MAX_ZFF_LEN]; + boolean_t attach_specified = FALSE, + detach_specified = FALSE, + connect_specified = FALSE, + ioerror_specified = FALSE, + listen_specified = FALSE, + socket_specified = FALSE, + delay_specified = FALSE, + nodelay_specified = FALSE, + bfsize_specified = FALSE, + ibfsize_specified = FALSE, + moreread_specified = FALSE, + create_new_socket; + int4 index, n_specified, zff_len, delimiter_len, moreread_timeout; + int fil_type, nodelay, p_offset = 0; + uint4 bfsize = DEFAULT_SOCKET_BUFFER_SIZE, ibfsize; + char *tab; + int save_errno; + size_t d_socket_struct_len; + mstr lcl_zff; + + error_def(ERR_ABNCOMPTINC); + error_def(ERR_ADDRTOOLONG); + error_def(ERR_ANCOMPTINC); + error_def(ERR_ACOMPTBINC); + error_def(ERR_CURRSOCKOFR); + error_def(ERR_DELIMSIZNA); + error_def(ERR_ILLESOCKBFSIZE); + error_def(ERR_SETSOCKOPTERR); + error_def(ERR_SOCKBFNOTEMPTY); + error_def(ERR_SOCKNOTFND); + error_def(ERR_SOCKMAX); + error_def(ERR_TEXT); + error_def(ERR_TTINVFILTER); + error_def(ERR_ZFF2MANY); + error_def(ERR_DEVPARMNEG); + error_def(ERR_DELIMWIDTH); + error_def(ERR_ZINTRECURSEIO); + error_def(ERR_MRTMAXEXCEEDED); + + assert(iod->state == dev_open); + assert(iod->type == gtmsocket); + dsocketptr = (d_socket_struct *)(iod->dev_sp); + /* ---------------------------------- parse the command line ------------------------------------ */ + n_specified = 0; + zff_len = -1; /* indicates neither ZFF nor ZNOFF specified */ + delimiter_len = -1; /* indicates neither DELIM nor NODELIM specified */ + + /* A read or wait was interrupted for this device. Allow only parmless use in $zinterrupt code for + and interrupted device. + */ + if (iop_eol != *(pp->str.addr + p_offset)) + { /* Parameters were specified */ + if (dsocketptr->mupintr) + { /* And if we are in $zinterrupt code this is not allowed */ + if (dollar_zininterrupt) + rts_error(VARLSTCNT(1) ERR_ZINTRECURSEIO); + /* We are not in $zinterrupt code and this device was not resumed properly + so clear its restartability. + */ + io_find_mvstent(iod, TRUE); + dsocketptr->mupintr = FALSE; + } + } else if (dsocketptr->mupintr && !dollar_zininterrupt) + { /* The interrupted read was not properly resumed so clear it now */ + dsocketptr->mupintr = FALSE; + dsocketptr->sock_save_state.who_saved = sockwhich_invalid; + io_find_mvstent(iod, TRUE); + } + + while (iop_eol != (ch = *(pp->str.addr + p_offset++))) + { + assert((params)ch < (params)n_iops); + switch (ch) + { + case iop_exception: + iod->error_handler.len = *(pp->str.addr + p_offset); + iod->error_handler.addr = (char *)(pp->str.addr + p_offset + 1); + s2pool(&iod->error_handler); + break; + case iop_filter: + len = *(pp->str.addr + p_offset); + tab = pp->str.addr + p_offset + 1; + if ((fil_type = namelook(filter_index, filter_names, tab, len)) < 0) + { + rts_error(VARLSTCNT(1) ERR_TTINVFILTER); + return; + } + switch (fil_type) + { + case 0: + iod->write_filter |= CHAR_FILTER; + break; + case 1: + iod->write_filter |= ESC1; + break; + case 2: + iod->write_filter &= ~CHAR_FILTER; + break; + case 3: + iod->write_filter &= ~ESC1; + break; + } + break; + case iop_nofilter: + iod->write_filter = 0; + break; + case iop_attach: + n_specified++; + attach_specified = TRUE; + handlea_len = (int)(*(pp->str.addr + p_offset)); + memcpy(handlea, (char *)(pp->str.addr + p_offset + 1), handlea_len); + break; + case iop_detach: + n_specified++; + detach_specified = TRUE; + handled_len = (int)(*(pp->str.addr + p_offset)); + memcpy(handled, (char *)(pp->str.addr + p_offset + 1), handled_len); + break; + case iop_connect: + n_specified++; + connect_specified = TRUE; + len = *(pp->str.addr + p_offset); + if (len < SA_MAXLITLEN) + { + memcpy(sockaddr, (char *)(pp->str.addr + p_offset + 1), len); + sockaddr[len] = '\0'; + } else + rts_error(VARLSTCNT(6) ERR_ADDRTOOLONG, + 4, len, pp->str.addr + p_offset + 1, len, SA_MAXLITLEN); + break; + case iop_delimiter: + n_specified++; + delimiter_len = (int4)(unsigned char)*(pp->str.addr + p_offset); + if (((MAX_DELIM_LEN + 1) * MAX_N_DELIMITER) >= delimiter_len) + memcpy(delimiter_buffer, (pp->str.addr + p_offset + 1), delimiter_len); + else + rts_error(VARLSTCNT(1) ERR_DELIMSIZNA); + break; + case iop_nodelimiter: + delimiter_len = 0; + break; + case iop_zdelay: + delay_specified = TRUE; + break; + case iop_znodelay: + nodelay_specified = TRUE; + break; + case iop_zbfsize: + bfsize_specified = TRUE; + GET_ULONG(bfsize, pp->str.addr + p_offset); + if ((0 == bfsize) || (MAX_SOCKET_BUFFER_SIZE < bfsize)) + rts_error(VARLSTCNT(3) ERR_ILLESOCKBFSIZE, 1, bfsize); + break; + case iop_zibfsize: + ibfsize_specified = TRUE; + GET_ULONG(ibfsize, pp->str.addr + p_offset); + if ((0 == ibfsize) || (MAX_INTERNAL_SOCBUF_SIZE < ibfsize)) + rts_error(VARLSTCNT(3) ERR_ILLESOCKBFSIZE, 1, ibfsize); + break; + case iop_ioerror: + n_specified++; + ioerror_specified = TRUE; + ioerror = *(char *)(pp->str.addr + p_offset + 1); + break; + case iop_zlisten: + n_specified++; + listen_specified = TRUE; + len = *(pp->str.addr + p_offset); + if (len < SA_MAXLITLEN) + { + memcpy(sockaddr, (char *)(pp->str.addr + p_offset + 1), len); + sockaddr[len] = '\0'; + } else + rts_error(VARLSTCNT(6) ERR_ADDRTOOLONG, + 4, len, pp->str.addr + p_offset + 1, len, SA_MAXLITLEN); + break; + case iop_socket: + n_specified++; + socket_specified = TRUE; + handles_len = (int)(*(pp->str.addr + p_offset)); + memcpy(handles, (char *)(pp->str.addr + p_offset + 1), handles_len); + break; + case iop_ipchset: +#if defined(KEEP_zOS_EBCDIC) || defined(VMS) + if ((iconv_t)0 != iod->input_conv_cd) + ICONV_CLOSE_CD(iod->input_conv_cd); + SET_CODE_SET(iod->in_code_set, (char *)(pp->str.addr + p_offset + 1)); + if (DEFAULT_CODE_SET != iod->in_code_set) + ICONV_OPEN_CD(iod->input_conv_cd, INSIDE_CH_SET, (char *)(pp->str.addr + p_offset + 1)); +#endif + break; + case iop_opchset: +#if defined(KEEP_zOS_EBCDIC) || defined(VMS) + if ((iconv_t)0 != iod->output_conv_cd) + ICONV_CLOSE_CD(iod->output_conv_cd); + SET_CODE_SET(iod->out_code_set, (char *)(pp->str.addr + p_offset + 1)); + if (DEFAULT_CODE_SET != iod->out_code_set) + ICONV_OPEN_CD(iod->output_conv_cd, (char *)(pp->str.addr + p_offset + 1), INSIDE_CH_SET); +#endif + break; + case iop_zff: + if (MAX_ZFF_LEN >= (zff_len = (int4)(unsigned char)*(pp->str.addr + p_offset))) + memcpy(zff_buffer, (char *)(pp->str.addr + p_offset + 1), zff_len); + else + rts_error(VARLSTCNT(4) ERR_ZFF2MANY, 2, zff_len, MAX_ZFF_LEN); + break; + case iop_znoff: + zff_len = 0; + break; + case iop_length: + GET_LONG(length, pp->str.addr + p_offset); + if (length < 0) + rts_error(VARLSTCNT(1) ERR_DEVPARMNEG); + iod->length = length; + break; + case iop_width: + /* SOCKET WIDTH is handled the same way as TERMINAL WIDTH */ + GET_LONG(width, pp->str.addr + p_offset); + if (width < 0) + rts_error(VARLSTCNT(1) ERR_DEVPARMNEG); + if (0 == width) + { + iod->width = TCPDEF_WIDTH; + iod->wrap = FALSE; + } else + { + iod->width = width; + iod->wrap = TRUE; + } + break; + case iop_wrap: + iod->wrap = TRUE; + break; + case iop_nowrap: + iod->wrap = FALSE; + break; + case iop_morereadtime: + /* Time in milliseconds socket read will wait for more data before returning */ + GET_LONG(moreread_timeout, pp->str.addr + p_offset); + if (-1 == moreread_timeout) + moreread_timeout = DEFAULT_MOREREAD_TIMEOUT; + else if (-1 > moreread_timeout) + rts_error(VARLSTCNT(1) ERR_DEVPARMNEG); + else if (MAX_MOREREAD_TIMEOUT < moreread_timeout) + rts_error(VARLSTCNT(3) ERR_MRTMAXEXCEEDED, 1, MAX_MOREREAD_TIMEOUT); + moreread_specified = TRUE; + break; + default: + /* ignore deviceparm */ + break; + } + p_offset += ((io_params_size[ch] == IOP_VAR_SIZE) ? + (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]); + } + /* ------ return immediately if no flag, worth a check because it is mostly true ------------ */ + if (1 == p_offset) + return; + /* ------------------------------ compatibility verification -------------------------------- */ + if ((socket_specified) && ((n_specified > 2) || ((2 == n_specified) && (0 >= delimiter_len)))) + { + rts_error(VARLSTCNT(8) ERR_ACOMPTBINC, 6, LEN_AND_LIT("SOCKET"), LEN_AND_LIT("DELIMITER"), LEN_AND_LIT("USE")); + return; + } + if (connect_specified && listen_specified) + { + rts_error(VARLSTCNT(8) ERR_ABNCOMPTINC, 6, LEN_AND_LIT("CONNECT"), LEN_AND_LIT("ZLISTEN"), LEN_AND_LIT("USE")); + return; + } + if (delay_specified && nodelay_specified) + { + rts_error(VARLSTCNT(8) ERR_ABNCOMPTINC, 6, LEN_AND_LIT("DELAY"), LEN_AND_LIT("NODELAY"), LEN_AND_LIT("OPEN")); + return; + } + /* ------------------ make a local copy of device structure to play with -------------------- */ + d_socket_struct_len = SIZEOF(d_socket_struct) + (SIZEOF(socket_struct) * (gtm_max_sockets - 1)); + memcpy(newdsocket, dsocketptr, d_socket_struct_len); + /* --------------- handle the two special cases attach/detach first ------------------------- */ + if (detach_specified) + { + if (1 < n_specified) + { + rts_error(VARLSTCNT(6) ERR_ANCOMPTINC, 4, LEN_AND_LIT("DETACH"), LEN_AND_LIT("USE")); + return; + } + if (NULL == socket_pool) + iosocket_poolinit(); + iosocket_switch(handled, handled_len, newdsocket, socket_pool); + memcpy(dsocketptr, newdsocket, d_socket_struct_len); + if (0 > dsocketptr->current_socket) + { + io_curr_device.in = io_std_device.in; + io_curr_device.out = io_std_device.out; + } + return; /* detach can only be specified by itself */ + } + if (attach_specified) + { /* NOTE: A socket could be moved from one device to another using DETACH/ATTACH. A socket does not carry I[O]CHSET with + * it while being moved. Such a socket will use the I[O]CHSET of the device it is ATTACHed to. If there is input still + * buffered, this may cause unintentional consequences in the application if I[O]CHSET changes. GT.M does not detect + * (or report) a change in I[O]CHSET due to DETACH/ATTACH. + */ + if (1 < n_specified) + { + rts_error(VARLSTCNT(6) ERR_ANCOMPTINC, 4, LEN_AND_LIT("ATTACH"), LEN_AND_LIT("USE")); + return; + } + if (NULL == socket_pool) + { + rts_error(VARLSTCNT(4) ERR_SOCKNOTFND, 2, handlea_len, handlea); + return; + } + iosocket_switch(handlea, handlea_len, socket_pool, newdsocket); + memcpy(dsocketptr, newdsocket, d_socket_struct_len); + return; /* attach can only be specified by itself */ + } + /* ------------ create/identify the socket to work on and make a local copy ----------------- */ + if (create_new_socket = (listen_specified || connect_specified)) /* real "=" */ + { + /* allocate the structure for a new socket */ + if (NULL == (socketptr = iosocket_create(sockaddr, bfsize, -1))) + return; + /* give the new socket a handle */ + iosocket_handle(handles, &handles_len, TRUE, dsocketptr); + socketptr->handle_len = handles_len; + memcpy(socketptr->handle, handles, handles_len); + socketptr->dev = newdsocket; /* use newdsocket temporarily for the sake of bind/connect */ + } else + { + if (socket_specified) + { + /* use the socket flag to identify which socket to apply changes */ + if (0 > (index = iosocket_handle(handles, &handles_len, FALSE, newdsocket))) + { + rts_error(VARLSTCNT(4) ERR_SOCKNOTFND, 2, handles_len, handles); + return; + } + newdsocket->current_socket = index; + socketptr = newdsocket->socket[index]; + } else + { + socketptr = newdsocket->socket[newdsocket->current_socket]; + if (newdsocket->n_socket <= newdsocket->current_socket) + { + assert(FALSE); + rts_error(VARLSTCNT(4) ERR_CURRSOCKOFR, 2, newdsocket->current_socket, newdsocket->n_socket); + return; + } + } + } + newsocket = *socketptr; + /* ---------------------- apply changes to the local copy of the socket --------------------- */ + if (0 <= delimiter_len) + { + iosocket_delimiter(delimiter_buffer, delimiter_len, &newsocket, (0 == delimiter_len)); + /* The delimiter has changed. The iosocket_readfl/write routine won't notice so we have to do + the UTF16xx conversion since we changed it. + */ + SOCKET_DEBUG2(PRINTF("socuse: Delimiter(s) replaced - num delims: %d delimiter_len: %d ichset: %d ochset: %d\n", + newsocket.n_delimiter, delimiter_len, iod->ichset, iod->ochset); DEBUGSOCKFLUSH); + if (0 < delimiter_len) + { + if (!newsocket.first_read && (CHSET_UTF16BE == iod->ichset || CHSET_UTF16LE == iod->ichset)) + { /* We have been reading with this socket so convert this new delimiter set */ + SOCKET_DEBUG2(PRINTF("socuse: Converting new delimiters for input\n"); DEBUGSOCKFLUSH); + iosocket_delim_conv(&newsocket, iod->ichset); + } + if (!newsocket.first_write && (CHSET_UTF16BE == iod->ochset || CHSET_UTF16LE == iod->ochset)) + { /* We have been writing with this socket so convert the new default output delimiter */ + SOCKET_DEBUG2(PRINTF("socuse: Converting new delimiters for output\n"); DEBUGSOCKFLUSH); + if (newsocket.first_read || (CHSET_UTF16BE != iod->ichset && CHSET_UTF16LE != iod->ichset)) + { /* Need to do conversion as iosocket_delim_conv above didn't do it for us */ + SOCKET_DEBUG2(PRINTF("socuse: running convert for write since input didn't do it\n"); + DEBUGSOCKFLUSH); + new_len = gtm_conv(chset_desc[CHSET_UTF8], chset_desc[iod->ochset], + &newsocket.delimiter[0], NULL, NULL); + if (MAX_DELIM_LEN < new_len) + { + rts_error(VARLSTCNT(1) ERR_DELIMSIZNA); + return; + } + } else + { + SOCKET_DEBUG2(PRINTF("socuse: using previous length from read conversion\n"); + DEBUGSOCKFLUSH); + new_len = newsocket.idelimiter[0].len; + } + newsocket.odelimiter0.len = new_len; + UNICODE_ONLY(newsocket.odelimiter0.char_len = newsocket.delimiter[0].char_len); + newsocket.odelimiter0.addr = malloc(new_len); + memcpy(newsocket.odelimiter0.addr, + (newsocket.first_read ? (char *)stringpool.free : newsocket.idelimiter[0].addr), + new_len); + } + } + } + if (iod->wrap && 0 != newsocket.n_delimiter && iod->width < newsocket.delimiter[0].len) + rts_error(VARLSTCNT(4) ERR_DELIMWIDTH, 2, iod->width, newsocket.delimiter[0].len); + if (0 <= zff_len && /* ZFF or ZNOFF specified */ + 0 < (newsocket.zff.len = zff_len)) /* assign the new ZFF len, might be 0 from ZNOFF, or ZFF="" */ + { /* ZFF="non-zero-len-string" specified */ + if (CHSET_UTF16BE == iod->ochset || CHSET_UTF16LE == iod->ochset) /* need conversion of ZFF */ + { + SOCKET_DEBUG2(PRINTF("socuse: Converting zff\n"); DEBUGSOCKFLUSH); + lcl_zff.addr = (char *)zff_buffer; + lcl_zff.len = zff_len; + new_len = gtm_conv(chset_desc[CHSET_UTF8], chset_desc[iod->ochset], &lcl_zff, NULL, NULL); + if (MAX_ZFF_LEN < new_len) + rts_error(VARLSTCNT(4) ERR_ZFF2MANY, 2, new_len, MAX_ZFF_LEN); + if (NULL == newsocket.zff.addr) /* we rely on newsocket.zff.addr being set to 0 in iosocket_create() */ + newsocket.zff.addr = (char *)malloc(MAX_ZFF_LEN); + newsocket.zff.len = new_len; + UNICODE_ONLY(newsocket.zff.char_len = 0); /* don't care */ + memcpy(newsocket.zff.addr, stringpool.free, new_len); + + } else + { /* Store parm without conversion */ + if (gtm_utf8_mode) /* Check if ZFF has any invalid UTF-8 character */ + { /* Note: the ZFF string originates from the source program, so is in UTF-8 mode or M mode + regardless of OCHSET of this device. ZFF is output on WRITE # command, and MUST contain + valid UTF-8 sequence. This validation is handled by gtm_conv in the path above. + */ + utf8_len_strict(zff_buffer, zff_len); + } + if (NULL == newsocket.zff.addr) /* we rely on newsocket.zff.addr being set to 0 in iosocket_create() */ + newsocket.zff.addr = (char *)malloc(MAX_ZFF_LEN); + memcpy(newsocket.zff.addr, zff_buffer, zff_len); + } + } + if (ioerror_specified) + newsocket.ioerror = ('T' == ioerror || 't' == ioerror); + if (nodelay_specified || delay_specified) + newsocket.nodelay = nodelay_specified; /* defaults to DELAY */ + if (ibfsize_specified) + newsocket.bufsiz = ibfsize; + if (moreread_specified) + { + newsocket.moreread_timeout = moreread_timeout; + newsocket.def_moreread_timeout = TRUE; /* need to know this was user-defined in iosocket_readfl.c */ + } + if (!create_new_socket) + { + /* these changes apply to only pre-existing sockets */ + if (bfsize_specified) + newsocket.buffer_size = bfsize; +#ifdef TCP_NODELAY + nodelay = newsocket.nodelay ? 1 : 0; + if ((socketptr->nodelay != newsocket.nodelay) && + (-1 == tcp_routines.aa_setsockopt(newsocket.sd, IPPROTO_TCP, + TCP_NODELAY, &nodelay, SIZEOF(nodelay)))) + { + save_errno = errno; + errptr = (char *)STRERROR(save_errno); + rts_error(VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, LEN_AND_LIT("TCP_NODELAY"), save_errno, LEN_AND_STR(errptr)); + return; + } +#endif + if ((socketptr->bufsiz != newsocket.bufsiz) && + (-1 == tcp_routines.aa_setsockopt(newsocket.sd, SOL_SOCKET, + SO_RCVBUF, &newsocket.bufsiz, SIZEOF(newsocket.bufsiz)))) + { + save_errno = errno; + errptr = (char *)STRERROR(save_errno); + rts_error(VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, LEN_AND_LIT("SO_RCVBUF"), save_errno, LEN_AND_STR(errptr)); + return; + } + if (socketptr->buffer_size != newsocket.buffer_size) + { + if (socketptr->buffered_length > bfsize) + rts_error(VARLSTCNT(4) ERR_SOCKBFNOTEMPTY, 2, bfsize, socketptr->buffered_length); + newsocket.buffer = (char *)malloc(bfsize); + if (0 < socketptr->buffered_length) + { + memcpy(newsocket.buffer, socketptr->buffer + socketptr->buffered_offset, + socketptr->buffered_length); + newsocket.buffered_offset = 0; + } + } + } + /* -------------------------------------- action -------------------------------------------- */ + if ((listen_specified && (!iosocket_bind(&newsocket, NO_M_TIMEOUT, ibfsize_specified))) || + (connect_specified && (!iosocket_connect(&newsocket, 0, ibfsize_specified)))) + { /* error message should be printed from bind/connect */ + if (socketptr->sd > 0) + (void)tcp_routines.aa_close(socketptr->sd); + iosocket_delimiter((unsigned char *)NULL, 0, &newsocket, TRUE); + if (NULL != socketptr->zff.addr) + free(socketptr->zff.addr); + if (NULL != socketptr->buffer) + free(socketptr->buffer); + free(socketptr); + return; + } + /* ------------------------------------ commit changes -------------------------------------- */ + if (create_new_socket) + { + if (gtm_max_sockets <= newdsocket->n_socket) + { + rts_error(VARLSTCNT(3) ERR_SOCKMAX, 1, gtm_max_sockets); + return; + } + /* a new socket is created. so add to the list */ + newsocket.dev = dsocketptr; + newdsocket->socket[newdsocket->n_socket++] = socketptr; + newdsocket->current_socket = newdsocket->n_socket - 1; + } + else if (socketptr->buffer_size != newsocket.buffer_size) + free(socketptr->buffer); + *socketptr = newsocket; + memcpy(dsocketptr, newdsocket, d_socket_struct_len); + return; +} diff --git a/sr_port/iosocket_wait.c b/sr_port/iosocket_wait.c new file mode 100644 index 0000000..5d152f7 --- /dev/null +++ b/sr_port/iosocket_wait.c @@ -0,0 +1,332 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iosocket_wait.c + * + * return a listening socket -- create a new socket for the connection and set it to current + * set it to current + * set $KEY to "CONNECT" + * return a connected socket -- set it to current + * set $KEY to "READ" + * timeout -- set $Test to 1 + */ +#include "mdef.h" +#include +#include "gtm_socket.h" +#include "gtm_inet.h" +#include "gtm_stdio.h" +#include "gtm_string.h" +#include "io_params.h" +#include "gt_timer.h" +#include "io.h" +#include "iotimer.h" +#include "iotcp_select.h" +#include "iotcproutine.h" +#include "iotcpdef.h" +#include "iosocketdef.h" +#include "min_max.h" +#include "outofband.h" +#include "rtnhdr.h" +#include "stack_frame.h" +#include "mv_stent.h" + +#define CONNECTED "CONNECT" +#define READ "READ" + +GBLREF tcp_library_struct tcp_routines; +GBLREF volatile int4 outofband; +GBLREF int4 gtm_max_sockets; +GBLREF int socketus_interruptus; +GBLREF boolean_t dollar_zininterrupt; +GBLREF stack_frame *frame_pointer; +GBLREF unsigned char *stackbase, *stacktop, *msp, *stackwarn; +GBLREF mv_stent *mv_chain; + +boolean_t iosocket_wait(io_desc *iod, int4 timepar) +{ + struct timeval utimeout; + ABS_TIME cur_time, end_time; + struct sockaddr_in peer; /* socket address + port */ + fd_set tcp_fd; + d_socket_struct *dsocketptr; + socket_struct *socketptr, *newsocketptr; + socket_interrupt *sockintr; + char *errptr; + int4 errlen, ii, msec_timeout; + int rv, max_fd, len; + GTM_SOCKLEN_TYPE size; + boolean_t zint_restart; + mv_stent *mv_zintdev; + int retry_num; + error_def(ERR_SOCKACPT); + error_def(ERR_SOCKWAIT); + error_def(ERR_TEXT); + error_def(ERR_SOCKMAX); + error_def(ERR_ZINTRECURSEIO); + error_def(ERR_STACKCRIT); + error_def(ERR_STACKOFLOW); + + /* check for validity */ + assert(iod->type == gtmsocket); + dsocketptr = (d_socket_struct *)iod->dev_sp; + sockintr = &dsocketptr->sock_save_state; + + /* Check for restart */ + if (!dsocketptr->mupintr) + /* Simple path, no worries*/ + zint_restart = FALSE; + else + { /* We have a pending wait restart of some sort - check we aren't recursing on this device */ + if (sockwhich_invalid == sockintr->who_saved) + GTMASSERT; /* Interrupt should never have an invalid save state */ + if (dollar_zininterrupt) + rts_error(VARLSTCNT(1) ERR_ZINTRECURSEIO); + if (sockwhich_wait != sockintr->who_saved) + GTMASSERT; /* ZINTRECURSEIO should have caught */ + SOCKET_DEBUG(PRINTF("socwait: *#*#*#*#*#*#*# Restarted interrupted wait\n"); DEBUGSOCKFLUSH); + mv_zintdev = io_find_mvstent(iod, FALSE); + if (mv_zintdev) + { + if (sockintr->end_time_valid) + /* Restore end_time for timeout */ + end_time = sockintr->end_time; + + /* Done with this mv_stent. Pop it off if we can, else mark it inactive. */ + if (mv_chain == mv_zintdev) + POP_MV_STENT(); /* pop if top of stack */ + else + { /* else mark it unused */ + mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid = FALSE; + mv_zintdev->mv_st_cont.mvs_zintdev.io_ptr = NULL; + } + zint_restart = TRUE; + SOCKET_DEBUG(PRINTF("socwait: mv_stent found - endtime: %d/%d\n", end_time.at_sec, end_time.at_usec); + DEBUGSOCKFLUSH); + } else + SOCKET_DEBUG(PRINTF("socwait: no mv_stent found !!\n"); DEBUGSOCKFLUSH); + dsocketptr->mupintr = FALSE; + sockintr->who_saved = sockwhich_invalid; + } + + /* check for events */ + max_fd = 0; + FD_ZERO(&tcp_fd); + for (ii = 0; ii < dsocketptr->n_socket; ii++) + { + socketptr = dsocketptr->socket[ii]; + if ((socket_listening == socketptr->state) || (socket_connected == socketptr->state)) + { + FD_SET(socketptr->sd, &tcp_fd); + max_fd = MAX(max_fd, socketptr->sd); + } + } + utimeout.tv_sec = timepar; + utimeout.tv_usec = 0; + msec_timeout = timeout2msec(timepar); + sys_get_curr_time(&cur_time); + if (!zint_restart || !sockintr->end_time_valid) + add_int_to_abs_time(&cur_time, msec_timeout, &end_time); + else + { /* end_time taken from restart data. Compute what msec_timeout should be so timeout timer + gets set correctly below. + */ + SOCKET_DEBUG(PRINTF("socwait: Taking timeout end time from wait restart data\n")); + cur_time = sub_abs_time(&end_time, &cur_time); + if (0 > cur_time.at_sec) + { + msec_timeout = -1; + utimeout.tv_sec = 0; + utimeout.tv_usec = 0; + } else + { + msec_timeout = (int4)(cur_time.at_sec * 1000 + cur_time.at_usec / 1000); + utimeout.tv_sec = cur_time.at_sec; + utimeout.tv_usec = (gtm_tv_usec_t)cur_time.at_usec; + } + } + sockintr->end_time_valid = FALSE; + + for ( ; ; ) + { + rv = select(max_fd + 1, (void *)&tcp_fd, (void *)0, (void *)0, + (timepar == NO_M_TIMEOUT ? (struct timeval *)0 : &utimeout)); + if (0 > rv && EINTR == errno) + { + if (0 != outofband) + { + SOCKET_DEBUG(PRINTF("socwait: outofband interrupt received (%d) -- " + "queueing mv_stent for wait intr\n", outofband); DEBUGSOCKFLUSH); + PUSH_MV_STENT(MVST_ZINTDEV); + mv_chain->mv_st_cont.mvs_zintdev.io_ptr = iod; + mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = FALSE; + sockintr->who_saved = sockwhich_wait; + sockintr->end_time = end_time; + sockintr->end_time_valid = TRUE; + dsocketptr->mupintr = TRUE; + socketus_interruptus++; + SOCKET_DEBUG(PRINTF("socwait: mv_stent queued - endtime: %d/%d interrupts: %d\n", + end_time.at_sec, end_time.at_usec, socketus_interruptus); + DEBUGSOCKFLUSH); + outofband_action(FALSE); + GTMASSERT; /* Should *never* return from outofband_action */ + return FALSE; /* For the compiler.. */ + } + sys_get_curr_time(&cur_time); + cur_time = sub_abs_time(&end_time, &cur_time); + if (0 > cur_time.at_sec) + { + rv = 0; /* time out */ + break; + } + utimeout.tv_sec = cur_time.at_sec; + utimeout.tv_usec = (gtm_tv_usec_t)cur_time.at_usec; + } else + break; /* either other error or done */ + } + if (rv == 0) + { + dsocketptr->dollar_key[0] = '\0'; + return FALSE; + } else if (rv < 0) + { + errptr = (char *)STRERROR(errno); + errlen = STRLEN(errptr); + rts_error(VARLSTCNT(6) ERR_SOCKWAIT, 0, ERR_TEXT, 2, errlen, errptr); + return FALSE; + } + /* find out which socket is ready */ + for (ii = 0; ii < dsocketptr->n_socket; ii++) + { + socketptr = dsocketptr->socket[ii]; + if (0 != FD_ISSET(socketptr->sd, &tcp_fd)) + break; + } + assert(ii < dsocketptr->n_socket); + if (socket_listening == socketptr->state) + { + if (gtm_max_sockets <= dsocketptr->n_socket) + { + rts_error(VARLSTCNT(3) ERR_SOCKMAX, 1, gtm_max_sockets); + return FALSE; + } + size = SIZEOF(struct sockaddr_in); + rv = tcp_routines.aa_accept(socketptr->sd, &peer, &size); + if (-1 == rv) + { +#ifdef __hpux + /*ENOBUFS in HP-UX is either because of a memory problem or when we have received a RST just + after a SYN before an accept call. Normally this is not fatal and is just a transient state. Hence + exiting just after a single error of this kind should not be done. So retry in case of HP-UX and ENOBUFS error.*/ + if (ENOBUFS == errno) + { + /*In case of succeeding with select in first go, accept will still get 5ms time difference*/ + retry_num = 0; + while (HPUX_MAX_RETRIES > retry_num) + { + SHORT_SLEEP(5); + for ( ; HPUX_MAX_RETRIES > retry_num; retry_num++) + { + utimeout.tv_sec = 0; + utimeout.tv_usec = HPUX_SEL_TIMEOUT; + FD_ZERO(&tcp_fd); + FD_SET(socketptr->sd, &tcp_fd); + rv = select(max_fd + 1, (void *)&tcp_fd, (void *)0, (void *)0, + &utimeout); + if (0 < rv) + break; + if (0 > rv && (EINTR == errno)) + { + if (0 != outofband) + { + SOCKET_DEBUG(PRINTF("socwait: outofband interrupt received (%d) -- " + "queueing mv_stent for wait intr\n", outofband); DEBUGSOCKFLUSH); + PUSH_MV_STENT(MVST_ZINTDEV); + mv_chain->mv_st_cont.mvs_zintdev.io_ptr = iod; + mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = FALSE; + sockintr->who_saved = sockwhich_wait; + sockintr->end_time = end_time; + sockintr->end_time_valid = TRUE; + dsocketptr->mupintr = TRUE; + socketus_interruptus++; + SOCKET_DEBUG( + PRINTF("socwait: mv_stent queued - endtime: %d/%d interrupts: %d\n", + end_time.at_sec, end_time.at_usec, socketus_interruptus); + DEBUGSOCKFLUSH); + outofband_action(FALSE); + GTMASSERT; /* Should *never* return from outofband_action */ + return FALSE; /* For the compiler.. */ + } + } + else + SHORT_SLEEP(5); + } + if (0 > rv) + { + errptr = (char *)STRERROR(errno); + errlen = STRLEN(errptr); + rts_error(VARLSTCNT(6) ERR_SOCKWAIT, 0, ERR_TEXT, 2, errlen, errptr); + return FALSE; + } + rv = tcp_routines.aa_accept(socketptr->sd, &peer, &size); + if ((-1 == rv) && (ENOBUFS == errno)) + retry_num++; + else + break; + } + } + if (-1 == rv) +#endif + { + errptr = (char *)STRERROR(errno); + errlen = STRLEN(errptr); + rts_error(VARLSTCNT(6) ERR_SOCKACPT, 0, ERR_TEXT, 2, errlen, errptr); + return FALSE; + } + } + /* got the connection, create a new socket in the device socket list */ + newsocketptr = (socket_struct *)malloc(SIZEOF(socket_struct)); + *newsocketptr = *socketptr; + newsocketptr->sd = rv; + memcpy(&newsocketptr->remote.sin, &peer, SIZEOF(struct sockaddr_in)); + SPRINTF(newsocketptr->remote.saddr_ip, "%s", tcp_routines.aa_inet_ntoa(peer.sin_addr)); + newsocketptr->remote.port = GTM_NTOHS(peer.sin_port); + newsocketptr->state = socket_connected; + newsocketptr->passive = FALSE; + iosocket_delimiter_copy(socketptr, newsocketptr); + newsocketptr->buffer = (char *)malloc(socketptr->buffer_size); + newsocketptr->buffer_size = socketptr->buffer_size; + newsocketptr->buffered_length = socketptr->buffered_offset = 0; + newsocketptr->first_read = newsocketptr->first_write = TRUE; + /* put the new-born socket to the list and create a handle for it */ + iosocket_handle(newsocketptr->handle, &newsocketptr->handle_len, TRUE, dsocketptr); + dsocketptr->socket[dsocketptr->n_socket++] = newsocketptr; + dsocketptr->current_socket = dsocketptr->n_socket - 1; + len = SIZEOF(CONNECTED) - 1; + memcpy(&dsocketptr->dollar_key[0], CONNECTED, len); + dsocketptr->dollar_key[len++] = '|'; + memcpy(&dsocketptr->dollar_key[len], newsocketptr->handle, newsocketptr->handle_len); + len += newsocketptr->handle_len; + dsocketptr->dollar_key[len++] = '|'; + strcpy(&dsocketptr->dollar_key[len], newsocketptr->remote.saddr_ip); + } else + { + assert(socket_connected == socketptr->state); + dsocketptr->current_socket = ii; + len = SIZEOF(READ) - 1; + memcpy(&dsocketptr->dollar_key[0], READ, len); + dsocketptr->dollar_key[len++] = '|'; + memcpy(&dsocketptr->dollar_key[len], socketptr->handle, socketptr->handle_len); + len += socketptr->handle_len; + dsocketptr->dollar_key[len++] = '|'; + strcpy(&dsocketptr->dollar_key[len], socketptr->remote.saddr_ip); + } + return TRUE; +} diff --git a/sr_port/iosocket_write.c b/sr_port/iosocket_write.c new file mode 100644 index 0000000..3642316 --- /dev/null +++ b/sr_port/iosocket_write.c @@ -0,0 +1,257 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iosocket_write.c */ + +#include "mdef.h" + +#include +#include "gtm_socket.h" +#include "gtm_inet.h" +#include "gtm_stdio.h" +#include "gtm_string.h" + +#include "io.h" +#include "iotcpdef.h" +#include "iotcproutine.h" +#include "gt_timer.h" +#include "iosocketdef.h" +#include "dollarx.h" +#include "gtm_conv.h" +#include "gtm_utf8.h" +#include "stringpool.h" +#include "send_msg.h" +#include "error.h" + +GBLREF io_pair io_curr_device; +#ifdef UNIX +GBLREF io_pair io_std_device; +GBLREF bool prin_out_dev_failure; +#endif + +GBLREF tcp_library_struct tcp_routines; +GBLREF mstr chset_names[]; +GBLREF UConverter *chset_desc[]; +GBLREF spdesc stringpool; + +void iosocket_write(mstr *v) +{ + iosocket_write_real(v, TRUE); +} + +void iosocket_write_real(mstr *v, boolean_t convert_output) +{ + io_desc *iod; + mstr tempv; + char *out, *c_ptr, *c_top; + int in_b_len, b_len, status, new_len, c_len, mb_len; + int flags; + d_socket_struct *dsocketptr; + socket_struct *socketptr; + + error_def(ERR_SOCKWRITE); + error_def(ERR_TEXT); + error_def(ERR_CURRSOCKOFR); + error_def(ERR_ZFF2MANY); + error_def(ERR_DELIMSIZNA); + error_def(ERR_ZINTRECURSEIO); + UNIX_ONLY(error_def(ERR_NOPRINCIO);) + + SOCKET_DEBUG2(PRINTF("socwrite: ************************** Top of iosocket_write\n"); DEBUGSOCKFLUSH); + iod = io_curr_device.out; + assert(gtmsocket == iod->type); + dsocketptr = (d_socket_struct *)iod->dev_sp; + socketptr = dsocketptr->socket[dsocketptr->current_socket]; + if (dsocketptr->n_socket <= dsocketptr->current_socket) + { + rts_error(VARLSTCNT(4) ERR_CURRSOCKOFR, 2, dsocketptr->current_socket, dsocketptr->n_socket); + return; + } + if (dsocketptr->mupintr) + rts_error(VARLSTCNT(1) ERR_ZINTRECURSEIO); +#ifdef MSG_NOSIGNAL + flags = MSG_NOSIGNAL; /* return EPIPE instead of SIGPIPE */ +#else + flags = 0; +#endif + socketptr->lastop = TCP_WRITE; + if (socketptr->first_write) + { /* First WRITE, do following + 1. Transition to UTF16BE if ochset is UTF16 and WRITE a BOM + 2. Convert ZFF into ochset format so we don't need to convert every time ZFF is output + 3. Convert DELIMITER 0 to OCHSET format to avoid repeated conversions of DELIM0 on output + */ + if (CHSET_UTF16 == iod->ochset) + { + SOCKET_DEBUG2(PRINTF("socwrite: First write UTF16 -- writing BOM\n"); DEBUGSOCKFLUSH); + iod->ochset = CHSET_UTF16BE; /* per Unicode standard, assume big endian when endian + format is unspecified */ + get_chset_desc(&chset_names[iod->ochset]); + DOTCPSEND(socketptr->sd, UTF16BE_BOM, UTF16BE_BOM_LEN, flags, status); + SOCKET_DEBUG2(PRINTF("socwrite: TCP send of BOM-BE with rc %d\n", status); DEBUGSOCKFLUSH); + if (0 != status) + { + SOCKERROR(iod, dsocketptr, socketptr, ERR_SOCKWRITE, status); + return; + } +#ifdef UNIX + else if (iod == io_std_device.out) + prin_out_dev_failure = FALSE; +#endif + } + if (CHSET_UTF16BE == iod->ochset || CHSET_UTF16LE == iod->ochset) /* need conversion of ZFF and DELIM0 */ + { + if (0 < socketptr->zff.len) + { + new_len = gtm_conv(chset_desc[CHSET_UTF8], chset_desc[iod->ochset], &socketptr->zff, NULL, + NULL); + if (MAX_ZFF_LEN < new_len) + rts_error(VARLSTCNT(4) ERR_ZFF2MANY, 2, new_len, MAX_ZFF_LEN); + if (NULL != socketptr->zff.addr) /* we rely on newsocket.zff.addr being set to NULL + in iosocket_create() */ + socketptr->zff.addr = (char *)malloc(MAX_ZFF_LEN); + socketptr->zff.len = new_len; + UNICODE_ONLY(socketptr->zff.char_len = 0); /* don't care */ + memcpy(socketptr->zff.addr, stringpool.free, new_len); + } + + if (0 < socketptr->n_delimiter) + { + new_len = gtm_conv(chset_desc[CHSET_UTF8], chset_desc[iod->ochset], + &socketptr->delimiter[0], NULL, NULL); + if (MAX_DELIM_LEN < new_len) + { + rts_error(VARLSTCNT(1) ERR_DELIMSIZNA); + return; + } + socketptr->odelimiter0.len = new_len; + UNICODE_ONLY(socketptr->odelimiter0.char_len = socketptr->delimiter[0].char_len); + socketptr->odelimiter0.addr = malloc(new_len); + memcpy(socketptr->odelimiter0.addr, stringpool.free, new_len); + } + } + socketptr->first_write = FALSE; + } + memcpy(dsocketptr->dollar_device, "0", SIZEOF("0")); + if (CHSET_M != iod->ochset) + { /* For ochset == UTF-8, validate the output, + * For ochset == UTF-16[B|L]E, convert the output (and validate during conversion) + */ + if (CHSET_UTF8 == iod->ochset) + { + UTF8_LEN_STRICT(v->addr, v->len); /* triggers badchar error for invalid sequence */ + tempv = *v; + } else + { + assert(CHSET_UTF16BE == iod->ochset || CHSET_UTF16LE == iod->ochset); + /* Certain types of writes (calls from iosocket_wteol or _wtff) already have their output + converted. Converting again just wrecks it so avoid that when necessary. + */ + if (convert_output) + { + new_len = gtm_conv(chset_desc[CHSET_UTF8], chset_desc[iod->ochset], v, NULL, NULL); + tempv.addr = (char *)stringpool.free; + tempv.len = new_len; + /* Since there is no dependence on string pool between now and when we send the data, + we won't bother "protecting" the stringpool value. This space can be used again + by whomever needs it without us forcing a garbage collection due to IO reformat. + */ + /* stringpool.free += new_len; */ + } else + tempv = *v; + } + } else + tempv = *v; + if (0 != (in_b_len = tempv.len)) + { + SOCKET_DEBUG2(PRINTF("socwrite: starting output loop (%d bytes) - iodwidth: %d wrap: %d\n", + in_b_len, iod->width, iod->wrap); DEBUGSOCKFLUSH); + for (out = tempv.addr; ; out += b_len) + { + SOCKET_DEBUG2(PRINTF("socwrite: ---------> Top of write loop $x: %d $y: %d in_b_len: %d\n", + iod->dollar.x, iod->dollar.y, in_b_len); DEBUGSOCKFLUSH); + if (!iod->wrap) + b_len = in_b_len; + else + { + if ((iod->dollar.x >= iod->width) && (START == iod->esc_state)) + { + /* Should this really be iosocket_Wteol() (for FILTER)? IF we call iosocket_wteol(), + * there will be recursion iosocket_Write -> iosocket_Wteol ->iosocket_Write */ + if (0 < socketptr->n_delimiter) + { + DOTCPSEND(socketptr->sd, socketptr->odelimiter0.addr, socketptr->odelimiter0.len, + (socketptr->urgent ? MSG_OOB : 0) | flags, status); + SOCKET_DEBUG2(PRINTF("socwrite: TCP send of %d byte delimiter with rc %d\n", + socketptr->odelimiter0.len, status); DEBUGSOCKFLUSH); + if (0 != status) + { + SOCKERROR(iod, dsocketptr, socketptr, ERR_SOCKWRITE, status); + return; + } +#ifdef UNIX + else if (iod == io_std_device.out) + prin_out_dev_failure = FALSE; +#endif + } + iod->dollar.y++; + iod->dollar.x = 0; + SOCKET_DEBUG2(PRINTF("socwrite: $x > width - wrote delimiter: %d $x: %d $y: %d\n", + (0 < socketptr->n_delimiter), iod->dollar.x, iod->dollar.y); + DEBUGSOCKFLUSH); + } + if ((START != iod->esc_state) || ((int)(iod->dollar.x + in_b_len) <= (int)iod->width)) + { /* enough room even in the worst case, i.e., if width - dollar.x can accommodate in_b_len chars, + * it certainly can accommodate in_b_len bytes */ + b_len = in_b_len; + } else + { + c_len = iod->width - iod->dollar.x; + for (c_ptr = out, c_top = out + in_b_len, b_len = 0; + (c_ptr < c_top) && c_len--; + b_len += mb_len, c_ptr += mb_len) + { + mb_len = (CHSET_M == iod->ochset) ? 0 : + (CHSET_UTF8 == iod->ochset) ? UTF8_MBFOLLOW(c_ptr) : + (CHSET_UTF16BE == iod->ochset) ? UTF16BE_MBFOLLOW(c_ptr, c_top) : + UTF16LE_MBFOLLOW(c_ptr, c_top); + assert(-1 != mb_len); + mb_len++; + } + SOCKET_DEBUG2(PRINTF("socwrite: computing string length in chars: in_b_len: %d" + " mb_len: %d\n", in_b_len, mb_len); DEBUGSOCKFLUSH); + } + } + assert(0 != b_len); + DOTCPSEND(socketptr->sd, out, b_len, (socketptr->urgent ? MSG_OOB : 0) | flags, status); + SOCKET_DEBUG2(PRINTF("socwrite: TCP data send of %d bytes with rc %d\n", b_len, status); DEBUGSOCKFLUSH); + if (0 != status) + { + SOCKERROR(iod, dsocketptr, socketptr, ERR_SOCKWRITE, status); + return; + } +#ifdef UNIX + else if (iod == io_std_device.out) + prin_out_dev_failure = FALSE; +#endif + dollarx(iod, (uchar_ptr_t)out, (uchar_ptr_t)out + b_len); + SOCKET_DEBUG2(PRINTF("socwrite: $x/$y updated by dollarx(): $x: %d $y: %d filter: %d escape: %d\n", + iod->dollar.x, iod->dollar.y, iod->write_filter, iod->esc_state); + DEBUGSOCKFLUSH); + in_b_len -= b_len; + if (0 >= in_b_len) + break; + } + iod->dollar.za = 0; + } + SOCKET_DEBUG2(PRINTF("socwrite: <--------- Leaving iosocket_write\n"); DEBUGSOCKFLUSH); + return; +} diff --git a/sr_port/iosocket_wteol.c b/sr_port/iosocket_wteol.c new file mode 100644 index 0000000..01f4c30 --- /dev/null +++ b/sr_port/iosocket_wteol.c @@ -0,0 +1,64 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iosocket_wteol.c */ +/* write the 0th delimiter and flush */ + +#include "mdef.h" + +#include "gtm_socket.h" +#include "gtm_inet.h" +#include + +#include "gt_timer.h" +#include "io.h" +#include "iotcpdef.h" +#include "iotcproutine.h" +#include "iottdef.h" +#include "iosocketdef.h" + +GBLREF tcp_library_struct tcp_routines; + +void iosocket_wteol(int4 val, io_desc *io_ptr) +{ + d_socket_struct *dsocketptr; + socket_struct *socketptr; + char *ch, *top; + int eol_cnt; + + assert(gtmsocket == io_ptr->type); + dsocketptr = (d_socket_struct *)io_ptr->dev_sp; + socketptr = dsocketptr->socket[dsocketptr->current_socket]; + assert(val); + io_ptr->esc_state = START; + if (socketptr->n_delimiter > 0) + { + for (eol_cnt = val; eol_cnt--; ) + { + io_ptr->dollar.x = 0; /* so that iosocket_write doesn't try to wrap (based on escape state and width) */ + iosocket_write_real(&socketptr->odelimiter0, FALSE); + } + } + /* $X is maintained in VMS without the below assignment (resetting to 0) because the NATIVE_TTEOL is \015\012 + * and the (\015) triggers appropriate maintenance of $X. In UNIX, NATIVE_TTEOL is \012, so + * FILTER=CHARACTER effectively turns off all $X maintenance (except for WRAP logic). + * In VMS the below assignment is not necessary, but harmless; it is always logically correct. + */ + io_ptr->dollar.x = 0; + if (!(io_ptr->write_filter & CHAR_FILTER) || !socketptr->delim0containsLF) + { /* If FILTER won't do it, also maintain $Y */ + io_ptr->dollar.y += val; + if (io_ptr->length) + io_ptr->dollar.y %= io_ptr->length; + } + iosocket_flush(io_ptr); + return; +} diff --git a/sr_port/iosocket_wtff.c b/sr_port/iosocket_wtff.c new file mode 100644 index 0000000..07794b8 --- /dev/null +++ b/sr_port/iosocket_wtff.c @@ -0,0 +1,41 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iosocket_wtff.c */ + +#include "mdef.h" +#include "gtm_socket.h" +#include "gtm_inet.h" +#include "io.h" +#include "gt_timer.h" +#include "iotcpdef.h" +#include "iosocketdef.h" + +GBLREF io_pair io_curr_device; + +void iosocket_wtff(void) +{ + io_desc *iod; + socket_struct *socketptr; + d_socket_struct *dsocketptr; + + iod = io_curr_device.out; + assert(gtmsocket == iod->type); + iod->esc_state = START; + dsocketptr = (d_socket_struct *)iod->dev_sp; + socketptr = dsocketptr->socket[dsocketptr->current_socket]; + if (socketptr->zff.len) + iosocket_write_real(&socketptr->zff, FALSE); + iosocket_flush(iod); + iod->dollar.x = 0; + iod->dollar.y = 0; + return; +} diff --git a/sr_port/iosocket_wtone.c b/sr_port/iosocket_wtone.c new file mode 100644 index 0000000..08dcc75 --- /dev/null +++ b/sr_port/iosocket_wtone.c @@ -0,0 +1,60 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iosocket_wtone.c */ + +#include "mdef.h" +#include "gtm_socket.h" +#include "gtm_inet.h" +#include "io.h" +#include "gt_timer.h" +#include "iotcpdef.h" +#include "iosocketdef.h" +#include "gtm_utf8.h" + +GBLREF io_pair io_curr_device; + +void iosocket_wtone(int ch) +{ + mstr temp; + char c, uni_c[4], *endptr; + io_desc *iod; + + if (CHSET_M == io_curr_device.out->ochset) + { + c = (char)ch; + temp.len = 1; + temp.addr = (char *)&c; + } else + { + switch(io_curr_device.out->ochset) + { + case CHSET_UTF8: + endptr = (char *)UTF8_WCTOMB(ch, uni_c); + break; + case CHSET_UTF16: /* unspecified endian format implies Big Endian */ + case CHSET_UTF16BE: + endptr = UTF16BE_WCTOMB(ch, uni_c); + break; + case CHSET_UTF16LE: + endptr = UTF16LE_WCTOMB(ch, uni_c); + break; + default: + GTMASSERT; + } + temp.addr = uni_c; + temp.len = INTCAST(endptr - uni_c); + assert(0 < temp.len); /* we validated the code point already in op_wtone() */ + } + UNICODE_ONLY(temp.char_len = 1); + iosocket_write_real(&temp, TRUE); + return; +} diff --git a/sr_port/iosocketdef.h b/sr_port/iosocketdef.h new file mode 100644 index 0000000..0a2fba0 --- /dev/null +++ b/sr_port/iosocketdef.h @@ -0,0 +1,202 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef IOSOCKETDEF_H +#define IOSOCKETDEF_H + +/* iosocketdef.h */ + +/* one socket device may have more than one socket associate with it. + * one socket may have more than one delimiter associate with it. + */ + +#include +#include "gtm_inet.h" + +#ifndef GTM_MB_LEN_MAX +#include "gtm_utf8.h" +#endif + +/* Debugging notes: Some debuging calls as as below. Others are for SOCKET_DEBUG2 which is *always* + disabled. As parts of the code work, the SOCKET_DEBUG calls are changed to SOCKET_DEBUG2 to get + them out of the way without removing them (they may be useful in the future). + + The macro DEBUGSOCKFLUSH can be set or not depending on if flushing after every write is desired +*/ +/*#define DEBUG_SOCKET*/ +#ifdef DEBUG_SOCKET +# define SOCKET_DEBUG(X) X +#else +# define SOCKET_DEBUG(X) +#endif +/*#define DEBUGSOCKFLUSH fflush(stdout)*/ +#define DEBUGSOCKFLUSH +#define SOCKET_DEBUG2(X) + +/* About the length of the delimiter string. While we are allocating lots of space here for the maximum representation + of 64 delimiters each of 64 chars MB chars, the fact is that the iop option processing actually limits the string + containing all the delimiters to 255 bytes by its nature of having single byte length field imbedded in the buffer + stream. When the iop processing is modified to handle a larger string, these options will be useful. But for right + now, they are way overkill.. +*/ +#define MAX_N_SOCKET 64 /* Initial default for gtm_max_sockets in gbldefs.c */ +#define MAX_MAX_N_SOCKET (1024 * 1024) /* Values higher than this absurd value are most likely wrong */ +#define MAX_N_DELIMITER 64 +#define MAX_DELIM_LEN (MAX_N_DELIMITER * GTM_MB_LEN_MAX) /* worst case byte length for 64 UTF-8 characters */ + +#define MAX_HANDLE_LEN (64 * GTM_MB_LEN_MAX) /* worst case byte length for 64 UTF-8 characters */ +#define MAX_ZFF_LEN (64 * GTM_MB_LEN_MAX) /* worst case byte length for 64 UTF-8 characters */ +#define DEFAULT_LISTEN_DEPTH 1 +#define DEFAULT_SOCKET_BUFFER_SIZE 0x400 +#define MAX_SOCKET_BUFFER_SIZE 0x100000 +#define MAX_INTERNAL_SOCBUF_SIZE 0x100000 +/* Next three fields relate to the time that a variable length unterminated read will wait to see + if there is more input coming in before it gives up and returns what it has to the user. This + time is specified in milliseconds. This value used to be 200ms but that was deemed too long on + modern systems yet now the user can change it if they wish to. Tradeoffs are longer waits for + variable reads versus potential CPU burner if the value gets too low. The implementation now + waits INITIAL_MOREREAD_TIMEOUT time for the first read to occur and then switches to the + DEFAULT_MOREREAD_TIMEOUT. This keeps CPU usage low during the potentially long period prior to + reading some data, while being more responsive for subsequent reads. + */ +#define INITIAL_MOREREAD_TIMEOUT 200 +#define DEFAULT_MOREREAD_TIMEOUT 10 +#define MAX_MOREREAD_TIMEOUT 999 + +#define ONE_COMMA "1," + +#define SOCKERROR(iod, dsocketptr, socketptr, gtmerror, syserror) \ +{ \ + int errlen; \ + char *errptr; \ + iod->dollar.za = 9; \ + memcpy(dsocketptr->dollar_device, ONE_COMMA, SIZEOF(ONE_COMMA)); \ + errptr = (char *)STRERROR(syserror); \ + errlen = STRLEN(errptr); \ + memcpy(&dsocketptr->dollar_device[SIZEOF(ONE_COMMA) - 1], errptr, errlen + 1); /* + 1 for null */ \ + assert(ERR_SOCKWRITE == gtmerror); \ + UNIX_ONLY(if (iod == io_std_device.out) \ + { \ + if (!prin_out_dev_failure) \ + prin_out_dev_failure = TRUE; \ + else \ + { \ + send_msg(VARLSTCNT(1) ERR_NOPRINCIO); \ + stop_image_no_core(); \ + } \ + }) \ + if (socketptr->ioerror) \ + rts_error(VARLSTCNT(6) gtmerror, 0, ERR_TEXT, 2, errlen, errptr); \ +} + +enum socket_state +{ + socket_connected, + socket_listening, + socket_bound, + socket_created, + socket_connect_inprogress +}; + +enum socket_protocol +{ + socket_tcpip, + socket_spx, + n_socket_protocol +}; + +enum socket_which /* which module saved the interrupted info */ +{ + sockwhich_invalid, + sockwhich_readfl, + sockwhich_wait, + sockwhich_connect +}; + +typedef struct socket_address_type +{ + struct sockaddr_in sin; /* accurate one */ + unsigned short port; + char saddr_ip[SA_MAXLEN]; + char saddr_lit[SA_MAXLITLEN]; +} socket_address; + +typedef struct socket_struct_type +{ + int sd; /* socket descriptor */ + struct d_socket_struct_type *dev; /* point back to the driver */ + boolean_t passive, + ioerror, + urgent, + delim0containsLF; + enum socket_state state; + enum socket_protocol protocol; + socket_address local, + remote; + uint4 lastop; + uint4 moreread_timeout; /* timeout to see if more data available (ms) */ + char handle[MAX_HANDLE_LEN]; + int handle_len; + int bufsiz; /* OS internal buffer size */ + int n_delimiter; + mstr delimiter[MAX_N_DELIMITER]; + mstr idelimiter[MAX_N_DELIMITER]; + mstr odelimiter0; + size_t buffer_size; /* size of the buffer for this socket */ + size_t buffered_length; /* length of stuff buffered for this socket */ + size_t buffered_offset; /* offset of the buffered stuff to buffer head */ + char *buffer; /* pointer to the the buffer of this socket */ + boolean_t nodelay; + boolean_t first_read; + boolean_t first_write; + boolean_t def_moreread_timeout; /* true if deviceparameter morereadtime defined in open or use */ + mstr zff; +} socket_struct; + +typedef struct socket_interrupt_type +{ + ABS_TIME end_time; + enum socket_which who_saved; + int max_bufflen; + int bytes_read; + int chars_read; + boolean_t end_time_valid; + boolean_t ibfsize_specified; + struct d_socket_struct_type *newdsocket; +} socket_interrupt; + +typedef struct d_socket_struct_type +{ + socket_interrupt sock_save_state; /* Saved state of interrupted IO */ + boolean_t mupintr; /* We were mupip interrupted */ + int4 current_socket; /* current socket index */ + int4 n_socket; /* number of sockets */ + char dollar_device[DD_BUFLEN]; + char dollar_key[DD_BUFLEN]; + struct io_desc_struct *iod; /* Point back to main IO descriptor block */ + struct socket_struct_type *socket[1]; /* Array size determined by gtm_max_sockets */ +}d_socket_struct; + +boolean_t iosocket_bind(socket_struct *socketptr, int4 timepar, boolean_t update_bufsiz); +boolean_t iosocket_connect(socket_struct *socketptr, int4 timepar, boolean_t update_bufsiz); +boolean_t iosocket_delimiter(unsigned char *delimiter_buffer, int4 delimiter_len, socket_struct *socketptr, boolean_t rm); +void iosocket_delim_conv(socket_struct *socketptr, gtm_chset_t to_chset); +void iosocket_delimiter_copy(socket_struct *from, socket_struct *to); +boolean_t iosocket_switch(char *handle, int handle_len, d_socket_struct *from, d_socket_struct *to); +int4 iosocket_handle(char *handle, int *len, boolean_t newhandle, d_socket_struct *dsocketptr); +socket_struct *iosocket_create(char *sockaddr, uint4 bfsize, int file_des); +ssize_t iosocket_snr(socket_struct *socketptr, void *buffer, size_t maxlength, int flags, ABS_TIME *time_for_read); +void iosocket_unsnr(socket_struct *socketptr, unsigned char *buffer, size_t len); +ssize_t iosocket_snr_utf_prebuffer(io_desc *iod, socket_struct *socketptr, int flags, ABS_TIME *time_for_read, + boolean_t wait_for_input); +void iosocket_write_real(mstr *v, boolean_t convert_output); +void iosocket_readfl_badchar(mval *vmvalptr, int datalen, int delimlen, unsigned char *delimptr, unsigned char *strend); +#endif diff --git a/sr_port/iotcp_close.c b/sr_port/iotcp_close.c new file mode 100644 index 0000000..aafb0ef --- /dev/null +++ b/sr_port/iotcp_close.c @@ -0,0 +1,86 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iotcp_close.c - close a TCP/IP connection + * Parameters- + * iod - I/O descriptor for the currently open TCP/IP connection. + * + * pp->str.addr - device parameters + * + */ +#include "mdef.h" + +#include "gtm_stdio.h" +#include + +#include "copy.h" +#include "io_params.h" +#include "io.h" +#include "iotcpdef.h" +#include "iotcproutine.h" +#include "stringpool.h" + +GBLREF tcp_library_struct tcp_routines; +LITREF unsigned char io_params_size[]; + +void iotcp_close(io_desc *iod, mval *pp) +{ + bool close_listen_socket = FALSE; + unsigned char c; + d_tcp_struct *tcpptr; + int p_offset; + +#ifdef DEBUG_TCP + PRINTF("%s >>>\n", __FILE__); +#endif + assert(iod->type == tcp); + tcpptr = (d_tcp_struct *)iod->dev_sp; + p_offset = 0; + while (*(pp->str.addr + p_offset) != iop_eol) + { + switch (c = *(pp->str.addr + p_offset++)) + { + case iop_listen: + /* close the listening socket associated with this connection, + * rather than the actual connection socket. + */ + close_listen_socket = TRUE; + break; + case iop_exception: + iod->error_handler.len = *(pp->str.addr + p_offset); + iod->error_handler.addr = (char *)(pp->str.addr + p_offset + 1); + s2pool(&iod->error_handler); + break; + default: + break; + } + p_offset += ((IOP_VAR_SIZE == io_params_size[c]) ? + (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[c]); + } + + if (close_listen_socket) + { + iotcp_rmlsock(iod); +#ifdef DEBUG_TCP + PRINTF("%s (listening socket for %d) <<<\n", __FILE__, tcpptr->socket); +#endif + } + else + { + if (iod->state != dev_open) + return; + tcp_routines.aa_close(tcpptr->socket); + iod->state = dev_closed; +#ifdef DEBUG_TCP + PRINTF("%s (%d) <<<\n", __FILE__, tcpptr->socket); +#endif + } +} diff --git a/sr_port/iotcp_dummy.c b/sr_port/iotcp_dummy.c new file mode 100644 index 0000000..6e8ab86 --- /dev/null +++ b/sr_port/iotcp_dummy.c @@ -0,0 +1,18 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" + +short iotcp_dummy(io_log_name *dev_name, mval *pp, int fd, mval *mspace, int4 timeout) +{ + return 0; +} diff --git a/sr_port/iotcp_fillroutine.c b/sr_port/iotcp_fillroutine.c new file mode 100644 index 0000000..6d71024 --- /dev/null +++ b/sr_port/iotcp_fillroutine.c @@ -0,0 +1,138 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* get socket routines address */ +#include "mdef.h" + +#include "gtm_netdb.h" +#include "gtm_unistd.h" +#include +#include "gtm_socket.h" +#include "gtm_inet.h" +#include "gtm_string.h" /* for FD_ZERO */ + +#include "io.h" +#include "iotcp_select.h" +#include "iotcproutine.h" + +GBLDEF tcp_library_struct tcp_routines; + +int gtm_accept(int socket, struct sockaddr *address, sssize_t *address_len); +int gtm_recv(int socket, void *buffer, size_t length, int flags); +int gtm_send(int socket, void *buffer, size_t length, int flags); + +/* + * Note - the checks for EINTR in these functions are valid and need to stay in, + * because the functions are being assigned to members of the tcp_routines table, + * and thus cannot be replaced by EINTR wrapper macros. + */ + +int gtm_accept(int socket, struct sockaddr *address, sssize_t *address_len) +{ + int res; + + do + { + res = accept(socket, address, (GTM_SOCKLEN_TYPE *)address_len); + } while (-1 == res && EINTR == errno); + + return(res); +} + +int gtm_connect(int socket, struct sockaddr *address, size_t address_len) +{ + int res, sockerror; + GTM_SOCKLEN_TYPE sockerrorlen; + fd_set writefds; + + res = connect(socket, address, (GTM_SOCKLEN_TYPE)address_len); + if ((-1 == res) && ((EINTR == errno) || (EINPROGRESS == errno) +#if (defined(__osf__) && defined(__alpha)) || defined(__sun) || defined(__vms) + || (EWOULDBLOCK == errno) +#endif + )) + {/* connection attempt will continue so wait for completion */ + do + { /* a plain connect will usually timeout after 75 seconds with ETIMEDOUT */ + FD_ZERO(&writefds); + FD_SET(socket, &writefds); + res = select(socket + 1, NULL, &writefds, NULL, NULL); + if (-1 == res && EINTR == errno) + continue; + if (0 < res) + { /* check for socket error */ + sockerrorlen = SIZEOF(sockerror); + res = getsockopt(socket, SOL_SOCKET, SO_ERROR, &sockerror, &sockerrorlen); + if (0 == res && 0 != sockerror) + { /* return socket error */ + res = -1; + errno = sockerror; + } + } + break; + } while (TRUE); + } else if (-1 == res && EISCONN == errno) + res = 0; /* socket is already connected */ + + return(res); +} + +int gtm_recv(int socket, void *buffer, size_t length, int flags) +{ + int res; + + do + { + res = (int)(recv(socket, buffer, (int)(length), flags)); + } while (-1 == res && EINTR == errno); + + return(res); +} + +int gtm_send(int socket, void *buffer, size_t length, int flags) +{ + int res; + + do + { + res = (int)(send(socket, buffer, (int)(length), flags)); + } while (-1 == res && EINTR == errno); + + return(res); +} + +int iotcp_fillroutine(void) +{ + if (gtm_accept == tcp_routines.aa_accept) + return 0; /* already done */ + tcp_routines.aa_accept = gtm_accept; + tcp_routines.aa_bind = bind; + tcp_routines.aa_close = close; + tcp_routines.aa_connect = gtm_connect; + tcp_routines.aa_getsockname = getsockname; + tcp_routines.aa_getsockopt = getsockopt; +#ifndef htons /* if htons is not a macro, assign the routine */ + tcp_routines.aa_htons = htons; +#endif + tcp_routines.aa_inet_addr = INET_ADDR; + tcp_routines.aa_inet_ntoa = INET_NTOA; +#ifndef ntohs + tcp_routines.aa_ntohs = ntohs; +#endif + tcp_routines.aa_listen = listen; + tcp_routines.aa_recv = (int (*)())gtm_recv; + tcp_routines.aa_select = select; + tcp_routines.aa_send = (int (*)())gtm_send; + tcp_routines.aa_setsockopt = setsockopt; + tcp_routines.aa_socket = socket; + + return 0; +} diff --git a/sr_port/iotcp_flush.c b/sr_port/iotcp_flush.c new file mode 100644 index 0000000..b8ddc9f --- /dev/null +++ b/sr_port/iotcp_flush.c @@ -0,0 +1,40 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" +#include + +#include "io.h" +#include "iotcpdef.h" + +void iotcp_flush(io_desc *iod) +{ +#ifdef C9A06001531 + /* pending change request C9A06001531 */ + + d_tcp_struct *tcpptr; + +#ifdef DEBUG_TCP + PRINTF("%s >>>\n", __FILE__); +#endif + tcpptr = (d_tcp_struct *)iod->dev_sp; + if ((TCP_WRITE == iod->dollar.x && tcpptr->lastop) && !iod->dollar.za) + iotcp_wteol(1, iod); + +#ifdef DEBUG_TCP + PRINTF("%s <<<\n", __FILE__); +#endif + +#endif + return; +} diff --git a/sr_port/iotcp_iocontrol.c b/sr_port/iotcp_iocontrol.c new file mode 100644 index 0000000..ed725c4 --- /dev/null +++ b/sr_port/iotcp_iocontrol.c @@ -0,0 +1,62 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include +#include + +#include "gtm_string.h" +#include "io.h" +#include "iotcpdef.h" + +GBLREF io_pair io_curr_device; + +void iotcp_iocontrol(mstr *d) +{ + return; +} + + +void iotcp_dlr_device(mstr *d) +{ + io_desc *iod; + int len; + d_tcp_struct *tcpptr; + + iod = io_curr_device.out; + tcpptr = (d_tcp_struct *)iod->dev_sp; + + len = STRLEN(tcpptr->dollar_device); + /* verify internal buffer has enough space for $DEVICE string value */ + assert((int)d->len > len); + memcpy(d->addr, tcpptr->dollar_device, len); + d->len = len; + return; +} + + +void iotcp_dlr_key(mstr *d) +{ + io_desc *iod; + int len; + d_tcp_struct *tcpptr; + + iod = io_curr_device.out; + tcpptr = (d_tcp_struct *)iod->dev_sp; + + len = STRLEN(tcpptr->saddr); + /* verify internal buffer has enough space for $KEY string value */ + assert((int)d->len > len); + memcpy(d->addr, tcpptr->saddr, len); + d->len = len; + return; +} diff --git a/sr_port/iotcp_list.c b/sr_port/iotcp_list.c new file mode 100644 index 0000000..f7b37f4 --- /dev/null +++ b/sr_port/iotcp_list.c @@ -0,0 +1,188 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iotcp_list.c - routines to maintain a list of TCP/IP listening sockets */ + +#include "mdef.h" + +#include "gtm_stdio.h" +#include "gtm_string.h" +#include "gtm_socket.h" +#include "gtm_inet.h" + +#include + +#include "io_params.h" +#include "io.h" +#include "iotcproutine.h" +#include "iotcpdef.h" + +/* list of listening sockets */ +typedef struct lsock_rec_s +{ + int socket; + struct sockaddr_in sin; + struct lsock_rec_s *next; + io_log_name *ldev; /* listening device record */ +} lsock_rec; + +static lsock_rec *lsock_list = NULL; + +GBLREF tcp_library_struct tcp_routines; + +int iotcp_newlsock(io_log_name *dev, d_tcp_struct *tcpptr); + +/* find the listening socket associated with the address of this + * new socket, creating a listening socket if there is none. + */ +int iotcp_getlsock(io_log_name *dev) +{ + d_tcp_struct *tcpptr; + lsock_rec *ls; + +#ifdef DEBUG_TCP + PRINTF("iotcp_getlsock ---\n"); +#endif + + tcpptr = (d_tcp_struct *)dev->iod->dev_sp; + + for (ls = lsock_list; ls != NULL; ls = ls->next) + if (tcpptr->sin.sin_addr.s_addr == ls->sin.sin_addr.s_addr && tcpptr->sin.sin_port == ls->sin.sin_port) + return ls->socket; + + return iotcp_newlsock(dev, tcpptr); +} + + +int iotcp_newlsock(io_log_name *dev, d_tcp_struct *tcpptr) +{ + lsock_rec *new_lsock; + + io_log_name *ldev; /* listening device */ + d_tcp_struct *lsock_tcp; + int lsock; + char buf[MAX_TRANS_NAME_LEN]; + static char ones[] = {1, 1, 1}; + mstr ldev_name; + int on = 1; + char *errptr; + int4 errlen; + + error_def(ERR_SOCKINIT); + error_def(ERR_TEXT); + + +#ifdef DEBUG_TCP + PRINTF("iotcp_newlsock ---\n"); +#endif + + lsock = tcp_routines.aa_socket(AF_INET, SOCK_STREAM, 0); + if (lsock == -1) + { + errptr = (char *)STRERROR(errno); + errlen = STRLEN(errptr); + rts_error(VARLSTCNT(5) ERR_SOCKINIT, 3, errno, errlen, errptr); + return 0; + } + + /* allow multiple connections to the same IP address */ + if (tcp_routines.aa_setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, &on, SIZEOF(on)) == -1) + { + errptr = (char *)STRERROR(errno); + errlen = STRLEN(errptr); + (void)tcp_routines.aa_close(lsock); + rts_error(VARLSTCNT(5) ERR_SOCKINIT, 3, errno, errlen, errptr); + return 0; + } + + if (tcp_routines.aa_bind(lsock, (struct sockaddr *)&tcpptr->sin, SIZEOF(struct sockaddr)) == -1) + { + errptr = (char *)STRERROR(errno); + errlen = STRLEN(errptr); + (void)tcp_routines.aa_close(lsock); + rts_error(VARLSTCNT(5) ERR_SOCKINIT, 3, errno, errlen, errptr); + return 0; + } + + /* establish a queue of length 5 for incoming connections */ + if (tcp_routines.aa_listen(lsock, 5) == -1) + { + errptr = (char *)STRERROR(errno); + errlen = STRLEN(errptr); + (void)tcp_routines.aa_close(lsock); + rts_error(VARLSTCNT(5) ERR_SOCKINIT, 3, errno, errlen, errptr); + return 0; + } + + /* create an extra device for the listening socket (device + * name is the user-specified device name with three ^A's + * appended). We need this device to be on the io_log_name + * list so that it gets closed automatically when the user's + * program exits. + */ + ldev_name.addr=buf; + memcpy(ldev_name.addr, dev->dollar_io, dev->len); + memcpy(ldev_name.addr+dev->len, ones, 3); + ldev_name.len = dev->len + 2; + ldev = get_log_name(&ldev_name, INSERT); + + /* copy all state information from the current device */ + + /* io descriptor */ + ldev->iod = (io_desc *)malloc(SIZEOF(io_desc)); + memcpy(ldev->iod, dev->iod, SIZEOF(io_desc)); + ldev->iod->state = dev_open; + ldev->iod->pair.in = ldev->iod; + ldev->iod->pair.out = ldev->iod; + + /* tcp-specific information */ + ldev->iod->dev_sp = (void *)malloc(SIZEOF(d_tcp_struct)); + lsock_tcp=(d_tcp_struct *)ldev->iod->dev_sp; + memcpy(lsock_tcp, tcpptr, SIZEOF(d_tcp_struct)); + + lsock_tcp->socket = lsock; + ldev->iod->state = dev_open; + + /* add to our list of tcp listening sockets */ + new_lsock = (lsock_rec *)malloc(SIZEOF(lsock_rec)); + new_lsock->socket = lsock; + new_lsock->sin = tcpptr->sin; + new_lsock->next = lsock_list; + new_lsock->ldev = ldev; + lsock_list = new_lsock; + + return lsock; +} + + +void iotcp_rmlsock(io_desc *iod) +{ + lsock_rec *ls, *prev, *next; + d_tcp_struct *tcpptr = (d_tcp_struct *)iod->dev_sp; + + for (prev = NULL, ls = lsock_list; ls != NULL;) + { + next = ls->next; + if (tcpptr->sin.sin_port == ls->sin.sin_port) + { + if (prev) + prev->next = ls->next; + else + lsock_list = ls->next; + tcp_routines.aa_close(ls->socket); + ls->ldev->iod->state = dev_closed; + free(ls); + } + else + prev = ls; + ls = next; + } +} diff --git a/sr_port/iotcp_name2ip.c b/sr_port/iotcp_name2ip.c new file mode 100644 index 0000000..2bb36a7 --- /dev/null +++ b/sr_port/iotcp_name2ip.c @@ -0,0 +1,39 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iotcp_name2ip.c - convert a host name to its ip address + * + * Parameters- + * name - the pointer to the string of hostname. + * + * Returns- + * ip - the pointer to the string of ip address. + * NULL - convert operation failed, host not reachable. + */ + +#include "mdef.h" + +#include "gtm_socket.h" +#include "gtm_netdb.h" +#include "gtm_inet.h" +#include "gtm_stdio.h" +#include "io.h" + +char *iotcp_name2ip(char *name) +{ /* only ASCII range characters allowed in hostnames, we are not using libidn to resolve hostnames that might have international + characters in their name */ + + struct hostent *host_ptr; + + if (NULL == (host_ptr = GETHOSTBYNAME(name))) + return NULL; + return INET_NTOA(*((struct in_addr *)host_ptr->h_addr_list[0])); +} diff --git a/sr_port/iotcp_open.c b/sr_port/iotcp_open.c new file mode 100644 index 0000000..31bc10a --- /dev/null +++ b/sr_port/iotcp_open.c @@ -0,0 +1,456 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* iotcp_open.c - open a TCP/IP connection + * Parameters- + * dev - the logical name associated with this socket (ignored by this routine). + * pp->str.addr - device parameters. The "stream" parameter is required. + * file_des - unused. (UNIX only) + * mspace - unused. + * t - maximum time to wait for a connection (in ms). + * + * Returns- + * non-zero - socket successfully opened and ready for I/O + * zero - open operation failed or timed out. + */ +#include "mdef.h" + +#include +#include "gtm_fcntl.h" +#include "gtm_time.h" +#include "gtm_socket.h" +#include "gtm_inet.h" +#include "gtm_ctype.h" +#include "gtm_string.h" +#include "gtm_stdio.h" +#include "gtm_netdb.h" + +#include "copy.h" +#include "gt_timer.h" +#include "io.h" +#include "iotimer.h" +#include "iotcp_select.h" +#include "iotcpdef.h" +#include "iotcpdefsp.h" +#include "iotcproutine.h" +#include "io_params.h" +#include "stringpool.h" +#include "outofband.h" +#include "wake_alarm.h" + +#ifdef __osf__ +/* Tru64 does not have the prototype for "hstrerror" even though the function is available in the library. + * Until we revamp the TCP communications setup stuff to use the new(er) POSIX definitions, we cannot move + * away from "hstrerror". Declare prototype for this function in Tru64 manually until then. + */ +const char *hstrerror(int err); +#endif + +GBLREF tcp_library_struct tcp_routines; +GBLREF bool out_of_time; +GBLREF volatile int4 outofband; +LITREF unsigned char io_params_size[]; + +short iotcp_open(io_log_name *dev, mval *pp, int file_des, mval *mspace, int4 timeout) +{ + boolean_t no_time_left = FALSE, timed; + char addr[SA_MAXLEN+1], *errptr, sockaddr[SA_MAXLEN+1], + temp_addr[SA_MAXLEN+1], temp_ch, *adptr; + unsigned char ch, len; + int4 length, width; + unsigned short port; + int4 errlen, msec_timeout; + GTM_SOCKLEN_TYPE size; + int ii, status, + on = 1, + p_offset = 0, + temp_1 = -2; + TID timer_id; + ABS_TIME cur_time, end_time, time_for_read, lcl_time_for_read; + d_tcp_struct *tcpptr, newtcp; + io_desc *ioptr; + struct sockaddr_in peer; /* socket address + port */ + fd_set tcp_fd; + int lsock; + short retry_num; + const char *terrptr; + + error_def(ERR_DEVPARMNEG); + error_def(ERR_INVADDRSPEC); + error_def(ERR_INVPORTSPEC); + error_def(ERR_IPADDRREQ); + error_def(ERR_OPENCONN); + error_def(ERR_SOCKACPT); + error_def(ERR_SOCKINIT); + error_def(ERR_SOCKPARMREQ); + error_def(ERR_SOCKWAIT); + error_def(ERR_TEXT); + +#ifdef DEBUG_TCP + PRINTF("iotcp_open.c >>> tt = %d\n", t); +#endif + ioptr = dev->iod; + assert((params) *(pp->str.addr + p_offset) < (unsigned char)n_iops); + assert(0 != ioptr); + assert(ioptr->state >= 0 && ioptr->state < n_io_dev_states); + assert(tcp == ioptr->type); + if (dev_never_opened == ioptr->state) + { + ioptr->dev_sp = (void *)malloc(SIZEOF(d_tcp_struct)); + memset(ioptr->dev_sp, 0, SIZEOF(d_tcp_struct)); + } + tcpptr = (d_tcp_struct *)ioptr->dev_sp; + if (dev_never_opened == ioptr->state) + { + ioptr->state = dev_closed; + ioptr->width = TCPDEF_WIDTH; + ioptr->length = TCPDEF_LENGTH; + ioptr->wrap = TRUE; + if (-1 == iotcp_fillroutine()) + assert(FALSE); + } + ioptr->dollar.zeof = FALSE; + newtcp = *tcpptr; + memcpy(newtcp.dollar_device, LITZERO, SIZEOF(LITZERO)); + newtcp.passive = FALSE; + while (iop_eol != *(pp->str.addr + p_offset)) + { + switch (ch = *(pp->str.addr + p_offset++)) + { + case iop_width: + GET_LONG(width, pp->str.addr + p_offset); + if (0 == width) + newtcp.width = TCPDEF_WIDTH; + else if (width > 0) + newtcp.width = width; + else + rts_error(VARLSTCNT(1) ERR_DEVPARMNEG); + break; + case iop_length: + GET_LONG(length, pp->str.addr + p_offset); + if (0 == length) + newtcp.length = TCPDEF_LENGTH; + else if (length > 0) + newtcp.length = length; + else + rts_error(VARLSTCNT(1) ERR_DEVPARMNEG); + break; + case iop_listen: + newtcp.passive = TRUE; + break; + case iop_socket: + len = *(pp->str.addr + p_offset); + memset(sockaddr, 0, SA_MAXLEN+1); + memcpy(sockaddr, pp->str.addr + p_offset + 1, (len <= SA_MAXLEN) ? len : SA_MAXLEN); + *temp_addr = '\0'; + *addr = '\0'; + port = 0; + if (SSCANF(sockaddr, "%[^,], %hu", temp_addr, &port) < 2) + { + newtcp.sin.sin_addr.s_addr = INADDR_ANY; + if (SSCANF(sockaddr, ",%hu", &port) < 1) + { + rts_error(VARLSTCNT(1) ERR_INVPORTSPEC); + return FALSE; + } + } else + { + ii = 0; + temp_ch = temp_addr[0]; + while(ISDIGIT_ASCII(temp_ch) || ('.' == temp_ch)) + { + ii++; + temp_ch = temp_addr[ii]; + } + + if ('\0' != temp_ch) + { + adptr = iotcp_name2ip(temp_addr); + if (NULL == adptr) + { +#if !defined(__hpux) && !defined(__MVS__) + terrptr = HSTRERROR(h_errno); + rts_error(VARLSTCNT(6) ERR_INVADDRSPEC, 0, ERR_TEXT, 2, LEN_AND_STR(terrptr)); +#else + /* Grumble grumble HPUX and z/OS don't have hstrerror() */ + rts_error(VARLSTCNT(1) ERR_INVADDRSPEC); +#endif + return FALSE; + } + SPRINTF(addr, "%s", adptr); + } else + SPRINTF(addr, "%s", temp_addr); + + if ((in_addr_t)-1 == (newtcp.sin.sin_addr.s_addr = tcp_routines.aa_inet_addr(addr))) + { + rts_error(VARLSTCNT(1) ERR_INVADDRSPEC); + return FALSE; + } + } + newtcp.sin.sin_port = GTM_HTONS(port); + newtcp.sin.sin_family = AF_INET; + break; + case iop_exception: + ioptr->error_handler.len = *(pp->str.addr + p_offset); + ioptr->error_handler.addr = (char *)(pp->str.addr + p_offset + 1); + s2pool(&ioptr->error_handler); + break; + default: + break; + } + p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ? + (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]); + } + if ((0 == newtcp.sin.sin_port) && (0 == newtcp.sin.sin_addr.s_addr)) + { + rts_error(VARLSTCNT(1) ERR_SOCKPARMREQ); + return FALSE; + } + /* active connection must have a complete address specification */ + if ((INADDR_ANY == newtcp.sin.sin_addr.s_addr) && !newtcp.passive) + { + rts_error(VARLSTCNT(1) ERR_IPADDRREQ); + return FALSE; + } + if (dev_closed == ioptr->state) + { + if (newtcp.passive) /* passive connection */ + { + /* no listening socket for this addr? make one. */ + memcpy(ioptr->dev_sp, &newtcp, SIZEOF(d_tcp_struct)); + if (!(lsock = iotcp_getlsock(dev))) + return FALSE; /* could not create listening socket */ + timer_id = (TID)iotcp_open; + out_of_time = FALSE; + time_for_read.at_sec = ((0 == timeout) ? 0 : 1); + time_for_read.at_usec = 0; + if (NO_M_TIMEOUT == timeout) + { + timed = FALSE; + msec_timeout = NO_M_TIMEOUT; + } else + { + timed = TRUE; + msec_timeout = timeout2msec(timeout); + if (msec_timeout > 0) + { /* there is time to wait */ + sys_get_curr_time(&cur_time); + add_int_to_abs_time(&cur_time, msec_timeout, &end_time); + start_timer(timer_id, msec_timeout, wake_alarm, 0, NULL); + } else + out_of_time = TRUE; + } + for (status = 0; 0 == status; ) + { + FD_ZERO(&tcp_fd); + FD_SET(lsock, &tcp_fd); + /* + * Note: the check for EINTR from the select below should remain, as aa_select is a + * function, and not all callers of aa_select behave the same when EINTR is returned. + */ + lcl_time_for_read = time_for_read; + status = tcp_routines.aa_select(lsock + 1, (void *)&tcp_fd, (void *)0, (void *)0, + &lcl_time_for_read); + if (0 > status) + { + if (EINTR == errno && FALSE == out_of_time) + /* interrupted by a signal which is not OUR timer */ + status = 0; + else + break; + } + if (outofband) + break; + if (timed) + { + if (msec_timeout > 0) + { + sys_get_curr_time(&cur_time); + cur_time = sub_abs_time(&end_time, &cur_time); + if (cur_time.at_sec <= 0) + { + out_of_time = TRUE; + cancel_timer(timer_id); + break; + } + } else + break; + } + } + if (timed) + { + if (0 != msec_timeout) + { + cancel_timer(timer_id); + if (out_of_time || outofband) + return FALSE; + /*if (outofband) + outofband_action(FALSE);*/ + } + } + if (0 > status) + { + errptr = (char *)STRERROR(errno); + errlen = STRLEN(errptr); + iotcp_rmlsock((io_desc *)dev->iod); + rts_error(VARLSTCNT(6) ERR_SOCKWAIT, 0, ERR_TEXT, 2, errlen, errptr); + return FALSE; + } + size = SIZEOF(struct sockaddr_in); + status = tcp_routines.aa_accept(lsock, &peer, &size); + if (-1 == status) + { +#ifdef __hpux + /*ENOBUFS in HP-UX is either because of a memory problem or when we have received a RST just + after a SYN before an accept call. Normally this is not fatal and is just a transient state. + Hence exiting just after a single error of this kind should not be done. + So retry in case of HP-UX and ENOBUFS error.*/ + if (ENOBUFS == errno) + { + retry_num = 0; + while (HPUX_MAX_RETRIES > retry_num) + { + /*In case of succeeding with select in first go, accept will still get 5ms time difference*/ + SHORT_SLEEP(5); + for ( ; HPUX_MAX_RETRIES > retry_num; retry_num++) + { + lcl_time_for_read.at_sec = 0; + lcl_time_for_read.at_usec = HPUX_SEL_TIMEOUT; + FD_ZERO(&tcp_fd); + FD_SET(lsock, &tcp_fd); + status = tcp_routines.aa_select(lsock + 1, (void *)&tcp_fd, (void *)0, + (void *)0, &lcl_time_for_read); + if (0 < status) + break; + else if (outofband) + break; + else + SHORT_SLEEP(5); + } + if (outofband) + return FALSE; + if (0 >= status) + { + errptr = (char *)STRERROR(errno); + errlen = STRLEN(errptr); + iotcp_rmlsock((io_desc *)dev->iod); + rts_error(VARLSTCNT(6) ERR_SOCKWAIT, 0, ERR_TEXT, 2, errlen, errptr); + return FALSE; + } + status = tcp_routines.aa_accept(lsock, &peer, &size); + if ((-1 == status) && (ENOBUFS == errno)) + retry_num++; + else + break; + } + } + if (-1 == status) +#endif + { + errptr = (char *)STRERROR(errno); + errlen = STRLEN(errptr); + iotcp_rmlsock((io_desc *)dev->iod); + rts_error(VARLSTCNT(6) ERR_SOCKACPT, 0, ERR_TEXT, 2, errlen, errptr); + return FALSE; + } + } + SPRINTF(newtcp.saddr, "%s,%d", tcp_routines.aa_inet_ntoa(peer.sin_addr), + GTM_NTOHS(newtcp.sin.sin_port)); + newtcp.socket = status; + } else /* active connection */ + { + if (NO_M_TIMEOUT != timeout) + { + msec_timeout = timeout2msec(timeout); + sys_get_curr_time(&cur_time); + add_int_to_abs_time(&cur_time, msec_timeout, &end_time); + } + no_time_left = FALSE; + temp_1 = 1; + do + { + if(1 != temp_1) + tcp_routines.aa_close(newtcp.socket); + newtcp.socket = tcp_routines.aa_socket(AF_INET, SOCK_STREAM, 0); + if (-1 == newtcp.socket) + { + errptr = (char *)STRERROR(errno); + errlen = STRLEN(errptr); + rts_error(VARLSTCNT(5) ERR_SOCKINIT, 3, errno, errlen, errptr); + return FALSE; + } + /* allow multiple connections to the same IP address */ + if (-1 == tcp_routines.aa_setsockopt(newtcp.socket, SOL_SOCKET, SO_REUSEADDR, &on, SIZEOF(on))) + { + (void)tcp_routines.aa_close(newtcp.socket); + errptr = (char *)STRERROR(errno); + errlen = STRLEN(errptr); + rts_error(VARLSTCNT(5) ERR_SOCKINIT, 3, errno, errlen, errptr); + return FALSE; + } + size=SIZEOF(newtcp.bufsiz); + if (-1 == tcp_routines.aa_getsockopt(newtcp.socket, SOL_SOCKET, SO_RCVBUF, &newtcp.bufsiz, &size)) + { + (void)tcp_routines.aa_close(newtcp.socket); + errptr = (char *)STRERROR(errno); + errlen = STRLEN(errptr); + rts_error(VARLSTCNT(5) ERR_SOCKINIT, 3, errno, errlen, errptr); + return FALSE; + } + /* + * Note: the check for EINTR from the connect need not be converted to an EINTR wrapper macro, + * since the connect is not retried on EINTR. + */ + temp_1 = tcp_routines.aa_connect(newtcp.socket, (struct sockaddr *)&newtcp.sin, SIZEOF(newtcp.sin)); + if ((temp_1 < 0) && (ECONNREFUSED != errno) && (EINTR != errno)) + { + (void)tcp_routines.aa_close(newtcp.socket); + errptr = (char *)STRERROR(errno); + errlen = STRLEN(errptr); + rts_error(VARLSTCNT(6) ERR_OPENCONN, 0, ERR_TEXT, 2, errlen, errptr); + return FALSE; + } + if ((temp_1 < 0) && (EINTR == errno)) + { + (void)tcp_routines.aa_close(newtcp.socket); + return FALSE; + } + if ((temp_1 < 0) && (NO_M_TIMEOUT != timeout)) + { + sys_get_curr_time(&cur_time); + cur_time = sub_abs_time(&end_time, &cur_time); + if (cur_time.at_sec <= 0) + no_time_left = TRUE; + } + SHORT_SLEEP(100); /* Sleep for 100 ms */ + } + while ((TRUE != no_time_left) && (temp_1 < 0)); + if (temp_1 < 0) /* out of time */ + { + tcp_routines.aa_close(newtcp.socket); + return FALSE; + } +#ifdef ntohs /* if it's a macro, use it instead of tcp_routines.aa_ntohs */ + SPRINTF(newtcp.saddr, "%s,%d", tcp_routines.aa_inet_ntoa(newtcp.sin.sin_addr), + ntohs(newtcp.sin.sin_port)); +#else + SPRINTF(newtcp.saddr, "%s,%d", tcp_routines.aa_inet_ntoa(newtcp.sin.sin_addr), + tcp_routines.aa_ntohs(newtcp.sin.sin_port)); +#endif + } + memcpy(ioptr->dev_sp, &newtcp, SIZEOF(d_tcp_struct)); + ioptr->state = dev_open; + } +#ifdef DEBUG_TCP + PRINTF("%s (%d) <<<\n", __FILE__, tcpptr->socket); +#endif + return TRUE; +} diff --git a/sr_port/iotcp_rdone.c b/sr_port/iotcp_rdone.c new file mode 100644 index 0000000..18deb0a --- /dev/null +++ b/sr_port/iotcp_rdone.c @@ -0,0 +1,26 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" + +int iotcp_rdone (mint *v, int4 timeout) /* timeout in seconds */ +{ + mval tmp; + int ret; + + ret = iotcp_readfl(&tmp, 1, timeout); + if (ret) + *v = (int4)*(unsigned char *)(tmp.str.addr); + else + *v = -1; + return ret; +} diff --git a/sr_port/iotcp_read.c b/sr_port/iotcp_read.c new file mode 100644 index 0000000..11dba9c --- /dev/null +++ b/sr_port/iotcp_read.c @@ -0,0 +1,18 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" + +int iotcp_read (mval *v, int4 timeout) +{ + return iotcp_readfl(v, 0, timeout); /* 0 means not fixed length */ +} diff --git a/sr_port/iotcp_readfl.c b/sr_port/iotcp_readfl.c new file mode 100644 index 0000000..352644b --- /dev/null +++ b/sr_port/iotcp_readfl.c @@ -0,0 +1,282 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include +#include "gtm_stdio.h" +#include "gtm_time.h" +#include "gtm_inet.h" +#include "gtm_string.h" + +#ifdef UNIX +#include "gtm_fcntl.h" +#include "eintr_wrappers.h" +static int fcntl_res; +#endif + +#include "gt_timer.h" +#include "io.h" +#include "iotimer.h" +#include "iotcpdef.h" +#include "iotcp_select.h" +#include "iotcproutine.h" +#include "stringpool.h" +#include "wake_alarm.h" + +/* Maximum we will read in one read for this device */ +#define MAX_READLEN 32767 + +GBLREF io_pair io_curr_device; +GBLREF bool out_of_time; +GBLREF spdesc stringpool; +GBLREF tcp_library_struct tcp_routines; +GBLREF int4 outofband; + +int iotcp_readfl(mval *v, int4 width, int4 timeout) +/* 0 == width is a flag that the caller is read and the length is not actually fixed */ +/* timeout in seconds */ +{ + /* VMS uses the UCX interface; should support others that emulate it */ + boolean_t ret, timed, vari; + int flags, len, real_errno, save_errno; + int i; + io_desc *io_ptr; + d_tcp_struct *tcpptr; + int4 status; + int4 msec_timeout; /* timeout in milliseconds */ + TID timer_id; + ABS_TIME cur_time, end_time, time_for_read, lcl_time_for_read, zero; + fd_set tcp_fd; + char *errptr; + int4 errlen; + + error_def(ERR_IOEOF); + error_def(ERR_TEXT); + error_def(ERR_GETSOCKOPTERR); + error_def(ERR_SETSOCKOPTERR); + +#ifdef DEBUG_TCP + PRINTF("%s >>>\n", __FILE__); +#endif + assert(stringpool.free >= stringpool.base); + assert(stringpool.free <= stringpool.top); + if (0 == width) + { /* op_readfl won't do this; must be a call from iotcp_read */ + vari = TRUE; + width = MAX_READLEN; + } else + { + vari = FALSE; + width = (width < MAX_READLEN) ? width : MAX_READLEN; + } + ENSURE_STP_FREE_SPACE(width); + io_ptr = io_curr_device.in; + assert(dev_open == io_ptr->state); + tcpptr = (d_tcp_struct *)(io_ptr->dev_sp); + if (io_ptr->dollar.x && (TCP_WRITE == tcpptr->lastop)) + { /* switching from write to read */ +#ifdef C9A06001531 + /* pending change request C9A06-001531 */ + if (!io_ptr->dollar.za) + iotcp_wteol(1, io_ptr); +#endif + io_ptr->dollar.x = 0; + } + tcpptr->lastop = TCP_READ; + ret = TRUE; + timer_id = (TID)iotcp_readfl; + out_of_time = FALSE; + time_for_read.at_sec = ((0 == timeout) ? 0 : 1); + time_for_read.at_usec = 0; + if (NO_M_TIMEOUT == timeout) + { + timed = FALSE; + msec_timeout = NO_M_TIMEOUT; + } else + { + timed = TRUE; + msec_timeout = timeout2msec(timeout); + if (msec_timeout > 0) + { /* there is time to wait */ +#ifdef UNIX + /* set blocking I/O */ + FCNTL2(tcpptr->socket, F_GETFL, flags); + if (flags < 0) + { + save_errno = errno; + errptr = (char *)STRERROR(errno); + rts_error(VARLSTCNT(7) ERR_GETSOCKOPTERR, 5, LEN_AND_LIT("F_GETFL FOR NON BLOCKING I/O"), + save_errno, LEN_AND_STR(errptr)); + } + FCNTL3(tcpptr->socket, F_SETFL, flags & (~(O_NDELAY | O_NONBLOCK)), fcntl_res); + if (fcntl_res < 0) + { + save_errno = errno; + errptr = (char *)STRERROR(errno); + rts_error(VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, LEN_AND_LIT("F_SETFL FOR NON BLOCKING I/O"), + save_errno, LEN_AND_STR(errptr)); + } +#endif + sys_get_curr_time(&cur_time); + add_int_to_abs_time(&cur_time, msec_timeout, &end_time); + start_timer(timer_id, msec_timeout, wake_alarm, 0, NULL); + } else + out_of_time = TRUE; + } + for (i = 0, status = 0; status >= 0; ) + { + FD_ZERO(&tcp_fd); + FD_SET(tcpptr->socket, &tcp_fd); + assert(0 != FD_ISSET(tcpptr->socket, &tcp_fd)); + assert(((1 == time_for_read.at_sec) || (0 == time_for_read.at_sec)) && (0 == time_for_read.at_usec)); + /* + * the check for EINTR below is valid and should not be converted to an EINTR + * wrapper macro, since it might be a timeout. + */ + lcl_time_for_read = time_for_read; + status = tcp_routines.aa_select(tcpptr->socket + 1, (void *)(&tcp_fd), (void *)0, (void *)0, + &lcl_time_for_read); + if (status > 0) + { + status = tcp_routines.aa_recv(tcpptr->socket, (char *)(stringpool.free + i), width - i, 0); + if ((0 == status) || ((-1 == status) && (ECONNRESET == errno || EPIPE == errno || EINVAL == errno))) + { /* lost connection. */ + if (0 == status) + errno = ECONNRESET; + real_errno = errno; + status = -2; + break; + } + + } + if (status < 0) + { + if (EINTR == errno && FALSE == out_of_time) + { /* interrupted by a signal which is not OUR timer, continue looping */ + status = 0; + } else + real_errno = errno; + } else + real_errno = 0; + if (outofband) + break; + if (timed) + { + if (msec_timeout > 0) + { + sys_get_curr_time(&cur_time); + cur_time = sub_abs_time(&end_time, &cur_time); + if (cur_time.at_sec <= 0) + { + out_of_time = TRUE; + cancel_timer(timer_id); + if (status > 0) + i += status; + break; + } + } else + { + if (status > 0) + i += status; + break; + } + } + if (0 > status) + break; + i += status; + if ((vari && (0 != i)) || (i >= width)) + break; + } + if (EINTR == real_errno) + status = 0; /* don't treat a or timeout as an error */ + if (timed) + { + if (0 == msec_timeout) + { + if (0 == status) + ret = FALSE; + } else + { +#ifdef UNIX + real_errno = errno; + FCNTL3(tcpptr->socket, F_SETFL, flags, fcntl_res); + if (fcntl_res < 0) + { + save_errno = errno; + errptr = (char *)STRERROR(errno); + rts_error(VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, LEN_AND_LIT("F_SETFL FOR RESTORING SOCKET OPTIONS"), + save_errno, LEN_AND_STR(errptr)); + } + errno = real_errno; +#endif + if (out_of_time && (i < width)) + ret = FALSE; + else + cancel_timer(timer_id); + } + } + if (i > 0) + { /* there's somthing to return */ + v->str.len = i; + v->str.addr = (char *)stringpool.free; + if (((io_ptr->dollar.x += i) >= io_ptr->width) && (TRUE == io_ptr->wrap)) + { + io_ptr->dollar.y += (io_ptr->dollar.x / io_ptr->width); + if (0 != io_ptr->length) + io_ptr->dollar.y %= io_ptr->length; + io_ptr->dollar.x %= io_ptr->width; + } + } else + v->str.len = 0; +#ifdef DEBUG_TCP + PRINTF("%s <<<\n", __FILE__); +#endif + len = SIZEOF("1,") - 1; + if (status >= 0) + { /* no real problems */ + io_ptr->dollar.za = 0; +/* the handling of urgent data doesn't work and never has, the design should be changed to use a /URGENT controlnmemonic + * because there is really only one character available at a time + zero.at_sec = zero.at_usec = 0; + FD_ZERO(&tcp_fd); + FD_SET(tcpptr->socket, &tcp_fd); + if (tcp_routines.aa_select(tcpptr->socket + 1, (void *)(tcpptr->urgent ? &tcp_fd : 0), (void *)0, + (void *)(tcpptr->urgent ? 0 : &tcp_fd), &zero) > 0) + { + memcpy(tcpptr->dollar_device, "1,", len); + if (tcpptr->urgent) + { + memcpy(&tcpptr->dollar_device[len], "No ",SIZEOF("No ")); + len += SIZEOF("No ") - 1; + } + memcpy(&tcpptr->dollar_device[len], "Urgent Data", SIZEOF("Urgent Data")); + } else +*/ + memcpy(tcpptr->dollar_device, "0", SIZEOF("0")); + } else + { /* there's a significant problem */ + if (0 == i) + io_ptr->dollar.x = 0; + io_ptr->dollar.za = 9; + memcpy(tcpptr->dollar_device, "1,", len); + errptr = (char *)STRERROR(errno); + errlen = STRLEN(errptr); + memcpy(&tcpptr->dollar_device[len], errptr, errlen); + if (io_ptr->dollar.zeof || -1 == status || 0 < io_ptr->error_handler.len) + { + io_ptr->dollar.zeof = TRUE; + rts_error(VARLSTCNT(6) ERR_IOEOF, 0, ERR_TEXT, 2, errlen, errptr); + } else + io_ptr->dollar.zeof = TRUE; + } + return (ret); +} diff --git a/sr_port/iotcp_use.c b/sr_port/iotcp_use.c new file mode 100644 index 0000000..3f6045f --- /dev/null +++ b/sr_port/iotcp_use.c @@ -0,0 +1,122 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gtm_unistd.h" +#include "gtm_inet.h" +#include "gtm_iconv.h" +#include "gtm_stdio.h" + +#include "copy.h" +#include "io.h" +#include "iosp.h" +#include "io_params.h" +#include "iotcpdef.h" +#include "stringpool.h" + +typedef struct +{ + unsigned short mem; + unsigned short grp; +} uic_struct; + +LITREF unsigned char io_params_size[]; + +void iotcp_use(io_desc *iod, mval *pp) +{ + unsigned char c; + int4 length, width; + d_tcp_struct *tcpptr, newtcp; + int p_offset; + + error_def(ERR_DEVPARMNEG); + error_def(ERR_RMWIDTHPOS); + +#ifdef DEBUG_TCP + PRINTF("%s >>>\n", __FILE__); +#endif + p_offset = 0; + tcpptr = (d_tcp_struct *)iod->dev_sp; + /* copy existing parameters */ + memcpy(&newtcp, tcpptr, SIZEOF(d_tcp_struct)); + while (*(pp->str.addr + p_offset) != iop_eol) + { + assert((params) *(pp->str.addr + p_offset) < (params) n_iops); + switch (c = *(pp->str.addr + p_offset++)) + { + case iop_width: + GET_LONG(width, pp->str.addr + p_offset); + if (width == 0) + newtcp.width = TCPDEF_WIDTH; + else if (width > 0) + newtcp.width = width; + else + rts_error(VARLSTCNT(1) ERR_DEVPARMNEG); + break; + case iop_length: + GET_LONG(length, pp->str.addr + p_offset); + if (length == 0) + newtcp.length = TCPDEF_LENGTH; + else if (length > 0) + newtcp.length = length; + else + rts_error(VARLSTCNT(1) ERR_DEVPARMNEG); + break; + case iop_urgent: + newtcp.urgent = TRUE; + break; + case iop_nourgent: + newtcp.urgent = FALSE; + break; + case iop_exception: + iod->error_handler.len = *(pp->str.addr + p_offset); + iod->error_handler.addr = (char *)(pp->str.addr + p_offset + 1); + s2pool(&iod->error_handler); + break; + case iop_ipchset: +#if defined(KEEP_zOS_EBCDIC) || defined(VMS) + if ( (iconv_t)0 != iod->input_conv_cd ) + { + ICONV_CLOSE_CD(iod->input_conv_cd); + } + SET_CODE_SET(iod->in_code_set, (char *)(pp->str.addr + p_offset + 1)); + if (DEFAULT_CODE_SET != iod->in_code_set) + ICONV_OPEN_CD(iod->input_conv_cd, INSIDE_CH_SET, (char *)(pp->str.addr + p_offset + 1)); +#endif + break; + case iop_opchset: +#if defined(KEEP_zOS_EBCDIC) || defined(VMS) + if ( (iconv_t)0 != iod->output_conv_cd ) + { + ICONV_CLOSE_CD(iod->output_conv_cd); + } + SET_CODE_SET(iod->out_code_set, (char *)(pp->str.addr + p_offset + 1)); + if (DEFAULT_CODE_SET != iod->out_code_set) + ICONV_OPEN_CD(iod->output_conv_cd, (char *)(pp->str.addr + p_offset + 1), INSIDE_CH_SET); +#endif + break; + default: + break; + } + p_offset += ((IOP_VAR_SIZE == io_params_size[c]) ? + (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[c]); + } + + /* commit changes */ + memcpy(tcpptr, &newtcp, SIZEOF(d_tcp_struct)); +#ifdef DEBUG_TCP + PRINTF("%s <<<\n", __FILE__); +#endif + return; +} diff --git a/sr_port/iotcp_write.c b/sr_port/iotcp_write.c new file mode 100644 index 0000000..7a301bb --- /dev/null +++ b/sr_port/iotcp_write.c @@ -0,0 +1,85 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_socket.h" +#include "gtm_stdio.h" +#include "gtm_string.h" +#include "gtm_inet.h" + +#include + +#include "io.h" +#include "iotcpdef.h" +#include "iotcpdefsp.h" +#include "iotcproutine.h" + +GBLREF io_pair io_curr_device; +GBLREF tcp_library_struct tcp_routines; + +void iotcp_write(mstr *v) +{ + io_desc *iod; + char *out; + int inlen, outlen, size; + d_tcp_struct *tcpptr; + char *errptr; + int4 errlen; + + error_def(ERR_SOCKWRITE); + error_def(ERR_TEXT); + +#ifdef DEBUG_TCP + PRINTF("%s >>>\n", __FILE__); +#endif + iod = io_curr_device.out; + tcpptr = (d_tcp_struct *)iod->dev_sp; + tcpptr->lastop = TCP_WRITE; + memcpy(tcpptr->dollar_device, LITZERO, SIZEOF(LITZERO)); + inlen = v->len; + outlen = iod->width - iod->dollar.x; + + if (!iod->wrap && inlen > outlen) + inlen = outlen; + if (!inlen) + return; + for (out = v->addr; ; out += size) + { + if (outlen > inlen) + outlen = inlen; + if ((size = tcp_routines.aa_send(tcpptr->socket, out, outlen, (tcpptr->urgent ? MSG_OOB : 0))) == -1) + { + iod->dollar.za = 9; + memcpy(tcpptr->dollar_device, LITONE_COMMA, SIZEOF(LITONE_COMMA)); + errptr = (char *)STRERROR(errno); + errlen = STRLEN(errptr); + memcpy(&tcpptr->dollar_device[SIZEOF(LITONE_COMMA) - 1], errptr, errlen); + rts_error(VARLSTCNT(6) ERR_SOCKWRITE, 0, ERR_TEXT, 2, errlen, errptr); + } + assert(size == outlen); + iod->dollar.x += size; + if ((inlen -= size) <= 0) + break; + + iod->dollar.x = 0; /* don't use wteol to terminate wrapped records for fixed. */ + iod->dollar.y++; /* \n is reserved as an end-of-rec delimiter for variable format */ + if (iod->length) /* and fixed format requires no padding for wrapped records */ + iod->dollar.y %= iod->length; + + outlen = iod->width; + } + iod->dollar.za = 0; +#ifdef DEBUG_TCP + PRINTF("%s <<<\n", __FILE__); +#endif + return; +} diff --git a/sr_port/iotcp_wteol.c b/sr_port/iotcp_wteol.c new file mode 100644 index 0000000..b212352 --- /dev/null +++ b/sr_port/iotcp_wteol.c @@ -0,0 +1,32 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include +#include +#include + +#include "gtm_stdio.h" +#include "gtm_string.h" +#include "gtm_socket.h" +#include "gtm_inet.h" + +#include "io.h" +#include "iotcpdef.h" +#include "iotcpdefsp.h" +#include "iotcproutine.h" + +void iotcp_wteol(int4 x, io_desc *iod) +{ + /* pending a change request C9A06-001531 */ + return; +} diff --git a/sr_port/iotcp_wtff.c b/sr_port/iotcp_wtff.c new file mode 100644 index 0000000..8668ea6 --- /dev/null +++ b/sr_port/iotcp_wtff.c @@ -0,0 +1,42 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_stdio.h" +#include "io.h" + +#define FORM_FEED "\014" +GBLREF io_pair io_curr_device; + +void iotcp_wtff(void) +{ + mstr temp; + io_desc *iod; + +#ifdef DEBUG_TCP + PRINTF("%s >>>\n",__FILE__); +#endif + iod = io_curr_device.out; +#ifdef C9A06001531 + iotcp_wteol(1,iod); +#endif + temp.len = SIZEOF(FORM_FEED) - 1; + temp.addr = FORM_FEED; + iotcp_write(&temp); +#ifdef C9A06001531 + iotcp_wteol(1,iod); +#endif + iod->dollar.x = 0; + iod->dollar.y = 0; +#ifdef DEBUG_TCP + PRINTF("%s <<<\n",__FILE__); +#endif +} diff --git a/sr_port/iotcp_wtone.c b/sr_port/iotcp_wtone.c new file mode 100644 index 0000000..8f8104a --- /dev/null +++ b/sr_port/iotcp_wtone.c @@ -0,0 +1,32 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_stdio.h" +#include "io.h" + +void iotcp_wtone(int ch) +{ + mstr temp; + char c; + +#ifdef DEBUG_TCP + PRINTF("%s >>>\n",__FILE__); +#endif + c = (char)ch; + temp.len = 1; + temp.addr = &c; + iotcp_write(&temp); +#ifdef DEBUG_TCP + PRINTF("%s <<<\n",__FILE__); +#endif + return; +} diff --git a/sr_port/iotcpdef.h b/sr_port/iotcpdef.h new file mode 100644 index 0000000..9a49a83 --- /dev/null +++ b/sr_port/iotcpdef.h @@ -0,0 +1,103 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __IOTCPDEF_H__ +#define __IOTCPDEF_H__ +#include "gtm_inet.h" +/* iotcpdef.h UNIX - TCP header file */ + +#define TCPDEF_WIDTH 255 +#define TCPDEF_LENGTH 66 + +#define TCP_NOOP 0 +#define TCP_WRITE 1 +#define TCP_READ 2 + +#define SA_MAXLEN 32 /* SIZEOF(123.567.901.345,78901) */ +#define SA_MAXLITLEN 128 /* maximun size of beowulf.sanchez.com */ +#define DD_BUFLEN 80 + +#ifdef VMS +#define VMS_MAX_TCP_IO_SIZE (64 * 1024 - 512) /* Hard limit for TCP send or recv size. On some implementations, the limit is + * 64K - 1, on others it is 64K - 512. We take the conservative approach and + * choose the lower limit + */ +#endif + +/*Definitions in case of ENOBUFs error in HPUX*/ +#ifdef __hpux +#define HPUX_MAX_RETRIES 8 +#define HPUX_SEL_TIMEOUT (20 * 1000) /*20 milliseconds(reperesented in micro secs)*/ +#endif + + +#define DOTCPSEND(SDESC, SBUFF, SBUFF_LEN, SFLAGS, RC) \ +{ \ + ssize_t gtmioStatus; \ + size_t gtmioBuffLen; \ + size_t gtmioChunk; \ + sm_uc_ptr_t gtmioBuff; \ + gtmioBuffLen = SBUFF_LEN; \ + gtmioBuff = (sm_uc_ptr_t)(SBUFF); \ + for (;;) \ + { \ + gtmioChunk = gtmioBuffLen VMS_ONLY(> VMS_MAX_TCP_IO_SIZE ? VMS_MAX_TCP_IO_SIZE : gtmioBuffLen); \ + if ((ssize_t)-1 != (gtmioStatus = tcp_routines.aa_send(SDESC, gtmioBuff, gtmioChunk, SFLAGS))) \ + { \ + gtmioBuffLen -= gtmioStatus; \ + if (0 == gtmioBuffLen) \ + break; \ + gtmioBuff += gtmioStatus; \ + } \ + else if (EINTR != errno) \ + break; \ + } \ + if ((ssize_t)-1 == gtmioStatus) /* Had legitimate error - return it */ \ + RC = errno; \ + else if (0 == gtmioBuffLen) \ + RC = 0; \ + else \ + RC = -1; /* Something kept us from sending what we wanted */ \ +} + +/* ***************************************************** */ +/* *********** structures for TCP driver *************** */ +/* ***************************************************** */ + +typedef struct +{ + char saddr[SA_MAXLEN]; /* socket address */ + char dollar_device[DD_BUFLEN]; + struct sockaddr_in sin; /* socket address + port */ + unsigned char lastop; + int bufsiz; /* OS internal buffer size */ + int socket; /* socket descriptor */ + int4 width; + int4 length; + bool passive; /* passive connection */ + bool urgent; /* urgent data mode */ +}d_tcp_struct; /* tcp */ + +/* if ntohs/htons are macros, use them, otherwise, use the tcp_routines */ + +#define MAX_DELIM_BUFF 64 +#ifdef ntohs +# define GTM_NTOHS ntohs +#else +# define GTM_NTOHS tcp_routines.aa_ntohs +#endif +#ifdef htons +# define GTM_HTONS htons +#else +# define GTM_HTONS tcp_routines.aa_htons +#endif + +#endif diff --git a/sr_port/iotcpdefsp.h b/sr_port/iotcpdefsp.h new file mode 100644 index 0000000..1656f2f --- /dev/null +++ b/sr_port/iotcpdefsp.h @@ -0,0 +1,19 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#define ASCII_LITONE_COMMA "1," +#define EBCDIC_LITONE_COMMA "\361\153" + +#define ASCII_LITZERO "0" +#define EBCDIC_LITZERO "\360" + +#define LITONE_COMMA ASCII_LITONE_COMMA +#define LITZERO ASCII_LITZERO diff --git a/sr_port/iotcproutine.h b/sr_port/iotcproutine.h new file mode 100644 index 0000000..a79763b --- /dev/null +++ b/sr_port/iotcproutine.h @@ -0,0 +1,40 @@ +/**************************************************************** + * * + * Copyright 2001, 2005 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* pointers for tcp/ip routines */ +typedef struct +{ + int (*aa_accept)(); + int (*aa_bind)(); + int (*aa_close)(); + int (*aa_connect)(); + int (*aa_getsockopt)(); + int (*aa_getsockname)(); + unsigned short (*aa_htons)(in_port_t); +/* smw 1999/12/15 STDC is not a good flag to use so why is it here + perhaps should define in_addr_t somewhere if needed. */ +#if !defined(__STDC__) + uint4 (*aa_inet_addr)(); +#else + in_addr_t (*aa_inet_addr)(const char *); +#endif + char *(*aa_inet_ntoa)(); + unsigned short (*aa_ntohs)(in_port_t); + int (*aa_listen)(); + int (*aa_recv)(); + int (*aa_select)(); + int (*aa_send)(); + int (*aa_setsockopt)(); + int (*aa_shutdown)(); + int (*aa_socket)(); + bool using_tcpware; /* use tcpware(1) or ucx(0) */ +}tcp_library_struct; + diff --git a/sr_port/iotimer.h b/sr_port/iotimer.h new file mode 100644 index 0000000..83d7f20 --- /dev/null +++ b/sr_port/iotimer.h @@ -0,0 +1,12 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#define NO_M_TIMEOUT 0x0007FFFE diff --git a/sr_port/iott_dummy.c b/sr_port/iott_dummy.c new file mode 100644 index 0000000..cac9979 --- /dev/null +++ b/sr_port/iott_dummy.c @@ -0,0 +1,18 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" + +short iott_dummy(io_log_name *dev_name, mval *pp, int fd, mval *mspace, int4 timeout) +{ + return 0; +} diff --git a/sr_port/iott_escape.c b/sr_port/iott_escape.c new file mode 100644 index 0000000..a711ae0 --- /dev/null +++ b/sr_port/iott_escape.c @@ -0,0 +1,100 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" +#include "iottdef.h" +#ifdef KEEP_zOS_EBCDIC +#include "gtm_unistd.h" +#endif + +uchar_ptr_t iott_escape(uchar_ptr_t strin, uchar_ptr_t strtop, io_desc *io_ptr) +{ + unsigned char *str; + unsigned char esc_type; +#ifdef KEEP_zOS_EBCDIC + error_def(ERR_ASC2EBCDICCONV); + if ((DEFAULT_CODE_SET != io_ptr->in_code_set) && ( -1 == __etoa_l((char *)strin, strtop - strin) )) + rts_error(VARLSTCNT(4) ERR_ASC2EBCDICCONV, 2, LEN_AND_LIT("__etoa_l")); +#endif + + str = strin; + esc_type = io_ptr->esc_state; + + while (esc_type < FINI) + { + switch (esc_type) + { + case START: + assert(*str == ESC); + esc_type = AFTESC; + break; + case AFTESC: + switch(*str) + { + case ';': + case '?': + esc_type = SEQ2; + break; + case 'O': + esc_type = SEQ4; + break; + case '[': + esc_type = SEQ1; + break; + default: + if (*str >= 0x30 && *str < 0x7F) + esc_type = FINI; + else if (*str > 0x1F && *str < 0x30) + esc_type = SEQ2; + else + esc_type = BADESC; + break; + } + break; + case SEQ1: + if (*str < 0x30 && *str > 0x1F) + esc_type = SEQ3; + else if (*str > 0x3F && *str < 0x7F) + esc_type = FINI; + else if (*str > 0x3F || *str < 0x30) + esc_type = BADESC; + break; + case SEQ2: + if (*str >= 0x30 && *str < 0x7F) + esc_type = FINI; + else if (*str > 0x2F || *str < 0x20) + esc_type = BADESC; + break; + case SEQ3: + if (*str > 0x3F && *str < 0x7F) + esc_type = FINI; + else if (*str > 0x2F || *str < 0x20) + esc_type = BADESC; + case SEQ4: + if (*str >= 0x40 && *str < 0x7F) + esc_type = FINI; + else if (*str > 0x2F || *str < 0x20) + esc_type = BADESC; + break; + default: + GTMASSERT; + } + if (esc_type == BADESC || ++str >= strtop) + break; + } + io_ptr->esc_state = esc_type; +#ifdef KEEP_zOS_EBCDIC + if ((DEFAULT_CODE_SET != io_ptr->in_code_set) && ( -1 == __atoe_l((char *)strin, strtop - strin) )) + rts_error(VARLSTCNT(4) ERR_ASC2EBCDICCONV, 2, LEN_AND_LIT("__atoe_l")); +#endif + return str; +} diff --git a/sr_port/iott_wrterr.c b/sr_port/iott_wrterr.c new file mode 100644 index 0000000..2055448 --- /dev/null +++ b/sr_port/iott_wrterr.c @@ -0,0 +1,25 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "iott_wrterr.h" + +GBLREF int iott_write_error; + +void iott_wrterr(void) +{ + int status; + error_def(ERR_TERMWRITE); + + status = iott_write_error; + iott_write_error = 0; + rts_error(VARLSTCNT(3) ERR_TERMWRITE, 0, status); +} diff --git a/sr_port/iott_wrterr.h b/sr_port/iott_wrterr.h new file mode 100644 index 0000000..710c56d --- /dev/null +++ b/sr_port/iott_wrterr.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef IOTT_WRTERR_H_INCLUDED +#define IOTT_WRTERR_H_INCLUDED + +void iott_wrterr(void); + +#endif /* IOTT_WRTERR_H_INCLUDED */ diff --git a/sr_port/iott_wteol.c b/sr_port/iott_wteol.c new file mode 100644 index 0000000..66e95d3 --- /dev/null +++ b/sr_port/iott_wteol.c @@ -0,0 +1,60 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#ifdef UNIX +#include "error.h" +#endif +#include "io.h" +#include "iottdef.h" + +/* essentially the same as ionl_wteol */ +void iott_wteol(int4 val, io_desc *io_ptr) +{ + mstr eol; + int eol_cnt; + UNIX_ONLY(d_tt_struct *tt_ptr;) + + UNIX_ONLY(error_def(ERR_ZINTRECURSEIO);) + + assert(val); + +#ifdef UNIX + tt_ptr = (d_tt_struct *)io_ptr->dev_sp; + if (tt_ptr->mupintr) + rts_error(VARLSTCNT(1) ERR_ZINTRECURSEIO); +#endif + io_ptr->esc_state = START; + eol.len = STRLEN(NATIVE_TTEOL); + eol.addr = (char *)NATIVE_TTEOL; + for (eol_cnt = val; eol_cnt--; ) + { + io_ptr->dollar.x = 0; /* so that iott_write doesn't try to wrap (based on escape state and width) */ + iott_write(&eol); + } + /* $X is maintained in VMS without the below assignment (resetting to 0) because the NATIVE_TTEOL is \015\012 + * and the (\015) triggers appropriate maintenance of $X. In UNIX, NATIVE_TTEOL is \012, so + * FILTER=CHARACTER effectively turns off all $X maintenance (except for WRAP logic). + * In VMS the below assignment is not necessary, but harmless; it is always logically correct. + */ + io_ptr->dollar.x = 0; + if (!(io_ptr->write_filter & CHAR_FILTER)) + { /* If no FILTER and EOL, also maintain $Y; + * If FILTER, dollarx() of the linefeed character \012 takes care of this maintenance. + */ + io_ptr->dollar.y += val; + if (io_ptr->length) + io_ptr->dollar.y %= io_ptr->length; + } + return; +} diff --git a/sr_port/iott_wtff.c b/sr_port/iott_wtff.c new file mode 100644 index 0000000..5c3d90e --- /dev/null +++ b/sr_port/iott_wtff.c @@ -0,0 +1,35 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "io.h" +#include "iottdef.h" +#include "io_params.h" + +GBLREF io_pair io_curr_device; + +static readonly unsigned char home_param_list[] = +{ + (unsigned char)iop_x, + 0, 0, 0, 0, + (unsigned char)iop_y, + 0, 0, 0, 0, + (unsigned char)iop_clearscreen, + (unsigned char)iop_eol +}; +static readonly mval home_params = DEFINE_MVAL_LITERAL(MV_STR, 0, 0, SIZEOF(home_param_list) - 1, (char *)home_param_list, 0, 0); + +void iott_wtff(void) +{ + io_curr_device.out->esc_state = START; + iott_use(io_curr_device.out, &home_params); +} diff --git a/sr_port/iott_wtone.c b/sr_port/iott_wtone.c new file mode 100644 index 0000000..9bb0a16 --- /dev/null +++ b/sr_port/iott_wtone.c @@ -0,0 +1,52 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "io.h" +#ifdef UNICODE_SUPPORTED +#include "gtm_utf8.h" +#endif + +GBLREF io_pair io_curr_device; +GBLREF boolean_t gtm_utf8_mode; +LITREF mstr chset_names[]; + +void iott_wtone(int v) +{ + mstr temp; + char p[1]; +#ifdef UNICODE_SUPPORTED + unsigned char utf_buf[GTM_MB_LEN_MAX], *up; +#endif + io_desc *iod; + UNICODE_ONLY(error_def(ERR_BADCHSET);) + + if (!gtm_utf8_mode UNICODE_ONLY(|| CHSET_M == io_curr_device.out->ochset)) + { + p[0] = (char)v; + temp.len = 1; + temp.addr = p; + UNICODE_ONLY(temp.char_len = 1;) + } +#ifdef UNICODE_SUPPORTED + else if (CHSET_UTF8 == io_curr_device.out->ochset) + { + up = UTF8_WCTOMB(v, utf_buf); + temp.len = INTCAST(up - utf_buf); + temp.addr = (char *)&utf_buf[0]; + } else + rts_error(VARLSTCNT(4) ERR_BADCHSET, 2, chset_names[io_curr_device.out->ochset].len, + chset_names[io_curr_device.out->ochset].addr); +#endif + iott_write(&temp); + return; +} diff --git a/sr_port/ious_close.c b/sr_port/ious_close.c new file mode 100644 index 0000000..25a697d --- /dev/null +++ b/sr_port/ious_close.c @@ -0,0 +1,21 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" +#include "iousdef.h" + +void ious_close(io_desc *iod, mval *pp) +{ + assert(iod->state == dev_open); + ((void(*)())(((d_us_struct*)(iod->dev_sp))->disp->close))(); + iod->state = dev_closed; +} diff --git a/sr_port/ious_dummy.c b/sr_port/ious_dummy.c new file mode 100644 index 0000000..86126b6 --- /dev/null +++ b/sr_port/ious_dummy.c @@ -0,0 +1,18 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" + +short ious_dummy(io_log_name *dev_name, mval *pp, int fd, mval *mspace, int4 timeout) +{ + return 0; +} diff --git a/sr_port/ious_flush.c b/sr_port/ious_flush.c new file mode 100644 index 0000000..1425eec --- /dev/null +++ b/sr_port/ious_flush.c @@ -0,0 +1,20 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" +#include "iousdef.h" + +void ious_flush(io_desc *iod) +{ + assert(iod->state == dev_open); + ((void(*)())(((d_us_struct*)(iod->dev_sp))->disp->flush))(); +} diff --git a/sr_port/ious_use.c b/sr_port/ious_use.c new file mode 100644 index 0000000..5a0c554 --- /dev/null +++ b/sr_port/ious_use.c @@ -0,0 +1,20 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" +#include "iousdef.h" + +void ious_use(io_desc *iod, mval *pp) +{ + assert(iod->state == dev_open); + ((void(*)())(((d_us_struct*)(iod->dev_sp))->disp->use))(); +} diff --git a/sr_port/ious_wteol.c b/sr_port/ious_wteol.c new file mode 100644 index 0000000..3e61d97 --- /dev/null +++ b/sr_port/ious_wteol.c @@ -0,0 +1,20 @@ +/**************************************************************** + * * + * Copyright 2001, 2004 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" +#include "iousdef.h" + +void ious_wteol(int4 x, io_desc *iod) +{ + assert(iod->state == dev_open); + ((void(*)())(((d_us_struct*)(iod->dev_sp))->disp->wteol))(x); +} diff --git a/sr_port/ious_wtff.c b/sr_port/ious_wtff.c new file mode 100644 index 0000000..d0c41db --- /dev/null +++ b/sr_port/ious_wtff.c @@ -0,0 +1,21 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "io.h" +#include "iousdef.h" + +GBLREF io_pair io_curr_device; + +void ious_wtff() +{ + (((d_us_struct*)(io_curr_device.out->dev_sp))->disp->wtff)(); +} diff --git a/sr_port/iousdef.h b/sr_port/iousdef.h new file mode 100644 index 0000000..1761d1c --- /dev/null +++ b/sr_port/iousdef.h @@ -0,0 +1,21 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#define MAX_US_READ 128 + +/* ***************************************************** */ +/* ********* structure for user device driver ********** */ +/* ***************************************************** */ + +typedef struct +{ + dev_dispatch_struct *disp; +}d_us_struct; diff --git a/sr_port/is_canonic_name.c b/sr_port/is_canonic_name.c new file mode 100644 index 0000000..da48b3b --- /dev/null +++ b/sr_port/is_canonic_name.c @@ -0,0 +1,359 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_ctype.h" +#include "is_canonic_name.h" + +#ifdef DEBUG +#include "subscript.h" +#endif + +#ifdef UNICODE_SUPPORTED +#include "gtm_utf8.h" +GBLREF boolean_t badchar_inhibit; +error_def(ERR_BADCHAR); +#endif + +/* + * ----------------------------------------------- + * is_canonic_name() + * validate a variable name + * + * Arguments: + * src - Pointer to Source Name string mval + * subscripts - Pointer to sequence number of subscript to find & return of subscript count + * start_off - Pointer offset of the component requested by op_fnqsubscript + * stop_off - Pointer offset of the end of the component requested by op_fnqsubscript + * Return: + * boolean_t - TRUE indicates good name; FALSE indicates defective + * ----------------------------------------------- + */ +boolean_t is_canonic_name(mval *src, int *subscripts, int *start_off, int *stop_off) +{ /* subscripts is overloaded - out to op_fnqlength, which doesn't use the last 2 arguments & in from op_fnqsubscript */ + char term; + int envpart; + boolean_t instring; + int isrc; + boolean_t keep_quotes; + char letter; + int point; + char previous; + int seq; + int start; + int state; + int stop; + int subs_count; + int utf8_len; + + /* state: + * 0 before start of name + * 1 found ^ allow environment + * 2 dispatch for starting a component + * 3 in string + * 4 in number + * 5 expect first letter of name + * 6 expect next letter of name + * 7 in $CHAR() + * 8 at end of processing + */ + + MV_FORCE_STR(src); + seq = *subscripts; + keep_quotes = FALSE; + start = stop = 0; + state = 0; + subs_count = -1; + for (isrc = 0; isrc < src->str.len; ) + { + letter = src->str.addr[isrc]; + switch (state) + { + case 0: /* start of name */ + if ('^' == letter) /* before start of name */ + { + state = 1; /* check for environment */ + break; + } + if (('%' == letter) || ISALPHA_ASCII(letter)) + { + if (0 == seq) + start = isrc; + state = 6; /* rest of name */ + break; + } + return FALSE; + case 1: /* global name */ + if (('%' == letter) ||ISALPHA_ASCII(letter)) /* found ^ allow environment */ + { /* found ^ allow environment */ + if (0 == seq) + start = isrc; + state = 6; /* rest of name */ + break; + } + if (('|' == letter) || ('[' == letter)) + { + term = (letter == '[') ? ']' : letter; + envpart = 0; + if (subs_count == seq) + start = isrc + 1; + state = 2; /* process environment */ + break; + } + return FALSE; + case 2: /* dispatch for starting a component */ + point = 0; + instring = FALSE; + if (envpart > 1) + return FALSE; /* too many environment components */ + if (')' == term) + subs_count++; /* new subscript */ + else + envpart++; /* next environment component */ + if ((subs_count == seq) && (0 == stop)) + start = isrc; + if ('"' == letter) + { + if ((subs_count == seq) && (1 == envpart)) + start++; + instring = TRUE; + state = 3; /* string */ + break; + } + if ('$' ==letter) + { + state = 7; /* $[z]char() */ + break; + } + if ('0' == letter) /* Canonic number cannot start with 0 unless is single char */ + { + if (++isrc < src->str.len) + letter = src->str.addr[isrc]; + else + return FALSE; /* Cannot end with "0" */ + if (term == letter) + state = (')' == term) ? 8 : 5; /* end or name */ + else if (',' != letter) + return FALSE; /* Not a single char number */ + if ((subs_count == seq) && (0 == stop)) + stop = isrc; + break; + } + if (('-' == letter) || ('.' == letter) || ISDIGIT_ASCII(letter)) + { + if ('.' == letter) + point++; + previous = letter; + state = 4; /* numeric */ + break; + } + return FALSE; + case 3: /* [quoted] string */ + if ('"' == letter) /* in string */ + { + instring = !instring; + if (instring) + break; + if (isrc + 1 >= src->str.len) + return FALSE; + if ('_' != src->str.addr[isrc + 1]) + break; + isrc++; + if (++isrc < src->str.len) + letter = src->str.addr[isrc]; + else + return FALSE; + if ('$' != letter) + return FALSE; + state = 7; /* $[z]char() */ + break; + } + if (!instring) + { + if (',' == letter) + state = 2; /* on to next */ + else if (term == letter) + state = (')' == term) ? 8 : 5; /* end or name */ + else + return FALSE; + if ((subs_count == seq) && (0 == stop)) + /* Not returning 2nd env part - maybe problem */ + stop = isrc - (keep_quotes ? 0 : 1); + } + break; + case 4: /* numeric */ + if (ISDIGIT_ASCII(letter)) /* in number */ + { + if (('-' == previous) && ('0' == letter)) + return FALSE; + previous = letter; + break; + } + if ('.' == letter) + { + if ((++point > 1)) + return FALSE; + previous = letter; + break; + } + if (point && ('0' == previous)) + return FALSE; + if (',' == letter) + state = 2; /* next */ + else if (term == letter) + state = (')' == term) ? 8 : 5; /* end or name */ + else + return FALSE; + if ((subs_count == seq) && (0 == stop)) + stop = isrc; + previous = letter; + break; + case 5: /* expect first letter of name */ + if (('%' == letter) || ISALPHA_ASCII(letter)) + { + if (0 == seq) + start = isrc; + state = 6; /* rest of name */ + break; + } + return FALSE; + case 6: /* expect next letter of name */ + if ('(' == letter) + { + term = ')'; + envpart = 1; + subs_count = 0; + state = 2; /* done with name */ + if (0 == seq) + stop = isrc; + } else if (!ISALNUM_ASCII(letter)) + return FALSE; + break; + case 7: /* $[Z]CHAR() */ + previous = letter; /* in $CHAR() - must be ASCII */ + if (('Z' == letter) || ('z' == letter)) + { if (++isrc < src->str.len) + letter = src->str.addr[isrc]; + else + return FALSE; + if ('z' == previous) + previous = 'Z'; + } + if (!(('C' == letter) || ('c' == letter))) + return FALSE; + if (++isrc < src->str.len) + letter = src->str.addr[isrc]; + else + return FALSE; + if (('H' == letter) || ('h' == letter)) + { + if (++isrc < src->str.len) + letter = src->str.addr[isrc]; + else + return FALSE; + if (!(('A' == letter) || ('a' == letter) || (('(' == letter) && ('Z' == previous)))) + return FALSE; + } else if ('Z' == previous) + return FALSE; + if ('(' != letter) + { + if (++isrc < src->str.len) + letter = src->str.addr[isrc]; + else + return FALSE; + if (!('R' == letter) || ('r' == letter)) + return FALSE; + if (++isrc < src->str.len) + letter = src->str.addr[isrc]; + else + return FALSE; + } + if ('(' != letter) + return FALSE; + if (subs_count == seq) + keep_quotes = TRUE; + for (++isrc ;isrc < src->str.len; isrc++) + { + letter = src->str.addr[isrc]; + if (ISDIGIT_ASCII(letter)) + continue; + if (!((',' == letter) || (')' == letter))) + return FALSE; + previous = letter; + if (++isrc < src->str.len) + letter = src->str.addr[isrc]; + else + return FALSE; + if (')' == previous) + break; + if (!ISDIGIT_ASCII(letter)) + return FALSE; + } + if (isrc > src->str.len) + return FALSE; + if ('_' == letter) + { + if (++isrc < src->str.len) + letter = src->str.addr[isrc]; + else + return FALSE; + if ('$' == letter) + break; + if ('"' != letter) + return FALSE; + instring = TRUE; + state = 3; /* back to string */ + break; + } + if (',' == letter) + state = 2; + else if (term == letter) + state = (')' == term) ? 8 : 5; /* end or name */ + else + return FALSE; + if ((subs_count == seq) && (0 == stop)) + stop = isrc - (keep_quotes ? 0 : 1); /* Not returning 2nd env part - maybe problem */ + break; + case 8: /* end of subscript but no closing paren - ")" */ + return FALSE; + break; + } +# ifdef UNICODE_SUPPORTED + if (!gtm_utf8_mode || (0 == (letter & 0x80))) + isrc++; + else if (0 < (utf8_len = UTF8_MBFOLLOW(&src->str.addr[isrc++]))) + { /* multi-byte increment */ + assert(4 > utf8_len); + if (0 > utf8_len) + rts_error(VARLSTCNT(6) ERR_BADCHAR, 4, 1, &src->str.addr[isrc - 1], LEN_AND_LIT(UTF8_NAME)); + isrc += utf8_len; + } +# endif + NON_UNICODE_ONLY(isrc++); + } + if ((8 != state) && (6 != state)) + return FALSE; + if ((0 <= seq) && (0 == stop)) + stop = src->str.len - (8 == state ? 1 : 0); + if (keep_quotes && ('"' == src->str.addr[start - 1])) + start--; + assert((0 < subs_count) || ((6 == state) && (-1 == subs_count))); + if (6 == state) + subs_count = 0; + assert((('^' == src->str.addr[0]) ? MAX_GVSUBSCRIPTS : MAX_LVSUBSCRIPTS) > subs_count); + assert((0 < isrc) && (isrc == src->str.len)); + assert(stop <= isrc); + assert((0 <= start) && (start <= stop)); + *subscripts = subs_count; + *start_off = start; + *stop_off = stop; + return TRUE; +} diff --git a/sr_port/is_canonic_name.h b/sr_port/is_canonic_name.h new file mode 100644 index 0000000..b927d1c --- /dev/null +++ b/sr_port/is_canonic_name.h @@ -0,0 +1,12 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +boolean_t is_canonic_name(mval *src, int *subscripts, int *start_off, int *stop_off); diff --git a/sr_port/is_equ.c b/sr_port/is_equ.c new file mode 100644 index 0000000..45edfd1 --- /dev/null +++ b/sr_port/is_equ.c @@ -0,0 +1,44 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +int is_equ(mval *u,mval *v) +{ + int land, lor, utyp, vtyp; + + utyp = u->mvtype; + vtyp = v->mvtype; + land = utyp & vtyp; + lor = utyp | vtyp; + if ((land & MV_NM) != 0 && (lor & MV_NUM_APPROX) == 0) + { + /* at this point, the mval's are both exact numbers, we can do a numeric comparison */ + /* If they are both integers, compare only the relevant cells */ + if (land & MV_INT) + return (u->m[1] == v->m[1]); + /* If one is an integer and the other is not, the two values cannot be equal */ + if (lor & MV_INT) + return 0; + /* They are both decimal floating numbers, do a full comparison */ + return ((((mval_b *)u)->sgne == ((mval_b *)v)->sgne) && (u->m[1] == v->m[1]) && (u->m[0]==v->m[0])); + } + /* At least one of the numbers is not in numeric form or is not a cannoical number, do a string compare */ + MV_FORCE_STR(u); + MV_FORCE_STR(v); + if ((u->str.len != v->str.len) + || (u->str.len && (u->str.addr != v->str.addr) && memcmp(u->str.addr, v->str.addr, u->str.len))) + return 0; + else + return 1; +} diff --git a/sr_port/is_file_identical.h b/sr_port/is_file_identical.h new file mode 100644 index 0000000..60d4a60 --- /dev/null +++ b/sr_port/is_file_identical.h @@ -0,0 +1,27 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __IS_FILE_IDENTICAL_H__ +#define __IS_FILE_IDENTICAL_H__ + +bool is_file_identical(char *filename1, char *filename2); +bool is_gdid_file_identical(gd_id_ptr_t fid, char *filename, int4 filelen); +#ifdef VMS +void set_gdid_from_file(gd_id_ptr_t fileid, char *filename, int4 filelen); +bool is_gdid_gdid_identical(gd_id_ptr_t fid_1, gd_id_ptr_t fid_2); +#elif defined(UNIX) +#include "gtm_stat.h" +bool is_gdid_identical(gd_id_ptr_t fid1, gd_id_ptr_t fid2); +bool is_gdid_stat_identical(gd_id_ptr_t fid, struct stat *stat_buf); +void set_gdid_from_stat(gd_id_ptr_t fid, struct stat *stat_buf); +boolean_t filename_to_id(gd_id_ptr_t fid, char *filename); +#endif +#endif diff --git a/sr_port/is_ident.c b/sr_port/is_ident.c new file mode 100644 index 0000000..f751ce9 --- /dev/null +++ b/sr_port/is_ident.c @@ -0,0 +1,62 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "toktyp.h" + +LITREF char ctypetab[NUM_CHARS]; + +char is_ident(mstr *v) +{ + int4 y; + char *c, *c_top, ret; + signed char ch; + bool dig_start; + + ret = TRUE; + c = v->addr; + c_top = c + v->len; + if (!v->len || (ch = *c++) < 0) + ret = 0; + else + { + switch (y = ctypetab[ch]) + { + case TK_LOWER: + case TK_PERCENT: + case TK_UPPER: + case TK_DIGIT: + dig_start = y == TK_DIGIT; + for ( ; c < c_top; c++) + { + ch = *c; + if (ch < 0) + break; + y = ctypetab[ch]; + if (y != TK_DIGIT && dig_start) + break; + if (y != TK_DIGIT && y != TK_UPPER && y != TK_LOWER) + break; + } + if (c == c_top) + { /* we have an ident */ + ret = 1 + dig_start; + } + else + { ret = 0; + } + break; + default: + ret = 0; + } + } + return ret; +} diff --git a/sr_port/is_proc_alive.h b/sr_port/is_proc_alive.h new file mode 100644 index 0000000..3f0496a --- /dev/null +++ b/sr_port/is_proc_alive.h @@ -0,0 +1,21 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef IS_PROC_ALIVE_INCLUDED +#define IS_PROC_ALIVE_INCLUDED + +#ifdef VMS +bool is_proc_alive(uint4 pid, uint4 imagecnt); +#elif defined(UNIX) +bool is_proc_alive(int4 pid, int4 imagecnt); +#endif + +#endif /* IS_PROC_ALIVE_INCLUDED */ diff --git a/sr_port/jfh_from_jnl_info.c b/sr_port/jfh_from_jnl_info.c new file mode 100644 index 0000000..9bce49a --- /dev/null +++ b/sr_port/jfh_from_jnl_info.c @@ -0,0 +1,78 @@ +/**************************************************************** + * * + * Copyright 2003, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gtm_time.h" + +#include "gdsroot.h" +#include "gdsbt.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "jnl.h" +#ifdef GTM_CRYPT +#include "gtmcrypt.h" +#endif +GBLREF jnl_process_vector *prc_vec; +GBLREF jnl_gbls_t jgbl; + +void jfh_from_jnl_info (jnl_create_info *info, jnl_file_header *header) +{ + trans_num db_tn; + + /**** We will write journal file header, epoch and eof in order ****/ + /* Write the file header */ + memset((char *)header, 0, JNL_HDR_LEN); /* note: In Unix, this means we 0-fill 2K REAL_JNL_HDR_LEN + 62K padding */ + memcpy(header->label, JNL_LABEL_TEXT, STR_LIT_LEN(JNL_LABEL_TEXT)); + header->is_little_endian = GTM_IS_LITTLE_ENDIAN; + assert(NULL != prc_vec); + JNL_WHOLE_FROM_SHORT_TIME(prc_vec->jpv_time, jgbl.gbl_jrec_time); + memcpy(&header->who_created, (unsigned char*)prc_vec, SIZEOF(jnl_process_vector)); + memcpy(&header->who_opened, (unsigned char*)prc_vec, SIZEOF(jnl_process_vector)); + /* EPOCHs are written unconditionally in Unix while they are written only for BEFORE_IMAGE in VMS */ + if (JNL_HAS_EPOCH(info)) + header->end_of_data = JNL_HDR_LEN + PINI_RECLEN + EPOCH_RECLEN + PFIN_RECLEN; + else + header->end_of_data = JNL_HDR_LEN + PINI_RECLEN + PFIN_RECLEN; + header->max_jrec_len = info->max_jrec_len; + header->bov_timestamp = jgbl.gbl_jrec_time; + header->eov_timestamp = jgbl.gbl_jrec_time; + db_tn = info->csd->trans_hist.curr_tn; + header->bov_tn = db_tn; + header->eov_tn = db_tn; + header->before_images = info->before_images; + /* Note that in case of MUPIP JOURNAL -ROLLBACK, we need to set header->repl_state to repl_open although replication + * is currently not ON in the database. This is so future ROLLBACKs know this journal is replication enabled. + */ + header->repl_state = jgbl.mur_rollback ? repl_open : info->repl_state; + header->data_file_name_length = info->fn_len; + memcpy(header->data_file_name, info->fn, info->fn_len); + header->data_file_name[info->fn_len] = '\0'; + header->alignsize = info->alignsize; + header->autoswitchlimit = info->autoswitchlimit; + header->epoch_interval = info->epoch_interval; + header->start_seqno = info->reg_seqno; + header->end_seqno = info->reg_seqno; + header->prev_jnl_file_name_length = info->prev_jnl_len; ; + memcpy(header->prev_jnl_file_name, info->prev_jnl, info->prev_jnl_len); + header->prev_jnl_file_name[info->prev_jnl_len] = '\0'; + assert(JNL_ALLOC_MIN <= info->alloc); + header->jnl_alq = info->alloc; + header->virtual_size = info->alloc; + header->jnl_deq = info->extend; + header->checksum = info->checksum; + GTMCRYPT_ONLY( + GTMCRYPT_COPY_HASH(info, header); + ) +} diff --git a/sr_port/jmp_opto.c b/sr_port/jmp_opto.c new file mode 100644 index 0000000..9ea2a47 --- /dev/null +++ b/sr_port/jmp_opto.c @@ -0,0 +1,350 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" + +#include "compiler.h" +#include "opcode.h" +#include "mdq.h" +#include "jmp_opto.h" +#include "gtmdbglvl.h" +#include "cdbg_dump.h" + +#define JOPT_NO_OPT 1 +#define JOPT_REP_JMP 2 +#define JOPT_REF_NXT_TRP 3 + +#define PTR_NOT_DEFINED 0 +#define IND_NOT_DEFINED ((unsigned char)-2) +#define NO_ENTRY ((unsigned char)-1) + +#define NUM_JO_TBL_ELE 11 +typedef struct +{ unsigned int opcode; + unsigned int index; + unsigned int opto_flag[NUM_JO_TBL_ELE]; +}jump_opto_struct; + +LITREF octabstruct oc_tab[]; /* op-code table */ +GBLREF triple t_orig; /* head of triples */ +GBLREF uint4 gtmDebugLevel; +const static readonly jump_opto_struct jump_opto_table[NUM_JO_TBL_ELE] = +{ + { OC_JMP, /* opcode */ + 0, /* index */ + { + JOPT_REP_JMP, /* OC_JMP */ JOPT_NO_OPT, /* OC_JMPTSET */ + JOPT_NO_OPT, /* OC_JMPTCLR */ JOPT_NO_OPT, /* OC_JMPEQU */ + JOPT_NO_OPT, /* OC_JMPNEQ */ JOPT_NO_OPT, /* OC_JMPGTR */ + JOPT_NO_OPT, /* OC_JMPLEQ */ JOPT_NO_OPT, /* OC_JMPLSS */ + JOPT_NO_OPT, /* OC_JMPGEQ */ JOPT_NO_OPT, /* HALT */ + JOPT_NO_OPT /* RET */ + } + }, + { OC_JMPTSET, /* opcode */ + 1, /* index */ + { + JOPT_REP_JMP, /* OC_JMP */ JOPT_REP_JMP, /* OC_JMPTSET */ + JOPT_REF_NXT_TRP, /* OC_JMPTCLR */ JOPT_NO_OPT, /* OC_JMPEQU */ + JOPT_NO_OPT, /* OC_JMPNEQ */ JOPT_NO_OPT, /* OC_JMPGTR */ + JOPT_NO_OPT, /* OC_JMPLEQ */ JOPT_NO_OPT, /* OC_JMPLSS */ + JOPT_NO_OPT, /* OC_JMPGEQ */ JOPT_NO_OPT, /* HALT */ + JOPT_NO_OPT /* RET */ + } + }, + { OC_JMPTCLR, /* opcode */ + 2, /* index */ + { + JOPT_REP_JMP, /* OC_JMP */ JOPT_REF_NXT_TRP, /* OC_JMPTSET */ + JOPT_REP_JMP, /* OC_JMPTCLR */ JOPT_NO_OPT, /* OC_JMPEQU */ + JOPT_NO_OPT, /* OC_JMPNEQ */ JOPT_NO_OPT, /* OC_JMPGTR */ + JOPT_NO_OPT, /* OC_JMPLEQ */ JOPT_NO_OPT, /* OC_JMPLSS */ + JOPT_NO_OPT, /* OC_JMPGEQ */ JOPT_NO_OPT, /* HALT */ + JOPT_NO_OPT /* RET */ + } + }, + { OC_JMPEQU, /* opcode */ + 3, /* index */ + { + JOPT_REP_JMP, /* OC_JMP */ JOPT_NO_OPT, /* OC_JMPTSET */ + JOPT_NO_OPT, /* OC_JMPTCLR */ JOPT_REP_JMP, /* OC_JMPEQU */ + JOPT_REF_NXT_TRP, /* OC_JMPNEQ */ JOPT_REF_NXT_TRP, /* OC_JMPGTR */ + JOPT_NO_OPT, /* OC_JMPLEQ */ JOPT_REF_NXT_TRP, /* OC_JMPLSS */ + JOPT_NO_OPT, /* OC_JMPGEQ */ JOPT_NO_OPT, /* HALT */ + JOPT_NO_OPT /* RET */ + } + }, + { OC_JMPNEQ, /* opcode */ + 4, /* index */ + { + JOPT_REP_JMP, /* OC_JMP */ JOPT_NO_OPT, /* OC_JMPTSET */ + JOPT_NO_OPT, /* OC_JMPTCLR */ JOPT_REF_NXT_TRP, /* OC_JMPEQU */ + JOPT_REP_JMP, /* OC_JMPNEQ */ JOPT_NO_OPT, /* OC_JMPGTR */ + JOPT_NO_OPT, /* OC_JMPLEQ */ JOPT_NO_OPT, /* OC_JMPLSS */ + JOPT_NO_OPT, /* OC_JMPGEQ */ JOPT_NO_OPT, /* HALT */ + JOPT_NO_OPT /* RET */ + } + }, + { OC_JMPGTR, /* opcode */ + 5, /* index */ + { + JOPT_REP_JMP, /* OC_JMP */ JOPT_NO_OPT, /* OC_JMPTSET */ + JOPT_NO_OPT, /* OC_JMPTCLR */ JOPT_REF_NXT_TRP, /* OC_JMPEQU */ + JOPT_REP_JMP, /* OC_JMPNEQ */ JOPT_REP_JMP, /* OC_JMPGTR */ + JOPT_REF_NXT_TRP, /* OC_JMPLEQ */ JOPT_REF_NXT_TRP, /* OC_JMPLSS */ + JOPT_NO_OPT, /* OC_JMPGEQ */ JOPT_NO_OPT, /* HALT */ + JOPT_NO_OPT /* RET */ + } + }, + { OC_JMPLEQ, /* opcode */ + 6, /* index */ + { + JOPT_REP_JMP, /* OC_JMP */ JOPT_NO_OPT, /* OC_JMPTSET */ + JOPT_NO_OPT, /* OC_JMPTCLR */ JOPT_NO_OPT, /* OC_JMPEQU */ + JOPT_NO_OPT, /* OC_JMPNEQ */ JOPT_REF_NXT_TRP, /* OC_JMPGTR */ + JOPT_REP_JMP, /* OC_JMPLEQ */ JOPT_NO_OPT, /* OC_JMPLSS */ + JOPT_NO_OPT, /* OC_JMPGEQ */ JOPT_NO_OPT, /* HALT */ + JOPT_NO_OPT /* RET */ + } + }, + { OC_JMPLSS, /* opcode */ + 7, /* index */ + { + JOPT_REP_JMP, /* OC_JMP */ JOPT_NO_OPT, /* OC_JMPTSET */ + JOPT_NO_OPT, /* OC_JMPTCLR */ JOPT_REF_NXT_TRP, /* OC_JMPEQU */ + JOPT_REP_JMP, /* OC_JMPNEQ */ JOPT_REF_NXT_TRP, /* OC_JMPGTR */ + JOPT_REP_JMP, /* OC_JMPLEQ */ JOPT_REP_JMP, /* OC_JMPLSS */ + JOPT_REF_NXT_TRP, /* OC_JMPGEQ */ JOPT_NO_OPT, /* HALT */ + JOPT_NO_OPT /* RET */ + } + }, + { OC_JMPGEQ, /* opcode */ + 8, /* index */ + { + JOPT_REP_JMP, /* OC_JMP */ JOPT_NO_OPT, /* OC_JMPTSET */ + JOPT_NO_OPT, /* OC_JMPTCLR */ JOPT_NO_OPT, /* OC_JMPEQU */ + JOPT_NO_OPT, /* OC_JMPNEQ */ JOPT_NO_OPT, /* OC_JMPGTR */ + JOPT_NO_OPT, /* OC_JMPLEQ */ JOPT_REF_NXT_TRP, /* OC_JMPLSS */ + JOPT_REP_JMP, /* OC_JMPGEQ */ JOPT_NO_OPT, /* HALT */ + JOPT_NO_OPT /* RET */ + } + }, + { OC_HALT, /* opcode */ + 9, /* index */ + { + JOPT_NO_OPT, /* OC_JMP */ JOPT_NO_OPT, /* OC_JMPTSET */ + JOPT_NO_OPT, /* OC_JMPTCLR */ JOPT_NO_OPT, /* OC_JMPEQU */ + JOPT_NO_OPT, /* OC_JMPNEQ */ JOPT_NO_OPT, /* OC_JMPGTR */ + JOPT_NO_OPT, /* OC_JMPLEQ */ JOPT_NO_OPT, /* OC_JMPLSS */ + JOPT_NO_OPT, /* OC_JMPGEQ */ JOPT_NO_OPT, /* HALT */ + JOPT_NO_OPT /* RET */ + } + }, + { OC_RET, /* opcode */ + 10, /* index */ + { + JOPT_NO_OPT, /* OC_JMP */ JOPT_NO_OPT, /* OC_JMPTSET */ + JOPT_NO_OPT, /* OC_JMPTCLR */ JOPT_NO_OPT, /* OC_JMPEQU */ + JOPT_NO_OPT, /* OC_JMPNEQ */ JOPT_NO_OPT, /* OC_JMPGTR */ + JOPT_NO_OPT, /* OC_JMPLEQ */ JOPT_NO_OPT, /* OC_JMPLSS */ + JOPT_NO_OPT, /* OC_JMPGEQ */ JOPT_NO_OPT, /* HALT */ + JOPT_NO_OPT /* RET */ + } + } +}; +static unsigned int *jo_ptr_ray[OPCODE_COUNT]; +static unsigned int jo_ind_ray[OPCODE_COUNT]; + +static void jo_get_ptrs(unsigned int op) +{ + const jump_opto_struct *j, *j_top; + + for (j = &jump_opto_table[0], j_top = j + NUM_JO_TBL_ELE; j < j_top; j++) + { + if (j->opcode == op) + { + jo_ind_ray[op] = j->index; + jo_ptr_ray[op] = (unsigned int *)&j->opto_flag[0]; + return; + } + } + jo_ind_ray[op] = NO_ENTRY; + jo_ptr_ray[op] = (unsigned int *)NO_ENTRY; +} + +const static readonly oprtype null_operand; + +/************************************************************************************************************ + NOTE: We may which to modify the lookup method at some point in the future. B. Shear suggests nested switch + statements. Another option is to do the lookup each time. +***********************************************************************************************************/ + +void jmp_opto(void) +{ + unsigned int i, *p, **clrp1, **clrtop1, *clrp2, *clrtop2; + tbp *b; + triple *cur_trip, *terminal_trip, *ref_trip, *jump_trip, *next_trip, *ct; + void get_jo_ptrs(); + +# ifdef DEBUG + /* If debug and compiler debugging is enabled, run through the triples again to show where we are jus + * before we modify them. + */ + if (gtmDebugLevel & GDL_DebugCompiler) + { + PRINTF(" \n\n\n\n************************************ Begin jmp_opto scan *****************************\n"); + } +# endif + + for (clrp1 = &jo_ptr_ray[0], clrtop1 = clrp1 + OPCODE_COUNT; clrp1 < clrtop1; clrp1++) + *clrp1 = (unsigned int *)NO_ENTRY; + for (clrp2 = &jo_ind_ray[0], clrtop2 = clrp2 + OPCODE_COUNT; clrp2 < clrtop2; clrp2++) + *clrp2 = NO_ENTRY; + + dqloop(&t_orig, exorder, cur_trip) + { + if (OC_GVSAVTARG == cur_trip->opcode) + { + for (next_trip = cur_trip->exorder.fl; + oc_tab[next_trip->opcode].octype & OCT_CGSKIP; + next_trip = next_trip->exorder.fl) + ; + + if (OC_GVRECTARG == next_trip->opcode + && next_trip->operand[0].oprval.tref == cur_trip + && next_trip->jmplist.que.fl == &(next_trip->jmplist)) + { + COMPDBG(PRINTF("jmp_opto: NOOPing OC_GVRECTARG opcode at triple addres 0x"lvaddr"\n", next_trip);); + next_trip->opcode = OC_NOOP; + next_trip->operand[0].oprclass = next_trip->operand[1].oprclass = 0; + } + continue; + } + if ((oc_tab[cur_trip->opcode].octype & OCT_JUMP) && + (OC_CALL != cur_trip->opcode) && (OC_CALLSP != cur_trip->opcode)) + { + assert(OPCODE_COUNT > cur_trip->opcode); + if (PTR_NOT_DEFINED == (p = jo_ptr_ray[cur_trip->opcode])) /* note assignment */ + { + jo_get_ptrs(cur_trip->opcode); + p = jo_ptr_ray[cur_trip->opcode]; + } + assert(TJMP_REF == cur_trip->operand[0].oprclass); + jump_trip = cur_trip->operand[0].oprval.tref; + if (IND_NOT_DEFINED == (i = jo_ind_ray[jump_trip->opcode])) /* note assignment */ + { + jo_get_ptrs(jump_trip->opcode); + i = jo_ind_ray[jump_trip->opcode]; + } + + while ((IND_NOT_DEFINED != i) && (NO_ENTRY != i)) + { + switch(p[i]) + { + case JOPT_NO_OPT: + i = NO_ENTRY; + break; + case JOPT_REF_NXT_TRP: + if (cur_trip->src.line == jump_trip->src.line) + { + dqloop(&jump_trip->jmplist, que, b) + { + if (b->bpt = cur_trip) + { + dqdel(b, que); + break; + } + } + dqins(&jump_trip->exorder.fl->jmplist, que, b); + + cur_trip->operand[0].oprval.tref = jump_trip->exorder.fl; + jump_trip = cur_trip->operand[0].oprval.tref; + if (IND_NOT_DEFINED == (i = jo_ind_ray[jump_trip->opcode])) /* assignmnt */ + { + jo_get_ptrs(jump_trip->opcode); + i = jo_ind_ray[jump_trip->opcode]; + } + COMPDBG(PRINTF("jmp_opto: JOPT_REF_NXT_TRP optimization on triple " + "0x"lvaddr"\n", cur_trip);); + } else + i = NO_ENTRY; + break; + case JOPT_REP_JMP: + if (cur_trip->src.line == jump_trip->src.line) + { + assert(TJMP_REF == jump_trip->operand[0].oprclass); + dqloop(&jump_trip->jmplist, que, b) + { + if (b->bpt = cur_trip) + { + dqdel(b, que); + break; + } + } + dqins(&jump_trip->operand[0].oprval.tref->jmplist, que, b); + + cur_trip->operand[0] = jump_trip->operand[0]; + jump_trip = cur_trip->operand[0].oprval.tref; + if (IND_NOT_DEFINED == (i = jo_ind_ray[jump_trip->opcode])) /* assgnmnt */ + { + jo_get_ptrs(jump_trip->opcode); + i = jo_ind_ray[jump_trip->opcode]; + } + COMPDBG(PRINTF("jmp_opto: JOPT_REP_JMP optimization on triple " + "0x"lvaddr"\n", cur_trip);); + } else + i = NO_ENTRY; + break; + default: + GTMASSERT; + break; + } /* switch */ + } /* while */ + + terminal_trip = cur_trip->exorder.fl; + while ((oc_tab[cur_trip->opcode].octype & OCT_JUMP) + && (OC_CALL != cur_trip->opcode) && (OC_CALLSP != cur_trip->opcode) + && (TJMP_REF == cur_trip->operand[0].oprclass)) + { + for ((ref_trip = cur_trip->operand[0].oprval.tref); + (oc_tab[ref_trip->opcode].octype & OCT_CGSKIP); + (ref_trip = ref_trip->exorder.fl)) + ; + + if (ref_trip == terminal_trip) + { + cur_trip->opcode = OC_NOOP; + cur_trip->operand[0] = null_operand; + COMPDBG(PRINTF("jmp_opto: Triple removed at address 0x"lvaddr"\n", cur_trip);); + cur_trip = cur_trip->exorder.bl; + } else + break; + } + cur_trip = terminal_trip->exorder.bl; + } /* if */ + } /* dqloop */ + +# ifdef DEBUG + /* If debug and compiler debugging is enabled, run through the triples again to show what we + * have done to them.. + */ + if (gtmDebugLevel & GDL_DebugCompiler) + { + dqloop(&t_orig, exorder, ct) + { + PRINTF("\n ************************ Triple Start **********************\n"); + cdbg_dump_triple(ct, 0); + } + } +# endif +} diff --git a/sr_port/jmp_opto.h b/sr_port/jmp_opto.h new file mode 100644 index 0000000..ce33ed1 --- /dev/null +++ b/sr_port/jmp_opto.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef JMP_OPTO_INCLUDED +#define JMP_OPTO_INCLUDED + +void jmp_opto(void); + +#endif /* JMP_OPTO_INCLUDED */ diff --git a/sr_port/jnl.h b/sr_port/jnl.h new file mode 100644 index 0000000..8059a54 --- /dev/null +++ b/sr_port/jnl.h @@ -0,0 +1,1286 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#include "mdef.h" +#ifndef JNL_H_INCLUDED +#define JNL_H_INCLUDED + +#ifdef DEBUG +#include /* for offsetof macro (see OFFSETOF usage in assert below) */ +#endif + +#ifndef JNLSP_H_INCLUDED +#include "jnlsp.h" +#endif +#ifdef GTM_CRYPT +#include "gtmcrypt.h" +#endif + +#define TID_STR_SIZE 8 +#define JPV_LEN_NODE 16 +#define JPV_LEN_USER 12 +#define JPV_LEN_PRCNAM 16 +#define JPV_LEN_TERMINAL 15 + +/* Whenever JNL_LABEL_TEXT changes, also change the following + * 1) Update JNL_VER_THIS + * 2) Add REPL_JNL_Vxx enum to repl_jnl_t typedef AND Vxx_JNL_VER #define in repl_filter.h + * 3) Add an entry each to repl_filter_old2cur & repl_filter_cur2old arrays in repl_filter.c. + * If the FILTER format is also changing, then do the following as well + * 4) Add REPL_FILTER_Vxx enum to repl_filter_t typedef in repl_filter.h + * 5) Add/Edit IF_xTOy macros in repl_filter.h to transform from/to the NEW jnl format version only. + * Remove all entries that dont have the new jnl format in either the from or to part of the conversion. + * 6) Add/Edit prototype and implement functions jnl_xTOy() and jnl_yTOx() in repl_filter.c + * 7) Enhance repl_tr_endian_convert() to endian convert journal records from previous jnl formats to new format. + * This is similar to the jnl_xTOy() filter conversion functions except that lot of byte-swaps are needed. + * 8) Periodically determine if the size of the array repl_filter_old2cur is huge and if so trim support of + * rolling upgrade (using replication internal filters) for older GT.M versions/jnl-formats. + * This would mean bumping the macro JNL_VER_EARLIEST_REPL and examining all arrays that are defined + * using this macro and changing the entries in all those arrays accordingly (e.g. repl_filter_old2cur + * array currently assumes earliest supported version is V15 and hence has the function named IF_curTO15 + * which needs to change to say IF_curTO17 if the earliest supported version changes to V17 or so). + * + */ +#define JNL_LABEL_TEXT "GDSJNL21" /* see above comment paragraph for todos whenever this is changed */ +#define JNL_VER_THIS 21 +#define JNL_VER_EARLIEST_REPL 15 /* Replication filter support starts here GDSJNL15 = GT.M V4.4-002 */ +#define JRT_MAX_V15 JRT_AIMG /* Maximum jnl record type in GDSJNL15 that can be input to replication filter */ +#define JRT_MAX_V17 JRT_AIMG /* Maximum jnl record type in GDSJNL17 that can be input to replication filter. + * Actually JRT_TRIPLE is a higher record type than JRT_AIMG but it is only + * sent through the replication pipe and never seen by filter routines. + */ +#define JRT_MAX_V19 JRT_UZTWORM /* Maximum jnl record type in GDSJNL19 that can be input to replication filter */ +#define ALIGN_KEY 0xdeadbeef + +#ifdef UNIX +#define JNL_ALLOC_DEF 2048 +#define JNL_ALLOC_MIN 200 +#elif defined(VMS) +#define JNL_ALLOC_DEF 100 +#define JNL_ALLOC_MIN 10 +#endif + +/* JNL_BUFFER_MIN database block size / 512 + 1 */ +#define JNL_BUFFER_MAX 32768 /* # of 512-byte blocks = 16Mb journal buffer size */ + +/* JNL_EXTEND_DEF allocation size / 10 +#define JNL_EXTEND_DEF_PERC 0.1 +* Uncomment this section when code is ready to use extension = 10% of allocation +*/ + +#define JNL_EXTEND_MIN 0 +#ifdef UNIX +#define JNL_EXTEND_DEF 2048 +#else +#define JNL_EXTEND_DEF 100 +#endif +#define JNL_EXTEND_MAX 65535 +#define JNL_MIN_WRITE 32768 +#define JNL_MAX_WRITE 65536 +/* FE was changed to EB because, the bit pattern there seems to vary more than the one for "FE". + * Also a research in ELWOOD journal file showed that "EB" was one of the few patterns that had the least occurrences */ +#define JNL_REC_SUFFIX_CODE 0xEB + +/* In Unix, with sync_io, we do journal writes to disk at filesystem block size boundaries. + * In VMS, the writes are at 512-byte boundaries only. + */ +#ifdef UNIX +# define JNL_WRT_START_MODULUS(jb) jb->fs_block_size +#elif defined(VMS) +# define JNL_WRT_START_MODULUS(jb) 512 +#endif +#define JNL_WRT_START_MASK(jb) ~(JNL_WRT_START_MODULUS(jb) - 1) + +#define JNL_WRT_END_MODULUS 8 +#define JNL_WRT_END_MASK ~(JNL_WRT_END_MODULUS - 1) + +#ifdef UNIX +#define JNL_MIN_ALIGNSIZE (1 << 8) /* 256 disk blocks effectively 128K alignsize */ +#define JNL_DEF_ALIGNSIZE (1 << 11) /* 2048 disk blocks effectively 1M alignsize */ +#else +#define JNL_MIN_ALIGNSIZE (1 << 5) /* 32 disk blocks effectively 16K alignsize */ +#define JNL_DEF_ALIGNSIZE (1 << 7) /* 128 disk blocks effectively 64K alignsize */ +#endif +#define JNL_MAX_ALIGNSIZE (1 << 22) /* 4194304 disk blocks effectively 2G alignsize */ +#define JNL_REC_START_BNDRY 8 +#define MAX_LOGI_JNL_REC_SIZE (MAX_DB_BLK_SIZE) /* maximum logical journal record size */ +#define MAX_JNL_REC_SIZE (MAX_LOGI_JNL_REC_SIZE + DISK_BLOCK_SIZE) /* one more disk-block for PBLK record header/footer */ + +#ifdef GTM_TRIGGER +/* Define maximum size that $ZTWORMHOLE can be. Since $ZTWORMHOLE should be able to fit in a journal record and the + * minimum alignsize is 128K, we do not want it to go more than 128K (that way irrespective of whatever alignsize the user + * specifies for the journal file, $ZTWORMHOLE will fit in the journal record). Leaving a max of 512 bytes for the + * journal record prefix/suffix (32-byte overhead) and MIN_ALIGN_RECLEN (see comment in JNL_MAX_RECLEN macro for why + * this is needed) we allow for a max of 128K-512 bytes in $ZTWORMHOLE. + */ +#define MAX_ZTWORMHOLE_LEN (128 * 1024) +#define MAX_ZTWORMHOLE_SIZE (MAX_ZTWORMHOLE_LEN - 512) +#define MAX_ZTWORM_JREC_LEN (MAX_ZTWORMHOLE_LEN - MIN_ALIGN_RECLEN) +#endif + +#define MIN_YIELD_LIMIT 0 +#define MAX_YIELD_LIMIT 2048 +#define DEFAULT_YIELD_LIMIT 8 + +#ifdef UNIX +/* Have a minimum jnl-file-auto-switch-limit of 16 align boundaries (currently each align boundary is 128K) */ +#define JNL_AUTOSWITCHLIMIT_MIN (16 * JNL_MIN_ALIGNSIZE) +#define JNL_AUTOSWITCHLIMIT_DEF 8386560 /* Instead of 8388607 it is adjusted for default allocation = extension = 2048 */ +#else +/* Have a minimum jnl-file-auto-switch-limit of 128 align boundaries (currently each align boundary is 16K) */ +#define JNL_AUTOSWITCHLIMIT_MIN (128 * JNL_MIN_ALIGNSIZE) +#define JNL_AUTOSWITCHLIMIT_DEF 8388600 /* Instead of 8388607 it is adjusted for default allocation = extension = 100 */ +#endif + +/* options (4-bytes unsigned integer) to wcs_flu() (currently flush_hdr, write_epoch, sync_epoch) are bit-wise ored */ +#define WCSFLU_NONE 0 +#define WCSFLU_FLUSH_HDR 1 +#define WCSFLU_WRITE_EPOCH 2 +#define WCSFLU_SYNC_EPOCH 4 +#define WCSFLU_FSYNC_DB 8 /* Currently used only in Unix wcs_flu() */ +#define WCSFLU_IN_COMMIT 16 /* Set if caller is t_end or tp_tend. See wcs_flu for explanation of when this is set */ +#define WCSFLU_MSYNC_DB 32 /* Force a full msync if NO_MSYNC is defined. Currently used only in Unix wcs_flu(). */ + +/* options for error_on_jnl_file_lost */ +#define JNL_FILE_LOST_TURN_OFF 0 /* Turn off journaling. */ +#define JNL_FILE_LOST_ERRORS 1 /* Throw an rts_error. */ +#define MAX_JNL_FILE_LOST_OPT JNL_FILE_LOST_ERRORS + +/* EPOCHs are written unconditionally in Unix (assuming jnl is ON) while they are written only for BEFORE_IMAGE in VMS */ +#define JNL_HAS_EPOCH(jnlfile) UNIX_ONLY(TRUE) VMS_ONLY(jnlfile->before_images) + +#ifdef DEBUG +#define DEFAULT_EPOCH_INTERVAL_IN_SECONDS 30 /* exercise epoch-syncing code relatively more often in DBG */ +#else +#define DEFAULT_EPOCH_INTERVAL_IN_SECONDS 300 +#endif + +#define DEFAULT_EPOCH_INTERVAL SECOND2EPOCH_SECOND(DEFAULT_EPOCH_INTERVAL_IN_SECONDS) /* ***MUST*** include math.h for VMS */ + +#define MAX_EPOCH_INTERVAL 32767 /* in seconds. Amounts to nearly 10 hours. Don't want to keep db stale so long */ + +#define JNL_ENABLED(X) ((X)->jnl_state == jnl_open) /* If TRUE, journal records are to be written */ +#define JNL_ALLOWED(X) ((X)->jnl_state != jnl_notallowed) /* If TRUE, journaling is allowed for the file */ +#define REPL_ENABLED(X) ((X)->repl_state == repl_open) /* If TRUE, replication records are to be written */ +#define REPL_WAS_ENABLED(X) ((X)->repl_state == repl_was_open) /* If TRUE, replication is now closed, but was open earlier */ + /* In this state, replication records are not written */ +#define REPL_ALLOWED(X) ((X)->repl_state != repl_closed) /* If TRUE, replication records are/were written */ + +/* Logical records should be written if journaling is enabled in the region OR if replication state is WAS_ON (repl_was_open). + * In the former case, the journal records will be written to the journal pool, journal buffer and journal file. + * In the latter case, the journal records will be written to the journal pool but not to the journal buffer and journal file. + * All code that generates logical journal records should use the below macro instead of JNL_ENABLED macro. + * Note that replication does not care about non-logical records (PBLK/AIMG/INCTN etc.) and hence code that generates them does + * not need to (and should not) use this macro. + */ +#define JNL_WRITE_LOGICAL_RECS(X) (JNL_ENABLED(X) || REPL_WAS_ENABLED(X)) + +/* The following macro should be used to invoke the function "jnl_write" for any logical record. This macro + * checks if journaling is enabled and if so invokes "jnl_write" else it invokes "jnl_write_poolonly" which + * writes only to the journal pool. + */ +#define JNL_WRITE_APPROPRIATE(CSA, JPC, RECTYPE, JREC, BLKPTR, JFB) \ +{ \ + assert(JNL_ENABLED(CSA) || REPL_WAS_ENABLED(CSA)); \ + assert((NULL == JFB) || (RECTYPE == ((jnl_record *)(((jnl_format_buffer *)JFB)->buff))->prefix.jrec_type)); \ + if (JNL_ENABLED(CSA)) \ + jnl_write(JPC, RECTYPE, JREC, BLKPTR, JFB); /* write to jnlbuffer, jnlfile, jnlpool */ \ + else \ + jnl_write_poolonly(JPC, RECTYPE, JREC, JFB); /* write to jnlpool only */ \ +} + +#define MUEXTRACT_TYPE(A) (((A)[0]-'0')*10 + ((A)[1]-'0')) /* A is a character pointer */ + +#define PADDED PADDING + +#ifdef BIGENDIAN +#define THREE_LOW_BYTES(x) ((uchar_ptr_t)((uchar_ptr_t)&x + 1)) +#else +#define THREE_LOW_BYTES(x) ((uchar_ptr_t)(&x)) +#endif +#define EXTTIME(S) extract_len = exttime(S, murgbl.extr_buff, extract_len) + +/* This macro should be used to initialize jgbl.gbl_jrec_time to the system time. The reason is that it does additional checks. */ +#define SET_GBL_JREC_TIME \ +{ \ + assert(!jgbl.dont_reset_gbl_jrec_time); \ + JNL_SHORT_TIME(jgbl.gbl_jrec_time); \ +} + +/* This macro ensures that journal records are written in non-decreasing time order in each journal file. + * It is passed the time field to adjust and a pointer to the journal buffer of the region. + * The journal buffer holds the timestamp of the most recently written journal record. + */ +#define ADJUST_GBL_JREC_TIME(jgbl, jbp) \ +{ \ + if (jgbl.gbl_jrec_time < jbp->prev_jrec_time) \ + { \ + assert(!jgbl.dont_reset_gbl_jrec_time); \ + jgbl.gbl_jrec_time = jbp->prev_jrec_time; \ + } \ +} + +/* Check if journal file is usable from the fields in the file header. + * Currently, the fields tested are LABEL and ENDIANNESS. + */ +#define JNL_HDR_ENDIAN_OFFSET 8 + +#define CHECK_JNL_FILE_IS_USABLE(JFH, STATUS, DO_GTMPUTMSG, JNL_FN_LEN, JNL_FN) \ +{ \ + boolean_t check_failed = FALSE; \ + uint4 lcl_status; \ + \ + error_def(ERR_JNLBADLABEL); \ + error_def(ERR_JNLENDIANLITTLE); \ + error_def(ERR_JNLENDIANBIG); \ + \ + assert(JNL_HDR_ENDIAN_OFFSET == OFFSETOF(jnl_file_header, is_little_endian)); \ + if (0 != MEMCMP_LIT(JFH->label, JNL_LABEL_TEXT)) \ + { \ + lcl_status = ERR_JNLBADLABEL; \ + check_failed = TRUE; \ + } \ + BIGENDIAN_ONLY( \ + else if (JFH->is_little_endian) \ + { \ + lcl_status = ERR_JNLENDIANLITTLE; \ + check_failed = TRUE; \ + } \ + ) \ + LITTLEENDIAN_ONLY( \ + else if (!JFH->is_little_endian) \ + { \ + lcl_status = ERR_JNLENDIANBIG; \ + check_failed = TRUE; \ + } \ + ) \ + /* Currently, we can do one gtm_putmsg for any of the above 3 error messages \ + * because all of them have a fao count of 2 and expect jnl_fn_len and jnl_fn \ + * as arguments. If a new error gets added and has a different fao format, \ + * then the below gtm_putmsg has to be done differently based on that error. \ + */ \ + if (check_failed) \ + { \ + STATUS = lcl_status; \ + if (DO_GTMPUTMSG) \ + gtm_putmsg(VARLSTCNT(4) lcl_status, 2, JNL_FN_LEN, JNL_FN); \ + } \ +} + +/* Token generation used in non-replicated journaled environment. Note the assumption here + that SIZEOF(token_split_t) == SIZEOF(token_build) which will be asserted in gvcst_init(). + The TOKEN_SET macro below depends on this assumption. +*/ +typedef struct token_split_t_struct +{ +# ifdef BIGENDIAN + uint4 process_id; + uint4 local_tn; +# else + uint4 local_tn; + uint4 process_id; +# endif +} token_split_t; + +typedef union +{ + token_split_t t_piece; + token_num token; +} token_build; + +/* To assist in setting token value, the following macro is supplied to handle the two token parts */ +#define TOKEN_SET(BASE, TN, PID) (((token_build_ptr_t)(BASE))->t_piece.local_tn = (uint4)(TN), \ + ((token_build_ptr_t)(BASE))->t_piece.process_id = (PID)) + +enum jpv_types +{ + CURR_JPV = 0, + ORIG_JPV, + JPV_COUNT +}; +/* Note we have two process verctors now for a pini record */ +typedef struct jnl_process_vector_struct /* name needed since this is used in cmmdef.h for "pvec" member */ +{ + uint4 jpv_pid; /* Process id */ + int4 jpv_image_count; /* Image activations [VMS only] */ + jnl_proc_time jpv_time; /* Timestamp of the process genarating this. + (This could be different than the journal record timestamp) */ + jnl_proc_time jpv_login_time; /* Used for process initialization time */ + char jpv_node[JPV_LEN_NODE], /* Node name */ + jpv_user[JPV_LEN_USER], /* User name */ + jpv_prcnam[JPV_LEN_PRCNAM], /* Process name [VMS only] */ + jpv_terminal[JPV_LEN_TERMINAL]; /* Login terminal */ + unsigned char jpv_mode; /* a la JPI$_MODE [VMS only] */ + int4 filler; + /* SIZEOF(jnl_process_vector) must be a multiple of SIZEOF(int4) */ +} jnl_process_vector; + +enum pini_rec_stat +{ + IGNORE_PROC = 0, + ACTIVE_PROC = 1, + FINISHED_PROC = 2, + BROKEN_PROC = 4 +}; + +typedef struct pini_list +{ + uint4 pini_addr; + uint4 new_pini_addr; /* used in forward phase of recovery */ + jnl_process_vector jpv; /* CURR_JPV. Current process's JPV. For GTCM server we also use this. */ + jnl_process_vector origjpv; /* ORIG_JPV. Used for GTCM client only */ + enum pini_rec_stat state; /* used for show qualifier */ +} pini_list_struct; + +enum jnl_record_type +{ +#define JNL_TABLE_ENTRY(rectype, extract_rtn, label, update, fixed_size, is_replicated) rectype, +#include "jnl_rec_table.h" +#undef JNL_TABLE_ENTRY + + JRT_RECTYPES /* Total number of JOURNAL record types */ +}; + +#include "jnl_typedef.h" + +enum jnl_state_codes +{ + jnl_notallowed, + jnl_closed, + jnl_open +}; + +enum repl_state_codes +{ + repl_closed, /* region not replicated, no records are written */ + repl_open, /* region is replicated, and records are written */ + repl_was_open /* region is currently not replicated, but it was earlier; jnl_file_lost() changes open to was_open */ +}; + +typedef struct +{ + trans_num eov_tn; /* curr_tn is saved as eov_tn by jnl_write_epoch. Used by recover/rollback */ + volatile trans_num epoch_tn; /* Transaction number for current epoch */ + seq_num end_seqno; /* reg_seqno saved by jnl_write_epoch. Used by recover/rollback */ + int4 min_write_size, /* if unwritten data gets to this size, write it */ + max_write_size, /* maximum size of any single write */ + size; /* buffer size */ + int4 epoch_interval; /* Time between successive epochs in epoch-seconds */ + boolean_t before_images; /* If TRUE, before-image processing is enabled */ + /* end not volatile QUAD */ + uintszofptr_t buff_off; /* relative offset to filesystem-block-size aligned buffer start */ + volatile int4 free; /* relative index of first byte to write in buffer */ + volatile uint4 freeaddr, /* virtual on-disk address which will correspond to free, when it is written */ + end_of_data, /* Synched offset updated by jnl_write_epoch. Used by recover/rollback */ + filesize; /* highest virtual address available in the file (units in disk-blocks) */ + /* end mainline QUAD */ + volatile int4 blocked; + volatile uint4 fsync_dskaddr; /* dskaddr upto which fsync is done */ + volatile int4 dsk; /* relative index of 1st byte to write to disk; + * if free == dsk, buffer is empty */ + volatile int4 wrtsize; /* size of write in progress */ + volatile uint4 dskaddr, /* virtual on-disk address corresponding to dsk */ + now_writer, /* current owner of io_in_prog (VMS-only) */ + image_count; /* for VMS is_proc_alive */ + volatile struct /* must be at least word aligned for memory coherency */ + { + short cond; + unsigned short length; + int4 dev_specific; + } iosb; + /* alignsize is removed and log2_of_alignsize introduced */ + uint4 log2_of_alignsize; /* Ceiling of log2(alignsize) */ + jnl_tm_t eov_timestamp; /* jgbl.gbl_jrec_time saved by jnl_write_epoch. Used by recover/rollback */ + uint4 cycle; /* shared copy of the number of the current journal file generation */ + volatile int4 qiocnt, /* Number of qio's issued */ + bytcnt, /* Number of bytes written */ + errcnt, /* Number of errors during writing */ + reccnt[JRT_RECTYPES]; /* Number of records written per opcode */ + int filler_align[35 - JRT_RECTYPES]; /* So buff below starts on even (QW) keel */ + /* Note the above filler will fail if JRT_RECTYPES grows beyond 31 elements and give compiler warning in VMS + * if JRT_RECTYPES equals 31. In that case, change the start num to the next odd number above MAX(31,JRT_RECTYPES). + */ + volatile jnl_tm_t prev_jrec_time; /* to ensure that time never decreases across successive jnl records */ + volatile int4 free_update_pid; /* pid that is updating jb->free and jb->freeaddr */ + volatile uint4 next_epoch_time; /* Time when next epoch is to be written (in epoch-seconds) */ + volatile boolean_t need_db_fsync; /* need an fsync of the db file */ + volatile int4 io_in_prog; /* VMS only: write in progress indicator (NOTE: must manipulate + only with interlocked instructions */ + uint4 enospc_errcnt; /* number of times jb->errcnt was last incremented due to ENOSPC error + * when writing to this journal file */ + uint4 max_jrec_len; /* copy of max_jrec_len from journal file header */ + uint4 fs_block_size; /* underlying journal file system block size; + * primarily used in Unix, 512 in VMS */ + /* CACHELINE_PAD macros provide spacing between the following latches so that they do + not interfere with each other which can happen if they fall in the same data cacheline + of a processor. + */ + CACHELINE_PAD(SIZEOF(global_latch_t), 0) /* start next latch at a different cacheline than previous fields */ + global_latch_t io_in_prog_latch; /* UNIX only: write in progress indicator */ + CACHELINE_PAD(SIZEOF(global_latch_t), 1) /* pad enough space so next latch falls in different cacheline */ + global_latch_t fsync_in_prog_latch; /* fsync in progress indicator */ + CACHELINE_PAD(SIZEOF(global_latch_t), 2) /* pad enough space so next non-filler byte falls in different cacheline */ + /**********************************************************************************************/ + /* Important: must keep header structure quadword (8 byte) aligned for buffers used in QIO's */ + /**********************************************************************************************/ + unsigned char buff[1]; /* Actually buff[size] */ +} jnl_buffer; + +#define FIX_NONZERO_FREE_UPDATE_PID(csa, jbp) \ +{ \ + assert(csa->now_crit); /* hold crit before manipulating freeaddr/free */ \ + assert(jbp->free_update_pid); \ + UNIX_ONLY(assert(!is_proc_alive(jbp->free_update_pid, 0));) \ + VMS_ONLY(assert(FALSE);) /* secshr_db_clnup should have cleaned up this field even in case of STOP/ID */ \ + if ((jbp->freeaddr % jbp->size) != jbp->free) \ + { /* Previous process in jnl_write got killed after incrementing freeaddr but before incrementing \ + * free. Recalculate jbp->free based on current value of jbp->freeaddr. */ \ + jbp->free = jbp->freeaddr % jbp->size; \ + jbp->free_update_pid = 0; \ + } \ + DBG_CHECK_JNL_BUFF_FREEADDR(jbp); \ +} + +#define DBG_CHECK_JNL_BUFF_FREEADDR(jbp) \ +{ \ + assert((jbp->freeaddr % jbp->size) == jbp->free); \ + assert((jbp->freeaddr >= jbp->dskaddr) \ + || (gtm_white_box_test_case_enabled \ + && (WBTEST_JNL_FILE_LOST_DSKADDR == gtm_white_box_test_case_number))); \ +} + +#ifdef DB64 +# ifdef __osf__ +# pragma pointer_size(save) +# pragma pointer_size(long) +# else +# error UNSUPPORTED PLATFORM +# endif +#endif + +typedef jnl_buffer *jnl_buffer_ptr_t; +typedef token_build *token_build_ptr_t; + +#ifdef DB64 +# ifdef __osf__ +# pragma pointer_size(restore) +# endif +#endif + +typedef struct jnl_private_control_struct +{ + jnl_buffer_ptr_t jnl_buff; /* pointer to shared memory */ + gd_region *region; /* backpointer to region head */ + fd_type channel, /* output channel, aka fd in UNIX */ + old_channel; /* VMS only - for dealing with deferred deassign */ + gd_id fileid; /* currently initialized and used only by source-server */ + vms_lock_sb *jnllsb; /* VMS only */ + uint4 pini_addr, /* virtual on-disk address for JRT_PINI record, if journaling */ + new_freeaddr; + int4 temp_free; /* M Temp copy of free relative index until full write done */ + double filler_q0; /* reset QUAD end mainline */ + int4 new_dsk; /* A VMS only */ + uint4 new_dskaddr; /* A VMS only */ + int4 status; /* A for error reporting */ + volatile boolean_t dsk_update_inprog; /* A VMS only */ + volatile boolean_t qio_active; /* jnl buffer write in progress in THIS process (recursion indicator) */ + boolean_t fd_mismatch; /* TRUE when jpc->channel does not point to the active journal */ + volatile boolean_t sync_io; /* TRUE if the process is using O_SYNC/O_DSYNC for this jnl (UNIX) */ + /* TRUE if writers open NOCACHING to bypass XFC cache (VMS) */ + boolean_t error_reported; /* TRUE if jnl_file_lost already reported the journaling error */ + uint4 status2; /* for secondary error status, currently used only in VMS */ + uint4 cycle; /* private copy of the number of this journal file generation */ +} jnl_private_control; + +typedef enum +{ + JNL_KILL, + JNL_SET, + JNL_ZKILL, +# ifdef GTM_TRIGGER + JNL_ZTWORM, + JNL_ZTRIG, +# endif + JA_MAX_TYPES +} jnl_action_code; + +typedef enum +{ +#define MUEXT_TABLE_ENTRY(muext_rectype, code0, code1) muext_rectype, +#include "muext_rec_table.h" +#undef MUEXT_TABLE_ENTRY + + MUEXT_MAX_TYPES /* Total number of EXTRACT JOURNAL record types */ +} muextract_type; + +typedef struct +{ + jnl_action_code operation; + uint4 nodeflags; +} jnl_action; + +#define JNL_FENCE_LIST_END ((sgmnt_addrs *)-1L) + +typedef struct +{ + sgmnt_addrs *fence_list; + int level; + token_num token; +} jnl_fence_control; + +typedef struct +{ + uint4 jrec_type : 8; /* Offset:0 :: Actually, enum jnl_record_type */ + uint4 forwptr : 24; /* Offset:1 :: Offset to beginning of next record */ + off_jnl_t pini_addr; /* Offset:4 :: Offset in the journal file which contains pini record */ + jnl_tm_t time; /* Offset:8 :: 4-byte time stamp both for UNIX and VMS */ + uint4 checksum; /* Offset:12 :: Generated from journal record */ + trans_num tn; /* Offset:16 */ +} jrec_prefix; /* 24-byte */ + +typedef struct +{ + uint4 backptr : 24; /* Offset to beginning of current record */ + uint4 suffix_code : 8; /* JNL_REC_SUFFIX_CODE */ +} jrec_suffix; /* 4-byte */ + +typedef union +{ + seq_num jnl_seqno; + token_num token; +} token_seq_t; + +typedef struct +{ + char label[SIZEOF(JNL_LABEL_TEXT) - 1]; + char is_little_endian; /* this field's offset (JNL_HDR_ENDIAN_OFFSET) should not change + * across journal versions and is checked right after reading the header. + */ + char filler_align8[7]; + jnl_process_vector who_created, /* Process who created */ + who_opened; /* Process who last opened */ + jnl_proc_time bov_timestamp, /* 8-byte time when journal was created */ + eov_timestamp; /* 8-byte time when journal was last updated + Updated by cre_jnl_file/jnl_file_extend/jnl_file_close */ + trans_num bov_tn, /* Beginning journal record's transaction number */ + eov_tn; /* End transaction number. + Updated by cre_jnl_file/jnl_file_extend/jnl_file_close */ + seq_num start_seqno; /* reg_seqno when this journal file was created */ + seq_num end_seqno; /* reg_seqno when this journal file was closed or last extended. + Updated by cre_jnl_file/jnl_file_extend/jnl_file_close */ + off_jnl_t end_of_data; /* Offset of beginning of last record. + Updated by cre_jnl_file/jnl_file_extend/jnl_file_close */ + off_jnl_t prev_recov_end_of_data; /* Recovered/Rolled back journal's turn around point's offset. + This offset was supposed to have EOF_RECORD before recover switched journal. + A non-zero value means this journal was recovered and had the turn around point. */ + off_jnl_t virtual_size; /* Allocation + n * Extension (in blocks). jnl_file_extend updates it */ + boolean_t crash; /* crashed before jnl_file_close() completed */ + boolean_t recover_interrupted; /* true when recover creates the journal file; false after success. */ + off_jnl_t turn_around_offset; /* At turn around point journal record's (EPOCH) offset */ + jnl_tm_t turn_around_time; /* At turn around point journal record's timestamp */ + boolean_t before_images; /* before image enabled in this journal */ + uint4 alignsize; /* align size of journal (where a valid record start) */ + int4 epoch_interval; /* Time between successive epochs in epoch-seconds */ + int4 repl_state; /* To state whether replication is turned on for this journal file */ + uint4 autoswitchlimit;/* Limit in disk blocks (max 4GBytes) when jnl should be auto switched */ + uint4 jnl_alq; /* initial allocation (in blocks) */ + uint4 jnl_deq; /* extension (in blocks) */ +#ifdef VMS + boolean_t update_disabled;/* If the secondary side has database update disabled. For rollback. */ +#else + boolean_t filler_update_disabled; /* obsoleted as part of multi-site replication changes */ +#endif + int4 max_jrec_len; /* Maximum length in bytes of a journal record. + * Although computed from the database block size, we need this + * stored as well in case database is not available */ + uint4 data_file_name_length; /* Length of data_file_name */ + uint4 prev_jnl_file_name_length; /* Length of prev_jnl_file_name */ + uint4 next_jnl_file_name_length; /* Length of next_jnl_file_name */ + uint4 checksum; /* Calculate from journal file id */ + uint4 prev_recov_blks_to_upgrd_adjust; /* amount to adjust filehdr "blks_to_upgrd" if ever + * backward recovery goes back past this journal file */ + unsigned char data_file_name[JNL_NAME_SIZE]; /* Database file name */ + unsigned char prev_jnl_file_name[JNL_NAME_SIZE]; /* Previous generation journal file name */ + unsigned char next_jnl_file_name[JNL_NAME_SIZE]; /* Next generation journal file name */ + /* encryption related fields */ + uint4 is_encrypted; + char encryption_hash[GTMCRYPT_RESERVED_HASH_LEN]; + + /* filler remaining */ + char filler[696]; +} jnl_file_header; + +typedef struct +{ + int4 status, + alloc, + extend, + buffer; + sgmnt_data_ptr_t csd; + seq_num reg_seqno; + unsigned char jnl[JNL_NAME_SIZE], + *fn; + uint4 max_jrec_len; + short fn_len, + jnl_len, + jnl_def_len; + bool before_images; + bool filler_bool[1]; + uint4 alignsize; + int4 autoswitchlimit; /* limit in disk blocks (8388607 blocks) + * when jnl should be auto switched */ + int4 epoch_interval; /* Time between successive epochs in epoch-seconds */ + char *prev_jnl; + int4 prev_jnl_len; + int4 jnl_state; /* current csd->jnl_state */ + int4 repl_state; + uint4 status2; /* for secondary error status information in VMS */ + boolean_t no_rename; + boolean_t no_prev_link; + int4 blks_to_upgrd; /* Blocks not at current block version level */ + uint4 checksum; + uint4 free_blocks; /* free blocks counter at time of epoch */ + uint4 total_blks; /* total blocks counter at time of epoch */ + uint4 is_encrypted; + char encryption_hash[GTMCRYPT_HASH_LEN]; +} jnl_create_info; + +/* Journal record definitions */ + +#define jnl_str_len_t uint4 /* 4 byte length (which is in turn split into bit fields below) */ + +/* Bit masks for the "nodeflags" field. Note that there is no flag to indicate whether this update actually invoked any triggers. + * That is because we have to format the journal record BEFORE invoking any triggers (that way the triggering update comes ahead + * of its corresponding triggered updates in the journal file as this ordering is relied upon by the update process) and as part + * of formatting, we also compute the checksum that includes the "nodeflags" field and so fixing this field AFTER trigger + * invocation to reflect if any triggers were invoked would mean recomputing the checksum all over again. Currently there is no + * need for the "triggers actually invoked" bit. If it is later desired, care should be taken to recompute the checksum. + */ +#define JS_NOT_REPLICATED_MASK (1 << 0) /* 1 if this update should NOT be replicated. + * All updates done inside of a trigger and a SET redo (because of changes to $ztval) + * fall in this category. + */ +#define JS_HAS_TRIGGER_MASK (1 << 1) /* 1 if the global being updated had at least one trigger defined (not necessarily + * invoked for this particular update) + */ +#define JS_NULL_ZTWORM_MASK (1 << 2) /* 1 if $ZTWORMHOLE for this update should be "" string, 0 otherwise */ +#define JS_SKIP_TRIGGERS_MASK (1 << 3) /* 1 if MUPIP LOAD update so triggers are not invoked on replay by update process */ +#define JS_MAX_MASK (1 << 8) /* max of 8 bits we have for mask */ + +/* Note that even though mumps_node, ztworm_str, ztrig_str and align_str are members defined as type "jnl_string" below, + * the "nodeflags" field is initialized to non-zero values ONLY in the case of the mumps_node member. + * For ztworm_str and align_str, nodeflags is guaranteed to be zero so the 24-bit "length" member + * can even be used as a 32-bit length (if necessary) without issues. This is why nodeflags is + * defined in a different order (BEFORE or AFTER the "length" member) based on big-endian or little-endian. + */ +typedef struct +{ +# ifdef BIGENDIAN + unsigned int nodeflags : 8; + unsigned int length : 24; +# else + unsigned int length : 24; + unsigned int nodeflags : 8; +# endif + char text[1]; /* Actually text[length] */ +} jnl_string; + +typedef struct jnl_format_buff_struct +{ + que_ent free_que; + struct jnl_format_buff_struct *next; +# ifdef GTM_TRIGGER + struct jnl_format_buff_struct *prev; +# endif + enum jnl_record_type rectype; + int4 record_size; + char *buff; + uint4 checksum; + jnl_action ja; +# ifdef GTM_CRYPT + char *alt_buff; /* for storing the unencrypted jnl *SET and *KILL records to be pushed + * into the jnl pool. */ + NON_GTM64_ONLY(int4 dummy_filler;) /* for alignment in 32 bit machines. */ +# endif +} jnl_format_buffer; + +/* All fixed size records are 8-byte-multiple size. + * All variable size records are made 8-byte multiple size by run-time process */ + +/* struct_jrec_upd for non-TP, TP or ZTP. For replication we use 8-byte jnl_seqno. Otherwise we use 8-byte token. + * Currently we dont support ZTP + replication. + */ +typedef struct /* variable length */ +{ + jrec_prefix prefix; + token_seq_t token_seq; /* must start at 8-byte boundary */ + uint4 update_num; /* 'n' where this is the nth journaled update (across all regions) in this TP + * transaction. n=1 for the first update inside TP, 2 for the second update + * inside TP and so on. Needed so journal recovery and update process can play + * all the updates inside of one TP transaction in the exact same order as GT.M. + */ + unsigned short filler_short; + unsigned short num_participants; /* # of regions that wrote a TCOM record in their jnl files. + * Currently written only for TSET/TKILL/TZTWORM records. + * Uninitialized for all other types of SET/KILL/ZTWORM records. + */ + jnl_string mumps_node; /* For set/kill/zkill : {jnl_str_len_t key_len, char key[key_len]} */ + /* For set additionally : {mstr_len_t data_len, char data[data_len]} */ +} struct_jrec_upd; + +/* $ztwormhole record */ +typedef struct /* variable length */ +{ + jrec_prefix prefix; + token_seq_t token_seq; /* must start at 8-byte boundary */ + uint4 update_num; /* 'n' where this is the nth journaled update (across all regions) in this TP + * transaction. n=1 for the first update inside TP, 2 for the second update + * inside TP and so on. Needed so journal recovery and update process can play + * all the updates inside of one TP transaction in the exact same order as GT.M. + */ + unsigned short filler_short; + unsigned short num_participants; /* # of regions that wrote a TCOM record in their jnl files. + * Currently written only for TSET/TKILL/TZTWORM records. + * Uninitialized for all other types of SET/KILL/ZTWORM records. + */ + jnl_string ztworm_str; /* jnl_str_len_t ztworm_str_len, char ztworm_str[ztworm_str_len]} */ +} struct_jrec_ztworm; + +#define INVALID_UPDATE_NUM (uint4)-1 + +typedef struct /* variable length */ +{ + jrec_prefix prefix; + block_id blknum; + uint4 bsiz; + enum db_ver ondsk_blkver; /* Previous version of block from cache_rec */ + int4 filler; + char blk_contents[1]; /* Actually blk_contents[bsiz] */ +} struct_jrec_blk; + +typedef struct /* variable length */ +{ + jrec_prefix prefix; + jnl_string align_str; + /* Note: Actual string (potentially 0-length too) follows the align_string and then jrec_suffix */ +} struct_jrec_align; + +/* Please change the "GBLDEF struct_jrec_tcom" initialization, if below is changed */ +typedef struct /* fixed length */ +{ + jrec_prefix prefix; + token_seq_t token_seq; /* must start at 8-byte boundary */ + unsigned short filler_short; + unsigned short num_participants; /* # of regions that wrote a TCOM record in their jnl files */ + char jnl_tid[TID_STR_SIZE]; + jrec_suffix suffix; +} struct_jrec_tcom; + +/* Please change the "static struct_jrec_ztcom" initialization in op_ztcommit.c, if below is changed */ +typedef struct /* fixed length */ +{ + jrec_prefix prefix; + token_num token; /* must start at 8-byte boundary */ + unsigned short filler_short; + unsigned short participants; /* # of regions that wrote ZTCOM record in their jnl files for this fenced tn */ + jrec_suffix suffix; +} struct_jrec_ztcom; + +/* Below are different inctn_detail_*_t type definitions based on the inctn record opcode. + * Each of them need to ensure the following. + * a) SIZEOF(inctn_detail_*_t) is identical. + * b) "opcode" member is at the same offset. + * c) "suffix" is the last member. + * Any new inctn_detail_*_t type definitions should have corresponding code changes in jnl_write_inctn_rec.c + */ +typedef struct +{ + block_id blknum; /* block that got upgraded or downgraded (opcode = inctn_blk*grd) */ + uint4 filler_uint4; + unsigned short filler_short; + unsigned short opcode; + jrec_suffix suffix; +} inctn_detail_blknum_t; + +typedef struct +{ + int4 blks_to_upgrd_delta; /* Delta to adjust csd->blks_to_upgrade (opcode = inctn_gdsfilext_*) */ + uint4 filler_uint4; + unsigned short filler_short; + unsigned short opcode; + jrec_suffix suffix; +} inctn_detail_blks2upgrd_t; + +typedef union +{ + inctn_detail_blknum_t blknum_struct; + inctn_detail_blks2upgrd_t blks2upgrd_struct; +} inctn_detail_t; + +typedef struct /* fixed length */ +{ + jrec_prefix prefix; + inctn_detail_t detail; + /* jrec_suffix is already part of inctn_detail_t */ +} struct_jrec_inctn; + +typedef struct /* fixed length */ +{ + jrec_prefix prefix; + jnl_process_vector process_vector[JPV_COUNT]; + int4 filler; + jrec_suffix suffix; +} struct_jrec_pini; + +typedef struct /* fixed length */ +{ + jrec_prefix prefix; + uint4 filler; + jrec_suffix suffix; +} struct_jrec_pfin; + +/* Following 3 are same structures. In case we change it in future, let's define them separately */ +typedef struct /* fixed length */ +{ + jrec_prefix prefix; + seq_num jnl_seqno; /* must start at 8-byte boundary */ + uint4 filler; + jrec_suffix suffix; +} struct_jrec_null; + +typedef struct /* fixed length */ +{ + jrec_prefix prefix; + seq_num jnl_seqno; /* must start at 8-byte boundary */ + uint4 blks_to_upgrd; /* blocks-to-upgrade counter at time of epoch */ + uint4 free_blocks; /* free blocks counter at time of epoch */ + uint4 total_blks; /* total blocks counter at time of epoch */ + jrec_suffix suffix; +} struct_jrec_epoch; + +typedef struct /* fixed length */ +{ + jrec_prefix prefix; + seq_num jnl_seqno; /* must start at 8-byte boundary */ + uint4 filler; + jrec_suffix suffix; +} struct_jrec_eof; + +typedef union +{ + jrec_prefix prefix; + struct_jrec_upd jrec_set_kill; + struct_jrec_ztworm jrec_ztworm; + struct_jrec_blk jrec_pblk, + jrec_aimg; + struct_jrec_align jrec_align; + /** All below are fixed size and above are variable size records */ + struct_jrec_tcom jrec_tcom; + struct_jrec_ztcom jrec_ztcom; + struct_jrec_inctn jrec_inctn; + struct_jrec_pini jrec_pini; + struct_jrec_pfin jrec_pfin; + struct_jrec_null jrec_null; + struct_jrec_epoch jrec_epoch; + struct_jrec_eof jrec_eof; +} jnl_record; + + +/* Macro to access fixed size record's size */ +#define TCOM_RECLEN SIZEOF(struct_jrec_tcom) +#define ZTCOM_RECLEN SIZEOF(struct_jrec_ztcom) +#define INCTN_RECLEN SIZEOF(struct_jrec_inctn) +#define PINI_RECLEN SIZEOF(struct_jrec_pini) +#define PFIN_RECLEN SIZEOF(struct_jrec_pfin) +#define NULL_RECLEN SIZEOF(struct_jrec_null) +#define EPOCH_RECLEN SIZEOF(struct_jrec_epoch) +#define EOF_RECLEN SIZEOF(struct_jrec_eof) +/* Macro to access variable size record's fixed part's size */ +#define FIXED_ZTWORM_RECLEN OFFSETOF(struct_jrec_ztworm, ztworm_str) +#define FIXED_UPD_RECLEN OFFSETOF(struct_jrec_upd, mumps_node) +#define MIN_ALIGN_RECLEN (OFFSETOF(struct_jrec_align, align_str.text[0]) + JREC_SUFFIX_SIZE) +#define FIXED_ALIGN_RECLEN OFFSETOF(struct_jrec_align, align_str.text[0]) +#define FIXED_BLK_RECLEN OFFSETOF(struct_jrec_blk, blk_contents[0]) +#define FIXED_PBLK_RECLEN OFFSETOF(struct_jrec_blk, blk_contents[0]) +#define FIXED_AIMG_RECLEN OFFSETOF(struct_jrec_blk, blk_contents[0]) +#define MIN_PBLK_RECLEN (OFFSETOF(struct_jrec_blk, blk_contents[0]) + JREC_SUFFIX_SIZE) +#define MIN_AIMG_RECLEN (OFFSETOF(struct_jrec_blk, blk_contents[0]) + JREC_SUFFIX_SIZE) + +#define JREC_PREFIX_SIZE SIZEOF(jrec_prefix) +#define JREC_SUFFIX_SIZE SIZEOF(jrec_suffix) +#define MIN_JNLREC_SIZE (JREC_PREFIX_SIZE + JREC_SUFFIX_SIZE) +#define JREC_PREFIX_UPTO_LEN_SIZE (offsetof(jrec_prefix, pini_addr)) + +typedef struct set_jnl_options_struct +{ + int cli_journal, cli_enable, cli_on, cli_replic_on; + boolean_t alignsize_specified, + allocation_specified, + autoswitchlimit_specified, + image_type_specified, /* beofre/nobefore option specified */ + buffer_size_specified, + epoch_interval_specified, + extension_specified, + filename_specified, + sync_io_specified, + yield_limit_specified; + /* since jnl_create_info does not have following fields, we need them here */ + boolean_t sync_io; + int4 yield_limit; +} set_jnl_options; + +/* rlist_state needed to be moved here to use with mu_set_reglist */ +enum rlist_state { + NONALLOCATED, + ALLOCATED, + DEALLOCATED +}; + +/* mu_set_reglist needed to be moved here for the journal specific fields */ +/* ATTN: the first four items in this structure need to be identical to those + * in structure tp_region in tp.h. + */ +typedef struct mu_set_reglist +{ + struct mu_set_reglist *fPtr; /* all fields after this are used for mupip_set_journal.c */ + gd_region *reg; + char unique_id[UNIQUE_ID_SIZE]; + enum rlist_state state; + sgmnt_data_ptr_t sd; + bool exclusive; /* standalone access is required for this region */ + int fd; + enum jnl_state_codes jnl_new_state; + enum repl_state_codes repl_new_state; + boolean_t before_images; +} mu_set_rlist; + +/* The enum codes below correspond to code-paths that can call set_jnl_file_close() in VMS */ +typedef enum +{ + SET_JNL_FILE_CLOSE_BACKUP = 1, /* just for safety a non-zero value to start with */ + SET_JNL_FILE_CLOSE_SETJNL, + SET_JNL_FILE_CLOSE_EXTEND, + SET_JNL_FILE_CLOSE_RUNDOWN, + SET_JNL_FILE_CLOSE_INVALID_OP +} set_jnl_file_close_opcode_t; + +typedef void (*pini_addr_reset_fnptr)(sgmnt_addrs *csa); + +typedef struct +{ + token_num mur_jrec_seqno; /* This is jnl_seqno */ + VMS_ONLY(seq_num max_resync_seqno;) /* for update process and rollback fetchresync */ + UNIX_ONLY(seq_num max_dualsite_resync_seqno;) /* for update process and rollback fetchresync */ + unsigned short filler_short; + unsigned short mur_jrec_participants; + jnl_tm_t gbl_jrec_time; + jnl_tm_t mur_tp_resolve_time; /* tp resolve time as determined by journal recovery. + * Time of the point upto which a region will be processed for + * TP token resolution for backward or forward recover. + * Note : This is what prevents user to change system time. + */ + boolean_t forw_phase_recovery; + boolean_t mur_rollback; /* a copy of mur_options.rollback to be accessible to runtime code */ + boolean_t mupip_journal; /* the current command is a MUPIP JOURNAL command */ + boolean_t dont_reset_gbl_jrec_time; /* Do not reset gbl_jrec_time */ + pini_addr_reset_fnptr mur_pini_addr_reset_fnptr; /* function pointer to invoke "mur_pini_addr_reset" */ + uint4 cumul_jnl_rec_len; /* cumulative length of the replicated journal records + * for the current TP or non-TP transaction */ + boolean_t wait_for_jnl_hard; + uint4 tp_ztp_jnl_upd_num; /* Incremented whenever a journaled update happens inside of + * TP or ZTP. Copied over to the corresponding journal record + * to record the sequence of all updates inside TP/ZTP transaction. + */ + uint4 mur_jrec_nodeflags; /* copy of "nodeflags" from jnl record currently being played */ +# ifdef GTM_TRIGGER + unsigned char *prev_ztworm_ptr; /* Non-NULL if at least one ztwormhole record was successfully + * formatted in this transaction. Note that ZTWORMHOLE records are + * formatted ONLY in case of journaled & replicated databases. + * 1. If replicated database is unencrypted, this points to + * jfb->buff + FIXED_UPD_RECLEN + * 2. If replicated database is encrypted, this points to + * jfb->alt_buff + FIXED_UPD_RECLEN + * If no ztwormhole record is yet formatted, then points to NULL + */ + unsigned char *save_ztworm_ptr; /* copy of prev_ztworm_ptr saved until we know for sure whether + * a ZTWORMHOLE journal record will be written or not. + */ +# endif +# ifdef DEBUG + boolean_t mur_fences_none; /* a copy of mur_options.fences to be accessible to runtime code */ + uint4 cumul_index; + uint4 cu_jnl_index; + uint4 max_tp_ztp_jnl_upd_num; /* Max of all values processed in this + * potentially multi-region transaction. Used only by jnl recovery. + */ +# endif +} jnl_gbls_t; + + +#define JNL_SHARE_SIZE(X) (JNL_ALLOWED(X) ? \ + (ROUND_UP(JNL_NAME_EXP_SIZE + SIZEOF(jnl_buffer), OS_PAGE_SIZE) \ + + ROUND_UP(((sgmnt_data_ptr_t)X)->jnl_buffer_size * DISK_BLOCK_SIZE, \ + OS_PAGE_SIZE) + OS_PAGE_SIZE) : 0) + +/* pass address of jnl_buffer to get address of expanded jnl file name */ +#define JNL_GDID_PVT(CSA) ((CSA)->jnl->fileid) + +#ifdef UNIX +#define JNL_GDID_PTR(CSA) ((gd_id_ptr_t)(&((CSA)->nl->jnl_file.u))) +#else +#define JNL_GDID_PTR(CSA) ((gd_id_ptr_t)(&((CSA)->nl->jnl_file.jnl_file_id))) +#endif + +/* Note that since "cycle" (in jpc and jb below) can rollover the 4G limit back to 0, it should + * only be used to do "!=" checks and never to do ordered checks like "<", ">", "<=" or ">=". + */ +#define JNL_FILE_SWITCHED(JPC) ((JPC)->cycle != (JPC)->jnl_buff->cycle) + +#define REG_STR "region" +#define FILE_STR "database file" + +/* Given a journal record, get_jnl_seqno returns the jnl_seqno field + * Now all replication type records, EOF and EPOCH have the jnl_seqno at the same offset. + * Modify the macro GET_JNL_SEQNO if offset of jnl_seqno is changed for any journal records + */ +#define GET_JNL_SEQNO(j) (((jnl_record *)(j))->jrec_null.jnl_seqno) +#define GET_REPL_JNL_SEQNO(j) (IS_REPLICATED(((jrec_prefix *)j)->jrec_type) ? GET_JNL_SEQNO(j) : 0) + +/* Given a journal record, GET_TN returns the tn field + */ +#define GET_TN(j) (((*jrec_prefix)(j))->prefix.tn) + +/* In t_end(), we need to write the after-image if DSE or mupip recover/rollback is playing it. + * But to write it out, we should have it already built before bg_update(). + * Hence, we pre-build the block here itself before invoking t_end(). + */ +#define BUILD_AIMG_IF_JNL_ENABLED(CSD, JFB, TN) \ +{ \ + GBLREF cw_set_element cw_set[]; \ + GBLREF unsigned char cw_set_depth; \ + \ + cw_set_element *cse; \ + \ + if (JNL_ENABLED(CSD)) \ + { \ + assert(1 == cw_set_depth); /* Only DSE uses this macro and it updates one block at a time */ \ + cse = (cw_set_element *)(&cw_set[0]); \ + cse->new_buff = JFB; \ + gvcst_blk_build(cse, (uchar_ptr_t)cse->new_buff, TN); \ + cse->done = TRUE; \ + } \ +} + +/* In Unix, the journal file header size is currently set to 64K so it is aligned with any possible filesystem block size + * known at this point. This will help us do aligned writes to the journal file header as well as the journal file contents + * without needing to mix both of them in the same aligned disk write. In VMS, we continue with 512-byte alignment so no change. + * Note that the journal_file_header structure is only 2K currently and is captured using the REAL_JNL_HDR_LEN macro while + * the padded 64K file header is captured using the JNL_HDR_LEN macro. Use either one as appropriate in the code. Both of them + * are identical in VMS where it is currently 2K. + */ +#define REAL_JNL_HDR_LEN SIZEOF(jnl_file_header) +#ifdef UNIX +# define JNL_HDR_LEN 64*1024 +#elif defined(VMS) +# define JNL_HDR_LEN REAL_JNL_HDR_LEN +#endif +#define JNL_FILE_FIRST_RECORD JNL_HDR_LEN + +/* Minimum possible journal file size */ +#define MIN_JNL_FILE_SIZE (JNL_HDR_LEN + PINI_RECLEN + EPOCH_RECLEN + PFIN_RECLEN + EOF_RECLEN) + +/* maximum required journal file size (in 512-byte blocks), if the current transaction was the only one in a fresh journal file */ +#define MAX_REQD_JNL_FILE_SIZE(tot_jrec_size) DIVIDE_ROUND_UP((tot_jrec_size + MIN_JNL_FILE_SIZE), DISK_BLOCK_SIZE) + +/* this macro aligns the input size to account that journal file sizes can increase only in multiples of the extension size */ +#define ALIGNED_ROUND_UP(tmp_tot_jrec_size, jnl_alq, jnl_deq) \ + (((tmp_tot_jrec_size) <= (jnl_alq) || !(jnl_deq)) \ + ? (jnl_alq) \ + : ((jnl_alq) + ROUND_UP((tmp_tot_jrec_size) - (jnl_alq), (jnl_deq)))) + +/* this macro aligns the input size to account that journal file sizes can increase only in multiples of the extension size */ +#define ALIGNED_ROUND_DOWN(tmp_tot_jrec_size, jnl_alq, jnl_deq) \ + (((tmp_tot_jrec_size) <= (jnl_alq) || !(jnl_deq)) \ + ? (jnl_alq) \ + : ((jnl_alq) + ROUND_DOWN((tmp_tot_jrec_size) - (jnl_alq), (jnl_deq)))) + +/* the following macro uses 8-byte quantities (gtm_uint64_t) to perform additions that might cause a 4G overflow */ +#define DISK_BLOCKS_SUM(freeaddr, jrec_size) DIVIDE_ROUND_UP((((gtm_uint64_t)(freeaddr)) + (jrec_size)), DISK_BLOCK_SIZE) + +#if defined(UNIX) +/* For future portability JNLBUFF_ALLOC is defined in jnl.h instead of jnlsp.h */ +#define JPC_ALLOC(csa) \ +{ \ + csa->jnl = (jnl_private_control *)malloc(SIZEOF(*csa->jnl)); \ + memset(csa->jnl, 0, SIZEOF(*csa->jnl)); \ +} +#define ASSERT_JNLFILEID_NOT_NULL(csa) \ +{ \ + assert(0 != csa->nl->jnl_file.u.inode); \ + assert(0 != csa->nl->jnl_file.u.device); \ +} +#define NULLIFY_JNL_FILE_ID(csa) \ +{ \ + csa->nl->jnl_file.u.inode = 0; \ + csa->nl->jnl_file.u.device = 0; \ +} +#elif defined(VMS) +#define JPC_ALLOC(csa) \ +{ \ + vms_lock_sb *tmp_jnllsb; \ + if (NULL == csa->jnl) \ + { \ + csa->jnl = (jnl_private_control *)malloc(SIZEOF(*csa->jnl)); \ + memset(csa->jnl, 0, SIZEOF(*csa->jnl)); \ + csa->jnl->jnllsb = malloc(SIZEOF(vms_lock_sb)); \ + } else \ + { \ + tmp_jnllsb = csa->jnl->jnllsb; \ + memset(csa->jnl, 0, SIZEOF(*csa->jnl)); \ + csa->jnl->jnllsb = tmp_jnllsb; \ + } \ + memset(csa->jnl->jnllsb, 0, SIZEOF(vms_lock_sb)); \ +} +#define ASSERT_JNLFILEID_NOT_NULL(csa) assert(0 != memcmp(csa->nl->jnl_file.jnl_file_id.fid, zero_fid, SIZEOF(zero_fid))); +#define NULLIFY_JNL_FILE_ID(csa) memset(&csa->nl->jnl_file.jnl_file_id, 0, SIZEOF(gds_file_id)) +#endif +#define JNL_INIT(csa, reg, csd) \ +{ \ + csa->jnl_state = csd->jnl_state; \ + csa->jnl_before_image = csd->jnl_before_image; \ + csa->repl_state = csd->repl_state; \ + if JNL_ALLOWED(csa) \ + { \ + JPC_ALLOC(csa); \ + csa->jnl->region = reg; \ + csa->jnl->jnl_buff = (jnl_buffer_ptr_t)((sm_uc_ptr_t)(csa->nl) + NODE_LOCAL_SPACE + JNL_NAME_EXP_SIZE); \ + csa->jnl->channel = NOJNL; \ + } else \ + csa->jnl = NULL; \ +} +#define JNL_FD_CLOSE(CHANNEL, RC) \ +{ \ + fd_type lcl_channel; \ + \ + /* Reset incoming channel BEFORE closing it. This way, if we get interrupted BEFORE the close but \ + * after we have reset channel, we could at most end up with a file descriptor leak. Doing it the \ + * other way around could cause us to close the channel but yet have a dangling pointer to it that \ + * could result in more than one close of the same file descriptor where the second close could \ + * be on some other valid open file descriptor. \ + */ \ + lcl_channel = CHANNEL; \ + CHANNEL = NOJNL; \ + F_CLOSE(lcl_channel, RC); /* resets "lcl_channel" to FD_INVALID */ \ + assert(SS_NORMAL == RC); \ +} + +#define MAX_EPOCH_DELAY 30 +#define EXT_NEW "_new" +#define PREFIX_ROLLED_BAK "rolled_bak_" +#define REC_TOKEN(jnlrec) ((struct_jrec_upd *)jnlrec)->token_seq.token +#define REC_JNL_SEQNO(jnlrec) ((struct_jrec_upd *)jnlrec)->token_seq.jnl_seqno +#define REC_LEN_FROM_SUFFIX(ptr, reclen) ((jrec_suffix *)((unsigned char *)ptr + reclen - JREC_SUFFIX_SIZE))->backptr + +#define JNL_MAX_SET_KILL_RECLEN(CSD) (uint4)ROUND_UP2(FIXED_UPD_RECLEN + JREC_SUFFIX_SIZE \ + + ((CSD)->blk_size - SIZEOF(blk_hdr) - SIZEOF(rec_hdr)) \ + + SIZEOF(jnl_str_len_t) + SIZEOF(mstr_len_t), JNL_REC_START_BNDRY) \ + /* fixed size part of update record + MAX possible (key + data) len + keylen-len + datalen-len */ + +#define JNL_MAX_PBLK_RECLEN(CSD) (uint4)ROUND_UP2(MIN_PBLK_RECLEN + (CSD)->blk_size, JNL_REC_START_BNDRY) + +/* Macro to compute the maximum possible journal record length in the journal file. + * In order to compute the maximum jnl record length, note that an align record is written whenever + * a would cause the jnl file offset to move past an aligned boundary. + * Therefore after computing the maximum possible non-align-jnl-record-length, we need to add MIN_ALIGN_RECLEN + * as this is the maximum possible align-jnl-record-length and should be the eventual max_jrec_len. + */ +#define JNL_MAX_RECLEN(JINFO, CSD) \ +{ \ + int4 max_logi_reclen, max_pblk_reclen; \ + \ + /* A logical record is a SET/KILL record. The SET could be as big as (CSD)->max_rec_size, but since \ + * csd->max_rec_size can be changed independent of journal file creation (through DSE), we consider \ + * the max possible record size that can fit in a GDS block. After that we consider $ZTWORMHOLE too. \ + */ \ + max_logi_reclen = JNL_MAX_SET_KILL_RECLEN(CSD); \ + GTMTRIG_ONLY(max_logi_reclen = MAX(max_logi_reclen, MAX_ZTWORM_JREC_LEN);) \ + max_pblk_reclen = JNL_MAX_PBLK_RECLEN(CSD); \ + (JINFO)->max_jrec_len = MAX(max_logi_reclen, max_pblk_reclen) + MIN_ALIGN_RECLEN; \ +} + +/* Macro that checks that the region seqno in the filehdr is never more than the seqno in the journal pool */ +#define ASSERT_JNL_SEQNO_FILEHDR_JNLPOOL(csd, jnlpool_ctl) \ +{ /* The seqno in the file header should be at most 1 greater than that in the journal pool. \ + * See step (5) of of commit logic flow in secshr_db_clnup.c for why. Assert that. \ + */ \ + assert((NULL == jnlpool_ctl) || (csd->reg_seqno <= (jnlpool_ctl->jnl_seqno + 1))); \ +} +#ifdef GTM_CRYPT +#define DECODE_SET_KILL_ZKILL_ZTRIG(mumps_node_ptr, rec_size, key_handle, RC) \ +{ \ + int span_length, fixed_prefix; \ + \ + RC = 0; \ + assert(FIXED_UPD_RECLEN == FIXED_ZTWORM_RECLEN); \ + fixed_prefix = FIXED_UPD_RECLEN; \ + ASSERT_ENCRYPTION_INITIALIZED; \ + span_length = rec_size - fixed_prefix - JREC_SUFFIX_SIZE; \ + GTMCRYPT_DECODE_FAST(key_handle, (char *)mumps_node_ptr, span_length, NULL, RC); \ +} + +#endif /* GTM_CRYPT */ +/* jnl_ prototypes */ +uint4 jnl_file_extend(jnl_private_control *jpc, uint4 total_jnl_rec_size); +uint4 jnl_file_lost(jnl_private_control *jpc, uint4 jnl_stat); +uint4 jnl_qio_start(jnl_private_control *jpc); +uint4 jnl_write_attempt(jnl_private_control *jpc, uint4 threshold); +void jnl_prc_vector(jnl_process_vector *pv); +void jnl_send_oper(jnl_private_control *jpc, uint4 status); +uint4 cre_jnl_file(jnl_create_info *info); +uint4 cre_jnl_file_common(jnl_create_info *info, char *rename_fn, int rename_fn_len); +void jfh_from_jnl_info (jnl_create_info *info, jnl_file_header *header); +uint4 jnl_ensure_open(void); +void set_jnl_info(gd_region *reg, jnl_create_info *set_jnl_info); +void jnl_write_epoch_rec(sgmnt_addrs *csa); +void jnl_write_inctn_rec(sgmnt_addrs *csa); +void jnl_write_logical(sgmnt_addrs *csa, jnl_format_buffer *jfb); +void jnl_write_ztp_logical(sgmnt_addrs *csa, jnl_format_buffer *jfb); +void jnl_write_eof_rec(sgmnt_addrs *csa, struct_jrec_eof *eof_record); +void jnl_write_poolonly(jnl_private_control *jpc, enum jnl_record_type rectype, jnl_record *jnl_rec, jnl_format_buffer *jfb); + +jnl_format_buffer *jnl_format(jnl_action_code opcode, gv_key *key, mval *val, uint4 nodeflags); + +#ifdef VMS +void finish_active_jnl_qio(void); +void jnl_start_ast(jnl_private_control *jpc); +uint4 jnl_permit_ast(jnl_private_control *jpc); +void jnl_qio_end(jnl_private_control *jpc); +#endif + +void wcs_defer_wipchk_ast(jnl_private_control *jpc); +uint4 set_jnl_file_close(set_jnl_file_close_opcode_t set_jnl_file_close_opcode); +uint4 jnl_file_open_common(gd_region *reg, off_jnl_t os_file_size); +uint4 jnl_file_open_switch(gd_region *reg, uint4 sts); +void jnl_file_close(gd_region *reg, bool clean, bool dummy); + +/* Consider putting followings in a mupip only header file : Layek 2/18/2003 */ +boolean_t mupip_set_journal_parse(set_jnl_options *jnl_options, jnl_create_info *jnl_info); +uint4 mupip_set_journal_newstate(set_jnl_options *jnl_options, jnl_create_info *jnl_info, mu_set_rlist *rptr); +void mupip_set_journal_fname(jnl_create_info *jnl_info); +uint4 mupip_set_jnlfile_aux(jnl_file_header *header, char *jnl_fname); +void jnl_setver(void); +void jnl_extr_init(void); +int exttime(uint4 time, char *buffer, int extract_len); +char *ext2jnlcvt(char *ext_buff, int4 ext_len, jnl_record *rec); +char *ext2jnl(char *ptr, jnl_record *rec); +char *jnl2extcvt(jnl_record *rec, int4 jnl_len, char *ext_buff); +char *jnl2ext(char *jnl_buff, char *ext_buff); + +#endif /* JNL_H_INCLUDED */ diff --git a/sr_port/jnl2ext.c b/sr_port/jnl2ext.c new file mode 100644 index 0000000..96149af --- /dev/null +++ b/sr_port/jnl2ext.c @@ -0,0 +1,232 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include /* for offsetof macro */ +#include "gtm_string.h" +#include "gtm_stdio.h" + +#include "gdsroot.h" /* for filestruct.h */ +#include "gdsbt.h" /* for gdsfhead.h */ +#include "gtm_facility.h" /* for fileinfo.h */ +#include "fileinfo.h" /* for gdsfhead.h */ +#include "gdsfhead.h" /* for filestruct.h */ +#include "filestruct.h" /* for jnl.h */ +#include "jnl.h" +#include "copy.h" /* for REF_CHAR macro */ +#include "repl_filter.h" +#include "format_targ_key.h" +#include "mlkdef.h" +#include "zshow.h" +#include "buddy_list.h" /* needed for muprec.h */ +#include "hashtab_mname.h" /* needed for muprec.h */ +#include "hashtab_int4.h" /* needed for muprec.h */ +#include "hashtab_int8.h" /* needed for muprec.h */ +#include "muprec.h" +#include "real_len.h" /* for real_len() prototype */ + +#define DELIMIT_CURR *curr++ = '\\'; +#define ZERO_TIME_DELIM "0,0\\" +#define PIDS_DELIM "0\\0\\" + +/* + * Generic function to convert a journal record into an extract format record. + * Expects a pointer to the journal record and the length of the buffer and + * returns the result in ext_buff. If there is a bad format record in between, + * it returns NULL. It sets jb_stop to the offset of the jnl_buff where + * the last jnlrecord was processed. On successful conversion, it returns a value + * ptr, such that ptr - ext_buff would be the length of the extracted buffer. + * If the ext_len is not enough for the conversion, it returns ext_buff + ext_len + */ + +GBLREF char *jb_stop; +GBLREF char muext_code[][2]; + +static boolean_t first_tstart = FALSE; +static int4 num_tstarts = 0; +static int4 num_tcommits = 0; + + +char *jnl2extcvt(jnl_record *rec, int4 jnl_len, char *ext_buff) +{ + int4 rec_len; + + for ( ; jnl_len > JREC_PREFIX_UPTO_LEN_SIZE && jnl_len >= (rec_len = rec->prefix.forwptr) && rec_len > MIN_JNLREC_SIZE; ) + { + ext_buff = jnl2ext((char *)rec, ext_buff); + jnl_len -= rec_len; + rec = (jnl_record *)((char *)rec + rec_len); + } + + jb_stop = (char *)rec; + return ext_buff; +} + +/* This was earlier declared as a local variable, but was moved up, because the HPIA compiler for some reason seems to + * optimize things, and thus not update the buffer correctly. Problem shows up only in optimized builds. Moving it to + * global status fixed the issue + */ +GBLDEF char key_buff[SIZEOF(gv_key) + MAX_KEY_SZ + 7]; + +char *jnl2ext(char *jnl_buff, char *ext_buff) +{ + char *curr, *val_ptr, rectype; + unsigned char *ptr; + jnl_record *rec; + gv_key *key; + jnl_string *keystr, *ztwormstr; + int val_extr_len, val_len, rec_len, tid_len; + + rec = (jnl_record *)jnl_buff; + rectype = rec->prefix.jrec_type; + rec_len = rec->prefix.forwptr; + if ((ROUND_DOWN2(rec_len, JNL_REC_START_BNDRY) != rec_len) || rec_len != REC_LEN_FROM_SUFFIX(jnl_buff, rec_len)) + { + assert(FALSE); + return ext_buff; + } + if (!IS_REPLICATED(rectype)) + { + assert(FALSE); + return ext_buff; + } + curr = ext_buff; + /* The following assumes the journal extract format is "GDSJEX05". Whenever that changes (in mur_jnl_ext.c), + * the below code as well as ext2jnl.c needs to change. Add an assert to let us know of that event. + */ + assert(!MEMCMP_LIT(JNL_EXTR_LABEL,"GDSJEX05")); + if (IS_TUPD(rectype)) + { + if (FALSE == first_tstart) + { + GET_SHORTP(curr, &muext_code[MUEXT_TSTART][0]); + curr += 2; + DELIMIT_CURR; + MEMCPY_LIT(curr, ZERO_TIME_DELIM); + curr += STR_LIT_LEN(ZERO_TIME_DELIM); + curr = (char *)i2ascl((uchar_ptr_t)curr, rec->jrec_set_kill.prefix.tn); + DELIMIT_CURR; + MEMCPY_LIT(curr, PIDS_DELIM); + curr += STR_LIT_LEN(PIDS_DELIM); + curr = (char *)i2ascl((uchar_ptr_t)curr, rec->jrec_set_kill.token_seq.jnl_seqno); + *curr++ = '\n'; + *curr = '\0'; + first_tstart = TRUE; + } + num_tstarts++; + } else if (JRT_TCOM == rectype) + { + num_tcommits++; + if (num_tcommits == num_tstarts) + { + num_tcommits = num_tstarts = 0; + first_tstart = FALSE; + GET_SHORTP(curr, &muext_code[MUEXT_TCOMMIT][0]); + curr += 2; + DELIMIT_CURR; + MEMCPY_LIT(curr, ZERO_TIME_DELIM); + curr += STR_LIT_LEN(ZERO_TIME_DELIM); + curr = (char *)i2ascl((uchar_ptr_t)curr, rec->jrec_tcom.prefix.tn); + DELIMIT_CURR; + MEMCPY_LIT(curr, PIDS_DELIM); + curr += STR_LIT_LEN(PIDS_DELIM); + curr = (char *)i2ascl((uchar_ptr_t)curr, rec->jrec_tcom.token_seq.jnl_seqno); + DELIMIT_CURR; + *curr = '1'; /* Only ONE TSTART..TCOM in the external filter format */ + curr++; + DELIMIT_CURR; + ptr = (unsigned char *)rec->jrec_tcom.jnl_tid; + tid_len = real_len(SIZEOF(rec->jrec_tcom.jnl_tid), ptr); + memcpy(curr, ptr, tid_len); + curr += tid_len; + *curr++ = '\n'; + *curr = '\0'; + return curr; + } + return ext_buff; + } + if (IS_SET(rectype)) + GET_SHORTP(curr, &muext_code[MUEXT_SET][0]); + else if (IS_KILL(rectype)) + GET_SHORTP(curr, &muext_code[MUEXT_KILL][0]); + else if (IS_ZKILL(rectype)) + GET_SHORTP(curr, &muext_code[MUEXT_ZKILL][0]); + else if (IS_ZTWORM(rectype)) + GET_SHORTP(curr, &muext_code[MUEXT_ZTWORM][0]); + else if (IS_ZTRIG(rectype)) + GET_SHORTP(curr, &muext_code[MUEXT_ZTRIG][0]); + else /* if (JRT_NULL == rectype) */ + { + assert(JRT_NULL == rectype); + GET_SHORTP(curr, &muext_code[MUEXT_NULL][0]); + } + curr += 2; + DELIMIT_CURR; + MEMCPY_LIT(curr, ZERO_TIME_DELIM); + curr += STR_LIT_LEN(ZERO_TIME_DELIM); + curr = (char *)i2ascl((uchar_ptr_t)curr, rec->jrec_set_kill.prefix.tn); + DELIMIT_CURR; + MEMCPY_LIT(curr, PIDS_DELIM); + curr += STR_LIT_LEN(PIDS_DELIM); + curr = (char *)i2ascl((uchar_ptr_t)curr, rec->jrec_set_kill.token_seq.jnl_seqno); + if (rectype == JRT_NULL) + { + *curr++ = '\n'; + *curr='\0'; + return curr; + } + DELIMIT_CURR; + /* print "update_num" */ + assert(IS_SET_KILL_ZKILL_ZTRIG_ZTWORM(rectype)); + assert(&rec->jrec_set_kill.update_num == &rec->jrec_ztworm.update_num); + curr = (char *)i2ascl((uchar_ptr_t)curr, rec->jrec_set_kill.update_num); + DELIMIT_CURR; + if (IS_ZTWORM(rectype)) + { + ztwormstr = &rec->jrec_ztworm.ztworm_str; + val_len = ztwormstr->length; + val_ptr = &ztwormstr->text[0]; + format2zwr((sm_uc_ptr_t)val_ptr, val_len, (uchar_ptr_t)curr, &val_extr_len); + curr += val_extr_len; + *curr++ = '\n'; + *curr='\0'; + return curr; + } + /* print "nodeflags" */ + keystr = (jnl_string *)&rec->jrec_set_kill.mumps_node; + curr = (char *)i2ascl((uchar_ptr_t)curr, keystr->nodeflags); + DELIMIT_CURR; + /* print "node" */ + key = (gv_key *)ROUND_UP((unsigned long)key_buff, 8); + key->top = MAX_KEY_SZ; + key->end = keystr->length; + if (key->end > key->top) + { + assert(FALSE); + return ext_buff; + } + memcpy(key->base, &keystr->text[0], keystr->length); + key->base[key->end] = 0; + curr = (char *)format_targ_key((uchar_ptr_t)curr, MAX_ZWR_KEY_SZ, key, TRUE); + if (IS_SET(rectype)) + { + *curr++ = '='; + val_ptr = &keystr->text[keystr->length]; + GET_MSTR_LEN(val_len, val_ptr); + val_ptr += SIZEOF(mstr_len_t); + format2zwr((sm_uc_ptr_t)val_ptr, val_len, (uchar_ptr_t)curr, &val_extr_len); + curr += val_extr_len; + } + *curr++ = '\n'; + *curr='\0'; + return curr; +} diff --git a/sr_port/jnl_ensure_open.c b/sr_port/jnl_ensure_open.c new file mode 100644 index 0000000..538d98d --- /dev/null +++ b/sr_port/jnl_ensure_open.c @@ -0,0 +1,103 @@ +/**************************************************************** + * * + * Copyright 2010, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gdsdbver.h" +#include "gdsroot.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "jnl.h" +#include "iosp.h" +#include "repl_sp.h" +#include "gtmio.h" +#include "gtmimagename.h" +#include "wbox_test_init.h" +#include "gtcm_jnl_switched.h" + +GBLREF boolean_t is_src_server; +GBLREF gd_region *gv_cur_region; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; + +/* make sure that the journal file is available if appropriate */ +uint4 jnl_ensure_open(void) +{ + uint4 jnl_status; + jnl_private_control *jpc; + sgmnt_addrs *csa; + sgmnt_data_ptr_t csd; + boolean_t first_open_of_jnl, need_to_open_jnl; + int close_res; +# if defined(VMS) + static const gds_file_id file; + uint4 status; +# endif + + error_def(ERR_JNLFILOPN); + + csa = cs_addrs; + csd = csa->hdr; + assert(csa->now_crit); + jpc = csa->jnl; + assert(NULL != jpc); + assert(JNL_ENABLED(csa->hdr)); + /* The goal is to change the code below to do only one JNL_FILE_SWITCHED(jpc) check instead of the additional + * (NOJNL == jpc->channel) check done below. The assert below ensures that the NOJNL check can indeed + * be subsumed by the JNL_FILE_SWITCHED check (with the exception of the source-server which has a special case that + * needs to be fixed in C9D02-002241). Over time, this has to be changed to one check. + */ + assert((NOJNL != jpc->channel) || JNL_FILE_SWITCHED(jpc) || is_src_server); + need_to_open_jnl = FALSE; + jnl_status = 0; + if (NOJNL == jpc->channel) + { +# ifdef VMS + if (NOJNL != jpc->old_channel) + { + if (lib$ast_in_prog()) /* called from wcs_wipchk_ast */ + jnl_oper_user_ast(gv_cur_region); + else + { + status = sys$setast(DISABLE); + jnl_oper_user_ast(gv_cur_region); + if (SS$_WASSET == status) + ENABLE_AST; + } + } +# endif + need_to_open_jnl = TRUE; + } else if (JNL_FILE_SWITCHED(jpc)) + { /* The journal file has been changed "on the fly"; close the old one and open the new one */ + VMS_ONLY(assert(FALSE);) /* everyone having older jnl open should have closed it at time of switch in VMS */ + JNL_FD_CLOSE(jpc->channel, close_res); /* sets jpc->channel to NOJNL */ + need_to_open_jnl = TRUE; + } + if (need_to_open_jnl) + { + /* Whenever journal file get switch, reset the pini_addr and new_freeaddr. */ + jpc->pini_addr = 0; + jpc->new_freeaddr = 0; + if (IS_GTCM_GNP_SERVER_IMAGE) + gtcm_jnl_switched(jpc->region); /* Reset pini_addr of all clients that had any older journal file open */ + UNIX_ONLY(first_open_of_jnl = (0 == csa->nl->jnl_file.u.inode);) + VMS_ONLY(first_open_of_jnl = (0 == memcmp(csa->nl->jnl_file.jnl_file_id.fid, file.fid, SIZEOF(file.fid)))); + jnl_status = jnl_file_open(gv_cur_region, first_open_of_jnl, NULL); + } + DEBUG_ONLY( + else + GTM_WHITE_BOX_TEST(WBTEST_JNL_FILE_OPEN_FAIL, jnl_status, ERR_JNLFILOPN); + ) + assert((0 != jnl_status) || !JNL_FILE_SWITCHED(jpc) + UNIX_ONLY(|| (is_src_server && !JNL_ENABLED(csa) && REPL_WAS_ENABLED(csa)))); + return jnl_status; +} diff --git a/sr_port/jnl_file_close.c b/sr_port/jnl_file_close.c new file mode 100644 index 0000000..034aecf --- /dev/null +++ b/sr_port/jnl_file_close.c @@ -0,0 +1,173 @@ +/**************************************************************** + * * + * Copyright 2003, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_time.h" +#include "gtm_string.h" + +#if defined(UNIX) +#include "gtm_unistd.h" +#include "aswp.h" +#include "lockconst.h" +#include "interlock.h" +#include "sleep_cnt.h" +#include "performcaslatchcheck.h" +#include "wcs_sleep.h" +#include "gt_timer.h" +#elif defined(VMS) +#include +#include +#include +#include +#include +#include "iosb_disk.h" +#endif + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "jnl.h" +#include "jnl_write.h" +#include "gtmio.h" +#include "repl_sp.h" /* F_CLOSE */ +#include "iosp.h" /* for SS_NORMAL */ +#include "ccp.h" +#include "send_msg.h" +#include "eintr_wrappers.h" + +#ifdef UNIX +#include "wcs_clean_dbsync.h" +#endif + +#if defined(VMS) +GBLREF short astq_dyn_avail; +static const unsigned short zero_fid[3]; +#endif + +void jnl_file_close(gd_region *reg, bool clean, bool dummy) +{ + jnl_file_header *header; + unsigned char hdr_base[REAL_JNL_HDR_LEN + MAX_IO_BLOCK_SIZE]; + sgmnt_addrs *csa; + sgmnt_data_ptr_t csd; + jnl_private_control *jpc; + jnl_buffer_ptr_t jb; + struct_jrec_eof eof_record; + off_jnl_t eof_addr; + uint4 status, read_write_size; + int rc, save_errno; + uint4 jnl_fs_block_size; + + error_def(ERR_JNLCLOSE); + error_def(ERR_JNLFLUSH); + error_def(ERR_JNLFSYNCERR); + error_def(ERR_JNLWRERR); + error_def(ERR_PREMATEOF); + error_def(ERR_TEXT); + + csa = &FILE_INFO(reg)->s_addrs; + csd = csa->hdr; + assert(!clean || csa->now_crit || (csd->clustered && (CCST_CLOSED == csa->nl->ccp_state))); + DEBUG_ONLY( + if (clean) + ASSERT_JNLFILEID_NOT_NULL(csa); + ) + jpc = csa->jnl; +#if defined(UNIX) + if (csa->dbsync_timer) + CANCEL_DBSYNC_TIMER(csa, FALSE); +#elif defined(VMS) + /* See comment about ordering of the two statements below, in similar code in gds_rundown */ + if (csa->dbsync_timer) + { + csa->dbsync_timer = FALSE; + ++astq_dyn_avail; + } + sys$cantim(csa, PSL$C_USER); /* cancel all dbsync-timers for this region */ +#endif + if ((NULL == jpc) || (NOJNL == jpc->channel)) + return; + jb = jpc->jnl_buff; + jnl_fs_block_size = jb->fs_block_size; + header = (jnl_file_header *)(ROUND_UP2((uintszofptr_t)hdr_base, jnl_fs_block_size)); + if (clean) + { + jnl_write_eof_rec(csa, &eof_record); + if (SS_NORMAL != (jpc->status = jnl_flush(reg))) + { + send_msg(VARLSTCNT(9) ERR_JNLFLUSH, 2, JNL_LEN_STR(csd), + ERR_TEXT, 2, RTS_ERROR_TEXT("Error with journal flush during jnl_file_close"), + jpc->status); + assert(FALSE); + rts_error(VARLSTCNT(9) ERR_JNLFLUSH, 2, JNL_LEN_STR(csd), + ERR_TEXT, 2, RTS_ERROR_TEXT("Error with journal flush during jnl_file_close"), + jpc->status); + } + assert(jb->dskaddr == jb->freeaddr); + UNIX_ONLY(jnl_fsync(reg, jb->dskaddr);) + UNIX_ONLY(assert(jb->freeaddr == jb->fsync_dskaddr);) + eof_addr = jb->freeaddr - EOF_RECLEN; + read_write_size = ROUND_UP2(REAL_JNL_HDR_LEN, jnl_fs_block_size); + assert((unsigned char *)header + read_write_size <= ARRAYTOP(hdr_base)); + DO_FILE_READ(jpc->channel, 0, header, read_write_size, jpc->status, jpc->status2); + if (SYSCALL_SUCCESS(jpc->status)) + { + assert(header->end_of_data <= eof_addr); + header->end_of_data = eof_addr; + header->eov_timestamp = eof_record.prefix.time; + assert(header->eov_timestamp >= header->bov_timestamp); + header->eov_tn = eof_record.prefix.tn; + assert(header->eov_tn >= header->bov_tn); + header->end_seqno = eof_record.jnl_seqno; + header->crash = FALSE; + DO_FILE_WRITE(jpc->channel, 0, header, read_write_size, jpc->status, jpc->status2); + if (SYSCALL_ERROR(jpc->status)) + { + assert(FALSE); + rts_error(VARLSTCNT(5) ERR_JNLWRERR, 2, JNL_LEN_STR(csd), jpc->status); + } + UNIX_ONLY( + GTM_FSYNC(jpc->channel, rc); + if (-1 == rc) + { + save_errno = errno; + send_msg(VARLSTCNT(9) ERR_JNLFSYNCERR, 2, JNL_LEN_STR(csd), + ERR_TEXT, 2, RTS_ERROR_TEXT("Error with fsync during jnl_file_close"), save_errno); + assert(FALSE); + rts_error(VARLSTCNT(9) ERR_JNLFSYNCERR, 2, JNL_LEN_STR(csd), + ERR_TEXT, 2, RTS_ERROR_TEXT("Error with fsync during jnl_file_close"), save_errno); + } + ) + } + /* jnl_file_id should be nullified only after the jnl file header has been written to disk. + * Nullifying the jnl_file_id signals that the jnl file has been switched. The replication source server + * assumes that the jnl file has been completely written to disk (including the header) before the switch is + * signalled. + */ + NULLIFY_JNL_FILE_ID(csa); + jb->cycle++; /* increment shared cycle so all future callers of jnl_ensure_open recognize journal switch */ + } + JNL_FD_CLOSE(jpc->channel, rc); /* sets jpc->channel to NOJNL */ + jpc->cycle--; /* decrement cycle so jnl_ensure_open() knows to reopen the journal */ + VMS_ONLY(jpc->qio_active = FALSE;) + jpc->pini_addr = 0; + if (clean && (SS_NORMAL != jpc->status)) + { + status = jpc->status; /* jnl_send_oper resets jpc->status, so save it */ + jnl_send_oper(jpc, ERR_JNLCLOSE); + rts_error(VARLSTCNT(5) ERR_JNLCLOSE, 2, JNL_LEN_STR(csd), status); + } +} diff --git a/sr_port/jnl_file_lost.c b/sr_port/jnl_file_lost.c new file mode 100644 index 0000000..de8c029 --- /dev/null +++ b/sr_port/jnl_file_lost.c @@ -0,0 +1,125 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_inet.h" +#ifdef VMS +#include +#include +#include +#include +#include +#endif + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "ccp.h" +#include "jnl.h" +#ifdef VMS +#include "locks.h" +#endif +#include "send_msg.h" +#include "repl_msg.h" +#include "gtmsource.h" + +GBLREF jnlpool_addrs jnlpool; +GBLREF gd_region *gv_cur_region; +GBLREF volatile boolean_t in_wcs_recover; +GBLREF boolean_t ok_to_UNWIND_in_exit_handling; +GBLREF int process_exiting; + +static const unsigned short zero_fid[3]; + +uint4 jnl_file_lost(jnl_private_control *jpc, uint4 jnl_stat) +{ /* Notify operator and terminate journaling */ + unsigned int status; + sgmnt_addrs *csa; + seq_num reg_seqno, jnlseqno; + bool was_lockid = FALSE; + + error_def(ERR_REPLJNLCLOSED); + error_def(ERR_JNLCLOSED); + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + switch(jpc->region->dyn.addr->acc_meth) + { + case dba_mm: + case dba_bg: + csa = &FILE_INFO(jpc->region)->s_addrs; + break; + default: + GTMASSERT; + } +#ifdef VMS + /* The following assert has been removed as it could be FALSE if the caller is "jnl_file_extend" + * assert(0 != memcmp(csa->nl->jnl_file.jnl_file_id.fid, zero_fid, SIZEOF(zero_fid))); + */ +#endif + assert(csa->now_crit); + if (JNL_FILE_LOST_ERRORS == TREF(error_on_jnl_file_lost)) + { +#ifdef VMS + assert(FALSE); /* Not fully implemented / supported on VMS. */ +#endif + if (!process_exiting || !csa->jnl->error_reported) + { + csa->jnl->error_reported = TRUE; + in_wcs_recover = FALSE; /* in case we're called in wcs_recover() */ + DEBUG_ONLY(ok_to_UNWIND_in_exit_handling = TRUE); + if (SS_NORMAL != jpc->status) + rts_error(VARLSTCNT(7) jnl_stat, 4, JNL_LEN_STR(csa->hdr), DB_LEN_STR(gv_cur_region), jpc->status); + else + rts_error(VARLSTCNT(6) jnl_stat, 4, JNL_LEN_STR(csa->hdr), DB_LEN_STR(gv_cur_region)); + } + return jnl_stat; + } + if (0 != jnl_stat) + jnl_send_oper(jpc, jnl_stat); + csa->hdr->jnl_state = jnl_closed; + jpc->jnl_buff->cycle++; /* increment shared cycle so all future callers of jnl_ensure_open recognize journal switch */ + assert(jpc->cycle < jpc->jnl_buff->cycle); + if (REPL_ENABLED(csa->hdr)) + { + csa->hdr->repl_state = repl_was_open; + reg_seqno = csa->hdr->reg_seqno; + jnlseqno = (NULL != jnlpool.jnlpool_ctl) ? jnlpool.jnlpool_ctl->jnl_seqno : MAX_SEQNO; + send_msg(VARLSTCNT(8) ERR_REPLJNLCLOSED, 6, DB_LEN_STR(jpc->region), ®_seqno, ®_seqno, &jnlseqno, &jnlseqno); + } else + send_msg(VARLSTCNT(5) ERR_JNLCLOSED, 3, DB_LEN_STR(jpc->region), &csa->ti->curr_tn); +#ifdef VMS + /* We can get a jnl_file_lost before the file is even created, so locking is done only if the lock exist */ + if (0 != csa->jnl->jnllsb->lockid) + { + was_lockid = TRUE; + status = gtm_enqw(EFN$C_ENF, LCK$K_EXMODE, csa->jnl->jnllsb, LCK$M_CONVERT | LCK$M_NODLCKBLK, + NULL, 0, NULL, 0, NULL, PSL$C_USER, 0); + if (SS$_NORMAL == status) + status = csa->jnl->jnllsb->cond; + } + jnl_file_close(jpc->region, FALSE, FALSE); + if (was_lockid) + { + if (SS$_NORMAL == status) + status = gtm_deq(csa->jnl->jnllsb->lockid, NULL, PSL$C_USER, 0); + if (SS$_NORMAL != status) + GTMASSERT; + } +# else + jnl_file_close(jpc->region, FALSE, FALSE); +#endif + return EXIT_NRM; +} diff --git a/sr_port/jnl_file_open_common.c b/sr_port/jnl_file_open_common.c new file mode 100644 index 0000000..8e6064f --- /dev/null +++ b/sr_port/jnl_file_open_common.c @@ -0,0 +1,272 @@ +/**************************************************************** + * * + * Copyright 2003, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stat.h" +#include "gtm_string.h" +#include "gtm_time.h" +#include "gtm_inet.h" +#if defined(UNIX) +#include +#include "gtm_fcntl.h" +#include "gtm_unistd.h" +#include "interlock.h" +#include "lockconst.h" +#include "aswp.h" +#elif defined(VMS) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "iosb_disk.h" +#endif + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "jnl.h" +#include "gtmio.h" +#include "eintr_wrappers.h" +#include "repl_msg.h" +#include "gtmsource.h" +#include "is_file_identical.h" +#include "gtmmsg.h" +#include "send_msg.h" +#include "repl_sp.h" +#include "iosp.h" /* for SS_NORMAL */ +#include "get_fs_block_size.h" + +GBLREF jnlpool_ctl_ptr_t jnlpool_ctl; +GBLREF boolean_t pool_init; +GBLREF jnl_process_vector *prc_vec; +GBLREF jnl_gbls_t jgbl; + +error_def(ERR_FILEIDMATCH); +error_def(ERR_JNLOPNERR); +error_def(ERR_JNLRDERR); +error_def(ERR_JNLBADRECFMT); +error_def(ERR_JNLRECTYPE); +error_def(ERR_JNLTRANSGTR); +error_def(ERR_JNLTRANSLSS); +error_def(ERR_JNLWRERR); +error_def(ERR_JNLVSIZE); +error_def(ERR_PREMATEOF); +error_def(ERR_JNLPREVRECOV); +#ifdef GTM_CRYPT +error_def(ERR_CRYPTJNLWRONGHASH); +#endif + +/* note: returns 0 on success */ +uint4 jnl_file_open_common(gd_region *reg, off_jnl_t os_file_size) +{ + sgmnt_addrs *csa; + sgmnt_data_ptr_t csd; + jnl_private_control *jpc; + jnl_buffer_ptr_t jb; + jnl_file_header *header; + unsigned char hdr_buff[REAL_JNL_HDR_LEN + MAX_IO_BLOCK_SIZE]; + struct_jrec_eof eof_record; /* pointer is in an attempt to use make code portable */ + unsigned char *eof_rec_buffer; + unsigned char eof_rec[(DISK_BLOCK_SIZE * 2) + MAX_IO_BLOCK_SIZE]; + off_jnl_t adjust; +#if defined(VMS) + io_status_block_disk iosb; +#endif + uint4 jnl_fs_block_size, read_write_size, read_size; + + csa = &FILE_INFO(reg)->s_addrs; + csd = csa->hdr; + jpc = csa->jnl; + jb = jpc->jnl_buff; + jpc->status = jpc->status2 = SS_NORMAL; + jnl_fs_block_size = get_fs_block_size(jpc->channel); + /* check that the filesystem block size is a power of 2 as we do a lot of calculations below assuming this is the case */ + assert(!(jnl_fs_block_size & (jnl_fs_block_size - 1))); + header = (jnl_file_header *)(ROUND_UP2((uintszofptr_t)hdr_buff, jnl_fs_block_size)); + eof_rec_buffer = (unsigned char *)(ROUND_UP2((uintszofptr_t)eof_rec, jnl_fs_block_size)); + /* Read the journal file header */ + read_write_size = ROUND_UP2(REAL_JNL_HDR_LEN, jnl_fs_block_size); + assert((unsigned char *)header + read_write_size <= ARRAYTOP(hdr_buff)); + DO_FILE_READ(jpc->channel, 0, header, read_write_size, jpc->status, jpc->status2); + if (SS_NORMAL != jpc->status) + { /* A PREMATEOF error is possible in Unix if a V54001 version is trying to open a pre-V54001 journal file + * This is because starting V54001, the journal file size is always maintained as a multiple of the underlying + * filesystem block size. And so in case of a previous version created journal file, it is possible the + * entire unaligned journal file size is lesser than the aligned journal file header size. + */ + UNIX_ONLY(assert(ERR_PREMATEOF == jpc->status);) + VMS_ONLY(assert(FALSE);) + return ERR_JNLRDERR; + } + /* Check if the header format matches our format. Cannot access any fields inside header unless this matches */ + CHECK_JNL_FILE_IS_USABLE(header, jpc->status, FALSE, 0, NULL); /* FALSE => NO gtm_putmsg even if errors */ + if (SS_NORMAL != jpc->status) + return ERR_JNLOPNERR; + adjust = header->end_of_data & (jnl_fs_block_size - 1); + /* Read the journal JRT_EOF at header->end_of_data offset. + * Make sure the buffer being read to is big enough and that as part of the read, + * we never touch touch the journal file header territory. + */ + read_size = ROUND_UP2((EOF_RECLEN + adjust), jnl_fs_block_size); + assert(eof_rec_buffer + read_size <= ARRAYTOP(eof_rec)); + assert(header->end_of_data - adjust >= JNL_HDR_LEN); + DO_FILE_READ(jpc->channel, header->end_of_data - adjust, eof_rec_buffer, read_size, jpc->status, jpc->status2); + if (SS_NORMAL != jpc->status) + { + assert(FALSE); + return ERR_JNLRDERR; + } + if (header->prev_recov_end_of_data) + { + /* not possible for run time. In case it happens user must fix it */ + jpc->status = ERR_JNLPREVRECOV; + return ERR_JNLOPNERR; + } + if (!is_gdid_file_identical(&FILE_ID(reg), (char *)header->data_file_name, header->data_file_name_length)) + { + rts_error(VARLSTCNT(7) ERR_JNLOPNERR, 4, JNL_LEN_STR(csd), DB_LEN_STR(reg), ERR_FILEIDMATCH); + assert(FALSE); /* we dont expect the rts_error in the line above to return */ + return ERR_JNLOPNERR; + } + memcpy(&eof_record, (unsigned char *)eof_rec_buffer + adjust, EOF_RECLEN); + if (JRT_EOF != eof_record.prefix.jrec_type) + { + jpc->status = ERR_JNLRECTYPE; + return ERR_JNLOPNERR; + } + if (eof_record.prefix.tn != csd->trans_hist.curr_tn) + { + if (eof_record.prefix.tn < csd->trans_hist.curr_tn) + jpc->status = ERR_JNLTRANSLSS; + else + jpc->status = ERR_JNLTRANSGTR; + return ERR_JNLOPNERR; + } + if (eof_record.suffix.suffix_code != JNL_REC_SUFFIX_CODE || + eof_record.suffix.backptr != eof_record.prefix.forwptr) + { + jpc->status = ERR_JNLBADRECFMT; + return ERR_JNLOPNERR; + } + GTMCRYPT_ONLY( + if (memcmp(header->encryption_hash, csd->encryption_hash, GTMCRYPT_HASH_LEN)) + { + send_msg(VARLSTCNT(6) ERR_CRYPTJNLWRONGHASH, 4, JNL_LEN_STR(csd), DB_LEN_STR(reg)); + jpc->status = ERR_CRYPTJNLWRONGHASH; + return ERR_JNLOPNERR; + } + ) + assert(header->eov_tn == eof_record.prefix.tn); + header->eov_tn = eof_record.prefix.tn; + assert(header->eov_timestamp == eof_record.prefix.time); + header->eov_timestamp = eof_record.prefix.time; + assert(header->eov_timestamp >= header->bov_timestamp); + assert(((off_jnl_t)os_file_size) % JNL_REC_START_BNDRY == 0); + assert(((off_jnl_t)os_file_size) % DISK_BLOCK_SIZE == 0); + assert(((off_jnl_t)os_file_size) % jnl_fs_block_size == 0); + if ((ROUND_UP2((header->virtual_size * DISK_BLOCK_SIZE), jnl_fs_block_size) < os_file_size) + || (header->jnl_deq && 0 != ((header->virtual_size - header->jnl_alq) % header->jnl_deq))) + { + send_msg(VARLSTCNT(8) ERR_JNLVSIZE, 6, JNL_LEN_STR(csd), header->virtual_size, + header->jnl_alq, header->jnl_deq, os_file_size, jnl_fs_block_size); + jpc->status = ERR_JNLVSIZE; + return ERR_JNLOPNERR; + } + /* For performance reasons (to be able to do aligned writes to the journal file), we need to ensure the journal buffer + * address is filesystem-block-size aligned in Unix. Although this is needed only in case of sync_io/direct-io, we ensure + * this alignment unconditionally in Unix. jb->buff_off is the number of bytes to go past before getting an aligned buffer. + * For VMS, this performance enhancement is currently not done and can be revisited later. + */ + UNIX_ONLY(jb->buff_off = (uintszofptr_t)ROUND_UP2((uintszofptr_t)&jb->buff[0], jnl_fs_block_size) + - (uintszofptr_t)&jb->buff[0];) + VMS_ONLY(jb->buff_off = 0;) + jb->size = ROUND_DOWN2(csd->jnl_buffer_size * DISK_BLOCK_SIZE - jb->buff_off, jnl_fs_block_size); + /* Assert that journal buffer does NOT spill past the allocated journal buffer size in shared memory */ + assert((sm_uc_ptr_t)&jb->buff[jb->buff_off + jb->size] < ((sm_uc_ptr_t)csa->nl + NODE_LOCAL_SPACE + JNL_SHARE_SIZE(csd))); + assert((sm_uc_ptr_t)jb == ((sm_uc_ptr_t)csa->nl + NODE_LOCAL_SPACE + JNL_NAME_EXP_SIZE)); + jb->freeaddr = jb->dskaddr = UNIX_ONLY(jb->fsync_dskaddr = ) header->end_of_data; + jb->fs_block_size = jnl_fs_block_size; + /* The following is to make sure that the data in jnl_buffer is aligned with the data in the + * disk file on an jnl_fs_block_size boundary. Since we assert that jb->size is a multiple of jnl_fs_block_size, + * alignment with respect to jb->size implies alignment with jnl_fs_block_size. + */ + assert(0 == (jb->size % jnl_fs_block_size)); + jb->free = jb->dsk = header->end_of_data % jb->size; + UNIX_ONLY( + SET_LATCH_GLOBAL(&jb->fsync_in_prog_latch, LOCK_AVAILABLE); + SET_LATCH_GLOBAL(&jb->io_in_prog_latch, LOCK_AVAILABLE); + ) + VMS_ONLY( + assert(0 == jb->now_writer); + bci(&jb->io_in_prog); + jb->now_writer = 0; + assert((jb->free % DISK_BLOCK_SIZE) == adjust); + ) + assert(0 == (jnl_fs_block_size % DISK_BLOCK_SIZE)); + if (adjust) + { /* if jb->free does not start at a filesystem-block-size aligned boundary (which is the alignment granularity used + * by "jnl_output_sp" for flushing to disk), copy as much pre-existing data from the journal file as necessary into + * the journal buffer to fill the gap so we do not lose this information in the next write to disk. + */ + memcpy(&jb->buff[ROUND_DOWN2(jb->free, jnl_fs_block_size) + jb->buff_off], eof_rec_buffer, adjust); + } + jb->filesize = header->virtual_size; + jb->min_write_size = JNL_MIN_WRITE; + jb->max_write_size = JNL_MAX_WRITE; + jb->before_images = header->before_images; + jb->epoch_tn = eof_record.prefix.tn; + csd->jnl_checksum = header->checksum; + LOG2_OF_INTEGER(header->alignsize, jb->log2_of_alignsize); + assert(header->autoswitchlimit == csd->autoswitchlimit); + assert(header->jnl_alq == csd->jnl_alq); + assert(header->jnl_deq == csd->jnl_deq); + assert(csd->autoswitchlimit >= csd->jnl_alq); + assert(ALIGNED_ROUND_UP(csd->autoswitchlimit, csd->jnl_alq, csd->jnl_deq) == csd->autoswitchlimit); + assert(csd->autoswitchlimit); + JNL_WHOLE_TIME(prc_vec->jpv_time); + jb->epoch_interval = header->epoch_interval; + jb->next_epoch_time = (uint4)(MID_TIME(prc_vec->jpv_time) + jb->epoch_interval); + jb->max_jrec_len = header->max_jrec_len; + memcpy(&header->who_opened, prc_vec, SIZEOF(jnl_process_vector)); + header->crash = TRUE; /* in case this processes is crashed, this will remain TRUE */ + VMS_ONLY( + if (REPL_ENABLED(csd) && pool_init) + header->update_disabled = jnlpool_ctl->upd_disabled; + ) + DO_FILE_WRITE(jpc->channel, 0, header, read_write_size, jpc->status, jpc->status2); + if (SS_NORMAL != jpc->status) + { + assert(FALSE); + return ERR_JNLWRERR; + } + if (!jb->prev_jrec_time || !header->prev_jnl_file_name_length) + { /* This is the first time a journal file for this database is being opened OR the previous link is NULL. + * In both these cases, we dont know or care about the timestamp of the last written journal record. + * Set it to the current time as we know it. + */ + jb->prev_jrec_time = jgbl.gbl_jrec_time; + } + jb->end_of_data = 0; + jb->eov_tn = 0; + jb->eov_timestamp = 0; + jb->end_seqno = 0; + return 0; +} diff --git a/sr_port/jnl_file_open_switch.c b/sr_port/jnl_file_open_switch.c new file mode 100644 index 0000000..976ea25 --- /dev/null +++ b/sr_port/jnl_file_open_switch.c @@ -0,0 +1,77 @@ +/**************************************************************** + * * + * Copyright 2003, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gtm_time.h" +#include "gtm_unistd.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "jnl.h" +#include "send_msg.h" +#include "gtmio.h" +#include "repl_sp.h" +#include "iosp.h" /* for SS_NORMAL */ + +GBLREF jnl_gbls_t jgbl; + +error_def(ERR_JNLFILOPN); +error_def(ERR_JNLINVALID); +error_def(ERR_PREVJNLLINKCUT); + +uint4 jnl_file_open_switch(gd_region *reg, uint4 sts) +{ + sgmnt_addrs *csa; + jnl_private_control *jpc; + jnl_create_info create; + char prev_jnl_fn[JNL_NAME_SIZE]; + int status; + + csa = &FILE_INFO(reg)->s_addrs; + jpc = csa->jnl; + + assert((ERR_JNLFILOPN != sts) && (NOJNL != jpc->channel) || (ERR_JNLFILOPN == sts) && (NOJNL == jpc->channel)); + if (NOJNL != jpc->channel) + JNL_FD_CLOSE(jpc->channel, status); /* sets jpc->channel to NOJNL */ + jnl_send_oper(jpc, sts); + /* attempt to create a new journal file */ + memset(&create, 0, SIZEOF(create)); + create.status = create.status2 = SS_NORMAL; + create.prev_jnl = &prev_jnl_fn[0]; + set_jnl_info(reg, &create); + create.no_prev_link = TRUE; + create.no_rename = FALSE; + assert(!jgbl.forw_phase_recovery); + if (!jgbl.dont_reset_gbl_jrec_time) + SET_GBL_JREC_TIME; /* needed for cre_jnl_file() */ + /* else mur_output_record() would have already set jgbl.gbl_jrec_time */ + assert(jgbl.gbl_jrec_time); + if (EXIT_NRM != cre_jnl_file(&create)) + { + jpc->status = create.status; + jpc->status2 = create.status2; + return ERR_JNLINVALID; + } else + { + jpc->status = SS_NORMAL; + csa->hdr->jnl_checksum = create.checksum; + csa->hdr->jnl_eovtn = csa->hdr->trans_hist.curr_tn; + } + send_msg(VARLSTCNT(6) ERR_PREVJNLLINKCUT, 4, JNL_LEN_STR(csa->hdr), DB_LEN_STR(reg)); + assert(csa->hdr->jnl_file_len == create.jnl_len); + assert(0 == memcmp(csa->hdr->jnl_file_name, create.jnl, create.jnl_len)); + return 0; +} diff --git a/sr_port/jnl_flush.c b/sr_port/jnl_flush.c new file mode 100644 index 0000000..0f7c42a --- /dev/null +++ b/sr_port/jnl_flush.c @@ -0,0 +1,47 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "iosp.h" +#include "jnl.h" +#include "wbox_test_init.h" + +GBLREF uint4 process_id; + +uint4 jnl_flush(gd_region *reg) +{ + sgmnt_addrs *csa; + jnl_private_control *jpc; + jnl_buffer_ptr_t jb; + uint4 status; + + if (!reg || !reg->open) + return SS_NORMAL; + csa = &FILE_INFO(reg)->s_addrs; + assert(csa->now_crit); + jpc = csa->jnl; + if (!JNL_ENABLED(csa->hdr) || (NULL == jpc) || (NOJNL == jpc->channel)) + return SS_NORMAL; + jb = jpc->jnl_buff; + jb->blocked = process_id; + status = (jb->freeaddr != jb->dskaddr) ? jnl_write_attempt(jpc, jb->freeaddr) : SS_NORMAL; + assert(((SS_NORMAL == status) && (jb->dskaddr == jb->freeaddr)) + || (gtm_white_box_test_case_enabled && (WBTEST_JNL_FILE_LOST_DSKADDR == gtm_white_box_test_case_number))); + jb->blocked = 0; + return status; +} diff --git a/sr_port/jnl_format.c b/sr_port/jnl_format.c new file mode 100644 index 0000000..db2c105 --- /dev/null +++ b/sr_port/jnl_format.c @@ -0,0 +1,333 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include /* for offsetof() macro */ +#include "gtm_string.h" +#include "gdsroot.h" +#include "gdskill.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "cdb_sc.h" +#include "min_max.h" /* needed for gdsblkops.h */ +#include "gdsblkops.h" +#include "jnl.h" +#include "gdscc.h" +#include "iosp.h" +#include "mdefsp.h" +#include "ccp.h" +#include "buddy_list.h" /* needed for tp.h */ +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" +#include "copy.h" +#include "jnl_get_checksum.h" +#include "gdsblk.h" /* for blk_hdr usage in JNL_MAX_SET_KILL_RECLEN macro */ + +#ifdef GTM_CRYPT +#include "gtmcrypt.h" +#endif + +#ifdef GTM_TRIGGER +/* In case of a ZTWORMHOLE, it should be immediately followed by a SET or KILL record. We do not maintain different + * update_num values for the ZTWORMHOLE and its corresponding SET or KILL record. So we should decrement the + * update_num before returning from this function in the hope that the next time jnl_format is called for the SET + * or KILL, update_num will be incremented thereby using the exact same value that was used for the ZTWORMHOLE record. + * An exception is journal recovery forward phase in which case, we dont do any increments of jgbl.tp_ztp_jnl_upd_num + * so we should do no decrements either. + */ +#define ZTWORM_DECR_UPD_NUM { if (!jgbl.forw_phase_recovery) jgbl.tp_ztp_jnl_upd_num--; } + +#define SET_PREV_ZTWORM_JFB_IF_NEEDED(is_ztworm_rec, src_ptr) \ +{ \ + if (is_ztworm_rec && !jgbl.forw_phase_recovery) \ + { \ + jgbl.save_ztworm_ptr = jgbl.prev_ztworm_ptr; \ + jgbl.prev_ztworm_ptr = (unsigned char *)src_ptr; \ + ZTWORM_DECR_UPD_NUM; \ + } \ +} +#else +#define SET_PREV_ZTWORM_JFB_IF_NEEDED(is_ztworm_rec, src_ptr) +#endif + +GBLREF gd_region *gv_cur_region; +GBLREF uint4 dollar_tlevel; +GBLREF jnl_fence_control jnl_fence_ctl; +GBLREF sgm_info *sgm_info_ptr; +GBLREF jnl_format_buffer *non_tp_jfb_ptr; +GBLREF jnl_gbls_t jgbl; +#ifdef GTM_TRIGGER +GBLREF int4 gtm_trigger_depth; +GBLREF int4 tstart_trigger_depth; +#endif + +/* Do NOT define first dimension of jnl_opcode array to be JA_MAX_TYPES. Instead let compiler fill in the value according + * to the number of rows actually specified in the array. This way, if ever a new entry is added in jnl.h to jnl_action_code + * (thereby increasing JA_MAX_TYPES) but is forgotten to add a corresponding row here, an assert (in this module) will fail + * indicating the inconsistency. Defining jnl_opcode[JA_MAX_TYPES][5] causes any changes to JA_MAX_TYPES to automatically + * allocate a bigger array filled with 0s which might cause one to overlook the inconsistency. + */ +static const enum jnl_record_type jnl_opcode[][5] = +{ + { JRT_KILL, JRT_FKILL, JRT_TKILL, JRT_GKILL, JRT_UKILL }, /* KILL record types */ + { JRT_SET, JRT_FSET, JRT_TSET, JRT_GSET, JRT_USET }, /* SET record types */ + { JRT_ZKILL, JRT_FZKILL, JRT_TZKILL, JRT_GZKILL, JRT_UZKILL }, /* ZKILL record types */ +# ifdef GTM_TRIGGER + { JRT_BAD, JRT_BAD, JRT_TZTWORM, JRT_BAD, JRT_UZTWORM }, /* ZTWORM record types */ + { JRT_BAD, JRT_BAD, JRT_TZTRIG, JRT_BAD, JRT_UZTRIG }, /* ZTRIG record types */ +# endif +}; + +jnl_format_buffer *jnl_format(jnl_action_code opcode, gv_key *key, mval *val, uint4 nodeflags) +{ + char *local_buffer, *mumps_node_ptr; + enum jnl_record_type rectype; + int subcode; + jnl_record *rec; + jnl_action *ja; + jnl_format_buffer *prev_jfb, *jfb, *prev_prev_jfb; + jnl_str_len_t keystrlen; + mstr_len_t valstrlen; + sgm_info *si; + sgmnt_addrs *csa; + sgmnt_data_ptr_t csd; + uint4 align_fill_size, jrec_size, tmp_jrec_size, update_length; + boolean_t is_ztworm_rec = FALSE; + DEBUG_ONLY( + static boolean_t dbg_in_jnl_format = FALSE; + ) + GTMCRYPT_ONLY(int crypt_status;) +# ifdef GTM_TRIGGER + boolean_t ztworm_matched, match_possible; + mstr prev_str, *cur_str; +# endif + + /* The below assert ensures that if ever jnl_format is interrupted by a signal, the interrupt handler never calls + * jnl_format again. This is because jnl_format plays with global pointers and we would possibly end up in a bad + * state if the interrupt handler calls jnl_format again. + */ + assert(!dbg_in_jnl_format); + DEBUG_ONLY(dbg_in_jnl_format = TRUE;) + if (jgbl.forw_phase_recovery) /* In case of recovery, copy "nodeflags" from journal record being played */ + nodeflags = jgbl.mur_jrec_nodeflags; + csa = &FILE_INFO(gv_cur_region)->s_addrs; + csd = csa->hdr; +# ifdef GTM_TRIGGER + /* If opcode is JNL_ZTWORM then check if ztwormhole operation can be avoided altogether. + * This is the case if the value of $ZTWORMHOLE passed in is identical to the value of + * $ZTWORMHOLE written for the immediately previous update stored in (global variable) jgbl.prev_ztworm_ptr + * across regions in the current TP transaction. In that case, return right away. + * For journal recovery, we skip this part since we want the ztwormhole record to be unconditionally written + * (because GT.M wrote it in the first place). + */ + is_ztworm_rec = (JNL_ZTWORM == opcode); + if (is_ztworm_rec && !jgbl.forw_phase_recovery) + { + assert(REPL_ALLOWED(csa) || jgbl.forw_phase_recovery); + assert(dollar_tlevel); + assert(tstart_trigger_depth == gtm_trigger_depth); + assert((NULL != val) && (NULL == key)); + assert(MV_IS_STRING(val)); + assert(FIXED_UPD_RECLEN == FIXED_ZTWORM_RECLEN); + if (NULL != jgbl.prev_ztworm_ptr) + { + cur_str = &val->str; + prev_str.len = (*(jnl_str_len_t *)jgbl.prev_ztworm_ptr); + prev_str.addr = (char *)(jgbl.prev_ztworm_ptr + SIZEOF(jnl_str_len_t)); + if ((prev_str.len == cur_str->len) && !memcmp(prev_str.addr, cur_str->addr, prev_str.len)) + { + DEBUG_ONLY(dbg_in_jnl_format = FALSE;) + return NULL; + } + } + } +# endif + /* Allocate a jfb structure */ + if (!dollar_tlevel) + { + jfb = non_tp_jfb_ptr; /* already malloced in gvcst_init() */ + jgbl.cumul_jnl_rec_len = 0; + DEBUG_ONLY(jgbl.cumul_index = jgbl.cu_jnl_index = 0;) + } else + { + si = sgm_info_ptr; /* reset "si" since previous set was #ifdef GTM_TRIGGER only code while this is not */ + assert(si->tp_csa == csa); + assert((NULL != si->jnl_head) || (NULL == csa->next_fenced)); + assert((NULL == si->jnl_head) || (NULL != csa->next_fenced)); + assert((NULL == csa->next_fenced) || (JNL_FENCE_LIST_END == csa->next_fenced) + || (NULL != csa->next_fenced->sgm_info_ptr->jnl_head)); + jfb = (jnl_format_buffer *)get_new_element(si->jnl_list, 1); + jfb->next = NULL; + assert(NULL != si->jnl_tail); + GTMTRIG_ONLY(SET_PREV_JFB(si, jfb->prev);) + assert(NULL == *si->jnl_tail); + *si->jnl_tail = jfb; + si->jnl_tail = &jfb->next; + si->update_trans |= UPDTRNS_JNL_LOGICAL_MASK; /* record that we are writing a logical jnl record in this region */ + if (!(nodeflags & JS_NOT_REPLICATED_MASK)) + si->update_trans |= UPDTRNS_JNL_REPLICATED_MASK; + } + ja = &(jfb->ja); + ja->operation = opcode; + ja->nodeflags = nodeflags; + /* Proceed with formatting the journal record in the allocated jfb */ + if (!jnl_fence_ctl.level && !dollar_tlevel) + { /* Non-TP */ + subcode = 0; + tmp_jrec_size = FIXED_UPD_RECLEN + JREC_SUFFIX_SIZE; + assert(0 == jgbl.tp_ztp_jnl_upd_num); + } else + { + if (NULL == csa->next_fenced) + { /* F (or T) */ + assert((NULL != jnl_fence_ctl.fence_list) || (0 == jgbl.tp_ztp_jnl_upd_num)); + subcode = 1; + csa->next_fenced = jnl_fence_ctl.fence_list; + jnl_fence_ctl.fence_list = csa; + } else /* G (or U) */ + { /* At least one call to "jnl_format" has occurred in this TP transaction already. We therefore + * expect jgbl.tp_ztp_jnl_upd_num to be non-zero at this point. The only exception is if "jnl_format" + * had been called just once before and that was for a ZTWORM type of record in which case it would be + * zero (both ZTWORM and following SET/KILL record will have the same update_num value of 1). + */ + assert(jgbl.tp_ztp_jnl_upd_num + GTMTRIG_ONLY(|| ((jfb->prev == si->jnl_head) && (JRT_TZTWORM == jfb->prev->rectype)))); + subcode = 3; + } + if (dollar_tlevel) + ++subcode; /* TP */ + tmp_jrec_size = FIXED_UPD_RECLEN + JREC_SUFFIX_SIZE; + assert(FIXED_UPD_RECLEN == FIXED_ZTWORM_RECLEN); + if (!jgbl.forw_phase_recovery) + jgbl.tp_ztp_jnl_upd_num++; + /* In case of forward phase of journal recovery, this would have already been set to appropriate value. + * It is necessary to honor the incoming jgbl value for ZTP (since recovery could be playing records + * from the middle of a ZTP transaction because the rest are before the EPOCH), but for TP it is not + * necessary since all records are guaranteed to be AFTER the EPOCH so we can generate the numbers in + * this function too. But since we expect recovery to play the TP records in the exact order in which + * GT.M wrote them no point regenerating the same set of numbers again here. So we use incoming jgbl always. + */ + } + assert(ARRAYSIZE(jnl_opcode) == JA_MAX_TYPES); + assert(JA_MAX_TYPES > opcode); + rectype = jnl_opcode[opcode][subcode]; + assert(IS_VALID_JRECTYPE(rectype)); + assert(IS_SET_KILL_ZKILL_ZTRIG_ZTWORM(rectype)); + GTMTRIG_ONLY(assert((JNL_ZTWORM != opcode) || (NULL == key));) + GTMTRIG_ONLY(assert((JNL_ZTWORM == opcode) || (NULL != key));) + /* Compute actual record length */ + if (NULL != key) + { + keystrlen = key->end; + tmp_jrec_size += keystrlen + SIZEOF(jnl_str_len_t); + } + GTMTRIG_ONLY(assert((JNL_ZTWORM != opcode) || (NULL != val));) + assert((JNL_SET != opcode) || (NULL != val)); + if (NULL != val) + { + valstrlen = val->str.len; + tmp_jrec_size += valstrlen + SIZEOF(mstr_len_t); + } + jrec_size = ROUND_UP2(tmp_jrec_size, JNL_REC_START_BNDRY); + align_fill_size = jrec_size - tmp_jrec_size; /* For JNL_REC_START_BNDRY alignment */ + if (dollar_tlevel) + { + assert((1 << JFB_ELE_SIZE_IN_BITS) == JNL_REC_START_BNDRY); + assert(JFB_ELE_SIZE == JNL_REC_START_BNDRY); + jfb->buff = (char *)get_new_element(si->format_buff_list, jrec_size >> JFB_ELE_SIZE_IN_BITS); + GTMCRYPT_ONLY( + if (REPL_ALLOWED(csa)) + jfb->alt_buff = (char *)get_new_element(si->format_buff_list, jrec_size >> JFB_ELE_SIZE_IN_BITS); + ) + /* assume an align record will be written while computing maximum jnl-rec size requirements */ + si->total_jnl_rec_size += (int)(jrec_size + MIN_ALIGN_RECLEN); + } + /* else if (!dollar_tlevel) jfb->buff/jfb->alt_buff already malloced in gvcst_init. */ + jfb->record_size = jrec_size; + jgbl.cumul_jnl_rec_len += jfb->record_size; + assert(0 == jgbl.cumul_jnl_rec_len % JNL_REC_START_BNDRY); + DEBUG_ONLY(jgbl.cumul_index++;) + jfb->rectype = rectype; + /* PREFIX */ + rec = (jnl_record *)jfb->buff; + rec->prefix.jrec_type = rectype; + assert(!IS_SET_KILL_ZKILL_ZTRIG(rectype) || (JNL_MAX_SET_KILL_RECLEN(csd) >= jrec_size)); + GTMTRIG_ONLY(assert(!IS_ZTWORM(rectype) || (MAX_ZTWORM_JREC_LEN >= jrec_size));) + rec->prefix.forwptr = jrec_size; + assert(&rec->jrec_set_kill.update_num == &rec->jrec_ztworm.update_num); + rec->jrec_set_kill.update_num = jgbl.tp_ztp_jnl_upd_num; + local_buffer = (char *)rec + FIXED_UPD_RECLEN; + mumps_node_ptr = local_buffer; + if (NULL != key) + { + ((jnl_string *)local_buffer)->length = keystrlen; + ((jnl_string *)local_buffer)->nodeflags = nodeflags; + local_buffer += SIZEOF(jnl_str_len_t); + memcpy(local_buffer, (uchar_ptr_t)key->base, keystrlen); + local_buffer += keystrlen; + } + if (NULL != val) + { + PUT_MSTR_LEN(local_buffer, valstrlen); /* SET command's data may not be aligned */ + /* The below assert ensures that it is okay for us to increment by jnl_str_len_t (uint4) + * even though valstrlen (above) is of type mstr_len_t (int). This is because PUT_MSTR_LEN + * casts the input to (uint4*) before storing it in the destination pointer (in this case + * local_buffer) + */ + assert(SIZEOF(uint4) == SIZEOF(jnl_str_len_t)); + local_buffer += SIZEOF(jnl_str_len_t); + memcpy(local_buffer, (uchar_ptr_t)val->str.addr, valstrlen); + local_buffer += valstrlen; + } + if (0 != align_fill_size) + { + memset(local_buffer, 0, align_fill_size); + local_buffer += align_fill_size; + } + /* SUFFIX */ + ((jrec_suffix *)local_buffer)->backptr = jrec_size; + ((jrec_suffix *)local_buffer)->suffix_code = JNL_REC_SUFFIX_CODE; + update_length = (jrec_size - (JREC_SUFFIX_SIZE + FIXED_UPD_RECLEN)); +# ifdef GTM_CRYPT + assert(REPL_ALLOWED(csa) || !is_ztworm_rec || jgbl.forw_phase_recovery); + if (csd->is_encrypted) + { + /* At this place we have all the components of the *SET or *KILL or *ZTWORM records filled. + * Before the variable part of the journal record gets encrypted, we make sure we copy the buff + * into alt_buff to be used in it's original form in jnl_write. + */ + if (REPL_ALLOWED(csa)) + { + memcpy(jfb->alt_buff, rec, jrec_size); + SET_PREV_ZTWORM_JFB_IF_NEEDED(is_ztworm_rec, (jfb->alt_buff + FIXED_UPD_RECLEN)); + } + /* With the fixed length computed above as FIXED_UPD_RECLEN + JREC_SUFFIX_SIZE, we encode + * the remaining buffer which consists of the *SET or *KILL or *ZTWORM components. + */ + ASSERT_ENCRYPTION_INITIALIZED; + GTMCRYPT_ENCODE_FAST(csa->encr_key_handle, mumps_node_ptr, update_length, NULL, crypt_status); + if (0 != crypt_status) + GC_RTS_ERROR(crypt_status, gv_cur_region->dyn.addr->fname); + } else +# endif + { + SET_PREV_ZTWORM_JFB_IF_NEEDED(is_ztworm_rec, mumps_node_ptr); + } + /* The below call to jnl_get_checksum makes sure that checksum computation happens AFTER the encryption (if turned on) */ + jfb->checksum = jnl_get_checksum((uint4 *)mumps_node_ptr, NULL, (int)(local_buffer - mumps_node_ptr)); + assert(0 == ((UINTPTR_T)local_buffer % SIZEOF(jrec_suffix))); + DEBUG_ONLY(dbg_in_jnl_format = FALSE;) + return jfb; +} diff --git a/sr_port/jnl_get_checksum.c b/sr_port/jnl_get_checksum.c new file mode 100644 index 0000000..1b652b0 --- /dev/null +++ b/sr_port/jnl_get_checksum.c @@ -0,0 +1,68 @@ +/**************************************************************** + * * + * Copyright 2005, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#include "mdef.h" +#include "gdsroot.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "jnl_get_checksum.h" + +/* + * Input : + * buff : Pointer to the input buffer whose checksum needs to be computed. + * bufflen : Buffer size in bytes + * + * Returns: + * Computed checksum. + * + * Algorithm: + * For every 512 (DISK_BLOCK_SIZE) byte block of the input buffer, the first 16 and last 16 bytes are considered for checksum. + * The way this is implemented is as follows. + * For the 0th 512-byte block, the first 16 bytes are considered. + * For the 1st 512-byte block, the last 16 bytes of the 0th block and the first 16 bytes of the 1st block are considered. + * This is taken as a contiguous sequence of 32-bytes (i.e. CHKSUM_SEGLEN4 uint4s) + * For the 2nd 512-byte block, the last 16 bytes of the 1st block and the first 16 bytes of the 2nd block are considered. + * This is taken as a contiguous sequence of 32-bytes (i.e. CHKSUM_SEGLEN4 uint4s) + * And so on until the input buffer length is exceeded. + * If bufflen is not divisible by 4, it is ROUNDED DOWN to the nearest 4-byte (uint4) multiple. + */ +uint4 jnl_get_checksum(uint4 *buff, sgmnt_addrs *csa, int bufflen) +{ + uint4 *top, *blk_base, *blk_top, blen, checksum; +# ifdef GTM_CRYPT + DEBUG_ONLY( + sm_uc_ptr_t orig_buff = NULL; + ) + + if (NULL != csa && (csa->hdr->is_encrypted)) + { + DBG_ENSURE_PTR_IS_VALID_GLOBUFF(csa, csa->hdr, (sm_uc_ptr_t)buff); + DEBUG_ONLY(orig_buff = (unsigned char *)buff;) + buff = (uint4 *)GDS_ANY_ENCRYPTGLOBUF(buff, csa); + DBG_ENSURE_PTR_IS_VALID_ENCTWINGLOBUFF(csa, csa->hdr, (sm_uc_ptr_t)buff); + } +# endif + checksum = INIT_CHECKSUM_SEED; + for (blen = bufflen / SIZEOF(*buff), top = buff + blen, blk_top = buff + CHKSUM_SEGLEN4 / 2; buff < top ;) + { + if (blk_top > top) + blk_top = top; + for ( ; buff < blk_top; buff++) + checksum = ADJUST_CHECKSUM(checksum, *buff); + blk_top = (uint4 *)((sm_uc_ptr_t)buff + DISK_BLOCK_SIZE); + buff = blk_top - CHKSUM_SEGLEN4; + } + /* It is theoretically possible the computed checksum calculates to 0. Since we use a 0 to imply checksum + * was never computed, we need to return a non-zero value so in this case return INIT_CHECKSUM_SEED + */ + if (!checksum) + checksum = INIT_CHECKSUM_SEED; + return checksum; +} diff --git a/sr_port/jnl_get_checksum.h b/sr_port/jnl_get_checksum.h new file mode 100644 index 0000000..f8c1ab2 --- /dev/null +++ b/sr_port/jnl_get_checksum.h @@ -0,0 +1,90 @@ +/**************************************************************** + * * + * Copyright 2005, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#ifndef __JNL_GET_CHECKSUM_H_ +#define __JNL_GET_CHECKSUM_H_ + +#define INIT_CHECKSUM_SEED 1 +#define CHKSUM_SEGLEN4 8 + +#define ADJUST_CHECKSUM(sum, num4) (((sum) >> 4) + ((sum) << 4) + (num4)) + +#ifdef UNIX +/* Include the sequence number field of the journal record as part of the checksum computation. ADJUST_CHECKSUM + * macro currently relies on 4 byte quanitites as inputs. But, the journal sequence number is an 8 byte quantity. + * To avoid multiple calls to ADJUST_CHECKSUM to include the complete 8 byte sequence number, each of which might + * take around 4-5 instructions, consider only the lower order order 4 bytes of the sequence number for the checksum + * compuation. Given that the lower order bytes are the ones that will keep changing for every transaction, this + * should suffice. + */ +#define ADJUST_CHECKSUM_WITH_SEQNO(IS_REPLICATED, CHECKSUM, SEQNO) \ +{ \ + seq_num rec_token_seq; \ + \ + if (IS_REPLICATED) \ + { \ + rec_token_seq = SEQNO; \ + CHECKSUM = ADJUST_CHECKSUM(CHECKSUM, (rec_token_seq & 0x0000FFFF)); \ + } \ +} +#else +#define ADJUST_CHECKSUM_WITH_SEQNO(IS_REPLICATED, CHECKSUM, SEQNO) +#endif + +/* This macro is to be used whenever we are computing the checksum of a block that has been acquired. */ +#define JNL_GET_CHECKSUM_ACQUIRED_BLK(cse, csd, csa, old_blk, bsize) \ +{ \ + cache_rec_ptr_t cr; \ + boolean_t cr_is_null; \ + \ + GBLREF uint4 dollar_tlevel; \ + \ + /* Record current database tn before computing checksum of acquired block. This is used \ + * later by the commit logic to determine if the block contents have changed (and hence \ + * if recomputation of checksum is necessary). For BG, we have two-phase commit where \ + * phase2 is done outside of crit. So it is possible that we note down the current database \ + * tn and then compute checksums outside of crit and then get crit and yet in the validation \ + * logic find the block header tn is LESSER than the noted dbtn (even though the block \ + * contents changed after the noted dbtn). This will cause us to falsely validate this block \ + * as not needing checksum recomputation. To ensure the checksum is recomputed inside crit, \ + * we note down a tn of 0 in case the block is locked for update (cr->in_tend is non-zero). \ + */ \ + assert((gds_t_acquired == cse->mode) || (gds_t_create == cse->mode)); \ + assert(cse->old_block == (sm_uc_ptr_t)(old_blk)); \ + assert((bsize) <= csd->blk_size); \ + /* Since this macro is invoked only in case of before-image journaling and since MM does not \ + * support before-image journaling, we can safely assert that BG is the only access method. \ + */ \ + assert(dba_bg == csd->acc_meth); \ + /* In rare cases cse->cr can be NULL even though this block is an acquired block. This is \ + * possible if we are in TP and this block was part of the tree in the initial phase of the \ + * transaction but was marked free (by another process concurrently) in the later phase of \ + * the same TP transaction. But this case is a sureshot restart situation so be safe and \ + * ensure recomputation happens inside of crit just in case we dont restart. Also add asserts \ + * (using donot_commit variable) to ensure we do restart this transaction. \ + */ \ + cr = cse->cr; \ + cr_is_null = (NULL == cr); \ + assert(!cr_is_null || dollar_tlevel); \ + DEBUG_ONLY(if (cr_is_null) TREF(donot_commit) |= DONOTCOMMIT_JNLGETCHECKSUM_NULL_CR;) \ + cse->tn = ((cr_is_null || cr->in_tend) ? 0 : csd->trans_hist.curr_tn); \ + /* If cr is NULL, it is a restartable situation. So dont waste time computing checksums. Also \ + * if the db is encrypted, we cannot get at the encryption global buffer (jnl_get_checksum \ + * requires this) since we dont even have a regular global buffer corresponding to this block \ + * so there is no way jnl_get_checksum can proceed in that case. So it is actually necessary \ + * to avoid computing checksums if cr is NULL. \ + */ \ + cse->blk_checksum = !cr_is_null ? jnl_get_checksum((uint4 *)(old_blk), csa, (bsize)) : 0; \ +} + +uint4 jnl_get_checksum(uint4 *buff, sgmnt_addrs *csa, int bufflen); +uint4 jnl_get_checksum_entire(uint4 *buff, int bufflen); + +#endif diff --git a/sr_port/jnl_get_checksum_entire.c b/sr_port/jnl_get_checksum_entire.c new file mode 100644 index 0000000..78412cf --- /dev/null +++ b/sr_port/jnl_get_checksum_entire.c @@ -0,0 +1,43 @@ +/**************************************************************** + * + * Copyright 2005, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#include "mdef.h" +#include "gdsroot.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "jnl_get_checksum.h" + +/* + * Input : + * buff : Pointer to the input buffer whose checksum needs to be computed. + * bufflen : Buffer size in bytes + * + * Returns: + * Computed checksum. + * + * Algorithm: + * It is different from the function "jnl_get_checksum" in the sense this calculates checksum of entire length passed. + * If bufflen is not divisible by 4, it is ROUNDED DOWN to the nearest 4-byte (uint4) multiple. + */ +uint4 jnl_get_checksum_entire(uint4 *buff, int bufflen) +{ + uint4 *blk_top, blen, checksum; + + checksum = INIT_CHECKSUM_SEED; + for (blen = bufflen / SIZEOF(*buff), blk_top = buff + blen ; buff < blk_top; buff++) + checksum = ADJUST_CHECKSUM(checksum, *buff); + assert(checksum); + /* It is theoretically possible that the computed checksum turns out to be 0. In order to differentiate this + * with the fact that the checksum was never computed, we returns INIT_CHECKSUM_SEED in the former case. + */ + if (!checksum) + checksum = INIT_CHECKSUM_SEED; + return checksum; +} diff --git a/sr_port/jnl_put_jrt_pfin.c b/sr_port/jnl_put_jrt_pfin.c new file mode 100644 index 0000000..19cb291 --- /dev/null +++ b/sr_port/jnl_put_jrt_pfin.c @@ -0,0 +1,46 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_string.h" +#include "gtm_time.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "jnl.h" +#include "jnl_write.h" +#include "jnl_get_checksum.h" + +GBLREF jnl_gbls_t jgbl; + +void jnl_put_jrt_pfin(sgmnt_addrs *csa) +{ + struct_jrec_pfin pfin_record; + jnl_private_control *jpc; + + assert(csa->now_crit); + jpc = csa->jnl; + assert(0 != jpc->pini_addr); + pfin_record.prefix.jrec_type = JRT_PFIN; + pfin_record.prefix.forwptr = pfin_record.suffix.backptr = PFIN_RECLEN; + pfin_record.suffix.suffix_code = JNL_REC_SUFFIX_CODE; + pfin_record.prefix.pini_addr = (0 == jpc->pini_addr) ? JNL_HDR_LEN : jpc->pini_addr; + pfin_record.prefix.tn = csa->ti->curr_tn; + /* At this point jgbl.gbl_jrec_time should be set by the caller */ + assert(jgbl.gbl_jrec_time); + pfin_record.prefix.time = jgbl.gbl_jrec_time; + pfin_record.prefix.checksum = INIT_CHECKSUM_SEED; + jnl_write(jpc, JRT_PFIN, (jnl_record *)&pfin_record, NULL, NULL); +} diff --git a/sr_port/jnl_put_jrt_pini.c b/sr_port/jnl_put_jrt_pini.c new file mode 100644 index 0000000..a3b9490 --- /dev/null +++ b/sr_port/jnl_put_jrt_pini.c @@ -0,0 +1,94 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_string.h" +#include "gtm_time.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "jnl.h" +#include "jnl_write.h" +#include "gtmimagename.h" +#include "jnl_get_checksum.h" +#include "buddy_list.h" /* needed for muprec.h */ +#include "hashtab_int4.h" /* needed for muprec.h */ +#include "hashtab_int8.h" /* needed for muprec.h */ +#include "hashtab_mname.h" /* needed for muprec.h */ +#include "muprec.h" + +GBLREF jnl_fence_control jnl_fence_ctl; +GBLREF jnl_process_vector *prc_vec; +GBLREF jnl_process_vector *originator_prc_vec; +GBLREF jnl_gbls_t jgbl; + +void jnl_put_jrt_pini(sgmnt_addrs *csa) +{ + struct_jrec_pini pini_record; + jnl_private_control *jpc; + jnl_buffer_ptr_t jbp; + struct pini_list *mur_plst; + + assert(csa->now_crit); + jpc = csa->jnl; + jbp = jpc->jnl_buff; + assert(prc_vec); + assert((csa->ti->early_tn == csa->ti->curr_tn) || (csa->ti->early_tn == csa->ti->curr_tn + 1)); + pini_record.prefix.jrec_type = JRT_PINI; + pini_record.prefix.forwptr = pini_record.suffix.backptr = PINI_RECLEN; + pini_record.suffix.suffix_code = JNL_REC_SUFFIX_CODE; + pini_record.prefix.pini_addr = jbp->freeaddr; + /* in case an ALIGN record is written before the PINI record in jnl_write(), pini_addr above is updated appropriately. */ + pini_record.prefix.tn = csa->ti->curr_tn; + /* At this point jgbl.gbl_jrec_time should be set by the caller */ + assert(jgbl.gbl_jrec_time); + pini_record.prefix.time = jgbl.gbl_jrec_time; + JNL_WHOLE_FROM_SHORT_TIME(prc_vec->jpv_time, jgbl.gbl_jrec_time); + pini_record.prefix.checksum = INIT_CHECKSUM_SEED; + /* Note that only pini_record.prefix.time is considered in mupip journal command processing. + * prc_vec->jpv_time is for accounting purpose only. Usually it is kind of redundant too. */ + if (!jgbl.forw_phase_recovery) + { + assert(NULL == jgbl.mur_pini_addr_reset_fnptr); + assert(NULL == csa->rctl); + mur_plst = NULL; + if (IS_GTCM_GNP_SERVER_IMAGE && (NULL != originator_prc_vec)) + { + memcpy((unsigned char*)&pini_record.process_vector[ORIG_JPV], + (unsigned char*)originator_prc_vec, SIZEOF(jnl_process_vector)); + } else + memset((unsigned char*)&pini_record.process_vector[ORIG_JPV], 0, SIZEOF(jnl_process_vector)); + } else + { + assert(NULL != csa->rctl); + mur_plst = csa->rctl->mur_plst; + if (NULL != jgbl.mur_pini_addr_reset_fnptr) + { + memcpy((unsigned char*)&pini_record.process_vector[ORIG_JPV], + (unsigned char *)&mur_plst->origjpv, SIZEOF(jnl_process_vector)); + } else + { /* gdsfilext done during "mur_block_count_correct" */ + assert(NULL == mur_plst); + memset((unsigned char*)&pini_record.process_vector[ORIG_JPV], 0, SIZEOF(jnl_process_vector)); + } + } + memcpy((unsigned char*)&pini_record.process_vector[CURR_JPV], (unsigned char*)prc_vec, SIZEOF(jnl_process_vector)); + jnl_write(jpc, JRT_PINI, (jnl_record *)&pini_record, NULL, NULL); + /* Note : jpc->pini_addr should not be updated until PINI record is written [C9D08-002376] */ + jpc->pini_addr = jbp->freeaddr - PINI_RECLEN; + assert(jgbl.forw_phase_recovery || (NULL == mur_plst)); + if (NULL != mur_plst) + mur_plst->new_pini_addr = jpc->pini_addr;/* note down for future forward play logical record processing */ +} diff --git a/sr_port/jnl_rec_table.h b/sr_port/jnl_rec_table.h new file mode 100644 index 0000000..13f2d86 --- /dev/null +++ b/sr_port/jnl_rec_table.h @@ -0,0 +1,52 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* New entries should be added at the end to maintain backward compatibility with previous journal files */ +/* Note: This is an exception where we have 132+ characters in a line. It is needed so that from a + * particular number we can find record type. */ + +/* +JNL_TABLE_ENTRY (rectype, extract_rtn, label, update, fixed_size, is_replicated) +*/ +JNL_TABLE_ENTRY (JRT_BAD, NULL, "*BAD* ", NA, FALSE, FALSE) /* 0: Catch-all for invalid record types (must be first) */ +JNL_TABLE_ENTRY (JRT_PINI, mur_extract_pini, "PINI ", NA, TRUE, FALSE) /* 1: Process initialization */ +JNL_TABLE_ENTRY (JRT_PFIN, mur_extract_pfin, "PFIN ", NA, TRUE, FALSE) /* 2: Process termination */ +JNL_TABLE_ENTRY (JRT_ZTCOM, mur_extract_tcom, "ZTCOM ", ZTCOMREC, TRUE, FALSE) /* 3: End of "fenced" transaction */ +JNL_TABLE_ENTRY (JRT_KILL, mur_extract_set, "KILL ", KILLREC, FALSE, TRUE) /* 4: After-image logical journal transaction */ +JNL_TABLE_ENTRY (JRT_FKILL, mur_extract_set, "FKILL ", KILLREC|FUPDREC, FALSE, FALSE) /* 5: Like KILL, but the first in a "fenced" transaction */ +JNL_TABLE_ENTRY (JRT_GKILL, mur_extract_set, "GKILL ", KILLREC|GUPDREC, FALSE, FALSE) /* 6: Like FKILL, but not the first */ +JNL_TABLE_ENTRY (JRT_SET, mur_extract_set, "SET ", SETREC, FALSE, TRUE) /* 7: After-image logical journal transaction */ +JNL_TABLE_ENTRY (JRT_FSET, mur_extract_set, "FSET ", SETREC|FUPDREC, FALSE, FALSE) /* 8: Like SET, but the first in a "fenced" transaction */ +JNL_TABLE_ENTRY (JRT_GSET, mur_extract_set, "GSET ", SETREC|GUPDREC, FALSE, FALSE) /* 9: Like FSET, but not the first */ +JNL_TABLE_ENTRY (JRT_PBLK, mur_extract_blk, "PBLK ", NA, FALSE, FALSE) /* 10: Before-image physical journal transaction */ +JNL_TABLE_ENTRY (JRT_EPOCH, mur_extract_epoch, "EPOCH ", NA, TRUE, FALSE) /* 11: A "new epoch" */ +JNL_TABLE_ENTRY (JRT_EOF, mur_extract_eof, "EOF ", NA, TRUE, FALSE) /* 12: End of file */ +JNL_TABLE_ENTRY (JRT_TKILL, mur_extract_set, "TKILL ", KILLREC|TUPDREC, FALSE, TRUE) /* 13: Like KILL, but the first in a TP transaction */ +JNL_TABLE_ENTRY (JRT_UKILL, mur_extract_set, "UKILL ", KILLREC|UUPDREC, FALSE, TRUE) /* 14: Like TKILL, but not the first */ +JNL_TABLE_ENTRY (JRT_TSET, mur_extract_set, "TSET ", SETREC|TUPDREC, FALSE, TRUE) /* 15: Like SET, but the first in a TP transaction */ +JNL_TABLE_ENTRY (JRT_USET, mur_extract_set, "USET ", SETREC|UUPDREC, FALSE, TRUE) /* 16: Like TSET, but not the first */ +JNL_TABLE_ENTRY (JRT_TCOM, mur_extract_tcom, "TCOM ", TCOMREC, TRUE, TRUE) /* 17: End of TP transaction */ +JNL_TABLE_ENTRY (JRT_ALIGN, mur_extract_align, "ALIGN ", NA, FALSE, FALSE) /* 18: Align record */ +JNL_TABLE_ENTRY (JRT_NULL, mur_extract_null, "NULL ", NA, TRUE, TRUE) /* 19: Null record */ +JNL_TABLE_ENTRY (JRT_ZKILL, mur_extract_set, "ZKILL ", ZKILLREC, FALSE, TRUE) /* 20: After-image logical journal transaction */ +JNL_TABLE_ENTRY (JRT_FZKILL, mur_extract_set, "FZKILL ", ZKILLREC|FUPDREC, FALSE, FALSE) /* 21: Like ZKILL, but the first in a "fenced" transaction */ +JNL_TABLE_ENTRY (JRT_GZKILL, mur_extract_set, "GZKILL ", ZKILLREC|GUPDREC, FALSE, FALSE) /* 22: Like FZKILL, but not the first */ +JNL_TABLE_ENTRY (JRT_TZKILL, mur_extract_set, "TZKILL ", ZKILLREC|TUPDREC, FALSE, TRUE) /* 23: Like ZKILL, but the first in a TP transaction */ +JNL_TABLE_ENTRY (JRT_UZKILL, mur_extract_set, "UZKILL ", ZKILLREC|UUPDREC, FALSE, TRUE) /* 24: Like TZKILL, but not the first */ +JNL_TABLE_ENTRY (JRT_INCTN, mur_extract_inctn, "INCTN ", NA, TRUE, FALSE) /* 25: Increment curr_tn only, no logical update */ +JNL_TABLE_ENTRY (JRT_AIMG, mur_extract_blk, "AIMG ", NA, FALSE, FALSE) /* 26: After-image physical journal transaction */ +JNL_TABLE_ENTRY (JRT_TRIPLE, NULL, "TRIPLE ", NA, TRUE, TRUE) /* 27: A REPL_NEW_TRIPLE message minus the 8-byte message + * header ("type" and "len"). Only used in the + * replication pipe. Never part of a journal file. */ +JNL_TABLE_ENTRY (JRT_TZTWORM, mur_extract_set, "TZTWORM", ZTWORMREC|TUPDREC, FALSE, TRUE) /* 28: If $ZTWORMHOLE is first record in TP */ +JNL_TABLE_ENTRY (JRT_UZTWORM, mur_extract_set, "UZTWORM", ZTWORMREC|UUPDREC, FALSE, TRUE) /* 29: Like TZTWORM but not the first record in TP */ +JNL_TABLE_ENTRY (JRT_TZTRIG, mur_extract_set, "TZTRIG ", ZTRIGREC|TUPDREC, FALSE, TRUE) /* 30: If ZTRIGGER is first record in TP */ +JNL_TABLE_ENTRY (JRT_UZTRIG, mur_extract_set, "UZTRIG ", ZTRIGREC|UUPDREC, FALSE, TRUE) /* 31: Like TZTRIG but not the first record in TP */ diff --git a/sr_port/jnl_send_oper.c b/sr_port/jnl_send_oper.c new file mode 100644 index 0000000..492ca3b --- /dev/null +++ b/sr_port/jnl_send_oper.c @@ -0,0 +1,107 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_string.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "iosp.h" +#include "jnl.h" +#include "error.h" +#include "send_msg.h" +#include "caller_id.h" + +#define ENOSPC_LOGGING_PERIOD 100 /* every 100th ENOSPC error is logged to avoid flooding the operator log */ + +GBLREF bool caller_id_flag; +GBLREF uint4 process_id; + +void jnl_send_oper(jnl_private_control *jpc, uint4 status) +{ + sgmnt_addrs *csa; + sgmnt_data_ptr_t csd; + jnl_buffer_ptr_t jb; + uint4 now_writer, fsync_pid; + int4 io_in_prog, fsync_in_prog; + boolean_t ok_to_log; /* TRUE except when we avoid flooding operator log due to ENOSPC error */ + + error_def(ERR_CALLERID); + error_def(ERR_JNLBUFINFO); + error_def(ERR_JNLPVTINFO); + error_def(ERR_JNLSENDOPER); + + switch(jpc->region->dyn.addr->acc_meth) + { + case dba_mm: + case dba_bg: + csa = &FILE_INFO(jpc->region)->s_addrs; + break; + default: + GTMASSERT; + } + csd = csa->hdr; + jb = jpc->jnl_buff; + UNIX_ONLY(assert((ENOSPC != jpc->status) || jb->enospc_errcnt);) + UNIX_ONLY(assert((SS_NORMAL == jpc->status) || (ENOSPC == jpc->status) || !jb->enospc_errcnt);) + VMS_ONLY(assert(!jb->enospc_errcnt)); /* currently not updated in VMS, so should be 0 */ + ok_to_log = (jb->enospc_errcnt ? (1 == (jb->enospc_errcnt % ENOSPC_LOGGING_PERIOD)) : TRUE); + + caller_id_flag = FALSE; + if (ok_to_log) + { + SEND_CALLERID("jnl_send_oper()"); + if (0 != status) + { + if (SS_NORMAL != jpc->status) + { + if (SS_NORMAL != jpc->status2) + { + send_msg(VARLSTCNT(14) + ERR_JNLSENDOPER, 5, process_id, status, jpc->status, jpc->status2, jb->iosb.cond, + status, 2, JNL_LEN_STR(csd), jpc->status, 0, jpc->status2); + } else + send_msg(VARLSTCNT(12) + ERR_JNLSENDOPER, 5, process_id, status, jpc->status, jpc->status2, jb->iosb.cond, + status, 2, JNL_LEN_STR(csd), jpc->status); + } else + send_msg(VARLSTCNT(11) ERR_JNLSENDOPER, 5, process_id, status, jpc->status, jpc->status2, + jb->iosb.cond, status, 2, JNL_LEN_STR(csd)); + } + } + jpc->status = SS_NORMAL; + jpc->status2 = SS_NORMAL; + UNIX_ONLY( + io_in_prog = (jb->io_in_prog_latch.u.parts.latch_pid ? TRUE : FALSE); + now_writer = jb->io_in_prog_latch.u.parts.latch_pid; + ) + VMS_ONLY( + io_in_prog = jb->io_in_prog; + now_writer = jb->now_writer; + ) + fsync_in_prog = jb->fsync_in_prog_latch.u.parts.latch_pid ? TRUE : FALSE; + fsync_pid = jb->fsync_in_prog_latch.u.parts.latch_pid; + /* note: the alignment of the parameters below is modelled on the alignment defined for JNLBUFINFO in merrors.msg */ + if (ok_to_log) + { + send_msg(VARLSTCNT(18) ERR_JNLBUFINFO, 16, process_id, + jb->dsk, jb->free, jb->bytcnt, io_in_prog, fsync_in_prog, + jb->dskaddr, jb->freeaddr, jb->qiocnt, now_writer, fsync_pid, + jb->filesize, jb->cycle, jb->errcnt, jb->wrtsize, jb->fsync_dskaddr); + send_msg(VARLSTCNT(10) ERR_JNLPVTINFO, 8, process_id, + jpc->cycle, jpc->fd_mismatch, jpc->channel, jpc->sync_io, + jpc->pini_addr, jpc->qio_active, jpc->old_channel); + } + caller_id_flag = TRUE; +} diff --git a/sr_port/jnl_setver.c b/sr_port/jnl_setver.c new file mode 100644 index 0000000..1df4fed --- /dev/null +++ b/sr_port/jnl_setver.c @@ -0,0 +1,38 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "jnl.h" + +GBLREF unsigned char jnl_ver; + +void jnl_setver(void) +{ + char *jnl_label = JNL_LABEL_TEXT; + unsigned char jnl_ver_lower, jnl_ver_higher; + + jnl_ver_lower = jnl_label[SIZEOF(JNL_LABEL_TEXT) - 2] - '0'; + assert('\012' > jnl_ver_lower); /* assert(10 > jnl_ver_lower); */ + jnl_ver_higher = jnl_label[SIZEOF(JNL_LABEL_TEXT) - 3] - '0'; + assert('\012' > jnl_ver_higher); /* assert(10 > jnl_ver_higher); */ + jnl_ver = jnl_ver_higher * 10 + jnl_ver_lower; + assert(JNL_VER_THIS == jnl_ver); + assert(JNL_VER_EARLIEST_REPL <= jnl_ver); + return; +} diff --git a/sr_port/jnl_typedef.h b/sr_port/jnl_typedef.h new file mode 100644 index 0000000..c08bcea --- /dev/null +++ b/sr_port/jnl_typedef.h @@ -0,0 +1,75 @@ +/**************************************************************** + * * + * Copyright 2003, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef JNL_TYPEDEF_H_INCLUDED +#define JNL_TYPEDEF_H_INCLUDED + +#define NA 0 +#define SETREC 0x00000001 +#define KILLREC 0x00000002 +#define ZKILLREC 0x00000004 +#define ZTWORMREC 0x00000008 +#define ZTRIGREC 0x00000800 + +#define SET_KILL_ZKILL_MASK (SETREC | KILLREC | ZKILLREC) +#define SET_KILL_ZKILL_ZTWORM_MASK (SETREC | KILLREC | ZKILLREC | ZTWORMREC) + +#define TUPDREC 0x00000010 +#define UUPDREC 0x00000020 +#define TCOMREC 0x00000040 +#define TPREC_MASK (TUPDREC | UUPDREC | TCOMREC) + +#define FUPDREC 0x00000100 +#define GUPDREC 0x00000200 +#define ZTCOMREC 0x00000400 +#define ZTPREC_MASK (FUPDREC | GUPDREC | ZTCOMREC) + +#define FENCE_MASK (TPREC_MASK | ZTPREC_MASK) + +/* the following LITREFs are needed by the below macros */ +LITREF int jrt_update[JRT_RECTYPES]; +LITREF boolean_t jrt_fixed_size[JRT_RECTYPES]; +LITREF boolean_t jrt_is_replicated[JRT_RECTYPES]; + +#define IS_VALID_RECTYPES_RANGE(rectype) ((JRT_BAD < rectype) && (JRT_RECTYPES > rectype)) +#define IS_REPLICATED(rectype) (jrt_is_replicated[rectype]) +#define IS_FIXED_SIZE(rectype) (jrt_fixed_size[rectype]) +#define IS_SET(rectype) (jrt_update[rectype] & SETREC) +#define IS_KILL(rectype) (jrt_update[rectype] & KILLREC) +#define IS_ZKILL(rectype) (jrt_update[rectype] & ZKILLREC) +#define IS_ZTWORM(rectype) (jrt_update[rectype] & ZTWORMREC) +#define IS_ZTRIG(rectype) (jrt_update[rectype] & ZTRIGREC) +#define IS_KILL_ZKILL(rectype) (jrt_update[rectype] & (KILLREC | ZKILLREC)) +#define IS_SET_KILL_ZKILL_ZTRIG(rectype) (jrt_update[rectype] & (SET_KILL_ZKILL_MASK | ZTRIGREC)) +#define IS_SET_KILL_ZKILL_ZTRIG_ZTWORM(rectype) (jrt_update[rectype] & (SET_KILL_ZKILL_ZTWORM_MASK | ZTRIGREC)) +#define IS_FENCED(rectype) (jrt_update[rectype] & FENCE_MASK) +#define IS_TP(rectype) (jrt_update[rectype] & TPREC_MASK) +#define IS_ZTP(rectype) (jrt_update[rectype] & ZTPREC_MASK) +#define IS_COM(rectype) (jrt_update[rectype] & (TCOMREC | ZTCOMREC)) +#define IS_FUPD(rectype) (jrt_update[rectype] & FUPDREC) +#define IS_GUPD(rectype) (jrt_update[rectype] & GUPDREC) +#define IS_TUPD(rectype) (jrt_update[rectype] & TUPDREC) +#define IS_UUPD(rectype) (jrt_update[rectype] & UUPDREC) +#define IS_FUPD_TUPD(rectype) (jrt_update[rectype] & (FUPDREC | TUPDREC)) +#define IS_GUPD_UUPD(rectype) (jrt_update[rectype] & (GUPDREC | UUPDREC)) + +#ifdef GTM_TRIGGER +# define IS_VALID_JRECTYPE(rectype) IS_VALID_RECTYPES_RANGE(rectype) +#else /* On trigger non-supporting platforms, it is an error if a ZTWORM or ZTRIG rectype is seen. */ +# define IS_VALID_JRECTYPE(rectype) (IS_VALID_RECTYPES_RANGE(rectype) && !IS_ZTWORM(rectype) && !IS_ZTRIG(rectype)) +#endif + +#define GET_REC_FENCE_TYPE(rectype) (!IS_FENCED(rectype)) ? NOFENCE : (IS_TP(rectype)) ? TPFENCE : ZTPFENCE +#define REC_HAS_TOKEN_SEQ(rectype) (IS_SET_KILL_ZKILL_ZTRIG_ZTWORM(rectype) || IS_COM(rectype) \ + || (JRT_EPOCH == (rectype)) || (JRT_EOF == (rectype)) \ + || (JRT_NULL == (rectype))) + +#endif /* JNL_TYPEDEF_H_INCLUDED */ diff --git a/sr_port/jnl_wait.c b/sr_port/jnl_wait.c new file mode 100644 index 0000000..ef55790 --- /dev/null +++ b/sr_port/jnl_wait.c @@ -0,0 +1,44 @@ +/**************************************************************** + * * + * Copyright 2001, 2003 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "gdsbt.h" +#include "fileinfo.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "iosp.h" +#include "jnl.h" +#include "is_file_identical.h" + +void jnl_wait(gd_region *reg) +{ + jnl_private_control *jpc; + sgmnt_addrs *csa; + uint4 status; + + if ((NULL != reg) && (TRUE == reg->open)) + { + csa = &FILE_INFO(reg)->s_addrs; + jpc = csa->jnl; + if ((TRUE == JNL_ENABLED(csa->hdr)) && (NULL != jpc)) + { /* wait on jnl writes for region */ + status = jnl_write_attempt(jpc, jpc->new_freeaddr); + UNIX_ONLY( + if (SS_NORMAL == status && !JNL_FILE_SWITCHED(jpc)) + jnl_fsync(reg, jpc->new_freeaddr); + ); + } + } + return; +} diff --git a/sr_port/jnl_write.c b/sr_port/jnl_write.c new file mode 100644 index 0000000..b6cd3a1 --- /dev/null +++ b/sr_port/jnl_write.c @@ -0,0 +1,482 @@ +/**************************************************************** + * * + * Copyright 2003, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gtm_inet.h" + +#include /* for offsetof() macro */ + +#ifdef VMS +#include /* Required for gtmsource.h */ +#endif + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "ccp.h" +#include "iosp.h" +#include "jnl.h" +#include "repl_msg.h" +#include "gtmsource.h" +#include "min_max.h" +#include "sleep_cnt.h" +#include "jnl_write.h" +#include "copy.h" +#include "jnl_get_checksum.h" +#include "memcoherency.h" +#include "is_proc_alive.h" +#include "wbox_test_init.h" +#include "gtmimagename.h" + +GBLREF jnlpool_ctl_ptr_t temp_jnlpool_ctl; +GBLREF uint4 process_id; +GBLREF sm_uc_ptr_t jnldata_base; +GBLREF jnlpool_addrs jnlpool; +GBLREF jnlpool_ctl_ptr_t jnlpool_ctl; +GBLREF jnl_gbls_t jgbl; + +#ifdef DEBUG +/* The fancy ordering of operators/operands in the JNL_SPACE_AVAILABLE calculation is to avoid overflows. */ +#define JNL_SPACE_AVAILABLE(jb, lcl_dskaddr, lcl_freeaddr, lcl_size, jnl_wrt_start_mask) \ +( \ + assert(((jb)->dskaddr <= lcl_freeaddr) \ + || (gtm_white_box_test_case_enabled \ + && (WBTEST_JNL_FILE_LOST_DSKADDR == gtm_white_box_test_case_number))), \ + /* the following assert is an || to take care of 4G value overflows or 0 underflows */ \ + assert((lcl_freeaddr <= lcl_size) || ((jb)->dskaddr >= lcl_freeaddr - lcl_size) \ + || (gtm_white_box_test_case_enabled \ + && (WBTEST_JNL_FILE_LOST_DSKADDR == gtm_white_box_test_case_number))), \ + (lcl_size - (lcl_freeaddr - ((lcl_dskaddr = (jb)->dskaddr) & jnl_wrt_start_mask))) \ +) +#else +#define JNL_SPACE_AVAILABLE(jb, dummy, lcl_freeaddr, lcl_size, jnl_wrt_start_mask) \ + (lcl_size - (lcl_freeaddr - ((jb)->dskaddr & jnl_wrt_start_mask))) +#endif + + +#define JNL_PUTSTR(lcl_free, lcl_buff, src, len, lcl_size) \ +{ \ + int size_before_wrap; \ + \ + size_before_wrap = lcl_size - lcl_free; \ + if (len <= size_before_wrap) \ + { \ + memcpy(&lcl_buff[lcl_free], src, len); \ + lcl_free += len; \ + if (len == size_before_wrap) \ + lcl_free = 0; \ + } else \ + { \ + memcpy(&lcl_buff[lcl_free], src, size_before_wrap); \ + lcl_free = len - size_before_wrap; \ + memcpy(&lcl_buff[0], src + size_before_wrap, lcl_free); \ + } \ +} + +/* jpc : Journal private control + * rectype : Record type + * jnl_rec : This contains fixed part of a variable size record or the complete fixed size records. + * blk_ptr : For JRT_PBLK and JRT_AIMG this has the block image + * jfb : For SET/KILL/ZKILL/ZTWORM records entire record is formatted in this. + * For JRT_PBLK and JRT_AIMG it contains partial records + */ +void jnl_write(jnl_private_control *jpc, enum jnl_record_type rectype, jnl_record *jnl_rec, blk_hdr_ptr_t blk_ptr, + jnl_format_buffer *jfb) +{ + int4 align_rec_len, rlen, rlen_with_align, dstlen, lcl_size, lcl_free, lcl_orig_free; + jnl_buffer_ptr_t jb; + sgmnt_addrs *csa; + sgmnt_data_ptr_t csd; + struct_jrec_align align_rec; + uint4 status; + jrec_suffix suffix; + boolean_t nowrap, is_replicated; + struct_jrec_blk *jrec_blk; + uint4 checksum, jnlpool_size, lcl_freeaddr; + sm_uc_ptr_t lcl_buff; + gd_region *reg; + char *ptr; + int jnl_wrt_start_modulus, jnl_wrt_start_mask; + uint4 jnl_fs_block_size, aligned_lcl_free, padding_size; +# ifdef DEBUG + uint4 lcl_dskaddr, mumps_node_sz; + char *mumps_node_ptr; +# endif + + error_def(ERR_JNLWRTNOWWRTR); + error_def(ERR_JNLWRTDEFER); + + reg = jpc->region; + csa = &FILE_INFO(reg)->s_addrs; + csd = csa->hdr; + is_replicated = jrt_is_replicated[rectype]; + /* Ensure that no replicated journal record is written by this routine if REPL-WAS_ENABLED(csa) is TRUE */ + assert((JNL_ENABLED(csa) && !REPL_WAS_ENABLED(csa)) || !is_replicated); + assert(csa->now_crit || (csd->clustered && csa->nl->ccp_state == CCST_CLOSED)); + assert(rectype > JRT_BAD && rectype < JRT_RECTYPES && JRT_ALIGN != rectype); + jb = jpc->jnl_buff; + /* Before taking a copy of jb->freeaddr, determine if both free and freeaddr are in sync. If not fix that first. */ + if (jb->free_update_pid) + { + FIX_NONZERO_FREE_UPDATE_PID(csa, jb); + } + lcl_freeaddr = jb->freeaddr; + lcl_free = jb->free; + lcl_size = jb->size; + lcl_buff = &jb->buff[jb->buff_off]; + DBG_CHECK_JNL_BUFF_FREEADDR(jb); + ++jb->reccnt[rectype]; + assert(NULL != jnl_rec); + rlen = jnl_rec->prefix.forwptr; + /* Do high-level check on rlen */ + assert(rlen <= jb->max_jrec_len); + /* Do fine-grained checks on rlen */ + GTMTRIG_ONLY(assert(!IS_ZTWORM(rectype) || (MAX_ZTWORM_JREC_LEN >= rlen));) /* ZTWORMHOLE */ + assert(!IS_SET_KILL_ZKILL_ZTRIG(rectype) || (JNL_MAX_SET_KILL_RECLEN(csd) >= rlen)); /* SET, KILL, ZKILL */ + assert((NULL == blk_ptr) || (JNL_MAX_PBLK_RECLEN(csd) >= rlen)); /* PBLK and AIMG */ + jb->bytcnt += rlen; + assert (0 == rlen % JNL_REC_START_BNDRY); + rlen_with_align = rlen + (int4)MIN_ALIGN_RECLEN; + assert(0 == rlen_with_align % JNL_REC_START_BNDRY); + assert((uint4)rlen_with_align < ((uint4)1 << jb->log2_of_alignsize)); + if ((lcl_freeaddr >> jb->log2_of_alignsize) == ((lcl_freeaddr + rlen_with_align - 1) >> jb->log2_of_alignsize)) + rlen_with_align = rlen; + else + { + align_rec.align_str.length = ROUND_UP2(lcl_freeaddr, ((uint4)1 << jb->log2_of_alignsize)) + - lcl_freeaddr - (uint4)MIN_ALIGN_RECLEN; + align_rec_len = (int4)(MIN_ALIGN_RECLEN + align_rec.align_str.length); + assert (0 == align_rec_len % JNL_REC_START_BNDRY); + rlen_with_align = rlen + align_rec_len; + } + jnl_wrt_start_mask = JNL_WRT_START_MASK(jb); + jnl_wrt_start_modulus = JNL_WRT_START_MODULUS(jb); + if (rlen_with_align != rlen) + { /* the calls below to jnl_write_attempt() and jnl_file_extend() are duplicated for the ALIGN record and the + * non-ALIGN journal record instead of making it a function. this is purely for performance reasons. + */ + assert((!jb->blocked) || (FALSE == is_proc_alive(jb->blocked, 0)) + VMS_ONLY(|| ((jb->blocked == process_id) && lib$ast_in_prog()))); + jb->blocked = process_id; + /* We should differentiate between a full and an empty journal buffer, hence the pessimism reflected in the <= + * check below. Hence also the -1 in lcl_freeaddr - (lcl_size - align_rec_len - 1). + * This means that although we have space we might still be invoking jnl_write_attempt (very unlikely). + */ + if (JNL_SPACE_AVAILABLE(jb, lcl_dskaddr, lcl_freeaddr, lcl_size, jnl_wrt_start_mask) <= align_rec_len) + { /* The fancy ordering of operators/operands in the calculation done below is to avoid overflows. */ + if (SS_NORMAL != jnl_write_attempt(jpc, + ROUND_UP2(lcl_freeaddr - (lcl_size - align_rec_len- 1), jnl_wrt_start_modulus))) + { + assert(NOJNL == jpc->channel); /* jnl file lost */ + return; /* let the caller handle the error */ + } + } + jb->blocked = 0; + if (jb->filesize < DISK_BLOCKS_SUM(lcl_freeaddr, align_rec_len)) /* not enough room in jnl file, extend it. */ + { /* We should never reach here if we are called from t_end/tp_tend */ + assert(!IS_GTM_IMAGE || csa->ti->early_tn == csa->ti->curr_tn); + if (SS_NORMAL != jnl_flush(reg)) + { + assert(NOJNL == jpc->channel); /* jnl file lost */ + return; /* let the caller handle the error */ + } + assert(lcl_freeaddr == jb->dskaddr); + if (EXIT_ERR == jnl_file_extend(jpc, align_rec_len)) /* if extension fails, not much we can do */ + { + assert(FALSE); + return; + } + if (0 == jpc->pini_addr && JRT_PINI != rectype) + { /* This can happen only if jnl got switched in jnl_file_extend above. + * We can't proceed now since the jnl record that we are writing now contains pini_addr information + * pointing to the older journal which is inappropriate if written into the new journal. + */ + GTMASSERT; + } + } + align_rec.prefix.jrec_type = JRT_ALIGN; + assert(align_rec_len <= jb->max_jrec_len); + align_rec.prefix.forwptr = suffix.backptr = align_rec_len; + align_rec.prefix.time = jnl_rec->prefix.time; + align_rec.prefix.tn = jnl_rec->prefix.tn; + /* we have to write an ALIGN record here before writing the PINI record but we do not have a non-zero + * pini_addr for the ALIGN since we have not yet written the PINI. we use the pini_addr field of the + * first PINI journal record in the journal file which is nothing but JNL_FILE_FIRST_RECORD. + */ + align_rec.prefix.pini_addr = (JRT_PINI == rectype) ? JNL_FILE_FIRST_RECORD : jnl_rec->prefix.pini_addr; + checksum = ADJUST_CHECKSUM(INIT_CHECKSUM_SEED, lcl_freeaddr); + checksum = ADJUST_CHECKSUM(checksum, csd->jnl_checksum); + assert(checksum); + align_rec.prefix.checksum = checksum; + suffix.suffix_code = JNL_REC_SUFFIX_CODE; + assert(lcl_free >= 0 && lcl_free < lcl_size); + if (lcl_size >= (lcl_free + align_rec_len)) + { /* before the string for zeroes */ + memcpy(lcl_buff + lcl_free, (uchar_ptr_t)&align_rec, FIXED_ALIGN_RECLEN); + lcl_free += (int4)(FIXED_ALIGN_RECLEN + align_rec.align_str.length); /* zeroing is not necessary */ + } else + { + JNL_PUTSTR(lcl_free, lcl_buff, (uchar_ptr_t)&align_rec, (int4)FIXED_ALIGN_RECLEN, lcl_size); + if (lcl_size >= (lcl_free + align_rec.align_str.length + SIZEOF(jrec_suffix))) + lcl_free += align_rec.align_str.length; /* zeroing is not necessary */ + else + { + if (lcl_size >= (lcl_free + align_rec.align_str.length)) + { + lcl_free += align_rec.align_str.length; /* zeroing is not necessary */ + if (lcl_size == lcl_free) + lcl_free = 0; + } else + lcl_free = lcl_free + align_rec.align_str.length - lcl_size; + } + } + /* Now copy suffix */ + assert(0 == (UINTPTR_T)(&lcl_buff[0] + lcl_free) % SIZEOF(jrec_suffix)); + *(jrec_suffix *)(lcl_buff + lcl_free) = *(jrec_suffix *)&suffix; + lcl_free += SIZEOF(jrec_suffix); + if (lcl_size == lcl_free) + lcl_free = 0; + jpc->new_freeaddr = lcl_freeaddr + align_rec_len; + assert(jgbl.gbl_jrec_time >= align_rec.prefix.time); + assert(align_rec.prefix.time >= jb->prev_jrec_time); + jb->prev_jrec_time = align_rec.prefix.time; + jpc->temp_free = lcl_free; /* set jpc->temp_free BEFORE setting free_update_pid (secshr_db_clnup relies on this) */ + assert(lcl_free == jpc->new_freeaddr % lcl_size); + /* Note that freeaddr should be updated ahead of free since jnl_output_sp.c does computation of wrtsize + * based on free and asserts follow later there which use freeaddr. + */ + jb->free_update_pid = process_id; + lcl_freeaddr = jpc->new_freeaddr; + jb->freeaddr = lcl_freeaddr; + /* Write memory barrier here to enforce the fact that freeaddr *must* be seen to be updated before + free is updated. It is less important if free is stale so we do not require a 2nd barrier for that + and will let the lock release (crit lock required since clustering not currently supported) do the + 2nd memory barrier for us. This barrier takes care of this process's responsibility to broadcast + cache changes. It is up to readers to also specify a read memory barrier if necessary to receive + this broadcast. + */ + SHM_WRITE_MEMORY_BARRIER; + jb->free = lcl_free; + jb->free_update_pid = 0; + DBG_CHECK_JNL_BUFF_FREEADDR(jb); + if (JRT_PINI == rectype) + jnl_rec->prefix.pini_addr = lcl_freeaddr; + } + checksum = jnl_rec->prefix.checksum; + assert(checksum); +# ifdef DEBUG + /* Ensure that the checksum computed earlier in jnl_format or jnl_write_pblk matches with the block's content. For fixed + * size records and AIMG records, this check is not needed since the checksum is initialized to INIT_CHECKSUM_SEED. + */ + if (!jrt_fixed_size[rectype] && (JRT_AIMG != rectype)) + { + if (JRT_PBLK == rectype) + assert(checksum == jnl_get_checksum((uint4 *)blk_ptr, NULL, jnl_rec->jrec_pblk.bsiz)); + else + { + assert(IS_SET_KILL_ZKILL_ZTRIG_ZTWORM(rectype)); + mumps_node_ptr = jfb->buff + FIXED_UPD_RECLEN; + mumps_node_sz = jfb->record_size - (FIXED_UPD_RECLEN + JREC_SUFFIX_SIZE); + assert(checksum == jnl_get_checksum((uint4 *)mumps_node_ptr, NULL, mumps_node_sz)); + } + } +# endif + checksum = ADJUST_CHECKSUM(checksum, lcl_freeaddr); + checksum = ADJUST_CHECKSUM(checksum, csd->jnl_checksum); + ADJUST_CHECKSUM_WITH_SEQNO(is_replicated, checksum, GET_JNL_SEQNO(jnl_rec)); /* Note: checksum is updated inside macro */ + jnl_rec->prefix.checksum = checksum; + UNIX_ONLY(assert((!jb->blocked) || (FALSE == is_proc_alive(jb->blocked, 0)));) + VMS_ONLY(assert(!jb->blocked || (jb->blocked == process_id) && lib$ast_in_prog())); /* wcs_wipchk_ast can set jb->blocked */ + jb->blocked = process_id; + /* We should differentiate between a full and an empty journal buffer, hence the pessimism reflected in the <= check below. + * Hence also the -1 in lcl_freeaddr - (lcl_size - rlen - 1). + * This means that although we have space we might still be invoking jnl_write_attempt (very unlikely). + */ + if (JNL_SPACE_AVAILABLE(jb, lcl_dskaddr, lcl_freeaddr, lcl_size, jnl_wrt_start_mask) <= rlen) + { /* The fancy ordering of operators/operands in the calculation done below is to avoid overflows. */ + if (SS_NORMAL != jnl_write_attempt(jpc, ROUND_UP2(lcl_freeaddr - (lcl_size - rlen - 1), jnl_wrt_start_modulus))) + { + assert(NOJNL == jpc->channel); /* jnl file lost */ + return; /* let the caller handle the error */ + } + } + jb->blocked = 0; + if (jb->filesize < DISK_BLOCKS_SUM(lcl_freeaddr, rlen)) /* not enough room in jnl file, extend it. */ + { /* We should never reach here if we are called from t_end/tp_tend */ + assert(!IS_GTM_IMAGE || csa->ti->early_tn == csa->ti->curr_tn); + if (SS_NORMAL != jnl_flush(reg)) + { + assert(NOJNL == jpc->channel); /* jnl file lost */ + return; /* let the caller handle the error */ + } + assert(lcl_freeaddr == jb->dskaddr); + if (EXIT_ERR == jnl_file_extend(jpc, rlen)) /* if extension fails, not much we can do */ + { + assert(FALSE); + return; + } + if (0 == jpc->pini_addr && JRT_PINI != rectype) + { /* This can happen only if jnl got switched in jnl_file_extend above. + * We can't proceed now since the jnl record that we are writing now contains pini_addr information + * pointing to the older journal which is inappropriate if written into the new journal. + */ + GTMASSERT; + } + } + lcl_orig_free = lcl_free; + nowrap = (lcl_size >= (lcl_free + rlen)); + assert(jrt_fixed_size[JRT_EOF]); + if (jrt_fixed_size[rectype]) + { + if (nowrap) + { + memcpy(lcl_buff + lcl_free, (uchar_ptr_t)jnl_rec, rlen); + lcl_free += rlen; + if (lcl_size == lcl_free) + lcl_free = 0; + } else + JNL_PUTSTR(lcl_free, lcl_buff, (uchar_ptr_t)jnl_rec, rlen, lcl_size); + /* As part of writing the EOF record into the journal buffer, add enough 0-padding needed to reach + * a filesystem-block-size aligned boundary. This way later jnl_qio_start can safely do aligned + * writes without having to write non-zero garbage after the EOF record. Note that this has to be + * done BEFORE updating freeaddr. Otherwise, it is possible that a jnl qio timer pops after freeaddr + * gets updated but before the 0-padding is done and flushes the eof record to disk without the 0-padding. + */ + if (JRT_EOF == rectype) + { + jnl_fs_block_size = jb->fs_block_size; + aligned_lcl_free = ROUND_UP2(lcl_free, jnl_fs_block_size); + padding_size = aligned_lcl_free - lcl_free; + assert(0 <= (int4)padding_size); + if (padding_size) + memset(lcl_buff + lcl_free, 0, padding_size); + } + } else + { + if (NULL != blk_ptr) /* PBLK and AIMG */ + { + assert(FIXED_BLK_RECLEN == FIXED_PBLK_RECLEN); + assert(FIXED_BLK_RECLEN == FIXED_AIMG_RECLEN); + jrec_blk = (struct_jrec_blk *)jnl_rec; + if (nowrap) + { /* write fixed part of record before the actual gds block image */ + memcpy(lcl_buff + lcl_free, (uchar_ptr_t)jnl_rec, FIXED_BLK_RECLEN); + lcl_free += (int4)FIXED_BLK_RECLEN; + /* write actual block */ + memcpy(lcl_buff + lcl_free, (uchar_ptr_t)blk_ptr, jrec_blk->bsiz); + lcl_free += jrec_blk->bsiz; + /* Now write trailing characters for 8-bye alignment and then suffix */ + memcpy(lcl_buff + lcl_free, (uchar_ptr_t)jfb->buff, jfb->record_size); + lcl_free += jfb->record_size; + assert(lcl_free <= lcl_size); + if (lcl_size == lcl_free) + lcl_free = 0; + } else + { /* write fixed part of record before the actual gds block image */ + JNL_PUTSTR(lcl_free, lcl_buff, (uchar_ptr_t)jnl_rec, (int4)FIXED_BLK_RECLEN, lcl_size); + /* write actual block */ + JNL_PUTSTR(lcl_free, lcl_buff, (uchar_ptr_t)blk_ptr, jrec_blk->bsiz, lcl_size); + /* Now write trailing characters for 8-bye alignment and then suffix */ + JNL_PUTSTR(lcl_free, lcl_buff, (uchar_ptr_t)jfb->buff, jfb->record_size, lcl_size); + } + } else + { /* SET, KILL, ZKILL for TP, ZTP, non-TP */ + assert(IS_TP(rectype) || IS_ZTP(rectype) || (0 == ((struct_jrec_upd *)jfb->buff)->update_num)); + assert((!IS_TP(rectype) && !IS_ZTP(rectype)) || (0 != ((struct_jrec_upd *)jfb->buff)->update_num)); + assert(((jrec_prefix *)jfb->buff)->forwptr == jfb->record_size); + if (nowrap) + { + memcpy(lcl_buff + lcl_free, (uchar_ptr_t)jfb->buff, rlen); + lcl_free += rlen; + if (lcl_size == lcl_free) + lcl_free = 0; + } else + JNL_PUTSTR(lcl_free, lcl_buff, (uchar_ptr_t)jfb->buff, rlen, lcl_size); + } + } + assert((lcl_free - lcl_orig_free + lcl_size) % lcl_size == rlen); + assert(lcl_buff[lcl_orig_free] == rectype); + assert(lcl_orig_free < lcl_free || lcl_free < jb->dsk); + assert((lcl_freeaddr >= jb->dskaddr) + || (gtm_white_box_test_case_enabled && (WBTEST_JNL_FILE_LOST_DSKADDR == gtm_white_box_test_case_number))); + jpc->new_freeaddr = lcl_freeaddr + rlen; + assert(lcl_free == jpc->new_freeaddr % lcl_size); + if (REPL_ENABLED(csa) && is_replicated) + { /* If the database is encrypted, then at this point jfb->buff will contain encrypted + * data which we don't want to to push into the jnlpool. Instead, we make use of the + * alternate alt_buff which is guaranteed to contain the original unencrypted data. + * */ + if (jrt_fixed_size[rectype]) + ptr = (char *)jnl_rec; + else + { +# ifdef GTM_CRYPT + if (csd->is_encrypted && IS_SET_KILL_ZKILL_ZTRIG_ZTWORM(rectype)) + ptr = jfb->alt_buff; + else +# endif + ptr = jfb->buff; + } + assert(NULL != jnlpool.jnlpool_ctl && NULL != jnlpool_ctl); /* ensure we haven't yet detached from the jnlpool */ + assert((&FILE_INFO(jnlpool.jnlpool_dummy_reg)->s_addrs)->now_crit); /* ensure we have the jnl pool lock */ + DEBUG_ONLY(jgbl.cu_jnl_index++;) + jnlpool_size = temp_jnlpool_ctl->jnlpool_size; + dstlen = jnlpool_size - temp_jnlpool_ctl->write; + if (rlen <= dstlen) /* dstlen >= rlen (most frequent case) */ + memcpy(jnldata_base + temp_jnlpool_ctl->write, ptr, rlen); + else /* dstlen < rlen */ + { + memcpy(jnldata_base + temp_jnlpool_ctl->write, ptr, dstlen); + memcpy(jnldata_base, ptr + dstlen, rlen - dstlen); + } + temp_jnlpool_ctl->write += rlen; + if (temp_jnlpool_ctl->write >= jnlpool_size) + temp_jnlpool_ctl->write -= jnlpool_size; + } + assert(jgbl.gbl_jrec_time >= jnl_rec->prefix.time); + assert(jnl_rec->prefix.time >= jb->prev_jrec_time); + jb->prev_jrec_time = jnl_rec->prefix.time; + jpc->temp_free = lcl_free; /* set jpc->temp_free BEFORE setting free_update_pid (secshr_db_clnup relies on this) */ + /* Note that freeaddr should be updated ahead of free since jnl_output_sp.c does computation of wrtsize + * based on free and asserts follow later there which use freeaddr. + */ + jb->free_update_pid = process_id; + lcl_freeaddr = jpc->new_freeaddr; + jb->freeaddr = lcl_freeaddr; + /* Write memory barrier here to enforce the fact that freeaddr *must* be seen to be updated before + free is updated. It is less important if free is stale so we do not require a 2nd barrier for that + and will let the lock release (crit lock required since clustering not currently supported) do the + 2nd memory barrier for us. This barrier takes care of this process's responsibility to broadcast + cache changes. It is up to readers to also specify a read memory barrier if necessary to receive + this broadcast. + */ + SHM_WRITE_MEMORY_BARRIER; + jb->free = lcl_free; + jb->free_update_pid = 0; + DBG_CHECK_JNL_BUFF_FREEADDR(jb); + VMS_ONLY( + if (((lcl_freeaddr - jb->dskaddr) > jb->min_write_size) + && (SS_NORMAL != (status = jnl_qio_start(jpc))) && (ERR_JNLWRTNOWWRTR != status) && (ERR_JNLWRTDEFER != status)) + { + jb->blocked = 0; + jnl_file_lost(jpc, status); + return; + } + ) + if (dba_mm == reg->dyn.addr->acc_meth) + jnl_mm_timer(csa, reg); +} diff --git a/sr_port/jnl_write.h b/sr_port/jnl_write.h new file mode 100644 index 0000000..47cc80c --- /dev/null +++ b/sr_port/jnl_write.h @@ -0,0 +1,19 @@ +/**************************************************************** + * * + * Copyright 2001, 2003 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __JNL_WRITE_H__ +#define __JNL_WRITE_H__ + +/* We do not put this in jnl.h, because it needs all including jnl.h must include gdsblk.h */ +void jnl_write(jnl_private_control *jpc, enum jnl_record_type rectype, jnl_record *jnl_rec, blk_hdr_ptr_t blk_ptr, + jnl_format_buffer *jfb); + +#endif diff --git a/sr_port/jnl_write_aimg_rec.c b/sr_port/jnl_write_aimg_rec.c new file mode 100644 index 0000000..5d3dd99 --- /dev/null +++ b/sr_port/jnl_write_aimg_rec.c @@ -0,0 +1,98 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include + +#include "gtm_string.h" +#include "gtm_time.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gdscc.h" +#include "jnl.h" +#include "jnl_write.h" +#include "jnl_write_aimg_rec.h" +#include "jnl_get_checksum.h" +#include "min_max.h" +#ifdef GTM_CRYPT +#include "gtmcrypt.h" +#endif +GBLREF jnl_gbls_t jgbl; + +void jnl_write_aimg_rec(sgmnt_addrs *csa, cw_set_element *cse) +{ + struct_jrec_blk aimg_record; + int tmp_jrec_size, jrec_size, zero_len; + jnl_format_buffer blk_trailer; /* partial record after the aimg block */ + char local_buff[JNL_REC_START_BNDRY + JREC_SUFFIX_SIZE]; + jrec_suffix *suffix; + blk_hdr_ptr_t buffer, save_buffer; + jnl_private_control *jpc; + sgmnt_data_ptr_t csd; +#ifdef GTM_CRYPT + char *buff; + int req_enc_blk_size, init_status, crypt_status; +#endif + csd = csa->hdr; + assert(csa->now_crit); + jpc = csa->jnl; + assert(0 != jpc->pini_addr); + aimg_record.prefix.jrec_type = JRT_AIMG; + aimg_record.prefix.pini_addr = (0 == jpc->pini_addr) ? JNL_HDR_LEN : jpc->pini_addr; + aimg_record.prefix.tn = csa->ti->curr_tn; + /* At this point jgbl.gbl_jrec_time should be set by the caller */ + assert(jgbl.gbl_jrec_time); + aimg_record.prefix.time = jgbl.gbl_jrec_time; + aimg_record.prefix.checksum = INIT_CHECKSUM_SEED; + aimg_record.blknum = cse->blk; + /* in case we have a bad block-size, we dont want to write an AIMG larger than the GDS block size (maximum block size) */ + buffer = (blk_hdr_ptr_t)cse->new_buff; + assert(buffer->bsiz <= csd->blk_size); + assert(buffer->bsiz >= SIZEOF(blk_hdr)); + aimg_record.bsiz = MIN(csd->blk_size, buffer->bsiz); + aimg_record.ondsk_blkver = cse->ondsk_blkver; + tmp_jrec_size = (int)FIXED_AIMG_RECLEN + aimg_record.bsiz + JREC_SUFFIX_SIZE; + jrec_size = ROUND_UP2(tmp_jrec_size, JNL_REC_START_BNDRY); + zero_len = jrec_size - tmp_jrec_size; + blk_trailer.buff = local_buff + (JNL_REC_START_BNDRY - zero_len); + memset(blk_trailer.buff, 0, zero_len); + blk_trailer.record_size = zero_len + JREC_SUFFIX_SIZE; + suffix = (jrec_suffix *)&local_buff[JNL_REC_START_BNDRY]; + aimg_record.prefix.forwptr = suffix->backptr = jrec_size; + suffix->suffix_code = JNL_REC_SUFFIX_CODE; + assert(SIZEOF(uint4) == SIZEOF(jrec_suffix)); + save_buffer = buffer; +# ifdef GTM_CRYPT + req_enc_blk_size = aimg_record.bsiz - SIZEOF(*buffer); + if (BLOCK_REQUIRE_ENCRYPTION(csd->is_encrypted, buffer->levl, req_enc_blk_size)) + { + ASSERT_ENCRYPTION_INITIALIZED; + memcpy(csa->encrypted_blk_contents, buffer, SIZEOF(*buffer)); + GTMCRYPT_ENCODE_FAST(csa->encr_key_handle, + (char *)(buffer + 1), + req_enc_blk_size, + (csa->encrypted_blk_contents + SIZEOF(*buffer)), + crypt_status); + if (0 != crypt_status) + GC_RTS_ERROR(crypt_status, NULL); + buffer = (blk_hdr_ptr_t)csa->encrypted_blk_contents; + } +# endif + jnl_write(jpc, JRT_AIMG, (jnl_record *)&aimg_record, buffer, &blk_trailer); + buffer = save_buffer; +} diff --git a/sr_port/jnl_write_aimg_rec.h b/sr_port/jnl_write_aimg_rec.h new file mode 100644 index 0000000..a3cf65a --- /dev/null +++ b/sr_port/jnl_write_aimg_rec.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001, 2005 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __JNL_WRITE_AIMG_REC_H__ +#define __JNL_WRITE_AIMG_REC_H__ + +void jnl_write_aimg_rec(sgmnt_addrs *csa, cw_set_element *cse); + +#endif diff --git a/sr_port/jnl_write_attempt.c b/sr_port/jnl_write_attempt.c new file mode 100644 index 0000000..c013532 --- /dev/null +++ b/sr_port/jnl_write_attempt.c @@ -0,0 +1,349 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsfhead.h" +#include "gdsbgtr.h" +#include "filestruct.h" +#include "iosp.h" +#include "jnl.h" +#include "lockconst.h" +#include "interlock.h" +#include "sleep_cnt.h" +#include "send_msg.h" +#include "wcs_sleep.h" +#include "is_proc_alive.h" +#include "compswap.h" +#include "is_file_identical.h" +#include "have_crit.h" +#include "wbox_test_init.h" + +#ifdef UNIX +#include "gtmmsg.h" +#endif +#include "gtm_c_stack_trace.h" + +GBLREF pid_t process_id; +GBLREF uint4 image_count; + +error_def(ERR_JNLCNTRL); +error_def(ERR_JNLFLUSH); +error_def(ERR_JNLFLUSHNOPROG); +error_def(ERR_JNLPROCSTUCK); +error_def(ERR_JNLWRTDEFER); +error_def(ERR_JNLWRTNOWWRTR); +error_def(ERR_TEXT); +error_def(ERR_JNLWRTDEFER); +error_def(ERR_JNLWRTNOWWRTR); + +#ifdef VMS +# define CURRENT_WRITER jb->now_writer +#else +# define CURRENT_WRITER jb->io_in_prog_latch.u.parts.latch_pid +#endif + +static uint4 jnl_sub_write_attempt(jnl_private_control *jpc, unsigned int *lcnt, uint4 threshold) +{ + sgmnt_addrs *csa; + jnl_buffer_ptr_t jb; + unsigned int status; + boolean_t was_crit, exact_check; + /**** Note static/local */ + static uint4 loop_image_count, writer; /* assumes calls from one loop at a time */ + uint4 new_dskaddr, new_dsk; + static uint4 stuck_cnt = 0; + + /* Some callers of jnl_sub_write_attempt (jnl_flush->jnl_write_attempt, jnl_write->jnl_write_attempt) are in + * crit, and some other (jnl_wait->jnl_write_attempt) are not. Callers in crit do not need worry about journal + * buffer fields (dskaddr, freeaddr) changing underneath them, but for those not in crit, jnl_sub_write_attempt + * might incorrectly return an error status when journal file is switched. Such callers should check for + * journal file switched condition and terminate any loops they are in. + */ + jb = jpc->jnl_buff; + status = ERR_JNLWRTDEFER; + csa = &FILE_INFO(jpc->region)->s_addrs; + was_crit = csa->now_crit; + exact_check = was_crit && (threshold == jb->freeaddr); /* see comment in jnl_write_attempt() for why this is needed */ + while (exact_check ? (jb->dskaddr != threshold) : (jb->dskaddr < threshold)) + { +#ifdef UNIX + if (jb->io_in_prog_latch.u.parts.latch_pid == process_id) + { + /* if error condition occurred while doing jnl_qio_start(), then release the lock before waiting */ + /* note that this is done only in UNIX because Unix does synchronous I/O */ + jb->image_count = 0; + RELEASE_SWAPLOCK(&jb->io_in_prog_latch); + } + if (!jb->io_in_prog_latch.u.parts.latch_pid) + status = jnl_qio_start(jpc); +#elif defined VMS + if (lib$ast_in_prog()) + { + if (!jb->io_in_prog) + { + assert(jb->blocked == process_id); + jnl_start_ast(jpc); + if (jb->now_writer == process_id) + status = jb->iosb.cond; + } + break; /* no fancy stuff within an AST */ + } else if (!jb->io_in_prog) + { /* Note down jpc->new_dskaddr/new_dsk into local variables so we get a consistent copy of these two + * variables for checking them later. + */ + new_dskaddr = jpc->new_dskaddr; + new_dsk = jpc->new_dsk; + status = jnl_qio_start(jpc); + } +#else +#error UNSUPPORTED PLATFORM +#endif + if (SS_NORMAL == status) + { +# if defined VMS + /* Check if JNLCNTRL error was signalled by jnl_qio_start(). Note that it does not explicitly + * return this error since it in turn calls an AST routine jnl_start_ast that actually has the + * qio lock (and hence can look at dskaddr/dsk without any concurrency issues). But jpc will + * have two fields new_dskaddr/new_dsk set to what dskaddr/dsk were right after obtaining the + * qio lock but before releasing it in case of a JNLCNTRL error. We use those two values to + * recheck if this is a JNLCNTRL error situation and if so return that error from here. + * Note that we cannot use fields from jpc since they could be set by an AST that pops right + * after we check new_dskaddr below but before we fetch the value of new_dsk. So it is important + * to use the local variables which we know are a consistent snapshot of jpc->new_dskaddr/new_dsk. + * The only consequence of this approach is that in case there is a dskaddr/dsk inconsistency, + * it will be detected by the local variables in the next iteration (not the first time around). + */ + if ((new_dskaddr % jb->size) != new_dsk) + { + assert(gtm_white_box_test_case_enabled + && (WBTEST_JNL_FILE_LOST_DSKADDR == gtm_white_box_test_case_number)); + status = ERR_JNLCNTRL; + } +# endif + break; + } + UNIX_ONLY(assert(ERR_JNLWRTNOWWRTR != status);) /* dont have asynchronous jnl writes in Unix */ + if ((ERR_JNLWRTNOWWRTR != status) && (ERR_JNLWRTDEFER != status)) + return status; + if ((writer != CURRENT_WRITER) || (1 == *lcnt)) + { + writer = CURRENT_WRITER; + loop_image_count = jb->image_count; + *lcnt = 1; /* !!! this should be detected and limited by the caller !!! */ + break; + } + if (*lcnt <= JNL_MAX_FLUSH_TRIES) + { + wcs_sleep(*lcnt); + break; + } + VMS_ONLY( + if ((CURRENT_WRITER == process_id) && (jpc->qio_active == TRUE) && (jb->iosb.cond == -2)) + { /* this an "impossible" condition where the private flag and the io have lost sync */ + GTMASSERT; /* this should only occur in VMS; secshr_db_clnup should clear the problem */ + } + ) + if (writer == CURRENT_WRITER) + { + if (!was_crit) + grab_crit(jpc->region); /* jnl_write_attempt has an assert about have_crit that this relies on */ + if (VMS_ONLY(0 == writer ||) FALSE == is_proc_alive(writer, jb->image_count)) + { /* no one home, clear the semaphore; */ + BG_TRACE_PRO_ANY(csa, jnl_blocked_writer_lost); + jnl_send_oper(jpc, ERR_JNLFLUSH); + send_msg(VARLSTCNT(3) ERR_JNLPROCSTUCK, 1, CURRENT_WRITER); + stuck_cnt++; + GET_C_STACK_FROM_SCRIPT("JNLPROCSTUCK", process_id, CURRENT_WRITER, stuck_cnt); + send_msg(VARLSTCNT(4) ERR_TEXT, 2, LEN_AND_LIT("Journal IO writer changed during wait")); + VMS_ONLY(jb->io_in_prog = 0); + UNIX_ONLY(COMPSWAP_UNLOCK(&jb->io_in_prog_latch, writer, jb->image_count, LOCK_AVAILABLE, 0)); + if (!was_crit) + rel_crit(jpc->region); + *lcnt = 1; + continue; + } + if (!was_crit) + rel_crit(jpc->region); + /* this is the interesting case: a process is stuck */ + BG_TRACE_PRO_ANY(csa, jnl_blocked_writer_stuck); + jpc->status = status; + jnl_send_oper(jpc, ERR_JNLFLUSH); + send_msg(VARLSTCNT(3) ERR_JNLPROCSTUCK, 1, CURRENT_WRITER); + stuck_cnt++; + GET_C_STACK_FROM_SCRIPT("JNLPROCSTUCK", process_id, CURRENT_WRITER, stuck_cnt); + *lcnt = 1; /* ??? is it necessary to limit this, and if so, how ??? */ + status = ERR_JNLPROCSTUCK; + break; + } + break; + } + if ((threshold > jb->freeaddr) + || (csa->now_crit && ((jb->dskaddr > jb->freeaddr) || (jb->free != (jb->freeaddr % jb->size))))) + { /* threshold > jb->freeaddr => somebody decremented jb->freeaddr after we computed threshold, or jnl was switched + * jb->dsk != jb->freeaddr % jb->size => out of design condition + * jb->dskaddr > jb->freeaddr => out of design condition, or jnl was switched + */ + status = ERR_JNLCNTRL; + } + return status; +} + +uint4 jnl_write_attempt(jnl_private_control *jpc, uint4 threshold) +{ + jnl_buffer_ptr_t jb; + unsigned int lcnt, prev_lcnt, cnt, proc_stuck_cnt; + sgmnt_addrs *csa; + unsigned int status; + boolean_t was_crit, jnlfile_lost, exact_check; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + jb = jpc->jnl_buff; + csa = &FILE_INFO(jpc->region)->s_addrs; + was_crit = csa->now_crit; + + /* If holding crit and input threshold matches jb->freeaddr, then we need to wait in the loop as long as dskaddr + * is not EQUAL to threshold. This is because if dskaddr is lesser than threshold we need to wait. If ever it + * becomes greater than threshold, it is an out-of-design situation (since dskaddr has effectively become > freeaddr) + * and so we need to trigger "jnl_file_lost" which is done in "jnl_sub_write_attempt" so it is important to invoke + * that routine (in the for loop below). Hence the need to do an exact match instead of a < match. If not holding + * crit or input threshold does not match jb->freeaddr, then dskaddr becoming GREATER than threshold is a valid + * condition so we should do a (dskaddr < threshold), not a (dskaddr != threshold) check in that case. + */ + exact_check = was_crit && (threshold == jb->freeaddr); + assert(!was_crit || threshold <= jb->freeaddr); + /* Check that we either own crit on the current region or we DONT own crit on ANY region. This is relied upon by + * the grab_crit calls (done in jnl_write_attempt and jnl_sub_write_attempt) to ensure no deadlocks are possible. + */ + assert(was_crit || (0 == have_crit(CRIT_HAVE_ANY_REG))); + for (prev_lcnt = lcnt = cnt = 1, proc_stuck_cnt = 0; + (was_crit || (NOJNL != jpc->channel)) && (exact_check ? jb->dskaddr != threshold : jb->dskaddr < threshold); + lcnt++, prev_lcnt = lcnt, cnt++) + { + status = jnl_sub_write_attempt(jpc, &lcnt, threshold); + if (JNL_FILE_SWITCHED(jpc)) + { /* If we are holding crit, the journal file switch could happen in the form of journaling getting + * turned OFF (due to disk space issues etc.) + */ + jpc->status = SS_NORMAL; + return SS_NORMAL; + } + if (SS_NORMAL == status) + { + if (JNL_FLUSH_PROG_TRIES > lcnt) + { + proc_stuck_cnt = 0; + /* In VMS, jnl writes are asynchronous. The above call to "jnl_sub_write_attempt" has returned + * SS_NORMAL status. This means the jnl qio lock is not in use by anyone else and is up for grabs. + * We would have scheduled a jnl qio write through a sys$dclast call. We have no control of when + * the AST routine "jnl_start_ast" will actually get control and start the write. Until then + * we dont want to keep reinvoking "jnl_sub_write_attempt" in a hard spin loop. So sleep. + * In Unix, writes are synchronous so SS_NORMAL status return implies we have completed a jnl + * write and "jb->dskaddr" is closer to "threshold" than it was in the previous iteration. + * A sleep at this point will only slow things down unnecessarily. Hence no sleep if Unix. + */ + VMS_ONLY(wcs_sleep(lcnt);) + continue; + } + jpc->status = SS_NORMAL; + jnl_send_oper(jpc, ERR_JNLFLUSH); + send_msg(VARLSTCNT(8) ERR_JNLFLUSHNOPROG, 2, JNL_LEN_STR(csa->hdr), + ERR_TEXT, 2, LEN_AND_LIT("Could not flush all the buffered journal data")); + GTMASSERT; /* too many attempts to flush journal data */ + } + if ((ERR_JNLCNTRL == status) + || (csa->now_crit + && (ERR_JNLWRTDEFER != status) && (ERR_JNLWRTNOWWRTR != status) && (ERR_JNLPROCSTUCK != status))) + { /* If JNLCNTRL or if holding crit and not waiting for some other writer (or self in VMS) + * better turn off journaling and proceed with database update to avoid a database hang. + */ + if (was_crit) + jb->blocked = 0; + else + grab_crit(jpc->region); /* jnl_write_attempt has an assert about have_crit that this relies on */ + jnlfile_lost = FALSE; + if (jb->free_update_pid) + { + FIX_NONZERO_FREE_UPDATE_PID(csa, jb); + } else + { + assert(gtm_white_box_test_case_enabled + && (WBTEST_JNL_FILE_LOST_DSKADDR == gtm_white_box_test_case_number)); + if (JNL_ENABLED(csa->hdr)) + { /* We ignore the return value of jnl_file_lost() since we always want to report the journal + * error, whatever its error handling method is. Also, an operator log will be sent by some + * callers (t_end()) only if an error is returned here, and the operator log is wanted in + * those cases. + */ + jnl_file_lost(jpc, status); + jnlfile_lost = TRUE; + } + /* Else journaling got closed concurrently by another process by invoking "jnl_file_lost" + * just before we got crit. Do not invoke "jnl_file_lost" again on the same journal file. + * Instead continue and next iteration will detect the journal file has switched and terminate. + */ + } + if (!was_crit) + rel_crit(jpc->region); + if (!jnlfile_lost) + continue; + else + return status; + } + if ((ERR_JNLWRTDEFER != status) && (ERR_JNLWRTNOWWRTR != status) && (ERR_JNLPROCSTUCK != status)) + { /* If holding crit, then jnl_sub_write_attempt would have invoked jnl_file_lost which would have + * caused the JNL_FILE_SWITCHED check at the beginning of this for loop to succeed and return from + * this function so we should never have gotten here. Assert accordingly. If not holding crit, + * wait for some crit holder to invoke jnl_file_lost. Until then keep sleep looping indefinitely. + * The sleep in this case is not time-limited because the callers of jnl_write_attempt (particularly + * jnl_wait) do not check its return value so they assume success returns from this function. It is + * non-trivial to change the interface and code of all callers to handle the error situation so we + * instead choose to sleep indefinitely here until some crit process encounters the same error and + * triggers jnl_file_lost processing which will terminate the loop due to the JNL_FILE_SWITCHED check. + */ + assert(!csa->now_crit); + wcs_sleep(lcnt); + } else if (prev_lcnt != lcnt) + { + assert(1 == lcnt); + if (ERR_JNLWRTDEFER == status) + { /* Change of writer */ + if (JNL_FLUSH_PROG_TRIES <= cnt) + { + send_msg(VARLSTCNT(8) ERR_JNLFLUSHNOPROG, 2, JNL_LEN_STR(csa->hdr), + ERR_TEXT, 2, LEN_AND_LIT("No progress even with multiple writers")); + GTMASSERT; + } + proc_stuck_cnt = 0; + } else if (ERR_JNLPROCSTUCK == status && (JNL_FLUSH_PROG_FACTOR <= ++proc_stuck_cnt)) + { + send_msg(VARLSTCNT(8) ERR_JNLFLUSHNOPROG, 2, JNL_LEN_STR(csa->hdr), ERR_TEXT, 2, + LEN_AND_LIT("Progress prevented by a process stuck flushing journal data")); + VMS_ONLY( + if (TREF(gtm_environment_init)) + { + proc_stuck_cnt = 0; + continue; + } + ) + + GTMASSERT; + } + } + } + return SS_NORMAL; +} diff --git a/sr_port/jnl_write_eof_rec.c b/sr_port/jnl_write_eof_rec.c new file mode 100644 index 0000000..4503970 --- /dev/null +++ b/sr_port/jnl_write_eof_rec.c @@ -0,0 +1,63 @@ +/**************************************************************** + * * + * Copyright 2003, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_inet.h" +#ifdef VMS +#include /* Required for gtmsource.h */ +#endif + +#include "gtm_time.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "jnl.h" +#include "jnl_write.h" +#include "repl_msg.h" +#include "gtmsource.h" +#include "jnl_get_checksum.h" + +GBLREF jnlpool_ctl_ptr_t jnlpool_ctl; +GBLREF jnl_gbls_t jgbl; + +void jnl_write_eof_rec(sgmnt_addrs *csa, struct_jrec_eof *eof_record) +{ + jnl_private_control *jpc; + + assert(csa->now_crit); + jpc = csa->jnl; + assert(0 != jpc->pini_addr); + eof_record->prefix.jrec_type = JRT_EOF; + eof_record->prefix.forwptr = eof_record->suffix.backptr = EOF_RECLEN; + eof_record->suffix.suffix_code = JNL_REC_SUFFIX_CODE; + eof_record->prefix.pini_addr = (0 == jpc->pini_addr) ? JNL_HDR_LEN : jpc->pini_addr; + eof_record->prefix.tn = csa->hdr->trans_hist.curr_tn; + eof_record->prefix.checksum = INIT_CHECKSUM_SEED; + /* At this point jgbl.gbl_jrec_time should be set by the caller */ + assert(jgbl.gbl_jrec_time); + eof_record->prefix.time = jgbl.gbl_jrec_time; + ASSERT_JNL_SEQNO_FILEHDR_JNLPOOL(csa->hdr, jnlpool_ctl); /* debug-only sanity check between seqno of filehdr and jnlpool */ + if (!jgbl.forw_phase_recovery) + { + if (REPL_ALLOWED(csa)) + eof_record->jnl_seqno = csa->hdr->reg_seqno;/* Note we cannot use jnlpool_ctl->jnl_seqno since + * we might not presently hold the journal pool lock */ + else + eof_record->jnl_seqno = 0; + } else + QWASSIGN(eof_record->jnl_seqno, jgbl.mur_jrec_seqno); + jnl_write(jpc, JRT_EOF, (jnl_record *)eof_record, NULL, NULL); +} diff --git a/sr_port/jnl_write_epoch_rec.c b/sr_port/jnl_write_epoch_rec.c new file mode 100644 index 0000000..005647a --- /dev/null +++ b/sr_port/jnl_write_epoch_rec.c @@ -0,0 +1,118 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#if defined(UNIX) +#include "gtm_fcntl.h" +#include "gtm_unistd.h" +#include "eintr_wrappers.h" +#elif defined(VMS) +#include /* Required for gtmsource.h */ +#include +#include +#include +#include "iosb_disk.h" +#endif +#include "gtm_inet.h" + +#include "gtm_time.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "jnl.h" +#include "jnl_write.h" +#include "repl_msg.h" +#include "gtmsource.h" +#include "gtmio.h" +#include "iosp.h" +#include "jnl_get_checksum.h" + +GBLREF jnl_gbls_t jgbl; +GBLREF jnlpool_ctl_ptr_t jnlpool_ctl; +GBLREF seq_num seq_num_zero; + +error_def (ERR_PREMATEOF); + +void jnl_write_epoch_rec(sgmnt_addrs *csa) +{ + struct_jrec_epoch epoch_record; + jnl_buffer_ptr_t jb; + jnl_private_control *jpc; + jnl_file_header *header; + unsigned char hdr_base[REAL_JNL_HDR_LEN + MAX_IO_BLOCK_SIZE]; + sgmnt_data_ptr_t csd; +#if defined(VMS) + io_status_block_disk iosb; +#endif + uint4 jnl_fs_block_size, read_write_size; + + assert(csa->now_crit); + jpc = csa->jnl; + jb = jpc->jnl_buff; + assert((csa->ti->early_tn == csa->ti->curr_tn) || (csa->ti->early_tn == csa->ti->curr_tn + 1)); + assert(0 != jpc->pini_addr); + csd = csa->hdr; + epoch_record.prefix.jrec_type = JRT_EPOCH; + epoch_record.prefix.forwptr = epoch_record.suffix.backptr = EPOCH_RECLEN; + epoch_record.blks_to_upgrd = csd->blks_to_upgrd; + epoch_record.total_blks = csd->trans_hist.total_blks; + epoch_record.free_blocks = csd->trans_hist.free_blocks; + epoch_record.suffix.suffix_code = JNL_REC_SUFFIX_CODE; + /* in case jpc->pini_addr turns out to be zero (not clear how), we use the pini_addr field of the + * first PINI journal record in the journal file which is nothing but JNL_HDR_LEN. + */ + epoch_record.prefix.pini_addr = (0 == jpc->pini_addr) ? JNL_HDR_LEN : jpc->pini_addr; + jb->epoch_tn = epoch_record.prefix.tn = csa->ti->curr_tn; + /* At this point jgbl.gbl_jrec_time should be set by the caller */ + assert(jgbl.gbl_jrec_time); + epoch_record.prefix.time = jgbl.gbl_jrec_time; + /* we need to write epochs if jgbl.forw_phase_recovery so future recovers will have a closer turnaround point */ + jb->next_epoch_time = epoch_record.prefix.time + jb->epoch_interval; + epoch_record.prefix.checksum = INIT_CHECKSUM_SEED; + ASSERT_JNL_SEQNO_FILEHDR_JNLPOOL(csd, jnlpool_ctl); /* debug-only sanity check between seqno of filehdr and jnlpool */ + if (jgbl.forw_phase_recovery) + /* As the file header is not flushed too often and recover/rollback doesn't update reg_seqno */ + QWASSIGN(epoch_record.jnl_seqno, jgbl.mur_jrec_seqno); + else if (REPL_ALLOWED(csd)) + QWASSIGN(epoch_record.jnl_seqno, csd->reg_seqno); /* Note we cannot use jnlpool_ctl->jnl_seqno since + * we might not presently hold the journal pool lock */ + else + QWASSIGN(epoch_record.jnl_seqno, seq_num_zero); + if (jb->end_of_data) + { + jnl_fs_block_size = jb->fs_block_size; + header = (jnl_file_header *)(ROUND_UP2((uintszofptr_t)hdr_base, jnl_fs_block_size)); + read_write_size = ROUND_UP2(REAL_JNL_HDR_LEN, jnl_fs_block_size); + assert((unsigned char *)header + read_write_size <= ARRAYTOP(hdr_base)); + DO_FILE_READ(jpc->channel, 0, header, read_write_size, jpc->status, jpc->status2); + assert(SS_NORMAL != jpc->status || SS_NORMAL == jpc->status2); + if (SS_NORMAL == jpc->status) + { + header->end_of_data = jb->end_of_data; + csa->hdr->jnl_eovtn = header->eov_tn; + header->eov_tn = jb->eov_tn; + header->eov_timestamp = jb->eov_timestamp; + header->end_seqno = jb->end_seqno; + DO_FILE_WRITE(jpc->channel, 0, header, read_write_size, jpc->status, jpc->status2); + /* for abnormal status do not do anything. journal file header will have previous end_of_data */ + } + } + jb->end_of_data = jb->freeaddr; + jb->eov_tn = csa->ti->curr_tn; + jb->eov_timestamp = jgbl.gbl_jrec_time; + jb->end_seqno = epoch_record.jnl_seqno; + jnl_write(jpc, JRT_EPOCH, (jnl_record *)&epoch_record, NULL, NULL); +} diff --git a/sr_port/jnl_write_inctn_rec.c b/sr_port/jnl_write_inctn_rec.c new file mode 100644 index 0000000..78ebd8d --- /dev/null +++ b/sr_port/jnl_write_inctn_rec.c @@ -0,0 +1,75 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#ifdef VMS +#include /* Required for gtmsource.h */ +#endif +#include "gtm_inet.h" + +#include "gtm_time.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "jnl.h" +#include "jnl_write.h" +#include "repl_msg.h" +#include "gtmsource.h" +#include "jnl_get_checksum.h" + +GBLREF jnlpool_ctl_ptr_t jnlpool_ctl; +GBLREF jnl_gbls_t jgbl; +GBLREF inctn_opcode_t inctn_opcode; +GBLREF inctn_detail_t inctn_detail; /* holds detail to fill in to inctn jnl record */ + +void jnl_write_inctn_rec(sgmnt_addrs *csa) +{ + struct_jrec_inctn inctn_record; + jnl_private_control *jpc; + DEBUG_ONLY(int inctn_detail_size;) + + assert(csa->now_crit); + jpc = csa->jnl; + assert(0 != jpc->pini_addr); + assert((csa->ti->early_tn == csa->ti->curr_tn) || (csa->ti->early_tn == csa->ti->curr_tn + 1)); + inctn_record.prefix.jrec_type = JRT_INCTN; + inctn_record.prefix.forwptr = INCTN_RECLEN; + assert(&inctn_detail.blknum_struct.suffix == &inctn_detail.blks2upgrd_struct.suffix); + DEBUG_ONLY(inctn_detail_size = OFFSETOF(inctn_detail_blknum_t, suffix) + SIZEOF(inctn_detail.blknum_struct.suffix);) + assert(0 == (inctn_detail_size % JNL_REC_START_BNDRY)); + assert(SIZEOF(inctn_detail) == inctn_detail_size); + inctn_detail.blknum_struct.suffix.backptr = INCTN_RECLEN; + inctn_detail.blknum_struct.suffix.suffix_code = JNL_REC_SUFFIX_CODE; + inctn_record.prefix.pini_addr = (0 == jpc->pini_addr) ? JNL_HDR_LEN : jpc->pini_addr; + /* At this point jgbl.gbl_jrec_time should be set by the caller */ + assert(jgbl.gbl_jrec_time); + inctn_record.prefix.time = jgbl.gbl_jrec_time; + inctn_record.prefix.tn = csa->ti->curr_tn; + inctn_record.prefix.checksum = INIT_CHECKSUM_SEED; + assert((inctn_opcode_total > inctn_opcode) && (inctn_invalid_op < inctn_opcode)); + /* Assert that the maximum inctn opcode # will fit in the "opcode" field in the inctn jnl record. + * But before that, assert opcode is at same offset in all the individual inctn_detail_* structure types. + */ + assert(&inctn_detail.blknum_struct.opcode == &inctn_detail.blks2upgrd_struct.opcode); + assert(inctn_opcode_total < (1 << (8 * SIZEOF(inctn_detail.blknum_struct.opcode)))); + inctn_detail.blknum_struct.opcode = inctn_opcode; /* fill in opcode from the global variable */ + /* Instead of having a multi-line switch statement that copies exactly those fields which are necessary, we + * copy the entire structure (16 bytes at this point). Pipeline breaks are considered more costly than a few + * unnecessary memory-to-memory copies. + */ + inctn_record.detail = inctn_detail; + jnl_write(jpc, JRT_INCTN, (jnl_record *)&inctn_record, NULL, NULL); +} diff --git a/sr_port/jnl_write_logical.c b/sr_port/jnl_write_logical.c new file mode 100644 index 0000000..48ba1d3 --- /dev/null +++ b/sr_port/jnl_write_logical.c @@ -0,0 +1,80 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_inet.h" +#ifdef VMS +#include /* Required for gtmsource.h */ +#endif + +#include "gtm_time.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "jnl.h" +#include "jnl_write.h" +#include "repl_msg.h" +#include "gtmsource.h" +#include "iosp.h" +#ifdef DEBUG +#include "jnl_typedef.h" +#endif + +GBLREF jnl_fence_control jnl_fence_ctl; +GBLREF uint4 dollar_tlevel; +GBLREF jnlpool_ctl_ptr_t temp_jnlpool_ctl; +GBLREF jnl_gbls_t jgbl; +GBLREF seq_num seq_num_zero; + +/* This called for TP and non-TP, but not for ZTP */ +void jnl_write_logical(sgmnt_addrs *csa, jnl_format_buffer *jfb) +{ + struct_jrec_upd *jrec; + GTMCRYPT_ONLY( + struct_jrec_upd *jrec_alt; + ) + jnl_private_control *jpc; + /* If REPL_WAS_ENABLED(csa) is TRUE, then we would not have gone through the code that initializes + * jgbl.gbl_jrec_time or jpc->pini_addr. But in this case, we are not writing the journal record + * to the journal buffer or journal file but write it only to the journal pool from where it gets + * sent across to the update process that does not care about these fields so it is ok to leave them as is. + */ + jpc = csa->jnl; + assert((0 != jpc->pini_addr) || REPL_WAS_ENABLED(csa)); + assert(jgbl.gbl_jrec_time || REPL_WAS_ENABLED(csa)); + assert(csa->now_crit); + assert(IS_SET_KILL_ZKILL_ZTRIG_ZTWORM(jfb->rectype)); + assert(!IS_ZTP(jfb->rectype)); + jrec = (struct_jrec_upd *)jfb->buff; + jrec->prefix.pini_addr = (0 == jpc->pini_addr) ? JNL_HDR_LEN : jpc->pini_addr; + jrec->prefix.tn = csa->ti->curr_tn; + jrec->prefix.time = jgbl.gbl_jrec_time; + jrec->prefix.checksum = jfb->checksum; + /* t_end/tp_tend/mur_output_record has already set token/jnl_seqno into jnl_fence_ctl.token */ + assert((0 != jnl_fence_ctl.token) || (!dollar_tlevel && !jgbl.forw_phase_recovery && !REPL_ENABLED(csa)) + || (!dollar_tlevel && jgbl.forw_phase_recovery && (repl_open != csa->hdr->intrpt_recov_repl_state))); + QWASSIGN(jrec->token_seq.token, jnl_fence_ctl.token); +# ifdef GTM_CRYPT + if (REPL_ALLOWED(csa)) + { + jrec_alt = (struct_jrec_upd *)jfb->alt_buff; + jrec_alt->prefix = jrec->prefix; + QWASSIGN(jrec_alt->token_seq, jrec->token_seq); + } +# endif + JNL_WRITE_APPROPRIATE(csa, jpc, jfb->rectype, (jnl_record *)jrec, NULL, jfb); +} diff --git a/sr_port/jnl_write_pblk.c b/sr_port/jnl_write_pblk.c new file mode 100644 index 0000000..7b7ac59 --- /dev/null +++ b/sr_port/jnl_write_pblk.c @@ -0,0 +1,76 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include + +#include "gtm_string.h" +#include "gtm_time.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "gdscc.h" +#include "jnl.h" +#include "jnl_write.h" +#include "jnl_write_pblk.h" +#include "min_max.h" +#include "jnl_get_checksum.h" + +GBLREF jnl_gbls_t jgbl; +GBLREF boolean_t dse_running; + +void jnl_write_pblk(sgmnt_addrs *csa, cw_set_element *cse, blk_hdr_ptr_t buffer) +{ + struct_jrec_blk pblk_record; + int tmp_jrec_size, jrec_size, zero_len; + jnl_format_buffer blk_trailer; + char local_buff[JNL_REC_START_BNDRY + JREC_SUFFIX_SIZE]; + jrec_suffix *suffix; + jnl_private_control *jpc; + + assert(csa->now_crit); + jpc = csa->jnl; + assert(0 != jpc->pini_addr); + pblk_record.prefix.jrec_type = JRT_PBLK; + pblk_record.prefix.pini_addr = (0 == jpc->pini_addr) ? JNL_HDR_LEN : jpc->pini_addr; + pblk_record.prefix.tn = csa->ti->curr_tn; + /* At this point jgbl.gbl_jrec_time should be set by the caller */ + assert(jgbl.gbl_jrec_time); + pblk_record.prefix.time = jgbl.gbl_jrec_time; + pblk_record.prefix.checksum = cse->blk_checksum; + pblk_record.blknum = cse->blk; + /* in case we have a bad block-size, we dont want to write a PBLK larger than the GDS block size (maximum block size). + * in addition, check that checksum computed in t_end/tp_tend did take the adjusted bsiz into consideration. + */ + assert(buffer->bsiz <= csa->hdr->blk_size || dse_running); + pblk_record.bsiz = MIN(csa->hdr->blk_size, buffer->bsiz); + assert((pblk_record.bsiz == buffer->bsiz) || + (cse->blk_checksum == jnl_get_checksum((uint4 *)buffer, NULL, pblk_record.bsiz))); + assert(pblk_record.bsiz >= SIZEOF(blk_hdr) || dse_running); + pblk_record.ondsk_blkver = cse->ondsk_blkver; + tmp_jrec_size = (int)FIXED_PBLK_RECLEN + pblk_record.bsiz + JREC_SUFFIX_SIZE; + jrec_size = ROUND_UP2(tmp_jrec_size, JNL_REC_START_BNDRY); + zero_len = jrec_size - tmp_jrec_size; + blk_trailer.buff = local_buff + (JNL_REC_START_BNDRY - zero_len); + memset(blk_trailer.buff, 0, zero_len); + blk_trailer.record_size = zero_len + JREC_SUFFIX_SIZE; + suffix = (jrec_suffix *)&local_buff[JNL_REC_START_BNDRY]; + pblk_record.prefix.forwptr = suffix->backptr = jrec_size; + suffix->suffix_code = JNL_REC_SUFFIX_CODE; + assert(SIZEOF(uint4) == SIZEOF(jrec_suffix)); + jnl_write(jpc, JRT_PBLK, (jnl_record *)&pblk_record, buffer, &blk_trailer); +} diff --git a/sr_port/jnl_write_pblk.h b/sr_port/jnl_write_pblk.h new file mode 100644 index 0000000..4928d3c --- /dev/null +++ b/sr_port/jnl_write_pblk.h @@ -0,0 +1,19 @@ +/**************************************************************** + * * + * Copyright 2001, 2005 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __JNL_WRITE_PBLK_H__ +#define __JNL_WRITE_PBLK_H__ + +/* We do not put this in jnl.h, because it needs all including jnl.h must include gdsblk.h */ +void jnl_write_pblk(sgmnt_addrs *csa, cw_set_element *cse, blk_hdr_ptr_t buffer); + +#endif + diff --git a/sr_port/jnl_write_poolonly.c b/sr_port/jnl_write_poolonly.c new file mode 100644 index 0000000..212555d --- /dev/null +++ b/sr_port/jnl_write_poolonly.c @@ -0,0 +1,109 @@ +/**************************************************************** + * * + * Copyright 2007, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_string.h" +#include "gtm_inet.h" + +#include /* for offsetof() macro */ + +#ifdef VMS +#include /* Required for gtmsource.h */ +#endif + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "ccp.h" +#include "iosp.h" +#include "jnl.h" +#include "repl_msg.h" +#include "gtmsource.h" +#include "min_max.h" +#include "sleep_cnt.h" +#include "jnl_write.h" +#include "copy.h" + +GBLREF jnlpool_ctl_ptr_t temp_jnlpool_ctl; + +GBLREF uint4 process_id; +GBLREF sm_uc_ptr_t jnldata_base; +GBLREF jnlpool_addrs jnlpool; +GBLREF jnlpool_ctl_ptr_t jnlpool_ctl; +GBLREF jnl_gbls_t jgbl; + +/* This function does a subset of what "jnl_write" does. While "jnl_write" writes the journal record to the journal buffer, + * journal file and journal pool, this function writes the journal records ONLY TO the journal pool. This function should + * be invoked only if replication state is WAS_ON (repl_was_open) and journaling state is jnl_closed. + * + * jpc : Journal private control + * rectype : Record type + * jnl_rec : This contains fixed part of a variable size record or the complete fixed size records. + * jfb : For SET/KILL/ZKILL records entire record is formatted in this. + */ +void jnl_write_poolonly(jnl_private_control *jpc, enum jnl_record_type rectype, jnl_record *jnl_rec, jnl_format_buffer *jfb) +{ + int4 align_rec_len, rlen, rlen_with_align, srclen, dstlen; + jnl_buffer_ptr_t jb; + sgmnt_addrs *csa; + struct_jrec_align align_rec; + uint4 status; + jrec_suffix suffix; + boolean_t nowrap; + struct_jrec_blk *jrec_blk; + uint4 jnlpool_size; + uchar_ptr_t jnlrecptr; + DEBUG_ONLY(uint4 lcl_dskaddr;) + uchar_ptr_t tmp_buff; + + error_def(ERR_JNLWRTNOWWRTR); + error_def(ERR_JNLWRTDEFER); + + assert(NULL != jnl_rec); + assert(rectype > JRT_BAD && rectype < JRT_RECTYPES && JRT_ALIGN != rectype); + assert(jrt_is_replicated[rectype]); + assert((NULL != jnlpool.jnlpool_ctl) && (NULL != jnlpool_ctl)); /* ensure we haven't yet detached from the jnlpool */ + assert((&FILE_INFO(jnlpool.jnlpool_dummy_reg)->s_addrs)->now_crit); /* ensure we have the jnl pool lock */ + csa = &FILE_INFO(jpc->region)->s_addrs; + assert(!JNL_ENABLED(csa) && REPL_WAS_ENABLED(csa)); + assert(csa->now_crit || (csa->hdr->clustered && csa->nl->ccp_state == CCST_CLOSED)); + jb = jpc->jnl_buff; + ++jb->reccnt[rectype]; + rlen = jnl_rec->prefix.forwptr; + assert(0 == rlen % JNL_REC_START_BNDRY); + jb->bytcnt += rlen; + DEBUG_ONLY(jgbl.cu_jnl_index++;) + jnlpool_size = temp_jnlpool_ctl->jnlpool_size; + dstlen = jnlpool_size - temp_jnlpool_ctl->write; + if (jrt_fixed_size[rectype]) + jnlrecptr = (uchar_ptr_t)jnl_rec; +# ifdef GTM_CRYPT + else if(csa->hdr->is_encrypted && IS_SET_KILL_ZKILL_ZTRIG_ZTWORM(rectype)) + jnlrecptr = (uchar_ptr_t)jfb->alt_buff; +# endif + else + jnlrecptr = (uchar_ptr_t)jfb->buff; + + if (rlen <= dstlen) /* dstlen & srclen >= rlen (most frequent case) */ + memcpy(jnldata_base + temp_jnlpool_ctl->write, jnlrecptr, rlen); + else /* dstlen < rlen <= srclen */ + { + memcpy(jnldata_base + temp_jnlpool_ctl->write, jnlrecptr, dstlen); + memcpy(jnldata_base, jnlrecptr + dstlen, rlen - dstlen); + } + temp_jnlpool_ctl->write += rlen; + if (temp_jnlpool_ctl->write >= jnlpool_size) + temp_jnlpool_ctl->write -= jnlpool_size; +} diff --git a/sr_port/jnl_write_ztp_logical.c b/sr_port/jnl_write_ztp_logical.c new file mode 100644 index 0000000..e18c19e --- /dev/null +++ b/sr_port/jnl_write_ztp_logical.c @@ -0,0 +1,83 @@ +/**************************************************************** + * * + * Copyright 2003, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_inet.h" +#ifdef VMS +#include /* Required for gtmsource.h */ +#endif + +#include "gtm_time.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsblk.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "jnl.h" +#include "jnl_write.h" +#include "repl_msg.h" +#include "gtmsource.h" +#include "iosp.h" +#ifdef DEBUG +#include "jnl_typedef.h" +#endif + +GBLREF jnl_fence_control jnl_fence_ctl; +GBLREF jnlpool_ctl_ptr_t temp_jnlpool_ctl; +GBLREF jnl_gbls_t jgbl; +GBLREF seq_num seq_num_zero; +GBLREF trans_num local_tn; /* transaction number for THIS PROCESS */ +GBLREF uint4 process_id; + +void jnl_write_ztp_logical(sgmnt_addrs *csa, jnl_format_buffer *jfb) +{ + struct_jrec_upd *jrec; + volatile seq_num temp_seqno; + GTMCRYPT_ONLY( + struct_jrec_upd *jrec_alt; + ) + jnl_private_control *jpc; + + /* If REPL_WAS_ENABLED(csa) is TRUE, then we would not have gone through the code that initializes + * jgbl.gbl_jrec_time or jpc->pini_addr. But in this case, we are not writing the journal record + * to the journal buffer or journal file but write it only to the journal pool from where it gets + * sent across to the update process that does not care about these fields so it is ok to leave them as is. + */ + jpc = csa->jnl; + assert((0 != jpc->pini_addr) || REPL_WAS_ENABLED(csa)); + assert(jgbl.gbl_jrec_time || REPL_WAS_ENABLED(csa)); + assert(csa->now_crit); + assert(IS_SET_KILL_ZKILL_ZTRIG(jfb->rectype)); + assert(IS_ZTP(jfb->rectype)); + jrec = (struct_jrec_upd *)jfb->buff; + jrec->prefix.pini_addr = (0 == jpc->pini_addr) ? JNL_HDR_LEN : jpc->pini_addr; + jrec->prefix.tn = csa->ti->curr_tn; + jrec->prefix.time = jgbl.gbl_jrec_time; + jrec->prefix.checksum = jfb->checksum; + temp_seqno = temp_jnlpool_ctl->jnl_seqno; + if (QWEQ(jnl_fence_ctl.token, seq_num_zero)) + { /* generate token once after op_ztstart and use for all its mini-transactions + * jnl_fence_ctl.token is set to seq_num_zero in op_ztstart */ + if (REPL_ALLOWED(csa)) + QWASSIGN(jnl_fence_ctl.token, temp_seqno); + else + { + TOKEN_SET(&jnl_fence_ctl.token, local_tn, process_id); + } + } + assert(0 != jnl_fence_ctl.token); + QWASSIGN(jrec->token_seq.token, jnl_fence_ctl.token); + GTMCRYPT_ONLY(assert(!REPL_ALLOWED(csa));) + JNL_WRITE_APPROPRIATE(csa, jpc, jfb->rectype, (jnl_record *)jrec, NULL, jfb); +} diff --git a/sr_port/jnlpool_hasnt_overflowed.c b/sr_port/jnlpool_hasnt_overflowed.c new file mode 100644 index 0000000..be35c45 --- /dev/null +++ b/sr_port/jnlpool_hasnt_overflowed.c @@ -0,0 +1,41 @@ +/**************************************************************** + * * + * Copyright 2003, 2004 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "memcoherency.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "repl_msg.h" +#ifdef VMS +#include +#endif +#include "gtmsource.h" + +boolean_t jnlpool_hasnt_overflowed(jnlpool_ctl_ptr_t jctl, uint4 jnlpool_size, qw_num read_addr) +{ /* the advantage of passing the three arguments is they are likely (on most platforms) to be scratch registers that can also + * be used for computation. Also, besides all callers would have already loaded jctl and jnlpool_size */ + + /* For systems with UNORDERED memory access (example, ALPHA, POWER4, PA-RISC 2.0), on a multi processor system, it is + * possible that the source server notices the change in jnlpool_ctl->write_addr before seeing the change to the content + * to be read (including the jnl_data_header). To avoid such conditions, we should commit the order of shared memory + * updates before and after the content is updated (see t_end.c, tp_tend.c). To ensure the source server reads content + * that is correct, it should invalidate its cache before the read. After the read, to ensure that the content is correct + * (not some that may have been overwritten), it has to invalidate its cache to fetch the latest value of early_write_addr. + * + */ + SHM_READ_MEMORY_BARRIER; /* to fetch the latest early_write_addr */ + return (jnlpool_size >= (jctl->early_write_addr - read_addr)); +} diff --git a/sr_port/job.h b/sr_port/job.h new file mode 100644 index 0000000..de49a91 --- /dev/null +++ b/sr_port/job.h @@ -0,0 +1,19 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __JOB_H_ +#define __JOB_H_ + +int4 ojchkfs(char *addr, int4 len, bool exist); + +#include "jobsp.h" + +#endif diff --git a/sr_port/job_addr.c b/sr_port/job_addr.c new file mode 100644 index 0000000..5b36815 --- /dev/null +++ b/sr_port/job_addr.c @@ -0,0 +1,44 @@ +/**************************************************************** + * * + * Copyright 2001, 2004 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "cmd_qlf.h" +#include "rtnhdr.h" +#include "op.h" +#include "job_addr.h" +#include "zbreak.h" + +void job_addr(mstr *rtn, mstr *label, int4 offset, char **hdr, char **labaddr) +{ + rhdtyp *rt_hdr; + int4 *lp; + error_def (ERR_JOBLABOFF); + + if ((rt_hdr = find_rtn_hdr(rtn)) == 0) + { + mval rt; + + rt.mvtype = MV_STR; + rt.str = *rtn; + op_zlink(&rt,0); + if ((rt_hdr = find_rtn_hdr (rtn)) == 0) + GTMASSERT; + } + lp = NULL; + if ((rt_hdr->compiler_qlf & CQ_LINE_ENTRY) || 0 == offset) + { /* label offset with routine compiled with NOLINE_ENTRY should cause error */ + lp = find_line_addr(rt_hdr, label, offset, NULL); + } + if (!lp) + rts_error(VARLSTCNT(1) ERR_JOBLABOFF); + *labaddr = (char *) LINE_NUMBER_ADDR(rt_hdr, lp); + *hdr = (char *)rt_hdr; +} diff --git a/sr_port/job_addr.h b/sr_port/job_addr.h new file mode 100644 index 0000000..2ef8f5b --- /dev/null +++ b/sr_port/job_addr.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __JOB_ADDR_H_ +#define __JOB_ADDR_H_ + +void job_addr(mstr *rtn, mstr *label, int4 offset, char **hdr, char **labaddr); + +#endif diff --git a/sr_port/jobexam_process.c b/sr_port/jobexam_process.c new file mode 100644 index 0000000..b3eafbf --- /dev/null +++ b/sr_port/jobexam_process.c @@ -0,0 +1,234 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include +#include +#include "gtm_unistd.h" +#include "gtm_string.h" +#include "gtm_stdio.h" + +#include "error.h" +#include "io_params.h" +#include "op.h" +#include "io.h" +#include "rtnhdr.h" +#include "stack_frame.h" +#include "jobexam_process.h" +#ifdef UNIX +# include "jobexam_signal_handler.h" +#endif +#include "send_msg.h" +#include "callg.h" +#include "zshow.h" +#include "util.h" +#include "mv_stent.h" + +#define DEFAULT_DUMP_FILENAME "GTM_JOBEXAM.ZSHOW_DMP" +#define NOCONCEAL_OPTION "NO_CONCEAL" + +static readonly mval empty_str_mval = DEFINE_MVAL_LITERAL(MV_STR, 0, 0, 0, 0, 0, 0); +static readonly mval no_conceal_op = DEFINE_MVAL_LITERAL(MV_STR, 0, 0, SIZEOF(NOCONCEAL_OPTION) - 1, + NOCONCEAL_OPTION, 0, 0); +static unsigned char dumpable_error_dump_file_parms[2] = {iop_newversion, iop_eol}; +static unsigned char dumpable_error_dump_file_noparms[1] = {iop_eol}; +static unsigned int jobexam_counter; + +GBLREF uint4 process_id; +GBLREF io_pair io_std_device, io_curr_device; +GBLREF mv_stent *mv_chain; +GBLREF unsigned char *msp, *stackwarn, *stacktop; +GBLREF char *util_outptr, util_outbuff[OUT_BUFF_SIZE]; +GBLREF boolean_t created_core; +UNIX_ONLY(GBLREF sigset_t blockalrm;) +DEBUG_ONLY(GBLREF boolean_t ok_to_UNWIND_in_exit_handling;) + +error_def(ERR_GTMASSERT); +error_def(ERR_GTMCHECK); +error_def(ERR_MEMORY); +error_def(ERR_VMSMEMORY); +error_def(ERR_OUTOFSPACE); +error_def(ERR_STACKOFLOW); +error_def(ERR_STACKCRIT); +error_def(ERR_JOBEXAMFAIL); +error_def(ERR_JOBEXAMDONE); + +void jobexam_process(mval *dump_file_name, mval *dump_file_spec) +{ + mval *input_dump_file_name; + io_pair dev_in_use; + mv_stent *new_mv_stent; + boolean_t saved_mv_stent; + char saved_util_outbuff[OUT_BUFF_SIZE]; + int saved_util_outbuff_len; +#ifdef UNIX + struct sigaction new_action, prev_action; + sigset_t savemask; +#endif + /* If the input file name is the result of an expression, it is likely being held in the + same temporary as the output file spec. We can tell if this is true by comparing the + address of the input and output mvals. If they are the same, make a copy of the input + filespec in a garbage collection safe mval prior to initializing the output mval + (which in this case would clear the input mval as well if it had not just been saved). + */ + if (dump_file_name == dump_file_spec) + { /* Make saved copy of input mval */ + PUSH_MV_STENT(MVST_MVAL); + new_mv_stent = mv_chain; + input_dump_file_name = &mv_chain->mv_st_cont.mvs_mval; + *input_dump_file_name = *dump_file_name; + saved_mv_stent = TRUE; + } else + { /* Just use input mval as-is */ + input_dump_file_name = dump_file_name; + saved_mv_stent = FALSE; + } + +#ifdef UNIX + /* Block out timer calls that might trigger processing that could fail. We especially want to prevent + nesting of signal handlers since the longjump() function used by the UNWIND macro is undefined on + Tru64 when signal handlers are nested. + */ + sigprocmask(SIG_BLOCK, &blockalrm, &savemask); + + /* Setup new signal handler to just drive condition handler which will do the right thing */ + memset(&new_action, 0, SIZEOF(new_action)); + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = SA_SIGINFO; +#ifdef __sparc + new_action.sa_handler = jobexam_signal_handler; +#else + new_action.sa_sigaction = jobexam_signal_handler; +#endif + sigaction(SIGBUS, &new_action, &prev_action); + sigaction(SIGSEGV, &new_action, 0); +#endif + *dump_file_spec = empty_str_mval; + dev_in_use = io_curr_device; /* Save current IO device */ + /* Save text in util_outbuff which can be detrimentally overwritten by ZSHOW */ + saved_util_outbuff_len = 0; + if (NULL == util_outptr) + util_outptr = util_outbuff; + if (0 != (saved_util_outbuff_len = (int)(util_outptr - util_outbuff))) /* Caution -- assignment */ + { + assert(0 <= saved_util_outbuff_len); + assert(saved_util_outbuff_len <= SIZEOF(saved_util_outbuff)); + memcpy(saved_util_outbuff, util_outbuff, saved_util_outbuff_len); + } + jobexam_dump(input_dump_file_name, dump_file_spec); + /* If any errors occur in job_exam_dump, the condition handler will unwind the stack to this point and return. */ + if (0 != saved_util_outbuff_len) + { /* Restore util_outbuff values */ + memcpy(util_outbuff, saved_util_outbuff, saved_util_outbuff_len); + util_outptr = util_outbuff + saved_util_outbuff_len; + } + io_curr_device = dev_in_use; /* Restore IO device */ + /* If we saved an mval on our stack, we need to pop it off. If there was an error while doing the + * jobexam dump, zshow may have left some other mv_stent entries on the stack. Pop them all off with + * just a regular POP_MV_STENT macro rather than unw_mv_ent() call because the mv_stent entries + * created in zshow_output reference automatic storage that cannot be referenced at this stack + * level without potential (C) stack corruption. + */ + if (saved_mv_stent) + { + if (mv_chain > new_mv_stent) + /* This violates our assumptions that the mv_stent we pushed onto the stack should + still be there */ + GTMASSERT; + while (mv_chain <= new_mv_stent) + { + POP_MV_STENT(); + } + } +#ifdef UNIX + /* Restore the signal handlers how they were */ + sigaction(SIGBUS, &prev_action, 0); + sigaction(SIGSEGV, &prev_action, 0); + /* Let the timers pop again.. */ + sigprocmask(SIG_SETMASK, &savemask, NULL); +#endif +} + +/* This routine is broken out as another ep so we can do cleanup processing in jobexam_process if + we trigger the condition handler and unwind. +*/ +void jobexam_dump(mval *dump_filename_arg, mval *dump_file_spec) +{ + unsigned char dump_file_name[50], *dump_file_name_ptr; + mval def_file_name, parms, zshowall; + + ESTABLISH(jobexam_dump_ch); + + ++jobexam_counter; + /* Setup default filename/type to use for the parse. Append processid and a counter. */ + MEMCPY_LIT(dump_file_name, DEFAULT_DUMP_FILENAME); + dump_file_name_ptr = dump_file_name + SIZEOF(DEFAULT_DUMP_FILENAME) - 1; + *dump_file_name_ptr++ = '_'; + dump_file_name_ptr = i2asc(dump_file_name_ptr, process_id); + *dump_file_name_ptr++ = '_'; + dump_file_name_ptr = i2asc(dump_file_name_ptr, jobexam_counter); + def_file_name.mvtype = MV_STR; + def_file_name.str.addr = (char *)dump_file_name; + def_file_name.str.len = INTCAST(dump_file_name_ptr - dump_file_name); + /* Call $ZPARSE processing to fill in any blanks, expand concealed logicals, etc. It is the callers + responsibility to make sure garbage collection knows about the value in the returned filespec. + */ + op_fnzparse(dump_filename_arg, &empty_str_mval, &def_file_name, &empty_str_mval, &no_conceal_op, dump_file_spec); + /* Parms of file to be created (newversion) */ + parms.mvtype = MV_STR; + parms.str.addr = (char *)dumpable_error_dump_file_parms; + parms.str.len = SIZEOF(dumpable_error_dump_file_parms); + /* Open, use, and zshow into new file, then close and reset current io device */ + op_open(dump_file_spec, &parms, 0, 0); + op_use(dump_file_spec, &parms); + zshowall.mvtype = MV_STR; + zshowall.str.addr = "*"; + zshowall.str.len = 1; + op_zshow(&zshowall, ZSHOW_DEVICE, NULL); + parms.str.addr = (char *)dumpable_error_dump_file_noparms; + parms.str.len = SIZEOF(dumpable_error_dump_file_noparms); + op_close(dump_file_spec, &parms); + /* Notify operator dump was taken */ + send_msg(VARLSTCNT(5) ERR_JOBEXAMDONE, 3, process_id, dump_file_spec->str.len, dump_file_spec->str.addr); + REVERT; +} + +CONDITION_HANDLER(jobexam_dump_ch) +{ + boolean_t save_created_core; + + START_CH; + + /* Operation: + 1) Flush out message we came here because of to operator console + 2) Put out our message stating that we screwed up + 3) Unwind the errant frames so we can return to the user without screwing + up the task that got interrupted to do this examine. + */ +#if defined(DEBUG) && defined(UNIX) + if (DUMPABLE) + { /* For debug UNIX issues, let's make a core if we would have made one in open code */ + save_created_core = created_core; + gtm_fork_n_core(); + created_core = save_created_core; + } +#endif + UNIX_ONLY(util_out_print(0, OPER)); + VMS_ONLY(sig->chf$l_sig_args -= 2); + VMS_ONLY(callg(send_msg, &sig->chf$l_sig_args)); + send_msg(VARLSTCNT(3) ERR_JOBEXAMFAIL, 1, process_id); + + /* Stop the errors here and return to caller */ + UNIX_ONLY(util_out_print("", RESET)); /* Prevent rts_error from flushing this error later */ + DEBUG_ONLY(ok_to_UNWIND_in_exit_handling = TRUE); + UNWIND(NULL, NULL); +} diff --git a/sr_port/jobexam_process.h b/sr_port/jobexam_process.h new file mode 100644 index 0000000..cd97707 --- /dev/null +++ b/sr_port/jobexam_process.h @@ -0,0 +1,19 @@ +/**************************************************************** + * * + * Copyright 2001, 2002 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef JOBEXAM_PROCESS_INCLUDED +#define JOBEXAM_PROCESS_INCLUDED + +void jobexam_process(mval *dump_file_name, mval *dump_file_spec); +void jobexam_dump(mval *dump_file_name, mval *dump_file_spec); + +#endif + diff --git a/sr_port/jobinterrupt_event.c b/sr_port/jobinterrupt_event.c new file mode 100644 index 0000000..e492199 --- /dev/null +++ b/sr_port/jobinterrupt_event.c @@ -0,0 +1,74 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* job interrupt event - an interrupt has been requested. + + - Call xfer_set_handlers so next M instruction traps to interrupt routine + - Other required housecleaning for VMS. + +*/ + +#include "mdef.h" + +#ifdef UNIX +# include +#else +# include +# include "efn.h" +#endif + +#include "gtm_stdio.h" +#include "io.h" +#include "op.h" +#include "xfer_enum.h" +#include "outofband.h" +#include "deferred_events.h" +#include "jobinterrupt_event.h" +#include "fix_xfer_entry.h" + +GBLREF xfer_entry_t xfer_table[]; +GBLREF volatile int4 outofband; +GBLREF volatile boolean_t dollar_zininterrupt; + +/* Routine called when an interrupt event occurs (signaled by mupip intrpt or other future method + of signaling interrupts). This code is driven as a signal handler on Unix and from the START_CH + macro on VMS where it intercepts the posix signal. +*/ +UNIX_ONLY(void jobinterrupt_event(int sig, siginfo_t *info, void *context)) +VMS_ONLY(void jobinterrupt_event(void)) +{ /* Note the (presently unused) args are to match signature for signal handlers in Unix */ + if (!dollar_zininterrupt) + (void)xfer_set_handlers(outofband_event, &jobinterrupt_set, 0); +} + +/* Call back routine from xfer_set_handlers to complete outofband setup */ +void jobinterrupt_set(int4 dummy_val) +{ + int4 status; + + VMS_ONLY( + status = sys$setef(efn_outofband); + assert(SS$_WASCLR == status); + if (SS$_WASCLR != status && SS$_WASSET != status) + GTMASSERT; + ) + if (jobinterrupt != outofband) + { /* We need jobinterrupt out of band processing at our earliest convenience */ + outofband = jobinterrupt; + FIX_XFER_ENTRY(xf_linefetch, op_fetchintrrpt); + FIX_XFER_ENTRY(xf_linestart, op_startintrrpt); + FIX_XFER_ENTRY(xf_zbfetch, op_fetchintrrpt); + FIX_XFER_ENTRY(xf_zbstart, op_startintrrpt); + FIX_XFER_ENTRY(xf_forchk1, op_startintrrpt); + FIX_XFER_ENTRY(xf_forloop, op_forintrrpt); + } + VMS_ONLY(sys$wake(0,0);) +} diff --git a/sr_port/jobinterrupt_event.h b/sr_port/jobinterrupt_event.h new file mode 100644 index 0000000..86a1d04 --- /dev/null +++ b/sr_port/jobinterrupt_event.h @@ -0,0 +1,20 @@ +/**************************************************************** + * * + * Copyright 2001, 2002 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef JOBINTR_EVENT_INCLUDED +#define JOBINTR_EVENT_INCLUDED + +UNIX_ONLY(void jobinterrupt_event(int sig, siginfo_t *info, void *context);) +VMS_ONLY(void jobinterrupt_event(void);) +void jobinterrupt_set(int4 dummy); + +#endif + diff --git a/sr_port/jobinterrupt_init.c b/sr_port/jobinterrupt_init.c new file mode 100644 index 0000000..35bddd9 --- /dev/null +++ b/sr_port/jobinterrupt_init.c @@ -0,0 +1,81 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* job interrupt initialization. Accomplish following setups: + + - Setup handler for SIGUSR1 signal to be handled by jobinterrupt_event() (UNIX only). + - Provide initial setting for $ZINTERRUPT from default or logical or + environment variable if present. + +*/ + +#include "mdef.h" + +#include + +#include "gtm_string.h" +#include "gtm_stdio.h" +#include "gtm_logicals.h" +#include "trans_log_name.h" +#include "io.h" +#include "iosp.h" +#include "stringpool.h" +#include "jobinterrupt_init.h" +#include "jobinterrupt_event.h" + +GBLREF mval dollar_zinterrupt; + +#define DEF_ZINTERRUPT "IF $ZJOBEXAM()" + +void jobinterrupt_init(void) +{ + mstr envvar_logical; + char trans_bufr[MAX_TRANS_NAME_LEN]; + DCL_THREADGBL_ACCESS; +#ifdef UNIX + struct sigaction new_action; + + SETUP_THREADGBL_ACCESS; + /* Setup new signal handler to just drive condition handler which will do the right thing. + Note that although we use send a posix-style signal with mupip intrpt on VMS, the signal + that comes in is NOT handled by posix signal handler because the posix handler is implemented + using native condition handlers which interfere with GT.M's use of native condition handlers. + The VMS signal is instead intercepted via the START_CH macro on VMS which then drives the + jobinterrupt_event routine. + */ + memset(&new_action, 0, SIZEOF(new_action)); + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = SA_SIGINFO; +#ifdef __sparc + new_action.sa_handler = jobinterrupt_event; +#else + new_action.sa_sigaction = jobinterrupt_event; +#endif + sigaction(SIGUSR1, &new_action, NULL); +#else + SETUP_THREADGBL_ACCESS; + /* Handler for VMS setup via function pointer called by START_CH macro */ + RFPTR(gtm_sigusr1_handler) = jobinterrupt_event; +#endif + + /* Provide initial setting for $ZINTERRUPT */ + envvar_logical.addr = GTM_ZINTERRUPT; + envvar_logical.len = SIZEOF(GTM_ZINTERRUPT) - 1; + if (SS_NORMAL != TRANS_LOG_NAME(&envvar_logical, &dollar_zinterrupt.str, trans_bufr, SIZEOF(trans_bufr), + do_sendmsg_on_log2long)) + { /* Translation failed - use default */ + dollar_zinterrupt.str.addr = DEF_ZINTERRUPT; + dollar_zinterrupt.str.len = SIZEOF(DEF_ZINTERRUPT) - 1; + } else /* put value in stringpool if translation succeeded */ + s2pool(&dollar_zinterrupt.str); + dollar_zinterrupt.mvtype = MV_STR; + return; +} diff --git a/sr_port/jobinterrupt_init.h b/sr_port/jobinterrupt_init.h new file mode 100644 index 0000000..834d467 --- /dev/null +++ b/sr_port/jobinterrupt_init.h @@ -0,0 +1,18 @@ +/**************************************************************** + * * + * Copyright 2001, 2002 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef JOBINTR_PROCESS_INCLUDED +#define JOBINTR_PROCESS_INCLUDED + +void jobinterrupt_init(void); + +#endif + diff --git a/sr_port/jobinterrupt_process.c b/sr_port/jobinterrupt_process.c new file mode 100644 index 0000000..1e228e0 --- /dev/null +++ b/sr_port/jobinterrupt_process.c @@ -0,0 +1,124 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "error.h" +#include "indir_enum.h" +#include "rtnhdr.h" +#include "op.h" +#include "stack_frame.h" +#include "error_trap.h" +#include "mv_stent.h" +#include "gtm_stdio.h" +#include "stringpool.h" +#include "jobinterrupt_process.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "hashtab_int4.h" +#include "gdskill.h" +#include "jnl.h" +#include "gdscc.h" +#include "buddy_list.h" +#include "tp.h" +#include "gvname_info.h" +#include "op_merge.h" +#include "zwrite.h" +#include "zshow.h" + +GBLREF stack_frame *frame_pointer, *error_frame; +GBLREF unsigned char *stackbase, *stacktop, *msp, *stackwarn; +GBLREF spdesc stringpool; +GBLREF mval dollar_zinterrupt; +GBLREF boolean_t dollar_zininterrupt; +GBLREF unsigned short proc_act_type; +GBLREF mv_stent *mv_chain; +GBLREF int dollar_truth; +GBLREF mstr extnam_str; +GBLREF unsigned char *restart_pc, *restart_ctxt; +GBLREF dollar_ecode_type dollar_ecode; +GBLREF dollar_stack_type dollar_stack; +GBLREF int merge_args; +GBLREF uint4 zwrtacindx; +GBLREF merge_glvn_ptr mglvnp; +GBLREF gvzwrite_datablk *gvzwrite_block; +GBLREF lvzwrite_datablk *lvzwrite_block; +GBLREF zshow_out *zwr_output; +GBLREF zwr_hash_table *zwrhtab; + +error_def(ERR_STACKOFLOW); +error_def(ERR_STACKCRIT); + +void jobinterrupt_process(void) +{ + mv_stent *mv_st_ent; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + assert(dollar_zininterrupt); + /* Compile and push new (counted) frame onto the stack to drive the + * $zinterrupt handler. + */ + assert((SFT_COUNT | SFT_ZINTR) == proc_act_type); + op_commarg(&dollar_zinterrupt, indir_linetail); + frame_pointer->type = proc_act_type; /* The mark of zorro.. */ + proc_act_type = 0; + /* Save restart_pc/ctxt so a resumed frame or ztrap can resume in the corrct place and + * not and inappropriate resume point determined by the interrupting code. + */ + PUSH_MV_STENT(MVST_RSTRTPC); + mv_st_ent = mv_chain; + mv_st_ent->mv_st_cont.mvs_rstrtpc.restart_pc_save = restart_pc; + mv_st_ent->mv_st_cont.mvs_rstrtpc.restart_ctxt_save = restart_ctxt; + /* Now we need to preserve our current environment. This MVST_ZINTR mv_stent type will hold + * the items deemed necessary to preserve. All other items are the user's responsibility. + * + * Initialize the mv_stent elements processed by stp_gcol which can be called for either the + * op_gvsavtarg() or extnam items. This initialization keeps stp_gcol from attempting to + * process unset fields with garbage in them as valid mstr address/length pairs. + */ + PUSH_MV_STENT(MVST_ZINTR); + mv_st_ent = mv_chain; + mv_st_ent->mv_st_cont.mvs_zintr.savtarg.str.len = 0; + mv_st_ent->mv_st_cont.mvs_zintr.savextref.len = 0; + mv_st_ent->mv_st_cont.mvs_zintr.saved_dollar_truth = dollar_truth; + op_gvsavtarg(&mv_st_ent->mv_st_cont.mvs_zintr.savtarg); + if (extnam_str.len) + { + ENSURE_STP_FREE_SPACE(extnam_str.len); + mv_st_ent->mv_st_cont.mvs_zintr.savextref.addr = (char *)stringpool.free; + memcpy(mv_st_ent->mv_st_cont.mvs_zintr.savextref.addr, extnam_str.addr, extnam_str.len); + stringpool.free += extnam_str.len; + assert(stringpool.free <= stringpool.top); + } + mv_st_ent->mv_st_cont.mvs_zintr.savextref.len = extnam_str.len; + /* save/restore $ECODE/$STACK over this invocation */ + mv_st_ent->mv_st_cont.mvs_zintr.error_frame_save = error_frame; + memcpy(&mv_st_ent->mv_st_cont.mvs_zintr.dollar_ecode_save, &dollar_ecode, SIZEOF(dollar_ecode)); + memcpy(&mv_st_ent->mv_st_cont.mvs_zintr.dollar_stack_save, &dollar_stack, SIZEOF(dollar_stack)); + NULLIFY_ERROR_FRAME; + ecode_init(); + /* If we interrupted a Merge, ZWrite, or ZShow, save the state info in an mv_stent that will be restored when this + * interrupt frame returns. Note that at this time, return from an interrupt does not "return" to the interrupt + * point but rather restarts the line of M code we were running *OR* at the most recent save point (set by + * op_restartpc or equivalent). In the future this is likely to change, at least for an interrupted Merge, ZWrite + * or ZShow command, so this save/restore is appropriate both now (to let these nest at all) and especially in the future. + */ + if (TREF(in_zwrite) || (0 != merge_args)) + PUSH_MVST_MRGZWRSV; + return; +} diff --git a/sr_port/jobinterrupt_process.h b/sr_port/jobinterrupt_process.h new file mode 100644 index 0000000..874fa37 --- /dev/null +++ b/sr_port/jobinterrupt_process.h @@ -0,0 +1,27 @@ +/**************************************************************** + * * + * Copyright 2001, 2003 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef JOBINTR_PROCESS_INCLUDED +#define JOBINTR_PROCESS_INCLUDED + +#define JOBINTR_TP_RETHROW \ +{ /* rethrow job interrupt($ZINT) if $ZTEXIT is true and not already in $ZINTR */ \ + GBLREF boolean_t dollar_zininterrupt; \ + GBLREF boolean_t dollar_ztexit_bool; \ + error_def(ERR_JOBINTRRETHROW); \ + \ + if (dollar_ztexit_bool && !dollar_zininterrupt) \ + rts_error(VARLSTCNT(1) ERR_JOBINTRRETHROW); \ +} + +void jobinterrupt_process(void); + +#endif diff --git a/sr_port/jobinterrupt_process_cleanup.c b/sr_port/jobinterrupt_process_cleanup.c new file mode 100644 index 0000000..2ba1aa3 --- /dev/null +++ b/sr_port/jobinterrupt_process_cleanup.c @@ -0,0 +1,91 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" +#include "rtnhdr.h" +#include "stack_frame.h" +#include "stringpool.h" +#include "objlabel.h" +#include "cache.h" +#include "dm_setup.h" +#include "error.h" +#include "error_trap.h" +#include "util.h" +#include "gtm_string.h" +#include "gtmmsg.h" +#include "jobinterrupt_process_cleanup.h" + +GBLREF stack_frame *frame_pointer; +GBLREF spdesc stringpool; +GBLREF spdesc rts_stringpool; +GBLREF unsigned short proc_act_type; +GBLREF volatile boolean_t dollar_zininterrupt; +GBLREF mval dollar_zstatus; +GBLREF dollar_ecode_type dollar_ecode; /* structure containing $ECODE related information */ + +error_def(ERR_ERRWZINTR); + +/* Counterpart to trans_code_cleanup for job interrupt errors */ +void jobinterrupt_process_cleanup(void) +{ + stack_frame *fp; + unsigned char msgbuf[OUT_BUFF_SIZE], *mbptr; + unsigned char *zstptr; + mstr msgbuff; + int zstlen; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + assert((SFT_COUNT | SFT_ZINTR) == proc_act_type); + assert(dollar_zininterrupt); + if (TREF(compile_time)) + { /* Make sure we are using the right stringpool */ + TREF(compile_time) = FALSE; + if (stringpool.base != rts_stringpool.base) + stringpool = rts_stringpool; + } + proc_act_type = 0; + /* Note, no frames are unwound as in trans_code_cleanup() because we should be + able to resume exactly where we left off since (1) it was not an error that + caused us to drive the $zinterrupt handler and (2) we were already on a nice + statement boundary when we were called to run $zinterrupt. + */ + TREF(transform) = TRUE; + dollar_zininterrupt = FALSE; /* No longer in a $zinterrupt */ + /* Now build message for operator log with the form ERRWZINTR, compiler-error */ + util_out_print(NULL, RESET); + msgbuff.addr = (char *)msgbuf; + msgbuff.len = SIZEOF(msgbuf); + gtm_getmsg(ERR_ERRWZINTR, &msgbuff); + mbptr = msgbuf + strlen((char *)msgbuf); + /* Find the beginning of the compiler error (look for "%") */ + zstptr = (unsigned char *)dollar_zstatus.str.addr; + for (zstlen = dollar_zstatus.str.len; zstlen; zstptr++, zstlen--) + { + if ('%' == *zstptr) + break; + } + if (zstlen) + { /* If found some message, add it to our operator missive */ + *mbptr++ = ','; + *mbptr++ = ' '; + memcpy(mbptr, zstptr, zstlen); + mbptr += zstlen; + } + *mbptr++ = 0; + util_out_print((caddr_t)msgbuf, OPER); + if (NULL == dollar_ecode.error_last_b_line) + { /* Was a direct mode frame this message needs to go out to the console */ + dec_err(VARLSTCNT(1) ERR_ERRWZINTR); + } +} diff --git a/sr_port/jobinterrupt_process_cleanup.h b/sr_port/jobinterrupt_process_cleanup.h new file mode 100644 index 0000000..f3d6530 --- /dev/null +++ b/sr_port/jobinterrupt_process_cleanup.h @@ -0,0 +1,18 @@ +/**************************************************************** + * * + * Copyright 2001, 2002 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef JOBINTR_PROCESS_CLEANUP_INCLUDED +#define JOBINTR_PROCESS_CLEANUP_INCLUDED + +void jobinterrupt_process_cleanup(void); + +#endif + diff --git a/sr_port/jobparameters.c b/sr_port/jobparameters.c new file mode 100644 index 0000000..b6835a7 --- /dev/null +++ b/sr_port/jobparameters.c @@ -0,0 +1,74 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "compiler.h" +/* +#include "opcode.h" +*/ + +#include "toktyp.h" +#include "job.h" +#include "advancewindow.h" + + +GBLREF char window_token; + + +int jobparameters (oprtype *c) +{ + char *parptr; + /* This is a workaround for the moment. The former maximum size of the + parameter string was completely incapable of handling the variety + of string parameters that may be passed. To further compound things, + no checks exist to enforce upper limits on parameter string lengths. + This new maximum was reached by calculating the maximum length of + the job parameter string (presuming each possible qualifier is represented + once and only once) and tacking on a safety net. This came to + 10 255 byte strings (plus length byte), 1 longword, and 17 single byte + identifiers for each of the job keywords. The maximum of 3000 leaves + a little room for expansion in the future + */ + char parastr[3000]; + error_def (ERR_RPARENMISSING); + + parptr = parastr; + if (window_token != TK_LPAREN) + { + if (window_token != TK_COLON) + { + if (!one_job_param (&parptr)) + return FALSE; + } + } else + { + advancewindow (); + for (;;) + { + if (!one_job_param (&parptr)) + return FALSE; + if (window_token == TK_COLON) + advancewindow (); + else if (window_token == TK_RPAREN) { + advancewindow (); + break; + } + else { + stx_error (ERR_RPARENMISSING); + return FALSE; + } + } + } + *parptr++ = jp_eol; + *c = put_str (parastr,(mstr_len_t)(parptr - parastr)); + return TRUE; +} diff --git a/sr_port/jobparams.h b/sr_port/jobparams.h new file mode 100644 index 0000000..704dd93 --- /dev/null +++ b/sr_port/jobparams.h @@ -0,0 +1,32 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + + + JPDEF (jp_eol, jpdt_nul), + JPDEF (jp_account, jpdt_nul), + JPDEF (jp_default, jpdt_str), + JPDEF (jp_detached, jpdt_nul), + JPDEF (jp_error, jpdt_str), + JPDEF (jp_gbldir, jpdt_str), + JPDEF (jp_image, jpdt_str), + JPDEF (jp_input, jpdt_str), + JPDEF (jp_logfile, jpdt_str), + JPDEF (jp_noaccount, jpdt_nul), + JPDEF (jp_nodetached, jpdt_nul), + JPDEF (jp_noswapping, jpdt_nul), + JPDEF (jp_output, jpdt_str), + JPDEF (jp_priority, jpdt_num), + JPDEF (jp_process_name, jpdt_str), + JPDEF (jp_schedule, jpdt_str), + JPDEF (jp_startup, jpdt_str), + JPDEF (jp_swapping, jpdt_nul), + JPDEF (jp_nmvals, jpdt_nul) + diff --git a/sr_port/jobparamstrs.h b/sr_port/jobparamstrs.h new file mode 100644 index 0000000..3764529 --- /dev/null +++ b/sr_port/jobparamstrs.h @@ -0,0 +1,30 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + + +JPSDEF ( 3, "ACC", jp_account), JPSDEF ( 8, "ACCOUNTI*", jp_account), +JPSDEF ( 3, "DEF", jp_default), JPSDEF ( 7, "DEFAULT", jp_default), +JPSDEF ( 3, "DET", jp_detached), JPSDEF ( 8, "DETACHED", jp_detached), +JPSDEF ( 3, "ERR", jp_error), JPSDEF ( 5, "ERROR", jp_error), +JPSDEF ( 3, "GBL", jp_gbldir), JPSDEF ( 6, "GBLDIR", jp_gbldir), +JPSDEF ( 2, "IM", jp_image), JPSDEF ( 5, "IMAGE", jp_image), +JPSDEF ( 2, "IN", jp_input), JPSDEF ( 5, "INPUT", jp_input), +JPSDEF ( 3, "LOG", jp_logfile), JPSDEF ( 7, "LOGFILE", jp_logfile), +JPSDEF ( 5, "NOACC", jp_noaccount), JPSDEF ( 8, "NOACCOUN*", jp_noaccount), +JPSDEF ( 5, "NODET", jp_nodetached), JPSDEF ( 8, "NODETACH*", jp_nodetached), +JPSDEF ( 5, "NOSWA", jp_noswapping), JPSDEF ( 8, "NOSWAPPI*", jp_noswapping), +JPSDEF ( 3, "OUT", jp_output), JPSDEF ( 6, "OUTPUT", jp_output), +JPSDEF ( 3, "PRI", jp_priority), JPSDEF ( 8, "PRIORITY", jp_priority), +JPSDEF ( 3, "PRO", jp_process_name), JPSDEF ( 7, "PROCESS*", jp_process_name), +JPSDEF ( 3, "SCH", jp_schedule), JPSDEF ( 8, "SCHEDULE", jp_schedule), +JPSDEF ( 3, "STA", jp_startup), JPSDEF ( 7, "STARTUP", jp_startup), +JPSDEF ( 3, "SWA", jp_swapping), JPSDEF ( 8, "SWAPPING", jp_swapping) + diff --git a/sr_port/la_encrypt.c b/sr_port/la_encrypt.c new file mode 100644 index 0000000..f0f6dc0 --- /dev/null +++ b/sr_port/la_encrypt.c @@ -0,0 +1,82 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* la_encrypt.c : for given encryption function number, and an input sequence of bytes, + the program computes the checksum of the sequence. + used in : la_create.c,lp_licensed.c,lm_verify +*/ + +#include "mdef.h" +#include "la_encrypt.h" + + +static void la_encrypt_table (uint4 poly, uint4 tbl[]); +static uint4 la_encrypt_value ( +uint4 tbl[], /* polynomial coefficients */ +uint4 cs, /* checksum initial value */ +unsigned char *c, /* string to compute the check sum for */ +int len); /* string length */ + +bool la_encrypt ( +short n , /* encryption function number */ +char *q , /* input sequence */ +int len, /* sequence length */ +uint4 bcs[]) /* result, binary form */ +{ + static uint4 poly0[10] = {0xEDB88320, 0xA001A001, 0x00008408, 0x00000000, 0xA001A001, 1, 1, 1, 1, 1}; + static uint4 init0[10] = {0xFFFFFFFF, 0x00000000, 0x0000FFFF, 0xFFFFFFFF, 0x00000000, 0, 0, 0, 0, 0}; + static uint4 poly1[10] = {0xA001A001, 0x00008408, 0xEDB88320, 0xEDB88320, 0x0000A001, 1, 1, 1, 1, 1}; + static uint4 init1[10] = {0x00000000, 0x0000FFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0, 0, 0, 0, 0}; + uint4 crctbl[16]; + if (n<5) + { + la_encrypt_table(poly0[n],crctbl) ; + bcs[0] = la_encrypt_value(crctbl,init0[n],(uchar_ptr_t)q,len) ; + la_encrypt_table(poly1[n],crctbl) ; + bcs[1] = la_encrypt_value(crctbl,init1[n],(uchar_ptr_t)q,len) ; + } + return n<5 ; +} + + +static void la_encrypt_table (uint4 poly, uint4 tbl[]) +{ + uint4 k, t, x; + int i; + for ( k= 0 ; k!=16 ; k++ ) + { + t= k ; + for ( i= 0 ; i!=4 ; i++ ) + { + x= t & 1 ; + t= t>>1 ; + if (x==1) t ^= poly ; + } + tbl[k]= t ; + } +} + + +static uint4 la_encrypt_value ( +uint4 tbl[], /* polynomial coefficients */ +uint4 cs, /* checksum initial value */ +unsigned char *c, /* string to compute the check sum for */ +int len) /* string length */ +{ + while ( len!=0 ) + { + cs ^= (int4)*c ; /* least signif. byte of cs differred with *c */ + cs = ( cs >> 4 ) ^ tbl[cs & 0xF] ; + cs = ( cs >> 4 ) ^ tbl[cs & 0xF] ; + c++ ; len-- ; + } + return cs ; +} diff --git a/sr_port/la_encrypt.h b/sr_port/la_encrypt.h new file mode 100644 index 0000000..9acf6f6 --- /dev/null +++ b/sr_port/la_encrypt.h @@ -0,0 +1,20 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ +#ifndef __LA_ENCRYPT_H__ +#define __LA_ENCRYPT_H__ + +bool la_encrypt ( + short n , /* encryption function number */ + char *q , /* input sequence */ + int len, /* sequence length */ + uint4 bcs[]); /* result, binary form */ + +#endif diff --git a/sr_port/lastchance1.c b/sr_port/lastchance1.c new file mode 100644 index 0000000..e69985f --- /dev/null +++ b/sr_port/lastchance1.c @@ -0,0 +1,51 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_unistd.h" + +#include "io.h" +#include "iosp.h" +#include "error.h" +#include "gv_rundown.h" +#include "util.h" + +GBLREF int4 exi_condition; +GBLREF boolean_t created_core; +GBLREF boolean_t dont_want_core; + +CONDITION_HANDLER(lastchance1) +{ + int4 actual_exi_condition; + + error_def(ERR_ASSERT); + error_def(ERR_FORCEDHALT); + error_def(ERR_GTMASSERT); + error_def(ERR_GTMCHECK); + error_def(ERR_LKRUNDOWN); + error_def(ERR_MEMORY); + error_def(ERR_VMSMEMORY); + error_def(ERR_STACKOFLOW); + error_def(ERR_OUTOFSPACE); + + actual_exi_condition = exi_condition; + PRN_ERROR; + dec_err(VARLSTCNT(1) ERR_LKRUNDOWN); + ESTABLISH(lastchance2); + gv_rundown(); + REVERT; + ESTABLISH(lastchance3); + io_rundown(NORMAL_RUNDOWN); + REVERT; + if (DUMPABLE && !SUPPRESS_DUMP) + DUMP_CORE; + PROCDIE(actual_exi_condition); +} diff --git a/sr_port/lastchance2.c b/sr_port/lastchance2.c new file mode 100644 index 0000000..b0260f5 --- /dev/null +++ b/sr_port/lastchance2.c @@ -0,0 +1,61 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_unistd.h" + +#ifdef VMS +#include +#include +#endif + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "error.h" +#include "filestruct.h" +#include "io.h" +#include "iosp.h" +#include "jnl.h" +#include "util.h" + +GBLREF gd_region *gv_cur_region; +GBLREF int4 exi_condition; +GBLREF boolean_t created_core; +GBLREF boolean_t dont_want_core; + +static const unsigned short zero_fid[3]; + +CONDITION_HANDLER(lastchance2) +{ + int4 actual_exi_condition; + + error_def(ERR_ASSERT); + error_def(ERR_GTMASSERT); + error_def(ERR_GTMCHECK); + error_def(ERR_GVRUNDOWN); + error_def(ERR_MEMORY); + error_def(ERR_VMSMEMORY); + error_def(ERR_STACKOFLOW); + error_def(ERR_OUTOFSPACE); + + actual_exi_condition = exi_condition; + PRN_ERROR; + dec_err(VARLSTCNT(1) ERR_GVRUNDOWN); + ESTABLISH(lastchance3); + io_rundown(NORMAL_RUNDOWN); + REVERT; + if (DUMPABLE && !SUPPRESS_DUMP) + DUMP_CORE; + PROCDIE(actual_exi_condition); +} diff --git a/sr_port/lastchance3.c b/sr_port/lastchance3.c new file mode 100644 index 0000000..9e66dc5 --- /dev/null +++ b/sr_port/lastchance3.c @@ -0,0 +1,48 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_unistd.h" + +#include "error.h" +#include "io.h" +#include "util.h" + +GBLREF int4 exi_condition; +GBLREF boolean_t created_core; +GBLREF boolean_t dont_want_core; + +CONDITION_HANDLER(lastchance3) +{ + + error_def(ERR_ASSERT); + error_def(ERR_FORCEDHALT); + error_def(ERR_GTMASSERT); + error_def(ERR_GTMCHECK); + error_def(ERR_IORUNDOWN); + error_def(ERR_MEMORY); + error_def(ERR_VMSMEMORY); + error_def(ERR_STACKOFLOW); + error_def(ERR_OUTOFSPACE); + + START_CH; + ESTABLISH(terminate_ch); + if (DUMPABLE) + { + PRN_ERROR; + dec_err(VARLSTCNT(1) ERR_IORUNDOWN); + if (!SUPPRESS_DUMP) + DUMP_CORE; + PROCDIE(exi_condition); + } + REVERT; + UNWIND(NULL, NULL); +} diff --git a/sr_port/lb_init.c b/sr_port/lb_init.c new file mode 100644 index 0000000..6d7a6a0 --- /dev/null +++ b/sr_port/lb_init.c @@ -0,0 +1,145 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "compiler.h" +#include "toktyp.h" +#include "comp_esc.h" +#include "advancewindow.h" +#include "lb_init.h" + +GBLREF unsigned char *source_buffer; +GBLREF char *lexical_ptr; +GBLREF struct ce_sentinel_desc *ce_def_list; + +error_def(ERR_CETOOMANY); + +LITREF char ctypetab[NUM_CHARS]; + +#define MAX_SUBSTITUTIONS 1024 + +void lb_init(void) +{ + int num_subs, y; + short int sav_last_src_col, source_col; + int4 source_len, skip_count; + unsigned char *cp, *cp1; + bool possible_sentinel; + struct ce_sentinel_desc *shp; +# ifdef DEBUG + unsigned char original_source[MAX_SRCLINE]; +# endif + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + if (NULL != ce_def_list) + { + for (source_len = 0; '\0' != source_buffer[source_len]; source_len++); +# ifdef DEBUG + memcpy (original_source, source_buffer, source_len + 2); /* include terminating null characters */ +# endif + source_col = 1; + num_subs = 0; + cp = source_buffer; + possible_sentinel = TRUE; + while (possible_sentinel && source_buffer[source_col - 1]) + { + possible_sentinel = FALSE; + cp = source_buffer + source_col - 1; + if (DEL < *cp) + break; + if ('\"' == *cp) + { + for (cp1 = cp + 1; ; ) + { + if (SP > *cp1) + break; + if ('\"' == *cp1++) + { + if ('\"' == *cp1) + cp1++; /* escaped quotation mark inside string */ + else + break; /* end of string */ + } + } + source_col += (cp1 - cp); + cp = cp1; + if ('\0' == *cp) + break; + } else if ('?' == *cp) + { + for (cp1 = cp + 1; ; ) + { + if (NUM_ASCII_CHARS <= *cp1) + break; + y = ctypetab[*cp1]; + if ((TK_UPPER == y) || (TK_LOWER == y) || (TK_DIGIT == y) || (TK_PERIOD == y) + || (TK_ATSIGN == y)) + cp1++; + else if ('\"' == *cp1) /* quoted string in pattern */ + { + for (cp1++; ; ) + { + if (SP > *cp1) + break; + if ('\"' == *cp1) + { + cp1++; + if ('\"' == *cp1) /* escaped quotation mark in string */ + cp1++; + else /* character following string */ + break; + } else + cp1++; + } + } else + break; + } + source_col += (cp1 - cp); + cp = cp1; + if ('\0' == *cp) + break; + } + for (shp = ce_def_list; NULL != shp; shp = shp->next) + { + if (0 == memcmp(cp, shp->escape_sentinel, shp->escape_length)) + { + ce_substitute (shp, source_col, &skip_count); + num_subs++; + if (MAX_SUBSTITUTIONS >= num_subs) + { + if (0 < skip_count) /* a substitution occurred */ + possible_sentinel = TRUE; + } else + { + sav_last_src_col = TREF(last_source_column); + TREF(last_source_column) = source_col; + stx_error (ERR_CETOOMANY); + TREF(last_source_column) = sav_last_src_col; + } + break; + } + } + if (!possible_sentinel) + { /* in this column */ + source_col++; + possible_sentinel = TRUE; /* next column may have sentinel */ + } + } + } + lexical_ptr = (char *)source_buffer; + advancewindow(); + advancewindow(); + return; +} diff --git a/sr_port/lb_init.h b/sr_port/lb_init.h new file mode 100644 index 0000000..d7a8164 --- /dev/null +++ b/sr_port/lb_init.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef LB_INIT_INCLUDED +#define LB_INIT_INCLUDED + +void lb_init(void); + +#endif /* LB_INIT_INCLUDED */ diff --git a/sr_port/lcase.mpt b/sr_port/lcase.mpt new file mode 100644 index 0000000..a79a3c2 --- /dev/null +++ b/sr_port/lcase.mpt @@ -0,0 +1,34 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 1989, 2006 Fidelity Information Services, Inc ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +%LCASE ;GT.M %UCASE utility - convert a string to all lower case + ;invoke with string in %S to return lower case string in %S + ;invoke at INT to execute interactively + ;invoke at FUNC as an extrinsic function + ;if you make heavy use of this routine, consider $ZCALL + ; + s %S=$$FUNC(%S) + q +INT n %S + r !,"String: ",%S w !,"Lower: ",$$FUNC(%S),! + q +FUNC(s) + new returnM,returnUTF8,ret,index + i ($zver["VMS")!($ZCHSET="M") do quit ret + . s returnM="set ret=$tr(s,""ABCDEFGHIJKLMNOPQRSTUVWXYZ" + . for index=192:1:207,209:1:221 s returnM=returnM_$char(index) + . s returnM=returnM_"""," + . s returnM=returnM_"""abcdefghijklmnopqrstuvwxyz" + . for index=224:1:239,241:1:253 s returnM=returnM_$char(index) + . s returnM=returnM_""")" + . xecute returnM + s returnUTF8="set ret=$zconvert(s,""L"")" + xecute returnUTF8 + q ret diff --git a/sr_port/lckclr.c b/sr_port/lckclr.c new file mode 100644 index 0000000..d8aa753 --- /dev/null +++ b/sr_port/lckclr.c @@ -0,0 +1,30 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "mlkdef.h" +#include "lckclr.h" + +GBLREF short lks_this_cmd; +GBLREF mlk_pvtblk *mlk_pvt_root; + +void lckclr(void) +{ + short i; + mlk_pvtblk *p1; + + p1 = mlk_pvt_root; + for (i = 0; i < lks_this_cmd; i++) + { + p1->trans = 0; + p1 = p1->next; + } +} diff --git a/sr_port/lckclr.h b/sr_port/lckclr.h new file mode 100644 index 0000000..33928ef --- /dev/null +++ b/sr_port/lckclr.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef LCKCLR_INCLUDED +#define LCKCLR_INCLUDED + +void lckclr(void); + +#endif /* LCKCLR_INCLUDED */ diff --git a/sr_port/lcl_arg1_is_desc_of_arg2.c b/sr_port/lcl_arg1_is_desc_of_arg2.c new file mode 100644 index 0000000..795826e --- /dev/null +++ b/sr_port/lcl_arg1_is_desc_of_arg2.c @@ -0,0 +1,32 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "lv_val.h" + +boolean_t lcl_arg1_is_desc_of_arg2(lv_val *cur, lv_val *ref) +{ + lv_val *lv; + lvTree *lvt; + + if (cur == ref) + return TRUE; + lv = cur; + while (!LV_IS_BASE_VAR(lv)) + { + lvt = LV_GET_PARENT_TREE(lv); + lv = (lv_val *)LVT_PARENT(lvt); + if (lv == ref) + return TRUE; + } + return FALSE; +} diff --git a/sr_port/lclcol.mpt b/sr_port/lclcol.mpt new file mode 100644 index 0000000..ee6cc50 --- /dev/null +++ b/sr_port/lclcol.mpt @@ -0,0 +1,23 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001, 2004 Sanchez Computer Associates, Inc. ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +%lclcol ; ; ; Local Variable Collation Control + ; +get() q $v("YLCT") +getncol() q $v("YLCT","ncol") ; return null collation order + ; +set(lct,ncol) + n ok,$et + s $et="s $ec="""" s ok=0",ok=1 + if ok&$data(lct)&$data(ncol) v "YLCT":lct:ncol q ok + if ok&$data(lct)&'$data(ncol) v "YLCT":lct:-1 q ok + if ok&'$data(lct)&$data(ncol) v "YLCT":-1:ncol q ok + if ok&'$data(lct)&'$data(ncol) v "YLCT":-1:-1 q ok + q ok diff --git a/sr_port/line.c b/sr_port/line.c new file mode 100644 index 0000000..0652c85 --- /dev/null +++ b/sr_port/line.c @@ -0,0 +1,214 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "cmd_qlf.h" +#include "toktyp.h" +#include "opcode.h" +#include "mdq.h" +#include "mmemory.h" +#include "advancewindow.h" + +GBLREF char window_token; +GBLREF mident window_ident; +GBLREF short int source_line; +GBLREF triple *curtchain; +GBLREF int mlmax; +GBLREF mline *mline_tail; +GBLREF short int block_level; +GBLREF mlabel *mlabtab; +GBLREF command_qualifier cmd_qlf; + +error_def(ERR_MULTLAB); +error_def(ERR_LSEXPECTED); +error_def(ERR_COMMAORRPAREXP); +error_def(ERR_MULTFORMPARM); +error_def(ERR_NAMEEXPECTED); +error_def(ERR_BLKTOODEEP); +error_def(ERR_NESTFORMP); + +boolean_t line(uint4 *lnc) +{ + mlabel *x; + triple *first_triple, *parmbase, *parmtail, *r; + int parmcount, varnum; + mline *curlin; + short int dot_count; + boolean_t success; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + first_triple = curtchain->exorder.bl; + parmbase = 0; + dot_count = 0; + success = TRUE; + curlin = (mline *)mcalloc(SIZEOF(*curlin)); + curlin->line_number = 0; + curlin->table = FALSE; + TREF(last_source_column) = 0; + if (TK_INTLIT == window_token) + int_label(); + if ((TK_IDENT == window_token) || (cmd_qlf.qlf & CQ_LINE_ENTRY)) + start_fetches(OC_LINEFETCH); + else + newtriple(OC_LINESTART); + curlin->line_number = *lnc; + *lnc = *lnc + 1; + curlin->table = TRUE; + CHKTCHAIN(curtchain); + TREF(pos_in_chain) = *curtchain; + if (TK_IDENT == window_token) + { + x = get_mladdr(&window_ident); + if (x->ml) + { + stx_error(ERR_MULTLAB); + success = FALSE; + } else + { + assert(NO_FORMALLIST == x->formalcnt); + x->ml = curlin; + advancewindow(); + if (window_token != TK_COLON) + mlmax++; + else + { + x->gbl = FALSE; + advancewindow(); + } + } + if (success && (TK_LPAREN == window_token)) + { + r = maketriple (OC_ISFORMAL); + dqins(curtchain->exorder.bl->exorder.bl, exorder, r); + advancewindow(); + parmbase = parmtail = newtriple(OC_BINDPARM); + for (parmcount = 0 ; window_token != TK_RPAREN ; parmcount++) + { + if (TK_IDENT != window_token) + { + stx_error(ERR_NAMEEXPECTED); + success = FALSE; + break; + } else + { + varnum = get_mvaddr(&window_ident)->mvidx; + for (r = parmbase->operand[1].oprval.tref ; r ; r = r->operand[1].oprval.tref) + { + assert(TRIP_REF == r->operand[0].oprclass); + assert(ILIT_REF == r->operand[0].oprval.tref->operand[0].oprclass); + assert((TRIP_REF == r->operand[1].oprclass) || (0 == r->operand[1].oprclass)); + if (r->operand[0].oprval.tref->operand[0].oprval.ilit == varnum) + { + stx_error(ERR_MULTFORMPARM); + success = FALSE; + break; + } + } + if (!success) + break; + r = newtriple(OC_PARAMETER); + parmtail->operand[1] = put_tref(r); + r->operand[0] = put_ilit(varnum); + parmtail = r; + advancewindow(); + } + if (TK_COMMA == window_token) + advancewindow(); + else if (TK_RPAREN != window_token) + { + stx_error(ERR_COMMAORRPAREXP); + success = FALSE; + break; + } + } + if (success) + { + advancewindow(); + parmbase->operand[0] = put_ilit(parmcount); + x->formalcnt = parmcount; + assert(!mlabtab->lson); + if (mlabtab->rson == x && !TREF(code_generated)) + mlabtab->formalcnt = parmcount; + } + } + } + if (success && (TK_EOL != window_token)) + { + if (TK_SPACE != window_token) + { + stx_error(ERR_LSEXPECTED); + success = FALSE; + } else + { + assert(dot_count == 0); + for (;;) + { + if (TK_SPACE == window_token) + advancewindow(); + else if (TK_PERIOD == window_token) + { + dot_count++; + advancewindow(); + } else + break; + } + } + if (block_level + 1 < dot_count) + { + dot_count = (block_level > 0 ? block_level : 0); + stx_error(ERR_BLKTOODEEP); + success = FALSE; + } + } + if ((0 != parmbase) && (0 != dot_count)) + { + stx_error(ERR_NESTFORMP); /* Should be warning */ + success = FALSE; + dot_count = (block_level > 0 ? block_level : 0); + } + if (dot_count >= block_level + 1) + { + mline_tail->child = curlin; + curlin->parent = mline_tail; + block_level = dot_count; + } else + { + for ( ; dot_count < block_level; block_level--) + mline_tail = mline_tail->parent; + mline_tail->sibling = curlin; + curlin->parent = mline_tail->parent; + } + mline_tail = curlin; + if (success) + { + assert(TREF(for_stack_ptr) == TADR(for_stack)); + *(TREF(for_stack_ptr)) = NULL; + success = linetail(); + if (success) + { + assert(TREF(for_stack_ptr) == TADR(for_stack)); + if (*(TREF(for_stack_ptr))) + tnxtarg(*(TREF(for_stack_ptr))); + } + } + assert(TREF(for_stack_ptr) == TADR(for_stack)); + if (first_triple->exorder.fl == curtchain) + newtriple(OC_NOOP); /* empty line (comment, blank, etc) */ + curlin->externalentry = first_triple->exorder.fl; + /* first_triple points to the last triple before this line was processed. Its forward + link will point to a LINEFETCH or a LINESTART, or possibly a NOOP. It the line was a comment, there is + only a LINESTART, and hence no "real" code yet */ + TREF(code_generated) = TREF(code_generated) | (first_triple->exorder.fl->opcode != OC_NOOP && + first_triple->exorder.fl->exorder.fl != curtchain); + return success; +} diff --git a/sr_port/linetail.c b/sr_port/linetail.c new file mode 100644 index 0000000..41882c0 --- /dev/null +++ b/sr_port/linetail.c @@ -0,0 +1,52 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "toktyp.h" +#include "opcode.h" +#include "advancewindow.h" +#include "cmd.h" + +GBLREF char window_token; +GBLREF triple *curtchain; + +error_def(ERR_SPOREOL); +error_def(ERR_CMD); + +int linetail(void) +{ + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + for (;;) + { + while (TK_SPACE == window_token) + advancewindow(); + if (TK_EOL == window_token) + return TRUE; + if (!cmd()) + { + if (curtchain->exorder.bl->exorder.bl->exorder.bl->opcode != OC_RTERROR) + { /* If rterror is last triple generated (has two args), then error already raised */ + TREF(source_error_found) ? stx_error(TREF(source_error_found)) : stx_error(ERR_CMD); + } + assert(curtchain->exorder.bl->exorder.fl == curtchain); + assert(TREF(source_error_found)); + return FALSE; + } + if ((TK_SPACE != window_token) && (TK_EOL != window_token)) + { + stx_error(ERR_SPOREOL); + return FALSE; + } + } +} diff --git a/sr_port/list_file.h b/sr_port/list_file.h new file mode 100644 index 0000000..580edc7 --- /dev/null +++ b/sr_port/list_file.h @@ -0,0 +1,24 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef LIST_FILE_INCLUDED +#define LIST_FILE_INCLUDED + +void open_list_file(void); +void close_list_file(void); +void list_chkpage(void); +void list_cmd(void); +void list_head(bool newpage); +void list_line(char *c); +void list_line_number(void); +void list_tab(void); + +#endif diff --git a/sr_port/lk_check_own.c b/sr_port/lk_check_own.c new file mode 100644 index 0000000..28d7841 --- /dev/null +++ b/sr_port/lk_check_own.c @@ -0,0 +1,74 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifdef VMS +#include +#endif + +#include "mdef.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "mlkdef.h" +#include "filestruct.h" +#include "lockdefs.h" +#include "lk_check_own.h" +#include "is_proc_alive.h" + +GBLREF short crash_count; + +/* + * ------------------------------------------------ + * Check if owner process of the lock is still alive + * If the process is not alive, clear the lock. + * + * Return: + * TRUE - cleared the owner + * FALSE - otherwise + * ------------------------------------------------ + */ +boolean_t lk_check_own(mlk_pvtblk *x) +{ + int4 status; + int4 icount, time[2]; + boolean_t ret_val, was_crit; + sgmnt_addrs *csa; + + if (!x->blocked) + return FALSE; + csa = &FILE_INFO(x->region)->s_addrs; + if (csa->critical) + crash_count = csa->critical->crashcnt; + was_crit = csa->now_crit; + if (!was_crit) + grab_crit(x->region); /* check on process that owns lock */ + ret_val = FALSE; + if (x->blocked->owner) + { /* There is an owner for the blocking node */ + if (x->blocked->sequence != x->blk_sequence) + { /* The node we were blocking on has been reused for something else so + we are no longer blocked on it and can pretend that the process + holding the lock went away */ + ret_val = TRUE; + } else if (BLOCKING_PROC_ALIVE(x, time, icount, status)) + { /* process that owned lock has died, free lock. */ + x->blocked->owner = 0; + csa->hdr->trans_hist.lock_sequence++; + ret_val = TRUE; + } + } else + ret_val = TRUE; /* There is no owner. Take credit for freeing it.. */ + if (!was_crit) + rel_crit(x->region); + return ret_val; +} diff --git a/sr_port/lk_check_own.h b/sr_port/lk_check_own.h new file mode 100644 index 0000000..ee59ba0 --- /dev/null +++ b/sr_port/lk_check_own.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef LK_CHECK_OWN_INCLUDED +#define LK_CHECK_OWN_INCLUDED + +boolean_t lk_check_own(mlk_pvtblk *x); + +#endif /* LK_CHECK_OWN_INCLUDED */ diff --git a/sr_port/lke.h b/sr_port/lke.h new file mode 100644 index 0000000..8fcd5cf --- /dev/null +++ b/sr_port/lke.h @@ -0,0 +1,35 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __LKE_H__ +#define __LKE_H__ + +#include "error.h" + +bool lke_get_answ(char *prompt); +bool lke_showlock(struct CLB *lnk, mlk_shrblk_ptr_t tree, mstr *name, bool all, bool wait, + bool interactive, int4 pid, mstr one_lock, boolean_t exact); +bool lke_showtree(struct CLB *lnk, mlk_shrblk_ptr_t tree, bool all, bool wait, pid_t pid, + mstr one_lock, bool memory); +void lke_exit(void); +void lke_clear(void); +void lke_help(void); +void lke_show(void); +void lke_show_memory(mlk_shrblk_ptr_t bhead, char *prefix); + +#ifdef VMS +void lke(void); +#else +CONDITION_HANDLER(lke_ctrlc_handler); +#endif +void lke_setgdr(void); + +#endif /* __LKE_H__ */ diff --git a/sr_port/lke.hlp b/sr_port/lke.hlp new file mode 100644 index 0000000..495a584 --- /dev/null +++ b/sr_port/lke.hlp @@ -0,0 +1,241 @@ + +1 Overview + The MUMPS LOCK Utility + The GT.M LOCK Utility, LKE, provides a tool for examining and changing + the GT.M LOCK environment. In MUMPS, the LOCK command reserves one or + more resource names. Only one process at a time can reserve a resource + name. No other process sharing the same environment can successfully + LOCK that resource name at the same time. MUMPS code commonly uses + LOCKs as flags controlling access to global data. Generally a LOCK + specifies the same as the name of the global variable that requires + protected access. However, this is only a convention. A LOCK argument + may contain any subscripted or unsubscripted MUMPS name including a + name with no preceding caret (^). Because they have the appearance of + local variable names, resource names with no preceding caret (^) are + commonly referred to as "local LOCKs." + + The ZALLOCATE and ZDEALLOCATE commands provide an alternative, + non-standard, mechanism for managing LOCKs. + +2 Functions + Functions + The two primary functions of the MUMPS LOCK Utility (LKE) are: + + o SHOW all or specified LOCKs currently active on the system + + o CLEAR all or specified LOCKs currently active on the system + + When debugging a MUMPS application, you may use LKE to identify and + clear a possible deadlock situation, i.e., two or more processes + have LOCKs and are waiting to add resource names LOCKed by the + other(s). + + When used with GT.CM, LKE may display and change information on + other nodes of a distributed database system. + +2 LOCK_database + MUMPS LOCKs and Global Directories + GT.M distributes the LOCK database among the database files + identified by the Global Directory (GD). The Global Directory + Editor (GDE) creates and maintains Global Directories. + + GT.M maps LOCKs of resource names starting with a caret (^) to the + database file used to map variables with the same name. If the + Global Directory maps the name A to file A.DAT, GT.M maps all LOCKs + on resource name ^A to file A.DAT. + + GT.M maps LOCKs on names not starting with a caret (^) to the + region of the database specified with the GDE command LOCK -REGION. + By default, GDE creates Global Directories mapping local LOCKs to + the region DEFAULT. + + These two factors result in the following: + + o ^ LOCKs automatically intersect for all users of the same data + in any database file, because GT.M stores the ^ LOCKs in the + same file as the data + + o "local" LOCKs intersect dependent on the Global Directory, + because users may access the database through different Global + Directories. + +2 Global_Directories + Establishing a Global Directory + GDE and LKE use the environment variable gtmgbldir to identify + which file to use for the Global Directory. gtmgbldir should be + defined by individual users in their login files. + + Example + + $ gtmgbldir=prod.gld + $ export gtmgbldir + + When a process invokes a GT.M image, GT.M identifies the current + Global Directory by the environment variable gtmgbldir. Within + MUMPS, SET $zgbldir=expr changes the Global Directory. $zgbldir is + an intrinsic special variable. An individual LOCK, ZALLOCATE or + ZDEALLOCATE argument may specify a Global Directory with the + extended global syntax. + +1 CLEAR + C[LEAR] + The CLEAR command removes active LOCKs. The format of the CLEAR + command is: + + C[LEAR] [-qualifier...] + + The optional CLEAR command qualifiers are: + + -A[LL] + -I[NTERACTIVE] + -O[UTPUT]=file-spec + -P[ID]=pid + -R[EGION]=region-name + + By default, CLEAR operates interactively (-INTERACTIVE). + +2 Qualifiers + +-ALL + -A[LL] + Specifies the removal of all current LOCKs. If used with the + -REGION qualifier -ALL removes all LOCKs in the region. Issue a + CLEAR -ALL only when there are no active GT.M processes using + LOCKs or when you can predict the effect on the application. + + The -ALL qualifier is incompatible with the -INTERACTIVE + qualifier. + +-INTERACTIVE + -I[NTERACTIVE] + Clears one LOCK at a time interactively. LKE displays each + current LOCK with the PID of the owner process and prompts for + verification that the LOCK should be cleared. LKE retains the + LOCK for any response other than Y[ES]. + + The -INTERACTIVE qualifier is incompatible with the -ALL + qualifier. + + By default, CLEAR operates interactively (-INTERACTIVE). + +-OUTPUT + -OUTPUT=file-spec + Directs the reporting of all cleared LOCKs. If you specify an + existing file, LKE overwrites that file. + + The -OUTPUT qualifier is compatible with all other qualifiers. + + By default, CLEAR sends its messages to the standard output. + +-PID + -/P[ID]=pid + Clears all LOCKs associated with the specified process + identification number. LKE interprets the PID as a decimal + number. This command provides a means for directing CLEAR to + LOCKs held by a process that is behaving abnormally. + + The -PID qualifier is compatible with all other qualifiers. + +-REGION + -/R[EGION]=region-name + Clears LOCKs mapped by the current Global Directory to a region + specified by the region-name. + + The -REGION qualifier is compatible with all other qualifiers. + + By default, CLEAR -REGION= operates interactively (-INTERACTIVE). + +1 EXIT + E[XIT] + The EXIT command ends an LKE session. The format of the EXIT command + is: + + E[XIT] + +1 HELP + H[ELP] + The HELP command explains LKE commands. The format of the HELP command + is: + + H[ELP] [options...] + + Enter the LKE command for which you want information at the Topic + prompt(s). Use or to return to the LKE prompt. + + Example + + LKE> HELP SHOW + + This command displays help for the SHOW command. + + +1 SHOW + SH[OW] + The SHOW command provides a status report on the LOCK mechanism and + the LOCK database. The format of the SHOW command is: + + SH[OW] [ -qualifier...] + + By default, SHOW displays -ALL. + + The SHOW command reports active LOCKs. Information displayed about + specific LOCKs includes the LOCK resource name and the process + identification (PID) of the LOCK owner. The results of a SHOW may be + immediately "outdated" by MUMPS LOCK activity. + +2 Qualifiers +-ALL + -A[LL] + Specifies a display of all current LOCKs in all regions and + information about the state of processes owning these LOCKs. The + -ALL qualifier is compatible with all other qualifiers. SHOW + -ALL -WAIT displays both -ALL and -WAIT information. + + By default, SHOW displays -ALL. + +-OUTPUT + -OUTPUT=file-spec + Directs the reporting of the current LOCKs. When you specify a + file, LKE overwrites that file. + + The -OUTPUT qualifier is compatible with all other qualifiers. + + By default, SHOW directs all messages to SYS$OUTPUT. + +-PID + -P[ID]=process-identification + Displays all LOCKs owned by the specified PID. + + The -PID qualifier is compatible with all other qualifiers. + + By default, SHOW displays the LOCKs for all PIDs. + +-REGION + -R[EGION]=region-name + Displays LOCKs for the specified region. + + The -REGION qualifier is compatible with all other qualifiers. + + By default, SHOW displays the LOCKs for all regions. + +-WAIT + -W[AIT] + Displays the LOCK resource name and the process state + information of all processes waiting for the LOCK to be granted. + LKE does not display the owner of the LOCK. SHOW -ALL -WAIT + displays both -ALL and -WAIT information. + +1 SPAWN + SP[AWN] + The SPAWN command creates a sub-process for access to the shell + without terminating the current LKE environment. Use the SPAWN command + to suspend a session and issue shell commands such as ls or printenv. + The SPAWN command accepts an optional command string for execution by + the spawned sub-process. If the SPAWN has command string parameter, + the created sub-process prompts and accepts any legal shell command. + To terminate the sub-process use the shell logout command. + + The format of the SPAWN command is: + + SP[AWN] + diff --git a/sr_port/lke_clear.c b/sr_port/lke_clear.c new file mode 100644 index 0000000..3161996 --- /dev/null +++ b/sr_port/lke_clear.c @@ -0,0 +1,136 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* + * ------------------------------------------------- + * lke_clear.c : removes locks for qualified regions + * used in : lke.c + * ------------------------------------------------- + */ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "mlkdef.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "cmidef.h" /* for cmmdef.h */ +#include "hashtab_mname.h" /* needed for cmmdef.h */ +#include "cmmdef.h" /* for gtcmtr_protos.h */ +#include "util.h" +#include "gtcmtr_protos.h" +#include "lke.h" +#include "lke_getcli.h" +#include "lke_cleartree.h" +#include "gtmmsg.h" + +#define NOFLUSH 0 +#define FLUSH 1 +#define RESET 2 + +GBLREF gd_region *gv_cur_region; +GBLREF gd_addr *gd_header; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF short crash_count; + +error_def(ERR_NOREGION); + + +void lke_clear(void) +{ + bool locks, all = TRUE, wait = FALSE, interactive = TRUE, match = FALSE, memory = FALSE, nocrit = FALSE; + boolean_t exact = TRUE, was_crit; + int4 pid; + int n; + char regbuf[MAX_RN_LEN], nodebuf[32], one_lockbuf[MAX_KEY_SZ]; + mlk_ctldata_ptr_t ctl; + mstr reg, node, one_lock; + + error_def(ERR_UNIMPLOP); + error_def(ERR_TEXT); + + /* Get all command parameters */ + reg.addr = regbuf; + reg.len = SIZEOF(regbuf); + node.addr = nodebuf; + node.len = SIZEOF(nodebuf); + one_lock.addr = one_lockbuf; + one_lock.len = SIZEOF(one_lockbuf); + + if (lke_getcli(&all, &wait, &interactive, &pid, ®, &node, &one_lock, &memory, &nocrit, &exact) == 0) + return; + + /* Search all regions specified on the command line */ + for (gv_cur_region = gd_header->regions, n = 0; + n != gd_header->n_regions; + ++gv_cur_region, ++n) + { /* If region matches and is open */ + if ((reg.len == 0 || + gv_cur_region->rname_len == reg.len && memcmp(gv_cur_region->rname, reg.addr, reg.len) == 0) && + gv_cur_region->open) + { + match = TRUE; + util_out_print("!/!AD!/", NOFLUSH, REG_LEN_STR(gv_cur_region)); + /* If distributed database, the region is located on another node */ + if (gv_cur_region->dyn.addr->acc_meth == dba_cm) + { +# if defined(LKE_WORKS_OK_WITH_CM) + /* Remote lock clears are not supported, so LKE CLEAR -EXACT qualifier + * will not be supported on GT.CM.*/ + locks = gtcmtr_lke_clearreq(gv_cur_region->dyn.addr->cm_blk, gv_cur_region->cmx_regnum, + all, interactive, pid, &node); +# else + gtm_putmsg(VARLSTCNT(10) ERR_UNIMPLOP, 0, ERR_TEXT, 2, + LEN_AND_LIT("GT.CM region - locks must be cleared on the local node"), + ERR_TEXT, 2, REG_LEN_STR(gv_cur_region)); + continue; +# endif + } else if ((dba_bg == gv_cur_region->dyn.addr->acc_meth) || (dba_mm == gv_cur_region->dyn.addr->acc_meth)) + { /* Local region */ + cs_addrs = &FILE_INFO(gv_cur_region)->s_addrs; + ctl = (mlk_ctldata_ptr_t)cs_addrs->lock_addrs[0]; + /* Prevent any modifications of locks while we are clearing */ + if (cs_addrs->critical != NULL) + crash_count = cs_addrs->critical->crashcnt; + was_crit = cs_addrs->now_crit; + if (!was_crit) + grab_crit(gv_cur_region); + locks = ctl->blkroot == 0 ? FALSE + : lke_cleartree(gv_cur_region, NULL, ctl, + (mlk_shrblk_ptr_t)R2A(ctl->blkroot), + all, interactive, pid, one_lock, exact); + if (!was_crit) + rel_crit(gv_cur_region); + } else + { + util_out_print(NULL, RESET); + util_out_print("Region is not BG, MM, or CM", FLUSH); + locks = TRUE; + } + + if (!locks) + { + util_out_print(NULL, RESET); + util_out_print("No locks were found in !AD", FLUSH, REG_LEN_STR(gv_cur_region)); + } + } + } + + if (!match && reg.len != 0) + rts_error(VARLSTCNT(4) ERR_NOREGION, 2, reg.len, reg.addr); + +} diff --git a/sr_port/lke_clearlock.c b/sr_port/lke_clearlock.c new file mode 100644 index 0000000..9585499 --- /dev/null +++ b/sr_port/lke_clearlock.c @@ -0,0 +1,116 @@ +/**************************************************************** + * * + * Copyright 2001, 2004 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* + * ----------------------------------------------- + * lke_clearlock : removes the qualified lock node + * used in : lke_cleartree.c + * ----------------------------------------------- + */ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "mlkdef.h" +#include "cmidef.h" +#include "hashtab_mname.h" /* needed for cmmdef.h */ +#include "cmmdef.h" +#include "util.h" +#include "lke.h" +#include "cmi.h" +#include "gvcmz.h" +#include "lke_clearlock.h" + +#define FLUSH 1 + +error_def(ERR_LCKGONE); + + +bool lke_clearlock( + gd_region *region, + struct CLB *lnk, + mlk_ctldata_ptr_t ctl, + mlk_shrblk_ptr_t node, + mstr *name, + bool all, + bool interactive, + int4 pid) +{ + clear_confirm confirm; + clear_reply reply; + uint4 status; + int len; + bool unlock = FALSE; + sgmnt_addrs *csa; + + if (node->owner != 0 && (pid == node->owner || pid == 0)) + { + if (interactive) + if (lnk == NULL) + unlock = lke_get_answ("Clear lock ? "); + else + { + lnk->mbl = sizeof confirm; + lnk->mbf = (unsigned char *)&confirm; + lnk->ast = NULL; + status = cmi_read(lnk); + if ((status & 1) == 0) + { + ((link_info *)(lnk->usr))->neterr = TRUE; + gvcmz_error(CMMS_U_LKEDELETE, status); + return FALSE; + } + unlock = confirm.clear; + } + else + unlock = TRUE; + + if (unlock) + { + csa = &FILE_INFO(region)->s_addrs; + node->owner = 0; + node->sequence = csa->hdr->trans_hist.lock_sequence++; + len = name->len - 1; + if (name->addr[len] != '(') + ++len; + if (lnk == NULL) + util_out_print("Lock removed : !AD", FLUSH, len, name->addr); + else + if (!interactive) + { + reply.code = CMMS_V_LKESHOW; + reply.status = ERR_LCKGONE; + reply.locknamelength = len; + memcpy(reply.lockname, name->addr, len); + lnk->cbl = sizeof reply - (sizeof reply.lockname - len); + lnk->mbf = (unsigned char *)&reply; + lnk->ast = NULL; + status = cmi_write(lnk); + if ((status & 1) == 0) + { + ((link_info *)(lnk->usr))->neterr = TRUE; + gvcmz_error(CMMS_V_LKESHOW, status); + return FALSE; + } + } + } + } + + return unlock; +} diff --git a/sr_port/lke_clearlock.h b/sr_port/lke_clearlock.h new file mode 100644 index 0000000..08a8410 --- /dev/null +++ b/sr_port/lke_clearlock.h @@ -0,0 +1,18 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __LKE_CLEARLOCK_H__ +#define __LKE_CLEARLOCK_H__ + +bool lke_clearlock(gd_region *region, struct CLB *lnk, mlk_ctldata_ptr_t ctl, + mlk_shrblk_ptr_t node, mstr *name, bool all, bool interactive, int4 pid); + +#endif diff --git a/sr_port/lke_cleartree.c b/sr_port/lke_cleartree.c new file mode 100644 index 0000000..96f8d71 --- /dev/null +++ b/sr_port/lke_cleartree.c @@ -0,0 +1,118 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* + * ---------------------------------- + * lke_cleartree : clears a lock tree + * used in : lke_clear.c + * ---------------------------------- + */ + +#include "mdef.h" + +#include + +#include "mlkdef.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "mlk_shrblk_delete_if_empty.h" +#include "mlk_wake_pending.h" +#include "lke.h" +#include "lke_cleartree.h" +#include "lke_clearlock.h" + +#define KDIM 64 /* max number of subscripts */ + +GBLREF VSIG_ATOMIC_T util_interrupt; + +bool lke_cleartree( + gd_region *region, + struct CLB *lnk, + mlk_ctldata_ptr_t ctl, + mlk_shrblk_ptr_t tree, + bool all, + bool interactive, + int4 pid, + mstr one_lock, + boolean_t exact) + +{ + mlk_shrblk_ptr_t node, oldnode, start[KDIM]; + unsigned char subscript_offset[KDIM]; + static char name_buffer[MAX_ZWR_KEY_SZ + 1]; + static MSTR_DEF(name, 0, name_buffer); + int depth = 0; + bool locks = FALSE, locked, deleted; + + error_def(ERR_CTRLC); + + node = start[0] + = tree; + subscript_offset[0] = 0; + + for (;;) + { + name.len = subscript_offset[depth]; + + /* Display the lock node */ + locked = lke_showlock(lnk, node, &name, all, FALSE, interactive, pid, one_lock, exact); + locks |= locked; + + /* If it was locked, clear it and wake up any processes waiting for it */ + if (locked && lke_clearlock(region, lnk, ctl, node, &name, all, interactive, pid) && node->pending != 0) + mlk_wake_pending(ctl, node, region); + + /* if a specific lock was requested (-EXACT and -LOCK=), then we are done */ + if (exact && (0 != one_lock.len) && locked) + return locks; + /* Move to the next node */ + if (node->children == 0) + { + /* This node has no children, so move to the right */ + oldnode = node; + node = (mlk_shrblk_ptr_t)R2A(node->rsib); + while (node == start[depth]) + { + /* There are no more siblings to the right at this depth, + so move up and then right */ + if (node->parent == 0) + { + /* We're already at the top, so we're done */ + assert(depth == 0); + (void)mlk_shrblk_delete_if_empty(ctl, node); + return locks; + } + --depth; + node = (mlk_shrblk_ptr_t)R2A(node->parent); + (void)mlk_shrblk_delete_if_empty(ctl, oldnode); + oldnode = node; + node = (mlk_shrblk_ptr_t)R2A(node->rsib); + } + deleted = mlk_shrblk_delete_if_empty(ctl, oldnode); + if (deleted && start[depth] == oldnode) + start[depth] = node; + } + else + { + /* This node has children, so move down */ + ++depth; + node = start[depth] + = (mlk_shrblk_ptr_t)R2A(node->children); + subscript_offset[depth] = name.len; + } + if (util_interrupt) + rts_error(VARLSTCNT(1) ERR_CTRLC); + } +} diff --git a/sr_port/lke_cleartree.h b/sr_port/lke_cleartree.h new file mode 100644 index 0000000..6044a47 --- /dev/null +++ b/sr_port/lke_cleartree.h @@ -0,0 +1,18 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __LKE_CLEARTREE_H__ +#define __LKE_CLEARTREE_H__ + +bool lke_cleartree(gd_region *region, struct CLB *lnk, mlk_ctldata_ptr_t ctl, + mlk_shrblk_ptr_t tree, bool all, bool interactive, int4 pid, mstr one_lock, boolean_t exact); + +#endif diff --git a/sr_port/lke_exit.c b/sr_port/lke_exit.c new file mode 100644 index 0000000..2bb64f7 --- /dev/null +++ b/sr_port/lke_exit.c @@ -0,0 +1,29 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdlib.h" /* for exit() */ + +#include "error.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "iosp.h" +#include "mlkdef.h" +#include "lke.h" + +void lke_exit(void) +{ + EXIT(SS_NORMAL); +} diff --git a/sr_port/lke_getcli.c b/sr_port/lke_getcli.c new file mode 100644 index 0000000..2d173b0 --- /dev/null +++ b/sr_port/lke_getcli.c @@ -0,0 +1,130 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* + lke_getcli.c : obtains CLI qualifiers, validates their values + used in : lke_clear.c, lke_show.c +*/ + +#include "mdef.h" +#include "gtm_string.h" +#include "cli.h" +#include "lke_getcli.h" +#include "util.h" + +/* + * ------------------------------------------------------ + * Get command line parameters + * ------------------------------------------------------ + */ +int4 lke_getcli(bool *all, + bool *wait, + bool *inta, + int4 *pid, + mstr *region, + mstr *node, + mstr *one_lock, + bool *memory, + bool *nocrit, + boolean_t *exact) +{ + int4 status; + unsigned short len; + int keylen; + char one_lockbuf[MAX_ZWR_KEY_SZ + 1]; + + status = TRUE; +/* + * ----------------------------------------------------------- + * -INTERACTIVE overrides any defaults + -NOINTERACTIVE overrides any defaults + + * otherwise: default is nointeractive when -ALL is set + default is original value of inta without -ALL + * ----------------------------------------------------------- + */ + + + *all = (*all && cli_present("ALL") == CLI_PRESENT); + + *inta = *inta && (cli_present("INTERACTIVE") != CLI_NEGATED); + + *wait = (*wait && cli_present("WAIT") == CLI_PRESENT); + + *memory = (*memory && cli_present("MEMORY") == CLI_PRESENT); + *nocrit = (*nocrit && cli_present("CRIT") == CLI_NEGATED); + *exact = (*exact && cli_present("EXACT") == CLI_PRESENT); + + if (cli_present("PID") == CLI_PRESENT) + { +#ifdef HEXPID + if (!cli_get_hex("PID", pid)) +#else + assert(SIZEOF(*pid) == SIZEOF(int)); + if (!cli_get_int("PID", (int4 *)pid)) +#endif + { + *pid = 0; + status = FALSE; + } + } + else + *pid = 0 ; + + if (cli_present("REGION") == CLI_PRESENT) + { + len = region->len; + if (!cli_get_str("REGION", region->addr, &len)) + { util_out_print("Error getting REGION parameter",TRUE); + region->len = 0; + status = FALSE; + } else + region->len = len; + } + else + region->len = 0; + + if (cli_present("LOCK") == CLI_PRESENT) + { + len = one_lock->len; + if (!cli_get_str("LOCK", one_lock->addr, &len) || -1 == (keylen = lke_getki(one_lock->addr, len, one_lockbuf))) + { + util_out_print("Error getting LOCK parameter",TRUE); + one_lock->len = 0; + status = FALSE; + } else + { + one_lock->len = keylen; + memcpy(one_lock->addr, one_lockbuf, keylen); + } + } + else + { + one_lock->len = 0; + one_lock->addr = 0; + } + + if (cli_present("NODE") == CLI_PRESENT) + { + len = node->len; + if (!cli_get_str("NODE", node->addr, &len)) + { util_out_print("Error getting NODE parameter",TRUE); + node->len = 0; + status = FALSE; + } else + node->len = len; + } + else + node->len = 0; + + return status ; +} + diff --git a/sr_port/lke_getcli.h b/sr_port/lke_getcli.h new file mode 100644 index 0000000..9f13c60 --- /dev/null +++ b/sr_port/lke_getcli.h @@ -0,0 +1,23 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef LKE_GETCLI_H +#define LKE_GETCLI_H + +int4 lke_getcli(bool *all, bool *wait, bool *inta, int4 *pid, mstr *region, mstr *node, + mstr *one_lock, bool *memory, bool *nocrit, boolean_t *exact); +int lke_getki(char* src, int srclen, char* outptr); + +#ifdef VMS +#define HEXPID +#endif + +#endif diff --git a/sr_port/lke_getki.c b/sr_port/lke_getki.c new file mode 100644 index 0000000..5ef7a2a --- /dev/null +++ b/sr_port/lke_getki.c @@ -0,0 +1,111 @@ +/**************************************************************** + * * + * Copyright 2006, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "lke_getcli.h" + +/* This routine performs the necessary transformation of the LOCK keys passed in + * from the CLI layer and produces a canonical formatted key. This routine + * + * validates if the key is indeed in the correct syntax. + * + * adds quotes to the string subscripts (which were removed by the CLI + * layer on UNIX) + * + * removes the redundant quotes in the key (which were passed intact by + * CLI layer on VMS). + * + */ +int lke_getki(char* src, int srclen, char* outbuff) +{ + char *inptr, *nextptr, *intop, *outptr, *tmpptr; + mval subsc = DEFINE_MVAL_STRING(MV_STR, 0, 0, 0, NULL, 0, 0); + char one_lockbuf[MAX_ZWR_KEY_SZ + 1]; + + if (srclen > 1 && '"' == src[0] && '"' == src[srclen - 1]) + { + outptr = one_lockbuf; + for (inptr = ++src, intop = src + srclen - 2; inptr < intop; *outptr++ = *inptr++) + { + if ('"' == *inptr && (++inptr >= intop || *inptr != '"')) + return -1; /* invalid (unescaped) quote within a quoted key */ + } + src = one_lockbuf; + srclen = (int)(outptr - one_lockbuf); + } + inptr = memchr(src, '(', srclen); + if (NULL == inptr) + { + memcpy(outbuff, src, srclen); + return srclen; + } + ++inptr; + outptr = outbuff; + memcpy(outptr, src, inptr - src); + outptr += inptr - src; + for (intop = src + srclen; inptr < intop; inptr = nextptr) + { + if ('$' == *inptr) + { /* the entire subscript is within $C() or $ZCH() */ + *outptr++ = *inptr++; + nextptr = memchr(inptr, ')', intop - inptr); + if (NULL == nextptr) + return -1; + ++nextptr; + memcpy(outptr, inptr, nextptr - inptr); + outptr += nextptr - inptr; + } else + { /* unquoted string or a number */ + nextptr = memchr(inptr, ',', intop - inptr); + if (NULL == nextptr) + { + nextptr = intop - 1;; + if (')' != *nextptr) + return -1; + } + subsc.str.len = INTCAST(nextptr - inptr); + subsc.str.addr = inptr; + if (val_iscan(&subsc)) + { + memcpy(outptr, subsc.str.addr, subsc.str.len); + outptr += subsc.str.len; + } else + { + if (nextptr - 1 > inptr && '"' == *inptr && '"' == *(nextptr - 1)) + { /* The string is already enclosed by a pair of quotes */ + *outptr++ = *inptr++; /* initial quote */ + for (; inptr < nextptr - 1; *outptr++ = *inptr++) + { + if ('"' == *inptr && (++inptr >= nextptr - 1 || *inptr != '"')) + return -1; /* invalid (unescaped) quote within a quoted string */ + } + *outptr++ = *inptr++; /* final quote */ + } else + { /* unquoted string: add quotes */ + *outptr++ = '"'; + for (tmpptr = inptr; tmpptr < nextptr; ++tmpptr) + { + *outptr++ = *tmpptr; + if ('"' == *tmpptr) + *outptr++ = '"'; + } + *outptr++ = '"'; + } + } + } + if (',' != *nextptr && ')' != *nextptr) + return -1; + *outptr++ = *nextptr++; + } + return (int)(outptr - outbuff); +} diff --git a/sr_port/lke_show.c b/sr_port/lke_show.c new file mode 100644 index 0000000..58b2831 --- /dev/null +++ b/sr_port/lke_show.c @@ -0,0 +1,137 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* + * ------------------------------------------------- + * lke_show.c : displays locks for qualified regions + * used in : lke.c + * ------------------------------------------------- + */ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "mlkdef.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "filestruct.h" +#include "cmidef.h" /* for cmmdef.h */ +#include "hashtab_mname.h" /* needed for cmmdef.h */ +#include "cmmdef.h" /* for gtcmtr_protos.h */ +#include "util.h" +#include "longcpy.h" +#include "gtcmtr_protos.h" +#include "lke.h" +#include "lke_getcli.h" +#include "gtmmsg.h" + +#define NOFLUSH 0 +#define FLUSH 1 +#define RESET 2 + +GBLREF gd_region *gv_cur_region; +GBLREF gd_addr *gd_header; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF short crash_count; + +error_def(ERR_NOREGION); + +void lke_show(void) +{ + bool locks, all = TRUE, wait = TRUE, interactive = FALSE, match = FALSE, memory = TRUE, nocrit = TRUE; + boolean_t exact = FALSE, was_crit; + int4 pid; + size_t ls_len; + int n; + char regbuf[MAX_RN_LEN], nodebuf[32], one_lockbuf[MAX_KEY_SZ]; + mlk_ctldata_ptr_t ctl; + mstr reg, node, one_lock; + + error_def(ERR_UNIMPLOP); + error_def(ERR_TEXT); + + /* Get all command parameters */ + reg.addr = regbuf; + reg.len = SIZEOF(regbuf); + node.addr = nodebuf; + node.len = SIZEOF(nodebuf); + one_lock.addr = one_lockbuf; + one_lock.len = SIZEOF(one_lockbuf); + + if (lke_getcli(&all, &wait, &interactive, &pid, ®, &node, &one_lock, &memory, &nocrit, &exact) == 0) + return; + + /* Search all regions specified on the command line */ + for (gv_cur_region = gd_header->regions, n = 0; n != gd_header->n_regions; ++gv_cur_region, ++n) + { + /* If region matches and is open */ + if ((reg.len == 0 || + gv_cur_region->rname_len == reg.len && memcmp(gv_cur_region->rname, reg.addr, reg.len) == 0) && + gv_cur_region->open) + { + match = TRUE; + util_out_print("!/!AD!/", NOFLUSH, REG_LEN_STR(gv_cur_region)); + + /* If distributed database, the region is located on another node */ + if (gv_cur_region->dyn.addr->acc_meth == dba_cm) + { +# if defined(LKE_WORKS_OK_WITH_CM) + /* Obtain lock info from the remote node */ + locks = gtcmtr_lke_showreq(gv_cur_region->dyn.addr->cm_blk, gv_cur_region->cmx_regnum, + all, wait, pid, &node); +# else + gtm_putmsg(VARLSTCNT(10) ERR_UNIMPLOP, 0, ERR_TEXT, 2, + LEN_AND_LIT("GT.CM region - locks must be displayed on the local node"), + ERR_TEXT, 2, REG_LEN_STR(gv_cur_region)); + continue; +# endif + } else if (gv_cur_region->dyn.addr->acc_meth == dba_bg || gv_cur_region->dyn.addr->acc_meth == dba_mm) + { /* Local region */ + cs_addrs = &FILE_INFO(gv_cur_region)->s_addrs; + ls_len = (size_t)(cs_addrs->lock_addrs[1] - cs_addrs->lock_addrs[0]); + ctl = (mlk_ctldata_ptr_t)malloc(ls_len); + /* Prevent any modification of the lock space while we make a local copy of it */ + if (cs_addrs->critical != NULL) + crash_count = cs_addrs->critical->crashcnt; + was_crit = cs_addrs->now_crit; + if (!nocrit && !was_crit) + grab_crit(gv_cur_region); + longcpy((uchar_ptr_t)ctl, (uchar_ptr_t)cs_addrs->lock_addrs[0], ls_len); + if (!nocrit && !was_crit) + rel_crit(gv_cur_region); + locks = ctl->blkroot == 0 ? + FALSE: + lke_showtree(NULL, (mlk_shrblk_ptr_t)R2A(ctl->blkroot), all, wait, pid, + one_lock, memory); + free(ctl); + } else + { + util_out_print(NULL, RESET); + util_out_print("Region is not BG, MM, or CM", FLUSH); + locks = TRUE; + } + if (!locks) + { + util_out_print(NULL, RESET); + util_out_print("No locks were found in !AD", FLUSH, REG_LEN_STR(gv_cur_region)); + } + } + } + + if (!match && reg.len != 0) + rts_error(VARLSTCNT(4) ERR_NOREGION, 2, reg.len, reg.addr); + +} diff --git a/sr_port/lke_showlock.c b/sr_port/lke_showlock.c new file mode 100644 index 0000000..dfa8acb --- /dev/null +++ b/sr_port/lke_showlock.c @@ -0,0 +1,241 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* + * ---------------------------------------------------------------- + * lke_showlock : displays the lock data for a given lock tree node + * used in : lke_showtree.c + * ---------------------------------------------------------------- + */ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gtm_stdio.h" /* for SPRINTF */ + +#ifdef VMS +#include +#include +#include +#include +#endif + +#include "mlkdef.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "cmidef.h" +#include "hashtab_mname.h" /* needed for cmmdef.h */ +#include "cmmdef.h" +#include "util.h" +#include "lke.h" +#include "is_proc_alive.h" +#include "real_len.h" /* for real_len() prototype */ +#include "zshow.h" + +#define LNAM 24 +#define NDIM 32 /* max node name size */ +#define CLNTNODE_LIT " : CLNTNODE = " +#define CLNTPID_LIT " : CLNTPID = " + +#if defined(UNIX) +# define PID_FMT_STR "!UL" +# define PIDPRINT_LIT "%d" +# define GNAM_FMT_STR "!AD " +#elif defined(VMS) +# define PID_FMT_STR "!XL" +# define PIDPRINT_LIT "%08X" +# define GNAM_FMT_STR "!24 " +#endif + +GBLREF int4 process_id; + +static char gnam[] = GNAM_FMT_STR, + gnaml[] = "!AD!/!24* ", + ownedby[] = "Owned by PID= " PID_FMT_STR " which is !AD!AD", + request[] = "Request PID= " PID_FMT_STR " which is !AD!AD", + nonexpr[] = "a nonexistent process", + existpr[] = "an existing process", + nonexam[] = "an inexaminable process", + nopriv[] = "no privilege"; + +bool lke_showlock( + struct CLB *lnk, + mlk_shrblk_ptr_t tree, + mstr *name, + bool all, + bool wait, + bool interactive, + int4 pid, + mstr one_lock, + boolean_t exact) +{ + mlk_prcblk pblk; + mlk_prcblk_ptr_t r; + mlk_shrsub_ptr_t value; + short len1; + int len2; + boolean_t lock = FALSE, owned; + UINTPTR_T f[7]; + int4 gtcmbufidx, item, ret; + uint4 status; + char *msg, save_ch, format[64], gtcmbuf[64]; /* gtcmbuf[] is to hold ": CLNTNODE = %s : CLNTPID = %s" */ + static mval subsc = DEFINE_MVAL_STRING(MV_STR, 0, 0, 0, NULL, 0, 0); + VMS_ONLY( + char sysinfo[NDIM]; + $DESCRIPTOR (sysinfo_dsc, sysinfo); + ) + + value = (mlk_shrsub_ptr_t)R2A(tree->value); + if (0 == name->len) + { /* unsubscripted lock name can never have control characters, so no ZWR translation needed */ + memcpy(name->addr, value->data, value->length); + f[0] = name->len = value->length; + name->addr[name->len++] = '('; + } else + { /* perform ZWR translation on the subscript */ + len1 = name->len - 1; + if (')' == name->addr[len1]) + name->addr[len1] = ','; + subsc.str.len = value->length; + subsc.str.addr = (char *)value->data; + if (val_iscan(&subsc)) + { /* avoid printing enclosed quotes for canonical numbers */ + save_ch = name->addr[len1]; + format2zwr((sm_uc_ptr_t)value->data, value->length, (unsigned char*)&name->addr[len1], &len2); + assert(name->addr[len1 + len2 - 1] == '"'); + name->addr[len1] = save_ch; + len2 -= 2; /* exclude the enclosing quotes */ + } else + format2zwr((sm_uc_ptr_t)value->data, value->length, (unsigned char*)&name->addr[name->len], &len2); + name->len += len2; + name->addr[name->len++] = ')'; + f[0] = name->len; + } + f[1] = (UINTPTR_T)name->addr; + if (tree->owner || (tree->pending && wait)) + { + pblk.process_id = tree->owner; + pblk.next = (wait && tree->pending) ? + (ptroff_t)((uchar_ptr_t)&tree->pending - (uchar_ptr_t)&pblk.next + tree->pending) + : 0; + owned = (all || !wait) && tree->owner; + r = owned ? &pblk + : ((0 == tree->pending) ? NULL + : (mlk_prcblk_ptr_t)R2A(tree->pending)); + while (NULL != r) + { + if ((0 == pid) || (pid == r->process_id)) + { + f[2] = r->process_id; + VMS_ONLY( + item = JPI$_STATE; + status = lib$getjpi(&item, &r->process_id, 0, &ret, &sysinfo_dsc, &len1); + switch (status) + { + case SS$_NORMAL: + f[3] = len1; + f[4] = sysinfo; + break; + case SS$_NOPRIV: + f[3] = STR_LIT_LEN(nopriv); + f[4] = nopriv; + break; + case SS$_NONEXPR: + f[3] = STR_LIT_LEN(nonexpr); + f[4] = nonexpr; + break; + default: + f[3] = STR_LIT_LEN(nonexam); + f[4] = nonexam; + break; + } + ) + UNIX_ONLY( + if (is_proc_alive((int4)r->process_id, 0)) + { + f[3] = STR_LIT_LEN(existpr); + f[4] = (INTPTR_T)existpr; + } else + { + f[3] = STR_LIT_LEN(nonexpr); + f[4] = (UINTPTR_T)nonexpr; + } + ) + if (tree->auxowner) + { + gtcmbufidx = 0; + memcpy(>cmbuf[gtcmbufidx], CLNTNODE_LIT, STR_LIT_LEN(CLNTNODE_LIT)); + gtcmbufidx += STR_LIT_LEN(CLNTNODE_LIT); + memcpy(>cmbuf[gtcmbufidx], tree->auxnode, SIZEOF(tree->auxnode)); + gtcmbufidx += real_len(SIZEOF(tree->auxnode), (uchar_ptr_t)tree->auxnode); + memcpy(>cmbuf[gtcmbufidx], CLNTPID_LIT, STR_LIT_LEN(CLNTPID_LIT)); + gtcmbufidx += STR_LIT_LEN(CLNTPID_LIT); + SPRINTF(>cmbuf[gtcmbufidx], PIDPRINT_LIT, tree->auxpid); + f[5] = strlen(gtcmbuf); + f[6] = (UINTPTR_T)>cmbuf[0]; + assert(f[5] > gtcmbufidx); + assert(gtcmbufidx < SIZEOF(gtcmbuf)); + } else + f[5] = f[6] = 0; + if (interactive) + { + if (LNAM >= f[0]) + { + msg = gnam; + len1 = STR_LIT_LEN(gnam); + } else + { + msg = gnaml; + len1 = STR_LIT_LEN(gnaml); + } + memcpy(format, msg, len1); + + if (owned && !lock) + { + msg = ownedby; + len2 = STR_LIT_LEN(ownedby); + } else + { + msg = request; + len2 = STR_LIT_LEN(request); + } + memcpy(format + len1, msg, len2); + format[len1 + len2] = '\0'; + assert((len1 + len2) < SIZEOF(format)); + if (NULL == lnk) + { + if ((NULL == one_lock.addr) || + (!memcmp(name->addr, one_lock.addr, one_lock.len) + && (!exact || (one_lock.len == f[0])))) + util_out_print(format, FLUSH, f[0], f[1], f[2], f[3], f[4], f[5], f[6]); + } else + util_cm_print(lnk, CMMS_V_LKESHOW, format, FLUSH, + f[0], f[1], f[2], f[3], f[4], f[5], f[6]); + } + if ((NULL != one_lock.addr) && + (memcmp(name->addr, one_lock.addr, one_lock.len) || + (exact && (one_lock.len != f[0])))) + { + lock = FALSE; + return lock; + } + lock = TRUE; + } + f[0] = 0; + r = (0 == r->next) ? (mlk_prcblk_ptr_t)NULL : (mlk_prcblk_ptr_t)R2A(r->next); + } + } + return lock; +} diff --git a/sr_port/lke_showtree.c b/sr_port/lke_showtree.c new file mode 100644 index 0000000..ddc7d63 --- /dev/null +++ b/sr_port/lke_showtree.c @@ -0,0 +1,120 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* + * ----------------------------------- + * lke_showtree : displays a lock tree + * used in : lke_show.c + * ----------------------------------- + */ + +#include "mdef.h" + +#include + +#include "gtm_string.h" +#include "gtm_stdio.h" + +#include "mlkdef.h" +#include "cmidef.h" +#include "lke.h" + +#define KDIM 64 /* max number of subscripts */ + +GBLREF VSIG_ATOMIC_T util_interrupt; + +void lke_show_memory(mlk_shrblk_ptr_t bhead, char *prefix) +{ + mlk_shrblk_ptr_t b, bnext; + mlk_shrsub_ptr_t dsub; + char temp[MAX_ZWR_KEY_SZ + 1]; + char new_prefix[KDIM+2]; + + SPRINTF(new_prefix, " %s", prefix); + for (b = bhead, bnext = 0; bnext != bhead; b = bnext) + { + dsub = (mlk_shrsub_ptr_t)R2A(b->value); + memcpy(temp, dsub->data, dsub->length); + temp[dsub->length] = '\0'; + PRINTF("%s%s : [shrblk] %lx : [shrsub] %lx\n", prefix, temp, (long unsigned int) b, (long unsigned int) dsub); + if (b->children) + lke_show_memory((mlk_shrblk_ptr_t)R2A(b->children), new_prefix); + bnext = (mlk_shrblk_ptr_t)R2A(b->rsib); + } +} + + +bool lke_showtree(struct CLB *lnk, + mlk_shrblk_ptr_t tree, + bool all, + bool wait, + pid_t pid, + mstr one_lock, + bool memory) +{ + mlk_shrblk_ptr_t node, start[KDIM]; + unsigned char subscript_offset[KDIM]; + static char name_buffer[MAX_ZWR_KEY_SZ + 1]; + static MSTR_DEF(name, 0, name_buffer); + int depth = 0; + bool locks = FALSE; + + error_def(ERR_CTRLC); + + if (memory) + { + lke_show_memory(tree, " "); + return TRUE; + } + + node = start[0] + = tree; + subscript_offset[0] = 0; + + for (;;) + { + name.len = subscript_offset[depth]; + + /* Display the lock node */ + locks = lke_showlock(lnk, node, &name, all, wait, TRUE, pid, one_lock, FALSE) + || locks; + + /* Move to the next node */ + if (node->children == 0) + { + /* This node has no children, so move to the right */ + node = (mlk_shrblk_ptr_t)R2A(node->rsib); + while (node == start[depth]) + { + /* There are no more siblings to the right at this depth, + so move up and then right */ + if (node->parent == 0) + { + /* We're already at the top, so we're done */ + assert(depth == 0); + return locks; + } + --depth; + node = (mlk_shrblk_ptr_t)R2A(((mlk_shrblk_ptr_t)R2A(node->parent))->rsib); + } + } + else + { + /* This node has children, so move down */ + ++depth; + node = start[depth] + = (mlk_shrblk_ptr_t)R2A(node->children); + subscript_offset[depth] = name.len; + } + if (util_interrupt) + rts_error(VARLSTCNT(1) ERR_CTRLC); + } +} diff --git a/sr_port/lkglvn.c b/sr_port/lkglvn.c new file mode 100644 index 0000000..9086a93 --- /dev/null +++ b/sr_port/lkglvn.c @@ -0,0 +1,120 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "subscript.h" +#include "advancewindow.h" + +GBLREF char window_token; +GBLREF mident window_ident; + +int lkglvn(bool gblvn) +{ + triple *ref, *t1; + char x, lkname_buf[MAX_MIDENT_LEN + 1], *lknam; + oprtype subscripts[MAX_LVSUBSCRIPTS], *sb1, *sb2; + opctype ox; + bool vbar, parse_status; + + error_def(ERR_COMMA); + error_def(ERR_EXTGBLDEL); + error_def(ERR_MAXNRSUBSCRIPTS); + error_def(ERR_LKNAMEXPECTED); + + ox = OC_LKNAME; + sb1 = sb2 = subscripts; + lknam = lkname_buf; + if (gblvn) + *lknam++ = '^'; + if ((TK_LBRACKET == window_token) || (TK_VBAR == window_token)) + { + vbar = (TK_VBAR == window_token); + advancewindow(); + if (vbar) + parse_status = expr(sb1++); + else + parse_status = expratom(sb1++); + if (!parse_status) + return FALSE; + if (window_token == TK_COMMA) + { + advancewindow(); + if (vbar) + parse_status = expr(sb1++); + else + parse_status = expratom(sb1++); + if (!parse_status) + { + return FALSE; + } + } else + *sb1++ = put_str(0,0); + if ((!vbar && (TK_RBRACKET != window_token)) || (vbar && (TK_VBAR != window_token))) + { + stx_error(ERR_EXTGBLDEL); + return FALSE; + } + advancewindow(); + ox = OC_LKEXTNAME; + } else + *sb1++ = put_ilit(0); + + if (window_token != TK_IDENT) + { stx_error(ERR_LKNAMEXPECTED); + return FALSE; + } + assert(window_ident.len <= MAX_MIDENT_LEN); + memcpy(lknam, window_ident.addr, window_ident.len); + lknam += window_ident.len; + *sb1++ = put_str(lkname_buf,(mstr_len_t)(lknam - lkname_buf)); + advancewindow(); + if (window_token == TK_LPAREN) + { + for (;;) + { + if (sb1 >= ARRAYTOP(subscripts)) + { + stx_error(ERR_MAXNRSUBSCRIPTS); + return FALSE; + } + advancewindow(); + if (!expr(sb1)) + return FALSE; + sb1++; + if ((x = window_token) == TK_RPAREN) + { + advancewindow(); + break; + } + if (x != TK_COMMA) + { + stx_error(ERR_COMMA); + return FALSE; + } + } + } + ref = newtriple(ox); + ref->operand[0] = put_ilit((mint)(sb1 - sb2)); + for ( ; sb2 < sb1 ; sb2++) + { + t1 = newtriple(OC_PARAMETER); + ref->operand[1] = put_tref(t1); + ref = t1; + ref->operand[0] = *sb2; + } + return TRUE; +} diff --git a/sr_port/lkinit.c b/sr_port/lkinit.c new file mode 100644 index 0000000..cc83037 --- /dev/null +++ b/sr_port/lkinit.c @@ -0,0 +1,18 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "mlkdef.h" + +GBLDEF bool remlkreq=FALSE; +GBLDEF mlk_pvtblk *mlk_pvt_root = 0; +GBLDEF unsigned short lks_this_cmd; +GBLDEF unsigned char cm_action; diff --git a/sr_port/loadop.m b/sr_port/loadop.m new file mode 100644 index 0000000..28274d9 --- /dev/null +++ b/sr_port/loadop.m @@ -0,0 +1,27 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001, 2008 Fidelity Information Services, Inc ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +loadop ;load op codes + n (opc,opx,opcdcnt,loadh) + k opc,opx,opcdcnt + s opcdcnt=0 + s file=loadh("opcode_def.h") o file:read u file +loop r x i $zeof g fini + i x?1"OPCODE_DEF"1.E s rec=x,opcdcnt=opcdcnt+1 + i d proc + g loop +fini c file + q +proc s val=opcdcnt-1,cd=$p($p(rec,"(",2),",",1) + s opc(cd)=val,opx(val)=cd + q +err u "" w rec,!,"error code=",ec," line=",opcdcnt,! + u file + q diff --git a/sr_port/loadvx.m b/sr_port/loadvx.m new file mode 100644 index 0000000..4976975 --- /dev/null +++ b/sr_port/loadvx.m @@ -0,0 +1,44 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; Copyright 2001, 2010 Fidelity Information Services, Inc ; +; ; +; This source code contains the intellectual property ; +; of its copyright holder(s), and is made available ; +; under a license. If you do not know the terms of ; +; the license, please stop and do not read further. ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +loadvx ;load op codes + n (vxi,vxx,loadh) + k vxi,vxx + s lnr=0 + s file=loadh("vxi.h") o file:read u file +loop r x i $zeof g fini + s rec=x,lnr=lnr+1 + d proc + g loop +fini c file + q +proc i $e(x,"1")'="#" q +pr0 s x=$tr(x,$c(9)," ") + i x'?1"#define"1." "1"VXI_"1.AN1." "1"(0x"1.3AN1")".E s ec=1 g err + s y=$f(x,"VXI_"),x=$e(x,y,999) + s cd=$p(x," ",1),cdx="VXI_"_cd + f i=1:1:$l(cd) i $e(cd,i)?1U s cd=$e(cd,1,i-1)_$c($a(cd,i)+32)_$e(cd,i+1,999) + s val=$p($p($p(x,"(",2),")",1),"0x",2) + d hex2dec + i $d(vxx(val)) s ec=2 g err + i $d(vxi(cd)) s ec=3 g err + s vxi(cd)=val,vxi(cd,1)=cdx,vxx(val)=cd + q +err u "" w rec,!,"error code=",ec," line=",lnr,! + u file + q +hex2dec n n,x,i + s n=0,i=1 +h2d1 s x=$e(val,i) i x="" s val=n q + i x?1U s x=$a(x)-55 + e i x?1L s x=$a(x)-87 + e i x'?1N b + s n=n*16+x,i=i+1 + g h2d1 diff --git a/sr_port/lockconst.h b/sr_port/lockconst.h new file mode 100644 index 0000000..c7e74bd --- /dev/null +++ b/sr_port/lockconst.h @@ -0,0 +1,82 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* lockconst.h - define values used for interlocks */ + +#define LOCK_AVAILABLE 0 +#define LOCK_IN_USE 1 +#define REC_UNDER_PROG 2 +/* CAUTION: the following two values are not currently optional on HPUX, + but rather chosen for HP-PA arthitectural reasons */ +#define GLOBAL_LOCK_AVAILABLE -1 +#define GLOBAL_LOCK_IN_USE 0 + +/* Global latch note for platforms with a micro latch (currently Solaris + and HPUX). The SET_LATCH_GLOBAL macro initializes both the compswap + lock (major latch) and the micro latch used by compswap. The order this is done in is + first the micro latch, then the major latch. The order is important so that + secshr_db_clnup can (re)initialize the entire latch and not run into problems + with concurrent users attempting to get the lock. The compswap routines in + these modules will check the value of the major latch first before even attempting + to obtain the micro latch. +*/ + +#ifdef __hppa +# define alignedaddr(x) (volatile int *)((UINTPTR_T)(x) + 15 & ~0xf) /* 32-bit */ + + /* Given a pointer into memory, round up to the nearest 16-byte boundary + by adding 15, then masking off the last four bits. + Assumption: the input address is already int (4-byte) aligned. + + The VOLATILE keyword is essential in this macro: it ensures that the + compiler does not perform certain optimizations which would compromise + the integrity the spinlock logic. + */ + +# define release_spinlock(lockarea) {if (1) { \ + _flush_globals(); \ + (*alignedaddr(&(lockarea)->hp_latch_space) = GLOBAL_LOCK_AVAILABLE); } else ;} + +/* HP white paper sets latch to 1 for available while we set it to -1 */ + + /* For performance, release_spinlock is a macro, rather than a function. + To release or initialize a spinlock, we simply set its value to one. + + We must call the psuedo function "_flush_globals()" to ensure that + the compiler doesn't hold any externally-visible values in registers + across the lock release */ + + int4 load_and_clear(sm_int_ptr_t); +# define GET_LATCH_GLOBAL(a) (GLOBAL_LOCK_AVAILABLE == *alignedaddr(&(a)->hp_latch_space) ? \ + load_and_clear((sm_int_ptr_t)&(a)->hp_latch_space) : GLOBAL_LOCK_IN_USE) + /* above tries a fast pretest before calling load_and_clear to actually + get the latch */ +# define RELEASE_LATCH_GLOBAL(a) release_spinlock(a) +# define SET_LATCH_GLOBAL(a, b) {RELEASE_LATCH_GLOBAL(a); assert(LOCK_AVAILABLE == b); SET_LATCH(a, b);} + +#elif defined(__sparc) && defined(SPARCV8_NO_CAS) +/* For Sun sparc, we use the extra word of the latch for a micro lock for compswap. Future + iterations of this should make use of the CAS (compare and swap) instruction newly available + in the Sparc Version 9 instruction set. + These *_GLOBAL macros are used only from compswap.c (currently) +*/ +# define GET_LATCH_GLOBAL(a) aswp(&(a)->u.parts.latch_word, GLOBAL_LOCK_IN_USE) +# define RELEASE_LATCH_GLOBAL(a) aswp(&(a)->u.parts.latch_word, GLOBAL_LOCK_AVAILABLE) +# define SET_LATCH_GLOBAL(a, b) {SET_LATCH(&(a)->u.parts.latch_word, GLOBAL_LOCK_AVAILABLE); SET_LATCH(a, b);} + +#elif defined(VMS) +# define SET_LATCH_GLOBAL(a, b) {(a)->u.parts.latch_image_count = 0; SET_LATCH(a, b);} +#else +# define SET_LATCH_GLOBAL(a, b) SET_LATCH(a, b) +#endif + +/* perhaps this should include flush so other CPUs see the change now */ +#define SET_LATCH(a,b) (*((sm_int_ptr_t)a) = b) diff --git a/sr_port/locklits.h b/sr_port/locklits.h new file mode 100644 index 0000000..aeb6ca3 --- /dev/null +++ b/sr_port/locklits.h @@ -0,0 +1,27 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#define MAX_LCKARG 253 +#define LOCKED 1 +#define ZALLOCATED 2 +#define LCK_REQUEST 4 +#define ZAL_REQUEST 8 +#define PENDING 16 +#define COMPLETE 32 +#define INCREMENTAL 0x40 +#define NEW 0x80 + +#define DEAD 2 +#define PART_DEAD 1 +#define NOT_DEAD 0 +#define NOT_THERE -1 + +#define LOCK_SELF_WAKE 100 diff --git a/sr_port/logical_truth_value.c b/sr_port/logical_truth_value.c new file mode 100644 index 0000000..7ab2b99 --- /dev/null +++ b/sr_port/logical_truth_value.c @@ -0,0 +1,87 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_ctype.h" + +#include "min_max.h" +#include "gtm_string.h" +#include "gtm_strings.h" /* for STRNCASECMP */ +#include "trans_log_name.h" +#include "iosp.h" +#include "logical_truth_value.h" + +/* returns the truth value based on the sense indicated by 'negate'. + * If negate is FALSE (i.e. in regular mode), + * returns TRUE if the env variable/logical log is defined and evaluates to "TRUE" (or part thereof), + * or "YES" (or part thereof), or a non zero integer + * returns FALSE otherwise + * If negate is TRUE(i.e. in negative mode), + * returns TRUE if the env variable/logical log is defined and evaluates to "FALSE" (or part thereof), + * or "NO" (or part thereof), or a zero integer + * returns FALSE otherwise + */ +boolean_t logical_truth_value(mstr *log, boolean_t negate, boolean_t *is_defined) +{ + int4 status; + mstr tn; + char buf[1024]; + boolean_t zero, is_num; + int index; + + error_def(ERR_LOGTOOLONG); + error_def(ERR_TRNLOGFAIL); + + tn.addr = buf; + if (NULL != is_defined) + *is_defined = FALSE; + if (SS_NORMAL == (status = TRANS_LOG_NAME(log, &tn, buf, SIZEOF(buf), dont_sendmsg_on_log2long))) + { + if (NULL != is_defined) + *is_defined = TRUE; + if (tn.len <= 0) + return FALSE; + for (is_num = TRUE, zero = TRUE, index = 0; index < tn.len; index++) + { + if (!ISDIGIT_ASCII(buf[index])) + { + is_num = FALSE; + break; + } + zero = (zero && ('0' == buf[index])); + } + if (!negate) + { /* regular mode */ + return (!is_num ? (0 == STRNCASECMP(buf, LOGICAL_TRUE, MIN(STR_LIT_LEN(LOGICAL_TRUE), tn.len)) || + 0 == STRNCASECMP(buf, LOGICAL_YES, MIN(STR_LIT_LEN(LOGICAL_YES), tn.len))) + : !zero); + } else + { /* negative mode */ + return (!is_num ? (0 == STRNCASECMP(buf, LOGICAL_FALSE, MIN(STR_LIT_LEN(LOGICAL_FALSE), tn.len)) || + 0 == STRNCASECMP(buf, LOGICAL_NO, MIN(STR_LIT_LEN(LOGICAL_NO), tn.len))) + : zero); + } + } else if (SS_NOLOGNAM == status) + return (FALSE); +# ifdef UNIX + else if (SS_LOG2LONG == status) + { + rts_error(VARLSTCNT(5) ERR_LOGTOOLONG, 3, log->len, log->addr, SIZEOF(buf) - 1); + return (FALSE); + } +# endif + else + { + rts_error(VARLSTCNT(5) ERR_TRNLOGFAIL, 2, log->len, log->addr, status); + return (FALSE); + } +} diff --git a/sr_port/logical_truth_value.h b/sr_port/logical_truth_value.h new file mode 100644 index 0000000..31a1914 --- /dev/null +++ b/sr_port/logical_truth_value.h @@ -0,0 +1,23 @@ +/**************************************************************** + * * + * Copyright 2001, 2006 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef LOGICAL_TRUTH_VALUE_H_INCLUDED +#define LOGICAL_TRUTH_VALUE_H_INCLUDED + +#define LOGICAL_TRUE "TRUE" +#define LOGICAL_YES "YES" + +#define LOGICAL_FALSE "FALSE" +#define LOGICAL_NO "NO" + +boolean_t logical_truth_value(mstr *logical, boolean_t negate, boolean_t *is_defined); + +#endif /* LOGICAL_TRUTH_VALUE_H_INCLUDED */ diff --git a/sr_port/longcpy.h b/sr_port/longcpy.h new file mode 100644 index 0000000..4eb8d2b --- /dev/null +++ b/sr_port/longcpy.h @@ -0,0 +1,28 @@ +/**************************************************************** + * * + * Copyright 2001, 2003 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef LONGCPY_INCLUDED +#define LONGCPY_INCLUDED + +/* + +To be one day eliminated when usage is totally replaced by the memcpy +calls it should be calling now instead. Since this is a stop-gap measure, +it is ok that this include pulls in gtm_string.h if necessary. + +void longcpy(uchar_ptr_t a, uchar_ptr_t b, int4 len); + + */ + +#include "gtm_string.h" +#define longcpy(dst, src, len) memcpy(dst, src, len) + +#endif /* LONGCPY_INCLUDED */ diff --git a/sr_port/longset.h b/sr_port/longset.h new file mode 100644 index 0000000..6c37dd4 --- /dev/null +++ b/sr_port/longset.h @@ -0,0 +1,28 @@ +/**************************************************************** + * * + * Copyright 2001, 2003 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef LONGSET_INCLUDED +#define LONGSET_INCLUDED + +/* + +To be one day eliminated when usage is totally replaced by the memset +calls it should be calling now instead. Since this is a stop-gap measure, +it is ok that this include pulls in gtm_string.h if necessary. + +void longset(uchar_ptr_t ptr, int len, unsigned char fill); + +*/ + +#include "gtm_string.h" +#define longset(dst, len, fill) memset(dst, fill, len) + +#endif /* LONGSET_INCLUDED */ diff --git a/sr_port/lookup_variable_htent.c b/sr_port/lookup_variable_htent.c new file mode 100644 index 0000000..b509fa5 --- /dev/null +++ b/sr_port/lookup_variable_htent.c @@ -0,0 +1,52 @@ +/**************************************************************** + * * + * Copyright 2009, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" +#include "gtm_string.h" + +#include "lv_val.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "rtnhdr.h" +#include "stack_frame.h" +#include "lookup_variable_htent.h" +#include "alias.h" + +GBLREF symval *curr_symval; +GBLREF stack_frame *frame_pointer; + +ht_ent_mname *lookup_variable_htent(unsigned int x) +{ + ht_ent_mname *tabent; + mident_fixed varname; + boolean_t added; + + assert(x < frame_pointer->vartab_len); + added = add_hashtab_mname_symval(&curr_symval->h_symtab, ((var_tabent *)frame_pointer->vartab_ptr + x), NULL, &tabent); + assert(tabent); + if (NULL == tabent->value) + { + assert(added); /* Should never be a valid name without an lv */ +#ifdef DEBUG_REFCNT + memset(varname.c, '\0', SIZEOF(varname)); + memcpy(varname.c, tabent->key.var_name.addr, tabent->key.var_name.len); + DBGRFCT((stderr, "lookup_variable_htent: Allocating lv_val for variable '%s'\n", varname.c)); +#endif + lv_newname(tabent, curr_symval); + } + assert(NULL != LV_GET_SYMVAL((lv_val *)tabent->value)); + return tabent; +} diff --git a/sr_port/lookup_variable_htent.h b/sr_port/lookup_variable_htent.h new file mode 100644 index 0000000..130e85c --- /dev/null +++ b/sr_port/lookup_variable_htent.h @@ -0,0 +1,17 @@ +/**************************************************************** + * * + * Copyright 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __LOOKUP_VARIABLE_HTENT_H__ +#define __LOOKUP_VARIABLE_HTENT_H__ + +ht_ent_mname *lookup_variable_htent(unsigned int x); + +#endif diff --git a/sr_port/lower_to_upper.c b/sr_port/lower_to_upper.c new file mode 100644 index 0000000..387f63c --- /dev/null +++ b/sr_port/lower_to_upper.c @@ -0,0 +1,25 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "gtm_caseconv.h" + +LITREF unsigned char lower_to_upper_table[]; + +void lower_to_upper(uchar_ptr_t d, uchar_ptr_t s, int4 len) +{ + uchar_ptr_t d_top; + + d_top = d + len; + for ( ; d < d_top; ) + { *d++ = lower_to_upper_table[*s++]; + } +} diff --git a/sr_port/lref.c b/sr_port/lref.c new file mode 100644 index 0000000..948980b --- /dev/null +++ b/sr_port/lref.c @@ -0,0 +1,75 @@ +/**************************************************************** + * * + * Copyright 2001, 2004 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "cmd_qlf.h" +#include "advancewindow.h" +#include "gtm_caseconv.h" + +GBLREF char window_token; +GBLREF mident window_ident; +GBLREF command_qualifier cmd_qlf; +LITREF mident zero_ident; + +int lref(oprtype *label,oprtype *offset,bool no_lab_ok,mint commarg_code,bool commarg_ok,bool *got_some) +{ + + triple *ref; + char c; + error_def(ERR_LABELEXPECTED); + + switch(window_token) + { + case TK_INTLIT: + int_label(); + /* caution: fall through */ + case TK_IDENT: + *got_some = TRUE; + if (!(cmd_qlf.qlf & CQ_LOWER_LABELS)) + lower_to_upper((uchar_ptr_t)window_ident.addr, (uchar_ptr_t)window_ident.addr, window_ident.len); + *label = put_str(window_ident.addr, window_ident.len); + advancewindow(); + break; + case TK_ATSIGN: + *got_some = TRUE; + if (!indirection(label)) + return FALSE; + if (commarg_ok && (c = window_token) != TK_COLON && c != TK_CIRCUMFLEX && c != TK_PLUS) + { + ref = newtriple(OC_COMMARG); + ref->operand[0] = *label; + ref->operand[1] = put_ilit(commarg_code); + *label = put_tref(ref); + return TRUE; + } + break; + case TK_COLON: + case TK_CIRCUMFLEX: + return TRUE; + case TK_PLUS: + *label = put_str(zero_ident.addr, zero_ident.len); + if (no_lab_ok) + break; + /* caution: fall through */ + default: + stx_error(ERR_LABELEXPECTED); + return FALSE; + } + if (window_token != TK_PLUS) + return TRUE; + *got_some = TRUE; + advancewindow(); + return intexpr(offset); + +} diff --git a/sr_port/lv_getslot.c b/sr_port/lv_getslot.c new file mode 100644 index 0000000..f7c4157 --- /dev/null +++ b/sr_port/lv_getslot.c @@ -0,0 +1,150 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" + +#include "lv_val.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "caller_id.h" +#include "alias.h" + +lv_val *lv_getslot(symval *sym) +{ + lv_blk *p,*q; + lv_val *lv; + unsigned int numElems, numUsed; + + numElems = MAXUINT4; /* maximum value */ + if (lv = sym->lv_flist) + { + assert(NULL == LV_PARENT(lv)); /* stp_gcol relies on this for correct garbage collection */ + sym->lv_flist = (lv_val *)lv->ptrs.free_ent.next_free; + } else + { + for (p = sym->lv_first_block; ; p = p->next) + { + if (NULL == p) + { + if (NULL != (p = sym->lv_first_block)) + numElems = p->numAlloc; + else + { + assert(FALSE); + numElems = LV_NEWBLOCK_INIT_ALLOC; /* be safe in pro */ + } + lv_newblock(sym, numElems > 64 ? 128 : numElems * 2); + p = sym->lv_first_block; + assert(NULL != p); + } + if ((numUsed = p->numUsed) < p->numAlloc) + { + lv = (lv_val *)LV_BLK_GET_BASE(p); + lv = &lv[numUsed]; + p->numUsed++; + break; + } + assert(numElems >= p->numAlloc); + DEBUG_ONLY(numElems = p->numAlloc); + } + } + assert(lv); + DBGRFCT((stderr, ">> lv_getslot(): Allocating new lv_val at 0x"lvaddr" by routine 0x"lvaddr"\n", lv, caller_id())); + return lv; +} + +lvTree *lvtree_getslot(symval *sym) +{ + lv_blk *p,*q; + lvTree *lvt; + unsigned int numElems, numUsed; + + numElems = MAXUINT4; /* maximum value */ + if (lvt = sym->lvtree_flist) + { + assert(NULL == LVT_GET_PARENT(lvt)); + sym->lvtree_flist = (lvTree *)lvt->avl_root; + } else + { + for (p = sym->lvtree_first_block; ; p = p->next) + { + if (NULL == p) + { + if (NULL != (p = sym->lvtree_first_block)) + numElems = p->numAlloc; + else + numElems = LV_NEWBLOCK_INIT_ALLOC; + lvtree_newblock(sym, numElems > 64 ? 128 : numElems * 2); + p = sym->lvtree_first_block; + assert(NULL != p); + } + if ((numUsed = p->numUsed) < p->numAlloc) + { + lvt = (lvTree *)LV_BLK_GET_BASE(p); + lvt = &lvt[numUsed]; + p->numUsed++; + break; + } + assert(numElems >= p->numAlloc); + DEBUG_ONLY(numElems = p->numAlloc); + } + } + assert(lvt); + DBGRFCT((stderr, ">> lvtree_getslot(): Allocating new lvTree at 0x"lvaddr" by routine 0x"lvaddr"\n", lvt, caller_id())); + return lvt; +} + +lvTreeNode *lvtreenode_getslot(symval *sym) +{ + lv_blk *p,*q; + lvTreeNode *lv; + unsigned int numElems, numUsed; + + numElems = MAXUINT4; /* maximum value */ + if (lv = sym->lvtreenode_flist) + { + assert(NULL == LV_PARENT(lv)); /* stp_gcol relies on this for correct garbage collection */ + sym->lvtreenode_flist = (lvTreeNode *)lv->sbs_child; + } else + { + for (p = sym->lvtreenode_first_block; ; p = p->next) + { + if (NULL == p) + { + if (NULL != (p = sym->lvtreenode_first_block)) + numElems = p->numAlloc; + else + numElems = LV_NEWBLOCK_INIT_ALLOC; + lvtreenode_newblock(sym, numElems > 64 ? 128 : numElems * 2); + p = sym->lvtreenode_first_block; + assert(NULL != p); + } + if ((numUsed = p->numUsed) < p->numAlloc) + { + lv = (lvTreeNode *)LV_BLK_GET_BASE(p); + lv = &lv[numUsed]; + p->numUsed++; + break; + } + assert(numElems >= p->numAlloc); + DEBUG_ONLY(numElems = p->numAlloc); + } + } + assert(lv); + DBGRFCT((stderr, ">> lvtreenode_getslot(): Allocating new lvTreeNode at 0x"lvaddr" by routine 0x"lvaddr"\n", + lv, caller_id())); + return lv; +} diff --git a/sr_port/lv_kill.c b/sr_port/lv_kill.c new file mode 100644 index 0000000..5539a74 --- /dev/null +++ b/sr_port/lv_kill.c @@ -0,0 +1,81 @@ +/**************************************************************** + * * + * Copyright 2009, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" +#include "gtm_string.h" + +#include "lv_val.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "alias.h" + +GBLREF lv_val *active_lv; +GBLREF uint4 dollar_tlevel; + +void lv_kill(lv_val *lv, boolean_t dotpsave, boolean_t do_subtree) +{ + lv_val *base_lv; + lvTree *lvt_child, *lvt; + boolean_t is_base_var; + symval *sym; + + active_lv = (lv_val *)NULL; /* if we get here, subscript set was successful. clear active_lv to avoid later + cleanup problems */ + if (lv) + { + is_base_var = LV_IS_BASE_VAR(lv); + base_lv = !is_base_var ? LV_GET_BASE_VAR(lv) : lv; + if (dotpsave && dollar_tlevel && (NULL != base_lv->tp_var) && !base_lv->tp_var->var_cloned) + TP_VAR_CLONE(base_lv); /* clone the tree */ + lvt_child = LV_GET_CHILD(lv); + if (do_subtree && (NULL != lvt_child)) + { + LV_CHILD(lv) = NULL; + lv_killarray(lvt_child, dotpsave); + } + DECR_AC_REF(lv, dotpsave); /* Decrement alias container refs and cleanup if necessary */ + if (!is_base_var && (do_subtree || (NULL == lvt_child))) + { + sym = LV_GET_SYMVAL(base_lv); + for ( ; ; ) + { + lvt = LV_GET_PARENT_TREE(lv); + LV_VAL_CLEAR_MVTYPE(lv); /* see comment in macro definition for why this is necessary */ + LV_TREE_NODE_DELETE(lvt, (lvTreeNode *)lv); + /* if there is at least one other sibling node to the deleted "lv" the zap stops here */ + if (lvt->avl_height) + break; + assert(NULL == lvt->avl_root); + lv = (lv_val *)LVT_PARENT(lvt); + assert(NULL != lv); + LV_CHILD(lv) = NULL; + LVTREE_FREESLOT(lvt); + assert(LV_IS_VAL_DEFINED(lv) == (0 != lv->v.mvtype)); + if (LV_IS_VAL_DEFINED(lv)) + break; + if (lv == base_lv) + { /* Base node. Do not invoke LV_FREESLOT/LV_FLIST_ENQUEUE as we will still keep the + * lv_val for the non-existing base local variable pointed to by the curr_symval + * hash table entry. Just clear mvtype to mark the lv_val undefined. + */ + lv->v.mvtype = 0; /* Base node */ + break; + } + } + } else + lv->v.mvtype = 0; /* Base node */ + } +} diff --git a/sr_port/lv_killarray.c b/sr_port/lv_killarray.c new file mode 100644 index 0000000..5cc2c40 --- /dev/null +++ b/sr_port/lv_killarray.c @@ -0,0 +1,66 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" +#include "gtm_string.h" + +#include "lv_val.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "alias.h" + +/* Note it is important that callers of this routine make sure that the pointer that is passed as + * an argument is removed from the lv_val it came from prior to the call. This prevents arrays + * that have alias containers pointing that form a loop back to the originating lv_val from causing + * processing loops, in effect over-processing arrays that have already been processed or lv_vals + * that have been deleted. + */ +void lv_killarray(lvTree *lvt, boolean_t dotpsave) +{ + lvTreeNode *node, *nextnode; + lvTree *tmplvt; + + DEBUG_ONLY( + lv_val *lv; + + assert(NULL != lvt); + lv = (lv_val *)LVT_PARENT(lvt); + assert(NULL == LV_CHILD(lv)); /* Owner lv's children pointer MUST be NULL! */ + ) + /* Iterate through the tree in post-order fashion. Doing it in-order or pre-order has issues since we would have + * freed up nodes in the tree but would need to access links in them to get at the NEXT node. + */ + for (node = lvAvlTreeFirstPostOrder(lvt); NULL != node; node = nextnode) + { + nextnode = lvAvlTreeNextPostOrder(node); /* determine "nextnode" before freeing "node" */ + assert(NULL != node); + tmplvt = LV_CHILD(node); + if (NULL != tmplvt) + { + LV_CHILD(node) = NULL; + lv_killarray(tmplvt, dotpsave); + } + DECR_AC_REF(((lv_val *)node), dotpsave); /* Decrement alias contain ref and cleanup if necessary */ + /* If node points to an "lv_val", we need to do a heavyweight LV_FREESLOT call to free up the lv_val. + * But we instead do a simple "LVTREENODE_FREESLOT" call because we are guaranteed node points to a "lvTreeNode" + * (i.e. it is a subscripted lv and never the base lv). Assert that. + */ + assert(!LV_IS_BASE_VAR(node)); + LV_VAL_CLEAR_MVTYPE(node); /* see comment in macro definition for why this is necessary */ + LVTREENODE_FREESLOT(node); + } + LVTREE_FREESLOT(lvt); +} diff --git a/sr_port/lv_newblock.c b/sr_port/lv_newblock.c new file mode 100644 index 0000000..b04379e --- /dev/null +++ b/sr_port/lv_newblock.c @@ -0,0 +1,86 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gtm_stdio.h" + +#include "gtm_malloc.h" +#include "lv_val.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "alias.h" + +void lv_newblock(symval *sym, int numElems) +{ + register lv_blk *ptr; + register int n; + lv_val *lv_base; + + n = numElems * SIZEOF(lv_val) + SIZEOF(lv_blk); + n = INTCAST(gtm_bestfitsize(n)); + /* Maximize use of storage block we are going to get */ + assert(DIVIDE_ROUND_DOWN(n - SIZEOF(lv_blk), SIZEOF(lv_val)) >= numElems); + numElems = DIVIDE_ROUND_DOWN(n - SIZEOF(lv_blk), SIZEOF(lv_val)); + ptr = (lv_blk *)malloc(n); + lv_base = (lv_val *)LV_BLK_GET_BASE(ptr); + memset(lv_base, 0, numElems * SIZEOF(lv_val)); + ptr->next = sym->lv_first_block; + sym->lv_first_block = ptr; + ptr->numAlloc = numElems; + ptr->numUsed = 0; + DBGRFCT((stderr, "lv_newblock: New lv_blk allocated ******************\n")); +} + +void lvtree_newblock(symval *sym, int numElems) +{ + register lv_blk *ptr; + register int n; + lvTree *lvt_base; + + n = numElems * SIZEOF(lvTree) + SIZEOF(lv_blk); + n = INTCAST(gtm_bestfitsize(n)); + /* Maximize use of storage block we are going to get */ + assert(DIVIDE_ROUND_DOWN(n - SIZEOF(lv_blk), SIZEOF(lvTree)) >= numElems); + numElems = DIVIDE_ROUND_DOWN(n - SIZEOF(lv_blk), SIZEOF(lvTree)); + ptr = (lv_blk *)malloc(n); + lvt_base = (lvTree *)LV_BLK_GET_BASE(ptr); + ptr->next = sym->lvtree_first_block; + sym->lvtree_first_block = ptr; + ptr->numAlloc = numElems; + ptr->numUsed = 0; + DBGRFCT((stderr, "lvtree_newblock: New lv_blk allocated ******************\n")); +} + +void lvtreenode_newblock(symval *sym, int numElems) +{ + register lv_blk *ptr; + register int n; + lvTreeNode *lv_base; + + n = numElems * SIZEOF(lvTreeNode) + SIZEOF(lv_blk); + n = INTCAST(gtm_bestfitsize(n)); + /* Maximize use of storage block we are going to get */ + assert(DIVIDE_ROUND_DOWN(n - SIZEOF(lv_blk), SIZEOF(lvTreeNode)) >= numElems); + numElems = DIVIDE_ROUND_DOWN(n - SIZEOF(lv_blk), SIZEOF(lvTreeNode)); + ptr = (lv_blk *)malloc(n); + lv_base = (lvTreeNode *)LV_BLK_GET_BASE(ptr); + memset(lv_base, 0, numElems * SIZEOF(lvTreeNode)); + ptr->next = sym->lvtreenode_first_block; + sym->lvtreenode_first_block = ptr; + ptr->numAlloc = numElems; + ptr->numUsed = 0; + DBGRFCT((stderr, "lvtreenode_newblock: New lv_blk allocated ******************\n")); +} diff --git a/sr_port/lv_newname.c b/sr_port/lv_newname.c new file mode 100644 index 0000000..a7f6ca7 --- /dev/null +++ b/sr_port/lv_newname.c @@ -0,0 +1,85 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" +#include "gtm_string.h" + +#include "rtnhdr.h" +#include "stack_frame.h" +#include "mv_stent.h" +#include "lv_val.h" +#include "tp_frame.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "caller_id.h" +#include "alias.h" + +GBLREF tp_frame *tp_pointer; +GBLREF stack_frame *frame_pointer; +GBLREF symval *curr_symval; +GBLREF mv_stent *mv_chain; + +void lv_newname(ht_ent_mname *hte, symval *sym) +{ + lv_val *lv, *var; + tp_frame *tf, *first_tf_saveall; + tp_var *restore_ent; + DBGRFCT_ONLY(mident_fixed vname;) + + assert(hte); + assert(sym); + lv = lv_getslot(sym); + LVVAL_INIT(lv, sym); + DBGRFCT_ONLY( + memcpy(vname.c, hte->key.var_name.addr, hte->key.var_name.len); + vname.c[hte->key.var_name.len] = '\0'; + ); + DBGRFCT((stderr, "lv_newname: Varname '%s' in sym 0x"lvaddr" resetting hte 0x"lvaddr" from 0x"lvaddr" to 0x"lvaddr + " -- called from 0x"lvaddr"\n\n", + &vname.c, sym, hte, hte->value, lv, caller_id())); + hte->value = lv; + assert(0 < lv->stats.trefcnt); + if (!sym->tp_save_all) + return; + /* Newly encountered variables need to be saved if there is restore all TP frame in effect as they + need to be restored to an undefined state but we only know about them when we encounter them + hence this code where new vars are created. We locate the earliest TP frame that has the same symval + in its tp_frame and save the entry there. This is so var set in later TP frame levels still get restored + even if the TSTART frame they were created in gets committed. + */ + DEBUG_ONLY(first_tf_saveall = NULL); + for (tf = tp_pointer; (NULL != tf) && (tf->sym == sym); tf = tf->old_tp_frame) + { + if (tf->tp_save_all_flg) + first_tf_saveall = tf; + } + assert(first_tf_saveall); + assert(sym == LV_SYMVAL(lv)); + var = lv_getslot(sym); + restore_ent = (tp_var *)malloc(SIZEOF(*restore_ent)); + restore_ent->current_value = lv; + restore_ent->save_value = var; + restore_ent->key = hte->key; + restore_ent->var_cloned = TRUE; + restore_ent->next = first_tf_saveall->vars; + first_tf_saveall->vars = restore_ent; + assert(NULL == lv->tp_var); + lv->tp_var = restore_ent; + *var = *lv; + INCR_CREFCNT(lv); /* With a copy made, bump the refcnt to keep lvval from being deleted */ + INCR_TREFCNT(lv); + assert(1 < lv->stats.trefcnt); +} diff --git a/sr_port/lv_tree.c b/sr_port/lv_tree.c new file mode 100644 index 0000000..fbbc66f --- /dev/null +++ b/sr_port/lv_tree.c @@ -0,0 +1,1904 @@ +/**************************************************************** + * * + * Copyright 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include /* for OFFSETOF macro used in the IS_OFFSET_AND_SIZE_MATCH macro */ + +#include "collseq.h" +#include "subscript.h" +#include "lv_tree.h" +#include "gtm_stdio.h" +#include "min_max.h" +#include "lv_val.h" +#include "numcmp.h" +#include "arit.h" +#include "promodemo.h" /* for "promote" & "demote" prototype */ + +#define LV_TREE_INIT_ALLOC 4 +#define LV_TREENODE_INIT_ALLOC 16 +#define LV_TREESUBSCR_INIT_ALLOC 16 + +/* the below assumes TREE_LEFT_HEAVY is 0, TREE_RIGHT_HEAVY is 1 and TREE_BALANCED is 2 */ +LITDEF int tree_balance_invert[] = {TREE_RIGHT_HEAVY, TREE_LEFT_HEAVY, TREE_BALANCED}; + +LITREF mval literal_null; +LITREF int4 ten_pwr[NUM_DEC_DG_1L+1]; + +STATICFNDCL void lvAvlTreeNodeFltConv(lvTreeNodeNum *fltNode); +STATICFNDCL lvTreeNode *lvAvlTreeSingleRotation(lvTreeNode *rebalanceNode, lvTreeNode *anchorNode, int4 balanceFactor); +STATICFNDCL lvTreeNode *lvAvlTreeDoubleRotation(lvTreeNode *rebalanceNode, lvTreeNode *anchorNode, int4 balanceFactor); +STATICFNDCL boolean_t lvAvlTreeLookupKeyCheck(treeKeySubscr *key); +STATICFNDCL int lvAvlTreeNodeHeight(lvTreeNode *node); +STATICFNDCL boolean_t lvAvlTreeNodeIsWellFormed(lvTree *lvt, lvTreeNode *node); + +STATICFNDEF void lvAvlTreeNodeFltConv(lvTreeNodeNum *fltNode) +{ + int int_m1, int_exp; + const int4 *pwr; + DEBUG_ONLY(int key_mvtype;) + + DEBUG_ONLY(key_mvtype = fltNode->key_mvtype;) + assert(MV_INT & key_mvtype); + assert(!fltNode->key_flags.key_bits.key_iconv); + int_m1 = fltNode->key_m0; + if (int_m1) + { + /* The following logic is very similar to that in promodemo.c (promote function). + * That operates on mvals whereas this does not hence the duplication inspite of similar logic. + */ + if (0 < int_m1) + fltNode->key_flags.key_bits.key_sgn = 0; + else + { + fltNode->key_flags.key_bits.key_sgn = 1; + int_m1 = -int_m1; + } + int_exp = 0; + pwr = &ten_pwr[0]; + while (int_m1 >= *pwr) + { + int_exp++; + pwr++; + assert(pwr < ARRAYTOP(ten_pwr)); + } + fltNode->key_m1 = int_m1 * ten_pwr[NUM_DEC_DG_1L - int_exp] ; + int_exp += EXP_INT_UNDERF; + fltNode->key_flags.key_bits.key_e = int_exp; + } else + fltNode->key_flags.key_bits.key_e = 0; /* integer 0 will have exponent 0 even after float promotion */ + fltNode->key_flags.key_bits.key_iconv = TRUE; + return; +} + +/* All the below comparison macros & functions return + * = 0 if equal; + * < 0 if key of aSUBSCR < key of bNODE; + * > 0 if key of aSUBSCR > key of bNODE + * The 3 LV_AVL_TREE_*KEY_CMP macros below have very similar (yet not completely identical) code. + * They are kept separate because this is frequently used code and we want to avoid if checks as much as possible. + * Having separate macros for each keytype lets us reduce the # of if checks we need inside the macro. + */ + +#define LV_AVL_TREE_INTKEY_CMP(aSUBSCR, aSUBSCR_M1, bNODE, retVAL) \ +{ \ + int a_m1, b_mvtype, a_sgn, b_sgn, exp_diff, m1_diff, m0_diff, b_m0; \ + lvTreeNodeNum *bNodeFlt; \ + treeKeySubscr fltSubscr, *tmpASubscr; \ + \ + /* aSUBSCR is a number */ \ + assert(TREE_KEY_SUBSCR_IS_CANONICAL((aSUBSCR)->mvtype)); \ + assert(MVTYPE_IS_INT((aSUBSCR)->mvtype)); \ + b_mvtype = (bNODE)->key_mvtype; \ + assert(!TREE_KEY_SUBSCR_IS_CANONICAL(b_mvtype) \ + || (MVTYPE_IS_NUMERIC(b_mvtype) && !MVTYPE_IS_NUM_APPROX(b_mvtype))); \ + assert(MVTYPE_IS_NUMERIC((aSUBSCR)->mvtype) && !MVTYPE_IS_NUM_APPROX((aSUBSCR)->mvtype)); \ + assert((aSUBSCR_M1) == (aSUBSCR)->m[1]); \ + if (MV_INT & b_mvtype) \ + { \ + bNodeFlt = (lvTreeNodeNum *)(bNODE); \ + (retVAL) = (aSUBSCR_M1) - bNodeFlt->key_m0; \ + } else if (TREE_KEY_SUBSCR_IS_CANONICAL(b_mvtype)) \ + { /* Both aSUBSCR and bNODE are numbers */ \ + b_mvtype &= MV_INT; \ + /* aSUBSCR is MV_INT but bNODE is not. Promote aSubscr to float representation before comparing \ + * it with bNODE. Cannot touch input mval since non-lv code expects MV_INT value to be stored in \ + * integer format only (and not float format). So use a temporary mval for comparison. \ + */ \ + fltSubscr.m[1] = (aSUBSCR_M1); \ + tmpASubscr = &fltSubscr; \ + promote(tmpASubscr); /* Note this modifies tmpASubscr->m[1] so no longer can use aSUBSCR_M1 below */ \ + assert(tmpASubscr->e || !tmpASubscr->m[1]); \ + bNodeFlt = (lvTreeNodeNum *)(bNODE); \ + if (b_mvtype && !bNodeFlt->key_flags.key_bits.key_iconv) \ + { \ + lvAvlTreeNodeFltConv(bNodeFlt); \ + assert(bNodeFlt->key_flags.key_bits.key_iconv); \ + } \ + a_sgn = (0 == tmpASubscr->sgn) ? 1 : -1; /* 1 if positive, -1 if negative */ \ + b_sgn = (0 == bNodeFlt->key_flags.key_bits.key_sgn) ? 1 : -1; \ + /* Check sign. If different we are done right there */ \ + if (a_sgn != b_sgn) \ + (retVAL) = a_sgn; \ + else \ + { /* Signs equal; compare exponents for magnitude and adjust sense depending on sign. */ \ + exp_diff = tmpASubscr->e - bNodeFlt->key_flags.key_bits.key_e; \ + if (exp_diff) \ + (retVAL) = (exp_diff * a_sgn); \ + else \ + { /* Signs and exponents equal; compare magnitudes. */ \ + /* First, compare high-order 9 digits of magnitude. */ \ + a_m1 = tmpASubscr->m[1]; \ + m1_diff = a_m1 - bNodeFlt->key_m1; \ + if (m1_diff) \ + (retVAL) = (m1_diff * a_sgn); \ + else \ + { /* High-order 9 digits equal; if not zero, compare low-order 9 digits. */ \ + if (0 == a_m1) /* zero special case */ \ + (retVAL) = 0; \ + else \ + { \ + b_m0 = (b_mvtype ? 0 : bNodeFlt->key_m0); \ + m0_diff = tmpASubscr->m[0] - b_m0; \ + if (m0_diff) \ + (retVAL) = (m0_diff * a_sgn); \ + else \ + { /* Signs, exponents, high-order & low-order magnitudes equal */ \ + (retVAL) = 0; \ + } \ + } \ + } \ + } \ + } \ + } else \ + (retVAL) = -1; /* aSubscr is a number, but bNODE is a string */ \ +} + +#define LV_AVL_TREE_NUMKEY_CMP(aSUBSCR, bNODE, retVAL) \ +{ \ + int b_mvtype, a_sgn, b_sgn, exp_diff, m1_diff, m0_diff, a_m1, b_m0; \ + lvTreeNodeNum *bNodeFlt; \ + \ + /* aSUBSCR is a number */ \ + assert(TREE_KEY_SUBSCR_IS_CANONICAL((aSUBSCR)->mvtype)); \ + assert(!MVTYPE_IS_INT((aSUBSCR)->mvtype)); \ + b_mvtype = (bNODE)->key_mvtype; \ + assert(!TREE_KEY_SUBSCR_IS_CANONICAL(b_mvtype) \ + || (MVTYPE_IS_NUMERIC(b_mvtype) && !MVTYPE_IS_NUM_APPROX(b_mvtype))); \ + assert(MVTYPE_IS_NUMERIC((aSUBSCR)->mvtype) && !MVTYPE_IS_NUM_APPROX((aSUBSCR)->mvtype)); \ + if (TREE_KEY_SUBSCR_IS_CANONICAL(b_mvtype)) \ + { /* Both aSUBSCR and bNODE are numbers */ \ + b_mvtype &= MV_INT; \ + bNodeFlt = (lvTreeNodeNum *)(bNODE); \ + if (b_mvtype && !bNodeFlt->key_flags.key_bits.key_iconv) \ + { \ + lvAvlTreeNodeFltConv(bNodeFlt); \ + assert(bNodeFlt->key_flags.key_bits.key_iconv); \ + } \ + a_sgn = (0 == (aSUBSCR)->sgn) ? 1 : -1; /* 1 if positive, -1 if negative */ \ + b_sgn = (0 == bNodeFlt->key_flags.key_bits.key_sgn) ? 1 : -1; \ + /* Check sign. If different we are done right there */ \ + if (a_sgn != b_sgn) \ + (retVAL) = a_sgn; \ + else \ + { /* Signs equal; compare exponents for magnitude and adjust sense depending on sign. */ \ + exp_diff = (aSUBSCR)->e - bNodeFlt->key_flags.key_bits.key_e; \ + if (exp_diff) \ + (retVAL) = (exp_diff * a_sgn); \ + else \ + { /* Signs and exponents equal; compare magnitudes. */ \ + /* First, compare high-order 9 digits of magnitude. */ \ + a_m1 = (aSUBSCR)->m[1]; \ + m1_diff = a_m1 - bNodeFlt->key_m1; \ + if (m1_diff) \ + (retVAL) = (m1_diff * a_sgn); \ + else \ + { /* High-order 9 digits equal; if not zero, compare low-order 9 digits. */ \ + if (0 == a_m1) /* zero special case */ \ + (retVAL) = 0; \ + else \ + { \ + b_m0 = (b_mvtype ? 0 : bNodeFlt->key_m0); \ + m0_diff = (aSUBSCR)->m[0] - b_m0; \ + if (m0_diff) \ + (retVAL) = (m0_diff * a_sgn); \ + else \ + { /* Signs, exponents, high-order & low-order magnitudes equal */ \ + (retVAL) = 0; \ + } \ + } \ + } \ + } \ + } \ + } else \ + (retVAL) = -1; /* aSUBSCR is a number, but bNODE is a string */ \ +} + +#define LV_AVL_TREE_STRKEY_CMP(aSUBSCR, aSUBSCR_ADDR, aSUBSCR_LEN, bNODE, retVAL) \ +{ \ + /* aSUBSCR is a string */ \ + assert(!TREE_KEY_SUBSCR_IS_CANONICAL((aSUBSCR)->mvtype)); \ + assert(MVTYPE_IS_STRING((aSUBSCR)->mvtype)); \ + if (TREE_KEY_SUBSCR_IS_CANONICAL((bNODE)->key_mvtype)) \ + (retVAL) = 1; /* aSUBSCR is a string, but bNODE is a number */ \ + else /* aSUBSCR and bNODE are both strings */ \ + MEMVCMP(aSUBSCR_ADDR, aSUBSCR_LEN, (bNODE)->key_addr, (bNODE)->key_len, (retVAL)); \ +} + +#ifdef DEBUG +/* This function is currently DEBUG-only because no one is supposed to be calling it. + * Callers are usually performance sensitive and should therefore use the LV_AVL_TREE_NUMKEY_CMP + * or LV_AVL_TREE_STRKEY_CMP macros that this function in turn invokes. + */ +int lvAvlTreeKeySubscrCmp(treeKeySubscr *aSubscr, lvTreeNode *bNode) +{ + int retVal, a_mvtype; + + a_mvtype = aSubscr->mvtype; + if (TREE_KEY_SUBSCR_IS_CANONICAL(a_mvtype)) + { + if (MVTYPE_IS_INT(a_mvtype)) + { + LV_AVL_TREE_INTKEY_CMP(aSubscr, aSubscr->m[1], bNode, retVal); + } else + LV_AVL_TREE_NUMKEY_CMP(aSubscr, bNode, retVal); + } else + LV_AVL_TREE_STRKEY_CMP(aSubscr, aSubscr->str.addr, aSubscr->str.len, bNode, retVal); + return retVal; +} + +/* This function is currently coded not as efficiently as it needs to be because it is only invoked by dbg code. + * Hence the definition inside a #ifdef DEBUG. If this change and pro code needs it, the below code needs to be revisited. + */ +int lvAvlTreeNodeSubscrCmp(lvTreeNode *aNode, lvTreeNode *bNode) +{ + int a_mvtype, b_mvtype, retVal; + int a_sgn, b_sgn, exp_diff, m1_diff, m0_diff, a_m1, b_m1, a_m0, b_m0; + lvTreeNodeNum *aNodeFlt, *bNodeFlt; + + a_mvtype = aNode->key_mvtype; + b_mvtype = bNode->key_mvtype; + if (TREE_KEY_SUBSCR_IS_CANONICAL(a_mvtype)) + { /* aSubscr is a number */ + assert(!MVTYPE_IS_STRING(a_mvtype)); + if (TREE_KEY_SUBSCR_IS_CANONICAL(b_mvtype)) + { /* Both aNode and bNode are numbers */ + assert(!MVTYPE_IS_STRING(b_mvtype)); + aNodeFlt = (lvTreeNodeNum *)aNode; + bNodeFlt = (lvTreeNodeNum *)bNode; + a_mvtype = a_mvtype & MV_INT; + if (a_mvtype & b_mvtype) + { /* Both aNode & bNode are integers */ + a_m1 = aNodeFlt->key_m0; + b_m1 = bNodeFlt->key_m0; + return (a_m1 - b_m1); + } + if (a_mvtype) + { + if (!aNodeFlt->key_flags.key_bits.key_iconv) + lvAvlTreeNodeFltConv(aNodeFlt); + assert(aNodeFlt->key_flags.key_bits.key_iconv); + } else if (b_mvtype = (MV_INT & b_mvtype)) + { + if (!bNodeFlt->key_flags.key_bits.key_iconv) + lvAvlTreeNodeFltConv(bNodeFlt); + assert(bNodeFlt->key_flags.key_bits.key_iconv); + } + a_sgn = (0 == aNodeFlt->key_flags.key_bits.key_sgn) ? 1 : -1; /* 1 if positive, -1 if negative */ + b_sgn = (0 == bNodeFlt->key_flags.key_bits.key_sgn) ? 1 : -1; + /* Check sign. If different we are done right there */ + if (a_sgn != b_sgn) + return a_sgn; + /* Signs equal; compare exponents for magnitude and adjust sense depending on sign. */ + exp_diff = aNodeFlt->key_flags.key_bits.key_e - bNodeFlt->key_flags.key_bits.key_e; + if (exp_diff) + return (exp_diff * a_sgn); + /* Signs and exponents equal; compare magnitudes. */ + /* First, compare high-order 9 digits of magnitude. */ + a_m1 = aNodeFlt->key_m1; + m1_diff = a_m1 - bNodeFlt->key_m1; + if (m1_diff) + return (m1_diff * a_sgn); + /* High-order 9 digits equal; if not zero, compare low-order 9 digits. */ + if (0 == a_m1) /* zero special case */ + return 0; + /* If key_iconv is TRUE, key_m0 is not lower-order 9 digits but instead is MV_INT representation */ + a_m0 = (a_mvtype && aNodeFlt->key_flags.key_bits.key_iconv) ? 0 : aNodeFlt->key_m0; + b_m0 = (b_mvtype && bNodeFlt->key_flags.key_bits.key_iconv) ? 0 : bNodeFlt->key_m0; + m0_diff = a_m0 - b_m0; + if (m0_diff) + return (m0_diff * a_sgn); + /* Signs, exponents, high-order magnitudes, and low-order magnitudes equal. */ + return 0; + } + /* aNode is a number, but bNode is a string */ + return(-1); + } + /* aNode is a string */ + assert(MVTYPE_IS_STRING(a_mvtype)); + if (TREE_KEY_SUBSCR_IS_CANONICAL(b_mvtype)) + { + assert(!MVTYPE_IS_STRING(b_mvtype)); + return(1); /* aNode is a string, but bNode is a number */ + } + /* aNode and bNode are both strings */ + assert(MVTYPE_IS_STRING(b_mvtype)); + MEMVCMP(aNode->key_addr, aNode->key_len, bNode->key_addr, bNode->key_len, retVal); + return retVal; +} +#endif + +/* Return first in-order traversal node (also smallest collating key) in avl tree. Returns NULL if NO key in tree */ +lvTreeNode *lvAvlTreeFirst(lvTree *lvt) +{ + lvTreeNode *avl_first, *next; + + next = lvt->avl_root; + if (NULL == next) + return NULL; + do + { + avl_first = next; + next = next->avl_left; + } while (NULL != next); + return avl_first; +} + +/* Return last (highest collating) key in avl tree. Returns NULL if NO key in tree */ +lvTreeNode *lvAvlTreeLast(lvTree *lvt) +{ + lvTreeNode *avl_last, *next; + + next = lvt->avl_root; + if (NULL == next) + return NULL; + do + { + avl_last = next; + next = next->avl_right; + } while (NULL != next); + return avl_last; +} + +/* Returns the in-order predecessor of the input "node". + * Assumes input is in the avl tree and operates within the avl tree only. + */ +lvTreeNode *lvAvlTreePrev(lvTreeNode *node) +{ + lvTreeNode *prev, *tmp, *parent; + + assert(NULL != node); + prev = node->avl_left; + if (NULL != prev) + { /* in-order predecessor is BELOW "node" */ + tmp = prev->avl_right; + while (NULL != tmp) + { + prev = tmp; + tmp = prev->avl_right; + } + } else + { /* in-order predecessor is an ancestor of "node" or could be "" (because "node" is the BIGGEST key in tree) */ + parent = node->avl_parent; + tmp = node; + while (NULL != parent) + { + if (parent->avl_right == tmp) + { + prev = parent; + break; + } + assert(parent->avl_left == tmp); + tmp = parent; + parent = tmp->avl_parent; + } + } + return prev; +} + +/* Return "node" in AVL tree IMMEDIATELY BEFORE input "key" */ +lvTreeNode *lvAvlTreeKeyPrev(lvTree *lvt, treeKeySubscr *key) +{ + lvTreeNode *node, *tmp, *prev, *parent; + + node = lvAvlTreeLookup(lvt, key, &parent); + if (NULL != node) /* most common case */ + { + prev = lvAvlTreePrev(node); + assert((NULL == prev) || (0 < lvAvlTreeKeySubscrCmp(key, prev))); + } else + { + assert(NULL != parent); /* lvAvlTreeLookup should have initialized this */ + if (TREE_DESCEND_LEFT == parent->descent_dir) + { + assert(NULL == parent->avl_left); + assert(0 > lvAvlTreeKeySubscrCmp(key, parent)); + prev = lvAvlTreePrev(parent); + assert((NULL == prev) || (0 < lvAvlTreeKeySubscrCmp(key, prev))); + } else + { + assert(NULL == parent->avl_right); + assert(0 < lvAvlTreeKeySubscrCmp(key, parent)); + DEBUG_ONLY(tmp = lvAvlTreeNext(parent);) + assert((NULL == tmp) || (0 > lvAvlTreeKeySubscrCmp(key, tmp))); + prev = parent; + } + } + return prev; +} + +/* Returns the in-order successor of the input "node". + * Assumes input is in the avl tree and operates within the avl tree. + */ +lvTreeNode *lvAvlTreeNext(lvTreeNode *node) +{ + lvTreeNode *next, *tmp, *parent; + DEBUG_ONLY(lvTree *lvt;) + + assert(NULL != node); + next = node->avl_right; + if (NULL != next) + { /* in-order successor is BELOW "node" */ + tmp = next->avl_left; + while (NULL != tmp) + { + next = tmp; + tmp = next->avl_left; + } + } else + { /* in-order successor is an ancestor of "node" or could be "" (because "node" is the BIGGEST key in tree) */ + parent = node->avl_parent; + tmp = node; + while (NULL != parent) + { + if (parent->avl_left == tmp) + { + next = parent; + break; + } + assert(parent->avl_right == tmp); + tmp = parent; + parent = tmp->avl_parent; + } + } + assert((NULL == next) || (node == lvAvlTreePrev(next))); + return next; +} + +/* Return "node" in AVL tree IMMEDIATELY AFTER input "key" */ +lvTreeNode *lvAvlTreeKeyNext(lvTree *lvt, treeKeySubscr *key) +{ + lvTreeNode *node, *tmp, *next, *parent; + + node = lvAvlTreeLookup(lvt, key, &parent); + if (NULL != node) /* most common case */ + { + next = lvAvlTreeNext(node); + assert((NULL == next) || (0 > lvAvlTreeKeySubscrCmp(key, next))); + } else + { + assert(NULL != parent); /* lvAvlTreeLookup should have initialized this */ + if (TREE_DESCEND_RIGHT == parent->descent_dir) + { + assert(NULL == parent->avl_right); + assert(0 < lvAvlTreeKeySubscrCmp(key, parent)); + next = lvAvlTreeNext(parent); + assert((NULL == next) || (0 > lvAvlTreeKeySubscrCmp(key, next))); + } else + { + assert(NULL == parent->avl_left); + assert(0 > lvAvlTreeKeySubscrCmp(key, parent)); + DEBUG_ONLY(tmp = lvAvlTreePrev(parent);) + assert((NULL == tmp) || (0 < lvAvlTreeKeySubscrCmp(key, tmp))); + next = parent; + } + } + return next; +} + +/* Return first post-order traversal node in avl tree. Returns NULL if NO key in tree */ +lvTreeNode *lvAvlTreeFirstPostOrder(lvTree *lvt) +{ + lvTreeNode *first, *tmp; + + first = lvt->avl_root; + if (NULL == first) + return NULL; + /* Look for first post-order traversal node under left subtree if any. If not look under right sub-tree. + * If neither is present, then this node is it. + */ + do + { + if ((NULL != (tmp = first->avl_left)) || (NULL != (tmp = first->avl_right))) + first = tmp; /* need to go further down the tree to find post-order successor */ + else + break; /* found leaf-level post-order successor */ + } while (TRUE); + return first; +} + +/* Returns the post-order successor of the input "node". Assumes input is in the avl tree and operates within the avl tree. */ +lvTreeNode *lvAvlTreeNextPostOrder(lvTreeNode *node) +{ + lvTreeNode *next, *tmp, *parent; + + assert(NULL != node); + parent = node->avl_parent; + if (NULL == parent) /* Reached root node of the AVL tree in the post order traversal */ + return NULL; /* Done with post-order traversal */ + if (node == parent->avl_right) /* done with "node" and its subtree so post-order successor is "parent" */ + return parent; + assert(node == parent->avl_left); + /* Post-order successor will be in the right subtree of "parent" */ + next = parent->avl_right; + if (NULL == next) + return parent; /* No right subtree so "parent" it is */ + /* Find post-order successor under the right subtree. Use logic similar to "lvAvlTreeFirstPostOrder" */ + do + { + if ((NULL != (tmp = next->avl_left)) || (NULL != (tmp = next->avl_right))) + next = tmp; /* need to go further down the tree to find post-order successor */ + else + break; /* found leaf-level post-order successor */ + } while (TRUE); + return next; +} + +/* Returns the collated successor of the input "key" taking into account the currently effective null collation scheme */ +lvTreeNode *lvAvlTreeKeyCollatedNext(lvTree *lvt, treeKeySubscr *key) +{ + lvTreeNode *node; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + if (TREF(local_collseq_stdnull)) + { + if (MV_IS_STRING(key) && (0 == key->str.len)) + { /* want the subscript AFTER the null subscript. That is the FIRST node in the tree + * unless the first one happens to also be the null subscript. In that case, we need + * to hop over that and get the next node in the tree. + */ + node = lvAvlTreeFirst(lvt); + } else + { /* want the subscript AFTER a numeric or string subscript. It could end up resulting + * in the null subscript in case "key" is the highest numeric subscript in the tree. + * In that case, hop over the null subscript as that comes first in the collation order + * even though it comes in between numbers and strings in the tree node storage order. + */ + node = lvAvlTreeKeyNext(lvt, key); + } + /* If "node" holds the NULL subscript, then hop over to the next one. */ + if ((NULL != node) && MVTYPE_IS_STRING(node->key_mvtype) && (0 == node->key_len)) + node = lvAvlTreeNext(node); /* Need to hop over the null subscript */ + } else + node = lvAvlTreeKeyNext(lvt, key); + return node; +} + +/* Returns the collated successor of the input "node" taking into account the currently effective null collation scheme */ +lvTreeNode *lvAvlTreeNodeCollatedNext(lvTreeNode *node) +{ + boolean_t get_next; + lvTree *lvt; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + if (TREF(local_collseq_stdnull)) + { /* If standard null collation, then null subscript needs special handling. + * + * If current subscript is a null subscript, the next subscript in collation order could + * very well be the first numeric (canonical) subscript in the avl tree (if one exists). + * If not the next subscript would be the first non-null string subscript in the tree. + * + * If current subscript is NOT a null subscript, it is possible we encounter the null + * subscript as the next subscript in collation order. In that case, we need to hop over + * it as that collates before the current numeric subscript. + */ + if ((NULL != node) && MVTYPE_IS_STRING(node->key_mvtype) && (0 == node->key_len)) + { + lvt = LV_GET_PARENT_TREE(node); + node = lvAvlTreeFirst(lvt); + assert(NULL != node); + } else + node = lvAvlTreeNext(node); + get_next = ((NULL != node) && MVTYPE_IS_STRING(node->key_mvtype) && (0 == node->key_len)); + } else + get_next = TRUE; + if (get_next) + node = lvAvlTreeNext(node); + return node; +} + +/* Function to clone an avl tree (used by the LV_TREE_CLONE macro). Uses recursion to descend the tree. */ +lvTreeNode *lvAvlTreeCloneSubTree(lvTreeNode *node, lvTree *lvt, lvTreeNode *avl_parent) +{ + lvTreeNodeVal *dupVal; + lvTreeNode *cloneNode, *left, *right; + lvTreeNode *leftSubTree, *rightSubTree; + lvTree *lvt_child; + lv_val *base_lv; + + assert(NULL != node); + cloneNode = lvtreenode_getslot(LVT_GET_SYMVAL(lvt)); + /* The following is optimized to do the initialization of just the needed structure members. For that it assumes a + * particular "lvTreeNode" structure layout. The assumed layout is asserted so any changes to the layout will + * automatically show an issue here and cause the below initialization to be accordingly reworked. + */ + assert(0 == OFFSETOF(lvTreeNode, v)); + assert(OFFSETOF(lvTreeNode, v) + SIZEOF(cloneNode->v) == OFFSETOF(lvTreeNode, sbs_child)); + assert(OFFSETOF(lvTreeNode, sbs_child) + SIZEOF(cloneNode->sbs_child) == OFFSETOF(lvTreeNode, tree_parent)); + assert(OFFSETOF(lvTreeNode, tree_parent) + SIZEOF(cloneNode->tree_parent) == OFFSETOF(lvTreeNode, key_mvtype)); + assert(OFFSETOF(lvTreeNode, key_mvtype) + SIZEOF(cloneNode->key_mvtype) == OFFSETOF(lvTreeNode, balance)); + assert(OFFSETOF(lvTreeNode, balance) + SIZEOF(cloneNode->balance) == OFFSETOF(lvTreeNode, descent_dir)); + assert(OFFSETOF(lvTreeNode, descent_dir) + SIZEOF(cloneNode->descent_dir) == OFFSETOF(lvTreeNode, key_len)); + assert(OFFSETOF(lvTreeNode, key_len) + SIZEOF(cloneNode->key_len) == OFFSETOF(lvTreeNode, key_addr)); + assert(OFFSETOF(lvTreeNode, key_mvtype) + 8 == OFFSETOF(lvTreeNode, key_addr)); + GTM64_ONLY(assert(OFFSETOF(lvTreeNode, key_addr) + SIZEOF(cloneNode->key_addr) == OFFSETOF(lvTreeNode, avl_left));) + NON_GTM64_ONLY( + assert(OFFSETOF(lvTreeNode, key_addr) + SIZEOF(cloneNode->key_addr) == OFFSETOF(lvTreeNode, filler_8byte)); + assert(OFFSETOF(lvTreeNode, filler_8byte) + SIZEOF(cloneNode->filler_8byte) == OFFSETOF(lvTreeNode, avl_left)); + ) + assert(OFFSETOF(lvTreeNode, avl_left) + SIZEOF(cloneNode->avl_left) == OFFSETOF(lvTreeNode, avl_right)); + assert(OFFSETOF(lvTreeNode, avl_right) + SIZEOF(cloneNode->avl_right) == OFFSETOF(lvTreeNode, avl_parent)); + assert(OFFSETOF(lvTreeNode, avl_parent) + SIZEOF(cloneNode->avl_parent) == SIZEOF(lvTreeNode)); + cloneNode->v = node->v; + /* "cloneNode->sbs_child" initialized later */ + cloneNode->tree_parent = lvt; + /* cloneNode->key_mvtype/balance/descent_dir/key_len all initialized in one shot. + * Note: We use "qw_num *" instead of "uint8 *" below because the former works on 32-bit platforms too. + */ + GTM64_ONLY(assert(IS_PTR_8BYTE_ALIGNED(&cloneNode->key_mvtype));) + NON_GTM64_ONLY(assert(IS_PTR_4BYTE_ALIGNED(&cloneNode->key_mvtype));) + GTM64_ONLY(assert(IS_PTR_8BYTE_ALIGNED(&node->key_mvtype));) + NON_GTM64_ONLY(assert(IS_PTR_4BYTE_ALIGNED(&node->key_mvtype));) + *(RECAST(qw_num *)&cloneNode->key_mvtype) = *(RECAST(qw_num *)&node->key_mvtype); + cloneNode->key_addr = node->key_addr; + NON_GTM64_ONLY( + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNode, filler_8byte, lvTreeNodeNum, key_m1)); + ((lvTreeNodeNum *)cloneNode)->key_m1 = ((lvTreeNodeNum *)node)->key_m1; + ) + cloneNode->avl_parent = avl_parent; + lvt_child = node->sbs_child; + base_lv = lvt->base_lv; + if (NULL != lvt_child) + { + LV_TREE_CLONE(lvt_child, cloneNode, base_lv); /* initializes "cloneNode->sbs_child" */ + } else + cloneNode->sbs_child = NULL; + left = node->avl_left; + leftSubTree = (NULL != left) ? lvAvlTreeCloneSubTree(left, lvt, cloneNode) : NULL; + cloneNode->avl_left = leftSubTree; + right = node->avl_right; + rightSubTree = (NULL != right) ? lvAvlTreeCloneSubTree(right, lvt, cloneNode) : NULL; + cloneNode->avl_right = rightSubTree; + return cloneNode; +} + +#ifdef DEBUG +/* Function to check integrity of lv key subscript. Currently tests the MV_CANONICAL bit is in sync with the key type */ +STATICFNDEF boolean_t lvAvlTreeLookupKeyCheck(treeKeySubscr *key) +{ + mval dummy_mval; + boolean_t is_canonical; + + /* Ensure "key->mvtype" comes in with the MV_CANONICAL bit set if applicable */ + dummy_mval = *key; + is_canonical = MV_IS_CANONICAL(&dummy_mval); + if (is_canonical) + TREE_KEY_SUBSCR_SET_MV_CANONICAL_BIT(&dummy_mval); + else + TREE_KEY_SUBSCR_RESET_MV_CANONICAL_BIT(&dummy_mval); + assert(TREE_KEY_SUBSCR_IS_CANONICAL(key->mvtype) == TREE_KEY_SUBSCR_IS_CANONICAL(dummy_mval.mvtype)); + assert(TREE_KEY_SUBSCR_IS_CANONICAL(key->mvtype) || MV_IS_STRING(key)); + assert(!TREE_KEY_SUBSCR_IS_CANONICAL(key->mvtype) || MV_IS_NUMERIC(key)); + return TRUE; +} + +boolean_t lvTreeIsWellFormed(lvTree *lvt) +{ + lvTreeNode *avl_root, *tmpNode, *minNode, *maxNode, *sbs_parent, *avl_node, *node; + treeSrchStatus *lastLookup; + lvTree *tmplvt; + int nm_node_cnt, sbs_depth; + lv_val *base_lv; + + if (NULL != lvt) + { + /* Check lvt->ident */ + assert(MV_LV_TREE == lvt->ident); + /* Check lvt->sbs_parent */ + sbs_parent = LVT_PARENT(lvt); + assert(NULL != sbs_parent); + assert(sbs_parent->sbs_child == lvt); + /* Check lvt->sbs_depth */ + sbs_depth = 1; + while (!LV_IS_BASE_VAR(sbs_parent)) + { + tmplvt = LV_GET_PARENT_TREE(sbs_parent); + sbs_parent = tmplvt->sbs_parent; + sbs_depth++; + } + assert(sbs_depth == lvt->sbs_depth); + /* Check lvt->base_lv */ + assert((lv_val *)sbs_parent == lvt->base_lv); + /* Note: lvt->avl_height is checked in lvAvlTreeNodeIsWellFormed */ + /* Check lvt->avl_root */ + if (NULL != (avl_root = lvt->avl_root)) + { + /* Check lvt->lastLookup clue fields */ + lastLookup = &lvt->lastLookup; + if (NULL != (tmpNode = lastLookup->lastNodeLookedUp)) + { + minNode = lastLookup->lastNodeMin; + if (NULL != minNode) + assert(0 > lvAvlTreeNodeSubscrCmp(minNode, tmpNode)); + maxNode = lastLookup->lastNodeMax; + if (NULL != maxNode) + assert(0 < lvAvlTreeNodeSubscrCmp(maxNode, tmpNode)); + } + lvAvlTreeNodeIsWellFormed(lvt, avl_root); + } + } + return TRUE; +} + +STATICFNDEF boolean_t lvAvlTreeNodeIsWellFormed(lvTree *lvt, lvTreeNode *node) +{ + int leftSubTreeHeight, rightSubTreeHeight; + boolean_t leftWellFormed, rightWellFormed; + lvTreeNode *left, *right; + lvTree *sbs_child; + + if (NULL == node) + return TRUE; + assert(node->tree_parent == lvt); + left = node->avl_left; + assert((NULL == left) || (left->avl_parent == node)); + right = node->avl_right; + assert((NULL == right) || (right->avl_parent == node)); + leftWellFormed = ((NULL == left) || (0 > lvAvlTreeNodeSubscrCmp(left, node))); + assert(leftWellFormed); + rightWellFormed = ((NULL == right) || (0 < lvAvlTreeNodeSubscrCmp(right, node))); + assert(rightWellFormed); + leftSubTreeHeight = lvAvlTreeNodeHeight(left); + rightSubTreeHeight = lvAvlTreeNodeHeight(right); + if (rightSubTreeHeight > leftSubTreeHeight) + assert(node->balance == TREE_RIGHT_HEAVY); + else if (rightSubTreeHeight == leftSubTreeHeight) + assert(node->balance == TREE_BALANCED); + else + assert(node->balance == TREE_LEFT_HEAVY); + if (node == node->tree_parent->avl_root) + assert(node->tree_parent->avl_height == MAX(leftSubTreeHeight, rightSubTreeHeight) + 1); + assert(lvAvlTreeNodeIsWellFormed(lvt, left)); + assert(lvAvlTreeNodeIsWellFormed(lvt, right)); + sbs_child = node->sbs_child; + if (NULL != sbs_child) + assert(lvTreeIsWellFormed(sbs_child)); + return TRUE; +} + +STATICFNDEF int lvAvlTreeNodeHeight(lvTreeNode *node) +{ + int leftSubTreeHeight, rightSubTreeHeight; + + if (NULL != node) + { + leftSubTreeHeight = lvAvlTreeNodeHeight(node->avl_left); + rightSubTreeHeight = lvAvlTreeNodeHeight(node->avl_right); + + return (MAX(leftSubTreeHeight, rightSubTreeHeight) + 1); + } + return 0; +} + +void assert_tree_member_offsets(void) +{ + STATICDEF boolean_t first_tree_create = TRUE; + + if (first_tree_create) + { + /* This is the FIRST "lvTree" that this process is creating. Do a few per-process one-time assertions first. */ + assert(0 == TREE_LEFT_HEAVY); /* tree_balance_invert array definition depends on this */ + assert(1 == TREE_RIGHT_HEAVY); /* tree_balance_invert array definition depends on this */ + assert(2 == TREE_BALANCED); /* tree_balance_invert array definition depends on this */ + assert(TREE_DESCEND_LEFT == TREE_LEFT_HEAVY); /* they are interchangeably used for performance reasons */ + assert(TREE_DESCEND_RIGHT == TREE_RIGHT_HEAVY); /* they are interchangeably used for performance reasons */ + + /* A lot of the lv functions are passed in an (lv_val *) which could be a (lvTreeNode *) as well. + * For example, in op_kill.c, if "kill a" is done, an "lv_val *" (corresponding to the base local variable "a") + * is passed whereas if "kill a(1,2)" is done, a "lvTreeNode *" (corresponding to the subscripted node "a(1,2)") + * is passed. From the input pointer, we need to determine in op_kill.c if it is a "lv_val *" or "lvTreeNode *". + * Assuming "curr_lv" is the input pointer of type "lv_val *", to find out if it is a pointer to an lv_val or + * lvTreeNode, one checks the ident of the parent. If it is MV_SYM, it means the pointer is to an lv_val (base + * variable) and if not it is a lvTreeNode pointer. The code will look like the below. + * + * if (MV_SYM == curr_lv->ptrs.val_ent.parent.sym->ident) + * { // curr_lv is a base var pointing to an "lv_val" structure + * ... + * } else + * { // curr_lv points to a lvTreeNode structure whose parent.sym field points to a "lvTree" structure + * assert(MV_LV_TREE == curr_lv->ptrs.val_ent.parent.sym->ident); + * ... + * } + * + * Note : The MV_SYM == check above has now been folded into a IS_LV_VAL_PTR macro in lv_val.h + * + * In order for the above check to work, one needs to ensure that that an lv_val.ptrs.val_ent.parent.sym + * is at the same offset and size as a lvTreeNode.tree_parent and that a symval.ident is at the same offset and + * size as a lvTree.ident. + * + * Similarly, in op_fndata.c we could be passed in a base lv_val * or a subscripted lv_val (i.e. lvTreeNode *) + * and want to find out if this lv_val has any children. To do this we would normally need to check + * if the input is an lv_val or a lvTreeNode pointer and access curr_lv->ptrs.val_ent.children or + * curr_lv->sbs_child respectively. To avoid this additional if checks, we ensure that both are at the + * same offset and have the same size. + * + * We therefore ensure that all fields below in the same line should be at same structure offset + * AND have same size as each other. Note: all of this might not be necessary in current code but + * might be useful in the future. Since it is easily possible for us to ensure this, we do so right now. + * + * lvTreeNode.tree_parent == lv_val.ptrs.val_ent.parent.sym + * lvTreeNode.sbs_child == lv_val.ptrs.val_ent.children + * lvTree.ident == symval.ident == lv_val.v.mvtype + * lvTree.sbs_depth == symval.sbs_depth + * lvTreeNode.v == lv_val.v + */ + /* lvTreeNode.tree_parent == lv_val.ptrs.val_ent.parent.sym */ + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNode, tree_parent, lv_val, ptrs.val_ent.parent.sym)); + /* lvTreeNode.sbs_child == lv_val.ptrs.val_ent.children */ + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNode, sbs_child, lv_val, ptrs.val_ent.children)); + /* lvTree.ident == symval.ident == lv_val.v.mvtype */ + assert(IS_OFFSET_AND_SIZE_MATCH(lvTree, ident, symval, ident)); + assert(IS_OFFSET_AND_SIZE_MATCH(lvTree, ident, lv_val, v.mvtype)); + /* In order to get OFFSETOF & SIZEOF to work on v.mvtype, the mval field "mvtype" was changed from + * being a 16-bit "unsigned int" type bitfield to a "unsigned short". While this should not affect the + * size of the "mvtype" fields, we fear it might affect the size of the immediately following fields + * "sgn" (1-bit), "e" (7-bit) and "fnpc_index" (8-bit). While all of them together occupy 16-bits, they + * have an "unsigned int" as the type specifier. In order to ensure the compiler does not allocate 4-bytes + * (because of the int specification) to those 3 bitfields (and actually use only 2-bytes of those) and + * create a 2-byte filler space, we assert that the offset of the immediately following non-bitfield (which + * is "m[2]" in Unix & "str" in VMS) in the mval is 4-bytes. If the compiler had allocated 4-bytes, then this + * offset would have been 8-bytes instead and the assert will fail alerting us of the unnecessary mval size bloat. + */ + UNIX_ONLY(assert(4 == OFFSETOF(mval, m[0]));) + VMS_ONLY(assert(4 == OFFSETOF(mval, str));) + /* lvTree.sbs_depth == symval.sbs_depth */ + assert(IS_OFFSET_AND_SIZE_MATCH(lvTree, sbs_depth, symval, sbs_depth)); + /* lvTreeNode.v == lv_val.v */ + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNode, v, lv_val, v)); + /* Assert that lvTreeNodeNum and lvTreeNodeStr structures have ALMOST the same layout. + * This makes them interchangeably usable with care on the non-intersecting fields. + */ + assert(SIZEOF(lvTreeNodeNum) == SIZEOF(lvTreeNodeStr)); + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNodeNum, v, lvTreeNodeStr, v)); + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNodeNum, sbs_child, lvTreeNodeStr, sbs_child)); + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNodeNum, tree_parent, lvTreeNodeStr, tree_parent)); + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNodeNum, key_mvtype, lvTreeNodeStr, key_mvtype)); + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNodeNum, balance, lvTreeNodeStr, balance)); + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNodeNum, descent_dir, lvTreeNodeStr, descent_dir)); + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNodeNum, avl_left, lvTreeNodeStr, avl_left)); + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNodeNum, avl_right, lvTreeNodeStr, avl_right)); + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNodeNum, avl_parent, lvTreeNodeStr, avl_parent)); + /* Assert that lvTreeNodeStr and lvTreeNode structures have EXACTLY the same layout. + * This makes them interchangeably usable. + */ + assert(SIZEOF(lvTreeNodeStr) == SIZEOF(lvTreeNode)); + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNodeStr, v, lvTreeNode, v)); + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNodeStr, sbs_child, lvTreeNode, sbs_child)); + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNodeStr, tree_parent, lvTreeNode, tree_parent)); + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNodeStr, key_mvtype, lvTreeNode, key_mvtype)); + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNodeStr, balance, lvTreeNode, balance)); + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNodeStr, descent_dir, lvTreeNode, descent_dir)); + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNodeStr, key_len, lvTreeNode, key_len)); + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNodeStr, key_addr, lvTreeNode, key_addr)); + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNodeStr, avl_left, lvTreeNode, avl_left)); + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNodeStr, avl_right, lvTreeNode, avl_right)); + assert(IS_OFFSET_AND_SIZE_MATCH(lvTreeNodeStr, avl_parent, lvTreeNode, avl_parent)); + first_tree_create = FALSE; + } +} +#endif + +/* In order to lookup a key in the AVL tree, there are THREE functions. One for each different keytype (integer, number, string). + * lvAvlTreeLookupInt (look up an integer subscript) + * lvAvlTreeLookupNum (look up a non-integer numeric subscript) + * lvAvlTreeLookupStr (look up a string subscript) + * The functions share a lot of code but have slightly different codepaths. Since these functions are frequently invoked, + * we want to keep their cost to a minimum and having separate functions for each keytype lets us minimize the # of if checks + * in them which is why we do it this way even if it means a lot of duplication. + * + * The lookup first attempts to use the clue (if possible) and avoid a full tree traversal. + * If not easily possible, we do the full tree traversal. As part of that, we update the clue (to help with future lookups). + * As this is an AVL tree, the full tree traversal will take O(log(n)) (i.e. logarithmic) time. + */ +lvTreeNode *lvAvlTreeLookupInt(lvTree *lvt, treeKeySubscr *key, lvTreeNode **lookupParent) +{ + int cmp; + int4 key_m1; + lvTreeNode *node, *nextNode, *lastNodeMin, *lastNodeMax, *minNode, *maxNode, **nodePtr; + lvTreeNode *parent, *tmpNode; + treeSrchStatus *lastLookup; + + assert(lvAvlTreeLookupKeyCheck(key)); + assert(TREE_KEY_SUBSCR_IS_CANONICAL(key->mvtype)); + assert(MVTYPE_IS_INT(key->mvtype)); + assert(NULL != lookupParent); + TREE_DEBUG_ONLY(assert(lvTreeIsWellFormed(lvt));) + key_m1 = key->m[1]; + /* First see if node can be looked up easily from the lastLookup clue (without a tree traversal) */ + lastLookup = &lvt->lastLookup; + if (NULL != (tmpNode = lastLookup->lastNodeLookedUp)) + { + assert(NULL != lvt->avl_root); /* there better be an AVL tree if we have a non-zero clue */ + LV_AVL_TREE_INTKEY_CMP(key, key_m1, tmpNode, cmp); + if (0 == cmp) + { /* Input key matches last used clue. Return right away with a match. */ + node = tmpNode; + /* "parent" or "parent->descent_dir" need not be set since node is non-NULL */ + return node; + } else if (0 < cmp) + { /* Input key is GREATER than last used clue. Check RIGHT subtree of clue node to see if input key + * could possibly be found there. + */ + if (NULL == tmpNode->avl_right) + { /* No subtree to the right of the "clue" node. + * If input key is LESSER than the maximum key that can be found under the "clue" node, + * then we know for sure input key can not be found anywhere else in the tree. + * If input key is GREATER than the maximum key then it definitely is not under "clue" + * but could be somewhere else in the tree. We choose to do a fresh traversal from the top. + * If input key is EQUAL to the maximum key then return right away with a match. + */ + maxNode = lastLookup->lastNodeMax; + if (NULL != maxNode) + { + assert(0 < lvAvlTreeNodeSubscrCmp(maxNode, tmpNode)); + LV_AVL_TREE_INTKEY_CMP(key, key_m1, maxNode, cmp); + if (0 == cmp) + { /* input key is EQUAL to the maximum possible key under "clue" node */ + node = maxNode; + /* "parent" or "parent->descent_dir" need not be set since node is non-NULL */ + return node; + } else if (0 > cmp) + { /* input key is LESSER than the maximum possible key under "clue" node */ + parent = tmpNode; + parent->descent_dir = TREE_DESCEND_RIGHT; + *lookupParent = parent; + return NULL; + } + } else + { /* Note: maxNode == NULL implies, max key is +INFINITY so treat this case the same way + * as if input key value is LESSER than the maximum possible key under the "clue" node. + */ + parent = tmpNode; + parent->descent_dir = TREE_DESCEND_RIGHT; + *lookupParent = parent; + return NULL; + } + } + } else if (NULL == tmpNode->avl_left) + { /* Input key is LESSER than last used clue AND no subtree to the left of the "clue" node. + * If input key is GREATER than the minimum key that can be found under the "clue" node, + * then we know for sure input key can not be found anywhere else in the tree. + * If input key is LESSER than the minimum key then it definitely is not under "clue" + * but could be somewhere else in the tree. We choose to do a fresh traversal from the top. + * If input key is EQUAL to the minimum key then return right away with a match. + */ + minNode = lastLookup->lastNodeMin; + if (NULL != minNode) + { + assert(0 > lvAvlTreeNodeSubscrCmp(minNode, tmpNode)); + LV_AVL_TREE_INTKEY_CMP(key, key_m1, minNode, cmp); + if (0 == cmp) + { /* input key is EQUAL to the minimum possible key under "clue" node */ + node = minNode; + /* "parent" or "parent->descent_dir" need not be set since node is non-NULL */ + return node; + } else if (0 < cmp) + { /* input key is GREATER than the minimum possible key under "clue" node */ + parent = tmpNode; + parent->descent_dir = TREE_DESCEND_LEFT; + *lookupParent = parent; + return NULL; + } + } else + { /* Note: minNode == NULL implies, min key is -INFINITY so treat this case the same way + * as if input key value is GREATER than the minimum possible key under the "clue" node. + */ + parent = tmpNode; + parent->descent_dir = TREE_DESCEND_LEFT; + *lookupParent = parent; + return NULL; + } + } + } + /* Now that we know "clue" did not help, do a fresh traversal looking for the input key. Maintain clue at the same time */ + parent = lvt->avl_root; + if (NULL == parent) + { + *lookupParent = NULL; + return NULL; + } + node = parent; + lastNodeMin = (lvTreeNode *)NULL; /* the minimum possible key under "clue" is initially -INFINITY */ + lastNodeMax = (lvTreeNode *)NULL; /* the maximum possible key under "clue" is initially +INFINITY */ + if (NULL != node) + { + while (TRUE) + { + LV_AVL_TREE_INTKEY_CMP(key, key_m1, node, cmp); + if (0 == cmp) + { + lvt->lastLookup.lastNodeLookedUp = node; + break; + } else if (cmp < 0) + { + node->descent_dir = TREE_DESCEND_LEFT; + /* if we descend left, we know for sure all-subtree-keys are < node key so update the max key */ + nodePtr = &lastNodeMax; + nextNode = node->avl_left; + } else /* (cmp > 0) */ + { + node->descent_dir = TREE_DESCEND_RIGHT; + /* if we descend right, we know for sure all-subtree-keys are > node-key so update the min key */ + nodePtr = &lastNodeMin; + nextNode = node->avl_right; + } + parent = node; + node = nextNode; + if (NULL == node) + { + lvt->lastLookup.lastNodeLookedUp = parent; + break; + } + *nodePtr = parent; /* the actual max-key or min-key update happens here */ + } + } else + lvt->lastLookup.lastNodeLookedUp = NULL; + *lookupParent = parent; + lvt->lastLookup.lastNodeMin = lastNodeMin; /* now that clue has been set, also set max-key and min-key */ + lvt->lastLookup.lastNodeMax = lastNodeMax; + return node; +} + +/* All comments for "lvAvlTreeLookupInt" function apply here as well */ +lvTreeNode *lvAvlTreeLookupNum(lvTree *lvt, treeKeySubscr *key, lvTreeNode **lookupParent) +{ + int cmp; + lvTreeNode *node, *nextNode, *lastNodeMin, *lastNodeMax, *minNode, *maxNode, **nodePtr; + lvTreeNode *parent, *tmpNode; + treeSrchStatus *lastLookup; + + assert(lvAvlTreeLookupKeyCheck(key)); + assert(TREE_KEY_SUBSCR_IS_CANONICAL(key->mvtype)); + assert(!MVTYPE_IS_INT(key->mvtype)); + assert(NULL != lookupParent); + TREE_DEBUG_ONLY(assert(lvTreeIsWellFormed(lvt));) + /* First see if node can be looked up easily from the lastLookup clue (without a tree traversal) */ + lastLookup = &lvt->lastLookup; + if (NULL != (tmpNode = lastLookup->lastNodeLookedUp)) + { + assert(NULL != lvt->avl_root); /* there better be an AVL tree if we have a non-zero clue */ + LV_AVL_TREE_NUMKEY_CMP(key, tmpNode, cmp); + if (0 == cmp) + { /* Input key matches last used clue. Return right away with a match. */ + node = tmpNode; + /* "parent" or "parent->descent_dir" need not be set since node is non-NULL */ + return node; + } else if (0 < cmp) + { /* Input key is GREATER than last used clue. Check RIGHT subtree of clue node to see if input key + * could possibly be found there. + */ + if (NULL == tmpNode->avl_right) + { /* No subtree to the right of the "clue" node. + * If input key is LESSER than the maximum key that can be found under the "clue" node, + * then we know for sure input key can not be found anywhere else in the tree. + * If input key is GREATER than the maximum key then it definitely is not under "clue" + * but could be somewhere else in the tree. We choose to do a fresh traversal from the top. + * If input key is EQUAL to the maximum key then return right away with a match. + */ + maxNode = lastLookup->lastNodeMax; + if (NULL != maxNode) + { + assert(0 < lvAvlTreeNodeSubscrCmp(maxNode, tmpNode)); + LV_AVL_TREE_NUMKEY_CMP(key, maxNode, cmp); + if (0 == cmp) + { /* input key is EQUAL to the maximum possible key under "clue" node */ + node = maxNode; + /* "parent" or "parent->descent_dir" need not be set since node is non-NULL */ + return node; + } else if (0 > cmp) + { /* input key is LESSER than the maximum possible key under "clue" node */ + parent = tmpNode; + parent->descent_dir = TREE_DESCEND_RIGHT; + *lookupParent = parent; + return NULL; + } + } else + { /* Note: maxNode == NULL implies, max key is +INFINITY so treat this case the same way + * as if input key value is LESSER than the maximum possible key under the "clue" node. + */ + parent = tmpNode; + parent->descent_dir = TREE_DESCEND_RIGHT; + *lookupParent = parent; + return NULL; + } + } + } else if (NULL == tmpNode->avl_left) + { /* Input key is LESSER than last used clue AND no subtree to the left of the "clue" node. + * If input key is GREATER than the minimum key that can be found under the "clue" node, + * then we know for sure input key can not be found anywhere else in the tree. + * If input key is LESSER than the minimum key then it definitely is not under "clue" + * but could be somewhere else in the tree. We choose to do a fresh traversal from the top. + * If input key is EQUAL to the minimum key then return right away with a match. + */ + minNode = lastLookup->lastNodeMin; + if (NULL != minNode) + { + assert(0 > lvAvlTreeNodeSubscrCmp(minNode, tmpNode)); + LV_AVL_TREE_NUMKEY_CMP(key, minNode, cmp); + if (0 == cmp) + { /* input key is EQUAL to the minimum possible key under "clue" node */ + node = minNode; + /* "parent" or "parent->descent_dir" need not be set since node is non-NULL */ + return node; + } else if (0 < cmp) + { /* input key is GREATER than the minimum possible key under "clue" node */ + parent = tmpNode; + parent->descent_dir = TREE_DESCEND_LEFT; + *lookupParent = parent; + return NULL; + } + } else + { /* Note: minNode == NULL implies, min key is -INFINITY so treat this case the same way + * as if input key value is GREATER than the minimum possible key under the "clue" node. + */ + parent = tmpNode; + parent->descent_dir = TREE_DESCEND_LEFT; + *lookupParent = parent; + return NULL; + } + } + } + /* Now that we know "clue" did not help, do a fresh traversal looking for the input key. Maintain clue at the same time */ + parent = lvt->avl_root; + if (NULL == parent) + { + *lookupParent = NULL; + return NULL; + } + node = parent; + lastNodeMin = (lvTreeNode *)NULL; /* the minimum possible key under "clue" is initially -INFINITY */ + lastNodeMax = (lvTreeNode *)NULL; /* the maximum possible key under "clue" is initially +INFINITY */ + if (NULL != node) + { + while (TRUE) + { + LV_AVL_TREE_NUMKEY_CMP(key, node, cmp); + if (0 == cmp) + { + lvt->lastLookup.lastNodeLookedUp = node; + break; + } else if (cmp < 0) + { + node->descent_dir = TREE_DESCEND_LEFT; + /* if we descend left, we know for sure all-subtree-keys are < node key so update the max key */ + nodePtr = &lastNodeMax; + nextNode = node->avl_left; + } else /* (cmp > 0) */ + { + node->descent_dir = TREE_DESCEND_RIGHT; + /* if we descend right, we know for sure all-subtree-keys are > node-key so update the min key */ + nodePtr = &lastNodeMin; + nextNode = node->avl_right; + } + parent = node; + node = nextNode; + if (NULL == node) + { + lvt->lastLookup.lastNodeLookedUp = parent; + break; + } + *nodePtr = parent; /* the actual max-key or min-key update happens here */ + } + } else + lvt->lastLookup.lastNodeLookedUp = NULL; + *lookupParent = parent; + lvt->lastLookup.lastNodeMin = lastNodeMin; /* now that clue has been set, also set max-key and min-key */ + lvt->lastLookup.lastNodeMax = lastNodeMax; + return node; +} + +/* All comments for "lvAvlTreeLookupInt" function apply here as well */ +lvTreeNode *lvAvlTreeLookupStr(lvTree *lvt, treeKeySubscr *key, lvTreeNode **lookupParent) +{ + int cmp, key_len; + char *key_addr; + lvTreeNode *node, *nextNode, *lastNodeMin, *lastNodeMax, *minNode, *maxNode, **nodePtr; + lvTreeNode *parent, *tmpNode; + treeSrchStatus *lastLookup; + + assert(lvAvlTreeLookupKeyCheck(key)); + assert(!TREE_KEY_SUBSCR_IS_CANONICAL(key->mvtype)); + assert(MVTYPE_IS_STRING(key->mvtype)); + assert(NULL != lookupParent); + TREE_DEBUG_ONLY(assert(lvTreeIsWellFormed(lvt));) + key_addr = key->str.addr; + key_len = key->str.len; + /* First see if node can be looked up easily from the lastLookup clue (without a tree traversal) */ + lastLookup = &lvt->lastLookup; + if (NULL != (tmpNode = lastLookup->lastNodeLookedUp)) + { + assert(NULL != lvt->avl_root); /* there better be an AVL tree if we have a non-zero clue */ + LV_AVL_TREE_STRKEY_CMP(key, key_addr, key_len, tmpNode, cmp); + if (0 == cmp) + { /* Input key matches last used clue. Return right away with a match. */ + node = tmpNode; + /* "parent" or "parent->descent_dir" need not be set since node is non-NULL */ + return node; + } else if (0 < cmp) + { /* Input key is GREATER than last used clue. Check RIGHT subtree of clue node to see if input key + * could possibly be found there. + */ + if (NULL == tmpNode->avl_right) + { /* No subtree to the right of the "clue" node. + * If input key is LESSER than the maximum key that can be found under the "clue" node, + * then we know for sure input key can not be found anywhere else in the tree. + * If input key is GREATER than the maximum key then it definitely is not under "clue" + * but could be somewhere else in the tree. We choose to do a fresh traversal from the top. + * If input key is EQUAL to the maximum key then return right away with a match. + */ + maxNode = lastLookup->lastNodeMax; + if (NULL != maxNode) + { + assert(0 < lvAvlTreeNodeSubscrCmp(maxNode, tmpNode)); + LV_AVL_TREE_STRKEY_CMP(key, key_addr, key_len, maxNode, cmp); + if (0 == cmp) + { /* input key is EQUAL to the maximum possible key under "clue" node */ + node = maxNode; + /* "parent" or "parent->descent_dir" need not be set since node is non-NULL */ + return node; + } else if (0 > cmp) + { /* input key is LESSER than the maximum possible key under "clue" node */ + parent = tmpNode; + parent->descent_dir = TREE_DESCEND_RIGHT; + *lookupParent = parent; + return NULL; + } + } else + { /* Note: maxNode == NULL implies, max key is +INFINITY so treat this case the same way + * as if input key value is LESSER than the maximum possible key under the "clue" node. + */ + parent = tmpNode; + parent->descent_dir = TREE_DESCEND_RIGHT; + *lookupParent = parent; + return NULL; + } + } + } else if (NULL == tmpNode->avl_left) + { /* Input key is LESSER than last used clue AND no subtree to the left of the "clue" node. + * If input key is GREATER than the minimum key that can be found under the "clue" node, + * then we know for sure input key can not be found anywhere else in the tree. + * If input key is LESSER than the minimum key then it definitely is not under "clue" + * but could be somewhere else in the tree. We choose to do a fresh traversal from the top. + * If input key is EQUAL to the minimum key then return right away with a match. + */ + minNode = lastLookup->lastNodeMin; + if (NULL != minNode) + { + assert(0 > lvAvlTreeNodeSubscrCmp(minNode, tmpNode)); + LV_AVL_TREE_STRKEY_CMP(key, key_addr, key_len, minNode, cmp); + if (0 == cmp) + { /* input key is EQUAL to the minimum possible key under "clue" node */ + node = minNode; + /* "parent" or "parent->descent_dir" need not be set since node is non-NULL */ + return node; + } else if (0 < cmp) + { /* input key is GREATER than the minimum possible key under "clue" node */ + parent = tmpNode; + parent->descent_dir = TREE_DESCEND_LEFT; + *lookupParent = parent; + return NULL; + } + } else + { /* Note: minNode == NULL implies, min key is -INFINITY so treat this case the same way + * as if input key value is GREATER than the minimum possible key under the "clue" node. + */ + parent = tmpNode; + parent->descent_dir = TREE_DESCEND_LEFT; + *lookupParent = parent; + return NULL; + } + } + } + /* Now that we know "clue" did not help, do a fresh traversal looking for the input key. Maintain clue at the same time */ + parent = lvt->avl_root; + if (NULL == parent) + { + *lookupParent = NULL; + return NULL; + } + node = parent; + lastNodeMin = (lvTreeNode *)NULL; /* the minimum possible key under "clue" is initially -INFINITY */ + lastNodeMax = (lvTreeNode *)NULL; /* the maximum possible key under "clue" is initially +INFINITY */ + if (NULL != node) + { + while (TRUE) + { + LV_AVL_TREE_STRKEY_CMP(key, key_addr, key_len, node, cmp); + if (0 == cmp) + { + lvt->lastLookup.lastNodeLookedUp = node; + break; + } else if (cmp < 0) + { + node->descent_dir = TREE_DESCEND_LEFT; + /* if we descend left, we know for sure all-subtree-keys are < node key so update the max key */ + nodePtr = &lastNodeMax; + nextNode = node->avl_left; + } else /* (cmp > 0) */ + { + node->descent_dir = TREE_DESCEND_RIGHT; + /* if we descend right, we know for sure all-subtree-keys are > node-key so update the min key */ + nodePtr = &lastNodeMin; + nextNode = node->avl_right; + } + parent = node; + node = nextNode; + if (NULL == node) + { + lvt->lastLookup.lastNodeLookedUp = parent; + break; + } + *nodePtr = parent; /* the actual max-key or min-key update happens here */ + } + } else + lvt->lastLookup.lastNodeLookedUp = NULL; + *lookupParent = parent; + lvt->lastLookup.lastNodeMin = lastNodeMin; /* now that clue has been set, also set max-key and min-key */ + lvt->lastLookup.lastNodeMax = lastNodeMax; + return node; +} + +/* Function to lookup an input key in the AVL tree. This function works for any input key type. + * Returns if a given key is found or not. "lookupParent" will be updated ONLY IF "node" is NOT found in tree. + * Note: It is preferable for performance-sensitive callers to call the lvAvlTreeLookupInt/lvAvlTreeLookupNum/lvAvlTreeLookupStr + * functions directly (assuming the caller already knows the key type) instead of going through here thereby avoiding an if check. + */ +lvTreeNode *lvAvlTreeLookup(lvTree *lvt, treeKeySubscr *key, lvTreeNode **lookupParent) +{ + int key_mvtype; + + assert(lvAvlTreeLookupKeyCheck(key)); + key_mvtype = key->mvtype; + if (TREE_KEY_SUBSCR_IS_CANONICAL(key_mvtype)) + { + if (MVTYPE_IS_INT(key_mvtype)) + return lvAvlTreeLookupInt(lvt, key, lookupParent); + else + return lvAvlTreeLookupNum(lvt, key, lookupParent); + } else + return lvAvlTreeLookupStr(lvt, key, lookupParent); +} + +/* The below two functions (lvAvlTreeSingleRotation & lvAvlTreeDoubleRotation) do height balancing of AVL tree by tree rotation. + * They are used by the "lvAvlTreeNodeInsert" and "lvAvlTreeNodeDelete" functions. + * A node insertion or deletion can cause the AVL tree height balance to be disturbed by taking it to one of 4 states. + * a) Left Left + * b) Right Right + * c) Left Right + * d) Right Left + * Cases (a) and (b) can be taken into a "Balanced" state by just ONE rotation (implemented by lvAvlTreeSingleRotation). + * Cases (c) and (d) need TWO rotations to take them to a "Balanced" state as shown below (implemented by lvAvlTreeDoubleRotation). + * + * In the below illustration (courtesy of http://en.wikipedia.org/wiki/AVL_tree) key points to note are + * -> 3,4,5 are nodes with those key values. + * -> A,B,C,D are arbitrary subtrees (not necessarily nodes). + * -> The double-width link (\\ or //) in each tree is where the rotation happens + * (the child in that link becomes the parent) to take it to the next state. + * + * Left Right case Left Left case Balanced case + * ---------------- ----------------- -------------- + * + * ------- ------- ------------- + * | 5 | | 5 | | 4 | + * ------- ------- ------------- + * / \ // \ / \ + * / \ // \ / \ + * ------- ------- ------- ------- ------- ------- + * | 3 | | D | | 4 | | D | | 3 | | 5 | + * ------- ------- ------- ------- ------- ------- + * / \\ ----> / \ ----> / \ / \ + * / \\ / \ / \ / \ + * ------- ------- ------- ------- ------- ------- ------- ------- + * | A | | 4 | | 3 | | C | | A | | B | | C | | D | + * ------- ------- ------- ------- ------- ------- ------- ------- + * / \ / \ + * / \ / \ + * ------- ------- ------- ------- + * | B | | C | | A | | B | + * ------- ------- ------- ------- + * + * Right Left case Right Right case Balanced case + * ---------------- ----------------- -------------- + * + * ------- ------- ------------- + * | 3 | | 3 | | 4 | + * ------- ------- ------------- + * / \ / \\ / \ + * / \ / \\ / \ + * ------- ------- ------- ------- ------- ------- + * | A | | 5 | | A | | 4 | | 3 | | 5 | + * ------- ------- ------- ------- ------- ------- + * // \ ----> / \ ----> / \ / \ + * // \ / \ / \ / \ + * ------- ------- ------- ------- ------- ------- ------- ------- + * | 4 | | D | | B | | 5 | | A | | B | | C | | D | + * ------- ------- ------- ------- ------- ------- ------- ------- + * / \ / \ + * / \ / \ + * ------- ------- ------- ------- + * | B | | C | | C | | D | + * ------- ------- ------- ------- + * + */ + +STATICFNDEF lvTreeNode *lvAvlTreeSingleRotation(lvTreeNode *rebalanceNode, lvTreeNode *anchorNode, int4 balanceFactor) +{ + lvTreeNode *newRoot, *node; + + TREE_DEBUG1("DOING SINGLE ROTATION\n"); + + assert((TREE_LEFT_HEAVY == balanceFactor) || (TREE_RIGHT_HEAVY == balanceFactor)); + if (TREE_LEFT_HEAVY == balanceFactor) + { /* Left-Left path rotate. In above illustration, 5 is rebalanceNode, 4 is anchorNode */ + assert(rebalanceNode->avl_left == anchorNode); + node = anchorNode->avl_right; + rebalanceNode->avl_left = node; + anchorNode->avl_right = rebalanceNode; + } else + { /* Right-Right path rotate. In above illustration, 3 is rebalanceNode, 4 is anchorNode */ + assert(rebalanceNode->avl_right == anchorNode); + node = anchorNode->avl_left; + rebalanceNode->avl_right = node; + anchorNode->avl_left = rebalanceNode; + } + if (node) + node->avl_parent = rebalanceNode; + rebalanceNode->avl_parent = anchorNode; + if (TREE_IS_NOT_BALANCED(anchorNode->balance)) + { + rebalanceNode->balance = TREE_BALANCED; + anchorNode->balance = TREE_BALANCED; + } else + anchorNode->balance = TREE_INVERT_BALANCE_FACTOR(balanceFactor); + newRoot = anchorNode; + return newRoot; +} + +/* Comments before "lvAvlTreeSingleRotation" function definition describe what this function does */ +STATICFNDEF lvTreeNode *lvAvlTreeDoubleRotation(lvTreeNode *rebalanceNode, lvTreeNode *anchorNode, int4 balanceFactor) +{ + lvTreeNode *newRoot, *node, *rebalanceChild, *anchorChild; + int balance; + + TREE_DEBUG1("DOING DOUBLE ROTATION\n"); + + assert((TREE_LEFT_HEAVY == balanceFactor) || (TREE_RIGHT_HEAVY == balanceFactor)); + if (TREE_LEFT_HEAVY == balanceFactor) + { /* Left-Right path rotate. In above illustration, 5 is rebalanceNode, 3 is anchorNode */ + assert(rebalanceNode->avl_left == anchorNode); + newRoot = anchorNode->avl_right; + rebalanceChild = newRoot->avl_right; + rebalanceNode->avl_left = rebalanceChild; + anchorChild = newRoot->avl_left; + anchorNode->avl_right = anchorChild; + newRoot->avl_left = anchorNode; + newRoot->avl_right = rebalanceNode; + } else + { /* Right-Left path rotate. In above illustration, 3 is rebalanceNode, 5 is anchorNode */ + assert(rebalanceNode->avl_right == anchorNode); + newRoot = anchorNode->avl_left; + rebalanceChild = newRoot->avl_left; + rebalanceNode->avl_right = rebalanceChild; + anchorChild = newRoot->avl_right; + anchorNode->avl_left = anchorChild; + newRoot->avl_left = rebalanceNode; + newRoot->avl_right = anchorNode; + } + if (NULL != rebalanceChild) + rebalanceChild->avl_parent = rebalanceNode; + if (NULL != anchorChild) + anchorChild->avl_parent = anchorNode; + anchorNode->avl_parent = newRoot; + rebalanceNode->avl_parent = newRoot; + balance = newRoot->balance; + if (balance == balanceFactor) + { + rebalanceNode->balance = TREE_INVERT_BALANCE_FACTOR(balanceFactor); + anchorNode->balance = TREE_BALANCED; + } else if (TREE_IS_BALANCED(balance)) + { + rebalanceNode->balance = TREE_BALANCED; + anchorNode->balance = TREE_BALANCED; + } else + { /* balance & balanceFactor are opposites */ + rebalanceNode->balance = TREE_BALANCED; + anchorNode->balance = balanceFactor; + } + newRoot->balance = TREE_BALANCED; + return(newRoot); +} + +/* Function to INSERT a node into the AVL tree. + * + * At a high level, insertion proceeds by doing a key lookup first and inserting the key wherever the lookup ends. + * After inserting a node, it is necessary to check each of the node's ancestors for consistency with the rules of AVL. + * For each node checked, if the height difference between the left and right subtrees is atmost 1 then no rotations are necessary. + * If not, then the subtree rooted at this node is unbalanced. It is possible more than one node in the ancestor path is + * unbalanced due to the insertion but at most ONE tree rotation is needed (at the last unbalanced node down the tree path) + * to restore the entire tree to the rules of AVL. + * + * Key points to note for AVL tree insertion are (courtesy of http://neil.brown.name/blog/20041124101820) + * + * When we step down from a node to the longest subtree, that node's tree will not get any larger. If the subtree + * we descend into grows, a rotation will be needed, but this will result in this tree being the same height as it + * was before. The only difference at this level is that it will be balanced whereas it wasn't before. This means + * that when we step down a longer path, we can forget about all the nodes above the one we step down from. They + * cannot be affected as neither of their subtrees will grow. This observation tells us where the rotation has to happen. + * + * We only need to rotate if as part of the lookup/insert, we step down to a longer subtree, and then all + * subsequent nodes are balanced, without longer or shorter subtrees. The actual rotation that happens + * (if needed) will depend on what happens immediately after stepping down to a longer subtree. If the + * next step is in the same direction as the step into the larger subtree, then a SINGLE rotation will be + * needed. If the next step is in the other direction, then a DOUBLE rotation will be needed. + * + * The important data that we need is that part of the path from the last unbalanced node to the insertion + * point. All nodes in this list other than the first will be balanced, including the newly inserted + * node. All of these nodes will either take part in a rotation, or will need to have their balance value + * changed (though the new node won't need it's balance changed, it could take part in a rotation though). + * + * This path may not actually start with an unbalanced node. If every node in the path from the root is + * balanced, then that whole path needs to be recorded, and every node will have it's balance changed. + * + * Time for insert = O(log n) for lookup + O(1) for at most one single or double rotation = O(log n) + */ +lvTreeNode *lvAvlTreeNodeInsert(lvTree *lvt, treeKeySubscr *key, lvTreeNode *parent) +{ + lvTreeNode *node, *t_root, *tmp_parent, *anchorNode, *tmpNode, **nodePtr; + lvTreeNode *rebalanceNode, *rebalanceNodeParent; + lvTreeNodeNum *fltNode; + int balanceFactor, curBalance, cmp, key_mvtype; + treeSrchStatus *lastLookup; + DEBUG_ONLY(lvTreeNode *dbg_node); + + assert(MV_DEFINED(key)); + /* At time of node insertion into tree, ensure that if we have MV_CANONICAL bit set, then MV_STR bit is not set. + * This makes it clear that the node key is a number and does not have the string representation constructed. + */ + assert(!TREE_KEY_SUBSCR_IS_CANONICAL(key->mvtype) || MV_IS_NUMERIC(key) && !MV_IS_STRING(key)); + assert(NULL == lvAvlTreeLookup(lvt, key, &tmp_parent)); + /* create a node in the avl tree and initialize it */ + node = lvtreenode_getslot(LVT_GET_SYMVAL(lvt)); + assert(NULL != node); + /* node->v must be initialized by caller */ + node->sbs_child = NULL; + node->tree_parent = lvt; + node->avl_left = node->avl_right = NULL; + node->avl_parent = parent; + key_mvtype = key->mvtype; + if (TREE_KEY_SUBSCR_IS_CANONICAL(key_mvtype)) + { /* Initialize lvTreeNodeNum structure */ + fltNode = (lvTreeNodeNum *)node; + fltNode->key_mvtype = (key_mvtype & MV_EXT_NUM_MASK); + if (MV_INT & key_mvtype) + { + fltNode->key_m0 = key->m[1]; + fltNode->key_flags.key_bits.key_iconv = FALSE; + } else + { + fltNode->key_flags.key_bytes.key_sgne = ((mval_b *)key)->sgne; + fltNode->key_m0 = key->m[0]; + fltNode->key_m1 = key->m[1]; + } + } else + { /* Initialize lvTreeNode structure */ + node->key_mvtype = MV_STR; /* do not use input mvtype as it might have MV_NM or MV_NUM_APPROX bits set */ + node->key_len = key->str.len; + node->key_addr = key->str.addr; + } + node->balance = TREE_BALANCED; + /* node->descent_dir is initialized later when this node is part of a lvAvlTreeLookup operation. + * this field is not used by anyone until then so ok not to initialize it here. + * Update lastLookup clue to reflect the newly inserted node. Note that this clue will continue + * to be valid inspite of any single or double rotations that happen as part of the insert below. + */ + lastLookup = &lvt->lastLookup; + lastLookup->lastNodeLookedUp = node; /* first part of lastLookup clue update */ + /* Do height-balancing tree rotations as needed to maintain avl property */ + if (NULL != parent) + { + if (TREE_DESCEND_LEFT == parent->descent_dir) + { + parent->avl_left = node; + nodePtr = &lastLookup->lastNodeMax; + } else + { + parent->avl_right = node; + nodePtr = &lastLookup->lastNodeMin; + } + *nodePtr = parent; /* second (and last) part of lastLookup clue update */ + t_root = lvt->avl_root; + /* Do balance factor adjustment & rotation as needed */ + tmpNode = parent; + while (t_root != tmpNode) + { + balanceFactor = tmpNode->balance; + if (TREE_IS_NOT_BALANCED(balanceFactor)) + break; + tmpNode->balance = tmpNode->descent_dir; + tmpNode = tmpNode->avl_parent; + } + rebalanceNode = tmpNode; + if (TREE_DESCEND_LEFT == rebalanceNode->descent_dir) + { + anchorNode = rebalanceNode->avl_left; + balanceFactor = TREE_LEFT_HEAVY; + } else + { + anchorNode = rebalanceNode->avl_right; + balanceFactor = TREE_RIGHT_HEAVY; + } + curBalance = rebalanceNode->balance; + if (TREE_IS_BALANCED(curBalance)) /* tree has grown taller */ + { + rebalanceNode->balance = balanceFactor; + TREE_DEBUG3("Tree height increased from %d to %d\n", lvt->avl_height, (lvt->avl_height + 1)); + lvt->avl_height++; + assert(1 < lvt->avl_height); + } else if (curBalance == TREE_INVERT_BALANCE_FACTOR(balanceFactor)) + { /* the tree has gotten more balanced */ + rebalanceNode->balance = TREE_BALANCED; + } else + { /* The tree has gotten out of balance so need a rotation */ + rebalanceNodeParent = rebalanceNode->avl_parent; + assert(anchorNode->balance == anchorNode->descent_dir); + assert(balanceFactor == rebalanceNode->descent_dir); + if (anchorNode->balance == balanceFactor) + tmpNode = lvAvlTreeSingleRotation(rebalanceNode, anchorNode, balanceFactor); + else + tmpNode = lvAvlTreeDoubleRotation(rebalanceNode, anchorNode, balanceFactor); + tmpNode->avl_parent = rebalanceNodeParent; + if (NULL != rebalanceNodeParent) + { /* root of tree has NOT changed */ + assert(rebalanceNodeParent->descent_dir + == ((rebalanceNodeParent->avl_left == rebalanceNode) + ? TREE_DESCEND_LEFT : TREE_DESCEND_RIGHT)); + if (TREE_DESCEND_LEFT == rebalanceNodeParent->descent_dir) + rebalanceNodeParent->avl_left = tmpNode; + else + rebalanceNodeParent->avl_right = tmpNode; + } else + lvt->avl_root = tmpNode; /* new root although tree height is still the same */ + } + } else + { /* we just inserted the real root of the tree */ + assert(NULL == lvt->avl_root); + lvt->avl_root = node; + assert(NULL == node->avl_right); + assert(NULL == node->avl_left); + assert(TREE_BALANCED == node->balance); + TREE_DEBUG3("Tree height increased from %d to %d\n", lvt->avl_height, (lvt->avl_height + 1)); + assert(0 == lvt->avl_height); + lvt->avl_height = 1; + lastLookup->lastNodeMin = NULL; /* ensure lastLookup clue is uptodate */ + lastLookup->lastNodeMax = NULL; /* ensure lastLookup clue is uptodate */ + } + TREE_DEBUG_ONLY(assert(lvTreeIsWellFormed(lvt));) + return node; +} + +/* Function to DELETE a node from the AVL tree. + * + * At a high level, deletion proceeds by doing a key lookup first and deleting the node wherever the lookup ends. + * After deleting a node, it is necessary to check each of the node's ancestors for consistency with the rules of AVL. + * For each node checked, if the height difference between the left and right subtrees is atmost 1 then no rotations are necessary. + * If not, then the subtree rooted at this node is unbalanced. It is possible more than one node in the ancestor path is + * unbalanced due to the deletion. Unlike insertion, MANY (as many as the height of the tree) tree rotations are needed + * to restore the entire tree to the rules of AVL. + * + * Key points to note for AVL tree deletion are (courtesy of http://neil.brown.name/blog/20041124141849) + * + * It is easy to remove a node when it only has one child, as that child can replace the deleted node as the child of + * the grandparent. If the target node has two children, the easiest approach is to swap the node with a node that does + * have at most one child. This can work providing we can find such a node that is adjacent to the target node in the sort-order. + * A few moments consideration will confirm that if a node has two children, then there must be a node both immediately above + * and below it in the sort order, and that both of these must be further down the tree than the target node, and that neither + * of these can have two children (as if they did, then one of the children would lie between that node and the target, which + * contradicts the selection of that node). In this implementation we choose the immediately-above node (in-order successor) only. + * + * While insert only needed one rotation, delete may need rotation at every level. + * If the shrinkage of a subtree (i.e. height reduction due to a tree rotation) causes the node's tree to shrink, the change + * must propagate up. If it does not, then all higher nodes in the tree will not be affected. + * + * Time for delete = O(log n) for lookup + at most O(log n) rotations = O(log n) + */ +void lvAvlTreeNodeDelete(lvTree *lvt, lvTreeNode *node) +{ + boolean_t treeBalanced; + int4 balanceFactor, curNodeBalance; + lvTreeNode *balanceThisNode, *nextBalanceThisNode, *anchorNode, *newRoot; + lvTreeNode *next, *nextLeft, *nextRight, *nextParent, *left; + lvTreeNode *nodeLeft, *nodeRight, *nodeParent; + lvTreeNode *rebalanceNode, *rebalanceNodeParent; + + assert(IS_LVAVLTREENODE(((lv_val *)node))); /* ensure input "node" is of type "lvTreeNode *" or "lvTreeNodeNum *" + * and not "lv_val *" */ + assert(lvt == node->tree_parent); + nodeLeft = node->avl_left; + nodeRight = node->avl_right; + nodeParent = node->avl_parent; + if (NULL == nodeRight) + { /* No right subtree. Move the left subtree (if it exists) over to the current node. */ + balanceThisNode = nodeParent; + if (NULL != balanceThisNode) + { + assert(balanceThisNode->descent_dir + == ((balanceThisNode->avl_left == node) ? TREE_DESCEND_LEFT : TREE_DESCEND_RIGHT)); + if (TREE_DESCEND_LEFT == balanceThisNode->descent_dir) + balanceThisNode->avl_left = nodeLeft; + else + balanceThisNode->avl_right = nodeLeft; + } else + { /* No parent and the fact that this is an AVL tree implies only two possibilities + * a) deleting only node of tree. + * b) deleting root of a height=2 tree that has only one child-node on its left and no other nodes. + * In either case, the tree height will reduce by 1 due to the deletion. + * Set tree root here. Tree height will be reduced later in the rebalancing for loop. + */ + lvt->avl_root = nodeLeft; + } + if (NULL != nodeLeft) + nodeLeft->avl_parent = balanceThisNode; + } else + { /* Replace node with in-order successor, and rearrange */ + next = nodeRight; + while (TRUE) + { + left = next->avl_left; + if (NULL != left) + { + next->descent_dir = TREE_DESCEND_LEFT; + next = left; + } else + break; + } + /* at this point, "next" is the in-order successor of "node" */ + assert(NULL == next->avl_left); + nextRight = next->avl_right; + nextParent = next->avl_parent; + if (nextParent != node) + { + balanceThisNode = nextParent; + assert(nextParent->descent_dir + == ((nextParent->avl_left == next) ? TREE_DESCEND_LEFT : TREE_DESCEND_RIGHT)); + if (TREE_DESCEND_LEFT == nextParent->descent_dir) + nextParent->avl_left = nextRight; + else + nextParent->avl_right = nextRight; + if (NULL != nextRight) + nextRight->avl_parent = nextParent; + next->avl_right = nodeRight; + nodeRight->avl_parent = next; + } else + { + assert(nextParent->avl_right == next); + balanceThisNode = next; + } + assert(NULL != balanceThisNode); + next->avl_left = nextLeft = nodeLeft; + if (NULL != nextLeft) + nextLeft->avl_parent = next; + next->avl_parent = nodeParent; + next->balance = node->balance; + next->descent_dir = TREE_DESCEND_RIGHT; + if (NULL != nodeParent) + { + assert(nodeParent->descent_dir + == ((nodeParent->avl_left == node) ? TREE_DESCEND_LEFT : TREE_DESCEND_RIGHT)); + if (TREE_DESCEND_LEFT == nodeParent->descent_dir) + nodeParent->avl_left = next; + else + nodeParent->avl_right = next; + } else + lvt->avl_root = next; /* root of avl tree changing from "node" to "next" */ + } + /* At this point "balanceThisNode" points to the first unbalanced node (from the bottom of the tree path) + * which might also need a rotation (single or double). The rotations need to bubble up the tree hence the for loop. + */ + for ( ; ; balanceThisNode = nextBalanceThisNode) + { /* Balancing act */ + if (NULL == balanceThisNode) + { /* Went past root of tree as part of balance factor adjustments. Tree height has reduced. */ + TREE_DEBUG3("Tree height reduced from %d to %d\n", lvt->avl_height, (lvt->avl_height - 1)); + assert(lvt->avl_height > 0); + lvt->avl_height--; + break; + } + balanceFactor = balanceThisNode->descent_dir; + curNodeBalance = balanceThisNode->balance; + if (TREE_IS_BALANCED(curNodeBalance)) + { /* Found a balanced node in the tree path. Fix its balance to account for the decrease in subtree height. + * No more tree rotations needed so break out of the for loop. + */ + balanceThisNode->balance = TREE_INVERT_BALANCE_FACTOR(balanceFactor); + break; + } + nextBalanceThisNode = balanceThisNode->avl_parent; + assert((NULL == nextBalanceThisNode) + || nextBalanceThisNode->descent_dir + == ((nextBalanceThisNode->avl_left == balanceThisNode) ? TREE_LEFT_HEAVY : TREE_RIGHT_HEAVY)); + if (curNodeBalance == balanceFactor) + { /* The longer subtree is the one which had its height reduce so this node now becomes balanced. + * So no rotation needed for this node, but the height of this node has now reduced so we still + * need to trace back up the tree to see if this causes any more height imbalances. + */ + balanceThisNode->balance = TREE_BALANCED; + continue; + } + /* balanceThisNode->balance == inverse of balanceFactor. + * + * The subtree at balanceThisNode is already heavy on one side and a node delete has caused + * the other side to reduce one more level increasing the height difference between the two + * subtrees to be 2 and therefore requires a rebalance (using tree rotation). + */ + rebalanceNode = balanceThisNode; + if (TREE_LEFT_HEAVY == balanceFactor) + { + balanceFactor = TREE_RIGHT_HEAVY; + anchorNode = rebalanceNode->avl_right; + } else + { + balanceFactor = TREE_LEFT_HEAVY; + anchorNode = rebalanceNode->avl_left; + } + treeBalanced = TREE_IS_BALANCED(anchorNode->balance); + if (treeBalanced || (anchorNode->balance == balanceFactor)) + { + newRoot = lvAvlTreeSingleRotation(rebalanceNode, anchorNode, balanceFactor); + assert((treeBalanced && TREE_IS_NOT_BALANCED(rebalanceNode->balance) + && TREE_IS_NOT_BALANCED(anchorNode->balance)) + || (TREE_IS_BALANCED(rebalanceNode->balance) && TREE_IS_BALANCED(anchorNode->balance))); + } else + newRoot = lvAvlTreeDoubleRotation(rebalanceNode, anchorNode, balanceFactor); + rebalanceNodeParent = nextBalanceThisNode; + newRoot->avl_parent = rebalanceNodeParent; + if (NULL != rebalanceNodeParent) + { /* Point parent to new root of the rotated subtree */ + assert(rebalanceNodeParent->descent_dir + == ((rebalanceNodeParent->avl_left == rebalanceNode) ? TREE_DESCEND_LEFT : TREE_DESCEND_RIGHT)); + if (TREE_DESCEND_LEFT == rebalanceNodeParent->descent_dir) + rebalanceNodeParent->avl_left = newRoot; + else + rebalanceNodeParent->avl_right = newRoot; + } else + lvt->avl_root = newRoot; + if (treeBalanced) + { /* If tree is balanced at "anchorNode" before the rotation, the post-rotated tree height does NOT change. + * This means no more need to propagate the rebalancing up the tree. Break out of the loop now. + */ + break; + } + } + lvt->lastLookup.lastNodeLookedUp = NULL; /* reset lastLookup clue since all bets are off after the delete */ +} diff --git a/sr_port/lv_tree.h b/sr_port/lv_tree.h new file mode 100644 index 0000000..c8f3f6d --- /dev/null +++ b/sr_port/lv_tree.h @@ -0,0 +1,321 @@ +/**************************************************************** + * * + * Copyright 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef _TREE_H +#define _TREE_H + +#include + +/* The "lvTree" typedef defines an AVL tree. One such structure exists for each local variable subscript level (starting from 0) + * The "lvTreeNode" typedef defines the layout of the tree node. One structure exists for each tree node in an AVL tree. + * e.g. if "a" is a local variable that has the following nodes defined + * a(1)=1 + * a(1,"a")=2 + * a(1,"b")=3 + * a(2,"c")=4 + * There is ONE "lv_val" structure corresponding to the base local variable "a". + * There are THREE "lvTree" structures (i.e. AVL trees) underneath the base local variable "a". + * 1) One for the base variable "a". This contains the keys/subscripts 1 and 2. + * 2) One for the subscripted variable "a(1)". This contains the keys/subscripts "a" and "b". + * 3) One for the subscripted variable "a(2)". This contains the key/subscript "c". + * There are FIVE "lvTreeNode" structures underneath the base local variable "a". + * 1) One for subscript 1. + * 2) One for subscript 2. + * 3) One for subscript "a". + * 4) One for subscript "b". + * 5) One for subscript "c". + */ + +typedef mval lvTreeNodeVal; +typedef mval treeKeySubscr; + +/* A numeric key value stored in the AVL tree is represented in a lvTreeNodeNum structure. Additionally, a string type key can + * also be stored in the same AVL tree. Instead of using an "mstr" type directly, we explicitly define the necessary fields + * from the mstr (no char_len field) so we can save 8 bytes in the lvTreeNode struct on 64-bit platforms). This way both the + * numeric and string lvTreeNode structures require 12 bytes thus have 8-byte alignment for the lvTreeNode* typedefs (on 64-bit + * platforms) without any wasted padding space. This means that any code that interprets the key in a lvTreeNode structure + * needs to examine the "key_mvtype" field and accordingly use a lvTreeNodeNum or lvTreeNodeStr structure to get at the key. + */ +typedef struct lvTreeNodeNumStruct +{ + lvTreeNodeVal v; /* If string, will point to stringpool location */ + struct lvTreeStruct *sbs_child; /* pointer to lvTreeStruct storing next level subscripts under this node */ + struct lvTreeStruct *tree_parent; /* pointer to lvTreeStruct under whose avl tree this node belongs to */ + unsigned short key_mvtype; /* will be (if non-integer) or (if integer) */ + signed char balance; /* height(left) - height(right). Can be -1, 0, or 1 */ + unsigned char descent_dir; /* direction of descent (LEFT = 0, RIGHT = 1) */ + /* the value of the numeric key is stored in the key_* members below */ + union + { + struct + { + unsigned char key_sgne; /* Contains sgn & e fields - fewer instructions to move this one byte + * rather than two bit fields when copying to/from an mval */ + } key_bytes; + struct + { +# ifdef BIGENDIAN + unsigned int key_sgn : 1; /* same as mval layout */ + unsigned int key_e : 7; /* same as mval layout */ +# else + unsigned int key_e : 7; /* same as mval layout */ + unsigned int key_sgn : 1; /* same as mval layout */ +# endif + unsigned int key_iconv : 1; /* 1 if (key_mvtype & MV_INT is TRUE) && (key_m0 field stores the + * mval->m[1] value of the integer before it was converted into the + * float representation. + */ + unsigned int filler :23; + } key_bits; + } key_flags; + int4 key_m0; + int4 key_m1; + struct treeNodeStruct *avl_left; /* left AVL subtree at this subscript level */ + struct treeNodeStruct *avl_right; /* right AVL subtree at this subscript level */ + struct treeNodeStruct *avl_parent; /* parent node within the current subscript level AVL tree */ +} lvTreeNodeNum; + +typedef struct treeNodeStruct +{ + lvTreeNodeVal v; /* If string, will point to stringpool location */ + struct lvTreeStruct *sbs_child; /* pointer to lvTreeStruct storing next level subscripts under this node; + * overloaded as the free list pointer if this "lvTreeNode" is in the free list. + */ + struct lvTreeStruct *tree_parent; /* pointer to lvTreeStruct under whose avl tree this node belongs to */ + unsigned short key_mvtype; /* will have MV_STR bit set */ + signed char balance; /* height(left) - height(right). Can be -1, 0, or 1 */ + unsigned char descent_dir; /* direction of descent (LEFT = 0, RIGHT = 1) */ + uint4 key_len; /* byte length of string */ + char *key_addr; /* pointer to string */ + NON_GTM64_ONLY(uint4 filler_8byte;) /* needed to ensure lvTreeNodeNum & lvTreeNode structures have + * same size & layout for 32-bit platforms also */ + struct treeNodeStruct *avl_left; /* left AVL subtree at this subscript level */ + struct treeNodeStruct *avl_right; /* right AVL subtree at this subscript level */ + struct treeNodeStruct *avl_parent; /* parent node within the current subscript level AVL tree */ +} lvTreeNode; + +/* Given a pointer to a "lvTreeNode" structure, the mvtype field will tell us whether it is a lvTreeNodeStr or lvTreeNodeNum type. + * key_mvtype will have the MV_STR bit set in case of lvTreeNodeStr and otherwise if lvTreeNodeNum. + */ +typedef lvTreeNode lvTreeNodeStr; + +/* last lookup clue structure */ +typedef struct +{ + lvTreeNode *lastNodeLookedUp; /* pointer to the node that was last looked up using lvAvlTreeLookup function */ + lvTreeNode *lastNodeMin; /* minimum value of key that can be underneath the last looked up node's subtree */ + lvTreeNode *lastNodeMax; /* maximum value of key that can be underneath the last looked up node's subtree */ +} treeSrchStatus; + +typedef struct lvTreeStruct +{ + unsigned short ident; /* 2-byte field (same size as mvtype) set to the value MV_LV_TREE */ + unsigned short sbs_depth; /* == "n" => all nodes in current avl tree represent lvns with "n" subscripts */ + uint4 avl_height; /* Height of the AVL tree rooted at "avl_root" */ + struct lv_val_struct *base_lv; /* back pointer to base var lv_val (subscript depth 0) */ + lvTreeNode *avl_root; /* pointer to the root of the AVL tree corresponding to this subscript level; + * overloaded as the free list pointer if this "lvTree" structure is in the free list. + */ + lvTreeNode *sbs_parent;/* pointer to parent (points to lv_val if sbs_depth==1 and lvTreeNode if sbs_depth>1) */ + treeSrchStatus lastLookup; /* clue to last node lookup in the AVL tree rooted at "avl_root" */ +} lvTree; + +/* This section defines macros for the AVL tree implementation. Note that an AVL tree maintains its log(n) height by ensuring + * the left and right subtrees at any level never differ in height by more than 1. + */ + +#define MAX_BALANCE 1 /* the maximum difference in height of the left and right subtrees of a tree, + * balance > MAX_BALANCE will trigger a re-balance */ + +#define TREE_DESCEND_LEFT 0 +#define TREE_DESCEND_RIGHT 1 + +/* AVL tree algorithms talk about balance factor as being -1 (left heavy), 1 (right heavy) or 0 (balanced) + * but we use 0, 1 and 2 instead for this implementation. + */ +#define TREE_LEFT_HEAVY 0 /* should be same as TREE_DESCEND_LEFT (an implementation efficiency) */ +#define TREE_RIGHT_HEAVY 1 /* should be same as TREE_DESCEND_RIGHT (an implementation efficiency) */ +#define TREE_BALANCED 2 + +#define TREE_IS_LEFT_HEAVY(bal) (bal) == TREE_LEFT_HEAVY +#define TREE_IS_RIGHT_HEAVY(bal) (bal) == TREE_RIGHT_HEAVY +#define TREE_IS_BALANCED(bal) ((bal) == TREE_BALANCED) +#define TREE_IS_NOT_BALANCED(bal) (!(TREE_IS_BALANCED(bal))) + +#define TREE_INVERT_BALANCE_FACTOR(bal) (tree_balance_invert[bal]) + +#define TREE_KEY_SUBSCR_IS_CANONICAL(A_MVTYPE) (A_MVTYPE & MV_CANONICAL) +#define TREE_KEY_SUBSCR_SET_MV_CANONICAL_BIT(A_MVAL) (A_MVAL)->mvtype |= MV_CANONICAL +#define TREE_KEY_SUBSCR_RESET_MV_CANONICAL_BIT(A_MVAL) (A_MVAL)->mvtype &= MV_CANONICAL_OFF + +/* Macro to get numeric valued subscript in an lv node */ +#define LV_NUM_NODE_GET_KEY(NODE, KEY) \ +{ \ + lvTreeNodeNum *lcl_flt_node; \ + int lcl_mvtype; \ + \ + lcl_flt_node = (lvTreeNodeNum *)NODE; \ + lcl_mvtype = lcl_flt_node->key_mvtype; \ + assert(MVTYPE_IS_NUMERIC(lcl_mvtype)); \ + if (MV_INT & lcl_mvtype) \ + (KEY)->m[1] = lcl_flt_node->key_m0; \ + else \ + { \ + assert(lcl_flt_node->key_flags.key_bits.key_e || !lcl_flt_node->key_m1); \ + ((mval_b *)(KEY))->sgne = lcl_flt_node->key_flags.key_bytes.key_sgne; \ + (KEY)->m[0] = lcl_flt_node->key_m0; \ + (KEY)->m[1] = lcl_flt_node->key_m1; \ + } \ + (KEY)->mvtype = lcl_mvtype; \ +} + +/* Macro to get string valued subscript in an lv node */ +#define LV_STR_NODE_GET_KEY(NODE, KEY) \ +{ \ + assert(MVTYPE_IS_STRING(NODE->key_mvtype)); \ + (KEY)->mvtype = NODE->key_mvtype; \ + (KEY)->str.len = NODE->key_len; \ + (KEY)->str.addr = NODE->key_addr; \ +} + +/* If NODE contains a numeric key, before returning the key's mval to non-lv code make sure we + * reset any bit(s) that are set only in lv code. Non-lv code does not know to handle this bit. + * The only one at this point in time is MV_CANONICAL. + */ +#define LV_NODE_GET_KEY(NODE, KEY_MVAL) \ +{ \ + if (TREE_KEY_SUBSCR_IS_CANONICAL(NODE->key_mvtype)) \ + { /* "NODE" is of type "lvTreeNodeNum *" */ \ + LV_NUM_NODE_GET_KEY(NODE, KEY_MVAL); \ + (KEY_MVAL)->mvtype &= MV_CANONICAL_OFF; \ + } else /* "NODE" is of type "lvTreeNode *" */ \ + LV_STR_NODE_GET_KEY(NODE, KEY_MVAL); \ +} + +/* Macro to return if a given "lvTreeNode *" pointer is a null subscript string. + * Input "node" could actually be any one of "lvTreeNodeNum *" or "lvTreeNode *". + * A null subscript string is of actual type "lvTreeNode *". To minimize the checks, we check for the MV_STR bit in the mvtype. + * This is asserted below. For "lvTreeNodeNum *", the MV_STR bit is guaranteed not to be set. That leaves us with "lvTreeNode *". + */ +#define LV_NODE_KEY_IS_STRING(NODE) (DBG_ASSERT(NULL != NODE) \ + MVTYPE_IS_STRING(NODE->key_mvtype)) + +#define LV_NODE_KEY_IS_NULL_SUBS(NODE) (LV_NODE_KEY_IS_STRING(NODE) && (0 == NODE->key_len)) + +#ifdef DEBUG +int lvAvlTreeKeySubscrCmp(treeKeySubscr *aSubscr, lvTreeNode *bNode); +int lvAvlTreeNodeSubscrCmp(lvTreeNode *aNode, lvTreeNode *bNode); +#endif +lvTreeNode *lvAvlTreeFirst(lvTree *lvt); +lvTreeNode *lvAvlTreeLast(lvTree *lvt); +lvTreeNode *lvAvlTreePrev(lvTreeNode *node); +lvTreeNode *lvAvlTreeKeyPrev(lvTree *lvt, treeKeySubscr *key); +lvTreeNode *lvAvlTreeNext(lvTreeNode *node); +lvTreeNode *lvAvlTreeKeyNext(lvTree *lvt, treeKeySubscr *key); +lvTreeNode *lvAvlTreeFirstPostOrder(lvTree *lvt); +lvTreeNode *lvAvlTreeNextPostOrder(lvTreeNode *node); +lvTreeNode *lvAvlTreeKeyCollatedNext(lvTree *lvt, treeKeySubscr *key); +lvTreeNode *lvAvlTreeNodeCollatedNext(lvTreeNode *node); +lvTreeNode *lvAvlTreeCloneSubTree(lvTreeNode *node, lvTree *lvt, lvTreeNode *avl_parent); + +#ifdef DEBUG +boolean_t lvTreeIsWellFormed(lvTree *lvt); +void assert_tree_member_offsets(void); +#endif + +lvTreeNode *lvAvlTreeLookupInt(lvTree *lvt, treeKeySubscr *key, lvTreeNode **lookupNode); +lvTreeNode *lvAvlTreeLookupNum(lvTree *lvt, treeKeySubscr *key, lvTreeNode **lookupNode); +lvTreeNode *lvAvlTreeLookupStr(lvTree *lvt, treeKeySubscr *key, lvTreeNode **lookupNode); +lvTreeNode *lvAvlTreeLookup(lvTree *lvt, treeKeySubscr *key, lvTreeNode **lookupNode); +lvTreeNode *lvAvlTreeNodeInsert(lvTree *lvt, treeKeySubscr *key, lvTreeNode *parent); +void lvAvlTreeNodeDelete(lvTree *lvt, lvTreeNode *node); + +/* The following LV_TREE_* macros are not defined as functions for performance reasons (to avoid overhead of parameter passing + * and C stack push and pop) + */ +#define LV_TREE_CREATE(NEWTREE, SBS_PARENT, SBS_DEPTH, BASE_LV) \ +{ \ + DEBUG_ONLY(assert_tree_member_offsets()); \ + NEWTREE = lvtree_getslot(LV_GET_SYMVAL(base_lv)); \ + /* Note: the fields are initialized below in the order in which they are defined in the "lvTree" structure */ \ + NEWTREE->ident = MV_LV_TREE; \ + assert(0 < SBS_DEPTH); \ + NEWTREE->sbs_depth = SBS_DEPTH; \ + NEWTREE->avl_height = 0; \ + NEWTREE->base_lv = BASE_LV; \ + NEWTREE->avl_root = NULL; \ + NEWTREE->sbs_parent = SBS_PARENT; /* note: LVT_PARENT macro not used as otherwise one would wonder why \ + * all members of "lvTree" structure except "sbs_parent" are initialized. \ + */ \ + (SBS_PARENT)->sbs_child = NEWTREE; \ + NEWTREE->lastLookup.lastNodeLookedUp = NULL; \ +} + +#define LV_TREE_NODE_DELETE(LVT, NODE) \ +{ \ + assert(NULL != LVT); \ + lvAvlTreeNodeDelete(LVT, NODE); \ + /* Now that "NODE" has been removed from the AVL tree, it is safe to free the slot */ \ + LVTREENODE_FREESLOT(NODE); \ + TREE_DEBUG_ONLY(assert(lvTreeIsWellFormed(LVT));) \ +} + +#define LV_TREE_CLONE(LVT, SBS_PARENT, BASE_LV) \ +{ \ + lvTree *cloneTree; \ + lvTreeNode *avl_root; \ + \ + cloneTree = lvtree_getslot(LV_GET_SYMVAL(BASE_LV)); \ + /* The following is optimized to do the initialization of just the needed structure members. \ + * For that it assumes a particular "lvTree" structure layout. The assumed layout is asserted \ + * so any changes to the layout will automatically alert us (by an assert failure) here and \ + * cause the below initialization to be accordingly reworked. \ + */ \ + assert(8 == OFFSETOF(lvTree, base_lv)); \ + assert(OFFSETOF(lvTree, base_lv) + SIZEOF(LVT->base_lv) == OFFSETOF(lvTree, avl_root)); \ + assert(OFFSETOF(lvTree, avl_root) + SIZEOF(LVT->avl_root) == OFFSETOF(lvTree, sbs_parent)); \ + assert(OFFSETOF(lvTree, sbs_parent) + SIZEOF(LVT->sbs_parent) == OFFSETOF(lvTree, lastLookup)); \ + assert(OFFSETOF(lvTree, lastLookup) + SIZEOF(LVT->lastLookup) == SIZEOF(lvTree)); \ + /* Note: We use "qw_num *" instead of "uint8 *" below because the former works on 32-bit platforms too. */ \ + GTM64_ONLY(assert(IS_PTR_8BYTE_ALIGNED(cloneTree));) \ + NON_GTM64_ONLY(assert(IS_PTR_4BYTE_ALIGNED(cloneTree));) \ + GTM64_ONLY(assert(IS_PTR_8BYTE_ALIGNED(LVT));) \ + NON_GTM64_ONLY(assert(IS_PTR_4BYTE_ALIGNED(LVT));) \ + *(qw_num *)cloneTree = *(qw_num *)LVT; \ + cloneTree->base_lv = BASE_LV; \ + cloneTree->sbs_parent = SBS_PARENT; /* see comment in LV_TREE_CREATE macro (against sbs_parent \ + * initialization) for why LVT_PARENT macro is not used */ \ + (SBS_PARENT)->sbs_child = cloneTree; \ + /* reset clue in cloned tree as source tree pointers are no longer relevant in cloned tree */ \ + cloneTree->lastLookup.lastNodeLookedUp = NULL; \ + if (NULL != (avl_root = (LVT)->avl_root)) \ + cloneTree->avl_root = lvAvlTreeCloneSubTree(avl_root, cloneTree, NULL); \ + else \ + cloneTree->avl_root = NULL; \ +} + +#ifdef TREE_DEBUG +# define TREE_DEBUG1(p) {printf(p); fflush(stdout);} +# define TREE_DEBUG2(p, q) {printf(p, q); fflush(stdout);} +# define TREE_DEBUG3(p, q, r) {printf(p, q, r); fflush(stdout);} +# define TREE_DEBUG4(p, q, r, s) {printf(p, q, r, s); fflush(stdout);} +# define TREE_DEBUG5(p, q, r, s, t) {printf(p, q, r, s, t); fflush(stdout);} +# define TREE_DEBUG_ONLY(X) X +#else +# define TREE_DEBUG1(p) +# define TREE_DEBUG2(p, q) +# define TREE_DEBUG3(p, q, r) +# define TREE_DEBUG4(p, q, r, s) +# define TREE_DEBUG5(p, q, r, s, t) +# define TREE_DEBUG_ONLY(X) +#endif + +#endif diff --git a/sr_port/lv_val.h b/sr_port/lv_val.h new file mode 100644 index 0000000..a95163e --- /dev/null +++ b/sr_port/lv_val.h @@ -0,0 +1,445 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + + +#ifndef LVVAL_H_INCLUDED +#define LVVAL_H_INCLUDED + +#include /* for OFFSETOF macro used in the IS_OFFSET_AND_SIZE_MATCH macro */ + +#include "hashtab_mname.h" +#include "lv_tree.h" + +/* Define a few generic LV related macros. These macros work irrespective of whether the input is an unsubscripted + * local variable (aka base variable) which is of type (lv_val *) or a subscripted local variable which is of type + * (lvTreeNode *). All of these assume the layout of the two structures is very similar (asserted in "LV_TREE_CREATE" + * macro). These macros will be used extensively for code centralization and to avoid code duplication. + * + * The macros that are only passed an LV_PTR need to ensure that it is indeed a "lv_val *" or a "lvTreeNode *". + * The macros that are also passed an IS_BASE_VAR parameter can safely assume this to be the case since the IS_BASE_VAR + * parameter would have been computed using the LV_IS_BAS_VAR macro which already does the ensure. + * So we avoid the duplicate assert in these cases. + */ + +#define SYM_IS_SYMVAL(SYM) (MV_SYM == (SYM)->ident) + +#define IS_LV_TREE(LVT) (MV_LV_TREE == ((lvTree *)LVT)->ident) +#define LV_PARENT(LV) LV_AVLNODE_PARENT(LV) +#define LV_AVLNODE_PARENT(PTR) (((lvTreeNode *)PTR)->tree_parent) +#define LVT_PARENT(LVT) (((lvTree *)LVT)->sbs_parent) +#define IS_LVAVLTREENODE(PTR) (IS_LV_TREE(LV_AVLNODE_PARENT((lv_val *)PTR))) + +#define IS_PARENT_MV_SYM(LV_PTR) (SYM_IS_SYMVAL(LV_SYMVAL((lv_val *)LV_PTR))) + +#define DBG_ASSERT_LVT(LVT) DBG_ASSERT(IS_LV_TREE(LVT)) /* is "lvTree *" */ + +#define DBG_ASSERT_LV_OR_TREENODE(LV_PTR) DBG_ASSERT(IS_PARENT_MV_SYM(LV_PTR) /* is "lv_val *" */ \ + || IS_LVAVLTREENODE(LV_PTR)) /* is "lvTreeNode *" or "lvTreeNodeFlt *" */ + +#define LV_IS_BASE_VAR(LV_PTR) (DBG_ASSERT_LV_OR_TREENODE(LV_PTR) \ + IS_PARENT_MV_SYM(LV_PTR)) + +/* Input to macro is LV_PTR which is guaranteed to be a non-base lv. Return value is the corresponding base_lv */ +#define LV_GET_BASE_VAR(LV_PTR) (DBG_ASSERT(!LV_IS_BASE_VAR(LV_PTR)) \ + DBG_ASSERT(NULL != LV_AVLNODE_PARENT(LV_PTR)) \ + LV_AVLNODE_PARENT(LV_PTR)->base_lv) + +#define LVT_GET_BASE_VAR(LVT) (DBG_ASSERT_LVT(LVT) \ + LVT->base_lv) + +#define LVT_GET_SYMVAL(LVT) (DBG_ASSERT_LVT(LVT) \ + LV_GET_SYMVAL(LVT_GET_BASE_VAR(LVT))) + +#define LV_GET_PARENT_TREE(LV_PTR) (DBG_ASSERT(!LV_IS_BASE_VAR(LV_PTR)) \ + DBG_ASSERT(NULL != LV_AVLNODE_PARENT(LV_PTR)) \ + LV_AVLNODE_PARENT(LV_PTR)) + +/* The following 3 macros operate on the ptrs.val_ent.children field. + * If the situation demands returning the children ptr, then use LV_GET_CHILD or LV_CHILD macros in that order of preference. + * If the situation demands checking if any children exist, then use LV_HAS_CHILD macro. + */ +#define LV_CHILD(LV_PTR) (((lv_val *)LV_PTR)->ptrs.val_ent.children) + +/* The following macro is an enhanced version of LV_CHILD that can be used wherever you would prefer or dont mind additional + * assert checking. In some cases, this cannot be used (e.g. in the left hand side of an assignment operation). But otherwise + * it is preferrable to use this macro as opposed to LV_CHILD. + */ +#define LV_GET_CHILD(LV_PTR) (DBG_ASSERT_LV_OR_TREENODE(LV_PTR) \ + LV_CHILD(LV_PTR)) \ + +/* if an lv_ptr's chlidren tree pointer is non-NULL, we should be guaranteed (by lv_kill) + * that there is at least one child node in that subscript tree (currently only an avl tree). + * assert that as well. + */ +#define LV_HAS_CHILD(LV_PTR) (DBG_ASSERT_LV_OR_TREENODE(LV_PTR) \ + (NULL != LV_CHILD(LV_PTR)) \ + DEBUG_ONLY(&& assert(MV_LV_TREE == (LV_CHILD(LV_PTR))->ident)) \ + DEBUG_ONLY(&& assert((LV_CHILD(LV_PTR))->avl_height))) + +/* Like the LV_CHILD and LV_GET_CHILD macro variants, the below macros need to be used with preference to LV_GET_SYMVAL + * unless it is needed in the left hand side of an assignment in which case use the LV_SYMVAL macro. + */ +#define LV_SYMVAL(BASE_VAR) ((BASE_VAR)->ptrs.val_ent.parent.sym) + +#define LV_GET_SYMVAL(BASE_VAR) (DBG_ASSERT(LV_IS_BASE_VAR(BASE_VAR)) \ + LV_SYMVAL(BASE_VAR)) + +/* The below macro relies on the fact that the parent field is at the same offset in an "lv_val" as it is in a "lvTreeNode". + * This is asserted in "LV_TREE_CREATE" macro. + */ +#define LV_GET_PARENT(LV_PTR) (DBG_ASSERT_LV_OR_TREENODE(LV_PTR) \ + LV_AVLNODE_PARENT(LV_PTR)) +#define LVT_GET_PARENT(LVT) (DBG_ASSERT_LVT(LVT) \ + LVT_PARENT(LVT)) + +#define LV_IS_VAL_DEFINED(LV_PTR) (DBG_ASSERT_LV_OR_TREENODE(LV_PTR) \ + MV_DEFINED(&((lv_val *)LV_PTR)->v)) + +#define LV_SBS_DEPTH(LV_PTR, IS_BASE_VAR, DEPTH) \ +{ \ + assert(IS_OFFSET_AND_SIZE_MATCH(lvTree, sbs_depth, symval, sbs_depth)); \ + assert(!IS_BASE_VAR || (0 == LV_PTR->ptrs.val_ent.parent.sbs_tree->sbs_depth)); \ + assert(IS_BASE_VAR || (0 < LV_PTR->ptrs.val_ent.parent.sbs_tree->sbs_depth)); \ + DEPTH = LV_PTR->ptrs.val_ent.parent.sbs_tree->sbs_depth; \ +} + +/* Mark mval held by lv_ptr to be undefined. Also lets stp_gcol and lv_gcol know to NOT protect + * (from garbage collection) any strings this lv_val was pointing to at the time of the free. + */ +#define LV_VAL_CLEAR_MVTYPE(LVPTR) (LVPTR)->v.mvtype = 0; /* note: also clears any use as MV_ALIASCONT */ + +/* Queue an lv_val block back on the lv_val free list at the given anchor. + * Operations: + * 1) Debugging aids in debug builds. + * 2) Do the queueing. + * 3) Clear the mv_type so it is definitely a deleted value. + * + * Callers should use LV_FREESLOT instead of directly invoking LV_FLIST_ENQUEUE. + * There are few exceptions like unw_mv_ent.c. + */ +#define LV_FLIST_ENQUEUE(flist_ptr, lv_ptr) \ +{ \ + lv_val **savflist_ptr = (flist_ptr); \ + DBGRFCT((stderr, "\n<< Free list queueing of lv_val at 0x"lvaddr" by %s line %d\n", \ + (lv_ptr), __FILE__, __LINE__)); \ + assert(LV_IS_BASE_VAR(lv_ptr)); \ + /* assert that any subtree underneath this lv_ptr has already been freed up */ \ + assert(NULL == LV_CHILD(lv_ptr)); \ + LV_VAL_CLEAR_MVTYPE(lv_ptr); \ + DEBUG_ONLY(memset((lv_ptr), 0xfd, SIZEOF(lv_val))); \ + (lv_ptr)->ptrs.free_ent.next_free = *savflist_ptr; \ + *savflist_ptr = (lv_ptr); \ + LV_SYMVAL(lv_ptr) = NULL; \ + DBGALS_ONLY((lv_ptr)->lvmon_mark = FALSE); \ +} + +/* Increment the cycle for tstarts. Field is compared to same name field in lv_val to signify an lv_val has been seen + during a given transaction so reference counts are kept correct. If counter wraps, clear all the counters in all + accessible lv_vals. +*/ +#define INCR_TSTARTCYCLE \ +{ \ + symval *lvlsymtab; \ + lv_blk *lvbp; \ + lv_val *lvp, *lvp_top; \ + \ + if (0 == ++tstartcycle) \ + { /* Set tstart cycle in all active lv_vals to 0 */ \ + for (lvlsymtab = curr_symval; lvlsymtab; lvlsymtab = lvlsymtab->last_tab) \ + for (lvbp = curr_symval->lv_first_block; lvbp; lvbp = lvbp->next) \ + for (lvp = (lv_val *)LV_BLK_GET_BASE(lvbp), lvp_top = LV_BLK_GET_FREE(lvbp, lvp); \ + lvp < lvp_top; lvp++) \ + lvp->stats.tstartcycle = 0; \ + tstartcycle = 1; \ + } \ +} + +/* Increment the cycle for misc lv tasks. Field is compared to same name field in lv_val to signify an lv_val has been seen + during a given transaction so reference counts are kept correct. If counter wraps, clear all the counters in all + accessible lv_vals. +*/ +#define INCR_LVTASKCYCLE \ +{ \ + symval *lvlsymtab; \ + lv_blk *lvbp; \ + lv_val *lvp, *lvp_top; \ + \ + if (0 == ++lvtaskcycle) \ + { /* Set tstart cycle in all active lv_vals to 0 */ \ + for (lvlsymtab = curr_symval; lvlsymtab; lvlsymtab = lvlsymtab->last_tab) \ + for (lvbp = curr_symval->lv_first_block; lvbp; lvbp = lvbp->next) \ + for (lvp = (lv_val *)LV_BLK_GET_BASE(lvbp), lvp_top = LV_BLK_GET_FREE(lvbp, lvp); \ + lvp < lvp_top; lvp++) \ + lvp->stats.lvtaskcycle = 0; \ + lvtaskcycle = 1; \ + } \ +} + +/* Initialize given lv_val (should be of type "lv_val *" and not "lvTreeNode *") */ +#define LVVAL_INIT(lv, symvalarg) \ +{ \ + DBGALS_ONLY(GBLREF boolean_t lvmon_enabled;) \ + assert(MV_SYM == symvalarg->ident); /* ensure above macro is never used to initialize a "lvTreeNode *" */ \ + (lv)->v.mvtype = 0; \ + (lv)->stats.trefcnt = 1; \ + (lv)->stats.crefcnt = 0; \ + (lv)->stats.tstartcycle = 0; \ + (lv)->stats.lvtaskcycle = 0; \ + (lv)->has_aliascont = FALSE; \ + DBGALS_ONLY(if (lvmon_enabled) (lv)->lvmon_mark = TRUE; else (lv)->lvmon_mark = FALSE); \ + (lv)->tp_var = NULL; \ + LV_CHILD(lv) = NULL; \ + LV_SYMVAL(lv) = symvalarg; \ +} + +/* Macro to call lv_var_clone and set the cloned status in the tp_var structure. + * Note that we want the cloned tree to have its base_lv point back to "lv" and not the cloned lv. + * This is because in case we want to restore the lv, we can then safely move the saved tree + * back to "lv" without then having to readjust the base_lv linked in the "lvTree *" structures beneath + */ +#define TP_VAR_CLONE(lv) \ +{ \ + lv_val *lcl_savelv; \ + tp_var *lcl_tp_var; \ + \ + assert(LV_IS_BASE_VAR(lv)); \ + lcl_tp_var = (lv)->tp_var; \ + assert(lcl_tp_var); \ + assert(!lcl_tp_var->var_cloned); \ + lcl_savelv = lcl_tp_var->save_value; \ + assert(NULL != lcl_savelv); \ + assert(NULL == LV_CHILD(lcl_savelv)); \ + LV_CHILD(lcl_savelv) = LV_CHILD(lv); \ + lv_var_clone(lcl_savelv, lv); \ + lcl_tp_var->var_cloned = TRUE; \ +} + +/* Macro to indicate if a given lv_val is an alias or not */ +#define IS_ALIASLV(lv) (DBG_ASSERT(LV_IS_BASE_VAR(lv)) ((1 < (lv)->stats.trefcnt) || (0 < (lv)->stats.crefcnt)) \ + DEBUG_ONLY(&& assert(IS_PARENT_MV_SYM(lv)))) + + +#define LV_NEWBLOCK_INIT_ALLOC 16 +#define LV_BLK_GET_BASE(LV_BLK) (((sm_uc_ptr_t)LV_BLK) + SIZEOF(lv_blk)) +#define LV_BLK_GET_FREE(LV_BLK, LVBLK_BASE) (&LVBLK_BASE[LV_BLK->numUsed]) + +typedef struct lv_val_struct +{ + mval v; /* Value associated with this lv_val */ + /* Note: The offsets of "ptrs.val_ent.children" and "ptrs.val_ent.parent.sym" are relied upon by + * other modules (e.g. lv_tree.h) and asserted in LV_TREE_CREATE macro. */ + union + { + struct + { + lvTree *children; + union + { /* Note these two fields are still available when mvtype == MV_LVCOPIED + * and there is code in "als_check_xnew_var_aliases" that depends on this + */ + struct symval_struct *sym; + lvTree *sbs_tree; + } parent; + } val_ent; + struct + { + struct lv_val_struct *next_free; + } free_ent; + struct + { /* When xnew'd lv's are copied to previous symtab, their new root is + * set here so multiple references can be resolved properly (mvtype == MV_LVCOPIED) + */ + struct lv_val_struct *newtablv; + } copy_loc; + } ptrs; + struct + { /* Note these flags are irrelevant for other than base (unsubscripted) local vars */ + int4 trefcnt; /* Total refcnt (includes container vars) */ + int4 crefcnt; /* Container reference count */ + uint4 tstartcycle; /* Cycle of level 0 tstart command */ + uint4 lvtaskcycle; /* Cycle of various lv related tasks */ + } stats; + boolean_t has_aliascont; /* This base var has or had an alias container in it */ + boolean_t lvmon_mark; /* This lv_val is being monitored; Used only #ifdef DEBUG_ALIAS */ + struct tp_var_struct *tp_var; +} lv_val; + +typedef struct lv_blk_struct +{ + struct lv_blk_struct *next; + uint4 numAlloc; + uint4 numUsed; +} lv_blk; + +/* When op_xnew creates a symtab, these blocks will describe the vars that were passed through from the + previous symtab. They need special alias processing. Note we keep our own copy of the key (rather than + pointing to the hash table entry) since op_xnew processing can cause a hash table expansion and we have + no good way to update pointers so save the hash values as part of the key to eliminate another lookup. +*/ +typedef struct lv_xnew_var_struct +{ + struct lv_xnew_var_struct *next; + mname_entry key; + lv_val *lvval; /* There are two uses for this field. In op_new, it is used to + hold the previous lvval addr for the 2nd pass. In unwind + processing (als_check_xnew_var_aliases) it holds the lvval + in the symtab being popped since it cannot be obtained once the + symtab entry is deleted in the first pass (step 2). + */ +} lv_xnew_var; +/* While lv_xnew_var_struct are the structures that were explicitly passed through, this is a list of the structures + that are pointed to by any container vars in any of the passed through vars and any of the vars those point to, etc. + The objective is to come up with a definitive list of structures to search to see if containers got created in them + that point to the structure being torn down. +*/ +typedef struct lv_xnewref_struct +{ + struct lv_xnewref_struct *next; + lv_val *lvval; /* This structure can be addressed through the passed thru vars + but is not itself one of them */ +} lv_xnew_ref; + +typedef struct symval_struct +{ + unsigned short ident; + unsigned short sbs_depth; /* is always 0. Defined to match offset & size of lvTree->sbs_depth. + * This way callers can avoid an if check depending on whether + * the input pointer is a "symval *" or a "lvTree *" type. + * This is also asserted in "LV_TREE_CREATE" macro. */ + boolean_t tp_save_all; + lv_xnew_var *xnew_var_list; + lv_xnew_ref *xnew_ref_list; + hash_table_mname h_symtab; + lv_blk *lv_first_block; + lv_blk *lvtree_first_block; + lv_blk *lvtreenode_first_block; + lv_val *lv_flist; + lvTree *lvtree_flist; + lvTreeNode *lvtreenode_flist; + struct symval_struct *last_tab; + int4 symvlvl; /* Level of symval struct (nesting) */ + boolean_t trigr_symval; /* Symval is owned by a trigger */ + boolean_t alias_activity; +} symval; + +/* Structure to describe the block allocated to describe a var specified on a TSTART to be restored + on a TP restart. Block moved here from tpframe.h due to the structure references it [now] makes. + Block can take two forms: (1) the standard tp_data form which marks vars to be modified or (2) the + tp_change form where a new symbol table was stacked. +*/ +typedef struct tp_var_struct +{ + struct tp_var_struct *next; + struct lv_val_struct *current_value; + struct lv_val_struct *save_value; + mname_entry key; + boolean_t var_cloned; + GTM64_ONLY(int4 filler;) +} tp_var; + +typedef struct lvname_info_struct +{ + intszofptr_t total_lv_subs; /* Total subscripts + 1 for name itself */ + lv_val *start_lvp; + mval *lv_subs[MAX_LVSUBSCRIPTS]; + lv_val *end_lvp; +} lvname_info; +typedef lvname_info *lvname_info_ptr; + +#define DOTPSAVE_FALSE FALSE /* macro to indicate parameter by name "dotpsave" is passed a value of "FALSE" */ +#define DOTPSAVE_TRUE TRUE /* macro to indicate parameter by name "dotpsave" is passed a value of "TRUE" */ + +#define DO_SUBTREE_FALSE FALSE /* macro to indicate parameter by name "do_subtree" is passed a value of "FALSE" */ +#define DO_SUBTREE_TRUE TRUE /* macro to indicate parameter by name "do_subtree" is passed a value of "TRUE" */ + +#define LV_FREESLOT(LV) \ +{ \ + symval *sym; \ + \ + assert(LV_IS_BASE_VAR(LV)); \ + sym = LV_GET_SYMVAL(LV); \ + LV_FLIST_ENQUEUE(&sym->lv_flist, LV); \ +} + +#define LVTREE_FREESLOT(LVT) \ +{ \ + symval *sym; \ + \ + sym = LVT_GET_SYMVAL(LVT); \ + assert(NULL != LVT_GET_PARENT(LVT)); \ + LVT_PARENT(LVT) = NULL; /* indicates this is free */ \ + /* avl_root is overloaded to store linked list in free state */ \ + LVT->avl_root = (lvTreeNode *)sym->lvtree_flist; \ + sym->lvtree_flist = LVT; \ +} + +#define LVTREENODE_FREESLOT(LV) \ +{ \ + symval *sym; \ + lv_val *base_lv; \ + \ + assert(!LV_IS_BASE_VAR(LV)); \ + base_lv = LV_GET_BASE_VAR(LV); \ + sym = LV_GET_SYMVAL(base_lv); \ + LV_AVLNODE_PARENT(LV) = NULL; /* indicates to stp_gcol this is free */ \ + /* sbs_child is overloaded to store linked list in free state */ \ + (LV)->sbs_child = (lvTree *)sym->lvtreenode_flist; \ + sym->lvtreenode_flist = LV; \ +} + +unsigned char *format_lvname(lv_val *start, unsigned char *buff, int size); +lv_val *lv_getslot(symval *sym); +lvTree *lvtree_getslot(symval *sym); +lvTreeNode *lvtreenode_getslot(symval *sym); + +void lv_kill(lv_val *lv, boolean_t dotpsave, boolean_t do_subtree); +void lv_killarray(lvTree *lvt, boolean_t dotpsave); +void lv_newblock(symval *sym, int numElems); +void lv_newname(ht_ent_mname *hte, symval *sym); +void lvtree_newblock(symval *sym, int numElems); +void lvtreenode_newblock(symval *sym, int numElems); +void lv_var_clone(lv_val *clone_var, lv_val *base_lv); +void lvzwr_var(lv_val *lv, int4 n); + +void op_clralsvars(lv_val *dst); +void op_forfreeindx(void); +void op_fornestlvl(uint4 level); +void op_fndata(lv_val *x, mval *y); +void op_fnzdata(lv_val *x, mval *y); +void op_fnincr(lv_val *local_var, mval *increment, mval *result); +void op_fnnext(lv_val *src,mval *key,mval *dst); +void op_fno2(lv_val *src,mval *key,mval *dst,mval *direct); +void op_fnorder(lv_val *src, mval *key, mval *dst); +void op_fnzahandle(lv_val *src, mval *dst); +void op_fnzprevious(lv_val *src, mval *key, mval *dst); +void op_kill(lv_val *lv); +void op_killalias(int srcindx); +void op_lvzwithdraw(lv_val *lv); +void op_setals2als(lv_val *src, int dstindx); +void op_setalsin2alsct(lv_val *src, lv_val *dst); +void op_setalsctin2als(lv_val *src, int dstindx); +void op_setalsct2alsct(lv_val *src, lv_val *dst); +void op_setfnretin2als(mval *srcmv, int destindx); /* no an lv_val ref but kept here with its friends so it not lonely */ +void op_setfnretin2alsct(mval *srcmv, lv_val *dstlv); +void op_zshow(mval *func, int type, lv_val *lvn); + +lv_val *op_getindx(UNIX_ONLY_COMMA(int argcnt) lv_val *start, ...); +lv_val *op_putindx(UNIX_ONLY_COMMA(int argcnt) lv_val *start, ...); +lv_val *op_rfrshindx(uint4 level, boolean_t put); +lv_val *op_savputindx(UNIX_ONLY_COMMA(int argcnt) lv_val *start, ...); +lv_val *op_srchindx(UNIX_ONLY_COMMA(int argcnt_arg) lv_val *lv, ...); +lv_val *op_m_srchindx(UNIX_ONLY_COMMA(int4 count) lv_val *lvarg, ...); + +/* Function Prototypes for local variables functions of merge */ +boolean_t lcl_arg1_is_desc_of_arg2(lv_val *cur, lv_val *ref); +unsigned char *format_key_mvals(unsigned char *buff, int size, lvname_info *lvnp); +unsigned char *format_key_lv_val(lv_val *lvpin, unsigned char *buff, int size); +#endif diff --git a/sr_port/lv_var_clone.c b/sr_port/lv_var_clone.c new file mode 100644 index 0000000..58741ba --- /dev/null +++ b/sr_port/lv_var_clone.c @@ -0,0 +1,50 @@ +/**************************************************************** + * * + * Copyright 2009, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include /* For offsetof() macro */ + +#include "gtm_string.h" +#include "gtm_stdio.h" + +#include "lv_val.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "alias.h" + +/* Routine to clone the children of a tree. The input lv_val "clone_var" should be a clone of the base lv_val + * owning the tree we wish to clone. The pointers in this copy will be duplicated and the new tree linked to + * "clone_var". Note that the owning symval of clone_var should be set appropriately such as in the case of + * xnew processing where we are cloneing a tree out of one symtab and into another. The new tree will be + * created in the symtab of the input lv_val regardless of which symtab owns the processed lv_vals. + * + * Input "base_lv" is the base variable that the cloned tree should point back to. + */ +void lv_var_clone(lv_val *clone_var, lv_val *base_lv) +{ + lvTree *clone_lvt; + + assert(clone_var); + assert(LV_IS_BASE_VAR(clone_var)); + assert(base_lv); + assert(LV_IS_BASE_VAR(base_lv)); + DBGRFCT((stderr, "\nlv_var_clone: Cloning lv_val tree at 0x"lvaddr"\n", clone_var)); + clone_lvt = LV_GET_CHILD(clone_var); /* "clone_lvt" holds tree to be cloned as we build new tree for clone_var */ + if (NULL != clone_lvt) + { + assert(1 == clone_lvt->sbs_depth); + LV_TREE_CLONE(clone_lvt, (lvTreeNode *)clone_var, base_lv); + } +} diff --git a/sr_port/lvn.c b/sr_port/lvn.c new file mode 100644 index 0000000..c3f4a2e --- /dev/null +++ b/sr_port/lvn.c @@ -0,0 +1,94 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "subscript.h" +#include "advancewindow.h" + +GBLREF char window_token; +GBLREF mident window_ident; + +int lvn(oprtype *a,opctype index_op,triple *parent) +{ + oprtype subscripts[MAX_LVSUBSCRIPTS],*sb1,*sb2,*sb; + triple *ref,*s, *root; + char x; + error_def(ERR_MAXNRSUBSCRIPTS); + error_def(ERR_RPARENMISSING); + error_def(ERR_VAREXPECTED); + + if (window_token != TK_IDENT) + { + stx_error(ERR_VAREXPECTED); + return FALSE; + } + *a = put_mvar(&window_ident); + advancewindow(); + if (window_token != TK_LPAREN) + return TRUE; + assert(a->oprclass == TRIP_REF); + ref = a->oprval.tref; + assert(ref->opcode == OC_VAR); + sb1 = sb2 = subscripts; + *sb1++ = *a; + for (;;) + { + if (sb1 >= ARRAYTOP(subscripts)) + { + stx_error(ERR_MAXNRSUBSCRIPTS); + return FALSE; + } + advancewindow(); + if (!expr(sb1++)) + return FALSE; + if ((x = window_token) == TK_RPAREN) + { + advancewindow(); + break; + } + if (x != TK_COMMA) + { + stx_error(ERR_RPARENMISSING); + return FALSE; + } + } + if (parent) + { /* only $ORDER, $NEXT, $ZPREV have parent */ + sb1--; + if (sb1 - sb2 == 1) /* only name and 1 subscript */ + { /* SRCHINDX not necessary if only 1 subscript */ + sb = &parent->operand[1]; *sb = *sb1; + return TRUE; + } + } + + root = ref = newtriple(index_op); + ref->operand[0] = put_ilit((mint)(sb1 - sb2)); + while (sb2 < sb1) + { + s = newtriple(OC_PARAMETER); + ref->operand[1] = put_tref(s); + s->operand[0] = *sb2++; + ref = s; + } + if (parent) + { + parent->operand[0] = put_tref(root); + sb = &parent->operand[1]; + *sb = *sb2; + return TRUE; + } + *a = put_tref(root); + return TRUE; +} diff --git a/sr_port/lvzwr_arg.c b/sr_port/lvzwr_arg.c new file mode 100644 index 0000000..22682ca --- /dev/null +++ b/sr_port/lvzwr_arg.c @@ -0,0 +1,60 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "hashtab_mname.h" +#include "hashtab_addr.h" +#include "zwrite.h" + +GBLREF lvzwrite_datablk *lvzwrite_block; + +void lvzwr_arg(int t, mval *a1, mval *a2) +{ + int sub_idx; + + assert(lvzwrite_block); + sub_idx = lvzwrite_block->subsc_count++; + /* it would be good to guard the array sub_idx < sizeof... */ + if (a1) + { + MV_FORCE_DEFINED(a1); + if (MV_IS_CANONICAL(a1)) + MV_FORCE_NUMD(a1); + MV_FORCE_STRD(a1); + if ((ZWRITE_VAL != t) && (0 == a1->str.len)) /* value is real - leave it alone */ + a1 = NULL; + } + if (a2) + { + MV_FORCE_DEFINED(a2); + if (MV_IS_CANONICAL(a2)) + MV_FORCE_NUMD(a2); + MV_FORCE_STRD(a2); + if (0 == a2->str.len) /* can never be value */ + a2 = NULL; + } + ((zwr_sub_lst *)lvzwrite_block->sub)->subsc_list[sub_idx].subsc_type = t; + ((zwr_sub_lst *)lvzwrite_block->sub)->subsc_list[sub_idx].first = a1; + ((zwr_sub_lst *)lvzwrite_block->sub)->subsc_list[sub_idx].second = a2; + if ((ZWRITE_ASTERISK != t) && (ZWRITE_ALL != t)) + lvzwrite_block->mask |= 1 << sub_idx; + if (ZWRITE_VAL != t) + lvzwrite_block->fixed = FALSE; + return; +} diff --git a/sr_port/lvzwr_fini.c b/sr_port/lvzwr_fini.c new file mode 100644 index 0000000..835ac70 --- /dev/null +++ b/sr_port/lvzwr_fini.c @@ -0,0 +1,92 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "lv_val.h" +#include "rtnhdr.h" +#include "mv_stent.h" +#include "mlkdef.h" +#include "zshow.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "zwrite.h" +#include "op.h" +#include "patcode.h" + +GBLREF symval *curr_symval; +GBLREF lvzwrite_datablk *lvzwrite_block; +GBLREF zshow_out *zwr_output; + +error_def(ERR_UNDEF); + +void lvzwr_fini(zshow_out *out, int t) +{ + int4 size; + mval local; + mname_entry temp_key; + ht_ent_mname *tabent; + mident_fixed m; + + zwr_output = out; + assert(lvzwrite_block); + if (zwr_patrn_mident == lvzwrite_block->zwr_intype) + { /* Mident specified for "pattern" (fixed name, no pattern) */ + size = (lvzwrite_block->pat->str.len <= MAX_MIDENT_LEN) ? lvzwrite_block->pat->str.len : MAX_MIDENT_LEN; + temp_key.var_name = lvzwrite_block->pat->str; + COMPUTE_HASH_MNAME(&temp_key); + tabent = lookup_hashtab_mname(&curr_symval->h_symtab, &temp_key); + if (!tabent || !LV_IS_VAL_DEFINED(tabent->value) && !LV_HAS_CHILD(tabent->value)) + { + lvzwrite_block->subsc_count = 0; + rts_error(VARLSTCNT(4) ERR_UNDEF, 2, size, lvzwrite_block->pat->str.addr); + } else + { + lvzwrite_block->curr_name = &tabent->key.var_name; + lvzwr_var(((lv_val *)tabent->value), 0); + } + } else + { /* mval specified for character "pattern" (pattern matching) */ + assert(zwr_patrn_mval == lvzwrite_block->zwr_intype); + memset(m.c, 0, SIZEOF(m.c)); + local.mvtype = MV_STR; + local.str.addr = &m.c[0]; + local.str.len = 1; + m.c[0] = '%'; /* Starting variable name for search (first possible varname) */ + + lvzwrite_block->fixed = FALSE; + while (local.str.len) + { + if (do_pattern(&local, lvzwrite_block->pat)) + { + memset(&m.c[local.str.len], 0, SIZEOF(m.c) - local.str.len); + temp_key.var_name = local.str; + COMPUTE_HASH_MNAME(&temp_key); + if (NULL != (tabent = lookup_hashtab_mname(&curr_symval->h_symtab, &temp_key))) + { + lvzwrite_block->curr_name = &tabent->key.var_name; + lvzwr_var(((lv_val *)tabent->value), 0); + } + } + op_fnlvname(&local, TRUE, &local); + assert(local.str.len <= MAX_MIDENT_LEN); + memcpy(&m.c[0], local.str.addr, local.str.len); + local.str.addr = &m.c[0]; + } + } + lvzwrite_block->curr_subsc = lvzwrite_block->subsc_count = 0; + return; +} diff --git a/sr_port/lvzwr_init.c b/sr_port/lvzwr_init.c new file mode 100644 index 0000000..11e4012 --- /dev/null +++ b/sr_port/lvzwr_init.c @@ -0,0 +1,72 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "zwrite.h" +#include "subscript.h" +#include "mlkdef.h" +#include "zshow.h" +#include "alias.h" + +GBLREF lvzwrite_datablk *lvzwrite_block; +GBLREF int merge_args; +GBLREF symval *curr_symval; +GBLREF uint4 zwrtacindx; + + +void lvzwr_init(enum zwr_init_types zwrpattyp, mval *val) +{ + lvzwrite_datablk *prevzwrb; + + /* Standard call at start of zwrite type functions. If this symval has aliases in it, + * prep a hash table we will use to track the lv_val addrs we process (but only if not merging). + */ + if (!merge_args) + { /* Re-initialize table even if no "aliases" defined since dotted parms are actually aliases too and + * will be placed in this table by lvzwr_out(). + */ + als_zwrhtab_init(); + zwrtacindx = 0; + } + if (!lvzwrite_block) + { + lvzwrite_block = (lvzwrite_datablk *)malloc(SIZEOF(lvzwrite_datablk)); + memset(lvzwrite_block, 0, SIZEOF(lvzwrite_datablk)); + } else + { /* Get back to one zwrite_block if multiples were stacked (and left over) */ + for (prevzwrb = lvzwrite_block->prev; prevzwrb; lvzwrite_block = prevzwrb, prevzwrb = lvzwrite_block->prev) + { + if (lvzwrite_block->sub) + free(lvzwrite_block->sub); + free(lvzwrite_block); + } + } + lvzwrite_block->zwr_intype = zwrpattyp; + if (!merge_args && val) + { /* val may be null when called from gtm_startup/gtm$startup */ + MV_FORCE_STR(val); + lvzwrite_block->pat = val; + } else + lvzwrite_block->pat = NULL; + lvzwrite_block->mask = lvzwrite_block->subsc_count = 0; + if (!lvzwrite_block->sub) + lvzwrite_block->sub = (zwr_sub_lst *)malloc(SIZEOF(zwr_sub_lst) * MAX_LVSUBSCRIPTS); + lvzwrite_block->fixed = TRUE; + return; +} diff --git a/sr_port/lvzwr_key.c b/sr_port/lvzwr_key.c new file mode 100644 index 0000000..2a18cd3 --- /dev/null +++ b/sr_port/lvzwr_key.c @@ -0,0 +1,70 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "min_max.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "zwrite.h" +#include "gtm_string.h" + +GBLREF lvzwrite_datablk *lvzwrite_block; + +unsigned char *lvzwr_key(unsigned char *buff, int size) +{ + int sub_idx, len; + mstr sub; + unsigned char *cp, *cq; + + assert(lvzwrite_block); + len = MIN(size, lvzwrite_block->curr_name->len); + assert(MAX_MIDENT_LEN >= len); + memcpy(buff, lvzwrite_block->curr_name->addr, len); + size -= len; + buff += len; + + if (lvzwrite_block->subsc_count) + { + if (size) + { + *buff++ = '('; + size--; + } + for (sub_idx = 0; ; ) + { + mval_lex(((zwr_sub_lst *)lvzwrite_block->sub)->subsc_list[sub_idx].actual, &sub); + if (0 <= (size -= sub.len)) + { + memcpy(buff, sub.addr, sub.len); + buff += sub.len; + } else + break; + if (++sub_idx < lvzwrite_block->curr_subsc && size) + { + *buff++ = ','; + size--; + } else + { + if (size) + { + *buff++ = ')'; + size--; + } + break; + } + } + } + return buff; +} diff --git a/sr_port/lvzwr_out.c b/sr_port/lvzwr_out.c new file mode 100644 index 0000000..f30d9f2 --- /dev/null +++ b/sr_port/lvzwr_out.c @@ -0,0 +1,311 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gtm_stdio.h" + +#include "lv_val.h" +#include "gdsroot.h" +#include "gdskill.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "zwrite.h" +#include "mlkdef.h" +#include "zshow.h" +#include "filestruct.h" +#include "gdscc.h" +#include "copy.h" +#include "jnl.h" +#include "buddy_list.h" +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" +#include "merge_def.h" +#include "gvname_info.h" +#include "op_merge.h" +#include "op.h" +#include "gtmmsg.h" +#include "sgnl.h" +#include "stringpool.h" +#include "alias.h" +#include "callg.h" + +GBLREF lvzwrite_datablk *lvzwrite_block; +GBLREF zshow_out *zwr_output; +GBLREF gv_namehead *gv_target; +GBLREF gv_key *gv_currkey; +GBLREF int merge_args; +GBLREF merge_glvn_ptr mglvnp; +GBLREF gd_region *gv_cur_region; +GBLREF symval *curr_symval; +GBLREF zwr_hash_table *zwrhtab; /* Used to track aliases during zwrites */ +GBLREF uint4 zwrtacindx; /* When creating $ZWRTACxxx vars for ZWRite, this holds xxx */ + +LITDEF MSTR_CONST(semi_star, " ;*"); +LITDEF MSTR_CONST(dzwrtac_clean, "$ZWRTAC=\"\""); + +error_def(ERR_MAXNRSUBSCRIPTS); +error_def(ERR_MERGEINCOMPL); + +void lvzwr_out_targkey(mstr *one); + + +void lvzwr_out_targkey(mstr *one) +{ + int n, nsubs; + + zshow_output(zwr_output, lvzwrite_block->curr_name); + nsubs = lvzwrite_block->curr_subsc; + if (nsubs) + { + *one->addr = '('; + zshow_output(zwr_output, one); + for (n = 0 ; ; ) + { + mval_write(zwr_output, ((zwr_sub_lst *)lvzwrite_block->sub)->subsc_list[n].actual, FALSE); + if (++n < nsubs) + { + *one->addr = ','; + zshow_output(zwr_output, one); + } else + { + *one->addr = ')'; + zshow_output(zwr_output, one); + break; + } + } + } +} + + +void lvzwr_out(lv_val *lvp) +{ + char buff; + uchar_ptr_t lastc; + int n, nsubs, sbs_depth; + lv_val *dst_lv, *res_lv, *lvpc; + mstr one; + mval *subscp, *val, outindx; + ht_ent_addr *tabent_addr; + ht_ent_mname *tabent_mname; + boolean_t htent_added, dump_container; + zwr_alias_var *newzav, *zav; + mident_fixed zwrt_varname; + lvzwrite_datablk *newzwrb; + gparam_list param_list; /* for op_putindx call through callg */ + + val = &lvp->v; + assert(lvzwrite_block); + if (!merge_args) + { /* The cases that exist here are: + * 1) This is a container variable. If the lv_val it refers to has been printed, show that association. + * Else, "create" a $ZWRTACxxx var/index that will define the value. Then before returning, cause + * that container var to be dumped with the appropriate $ZWRTACxxx index as the var name. + * 2) This is an alias base variable. If first time seen, we print normally but record it and put a + * ";#" tag on the end to signify it is an alias var (doesn't affect value). If we look it up and it + * is not the first time this lv_val has been printed, then we instead print the statement needed to + * alias it to the first seen var. + * 3) This is just a normal var needing to be printed normally. + */ + htent_added = FALSE; + one.addr = &buff; + one.len = 1; + lvzwrite_block->zav_added = FALSE; + if (lvp->v.mvtype & MV_ALIASCONT) + { /* Case 1 -- have an alias container */ + assert(curr_symval->alias_activity); + assert(!LV_IS_BASE_VAR(lvp)); /* verify is subscripted var */ + lvpc = (lv_val *)lvp->v.str.addr; + assert(lvpc); + assert(LV_IS_BASE_VAR(lvpc)); /* Verify base var lv_val */ + if (tabent_addr = (ht_ent_addr *)lookup_hashtab_addr(&zwrhtab->h_zwrtab, (char **)&lvpc)) + { /* The value was found, we have a reference we can print now */ + assert(HTENT_VALID_ADDR(tabent_addr, zwr_alias_var, zav)); + *one.addr = '*'; + zshow_output(zwr_output, &one); + lvzwr_out_targkey(&one); + *one.addr = '='; + zshow_output(zwr_output, &one); + zav = (zwr_alias_var *)tabent_addr->value; + assert(0 < zav->zwr_var.len); + zwr_output->flush = TRUE; + zshow_output(zwr_output, (const mstr *)&zav->zwr_var); + return; + } + /* This lv_val isn't known to us yet. Scan the hash curr_symval hash table to see if it is known as a + * base variable as we could have a "forward reference" here. + */ + tabent_mname = als_lookup_base_lvval(lvpc); + /* note even though both paths below add a zav, not bothering to set zav_added because that flag is + * really only (currently) cared about in reference to processing a basevar so we wouldn't + * be in this code path anyway. Comment here to record potential usage if that changes. + */ + if (tabent_mname) + { /* Found a base var it can reference -- create a zwrhtab entry for it */ + assert(tabent_mname->key.var_name.len); + newzav = als_getzavslot(); + newzav->zwr_var = tabent_mname->key.var_name; + htent_added = add_hashtab_addr(&zwrhtab->h_zwrtab, (char **)&lvpc, newzav, &tabent_addr); + assert(htent_added); + dump_container = FALSE; + } else + { /* Unable to find lv_val .. must be "orphaned" so we generate a new $ZWRTAC var for it. The first + * check however is if this is the first $ZWRTAC var being generated for this $ZWR. If yes, generate + * a $ZWRTAC="" line to preceed it. This will be a flag to load to clear out all existing $ZWRTAC + * temp vars so there is no pollution between loads of ZWRitten data. + */ + if (0 == zwrtacindx++) + { /* Put out "dummy" statement that will clear all the $ZWRTAC vars for a clean slate */ + zwr_output->flush = TRUE; + zshow_output(zwr_output, &dzwrtac_clean); + } + MEMCPY_LIT(zwrt_varname.c, DOLLAR_ZWRTAC); + lastc = i2asc((uchar_ptr_t)zwrt_varname.c + STR_LIT_LEN(DOLLAR_ZWRTAC), zwrtacindx); + newzav = als_getzavslot(); + newzav->zwr_var.addr = zwrt_varname.c; + newzav->zwr_var.len = INTCAST(((char *)lastc - &zwrt_varname.c[0])); + s2pool(&newzav->zwr_var); + htent_added = add_hashtab_addr(&zwrhtab->h_zwrtab, (char **)&lvpc, newzav, &tabent_addr); + assert(htent_added); + dump_container = TRUE; + } + /* Note value_printed flag in newzav not set since we are NOT dumping the value at this point + * but only the association. Since the flag is not set, we *will* dump it when we get to that + * actual variable. + */ + *one.addr = '*'; + zshow_output(zwr_output, &one); + lvzwr_out_targkey(&one); + *one.addr = '='; + zshow_output(zwr_output, &one); + zwr_output->flush = TRUE; + zshow_output(zwr_output, (const mstr *)&newzav->zwr_var); + if (dump_container) + { /* We want to dump the entire container variable but the name doesn't match the var we are + * currently dumping so push a new lvzwrite_block onto the stack, fill it in for the current var + * and call lvzwr_var() to handle it. When done, dismantle the temp lvzwrite_block. + */ + newzwrb = (lvzwrite_datablk *)malloc(SIZEOF(lvzwrite_datablk)); + memset(newzwrb, 0, SIZEOF(lvzwrite_datablk)); + newzwrb->sub = (zwr_sub_lst *)malloc(SIZEOF(zwr_sub_lst) * MAX_LVSUBSCRIPTS); + newzwrb->curr_name = &newzav->zwr_var; + newzwrb->prev = lvzwrite_block; + lvzwrite_block = newzwrb; + lvzwr_var(lvpc, 0); + assert(newzav->value_printed); + assert(newzwrb == lvzwrite_block); + free(newzwrb->sub); + lvzwrite_block = newzwrb->prev; + free(newzwrb); + } + return; + } else if (LV_IS_BASE_VAR(lvp) && IS_ALIASLV(lvp)) + { /* Case 2 -- alias base variable (only base vars have reference counts). Note this can occur with + * TP save/restore vars since we increment both trefcnt and crefcnt for these hidden copied references. + * Because of that, we can't assert alias_activity but otherwise it shouldn't affect processing. + */ + if (!(htent_added = add_hashtab_addr(&zwrhtab->h_zwrtab, (char **)&lvp, NULL, &tabent_addr))) + { /* Entry already existed -- need to output association rather than values */ + assert(tabent_addr); + zav = (zwr_alias_var *)tabent_addr->value; + assert(zav); + if (zav->value_printed) + { /* Value has already been output -- print association this time */ + *one.addr = '*'; /* Flag as creating an alias */ + zshow_output(zwr_output, &one); + /* Now for (new) variable name */ + zshow_output(zwr_output, lvzwrite_block->curr_name); + *one.addr = '='; + zshow_output(zwr_output, &one); + /* .. and the var name aliasing to (the first seen with this lv_val) */ + assert(zav->zwr_var.len); + zwr_output->flush = TRUE; + zshow_output(zwr_output, &zav->zwr_var); + return; + } + /* Else the value for this entry has not yet been printed so let us fall into case 3 + * and get that done. Also set the flag so we mark it as an alias. Note this can happen if + * a container value for a name is encountered before the base var it points to. We will + * properly resolve the entry but its value won't have been printed until we actually encounter + * it in the tree. + */ + htent_added = TRUE; /* to force the ;# tag at end of value printing */ + zav->value_printed = TRUE; /* value will be output shortly below */ + } else + { /* Entry was added so is first appearance -- give it a value to hold onto and print it */ + newzav = als_getzavslot(); + newzav->zwr_var = *lvzwrite_block->curr_name; + newzav->value_printed = TRUE; /* or rather it will be shortly.. */ + tabent_addr->value = (void *)newzav; + lvzwrite_block->zav_added = TRUE; + /* Note fall into case 3 to print var and value if exists */ + } + } + /* Case 3 - everything else */ + if (!MV_DEFINED(val)) + return; + MV_FORCE_STR(val); + lvzwr_out_targkey(&one); + *one.addr = '='; + zshow_output(zwr_output, &one); + mval_write(zwr_output, val, !htent_added); + if (htent_added) + { /* output the ";#" tag to indicate this is an alias output */ + zwr_output->flush = TRUE; + zshow_output(zwr_output, &semi_star); + } + } else + { /* MERGE assignment from local variable */ + nsubs = lvzwrite_block->curr_subsc; + if (MARG1_IS_GBL(merge_args)) + { /* Target global var */ + memcpy(gv_currkey->base, mglvnp->gblp[IND1]->s_gv_currkey->base, mglvnp->gblp[IND1]->s_gv_currkey->end + 1); + gv_currkey->end = mglvnp->gblp[IND1]->s_gv_currkey->end; + for (n = 0 ; n < nsubs; n++) + { + subscp = ((zwr_sub_lst *)lvzwrite_block->sub)->subsc_list[n].actual; + MV_FORCE_STR(subscp); + mval2subsc(subscp, gv_currkey); + if (!subscp->str.len && (ALWAYS != gv_cur_region->null_subs)) + sgnl_gvnulsubsc(); + } + MV_FORCE_STR(val); + op_gvput(val); + } else + { /* Target local var - pre-process target in case is a container */ + assert(MARG1_IS_LCL(merge_args)); + dst_lv = mglvnp->lclp[IND1]; + if (!LV_IS_BASE_VAR(dst_lv)) + { + LV_SBS_DEPTH(dst_lv, FALSE, sbs_depth); + if (MAX_LVSUBSCRIPTS < (sbs_depth + nsubs)) + rts_error(VARLSTCNT(3) ERR_MERGEINCOMPL, 0, ERR_MAXNRSUBSCRIPTS); + } + param_list.arg[0] = dst_lv; /* this is already protected from stp_gcol by op_merge so no need to + * push this into the stack for stp_gcol protection. */ + for (n = 0 ; n < nsubs; n++) + { /* Note: no need to do push these mvals on the stack before calling op_putindx + * as lvzwrite_block->sub is already protected by stp_gcol_src.h. + */ + param_list.arg[n+1] = ((zwr_sub_lst *)lvzwrite_block->sub)->subsc_list[n].actual; + } + param_list.n = n + 1; + dst_lv = (lv_val *)callg((callgfnptr)op_putindx, ¶m_list); + MV_FORCE_STR(val); + DECR_AC_REF(dst_lv, TRUE); + dst_lv->v = *val; + dst_lv->v.mvtype &= ~MV_ALIASCONT; /* Make sure alias container property does not pass */ + } + } +} diff --git a/sr_port/lvzwr_var.c b/sr_port/lvzwr_var.c new file mode 100644 index 0000000..6ac4019 --- /dev/null +++ b/sr_port/lvzwr_var.c @@ -0,0 +1,311 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_stdio.h" + +#include "lv_val.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "zwrite.h" +#include "mlkdef.h" +#include "zshow.h" +#include "collseq.h" +#include "stringpool.h" +#include "op.h" +#include "outofband.h" +#include "do_xform.h" +#include "numcmp.h" +#include "patcode.h" +#include "mvalconv.h" +#include "follow.h" +#include "gtm_string.h" +#include "alias.h" +#include "promodemo.h" /* for "demote" prototype used in LV_NODE_GET_KEY */ + +#define eb_less(u, v) (numcmp(u, v) < 0) + +#define COMMON_STR_PROCESSING(NODE) \ +{ \ + mstr key_mstr; \ + mval tmp_sbs; \ + \ + assert(MV_STR & mv.mvtype); \ + if (TREF(local_collseq)) \ + { \ + key_mstr = mv.str; \ + mv.str.len = 0; /* protect from "stp_gcol", if zwr_sub->subsc_list[n].actual points to mv */ \ + ALLOC_XFORM_BUFF(key_mstr.len); \ + tmp_sbs.mvtype = MV_STR; \ + tmp_sbs.str.len = TREF(max_lcl_coll_xform_bufsiz); \ + assert(NULL != TREF(lcl_coll_xform_buff)); \ + tmp_sbs.str.addr = TREF(lcl_coll_xform_buff); \ + do_xform(TREF(local_collseq), XBACK, &key_mstr, &tmp_sbs.str, &length); \ + tmp_sbs.str.len = length; \ + s2pool(&(tmp_sbs.str)); \ + mv.str = tmp_sbs.str; \ + } \ + do_lev = TRUE; \ + if (n < lvzwrite_block->subsc_count) \ + { \ + if (zwr_sub->subsc_list[n].subsc_type == ZWRITE_PATTERN) \ + { \ + if (!do_pattern(&mv, zwr_sub->subsc_list[n].first)) \ + do_lev = FALSE; \ + } else if (zwr_sub->subsc_list[n].subsc_type != ZWRITE_ALL) \ + { \ + if (zwr_sub->subsc_list[n].first) \ + { \ + if (!MV_IS_CANONICAL(zwr_sub->subsc_list[n].first) && \ + (!follow(&mv, zwr_sub->subsc_list[n].first) && \ + (mv.str.len != zwr_sub->subsc_list[n].first->str.len || \ + memcmp(mv.str.addr, zwr_sub->subsc_list[n].first->str.addr, \ + mv.str.len)))) \ + do_lev = FALSE; \ + } \ + if (do_lev && zwr_sub->subsc_list[n].second) \ + { \ + if (MV_IS_CANONICAL(zwr_sub->subsc_list[n].second) || \ + (!follow(zwr_sub->subsc_list[n].second, &mv) && \ + (mv.str.len != zwr_sub->subsc_list[n].second->str.len || \ + memcmp(mv.str.addr, \ + zwr_sub->subsc_list[n].second->str.addr, \ + mv.str.len)))) \ + do_lev = FALSE; \ + } \ + } \ + } \ + if (do_lev) \ + lvzwr_var((lv_val *)NODE, n + 1); \ +} + +#define COMMON_NUMERIC_PROCESSING(NODE) \ +{ \ + do_lev = TRUE; \ + if (n < lvzwrite_block->subsc_count) \ + { \ + if (zwr_sub->subsc_list[n].subsc_type == ZWRITE_PATTERN) \ + { \ + if (!do_pattern(&mv, zwr_sub->subsc_list[n].first)) \ + do_lev = FALSE; \ + } else if (zwr_sub->subsc_list[n].subsc_type != ZWRITE_ALL) \ + { \ + if (zwr_sub->subsc_list[n].first) \ + { \ + if (!MV_IS_CANONICAL(zwr_sub->subsc_list[n].first) \ + || eb_less(&mv, zwr_sub->subsc_list[n].first)) \ + do_lev = FALSE; \ + } \ + if (do_lev && zwr_sub->subsc_list[n].second) \ + { \ + if (MV_IS_CANONICAL(zwr_sub->subsc_list[n].second) \ + && eb_less(zwr_sub->subsc_list[n].second, &mv)) \ + do_lev = FALSE; \ + } \ + } \ + } \ + if (do_lev) \ + lvzwr_var((lv_val *)NODE, n + 1); \ +} + +GBLREF lvzwrite_datablk *lvzwrite_block; +GBLREF int4 outofband; +GBLREF zshow_out *zwr_output; +GBLREF int merge_args; +GBLREF zwr_hash_table *zwrhtab; /* How we track aliases during zwrites */ + +LITREF mval literal_null; + +error_def(ERR_UNDEF); + +/* lv subscript usage notes: + * 1. The sub field in lvzwrite_datablk is an array allocated at MAX_LVSUBSCRIPTS. + * 2. The subscripts that appear at any given time are those for the current node being processed. + * 3. Nodes are setup by lvzwr_arg(). + * + * Example - take the following nodes: + * A(1,1)=10 + * A(1,2)=20 + * + * The simplified processing that occurs is as follows: + * 1. lvzwr_fini() sets curr_name which is the base var name (A) + * 2. First level lvzwr_var is called with level (aka n) == 0 + * 3. Since A has no value, nothing is printed. Notices that there are children so lvzwr_arg() + * is called recursively with level 1. + * 4. Sets up the level 1 subscript (key = 1). + * 5. Since A(1) has no value, nothing is printed. Notices that there are children so lvzwr_arg() + * is called recursively withe level 2. + * 6. Sets up the level 2 subscript (key = 1). + * 7. A(1,1) does have a value so lvzwr_out() is called to print the current key (from these + * subscripts) and its value. + * 8. No more subscripts at this level so pops back to level 1. + * 9. There is another child at this level so calls lvzwr_arg() recursively with level 2. + * 10. Replaces the level 2 subscript with the new key value (key = 2). + * 11. A(1,2) does have a value so lvzwr_out() is called to print the current key. + * 12. no more children at any level so everything pops back. + */ +void lvzwr_var(lv_val *lv, int4 n) +{ + mval mv; + int length; + lv_val *var; + char *top; + int4 i; + boolean_t do_lev, verify_hash_add, htent_added, value_printed_pending; + zwr_sub_lst *zwr_sub; + ht_ent_addr *tabent_addr; + zwr_alias_var *zav, *newzav; + lvTree *lvt; + lvTreeNode *node, *nullsubsnode, *parent; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + assert(lvzwrite_block); + if (lv == zwr_output->out_var.lv.child) + return; + if (outofband) + { + lvzwrite_block->curr_subsc = lvzwrite_block->subsc_count = 0; + outofband_action(FALSE); + } + lvzwrite_block->curr_subsc = n; + zwr_sub = (zwr_sub_lst *)lvzwrite_block->sub; + zwr_sub->subsc_list[n].actual = (mval *)NULL; + /* Before we process this var, there are some special cases to check for first when + * this is a base var (0 == lvzwrite_block->subsc_count) and the var is an alias. + * + * 1. Check if we have seen it before (the lvval is in the zwr_alias_var hash table), then we + * need to process this var with lvzwr_out NOW and we will only be processing the base + * var, not any of the subscripts. This is because all those subscripts (and the value + * of the base var itself) have been dealt with previously when we first saw this + * lvval. So in that case, call lvzwr_out() to output the association after which we are + * done with this var. + * 2. If we haven't seen it before, set a flag so we verify if the base var gets processed by + * lvzwr_out or not (i.e. whether it has a value and the "subscript" or lack there of is + * either wildcarded or whatever so that it actually gets dumped by lvzwr_out (see conditions + * below). If not, then *we* need to add the lvval to the hash table to signify we have seen + * it before so the proper associations to this alias var can be printed at a later time + * when/if they are encountered. + */ + verify_hash_add = FALSE; /* By default we don't need to verify add */ + value_printed_pending = FALSE; /* Force the "value_printed" flag on if TRUE */ + zav = NULL; + if (!merge_args && LV_IS_BASE_VAR(lv) && IS_ALIASLV(lv)) + { + assert(0 == n); /* Verify base var lv_val */ + if (tabent_addr = (ht_ent_addr *)lookup_hashtab_addr(&zwrhtab->h_zwrtab, (char **)&lv)) + { /* We've seen it before but check if it was actually printed at that point */ + zav = (zwr_alias_var *)tabent_addr->value; + assert(zav); + if (zav->value_printed) + { + lvzwr_out(lv); + lvzwrite_block->curr_subsc = lvzwrite_block->subsc_count = 0; + return; + } else + value_printed_pending = TRUE; /* We will set value_printed flag true later */ + } else + verify_hash_add = TRUE; + } + if ((0 == lvzwrite_block->subsc_count) && (0 == n)) + zwr_sub->subsc_list[n].subsc_type = ZWRITE_ASTERISK; + if (LV_IS_VAL_DEFINED(lv) + && (!lvzwrite_block->subsc_count || ((0 == n) && ZWRITE_ASTERISK == zwr_sub->subsc_list[n].subsc_type) + || ((0 != n) && !(lvzwrite_block->mask >> n)))) + { /* Print value for *this* node */ + lvzwr_out(lv); + } + if (verify_hash_add && !lvzwrite_block->zav_added) + { /* lvzwr_out processing didn't add a zav for this var. Take care of that now so we + * recognize it as a "dealt with" alias when/if it is encountered later. + */ + newzav = als_getzavslot(); + newzav->zwr_var = *lvzwrite_block->curr_name; + newzav->value_printed = TRUE; + htent_added = add_hashtab_addr(&zwrhtab->h_zwrtab, (char **)&lv, newzav, &tabent_addr); + assert(htent_added); + } + /* If we processed a base var above to print an alias association but it hadn't been printed yet, + * we had to wait until after lvzwr_out() was called before we could set the flag that indicated + * the printing had occurred. Do that now. Note that it is only when this flag is set we are + * certain to have a good value in zav. + */ + if (value_printed_pending) + { + assert(zav); + zav->value_printed = TRUE; + } + if (lvzwrite_block->subsc_count && (n >= lvzwrite_block->subsc_count) + && (ZWRITE_ASTERISK != zwr_sub->subsc_list[lvzwrite_block->subsc_count - 1].subsc_type)) + return; + + if (n < lvzwrite_block->subsc_count && ZWRITE_VAL == zwr_sub->subsc_list[n].subsc_type) + { + var = op_srchindx(VARLSTCNT(2) lv, zwr_sub->subsc_list[n].first); + zwr_sub->subsc_list[n].actual = zwr_sub->subsc_list[n].first; + if (var && (LV_IS_VAL_DEFINED(var) || n < lvzwrite_block->subsc_count -1)) + { + lvzwr_var(var, n + 1); + zwr_sub->subsc_list[n].actual = (mval *)NULL; + lvzwrite_block->curr_subsc = n; + } else + { + if (lvzwrite_block->fixed) + { + unsigned char buff[512], *end; + + lvzwrite_block->curr_subsc++; + end = lvzwr_key(buff, SIZEOF(buff)); + zwr_sub->subsc_list[n].actual = (mval *)NULL; + lvzwrite_block->curr_subsc = lvzwrite_block->subsc_count = 0; + rts_error(VARLSTCNT(4) ERR_UNDEF, 2, end - buff, buff); + } + } + } else if (lvt = LV_GET_CHILD(lv)) + { /* If node has children, process them now */ + zwr_sub->subsc_list[n].actual = &mv; + /* In case of standard null collation, first process null subscript if it exists */ + if (TREF(local_collseq_stdnull)) + { + nullsubsnode = lvAvlTreeLookupStr(lvt, (treeKeySubscr *)&literal_null, &parent); + if (NULL != nullsubsnode) + { + assert(MVTYPE_IS_STRING(nullsubsnode->key_mvtype) && !nullsubsnode->key_len); + /* Process null subscript first */ + LV_STR_NODE_GET_KEY(nullsubsnode, &mv); /* Get node key into "mv" */ + COMMON_STR_PROCESSING(nullsubsnode); + } + } else + nullsubsnode = NULL; + for (node = lvAvlTreeFirst(lvt); NULL != node; node = lvAvlTreeNext(node)) + { + if (node == nullsubsnode) + { + assert(TREF(local_collseq_stdnull)); + continue; /* skip null subscript as it has already been processed */ + } + LV_NODE_GET_KEY(node, &mv); /* Get node key into "mv" depending on the structure type of "node" */ + if (!MVTYPE_IS_STRING(mv.mvtype)) + { /* "node" is of type "lvTreeNodeNum *" */ + COMMON_NUMERIC_PROCESSING(node); + } else + { /* "node" is of type "lvTreeNode *" */ + COMMON_STR_PROCESSING(node); + } + } + zwr_sub->subsc_list[n].actual = (mval *)NULL; + lvzwrite_block->curr_subsc = n; + } +} diff --git a/sr_port/m_break.c b/sr_port/m_break.c new file mode 100644 index 0000000..97fff45 --- /dev/null +++ b/sr_port/m_break.c @@ -0,0 +1,34 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Inforformation Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "cmd.h" + +GBLREF char window_token; + +int m_break(void) +{ + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + if ((TK_SPACE != window_token) && (TK_EOL != window_token)) + if (!m_xecute()) + return FALSE; + newtriple(OC_BREAK); + if (TREF(for_stack_ptr) == TADR(for_stack)) + start_fetches (OC_FETCH); + else + start_for_fetches (); + return TRUE; +} diff --git a/sr_port/m_close.c b/sr_port/m_close.c new file mode 100644 index 0000000..746c57a --- /dev/null +++ b/sr_port/m_close.c @@ -0,0 +1,68 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "io_params.h" +#include "indir_enum.h" +#include "advancewindow.h" +#include "cmd.h" +#include "deviceparameters.h" + + +GBLREF char window_token; + +int m_close(void) +{ + oprtype sopr, plist, devpopr; + int4 rval; + triple *ref; + triple *indref; + boolean_t inddevparms; + static readonly unsigned char empty_plist[1] = { iop_eol }; + + inddevparms = FALSE; + if (!(rval = strexpr(&sopr))) + return FALSE; + if (window_token != TK_COLON) + { /* Single parameter */ + if (rval == EXPR_INDR) + { /* Indirect entire parameter list */ + make_commarg(&sopr, indir_close); + return TRUE; + } else + { /* default device parms */ + plist = put_str((char *)empty_plist, SIZEOF(empty_plist)); + } + } else + { /* Have device parms. Determine type */ + advancewindow(); + if (TK_ATSIGN == window_token) + { /* Have indirect device parms */ + if (!indirection(&devpopr)) + return FALSE; + indref = newtriple(OC_INDDEVPARMS); + indref->operand[0] = devpopr; + indref->operand[1] = put_ilit(IOP_CLOSE_OK); + inddevparms = TRUE; + } else + { /* Process device parameters now */ + if (!deviceparameters(&plist, IOP_CLOSE_OK)) + return FALSE; + } + } + ref = newtriple(OC_CLOSE); + ref->operand[0] = sopr; + ref->operand[1] = !inddevparms ? plist : put_tref(indref); + return TRUE; +} diff --git a/sr_port/m_do.c b/sr_port/m_do.c new file mode 100644 index 0000000..f2e480b --- /dev/null +++ b/sr_port/m_do.c @@ -0,0 +1,173 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "mdq.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "mmemory.h" +#include "advancewindow.h" +#include "cmd.h" + +GBLREF boolean_t run_time; +GBLREF char window_token; + +error_def(ERR_ACTOFFSET); + +int m_do(void) +{ + triple tmpchain, *oldchain, *obp, *ref0, *tripsize, + *triptr, *ref1, *calltrip, *routineref, *labelref; + oprtype *cr; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + if ((TK_SPACE == window_token) || (TK_EOL == window_token)) + { + if (!run_time) /* DO SP SP is a noop at run time */ + { + calltrip = newtriple(OC_CALLSP); + calltrip->operand[0] = put_mnxl(); + calltrip->operand[1] = put_ocnt(); + } + return TRUE; + } else if (TK_AMPERSAND == window_token) + { + if (!extern_func(0)) + return FALSE; + else + return TRUE; + } + dqinit(&tmpchain, exorder); + oldchain = setcurtchain(&tmpchain); + calltrip = entryref(OC_CALL, OC_EXTCALL, (mint)indir_do, TRUE, FALSE, FALSE); + setcurtchain(oldchain); + if (!calltrip) + return FALSE; + if (TK_LPAREN == window_token) + { + if (OC_CALL == calltrip->opcode) + { + assert(MLAB_REF == calltrip->operand[0].oprclass); + calltrip->opcode = OC_EXCAL; + ref0 = newtriple(OC_PARAMETER); + calltrip->operand[1] = put_tref(ref0); + ref0->operand[0] = put_tsiz(); /* parm to hold size of jump codegen */ + tripsize = ref0->operand[0].oprval.tref; + assert(OC_TRIPSIZE == tripsize->opcode); + } else + { + if (OC_EXTCALL == calltrip->opcode) + { + assert(TRIP_REF == calltrip->operand[1].oprclass); + if (OC_CDLIT == calltrip->operand[1].oprval.tref->opcode) + assert(CDLT_REF == calltrip->operand[1].oprval.tref->operand[0].oprclass); + else + { + assert(OC_LABADDR == calltrip->operand[1].oprval.tref->opcode); + assert(TRIP_REF == calltrip->operand[1].oprval.tref->operand[1].oprclass); + assert(OC_PARAMETER == calltrip->operand[1].oprval.tref->operand[1].oprval.tref->opcode); + assert(TRIP_REF == + calltrip->operand[1].oprval.tref->operand[1].oprval.tref->operand[0].oprclass); + assert(OC_ILIT == calltrip->operand[1].oprval.tref->operand[1].oprval.tref-> + operand[0].oprval.tref->opcode); + assert(ILIT_REF == calltrip->operand[1].oprval.tref->operand[1].oprval.tref-> + operand[0].oprval.tref->operand[0].oprclass); + if (0 != calltrip->operand[1].oprval.tref->operand[1].oprval.tref-> + operand[0].oprval.tref->operand[0].oprval.ilit) + { + stx_error(ERR_ACTOFFSET); + return FALSE; + } + } + } else /* DO _ @dlabel actuallist */ + { + assert(OC_COMMARG == calltrip->opcode); + assert(TRIP_REF == calltrip->operand[1].oprclass); + assert(OC_ILIT == calltrip->operand[1].oprval.tref->opcode); + assert(ILIT_REF == calltrip->operand[1].oprval.tref->operand[0].oprclass); + assert((mint)indir_do == calltrip->operand[1].oprval.tref->operand[0].oprval.ilit); + assert(calltrip->exorder.fl == &tmpchain); + routineref = maketriple(OC_CURRHD); + labelref = maketriple(OC_LABADDR); + ref0 = maketriple(OC_PARAMETER); + dqins(calltrip->exorder.bl, exorder, routineref); + dqins(calltrip->exorder.bl, exorder, labelref); + dqins(calltrip->exorder.bl, exorder, ref0); + labelref->operand[0] = calltrip->operand[0]; + labelref->operand[1] = put_tref(ref0); + ref0->operand[0] = calltrip->operand[1]; + ref0->operand[0].oprval.tref->operand[0].oprval.ilit = 0; + ref0->operand[1] = put_tref(routineref); + calltrip->operand[0] = put_tref(routineref); + calltrip->operand[1] = put_tref(labelref); + } + calltrip->opcode = OC_EXTEXCAL; + ref0 = newtriple(OC_PARAMETER); + ref0->operand[0] = calltrip->operand[1]; + calltrip->operand[1] = put_tref(ref0); + } + if (!actuallist(&ref0->operand[1])) + return FALSE; + } else if (OC_CALL == calltrip->opcode) + { + calltrip->operand[1] = put_ocnt(); + if (TREF(for_stack_ptr) != TADR(for_stack)) + { + if (TAREF1(for_temps, (TREF(for_stack_ptr) - TADR(for_stack)))) + calltrip->opcode = OC_FORLCLDO; + } + } + if (TK_COLON == window_token) + { + advancewindow(); + cr = (oprtype *)mcalloc(SIZEOF(oprtype)); + if (!bool_expr((bool) FALSE, cr)) + return FALSE; + if (TREF(expr_start) != TREF(expr_start_orig)) + { + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(TREF(expr_start)); + } + obp = oldchain->exorder.bl; + dqadd(obp, &tmpchain, exorder); /*this is a violation of info hiding*/ + if (OC_EXCAL == calltrip->opcode) + { + triptr = newtriple(OC_JMP); + triptr->operand[0] = put_mfun(&calltrip->operand[0].oprval.lab->mvname); + calltrip->operand[0].oprclass = ILIT_REF; /* dummy placeholder */ + tripsize->operand[0].oprval.tsize->ct = triptr; + } + if (TREF(expr_start) != TREF(expr_start_orig)) + { + ref0 = newtriple(OC_JMP); + ref1 = newtriple(OC_GVRECTARG); + ref1->operand[0] = put_tref(TREF(expr_start)); + *cr = put_tjmp(ref1); + tnxtarg(&ref0->operand[0]); + } else + tnxtarg(cr); + } else + { + obp = oldchain->exorder.bl; + dqadd(obp, &tmpchain, exorder); /*this is a violation of info hiding*/ + if (OC_EXCAL == calltrip->opcode) + { + triptr = newtriple(OC_JMP); + triptr->operand[0] = put_mfun(&calltrip->operand[0].oprval.lab->mvname); + calltrip->operand[0].oprclass = ILIT_REF; /* dummy placeholder */ + tripsize->operand[0].oprval.tsize->ct = triptr; + } + } + return TRUE; +} diff --git a/sr_port/m_else.c b/sr_port/m_else.c new file mode 100644 index 0000000..50ea699 --- /dev/null +++ b/sr_port/m_else.c @@ -0,0 +1,45 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "mmemory.h" +#include "cmd.h" + +GBLREF char window_token; + +error_def(ERR_SPOREOL); + +int m_else(void) +{ + triple *jmpref, elsepos_in_chain; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + elsepos_in_chain = TREF(pos_in_chain); + if (TK_EOL == window_token) + return TRUE; + if (TK_SPACE != window_token) + { + stx_error(ERR_SPOREOL); + return FALSE; + } + jmpref = newtriple(OC_JMPTSET); + FOR_END_OF_SCOPE(0, jmpref->operand[0]); + if (!linetail()) + { tnxtarg(&jmpref->operand[0]); + TREF(pos_in_chain) = elsepos_in_chain; + return FALSE; + } else + return TRUE; +} diff --git a/sr_port/m_for.c b/sr_port/m_for.c new file mode 100644 index 0000000..e00ef71 --- /dev/null +++ b/sr_port/m_for.c @@ -0,0 +1,333 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "mdq.h" +#include "opcode.h" +#include "toktyp.h" +#include "mmemory.h" +#include "advancewindow.h" +#include "cmd.h" +#include "lv_val.h" + +GBLREF char window_token; +GBLREF triple *curtchain; + +error_def(ERR_EQUAL); +error_def(ERR_FOROFLOW); +error_def(ERR_MAXFORARGS); +error_def(ERR_SPOREOL); + +/* The following macro checks to see if the evaluation of control variable components has done + * anything that might have expose us to a messed up the control variable context. We only + * have a problem when the control variable is subscripted, because if an extrinsic rearranges + * the array - a KILL will do it - the op_putindx we did initially might be pointing into + * never-neverland and slamming a value into it would definately not be a healthly thing. + * Without indirection we know at compile time whether or not the control variable is subscripted + * but with indirection we only know at run-time; we tried some contortions to skip the refresh if + * it's not needed but lost the battle with the compiler's tendency to lose reference with a scope + * that's not short - OC_PASSTHRU is suppose to give it a clue but having two of those in a row + * seems not to work. + */ +#define DEAL_WITH_DANGER(LVL, CNTL_VAR, VAL) \ +{ \ + triple *Ref; \ + \ + if ((TRUE_WITH_INDX == TAREF1(for_temps, for_stack_level))) \ + { \ + Ref = newtriple(OC_RFRSHINDX); \ + Ref->operand[0] = put_ilit(LVL); \ + Ref->operand[1] = put_ilit(1); \ + CNTL_VAR = put_tref(Ref); \ + newtriple(OC_PASSTHRU)->operand[0] = CNTL_VAR; /* warn off optimizer */ \ + } \ + Ref = newtriple(OC_STO); \ + Ref->operand[0] = CNTL_VAR; \ + Ref->operand[1] = VAL; \ +} + +/* the macro below pushes the compiler FOR stack - the FOR_POP is in compiler.h 'cause stx_error uses it + * there are actually two stacks - one for code references and one for temps flags; the code reference + * one, for_stack, uses for_stack_ptr; the for_temps doesn't have its own global index, but instead uses + * a local variable calculated from the relationship between the for_stack and for_stack_ptr + */ +#define FOR_PUSH() \ +{ \ + int Level; \ + \ + Level = ((++(TREF(for_stack_ptr))) - (oprtype **)TADR(for_stack)); \ + if (MAX_FOR_STACK > Level) \ + { \ + assert(TREF(for_stack_ptr) > (oprtype **)TADR(for_stack)); \ + *(TREF(for_stack_ptr)) = NULL; \ + TAREF1(for_temps, Level) = TAREF1(for_temps, Level - 1) ? TRUE : FALSE; \ + } else \ + { \ + --(TREF(for_stack_ptr)); \ + stx_error(ERR_FOROFLOW, 1, (MAX_FOR_STACK - 1)); \ + FOR_POP(BLOWN_FOR); \ + return FALSE; \ + } \ +} + +/* the macro below tucks a code reference into the for_stack so a FOR that's done can move on correctly when skipped */ +#define SAVE_FOR_OVER_ADDR() \ +{ \ + assert(TREF(for_stack_ptr) >= (oprtype **)TADR(for_stack)); \ + assert(TREF(for_stack_ptr) < (oprtype **)(TADR(for_stack) + (MAX_FOR_STACK * SIZEOF(oprtype **)))); \ + if (NULL == *(TREF(for_stack_ptr))) \ + *(TREF(for_stack_ptr)) = (oprtype *)mcalloc(SIZEOF(oprtype)); \ + tnxtarg(*(TREF(for_stack_ptr))); \ +} + +int m_for(void) +{ + unsigned int arg_cnt, arg_index, for_stack_level; + oprtype arg_eval_addr[MAX_FORARGS], increment[MAX_FORARGS], terminate[MAX_FORARGS], + arg_next_addr, arg_value, dummy, *body, control_variable, + *iteration_start_addr, iteration_start_addr_indr, *not_even_once_addr; + triple *eval_next_addr[MAX_FORARGS], *control_ref, + *forchk1opc, forpos_in_chain, *init_ref, *ref, *step_ref, *term_ref; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + forpos_in_chain = TREF(pos_in_chain); + FOR_PUSH(); + if (TK_SPACE == window_token) + { /* "argumentless" form */ + FOR_END_OF_SCOPE(1, dummy); + ref = newtriple(OC_FORCHK1); + if (!linetail()) + { + TREF(pos_in_chain) = forpos_in_chain; + assert(TREF(source_error_found)); + stx_error(TREF(source_error_found)); + FOR_POP(BLOWN_FOR); + return FALSE; + } + SAVE_FOR_OVER_ADDR(); /* stash address of next op in the for_stack array */ + newtriple(OC_JMP)->operand[0] = put_tjmp(ref); /* transfer back to just before the begining of the body */ + FOR_POP(GOOD_FOR); /* and pop the array */ + return TRUE; + } + for_stack_level = (TREF(for_stack_ptr) - TADR(for_stack)); + init_ref = newtriple(OC_FORNESTLVL); + init_ref->operand[0] = put_ilit(for_stack_level); + if (TK_ATSIGN == window_token) + { + if (!indirection(&control_variable)) + { + FOR_POP(BLOWN_FOR); + return FALSE; + } + ref = newtriple(OC_INDLVADR); + ref->operand[0] = control_variable; + control_variable = put_tref(ref); + control_ref = NULL; + } else + { + /* The following relies on the fact that lvn() always generates an OC_VAR triple first */ + control_ref = curtchain->exorder.bl; + if (!lvn(&control_variable, OC_SAVPUTINDX, NULL)) + { + FOR_POP(BLOWN_FOR); + return FALSE; + } + assert(OC_VAR == control_ref->exorder.fl->opcode); + assert(MVAR_REF == control_ref->exorder.fl->operand[0].oprclass); + } + if (TK_EQUAL != window_token) + { + stx_error(ERR_EQUAL); + FOR_POP(BLOWN_FOR); + return FALSE; + } + newtriple(OC_PASSTHRU)->operand[0] = control_variable; /* make sure optimizer doesn't ditch control_variable */ + FOR_END_OF_SCOPE(1, dummy); + assert((0 < for_stack_level) && (MAX_FOR_STACK >= for_stack_level)); + if ((OC_SAVPUTINDX == control_variable.oprval.tref->opcode) || (OC_INDLVADR == control_variable.oprval.tref->opcode)) + TAREF1(for_temps, for_stack_level) = TRUE_WITH_INDX; /* most uses treat this as a boolean, but some need more */ + else + init_ref->opcode = OC_NOOP; + iteration_start_addr = (oprtype *)mcalloc(SIZEOF(oprtype)); + iteration_start_addr_indr = put_indr(iteration_start_addr); + arg_next_addr.oprclass = NOCLASS; + not_even_once_addr = NULL; /* used to skip processing where the initial control exceeds the termination */ + for (arg_cnt = 0; ; ++arg_cnt) + { + if (MAX_FORARGS <= arg_cnt) + { + stx_error(ERR_MAXFORARGS); + FOR_POP(BLOWN_FOR); + return FALSE; + } + assert((TK_COMMA == window_token) || (TK_EQUAL == window_token)); + advancewindow(); + tnxtarg(&arg_eval_addr[arg_cnt]); /* put location of this arg eval in arg_eval_addr array */ + if (NULL != not_even_once_addr) + { + *not_even_once_addr = arg_eval_addr[arg_cnt]; + not_even_once_addr = NULL; + } + if (EXPR_FAIL == expr(&arg_value)) /* starting (possibly only) value */ + { + FOR_POP(BLOWN_FOR); + return FALSE; + } + assert(TRIP_REF == arg_value.oprclass); + if (TK_COLON != window_token) + { /* list point value? */ + increment[arg_cnt].oprclass = terminate[arg_cnt].oprclass = 0; + DEAL_WITH_DANGER(for_stack_level, control_variable, arg_value); + } else + { /* stepping value */ + init_ref = newtriple(OC_STOTEMP); /* tuck it in a temp undisturbed by coming evals */ + init_ref->operand[0] = arg_value; + newtriple(OC_CONUM)->operand[0] = put_tref(init_ref); /* make start numeric */ + advancewindow(); /* past the first colon */ + if (EXPR_FAIL == expr(&increment[arg_cnt])) /* pick up step */ + { + FOR_POP(BLOWN_FOR); + return FALSE; + } + assert(TRIP_REF == increment[arg_cnt].oprclass); + ref = increment[arg_cnt].oprval.tref; + if (OC_LIT != ref->opcode) + { + if (!TAREF1(for_temps, for_stack_level)) + TAREF1(for_temps, for_stack_level) = TRUE; + if (OC_VAR == ref->opcode) + { + step_ref = newtriple(OC_STOTEMP); + step_ref->operand[0] = put_tref(ref); + increment[arg_cnt] = put_tref(step_ref); + } + } + if (TK_COLON != window_token) + { + DEAL_WITH_DANGER(for_stack_level, control_variable, put_tref(init_ref)); + terminate[arg_cnt].oprclass = 0; /* no termination on iteration for this arg */ + } else + { + advancewindow(); /* past the second colon */ + if (EXPR_FAIL == expr(&terminate[arg_cnt])) /* termination control value */ + { + FOR_POP(BLOWN_FOR); + return FALSE; + } + assert(TRIP_REF == terminate[arg_cnt].oprclass); + ref = terminate[arg_cnt].oprval.tref; + if (OC_LIT != ref->opcode) + { + if (!TAREF1(for_temps, for_stack_level)) + TAREF1(for_temps, for_stack_level) = TRUE; + if (OC_VAR == ref->opcode) + { + term_ref = newtriple(OC_STOTEMP); + term_ref->operand[0] = put_tref(ref); + terminate[arg_cnt] = put_tref(term_ref); + } + } + DEAL_WITH_DANGER(for_stack_level, control_variable, put_tref(init_ref)); + term_ref = newtriple(OC_PARAMETER); + term_ref->operand[0] = terminate[arg_cnt]; + step_ref = newtriple(OC_PARAMETER); + step_ref->operand[0] = increment[arg_cnt]; + step_ref->operand[1] = put_tref(term_ref); + ref = newtriple(OC_FORINIT); + ref->operand[0] = control_variable; + ref->operand[1] = put_tref(step_ref); + not_even_once_addr = newtriple(OC_JMPGTR)->operand; + } + } + if ((0 < arg_cnt) || (TK_COMMA == window_token)) + { + if (!TAREF1(for_temps, for_stack_level)) + TAREF1(for_temps, for_stack_level) = TRUE; + if (NOCLASS == arg_next_addr.oprclass) + arg_next_addr = put_tref(newtriple(OC_CDADDR)); + (eval_next_addr[arg_cnt] = newtriple(OC_LDADDR))->destination = arg_next_addr; + } + if (TK_COMMA != window_token) + break; + newtriple(OC_JMP)->operand[0] = iteration_start_addr_indr; + } + if (not_even_once_addr) + FOR_END_OF_SCOPE(1, *not_even_once_addr); /* 1 means down a level */ + forchk1opc = newtriple(OC_FORCHK1); /* FORCHK1 is a do-nothing routine used by the out-of-band mechanism */ + *iteration_start_addr = put_tjmp(forchk1opc); + body = (oprtype *)mcalloc(SIZEOF(oprtype)); + tnxtarg(body); + if ((TK_EOL != window_token) && (TK_SPACE != window_token)) + { + stx_error(ERR_SPOREOL); + FOR_POP(BLOWN_FOR); + return FALSE; + } + if (!linetail()) + { + TREF(pos_in_chain) = forpos_in_chain; + assert(TREF(source_error_found)); + stx_error(TREF(source_error_found)); + FOR_POP(BLOWN_FOR); + return FALSE; + } + SAVE_FOR_OVER_ADDR(); /* stash address of next op in the for_stack array */ + if (0 < arg_cnt) + newtriple(OC_JMPAT)->operand[0] = put_tref(eval_next_addr[0]); + for (arg_index = 0; arg_index <= arg_cnt; ++arg_index) + { + if (0 < arg_cnt) + tnxtarg(eval_next_addr[arg_index]->operand); + if (TRUE_WITH_INDX == TAREF1(for_temps, for_stack_level)) + { /* since it might have moved, before touching the control variable get a fix on it */ + ref = newtriple(OC_RFRSHINDX); + ref->operand[0] = put_ilit(for_stack_level); + ref->operand[1] = put_ilit((increment[arg_index].oprclass || terminate[arg_index].oprclass) + ? FALSE : TRUE); /* if increment rather than new value, rfrsh w/ srchindx else putindx */ + control_variable = put_tref(ref); + } else + { + assert(control_ref); + control_variable = put_mvar(&control_ref->exorder.fl->operand[0].oprval.vref->mvname); + } + newtriple(OC_PASSTHRU)->operand[0] = control_variable; /* warn off optimizer */ + if (terminate[arg_index].oprclass) + { + term_ref = newtriple(OC_PARAMETER); + term_ref->operand[0] = terminate[arg_index]; + step_ref = newtriple(OC_PARAMETER); + step_ref->operand[0] = increment[arg_index]; + step_ref->operand[1] = put_tref(term_ref); + init_ref = newtriple(OC_PARAMETER); + init_ref->operand[0] = control_variable; + init_ref->operand[1] = put_tref(step_ref); + ref = newtriple(OC_FORLOOP); + /* redirects back to forchk1, which is at the beginning of new iteration */ + ref->operand[0] = *iteration_start_addr; + ref->operand[1] = put_tref(init_ref); + } else if (increment[arg_index].oprclass) + { + step_ref = newtriple(OC_ADD); + step_ref->operand[0] = control_variable; + step_ref->operand[1] = increment[arg_index]; + ref = newtriple(OC_STO); + ref->operand[0] = control_variable; + ref->operand[1] = put_tref(step_ref); + newtriple(OC_JMP)->operand[0] = *iteration_start_addr; + } + if (arg_index < arg_cnt) /* go back and evaluate the next argument */ + newtriple(OC_JMP)->operand[0] = arg_eval_addr[arg_index + 1]; + } + FOR_POP(GOOD_FOR); + return TRUE; +} diff --git a/sr_port/m_goto.c b/sr_port/m_goto.c new file mode 100644 index 0000000..b9d38cb --- /dev/null +++ b/sr_port/m_goto.c @@ -0,0 +1,66 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "mdq.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "mmemory.h" +#include "advancewindow.h" +#include "cmd.h" + +GBLREF char window_token; + +int m_goto(void) +{ + triple tmpchain, *oldchain, *obp, *ref0, *ref1, *triptr; + oprtype *cr; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + dqinit(&tmpchain, exorder); + oldchain = setcurtchain(&tmpchain); + if (!entryref(OC_JMP, OC_EXTJMP, (mint)indir_goto, TRUE, FALSE, FALSE)) + { + setcurtchain(oldchain); + return FALSE; + } + setcurtchain(oldchain); + if (window_token == TK_COLON) + { + advancewindow(); + cr = (oprtype *) mcalloc(SIZEOF(oprtype)); + if (!bool_expr((bool) FALSE, cr)) + return FALSE; + if (TREF(expr_start) != TREF(expr_start_orig)) + { + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(TREF(expr_start)); + } + obp = oldchain->exorder.bl; + dqadd(obp, &tmpchain, exorder); /*this is a violation of info hiding*/ + if (TREF(expr_start) != TREF(expr_start_orig)) + { + ref0 = newtriple(OC_JMP); + ref1 = newtriple(OC_GVRECTARG); + ref1->operand[0] = put_tref(TREF(expr_start)); + *cr = put_tjmp(ref1); + tnxtarg(&ref0->operand[0]); + } else + tnxtarg(cr); + return TRUE; + } + obp = oldchain->exorder.bl; + dqadd(obp, &tmpchain, exorder); /*this is a violation of info hiding*/ + return TRUE; +} diff --git a/sr_port/m_halt.c b/sr_port/m_halt.c new file mode 100644 index 0000000..3524442 --- /dev/null +++ b/sr_port/m_halt.c @@ -0,0 +1,26 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "cmd.h" + +int m_halt(void) +{ + + oprtype x; + + newtriple(OC_HALT); + return TRUE; + +} diff --git a/sr_port/m_hang.c b/sr_port/m_hang.c new file mode 100644 index 0000000..4880265 --- /dev/null +++ b/sr_port/m_hang.c @@ -0,0 +1,40 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "cmd.h" + +GBLREF char window_token; + +int m_hang(void) +{ + triple *triptr; + oprtype ot; + + switch (numexpr(&ot)) + { + case EXPR_FAIL: + return FALSE; + case EXPR_GOOD: + triptr = newtriple(OC_HANG); + triptr->operand[0] = ot; + return TRUE; + case EXPR_INDR: + make_commarg(&ot, indir_hang); + return TRUE; + } + + return FALSE; /* This should never get executed, added to make compiler happy */ +} diff --git a/sr_port/m_hcmd.c b/sr_port/m_hcmd.c new file mode 100644 index 0000000..7f3700b --- /dev/null +++ b/sr_port/m_hcmd.c @@ -0,0 +1,25 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "cmd.h" + +GBLREF char window_token; + +int m_hcmd(void) +{ +if (window_token == TK_SPACE || window_token == TK_EOL) + return m_halt(); +return m_hang(); +} diff --git a/sr_port/m_if.c b/sr_port/m_if.c new file mode 100644 index 0000000..6a0e973 --- /dev/null +++ b/sr_port/m_if.c @@ -0,0 +1,136 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "mdq.h" +#include "mmemory.h" +#include "advancewindow.h" +#include "cmd.h" + +GBLREF char window_token; +GBLREF triple *curtchain; + +error_def(ERR_SPOREOL); +error_def(ERR_INDEXTRACHARS); + + typedef struct jmpchntype +{ + struct + { + struct jmpchntype *fl,*bl; + } link; + triple *jmptrip; +} jmpchn; + +int m_if(void) +{ + triple *ref0, *ref1, *ref2, *jmpref, ifpos_in_chain, *triptr; + oprtype x, y, *ta_opr; + boolean_t first_time, t_set, is_commarg; + jmpchn *jmpchain, *nxtjmp; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + ifpos_in_chain = TREF(pos_in_chain); + jmpchain = (jmpchn*)mcalloc(SIZEOF(jmpchn)); + dqinit(jmpchain,link); + if (TK_EOL == window_token) + return TRUE; + is_commarg = (1 == TREF(last_source_column)); + FOR_END_OF_SCOPE(0, x); + assert(INDR_REF == x.oprclass); + if (TK_SPACE == window_token) + { + jmpref = newtriple(OC_JMPTCLR); + jmpref->operand[0] = x; + nxtjmp = (jmpchn *)mcalloc(SIZEOF(jmpchn)); + nxtjmp->jmptrip = jmpref; + dqins(jmpchain,link,nxtjmp); + } else + { + first_time = TRUE; + for (;;) + { + ta_opr = (oprtype *)mcalloc(SIZEOF(oprtype)); + if (!bool_expr((bool)TRUE, ta_opr)) + return FALSE; + if (((OC_JMPNEQ == (ref0 = curtchain->exorder.bl)->opcode)) + && (OC_COBOOL == (ref1 = ref0->exorder.bl)->opcode) + && (OC_INDGLVN == (ref2 = ref1->exorder.bl)->opcode)) + { + dqdel(ref0,exorder); + ref1->opcode = OC_JMPTSET; + ref1->operand[0] = put_indr(ta_opr); + ref2->opcode = OC_COMMARG; + ref2->operand[1] = put_ilit((mint)indir_if); + } + t_set = (OC_JMPTSET == curtchain->exorder.bl->opcode); + if (!t_set) + newtriple(OC_CLRTEST); + if (TREF(expr_start) != TREF(expr_start_orig)) + { + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(TREF(expr_start)); + } + jmpref = newtriple(OC_JMP); + jmpref->operand[0] = x; + nxtjmp = (jmpchn *)mcalloc(SIZEOF(jmpchn)); + nxtjmp->jmptrip = jmpref; + dqins(jmpchain,link,nxtjmp); + tnxtarg(ta_opr); + if (first_time) + { + if (!t_set) + newtriple(OC_SETTEST); + if (TREF(expr_start) != TREF(expr_start_orig)) + { + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(TREF(expr_start)); + } + first_time = FALSE; + } + if (TK_COMMA != window_token) + break; + advancewindow(); + } + } + if (is_commarg) + { + while(TK_SPACE == window_token) /* Eat up trailing white space */ + advancewindow(); + if (TK_EOL != window_token) + { + stx_error(ERR_INDEXTRACHARS); + return FALSE; + } + return TRUE; + } + if ((TK_EOL != window_token) && (TK_SPACE != window_token)) + { + stx_error(ERR_SPOREOL); + return FALSE; + } + if (!linetail()) + { tnxtarg(&x); + dqloop(jmpchain,link,nxtjmp) + { + ref1 = nxtjmp->jmptrip; + ref1->operand[0] = x; + } + TREF(pos_in_chain) = ifpos_in_chain; + return FALSE; + } + return TRUE; +} diff --git a/sr_port/m_job.c b/sr_port/m_job.c new file mode 100644 index 0000000..963a4e8 --- /dev/null +++ b/sr_port/m_job.c @@ -0,0 +1,158 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "iotimer.h" +#include "job.h" +#include "advancewindow.h" +#include "cmd.h" + +GBLREF char window_token; +GBLREF mident window_ident; +GBLREF boolean_t run_time; +GBLREF mident routine_name; +LITREF mident zero_ident; + +int m_job(void) +{ + int argcnt; + triple *ref,*next; + oprtype label, offset, routine, plist, timeout, arglst, *argptr, argval; + static readonly unsigned char empty_plist[1] = { jp_eol }; + bool is_timeout,dummybool; + + error_def(ERR_MAXACTARG); + error_def(ERR_RTNNAME); + error_def(ERR_COMMAORRPAREXP); + error_def(ERR_JOBACTREF); + + label = put_str(zero_ident.addr, zero_ident.len); + offset = put_ilit((mint)0); + if (!lref(&label, &offset, FALSE, indir_job, TRUE, &dummybool)) + return FALSE; + if ((TRIP_REF == label.oprclass) && (OC_COMMARG == label.oprval.tref->opcode)) + return TRUE; + if (TK_CIRCUMFLEX != window_token) + { + if (!run_time) + routine = put_str(routine_name.addr, routine_name.len); + else + routine = put_tref(newtriple(OC_CURRTN)); + } else + { + advancewindow(); + switch(window_token) + { + case TK_IDENT: + routine = put_str(window_ident.addr, window_ident.len); + advancewindow(); + break; + case TK_ATSIGN: + if (!indirection(&routine)) + return FALSE; + break; + default: + stx_error(ERR_RTNNAME); + return FALSE; + } + } + argcnt = 0; + if (TK_LPAREN == window_token) + { + advancewindow(); + argptr = &arglst; + do + { + if (argcnt > MAX_ACTUALS) + { + stx_error(ERR_MAXACTARG); + return FALSE; + } + if (TK_PERIOD == window_token) + { + stx_error(ERR_JOBACTREF); + return FALSE; + } + if ((TK_COMMA == window_token) || (TK_RPAREN == window_token)) + { + ref = newtriple(OC_NULLEXP); + argval = put_tref(ref); + } else if (!expr(&argval)) + return FALSE; + ref = newtriple(OC_PARAMETER); + ref->operand[0] = argval; + *argptr = put_tref(ref); + argptr = &ref->operand[1]; + argcnt++; + if (TK_COMMA == window_token) + advancewindow(); + else if (TK_RPAREN != window_token) + { + stx_error(ERR_COMMAORRPAREXP); + return FALSE; + } + } while (TK_RPAREN != window_token); + advancewindow(); /* jump over close paren */ + } + if (TK_COLON == window_token) + { + advancewindow(); + if (TK_COLON == window_token) + { + is_timeout = TRUE; + plist = put_str((char *)empty_plist,SIZEOF(empty_plist)); + } else + { + if (!jobparameters(&plist)) + return FALSE; + is_timeout = (TK_COLON == window_token); + } + if (is_timeout) + { + advancewindow(); + if (!intexpr(&timeout)) + return FALSE; + } else + timeout = put_ilit(NO_M_TIMEOUT); + } else + { + is_timeout = FALSE; + plist = put_str((char *)empty_plist,SIZEOF(empty_plist)); + timeout = put_ilit(NO_M_TIMEOUT); + } + + ref = newtriple(OC_JOB); + ref->operand[0] = put_ilit(argcnt + 5); /* parameter list + five fixed arguments */ + next = newtriple(OC_PARAMETER); + ref->operand[1] = put_tref(next); + next->operand[0] = label; + ref = newtriple(OC_PARAMETER); + next->operand[1] = put_tref(ref); + ref->operand[0] = offset; + next = newtriple(OC_PARAMETER); + ref->operand[1] = put_tref(next); + next->operand[0] = routine; + ref = newtriple(OC_PARAMETER); + next->operand[1] = put_tref(ref); + ref->operand[0] = plist; + next = newtriple(OC_PARAMETER); + ref->operand[1] = put_tref(next); + next->operand[0] = timeout; + if (argcnt) + next->operand[1] = arglst; + if (is_timeout) + newtriple(OC_TIMTRU); + return TRUE; +} diff --git a/sr_port/m_kill.c b/sr_port/m_kill.c new file mode 100644 index 0000000..246688c --- /dev/null +++ b/sr_port/m_kill.c @@ -0,0 +1,140 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "mdq.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "advancewindow.h" +#include "cmd.h" + +GBLREF char window_token; +GBLREF mident window_ident; +GBLREF char director_token; + +int m_kill(void) +{ + oprtype tmparg; + triple *ref, *next, *org, *s; + int count; + boolean_t alias_processing; + mvar *mvarptr; + + error_def(ERR_RPARENMISSING); + error_def(ERR_VAREXPECTED); + error_def(ERR_ALIASEXPECTED); + error_def(ERR_NOALIASLIST); + + if (alias_processing = (TK_ASTERISK == window_token)) /* Note assignment */ + advancewindow(); + + switch (window_token) + { + case TK_IDENT: + /* If doing alias processing, we need to pass the index of the var rather than its lv_val + but do the common case first. Note that a kill of an alias container is handled the same + as the kill of any other regular local variable. + */ + if (!alias_processing || TK_LPAREN == director_token) + { + if (!lvn(&tmparg,OC_SRCHINDX,0)) + return FALSE; + ref = newtriple(OC_KILL); + ref->operand[0] = tmparg; + } else + { /* alias (unsubscripted var) kill */ + ref = newtriple(OC_KILLALIAS); + mvarptr = get_mvaddr(&window_ident); + ref->operand[0] = put_ilit(mvarptr->mvidx); + advancewindow(); + } + break; + case TK_CIRCUMFLEX: + if (alias_processing) + { + stx_error(ERR_ALIASEXPECTED); + return FALSE; + } + if (!gvn()) + return FALSE; + ref = newtriple(OC_GVKILL); + break; + case TK_ATSIGN: + if (alias_processing) + { + stx_error(ERR_ALIASEXPECTED); + return FALSE; + } + if (!indirection(&tmparg)) + return FALSE; + ref = maketriple(OC_COMMARG); + ref->operand[0] = tmparg; + ref->operand[1] = put_ilit((mint)indir_kill); + ins_triple(ref); + return TRUE; + case TK_EOL: + case TK_SPACE: + newtriple(alias_processing ? OC_KILLALIASALL : OC_KILLALL); + break; + case TK_LPAREN: + if (alias_processing) + { + stx_error(ERR_NOALIASLIST); + return FALSE; + } + ref = org = maketriple(OC_XKILL); + count = 0; + do + { + advancewindow(); + next = maketriple(OC_PARAMETER); + ref->operand[1] = put_tref(next); + switch (window_token) + { + case TK_IDENT: + next->operand[0] = put_str(window_ident.addr, window_ident.len); + advancewindow(); + break; + case TK_ATSIGN: + if (!indirection(&tmparg)) + return FALSE; + s = newtriple(OC_INDLVARG); + s->operand[0] = tmparg; + next->operand[0] = put_tref(s); + break; + case TK_ASTERISK: + stx_error(ERR_NOALIASLIST); + return FALSE; + default: + stx_error(ERR_VAREXPECTED); + return FALSE; + } + ins_triple(next); + ref = next; + count++; + } while (TK_COMMA == window_token); + if (TK_RPAREN != window_token) + { + stx_error(ERR_RPARENMISSING); + return FALSE; + } + advancewindow(); + org->operand[0] = put_ilit((mint)count); + ins_triple(org); + return TRUE; + default: + stx_error(alias_processing ? ERR_ALIASEXPECTED : ERR_VAREXPECTED); + return FALSE; + } + return TRUE; +} diff --git a/sr_port/m_lock.c b/sr_port/m_lock.c new file mode 100644 index 0000000..91c39d6 --- /dev/null +++ b/sr_port/m_lock.c @@ -0,0 +1,111 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "iotimer.h" +#include "indir_enum.h" +#include "advancewindow.h" +#include "cmd.h" + +GBLREF char window_token; + +int m_lock(void) +{ + triple *ref, *restart; + oprtype indopr; + opctype ox; + bool indirect; + error_def(ERR_RPARENMISSING); + + restart = newtriple(OC_RESTARTPC); + newtriple(OC_LKINIT); + + indirect = FALSE; + + switch(window_token) + { + case TK_MINUS: + advancewindow(); + ox = OC_LCKDECR; + break; + case TK_PLUS: + advancewindow(); + ox = OC_LCKINCR; + break; + case TK_EOL: + case TK_SPACE: + ox = OC_UNLOCK; + restart->opcode = OC_NOOP; + newtriple(OC_UNLOCK); + return TRUE; + break; + case TK_ATSIGN: + if (!indirection(&indopr)) + return FALSE; + ref = maketriple(OC_COMMARG); + ref->operand[0] = indopr; + if (window_token != TK_COLON) + { + ref->operand[1] = put_ilit((mint) indir_lock); + ins_triple(ref); + return TRUE; + } + ref->operand[1] = put_ilit((mint) indir_nref); + indirect = TRUE; + /*** CAUTION: FALL-THROUGH ***/ + default: + newtriple(OC_UNLOCK); + ox = OC_LOCK; + } + if (indirect) + ins_triple(ref); + else + { + switch (window_token) + { + case TK_LPAREN: + do + { + advancewindow(); + if (nref() == EXPR_FAIL) + return FALSE; + } while (window_token == TK_COMMA); + if (window_token != TK_RPAREN) + { + stx_error(ERR_RPARENMISSING); + return FALSE; + } + advancewindow(); + break; + default: + if (nref() == EXPR_FAIL) + return FALSE; + break; + } + } + ref = maketriple(ox); + if (window_token != TK_COLON) + { ref->operand[0] = put_ilit(NO_M_TIMEOUT); + ins_triple(ref); + } + else + { + advancewindow(); + if (!intexpr(&(ref->operand[0]))) + return FALSE; + ins_triple(ref); + newtriple(OC_TIMTRU); + } + return TRUE; +} diff --git a/sr_port/m_merge.c b/sr_port/m_merge.c new file mode 100644 index 0000000..770a7b7 --- /dev/null +++ b/sr_port/m_merge.c @@ -0,0 +1,149 @@ +/**************************************************************** + * * + * Copyright 2001, 2008 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "mdq.h" +#include "opcode.h" +#include "indir_enum.h" +#include "nametabtyp.h" +#include "toktyp.h" +#include "merge_def.h" +#include "cmd.h" +#include "mvalconv.h" +#include "advancewindow.h" + +GBLREF triple *curtchain; +GBLREF char window_token; + +int m_merge(void) +{ + error_def(ERR_VAREXPECTED); + error_def(ERR_RPARENMISSING); + error_def(ERR_EQUAL); + + opctype put_oc; + oprtype mopr; + triple *sub, *ref, *obp, *s1, *restart, tmpchain; + mval mv; + int type; + + restart = newtriple(OC_RESTARTPC); /* Here is where a restart should pick up */ + + dqinit(&tmpchain, exorder); + /* Left Hand Side of EQUAL sign */ + switch (window_token) + { + case TK_IDENT: + if (!lvn(&mopr, OC_PUTINDX, 0)) + return FALSE; + if (OC_PUTINDX == mopr.oprval.tref->opcode) + { /* we insert left hand side argument into tmpchain. */ + dqdel(mopr.oprval.tref, exorder); + dqins(tmpchain.exorder.bl, exorder, mopr.oprval.tref); + } + ref = maketriple(OC_MERGE_LVARG); + ref->operand[0] = put_ilit(MARG1_LCL); + ref->operand[1] = mopr; + dqins(tmpchain.exorder.bl, exorder, ref); + break; + case TK_CIRCUMFLEX: + s1 = curtchain->exorder.bl; + if (!gvn()) + return FALSE; + for (sub = curtchain->exorder.bl; sub != s1; sub = sub->exorder.bl) + { + put_oc = sub->opcode; + if (OC_GVNAME == put_oc || OC_GVNAKED == put_oc || OC_GVEXTNAM == put_oc) + break; + } + assert(OC_GVNAME == put_oc || OC_GVNAKED == put_oc || OC_GVEXTNAM == put_oc); + /* we insert left hand side argument into tmpchain. */ + dqdel(sub, exorder); + dqins(tmpchain.exorder.bl ,exorder, sub); + ref = maketriple(OC_MERGE_GVARG); + ref->operand[0] = put_ilit(MARG1_GBL); + dqins(tmpchain.exorder.bl, exorder, ref); + break; + case TK_ATSIGN: + if (!indirection(&mopr)) + return FALSE; + if (window_token != TK_EQUAL) + { + ref = newtriple(OC_COMMARG); + ref->operand[0] = mopr; + ref->operand[1] = put_ilit((mint) indir_merge); + return TRUE; + } + type = MARG1_LCL | MARG1_GBL; + MV_FORCE_MVAL(&mv, type); + MV_FORCE_STRD(&mv); + ref = maketriple(OC_INDMERGE); + ref->operand[0] = put_lit(&mv); + ref->operand[1] = mopr; + /* we insert left hand side argument into tmpchain. */ + dqins(tmpchain.exorder.bl, exorder, ref); + break; + default: + stx_error(ERR_VAREXPECTED); + return FALSE; + } + + if (window_token != TK_EQUAL) + { + stx_error(ERR_EQUAL); + return FALSE; + } + advancewindow(); + + /* Right Hand Side of EQUAL sign */ + switch (window_token) + { + case TK_IDENT: + if (!lvn(&mopr, OC_M_SRCHINDX, 0)) + return FALSE; + ref = newtriple(OC_MERGE_LVARG); + ref->operand[0] = put_ilit(MARG2_LCL); + ref->operand[1] = mopr; + break; + case TK_CIRCUMFLEX: + if (!gvn()) + return FALSE; + ref = newtriple(OC_MERGE_GVARG); + ref->operand[0] = put_ilit(MARG2_GBL); + break; + case TK_ATSIGN: + if (!indirection(&mopr)) + { + stx_error(ERR_VAREXPECTED); + return FALSE; + } + type = MARG2_LCL | MARG2_GBL; + MV_FORCE_MVAL(&mv, type); + MV_FORCE_STRD(&mv); + ref = maketriple(OC_INDMERGE); + ref->operand[0] = put_lit(&mv); + ref->operand[1] = mopr; + ins_triple(ref); + break; + default: + stx_error(ERR_VAREXPECTED); + return FALSE; + } + /* + * Make sure that during runtime right hand side argument is processed first. + * This is specially important if global naked variable is used . + */ + obp = curtchain->exorder.bl; + dqadd(obp, &tmpchain, exorder); + ref = newtriple(OC_MERGE); + return TRUE; +} diff --git a/sr_port/m_new.c b/sr_port/m_new.c new file mode 100644 index 0000000..c798881 --- /dev/null +++ b/sr_port/m_new.c @@ -0,0 +1,172 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "mdq.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "svnames.h" +#include "nametabtyp.h" +#include "funsvn.h" +#include "advancewindow.h" +#include "cmd.h" +#include "namelook.h" + +LITREF unsigned char svn_index[]; +LITREF nametabent svn_names[]; +GBLREF char window_token; +GBLREF mident window_ident; +LITREF svn_data_type svn_data[]; +GBLREF triple *curr_fetch_trip, *curr_fetch_opr; +GBLREF triple *curtchain; +GBLREF int4 curr_fetch_count; + +error_def(ERR_INVSVN); +error_def(ERR_SVNONEW); +error_def(ERR_SVNEXPECTED); +error_def(ERR_RPARENMISSING); +error_def(ERR_VAREXPECTED); + +int m_new(void) +{ + oprtype tmparg; + triple *ref, *next, *org, *tmp, *s, *fetch; + int n; + int count; + mvar *var; + boolean_t parse_warn; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + switch (window_token) + { + case TK_IDENT: + var = get_mvaddr(&window_ident); + if (var->last_fetch != curr_fetch_trip) + { + fetch = newtriple(OC_PARAMETER); + curr_fetch_opr->operand[1] = put_tref(fetch); + fetch->operand[0] = put_ilit(var->mvidx); + curr_fetch_count++; + curr_fetch_opr = fetch; + var->last_fetch = curr_fetch_trip; + } + tmp = maketriple(OC_NEWVAR); + tmp->operand[0] = put_ilit(var->mvidx); + ins_triple(tmp); + advancewindow(); + return TRUE; + case TK_ATSIGN: + if (!indirection(&tmparg)) + return FALSE; + ref = maketriple(OC_COMMARG); + ref->operand[0] = tmparg; + ref->operand[1] = put_ilit((mint) indir_new); + ins_triple(ref); + start_fetches(OC_FETCH); + return TRUE; + case TK_DOLLAR: + advancewindow(); + if (TK_IDENT == window_token) + { + parse_warn = FALSE; + if ((n = namelook(svn_index, svn_names, window_ident.addr, window_ident.len)) >= 0) + { + switch(svn_data[n].opcode) + { + case SV_ZTRAP: + case SV_ETRAP: + case SV_ESTACK: + case SV_ZYERROR: + case SV_ZGBLDIR: + GTMTRIG_ONLY(case SV_ZTWORMHOLE:) + tmp = maketriple(OC_NEWINTRINSIC); + tmp->operand[0] = put_ilit(svn_data[n].opcode); + break; + default: + STX_ERROR_WARN(ERR_SVNONEW); /* sets "parse_warn" to TRUE */ + } + } else + { + STX_ERROR_WARN(ERR_INVSVN); /* sets "parse_warn" to TRUE */ + } + advancewindow(); + if (!parse_warn) + ins_triple(tmp); + else + { /* OC_RTERROR triple would have been inserted in curtchain by ins_errtriple + * (invoked by stx_error). No need to do anything else. + */ + assert(OC_RTERROR == curtchain->exorder.bl->exorder.bl->exorder.bl->opcode); + } + return TRUE; + } + stx_error(ERR_SVNEXPECTED); + return FALSE; + case TK_EOL: + case TK_SPACE: + tmp = maketriple(OC_XNEW); + tmp->operand[0] = put_ilit((mint) 0); + ins_triple(tmp); + if (TREF(for_stack_ptr) == TADR(for_stack)) + start_fetches (OC_FETCH); + else + start_for_fetches (); + return TRUE; + case TK_LPAREN: + ref = org = maketriple(OC_XNEW); + count = 0; + do + { + advancewindow(); + next = maketriple(OC_PARAMETER); + ref->operand[1] = put_tref(next); + switch (window_token) + { + case TK_IDENT: + next->operand[0] = put_str(window_ident.addr, window_ident.len); + advancewindow(); + break; + case TK_ATSIGN: + if (!indirection(&tmparg)) + return FALSE; + s = newtriple(OC_INDLVARG); + s->operand[0] = tmparg; + next->operand[0] = put_tref(s); + break; + default: + stx_error(ERR_VAREXPECTED); + return FALSE; + } + ins_triple(next); + ref = next; + count++; + } while (TK_COMMA == window_token); + if (TK_RPAREN != window_token) + { + stx_error(ERR_RPARENMISSING); + return FALSE; + } + advancewindow(); + org->operand[0] = put_ilit((mint) count); + ins_triple(org); + if (TREF(for_stack_ptr) == TADR(for_stack)) + start_fetches (OC_FETCH); + else + start_for_fetches (); + return TRUE; + default: + stx_error(ERR_VAREXPECTED); + return FALSE; + } +} diff --git a/sr_port/m_open.c b/sr_port/m_open.c new file mode 100644 index 0000000..851ec91 --- /dev/null +++ b/sr_port/m_open.c @@ -0,0 +1,110 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "iotimer.h" +#include "io_params.h" +#include "advancewindow.h" +#include "cmd.h" +#include "deviceparameters.h" +#include "mdq.h" + +GBLREF char window_token; +LITREF mval literal_null; + +int m_open(void) +{ + int4 rval; + static readonly unsigned char empty_plist[1] = { iop_eol }; + triple tmpchain, *ref1, *ref2, *indref; + oprtype sopr, devpopr, plist, timeout, mspace; + opctype opcd; + boolean_t is_timeout, inddevparms; + + inddevparms = FALSE; + if (!(rval = strexpr(&sopr))) + return FALSE; + if (TK_COLON != window_token) + { /* Single arg specified */ + if (EXPR_INDR == rval) + { /* All arguments indirect */ + make_commarg(&sopr, indir_open); + return TRUE; + } else /* Only device given, default device parms */ + plist = put_str((char *)empty_plist, SIZEOF(empty_plist)); + } else + { + advancewindow(); + switch(window_token) + { + case TK_COLON: + /* Default device parms */ + plist = put_str((char *)empty_plist, SIZEOF(empty_plist)); + break; + case TK_ATSIGN: + /* Indirect for device parms */ + if (!indirection(&devpopr)) + return FALSE; + indref = newtriple(OC_INDDEVPARMS); + indref->operand[0] = devpopr; + indref->operand[1] = put_ilit(IOP_OPEN_OK); + inddevparms = TRUE; + break; + default: + /* Literal device parms specified */ + if (!deviceparameters(&plist, IOP_OPEN_OK)) + return FALSE; + } + } + + /* Code generation for the optional timeout parm */ + is_timeout = FALSE; + if (TK_COLON != window_token) + timeout = put_ilit(NO_M_TIMEOUT); + else + { + advancewindow(); + if (TK_COLON == window_token) + timeout = put_ilit(NO_M_TIMEOUT); + else + { + is_timeout = TRUE; + if (!intexpr(&timeout)) + return FALSE; + } + } + if (TK_COLON != window_token) + mspace = put_lit((mval *)&literal_null); + else + { + advancewindow(); + if (!expr(&mspace)) + return FALSE; + } + ref1 = newtriple(OC_OPEN); + ref1->operand[0] = sopr; + ref2 = newtriple(OC_PARAMETER); + ref1->operand[1] = put_tref(ref2); + ref2->operand[0] = !inddevparms ? plist : put_tref(indref); + ref1 = newtriple(OC_PARAMETER); + ref2->operand[1] = put_tref(ref1); + ref1->operand[0] = timeout; + ref2 = newtriple(OC_PARAMETER); + ref1->operand[1] = put_tref(ref2); + ref2->operand[0] = mspace; + if (is_timeout) + newtriple(OC_TIMTRU); + return TRUE; +} diff --git a/sr_port/m_quit.c b/sr_port/m_quit.c new file mode 100644 index 0000000..69e9f0a --- /dev/null +++ b/sr_port/m_quit.c @@ -0,0 +1,114 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "mmemory.h" +#include "cmd.h" +#include "indir_enum.h" +#include "svnames.h" +#include "advancewindow.h" + +GBLREF char window_token; +GBLREF boolean_t run_time; +GBLREF triple *curtchain; +GBLREF boolean_t dollar_zquit_anyway; + +error_def(ERR_ALIASEXPECTED); +error_def(ERR_QUITARGUSE); +error_def(ERR_QUITARGLST); + +LITREF mval literal_null; + +int m_quit(void) +{ + boolean_t arg; + int rval; + triple *triptr; + triple *r; + oprtype x, *cr, tmparg; + mvar *mvarptr; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + arg = ((TK_EOL != window_token) && (TK_SPACE != window_token)); + if (TREF(for_stack_ptr) == TADR(for_stack)) + { /* not FOR */ + if (dollar_zquit_anyway && !run_time) + { /* turn a plain quit into a set with and without argument conditioned on $QUIT */ + r = newtriple(OC_SVGET); + r->operand[0] = put_ilit(SV_QUIT); + x = put_tref(r); + coerce(&x, OCT_BOOL); + cr = (oprtype *)mcalloc(SIZEOF(oprtype)); /* for jump target */ + bx_tail(x.oprval.tref, (bool)TRUE, cr); + r = newtriple(OC_RET); + x = put_tref(r); + r = newtriple(OC_NOOP); /* need a jump target */ + x = put_tref(r); + *cr = put_tjmp(curtchain->exorder.bl); + if (!arg) + { + r = newtriple(OC_RETARG); + r->operand[0] = put_lit((mval *)&literal_null); + r->operand[1] = put_ilit(FALSE); + x = put_tref(r); + return TRUE; + } + } + if (!arg) + { + newtriple((run_time) ? OC_HARDRET : OC_RET); + return TRUE; + } + /* We now know we have an arg. See if it is an alias indicated arg */ + if (TK_ASTERISK == window_token) + { /* We have QUIT * alias syntax */ + advancewindow(); + if (TK_IDENT == window_token) + { /* Both alias and alias container sources go through here */ + if (!lvn(&tmparg, OC_GETINDX, 0)) + return FALSE; + r = newtriple(OC_RETARG); + r->operand[0] = tmparg; + r->operand[1] = put_ilit(TRUE); + return TRUE; + } else + { /* Unexpected text after alias indicator */ + stx_error(ERR_ALIASEXPECTED); + return FALSE; + } + } else if ((rval = expr(&x)) && (TK_COMMA != window_token)) + { + if (EXPR_INDR != rval) + { + r = newtriple(OC_RETARG); + r->operand[0] = x; + r->operand[1] = put_ilit(FALSE); + } else /* Indirect argument */ + make_commarg(&x, indir_quit); + return TRUE; + } + if (window_token == TK_COMMA) + stx_error (ERR_QUITARGLST); + return FALSE; + } else if (!arg) /* FOR */ + { + triptr = newtriple(OC_JMP); + FOR_END_OF_SCOPE(1, triptr->operand[0]); + return TRUE; + } + stx_error(ERR_QUITARGUSE); + return FALSE; +} diff --git a/sr_port/m_read.c b/sr_port/m_read.c new file mode 100644 index 0000000..2d0df1c --- /dev/null +++ b/sr_port/m_read.c @@ -0,0 +1,190 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "iotimer.h" +#include "mdq.h" +#include "advancewindow.h" +#include "cmd.h" +#include "rwformat.h" + +GBLREF triple *curtchain; +GBLREF mval window_mval; +GBLREF char window_token; + +int m_read(void) +{ + oprtype x, *timeout; + opctype read_oc, put_oc; + triple *ref, tmpchain, *s1, *sub, *put; + boolean_t local; + error_def(ERR_RWARG); + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + TREF(temp_subs) = FALSE; + local = TRUE; + dqinit(&tmpchain, exorder); + switch(window_token) + { + case TK_ASTERISK: + advancewindow(); + switch(window_token) + { + default: + case TK_IDENT: + if (!lvn(&x, OC_PUTINDX, 0)) + return FALSE; + if (OC_PUTINDX == x.oprval.tref->opcode) + { + dqdel(x.oprval.tref, exorder); + dqins(tmpchain.exorder.bl, exorder, x.oprval.tref); + sub = x.oprval.tref; + put_oc = OC_PUTINDX; + } + put = maketriple(OC_STO); + put->operand[0] = x; + dqins(tmpchain.exorder.bl, exorder, put); + break; + case TK_CIRCUMFLEX: + local = FALSE; + s1 = curtchain->exorder.bl; + if (!gvn()) + return FALSE; + for (sub = curtchain->exorder.bl; sub != s1; sub = sub->exorder.bl) + { + put_oc = sub->opcode; + if ((OC_GVNAME == put_oc) || (OC_GVNAKED == put_oc) || (OC_GVEXTNAM == put_oc)) + break; + } + assert((OC_GVNAME == put_oc) || (OC_GVNAKED == put_oc) || (OC_GVEXTNAM == put_oc)); + dqdel(sub, exorder); + dqins(tmpchain.exorder.bl, exorder, sub); + put = maketriple(OC_GVPUT); + dqins(tmpchain.exorder.bl, exorder, put); + break; + case TK_ATSIGN: + if (!indirection(&x)) + return FALSE; + put = maketriple(OC_INDSET); + put->operand[0] = x; + dqins(tmpchain.exorder.bl, exorder, put); + break; + } + if (TK_HASH == window_token) + { + stx_error(ERR_RWARG); + return FALSE; + } + read_oc = OC_RDONE; + break; + case TK_QUESTION: + case TK_EXCLAIMATION: + case TK_HASH: + case TK_SLASH: + return rwformat(); + case TK_STRLIT: + x = put_lit(&window_mval); + advancewindow(); + ref = newtriple(OC_WRITE); + ref->operand[0] = x; + return TRUE; + case TK_IDENT: + if (!lvn(&x, OC_PUTINDX, 0)) + return FALSE; + read_oc = OC_READ; + if (OC_PUTINDX == x.oprval.tref->opcode) + { + dqdel(x.oprval.tref, exorder); + dqins(tmpchain.exorder.bl, exorder, x.oprval.tref); + sub = x.oprval.tref; + put_oc = OC_PUTINDX; + } + put = maketriple(OC_STO); + put->operand[0] = x; + dqins(tmpchain.exorder.bl, exorder, put); + break; + case TK_CIRCUMFLEX: + local = FALSE; + read_oc = OC_READ; + s1 = curtchain->exorder.bl; + if (!gvn()) + return FALSE; + for (sub = curtchain->exorder.bl; sub != s1; sub = sub->exorder.bl) + { + put_oc = sub->opcode; + if ((OC_GVNAME == put_oc) || (OC_GVNAKED == put_oc) || (OC_GVEXTNAM == put_oc)) + break; + } + assert((OC_GVNAME == put_oc) || (OC_GVNAKED == put_oc) || (OC_GVEXTNAM == put_oc)); + dqdel(sub, exorder); + dqins(tmpchain.exorder.bl, exorder, sub); + put = maketriple(OC_GVPUT); + dqins(tmpchain.exorder.bl, exorder, put); + break; + case TK_ATSIGN: + if (!indirection(&x)) + return FALSE; + if ((TK_COLON != window_token) && (TK_HASH != window_token)) + { + ref = maketriple(OC_COMMARG); + ref->operand[0] = x; + ref->operand[1] = put_ilit(indir_read); + ins_triple(ref); + return TRUE; + } + put = maketriple(OC_INDSET); + put->operand[0] = x; + dqins(tmpchain.exorder.bl, exorder, put); + read_oc = OC_READ; + break; + default: + stx_error(ERR_RWARG); + return FALSE; + } + if (TK_HASH == window_token) + { + advancewindow(); + ref = maketriple(OC_READFL); + if (!intexpr(&ref->operand[0])) + return FALSE; + timeout = &ref->operand[1]; + } else + { + ref = maketriple(read_oc); + timeout = &ref->operand[0]; + } + if (TK_COLON != window_token) + { + *timeout = put_ilit(NO_M_TIMEOUT); + ins_triple(ref); + } else + { + advancewindow(); + if (!intexpr(timeout)) + return FALSE; + ins_triple(ref); + newtriple(OC_TIMTRU); + } + if (local) + put->operand[1] = put_tref(ref); + else + put->operand[0] = put_tref(ref); + + ref = curtchain->exorder.bl; + dqadd(ref, &tmpchain, exorder); /*this is a violation of info hiding*/ + + return TRUE; +} diff --git a/sr_port/m_set.c b/sr_port/m_set.c new file mode 100644 index 0000000..e91d6d2 --- /dev/null +++ b/sr_port/m_set.c @@ -0,0 +1,950 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gtm_ctype.h" + +#include "compiler.h" +#include "mdq.h" +#include "opcode.h" +#include "indir_enum.h" +#include "nametabtyp.h" +#include "toktyp.h" +#include "funsvn.h" +#include "mmemory.h" +#include "advancewindow.h" +#include "namelook.h" +#include "cmd.h" +#include "svnames.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "alias.h" +#ifdef UNICODE_SUPPORTED +#include "gtm_utf8.h" +#endif + +GBLREF boolean_t badchar_inhibit; +GBLREF triple *curtchain; +GBLREF char director_token, window_token; +GBLREF mident director_ident, window_ident; +GBLREF boolean_t gtm_utf8_mode; + +error_def(ERR_INVSVN); +error_def(ERR_VAREXPECTED); +error_def(ERR_RPARENMISSING); +error_def(ERR_EQUAL); +error_def(ERR_COMMA); +error_def(ERR_SVNOSET); +error_def(ERR_NOALIASLIST); +error_def(ERR_ALIASEXPECTED); +error_def(ERR_DZWRNOPAREN); +error_def(ERR_DZWRNOALIAS); + +LITREF unsigned char svn_index[], fun_index[]; +LITREF nametabent svn_names[], fun_names[]; +LITREF svn_data_type svn_data[]; +LITREF fun_data_type fun_data[]; + +#define FIRST_SETLEFT_NOTSEEN -1 /* see comment against variable "first_setleft_invalid" for details */ + +/* This macro is used to insert the conditional jump triples (in SET $PIECE/$EXTRACT) ahead of the global variable + * reference of the SET $PIECE target. This is to ensure the naked indicator is not touched in cases where the M-standard + * says it should not be. e.g. set $piece(^x,"delim",2,1) should not touch naked indicator since 2>1. + */ +#define DQINSCURTARGCHAIN(curtargtriple) \ +{ \ + dqins(curtargchain, exorder, curtargtriple); \ + assert(curtargchain->exorder.fl == curtargtriple); \ + curtargchain = curtargtriple; \ +} + +#define RESTORE_CURTCHAIN_IF_NEEDED \ +{ \ + if (curtchain_switched) \ + { \ + assert(NULL != save_curtchain); \ + setcurtchain(save_curtchain); \ + curtchain_switched = FALSE; \ + } \ +} + +#define SYNTAX_ERROR(errnum) \ +{ \ + RESTORE_CURTCHAIN_IF_NEEDED; \ + stx_error(errnum); \ + return FALSE; \ +} + +#define SYNTAX_ERROR_NOREPORT_HERE \ +{ \ + RESTORE_CURTCHAIN_IF_NEEDED; \ + return FALSE; \ +} + +void m_set_create_temporaries(triple *sub, opctype put_oc); +void allow_dzwrtac_as_mident(void); + +int m_set(void) +{ + /* Some comment on "parse_warn". It is set to TRUE whenever the parse encounters an + invalid setleft target. + + * Note that even if "parse_warn" is TRUE, we should not return FALSE right away but need to continue the parse + * until the end of the current SET command. This way any remaining commands in the current parse line will be + * parsed and triples generated for them. This is necessary just in case the currently parsed invalid SET command + * does not get executed at runtime (due to postconditionals etc.) + * + * Some comment on the need for "first_setleft_invalid". This variable is needed only in the + * case we encounter an invalid-SVN/invalid-FCN/unsettable-SVN as a target of the SET. We need to evaluate the + * right-hand-side of the SET command only if at least one valid setleft target is parsed before an invalid setleft + * target is encountered. This is because we still need to execute the valid setlefts at runtime before triggering + * a runtime error for the invalid setleft. If the first setleft target is an invalid one, then there is no need + * to evaluate the right-hand-side. In fact, in this case, adding triples (corresponding to the right hand side) + * to the execution chain could cause problems with emit_code later in the compilation as the destination + * for the right hand side triples could now be undefined (for example a valid SVN on the left side of the + * SET would have generated an OC_SVPUT triple with one of its operands holding the result of the right + * hand side evaluation, but an invalid SVN on the left side which would have instead caused an OC_RTERROR triple + * to have been generated leaving no triple to receive the result of the right hand side evaluation thus causing + * emit_code to be confused and GTMASSERT). Therefore discard all triples generated by the right hand side in this case. + * By the same reasoning, discard all triples generated by setleft targets AFTER this invalid one as well. + * "first_setleft_invalid" is set to TRUE if the first setleft target is invalid and set to FALSE if the first setleft + * target is valid. It is initialized to -1 before the start of the parse. + */ + + int index, setop, delimlen; + int first_val_lit, last_val_lit, nakedzalias; + boolean_t first_is_lit, last_is_lit, got_lparen, delim1char, is_extract, valid_char; + boolean_t alias_processing, have_lh_alias; + opctype put_oc; + oprtype v, delimval, firstval, lastval, *result, resptr; + triple *curtargchain, *delimiter, discardcurtchain, *first, *get, *jmptrp1, *jmptrp2, *last, *obp, *put; + triple *s, *s0, *s1, save_targchain, *save_curtchain, *save_curtchain1, *sub, targchain, *tmp; + mint delimlit; + mval *delim_mval; + mvar *mvarptr; + boolean_t parse_warn; /* set to TRUE in case of an invalid SVN etc. */ + boolean_t curtchain_switched; /* set to TRUE if a setcurtchain was done */ + int first_setleft_invalid; /* set to TRUE if the first setleft target is invalid */ + boolean_t temp_subs_was_FALSE; + union + { + uint4 unichar_val; + unsigned char unibytes_val[4]; + } unichar; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + TREF(temp_subs) = FALSE; + dqinit(&targchain, exorder); + result = (oprtype *)mcalloc(SIZEOF(oprtype)); + resptr = put_indr(result); + delimiter = sub = last = NULL; + /* A SET clause must be entirely alias related or a normal set. Parenthized multiple sets of aliases are not allowed + * and will trigger an error. This is because the source and targets of aliases require different values and references + * than normal sets do and thus cannot be mixed. + */ + if (alias_processing = (TK_ASTERISK == window_token)) + advancewindow(); + if (got_lparen = (TK_LPAREN == window_token)) + { + if (alias_processing) + stx_error(ERR_NOALIASLIST); + advancewindow(); + TREF(temp_subs) = TRUE; + } + /* Some explanation: The triples from the left hand side of the SET expression that are + * expressly associated with fetching (in case of set $piece/$extract) and/or storing of + * the target value are removed from curtchain and placed on the targchain. Later, these + * triples will be added to the end of curtchain to do the finishing store of the target + * after the righthand side has been evaluated. This is per the M standard. + * + * Note that SET $PIECE/$EXTRACT have special conditions in which the first argument is not referenced at all. + * (e.g. set $piece(^a," ",3,2) in this case 3 > 2 so this should not evaluate ^a and therefore should not + * modify the naked indicator). That is, the triples that do these conditional checks need to be inserted + * ahead of the OC_GVNAME of ^a, all of which need to be inserted on the targchain. But the conditionalization + * can be done only after parsing the first argument of the SET $PIECE and examining the remaining arguments. + * Therefore we maintain the "curtargchain" variable which stores the value of the "targchain" at the beginning + * of the iteration (at the start of the $PIECE parsing) and all the conditionalization will be inserted right + * here which is guaranteed to be ahead of where the OC_GVNAME gets inserted. + * + * For example, SET $PIECE(^A(x,y),delim,first,last)=RHS will generate a final triple chain as follows + * + * A - Triples to evaluate subscripts (x,y) of the global ^A + * A - Triples to evaluate delim + * A - Triples to evaluate first + * A - Triples to evaluate last + * B - Triples to evaluate RHS + * C - Triples to do conditional check (e.g. first > last etc.) + * C - Triples to branch around if the checks indicate this is a null operation SET $PIECE + * D - Triple that does OC_GVNAME of ^A + * D - Triple that does OC_SETPIECE to determine the new value + * D - Triple that does OC_GVPUT of the new value into ^A(x,y) + * This is the point where the conditional check triples will branch around to if they chose to. + * + * A - triples that evaluates the arguments/subscripts in the left-hand-side of the SET command + * These triples are built in "curtchain" + * B - triples that evaluates the arguments/subscripts in the right-hand-side of the SET command + * These triples are built in "curtchain" + * C - triples that do conditional check for any $PIECE/$EXTRACT in the left side of the SET command. + * These triples are built in "curtargchain" + * D - triples that generate the reference to the target of the SET and the store into the target. + * These triples are built in "targchain" + * + * Note alias processing does not support the SET *(...)=.. type syntax because the type of argument + * created for RHS processing is dependent on the LHS receiver type and we do not support more than one + * type of source argument in a single SET. + */ + first_setleft_invalid = FIRST_SETLEFT_NOTSEEN; + curtchain_switched = FALSE; + nakedzalias = have_lh_alias = FALSE; + save_curtchain = NULL; + assert(FIRST_SETLEFT_NOTSEEN != TRUE); + assert(FIRST_SETLEFT_NOTSEEN != FALSE); + for (parse_warn = FALSE; ; parse_warn = FALSE) + { + curtargchain = targchain.exorder.bl; + jmptrp1 = jmptrp2 = NULL; + delim1char = is_extract = FALSE; + allow_dzwrtac_as_mident(); /* Allows $ZWRTACxxx as target to be treated as an mident */ + switch (window_token) + { + case TK_IDENT: + /* A slight diversion first. If this is a $ZWRTAC set (indication of $ in first char + * is currently enough to signify that), then we need to check a few conditions first. + * If this is a "naked $ZWRTAC", meaning no numeric suffix, then this is a flag that + * all the $ZWRTAC vars in the local variable tree need to be kill *'d which will not + * be generating a SET instruction. First we need to verify that fact and make sure + * we are not in PARENs and not doing alias processing. Note *any* value can be + * specified as the source but while it will be evaluated, it is NOT stored anywhere. + */ + if ('$' == *window_ident.addr) + { /* We have a $ZWRTAC target */ + if (got_lparen) + /* We don't allow $ZWRTACxxx to be specified in a parenthesized list. + * Verify that first + */ + SYNTAX_ERROR(ERR_DZWRNOPAREN); + if (STR_LIT_LEN(DOLLAR_ZWRTAC) == window_ident.len) + { /* Ok, this is a naked $ZWRTAC targeted set */ + if (alias_processing) + SYNTAX_ERROR(ERR_DZWRNOALIAS); + nakedzalias = TRUE; + /* This opcode doesn't really need args but it is easier to fit in with the rest + * of m_set processing to pass it the result arg, which there may actually be + * a use for someday.. + */ + put = maketriple(OC_CLRALSVARS); + put->operand[0] = resptr; + dqins(targchain.exorder.bl, exorder, put); + advancewindow(); + break; + } + } + /* If we are doing alias processing, there are two possibilities: + * 1) LHS is unsubscripted - it is an alias variable being created or replaced. Need to parse + * the varname as if this were a regular set. + * 2) LHS is subscripted - it is an alias container variable being created or replaced. The + * processing here is to pass the base variable index to the store routine so bypass the + * lvn() call. + */ + if (!alias_processing || TK_LPAREN == director_token) + { /* Normal variable processing or we have a lh alias container */ + if (!lvn(&v, OC_PUTINDX, 0)) + SYNTAX_ERROR_NOREPORT_HERE; + if (OC_PUTINDX == v.oprval.tref->opcode) + { + dqdel(v.oprval.tref, exorder); + dqins(targchain.exorder.bl, exorder, v.oprval.tref); + sub = v.oprval.tref; + put_oc = OC_PUTINDX; + if (TREF(temp_subs)) + m_set_create_temporaries(sub, put_oc); + } + } else + { /* Have alias variable. Argument is index into var table rather than pointer to var */ + have_lh_alias = TRUE; + /* We only want the variable index in this case. Since the entire hash structure to which + * this variable is going to be pointing to is changing, doing anything that calls fetch() + * is somewhat pointless so we avoid it by just accessing the variable information + * directly. + */ + mvarptr = get_mvaddr(&window_ident); + v = put_ilit(mvarptr->mvidx); + advancewindow(); + } + /* Determine correct storing triple */ + put = maketriple((!alias_processing ? OC_STO : + (have_lh_alias ? OC_SETALS2ALS : OC_SETALSIN2ALSCT))); + put->operand[0] = v; + put->operand[1] = resptr; + dqins(targchain.exorder.bl, exorder, put); + break; + case TK_CIRCUMFLEX: + if (alias_processing) + SYNTAX_ERROR(ERR_ALIASEXPECTED); + s1 = curtchain->exorder.bl; + if (!gvn()) + SYNTAX_ERROR_NOREPORT_HERE; + for (sub = curtchain->exorder.bl; sub != s1; sub = sub->exorder.bl) + { + put_oc = sub->opcode; + if (OC_GVNAME == put_oc || OC_GVNAKED == put_oc || OC_GVEXTNAM == put_oc) + break; + } + assert(OC_GVNAME == put_oc || OC_GVNAKED == put_oc || OC_GVEXTNAM == put_oc); + dqdel(sub, exorder); + dqins(targchain.exorder.bl, exorder, sub); + if (TREF(temp_subs)) + m_set_create_temporaries(sub, put_oc); + put = maketriple(OC_GVPUT); + put->operand[0] = resptr; + dqins(targchain.exorder.bl, exorder, put); + break; + case TK_ATSIGN: + if (alias_processing) + SYNTAX_ERROR(ERR_ALIASEXPECTED); + if (!indirection(&v)) + SYNTAX_ERROR_NOREPORT_HERE; + if (!got_lparen && TK_EQUAL != window_token) + { + assert(!curtchain_switched); + put = newtriple(OC_COMMARG); + put->operand[0] = v; + put->operand[1] = put_ilit(indir_set); + return TRUE; + } + put = maketriple(OC_INDSET); + put->operand[0] = v; + put->operand[1] = resptr; + dqins(targchain.exorder.bl, exorder, put); + break; + case TK_DOLLAR: + if (alias_processing) + SYNTAX_ERROR(ERR_ALIASEXPECTED); + advancewindow(); + if (TK_IDENT != window_token) + SYNTAX_ERROR(ERR_VAREXPECTED); + if (TK_LPAREN != director_token) + { /* Look for intrinsic special variables */ + s1 = curtchain->exorder.bl; + if (0 > (index = namelook(svn_index, svn_names, window_ident.addr, window_ident.len))) + { + STX_ERROR_WARN(ERR_INVSVN); /* sets "parse_warn" to TRUE */ + } else if (!svn_data[index].can_set) + { + STX_ERROR_WARN(ERR_SVNOSET); /* sets "parse_warn" to TRUE */ + } + advancewindow(); + if (!parse_warn) + { + if (SV_ETRAP != svn_data[index].opcode && SV_ZTRAP != svn_data[index].opcode) + { /* Setting of $ZTRAP or $ETRAP must go through opp_svput because they + * may affect the stack pointer. All others directly to op_svput(). + */ + put = maketriple(OC_SVPUT); + } else + put = maketriple(OC_PSVPUT); + put->operand[0] = put_ilit(svn_data[index].opcode); + put->operand[1] = resptr; + dqins(targchain.exorder.bl, exorder, put); + } else + { /* OC_RTERROR triple would have been inserted in curtchain by ins_errtriple + * (invoked by stx_error). To maintain consistency with the "if" portion of + * this code, we need to move this triple to the "targchain". + */ + tmp = curtchain->exorder.bl; /* corresponds to put_ilit(FALSE) in ins_errtriple */ + tmp = tmp->exorder.bl; /* corresponds to put_ilit(in_error) in ins_errtriple */ + tmp = tmp->exorder.bl; /* corresponds to newtriple(OC_RTERROR) in ins_errtriple */ + assert(OC_RTERROR == tmp->opcode); + dqdel(tmp, exorder); + dqins(targchain.exorder.bl, exorder, tmp); + CHKTCHAIN(&targchain); + } + break; + } + /* Only 4 function names allowed on left side: $[Z]Piece and $[Z]Extract */ + index = namelook(fun_index, fun_names, window_ident.addr, window_ident.len); + if (0 > index) + { + STX_ERROR_WARN(ERR_INVFCN); /* sets "parse_warn" to TRUE */ + /* OC_RTERROR triple would have been inserted in "curtchain" by ins_errtriple + * (invoked by stx_error). We need to switch it to "targchain" to be consistent + * with every other codepath in this module. + */ + tmp = curtchain->exorder.bl; /* corresponds to put_ilit(FALSE) in ins_errtriple */ + tmp = tmp->exorder.bl; /* corresponds to put_ilit(in_error) in ins_errtriple */ + tmp = tmp->exorder.bl; /* corresponds to newtriple(OC_RTERROR) in ins_errtriple */ + assert(OC_RTERROR == tmp->opcode); + dqdel(tmp, exorder); + dqins(targchain.exorder.bl, exorder, tmp); + CHKTCHAIN(&targchain); + advancewindow(); /* skip past the function name */ + advancewindow(); /* skip past the left paren */ + /* Parse the remaining arguments until corresponding RIGHT-PAREN/SPACE/EOL is reached */ + if (!parse_until_rparen_or_space()) + SYNTAX_ERROR_NOREPORT_HERE; + } else + { + switch(fun_data[index].opcode) + { + case OC_FNPIECE: + setop = OC_SETPIECE; + break; + case OC_FNEXTRACT: + is_extract = TRUE; + setop = OC_SETEXTRACT; + break; + case OC_FNZPIECE: + setop = OC_SETZPIECE; + break; + case OC_FNZEXTRACT: + is_extract = TRUE; + setop = OC_SETZEXTRACT; + break; + default: + SYNTAX_ERROR(ERR_VAREXPECTED); + } + advancewindow(); + advancewindow(); + /* Although we see the get (target) variable first, we need to save it's processing + * on another chain -- the targchain -- because the retrieval of the target is bypassed + * and the naked indicator is not reset if the first/last parameters are not set in a + * logical manner (must be > 0 and first <= last). So the evaluation order is + * delimiter (if $piece), first, last, RHS of the set and then the target if applicable. + * Set up primary action triple now since it is ref'd by the put triples generated below. + */ + s = maketriple(setop); + /* Even for SET[Z]PIECE and SET[Z]EXTRACT, the SETxxxxx opcodes + * do not do the final store, they only create the final value TO be + * stored so generate the triples that will actually do the store now. + * Note we are still building triples on the original curtchain. + */ + switch (window_token) + { + case TK_IDENT: + if (!lvn(&v, OC_PUTINDX, 0)) + SYNTAX_ERROR(ERR_VAREXPECTED); + if (OC_PUTINDX == v.oprval.tref->opcode) + { + dqdel(v.oprval.tref, exorder); + dqins(targchain.exorder.bl, exorder, v.oprval.tref); + sub = v.oprval.tref; + put_oc = OC_PUTINDX; + if (TREF(temp_subs)) + m_set_create_temporaries(sub, put_oc); + } + get = maketriple(OC_FNGET); + get->operand[0] = v; + put = maketriple(OC_STO); + put->operand[0] = v; + put->operand[1] = put_tref(s); + break; + case TK_ATSIGN: + if (!indirection(&v)) + SYNTAX_ERROR(ERR_VAREXPECTED); + get = maketriple(OC_INDGET); + get->operand[0] = v; + get->operand[1] = put_str(0, 0); + put = maketriple(OC_INDSET); + put->operand[0] = v; + put->operand[1] = put_tref(s); + break; + case TK_CIRCUMFLEX: + s1 = curtchain->exorder.bl; + if (!gvn()) + SYNTAX_ERROR_NOREPORT_HERE; + for (sub = curtchain->exorder.bl; sub != s1 ; sub = sub->exorder.bl) + { + put_oc = sub->opcode; + if ((OC_GVNAME == put_oc) || (OC_GVNAKED == put_oc) + || (OC_GVEXTNAM == put_oc)) + break; + } + assert((OC_GVNAME == put_oc) || (OC_GVNAKED == put_oc) + || (OC_GVEXTNAM == put_oc)); + dqdel(sub, exorder); + dqins(targchain.exorder.bl, exorder, sub); + if (TREF(temp_subs)) + m_set_create_temporaries(sub, put_oc); + get = maketriple(OC_FNGVGET); + get->operand[0] = put_str(0, 0); + put = maketriple(OC_GVPUT); + put->operand[0] = put_tref(s); + break; + default: + SYNTAX_ERROR(ERR_VAREXPECTED); + } + s->operand[0] = put_tref(get); + /* Code to fetch args for target triple are on targchain. Put get there now too. */ + dqins(targchain.exorder.bl, exorder, get); + CHKTCHAIN(&targchain); + if (!is_extract) + { /* Set $[z]piece */ + delimiter = newtriple(OC_PARAMETER); + s->operand[1] = put_tref(delimiter); + first = newtriple(OC_PARAMETER); + delimiter->operand[1] = put_tref(first); + /* Process delimiter string ($[z]piece only) */ + if (TK_COMMA != window_token) + SYNTAX_ERROR(ERR_COMMA); + advancewindow(); + if (!strexpr(&delimval)) + SYNTAX_ERROR_NOREPORT_HERE; + assert(TRIP_REF == delimval.oprclass); + } else + { /* Set $[Z]Extract */ + first = newtriple(OC_PARAMETER); + s->operand[1] = put_tref(first); + } + /* Process first integer value */ + if (window_token != TK_COMMA) + firstval = put_ilit(1); + else + { + advancewindow(); + if (!intexpr(&firstval)) + SYNTAX_ERROR(ERR_COMMA); + assert(firstval.oprclass == TRIP_REF); + } + first->operand[0] = firstval; + if (first_is_lit = (OC_ILIT == firstval.oprval.tref->opcode)) + { + assert(ILIT_REF ==firstval.oprval.tref->operand[0].oprclass); + first_val_lit = firstval.oprval.tref->operand[0].oprval.ilit; + } + if (TK_COMMA != window_token) + { /* There is no "last" value. Only if 1 char literal delimiter and + * no "last" value can we generate shortcut code to op_set[z]p1 entry + * instead of op_set[z]piece. Note if UTF8 mode is in effect, then this + * optimization applies if the literal is one unicode char which may in + * fact be up to 4 bytes but will still be passed as a single unsigned + * integer. + */ + if (!is_extract) + { + delim_mval = &delimval.oprval.tref->operand[0].oprval.mlit->v; + valid_char = TRUE; /* Basic assumption unles proven otherwise */ + if (delimval.oprval.tref->opcode == OC_LIT && + (1 == (gtm_utf8_mode ? + MV_FORCE_LEN(delim_mval) : delim_mval->str.len))) + { /* Single char delimiter for set $piece */ + UNICODE_ONLY( + if (gtm_utf8_mode) + { /* We have a supposed single char delimiter but it + * must be a valid utf8 char to be used by + * op_setp1() and MV_FORCE_LEN won't tell us that. + */ + valid_char = UTF8_VALID(delim_mval->str.addr, + (delim_mval->str.addr + + delim_mval->str.len), + delimlen); + if (!valid_char && !badchar_inhibit) + UTF8_BADCHAR(0, delim_mval->str.addr, + (delim_mval->str.addr + + delim_mval->str.len), + 0, NULL); + } + ); + if (valid_char || 1 == delim_mval->str.len) + { /* This reference to a one character literal or a single + * byte invalid utf8 character that needs to be turned into + * an explict formated integer literal instead + */ + unichar.unichar_val = 0; + if (!gtm_utf8_mode) + { /* Single byte delimiter */ + assert(1 == delim_mval->str.len); + UNIX_ONLY(s->opcode = OC_SETZP1); + VMS_ONLY(s->opcode = OC_SETP1); + unichar.unibytes_val[0] = *delim_mval->str.addr; + } + UNICODE_ONLY( + else + { /* Potentially multiple bytes in one int */ + assert(SIZEOF(int) >= delim_mval->str.len); + memcpy(unichar.unibytes_val, + delim_mval->str.addr, + delim_mval->str.len); + s->opcode = OC_SETP1; + } + ); + delimlit = (mint)unichar.unichar_val; + delimiter->operand[0] = put_ilit(delimlit); + delim1char = TRUE; + } + } + } + if (!delim1char) + { /* Was not handled as a single char delim by code above either bcause it + * was (1) not set $piece, or (2) was not a single char delim or (3) it was + * not a VALID utf8 single char delim and badchar was inhibited. + */ + if (!is_extract) + delimiter->operand[0] = delimval; + last = newtriple(OC_PARAMETER); + first->operand[1] = put_tref(last); + last->operand[0] = first->operand[0]; /* start = end range */ + } + /* Generate test sequences for first/last to bypass the set operation if + * first/last are not in a usable form + */ + if (first_is_lit) + { + if (1 > first_val_lit) + { + jmptrp1 = maketriple(OC_JMP); + DQINSCURTARGCHAIN(jmptrp1); + } + /* note else no test necessary since first == last and are > 0 */ + } else + { /* Generate test for first being <= 0 */ + jmptrp1 = maketriple(OC_COBOOL); + jmptrp1->operand[0] = first->operand[0]; + DQINSCURTARGCHAIN(jmptrp1); + jmptrp1 = maketriple(OC_JMPLEQ); + DQINSCURTARGCHAIN(jmptrp1); + } + } else + { /* There IS a last value */ + if (!is_extract) + delimiter->operand[0] = delimval; + last = newtriple(OC_PARAMETER); + first->operand[1] = put_tref(last); + advancewindow(); + if (!intexpr(&lastval)) + SYNTAX_ERROR_NOREPORT_HERE; + assert(lastval.oprclass == TRIP_REF); + last->operand[0] = lastval; + /* Generate inline code to test first/last for usability and if found + * lacking, branch around the getchain and the actual store so we avoid + * setting the naked indicator so far as the target gvn is concerned. + */ + if (last_is_lit = (lastval.oprval.tref->opcode == OC_ILIT)) + { /* Case 1: last is a literal */ + assert(lastval.oprval.tref->operand[0].oprclass == ILIT_REF); + last_val_lit = lastval.oprval.tref->operand[0].oprval.ilit; + if (last_val_lit < 1 || (first_is_lit && first_val_lit > last_val_lit)) + { /* .. and first is a literal and one or both of them is no good + * so unconditionally branch around the whole thing. + */ + jmptrp1 = maketriple(OC_JMP); + DQINSCURTARGCHAIN(jmptrp1); + } /* else case actually handled at next 'if' .. */ + } else + { /* Last is not literal. Do test if it is greater than 0 */ + jmptrp1 = maketriple(OC_COBOOL); + jmptrp1->operand[0] = last->operand[0]; + DQINSCURTARGCHAIN(jmptrp1); + jmptrp1 = maketriple(OC_JMPLEQ); + DQINSCURTARGCHAIN(jmptrp1); + } + if (!last_is_lit || !first_is_lit) + { /* Compare to check that last >= first */ + jmptrp2 = maketriple(OC_VXCMPL); + jmptrp2->operand[0] = first->operand[0]; + jmptrp2->operand[1] = last->operand[0]; + DQINSCURTARGCHAIN(jmptrp2); + jmptrp2 = maketriple(OC_JMPGTR); + DQINSCURTARGCHAIN(jmptrp2); + } + } + } + if (window_token != TK_RPAREN) + SYNTAX_ERROR(ERR_RPARENMISSING); + advancewindow(); + if (!parse_warn) + { + dqins(targchain.exorder.bl, exorder, s); + dqins(targchain.exorder.bl, exorder, put); + CHKTCHAIN(&targchain); + /* Put result operand on the chain. End of chain depends on whether or not + * we are calling the shortcut or the full set-piece code + */ + if (delim1char) + first->operand[1] = resptr; + else + last->operand[1] = resptr; + /* Set jump targets if we did tests above. The function "tnxtarg" operates on "curtchain" + * but we want to set targets based on "targchain", so temporarily switch them. Should not + * use "save_curtchain" here as it might already be in use. + */ + save_curtchain1 = setcurtchain(&targchain); + if (NULL != jmptrp1) + tnxtarg(&jmptrp1->operand[0]); + if (NULL != jmptrp2) + tnxtarg(&jmptrp2->operand[0]); + setcurtchain(save_curtchain1); + } + break; + case TK_ASTERISK: + /* The only way an asterisk can be detected here is if we are inside a list so mention this is + * not possible and give error. + */ + stx_error(ERR_NOALIASLIST); + return FALSE; + default: + SYNTAX_ERROR(ERR_VAREXPECTED); + } + if (FIRST_SETLEFT_NOTSEEN == first_setleft_invalid) + { + first_setleft_invalid = parse_warn; + if (first_setleft_invalid) + { /* We are not going to evaluate the right hand side of the SET command. This means + * we should not evaluate any more setleft targets (whether they parse validly or not) + * as well since their source (the RHS of the set) is undefined. To achieve this, we + * switch to a temporary chain (both for curtchain and targchain) that will be discarded finally. + */ + /* save curtchain */ + dqinit(&discardcurtchain, exorder); + save_curtchain = setcurtchain(&discardcurtchain); + assert(!curtchain_switched); + curtchain_switched = TRUE; + /* save targchain */ + save_targchain = targchain; + dqinit(&targchain, exorder); + } + } + assert(FIRST_SETLEFT_NOTSEEN != first_setleft_invalid); + if (!got_lparen) + break; + if (TK_COMMA == window_token) + advancewindow(); + else + { + if (TK_RPAREN == window_token) + { + advancewindow(); + break; + } else + SYNTAX_ERROR(ERR_RPARENMISSING); + } + } + if (TK_EQUAL != window_token) + SYNTAX_ERROR(ERR_EQUAL); + advancewindow(); + assert(FIRST_SETLEFT_NOTSEEN != first_setleft_invalid); + temp_subs_was_FALSE = (FALSE == TREF(temp_subs)); /* Note down if temp_subs is FALSE at this point */ + /* If we are in alias processing mode, the RHS cannot be an expression but must be one of a subscripted or unsubscripted + * local variable, or a $$func(..) function call. + */ + if (!alias_processing) + { /* Normal case first - evaluate expression creating triples on the current chain */ + if (!expr(result)) + SYNTAX_ERROR_NOREPORT_HERE; + } else + { /* Alias processing -- determine which of the three types of sources we have: var, subscripted var or $$func */ + allow_dzwrtac_as_mident(); /* Allow source of $ZWRTACxxx as an mident */ + if (TK_IDENT != window_token) + { /* Check if we have a $$func() call source */ + if (TK_DOLLAR == window_token && TK_DOLLAR == director_token) + { /* Parse the function only with exfunc(). We definitely do not want an expression */ + TREF(temp_subs) = TRUE; /* RHS $$ function detected - need temporary */ + advancewindow(); + if (!exfunc(result, TRUE)) + SYNTAX_ERROR_NOREPORT_HERE; + if (OC_SETALSIN2ALSCT == put->opcode) + /* Change opcode to create an alias container from the returned alias */ + put->opcode = OC_SETFNRETIN2ALSCT; + else + { /* Change opcode to create an alias from the returned alias */ + assert(OC_SETALS2ALS == put->opcode); + put->opcode = OC_SETFNRETIN2ALS; + } + } else + /* Else, only local variables allowed as aliases */ + SYNTAX_ERROR(ERR_ALIASEXPECTED); + } else + { /* Alias var source */ + if ('$' == *window_ident.addr && STR_LIT_LEN(DOLLAR_ZWRTAC) >= window_ident.len) + /* $ZWRTAC is not allowed as a "source" value. Must be a $ZWRTACn format */ + SYNTAX_ERROR(ERR_DZWRNOALIAS); + if (TK_LPAREN == director_token) + { /* Subscripted local variable - have alias container. + * The storing opcode set into the "put" triple at the top of this routine was + * set assuming the source was an alias. Now that we know the source is actually + * an alias container (and hence a different data type), we need to adjust the + * opcode accordingly. + */ + if (OC_SETALS2ALS == put->opcode) + put->opcode = OC_SETALSCTIN2ALS; + else + { + assert(OC_SETALSIN2ALSCT == put->opcode); + put->opcode = OC_SETALSCT2ALSCT; + } + } + /* For RHS processing, both alias var and alias container vars have their lv_val addr + * passed so normal var processing applies. + */ + if (!lvn(result, OC_GETINDX, 0)) + SYNTAX_ERROR(ERR_ALIASEXPECTED); + } + } + if (first_setleft_invalid) + { /* switch from the temporary chain back to the current execution chain */ + assert(curtchain_switched); + RESTORE_CURTCHAIN_IF_NEEDED; /* does a setcurtchain(save_curtchain) */ + targchain = save_targchain; + } + /* Now add in the left-hand side triples */ + assert(!curtchain_switched); + obp = curtchain->exorder.bl; + dqadd(obp, &targchain, exorder); /* this is a violation of info hiding */ + CHKTCHAIN(curtchain); + /* Check if "temp_subs" was FALSE originally but got set to TRUE as part of evaluating the right hand side + * (for example, if rhs had $$ or $& or $INCR usages). If so need to create temporaries. + */ + if (TREF(temp_subs) && temp_subs_was_FALSE && (NULL != sub)) + m_set_create_temporaries(sub, put_oc); + TREF(temp_subs) = FALSE; + return TRUE; +} + +/* This function adds triples to the execution chain to store the values of subscripts (in glvns in compound SETs) + * in temporaries (using OC_STOTEMP opcode). This function is only invoked when + * a) The SET is a compound SET (i.e. there are multiple targets specified on the left side of the SET command). + * b) Subscripts are specified in glvns which are targets of the SET. + * e.g. set a=0,(a,array(a))=1 + * The expected result of the above command as per the M-standard is that array(0) (not array(1)) gets set to 1. + * That is, the value of the subscript "a" should be evaluated at the start of the compound SET before any sets happen + * and should be used in any subscripts that refer to the name "a". + * In the above example, since it is a compound SET and "a" is used in a subscript, we need to store the value of "a" + * before the start of the compound SET (i.e.a=0) in a temporary and use that as the subscript for "array". + * If in the above example the compound set was instead specified as set a=1,array(a)=1, the value of 1 gets substituted + * when used in "array(a)". + * This is where the compound set acts differently from a sequence of multiple sets. This is per the M-standard. + * In the above example, the subscript used was also a target within the compound SET. It is possible that the + * subscript is not also an individual target within the same compound SET. Even in that case, this function + * will be called to store the subscript in temporaries (as we dont know at compile time if a particular + * subscript is also used as a target within a compound SET). + */ +void m_set_create_temporaries(triple *sub, opctype put_oc) +{ + oprtype *sb1; + triple *s0, *s1; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + assert(TREF(temp_subs)); + assert(NULL != sub); + sb1 = &sub->operand[1]; + if ((OC_GVNAME == put_oc) || (OC_PUTINDX == put_oc)) + { + sub = sb1->oprval.tref; /* global name */ + assert(sub->opcode == OC_PARAMETER); + sb1 = &sub->operand[1]; + } else if (OC_GVEXTNAM == put_oc) + { + sub = sb1->oprval.tref; /* first env */ + assert(sub->opcode == OC_PARAMETER); + sb1 = &sub->operand[0]; + assert(sb1->oprclass == TRIP_REF); + s0 = sb1->oprval.tref; + if ((OC_GETINDX == s0->opcode) || (OC_VAR == s0->opcode)) + { + s1 = maketriple(OC_STOTEMP); + s1->operand[0] = *sb1; + *sb1 = put_tref(s1); + s0 = s0->exorder.fl; + dqins(s0->exorder.bl, exorder, s1); + } + sb1 = &sub->operand[1]; + sub = sb1->oprval.tref; /* second env */ + assert(sub->opcode == OC_PARAMETER); + sb1 = &sub->operand[0]; + assert(sb1->oprclass == TRIP_REF); + s0 = sb1->oprval.tref; + if ((OC_GETINDX == s0->opcode) || (OC_VAR == s0->opcode)) + { + s1 = maketriple(OC_STOTEMP); + s1->operand[0] = *sb1; + *sb1 = put_tref(s1); + s0 = s0->exorder.fl; + dqins(s0->exorder.bl, exorder, s1); + } + sb1 = &sub->operand[1]; + sub = sb1->oprval.tref; /* global name */ + assert(sub->opcode == OC_PARAMETER); + sb1 = &sub->operand[1]; + } + while (sb1->oprclass) + { + assert(sb1->oprclass == TRIP_REF); + sub = sb1->oprval.tref; + assert(sub->opcode == OC_PARAMETER); + sb1 = &sub->operand[0]; + assert(sb1->oprclass == TRIP_REF); + s0 = sb1->oprval.tref; + if ((OC_GETINDX == s0->opcode) || (OC_VAR == s0->opcode)) + { + s1 = maketriple(OC_STOTEMP); + s1->operand[0] = *sb1; + *sb1 = put_tref(s1); + s0 = s0->exorder.fl; + dqins(s0->exorder.bl, exorder, s1); + } + sb1 = &sub->operand[1]; + } +} + +/* Prior to Alias support, the ZWRITE command was able to dump the entire local variable environment such that it could be + * reloaded by Xecuting the dumped lines. With the addition of Alias type variables, the output lines not only include the + * variable content but their alias associations as well. One aspect of this is the $ZWRTAC variable which is a temporary + * "variable" which we allow to hold the content of what would otherwise be "orphaned data" or data which has a container + * pointing to it but is not referenced by a base variable. During the reload operation, it is this orphaned data that is + * put into the $ZWRTAC environment. The first orphaned array is put into $ZWRTAC1(...), the second into $ZWRTAC2(..) and + * so on. Setting the $ZWRTAC variable itself to null (or to any value actually - null only is not enforced) causes all of + * the $ZWRTAC variables to be KILL *'d. The only syntactic allowances for using $ZWRTACxxx as an mident in GTM are in this + * SET statement. We do not support its use in any other statement type. This routine allows us to modify the tokens so that + * if the current token is a '$' and the next token is ZWRTACxxx, we can combine them into a single token and the parser + * need not be further modified. + */ +void allow_dzwrtac_as_mident(void) +{ + char_ptr_t chrp, chrpmin, chrplast; + int movlen; + + if (TK_DOLLAR != window_token) + return; /* Couldn't be $ZWRTACxxx without first $ */ + if (TK_IDENT != director_token) + return; /* Couldn't be $ZWRTACxxx without token 2nd part */ + if (STR_LIT_LEN("ZWRTAC") > director_ident.len) + return; /* Couldn't be $ZALAISxxx without sufficient length of name */ + if (0 != MEMCMP_LIT(director_ident.addr, "ZWRTAC")) + return; /* Couldn't be $ZWRTACxxx without ZWRTAC as first part of token */ + /* We need to shift the existing token over 1 byte to make room for insertion of the '$' prefix. Normally, + * we wouldn't want to do this as we are verifying but since if the verification fails the code path will + * raise an error and since the error does not use this token buffer, we are safe in migrating while + * we do the verification check. Saves us having to scan the line backwards via memmove() again below. So + * verify the token suffix is all numeric while we do our shift. + */ + movlen = (director_ident.len < MAX_MIDENT_LEN) ? director_ident.len : MAX_MIDENT_LEN - 1; + for (chrplast = director_ident.addr + movlen, chrp = chrplast - 1, + chrpmin = director_ident.addr + STR_LIT_LEN("ZWRTAC") - 1; + chrp > chrpmin; + --chrp, --chrplast) + { + if (!ISDIGIT_ASCII((int)*chrp)) + return; /* Couldn't be $ZWRTACxxx without all numeric suffix (if exists) */ + *chrplast = *chrp; + } + /* Verification (and shift) complete -- finish modifying director token */ + MEMCPY_LIT(director_ident.addr, DOLLAR_ZWRTAC); + director_ident.len = movlen + 1; + /* Nnw forward the scan to pull director token values into window token for use by our caller */ + advancewindow(); + assert(TK_IDENT == window_token); +} diff --git a/sr_port/m_tcommit.c b/sr_port/m_tcommit.c new file mode 100644 index 0000000..9634b90 --- /dev/null +++ b/sr_port/m_tcommit.c @@ -0,0 +1,31 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "cmd.h" + +GBLREF char window_token; + +int m_tcommit(void) +{ + error_def(ERR_SPOREOL); + + if (window_token != TK_EOL && window_token != TK_SPACE) + { + stx_error(ERR_SPOREOL); + return FALSE; + } + newtriple(OC_TCOMMIT); + return TRUE; +} diff --git a/sr_port/m_trestart.c b/sr_port/m_trestart.c new file mode 100644 index 0000000..8e7d743 --- /dev/null +++ b/sr_port/m_trestart.c @@ -0,0 +1,33 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "cmd.h" + +GBLREF char window_token; + +int m_trestart(void) +{ + triple *ref; + error_def(ERR_SPOREOL); + + if (window_token != TK_EOL && window_token != TK_SPACE) + { + stx_error(ERR_SPOREOL); + return FALSE; + } + ref = newtriple(OC_TRESTART); + ref->operand[0] = put_ilit(1); + return TRUE; +} diff --git a/sr_port/m_trollback.c b/sr_port/m_trollback.c new file mode 100644 index 0000000..de3408f --- /dev/null +++ b/sr_port/m_trollback.c @@ -0,0 +1,49 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "indir_enum.h" +#include "opcode.h" +#include "toktyp.h" +#include "cmd.h" + +GBLREF char window_token; +GBLREF mval window_mval; + +int m_trollback(void) +{ + triple *ref; + oprtype x; + + if (TK_SPACE == window_token || TK_EOL == window_token) + { + ref = newtriple(OC_TROLLBACK); + ref->operand[0] = put_ilit(0); + return TRUE; + } else + { + switch (intexpr(&x)) + { + case EXPR_FAIL: + return FALSE; + case EXPR_GOOD: + ref = newtriple(OC_TROLLBACK); + ref->operand[0] = x; + return TRUE; + case EXPR_INDR: + make_commarg(&x,indir_trollback); + return TRUE; + } + } + + return FALSE; /* This should never get executed, added to make compiler happy */ +} diff --git a/sr_port/m_tstart.c b/sr_port/m_tstart.c new file mode 100644 index 0000000..fad06d8 --- /dev/null +++ b/sr_port/m_tstart.c @@ -0,0 +1,198 @@ +/**************************************************************** + * * + * Copyright 2001, 2004 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "nametabtyp.h" +#include "indir_enum.h" +#include "advancewindow.h" +#include "cmd.h" +#include "namelook.h" + +static readonly nametabent tst_param_names[] = +{ + {1,"S"} + ,{6,"SERIAL"} + + ,{1,"T"} + ,{8,"TRANSACT*"} +}; +/* Offset of letter with dev_param_names */ +static readonly unsigned char tst_param_index[27] = +{ +/* A B C D E F G H I J K L M N */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* O P Q R S T U V W X Y Z end */ + 0, 0, 0, 0, 0, 2, 4, 4, 4, 4, 4, 4, 4 +}; + +GBLREF char window_token; +GBLREF mident window_ident; + +int m_tstart(void) +{ + triple *ser, *tid, *ref, *varlst, *varp, *varnext, *fetch, *s; + oprtype tmparg; + int count, n; + bool has_ser, has_tid, has_lpar, bad_parse; + mval dummyid; + error_def(ERR_RPARENMISSING); + error_def(ERR_VAREXPECTED); + error_def(ERR_TSTRTPARM); + + count = -1; /* indicates not restartable */ + varlst = newtriple(OC_PARAMETER); + switch (window_token) + { + case TK_IDENT: + count = 1; + varlst->operand[1] = put_str(window_ident.addr, window_ident.len); + advancewindow(); + break; + case TK_ASTERISK: + count = -2; /* indicates save all variables */ + advancewindow(); + break; + case TK_ATSIGN: + if (!indirection(&tmparg)) + return FALSE; + if (window_token == TK_COLON) + { + ref = newtriple(OC_COMMARG); + ref->operand[0] = tmparg; + ref->operand[1] = put_ilit((mint) indir_tstart); + return TRUE; + } + else + { count = 1; + fetch = newtriple(OC_INDLVARG); + fetch->operand[0] = tmparg; + varlst->operand[1] = put_tref(fetch); + } + break; + case TK_EOL: + case TK_SPACE: + break; + case TK_LPAREN: + varp = varlst; + for(count = 0; ;) + { + advancewindow(); + switch (window_token) + { + case TK_IDENT: + varnext = newtriple(OC_PARAMETER); + varnext->operand[0] = put_str(window_ident.addr, window_ident.len); + varp->operand[1] = put_tref(varnext); + varp = varnext; + advancewindow(); + count++; + break; + case TK_ATSIGN: + if (!indirection(&tmparg)) + return FALSE; + s = newtriple(OC_INDLVARG); + s->operand[0] = tmparg; + varnext = newtriple(OC_PARAMETER); + varnext->operand[0] = put_tref(s); + varp->operand[1] = put_tref(varnext); + varp = varnext; + count++; + break; + case TK_RPAREN: + break; + default: + stx_error(ERR_VAREXPECTED); + return FALSE; + } + if (window_token != TK_COMMA) + break; + } + if (window_token != TK_RPAREN) + { + stx_error(ERR_RPARENMISSING); + return FALSE; + } + advancewindow(); + break; + } + varlst->operand[0] = put_ilit(count); + has_ser = has_tid = FALSE; + tid = newtriple(OC_PARAMETER); + if (window_token == TK_COLON) + { advancewindow(); + if (has_lpar = (window_token == TK_LPAREN)) + advancewindow(); + for(;;) + { + bad_parse = TRUE; + if (window_token == TK_IDENT) + { + if ((n = namelook(tst_param_index, tst_param_names, window_ident.addr, window_ident.len)) >= 0) + { + if (n < 2 && !has_ser) + { has_ser = TRUE; + bad_parse = FALSE; + advancewindow(); + } + else if (!has_tid) + { + advancewindow(); + if (window_token == TK_EQUAL) + { advancewindow(); + if (!expr(&tid->operand[0])) + return FALSE; + has_tid = TRUE; + bad_parse = FALSE; + } + } + } + } + if (bad_parse) + { stx_error(ERR_TSTRTPARM); + return FALSE; + } + if (!has_lpar || window_token == TK_RPAREN) + break; + if (window_token != TK_COLON) + { stx_error(ERR_TSTRTPARM); + return FALSE; + } + advancewindow(); + } + if (has_lpar) + { assert(window_token == TK_RPAREN); + advancewindow(); + } + } + if (!has_tid) + { + dummyid.mvtype = MV_STR; + dummyid.str.len = 0; + tid->operand[0] = put_lit(&dummyid); + } + tid->operand[1] = put_tref(varlst); +/* ser = newtriple(OC_PARAMETER); + ser->operand[0] = put_ilit(has_ser); + ser->operand[1] = put_tref(tid); + s = newtriple(OC_TSTARTPC); + ref = newtriple(OC_TSTART); + ref->operand[0] = put_tref(s); + ref->operand[1] = put_tref(ser); +*/ + ref = newtriple(OC_TSTART); + ref->operand[0] = put_ilit(has_ser); + ref->operand[1] = put_tref(tid); + + return TRUE; +} diff --git a/sr_port/m_use.c b/sr_port/m_use.c new file mode 100644 index 0000000..bdbeb0f --- /dev/null +++ b/sr_port/m_use.c @@ -0,0 +1,67 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "io_params.h" +#include "indir_enum.h" +#include "advancewindow.h" +#include "cmd.h" +#include "deviceparameters.h" + +GBLREF char window_token; + +int m_use(void) +{ + oprtype sopr, plist, devpopr; + int4 rval; + triple *ref; + triple *indref; + boolean_t inddevparms; + static readonly unsigned char empty_plist[1] = { iop_eol }; + + inddevparms = FALSE; + if (!(rval = strexpr(&sopr))) + return FALSE; + if (window_token != TK_COLON) + { /* Single parameter */ + if (rval == EXPR_INDR) + { /* Indirect entire parameter list */ + make_commarg(&sopr, indir_use); + return TRUE; + } else + { /* default device parms */ + plist = put_str((char *)empty_plist, SIZEOF(empty_plist)); + } + } else + { /* Have device parms. Determine type */ + advancewindow(); + if (TK_ATSIGN == window_token) + { /* Have indirect device parms */ + if (!indirection(&devpopr)) + return FALSE; + indref = newtriple(OC_INDDEVPARMS); + indref->operand[0] = devpopr; + indref->operand[1] = put_ilit(IOP_USE_OK); + inddevparms = TRUE; + } else + { /* Process device parameters now */ + if (!deviceparameters(&plist, IOP_USE_OK)) + return FALSE; + } + } + ref = newtriple(OC_USE); + ref->operand[0] = sopr; + ref->operand[1] = !inddevparms ? plist : put_tref(indref); + return TRUE; +} diff --git a/sr_port/m_view.c b/sr_port/m_view.c new file mode 100644 index 0000000..ef64242 --- /dev/null +++ b/sr_port/m_view.c @@ -0,0 +1,75 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "toktyp.h" +#include "opcode.h" +#include "indir_enum.h" +#include "advancewindow.h" +#include "cmd.h" + +GBLREF char window_token; + +int m_view(void) +{ + + oprtype argv[CHARMAXARGS], *argp; + triple *view, *parm, *parm1; + unsigned short count; + error_def(ERR_FCHARMAXARGS); + + argp = &argv[0]; + count = 0; + switch (expr(argp)) + { + case EXPR_FAIL: + return FALSE; + case EXPR_INDR: + if (window_token != TK_COLON) + { make_commarg(argp,indir_view); + return TRUE; + } + /* caution: fall through */ + case EXPR_GOOD: + view = maketriple(OC_VIEW); + parm = newtriple(OC_PARAMETER); + view->operand[1] = put_tref(parm); + parm->operand[0] = *argp; + count++; + argp++; + break; + } + + for (;;) + { + if (window_token != TK_COLON) + break; + + advancewindow(); + if (!expr(argp)) + return FALSE; + parm1 = newtriple(OC_PARAMETER); + parm->operand[1] = put_tref(parm1); + parm1->operand[0] = *argp; + parm = parm1; + count++; + argp++; + if (count >= CHARMAXARGS) + { + stx_error(ERR_FCHARMAXARGS); + return FALSE; + } + } + view->operand[0] = put_ilit(count); + ins_triple(view); + return TRUE; +} diff --git a/sr_port/m_write.c b/sr_port/m_write.c new file mode 100644 index 0000000..e968d7b --- /dev/null +++ b/sr_port/m_write.c @@ -0,0 +1,153 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "compiler.h" +#include "stringpool.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "advancewindow.h" +#include "cmd.h" +#include "rwformat.h" + +GBLREF char window_token; +GBLREF spdesc stringpool; +GBLREF bool devctlexp; + +#define STO_LLPTR(X) (llptr ? *++llptr = (X) : 0) +#define LITLST_TOP (&litlst[(SIZEOF(litlst) / SIZEOF(triple *)) - 2]) +int m_write(void) +{ + error_def(ERR_STRINGOFLOW); + oprtype x,*oprptr; + mval lit; + mstr *msp; + int lnx; + char *cp; + triple *ref, *t1; + triple *litlst[128], **llptr, **ptx, **ltop; + + llptr = litlst; + ltop = 0; + *llptr = 0; + for (;;) + { + devctlexp = FALSE; + switch(window_token) + { + case TK_ASTERISK: + advancewindow(); + if (!intexpr(&x)) + return FALSE; + assert(x.oprclass == TRIP_REF); + ref = newtriple(OC_WTONE); + ref->operand[0] = x; + STO_LLPTR((x.oprval.tref->opcode == OC_ILIT) ? ref : 0); + break; + case TK_QUESTION: + case TK_EXCLAIMATION: + case TK_HASH: + case TK_SLASH: + if (!rwformat()) + return FALSE; + STO_LLPTR(0); + break; + default: + switch (strexpr(&x)) + { + case EXPR_FAIL: + return FALSE; + case EXPR_GOOD: + assert(x.oprclass == TRIP_REF); + if (devctlexp) + { + ref = newtriple(OC_WRITE); + ref->operand[0] = x; + STO_LLPTR(0); + } else if (x.oprval.tref->opcode == OC_CAT) + { + wrtcatopt(x.oprval.tref,&llptr,LITLST_TOP); + } else + { + ref = newtriple(OC_WRITE); + ref->operand[0] = x; + STO_LLPTR((x.oprval.tref->opcode == OC_LIT) ? ref : 0); + } + break; + case EXPR_INDR: + make_commarg(&x,indir_write); + STO_LLPTR(0); + break; + default: + assert(FALSE); + } + break; + } + if (window_token != TK_COMMA) + break; + advancewindow(); + if (llptr >= LITLST_TOP) + { + *++llptr = 0; + ltop = llptr; + llptr = 0; + } + } + STO_LLPTR(0); + if (ltop) + llptr = ltop; + for (ptx = litlst ; ptx < llptr ; ptx++) + { + if (*ptx && *(ptx + 1)) + { + lit.mvtype = MV_STR; + lit.str.addr = cp = (char * ) stringpool.free; + for (t1 = ref = *ptx++ ; ref ; ref = *ptx++) + { + if (ref->opcode == OC_WRITE) + { + msp = &(ref->operand[0].oprval.tref->operand[0].oprval.mlit->v.str); + lnx = msp->len; + if ( cp + lnx > (char *) stringpool.top) + { stx_error(ERR_STRINGOFLOW); + return FALSE; + } + memcpy(cp, msp->addr, lnx); + cp += lnx; + } + else + { + assert(ref->opcode == OC_WTONE); + if (cp + 1 > (char *) stringpool.top) + { stx_error(ERR_STRINGOFLOW); + return FALSE; + } + *cp++ = ref->operand[0].oprval.tref->operand[0].oprval.ilit; + } + ref->operand[0].oprval.tref->opcode = OC_NOOP; + ref->opcode = OC_NOOP; + ref->operand[0].oprval.tref->operand[0].oprclass = OC_NOOP; + ref->operand[0].oprclass = 0; + } + ptx--; + stringpool.free = (unsigned char *) cp; + lit.str.len = INTCAST(cp - lit.str.addr); + s2n(&lit); + t1->opcode = OC_WRITE; + t1->operand[0] = put_lit(&lit); + } + } + return TRUE; +} diff --git a/sr_port/m_xecute.c b/sr_port/m_xecute.c new file mode 100644 index 0000000..1362895 --- /dev/null +++ b/sr_port/m_xecute.c @@ -0,0 +1,79 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "mdq.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "mmemory.h" +#include "advancewindow.h" +#include "cmd.h" + +GBLREF char window_token; + +int m_xecute(void) +{ + triple tmpchain, *oldchain, *obp, *ref0, *ref1, *triptr; + oprtype *cr, x; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + dqinit(&tmpchain,exorder); + oldchain = setcurtchain(&tmpchain); + switch (strexpr(&x)) + { + case EXPR_FAIL: + setcurtchain(oldchain); + return FALSE; + case EXPR_INDR: + if (window_token != TK_COLON) + { + make_commarg(&x,indir_xecute); + break; + } + /* caution: fall through */ + case EXPR_GOOD: + ref0 = maketriple(OC_COMMARG); + ref0->operand[0] = x; + ref0->operand[1] = put_ilit(indir_linetail); + ins_triple(ref0); + } + setcurtchain(oldchain); + if (window_token == TK_COLON) + { + advancewindow(); + cr = (oprtype *) mcalloc(SIZEOF(oprtype)); + if (!bool_expr((bool) FALSE,cr)) + return FALSE; + if (TREF(expr_start) != TREF(expr_start_orig)) + { + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(TREF(expr_start)); + } + obp = oldchain->exorder.bl; + dqadd(obp,&tmpchain,exorder); /* violates info hiding */ + if (TREF(expr_start) != TREF(expr_start_orig)) + { + ref0 = newtriple(OC_JMP); + ref1 = newtriple(OC_GVRECTARG); + ref1->operand[0] = put_tref(TREF(expr_start)); + *cr = put_tjmp(ref1); + tnxtarg(&ref0->operand[0]); + } else + tnxtarg(cr); + return TRUE; + } + obp = oldchain->exorder.bl; + dqadd(obp,&tmpchain,exorder); /* violates info hiding */ + return TRUE; +} diff --git a/sr_port/m_zallocate.c b/sr_port/m_zallocate.c new file mode 100644 index 0000000..1b1017f --- /dev/null +++ b/sr_port/m_zallocate.c @@ -0,0 +1,83 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "iotimer.h" +#include "indir_enum.h" +#include "advancewindow.h" +#include "cmd.h" + +GBLREF char window_token; + +int m_zallocate(void) +{ + + triple *ref; + oprtype indopr; + bool indirect; + + error_def(ERR_RPARENMISSING); + + newtriple(OC_RESTARTPC); + indirect = FALSE; + newtriple(OC_LKINIT); + switch(window_token) + { + case TK_ATSIGN: + if (!indirection(&indopr)) + return FALSE; + ref = newtriple(OC_COMMARG); + ref->operand[0] = indopr; + if (TK_COLON != window_token) + { + ref->operand[1] = put_ilit((mint)indir_zallocate); + return TRUE; + } + ref->operand[1] = put_ilit((mint)indir_nref); + indirect = TRUE; + break; + case TK_LPAREN: + do + { + advancewindow(); + if (EXPR_FAIL == nref()) + return FALSE; + } while (TK_COMMA == window_token); + if (TK_RPAREN != window_token) + { + stx_error(ERR_RPARENMISSING); + return FALSE; + } + advancewindow(); + break; + default: + if (EXPR_FAIL == nref()) + return FALSE; + break; + } + ref = maketriple(OC_ZALLOCATE); + if (TK_COLON != window_token) + { + ref->operand[0] = put_ilit(NO_M_TIMEOUT); + ins_triple(ref); + } else + { + advancewindow(); + if (!intexpr(&(ref->operand[0]))) + return EXPR_FAIL; + ins_triple(ref); + newtriple(OC_TIMTRU); + } + return EXPR_GOOD; +} diff --git a/sr_port/m_zattach.c b/sr_port/m_zattach.c new file mode 100644 index 0000000..9058e00 --- /dev/null +++ b/sr_port/m_zattach.c @@ -0,0 +1,47 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "cmd.h" + +GBLREF char window_token; + +int m_zattach(void) +{ + oprtype x; + triple *triptr; + + if (window_token == TK_EOL || window_token == TK_SPACE) + { + triptr = newtriple(OC_ZATTACH); + triptr->operand[0] = put_str("",0); + return TRUE; + } + else + switch (strexpr(&x)) + { + case EXPR_FAIL: + return FALSE; + case EXPR_GOOD: + triptr = newtriple(OC_ZATTACH); + triptr->operand[0] = x; + return TRUE; + case EXPR_INDR: + make_commarg(&x,indir_zattach); + return TRUE; + } + + return FALSE; /* This should never get executed, added to make compiler happy */ +} diff --git a/sr_port/m_zbreak.c b/sr_port/m_zbreak.c new file mode 100644 index 0000000..95e965d --- /dev/null +++ b/sr_port/m_zbreak.c @@ -0,0 +1,139 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "advancewindow.h" +#include "cmd.h" + +#define CANCEL_ONE -1 +#define CANCEL_ALL -2 + +GBLREF char window_token, director_token; +GBLREF mident window_ident; +GBLREF boolean_t run_time; +GBLREF mident routine_name; +LITREF mident zero_ident; + +int m_zbreak(void) +{ + triple *ref, *next; + oprtype label, offset, routine, action, count; + bool cancel, cancel_all, is_count, dummybool; + error_def(ERR_LABELEXPECTED); + error_def(ERR_RTNNAME); + + label = put_str((char *)zero_ident.addr, zero_ident.len); + cancel_all = FALSE; + action = put_str("B", 1); + if (window_token == TK_MINUS) + { + advancewindow(); + cancel = TRUE; + count = put_ilit((mint)CANCEL_ONE); + } else + { + cancel = FALSE; + count = put_ilit((mint)0); + } + if (window_token == TK_ASTERISK) + { + if (cancel) + { + advancewindow(); + cancel_all = TRUE; + if (!run_time) + routine = put_str(routine_name.addr, routine_name.len); + else + routine = put_tref(newtriple(OC_CURRTN)); + offset = put_ilit((mint) 0); + count = put_ilit((mint) CANCEL_ALL); + } else + { + stx_error(ERR_LABELEXPECTED); + return FALSE; + } + } else + { + offset = put_ilit((mint) 0); + if (!lref(&label,&offset, TRUE, indir_zbreak, !cancel, &dummybool)) + return FALSE; + if (label.oprclass == TRIP_REF && label.oprval.tref->opcode == OC_COMMARG) + return TRUE; + if (window_token != TK_CIRCUMFLEX) + { /* Routine not specified, assume current routine */ + if (!run_time) + routine = put_str(routine_name.addr, routine_name.len); + else + routine = put_tref(newtriple(OC_CURRTN)); + } else + { + advancewindow(); + switch(window_token) + { + case TK_IDENT: +# ifdef GTM_TRIGGER + if (TK_HASH == director_token) + /* Coagulate tokens as necessary (and available) to allow '#' in the rtn name */ + advwindw_hash_in_mname_allowed(); +# endif + routine = put_str(window_ident.addr, window_ident.len); + advancewindow(); + break; + case TK_ATSIGN: + if (!indirection(&routine)) + return FALSE; + break; + default: + stx_error(ERR_RTNNAME); + return FALSE; + } + } + if (!cancel && window_token == TK_COLON) + { + advancewindow(); + if (window_token == TK_COLON) + { + is_count = TRUE; + action = put_str("B",1); + } else + { + if (!strexpr(&action)) + return FALSE; + is_count = window_token == TK_COLON; + } + if (is_count) + { + advancewindow(); + if (!intexpr(&count)) + return FALSE; + } + } + } + ref = newtriple(OC_SETZBRK); + ref->operand[0] = label; + next = newtriple(OC_PARAMETER); + ref->operand[1] = put_tref(next); + next->operand[0] = offset; + ref = newtriple(OC_PARAMETER); + next->operand[1] = put_tref(ref); + ref->operand[0] = routine; + next = newtriple(OC_PARAMETER); + ref->operand[1] = put_tref(next); + next->operand[0] = action; + ref = newtriple(OC_PARAMETER); + next->operand[1] = put_tref(ref); + ref->operand[0] = count; + return TRUE; +} diff --git a/sr_port/m_zcompile.c b/sr_port/m_zcompile.c new file mode 100644 index 0000000..8143951 --- /dev/null +++ b/sr_port/m_zcompile.c @@ -0,0 +1,30 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "cmd.h" + +int m_zcompile(void) +{ + oprtype x; + triple *triptr; + + if(!strexpr(&x)) + return FALSE; + + triptr = newtriple(OC_ZCOMPILE); + triptr->operand[0] = x; + triptr->operand[1] = put_ilit(FALSE); /* mExtReqd arg */ + return TRUE; +} diff --git a/sr_port/m_zcontinue.c b/sr_port/m_zcontinue.c new file mode 100644 index 0000000..16843d8 --- /dev/null +++ b/sr_port/m_zcontinue.c @@ -0,0 +1,26 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "cmd.h" + +int m_zcontinue(void) +{ + + oprtype x; + + newtriple(OC_ZCONT); + return TRUE; + +} diff --git a/sr_port/m_zdeallocate.c b/sr_port/m_zdeallocate.c new file mode 100644 index 0000000..63bec7f --- /dev/null +++ b/sr_port/m_zdeallocate.c @@ -0,0 +1,69 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "iotimer.h" +#include "indir_enum.h" +#include "advancewindow.h" +#include "cmd.h" + +GBLREF char window_token; + +int m_zdeallocate(void) +{ + + triple *ref; + oprtype indopr; + bool indirect; + + error_def(ERR_RPARENMISSING); + + indirect = FALSE; + newtriple(OC_LKINIT); + switch(window_token) + { + case TK_EOL: + case TK_SPACE: + break; + case TK_ATSIGN: + if (!indirection(&indopr)) + return FALSE; + ref = newtriple(OC_COMMARG); + ref->operand[0] = indopr; + ref->operand[1] = put_ilit((mint)indir_zdeallocate); + return TRUE; + break; + case TK_LPAREN: + do + { + advancewindow(); + if (EXPR_FAIL == nref()) + return FALSE; + } while (TK_COMMA == window_token); + if (TK_RPAREN != window_token) + { + stx_error(ERR_RPARENMISSING); + return FALSE; + } + advancewindow(); + break; + default: + if (EXPR_FAIL == nref()) + return FALSE; + break; + } + ref = newtriple(OC_ZDEALLOCATE); + ref->operand[0] = put_ilit(NO_M_TIMEOUT); + return EXPR_GOOD; +} diff --git a/sr_port/m_zedit.c b/sr_port/m_zedit.c new file mode 100644 index 0000000..852fd87 --- /dev/null +++ b/sr_port/m_zedit.c @@ -0,0 +1,69 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "svnames.h" +#include "advancewindow.h" +#include "cmd.h" + +GBLREF char window_token; + +int m_zedit(void) +{ + + int4 rval; + triple *ref; + oprtype file,opts; + + if (window_token == TK_EOL || window_token == TK_SPACE || + window_token == TK_COLON) + { + ref = newtriple(OC_SVGET); + ref->operand[0] = put_ilit(SV_ZSOURCE); + file = put_tref(ref); + if (window_token == TK_COLON) + { + advancewindow(); + if (!strexpr(&opts)) + return FALSE; + } + else + opts = put_str("",0); + } + else + { + if (!(rval = strexpr(&file))) + return FALSE; + if (window_token != TK_COLON) + { + if (rval == EXPR_INDR) + { + make_commarg(&file,indir_zedit); + return TRUE; + } + opts = put_str("",0); + } + else + { + advancewindow(); + if (!strexpr(&opts)) + return FALSE; + } + } + ref = newtriple(OC_ZEDIT); + ref->operand[0] = file; + ref->operand[1] = opts; + return TRUE; +} diff --git a/sr_port/m_zgoto.c b/sr_port/m_zgoto.c new file mode 100644 index 0000000..c7dd058 --- /dev/null +++ b/sr_port/m_zgoto.c @@ -0,0 +1,114 @@ +/**************************************************************** + * * + * Copyright 2010, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "mdq.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "mmemory.h" +#include "advancewindow.h" +#include "cmd.h" + +GBLREF char window_token; + +error_def(ERR_COLON); + +int m_zgoto(void) +{ + triple tmpchain, *oldchain, *obp, *ref0, *ref1, *triptr; + oprtype *cr, quits; + int4 rval; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + dqinit(&tmpchain, exorder); + oldchain = setcurtchain(&tmpchain); + if ((TK_EOL == window_token) || (TK_SPACE == window_token)) + { /* Default zgoto level is 1 */ + quits = put_ilit(1); + rval = EXPR_GOOD; + } else if (!(rval = intexpr(&quits))) /* note assignment */ + { + setcurtchain(oldchain); + return FALSE; + } + if ((EXPR_INDR != rval) && ((TK_EOL == window_token) || (TK_SPACE == window_token))) + { /* Only level parm supplied (no entry ref) - job for op_zg1 */ + setcurtchain(oldchain); + obp = oldchain->exorder.bl; + dqadd(obp, &tmpchain, exorder); /* this is a violation of info hiding */ + ref0 = newtriple(OC_ZG1); + ref0->operand[0] = quits; + return TRUE; + } + if (TK_COLON != window_token) + { /* First arg parsed, not ending in ":". Better have been indirect */ + setcurtchain(oldchain); + if (EXPR_INDR != rval) + { + stx_error(ERR_COLON); + return FALSE; + } + make_commarg(&quits, indir_zgoto); + obp = oldchain->exorder.bl; + dqadd(obp, &tmpchain, exorder); /* this is a violation of info hiding */ + return TRUE; + } + advancewindow(); + if (TK_COLON != window_token) + { + if (!entryref(OC_NOOP, OC_PARAMETER, (mint)indir_goto, FALSE, FALSE, TRUE)) + { + setcurtchain(oldchain); + return FALSE; + } + ref0 = maketriple(OC_ZGOTO); + ref0->operand[0] = quits; + ref0->operand[1] = put_tref(tmpchain.exorder.bl); + ins_triple(ref0); + setcurtchain(oldchain); + } else + { + ref0 = maketriple(OC_ZG1); + ref0->operand[0] = quits; + ins_triple(ref0); + setcurtchain(oldchain); + } + if (TK_COLON == window_token) + { /* post conditional expression */ + advancewindow(); + cr = (oprtype *)mcalloc(SIZEOF(oprtype)); + if (!bool_expr((bool)FALSE, cr)) + return FALSE; + if (TREF(expr_start) != TREF(expr_start_orig)) + { + triptr = newtriple(OC_GVRECTARG); + triptr->operand[0] = put_tref(TREF(expr_start)); + } + obp = oldchain->exorder.bl; + dqadd(obp, &tmpchain, exorder); /* this is a violation of info hiding */ + if (TREF(expr_start) != TREF(expr_start_orig)) + { + ref0 = newtriple(OC_JMP); + ref1 = newtriple(OC_GVRECTARG); + ref1->operand[0] = put_tref(TREF(expr_start)); + *cr = put_tjmp(ref1); + tnxtarg(&ref0->operand[0]); + } else + tnxtarg(cr); + return TRUE; + } + obp = oldchain->exorder.bl; + dqadd(obp, &tmpchain, exorder); /* this is a violation of info hiding */ + return TRUE; +} diff --git a/sr_port/m_zhalt.c b/sr_port/m_zhalt.c new file mode 100644 index 0000000..b4fb34b --- /dev/null +++ b/sr_port/m_zhalt.c @@ -0,0 +1,51 @@ +/**************************************************************** + * * + * Copyright 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "cmd.h" +#include "compiler.h" +#include "indir_enum.h" +#include "opcode.h" +#include "toktyp.h" + +GBLREF char window_token; + +LITREF mval literal_zero; + +/* Halt the process similar to op_halt but allow a return code to be specified. If no return code + * is specified, return code 0 is used as a default (making it identical to op_halt). + */ +int m_zhalt(void) +{ + triple *triptr; + oprtype ot; + int status; + + /* Let m_halt() handle the case of the missing return code */ + if (window_token == TK_SPACE || window_token == TK_EOL) + return m_halt(); + switch (status = numexpr(&ot)) /* note assignment */ + { + case EXPR_FAIL: + return FALSE; + case EXPR_GOOD: + triptr = newtriple(OC_ZHALT); + triptr->operand[0] = ot; + return TRUE; + case EXPR_INDR: + make_commarg(&ot, indir_zhalt); + return TRUE; + default: + GTMASSERT; /* At least in debug builds we will see the status */ + } + return FALSE; /* This should never get executed, added to make compiler happy */ +} diff --git a/sr_port/m_zhelp.c b/sr_port/m_zhelp.c new file mode 100644 index 0000000..5904366 --- /dev/null +++ b/sr_port/m_zhelp.c @@ -0,0 +1,64 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "advancewindow.h" +#include "cmd.h" + +GBLREF char window_token; + +int m_zhelp (void) +{ + triple *ref; + int rval; + oprtype text, lib; + + if (window_token == TK_EOL || window_token == TK_SPACE || window_token == TK_COLON) + { + text = put_str("",0); + if (window_token == TK_COLON) + { + advancewindow(); + if (!strexpr(&lib)) + return FALSE; + } + else + lib = put_str("",0); + } + else + { + if (!(rval = strexpr(&text))) + return FALSE; + if (window_token != TK_COLON) + { + if (rval == EXPR_INDR) + { + make_commarg(&text,indir_zhelp); + return TRUE; + } + lib = put_str("",0); + } + else + { + advancewindow(); + if (!strexpr(&lib)) + return FALSE; + } + } + ref = newtriple(OC_ZHELP); + ref->operand[0] = text; + ref->operand[1] = lib; + return TRUE; +} diff --git a/sr_port/m_zlink.c b/sr_port/m_zlink.c new file mode 100644 index 0000000..fea2894 --- /dev/null +++ b/sr_port/m_zlink.c @@ -0,0 +1,73 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "svnames.h" +#include "advancewindow.h" +#include "cmd.h" + +GBLREF char window_token; + +int m_zlink(void) +{ + int rval; + triple *ref; + oprtype file, quals; + + if (window_token == TK_EOL || window_token == TK_SPACE || window_token == TK_COLON) + { + ref = newtriple(OC_SVGET); + ref->operand[0] = put_ilit(SV_ZSOURCE); + file = put_tref(ref); + if (window_token == TK_COLON) + { + advancewindow(); + if (!strexpr(&quals)) + return FALSE; + } + else + { + ref = newtriple(OC_SVGET); + ref->operand[0] = put_ilit(SV_ZCOMPILE); + quals = put_tref(ref); + } + } + else + { + if (!(rval = strexpr(&file))) + return FALSE; + if (window_token != TK_COLON) + { + if (rval == EXPR_INDR) + { + make_commarg(&file,indir_zlink); + return TRUE; + } + ref = newtriple(OC_SVGET); + ref->operand[0] = put_ilit(SV_ZCOMPILE); + quals = put_tref(ref); + } + else + { + advancewindow(); + if (!strexpr(&quals)) + return FALSE; + } + } + ref = newtriple(OC_ZLINK); + ref->operand[0] = file; + ref->operand[1] = quals; + return TRUE; +} diff --git a/sr_port/m_zmessage.c b/sr_port/m_zmessage.c new file mode 100644 index 0000000..00ef627 --- /dev/null +++ b/sr_port/m_zmessage.c @@ -0,0 +1,52 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "advancewindow.h" +#include "cmd.h" + +GBLREF char window_token; + +int m_zmessage (void) +{ + triple *ref0, *ref1; + oprtype code, arg; + int count; + + switch (intexpr (&code)) + { + case EXPR_FAIL: + return FALSE; + case EXPR_INDR: + if (window_token != TK_COLON) + make_commarg (&code, indir_zmess); + } + ref0 = newtriple (OC_PARAMETER); + ref0->operand[0] = code; + ref1 = ref0; + for (count = 1; window_token == TK_COLON; count++) + { + advancewindow(); + if (expr (&arg) == EXPR_FAIL) + return FALSE; + ref1->operand[1] = put_tref (newtriple (OC_PARAMETER)); + ref1 = ref1->operand[1].oprval.tref; + ref1->operand[0] = arg; + } + ref1 = newtriple (OC_ZMESS); + ref1->operand[0] = put_ilit (count); + ref1->operand[1] = put_tref (ref0); + return TRUE; +} diff --git a/sr_port/m_zprint.c b/sr_port/m_zprint.c new file mode 100644 index 0000000..7a7122f --- /dev/null +++ b/sr_port/m_zprint.c @@ -0,0 +1,110 @@ +/**************************************************************** + * * + * Copyright 2001, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "advancewindow.h" +#include "cmd.h" + +GBLREF char window_token, director_token; +GBLREF mident window_ident; +GBLREF boolean_t run_time; +GBLREF mident routine_name; +LITREF mident zero_ident; + +int m_zprint(void) +{ + oprtype lab1, lab2, off1, off2, rtn; + triple *ref, *next; + bool got_some; + + error_def(ERR_LABELEXPECTED); + error_def(ERR_RTNNAME); + + got_some = FALSE; + lab1 = put_str(zero_ident.addr, zero_ident.len); + off1 = put_ilit(0); + if (window_token != TK_EOL && window_token != TK_SPACE && !lref(&lab1,&off1,TRUE,indir_zprint,TRUE,&got_some)) + return FALSE; + if (lab1.oprclass == TRIP_REF && lab1.oprval.tref->opcode == OC_COMMARG) + return TRUE; + if (window_token != TK_CIRCUMFLEX) + { /* Routine not specified, use current routine */ + if (!run_time) + rtn = put_str(routine_name.addr, routine_name.len); + else + rtn = put_tref(newtriple(OC_CURRTN)); + } else + { + got_some = TRUE; + advancewindow(); + switch(window_token) + { + case TK_IDENT: +# ifdef GTM_TRIGGER + if (TK_HASH == director_token) + /* Coagulate tokens as necessary (and available) to allow '#' in the rtn name */ + advwindw_hash_in_mname_allowed(); +# endif + rtn = put_str(window_ident.addr, window_ident.len); + advancewindow(); + break; + case TK_ATSIGN: + if (!indirection(&rtn)) + return FALSE; + break; + default: + stx_error(ERR_RTNNAME); + return FALSE; + } + } + if (window_token == TK_COLON) + { + if (!got_some) + { + stx_error(ERR_LABELEXPECTED); + return FALSE; + } + lab2 = put_str(zero_ident.addr, zero_ident.len); + off2 = put_ilit(0); + advancewindow(); + if (!lref(&lab2,&off2,TRUE,indir_zprint,FALSE,&got_some)) + return FALSE; + if (!got_some) + { + stx_error(ERR_LABELEXPECTED); + return FALSE; + } + } else + { + lab2 = lab1; + off2 = off1; + } + ref = newtriple(OC_ZPRINT); + ref->operand[0] = rtn; + next = newtriple(OC_PARAMETER); + ref->operand[1] = put_tref(next); + next->operand[0] = lab1; + ref = newtriple(OC_PARAMETER); + next->operand[1] = put_tref(ref); + ref->operand[0] = off1; + next = newtriple(OC_PARAMETER); + ref->operand[1] = put_tref(next); + next->operand[0] = lab2; + ref = newtriple(OC_PARAMETER); + next->operand[1] = put_tref(ref); + ref->operand[0] = off2; + return TRUE; + +} diff --git a/sr_port/m_zshow.c b/sr_port/m_zshow.c new file mode 100644 index 0000000..af705cf --- /dev/null +++ b/sr_port/m_zshow.c @@ -0,0 +1,130 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "mlkdef.h" +#include "zshow.h" +#include "advancewindow.h" +#include "cmd.h" + +GBLREF char window_token; + +int m_zshow(void) +{ + static readonly char def_str[]="S"; + int rval, code; + triple *r,*outtype,*lvar; + oprtype func,output; + error_def(ERR_VAREXPECTED); + + if (window_token == TK_SPACE || window_token == TK_EOL || window_token == TK_COLON) + { + code = ZSHOW_NOPARM; + }else + { + code = ZSHOW_DEVICE; + switch (strexpr(&func)) + { + case EXPR_FAIL: + return FALSE; + case EXPR_GOOD: + break; + case EXPR_INDR: + make_commarg(&func,indir_zshow); + return TRUE; + break; + default: + assert(FALSE); + } + } + if (window_token == TK_COLON) + { + advancewindow(); + switch(window_token) + { + case TK_CIRCUMFLEX: + if (!gvn()) + return FALSE; + r = maketriple(OC_ZSHOW); + outtype = newtriple(OC_PARAMETER); + r->operand[1] = put_tref(outtype); + if (code == ZSHOW_NOPARM) + { + r->operand[0] = put_str(&def_str[0], (SIZEOF(def_str) - 1)); + }else + { + r->operand[0] = func; + } + outtype->operand[0] = put_ilit(ZSHOW_GLOBAL); + ins_triple(r); + return TRUE; + case TK_IDENT: + if (!lvn(&output, OC_PUTINDX, 0)) + { + stx_error(ERR_VAREXPECTED); + return FALSE; + } + r = maketriple(OC_ZSHOWLOC); + outtype = newtriple(OC_PARAMETER); + r->operand[1] = put_tref(outtype); + if (code == ZSHOW_NOPARM) + { + r->operand[0] = put_str(&def_str[0], (SIZEOF(def_str) - 1)); + }else + { + r->operand[0] = func; + } + lvar = newtriple(OC_PARAMETER); + outtype->operand[1] = put_tref(lvar); + lvar->operand[0] = output; + outtype->operand[0] = put_ilit(ZSHOW_LOCAL); + ins_triple(r); + return TRUE; + case TK_ATSIGN: + if (!indirection(&output)) + { + stx_error(ERR_VAREXPECTED); + return FALSE; + } + r = newtriple(OC_INDRZSHOW); + if (code == ZSHOW_NOPARM) + { + r->operand[0] = put_str(&def_str[0], (SIZEOF(def_str) - 1)); + }else + { + r->operand[0] = func; + } + r->operand[1] = output; + return TRUE; + default: + stx_error(ERR_VAREXPECTED); + return FALSE; + } + } + r = maketriple(OC_ZSHOW); + outtype = newtriple(OC_PARAMETER); + r->operand[1] = put_tref(outtype); + if (code == ZSHOW_NOPARM) + { + r->operand[0] = put_str(&def_str[0], (SIZEOF(def_str) - 1)); + }else + { + r->operand[0] = func; + } + outtype->operand[0] = put_ilit(ZSHOW_DEVICE); + ins_triple(r); + return TRUE; + +} diff --git a/sr_port/m_zstep.c b/sr_port/m_zstep.c new file mode 100644 index 0000000..2291ce7 --- /dev/null +++ b/sr_port/m_zstep.c @@ -0,0 +1,94 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "zstep.h" +#include "toktyp.h" +#include "nametabtyp.h" +#include "mdq.h" +#include "advancewindow.h" +#include "cmd.h" +#include "namelook.h" + +GBLREF char window_token; +GBLREF mval window_mval; +GBLREF mident window_ident; +GBLREF char director_token; +GBLREF short int source_column; + +error_def(ERR_INVZSTEP); +error_def(ERR_ZSTEPARG); + +static readonly nametabent zstep_names[] = +{ + { 1, "I"}, { 4, "INTO"} + ,{ 2,"OU" }, { 5,"OUTOF" } + ,{ 2,"OV" }, { 4,"OVER" } +}; +static readonly unsigned char zstep_index[27] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2 + ,2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6 + ,6, 6, 6 +}; + +static readonly char zstep_type[]={ ZSTEP_INTO, ZSTEP_INTO, + ZSTEP_OUTOF, ZSTEP_OUTOF, + ZSTEP_OVER, ZSTEP_OVER +}; + +int m_zstep(void) +{ + triple *head; + char type; + int x; + oprtype action; + opctype op; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + assert((SIZEOF(zstep_names) / SIZEOF(nametabent)) == zstep_index[26]); + op = OC_ZSTEP; + switch(window_token) + { + case TK_SPACE: + case TK_EOL: + type = ZSTEP_OVER; + break; + case TK_IDENT: + if (0 > (x = namelook(zstep_index, zstep_names, window_ident.addr, window_ident.len))) /* assignment */ + { + stx_error(ERR_INVZSTEP); + return FALSE; + } + type = zstep_type[x]; + advancewindow(); + break; + default: + stx_error(ERR_ZSTEPARG); + return FALSE; + break; + } + if (TK_COLON == window_token) + { advancewindow(); + if (!expr(&action)) + return FALSE; + op = OC_ZSTEPACT; + } + head = maketriple(op); + head->operand[0] = put_ilit(type); + if (op == OC_ZSTEPACT) + head->operand[1] = action; + ins_triple(head); + return TRUE; +} diff --git a/sr_port/m_zsystem.c b/sr_port/m_zsystem.c new file mode 100644 index 0000000..1ddbf11 --- /dev/null +++ b/sr_port/m_zsystem.c @@ -0,0 +1,47 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "cmd.h" + +GBLREF char window_token; + +int m_zsystem(void) +{ + oprtype x; + triple *triptr; + + if (window_token == TK_EOL || window_token == TK_SPACE) + { + triptr = newtriple(OC_ZSYSTEM); + triptr->operand[0] = put_str("",0); + return TRUE; + } + else + switch (strexpr(&x)) + { + case EXPR_FAIL: + return FALSE; + case EXPR_GOOD: + triptr = newtriple(OC_ZSYSTEM); + triptr->operand[0] = x; + return TRUE; + case EXPR_INDR: + make_commarg(&x,indir_zsystem); + return TRUE; + } + + return FALSE; /* This will never get executed, added to make compiler happy */ +} diff --git a/sr_port/m_ztcommit.c b/sr_port/m_ztcommit.c new file mode 100644 index 0000000..6765f4f --- /dev/null +++ b/sr_port/m_ztcommit.c @@ -0,0 +1,37 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "cmd.h" + +GBLREF char window_token; + +int m_ztcommit(void) +{ + triple *head; + + head = maketriple(OC_ZTCOMMIT); + if (window_token == TK_EOL || window_token == TK_SPACE) + { + head->operand[0] = put_ilit(1); + ins_triple(head); + return TRUE; + } + + if (!intexpr(&head->operand[0])) + return FALSE; + + ins_triple(head); + return TRUE; +} diff --git a/sr_port/m_ztstart.c b/sr_port/m_ztstart.c new file mode 100644 index 0000000..3371bf0 --- /dev/null +++ b/sr_port/m_ztstart.c @@ -0,0 +1,30 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "toktyp.h" +#include "cmd.h" + +GBLREF char window_token; + +int m_ztstart(void) +{ + error_def(ERR_SPOREOL); + + if (window_token != TK_EOL && window_token != TK_SPACE) + { stx_error(ERR_SPOREOL); + return FALSE; + } + newtriple(OC_ZTSTART); + return TRUE; +} diff --git a/sr_port/m_zwatch.c b/sr_port/m_zwatch.c new file mode 100644 index 0000000..bd98dce --- /dev/null +++ b/sr_port/m_zwatch.c @@ -0,0 +1,131 @@ +/**************************************************************** + * * + * Copyright 2001, 2004 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "advancewindow.h" +#include "cmd.h" + +#define CANCEL_ONE -1 +#define CANCEL_ALL -2 + +GBLREF char window_token; +GBLREF mident window_ident; +LITREF mident zero_ident; + +int m_zwatch(void) +{ + + triple *ref,*next; + opctype op; + oprtype name,action,count; + bool is_count; + error_def(ERR_VAREXPECTED); + + if (window_token == TK_MINUS) + { + advancewindow(); + switch(window_token) + { + case TK_ASTERISK: + name = put_str(zero_ident.addr, zero_ident.len); + count = put_ilit(CANCEL_ALL); + advancewindow(); + break; + case TK_IDENT: + name = put_str(window_ident.addr, window_ident.len); + count = put_ilit(CANCEL_ONE); + advancewindow(); + break; + case TK_ATSIGN: + if (!indirection(&name)) + return FALSE; + count = put_ilit(CANCEL_ONE); + break; + default: + stx_error(ERR_VAREXPECTED); + return FALSE; + } + action = put_str("",0); + op = OC_WATCHREF; + } + else + { + if (window_token == TK_EQUAL) + { + advancewindow(); + op = OC_WATCHMOD; + } + else + op = OC_WATCHREF; + switch(window_token) + { + case TK_IDENT: + name = put_str(window_ident.addr, window_ident.len); + advancewindow(); + break; + case TK_ATSIGN: + if (!indirection(&name)) + return FALSE; + if (op == OC_WATCHREF && window_token != TK_COLON) + { + ref = maketriple(OC_COMMARG); + ref->operand[0] = name; + ref->operand[1] = put_ilit((mint) indir_zwatch); + ins_triple(ref); + return TRUE; + } + break; + default: + stx_error(ERR_VAREXPECTED); + return FALSE; + } + if (window_token != TK_COLON) + { + action = put_str("",0); + count = put_ilit(0); + } + else + { + advancewindow(); + if (window_token == TK_COLON) + { + is_count = TRUE; + action = put_str("",0); + } + else + { + if (!strexpr(&action)) + return FALSE; + is_count = window_token == TK_COLON; + } + if (is_count) + { + advancewindow(); + if (!intexpr(&count)) + return FALSE; + } + else + count = put_ilit(0); + } + } + ref = newtriple(op); + ref->operand[0] = name; + next = newtriple(OC_PARAMETER); + ref->operand[1] = put_tref(next); + next->operand[0] = action; + next->operand[1] = count; + return TRUE; + +} diff --git a/sr_port/m_zwithdraw.c b/sr_port/m_zwithdraw.c new file mode 100644 index 0000000..1134bd7 --- /dev/null +++ b/sr_port/m_zwithdraw.c @@ -0,0 +1,53 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "indir_enum.h" +#include "toktyp.h" +#include "cmd.h" + +GBLREF char window_token; + +int m_zwithdraw(void) +{ + oprtype tmparg; + triple *ref; + error_def(ERR_VAREXPECTED); + + switch (window_token) + { + case TK_IDENT: + if (!lvn(&tmparg,OC_SRCHINDX,0)) + return FALSE; + ref = newtriple(OC_LVZWITHDRAW); + ref->operand[0] = tmparg; + break; + case TK_CIRCUMFLEX: + if (!gvn()) + return FALSE; + ref = newtriple(OC_GVZWITHDRAW); + break; + case TK_ATSIGN: + if (!indirection(&tmparg)) + return FALSE; + ref = maketriple(OC_COMMARG); + ref->operand[0] = tmparg; + ref->operand[1] = put_ilit((mint) indir_zwithdraw); + ins_triple(ref); + return TRUE; + default: + stx_error(ERR_VAREXPECTED); + return FALSE; + } + return TRUE; +} diff --git a/sr_port/m_zwrite.c b/sr_port/m_zwrite.c new file mode 100644 index 0000000..0229381 --- /dev/null +++ b/sr_port/m_zwrite.c @@ -0,0 +1,357 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "mdq.h" +#include "opcode.h" +#include "nametabtyp.h" +#include "indir_enum.h" +#include "gdsroot.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "zwrite.h" +#include "toktyp.h" +#include "svnames.h" +#include "funsvn.h" +#include "advancewindow.h" +#include "cmd.h" +#include "compile_pattern.h" +#include "mvalconv.h" +#include "namelook.h" + +GBLREF char window_token; +GBLREF mval window_mval; +GBLREF mident window_ident; +GBLREF triple *curtchain; +GBLREF char director_token; +GBLREF short int source_column; +GBLREF uint4 pat_everything[]; +GBLREF mstr_len_t sizeof_pat_everything; + +error_def(ERR_COMMA); +error_def(ERR_INVSVN); +error_def(ERR_RPARENMISSING); +error_def(ERR_SVNEXPECTED); +error_def(ERR_VAREXPECTED); +error_def(ERR_ZWRSPONE); + +LITREF unsigned char svn_index[]; +LITREF nametabent svn_names[]; +LITREF svn_data_type svn_data[]; + +/****** CAUTION !!! ****** + * All occurrences of put_lit should be replaced by put_ilit. In order to maintain object + * code compatibility, however, this replacement has been preempted by preceding put_lit + * with n2s. + * -no runtime module looks at anything but nm, so call to n2s is dispensed with. -mwm + */ + +int m_zwrite(void) +{ + int index; + int4 pcount; /* parameter count */ + triple *ref, *ref1, *head, *last, *count; + opctype op; + oprtype name, limit; + mval mv; + mint code; + mint subscount; + char c; + boolean_t parse_warn, pat; + DCL_THREADGBL_ACCESS; + + SETUP_THREADGBL_ACCESS; + subscount = 0; + count = 0; + pat = FALSE; + if (TK_CIRCUMFLEX == window_token) + { + advancewindow(); + op = OC_GVZWRITE; + } else + op = OC_LVZWRITE; + switch(window_token) + { + case TK_SPACE: + case TK_EOL: + if (OC_GVZWRITE == op) + { + stx_error(ERR_VAREXPECTED); + return FALSE; + } + op = OC_LVPATWRITE; + head = maketriple(op); + head->operand[0] = put_ilit((mint)3); /* count */ + ref1 = newtriple(OC_PARAMETER); + head->operand[1] = put_tref(ref1); + ref1->operand[0] = put_ilit(0); /* shows not from zshow */ + ref = newtriple(OC_PARAMETER); + ref1->operand[1] = put_tref(ref); + ref->operand[0] = put_str((char *)pat_everything, sizeof_pat_everything); + MV_FORCE_MVAL(&mv, ZWRITE_ASTERISK) ; + ref->operand[1] = put_lit(&mv); + ins_triple(head); + return TRUE; + case TK_IDENT: + name = put_str(window_ident.addr, window_ident.len); + advancewindow(); + break; + case TK_DOLLAR: + advancewindow(); + if ((TK_IDENT != window_token) || (OC_GVZWRITE == op)) + { + stx_error(ERR_SVNEXPECTED); + return FALSE; + } + parse_warn = FALSE; + index = namelook(svn_index, svn_names, window_ident.addr, window_ident.len); + if (0 > index) + { + STX_ERROR_WARN(ERR_INVSVN); /* sets "parse_warn" to TRUE */ + } else + { + if (!VALID_SVN(index)) + { + STX_ERROR_WARN(ERR_FNOTONSYS); /* sets "parse_warn" to TRUE */ + } + } + advancewindow(); + switch(window_token) + { + case TK_SPACE: + case TK_EOL: + case TK_COMMA: + if (!parse_warn) + { + assert(SV_NUM_SV > svn_data[index].opcode); + ref = maketriple(OC_ZWRITESVN); + ref->operand[0] = put_ilit(svn_data[index].opcode); + ins_triple(ref); + } else + { /* OC_RTERROR triple would have been inserted in curtchain by ins_errtriple + * (invoked by stx_error). No need to do anything else. + */ + assert(OC_RTERROR == curtchain->exorder.bl->exorder.bl->exorder.bl->opcode); + } + return TRUE; + default: + stx_error(ERR_SVNEXPECTED); + return FALSE; + } + break; + case TK_LPAREN: + if (OC_GVZWRITE != op) /* naked reference */ + { + stx_error(ERR_VAREXPECTED); + return FALSE; + } + name = put_str(window_ident.addr, 0); + break; + case TK_ATSIGN: + if (!indirection(&name)) + return FALSE; + if ((OC_LVZWRITE == op) && (TK_LPAREN != window_token)) + { + ref = maketriple(OC_COMMARG); + ref->operand[0] = name; + ref->operand[1] = put_ilit(indir_zwrite); + ins_triple(ref); + return TRUE; + } + ref = newtriple(OC_INDPAT); + ref->operand[0] = name; + name = put_tref(ref); + break; + case TK_QUESTION: + advancewindow(); + source_column = TREF(last_source_column); + if (!compile_pattern(&name, FALSE)) + return FALSE; + if (op == OC_LVZWRITE) + op = OC_LVPATWRITE; + pat = TRUE; + break; + default: + stx_error(ERR_VAREXPECTED); + return FALSE; + } + head = maketriple(op); + last = newtriple(OC_PARAMETER); + head->operand[1] = put_tref(last); + pcount = 1; + if ((OC_LVPATWRITE == op) || (OC_GVZWRITE == op)) + { + pcount++; + last->operand[0] = put_ilit((op == OC_GVZWRITE ? pat : 0)); + ref = newtriple(OC_PARAMETER); + last->operand[1] = put_tref(ref); + last = ref; + if (OC_GVZWRITE == op) + { + pcount++; + count = last; + ref = newtriple(OC_PARAMETER); + last->operand[1] = put_tref(ref); + last = ref; + } + } + last->operand[0] = name; + if (TK_LPAREN != window_token) + { + pcount++; + if (pat) + { + MV_FORCE_MVAL(&mv, ZWRITE_END); + } else + { + subscount++ ; + MV_FORCE_MVAL(&mv, ZWRITE_ASTERISK); + } + last->operand[1] = put_lit(&mv); + head->operand[0] = put_ilit(pcount); + if (count) + count->operand[0] = put_ilit(subscount); + ins_triple(head); + return TRUE; + } + advancewindow(); + for(;;) + { + ref = newtriple(OC_PARAMETER); + last->operand[1] = put_tref(ref); + switch (window_token) + { + case TK_RPAREN: + dqdel(ref,exorder); + advancewindow(); + MV_FORCE_MVAL(&mv, ZWRITE_END); + last->operand[1] = put_lit(&mv); + pcount++; + head->operand[0] = put_ilit((mint)pcount); + if (count) + count->operand[0] = put_ilit(subscount); + ins_triple(head); + return TRUE; + case TK_ASTERISK: + dqdel(ref,exorder); + advancewindow(); + if (window_token != TK_RPAREN) + { + stx_error(ERR_RPARENMISSING); + return FALSE; + } + advancewindow(); + MV_FORCE_MVAL(&mv, ZWRITE_ASTERISK); + last->operand[1] = put_lit(&mv); + pcount++; + subscount++; + head->operand[0] = put_ilit((mint)pcount); + if (count) + count->operand[0] = put_ilit(subscount); + ins_triple(head); + return TRUE; + case TK_QUESTION: + advancewindow(); + source_column = TREF(last_source_column); + if (!compile_pattern(&limit, FALSE)) + return FALSE; + if (window_token != TK_COMMA && window_token != TK_RPAREN) + { + stx_error(ERR_ZWRSPONE); + return FALSE; + } + if (TK_COMMA == window_token) + advancewindow(); + subscount++; + MV_FORCE_MVAL(&mv, ZWRITE_PATTERN); + ref->operand[0] = put_lit(&mv); + pcount++; + ref1 = newtriple(OC_PARAMETER); + ref->operand[1] = put_tref(ref1); + ref1->operand[0] = limit; + last = ref1; + pcount++; + continue; + case TK_COLON: + if ((c = director_token) != TK_RPAREN) + { + if (TK_COMMA != c) + { + advancewindow(); + MV_FORCE_MVAL(&mv, ZWRITE_UPPER); + ref->operand[0] = put_lit(&mv); + pcount++; + subscount++; + break; + } + advancewindow(); + } + /* caution: fall through */ + case TK_COMMA: + advancewindow(); + MV_FORCE_MVAL(&mv, ZWRITE_ALL); + ref->operand[0] = put_lit(&mv); + pcount++; + subscount++; + last = ref; + continue; + default: + if (!expr(&limit)) + return FALSE; + subscount++; + last = newtriple(OC_PARAMETER); + ref->operand[1] = put_tref(last); + last->operand[0] = limit; + pcount++; + if (TK_COLON == (c = window_token)) + { + code = ZWRITE_LOWER; + advancewindow(); + c = window_token; + } else + code = ZWRITE_VAL; + switch (c) + { + case TK_COMMA: + advancewindow(); + /* caution: fall through */ + case TK_RPAREN: + MV_FORCE_MVAL(&mv, code) ; + ref->operand[0] = put_lit(&mv); + pcount++; + continue; + default: + if (code == ZWRITE_VAL) + { + stx_error(ERR_COMMA); + return FALSE; + } + MV_FORCE_MVAL(&mv, ZWRITE_BOTH) ; + ref->operand[0] = put_lit(&mv); + pcount++; + ref = last; + break; + } + break; + } + if (!expr(&limit)) + return FALSE; + last = newtriple(OC_PARAMETER); + ref->operand[1] = put_tref(last); + last->operand[0] = limit; + pcount++; + if (window_token == TK_COMMA) + advancewindow(); + } +} diff --git a/sr_port/main_pragma.h b/sr_port/main_pragma.h new file mode 100644 index 0000000..88e3f4e --- /dev/null +++ b/sr_port/main_pragma.h @@ -0,0 +1,21 @@ +/**************************************************************** + * * + * Copyright 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + + +#ifndef MAIN_PRAGMA_included +#define MAIN_PRAGMA_included + +#ifdef __MVS__ +#pragma runopts(ENVAR(_BPXK_AUTOCVT=ON)) +#pragma runopts(FILETAG(AUTOCVT,AUTOTAG)) +#endif + +#endif /* MAIN_PRAGMA_included */ diff --git a/sr_port/make_commarg.c b/sr_port/make_commarg.c new file mode 100644 index 0000000..f3e5d26 --- /dev/null +++ b/sr_port/make_commarg.c @@ -0,0 +1,38 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "opcode.h" +#include "mdq.h" + +#ifdef DEBUG +LITREF octabstruct oc_tab[]; +#endif + +void make_commarg(oprtype *x,mint ind) +{ + triple *ref; + + assert(x->oprclass == TRIP_REF); + ref = x->oprval.tref; + if (ref->opcode != OC_INDGLVN) + { + assert(ref->opcode == OC_COMVAL || ref->opcode == OC_COMINT || ref->opcode == OC_COBOOL); + dqdel(ref,exorder); + assert(ref->operand[0].oprclass == TRIP_REF); + ref = ref->operand[0].oprval.tref; + assert(ref->opcode == OC_INDGLVN); + } + ref->opcode = OC_COMMARG; + ref->operand[1] = put_ilit(ind); + return; +} diff --git a/sr_port/make_gvsubsc.c b/sr_port/make_gvsubsc.c new file mode 100644 index 0000000..6f18b86 --- /dev/null +++ b/sr_port/make_gvsubsc.c @@ -0,0 +1,43 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "stringpool.h" +#include "gdsroot.h" +#include "gdsblk.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "mvalconv.h" + +GBLREF spdesc stringpool; + +oprtype make_gvsubsc(mval *v) +{ + mval w; + gv_key *gp; + + ENSURE_STP_FREE_SPACE(MAX_SRCLINE + SIZEOF(gv_key)); + if ((INTPTR_T)stringpool.free & 1) + stringpool.free++; /* word align key for structure refs */ + gp = (gv_key *) stringpool.free; + gp->top = MAX_SRCLINE; + gp->end = gp->prev = 0; + mval2subsc(v,gp); + w.mvtype = MV_STR | MV_SUBLIT; + w.str.addr = (char *) gp->base; + w.str.len = gp->end + 1; + stringpool.free = &gp->base[gp->end + 1]; + assert(stringpool.free <= stringpool.top); + return put_lit(&w); +} diff --git a/sr_port/maketriple.c b/sr_port/maketriple.c new file mode 100644 index 0000000..9d4378a --- /dev/null +++ b/sr_port/maketriple.c @@ -0,0 +1,30 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "compiler.h" +#include "mdq.h" +#include "mmemory.h" + +GBLREF short int source_line,source_column; + +triple *maketriple(opctype op) +{ + triple *x; + + x = (triple *)mcalloc(SIZEOF(triple)); + x->opcode = op; + x->src.line = source_line; + x->src.column = source_column; + dqinit(&(x->backptr), que); + dqinit(&(x->jmplist), que); + return x; +} diff --git a/sr_port/matchc.c b/sr_port/matchc.c new file mode 100644 index 0000000..a63623a --- /dev/null +++ b/sr_port/matchc.c @@ -0,0 +1,197 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" +#include "matchc.h" + +#define RETURN_NOMATCH \ +{ \ + *res = 0; \ + assert(0 < numpcs_unmatched); \ + *numpcs = numpcs_unmatched; \ + return src_top; \ +} + +#define RETURN_YESMATCH(RET) \ +{ \ + *res = RET; \ + assert(0 == numpcs_unmatched); \ + *numpcs = 0; \ + return src_ptr; \ +} + +/* + * ----------------------------------------------- + * Pseudo equivalent of VAX matchc instruction + * + * Arguments: + * del_len - delimiter length + * del_str - pointer to delimiter string + * src_len - length of source string + * src_str - pointer to source string + * res - pointer to the result + * numpcs - pointer to the number of pieces that are desired to be matched. + * + * Return: + * pointer to next character after match substring + * in the source string, if found. Otherwise src_str + src_len. + * + * Side effects: + * set res arg to: + * 0 - if match not found + * 1 + char_len - if match found, where char_len is the position + * of the next character after the match substring. + * set numpcs arg to # of pieces that could not be matched (because end of source string was reached before then) + * ----------------------------------------------- + */ +#ifdef UNICODE_SUPPORTED +#include "gtm_utf8.h" + +GBLREF boolean_t gtm_utf8_mode; +GBLREF boolean_t badchar_inhibit; + +/* multi-byte character-oriented substring matching */ +unsigned char *matchc(int del_len, unsigned char *del_str, int src_len, unsigned char *src_str, int *res, int *numpcs) +{ + unsigned char *src_ptr, *src_top, *src_next, *del_ptr, *del_top, *del_next, *del_next1, *restart_ptr; + wint_t src_cp, del_cp, del_cp1; /* code points for the source and delimiter characters */ + int char_len, restart_char_len, del_charlen, bytelen, numpcs_unmatched; + + if (!gtm_utf8_mode) + return matchb(del_len, del_str, src_len, src_str, res, numpcs); + assert(0 <= del_len); + assert(0 < *numpcs); + if (0 == del_len) + { /* always matches a null string */ + *numpcs = 0; + *res = 1; + return src_str; + } + src_ptr = src_str; + src_top = src_str + src_len; + del_top = del_str + del_len; + /* Check UTF8 byte sequence validity of delimiter string. The following code is very similar to utf8_len() but + * we dont invoke the function here for performance reasons as this piece of code is used by heavy hitters like $piece. + * Also, the code below can be forked off into two cases depending on the value of "badchar_inhibit". This is a + * performance enhancement that can be done later if this is found to be a bottleneck. + */ + if (!badchar_inhibit) + { + for (del_charlen = 0, del_ptr = del_str; del_ptr < del_top; del_charlen++, del_ptr += bytelen) + { + if (!UTF8_VALID(del_ptr, del_top, bytelen)) + utf8_badchar(0, del_ptr, del_top, 0, NULL); + } + } + numpcs_unmatched = *numpcs; /* note down # of pieces left to match */ + /* compute the code point of the 1st delimiter char */ + del_next1 = UTF8_MBTOWC(del_str, del_top, del_cp1); + assert((WEOF != del_cp1) || badchar_inhibit); + for (char_len = 0; (src_ptr < src_top) && (src_top - src_ptr) >= del_len; ) + { + src_next = src_ptr; + do + { /* find the occurrence of 1st delimiter char in the source */ + src_ptr = src_next; + src_next = UTF8_MBTOWC(src_ptr, src_top, src_cp); + if ((WEOF == src_cp) && !badchar_inhibit) + utf8_badchar(0, src_ptr, src_top, 0, NULL); + ++char_len; /* maintain the source character position */ + } while ((src_next < src_top) && ((src_cp != del_cp1) || ((WEOF == src_cp) && (*src_ptr != *del_str)))); + + if ((src_cp != del_cp1) || (WEOF == src_cp) && (*src_ptr != *del_str)) + { /* could not find the 1st delimiter char in the source */ + RETURN_NOMATCH; + } + /* 1st delimiter character match found. match the other delimiter characters */ + del_ptr = del_next1; /* advance past the 1st delimiter character */ + restart_ptr = src_ptr = src_next; /* advance past the 1st source character */ + restart_char_len = char_len; + for ( ; (src_ptr < src_top) && (del_ptr < del_top); src_ptr = src_next, del_ptr = del_next, ++char_len) + { + src_next = UTF8_MBTOWC(src_ptr, src_top, src_cp); + if ((WEOF == src_cp) && !badchar_inhibit) + utf8_badchar(0, src_ptr, src_top, 0, NULL); + del_next = UTF8_MBTOWC(del_ptr, del_top, del_cp); + if ((src_cp != del_cp) || ((WEOF == src_cp) && *src_ptr != *del_ptr)) + { /* match lost. restart the search skipping the first delimiter character */ + src_ptr = restart_ptr; + char_len = restart_char_len; + break; + } + } + if (del_ptr >= del_top) + { /* Match found : Return success if no more pieces to match else continue with scan */ + assert(del_top == del_ptr); + assert(0 < numpcs_unmatched); + if (0 == --numpcs_unmatched) + RETURN_YESMATCH(1 + char_len); + } + } + RETURN_NOMATCH; +} +#endif /* UNICODE_SUPPORTED */ + +/* byte-oriented substring matching */ +unsigned char *matchb(int del_len, unsigned char *del_str, int src_len, unsigned char *src_str, int *res, int *numpcs) +{ + unsigned char *src_ptr, *pdel, *src_base, *src_top, *del_top; + int src_cnt, numpcs_unmatched; + boolean_t match_found; + + assert(0 <= del_len); + assert(0 < *numpcs); + if (0 == del_len) + { /* always matches a null string */ + *numpcs = 0; + *res = 1; + return src_str; + } + numpcs_unmatched = *numpcs; /* note down # of pieces to be matched */ + src_ptr = src_base = src_str; + src_top = src_ptr + src_len; + if (src_len < del_len) /* Input string is shorter than delimiter string so no match possible */ + RETURN_NOMATCH; + del_top = del_str + del_len; + pdel = del_str; + while (src_ptr < src_top) + { + /* Quick Find 1st delimiter char */ + while (*src_ptr != *pdel) + { + src_ptr = ++src_str; + if (src_ptr == src_top) + RETURN_NOMATCH; + } + match_found = FALSE; + /* Found delimiter */ + while (*src_ptr++ == *pdel++) + { + if (pdel == del_top) + { /* Found matching piece. */ + match_found = TRUE; + break; + } + if (src_ptr == src_top) + RETURN_NOMATCH; + } + if (match_found) + { /* Return success if no more pieces to match else continue with scan */ + assert(0 < numpcs_unmatched); + if (0 == --numpcs_unmatched) + RETURN_YESMATCH(INTCAST(1 + (src_ptr - src_base))); + src_str = src_ptr; + } else + src_ptr = ++src_str; /* Match lost, goto next source character */ + pdel = del_str; + } + RETURN_NOMATCH; +} diff --git a/sr_port/matchc.h b/sr_port/matchc.h new file mode 100644 index 0000000..6e6f7f1 --- /dev/null +++ b/sr_port/matchc.h @@ -0,0 +1,20 @@ +/**************************************************************** + * * + * Copyright 2001, 2009 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef MATCHC_INCLUDED +#define MATCHC_INCLUDED + +/* Character oriented match call */ +unsigned char *matchc(int del_len, unsigned char *del_str, int src_len, unsigned char *src_str, int *res, int *numpcs); +/* Byte oriented match call */ +unsigned char *matchb(int del_len, unsigned char *del_str, int src_len, unsigned char *src_str, int *res, int *numpcs); + +#endif /* MATCHC_INCLUDED */ diff --git a/sr_port/mcalloc.c b/sr_port/mcalloc.c new file mode 100644 index 0000000..0418eb0 --- /dev/null +++ b/sr_port/mcalloc.c @@ -0,0 +1,69 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" + +#include "mmemory.h" +#include "min_max.h" + +GBLREF int mcavail; +GBLREF mcalloc_hdr *mcavailptr, *mcavailbase; + +char *mcalloc(unsigned int n) +{ + mcalloc_hdr *hdr, *nxt, *ptr; + int new_size, rel_size; + + /* Choice of char_ptr_t made because it is a 64 bit pointer on Tru64 which + * is the alignment we need there, or any other 64 bit platforms we support + * in the future. + */ + n = ROUND_UP2(n, SIZEOF(char_ptr_t)); + + if (n > mcavail) + { /* No sufficient space in the current block. Follow the link and check if the next block has sufficient + * space. There is no next block or the next one doesn't have enough space, allocate a new block with + * the requested size and insert it after the current block. + */ + hdr = mcavailptr->link; + if (NULL == hdr || n > hdr->size) + { + if (NULL != hdr) + { /* i.e. the next block doesn't have sufficient space for n. Release as many small blocks as + * necessary to make up for the space that we are allocating for the large block. + * By release several small blocks and replacing them with a large block ensures that total + * memory footprint is not increased due to a rare occurence of large routine compilation. + */ + rel_size = 0; + for (nxt = hdr; NULL != nxt && (rel_size += nxt->size) < n; nxt = ptr) + { + ptr = nxt->link; + free(nxt); + } + } else + nxt = NULL; + new_size = (int)MAX(MC_DSBLKSIZE, (n + MCALLOC_HDR_SZ)); + hdr = (mcalloc_hdr *)malloc(new_size); + hdr->link = nxt; + hdr->size = (int4)(new_size - MCALLOC_HDR_SZ); + mcavailptr->link = hdr; + } + assert(n <= hdr->size); + memset(&hdr->data[0], 0, hdr->size); + mcavailptr = hdr; + mcavail = hdr->size; + } + mcavail -= n; + assert(mcavail >= 0); + return &mcavailptr->data[mcavail]; +} diff --git a/sr_port/mcfree.c b/sr_port/mcfree.c new file mode 100644 index 0000000..c08401a --- /dev/null +++ b/sr_port/mcfree.c @@ -0,0 +1,29 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "mmemory.h" + +GBLREF int mcavail; +GBLREF mcalloc_hdr *mcavailptr, *mcavailbase; + +/* This routine doesn't actually release memory at the end of compilation. For efficiency sake + * (esp. during indirect code compilation), it just resets the mcavail* pointers to the beginning + * of the list so that the allocated area will be reused for the next compilation. + */ +void mcfree(void) +{ + mcavailptr = mcavailbase; + assert((NULL != mcavailptr) || !mcavail); + mcavail = (NULL != mcavailptr) ? mcavailptr->size : 0; + return; +} diff --git a/sr_port/mdb_condition_handler.c b/sr_port/mdb_condition_handler.c new file mode 100644 index 0000000..e4f892e --- /dev/null +++ b/sr_port/mdb_condition_handler.c @@ -0,0 +1,1093 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "gtm_stdlib.h" +#include "gtm_inet.h" /* Required for gtmsource.h */ +#include "gtm_stdio.h" + +#ifdef VMS +#include /* required for gtmsource.h */ +#include +#endif +#ifdef UNIX +#include +#endif + +#include "ast.h" +#include "gdsroot.h" +#include "gdsbt.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsblk.h" +#include "gdsfhead.h" +#include "gdscc.h" +#include "gdskill.h" +#include "gt_timer.h" +#include "filestruct.h" +#include "gtmdbglvl.h" +#include "error.h" +#include "hashtab_mname.h" +#include "io.h" +#include "io_params.h" +#include "jnl.h" +#include "lv_val.h" +#include "rtnhdr.h" +#include "mv_stent.h" +#include "outofband.h" +#include "stack_frame.h" +#include "stringpool.h" +#include "hashtab_int4.h" /* needed for tp.h */ +#include "buddy_list.h" /* needed for tp.h */ +#include "tp.h" +#include "tp_frame.h" +#include "tp_timeout.h" +#include "xfer_enum.h" +#include "mlkdef.h" +#include "repl_msg.h" +#include "gtmsource.h" +#include "zwrite.h" +#include "cache.h" +#include "cache_cleanup.h" +#include "objlabel.h" +#include "op.h" +#include "dpgbldir.h" +#include "preemptive_ch.h" +#include "compiler.h" /* needed for MAX_SRCLINE */ +#include "show_source_line.h" +#include "trans_code_cleanup.h" +#include "dm_setup.h" +#include "util.h" +#include "tp_restart.h" +#include "dollar_zlevel.h" +#include "error_trap.h" +#include "golevel.h" +#include "getzposition.h" +#include "send_msg.h" +#include "jobinterrupt_process_cleanup.h" +#include "fix_xfer_entry.h" +#include "change_reg.h" +#include "tp_change_reg.h" +#include "alias.h" +#include "create_fatal_error_zshow_dmp.h" +#ifdef UNIX +# include "iormdef.h" +# include "ftok_sems.h" +#endif +#ifdef GTM_TRIGGER +# include "gv_trigger.h" +# include "gtm_trigger.h" +# include "have_crit.h" +#endif + +GBLREF spdesc stringpool, rts_stringpool, indr_stringpool; +GBLREF volatile int4 outofband; +GBLREF volatile bool std_dev_outbnd; +GBLREF unsigned char *restart_pc; +GBLREF unsigned char *restart_ctxt; +GBLREF unsigned char *stackwarn, *tpstackwarn; +GBLREF unsigned char *stacktop, *tpstacktop; +GBLREF unsigned char *msp, *tp_sp; +GBLREF mv_stent *mv_chain; +GBLREF stack_frame *frame_pointer, *zyerr_frame, *error_frame; +GBLREF tp_frame *tp_pointer; +GBLREF io_desc *active_device; +GBLREF lv_val *active_lv; +GBLREF io_pair io_std_device, io_curr_device; +GBLREF mval dollar_ztrap; +GBLREF volatile bool neterr_pending; +GBLREF xfer_entry_t xfer_table[]; +GBLREF unsigned short proc_act_type; +GBLREF mval **ind_result_array, **ind_result_sp; +GBLREF mval **ind_source_array, **ind_source_sp; +GBLREF int mumps_status; +GBLREF mstr *err_act; +GBLREF tp_region *tp_reg_list; /* Chained list of regions used in this transaction not cleared on tp_restart */ +GBLREF void (*tp_timeout_clear_ptr)(void); +GBLREF uint4 gtmDebugLevel; /* Debug level */ +GBLREF uint4 process_id; +GBLREF jnlpool_addrs jnlpool; +GBLREF boolean_t pool_init; +GBLREF boolean_t created_core; +GBLREF boolean_t dont_want_core; +GBLREF mval dollar_zstatus, dollar_zerror; +GBLREF mval dollar_etrap; +GBLREF volatile int4 gtmMallocDepth; +GBLREF int4 exi_condition; +#ifdef VMS +GBLREF struct chf$signal_array *tp_restart_fail_sig; +GBLREF boolean_t tp_restart_fail_sig_used; +#endif +GBLREF int merge_args; +GBLREF lvzwrite_datablk *lvzwrite_block; +GBLREF volatile boolean_t dollar_zininterrupt; +GBLREF boolean_t ztrap_explicit_null; /* whether $ZTRAP was explicitly set to NULL in this frame */ +GBLREF dollar_ecode_type dollar_ecode; /* structure containing $ECODE related information */ +GBLREF boolean_t in_gvcst_incr; +GBLREF gv_namehead *gv_target; +GBLREF gd_region *gv_cur_region; +GBLREF sgmnt_addrs *cs_addrs; +GBLREF sgmnt_data_ptr_t cs_data; +GBLREF sgm_info *first_sgm_info; +GBLREF dollar_stack_type dollar_stack; +GBLREF mval *alias_retarg; +#ifdef UNIX +GBLREF io_desc *gtm_err_dev; +GBLREF char *util_outptr, util_outbuff[OUT_BUFF_SIZE]; +#endif +#ifdef GTM_TRIGGER +GBLREF int tprestart_state; /* When triggers restart, multiple states possible. + See tp_restart.h */ +GBLREF int4 gtm_trigger_depth; +#endif +#ifdef DEBUG +GBLREF boolean_t donot_INVOKE_MUMTSTART; +#endif + +error_def(ERR_NOEXCNOZTRAP); +error_def(ERR_NOTPRINCIO); +error_def(ERR_RTSLOC); +error_def(ERR_SRCLOCUNKNOWN); +error_def(ERR_LABELMISSING); +error_def(ERR_STACKOFLOW); +error_def(ERR_STACKCRIT); +error_def(ERR_TPSTACKOFLOW); +error_def(ERR_TPSTACKCRIT); +error_def(ERR_GTMCHECK); +error_def(ERR_CTRLC); +error_def(ERR_CTRLY); +error_def(ERR_CTRAP); +error_def(ERR_UNSOLCNTERR); +error_def(ERR_RESTART); +error_def(ERR_TPRETRY); +error_def(ERR_ASSERT); +error_def(ERR_GTMASSERT); +error_def(ERR_TPTIMEOUT); +error_def(ERR_OUTOFSPACE); +error_def(ERR_REPEATERROR); +error_def(ERR_TPNOTACID); +error_def(ERR_JOBINTRRQST); +error_def(ERR_JOBINTRRETHROW); +error_def(ERR_MEMORY); +error_def(ERR_VMSMEMORY); +error_def(ERR_GTMERREXIT); + +#define RUNTIME_ERROR_STR "Following runtime error" + +boolean_t clean_mum_tstart(void); + +#ifdef GTM_TRIGGER +/* When we go to restart generated code after handling an error, verify that we are not in frame or one created on its + * behalf that invoked a trigger and caused a dynamic TSTART to be done on its behalf. This can happen for example if + * a trigger is invoked for the first time but get a compilation or link failure error. This error is thrown from + * gtm_trigger() while no trigger based error handling is in effect so no rollback of the dynamic frame occurs which + * will result in unhandled TPQUIT errors, perhaps interminably. + */ +#define MUM_TSTART_FRAME_CHECK \ +{ \ + if ((0 == gtm_trigger_depth) && tp_pointer && tp_pointer->implicit_tstart) \ + { \ + DEBUG_ONLY(donot_INVOKE_MUMTSTART = FALSE); \ + OP_TROLLBACK(-1); /* Unroll implicit TP frame */ \ + } \ +} +#else +#define MUM_TSTART_FRAME_CHECK +#endif + + +/* We ignore errors in the $ZYERROR routine. When an error occurs, we unwind all stack frames upto and including + * zyerr_frame. MUM_TSTART then transfers control to the $ZTRAP frame. + */ +boolean_t clean_mum_tstart(void) +{ + stack_frame *save_zyerr_frame, *fp, *fpprev; + boolean_t save_check_flag; + + if (NULL != zyerr_frame) + { + while ((NULL != frame_pointer) && (NULL != zyerr_frame)) + { + GOFRAMES(1, TRUE, FALSE); + } + assert(NULL != frame_pointer); + proc_act_type = 0; + if (indr_stringpool.base == stringpool.base) + { /* switch to run time stringpool */ + indr_stringpool = stringpool; + stringpool = rts_stringpool; + } + return TRUE; + } + return (NULL != err_act); +} + +CONDITION_HANDLER(mdb_condition_handler) +{ + unsigned char *cp, *context, *sp_base; + boolean_t dm_action; /* did the error occur on a action from direct mode */ + boolean_t trans_action; /* did the error occur during "transcendental" code */ + char src_line[MAX_ENTRYREF_LEN]; + char source_line_buff[MAX_SRCLINE + SIZEOF(ARROW)]; + char *saved_msg; + mstr src_line_d; + io_desc *err_dev; + tp_region *tr; + gd_region *reg_top, *reg_save, *reg_local; + gd_addr *addr_ptr; + sgmnt_addrs *csa; + mval zpos; + stack_frame *fp; + boolean_t error_in_zyerror; + boolean_t repeat_error, etrap_handling, reset_mpc; + int level, rc, saved_msg_len; + lv_val *lvptr; +# ifdef UNIX + unix_db_info *udi; +# endif + + START_CH; + DBGEHND((stderr, "mdb_condition_handler: Entered with SIGNAL=%d frame_pointer=0x"lvaddr"\n", SIGNAL, frame_pointer)); +# ifdef UNIX + /* It is possible that we entered here from a bad compile of the open exception handler + * for an rm device. If gtm_err_dev is still set and SFT_DEV_ACT is equal to + * proc_act_type then its structures should be released now. + */ + if (NULL != gtm_err_dev) + { + if (SFT_DEV_ACT == proc_act_type) + { + remove_rms(gtm_err_dev); + } else + assert(FALSE); + gtm_err_dev = NULL; + } +# endif + if (repeat_error = (ERR_REPEATERROR == SIGNAL)) /* assignment and comparison */ + SIGNAL = dollar_ecode.error_last_ecode; + preemptive_ch(SEVERITY); + if (NULL != alias_retarg) + { /* An error has occurred while an alias return arg was in-flight. Delivery won't happen now + * so we need to remove the extra counts that were added in unw_retarg() and dis-enchant + * the alias container itself. + */ + assert(alias_retarg->mvtype & MV_ALIASCONT); + if (alias_retarg->mvtype & MV_ALIASCONT) + { /* Protect the refs were are about to make in case ptr got banged up somehow */ + lvptr = (lv_val *)alias_retarg->str.addr; + assert(LV_IS_BASE_VAR(lvptr)); + DECR_CREFCNT(lvptr); + DECR_TREFCNT(lvptr); + } + alias_retarg->mvtype = 0; /* Kill the temp var (no longer a container) */ + alias_retarg = NULL; /* .. and no more in-flight return argument */ + } + if ((int)ERR_UNSOLCNTERR == SIGNAL) + { + /* --------------------------------------------------------------------- + * this is here for linking purposes. We want to delay the receipt of + * network errors in gtm until we are ready to deal with them. Hence + * the transfer table hijinx. To avoid doing this in the gvcmz routine, + * we signal the error and do it here + * --------------------------------------------------------------------- + */ + neterr_pending = TRUE; + FIX_XFER_ENTRY(xf_linefetch, op_fetchintrrpt); + FIX_XFER_ENTRY(xf_linestart, op_startintrrpt); + FIX_XFER_ENTRY(xf_forchk1, op_startintrrpt); + FIX_XFER_ENTRY(xf_forloop, op_forintrrpt); + CONTINUE; + } + MDB_START; + assert(FALSE == in_gvcst_incr); /* currently there is no known case where this can be TRUE at this point */ + in_gvcst_incr = FALSE; /* reset this just in case gvcst_incr/gvcst_put failed to do a good job of resetting */ + /* + * Ideally merge should have a condition handler to reset followings, but generated code + * can call other routines during MERGE command. So it is not easy to establish a condition handler there. + * Easy solution is following one line code + */ + merge_args = 0; + TREF(in_zwrite) = FALSE; + if ((SUCCESS != SEVERITY) && (INFO != SEVERITY)) + { + if (lvzwrite_block) + /* If lvzwrite_block does not (yet) exist, no harm, no foul */ + lvzwrite_block->curr_subsc = lvzwrite_block->subsc_count = 0; + } + if ((int)ERR_TPRETRY == SIGNAL) + { + /* ---------------------------------------------------- + * put the restart here for linking purposes. + * Utilities use T_RETRY, so calling from there causes + * all sorts of linking overlaps. + * ---------------------------------------------------- + */ + VMS_ONLY(assert(FALSE == tp_restart_fail_sig_used)); +# ifdef GTM_TRIGGER + /* Assert that we never end up invoking the MUM_TSTART macro handler in case of an implicit tstart restart. + * See GBLDEF of skip_INVOKE_RESTART and donot_INVOKE_MUMTSTART in gbldefs.c for more information. + * Note that it is possible for this macro to be invoked from generated code in a trigger frame (in which + * case gtm_trigger/tp_restart ensure control passed to mdb_condition_handler only until the outermost + * implicit tstart in which case they return). Assert accordingly. + */ + assert(!donot_INVOKE_MUMTSTART || gtm_trigger_depth); + TRIGGER_BASE_FRAME_UNWIND_IF_NOMANSLAND; +# endif + rc = tp_restart(1, TP_RESTART_HANDLES_ERRORS); + DBGEHND((stderr, "mdb_condition_handler: tp_restart returned with rc=%d. state=%d, and SIGNAL=%d\n", + rc, tprestart_state, error_condition)); +# ifdef GTM_TRIGGER + if (0 != rc) + { /* The only time "tp_restart" will return non-zero is if the error needs to be + * rethrown. To accomplish that, we will unwind this handler which will return to + * the inner most initiating dm_start() with the return code set to whatever mumps_status + * is set to. + */ + assert(TPRESTART_STATE_NORMAL != tprestart_state); + assert(rc == SIGNAL); + if (!(SFT_TRIGR & frame_pointer->type) || (0 == gtm_trigger_depth)) + /* protect against unwind/exit */ + GTMASSERT; + mumps_status = rc; + DBGEHND((stderr, "mdb_condition_handler: Unwind-return to caller (gtm_trigger)\n")); + UNWIND(NULL, NULL); + } + /* "tp_restart" has succeeded so we have unwound back to the return point but check if the + * transaction was initiated by an implicit trigger TSTART. This can occur if an error was + * encountered in a trigger before the trigger base-frame was setup. It can occur at any trigger + * level if a triggered update is preceeded by a TROLLBACK. + */ + if (!(SFT_TRIGR & frame_pointer->type) && tp_pointer && tp_pointer->implicit_tstart) + { + mumps_status = rc; + DBGEHND((stderr, "mdb_condition_handler: Returning to implicit TSTART originator\n")); + UNWIND(NULL, NULL); + } + assert(!donot_INVOKE_MUMTSTART); +# endif +# ifdef UNIX + if (ERR_TPRETRY == SIGNAL) /* (signal value undisturbed) */ +# elif defined VMS + if (!tp_restart_fail_sig_used) /* If tp_restart ran clean */ +# endif + { + /* ------------------------------------ + * clean up both stacks, and set mumps + * program counter back tstart level 1 + * ------------------------------------ + */ + ind_result_sp = ind_result_array; /* clean up any active indirection pool usages */ + ind_source_sp = ind_source_array; + MUM_TSTART; + } +# ifdef VMS + else + { /* Otherwise tp_restart had a signal that we must now deal with -- replace the TPRETRY + * information with that saved from tp_restart. + */ + /* Assert that we have room for these arguments - the array malloc is in tp_restart */ + assert(TPRESTART_ARG_CNT >= tp_restart_fail_sig->chf$is_sig_args); + memcpy(sig, tp_restart_fail_sig, (tp_restart_fail_sig->chf$l_sig_args + 1) * SIZEOF(int)); + tp_restart_fail_sig_used = FALSE; + } +# endif + } + /* Ensure gv_target and cs_addrs are in sync. If not, make them so. */ + if (NULL != gv_target) + { + csa = gv_target->gd_csa; + if ((NULL != csa) && (csa != cs_addrs)) + { + assert(0 < csa->regcnt); + /* If csa->regcnt is > 1, it is possible that csa->region is different from the actual gv_cur_region + * (before we encountered the runtime error). This is a case of two regions mapping to the same csa. + * The only issue with this is that some user-level error messages that have the region name (as + * opposed to the database file name) could print incorrect values. But other than that there should + * be no issues since finally the csa (corresponding to the physical database file) is what matters + * and that is the same for both the regions. Given that the region mismatch potential exists only + * until the next global reference which is different from $REFERENCE, we consider this acceptable. + */ + gv_cur_region = csa->region; + assert(gv_cur_region->open); + assert((dba_mm == gv_cur_region->dyn.addr->acc_meth) || (dba_bg == gv_cur_region->dyn.addr->acc_meth)); + /* The above assert is needed to ensure that change_reg/tp_change_reg (invoked below) + * will set cs_addrs, cs_data etc. to non-zero values. + */ + if (NULL != first_sgm_info) + change_reg(); /* updates "cs_addrs", "cs_data", "sgm_info_ptr" and maybe "first_sgm_info" */ + else + { /* We are either inside a non-TP transaction or in a TP transaction that has done NO database + * references. In either case, we do NOT want to setting sgm_info_ptr or first_sgm_info. + * Hence use tp_change_reg instead of change_reg below. + */ + tp_change_reg(); /* updates "cs_addrs", "cs_data" */ + } + assert(cs_addrs == csa); + assert(cs_data == csa->hdr); + assert(NULL != cs_data); + } + } + if (DUMPABLE) + { /* Certain conditions we don't want to attempt to create the M-level ZSHOW dump. + * 1) Unix: If gtmMallocDepth > 0 indicating memory manager was active and could be reentered. + * 2) Unix: If we have a SIGBUS or SIGSEGV (could be likely to occur again + * in the local variable code which would cause immediate shutdown with + * no cleanup). + * 3) VMS: If we got an ACCVIO for the same as reason (2). + * Note that we will bypass checks 2 and 3 if GDL_ZSHOWDumpOnSignal debug flag is on + */ + SET_PROCESS_EXITING_TRUE; /* So zshow doesn't push stuff on stack to "protect" it when + we potentially already have a stack overflow */ + cancel_timer(0); /* No interruptions now that we are dying */ + if (!repeat_error UNIX_ONLY(&& (0 == gtmMallocDepth))) + { + src_line_d.addr = src_line; /* Setup entry point buffer for set_zstatus() */ + src_line_d.len = 0; + SET_ZSTATUS(NULL); + } + /* Create the ZSHOW dump file if it can be created */ + create_fatal_error_zshow_dmp(SIGNAL, repeat_error); + + /* If we are about to core/exit on a stack over flow, only do the core part if a debug + * flag requests this behaviour. Otherwise, supress the core and just exit. + * 2006-03-07 se: If a stack overflow occurs on VMS, it has happened that the stack is no + * longer well formed so attempting to unwind it as it does in MUMPS_EXIT causes things + * to really become screwed up. For this reason, this niceness of avoiding a dump on a + * stack overflow on VMS is being disabled. The dump can be controlled wih set proc/dump + * (or not) as desired. + * 2008-01-29 (se): Added fatal MEMORY error so we no longer generate a core for it by + * default unless the DumpOnStackOFlow flag is turned on. Since this flag is not a user-exposed + * interface, I'm avoiding renaming it for now. Note the core avoidance applies to both UNIX + * and VMS since stack formation is not at issue in this sort of memory request. + * Finally note that in UNIX, ch_cond_core (called by DRIVECH macro which invoked this condition + * handler has likely already created the core and set the created_core flag which will prevent + * this process from creating another core for the same SIGNAL. We leave this code in here in + * case methods exist in the future for this module to be driven without invoking cond_core_ch + * first. + */ + if (!(GDL_DumpOnStackOFlow & gtmDebugLevel) && + VMS_ONLY((int)ERR_VMSMEMORY == SIGNAL) + UNIX_ONLY(((int)ERR_STACKOFLOW == SIGNAL || (int)ERR_STACKOFLOW == arg + || (int)ERR_MEMORY == SIGNAL || (int)ERR_MEMORY == arg))) + { +# ifdef VMS + /* Inside this ifdef, we are definitely here because of ERR_VMSMEMORY. If the conditions + * of the above if change, revisit these assmuptions. + * For VMSMEMORY error, we have to send the message to the operator log and to the + * console ourselves because the MUMP_EXIT method of exiting on a fatal error does + * not preserve the substitution parameters for the message making it useless. After + * sending the message change the status code so we exit with something other than the + * duplicate message. + */ + assert(ERR_VMSMEMORY == SIGNAL); + send_msg(VARLSTCNT(4) ERR_VMSMEMORY, 2, *(int **)(&sig->chf$is_sig_arg1 + 1), + *(int **)(&sig->chf$is_sig_arg1 + 2)); + gtm_putmsg(VARLSTCNT(4) ERR_VMSMEMORY, 2, *(int **)(&sig->chf$is_sig_arg1 + 1), + *(int **)(&sig->chf$is_sig_arg1 + 2)); + SIGNAL = ERR_GTMERREXIT; /* Override reason for "stop" */ +# endif + MUMPS_EXIT; /* Do a clean exit rather than messy core exit */ + } + gtm_dump(); + TERMINATE; + } +# ifdef GTM_TRIGGER + if (TPRESTART_STATE_NORMAL != tprestart_state) + GTMASSERT; /* Can't leave half-restarted transaction around - out of design */ +# endif + if (active_lv) + { + if (!LV_IS_VAL_DEFINED(active_lv) && !LV_HAS_CHILD(active_lv)) + op_kill(active_lv); + active_lv = (lv_val *)0; + } + /* ----------------------------------------------------------- + * Don't release crit: + * -- unless SEVERITY is at least "WARNING". + * -- until after TP retries have been handled and + * dumpable errors have dumped + * NOTE: holding crit till after dump can stop the world for + * a while. That is acceptable because: + * -- It's better to make other processes wait, to ensure + * dump reflects state at time of error. + * -- Dumping above this point prevents a second trip + * through here when an error occurs in rel_crit(). + * NOTE: Release of crit during "final" TP retry can trigger + * an assert failure (in dbg/bta builds only), if execution + * continues and no TROLLBACK is issued. + * ----------------------------------------------------------- + */ + if ((SUCCESS != SEVERITY) && (INFO != SEVERITY)) + { /* Note the existence of similar (and yet largely different) code in the TPNOTACID_CHECK macro. + * Any changes here might need to be reflected there too. + */ + if (IS_TP_AND_FINAL_RETRY) + { + TP_FINAL_RETRY_DECREMENT_T_TRIES_IF_OK; + getzposition(&zpos); +# ifdef UNIX + /* We want to put a message out to the operator log to warn of potential loss of ACID qualities + * but doing so in UNIX will overlay the message buffer in util_output so we save and restore + * that buffer. Since this code is seldom hit and allocating a full sized message buffer permanently + * is a waste, we allocate and free the buffer. + */ + assert(0 < (util_outptr - util_outbuff)); + saved_msg_len = INTCAST(util_outptr - util_outbuff); + saved_msg = (char *)malloc(saved_msg_len); + memcpy(saved_msg, util_outbuff, saved_msg_len); + send_msg(VARLSTCNT(9) ERR_TPNOTACID, 8, LEN_AND_LIT(RUNTIME_ERROR_STR), zpos.str.len, zpos.str.addr, + RTS_ERROR_TEXT("-"), saved_msg_len, saved_msg); + memcpy(util_outbuff, saved_msg, saved_msg_len); + util_outptr = util_outbuff + saved_msg_len; + free(saved_msg); +# else + send_msg(VARLSTCNT(8) ERR_TPNOTACID, 4, LEN_AND_LIT(RUNTIME_ERROR_STR), zpos.str.len, zpos.str.addr, + SIGNAL, 0); +# endif + } + ENABLE_AST + for (addr_ptr = get_next_gdr(NULL); addr_ptr; addr_ptr = get_next_gdr(addr_ptr)) + { + for (reg_local = addr_ptr->regions, reg_top = reg_local + addr_ptr->n_regions; + reg_local < reg_top; reg_local++) + { + if (reg_local->open && !reg_local->was_open) + { + csa = (sgmnt_addrs *)&FILE_INFO(reg_local)->s_addrs; + if (csa && csa->now_crit) + rel_crit(reg_local); + } + } + } + UNIX_ONLY( + /* Release FTOK lock on the replication instance file if holding it (possible if error in jnlpool_init) */ + assert((NULL == jnlpool.jnlpool_dummy_reg) || jnlpool.jnlpool_dummy_reg->open || !pool_init); + if ((NULL != jnlpool.jnlpool_dummy_reg) && jnlpool.jnlpool_dummy_reg->open) + { + udi = FILE_INFO(jnlpool.jnlpool_dummy_reg); + assert(NULL != udi); + if (NULL != udi) + { + if (udi->grabbed_ftok_sem) + ftok_sem_release(jnlpool.jnlpool_dummy_reg, FALSE, FALSE); + assert(!udi->grabbed_ftok_sem); + } + } + ) + /* Release crit lock on journal pool if holding it */ + if (pool_init) /* atleast one region replicated and we have done jnlpool init */ + { + csa = (sgmnt_addrs *)&FILE_INFO(jnlpool.jnlpool_dummy_reg)->s_addrs; + if (csa && csa->now_crit) + rel_lock(jnlpool.jnlpool_dummy_reg); + } + TREF(in_op_fnnext) = FALSE; /* in case we were in a next */ + } +# ifdef GTM_TRIGGER + /* At this point, we are past the point where the frame pointer is allowed to be resting on a trigger frame + * (this is possible in a TPRETRY situation where gtm_trigger must return to gtm_trigger() signaling a + * restart is necessary). If we are on a trigger base frame, unwind it so the error is recognized in + * the invoker's frame. + */ + if (SFT_TRIGR & frame_pointer->type) + { + /* Better be an error in here info or success messages want to continue, not be unwound but + * we cannot go past this point in a trigger frame or the frame_pointer back reference below + * will fail. + */ + assert((SUCCESS != SEVERITY) && (INFO != SEVERITY)); + /* These outofband conditions depend on saving the current stack frame info in restart_pc which + * is of course no longer valid once the frame is unrolled so they must be avoided. At the time + * of this writing, there are no conditions that these should validly be called in this + * situation so this check is more for the future. + */ + assert(((int)ERR_CTRLY != SIGNAL) && ((int)ERR_CTRLC != SIGNAL) && ((int)ERR_CTRAP != SIGNAL) + && ((int)ERR_JOBINTRRQST != SIGNAL) && ((int)ERR_JOBINTRRETHROW != SIGNAL)); + gtm_trigger_fini(TRUE, FALSE); + DBGEHND((stderr, "mdb_condition_handler: Current trigger frame unwound so error is thrown" + " on trigger invoker's frame instead.\n")); + } +# endif + err_dev = active_device; + active_device = (io_desc *)NULL; + ind_result_sp = ind_result_array; /* clean up any active indirection pool usages */ + ind_source_sp = ind_source_array; + dm_action = (frame_pointer->old_frame_pointer->type & SFT_DM) + || (TREF(compile_time) && (frame_pointer->type & SFT_DM)); + /* The errors are said to be transcendental when they occur during compilation/execution + * of the error trap ({z,e}trap, device exception) or $zinterrupt. The errors in other + * indirect code frames (zbreak, zstep, xecute etc.) aren't defined to be trancendental + * and will be treated as if they occured in a regular code frame. + */ + trans_action = proc_act_type || (frame_pointer->type & SFT_ZTRAP) || (frame_pointer->type & SFT_DEV_ACT); + src_line_d.addr = src_line; + src_line_d.len = 0; + flush_pio(); + if ((int)ERR_CTRLY == SIGNAL) + { + outofband_clear(); + assert(NULL != restart_pc); + frame_pointer->mpc = restart_pc; + frame_pointer->ctxt = restart_ctxt; + MUM_TSTART; + } else if ((int)ERR_CTRLC == SIGNAL) + { + outofband_clear(); + if (!trans_action && !dm_action) + { + frame_pointer->mpc = restart_pc; + frame_pointer->ctxt = restart_ctxt; + assert(NULL != frame_pointer->mpc); + if (!(frame_pointer->type & SFT_DM)) + dm_setup(); + } else if (frame_pointer->type & SFT_DM) + { + frame_pointer->ctxt = GTM_CONTEXT(call_dm); + frame_pointer->mpc = CODE_ADDRESS(call_dm); + } else + { + /* Do cleanup on indirect frames prior to reset */ + IF_INDR_FRAME_CLEANUP_CACHE_ENTRY_AND_UNMARK(frame_pointer); + frame_pointer->ctxt = GTM_CONTEXT(pseudo_ret); + frame_pointer->mpc = CODE_ADDRESS(pseudo_ret); + } + frame_pointer->flags &= SFF_TRIGR_CALLD_OFF; /* Frame enterable now with mpc reset */ + GTMTRIG_ONLY(DBGTRIGR((stderr, "mdb_condition_handler: turning off SFF_TRIGR_CALLD (1) in frame 0x"lvaddr"\n", + frame_pointer))); + PRN_ERROR; + if (io_curr_device.out != io_std_device.out) + { + dec_err(VARLSTCNT(4) ERR_NOTPRINCIO, 2, io_curr_device.out->trans_name->len, + io_curr_device.out->trans_name->dollar_io); + } + MUM_TSTART; + } else if ((int)ERR_CTRAP == SIGNAL) + { + outofband_clear(); + if (!trans_action && !dm_action && !(frame_pointer->type & SFT_DM)) + { + sp_base = stringpool.base; + if (sp_base != rts_stringpool.base) + { + indr_stringpool = stringpool; /* update indr_stringpool */ + stringpool = rts_stringpool; /* change for set_zstatus */ + } + if (!repeat_error) + { + dollar_ecode.error_last_b_line = SET_ZSTATUS(NULL); + } + if (sp_base != rts_stringpool.base) + { + rts_stringpool = stringpool; /* update rts_stringpool */ + stringpool = indr_stringpool; /* change back */ + } + assert(NULL != dollar_ecode.error_last_b_line); + assert(NULL != restart_pc); + frame_pointer->mpc = restart_pc; + frame_pointer->ctxt = restart_ctxt; + err_act = NULL; + dollar_ecode.error_last_ecode = SIGNAL; + if (std_dev_outbnd && io_std_device.in && io_std_device.in->type == tt && + io_std_device.in->error_handler.len) + { + proc_act_type = SFT_DEV_ACT; + err_act = &io_std_device.in->error_handler; + } else if (!std_dev_outbnd && err_dev && (err_dev->type == tt) && err_dev->error_handler.len) + { + proc_act_type = SFT_DEV_ACT; + err_act = &err_dev->error_handler; + } else if (NULL != error_frame) + { /* a primary error occurred already. irrespective of whether ZTRAP or ETRAP is active now, + * we need to consider this as a nested error and trigger nested error processing. + */ + goerrorframe(); /* unwind upto error_frame */ + proc_act_type = 0; + } else if (0 != dollar_ztrap.str.len) + { + proc_act_type = SFT_ZTRAP; + err_act = &dollar_ztrap.str; + } else + { /* either $ETRAP is empty-string or non-empty. + * if non-empty, use $ETRAP for error-handling. + * if empty, + * if ztrap_explicit_null is FALSE use empty-string $ETRAP for error-handling + * if ztrap_explicit_null is TRUE unwind as many frames as possible until we see a frame + * where ztrap_explicit_null is FALSE and $ZTRAP is NULL. + * in that frame, use $ETRAP for error-handling. + * if no such frame is found, exit after printing the error. + */ + etrap_handling = TRUE; + if (ztrap_explicit_null) + { + assert(0 == dollar_etrap.str.len); + for (level = dollar_zlevel() - 1; level > 0; level--) + { + GOLEVEL(level, FALSE); + assert(level == dollar_zlevel()); + if (!ztrap_explicit_null && !dollar_ztrap.str.len) + break; + } + if (0 >= level) + { + assert(0 == level); + etrap_handling = FALSE; + } + } + if (SFF_CI & frame_pointer->flags) + { /* Unhandled errors from called-in routines should return to gtm_ci() with error status */ + mumps_status = SIGNAL; + MUM_TSTART_FRAME_CHECK; + MUM_TSTART; + } else if (etrap_handling) + { + proc_act_type = SFT_ZTRAP; + err_act = &dollar_etrap.str; + } else + { + PRN_ERROR; + rts_error(VARLSTCNT(1) ERR_NOEXCNOZTRAP); + } + } + if (clean_mum_tstart()) + { + MUM_TSTART_FRAME_CHECK; + MUM_TSTART; + } + } else if (frame_pointer->type & SFT_DM) + { + frame_pointer->ctxt = GTM_CONTEXT(call_dm); + frame_pointer->mpc = CODE_ADDRESS(call_dm); + frame_pointer->flags &= SFF_TRIGR_CALLD_OFF; /* Frame enterable now with mpc reset */ + GTMTRIG_ONLY(DBGTRIGR((stderr, "mdb_condition_handler: turning off SFF_TRIGR_CALLD (2) in frame 0x" + lvaddr"\n", frame_pointer))); + } else + { + /* Do cleanup on indirect frames prior to reset */ + IF_INDR_FRAME_CLEANUP_CACHE_ENTRY_AND_UNMARK(frame_pointer); + frame_pointer->ctxt = GTM_CONTEXT(pseudo_ret); + frame_pointer->mpc = CODE_ADDRESS(pseudo_ret); + frame_pointer->flags &= SFF_TRIGR_CALLD_OFF; /* Frame enterable now with mpc reset */ + GTMTRIG_ONLY(DBGTRIGR((stderr, "mdb_condition_handler: turning off SFF_TRIGR_CALLD (3) in frame 0x" + lvaddr"\n", frame_pointer))); + } + PRN_ERROR; + if (io_curr_device.out != io_std_device.out) + { + dec_err(VARLSTCNT(4) ERR_NOTPRINCIO, 2, io_curr_device.out->trans_name->len, + io_curr_device.out->trans_name->dollar_io); + } + MUM_TSTART_FRAME_CHECK; + MUM_TSTART; + } else if ((int)ERR_JOBINTRRQST == SIGNAL) + { + assert(NULL != restart_pc); + frame_pointer->mpc = restart_pc; + frame_pointer->ctxt = restart_ctxt; + assert(!dollar_zininterrupt); + dollar_zininterrupt = TRUE; /* Note done before outofband is cleared to prevent nesting */ + outofband_clear(); + proc_act_type = SFT_ZINTR | SFT_COUNT; /* trans_code will invoke jobinterrupt_process for us */ + MUM_TSTART; + } else if ((int)ERR_JOBINTRRETHROW == SIGNAL) + { /* job interrupt is rethrown from TC/TRO */ + assert(!dollar_zininterrupt); + dollar_zininterrupt = TRUE; + proc_act_type = SFT_ZINTR | SFT_COUNT; /* trans_code will invoke jobinterrupt_process for us */ + MUM_TSTART; + } else if ((int)ERR_STACKCRIT == SIGNAL) + { + assert(msp > stacktop); + assert(stackwarn > stacktop); + cp = stackwarn; + stackwarn = stacktop; + push_stck(cp, 0, (void**)&stackwarn, MVST_STCK_SP); + } + if (!repeat_error) + dollar_ecode.error_last_b_line = NULL; + /* ---------------------------------------------------------------- + * error from direct mode actions does not set $zstatus and is not + * restarted (dollar_ecode.error_last_b_line = NULL); error from transcendental + * code does set $zstatus but does not restart the line + * ---------------------------------------------------------------- + */ + if (!dm_action) + { + sp_base = stringpool.base; + if (sp_base != rts_stringpool.base) + { + indr_stringpool = stringpool; /* update indr_stringpool */ + stringpool = rts_stringpool; /* change for set_zstatus */ + } + if (!repeat_error) + { + dollar_ecode.error_last_b_line = SET_ZSTATUS(&context); + } + assert(NULL != dollar_ecode.error_last_b_line); + if (sp_base != rts_stringpool.base) + { + rts_stringpool = stringpool; /* update rts_stringpool */ + stringpool = indr_stringpool; /* change back */ + } + } + if ((SUCCESS == SEVERITY) || (INFO == SEVERITY)) + { + PRN_ERROR; + CONTINUE; + } + /* ----------------------------------------------------------------------- + * This call to clear TP timeout is like the one currently in op_halt, in + * case there's another path with a similar need. If so, it would likely + * go through here. + * ----------------------------------------------------------------------- + */ + (*tp_timeout_clear_ptr)(); + /* ---------------------------------------------------------------- + * error from direct mode actions or "transcendental" code does not + * invoke MUMPS error handling routines + * ---------------------------------------------------------------- + */ + if (!dm_action && !trans_action) + { + DBGEHND((stderr, "mdb_condition_handler: Handler to dispatch selection checks\n")); + err_act = NULL; + dollar_ecode.error_last_ecode = SIGNAL; + reset_mpc = FALSE; + if (err_dev && err_dev->error_handler.len && ((int)ERR_TPTIMEOUT != SIGNAL)) + { + proc_act_type = SFT_DEV_ACT; + err_act = &err_dev->error_handler; + /* Reset mpc to beginning of the current line (to retry after processing the IO exception handler) */ + reset_mpc = TRUE; + DBGEHND((stderr, "mdb_condition_handler: dispatching device error handler [%.*s]\n", err_act->len, + err_act->addr)); + } else if (NULL != error_frame) + { /* a primary error occurred already. irrespective of whether ZTRAP or ETRAP is active now, we need to + * consider this as a nested error and trigger nested error processing. + */ + goerrorframe(); /* unwind upto error_frame */ + proc_act_type = 0; + DBGEHND((stderr, "mdb_condition_handler: Have unwound to error frame via goerrorframe() and am " + "re-dispatching error frame\n")); + MUM_TSTART_FRAME_CHECK; + MUM_TSTART; /* unwind the current C-stack and restart executing from the top of the current M-stack */ + assert(FALSE); + } else if (0 != dollar_ztrap.str.len) + { + assert(!ztrap_explicit_null); + proc_act_type = SFT_ZTRAP; + err_act = &dollar_ztrap.str; + DBGEHND((stderr, "mdb_condition_handler: Dispatching $ZTRAP error handler [%.*s]\n", err_act->len, + err_act->addr)); + /* Reset mpc to beginning of the current line (to retry after invoking $ZTRAP) */ + reset_mpc = TRUE; + } else + { /* either $ETRAP is empty-string or non-empty. + * if non-empty, use $ETRAP for error-handling. + * if empty, + * if ztrap_explicit_null is FALSE use empty-string $ETRAP for error-handling + * if ztrap_explicit_null is TRUE unwind as many frames as possible until we see a frame + * where ztrap_explicit_null is FALSE and $ZTRAP is NULL. + * in that frame, use $ETRAP for error-handling. + * if no such frame is found, exit after printing the error. + */ + etrap_handling = TRUE; + if (ztrap_explicit_null) + { + GTMTRIG_ONLY(assert(0 == gtm_trigger_depth)); /* Should never happen in a trigger */ + DBGEHND((stderr, "mdb_condition_handler: ztrap_explicit_null set - unwinding till find handler\n")); + assert(0 == dollar_etrap.str.len); + for (level = dollar_zlevel() - 1; level > 0; level--) + { + GOLEVEL(level, FALSE); + assert(level == dollar_zlevel()); + if (!ztrap_explicit_null && !dollar_ztrap.str.len) + break; + } + if (0 >= level) + { + assert(0 == level); + etrap_handling = FALSE; + } + } + if (SFF_CI & frame_pointer->flags) + { /* Unhandled errors from called-in routines should return to gtm_ci() with error status */ + mumps_status = SIGNAL; + DBGEHND((stderr, "mdb_condition_handler: Call in base frame found - returnning to callins\n")); + MUM_TSTART_FRAME_CHECK; + MUM_TSTART; + } else if (etrap_handling) + { + proc_act_type = SFT_ZTRAP; + err_act = &dollar_etrap.str; + DBGEHND((stderr, "mdb_condition_handler: $ETRAP handler being dispatched [%.*s]\n", err_act->len, + err_act->addr)); + } + } + if (reset_mpc) + { /* ---------------------------------------------------------------------------------------------------- + * Reset the mpc such that + * (a) If the current frame is a counted frame, the error line is retried after the error is handled, + * (b) If the current frame is "transcendental" code, set frame to return. + * If we are in $ZYERROR, we don't care about restarting the line that errored since we will + * unwind all frames upto and including zyerr_frame. + * If this is a rethrown error (ERR_REPEATERROR) from a child frame, do NOT reset mpc of the current + * frame in that case. We do NOT want to retry the current line (after the error has been + * processed) because the error did not occur in this line and therefore re-executing the same + * line could cause undesirable effects at the M-user level. We will resume normal execution + * once the error is handled. Not that it matters, but note that in the case of a rethrown error + * (repeat_error is TRUE), we would NOT have noted down dollar_ecode.error_last_b_line so cannot + * use that to reset mpc anyways. + * ---------------------------------------------------------------------------------------------------- + */ + if ((NULL == zyerr_frame) && !repeat_error) + { + DBGEHND((stderr, "mdb_condition_handler: reset_mpc triggered\n")); + for (fp = frame_pointer; fp; fp = fp->old_frame_pointer) + { /* See if this is a $ZINTERRUPT frame. If yes, we want to restart *this* line + * at the beginning. Since it is always an indirect frame, we can use the context + * pointer to start over. $ETRAP does things somewhat differently in that the current + * frame is always returned from. + */ + if (SFT_ZINTR & fp->type) + { + assert(SFF_INDCE & fp->flags); + fp->mpc = fp->ctxt; + break; + } + /* Do cleanup on indirect frames prior to reset */ + IF_INDR_FRAME_CLEANUP_CACHE_ENTRY_AND_UNMARK(fp); + /* mpc points to PTEXT */ + /* The equality check in the second half of the expression below is to account for the + * delay-slot in HP-UX for implicit quits. Not an issue here, but added for uniformity. + */ + if (ADDR_IN_CODE(fp->mpc, fp->rvector)) + { /* GT.M specific error trapping retries the line with the error */ + fp->mpc = dollar_ecode.error_last_b_line; + fp->ctxt = context; + break; + } else + { + fp->ctxt = GTM_CONTEXT(pseudo_ret); + fp->mpc = CODE_ADDRESS(pseudo_ret); + } + fp->flags &= SFF_TRIGR_CALLD_OFF; /* Frame enterable now with mpc reset */ + GTMTRIG_ONLY(DBGTRIGR((stderr, "mdb_condition_handler: turning off SFF_TRIGR_CALLD (4) " + "in frame 0x"lvaddr"\n", frame_pointer))); + } + } + } + if (clean_mum_tstart()) + { +# ifdef UNIX + /* on zos, if opening a fifo which is not read only we need to fix the type for + the err_dev to rm */ +# ifdef __MVS__ + if (err_dev && dev_open != err_dev->state && (ff == err_dev->type)) + { + assert(NULL != err_dev->pair.out); + if (rm == err_dev->pair.out->type) + { + /* have to massage the device so remove_rms will cleanup the partially + created fifo. Refer to io_open_try.c for creation of split fifo device. */ + err_dev->newly_created = 1; + err_dev->type = rm; + err_dev->dev_sp = err_dev->pair.out->dev_sp; + err_dev->pair.out->dev_sp = NULL; + } + } +# endif + if (err_dev && dev_open != err_dev->state && (rm == err_dev->type)) + { + gtm_err_dev = err_dev; + /* structures pointed to by err_dev were freed so make sure it's not used again */ + err_dev = NULL; + } +# endif + MUM_TSTART_FRAME_CHECK; + MUM_TSTART; + } else + DBGEHND((stderr, "mdb_condition_handler: clean_mum_tstart returned FALSE\n")); + } else + { + DBGEHND((stderr, "mdb_condition_handler: Transient or direct mode frame -- bypassing handler dispatch\n")); +# ifdef UNIX + /* executed from the direct mode so do the rms check and cleanup if necessary */ + /* on zos, if opening a fifo which is not read only we need to fix the type for + the err_dev to rm */ +# ifdef __MVS__ + if (err_dev && dev_open != err_dev->state && (ff == err_dev->type)) + { + assert(NULL != err_dev->pair.out); + if (rm == err_dev->pair.out->type) + { + /* have to massage the device so remove_rms will cleanup the partially + created fifo */ + err_dev->newly_created = 1; + err_dev->type = rm; + err_dev->dev_sp = err_dev->pair.out->dev_sp; + err_dev->pair.out->dev_sp = NULL; + } + } +# endif + if (err_dev && dev_open != err_dev->state && (rm == err_dev->type)) + { + remove_rms(err_dev); + /* structures pointed to by err_dev were freed so make sure it's not used again */ + err_dev = NULL; + } +# endif + } + if ((SFT_ZINTR | SFT_COUNT) != proc_act_type || 0 == dollar_ecode.error_last_b_line) + { /* No user console error for $zinterrupt compile problems and if not direct mode. Accomplish + this by bypassing the code inside this if which *will* be executed for most cases + */ + DBGEHND((stderr, "mdb_condition_handler: Printing error status\n")); + PRN_ERROR; + if (TREF(compile_time) && ((int)ERR_LABELMISSING) != SIGNAL) + show_source_line(source_line_buff, SIZEOF(source_line_buff), TRUE); + } + if (!dm_action && !trans_action && (0 != src_line_d.len)) + { + if (MSG_OUTPUT) + dec_err(VARLSTCNT(4) ERR_RTSLOC, 2, src_line_d.len, src_line_d.addr); + } else + { + if (trans_action || dm_action) + { /* If true transcendental, do trans_code_cleanup. If our counted frame that + * is masquerading as a transcendental frame, run jobinterrupt_process_clean + */ + DBGEHND((stderr, "mdb_condition_handler: trans_code_cleanup() or jobinterrupt_process_cleanup being " + "dispatched\n")); + if (!(SFT_ZINTR & proc_act_type)) + trans_code_cleanup(); + else + jobinterrupt_process_cleanup(); + MUM_TSTART_FRAME_CHECK; + MUM_TSTART; + } else if (MSG_OUTPUT) + { /* If a message about the location is needed, it should be possible to pull the location + * out of the $STACK array. If it exists, use it instead. + */ + if ((NULL != dollar_stack.array) && (0 < dollar_stack.index)) + { /* Error entry exists */ + src_line_d = dollar_stack.array[dollar_stack.index - 1].place_str; + assert(src_line_d.len); + assert(src_line_d.addr); + dec_err(VARLSTCNT(4) ERR_RTSLOC, 2, src_line_d.len, src_line_d.addr); + } else + dec_err(VARLSTCNT(1) ERR_SRCLOCUNKNOWN); + } + } + DBGEHND((stderr, "mdb_condition_handler: Condition not handled -- defaulting to process exit\n")); + MUMPS_EXIT; +} diff --git a/sr_port/mdef.h b/sr_port/mdef.h new file mode 100644 index 0000000..c7e6af4 --- /dev/null +++ b/sr_port/mdef.h @@ -0,0 +1,1616 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + + +#ifndef MDEF_included +#define MDEF_included + +/* mstr needs to be defined before including "mdefsp.h". */ +typedef int mstr_len_t; +#ifndef __vms +typedef struct +{ + unsigned int char_len; /* Character length */ + mstr_len_t len; + char *addr; +} mstr; +# define MSTR_CONST(name, string) mstr name = {0, LEN_AND_LIT(string)} +# define MSTR_DEF(name, length, string) mstr name = {0, length, string} +# define MIDENT_CONST(name, string) mident name = {0, LEN_AND_LIT(string)} +# define MIDENT_DEF(name, length, string) mident name = {0, length, string} +#else +typedef struct +{ + mstr_len_t len; /* Byte length */ + char *addr; +} mstr; +# define MSTR_CONST(name, string) mstr name = {LEN_AND_LIT(string)} +# define MSTR_DEF(name, length, string) mstr name = {length, string} +# define MIDENT_CONST(name, string) mident name = {LEN_AND_LIT(string)} +# define MIDENT_DEF(name, length, string) mident name = {length, string} +#endif + +#define GET_MSTR_LEN(X, Y) GET_ULONG(X, Y) +#define PUT_MSTR_LEN(X, Y) PUT_ULONG(X, Y) + +#define MEMVCMP(STR1, STR1LEN, STR2, STR2LEN, RESULT) \ +{ \ + int lcl_str1Len, lcl_str2Len; \ + int lcl_minLen, lcl_retVal, lcl_retVal2; \ + \ + lcl_str1Len = STR1LEN; \ + lcl_str2Len = STR2LEN; \ + if (lcl_str1Len < lcl_str2Len) \ + { \ + lcl_minLen = lcl_str1Len; \ + lcl_retVal = -1; \ + } else if (lcl_str1Len > lcl_str2Len) \ + { \ + lcl_minLen = lcl_str2Len; \ + lcl_retVal = 1; \ + } else \ + { \ + lcl_minLen = lcl_str1Len; \ + lcl_retVal = 0; \ + } \ + RESULT = (0 == (lcl_retVal2 = memcmp(STR1, STR2, lcl_minLen))) ? lcl_retVal : lcl_retVal2; \ +} + +/* There are 2 MSTR*CMP macros. One is if the parameters are available as MSTRs and another if the parameters + * are available as MSTR pointers. Use whichever is appropriate as it saves cycles. + */ +#define MSTRP_CMP(x, y, result) MEMVCMP((x)->addr, (x)->len, (y)->addr, (y)->len, result) +#define MSTR_CMP(x, y, result) MEMVCMP((x).addr, (x).len, (y).addr, (y).len, result) +#define MSTR_EQ(x, y) (((x)->len == (y)->len) && !memcmp((x)->addr, (y)->addr, (x)->len)) + +#include + +#define sssize_t size_t +#define SHMDT(X) shmdt((void *)(X)) + +/* constant needed for FIFO - OS390 redefines in mdefsp.h */ +#define FIFO_PERMISSION 010666 /* fifo with RW permissions for owner, group, other */ + +#include +#include "mdefsa.h" +#include "mdefsp.h" +#include "gtm_sizeof.h" +#include "gtm_threadgbl.h" +/* Anchor for thread-global structure rather than individual global vars */ +GBLREF void *gtm_threadgbl; /* Accessed through TREF macro in gtm_threadgbl.h */ + +#ifdef DEBUG +error_def(ERR_ASSERT); +#define assert(x) ((x) ? 1 : rts_error(VARLSTCNT(7) ERR_ASSERT, 5, LEN_AND_LIT(__FILE__), __LINE__, (SIZEOF(#x) - 1), (#x))) +#else +#define assert(x) +#endif + +#ifdef GTM64 +# define lvaddr "%016lx" +#else +# define lvaddr "%08lx" +#endif + +/* Define GT.M interlude functions for open, close, pipe, creat and dup system calls. This lets GT.M trace through all file + * descriptor activity (needed for D9I11-002714). Do this on all Unix platforms. Note that only the macro GTM_FD_TRACE is + * defined here. gtm_unistd.h and gtm_fcntl.h define the actual GT.M interlude functions based on this macro. + */ +#if defined(UNIX) +# define GTM_FD_TRACE +# define GTM_FD_TRACE_ONLY(X) X +#else +# define GTM_FD_TRACE_ONLY(X) +#endif + +/* Define what is an invalid file descriptor in Unix and VMS. */ +#if defined(UNIX) +# define FD_INVALID -1 /* fd of -1 is invalid in Unix posix calls */ +# define FD_INVALID_NONPOSIX FD_INVALID +#else +# define FD_INVALID -1 /* fd of -1 is invalid in VMS if using POSIX interface (open/close etc.) */ +# define FD_INVALID_NONPOSIX 0 /* fd of 0 is invalid in VMS if using RMS sys$open calls (non-posix interface) */ +#endif + +/* Now that mdefsp.h is included, GBLDEF should have been #defined. Use it to define STATICDEF for variables + * and STATICFNDEF, STATICFNDCL for functions. Define STATICDEF to "GBLDEF". This way we know such usages are intended + * to be "static" but yet can effectively debug these variables since they are externally visible. + * For functions, do not use the "static" keyword to make them externally visible. + * Note that a STATICREF for variables does not make sense since statics are supposed to be used only within one module. + */ +#define STATICDEF GBLDEF +#define STATICFNDCL extern +#define STATICFNDEF + +/* INTPTR_T is an integer that has the same length as a pointer on each platform. Its basic use is for arithmetic + or generic parameters. For all platforms except Tru64/VMS (alpha platforms), the [U]INTPTR_T types will be + equivalenced to [u]intptr_t. But since this type is used for alignment and other checking, and since Tru64/VMS + (implemented as a 32 bit platform) unconditionally sets this type to its 8 char variant, on Tru64/VMS we will + explicitly make [U]INTPTR_T a 4 byte creature. +*/ +#if !defined(__alpha) +typedef intptr_t INTPTR_T; +typedef uintptr_t UINTPTR_T; +#else +typedef int INTPTR_T; +typedef unsigned int UINTPTR_T; +#endif +/* The intszofptr_t type is defined to be basically the same size as an address on the platforms it runs on. So it + is the same size as INTPTR_T without the connotation of being a pointer. This is used in places where size_t + or ssize_t would normally be used except they can't be used because they are the wrong size on Alpha systems. + Classic usage is in places where need consistant integer and pointer sized elements like constructed parameter + lists or other arrays. +*/ +typedef INTPTR_T intszofptr_t; +typedef UINTPTR_T uintszofptr_t; + +#ifdef GTM64 +# define USER_STACK_SIZE 8192 +# define GTM64_ONLY(X) X +# define NON_GTM64_ONLY(X) +# define VA_ARG_TYPE long +# define VA_ARG_TYPE_BOOL int +# define GTM_IS_64BIT TRUE +# define GTM_BITNESS_THIS "64-bit" +# define GTM_BITNESS_OTHER "32-bit" +#else +# define USER_STACK_SIZE 4096 +# define GTM64_ONLY(X) +# define NON_GTM64_ONLY(X) X +# define VA_ARG_TYPE int +# define VA_ARG_TYPE_BOOL int +# define GTM_IS_64BIT FALSE +# define GTM_BITNESS_THIS "32-bit" +# define GTM_BITNESS_OTHER "64-bit" +#endif /* GTM64 */ + +#ifdef __CYGWIN__ +# define CYGWIN_ONLY(X) X +#else +# define CYGWIN_ONLY(X) +#endif + +#ifdef __linux__ +# define LINUX_ONLY(X) X +# define NON_LINUX_ONLY(X) +#else +# define LINUX_ONLY(X) +# define NON_LINUX_ONLY(X) X +#endif + +#ifdef __MVS__ +# define ZOS_ONLY(X) X +#else +# define ZOS_ONLY(X) +#endif + +#ifdef Linux390 +# define Linux390_ONLY(X) X +#else +# define Linux390_ONLY(X) +#endif + +#if !defined(__alpha) && !defined(__sparc) && !defined(__hpux) && !defined(mips) && !defined(__ia64) +# define UNALIGNED_ACCESS_SUPPORTED +#endif + +#if defined(__ia64) +# define IA64_ONLY(X) X +# define NON_IA64_ONLY(X) +# ifdef DEBUG +# define IA64_DEBUG_ONLY(X) X +# else +# define IA64_DEBUG_ONLY(X) +# endif /* DEBUG */ +#else +# define IA64_ONLY(X) +# define NON_IA64_ONLY(X) X +# define IA64_DEBUG_ONLY(X) +#endif/* __ia64 */ + +#if defined(__ia64) || defined(__MVS__) +# define INTCAST(X) ((int)(X)) +# define UINTCAST(X) ((uint4)(X)) +# define STRLEN(X) ((int)(strlen(X))) +# define USTRLEN(X) ((unsigned int)(strlen(X))) +# define OFFSETOF(X,Y) ((int)(offsetof(X,Y))) +#else +# define INTCAST(X) X +# define UINTCAST(X) X +# define STRLEN(X) strlen(X) +# define USTRLEN(X) strlen(X) +# define OFFSETOF(X,Y) offsetof(X,Y) +#endif + +/* macro to check that the OFFSET & SIZE of TYPE1.MEMBER1 is identical to that of TYPE2.MEMBER2 */ +#define IS_OFFSET_AND_SIZE_MATCH(TYPE1, MEMBER1, TYPE2, MEMBER2) \ + (SIZEOF(((TYPE1 *)NULL)->MEMBER1) == SIZEOF(((TYPE2 *)NULL)->MEMBER2)) \ + && (OFFSETOF(TYPE1, MEMBER1) == OFFSETOF(TYPE2, MEMBER2)) + +#define IS_OFFSET_MATCH(TYPE1, MEMBER1, TYPE2, MEMBER2) (OFFSETOF(TYPE1, MEMBER1) == OFFSETOF(TYPE2, MEMBER2)) + +#define ARRAYSIZE(arr) SIZEOF(arr)/SIZEOF(arr[0]) /* # of elements defined in the array */ +#define ARRAYTOP(arr) (&arr[0] + ARRAYSIZE(arr)) /* address of the TOP of the array (first byte AFTER array limits). + * use &arr[0] + size instead of &arr[size] to avoid compiler warning. + */ +#ifdef __x86_64__ +#define X86_64_ONLY(x) x +#define NON_X86_64_ONLY(x) +#else +#define X86_64_ONLY(x) +#define NON_X86_64_ONLY(x) x +#endif /* __x86_64__ */ + +#if defined(__i386) || defined(__x86_64__) || defined(__ia64) || defined(__MVS__) || defined(Linux390) +#define NON_RISC_ONLY(x) x +#define RISC_ONLY(x) +#elif defined(__sparc) || defined(_AIX) || defined(__hppa) || defined(__alpha) +#define RISC_ONLY(x) x +#define NON_RISC_ONLY(x) +#endif + + +#ifdef _AIX +# define AIX_ONLY(X) X +#else +# define AIX_ONLY(X) +#endif + +#ifdef __sparc +# define SPARC_ONLY(X) X +#else +#define SPARC_ONLY(X) +#endif +#define BITS_PER_UCHAR 8 /* note, C does not require this to be 8, see for definitions of CHAR_BIT and UCHAR_MAX */ + +#define MAXPOSINT4 ((int4)0x7fffffff) +#define MAX_DIGITS_IN_INT 10 /* maximum number of decimal digits in a 4-byte integer */ +#define MAX_DIGITS_IN_INT8 20 /* maximum number of decimal digits in an 8-byte integer */ +#define MAX_HEX_DIGITS_IN_INT 8 /* maximum number of hexadecimal digits in a 4-byte integer */ +#define MAX_HEX_DIGITS_IN_INT8 16 /* maximum number of hexadecimal digits in an 8-byte integer */ + +#define MAX_DIGITS_IN_EXP 2 /* maximum number of decimal digits in an exponent */ +#define MAX_HOST_NAME_LEN 256 +#define MAX_LONG_IN_DOUBLE 0xFFFFFFFFFFFFF /*Max Fraction part in IEEE double format*/ + +#ifndef _AIX +# ifndef __sparc + typedef int boolean_t; +# endif +#endif +typedef char bool; +typedef unsigned char mreg; +typedef int4 mint; + +#define PRE_V5_MAX_MIDENT_LEN 8 /* Maximum length of an mident/mname before GT.M V5.0 */ +typedef struct +{ /* The old mident structure used before V50FT01 */ + char c[PRE_V5_MAX_MIDENT_LEN]; +} pre_v5_mident; + +#define MAX_MIDENT_LEN 31 /* Maximum length of an mident/mname */ +typedef mstr mident; +typedef struct +{ /* Although we use 31 chars, the extra byte is to keep things aligned */ + char c[MAX_MIDENT_LEN + 1]; +} mident_fixed; +#define mid_len(name) strlen(&(name)->c[0]) /* callers of mid_len should include gtm_string.h as well */ + +#define MIDENT_CMP(x,y,result) MSTRP_CMP(x, y, result) +#define MIDENT_EQ(x,y) MSTR_EQ(x, y) + +#ifdef INT8_NATIVE +# define NATIVE_WSIZE 8 +#else +# define NATIVE_WSIZE 4 +#endif + +/* Maximum length of entry reference of the form "label+offset^routine" */ +#define MAX_ENTRYREF_LEN (2 * MAX_MIDENT_LEN + MAX_DIGITS_IN_INT + STR_LIT_LEN("+^")) + +/* M name entry used in various structures - variable table (rtnhdr.h), hash table (hashtab_def.h) and + * global variable (gv_namehead in gdsfhead.h) */ +typedef struct +{ + mident var_name; /* var_name.addr points to the actual variable name */ + uint4 hash_code; /* hash (scrambled) value of the variable name text */ + boolean_t marked; /* Used when in hashtable entry for xkill (at least) */ +} mname_entry; + +/* The M stack frame on all platforms that follow pv-based linkage model (alpha model) + * contains a pointer to the base of routine's literal section. All such platforms + * must define HAS_LITERAL_SECT so that the routines that create a new stack frame + * initialize literal_ptr field apppropriately. + * + */ +#if defined(__alpha) || defined(_AIX) || defined(__hpux) || defined(__sparc) || defined(__MVS__) || (defined(__linux__) && \ + (defined(__ia64) || defined(__x86_64__) || defined(__s390__))) +# define HAS_LITERAL_SECT +#endif + +typedef long ulimit_t; /* NOT int4; the Unix ulimit function returns a value of type long */ + +/* Bit definitions for mval type (mvtype) */ +#define MV_NM 1 /* 0x0001 */ +#define MV_INT 2 /* 0x0002 + * Note: this bit is set for integers and non-integers with <= 3 digits after the decimal point */ +#define MV_NUM_MASK 3 /* 0x0003 (MV_NM | MV_INT) */ +#define MV_STR 4 /* 0x0004 */ +#define MV_NUM_APPROX 8 /* 0x0008 */ /* bit set implies value is guaranteed to be part number, part string */ +#define MV_CANONICAL 16 /* 0x0010 + * Note: this bit is set currently only for mvals corresponding to local variable subscripts + * in lv_tree.c/lv_tree.h. This bit should not be examined/relied-upon anywhere outside lv_tree.c + */ +#define MV_SYM 32 /* 0x0020 */ +#define MV_SUBLIT 64 /* 0x0040 */ +#define MV_RETARG 128 /* 0x0080 */ +#define MV_UTF_LEN 256 /* 0x0100 */ +#define MV_ALIASCONT 512 /* 0x0200 */ + +#define MV_INT_OFF ~(MV_INT) /* Mask to turn off MV_INT */ +#define MV_STR_OFF ~(MV_STR) /* Mask to turn off MV_STR */ +#define MV_CANONICAL_OFF ~(MV_CANONICAL) /* Mask to turn off MV_CANONICAL */ +#define MV_UTF_LEN_OFF ~(MV_UTF_LEN) /* Mask to turn off MV_UTF_LEN */ + +#define MV_EXT_NUM_MASK (MV_NM | MV_INT | MV_CANONICAL) + +/* Special definition used when an xnew'd lv_val is moved from a popped symtab to an earlier + * one so it can be preserved. This flag marks the lv_val as a pointer to the new symtab so + * multiple references to it can be resolved. + */ +#define MV_LVCOPIED 0xf000 + +/* A few more special definitions */ +#define MV_LV_TREE 0xf001 /* An "lvTree" structure has its "ident" field set to this special value */ + +#define MV_XBIAS 62 +#define MV_XZERO 0 +#define MV_BIAS 1000 +#define MV_BIAS_PWR 3 + +#define NR_REG 16 +#ifndef TRUE +# define TRUE 1 +#endif +#ifndef FALSE +# define FALSE 0 +#endif +#ifndef NULL +# define NULL ((void *) 0) +#endif +#define NUL 0x00 +#define SP 0x20 +#define DEL 0x7f + +#define MAX_STRLEN_32K 32767 +/* MAX_STRLEN for local variable is changed from 32767 to 1048576 (1 MB) */ +#define MAX_STRLEN (1 * 1024 * 1024) /*maximum GT.M string size (1 MB)*/ +#define MAX_DBSTRLEN (32 * 1024 - 1) /* Maximum database string size */ +/* Initial buffer size allocated for a GT.M string which can geometrically be increased upto the size enough to fit in MAX_STRLEN */ +#define MAX_STRBUFF_INIT (32 * 1024) + +#define MAX_NUM_SIZE 64 +#define MAX_FORM_NUM_SUBLEN 128 /* this is enough to hold the largest numeric subscript */ +#define PERIODIC_FLUSH_CHECK_INTERVAL (30 * 1000) +#define MAX_ARGS 256 /* in formallist */ + +#define MAX_KEY_SZ 255 /* maximum database key size */ +/* The macro ZWR_EXP_RATIO returns the inflated length when converting the internal subscript + * representation (byte) length to ZWR representation. + * In "M" mode, + * Worst case is every other character is non-graphic. e.g. $C(128)_"A"_$C(128). + * In "UTF-8" mode, + * Worst case is with a non-graphic character and every other character is an illegal + * character. Here are the expansion ratios for different ranges of characters. + * ------------------------------------------------------------------------------ + * Byte pattern max. expanded input byte ratio + * output length length + * ------------------------------------------------------------------------------ + * $C(129)_$ZCH(128)_ 18 2 9 + * $C(1536)_$ZCH(128)_ 19 3 7 + * $C(65279)_$ZCH(128)_ 20 4 5 + * $C(917585)_$ZCH(128)_ 21 5 6 + * $C(1114111)_$ZCH(128)_ 22 5 6 + * ------------------------------------------------------------------------------ + * To cover cases of odd numbers of characters, add some buffer. + * + * MAX_ZWR_KEY_SZ, on the other hand, needs to be a compile-time constant since it's used in + * temporary allocation on the stack + */ +GBLREF boolean_t gtm_utf8_mode; +#ifdef UNICODE_SUPPORTED +# define ZWR_EXP_RATIO(X) ((!gtm_utf8_mode) ? (((X) * 6 + 7)) : ((X) * 9 + 11)) +# define MAX_ZWR_KEY_SZ (MAX_KEY_SZ * 9 + 11) +# define MAX_ZWR_EXP_RATIO 9 +#else +# define ZWR_EXP_RATIO(X) ((X) * 6 + 7) +# define MAX_ZWR_KEY_SZ (MAX_KEY_SZ * 6 + 7) +# define MAX_ZWR_EXP_RATIO 6 +#endif + +#define MAX_SYSERR 1000000 + +unsigned char *n2s(mval *mv_ptr); +char *s2n(mval *u); +mval *underr (mval *start, ...); + +#ifdef DEBUG +# define DBG_ASSERT(X) assert(X), +#else +# define DBG_ASSERT(X) +#endif + +/* Use the "D" format of these MV_FORCE macros only in those places where there is no possibility of the input being undefined */ +#define MV_FORCE_STR(X) (MV_FORCE_DEFINED(X), MV_FORCE_STRD(X)) +#define MV_FORCE_STRD(X) (DBG_ASSERT(MV_DEFINED(X)) (0 == ((X)->mvtype & MV_STR)) ? n2s(X) : NULL) +#define MV_FORCE_NUM(X) (MV_FORCE_DEFINED(X), MV_FORCE_NUMD(X)) +#define MV_FORCE_NUMD(X) (DBG_ASSERT(MV_DEFINED(X)) (0 == ((X)->mvtype & MV_NM )) ? s2n(X) : NULL) +#define MV_FORCE_BOOL(X) (MV_FORCE_NUM(X), (X)->m[1] ? TRUE : FALSE) +#define MV_FORCE_INT(M) (MV_FORCE_DEFINED(M), MV_FORCE_INTD(M)) +#define MV_FORCE_INTD(M) (DBG_ASSERT(MV_DEFINED(M)) (M)->mvtype & MV_INT ? (M)->m[1]/MV_BIAS : mval2i(M)) +#define MV_FORCE_UMVAL(M,I) (((I) >= 1000000) ? i2usmval((M),(int)(I)) : \ + (void)( (M)->mvtype = MV_NM | MV_INT , (M)->m[1] = (int)(I)*MV_BIAS )) +#define MV_FORCE_MVAL(M,I) (((I) >= 1000000 || (I) <= -1000000) ? i2mval((M),(int)(I)) : \ + (void)( (M)->mvtype = MV_NM | MV_INT , (M)->m[1] = (int)(I)*MV_BIAS )) +#define MV_FORCE_DEFINED(X) ((!MV_DEFINED(X)) ? (X) = underr(X) : (X)) +/* Note MV_FORCE_CANONICAL currently only used in op_add() when vars are known to be defined so no MV_FORCE_DEFINED() + macro has been added. If uses are added, this needs to be revisited. 01/2008 se +*/ +#define MV_FORCE_CANONICAL(X) ((((X)->mvtype & MV_NM) == 0 ? s2n(X) : 0 ) \ + ,((X)->mvtype & MV_NUM_APPROX ? (X)->mvtype &= MV_NUM_MASK : 0 )) +#define MV_IS_NUMERIC(X) (((X)->mvtype & MV_NM) != 0) +#define MV_IS_INT(X) (((X)->mvtype & MV_INT) != 0) /* returns TRUE if input has MV_INT bit set */ +#define MV_IS_TRUEINT(X, INTVAL_P) (isint(X, INTVAL_P)) /* returns TRUE if input is a true integer (no fractions) */ +#define MV_IS_STRING(X) (((X)->mvtype & MV_STR) != 0) +#define MV_DEFINED(X) (((X)->mvtype & (MV_STR | MV_NM)) != 0) +#define MV_IS_CANONICAL(X) (((X)->mvtype & MV_NM) ? (((X)->mvtype & MV_NUM_APPROX) == 0) : (boolean_t)val_iscan(X)) +#define MV_INIT(X) ((X)->mvtype = 0, (X)->fnpc_indx = 0xff) +#define MV_INIT_STRING(X, LEN, ADDR) ((X)->mvtype = MV_STR, (X)->fnpc_indx = 0xff, \ + (X)->str.len = INTCAST(LEN), (X)->str.addr = (char *)ADDR) + +/* The MVTYPE_IS_* macros are similar to the MV_IS_* macros except that the input is an mvtype instead of an "mval *". + * In the caller, use appropriate macro depending on available input. Preferable to use the MVTYPE_IS_* variant to avoid + * the (X)->mvtype dereference */ +#define MVTYPE_IS_NUMERIC(X) (0 != ((X) & MV_NM)) +#define MVTYPE_IS_INT(X) (0 != ((X) & MV_INT)) +#define MVTYPE_IS_NUM_APPROX(X) (0 != ((X) & MV_NUM_APPROX)) +#define MVTYPE_IS_STRING(X) (0 != ((X) & MV_STR)) + +/* DEFINE_MVAL_LITERAL is intended to be used to define a string mval where the string is a literal or defined with type + * "readonly". In other words, the value of the string does not change. Since we expect all callers of this macro to use + * ASCII literals, the MV_UTF_LEN bit is set in the type, and the character length is set to the same value as the byte length. + */ +#define DEFINE_MVAL_LITERAL(TYPE, EXPONENT, SIGN, LENGTH, ADDRESS, MANT_LOW, MANT_HIGH) \ + DEFINE_MVAL_COMMON(TYPE | MV_UTF_LEN, EXPONENT, SIGN, LENGTH, LENGTH, ADDRESS, MANT_LOW, MANT_HIGH) + +/* DEFINE_MVAL_STRING is intended to be used to define a string mval where the value of the string can change */ +#define DEFINE_MVAL_STRING(TYPE, EXPONENT, SIGN, LENGTH, ADDRESS, MANT_LOW, MANT_HIGH) \ + DEFINE_MVAL_COMMON(TYPE, EXPONENT, SIGN, 0, LENGTH, ADDRESS, MANT_LOW, MANT_HIGH) + +#ifdef VMS +#define DEFINE_MVAL_COMMON(TYPE, EXPONENT, SIGN, UTF_LEN, LENGTH, ADDRESS, MANT_LOW, MANT_HIGH) \ + {TYPE, EXPONENT, SIGN, 0xff, LENGTH, ADDRESS, MANT_LOW, MANT_HIGH} +#else +#ifdef BIGENDIAN +#ifdef UNICODE_SUPPORTED +#define DEFINE_MVAL_COMMON(TYPE, EXPONENT, SIGN, UTF_LEN, LENGTH, ADDRESS, MANT_LOW, MANT_HIGH) \ + {TYPE, SIGN, EXPONENT, 0xff, MANT_LOW, MANT_HIGH, UTF_LEN, LENGTH, ADDRESS} +#else +#define DEFINE_MVAL_COMMON(TYPE, EXPONENT, SIGN, UTF_LEN, LENGTH, ADDRESS, MANT_LOW, MANT_HIGH) \ + {TYPE, SIGN, EXPONENT, 0xff, MANT_LOW, MANT_HIGH, LENGTH, ADDRESS} +#endif +#else /* BIGENDIAN */ +#ifdef UNICODE_SUPPORTED +#define DEFINE_MVAL_COMMON(TYPE, EXPONENT, SIGN, UTF_LEN, LENGTH, ADDRESS, MANT_LOW, MANT_HIGH) \ + {TYPE, EXPONENT, SIGN, 0xff, MANT_LOW, MANT_HIGH, UTF_LEN, LENGTH, ADDRESS} +#else +#define DEFINE_MVAL_COMMON(TYPE, EXPONENT, SIGN, UTF_LEN, LENGTH, ADDRESS, MANT_LOW, MANT_HIGH) \ + {TYPE, EXPONENT, SIGN, 0xff, MANT_LOW, MANT_HIGH, LENGTH, ADDRESS} +#endif /* UNICODE */ +#endif /* BIGENDIAN */ +#endif /* VMS */ + +#define ASCII_MAX (unsigned char)0x7F +#define IS_ASCII(X) ((uint4)(X) <= ASCII_MAX) /* X can be greater than 255 hence the typecast to uint4 */ + +#ifdef UNICODE_SUPPORTED +# define MV_FORCE_LEN(X) ((!((X)->mvtype & MV_UTF_LEN)) \ + ? (utf8_len(&(X)->str), ((X)->mvtype |= MV_UTF_LEN), (X)->str.char_len) \ + : (X)->str.char_len) + +/* MV_FORCE_LEN_STRICT() is used to ensure that mval is valid in addition to computing the char_len. + * Note that the validation is always forced even if MV_UTF_LEN is set since the previously computed + * char_len might have been evaluated in VIEW "NOBADCHAR" setting. */ +# define MV_FORCE_LEN_STRICT(X) (((X)->str.char_len = UTF8_LEN_STRICT((X)->str.addr, (X)->str.len)), \ + ((X)->mvtype |= MV_UTF_LEN), (X)->str.char_len) + +# define MV_IS_SINGLEBYTE(X) (((X)->mvtype & MV_UTF_LEN) && ((X)->str.len == (X)->str.char_len)) +#else +# define MV_FORCE_LEN(X) ((X)->str.len) +# define MV_FORCE_LEN_STRICT(X) ((X)->str.len) +# define MV_IS_SINGLEBYTE(X) (TRUE) /* all characters are single-byte in non-Unicode platforms */ +#endif + +#define DISK_BLOCK_SIZE 512 +#define LOG2_DISK_BLOCK_SIZE 9 + +#define DIVIDE_ROUND_UP(VALUE, MODULUS) (((VALUE) + ((MODULUS) - 1)) / (MODULUS)) +#define DIVIDE_ROUND_DOWN(VALUE, MODULUS) ((VALUE) / (MODULUS)) +#define ROUND_UP(VALUE, MODULUS) (DIVIDE_ROUND_UP(VALUE, MODULUS) * (MODULUS)) +#define ROUND_DOWN(VALUE, MODULUS) (DIVIDE_ROUND_DOWN(VALUE, MODULUS) * (MODULUS)) + +#ifdef DEBUG +# define CHECKPOT(MODULUS) ((MODULUS) & ((MODULUS) - 1)) ? GTMASSERT, 0 : +# define BREAK_IN_PRO__CONTINUE_IN_DBG continue +# define DEBUG_ONLY(statement) statement +# define DEBUG_ONLY_COMMA(statement) statement, +# define PRO_ONLY(statement) +#else +# define CHECKPOT(MODULUS) +# define BREAK_IN_PRO__CONTINUE_IN_DBG break +# define DEBUG_ONLY(statement) +# define DEBUG_ONLY_COMMA(statement) +# define PRO_ONLY(statement) statement +#endif + +/* These are the analogs of the preceeding, but are more efficient when the MODULUS is a Power Of Two. + * One thing to watch for is that VALUE could be 8-byte and MODULUS could be 4-bytes. In that case, we + * want to return an 8-byte value. So need to typecast MODULUS to 8-bytes before we do "& ~(MODULUS -1)" + * or else that will be a 4-byte value and cause a bitwise & with an 8-byte value resulting in a truncated + * 8-byte return value (loss of high order bits). We choose sm_long_t to reflect that type as it is 8-bytes + * on the 64-bit platforms and 4-bytes on the 32-bit platforms. Choosing gtm_uint64_t unconditionally will + * make it 8-bytes on the 32-bit platforms too and result in warnings due to the 8-byte value eventually being + * truncated to 4-bytes by the caller after the return from the below macro. + */ +#define ROUND_UP2(VALUE, MODULUS) (CHECKPOT(MODULUS) ((VALUE) + ((MODULUS) - 1)) & ~(((sm_long_t)MODULUS) - 1)) +#define ROUND_DOWN2(VALUE, MODULUS) (CHECKPOT(MODULUS) (VALUE) & ~(((sm_long_t)MODULUS) - 1)) + +/* Length needed to pad out to a given power of 2 boundary */ +#define PADLEN(value, bndry) (int)(ROUND_UP2((sm_long_t)(value), bndry) - (sm_long_t)(value)) + +/* LOG2_OF_INTEGER returns the ceiling of log (base 2) of number */ +#define LOG2_OF_INTEGER(number, log2_of_number) \ +{ \ + int temp = (number) - 1; \ + for (log2_of_number = 0; 0 < temp; log2_of_number++) \ + temp = (temp) >> 1; \ +} + +#define CALLFROM LEN_AND_LIT(__FILE__), __LINE__ +void gtm_assert ( int file_name_len, char file_name[], int line_no); +#define GTMASSERT (gtm_assert(CALLFROM)) + +#ifdef UNIX +int rts_error(int argcnt, ...); +void dec_err(uint4 argcnt, ...); +#elif defined(VMS) +void dec_err(int4 msgnum, ...); +#else +#error unsupported platform +#endif +void stx_error(int in_error, ...); +void ins_errtriple(int4 in_error); + +int4 timeout2msec(int4 timeout); + +/* the RTS_ERROR_TEXT macro will stay till all existing references to it have been renamed to RTS_ERROR_{LITERAL,STRING} */ +#define RTS_ERROR_TEXT(STRING) LENGTH_AND_STRING(STRING) + +/* for those who prefer not remembering the order of the length and the literal/string in the rts_error command line */ +#define RTS_ERROR_LITERAL(LITERAL) LENGTH_AND_LITERAL(LITERAL) +#define RTS_ERROR_STRING(STRING) LENGTH_AND_STRING(STRING) + +/* the LITERAL version of the macro should be used over STRING whenever possible for efficiency reasons */ +#define STR_LIT_LEN(LITERAL) (SIZEOF(LITERAL) - 1) +#define LITERAL_AND_LENGTH(LITERAL) (LITERAL), (SIZEOF(LITERAL) - 1) +#define LENGTH_AND_LITERAL(LITERAL) (SIZEOF(LITERAL) - 1), (LITERAL) +#define STRING_AND_LENGTH(STRING) (STRING), (STRLEN((char *)(STRING))) +#define LENGTH_AND_STRING(STRING) (strlen((char *)(STRING))), (STRING) + +#define LEN_AND_LIT(LITERAL) LENGTH_AND_LITERAL(LITERAL) +#define LIT_AND_LEN(LITERAL) LITERAL_AND_LENGTH(LITERAL) +#define STR_AND_LEN(STRING) STRING_AND_LENGTH(STRING) +#define LEN_AND_STR(STRING) LENGTH_AND_STRING(STRING) + +#define MEMCMP_LIT(SOURCE, LITERAL) memcmp(SOURCE, LITERAL, SIZEOF(LITERAL) - 1) +#define MEMCPY_LIT(TARGET, LITERAL) memcpy(TARGET, LITERAL, SIZEOF(LITERAL) - 1) + +#define SET_PROCESS_EXITING_TRUE \ +{ \ + GBLREF int process_exiting; \ + GBLREF boolean_t hold_onto_locks; \ + \ + process_exiting = TRUE; \ + /* as we are about to exit, no point requiring crit \ + * to be held in case we held it until now (e.g. online \ + * mupip journal recover/rollback */ \ + hold_onto_locks = FALSE; \ +} + +/* Macro to copy a source string to a malloced area that is set to the destination pointer. + * Since it is possible that DST might have multiple pointer dereferences in its usage, we + * use a local pointer variable and finally assign it to DST thereby avoiding duplication of + * those pointer dereferences (one for the malloc and one for the strcpy). + * There are two macros depending on whether a string or literal is passed. + */ +#define MALLOC_CPY_STR(DST, SRC) \ +{ \ + char *mcs_ptr; \ + int mcs_len; \ + \ + mcs_len = STRLEN(SRC) + 1; \ + mcs_ptr = malloc(mcs_len); \ + memcpy(mcs_ptr, SRC, mcs_len); \ + DST = mcs_ptr; \ +} + +#define MALLOC_CPY_LIT(DST, SRC) \ +{ \ + char *mcs_ptr; \ + int mcs_len; \ + \ + mcs_len = SIZEOF(SRC); \ + mcs_ptr = malloc(mcs_len); \ + memcpy(mcs_ptr, SRC, mcs_len); \ + DST = mcs_ptr; \ +} + +#define MALLOC_INIT(DST, SIZ) \ +{ \ + void *lcl_ptr; \ + \ + lcl_ptr = malloc(SIZ); \ + memset(lcl_ptr, 0, SIZ); \ + DST = lcl_ptr; \ +} + +/* *********************************************************************************************************** */ +/* Frequently used len + str combinations in macro form. */ +/* *********************************************************************************************************** */ +#define DB_STR_LEN(reg) (reg)->dyn.addr->fname, (reg)->dyn.addr->fname_len +#define DB_LEN_STR(reg) (reg)->dyn.addr->fname_len, (reg)->dyn.addr->fname +#define REG_STR_LEN(reg) (reg)->rname, (reg)->rname_len +#define REG_LEN_STR(reg) (reg)->rname_len, (reg)->rname +#define JNL_STR_LEN(csd) (csd)->jnl_file_name, (csd)->jnl_file_len +#define JNL_LEN_STR(csd) (csd)->jnl_file_len, (csd)->jnl_file_name + +#define FAB_LEN_STR(fab) (fab)->fab$b_fns, (fab)->fab$l_fna +/* *********************************************************************************************************** */ + + +#ifdef DEBUG +/* Original debug code has been removed since it was superfluous and did not work on all platforms. SE 03/01 */ +# define SET_TRACEABLE_VAR(var,value) var = value; +#else +# define SET_TRACEABLE_VAR(var,value) var = value; +#endif + +/* If this is unix, we have a faster sleep for short sleeps ( < 1 second) than doing a hiber start. + * Take this chance to define UNIX_ONLY and VMS_ONLY macros. + */ +int m_usleep(int useconds); +#ifdef UNIX +# define SHORT_SLEEP(x) {assert(1000 > (x)); m_usleep((x) * 1000);} +#else +# define SHORT_SLEEP(x) hiber_start(x); +#endif + +/* The following "MSYNC" defines are for the MM access method + * NO_MSYNC -- minimum number of msyncs -- only in run down + * UNTARGETED_MSYNC -- msync the entire file + * TARGETED_MSYNC -- keep track of changed buffers and only msync them + * REGULAR_MSYNC -- do regular file I/O on the mapped file (ignoring the fact it is mapped) + * + * If none of the MSYNCs are explicitly defined, the ifdef and elif defined sequence will fall through + * to the else case, defining NO_MSYNC as the default. + */ +#ifdef UNIX +# define UNIX_ONLY(X) X +# define UNIX_ONLY_COMMA(X) X, +# if defined UNTARGETED_MSYNC +# define UNTARGETED_MSYNC_ONLY(X) X +# define NON_UNTARGETED_MSYNC_ONLY(X) +# define TARGETED_MSYNC_ONLY(X) +# define NON_TARGETED_MSYNC_ONLY(X) X +# define REGULAR_MSYNC_ONLY(X) +# define NON_REGULAR_MSYNC_ONLY(X) X +# define NO_MSYNC_ONLY(X) +# define NON_NO_MSYNC_ONLY(X) +# elif defined TARGETED_MSYNC +# define UNTARGETED_MSYNC_ONLY(X) +# define NON_UNTARGETED_MSYNC_ONLY(X) X +# define TARGETED_MSYNC_ONLY(X) X +# define NON_TARGETED_MSYNC_ONLY(X) +# define REGULAR_MSYNC_ONLY(X) +# define NON_REGULAR_MSYNC_ONLY(X) X +# define NO_MSYNC_ONLY(X) +# define NON_NO_MSYNC_ONLY(X) +# elif defined REGULAR_MSYNC +# define UNTARGETED_MSYNC_ONLY(X) +# define NON_UNTARGETED_MSYNC_ONLY(X) X +# define TARGETED_MSYNC_ONLY(X) +# define NON_TARGETED_MSYNC_ONLY(X) X +# define REGULAR_MSYNC_ONLY(X) X +# define NON_REGULAR_MSYNC_ONLY(X) +# define NO_MSYNC_ONLY(X) +# define NON_NO_MSYNC_ONLY(X) +# else +# define NO_MSYNC +# define UNTARGETED_MSYNC_ONLY(X) +# define NON_UNTARGETED_MSYNC_ONLY(X) +# define TARGETED_MSYNC_ONLY(X) +# define NON_TARGETED_MSYNC_ONLY(X) +# define REGULAR_MSYNC_ONLY(X) +# define NON_REGULAR_MSYNC_ONLY(X) +# define NO_MSYNC_ONLY(X) X +# define NON_NO_MSYNC_ONLY(X) +# endif +#else +# define UNIX_ONLY(X) +# define UNIX_ONLY_COMMA(X) +# define UNTARGETED_MSYNC_ONLY(X) +# define TARGETED_MSYNC_ONLY(X) +# define REGULAR_MSYNC_ONLY(X) +# define NON_UNTARGETED_MSYNC_ONLY(X) +# define NON_TARGETED_MSYNC_ONLY(X) +# define NON_REGULAR_MSYNC_ONLY(X) +# define NO_MSYNC_ONLY(X) +# define NON_NO_MSYNC_ONLY(X) +#endif + +/* HP-UX on PA-RISC and z/OS are not able to have dynamic file extensions while running in MM access mode + * HP-UX: + * All HP-UX before v3 (PA-RISC and 11i v1 and v2) have distinct memory map buffers and file system buffers with no simple + * way to map between them. To get around this problem the "Unified File Cache" was implemented in v3 for both Itanium + * and PA-RISC which solves things. The only way around the limitation in v1 and v2 would be to strategically place calls + * to "msync" throughout the code to keep the memory maps and file cache buffers in sync. This is too onerous a price + * to pay. + * z/OS: + * If multiple processes are accessing the same mapped file, and one process needs to extend/remap the file, + * all the other processes must also unmap the file. + * + * This same comment is in the test framework in set_gtm_machtype.csh. If this comment is updated, also update the other. + */ +#ifdef UNIX +# if !defined(__hppa) && !defined(__MVS__) +# define MM_FILE_EXT_OK +# else +# undef MM_FILE_EXT_OK +# endif +#endif + +#ifdef VMS +# define VMS_ONLY(X) X +# define VMS_ONLY_COMMA(X) X, +#else +# define VMS_ONLY(X) +# define VMS_ONLY_COMMA(X) +#endif + +#if (defined(UNIX) || defined(VMS)) +# define UNSUPPORTED_PLATFORM_CHECK +#else +# define UNSUPPORTED_PLATFORM_CHECK #error UNSUPPORTED PLATFORM +#endif + +/* Note the macros below refer to the UNIX Shared Binary Support. Because the + support is *specifically* for the Unix platform, "NON_USHBIN_ONLY()" will + also be true for VMS even though that platform does have shared binary support + (but it does not have Unix Shared Binary support). Use "NON_USHBIN_UNIX_ONLY()" + for UNIX platforms that do not support Shared Binaries. */ +#ifdef USHBIN_SUPPORTED +# define USHBIN_ONLY(X) X +# define NON_USHBIN_ONLY(X) +# define NON_USHBIN_UNIX_ONLY(X) +#else +# define USHBIN_ONLY(X) +# define NON_USHBIN_ONLY(X) X +# ifdef UNIX +# define NON_USHBIN_UNIX_ONLY(X) X +# else +# define NON_USHBIN_UNIX_ONLY(X) +# endif +#endif + +/* Unicode. Although most (all?) Unix platforms currently support Unicode, that may + not always be the case so a separate contingent is defined. +*/ +#ifdef UNICODE_SUPPORTED +# define UNICODE_ONLY(X) X +# define NON_UNICODE_ONLY(X) +#else +# define UNICODE_ONLY(X) +# define NON_UNICODE_ONLY(X) X +#endif + +/* Note: LONG_SLEEP *MUST*NOT* be the sleep() function because use of the sleep() function in + GT.M causes problems with GT.M's timers on some platforms. Specifically, the sleep() function + causes the SIGARLM handler to be silently deleted on Solaris systems (through Solaris 9 at least). + This leads to lost timer pops and has the potential for system hangs. + */ +#define LONG_SLEEP(x) hiber_start((x) * 1000) + +#define OS_PAGE_SIZE gtm_os_page_size +#define OS_PAGE_SIZE_DECLARE GBLREF int4 gtm_os_page_size; +#ifdef VMS +# define MAX_IO_BLOCK_SIZE DISK_BLOCK_SIZE +#else +# define MAX_IO_BLOCK_SIZE 65536 +#endif + +#ifndef GTM_INT64T_DEFINED +#define GTM_INT64T_DEFINED + typedef uint64_t gtm_uint64_t; + typedef int64_t gtm_int64_t; +#endif + +typedef INTPTR_T sm_off_t; + +/* HPPA latches (used by load_and_clear) must be 16 byte aligned. + * By allocating 16 bytes, the routines and macros used to access the latch can do the alignment. + * Since nothing else should follow to avoid cache threshing, this doesn't really waste space. + * Note that the additional space for this latch is only allocated on HPPA. All other platforms + * have a "sensible" compare-and-swap type lock using the first two words in the latch. + */ +typedef struct +{ + union + { + gtm_uint64_t pid_imgcnt; /* Combined atomic (unique) process id used on VMS */ + struct + { + volatile int4 latch_pid; /* (Usually) Process id of latch holder or LOCK_AVAILABLE. On VMS + this word may have other values. */ + volatile int4 latch_word; /* Extra word associated with lock (sometimes bci lock or image cnt + for VMS) */ + } parts; + } u; +#if defined __hppa + volatile int4 hp_latch_space[4]; /* Used for HP load_and_clear locking instructions per + HP whitepaper on spinlocks */ +#endif +} global_latch_t; +#define latch_image_count latch_word + +#define GLOBAL_LATCH_HELD_BY_US(latch) (process_id == (latch)->u.parts.latch_pid \ + VMS_ONLY(&& image_count == (latch)->u.parts.latch_image_count)) + +typedef union gtm_time8_struct +{ + time_t ctime; /* For current GTM code sem_ctime field corresponds to creation time */ + int4 filler[2]; /* Filler to ensure size is 2 words on all platforms */ +} gtm_time8; + +typedef uint4 gtm_time4_t; + +typedef struct +{ + sm_off_t fl; /* forward link - relative offset from beginning of this element to next element in queue */ + sm_off_t bl; /* backward link - relative offset from beginning of this element to previous element in queue */ +} que_ent; /* this structure is intended to be identical to the first two items in a cache_que_head */ + +typedef struct +{ + sm_off_t fl; /* forward link - relative offset from beginning of this element to next element in queue */ + sm_off_t bl; /* backward link - relative offset from beginning of this element to previous element in queue */ + global_latch_t latch; /* required for platforms without atomic operations to modify both fl and bl concurrently; + * unused on platforms with such instructions. */ +} que_head, cache_que_head, mmblk_que_head; + +#define IS_PTR_ALIGNED(ptr, ptr_base, elemSize) \ + (0 == ((((sm_uc_ptr_t)(ptr)) - ((sm_uc_ptr_t)(ptr_base))) % elemSize)) +#define IS_PTR_IN_RANGE(ptr, ptr_lo, ptr_hi) \ + (((sm_uc_ptr_t)(ptr) >= (sm_uc_ptr_t)(ptr_lo)) && ((sm_uc_ptr_t)(ptr) < (sm_uc_ptr_t)(ptr_hi))) + +#define IS_PTR_2BYTE_ALIGNED(ptr) (0 == (((uintszofptr_t)ptr) % 2)) +#define IS_PTR_4BYTE_ALIGNED(ptr) (0 == (((uintszofptr_t)ptr) % 4)) +#define IS_PTR_8BYTE_ALIGNED(ptr) (0 == (((uintszofptr_t)ptr) % 8)) + +#ifdef DB64 +# ifdef __osf__ +# pragma pointer_size(save) +# pragma pointer_size(long) +# else +# error UNSUPPORTED PLATFORM +# endif +#endif + +typedef que_ent * que_ent_ptr_t; +typedef que_head * que_head_ptr_t; + +#ifdef DB64 +# ifdef __osf__ +# pragma pointer_size(restore) +# endif +#endif + + /* Define 8-bytes as a structure containing 2-byte array of uint4s. Overlay this structure upon an 8 byte quantity for easy + * access to the lower or upper 4 bytes using lsb_index and msb_index respectively. + */ + typedef struct + { + uint4 value[2]; + } non_native_uint8; + +# define BIG_ENDIAN_MARKER 'B' /* to denote BIG-ENDIAN machine */ +# define LITTLE_ENDIAN_MARKER 'L' /* to denote LITTLE-ENDIAN machine */ + +#ifdef BIGENDIAN +# define msb_index 0 +# define lsb_index 1 +# define NODE_ENDIANNESS BIG_ENDIAN_MARKER +# define ENDIANTHIS "BIG" +# define ENDIANOTHER "LITTLE" +# define ENDIANTHISJUSTIFY " BIG" /* right justified */ +# define GTM_IS_LITTLE_ENDIAN FALSE +# define BIGENDIAN_ONLY(X) X +# define LITTLEENDIAN_ONLY(X) +#else +# define msb_index 1 +# define lsb_index 0 +# define NODE_ENDIANNESS LITTLE_ENDIAN_MARKER +# define ENDIANTHIS "LITTLE" +# define ENDIANOTHER "BIG" +# define ENDIANTHISJUSTIFY "LITTLE" /* right justified */ +# define GTM_IS_LITTLE_ENDIAN TRUE +# define BIGENDIAN_ONLY(X) +# define LITTLEENDIAN_ONLY(X) X +#endif + +#ifdef INT8_SUPPORTED + typedef gtm_uint64_t qw_num; + typedef gtm_uint64_t seq_num; /* Define 8-byte sequence number */ + typedef gtm_uint64_t token_num; /* Define 8-byte token number */ + typedef gtm_uint64_t qw_off_t; /* quad-word offset */ +# define DWASSIGNQW(A,B) (A)=(uint4)(B) +# define QWASSIGN(A,B) (A)=(B) +# define QWASSIGNDW(A,B) QWASSIGN((A),(gtm_uint64_t)(B)) +# define QWASSIGN2DW(A,B,C) QWASSIGN((A),(gtm_uint64_t)(B) << 32 | (C)) +# define QWADD(A,B,C) (A)=(B)+(C) +# define QWSUB(A,B,C) (A)=(B)-(C) +# define QWADDDW(A,B,C) (A)=(B)+(gtm_uint64_t)(C) +# define QWSUBDW(A,B,C) (A)=(B)-(gtm_uint64_t)(C) +# define QWINCRBY(A,B) (A)+=(B) +# define QWDECRBY(A,B) (A)-=(B) +# define QWINCRBYDW(A,B) (A)+=(gtm_uint64_t)(B) +# define QWDECRBYDW(A,B) (A)-=(gtm_uint64_t)(B) +# define QWMULBYDW(A,B,C) (A)=(B)*(C) +# define QWDIVIDEBYDW(A,B,Q,R) {(R)=(int)((A)%(B)); (Q)=(A)/(B);} +# define QWMODDW(A,B) ((A)%(B)) +# define QWLE(A,B) ((A)<=(B)) +# define QWLT(A,B) ((A)<(B)) +# define QWGE(A,B) ((A)>=(B)) +# define QWGT(A,B) ((A)>(B)) +# define QWEQ(A,B) ((A)==(B)) +# define QWNE(A,B) ((A)!=(B)) +# define INT8_PRINT(x) x +# define INT8_PRINTX(x) x +# define INT8_ONLY(x) x +#else + typedef struct non_native_uint8 qw_num; + typedef struct non_native_uint8 seq_num; + typedef struct non_native_uint8 token_num; + typedef struct non_native_uint8 qw_off_t; + +# define DWASSIGNQW(A,B) (A)=(B).value[lsb_index] +# define QWASSIGN(A,B) (A)=(B) +# define QWASSIGNDW(A,B) {(A).value[msb_index]=0; (A).value[lsb_index]=B;} +# define QWASSIGN2DW(A,B,C) {(A).value[msb_index]=B; (A).value[lsb_index]=C;} +# define QWADD(A,B,C) { \ + uint4 temp; \ + temp = (B).value[lsb_index]; \ + (A).value[lsb_index]=(B).value[lsb_index]+(C).value[lsb_index]; \ + (A).value[msb_index]=(B).value[msb_index]+(C).value[msb_index]; \ + if ((A).value[lsb_index] < temp) (A).value[msb_index]++; \ + } +# define QWSUB(A,B,C) { \ + uint4 temp; \ + temp = (B).value[lsb_index]; \ + (A).value[lsb_index]=(B).value[lsb_index]-(C).value[lsb_index]; \ + (A).value[msb_index]=(B).value[msb_index]-(C).value[msb_index]; \ + if ((A).value[lsb_index] > temp) (A).value[msb_index]--; \ + } +# define QWADDDW(A,B,C) { \ + uint4 temp; \ + temp = (B).value[lsb_index]; \ + (A).value[lsb_index]=(B).value[lsb_index]+C; \ + (A).value[msb_index]=(B).value[msb_index]; \ + if ((A).value[lsb_index] < temp) (A).value[msb_index]++; \ + } +# define QWSUBDW(A,B,C) { \ + uint4 temp; \ + temp = (B).value[lsb_index]; \ + (A).value[lsb_index]=(B).value[lsb_index]-(C); \ + (A).value[msb_index]=(B).value[msb_index]; \ + if ((A).value[lsb_index] > temp) (A).value[msb_index]--; \ + } +# define QWINCRBY(A,B) QWADD(A,A,B) +# define QWDECRBY(A,B) QWSUB(A,A,B) +# define QWINCRBYDW(A,B) QWADDDW(A,A,B) +# define QWDECRBYDW(A,B) QWSUBDW(A,A,B) + + /* B should be less than 64K for the QWDIDIVEBYDW, QWMODDW macros to work correctly */ + +# define QWMULBYDW(A,B,C) { \ + uint4 bh, bl, ch, cl, temp, temp1, temp2; \ + (A).value[msb_index] = (B).value[msb_index] * (C); \ + bl = (B).value[lsb_index] & 0x0000ffff; \ + bh = ((B).value[lsb_index] & 0xffff0000) >> 16; \ + cl = (C) & 0x0000ffff; \ + ch = ((C) & 0xffff0000) >> 16; \ + (A).value[msb_index] += bh * ch; \ + (A).value[lsb_index] = bl * cl; \ + temp = temp1 = bh * cl; \ + temp += bl * ch; \ + if (temp1 > temp) \ + (A).value[msb_index] += 0x00010000; \ + temp2 = (A).value[lsb_index]; \ + (A).value[lsb_index] += (temp & 0x0000ffff) << 16; \ + if ((A).value[lsb_index] < temp2) \ + (A).value[msb_index] ++; \ + (A).value[msb_index] += (temp & 0xffff0000) >> 16; \ + } +# define QWDIVIDEBYDW(A,B,Q,R) { \ + uint4 msbr, lsbq, twoq, twor; \ + (R) = (A).value[lsb_index] % (B); \ + lsbq = (A).value[lsb_index] / (B); \ + msbr = A.value[msb_index] % B; \ + (Q).value[msb_index] = (A).value[msb_index] / (B); \ + twoq = ((uint4)-1) / (B); \ + twor = (((uint4)-1) % (B) + 1) % (B); \ + if (0 == twor) \ + twoq++; \ + (Q).value[lsb_index] = lsbq; \ + (Q).value[lsb_index] += twoq * msbr; \ + if ((Q).value[lsb_index] < lsbq) \ + (Q).value[msb_index]++; \ + (R) = (R) + (twor * msbr) % (B); \ + lsbq = (Q).value[lsb_index]; \ + (Q).value[lsb_index] += (twor * msbr) / (B); \ + if ((R) > (B)) \ + { \ + (R) -= (B); \ + (Q).value[lsb_index]++; \ + } \ + if ((Q).value[lsb_index] < lsbq) \ + (Q).value[msb_index]++; \ + } +# define QWMODDW(A,B) ((((A).value[msb_index] % (B)) * (((uint4)-1) % (B) + 1) \ + + (A).value[lsb_index]) % (B)) +# define QWLE(A,B) ((A).value[msb_index] < (B).value[msb_index] || \ + ((A).value[msb_index] == (B).value[msb_index] \ + && (A).value[lsb_index] <= (B).value[lsb_index])) +# define QWLT(A,B) ((A).value[msb_index] < (B).value[msb_index] || \ + ((A).value[msb_index] == (B).value[msb_index] \ + && (A).value[lsb_index] < (B).value[lsb_index])) +# define QWGE(A,B) ((A).value[msb_index] > (B).value[msb_index] || \ + ((A).value[msb_index] == (B).value[msb_index] \ + && (A).value[lsb_index] >= (B).value[lsb_index])) +# define QWGT(A,B) ((A).value[msb_index] > (B).value[msb_index] || \ + ((A).value[msb_index] == (B).value[msb_index] \ + && (A).value[lsb_index] > (B).value[lsb_index])) +# define QWEQ(A,B) ((A).value[msb_index] == (B).value[msb_index] \ + && (A).value[lsb_index] == (B).value[lsb_index]) +# define QWNE(A,B) ((A).value[msb_index] != (B).value[msb_index] \ + || (A).value[lsb_index] != (B).value[lsb_index]) +# define INT8_FMT "%s" +# define INT8_FMTX "[0x%s]" +# define INT8_PRINT(x) (seq_num_ptr = i2ascl(seq_num_str, x), \ + seq_num_str[seq_num_ptr - &seq_num_str[0]] = '\0', seq_num_str) +# define INT8_PRINTX(x) (seq_num_ptrx = i2asclx(seq_num_strx, x), \ + seq_num_strx[seq_num_ptrx - &seq_num_strx[0]] = '\0', seq_num_strx) +# define INT8_ONLY(x) +#endif + +#define MAX_SEQNO ((seq_num)-1) /* actually 0xFFFFFFFFFFFFFFFF (max possible seqno) */ + + +/* The HPUX Itanium compiler is giving warnings whenever a cast is being done and there is a potential alignment change */ +/* The RECAST macro will eliminate these warnings by first casting to (void *) before the doing the ultimate cast */ + +#define RECAST(type) (type)(void_ptr_t) + +/* Define some basic types for shared memory (sm) access depending on whether the platform we are */ +/* using is capable of supporting 32 or 64 bit pointers or not. */ + +#if defined(DB64) || defined(GTM64) +# if defined(__osf__) && defined(__alpha) +# pragma pointer_size(save) +# pragma pointer_size(long) +# endif + typedef char *char_ptr_t; /* Define 64 bit pointer to char */ + typedef unsigned char *uchar_ptr_t; /* Define 64 bit pointer to unsigned char */ + typedef short *short_ptr_t; /* Define 64 bit pointer to short */ + typedef unsigned short *ushort_ptr_t; /* Define 64 bit pointer to unsigned short */ + typedef int4 *int_ptr_t; /* Define 64 bit pointer to int */ + typedef volatile int4 *vint_ptr_t; /* Define 64 bit pointer to volatile int */ + typedef uint4 *uint_ptr_t; /* Define 64 bit pointer to uint */ + typedef volatile uint4 *vuint_ptr_t; /* Define 64 bit pointer to volatile uint */ + typedef void *void_ptr_t; /* Define 64 bit pointer to void */ + typedef qw_num *qw_num_ptr_t; /* Define 64 bit pointer to qw_num */ + typedef latch_t *latch_ptr_t; /* Define 64 bit pointer to latch_t */ + typedef ulatch_t *ulatch_ptr_t; /* Define 64 bit pointer to ulatch_t */ + + /* Shared memory connotation */ + typedef char_ptr_t sm_c_ptr_t; /* Define 64 bit pointer to char */ + typedef uchar_ptr_t sm_uc_ptr_t; /* Define 64 bit pointer to unsigned char */ + typedef short_ptr_t sm_short_ptr_t; /* Define 64 bit pointer to short */ + typedef ushort_ptr_t sm_ushort_ptr_t; /* Define 64 bit pointer to unsigned short */ + typedef int_ptr_t sm_int_ptr_t; /* Define 64 bit pointer to int */ + typedef vint_ptr_t sm_vint_ptr_t; /* Define 64 bit pointer to volatile int */ + typedef uint_ptr_t sm_uint_ptr_t; /* Define 64 bit pointer to uint */ + typedef vuint_ptr_t sm_vuint_ptr_t; /* Define 64 bit pointer to volatile uint */ + typedef gtm_int64_t sm_long_t; /* Define 64 bit integer type */ + typedef gtm_uint64_t sm_ulong_t; /* Define 64 bit unsigned integer type */ + typedef global_latch_t *sm_global_latch_ptr_t; /* Define 64 bit pointer to hp_latch */ +# ifdef __osf__ +# pragma pointer_size(restore) +# endif + /* The macro FILL8DCL (explained below) is simple on a 64 bit system since all 64 bits + will be declared and used. */ +# define FILL8DCL(type,name,fillnum) type name +#else + typedef char *char_ptr_t; /* Define 32 bit pointer to char */ + typedef unsigned char *uchar_ptr_t; /* Define 32 bit pointer to unsigned char */ + typedef short *short_ptr_t; /* Define 32 bit pointer to short */ + typedef unsigned short *ushort_ptr_t; /* Define 32 bit pointer to unsigned short */ + typedef int4 *int_ptr_t; /* Define 32 bit pointer to int */ + typedef volatile int4 *vint_ptr_t; /* Define 32 bit pointer to volatile int */ + typedef uint4 *uint_ptr_t; /* Define 32 bit pointer to uint */ + typedef volatile uint4 *vuint_ptr_t; /* Define 32 bit pointer to volatile uint */ + typedef void *void_ptr_t; /* Define 32 bit pointer to void */ + typedef qw_num *qw_num_ptr_t; /* Define 32 bit pointer to qw_num */ + typedef latch_t *latch_ptr_t; /* Define 32 bit pointer to latch_t */ + typedef ulatch_t *ulatch_ptr_t; /* Define 32 bit pointer to ulatch_t */ + + /* Shared memory connotation */ + typedef char_ptr_t sm_c_ptr_t; /* Define 32 bit pointer to char */ + typedef uchar_ptr_t sm_uc_ptr_t; /* Define 32 bit pointer to unsigned char */ + typedef short_ptr_t sm_short_ptr_t; /* Define 32 bit pointer to short */ + typedef ushort_ptr_t sm_ushort_ptr_t; /* Define 32 bit pointer to unsigned short */ + typedef int_ptr_t sm_int_ptr_t; /* Define 32 bit pointer to int */ + typedef vint_ptr_t sm_vint_ptr_t; /* Define 32 bit pointer to volatile int */ + typedef uint_ptr_t sm_uint_ptr_t; /* Define 32 bit pointer to uint */ + typedef vuint_ptr_t sm_vuint_ptr_t; /* Define 32 bit pointer to volatile uint */ + typedef INTPTR_T sm_long_t; /* Define 32 bit integer type */ + typedef UINTPTR_T sm_ulong_t; /* Define 32 bit unsigned integer type */ + typedef global_latch_t *sm_global_latch_ptr_t; /* Define 32 bit pointer to hp_latch */ + /* The macro FILL8DCL is used (on a 32 bit system) to provide a filler area of 32 bits and + the actual 32 bit declared area. Whether the high order word or the low order word of + the 64 bit area should be filler depends on the endian mode of the machine. This macro + will be defined to take care of that for us. */ +# ifdef BIGENDIAN +# define FILL8DCL(type,name,fillnum) type fill##fillnum,name +# else +# define FILL8DCL(type,name,fillnum) type name,fill##fillnum +# endif +#endif + +/* Need to define a type for storing pointer differences */ +typedef INTPTR_T ptroff_t; + +/* Need to define a consistently sized off_t type. Some platforms it is 4 bytes, others it is + 4 or 8 bytes depending on flags. The following OFF_T macro is setup to allow the size of the + variable declared by it to always take up 8 bytes for alignment purposes. If the OFF_T_LONG + value is set, we will expect the size of 'off_t' to be 8 bytes. An assert will be placed in + gtm.c to verify this. */ + +#ifdef OFF_T_LONG +# define OFF_T(name,fillnum) off_t name +#else +# define OFF_T(name,fillnum) FILL8DCL(off_t,name,fillnum) +#endif + +/* Type for offsets in journal files. VMS uses uint4 to get a full 32 bit + offset for large journal files (OK since doesn't use lseek/etc. for IO.) */ + +#ifdef OFF_T_LONG +# define JNL_OFF_T(name,fillnum) off_t name +#else +# ifdef VMS +# define JNL_OFF_T(name,fillnum) FILL8DCL(uint4,name,fillnum) +# else +# define JNL_OFF_T(name,fillnum) FILL8DCL(off_t,name,fillnum) +# endif +#endif + +/* Need to define a consistently sized counter that is controlled by interlocks. The counter + will occupy 4 bytes in the file header but on some platforms (currently VAX and AXP VMS), + these counters need to be shorts whereas other platforms would realize a performance + improvement if they were 32 bits long. So we create another macro in the spirit of the + FILL8DCL macro above which will always give us a 32 byte entity but will pad a 2 byte + addressable entity if necessary. If not specified, the default is for 'short' counters. */ +# ifdef CNTR_WORD_32 +# define FILL4DCL(type,name,fillnum) type name +# define CNTR4DCL(name,fillnum) int4 name +# else +# ifdef BIGENDIAN +# define FILL4DCL(type,name,fillnum) type fill##fillnum,name +# else +# define FILL4DCL(type,name,fillnum) type name,fill##fillnum +# endif +# define CNTR4DCL(name,fillnum) FILL4DCL(short,name,fillnum) +# endif + +/* For machines with a cache line dependency for locks and such, + define a macro that can be used to generate padding such that + fields are in separate cache lines. Note that this macro should + *NOT* be used in the fileheader as its length expansion is platform + specific. It should only be used in internal shared memory + structures that are NOT otherwise placement sensitive. + A ; is included in the definition instead of when used since an extra ; + in a structure is not accepted by some compilers. +*/ + +#ifdef CACHELINE_SIZE +# define CACHELINE_PAD(fieldSize, fillnum) char fill_cacheline##fillnum[CACHELINE_SIZE - (fieldSize)]; +#else +# define CACHELINE_PAD(fieldSize, fillnum) +#endif + +/* In certain cases we need to conditionally do a CACHELINE pad. For those platforms that + have load-locked/store-conditional logic, counters that are incremented under interlock + need to have spacing so they do not interfere with each other. But platforms that do + NOT have this capability need the spacing on the actual latch used instead. Hence this + form of padding is conditional. +*/ +#if defined(__alpha) || defined(_AIX) +# define CACHELINE_PAD_COND(fieldSize, fillnum) CACHELINE_PAD(fieldSize, fillnum) +#else +# define CACHELINE_PAD_COND(fieldSize, fillnum) +#endif + +#define MEMCP(dst,src,start,count,limit){ \ + if (start+count > limit) \ + rts_error(VARLSTCNT(1) ERR_CPBEYALLOC); \ + else \ + memcpy(dst+start,src,count); \ +} + +#ifndef USING_ICONV +typedef enum +{ + NO_XLAT = 0, + EBCDIC_TO_ASCII, + ASCII_TO_EBCDIC +} gtm_iconv_t; +#define iconv_t gtm_iconv_t +#endif + +#ifdef _AIX +# define VSIG_ATOMIC_T sig_atomic_t +#else +# define VSIG_ATOMIC_T volatile sig_atomic_t +#endif + +/* For copying va_list items - Linux/390 needs __va_copy */ +#ifndef VAR_COPY +#define VAR_COPY(dst, src) dst = src +#endif + +#define NOLICENSE /* cheap way to obsolete it */ + +/* integer conversion functions */ +void i2hex(UINTPTR_T val, uchar_ptr_t dest, int len); +void i2hexl(qw_num val, uchar_ptr_t dest, int len); +void i2hex_blkfill(int num, uchar_ptr_t addr, int len); +void i2hexl_blkfill(qw_num num, uchar_ptr_t addr, int len); +int i2hex_nofill(int num, uchar_ptr_t addr, int len); +int i2hexl_nofill(qw_num num, uchar_ptr_t addr, int len); + +uchar_ptr_t i2ascl(uchar_ptr_t p, qw_num n); +uchar_ptr_t i2asclx(uchar_ptr_t p, qw_num n); +uchar_ptr_t i2asc(uchar_ptr_t p, unsigned int n); + +/* ascii conversion functions */ +int4 asc2i(uchar_ptr_t p, int4 len); +qw_num asc2l(uchar_ptr_t p, int4 len); +unsigned int asc_hex2i(char *p, int len); + +/* This macro converts an integer to a decimal string (a more efficient alternative to i2asc). + * It is used by format2zwr() which is called a lot during MUPIP EXTRACT (which can be time-consuming + * for a big database), hence the need to make it efficient. + */ +#define I2A(des, des_len, num) \ +{ \ + if ((unsigned)(num) < 1000) \ + { /* perform light-weight conversion of numbers upto 3 digits */ \ + int n1, n2; /* digits at the 10th and 100th decimal positions respectively */ \ + n2 = ((num) / 100) % 10; \ + if (0 != n2) \ + (des)[(des_len)++] = n2 + '0'; \ + n1 = ((num) / 10) % 10; \ + if (0 != n1 || 0 != n2) \ + (des)[(des_len)++] = n1 + '0'; \ + (des)[(des_len)++] = ((num) % 10) + '0'; \ + } else \ + des_len += (int)(i2asc((uchar_ptr_t)((des) + des_len), num) - (uchar_ptr_t)((des) + des_len)); \ +} + +/* The following is similar to I2A except that it updates the input pointer directly (no length parameter needed) */ +#define I2A_INLINE(des, num) \ +{ \ + if ((unsigned)(num) < 1000) \ + { /* perform light-weight conversion of numbers upto 3 digits */ \ + int n1, n2; /* digits at the 10th and 100th decimal positions respectively */ \ + n2 = ((num) / 100) % 10; \ + if (0 != n2) \ + *des++ = n2 + '0'; \ + n1 = ((num) / 10) % 10; \ + if (0 != n1 || 0 != n2) \ + *des++ = n1 + '0'; \ + *des++ = ((num) % 10) + '0'; \ + } else \ + des = (char *)i2asc((uchar_ptr_t)des, num); \ +} + +/* This macro converts a decimal string to a number (a more efficient alternative to asc2i). + * It is used by zwr2format() and str2gvargs which is called a lot during MUPIP LOAD (can be time-consuming for a big database). + */ +#define A2I(cp, end, num) \ +{ \ + unsigned char *cpbase = (unsigned char*)(cp); \ + char ch; \ + \ + for (num = 0; (cp) < (end) && ('0' <= (ch = *((unsigned char*)cp))) && ('9' >= ch); ++(cp)) \ + num = (num) * 10 + (ch - '0'); \ + if (cpbase == ((unsigned char*)cp)) \ + num = -1; \ +} + +void double2s(double *dp, mval *v); /* double conversion */ +int skpc(char c, int length, char *string); + +/* If the below declaration changes, corresponding changes in gtmxc_types.h needs to be done. */ +void *gtm_malloc(size_t size); +/* If the below declaration changes, corresponding changes in gtmxc_types.h needs to be done. */ +void gtm_free(void *addr); +int gtm_memcmp (const void *, const void *, size_t); +DEBUG_ONLY(void printMallocInfo(void);) +int is_equ(mval *u, mval *v); +char is_ident(mstr *v); +int val_iscan(mval *v); +void mcfree(void); +int4 getprime(int4 n); +void push_parm(UNIX_ONLY_COMMA(unsigned int totalcnt) int truth_value, ...); +void suspend(void); +mval *push_mval(mval *arg1); +void mval_lex(mval *v, mstr *output); + +#define ZTRAP_CODE 0x00000001 +#define ZTRAP_ENTRYREF 0x00000002 +#define ZTRAP_POP 0x00000004 +#define ZTRAP_ADAPTIVE (ZTRAP_CODE | ZTRAP_ENTRYREF) + +#define GTM_BYTESWAP_16(S) \ + ( (((S) & 0xff00) >> 8) \ + | (((S) & 0x00ff) << 8) \ + ) + +#define GTM_BYTESWAP_24(L) \ + ( (((L) & 0xff0000) >> 16) \ + | ((L) & 0x00ff00) \ + | (((L) & 0x0000ff) << 16) \ + ) + +#define GTM_BYTESWAP_32(L) \ + ( (((L) & 0xff000000) >> 24) \ + | (((L) & 0x00ff0000) >> 8) \ + | (((L) & 0x0000ff00) << 8) \ + | (((L) & 0x000000ff) << 24) \ + ) + +qw_num gtm_byteswap_64(qw_num num64); +#ifdef INT8_SUPPORTED +#define GTM_BYTESWAP_64(LL) \ + ( (((LL) & 0xff00000000000000ull) >> 56) \ + | (((LL) & 0x00ff000000000000ull) >> 40) \ + | (((LL) & 0x0000ff0000000000ull) >> 24) \ + | (((LL) & 0x000000ff00000000ull) >> 8) \ + | (((LL) & 0x00000000ff000000ull) << 8) \ + | (((LL) & 0x0000000000ff0000ull) << 24) \ + | (((LL) & 0x000000000000ff00ull) << 40) \ + | (((LL) & 0x00000000000000ffull) << 56) \ + ) +#else +#define GTM_BYTESWAP_64(LL) gtm_byteswap_64(LL) +#endif + +#define ZDIR_FORM_FULLPATH 0x00000000 +#define ZDIR_FORM_DIRECTORY 0x00000001 +#define IS_VALID_ZDIR_FORM(zdirform) (ZDIR_FORM_FULLPATH == (zdirform) || ZDIR_FORM_DIRECTORY == (zdirform)) + +#define MAXNUMLEN 128 /* from PV_N2S */ +#define CENTISECONDS 100 /* VMS lib$day returns 1/100s, we want seconds, use this factor to convert b/n the two */ +#define MINUTE 60 /* seconds in a minute */ +#define HOUR 3600 /* one hour in seconds 60 * 60 */ +#define ONEDAY 86400 /* seconds in a day */ +#define MILLISECS_IN_SEC 1000 /* millseconds in a second */ +#define MICROSEC_IN_SEC 1000000 /* microseconds in a second */ + +#define ASSERT_IN_RANGE(low, x, high) assert((low <= x) && (x <= high)) + +#if defined(VMS) +#define DAYS 6530 /* adjust VMS returned days by this amount; GTM zero time Dec 31, 1840, VMS zero time 7-NOV-1858 */ +#define VARLSTCNT1(CNT) VARLSTCNT(CNT) +#define PUT_SYS_ERRNO(SYS_ERRNO) SYS_ERRNO +#elif defined(UNIX) +#define DAYS 47117 /* adjust Unix returned days (seconds converted to days); Unix zero time 1970 */ +#define VARLSTCNT1(CNT) VARLSTCNT(CNT + 1) +#define PUT_SYS_ERRNO(SYS_ERRNO) 0, SYS_ERRNO +#else +#error Unsupported platform +#endif + +#define EXIT_NRM 0 +#define EXIT_INF 1 +#define EXIT_WRN 2 +#define EXIT_ERR 4 +#define EXIT_RDONLY 8 +#define EXIT_MASK 7 +#define MIN_FN_LEN 1 +#define MAX_FN_LEN 255 +#define V4_MAX_FN_LEN 255 /* required for dbcertify.h */ +#define MAX_TRANS_NAME_LEN 257 + +typedef uint4 jnl_tm_t; +typedef uint4 off_jnl_t; +typedef gtm_uint64_t gtm_off_t; + +#define MAXUINT8 ((gtm_uint64_t)-1) +#define MAXUINT4 ((uint4)-1) +#define MAXUINT2 ((unsigned short)-1) +#define MAXINT2 (MAXUINT2/2) + +/* On platforms that support native 8 byte operations (such as Alpha), an assignment to an 8 byte field is atomic. On other + * platforms, an 8 byte assignment is a sequence of 4 byte operations. On such platforms, use this macro to determine if the + * change from the current value to the new value provides a consistent view (entirely the pre read, or entirely the post read, + * and not in between). Any change that causes the most significant 4 bytes to differ can cause inconsistency. In such cases, it + * may be necessary to grab crit if modifying a shared field. + */ +#ifdef INT8_NATIVE +#define QWCHANGE_IS_READER_CONSISTENT(FROM8, TO8) (TRUE) +#else +/* Note: cannot use this macro when FROM8 or TO8 do not have an lvalue (eg. literal) */ +#define QWCHANGE_IS_READER_CONSISTENT(FROM8, TO8) (((non_native_uint8 *)&(FROM8))->value[msb_index] \ + == ((non_native_uint8 *)&(TO8))->value[msb_index]) +#endif + +#ifdef UNIX /* Replication instance file related structures */ + +/* The below macros and typedef are required in "repl_instance.h", "gtmsource.h", "gtmrecv.h" and "repl_msg.h". + * They are hence included in this common header file + */ +#define MAX_INSTNAME_LEN 16 /* Max Length of the replication instance name including terminating null character '\0' */ +#define NUM_GTMSRC_LCL 16 /* number of gtmsrc_lcl structures in the replication instance file */ +#define REPL_INST_HDR_SIZE (SIZEOF(repl_inst_hdr)) +#define GTMSRC_LCL_SIZE (SIZEOF(gtmsrc_lcl) * NUM_GTMSRC_LCL) /* size of the gtmsrc_lcl array */ +#define GTMSOURCE_LOCAL_SIZE (SIZEOF(gtmsource_local_struct) * NUM_GTMSRC_LCL) /* size of the gtmsource_local array */ +#define REPL_INST_TRIPLE_OFFSET (REPL_INST_HDR_SIZE + GTMSRC_LCL_SIZE) + +#define MAX_NODENAME_LEN 16 /* used by repl_instance.h. A similar macro JPV_LEN_NODE is defined in jnl.h */ + +typedef struct repl_triple_struct +{ /* Each repl_triple is uniquely defined by the following 3 fields */ + unsigned char root_primary_instname[MAX_INSTNAME_LEN];/* the root primary instance that generated the seqnos */ + seq_num start_seqno; /* the first seqno generated in this triple by the root primary */ + uint4 root_primary_cycle; /* a copy of the "root_primary_cycle" field in the instance file + * header of the root primary when it generated the seqno + * "start_seqno". This is needed to distinguish two invocations + * of the same instance + */ + /* Time fields have surrounding fillers so all fields in this structure (and hence the replication instance file + * which this is a part of) start at the same offset irrespective of whether time_t is 4-bytes or 8-bytes. + */ + int4 filler8bytealign_1; + time_t created_time; /* Time when triple was written to this file */ + NON_GTM64_ONLY(int4 filler8bytealign_2;) + unsigned char rcvd_from_instname[MAX_INSTNAME_LEN]; /* NULL if this triple was written by a source server (i.e. this + * instance was a root primary then). Non-NULL if this was written + * by the update process (on receipt of a REPL_NEW_TRIPLE record). + * In this case this field holds the instance name of the immediate + * primary that sent this REPL_NEW_TRIPLE record. + */ + unsigned char filler_64[8]; /* for future expansion */ +} repl_triple; + +#endif /* Replication instance file related structures */ + +/* Enumerator codes for supported CHSETs in GT.M */ +typedef enum +{ + CHSET_M, + CHSET_UTF8, + CHSET_UTF16, + CHSET_UTF16LE, + CHSET_UTF16BE, + CHSET_ASCII, + CHSET_EBCDIC, + CHSET_BINARY, + CHSET_MAX_IDX_ALL /* maximum number of CHSETs supported */ +} gtm_chset_t; + +#define CHSET_UTF_MIN CHSET_UTF8 +#define CHSET_UTF_MAX CHSET_UTF16BE +#define CHSET_MAX_IDX CHSET_ASCII /* max true CHSETs */ + +#define IS_UTF16_CHSET(chset) ((CHSET_UTF16 == (chset)) || (CHSET_UTF16LE == (chset)) || (CHSET_UTF16BE == (chset))) +#define IS_UTF_CHSET(chset) ((CHSET_UTF_MIN <= (chset)) && (CHSET_UTF_MAX >= (chset))) + +#define CHK_BOUNDARY_ALIGNMENT(pointer) (((UINTPTR_T)pointer) & (SIZEOF(UINTPTR_T) - 1)) +#if defined(__ia64) || defined(__i386) || defined(__x86_64__) || defined(__sparc) || defined(_AIX) || defined(__MVS__) \ + || defined(__s390__) +#define GTM_CRYPT +#define GTMCRYPT_ONLY(X) X +#else +#define GTMCRYPT_ONLY(X) +#endif +#define GTMCRYPT_HASH_LEN 64 +#define GTMCRYPT_HASH_HEX_LEN GTMCRYPT_HASH_LEN * 2 +#define GTMCRYPT_RESERVED_HASH_LEN 256 +#define GET_HASH_IN_HEX(in, out, len) \ +{ \ + int i; \ + \ + assert(0 == len % 2); \ + for (i = 0; i < len; i+=2) \ + SPRINTF((char *)out + i, "%02X", (unsigned char)in[i/2]); \ +} + +#ifdef UNIX +# define GTM_SNAPSHOT +# define NON_GTM_SNAPSHOT_ONLY(X) +# define GTM_SNAPSHOT_ONLY(X) X +#else +# define NON_GTM_SNAPSHOT_ONLY(X) X +# define GTM_SNAPSHOT_ONLY(X) +#endif + +/* Currently triggers are supported only on Unix */ +#if defined(UNIX) && !defined(__hppa) /* triggers not supported on HPUX-HPPA */ +# define GTM_TRIGGER +# define GTMTRIG_ONLY(X) X +# define NON_GTMTRIG_ONLY(X) +# define GTMTRIG_DBG_ONLY(X) DEBUG_ONLY(X) +# define GTM_TRIGGER_DEPTH_MAX 127 /* Maximum depth triggers can nest */ +#else +# define GTMTRIG_ONLY(X) +# define NON_GTMTRIG_ONLY(X) X +# define GTMTRIG_DBG_ONLY(X) +#endif + +/* A type definition to hold a range of numbers */ +typedef struct gtm_num_range_struct +{ + uint4 min; /* included in range */ + uint4 max; /* included in range */ +} gtm_num_range_t; + +/* Debug FPRINTF with pre and post requisite flushing of appropriate streams */ +#ifndef DBGFPF +# define DBGFPF(x) {flush_pio(); FPRINTF x; fflush(stderr); fflush(stdout);} +#endif + +/* Settings for lv_null_subs */ +enum +{ + LVNULLSUBS_FIRST = -1, /* So _NO is 0 to match existing values */ + LVNULLSUBS_NO, /* No null LV subscripts in SET type cases */ + LVNULLSUBS_OK, /* Null LV subscripts are allowed */ + LVNULLSUBS_NEVER, /* LVNULLSUBS_NO plus LV subscripts prohibited in $DATA, $GET, $ORDER, $QUERY, KILL, etc */ + LVNULLSUBS_LAST +}; +#define MAX_GVSUBSCRIPTS 32 +#define MAX_LVSUBSCRIPTS 32 +#define MAX_INDSUBSCRIPTS 32 +#define MAX_FOR_STACK 32 + +#endif /* MDEF_included */ diff --git a/sr_port/mdq.h b/sr_port/mdq.h new file mode 100644 index 0000000..0f9f824 --- /dev/null +++ b/sr_port/mdq.h @@ -0,0 +1,38 @@ +/**************************************************************** + * * + * Copyright 2001, 2007 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifdef DEBUG_TRIPLES +# define CHKTCHAIN(x) chktchain(x) +#else +# define CHKTCHAIN(x) +#endif + +/* All the following macros assume a doubly-linked list using "fl" and "bl" */ + +#define dqloop(q,n,i) for (i = (q)->n.fl ; i != (q) ; i = (i)->n.fl) + +#define dqinit(q,n) ((q)->n.fl = (q)->n.bl = q) + +/* delete one element "x" from the doubly linked list */ +#define dqdel(x,n) ((x)->n.bl->n.fl = (x)->n.fl , (x)->n.fl->n.bl = (x)->n.bl) + +/* delete a doubly-linked list of elements from "x->n.fl" to "y->n.bl" (i.e. everything in between "x" and "y" excluding them) */ +#define dqdelchain(x,y,n) { ((x)->n.fl = (y), (y)->n.bl = (x)); CHKTCHAIN(x); CHKTCHAIN(y); } + +/* Insert one element "x" in between "q" and "q->n.fl" */ +#define dqins(q,n,x) ((x)->n.fl = (q)->n.fl, (x)->n.bl =(q), (q)->n.fl=(x), ((x)->n.fl)->n.bl=(x)) + +/* Insert a doubly-linked list of elements from "n->q.fl" to "n->q.bl" in between "o" and "o->q.fl" */ +#define dqadd(o,n,q) \ +{ \ + ((o)->q.fl->q.bl=(n)->q.bl,(n)->q.bl->q.fl=(o)->q.fl,(o)->q.fl=(n)->q.fl,(n)->q.fl->q.bl=(o)); \ + CHKTCHAIN(o); \ +} diff --git a/sr_port/mem_access.h b/sr_port/mem_access.h new file mode 100644 index 0000000..8163f98 --- /dev/null +++ b/sr_port/mem_access.h @@ -0,0 +1,18 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef __MEM_ACCESS_H__ +#define __MEM_ACCESS_H__ + +void set_noaccess(unsigned char *na_page[], unsigned char *prvprt); +void reset_access(unsigned char *na_page[], unsigned char oldprt); + +#endif diff --git a/sr_port/memcoherency.h b/sr_port/memcoherency.h new file mode 100644 index 0000000..3bd002f --- /dev/null +++ b/sr_port/memcoherency.h @@ -0,0 +1,176 @@ +/**************************************************************** + * * + * Copyright 2003, 2010 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef MEMCOHERENCY_H_INCLUDED +#define MEMCOHERENCY_H_INCLUDED + +/* for Uniprocessor systems, no need for "memory barrier" as memory is always coherent. + * But almost always we expect to be running on a multi-processor system so we want to avoid the cost + * of the if check and do the memory barrier ALWAYS. + */ + +#ifdef __alpha + +#include + +/* Read Alpha Architecture Reference Manual, edited by Richard L Sites, + * Chapter "System Architecture and Programming Implications" for memory + * coherency issues and behavior of "mb" instruction (memory barrier) + */ + +/* NOTES about Alpha (pp. 5-20, section 5.6.5 Implications for Hardware, Chapter 5 System Architecture and Programming + * Implications, Alpha Architecture Reference Manual, Edited by Richard L Sites + * + * MB and IMB force all preceding writes to at least reach their respective coherency points. This does not mean that + * main-memory writes have been done, just that the order of the eventual writes is committed. MB and IMB also force all + * queued cache invalidates to be delivered to the local caches before starting any subsequent reads (that may otherwise + * cache hit on stale data) or writes (that may otherwise write the cache, only to have the write effectively overwriiten + * by a late-delivered invalidate) + */ +#define SHM_WRITE_MEMORY_BARRIER asm("mb") + +#define SHM_READ_MEMORY_BARRIER SHM_WRITE_MEMORY_BARRIER /* same MB instruction for both read and write barriers */ + +#ifdef __vms +#define SECSHR_SHM_WRITE_MEMORY_BARRIER asm("mb") +#define SECSHR_SHM_READ_MEMORY_BARRIER SECSHR_SHM_WRITE_MEMORY_BARRIER +#endif /* __vms */ + +#elif defined(POWER) || defined(PWRPC) /* GT.M defines POWER and PWRPC if _AIX is defined, see sr_rs6000/mdefsp.h */ + +/* Refer to article "POWER4 and shared memory synchronization by R. William Hay and Gary R. Hook" available at + * http://www-106.ibm.com/developerworks/eserver/articles/power4_mem.html + */ + +/* prototypes */ +void do_sync(void); +void do_lwsync(void); +void do_eieio(void); +void do_isync(void); + +/* The machine codes were fetched from http://www-106.ibm.com/developerworks/eserver/articles/powerpc.html */ + +/* sync : Creates a memory barrier. On a given processor, any load or store instructions ahead of the sync instruction + * in the program sequence must complete their accesses to memory first, and then any load or store instructions after + * sync can begin + */ +#pragma mc_func do_sync{"7c0004ac"} +#pragma reg_killed_by do_sync + +/* lwsync : Creates a memory barrier that provides the same ordering function as the sync instruction, except that a + * load caused by an instruction following the lwsync may be performed before a store caused by an instruction that + * precedes the lwsync, and the ordering does not apply to accesses to I/O memory (memory-mapped I/O). + * lwsync is a new variant of the sync instruction and is interpreted by older processors as a sync. The instruction, + * as its name implies, has much less performance impact than sync, and is recommended for syncrhonisation of most + * memory (but not I/O) references. + */ +#pragma mc_func do_lwsync{"7c2004ac"} +#pragma reg_killed_by do_lwsync + +/* eieio : Creates a memory barrier that provides the same ordering function as the sync instruction except that + * ordering applies only to accesses to I/O memory + */ +#pragma mc_func do_eieio{"7c0006ac"} +#pragma reg_killed_by do_eieio + +/* isync : Causes the processor to discard any prefetched (and possibly speculatively executed) instructions and + * refetch the next following instructions. It is used in locking code (e.g. __check_lock()) to ensure that no + * loads following entry into a critical section can access data (because of aggressive out-of-order and speculative + * execution in the processor) before the lock is acquired. + */ +#pragma mc_func do_isync{"4c00012c"} +#pragma reg_killed_by do_isync + +#define SHM_WRITE_MEMORY_BARRIER \ +{ /* Ensure that code does not rely on ordering of "loads" following lwsync in programming sequence to occur */ \ + /* after "stores" before lwsync. Use do_sync() if such ordering is required. Replication code (t_end.c, */ \ + /* tp_end.c) do not rely on store-load order across memory barrier. Note that grab/rel_lock() perform */ \ + /* "sync" (via call to _clear_lock()), and so, we are guaranteed strict ordering of loads and stores of */ \ + /* code that reads/writes to journal pool in transaction logic */ \ + do_lwsync(); \ +} + +#define SHM_READ_MEMORY_BARRIER \ +{ \ + do_isync(); \ +} + +#elif defined(__hppa) +/* For _PA_RISC1_0, _PA_RISC1_1, accesses to the address space (both to memory and I/O) through load, store and + * semaphore instructions are strongly ordered. This means that accesses appear to software to be done in program order + * For _PA_RISC2_0, accesses could be "strongly ordered", "ordered", or "weakly ordered" (read PA-RISC 2.0 ARCHITECTURE + * by Gerry Kane, appendix "Memory Ordering Model"). + * + * For all PA-RISC architectures, cache flush operations are weakly ordered. Flushes may be delayed or held pending, and + * a sequence of flush operations may be executed in any order. + * + * SYNC : Enforce program order of memory references + * Any load, store, semaphore, cache flush, or cache purge instructions that follow the SYNC instruction get executed + * only after all such instructions prior to the SYNC instruction have completed executing. On implementations which + * execute such instructions out of sequence, this instruction enforces program ordering. In sytems in which all memory + * references are performed in order, this instruction executes as a null instruction. + * + * IMPORTANT: SYNC instruction enforces ordering of only those accesses caused by the instructions executed on the + * same processor which executes the SYNC instruction. + * + * [Vinaya] Research results: Accesses to fields (global) that are defined volatile are ordered (compiler generates + * LDW (or STW) instruction with the O (ordered) completer, i.e., instructions generated are LDW,O (STW,O). Depending + * on the requirements, it may be sufficient to define shared fields as volatile to enforce ordering. With replication + * though, it is important that pending cache flushes are completed so that source server sees the transaction data + * in its entirety. + */ + +#define SHM_WRITE_MEMORY_BARRIER (void)_asm("SYNC") + +#define SHM_READ_MEMORY_BARRIER SHM_WRITE_MEMORY_BARRIER /* same SYNC instruction for both read and write barriers. + * For read, we want all cache purges to be completed before + * we load shared fields */ +#elif defined(__ia64) + +#if defined(__hpux) + +#include +#define SHM_WRITE_MEMORY_BARRIER _MF() + +#elif defined(__linux__) && defined(__INTEL_COMPILER) + +# define SHM_WRITE_MEMORY_BARRIER __mf() + +#elif defined(__linux__) /* gcc */ + +# define SHM_WRITE_MEMORY_BARRIER __asm__ __volatile__ ("mf" ::: "memory") +#endif /* __linux__ */ + +/* On IA64, cross processor notifications of write barriers are automatic so no read barrier is necessary */ +#define SHM_READ_MEMORY_BARRIER + +#else /* SPARC, I386, S390, Itanium */ + +/* Although SPARC architecture allows for out-of-order memory accesses, Solaris forces strong ordering on memory accesses. + * We do not need memory barrier primitives on Solaris/SPARC. + */ + +/* Memory accesses in Intel x86 and IBM S390 archtectures are strongly ordered */ + +#define SHM_WRITE_MEMORY_BARRIER +#define SHM_READ_MEMORY_BARRIER + +#endif + +#if !defined(SECSHR_SHM_WRITE_MEMORY_BARRIER) +#define SECSHR_SHM_WRITE_MEMORY_BARRIER SHM_WRITE_MEMORY_BARRIER /* default definition */ +#endif + +#if !defined(SECSHR_SHM_READ_MEMORY_BARRIER) +#define SECSHR_SHM_READ_MEMORY_BARRIER SHM_READ_MEMORY_BARRIER /* default definition */ +#endif + +#endif /* MEMCOHERENCY_H_INCLUDED */ diff --git a/sr_port/memvcmp.c b/sr_port/memvcmp.c new file mode 100644 index 0000000..adc5931 --- /dev/null +++ b/sr_port/memvcmp.c @@ -0,0 +1,27 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +/* Compare two character strings using "0" as a filler + return 0 iff they are equal + return < 0 if a < b and > 0 if a > b */ + +#include "mdef.h" + +#include "gtm_string.h" +#include "mmemory.h" + +int memvcmp(void *a, int a_len, void *b, int b_len) +{ + int retval; + + MEMVCMP(a, a_len, b, b_len, retval); + return retval; +} diff --git a/sr_port/merge_def.h b/sr_port/merge_def.h new file mode 100644 index 0000000..af5e5fb --- /dev/null +++ b/sr_port/merge_def.h @@ -0,0 +1,24 @@ +/**************************************************************** + * * + * Copyright 2001 Sanchez Computer Associates, Inc. * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#ifndef MERGE_DEF_DEFINED +#define MARG1_LCL 1 +#define MARG1_GBL 2 +#define MARG2_LCL 4 +#define MARG2_GBL 8 +#define IND1 0 +#define IND2 1 +#define MARG1_IS_LCL(arg) (arg & MARG1_LCL) +#define MARG1_IS_GBL(arg) (arg & MARG1_GBL) +#define MARG2_IS_LCL(arg) (arg & MARG2_LCL) +#define MARG2_IS_GBL(arg) (arg & MARG2_GBL) +#define MERGE_DEF_DEFINED +#endif diff --git a/sr_port/merge_desc_check.c b/sr_port/merge_desc_check.c new file mode 100644 index 0000000..a98b1d3 --- /dev/null +++ b/sr_port/merge_desc_check.c @@ -0,0 +1,102 @@ +/**************************************************************** + * * + * Copyright 2001, 2011 Fidelity Information Services, Inc * + * * + * This source code contains the intellectual property * + * of its copyright holder(s), and is made available * + * under a license. If you do not know the terms of * + * the license, please stop and do not read further. * + * * + ****************************************************************/ + +#include "mdef.h" + +#include "gtm_string.h" +#include "min_max.h" +#include "gdsroot.h" +#include "gdskill.h" +#include "gtm_facility.h" +#include "fileinfo.h" +#include "gdsbt.h" +#include "gdsfhead.h" +#include "zshow.h" +#include "zwrite.h" +#include "filestruct.h" +#include "gdscc.h" +#include "copy.h" +#include "jnl.h" +#include "buddy_list.h" +#include "hashtab_int4.h" /* needed for tp.h */ +#include "tp.h" +#include "merge_def.h" +#include "gvname_info.h" +#include "op_merge.h" +#include "format_targ_key.h" +#include "ddphdr.h" + +GBLREF int merge_args; +GBLREF merge_glvn_ptr mglvnp; + +void merge_desc_check(void) +{ + unsigned char buff1[MAX_ZWR_KEY_SZ], buff2[MAX_ZWR_KEY_SZ], *end1, *end2; + enum db_acc_method acc_meth1, acc_meth2; + gd_region *reg1, *reg2; + gv_namehead *gvt1, *gvt2; + + error_def(ERR_MERGEDESC); + + if (MARG1_IS_GBL(merge_args) && MARG2_IS_GBL(merge_args)) + { + reg1 = mglvnp->gblp[IND1]->s_gv_cur_region; + reg2 = mglvnp->gblp[IND2]->s_gv_cur_region; + gvt1 = mglvnp->gblp[IND1]->s_gv_target; + gvt2 = mglvnp->gblp[IND2]->s_gv_target; + acc_meth1 = reg1->dyn.addr->acc_meth; + acc_meth2 = reg2->dyn.addr->acc_meth; + assert(!(dba_bg == acc_meth1 || dba_mm == acc_meth1) || (NULL != gvt1->gd_csa)); + assert(!(dba_bg == acc_meth2 || dba_mm == acc_meth2) || (NULL != gvt2->gd_csa)); + /* if (!(both are bg/mm regions && dbs are same && same global) && + * !(both are cm regions && on the same remote node && same region) + * !(both are usr regions && in the same volume set)) + * NO DESCENDANTS + * endif + */ + if (!(((dba_bg == acc_meth1 || dba_mm == acc_meth2) && (dba_bg == acc_meth1 || dba_mm == acc_meth2)) + && (gvt1->gd_csa == gvt2->gd_csa) && (gvt1->root == gvt2->root)) + && !((dba_cm == acc_meth1) && (dba_cm == acc_meth2) + && (reg1->dyn.addr->cm_blk == reg2->dyn.addr->cm_blk) && (reg1->cmx_regnum == reg2->cmx_regnum)) + VMS_ONLY (&& + !((dba_usr == acc_meth1) && (dba_usr == acc_meth2) + && ((ddp_info *)(&FILE_INFO(reg1)->file_id))->volset == ((ddp_info *)(&FILE_INFO(reg2)->file_id))->volset))) + { + UNIX_ONLY(assert(dba_usr != acc_meth1 && dba_usr != acc_meth2);) + return; + } + if (0 == memcmp(mglvnp->gblp[IND1]->s_gv_currkey->base, mglvnp->gblp[IND2]->s_gv_currkey->base, + MIN(mglvnp->gblp[IND1]->s_gv_currkey->end, mglvnp->gblp[IND2]->s_gv_currkey->end))) + { + if (0 == (end1 = format_targ_key(buff1, MAX_ZWR_KEY_SZ, mglvnp->gblp[IND1]->s_gv_currkey, TRUE))) + end1 = &buff1[MAX_ZWR_KEY_SZ - 1]; + if (0 == (end2 = format_targ_key(buff2, MAX_ZWR_KEY_SZ, mglvnp->gblp[IND2]->s_gv_currkey, TRUE))) + end2 = &buff2[MAX_ZWR_KEY_SZ - 1]; + if (mglvnp->gblp[IND1]->s_gv_currkey->end > mglvnp->gblp[IND2]->s_gv_currkey->end) + rts_error(VARLSTCNT(6) ERR_MERGEDESC, 4, end1 - buff1, buff1, end2 - buff2, buff2); + else + rts_error(VARLSTCNT(6) ERR_MERGEDESC, 4, end2 - buff2, buff2, end1 - buff1, buff1); + } + } else if (MARG1_IS_LCL(merge_args) && MARG2_IS_LCL(merge_args)) + { + if (lcl_arg1_is_desc_of_arg2(mglvnp->lclp[IND1], mglvnp->lclp[IND2])) + { + end1 = format_key_lv_val(mglvnp->lclp[IND1], buff1, SIZEOF(buff1)); + end2 = format_key_lv_val(mglvnp->lclp[IND2], buff2, SIZEOF(buff2)); + rts_error(VARLSTCNT(6) ERR_MERGEDESC, 4, end1 - buff1, buff1, end2 - buff2, buff2); + } else if (lcl_arg1_is_desc_of_arg2(mglvnp->lclp[IND2], mglvnp->lclp[IND1])) + { + end1 = format_key_lv_val(mglvnp->lclp[IND1], buff1, SIZEOF(buff1)); + end2 = format_key_lv_val(mglvnp->lclp[IND2], buff2, SIZEOF(buff2)); + rts_error(VARLSTCNT(6) ERR_MERGEDESC, 4, end2 - buff2, buff2, end1 - buff1, buff1); + } + } +} diff --git a/sr_port/merrors.msg b/sr_port/merrors.msg new file mode 100644 index 0000000..434af2f --- /dev/null +++ b/sr_port/merrors.msg @@ -0,0 +1,1393 @@ +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! ! +! Copyright 2001, 2011 Fidelity Information Services, Inc ! +! ! +! This source code contains the intellectual property ! +! of its copyright holder(s), and is made available ! +! under a license. If you do not know the terms of ! +! the license, please stop and do not read further. ! +! ! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + .FACILITY GTM,246/PREFIX=ERR_ + .TITLE MERRORS Error Messages for GTM +! +! The code numbers for the errors that have been +! standardized by the MUMPS Development Committee are +! listed below. +! Those error messages that have a standardized +! error code have a definition that ends in !/ansi=### +! If not specified, the value for "ansi" defaults to 0. +! (Note that the first entry in this file MUST have +! an !/ansi= specification; any subsequent ones are +! optional.) +! +! M1, Naked indicator undefined +! M2, Invalid combination with P _fncodatom_ +! M3, $RANDOM seed less than 1 +! M4, No true condition in $SELECT +! M5, _lineref_ less than zero +! M6, Undefined _lvn_ +! M7, Undefined _gvn_ +! M8, Undefined _svn_ +! M9, Divide by zero +! M10, Invalid pattern match range +! M11, No parameters passed +! M12, Invalid _lineref_ (negative offset) +! M13, Invalid _lineref_ (label not found) +! M14, _line_ level not 1 +! M15, Undefined index variable +! M16, Argumented QUIT not allowed +! M17, Argumented QUIT required +! M18, Fixed length READ not greater than zero +! M19, Cannot copy a tree or subtree into itself +! M20, _line_ must have _formallist_ +! M21, Formal parameter occurs multiple times (original text: Algorithm specification invalid) +! M22, SET or KILL to ^$GLOBAL when data in global +! M23, SET or KILL to ^$JOB for non-existent job number +! M24, Change to collation algorithm while subscripted local variables defined +! M25, Attempt to modify currently executing routine +! M26, Non-existent _environment_ +! M27, Attempt to rollback a transaction that is not restartable +! M28, Mathematical function, parameter out of range +! M29, SET or KILL on _ssvn_ not allowed by implementation +! M30, Reference to _glvn_ with different collating sequence within a collating algorithm +! M31, _controlmnemonic_ used for device without a _mnemonicspace_ selected +! M32, _controlmnemonic_ used in user-defined _mnemonicspace_ which has no associated line +! M33, SET or KILL to ^$ROUTINE when _routine_ exists +! M34, --- currently unassigned --- +! M35, Device does not support _mnemonicspace_ +! M36, Incompatible _mnemonicspace_s +! M37, READ from device identified by the empty string +! M38, Invalid _ssvn_ subscript +! M39, Name of variable expected (original text: Invalid $NAME argument) +! M40, Call-by-reference in JOB _actual_ +! M41, Invalid LOCK argument within a TRANSACTION +! M42, Invalid QUIT within a TRANSACTION +! M43, Invalid range ($X, $Y) +! M44, Invalid _command_ outside of a TRANSACTION +! M45, Invalid GOTO reference +! M46, Invalid attribute name +! M47, Invalid attribute value (original text: Invalid attribute name) +! M48, Nonexistent window, element or choice +! M49, Invalid attempt to set focus +! M50, Attempt to reference a non M-Term window in an OPEN command +! M51, Attempt to destroy M-Term window prior to CLOSE +! M52, Required attribute missing +! M53, Invalid argument for font function +! M54, Attempt to create non-modal child of a modal parent +! M55, Invalid nested TSTART command +! M56, Name length exceeds implementation's limit +! M57, More than one defining occurrence of label in routine +! M58, Too few formal parameters +! M59, Environment reference not permitted for this _ssvn_ +! M60, Undefined _ssvn_ +! M61, Attempt to OPEN file with conflicting ACCESS parameters +! M62, Illegal value for ACCESS parameter while attempting to OPEN file +! M63, Illegal value for DISPOSITION parameter while attempting to CLOSE file +! M64, Illegal value for RENAME parameter while attempting to CLOSE file +! M65, Illegal value for VOLUME label +! M66, Illegal value for DENSITY parameter +! M67, Illegal value for ACCESS parameter +! M68, Illegal value for MOUNT parameter +! M69, Attempted tape I/O while no tape mounted +! M70, Illegal value for BLOCKSIZE parameter +! M71, Attempt to read data block larger than buffer size +! M72, Illegal value for recordsize parameter +! M73, Invalid usage of _devicekeyword_ NEWFILE +! M74, Illegal value for TRANSLATION parameter +! M75, String length exceeds implementation's limit +! M76, TCP socket state incorrect for CONNECT or LISTEN +! M77, TCP _deviceattribute_ missing +! M78, TCP _devicekeyword_ missing +! M79, TCP socket allocated to another device +! M80, Network error not otherwise specified +! M81, Unable to establish network connection +! M82, Network connection suspended: wait to resume +! M83, Network connection lost +! M84, Network protocol error: invalid client message +! M85, Network protocol error: invalid server message +! M86, Cannot relinquish device with I/O pending +! M87, Network buffer overflow +! M88, Non-existent _routine_ +! M89, Specified pattern is not a _subpattern_ +! M90, Invalid _namevalue_ +! M91, Routine source is not available +! M92, Mathematical overflow +! M93, Mathematical underflow +! M94, Attempt to compute zero to the zero-eth power +! M95, Exponentiation returns complex number with non-zero imaginary part +! M96, Attempt to assign value to already valued write-once _ssvn_ +! M97, Routine associated with user-defined _ssvn_ does not exist +! M98, Resource unavailable +! M99, Invalid operation for context +! M100, Output time-out expired +! M101, Attempt to assign incorrect value to $ECODE +! M102, Simultaneous synchronous and asynchronous event class +! M103, Invalid event identifier +! M104, IPC event identifier is not a valid job-number +! M105, Object not currently accessible +! M106, Object does not support requested method or property +! M107, Object has no default value +! M108, Value if not of data type OREF +! M109, Undefined _devicekeyword_ +! M110, Event identifier not available +! M111, Invalid number of days for date +! M112, Invalid number of seconds for time +! +! List of messages which are not reported by GT.M standard error routines(rts_error etc.) but +! by PRINTFs. NOTE: if the value of any these messages changes in future, all its non-standard +! usages should be reflected with the new value. +! ERR_GTMDISTUNDEF 150377714 (reported in gtm.c if $gtm_dist is not defined) +! ERR_DISTPATHMAX 150377682 (reported in gtm.c if $gtm_dist buffer is insufficient) +! ERR_DLLNOOPEN 150379250 (reported in gtm.c if libgtmshr cannot be opened) +! ERR_DLLNORTN 150379258 (reported in gtm.c if gtm_main is not found in libgtmshr) +! ERR_OPCOMMISSED 150381275 (sent via $SNDOPR in util_output.c if prior errors) +! +! List of known undocumented messages follows (along with a comment) +! ERR_JNLREQUIRED message referenced only in GT.CX code (not supported currently) +! ERR_LKSECINIT message referenced only in GT.CX code (not supported currently) +! ERR_JNLWRTNOWWRTR internal error (not displayed to the users) +! ERR_ACK internal error (not displayed to the users) +! ERR_ENQ internal error (not displayed to the users) +! ERR_REPEATERROR internal error (not displayed to the users) +! ERR_TPRETRY internal error (not displayed to the users) +! ERR_INVDBGLVL referenced only if "gtmdbglvl" is set to non-zero value (which is a debugging feature) +! ERR_DEFEREVENT referenced only if #define DEBUG is TRUE (i.e. for debug builds only) +! ERR_FREEZEID referenced only if #define DEBUG_FREEZE is TRUE. +! ERR_MUDESTROYFAIL referenced only if #define IPCRM_FOR_SANCHEZ_ONLY is TRUE. +! ERR_MUDESTROYSUC referenced only if #define IPCRM_FOR_SANCHEZ_ONLY is TRUE. +! ERR_ASC2EBCDICCONV not yet documented since zOS is not officially supported +! ERR_SYSTEMVALUE code is not executed (set $system won't reach op_svput at all to signal the error) +! ERR_YDIRTSZ used by a percent utility, which is not documented intentionally +! ERR_ZDEFACTIVE functionality not documented (GT.CM related weirdness) +! ERR_ZDEFOFLOW functionality not documented (GT.CM related weirdness) +! ERR_WILLEXPIRE error triggered by the license management code which has been since disabled. +! ERR_RBWRNNOTCHG not seen by user since return status of mupip_set_file (that triggers this) is not displayed. +! +! +! ----- Buffer to introduce new undocumented error messages without affecting UNUSEDMSGnnn match with corresponding line numbers. +! +! +! +! +! +! +! +! In addition all messages of the LMU and GTLP message facility are not documented as Licensing is out of GT.M since V4.2. +! +ACK <>/success/fao=0!/ansi=0 +BREAKZST /info/fao=0!/ansi=0 +BADACCMTHD /info/fao=0!/ansi=0 +BADJPIPARAM /error/fao=2!/ansi=0 +BADSYIPARAM /error/fao=2!/ansi=0 +BITMAPSBAD /error/fao=0!/ansi=0 +BREAK /info/fao=0!/ansi=0 +BREAKDEA /info/fao=0!/ansi=0 +BREAKZBA /info/fao=0!/ansi=0 +STATCNT /info/fao=5!/ansi=0 +BTFAIL /error/fao=1!/ansi=0 +MUPRECFLLCK /error/fao=2!/ansi=0 +CMD /error/fao=0!/ansi=0 +COLON /error/fao=0!/ansi=0 +COMMA /error/fao=0!/ansi=0 +COMMAORRPAREXP /error/fao=0!/ansi=0 +COMMENT /info/fao=0!/ansi=0 +CTRAP /error/fao=1!/ansi=0 +CTRLC /info/fao=0!/ansi=0 +CTRLY /info/fao=0!/ansi=0 +DBCCERR /error/fao=2!/ansi=0 +DUPTOKEN /error/fao=5!/ansi=0 +DBJNLNOTMATCH /error/fao=6!/ansi=0 +DBFILERR /error/fao=2!/ansi=0 +DBNOTGDS /error/fao=2!/ansi=0 +DBOPNERR /error/fao=2!/ansi=0 +DBRDERR /error/fao=2!/ansi=0 +CCEDUMPNOW <>/fatal/fao=0!/ansi=0 +DEVPARINAP /error/fao=0!/ansi=0 +RECORDSTAT /info/fao=6!/ansi=0 +NOTGBL /error/fao=2!/ansi=0 +DEVPARPROT /error/fao=0!/ansi=0 +PREMATEOF /error/fao=0!/ansi=0 +GVINVALID /error/fao=2!/ansi=0 +DEVPARTOOBIG /error/fao=0!/ansi=0 +DEVPARUNK /error/fao=0!/ansi=0 +DEVPARVALREQ /error/fao=0!/ansi=0 +DEVPARMNEG /error/fao=0!/ansi=0 +DSEBLKRDFAIL /error/fao=0!/ansi=0 +DSEFAIL /error/fao=2!/ansi=0 +NOTALLREPLON /warning/fao=0!/ansi=0 +BADLKIPARAM /error/fao=2!/ansi=0 +JNLREADBOF /error/fao=2!/ansi=0 +DVIKEYBAD <$ZGETDVI("!AD","!AD") contains an illegal keyword>/error/fao=4!/ansi=0 +ENQ <>/success/fao=0!/ansi=0 +EQUAL /error/fao=0!/ansi=0 +ERRORSUMMARY /error/fao=0!/ansi=0 +ERRWEXC /error/fao=0!/ansi=0 +ERRWIOEXC /error/fao=0!/ansi=0 +ERRWZBRK /error/fao=0!/ansi=0 +ERRWZTRAP /error/fao=0!/ansi=0 +NUMUNXEOR /error/fao=2!/ansi=0 +EXPR /error/fao=0!/ansi=0 +STRUNXEOR /error/fao=2!/ansi=0 +JNLEXTEND /error/fao=2!/ansi=0 +FCHARMAXARGS /error/fao=0!/ansi=0 +FCNSVNEXPECTED /error/fao=0!/ansi=0 +FNARGINC /error/fao=2!/ansi=2 +JNLACCESS /error/fao=2!/ansi=0 +TRANSNOSTART /error/fao=0!/ansi=44 +FNUMARG <$FNUMBER format specifier "!AD" contains an illegal character: "!AD">/error/fao=4!/ansi=0 +FOROFLOW /error/fao=1!/ansi=0 +YDIRTSZ /error/fao=0!/ansi=0 +JNLSUCCESS /success/fao=2!/ansi=0 +GBLNAME /error/fao=0!/ansi=29 +GBLOFLOW /error/fao=0!/ansi=0 +CORRUPT /error/fao=2!/ansi=0 +GTMCHECK /fatal/fao=0!/ansi=0 +GVDATAFAIL /error/fao=2!/ansi=0 +EORNOTFND /error/fao=2!/ansi=0 +GVGETFAIL /error/fao=2!/ansi=0 +GVIS /info/fao=2!/ansi=0 +GVKILLFAIL /error/fao=2!/ansi=0 +GVNAKED /error/fao=0!/ansi=1 +GVNEXTARG /error/fao=0!/ansi=0 +GVORDERFAIL /error/fao=2!/ansi=0 +GVPUTFAIL /error/fao=2!/ansi=0 +PATTABSYNTAX /error/fao=3!/ansi=0 +GVSUBOFLOW /error/fao=0!/ansi=0 +GVUNDEF /error/fao=2!/ansi=7 +TRANSNEST /error/fao=0!/ansi=0 +INDEXTRACHARS /error/fao=0!/ansi=0 +INDMAXNEST /error/fao=0!/ansi=0 +INDRMAXLEN /error/fao=1!/ansi=0 +INSFFBCNT /error/fao=0!/ansi=0 +INTEGERRS /error/fao=0!/ansi=0 +INVCMD /error/fao=0!/ansi=0 +INVFCN /error/fao=0!/ansi=0 +INVOBJ /error/fao=0!/ansi=0 +INVSVN /error/fao=0!/ansi=8 +IOEOF /error/fao=0!/ansi=0 +IONOTOPEN /error/fao=0!/ansi=0 +MUPIPINFO /info/fao=2!/ansi=0 +IVTIME /error/fao=2!/ansi=0 +JOBFAIL /error/fao=0!/ansi=0 +JOBLABOFF /error/fao=0!/ansi=0 +JUSTFRACT /error/fao=0!/ansi=0 +KEY2BIG /error/fao=4!/ansi=0 +LABELEXPECTED /error/fao=0!/ansi=0 +LVORDERARG /error/fao=0!/ansi=0 +MAXFORARGS /error/fao=0!/ansi=0 +TRANSMINUS /error/fao=0!/ansi=0 +MAXNRSUBSCRIPTS /error/fao=0!/ansi=0 +MAXSTRLEN /error/fao=0!/ansi=75 +JNLDBERR /error/fao=4!/ansi=0 +JNLFILOPN /error/fao=4!/ansi=0 +MBXRDONLY /error/fao=0!/ansi=0 +JNLINVALID /error/fao=4!/ansi=0 +MBXWRTONLY /error/fao=0!/ansi=0 +MEMORY /fatal/fao=2!/ansi=0 +MTBLKTOOBIG /error/fao=0!/ansi=70 +MTBLKTOOSM /error/fao=1!/ansi=70 +MTFIXRECSZ /error/fao=2!/ansi=70 +MTIS /info/fao=2!/ansi=0 +MTRDBADBLK /error/fao=2!/ansi=0 +MTRDONLY /error/fao=0!/ansi=62 +MTRDTHENWRT /error/fao=0!/ansi=0 +MTRECGTRBLK /error/fao=0!/ansi=71 +MTRECTOOBIG /error/fao=0!/ansi=72 +MTRECTOOSM /error/fao=0!/ansi=72 +JNLTMQUAL3 /error/fao=0!/ansi=0 +JNLWRTDEFER /info/fao=0!/ansi=0 +SELECTFALSE /error/fao=0!/ansi=4 +SPOREOL /error/fao=0!/ansi=0 +SRCLIN /info/fao=4!/ansi=0 +SRCLOC /info/fao=4!/ansi=0 +SRCLOCUNKNOWN /info/fao=0!/ansi=0 +STACKCRIT /error/fao=0!/ansi=0 +STACKOFLOW /fatal/fao=0!/ansi=0 +STACKUNDERFLO /error/fao=0!/ansi=0 +STRINGOFLOW /error/fao=0!/ansi=0 +SVNOSET /error/fao=0!/ansi=0 +VIEWFN /error/fao=0!/ansi=0 +TERMASTQUOTA /error/fao=0!/ansi=0 +TEXTARG /error/fao=0!/ansi=5 +TMPSTOREMAX /error/fao=0!/ansi=0 +VIEWCMD /error/fao=0!/ansi=0 +TXTNEGLIN /error/fao=0!/ansi=12 +TXTSRCFMT <$TEXT encountered an invalid source program file format>/error/fao=0!/ansi=0 +UIDMSG /error/fao=0!/ansi=0 +UIDSND /error/fao=0!/ansi=0 +UNDEF /error/fao=2!/ansi=6 +UNIMPLOP /error/fao=0!/ansi=0 +VAREXPECTED /error/fao=0!/ansi=39 +VARRECBLKSZ /error/fao=0!/ansi=0 +MAXARGCNT /error/fao=1!/ansi=0 +WCFAIL /error/fao=0!/ansi=0 +VIEWARGCNT /error/fao=2!/ansi=0 +XKILLCNTEXC /error/fao=1!/ansi=0 +ZATTACHERR /error/fao=2!/ansi=0 +ZDATEFMT <$ZDATE format string contains invalid character>/error/fao=0!/ansi=0 +ZEDFILSPEC /error/fao=2!/ansi=0 +ZFILENMTOOLONG /error/fao=2!/ansi=75 +ZFILKEYBAD /error/fao=2!/ansi=0 +ZFILNMBAD /error/fao=2!/ansi=0 +ZGOTOLTZERO /error/fao=0!/ansi=0 +ZGOTOTOOBIG /error/fao=0!/ansi=0 +ZLINKFILE /error/fao=2!/ansi=0 +ZPARSETYPE /error/fao=2!/ansi=0 +ZPARSFLDBAD /error/fao=2!/ansi=0 +ZPIDBADARG /error/fao=0!/ansi=0 +ZPRIVARGBAD /error/fao=2!/ansi=0 +ZPRIVSYNTAXERR /error/fao=0!/ansi=0 +ZPRTLABNOTFND
/info/fao=2!/ansi=0 +MUSECNOTDEL
/info/fao=2!/ansi=0 +RPARENREQD /error/fao=2!/ansi=0 +ZGBLDIRACC /error/fao=6!/ansi=26 +GVNAKEDEXTNM /error/fao=0!/ansi=0 +EXTGBLDEL /error/fao=0!/ansi=0 +DSEWCINITCON /info/fao=0!/ansi=0 +LASTFILCMPLD /info/fao=2!/ansi=0 +NOEXCNOZTRAP /info/fao=0!/ansi=0 +UNSDCLASS /error/fao=0!/ansi=0 +UNSDDTYPE /error/fao=0!/ansi=0 +ZCUNKTYPE /error/fao=0!/ansi=0 +ZCUNKMECH /error/fao=0!/ansi=0 +ZCUNKQUAL /error/fao=0!/ansi=0 +JNLDBTNNOMATCH /error/fao=9!/ansi=0 +ZCALLTABLE /error/fao=0!/ansi=0 +ZCARGMSMTCH /error/fao=2!/ansi=58 +ZCCONMSMTCH /error/fao=0!/ansi=58 +ZCOPT0 /error/fao=0!/ansi=0 +ZCSTATUS /error/fao=0!/ansi=0 +ZCUSRRTN /error/fao=0!/ansi=0 +ZCPOSOVR /error/fao=1!/ansi=0 +ZCINPUTREQ /error/fao=0!/ansi=0 +JNLTNOUTOFSEQ /error/fao=6!/ansi=0 +ACTRANGE /error/fao=1!/ansi=0 +ZCCONVERT /error/fao=0!/ansi=0 +ZCRTENOTF /error/fao=2!/ansi=0 +GVRUNDOWN /error/fao=0!/ansi=0 +LKRUNDOWN /error/fao=0!/ansi=0 +IORUNDOWN /error/fao=0!/ansi=0 +FILENOTFND /error/fao=2!/ansi=0 +MUFILRNDWNFL /info/fao=2!/ansi=0 +JNLTMQUAL1 /error/fao=0!/ansi=0 +ACTLSTEXP /error/fao=0!/ansi=11 +NOTEXTRINSIC /error/fao=0!/ansi=16 +FMLLSTPRESENT /error/fao=2!/ansi=11 +FMLLSTMISSING /error/fao=2!/ansi=20 +ACTLSTTOOLONG /error/fao=2!/ansi=58 +ACTOFFSET /error/fao=0!/ansi=0 +MAXACTARG /error/fao=0!/ansi=0 +GTMDUMPFAIL /error/fao=0!/ansi=0 +JNLTMQUAL2