pax_global_header00006660000000000000000000000064126474311320014515gustar00rootroot0000000000000052 comment=436820d3652ee9f29d16b0268445b5a6b4192698 gap-io-4.4.5+ds/000077500000000000000000000000001264743113200133055ustar00rootroot00000000000000gap-io-4.4.5+ds/CHANGES000066400000000000000000000162211264743113200143020ustar00rootroot00000000000000This file describes changes in the IO package. 4.4.5 (2016-01-07) - Move website to http://gap-packages.github.io/io/ - Change IO_PipeThroughWithError to also return the exit status of the child process - Improve test suite a little bit - GAP 4.8 also provides ChangeDirectoryCurrent, so we only define it if it does not already exist - Don't wait for child processes to exit on GAP exit 4.4.4 (2014-11-19) - Fix a bug in the IO_Write code, which for example could cause IO_WriteLine to fail for certain long string 4.4.3 (2014-11-08) - Tweak the build system to avoid potential issues when the source code gets repackages by third parties (e.g. by the GAP team, when creating their big package archives) 4.4.2 (2014-10-02) - Recompress tst/test.txt with 'gzip --no-name' - Fix package name in manual title ("io" -> "IO") 4.4.1 (2014-09-25) - Fix the release archive (it extracted to a directory starting with "IO" instead of "io") 4.4 (2014-09-24) - Fix problem with IO_Pickling some very large objects - Add pickle support for transformations and partial permutations - Try to ensure files are flushed on exit, even if they are not explicitly closed. - Ensure file buffers are flushed before GAP closes, to avoid data loss when files are not closed before exiting. - Add = and < methods for IsFile objects - Update Max Neunhoeffer's contact data - Use AutoDoc to generate title page of the manual from PackageInfo.g - Add Max Horn as a maintainer 4.3.1 (2014-04-04) - Previous release had an invalid release date in PackageInfo.g (2014-04-31 instead of 2014-03-31). - Merge HISTORY and CHANGES files. 4.3 (2014-03-31) - Update and tweak the package build system - Move package repository and website to GitHub - Pickling/Unpickling of Floats 4.2 (2012-05-28) - Fix behaviour of IO_select to try again if the system call was interrupted. This fixes an infinite loop bug in ParTakeFirstResultByFork. - Change "source" to "." in AC_FIND_GAP to provide support for BSD. - Add script "configure_like_gap" - Compile documentation against 4.5.4 - Fix installations instructions, remove static linking from README. 4.1 (2012-01-30) - Make it compile on Windows. 4.0 (2012-01-27) - Use new build setup using automake/autoconf/libtool - Move repository to git@git.gap-system.org/io - Updated the documentation of IO_select 3.3 (2011-03-23) - Fixed a bug to compile on latest cygwin without warning. - Added IOHub functionality and documented it. - Hashserver example. - Cleanup autoconf configuration. 3.2 (2011-02-02) - Remove the global function f which was accidentally put in there in 3.1. - Update cnf files for clang and to compile on GAP 4.4 and 4.5 - Update cnf files to autoconf 2.65 - Add functions gethostname and getsockname. - Change license to GPL 3 3.1 (2010-07-23) - Fix documentation of /dev/random and /dev/urandom - Background jobs - Parallel patterns: ParMapReduce, ParTakeFirstResult, ParDo 3.0 (2009-04-08) - Added ignoring of SIGPIPE for architectures where standard behaviour is to terminate the process. - Completely new configure process with our own autoconf scripts - IO_gettimeofday new - IO_gmtime and IO_localtime new - Check for IO_getpid and IO_getppid and IO_kill - Load dynamic module earlier (in init.g at the beginning) - Release revision 342 as 3.0: 8.4.2009 2.3 (2007-10-03) - Require GAP >= 4.4.9 in PackageInfo.g - Change addresses to St Andrews. - Add understanding of chunked transfer encoding. - Make IO compile on FreeBSD. - Added IO_getpid, IO_getppid, IO_kill. - Change license to GPL V2 or later. - Released revision 328 as 2.3: 3.10.2007 2.2 (2007-04-02) - Fixed a serious bug with the generic object pickler. - Released version 296 as 2.2: 2.4.2007 2.1 (2007-02-26) - Fixed bug with IO_stat, that time stamps are usually >= 2^28. - Add framework for other packages to install picklers and unpicklers even if they are loaded before the IO package. - Pickling/Unpickling of functions and operations (methods still a problem) - Leave out last argument of IO_GenericObjectUnpickler because it was never needed, return either IO_Error or unpickled object. This changes the semantics! - Pickling/Unpickling of WeakPointerObjects - Pickling/Unpickling of permutation groups (including Size and base of StabChain) - Pickling/Unpickling of matrix groups (only generators and Size) - Pickling/Unpickling of finite fields - Set Host component of HTTP request header by default of the name of the server argument in SingleHTTPRequest. - Alexander Konovalov's CheckForUpdates function. - Released revision 289 as 2.1: 26.2.2007 2.0 (2006-12-12) - See to SIGCHLD signal handler in Popen, Popen2, and Popen3 - WaitPid in PipeThrough* - Loop around IO_select calls to ignore EINTR error - Sort out __stack_chk_fail_local gcc 4.1 problem - case insensitivity in header field names in HTTP protocol - fix bug that crashed GAP when starting another process or terminating GAP after calling Popen* - Added http protocol test. - Add tst/platform.g to have a check of "standard things" - include more headers with #include - take care of PIPE_BUF variable (might not exist on platform!) - Improved installation instructions in the manual and the README file. - Add proper preface to the manual - Fix documentation of Popen* - Clean up code for Popen, create IO_ForkExecWithFDs - IO_StartPipeline - IO_StringFilterFile, IO_FileFilterString - paths to executables do PATH lookups using IO_FindExecutable - IO_FilteredFile to create a File object which filteres through a pipeline. - Let IO_Close automatically call IO_WaitPid under certain circumstances. - Released revision 238 as 2.0: 12.12.2006 1.6 (2006-11-16) - New binding IO_fcntl with corresponding constants in IO (this is necessary for switching a file descriptor to O_NONBLOCK) - In IO_WriteNonBlocking only try to send PIPE_BUF bytes instead of full buffer to avoid blocking (is this the solution?) - Functionality to pipe a string through an external command using I/O multiplexing - New client side HTTP protocol implementation - Use chapters in documentation - New functions IO_PipeThrough and IO_PipeThroughWithError to pipe a string through a process. - Released version 1.6: 16.11.2006 1.5 (??) - Big changes in the buffered I/O functionality to allow for non-blocking I/O with buffers. Documented all. This changed the interface to buffered I/O considerable! - Test code for buffered I/O. - Test code for pickling/unpickling. - Some small bug fixes of bugs found during development of test code. 1.4 (??) - moved real random sources from the orb package here adjust them to new library code - implement picklers/unpicklers for random sources 1.3 (2006-09-01) - hint to load a package if there is no handler for magic during unpickling - follow Frank's suggestion to allow more than one package with a C-part to be linked to a statically compiled GAP, document this - release subversion revision 155 as Version 1.3 1.2 (??) - ? 1.1 (??) - ? 1.0 (??) - Initial release gap-io-4.4.5+ds/GPL000066400000000000000000001045131264743113200136560ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 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 General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is 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. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for 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. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. 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 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. Use with the GNU Affero General Public License. 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 Affero 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 special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 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 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 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 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". 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 GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . gap-io-4.4.5+ds/LICENSE000066400000000000000000000012331264743113200143110ustar00rootroot00000000000000This IO package is Copyright (C) by Max Neunhöffer This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . gap-io-4.4.5+ds/Makefile.am000066400000000000000000000013151264743113200153410ustar00rootroot00000000000000ACLOCAL_AMFLAGS = -I m4 BINARCHDIR = bin/$(GAPARCH) GAPINSTALLLIB = $(BINARCHDIR)/io.so lib_LTLIBRARIES = io.la io_la_SOURCES = src/io.c io_la_CPPFLAGS = $(GAP_CPPFLAGS) -DCONFIG_H # Note that the latter is only for GAP 4.4.12 io_la_LDFLAGS = -module -avoid-version if SYS_IS_CYGWIN io_la_LDFLAGS += -no-undefined -version-info 0:0:0 -Wl,$(GAPROOT)/bin/$(GAPARCH)/gap.dll endif all-local: $(GAPINSTALLLIB) $(GAPINSTALLLIB): io.la $(mkdir_p) $(top_srcdir)/$(BINARCHDIR) if SYS_IS_CYGWIN cp .libs/io.dll $(GAPINSTALLLIB) else cp .libs/io.so $(GAPINSTALLLIB) endif clean-local: rm -f $(GAPINSTALLLIB) distclean-local: rm -rf bin/* (cd doc ; ./clean) doc: ($(GAPROOT)/bin/gap.sh -A makedoc.g) .PHONY: doc gap-io-4.4.5+ds/PackageInfo.g000066400000000000000000000111541264743113200156260ustar00rootroot00000000000000############################################################################# ## ## PackageInfo.g for the package `IO' ## SetPackageInfo( rec( PackageName := "IO", Subtitle := "Bindings for low level C library I/O routines", Version := "4.4.5", Date := "07/01/2016", # dd/mm/yyyy format ## Information about authors and maintainers. Persons := [ rec( LastName := "Neunhöffer", FirstNames := "Max", IsAuthor := true, IsMaintainer := false, Email := "max@9hoeffer.de", WWWHome := "http://www-groups.mcs.st-and.ac.uk/~neunhoef", PostalAddress := Concatenation( [ "Gustav-Freytag-Straße 40\n", "50354 Hürth\n", "Germany" ] ), #Place := "St Andrews", #Institution := "University of St Andrews" ), rec( LastName := "Horn", FirstNames := "Max", IsAuthor := false, IsMaintainer := true, Email := "max.horn@math.uni-giessen.de", WWWHome := "http://www.quendi.de/math", PostalAddress := Concatenation( "AG Algebra\n", "Mathematisches Institut\n", "Justus-Liebig-Universität Gießen\n", "Arndtstraße 2\n", "35392 Gießen\n", "Germany" ), Place := "Gießen", Institution := "Justus-Liebig-Universität Gießen" ), ], ## Status information. Currently the following cases are recognized: ## "accepted" for successfully refereed packages ## "deposited" for packages for which the GAP developers agreed ## to distribute them with the core GAP system ## "dev" for development versions of packages ## "other" for all other packages ## # Status := "accepted", Status := "deposited", ## You must provide the next two entries if and only if the status is ## "accepted" because is was successfully refereed: # format: 'name (place)' # CommunicatedBy := "Mike Atkinson (St. Andrews)", #CommunicatedBy := "", # format: mm/yyyy # AcceptDate := "08/1999", #AcceptDate := "", PackageWWWHome := "http://gap-packages.github.io/io/", README_URL := Concatenation(~.PackageWWWHome, "README"), PackageInfoURL := Concatenation(~.PackageWWWHome, "PackageInfo.g"), ArchiveURL := Concatenation("https://github.com/gap-packages/io/", "releases/download/v", ~.Version, "/io-", ~.Version), ArchiveFormats := ".tar.gz .tar.bz2", ## Here you must provide a short abstract explaining the package content ## in HTML format (used on the package overview Web page) and an URL ## for a Webpage with more detailed information about the package ## (not more than a few lines, less is ok): ## Please, use 'GAP' and ## 'MyPKG' for specifing package names. ## AbstractHTML := "The IO package, as its name suggests, \ provides bindings for GAP to the lower \ levels of Input/Output functionality in the C library.", PackageDoc := rec( BookName := "IO", ArchiveURLSubset := ["doc"], HTMLStart := "doc/chap0.html", PDFFile := "doc/manual.pdf", SixFile := "doc/manual.six", LongTitle := "Bindings for low level C library I/O routines", ), Dependencies := rec( GAP := ">=4.7.4", NeededOtherPackages := [["GAPDoc", ">= 1.2"]], SuggestedOtherPackages := [], ExternalConditions := [] ), AvailabilityTest := function() if (not("io" in SHOW_STAT())) and (Filename(DirectoriesPackagePrograms("io"), "io.so") = fail) then #Info(InfoWarning, 1, "IO: kernel IO functions not available."); return fail; fi; return true; end, ## *Optional*, but recommended: path relative to package root to a file which ## contains as many tests of the package functionality as sensible. #TestFile := "tst/testall.g", ## *Optional*: Here you can list some keyword related to the topic ## of the package. Keywords := ["input", "output", "I/O", "C-library", "network", "http", "object serialisation", "unpredictable random numbers", "TCP/IP", "inter process communication", "background jobs", "parallel skeletons", "I/O multiplexing" ], AutoDoc := rec( TitlePage := rec( Copyright := Concatenation( "©right; 2005-2014 by Max Neunhöffer

\n", "\n", "This package may be distributed under the terms and conditions of the\n", "GNU Public License Version 3 or later.\n" ), ) ), )); gap-io-4.4.5+ds/README000066400000000000000000000047031264743113200141710ustar00rootroot00000000000000 ========================================================= README file for the `IO' GAP4 package (Max Neunhoeffer) ========================================================= To get the newest version of this GAP 4 package download the archive file io-x.x.tar.gz or io-x.x.tar.bz2 or io-x.x.zip and unpack it using gunzip io-x.x.tar.gz; tar xvf io-x.x.tar or bzip2 -d io-x.x.tar.bz2; tar xvf io-x.x.tar or unzip -x io-x.x.zip respectively. Do this in a directory called 'pkg', preferably (but not necessarily) in the 'pkg' subdirectory of your GAP 4 installation. It creates a subdirectory called 'io'. To install this package do cd io ./configure If you installed io in another directory than the usual 'pkg' subdirectory, do ./configure --with-gaproot=path where 'path' is a path to the main GAP root directory. See ./configure --help for further options. Afterwards call 'make' to compile a binary file. The package will not work without this step. If you installed the package in another 'pkg' directory than the standard 'pkg' directory in your GAP 4 installation, then you have to add the path to the directory containing your 'pkg' directory to GAP's list of directories. This can be done by starting GAP with the '-l' command line option followed by the name of the directory and a semicolon. Then your directory is prepended to the list of directories searched. Otherwise the package is not found by GAP. Of course, you can add this option to your GAP startup script. If you installed GAP on several architectures, you must execute the configure/make step for each of the architectures. You can either do this immediately after configuring and compiling GAP itself on this architecture, or alternatively (when using version 4.5 of GAP or newer) set the environment variable "CONFIGNAME" to the name of the configuration you used when compiling GAP before running "./configure". Note however that your compiler choice and flags (environment variables "CC" and "CFLAGS" need to be chosen to match the setup of the original GAP compilation. For example you have to specify 32-bit or 64-bit mode correctly! ---------------------------------------------------------------------------- Recompiling the documentation is possible by the command `gap makedoc.g' in the IO directory. But this should not be necessary. For bug reports, feature requests and suggestions, please refer to https://github.com/gap-packages/io/issues gap-io-4.4.5+ds/TODO000066400000000000000000000023241264743113200137760ustar00rootroot00000000000000function()     local v;     v := IO_gettimeofday();     return v.tv_sec+0.000001_l*v.tv_usec; end; for Laurent IO_Gettimeofday() returning an integer with the microseconds and IO_Gettimeofday_float() returning a float in seconds with your warning in the documentation also in Nanoseconds... -> problem: probably need librt for new timing function Implement posix_openpt, grantpt, unlockpt and ptsname to allow usage of pseudo ttys Later: See Mail Laurent Feb 02 2011 --> partially done HTTP access scheint nicht immer zu funktionieren --> debug offenbar warte ich bei nicht ang. contentlength fuer immer (ev. nur bei "chunked access"???) --> www.tagesschau.de z.B. HTTP/1.1-Konformitaet -> Alexander Konovalovs Tests mit Online int seq site chunked transfer Web-Proxy??? Seek fuer File s? Runtimes() zaehlt children nicht mit??? Ausgabe der Testdateien aussagekraeftiger ??? access to utime/utimes? More tests? More examples? For every release: Fix "platform.g" test with latest additions. fix permissions in checked out files (far) future: support routines for http requests? (query strings, MIME?) Maybe a function to change buffer size later on? Unicode? gap-io-4.4.5+ds/VERSION000066400000000000000000000000051264743113200143500ustar00rootroot000000000000004.4.5gap-io-4.4.5+ds/autogen.sh000077500000000000000000000000551264743113200153060ustar00rootroot00000000000000#!/bin/sh -ex autoreconf -vif `dirname "$0"` gap-io-4.4.5+ds/configure.ac000066400000000000000000000040321264743113200155720ustar00rootroot00000000000000AC_PREREQ([2.68]) AC_INIT([io], m4_esyscmd([tr -d '\n' < VERSION]), [https://github.com/gap-packages/io/issues], [io], [http://gap-packages.github.io/io/]) AC_CONFIG_SRCDIR([src/io.c]) AC_CONFIG_HEADER([src/pkgconfig.h:cnf/pkgconfig.h.in]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([cnf]) AM_INIT_AUTOMAKE([1.11 -Wall foreign subdir-objects]) AM_SILENT_RULES([yes]) dnl Disable maintainer mode by default. This avoids troubles during packaging, dnl in particular when the GAP team repackages the source archive. dnl Users can re-enable it by passing "--enable-maintainer-mode" to configure. AM_MAINTAINER_MODE AC_PROG_CC AM_PROG_AR LT_PREREQ([2.4.2]) LT_INIT([disable-static dlopen win32-dll]) ## Find out what the best compiler flags are AX_CC_MAXOPT AC_FIND_GAP AC_CHECK_SIZEOF([void **]) # Checks for libraries. # Checks for header files. AC_HEADER_DIRENT AC_HEADER_STDC AC_HEADER_SYS_WAIT AC_CHECK_HEADERS([fcntl.h netdb.h netinet/in.h netinet/tcp.h stdlib.h sys/socket.h sys/time.h time.h unistd.h signal.h]) # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_TYPE_PID_T AC_CHECK_MEMBERS([struct stat.st_blksize]) AC_STRUCT_ST_BLOCKS AC_CHECK_MEMBERS([struct stat.st_rdev]) AC_HEADER_TIME # Checks for library functions. AC_FUNC_CHOWN AC_FUNC_CLOSEDIR_VOID AC_FUNC_FORK AC_FUNC_LSTAT AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK AC_FUNC_SELECT_ARGTYPES AC_TYPE_SIGNAL AC_FUNC_STAT AC_CHECK_FUNCS([dup2 gethostbyname lchown memset mkdir mkfifo rmdir select socket getprotobyname signal sigaction opendir readdir closedir rewinddir telldir seekdir unlink link rename symlink readlink accept bind chmod connect dup fchmod fchown stat fstat lstat getsockopt listen lstat mknod recv recvfrom send sendto setsockopt gettimeofday gmtime localtime getpid getppid kill gethostname getsockname]) AC_CYGWIN AM_CONDITIONAL([SYS_IS_CYGWIN], [test "$CYGWIN" = "yes"]) if test "$CYGWIN" = "yes"; then AC_DEFINE(SYS_IS_CYGWIN32, 1, are we on CYGWIN?) else AC_DEFINE(SYS_IS_CYGWIN32, 0, are we on CYGWIN?) fi AC_CONFIG_FILES([Makefile]) AC_OUTPUT gap-io-4.4.5+ds/doc/000077500000000000000000000000001264743113200140525ustar00rootroot00000000000000gap-io-4.4.5+ds/doc/clean000077500000000000000000000001431264743113200150600ustar00rootroot00000000000000#!/bin/bash rm -f *.{aux,lab,log,dvi,ps,pdf,bbl,ilg,ind,idx,out,html,tex,pnr,txt,blg,toc,six,brf} gap-io-4.4.5+ds/doc/io.xml000066400000000000000000000024241264743113200152050ustar00rootroot00000000000000 <#Include SYSTEM "title.xml"> <#Include SYSTEM "main.xml"> gap-io-4.4.5+ds/doc/main.xml000066400000000000000000003647541264743113200155430ustar00rootroot00000000000000 Preface IO The purpose of this package is to allow efficient and flexible input/output operations from &GAP;. This is achieved by providing bindings to the low-level I/O functions in the C-library. On top of this an implementation of buffered I/O in the &GAP; language is provided. Further, a framework for serialisation of arbitrary &GAP; objects is implemented. Finally, an implementation of the client side of the HTTP protocol is included in the package.

This package allows to use file based I/O, access to links and file systems, pipes, sockets, and the UDP and TCP/IP protocols.

By default the IO package is not automatically loaded by &GAP; when it is installed. You must load the package with LoadPackage("IO"); before its functions become available.

For bug reports, feature requests and suggestions, please use our https://github.com/neunhoef/io/issues. Installation of the IO-package To get the newest version of this &GAP; 4 package download one of the archive files io-x.x.tar.gz io-x.x.tar.bz2 io-x.x.zip and unpack it using gunzip io-x.x.tar.gz; tar xvf io-x.x.tar or bzip2 -d io-x.x.tar.bz2; tar xvf io-x.x.tar or unzip -x io-x.x.zip respectively.

Do this in a directory called pkg, preferably (but not necessarily) in the pkg subdirectory of your &GAP; 4 installation. It creates a subdirectory called io.

The package will not work without the following compilation step.

To compile the C part of the package do (in the pkg directory) cd io ./configure make If you installed the package in another pkg directory than the standard pkg directory in your &GAP; 4 installation, then you have to do two things. Firstly during compilation you have to use the option --with-gaproot=PATH of the configure script where PATH is a path to the main &GAP; root directory (if not given the default ../.. is assumed).

Secondly you have to specify the path to the directory containing your pkg directory to &GAP;'s list of directories. This can be done by starting &GAP; with the -l command line option followed by the name of the directory and a semicolon. Then your directory is prepended to the list of directories searched. Otherwise the package is not found by &GAP;. Of course, you can add this option to your &GAP; startup script.

If you installed &GAP; on several architectures, you must execute the configure/make step for each of the architectures. You can either do this immediately after configuring and compiling &GAP; itself on this architecture, or alternatively (when using version 4.5 of &GAP; or newer) set the environment variable CONFIGNAME to the name of the configuration you used when compiling &GAP; before running ./configure. Note however that your compiler choice and flags (environment variables CC and CFLAGS) need to be chosen to match the setup of the original &GAP; compilation. For example you have to specify 32-bit or 64-bit mode correctly!

Recompiling the documentation Recompiling the documentation is possible by the command gap makedoc.g in the io directory. But this should not be necessary.
Functions directly available from the C library The following functions from the C library are made available as &GAP; functions:

accept, bind, chdir, chmod, chown, close, closedir, connect, creat, dup, dup2, execv, execve, execvp, exit, fchmod, fchown, fcntl, fork, fstat, gethostbyname, gethostname, getpid, getppid, getsockname, getsockopt, gettimeofday, gmtime, kill, lchown, link, listen, localtime, lseek, lstat, mkdir, mkfifo, mknod, open, opendir, pipe, read, readdir, readlink, recv, recvfrom, rename, rewinddir, rmdir, seekdir, select, send, sendto, setsockopt, socket, stat, symlink, telldir, unlink, write.

Use the man command in your shell to get information about these functions.

For each of these functions there is a corresponding &GAP; global function with the prefix IO&uscore; before its name. Apart from minor differences (see below) they take exactly the same arguments as their C counterparts. Strings must be specified as &GAP; strings and integers as &GAP; immediate integers. Return values are in general the same as for the C counterparts. However, an error condition is indicated by the value fail instead of -1, and if the result can only be success or failure, true indicates success.

All errors are reported via the function.

In the C library a lot of integers are defined as macros in header files. All the necessary values for the above functions are bound to their name in the global IO record.

Warning: Existence of many of these functions and constants is platform dependent. The compilation process checks existence and this leads to the situation that on the &GAP; levels the functions and constants are there or not. If you want to develop platform independent &GAP; code using this package, then you have to check for existence of the functions and constants you need.

Differences in arguments - an overview The open function has to be called with three arguments. The version with two arguments is not available on the &GAP; level.

The read function takes four arguments: fd is an integer file descriptor, st is a &GAP; string, offset is an offset within this string (zero based), and count is the maximal number of bytes to read. The data is read and stored into the string st, starting at position offset+1. The string st is made long enough, such that count bytes would fit into it, beginning at position offset+1. The number of bytes read is returned or fail in case of an error.

The write function is similar, it also takes four arguments: fd is an integer file descriptor, st is a &GAP; string, offset is an offset within this string (zero based), and count is the number of bytes to write, starting from position offset+1 in the string st. The number of bytes written is returned, or a fail in case of an error.

The opendir function only returns true or fail.

The readdir function takes no argument. It reads the directory that was specified in the last call to opendir. It just returns a string, which is the name of a file or subdirectory in the corresponding directory. It returns false after the last file name in the directory or fail in case of an error.

The closedir function takes no argument. It should be called after readdir returned false or fail to avoid excessive use of file descriptors.

The functions stat, fstat, and lstat only take one argument and return a &GAP; record that has the same entries as a struct stat.

The function socket can optionally take a string as third argument. In that case it automatically calls getprotobyname to look up the protocol name.

The functions bind and connect take only one string argument as address field, because the string already encodes the length.

There are two convenience functions and to create such addresses. The first takes two arguments addr and port, where addr is a string of length 4, containing the 4 bytes of the IP address and port is a port number as &GAP; integer. The function takes the same arguments, but the first can be a string containing an IP address in dot notation like 137.226.152.77 or a hostname to be looked up.

The setsockopt function has no argument optlen. The length of the string optval is taken.

The select function works as the function UNIXSelect in the &GAP; library.

As of now, the file locking mechanisms of fcntl using struct flock are not yet implemented on the &GAP; level.

The low-level functions in detail Nearly all of this functions return an integer result in the C library. On the &GAP; level this is either returned as a non-negative integer in case of success or as fail in case of an error (where on the C level -1 would be returned). If the integer can only be 0 for no error this is changed to true on the &GAP; level. an integer or fail Accepts an incoming network connection. For details see man 2 accept. The argument addr can be made with and contains its length such that no third argument is necessary. an integer or fail Binds a local address to a socket. For details see man 2 bind. The argument my&uscore;addr can be made with and contains its length such that no third argument is necessary. true or fail Changes the current working directory. For details see man 2 chdir. true or fail Changes the mode of a file. For details see man 2 chmod. true or fail Sets owner and/or group of file. For details see man 2 chown. true or fail Closes a file descriptor. For details see man 2 close. true or fail Closes a directory. For details see man 3 closedir. Has no arguments, because we only have one DIR struct in the C part. true or fail Connects to a remote socket. For details see man 2 connect. The argument serv&uscore;addr can be made with and contains its length such that no third argument is necessary. an integer or fail Creates a new file. For details see man 2 creat. an integer or fail Duplicates a file descriptor. For details see man 2 dup. true or fail Duplicates a file descriptor to a new one. For details see man 2 dup2. fail or does not return Replaces the process with another process. For details see man 3 execv. The argument argv is a list of strings. The called program does not have to be the first argument in this list. fail or does not return Replaces the process with another process. For details see man 3 execve. The arguments argv and envp are both lists of strings. The called program does not have to be the first argument in argv. The list envp can be made with from a record acquired from and modified later. fail or does not return Replaces the process with another process. For details see man 3 execvp. The argument argv is a list of strings. The called program does not have to be the first argument in this list. Stops process immediately with return code status. For details see man 2 exit. The argument status must be an integer. Does not return. true or fail Changes mode of an opened file. For details see man 2 fchmod. true or fail Changes owner and/or group of an opened file. For details see man 2 fchown. an integer or fail Does various things to control the behaviour of a file descriptor. For details see man 2 fcntl. an integer or fail Forks off a child process, which is an identical copy. For details see man 2 fork. Note that if you want to use the function to wait or check for the termination of child processes, you have to activate the SIGCHLD handler for this package beforehand by using the function . Note further that after that you cannot use the function any more, since its SIGCHLD handler does not work any more. To switch back to that functionality use the function . a record or fail Returns the file meta data for an opened file. For details see man 2 fstat. A &GAP; record is returned with the same entries than a struct stat. a record or fail Return host information by name. For details see man 3 gethostbyname. A &GAP; record is returned with all the relevant information about the host. a string or fail Return host name. For details see man 3 gethostname. an integer Returns the process ID of the current process as an integer. For details see man 2 getpid. an integer Returns the process ID of the parent of the current process as an integer. For details see man 2 getppid. a string or fail Get a socket name. For details see man 2 getsockname. true or false Get a socket option. For details see man 2 getsockopt. Note that the argument optval carries its length around, such that no 5th argument is necessary. A record with components tv_sec and tv_usec This returns the time elapsed since 1.1.1970, 0:00 GMT. The component tv_sec contains the number of full seconds and the number tv_usec the additional microseconds. A record The argument is the number of seconds that have elapsed since 1.1.1970, 0:00 GMT. The result is a record with the current Greenwich mean time broken down into date and time as in the C-library function gmtime. true or fail Sends the signal sig to the process with process ID pid. For details see man 2 kill. The signal numbers available can be found in the global IO record with names like SIGTERM. true or false Changes owner and/or group of a file not following links. For details see man 2 lchown. true or false Create a hard link. For details see man 2 link. true or false Switch a socket to listening. For details see man 2 listen. A record The argument is the number of seconds that have elapsed since 1.1.1970, 0:00 GMT. The result is a record with the current local time broken down into date and time as in the C-library function localtime. an integer or fail Seeks within an open file. For details see man 2 lseek. a record or fail Returns the file meta data for a file not following links. For details see man 2 lstat. A &GAP; record is returned with the same entries than a struct stat. true or false Creates a directory. For details see man 2 mkdir. true or false Creates a FIFO special file (a named pipe). For details see man 3 mkfifo. true or false Create a special or ordinary file. For details see man 2 mknod. an integer or fail Open and possibly create a file or device. For details see man 2 open. Only the variant with 3 arguments can be used. true or false Opens a directory. For details see man 3 opendir. Note that only true is returned if everything is OK, since only one DIR struct is stored on the C level and thus only one directory can be open at any time. a record or fail Create a pair of file descriptors with a pipe between them. For details see man 2 pipe. Note that no arguments are needed. The result is either fail in case of an error or a record with two components toread and towrite bound to the two filedescriptors for reading and writing respectively. an integer or fail Reads from file descriptor. For details see man 2 read. Note that there is one more argument offset to specify at which position in the string st the read data should be stored. Note that offset zero means at the beginning of the string, which is position 1 in &GAP;. The number of bytes read or fail in case of an error is returned. a string or fail or false Reads from a directory. For details see man 2 readdir. Note that no argument is required as we have only one DIR struct on the C level. If the directory is read completely false is returned, and otherwise a string. An error is indicated by fail. an integer or fail Reads the value of a symbolic link. For details see man 2 readlink. buf is modified. The new length of buf is returned or fail in case of an error. an integer or fail Receives data from a socket. For details see man 2 recv. Note the additional argument offset which plays the same role as for the function. an integer or fail Receives data from a socket with given address. For details see man 2 recvfrom. Note the additional argument offset which plays the same role as for the function. The argument addr can be made with and contains its length such that no 7th argument is necessary. true or false Renames a file or moves it. For details see man 2 rename. true or fail Rewinds a directory. For details see man 2 rewinddir. Note that no argument is required as we have only one DIR struct on the C level. Returns fail only, if no prior command has been called. true or fail Removes an empty directory. For details see man 2 rmdir. true or fail Sets the position of the next readdir call. For details see man 3 seekdir. Note that no second argument is required as we have only one DIR struct on the C level. an integer or fail Used for I/O multiplexing. For details see man 2 select. inlist, outlist and exclist are lists of filedescriptors, which are modified. If the corresponding file descriptor is not yet ready, it is replaced by fail. The timeout values timeoutsec and timeoutusec correspond to the usual arguments of select, if both are immediate integers, they are set, otherwise select is called with no timeout value. an integer or fail Sends data to a socket. For details see man 2 send. Note that the additional argument offset specifies the position of the data to send within the string st. It is zero based, meaning that zero indicates the start of the string, which is position 1 in &GAP;. an integer or fail Sends data to a socket. For details see man 2 sendto. Note that the additional argument offset specifies the position of the data to send within the string st. It is zero based, meaning that zero indicates the start of the string, which is position 1 in &GAP;. The argument addr can be made with and contains its length such that no 7th argument is necessary. true or fail Sets a socket option. For details see man 2 setsockopt. Note that the argument optval carries its length around, such that no 5th argument is necessary. an integer or fail Creates a socket, an endpoint for communication. For details see man 2 socket. There is one little special: On systems that have getprotobyname you can pass a string as third argument protocol which is automatically looked up by getprotobyname. a record or fail Returns the file metadata for the file pathname. For details see man 2 stat. A &GAP; record is returned with the same entries than a struct stat. true or fail Creates a symbolic link. For details see man 2 symlink. an integer or fail Return current location in directory. For details see man 3 telldir. Note that no second argument is required as we have only one DIR struct on the C level. true or fail Delete a name and possibly the file it refers to. For details see man 2 unlink. a record or fail Waits for the termination of a child process. For details see man 2 waitpid. Returns a &GAP; record describing PID and exit status. The second argument wait must be either true or false. In the first case, the call blocks until new information about a terminated child process is available. In the second case no such waiting is performed, the call returns immediately. See . an integer or fail Writes to a file descriptor. For details see man 2 write. Note that the additional argument offset specifies the position of the data to send within the string st. It is zero based, meaning that zero indicates the start of the string, which is position 1 in &GAP;.
Further C level functions The following functions do not correspond to functions in the C library, but are there to provide convenience to use other functions: a string or fail Makes a struct sockaddr&uscore;in from IP address and port. The IP address must be given as a string of length four, containing the four bytes of an IPv4 address in natural order. The port must be a port number. Returns a string containing the struct, which can be given to all functions above having an address argument. a list of strings For details see man environ. Returns the current environment as a list of strings of the form key=value. true or false Installs our SIGCHLD handler. This functions works as an idempotent. That is, calling it twice does exactly the same as calling it once. It returns true when it is called for the first time since then a pointer to the old signal handler is stored in a global variable. See . Restores the original SIGCHLD handler. This function works as an idempotent. That is, calling it twice does exactly the same as calling it once. It returns true when it is called for the first time after calling . See .
High level functions for buffered I/O The functions in the previous sections are intended to be a possibility for direct access to the low level I/O functions in the C library. Thus, the calling conventions are strictly as in the original.

The functionality described in this section is implemented completely in the &GAP; language and is intended to provide a good interface for programming in &GAP;. The fundamental object for I/O on the C library level is the file descriptor, which is just a non-negative integer representing an open file of the process. The basic idea is to wrap up file descriptors in &GAP; objects that do the buffering.

Note that considerable care has been taken to ensure that one can do I/O multiplexing with buffered I/O. That is, one always has the possibility to make sure before a read or write operation, that this read or write operation will not block. This is crucial when one wants to serve more than one I/O channel from the same (single-threaded) &GAP; process. This design principle sometimes made it necessary to have more than one function for a certain operation. Those functions usually differ in a subtle way with respect to their blocking behaviour.

One remark applies again to nearly all functions presented here: If an error is indicated by the returned value fail one can use the library function to find out more about the cause of the error. This fact is not mentioned with every single function.

Types and the creation of File objects The wrapped file objects are in the following category: true or false The category of File objects. To create objects in this category, one uses the following function: a File object The argument fd must be a file descriptor (i.e. an integer) or -1 (see below).

rbufsize can either be false for unbuffered reading or an integer buffer size or a string. If it is an integer, a read buffer of that size is used. If it is a string, then fd must be -1 and a File object that reads from that string is created.

wbufsize can either be false for unbuffered writing or an integer buffer size or a string. If it is an integer, a write buffer of that size is used. If it is a string, then fd must be -1 and a File object that appends to that string is created.

The result of this function is a new File object. A convenient way to do this for reading or writing of files on disk is the following function: a File object or fail The argument filename must be a string specifying the path name of the file to work on. mode must also be a string with possible values r, w, or a, meaning read access, write access (with creating and truncating), and append access respectively. If mode is omitted, it defaults to r. bufsize, if given, must be a positive integer or false, otherwise it defaults to IO.DefaultBufSize. Internally, the function is used and the result file descriptor is wrapped using with bufsize as the buffer size.

The result is either fail in case of an error or a File object in case of success. Note that there is a similar function which also creates a File object but with additional functionality with respect to a pipeline for filtering. It is described in its section in Section . There is some more low-level functionality to acquire open file descriptors. These can be wrapped into File objects using .

Reading and writing Once a File object is created, one can use the following functions on it: a string or fail This function reads all data from the file f until the end of file. The data is returned as a &GAP; string. If the file is already at end of file, an empty string is returned. If an error occurs, then fail is returned. Note that you still have to call on the File object to properly close the file later. a string or fail This function gets two arguments, the first argument f must be a File object and the second argument len must be a positive integer. The function tries to read len bytes and returns a string of that length. If and only if the end of file is reached earlier, fewer bytes are returned. If an error occurs, fail is returned. Note that this function blocks until either len bytes are read, or the end of file is reached, or an error occurs. For the case of pipes or internet connections it is possible that currently no more data is available, however, by definition the end of file is only reached after the connection has been closed by the other side! a string or fail This function gets exactly one argument, which must be a File object f. It reads one line of data, where the definition of line is operating system dependent. The line end character(s) are included in the result. The function returns a string with the line in case of success and fail in case of an error. In the latter case, one can query the error with .

Note that the reading is done via the buffer of f, such that this function will be quite fast also for large amounts of data.

If the end of file is hit without a line end, the rest of the file is returned. If the file is already at end of file before the call, then a string of length 0 is returned. Note that this is not an error but the standard end of file convention! a list of strings or fail This function gets one or two arguments, the first of which must always be a File object f. It reads lines of data (where the definition of line is operating system dependent) either until end of file (without a second argument) or up to max lines (with a second argument max. A list of strings with the result is returned, if everything went well and fail oterwise. In the latter case, one can query the error with .

Note that the reading is done via the buffer of f, such that this function will be quite fast also for large amounts of data.

If the file is already at the end of file, the function returns a list of length 0. Note that this is not an error but the standard end of file convention! true or false This function takes one argument f which must be a File object. It returns true or false according to whether there is data to read available in the file f. A return value of true guarantees that the next call to on that file will succeed without blocking and return at least one byte or an empty string to indicate the end of file. a string or fail The function gets two arguments, the first of which must be a File object f. The second argument must be a positive integer. The function reads data up to len bytes. A string with the result is returned, if everything went well and fail otherwise. In the latter case, one can query the error with .

Note that the reading is done via the buffer of f, such that this function will be quite fast also for large amounts of data.

If the file is already at the end of the file, the function returns a string of length 0. Note that this is not an error!

If a previous call to or to indicated that there is data available to read, then it is guaranteed that the function does not block and returns at least one byte if the file is not yet at end of file and an empty string otherwise. an integer or fail This function can get an arbitrary number of arguments, the first of which must be a File object f. All the other arguments are just written to f if they are strings. Otherwise, the String function is called on them and the result is written out to f.

Note that the writing is done buffered. That is, the data is first written to the buffer and only really written out after the buffer is full or after the user explicitly calls on f.

The result is either the number of bytes written in case of success or fail in case of an error. In the latter case the error can be queried with .

Note that this function blocks until all data is at least written into the buffer and might block until data can be sent again if the buffer is full. an integer or fail Behaves like but works on a single string line and sends an (operating system dependent) end of line string afterwards. Also is called automatically after the operation, such that one can be sure, that the data is actually written out after the function has completed. an integer or fail Behaves like but works on a list of strings list and sends an (operating system dependent) end of line string after each string in the list. Also is called automatically after the operation, such that one can be sure, that the data is actually written out after the function has completed. true or fail This function gets one argument f, which must be a File object. It writes out all the data that is in the write buffer. This is not necessary before the call to the function , since that function calls automatically. However, it is necessary to call after calls to to be sure that the data is really sent out. The function returns true if everything goes well and fail if an error occurs.

Remember that the functions and implicitly call after they are done.

Note that this function might block until all data is actually written to the file descriptor. an integer or fail This function behaves like followed by a call to . It returns either the number of bytes written or fail if an error occurs. true or false This function takes one argument f which must be a File object. It returns true or false according to whether the file f is ready to write. A return value of true guarantees that the next call to on that file will succeed without blocking and accept at least one byte. an integer or fail This function takes four arguments. The first one f must be a File object, the second st a string, and the arguments pos and len must be integers, such that positions pos+1 until pos+len are bound in st. The function tries to write up to len bytes from st from position pos+1 to the file f. If a previous call to or to indicates that f is writable, then it is guaranteed that the following call to will not block and accept at least one byte of data. Note that it is not guaranteed that all len bytes are written. The function returns the number of bytes written or fail if an error occurs. true or false This function takes one argument f which must be a File object. It returns true or false according to whether the file f is ready to flush. A return value of true guarantees that the next call to on that file will succeed without blocking and flush out at least one byte. Note that this does not guarantee, that this call succeeds to flush out the whole content of the buffer! true, false, or fail This function takes one argument f which must be a File object. It tries to write all data in the writing buffer to the file descriptor. If this succeeds, the function returns true and false otherwise. If an error occurs, fail is returned. If a previous call to or indicated that f is flushable, then it is guaranteed that the following call to does not block. However, it is not guaranteed that true is returned from that call. true or fail This function closes the File object f after writing all data in the write buffer out and closing the file descriptor. All buffers are freed. In case of an error, the function returns fail and otherwise true. Note that for pipes to other processes this function collects data about the terminated processes using .

Other functions an integer This function returns the real file descriptor that is behind the File object f. a string or false This function gets one argument f which must be a File object and returns the writing buffer of that File object. This is necessary for File objects, that are not associated to a real file descriptor but just collect everything that was written in their writing buffer. Remember to use this function before closing the File object. an integer or fail This function is the corresponding function to for buffered file access. It behaves similarly to that function. The differences are the following: There are four lists of files r, w, f, and e. They all can contain either integers (standing for file descriptors) or File objects. The list r is for checking, whether files or file descriptors are ready to read, the list w is for checking whether they are ready to write, the list f is for checking whether they are ready to flush, and the list e is for checking whether they have exceptions.

For File objects it is always first checked, whether there is either data available in a reading buffer or space in a writing buffer. If so, they are immediately reported to be ready (this feature makes the list of File objects to test for flushability necessary). For the remaining files and for all specified file descriptors, the function is called to get an overview about readiness. The timeout values t1 and t2 are set to zero for immediate returning if one of the requested buffers were ready.

returns the number of files or file descriptors that are ready to serve or fail if an error occurs. The following function is a convenience function for directory access: a list of strings or fail This function gets a string containing a path name as single argument and returns a list of strings that are the names of the files in that directory, or fail, if an error occurred. true on success and fail on failure Changes the current directory. Returns true on success and fail on failure. The following function is used to create strings describing a pair of an IP address and a port number in a binary way. These strings can be used in connection with the C library functions connect, bind, recvfrom, and sendto for the arguments needing such address pairs. a string This function gets a string ipstring containing an IP address in dot notation, i.e. four numbers in the range from 0 to 255 separated by dots ., and an integer portnr, which is a port number. The result is a string of the correct length to be used for the low level C library functions, wherever IP address port number pairs are needed. The string ipstring can also be a host name, which is then looked up using to find the IP address. a record or fail Takes no arguments, uses to get the environment and returns a record in which the component names are the names of the environment variables and the values are the values. This can then be changed and the changed record can be given to to produce again a list which can be used for as third argument. a list of strings Takes a record as returned by and turns it into a list of strings as needed by as third argument.

Inter process communication fail or the path to an executable If the path name path contains a slash, this function simply checks whether the string path refers to an executable file. If so, path is returned as is. Otherwise, fail is returned. If the path name path does not contain a slash, all directories in the environment variable PATH are searched for an executable with name path. If so, the full path to that executable is returned, otherwise fail.

This function is used whenever one of the following functions gets an argument that should refer to an executable. nothing Closes all file descriptors except those listed in exceptions, which must be a list of integers. a File object or fail The argument path must refer to an executable file in the sense of .

Starts a child process using the executable in path with either stdout or stdin being a pipe. The argument mode must be either the string r or the string w.

In the first case, the standard output of the child process will be the writing end of a pipe. A File object for reading connected to the reading end of the pipe is returned. The standard input and standard error of the child process will be the same than the calling &GAP; process.

In the second case, the standard input of the child process will be the reading end of a pipe. A File object for writing connected to the writing end of the pipe is returned. The standard output and standard error of the child process will be the same than the calling &GAP; process.

In case of an error, fail is returned.

The process will usually die, when the pipe is closed, but can also do so without that. The File object remembers the process ID of the started process and the function then calls for it to acquire information about the terminated process.

Note that activates our SIGCHLD handler (see ).

In either case the File object will have the attribute ProcessID set to the process ID of the child process. a record or fail The argument path must refer to an executable file in the sense of .

A new child process is started using the executable in path The standard input and standard output of it are pipes. The writing end of the input pipe and the reading end of the output pipe are returned as File objects bound to two components stdin and stdout (resp.) of the returned record. This means, you have to write to stdin and read from stdout in the calling &GAP; process. The standard error of the child process will be the same as the one of the calling &GAP; process.

Returns fail if an error occurred.

The process will usually die, when one of the pipes is closed. The File objects remember the process ID of the called process and the function call to for the stdout object will call for it to acquire information about the terminated process.

Note that activates our SIGCHLD handler (see ).

Both File objects will have the attribute ProcessID set to the process ID of the child process, which will also be bound to the pid component of the returned record. a record or fail The argument path must refer to an executable file in the sense of .

A new child process is started using the executable in path The standard input, standard output, and standard error of it are pipes. The writing end of the input pipe, the reading end of the output pipe and the reading end of the error pipe are returned as File objects bound to two components stdin, stdout, and stderr (resp.) of the returned record. This means, you have to write to stdin and read from stdout and stderr in the calling &GAP; process.

Returns fail if an error occurred.

The process will usually die, when one of the pipes is closed. All three File objects will remember the process ID of the newly created process and the call to the function for the stdout object will call for it to acquire information about the terminated child process.

Note that activates our SIGCHLD handler (see ).

All three File objects will have the attribute ProcessID set to the process ID of the child process, which will also be bound to the pid component of the returned record. a record or fail The argument progs is a list of pairs, the first entry being a path to an executable (in the sense of ), the second an argument list, the argument infd is an open file descriptor for reading, outfd is an open file descriptor for writing, both can be replaced by the string open in which case a new pipe will be opened. The argument switcherror is a boolean indicating whether standard error channels are also switched to the corresponding output channels.

This function starts up all processes and connects them with pipes. The input of the first is switched to infd and the output of the last to outfd.

Returns a record with the following components: pids is a list of process ids if everything worked. For each process for which some error occurred the corresponding pid is replaced by fail. The stdin component is equal to false, or to the file descriptor of the writing end of the newly created pipe which is connected to the standard input of the first of the new processes if infd was open. The stdout component is equal to false or to the file descriptor of the reading end of the newly created pipe which is connected to the standard output of the last of the new processes if outfd was open.

Note that the SIGCHLD handler of the IO package is installed by this function (see ) and that it lies in the responsibility of the caller to use to ask for the status information of all child processes after their termination. a string or fail Reads the file with the name filename, however, a pipeline is created by the processes described by progs (see ) to filter the content of the file through the pipeline. The result is put into a &GAP; string and returned. If something goes wrong, fail is returned. a string or fail Writes the content of the string st to the file with the name filename, however, a pipeline is created by the processes described by progs (see ) to filter the content of the string through the pipeline. The result is put into the file. If the boolean value append is given and equal to true, then the data will be appended to the already existing file. If something goes wrong, fail is returned. a File object or fail This function is similar to and behaves nearly identically. The only difference is that a filtering pipeline is switched between the file and the File object such that all things read or written respectively are filtered through this pipeline of processes.

The File object remembers the started processes and upon the final call to automatically uses the function to acquire information from the terminated processes in the pipeline after their termination. This means that you do not have to call any more after the call to .

Note that activates our SIGCHLD handler (see ).

The File object will have the attribute ProcessID set to the list of process IDs of the child processes. a File object or fail This function is a convenience wrapper around which handles a number of common compressed file formats transparently, by calling an external program. The arguments to this function are identical to . If the extension to filename is one of gz, bz2 or xz, then the file is transparently compressed/uncompressed using gzip, bzip2 or xz respectively. If the extension is none of these, then the command behaves identically to .

Note that as this function calls , it will activate our SIGCHLD handler (see ).

When compression / decompression is active, the File object will have the attribute ProcessID set to the list of process IDs of the child processes. This functions uses to write the whole string st to the File object f. However, this is done by forking off a child process identical to the calling &GAP; process that does the sending. The calling &GAP; process returns immediately, even before anything has been sent away with the result true. The forked off sender process terminates itself immediately after it has sent all data away.

The reason for having this function available is the following: If one uses or to start up a child process with standard input and standard output being a pipe, then one usually has the problem, that the child process starts reading some data, but then wants to write data, before it received all data coming. If the calling &GAP; process would first try to write all data and only start to read the output of the child process after sending away all data, a deadlock situation would occur. This is avoided with the forking and backgrounding approach.

Remember to close the writing end of the standard input pipe in the calling &GAP; process directly after has returned, because otherwise the child process might not notice that all data has arrived, because the pipe persists! See the file popen2.g in the example directory for an example.

Note that with most modern operating systems the forking off of an identical child process does in fact not mean a duplication of the total main memory used by both processes, because the operating system kernel will use copy on write. However, if a garbage collection happens to become necessary during the sending of the data in the forked off sending process, this might trigger doubled memory usage. a string or fail Starts the process with the executable given by the file name cmd (in the sense of ) with arguments in the argument list args (a list of strings). The standard input and output of the started process are connected via pipes to the calling process. The content of the string input is written to the standard input of the called process and its standard output is read and returned as a string.

All the necessary I/O multiplexing and non-blocking I/O to avoid deadlocks is done in this function.

This function properly does to wait for the termination of the child process but does not restore the original &GAP; SIGCHLD signal handler (see ). a record or fail Starts the process with the executable given by the file name cmd (in the sense of ) with arguments in the argument list args (a list of strings). The standard input, output and error of the started process are connected via pipes to the calling process. The content of the string input is written to the standard input of the called process and its standard output and error are read and returned as a record with components out and err, which are strings.

All the necessary I/O multiplexing and non-blocking I/O to avoid deadlocks is done in this function.

This function properly does to wait for the termination of the child process but does not restore the original &GAP; SIGCHLD signal handler (see ).

The functions returns either fail if an error occurred, or otherwise a record with components out and err which are bound to strings containing the full standard output and standard error of the called process, and status which is the status returned from the exiting process.

Object serialisation (Pickling) The idea of object serialisation is that one wants to store nearly arbitrary &GAP; objects to disk or transfer them over the network. To this end, one wants to convert them to a byte stream that is platform independent and can later be converted back to a copy of the same object in memory, be it in the same &GAP; process or another one maybe even on another machine. The main problem here are the vast amount of different types occurring in &GAP; and the possibly highly self-referential structure of &GAP; objects.

The IO package contains a framework to implement object serialisation and implementations for most of the basic data types in &GAP;. The framework is easily extendible to other types and takes complete care of self-references and corresponding problems. It builds upon the buffered I/O functions described in Section . We start by describing the user interface.

Result objects The following static objects are used to report about success or failure of the (un-)pickling operations: This object is returned if an error occurs. This object is returned when there is nothing to return, for example if an unpickler (see ) encounters the end of a file. This object is returned if everything went well and there is no other canonical value to return to indicate this. The only thing you can do with these special values is to compare them to each other and to other objects.

Pickling and unpickling IO&uscore;OK or IO&uscore;Error The argument f must be an open, writable File object. The object ob can be an arbitrary &GAP; object. The operation pickles or serialises the object ob and writes the result into the File object f. If everything is OK, the unique value IO&uscore;OK is returned and otherwise the unique value IO&uscore;Error. The resulting byte stream can be read again using the operation and is platform- and architecture independent. Especially the question whether a system has 32 bit or 64 bit wide words and the question of endianess does not matter.

Note that not all of &GAP;'s object types are supported but it is relatively easy to extend the system. This package supports in particular boolean values, integers, permutations, rational numbers, finite field elements, cyclotomics, strings, polynomials, rational functions, lists, records, compressed vectors and matrices over finite fields (objects are uncompressed in the byte stream but recompressed during unpickling), and straight line programs.

Self-referential objects built from records and lists are handled correctly and are restored completely with the same self-references during unpickling. IO&uscore;Error or a &GAP; object The argument f must be an open, readable File object. The operation reads from f and unpickles the next object. If an error occurs, the unique value IO&uscore;Error is returned. If the File object is at end of file, the value IO&uscore;Nothing is returned. Note that these two values are not picklable, because of their special meaning as return values of this operation here. Nothing This function clears the pickle cache. This cache stores all object pickled in the current recursive call to and is necessary to handle self-references. Usually it is not necessary to call this function explicitly. Only in the rare case (that should not happen) that a pickling or unpickling operation enters a break loop which is left by the user, the pickle cache has to be cleared explicitly using this function for later calls to and to work!

Extending the pickling framework The framework can be extended for other &GAP; object types as follows:

For pickling, a method for the operation has to be installed which does the work. If the object to be pickled has subobjects, then the first action of the method is to call the function IO&uscore;AddToPickled with the object as argument. This will put it into the pickle cache and take care of self-references. Arbitrary subobjects can then be pickled using recursive calls to the operation handing down the same File object into the recursion. The method must either return IO&uscore;Error in case of an error or IO&uscore;OK if everything goes well. Before returning, a method that has called IO&uscore;AddToPickled must call the function IO&uscore;FinalizePickled without arguments under all circumstances. If this call is missing, global data for the pickling procedure becomes corrupt!

Every pickling method must first write a 4 byte magic value such that later during unpickling of the byte stream the right unpickling method can be called (see below). Then it can write arbitrary data, however, this data should be platform- and architecture independent, and it must be possible to unpickle it later without lookahead.

Pickling methods should usually not go into a break loop, because after leaving the user has to call explicitly!

Unpickling is implemented as follows: For every 4 byte magic value there must be a function bound to that value in the record IO&uscore;Unpicklers. If the unpickling operation encounters that magic value, it calls the corresponding unpickling function. This function just gets one File object as argument. Since the magic value is already read, it can immediately start with reading and rebuilding the serialised object in memory. The method has to take care to restore the object including its type completely.

If an object type has subobjects, the unpickling function has to first create a skeleton of the object without its subobjects, then call IO&uscore;AddToUnpickled on this skeleton, before unpickling subobjects. If things are not done in this order, the handling of self-references down in the recursion will not work! An unpickling function that has called IO&uscore;AddToUnpickled at the beginning has to call IO&uscore;FinalizeUnpickled without arguments before returning under all circumstances! If this call is missing, global data for the unpickling procedure becomes corrupt!

Of course, unpickling functions can recursively call to unpickle subobjects. Apart from this, unpickling functions can use arbitrary reading functions on the File object. However, they should only read sequentially and never move the current file position pointer otherwise. An unpickling function should return the newly created object or the value IO&uscore;Error if an error occurred. They should never go into a break loop, because after leaving the user has to call explicitly!

Perhaps the best way to learn how to extend the framework is to study the code for the basic &GAP; objects in the file pkg/io/gap/pickle.gi.

Really random sources This section describes so called real random sources. It is an extension to the library mechanism of random source objects that uses the devices /dev/random and /dev/urandom available on Linux systems (and maybe on other operating systems) providing random numbers that are impossible to predict. The idea is that such sources of random numbers are useful to produce unpredictable secret keys for cryptographic applications.
The functions a real random source object or fail The first argument r must be the &GAP; filter IsRealRandomSource and the second either the string random or the string urandom. A real random source object is created that draws its random numbers from the kernel devices /dev/random and /dev/urandom respectively. Whereas /dev/urandom always provides random numbers of not guaranteed quality, the device /dev/random measures its entropy and produces guaranteed unpredictable numbers. However, it might block until enough random events (like mouse movements) have been accumulated.
A client side implementation of the HTTP protocol The IO package contains an implementation of the client side of the HTTP protocol. The basic purpose of this is of course to be able to download data from web servers from the &GAP; language. However, the HTTP protocol can perform a much bigger variety of tasks.
Functions for client side HTTP a record The first argument hostname must be a string containing the hostname of the server to connect. The second argument port must be an integer in the range from 1 to 65535 and describes the port to connect to on the server.

The function opens a TCP/IP connection to the server and returns a record conn with the following components: conn.sock is fail if an error occurs and otherwise a File object linked to the file descriptor of the socket. In case of an error, the component conn.errormsg contains an error message, it is otherwise empty. If everything went well then the component conn.host is the result from the host name lookup (see ) and the component conn.closed is set to false.

No data is sent or received on the socket in this function. a record This function performs a complete HTTP request. The first argument must be a connection record as returned by a successful call to . The argument method must be a valid HTTP request method in form of a string. The most common will be GET, POST, or HEAD. The argument uri is a string containing the URI of the request, which is given in the first line of the request. This will usually be a relative or absolute path name given to the server. The argument header must be a &GAP; record. Each bound field of this record will we transformed into one header line with the name of the component being the key and the value the value. All bound values must be strings. The argument body must either be a string or false. If it is a string, this string is sent away as the body of the request. If no string or an empty string is given, no body will be sent. The header field Content-Length is automatically created from the length of the string body. Finally, the argument target can either be false or a string. In the latter case, the body of the request answer is written to the file with the name given in target. The body component of the result will be the file name in this case. If target is false, the full body of the answer is stored into the body component of the result.

The function sends away the request and awaits the answer. If anything goes wrong during the transfer (for example if the connection is broken prematurely), then the component statuscode of the resulting record is 0 and the component status is a corresponding error message. In that case, all other fields may or may not be bound to sensible values, according to when the error occurred. If everything goes well, then statuscode and status are bound to the corresponding values coming from the request answer. statuscode is transformed into a &GAP; integer. The header of the answer is parsed, transformed into a &GAP; record, and stored into the component header of the result. The body component of the result record is set as described above. Finally, the protoversion component contains the HTTP protocol version number used by the server as a string and the boolean value closed indicates, whether or not the function has detected, that the connection has been closed by the server. Note that by default, the connection will stay open, at least for a certain time after the end of the request.

See the description of the global variable for rules how timeouts are done in this function.

Note that if the method is HEAD, then no body is expected (none will be sent anyway) and the function returns immediately with empty body. Of course, the Content-Length value in the header is as if it the request would be done with the GET method. This global variable holds a list of length two. By default, both entries are fail indicating that should never timeout and wait forever for an answer. Actually, the two values in this variable are given to the function call during I/O multiplexing. That is, the first number is in seconds and the second in milliseconds. Together they lead to a timeout for the HTTP request. If a timeout occurs, an error condition is triggered which returns a record with status code 0 and status being the timeout error message.

You can change the timeout by accessing the two entries of this write protected list variable directly. nothing Closes the connection described by the connection record conn. No error can possibly occur. a record The arguments are as the corresponding ones in the functions and respectively. This function opens an HTTP connection, tries a single HTTP request and immediately closes the connection again. The result is as for the function. If an error occurs during the opening of the connection, the statuscode value of the result is 0 and the error message is stored in the status component of the result. The previous function allows for a very simple implementation of a function that checks, whether your current &GAP; installation is up to date: nothing This function accesses a web page in St. Andrews and runs some &GAP; code from there. This code knows all the currently released versions of &GAP; and its packages. It prints out a summary and possibly suggests upgrades. If you do not want to executed code downloaded from the internet, then do not call this function.

More concretely, the page accessed is http://www.gap-system.org/Download/upgrade.html and the code executed is a single call to the function SuggestUpgrades function in the &GAP; library. nothing This function downloads the file from the given uniform resource locator URL using the HTTP protocol and reads the contents into &GAP; using .

Note that this can execute arbitrary code on your machine with the privileges of the &GAP; job running, so you should be very careful what files you download and execute. You have been warned!

Background jobs using fork This chapter describes a way to use multi-processor or multi-core machines from within &GAP;. In its current version the &GAP; system is a single threaded and single process system. However, modern operating systems allow, via the fork system call, to replicate a complete process on the same machine relatively efficiently. That is, at first after a fork the two processes actually use the same physical memory such that not much copying needs to be done. The child process is in exactly the same state as the parent process, sharing open files, network connections and the complete status of the workspace. However, whenever a page of memory is written, it is then automatically copied using new, additional physical memory, such that it behaves like a completely separate process. This method is called copy-on-write.

Thus this is a method to parallelise certain computations. Note however, that from the point of time when the fork has occurred, all further communication between the two processes has to be realised via pipes or even files.

The operations and methods described in this chapter help to use &GAP; in this way and implement certain skeletons of parallel programming to make these readily available in &GAP;. Note that this implementation has its severe limitations and should probably eventually be replaced by a proper multi-threaded version of &GAP;.

Background jobs One creates a background job with the following operation: a background job object or fail This operation creates a background job using which starts up as an identical copy of the currently running &GAP; process. In this child process the function fun is called with the argument list args. The third argument opt must be a record for options. The operation returns either an object representing the background job or fail if the startup did not work.

This operation automatically sets up two pipes for communication with the child process. This is in particular used to report the result of the function call to fun back to the parent. However, if called without the option TerminateImmediately (see below) the child process stays alive even after the completion of fun and one can submit further argument lists for subsequent calls to fun. Of course, these additional argument lists will have to be sent over a pipe to the child process. A special case is if the argument args is equal to fail, in this case the child process is started but does not automatically call fun but rather waits in an idle state until an argument list is submitted via the pipe using the operation described below.

There are two components defined which can be bound in the options record opt. One is TerminateImmediately, if this is bound to true then the child process immediately terminates after the function fun returns its result. In this case, no pipe for communication from parent to child is created since it would never be used. Note that in this case one can still get the result of the function fun using the operation described below, even when the child has already terminated, since the result is first transmitted back to the parent before termination. The following operations are available to deal with background job objects: true, false or fail This operation checks whether or not the background job represented by the object job has already finished the function call to its worker function and is now idle. If so, true is returned. If it is still running and working on the worker function, false is returned. If the background job has already terminated altogether, this operation returns fail. Note that if a child process terminates automatically after the first completion of its worker function and sending the result, then the first call to after completion will return true to indicate successful completion and all subsequent calls will return fail. true or false This operation checks whether or not the background job represented by the object job has already terminated. If so, true is returned, if not, false is returned. the result of the worker function or fail This operation waits until the worker function of the background job job has finished and the job is idle. It then returns the result of the worker function, which has automatically been transmitted to the parent process. If the child process has died before completion fail is returned. the result of the worker function or fail This operation does the same as . true or fail This submits another argument list args for another call to the worker function in the background job job. It is an error if either the background job has already terminated or if it is still busy working on the previous argument list. That is, one must only submit another argument in a situation when would return true. This is for example the case directly after a successful call to or i which did not return fail, unless the background job was created with the TerminateImmediately option set to true.

This operation returns immediately after submission, when the new argument list has been sent to the child process through a pipe. In particular, it does not await completion of the worker function for the new argument list. nothing This kills the background job represented by the object job with immediate effect. No more results can be expected from it. Note that unless one has created the background job with the TerminateImmediately option set to true one always has to call on a background job eventually for cleanup purposes. Otherwise, the background job and the connecting pipes remain alive until the parent &GAP; process terminates.

Parallel programming skeletons In this section we document the operations for the available skeletons. For a general description of these ideas see other sources. a list of results or fail The argument jobs must be a list of &GAP; functions and the argument args a list of the same length containing argument lists with which the job functions can be called. This operation starts up a background job using fork for each of the functions in jobs, calls it with the corresponding argument list in args. As soon as any of the background jobs finishes with a result, terminates all other jobs and reports the results found so far. Note that it can happen that two jobs finish at the same time in the sense that both results are received before all other jobs could be terminated. Therefore the result of is a list, in which position i is bound if and only if job number i returned a result. So in the result at least one entry is bound but it is possible that more than one entry is bound.

You can specify an overall timeout to give up the whole computation if no job finishes by setting the TimeOut component of the options record opt. In this case you have to set it to a record with two components tv_sec and tv_usec which are seconds and microseconds respectively, exactly as returned by the function. In the case of timeout an empty list is returned. a list of results or fail The argument jobs must be a list of &GAP; functions and the argument args a list of the same length containing argument lists with which the job functions can be called. This operation starts up a background job using fork for each of the functions in jobs, calls it with the corresponding argument list in args. As soon as all of the background jobs finish with a result, reports the results found. Therefore the result of is a list, in which position i is bound to the result that job number i returned.

You can specify an overall timeout to stop the whole computation if not all jobs finish in time by setting the TimeOut component of the options record opt. In this case you have to set it to a record with two components tv_sec and tv_usec which are seconds and microseconds respectively, exactly as returned by the function. In the case of timeout a list is returned in which the positions corresponding to those jobs that have already finished are bound to the respective results and the other positions are unbound. a list of results or fail This is a parallel version of the function. It applies the function worker to all elements of the list l and returns a list containing the results in corresponding positions. You have to specify the component NumberJobs in the options record opt which indicates how many background processes to start. You can optionally use the TimeOut option exactly as for , however, if a timeout occurs, returns fail.

Note that the usefulness of this operation is relatively limited, since every individual result has to be sent back over a pipe from the child process to the parent process. Therefore this only makes sense if the computation time for the worker function dominates the communication time. a value or fail This is a parallel version implementation of the classical MapReduce pattern. It applies the function map to all elements of the list l and then reduces the result using the reduce function which accepts two return values of map and returns one of them. Thus, the final result is one return value or fail if the startup of the jobs fails. You have to specify the component NumberJobs in the options record opt which indicates how many background processes to start. You can optionally use the TimeOut option exactly as for , however, if a timeout occurs, returns fail.

Note that this can be very useful because quite often the cumulated computation time for all the worker function calls dominates the communication time for a single result. Note that the next parallel skeleton is a worker farm which is described in the following section.

Worker farms The parallel skeleton of a worker farm is basically nothing but a bunch of background jobs all with the same worker function and all eagerly waiting for work. The only additional concepts needed are an input and an output queue. The input queue contains argument lists and the output queue pairs of argument lists and results.

One creates a worker farm with the following operation: an object representing the worker farm or fail This operation creates a worker farm with the worker function fun and sets up its input and output queue. An object representing the farm is returned unless not all jobs could be started up in which case fail is returned. After startup all background jobs in the farm are idle. The only valid option in the options record opt is NumberJobs and it must be bound to the number of worker jobs in the farm, a positive integer. The following operations are for worker farm objects: nothing This operation called on a worker farm object wf administrates the input and output queues of the worker farm. In particular it checks whether new results are available from the workers and if so it appends them to the output queue. If jobs are idle and the input queue is non-empty, argument lists from the input queue are sent to the idle jobs and removed from the input queue.

This operation must be called regularly to keep up the communication with the clients. It uses select and so does not block if the boolean argument block is set to false. However, if larger chunks of data has to be sent or received this operation might need some time to return.

If the boolean argument block is set to true then the blocks until at least one job has returned a result. This can be used to wait for the termination of all tasks without burning CPU cycles in the parent job. One would repeatedly call with block set to true and after each such call check with whether all tasks are done. Note that one should no longer call with block set to true once this is the case since then it would block forever.

This operation is called automatically by most of the following operations. nothing This operation terminates all background jobs in the farm wf, which cannot be used subsequently. One should always call this operation when the worker farm is no longer needed to free resources. true or false This operation returns true if all background jobs in the worker farm wf are idle. This means, that all tasks which have previously been submitted using have been completed and their result been appended to the output queue. The operation is automatically called before the execution of . nothing This operation submits a task in the form of an argument list for the worker function to the worker farm. It is appended at the end of the input queue. The operation is automatically called after the execution of , giving the farm a chance to actually send the work out to the worker background jobs. nothing This operation collects all results from the output queue of the worker farm. The output queue is empty after this function returns. The results are reported as a list of pairs, each pair has the input argument list as first component and the output object as second component.

The operation is automatically called before the execution of , giving the farm a chance to actually receive some more results from the worker background jobs.

I/O multiplexing
Introduction Whenever one needs to do input/output on more than one connection (file descriptor) at a time, some code is needed to organise the I/O multiplexing. Due to the single-threaded nature of the current &GAP; language one has to use and some buffering and queueing to organise this. This chapter describes a relative generic implementation of I/O-multiplexing using so-called objects. The basic idea is that an object handles lots of I/O connections at the same time and maintains a buffer for each of them. There is a very simple protocol that marks chunks of data (called messages) and whenever a message has been received completely it is collected in the input queue of the , marked with the number of the connection it came from. Rather than sending a message away in one go, one would always schedule it for sending by appending it to the output queue. The operation , when called often enough, will then make sure that the message is sent away eventually.
The operations for IOHub objects In this section, we simply describe the functions and operations to create, use and destroy objects. an object This creates a new object at first without any open connections. a positive integer This operation adds a new connection to the object h. The arguments i and o must be Unix file descriptors or 0 and i must be open for reading if it is positive and o must be open for writing if it is positive. It is allowed that both file descriptors are equal, but they may not both be equal to 0. The operation returns a positive integer which is the number under which this new connection will be administrated in the object. Note that this number is specific to the object h.

From the moment these file descriptors are registered with the object, every subsequent call to will try to do input and output on them. This means in particular that the other side of this connection should be in the same initial state of the protocol. Usually this will be achieved by them being added as a new connection to a corresponding object on the other side at the same time.

See also below. nothing The argument h must be an object and nr the number of a connection which was previously returned by . The corresponding connection is closed and removed from the . a Unix file descriptor or fail The argument h must be an object, addr an IP address or host name as a string and port a port number (see also ). This operation creates a new socket, binds it to the IP address and port and attaches it to the object. From this moment on the operation will accept new bidirectional TCP/IP connections on that socket and add them to h. The operation returns either the file descriptor of the new socket or fail if an error occurred. nothing The argument h must be an object. Any server socket which was attached to h is shut down, so no new connections will be accepted. nothing The argument h must be an object. All connections of h will be closed using and any serving socket will be shut down using . The object will not be usable any more after this call. a positive integer or fail The argument h must be an object. The object h must have a serving socket attached to it via , otherwise fail is returned and nothing happens. One more connection is accepted through the serving socket. It is added as a new bidirectional TCP/IP connection to the object and the new connection number is returned. Note first that this operation blocks until a new connection comes in. Note furthermore that this operation is usually called automatically in whenever a new connection has come in, which is reported in the internal call. So usually, the client code does not have to call this operation at all. true or fail The argument h must be an object, nr must be a positive integer which is the number of an open connection of h which can be used for output. The argument st must be a &GAP; string. This operation appends the message st to the end of the output queue for the connection nr. Note that at this stage no output is actually performed automatically. One has to call subsequently to actually send the message away. a list of length 2 The argument h must be an object, nr must be an integer. If nr is positive, this operation returns the earliest message which has come in from connection number nr and has not yet been returned by before. This message is then removed from the input queue. If there is no such message, then false is returned. A message is returned as a plain list of length 2 where the first entry is the connection number it came from and the second entry is a string containing the message itself. If nr is equal to 0 then the first message in the input queue from any connection is returned or false if there is no message in the input queue. a connection number or fail The argument h must be an object, the arguments addr and port must be an address/port pair as used in , so address can either be a host name or an IP address and port is a port number. This operation opens a new TCP connection to the address and port specified, adds a new bidirectional connection to the h using and returns the connection number specific to the object h. If anything goes wrong, fail is returned. a list The argument h must be an object. This returns the internal object for the output queue. Its elements are pairs where the first entry is the connection number where it is going to be sent and the second entry is the message as a string. Only modify this list if you really know what you are doing. a list The argument h must be an object. This returns the internal object for the input queue. Its elements are pairs where the first entry is the connection number from where the message was received and the second entry is the message as a string. Only modify this list if you really know what you are doing. true or false or fail The argument h must be an object, and the optional second argument block must be true or false. This operation uses to decide which of the file descriptors belonging to the connections of h are ready to read or write. All file descriptors which are ready are served, possibly updating the input and output queues. A possible serving socket is also served accepting a new connection if there is one. The operation loops until no more file descriptors are ready. It returns true if some I/O was performed and false if not. It returns fail if the is already shut down. The second argument block indicates whether or not should block until some I/O has taken place. If this argument is omitted then false (non-blocking operation) is the default.

Note that broken connections are silently closed.

Examples There is an example hash server in the file examples/hashserver.g.
Examples of usage For larger examples see the example directory of the package. You find there a small server using the TCP/IP protocol and a corresponding client and another small server using the UDP protocol and a corresponding client.

Further, there is an example for the usage of File objects, that read from or write to strings.

Another example there shows starting up a child process and piping a few megabytes through it using .

In the following, we present a few explicit, interactive short examples for the usage of the functions in this package. Note that you have to load the IO package with the command LoadPackage("IO"); before trying these examples.

Writing and reading a file The following sequence of commands opens a file with name guck and writes some things to it: gap> f := IO_File("guck","w"); <file fd=3 wbufsize=65536 wdata=0> gap> IO_Write(f,"Hello world\n"); 12 gap> IO_WriteLine(f,"Hello world2!"); 14 gap> IO_Write(f,12345); 5 gap> IO_Flush(f); true gap> IO_Close(f); true There is nothing special about this, the numbers are numbers of bytes written. Note that only after the command the data is actually written to disk. Before that, it resides in the write buffer of the file. Note further, that the call here would not have been necessary, since the call flushes the buffer anyway.

The file can again be read with the following sequence of commands: gap> f := IO_File("guck","r"); <file fd=3 rbufsize=65536 rpos=1 rdata=0> gap> IO_Read(f,10); "Hello worl" gap> IO_ReadLine(f); "d\n" gap> IO_ReadLine(f); "Hello world2!\n" gap> IO_ReadLine(f); "12345" gap> IO_ReadLine(f); "" gap> IO_Close(f); true Note here that reading line-wise can only be done efficiently by using buffered I/O. You can mix calls to and to . The end of file is indicated by an empty string returned by one of the read functions.

Using filtering programs to read and write files If you want to write a big amount of data to file you might want to compress it on the fly without using much disk space. This can be achieved with the following command: gap> s := "";; for i in [1..10000] do Append(s,String(i)); od;; gap> Length(s); 38894 gap> IO_FileFilterString("guck.gz",[["gzip",["-9c"]]],s); true gap> sgz := StringFile("guck.gz");; gap> Length(sgz); 18541 gap> ss := IO_StringFilterFile([["gzip",["-dc"]]],"guck.gz");; gap> s=ss; true This sequence of commands needs that the program gzip is installed on your system.
Using filters when reading or writing files sequentially If you want to process bigger amounts of data you might not want to store all of it in a single &GAP; string. In that case you might want to access a file on disk sequentially through a filter: gap> f := IO_FilteredFile([["gzip",["-9c"]]],"guck.gz","w"); <file fd=5 wbufsize=65536 wdata=0> gap> IO_Write(f,"Hello world!\n"); 13 gap> IO_Write(f,Elements(SymmetricGroup(5)),"\n"); 1359 gap> IO_Close(f); true gap> f := IO_FilteredFile([["gzip",["-dc"]]],"guck.gz","r"); <file fd=4 rbufsize=65536 rpos=1 rdata=0> gap> IO_ReadLine(f); "Hello world!\n" gap> s := IO_ReadLine(f);; Length(s); 1359 gap> IO_Read(f,10); "" gap> IO_Close(f); true
Accessing a web page The IO package has an HTTP client implementation. Using this you can access web pages and other web downloads from within &GAP;. Here is an example: gap> r := SingleHTTPRequest("www.math.rwth-aachen.de",80,"GET", > "/~Max.Neunhoeffer/index.html",rec(),false,false);; gap> RecFields(r); [ "protoversion", "statuscode", "status", "header", "body", "closed" ] gap> r.status; "OK" gap> r.statuscode; 200 gap> r.header; rec( date := "Thu, 07 Dec 2006 22:08:22 GMT", server := "Apache/2.0.55 (Ubuntu)", last-modified := "Thu, 16 Nov 2006 00:21:44 GMT", etag := "\"2179cf-11a5-3c77f600\"", accept-ranges := "bytes", content-length := "4517", content-type := "text/html; charset=ISO-8859-1" ) gap> Length(r.body); 4517 Of course, the time stamps and exact sizes of the answer may differ when you do this.
(Un-)Pickling Assume you have some &GAP; objects you want to archive to disk grouped together. Then you might do the following: gap> r := rec( a := 1, b := "Max", c := [1,2,3] ); rec( a := 1, b := "Max", c := [ 1, 2, 3 ] ) gap> r.c[4] := r; rec( a := 1, b := "Max", c := [ 1, 2, 3, ~ ] ) gap> f := IO_File("guck","w"); <file fd=3 wbufsize=65536 wdata=0> gap> IO_Pickle(f,r); IO_OK gap> IO_Pickle(f,[(1,2,3,4),(3,4)]); IO_OK gap> IO_Close(f); true Then, to read it in again, just do: gap> f := IO_File("guck"); <file fd=3 rbufsize=65536 rpos=1 rdata=0> gap> IO_Unpickle(f); rec( a := 1, b := "Max", c := [ 1, 2, 3, ~ ] ) gap> IO_Unpickle(f); [ (1,2,3,4), (3,4) ] gap> IO_Unpickle(f); IO_Nothing gap> IO_Close(f); true Note that this works for a certain amount of builtin objects. If you want to archive your own objects or more sophisticated objects you have to use extend the functionality as explained in Section . However, it works for lists and records and they may be arbitrarily self-referential.
License This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 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 General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. gap-io-4.4.5+ds/doc/title.xml000066400000000000000000000020271264743113200157160ustar00rootroot00000000000000 IO Bindings for low level C library I/O routines 4.4.5 Max Neunhöffer

Gustav-Freytag-Straße 40
50354 Hürth
Germany
max@9hoeffer.de http://www-groups.mcs.st-and.ac.uk/~neunhoef Max Horn
AG Algebra
Mathematisches Institut
Justus-Liebig-Universität Gießen
Arndtstraße 2
35392 Gießen
Germany
max.horn@math.uni-giessen.de http://www.quendi.de/math
07/01/2016 ©right; 2005-2014 by Max Neunhöffer

This package may be distributed under the terms and conditions of the GNU Public License Version 3 or later. gap-io-4.4.5+ds/example/000077500000000000000000000000001264743113200147405ustar00rootroot00000000000000gap-io-4.4.5+ds/example/clientTCP.g000066400000000000000000000015661264743113200167450ustar00rootroot00000000000000# A little network client using TCP/IP: LoadPackage("io"); Print("Connecting via TCP/IP...\n"); s := IO_socket(IO.PF_INET,IO.SOCK_STREAM,"tcp"); res := IO_connect(s,IO_MakeIPAddressPort("127.0.0.1",8000)); if res = fail then Print("Error: ",LastSystemError(),"\n"); IO_close(s); else f := IO_WrapFD(s,IO.DefaultBufSize,IO.DefaultBufSize); IO_WriteLine(f,"Hello world!\n"); Print("Sent: Hello word!\n"); st := IO_ReadLine(f); Print("Got back: ",st); IO_Close(f); fi; s := IO_socket(IO.PF_INET,IO.SOCK_STREAM,"tcp"); res := IO_connect(s,IO_MakeIPAddressPort("127.0.0.1",8000)); if res = fail then Print("Error: ",LastSystemError(),"\n"); IO_close(s); else f := IO_WrapFD(s,IO.DefaultBufSize,IO.DefaultBufSize); IO_WriteLine(f,"QUIT\n"); Print("Sent: QUIT\n"); st := IO_ReadLine(f); Print("Got back: ",st); IO_Close(f); fi; gap-io-4.4.5+ds/example/clientUDP.g000066400000000000000000000004651264743113200167440ustar00rootroot00000000000000# A small client example using UDP: LoadPackage("io"); Print("Sending packets using UDP...\n"); s := IO_socket(IO.PF_INET,IO.SOCK_DGRAM,"udp"); IO_connect(s,IO_MakeIPAddressPort("127.0.0.1",8000)); IO_send(s,"Max",0,3,0); IO_send(s,"is",0,2,0); IO_send(s,"here!",0,5,0); IO_send(s,"QUIT",0,4,0); IO_close(s); gap-io-4.4.5+ds/example/fork.g000066400000000000000000000023001264743113200160440ustar00rootroot00000000000000# An example using fork: LoadPackage("io"); IO_InstallSIGCHLDHandler(); # install correct signal handler pid := IO_fork(); if pid < 0 then Error("Cannot fork!"); fi; if pid > 0 then # the parent Print("Did fork, now waiting for child...\n"); a := IO_WaitPid(pid,true); Print("Got ",a," as result of WaitPid.\n"); else # the child: res := IO_execv("/bin/ls",["/tmp"]); Print("execv did not work: ",res); fi; pid := IO_fork(); if pid < 0 then Error("Cannot fork!"); fi; if pid > 0 then # the parent repeat a := IO_WaitPid(pid,false); Print(".\c"); until a <> false; Print("Got ",a," as result of WaitPid.\n"); else # the child: e := IO_Environment(); e.myvariable := "xyz"; res := IO_execve("/usr/bin/env",[],IO_MakeEnvList(e)); Print("execve did not work: ",res); fi; pid := IO_fork(); if pid < 0 then Error("Cannot fork!"); fi; if pid > 0 then # the parent repeat a := IO_WaitPid(pid,false); Print(".\c"); Sleep(1); until a <> false; Print("Got ",a," as result of WaitPid.\n"); else # the child: res := IO_execvp("sleep",["5"]); Print("execvp did not work: ",res); fi; gap-io-4.4.5+ds/example/hashserver.g000066400000000000000000000030031264743113200172560ustar00rootroot00000000000000# This is an example for the usage of IOHubs: LoadPackage("orb"); HashServer := function(pt,size,addr,port,chunksize) local done,h,ht,i,l,len,p,r,todo,val; ht := HTCreate(pt,rec( treehashsize := size )); todo := []; h := IOHub(); AttachServingSocket(h,addr,port); done := false; while not(done) do DoIO(h,true); # this is blocking #Print("done\n"); while true do r := GetInput(h,0); # a pair [connection,string] #Print(r,"\n"); if r = false then break; fi; if r[2] = "exit" then done := true; break; fi; if r[2] = "gettask" then len := Length(todo); if len = 0 then l := []; elif len >= chunksize then l := todo{[len-chunksize+1..len]}; for i in [len,len-1..len-chunksize+1] do Unbind(l[i]); od; else l := ShallowCopy(todo); for i in [len,len-1..1] do Unbind(l[i]); od; fi; SubmitOutput(h,r[1],IO_Pickle(l)); else # some new points l := IO_Unpickle(r[2]); for p in l do val := HTValue(ht,p); if val = fail then HTAdd(ht,p,true); Add(todo,p); fi; od; fi; DoIO(h,false); # non-blocking! od; od; Shutdown(h); end; gap-io-4.4.5+ds/example/parlist1.g000066400000000000000000000005241264743113200166500ustar00rootroot00000000000000LoadPackage("io"); # This is silly, since the communication dominates everything timewise, # and in actual fact we serialise this. But nevertheless, it works: l := [1..1000000]*2; ll := ParListByFork(l,function(x) return x^2; end, rec( NumberJobs := 3 )); if ll <> List(l,x->x^2) then Error("did not work"); fi; gap-io-4.4.5+ds/example/parlist2.g000066400000000000000000000007421264743113200166530ustar00rootroot00000000000000LoadPackage("io"); SetInfoLevel(InfoIO,2); f := function(g) return Size(Centre(g)); end; l := AllSmallGroups(128); Print("Have ",Length(l)," small groups.\n"); t := IO_gettimeofday(); ll := ParListByFork(l,f,rec(NumberJobs := 4)); Print("Parallel time (4 jobs): ",DifferenceTimes(IO_gettimeofday(),t),"\n"); t := IO_gettimeofday(); lll := List(l,f); Print("Sequential time (1 job): ",DifferenceTimes(IO_gettimeofday(),t),"\n"); if ll <> lll then Error("did not work"); fi; gap-io-4.4.5+ds/example/parmapreduce1.g000066400000000000000000000006551264743113200176470ustar00rootroot00000000000000LoadPackage("io"); l := List([1..1000000],i->Random(1,100));; GASMAN("collect"); start := IO_gettimeofday();; a := Sum(l,Factorial); Print("Non-parallel: ",DifferenceTimes(IO_gettimeofday(),start),"\n"); for i in [2..8] do GASMAN("collect"); start := IO_gettimeofday();; b := ParMapReduceByFork(l,Factorial,\+,rec(NumberJobs := i)); Print("With ",i," jobs: ",DifferenceTimes(IO_gettimeofday(),start),"\n"); od; gap-io-4.4.5+ds/example/partakefirst1.g000066400000000000000000000007751264743113200177010ustar00rootroot00000000000000LoadPackage("io"); f := function(delay,x) Sleep(delay); return x^10; end; Print(ParTakeFirstResultByFork([f,f,f],[[2,17],[3,18],[4,19]]),"\n"); Print(ParTakeFirstResultByFork([f,f,f],[[4,17],[3,18],[4,19]]),"\n"); Print(ParTakeFirstResultByFork([f,f,f],[[4,17],[3,18],[2,19]]),"\n"); # A race condition: Print(ParTakeFirstResultByFork([f,f,f],[[1,1],[1,2],[1,3]]),"\n"); Print(ParTakeFirstResultByFork([f,f,f],[[1,1],[1,2],[1,3]]),"\n"); Print(ParTakeFirstResultByFork([f,f,f],[[1,1],[1,2],[1,3]]),"\n"); gap-io-4.4.5+ds/example/partakefirst2.g000066400000000000000000000024761264743113200177020ustar00rootroot00000000000000LoadPackage("io"); # f an FpGroup: TryFinite := function(f) local ct; ct := CosetTable(f,TrivialSubgroup(f):silent); if ct <> fail then return Length(ct[1]); fi; Print("Finite: looping\n"); while true do ct := ct; od; end; TryInfinite := function(f) local ab,h,l; ab := AbelianInvariants(f); if 0 in ab then return infinity; fi; l := LowIndexSubgroupsFpGroupIterator(f,40); while not(IsDoneIterator(l)) do h := NextIterator(l); ab := AbelianInvariants(h); if 0 in ab then return infinity; fi; od; Print("Infinite: looping\n"); while true do ab := ab; od; end; f := FreeGroup(2); Print(ParTakeFirstResultByFork([TryFinite,TryInfinite],[[f],[f]], rec( TimeOut := rec( tv_sec := 60, tv_usec := 0 ) )),"\n"); f := FreeGroup("a","b"); a := f.a; b := f.b; rels := [ a^2, b^3, a*b*a*b*a*b*a*b*a*b*a*b*a*b*a*b*a*b*a*b*a*b, a^-1*b^-1*a*b*a^-1*b^-1*a*b*a^-1*b^-1*a*b*a^-1*b^-1*a*b*a^-1*b^-1*a*b*a^ -1*b^-1*a*b, a*b*a*b*a*b^-1*a*b*a*b*a*b^-1*a*b*a*b*a*b^-1*a*b*a*b*a*b^ -1*a*b*a*b*a*b^-1*a*b*a*b*a*b^-1, a*b*a*b*a*b^-1*a*b^-1*a*b*a*b*a*b^ -1*a*b^-1*a*b*a*b*a*b^-1*a*b^-1*a*b*a*b*a*b^-1*a*b^-1*a*b*a*b*a*b^-1*a*b^-1 ]; g := f/rels; # this is M12 Print(ParTakeFirstResultByFork([TryFinite,TryInfinite],[[g],[g]], rec( TimeOut := rec( tv_sec := 60, tv_usec := 0 ) )),"\n"); gap-io-4.4.5+ds/example/parworkerfarm1.g000066400000000000000000000004541264743113200200560ustar00rootroot00000000000000LoadPackage("io"); l := List([1..100],x->Random(1,1000));; wf := ParWorkerFarmByFork(x->x^2,rec(NumberJobs := 4)); for i in [1..Length(l)] do Submit(wf,[l[i]]); od; while not(IsIdle(wf)) do DoQueues(wf,true); Print(".\c"); od; Print("\n"); result := Pickup(wf); Kill(wf); Print(l,"\n",result,"\n"); gap-io-4.4.5+ds/example/popen2.g000066400000000000000000000007471264743113200163230ustar00rootroot00000000000000# Pipes some megabytes through /bin/cat: LoadPackage("io"); s := "Max"; for i in [1..25] do Append(s,s); od; Print("Have string of length ",Length(s),".\n"); p := IO_Popen2("/bin/cat",[]); IO_SendStringBackground(p.stdin,s); # We want that /bin/cat terminates after the child has sent everything: IO_Close(p.stdin); t := IO_ReadUntilEOF(p.stdout); IO_Close(p.stdout); Print("Have read string!\n"); if s <> t then Print("Alert: Received string not identical to original one!\n"); fi; gap-io-4.4.5+ds/example/serverTCP.g000066400000000000000000000015631264743113200167720ustar00rootroot00000000000000# A small example for a network server: LoadPackage("io"); Print("Waiting for TCP/IP connections...\n"); s := IO_socket(IO.PF_INET,IO.SOCK_STREAM,"tcp"); IO_bind(s,IO_MakeIPAddressPort("127.0.0.1",8000)); IO_listen(s,5); # Allow a backlog of 5 connections terminate := false; repeat # We accept connections from everywhere: t := IO_accept(s,IO_MakeIPAddressPort("0.0.0.0",0)); Print("Got connection...\n"); f := IO_WrapFD(t,IO.DefaultBufSize,IO.DefaultBufSize); repeat line := IO_ReadLine(f); if line <> "" and line <> fail then Print("Got line: ",line); IO_Write(f,line); IO_Flush(f); if line = "QUIT\n" then terminate := true; fi; fi; until line = "" or line = fail; Print("Connection terminated.\n"); IO_Close(f); until terminate; IO_close(s); gap-io-4.4.5+ds/example/serverUDP.g000066400000000000000000000005161264743113200167710ustar00rootroot00000000000000# A small server example using UDP: LoadPackage("io"); Print("Waiting for UDP packets...\n"); s := IO_socket(IO.PF_INET,IO.SOCK_DGRAM,"udp"); IO_bind(s,IO_MakeIPAddressPort("127.0.0.1",8000)); repeat b := ""; l := IO_recv(s,b,0,80,0); Print("Received ",l," bytes: ",b{[1..l]},"\n"); until b{[1..l]} = "QUIT"; IO_close(s); gap-io-4.4.5+ds/example/stringIO.g000066400000000000000000000011611264743113200166450ustar00rootroot00000000000000# A few examples for IO to and from strings: # Click this into a GAP: LoadPackage("io"); # Reading from a string: s := "A long string\nMax is here!\nHello world"; f := IO_WrapFD(-1,s,false); IO_ReadLine(f); f; IO_Read(f,2); f; IO_ReadLines(f); f; IO_ReadLines(f); IO_ReadUntilEOF(f); IO_ReadLine(f); IO_Close(f); # Writing into a string: b := "Anfang\n"; f2:= IO_WrapFD(-1,false,b); IO_WriteLine(f2,"Max"); f2; IO_GetWBuf(f2); IO_Write(f2,"Hi there","\n","\c",1234,2/3,"\n"); f2; IO_Write(f2,Elements(SymmetricGroup(3)),"\n"); l := ["a","b","c"]; IO_WriteLines(f2,l); IO_GetWBuf(f2); f2; IO_Close(f2); f2; IO_GetWBuf(f2); gap-io-4.4.5+ds/gap/000077500000000000000000000000001264743113200140545ustar00rootroot00000000000000gap-io-4.4.5+ds/gap/background.gd000066400000000000000000000110271264743113200165100ustar00rootroot00000000000000############################################################################# ## ## background.gd GAP 4 package IO ## Max Neunhoeffer ## ## Copyright (C) 2006-2011 by Max Neunhoeffer ## This file is free software, see license information at the end. ## ## This file contains declarations for background processes using fork. ## # The types for background jobs by fork: BindGlobal("BackgroundJobsFamily", NewFamily("BackgroundJobsFamily")); DeclareCategory("IsBackgroundJob", IsComponentObjectRep and IsAttributeStoringRep); DeclareRepresentation("IsBackgroundJobByFork", IsBackgroundJob, ["pid", "childtoparent", "parenttochild", "result", "terminated"]); BindGlobal("BGJobByForkType", NewType(BackgroundJobsFamily, IsBackgroundJobByFork)); # Some helpers for times: DeclareGlobalFunction("DifferenceTimes"); DeclareGlobalFunction("CompareTimes"); # The constructor: DeclareOperation("BackgroundJobByFork", [IsFunction, IsObject]); DeclareOperation("BackgroundJobByFork", [IsFunction, IsObject, IsRecord]); DeclareGlobalVariable("BackgroundJobByForkOptions"); DeclareGlobalFunction("BackgroundJobByForkChild"); # The operations/attributes/properties: DeclareOperation("IsIdle", [IsBackgroundJob]); DeclareOperation("HasTerminated", [IsBackgroundJob]); DeclareOperation("WaitUntilIdle", [IsBackgroundJob]); DeclareOperation("Kill", [IsBackgroundJob]); DeclareOperation("Pickup", [IsBackgroundJob]); DeclareOperation("Submit", [IsBackgroundJob, IsObject]); # Parallel skeletons: DeclareGlobalVariable("ParTakeFirstResultByForkOptions"); DeclareOperation("ParTakeFirstResultByFork", [IsList, IsList]); DeclareOperation("ParTakeFirstResultByFork", [IsList, IsList, IsRecord]); # Arguments are: # list of job functions # list of argument lists # options record DeclareGlobalVariable("ParDoByForkOptions"); DeclareOperation( "ParDoByFork", [IsList, IsList]); DeclareOperation( "ParDoByFork", [IsList, IsList, IsRecord]); # Arguments are: # list of job functions # list of argument lists # options record DeclareGlobalFunction("ParMapReduceWorker"); DeclareGlobalVariable("ParMapReduceByForkOptions"); DeclareOperation("ParMapReduceByFork", [IsList, IsFunction, IsFunction, IsRecord]); # Arguments are: # list to work on # map function # reduce function (taking two arguments) # options record DeclareGlobalFunction("ParListWorker"); DeclareGlobalVariable("ParListByForkOptions"); DeclareOperation("ParListByFork", [IsList, IsFunction, IsRecord]); # Arguments are: # list to work on # map function # options record # The types for worker farms by fork: BindGlobal("WorkerFarmsFamily", NewFamily("WorkerFarmsFamily")); DeclareCategory("IsWorkerFarm", IsComponentObjectRep and IsAttributeStoringRep); DeclareRepresentation("IsWorkerFarmByFork", IsWorkerFarm, ["jobs", "inqueue", "outqueue", "whodoeswhat"]); BindGlobal("WorkerFarmByForkType", NewType(WorkerFarmsFamily, IsWorkerFarmByFork)); DeclareGlobalVariable("ParWorkerFarmByForkOptions"); DeclareOperation("ParWorkerFarmByFork", [IsFunction, IsRecord]); # Arguments are: # worker function # options record # # This creates a new object of type "IsWorkerFarmByFork". DeclareOperation("DoQueues", [IsWorkerFarmByFork, IsBool]); DeclareOperation("Kill", [IsWorkerFarmByFork]); DeclareOperation("Submit", [IsWorkerFarmByFork, IsList]); DeclareOperation("IsIdle", [IsWorkerFarmByFork]); DeclareOperation("Pickup", [IsWorkerFarmByFork]); # Semantics: # Starts some background jobs, maintains an "in" and an "out" queue. # DoQueues feeds idle jobs from the input queue and gets results # from them for the output queue. # Kill and IsIdle work on all workers at the same time. Submit queues # new jobs to the input queue and Pickup fetches all from the current # output queue (pairs of the form [arglist,result]). ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 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 General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## gap-io-4.4.5+ds/gap/background.gi000066400000000000000000000472751264743113200165330ustar00rootroot00000000000000############################################################################# ## ## background.gi GAP 4 package IO ## Max Neunhoeffer ## ## Copyright (C) 2006-2011 by Max Neunhoeffer ## ## This file is free software, see license information at the end. ## ## This file contains the implementations for background jobs using fork. ## InstallGlobalFunction(DifferenceTimes, function(t1, t2) local x; x := (t1.tv_sec*1000000+t1.tv_usec) - (t2.tv_sec*1000000+t2.tv_usec); return rec(tv_usec := x mod 1000000, tv_sec := (x - x mod 1000000) / 1000000); end); InstallGlobalFunction(CompareTimes, function(t1, t2) local a,b; a := t1.tv_sec * 1000000 + t1.tv_usec; b := t2.tv_sec * 1000000 + t2.tv_usec; if a < b then return -1; elif a > b then return 1; else return 0; fi; end); InstallMethod(BackgroundJobByFork, "for a function and a list", [IsFunction, IsObject], function(fun, args) return BackgroundJobByFork(fun, args, rec()); end ); InstallValue(BackgroundJobByForkOptions, rec( TerminateImmediately := false, BufferSize := 8192, )); InstallMethod(BackgroundJobByFork, "for a function, a list and a record", [IsFunction, IsObject, IsRecord], function(fun, args, opt) local j, n; IO_InstallSIGCHLDHandler(); for n in RecNames(BackgroundJobByForkOptions) do if not(IsBound(opt.(n))) then opt.(n) := BackgroundJobByForkOptions.(n); fi; od; j := rec( ); j.childtoparent := IO_pipe(); if j.childtoparent = fail then Info(InfoIO, 1, "Could not create pipe."); return fail; fi; if opt.TerminateImmediately then j.parenttochild := false; else j.parenttochild := IO_pipe(); if j.parenttochild = fail then IO_close(j.childtoparent.toread); IO_close(j.childtoparent.towrite); Info(InfoIO, 1, "Could not create pipe."); return fail; fi; fi; j.pid := IO_fork(); if j.pid = fail then Info(InfoIO, 1, "Could not fork."); return fail; fi; if j.pid = 0 then # we are in the child: IO_close(j.childtoparent.toread); j.childtoparent := IO_WrapFD(j.childtoparent.towrite, false, opt.BufferSize); if j.parenttochild <> false then IO_close(j.parenttochild.towrite); j.parenttochild := IO_WrapFD(j.parenttochild.toread, opt.BufferSize, false); fi; BackgroundJobByForkChild(j, fun, args); IO_exit(0); # just in case fi; # Here we are in the parent: IO_close(j.childtoparent.towrite); j.childtoparent := IO_WrapFD(j.childtoparent.toread, opt.BufferSize, false); if j.parenttochild <> false then IO_close(j.parenttochild.toread); j.parenttochild := IO_WrapFD(j.parenttochild.towrite, false, opt.BufferSize); fi; j.terminated := false; j.result := false; j.idle := args = fail; Objectify(BGJobByForkType, j); return j; end ); InstallGlobalFunction(BackgroundJobByForkChild, function(j, fun, args) local ret; while true do # will be left by break if args <> fail then # the case to make an as yet idle worker ret := CallFuncList(fun, args); IO_Pickle(j.childtoparent, ret); IO_Flush(j.childtoparent); fi; if j.parenttochild = false then break; fi; args := IO_Unpickle(j.parenttochild); if not(IsList(args)) then break; fi; od; IO_Close(j.childtoparent); if j.parenttochild <> false then IO_Close(j.parenttochild); fi; IO_exit(0); end); InstallMethod(IsIdle, "for a background job by fork", [IsBackgroundJobByFork], function(j) if j!.terminated then return fail; fi; # Note that we have to check every time, since the job might have # terminated in the meantime! if IO_HasData(j!.childtoparent) then j!.result := IO_Unpickle(j!.childtoparent); if j!.result = IO_Nothing or j!.result = IO_Error then j!.result := fail; j!.terminated := true; j!.idle := fail; IO_Close(j!.childtoparent); if j!.parenttochild <> false then IO_Close(j!.parenttochild); fi; IO_WaitPid(j!.pid,true); return fail; fi; j!.idle := true; return true; fi; return j!.idle; end); InstallMethod(HasTerminated, "for a background job by fork", [IsBackgroundJobByFork], function(j) if j!.terminated then return true; fi; return IsIdle(j) = fail; end); InstallMethod(WaitUntilIdle, "for a background job by fork", [IsBackgroundJobByFork], function(j) local fd,idle,l; idle := IsIdle(j); if idle = true then return j!.result; fi; if idle = fail then return fail; fi; fd := IO_GetFD(j!.childtoparent); l := [fd]; IO_select(l,[],[],false,false); j!.result := IO_Unpickle(j!.childtoparent); if j!.result = IO_Nothing or j!.result = IO_Error then j!.result := fail; j!.terminated := true; j!.idle := fail; IO_Close(j!.childtoparent); if j!.parenttochild <> false then IO_Close(j!.parenttochild); fi; IO_WaitPid(j!.pid,true); return fail; fi; j!.idle := true; if j!.parenttochild = false then IO_Close(j!.childtoparent); IO_WaitPid(j!.pid,true); j!.terminated := true; fi; return j!.result; end); InstallMethod(Kill, "for a background job by fork", [IsBackgroundJobByFork], function(j) if j!.terminated then return; fi; IO_kill(j!.pid,IO.SIGTERM); IO_Close(j!.childtoparent); if j!.parenttochild <> false then IO_Close(j!.parenttochild); fi; IO_WaitPid(j!.pid,true); j!.idle := fail; j!.terminated := true; j!.result := fail; end); InstallMethod(ViewObj, "for a background job by fork", [IsBackgroundJobByFork], function(j) local idle; Print(""); elif idle = fail then Print(" already terminated>"); else Print(" busy>"); fi; end); InstallMethod(Pickup, "for a background job by fork", [IsBackgroundJobByFork], function(j) return WaitUntilIdle(j); end); InstallMethod(Submit, "for a background job by fork and an object", [IsBackgroundJobByFork, IsObject], function(j,o) local idle,res; if j!.parenttochild = false then Error("job terminated immediately after finishing computation"); return fail; fi; idle := IsIdle(j); if idle = false then Error("job must be idle to send the next argument list"); return fail; elif idle = fail then Error("job has already terminated"); return fail; fi; res := IO_Pickle(j!.parenttochild,o); if res <> IO_OK then Info(InfoIO, 1, "problems sending argument list", res); return fail; fi; IO_Flush(j!.parenttochild); j!.idle := false; return true; end); InstallMethod(ParTakeFirstResultByFork, "for two lists", [IsList, IsList], function(jobs, args) return ParTakeFirstResultByFork(jobs, args, rec()); end); InstallValue( ParTakeFirstResultByForkOptions, rec( TimeOut := rec(tv_sec := false, tv_usec := false), )); # Hack for old windows binary: if not(IsBound(IO_gettimeofday)) then IO_gettimeofday := function() return rec( tv_sec := 0, tv_usec := 0 ); end; fi; InstallMethod(ParTakeFirstResultByFork, "for two lists and a record", [IsList, IsList, IsRecord], function(jobs, args, opt) local answered,answers,i,j,jo,n,pipes,r; if not(ForAll(jobs,IsFunction) and ForAll(args,IsList) and Length(jobs) = Length(args)) then Error("jobs must be a list of functions and args a list of lists, ", "both of the same length"); return fail; fi; for n in RecNames(ParTakeFirstResultByForkOptions) do if not(IsBound(opt.(n))) then opt.(n) := ParTakeFirstResultByForkOptions.(n); fi; od; n := Length(jobs); jo := EmptyPlist(n); for i in [1..n] do jo[i] := BackgroundJobByFork(jobs[i],args[i], rec(ImmediatelyTerminate := true)); if jo[i] = fail then for j in [1..i-1] do Kill(jo[i]); od; Info(InfoIO, 1, "Could not start all background jobs."); return fail; fi; od; pipes := List(jo,j->IO_GetFD(j!.childtoparent)); r := IO_select(pipes,[],[],opt.TimeOut.tv_sec,opt.TimeOut.tv_usec); answered := []; answers := EmptyPlist(n); for i in [1..n] do if pipes[i] = fail then Kill(jo[i]); Info(InfoIO,2,"Child ",jo[i]!.pid," has been terminated."); else Add(answered,i); fi; od; Info(InfoIO,2,"Getting answers..."); for i in answered do answers[i] := WaitUntilIdle(jo[i]); Info(InfoIO,2,"Child ",jo[i]!.pid," has terminated with answer."); Kill(jo[i]); # this is to cleanup data structures od; return answers; end); InstallMethod(ParDoByFork, "for two lists", [IsList, IsList], function(jobs, args) return ParDoByFork(jobs, args, rec()); end); InstallValue( ParDoByForkOptions, rec( TimeOut := rec(tv_sec := false, tv_usec := false), )); InstallMethod(ParDoByFork, "for two lists and a record", [IsList, IsList, IsRecord], function(jobs, args, opt) local cmp,diff,fds,i,j,jo,jobnr,n,now,pipes,r,results,start; if not(ForAll(jobs,IsFunction) and ForAll(args,IsList) and Length(jobs) = Length(args)) then Error("jobs must be a list of functions and args a list of lists, ", "both of the same length"); return fail; fi; for n in RecNames(ParDoByForkOptions) do if not(IsBound(opt.(n))) then opt.(n) := ParDoByForkOptions.(n); fi; od; n := Length(jobs); jo := EmptyPlist(n); for i in [1..n] do jo[i] := BackgroundJobByFork(jobs[i],args[i], rec(ImmediatelyTerminate := true)); if jo[i] = fail then for j in [1..i-1] do Kill(jo[i]); od; Info(InfoIO, 1, "Could not start all background jobs."); return fail; fi; od; pipes := List(jo,j->IO_GetFD(j!.childtoparent)); results := EmptyPlist(n); start := IO_gettimeofday(); Info(InfoIO, 2, "Started ", n, " jobs..."); while true do fds := EmptyPlist(n); jobnr := EmptyPlist(n); for i in [1..n] do if not(IsBound(results[i])) then Add(fds,pipes[i]); Add(jobnr,i); fi; od; if Length(fds) = 0 then break; fi; if opt.TimeOut.tv_sec = false then r := IO_select(fds,[],[],false,false); else now := IO_gettimeofday(); diff := DifferenceTimes(now,start); cmp := CompareTimes(opt.TimeOut, diff); if cmp <= 0 then for i in [1..n] do Kill(jo[i]); od; Info(InfoIO, 2, "Timeout occurred, all jobs killed."); return results; fi; diff := DifferenceTimes(opt.TimeOut, diff); r := IO_select(fds, [], [], diff.tv_sec, diff.tv_usec); fi; for i in [1..Length(fds)] do if fds[i] <> fail then j := jobnr[i]; results[j] := WaitUntilIdle(jo[j]); Info(InfoIO,2,"Child ",jo[j]!.pid, " has terminated with answer."); Kill(jo[j]); # this is to cleanup data structures fi; od; od; return results; end); InstallValue(ParMapReduceByForkOptions, rec( TimeOut := rec(tv_sec := false, tv_usec := false), )); InstallGlobalFunction(ParMapReduceWorker, function(l, what, map, reduce) local res,i; res := map(l[what[1]]); for i in what{[2..Length(what)]} do res := reduce(res,map(l[i])); od; return res; end); InstallMethod(ParMapReduceByFork, "for a list, two functions and a record", [IsList, IsFunction, IsFunction, IsRecord], function(l, map, reduce, opt) local args,i,jobs,m,n,res,res2,where; for n in RecNames(ParMapReduceByForkOptions) do if not(IsBound(opt.(n))) then opt.(n) := ParMapReduceByForkOptions.(n); fi; od; if not(IsBound(opt.NumberJobs)) then Error("Need component NumberJobs in options record"); return fail; fi; if Length(l) = 0 then Error("List to work on must have length at least 1"); return fail; fi; n := opt.NumberJobs; if Length(l) < n or n = 1 then return ParMapReduceWorker(l,[1..Length(l)],map,reduce); fi; m := QuoInt(Length(l),n); # is at least 1 by now jobs := ListWithIdenticalEntries(n, ParMapReduceWorker); args := EmptyPlist(n); where := 0; for i in [1..n-1] do args[i] := [l,[where+1..where+m],map,reduce]; where := where+m; od; args[n] := [l,[where+1..Length(l)],map,reduce]; res := ParDoByFork(jobs,args,opt); # hand down timeout if not(Length(res) = n and ForAll([1..n],x->IsBound(res[x]))) then Info(InfoIO, 1, "Timeout in ParMapReduceByFork"); return fail; fi; res2 := reduce(res[1],res[2]); # at least 2 jobs! for i in [3..n] do res2 := reduce(res2,res[i]); od; return res2; end); InstallValue(ParListByForkOptions, rec( TimeOut := rec(tv_sec := false, tv_usec := false), )); InstallGlobalFunction(ParListWorker, function(l, what, map) local res,i; res := EmptyPlist(Length(what)); for i in what do res[Length(res)+1] := map(l[i]); od; return res; end); InstallMethod(ParListByFork, "for a list, two functions and a record", [IsList, IsFunction, IsRecord], function(l, map, opt) local args,i,jobs,m,n,res,where; for n in RecNames(ParListByForkOptions) do if not(IsBound(opt.(n))) then opt.(n) := ParListByForkOptions.(n); fi; od; if not(IsBound(opt.NumberJobs)) then Error("Need component NumberJobs in options record"); return fail; fi; if Length(l) = 0 then return []; fi; n := opt.NumberJobs; if n = 1 then return List(l,map); fi; if Length(l) < n then n := Length(l); fi; m := QuoInt(Length(l),n); # is at least 1 by now jobs := ListWithIdenticalEntries(n, ParListWorker); args := EmptyPlist(n); where := 0; for i in [1..n-1] do args[i] := [l,[where+1..where+m],map]; where := where+m; od; args[n] := [l,[where+1..Length(l)],map]; res := ParDoByFork(jobs,args,opt); # hand down timeout if not(Length(res) = n and ForAll([1..n],x->IsBound(res[x]))) then Info(InfoIO, 1, "Timeout in ParListByFork"); return fail; fi; return Concatenation(res); end); InstallValue(ParWorkerFarmByForkOptions, rec( )); InstallMethod(ParWorkerFarmByFork, "for a function and a record", [IsFunction, IsRecord], function(worker, opt) local f,i,j,n; for n in RecNames(ParWorkerFarmByForkOptions) do if not(IsBound(opt.(n))) then opt.(n) := ParWorkerFarmByForkOptions.(n); fi; od; if not(IsBound(opt.NumberJobs)) then Error("Need component NumberJobs in options record"); return fail; fi; n := opt.NumberJobs; f := rec( jobs := EmptyPlist(n), inqueue := [], outqueue := [], whodoeswhat := EmptyPlist(n) ); # Now create the background jobs: for i in [1..n] do f.jobs[i] := BackgroundJobByFork(worker,fail,rec()); if f.jobs[i] = fail then for j in [1..i-1] do Kill(f.jobs[i]); od; Info(InfoIO, 1, "Could not start all background jobs."); return fail; fi; od; return Objectify(WorkerFarmByForkType, f); end); InstallMethod(Kill, "for a worker farm by fork", [IsWorkerFarmByFork], function(f) local i; for i in [1..Length(f!.jobs)] do Kill(f!.jobs[i]); od; f!.jobs := []; end); InstallMethod(ViewObj, "for a worker farm by fork", [IsWorkerFarmByFork], function(f) Print(""); else if IsIdle(f) then Print(" currently idle>"); else Print(" busy>"); fi; fi; end); InstallMethod(Submit, "for a worker farm by fork", [IsWorkerFarmByFork, IsList], function(f,args) Add(f!.inqueue,args); DoQueues(f,false); end); InstallMethod(Pickup, "for a worker farm by fork", [IsWorkerFarmByFork], function(f) local res; DoQueues(f,false); res := f!.outqueue; f!.outqueue := []; return res; end); InstallMethod(IsIdle, "for a worker farm by fork", [IsWorkerFarmByFork], function(f) DoQueues(f,false); return Length(f!.whodoeswhat) = 0; end); InstallMethod(DoQueues, "for a worker farm by fork", [IsWorkerFarmByFork, IsBool], function(f, block) local args,i,k,n,pipes,res; if Length(f!.jobs) = 0 then Error("worker farm is already terminated"); return; fi; n := Length(f!.jobs); # First send arguments to jobs which are known to be idle: if Length(f!.inqueue) > 0 then for i in [1..n] do if not(IsBound(f!.whodoeswhat[i])) then Info(InfoIO, 3, "Submitting arglist to worker #", i); args := Remove(f!.inqueue,1); Submit(f!.jobs[i],args); f!.whodoeswhat[i] := args; fi; if Length(f!.inqueue) = 0 then break; fi; od; fi; # Now check all jobs, see whether they have become idle, get the # results and possibly submit another task. We limit the selection # by a non-blocking select call (note that jobs known to be idle # do not show up here!): repeat pipes := List(f!.jobs,x->IO_GetFD(x!.childtoparent)); if not(block) then k := IO_select(pipes,[],[],0,0); else k := IO_select(pipes,[],[],false,false); fi; for i in [1..Length(f!.jobs)] do if pipes[i] <> fail then # Must have finished since we last looked: Info(InfoIO, 3, "Getting result from worker #", i); res := Pickup(f!.jobs[i]); Add(f!.outqueue,[f!.whodoeswhat[i],res]); Unbind(f!.whodoeswhat[i]); if Length(f!.inqueue) > 0 then Info(InfoIO, 3, "Submitting arglist to worker #", i); args := Remove(f!.inqueue,1); Submit(f!.jobs[i],args); f!.whodoeswhat[i] := args; fi; fi; od; until k = 0 or block; end); ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 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 General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## gap-io-4.4.5+ds/gap/forkparallel.g000066400000000000000000000047021264743113200167050ustar00rootroot00000000000000DeclareInfoClass("InfoIO"); DoChild := function( pipefd, func, arglist ) local file,ppid,ret; ppid := IO_getppid(); ret := CallFuncList(func,arglist); file := IO_WrapFD(pipefd,false,1024); IO_Pickle(file,ret); IO_Close(file); IO_exit(0); end; DoParallelOptions := rec( TimeOutSecs := false, TimeOutuSecs := false, ); DoParallelByFork := function(jobs,opt) local answered,answers,file,i,j,n,pid,pids,pipes,pipescopy,r; if not(IsEvenInt(Length(jobs))) or Length(jobs) < 4 or not(IsRecord(opt)) then Error(Concatenation("Usage: DoParallelByFork(jobs,opt); where ", "jobs is [func,arglist{,func,arglist})")); return fail; fi; IO_InstallSIGCHLDHandler(); for n in RecNames(DoParallelOptions) do if not(IsBound(opt.(n))) then opt.(n) := DoParallelOptions.(n); fi; od; n := Length(jobs)/2; pipes := EmptyPlist(n); for i in [1..n] do pipes[i] := IO_pipe(); if pipes[i] = fail then for j in [1..i-1] do IO_close(pipes[j].towrite); IO_close(pipes[j].toread); od; Error("Cannot make pipes"); fi; od; pids := EmptyPlist(n); for i in [1..n] do pid := IO_fork(); if pid = 0 then # we are in the child: for j in [1..n] do if j <> i then IO_close(pipes[j].towrite); IO_close(pipes[j].toread); else IO_close(pipes[j].toread); fi; od; DoChild( pipes[i].towrite, jobs[2*i-1], jobs[2*i] ); IO_exit(0); fi; pids[i] := pid; Info(InfoIO,2,"Started child, pid=",pid); IO_close(pipes[i].towrite); od; pipes := List(pipes,x->x.toread); pipescopy := ShallowCopy(pipes); r := IO_select(pipescopy,[],[],opt.TimeOutSecs,opt.TimeOutuSecs); answered := []; answers := EmptyPlist(n); for i in [1..n] do if pipescopy[i] = fail then IO_close(pipes[i]); IO_kill(pids[i],IO.SIGTERM); IO_WaitPid(pids[i],true); Info(InfoIO,2,"Child ",pids[i]," terminated."); else Add(answered,i); fi; od; Info(InfoIO,2,"Getting answers..."); for i in answered do file := IO_WrapFD(pipes[i],1024,false); answers[i] := IO_Unpickle(file); IO_Close(file); IO_WaitPid(pids[i],true); Info(InfoIO,2,"Child ",pids[i]," terminated with answer."); od; return answers; end; gap-io-4.4.5+ds/gap/forkparalleldream.g000066400000000000000000000010651264743113200177150ustar00rootroot00000000000000j := BackgroundJobByFork( func, args ) IsIdle(j) -> true or false HasTerminated(j) -> true or false WaitUntilIdle(j) -> returns value WaitUntilTerminated(j) Kill(j) GetResult(j) SendArguments(j,args) ParMapReduceByFork(l,mapfunc,redfunc,opt) options: NumberJobs ParTakeFirstResultByFork(jobs,args,opt) options: TimeOutSecs TimeOutuSecs ParDo(jobs,args,opt) options: TimeOutSecs TimeOutuSecs w := ParMakeWorkersByFork(jobs,args,opt) options: NumberJobs Kill(w) SendWork(w,args) IsIdle(w) AreAllIdle(w) gap-io-4.4.5+ds/gap/graveyard.g000066400000000000000000000102731264743113200162130ustar00rootroot00000000000000HTTPRequestOld := function(server,port,method,uri,header,body) local byt,chunk,f,inpflushed,inpos,k,line,lineend,lookup,msg,nr,out, outeof,pos,pos2,pos3,r,res,responseheader,ret,s,sock,w; s := IO_socket(IO.PF_INET,IO.SOCK_STREAM,"tcp"); if s = fail then Print("HTTPRequest: cannot create socket\n"); return fail; fi; lookup := IO_gethostbyname(server); if lookup = fail then Print("HTTPRequest: cannot find hostname\n"); return fail; fi; res := IO_connect(s,IO_make_sockaddr_in(lookup.addr[1],port)); if res = fail then Print("HTTPRequest: cannot connect: ",LastSystemError(),"\n"); IO_close(s); return fail; fi; sock := IO_WrapFD(s,false,false); # Maybe add some default values: if not(IsBound(header.UserAgent)) then header.UserAgent := Concatenation("GAP/IO/", PackageInfo("io")[1].Version); fi; if IsString(body) and Length(body) > 0 then header.Content\-Length := String(Length(body)); fi; # Now we have a TCP connection, we can start talking: msg := Concatenation(method," ",uri," HTTP/1.0\r\n"); for k in RecNames(header) do Append(msg,k); Append(msg,": "); Append(msg,header.(k)); Append(msg,"\r\n"); od; Append(msg,"\r\n"); if IsString(body) then Append(msg,body); fi; # Now we have collected the complete request, we do I/O multiplexing # to send away everything eventually and getting back the answer: # Note that the flushing part is superfluous since we switched off # the buffers, but still, like this, the code would also work with # buffering. # Here we just do I/O multiplexing, sending away msg (if non-empty) # and receiving from the connection. inpos := 0; inpflushed := (msg = ""); outeof := false; # Here we collect the answer: out := ""; repeat if not(outeof) then r := [sock]; else r := []; fi; if inpos < Length(msg) then w := [sock]; f := []; else w := []; if not(inpflushed) then f := [sock]; else f := []; fi; fi; nr := IO_Select(r,w,f,[],fail,fail); # First writing: if Length(w) > 0 and w[1] <> fail then byt := IO_WriteNonBlocking(sock,msg,inpos, Minimum(Length(msg)-inpos,IO.PIPE_BUF)); inpos := inpos + byt; fi; # Now perhaps flushing: if Length(f) > 0 and f[1] <> fail then if IO_FlushNonBlocking(sock) = true then inpflushed := true; fi; fi; # Now reading: if not(outeof) and r[1] <> fail then chunk := IO_Read(sock,65536); if chunk = "" then outeof := true; fi; Append(out,chunk); fi; until outeof; IO_Close(sock); # Now we want to take this apart: pos := 0; lineend := Position(out,'\n'); ret := rec(); if lineend <> fail then if lineend >= 2 and out[lineend-1] = '\r' then line := out{[1..lineend-2]}; else line := out{[1..lineend-1]}; fi; if line{[1..5]} = "HTTP/" and Length(line) >= 8 then ret.protoversion := line{[6..8]}; pos3 := Position(line,' '); if pos3 <> fail then pos2 := Position(line,' ',pos3); if pos2 <> fail then ret.statuscode := Int(line{[pos3+1..pos2-1]}); ret.status := line{[pos2+1..Length(line)]}; fi; fi; fi; pos := lineend; fi; responseheader := rec(); while true do # will be left by break lineend := Position(out,'\n',pos); if lineend <= pos+2 or lineend = fail then if lineend <> fail then pos := lineend+1; fi; break; # we have seen the header fi; if out[lineend-1] = '\r' then line := out{[pos+1..lineend-2]}; else line := out{[pos+1..lineend-1]}; fi; pos2 := PositionSublist(line,": "); if pos2 <> fail then responseheader.(line{[1..pos2-1]}) := line{[pos2+2..Length(line)]}; fi; pos := lineend; od; ret.header := responseheader; ret.body := out{[pos..Length(out)]}; return ret; end; gap-io-4.4.5+ds/gap/http.gd000066400000000000000000000026511264743113200153530ustar00rootroot00000000000000############################################################################# ## ## http.gd GAP 4 package IO ## Max Neunhoeffer ## ## Copyright (C) 2006-2011 by Max Neunhoeffer ## This file is free software, see license information at the end. ## ## This file contains declarations for the implementation of the client ## side of the HTTP protocol. ## DeclareGlobalVariable( "HTTPTimeoutForSelect" ); DeclareGlobalFunction( "OpenHTTPConnection" ); DeclareGlobalFunction( "HTTPRequest" ); DeclareGlobalFunction( "CloseHTTPConnection" ); DeclareGlobalFunction( "SingleHTTPRequest" ); DeclareGlobalFunction( "FixChunkedBody" ); DeclareGlobalFunction( "CheckForUpdates" ); DeclareGlobalFunction( "ReadWeb" ); ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 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 General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## gap-io-4.4.5+ds/gap/http.gi000066400000000000000000000335241264743113200153630ustar00rootroot00000000000000############################################################################# ## ## http.gi GAP 4 package IO ## Max Neunhoeffer ## ## Copyright (C) by Max Neunhoeffer ## This file is free software, see license information at the end. ## ## This file contains functions implementing the client side of the ## HTTP protocol. ## # The following is given as argument to IO_Select for the timeout # values in a HTTP request. InstallValue( HTTPTimeoutForSelect, [fail,fail] ); InstallGlobalFunction( OpenHTTPConnection, function(server,port) local lookup,res,s; s := IO_socket(IO.PF_INET,IO.SOCK_STREAM,"tcp"); if s = fail then return rec( sock := fail, errormsg := "OpenHTTPConnection: cannot create socket" ); fi; lookup := IO_gethostbyname(server); if lookup = fail then IO_close(s); return rec( sock := fail, errormsg := "OpenHTTPConnection: cannot find hostname" ); fi; res := IO_connect(s,IO_make_sockaddr_in(lookup.addr[1],port)); if res = fail then IO_close(s); return rec( sock := fail, errormsg := Concatenation("OpenHTTPConnection: cannot connect: ", LastSystemError().message) ); fi; # Switch the socket to non-blocking mode, just to be sure! IO_fcntl(s,IO.F_SETFL,IO.O_NONBLOCK); return rec( sock := IO_WrapFD(s,false,false), errormsg := "", host := lookup, closed := false ); end ); InstallGlobalFunction( FixChunkedBody, function( st ) # This parses a chunked body and returns the final result resulting # from putting together the chunks. Follows rfc2616, section 3.6.1. # If anything goes wrong, the original is returned. local chunklen,head,p,pos,q,res; pos := 0; res := []; while true do # will return eventually p := PositionSublist(st,"\r\n",pos); if p = fail then break; fi; head := st{[pos+1..p]}; pos := p+1; q := 1; while q <= Length(head) and head[q] in "0123456789ABCDEFabcdef" do q := q + 1; od; chunklen := IntHexString(head{[1..q-1]}); if chunklen = 0 then # this was the last chunk break; else Add(res,st{[pos+1..pos+chunklen]}); pos := pos + chunklen + 2; # eat up CR and LF fi; od; if Length(res) > 0 then return Concatenation(res); else return st; fi; end ); InstallGlobalFunction( HTTPRequest, function(conn,method,uri,header,body,target) # method, uri are the strings for the first line of the request # header must be a record # body either false or a string # target either false or the name of a file where the body is stored local ParseHeader,bodyread,byt,chunk,contentlength,haveseenheader, inpos,k,msg,nr,out,outeof,r,responseheader,ret,w,SetError, chunked; if conn.sock = fail or conn.closed = true then Error("Trying to work with closed connection"); fi; ParseHeader := function( out ) # Now we want to take this apart: # This function modifies the variables ret and responseheader # in the outer function and returns the position of the first # byte in out after the header. local line,lineend,pos,pos2,pos3; pos := 0; lineend := Position(out,'\n'); if lineend <> fail then if lineend >= 2 and out[lineend-1] = '\r' then line := out{[pos+1..lineend-2]}; else line := out{[pos+1..lineend-1]}; fi; ret.status := "Header corrupt"; if line{[1..5]} = "HTTP/" and Length(line) >= 8 then ret.protoversion := line{[6..8]}; pos3 := Position(line,' '); if pos3 <> fail then pos2 := Position(line,' ',pos3); if pos2 <> fail then ret.statuscode := Int(line{[pos3+1..pos2-1]}); ret.status := line{[pos2+1..Length(line)]}; fi; fi; fi; pos := lineend; fi; while true do # will be left by break lineend := Position(out,'\n',pos); if lineend = fail or lineend <= pos+2 then if lineend <> fail then pos := lineend+1; fi; break; # we have seen the header fi; if out[lineend-1] = '\r' then line := out{[pos+1..lineend-2]}; else line := out{[pos+1..lineend-1]}; fi; pos2 := PositionSublist(line,": "); if pos2 <> fail then responseheader.(LowercaseString(line{[1..pos2-1]})) := line{[pos2+2..Length(line)]}; fi; pos := lineend; od; if lineend = fail then # incomplete or corrupt header! return fail; else return pos; fi; end; # Maybe add some default values: if not(IsBound(header.UserAgent)) then header.UserAgent := Concatenation("GAP/IO/", PackageInfo("io")[1].Version); fi; if IsString(body) and Length(body) > 0 then header.Content\-Length := String(Length(body)); fi; if not(IsBound(header.Host)) then header.Host := conn.host.name; fi; # Now we have a TCP connection, we can start talking: msg := Concatenation(method," ",uri," HTTP/1.1\r\n"); for k in RecNames(header) do Append(msg,k); Append(msg,": "); Append(msg,header.(k)); Append(msg,"\r\n"); od; Append(msg,"\r\n"); if IsString(body) then Append(msg,body); fi; # Here we collect first the header, then maybe the rest: out := ""; # Now we have collected the complete request, we do I/O multiplexing # to send away everything eventually and getting back the answer: # Here we just do I/O multiplexing, sending away msg (if non-empty) # and receiving from the connection. # Note that we first look for the header to learn the content length: haveseenheader := false; # The answer: ret := rec( protoversion := "unknown", statuscode := 0, # indicates an error status := "", # will be filled before return header := fail, body := fail, closed := false ); # The following function is used to report on errors: SetError := function(msg) # Changes the variable ret outside! ret.status := msg; ret.statuscode := 0; if haveseenheader then ret.header := responseheader; fi; if IsList(out) then if IsStringRep(out) then ret.body := out; else ret.body := Concatenation(out); fi; else IO_Close(out); ret.body := target; fi; end; inpos := 0; outeof := false; chunked := false; repeat if not(outeof) then r := [conn.sock]; else r := []; fi; if inpos < Length(msg) then w := [conn.sock]; else w := []; fi; nr := IO_Select(r,w,[],[],HTTPTimeoutForSelect[1], HTTPTimeoutForSelect[2]); if nr = fail then # an error! SetError("Error in select, connection broken?"); return ret; fi; if nr = 0 then # a timeout SetError("Connection timed out"); return ret; fi; # First writing: if Length(w) > 0 and w[1] <> fail then byt := IO_WriteNonBlocking(conn.sock,msg,inpos, Minimum(Length(msg)-inpos,65536)); if byt = fail and LastSystemError().number <> IO.EWOULDBLOCK then # an error occured, probably connection broken SetError("Connection broken"); return ret; fi; inpos := inpos + byt; fi; # Now reading: if not(outeof) and r[1] <> fail then chunk := IO_Read(conn.sock,4096); if chunk = "" or chunk = fail then outeof := true; break; fi; # Otherwise it must be a non-empty string if not(haveseenheader) then Append(out,chunk); responseheader := rec(); r := ParseHeader(out); if r <> fail then # then it is a position number! if IsBound(responseheader.transfer\-encoding) and responseheader.transfer\-encoding = "chunked" then chunked := true; contentlength := infinity; # This now only works if the server closes the # connection after sending the body! elif not(IsBound(responseheader.content\-length)) then Print("HTTP Warning: no content length!\n"); contentlength := infinity; else if method <> "HEAD" then contentlength:=Int(responseheader.content\-length); else contentlength := 0; fi; fi; chunk := out{[r..Length(out)]}; # See to the target: if IsString(target) then out := IO_File(target,"w",false); IO_Write(out,chunk); bodyread := Length(chunk); else out := [chunk]; bodyread := Length(chunk); fi; haveseenheader := true; fi; else # We are only reading the body until done: if IsList(out) then Add(out,chunk); else IO_Write(out,chunk); fi; bodyread := bodyread + Length(chunk); fi; fi; until outeof or (haveseenheader and bodyread >= contentlength); if outeof and not(haveseenheader) then # Obviously, the connection broke: SetError("Connection broken"); return ret; fi; # In the case that contentlength is infinity because it was not # specified and we thus read until end of file we still report # success! This is some tolerance against faulty servers. ret.closed := outeof; ret.header := responseheader; if IsList(out) then ret.body := Concatenation(out); else IO_Close(out); ret.body := target; fi; if chunked then # Now we have to fix everything since it came in chunks. if target = false then # things are still in memory ret.body := FixChunkedBody(ret.body); else # oops, already in a file! # This is a dirty hack: FileString(target,FixChunkedBody(StringFile(target))); fi; fi; return ret; end ); InstallGlobalFunction( CloseHTTPConnection, function( conn ) IO_Close(conn.sock); conn.closed := true; end ); InstallGlobalFunction( SingleHTTPRequest, function(server,port,method,uri,header,body,target) local conn,r; conn := OpenHTTPConnection(server,port); if conn.sock = fail then return rec( protoversion := "unknown", statuscode := 0, status := conn.errormsg, header := fail, body := fail, closed := true ); fi; if not(IsBound(header.Host)) then header.Host := server; fi; r := HTTPRequest(conn,method,uri,header,body,target); CloseHTTPConnection(conn); r.closed := true; return r; end ); InstallGlobalFunction( CheckForUpdates, # This function was kindly contributed by Alexander Konovalov. function() local n1,n2,r; r := SingleHTTPRequest( "www.gap-system.org", 80, "GET", "/Download/upgrade.html", rec(), false, false); n1 := PositionSublist( r.body, "SuggestUpgrades" ); n2 := PositionSublist( r.body, "]);" ) + 3; Read( InputTextString( r.body{[n1..n2]} ) ); end ); InstallGlobalFunction( ReadWeb, function(url) local p, domain, uri, f; # split off http:// if Length(url)>7 and LowercaseString(url{[1..7]})="http://" then url:=url{[8..Length(url)]}; fi; p:=Position(url,'/'); domain:=url{[1..p-1]}; # e.g. www.gap-system.org uri:=url{[p..Length(url)]}; # e.g. ~xyrxmir/mystuff/bla.txt f:=SingleHTTPRequest(domain,80,"GET",uri,rec(),false,false); if f.statuscode=404 then Error("File not found -- Check URL"); elif f.statuscode >= 400 then Error("HTTP error code ",f.statuscode); fi; f:=f.body; # now `f' is a string containing the file. Read(InputTextString(f)); # read in the contents end); ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 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 General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## gap-io-4.4.5+ds/gap/io.gd000066400000000000000000000066221264743113200150050ustar00rootroot00000000000000############################################################################# ## ## io.gd GAP 4 package IO ## Max Neunhoeffer ## ## Copyright (C) by Max Neunhoeffer ## This file is free software, see license information at the end. ## ## This file contains functions mid level IO providing buffering and ## easier access from the GAP level. ## DeclareInfoClass("InfoIO"); SetInfoLevel(InfoIO, 1); DeclareCategory( "IsFile", IsObject ); DeclareGlobalVariable( "FileType" ); DeclareAttribute( "ProcessID", IsFile ); BindGlobal( "IO_ResultsFamily", NewFamily( "IO_ResultsFamily" ) ); DeclareCategory( "IO_Result", IsComponentObjectRep ); DeclareGlobalVariable( "IO_Error" ); DeclareGlobalVariable( "IO_Nothing" ); DeclareGlobalVariable( "IO_OK" ); DeclareGlobalVariable( "IO_EOF" ); # End of file marker DeclareGlobalFunction( "IO_WrapFD" ); DeclareGlobalFunction( "IO_File" ); DeclareGlobalFunction( "IO_Close" ); DeclareGlobalFunction( "IO_ReadUntilEOF" ); DeclareGlobalFunction( "IO_ReadBlock" ); DeclareGlobalFunction( "IO_Read" ); DeclareGlobalFunction( "IO_ReadLine" ); DeclareGlobalFunction( "IO_ReadLines" ); DeclareGlobalFunction( "IO_HasData" ); DeclareGlobalFunction( "IO_ReadyForWrite" ); DeclareGlobalFunction( "IO_ReadyForFlush" ); DeclareGlobalFunction( "IO_Write" ); DeclareGlobalFunction( "IO_WriteFlush" ); DeclareGlobalFunction( "IO_WriteLine" ); DeclareGlobalFunction( "IO_WriteLines" ); DeclareGlobalFunction( "IO_WriteNonBlocking" ); DeclareGlobalFunction( "IO_Flush" ); DeclareGlobalFunction( "IO_FlushNonBlocking" ); DeclareGlobalFunction( "IO_Select" ); DeclareGlobalFunction( "IO_GetFD" ); DeclareGlobalFunction( "IO_GetWBuf" ); DeclareGlobalFunction( "IO_ListDir" ); DeclareGlobalFunction( "IO_MakeIPAddressPort" ); DeclareGlobalFunction( "IO_FindExecutable" ); DeclareGlobalFunction( "IO_Environment" ); DeclareGlobalFunction( "IO_MakeEnvList" ); DeclareGlobalFunction( "IO_CloseAllFDs" ); DeclareGlobalFunction( "IO_ForkExecWithFDs" ); DeclareGlobalFunction( "IO_Popen" ); DeclareGlobalFunction( "IO_Popen2" ); DeclareGlobalFunction( "IO_Popen3" ); DeclareGlobalFunction( "IO_StartPipeline" ); DeclareGlobalFunction( "IO_StringFilterFile" ); DeclareGlobalFunction( "IO_FileFilterString" ); DeclareGlobalFunction( "IO_FilteredFile" ); DeclareGlobalFunction( "IO_CompressedFile" ); DeclareGlobalFunction( "IO_SendStringBackground" ); DeclareGlobalFunction( "IO_PipeThroughWithError" ); DeclareGlobalFunction( "IO_PipeThrough" ); if not(IsBoundGlobal("ChangeDirectoryCurrent")) then _IO_Defines_ChangeDirectoryCurrent := true; DeclareGlobalFunction( "ChangeDirectoryCurrent" ); fi; # This is for other packages to detect whether the I/O package ist loaded: BindGlobal( "IO_PackageIsLoaded", true ); ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 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 General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## gap-io-4.4.5+ds/gap/io.gi000066400000000000000000001520671264743113200150170ustar00rootroot00000000000000############################################################################# ## ## io.gi GAP 4 package IO ## Max Neunhoeffer ## ## Copyright (C) by Max Neunhoeffer ## This file is free software, see license information at the end. ## ## This file contains functions mid level IO providing buffering and ## easier access from the GAP level. ## ##################################### # Some technical preparations: # ##################################### # The family: BindGlobal( "FileFamily", NewFamily("FileFamily", IsFile) ); # The type: InstallValue( FileType, NewType(FileFamily, IsFile and IsAttributeStoringRep)); # one can now create objects by doing: # r := rec( ... ) # Objectify(FileType,r); IO.LineEndChars := "\n"; IO.LineEndChar := '\n'; if ARCH_IS_WINDOWS() then IO.LineEndChars := "\r\n"; fi; if IsBound(IO.PIPE_BUF) then IO.NonBlockWriteAmount := IO.PIPE_BUF; else IO.NonBlockWriteAmount := 4096; fi; InstallValue( IO_Error, Objectify( NewType( IO_ResultsFamily, IO_Result ), rec( val := "IO_Error" )) ); InstallValue( IO_Nothing, Objectify( NewType( IO_ResultsFamily, IO_Result ), rec( val := "IO_Nothing")) ); InstallValue( IO_OK, Objectify( NewType( IO_ResultsFamily, IO_Result ), rec( val := "IO_OK")) ); InstallMethod( \=, "for two IO_Results", [ IO_Result, IO_Result ], function(a,b) return a!.val = b!.val; end ); InstallMethod( \=, "for an IO_Result and another object", [ IO_Result, IsObject ], ReturnFalse ); InstallMethod( \=, "for another object and an IO_Result", [ IsObject, IO_Result], ReturnFalse ); InstallMethod( ViewObj, "for an IO_Result", [ IO_Result ], function(r) Print(r!.val); end ); # Store a list of open files, so we can close them on GAP exit # To ensure all streams are flushed. # We unbind 'dowaitpid', because at close we don't want to wait # for hung child processes IO.OpenFiles := Set([]); InstallAtExit(function() local file; for file in IO.OpenFiles do if IsBound(file!.dowaitpid) then Unbind(file!.dowaitpid); fi; IO_Close(file); od; end ); ########################################################################### # Now the functions to create and work with objects in the filter IsFile: # ########################################################################### InstallGlobalFunction(IO_WrapFD,function(fd,rbuf,wbuf) # fd: a small integer (a file descriptor). # rbuf: either false (for unbuffered) or a size for the read buffer size # wbuf: either false (for unbuffered) or a size for the write buffer size # rbuf can also be a string in which case fd must be -1 and we get # a File object that reads from that string. # wbuf can also be a string in which case fd must be -1 and we get # a File object that writes to that string by appending. local f, fileObj; f := rec(fd := fd, rbufsize := rbuf, wbufsize := wbuf, closed := false); if f.rbufsize <> false then if IsInt(f.rbufsize) then f.rbuf := ""; # this can grow up to r.bufsize f.rpos := 1; f.rdata := 0; # nothing in the buffer up to now else f.fd := -1; f.rbuf := f.rbufsize; f.rbufsize := Length(f.rbuf); f.rpos := 1; f.rdata := Length(f.rbuf); fi; fi; if f.wbufsize <> false then if IsInt(f.wbufsize) then f.wbuf := ""; f.wdata := 0; # nothing in the buffer up to now else f.fd := -1; f.wbuf := f.wbufsize; f.wbufsize := infinity; f.wdata := Length(f.wbuf); fi; fi; fileObj := Objectify(FileType, f); AddSet(IO.OpenFiles, fileObj); return fileObj; end ); IO.DefaultBufSize := 65536; # A convenience function for files on disk: InstallGlobalFunction(IO_File, function( arg ) # arguments: filename [,mode][,bufsize] # filename is a string and mode can be: # "r" : open for reading only (default) # "w" : open for writing only, possibly creating/truncating # "a" : open for appending local fd,filename,mode,bufsize; if Length(arg) = 1 then filename := arg[1]; mode := "r"; bufsize := IO.DefaultBufSize; elif Length(arg) = 2 then filename := arg[1]; if IsString(arg[2]) then mode := arg[2]; bufsize := IO.DefaultBufSize; else mode := "r"; bufsize := arg[2]; fi; elif Length(arg) = 3 then filename := arg[1]; mode := arg[2]; bufsize := arg[3]; else Error("IO: Usage: IO_File( filename [,mode][,bufsize] )\n", "with IsString(filename)"); fi; if not(IsString(filename)) and not(IsString(mode)) then Error("IO: Usage: IO_File( filename [,mode][,bufsize] )\n", "with IsString(filename)"); fi; if mode = "r" then fd := IO_open(filename,IO.O_RDONLY,0); if fd = fail then return fail; fi; return IO_WrapFD(fd,bufsize,false); elif mode = "w" then fd := IO_open(filename,IO.O_CREAT+IO.O_WRONLY+IO.O_TRUNC, IO.S_IRUSR+IO.S_IWUSR+IO.S_IRGRP+IO.S_IWGRP+ IO.S_IROTH+IO.S_IWOTH); if fd = fail then return fail; fi; return IO_WrapFD(fd,false,bufsize); elif mode = "a" then fd := IO_open(filename,IO.O_APPEND+IO.O_WRONLY,0); if fd = fail then return fail; fi; return IO_WrapFD(fd,false,bufsize); else Error("IO: Mode not supported!"); fi; end ); # Add equality and order for IsFile objects. # This ordering is not based on fds as they can be reused when files are closed. # We use the fact that Files objects can't be copied (as they contain the cache) InstallMethod( \=, "for two IsFile objects", [ IsFile, IsFile ], function(a,b) return IO_MasterPointerNumber(a) < IO_MasterPointerNumber(b); end ); InstallMethod( \<, "for two IsFile objects", [ IsFile, IsFile ], function(a,b) return IO_MasterPointerNumber(a) < IO_MasterPointerNumber(b); end ); # A nice View method: InstallMethod( ViewObj, "for IsFile objects", [IsFile], function(f) if f!.closed then Print(""); end); # Now a convenience function for closing: InstallGlobalFunction( IO_Close, function( f ) # f must be an object of type IsFile local i,pid,ret; if not(IsFile(f)) then Error("Usage: IO_Close( f ) with IsFile(f) and f open"); fi; if f!.closed then Error("Cannot close closed file"); fi; RemoveSet(IO.OpenFiles, f); # First flush if necessary: ret := true; if f!.wbufsize <> false and f!.wdata <> 0 then if IO_Flush( f ) = fail then ret := fail; fi; fi; f!.closed := true; f!.rbufsize := false; f!.wbufsize := false; # to free the memory for the buffer f!.rbuf := fail; f!.wbuf := fail; if f!.fd <> -1 then if IO_close(f!.fd) = fail then ret := fail; fi; fi; if HasProcessID(f) and IsBound(f!.dowaitpid) then pid := ProcessID(f); if IsInt(pid) then pid := [pid]; fi; f!.results := []; for i in [1..Length(pid)] do f!.results[i] := IO_WaitPid(pid[i],true); od; fi; return ret; end ); InstallGlobalFunction( IO_ReadUntilEOF, function( f ) # arguments: f # f must be an object of type IsFile # Reads until end of file. Returns either a (non-empty) string # or "" (if f is already at end of file) or fail if # an error occurs. local bytes,res; if not(IsFile(f)) then Error("Usage: IO_ReadUntilEOF( f ) with IsFile(f)"); fi; if f!.closed then Error("Tried to read from closed file."); fi; # Read until end of file: if f!.rbufsize <> false and f!.rdata <> 0 then # we read buffered: # First empty the buffer: res := f!.rbuf{[f!.rpos..f!.rpos+f!.rdata-1]}; f!.rpos := 1; f!.rdata := 0; else res := ""; fi; # Now read on: if f!.fd = -1 then return res; fi; repeat bytes := IO_read(f!.fd,res,Length(res),IO.NonBlockWriteAmount); if bytes = fail then return fail; fi; until bytes = 0; return res; end ); InstallGlobalFunction( IO_ReadBlock, function( f, len ) # arguments: f ,len # f must be an object of type IsFile # len is the length to read # Reads length bytes. Guarantees to return length bytes or less "" # indicating EOF or fail for an error. Blocks until enough data arrives. # This function only returns less than length bytes, if EOF is reached # before length bytes are read. local amount,bytes,res; if not(IsFile(f)) or not(IsInt(len)) then Error("Usage: IO_ReadBlock( f [,len] ) with IsFile(f) ", "and IsInt(len)"); fi; if f!.closed then Error("Tried to read from closed file."); fi; res := ""; # First the case of no buffer: if f!.rbufsize = false then while Length(res) < len do bytes := IO_read(f!.fd,res,Length(res),len - Length(res)); if bytes = fail then return fail; fi; if bytes = 0 then return res; fi; # this is EOF od; return res; fi; # read up to len bytes, using our buffer: while Length(res) < len do # First empty the buffer: if f!.rdata > len - Length(res) then # more data available amount := len - Length(res); Append(res,f!.rbuf{[f!.rpos..f!.rpos+amount-1]}); f!.rpos := f!.rpos + amount; f!.rdata := f!.rdata - amount; return res; elif f!.rdata > 0 then Append(res,f!.rbuf{[f!.rpos..f!.rpos+f!.rdata-1]}); f!.rpos := 1; f!.rdata := 0; if Length(res) = len then return res; fi; fi; if f!.fd = -1 then return res; fi; if len - Length(res) > f!.rbufsize then # In this case we read the whole thing: bytes := IO_read(f!.fd,res,Length(res),len - Length(res)); if bytes = fail then return fail; elif bytes = 0 then return res; fi; else # Now the buffer is empty, so refill it: bytes := IO_read(f!.fd,f!.rbuf,0,f!.rbufsize); if bytes = fail then return fail; elif bytes = 0 then return res; fi; f!.rdata := bytes; fi; od; return res; end ); InstallGlobalFunction( IO_ReadLine, function( f ) # f must be an object of type IsFile # The IO.LineEndChars are not removed at the end local bytes,pos,res; if not(IsFile(f)) then Error("Usage: IO_ReadLine( f ) with IsFile(f)"); fi; if f!.closed then Error("Tried to read from closed file."); fi; if f!.rbufsize = false then Error("IO: Readline not possible for unbuffered files."); fi; res := ""; while true do # First try to find a line end within the buffer: pos := Position(f!.rbuf,IO.LineEndChar,f!.rpos-1); if pos <> fail and pos < f!.rpos + f!.rdata then # The line is completely within the buffer Append(res,f!.rbuf{[f!.rpos..pos]}); f!.rdata := f!.rdata - (pos + 1 - f!.rpos); f!.rpos := pos + 1; return res; else Append(res,f!.rbuf{[f!.rpos..f!.rpos + f!.rdata - 1]}); f!.rpos := 1; f!.rdata := 0; if f!.fd = -1 then return res; fi; # Now read more data into buffer: bytes := IO_read(f!.fd,f!.rbuf,0,f!.rbufsize); if bytes = fail then return fail; fi; if bytes = 0 then # we are at end of file return res; fi; f!.rdata := bytes; fi; od; end ); InstallGlobalFunction( IO_ReadLines, function (arg) # arguments: f [,maxlines] # f must be an object of type IsFile # maxlines is the maximal number of lines read # Reads lines (max. maxlines or until end of file) and returns a list # of strings, which are the lines. local f,l,li,max; if Length(arg) = 1 then f := arg[1]; max := infinity; elif Length(arg) = 2 then f := arg[1]; max := arg[2]; else Error("Usage: IO_ReadLines( f [,max] ) with IsFile(f) and IsInt(max)"); fi; if not(IsFile(f)) or not(IsInt(max) or max = infinity) then Error("Usage: IO_ReadLines( f [,max] ) with IsFile(f) and IsInt(max)"); fi; if f!.closed then Error("Tried to read from closed file."); fi; li := []; while Length(li) < max do l := IO_ReadLine(f); if l = fail then return fail; fi; if Length(l) = 0 then return li; fi; Add(li,l); od; return li; end ); InstallGlobalFunction( IO_Read, function( f, len ) # arguments: f ,len # f must be an object of type IsFile # len is the length to read # Reads up to length bytes. Returns at least 1 byte or "" (for EOF) # or fail for an error. Blocks only if there is no data available # and the file is not yet at EOF (for pipes or sockets). If IO_Select # states that a file object is ready to read, then this function will # not block. The function may return less than len bytes. It is *not* # guaranteed that all available data is returned. This function is # intended to behave very similar to IO_read except for the buffering. local amount,bytes,res; if not(IsFile(f)) or not(IsInt(len)) then Error("Usage: IO_Read( f ,len ) with IsFile(f) ", "and IsInt(len)"); fi; if f!.closed then Error("Tried to read from closed file."); fi; res := ""; # First the case of no buffer: if f!.rbufsize = false then bytes := IO_read(f!.fd,res,Length(res),len - Length(res)); if bytes = fail then return fail; fi; return res; fi; # read up to len bytes, using our buffer: # First empty the buffer: while true do # will be exited if f!.rdata > len - Length(res) then # more data available amount := len - Length(res); res := f!.rbuf{[f!.rpos..f!.rpos+amount-1]}; f!.rpos := f!.rpos + amount; f!.rdata := f!.rdata - amount; return res; elif f!.rdata > 0 then res := f!.rbuf{[f!.rpos..f!.rpos+f!.rdata-1]}; f!.rpos := 1; f!.rdata := 0; return res; fi; if f!.fd = -1 then return ""; fi; if len - Length(res) > f!.rbufsize then # In this case we read the whole thing: bytes := IO_read(f!.fd,res,Length(res),len - Length(res)); if bytes = fail then return fail; elif bytes = 0 then return ""; else return res; fi; else # Now the buffer is empty, so refill it: bytes := IO_read(f!.fd,f!.rbuf,0,f!.rbufsize); if bytes = fail then return fail; elif bytes = 0 then return ""; fi; f!.rdata := bytes; # The next loop will do it fi; od; end ); InstallGlobalFunction( IO_HasData, # Returns true or false. True means, that IO_Read will not block, i.e., # it will either produce data or indicate end of file. Note that for a # file at end of file this function returns true. function(f) local l,nr; if not(IsFile(f)) then Error("Usage: IO_HasData( f ) with IsFile(f)"); fi; if f!.closed then Error("Tried to check for data on closed file."); fi; if f!.rbufsize <> false and f!.rdata <> 0 then return true; fi; if f!.fd = -1 then return false; fi; # Now use select: l := [f!.fd]; nr := IO_select(l,[],[],0,0); if nr = 0 then return false; fi; return true; end ); # The buffered write functionality: InstallGlobalFunction( IO_Write, function( arg ) # arguments: f {,things ... } # f must be an object of type IsFile # all other arguments: either they are strings, in which case they are # written directly, otherwise they are converted to strings with "String" # and the result is being written. The result is either the number of # bytes written or fail to indicate an error. This functions blocks # until everything is either written to the buffer or to the actual # file descriptor. Note that you can never be sure that this function # returns immediately, even if IO_Select returned a certain file to # be writable. Use IO_WriteNonBlocking for that purpose. local bytes,f,i,pos,pos2,st,sumbytes; if Length(arg) < 2 or not(IsFile(arg[1])) then Error("Usage: IO_Write( f ,things ... ) with IsFile(f)"); fi; f := arg[1]; if f!.closed then Error("Tried to write on closed file."); fi; if Length(arg) = 2 and IsStringRep(arg[2]) then # This is the main buffered Write functionality, all else delegates here: st := arg[2]; # Do we buffer? if f!.wbufsize = false then # Non-buffered I/O: pos := 0; while pos < Length(st) do bytes := IO_write(f!.fd,st,pos,Length(st)); if bytes = fail then return fail; fi; pos := pos + bytes; od; return Length(st); # this indicates success else # we do buffering: pos := 0; while pos < Length(st) do # First fill the buffer: if Length(st) - pos + f!.wdata < f!.wbufsize then f!.wbuf{[f!.wdata+1..f!.wdata+Length(st)-pos]} := st{[pos+1..Length(st)]}; f!.wdata := f!.wdata + Length(st) - pos; return Length(st); else f!.wbuf{[f!.wdata+1..f!.wbufsize]} := st{[pos+1..pos+f!.wbufsize-f!.wdata]}; pos := pos + f!.wbufsize - f!.wdata; f!.wdata := f!.wbufsize; # Now the buffer is full and pos is still < Length(st)! fi; # Write out the buffer: pos2 := 0; while pos2 < f!.wbufsize do bytes := IO_write(f!.fd,f!.wbuf,pos2,f!.wbufsize-pos2); if bytes = fail then return fail; fi; pos2 := pos2 + bytes; od; f!.wdata := 0; # Perhaps we can write a big chunk: if Length(st)-pos > f!.wbufsize then bytes := IO_write(f!.fd,st,pos,Length(st)-pos); if bytes = fail then return fail; fi; pos := pos + bytes; fi; od; return Length(st); fi; fi; sumbytes := 0; for i in [2..Length(arg)] do if IsStringRep(arg[i]) then st := arg[i]; else st := String(arg[i]); ConvertToStringRep(st); fi; bytes := IO_Write(f,st); # delegate to above if bytes = fail then return fail; fi; sumbytes := sumbytes + bytes; od; return sumbytes; end ); InstallGlobalFunction( IO_WriteLine, function( arg ) # The same as IO_write, except that a line end is written in the end # and the buffer is flushed afterwards. local res; Add(arg,IO.LineEndChars); res := CallFuncList( IO_Write, arg ); if res = fail then return fail; fi; if IO_Flush(arg[1]) = fail then return fail; else return res; fi; end ); InstallGlobalFunction( IO_WriteLines, function( f, l ) # f must be an object of type IsFile # l must be a list. Calls IO_Write( f, o, IO.LineEndChars ) for all o in l. local o,res,written; if not(IsFile(f)) or not(IsList(l)) then Error("Usage: IO_WriteLines( f, l ) with IsFile(f) and IsList(l)"); fi; written := 0; for o in l do res := IO_Write(f, o, IO.LineEndChars); if res = fail then return fail; fi; written := written + res; od; if IO_Flush(f) = fail then return fail; else return written; fi; end ); InstallGlobalFunction( IO_WriteNonBlocking, function( f, st, pos, len ) # This function tries to write data of len bytes in the string st beginning # at position pos+1 to f. It is guaranteed that this function does not # block, if IO_ReadyForWrite(f) returned true or IO_Select indicated # possibility to write. Therefore, it might write fewer characters # than requested! The function returns the number of bytes written # or fail in case of an error. The function can block, if the # buffer is full and the file descriptor is not ready to write. local bytes,pos2; if not(IsFile(f)) or not(IsStringRep(st)) or not(IsInt(pos)) then Error("Usage: IO_WriteNonBlocking( f, st, pos )"); fi; if f!.closed then Error("Tried to write on closed file."); fi; # Do we buffer? if f!.wbufsize = false then # Non-buffered I/O: return IO_write(f!.fd,st,pos,len); else # we do buffering: while true do # will be left by return and run at most twice! # First fill the buffer: if f!.wdata < f!.wbufsize then # buffer not full if len + f!.wdata < f!.wbufsize then f!.wbuf{[f!.wdata+1..f!.wdata+len]} := st{[pos+1..pos+len]}; f!.wdata := f!.wdata + len; return len; else f!.wbuf{[f!.wdata+1..f!.wbufsize]} := st{[pos+1..pos+f!.wbufsize-f!.wdata]}; bytes := f!.wbufsize - f!.wdata; f!.wdata := f!.wbufsize; # Now the buffer is full and pos is still < Length(st)! return bytes; fi; fi; # Write out the buffer: pos2 := 0; bytes := IO_write(f!.fd,f!.wbuf,0,Minimum(IO.NonBlockWriteAmount, f!.wbufsize)); if bytes = fail then return fail; fi; if bytes = f!.wbufsize then f!.wdata := 0; else f!.wbuf{[1..f!.wbufsize-bytes]} := f!.wbuf{[bytes+1..f!.wbufsize]}; f!.wdata := f!.wdata - bytes; fi; # Now there is again space in the buffer and the next loop # will succeed to write at least something. od; fi; end ); InstallGlobalFunction( IO_Flush, function( f ) local res; if not(IsFile(f)) then Error("Usage: IO_Flush( f ) with IsFile(f)"); fi; if f!.fd = -1 then # Nothing to do for string Files return true; fi; while f!.wbufsize <> false and f!.wdata <> 0 do res := IO_write( f!.fd, f!.wbuf, 0, f!.wdata ); if res = fail then return fail; fi; f!.wdata := f!.wdata - res; od; return true; end ); InstallGlobalFunction( IO_FlushNonBlocking, function( f ) # This function is guaranteed to make some progress but also not to # block if IO_ReadyForWrite or IO_Select returned f to be ready for # writing. It returns true if the buffers have been flushed and false # otherwise. An error is signalled by fail. local res; if not(IsFile(f)) then Error("Usage: IO_FlushNonBlocking( f ) with IsFile(f)"); fi; if f!.fd = -1 or # Nothing to do for string Files f!.wbufsize = false or (f!.wbufsize <> false and f!.wdata = 0) then # or if buffer empty return true; fi; res := IO_write( f!.fd, f!.wbuf, 0, f!.wdata ); if res = fail then return fail; fi; if res < f!.wdata then f!.wbuf{[1..f!.wdata-res]} := f!.wbuf{[res+1..f!.wdata]}; f!.wdata := f!.wdata - res; return false; else f!.wdata := 0; return true; fi; end ); InstallGlobalFunction( IO_WriteFlush, function(arg) local bytes; bytes := CallFuncList(IO_Write,arg); if bytes = fail then return fail; fi; if IO_Flush(arg[1]) = fail then return fail; fi; return bytes; end ); InstallGlobalFunction( IO_ReadyForWrite, # Returns true or false. True means, that the next IO_WriteNonBlocking # will not block, false means, that the next IO_WriteNonBlocking might # block. function(f) local l,nr; if not(IsFile(f)) then Error("Usage: IO_ReadyForWrite( f ) with IsFile(f)"); fi; if f!.closed then Error("Tried to check for write on closed file."); fi; if f!.wbufsize <> false and f!.wdata < f!.wbufsize then return true; fi; if f!.fd = -1 then return true; fi; # Now use select: l := [f!.fd]; nr := IO_select([],l,[],0,0); if nr = 0 then return false; fi; return true; end ); InstallGlobalFunction( IO_ReadyForFlush, # Returns true or false. True means, that the next IO_FlushNonBlocking # will not block, false means, that the next IO_FlushNonBlocking might # block. function(f) local l,nr; if not(IsFile(f)) then Error("Usage: IO_ReadyForFlush( f ) with IsFile(f)"); fi; if f!.closed then Error("Tried to check for flush on closed file."); fi; if f!.wbufsize = false or f!.wdata = 0 then return true; fi; if f!.fd = -1 then return true; fi; # Now use select: l := [f!.fd]; nr := IO_select([],l,[],0,0); if nr = 0 then return false; fi; return true; end ); InstallGlobalFunction( IO_Select, function( r, w, f, e, t1, t2 ) # Provides select functionality for file descriptors and IsFile objects. # The list f is for a test for flushability. local ep,ee,i,nr,nrfinal,rp,rr,wp,ww; nrfinal := 0; rr := []; rp := []; for i in [1..Length(r)] do if IsInt(r[i]) then # A file descriptor Add(rr,r[i]); Add(rp,i); elif IsFile(r[i]) then if r[i]!.rbufsize <> false and r[i]!.rdata > 0 then nrfinal := nrfinal + 1; else Add(rr,r[i]!.fd); Add(rp,i); fi; else r[i] := fail; fi; od; ww := []; wp := []; for i in [1..Length(w)] do if IsInt(w[i]) then # A file descriptor Add(ww,w[i]); Add(wp,i); elif IsFile(w[i]) then if w[i]!.wbufsize <> false and w[i]!.wdata < w[i]!.wbufsize then nrfinal := nrfinal + 1; else Add(ww,w[i]!.fd); Add(wp,i); fi; else w[i] := fail; fi; od; for i in [1..Length(f)] do if IsInt(f[i]) then # A file descriptor Add(ww,f[i]); Add(wp,-i); elif IsFile(f[i]) then Add(ww,f[i]!.fd); Add(wp,-i); else f[i] := fail; fi; od; ee := []; ep := []; for i in [1..Length(e)] do if IsInt(e[i]) then # A file descriptor Add(ee,e[i]); Add(ep,i); elif IsFile(e[i]) then Add(ee,e[i]!.fd); Add(ep,i); else e[i] := fail; fi; od; if Length(rr) > 0 or Length(ww) > 0 or Length(ee) > 0 then # we have to investigate further: if nrfinal > 0 then t1 := 0; # set timeout to 0 because we know we have buffers ready t2 := 0; fi; # Now do the select: repeat nr := IO_select(rr,ww,ee,t1,t2); if nr = fail and LastSystemError().number <> IO.EINTR then # An error, bits and timeout are undefined # LastSystemError is set return fail; fi; # Otherwise we have an interrupted system call and want to # restart the same system call afterwards. until IsInt(nr); nrfinal := nrfinal + nr; # Now look for results: for i in [1..Length(rr)] do if rr[i] = fail then r[rp[i]] := fail; fi; od; for i in [1..Length(ww)] do if ww[i] = fail then if wp[i] > 0 then w[wp[i]] := fail; else f[-wp[i]] := fail; fi; fi; od; for i in [1..Length(ee)] do if ee[i] = fail then e[ep[i]] := fail; fi; od; fi; return nrfinal; end ); # Allow access to the file descriptor: InstallGlobalFunction( IO_GetFD, function(f) if not(IsFile(f)) then Error("Usage: IO_GetFD( f ) with IsFile(f)"); fi; return f!.fd; end ); # Allow access to the buffers: InstallGlobalFunction( IO_GetWBuf, function(f) if not(IsFile(f)) then Error("Usage IO_GetWBuf( f ) with IsFile(f)"); fi; return f!.wbuf; end ); # Read a full directory: InstallGlobalFunction( IO_ListDir, function( dirname ) local f,l,res; l := []; res := IO_opendir( dirname ); if res = fail then return fail; fi; repeat f := IO_readdir(); if IsString(f) then Add(l,f); fi; until f = false or f = fail; IO_closedir(); return l; end ); # A helper to make pairs IP address and port for TCP and UDP transfer: InstallGlobalFunction( IO_MakeIPAddressPort, function(ip,port) local i,l,nr,r,res; if Length(ip) = 4 then res := ip; else l := List(SplitString(ip,"."),Int); if Length(l) <> 4 or not(ForAll(l,IsInt)) then r := IO_gethostbyname(ip); if r = fail then Error("This is not an IP address or a valid host name"); return fail; fi; res := r.addr[1]; else res := " "; for i in [1..4] do nr := l[i]; if nr < 0 or nr > 255 then Error("IPv4 addresses must contain numbers between 0 and 255"); fi; res[i] := CHAR_INT(nr); od; fi; fi; if port < 0 or port > 65535 then Error("IPv4 port numbers must be between 0 and 65535"); fi; return IO_make_sockaddr_in(res,port); end ); InstallGlobalFunction( IO_FindExecutable, function(path) if '/' in path then if not(IsExecutableFile(path)) then return fail; else return path; fi; else path := Filename(DirectoriesSystemPrograms(),path); if path = fail then return fail; fi; if not(IsExecutableFile(path)) then return fail; fi; return path; fi; end ); ############################################################################# # Two helper functions to access and change the environment: # ############################################################################# InstallGlobalFunction( IO_Environment, function() # Returns a record with the components corresponding to the set # environment variables. local l,ll,p,r,v; l := IO_environ(); r := rec(); for v in l do p := Position(v,'='); if p <> fail then r.(v{[1..p-1]}) := v{[p+1..Length(v)]}; fi; od; return r; end ); InstallGlobalFunction( IO_MakeEnvList, function(r) # Returns a list of strings for usage with execve made from the # components of r in the form "key=value". local k,l; l := []; for k in RecFields(r) do Add(l,Concatenation(k,"=",r.(k))); od; return l; end ); IO.MaxFDToClose := 64; InstallGlobalFunction( IO_CloseAllFDs, function(exceptions) local i; exceptions := Set(exceptions); for i in [0..IO.MaxFDToClose] do if not(i in exceptions) then IO_close(i); fi; od; return; end ); InstallGlobalFunction( IO_ForkExecWithFDs, function(path,argv,stdinfd,stdoutfd,stderrfd) # This is an internal function. Does the usual fork/exec combination # with dup2ing the three given file descriptors to standard input, # standard output and standard error of the child process respectively. # It installs our signal handler local pid; IO_InstallSIGCHLDHandler(); # to be able to use IO_WaidPID pid := IO_fork(); if pid < 0 then return fail; fi; if pid = 0 then # the child # First close all files IO_CloseAllFDs([stdinfd,stdoutfd,stderrfd]); if stdinfd <> 0 then IO_dup2(stdinfd,0); IO_close(stdinfd); fi; if stdoutfd <> 1 then IO_dup2(stdoutfd,1); IO_close(stdoutfd); fi; if stderrfd <> 2 then IO_dup2(stderrfd,2); IO_close(stderrfd); fi; IO_execv(path,argv); # The following should not happen: IO_exit(-1); fi; # Now the parent: return pid; end ); InstallGlobalFunction( IO_Popen, function(arg) # mode can be "w" or "r". In the first case, the standard input of the # new process will be a pipe, the writing end is returned as a File object. # In the second case, the standard output of the new process will be a # pipe, the reading end is returned as a File object. # The other (standard out or in resp.) is identical to the one of the # calling GAP process. # Returns fail if an error occurred. # The process will usually die, when the pipe is closed. # The File object will have the Attribute "ProcessID" set to the process ID. local path,argv,mode,bufsize,fil,pid,pipe; if Length(arg) < 3 then Print("Usage: IO_Popen(path,arg,mode,[bufsize])\n"); return; fi; path := arg[1]; argv := arg[2]; mode := arg[3]; if Length(arg) > 3 then bufsize := arg[4]; else bufsize := IO.DefaultBufSize; fi; path := IO_FindExecutable(path); if path = fail then Error("Popen: must refer to an executable file."); fi; IO_InstallSIGCHLDHandler(); # to be able to use IO_WaidPID if mode = "r" then pipe := IO_pipe(); if pipe = fail then return fail; fi; pid := IO_ForkExecWithFDs(path,argv,0,pipe.towrite,2); if pid = fail then IO_close(pipe.toread); IO_close(pipe.towrite); return fail; fi; # Now the parent: IO_close(pipe.towrite); fil := IO_WrapFD(pipe.toread,bufsize,false); SetProcessID(fil,pid); fil!.dowaitpid := true; return fil; elif mode = "w" then pipe := IO_pipe(); if pipe = fail then return fail; fi; pid := IO_ForkExecWithFDs(path,argv,pipe.toread,1,2); if pid = fail then IO_close(pipe.toread); IO_close(pipe.towrite); return fail; fi; # Now the parent: IO_close(pipe.toread); fil := IO_WrapFD(pipe.towrite,false,bufsize); SetProcessID(fil,pid); fil!.dowaitpid := true; return fil; else Error("mode must be \"r\" or \"w\"."); fi; end ); InstallGlobalFunction( IO_Popen2, function(arg) # A new child process is started. The standard in and out of it are # pipes. The writing end of the input pipe and the reading end of the # output pipe are returned as File objects bound to two components # "stdin" and "stdout" of the returned record. This means, you have to # *write* to "stdin" and read from "stdout". The stderr will be the same # as the one of the calling GAP process. # Returns fail if an error occurred. # The process will usually die, when one of the pipes is closed. local path,argv,rbufsize,wbufsize,pid,pipe,pipe2,stdin,stdout; if Length(arg) < 2 then Print("Usage: IO_Popen2(path,argv,[rbufsize,wbufsize])\n"); return; fi; path := arg[1]; argv := arg[2]; if Length(arg) > 3 then rbufsize := arg[3]; wbufsize := arg[4]; else rbufsize := IO.DefaultBufSize; wbufsize := IO.DefaultBufSize; fi; path := IO_FindExecutable(path); if path = fail then Error("Popen2: must refer to an executable file."); fi; IO_InstallSIGCHLDHandler(); # to be able to use IO_WaidPID pipe := IO_pipe(); if pipe = fail then return fail; fi; pipe2 := IO_pipe(); if pipe2 = fail then IO_close(pipe.toread); IO_close(pipe.towrite); return fail; fi; pid := IO_ForkExecWithFDs(path,argv,pipe.toread,pipe2.towrite,2); if pid = fail then IO_close(pipe.toread); IO_close(pipe.towrite); IO_close(pipe2.toread); IO_close(pipe2.towrite); return fail; fi; # Now the parent: IO_close(pipe.toread); IO_close(pipe2.towrite); stdin := IO_WrapFD(pipe.towrite,false,wbufsize); stdout := IO_WrapFD(pipe2.toread,rbufsize,false); SetProcessID(stdin,pid); SetProcessID(stdout,pid); # We only set the dowaitpid flag for the standard output! stdout!.dowaitpid := true; return rec(stdin := stdin, stdout := stdout, pid := pid); end ); InstallGlobalFunction( IO_Popen3, function(arg) # A new child process is started. The standard in and out and error are # pipes. All three "other" ends of the pipes are returned as File # objectes bound to the three components "stdin", "stdout", and "stderr" # of the returned record. This means, you have to *write* to "stdin" # and read from "stdout" and "stderr". # Returns fail if an error occurred. local path,argv,rbufsize,wbufsize,ebufsize,pid,pipe,pipe2,pipe3, stderr,stdin,stdout; if Length(arg) < 2 then Print("Usage: IO_Popen3(path,argv,[rbufsize,wbufsize,ebufsize])\n"); return; fi; path := arg[1]; argv := arg[2]; if Length(arg) > 4 then rbufsize := arg[3]; wbufsize := arg[4]; ebufsize := arg[5]; else rbufsize := IO.DefaultBufSize; wbufsize := IO.DefaultBufSize; ebufsize := IO.DefaultBufSize; fi; path := IO_FindExecutable(path); if path = fail then Error("Popen3: must refer to an executable file."); fi; IO_InstallSIGCHLDHandler(); # to be able to use IO_WaidPID pipe := IO_pipe(); if pipe = fail then return fail; fi; pipe2 := IO_pipe(); if pipe2 = fail then IO_close(pipe.toread); IO_close(pipe.towrite); return fail; fi; pipe3 := IO_pipe(); if pipe3 = fail then IO_close(pipe.toread); IO_close(pipe.towrite); IO_close(pipe2.toread); IO_close(pipe2.towrite); return fail; fi; pid := IO_ForkExecWithFDs(path,argv,pipe.toread,pipe2.towrite,pipe3.towrite); if pid = fail then IO_close(pipe.toread); IO_close(pipe.towrite); IO_close(pipe2.toread); IO_close(pipe2.towrite); IO_close(pipe3.toread); IO_close(pipe3.towrite); return fail; fi; # Now the parent: IO_close(pipe.toread); IO_close(pipe2.towrite); IO_close(pipe3.towrite); stdin := IO_WrapFD(pipe.towrite,false,wbufsize); stdout := IO_WrapFD(pipe2.toread,rbufsize,false); stderr := IO_WrapFD(pipe3.toread,ebufsize,false); SetProcessID(stdin,pid); SetProcessID(stdout,pid); SetProcessID(stderr,pid); # We only set the dowaitpid flag for the standard output! stdout!.dowaitpid := true; return rec(stdin := stdin, stdout := stdout, stderr := stderr, pid := pid); end ); InstallGlobalFunction( IO_StartPipeline, function( progs, infd, outfd, switcherror ) # progs is a list of pairs, the first entry being a path to an executable, # the second an argument list, infd is an open file descriptor for # reading, outfd is an open file descriptor for writing, both can be # replaced by "open" in which case a new pipe will be opened. switcherror # is a boolean indicating whether standard error channels are also # switched to the output channel. This function starts up all processes # and connects them with pipes. The input of the first is switched to # infd and the output of the last to outfd. # Returns a record with the following components: "pids" is a list of # pids if everything worked. For each process where # some error occurred the corresponding pid is replaced by fail. # "stdin" is equal to infd (or the new file descriptor if infd was "open"), # "stdout" is euqal to outfd (or the new file descriptor if outfd was # "open"). local a,b,c,i,inpipe,j,outpipe,pids,pipe,pipes,r; for i in [1..Length(progs)] do progs[i][1] := IO_FindExecutable(progs[i][1]); if progs[i][1] = fail then Error("IO_StartPipeline: must refer to a executable files."); return fail; fi; od; if infd = "open" then inpipe := IO_pipe(); if inpipe = fail then return fail; fi; infd := inpipe.toread; else inpipe := false; fi; if outfd = "open" then outpipe := IO_pipe(); if outpipe = fail then IO_close(inpipe.towrite); IO_close(inpipe.toread); return fail; fi; outfd := outpipe.towrite; else outpipe := false; fi; pipes := []; for i in [1..Length(progs)-1] do pipe := IO_pipe(); if pipe = fail then for j in [1..Length(pipes)] do IO_close(pipes[j].toread); IO_close(pipes[j].towrite); od; IO_close(inpipe.toread); IO_close(inpipe.towrite); IO_close(outpipe.toread); IO_close(outpipe.towrite); return fail; fi; Add(pipes,pipe); od; IO_InstallSIGCHLDHandler(); # to be able to use IO_WaidPID pids := 0*[1..Length(progs)]; for i in [1..Length(progs)] do if i = 1 then a := infd; else a := pipes[i-1].toread; fi; if i = Length(progs) then b := outfd; else b := pipes[i].towrite; fi; if switcherror then c := b; else c := 2; fi; pids[i] := IO_ForkExecWithFDs(progs[i][1],progs[i][2],a,b,c); od; # Now close all pipes in the parent: for i in [1..Length(pipes)] do IO_close(pipes[i].toread); IO_close(pipes[i].towrite); od; r := rec( pids := pids ); if inpipe <> false then IO_close(inpipe.toread); r.stdin := inpipe.towrite; else IO_close(infd); r.stdin := false; fi; if outpipe <> false then IO_close(outpipe.towrite); r.stdout := outpipe.toread; else IO_close(outfd); r.stdout := false; fi; return r; end ); InstallGlobalFunction( IO_StringFilterFile, function( progs, filename ) local f,fd,i,r,s; fd := IO_open(filename,0,IO.O_RDONLY); if fd = fail then return fail; fi; r := IO_StartPipeline(progs, fd, "open", false); if r = fail or fail in r.pids then IO_close(fd); return fail; fi; f := IO_WrapFD(r.stdout,false,false); s := IO_ReadUntilEOF(f); IO_Close(f); for i in r.pids do IO_WaitPid(i,true); od; return s; end ); InstallGlobalFunction( IO_FileFilterString, function( arg ) local append,f,fd,filename,i,le,progs,r,st; # Check arguments: if Length(arg) < 3 or Length(arg) > 4 then Error("Usage: IO_FileFilterString( filename, progs, st [,append]"); return fail; fi; filename := arg[1]; progs := arg[2]; st := arg[3]; if Length(arg) > 3 then append := arg[4]; else append := false; fi; if append then fd := IO_open(filename,IO.O_WRONLY + IO.O_APPEND, IO.S_IRUSR+IO.S_IWUSR+IO.S_IRGRP+IO.S_IWGRP+ IO.S_IROTH+IO.S_IWOTH); else fd := IO_open(filename,IO.O_WRONLY + IO.O_TRUNC + IO.O_CREAT, IO.S_IRUSR+IO.S_IWUSR+IO.S_IRGRP+IO.S_IWGRP+ IO.S_IROTH+IO.S_IWOTH); fi; if fd = fail then return fail; fi; r := IO_StartPipeline(progs, "open", fd, false); if r = fail or fail in r.pids then IO_close(fd); return fail; fi; f := IO_WrapFD(r.stdin,false,false); le := IO_Write(f,st); IO_Close(f); for i in r.pids do IO_WaitPid(i,true); od; if le = fail or le < Length(st) then return fail; fi; return true; end ); InstallGlobalFunction( IO_FilteredFile, function(arg) # arguments: progs, filename [,mode][,bufsize] # mode and bufsize as in IO_File, progs as for StartPipeline local bufsize,f,fd,filename,mode,progs,r; if Length(arg) = 2 then progs := arg[1]; filename := arg[2]; mode := "r"; bufsize := IO.DefaultBufSize; elif Length(arg) = 3 then progs := arg[1]; filename := arg[2]; if IsString(arg[3]) then mode := arg[3]; bufsize := IO.DefaultBufSize; else mode := "r"; bufsize := arg[3]; fi; elif Length(arg) = 4 then progs := arg[1]; filename := arg[2]; mode := arg[3]; bufsize := arg[4]; else Error("IO: Usage: IO_FilteredFile( progs,filename [,mode][,bufsize] )\n", "with IsString(filename)"); fi; if not(IsString(filename)) and not(IsString(mode)) then Error("IO: Usage: IO_FilteredFile( progs, filename [,mode][,bufsize] )\n", "with IsString(filename)"); fi; if Length(progs) = 0 then return IO_File(filename,mode,bufsize); fi; if mode = "r" then fd := IO_open(filename,IO.O_RDONLY,0); if fd = fail then return fail; fi; r := IO_StartPipeline(progs,fd,"open",false); if r = fail or fail in r.pids then IO_close(fd); return fail; fi; f := IO_WrapFD(r.stdout,bufsize,false); SetProcessID(f,r.pids); f!.dowaitpid := true; else if mode = "w" then fd := IO_open(filename,IO.O_CREAT+IO.O_WRONLY+IO.O_TRUNC, IO.S_IRUSR+IO.S_IWUSR+IO.S_IRGRP+IO.S_IWGRP+ IO.S_IROTH+IO.S_IWOTH); else fd := IO_open(filename,IO.O_WRONLY + IO.O_APPEND, IO.S_IRUSR+IO.S_IWUSR+IO.S_IRGRP+IO.S_IWGRP+ IO.S_IROTH+IO.S_IWOTH); fi; if fd = fail then return fail; fi; r := IO_StartPipeline(progs, "open", fd, false); if r = fail or fail in r.pids then IO_close(fd); return fail; fi; f := IO_WrapFD(r.stdin,false,bufsize); SetProcessID(f,r.pids); f!.dowaitpid := true; fi; return f; end ); InstallGlobalFunction( IO_CompressedFile, function(arg) # arguments: filename [,mode][,bufsize] # mode and bufsize as in IO_File local bufsize,f,fd,filename,mode,r,ext,splitname,extension,compressor,file; if Length(arg) = 1 then filename := arg[1]; mode := "r"; bufsize := IO.DefaultBufSize; elif Length(arg) = 2 then filename := arg[1]; if IsString(arg[2]) then mode := arg[2]; bufsize := IO.DefaultBufSize; else mode := "r"; bufsize := arg[2]; fi; elif Length(arg) = 3 then filename := arg[1]; mode := arg[2]; bufsize := arg[3]; else Error("IO: Usage: IO_CompressedFile( filename [,mode][,bufsize] )\n", "with IsString(filename)"); fi; if not(IsString(filename)) and not(IsString(mode)) then Error("IO: Usage: IO_CompressedFile( filename [,mode][,bufsize] )\n", "with IsString(filename)"); fi; splitname := SplitString(filename, "."); extension := splitname[Length(splitname)]; # compressor format: ["executable", args for compression, args for uncompression] if extension = "gz" then compressor := ["gzip",["-9q"],["-dq"]]; elif extension = "bz2" then compressor := ["bzip2",["-9q"],["-dq"]]; elif extension = "xz" then # xz higher than 6 is not really worth the time / space tradeoff compressor := ["xz",["-6q"],["-dq"]]; else return IO_File(filename,mode,bufsize); fi; if mode = "r" then file := IO_FilteredFile([[compressor[1], compressor[3]]], filename, mode, bufsize); else file := IO_FilteredFile([[compressor[1], compressor[2]]], filename, mode, bufsize); fi; if file <> fail then return file; fi; # Oops, something went wrong. Let's see if the problem is the compression prog if IO_FindExecutable(compressor[1]) = fail then Error("Cannot find '",compressor[1], "', required for "+extension+" files in IO_CompressedFile"); fi; #Nope, then just return fail return fail; end ); InstallGlobalFunction( IO_SendStringBackground, function(f,st) # The whole string st is send to the File object f but in the background. # This works by forking off a child process which sends away the string # such that the parent can go on and can already read from the other end. # This is especially useful for piping large amounts of data through # a program that has been started with Popen2 or Popen3. # The component pid will be bound to the process id of the child process. # Returns fail if an error occurred. local pid,len; pid := IO_fork(); if pid = -1 then return fail; fi; if pid = 0 then # the child len := IO_Write(f,st); IO_Flush(f); IO_Close(f); IO_exit(0); fi; return true; end ); InstallGlobalFunction( IO_PipeThroughWithError, function(cmd,args,input) local byt,chunk,err,erreof,inpos,nr,out,outeof,r,s,w,status; # Start the coprocess: s := IO_Popen3(cmd,args,false,false,false); if s = fail then return fail; fi; # Switch the one we write to to non-blocking mode, just to be sure! IO_fcntl(s.stdin!.fd,IO.F_SETFL,IO.O_NONBLOCK); # Here we just do I/O multiplexing, sending away input (if non-empty) # and receiving stdout and stderr. inpos := 0; outeof := false; erreof := false; # Here we collect stderr and stdout: err := ""; out := []; if Length(input) = 0 then IO_Close(s.stdin); fi; repeat if not(outeof) then r := [s.stdout]; else r := []; fi; if not(erreof) then Add(r,s.stderr); fi; if inpos < Length(input) then w := [s.stdin]; else w := []; fi; nr := IO_Select(r,w,[],[],fail,fail); if nr = fail then # an error occurred if inpos < Length(input) then IO_Close(s.stdin); fi; IO_Close(s.stdout); IO_Close(s.stderr); return fail; fi; # First writing: if Length(w) > 0 and w[1] <> fail then byt := IO_WriteNonBlocking(s.stdin,input,inpos,Length(input)-inpos); if byt = fail then if LastSystemError().number <> IO.EWOULDBLOCK then IO_Close(s.stdin); IO_Close(s.stdout); IO_Close(s.stderr); return fail; fi; else inpos := inpos + byt; if inpos = Length(input) then IO_Close(s.stdin); fi; fi; fi; # Now reading: if not(outeof) and r[1] <> fail then chunk := IO_Read(s.stdout,4096); if chunk = "" then outeof := true; elif chunk = fail then if inpos < Length(input) then IO_Close(s.stdin); fi; IO_Close(s.stdout); IO_Close(s.stderr); return fail; else Add(out,chunk); fi; fi; if not(erreof) and r[Length(r)] <> fail then chunk := IO_Read(s.stderr,4096); if chunk = "" then erreof := true; elif chunk = fail then if inpos < Length(input) then IO_Close(s.stdin); fi; IO_Close(s.stdout); IO_Close(s.stderr); return fail; else Append(err,chunk); fi; fi; until outeof and erreof; status := IO_WaitPid(s.pid, true); # We have to unbind this here, as by default IO will do its own # IO_WaitPid when we close stdout. Unbind(s.stdout!.dowaitpid); IO_Close(s.stdout); IO_Close(s.stderr); return rec( out := Concatenation(out), err := err, status := status ); end); InstallGlobalFunction( IO_PipeThrough, function(cmd,args,input) local byt,chunk,inpos,nr,out,outeof,r,s,w; # Start the coprocess: s := IO_Popen2(cmd,args,false,false); if s = fail then return fail; fi; # Switch the one we write to to non-blocking mode, just to be sure! IO_fcntl(s.stdin!.fd,IO.F_SETFL,IO.O_NONBLOCK); # Here we just do I/O multiplexing, sending away input (if non-empty) # and receiving stdout and stderr. # Note that the flushing part is superfluous since we switched off # the buffers, but still, like this, the code would also work with # buffering. inpos := 0; outeof := false; # Here we collect stdout: out := []; if Length(input) = 0 then IO_Close(s.stdin); fi; repeat if not(outeof) then r := [s.stdout]; else r := []; fi; if inpos < Length(input) then w := [s.stdin]; else w := []; fi; nr := IO_Select(r,w,[],[],fail,fail); if nr = fail then # an error occurred if inpos < Length(input) then IO_Close(s.stdin); fi; IO_Close(s.stdout); return fail; fi; # First writing: if Length(w) > 0 and w[1] <> fail then byt := IO_WriteNonBlocking(s.stdin,input,inpos,Length(input)-inpos); if byt = fail then if LastSystemError().number <> IO.EWOULDBLOCK then if inpos < Length(input) then IO_Close(s.stdin); fi; IO_Close(s.stdout); return fail; fi; else inpos := inpos + byt; if inpos = Length(input) then IO_Close(s.stdin); fi; fi; fi; # Now reading: if not(outeof) and r[1] <> fail then chunk := IO_Read(s.stdout,4096); if chunk = "" then outeof := true; elif chunk = fail then if inpos < Length(input) then IO_Close(s.stdin); fi; IO_Close(s.stdout); return fail; else Add(out,chunk); fi; fi; until outeof; IO_Close(s.stdout); return Concatenation(out); end); if IsBoundGlobal("_IO_Defines_ChangeDirectoryCurrent") then InstallGlobalFunction( ChangeDirectoryCurrent, function( path ) if IO_chdir(path) then GAPInfo.DirectoryCurrent := Directory(path); return true; else return fail; fi; end ); Unbind(_IO_Defines_ChangeDirectoryCurrent); fi; ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 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 General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## gap-io-4.4.5+ds/gap/iohub.gd000066400000000000000000000045271264743113200155060ustar00rootroot00000000000000############################################################################# ## ## iohub.gd GAP 4 package IO ## Max Neunhoeffer ## ## Copyright (C) by Max Neunhoeffer ## This file is free software, see license information at the end. ## ## This file contains declarations for a generic client server framework ## for GAP ## ## Main points: ## ## - handle multiple connections using IO multiplexing ## - single threaded ## - use pickling for data transfer ## BindGlobal( "IOHubFamily", NewFamily("IOHubFamily") ); DeclareCategory( "IsIOHubCat", IsComponentObjectRep ); DeclareRepresentation( "IsIOHub", IsIOHubCat, [ "inqueue", "outqueue", "sock", "connections", "tosend", "torecv", "inbuf", "outbuf" ] ); DeclareOperation( "IOHub", [] ); BindGlobal( "IOHubType", NewType(IOHubFamily, IsIOHub) ); DeclareOperation( "CloseConnection", [IsIOHub, IsPosInt] ); DeclareOperation( "ShutdownServingSocket", [IsIOHub] ); DeclareOperation( "Shutdown", [IsIOHub] ); DeclareOperation( "AttachServingSocket", [IsIOHub, IsStringRep, IsPosInt] ); DeclareOperation( "NewConnection", [IsIOHub, IsInt, IsInt] ); DeclareOperation( "AcceptNewConnection", [IsIOHub] ); DeclareOperation( "GetInput", [IsIOHub, IsInt] ); DeclareOperation( "SubmitOutput", [IsIOHub, IsPosInt, IsStringRep] ); DeclareOperation( "OutputQueue", [IsIOHub] ); DeclareOperation( "InputQueue", [IsIOHub] ); DeclareOperation( "DoIO", [IsIOHub, IsBool] ); DeclareOperation( "DoIO", [IsIOHub] ); DeclareOperation( "NewTCPConnection", [IsIOHub, IsStringRep, IsPosInt] ); DeclareOperation( "StoreLenIn8Bytes", [IsStringRep, IsInt] ); DeclareOperation( "GetLenFrom8Bytes", [IsStringRep] ); ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 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 General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## gap-io-4.4.5+ds/gap/iohub.gi000066400000000000000000000312401264743113200155030ustar00rootroot00000000000000############################################################################# ## ## iohub.gd GAP 4 package IO ## Max Neunhoeffer ## ## Copyright (C) by Max Neunhoeffer ## This file is free software, see license information at the end. ## ## This file contains functions for a generic client server framework ## for GAP ## ## Main points: ## ## - handle multiple connections using IO multiplexing ## - single threaded ## - use pickling for data transfer ## InstallMethod( IOHub, "constructor without arguments", [ ], function( ) local s; s := rec( sock := fail, inqueue := [], outqueue := [], tosend := [], torecv := [], inbuf := [], outbuf := [], connections := [], isactive := true ) ; Objectify(IOHubType, s); return s; end ); InstallMethod( AttachServingSocket, "for an address and a port", [ IsIOHub, IsStringRep, IsPosInt ], function( s, address, port ) s!.sock := IO_socket(IO.PF_INET,IO.SOCK_STREAM,"tcp"); if s!.sock = fail then return fail; fi; IO_setsockopt(s!.sock,IO.SOL_SOCKET,IO.SO_REUSEADDR,"\001\001\001\001"); if IO_bind(s!.sock,IO_MakeIPAddressPort(address,port)) = fail then IO_close(s!.sock); s!.sock := fail; return fail; fi; if not(IO_listen(s!.sock,5)) then IO_close(s!.sock); s!.sock := fail; return fail; fi; return s!.sock; end ); InstallMethod( CloseConnection, "for an IO hub and a positive integer", [ IsIOHub, IsPosInt ], function( s, nr ) # First remove all entries in the queue from or for this connection: local i; if not(IsBound(s!.connections[nr])) then return fail; fi; if IsBound(s!.connections[nr][1]) then i := 1; while i <= Length(s!.inqueue) do if s!.inqueue[i][1] = nr then Remove(s!.inqueue[i]); else i := i + 1; fi; od; IO_close(s!.connections[nr][1]); fi; if IsBound(s!.connections[nr][2]) then i := 1; while i <= Length(s!.outqueue) do if s!.outqueue[i][1] = nr then Remove(s!.outqueue[i]); else i := i + 1; fi; od; IO_close(s!.connections[nr][2]); fi; Unbind(s!.connections[nr]); Unbind(s!.tosend[nr]); Unbind(s!.torecv[nr]); Unbind(s!.inbuf[nr]); Unbind(s!.outbuf[nr]); Print("Connection #",nr," closed.\n"); end ); InstallMethod( ShutdownServingSocket, "for an IO hub", [IsIOHub], function(s) if s!.sock <> fail then IO_close(s!.sock); s!.sock := fail; fi; end ); InstallMethod( Shutdown, "for an IO hub", [ IsIOHub ], function( s ) local i; if not(s!.isactive) then return; fi; for i in [1..Length(s!.connections)] do CloseConnection(s,i); od; ShutdownServingSocket(s); s!.isactive := false; end ); InstallMethod( ViewObj, "for a tcp server", [ IsIOHub ], function( s ) local nr; if s!.isactive then Print(" fail then Print(" with serving socket"); fi; nr := Number([1..Length(s!.connections)], i->IsBound(s!.connections[i]) and IsBound(s!.connections[i][1])); if nr > 0 then Print(", reading from ",nr," fds"); fi; nr := Number([1..Length(s!.connections)], i->IsBound(s!.connections[i]) and IsBound(s!.connections[i][2])); if nr > 0 then Print(", writing to ",nr," fds"); fi; Print(">"); else Print(""); fi; end ); InstallMethod( NewConnection, "for an IO hub and two integers", [ IsIOHub, IsInt, IsInt ], function( s, inp, out ) local i,l; if not(s!.isactive) then return fail; fi; i := Length(s!.connections)+1; # do not reuse old connection numbers l := []; if inp > 0 then l[1] := inp; fi; if out > 0 then l[2] := out; fi; s!.connections[i] := l; s!.tosend[i] := 0; s!.torecv[i] := 0; s!.inbuf[i] := EmptyString(8); s!.outbuf[i] := ""; return i; end ); InstallMethod( NewTCPConnection, "for an IO hub, an address and a port", [ IsIOHub, IsStringRep, IsPosInt ], function( s, address, port ) local t; t := IO_socket(IO.PF_INET,IO.SOCK_STREAM,"tcp"); if IO_connect(t,IO_MakeIPAddressPort(address,port)) = fail then IO_close(t); return fail; fi; return NewConnection(s,t,t); end ); InstallMethod( AcceptNewConnection, "for an IO hub", [ IsIOHub ], function( s ) local t,i; if not(s!.isactive) or not(IsBound(s!.sock)) then return fail; fi; t := IO_accept(s!.sock,IO_MakeIPAddressPort("0.0.0.0",0)); i := NewConnection( s, t, t ); Print("Got new connection #",i,"...\n"); return i; end ); InstallMethod( GetInput, "for an IO hub and an integer", [ IsIOHub, IsInt ], function( s, i ) local p; if not(s!.isactive) then return fail; fi; if i = 0 then # get something from any connection if Length(s!.inqueue) = 0 then return false; else return Remove(s!.inqueue,1); fi; else p := 1; while p <= Length(s!.inqueue) and s!.inqueue[p][1] <> i do p := p + 1; od; if p > Length(s!.inqueue) then return false; else return Remove(s!.inqueue,p); fi; fi; end ); InstallMethod( SubmitOutput, "for an IO hub, a positive integers and an obj", [ IsIOHub, IsPosInt, IsStringRep ], function( s, i, o ) if not(IsBound(s!.connections[i]) and IsBound(s!.connections[i][2])) then Error("This connection is closed or has no output"); return fail; fi; Add(s!.outqueue,[i,o]); return true; end ); InstallMethod( OutputQueue, "for an IO hub", [ IsIOHub ], function( s ) return s!.outqueue; end ); InstallMethod( InputQueue, "for an IO hub", [ IsIOHub ], function( s ) return s!.inqueue; end ); InstallMethod( StoreLenIn8Bytes, "for a string and a len", [IsStringRep, IsInt], function( st, len ) local c,i; for i in [1..8] do c := len mod 256; st[i] := CHAR_INT(c); len := (len - c) / 256; od; end ); InstallMethod( GetLenFrom8Bytes, "for a string", [IsStringRep], function( st ) local len,i; len := 0; for i in [8,7..1] do len := len * 256 + INT_CHAR(st[i]); od; return len; end ); InstallMethod( DoIO, "for an IO hub", [ IsIOHub ], function( s ) return DoIO(s,false); end ); InstallMethod( DoIO, "for an IO hub and a boolean", [ IsIOHub, IsBool ], function( s, block ) # This uses select to see to all open connections including the # original socket to perform all possible IO on them. New connections # are created if needed and those to which network connectivity is # lost are closed. # Note that this does not automatically call the worker on the input # queue. # However, it does serve the output queue. local activity,bytes,hadactivity,i,infds,inptab,j,len,nr,outfds,outtab,st; if not(s!.isactive) then return fail; fi; hadactivity := false; repeat activity := false; # First we check whether some output from the queue has to be sent: j := 1; while j <= Length(s!.outqueue) do i := s!.outqueue[j][1]; if s!.tosend[i] = 0 then # idle st := Concatenation("00000000",s!.outqueue[j][2]); # the first 8 will be the length len := Length(st); StoreLenIn8Bytes(st,len-8); s!.outbuf[i] := st; s!.tosend[i] := len; Remove(s!.outqueue,j); else j := j + 1; fi; od; # Now do a select: infds := EmptyPlist(Length(s!.connections)+1); outfds := EmptyPlist(Length(s!.connections)); inptab := EmptyPlist(Length(s!.connections)+1); outtab := EmptyPlist(Length(s!.connections)); for i in [1..Length(s!.connections)] do if IsBound(s!.connections[i]) then if IsBound(s!.connections[i][1]) then Add(infds,s!.connections[i][1]); Add(inptab,i); fi; if IsBound(s!.connections[i][2]) and s!.tosend[i] <> 0 then Add(outfds,s!.connections[i][2]); Add(outtab,i); fi; fi; od; if s!.sock <> fail then Add(infds,s!.sock); Add(inptab,0); fi; if block and not(hadactivity) then nr := IO_select(infds,outfds,[],false,false); else nr := IO_select(infds,outfds,[],0,0); fi; if nr > 0 then # Look for possible output first: for j in [1..Length(outfds)] do if outfds[j] <> fail then activity := true; i := outtab[j]; bytes := IO_write(s!.connections[i][2],s!.outbuf[i], Length(s!.outbuf[i])-s!.tosend[i], s!.tosend[i]); if bytes <= 0 then # an error CloseConnection(s,i); # maybe we want to have a callback here! else s!.tosend[i] := s!.tosend[i] - bytes; if s!.tosend[i] = 0 then Unbind(s!.outbuf[i]); fi; fi; fi; od; # Now look for possible inputs next: # We need to remember that some connections might already # me closed by the output routine above! for j in [1..Length(infds)] do if infds[j] <> fail then activity := true; i := inptab[j]; if i = 0 then AcceptNewConnection(s); else if IsBound(s!.connections[i]) then if s!.torecv[i] = 0 then # read length bytes := IO_read(s!.connections[i][1], s!.inbuf[i],Length(s!.inbuf[i]), 8-Length(s!.inbuf[i])); if bytes <= 0 then # an error CloseConnection(s,i); # maybe we want to have a callback here! continue; fi; if Length(s!.inbuf[i]) = 8 then s!.torecv[i]:=GetLenFrom8Bytes(s!.inbuf[i]); s!.inbuf[i] := EmptyString(s!.torecv[i]); fi; else # we are in the reading process bytes := IO_read(s!.connections[i][1], s!.inbuf[i],Length(s!.inbuf[i]), s!.torecv[i]-Length(s!.inbuf[i])); if bytes <= 0 then # an error CloseConnection(s,i); # maybe we want to have a callback here! continue; fi; if Length(s!.inbuf[i]) = s!.torecv[i] then Add(s!.inqueue,[i,s!.inbuf[i]]); s!.torecv[i] := 0; s!.inbuf[i] := EmptyString(8); fi; fi; fi; fi; fi; od; fi; if activity then hadactivity := true; fi; until activity = false; return hadactivity; end ); ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 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 General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## gap-io-4.4.5+ds/gap/pickle.gd000066400000000000000000000073141264743113200156440ustar00rootroot00000000000000############################################################################# ## ## pickle.gd GAP 4 package IO ## Max Neunhoeffer ## ## Copyright (C) by Max Neunhoeffer ## This file is free software, see license information at the end. ## ## This file contains functions for pickling and unpickling. ## DeclareGlobalVariable( "IO_PICKLECACHE" ); DeclareGlobalFunction( "IO_ClearPickleCache" ); DeclareGlobalFunction( "IO_AddToPickled" ); DeclareGlobalFunction( "IO_IsAlreadyPickled" ); DeclareGlobalFunction( "IO_FinalizePickled" ); DeclareGlobalFunction( "IO_AddToUnpickled" ); DeclareGlobalFunction( "IO_FinalizeUnpickled" ); DeclareGlobalFunction( "IO_WriteSmallInt" ); DeclareGlobalFunction( "IO_ReadSmallInt" ); DeclareGlobalFunction( "IO_WriteAttribute" ); DeclareGlobalFunction( "IO_ReadAttribute" ); DeclareGlobalFunction( "IO_PickleByString" ); DeclareGlobalFunction( "IO_UnpickleByEvalString" ); DeclareGlobalFunction( "IO_UnpickleByFunction" ); DeclareGlobalFunction( "IO_GenericObjectPickler" ); DeclareGlobalFunction( "IO_GenericObjectUnpickler" ); DeclareOperation( "IO_Pickle", [ IsFile, IsObject ] ); DeclareOperation( "IO_Unpickle", [ IsFile ] ); DeclareOperation( "IO_Pickle", [ IsObject ]); DeclareOperation( "IO_Unpickle", [ IsStringRep ]); BindGlobal ("IO_Unpicklers", rec() ); # Here is an overview over the defined tags in this package: # # CHAR a character # CYCL a cyclotomic # FAIL fail # FALS false # FFEL a finite field element # FLOT a Floating point number # FUNC a GAP function, if it is a global one, only its name is pickled # GAPL a gap in a list (unbound entries) # GSLP a GAP straight line program # IF2M an immutable compressed GF2 matrix # IF2V an immutable compressed GF2 vector # IF8M an immutable compressed 8Bit matrix # IF8V an immutable compressed 8Bit vector # ILIS an immutable list # INTG an integer # IREC an immutable record # ISTR an immutable string # MF2M a mutable compressed GF2 matrix # MF2V a mutable compressed GF2 vector # MF8M a mutable compressed 8Bit matrix # MF8V a mutable compressed 8Bit vector # MLIS a mutable list # MREC a mutable record # MSTR a mutable string # OPER a GAP operation, only its name is pickled # PERM a permutation # PPER a partial permutation # POLF an object in the representation IsPolynomialDefaultRep # POLY a Laurent polynomial (or a rational function) deprecated # RATF an object in the representation IsRationalFunctionDefaultRep # RSGL the global random source # RSGA a GAP random source # RSMT a Mersenne twister random source # RSRE a really random source # SPRF SuPeRfail # SREF a self-reference # TRAN a transformation # TRUE true # UPOL an object in the representation IsLaurentPolynomialDefaultRep # URFU an object in the representation IsUnivariateRationalFunctionDefaultRep # # Some tags defined in other packages: # # ICVC an immutable cvec # MCVC a mutable cvec # ICMA an immutable cmat # MCMA a mutable cmat # CMOD a module from the CHOP package # GWDG a GAP word generator from the CHOP package # ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 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 General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## gap-io-4.4.5+ds/gap/pickle.gi000066400000000000000000001014021264743113200156420ustar00rootroot00000000000000############################################################################# ## ## pickle.gi GAP 4 package IO ## Max Neunhoeffer ## ## Copyright (C) by Max Neunhoeffer ## This file is free software, see license information at the end. ## ## This file contains functions for pickling and unpickling. ## ################# # (Un-)Pickling: ################# InstallValue( IO_PICKLECACHE, rec( ids := [], nrs := [], obs := [], depth := 0 ) ); InstallGlobalFunction( IO_ClearPickleCache, function( ) IO_PICKLECACHE.ids := []; IO_PICKLECACHE.nrs := []; IO_PICKLECACHE.obs := []; IO_PICKLECACHE.depth := 0; end ); InstallGlobalFunction( IO_AddToPickled, function( ob ) local id,pos; IO_PICKLECACHE.depth := IO_PICKLECACHE.depth + 1; id := IO_MasterPointerNumber(ob); pos := PositionSorted( IO_PICKLECACHE.ids, id ); if pos <= Length(IO_PICKLECACHE.ids) and IO_PICKLECACHE.ids[pos] = id then return IO_PICKLECACHE.nrs[pos]; else Add(IO_PICKLECACHE.ids,id,pos); Add(IO_PICKLECACHE.nrs,Length(IO_PICKLECACHE.ids),pos); # Store a reference here so the result of # IO_MasterPointerNumber will not get reused by another object. Add(IO_PICKLECACHE.obs,ob); return false; fi; end ); InstallGlobalFunction( IO_IsAlreadyPickled, function( ob ) local id,pos; id := IO_MasterPointerNumber(ob); pos := PositionSorted( IO_PICKLECACHE.ids, id ); if pos <= Length(IO_PICKLECACHE.ids) and IO_PICKLECACHE.ids[pos] = id then return IO_PICKLECACHE.nrs[pos]; else return false; fi; end ); InstallGlobalFunction( IO_FinalizePickled, function( ) if IO_PICKLECACHE.depth <= 0 then Error("pickling depth has gone below zero!"); fi; IO_PICKLECACHE.depth := IO_PICKLECACHE.depth - 1; if IO_PICKLECACHE.depth = 0 then # important to clear the cache: IO_ClearPickleCache(); fi; end ); InstallGlobalFunction( IO_AddToUnpickled, function( ob ) IO_PICKLECACHE.depth := IO_PICKLECACHE.depth + 1; Add( IO_PICKLECACHE.obs, ob ); end ); InstallGlobalFunction( IO_FinalizeUnpickled, function( ) if IO_PICKLECACHE.depth <= 0 then Error("pickling depth has gone below zero!"); fi; IO_PICKLECACHE.depth := IO_PICKLECACHE.depth - 1; if IO_PICKLECACHE.depth = 0 then # important to clear the cache: IO_ClearPickleCache(); fi; end ); InstallGlobalFunction( IO_WriteSmallInt, function( f, i ) local h,l; h := HexStringInt(i); l := Length(h); Add(h,CHAR_INT(Length(h)),1); if IO_Write(f,h) = fail then return IO_Error; else return IO_OK; fi; end ); InstallGlobalFunction( IO_ReadSmallInt, function( f ) local h,l; l := IO_ReadBlock(f,1); if l = "" or l = fail then return IO_Error; fi; h := IO_ReadBlock(f,INT_CHAR(l[1])); if h = fail or Length(h) < INT_CHAR(l[1]) then return IO_Error; fi; return IntHexString(h); end ); InstallGlobalFunction( IO_WriteAttribute, # can also do properties function( f, at, ob ) if IO_Pickle(f, Tester(at)(ob)) = IO_Error then return IO_Error; fi; if Tester(at)(ob) then if IO_Pickle(f, at(ob)) = IO_Error then return IO_Error; fi; fi; return IO_OK; end ); InstallGlobalFunction( IO_ReadAttribute, # can also do properties function( f, at, ob ) local val; val := IO_Unpickle(f); if val = IO_Error then return IO_Error; fi; if val = true then val := IO_Unpickle(f); if val = IO_Error then return IO_Error; fi; Setter(at)(ob,val); fi; return IO_OK; end ); InstallGlobalFunction( IO_PickleByString, function( f, ob, tag ) local s; s := String(ob); if IO_Write(f,tag) = fail then return IO_Error; fi; if IO_WriteSmallInt(f,Length(s)) = IO_Error then return IO_Error; fi; if IO_Write(f,s) = fail then return IO_Error; fi; return IO_OK; end ); InstallGlobalFunction( IO_UnpickleByFunction, function( unpickleFn ) return function( f ) local len,s; len := IO_ReadSmallInt(f); if len = IO_Error then return IO_Error; fi; s := IO_ReadBlock(f,len); if s = fail then return IO_Error; fi; return unpickleFn(s); end; end ); InstallGlobalFunction( IO_UnpickleByEvalString, IO_UnpickleByFunction(EvalString) ); InstallGlobalFunction( IO_GenericObjectPickler, function( f, tag, prepickle, ob, atts, filts, comps ) local at,com,fil,nr,o; nr := IO_IsAlreadyPickled(ob); if nr = false then # not yet known if IO_Write(f,tag) = fail then return IO_Error; fi; for o in prepickle do if IO_Pickle(f,o) = IO_Error then return IO_Error; fi; od; nr := IO_AddToPickled(ob); if nr <> false then Error("prepickle objects had references to object - panic!"); return IO_Error; fi; for at in atts do if IO_WriteAttribute(f,at,ob) = IO_Error then IO_FinalizePickled(); return IO_Error; fi; od; for fil in filts do if IO_Pickle(f,fil(ob)) = IO_Error then IO_FinalizePickled(); return IO_Error; fi; od; for com in comps do if IsBound(ob!.(com)) then if IO_Pickle(f,com) = IO_Error then IO_FinalizePickled(); return IO_Error; fi; if IO_Pickle(f,ob!.(com)) = IO_Error then IO_FinalizePickled(); return IO_Error; fi; fi; od; IO_FinalizePickled(); if IO_Pickle(f,fail) = IO_Error then return IO_Error; fi; return IO_OK; else # this object was already pickled once! if IO_Write(f,"SREF") = IO_Error then return IO_Error; fi; if IO_WriteSmallInt(f,nr) = IO_Error then return IO_Error; fi; return IO_OK; fi; end ); InstallGlobalFunction( IO_GenericObjectUnpickler, function( f, ob, atts, filts ) local at,fil,val,val2; IO_AddToUnpickled(ob); for at in atts do if IO_ReadAttribute(f,at,ob) = IO_Error then IO_FinalizeUnpickled(); return IO_Error; fi; od; for fil in filts do val := IO_Unpickle(f); if val = IO_Error then IO_FinalizeUnpickled(); return IO_Error; fi; if val <> fil(ob) then if val then SetFilterObj(ob,fil); else ResetFilterObj(ob,fil); fi; fi; od; while true do val := IO_Unpickle(f); if val = fail then IO_FinalizeUnpickled(); return ob; fi; if val = IO_Error then IO_FinalizeUnpickled(); return IO_Error; fi; if IsString(val) then val2 := IO_Unpickle(f); if val2 = IO_Error then IO_FinalizeUnpickled(); return IO_Error; fi; ob!.(val) := val2; fi; od; end ); InstallMethod( IO_Unpickle, "for a file", [ IsFile ], function( f ) local magic,up; magic := IO_ReadBlock(f,4); if magic = fail then return IO_Error; elif Length(magic) < 4 then return IO_Nothing; fi; if not(IsBound(IO_Unpicklers.(magic))) then Print("No unpickler for magic value \"",magic,"\"\n"); Print("Maybe you have to load a package for this to work?\n"); return IO_Error; fi; up := IO_Unpicklers.(magic); if IsFunction(up) then return up(f); else return up; fi; end ); InstallMethod(IO_Pickle, "for an object, pickle to string method", [IsObject], function(o) local f,s; s := EmptyString(1000000); f := IO_WrapFD(-1,false,s); IO_Pickle(f,o); IO_Close(f); ShrinkAllocationString(s); return s; end); InstallMethod(IO_Unpickle, "for a string, unpickle from string method", [IsStringRep], function(s) local f,o; f := IO_WrapFD(-1,s,false); o := IO_Unpickle(f); IO_Close(f); return o; end); InstallMethod( IO_Pickle, "for an integer", [ IsFile, IsInt ], function( f, i ) local h; if IO_Write( f, "INTG" ) = fail then return IO_Error; fi; h := HexStringInt(i); if IO_WriteSmallInt( f, Length(h) ) = fail then return IO_Error; fi; if IO_Write(f,h) = fail then return fail; fi; return IO_OK; end ); IO_Unpicklers.INTG := function( f ) local h,len; len := IO_ReadSmallInt(f); if len = IO_Error then return IO_Error; fi; h := IO_ReadBlock(f,len); if h = fail or Length(h) < len then return IO_Error; fi; return IntHexString(h); end; InstallMethod( IO_Pickle, "for a string", [ IsFile, IsStringRep and IsList ], function( f, s ) local tag; if IsMutable(s) then tag := "MSTR"; else tag := "ISTR"; fi; if IO_Write(f,tag) = fail then return IO_Error; fi; if IO_WriteSmallInt(f, Length(s)) = IO_Error then return IO_Error; fi; if IO_Write(f,s) = fail then return IO_Error; fi; return IO_OK; end ); IO_Unpicklers.MSTR := function( f ) local len,s; len := IO_ReadSmallInt(f); if len = IO_Error then return IO_Error; fi; s := IO_ReadBlock(f,len); if s = fail or Length(s) < len then return IO_Error; fi; return s; end; IO_Unpicklers.ISTR := function( f ) local s; s := IO_Unpicklers.MSTR(f); if s = IO_Error then return IO_Error; fi; MakeImmutable(s); return s; end; InstallMethod( IO_Pickle, "for a boolean", [ IsFile, IsBool ], function( f, b ) local val; if b = false then val := "FALS"; elif b = true then val := "TRUE"; elif b = fail then val := "FAIL"; elif b = SuPeRfail then val := "SPRF"; else Error("Unknown boolean value"); fi; if IO_Write(f,val) = fail then return IO_Error; else return IO_OK; fi; end ); IO_Unpicklers.FALS := false; IO_Unpicklers.TRUE := true; IO_Unpicklers.FAIL := fail; IO_Unpicklers.SPRF := SuPeRfail; InstallMethod( IO_Pickle, "for a permutation", [ IsFile, IsPerm ], function( f, p ) return IO_PickleByString( f, p, "PERM" ); end ); IO_Unpicklers.PERM := IO_UnpickleByEvalString; InstallMethod( IO_Pickle, "for a transformation", [ IsFile, IsTransformation ], function( f, t ) if IO_Write(f,"TRAN") = fail then return IO_Error; fi; if IO_Pickle(f,ListTransformation(t)) = IO_Error then return IO_Error; fi; return IO_OK; end); IO_Unpicklers.TRAN := function( f ) local l; l := IO_Unpickle(f); if l = IO_Error then return IO_Error; fi; return TransformationList(l); end; InstallMethod( IO_Pickle, "for a partial perm", [ IsFile, IsPartialPerm ], function( f, pp ) local d; d := DomainOfPartialPerm(pp); if IO_Write(f,"PPER") = fail then return IO_Error; fi; if IO_Pickle(f,d) = IO_Error then return IO_Error; fi; if IO_Pickle(f,List(d, x -> x^pp)) = IO_Error then return IO_Error; fi; return IO_OK; end); IO_Unpicklers.PPER := function( f ) local dom, im; dom := IO_Unpickle(f); if dom = IO_Error then return IO_Error; fi; im := IO_Unpickle(f); if im = IO_Error then return IO_Error; fi; return PartialPerm(dom, im); end; InstallMethod( IO_Pickle, "for a float", [ IsFile, IsFloat ], function( f, fl ) return IO_PickleByString( f, fl, "FLOT" ); end ); IO_Unpicklers.FLOT := IO_UnpickleByFunction(Float); InstallMethod( IO_Pickle, "for a character", [ IsFile, IsChar ], function(f, c) local s; s := "CHARx"; s[5] := c; if IO_Write(f,s) = fail then return IO_Error; fi; return IO_OK; end ); IO_Unpicklers.CHAR := function( f ) local s; s := IO_ReadBlock(f,1); if s = fail or Length(s) < 1 then return IO_Error; fi; return s[1]; end; InstallMethod( IO_Pickle, "for a finite field element", [ IsFile, IsFFE ], function( f, ffe ) return IO_PickleByString( f, ffe, "FFEL" ); end ); IO_Unpicklers.FFEL := IO_UnpickleByEvalString; InstallMethod( IO_Pickle, "for a cyclotomic", [ IsFile, IsCyclotomic ], function( f, cyc ) return IO_PickleByString( f, cyc, "CYCL" ); end ); IO_Unpicklers.CYCL := IO_UnpickleByEvalString; InstallMethod( IO_Pickle, "for a list", [ IsFile, IsList ], function( f, l ) local count,i,nr,tag; nr := IO_AddToPickled(l); if nr = false then # not yet known # Here we have to do something if IsMutable(l) then tag := "M"; else tag := "I"; fi; if IsGF2VectorRep(l) then Append(tag,"F2V"); elif Is8BitVectorRep(l) then Append(tag,"F8V"); elif IsGF2MatrixRep(l) then Append(tag,"F2M"); elif Is8BitMatrixRep(l) then Append(tag,"F8M"); else Append(tag,"LIS"); fi; if IO_Write(f,tag) = fail then IO_FinalizePickled(); return IO_Error; fi; if IO_WriteSmallInt(f,Length(l)) = IO_Error then IO_FinalizePickled(); return IO_Error; fi; count := 0; i := 1; while i <= Length(l) do if not(IsBound(l[i])) then count := count + 1; else if count > 0 then if IO_Write(f,"GAPL") = fail then IO_FinalizePickled(); return IO_Error; fi; if IO_WriteSmallInt(f,count) = IO_Error then IO_FinalizePickled(); return IO_Error; fi; count := 0; fi; if IO_Pickle(f,l[i]) = IO_Error then IO_FinalizePickled(); return IO_Error; fi; fi; i := i + 1; od; # Note that the last entry is always bound! IO_FinalizePickled(); return IO_OK; else if IO_Write(f,"SREF") = IO_Error then IO_FinalizePickled(); return IO_Error; fi; if IO_WriteSmallInt(f,nr) = IO_Error then IO_FinalizePickled(); return IO_Error; fi; IO_FinalizePickled(); return IO_OK; fi; end ); IO_Unpicklers.MLIS := function( f ) local i,j,l,len,ob; len := IO_ReadSmallInt(f); if len = IO_Error then return IO_Error; fi; l := 0*[1..len]; IO_AddToUnpickled(l); i := 1; while i <= len do ob := IO_Unpickle(f); if ob = IO_Error then IO_FinalizeUnpickled(); return IO_Error; fi; # IO_OK or IO_Nothing cannot happen! if IO_Result(ob) then if ob!.val = "Gap" then # this is a Gap for j in [0..ob!.nr-1] do Unbind(l[i+j]); od; i := i + ob!.nr; fi; else l[i] := ob; i := i + 1; fi; od; # i is already incremented IO_FinalizeUnpickled(); return l; end; IO_Unpicklers.ILIS := function( f ) local l; l := IO_Unpicklers.MLIS(f); if l = IO_Error then return IO_Error; fi; MakeImmutable(l); return l; end; IO_Unpicklers.MF2V := function( f ) local v; v := IO_Unpicklers.MLIS(f); if v = IO_Error then return IO_Error; fi; ConvertToVectorRep(v,2); return v; end; IO_Unpicklers.MF8V := function( f ) local v; v := IO_Unpicklers.MLIS(f); if v = IO_Error then return IO_Error; fi; ConvertToVectorRep(v); return v; end; IO_Unpicklers.IF2V := function( f ) local v; v := IO_Unpicklers.MLIS(f); if v = IO_Error then return IO_Error; fi; ConvertToVectorRep(v); MakeImmutable(v); return v; end; IO_Unpicklers.IF8V := function( f ) local v; v := IO_Unpicklers.MLIS(f); if v = IO_Error then return IO_Error; fi; ConvertToVectorRep(v); MakeImmutable(v); return v; end; IO_Unpicklers.MF2M := function( f ) local v; v := IO_Unpicklers.MLIS(f); if v = IO_Error then return IO_Error; fi; ConvertToMatrixRep(v,2); return v; end; IO_Unpicklers.MF8M := function( f ) local v; v := IO_Unpicklers.MLIS(f); if v = IO_Error then return IO_Error; fi; ConvertToMatrixRep(v); return v; end; IO_Unpicklers.IF2M := function( f ) local v; v := IO_Unpicklers.MLIS(f); if v = IO_Error then return IO_Error; fi; ConvertToMatrixRep(v); MakeImmutable(v); return v; end; IO_Unpicklers.IF8M := function( f ) local v; v := IO_Unpicklers.MLIS(f); if v = IO_Error then return IO_Error; fi; ConvertToMatrixRep(v); MakeImmutable(v); return v; end; IO_Unpicklers.GAPL := function( f ) local ob; ob := rec( val := "Gap", nr := IO_ReadSmallInt(f) ); if ob.nr = IO_Error then return IO_Error; fi; return Objectify( NewType( IO_ResultsFamily, IO_Result ), ob ); end; IO_Unpicklers.SREF := function( f ) local nr; nr := IO_ReadSmallInt(f); if nr = IO_Error then return IO_Error; fi; if not(IsBound(IO_PICKLECACHE.obs[nr])) then Print("Found a self-reference to an unknown object!\n"); return IO_Error; fi; return IO_PICKLECACHE.obs[nr]; end; InstallMethod( IO_Pickle, "for a record", [ IsFile, IsRecord ], function( f, r ) local n,names,nr,tag; nr := IO_AddToPickled(r); if nr = false then # not yet known # Here we have to do something if IsMutable(r) then tag := "MREC"; else tag := "IREC"; fi; if IO_Write(f,tag) = fail then IO_FinalizePickled(); return IO_Error; fi; names := RecNames(r); if IO_WriteSmallInt(f,Length(names)) = IO_Error then IO_FinalizePickled(); return IO_Error; fi; for n in names do if IO_Pickle(f,n) = IO_Error then IO_FinalizePickled(); return IO_Error; fi; if IO_Pickle(f,r.(n)) = IO_Error then IO_FinalizePickled(); return IO_Error; fi; od; IO_FinalizePickled(); return IO_OK; else if IO_Write(f,"SREF") = IO_Error then IO_FinalizePickled(); return IO_Error; fi; if IO_WriteSmallInt(f,nr) = IO_Error then IO_FinalizePickled(); return IO_Error; fi; IO_FinalizePickled(); return IO_OK; fi; end ); IO_Unpicklers.MREC := function( f ) local i,len,name,ob,r; len := IO_ReadSmallInt(f); if len = IO_Error then return IO_Error; fi; r := rec(); IO_AddToUnpickled(r); for i in [1..len] do name := IO_Unpickle(f); if name = IO_Error or not(IsString(name)) then IO_FinalizeUnpickled(); return IO_Error; fi; ob := IO_Unpickle(f); if IO_Result(ob) then if ob = IO_Error then IO_FinalizeUnpickled(); return IO_Error; fi; else r.(name) := ob; fi; od; IO_FinalizeUnpickled(); return r; end; IO_Unpicklers.IREC := function( f ) local r; r := IO_Unpicklers.MREC(f); if r = IO_Error then return IO_Error; fi; MakeImmutable(r); return r; end; InstallMethod( IO_Pickle, "IO_Results are forbidden", [ IsFile, IO_Result ], function( f, ob ) Print("Pickling of IO_Result is forbidden!\n"); return IO_Error; end ); InstallMethod( IO_Pickle, "for rational functions", [ IsFile, IsPolynomialFunction and IsRationalFunctionDefaultRep ], function( f, pol ) local num,den,one; one := One(CoefficientsFamily(FamilyObj(pol))); num := ExtRepNumeratorRatFun(pol); den := ExtRepDenominatorRatFun(pol); if IO_Write(f,"RATF") = fail then return IO_Error; fi; if IO_Pickle(f,one) = IO_Error then return IO_Error; fi; if IO_Pickle(f,num) = IO_Error then return IO_Error; fi; if IO_Pickle(f,den) = IO_Error then return IO_Error; fi; return IO_OK; end ); IO_Unpicklers.RATF := function( f ) local num,den,one,poly; one := IO_Unpickle(f); if one = IO_Error then return IO_Error; fi; num := IO_Unpickle(f); if num = IO_Error then return IO_Error; fi; den := IO_Unpickle(f); if den = IO_Error then return IO_Error; fi; poly := RationalFunctionByExtRepNC( RationalFunctionsFamily(FamilyObj(one)),num,den); return poly; end; InstallMethod( IO_Pickle, "for rational functions", [ IsFile, IsPolynomialFunction and IsPolynomialDefaultRep ], function( f, pol ) local num,one; one := One(CoefficientsFamily(FamilyObj(pol))); num := ExtRepNumeratorRatFun(pol); if IO_Write(f,"POLF") = fail then return IO_Error; fi; if IO_Pickle(f,one) = IO_Error then return IO_Error; fi; if IO_Pickle(f,num) = IO_Error then return IO_Error; fi; return IO_OK; end ); IO_Unpicklers.POLF := function( f ) local num,one,poly; one := IO_Unpickle(f); if one = IO_Error then return IO_Error; fi; num := IO_Unpickle(f); if num = IO_Error then return IO_Error; fi; poly := PolynomialByExtRepNC( RationalFunctionsFamily(FamilyObj(one)),num); return poly; end; # This is for compatibility only and will go eventually: IO_Unpicklers.POLY := function( f ) local ext,one,poly; one := IO_Unpickle(f); if one = IO_Error then return IO_Error; fi; ext := IO_Unpickle(f); if ext = IO_Error then return IO_Error; fi; poly := PolynomialByExtRepNC( RationalFunctionsFamily(FamilyObj(one)),ext); IsUnivariatePolynomial(poly); # to make it learn IsLaurentPolynomial(poly); # to make it learn return poly; end; InstallMethod( IO_Pickle, "for a univariate Laurent polynomial", [ IsFile, IsLaurentPolynomial and IsLaurentPolynomialDefaultRep ], function( f, pol ) local cofs,one,ind; one := One(CoefficientsFamily(FamilyObj(pol))); cofs := CoefficientsOfLaurentPolynomial(pol); ind := IndeterminateNumberOfLaurentPolynomial(pol); if IO_Write(f,"UPOL") = fail then return IO_Error; fi; if IO_Pickle(f,one) = IO_Error then return IO_Error; fi; if IO_Pickle(f,cofs) = IO_Error then return IO_Error; fi; if IO_Pickle(f,ind) = IO_Error then return IO_Error; fi; return IO_OK; end ); IO_Unpicklers.UPOL := function( f ) local cofs,one,ind,poly; one := IO_Unpickle(f); if one = IO_Error then return IO_Error; fi; cofs := IO_Unpickle(f); if cofs = IO_Error then return IO_Error; fi; ind := IO_Unpickle(f); if ind = IO_Error then return IO_Error; fi; poly := LaurentPolynomialByCoefficients(FamilyObj(one),cofs[1],cofs[2],ind); return poly; end; InstallMethod( IO_Pickle, "for a univariate rational function", [ IsFile, IsUnivariateRationalFunction and IsUnivariateRationalFunctionDefaultRep ], function( f, pol ) local cofs,one,ind; one := One(CoefficientsFamily(FamilyObj(pol))); cofs := CoefficientsOfUnivariateRationalFunction(pol); ind := IndeterminateNumberOfUnivariateRationalFunction(pol); if IO_Write(f,"URFU") = fail then return IO_Error; fi; if IO_Pickle(f,one) = IO_Error then return IO_Error; fi; if IO_Pickle(f,cofs) = IO_Error then return IO_Error; fi; if IO_Pickle(f,ind) = IO_Error then return IO_Error; fi; return IO_OK; end ); IO_Unpicklers.URFU := function( f ) local cofs,one,ind,poly; one := IO_Unpickle(f); if one = IO_Error then return IO_Error; fi; cofs := IO_Unpickle(f); if cofs = IO_Error then return IO_Error; fi; ind := IO_Unpickle(f); if ind = IO_Error then return IO_Error; fi; poly := UnivariateRationalFunctionByCoefficients( FamilyObj(one),cofs[1],cofs[2],cofs[3],ind); return poly; end; InstallMethod( IO_Pickle, "for a straight line program", [ IsFile, IsStraightLineProgram ], function( f, s ) if IO_Write(f,"GSLP") = fail then return IO_Error; fi; if IO_Pickle(f,LinesOfStraightLineProgram(s)) = IO_Error then return IO_Error; fi; if IO_Pickle(f,NrInputsOfStraightLineProgram(s)) = IO_Error then return IO_Error; fi; return IO_OK; end); IO_Unpicklers.GSLP := function( f ) local l,n,s; l := IO_Unpickle(f); if l = IO_Error then return IO_Error; fi; n := IO_Unpickle(f); if l = IO_Error then return IO_Error; fi; s := StraightLineProgramNC(l,n); return s; end; InstallMethod( IO_Pickle, "for the global random source", [ IsFile, IsRandomSource and IsGlobalRandomSource ], function( f, r ) local s; if IO_Write(f,"RSGL") = fail then return IO_Error; fi; s := State(r); if IO_Pickle(f,s) = IO_Error then return IO_Error; fi; return IO_OK; end ); IO_Unpicklers.RSGL := function( f ) local s; s := IO_Unpickle(f); if s = IO_Error then return IO_Error; fi; return RandomSource(IsGlobalRandomSource,s); end; InstallMethod( IO_Pickle, "for a GAP random source", [ IsFile, IsRandomSource and IsGAPRandomSource ], function( f, r ) local s; if IO_Write(f,"RSGA") = fail then return IO_Error; fi; s := State(r); if IO_Pickle(f,s) = IO_Error then return IO_Error; fi; return IO_OK; end ); IO_Unpicklers.RSGA := function( f ) local s; s := IO_Unpickle(f); if s = IO_Error then return IO_Error; fi; return RandomSource(IsGAPRandomSource,s); end; InstallMethod( IO_Pickle, "for a Mersenne twister random source", [ IsFile, IsRandomSource and IsMersenneTwister ], function( f, r ) local s; if IO_Write(f,"RSMT") = fail then return IO_Error; fi; s := State(r); if IO_Pickle(f,s) = IO_Error then return IO_Error; fi; return IO_OK; end ); IO_Unpicklers.RSMT := function( f ) local s; s := IO_Unpickle(f); if s = IO_Error then return IO_Error; fi; return RandomSource(IsMersenneTwister,s); end; InstallMethod( IO_Pickle, "for an operation", [ IsFile, IsOperation and IsFunction ], function(f,o) if IO_Write(f,"OPER") = fail then return IO_Error; fi; if IO_Pickle(f,NAME_FUNC(o)) = IO_Error then return IO_Error; fi; return IO_OK; end ); IO_FuncToUnpickle := fail; IO_Unpicklers.OPER := function( f ) local i,s; s := IO_Unpickle(f); if s = IO_Error then return IO_Error; fi; s := Concatenation( "IO_FuncToUnpickle := ",s,";" ); i := InputTextString(s); Read(i); if not(IsBound(IO_FuncToUnpickle)) then return IO_Error; fi; s := IO_FuncToUnpickle; Unbind(IO_FuncToUnpickle); return s; end; InstallMethod( IO_Pickle, "for a function", [ IsFile, IsFunction ], function( f, fu ) local o,s; s := NAME_FUNC(fu); if not(IsBoundGlobal(s)) or not(IsIdenticalObj(ValueGlobal(s),fu)) then s := ""; o := OutputTextString(s,true); PrintTo(o,fu); CloseStream(o); if PositionSublist(s,"<>") <> fail then Print("#Error: Cannot pickle compiled function.\n"); return IO_Error; fi; fi; if IO_Write(f,"FUNC") = fail then return IO_Error; fi; if IO_Pickle(f,s) = IO_Error then return IO_Error; fi; return IO_OK; end ); IO_Unpicklers.FUNC := function( f ) local i,s; s := IO_Unpickle(f); if s = IO_Error then return IO_Error; fi; s := Concatenation( "IO_FuncToUnpickle := ",s,";" ); i := InputTextString(s); Read(i); if not(IsBound(IO_FuncToUnpickle)) then return IO_Error; fi; s := IO_FuncToUnpickle; Unbind(IO_FuncToUnpickle); return s; end; Unbind(IO_FuncToUnpickle); InstallMethod( IO_Pickle, "for a weak pointer object", [ IsFile, IsWeakPointerObject and IsList ], function( f, l ) local count,i,nr; nr := IO_AddToPickled(l); if nr = false then # not yet known # Here we have to do something if IO_Write(f,"WPOB") = fail then IO_FinalizePickled(); return IO_Error; fi; if IO_WriteSmallInt(f,Length(l)) = IO_Error then IO_FinalizePickled(); return IO_Error; fi; count := 0; i := 1; while i <= Length(l) do if not(IsBound(l[i])) then count := count + 1; else if count > 0 then if IO_Write(f,"GAPL") = fail then IO_FinalizePickled(); return IO_Error; fi; if IO_WriteSmallInt(f,count) = IO_Error then IO_FinalizePickled(); return IO_Error; fi; count := 0; fi; if IO_Pickle(f,l[i]) = IO_Error then IO_FinalizePickled(); return IO_Error; fi; fi; i := i + 1; od; # Note that the last entry is always bound! IO_FinalizePickled(); return IO_OK; else if IO_Write(f,"SREF") = IO_Error then IO_FinalizePickled(); return IO_Error; fi; if IO_WriteSmallInt(f,nr) = IO_Error then IO_FinalizePickled(); return IO_Error; fi; IO_FinalizePickled(); return IO_OK; fi; end ); IO_Unpicklers.WPOB := function( f ) local i,l,len,ob; len := IO_ReadSmallInt(f); if len = IO_Error then return IO_Error; fi; l := WeakPointerObj( [] ); if len > 0 then SetElmWPObj(l,len,0); fi; IO_AddToUnpickled(l); i := 1; while i <= len do ob := IO_Unpickle(f); if ob = IO_Error then IO_FinalizeUnpickled(); return IO_Error; fi; # IO_OK or IO_Nothing cannot happen! if IO_Result(ob) then if ob!.val = "Gap" then # this is a Gap i := i + ob!.nr; fi; else SetElmWPObj(l,i,ob); i := i + 1; fi; od; # i is already incremented IO_FinalizeUnpickled(); return l; end; InstallMethod( IO_Pickle, "for a permutation group", [ IsFile, IsPermGroup ], function( f, g ) if IO_Write(f,"PRMG") = fail then return IO_Error; fi; if IO_Pickle(f,GeneratorsOfGroup(g)) = IO_Error then return IO_Error; fi; if HasSize(g) then if IO_Pickle(f,Size(g)) = IO_Error then return IO_Error; fi; else if IO_Pickle(f,fail) = IO_Error then return IO_Error; fi; fi; if HasStabChainImmutable(g) then if IO_Pickle(f,BaseStabChain(StabChainImmutable(g))) = IO_Error then return IO_Error; fi; elif HasStabChainMutable(g) then if IO_Pickle(f,BaseStabChain(StabChainMutable(g))) = IO_Error then return IO_Error; fi; else if IO_Pickle(f,fail) = IO_Error then return IO_Error; fi; fi; return IO_OK; end ); IO_Unpicklers.PRMG := function(f) local base,g,gens,size; gens := IO_Unpickle(f); if gens = IO_Error then return IO_Error; fi; g := GroupWithGenerators(gens); size := IO_Unpickle(f); if size = IO_Error then return IO_Error; fi; if size <> fail then SetSize(g,size); fi; base := IO_Unpickle(f); if base = IO_Error then return IO_Error; fi; if base <> fail then StabChain(g,rec(knownBase := base)); fi; return g; end; InstallMethod( IO_Pickle, "for a matrix group", [ IsFile, IsMatrixGroup ], function( f, g ) return IO_GenericObjectPickler(f,"MATG",[GeneratorsOfGroup(g)],g, [Name,Size,DimensionOfMatrixGroup,FieldOfMatrixGroup],[],[]); end ); IO_Unpicklers.MATG := function(f) local g,gens; gens := IO_Unpickle(f); if gens = IO_Error then return IO_Error; fi; g := GroupWithGenerators(gens); return IO_GenericObjectUnpickler(f,g, [Name,Size,DimensionOfMatrixGroup,FieldOfMatrixGroup],[]); return g; end; InstallMethod( IO_Pickle, "for a finite field", [ IsFile, IsField and IsFinite ], function(f,F) return IO_GenericObjectPickler(f,"FFIE", [Characteristic(F),DegreeOverPrimeField(F)],F,[],[],[]); end ); IO_Unpicklers.FFIE := function(f) local d,p; p := IO_Unpickle(f); if p = IO_Error then return IO_Error; fi; d := IO_Unpickle(f); if d = IO_Error then return IO_Error; fi; if IO_Unpickle(f) <> fail then return IO_Error; fi; return GF(p,d); end; ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 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 General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## gap-io-4.4.5+ds/gap/realrandom.gd000066400000000000000000000022251264743113200165150ustar00rootroot00000000000000############################################################################# ## ## realrandom.gd IO package ## by Max Neunhoeffer ## ## Copyright (C) by Max Neunhoeffer ## This file is free software, see license information at the end. ## ## Code for "real" random sources using /dev/random ## ############################################################################# DeclareRepresentation( "IsRealRandomSource", IsRandomSource, [] ); ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 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 General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## gap-io-4.4.5+ds/gap/realrandom.gi000066400000000000000000000075561264743113200165360ustar00rootroot00000000000000############################################################################# ## ## realrandom.gi IO package ## by Max Neunhoeffer ## ## Copyright (C) 2006-2010 by Max Neunhoeffer ## This file is free software, see license information at the end. ## ## Code for "real" random sources using /dev/random ## ############################################################################# InstallMethod( Init, "for a real random source", [IsRealRandomSource,IsString], 1, function( r, type ) local f; if type <> "random" and type <> "urandom" then Error("seed must be \"random\" or \"urandom\""); fi; if type = "random" then f := IO_File("/dev/random",128); # Use smaller buffer size else f := IO_File("/dev/urandom",1024); # Use medium buffer size fi; if f = fail then return fail; fi; r!.file := f; r!.type := type; return r; end ); InstallMethod( State, "for a real random source", [IsRealRandomSource], function(r) return fail; end ); InstallMethod( Reset, "for a real random source", [IsRealRandomSource], function(r) return; end ); InstallMethod( Reset, "for a real random source and an object", [IsRealRandomSource,IsObject], function(r,o) return; end ); InstallMethod( Random, "for a real random source and two integers", [ IsRealRandomSource, IsInt, IsInt ], function( r, f, t ) local c,d,h,i,l,q,s; d := t-f; # we need d+1 different outcomes from [0..d] if d <= 0 then return fail; fi; l := (Log2Int(d)+1); # now 2^l >= d l := (l+7) - (l+7) mod 8; # this rounds up to a multiple of 8, still 2^l>=d q := QuoInt(2^l,d+1); # now q*(d+1) <= 2^l < (q+1)*(d+1) # thus for 0 <= x < 2^l # we have 0 <= x/q <= d+1 <= 2^l/q # Thus if we do QuoInt(x,q) we get something # between 0 and d inclusively, and if x is # evenly distributed in [0..2^l-1], all values # between 0 and d occur equally often repeat s := IO_ReadBlock(r!.file,l/8); # note that l is divisible by 8 h := ""; for c in s do Append(h,HexStringInt(INT_CHAR(c))); od; i := IntHexString(h); # this is now between 0 and 2^l-1 inclusively i := QuoInt(i,q); until i <= d; return f+i; end ); InstallMethod( Random, "for a real random source and a list", [ IsRealRandomSource, IsList ], function( r, l ) local nr; repeat nr := Random(r,1,Length(l)); until IsBound(l[nr]); return l[nr]; end ); InstallMethod( ViewObj, "for a real random source", [IsRealRandomSource], function(rs) Print(""); end ); InstallMethod( IO_Pickle, "for a real random source", [IsFile, IsRealRandomSource], function(f,rs) if IO_Write(f,"RSRE") = fail then return IO_Error; fi; if IO_Pickle(f,rs!.type) = IO_Error then return IO_Error; fi; return IO_OK; end ); IO_Unpicklers.RSRE := function(f) local t; t := IO_Unpickle(f); if t = IO_Error then return IO_Error; fi; return RandomSource(IsRealRandomSource,t); end; ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 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 General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## gap-io-4.4.5+ds/init.g000066400000000000000000000031571264743113200144260ustar00rootroot00000000000000############################################################################# ## ## init.g IO-package ## Max Neunhoeffer ## ## Copyright (C) by Max Neunhoeffer ## This file is free software, see license information at the end. ## ## Initialization of the IO package ## ################################ # First look after our C part: # ################################ # load kernel function if it is installed: if (not IsBound(IO)) and ("io" in SHOW_STAT()) then # try static module LoadStaticModule("io"); fi; if (not IsBound(IO)) and (Filename(DirectoriesPackagePrograms("io"), "io.so") <> fail) then LoadDynamicModule(Filename(DirectoriesPackagePrograms("io"), "io.so")); fi; ReadPackage("IO", "gap/io.gd"); ReadPackage("IO", "gap/pickle.gd"); ReadPackage("IO", "gap/realrandom.gd"); ReadPackage("IO", "gap/http.gd"); ReadPackage("IO", "gap/background.gd"); ReadPackage("IO", "gap/iohub.gd"); ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 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 General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## gap-io-4.4.5+ds/m4/000077500000000000000000000000001264743113200136255ustar00rootroot00000000000000gap-io-4.4.5+ds/m4/ac_find_gap.m4000066400000000000000000000120601264743113200163000ustar00rootroot00000000000000# Find the location of GAP # Sets GAPROOT, GAPARCH and GAP_CPPFLAGS appropriately # Can be configured using --with-gaproot=... and CONFIGNAME=... ####################################################################### AC_DEFUN([AC_FIND_GAP], [ AC_LANG_PUSH([C]) # Make sure CDPATH is portably set to a sensible value CDPATH=${ZSH_VERSION+.}: GAP_CPPFLAGS="" #Allow the user to specify a configname: AC_MSG_CHECKING([for CONFIGNAME]) AC_ARG_VAR(CONFIGNAME, [Set this to the CONFIGNAME of the GAP compilation against which you want to compile this package. Leave this variable empty for GAP versions < 4.5.]) if test "x$CONFIGNAME" = "x"; then SYSINFO="sysinfo.gap" AC_MSG_RESULT([none]) else SYSINFO="sysinfo.gap-$CONFIGNAME" AC_MSG_RESULT([$CONFIGNAME]) fi ###################################### # Find the GAP root directory by # checking for the sysinfo.gap file AC_MSG_CHECKING([for GAP root directory]) DEFAULT_GAPROOTS="../.." #Allow the user to specify the location of GAP # AC_ARG_WITH(gaproot, [AC_HELP_STRING([--with-gaproot=], [specify root of GAP installation])], [DEFAULT_GAPROOTS="$withval"]) havesysinfo=0 # Otherwise try likely directories for GAPROOT in ${DEFAULT_GAPROOTS} do # Convert the path to absolute GAPROOT=`cd $GAPROOT > /dev/null 2>&1 && pwd` if test -e ${GAPROOT}/${SYSINFO}; then havesysinfo=1 break fi done if test "x$havesysinfo" = "x1"; then AC_MSG_RESULT([${GAPROOT}]) else AC_MSG_RESULT([Not found]) echo "" echo "********************************************************************" echo " ERROR" echo "" echo " Cannot find your GAP installation. Please specify the location of" echo " GAP's root directory using --with-gaproot=" echo "" echo " The GAP root directory (as far as this package is concerned) is" echo " the one containing the file sysinfo.gap and the subdirectories " echo " src/ and bin/." echo "********************************************************************" echo "" AC_MSG_ERROR([Unable to find GAP root directory]) fi ##################################### # Now find the architecture AC_MSG_CHECKING([for GAP architecture]) GAPARCH="Unknown" . $GAPROOT/$SYSINFO if test "x$GAParch" != "x"; then GAPARCH=$GAParch fi AC_ARG_WITH(gaparch, [AC_HELP_STRING([--with-gaparch=], [override GAP architecture string])], [GAPARCH=$withval]) AC_MSG_RESULT([${GAPARCH}]) if test "x$GAPARCH" = "xUnknown" -o ! -d $GAPROOT/bin/$GAPARCH ; then echo "" echo "********************************************************************" echo " ERROR" echo "" echo " Found a GAP installation at $GAPROOT but could not find" echo " information about GAP's architecture in the" echo " file ${GAPROOT}/${SYSINFO} or did not find the directory" echo " ${GAPROOT}/bin/${GAPARCH}." echo " This file and directory should be present: please check your" echo " GAP installation." echo "********************************************************************" echo "" AC_MSG_ERROR([Unable to find plausible GAParch information.]) fi ##################################### # Now check for the GAP header files bad=0 AC_MSG_CHECKING([for GAP include files]) if test -r $GAPROOT/src/compiled.h; then AC_MSG_RESULT([$GAPROOT/src/compiled.h]) else AC_MSG_RESULT([Not found]) bad=1 fi AC_MSG_CHECKING([for GAP config.h]) if test -r $GAPROOT/bin/$GAPARCH/config.h; then AC_MSG_RESULT([$GAPROOT/bin/$GAPARCH/config.h]) else AC_MSG_RESULT([Not found]) bad=1 fi if test "x$bad" = "x1"; then echo "" echo "********************************************************************" echo " ERROR" echo "" echo " Failed to find the GAP source header files in src/ and" echo " GAP's config.h in the architecture dependend directory" echo "" echo " The kernel build process expects to find the normal GAP " echo " root directory structure as it is after building GAP itself, and" echo " in particular the files" echo " /sysinfo.gap" echo " /src/" echo " and /bin//bin/config.h." echo " Please make sure that your GAP root directory structure" echo " conforms to this layout, or give the correct GAP root using" echo " --with-gaproot=" echo "********************************************************************" echo "" AC_MSG_ERROR([Unable to find GAP include files in /src subdirectory]) fi ARCHPATH=$GAPROOT/bin/$GAPARCH GAP_CPPFLAGS="-I$GAPROOT -I$ARCHPATH" AC_MSG_CHECKING([for GAP's gmp.h location]) if test -r "$ARCHPATH/extern/gmp/include/gmp.h"; then GAP_CPPFLAGS="$GAP_CPPFLAGS -I$ARCHPATH/extern/gmp/include" AC_MSG_RESULT([$ARCHPATH/extern/gmp/include/gmp.h]) else AC_MSG_RESULT([not found, GAP was compiled without its own GMP]) fi; AC_SUBST(GAPARCH) AC_SUBST(GAPROOT) AC_SUBST(GAP_CPPFLAGS) AC_LANG_POP([C]) ]) gap-io-4.4.5+ds/m4/ax_cc_maxopt.m4000066400000000000000000000160411264743113200165360ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_cc_maxopt.html # =========================================================================== # # SYNOPSIS # # AX_CC_MAXOPT # # DESCRIPTION # # Try to turn on "good" C optimization flags for various compilers and # architectures, for some definition of "good". (In our case, good for # FFTW and hopefully for other scientific codes. Modify as needed.) # # The user can override the flags by setting the CFLAGS environment # variable. The user can also specify --enable-portable-binary in order to # disable any optimization flags that might result in a binary that only # runs on the host architecture. # # Note also that the flags assume that ANSI C aliasing rules are followed # by the code (e.g. for gcc's -fstrict-aliasing), and that floating-point # computations can be re-ordered as needed. # # Requires macros: AX_CHECK_COMPILE_FLAG, AX_COMPILER_VENDOR, # AX_GCC_ARCHFLAG, AX_GCC_X86_CPUID. # # LICENSE # # Copyright (c) 2008 Steven G. Johnson # Copyright (c) 2008 Matteo Frigo # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 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 General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 13 AC_DEFUN([AX_CC_MAXOPT], [ AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AX_COMPILER_VENDOR]) AC_REQUIRE([AC_CANONICAL_HOST]) AC_ARG_ENABLE(portable-binary, [AS_HELP_STRING([--enable-portable-binary], [disable compiler optimizations that would produce unportable binaries])], acx_maxopt_portable=$enableval, acx_maxopt_portable=no) # Try to determine "good" native compiler flags if none specified via CFLAGS if test "$ac_test_CFLAGS" != "set"; then CFLAGS="" case $ax_cv_c_compiler_vendor in dec) CFLAGS="-newc -w0 -O5 -ansi_alias -ansi_args -fp_reorder -tune host" if test "x$acx_maxopt_portable" = xno; then CFLAGS="$CFLAGS -arch host" fi;; sun) CFLAGS="-native -fast -xO5 -dalign" if test "x$acx_maxopt_portable" = xyes; then CFLAGS="$CFLAGS -xarch=generic" fi;; hp) CFLAGS="+Oall +Optrs_ansi +DSnative" if test "x$acx_maxopt_portable" = xyes; then CFLAGS="$CFLAGS +DAportable" fi;; ibm) if test "x$acx_maxopt_portable" = xno; then xlc_opt="-qarch=auto -qtune=auto" else xlc_opt="-qtune=auto" fi AX_CHECK_COMPILE_FLAG($xlc_opt, CFLAGS="-O3 -qansialias -w $xlc_opt", [CFLAGS="-O3 -qansialias -w" echo "******************************************************" echo "* You seem to have the IBM C compiler. It is *" echo "* recommended for best performance that you use: *" echo "* *" echo "* CFLAGS=-O3 -qarch=xxx -qtune=xxx -qansialias -w *" echo "* ^^^ ^^^ *" echo "* where xxx is pwr2, pwr3, 604, or whatever kind of *" echo "* CPU you have. (Set the CFLAGS environment var. *" echo "* and re-run configure.) For more info, man cc. *" echo "******************************************************"]) ;; intel) CFLAGS="-O3 -ansi_alias" if test "x$acx_maxopt_portable" = xno; then icc_archflag=unknown icc_flags="" case $host_cpu in i686*|x86_64*) # icc accepts gcc assembly syntax, so these should work: AX_GCC_X86_CPUID(0) AX_GCC_X86_CPUID(1) case $ax_cv_gcc_x86_cpuid_0 in # see AX_GCC_ARCHFLAG *:756e6547:*:*) # Intel case $ax_cv_gcc_x86_cpuid_1 in *6a?:*[[234]]:*:*|*6[[789b]]?:*:*:*) icc_flags="-xK";; *f3[[347]]:*:*:*|*f4[1347]:*:*:*) icc_flags="-xP -xN -xW -xK";; *f??:*:*:*) icc_flags="-xN -xW -xK";; esac ;; esac ;; esac if test "x$icc_flags" != x; then for flag in $icc_flags; do AX_CHECK_COMPILE_FLAG($flag, [icc_archflag=$flag; break]) done fi AC_MSG_CHECKING([for icc architecture flag]) AC_MSG_RESULT($icc_archflag) if test "x$icc_archflag" != xunknown; then CFLAGS="$CFLAGS $icc_archflag" fi fi ;; gnu) # default optimization flags for gcc on all systems CFLAGS="-O3 -fomit-frame-pointer" # -malign-double for x86 systems AX_CHECK_COMPILE_FLAG(-malign-double, CFLAGS="$CFLAGS -malign-double") # -fstrict-aliasing for gcc-2.95+ AX_CHECK_COMPILE_FLAG(-fstrict-aliasing, CFLAGS="$CFLAGS -fstrict-aliasing") # note that we enable "unsafe" fp optimization with other compilers, too AX_CHECK_COMPILE_FLAG(-ffast-math, CFLAGS="$CFLAGS -ffast-math") AX_GCC_ARCHFLAG($acx_maxopt_portable) ;; esac if test -z "$CFLAGS"; then echo "" echo "********************************************************" echo "* WARNING: Don't know the best CFLAGS for this system *" echo "* Use ./configure CFLAGS=... to specify your own flags *" echo "* (otherwise, a default of CFLAGS=-O3 will be used) *" echo "********************************************************" echo "" CFLAGS="-O3" fi AX_CHECK_COMPILE_FLAG($CFLAGS, [], [ echo "" echo "********************************************************" echo "* WARNING: The guessed CFLAGS don't seem to work with *" echo "* your compiler. *" echo "* Use ./configure CFLAGS=... to specify your own flags *" echo "********************************************************" echo "" CFLAGS="" ]) fi ]) gap-io-4.4.5+ds/m4/ax_check_compile_flag.m4000066400000000000000000000064111264743113200203370ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) # # DESCRIPTION # # Check whether the given FLAG works with the current language's compiler # or gives an error. (Warnings, however, are ignored) # # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on # success/failure. # # If EXTRA-FLAGS is defined, it is added to the current language's default # flags (e.g. CFLAGS) when the check is done. The check is thus made with # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to # force the compiler to issue an error when a bad flag is given. # # INPUT gives an alternative input source to AC_COMPILE_IFELSE. # # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this # macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. # # LICENSE # # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 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 General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 3 AC_DEFUN([AX_CHECK_COMPILE_FLAG], [AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes], [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl ])dnl AX_CHECK_COMPILE_FLAGS gap-io-4.4.5+ds/m4/ax_compiler_vendor.m4000066400000000000000000000066561264743113200177630ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_compiler_vendor.html # =========================================================================== # # SYNOPSIS # # AX_COMPILER_VENDOR # # DESCRIPTION # # Determine the vendor of the C/C++ compiler, e.g., gnu, intel, ibm, sun, # hp, borland, comeau, dec, cray, kai, lcc, metrowerks, sgi, microsoft, # watcom, etc. The vendor is returned in the cache variable # $ax_cv_c_compiler_vendor for C and $ax_cv_cxx_compiler_vendor for C++. # # LICENSE # # Copyright (c) 2008 Steven G. Johnson # Copyright (c) 2008 Matteo Frigo # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 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 General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 12 AC_DEFUN([AX_COMPILER_VENDOR], [AC_CACHE_CHECK([for _AC_LANG compiler vendor], ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor, [# note: don't check for gcc first since some other compilers define __GNUC__ vendors="intel: __ICC,__ECC,__INTEL_COMPILER ibm: __xlc__,__xlC__,__IBMC__,__IBMCPP__ pathscale: __PATHCC__,__PATHSCALE__ clang: __clang__ fujitsu: __FUJITSU gnu: __GNUC__ sun: __SUNPRO_C,__SUNPRO_CC hp: __HP_cc,__HP_aCC dec: __DECC,__DECCXX,__DECC_VER,__DECCXX_VER borland: __BORLANDC__,__TURBOC__ comeau: __COMO__ cray: _CRAYC kai: __KCC lcc: __LCC__ sgi: __sgi,sgi microsoft: _MSC_VER metrowerks: __MWERKS__ watcom: __WATCOMC__ portland: __PGI unknown: UNKNOWN" for ventest in $vendors; do case $ventest in *:) vendor=$ventest; continue ;; *) vencpp="defined("`echo $ventest | sed 's/,/) || defined(/g'`")" ;; esac AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,[ #if !($vencpp) thisisanerror; #endif ])], [break]) done ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor=`echo $vendor | cut -d: -f1` ]) ]) gap-io-4.4.5+ds/m4/ax_gcc_archflag.m4000066400000000000000000000227421264743113200171510ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_gcc_archflag.html # =========================================================================== # # SYNOPSIS # # AX_GCC_ARCHFLAG([PORTABLE?], [ACTION-SUCCESS], [ACTION-FAILURE]) # # DESCRIPTION # # This macro tries to guess the "native" arch corresponding to the target # architecture for use with gcc's -march=arch or -mtune=arch flags. If # found, the cache variable $ax_cv_gcc_archflag is set to this flag and # ACTION-SUCCESS is executed; otherwise $ax_cv_gcc_archflag is set to # "unknown" and ACTION-FAILURE is executed. The default ACTION-SUCCESS is # to add $ax_cv_gcc_archflag to the end of $CFLAGS. # # PORTABLE? should be either [yes] (default) or [no]. In the former case, # the flag is set to -mtune (or equivalent) so that the architecture is # only used for tuning, but the instruction set used is still portable. In # the latter case, the flag is set to -march (or equivalent) so that # architecture-specific instructions are enabled. # # The user can specify --with-gcc-arch= in order to override the # macro's choice of architecture, or --without-gcc-arch to disable this. # # When cross-compiling, or if $CC is not gcc, then ACTION-FAILURE is # called unless the user specified --with-gcc-arch manually. # # Requires macros: AX_CHECK_COMPILE_FLAG, AX_GCC_X86_CPUID # # (The main emphasis here is on recent CPUs, on the principle that doing # high-performance computing on old hardware is uncommon.) # # LICENSE # # Copyright (c) 2008 Steven G. Johnson # Copyright (c) 2008 Matteo Frigo # Copyright (c) 2012 Tsukasa Oi # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 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 General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 14 AC_DEFUN([AX_GCC_ARCHFLAG], [AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_CANONICAL_HOST]) AC_REQUIRE([AC_PROG_SED]) AC_ARG_WITH(gcc-arch, [AS_HELP_STRING([--with-gcc-arch=], [use architecture for gcc -march/-mtune, instead of guessing])], ax_gcc_arch=$withval, ax_gcc_arch=yes) AC_MSG_CHECKING([for gcc architecture flag]) AC_MSG_RESULT([]) AC_CACHE_VAL(ax_cv_gcc_archflag, [ ax_cv_gcc_archflag="unknown" if test "$GCC" = yes; then if test "x$ax_gcc_arch" = xyes; then ax_gcc_arch="" if test "$cross_compiling" = no; then case $host_cpu in i[[3456]]86*|x86_64*|amd64*) # use cpuid codes AX_GCC_X86_CPUID(0) AX_GCC_X86_CPUID(1) case $ax_cv_gcc_x86_cpuid_0 in *:756e6547:*:*) # Intel case $ax_cv_gcc_x86_cpuid_1 in *5[[48]]?:*:*:*) ax_gcc_arch="pentium-mmx pentium" ;; *5??:*:*:*) ax_gcc_arch=pentium ;; *1?6[[7d]]?:*:*:*) ax_gcc_arch="penryn core2 pentium-m pentium3 pentiumpro" ;; *1?6[[aef]]?:*:*:*|*2?6[[5cef]]?:*:*:*) ax_gcc_arch="corei7 core2 pentium-m pentium3 pentiumpro" ;; *1?6c?:*:*:*|*[[23]]?66?:*:*:*) ax_gcc_arch="atom core2 pentium-m pentium3 pentiumpro" ;; *2?6[[ad]]?:*:*:*) ax_gcc_arch="corei7-avx corei7 core2 pentium-m pentium3 pentiumpro" ;; *[[1-9a-f]]?6??:*:*:*) ax_gcc_arch="core2 pentiumpro" ;; *6[[3456]]?:*:*:*) ax_gcc_arch="pentium2 pentiumpro" ;; *6a?:*[[01]]:*:*) ax_gcc_arch="pentium2 pentiumpro" ;; *6a?:*[[234]]:*:*) ax_gcc_arch="pentium3 pentiumpro" ;; *6[[9de]]?:*:*:*) ax_gcc_arch="pentium-m pentium3 pentiumpro" ;; *6[[78b]]?:*:*:*) ax_gcc_arch="pentium3 pentiumpro" ;; *6f?:*:*:*) ax_gcc_arch="core2 pentium-m pentium3 pentiumpro" ;; *6??:*:*:*) ax_gcc_arch=pentiumpro ;; *f3[[347]]:*:*:*|*f4[1347]:*:*:*|*f6?:*:*:*) case $host_cpu in x86_64*) ax_gcc_arch="nocona pentium4 pentiumpro" ;; *) ax_gcc_arch="prescott pentium4 pentiumpro" ;; esac ;; *f??:*:*:*) ax_gcc_arch="pentium4 pentiumpro";; esac ;; *:68747541:*:*) # AMD case $ax_cv_gcc_x86_cpuid_1 in *5[[67]]?:*:*:*) ax_gcc_arch=k6 ;; *5[[8d]]?:*:*:*) ax_gcc_arch="k6-2 k6" ;; *5[[9]]?:*:*:*) ax_gcc_arch="k6-3 k6" ;; *60?:*:*:*) ax_gcc_arch=k7 ;; *6[[12]]?:*:*:*) ax_gcc_arch="athlon k7" ;; *6[[34]]?:*:*:*) ax_gcc_arch="athlon-tbird k7" ;; *67?:*:*:*) ax_gcc_arch="athlon-4 athlon k7" ;; *6[[68a]]?:*:*:*) AX_GCC_X86_CPUID(0x80000006) # L2 cache size case $ax_cv_gcc_x86_cpuid_0x80000006 in *:*:*[[1-9a-f]]??????:*) # (L2 = ecx >> 16) >= 256 ax_gcc_arch="athlon-xp athlon-4 athlon k7" ;; *) ax_gcc_arch="athlon-4 athlon k7" ;; esac ;; *5??f??:*:*:*) ax_gcc_arch="btver1 amdfam10 k8" ;; *6??f??:*:*:*) ax_gcc_arch="bdver1 amdfam10 k8" ;; *[[1-9a-f]]??f??:*:*:*) ax_gcc_arch="amdfam10 k8" ;; *f[[4cef8b]]?:*:*:*) ax_gcc_arch="athlon64 k8" ;; *f5?:*:*:*) ax_gcc_arch="opteron k8" ;; *f7?:*:*:*) ax_gcc_arch="athlon-fx opteron k8" ;; *f??:*:*:*) ax_gcc_arch="k8" ;; esac ;; *:746e6543:*:*) # IDT case $ax_cv_gcc_x86_cpuid_1 in *54?:*:*:*) ax_gcc_arch=winchip-c6 ;; *58?:*:*:*) ax_gcc_arch=winchip2 ;; *6[[78]]?:*:*:*) ax_gcc_arch=c3 ;; *69?:*:*:*) ax_gcc_arch="c3-2 c3" ;; esac ;; esac if test x"$ax_gcc_arch" = x; then # fallback case $host_cpu in i586*) ax_gcc_arch=pentium ;; i686*) ax_gcc_arch=pentiumpro ;; esac fi ;; sparc*) AC_PATH_PROG([PRTDIAG], [prtdiag], [prtdiag], [$PATH:/usr/platform/`uname -i`/sbin/:/usr/platform/`uname -m`/sbin/]) cputype=`(((grep cpu /proc/cpuinfo | cut -d: -f2) ; ($PRTDIAG -v |grep -i sparc) ; grep -i cpu /var/run/dmesg.boot ) | head -n 1) 2> /dev/null` cputype=`echo "$cputype" | tr -d ' -' | $SED 's/SPARCIIi/SPARCII/' |tr $as_cr_LETTERS $as_cr_letters` case $cputype in *ultrasparciv*) ax_gcc_arch="ultrasparc4 ultrasparc3 ultrasparc v9" ;; *ultrasparciii*) ax_gcc_arch="ultrasparc3 ultrasparc v9" ;; *ultrasparc*) ax_gcc_arch="ultrasparc v9" ;; *supersparc*|*tms390z5[[05]]*) ax_gcc_arch="supersparc v8" ;; *hypersparc*|*rt62[[056]]*) ax_gcc_arch="hypersparc v8" ;; *cypress*) ax_gcc_arch=cypress ;; esac ;; alphaev5) ax_gcc_arch=ev5 ;; alphaev56) ax_gcc_arch=ev56 ;; alphapca56) ax_gcc_arch="pca56 ev56" ;; alphapca57) ax_gcc_arch="pca57 pca56 ev56" ;; alphaev6) ax_gcc_arch=ev6 ;; alphaev67) ax_gcc_arch=ev67 ;; alphaev68) ax_gcc_arch="ev68 ev67" ;; alphaev69) ax_gcc_arch="ev69 ev68 ev67" ;; alphaev7) ax_gcc_arch="ev7 ev69 ev68 ev67" ;; alphaev79) ax_gcc_arch="ev79 ev7 ev69 ev68 ev67" ;; powerpc*) cputype=`((grep cpu /proc/cpuinfo | head -n 1 | cut -d: -f2 | cut -d, -f1 | $SED 's/ //g') ; /usr/bin/machine ; /bin/machine; grep CPU /var/run/dmesg.boot | head -n 1 | cut -d" " -f2) 2> /dev/null` cputype=`echo $cputype | $SED -e 's/ppc//g;s/ *//g'` case $cputype in *750*) ax_gcc_arch="750 G3" ;; *740[[0-9]]*) ax_gcc_arch="$cputype 7400 G4" ;; *74[[4-5]][[0-9]]*) ax_gcc_arch="$cputype 7450 G4" ;; *74[[0-9]][[0-9]]*) ax_gcc_arch="$cputype G4" ;; *970*) ax_gcc_arch="970 G5 power4";; *POWER4*|*power4*|*gq*) ax_gcc_arch="power4 970";; *POWER5*|*power5*|*gr*|*gs*) ax_gcc_arch="power5 power4 970";; 603ev|8240) ax_gcc_arch="$cputype 603e 603";; *) ax_gcc_arch=$cputype ;; esac ax_gcc_arch="$ax_gcc_arch powerpc" ;; esac fi # not cross-compiling fi # guess arch if test "x$ax_gcc_arch" != x -a "x$ax_gcc_arch" != xno; then for arch in $ax_gcc_arch; do if test "x[]m4_default([$1],yes)" = xyes; then # if we require portable code flags="-mtune=$arch" # -mcpu=$arch and m$arch generate nonportable code on every arch except # x86. And some other arches (e.g. Alpha) don't accept -mtune. Grrr. case $host_cpu in i*86|x86_64*) flags="$flags -mcpu=$arch -m$arch";; esac else flags="-march=$arch -mcpu=$arch -m$arch" fi for flag in $flags; do AX_CHECK_COMPILE_FLAG($flag, [ax_cv_gcc_archflag=$flag; break]) done test "x$ax_cv_gcc_archflag" = xunknown || break done fi fi # $GCC=yes ]) AC_MSG_CHECKING([for gcc architecture flag]) AC_MSG_RESULT($ax_cv_gcc_archflag) if test "x$ax_cv_gcc_archflag" = xunknown; then m4_default([$3],:) else m4_default([$2], [CFLAGS="$CFLAGS $ax_cv_gcc_archflag"]) fi ]) gap-io-4.4.5+ds/m4/ax_gcc_x86_cpuid.m4000066400000000000000000000063611264743113200172120ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_gcc_x86_cpuid.html # =========================================================================== # # SYNOPSIS # # AX_GCC_X86_CPUID(OP) # # DESCRIPTION # # On Pentium and later x86 processors, with gcc or a compiler that has a # compatible syntax for inline assembly instructions, run a small program # that executes the cpuid instruction with input OP. This can be used to # detect the CPU type. # # On output, the values of the eax, ebx, ecx, and edx registers are stored # as hexadecimal strings as "eax:ebx:ecx:edx" in the cache variable # ax_cv_gcc_x86_cpuid_OP. # # If the cpuid instruction fails (because you are running a # cross-compiler, or because you are not using gcc, or because you are on # a processor that doesn't have this instruction), ax_cv_gcc_x86_cpuid_OP # is set to the string "unknown". # # This macro mainly exists to be used in AX_GCC_ARCHFLAG. # # LICENSE # # Copyright (c) 2008 Steven G. Johnson # Copyright (c) 2008 Matteo Frigo # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 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 General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 7 AC_DEFUN([AX_GCC_X86_CPUID], [AC_REQUIRE([AC_PROG_CC]) AC_LANG_PUSH([C]) AC_CACHE_CHECK(for x86 cpuid $1 output, ax_cv_gcc_x86_cpuid_$1, [AC_RUN_IFELSE([AC_LANG_PROGRAM([#include ], [ int op = $1, eax, ebx, ecx, edx; FILE *f; __asm__("cpuid" : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (op)); f = fopen("conftest_cpuid", "w"); if (!f) return 1; fprintf(f, "%x:%x:%x:%x\n", eax, ebx, ecx, edx); fclose(f); return 0; ])], [ax_cv_gcc_x86_cpuid_$1=`cat conftest_cpuid`; rm -f conftest_cpuid], [ax_cv_gcc_x86_cpuid_$1=unknown; rm -f conftest_cpuid], [ax_cv_gcc_x86_cpuid_$1=unknown])]) AC_LANG_POP([C]) ]) gap-io-4.4.5+ds/makedoc.g000066400000000000000000000022261264743113200150620ustar00rootroot00000000000000## ## this creates the documentation, needs: GAPDoc package, latex, pdflatex, ## mkindex, dvips ## ## Call this with GAP. ## PACKAGE := "IO"; SetPackagePath(PACKAGE, "."); PrintTo("VERSION", PackageInfo(PACKAGE)[1].Version); LoadPackage("GAPDoc"); if fail <> LoadPackage("AutoDoc", ">= 2014.03.27") then AutoDoc(PACKAGE : scaffold := rec( MainPage := false )); else MakeGAPDocDoc("doc", PACKAGE, [], PACKAGE, "MathJax"); CopyHTMLStyleFiles("doc"); GAPDocManualLab(PACKAGE); fi; QUIT; ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 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 General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## gap-io-4.4.5+ds/read.g000066400000000000000000000026431264743113200143750ustar00rootroot00000000000000############################################################################# ## ## read.g The IO package ## Max Neunhoeffer ## ## Copyright (C) by Max Neunhoeffer ## This file is free software, see license information at the end. ## ReadPackage("IO", "gap/io.gi"); ReadPackage("IO", "gap/pickle.gi"); ReadPackage("IO", "gap/realrandom.gi"); ReadPackage("IO", "gap/http.gi"); ReadPackage("IO", "gap/background.gi"); ReadPackage("IO", "gap/iohub.gi"); # We now create the possibility that other packages can provide pickling # and unpickling handlers. if IsBound(IO_PkgThingsToRead) then for p in IO_PkgThingsToRead do ReadPackage(p[1],p[2]); od; Unbind(IO_PkgThingsToRead); fi; ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 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 General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## gap-io-4.4.5+ds/run_tests.sh000077500000000000000000000021431264743113200156720ustar00rootroot00000000000000#!/bin/sh # Maybe there's a better way to do this set -e mkdir tmp cd tmp case $1 in gap) git clone --depth=50 https://github.com/gap-system/gap.git gap cd gap ./configure --with-gmp=system make mkdir pkg cd pkg wget ftp://ftp.gap-system.org/pub/gap/gap47/tar.gz/packages/GAPDoc-1.5.1.tar.gz tar xvzf GAPDoc-1.5.1.tar.gz 2> /dev/null ln -s ../../.. io cd io sh autogen.sh ./configure make cd ../.. ;; hpcgap) git clone --depth=50 -b hpcgap-default https://github.com/gap-system/gap.git gap cd gap git clone --depth=50 https://github.com/gap-system/ward extern/ward ./make.hpc WARD="extern/ward" ZMQ=no GMP=system cd pkg ln -s ../../.. io cd io sh autogen.sh ./configure CFLAGS="`cat ../io/tmp/gap/build/cflags`" make cd ../.. ;; esac echo "Read(\"pkg/io/tst/testall.g\"); quit;" | sh bin/gap.sh | tee testlog.txt | grep --colour=always -E "########> Diff|$" ( ! grep "########> Diff" testlog.txt ) gap-io-4.4.5+ds/src/000077500000000000000000000000001264743113200140745ustar00rootroot00000000000000gap-io-4.4.5+ds/src/io.c000066400000000000000000002407251264743113200146610ustar00rootroot00000000000000/*************************************************************************** ** *A io.c IO-package Max Neunhoeffer ** ** ** Copyright (C) by Max Neunhoeffer ** This file is free software, see license information at the end. ** */ /* Try to use as much of the GNU C library as possible: */ #define _GNU_SOURCE #include "src/compiled.h" /* GAP headers */ #undef PACKAGE #undef PACKAGE_BUGREPORT #undef PACKAGE_NAME #undef PACKAGE_STRING #undef PACKAGE_TARNAME #undef PACKAGE_URL #undef PACKAGE_VERSION #include "pkgconfig.h" /* our own autoconf results */ /* Note that SIZEOF_VOID_P comes from GAP's config.h whereas * SIZEOF_VOID_PP comes from pkgconfig.h! */ #if SIZEOF_VOID_PP != SIZEOF_VOID_P #error GAPs word size is different from ours, 64bit/32bit mismatch #endif #include #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_TIME_H #include #endif #include #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_DIRENT_H #include #endif #ifdef HAVE_NETDB_H #include #endif #ifdef HAVE_SYS_WAIT_H #include #endif #ifdef HAVE_SIGNAL_H /* Maybe the GAP kernel headers have already included it: */ #ifndef SYS_SIGNAL_H #include #endif #endif /* We should test for existence of netinet/in.h and netinet/tcp.h, but * this would require a change in the GAP configure script, which is * tedious. */ #ifdef HAVE_NETINET_IN_H #include /* #include */ #endif #ifdef HAVE_NETINET_TCP_H #include #endif #if SYS_IS_CYGWIN32 #include #endif /* The following seems to be necessary to run under modern gcc compilers * which have the ssp stack checking enabled. Hopefully this does not * hurt in future or other versions... */ #ifdef __GNUC__ #if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) #if SYS_IS_CYGWIN32 == 0 extern void __stack_chk_fail(); void __stack_chk_fail_local (void) { __stack_chk_fail (); } #endif #endif #endif /* Functions that are done: * open, creat, read, write, close, unlink, lseek, opendir, readdir, * closedir, rewinddir, telldir, seekdir, link, rename, symlink, readlink, * rmdir, mkdir, stat, lstat, fstat, chmod, fchmod, chown, fchown, lchown, * mknod, mkfifo, dup, dup2, socket, bind, connect, gethostbyname, listen, * accept, recv, recvfrom, send, sendto, getsockopt, setsockopt, select, * fork, execv, execvp, execve, pipe, exit, getsockname, gethostname, * * Additional helper functions: * make_sockaddr_in, MakeEnvList, environ, */ /* Functions that are to do (maybe later): * * and perhaps: * socketpair, getsockname, poll, setrlimit, getrlimit, getrusage, ulimit, * NOTE: There are some problems with respect to signal handling, * because the code for InputOutputLocalProcess and things * has a signal handler for SIGCHLD, which interferes with * things. This is solved in the sense that our SIGCHLD handler * can be switched on and off, thereby providing support for * either InputOutputLocalProcess *or* fork/exec and friends. * not for the moment (portability or implementation problems): * remove, scandir, ioctl? (absolutely unportable, as it seems), * fcntl? (for file locking purposes), recvmsg, sendmsg, */ /*********************************************************************** * First we have our own SIGCHLD handler. It is a copy of the one in the * GAP kernel, however, information about all children that are not * coming from streams is stored in one data structure here, such that * we can read it out from GAP using IO.Wait. ***********************************************************************/ // FIXME: globals #define MAXCHLDS 1024 /* The following arrays make a FIFO structure: */ static int maxstats = MAXCHLDS; /* This number must always be the same */ static int stats[MAXCHLDS]; /* than this number */ static int pids[MAXCHLDS]; /* and this number! */ static int fistats = 0; /* First used entry */ static int lastats = 0; /* First unused entry */ static int statsfull = 0; /* Flag, whether stats FIFO full */ static RETSIGTYPE (*oldhandler)(int whichsig) = 0; /* the old handler */ #ifdef HAVE_SIGNAL RETSIGTYPE IO_SIGCHLDHandler( int whichsig ) { int retcode,status; /* We collect information about our child processes that have terminated: */ do { retcode = waitpid(-1, &status, WNOHANG); if (retcode > 0) { /* One of our child processes terminated */ if (WIFEXITED(status) || WIFSIGNALED(status)) { if (!statsfull) { stats[lastats] = status; pids[lastats++] = retcode; if (lastats >= maxstats) lastats = 0; if (lastats == fistats) statsfull = 1; } else Pr("#E Overflow in table of terminated processes\n",0,0); } } } while (retcode > 0); signal(SIGCHLD, IO_SIGCHLDHandler); } Obj FuncIO_InstallSIGCHLDHandler( Obj self ) { /* Do not install ourselves twice: */ if (oldhandler == 0) { oldhandler = signal(SIGCHLD, IO_SIGCHLDHandler); signal(SIGPIPE,SIG_IGN); return True; } else return False; } Obj FuncIO_RestoreSIGCHLDHandler( Obj self ) { if (oldhandler == 0) return False; else { signal(SIGCHLD,oldhandler); oldhandler = 0; signal(SIGPIPE,SIG_DFL); return True; } } Obj FuncIO_WaitPid(Obj self,Obj pid,Obj wait) { Int pidc; int pos,newpos; Obj tmp; int retcode,status; int reallytried; if (!IS_INTOBJ(pid)) { SyClearErrorNo(); return Fail; } /* First set SIGCHLD to default action to avoid clashes with access: */ signal(SIGCHLD,SIG_DFL); reallytried = 0; do { pidc = INT_INTOBJ(pid); if (fistats == lastats && !statsfull) /* queue empty */ pos = -1; else if (pidc == -1) /* queue not empty and any entry welcome */ pos = fistats; else { /* Queue nonempty, so look for matching entry: */ pos = fistats; do { if (pids[pos] == pidc) break; pos++; if (pos >= maxstats) pos = 0; if (pos == lastats) { pos = -1; /* None found */ break; } } while (1); } if (pos != -1) break; /* we found something! */ if (reallytried && wait != True) { /* Reinstantiate our handler: */ signal(SIGCHLD,IO_SIGCHLDHandler); return False; } /* Really wait for something, blocking: */ if (wait == True) retcode = waitpid(-1, &status, 0); else retcode = waitpid(-1, &status, WNOHANG); if (retcode > 0) { /* One of our child processes terminated */ if (WIFEXITED(status) || WIFSIGNALED(status)) { /* Append it to the queue: */ if (!statsfull) { stats[lastats] = status; pids[lastats++] = retcode; if (lastats >= maxstats) lastats = 0; if (lastats == fistats) statsfull = 1; } else Pr("#E Overflow in table of terminated processes\n",0,0); } } reallytried = 1; /* Do not try again. */ } while (1); /* Left by break */ tmp = NEW_PREC(0); AssPRec(tmp,RNamName("pid"),INTOBJ_INT(pids[pos])); AssPRec(tmp,RNamName("status"),INTOBJ_INT(stats[pos])); /* Dequeue element: */ if (pos == fistats) { /* this is the easy case: */ fistats++; if (fistats >= maxstats) fistats = 0; } else { /* The more difficult case: */ do { newpos = pos+1; if (newpos >= maxstats) newpos = 0; if (newpos == lastats) break; stats[pos] = stats[newpos]; pids[pos] = pids[newpos]; pos = newpos; } while(1); lastats = pos; } statsfull = 0; /* Reinstantiate our handler: */ signal(SIGCHLD,IO_SIGCHLDHandler); return tmp; } #endif Obj FuncIO_open(Obj self,Obj path,Obj flags,Obj mode) { Int res; if (!IS_STRING(path) || !IS_STRING_REP(path) || !IS_INTOBJ(flags) || !IS_INTOBJ(mode) ) { SyClearErrorNo(); return Fail; } else { res = open((char *) CHARS_STRING(path), INT_INTOBJ(flags),INT_INTOBJ(mode)); if (res < 0) { SySetErrorNo(); return Fail; } else return INTOBJ_INT(res); } } Obj FuncIO_creat(Obj self,Obj path,Obj mode) { Int res; if (!IS_STRING(path) || !IS_STRING_REP(path) || !IS_INTOBJ(mode) ) { SyClearErrorNo(); return Fail; } else { res = creat((char *) CHARS_STRING(path),INT_INTOBJ(mode)); if (res < 0) { SySetErrorNo(); return Fail; } else return INTOBJ_INT(res); } } Obj FuncIO_read(Obj self,Obj fd,Obj st,Obj offset,Obj count) { Int bytes; Int len; if (!IS_INTOBJ(fd) || !IS_STRING(st) || !IS_STRING_REP(st) || !IS_INTOBJ(count)) { SyClearErrorNo(); return Fail; } len = INT_INTOBJ(offset)+INT_INTOBJ(count); if (len > GET_LEN_STRING(st)) GrowString(st,len); bytes = read(INT_INTOBJ(fd),CHARS_STRING(st)+INT_INTOBJ(offset), INT_INTOBJ(count)); if (bytes < 0) { SySetErrorNo(); return Fail; } else { if (bytes + INT_INTOBJ(offset) > GET_LEN_STRING(st)) { SET_LEN_STRING(st,bytes + INT_INTOBJ(offset)); CHARS_STRING(st)[len] = 0; } return INTOBJ_INT(bytes); } } Obj FuncIO_write(Obj self,Obj fd,Obj st,Obj offset,Obj count) { Int bytes; if (!IS_INTOBJ(fd) || !IS_STRING(st) || !IS_STRING_REP(st) || !IS_INTOBJ(offset) || !IS_INTOBJ(count)) { SyClearErrorNo(); return Fail; } if (GET_LEN_STRING(st) < INT_INTOBJ(offset)+INT_INTOBJ(count)) { SyClearErrorNo(); return Fail; } bytes = (Int) write(INT_INTOBJ(fd),CHARS_STRING(st)+INT_INTOBJ(offset), INT_INTOBJ(count)); if (bytes < 0) { SySetErrorNo(); return Fail; } else return INTOBJ_INT(bytes); } Obj FuncIO_close(Obj self,Obj fd) { Int res; if (!IS_INTOBJ(fd)) { SyClearErrorNo(); return Fail; } else { res = close(INT_INTOBJ(fd)); if (res < 0) { SySetErrorNo(); return Fail; } else return True; } } Obj FuncIO_lseek(Obj self,Obj fd,Obj offset,Obj whence) { Int bytes; if (!IS_INTOBJ(fd) || !IS_INTOBJ(offset) || !IS_INTOBJ(whence)) { SyClearErrorNo(); return Fail; } bytes = lseek(INT_INTOBJ(fd),INT_INTOBJ(offset),INT_INTOBJ(whence)); if (bytes < 0) { SySetErrorNo(); return Fail; } else { return INTOBJ_INT(bytes); } } #ifdef HAVE_DIRENT_H // FIXME: globals static DIR *ourDIR = 0; static struct dirent *ourdirent; #ifdef HAVE_OPENDIR Obj FuncIO_opendir(Obj self,Obj name) { if (!IS_STRING(name) || !IS_STRING_REP(name)) { SyClearErrorNo(); return Fail; } else { ourDIR = opendir((char *) CHARS_STRING(name)); if (ourDIR == 0) { SySetErrorNo(); return Fail; } else return True; } } #endif /* HAVE_OPENDIR */ #ifdef HAVE_READDIR Obj FuncIO_readdir(Obj self) { Obj res; Int olderrno; if (ourDIR == 0) { SyClearErrorNo(); return Fail; } olderrno = errno; ourdirent = readdir(ourDIR); if (ourdirent == 0) { /* This is a bit of a hack, but how should this be done? */ if (errno == EBADF && olderrno != EBADF) { SySetErrorNo(); return Fail; } else { SyClearErrorNo(); return False; } } C_NEW_STRING(res,strlen(ourdirent->d_name),ourdirent->d_name); return res; } #endif /* HAVE_READDIR */ #ifdef HAVE_CLOSEDIR Obj FuncIO_closedir(Obj self) { Int res; if (ourDIR == 0) { SyClearErrorNo(); return Fail; } res = closedir(ourDIR); if (res < 0) { SySetErrorNo(); return Fail; } else return True; } #endif /* HAVE_CLOSEDIR */ #ifdef HAVE_REWINDDIR Obj FuncIO_rewinddir(Obj self) { if (ourDIR == 0) { SyClearErrorNo(); return Fail; } rewinddir(ourDIR); return True; } #endif /* HAVE_REWINDDIR */ #ifdef HAVE_TELLDIR Obj FuncIO_telldir(Obj self) { Int o; if (ourDIR == 0) { SyClearErrorNo(); return Fail; } o = telldir(ourDIR); if (o < 0) { SySetErrorNo(); return Fail; } else return INTOBJ_INT(o); } #endif /* HAVE_TELLDIR */ #ifdef HAVE_SEEKDIR Obj FuncIO_seekdir(Obj self,Obj offset) { if (!IS_INTOBJ(offset)) { SyClearErrorNo(); return Fail; } if (ourDIR == 0) { SyClearErrorNo(); return Fail; } seekdir(ourDIR,INT_INTOBJ(offset)); return True; } #endif /* HAVE_SEEKDIR */ #endif /* HAVE_DIRENT_H */ #ifdef HAVE_UNLINK Obj FuncIO_unlink(Obj self,Obj path) { Int res; if (!IS_STRING(path) || !IS_STRING_REP(path)) { SyClearErrorNo(); return Fail; } else { res = unlink((char *) CHARS_STRING(path)); if (res < 0) { SySetErrorNo(); return Fail; } else return True; } } #endif #ifdef HAVE_LINK Obj FuncIO_link(Obj self,Obj oldpath,Obj newpath) { Int res; if (!IS_STRING(oldpath) || !IS_STRING_REP(oldpath) || !IS_STRING(newpath) || !IS_STRING_REP(newpath)) { SyClearErrorNo(); return Fail; } else { res = link((char *) CHARS_STRING(oldpath),(char *) CHARS_STRING(newpath)); if (res < 0) { SySetErrorNo(); return Fail; } else return True; } } #endif #ifdef HAVE_RENAME Obj FuncIO_rename(Obj self,Obj oldpath,Obj newpath) { Int res; if (!IS_STRING(oldpath) || !IS_STRING_REP(oldpath) || !IS_STRING(newpath) || !IS_STRING_REP(newpath)) { SyClearErrorNo(); return Fail; } else { res = rename((char *) CHARS_STRING(oldpath), (char *) CHARS_STRING(newpath)); if (res < 0) { SySetErrorNo(); return Fail; } else return True; } } #endif #ifdef HAVE_SYMLINK Obj FuncIO_symlink(Obj self,Obj oldpath,Obj newpath) { Int res; if (!IS_STRING(oldpath) || !IS_STRING_REP(oldpath) || !IS_STRING(newpath) || !IS_STRING_REP(newpath)) { SyClearErrorNo(); return Fail; } else { res = symlink((char *) CHARS_STRING(oldpath), (char *) CHARS_STRING(newpath)); if (res < 0) { SySetErrorNo(); return Fail; } else return True; } } #endif #ifdef HAVE_READLINK Obj FuncIO_readlink(Obj self,Obj path,Obj buf,Obj bufsize) { Int res; if (!IS_STRING(path) || !IS_STRING_REP(path) || !IS_STRING(buf) || !IS_STRING_REP(buf) || !IS_INTOBJ(bufsize)) { SyClearErrorNo(); return Fail; } else { GrowString(buf,INT_INTOBJ(bufsize)); res = readlink((char *) CHARS_STRING(path), (char *) CHARS_STRING(buf),INT_INTOBJ(bufsize)); if (res < 0) { SySetErrorNo(); return Fail; } else { SET_LEN_STRING(buf,res); CHARS_STRING(buf)[res] = 0; return INTOBJ_INT(res); } } } #endif Obj FuncIO_chdir(Obj self,Obj pathname) { Int res; if (!IS_STRING(pathname) || !IS_STRING_REP(pathname)) { SyClearErrorNo(); return Fail; } else { res = chdir((char *) CHARS_STRING(pathname)); if (res < 0) { SySetErrorNo(); return Fail; } else return True; } } #ifdef HAVE_MKDIR Obj FuncIO_mkdir(Obj self,Obj pathname,Obj mode) { Int res; if (!IS_STRING(pathname) || !IS_STRING_REP(pathname) || !IS_INTOBJ(mode)) { SyClearErrorNo(); return Fail; } else { res = mkdir((char *) CHARS_STRING(pathname),INT_INTOBJ(mode)); if (res < 0) { SySetErrorNo(); return Fail; } else return True; } } #endif #ifdef HAVE_RMDIR Obj FuncIO_rmdir(Obj self,Obj path) { Int res; if (!IS_STRING(path) || !IS_STRING_REP(path)) { SyClearErrorNo(); return Fail; } else { res = rmdir((char *) CHARS_STRING(path)); if (res < 0) { SySetErrorNo(); return Fail; } else return True; } } #endif #ifndef ADDR_INT #define ADDR_INT(op) ((TypDigit*)ADDR_OBJ(op)) #endif #ifndef USE_GMP static Obj MyObjInt_Int(Int i) { // FIXME: WTF? Obj n; Int bound = 1L << NR_SMALL_INT_BITS; if (i >= bound) { /* We have to make a big integer */ n = NewBag(T_INTPOS,4*sizeof(TypDigit)); ADDR_INT(n)[0] = (TypDigit) (i & ((Int) INTBASE - 1L)); ADDR_INT(n)[1] = (TypDigit) (i >> NR_DIGIT_BITS); ADDR_INT(n)[2] = 0; ADDR_INT(n)[3] = 0; return n; } else if (-i > bound) { n = NewBag(T_INTNEG,4*sizeof(TypDigit)); ADDR_INT(n)[0] = (TypDigit) ((-i) & ((Int) INTBASE - 1L)); ADDR_INT(n)[1] = (TypDigit) ((-i) >> NR_DIGIT_BITS); ADDR_INT(n)[2] = 0; ADDR_INT(n)[3] = 0; return n; } else { return INTOBJ_INT(i); } } #else #define MyObjInt_Int(i) ObjInt_Int(i) #endif #ifdef HAVE_STAT // FIXME: globals static struct stat ourstatbuf; Obj FuncIO_stat(Obj self,Obj filename) { Int res; Obj rec; if (!IS_STRING(filename) || !IS_STRING_REP(filename)) { SyClearErrorNo(); return Fail; } else { res = stat((char *) CHARS_STRING(filename),&ourstatbuf); if (res < 0) { SySetErrorNo(); return Fail; } rec = NEW_PREC(0); AssPRec(rec,RNamName("dev"),MyObjInt_Int((Int) ourstatbuf.st_dev)); AssPRec(rec,RNamName("ino"),MyObjInt_Int((Int) ourstatbuf.st_ino)); AssPRec(rec,RNamName("mode"),MyObjInt_Int((Int) ourstatbuf.st_mode)); AssPRec(rec,RNamName("nlink"),MyObjInt_Int((Int) ourstatbuf.st_nlink)); AssPRec(rec,RNamName("uid"),MyObjInt_Int((Int) ourstatbuf.st_uid)); AssPRec(rec,RNamName("gid"),MyObjInt_Int((Int) ourstatbuf.st_gid)); AssPRec(rec,RNamName("rdev"),MyObjInt_Int((Int) ourstatbuf.st_rdev)); AssPRec(rec,RNamName("size"),MyObjInt_Int((Int) ourstatbuf.st_size)); AssPRec(rec,RNamName("blksize"),MyObjInt_Int((Int)ourstatbuf.st_blksize)); AssPRec(rec,RNamName("blocks"),MyObjInt_Int((Int) ourstatbuf.st_blocks)); AssPRec(rec,RNamName("atime"),MyObjInt_Int((Int) ourstatbuf.st_atime)); AssPRec(rec,RNamName("mtime"),MyObjInt_Int((Int) ourstatbuf.st_mtime)); AssPRec(rec,RNamName("ctime"),MyObjInt_Int((Int) ourstatbuf.st_ctime)); return rec; } } #endif #ifdef HAVE_FSTAT // FIXME: globals static struct stat ourfstatbuf; Obj FuncIO_fstat(Obj self,Obj fd) { Int res; Obj rec; if (!IS_INTOBJ(fd)) { SyClearErrorNo(); return Fail; } else { res = fstat(INT_INTOBJ(fd),&ourfstatbuf); if (res < 0) { SySetErrorNo(); return Fail; } rec = NEW_PREC(0); AssPRec(rec,RNamName("dev"),MyObjInt_Int((Int) ourfstatbuf.st_dev)); AssPRec(rec,RNamName("ino"),MyObjInt_Int((Int) ourfstatbuf.st_ino)); AssPRec(rec,RNamName("mode"),MyObjInt_Int((Int) ourfstatbuf.st_mode)); AssPRec(rec,RNamName("nlink"),MyObjInt_Int((Int) ourfstatbuf.st_nlink)); AssPRec(rec,RNamName("uid"),MyObjInt_Int((Int) ourfstatbuf.st_uid)); AssPRec(rec,RNamName("gid"),MyObjInt_Int((Int) ourfstatbuf.st_gid)); AssPRec(rec,RNamName("rdev"),MyObjInt_Int((Int) ourfstatbuf.st_rdev)); AssPRec(rec,RNamName("size"),MyObjInt_Int((Int) ourfstatbuf.st_size)); AssPRec(rec,RNamName("blksize"),MyObjInt_Int((Int)ourfstatbuf.st_blksize)); AssPRec(rec,RNamName("blocks"),MyObjInt_Int((Int) ourfstatbuf.st_blocks)); AssPRec(rec,RNamName("atime"),MyObjInt_Int((Int) ourfstatbuf.st_atime)); AssPRec(rec,RNamName("mtime"),MyObjInt_Int((Int) ourfstatbuf.st_mtime)); AssPRec(rec,RNamName("ctime"),MyObjInt_Int((Int) ourfstatbuf.st_ctime)); return rec; } } #endif #ifdef HAVE_LSTAT // FIXME: globals static struct stat ourlstatbuf; Obj FuncIO_lstat(Obj self,Obj filename) { Int res; Obj rec; if (!IS_STRING(filename) || !IS_STRING_REP(filename)) { SyClearErrorNo(); return Fail; } else { res = lstat((char *) CHARS_STRING(filename),&ourlstatbuf); if (res < 0) { SySetErrorNo(); return Fail; } rec = NEW_PREC(0); AssPRec(rec,RNamName("dev"),MyObjInt_Int((Int) ourlstatbuf.st_dev)); AssPRec(rec,RNamName("ino"),MyObjInt_Int((Int) ourlstatbuf.st_ino)); AssPRec(rec,RNamName("mode"),MyObjInt_Int((Int) ourlstatbuf.st_mode)); AssPRec(rec,RNamName("nlink"),MyObjInt_Int((Int) ourlstatbuf.st_nlink)); AssPRec(rec,RNamName("uid"),MyObjInt_Int((Int) ourlstatbuf.st_uid)); AssPRec(rec,RNamName("gid"),MyObjInt_Int((Int) ourlstatbuf.st_gid)); AssPRec(rec,RNamName("rdev"),MyObjInt_Int((Int) ourlstatbuf.st_rdev)); AssPRec(rec,RNamName("size"),MyObjInt_Int((Int) ourlstatbuf.st_size)); AssPRec(rec,RNamName("blksize"),MyObjInt_Int((Int)ourlstatbuf.st_blksize)); AssPRec(rec,RNamName("blocks"),MyObjInt_Int((Int) ourlstatbuf.st_blocks)); AssPRec(rec,RNamName("atime"),MyObjInt_Int((Int) ourlstatbuf.st_atime)); AssPRec(rec,RNamName("mtime"),MyObjInt_Int((Int) ourlstatbuf.st_mtime)); AssPRec(rec,RNamName("ctime"),MyObjInt_Int((Int) ourlstatbuf.st_ctime)); return rec; } } #endif #ifdef HAVE_CHMOD Obj FuncIO_chmod(Obj self,Obj pathname,Obj mode) { Int res; if (!IS_STRING(pathname) || !IS_STRING_REP(pathname) || !IS_INTOBJ(mode)) { SyClearErrorNo(); return Fail; } else { res = chmod((char *) CHARS_STRING(pathname),INT_INTOBJ(mode)); if (res < 0) { SySetErrorNo(); return Fail; } else return True; } } #endif #ifdef HAVE_FCHMOD Obj FuncIO_fchmod(Obj self,Obj fd,Obj mode) { Int res; if (!IS_INTOBJ(fd) || !IS_INTOBJ(mode)) { SyClearErrorNo(); return Fail; } else { res = fchmod(INT_INTOBJ(fd),INT_INTOBJ(mode)); if (res < 0) { SySetErrorNo(); return Fail; } else return True; } } #endif #ifdef HAVE_CHOWN Obj FuncIO_chown(Obj self,Obj path,Obj owner,Obj group) { Int res; if (!IS_STRING(path) || !IS_STRING_REP(path) || !IS_INTOBJ(owner) || !IS_INTOBJ(group)) { SyClearErrorNo(); return Fail; } else { res = chown((char *) CHARS_STRING(path), INT_INTOBJ(owner),INT_INTOBJ(group)); if (res < 0) { SySetErrorNo(); return Fail; } else return True; } } #endif #ifdef HAVE_FCHOWN Obj FuncIO_fchown(Obj self,Obj fd,Obj owner,Obj group) { Int res; if (!IS_INTOBJ(fd) || !IS_INTOBJ(owner) || !IS_INTOBJ(group)) { SyClearErrorNo(); return Fail; } else { res = fchown(INT_INTOBJ(fd),INT_INTOBJ(owner),INT_INTOBJ(group)); if (res < 0) { SySetErrorNo(); return Fail; } else return True; } } #endif #ifdef HAVE_LCHOWN Obj FuncIO_lchown(Obj self,Obj path,Obj owner,Obj group) { Int res; if (!IS_STRING(path) || !IS_STRING_REP(path) || !IS_INTOBJ(owner) || !IS_INTOBJ(group)) { SyClearErrorNo(); return Fail; } else { res = lchown((char *) CHARS_STRING(path), INT_INTOBJ(owner),INT_INTOBJ(group)); if (res < 0) { SySetErrorNo(); return Fail; } else return True; } } #endif #ifdef HAVE_MKNOD Obj FuncIO_mknod(Obj self,Obj path,Obj mode,Obj dev) { Int res; if (!IS_STRING(path) || !IS_STRING_REP(path) || !IS_INTOBJ(mode) || !IS_INTOBJ(dev)) { SyClearErrorNo(); return Fail; } else { res = mknod((char *) CHARS_STRING(path),INT_INTOBJ(mode),INT_INTOBJ(dev)); if (res < 0) { SySetErrorNo(); return Fail; } else return True; } } #endif #ifdef HAVE_MKFIFO Obj FuncIO_mkfifo(Obj self,Obj path,Obj mode) { Int res; if (!IS_STRING(path) || !IS_STRING_REP(path) || !IS_INTOBJ(mode)) { SyClearErrorNo(); return Fail; } else { res = mkfifo((char *) CHARS_STRING(path),INT_INTOBJ(mode)); if (res < 0) { SySetErrorNo(); return Fail; } else return True; } } #endif #ifdef HAVE_DUP Obj FuncIO_dup(Obj self,Obj oldfd) { Int res; if (!IS_INTOBJ(oldfd)) { SyClearErrorNo(); return Fail; } else { res = dup(INT_INTOBJ(oldfd)); if (res < 0) { SySetErrorNo(); return Fail; } else return INTOBJ_INT(res); } } #endif #ifdef HAVE_DUP2 Obj FuncIO_dup2(Obj self,Obj oldfd,Obj newfd) { Int res; if (!IS_INTOBJ(oldfd) || !IS_INTOBJ(newfd)) { SyClearErrorNo(); return Fail; } else { res = dup2(INT_INTOBJ(oldfd),INT_INTOBJ(newfd)); if (res < 0) { SySetErrorNo(); return Fail; } else return True; } } #endif #ifdef HAVE_SOCKET Obj FuncIO_socket(Obj self,Obj domain,Obj type,Obj protocol) { Int res; #ifdef HAVE_GETPROTOBYNAME struct protoent *pe; #endif Int proto; if (!IS_INTOBJ(domain) || !IS_INTOBJ(type) || !(IS_INTOBJ(protocol) #ifdef HAVE_GETPROTOBYNAME || (IS_STRING(protocol) && IS_STRING_REP(protocol)) #endif )) { SyClearErrorNo(); return Fail; } else { #ifdef HAVE_GETPROTOBYNAME if (IS_STRING(protocol)) { /* we have to look up the protocol */ pe = getprotobyname((char *) CHARS_STRING(protocol)); if (pe == NULL) { SySetErrorNo(); return Fail; } proto = pe->p_proto; } else #endif proto = INT_INTOBJ(protocol); res = socket(INT_INTOBJ(domain),INT_INTOBJ(type),proto); if (res < 0) { SySetErrorNo(); return Fail; } else return INTOBJ_INT(res); } } #endif #ifdef HAVE_BIND Obj FuncIO_bind(Obj self,Obj fd,Obj my_addr) { Int res; Int len; if (!IS_INTOBJ(fd) || !IS_STRING(my_addr) || !IS_STRING_REP(my_addr)) { SyClearErrorNo(); return Fail; } else { len = GET_LEN_STRING(my_addr); res = bind(INT_INTOBJ(fd),(struct sockaddr *)CHARS_STRING(my_addr),len); if (res < 0) { SySetErrorNo(); return Fail; } else return True; } } #endif #ifdef HAVE_CONNECT Obj FuncIO_connect(Obj self,Obj fd,Obj serv_addr) { Int res; Int len; if (!IS_INTOBJ(fd) || !IS_STRING(serv_addr) || !IS_STRING_REP(serv_addr)) { SyClearErrorNo(); return Fail; } else { len = GET_LEN_STRING(serv_addr); res = connect(INT_INTOBJ(fd), (struct sockaddr *)(CHARS_STRING(serv_addr)),len); if (res < 0) { SySetErrorNo(); return Fail; } else return True; } } #endif #ifdef HAVE_SOCKET Obj FuncIO_make_sockaddr_in(Obj self,Obj ip,Obj port) { struct sockaddr_in sa; Obj res; if (!IS_INTOBJ(port) || !IS_STRING(ip) || !IS_STRING_REP(ip) || GET_LEN_STRING(ip) != 4) { SyClearErrorNo(); return Fail; } else { memset(&sa,0,sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = htons(INT_INTOBJ(port)); memcpy(&(sa.sin_addr.s_addr),CHARS_STRING(ip),4); res = NEW_STRING(sizeof(sa)); memcpy(CHARS_STRING(res),&sa,sizeof(sa)); return res; } } #endif #ifdef HAVE_GETHOSTBYNAME Obj FuncIO_gethostbyname(Obj self,Obj name) { struct hostent *he; Obj res; Obj tmp; Obj tmp2; char **p; Int i; Int len; if (!IS_STRING(name) || !IS_STRING_REP(name)) { SyClearErrorNo(); return Fail; } else { he = gethostbyname((char *) CHARS_STRING(name)); if (he == NULL) { SySetErrorNo(); return Fail; } res = NEW_PREC(0); C_NEW_STRING(tmp,strlen(he->h_name),he->h_name); AssPRec(res,RNamName("name"),tmp); for (len = 0,p = he->h_aliases; *p != NULL ; len++, p++) ; tmp2 = NEW_PLIST(T_PLIST_DENSE,len); SET_LEN_PLIST(tmp2,len); for (i = 1,p = he->h_aliases; i <= len; i++,p++) { C_NEW_STRING(tmp,strlen(*p),*p); SET_ELM_PLIST(tmp2,i,tmp); CHANGED_BAG(tmp2); } AssPRec(res,RNamName("aliases"),tmp2); AssPRec(res,RNamName("addrtype"),INTOBJ_INT(he->h_addrtype)); AssPRec(res,RNamName("length"),INTOBJ_INT(he->h_length)); for (len = 0,p = he->h_addr_list; *p != NULL ; len++, p++) ; tmp2 = NEW_PLIST(T_PLIST_DENSE,len); SET_LEN_PLIST(tmp2,len); for (i = 1,p = he->h_addr_list; i <= len; i++,p++) { C_NEW_STRING(tmp,he->h_length,*p); SET_ELM_PLIST(tmp2,i,tmp); CHANGED_BAG(tmp2); } AssPRec(res,RNamName("addr"),tmp2); return res; } } #endif #ifdef HAVE_LISTEN Obj FuncIO_listen(Obj self,Obj s,Obj backlog) { Int res; if (!IS_INTOBJ(s) || !IS_INTOBJ(backlog)) { SyClearErrorNo(); return Fail; } else { res = listen(INT_INTOBJ(s),INT_INTOBJ(backlog)); if (res < 0) { SySetErrorNo(); return Fail; } else return True; } } #endif #ifdef HAVE_ACCEPT Obj FuncIO_accept(Obj self,Obj fd,Obj addr) { Int res; socklen_t len; if (!IS_INTOBJ(fd) || !IS_STRING(addr) || !IS_STRING_REP(addr)) { SyClearErrorNo(); return Fail; } else { len = GET_LEN_STRING(addr); res = accept(INT_INTOBJ(fd), (struct sockaddr *)(CHARS_STRING(addr)),&len); if (res < 0) { SySetErrorNo(); return Fail; } else return INTOBJ_INT(res); } } #endif #ifdef HAVE_RECV Obj FuncIO_recv(Obj self,Obj fd,Obj st,Obj offset,Obj count,Obj flags) { Int bytes; Int len; if (!IS_INTOBJ(fd) || !IS_STRING(st) || !IS_STRING_REP(st) || !IS_INTOBJ(count) || !IS_INTOBJ(flags)) { SyClearErrorNo(); return Fail; } len = INT_INTOBJ(offset)+INT_INTOBJ(count); if (len > GET_LEN_STRING(st)) GrowString(st,len); bytes = recv(INT_INTOBJ(fd),CHARS_STRING(st)+INT_INTOBJ(offset), INT_INTOBJ(count),INT_INTOBJ(flags)); if (bytes < 0) { SySetErrorNo(); return Fail; } else { if (bytes + INT_INTOBJ(offset) > GET_LEN_STRING(st)) { SET_LEN_STRING(st,bytes + INT_INTOBJ(offset)); CHARS_STRING(st)[len] = 0; } return INTOBJ_INT(bytes); } } #endif #ifdef HAVE_RECVFROM Obj FuncIO_recvfrom(Obj self,Obj fd,Obj st,Obj offset,Obj count,Obj flags, Obj from) { Int bytes; Int len; socklen_t fromlen; if (!IS_INTOBJ(fd) || !IS_STRING(st) || !IS_STRING_REP(st) || !IS_INTOBJ(count) || !IS_INTOBJ(flags) || !IS_STRING(from) || !IS_STRING_REP(from)) { SyClearErrorNo(); return Fail; } len = INT_INTOBJ(offset)+INT_INTOBJ(count); if (len > GET_LEN_STRING(st)) GrowString(st,len); fromlen = GET_LEN_STRING(from); bytes = recvfrom(INT_INTOBJ(fd),(char *) CHARS_STRING(st)+INT_INTOBJ(offset), INT_INTOBJ(count),INT_INTOBJ(flags), (struct sockaddr *)CHARS_STRING(from),&fromlen); if (bytes < 0) { SySetErrorNo(); return Fail; } else { if (bytes + INT_INTOBJ(offset) > GET_LEN_STRING(st)) { SET_LEN_STRING(st,bytes + INT_INTOBJ(offset)); CHARS_STRING(st)[len] = 0; } return INTOBJ_INT(bytes); } } #endif #ifdef HAVE_SEND Obj FuncIO_send(Obj self,Obj fd,Obj st,Obj offset,Obj count,Obj flags) { Int bytes; if (!IS_INTOBJ(fd) || !IS_STRING(st) || !IS_STRING_REP(st) || !IS_INTOBJ(offset) || !IS_INTOBJ(count) || !IS_INTOBJ(flags)) { SyClearErrorNo(); return Fail; } if (GET_LEN_STRING(st) < INT_INTOBJ(offset)+INT_INTOBJ(count)) { SyClearErrorNo(); return Fail; } bytes = (Int) send(INT_INTOBJ(fd), (char *) CHARS_STRING(st)+INT_INTOBJ(offset), INT_INTOBJ(count),INT_INTOBJ(flags)); if (bytes < 0) { SySetErrorNo(); return Fail; } else return INTOBJ_INT(bytes); } #endif #ifdef HAVE_SENDTO Obj FuncIO_sendto(Obj self,Obj fd,Obj st,Obj offset,Obj count,Obj flags, Obj to) { Int bytes; socklen_t fromlen; if (!IS_INTOBJ(fd) || !IS_STRING(st) || !IS_STRING_REP(st) || !IS_INTOBJ(offset) || !IS_INTOBJ(count) || !IS_INTOBJ(flags) || !IS_STRING(to) || !IS_STRING_REP(to)) { SyClearErrorNo(); return Fail; } if (GET_LEN_STRING(st) < INT_INTOBJ(offset)+INT_INTOBJ(count)) { SyClearErrorNo(); return Fail; } fromlen = GET_LEN_STRING(to); bytes = (Int) sendto(INT_INTOBJ(fd), (char *) CHARS_STRING(st)+INT_INTOBJ(offset), INT_INTOBJ(count),INT_INTOBJ(flags), (struct sockaddr *)CHARS_STRING(to),fromlen); if (bytes < 0) { SySetErrorNo(); return Fail; } else return INTOBJ_INT(bytes); } #endif #ifdef HAVE_GETSOCKOPT Obj FuncIO_getsockopt(Obj self,Obj fd,Obj level,Obj optname, Obj optval,Obj optlen) { Int res; socklen_t olen; if (!IS_INTOBJ(fd) || !IS_INTOBJ(level) || !IS_INTOBJ(optname) || !IS_INTOBJ(optlen) || !IS_STRING(optval) || !IS_STRING_REP(optval)) { SyClearErrorNo(); return Fail; } olen = INT_INTOBJ(optlen); if (olen > GET_LEN_STRING(optval)) GrowString(optval,olen); res = (Int) getsockopt(INT_INTOBJ(fd),INT_INTOBJ(level),INT_INTOBJ(optname), (char *) CHARS_STRING(optval),&olen); if (res < 0) { SySetErrorNo(); return Fail; } else { SET_LEN_STRING(optval,olen); return True; } } #endif #ifdef HAVE_SETSOCKOPT Obj FuncIO_setsockopt(Obj self,Obj fd,Obj level,Obj optname, Obj optval) { Int res; socklen_t olen; if (!IS_INTOBJ(fd) || !IS_INTOBJ(level) || !IS_INTOBJ(optname) || !IS_STRING(optval) || !IS_STRING_REP(optval)) { SyClearErrorNo(); return Fail; } olen = GET_LEN_STRING(optval); res = (Int) setsockopt(INT_INTOBJ(fd),INT_INTOBJ(level),INT_INTOBJ(optname), (char *) CHARS_STRING(optval),olen); if (res < 0) { SySetErrorNo(); return Fail; } else return True; } #endif #ifdef HAVE_SELECT Obj FuncIO_select(Obj self, Obj inlist, Obj outlist, Obj exclist, Obj timeoutsec, Obj timeoutusec) { fd_set infds,outfds,excfds; struct timeval tv; int n,maxfd; Int i,j; Obj o; time_t t1,t2; while (inlist == (Obj) 0 || !(IS_PLIST(inlist))) inlist = ErrorReturnObj( " must be a list of small integers (not a %s)", (Int)TNAM_OBJ(inlist),0L, "you can replace via 'return ;'" ); while (outlist == (Obj) 0 || !(IS_PLIST(outlist))) outlist = ErrorReturnObj( " must be a list of small integers (not a %s)", (Int)TNAM_OBJ(outlist),0L, "you can replace via 'return ;'" ); while (exclist == (Obj) 0 || !(IS_PLIST(exclist))) exclist = ErrorReturnObj( " must be a list of small integers (not a %s)", (Int)TNAM_OBJ(exclist),0L, "you can replace via 'return ;'" ); FD_ZERO(&infds); FD_ZERO(&outfds); FD_ZERO(&excfds); maxfd = 0; /* Handle input file descriptors: */ for (i = 1;i <= LEN_PLIST(inlist);i++) { o = ELM_PLIST(inlist,i); if (o != (Obj) 0 && IS_INTOBJ(o)) { j = INT_INTOBJ(o); /* a UNIX file descriptor */ FD_SET(j,&infds); if (j > maxfd) maxfd = j; } } /* Handle output file descriptors: */ for (i = 1;i <= LEN_PLIST(outlist);i++) { o = ELM_PLIST(outlist,i); if (o != (Obj) 0 && IS_INTOBJ(o)) { j = INT_INTOBJ(o); /* a UNIX file descriptor */ FD_SET(j,&outfds); if (j > maxfd) maxfd = j; } } /* Handle exception file descriptors: */ for (i = 1;i <= LEN_PLIST(exclist);i++) { o = ELM_PLIST(exclist,i); if (o != (Obj) 0 && IS_INTOBJ(o)) { j = INT_INTOBJ(o); /* a UNIX file descriptor */ FD_SET(j,&excfds); if (j > maxfd) maxfd = j; } } /* Handle the timeout: */ if (timeoutsec != (Obj) 0 && IS_INTOBJ(timeoutsec) && timeoutusec != (Obj) 0 && IS_INTOBJ(timeoutusec)) { tv.tv_sec = INT_INTOBJ(timeoutsec); tv.tv_usec = INT_INTOBJ(timeoutusec); while (1) { t1 = time(NULL); n = select(maxfd+1,&infds,&outfds,&excfds,&tv); if (n != -1 || errno != EINTR) break; t2 = time(NULL); tv.tv_sec -= (t2-t1); if (tv.tv_sec < 0) { tv.tv_sec = 0; tv.tv_usec = 0; } } } else { do { n = select(maxfd+1,&infds,&outfds,&excfds,NULL); } while (n == -1 && errno == EINTR); } if (n >= 0) { /* Now run through the lists and call functions if ready: */ for (i = 1;i <= LEN_PLIST(inlist);i++) { o = ELM_PLIST(inlist,i); if (o != (Obj) 0 && IS_INTOBJ(o)) { j = INT_INTOBJ(o); /* a UNIX file descriptor */ if (!(FD_ISSET(j,&infds))) { SET_ELM_PLIST(inlist,i,Fail); CHANGED_BAG(inlist); } } } /* Handle output file descriptors: */ for (i = 1;i <= LEN_PLIST(outlist);i++) { o = ELM_PLIST(outlist,i); if (o != (Obj) 0 && IS_INTOBJ(o)) { j = INT_INTOBJ(o); /* a UNIX file descriptor */ if (!(FD_ISSET(j,&outfds))) { SET_ELM_PLIST(outlist,i,Fail); CHANGED_BAG(outlist); } } } /* Handle exception file descriptors: */ for (i = 1;i <= LEN_PLIST(exclist);i++) { o = ELM_PLIST(exclist,i); if (o != (Obj) 0 && IS_INTOBJ(o)) { j = INT_INTOBJ(o); /* a UNIX file descriptor */ if (!(FD_ISSET(j,&excfds))) { SET_ELM_PLIST(exclist,i,Fail); CHANGED_BAG(exclist); } } } return INTOBJ_INT(n); } else { SySetErrorNo(); return Fail; } } #endif #ifdef HAVE_FORK Obj FuncIO_fork(Obj self) { int res; res = fork(); if (res == -1) { SySetErrorNo(); return Fail; } if (res != 0) { /* we are the parent */ return INTOBJ_INT(res); } else { /* we are the child */ return INTOBJ_INT(0); } } #endif // FIXME: globals static char *argv[1024]; /* Up to 1024 arguments */ static char *envp[1024]; /* Up to 1024 environment entries */ Obj FuncIO_execv(Obj self,Obj path,Obj Argv) { int argc; int i; Obj tmp; if (!IS_STRING(path) || !IS_STRING_REP(path) || !IS_PLIST(Argv)) { SyClearErrorNo(); return Fail; } argv[0] = (char *) CHARS_STRING(path); argc = LEN_PLIST(Argv); if (argc > 1022) { Pr("#E Ignored arguments after the 1022th.\n",0,0); argc = 1022; } for (i = 1;i <= argc;i++) { tmp = ELM_PLIST(Argv,i); if (!IS_STRING(tmp) || !IS_STRING_REP(tmp)) { SyClearErrorNo(); return Fail; } argv[i] = (char *) CHARS_STRING(tmp); } argv[i] = 0; i = execv((char *) CHARS_STRING(path),argv); if (i == -1) { SySetErrorNo(); return INTOBJ_INT(i); } /* This will never happen: */ return Fail; } // FIXME: globals extern char **environ; Obj FuncIO_execvp(Obj self,Obj file,Obj Argv) { int argc; int i; Obj tmp; if (!IS_STRING(file) || !IS_STRING_REP(file) || !IS_PLIST(Argv)) { SyClearErrorNo(); return Fail; } argv[0] = (char *) CHARS_STRING(file); argc = LEN_PLIST(Argv); if (argc > 1022) { Pr("#E Ignored arguments after the 1022th.\n",0,0); argc = 1022; } for (i = 1;i <= argc;i++) { tmp = ELM_PLIST(Argv,i); if (!IS_STRING(tmp) || !IS_STRING_REP(tmp)) { SyClearErrorNo(); return Fail; } argv[i] = (char *) CHARS_STRING(tmp); } argv[i] = 0; i = execvp((char *) CHARS_STRING(file),argv); if (i == -1) { SySetErrorNo(); return Fail; } /* This will never happen: */ return Fail; } Obj FuncIO_execve(Obj self,Obj path,Obj Argv,Obj Envp) { int argc; int i; Obj tmp; if (!IS_STRING(path) || !IS_STRING_REP(path) || !IS_PLIST(Argv) || !IS_PLIST(Envp) ) { SyClearErrorNo(); return Fail; } argv[0] = (char *) CHARS_STRING(path); argc = LEN_PLIST(Argv); if (argc > 1022) { Pr("#E Ignored arguments after the 1022th.\n",0,0); argc = 1022; } for (i = 1;i <= argc;i++) { tmp = ELM_PLIST(Argv,i); if (!IS_STRING(tmp) || !IS_STRING_REP(tmp)) { SyClearErrorNo(); return Fail; } argv[i] = (char *) CHARS_STRING(tmp); } argv[i] = 0; argc = LEN_PLIST(Envp); if (argc > 1022) { Pr("#E Ignored environment strings after the 1022th.\n",0,0); argc = 1022; } for (i = 1;i <= argc;i++) { tmp = ELM_PLIST(Envp,i); if (!IS_STRING(tmp) || !IS_STRING_REP(tmp)) { SyClearErrorNo(); return Fail; } envp[i-1] = (char *) CHARS_STRING(tmp); } envp[i-1] = 0; i = execve((char *) CHARS_STRING(path),argv,envp); if (i == -1) { SySetErrorNo(); return Fail; } /* This will never happen: */ return Fail; } Obj FuncIO_environ(Obj self) { Int i,len; char **p; Obj tmp,tmp2; /* First count the entries: */ for (len = 0,p = environ;*p;p++,len++) ; /* Now make a list: */ tmp = NEW_PLIST(T_PLIST_DENSE,len); tmp2 = tmp; /* Just to please the compiler */ SET_LEN_PLIST(tmp2,len); for (i = 1, p = environ;i <= len;i++,p++) { C_NEW_STRING(tmp2,strlen(*p),*p); SET_ELM_PLIST(tmp,i,tmp2); CHANGED_BAG(tmp); } return tmp; } Obj FuncIO_pipe(Obj self) { Obj tmp; int fds[2]; int res; res = pipe(fds); if (res == -1) { SySetErrorNo(); return Fail; } tmp = NEW_PREC(0); AssPRec(tmp,RNamName("toread"),INTOBJ_INT(fds[0])); AssPRec(tmp,RNamName("towrite"),INTOBJ_INT(fds[1])); return tmp; } Obj FuncIO_exit(Obj self,Obj status) { if (!IS_INTOBJ(status)) { SyClearErrorNo(); return Fail; } exit(INT_INTOBJ(status)); /* This never happens: */ return True; } Obj FuncIO_MasterPointerNumber(Obj self, Obj o) { if ((void **) o >= (void **) MptrBags && (void **) o < (void **) OldBags) { return INTOBJ_INT( ((void **) o - (void **) MptrBags) + 1 ); } else { return INTOBJ_INT( 0 ); } } #ifdef HAVE_FCNTL_H Obj FuncIO_fcntl(Obj self, Obj fd, Obj cmd, Obj arg) { Int ret; if (!IS_INTOBJ(fd) || !IS_INTOBJ(cmd) || !IS_INTOBJ(arg)) { SyClearErrorNo(); return Fail; } ret = fcntl(INT_INTOBJ(fd),INT_INTOBJ(cmd),INT_INTOBJ(arg)); if (ret == -1) { SySetErrorNo(); return Fail; } else return INTOBJ_INT(ret); } #endif #ifdef HAVE_GETPID Obj FuncIO_getpid(Obj self) { return INTOBJ_INT(getpid()); } #endif #ifdef HAVE_GETPPID Obj FuncIO_getppid(Obj self) { return INTOBJ_INT(getppid()); } #endif #ifdef HAVE_KILL Obj FuncIO_kill(Obj self, Obj pid, Obj sig) { Int ret; if (!IS_INTOBJ(pid) || !IS_INTOBJ(sig)) { SyClearErrorNo(); return Fail; } ret = kill((pid_t) INT_INTOBJ(pid),(int) INT_INTOBJ(sig)); if (ret == -1) { SySetErrorNo(); return Fail; } else return True; } #endif #ifdef HAVE_GETTIMEOFDAY Obj FuncIO_gettimeofday( Obj self ) { Obj tmp; struct timeval tv; gettimeofday(&tv, NULL); tmp = NEW_PREC(0); AssPRec(tmp, RNamName("tv_sec"), MyObjInt_Int( tv.tv_sec )); AssPRec(tmp, RNamName("tv_usec"), MyObjInt_Int( tv.tv_usec )); return tmp; } #endif #ifdef HAVE_GMTIME Obj FuncIO_gmtime( Obj self, Obj time ) { Obj tmp; time_t t; struct tm *s; if (!IS_INTOBJ(time)) { tmp = QuoInt(time,INTOBJ_INT(256)); if (!IS_INTOBJ(tmp)) return Fail; t = INT_INTOBJ(tmp)*256 + INT_INTOBJ(ModInt(time,INTOBJ_INT(256))); } else t = INT_INTOBJ(time); s = gmtime(&t); if (s == NULL) return Fail; tmp = NEW_PREC(0); AssPRec(tmp, RNamName("tm_sec"), INTOBJ_INT(s->tm_sec)); AssPRec(tmp, RNamName("tm_min"), INTOBJ_INT(s->tm_min)); AssPRec(tmp, RNamName("tm_hour"), INTOBJ_INT(s->tm_hour)); AssPRec(tmp, RNamName("tm_mday"), INTOBJ_INT(s->tm_mday)); AssPRec(tmp, RNamName("tm_mon"), INTOBJ_INT(s->tm_mon)); AssPRec(tmp, RNamName("tm_year"), INTOBJ_INT(s->tm_year)); AssPRec(tmp, RNamName("tm_wday"), INTOBJ_INT(s->tm_wday)); AssPRec(tmp, RNamName("tm_yday"), INTOBJ_INT(s->tm_yday)); AssPRec(tmp, RNamName("tm_isdst"), INTOBJ_INT(s->tm_isdst)); return tmp; } #endif #ifdef HAVE_LOCALTIME Obj FuncIO_localtime( Obj self, Obj time ) { Obj tmp; time_t t; struct tm *s; if (!IS_INTOBJ(time)) { tmp = QuoInt(time,INTOBJ_INT(256)); if (!IS_INTOBJ(tmp)) return Fail; t = INT_INTOBJ(tmp)*256 + INT_INTOBJ(ModInt(time,INTOBJ_INT(256))); } else t = INT_INTOBJ(time); s = localtime(&t); if (s == NULL) return Fail; tmp = NEW_PREC(0); AssPRec(tmp, RNamName("tm_sec"), INTOBJ_INT(s->tm_sec)); AssPRec(tmp, RNamName("tm_min"), INTOBJ_INT(s->tm_min)); AssPRec(tmp, RNamName("tm_hour"), INTOBJ_INT(s->tm_hour)); AssPRec(tmp, RNamName("tm_mday"), INTOBJ_INT(s->tm_mday)); AssPRec(tmp, RNamName("tm_mon"), INTOBJ_INT(s->tm_mon)); AssPRec(tmp, RNamName("tm_year"), INTOBJ_INT(s->tm_year)); AssPRec(tmp, RNamName("tm_wday"), INTOBJ_INT(s->tm_wday)); AssPRec(tmp, RNamName("tm_yday"), INTOBJ_INT(s->tm_yday)); AssPRec(tmp, RNamName("tm_isdst"), INTOBJ_INT(s->tm_isdst)); return tmp; } #endif #ifdef HAVE_GETSOCKNAME Obj FuncIO_getsockname(Obj self, Obj fd) { struct sockaddr_in sa; socklen_t sa_len; Obj res; if (!IS_INTOBJ(fd)) { SyClearErrorNo(); return Fail; } else { sa_len = sizeof sa; getsockname (INT_INTOBJ(fd), (struct sockaddr *) (&sa), &sa_len); res = NEW_STRING(sa_len); memcpy(CHARS_STRING(res),&sa,sa_len); return res; } } #endif #ifdef HAVE_GETHOSTNAME Obj FuncIO_gethostname(Obj self) { char name[256]; Obj res; int i,r; r = gethostname(name, 256); if (r < 0) { return Fail; } i = strlen(name); res = NEW_STRING(i); memcpy(CHARS_STRING(res),name,i); return res; } #endif /*F * * * * * * * * * * * * * initialize package * * * * * * * * * * * * * * */ /****************************************************************************** *V GVarFuncs . . . . . . . . . . . . . . . . . . list of functions to export */ static StructGVarFunc GVarFuncs [] = { { "IO_open", 3, "pathname, flags, mode", FuncIO_open, "io.c:IO_open" }, { "IO_creat", 2, "pathname, mode", FuncIO_creat, "io.c:IO_creat" }, { "IO_read", 4, "fd, st, offset, count", FuncIO_read, "io.c:IO_read" }, { "IO_write", 4, "fd, st, offset, count", FuncIO_write, "io.c:IO_write" }, { "IO_close", 1, "fd", FuncIO_close, "io.c:IO_close" }, { "IO_lseek", 3, "fd, offset, whence", FuncIO_lseek, "io.c:IO_lseek" }, #ifdef HAVE_DIRENT_H #ifdef HAVE_OPENDIR { "IO_opendir", 1, "name", FuncIO_opendir, "io.c:IO_opendir" }, #endif #ifdef HAVE_READDIR { "IO_readdir", 0, "", FuncIO_readdir, "io.c:IO_readdir" }, #endif #ifdef HAVE_REWINDDIR { "IO_rewinddir", 0, "", FuncIO_rewinddir, "io.c:IO_rewinddir" }, #endif #ifdef HAVE_CLOSEDIR { "IO_closedir", 0, "", FuncIO_closedir, "io.c:IO_closedir" }, #endif #ifdef HAVE_TELLDIR { "IO_telldir", 0, "", FuncIO_telldir, "io.c:IO_telldir" }, #endif #ifdef HAVE_SEEKDIR { "IO_seekdir", 1, "offset", FuncIO_seekdir, "io.c:IO_seekdir" }, #endif #endif /* HAVE_DIRENT_H */ #ifdef HAVE_UNLINK { "IO_unlink", 1, "pathname", FuncIO_unlink, "io.c:IO_unlink" }, #endif #ifdef HAVE_LINK { "IO_link", 2, "oldpath, newpath", FuncIO_link, "io.c:IO_link" }, #endif #ifdef HAVE_RENAME { "IO_rename", 2, "oldpath, newpath", FuncIO_rename, "io.c:IO_rename" }, #endif #ifdef HAVE_SYMLINK { "IO_symlink", 2, "oldpath, newpath", FuncIO_symlink, "io.c:IO_symlink" }, #endif #ifdef HAVE_READLINK { "IO_readlink", 3, "path, buf, bufsize", FuncIO_readlink, "io.c:IO_readlink" }, #endif #ifdef HAVE_MKDIR { "IO_mkdir", 2, "pathname, mode", FuncIO_mkdir, "io.c:IO_mkdir" }, #endif { "IO_chdir", 1, "path", FuncIO_chdir, "io.c:IO_chdir" }, #ifdef HAVE_RMDIR { "IO_rmdir", 1, "pathname", FuncIO_rmdir, "io.c:IO_rmdir" }, #endif #ifdef HAVE_STAT { "IO_stat", 1, "pathname", FuncIO_stat, "io.c:IO_stat" }, #endif #ifdef HAVE_FSTAT { "IO_fstat", 1, "fd", FuncIO_fstat, "io.c:IO_fstat" }, #endif #ifdef HAVE_LSTAT { "IO_lstat", 1, "pathname", FuncIO_lstat, "io.c:IO_lstat" }, #endif #ifdef HAVE_CHMOD { "IO_chmod", 2, "path, mode", FuncIO_chmod, "io.c:IO_chmod" }, #endif #ifdef HAVE_FCHMOD { "IO_fchmod", 2, "fd, mode", FuncIO_fchmod, "io.c:IO_fchmod" }, #endif #ifdef HAVE_CHOWN { "IO_chown", 3, "path, owner, group", FuncIO_chown, "io.c:IO_chown" }, #endif #ifdef HAVE_FCHOWN { "IO_fchown", 3, "fd, owner, group", FuncIO_fchown, "io.c:IO_fchown" }, #endif #ifdef HAVE_LCHOWN { "IO_lchown", 3, "path, owner, group", FuncIO_lchown, "io.c:IO_lchown" }, #endif #ifdef HAVE_MKNOD { "IO_mknod", 3, "path, mode, dev", FuncIO_mknod, "io.c:IO_mknod" }, #endif #ifdef HAVE_MKFIFO { "IO_mkfifo", 2, "path, mode", FuncIO_mkfifo, "io.c:IO_mkfifo" }, #endif #ifdef HAVE_DUP { "IO_dup", 1, "oldfd", FuncIO_dup, "io.c:IO_dup" }, #endif #ifdef HAVE_DUP2 { "IO_dup2", 2, "oldfd, newfd", FuncIO_dup2, "io.c:IO_dup2" }, #endif #ifdef HAVE_SOCKET { "IO_socket", 3, "domain, type, protocol", FuncIO_socket, "io.c:IO_socket" }, #endif #ifdef HAVE_BIND { "IO_bind", 2, "fd, my_addr", FuncIO_bind, "io.c:IO_bind" }, #endif #ifdef HAVE_CONNECT { "IO_connect", 2, "fd, serv_addr", FuncIO_connect, "io.c:IO_connect" }, #endif #ifdef HAVE_SOCKET { "IO_make_sockaddr_in", 2, "ip, port", FuncIO_make_sockaddr_in, "io.c:IO_make_sockaddr_in" }, #endif #ifdef HAVE_GETHOSTBYNAME { "IO_gethostbyname", 1, "name", FuncIO_gethostbyname, "io.c:IO_gethostbyname" }, #endif #ifdef HAVE_LISTEN { "IO_listen", 2, "s, backlog", FuncIO_listen, "io.c:IO_listen" }, #endif #ifdef HAVE_ACCEPT { "IO_accept", 2, "fd, addr", FuncIO_accept, "io.c:IO_accept" }, #endif #ifdef HAVE_RECV { "IO_recv", 5, "fd, st, offset, len, flags", FuncIO_recv, "io.c:IO_recv" }, #endif #ifdef HAVE_RECVFROM { "IO_recvfrom", 6, "fd, st, offset, len, flags, from", FuncIO_recvfrom, "io.c:IO_recvfrom" }, #endif #ifdef HAVE_SEND { "IO_send", 5, "fd, st, offset, len, flags", FuncIO_send, "io.c:IO_send" }, #endif #ifdef HAVE_SENDTO { "IO_sendto", 6, "fd, st, offset, len, flags, to", FuncIO_sendto, "io.c:IO_sendto" }, #endif #ifdef HAVE_GETSOCKOPT { "IO_getsockopt", 5, "fd, level, optname, optval, optlen", FuncIO_getsockopt, "io.c:IO_getsockopt" }, #endif #ifdef HAVE_SETSOCKOPT { "IO_setsockopt", 4, "fd, level, optname, optval", FuncIO_setsockopt, "io.c:IO_setsockopt" }, #endif #ifdef HAVE_SELECT { "IO_select", 5, "inlist, outlist, exclist, timeoutsec, timeoutusec", FuncIO_select, "io.c:IO_select" }, #endif #if defined(HAVE_SIGACTION) || defined(HAVE_SIGNAL) { "IO_WaitPid", 2, "pid, wait", FuncIO_WaitPid, "io.c:IO_WaitPid" }, #endif #ifdef HAVE_FORK { "IO_fork", 0, "", FuncIO_fork, "io.c:IO_fork" }, #endif { "IO_execv", 2, "path, argv", FuncIO_execv, "io.c:IO_execv" }, { "IO_execvp", 2, "path, argv", FuncIO_execvp, "io.c:IO_execvp" }, { "IO_execve", 3, "path, argv, envp", FuncIO_execve, "io.c:IO_execve" }, { "IO_environ", 0, "", FuncIO_environ, "io.c:IO_environ" }, #ifdef HAVE_SIGNAL { "IO_InstallSIGCHLDHandler", 0, "", FuncIO_InstallSIGCHLDHandler, "io.c:IO_InstallSIGCHLDHandler" }, { "IO_RestoreSIGCHLDHandler", 0, "", FuncIO_RestoreSIGCHLDHandler, "io.c:IO_RestoreSIGCHLDHandler" }, #endif { "IO_pipe", 0, "", FuncIO_pipe, "io.c:IO_pipe" }, { "IO_exit", 1, "status", FuncIO_exit, "io.c:IO_exit" }, { "IO_MasterPointerNumber", 1, "obj", FuncIO_MasterPointerNumber, "io.c:IO_MasterPointerNumber" }, #ifdef HAVE_FCNTL_H { "IO_fcntl", 3, "fd, cmd, arg", FuncIO_fcntl, "io.c:IO_fcntl" }, #endif #ifdef HAVE_GETPID { "IO_getpid", 0, "", FuncIO_getpid, "io.c:IO_getpid" }, #endif #ifdef HAVE_GETPPID { "IO_getppid", 0, "", FuncIO_getppid, "io.c:IO_getppid" }, #endif #ifdef HAVE_KILL { "IO_kill", 2, "pid, sig", FuncIO_kill, "io.c:IO_kill" }, #endif #ifdef HAVE_GETTIMEOFDAY { "IO_gettimeofday", 0, "", FuncIO_gettimeofday, "io.c:IO_gettimeofday" }, #endif #ifdef HAVE_GMTIME { "IO_gmtime", 1, "seconds", FuncIO_gmtime, "io.c:IO_gmtime" }, #endif #ifdef HAVE_LOCALTIME { "IO_localtime", 1, "seconds", FuncIO_localtime, "io.c:IO_localtime" }, #endif #ifdef HAVE_GETSOCKNAME { "IO_getsockname", 1, "fd", FuncIO_getsockname, "io.c:IO_getsockname" }, #endif #ifdef HAVE_GETHOSTNAME { "IO_gethostname", 0, "", FuncIO_gethostname, "io.c:IO_gethostname" }, #endif { 0 } }; /****************************************************************************** *F InitKernel( ) . . . . . . . . initialise kernel data structures */ static Int InitKernel ( StructInitInfo *module ) { /* init filters and functions */ InitHdlrFuncsFromTable( GVarFuncs ); /* return success */ return 0; } /****************************************************************************** *F InitLibrary( ) . . . . . . . initialise library data structures */ static Int InitLibrary ( StructInitInfo *module ) { Int gvar; Obj tmp; /* init filters and functions we assign the functions to components of a record "IO" */ InitGVarFuncsFromTable(GVarFuncs); tmp = NEW_PREC(0); /* Constants for the flags: */ AssPRec(tmp, RNamName("O_RDONLY"), INTOBJ_INT((Int) O_RDONLY)); AssPRec(tmp, RNamName("O_WRONLY"), INTOBJ_INT((Int) O_WRONLY)); AssPRec(tmp, RNamName("O_RDWR"), INTOBJ_INT((Int) O_RDWR)); #ifdef O_CREAT AssPRec(tmp, RNamName("O_CREAT"), INTOBJ_INT((Int) O_CREAT)); #endif #ifdef O_APPEND AssPRec(tmp, RNamName("O_APPEND"), INTOBJ_INT((Int) O_APPEND)); #endif #ifdef O_ASYNC AssPRec(tmp, RNamName("O_ASYNC"), INTOBJ_INT((Int) O_ASYNC)); #endif #ifdef O_DIRECT AssPRec(tmp, RNamName("O_DIRECT"), INTOBJ_INT((Int) O_DIRECT)); #endif #ifdef O_DIRECTORY AssPRec(tmp, RNamName("O_DIRECTORY"), INTOBJ_INT((Int) O_DIRECTORY)); #endif #ifdef O_EXCL AssPRec(tmp, RNamName("O_EXCL"), INTOBJ_INT((Int) O_EXCL)); #endif #ifdef O_LARGEFILE AssPRec(tmp, RNamName("O_LARGEFILE"), INTOBJ_INT((Int) O_LARGEFILE)); #endif #ifdef O_NOATIME AssPRec(tmp, RNamName("O_NOATIME"), INTOBJ_INT((Int) O_NOATIME)); #endif #ifdef O_NOCTTY AssPRec(tmp, RNamName("O_NOCTTY"), INTOBJ_INT((Int) O_NOCTTY)); #endif #ifdef O_NOFOLLOW AssPRec(tmp, RNamName("O_NOFOLLOW"), INTOBJ_INT((Int) O_NOFOLLOW)); #endif #ifdef O_NONBLOCK AssPRec(tmp, RNamName("O_NONBLOCK"), INTOBJ_INT((Int) O_NONBLOCK)); #endif #ifdef O_NDELAY AssPRec(tmp, RNamName("O_NDELAY"), INTOBJ_INT((Int) O_NDELAY)); #endif #ifdef O_SYNC AssPRec(tmp, RNamName("O_SYNC"), INTOBJ_INT((Int) O_SYNC)); #endif #ifdef O_TRUNC AssPRec(tmp, RNamName("O_TRUNC"), INTOBJ_INT((Int) O_TRUNC)); #endif #ifdef SEEK_SET AssPRec(tmp, RNamName("SEEK_SET"), INTOBJ_INT((Int) SEEK_SET)); #endif #ifdef SEEK_CUR AssPRec(tmp, RNamName("SEEK_CUR"), INTOBJ_INT((Int) SEEK_CUR)); #endif #ifdef SEEK_END AssPRec(tmp, RNamName("SEEK_END"), INTOBJ_INT((Int) SEEK_END)); #endif /* Constants for the mode: */ #ifdef S_IRWXU AssPRec(tmp, RNamName("S_IRWXU"), INTOBJ_INT((Int) S_IRWXU)); #endif #ifdef S_IRUSR AssPRec(tmp, RNamName("S_IRUSR"), INTOBJ_INT((Int) S_IRUSR)); #endif #ifdef S_IWUSR AssPRec(tmp, RNamName("S_IWUSR"), INTOBJ_INT((Int) S_IWUSR)); #endif #ifdef S_IXUSR AssPRec(tmp, RNamName("S_IXUSR"), INTOBJ_INT((Int) S_IXUSR)); #endif #ifdef S_IRWXG AssPRec(tmp, RNamName("S_IRWXG"), INTOBJ_INT((Int) S_IRWXG)); #endif #ifdef S_IRGRP AssPRec(tmp, RNamName("S_IRGRP"), INTOBJ_INT((Int) S_IRGRP)); #endif #ifdef S_IWGRP AssPRec(tmp, RNamName("S_IWGRP"), INTOBJ_INT((Int) S_IWGRP)); #endif #ifdef S_IXGRP AssPRec(tmp, RNamName("S_IXGRP"), INTOBJ_INT((Int) S_IXGRP)); #endif #ifdef S_IRWXO AssPRec(tmp, RNamName("S_IRWXO"), INTOBJ_INT((Int) S_IRWXO)); #endif #ifdef S_IROTH AssPRec(tmp, RNamName("S_IROTH"), INTOBJ_INT((Int) S_IROTH)); #endif #ifdef S_IWOTH AssPRec(tmp, RNamName("S_IWOTH"), INTOBJ_INT((Int) S_IWOTH)); #endif #ifdef S_IXOTH AssPRec(tmp, RNamName("S_IXOTH"), INTOBJ_INT((Int) S_IXOTH)); #endif #ifdef S_IFMT AssPRec(tmp, RNamName("S_IFMT"), INTOBJ_INT((Int) S_IFMT)); #endif #ifdef S_IFSOCK AssPRec(tmp, RNamName("S_IFSOCK"), INTOBJ_INT((Int) S_IFSOCK)); #endif #ifdef S_IFLNK AssPRec(tmp, RNamName("S_IFLNK"), INTOBJ_INT((Int) S_IFLNK)); #endif #ifdef S_IFREG AssPRec(tmp, RNamName("S_IFREG"), INTOBJ_INT((Int) S_IFREG)); #endif #ifdef S_IFBLK AssPRec(tmp, RNamName("S_IFBLK"), INTOBJ_INT((Int) S_IFBLK)); #endif #ifdef S_IFDIR AssPRec(tmp, RNamName("S_IFDIR"), INTOBJ_INT((Int) S_IFDIR)); #endif #ifdef S_IFCHR AssPRec(tmp, RNamName("S_IFCHR"), INTOBJ_INT((Int) S_IFCHR)); #endif #ifdef S_IFIFO AssPRec(tmp, RNamName("S_IFIFO"), INTOBJ_INT((Int) S_IFIFO)); #endif #ifdef S_ISUID AssPRec(tmp, RNamName("S_ISUID"), INTOBJ_INT((Int) S_ISUID)); #endif #ifdef S_ISGID AssPRec(tmp, RNamName("S_ISGID"), INTOBJ_INT((Int) S_ISGID)); #endif #ifdef S_ISVTX AssPRec(tmp, RNamName("S_ISVTX"), INTOBJ_INT((Int) S_ISVTX)); #endif /* Constants for the errors: */ #ifdef EACCES AssPRec(tmp, RNamName("EACCES"), INTOBJ_INT((Int) EACCES)); #endif #ifdef EEXIST AssPRec(tmp, RNamName("EEXIST"), INTOBJ_INT((Int) EEXIST)); #endif #ifdef EFAULT AssPRec(tmp, RNamName("EFAULT"), INTOBJ_INT((Int) EFAULT)); #endif #ifdef EISDIR AssPRec(tmp, RNamName("EISDIR"), INTOBJ_INT((Int) EISDIR)); #endif #ifdef ELOOP AssPRec(tmp, RNamName("ELOOP"), INTOBJ_INT((Int) ELOOP)); #endif #ifdef EMFILE AssPRec(tmp, RNamName("EMFILE"), INTOBJ_INT((Int) EMFILE)); #endif #ifdef ENAMETOOLONG AssPRec(tmp, RNamName("ENAMETOOLONG"), INTOBJ_INT((Int) ENAMETOOLONG)); #endif #ifdef ENFILE AssPRec(tmp, RNamName("ENFILE"), INTOBJ_INT((Int) ENFILE)); #endif #ifdef ENODEV AssPRec(tmp, RNamName("ENODEV"), INTOBJ_INT((Int) ENODEV)); #endif #ifdef ENOENT AssPRec(tmp, RNamName("ENOENT"), INTOBJ_INT((Int) ENOENT)); #endif #ifdef ENOMEM AssPRec(tmp, RNamName("ENOMEM"), INTOBJ_INT((Int) ENOMEM)); #endif #ifdef ENOSPC AssPRec(tmp, RNamName("ENOSPC"), INTOBJ_INT((Int) ENOSPC)); #endif #ifdef ENOTDIR AssPRec(tmp, RNamName("ENOTDIR"), INTOBJ_INT((Int) ENOTDIR)); #endif #ifdef ENXIO AssPRec(tmp, RNamName("ENXIO"), INTOBJ_INT((Int) ENXIO)); #endif #ifdef EOVERFLOW AssPRec(tmp, RNamName("EOVERFLOW"), INTOBJ_INT((Int) EOVERFLOW)); #endif #ifdef EPERM AssPRec(tmp, RNamName("EPERM"), INTOBJ_INT((Int) EPERM)); #endif #ifdef EROFS AssPRec(tmp, RNamName("EROFS"), INTOBJ_INT((Int) EROFS)); #endif #ifdef ETXTBSY AssPRec(tmp, RNamName("ETXTBSY"), INTOBJ_INT((Int) ETXTBSY)); #endif #ifdef EAGAIN AssPRec(tmp, RNamName("EAGAIN"), INTOBJ_INT((Int) EAGAIN)); #endif #ifdef EBADF AssPRec(tmp, RNamName("EBADF"), INTOBJ_INT((Int) EBADF)); #endif #ifdef EINTR AssPRec(tmp, RNamName("EINTR"), INTOBJ_INT((Int) EINTR)); #endif #ifdef EINVAL AssPRec(tmp, RNamName("EINVAL"), INTOBJ_INT((Int) EINVAL)); #endif #ifdef EIO AssPRec(tmp, RNamName("EIO"), INTOBJ_INT((Int) EIO)); #endif #ifdef EFBIG AssPRec(tmp, RNamName("EFBIG"), INTOBJ_INT((Int) EFBIG)); #endif #ifdef ENOSPC AssPRec(tmp, RNamName("ENOSPC"), INTOBJ_INT((Int) ENOSPC)); #endif #ifdef EPIPE AssPRec(tmp, RNamName("EPIPE"), INTOBJ_INT((Int) EPIPE)); #endif #ifdef EBUSY AssPRec(tmp, RNamName("EBUSY"), INTOBJ_INT((Int) EBUSY)); #endif #ifdef ESPIPE AssPRec(tmp, RNamName("ESPIPE"), INTOBJ_INT((Int) ESPIPE)); #endif #ifdef EMLINK AssPRec(tmp, RNamName("EMLINK"), INTOBJ_INT((Int) EMLINK)); #endif #ifdef EXDEV AssPRec(tmp, RNamName("EXDEV"), INTOBJ_INT((Int) EXDEV)); #endif #ifdef ENOTEMPTY AssPRec(tmp, RNamName("ENOTEMPTY"), INTOBJ_INT((Int) ENOTEMPTY)); #endif #ifdef EAFNOSUPPORT AssPRec(tmp, RNamName("EAFNOSUPPORT"), INTOBJ_INT((Int) EAFNOSUPPORT)); #endif #ifdef ENOBUGS AssPRec(tmp, RNamName("ENOBUGS"), INTOBJ_INT((Int) ENOBUGS)); #endif #ifdef EPROTONOSUPPORT AssPRec(tmp, RNamName("EPROTONOSUPPORT"),INTOBJ_INT((Int) EPROTONOSUPPORT)); #endif #ifdef ENOTSOCK AssPRec(tmp, RNamName("ENOTSOCK"),INTOBJ_INT((Int) ENOTSOCK)); #endif #ifdef EADDRINUSE AssPRec(tmp, RNamName("EADDRINUSE"), INTOBJ_INT((Int) EADDRINUSE)); #endif #ifdef EALREADY AssPRec(tmp, RNamName("EALREADY"), INTOBJ_INT((Int) EALREADY)); #endif #ifdef ECONNREFUSED AssPRec(tmp, RNamName("ECONNREFUSED"), INTOBJ_INT((Int) ECONNREFUSED)); #endif #ifdef EINPROGRESS AssPRec(tmp, RNamName("EINPROGRESS"), INTOBJ_INT((Int) EINPROGRESS)); #endif #ifdef EISCONN AssPRec(tmp, RNamName("EISCONN"), INTOBJ_INT((Int) EISCONN)); #endif #ifdef ETIMEDOUT AssPRec(tmp, RNamName("ETIMEDOUT"), INTOBJ_INT((Int) ETIMEDOUT)); #endif #ifdef EOPNOTSUPP AssPRec(tmp, RNamName("EOPNOTSUPP"), INTOBJ_INT((Int) EOPNOTSUPP)); #endif #ifdef EPROTO AssPRec(tmp, RNamName("EPROTO"), INTOBJ_INT((Int) EPROTO)); #endif #ifdef ECONNABORTED AssPRec(tmp, RNamName("ECONNABORTED"), INTOBJ_INT((Int) ECONNABORTED)); #endif #ifdef ECHILD AssPRec(tmp, RNamName("ECHILD"), INTOBJ_INT((Int) ECHILD)); #endif #ifdef EWOULDBLOCK AssPRec(tmp, RNamName("EWOULDBLOCK"), INTOBJ_INT((Int) EWOULDBLOCK)); #endif #ifdef HOST_NOT_FOUND AssPRec(tmp, RNamName("HOST_NOT_FOUND"), INTOBJ_INT((Int) HOST_NOT_FOUND)); #endif #ifdef NO_ADDRESS AssPRec(tmp, RNamName("NO_ADDRESS"), INTOBJ_INT((Int) NO_ADDRESS)); #endif #ifdef NO_DATA AssPRec(tmp, RNamName("NO_DATA"), INTOBJ_INT((Int) NO_DATA)); #endif #ifdef NO_RECOVERY AssPRec(tmp, RNamName("NO_RECOVERY"), INTOBJ_INT((Int) NO_RECOVERY)); #endif #ifdef TRY_AGAIN AssPRec(tmp, RNamName("TRY_AGAIN"), INTOBJ_INT((Int) TRY_AGAIN)); #endif /* Constants for networking: */ #ifdef AF_APPLETALK AssPRec(tmp, RNamName("AF_APPLETALK"), INTOBJ_INT((Int) AF_APPLETALK)); #endif #ifdef AF_ASH AssPRec(tmp, RNamName("AF_ASH"), INTOBJ_INT((Int) AF_ASH)); #endif #ifdef AF_ATMPVC AssPRec(tmp, RNamName("AF_ATMPVC"), INTOBJ_INT((Int) AF_ATMPVC)); #endif #ifdef AF_ATMSVC AssPRec(tmp, RNamName("AF_ATMSVC"), INTOBJ_INT((Int) AF_ATMSVC)); #endif #ifdef AF_AX25 AssPRec(tmp, RNamName("AF_AX25"), INTOBJ_INT((Int) AF_AX25)); #endif #ifdef AF_BLUETOOTH AssPRec(tmp, RNamName("AF_BLUETOOTH"), INTOBJ_INT((Int) AF_BLUETOOTH)); #endif #ifdef AF_BRIDGE AssPRec(tmp, RNamName("AF_BRIDGE"), INTOBJ_INT((Int) AF_BRIDGE)); #endif #ifdef AF_DECnet AssPRec(tmp, RNamName("AF_DECnet"), INTOBJ_INT((Int) AF_DECnet)); #endif #ifdef AF_ECONET AssPRec(tmp, RNamName("AF_ECONET"), INTOBJ_INT((Int) AF_ECONET)); #endif #ifdef AF_FILE AssPRec(tmp, RNamName("AF_FILE"), INTOBJ_INT((Int) AF_FILE)); #endif #ifdef AF_INET AssPRec(tmp, RNamName("AF_INET"), INTOBJ_INT((Int) AF_INET)); #endif #ifdef AF_INET6 AssPRec(tmp, RNamName("AF_INET6"), INTOBJ_INT((Int) AF_INET6)); #endif #ifdef AF_IPX AssPRec(tmp, RNamName("AF_IPX"), INTOBJ_INT((Int) AF_IPX)); #endif #ifdef AF_IRDA AssPRec(tmp, RNamName("AF_IRDA"), INTOBJ_INT((Int) AF_IRDA)); #endif #ifdef AF_KEY AssPRec(tmp, RNamName("AF_KEY"), INTOBJ_INT((Int) AF_KEY)); #endif #ifdef AF_LOCAL AssPRec(tmp, RNamName("AF_LOCAL"), INTOBJ_INT((Int) AF_LOCAL)); #endif #ifdef AF_MAX AssPRec(tmp, RNamName("AF_MAX"), INTOBJ_INT((Int) AF_MAX)); #endif #ifdef AF_NETBEUI AssPRec(tmp, RNamName("AF_NETBEUI"), INTOBJ_INT((Int) AF_NETBEUI)); #endif #ifdef AF_NETLINK AssPRec(tmp, RNamName("AF_NETLINK"), INTOBJ_INT((Int) AF_NETLINK)); #endif #ifdef AF_NETROM AssPRec(tmp, RNamName("AF_NETROM"), INTOBJ_INT((Int) AF_NETROM)); #endif #ifdef AF_PACKET AssPRec(tmp, RNamName("AF_PACKET"), INTOBJ_INT((Int) AF_PACKET)); #endif #ifdef AF_PPPOX AssPRec(tmp, RNamName("AF_PPPOX"), INTOBJ_INT((Int) AF_PPPOX)); #endif #ifdef AF_ROSE AssPRec(tmp, RNamName("AF_ROSE"), INTOBJ_INT((Int) AF_ROSE)); #endif #ifdef AF_ROUTE AssPRec(tmp, RNamName("AF_ROUTE"), INTOBJ_INT((Int) AF_ROUTE)); #endif #ifdef AF_SECURITY AssPRec(tmp, RNamName("AF_SECURITY"), INTOBJ_INT((Int) AF_SECURITY)); #endif #ifdef AF_SNA AssPRec(tmp, RNamName("AF_SNA"), INTOBJ_INT((Int) AF_SNA)); #endif #ifdef AF_UNIX AssPRec(tmp, RNamName("AF_UNIX"), INTOBJ_INT((Int) AF_UNIX)); #endif #ifdef AF_UNSPEC AssPRec(tmp, RNamName("AF_UNSPEC"), INTOBJ_INT((Int) AF_UNSPEC)); #endif #ifdef AF_WANPIPE AssPRec(tmp, RNamName("AF_WANPIPE"), INTOBJ_INT((Int) AF_WANPIPE)); #endif #ifdef AF_X25 AssPRec(tmp, RNamName("AF_X25"), INTOBJ_INT((Int) AF_X25)); #endif #ifdef PF_APPLETALK AssPRec(tmp, RNamName("PF_APPLETALK"), INTOBJ_INT((Int) PF_APPLETALK)); #endif #ifdef PF_ASH AssPRec(tmp, RNamName("PF_ASH"), INTOBJ_INT((Int) PF_ASH)); #endif #ifdef PF_ATMPVC AssPRec(tmp, RNamName("PF_ATMPVC"), INTOBJ_INT((Int) PF_ATMPVC)); #endif #ifdef PF_ATMSVC AssPRec(tmp, RNamName("PF_ATMSVC"), INTOBJ_INT((Int) PF_ATMSVC)); #endif #ifdef PF_AX25 AssPRec(tmp, RNamName("PF_AX25"), INTOBJ_INT((Int) PF_AX25)); #endif #ifdef PF_BLUETOOTH AssPRec(tmp, RNamName("PF_BLUETOOTH"), INTOBJ_INT((Int) PF_BLUETOOTH)); #endif #ifdef PF_BRIDGE AssPRec(tmp, RNamName("PF_BRIDGE"), INTOBJ_INT((Int) PF_BRIDGE)); #endif #ifdef PF_DECnet AssPRec(tmp, RNamName("PF_DECnet"), INTOBJ_INT((Int) PF_DECnet)); #endif #ifdef PF_ECONET AssPRec(tmp, RNamName("PF_ECONET"), INTOBJ_INT((Int) PF_ECONET)); #endif #ifdef PF_FILE AssPRec(tmp, RNamName("PF_FILE"), INTOBJ_INT((Int) PF_FILE)); #endif #ifdef PF_INET AssPRec(tmp, RNamName("PF_INET"), INTOBJ_INT((Int) PF_INET)); #endif #ifdef PF_INET6 AssPRec(tmp, RNamName("PF_INET6"), INTOBJ_INT((Int) PF_INET6)); #endif #ifdef PF_IPX AssPRec(tmp, RNamName("PF_IPX"), INTOBJ_INT((Int) PF_IPX)); #endif #ifdef PF_IRDA AssPRec(tmp, RNamName("PF_IRDA"), INTOBJ_INT((Int) PF_IRDA)); #endif #ifdef PF_KEY AssPRec(tmp, RNamName("PF_KEY"), INTOBJ_INT((Int) PF_KEY)); #endif #ifdef PF_LOCAL AssPRec(tmp, RNamName("PF_LOCAL"), INTOBJ_INT((Int) PF_LOCAL)); #endif #ifdef PF_MAX AssPRec(tmp, RNamName("PF_MAX"), INTOBJ_INT((Int) PF_MAX)); #endif #ifdef PF_NETBEUI AssPRec(tmp, RNamName("PF_NETBEUI"), INTOBJ_INT((Int) PF_NETBEUI)); #endif #ifdef PF_NETLINK AssPRec(tmp, RNamName("PF_NETLINK"), INTOBJ_INT((Int) PF_NETLINK)); #endif #ifdef PF_NETROM AssPRec(tmp, RNamName("PF_NETROM"), INTOBJ_INT((Int) PF_NETROM)); #endif #ifdef PF_PACKET AssPRec(tmp, RNamName("PF_PACKET"), INTOBJ_INT((Int) PF_PACKET)); #endif #ifdef PF_PPPOX AssPRec(tmp, RNamName("PF_PPPOX"), INTOBJ_INT((Int) PF_PPPOX)); #endif #ifdef PF_ROSE AssPRec(tmp, RNamName("PF_ROSE"), INTOBJ_INT((Int) PF_ROSE)); #endif #ifdef PF_ROUTE AssPRec(tmp, RNamName("PF_ROUTE"), INTOBJ_INT((Int) PF_ROUTE)); #endif #ifdef PF_SECURITY AssPRec(tmp, RNamName("PF_SECURITY"), INTOBJ_INT((Int) PF_SECURITY)); #endif #ifdef PF_SNA AssPRec(tmp, RNamName("PF_SNA"), INTOBJ_INT((Int) PF_SNA)); #endif #ifdef PF_UNIX AssPRec(tmp, RNamName("PF_UNIX"), INTOBJ_INT((Int) PF_UNIX)); #endif #ifdef PF_WANPIPE AssPRec(tmp, RNamName("PF_WANPIPE"), INTOBJ_INT((Int) PF_WANPIPE)); #endif #ifdef PF_X25 AssPRec(tmp, RNamName("PF_X25"), INTOBJ_INT((Int) PF_X25)); #endif #ifdef SOCK_DGRAM AssPRec(tmp, RNamName("SOCK_DGRAM"), INTOBJ_INT((Int) SOCK_DGRAM)); #endif #ifdef SOCK_PACKET AssPRec(tmp, RNamName("SOCK_PACKET"), INTOBJ_INT((Int) SOCK_PACKET)); #endif #ifdef SOCK_RAW AssPRec(tmp, RNamName("SOCK_RAW"), INTOBJ_INT((Int) SOCK_RAW)); #endif #ifdef SOCK_RDM AssPRec(tmp, RNamName("SOCK_RDM"), INTOBJ_INT((Int) SOCK_RDM)); #endif #ifdef SOCK_SEQPACKET AssPRec(tmp, RNamName("SOCK_SEQPACKET"), INTOBJ_INT((Int) SOCK_SEQPACKET)); #endif #ifdef SOCK_STREAM AssPRec(tmp, RNamName("SOCK_STREAM"), INTOBJ_INT((Int) SOCK_STREAM)); #endif #ifdef SOL_SOCKET AssPRec(tmp, RNamName("SOL_SOCKET"), INTOBJ_INT((Int) SOL_SOCKET)); #endif #ifdef IP_OPTIONS AssPRec(tmp, RNamName("IP_OPTIONS"), INTOBJ_INT((Int) IP_OPTIONS)); #endif #ifdef IP_PKTINFO AssPRec(tmp, RNamName("IP_PKTINFO"), INTOBJ_INT((Int) IP_PKTINFO)); #endif #ifdef IP_RECVTOS AssPRec(tmp, RNamName("IP_RECVTOS"), INTOBJ_INT((Int) IP_RECVTOS)); #endif #ifdef IP_RECVTTL AssPRec(tmp, RNamName("IP_RECVTTL"), INTOBJ_INT((Int) IP_RECVTTL)); #endif #ifdef IP_RECVOPTS AssPRec(tmp, RNamName("IP_RECVOPTS"), INTOBJ_INT((Int) IP_RECVOPTS)); #endif #ifdef IP_RETOPTS AssPRec(tmp, RNamName("IP_RETOPTS"), INTOBJ_INT((Int) IP_RETOPTS)); #endif #ifdef IP_TOS AssPRec(tmp, RNamName("IP_TOS"), INTOBJ_INT((Int) IP_TOS)); #endif #ifdef IP_TTL AssPRec(tmp, RNamName("IP_TTL"), INTOBJ_INT((Int) IP_TTL)); #endif #ifdef IP_HDRINCL AssPRec(tmp, RNamName("IP_HDRINCL"), INTOBJ_INT((Int) IP_HDRINCL)); #endif #ifdef IP_RECVERR AssPRec(tmp, RNamName("IP_RECVERR"), INTOBJ_INT((Int) IP_RECVERR)); #endif #ifdef IP_MTU_DISCOVER AssPRec(tmp, RNamName("IP_MTU_DISCOVER"), INTOBJ_INT((Int) IP_MTU_DISCOVER)); #endif #ifdef IP_MTU AssPRec(tmp, RNamName("IP_MTU"), INTOBJ_INT((Int) IP_MTU)); #endif #ifdef IP_ROUTER_ALERT AssPRec(tmp, RNamName("IP_ROUTER_ALERT"), INTOBJ_INT((Int) IP_ROUTER_ALERT)); #endif #ifdef IP_MULTICAST_TTL AssPRec(tmp, RNamName("IP_MULTICAST_TTL"), INTOBJ_INT((Int) IP_MULTICAST_TTL)); #endif #ifdef IP_MULTICAST_LOOP AssPRec(tmp, RNamName("IP_MULTICAST_LOOP"), INTOBJ_INT((Int) IP_MULTICAST_LOOP)); #endif #ifdef IP_ADD_MEMBERSHIP AssPRec(tmp, RNamName("IP_ADD_MEMBERSHIP"), INTOBJ_INT((Int) IP_ADD_MEMBERSHIP)); #endif #ifdef IP_DROP_MEMBERSHIP AssPRec(tmp, RNamName("IP_DROP_MEMBERSHIP"), INTOBJ_INT((Int)IP_DROP_MEMBERSHIP)); #endif #ifdef IP_MULTICAST_IF AssPRec(tmp, RNamName("IP_MULTICAST_IF"),INTOBJ_INT((Int) IP_MULTICAST_IF)); #endif #ifdef SO_RCVBUF AssPRec(tmp, RNamName("SO_RCVBUF"), INTOBJ_INT((Int) SO_RCVBUF)); #endif #ifdef SO_SNDBUF AssPRec(tmp, RNamName("SO_SNDBUF"), INTOBJ_INT((Int) SO_SNDBUF)); #endif #ifdef SO_SNDLOWAT AssPRec(tmp, RNamName("SO_SNDLOWAT"), INTOBJ_INT((Int) SO_SNDLOWAT)); #endif #ifdef SO_RCVLOWAT AssPRec(tmp, RNamName("SO_RCVLOWAT"), INTOBJ_INT((Int) SO_RCVLOWAT)); #endif #ifdef SO_SNDTIMEO AssPRec(tmp, RNamName("SO_SNDTIMEO"), INTOBJ_INT((Int) SO_SNDTIMEO)); #endif #ifdef SO_RCVTIMEO AssPRec(tmp, RNamName("SO_RCVTIMEO"), INTOBJ_INT((Int) SO_RCVTIMEO)); #endif #ifdef SO_REUSEADDR AssPRec(tmp, RNamName("SO_REUSEADDR"), INTOBJ_INT((Int) SO_REUSEADDR)); #endif #ifdef SO_KEEPALIVE AssPRec(tmp, RNamName("SO_KEEPALIVE"), INTOBJ_INT((Int) SO_KEEPALIVE)); #endif #ifdef SO_OOBINLINE AssPRec(tmp, RNamName("SO_OOBINLINE"), INTOBJ_INT((Int) SO_OOBINLINE)); #endif #ifdef SO_BSDCOMPAT AssPRec(tmp, RNamName("SO_BSDCOMPAT"), INTOBJ_INT((Int) SO_BSDCOMPAT)); #endif #ifdef SO_PASSCRED AssPRec(tmp, RNamName("SO_PASSCRED"), INTOBJ_INT((Int) SO_PASSCRED)); #endif #ifdef SO_PEERCRED AssPRec(tmp, RNamName("SO_PEERCRED"), INTOBJ_INT((Int) SO_PEERCRED)); #endif #ifdef SO_BINDTODEVICE AssPRec(tmp, RNamName("SO_BINDTODEVICE"),INTOBJ_INT((Int) SO_BINDTODEVICE)); #endif #ifdef SO_DEBUG AssPRec(tmp, RNamName("SO_DEBUG"), INTOBJ_INT((Int) SO_DEBUG)); #endif #ifdef SO_TYPE AssPRec(tmp, RNamName("SO_TYPE"), INTOBJ_INT((Int) SO_TYPE)); #endif #ifdef SO_ACCEPTCONN AssPRec(tmp, RNamName("SO_ACCEPTCONN"), INTOBJ_INT((Int) SO_ACCEPTCONN)); #endif #ifdef SO_DONTROUTE AssPRec(tmp, RNamName("SO_DONTROUTE"), INTOBJ_INT((Int) SO_DONTROUTE)); #endif #ifdef SO_BROADCAST AssPRec(tmp, RNamName("SO_BROADCAST"), INTOBJ_INT((Int) SO_BROADCAST)); #endif #ifdef SO_LINGER AssPRec(tmp, RNamName("SO_LINGER"), INTOBJ_INT((Int) SO_LINGER)); #endif #ifdef SO_PRIORITY AssPRec(tmp, RNamName("SO_PRIORITY"), INTOBJ_INT((Int) SO_PRIORITY)); #endif #ifdef SO_ERROR AssPRec(tmp, RNamName("SO_ERROR"), INTOBJ_INT((Int) SO_ERROR)); #endif #ifdef TCP_CORK AssPRec(tmp, RNamName("TCP_CORK"), INTOBJ_INT((Int) TCP_CORK)); #endif #ifdef TCP_DEFER_ACCEPT AssPRec(tmp,RNamName("TCP_DEFER_ACCEPT"),INTOBJ_INT((Int)TCP_DEFER_ACCEPT)); #endif #ifdef TCP_INFO AssPRec(tmp, RNamName("TCP_INFO"), INTOBJ_INT((Int) TCP_INFO)); #endif #ifdef TCP_KEEPCNT AssPRec(tmp, RNamName("TCP_KEEPCNT"), INTOBJ_INT((Int) TCP_KEEPCNT)); #endif #ifdef TCP_KEEPIDLE AssPRec(tmp, RNamName("TCP_KEEPIDLE"), INTOBJ_INT((Int) TCP_KEEPIDLE)); #endif #ifdef TCP_KEEPINTVL AssPRec(tmp, RNamName("TCP_KEEPINTVL"), INTOBJ_INT((Int) TCP_KEEPINTVL)); #endif #ifdef TCP_LINGER2 AssPRec(tmp, RNamName("TCP_LINGER2"), INTOBJ_INT((Int) TCP_LINGER2)); #endif #ifdef TCP_MAXSEG AssPRec(tmp, RNamName("TCP_MAXSEG"), INTOBJ_INT((Int) TCP_MAXSEG)); #endif #ifdef TCP_NODELAY AssPRec(tmp, RNamName("TCP_NODELAY"), INTOBJ_INT((Int) TCP_NODELAY)); #endif #ifdef TCP_QUICKACK AssPRec(tmp, RNamName("TCP_QUICKACK"), INTOBJ_INT((Int) TCP_QUICKACK)); #endif #ifdef TCP_SYNCNT AssPRec(tmp, RNamName("TCP_SYNCNT"), INTOBJ_INT((Int) TCP_SYNCNT)); #endif #ifdef TCP_WINDOW_CLAMP AssPRec(tmp,RNamName("TCP_WINDOW_CLAMP"),INTOBJ_INT((Int)TCP_WINDOW_CLAMP)); #endif #ifdef ICMP_FILTER AssPRec(tmp, RNamName("ICMP_FILTER"), INTOBJ_INT((Int) ICMP_FILTER)); #endif /* Constants for messages for recv and send: */ #ifdef MSG_OOB AssPRec(tmp, RNamName("MSG_OOB"), INTOBJ_INT((Int) MSG_OOB)); #endif #ifdef MSG_PEEK AssPRec(tmp, RNamName("MSG_PEEK"), INTOBJ_INT((Int) MSG_PEEK)); #endif #ifdef MSG_WAITALL AssPRec(tmp, RNamName("MSG_WAITALL"), INTOBJ_INT((Int) MSG_WAITALL)); #endif #ifdef MSG_TRUNC AssPRec(tmp, RNamName("MSG_TRUNC"), INTOBJ_INT((Int) MSG_TRUNC)); #endif #ifdef MSG_ERRQUEUE AssPRec(tmp, RNamName("MSG_ERRQUEUE"), INTOBJ_INT((Int) MSG_ERRQUEUE)); #endif #ifdef MSG_EOR AssPRec(tmp, RNamName("MSG_EOR"), INTOBJ_INT((Int) MSG_EOR)); #endif #ifdef MSG_CTRUNC AssPRec(tmp, RNamName("MSG_CTRUNC"), INTOBJ_INT((Int) MSG_CTRUNC)); #endif #ifdef MSG_OOB AssPRec(tmp, RNamName("MSG_OOB"), INTOBJ_INT((Int) MSG_OOB)); #endif #ifdef MSG_ERRQUEUE AssPRec(tmp, RNamName("MSG_ERRQUEUE"), INTOBJ_INT((Int) MSG_ERRQUEUE)); #endif #ifdef MSG_DONTWAIT AssPRec(tmp, RNamName("MSG_DONTWAIT"), INTOBJ_INT((Int) MSG_DONTWAIT)); #endif #ifdef PIPE_BUF AssPRec(tmp, RNamName("PIPE_BUF"), INTOBJ_INT((Int) PIPE_BUF)); #endif #ifdef F_DUPFD AssPRec(tmp, RNamName("F_DUPFD"), INTOBJ_INT((Int) F_DUPFD)); #endif #ifdef F_GETFD AssPRec(tmp, RNamName("F_GETFD"), INTOBJ_INT((Int) F_GETFD)); #endif #ifdef F_SETFD AssPRec(tmp, RNamName("F_SETFD"), INTOBJ_INT((Int) F_SETFD)); #endif #ifdef FD_CLOEXEC AssPRec(tmp, RNamName("FD_CLOEXEC"), INTOBJ_INT((Int) FD_CLOEXEC)); #endif #ifdef F_GETFL AssPRec(tmp, RNamName("F_GETFL"), INTOBJ_INT((Int) F_GETFL)); #endif #ifdef F_SETFL AssPRec(tmp, RNamName("F_SETFL"), INTOBJ_INT((Int) F_SETFL)); #endif #ifdef F_GETOWN AssPRec(tmp, RNamName("F_GETOWN"), INTOBJ_INT((Int) F_GETOWN)); #endif #ifdef F_SETOWN AssPRec(tmp, RNamName("F_SETOWN"), INTOBJ_INT((Int) F_SETOWN)); #endif #ifdef F_GETSIG AssPRec(tmp, RNamName("F_GETSIG"), INTOBJ_INT((Int) F_GETSIG)); #endif #ifdef F_SETSIG AssPRec(tmp, RNamName("F_SETSIG"), INTOBJ_INT((Int) F_SETSIG)); #endif #ifdef F_GETLEASE AssPRec(tmp, RNamName("F_GETLEASE"), INTOBJ_INT((Int) F_GETLEASE)); #endif #ifdef F_SETLEASE AssPRec(tmp, RNamName("F_SETLEASE"), INTOBJ_INT((Int) F_SETLEASE)); #endif #ifdef F_RDLCK AssPRec(tmp, RNamName("F_RDLCK"), INTOBJ_INT((Int) F_RDLCK)); #endif #ifdef F_WRLCK AssPRec(tmp, RNamName("F_WRLCK"), INTOBJ_INT((Int) F_WRLCK)); #endif #ifdef F_UNLCK AssPRec(tmp, RNamName("F_UNLCK"), INTOBJ_INT((Int) F_UNLCK)); #endif #ifdef __GNUC__ AssPRec(tmp, RNamName("__GNUC__"), INTOBJ_INT((Int) __GNUC__)); #endif #ifdef __GNUC_MINOR__ AssPRec(tmp, RNamName("__GNUC_MINOR__"), INTOBJ_INT((Int) __GNUC_MINOR__)); #endif #ifdef SIGHUP AssPRec(tmp, RNamName("SIGHUP"), INTOBJ_INT((Int) SIGHUP)); #endif #ifdef SIGINT AssPRec(tmp, RNamName("SIGINT"), INTOBJ_INT((Int) SIGINT)); #endif #ifdef SIGQUIT AssPRec(tmp, RNamName("SIGQUIT"), INTOBJ_INT((Int) SIGQUIT)); #endif #ifdef SIGILL AssPRec(tmp, RNamName("SIGILL"), INTOBJ_INT((Int) SIGILL)); #endif #ifdef SIGABRT AssPRec(tmp, RNamName("SIGABRT"), INTOBJ_INT((Int) SIGABRT)); #endif #ifdef SIGFPE AssPRec(tmp, RNamName("SIGFPE"), INTOBJ_INT((Int) SIGFPE)); #endif #ifdef SIGKILL AssPRec(tmp, RNamName("SIGKILL"), INTOBJ_INT((Int) SIGKILL)); #endif #ifdef SIGSEGV AssPRec(tmp, RNamName("SIGSEGV"), INTOBJ_INT((Int) SIGSEGV)); #endif #ifdef SIGPIPE AssPRec(tmp, RNamName("SIGPIPE"), INTOBJ_INT((Int) SIGPIPE)); #endif #ifdef SIGALRM AssPRec(tmp, RNamName("SIGALRM"), INTOBJ_INT((Int) SIGALRM)); #endif #ifdef SIGTERM AssPRec(tmp, RNamName("SIGTERM"), INTOBJ_INT((Int) SIGTERM)); #endif #ifdef SIGUSR1 AssPRec(tmp, RNamName("SIGUSR1"), INTOBJ_INT((Int) SIGUSR1)); #endif #ifdef SIGUSR2 AssPRec(tmp, RNamName("SIGUSR2"), INTOBJ_INT((Int) SIGUSR2)); #endif #ifdef SIGCHLD AssPRec(tmp, RNamName("SIGCHLD"), INTOBJ_INT((Int) SIGCHLD)); #endif #ifdef SIGCONT AssPRec(tmp, RNamName("SIGCONT"), INTOBJ_INT((Int) SIGCONT)); #endif #ifdef SIGSTOP AssPRec(tmp, RNamName("SIGSTOP"), INTOBJ_INT((Int) SIGSTOP)); #endif #ifdef SIGTSTP AssPRec(tmp, RNamName("SIGTSTP"), INTOBJ_INT((Int) SIGTSTP)); #endif #ifdef SIGTTIN AssPRec(tmp, RNamName("SIGTTIN"), INTOBJ_INT((Int) SIGTTIN)); #endif #ifdef SIGTTOU AssPRec(tmp, RNamName("SIGTTOU"), INTOBJ_INT((Int) SIGTTOU)); #endif #ifdef SIGBUS AssPRec(tmp, RNamName("SIGBUS"), INTOBJ_INT((Int) SIGBUS)); #endif #ifdef SIGPOLL AssPRec(tmp, RNamName("SIGPOLL"), INTOBJ_INT((Int) SIGPOLL)); #endif #ifdef SIGPROF AssPRec(tmp, RNamName("SIGPROF"), INTOBJ_INT((Int) SIGPROF)); #endif #ifdef SIGSYS AssPRec(tmp, RNamName("SIGSYS"), INTOBJ_INT((Int) SIGSYS)); #endif #ifdef SIGTRAP AssPRec(tmp, RNamName("SIGTRAP"), INTOBJ_INT((Int) SIGTRAP)); #endif #ifdef SIGURG AssPRec(tmp, RNamName("SIGURG"), INTOBJ_INT((Int) SIGURG)); #endif #ifdef SIGVTALRM AssPRec(tmp, RNamName("SIGVTALRM"), INTOBJ_INT((Int) SIGVTALRM)); #endif #ifdef SIGXCPU AssPRec(tmp, RNamName("SIGXCPU"), INTOBJ_INT((Int) SIGXCPU)); #endif #ifdef SIGXFSZ AssPRec(tmp, RNamName("SIGXFSZ"), INTOBJ_INT((Int) SIGXFSZ)); #endif #ifdef SIGIOT AssPRec(tmp, RNamName("SIGIOT"), INTOBJ_INT((Int) SIGIOT)); #endif #ifdef SIGEMT AssPRec(tmp, RNamName("SIGEMT"), INTOBJ_INT((Int) SIGEMT)); #endif #ifdef SIGSTKFLT AssPRec(tmp, RNamName("SIGSTKFLT"), INTOBJ_INT((Int) SIGSTKFLT)); #endif #ifdef SIGIO AssPRec(tmp, RNamName("SIGIO"), INTOBJ_INT((Int) SIGIO)); #endif #ifdef SIGCLD AssPRec(tmp, RNamName("SIGCLD"), INTOBJ_INT((Int) SIGCLD)); #endif #ifdef SIGPWR AssPRec(tmp, RNamName("SIGPWR"), INTOBJ_INT((Int) SIGPWR)); #endif #ifdef SIGINFO AssPRec(tmp, RNamName("SIGINFO"), INTOBJ_INT((Int) SIGINFO)); #endif #ifdef SIGLOST AssPRec(tmp, RNamName("SIGLOST"), INTOBJ_INT((Int) SIGLOST)); #endif #ifdef SIGWINCH AssPRec(tmp, RNamName("SIGWINCH"), INTOBJ_INT((Int) SIGWINCH)); #endif #ifdef SIGUNUSED AssPRec(tmp, RNamName("SIGUNUSED"), INTOBJ_INT((Int) SIGUNUSED)); #endif gvar = GVarName("IO"); MakeReadWriteGVar( gvar); AssGVar( gvar, tmp ); MakeReadOnlyGVar(gvar); /* return success */ return 0; } /****************************************************************************** *F InitInfopl() . . . . . . . . . . . . . . . . . table of init functions */ static StructInitInfo module = { #ifdef IOSTATIC /* type = */ MODULE_STATIC, #else /* type = */ MODULE_DYNAMIC, #endif /* name = */ "io", /* revision_c = */ 0, /* revision_h = */ 0, /* version = */ 0, /* crc = */ 0, /* initKernel = */ InitKernel, /* initLibrary = */ InitLibrary, /* checkInit = */ 0, /* preSave = */ 0, /* postSave = */ 0, /* postRestore = */ 0 }; #ifndef IOSTATIC StructInitInfo * Init__Dynamic ( void ) { return &module; } #endif StructInitInfo * Init__io ( void ) { return &module; } /* * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ gap-io-4.4.5+ds/tst/000077500000000000000000000000001264743113200141175ustar00rootroot00000000000000gap-io-4.4.5+ds/tst/buffered.g000066400000000000000000000071531264743113200160570ustar00rootroot00000000000000# This file checks buffered I/O: # We use this to general unique filenames, for parallel testing basepid := String(IO_getpid()); # First we create a longish string: st := ""; for i in [1..100000] do Append(st,String(i)); Add(st,'\n'); od; tmpname := Concatenation("tmpfile", basepid); # Now we write this to a file called "tmpfile": f := IO_File(tmpname,"w"); # we use standard buffer size if IO_Write(f,st) = fail then Error("write error ",1); fi; IO_Close(f); # Now read the same file again in different ways: f := IO_File(tmpname); s := ""; repeat block := IO_ReadBlock(f,1000); if block = fail then Error("read error ",2); fi; Append(s,block); until block = ""; IO_Close(f); if s <> st then Error("reading unsuccessful ",3); fi; # Now in principle it works, we now try nonblocking I/O on files: f := IO_File(tmpname,"w"); pos := 1; while pos <= Length(st) do if IO_ReadyForWrite(f) then bytes := IO_WriteNonBlocking(f,st,pos-1,Minimum(Length(st)-pos+1,1000)); pos := pos + bytes; else Print(".\c"); fi; od; IO_Close(f); # Now read the same file again: f := IO_File(tmpname); s := ""; block := "non-space"; repeat if IO_HasData(f) then block := IO_Read(f,1000); Append(s,block); else Print(".\c"); fi; until block = ""; IO_Close(f); if s <> st then Error("reading unsuccessful ",4); fi; PIPENAME := Concatenation("PIPE", basepid); # Now we want to send it over a named pipe: IO_mkfifo(PIPENAME,6*64+4*8+4); sender := function() local f,pos; f := IO_File(PIPENAME,"w"); pos := 1; while pos <= Length(st) do if IO_ReadyForWrite(f) then bytes := IO_WriteNonBlocking(f,st,pos-1,Minimum(Length(st)-pos+1, 100000)); pos := pos + bytes; #else # Print("Cannot write\n"); fi; od; IO_Close(f); end; selectsender := function() local f,pos; f := IO_File(PIPENAME,"w"); pos := 1; while pos <= Length(st) do if IO_Select([],[f],[],[],fail,fail) = 1 then bytes := IO_WriteNonBlocking(f,st,pos-1,Minimum(Length(st)-pos+1, 10000)); pos := pos + bytes; #Print("Wrote ",bytes,"\n"); #else # Print("Cannot write\n"); fi; od; repeat IO_Select([],[],[f],[],fail,fail); #Print("Can flush\n"); until IO_FlushNonBlocking(f); IO_Close(f); end; # Let's fork to get a second process going: pid := IO_fork(); if pid = -1 then Error("cannot fork ",5); fi; if pid = 0 then # The child sender(); IO_exit(0); fi; # Now read the same file again: f := IO_File(PIPENAME); s := ""; block := "non-space"; repeat if IO_HasData(f) then block := IO_Read(f,100000); Append(s,block); #else # Print("Cannot read\n"); fi; until block = ""; IO_Close(f); if s <> st then Error("reading unsuccessful ",4); fi; # Wait for the last process to finish IO_WaitPid(pid, true); # Let's fork to get a third process going: pid := IO_fork(); if pid = -1 then Error("cannot fork ",6); fi; if pid = 0 then # The child selectsender(); IO_exit(0); fi; # Now read the same file again: f := IO_File(PIPENAME,"r"); s := ""; block := "non-space"; repeat if IO_Select([f],[],[],[],fail,fail) = 1 then block := IO_Read(f,10000); Append(s,block); #Print("Read ",Length(block),"\n"); #else # Print("Cannot read!\n"); fi; until block = ""; IO_Close(f); IO_WaitPid(pid, true); if s <> st then Error("reading unsuccessful ",7); fi; # Cleanup: IO_unlink(PIPENAME); IO_unlink(tmpname); gap-io-4.4.5+ds/tst/bugfix.tst000066400000000000000000000021501264743113200161350ustar00rootroot00000000000000gap> s := List([1..2^18], x->CharInt(Random([63..126])));; gap> s2 := String(s);; ConvertToStringRep(s2); gap> f := IO_File("/dev/null", "w", 65536);; gap> IO_WriteLine(f, s{[1..30]}); 31 gap> IO_WriteLine(f, s); 262145 gap> IO_WriteLine(f, s{[1..2^17]}); 131073 gap> IO_WriteLine(f, s{[1..2^17+1]}); 131074 gap> IO_WriteLine(f, s2{[1..30]}); 31 gap> IO_WriteLine(f, s2); 262145 gap> IO_WriteLine(f, s2{[1..2^17]}); 131073 gap> IO_WriteLine(f, s2{[1..2^17+1]}); 131074 gap> IO_WriteNonBlocking(f, s, 1, 2^10); Error, Usage: IO_WriteNonBlocking( f, st, pos ) gap> IO_WriteNonBlocking(f, s2, 1, 2^10); 1024 gap> IO_WriteNonBlocking(f, s2, 1, 2^10+1); 1025 gap> IO_Close(f); true gap> gap> # Without buffering gap> gap> f := IO_File("/dev/null", "w", false);; gap> IO_WriteLine(f, s{[1..30]}); 31 gap> IO_WriteLine(f, s); 262145 gap> IO_WriteLine(f, s{[1..2^17]}); 131073 gap> IO_WriteLine(f, s{[1..2^17+1]}); 131074 gap> IO_WriteNonBlocking(f, s, 1, 2^10); Error, Usage: IO_WriteNonBlocking( f, st, pos ) gap> IO_WriteNonBlocking(f, s2, 1, 2^17); 131072 gap> IO_WriteNonBlocking(f, s2, 1, 2^17+1); 131073 gap> IO_Close(f); true gap-io-4.4.5+ds/tst/compression.g000066400000000000000000000040621264743113200166320ustar00rootroot00000000000000# NOTE: This test reads this first line!! # This file tests we can read compressed files # We use this to general unique filenames, for parallel testing basepid := String(IO_getpid()); LoadPackage("io"); d := DirectoriesPackageLibrary("io", "tst"); checkCompression := function(filename) local f, lines; filename := Concatenation(basepid, filename); # Lets hope we can write to the current directory f := IO_CompressedFile(filename, "w"); if f = fail then Error("Unable to create compressed file: ", 2); fi; if IO_WriteLine(f, "xyz") = fail then Error("Invalid write compressed file: ", 3); fi; IO_Close(f); # Let's check we can append f := IO_CompressedFile(filename, "a"); if f = fail then Error("Unable to append to compressed file: ", 4); fi; if IO_WriteLine(f, "abc") = fail then Error("Invalid write compressed file: ", 5); fi; IO_Close(f); f := IO_CompressedFile(filename, "r"); if IO_ReadLines(f) <> [ "xyz\n", "abc\n" ] then Error("Unable to read compressed file correctly: ", 6); fi; IO_Close(f); IO_unlink(filename); end; # Check no compression works checkCompression("tmpcompfile.txt"); f := IO_CompressedFile(Filename(d,"compression.g"), "r"); if IO_ReadLine(f) <> "# NOTE: This test reads this first line!!\n" then Error("IO_CompressedFile is broken on uncompressed files: ", 7); fi; IO_Close(f); # First let's check a pre-existing compressed file: if IO_FindExecutable("gzip") <> fail then f := IO_CompressedFile(Filename(d,"test.txt.gz"), "r"); lines := IO_ReadLines(f); if lines <> [ "Line\n", "Another Line\n", "Final Line\n", "\n" ] then Error("Invalid reading of compressed file: ",1); fi; IO_Close(f); # Now lets check we can create files checkCompression("tmpcompfile.gz"); fi; # Only do these if the executable exists if IO_FindExecutable("bzip2") <> fail then checkCompression("tmpcompfile.bz2"); fi; if IO_FindExecutable("xz") <> fail then checkCompression("tmpcompfile.xz"); fi; gap-io-4.4.5+ds/tst/exitcheck.g000066400000000000000000000007341264743113200162420ustar00rootroot00000000000000LoadPackage("io"); # Test if GAP hangs while exiting. We will not # clean up these processes, but hopefully they won't # cause too much trouble. x1 := IO_Popen("sleep", ["3600"],"r"); x2 := IO_Popen("sleep", ["3600"],"w"); x3 := IO_Popen2("sleep", ["3600"]); x4 := IO_Popen3("sleep", ["3600"]); y1 := IO_Popen("sleep", ["3600"],"r"); y2 := IO_Popen("sleep", ["3600"],"w"); y3 := IO_Popen2("sleep", ["3600"]); y4 := IO_Popen3("sleep", ["3600"]); Print("trying to exit..."); gap-io-4.4.5+ds/tst/http.g000066400000000000000000000010611264743113200152440ustar00rootroot00000000000000# Test HTTP protocol: LoadPackage("io"); r := SingleHTTPRequest("www-groups.mcs.st-and.ac.uk",80,"GET", "/~neunhoef/Computer/Software/Gap/io.version", rec(),false,false); if r.statuscode <> 200 then Print("Request was not successful, please check record r!\n"); else expected := Concatenation( "\n\n", "5.0\n\n"); if r.body <> expected then Print("Did not find expected body. ", "Maybe your IO package is not current?\n"); fi; fi; gap-io-4.4.5+ds/tst/pickle.g000066400000000000000000000125021264743113200155360ustar00rootroot00000000000000# Test file for pickling/unpickling: # Preparations: x := X(Rationals); InstallMethod( EQ, [ IsStraightLineProgram, IsStraightLineProgram ], function(a,b) return LinesOfStraightLineProgram(a) = LinesOfStraightLineProgram(b) and NrInputsOfStraightLineProgram(a) = NrInputsOfStraightLineProgram(b); end ); InstallMethod( PrintObj, "for element in Z/pZ (ModulusRep)", [ IsZmodpZObj and IsModulusRep ], function( x ) Print( "ZmodnZObj( ", x![1], ", ", Characteristic( x ), " )" ); end ); InstallMethod( String, "for element in Z/pZ (ModulusRep)", [ IsZmodpZObj and IsModulusRep ], function( x ) return Concatenation( "ZmodnZObj(", String(x![1]), ",", String(Characteristic( x )), ")" ); end ); # Build up a variety of different GAP objects in a list: l := [ false, true, fail, SuPeRfail, 0, -1, 1, 1234567123512636523123561311223123123123234234234, 1.0, -1.0, 1.23456789123456789 -0., # Note, we test floats further at Error(36) 0., 1.0/0.0, # inf (-1.0)/0.0, # -inf 3.141^100, 3.141^-100, Transformation([]), Transformation([3,2,1]), Transformation([1,1,1]), Transformation([3,3,3]), Transformation([100],[100]), Transformation([100],[103]), PartialPerm([]), PartialPerm([2],[3]), PartialPerm([1000],[999]), PartialPerm([1000,1001],[1000,3]), PartialPerm([9,10,20],[30,40,50]), "Max", 'M', E(4), E(4)+E(4)^3, StraightLineProgram([[1,1,2,1,1,-1],[3,1,2,-1]],2), Z(2), Z(2)^0, 0*Z(2), Z(2^3), Z(2^3)^0, 0*Z(2^3), Z(3), Z(3)^0, 0*Z(3), Z(3^5), Z(3^5)^0, 0*Z(3^5), Z(257), 0*Z(257), Z(257)^0, Z(257^4), 0*Z(257^4), Z(257^4)^0, Z(65537), Z(65537)^0, 0*Z(65537), Z(65537^2), Z(65537^2)^0, 0*Z(65537^2), (1,2,3,4), ,,,, # a gap x^2+x+1, x^-3+1+x^4, (x+1)/(x+2), rec( a := 1, b := "Max" ), rec( c := 3, d := "Till" ), ]; MakeImmutable(l[Length(l)]); v := [Z(5),0*Z(5),Z(5)^2]; ConvertToVectorRep(v,5); Add(l,v); vecpos := Length(l); w := ShallowCopy(v); MakeImmutable(w); Add(l,w); vv := [Z(7),0*Z(7),Z(7)^2]; ConvertToVectorRep(vv,7^2); Add(l,vv); ww := ShallowCopy(vv); MakeImmutable(ww); Add(l,ww); vvv := [Z(2),0*Z(2)]; ConvertToVectorRep(vvv,2); Add(l,vvv); www := ShallowCopy(vvv); MakeImmutable(www); Add(l,www); # compressed matrices: m := [[Z(5),0*Z(5),Z(5)^2]]; ConvertToMatrixRep(m,5); Add(l,m); n := MutableCopyMat(m); ConvertToMatrixRep(n,5); MakeImmutable(n); Add(l,n); mm := [[Z(7),0*Z(7),Z(7)^2]]; ConvertToMatrixRep(mm,7^2); Add(l,mm); nn := MutableCopyMat(mm); ConvertToMatrixRep(nn,7^2); MakeImmutable(nn); Add(l,nn); mmm := [[Z(2),0*Z(2)]]; ConvertToMatrixRep(mmm,2); Add(l,mmm); nnn := MutableCopyMat(mmm); ConvertToMatrixRep(nnn,2); MakeImmutable(nnn); Add(l,nnn); # Finally self-references: r := rec( l := l, x := 1 ); r.r := r; Add(l,l); Add(l,r); s := ""; f := IO_WrapFD(-1,false,s); if IO_Pickle(f,l) <> IO_OK then Error(1); fi; if IO_Pickle(f,"End") <> IO_OK then Error(2); fi; IO_Close(f); # Print("Bytes pickled: ",Length(s),"\n"); f := IO_WrapFD(-1,s,false); ll := IO_Unpickle(f); for i in [1..Length(l)-2] do if not( (not(IsBound(l[i])) and not(IsBound(ll[i]))) or (IsBound(l[i]) and IsBound(ll[i]) and l[i] = ll[i]) ) then Error(3); fi; od; if not(IsIdenticalObj(ll,ll[Length(ll)-1])) then Error(4); fi; if not(IsIdenticalObj(ll,ll[Length(ll)].l)) then Error(5); fi; if not(IsIdenticalObj(ll[Length(ll)],ll[Length(ll)].r)) then Error(6); fi; if ll[Length(ll)].x <> l[Length(l)].x then Error(7); fi; if not(IsMutable(ll[vecpos-2])) then Error(8); fi; if IsMutable(ll[vecpos-1]) then Error(9); fi; if not(Is8BitVectorRep(ll[vecpos])) then Error(10); fi; if not(IsMutable(ll[vecpos])) then Error(11); fi; if not(Is8BitVectorRep(ll[vecpos+1])) then Error(12); fi; if IsMutable(ll[vecpos+1]) then Error(13); fi; if not(Is8BitVectorRep(ll[vecpos+2])) then Error(14); fi; if not(IsMutable(ll[vecpos+2])) then Error(15); fi; if not(Is8BitVectorRep(ll[vecpos+3])) then Error(16); fi; if IsMutable(ll[vecpos+3]) then Error(17); fi; if not(IsGF2VectorRep(ll[vecpos+4])) then Error(18); fi; if not(IsMutable(ll[vecpos+4])) then Error(19); fi; if not(IsGF2VectorRep(ll[vecpos+5])) then Error(20); fi; if IsMutable(ll[vecpos+5]) then Error(21); fi; if not(Is8BitMatrixRep(ll[vecpos+6])) then Error(22); fi; if not(IsMutable(ll[vecpos+6])) or not(IsMutable(ll[vecpos+6][1])) then Error(23); fi; if not(Is8BitMatrixRep(ll[vecpos+7])) then Error(24); fi; if IsMutable(ll[vecpos+7]) or IsMutable(ll[vecpos+7]) then Error(25); fi; if not(Is8BitMatrixRep(ll[vecpos+8])) then Error(26); fi; if not(IsMutable(ll[vecpos+8])) or not(IsMutable(ll[vecpos+8])) then Error(27); fi; if not(Is8BitMatrixRep(ll[vecpos+9])) then Error(28); fi; #if IsMutable(ll[vecpos+9]) or IsMutable(ll[vecpos+9][1]) then Error(29); fi; if not(IsGF2MatrixRep(ll[vecpos+10])) then Error(30); fi; if not(IsMutable(ll[vecpos+10])) or not(IsMutable(ll[vecpos+10][1])) then Error(31); fi; if not(IsGF2MatrixRep(ll[vecpos+11])) then Error(32); fi; #if IsMutable(ll[vecpos+11]) or IsMutable(ll[vecpos+11][1]) then Error(33); fi; ee := IO_Unpickle(f); if ee <> "End" then Error(34); fi; if IO_Unpickle(f) <> IO_Nothing then Error(35); fi; IO_Close(f); floatlist := [-0.0, 0.0, 0.0/0.0, 1.0/0.0, -1.0/0.0, 1.23456789123456789]; pickledlist := IO_Unpickle(IO_Pickle(floatlist)); # ExtRepOfObj deals with issues like infinity, -0 vs +0, nan, etc. if List(floatlist, x -> ExtRepOfObj(x)) <> List(pickledlist, x -> ExtRepOfObj(x)) then Error(36); fi; gap-io-4.4.5+ds/tst/platform.g000066400000000000000000000143761264743113200161260ustar00rootroot00000000000000# This tests, which things in the IO package exist on a given platform: StdGlobalVarsIO := Set( [ "IO", "IO_AddToPickled", "IO_AddToUnpickled", "IO_ClearPickleCache", "IO_Close", "IO_CloseAllFDs", "IO_EOF", "IO_Environment", "IO_Error", "IO_File", "IO_FinalizePickled", "IO_FinalizeUnpickled", "IO_Flush", "IO_FlushNonBlocking", "IO_GenericObjectPickler", "IO_GenericObjectUnpickler", "IO_GetFD", "IO_GetWBuf", "IO_HasData", "IO_InstallSIGCHLDHandler", "IO_ListDir", "IO_MakeEnvList", "IO_MakeIPAddressPort", "IO_MasterPointerNumber", "IO_Nothing", "IO_OK", "IO_PICKLECACHE", "IO_Pickle", "IO_PickleByString", "IO_PipeThrough", "IO_PipeThroughWithError", "IO_Popen", "IO_Popen2", "IO_Popen3", "IO_Read", "IO_ReadAttribute", "IO_ReadBlock", "IO_ReadLine", "IO_ReadLines", "IO_ReadSmallInt", "IO_ReadUntilEOF", "IO_ReadyForFlush", "IO_ReadyForWrite", "IO_RestoreSIGCHLDHandler", "IO_Result", "IO_ResultsFamily", "IO_Select", "IO_SendStringBackground", "IO_Unpickle", "IO_UnpickleByEvalString", "IO_Unpicklers", "IO_WaitPid", "IO_WrapFD", "IO_Write", "IO_WriteAttribute", "IO_WriteFlush", "IO_WriteLine", "IO_WriteLines", "IO_WriteNonBlocking", "IO_WriteSmallInt", "IO_accept", "IO_bind", "IO_chdir", "IO_chmod", "IO_chown", "IO_close", "IO_closedir", "IO_connect", "IO_creat", "IO_dup", "IO_dup2", "IO_environ", "IO_execv", "IO_execve", "IO_execvp", "IO_exit", "IO_fchmod", "IO_fchown", "IO_fcntl", "IO_fork", "IO_fstat", "IO_gethostbyname", "IO_getsockopt", "IO_lchown", "IO_link", "IO_listen", "IO_lseek", "IO_lstat", "IO_make_sockaddr_in", "IO_mkdir", "IO_mkfifo", "IO_mknod", "IO_open", "IO_opendir", "IO_pipe", "IO_read", "IO_readdir", "IO_readlink", "IO_recv", "IO_recvfrom", "IO_rename", "IO_rewinddir", "IO_rmdir", "IO_seekdir", "IO_select", "IO_send", "IO_sendto", "IO_setsockopt", "IO_socket", "IO_stat", "IO_symlink", "IO_telldir", "IO_unlink", "IO_write", "IO_FileFilterString", "IO_FilteredFile", "IO_FindExecutable", "IO_ForkExecWithFDs", "IO_StartPipeline", "IO_StringFilterFile", "IO_FuncToUnpickle", "IO_IsAlreadyPickled", "IO_PackageIsLoaded", "IO_PkgThingsToRead", "IO_getpid", "IO_getppid", "IO_kill", "IO_gettimeofday", "IO_gmtime", "IO_localtime", "IO_gethostname", "IO_getsockname", "IOHub", "IOHubFamily", "IOHubType" ] ); StdComponentsIO := Set( [ "AF_APPLETALK", "AF_ASH", "AF_ATMPVC", "AF_ATMSVC", "AF_AX25", "AF_BLUETOOTH", "AF_BRIDGE", "AF_DECnet", "AF_ECONET", "AF_FILE", "AF_INET", "AF_INET6", "AF_IPX", "AF_IRDA", "AF_KEY", "AF_LOCAL", "AF_MAX", "AF_NETBEUI", "AF_NETLINK", "AF_NETROM", "AF_PACKET", "AF_PPPOX", "AF_ROSE", "AF_ROUTE", "AF_SECURITY", "AF_SNA", "AF_UNIX", "AF_UNSPEC", "AF_WANPIPE", "AF_X25", "DefaultBufSize", "EACCES", "EADDRINUSE", "EAFNOSUPPORT", "EAGAIN", "EALREADY", "EBADF", "EBUSY", "ECHILD", "ECONNABORTED", "ECONNREFUSED", "EEXIST", "EFAULT", "EFBIG", "EINPROGRESS", "EINTR", "EINVAL", "EIO", "EISCONN", "EISDIR", "ELOOP", "EMFILE", "EMLINK", "ENAMETOOLONG", "ENFILE", "ENODEV", "ENOENT", "ENOMEM", "ENOSPC", "ENOTDIR", "ENOTEMPTY", "ENOTSOCK", "ENXIO", "EOPNOTSUPP", "EOVERFLOW", "EPERM", "EPIPE", "EPROTO", "EPROTONOSUPPORT", "EROFS", "ESPIPE", "ETIMEDOUT", "ETXTBSY", "EWOULDBLOCK", "EXDEV", "FD_CLOEXEC", "F_DUPFD", "F_GETFD", "F_GETFL", "F_GETLEASE", "F_GETOWN", "F_GETSIG", "F_RDLCK", "F_SETFD", "F_SETFL", "F_SETLEASE", "F_SETOWN", "F_SETSIG", "F_UNLCK", "F_WRLCK", "HOST_NOT_FOUND", "IP_ADD_MEMBERSHIP", "IP_DROP_MEMBERSHIP", "IP_HDRINCL", "IP_MTU_DISCOVER", "IP_MULTICAST_IF", "IP_MULTICAST_LOOP", "IP_MULTICAST_TTL", "IP_OPTIONS", "IP_PKTINFO", "IP_RECVERR", "IP_RECVOPTS", "IP_RECVTOS", "IP_RECVTTL", "IP_RETOPTS", "IP_ROUTER_ALERT", "IP_TOS", "IP_TTL", "LineEndChar", "LineEndChars", "MSG_CTRUNC", "MSG_DONTWAIT", "MSG_EOR", "MSG_ERRQUEUE", "MSG_OOB", "MSG_PEEK", "MSG_TRUNC", "MSG_WAITALL", "MaxFDToClose", "NO_ADDRESS", "NO_DATA", "NO_RECOVERY", "O_APPEND", "O_ASYNC", "O_CREAT", "O_DIRECT", "O_DIRECTORY", "O_EXCL", "O_LARGEFILE", "O_NDELAY", "O_NOATIME", "O_NOCTTY", "O_NOFOLLOW", "O_NONBLOCK", "O_RDONLY", "O_RDWR", "O_SYNC", "O_TRUNC", "O_WRONLY", "PF_APPLETALK", "PF_ASH", "PF_ATMPVC", "PF_ATMSVC", "PF_AX25", "PF_BLUETOOTH", "PF_BRIDGE", "PF_DECnet", "PF_ECONET", "PF_FILE", "PF_INET", "PF_INET6", "PF_IPX", "PF_IRDA", "PF_KEY", "PF_LOCAL", "PF_MAX", "PF_NETBEUI", "PF_NETLINK", "PF_NETROM", "PF_PACKET", "PF_PPPOX", "PF_ROSE", "PF_ROUTE", "PF_SECURITY", "PF_SNA", "PF_UNIX", "PF_WANPIPE", "PF_X25", "PIPE_BUF", "SEEK_CUR", "SEEK_END", "SEEK_SET", "SOCK_DGRAM", "SOCK_PACKET", "SOCK_RAW", "SOCK_RDM", "SOCK_SEQPACKET", "SOCK_STREAM", "SOL_SOCKET", "SO_ACCEPTCONN", "SO_BINDTODEVICE", "SO_BROADCAST", "SO_BSDCOMPAT", "SO_DEBUG", "SO_DONTROUTE", "SO_ERROR", "SO_KEEPALIVE", "SO_LINGER", "SO_OOBINLINE", "SO_PASSCRED", "SO_PEERCRED", "SO_PRIORITY", "SO_RCVBUF", "SO_RCVLOWAT", "SO_RCVTIMEO", "SO_REUSEADDR", "SO_SNDBUF", "SO_SNDLOWAT", "SO_SNDTIMEO", "SO_TYPE", "S_IFBLK", "S_IFCHR", "S_IFDIR", "S_IFIFO", "S_IFLNK", "S_IFMT", "S_IFREG", "S_IFSOCK", "S_IRGRP", "S_IROTH", "S_IRUSR", "S_IRWXG", "S_IRWXO", "S_IRWXU", "S_ISGID", "S_ISUID", "S_ISVTX", "S_IWGRP", "S_IWOTH", "S_IWUSR", "S_IXGRP", "S_IXOTH", "S_IXUSR", "TCP_CORK", "TCP_DEFER_ACCEPT", "TCP_INFO", "TCP_KEEPCNT", "TCP_KEEPIDLE", "TCP_KEEPINTVL", "TCP_LINGER2", "TCP_MAXSEG", "TCP_NODELAY", "TCP_QUICKACK", "TCP_SYNCNT", "TCP_WINDOW_CLAMP", "TRY_AGAIN", "__GNUC_MINOR__", "__GNUC__", "NonBlockWriteAmount", "SIGABRT", "SIGALRM", "SIGBUS", "SIGCHLD", "SIGCLD", "SIGCONT", "SIGFPE", "SIGHUP", "SIGILL", "SIGINT", "SIGIO", "SIGIOT", "SIGKILL", "SIGPIPE", "SIGPOLL", "SIGPROF", "SIGPWR", "SIGQUIT", "SIGSEGV", "SIGSTKFLT", "SIGSTOP", "SIGSYS", "SIGTERM", "SIGTRAP", "SIGTSTP", "SIGTTIN", "SIGTTOU", "SIGUNUSED", "SIGURG", "SIGUSR1", "SIGUSR2", "SIGVTALRM", "SIGWINCH", "SIGXCPU", "SIGXFSZ", "IP_MTU" ]); # Note missing but possible (includes missing because of autoconf: # "ENOBUGS", "ICMP_FILTER", "IP_MTU" GlobalVarsIO := Set(Filtered(NamesGVars(),x->Length(x)>=2 and x{[1..2]} = "IO")); ComponentsIO := Set(NamesOfComponents(IO)); Print("Not available here:\n",Difference(StdGlobalVarsIO,GlobalVarsIO),"\n", Difference(StdComponentsIO,ComponentsIO),"\n\n"); Print("More available here:\n",Difference(GlobalVarsIO,StdGlobalVarsIO),"\n", Difference(ComponentsIO,StdComponentsIO),"\n\n"); gap-io-4.4.5+ds/tst/test.txt.gz000066400000000000000000000000531264743113200162540ustar00rootroot00000000000000‹óÉÌKårÌË/ÉH-RðqÜ2ós L.]€·%gap-io-4.4.5+ds/tst/testall.g000066400000000000000000000002011264743113200157300ustar00rootroot00000000000000LoadPackage("IO"); d := DirectoriesPackageLibrary("IO", "tst"); Test(Filename(d, "bugfix.tst")); Read(Filename(d, "testgap.g")); gap-io-4.4.5+ds/tst/testgap.g000066400000000000000000000006731264743113200157440ustar00rootroot00000000000000LoadPackage("IO"); d := DirectoriesPackageLibrary("IO", "tst"); # Too many things in this directory, we'll just list the tests we want to run files := ["buffered.g", "compression.g", "http.g", "pickle.g"]; # We do this 50 times to catch issues with too many files / processes being created for i in [1..50] do Print("Iteration #", i, "\n"); for f in files do Read(Filename(d[1], f)); od; od; Read(Filename(d[1], "exitcheck.g"));