diff options
author | Anas Nashif <anas.nashif@intel.com> | 2012-11-06 22:56:34 -0800 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2012-11-06 22:56:34 -0800 |
commit | a95df6d374a9be6fa8cee82be6ff1fb51a3c8593 (patch) | |
tree | d9e3755a2062db09de25982bc0d33b3eab272275 | |
download | ed-a95df6d374a9be6fa8cee82be6ff1fb51a3c8593.tar.gz ed-a95df6d374a9be6fa8cee82be6ff1fb51a3c8593.tar.bz2 ed-a95df6d374a9be6fa8cee82be6ff1fb51a3c8593.zip |
Imported Upstream version 1.6upstream/1.6
211 files changed, 8494 insertions, 0 deletions
@@ -0,0 +1,25 @@ +Since 2006 GNU ed is maintained by Antonio Diaz Diaz. + +Before version 0.3, GNU ed and its man page were written and maintained +(sic) by Andrew L. Moore. + +The original info page and GNUification of the code were graciously +provided by François Pinard. + +------------------- + +GNU ed THANKS file - last updated on 15 November 1994. + +GNU ed originated with the editor algorithm from Brian W. Kernighan & +P. J. Plauger's wonderful book "Software Tools in Pascal," Addison-Wesley, +1981. GNU ed has also benefitted from the contributions of numerous people +who reported problems, suggested various improvements or submitted actual +code. Among these are the following: + +Eric Backus <ericb@lsid.hp.com> +Karl Berry <kb@cs.umb.edu> +Theo Deraadt <deraadt@newt.fsa.ca> +Kaveh R. Ghazi <ghazi@noc.rutgers.edu> +Mike Haertel <mike@ichips.intel.com> +François Pinard <pinard@iro.umontreal.ca> +Rodney Ruddock <rodney@snowhite.cis.uoguelph.ca> @@ -0,0 +1,676 @@ + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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/>. + +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: + + <program> Copyright (C) <year> <name of author> + 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 +<http://www.gnu.org/licenses/>. + + 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 +<http://www.gnu.org/philosophy/why-not-lgpl.html>. + diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..52f2795 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,190 @@ +2012-01-01 Antonio Diaz Diaz <ant_diaz@teleline.es> + + * Version 1.6 released. + * io.c (put_tty_line): Null characters where incorrectly + shown by the `l' command. (Reported by Martin Guy). + * io.c (read_stream): Corrected the condition deciding when to + show the message "Newline appended". + * main_loop.c (exec_command): The `modified' flag is now set + when reading a non-empty file into an empty buffer. + * regex.c (translit_text): Fixed typo that prevented using NUL + characters in regular expressions. + * main_loop.c (exec_command): Return ERR if `system' can't + create a shell process. + * main_loop.c (main_loop): Flush stdout/stderr before reading a + new command. + * buffer.c (put_sbuf_line): Added size parameter. + * ed.1: Man page is now generated with `help2man'. + * ed.1: All command-line options are now documented in the man page. + * Restored copyright notices of Andrew L. Moore. It seems Andrew + granted some permissions but never assigned copyright to the FSF. + +2010-08-30 Antonio Diaz Diaz <ant_diaz@teleline.es> + + * Version 1.5 released. + * buffer.c (append_lines): Fixed `a', `c' and `i' commands. + (When used in a global command list, the commands following + them in the list were ignored). + * main_loop.c (exec_command): Fixed `e' command. (It quitted + when invoked a second time with a modified buffer). + * main.c: Added new option `--restricted'. + * `red' has been converted to a script invoking `ed --restricted'. + * Description of ed in the manual has been changed. + * testsuite: Modified some tests and removed obsolete posix tests. + * main_loop.c: `ibufp' variable made local to main_loop. + * Defined type bool to make clear which functions and variables + are Boolean. + * Added `const' to all pointer declarations accepting it. + * regex.c (replace_matching_text): Make se_max an enum. + * signal.c: Include termios.h + * Converted C99 style comments `//' to C89 style comments `/* */'. + * ed.texinfo: Fixed an erratum. + * Changed copyright holder from Andrew, Antonio to the FSF. + +2009-07-10 Antonio Diaz Diaz <ant_diaz@teleline.es> + + * Version 1.4 released. + * buffer.c, main_loop.c: Undo now restores the modified status. + * regex.c (search_and_replace): Fixed a race condition with user + interrupt. + * signal.c: Added functions resize_line_buffer and + resize_undo_buffer to definitively fix the aliasing warnings. + * Some minor corrections have been made to the manual. + +2009-05-24 Antonio Diaz Diaz <ant_diaz@teleline.es> + + * Version 1.3 released. + * carg_parser.c (ap_resize_buffer): An aliasing related segfault + that only occurs when overoptimizing with GCC on some + architectures (alpha, sparc) has been (hopefully) fixed. + * signal.c (resize_buffer): Likewise. + +2009-01-31 Antonio Diaz Diaz <ant_diaz@teleline.es> + + * Version 1.2 released. + * configure: Locale has been fixed to `C'. + * Makefile.in: Man page is now installed by default. + * `make install-info' should now work on Debian and OS X. + * ed.texinfo: License updated to GFDL version 1.3 or later. + +2008-10-14 Antonio Diaz Diaz <ant_diaz@teleline.es> + + * Version 1.1 released. + * configure: Quote arguments stored in config.status. + +2008-08-21 Antonio Diaz Diaz <ant_diaz@teleline.es> + + * Version 1.0 released. + * configure: Added option `--program-prefix'. + * signal.c (strip_escapes): Buffer overflow fixed. + * signal.c (resize_buffer): Pointer aliasing warning fixed. + +2008-02-24 Antonio Diaz Diaz <ant_diaz@teleline.es> + + * Version 0.9 released. + * signal.c (sighup_handler): Return 0 if no error. + * Arg_parser updated to 1.1. + +2007-08-18 Antonio Diaz Diaz <ant_diaz@teleline.es> + + * Version 0.8 released. + * check.sh: Testsuite exits unsuccesfully in case of error. + * ed.1: Fixed some minor problems in the manual page. + * ed.texinfo: Added 21kB of legalese (fdl.texinfo). + +2007-07-18 Antonio Diaz Diaz <ant_diaz@teleline.es> + + * Version 0.7 released. + * buffer.c (dec_addr): Now returns correct address when wrapping. + +2007-06-29 Antonio Diaz Diaz <ant_diaz@teleline.es> + + * Version 0.6 released. + * License updated to GPL version 3 or later. + * signal.c (sigwinch_handler, set_signal): + Fixed two minor compatibility problems. + * main_loop.c (main_loop): + Fixed an infinite loop when reading an empty script. + +2007-03-09 Antonio Diaz Diaz <ant_diaz@teleline.es> + + * Version 0.5 released. + * main_loop.c (next_addr): '%' reimplemented as it was in ed 0.2. + +2007-01-15 Antonio Diaz Diaz <ant_diaz@teleline.es> + + * Version 0.4 released. + * Fixed some minor problems in the testsuite. + +2006-11-11 Antonio Diaz Diaz <ant_diaz@teleline.es> + + * Version 0.3 released. + * buffer.c (open_sbuf): Fixed symlink vulnerability using `tmpfile'. + * signal.c: Fixed signal handling for SIGINT. + * main_loop.c (exec_command): Fixed `c' and `i' commands with + address 0. + * The pause mode has been removed. + * main.c: Added new options `--loose-exit-status' and `--verbose'. + * carg_parser.c: New argument parser that replaces `getopt_long'. + * `configure' and `Makefile.in' have been replaced. + * Removed recursive make for testsuite. + * Created directory `doc'. + * Removed all pre ISO C89 code. + * Removed all global variables. + * ed.texinfo: Added the changes from Andrew and some mine. + +Sun Jun 26 22:21:59 1994 Andrew Moore <alm@worm.talke.org> + + * GNU ed 0.2 release. + + * main.c (yank_lines): Added yank buffer. + A range of lines may be cut ('d') to or yanked ('y') from + a yank buffer. Lines in the buffer may be put ('x') + after the addressed line (. by default). + + * main.c (display_lines): Page output of listed ('l') lines + if isatty(0). + + * main.c (main): Replaced isatty(0) with is_regular_file(). + Errors in piped scripts, as opposed to regular scripts or + here documents, do not force ed to exit. + + * Capitilize error messages per the standard. + +Wed Jun 22 01:06:11 1994 Andrew Moore <alm@woops.talke.org> + + * ed.h: Generic definition of INT_MAX <bson@ai.mit.edu> + + * signal.c: Added #ifndef SIG_ERR <assar@stacken.kth.se> + +Tue Apr 19 10:52:51 1994 Andrew Moore <alm@woops.talke.org> + + * Version 0.1. Initial release for GNU. + + * main.c (exec_command): Add comment command `#'. + +Mon Mar 21 21:58:11 PST 1994 Andrew Moore <alm@netcom.com> + + * Use umask 077 to open buffer file. + +Sat Mar 19 14:06:52 PST 1994 Andrew Moore <alm@netcom.com> + + * Removed problematic DES and insque support. + +Wed Jan 19 20:42:50 PST 1994 Andrew Moore <alm@netcom.com> + + * Added reliable signal(2) for SysV. + +Dec 1993 François Pinard <pinard@icule> + + * GNUified ed. + + +Copyright (C) 1993 François Pinard +Copyright (C) 1994 Andrew Moore +Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 +Free Software Foundation, Inc. + +This file is a collection of facts, and thus it is not copyrightable, +but just in case, you have unlimited permission to copy, distribute and +modify it. @@ -0,0 +1,57 @@ +Requirements +------------ +You'll need a C compiler and a C library compatible with GNU libc. +I use gcc 4.3.5 and 3.3.6, but the code should compile with any +standards compliant compiler. +Gcc is available at http://gcc.gnu.org. + + +Procedure +--------- +1. Unpack the archive if you have not done so already: + + lzip -cd ed[version].tar.lz | tar -xf - +or + gzip -cd ed[version].tar.gz | tar -xf - + +This creates the directory ./ed[version] containing the source from +the main archive. + +2. Change to ed directory and run configure. + (Try `configure --help' for usage instructions). + + cd ed[version] + ./configure + +3. Run make. + + make + +4. Optionally, type `make check' to run the tests that come with ed. + +5. Type `make install' to install the program and any data files and + documentation. + + +Another way +----------- +You can also compile ed into a separate directory. To do this, you +must use a version of `make' that supports the `VPATH' variable, such +as GNU `make'. `cd' to the directory where you want the object files +and executables to go and run the `configure' script. `configure' +automatically checks for the source code in `.', in `..' and in the +directory that `configure' is in. + +`configure' recognizes the option `--srcdir=DIR' to control where to +look for the sources. Usually `configure' can determine that directory +automatically. + +After running `configure', you can run `make' and `make install' as +explained above. + + +Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 +Antonio Diaz Diaz. + +This file is free documentation: you have unlimited permission to copy, +distribute and modify it. diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..6567008 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,123 @@ + +DISTNAME = $(pkgname)-$(pkgversion) +INSTALL = install +INSTALL_PROGRAM = $(INSTALL) -p -m 755 +INSTALL_SCRIPT = $(INSTALL) -p -m 755 +INSTALL_DATA = $(INSTALL) -p -m 644 +INSTALL_DIR = $(INSTALL) -d -m 755 +SHELL = /bin/sh + +objs = buffer.o carg_parser.o global.o io.o \ + main.o main_loop.o regex.o signal.o + + +.PHONY : all install install-info install-man install-strip \ + uninstall uninstall-info uninstall-man \ + doc info man check dist clean distclean + +all : $(progname) r$(progname) + +$(progname) : $(objs) + $(CC) $(LDFLAGS) -o $@ $(objs) + +$(progname)_profiled : $(objs) + $(CC) $(LDFLAGS) -pg -o $@ $(objs) + +r$(progname) : r$(progname).in + cat $(VPATH)/r$(progname).in > $@ + chmod a+x $@ + +main.o : main.c + $(CC) $(CPPFLAGS) $(CFLAGS) -DPROGVERSION=\"$(pkgversion)\" -c -o $@ $< + +%.o : %.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +$(objs) : Makefile ed.h +carg_parser.o : carg_parser.h +main.o : carg_parser.h + + +doc : info man + +info : $(VPATH)/doc/$(pkgname).info + +$(VPATH)/doc/$(pkgname).info : $(VPATH)/doc/$(pkgname).texinfo + cd $(VPATH)/doc && makeinfo $(pkgname).texinfo + +man : $(VPATH)/doc/$(progname).1 + +$(VPATH)/doc/$(progname).1 : $(progname) + help2man -n 'line-oriented text editor' \ + -o $@ ./$(progname) + +Makefile : $(VPATH)/configure $(VPATH)/Makefile.in + ./config.status + +check : all + @$(VPATH)/testsuite/check.sh $(VPATH)/testsuite $(pkgversion) + +install : all install-info install-man + if [ ! -d "$(DESTDIR)$(bindir)" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(bindir)" ; fi + $(INSTALL_PROGRAM) ./$(progname) "$(DESTDIR)$(bindir)/$(program_prefix)$(progname)" + $(INSTALL_SCRIPT) ./r$(progname) "$(DESTDIR)$(bindir)/$(program_prefix)r$(progname)" + +install-info : + if [ ! -d "$(DESTDIR)$(infodir)" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(infodir)" ; fi + $(INSTALL_DATA) $(VPATH)/doc/$(pkgname).info "$(DESTDIR)$(infodir)/$(program_prefix)$(pkgname).info" + -install-info --info-dir="$(DESTDIR)$(infodir)" "$(DESTDIR)$(infodir)/$(program_prefix)$(pkgname).info" + +install-man : + if [ ! -d "$(DESTDIR)$(mandir)/man1" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1" ; fi + $(INSTALL_DATA) $(VPATH)/doc/$(progname).1 "$(DESTDIR)$(mandir)/man1/$(program_prefix)$(progname).1" + -rm -f "$(DESTDIR)$(mandir)/man1/$(program_prefix)r$(progname).1" + cd "$(DESTDIR)$(mandir)/man1" && ln -s "$(program_prefix)$(progname).1" "$(program_prefix)r$(progname).1" + +install-strip : all + $(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' install + +uninstall : uninstall-info uninstall-man + -rm -f "$(DESTDIR)$(bindir)/$(program_prefix)$(progname)" + -rm -f "$(DESTDIR)$(bindir)/$(program_prefix)r$(progname)" + +uninstall-info : + -install-info --info-dir="$(DESTDIR)$(infodir)" --remove "$(DESTDIR)$(infodir)/$(program_prefix)$(pkgname).info" + -rm -f "$(DESTDIR)$(infodir)/$(program_prefix)$(pkgname).info" + +uninstall-man : + -rm -f "$(DESTDIR)$(mandir)/man1/$(program_prefix)$(progname).1" + -rm -f "$(DESTDIR)$(mandir)/man1/$(program_prefix)r$(progname).1" + +dist : doc + ln -sf $(VPATH) $(DISTNAME) + tar -cvf $(DISTNAME).tar \ + $(DISTNAME)/AUTHORS \ + $(DISTNAME)/COPYING \ + $(DISTNAME)/ChangeLog \ + $(DISTNAME)/INSTALL \ + $(DISTNAME)/Makefile.in \ + $(DISTNAME)/NEWS \ + $(DISTNAME)/README \ + $(DISTNAME)/TODO \ + $(DISTNAME)/configure \ + $(DISTNAME)/doc/$(progname).1 \ + $(DISTNAME)/doc/$(pkgname).info \ + $(DISTNAME)/doc/$(pkgname).texinfo \ + $(DISTNAME)/doc/fdl.texinfo \ + $(DISTNAME)/r$(progname).in \ + $(DISTNAME)/testsuite/check.sh \ + $(DISTNAME)/testsuite/*.t \ + $(DISTNAME)/testsuite/*.d \ + $(DISTNAME)/testsuite/*.r \ + $(DISTNAME)/testsuite/*.pr \ + $(DISTNAME)/testsuite/*.err \ + $(DISTNAME)/*.h \ + $(DISTNAME)/*.c + rm -f $(DISTNAME) + lzip -v -9 $(DISTNAME).tar + +clean : + -rm -f $(progname) r$(progname) $(progname)_profiled $(objs) + +distclean : clean + -rm -f Makefile config.status *.tar *.tar.lz @@ -0,0 +1,20 @@ +Changes in version 1.6: + +Displaying of null characters by the "l" command has been fixed. + +The condition deciding when to show the message "Newline appended" has +been corrected. + +The "modified" flag is now set when reading a non-empty file into an +empty buffer. + +An error that prevented using NUL characters in regular expressions has +been fixed. + +Ed now signals an error if it can't create a shell process when +executing a shell command. + +Ed now flushes stdout/stderr before reading a new command. + +Man page is now generated with "help2man". All command-line options are +now documented in the man page. @@ -0,0 +1,145 @@ +GNU ed is a line-oriented text editor. It is used to create, display, +modify and otherwise manipulate text files, both interactively and via +shell scripts. A restricted version of ed, red, can only edit files in +the current directory and cannot execute shell commands. Ed is the +"standard" text editor in the sense that it is the original editor for +Unix, and thus widely available. For most purposes, however, it is +superseded by full-screen editors such as GNU Emacs or GNU Moe. + +Extensions to and deviations from the POSIX standard are described below. + +See the file INSTALL for compilation and installation instructions. + +Try "ed --help" for usage instructions. + +Report bugs to <bug-ed@gnu.org>. + +Ed home page: http://www.gnu.org/software/ed/ed.html + +For a description of the ed algorithm, see Kernighan and Plauger's book +"Software Tools in Pascal," Addison-Wesley, 1981. + + +GNU ed(1) is not strictly POSIX compliant, as described in the +POSIX 1003.1-2004 document. The following is a summary of omissions +and extensions to, and deviations from, the POSIX standard. + +OMISSIONS +--------- + * Locale(3) is not supported. + +EXTENSIONS +---------- + * Though GNU ed is not a stream editor, it can be used to edit binary files. + To assist in binary editing, when a file containing at least one ASCII + NUL character is written, a newline is not appended if it did not + already contain one upon reading. In particular, reading /dev/null + prior to writing prevents appending a newline to a binary file. + + For example, to create a file with GNU ed containing a single NUL character: + $ ed file + a + ^@ + . + r /dev/null + wq + + Similarly, to remove a newline from the end of binary `file': + $ ed file + r /dev/null + wq + + * BSD commands have been implemented wherever they do not conflict with + the POSIX standard. The BSD-ism's included are: + * `s' (i.e., s[n][rgp]*) to repeat a previous substitution, + * `W' for appending text to an existing file, + * `wq' for exiting after a write, and + * `z' for scrolling through the buffer. + + * The POSIX interactive global commands `G' and `V' are extended to + support multiple commands, including `a', `i' and `c'. The command + format is the same as for the global commands `g' and `v', i.e., one + command per line with each line, except for the last, ending in a + backslash (\). + + * The file commands `E', `e', `r', `W' and `w' process a <file> + argument for backslash escapes; i.e., any character preceded by a + backslash is interpreted literally. If the first unescaped character + of a <file> argument is a bang (!), then the rest of the line is + interpreted as a shell command, and no escape processing is performed + by GNU ed. + + * For SunOS ed(1) compatibility, GNU ed runs in restricted mode if invoked + as red. This limits editing of files in the local directory only and + prohibits shell commands. + +DEVIATIONS +---------- + * For backwards compatibility, the POSIX rule that says a range of + addresses cannot be used where only a single address is expected has + been relaxed. + + * To support the BSD `s' command (see EXTENSIONS above), + substitution patterns cannot be delimited by numbers or the characters + `r', `g' and `p'. In contrast, POSIX specifies any character expect + space or newline can used as a delimiter. + + * Since the behavior of `u' (undo) within a `g' (global) command list is + not specified by POSIX, GNU ed follows the behavior of the SunOS ed: + undo forces a global command list to be executed only once, rather than + for each line matching a global pattern. In addtion, each instance of + `u' within a global command undoes all previous commands (including + undo's) in the command list. This seems the best way, since the + alternatives are either too complicated to implement or too confusing + to use. + + * The `m' (move) command within a `g' command list also follows the SunOS + ed implementation: any moved lines are removed from the global command's + `active' list. + + * If GNU ed is invoked with a name argument prefixed by a bang (!), then + the remainder of the argument is interpreted as a shell command. To invoke + ed on a file whose name starts with bang, prefix the name with a + (quoted) backslash. + + * For backwards compatibility, errors in piped scripts do not force ed + to exit. POSIX only specifies ed's response for input via regular + files (including here documents) or tty's. + + +TESTSUITE +--------- +The files in the `testsuite' directory with suffixes `.t', `.d', `.r', +`.pr' and `.err' are used for testing ed. To run the tests, configure +the package and type `make check' from the build directory. The tests do +not exhaustively verify POSIX compliance nor do they verify correct +8-bit or long line support. + +The test file suffixes have the following meanings: +.t Template - a list of ed commands from which an ed script is + constructed +.d Data - read by an ed script +.r Result - the expected output after processing data via an ed + script. +.pr Result from a piped ed script. +.err Error - invalid ed commands that should generate an error + +The output of the tests is written to the files errs.ck, pipes.ck and +scripts.ck. At the end of the tests, these files are grep'ed for error +messages, which look like: + + *** The script u.ed exited abnormally *** +or: + *** Output u.o of script u.ed is incorrect *** + + +Copyright (C) 1993, 1994 Andrew Moore +Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 +Free Software Foundation, Inc. + +This file is free documentation: you have unlimited permission to copy, +distribute and modify it. + +The file Makefile.in is a data file used by configure to produce the +Makefile. It has the same copyright owner and permissions that configure +itself. @@ -0,0 +1,15 @@ +Some missing tests: +0) g/./s^@^@ - okay: NULs in commands +1) g/./s/^@/ - okay: NULs in patterns +2) a + hello^V^Jworld + . - okay: embedded newlines in insert mode +3) ed "" - error: invalid filename +4) red .. - error: restricted +5) red / - error: restricted +5) red !xx - error: restricted +6) ed -x - verify: 8-bit clean +7) ed - verify: long-line support +8) ed - verify: interactive/help mode +9) G/pat/ - verify: global interactive command +10) V/pat/ - verify: global interactive command diff --git a/buffer.c b/buffer.c new file mode 100644 index 0000000..8b4784f --- /dev/null +++ b/buffer.c @@ -0,0 +1,632 @@ +/* buffer.c: scratch-file buffer routines for the ed line editor. */ +/* GNU ed - The GNU line editor. + Copyright (C) 1993, 1994 Andrew Moore, Talke Studio + Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 + Free Software Foundation, Inc. + + 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/>. +*/ + +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/file.h> +#include <sys/stat.h> + +#include "ed.h" + + +static int current_addr_ = 0; /* current address in editor buffer */ +static int last_addr_ = 0; /* last address in editor buffer */ +static bool isbinary_ = false; /* if set, buffer contains ASCII NULs */ +static bool modified_ = false; /* if set, buffer modified since last write */ +static bool newline_added_ = false; /* if set, newline appended to input file */ + +static bool seek_write = false; /* seek before writing */ +static FILE * sfp = 0; /* scratch file pointer */ +static long sfpos = 0; /* scratch file position */ +static line_t buffer_head; /* editor buffer ( linked list of line_t )*/ +static line_t yank_buffer_head; + + +int current_addr( void ) { return current_addr_; } +int inc_current_addr( void ) + { if( ++current_addr_ > last_addr_ ) current_addr_ = last_addr_; + return current_addr_; } +void set_current_addr( const int addr ) { current_addr_ = addr; } + +int last_addr( void ) { return last_addr_; } + +bool isbinary( void ) { return isbinary_; } +void set_binary( void ) { isbinary_ = true; } + +bool modified( void ) { return modified_; } +void set_modified( const bool m ) { modified_ = m; } + +bool newline_added( void ) { return newline_added_; } +void set_newline_added( void ) { newline_added_ = true; } + + +int inc_addr( int addr ) + { if( ++addr > last_addr_ ) addr = 0; return addr; } + +int dec_addr( int addr ) + { if( --addr < 0 ) addr = last_addr_; return addr; } + + +/* link next and previous nodes */ +static void link_nodes( line_t * const prev, line_t * const next ) + { prev->q_forw = next; next->q_back = prev; } + + +/* insert line node into circular queue after previous */ +static void insert_node( line_t * const lp, line_t * const prev ) + { + link_nodes( lp, prev->q_forw ); + link_nodes( prev, lp ); + } + + +/* add a line node in the editor buffer after the given line */ +static void add_line_node( line_t * const lp, const int addr ) + { + line_t * const prev = search_line_node( addr ); + insert_node( lp, prev ); + ++last_addr_; + } + + +/* return a pointer to a copy of a line node, or to a new node if lp == 0 */ +static line_t * dup_line_node( line_t * const lp ) + { + line_t * const p = (line_t *) malloc( sizeof (line_t) ); + if( !p ) + { + show_strerror( 0, errno ); + set_error_msg( "Memory exhausted" ); + return 0; + } + if( lp ) { p->pos = lp->pos; p->len = lp->len; } + return p; + } + + +/* Insert text from stdin (or from command buffer if global) to after + line n; stop when either a single period is read or EOF. + Return false if insertion fails. */ +bool append_lines( const char ** const ibufpp, const int addr, + const bool isglobal ) + { + int size = 0; + undo_t * up = 0; + current_addr_ = addr; + + while( true ) + { + if( !isglobal ) + { + *ibufpp = get_tty_line( &size ); + if( !*ibufpp ) return false; + if( size == 0 || (*ibufpp)[size-1] != '\n' ) + { clearerr( stdin ); return ( size == 0 ); } + } + else + { + if( !**ibufpp ) return true; + for( size = 0; (*ibufpp)[size++] != '\n'; ) ; + } + if( size == 2 && **ibufpp == '.' ) { *ibufpp += size; return true; } + disable_interrupts(); + if( !put_sbuf_line( *ibufpp, size, current_addr_ ) ) + { enable_interrupts(); return false; } + if( up ) up->tail = search_line_node( current_addr_ ); + else + { + up = push_undo_atom( UADD, current_addr_, current_addr_ ); + if( !up ) { enable_interrupts(); return false; } + } + *ibufpp += size; + modified_ = true; + enable_interrupts(); + } + } + + +static void clear_yank_buffer( void ) + { + line_t * lp = yank_buffer_head.q_forw; + + disable_interrupts(); + while( lp != &yank_buffer_head ) + { + line_t * const p = lp->q_forw; + link_nodes( lp->q_back, lp->q_forw ); + free( lp ); + lp = p; + } + enable_interrupts(); + } + + +/* close scratch file */ +bool close_sbuf( void ) + { + clear_yank_buffer(); + clear_undo_stack(); + if( sfp ) + { + if( fclose( sfp ) != 0 ) + { + show_strerror( 0, errno ); + set_error_msg( "Cannot close temp file" ); + return false; + } + sfp = 0; + } + sfpos = 0; + seek_write = false; + return true; + } + + +/* copy a range of lines; return false if error */ +bool copy_lines( const int first_addr, const int second_addr, const int addr ) + { + line_t *lp, *np = search_line_node( first_addr ); + undo_t * up = 0; + int n = second_addr - first_addr + 1; + int m = 0; + + current_addr_ = addr; + if( addr >= first_addr && addr < second_addr ) + { + n = addr - first_addr + 1; + m = second_addr - addr; + } + for( ; n > 0; n = m, m = 0, np = search_line_node( current_addr_ + 1 ) ) + for( ; n-- > 0; np = np->q_forw ) + { + disable_interrupts(); + lp = dup_line_node( np ); + if( !lp ) { enable_interrupts(); return false; } + add_line_node( lp, current_addr_++ ); + if( up ) up->tail = lp; + else + { + up = push_undo_atom( UADD, current_addr_, current_addr_ ); + if( !up ) { enable_interrupts(); return false; } + } + modified_ = true; + enable_interrupts(); + } + return true; + } + + +/* delete a range of lines */ +bool delete_lines( const int from, const int to, const bool isglobal ) + { + line_t *n, *p; + + if( !yank_lines( from, to ) ) return false; + disable_interrupts(); + if( !push_undo_atom( UDEL, from, to ) ) + { enable_interrupts(); return false; } + n = search_line_node( inc_addr( to ) ); + p = search_line_node( from - 1 ); /* this search_line_node last! */ + if( isglobal ) unset_active_nodes( p->q_forw, n ); + link_nodes( p, n ); + last_addr_ -= to - from + 1; + current_addr_ = from - 1; + modified_ = true; + enable_interrupts(); + return true; + } + + +/* return line number of pointer */ +int get_line_node_addr( const line_t * const lp ) + { + const line_t * p = &buffer_head; + int addr = 0; + + while( p != lp && ( p = p->q_forw ) != &buffer_head ) ++addr; + if( addr && p == &buffer_head ) + { set_error_msg( "Invalid address" ); return -1; } + return addr; + } + + +/* get a line of text from the scratch file; return pointer to the text */ +char * get_sbuf_line( const line_t * const lp ) + { + static char * buf = 0; + static int bufsz = 0; + int len; + + if( lp == &buffer_head ) return 0; + seek_write = true; /* force seek on write */ + /* out of position */ + if( sfpos != lp->pos ) + { + sfpos = lp->pos; + if( fseek( sfp, sfpos, SEEK_SET ) != 0 ) + { + show_strerror( 0, errno ); + set_error_msg( "Cannot seek temp file" ); + return 0; + } + } + len = lp->len; + if( !resize_buffer( &buf, &bufsz, len + 1 ) ) return 0; + if( (int)fread( buf, 1, len, sfp ) != len ) + { + show_strerror( 0, errno ); + set_error_msg( "Cannot read temp file" ); + return 0; + } + sfpos += len; /* update file position */ + buf[len] = 0; + return buf; + } + + +/* open scratch buffer; initialize line queue */ +bool init_buffers( void ) + { + /* Read stdin one character at a time to avoid i/o contention + with shell escapes invoked by nonterminal input, e.g., + ed - <<EOF + !cat + hello, world + EOF */ + setvbuf( stdin, 0, _IONBF, 0 ); + if( !open_sbuf() ) return false; + link_nodes( &buffer_head, &buffer_head ); + link_nodes( &yank_buffer_head, &yank_buffer_head ); + return true; + } + + +/* replace a range of lines with the joined text of those lines */ +bool join_lines( const int from, const int to, const bool isglobal ) + { + static char * buf = 0; + static int bufsz = 0; + int size = 0; + line_t * const ep = search_line_node( inc_addr( to ) ); + line_t * bp = search_line_node( from ); + + while( bp != ep ) + { + const char * const s = get_sbuf_line( bp ); + if( !s || !resize_buffer( &buf, &bufsz, size + bp->len ) ) return false; + memcpy( buf + size, s, bp->len ); + size += bp->len; + bp = bp->q_forw; + } + if( !resize_buffer( &buf, &bufsz, size + 2 ) ) return false; + memcpy( buf + size, "\n", 2 ); + size += 2; + if( !delete_lines( from, to, isglobal ) ) return false; + current_addr_ = from - 1; + disable_interrupts(); + if( !put_sbuf_line( buf, size, current_addr_ ) || + !push_undo_atom( UADD, current_addr_, current_addr_ ) ) + { enable_interrupts(); return false; } + modified_ = true; + enable_interrupts(); + return true; + } + + +/* move a range of lines */ +bool move_lines( const int first_addr, const int second_addr, const int addr, + const bool isglobal ) + { + line_t *b1, *a1, *b2, *a2; + int n = inc_addr( second_addr ); + int p = first_addr - 1; + + disable_interrupts(); + if( addr == first_addr - 1 || addr == second_addr ) + { + a2 = search_line_node( n ); + b2 = search_line_node( p ); + current_addr_ = second_addr; + } + else if( !push_undo_atom( UMOV, p, n ) || + !push_undo_atom( UMOV, addr, inc_addr( addr ) ) ) + { enable_interrupts(); return false; } + else + { + a1 = search_line_node( n ); + if( addr < first_addr ) + { + b1 = search_line_node( p ); + b2 = search_line_node( addr ); /* this search_line_node last! */ + } + else + { + b2 = search_line_node( addr ); + b1 = search_line_node( p ); /* this search_line_node last! */ + } + a2 = b2->q_forw; + link_nodes( b2, b1->q_forw ); + link_nodes( a1->q_back, a2 ); + link_nodes( b1, a1 ); + current_addr_ = addr + ( ( addr < first_addr ) ? + second_addr - first_addr + 1 : 0 ); + } + if( isglobal ) unset_active_nodes( b2->q_forw, a2 ); + modified_ = true; + enable_interrupts(); + return true; + } + + +/* open scratch file */ +bool open_sbuf( void ) + { + isbinary_ = newline_added_ = false; + sfp = tmpfile(); + if( !sfp ) + { + show_strerror( 0, errno ); + set_error_msg( "Cannot open temp file" ); + return false; + } + return true; + } + + +int path_max( const char * filename ) + { + long result; + if( !filename ) filename = "/"; + errno = 0; + result = pathconf( filename, _PC_PATH_MAX ); + if( result < 0 ) { if( errno ) result = 256; else result = 1024; } + else if( result < 256 ) result = 256; + return result; + } + + +/* append lines from the yank buffer */ +bool put_lines( const int addr ) + { + undo_t * up = 0; + line_t *p, *lp = yank_buffer_head.q_forw; + + if( lp == &yank_buffer_head ) + { set_error_msg( "Nothing to put" ); return false; } + current_addr_ = addr; + while( lp != &yank_buffer_head ) + { + disable_interrupts(); + p = dup_line_node( lp ); + if( !p ) { enable_interrupts(); return false; } + add_line_node( p, current_addr_++ ); + if( up ) up->tail = p; + else + { + up = push_undo_atom( UADD, current_addr_, current_addr_ ); + if( !up ) { enable_interrupts(); return false; } + } + modified_ = true; + lp = lp->q_forw; + enable_interrupts(); + } + return true; + } + + +/* write a line of text to the scratch file and add a line node to the + editor buffer; return a pointer to the end of the text */ +const char * put_sbuf_line( const char * const buf, const int size, + const int addr ) + { + line_t * const lp = dup_line_node( 0 ); + const char * const p = (const char *) memchr( buf, '\n', size ); + int len; + + if( !lp ) return 0; + if( !p ) { set_error_msg( "Line too long" ); return 0; } + len = p - buf; + /* out of position */ + if( seek_write ) + { + if( fseek( sfp, 0L, SEEK_END ) != 0 ) + { + show_strerror( 0, errno ); + set_error_msg( "Cannot seek temp file" ); + return 0; + } + sfpos = ftell( sfp ); + seek_write = false; + } + if( (int)fwrite( buf, 1, len, sfp ) != len ) /* assert: interrupts disabled */ + { + sfpos = -1; + show_strerror( 0, errno ); + set_error_msg( "Cannot write temp file" ); + return 0; + } + lp->pos = sfpos; lp->len = len; + add_line_node( lp, addr ); + ++current_addr_; + sfpos += len; /* update file position */ + return p + 1; + } + + +/* return pointer to a line node in the editor buffer */ +line_t * search_line_node( const int addr ) + { + static line_t * lp = &buffer_head; + static int o_addr = 0; + + disable_interrupts(); + if( o_addr < addr ) + { + if( o_addr + last_addr_ >= 2 * addr ) + while( o_addr < addr ) { ++o_addr; lp = lp->q_forw; } + else + { + lp = buffer_head.q_back; o_addr = last_addr_; + while( o_addr > addr ) { --o_addr; lp = lp->q_back; } + } + } + else if( o_addr <= 2 * addr ) + while( o_addr > addr ) { --o_addr; lp = lp->q_back; } + else + { lp = &buffer_head; o_addr = 0; + while( o_addr < addr ) { ++o_addr; lp = lp->q_forw; } } + enable_interrupts(); + return lp; + } + + +/* copy a range of lines to the cut buffer */ +bool yank_lines( const int from, const int to ) + { + line_t * const ep = search_line_node( inc_addr( to ) ); + line_t * bp = search_line_node( from ); + line_t * lp = &yank_buffer_head; + line_t * p; + + clear_yank_buffer(); + while( bp != ep ) + { + disable_interrupts(); + p = dup_line_node( bp ); + if( !p ) { enable_interrupts(); return false; } + insert_node( p, lp ); + bp = bp->q_forw; lp = p; + enable_interrupts(); + } + return true; + } + + +static undo_t * ustack = 0; /* undo stack */ +static int usize = 0; /* ustack size (in bytes) */ +static int u_ptr = 0; /* undo stack pointer */ +static int u_current_addr = -1; /* if < 0, undo disabled */ +static int u_last_addr = -1; /* if < 0, undo disabled */ +static bool u_modified = false; + + +void clear_undo_stack( void ) + { + while( u_ptr-- ) + if( ustack[u_ptr].type == UDEL ) + { + line_t * const ep = ustack[u_ptr].tail->q_forw; + line_t * bp = ustack[u_ptr].head; + while( bp != ep ) + { + line_t * const lp = bp->q_forw; + unmark_line_node( bp ); + free( bp ); + bp = lp; + } + } + u_ptr = 0; + u_current_addr = current_addr_; + u_last_addr = last_addr_; + u_modified = modified_; + } + + +void reset_undo_state( void ) + { + clear_undo_stack(); + u_current_addr = u_last_addr = -1; + u_modified = false; + } + + +/* return pointer to intialized undo node */ +undo_t * push_undo_atom( const int type, const int from, const int to ) + { + disable_interrupts(); + if( !resize_undo_buffer( &ustack, &usize, ( u_ptr + 1 ) * sizeof (undo_t) ) ) + { + show_strerror( 0, errno ); + set_error_msg( "Memory exhausted" ); + if( ustack ) + { + clear_undo_stack(); + free( ustack ); + ustack = 0; + usize = u_ptr = 0; + u_current_addr = u_last_addr = -1; + } + enable_interrupts(); + return 0; + } + enable_interrupts(); + ustack[u_ptr].type = type; + ustack[u_ptr].tail = search_line_node( to ); + ustack[u_ptr].head = search_line_node( from ); + return ustack + u_ptr++; + } + + +/* undo last change to the editor buffer */ +bool undo( const bool isglobal ) + { + int n; + const int o_current_addr = current_addr_; + const int o_last_addr = last_addr_; + const bool o_modified = modified_; + + if( u_ptr <= 0 || u_current_addr < 0 || u_last_addr < 0 ) + { set_error_msg( "Nothing to undo" ); return false; } + search_line_node( 0 ); /* reset cached value */ + disable_interrupts(); + for( n = u_ptr - 1; n >= 0; --n ) + { + switch( ustack[n].type ) + { + case UADD: link_nodes( ustack[n].head->q_back, ustack[n].tail->q_forw ); + break; + case UDEL: link_nodes( ustack[n].head->q_back, ustack[n].head ); + link_nodes( ustack[n].tail, ustack[n].tail->q_forw ); + break; + case UMOV: + case VMOV: link_nodes( ustack[n-1].head, ustack[n].head->q_forw ); + link_nodes( ustack[n].tail->q_back, ustack[n-1].tail ); + link_nodes( ustack[n].head, ustack[n].tail ); --n; + break; + } + ustack[n].type ^= 1; + } + /* reverse undo stack order */ + for( n = 0; 2 * n < u_ptr - 1; ++n ) + { + undo_t tmp = ustack[n]; + ustack[n] = ustack[u_ptr-1-n]; ustack[u_ptr-1-n] = tmp; + } + if( isglobal ) clear_active_list(); + current_addr_ = u_current_addr; u_current_addr = o_current_addr; + last_addr_ = u_last_addr; u_last_addr = o_last_addr; + modified_ = u_modified; u_modified = o_modified; + enable_interrupts(); + return true; + } diff --git a/carg_parser.c b/carg_parser.c new file mode 100644 index 0000000..fa3be26 --- /dev/null +++ b/carg_parser.c @@ -0,0 +1,294 @@ +/* Arg_parser - POSIX/GNU command line argument parser. (C version) + Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 + Antonio Diaz Diaz. + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. + + As a special exception, you may use this file as part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this + file, or you compile this file and link it with other files to + produce an executable, this file does not by itself cause the + resulting executable to be covered by the GNU General Public + License. This exception does not however invalidate any other + reasons why the executable file might be covered by the GNU General + Public License. +*/ + +#include <stdlib.h> +#include <string.h> + +#include "carg_parser.h" + + +/* assure at least a minimum size for buffer `buf' */ +static void * ap_resize_buffer( void * buf, const int min_size ) + { + if( buf ) buf = realloc( buf, min_size ); + else buf = malloc( min_size ); + return buf; + } + + +static char push_back_record( struct Arg_parser * const ap, + const int code, const char * const argument ) + { + const int len = strlen( argument ); + struct ap_Record *p; + void * tmp = ap_resize_buffer( ap->data, + ( ap->data_size + 1 ) * sizeof (struct ap_Record) ); + if( !tmp ) return 0; + ap->data = (struct ap_Record *)tmp; + p = &(ap->data[ap->data_size]); + p->code = code; + p->argument = 0; + tmp = ap_resize_buffer( p->argument, len + 1 ); + if( !tmp ) return 0; + p->argument = (char *)tmp; + strncpy( p->argument, argument, len + 1 ); + ++ap->data_size; + return 1; + } + + +static char add_error( struct Arg_parser * const ap, const char * const msg ) + { + const int len = strlen( msg ); + void * tmp = ap_resize_buffer( ap->error, ap->error_size + len + 1 ); + if( !tmp ) return 0; + ap->error = (char *)tmp; + strncpy( ap->error + ap->error_size, msg, len + 1 ); + ap->error_size += len; + return 1; + } + + +static void free_data( struct Arg_parser * const ap ) + { + int i; + for( i = 0; i < ap->data_size; ++i ) free( ap->data[i].argument ); + if( ap->data ) { free( ap->data ); ap->data = 0; } + ap->data_size = 0; + } + + +static char parse_long_option( struct Arg_parser * const ap, + const char * const opt, const char * const arg, + const struct ap_Option options[], + int * const argindp ) + { + unsigned int len; + int index = -1; + int i; + char exact = 0, ambig = 0; + + for( len = 0; opt[len+2] && opt[len+2] != '='; ++len ) ; + + /* Test all long options for either exact match or abbreviated matches. */ + for( i = 0; options[i].code != 0; ++i ) + if( options[i].name && !strncmp( options[i].name, &opt[2], len ) ) + { + if( strlen( options[i].name ) == len ) /* Exact match found */ + { index = i; exact = 1; break; } + else if( index < 0 ) index = i; /* First nonexact match found */ + else if( options[index].code != options[i].code || + options[index].has_arg != options[i].has_arg ) + ambig = 1; /* Second or later nonexact match found */ + } + + if( ambig && !exact ) + { + add_error( ap, "option `" ); add_error( ap, opt ); + add_error( ap, "' is ambiguous" ); + return 1; + } + + if( index < 0 ) /* nothing found */ + { + add_error( ap, "unrecognized option `" ); add_error( ap, opt ); + add_error( ap, "'" ); + return 1; + } + + ++*argindp; + + if( opt[len+2] ) /* `--<long_option>=<argument>' syntax */ + { + if( options[index].has_arg == ap_no ) + { + add_error( ap, "option `--" ); add_error( ap, options[index].name ); + add_error( ap, "' doesn't allow an argument" ); + return 1; + } + if( options[index].has_arg == ap_yes && !opt[len+3] ) + { + add_error( ap, "option `--" ); add_error( ap, options[index].name ); + add_error( ap, "' requires an argument" ); + return 1; + } + return push_back_record( ap, options[index].code, &opt[len+3] ); + } + + if( options[index].has_arg == ap_yes ) + { + if( !arg || !arg[0] ) + { + add_error( ap, "option `--" ); add_error( ap, options[index].name ); + add_error( ap, "' requires an argument" ); + return 1; + } + ++*argindp; + return push_back_record( ap, options[index].code, arg ); + } + + return push_back_record( ap, options[index].code, "" ); + } + + +static char parse_short_option( struct Arg_parser * const ap, + const char * const opt, const char * const arg, + const struct ap_Option options[], + int * const argindp ) + { + int cind = 1; /* character index in opt */ + + while( cind > 0 ) + { + int index = -1; + int i; + const unsigned char code = opt[cind]; + char code_str[2]; + code_str[0] = code; code_str[1] = 0; + + if( code != 0 ) + for( i = 0; options[i].code; ++i ) + if( code == options[i].code ) + { index = i; break; } + + if( index < 0 ) + { + add_error( ap, "invalid option -- " ); add_error( ap, code_str ); + return 1; + } + + if( opt[++cind] == 0 ) { ++*argindp; cind = 0; } /* opt finished */ + + if( options[index].has_arg != ap_no && cind > 0 && opt[cind] ) + { + if( !push_back_record( ap, code, &opt[cind] ) ) return 0; + ++*argindp; cind = 0; + } + else if( options[index].has_arg == ap_yes ) + { + if( !arg || !arg[0] ) + { + add_error( ap, "option requires an argument -- " ); + add_error( ap, code_str ); + return 1; + } + ++*argindp; cind = 0; + if( !push_back_record( ap, code, arg ) ) return 0; + } + else if( !push_back_record( ap, code, "" ) ) return 0; + } + return 1; + } + + +char ap_init( struct Arg_parser * const ap, + const int argc, const char * const argv[], + const struct ap_Option options[], const char in_order ) + { + const char ** non_options = 0; /* skipped non-options */ + int non_options_size = 0; /* number of skipped non-options */ + int argind = 1; /* index in argv */ + int i; + + ap->data = 0; + ap->error = 0; + ap->data_size = 0; + ap->error_size = 0; + if( argc < 2 || !argv || !options ) return 1; + + while( argind < argc ) + { + const unsigned char ch1 = argv[argind][0]; + const unsigned char ch2 = ( ch1 ? argv[argind][1] : 0 ); + + if( ch1 == '-' && ch2 ) /* we found an option */ + { + const char * const opt = argv[argind]; + const char * const arg = (argind + 1 < argc) ? argv[argind+1] : 0; + if( ch2 == '-' ) + { + if( !argv[argind][2] ) { ++argind; break; } /* we found "--" */ + else if( !parse_long_option( ap, opt, arg, options, &argind ) ) return 0; + } + else if( !parse_short_option( ap, opt, arg, options, &argind ) ) return 0; + if( ap->error ) break; + } + else + { + if( !in_order ) + { + void * tmp = ap_resize_buffer( non_options, + ( non_options_size + 1 ) * sizeof *non_options ); + if( !tmp ) return 0; + non_options = (const char **)tmp; + non_options[non_options_size++] = argv[argind++]; + } + else if( !push_back_record( ap, 0, argv[argind++] ) ) return 0; + } + } + if( ap->error ) free_data( ap ); + else + { + for( i = 0; i < non_options_size; ++i ) + if( !push_back_record( ap, 0, non_options[i] ) ) return 0; + while( argind < argc ) + if( !push_back_record( ap, 0, argv[argind++] ) ) return 0; + } + if( non_options ) free( non_options ); + return 1; + } + + +void ap_free( struct Arg_parser * const ap ) + { + free_data( ap ); + if( ap->error ) { free( ap->error ); ap->error = 0; } + ap->error_size = 0; + } + + +const char * ap_error( const struct Arg_parser * const ap ) + { return ap->error; } + + +int ap_arguments( const struct Arg_parser * const ap ) + { return ap->data_size; } + + +int ap_code( const struct Arg_parser * const ap, const int i ) + { + if( i >= 0 && i < ap_arguments( ap ) ) return ap->data[i].code; + else return 0; + } + + +const char * ap_argument( const struct Arg_parser * const ap, const int i ) + { + if( i >= 0 && i < ap_arguments( ap ) ) return ap->data[i].argument; + else return ""; + } diff --git a/carg_parser.h b/carg_parser.h new file mode 100644 index 0000000..fdff4d5 --- /dev/null +++ b/carg_parser.h @@ -0,0 +1,102 @@ +/* Arg_parser - POSIX/GNU command line argument parser. (C version) + Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 + Antonio Diaz Diaz. + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. + + As a special exception, you may use this file as part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this + file, or you compile this file and link it with other files to + produce an executable, this file does not by itself cause the + resulting executable to be covered by the GNU General Public + License. This exception does not however invalidate any other + reasons why the executable file might be covered by the GNU General + Public License. +*/ + +/* Arg_parser reads the arguments in `argv' and creates a number of + option codes, option arguments and non-option arguments. + + In case of error, `ap_error' returns a non-null pointer to an error + message. + + `options' is an array of `struct ap_Option' terminated by an element + containing a code which is zero. A null name means a short-only + option. A code value outside the unsigned char range means a + long-only option. + + Arg_parser normally makes it appear as if all the option arguments + were specified before all the non-option arguments for the purposes + of parsing, even if the user of your program intermixed option and + non-option arguments. If you want the arguments in the exact order + the user typed them, call `ap_init' with `in_order' = true. + + The argument `--' terminates all options; any following arguments are + treated as non-option arguments, even if they begin with a hyphen. + + The syntax for optional option arguments is `-<short_option><argument>' + (without whitespace), or `--<long_option>=<argument>'. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +enum ap_Has_arg { ap_no, ap_yes, ap_maybe }; + +struct ap_Option + { + int code; /* Short option letter or code ( code != 0 ) */ + const char * name; /* Long option name (maybe null) */ + enum ap_Has_arg has_arg; + }; + + +struct ap_Record + { + int code; + char * argument; + }; + + +struct Arg_parser + { + struct ap_Record * data; + char * error; + int data_size; + int error_size; + }; + + +char ap_init( struct Arg_parser * const ap, + const int argc, const char * const argv[], + const struct ap_Option options[], const char in_order ); + +void ap_free( struct Arg_parser * const ap ); + +const char * ap_error( const struct Arg_parser * const ap ); + + /* The number of arguments parsed (may be different from argc) */ +int ap_arguments( const struct Arg_parser * const ap ); + + /* If ap_code( i ) is 0, ap_argument( i ) is a non-option. + Else ap_argument( i ) is the option's argument (or empty). */ +int ap_code( const struct Arg_parser * const ap, const int i ); + +const char * ap_argument( const struct Arg_parser * const ap, const int i ); + +#ifdef __cplusplus +} +#endif diff --git a/configure b/configure new file mode 100755 index 0000000..d01a036 --- /dev/null +++ b/configure @@ -0,0 +1,200 @@ +#! /bin/sh +# configure script for GNU ed - The GNU line editor +# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 +# Antonio Diaz Diaz. +# +# This configure script is free software: you have unlimited permission +# to copy, distribute and modify it. + +args= +no_create= +pkgname=ed +pkgversion=1.6 +progname=ed +srctrigger=ed.h + +# clear some things potentially inherited from environment. +LC_ALL=C +export LC_ALL +srcdir= +prefix=/usr/local +exec_prefix='$(prefix)' +bindir='$(exec_prefix)/bin' +datadir='$(prefix)/share' +infodir='$(datadir)/info' +mandir='$(datadir)/man' +sysconfdir='$(prefix)/etc' +program_prefix= +CC= +CPPFLAGS= +CFLAGS='-Wall -W -O2' +LDFLAGS= + +# Loop over all args +while [ -n "$1" ] ; do + + # Get the first arg, and shuffle + option=$1 + shift + + # Add the argument quoted to args + args="${args} \"${option}\"" + + # Split out the argument for options that take them + case ${option} in + *=*) optarg=`echo ${option} | sed -e 's,^[^=]*=,,'` ;; + esac + + # Process the options + case ${option} in + --help | --he* | -h) + echo "Usage: configure [options]" + echo + echo "Options: [defaults in brackets]" + echo " -h, --help display this help and exit" + echo " -V, --version output version information and exit" + echo " --srcdir=DIR find the sources in DIR [. or ..]" + echo " --prefix=DIR install into DIR [${prefix}]" + echo " --exec-prefix=DIR base directory for arch-dependent files [${exec_prefix}]" + echo " --bindir=DIR user executables directory [${bindir}]" + echo " --datadir=DIR base directory for doc and data [${datadir}]" + echo " --infodir=DIR info files directory [${infodir}]" + echo " --mandir=DIR man pages directory [${mandir}]" + echo " --sysconfdir=DIR read-only single-machine data directory [${sysconfdir}]" + echo " --program-prefix=NAME install program and documentation prefixed with NAME" + echo " CC=COMPILER C compiler to use [gcc]" + echo " CPPFLAGS=OPTIONS command line options for the preprocessor [${CPPFLAGS}]" + echo " CFLAGS=OPTIONS command line options for the C compiler [${CFLAGS}]" + echo " LDFLAGS=OPTIONS command line options for the linker [${LDFLAGS}]" + echo + exit 0 ;; + --version | --ve* | -V) + echo "Configure script for GNU ${pkgname} version ${pkgversion}" + exit 0 ;; + --srcdir* | --sr*) + srcdir=`echo ${optarg} | sed -e 's,/$,,'` ;; + --prefix* | --pre*) + prefix=`echo ${optarg} | sed -e 's,/$,,'` ;; + --exec-prefix* | --ex*) + exec_prefix=`echo ${optarg} | sed -e 's,/$,,'` ;; + --bindir* | --bi*) + bindir=`echo ${optarg} | sed -e 's,/$,,'` ;; + --datadir* | --da*) + datadir=`echo ${optarg} | sed -e 's,/$,,'` ;; + --infodir* | --inf*) + infodir=`echo ${optarg} | sed -e 's,/$,,'` ;; + --mandir* | --ma*) + mandir=`echo ${optarg} | sed -e 's,/$,,'` ;; + --sysconfdir* | --sy*) + sysconfdir=`echo ${optarg} | sed -e 's,/$,,'` ;; + --program-prefix* | --pro*) + program_prefix=`echo ${optarg} | sed -e 's,/$,,'` ;; + --no-create | --no-c*) + no_create=yes ;; + + CC=*) CC=${optarg} ;; + CPPFLAGS=*) CPPFLAGS=${optarg} ;; + CFLAGS=*) CFLAGS=${optarg} ;; + LDFLAGS=*) LDFLAGS=${optarg} ;; + + --* | *=* | *-*-*) ;; + *) + echo "configure: Unrecognized option: \"${option}\"; use --help for usage." 1>&2 + exit 1 ;; + esac +done + +# Find the source files, if location was not specified. +srcdirtext= +if [ -z "${srcdir}" ] ; then + srcdirtext="or . or .." ; srcdir=. + if [ ! -r ${srcdir}/${srctrigger} ] ; then srcdir=.. ; fi + if [ ! -r ${srcdir}/${srctrigger} ] ; then + ## the sed command below emulates the dirname command + srcdir=`echo $0 | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + fi +fi + +if [ ! -r ${srcdir}/${srctrigger} ] ; then + exec 1>&2 + echo + echo "configure: Can't find sources in ${srcdir} ${srcdirtext}" + echo "configure: (At least ${srctrigger} is missing)." + exit 1 +fi + +# Set srcdir to . if that's what it is. +if [ "`pwd`" = "`cd ${srcdir} ; pwd`" ] ; then srcdir=. ; fi + +# checking whether we are using GNU C. +if [ -z "${CC}" ] ; then # Let the user override the test. + if [ -x /bin/gcc ] || + [ -x /usr/bin/gcc ] || + [ -x /usr/local/bin/gcc ] ; then + CC="gcc" + else + CC="cc" + fi +fi + +echo +if [ -z "${no_create}" ] ; then + echo "creating config.status" + rm -f config.status + cat > config.status << EOF +#! /bin/sh +# This file was generated automatically by configure. Do not edit. +# Run this file to recreate the current configuration. +# +# This script is free software: you have unlimited permission +# to copy, distribute and modify it. + +exec /bin/sh $0 ${args} --no-create +EOF + chmod +x config.status +fi + +echo "creating Makefile" +echo "VPATH = ${srcdir}" +echo "prefix = ${prefix}" +echo "exec_prefix = ${exec_prefix}" +echo "bindir = ${bindir}" +echo "datadir = ${datadir}" +echo "infodir = ${infodir}" +echo "mandir = ${mandir}" +echo "sysconfdir = ${sysconfdir}" +echo "program_prefix = ${program_prefix}" +echo "CC = ${CC}" +echo "CPPFLAGS = ${CPPFLAGS}" +echo "CFLAGS = ${CFLAGS}" +echo "LDFLAGS = ${LDFLAGS}" +rm -f Makefile +cat > Makefile << EOF +# Makefile for GNU ed - The GNU line editor +# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 +# Antonio Diaz Diaz. +# This file was generated automatically by configure. Do not edit. +# +# This Makefile is free software: you have unlimited permission +# to copy, distribute and modify it. + +pkgname = ${pkgname} +pkgversion = ${pkgversion} +progname = ${progname} +VPATH = ${srcdir} +prefix = ${prefix} +exec_prefix = ${exec_prefix} +bindir = ${bindir} +datadir = ${datadir} +infodir = ${infodir} +mandir = ${mandir} +sysconfdir = ${sysconfdir} +program_prefix = ${program_prefix} +CC = ${CC} +CPPFLAGS = ${CPPFLAGS} +CFLAGS = ${CFLAGS} +LDFLAGS = ${LDFLAGS} +EOF +cat ${srcdir}/Makefile.in >> Makefile + +echo "OK. Now you can run make." diff --git a/doc/ed.1 b/doc/ed.1 new file mode 100644 index 0000000..2a0708a --- /dev/null +++ b/doc/ed.1 @@ -0,0 +1,63 @@ +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1. +.TH ED "1" "January 2012" "Ed 1.6" "User Commands" +.SH NAME +Ed \- line-oriented text editor +.SH SYNOPSIS +.B ed +[\fIoptions\fR] [\fIfile\fR] +.SH DESCRIPTION +GNU Ed \- The GNU line editor. +.SH OPTIONS +.TP +\fB\-h\fR, \fB\-\-help\fR +display this help and exit +.TP +\fB\-V\fR, \fB\-\-version\fR +output version information and exit +.TP +\fB\-G\fR, \fB\-\-traditional\fR +run in compatibility mode +.TP +\fB\-l\fR, \fB\-\-loose\-exit\-status\fR +exit with 0 status even if a command fails +.TP +\fB\-p\fR, \fB\-\-prompt\fR=\fISTRING\fR +use STRING as an interactive prompt +.TP +\fB\-r\fR, \fB\-\-restricted\fR +run in restricted mode +.TP +\fB\-s\fR, \fB\-\-quiet\fR, \fB\-\-silent\fR +suppress diagnostics +.TP +\fB\-v\fR, \fB\-\-verbose\fR +be verbose +.PP +Start edit by reading in `file' if given. +If `file' begins with a `!', read output of shell command. +.SH "REPORTING BUGS" +Report bugs to <bug\-ed@gnu.org>. +.br +Ed home page: http://www.gnu.org/software/ed/ed.html +.br +General help using GNU software: http://www.gnu.org/gethelp +.SH COPYRIGHT +Copyright \(co 1994 Andrew L. Moore. +.br +Copyright \(co 2012 Free Software Foundation, Inc. +License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> +.br +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. +.SH "SEE ALSO" +The full documentation for +.B Ed +is maintained as a Texinfo manual. If the +.B info +and +.B Ed +programs are properly installed at your site, the command +.IP +.B info Ed +.PP +should give you access to the complete manual. diff --git a/doc/ed.info b/doc/ed.info new file mode 100644 index 0000000..655ea94 --- /dev/null +++ b/doc/ed.info @@ -0,0 +1,1427 @@ +This is ed.info, produced by makeinfo version 4.13 from ed.texinfo. + +INFO-DIR-SECTION Basics +START-INFO-DIR-ENTRY +* Ed: (ed). The GNU line editor +END-INFO-DIR-ENTRY + + Copyright (C) 1993, 1994, 2006, 2007, 2008, 2009, 2010, 2011, 2012 +Free Software Foundation, Inc. + + Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.3 or +any later version published by the Free Software Foundation; with no +Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + + +File: ed.info, Node: Top, Next: Overview, Up: (dir) + +The GNU ed line editor +********************** + +This manual is for GNU ed (version 1.6, 1 January 2012). + + + GNU ed is a line-oriented text editor. It is used to create, display, +modify and otherwise manipulate text files, both interactively and via +shell scripts. A restricted version of ed, red, can only edit files in +the current directory and cannot execute shell commands. Ed is the +"standard" text editor in the sense that it is the original editor for +Unix, and thus widely available. For most purposes, however, it is +superseded by full-screen editors such as GNU Emacs or GNU Moe. + +* Menu: + +* Overview:: Overview of the `ed' command +* Introduction to Line Editing:: Getting started with GNU `ed' +* Invoking Ed:: Command line interface +* Line Addressing:: Specifying lines/ranges in the buffer +* Regular Expressions:: Patterns for selecting text +* Commands:: Commands recognized by GNU `ed' +* Limitations:: Intrinsic limits of GNU `ed' +* Diagnostics:: GNU `ed' error handling +* Problems:: Reporting bugs +* GNU Free Documentation License:: How you can copy and share this manual + + + Copyright (C) 1993, 1994, 2006, 2007, 2008, 2009, 2010, 2011, 2012 +Free Software Foundation, Inc. + + Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.3 or +any later version published by the Free Software Foundation; with no +Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + + +File: ed.info, Node: Overview, Next: Introduction to Line Editing, Prev: Top, Up: Top + +1 Overview +********** + +`ed' is a line-oriented text editor. It is used to create, display, +modify and otherwise manipulate text files. `red' is a restricted `ed': +it can only edit files in the current directory and cannot execute +shell commands. + + If invoked with a FILE argument, then a copy of FILE is read into +the editor's buffer. Changes are made to this copy and not directly to +FILE itself. Upon quitting `ed', any changes not explicitly saved with +a `w' command are lost. + + Editing is done in two distinct modes: "command" and "input". When +first invoked, `ed' is in command mode. In this mode commands are read +from the standard input and executed to manipulate the contents of the +editor buffer. A typical command might look like: + + ,s/OLD/NEW/g + + which replaces all occurences of the string OLD with NEW. + + When an input command, such as `a' (append), `i' (insert) or `c' +(change), is given, `ed' enters input mode. This is the primary means +of adding text to a file. In this mode, no commands are available; +instead, the standard input is written directly to the editor buffer. A +"line" consists of the text up to and including a <newline> character. +Input mode is terminated by entering a single period (`.') on a line. + + All `ed' commands operate on whole lines or ranges of lines; e.g., +the `d' command deletes lines; the `m' command moves lines, and so on. +It is possible to modify only a portion of a line by means of +replacement, as in the example above. However even here, the `s' +command is applied to whole lines at a time. + + In general, `ed' commands consist of zero or more line addresses, +followed by a single character command and possibly additional +parameters; i.e., commands have the structure: + + [ADDRESS [,ADDRESS]]COMMAND[PARAMETERS] + + The ADDRESSes indicate the line or range of lines to be affected by +the command. If fewer addresses are given than the command accepts, +then default addresses are supplied. + + +File: ed.info, Node: Introduction to Line Editing, Next: Invoking Ed, Prev: Overview, Up: Top + +2 Introduction to Line Editing +****************************** + +`ed' was created, along with the Unix operating system, by Ken Thompson +and Dennis Ritchie. It is the refinement of its more complex, +programmable predecessor, `QED', to which Thompson and Ritchie had +already added pattern matching capabilities (*note Regular +Expressions::). + + For the purposes of this tutorial, a working knowledge of the Unix +shell `sh' (*note Bash: (bash)Bash.) and the Unix file system is +recommended, since `ed' is designed to interact closely with them. + + The principal difference between line editors and display editors is +that display editors provide instant feedback to user commands, whereas +line editors require sometimes lengthy input before any effects are +seen. The advantage of instant feedback, of course, is that if a mistake +is made, it can be corrected immediately, before more damage is done. +Editing in `ed' requires more strategy and forethought; but if you are +up to the task, it can be quite efficient. + + Much of the `ed' command syntax is shared with other Unix utilities. + + As with the shell, <RETURN> (the carriage-return key) enters a line +of input. So when we speak of "entering" a command or some text in +`ed', <RETURN> is implied at the end of each line. Prior to typing +<RETURN>, corrections to the line may be made by typing either +<BACKSPACE> (sometimes labeled <DELETE> or <DEL>) to erase characters +backwards, or <CONTROL>-u (i.e., hold the CONTROL key and type u) to +erase the whole line. + + When `ed' first opens, it expects to be told what to do but doesn't +prompt us like the shell. So let's begin by telling `ed' to do so with +the <P> ("prompt") command: + + $ ed + P + * + + By default, `ed' uses asterisk (`*') as command prompt to avoid +confusion with the shell command prompt (`$'). + + We can run Unix shell (`sh') commands from inside `ed' by prefixing +them with <!> (exclamation mark, aka "bang"). For example: + + *!date + Mon Jun 26 10:08:41 PDT 2006 + ! + *!for s in hello world; do echo $s; done + hello + world + ! + * + + So far, this is no different from running commands in the Unix shell. +But let's say we want to edit the output of a command, or save it to a +file. First we must capture the command output to a temporary location +called a "buffer" where `ed' can access it. This is done with `ed''s +<r> command (mnemonic: "read"): + + *r !cal + 143 + * + + Here `ed' is telling us that it has just read 143 characters into +the editor buffer - i.e., the output of the `cal' command, which prints +a simple ASCII calendar. To display the buffer contents we issue the +<p> ("print") command (not to be confused with the prompt command, +which is uppercase!). To indicate the range of lines in the buffer that +should be printed, we prefix the command with <,> (comma) which is +shorthand for "the whole buffer": + + *,p + September 2006 + Mo Tu We Th Fr Sa Su + 1 2 3 + 4 5 6 7 8 9 10 + 11 12 13 14 15 16 17 + 18 19 20 21 22 23 24 + 25 26 27 28 29 30 + + * + + Now let's write the buffer contents to a file named `junk' with the +<w> ("write") command. Again, we use the <,> prefix to indicate that +it's the whole buffer we want: + + *,w junk + 143 + * + + Need we say? It's good practice to frequently write the buffer +contents, since unwritten changes to the buffer will be lost when we +exit `ed'. + + The sample sessions below illustrate some basic concepts of line +editing with `ed'. We begin by creating a file, `sonnet', with some +help from Shakespeare. As with the shell, all input to `ed' must be +followed by a <newline> character. Comments begin with a `#'. + + $ ed + # The `a' command is for appending text to the editor buffer. + a + No more be grieved at that which thou hast done. + Roses have thorns, and filvers foutians mud. + Clouds and eclipses stain both moon and sun, + And loathsome canker lives in sweetest bud. + . + # Entering a single period on a line returns `ed' to command mode. + # Now write the buffer to the file `sonnet' and quit: + w sonnet + 183 + # `ed' reports the number of characters written. + q + $ ls -l + total 2 + -rw-rw-r-- 1 alm 183 Nov 10 01:16 sonnet + $ + + In the next example, some typos are corrected in the file `sonnet'. + + $ ed sonnet + 183 + # Begin by printing the buffer to the terminal with the `p' command. + # The `,' means ``all lines.'' + ,p + No more be grieved at that which thou hast done. + Roses have thorns, and filvers foutians mud. + Clouds and eclipses stain both moon and sun, + And loathsome canker lives in sweetest bud. + # Select line 2 for editing. + 2 + Roses have thorns, and filvers foutians mud. + # Use the substitute command, `s', to replace `filvers' with `silver', + # and print the result. + s/filvers/silver/p + Roses have thorns, and silver foutians mud. + # And correct the spelling of `fountains'. + s/utia/untai/p + Roses have thorns, and silver fountains mud. + w sonnet + 183 + q + $ + + Since `ed' is line-oriented, we have to tell it which line, or range +of lines we want to edit. In the above example, we do this by +specifying the line's number, or sequence in the buffer. Alternatively, +we could have specified a unique string in the line, e.g., `/filvers/', +where the `/'s delimit the string in question. Subsequent commands +affect only the selected line, a.k.a. the "current" line. Portions of +that line are then replaced with the substitute command, whose syntax +is `s/OLD/NEW/'. + + Although `ed' accepts only one command per line, the print command +`p' is an exception, and may be appended to the end of most commands. + + In the next example, a title is added to our sonnet. + + $ ed sonnet + 183 + a + Sonnet #50 + . + ,p + No more be grieved at that which thou hast done. + Roses have thorns, and silver fountains mud. + Clouds and eclipses stain both moon and sun, + And loathsome canker lives in sweetest bud. + Sonnet #50 + # The title got appended to the end; we should have used `0a' + # to append ``before the first line.'' + # Move the title to its proper place. + 5m0p + Sonnet #50 + # The title is now the first line, and the current line has been + # set to this line as well. + ,p + Sonnet #50 + No more be grieved at that which thou hast done. + Roses have thorns, and silver fountains mud. + Clouds and eclipses stain both moon and sun, + And loathsome canker lives in sweetest bud. + wq sonnet + 195 + $ + + When `ed' opens a file, the current line is initially set to the +last line of that file. Similarly, the move command `m' sets the +current line to the last line moved. + + Related programs or routines are `vi (1)', `sed (1)', `regex (3)', +`sh (1)'. Relevant documents are: + + Unix User's Manual Supplementary Documents: 12 -- 13 + + B. W. Kernighan and P. J. Plauger: "Software Tools in Pascal", + Addison-Wesley, 1981. + + +File: ed.info, Node: Invoking Ed, Next: Line Addressing, Prev: Introduction to Line Editing, Up: Top + +3 Invoking Ed +************* + +The format for running `ed' is: + + ed [OPTIONS] [FILE] + red [OPTIONS] [FILE] + + FILE specifies the name of a file to read. If FILE is prefixed with +a bang (!), then it is interpreted as a shell command. In this case, +what is read is the standard output of FILE executed via `sh (1)'. To +read a file whose name begins with a bang, prefix the name with a +backslash (`\'). The default filename is set to FILE only if it is not +prefixed with a bang. + + `ed' supports the following options: + +`-h' +`--help' + Print an informative help message describing the options and exit. + +`-V' +`--version' + Print the version number of `ed' on the standard output and exit. + +`-G' +`--traditional' + Forces backwards compatibility. This affects the behavior of the + `ed' commands `G', `V', `f', `l', `m', `t' and `!!'. If the + default behavior of these commands does not seem familiar, then + try invoking `ed' with this switch. + +`-l' +`--loose-exit-status' + Do not exit with bad status if a command happens to "fail" (for + example if a substitution command finds nothing to replace). This + can be useful when `ed' is invoked as the editor for crontab. + +`-p STRING' +`--prompt=STRING' + Specifies a command prompt. This may be toggled on and off with the + `P' command. + +`-r' +`--restricted' + Run in restricted mode. This mode disables edition of files out of + the current directory and execution of shell commands. + +`-s' +`--quiet' +`--silent' + Suppresses diagnostics. This should be used if `ed''s standard + input is from a script. + +`-v' +`--verbose' + Verbose mode. This may be toggled on and off with the `H' command. + + + +File: ed.info, Node: Line Addressing, Next: Regular Expressions, Prev: Invoking Ed, Up: Top + +4 Line Addressing +***************** + +An address represents the number of a line in the buffer. `ed' +maintains a "current address" which is typically supplied to commands +as the default address when none is specified. When a file is first +read, the current address is set to the last line of the file. In +general, the current address is set to the last line affected by a +command. + + A line address is constructed from one of the bases in the list +below, optionally followed by a numeric offset. The offset may include +any combination of digits, operators (i.e., `+' and `-') and +whitespace. Addresses are read from left to right, and their values may +be absolute or relative to the current address. + + One exception to the rule that addresses represent line numbers is +the address `0' (zero). This means "before the first line," and is +valid wherever it makes sense. + + An address range is two addresses separated either by a comma or +semicolon. The value of the first address in a range cannot exceed the +value of the second. If only one address is given in a range, then the +second address is set to the given address. If an N-tuple of addresses +is given where N > 2, then the corresponding range is determined by the +last two addresses in the N-tuple. If only one address is expected, +then the last address is used. + + Each address in a comma-delimited range is interpreted relative to +the current address. In a semicolon-delimited range, the first address +is used to set the current address, and the second address is +interpreted relative to the first. + + The following address symbols are recognized. + +`.' + The current line (address) in the buffer. + +`$' + The last line in the buffer. + +`N' + The Nth, line in the buffer where N is a number in the range `0,$'. + +`+' + The next line. This is equivalent to `+1' and may be repeated with + cumulative effect. + +`-' + The previous line. This is equivalent to `-1' and may be repeated + with cumulative effect. + +`+N' +`WHITESPACE N' + The Nth next line, where N is a non-negative number. Whitespace + followed by a number N is interpreted as `+N'. + +`-N' + The Nth previous line, where N is a non-negative number. + +`,' + The first through last lines in the buffer. This is equivalent to + the address range `1,$'. + +`;' + The current through last lines in the buffer. This is equivalent + to the address range `.,$'. + +`/RE/' + The next line containing the regular expression RE. The search + wraps to the beginning of the buffer and continues down to the + current line, if necessary. `//' repeats the last search. + +`?RE?' + The previous line containing the regular expression RE. The search + wraps to the end of the buffer and continues up to the current + line, if necessary. `??' repeats the last search. + +`'x' + The apostrophe-x character pair addresses the line previously + marked by a `k' (mark) command, where `x' is a lower case letter + from the portable character set. + + + +File: ed.info, Node: Regular Expressions, Next: Commands, Prev: Line Addressing, Up: Top + +5 Regular Expressions +********************* + +Regular expressions are patterns used in selecting text. For example, +the `ed' command + + g/STRING/ + +prints all lines containing STRING. Regular expressions are also used +by the `s' command for selecting old text to be replaced with new text. + + In addition to a specifying string literals, regular expressions can +represent classes of strings. Strings thus represented are said to be +matched by the corresponding regular expression. If it is possible for a +regular expression to match several strings in a line, then the +left-most longest match is the one selected. + + The following symbols are used in constructing regular expressions: + +`C' + Any character C not listed below, including `{', `}', `(', `)', + `<' and `>', matches itself. + +`\C' + Any backslash-escaped character C, other than `{', ``}', `(', `)', + `<', `>', `b', `B', `w', `W', `+' and `?', matches itself. + +`.' + Matches any single character. + +`[CHAR-CLASS]' + Matches any single character in CHAR-CLASS. To include a `]' in + CHAR-CLASS, it must be the first character. A range of characters + may be specified by separating the end characters of the range + with a `-', e.g., `a-z' specifies the lower case characters. The + following literal expressions can also be used in CHAR-CLASS to + specify sets of characters: + + [:alnum:] [:cntrl:] [:lower:] [:space:] + [:alpha:] [:digit:] [:print:] [:upper:] + [:blank:] [:graph:] [:punct:] [:xdigit:] + + If `-' appears as the first or last character of CHAR-CLASS, then + it matches itself. All other characters in CHAR-CLASS match + themselves. + + Patterns in CHAR-CLASS of the form: + [.COL-ELM.] + [=COL-ELM=] + + where COL-ELM is a "collating element" are interpreted according + to `locale (5)'. See `regex (3)' for an explanation of these + constructs. + +`[^CHAR-CLASS]' + Matches any single character, other than newline, not in + CHAR-CLASS. CHAR-CLASS is defined as above. + +`^' + If `^' is the first character of a regular expression, then it + anchors the regular expression to the beginning of a line. + Otherwise, it matches itself. + +`$' + If `$' is the last character of a regular expression, it anchors + the regular expression to the end of a line. Otherwise, it matches + itself. + +`\(RE\)' + Defines a (possibly null) subexpression RE. Subexpressions may be + nested. A subsequent backreference of the form `\N', where N is a + number in the range [1,9], expands to the text matched by the Nth + subexpression. For example, the regular expression `\(a.c\)\1' + matches the string `abcabc', but not `abcadc'. Subexpressions are + ordered relative to their left delimiter. + +`*' + Matches the single character regular expression or subexpression + immediately preceding it zero or more times. If `*' is the first + character of a regular expression or subexpression, then it matches + itself. The `*' operator sometimes yields unexpected results. For + example, the regular expression `b*' matches the beginning of the + string `abbb', as opposed to the substring `bbb', since a null + match is the only left-most match. + +`\{N,M\}' +`\{N,\}' +`\{N\}' + Matches the single character regular expression or subexpression + immediately preceding it at least N and at most M times. If M is + omitted, then it matches at least N times. If the comma is also + omitted, then it matches exactly N times. If any of these forms + occurs first in a regular expression or subexpression, then it is + interpreted literally (i.e., the regular expression `\{2\}' + matches the string `{2}', and so on). + +`\<' +`\>' + Anchors the single character regular expression or subexpression + immediately following it to the beginning (in the case of `\<') or + ending (in the case of `\>') of a "word", i.e., in ASCII, a + maximal string of alphanumeric characters, including the + underscore (_). + + + The following extended operators are preceded by a backslash `\' to +distinguish them from traditional `ed' syntax. + +`\`' +`\'' + Unconditionally matches the beginning `\`' or ending `\'' of a + line. + +`\?' + Optionally matches the single character regular expression or + subexpression immediately preceding it. For example, the regular + expression `a[bd]\?c' matches the strings `abc', `adc' and `ac'. + If `\?' occurs at the beginning of a regular expressions or + subexpression, then it matches a literal `?'. + +`\+' + Matches the single character regular expression or subexpression + immediately preceding it one or more times. So the regular + expression `a+' is shorthand for `aa*'. If `\+' occurs at the + beginning of a regular expression or subexpression, then it + matches a literal `+'. + +`\b' + Matches the beginning or ending (null string) of a word. Thus the + regular expression `\bhello\b' is equivalent to `\<hello\>'. + However, `\b\b' is a valid regular expression whereas `\<\>' is + not. + +`\B' + Matches (a null string) inside a word. + +`\w' + Matches any character in a word. + +`\W' + Matches any character not in a word. + + + +File: ed.info, Node: Commands, Next: Limitations, Prev: Regular Expressions, Up: Top + +6 Commands +********** + +All `ed' commands are single characters, though some require additonal +parameters. If a command's parameters extend over several lines, then +each line except for the last must be terminated with a backslash (`\'). + + In general, at most one command is allowed per line. However, most +commands accept a print suffix, which is any of `p' (print), `l' +(list), or `n' (enumerate), to print the last line affected by the +command. + + An interrupt (typically <Control-C>) has the effect of aborting the +current command and returning the editor to command mode. + + `ed' recognizes the following commands. The commands are shown +together with the default address or address range supplied if none is +specified (in parenthesis). + +`(.)a' + Appends text to the buffer after the addressed line, which may be + the address `0' (zero). Text is entered in input mode. The current + address is set to last line entered. + +`(.,.)c' + Changes lines in the buffer. The addressed lines are deleted from + the buffer, and text is appended in their place. Text is entered + in input mode. The current address is set to last line entered. + +`(.,.)d' + Deletes the addressed lines from the buffer. If there is a line + after the deleted range, then the current address is set to this + line. Otherwise the current address is set to the line before the + deleted range. + +`e FILE' + Edits FILE, and sets the default filename. If FILE is not + specified, then the default filename is used. Any lines in the + buffer are deleted before the new file is read. The current + address is set to the last line read. + +`e !COMMAND' + Edits the standard output of `!COMMAND', (see the `!' command + below). The default filename is unchanged. Any lines in the buffer + are deleted before the output of COMMAND is read. The current + address is set to the last line read. + +`E FILE' + Edits FILE unconditionally. This is similar to the `e' command, + except that unwritten changes are discarded without warning. The + current address is set to the last line read. + +`f FILE' + Sets the default filename to FILE. If FILE is not specified, then + the default unescaped filename is printed. + +`(1,$)g /RE/COMMAND-LIST' + Global command. Applies COMMAND-LIST to each of the addressed + lines matching a regular expression RE. The current address is set + to the line currently matched before COMMAND-LIST is executed. At + the end of the `g' command, the current address is set to the last + line affected by COMMAND-LIST. + + At least the first command of COMMAND-LIST must appear on the same + line as the `g' command. All lines of a multi-line COMMAND-LIST + except the last line must be terminated with a backslash (`\'). + Any commands are allowed, except for `g', `G', `v', and `V'. By + default, a newline alone in COMMAND-LIST is equivalent to a `p' + command. If `ed' is invoked with the command-line option `-G', + then a newline in COMMAND-LIST is equivalent to a `.+1p' command. + +`(1,$)G /RE/' + Interactive global command. Interactively edits the addressed lines + matching a regular expression RE. For each matching line, the line + is printed, the current address is set, and the user is prompted to + enter a COMMAND-LIST. At the end of the `G' command, the current + address is set to the last line affected by (the last) + COMMAND-LIST. + + The format of COMMAND-LIST is the same as that of the `g' command. + A newline alone acts as a null command list. A single `&' repeats + the last non-null command list. + +`H' + Toggles the printing of error explanations. By default, + explanations are not printed. It is recommended that ed scripts + begin with this command to aid in debugging. + +`h' + Prints an explanation of the last error. + +`(.)i' + Inserts text in the buffer before the current line. The address `0' + (zero) is valid for this command; it is equivalent to address `1'. + Text is entered in input mode. The current address is set to the + last line entered. + +`(.,.+1)j' + Joins the addressed lines. The addressed lines are deleted from the + buffer and replaced by a single line containing their joined text. + The current address is set to the resultant line. + +`(.)kx' + Marks a line with a lower case letter `x'. The line can then be + addressed as `'x' (i.e., a single quote followed by `x') in + subsequent commands. The mark is not cleared until the line is + deleted or otherwise modified. + +`(.,.)l' + Prints the addressed lines unambiguously. The end of each line is + marked with a `$', and every `$' character within the text is + printed with a preceding backslash. The current address is set to + the last line printed. + +`(.,.)m(.)' + Moves lines in the buffer. The addressed lines are moved to after + the right-hand destination address, which may be the address `0' + (zero). The current address is set to the new address of the last + line moved. + +`(.,.)n' + Prints the addressed lines, preceding each line by its line number + and a <tab>. The current address is set to the last line printed. + +`(.,.)p' + Prints the addressed lines. The current address is set to the last + line printed. + +`P' + Toggles the command prompt on and off. Unless a prompt is + specified with command-line option `-p', the command prompt is by + default turned off. + +`q' + Quits `ed'. + +`Q' + Quits `ed' unconditionally. This is similar to the `q' command, + except that unwritten changes are discarded without warning. + +`($)r FILE' + Reads FILE to after the addressed line. If FILE is not specified, + then the default filename is used. If there is no default filename + prior to the command, then the default filename is set to FILE. + Otherwise, the default filename is unchanged. The current address + is set to the last line read. + +`($)r !COMMAND' + Reads to after the addressed line the standard output of + `!command', (see the `!' command below). The default filename is + unchanged. The current address is set to the last line read. + +`(.,.)s /RE/REPLACEMENT/' +`(.,.)s /RE/REPLACEMENT/g' +`(.,.)s /RE/REPLACEMENT/N' + Replaces text in the addressed lines matching a regular expression + RE with REPLACEMENT. By default, only the first match in each line + is replaced. If the `g' (global) suffix is given, then every match + is replaced. The N suffix, where N is a postive number, causes + only the Nth match to be replaced. It is an error if no + substitutions are performed on any of the addressed lines. The + current address is set to the last line affected. + + RE and REPLACEMENT may be delimited by any character other than + <space>, <newline> and the characters used by the form of the `s' + command shown below. If one or two of the last delimiters is + omitted, then the last line affected is printed as if the print + suffix `p' were specified. + + An unescaped `&' in REPLACEMENT is replaced by the currently + matched text. The character sequence `\M' where M is a number in + the range [1,9], is replaced by the Mth backreference expression + of the matched text. If REPLACEMENT consists of a single `%', then + REPLACEMENT from the last substitution is used. Newlines may be + embedded in REPLACEMENT if they are escaped with a backslash (`\'). + +`(.,.)s' + Repeats the last substitution. This form of the `s' command accepts + a count suffix N, and any combination of the characters `r', `g', + and `p'. If a count suffix N is given, then only the Nth match is + replaced. The `r' suffix causes the regular expression of the last + search to be used instead of the that of the last substitution. + The `g' suffix toggles the global suffix of the last substitution. + The `p' suffix toggles the print suffix of the last substitution. + The current address is set to the last line affected. + +`(.,.)t(.)' + Copies (i.e., transfers) the addressed lines to after the + right-hand destination address, which may be the address `0' + (zero). The current address is set to the last line copied. + +`u' + Undoes the last command and restores the current address to what + it was before the command. The global commands `g', `G', `v', and + `V' are treated as a single command by undo. `u' is its own + inverse. + +`(1,$)v /RE/COMMAND-LIST' + This is similar to the `g' command except that it applies + COMMAND-LIST to each of the addressed lines not matching the + regular expression RE. + +`(1,$)V /RE/' + This is similar to the `G' command except that it interactively + edits the addressed lines not matching the regular expression RE. + +`(1,$)w FILE' + Writes the addressed lines to FILE. Any previous contents of FILE + is lost without warning. If there is no default filename, then the + default filename is set to FILE, otherwise it is unchanged. If no + filename is specified, then the default filename is used. The + current address is unchanged. + +`(1,$)w !COMMAND' + Writes the addressed lines to the standard input of `!COMMAND', + (see the `!' command below). The default filename and current + address are unchanged. + +`(1,$)wq FILE' + Writes the addressed lines to FILE, and then executes a `q' + command. + +`(1,$)W FILE' + Appends the addressed lines to the end of FILE. This is similar to + the `w' command, expect that the previous contents of file is not + clobbered. The current address is unchanged. + +`(.)x' + Copies (puts) the contents of the cut buffer to after the addressed + line. The current address is set to the last line copied. + +`(.,.)y' + Copies (yanks) the addressed lines to the cut buffer. The cut + buffer is overwritten by subsequent `y', `s', `j', `d', or `c' + commands. The current address is unchanged. + +`(.+1)z N' + Scrolls N lines at a time starting at addressed line. If N is not + specified, then the current window size is used. The current + address is set to the last line printed. + +`!COMMAND' + Executes COMMAND via `sh (1)'. If the first character of COMMAND + is `!', then it is replaced by text of the previous `!COMMAND'. + `ed' does not process COMMAND for backslash (`\') escapes. + However, an unescaped `%' is replaced by the default filename. + When the shell returns from execution, a `!' is printed to the + standard output. The current line is unchanged. + +`(.,.)#' + Begins a comment; the rest of the line, up to a newline, is + ignored. If a line address followed by a semicolon is given, then + the current address is set to that address. Otherwise, the current + address is unchanged. + +`($)=' + Prints the line number of the addressed line. + +`(.+1)<newline>' + An address alone prints the addressed line. A <newline> alone is + equivalent to `+1p'. the current address is set to the address of + the printed line. + + + +File: ed.info, Node: Limitations, Next: Diagnostics, Prev: Commands, Up: Top + +7 Limitations +************* + +If the terminal hangs up, `ed' attempts to write the buffer to file +`ed.hup' or, if this fails, to `$HOME/ed.hup'. + + `ed' processes FILE arguments for backslash escapes, i.e., in a +filename, any character preceded by a backslash (`\') is interpreted +literally. + + If a text (non-binary) file is not terminated by a newline character, +then `ed' appends one on reading/writing it. In the case of a binary +file, `ed' does not append a newline on reading/writing. + + Per line overhead: 2 `pointer's, 1 `long int', and 1 `int'. + + +File: ed.info, Node: Diagnostics, Next: Problems, Prev: Limitations, Up: Top + +8 Diagnostics +************* + +When an error occurs, if `ed''s input is from a regular file or here +document, then it exits, otherwise it prints a `?' and returns to +command mode. An explanation of the last error can be printed with the +`h' (help) command. + + If the `u' (undo) command occurs in a global command list, then the +command list is executed only once. + + Attempting to quit `ed' or edit another file before writing a +modified buffer results in an error. If the command is entered a second +time, it succeeds, but any changes to the buffer are lost. + + `ed' exits with 0 if no errors occurred; otherwise >0. + + +File: ed.info, Node: Problems, Next: GNU Free Documentation License, Prev: Diagnostics, Up: Top + +9 Reporting Bugs +**************** + +There are probably bugs in `ed'. There are certainly errors and +omissions in this manual. If you report them, they will get fixed. If +you don't, no one will ever know about them and they will remain unfixed +for all eternity, if not longer. + + If you find a bug in `ed', please send electronic mail to +<bug-ed@gnu.org>. Include the version number, which you can find by +running ``ed' --version'. + + +File: ed.info, Node: GNU Free Documentation License, Prev: Problems, Up: Top + +10 GNU Free Documentation License +********************************* + + Version 1.3, 3 November 2008 + + Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. + `http://fsf.org/' + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + 0. PREAMBLE + + The purpose of this License is to make a manual, textbook, or other + functional and useful document "free" in the sense of freedom: to + assure everyone the effective freedom to copy and redistribute it, + with or without modifying it, either commercially or + noncommercially. Secondarily, this License preserves for the + author and publisher a way to get credit for their work, while not + being considered responsible for modifications made by others. + + This License is a kind of "copyleft", which means that derivative + works of the document must themselves be free in the same sense. + It complements the GNU General Public License, which is a copyleft + license designed for free software. + + We have designed this License in order to use it for manuals for + free software, because free software needs free documentation: a + free program should come with manuals providing the same freedoms + that the software does. But this License is not limited to + software manuals; it can be used for any textual work, regardless + of subject matter or whether it is published as a printed book. + We recommend this License principally for works whose purpose is + instruction or reference. + + 1. APPLICABILITY AND DEFINITIONS + + This License applies to any manual or other work, in any medium, + that contains a notice placed by the copyright holder saying it + can be distributed under the terms of this License. Such a notice + grants a world-wide, royalty-free license, unlimited in duration, + to use that work under the conditions stated herein. The + "Document", below, refers to any such manual or work. Any member + of the public is a licensee, and is addressed as "you". You + accept the license if you copy, modify or distribute the work in a + way requiring permission under copyright law. + + A "Modified Version" of the Document means any work containing the + Document or a portion of it, either copied verbatim, or with + modifications and/or translated into another language. + + A "Secondary Section" is a named appendix or a front-matter section + of the Document that deals exclusively with the relationship of the + publishers or authors of the Document to the Document's overall + subject (or to related matters) and contains nothing that could + fall directly within that overall subject. (Thus, if the Document + is in part a textbook of mathematics, a Secondary Section may not + explain any mathematics.) The relationship could be a matter of + historical connection with the subject or with related matters, or + of legal, commercial, philosophical, ethical or political position + regarding them. + + The "Invariant Sections" are certain Secondary Sections whose + titles are designated, as being those of Invariant Sections, in + the notice that says that the Document is released under this + License. If a section does not fit the above definition of + Secondary then it is not allowed to be designated as Invariant. + The Document may contain zero Invariant Sections. If the Document + does not identify any Invariant Sections then there are none. + + The "Cover Texts" are certain short passages of text that are + listed, as Front-Cover Texts or Back-Cover Texts, in the notice + that says that the Document is released under this License. A + Front-Cover Text may be at most 5 words, and a Back-Cover Text may + be at most 25 words. + + A "Transparent" copy of the Document means a machine-readable copy, + represented in a format whose specification is available to the + general public, that is suitable for revising the document + straightforwardly with generic text editors or (for images + composed of pixels) generic paint programs or (for drawings) some + widely available drawing editor, and that is suitable for input to + text formatters or for automatic translation to a variety of + formats suitable for input to text formatters. A copy made in an + otherwise Transparent file format whose markup, or absence of + markup, has been arranged to thwart or discourage subsequent + modification by readers is not Transparent. An image format is + not Transparent if used for any substantial amount of text. A + copy that is not "Transparent" is called "Opaque". + + Examples of suitable formats for Transparent copies include plain + ASCII without markup, Texinfo input format, LaTeX input format, + SGML or XML using a publicly available DTD, and + standard-conforming simple HTML, PostScript or PDF designed for + human modification. Examples of transparent image formats include + PNG, XCF and JPG. Opaque formats include proprietary formats that + can be read and edited only by proprietary word processors, SGML or + XML for which the DTD and/or processing tools are not generally + available, and the machine-generated HTML, PostScript or PDF + produced by some word processors for output purposes only. + + The "Title Page" means, for a printed book, the title page itself, + plus such following pages as are needed to hold, legibly, the + material this License requires to appear in the title page. For + works in formats which do not have any title page as such, "Title + Page" means the text near the most prominent appearance of the + work's title, preceding the beginning of the body of the text. + + The "publisher" means any person or entity that distributes copies + of the Document to the public. + + A section "Entitled XYZ" means a named subunit of the Document + whose title either is precisely XYZ or contains XYZ in parentheses + following text that translates XYZ in another language. (Here XYZ + stands for a specific section name mentioned below, such as + "Acknowledgements", "Dedications", "Endorsements", or "History".) + To "Preserve the Title" of such a section when you modify the + Document means that it remains a section "Entitled XYZ" according + to this definition. + + The Document may include Warranty Disclaimers next to the notice + which states that this License applies to the Document. These + Warranty Disclaimers are considered to be included by reference in + this License, but only as regards disclaiming warranties: any other + implication that these Warranty Disclaimers may have is void and + has no effect on the meaning of this License. + + 2. VERBATIM COPYING + + You may copy and distribute the Document in any medium, either + commercially or noncommercially, provided that this License, the + copyright notices, and the license notice saying this License + applies to the Document are reproduced in all copies, and that you + add no other conditions whatsoever to those of this License. You + may not use technical measures to obstruct or control the reading + or further copying of the copies you make or distribute. However, + you may accept compensation in exchange for copies. If you + distribute a large enough number of copies you must also follow + the conditions in section 3. + + You may also lend copies, under the same conditions stated above, + and you may publicly display copies. + + 3. COPYING IN QUANTITY + + If you publish printed copies (or copies in media that commonly + have printed covers) of the Document, numbering more than 100, and + the Document's license notice requires Cover Texts, you must + enclose the copies in covers that carry, clearly and legibly, all + these Cover Texts: Front-Cover Texts on the front cover, and + Back-Cover Texts on the back cover. Both covers must also clearly + and legibly identify you as the publisher of these copies. The + front cover must present the full title with all words of the + title equally prominent and visible. You may add other material + on the covers in addition. Copying with changes limited to the + covers, as long as they preserve the title of the Document and + satisfy these conditions, can be treated as verbatim copying in + other respects. + + If the required texts for either cover are too voluminous to fit + legibly, you should put the first ones listed (as many as fit + reasonably) on the actual cover, and continue the rest onto + adjacent pages. + + If you publish or distribute Opaque copies of the Document + numbering more than 100, you must either include a + machine-readable Transparent copy along with each Opaque copy, or + state in or with each Opaque copy a computer-network location from + which the general network-using public has access to download + using public-standard network protocols a complete Transparent + copy of the Document, free of added material. If you use the + latter option, you must take reasonably prudent steps, when you + begin distribution of Opaque copies in quantity, to ensure that + this Transparent copy will remain thus accessible at the stated + location until at least one year after the last time you + distribute an Opaque copy (directly or through your agents or + retailers) of that edition to the public. + + It is requested, but not required, that you contact the authors of + the Document well before redistributing any large number of + copies, to give them a chance to provide you with an updated + version of the Document. + + 4. MODIFICATIONS + + You may copy and distribute a Modified Version of the Document + under the conditions of sections 2 and 3 above, provided that you + release the Modified Version under precisely this License, with + the Modified Version filling the role of the Document, thus + licensing distribution and modification of the Modified Version to + whoever possesses a copy of it. In addition, you must do these + things in the Modified Version: + + A. Use in the Title Page (and on the covers, if any) a title + distinct from that of the Document, and from those of + previous versions (which should, if there were any, be listed + in the History section of the Document). You may use the + same title as a previous version if the original publisher of + that version gives permission. + + B. List on the Title Page, as authors, one or more persons or + entities responsible for authorship of the modifications in + the Modified Version, together with at least five of the + principal authors of the Document (all of its principal + authors, if it has fewer than five), unless they release you + from this requirement. + + C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. + + D. Preserve all the copyright notices of the Document. + + E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. + + F. Include, immediately after the copyright notices, a license + notice giving the public permission to use the Modified + Version under the terms of this License, in the form shown in + the Addendum below. + + G. Preserve in that license notice the full lists of Invariant + Sections and required Cover Texts given in the Document's + license notice. + + H. Include an unaltered copy of this License. + + I. Preserve the section Entitled "History", Preserve its Title, + and add to it an item stating at least the title, year, new + authors, and publisher of the Modified Version as given on + the Title Page. If there is no section Entitled "History" in + the Document, create one stating the title, year, authors, + and publisher of the Document as given on its Title Page, + then add an item describing the Modified Version as stated in + the previous sentence. + + J. Preserve the network location, if any, given in the Document + for public access to a Transparent copy of the Document, and + likewise the network locations given in the Document for + previous versions it was based on. These may be placed in + the "History" section. You may omit a network location for a + work that was published at least four years before the + Document itself, or if the original publisher of the version + it refers to gives permission. + + K. For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the + section all the substance and tone of each of the contributor + acknowledgements and/or dedications given therein. + + L. Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section + titles. + + M. Delete any section Entitled "Endorsements". Such a section + may not be included in the Modified Version. + + N. Do not retitle any existing section to be Entitled + "Endorsements" or to conflict in title with any Invariant + Section. + + O. Preserve any Warranty Disclaimers. + + If the Modified Version includes new front-matter sections or + appendices that qualify as Secondary Sections and contain no + material copied from the Document, you may at your option + designate some or all of these sections as invariant. To do this, + add their titles to the list of Invariant Sections in the Modified + Version's license notice. These titles must be distinct from any + other section titles. + + You may add a section Entitled "Endorsements", provided it contains + nothing but endorsements of your Modified Version by various + parties--for example, statements of peer review or that the text + has been approved by an organization as the authoritative + definition of a standard. + + You may add a passage of up to five words as a Front-Cover Text, + and a passage of up to 25 words as a Back-Cover Text, to the end + of the list of Cover Texts in the Modified Version. Only one + passage of Front-Cover Text and one of Back-Cover Text may be + added by (or through arrangements made by) any one entity. If the + Document already includes a cover text for the same cover, + previously added by you or by arrangement made by the same entity + you are acting on behalf of, you may not add another; but you may + replace the old one, on explicit permission from the previous + publisher that added the old one. + + The author(s) and publisher(s) of the Document do not by this + License give permission to use their names for publicity for or to + assert or imply endorsement of any Modified Version. + + 5. COMBINING DOCUMENTS + + You may combine the Document with other documents released under + this License, under the terms defined in section 4 above for + modified versions, provided that you include in the combination + all of the Invariant Sections of all of the original documents, + unmodified, and list them all as Invariant Sections of your + combined work in its license notice, and that you preserve all + their Warranty Disclaimers. + + The combined work need only contain one copy of this License, and + multiple identical Invariant Sections may be replaced with a single + copy. If there are multiple Invariant Sections with the same name + but different contents, make the title of each such section unique + by adding at the end of it, in parentheses, the name of the + original author or publisher of that section if known, or else a + unique number. Make the same adjustment to the section titles in + the list of Invariant Sections in the license notice of the + combined work. + + In the combination, you must combine any sections Entitled + "History" in the various original documents, forming one section + Entitled "History"; likewise combine any sections Entitled + "Acknowledgements", and any sections Entitled "Dedications". You + must delete all sections Entitled "Endorsements." + + 6. COLLECTIONS OF DOCUMENTS + + You may make a collection consisting of the Document and other + documents released under this License, and replace the individual + copies of this License in the various documents with a single copy + that is included in the collection, provided that you follow the + rules of this License for verbatim copying of each of the + documents in all other respects. + + You may extract a single document from such a collection, and + distribute it individually under this License, provided you insert + a copy of this License into the extracted document, and follow + this License in all other respects regarding verbatim copying of + that document. + + 7. AGGREGATION WITH INDEPENDENT WORKS + + A compilation of the Document or its derivatives with other + separate and independent documents or works, in or on a volume of + a storage or distribution medium, is called an "aggregate" if the + copyright resulting from the compilation is not used to limit the + legal rights of the compilation's users beyond what the individual + works permit. When the Document is included in an aggregate, this + License does not apply to the other works in the aggregate which + are not themselves derivative works of the Document. + + If the Cover Text requirement of section 3 is applicable to these + copies of the Document, then if the Document is less than one half + of the entire aggregate, the Document's Cover Texts may be placed + on covers that bracket the Document within the aggregate, or the + electronic equivalent of covers if the Document is in electronic + form. Otherwise they must appear on printed covers that bracket + the whole aggregate. + + 8. TRANSLATION + + Translation is considered a kind of modification, so you may + distribute translations of the Document under the terms of section + 4. Replacing Invariant Sections with translations requires special + permission from their copyright holders, but you may include + translations of some or all Invariant Sections in addition to the + original versions of these Invariant Sections. You may include a + translation of this License, and all the license notices in the + Document, and any Warranty Disclaimers, provided that you also + include the original English version of this License and the + original versions of those notices and disclaimers. In case of a + disagreement between the translation and the original version of + this License or a notice or disclaimer, the original version will + prevail. + + If a section in the Document is Entitled "Acknowledgements", + "Dedications", or "History", the requirement (section 4) to + Preserve its Title (section 1) will typically require changing the + actual title. + + 9. TERMINATION + + You may not copy, modify, sublicense, or distribute the Document + except as expressly provided under this License. Any attempt + otherwise to copy, modify, sublicense, or distribute it is void, + and will automatically terminate your rights under this License. + + 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, receipt of a copy of some or all of + the same material does not give you any rights to use it. + + 10. FUTURE REVISIONS OF THIS LICENSE + + The Free Software Foundation may publish new, revised versions of + the GNU Free Documentation 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. See + `http://www.gnu.org/copyleft/'. + + Each version of the License is given a distinguishing version + number. If the Document specifies that a particular numbered + version of this License "or any later version" applies to it, you + have the option of following the terms and conditions either of + that specified version or of any later version that has been + published (not as a draft) by the Free Software Foundation. If + the Document does not specify a version number of this License, + you may choose any version ever published (not as a draft) by the + Free Software Foundation. If the Document specifies that a proxy + can decide which future versions of this License can be used, that + proxy's public statement of acceptance of a version permanently + authorizes you to choose that version for the Document. + + 11. RELICENSING + + "Massive Multiauthor Collaboration Site" (or "MMC Site") means any + World Wide Web server that publishes copyrightable works and also + provides prominent facilities for anybody to edit those works. A + public wiki that anybody can edit is an example of such a server. + A "Massive Multiauthor Collaboration" (or "MMC") contained in the + site means any set of copyrightable works thus published on the MMC + site. + + "CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 + license published by Creative Commons Corporation, a not-for-profit + corporation with a principal place of business in San Francisco, + California, as well as future copyleft versions of that license + published by that same organization. + + "Incorporate" means to publish or republish a Document, in whole or + in part, as part of another Document. + + An MMC is "eligible for relicensing" if it is licensed under this + License, and if all works that were first published under this + License somewhere other than this MMC, and subsequently + incorporated in whole or in part into the MMC, (1) had no cover + texts or invariant sections, and (2) were thus incorporated prior + to November 1, 2008. + + The operator of an MMC Site may republish an MMC contained in the + site under CC-BY-SA on the same site at any time before August 1, + 2009, provided the MMC is eligible for relicensing. + + +ADDENDUM: How to use this License for your documents +==================================================== + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and license +notices just after the title page: + + Copyright (C) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.3 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + Texts. A copy of the license is included in the section entitled ``GNU + Free Documentation License''. + + If you have Invariant Sections, Front-Cover Texts and Back-Cover +Texts, replace the "with...Texts." line with this: + + with the Invariant Sections being LIST THEIR TITLES, with + the Front-Cover Texts being LIST, and with the Back-Cover Texts + being LIST. + + If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + + If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, to +permit their use in free software. + + + +Tag Table: +Node: Top568 +Node: Overview2247 +Node: Introduction to Line Editing4304 +Node: Invoking Ed11516 +Node: Line Addressing13317 +Node: Regular Expressions16420 +Node: Commands21765 +Node: Limitations32910 +Node: Diagnostics33551 +Node: Problems34255 +Node: GNU Free Documentation License34790 + +End Tag Table + + +Local Variables: +coding: iso-8859-15 +End: diff --git a/doc/ed.texinfo b/doc/ed.texinfo new file mode 100644 index 0000000..0cbd524 --- /dev/null +++ b/doc/ed.texinfo @@ -0,0 +1,990 @@ +\input texinfo @c -*-texinfo-*- +@c %**start of header +@setfilename ed.info +@documentencoding ISO-8859-15 +@settitle GNU @command{ed} Manual +@finalout +@c %**end of header + +@set UPDATED 1 January 2012 +@set VERSION 1.6 + +@dircategory Basics +@direntry +* Ed: (ed). The GNU line editor +@end direntry + +@copying +Copyright @copyright{} 1993, 1994, 2006, 2007, 2008, 2009, 2010, 2011, +2012 Free Software Foundation, Inc. + +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.3 or +any later version published by the Free Software Foundation; with no +Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. +@end copying + +@ifnothtml +@titlepage +@title GNU ed +@subtitle The GNU line editor +@subtitle for GNU ed version @value{VERSION}, @value{UPDATED} +@author by Andrew L. Moore, François Pinard, and Antonio Diaz Diaz + +@page +@vskip 0pt plus 1filll +@insertcopying +@end titlepage + +@contents +@end ifnothtml + +@ifnottex +@node Top +@top The GNU ed line editor + +This manual is for GNU ed (version @value{VERSION}, @value{UPDATED}). + +@sp 1 +GNU ed is a line-oriented text editor. It is used to create, display, +modify and otherwise manipulate text files, both interactively and via +shell scripts. A restricted version of ed, red, can only edit files in +the current directory and cannot execute shell commands. Ed is the +"standard" text editor in the sense that it is the original editor for +Unix, and thus widely available. For most purposes, however, it is +superseded by full-screen editors such as GNU Emacs or GNU Moe. +@end ifnottex + +@menu +* Overview:: Overview of the @command{ed} command +* Introduction to Line Editing:: Getting started with GNU @command{ed} +* Invoking Ed:: Command line interface +* Line Addressing:: Specifying lines/ranges in the buffer +* Regular Expressions:: Patterns for selecting text +* Commands:: Commands recognized by GNU @command{ed} +* Limitations:: Intrinsic limits of GNU @command{ed} +* Diagnostics:: GNU @command{ed} error handling +* Problems:: Reporting bugs +* GNU Free Documentation License:: How you can copy and share this manual + +@end menu + +@sp 1 +@insertcopying + + +@node Overview +@chapter Overview + +@command{ed} is a line-oriented text editor. It is used to create, +display, modify and otherwise manipulate text files. @command{red} is a +restricted @command{ed}: it can only edit files in the current directory +and cannot execute shell commands. + +If invoked with a @var{file} argument, then a copy of @var{file} is read +into the editor's buffer. Changes are made to this copy and not directly +to @var{file} itself. Upon quitting @command{ed}, any changes not +explicitly saved with a @samp{w} command are lost. + +Editing is done in two distinct modes: @dfn{command} and @dfn{input}. +When first invoked, @command{ed} is in command mode. In this mode +commands are read from the standard input and executed to manipulate the +contents of the editor buffer. A typical command might look like: + +@example +,s/@var{old}/@var{new}/g +@end example + +which replaces all occurences of the string @var{old} with @var{new}. + +When an input command, such as @samp{a} (append), @samp{i} (insert) or +@samp{c} (change), is given, @command{ed} enters input mode. This is the +primary means of adding text to a file. In this mode, no commands are +available; instead, the standard input is written directly to the editor +buffer. A @dfn{line} consists of the text up to and including a +@key{newline} character. Input mode is terminated by entering a single +period (@samp{.}) on a line. + +All @command{ed} commands operate on whole lines or ranges of lines; +e.g., the @samp{d} command deletes lines; the @samp{m} command moves +lines, and so on. It is possible to modify only a portion of a line by +means of replacement, as in the example above. However even here, the +@samp{s} command is applied to whole lines at a time. + +In general, @command{ed} commands consist of zero or more line +addresses, followed by a single character command and possibly +additional parameters; i.e., commands have the structure: + +@example +[@var{address} [,@var{address}]]@var{command}[@var{parameters}] +@end example + +The @var{address}es indicate the line or range of lines to be affected +by the command. If fewer addresses are given than the command accepts, +then default addresses are supplied. + + +@node Introduction to Line Editing +@chapter Introduction to Line Editing + +@command{ed} was created, along with the Unix operating system, by Ken +Thompson and Dennis Ritchie. It is the refinement of its more complex, +programmable predecessor, @cite{QED}, to which Thompson and Ritchie had +already added pattern matching capabilities (@pxref{Regular +Expressions}). + +For the purposes of this tutorial, a working knowledge of the Unix shell +@command{sh} (@pxref{Bash,,, bash, The GNU Bash Reference Manual}) and +the Unix file system is recommended, since @command{ed} is designed to +interact closely with them. + +The principal difference between line editors and display editors is +that display editors provide instant feedback to user commands, whereas +line editors require sometimes lengthy input before any effects are +seen. The advantage of instant feedback, of course, is that if a mistake +is made, it can be corrected immediately, before more damage is done. +Editing in @command{ed} requires more strategy and forethought; but if +you are up to the task, it can be quite efficient. + +Much of the @command{ed} command syntax is shared with other Unix +utilities. + +As with the shell, @key{RETURN} (the carriage-return key) enters a line +of input. So when we speak of ``entering'' a command or some text in +@command{ed}, @key{RETURN} is implied at the end of each line. Prior to +typing @key{RETURN}, corrections to the line may be made by typing +either @key{BACKSPACE} (sometimes labeled @key{DELETE} or @key{DEL}) to +erase characters backwards, or @key{CONTROL}-u (i.e., hold the CONTROL +key and type u) to erase the whole line. + +When @command{ed} first opens, it expects to be told what to do but +doesn't prompt us like the shell. So let's begin by telling @command{ed} +to do so with the @key{P} (@dfn{prompt}) command: + +@example +$ ed +P +* +@end example + +By default, @command{ed} uses asterisk (@samp{*}) as command prompt to +avoid confusion with the shell command prompt (@samp{$}). + +We can run Unix shell (@command{sh}) commands from inside @command{ed} +by prefixing them with @key{!} (exclamation mark, aka ``bang''). For +example: + +@example +*!date +Mon Jun 26 10:08:41 PDT 2006 +! +*!for s in hello world; do echo $s; done +hello +world +! +* +@end example + +So far, this is no different from running commands in the Unix shell. +But let's say we want to edit the output of a command, or save it to a +file. First we must capture the command output to a temporary location +called a @dfn{buffer} where @command{ed} can access it. This is done +with @command{ed}'s @key{r} command (mnemonic: @dfn{read}): + +@example +*r !cal +143 +* +@end example + +Here @command{ed} is telling us that it has just read 143 characters +into the editor buffer - i.e., the output of the @command{cal} command, +which prints a simple ASCII calendar. To display the buffer contents we +issue the @key{p} (@dfn{print}) command (not to be confused with the +prompt command, which is uppercase!). To indicate the range of lines in +the buffer that should be printed, we prefix the command with @key{,} +(comma) which is shorthand for ``the whole buffer'': + +@example +*,p + September 2006 +Mo Tu We Th Fr Sa Su + 1 2 3 + 4 5 6 7 8 9 10 +11 12 13 14 15 16 17 +18 19 20 21 22 23 24 +25 26 27 28 29 30 + +* +@end example + +Now let's write the buffer contents to a file named @code{junk} with the +@key{w} (@dfn{write}) command. Again, we use the @key{,} prefix to +indicate that it's the whole buffer we want: + +@example +*,w junk +143 +* +@end example + +Need we say? It's good practice to frequently write the buffer contents, +since unwritten changes to the buffer will be lost when we exit +@command{ed}. + +The sample sessions below illustrate some basic concepts of line editing +with @command{ed}. We begin by creating a file, @samp{sonnet}, with some +help from Shakespeare. As with the shell, all input to @command{ed} must +be followed by a @key{newline} character. Comments begin with a @samp{#}. + +@example +$ ed +# The `a' command is for appending text to the editor buffer. +a +No more be grieved at that which thou hast done. +Roses have thorns, and filvers foutians mud. +Clouds and eclipses stain both moon and sun, +And loathsome canker lives in sweetest bud. +. +# Entering a single period on a line returns @command{ed} to command mode. +# Now write the buffer to the file @samp{sonnet} and quit: +w sonnet +183 +# @command{ed} reports the number of characters written. +q +$ ls -l +total 2 +-rw-rw-r-- 1 alm 183 Nov 10 01:16 sonnet +$ +@end example + +In the next example, some typos are corrected in the file @samp{sonnet}. + +@example +$ ed sonnet +183 +# Begin by printing the buffer to the terminal with the @samp{p} command. +# The `,' means ``all lines.'' +,p +No more be grieved at that which thou hast done. +Roses have thorns, and filvers foutians mud. +Clouds and eclipses stain both moon and sun, +And loathsome canker lives in sweetest bud. +# Select line 2 for editing. +2 +Roses have thorns, and filvers foutians mud. +# Use the substitute command, @samp{s}, to replace `filvers' with `silver', +# and print the result. +s/filvers/silver/p +Roses have thorns, and silver foutians mud. +# And correct the spelling of `fountains'. +s/utia/untai/p +Roses have thorns, and silver fountains mud. +w sonnet +183 +q +$ +@end example + +Since @command{ed} is line-oriented, we have to tell it which line, or +range of lines we want to edit. In the above example, we do this by +specifying the line's number, or sequence in the buffer. Alternatively, +we could have specified a unique string in the line, e.g., +@samp{/filvers/}, where the @samp{/}s delimit the string in question. +Subsequent commands affect only the selected line, a.k.a. the +@dfn{current} line. Portions of that line are then replaced with the +substitute command, whose syntax is @samp{s/@var{old}/@var{new}/}. + +Although @command{ed} accepts only one command per line, the print +command @samp{p} is an exception, and may be appended to the end of most +commands. + +In the next example, a title is added to our sonnet. + +@example +$ ed sonnet +183 +a + Sonnet #50 +. +,p +No more be grieved at that which thou hast done. +Roses have thorns, and silver fountains mud. +Clouds and eclipses stain both moon and sun, +And loathsome canker lives in sweetest bud. + Sonnet #50 +# The title got appended to the end; we should have used `0a' +# to append ``before the first line.'' +# Move the title to its proper place. +5m0p + Sonnet #50 +# The title is now the first line, and the current line has been +# set to this line as well. +,p + Sonnet #50 +No more be grieved at that which thou hast done. +Roses have thorns, and silver fountains mud. +Clouds and eclipses stain both moon and sun, +And loathsome canker lives in sweetest bud. +wq sonnet +195 +$ +@end example + +When @command{ed} opens a file, the current line is initially set to the +last line of that file. Similarly, the move command @samp{m} sets the +current line to the last line moved. + +Related programs or routines are @command{vi (1)}, @command{sed (1)}, +@command{regex (3)}, @command{sh (1)}. Relevant documents +are: + +@quotation +Unix User's Manual Supplementary Documents: 12 --- 13 +@end quotation + +@quotation +B. W. Kernighan and P. J. Plauger: ``Software Tools in Pascal'', +Addison-Wesley, 1981. +@end quotation + + +@node Invoking Ed +@chapter Invoking Ed + +The format for running @command{ed} is: + +@example +ed [@var{options}] [@var{file}] +red [@var{options}] [@var{file}] +@end example + +@var{file} specifies the name of a file to read. If @var{file} is +prefixed with a bang (!), then it is interpreted as a shell command. In +this case, what is read is the standard output of @var{file} executed +via @command{sh (1)}. To read a file whose name begins with a bang, +prefix the name with a backslash (@kbd{\}). The default filename is set +to @var{file} only if it is not prefixed with a bang. + +@command{ed} supports the following options: + +@table @code +@item -h +@itemx --help +Print an informative help message describing the options and exit. + +@item -V +@itemx --version +Print the version number of @command{ed} on the standard output and exit. + +@item -G +@itemx --traditional +Forces backwards compatibility. This affects the behavior of the +@command{ed} commands @samp{G}, @samp{V}, @samp{f}, @samp{l}, @samp{m}, +@samp{t} and @samp{!!}. If the default behavior of these commands does +not seem familiar, then try invoking @command{ed} with this switch. + +@item -l +@itemx --loose-exit-status +Do not exit with bad status if a command happens to "fail" (for example +if a substitution command finds nothing to replace). This can be useful +when @command{ed} is invoked as the editor for crontab. + +@item -p @var{string} +@itemx --prompt=@var{string} +Specifies a command prompt. This may be toggled on and off with the +@samp{P} command. + +@item -r +@itemx --restricted +Run in restricted mode. This mode disables edition of files out of the +current directory and execution of shell commands. + +@item -s +@itemx --quiet +@itemx --silent +Suppresses diagnostics. This should be used if @command{ed}'s standard +input is from a script. + +@item -v +@itemx --verbose +Verbose mode. This may be toggled on and off with the @samp{H} command. + +@end table + + +@node Line Addressing +@chapter Line Addressing + +An address represents the number of a line in the buffer. @command{ed} +maintains a @dfn{current address} which is typically supplied to +commands as the default address when none is specified. When a file is +first read, the current address is set to the last line of the file. In +general, the current address is set to the last line affected by a +command. + +A line address is constructed from one of the bases in the list below, +optionally followed by a numeric offset. The offset may include any +combination of digits, operators (i.e., @samp{+} and @samp{-}) and +whitespace. Addresses are read from left to right, and their values may +be absolute or relative to the current address. + +One exception to the rule that addresses represent line numbers is the +address @samp{0} (zero). This means ``before the first line,'' and is +valid wherever it makes sense. + +An address range is two addresses separated either by a comma or +semicolon. The value of the first address in a range cannot exceed the +value of the second. If only one address is given in a range, then the +second address is set to the given address. If an @var{n}-tuple of +addresses is given where @var{n} > 2, then the corresponding range is +determined by the last two addresses in the @var{n}-tuple. If only one +address is expected, then the last address is used. + +Each address in a comma-delimited range is interpreted relative to the +current address. In a semicolon-delimited range, the first address is +used to set the current address, and the second address is interpreted +relative to the first. + +The following address symbols are recognized. + +@table @code +@item . +The current line (address) in the buffer. + +@item $ +The last line in the buffer. + +@item @var{n} +The @var{n}th, line in the buffer where @var{n} is a number in the range +@samp{0,$}. + +@item + +The next line. This is equivalent to @samp{+1} and may be repeated with +cumulative effect. + +@item - +The previous line. This is equivalent to @samp{-1} and may be repeated +with cumulative effect. + +@item +@var{n} +@itemx @var{whitespace} @var{n} +The @var{n}th next line, where @var{n} is a non-negative number. +Whitespace followed by a number @var{n} is interpreted as +@samp{+@var{n}}. + +@item -@var{n} +The @var{n}th previous line, where @var{n} is a non-negative number. + +@item , +The first through last lines in the buffer. This is equivalent to the +address range @samp{1,$}. + +@item ; +The current through last lines in the buffer. This is equivalent to the +address range @samp{.,$}. + +@item /@var{re}/ +The next line containing the regular expression @var{re}. The search +wraps to the beginning of the buffer and continues down to the current +line, if necessary. @samp{//} repeats the last search. + +@item ?@var{re}? +The previous line containing the regular expression @var{re}. The search +wraps to the end of the buffer and continues up to the current line, if +necessary. @samp{??} repeats the last search. + +@item 'x +The apostrophe-x character pair addresses the line previously marked by +a @samp{k} (mark) command, where @samp{x} is a lower case letter from +the portable character set. + +@end table + + +@node Regular Expressions +@chapter Regular Expressions + +Regular expressions are patterns used in selecting text. For example, +the @command{ed} command + +@example +g/@var{string}/ +@end example + +@noindent +prints all lines containing @var{string}. Regular expressions are also +used by the @samp{s} command for selecting old text to be replaced with +new text. + +In addition to a specifying string literals, regular expressions can +represent classes of strings. Strings thus represented are said to be +matched by the corresponding regular expression. If it is possible for a +regular expression to match several strings in a line, then the +left-most longest match is the one selected. + +The following symbols are used in constructing regular expressions: + +@table @code + +@item @var{c} +Any character @var{c} not listed below, including @samp{@{}, @samp{@}}, +@samp{(}, @samp{)}, @samp{<} and @samp{>}, matches itself. + +@item \@var{c} +Any backslash-escaped character @var{c}, other than @samp{@{}, +`@samp{@}}, @samp{(}, @samp{)}, @samp{<}, @samp{>}, @samp{b}, @samp{B}, +@samp{w}, @samp{W}, @samp{+} and @samp{?}, matches itself. + +@item . +Matches any single character. + +@item [@var{char-class}] +Matches any single character in @var{char-class}. To include a @samp{]} +in @var{char-class}, it must be the first character. A range of +characters may be specified by separating the end characters of the +range with a @samp{-}, e.g., @samp{a-z} specifies the lower case +characters. The following literal expressions can also be used in +@var{char-class} to specify sets of characters: + +@example +[:alnum:] [:cntrl:] [:lower:] [:space:] +[:alpha:] [:digit:] [:print:] [:upper:] +[:blank:] [:graph:] [:punct:] [:xdigit:] +@end example + +If @samp{-} appears as the first or last character of @var{char-class}, +then it matches itself. All other characters in @var{char-class} match +themselves. + +Patterns in +@var{char-class} +of the form: +@example +[.@var{col-elm}.] +[=@var{col-elm}=] +@end example + +@noindent +where @var{col-elm} is a @dfn{collating element} are interpreted +according to @code{locale (5)}. See +@code{regex (3)} for an explanation of these constructs. + +@item [^@var{char-class}] +Matches any single character, other than newline, not in +@var{char-class}. @var{char-class} is defined as above. + +@item ^ +If @samp{^} is the first character of a regular expression, then it +anchors the regular expression to the beginning of a line. Otherwise, +it matches itself. + +@item $ +If @samp{$} is the last character of a regular expression, it anchors +the regular expression to the end of a line. Otherwise, it matches +itself. + +@item \(@var{re}\) +Defines a (possibly null) subexpression @var{re}. Subexpressions may be +nested. A subsequent backreference of the form @samp{\@var{n}}, where +@var{n} is a number in the range [1,9], expands to the text matched by +the @var{n}th subexpression. For example, the regular expression +@samp{\(a.c\)\1} matches the string @samp{abcabc}, but not +@samp{abcadc}. Subexpressions are ordered relative to their left +delimiter. + +@item * +Matches the single character regular expression or subexpression +immediately preceding it zero or more times. If @samp{*} is the first +character of a regular expression or subexpression, then it matches +itself. The @samp{*} operator sometimes yields unexpected results. For +example, the regular expression @samp{b*} matches the beginning of the +string @samp{abbb}, as opposed to the substring @samp{bbb}, since a null +match is the only left-most match. + +@item \@{@var{n},@var{m}\@} +@itemx \@{@var{n},\@} +@itemx \@{@var{n}\@} +Matches the single character regular expression or subexpression +immediately preceding it at least @var{n} and at most @var{m} times. If +@var{m} is omitted, then it matches at least @var{n} times. If the comma +is also omitted, then it matches exactly @var{n} times. If any of these +forms occurs first in a regular expression or subexpression, then it is +interpreted literally (i.e., the regular expression @samp{\@{2\@}} +matches the string @samp{@{2@}}, and so on). + +@item \< +@itemx \> +Anchors the single character regular expression or subexpression +immediately following it to the beginning (in the case of @samp{\<}) or +ending (in the case of @samp{\>}) of a @dfn{word}, i.e., in ASCII, a +maximal string of alphanumeric characters, including the underscore (_). + +@end table + +The following extended operators are preceded by a backslash @samp{\} to +distinguish them from traditional @command{ed} syntax. + +@table @code + +@item \` +@itemx \' +Unconditionally matches the beginning @samp{\`} or ending @samp{\'} of a line. + +@item \? +Optionally matches the single character regular expression or +subexpression immediately preceding it. For example, the regular +expression @samp{a[bd]\?c} matches the strings @samp{abc}, @samp{adc} +and @samp{ac}. If @samp{\?} occurs at the beginning of a regular +expressions or subexpression, then it matches a literal @samp{?}. + +@item \+ +Matches the single character regular expression or subexpression +immediately preceding it one or more times. So the regular expression +@samp{a+} is shorthand for @samp{aa*}. If @samp{\+} occurs at the +beginning of a regular expression or subexpression, then it matches a +literal @samp{+}. + +@item \b +Matches the beginning or ending (null string) of a word. Thus the +regular expression @samp{\bhello\b} is equivalent to @samp{\<hello\>}. +However, @samp{\b\b} is a valid regular expression whereas @samp{\<\>} +is not. + +@item \B +Matches (a null string) inside a word. + +@item \w +Matches any character in a word. + +@item \W +Matches any character not in a word. + +@end table + + +@node Commands +@chapter Commands + +All @command{ed} commands are single characters, though some require +additonal parameters. If a command's parameters extend over several +lines, then each line except for the last must be terminated with a +backslash (@samp{\}). + +In general, at most one command is allowed per line. However, most +commands accept a print suffix, which is any of @samp{p} (print), +@samp{l} (list), or @samp{n} (enumerate), to print the last line +affected by the command. + +An interrupt (typically @key{Control-C}) has the effect of aborting the +current command and returning the editor to command mode. + +@command{ed} recognizes the following commands. The commands are shown +together with the default address or address range supplied if none is +specified (in parenthesis). + +@table @code + +@item (.)a +Appends text to the buffer after the addressed line, which may be the +address @samp{0} (zero). Text is entered in input mode. The current +address is set to last line entered. + +@item (.,.)c +Changes lines in the buffer. The addressed lines are deleted from the +buffer, and text is appended in their place. Text is entered in input +mode. The current address is set to last line entered. + +@item (.,.)d +Deletes the addressed lines from the buffer. If there is a line after +the deleted range, then the current address is set to this line. +Otherwise the current address is set to the line before the deleted +range. + +@item e @var{file} +Edits @var{file}, and sets the default filename. If @var{file} is not +specified, then the default filename is used. Any lines in the buffer +are deleted before the new file is read. The current address is set to +the last line read. + +@item e !@var{command} +Edits the standard output of @samp{!@var{command}}, (see the @samp{!} +command below). The default filename is unchanged. Any lines in the +buffer are deleted before the output of @var{command} is read. The +current address is set to the last line read. + +@item E @var{file} +Edits @var{file} unconditionally. This is similar to the @samp{e} +command, except that unwritten changes are discarded without warning. +The current address is set to the last line read. + +@item f @var{file} +Sets the default filename to @var{file}. If @var{file} is not specified, +then the default unescaped filename is printed. + +@item (1,$)g /@var{re}/@var{command-list} +Global command. Applies @var{command-list} to each of the addressed +lines matching a regular expression @var{re}. The current address is set +to the line currently matched before @var{command-list} is executed. At +the end of the @samp{g} command, the current address is set to the last +line affected by @var{command-list}. + +At least the first command of @var{command-list} must appear on the same +line as the @samp{g} command. All lines of a multi-line +@var{command-list} except the last line must be terminated with a +backslash (@samp{\}). Any commands are allowed, except for @samp{g}, +@samp{G}, @samp{v}, and @samp{V}. By default, a newline alone in +@var{command-list} is equivalent to a @samp{p} command. If @command{ed} +is invoked with the command-line option @samp{-G}, then a newline in +@var{command-list} is equivalent to a @samp{.+1p} command. + +@item (1,$)G /@var{re}/ +Interactive global command. Interactively edits the addressed lines +matching a regular expression @var{re}. For each matching line, the line +is printed, the current address is set, and the user is prompted to +enter a @var{command-list}. At the end of the @samp{G} command, the +current address is set to the last line affected by (the last) +@var{command-list}. + +The format of @var{command-list} is the same as that of the @samp{g} +command. A newline alone acts as a null command list. A single @samp{&} +repeats the last non-null command list. + +@item H +Toggles the printing of error explanations. By default, explanations are +not printed. It is recommended that ed scripts begin with this command +to aid in debugging. + +@item h +Prints an explanation of the last error. + +@item (.)i +Inserts text in the buffer before the current line. The address @samp{0} +(zero) is valid for this command; it is equivalent to address @samp{1}. +Text is entered in input mode. The current address is set to the last +line entered. + +@item (.,.+1)j +Joins the addressed lines. The addressed lines are deleted from the +buffer and replaced by a single line containing their joined text. The +current address is set to the resultant line. + +@item (.)kx +Marks a line with a lower case letter @samp{x}. The line can then be +addressed as @samp{'x} (i.e., a single quote followed by @samp{x}) in +subsequent commands. The mark is not cleared until the line is deleted +or otherwise modified. + +@item (.,.)l +Prints the addressed lines unambiguously. The end of each line is marked +with a @samp{$}, and every @samp{$} character within the text is printed +with a preceding backslash. The current address is set to the last line +printed. + +@item (.,.)m(.) +Moves lines in the buffer. The addressed lines are moved to after the +right-hand destination address, which may be the address @samp{0} +(zero). The current address is set to the new address of the last line +moved. + +@item (.,.)n +Prints the addressed lines, preceding each line by its line number and a +@key{tab}. The current address is set to the last line printed. + +@item (.,.)p +Prints the addressed lines. The current address is set to the last line +printed. + +@item P +Toggles the command prompt on and off. Unless a prompt is specified with +command-line option @samp{-p}, the command prompt is by default turned +off. + +@item q +Quits @command{ed}. + +@item Q +Quits @command{ed} unconditionally. This is similar to the @code{q} +command, except that unwritten changes are discarded without warning. + +@item ($)r @var{file} +Reads @var{file} to after the addressed line. If @var{file} is not +specified, then the default filename is used. If there is no default +filename prior to the command, then the default filename is set to +@var{file}. Otherwise, the default filename is unchanged. The current +address is set to the last line read. + +@item ($)r !@var{command} +Reads to after the addressed line the standard output of +@samp{!command}, (see the @samp{!} command below). The default filename +is unchanged. The current address is set to the last line read. + +@item (.,.)s /@var{re}/@var{replacement}/ +@itemx (.,.)s /@var{re}/@var{replacement}/g +@itemx (.,.)s /@var{re}/@var{replacement}/@var{n} +Replaces text in the addressed lines matching a regular expression +@var{re} with @var{replacement}. By default, only the first match in +each line is replaced. If the @samp{g} (global) suffix is given, then +every match is replaced. The @var{n} suffix, where @var{n} is a postive +number, causes only the @var{n}th match to be replaced. It is an error +if no substitutions are performed on any of the addressed lines. The +current address is set to the last line affected. + +@var{re} and @var{replacement} may be delimited by any character other +than @key{space}, @key{newline} and the characters used by the form of +the @samp{s} command shown below. If one or two of the last delimiters +is omitted, then the last line affected is printed as if the print +suffix @samp{p} were specified. + +An unescaped @samp{&} in @var{replacement} is replaced by the currently +matched text. The character sequence @samp{\@var{m}} where @var{m} is a +number in the range [1,9], is replaced by the @var{m}th backreference +expression of the matched text. If @var{replacement} consists of a +single @samp{%}, then @var{replacement} from the last substitution is +used. Newlines may be embedded in @var{replacement} if they are escaped +with a backslash (@samp{\}). + +@item (.,.)s +Repeats the last substitution. This form of the @samp{s} command accepts +a count suffix @var{n}, and any combination of the characters @samp{r}, +@samp{g}, and @samp{p}. If a count suffix @var{n} is given, then only +the @var{n}th match is replaced. The @samp{r} suffix causes the regular +expression of the last search to be used instead of the that of the last +substitution. The @samp{g} suffix toggles the global suffix of the last +substitution. The @samp{p} suffix toggles the print suffix of the last +substitution. The current address is set to the last line affected. + +@item (.,.)t(.) +Copies (i.e., transfers) the addressed lines to after the right-hand +destination address, which may be the address @samp{0} (zero). The +current address is set to the last line copied. + +@item u +Undoes the last command and restores the current address to what it was +before the command. The global commands @samp{g}, @samp{G}, @samp{v}, +and @samp{V} are treated as a single command by undo. @samp{u} is its +own inverse. + +@item (1,$)v /@var{re}/@var{command-list} +This is similar to the @samp{g} command except that it applies +@var{command-list} to each of the addressed lines not matching the +regular expression @var{re}. + +@item (1,$)V /@var{re}/ +This is similar to the @samp{G} command except that it interactively +edits the addressed lines not matching the regular expression @var{re}. + +@item (1,$)w @var{file} +Writes the addressed lines to @var{file}. Any previous contents of +@var{file} is lost without warning. If there is no default filename, +then the default filename is set to @var{file}, otherwise it is +unchanged. If no filename is specified, then the default filename is +used. The current address is unchanged. + +@item (1,$)w !@var{command} +Writes the addressed lines to the standard input of +@samp{!@var{command}}, (see the @samp{!} command below). The default +filename and current address are unchanged. + +@item (1,$)wq @var{file} +Writes the addressed lines to @var{file}, and then executes a @samp{q} +command. + +@item (1,$)W @var{file} +Appends the addressed lines to the end of @var{file}. This is similar to +the @samp{w} command, expect that the previous contents of file is not +clobbered. The current address is unchanged. + +@item (.)x +Copies (puts) the contents of the cut buffer to after the addressed +line. The current address is set to the last line copied. + +@item (.,.)y +Copies (yanks) the addressed lines to the cut buffer. The cut buffer is +overwritten by subsequent @samp{y}, @samp{s}, @samp{j}, @samp{d}, or +@samp{c} commands. The current address is unchanged. + +@item (.+1)z @var{n} +Scrolls @var{n} lines at a time starting at addressed line. If @var{n} +is not specified, then the current window size is used. The current +address is set to the last line printed. + +@item !@var{command} +Executes @var{command} via @command{sh (1)}. If the first character of +@var{command} is @samp{!}, then it is replaced by text of the previous +@samp{!@var{command}}. @command{ed} does not process @var{command} for +backslash (@samp{\}) escapes. However, an unescaped @samp{%} is replaced +by the default filename. When the shell returns from execution, a +@samp{!} is printed to the standard output. The current line is +unchanged. + +@item (.,.)# +Begins a comment; the rest of the line, up to a newline, is ignored. If +a line address followed by a semicolon is given, then the current +address is set to that address. Otherwise, the current address is +unchanged. + +@item ($)= +Prints the line number of the addressed line. + +@item (.+1)@key{newline} +An address alone prints the addressed line. A @key{newline} alone is +equivalent to @samp{+1p}. the current address is set to the address of +the printed line. + +@end table + + +@node Limitations +@chapter Limitations + +If the terminal hangs up, @command{ed} attempts to write the buffer to +file @file{ed.hup} or, if this fails, to @file{$HOME/ed.hup}. + +@command{ed} processes @var{file} arguments for backslash escapes, i.e., +in a filename, any character preceded by a backslash (@samp{\}) is +interpreted literally. + +If a text (non-binary) file is not terminated by a newline character, +then @command{ed} appends one on reading/writing it. In the case of a +binary file, @command{ed} does not append a newline on reading/writing. + +Per line overhead: 2 @code{pointer}s, 1 @code{long int}, and 1 @code{int}. + + +@node Diagnostics +@chapter Diagnostics + +When an error occurs, if @command{ed}'s input is from a regular file or +here document, then it exits, otherwise it prints a @samp{?} and returns +to command mode. An explanation of the last error can be printed with +the @samp{h} (help) command. + +If the @samp{u} (undo) command occurs in a global command list, then the +command list is executed only once. + +Attempting to quit @command{ed} or edit another file before writing a +modified buffer results in an error. If the command is entered a second +time, it succeeds, but any changes to the buffer are lost. + +@command{ed} exits with 0 if no errors occurred; otherwise >0. + + +@node Problems +@chapter Reporting Bugs + +There are probably bugs in @command{ed}. There are certainly errors and +omissions in this manual. If you report them, they will get fixed. If +you don't, no one will ever know about them and they will remain unfixed +for all eternity, if not longer. + +If you find a bug in @command{ed}, please send electronic mail to +@email{bug-ed@@gnu.org}. Include the version number, which you can +find by running @w{@samp{@command{ed} --version}}. + + +@node GNU Free Documentation License +@chapter GNU Free Documentation License +@include fdl.texinfo + +@bye diff --git a/doc/fdl.texinfo b/doc/fdl.texinfo new file mode 100644 index 0000000..8805f1a --- /dev/null +++ b/doc/fdl.texinfo @@ -0,0 +1,506 @@ +@c The GNU Free Documentation License. +@center Version 1.3, 3 November 2008 + +@c This file is intended to be included within another document, +@c hence no sectioning command or @node. + +@display +Copyright @copyright{} 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. +@uref{http://fsf.org/} + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +@end display + +@enumerate 0 +@item +PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document @dfn{free} in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of ``copyleft'', which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + +@item +APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The ``Document'', below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as ``you''. You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A ``Modified Version'' of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A ``Secondary Section'' is a named appendix or a front-matter section +of the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall +subject (or to related matters) and contains nothing that could fall +directly within that overall subject. (Thus, if the Document is in +part a textbook of mathematics, a Secondary Section may not explain +any mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The ``Invariant Sections'' are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The ``Cover Texts'' are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A ``Transparent'' copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not ``Transparent'' is called ``Opaque''. + +Examples of suitable formats for Transparent copies include plain +@sc{ascii} without markup, Texinfo input format, La@TeX{} input +format, @acronym{SGML} or @acronym{XML} using a publicly available +@acronym{DTD}, and standard-conforming simple @acronym{HTML}, +PostScript or @acronym{PDF} designed for human modification. Examples +of transparent image formats include @acronym{PNG}, @acronym{XCF} and +@acronym{JPG}. Opaque formats include proprietary formats that can be +read and edited only by proprietary word processors, @acronym{SGML} or +@acronym{XML} for which the @acronym{DTD} and/or processing tools are +not generally available, and the machine-generated @acronym{HTML}, +PostScript or @acronym{PDF} produced by some word processors for +output purposes only. + +The ``Title Page'' means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, ``Title Page'' means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +The ``publisher'' means any person or entity that distributes copies +of the Document to the public. + +A section ``Entitled XYZ'' means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as ``Acknowledgements'', +``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title'' +of such a section when you modify the Document means that it remains a +section ``Entitled XYZ'' according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + +@item +VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + +@item +COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + +@item +MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +@enumerate A +@item +Use in the Title Page (and on the covers, if any) a title distinct +from that of the Document, and from those of previous versions +(which should, if there were any, be listed in the History section +of the Document). You may use the same title as a previous version +if the original publisher of that version gives permission. + +@item +List on the Title Page, as authors, one or more persons or entities +responsible for authorship of the modifications in the Modified +Version, together with at least five of the principal authors of the +Document (all of its principal authors, if it has fewer than five), +unless they release you from this requirement. + +@item +State on the Title page the name of the publisher of the +Modified Version, as the publisher. + +@item +Preserve all the copyright notices of the Document. + +@item +Add an appropriate copyright notice for your modifications +adjacent to the other copyright notices. + +@item +Include, immediately after the copyright notices, a license notice +giving the public permission to use the Modified Version under the +terms of this License, in the form shown in the Addendum below. + +@item +Preserve in that license notice the full lists of Invariant Sections +and required Cover Texts given in the Document's license notice. + +@item +Include an unaltered copy of this License. + +@item +Preserve the section Entitled ``History'', Preserve its Title, and add +to it an item stating at least the title, year, new authors, and +publisher of the Modified Version as given on the Title Page. If +there is no section Entitled ``History'' in the Document, create one +stating the title, year, authors, and publisher of the Document as +given on its Title Page, then add an item describing the Modified +Version as stated in the previous sentence. + +@item +Preserve the network location, if any, given in the Document for +public access to a Transparent copy of the Document, and likewise +the network locations given in the Document for previous versions +it was based on. These may be placed in the ``History'' section. +You may omit a network location for a work that was published at +least four years before the Document itself, or if the original +publisher of the version it refers to gives permission. + +@item +For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve +the Title of the section, and preserve in the section all the +substance and tone of each of the contributor acknowledgements and/or +dedications given therein. + +@item +Preserve all the Invariant Sections of the Document, +unaltered in their text and in their titles. Section numbers +or the equivalent are not considered part of the section titles. + +@item +Delete any section Entitled ``Endorsements''. Such a section +may not be included in the Modified Version. + +@item +Do not retitle any existing section to be Entitled ``Endorsements'' or +to conflict in title with any Invariant Section. + +@item +Preserve any Warranty Disclaimers. +@end enumerate + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled ``Endorsements'', provided it contains +nothing but endorsements of your Modified Version by various +parties---for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + +@item +COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled ``History'' +in the various original documents, forming one section Entitled +``History''; likewise combine any sections Entitled ``Acknowledgements'', +and any sections Entitled ``Dedications''. You must delete all +sections Entitled ``Endorsements.'' + +@item +COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + +@item +AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an ``aggregate'' if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + +@item +TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled ``Acknowledgements'', +``Dedications'', or ``History'', the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + +@item +TERMINATION + +You may not copy, modify, sublicense, or distribute the Document +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense, or distribute it is void, and +will automatically terminate your rights under this License. + +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, receipt of a copy of some or all of the same material does +not give you any rights to use it. + +@item +FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation 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. See +@uref{http://www.gnu.org/copyleft/}. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License ``or any later version'' applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. If the Document +specifies that a proxy can decide which future versions of this +License can be used, that proxy's public statement of acceptance of a +version permanently authorizes you to choose that version for the +Document. + +@item +RELICENSING + +``Massive Multiauthor Collaboration Site'' (or ``MMC Site'') means any +World Wide Web server that publishes copyrightable works and also +provides prominent facilities for anybody to edit those works. A +public wiki that anybody can edit is an example of such a server. A +``Massive Multiauthor Collaboration'' (or ``MMC'') contained in the +site means any set of copyrightable works thus published on the MMC +site. + +``CC-BY-SA'' means the Creative Commons Attribution-Share Alike 3.0 +license published by Creative Commons Corporation, a not-for-profit +corporation with a principal place of business in San Francisco, +California, as well as future copyleft versions of that license +published by that same organization. + +``Incorporate'' means to publish or republish a Document, in whole or +in part, as part of another Document. + +An MMC is ``eligible for relicensing'' if it is licensed under this +License, and if all works that were first published under this License +somewhere other than this MMC, and subsequently incorporated in whole +or in part into the MMC, (1) had no cover texts or invariant sections, +and (2) were thus incorporated prior to November 1, 2008. + +The operator of an MMC Site may republish an MMC contained in the site +under CC-BY-SA on the same site at any time before August 1, 2009, +provided the MMC is eligible for relicensing. + +@end enumerate + +@page +@heading ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + +@smallexample +@group + Copyright (C) @var{year} @var{your name}. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.3 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + Texts. A copy of the license is included in the section entitled ``GNU + Free Documentation License''. +@end group +@end smallexample + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the ``with@dots{}Texts.'' line with this: + +@smallexample +@group + with the Invariant Sections being @var{list their titles}, with + the Front-Cover Texts being @var{list}, and with the Back-Cover Texts + being @var{list}. +@end group +@end smallexample + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. + +@c Local Variables: +@c ispell-local-pdict: "ispell-dict" +@c End: + @@ -0,0 +1,153 @@ +/* Global declarations for the ed editor. */ +/* GNU ed - The GNU line editor. + Copyright (C) 1993, 1994 Andrew Moore, Talke Studio + Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 + Free Software Foundation, Inc. + + 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/>. +*/ + +#ifndef __cplusplus +enum Bool { false = 0, true = 1 }; +typedef enum Bool bool; +#endif + +enum Gflags + { + GLB = 0x01, /* global command */ + GLS = 0x02, /* list after command */ + GNP = 0x04, /* enumerate after command */ + GPR = 0x08, /* print after command */ + GSG = 0x10 /* global substitute */ + }; + + +typedef struct line /* Line node */ + { + struct line * q_forw; + struct line * q_back; + long pos; /* position of text in scratch buffer */ + int len; /* length of line */ + } +line_t; + + +typedef struct + { + enum { UADD = 0, UDEL = 1, UMOV = 2, VMOV = 3 } type; + line_t * head; /* head of list */ + line_t * tail; /* tail of list */ + } +undo_t; + +#ifndef max +#define max( a,b ) ( (( a ) > ( b )) ? ( a ) : ( b ) ) +#endif +#ifndef min +#define min( a,b ) ( (( a ) < ( b )) ? ( a ) : ( b ) ) +#endif + + +/* defined in buffer.c */ +bool append_lines( const char ** const ibufpp, const int addr, + const bool isglobal ); +bool close_sbuf( void ); +bool copy_lines( const int first_addr, const int second_addr, const int addr ); +int current_addr( void ); +int dec_addr( int addr ); +bool delete_lines( const int from, const int to, const bool isglobal ); +int get_line_node_addr( const line_t * const lp ); +char * get_sbuf_line( const line_t * const lp ); +int inc_addr( int addr ); +int inc_current_addr( void ); +bool init_buffers( void ); +bool isbinary( void ); +bool join_lines( const int from, const int to, const bool isglobal ); +int last_addr( void ); +bool modified( void ); +bool move_lines( const int first_addr, const int second_addr, const int addr, + const bool isglobal ); +bool newline_added( void ); +bool open_sbuf( void ); +int path_max( const char * filename ); +bool put_lines( const int addr ); +const char * put_sbuf_line( const char * const buf, const int size, + const int addr ); +line_t * search_line_node( const int addr ); +void set_binary( void ); +void set_current_addr( const int addr ); +void set_modified( const bool m ); +void set_newline_added( void ); +bool yank_lines( const int from, const int to ); +void clear_undo_stack( void ); +undo_t * push_undo_atom( const int type, const int from, const int to ); +void reset_undo_state( void ); +bool undo( const bool isglobal ); + +/* defined in global.c */ +void clear_active_list( void ); +const line_t * next_active_node( void ); +bool set_active_node( const line_t * const lp ); +void unset_active_nodes( const line_t * bp, const line_t * const ep ); + +/* defined in io.c */ +bool display_lines( int from, const int to, const int gflags ); +bool get_extended_line( const char ** const ibufpp, int * const lenp, + const bool strip_escaped_newlines ); +const char * get_tty_line( int * const sizep ); +int read_file( const char * const filename, const int addr ); +int write_file( const char * const filename, const char * const mode, + const int from, const int to ); + +/* defined in main.c */ +bool is_regular_file( const int fd ); +bool may_access_filename( const char * const name ); +bool restricted( void ); +bool scripted( void ); +void show_strerror( const char * const filename, const int errcode ); +bool traditional( void ); + +/* defined in main_loop.c */ +int main_loop( const bool loose ); +void set_def_filename( const char * const s ); +void set_error_msg( const char * msg ); +void set_prompt( const char * const s ); +void set_verbose( void ); +void unmark_line_node( const line_t * const lp ); + +/* defined in regex.c */ +bool build_active_list( const char ** const ibufpp, const int first_addr, + const int second_addr, const bool match ); +bool extract_subst_tail( const char ** const ibufpp, int * const gflagsp, + int * const snump, const bool isglobal ); +int next_matching_node_addr( const char ** const ibufpp, const bool forward ); +bool new_compiled_pattern( const char ** const ibufpp ); +bool prev_pattern( void ); +bool search_and_replace( const int first_addr, const int second_addr, + const int gflags, const int snum, const bool isglobal ); + +/* defined in signal.c */ +void disable_interrupts( void ); +void enable_interrupts( void ); +bool parse_int( int * const i, const char * const str, const char ** const tail ); +bool resize_buffer( char ** const buf, int * const size, const int min_size ); +bool resize_line_buffer( const line_t *** const buf, int * const size, + const int min_size ); +bool resize_undo_buffer( undo_t ** const buf, int * const size, + const int min_size ); +void set_signals( void ); +void set_window_lines( const int lines ); +const char * strip_escapes( const char * p ); +int window_columns( void ); +int window_lines( void ); diff --git a/global.c b/global.c new file mode 100644 index 0000000..c817268 --- /dev/null +++ b/global.c @@ -0,0 +1,87 @@ +/* global.c: global command routines for the ed line editor */ +/* GNU ed - The GNU line editor. + Copyright (C) 1993, 1994 Andrew Moore, Talke Studio + Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 + Free Software Foundation, Inc. + + 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/>. +*/ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "ed.h" + + +static const line_t **active_list = 0; /* list of lines active in a global command */ +static int active_size = 0; /* size (in bytes) of active_list */ +static int active_len = 0; /* number of lines in active_list */ +static int active_ptr = 0; /* active_list index ( non-decreasing ) */ +static int active_ndx = 0; /* active_list index ( modulo active_last ) */ + + +/* clear the global-active list */ +void clear_active_list( void ) + { + disable_interrupts(); + if( active_list ) free( active_list ); + active_list = 0; + active_size = active_len = active_ptr = active_ndx = 0; + enable_interrupts(); + } + + +/* return the next global-active line node */ +const line_t * next_active_node( void ) + { + while( active_ptr < active_len && !active_list[active_ptr] ) + ++active_ptr; + return ( active_ptr < active_len ) ? active_list[active_ptr++] : 0; + } + + +/* add a line node to the global-active list */ +bool set_active_node( const line_t * const lp ) + { + disable_interrupts(); + if( !resize_line_buffer( &active_list, &active_size, + ( active_len + 1 ) * sizeof (line_t **) ) ) + { + show_strerror( 0, errno ); set_error_msg( "Memory exhausted" ); + enable_interrupts(); + return false; + } + enable_interrupts(); + active_list[active_len++] = lp; + return true; + } + + +/* remove a range of lines from the global-active list */ +void unset_active_nodes( const line_t * bp, const line_t * const ep ) + { + while( bp != ep ) + { + int i; + for( i = 0; i < active_len; ++i ) + { + if( ++active_ndx >= active_len ) active_ndx = 0; + if( active_list[active_ndx] == bp ) + { active_list[active_ndx] = 0; break; } + } + bp = bp->q_forw; + } + } @@ -0,0 +1,332 @@ +/* io.c: i/o routines for the ed line editor */ +/* GNU ed - The GNU line editor. + Copyright (C) 1993, 1994 Andrew Moore, Talke Studio + Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 + Free Software Foundation, Inc. + + 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/>. +*/ + +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#include "ed.h" + + +/* print text to stdout */ +static void put_tty_line( const char * p, int len, const int gflags ) + { + const char escapes[] = "\a\b\f\n\r\t\v\\"; + const char escchars[] = "abfnrtv\\"; + int col = 0; + + if( gflags & GNP ) { printf( "%d\t", current_addr() ); col = 8; } + while( --len >= 0 ) + { + const unsigned char ch = *p++; + if( !( gflags & GLS ) ) putchar( ch ); + else + { + if( ++col > window_columns() ) { col = 1; fputs( "\\\n", stdout ); } + if( ch >= 32 && ch <= 126 && ch != '\\' ) putchar( ch ); + else + { + char * const p = strchr( escapes, ch ); + ++col; putchar('\\'); + if( ch && p ) putchar( escchars[p-escapes] ); + else + { + col += 2; + putchar( ( ( ch >> 6 ) & 7 ) + '0' ); + putchar( ( ( ch >> 3 ) & 7 ) + '0' ); + putchar( ( ch & 7 ) + '0' ); + } + } + } + } + if( !traditional() && ( gflags & GLS ) ) putchar('$'); + putchar('\n'); + } + + +/* print a range of lines to stdout */ +bool display_lines( int from, const int to, const int gflags ) + { + line_t * const ep = search_line_node( inc_addr( to ) ); + line_t * bp = search_line_node( from ); + + if( !from ) { set_error_msg( "Invalid address" ); return false; } + while( bp != ep ) + { + const char * const s = get_sbuf_line( bp ); + if( !s ) return false; + set_current_addr( from++ ); + put_tty_line( s, bp->len, gflags ); + bp = bp->q_forw; + } + return true; + } + + +/* return the parity of escapes at the end of a string */ +static bool trailing_escape( const char * const s, int len ) + { + bool odd_escape = false; + while( --len >= 0 && s[len] == '\\' ) odd_escape = !odd_escape; + return odd_escape; + } + + +/* If *ibufpp contains an escaped newline, get an extended line (one + with escaped newlines) from stdin */ +bool get_extended_line( const char ** const ibufpp, int * const lenp, + const bool strip_escaped_newlines ) + { + static char * buf = 0; + static int bufsz = 0; + int len; + + for( len = 0; (*ibufpp)[len++] != '\n'; ) ; + if( len < 2 || !trailing_escape( *ibufpp, len - 1 ) ) + { if( lenp ) *lenp = len; return true; } + if( !resize_buffer( &buf, &bufsz, len ) ) return false; + memcpy( buf, *ibufpp, len ); + --len; buf[len-1] = '\n'; /* strip trailing esc */ + if( strip_escaped_newlines ) --len; /* strip newline */ + while( true ) + { + int len2; + const char * const s = get_tty_line( &len2 ); + if( !s ) return false; + if( len2 == 0 || s[len2-1] != '\n' ) + { set_error_msg( "Unexpected end-of-file" ); return false; } + if( !resize_buffer( &buf, &bufsz, len + len2 ) ) return false; + memcpy( buf + len, s, len2 ); + len += len2; + if( len2 < 2 || !trailing_escape( buf, len - 1 ) ) break; + --len; buf[len-1] = '\n'; /* strip trailing esc */ + if( strip_escaped_newlines ) --len; /* strip newline */ + } + if( !resize_buffer( &buf, &bufsz, len + 1 ) ) return false; + buf[len] = 0; + *ibufpp = buf; + if( lenp ) *lenp = len; + return true; + } + + +/* Read a line of text from stdin. + Return pointer to buffer and line size (uncluding trailing newline + if it exists) */ +const char * get_tty_line( int * const sizep ) + { + static char * buf = 0; + static int bufsz = 0; + int i = 0, oi = -1; + + while( true ) + { + const int c = getchar(); + if( c == EOF ) + { + if( ferror( stdin ) ) + { + show_strerror( "stdin", errno ); set_error_msg( "Cannot read stdin" ); + clearerr( stdin ); if( sizep ) *sizep = 0; + return 0; + } + else + { + clearerr( stdin ); if( i != oi ) { oi = i; continue; } + if( i ) buf[i] = 0; if( sizep ) *sizep = i; + return buf; + } + } + else + { + if( !resize_buffer( &buf, &bufsz, i + 2 ) ) + { if( sizep ) *sizep = 0; return 0; } + buf[i++] = c; if( !c ) set_binary(); if( c != '\n' ) continue; + buf[i] = 0; if( sizep ) *sizep = i; + return buf; + } + } + } + + +/* Read a line of text from a stream. + Return pointer to buffer and line size (uncluding trailing newline + if it exists and is not added now) */ +static const char * read_stream_line( FILE * const fp, int * const sizep, + bool * const newline_added_nowp ) + { + static char * buf = 0; + static int bufsz = 0; + int c, i = 0; + + while( true ) + { + if( !resize_buffer( &buf, &bufsz, i + 2 ) ) return 0; + c = getc( fp ); if( c == EOF ) break; + buf[i++] = c; + if( !c ) set_binary(); else if( c == '\n' ) break; + } + buf[i] = 0; + if( c == EOF ) + { + if( ferror( fp ) ) + { + show_strerror( 0, errno ); + set_error_msg( "Cannot read input file" ); + return 0; + } + else if( i ) + { + buf[i] = '\n'; buf[i+1] = 0; *newline_added_nowp = true; + if( !isbinary() ) ++i; + } + } + *sizep = i; + return buf; + } + + +/* read a stream into the editor buffer; return total size of data read */ +static long read_stream( FILE * const fp, const int addr ) + { + line_t * lp = search_line_node( addr ); + undo_t * up = 0; + long total_size = 0; + const bool o_isbinary = isbinary(); + const bool appended = ( addr == last_addr() ); + bool newline_added_now = false; + + set_current_addr( addr ); + while( true ) + { + int size = 0; + const char * const s = read_stream_line( fp, &size, &newline_added_now ); + if( !s ) return -1; + if( size > 0 ) total_size += size; + else break; + disable_interrupts(); + if( !put_sbuf_line( s, size + newline_added_now, current_addr() ) ) + { enable_interrupts(); return -1; } + lp = lp->q_forw; + if( up ) up->tail = lp; + else + { + up = push_undo_atom( UADD, current_addr(), current_addr() ); + if( !up ) { enable_interrupts(); return -1; } + } + enable_interrupts(); + } + if( addr && appended && total_size && o_isbinary && newline_added() ) + fputs( "Newline inserted\n", stderr ); + else if( newline_added_now && ( !appended || !isbinary() ) ) + fputs( "Newline appended\n", stderr ); + if( isbinary() && !o_isbinary && newline_added_now && !appended ) + ++total_size; + if( !total_size ) newline_added_now = true; + if( appended && newline_added_now ) set_newline_added(); + return total_size; + } + + +/* read a named file/pipe into the buffer; return line count */ +int read_file( const char * const filename, const int addr ) + { + FILE * fp; + long size; + int ret; + + if( *filename == '!' ) fp = popen( filename + 1, "r" ); + else fp = fopen( strip_escapes( filename ), "r" ); + if( !fp ) + { + show_strerror( filename, errno ); + set_error_msg( "Cannot open input file" ); + return -1; + } + size = read_stream( fp, addr ); + if( size < 0 ) return -1; + if( *filename == '!' ) ret = pclose( fp ); else ret = fclose( fp ); + if( ret != 0 ) + { + show_strerror( filename, errno ); + set_error_msg( "Cannot close input file" ); + return -1; + } + if( !scripted() ) fprintf( stderr, "%lu\n", size ); + return current_addr() - addr; + } + + +/* write a range of lines to a stream */ +static long write_stream( FILE * const fp, int from, const int to ) + { + line_t * lp = search_line_node( from ); + long size = 0; + + while( from && from <= to ) + { + int len; + char * p = get_sbuf_line( lp ); + if( !p ) return -1; + len = lp->len; + if( from != last_addr() || !isbinary() || !newline_added() ) + p[len++] = '\n'; + size += len; + while( --len >= 0 ) + if( fputc( *p++, fp ) == EOF ) + { + show_strerror( 0, errno ); + set_error_msg( "Cannot write file" ); + return -1; + } + ++from; lp = lp->q_forw; + } + return size; + } + + +/* write a range of lines to a named file/pipe; return line count */ +int write_file( const char * const filename, const char * const mode, + const int from, const int to ) + { + FILE * fp; + long size; + int ret; + + if( *filename == '!' ) fp = popen( filename + 1, "w" ); + else fp = fopen( strip_escapes( filename ), mode ); + if( !fp ) + { + show_strerror( filename, errno ); + set_error_msg( "Cannot open output file" ); + return -1; + } + size = write_stream( fp, from, to ); + if( size < 0 ) return -1; + if( *filename == '!' ) ret = pclose( fp ); else ret = fclose( fp ); + if( ret != 0 ) + { + show_strerror( filename, errno ); + set_error_msg( "Cannot close output file" ); + return -1; + } + if( !scripted() ) fprintf( stderr, "%lu\n", size ); + return ( from && from <= to ) ? to - from + 1 : 0; + } @@ -0,0 +1,205 @@ +/* GNU ed - The GNU line editor. + Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 + Free Software Foundation, Inc. + + 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/>. +*/ +/* + Return values: 0 for a normal exit, 1 for environmental problems + (file not found, invalid flags, I/O errors, etc), 2 to indicate a + corrupt or invalid input file, 3 for an internal consistency error + (eg, bug) which caused ed to panic. +*/ +/* + * CREDITS + * + * This program is based on the editor algorithm described in + * Brian W. Kernighan and P. J. Plauger's book "Software Tools + * in Pascal," Addison-Wesley, 1981. + * + * The buffering algorithm is attributed to Rodney Ruddock of + * the University of Guelph, Guelph, Ontario. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <locale.h> + +#include "carg_parser.h" +#include "ed.h" + + +static const char * const Program_name = "GNU Ed"; +static const char * const program_name = "ed"; +static const char * const program_year = "2012"; +static const char * invocation_name = 0; + +static bool restricted_ = false; /* if set, run in restricted mode */ +static bool scripted_ = false; /* if set, suppress diagnostics */ +static bool traditional_ = false; /* if set, be backwards compatible */ + + +bool restricted( void ) { return restricted_; } +bool scripted( void ) { return scripted_; } +bool traditional( void ) { return traditional_; } + + +static void show_help( void ) + { + printf( "%s - The GNU line editor.\n", Program_name ); + printf( "\nUsage: %s [options] [file]\n", invocation_name ); + printf( "\nOptions:\n" + " -h, --help display this help and exit\n" + " -V, --version output version information and exit\n" + " -G, --traditional run in compatibility mode\n" + " -l, --loose-exit-status exit with 0 status even if a command fails\n" + " -p, --prompt=STRING use STRING as an interactive prompt\n" + " -r, --restricted run in restricted mode\n" + " -s, --quiet, --silent suppress diagnostics\n" + " -v, --verbose be verbose\n" + "Start edit by reading in `file' if given.\n" + "If `file' begins with a `!', read output of shell command.\n" + "\nReport bugs to <bug-ed@gnu.org>.\n" + "Ed home page: http://www.gnu.org/software/ed/ed.html\n" + "General help using GNU software: http://www.gnu.org/gethelp\n" ); + } + + +static void show_version( void ) + { + printf( "%s %s\n", Program_name, PROGVERSION ); + printf( "Copyright (C) 1994 Andrew L. Moore.\n" + "Copyright (C) %s Free Software Foundation, Inc.\n", program_year ); + printf( "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n" ); + } + + +void show_strerror( const char * const filename, const int errcode ) + { + if( !scripted_ ) + { + if( filename && filename[0] != 0 ) + fprintf( stderr, "%s: ", filename ); + fprintf( stderr, "%s\n", strerror( errcode ) ); + } + } + + +static void show_error( const char * const msg, const int errcode, const bool help ) + { + if( msg && msg[0] ) + { + fprintf( stderr, "%s: %s", program_name, msg ); + if( errcode > 0 ) fprintf( stderr, ": %s", strerror( errcode ) ); + fprintf( stderr, "\n" ); + } + if( help && invocation_name && invocation_name[0] ) + fprintf( stderr, "Try `%s --help' for more information.\n", invocation_name ); + } + + +/* return true if file descriptor is a regular file */ +bool is_regular_file( const int fd ) + { + struct stat st; + return ( fstat( fd, &st ) != 0 || S_ISREG( st.st_mode ) ); + } + + +bool may_access_filename( const char * const name ) + { + if( restricted_ && + ( *name == '!' || !strcmp( name, ".." ) || strchr( name, '/' ) ) ) + { + set_error_msg( "Shell access restricted" ); + return false; + } + return true; + } + + +int main( const int argc, const char * const argv[] ) + { + int argind; + bool loose = false; + const struct ap_Option options[] = + { + { 'G', "traditional", ap_no }, + { 'h', "help", ap_no }, + { 'l', "loose-exit-status", ap_no }, + { 'p', "prompt", ap_yes }, + { 'r', "restricted", ap_no }, + { 's', "quiet", ap_no }, + { 's', "silent", ap_no }, + { 'v', "verbose", ap_no }, + { 'V', "version", ap_no }, + { 0 , 0, ap_no } }; + + struct Arg_parser parser; + + if( !ap_init( &parser, argc, argv, options, 0 ) ) + { show_error( "Memory exhausted.", 0, false ); return 1; } + if( ap_error( &parser ) ) /* bad option */ + { show_error( ap_error( &parser ), 0, true ); return 1; } + invocation_name = argv[0]; + + for( argind = 0; argind < ap_arguments( &parser ); ++argind ) + { + const int code = ap_code( &parser, argind ); + const char * const arg = ap_argument( &parser, argind ); + if( !code ) break; /* no more options */ + switch( code ) + { + case 'G': traditional_ = true; break; /* backward compatibility */ + case 'h': show_help(); return 0; + case 'l': loose = true; break; + case 'p': set_prompt( arg ); break; + case 'r': restricted_ = true; break; + case 's': scripted_ = true; break; + case 'v': set_verbose(); break; + case 'V': show_version(); return 0; + default : show_error( "internal error: uncaught option.", 0, false ); + return 3; + } + } /* end process options */ + setlocale( LC_ALL, "" ); + if( !init_buffers() ) return 1; + + while( argind < ap_arguments( &parser ) ) + { + const char * const arg = ap_argument( &parser, argind ); + if( !strcmp( arg, "-" ) ) { scripted_ = true; ++argind; continue; } + if( may_access_filename( arg ) ) + { + if( read_file( arg, 0 ) < 0 && is_regular_file( 0 ) ) + return 2; + else if( arg[0] != '!' ) set_def_filename( arg ); + } + else + { + fputs( "?\n", stderr ); + if( arg[0] ) set_error_msg( "Invalid filename" ); + if( is_regular_file( 0 ) ) return 2; + } + break; + } + ap_free( &parser ); + + return main_loop( loose ); + } diff --git a/main_loop.c b/main_loop.c new file mode 100644 index 0000000..fedfff5 --- /dev/null +++ b/main_loop.c @@ -0,0 +1,762 @@ +/* GNU ed - The GNU line editor. + Copyright (C) 1993, 1994 Andrew Moore, Talke Studio + Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 + Free Software Foundation, Inc. + + 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/>. +*/ + +#include <ctype.h> +#include <setjmp.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "ed.h" + + +enum Status { QUIT = -1, ERR = -2, EMOD = -3, FATAL = -4 }; + +static char def_filename[1024] = ""; /* default filename */ +static char errmsg[80] = ""; /* error message buffer */ +static char prompt_str[80] = "*"; /* command prompt */ +static int first_addr = 0, second_addr = 0; +static bool prompt_on = false; /* if set, show command prompt */ +static bool verbose = false; /* if set, print all error messages */ + + +void set_def_filename( const char * const s ) + { + strncpy( def_filename, s, sizeof def_filename ); + def_filename[sizeof(def_filename)-1] = 0; + } + +void set_error_msg( const char * msg ) + { + if( !msg ) msg = ""; + strncpy( errmsg, msg, sizeof errmsg ); + errmsg[sizeof(errmsg)-1] = 0; + } + +void set_prompt( const char * const s ) + { + prompt_on = true; + strncpy( prompt_str, s, sizeof prompt_str ); + prompt_str[sizeof(prompt_str)-1] = 0; + } + +void set_verbose( void ) { verbose = true; } + + +static const line_t * mark[26]; /* line markers */ +static int markno; /* line marker count */ + +static bool mark_line_node( const line_t * const lp, int c ) + { + c -= 'a'; + if( c < 0 || c >= 26 ) + { set_error_msg( "Invalid mark character" ); return false; } + if( !mark[c] ) ++markno; + mark[c] = lp; + return true; + } + + +void unmark_line_node( const line_t * const lp ) + { + int i; + for( i = 0; markno && i < 26; ++i ) + if( mark[i] == lp ) + { mark[i] = 0; --markno; } + } + + +/* return address of a marked line */ +static int get_marked_node_addr( int c ) + { + c -= 'a'; + if( c < 0 || c >= 26 ) + { set_error_msg( "Invalid mark character" ); return -1; } + return get_line_node_addr( mark[c]); + } + + +/* Return pointer to copy of shell command in the command buffer */ +static const char * get_shell_command( const char ** const ibufpp ) + { + static char * buf = 0; + static int bufsz = 0; + static char * shcmd = 0; /* shell command buffer */ + static int shcmdsz = 0; /* shell command buffer size */ + static int shcmdlen = 0; /* shell command length */ + const char * p; /* substitution char pointer */ + int i = 0, len; + + if( restricted() ) { set_error_msg( "Shell access restricted" ); return 0; } + if( !get_extended_line( ibufpp, &len, true ) ) return 0; + p = *ibufpp; + if( !resize_buffer( &buf, &bufsz, len + 1 ) ) return 0; + buf[i++] = '!'; /* prefix command w/ bang */ + while( **ibufpp != '\n' ) + { + if( **ibufpp == '!' ) + { + if( p != *ibufpp ) + { + if( !resize_buffer( &buf, &bufsz, i + 1 ) ) return 0; + buf[i++] = *(*ibufpp)++; + } + else if( !shcmd || ( traditional() && !*( shcmd + 1 ) ) ) + { set_error_msg( "No previous command" ); return 0; } + else + { + if( !resize_buffer( &buf, &bufsz, i + shcmdlen ) ) return 0; + for( p = shcmd + 1; p < shcmd + shcmdlen; ) buf[i++] = *p++; + p = (*ibufpp)++; + } + } + else if( **ibufpp == '%' ) + { + if( !def_filename[0] ) + { set_error_msg( "No current filename" ); return 0; } + p = strip_escapes( def_filename ); + len = strlen( p ); + if( !resize_buffer( &buf, &bufsz, i + len ) ) return 0; + while( len-- ) buf[i++] = *p++; + p = (*ibufpp)++; + } + else + { + if( !resize_buffer( &buf, &bufsz, i + 2 ) ) return 0; + buf[i++] = **ibufpp; + if( *(*ibufpp)++ == '\\' ) buf[i++] = *(*ibufpp)++; + } + } + while( **ibufpp == '\n' ) ++*ibufpp; /* skip newline */ + if( !resize_buffer( &shcmd, &shcmdsz, i + 1 ) ) return 0; + memcpy( shcmd, buf, i ); + shcmdlen = i; shcmd[i] = 0; + if( *p == '!' || *p == '%' ) printf( "%s\n", shcmd + 1 ); + return shcmd; + } + + +static const char * skip_blanks( const char * p ) + { + while( isspace( (unsigned char)*p ) && *p != '\n' ) ++p; + return p; + } + + +/* Return pointer to copy of filename in the command buffer */ +static const char * get_filename( const char ** const ibufpp ) + { + static char * buf = 0; + static int bufsz = 0; + const int pmax = path_max( 0 ); + int n; + + *ibufpp = skip_blanks( *ibufpp ); + if( **ibufpp != '\n' ) + { + int size = 0; + if( !get_extended_line( ibufpp, &size, true ) ) return 0; + if( **ibufpp == '!' ) + { + ++*ibufpp; + return get_shell_command( ibufpp ); + } + else if( size > pmax ) + { set_error_msg( "Filename too long" ); return 0; } + } + else if( !traditional() && !def_filename[0] ) + { set_error_msg( "No current filename" ); return 0; } + if( !resize_buffer( &buf, &bufsz, pmax + 1 ) ) return 0; + for( n = 0; **ibufpp != '\n'; ++n, ++*ibufpp ) buf[n] = **ibufpp; + buf[n] = 0; + while( **ibufpp == '\n' ) ++*ibufpp; /* skip newline */ + return ( may_access_filename( buf ) ? buf : 0 ); + } + + +static void invalid_address( void ) { set_error_msg( "Invalid address" ); } + + +/* return the next line address in the command buffer */ +static int next_addr( const char ** const ibufpp, int * const addr_cnt ) + { + const char * const s = *ibufpp = skip_blanks( *ibufpp ); + int addr = current_addr(); + bool first = true; + + while( true ) + { + int n; + const unsigned char ch = **ibufpp; + if( isdigit( ch ) ) + { + if( !first ) { invalid_address(); return -2; }; + if( !parse_int( &addr, *ibufpp, ibufpp ) ) return -2; + } + else switch( ch ) + { + case '+': + case '\t': + case ' ': + case '-': *ibufpp = skip_blanks( ++*ibufpp ); + if( isdigit( (unsigned char)**ibufpp ) ) + { + if( !parse_int( &n, *ibufpp, ibufpp ) ) return -2; + addr += ( ( ch == '-' ) ? -n : n ); + } + else if( ch == '+' ) ++addr; + else if( ch == '-' ) --addr; + break; + case '.': + case '$': if( !first ) { invalid_address(); return -2; }; + ++*ibufpp; + addr = ( ( ch == '.' ) ? current_addr() : last_addr() ); + break; + case '/': + case '?': if( !first ) { invalid_address(); return -2; }; + addr = next_matching_node_addr( ibufpp, ch == '/' ); + if( addr < 0 ) return -2; + if( ch == **ibufpp ) ++*ibufpp; + break; + case '\'':if( !first ) { invalid_address(); return -2; }; + ++*ibufpp; + addr = get_marked_node_addr( *(*ibufpp)++ ); + if( addr < 0 ) return -2; + break; + case '%': + case ',': + case ';': if( first ) + { + ++*ibufpp; ++*addr_cnt; + second_addr = ( ( ch == ';' ) ? current_addr() : 1 ); + addr = last_addr(); + break; + } /* FALL THROUGH */ + default : if( *ibufpp == s ) return -1; /* EOF */ + if( addr < 0 || addr > last_addr() ) + { invalid_address(); return -2; } + ++*addr_cnt; return addr; + } + first = false; + } + } + + +/* get line addresses from the command buffer until an invalid address + is seen. Return number of addresses read */ +static int extract_addr_range( const char ** const ibufpp ) + { + int addr; + int addr_cnt = 0; + + first_addr = second_addr = current_addr(); + while( true ) + { + addr = next_addr( ibufpp, &addr_cnt ); + if( addr < 0 ) break; + first_addr = second_addr; second_addr = addr; + if( **ibufpp != ',' && **ibufpp != ';' ) break; + if( **ibufpp == ';' ) set_current_addr( addr ); + ++*ibufpp; + } + if( addr_cnt == 1 || second_addr != addr ) first_addr = second_addr; + return ( ( addr != -2 ) ? addr_cnt : -1 ); + } + + +/* get a valid address from the command buffer */ +static bool get_third_addr( const char ** const ibufpp, int * const addr ) + { + const int old1 = first_addr; + const int old2 = second_addr; + int addr_cnt = extract_addr_range( ibufpp ); + + if( addr_cnt < 0 ) return false; + if( traditional() && addr_cnt == 0 ) + { set_error_msg( "Destination expected" ); return false; } + if( second_addr < 0 || second_addr > last_addr() ) + { invalid_address(); return false; } + *addr = second_addr; + first_addr = old1; second_addr = old2; + return true; + } + + +/* return true if address range is valid */ +static bool check_addr_range( const int n, const int m, const int addr_cnt ) + { + if( addr_cnt == 0 ) + { + first_addr = n; + second_addr = m; + } + if( first_addr < 1 || first_addr > second_addr || second_addr > last_addr() ) + { invalid_address(); return false; } + return true; + } + + +/* return true if current address is valid */ +static bool check_current_addr( const int addr_cnt ) + { + return check_addr_range( current_addr(), current_addr(), addr_cnt ); + } + + +/* verify the command suffix in the command buffer */ +static bool get_command_suffix( const char ** const ibufpp, + int * const gflagsp ) + { + while( true ) + { + const char ch = **ibufpp; + if( ch == 'l' ) *gflagsp |= GLS; + else if( ch == 'n' ) *gflagsp |= GNP; + else if( ch == 'p' ) *gflagsp |= GPR; + else break; + ++*ibufpp; + } + if( *(*ibufpp)++ != '\n' ) + { set_error_msg( "Invalid command suffix" ); return false; } + return true; + } + + +static bool unexpected_address( const int addr_cnt ) + { + if( addr_cnt > 0 ) { set_error_msg( "Unexpected address" ); return true; } + return false; + } + +static bool unexpected_command_suffix( const unsigned char ch ) + { + if( !isspace( ch ) ) + { set_error_msg( "Unexpected command suffix" ); return true; } + return false; + } + + +static bool command_s( const char ** const ibufpp, int * const gflagsp, + const int addr_cnt, const bool isglobal ) + { + static int gflags = 0; + static int snum = 0; + enum Sflags { + SGG = 0x01, /* complement previous global substitute suffix */ + SGP = 0x02, /* complement previous print suffix */ + SGR = 0x04, /* use last regex instead of last pat */ + SGF = 0x08 /* repeat last substitution */ + } sflags = 0; + + do { + if( isdigit( (unsigned char)**ibufpp ) ) + { + if( !parse_int( &snum, *ibufpp, ibufpp ) ) return false; + sflags |= SGF; gflags &= ~GSG; /* override GSG */ + } + else switch( **ibufpp ) + { + case '\n':sflags |= SGF; break; + case 'g': sflags |= SGG; ++*ibufpp; break; + case 'p': sflags |= SGP; ++*ibufpp; break; + case 'r': sflags |= SGR; ++*ibufpp; break; + default : if( sflags ) + { set_error_msg( "Invalid command suffix" ); return false; } + } + } + while( sflags && **ibufpp != '\n' ); + if( sflags && !prev_pattern() ) + { set_error_msg( "No previous substitution" ); return false; } + if( sflags & SGG ) snum = 0; /* override numeric arg */ + if( **ibufpp != '\n' && (*ibufpp)[1] == '\n' ) + { set_error_msg( "Invalid pattern delimiter" ); return false; } + if( ( !sflags || ( sflags & SGR ) ) && !new_compiled_pattern( ibufpp ) ) + return false; + if( !sflags && !extract_subst_tail( ibufpp, &gflags, &snum, isglobal ) ) + return false; + if( isglobal ) gflags |= GLB; + else gflags &= ~GLB; + if( sflags & SGG ) gflags ^= GSG; + if( sflags & SGP ) { gflags ^= GPR; gflags &= ~( GLS | GNP ); } + switch( **ibufpp ) + { + case 'l': gflags |= GLS; ++*ibufpp; break; + case 'n': gflags |= GNP; ++*ibufpp; break; + case 'p': gflags |= GPR; ++*ibufpp; break; + } + if( !check_current_addr( addr_cnt ) || + !get_command_suffix( ibufpp, gflagsp ) ) return false; + if( !isglobal ) clear_undo_stack(); + if( !search_and_replace( first_addr, second_addr, gflags, snum, isglobal ) ) + return false; + if( ( gflags & ( GPR | GLS | GNP ) ) && + !display_lines( current_addr(), current_addr(), gflags ) ) + return false; + return true; + } + + +static bool exec_global( const char ** const ibufpp, const int gflags, + const bool interactive ); + +/* execute the next command in command buffer; return error status */ +static int exec_command( const char ** const ibufpp, const int prev_status, + const bool isglobal ) + { + const char * fnp; + int gflags = 0; + int addr, c, n; + const int addr_cnt = extract_addr_range( ibufpp ); + + if( addr_cnt < 0 ) return ERR; + *ibufpp = skip_blanks( *ibufpp ); + c = *(*ibufpp)++; + switch( c ) + { + case 'a': if( !get_command_suffix( ibufpp, &gflags ) ) return ERR; + if( !isglobal ) clear_undo_stack(); + if( !append_lines( ibufpp, second_addr, isglobal ) ) return ERR; + break; + case 'c': if( first_addr == 0 ) first_addr = 1; + if( second_addr == 0 ) second_addr = 1; + if( !check_current_addr( addr_cnt ) || + !get_command_suffix( ibufpp, &gflags ) ) return ERR; + if( !isglobal ) clear_undo_stack(); + if( !delete_lines( first_addr, second_addr, isglobal ) || + !append_lines( ibufpp, current_addr(), isglobal ) ) return ERR; + break; + case 'd': if( !check_current_addr( addr_cnt ) || + !get_command_suffix( ibufpp, &gflags ) ) return ERR; + if( !isglobal ) clear_undo_stack(); + if( !delete_lines( first_addr, second_addr, isglobal ) ) return ERR; + inc_current_addr(); + break; + case 'e': if( modified() && !scripted() && prev_status != EMOD ) + return EMOD; /* fall through */ + case 'E': if( unexpected_address( addr_cnt ) || + unexpected_command_suffix( **ibufpp ) ) return ERR; + fnp = get_filename( ibufpp ); + if( !fnp || !delete_lines( 1, last_addr(), isglobal ) || + !close_sbuf() ) return ERR; + if( !open_sbuf() ) return FATAL; + if( fnp[0] && fnp[0] != '!' ) set_def_filename( fnp ); + if( traditional() && !fnp[0] && !def_filename[0] ) + { set_error_msg( "No current filename" ); return ERR; } + if( read_file( fnp[0] ? fnp : def_filename, 0 ) < 0 ) + return ERR; + reset_undo_state(); set_modified( false ); + break; + case 'f': if( unexpected_address( addr_cnt ) || + unexpected_command_suffix( **ibufpp ) ) return ERR; + fnp = get_filename( ibufpp ); + if( !fnp ) return ERR; + if( fnp[0] == '!' ) + { set_error_msg( "Invalid redirection" ); return ERR; } + if( fnp[0] ) set_def_filename( fnp ); + printf( "%s\n", strip_escapes( def_filename ) ); + break; + case 'g': + case 'v': + case 'G': + case 'V': if( isglobal ) + { set_error_msg( "Cannot nest global commands" ); return ERR; } + n = ( c == 'g' || c == 'G' ); /* mark matching lines */ + if( !check_addr_range( 1, last_addr(), addr_cnt ) || + !build_active_list( ibufpp, first_addr, second_addr, n ) ) + return ERR; + n = ( c == 'G' || c == 'V' ); /* interactive */ + if( ( n && !get_command_suffix( ibufpp, &gflags ) ) || + !exec_global( ibufpp, gflags, n ) ) + return ERR; + break; + case 'h': + case 'H': if( unexpected_address( addr_cnt ) || + !get_command_suffix( ibufpp, &gflags ) ) return ERR; + if( c == 'H' ) verbose = !verbose; + if( ( c == 'h' || verbose ) && errmsg[0] ) + fprintf( stderr, "%s\n", errmsg ); + break; + case 'i': if( second_addr == 0 ) second_addr = 1; + if( !get_command_suffix( ibufpp, &gflags ) ) return ERR; + if( !isglobal ) clear_undo_stack(); + if( !append_lines( ibufpp, second_addr - 1, isglobal ) ) + return ERR; + break; + case 'j': if( !check_addr_range( current_addr(), current_addr() + 1, addr_cnt ) || + !get_command_suffix( ibufpp, &gflags ) ) return ERR; + if( !isglobal ) clear_undo_stack(); + if( first_addr != second_addr && + !join_lines( first_addr, second_addr, isglobal ) ) return ERR; + break; + case 'k': n = *(*ibufpp)++; + if( second_addr == 0 ) { invalid_address(); return ERR; } + if( !get_command_suffix( ibufpp, &gflags ) || + !mark_line_node( search_line_node( second_addr ), n ) ) + return ERR; + break; + case 'l': + case 'n': + case 'p': if( c == 'l' ) n = GLS; else if( c == 'n' ) n = GNP; else n = GPR; + if( !check_current_addr( addr_cnt ) || + !get_command_suffix( ibufpp, &gflags ) || + !display_lines( first_addr, second_addr, gflags | n ) ) + return ERR; + gflags = 0; + break; + case 'm': if( !check_current_addr( addr_cnt ) || + !get_third_addr( ibufpp, &addr ) ) return ERR; + if( addr >= first_addr && addr < second_addr ) + { set_error_msg( "Invalid destination" ); return ERR; } + if( !get_command_suffix( ibufpp, &gflags ) ) return ERR; + if( !isglobal ) clear_undo_stack(); + if( !move_lines( first_addr, second_addr, addr, isglobal ) ) + return ERR; + break; + case 'P': + case 'q': + case 'Q': if( unexpected_address( addr_cnt ) || + !get_command_suffix( ibufpp, &gflags ) ) return ERR; + if( c == 'P' ) prompt_on = !prompt_on; + else if( modified() && !scripted() && c == 'q' && + prev_status != EMOD ) return EMOD; + else return QUIT; + break; + case 'r': if( unexpected_command_suffix( **ibufpp ) ) return ERR; + if( addr_cnt == 0 ) second_addr = last_addr(); + fnp = get_filename( ibufpp ); + if( !fnp ) return ERR; + if( !isglobal ) clear_undo_stack(); + if( !def_filename[0] && fnp[0] != '!' ) set_def_filename( fnp ); + if( traditional() && !fnp[0] && !def_filename[0] ) + { set_error_msg( "No current filename" ); return ERR; } + addr = read_file( fnp[0] ? fnp : def_filename, second_addr ); + if( addr < 0 ) return ERR; + if( addr ) set_modified( true ); + break; + case 's': if( !command_s( ibufpp, &gflags, addr_cnt, isglobal ) ) + return ERR; + break; + case 't': if( !check_current_addr( addr_cnt ) || + !get_third_addr( ibufpp, &addr ) || + !get_command_suffix( ibufpp, &gflags ) ) return ERR; + if( !isglobal ) clear_undo_stack(); + if( !copy_lines( first_addr, second_addr, addr ) ) return ERR; + break; + case 'u': if( unexpected_address( addr_cnt ) || + !get_command_suffix( ibufpp, &gflags ) || + !undo( isglobal ) ) return ERR; + break; + case 'w': + case 'W': n = **ibufpp; + if( n == 'q' || n == 'Q' ) ++*ibufpp; + if( unexpected_command_suffix( **ibufpp ) ) return ERR; + fnp = get_filename( ibufpp ); + if( !fnp ) return ERR; + if( addr_cnt == 0 && last_addr() == 0 ) + first_addr = second_addr = 0; + else if( !check_addr_range( 1, last_addr(), addr_cnt ) ) + return ERR; + if( !def_filename[0] && fnp[0] != '!' ) set_def_filename( fnp ); + if( traditional() && !fnp[0] && !def_filename[0] ) + { set_error_msg( "No current filename" ); return ERR; } + addr = write_file( fnp[0] ? fnp : def_filename, + ( c == 'W' ) ? "a" : "w", first_addr, second_addr ); + if( addr < 0 ) return ERR; + if( addr == last_addr() ) set_modified( false ); + else if( modified() && !scripted() && n == 'q' && + prev_status != EMOD ) return EMOD; + if( n == 'q' || n == 'Q' ) return QUIT; + break; + case 'x': if( second_addr < 0 || last_addr() < second_addr ) + { invalid_address(); return ERR; } + if( !get_command_suffix( ibufpp, &gflags ) ) return ERR; + if( !isglobal ) clear_undo_stack(); + if( !put_lines( second_addr ) ) return ERR; + break; + case 'y': if( !check_current_addr( addr_cnt ) || + !get_command_suffix( ibufpp, &gflags ) || + !yank_lines( first_addr, second_addr ) ) return ERR; + break; + case 'z': first_addr = 1; + if( !check_addr_range( first_addr, current_addr() + + ( traditional() || !isglobal ), addr_cnt ) ) + return ERR; + if( **ibufpp > '0' && **ibufpp <= '9' ) + { if( parse_int( &n, *ibufpp, ibufpp ) ) set_window_lines( n ); + else return ERR; } + if( !get_command_suffix( ibufpp, &gflags ) || + !display_lines( second_addr, min( last_addr(), second_addr + window_lines() ), + gflags ) ) + return ERR; + gflags = 0; + break; + case '=': if( !get_command_suffix( ibufpp, &gflags ) ) return ERR; + printf( "%d\n", addr_cnt ? second_addr : last_addr() ); + break; + case '!': if( unexpected_address( addr_cnt ) ) return ERR; + fnp = get_shell_command( ibufpp ); + if( !fnp ) return ERR; + if( system( fnp + 1 ) < 0 ) + { set_error_msg( "Can't create shell process" ); return ERR; } + if( !scripted() ) printf( "!\n" ); + break; + case '\n': first_addr = 1; + if( !check_addr_range( first_addr, current_addr() + + ( traditional() || !isglobal ), addr_cnt ) || + !display_lines( second_addr, second_addr, 0 ) ) + return ERR; + break; + case '#': while( *(*ibufpp)++ != '\n' ) ; + break; + default : set_error_msg( "Unknown command" ); return ERR; + } + if( gflags && !display_lines( current_addr(), current_addr(), gflags ) ) + return ERR; + return 0; + } + + +/* apply command list in the command buffer to the active lines in a + range; return false if error */ +static bool exec_global( const char ** const ibufpp, const int gflags, + const bool interactive ) + { + static char * buf = 0; + static int bufsz = 0; + const char * cmd = 0; + + if( !interactive ) + { + if( traditional() && !strcmp( *ibufpp, "\n" ) ) + cmd = "p\n"; /* null cmd_list == `p' */ + else + { + if( !get_extended_line( ibufpp, 0, false ) ) return false; + cmd = *ibufpp; + } + } + clear_undo_stack(); + while( true ) + { + const line_t * const lp = next_active_node(); + if( !lp ) break; + set_current_addr( get_line_node_addr( lp ) ); + if( current_addr() < 0 ) return false; + if( interactive ) + { + /* print current_addr; get a command in global syntax */ + int len; + if( !display_lines( current_addr(), current_addr(), gflags ) ) + return false; + do { *ibufpp = get_tty_line( &len ); } + while( *ibufpp && len > 0 && (*ibufpp)[len-1] != '\n' ); + if( !*ibufpp ) return false; + if( len == 0 ) + { set_error_msg( "Unexpected end-of-file" ); return false; } + if( len == 1 && !strcmp( *ibufpp, "\n" ) ) continue; + if( len == 2 && !strcmp( *ibufpp, "&\n" ) ) + { if( !cmd ) { set_error_msg( "No previous command" ); return false; } } + else + { + if( !get_extended_line( ibufpp, &len, false ) || + !resize_buffer( &buf, &bufsz, len + 1 ) ) return false; + memcpy( buf, *ibufpp, len + 1 ); + cmd = buf; + } + } + *ibufpp = cmd; + while( **ibufpp ) if( exec_command( ibufpp, 0, true ) < 0 ) return false; + } + return true; + } + + +int main_loop( const bool loose ) + { + extern jmp_buf jmp_state; + const char * ibufp; /* pointer to command buffer */ + volatile int err_status = 0; /* program exit status */ + volatile int linenum = 0; /* script line number */ + int len, status; + + disable_interrupts(); + set_signals(); + status = setjmp( jmp_state ); + if( !status ) enable_interrupts(); + else { status = -1; fputs( "\n?\n", stderr ); set_error_msg( "Interrupt" ); } + + while( true ) + { + fflush( stdout ); + if( status < 0 && verbose ) + { fprintf( stderr, "%s\n", errmsg ); fflush( stderr ); } + if( prompt_on ) { printf( "%s", prompt_str ); fflush( stdout ); } + ibufp = get_tty_line( &len ); + if( !ibufp ) return err_status; + if( !len ) + { + if( !modified() || scripted() ) return err_status; + fputs( "?\n", stderr ); set_error_msg( "Warning: buffer modified" ); + if( is_regular_file( 0 ) ) + { + if( verbose ) fprintf( stderr, "script, line %d: %s\n", linenum, errmsg ); + return 2; + } + set_modified( false ); status = EMOD; continue; + } + else if( ibufp[len-1] != '\n' ) /* discard line */ + { set_error_msg( "Unexpected end-of-file" ); status = ERR; continue; } + else ++linenum; + status = exec_command( &ibufp, status, false ); + if( status == 0 ) continue; + if( status == QUIT ) return err_status; + if( status == EMOD ) + { + fputs( "?\n", stderr ); /* give warning */ + set_error_msg( "Warning: buffer modified" ); + if( is_regular_file( 0 ) ) + { + if( verbose ) + fprintf( stderr, "script, line %d: %s\n", linenum, errmsg ); + return 1; + } + } + else if( status == FATAL ) + { + if( verbose ) + { + if( is_regular_file( 0 ) ) + fprintf( stderr, "script, line %d: %s\n", linenum, errmsg ); + else fprintf( stderr, "%s\n", errmsg ); + } + return 1; + } + else + { + fputs( "?\n", stderr ); /* give warning */ + if( is_regular_file( 0 ) ) + { + if( verbose ) + fprintf( stderr, "script, line %d: %s\n", linenum, errmsg ); + return 1; + } + } + if( !loose ) err_status = 1; + } + } @@ -0,0 +1,3 @@ +#! /bin/sh +bindir=`echo "$0" | sed -e 's,[^/]*$,,'` +exec "${bindir}"ed --restricted "$@" @@ -0,0 +1,403 @@ +/* regex.c: regular expression interface routines for the ed line editor. */ +/* GNU ed - The GNU line editor. + Copyright (C) 1993, 1994 Andrew Moore, Talke Studio + Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 + Free Software Foundation, Inc. + + 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/>. +*/ + +#include <stddef.h> +#include <errno.h> +#include <sys/types.h> +#include <regex.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "ed.h" + + +static regex_t * global_pat = 0; +static bool patlock = false; /* if set, pattern not freed by get_compiled_pattern */ + +static char * stbuf = 0; /* substitution template buffer */ +static int stbufsz = 0; /* substitution template buffer size */ +static int stlen = 0; /* substitution template length */ + +static char * rbuf = 0; /* replace_matching_text buffer */ +static int rbufsz = 0; /* replace_matching_text buffer size */ + + +bool prev_pattern( void ) { return global_pat != 0; } + + +/* translate characters in a string */ +static void translit_text( char * p, int len, const char from, const char to ) + { + while( --len >= 0 ) + { + if( *p == from ) *p = to; + ++p; + } + } + + +/* overwrite newlines with ASCII NULs */ +static void newline_to_nul( char * const s, const int len ) + { translit_text( s, len, '\n', '\0' ); } + +/* overwrite ASCII NULs with newlines */ +static void nul_to_newline( char * const s, const int len ) + { translit_text( s, len, '\0', '\n' ); } + + +/* expand a POSIX character class */ +static const char * parse_char_class( const char * p ) + { + char c, d; + + if( *p == '^' ) ++p; + if( *p == ']' ) ++p; + for( ; *p != ']' && *p != '\n'; ++p ) + if( *p == '[' && ( ( d = p[1] ) == '.' || d == ':' || d == '=' ) ) + for( ++p, c = *++p; *p != ']' || c != d; ++p ) + if( ( c = *p ) == '\n' ) + return 0; + return ( ( *p == ']' ) ? p : 0 ); + } + + +/* copy a pattern string from the command buffer; return pointer to the copy */ +static char * extract_pattern( const char ** const ibufpp, const char delimiter ) + { + static char * buf = 0; + static int bufsz = 0; + const char * nd = *ibufpp; + int len; + + while( *nd != delimiter && *nd != '\n' ) + { + if( *nd == '[' ) + { + nd = parse_char_class( ++nd ); + if( !nd ) { set_error_msg( "Unbalanced brackets ([])" ); return 0; } + } + else if( *nd == '\\' && *++nd == '\n' ) + { set_error_msg( "Trailing backslash (\\)" ); return 0; } + ++nd; + } + len = nd - *ibufpp; + if( !resize_buffer( &buf, &bufsz, len + 1 ) ) return 0; + memcpy( buf, *ibufpp, len ); + buf[len] = 0; + *ibufpp = nd; + if( isbinary() ) nul_to_newline( buf, len ); + return buf; + } + + +/* return pointer to compiled pattern from command buffer */ +static regex_t * get_compiled_pattern( const char ** const ibufpp ) + { + static regex_t * exp = 0; + const char * exps; + const char delimiter = **ibufpp; + int n; + + if( delimiter == ' ' ) + { set_error_msg( "Invalid pattern delimiter" ); return 0; } + if( delimiter == '\n' || *++*ibufpp == '\n' || **ibufpp == delimiter ) + { + if( !exp ) set_error_msg( "No previous pattern" ); + return exp; + } + exps = extract_pattern( ibufpp, delimiter ); + if( !exps ) return 0; + /* buffer alloc'd && not reserved */ + if( exp && !patlock ) regfree( exp ); + else + { + exp = (regex_t *) malloc( sizeof (regex_t) ); + if( !exp ) + { + show_strerror( 0, errno ); + set_error_msg( "Memory exhausted" ); + return 0; + } + } + patlock = false; + n = regcomp( exp, exps, 0 ); + if( n ) + { + char buf[80]; + regerror( n, exp, buf, sizeof buf ); + set_error_msg( buf ); + free( exp ); + exp = 0; + } + return exp; + } + + +/* add line matching a pattern to the global-active list */ +bool build_active_list( const char ** const ibufpp, const int first_addr, + const int second_addr, const bool match ) + { + const regex_t * pat; + const line_t * lp; + int addr; + const char delimiter = **ibufpp; + + if( delimiter == ' ' || delimiter == '\n' ) + { set_error_msg( "Invalid pattern delimiter" ); return false; } + pat = get_compiled_pattern( ibufpp ); + if( !pat ) return false; + if( **ibufpp == delimiter ) ++*ibufpp; + clear_active_list(); + lp = search_line_node( first_addr ); + for( addr = first_addr; addr <= second_addr; ++addr, lp = lp->q_forw ) + { + char * const s = get_sbuf_line( lp ); + if( !s ) return false; + if( isbinary() ) nul_to_newline( s, lp->len ); + if( !regexec( pat, s, 0, 0, 0 ) == match && !set_active_node( lp ) ) + return false; + } + return true; + } + + +/* return pointer to copy of substitution template in the command buffer */ +static char * extract_subst_template( const char ** const ibufpp, + const bool isglobal ) + { + int i = 0, n = 0; + char c; + const char delimiter = **ibufpp; + + ++*ibufpp; + if( **ibufpp == '%' && (*ibufpp)[1] == delimiter ) + { + ++*ibufpp; + if( !stbuf ) set_error_msg( "No previous substitution" ); + return stbuf; + } + while( **ibufpp != delimiter ) + { + if( !resize_buffer( &stbuf, &stbufsz, i + 2 ) ) return 0; + c = stbuf[i++] = *(*ibufpp)++; + if( c == '\n' && **ibufpp == 0 ) { --i, --*ibufpp; break; } + if( c == '\\' && ( stbuf[i++] = *(*ibufpp)++ ) == '\n' && !isglobal ) + { + while( ( *ibufpp = get_tty_line( &n ) ) && + ( n == 0 || ( n > 0 && (*ibufpp)[n-1] != '\n' ) ) ) + clearerr( stdin ); + if( !*ibufpp ) return 0; + } + } + if( !resize_buffer( &stbuf, &stbufsz, i + 1 ) ) return 0; + stbuf[stlen = i] = 0; + return stbuf; + } + + +/* extract substitution tail from the command buffer */ +bool extract_subst_tail( const char ** const ibufpp, int * const gflagsp, + int * const snump, const bool isglobal ) + { + const char delimiter = **ibufpp; + + *gflagsp = *snump = 0; + if( delimiter == '\n' ) { stlen = 0; *gflagsp = GPR; return true; } + if( !extract_subst_template( ibufpp, isglobal ) ) return false; + if( **ibufpp == '\n' ) { *gflagsp = GPR; return true; } + if( **ibufpp == delimiter ) ++*ibufpp; + if( **ibufpp >= '1' && **ibufpp <= '9' ) + return parse_int( snump, *ibufpp, ibufpp ); + if( **ibufpp == 'g' ) { ++*ibufpp; *gflagsp = GSG; } + return true; + } + + +/* return the address of the next line matching a pattern in a given + direction. wrap around begin/end of editor buffer if necessary */ +int next_matching_node_addr( const char ** const ibufpp, const bool forward ) + { + const regex_t * const pat = get_compiled_pattern( ibufpp ); + int addr = current_addr(); + + if( !pat ) return -1; + do { + addr = ( forward ? inc_addr( addr ) : dec_addr( addr ) ); + if( addr ) + { + const line_t * const lp = search_line_node( addr ); + char * const s = get_sbuf_line( lp ); + if( !s ) return -1; + if( isbinary() ) nul_to_newline( s, lp->len ); + if( !regexec( pat, s, 0, 0, 0 ) ) return addr; + } + } + while( addr != current_addr() ); + set_error_msg( "No match" ); + return -1; + } + + +bool new_compiled_pattern( const char ** const ibufpp ) + { + regex_t * tpat; + + disable_interrupts(); + tpat = get_compiled_pattern( ibufpp ); + if( tpat && tpat != global_pat ) + { + if( global_pat ) { regfree( global_pat ); free( global_pat ); } + global_pat = tpat; + patlock = true; /* reserve pattern */ + } + enable_interrupts(); + return ( tpat ? true : false ); + } + + +/* modify text according to a substitution template; return offset to + end of modified text */ +static int apply_subst_template( const char * const boln, + const regmatch_t * const rm, int offset, + const int re_nsub ) + { + const char * sub = stbuf; + + for( ; sub - stbuf < stlen; ++sub ) + { + int n; + if( *sub == '&' ) + { + int j = rm[0].rm_so; int k = rm[0].rm_eo; + if( !resize_buffer( &rbuf, &rbufsz, offset + k - j ) ) return -1; + while( j < k ) rbuf[offset++] = boln[j++]; + } + else if( *sub == '\\' && *++sub >= '1' && *sub <= '9' && + ( n = *sub - '0' ) <= re_nsub ) + { + int j = rm[n].rm_so; int k = rm[n].rm_eo; + if( !resize_buffer( &rbuf, &rbufsz, offset + k - j ) ) return -1; + while( j < k ) rbuf[offset++] = boln[j++]; + } + else + { + if( !resize_buffer( &rbuf, &rbufsz, offset + 1 ) ) return -1; + rbuf[offset++] = *sub; + } + } + if( !resize_buffer( &rbuf, &rbufsz, offset + 1 ) ) return -1; + rbuf[offset] = 0; + return offset; + } + + +/* replace text matched by a pattern according to a substitution + template; return size of the modified text */ +static int replace_matching_text( const line_t * const lp, const int gflags, + const int snum ) + { + enum { se_max = 30 }; /* max subexpressions in a regular expression */ + regmatch_t rm[se_max]; + char * txt = get_sbuf_line( lp ); + const char * eot; + int i = 0, offset = 0; + bool changed = false; + + if( !txt ) return -1; + if( isbinary() ) nul_to_newline( txt, lp->len ); + eot = txt + lp->len; + if( !regexec( global_pat, txt, se_max, rm, 0 ) ) + { + int matchno = 0; + do { + if( !snum || snum == ++matchno ) + { + changed = true; i = rm[0].rm_so; + if( !resize_buffer( &rbuf, &rbufsz, offset + i ) ) return -1; + if( isbinary() ) newline_to_nul( txt, rm[0].rm_eo ); + memcpy( rbuf + offset, txt, i ); offset += i; + offset = apply_subst_template( txt, rm, offset, global_pat->re_nsub ); + if( offset < 0 ) return -1; + } + else + { + i = rm[0].rm_eo; + if( !resize_buffer( &rbuf, &rbufsz, offset + i ) ) return -1; + if( isbinary() ) newline_to_nul( txt, i ); + memcpy( rbuf + offset, txt, i ); offset += i; + } + txt += rm[0].rm_eo; + } + while( *txt && ( !changed || ( ( gflags & GSG ) && rm[0].rm_eo ) ) && + !regexec( global_pat, txt, se_max, rm, REG_NOTBOL ) ); + i = eot - txt; + if( !resize_buffer( &rbuf, &rbufsz, offset + i + 2 ) ) return -1; + if( i > 0 && !rm[0].rm_eo && ( gflags & GSG ) ) + { set_error_msg( "Infinite substitution loop" ); return -1; } + if( isbinary() ) newline_to_nul( txt, i ); + memcpy( rbuf + offset, txt, i ); + memcpy( rbuf + offset + i, "\n", 2 ); + } + return ( changed ? offset + i + 1 : 0 ); + } + + +/* for each line in a range, change text matching a pattern according to + a substitution template; return false if error */ +bool search_and_replace( const int first_addr, const int second_addr, + const int gflags, const int snum, const bool isglobal ) + { + int lc; + bool match_found = false; + + set_current_addr( first_addr - 1 ); + for( lc = 0; lc <= second_addr - first_addr; ++lc ) + { + const line_t * const lp = search_line_node( inc_current_addr() ); + const int size = replace_matching_text( lp, gflags, snum ); + if( size < 0 ) return false; + if( size ) + { + const char * txt = rbuf; + const char * const eot = rbuf + size; + undo_t * up = 0; + disable_interrupts(); + if( !delete_lines( current_addr(), current_addr(), isglobal ) ) + { enable_interrupts(); return false; } + do { + txt = put_sbuf_line( txt, size, current_addr() ); + if( !txt ) { enable_interrupts(); return false; } + if( up ) up->tail = search_line_node( current_addr() ); + else + { + up = push_undo_atom( UADD, current_addr(), current_addr() ); + if( !up ) { enable_interrupts(); return false; } + } + } + while( txt != eot ); + enable_interrupts(); + match_found = true; + } + } + if( !match_found && !( gflags & GLB ) ) + { set_error_msg( "No match" ); return false; } + return true; + } diff --git a/signal.c b/signal.c new file mode 100644 index 0000000..4256ca4 --- /dev/null +++ b/signal.c @@ -0,0 +1,265 @@ +/* signal.c: signal and miscellaneous routines for the ed line editor. */ +/* GNU ed - The GNU line editor. + Copyright (C) 1993, 1994 Andrew Moore, Talke Studio + Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 + Free Software Foundation, Inc. + + 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/>. +*/ + +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <setjmp.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> +#include <sys/ioctl.h> + +#include "ed.h" + + +jmp_buf jmp_state; +static int mutex = 0; /* If > 0, signals stay pending */ +static int window_lines_ = 22; /* scroll length: ws_row - 2 */ +static int window_columns_ = 72; +static bool sighup_pending = false; +static bool sigint_pending = false; + + +static void sighup_handler( int signum ) + { + signum = 0; /* keep compiler happy */ + if( mutex ) sighup_pending = true; + else + { + const char hb[] = "ed.hup"; + sighup_pending = false; + if( last_addr() && modified() && + write_file( hb, "w", 1, last_addr() ) < 0 ) + { + char * const s = getenv( "HOME" ); + const int len = ( s ? strlen( s ) : 0 ); + const int need_slash = ( ( !len || s[len-1] != '/' ) ? 1 : 0 ); + char * const hup = ( ( len + need_slash + (int)sizeof hb < path_max( 0 ) ) ? + (char *) malloc( len + need_slash + sizeof hb ) : 0 ); + if( len && hup ) /* hup filename */ + { + memcpy( hup, s, len ); + if( need_slash ) hup[len] = '/'; + memcpy( hup + len + need_slash, hb, sizeof hb ); + if( write_file( hup, "w", 1, last_addr() ) >= 0 ) exit( 0 ); + } + exit( 1 ); /* hup file write failed */ + } + exit( 0 ); + } + } + + +static void sigint_handler( int signum ) + { + if( mutex ) sigint_pending = true; + else + { + sigset_t set; + sigint_pending = false; + sigemptyset( &set ); + sigaddset( &set, signum ); + sigprocmask( SIG_UNBLOCK, &set, 0 ); + longjmp( jmp_state, -1 ); + } + } + + +static void sigwinch_handler( int signum ) + { +#ifdef TIOCGWINSZ + struct winsize ws; /* window size structure */ + + if( ioctl( 0, TIOCGWINSZ, (char *) &ws ) >= 0 ) + { + /* Sanity check values of environment vars */ + if( ws.ws_row > 2 && ws.ws_row < 600 ) window_lines_ = ws.ws_row - 2; + if( ws.ws_col > 8 && ws.ws_col < 1800 ) window_columns_ = ws.ws_col - 8; + } +#endif + signum = 0; /* keep compiler happy */ + } + + +static int set_signal( int signum, void (*handler)( int ) ) + { + struct sigaction new_action; + + new_action.sa_handler = handler; + sigemptyset( &new_action.sa_mask ); +#ifdef SA_RESTART + new_action.sa_flags = SA_RESTART; +#else + new_action.sa_flags = 0; +#endif + return sigaction( signum, &new_action, 0 ); + } + + +void enable_interrupts( void ) + { + if( --mutex <= 0 ) + { + mutex = 0; + if( sighup_pending ) sighup_handler( SIGHUP ); + if( sigint_pending ) sigint_handler( SIGINT ); + } + } + + +void disable_interrupts( void ) { ++mutex; } + + +void set_signals( void ) + { +#ifdef SIGWINCH + sigwinch_handler( SIGWINCH ); + if( isatty( 0 ) ) set_signal( SIGWINCH, sigwinch_handler ); +#endif + set_signal( SIGHUP, sighup_handler ); + set_signal( SIGQUIT, SIG_IGN ); + set_signal( SIGINT, sigint_handler ); + } + + +void set_window_lines( const int lines ) { window_lines_ = lines; } +int window_columns( void ) { return window_columns_; } +int window_lines( void ) { return window_lines_; } + + +/* convert a string to int with out_of_range detection */ +bool parse_int( int * const i, const char * const str, const char ** const tail ) + { + char * tmp; + long li; + + errno = 0; + *i = li = strtol( str, &tmp, 10 ); + if( tail ) *tail = tmp; + if( tmp == str ) + { + set_error_msg( "Bad numerical result" ); + *i = 0; + return false; + } + if( errno == ERANGE || li > INT_MAX || li < INT_MIN ) + { + set_error_msg( "Numerical result out of range" ); + *i = 0; + return false; + } + return true; + } + + +/* assure at least a minimum size for buffer `buf' */ +bool resize_buffer( char ** const buf, int * const size, const int min_size ) + { + if( *size < min_size ) + { + const int new_size = ( min_size < 512 ? 512 : ( min_size / 512 ) * 1024 ); + void * new_buf = 0; + disable_interrupts(); + if( *buf ) new_buf = realloc( *buf, new_size ); + else new_buf = malloc( new_size ); + if( !new_buf ) + { + show_strerror( 0, errno ); + set_error_msg( "Memory exhausted" ); + enable_interrupts(); + return false; + } + *size = new_size; + *buf = (char *)new_buf; + enable_interrupts(); + } + return true; + } + + +/* assure at least a minimum size for buffer `buf' */ +bool resize_line_buffer( const line_t *** const buf, int * const size, + const int min_size ) + { + if( *size < min_size ) + { + const int new_size = ( min_size < 512 ? 512 : ( min_size / 512 ) * 1024 ); + void * new_buf = 0; + disable_interrupts(); + if( *buf ) new_buf = realloc( *buf, new_size ); + else new_buf = malloc( new_size ); + if( !new_buf ) + { + show_strerror( 0, errno ); + set_error_msg( "Memory exhausted" ); + enable_interrupts(); + return false; + } + *size = new_size; + *buf = (const line_t **)new_buf; + enable_interrupts(); + } + return true; + } + + +/* assure at least a minimum size for buffer `buf' */ +bool resize_undo_buffer( undo_t ** const buf, int * const size, + const int min_size ) + { + if( *size < min_size ) + { + const int new_size = ( min_size < 512 ? 512 : ( min_size / 512 ) * 1024 ); + void * new_buf = 0; + disable_interrupts(); + if( *buf ) new_buf = realloc( *buf, new_size ); + else new_buf = malloc( new_size ); + if( !new_buf ) + { + show_strerror( 0, errno ); + set_error_msg( "Memory exhausted" ); + enable_interrupts(); + return false; + } + *size = new_size; + *buf = (undo_t *)new_buf; + enable_interrupts(); + } + return true; + } + + +/* return unescaped copy of escaped string */ +const char * strip_escapes( const char * p ) + { + static char * buf = 0; + static int bufsz = 0; + const int len = strlen( p ); + int i = 0; + + if( !resize_buffer( &buf, &bufsz, len + 1 ) ) return 0; + /* assert: no trailing escape */ + while( ( buf[i++] = ( (*p == '\\' ) ? *++p : *p ) ) ) + ++p; + return buf; + } diff --git a/testsuite/a.d b/testsuite/a.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/a.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/a.err b/testsuite/a.err new file mode 100644 index 0000000..ec4b00b --- /dev/null +++ b/testsuite/a.err @@ -0,0 +1,3 @@ +aa +hello world +. diff --git a/testsuite/a.pr b/testsuite/a.pr new file mode 100644 index 0000000..ec4b00b --- /dev/null +++ b/testsuite/a.pr @@ -0,0 +1,3 @@ +aa +hello world +. diff --git a/testsuite/a.r b/testsuite/a.r new file mode 100644 index 0000000..26257bd --- /dev/null +++ b/testsuite/a.r @@ -0,0 +1,8 @@ +hello world +line 1 +hello world! +line 2 +line 3 +line 4 +line5 +hello world!! diff --git a/testsuite/a.t b/testsuite/a.t new file mode 100644 index 0000000..ac98c40 --- /dev/null +++ b/testsuite/a.t @@ -0,0 +1,9 @@ +0a +hello world +. +2a +hello world! +. +$a +hello world!! +. diff --git a/testsuite/addr.d b/testsuite/addr.d new file mode 100644 index 0000000..8f7ba1b --- /dev/null +++ b/testsuite/addr.d @@ -0,0 +1,9 @@ +line 1 +line 2 +line 3 +line 4 +line5 +1ine6 +line7 +line8 +line9 diff --git a/testsuite/addr.r b/testsuite/addr.r new file mode 100644 index 0000000..04caf17 --- /dev/null +++ b/testsuite/addr.r @@ -0,0 +1,2 @@ +line 2 +line9 diff --git a/testsuite/addr.t b/testsuite/addr.t new file mode 100644 index 0000000..750b224 --- /dev/null +++ b/testsuite/addr.t @@ -0,0 +1,5 @@ +1 d +1 1 d +1,2,d +1;+ + ,d +1,2;., + 2d diff --git a/testsuite/addr1.err b/testsuite/addr1.err new file mode 100644 index 0000000..29d6383 --- /dev/null +++ b/testsuite/addr1.err @@ -0,0 +1 @@ +100 diff --git a/testsuite/addr1.pr b/testsuite/addr1.pr new file mode 100644 index 0000000..29d6383 --- /dev/null +++ b/testsuite/addr1.pr @@ -0,0 +1 @@ +100 diff --git a/testsuite/addr2.err b/testsuite/addr2.err new file mode 100644 index 0000000..e96acb9 --- /dev/null +++ b/testsuite/addr2.err @@ -0,0 +1 @@ +-100 diff --git a/testsuite/addr2.pr b/testsuite/addr2.pr new file mode 100644 index 0000000..e96acb9 --- /dev/null +++ b/testsuite/addr2.pr @@ -0,0 +1 @@ +-100 diff --git a/testsuite/ascii.d b/testsuite/ascii.d Binary files differnew file mode 100644 index 0000000..c866266 --- /dev/null +++ b/testsuite/ascii.d diff --git a/testsuite/ascii.r b/testsuite/ascii.r Binary files differnew file mode 100644 index 0000000..c866266 --- /dev/null +++ b/testsuite/ascii.r diff --git a/testsuite/ascii.t b/testsuite/ascii.t new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/testsuite/ascii.t diff --git a/testsuite/bang.d b/testsuite/bang.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/bang.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/bang.r b/testsuite/bang.r new file mode 100644 index 0000000..237f034 --- /dev/null +++ b/testsuite/bang.r @@ -0,0 +1,6 @@ +line 1 +line 2 +line 3 +okay +line 4 +line5 diff --git a/testsuite/bang.t b/testsuite/bang.t new file mode 100644 index 0000000..1e6152e --- /dev/null +++ b/testsuite/bang.t @@ -0,0 +1,6 @@ +3p +!read one +hello, world +a +okay +. diff --git a/testsuite/bang1.err b/testsuite/bang1.err new file mode 100644 index 0000000..630af90 --- /dev/null +++ b/testsuite/bang1.err @@ -0,0 +1 @@ +.!date diff --git a/testsuite/bang1.pr b/testsuite/bang1.pr new file mode 100644 index 0000000..630af90 --- /dev/null +++ b/testsuite/bang1.pr @@ -0,0 +1 @@ +.!date diff --git a/testsuite/bang2.err b/testsuite/bang2.err new file mode 100644 index 0000000..79d8956 --- /dev/null +++ b/testsuite/bang2.err @@ -0,0 +1 @@ +!! diff --git a/testsuite/bang2.pr b/testsuite/bang2.pr new file mode 100644 index 0000000..79d8956 --- /dev/null +++ b/testsuite/bang2.pr @@ -0,0 +1 @@ +!! diff --git a/testsuite/c.d b/testsuite/c.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/c.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/c.err b/testsuite/c.err new file mode 100644 index 0000000..658ec38 --- /dev/null +++ b/testsuite/c.err @@ -0,0 +1,3 @@ +cc +hello world +. diff --git a/testsuite/c.pr b/testsuite/c.pr new file mode 100644 index 0000000..658ec38 --- /dev/null +++ b/testsuite/c.pr @@ -0,0 +1,3 @@ +cc +hello world +. diff --git a/testsuite/c.r b/testsuite/c.r new file mode 100644 index 0000000..0fb3e4f --- /dev/null +++ b/testsuite/c.r @@ -0,0 +1,4 @@ +at the top +between top/middle +in the middle +at the bottom diff --git a/testsuite/c.t b/testsuite/c.t new file mode 100644 index 0000000..5fba319 --- /dev/null +++ b/testsuite/c.t @@ -0,0 +1,12 @@ +0c +at the top +. +4c +in the middle +. +$c +at the bottom +. +2,3c +between top/middle +. diff --git a/testsuite/check.sh b/testsuite/check.sh new file mode 100755 index 0000000..98d539c --- /dev/null +++ b/testsuite/check.sh @@ -0,0 +1,107 @@ +#! /bin/sh +# check script for GNU ed - The GNU line editor +# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 +# Free Software Foundation, Inc. +# +# This script is free software; you have unlimited permission +# to copy, distribute and modify it. + +LC_ALL=C +export LC_ALL +objdir=`pwd` +testdir=`cd "$1" ; pwd` +ED="${objdir}"/ed + +if [ ! -x "${ED}" ] ; then + echo "${ED}: cannot execute" + exit 1 +fi + +if [ -d tmp ] ; then rm -rf tmp ; fi +mkdir tmp + +# Generate ed test scripts, with extensions .ed and .red, from +# .t and .err files, respectively. +printf "building test scripts for ed-%s...\n" "$2" +cd "${testdir}" + +for i in *.t ; do + base=`echo "$i" | sed 's/\.t$//'` + ( + echo "#! /bin/sh" + echo "${ED} -s <<'EOT'" + echo H + echo "r ${testdir}/${base}.d" + cat "$i" + echo "w ${base}.o" + echo EOT + ) > "${objdir}/tmp/${base}.ed" + chmod u+x "${objdir}/tmp/${base}.ed" +done + +for i in *.err ; do + base=`echo "$i" | sed 's/\.err$//'` + ( + echo "#! /bin/sh -" + echo "${ED} -s <<'EOT'" + echo H + echo "r ${testdir}/${base}.err" + cat "$i" + echo "w ${base}.ro" + echo EOT + ) > "${objdir}/tmp/${base}.red" + chmod u+x "${objdir}/tmp/${base}.red" +done + + +# Run the .ed and .red scripts just generated +# and compare their output against the .r and .pr files, which contain +# the correct output. +printf "testing ed-%s...\n" "$2" +cd "${objdir}"/tmp + +# Run the *.red scripts first, since these don't generate output; +# they exit with non-zero status +for i in *.red ; do + echo "$i" + if ./"$i" ; then + echo "*** The script $i exited abnormally ***" + fi +done > errs.ck 2>&1 + +# Run error scripts again as pipes - these should generate output and +# exit with error (>0) status. +for i in *.red ; do + base=`echo "$i" | sed 's/\.red$//'` + if cat ${base}.red | "${ED}" -s ; then + echo "*** The piped script $i exited abnormally ***" + else + if cmp -s ${base}.ro "${testdir}"/${base}.pr ; then + true + else + echo "*** Output ${base}.ro of piped script $i is incorrect ***" + fi + fi +done > pipes.ck 2>&1 + +# Run the remainding scripts; they exit with zero status +for i in *.ed ; do + base=`echo "$i" | sed 's/\.ed$//'` + if ./${base}.ed ; then + if cmp -s ${base}.o "${testdir}"/${base}.r ; then + true + else + echo "*** Output ${base}.o of script $i is incorrect ***" + fi + else + echo "*** The script $i exited abnormally ***" + fi +done > scripts.ck 2>&1 + +grep '\*\*\*' *.ck | sed 's/^[^*]*//' +if grep '\*\*\*' *.ck > /dev/null ; then + exit 127 +else + echo "tests completed successfully." + cd "${objdir}" && rm -r tmp +fi diff --git a/testsuite/comment.d b/testsuite/comment.d new file mode 100644 index 0000000..ec1e35a --- /dev/null +++ b/testsuite/comment.d @@ -0,0 +1,6 @@ +hello +world +this is a simple +line of text +for testing the comment +command in global lists diff --git a/testsuite/comment.r b/testsuite/comment.r new file mode 100644 index 0000000..ae60d9d --- /dev/null +++ b/testsuite/comment.r @@ -0,0 +1,6 @@ +heylo +woryd +this is a simpye +yine of text +for testing the comment +command in gyobal lists diff --git a/testsuite/comment.t b/testsuite/comment.t new file mode 100644 index 0000000..ab873f8 --- /dev/null +++ b/testsuite/comment.t @@ -0,0 +1,5 @@ +# lines beginning with a `#' should be ignored +g/./# including in global commands \ +s/l/x/\ +# and in the command list \ +s/x/y/ diff --git a/testsuite/d.d b/testsuite/d.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/d.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/d.err b/testsuite/d.err new file mode 100644 index 0000000..f03f694 --- /dev/null +++ b/testsuite/d.err @@ -0,0 +1 @@ +dd diff --git a/testsuite/d.pr b/testsuite/d.pr new file mode 100644 index 0000000..f03f694 --- /dev/null +++ b/testsuite/d.pr @@ -0,0 +1 @@ +dd diff --git a/testsuite/d.r b/testsuite/d.r new file mode 100644 index 0000000..b7e242c --- /dev/null +++ b/testsuite/d.r @@ -0,0 +1 @@ +line 2 diff --git a/testsuite/d.t b/testsuite/d.t new file mode 100644 index 0000000..c7c473f --- /dev/null +++ b/testsuite/d.t @@ -0,0 +1,3 @@ +1d +2;+1d +$d diff --git a/testsuite/e1.d b/testsuite/e1.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/e1.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/e1.err b/testsuite/e1.err new file mode 100644 index 0000000..827cc29 --- /dev/null +++ b/testsuite/e1.err @@ -0,0 +1 @@ +ee e1.err diff --git a/testsuite/e1.pr b/testsuite/e1.pr new file mode 100644 index 0000000..827cc29 --- /dev/null +++ b/testsuite/e1.pr @@ -0,0 +1 @@ +ee e1.err diff --git a/testsuite/e1.r b/testsuite/e1.r new file mode 100644 index 0000000..d141049 --- /dev/null +++ b/testsuite/e1.r @@ -0,0 +1,5 @@ +3d +e e1.ed +1;/H/+d +w e1.o +EOT diff --git a/testsuite/e1.t b/testsuite/e1.t new file mode 100644 index 0000000..ccbccfc --- /dev/null +++ b/testsuite/e1.t @@ -0,0 +1,3 @@ +3d +e e1.ed +1;/H/+d diff --git a/testsuite/e2.d b/testsuite/e2.d new file mode 100644 index 0000000..aa44630 --- /dev/null +++ b/testsuite/e2.d @@ -0,0 +1 @@ +E !echo hello world- diff --git a/testsuite/e2.err b/testsuite/e2.err new file mode 100644 index 0000000..779a64b --- /dev/null +++ b/testsuite/e2.err @@ -0,0 +1 @@ +.e e2.err diff --git a/testsuite/e2.pr b/testsuite/e2.pr new file mode 100644 index 0000000..779a64b --- /dev/null +++ b/testsuite/e2.pr @@ -0,0 +1 @@ +.e e2.err diff --git a/testsuite/e2.r b/testsuite/e2.r new file mode 100644 index 0000000..59ebf11 --- /dev/null +++ b/testsuite/e2.r @@ -0,0 +1 @@ +hello world- diff --git a/testsuite/e2.t b/testsuite/e2.t new file mode 100644 index 0000000..aa44630 --- /dev/null +++ b/testsuite/e2.t @@ -0,0 +1 @@ +E !echo hello world- diff --git a/testsuite/e3.d b/testsuite/e3.d new file mode 100644 index 0000000..aa44630 --- /dev/null +++ b/testsuite/e3.d @@ -0,0 +1 @@ +E !echo hello world- diff --git a/testsuite/e3.err b/testsuite/e3.err new file mode 100644 index 0000000..80a7fdc --- /dev/null +++ b/testsuite/e3.err @@ -0,0 +1 @@ +ee.err diff --git a/testsuite/e3.pr b/testsuite/e3.pr new file mode 100644 index 0000000..80a7fdc --- /dev/null +++ b/testsuite/e3.pr @@ -0,0 +1 @@ +ee.err diff --git a/testsuite/e3.r b/testsuite/e3.r new file mode 100644 index 0000000..aa44630 --- /dev/null +++ b/testsuite/e3.r @@ -0,0 +1 @@ +E !echo hello world- diff --git a/testsuite/e3.t b/testsuite/e3.t new file mode 100644 index 0000000..1c50726 --- /dev/null +++ b/testsuite/e3.t @@ -0,0 +1 @@ +E diff --git a/testsuite/e4.d b/testsuite/e4.d new file mode 100644 index 0000000..aa44630 --- /dev/null +++ b/testsuite/e4.d @@ -0,0 +1 @@ +E !echo hello world- diff --git a/testsuite/e4.r b/testsuite/e4.r new file mode 100644 index 0000000..aa44630 --- /dev/null +++ b/testsuite/e4.r @@ -0,0 +1 @@ +E !echo hello world- diff --git a/testsuite/e4.t b/testsuite/e4.t new file mode 100644 index 0000000..d905d9d --- /dev/null +++ b/testsuite/e4.t @@ -0,0 +1 @@ +e diff --git a/testsuite/f1.err b/testsuite/f1.err new file mode 100644 index 0000000..e60975a --- /dev/null +++ b/testsuite/f1.err @@ -0,0 +1 @@ +.f f1.err diff --git a/testsuite/f1.pr b/testsuite/f1.pr new file mode 100644 index 0000000..e60975a --- /dev/null +++ b/testsuite/f1.pr @@ -0,0 +1 @@ +.f f1.err diff --git a/testsuite/f2.err b/testsuite/f2.err new file mode 100644 index 0000000..26d1c5e --- /dev/null +++ b/testsuite/f2.err @@ -0,0 +1 @@ +ff1.err diff --git a/testsuite/f2.pr b/testsuite/f2.pr new file mode 100644 index 0000000..26d1c5e --- /dev/null +++ b/testsuite/f2.pr @@ -0,0 +1 @@ +ff1.err diff --git a/testsuite/g1.d b/testsuite/g1.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/g1.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/g1.err b/testsuite/g1.err new file mode 100644 index 0000000..f95ea22 --- /dev/null +++ b/testsuite/g1.err @@ -0,0 +1 @@ +g/./s //x/ diff --git a/testsuite/g1.pr b/testsuite/g1.pr new file mode 100644 index 0000000..f95ea22 --- /dev/null +++ b/testsuite/g1.pr @@ -0,0 +1 @@ +g/./s //x/ diff --git a/testsuite/g1.r b/testsuite/g1.r new file mode 100644 index 0000000..9185f3a --- /dev/null +++ b/testsuite/g1.r @@ -0,0 +1,20 @@ +line5 +help! world +caos +order +line 4 +help! world +caos +order +line 3 +help! world +caos +order +line 2 +help! world +caos +order +line 1 +help! world +caos +order diff --git a/testsuite/g1.t b/testsuite/g1.t new file mode 100644 index 0000000..8523db1 --- /dev/null +++ b/testsuite/g1.t @@ -0,0 +1,9 @@ +g/./m0 +g/./s/$/\ +hello world +g/hello /s/lo/p!/\ +a\ +order\ +.\ +i\ +caos diff --git a/testsuite/g2.d b/testsuite/g2.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/g2.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/g2.err b/testsuite/g2.err new file mode 100644 index 0000000..0ff6a5a --- /dev/null +++ b/testsuite/g2.err @@ -0,0 +1 @@ +g//s/./x/ diff --git a/testsuite/g2.pr b/testsuite/g2.pr new file mode 100644 index 0000000..0ff6a5a --- /dev/null +++ b/testsuite/g2.pr @@ -0,0 +1 @@ +g//s/./x/ diff --git a/testsuite/g2.r b/testsuite/g2.r new file mode 100644 index 0000000..3b18e51 --- /dev/null +++ b/testsuite/g2.r @@ -0,0 +1 @@ +hello world diff --git a/testsuite/g2.t b/testsuite/g2.t new file mode 100644 index 0000000..831ee83 --- /dev/null +++ b/testsuite/g2.t @@ -0,0 +1,2 @@ +g/[2-4]/-1,+1c\ +hello world diff --git a/testsuite/g3.d b/testsuite/g3.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/g3.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/g3.err b/testsuite/g3.err new file mode 100644 index 0000000..01058d8 --- /dev/null +++ b/testsuite/g3.err @@ -0,0 +1 @@ +g diff --git a/testsuite/g3.pr b/testsuite/g3.pr new file mode 100644 index 0000000..01058d8 --- /dev/null +++ b/testsuite/g3.pr @@ -0,0 +1 @@ +g diff --git a/testsuite/g3.r b/testsuite/g3.r new file mode 100644 index 0000000..cc6fbdd --- /dev/null +++ b/testsuite/g3.r @@ -0,0 +1,5 @@ +linc 3 +xine 1 +xine 2 +xinc 4 +xinc5 diff --git a/testsuite/g3.t b/testsuite/g3.t new file mode 100644 index 0000000..2d052a6 --- /dev/null +++ b/testsuite/g3.t @@ -0,0 +1,4 @@ +g/./s//x/\ +3m0 +g/./s/e/c/\ +2,3m1 diff --git a/testsuite/g4.d b/testsuite/g4.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/g4.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/g4.r b/testsuite/g4.r new file mode 100644 index 0000000..350882d --- /dev/null +++ b/testsuite/g4.r @@ -0,0 +1,7 @@ +hello +zine 1 +line 2 +line 3 +line 4 +line5 +world diff --git a/testsuite/g4.t b/testsuite/g4.t new file mode 100644 index 0000000..ec61816 --- /dev/null +++ b/testsuite/g4.t @@ -0,0 +1,13 @@ +g/./s/./x/\ +u\ +s/./y/\ +u\ +s/./z/\ +u +u +0a +hello +. +$a +world +. diff --git a/testsuite/g5.d b/testsuite/g5.d new file mode 100644 index 0000000..a92d664 --- /dev/null +++ b/testsuite/g5.d @@ -0,0 +1,3 @@ +line 1 +line 2 +line 3 diff --git a/testsuite/g5.r b/testsuite/g5.r new file mode 100644 index 0000000..15a2675 --- /dev/null +++ b/testsuite/g5.r @@ -0,0 +1,9 @@ +line 1 +line 2 +line 3 +line 2 +line 3 +line 1 +line 3 +line 1 +line 2 diff --git a/testsuite/g5.t b/testsuite/g5.t new file mode 100644 index 0000000..e213481 --- /dev/null +++ b/testsuite/g5.t @@ -0,0 +1,2 @@ +g/./1,3t$\ +1d diff --git a/testsuite/h.err b/testsuite/h.err new file mode 100644 index 0000000..a71e506 --- /dev/null +++ b/testsuite/h.err @@ -0,0 +1 @@ +.h diff --git a/testsuite/h.pr b/testsuite/h.pr new file mode 100644 index 0000000..a71e506 --- /dev/null +++ b/testsuite/h.pr @@ -0,0 +1 @@ +.h diff --git a/testsuite/i.d b/testsuite/i.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/i.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/i.err b/testsuite/i.err new file mode 100644 index 0000000..b63f5ac --- /dev/null +++ b/testsuite/i.err @@ -0,0 +1,3 @@ +ii +hello world +. diff --git a/testsuite/i.pr b/testsuite/i.pr new file mode 100644 index 0000000..b63f5ac --- /dev/null +++ b/testsuite/i.pr @@ -0,0 +1,3 @@ +ii +hello world +. diff --git a/testsuite/i.r b/testsuite/i.r new file mode 100644 index 0000000..5f27af0 --- /dev/null +++ b/testsuite/i.r @@ -0,0 +1,8 @@ +hello world +hello world! +line 1 +line 2 +line 3 +line 4 +hello world!! +line5 diff --git a/testsuite/i.t b/testsuite/i.t new file mode 100644 index 0000000..6de2233 --- /dev/null +++ b/testsuite/i.t @@ -0,0 +1,9 @@ +0i +hello world +. +2i +hello world! +. +$i +hello world!! +. diff --git a/testsuite/j.d b/testsuite/j.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/j.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/j.r b/testsuite/j.r new file mode 100644 index 0000000..66f36a8 --- /dev/null +++ b/testsuite/j.r @@ -0,0 +1,4 @@ +line 1 +line 2line 3 +line 4 +line5 diff --git a/testsuite/j.t b/testsuite/j.t new file mode 100644 index 0000000..9b5d28d --- /dev/null +++ b/testsuite/j.t @@ -0,0 +1,2 @@ +1,1j +2,3j diff --git a/testsuite/k.d b/testsuite/k.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/k.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/k.r b/testsuite/k.r new file mode 100644 index 0000000..eeb38db --- /dev/null +++ b/testsuite/k.r @@ -0,0 +1,5 @@ +line 3 +hello world +line 4 +line5 +line 2 diff --git a/testsuite/k.t b/testsuite/k.t new file mode 100644 index 0000000..53d588d --- /dev/null +++ b/testsuite/k.t @@ -0,0 +1,10 @@ +2ka +1d +'am$ +1ka +0a +hello world +. +'ad +u +'am0 diff --git a/testsuite/k2.err b/testsuite/k2.err new file mode 100644 index 0000000..b34a18d --- /dev/null +++ b/testsuite/k2.err @@ -0,0 +1 @@ +kA diff --git a/testsuite/k2.pr b/testsuite/k2.pr new file mode 100644 index 0000000..b34a18d --- /dev/null +++ b/testsuite/k2.pr @@ -0,0 +1 @@ +kA diff --git a/testsuite/k3.err b/testsuite/k3.err new file mode 100644 index 0000000..70190c4 --- /dev/null +++ b/testsuite/k3.err @@ -0,0 +1 @@ +0ka diff --git a/testsuite/k3.pr b/testsuite/k3.pr new file mode 100644 index 0000000..70190c4 --- /dev/null +++ b/testsuite/k3.pr @@ -0,0 +1 @@ +0ka diff --git a/testsuite/k4.err b/testsuite/k4.err new file mode 100644 index 0000000..3457642 --- /dev/null +++ b/testsuite/k4.err @@ -0,0 +1,6 @@ +a +hello +. +.ka +'ad +'ap diff --git a/testsuite/k4.pr b/testsuite/k4.pr new file mode 100644 index 0000000..3457642 --- /dev/null +++ b/testsuite/k4.pr @@ -0,0 +1,6 @@ +a +hello +. +.ka +'ad +'ap diff --git a/testsuite/m.d b/testsuite/m.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/m.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/m.err b/testsuite/m.err new file mode 100644 index 0000000..3aec4c3 --- /dev/null +++ b/testsuite/m.err @@ -0,0 +1,4 @@ +a +hello world +. +1,$m1 diff --git a/testsuite/m.pr b/testsuite/m.pr new file mode 100644 index 0000000..edbb96f --- /dev/null +++ b/testsuite/m.pr @@ -0,0 +1,5 @@ +a +hello world +. +1,$m1 +hello world diff --git a/testsuite/m.r b/testsuite/m.r new file mode 100644 index 0000000..186cf54 --- /dev/null +++ b/testsuite/m.r @@ -0,0 +1,5 @@ +line5 +line 1 +line 2 +line 3 +line 4 diff --git a/testsuite/m.t b/testsuite/m.t new file mode 100644 index 0000000..c39c088 --- /dev/null +++ b/testsuite/m.t @@ -0,0 +1,7 @@ +1,2m$ +1,2m$ +1,2m$ +$m0 +$m0 +2,3m1 +2,3m3 diff --git a/testsuite/nl.err b/testsuite/nl.err new file mode 100644 index 0000000..8949a85 --- /dev/null +++ b/testsuite/nl.err @@ -0,0 +1 @@ +,1 diff --git a/testsuite/nl.pr b/testsuite/nl.pr new file mode 100644 index 0000000..8949a85 --- /dev/null +++ b/testsuite/nl.pr @@ -0,0 +1 @@ +,1 diff --git a/testsuite/nl1.d b/testsuite/nl1.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/nl1.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/nl1.r b/testsuite/nl1.r new file mode 100644 index 0000000..9d8854c --- /dev/null +++ b/testsuite/nl1.r @@ -0,0 +1,8 @@ + + +hello world +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/nl1.t b/testsuite/nl1.t new file mode 100644 index 0000000..ea192e9 --- /dev/null +++ b/testsuite/nl1.t @@ -0,0 +1,8 @@ +1 + + +0a + + +hello world +. diff --git a/testsuite/nl2.d b/testsuite/nl2.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/nl2.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/nl2.r b/testsuite/nl2.r new file mode 100644 index 0000000..fe99e41 --- /dev/null +++ b/testsuite/nl2.r @@ -0,0 +1,6 @@ +line 1 +line 2 +line 3 +line 4 +line5 +hello world diff --git a/testsuite/nl2.t b/testsuite/nl2.t new file mode 100644 index 0000000..73fd27b --- /dev/null +++ b/testsuite/nl2.t @@ -0,0 +1,4 @@ +a +hello world +. +0;/./ diff --git a/testsuite/q.d b/testsuite/q.d new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/testsuite/q.d diff --git a/testsuite/q.err b/testsuite/q.err new file mode 100644 index 0000000..0a7e178 --- /dev/null +++ b/testsuite/q.err @@ -0,0 +1 @@ +.q diff --git a/testsuite/q.pr b/testsuite/q.pr new file mode 100644 index 0000000..0a7e178 --- /dev/null +++ b/testsuite/q.pr @@ -0,0 +1 @@ +.q diff --git a/testsuite/q.r b/testsuite/q.r new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/testsuite/q.r diff --git a/testsuite/q.t b/testsuite/q.t new file mode 100644 index 0000000..123a2c8 --- /dev/null +++ b/testsuite/q.t @@ -0,0 +1,5 @@ +w q.o +a +hello +. +q diff --git a/testsuite/r.err b/testsuite/r.err new file mode 100644 index 0000000..1c44fa3 --- /dev/null +++ b/testsuite/r.err @@ -0,0 +1 @@ +r a-good-book diff --git a/testsuite/r.pr b/testsuite/r.pr new file mode 100644 index 0000000..1c44fa3 --- /dev/null +++ b/testsuite/r.pr @@ -0,0 +1 @@ +r a-good-book diff --git a/testsuite/r1.d b/testsuite/r1.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/r1.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/r1.r b/testsuite/r1.r new file mode 100644 index 0000000..a3ff506 --- /dev/null +++ b/testsuite/r1.r @@ -0,0 +1,7 @@ +line 1 +hello world +line 2 +line 3 +line 4 +line5 +hello world diff --git a/testsuite/r1.t b/testsuite/r1.t new file mode 100644 index 0000000..d787a92 --- /dev/null +++ b/testsuite/r1.t @@ -0,0 +1,3 @@ +1;r !echo hello world +1 +r !echo hello world diff --git a/testsuite/r2.d b/testsuite/r2.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/r2.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/r2.r b/testsuite/r2.r new file mode 100644 index 0000000..ac152ba --- /dev/null +++ b/testsuite/r2.r @@ -0,0 +1,10 @@ +line 1 +line 2 +line 3 +line 4 +line5 +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/r2.t b/testsuite/r2.t new file mode 100644 index 0000000..4286f42 --- /dev/null +++ b/testsuite/r2.t @@ -0,0 +1 @@ +r diff --git a/testsuite/r3.d b/testsuite/r3.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/r3.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/r3.r b/testsuite/r3.r new file mode 100644 index 0000000..d925d88 --- /dev/null +++ b/testsuite/r3.r @@ -0,0 +1,4 @@ +r r3.ed +1;/H/+d +w r3.o +EOT diff --git a/testsuite/r3.t b/testsuite/r3.t new file mode 100644 index 0000000..cdc206d --- /dev/null +++ b/testsuite/r3.t @@ -0,0 +1,2 @@ +r r3.ed +1;/H/+d diff --git a/testsuite/s1.d b/testsuite/s1.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/s1.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/s1.err b/testsuite/s1.err new file mode 100644 index 0000000..d7ca0cf --- /dev/null +++ b/testsuite/s1.err @@ -0,0 +1 @@ +s . x diff --git a/testsuite/s1.pr b/testsuite/s1.pr new file mode 100644 index 0000000..d7ca0cf --- /dev/null +++ b/testsuite/s1.pr @@ -0,0 +1 @@ +s . x diff --git a/testsuite/s1.r b/testsuite/s1.r new file mode 100644 index 0000000..4eb0980 --- /dev/null +++ b/testsuite/s1.r @@ -0,0 +1,5 @@ +liene 1 +(liene) (2) +(liene) (3) +liene (4) +(()liene5) diff --git a/testsuite/s1.t b/testsuite/s1.t new file mode 100644 index 0000000..cca3dcc --- /dev/null +++ b/testsuite/s1.t @@ -0,0 +1,6 @@ +s/\([^ ][^ ]*\)/(\1)/g +2s +/3/s +/\(4\)/sr +/\(.\)/srg +,s/i/&e/ diff --git a/testsuite/s10.err b/testsuite/s10.err new file mode 100644 index 0000000..0d8d83d --- /dev/null +++ b/testsuite/s10.err @@ -0,0 +1,4 @@ +a +hello +. +s/[h[.]/x/ diff --git a/testsuite/s10.pr b/testsuite/s10.pr new file mode 100644 index 0000000..1304de1 --- /dev/null +++ b/testsuite/s10.pr @@ -0,0 +1,5 @@ +a +hello +. +s/[h[.]/x/ +hello diff --git a/testsuite/s2.d b/testsuite/s2.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/s2.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/s2.err b/testsuite/s2.err new file mode 100644 index 0000000..b5c851d --- /dev/null +++ b/testsuite/s2.err @@ -0,0 +1,4 @@ +a +a +. +s/x*/a/g diff --git a/testsuite/s2.pr b/testsuite/s2.pr new file mode 100644 index 0000000..949ac99 --- /dev/null +++ b/testsuite/s2.pr @@ -0,0 +1,5 @@ +a +a +. +s/x*/a/g +a diff --git a/testsuite/s2.r b/testsuite/s2.r new file mode 100644 index 0000000..ca305c8 --- /dev/null +++ b/testsuite/s2.r @@ -0,0 +1,5 @@ +li(n)e 1 +i(n)e 200 +li(n)e 3 +li(n)e 4 +li(n)e500 diff --git a/testsuite/s2.t b/testsuite/s2.t new file mode 100644 index 0000000..f365849 --- /dev/null +++ b/testsuite/s2.t @@ -0,0 +1,4 @@ +,s/./(&)/3 +s/$/00 +2s//%/g +s/^l diff --git a/testsuite/s3.d b/testsuite/s3.d new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/testsuite/s3.d diff --git a/testsuite/s3.err b/testsuite/s3.err new file mode 100644 index 0000000..d68c7d0 --- /dev/null +++ b/testsuite/s3.err @@ -0,0 +1 @@ +s/[xyx/a/ diff --git a/testsuite/s3.pr b/testsuite/s3.pr new file mode 100644 index 0000000..d68c7d0 --- /dev/null +++ b/testsuite/s3.pr @@ -0,0 +1 @@ +s/[xyx/a/ diff --git a/testsuite/s3.r b/testsuite/s3.r new file mode 100644 index 0000000..d6cada2 --- /dev/null +++ b/testsuite/s3.r @@ -0,0 +1 @@ +hello world diff --git a/testsuite/s3.t b/testsuite/s3.t new file mode 100644 index 0000000..fbf8803 --- /dev/null +++ b/testsuite/s3.t @@ -0,0 +1,6 @@ +a +hello/[]world +. +s/[/]/ / +s/[[:digit:][]/ / +s/[]]/ / diff --git a/testsuite/s4.err b/testsuite/s4.err new file mode 100644 index 0000000..35b609f --- /dev/null +++ b/testsuite/s4.err @@ -0,0 +1 @@ +s/\a\b\c/xyz/ diff --git a/testsuite/s4.pr b/testsuite/s4.pr new file mode 100644 index 0000000..35b609f --- /dev/null +++ b/testsuite/s4.pr @@ -0,0 +1 @@ +s/\a\b\c/xyz/ diff --git a/testsuite/s5.err b/testsuite/s5.err new file mode 100644 index 0000000..89104c5 --- /dev/null +++ b/testsuite/s5.err @@ -0,0 +1 @@ +s//xyz/ diff --git a/testsuite/s5.pr b/testsuite/s5.pr new file mode 100644 index 0000000..89104c5 --- /dev/null +++ b/testsuite/s5.pr @@ -0,0 +1 @@ +s//xyz/ diff --git a/testsuite/s6.err b/testsuite/s6.err new file mode 100644 index 0000000..b478595 --- /dev/null +++ b/testsuite/s6.err @@ -0,0 +1 @@ +s diff --git a/testsuite/s6.pr b/testsuite/s6.pr new file mode 100644 index 0000000..b478595 --- /dev/null +++ b/testsuite/s6.pr @@ -0,0 +1 @@ +s diff --git a/testsuite/s7.err b/testsuite/s7.err new file mode 100644 index 0000000..30ba4fd --- /dev/null +++ b/testsuite/s7.err @@ -0,0 +1,5 @@ +a +hello world +. +/./ +sr diff --git a/testsuite/s7.pr b/testsuite/s7.pr new file mode 100644 index 0000000..47a94c3 --- /dev/null +++ b/testsuite/s7.pr @@ -0,0 +1,6 @@ +a +hello world +. +/./ +sr +hello world diff --git a/testsuite/s8.err b/testsuite/s8.err new file mode 100644 index 0000000..5665767 --- /dev/null +++ b/testsuite/s8.err @@ -0,0 +1,4 @@ +a +hello +. +s/[h[=]/x/ diff --git a/testsuite/s8.pr b/testsuite/s8.pr new file mode 100644 index 0000000..ec6a965 --- /dev/null +++ b/testsuite/s8.pr @@ -0,0 +1,5 @@ +a +hello +. +s/[h[=]/x/ +hello diff --git a/testsuite/s9.err b/testsuite/s9.err new file mode 100644 index 0000000..1ff16dd --- /dev/null +++ b/testsuite/s9.err @@ -0,0 +1,4 @@ +a +hello +. +s/[h[:]/x/ diff --git a/testsuite/s9.pr b/testsuite/s9.pr new file mode 100644 index 0000000..f22b3b3 --- /dev/null +++ b/testsuite/s9.pr @@ -0,0 +1,5 @@ +a +hello +. +s/[h[:]/x/ +hello diff --git a/testsuite/t.d b/testsuite/t.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/t.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/t.r b/testsuite/t.r new file mode 100644 index 0000000..b7e0a71 --- /dev/null +++ b/testsuite/t.r @@ -0,0 +1,17 @@ +line 1 +line5 +line 1 +line 1 +line 2 +line 2 +line 3 +line 4 +line5 +line 1 +line 1 +line 1 +line 2 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/t.t b/testsuite/t.t new file mode 100644 index 0000000..bb42163 --- /dev/null +++ b/testsuite/t.t @@ -0,0 +1,4 @@ +1t0 +2,3t2 +,t$ +t0;/./ diff --git a/testsuite/t1.err b/testsuite/t1.err new file mode 100644 index 0000000..c49c556 --- /dev/null +++ b/testsuite/t1.err @@ -0,0 +1 @@ +tt diff --git a/testsuite/t1.pr b/testsuite/t1.pr new file mode 100644 index 0000000..c49c556 --- /dev/null +++ b/testsuite/t1.pr @@ -0,0 +1 @@ +tt diff --git a/testsuite/t2.err b/testsuite/t2.err new file mode 100644 index 0000000..c202051 --- /dev/null +++ b/testsuite/t2.err @@ -0,0 +1 @@ +t0;-1 diff --git a/testsuite/t2.pr b/testsuite/t2.pr new file mode 100644 index 0000000..c202051 --- /dev/null +++ b/testsuite/t2.pr @@ -0,0 +1 @@ +t0;-1 diff --git a/testsuite/u.d b/testsuite/u.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/u.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/u.err b/testsuite/u.err new file mode 100644 index 0000000..caa1ba1 --- /dev/null +++ b/testsuite/u.err @@ -0,0 +1 @@ +.u diff --git a/testsuite/u.pr b/testsuite/u.pr new file mode 100644 index 0000000..caa1ba1 --- /dev/null +++ b/testsuite/u.pr @@ -0,0 +1 @@ +.u diff --git a/testsuite/u.r b/testsuite/u.r new file mode 100644 index 0000000..ad558d8 --- /dev/null +++ b/testsuite/u.r @@ -0,0 +1,9 @@ +line 1 +hello +hello world!! +line 2 +line 3 +line 4 +line5 +hello +hello world!! diff --git a/testsuite/u.t b/testsuite/u.t new file mode 100644 index 0000000..1b9eb15 --- /dev/null +++ b/testsuite/u.t @@ -0,0 +1,31 @@ +1;r ascii.o +u +a +hello +world +. +g/./s//x/\ +a\ +hello\ +world +u +u +u +a +hello world! +. +u +1,$d +u +2,3d +u +c +hello world!! +. +u +u +-1;.,+1j +u +u +u +.,+1t$ diff --git a/testsuite/v.d b/testsuite/v.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/v.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/v.r b/testsuite/v.r new file mode 100644 index 0000000..714db63 --- /dev/null +++ b/testsuite/v.r @@ -0,0 +1,11 @@ +line5 +order +hello world +line 1 +order +line 2 +order +line 3 +order +line 4 +order diff --git a/testsuite/v.t b/testsuite/v.t new file mode 100644 index 0000000..608a77f --- /dev/null +++ b/testsuite/v.t @@ -0,0 +1,6 @@ +v/[ ]/m0 +v/[ ]/s/$/\ +hello world +v/hello /s/lo/p!/\ +a\ +order diff --git a/testsuite/w.d b/testsuite/w.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/testsuite/w.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/w.r b/testsuite/w.r new file mode 100644 index 0000000..ac152ba --- /dev/null +++ b/testsuite/w.r @@ -0,0 +1,10 @@ +line 1 +line 2 +line 3 +line 4 +line5 +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/testsuite/w.t b/testsuite/w.t new file mode 100644 index 0000000..c2e18bd --- /dev/null +++ b/testsuite/w.t @@ -0,0 +1,2 @@ +w !cat >\!.z +r \!.z diff --git a/testsuite/w1.err b/testsuite/w1.err new file mode 100644 index 0000000..e2c8a60 --- /dev/null +++ b/testsuite/w1.err @@ -0,0 +1 @@ +w /to/some/far-away/place diff --git a/testsuite/w1.pr b/testsuite/w1.pr new file mode 100644 index 0000000..e2c8a60 --- /dev/null +++ b/testsuite/w1.pr @@ -0,0 +1 @@ +w /to/some/far-away/place diff --git a/testsuite/w2.err b/testsuite/w2.err new file mode 100644 index 0000000..9daf89c --- /dev/null +++ b/testsuite/w2.err @@ -0,0 +1 @@ +ww.o diff --git a/testsuite/w2.pr b/testsuite/w2.pr new file mode 100644 index 0000000..9daf89c --- /dev/null +++ b/testsuite/w2.pr @@ -0,0 +1 @@ +ww.o diff --git a/testsuite/w3.err b/testsuite/w3.err new file mode 100644 index 0000000..39bbf4c --- /dev/null +++ b/testsuite/w3.err @@ -0,0 +1 @@ +wqp w.o diff --git a/testsuite/w3.pr b/testsuite/w3.pr new file mode 100644 index 0000000..39bbf4c --- /dev/null +++ b/testsuite/w3.pr @@ -0,0 +1 @@ +wqp w.o diff --git a/testsuite/x.d b/testsuite/x.d new file mode 100644 index 0000000..94c99a3 --- /dev/null +++ b/testsuite/x.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line 5 diff --git a/testsuite/x.err b/testsuite/x.err new file mode 100644 index 0000000..898398e --- /dev/null +++ b/testsuite/x.err @@ -0,0 +1,10 @@ +2,3y +$x +0x +,y +$x +2 +y +x +E addr1.ro +x diff --git a/testsuite/x.pr b/testsuite/x.pr new file mode 100644 index 0000000..29d6383 --- /dev/null +++ b/testsuite/x.pr @@ -0,0 +1 @@ +100 diff --git a/testsuite/x.r b/testsuite/x.r new file mode 100644 index 0000000..7b8b7c0 --- /dev/null +++ b/testsuite/x.r @@ -0,0 +1,19 @@ +line 2 +line 3 +line 3 +line 1 +line 2 +line 3 +line 4 +line 5 +line 2 +line 3 +line 2 +line 3 +line 1 +line 2 +line 3 +line 4 +line 5 +line 2 +line 3 diff --git a/testsuite/x.t b/testsuite/x.t new file mode 100644 index 0000000..0e868b8 --- /dev/null +++ b/testsuite/x.t @@ -0,0 +1,8 @@ +2,3y +$x +0x +,y +$x +2 +y +x diff --git a/testsuite/z.err b/testsuite/z.err new file mode 100644 index 0000000..6a51a2d --- /dev/null +++ b/testsuite/z.err @@ -0,0 +1,2 @@ +z +z diff --git a/testsuite/z.pr b/testsuite/z.pr new file mode 100644 index 0000000..6a51a2d --- /dev/null +++ b/testsuite/z.pr @@ -0,0 +1,2 @@ +z +z |