summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS25
-rw-r--r--COPYING676
-rw-r--r--ChangeLog190
-rw-r--r--INSTALL57
-rw-r--r--Makefile.in123
-rw-r--r--NEWS20
-rw-r--r--README145
-rw-r--r--TODO15
-rw-r--r--buffer.c632
-rw-r--r--carg_parser.c294
-rw-r--r--carg_parser.h102
-rwxr-xr-xconfigure200
-rw-r--r--doc/ed.163
-rw-r--r--doc/ed.info1427
-rw-r--r--doc/ed.texinfo990
-rw-r--r--doc/fdl.texinfo506
-rw-r--r--ed.h153
-rw-r--r--global.c87
-rw-r--r--io.c332
-rw-r--r--main.c205
-rw-r--r--main_loop.c762
-rw-r--r--red.in3
-rw-r--r--regex.c403
-rw-r--r--signal.c265
-rw-r--r--testsuite/a.d5
-rw-r--r--testsuite/a.err3
-rw-r--r--testsuite/a.pr3
-rw-r--r--testsuite/a.r8
-rw-r--r--testsuite/a.t9
-rw-r--r--testsuite/addr.d9
-rw-r--r--testsuite/addr.r2
-rw-r--r--testsuite/addr.t5
-rw-r--r--testsuite/addr1.err1
-rw-r--r--testsuite/addr1.pr1
-rw-r--r--testsuite/addr2.err1
-rw-r--r--testsuite/addr2.pr1
-rw-r--r--testsuite/ascii.dbin0 -> 256 bytes
-rw-r--r--testsuite/ascii.rbin0 -> 256 bytes
-rw-r--r--testsuite/ascii.t0
-rw-r--r--testsuite/bang.d5
-rw-r--r--testsuite/bang.r6
-rw-r--r--testsuite/bang.t6
-rw-r--r--testsuite/bang1.err1
-rw-r--r--testsuite/bang1.pr1
-rw-r--r--testsuite/bang2.err1
-rw-r--r--testsuite/bang2.pr1
-rw-r--r--testsuite/c.d5
-rw-r--r--testsuite/c.err3
-rw-r--r--testsuite/c.pr3
-rw-r--r--testsuite/c.r4
-rw-r--r--testsuite/c.t12
-rwxr-xr-xtestsuite/check.sh107
-rw-r--r--testsuite/comment.d6
-rw-r--r--testsuite/comment.r6
-rw-r--r--testsuite/comment.t5
-rw-r--r--testsuite/d.d5
-rw-r--r--testsuite/d.err1
-rw-r--r--testsuite/d.pr1
-rw-r--r--testsuite/d.r1
-rw-r--r--testsuite/d.t3
-rw-r--r--testsuite/e1.d5
-rw-r--r--testsuite/e1.err1
-rw-r--r--testsuite/e1.pr1
-rw-r--r--testsuite/e1.r5
-rw-r--r--testsuite/e1.t3
-rw-r--r--testsuite/e2.d1
-rw-r--r--testsuite/e2.err1
-rw-r--r--testsuite/e2.pr1
-rw-r--r--testsuite/e2.r1
-rw-r--r--testsuite/e2.t1
-rw-r--r--testsuite/e3.d1
-rw-r--r--testsuite/e3.err1
-rw-r--r--testsuite/e3.pr1
-rw-r--r--testsuite/e3.r1
-rw-r--r--testsuite/e3.t1
-rw-r--r--testsuite/e4.d1
-rw-r--r--testsuite/e4.r1
-rw-r--r--testsuite/e4.t1
-rw-r--r--testsuite/f1.err1
-rw-r--r--testsuite/f1.pr1
-rw-r--r--testsuite/f2.err1
-rw-r--r--testsuite/f2.pr1
-rw-r--r--testsuite/g1.d5
-rw-r--r--testsuite/g1.err1
-rw-r--r--testsuite/g1.pr1
-rw-r--r--testsuite/g1.r20
-rw-r--r--testsuite/g1.t9
-rw-r--r--testsuite/g2.d5
-rw-r--r--testsuite/g2.err1
-rw-r--r--testsuite/g2.pr1
-rw-r--r--testsuite/g2.r1
-rw-r--r--testsuite/g2.t2
-rw-r--r--testsuite/g3.d5
-rw-r--r--testsuite/g3.err1
-rw-r--r--testsuite/g3.pr1
-rw-r--r--testsuite/g3.r5
-rw-r--r--testsuite/g3.t4
-rw-r--r--testsuite/g4.d5
-rw-r--r--testsuite/g4.r7
-rw-r--r--testsuite/g4.t13
-rw-r--r--testsuite/g5.d3
-rw-r--r--testsuite/g5.r9
-rw-r--r--testsuite/g5.t2
-rw-r--r--testsuite/h.err1
-rw-r--r--testsuite/h.pr1
-rw-r--r--testsuite/i.d5
-rw-r--r--testsuite/i.err3
-rw-r--r--testsuite/i.pr3
-rw-r--r--testsuite/i.r8
-rw-r--r--testsuite/i.t9
-rw-r--r--testsuite/j.d5
-rw-r--r--testsuite/j.r4
-rw-r--r--testsuite/j.t2
-rw-r--r--testsuite/k.d5
-rw-r--r--testsuite/k.r5
-rw-r--r--testsuite/k.t10
-rw-r--r--testsuite/k2.err1
-rw-r--r--testsuite/k2.pr1
-rw-r--r--testsuite/k3.err1
-rw-r--r--testsuite/k3.pr1
-rw-r--r--testsuite/k4.err6
-rw-r--r--testsuite/k4.pr6
-rw-r--r--testsuite/m.d5
-rw-r--r--testsuite/m.err4
-rw-r--r--testsuite/m.pr5
-rw-r--r--testsuite/m.r5
-rw-r--r--testsuite/m.t7
-rw-r--r--testsuite/nl.err1
-rw-r--r--testsuite/nl.pr1
-rw-r--r--testsuite/nl1.d5
-rw-r--r--testsuite/nl1.r8
-rw-r--r--testsuite/nl1.t8
-rw-r--r--testsuite/nl2.d5
-rw-r--r--testsuite/nl2.r6
-rw-r--r--testsuite/nl2.t4
-rw-r--r--testsuite/q.d0
-rw-r--r--testsuite/q.err1
-rw-r--r--testsuite/q.pr1
-rw-r--r--testsuite/q.r0
-rw-r--r--testsuite/q.t5
-rw-r--r--testsuite/r.err1
-rw-r--r--testsuite/r.pr1
-rw-r--r--testsuite/r1.d5
-rw-r--r--testsuite/r1.r7
-rw-r--r--testsuite/r1.t3
-rw-r--r--testsuite/r2.d5
-rw-r--r--testsuite/r2.r10
-rw-r--r--testsuite/r2.t1
-rw-r--r--testsuite/r3.d5
-rw-r--r--testsuite/r3.r4
-rw-r--r--testsuite/r3.t2
-rw-r--r--testsuite/s1.d5
-rw-r--r--testsuite/s1.err1
-rw-r--r--testsuite/s1.pr1
-rw-r--r--testsuite/s1.r5
-rw-r--r--testsuite/s1.t6
-rw-r--r--testsuite/s10.err4
-rw-r--r--testsuite/s10.pr5
-rw-r--r--testsuite/s2.d5
-rw-r--r--testsuite/s2.err4
-rw-r--r--testsuite/s2.pr5
-rw-r--r--testsuite/s2.r5
-rw-r--r--testsuite/s2.t4
-rw-r--r--testsuite/s3.d0
-rw-r--r--testsuite/s3.err1
-rw-r--r--testsuite/s3.pr1
-rw-r--r--testsuite/s3.r1
-rw-r--r--testsuite/s3.t6
-rw-r--r--testsuite/s4.err1
-rw-r--r--testsuite/s4.pr1
-rw-r--r--testsuite/s5.err1
-rw-r--r--testsuite/s5.pr1
-rw-r--r--testsuite/s6.err1
-rw-r--r--testsuite/s6.pr1
-rw-r--r--testsuite/s7.err5
-rw-r--r--testsuite/s7.pr6
-rw-r--r--testsuite/s8.err4
-rw-r--r--testsuite/s8.pr5
-rw-r--r--testsuite/s9.err4
-rw-r--r--testsuite/s9.pr5
-rw-r--r--testsuite/t.d5
-rw-r--r--testsuite/t.r17
-rw-r--r--testsuite/t.t4
-rw-r--r--testsuite/t1.err1
-rw-r--r--testsuite/t1.pr1
-rw-r--r--testsuite/t2.err1
-rw-r--r--testsuite/t2.pr1
-rw-r--r--testsuite/u.d5
-rw-r--r--testsuite/u.err1
-rw-r--r--testsuite/u.pr1
-rw-r--r--testsuite/u.r9
-rw-r--r--testsuite/u.t31
-rw-r--r--testsuite/v.d5
-rw-r--r--testsuite/v.r11
-rw-r--r--testsuite/v.t6
-rw-r--r--testsuite/w.d5
-rw-r--r--testsuite/w.r10
-rw-r--r--testsuite/w.t2
-rw-r--r--testsuite/w1.err1
-rw-r--r--testsuite/w1.pr1
-rw-r--r--testsuite/w2.err1
-rw-r--r--testsuite/w2.pr1
-rw-r--r--testsuite/w3.err1
-rw-r--r--testsuite/w3.pr1
-rw-r--r--testsuite/x.d5
-rw-r--r--testsuite/x.err10
-rw-r--r--testsuite/x.pr1
-rw-r--r--testsuite/x.r19
-rw-r--r--testsuite/x.t8
-rw-r--r--testsuite/z.err2
-rw-r--r--testsuite/z.pr2
211 files changed, 8494 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..df41f2b
--- /dev/null
+++ b/AUTHORS
@@ -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>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..4432540
--- /dev/null
+++ b/COPYING
@@ -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.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..1c40c81
--- /dev/null
+++ b/INSTALL
@@ -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
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..5f4c6ad
--- /dev/null
+++ b/NEWS
@@ -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.
diff --git a/README b/README
new file mode 100644
index 0000000..db577d3
--- /dev/null
+++ b/README
@@ -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.
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..7a4b74f
--- /dev/null
+++ b/TODO
@@ -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:
+
diff --git a/ed.h b/ed.h
new file mode 100644
index 0000000..9c16cee
--- /dev/null
+++ b/ed.h
@@ -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;
+ }
+ }
diff --git a/io.c b/io.c
new file mode 100644
index 0000000..68956e1
--- /dev/null
+++ b/io.c
@@ -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;
+ }
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..27ff412
--- /dev/null
+++ b/main.c
@@ -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;
+ }
+ }
diff --git a/red.in b/red.in
new file mode 100644
index 0000000..5e147db
--- /dev/null
+++ b/red.in
@@ -0,0 +1,3 @@
+#! /bin/sh
+bindir=`echo "$0" | sed -e 's,[^/]*$,,'`
+exec "${bindir}"ed --restricted "$@"
diff --git a/regex.c b/regex.c
new file mode 100644
index 0000000..d66b20a
--- /dev/null
+++ b/regex.c
@@ -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
new file mode 100644
index 0000000..c866266
--- /dev/null
+++ b/testsuite/ascii.d
Binary files differ
diff --git a/testsuite/ascii.r b/testsuite/ascii.r
new file mode 100644
index 0000000..c866266
--- /dev/null
+++ b/testsuite/ascii.r
Binary files differ
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