summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS25
-rw-r--r--COPYING676
-rw-r--r--ChangeLog146
-rw-r--r--INSTALL56
-rw-r--r--Makefile.in113
-rw-r--r--NEWS12
-rw-r--r--README152
-rw-r--r--TODO15
-rw-r--r--buffer.c622
-rw-r--r--carg_parser.c275
-rw-r--r--carg_parser.h85
-rwxr-xr-xconfigure220
-rw-r--r--doc/ed.11045
-rw-r--r--doc/ed.info1417
-rw-r--r--doc/ed.texinfo982
-rw-r--r--doc/fdl.texinfo506
-rw-r--r--ed.h142
-rw-r--r--global.c88
-rw-r--r--io.c312
-rw-r--r--main.c199
-rw-r--r--main_loop.c730
-rw-r--r--packaging/ed.changes165
-rw-r--r--packaging/ed.spec59
-rw-r--r--regex.c392
-rw-r--r--signal.c259
-rw-r--r--testsuite/=.err.posix1
-rw-r--r--testsuite/a.d5
-rw-r--r--testsuite/a.r8
-rw-r--r--testsuite/a.t9
-rw-r--r--testsuite/a1.err.posix3
-rw-r--r--testsuite/a2.err3
-rw-r--r--testsuite/a2.pr3
-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/bang1.d0
-rw-r--r--testsuite/bang1.err1
-rw-r--r--testsuite/bang1.pr1
-rw-r--r--testsuite/bang1.r1
-rw-r--r--testsuite/bang1.t5
-rw-r--r--testsuite/bang2.err1
-rw-r--r--testsuite/bang2.pr1
-rw-r--r--testsuite/c.d5
-rw-r--r--testsuite/c.r4
-rw-r--r--testsuite/c.t12
-rw-r--r--testsuite/c1.err3
-rw-r--r--testsuite/c1.pr3
-rwxr-xr-xtestsuite/check.sh104
-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.r4
-rw-r--r--testsuite/e1.t2
-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.r15
-rw-r--r--testsuite/g1.t6
-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.r8
-rw-r--r--testsuite/i.t9
-rw-r--r--testsuite/i1.err.posix3
-rw-r--r--testsuite/i2.err3
-rw-r--r--testsuite/i2.pr3
-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/k1.err.posix1
-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/l.d0
-rw-r--r--testsuite/l.r0
-rw-r--r--testsuite/l.t0
-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/n.d0
-rw-r--r--testsuite/n.r0
-rw-r--r--testsuite/n.t0
-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/p.d0
-rw-r--r--testsuite/p.r0
-rw-r--r--testsuite/p.t0
-rw-r--r--testsuite/q.d0
-rw-r--r--testsuite/q.r0
-rw-r--r--testsuite/q.t5
-rw-r--r--testsuite/q1.err1
-rw-r--r--testsuite/q1.pr1
-rw-r--r--testsuite/r1.d5
-rw-r--r--testsuite/r1.err.posix1
-rw-r--r--testsuite/r1.r7
-rw-r--r--testsuite/r1.t3
-rw-r--r--testsuite/r2.d5
-rw-r--r--testsuite/r2.err1
-rw-r--r--testsuite/r2.pr1
-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.r16
-rw-r--r--testsuite/t1.d5
-rw-r--r--testsuite/t1.err1
-rw-r--r--testsuite/t1.pr1
-rw-r--r--testsuite/t1.r16
-rw-r--r--testsuite/t1.t3
-rw-r--r--testsuite/t2.d5
-rw-r--r--testsuite/t2.err1
-rw-r--r--testsuite/t2.pr1
-rw-r--r--testsuite/t2.r6
-rw-r--r--testsuite/t2.t1
-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
231 files changed, 9528 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..068a449
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,25 @@
+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..c11f22e
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,146 @@
+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 Antonio Diaz Diaz.
+
+This file is a collection of facts, and thus it is not copyrightable,
+but just in case, I give you unlimited permission to copy, distribute
+and modify it.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..3661149
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,56 @@
+Requirements
+------------
+You'll need a C compiler and a C library compatible with GNU libc.
+I use gcc 4.3.3 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 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..551784d
--- /dev/null
+++ b/Makefile.in
@@ -0,0 +1,113 @@
+
+DISTNAME = $(progname)-$(progversion)
+INSTALL = install
+INSTALL_PROGRAM = $(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)
+
+$(progname) : $(objs)
+ $(CC) $(LDFLAGS) -o $(progname) $(objs)
+
+$(progname)_profiled : $(objs)
+ $(CC) $(LDFLAGS) -pg -o $(progname)_profiled $(objs)
+
+main.o : main.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) -DPROGVERSION=\"$(progversion)\" -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
+
+info : $(VPATH)/doc/$(progname).info
+
+$(VPATH)/doc/$(progname).info : $(VPATH)/doc/$(progname).texinfo
+ cd $(VPATH)/doc && makeinfo $(progname).texinfo
+
+Makefile : $(VPATH)/configure $(VPATH)/Makefile.in
+ ./config.status
+
+check : all $(VPATH)/testsuite/check.sh
+ @$(VPATH)/testsuite/check.sh $(VPATH)/testsuite
+
+install : all install-info install-man
+ if test ! -d $(DESTDIR)$(bindir) ; then $(INSTALL_DIR) $(DESTDIR)$(bindir) ; fi
+ $(INSTALL_PROGRAM) ./$(progname) $(DESTDIR)$(bindir)/$(program_prefix)$(progname)
+ -rm -f $(DESTDIR)$(bindir)/$(program_prefix)r$(progname)
+ cd $(DESTDIR)$(bindir) && ln -s $(program_prefix)$(progname) $(program_prefix)r$(progname)
+
+install-info :
+ if test ! -d $(DESTDIR)$(infodir) ; then $(INSTALL_DIR) $(DESTDIR)$(infodir) ; fi
+ $(INSTALL_DATA) $(VPATH)/doc/$(progname).info $(DESTDIR)$(infodir)/$(program_prefix)$(progname).info
+ -install-info --info-dir=$(DESTDIR)$(infodir) $(DESTDIR)$(infodir)/$(program_prefix)$(progname).info
+
+install-man :
+ if test ! -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 --remove $(DESTDIR)$(infodir)/$(program_prefix)$(progname).info $(DESTDIR)$(infodir)/dir
+ -rm -f $(DESTDIR)$(infodir)/$(program_prefix)$(progname).info
+
+uninstall-man :
+ -rm -f $(DESTDIR)$(mandir)/man1/$(program_prefix)$(progname).1
+ -rm -f $(DESTDIR)$(mandir)/man1/$(program_prefix)r$(progname).1
+
+dist :
+ 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/$(progname).info \
+ $(DISTNAME)/doc/$(progname).texinfo \
+ $(DISTNAME)/doc/fdl.texinfo \
+ $(DISTNAME)/testsuite/check.sh \
+ $(DISTNAME)/testsuite/*.t \
+ $(DISTNAME)/testsuite/*.d \
+ $(DISTNAME)/testsuite/*.r \
+ $(DISTNAME)/testsuite/*.pr \
+ $(DISTNAME)/testsuite/*.err \
+ $(DISTNAME)/testsuite/*.posix \
+ $(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..bfb09e2
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,12 @@
+Changes in version 1.4:
+
+The undo command has been fixed to restore the modified status of the
+buffer being edited.
+
+A race condition with user interrupt has been fixed in
+search_and_replace.
+
+The functions resize_line_buffer and resize_undo_buffer have been added
+to definitively fix the aliasing warnings.
+
+Some minor corrections have been made to the manual.
diff --git a/README b/README
new file mode 100644
index 0000000..086e7db
--- /dev/null
+++ b/README
@@ -0,0 +1,152 @@
+GNU ed is an 8-bit clean, more or less POSIX-compliant implementation of
+the standard Unix line editor. These days, full-screen editors have
+rendered `ed' mostly of historical interest. Nonetheless, it appeals to
+a handful of aging programmers who still believe that "Small is Beautiful".
+
+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 ***
+
+The POSIX requirement that an address range not be used where at most
+a single address is expected has been relaxed in this version of ed.
+Therefore, the following script templates are disabled:
+=-err.t.posix
+a1-err.posix
+i1-err.posix
+k1-err.posix
+r1-err.posix
+
+To use these, remove the .posix suffix and run the tests as described
+above.
+
+
+Copyright (C) 2006, 2007, 2008, 2009 Antonio Diaz Diaz.
+
+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 this
+file.
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..fbb1cfc
--- /dev/null
+++ b/buffer.c
@@ -0,0 +1,622 @@
+/* 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 Antonio Diaz Diaz.
+
+ 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 char isbinary_ = 0; /* if set, buffer contains ASCII NULs */
+static char modified_ = 0; /* if set, buffer modified since last write */
+static char newline_added_ = 0; /* if set, newline appended to input file */
+
+static int seek_write = 0; /* 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_; }
+
+char isbinary( void ) { return isbinary_; }
+void set_binary( void ) { isbinary_ = 1; }
+
+char modified( void ) { return modified_; }
+void set_modified( const char m ) { modified_ = m; }
+
+char newline_added( void ) { return newline_added_; }
+void set_newline_added( void ) { newline_added_ = 1; }
+
+
+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 *prev, line_t *next )
+ { prev->q_forw = next; next->q_back = prev; }
+
+
+/* insert node into circular queue after previous */
+static void insert_node( line_t *node, line_t *prev )
+ {
+ link_nodes( node, prev->q_forw );
+ link_nodes( prev, node );
+ }
+
+
+/* remove node from circular queue */
+//static void remove_node( const line_t *node )
+// { link_nodes( node->q_back, node->q_forw ); }
+
+
+/* add a line node in the editor buffer after the given line */
+static void add_line_node( line_t *lp, const int addr )
+ {
+ line_t *p = search_line_node( addr );
+ insert_node( lp, p );
+ ++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 *lp )
+ {
+ line_t *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 to after line n; stop when either a single
+ period is read or EOF */
+char append_lines( const char *ibufp, const int addr, const char isglobal )
+ {
+ int len;
+ undo_t *up = 0;
+ current_addr_ = addr;
+
+ while( 1 )
+ {
+ if( !isglobal )
+ {
+ ibufp = get_tty_line( &len );
+ if( !ibufp ) return 0;
+ if( !len || ibufp[len-1] != '\n' ) { clearerr( stdin ); return !len; }
+ }
+ else
+ {
+ if( !*ibufp ) return 1;
+ for( len = 0; ibufp[len++] != '\n'; ) ;
+ }
+ if( len == 2 && ibufp[0] == '.' ) return 1;
+ disable_interrupts();
+ if( !put_sbuf_line( ibufp, current_addr_ ) )
+ { enable_interrupts(); return 0; }
+ if( up ) up->tail = search_line_node( current_addr_ );
+ else if( !( up = push_undo_atom( UADD, -1, -1 ) ) )
+ { enable_interrupts(); return 0; }
+ ibufp += len;
+ modified_ = 1;
+ 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 *cp = lp->q_forw;
+ link_nodes( lp->q_back, lp->q_forw );
+ free( lp );
+ lp = cp;
+ }
+ enable_interrupts();
+ }
+
+
+/* close scratch file */
+char 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 0;
+ }
+ sfp = 0;
+ }
+ sfpos = 0; seek_write = 0;
+ return 1;
+ }
+
+
+/* copy a range of lines; return false if error */
+char 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 0; }
+ add_line_node( lp, current_addr_++ );
+ if( up ) up->tail = lp;
+ else if( !( up = push_undo_atom( UADD, -1, -1 ) ) )
+ { enable_interrupts(); return 0; }
+ modified_ = 1;
+ enable_interrupts();
+ }
+ return 1;
+ }
+
+
+/* delete a range of lines */
+char delete_lines( const int from, const int to, const char isglobal )
+ {
+ line_t *n, *p;
+
+ if( !yank_lines( from, to ) ) return 0;
+ disable_interrupts();
+ if( !push_undo_atom( UDEL, from, to ) )
+ { enable_interrupts(); return 0; }
+ 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_ = 1;
+ enable_interrupts();
+ return 1;
+ }
+
+
+/* return line number of pointer */
+int get_line_node_addr( const line_t *lp )
+ {
+ const line_t *cp = &buffer_head;
+ int addr = 0;
+
+ while( cp != lp && ( cp = cp->q_forw ) != &buffer_head ) ++addr;
+ if( addr && cp == &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 *lp )
+ {
+ static char *buf = 0;
+ static int bufsz = 0;
+ int len, ct;
+
+ if( lp == &buffer_head ) return 0;
+ seek_write = 1; /* 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;
+ ct = fread( buf, 1, len, sfp );
+ if( ct < 0 || ct != 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 */
+char 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 0;
+ link_nodes( &buffer_head, &buffer_head );
+ link_nodes( &yank_buffer_head, &yank_buffer_head );
+ return 1;
+ }
+
+
+/* replace a range of lines with the joined text of those lines */
+char join_lines( const int from, const int to, const char isglobal )
+ {
+ static char *buf = 0;
+ static int bufsz = 0;
+ int size = 0;
+ line_t *ep = search_line_node( inc_addr( to ) );
+ line_t *bp = search_line_node( from );
+
+ while( bp != ep )
+ {
+ char *s = get_sbuf_line( bp );
+ if( !s || !resize_buffer( &buf, &bufsz, size + bp->len ) ) return 0;
+ memcpy( buf + size, s, bp->len );
+ size += bp->len;
+ bp = bp->q_forw;
+ }
+ if( !resize_buffer( &buf, &bufsz, size + 2 ) ) return 0;
+ memcpy( buf + size, "\n", 2 );
+ if( !delete_lines( from, to, isglobal ) ) return 0;
+ current_addr_ = from - 1;
+ disable_interrupts();
+ if( !put_sbuf_line( buf, current_addr_ ) ||
+ !push_undo_atom( UADD, -1, -1 ) ) { enable_interrupts(); return 0; }
+ modified_ = 1;
+ enable_interrupts();
+ return 1;
+ }
+
+
+/* move a range of lines */
+char move_lines( const int first_addr, const int second_addr, const int addr,
+ const char 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 0; }
+ 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_ = 1;
+ enable_interrupts();
+ return 1;
+ }
+
+
+/* open scratch file */
+char open_sbuf( void )
+ {
+ isbinary_ = newline_added_ = 0;
+ sfp = tmpfile();
+ if( !sfp )
+ {
+ show_strerror( 0, errno );
+ set_error_msg( "Cannot open temp file" );
+ return 0;
+ }
+ return 1;
+ }
+
+
+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 */
+char put_lines( const int addr )
+ {
+ undo_t *up = 0;
+ line_t *lp = yank_buffer_head.q_forw, *cp;
+
+ if( lp == &yank_buffer_head ) { set_error_msg( "Nothing to put" ); return 0; }
+ current_addr_ = addr;
+ while( lp != &yank_buffer_head )
+ {
+ disable_interrupts();
+ if( !( cp = dup_line_node( lp ) ) ) { enable_interrupts(); return 0; }
+ add_line_node( cp, current_addr_++ );
+ if( up ) up->tail = cp;
+ else if( !( up = push_undo_atom( UADD, -1, -1 ) ) )
+ { enable_interrupts(); return 0; }
+ modified_ = 1;
+ lp = lp->q_forw;
+ enable_interrupts();
+ }
+ return 1;
+ }
+
+
+/* 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 *cs, const int addr )
+ {
+ line_t *lp = dup_line_node( 0 );
+ int len, ct;
+ const char *s = cs;
+
+ if( !lp ) return 0;
+ while( *s != '\n' ) ++s; /* assert: cs is '\n' terminated */
+ if( s - cs >= INT_MAX ) /* max chars per line */
+ { set_error_msg( "Line too long" ); return 0; }
+ len = s - cs;
+ /* 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 = 0;
+ }
+ ct = fwrite( cs, 1, len, sfp ); /* assert: interrupts disabled */
+ if( ct < 0 || ct != len )
+ {
+ sfpos = -1;
+ show_strerror( 0, errno );
+ set_error_msg( "Cannot write temp file" );
+ return 0;
+ }
+ lp->len = len; lp->pos = sfpos;
+ add_line_node( lp, addr );
+ ++current_addr_;
+ sfpos += len; /* update file position */
+ return ++s;
+ }
+
+
+/* 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 */
+char yank_lines( const int from, const int to )
+ {
+ line_t *ep = search_line_node( inc_addr( to ) );
+ line_t *bp = search_line_node( from );
+ line_t *lp = &yank_buffer_head;
+ line_t *cp;
+
+ clear_yank_buffer();
+ while( bp != ep )
+ {
+ disable_interrupts();
+ cp = dup_line_node( bp );
+ if( !cp ) { enable_interrupts(); return 0; }
+ insert_node( cp, lp );
+ bp = bp->q_forw; lp = cp;
+ enable_interrupts();
+ }
+ return 1;
+ }
+
+
+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_addr_last = -1; /* if < 0, undo disabled */
+static char u_modified = 0;
+
+
+void clear_undo_stack( void )
+ {
+ while( u_ptr-- )
+ if( ustack[u_ptr].type == UDEL )
+ {
+ line_t *ep = ustack[u_ptr].tail->q_forw;
+ line_t *lp = ustack[u_ptr].head;
+ while( lp != ep )
+ {
+ line_t *tl = lp->q_forw;
+ unmark_line_node( lp );
+ free( lp );
+ lp = tl;
+ }
+ }
+ u_ptr = 0;
+ u_current_addr = current_addr_;
+ u_addr_last = last_addr_;
+ u_modified = modified_;
+ }
+
+
+void reset_undo_state( void )
+ {
+ clear_undo_stack();
+ u_current_addr = u_addr_last = -1;
+ u_modified = 0;
+ }
+
+
+/* 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_addr_last = -1;
+ }
+ enable_interrupts();
+ return 0;
+ }
+ enable_interrupts();
+ ustack[u_ptr].type = type;
+ ustack[u_ptr].tail = search_line_node( ( to >= 0 ) ? to : current_addr_ );
+ ustack[u_ptr].head = search_line_node( ( from >= 0 ) ? from : current_addr_ );
+ return ustack + u_ptr++;
+ }
+
+
+/* undo last change to the editor buffer */
+char undo( const char isglobal )
+ {
+ int n;
+ const int o_current_addr = current_addr_;
+ const int o_addr_last = last_addr_;
+ const char o_modified = modified_;
+
+ if( u_ptr <= 0 || u_current_addr < 0 || u_addr_last < 0 )
+ { set_error_msg( "Nothing to undo" ); return 0; }
+ 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_addr_last; u_addr_last = o_addr_last;
+ modified_ = u_modified; u_modified = o_modified;
+ enable_interrupts();
+ return 1;
+ }
diff --git a/carg_parser.c b/carg_parser.c
new file mode 100644
index 0000000..6b5cf55
--- /dev/null
+++ b/carg_parser.c
@@ -0,0 +1,275 @@
+/* Arg_parser - A POSIX/GNU command line argument parser. C version
+ Copyright (C) 2006, 2007, 2008, 2009 Antonio Diaz Diaz.
+
+ 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 <stdlib.h>
+#include <string.h>
+
+#include "carg_parser.h"
+
+
+/* assure at least a minimum size for buffer `buf' */
+void * ap_resize_buffer( void *buf, const int min_size )
+ {
+ if( buf ) buf = realloc( buf, min_size );
+ else buf = malloc( min_size );
+ return buf;
+ }
+
+
+char push_back_record( Arg_parser * ap, const int code, const char * argument )
+ {
+ const int len = strlen( argument );
+ ap_Record *p;
+ void * tmp = ap_resize_buffer( ap->data, ( ap->data_size + 1 ) * sizeof( ap_Record ) );
+ if( !tmp ) return 0;
+ ap->data = (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;
+ }
+
+
+char add_error( Arg_parser * ap, const char * 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;
+ }
+
+
+void free_data( Arg_parser * 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;
+ }
+
+
+char parse_long_option( Arg_parser * ap,
+ const char * const opt, const char * const arg,
+ const ap_Option options[], int * 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, "" );
+ }
+
+
+char parse_short_option( Arg_parser * ap,
+ const char * const opt, const char * const arg,
+ const ap_Option options[], int * argindp )
+ {
+ int cind = 1; // character index in opt
+
+ while( cind > 0 )
+ {
+ int index = -1;
+ int i;
+ const unsigned char code = opt[cind];
+ const char code_str[2] = { code, 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( Arg_parser * ap, const int argc, const char * const argv[],
+ const 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( Arg_parser * ap )
+ {
+ free_data( ap );
+ if( ap->error ) { free( ap->error ); ap->error = 0; }
+ ap->error_size = 0;
+ }
+
+
+const char * ap_error( const Arg_parser * ap ) { return ap->error; }
+
+
+int ap_arguments( const Arg_parser * ap ) { return ap->data_size; }
+
+
+int ap_code( const Arg_parser * ap, const int i )
+ {
+ if( i >= 0 && i < ap_arguments( ap ) ) return ap->data[i].code;
+ else return 0;
+ }
+
+
+const char * ap_argument( const Arg_parser * 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..d343c3c
--- /dev/null
+++ b/carg_parser.h
@@ -0,0 +1,85 @@
+/* Arg_parser - A POSIX/GNU command line argument parser. C version
+ Copyright (C) 2006, 2007, 2008, 2009 Antonio Diaz Diaz.
+
+ 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/>.
+*/
+
+/* 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>'.
+*/
+
+typedef enum { ap_no, ap_yes, ap_maybe } ap_Has_arg;
+
+typedef struct
+ {
+ int code; // Short option letter or code ( code != 0 )
+ const char * name; // Long option name (maybe null)
+ ap_Has_arg has_arg;
+ }
+ap_Option;
+
+
+typedef struct
+ {
+ int code;
+ char * argument;
+ }
+ap_Record;
+
+
+typedef struct
+ {
+ ap_Record * data;
+ char * error;
+ int data_size;
+ int error_size;
+ }
+Arg_parser;
+
+
+char ap_init( Arg_parser * ap, const int argc, const char * const argv[],
+ const ap_Option options[], const char in_order );
+
+void ap_free( Arg_parser * ap );
+
+const char * ap_error( const Arg_parser * ap );
+
+ // The number of arguments parsed (may be different from argc)
+int ap_arguments( const Arg_parser * 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 Arg_parser * ap, const int i );
+
+const char * ap_argument( const Arg_parser * ap, const int i );
diff --git a/configure b/configure
new file mode 100755
index 0000000..b9d4d3f
--- /dev/null
+++ b/configure
@@ -0,0 +1,220 @@
+#! /bin/sh
+# configure script for GNU ed - The GNU line editor
+# Copyright (C) 2006, 2007, 2008, 2009 Antonio Diaz Diaz.
+#
+# This configure script is free software: you have unlimited permission
+# to copy, distribute and modify it.
+#
+# Date of this version: 2009-07-10
+
+invocation_name=$0
+args=
+no_create=
+progname=ed
+progversion=1.4
+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=
+CXX=
+CPPFLAGS=
+CFLAGS='-Wall -W -O2'
+CXXFLAGS='-Wall -W -O2'
+LDFLAGS=
+
+# Loop over all args
+while [ x"$1" != x ] ; 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 " CXX=COMPILER C++ compiler to use [g++]"
+ echo " CPPFLAGS=OPTIONS command line options for the preprocessor [${CPPFLAGS}]"
+ echo " CFLAGS=OPTIONS command line options for the C compiler [${CFLAGS}]"
+ echo " CXXFLAGS=OPTIONS command line options for the C++ compiler [${CXXFLAGS}]"
+ echo " LDFLAGS=OPTIONS command line options for the linker [${LDFLAGS}]"
+ echo
+ exit 0 ;;
+ --version | --ve* | -V)
+ echo "Configure script for GNU ${progname} version ${progversion}"
+ 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* | --in*)
+ 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} ;;
+ CXX=*) CXX=${optarg} ;;
+ CPPFLAGS=*) CPPFLAGS=${optarg} ;;
+ CFLAGS=*) CFLAGS=${optarg} ;;
+ CXXFLAGS=*) CXXFLAGS=${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 [ x"${srcdir}" = x ] ; 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 ${invocation_name} | 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 [ x"${CC}" = x ] ; 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
+
+# checking whether we are using GNU C++.
+if [ x"${CXX}" = x ] ; then # Let the user override the test.
+ if [ -x /bin/g++ ] ||
+ [ -x /usr/bin/g++ ] ||
+ [ -x /usr/local/bin/g++ ] ; then
+ CXX="g++"
+ else
+ CXX="c++"
+ fi
+fi
+
+echo
+if [ x${no_create} = x ] ; 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 ${invocation_name} ${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 "CXX = ${CXX}"
+echo "CPPFLAGS = ${CPPFLAGS}"
+echo "CFLAGS = ${CFLAGS}"
+echo "CXXFLAGS = ${CXXFLAGS}"
+echo "LDFLAGS = ${LDFLAGS}"
+rm -f Makefile
+cat > Makefile << EOF
+# Makefile for GNU ed - The GNU line editor
+# Copyright (C) 2006, 2007, 2008, 2009 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.
+
+progname = ${progname}
+progversion = ${progversion}
+VPATH = ${srcdir}
+prefix = ${prefix}
+exec_prefix = ${exec_prefix}
+bindir = ${bindir}
+datadir = ${datadir}
+infodir = ${infodir}
+mandir = ${mandir}
+sysconfdir = ${sysconfdir}
+program_prefix = ${program_prefix}
+CC = ${CC}
+CXX = ${CXX}
+CPPFLAGS = ${CPPFLAGS}
+CFLAGS = ${CFLAGS}
+CXXFLAGS = ${CXXFLAGS}
+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..4a3e81e
--- /dev/null
+++ b/doc/ed.1
@@ -0,0 +1,1045 @@
+.TH ED 1 "13 June 2009"
+.SH NAME
+ed, red \- text editor
+.SH SYNOPSIS
+ed [-GVhs] [-p \fIstring\fR] [\fIfile\fR]
+.LP
+red [-GVhs] [-p \fIstring\fR] [\fIfile\fR]
+.SH DESCRIPTION
+.B ed
+is a line-oriented text editor.
+It is used to create, display, modify and otherwise manipulate text
+files.
+.B red
+is a restricted
+.BR ed :
+it can only edit files in the current
+directory and cannot execute shell commands.
+
+If invoked with a
+.I file
+argument, then a copy of
+.I file
+is read into the editor's buffer.
+Changes are made to this copy and not directly to
+.I file
+itself.
+Upon quitting
+.BR ed ,
+any changes not explicitly saved with a
+.I `w'
+command are lost.
+
+Editing is done in two distinct modes:
+.I command
+and
+.IR input .
+When first invoked,
+.B 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:
+.sp
+.RS
+,s/\fIold\fR/\fInew\fR/g
+.RE
+.sp
+which replaces all occurrences of the string
+.I old
+with
+.IR new .
+
+When an input command, such as
+.I `a'
+(append),
+.I `i'
+(insert) or
+.I `c'
+(change), is given,
+.B 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. Lines consist of text up to and
+including a
+.IR newline
+character.
+Input mode is terminated by
+entering a single period (\fI.\fR) on a line.
+
+All
+.B ed
+commands operate on whole lines or ranges of lines; e.g.,
+the
+.I `d'
+command deletes lines; the
+.I `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
+.I `s'
+command is applied to whole lines at a time.
+
+In general,
+.B 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:
+.sp
+.RS
+.I [address [,address]]command[parameters]
+.RE
+.sp
+The 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.
+
+.SS OPTIONS
+.TP 8
+-G
+Forces backwards compatibility. Affects the commands
+.IR `G' ,
+.IR `V' ,
+.IR `f' ,
+.IR `l' ,
+.IR `m' ,
+.IR `t' ,
+and
+.IR `!!' .
+.TP 8
+-s
+Suppresses diagnostics. This should be used if
+.BR ed 's
+standard input is from a script.
+
+.TP 8
+.RI \-p \ string
+Specifies a command prompt. This may be toggled on and off with the
+.I `P'
+command.
+
+.TP 8
+.I file
+Specifies the name of a file to read. If
+.I 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
+.I file
+executed via
+.IR sh (1).
+To read a file whose name begins with a bang, prefix the
+name with a backslash (\e).
+The default filename is set to
+.I file
+only if it is not prefixed with a bang.
+
+.SS LINE ADDRESSING
+An address represents the number of a line in the buffer.
+.B ed
+maintains a
+.I 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.
+.IR +
+and
+.IR - )
+and whitespace.
+Addresses are read from left to right, and their values are computed
+relative to the current address.
+
+One exception to the rule that addresses represent line numbers is the
+address
+.I 0
+(zero).
+This means "before the first line,"
+and is legal 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
+.IR n- tuple
+of addresses is given where
+.I n > 2,
+then the corresponding range is determined by the last two addresses in
+the
+.IR 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.
+
+.TP 8
+\&.
+The current line (address) in the buffer.
+
+.TP 8
+$
+The last line in the buffer.
+
+.TP 8
+.I n
+The
+.IR n th,
+line in the buffer
+where
+.I n
+is a number in the range
+.IR [0,$] .
+
+.TP 8
+-
+The previous line.
+This is equivalent to
+.I -1
+and may be repeated with cumulative effect.
+
+.TP 8
+^\fIn\fR
+The
+.IR n th
+previous line, where
+.I n
+is a non-negative number.
+
+.TP 8
++
+The
+next line.
+This is equivalent to
+.I +1
+and may be repeated with cumulative effect.
+
+.HP
+.I whitespace n
+.TP 8
++\fIn\fR
+The
+.IR n th
+next line, where
+.I n
+is a non-negative number.
+.I Whitespace
+followed by a number
+.I n
+is interpreted as
+.IR +n .
+
+.TP 8
+,
+The first through last lines in the buffer. This is equivalent to
+the address range
+.IR 1,$ .
+
+.TP 8
+;
+The current through last lines in the buffer. This is equivalent to
+the address range
+.IR .,$ .
+
+.TP 8
+.RI / re /
+The
+next line containing the regular expression
+.IR re .
+The search wraps to the beginning of the buffer and continues down to the
+current line, if necessary.
+// repeats the last search.
+
+.TP 8
+.RI ? re ?
+The
+previous line containing the regular expression
+.IR re .
+The search wraps to the end of the buffer and continues up to the
+current line, if necessary.
+?? repeats the last search.
+
+.TP 8
+.RI ' lc
+The
+line previously marked by a
+.I `k'
+(mark) command, where
+.I lc
+is a lower case letter.
+
+.SS REGULAR EXPRESSIONS
+Regular expressions are patterns used in selecting text.
+For example, the
+.B ed
+command
+.sp
+.RS
+g/\fIstring\fR/
+.RE
+.sp
+prints all lines containing
+.IR string .
+Regular expressions are also
+used by the
+.I `s'
+command for selecting old text to be replaced with new.
+
+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:
+
+.TP 8
+c
+Any character
+.I c
+not listed below, including `{', '}', `(', `)', `<' and `>',
+matches itself.
+
+.TP 8
+\e\fIc\fR
+A backslash-escaped character
+.IR c
+other than `{', '}', `(', `)', `<', `>',
+`b', 'B', `w', `W', `+', and `?'
+matches itself.
+
+.TP 8
+\&.
+Matches any single character.
+
+.TP 8
+.I [char-class]
+Matches any single character in
+.IR char-class .
+To include a `]'
+in
+.IR 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
+.I char-class
+to specify sets of characters:
+.sp
+.nf
+\ \ [:alnum:]\ \ [:cntrl:]\ \ [:lower:]\ \ [:space:]
+.PD 0
+\ \ [:alpha:]\ \ [:digit:]\ \ [:print:]\ \ [:upper:]
+.PD 0
+\ \ [:blank:]\ \ [:graph:]\ \ [:punct:]\ \ [:xdigit:]
+.fi
+.sp
+If `-' appears as the first or last
+character of
+.IR char-class ,
+then it matches itself.
+All other characters in
+.I char-class
+match themselves.
+.sp
+Patterns in
+.I char-class
+of the form:
+.sp
+\ \ [.\fIcol-elm\fR.] or,
+.PD 0
+\ \ [=\fIcol-elm\fR=]
+.sp
+where
+.I col-elm
+is a
+.I collating element
+are interpreted according to
+.IR locale (5)
+(not currently supported).
+See
+.IR regex (3)
+for an explanation of these constructs.
+
+.TP 8
+[^\fIchar-class\fR]
+Matches any single character, other than newline, not in
+.IR char-class .
+.IR char-class
+is defined
+as above.
+
+.TP 8
+^
+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.
+
+.TP 8
+$
+If `$' is the last character of a regular expression, it
+anchors the regular expression to the end of a line.
+Otherwise, it matches itself.
+
+.TP 8
+\e(\fIre\fR\e)
+Defines a (possibly null) subexpression
+.IR re .
+Subexpressions may be nested.
+A subsequent backreference of the form `\e\fIn\fR', where
+.I n
+is a number in the range [1,9], expands to the text matched by the
+.IR n th
+subexpression.
+For example, the regular expression `\e(a.c\e)\e1' matches the
+string `abcabc', but not `abcadc'.
+Subexpressions are ordered relative to
+their left delimiter.
+
+.TP 8
+*
+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.
+
+.HP
+\fR\e{\fIn,m\fR\e}\fR
+.HP
+\fR\e{\fIn,\fR\e}\fR
+.TP 8
+\fR\e{\fIn\fR\e}\fR
+Matches the single character regular expression or subexpression
+immediately preceding it at least
+.I n
+and at most
+.I m
+times.
+If
+.I m
+is omitted, then it matches at least
+.I n
+times.
+If the comma is also omitted, then it matches exactly
+.I 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 `\e{2\e}' matches the string `{2}', and so on).
+
+.HP
+\e<
+.TP 8
+\fR\e>\fR
+Anchors the single character regular expression or subexpression
+immediately following it to the beginning (\e<) or ending
+(\e>) of a \fIword\fR, i.e., in ASCII, a maximal string of alphanumeric
+characters, including the underscore (_).
+
+
+.LP
+The following extended operators are preceded by a backslash (\e) to
+distinguish them from traditional
+.B ed
+syntax.
+
+.HP
+\fR\e\`\fR
+.TP 8
+\fR\e\'\fR
+Unconditionally matches the beginning (\e\`) or ending (\e\') of a line.
+
+.TP 8
+\fR\e?\fR
+Optionally matches the single character regular expression or subexpression
+immediately preceding it. For example, the regular expression `a[bd]\e?c'
+matches the strings `abc', `adc' and `ac'. If \e? occurs at the beginning
+of a regular expressions or subexpression, then it matches a literal `?'.
+
+.TP 8
+\fR\e+\fR
+Matches the single character regular expression or subexpression
+immediately preceding it one or more times. So the regular expression
+`a\e+' is shorthand for `aa*'. If \e+ occurs at the beginning of a
+regular expression or subexpression, then it matches a literal `+'.
+
+
+.TP 8
+\fR\eb\fR
+Matches the beginning or ending (null string) of a word. Thus the regular
+expression `\ebhello\eb' is equivalent to `\e<hello\e>'. However, `\eb\eb'
+is a valid regular expression whereas `\e<\e>' is not.
+
+.TP 8
+\fR\eB\fR
+Matches (a null string) inside a word.
+
+.TP 8
+\fR\ew\fR
+Matches any character in a word.
+
+.TP 8
+\fR\eW\fR
+Matches any character not in a word.
+
+.SS COMMANDS
+All
+.B ed
+commands are single characters, though some require additional parameters.
+If a command's parameters extend over several lines, then
+each line except for the last
+must be terminated with a backslash (\e).
+
+In general, at most one command is allowed per line.
+However, most commands accept a print suffix, which is any of
+.I `p'
+(print),
+.I `l'
+(list) ,
+or
+.I `n'
+(enumerate),
+to print the last line affected by the command.
+
+An interrupt (typically ^C) has the effect of aborting the current command
+and returning the editor to command mode.
+
+.B ed
+recognizes the following commands. The commands are shown together with
+the default address or address range supplied if none is
+specified (in parenthesis).
+
+.TP 8
+(.)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.
+
+.TP 8
+(.,.)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.
+
+.TP 8
+(.,.)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.
+
+.TP 8
+.RI e \ file
+Edits
+.IR file ,
+and sets the default filename.
+If
+.I 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.
+
+.TP 8
+e !\fIcommand\fR
+Edits the standard output of
+.IR `!command' ,
+(see
+.RI ! command
+below).
+The default filename is unchanged.
+Any lines in the buffer are deleted before the output of
+.I command
+is read.
+The current address is set to the last line read.
+
+.TP 8
+.RI E \ file
+Edits
+.I file
+unconditionally.
+This is similar to the
+.I e
+command,
+except that unwritten changes are discarded without warning.
+The current address is set to the last line read.
+
+.TP 8
+.RI f \ file
+Sets the default filename to
+.IR file .
+If
+.I file
+is not specified, then the default unescaped filename is printed.
+
+.TP 8
+.RI (1,$)g /re/command-list
+Applies
+.I command-list
+to each of the addressed lines matching a regular expression
+.IR re .
+The current address is set to the
+line currently matched before
+.I command-list
+is executed.
+At the end of the
+.I `g'
+command, the current address is set to the last line affected by
+.IR command-list .
+
+Each command in
+.I command-list
+must be on a separate line,
+and every line except for the last must be terminated by a backslash
+(\e).
+Any commands are allowed, except for
+.IR `g' ,
+.IR `G' ,
+.IR `v' ,
+and
+.IR `V' .
+A newline alone in
+.I command-list
+is equivalent to a
+.I `p'
+command.
+
+.TP 8
+.RI (1,$)G /re/
+Interactively edits the addressed lines matching a regular expression
+.IR re.
+For each matching line,
+the line is printed,
+the current address is set,
+and the user is prompted to enter a
+.IR command-list .
+At the end of the
+.I `G'
+command, the current address
+is set to the last line affected by (the last)
+.IR command-list .
+
+The format of
+.I command-list
+is the same as that of the
+.I `g'
+command. A newline alone acts as a null command list.
+A single `&' repeats the last non-null command list.
+
+.TP 8
+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.
+
+.TP 8
+h
+Prints an explanation of the last error.
+
+.TP 8
+(.)i
+Inserts text in the buffer before the current line.
+Text is entered in input mode.
+The current address is set to the last line entered.
+
+.TP 8
+(.,.+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.
+
+.TP 8
+.RI (.)k lc
+Marks a line with a lower case letter
+.IR lc .
+The line can then be addressed as
+.I 'lc
+(i.e., a single quote followed by
+.I lc
+) in subsequent commands. The mark is not cleared until the line is
+deleted or otherwise modified.
+
+.TP 8
+(.,.)l
+Prints the addressed lines unambiguously. If invoked from a terminal,
+.B ed
+pauses at the end of each page until a newline is entered.
+The current address is set to the last line printed.
+
+.TP 8
+(.,.)m(.)
+Moves lines in the buffer. The addressed lines are moved to after the
+right-hand destination address, which may be the address
+.IR 0
+(zero).
+The current address is set to the new address of the last line moved.
+
+.TP 8
+(.,.)n
+Prints the addressed lines along with
+their line numbers. The current address is set to the last line
+printed.
+
+.TP 8
+(.,.)p
+Prints the addressed lines. If invoked from a terminal,
+.B ed
+pauses at the end of each page until a newline is entered.
+The current address is set to the last line
+printed.
+
+.TP 8
+P
+Toggles the command prompt on and off.
+Unless a prompt was specified by with command-line option
+\fI-p string\fR, the command prompt is by default turned off.
+
+.TP 8
+q
+Quits ed.
+
+.TP 8
+Q
+Quits ed unconditionally.
+This is similar to the
+.I q
+command,
+except that unwritten changes are discarded without warning.
+
+.TP 8
+.RI ($)r \ file
+Reads
+.I file
+to after the addressed line. If
+.I file
+is not specified, then the default
+filename is used. If there was no default filename prior to the command,
+then the default filename is set to
+.IR file .
+Otherwise, the default filename is unchanged.
+The current address is set to the last line read.
+
+.TP 8
+($)r !\fIcommand\fR
+Reads
+to after the addressed line
+the standard output of
+.IR `!command' ,
+(see the
+.RI ! command
+below).
+The default filename is unchanged.
+The current address is set to the last line read.
+
+.HP
+.RI (.,.)s /re/replacement/
+.HP
+.RI (.,.)s /re/replacement/\fRg\fR
+.HP
+.RI (.,.)s /re/replacement/n
+.br
+Replaces text in the addressed lines
+matching a regular expression
+.I re
+with
+.IR replacement .
+By default, only the first match in each line is replaced.
+If the
+.I `g'
+(global) suffix is given, then every match to be replaced.
+The
+.I `n'
+suffix, where
+.I n
+is a positive number, causes only the
+.IR 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.
+
+.I re
+and
+.I replacement
+may be delimited by any character other than space, newline and the
+characters used by the form of the
+.I `s'
+command shown below.
+If one or two of the last delimiters is omitted, then the last line
+affected is printed as though the print suffix
+.I `p'
+were specified.
+
+
+An unescaped `&' in
+.I replacement
+is replaced by the currently matched text.
+The character sequence
+\fI`\em'\fR,
+where
+.I m
+is a number in the range [1,9], is replaced by the
+.IR m th
+backreference expression of the matched text.
+If
+.I replacement
+consists of a single `%', then
+.I replacement
+from the last substitution is used.
+Newlines may be embedded in
+.I replacement
+if they are escaped with a backslash (\e).
+
+.TP 8
+(.,.)s
+Repeats the last substitution.
+This form of the
+.I `s'
+command accepts a count suffix
+.IR `n' ,
+and any combination of the characters
+.IR `r' ,
+.IR `g' ,
+and
+.IR `p' .
+If a count suffix
+.I `n'
+is given, then only the
+.IR n th
+match is replaced.
+The
+.I `r'
+suffix causes
+the regular expression of the last search to be used instead of the
+that of the last substitution.
+The
+.I `g'
+suffix toggles the global suffix of the last substitution.
+The
+.I `p'
+suffix toggles the print suffix of the last substitution.
+The current address is set to the last line affected.
+
+.TP 8
+(.,.)t(.)
+Copies (i.e., transfers) the addressed lines to after the right-hand
+destination address, which may be the address
+.IR 0
+(zero).
+The current address is set to the last line
+copied.
+
+.TP 8
+u
+Undoes the last command and restores the current address
+to what it was before the command.
+The global commands
+.IR `g' ,
+.IR `G' ,
+.IR `v' ,
+and
+.IR `V' .
+are treated as a single command by undo.
+.I `u'
+is its own inverse.
+
+.TP 8
+.RI (1,$)v /re/command-list
+Applies
+.I command-list
+to each of the addressed lines not matching a regular expression
+.IR re .
+This is similar to the
+.I `g'
+command.
+
+.TP 8
+.RI (1,$)V /re/
+Interactively edits the addressed lines not matching a regular expression
+.IR re.
+This is similar to the
+.I `G'
+command.
+
+.TP 8
+.RI (1,$)w \ file
+Writes the addressed lines to
+.IR file .
+Any previous contents of
+.I file
+is lost without warning.
+If there is no default filename, then the default filename is set to
+.IR file,
+otherwise it is unchanged. If no filename is specified, then the default
+filename is used.
+The current address is unchanged.
+
+.TP 8
+.RI (1,$)wq \ file
+Writes the addressed lines to
+.IR file ,
+and then executes a
+.I `q'
+command.
+
+.TP 8
+(1,$)w !\fIcommand\fR
+Writes the addressed lines to the standard input of
+.IR `!command' ,
+(see the
+.RI ! command
+below).
+The default filename and current address are unchanged.
+
+.TP 8
+.RI (1,$)W \ file
+Appends the addressed lines to the end of
+.IR file .
+This is similar to the
+.I `w'
+command, expect that the previous contents of file is not clobbered.
+The current address is unchanged.
+
+.TP 8
+(.)x
+Copies (puts) the contents of the cut buffer to after the addressed line.
+The current address is set to the last line copied.
+
+.TP 8
+(.,.)y
+Copies (yanks) the addressed lines to the cut buffer.
+The cut buffer is overwritten by subsequent
+.IR `y' ,
+.IR `s' ,
+.IR `j' ,
+.IR `d' ,
+or
+.I `c'
+commands.
+The current address is unchanged.
+
+.TP 8
+.RI (.+1)z n
+Scrolls
+.I n
+lines at a time starting at addressed line. If
+.I n
+is not specified, then the current window size is used.
+The current address is set to the last line printed.
+
+.TP 8
+.RI ! command
+Executes
+.I command
+via
+.IR sh (1).
+If the first character of
+.I command
+is `!', then it is replaced by text of the
+previous
+.IR `!command' .
+.B ed
+does not process
+.I command
+for backslash (\e) escapes.
+However, an unescaped
+.I `%'
+is replaced by the default filename.
+When the shell returns from execution, a `!'
+is printed to the standard output.
+The current line is unchanged.
+
+.TP 8
+(.,.)#
+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.
+
+.TP 8
+($)=
+Prints the line number of the addressed line.
+
+.TP 8
+(.+1)newline
+Prints the addressed line, and sets the current address to
+that line.
+
+.SH FILES
+.TP 8
+ed.hup
+The file to which
+.B ed
+attempts to write the buffer if the terminal hangs up.
+
+.SH SEE ALSO
+
+.IR vi (1),
+.IR sed (1),
+.IR regex (3),
+.IR sh (1).
+
+USD:12-13
+
+B. W. Kernighan and P. J. Plauger,
+.I Software Tools in Pascal ,
+Addison-Wesley, 1981.
+
+.SH LIMITATIONS
+.B ed
+processes
+.I file
+arguments for backslash escapes, i.e., in a filename,
+any characters preceded by a backslash (\e) are
+interpreted literally.
+
+If a text (non-binary) file is not terminated by a newline character,
+then
+.B ed
+appends one on reading/writing it. In the case of a binary file,
+.B ed
+does not append a newline on reading/writing.
+
+per line overhead: 4 ints
+
+.SH DIAGNOSTICS
+When an error occurs,
+if
+.BR 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
+.I `h'
+(help) command.
+
+Attempting to quit
+.B 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.
+
+.B ed
+exits with 0 if no errors occurred; otherwise >0.
diff --git a/doc/ed.info b/doc/ed.info
new file mode 100644
index 0000000..de0f728
--- /dev/null
+++ b/doc/ed.info
@@ -0,0 +1,1417 @@
+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, 2006, 2007, 2008, 2009 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.4, 10 July 2009).
+
+
+ GNU ed is an 8-bit clean, more or less POSIX-compliant
+implementation of the standard Unix line editor. These days,
+full-screen editors have rendered `ed' mostly of historical interest.
+Nonetheless, it appeals to a handful of aging programmers who still
+believe that "Small is Beautiful".
+
+* 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, 2006, 2007, 2008, 2009 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.
+
+ In summary:
+
+ Structurally, 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:
+
+`--help'
+`-h'
+ Print an informative help message describing the options and exit.
+
+`--version'
+`-V'
+ Print the version number of `ed' on the standard output and exit.
+
+`--loose-exit-status'
+`-l'
+ 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.
+
+`--prompt=STRING'
+`-p STRING'
+ Specifies a command prompt. This may be toggled on and off with the
+ `P' command.
+
+`--quiet'
+`--silent'
+`-s'
+ Suppresses diagnostics. This should be used if `ed''s standard
+ input is from a script.
+
+`--traditional'
+`-G'
+ 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.
+
+`--verbose'
+`-v'
+ 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'.
+
+ `ed' processes FILE arguments for backslash escapes, i.e., in a
+filename, any characters preceded by a backslash (`\') are 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: 4 `int's.
+
+
+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: Top544
+Node: Overview2005
+Node: Introduction to Line Editing4062
+Node: Invoking Ed11305
+Node: Line Addressing12953
+Node: Regular Expressions16056
+Node: Commands21401
+Node: Limitations32546
+Node: Diagnostics33121
+Node: Problems33825
+Node: GNU Free Documentation License34360
+
+End Tag Table
diff --git a/doc/ed.texinfo b/doc/ed.texinfo
new file mode 100644
index 0000000..5b77d4d
--- /dev/null
+++ b/doc/ed.texinfo
@@ -0,0 +1,982 @@
+\input texinfo @c -*-texinfo-*-
+@c %**start of header
+@setfilename ed.info
+@settitle GNU @command{ed} Manual
+@finalout
+@c %**end of header
+
+@set UPDATED 10 July 2009
+@set VERSION 1.4
+
+@dircategory Basics
+@direntry
+* Ed: (ed). The GNU line editor
+@end direntry
+
+@copying
+Copyright @copyright{} 1993, 2006, 2007, 2008, 2009
+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
+
+@titlepage
+@title GNU ed
+@subtitle The GNU line editor
+@subtitle for GNU ed version @value{VERSION}, @value{UPDATED}
+@author by Andrew L. Moore and Antonio Diaz Diaz
+@page
+@vskip 0pt plus 1filll
+@insertcopying
+@end titlepage
+
+@contents
+
+@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 an 8-bit clean, more or less POSIX-compliant implementation of
+the standard Unix line editor. These days, full-screen editors have
+rendered @command{ed} mostly of historical interest. Nonetheless, it
+appeals to a handful of aging programmers who still believe that ``Small
+is Beautiful''.
+@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.
+
+In summary:
+
+Structurally,
+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 --help
+@itemx -h
+Print an informative help message describing the options and exit.
+
+@item --version
+@itemx -V
+Print the version number of @command{ed} on the standard output and exit.
+
+@item --loose-exit-status
+@itemx -l
+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 --prompt=@var{string}
+@itemx -p @var{string}
+Specifies a command prompt. This may be toggled on and off with the
+@samp{P} command.
+
+@item --quiet
+@itemx --silent
+@itemx -s
+Suppresses diagnostics. This should be used if @command{ed}'s standard
+input is from a script.
+
+@item --traditional
+@itemx -G
+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 --verbose
+@itemx -v
+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}.
+
+@command{ed} processes @var{file} arguments for backslash escapes, i.e.,
+in a filename, any characters preceded by a backslash (@samp{\}) are
+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: 4 @code{int}s.
+
+
+@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..aaed957
--- /dev/null
+++ b/ed.h
@@ -0,0 +1,142 @@
+/* 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 Antonio Diaz Diaz.
+
+ 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/>.
+*/
+
+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 */
+char append_lines( const char *ibufp, const int addr, const char isglobal );
+char close_sbuf( void );
+char copy_lines( const int first_addr, const int second_addr, const int addr );
+int current_addr( void );
+int dec_addr( int addr );
+char delete_lines( const int from, const int to, const char isglobal );
+int get_line_node_addr( const line_t *lp );
+char *get_sbuf_line( const line_t *lp );
+int inc_addr( int addr );
+int inc_current_addr( void );
+char init_buffers( void );
+char isbinary( void );
+char join_lines( const int from, const int to, const char isglobal );
+int last_addr( void );
+char modified( void );
+char move_lines( const int first_addr, const int second_addr, const int addr,
+ const char isglobal );
+char newline_added( void );
+char open_sbuf( void );
+int path_max( const char *filename );
+char put_lines( const int addr );
+const char *put_sbuf_line( const char *cs, const int addr );
+line_t *search_line_node( const int n );
+void set_binary( void );
+void set_current_addr( const int addr );
+void set_modified( const char m );
+void set_newline_added( void );
+char 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 );
+char undo( const char isglobal );
+
+/* defined in global.c */
+void clear_active_list( void );
+const line_t *next_active_node( void );
+char set_active_node( const line_t *lp );
+void unset_active_nodes( const line_t *np, const line_t *mp );
+
+/* defined in io.c */
+char display_lines( int from, const int to, const int gflags );
+const char *get_extended_line( const char *ibufp, int *lenp, const char nonl );
+const char *get_tty_line( int *lenp );
+int read_file( const char *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 */
+char is_regular_file( int fd );
+char may_access_filename( const char *name );
+char restricted( void );
+char scripted( void );
+void show_strerror( const char *filename, int errcode );
+char traditional( void );
+
+/* defined in main_loop.c */
+int main_loop( const char loose );
+void set_def_filename( const char *s );
+void set_error_msg( const char *msg );
+void set_prompt( const char *s );
+void set_verbose( void );
+void unmark_line_node( const line_t *lp );
+
+/* defined in regex.c */
+char build_active_list( const char **ibufpp, const int first_addr,
+ const int second_addr, const char match );
+char extract_subst_tail( const char **ibufpp, int *gflagsp, int *snump,
+ const char isglobal );
+int get_matching_node_addr( const char **ibufpp, const char forward );
+char new_compiled_pattern( const char **ibufpp );
+char prev_pattern( void );
+char search_and_replace( const int first_addr, const int second_addr,
+ const int gflags, const int snum, const char isglobal );
+
+/* defined in signal.c */
+void disable_interrupts( void );
+void enable_interrupts( void );
+char parse_int( int *i, const char *str, const char **tail );
+char resize_buffer( char **buf, int *size, int min_size );
+char resize_line_buffer( const line_t ***buf, int *size, int min_size );
+char resize_undo_buffer( undo_t **buf, int *size, int min_size );
+void set_signals( void );
+void set_window_lines( const int lines );
+const char *strip_escapes( const char *s );
+int window_columns( void );
+int window_lines( void );
diff --git a/global.c b/global.c
new file mode 100644
index 0000000..2a71e58
--- /dev/null
+++ b/global.c
@@ -0,0 +1,88 @@
+/* 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 Antonio Diaz Diaz.
+
+ 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 */
+char set_active_node( const line_t *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 0;
+ }
+ enable_interrupts();
+ active_list[active_len++] = lp;
+ return 1;
+ }
+
+
+/* remove a range of lines from the global-active list */
+void unset_active_nodes( const line_t *np, const line_t *mp )
+ {
+ const line_t *lp = np;
+
+ while( lp != mp )
+ {
+ int i;
+ for( i = 0; i < active_len; ++i )
+ {
+ if( ++active_ndx >= active_len ) active_ndx = 0;
+ if( active_list[active_ndx] == lp )
+ { active_list[active_ndx] = 0; break; }
+ }
+ lp = lp->q_forw;
+ }
+ }
diff --git a/io.c b/io.c
new file mode 100644
index 0000000..cce8155
--- /dev/null
+++ b/io.c
@@ -0,0 +1,312 @@
+/* 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 Antonio Diaz Diaz.
+
+ 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 char put_tty_line( const char *s, 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 = *s++;
+ 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 *cp = strchr( escapes, ch );
+ ++col; putchar('\\');
+ if( cp ) putchar( escchars[cp-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');
+ return 1;
+ }
+
+
+/* print a range of lines to stdout */
+char display_lines( int from, const int to, const int gflags )
+ {
+ line_t *ep = search_line_node( inc_addr( to ) );
+ line_t *bp = search_line_node( from );
+
+ if( !from ) { set_error_msg( "Invalid address" ); return 0; }
+ while( bp != ep )
+ {
+ char *s = get_sbuf_line( bp );
+ if( !s ) return 0;
+ set_current_addr( from++ );
+ if( !put_tty_line( s, bp->len, gflags ) ) return 0;
+ bp = bp->q_forw;
+ }
+ return 1;
+ }
+
+
+/* return the parity of escapes at the end of a string */
+static char trailing_escape( const char * const s, int len )
+ {
+ char parity = 0;
+ while( --len >= 0 && s[len] == '\\' ) parity = !parity;
+ return parity;
+ }
+
+
+/* get an extended line from stdin */
+const char *get_extended_line( const char *ibufp, int *lenp, const char nonl )
+ {
+ static char *buf = 0;
+ static int bufsz = 0;
+ int len;
+
+ for( len = 0; ibufp[len++] != '\n'; ) ;
+ if( len < 2 || !trailing_escape( ibufp, len - 1 ) )
+ { if( lenp ) *lenp = len; return ibufp; }
+ if( !resize_buffer( &buf, &bufsz, len ) ) return 0;
+ memcpy( buf, ibufp, len );
+ --len; buf[len-1] = '\n'; /* strip trailing esc */
+ if( nonl ) --len; /* strip newline */
+ while( 1 )
+ {
+ int len2;
+ if( !( ibufp = get_tty_line( &len2 ) ) ) return 0;
+ if( len2 == 0 || ibufp[len2-1] != '\n' )
+ { set_error_msg( "Unexpected end-of-file" ); return 0; }
+ if( !resize_buffer( &buf, &bufsz, len + len2 ) ) return 0;
+ memcpy( buf + len, ibufp, len2 );
+ len += len2;
+ if( len2 < 2 || !trailing_escape( buf, len - 1 ) ) break;
+ --len; buf[len-1] = '\n'; /* strip trailing esc */
+ if( nonl ) --len; /* strip newline */
+ }
+ if( !resize_buffer( &buf, &bufsz, len + 1 ) ) return 0;
+ buf[len] = 0;
+ if( lenp ) *lenp = len;
+ return buf;
+ }
+
+
+/* read a line of text from stdin; return pointer to buffer and line length */
+const char *get_tty_line( int *lenp )
+ {
+ static char *buf = 0;
+ static int bufsz = 0;
+ int i = 0, oi = -1;
+
+ while( 1 )
+ {
+ const int c = getchar();
+ if( c == EOF )
+ {
+ if( ferror( stdin ) )
+ {
+ show_strerror( "stdin", errno ); set_error_msg( "Cannot read stdin" );
+ clearerr( stdin ); if( lenp ) *lenp = 0;
+ return 0;
+ }
+ else
+ {
+ clearerr( stdin ); if( i != oi ) { oi = i; continue; }
+ if( i ) buf[i] = 0; if( lenp ) *lenp = i;
+ return buf;
+ }
+ }
+ else
+ {
+ if( !resize_buffer( &buf, &bufsz, i + 2 ) )
+ { if( lenp ) *lenp = 0; return 0; }
+ buf[i++] = c; if( !c ) set_binary(); if( c != '\n' ) continue;
+ buf[i] = 0; if( lenp ) *lenp = i;
+ return buf;
+ }
+ }
+ }
+
+
+/* read a line of text from a stream */
+static const char * read_stream_line( FILE *fp, int *lenp, char *newline_added_now )
+ {
+ static char *buf = 0;
+ static int bufsz = 0;
+ int c, i = 0;
+
+ while( 1 )
+ {
+ 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_now = 1;
+ if( !isbinary() ) ++i;
+ }
+ }
+ *lenp = i;
+ return buf;
+ }
+
+
+/* read a stream into the editor buffer; return size of data read */
+static long read_stream( FILE *fp, const int addr )
+ {
+ line_t *lp = search_line_node( addr );
+ undo_t *up = 0;
+ long size = 0;
+ const char o_isbinary = isbinary();
+ const char appended = ( addr == last_addr() );
+ char newline_added_now = 0;
+
+ set_current_addr( addr );
+ while( 1 )
+ {
+ int len = 0;
+ const char *buf = read_stream_line( fp, &len, &newline_added_now );
+ if( !buf ) return -1;
+ if( len > 0 ) size += len; else break;
+ disable_interrupts();
+ if( !put_sbuf_line( buf, current_addr() ) )
+ { enable_interrupts(); return -1; }
+ lp = lp->q_forw;
+ if( up ) up->tail = lp;
+ else if( !( up = push_undo_atom( UADD, -1, -1 ) ) )
+ { enable_interrupts(); return -1; }
+ enable_interrupts();
+ }
+ if( addr && appended && size && o_isbinary && newline_added() )
+ fputs( "Newline inserted\n", stderr );
+ else if( newline_added_now && appended )
+ fputs( "Newline appended\n", stderr );
+ if( isbinary() && !o_isbinary && newline_added_now && !appended ) ++size;
+ if( !size ) newline_added_now = 1;
+ if( appended && newline_added_now ) set_newline_added();
+ return size;
+ }
+
+
+/* read a named file/pipe into the buffer; return line count */
+int read_file( const char *filename, const int addr )
+ {
+ FILE *fp;
+ long size;
+
+ 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;
+ }
+ if( ( size = read_stream( fp, addr ) ) < 0 ) return -1;
+ if( ( (*filename == '!' ) ? pclose( fp ) : fclose( fp ) ) < 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 *fp, int from, const int to )
+ {
+ line_t *lp = search_line_node( from );
+ long size = 0;
+
+ while( from && from <= to )
+ {
+ int len;
+ char *s = get_sbuf_line( lp );
+ if( !s ) return -1;
+ len = lp->len;
+ if( from != last_addr() || !isbinary() || !newline_added() )
+ s[len++] = '\n';
+ size += len;
+ while( --len >= 0 )
+ if( fputc( *s++, fp ) < 0 )
+ {
+ 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;
+
+ 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;
+ }
+ if( ( size = write_stream( fp, from, to ) ) < 0 ) return -1;
+ if( ( (*filename == '!' ) ? pclose( fp ) : fclose( fp ) ) < 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..069bce1
--- /dev/null
+++ b/main.c
@@ -0,0 +1,199 @@
+/* GNU ed - The GNU line editor.
+ Copyright (C) 2006, 2007, 2008, 2009 Antonio Diaz Diaz.
+
+ 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 * invocation_name = 0;
+static const char * const Program_name = "GNU Ed";
+static const char * const program_name = "ed";
+static const char * const program_year = "2009";
+
+static char restricted_ = 0; /* invoked as "red" */
+static char scripted_ = 0; /* if set, suppress diagnostics */
+static char traditional_ = 0; /* if set, be backwards compatible */
+
+
+char restricted( void ) { return restricted_; }
+char scripted( void ) { return scripted_; }
+char 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" );
+ printf( " -h, --help display this help and exit\n" );
+ printf( " -V, --version output version information and exit\n" );
+ printf( " -G, --traditional run in compatibility mode\n" );
+ printf( " -l, --loose-exit-status exit with 0 status even if a command fails\n" );
+ printf( " -p, --prompt=STRING use STRING as an interactive prompt\n" );
+ printf( " -s, --quiet, --silent suppress diagnostics\n" );
+ printf( " -v, --verbose be verbose\n" );
+ printf( "Start edit by reading in `file' if given. Read output of shell command\n" );
+ printf( "if `file' begins with a `!'.\n" );
+ printf( "\nReport bugs to <bug-ed@gnu.org>.\n" );
+ printf( "Ed home page: http://www.gnu.org/software/ed/ed.html\n" );
+ printf( "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, %s Antonio Diaz Diaz.\n", program_year );
+ printf( "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n" );
+ printf( "This is free software: you are free to change and redistribute it.\n" );
+ printf( "There is NO WARRANTY, to the extent permitted by law.\n" );
+ }
+
+
+void show_strerror( const char *filename, 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 * msg, const int errcode, const char help )
+ {
+ if( msg && msg[0] != 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] != 0 )
+ fprintf( stderr, "Try `%s --help' for more information.\n", invocation_name );
+ }
+
+
+/* return true if file descriptor is a regular file */
+char is_regular_file( int fd )
+ {
+ struct stat sb;
+ return ( fstat( fd, &sb ) < 0 || S_ISREG( sb.st_mode ) );
+ }
+
+
+char may_access_filename( const char *name )
+ {
+ if( restricted_ && ( *name == '!' || !strcmp( name, ".." ) || strchr( name, '/' ) ) )
+ {
+ set_error_msg( "Shell access restricted" );
+ return 0;
+ }
+ return 1;
+ }
+
+
+int main( const int argc, const char *argv[] )
+ {
+ int n = strlen( argv[0] );
+ char loose = 0;
+ const ap_Option options[] =
+ {
+ { 'G', "traditional", ap_no },
+ { 'h', "help", ap_no },
+ { 'l', "loose-exit-status", ap_no },
+ { 'p', "prompt", ap_yes },
+ { 's', "quiet", ap_no },
+ { 's', "silent", ap_no },
+ { 'v', "verbose", ap_no },
+ { 'V', "version", ap_no },
+ { 0, 0, ap_no } };
+ Arg_parser parser;
+ int argind;
+
+ if( !ap_init( &parser, argc, argv, options, 0 ) )
+ { show_error( "Memory exhausted", 0, 0 ); return 1; }
+ if( ap_error( &parser ) ) /* bad option */
+ { show_error( ap_error( &parser ), 0, 1 ); return 1; }
+ invocation_name = argv[0];
+ restricted_ = ( n > 2 && argv[0][n-3] == 'r' );
+
+ for( argind = 0; argind < ap_arguments( &parser ); ++argind )
+ {
+ const int code = ap_code( &parser, argind );
+ const char * arg = ap_argument( &parser, argind );
+ if( !code ) break; /* no more options */
+ switch( code )
+ {
+ case 'G': traditional_ = 1; break; /* backward compatibility */
+ case 'h': show_help(); return 0;
+ case 'l': loose = 1; break;
+ case 'p': set_prompt( arg ); break;
+ case 's': scripted_ = 1; break;
+ case 'v': set_verbose(); break;
+ case 'V': show_version(); return 0;
+ default: show_error( "internal_error: uncaught option", 0, 0 ); return 3;
+ }
+ }
+ setlocale( LC_ALL, "" );
+ if( !init_buffers() ) return 1;
+
+ while( argind < ap_arguments( &parser ) )
+ {
+ const char * arg = ap_argument( &parser, argind );
+ if( !strcmp( arg, "-" ) ) { scripted_ = 1; ++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..24603b7
--- /dev/null
+++ b/main_loop.c
@@ -0,0 +1,730 @@
+/* GNU ed - The GNU line editor.
+ Copyright (C) 1993, 1994 Andrew Moore, Talke Studio
+ Copyright (C) 2006, 2007, 2008, 2009 Antonio Diaz Diaz.
+
+ 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 { 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-line prompt */
+static const char *ibufp = 0; /* pointer to ed command-line buffer */
+static char *shcmd = 0; /* shell command buffer */
+static int shcmdsz = 0; /* shell command buffer size */
+static int shcmdlen = 0; /* shell command length */
+static int first_addr = 0, second_addr = 0;
+static char verbose = 0; /* if set, print all error messages */
+static char prompt_on = 0; /* if set, show command-line prompt */
+
+
+void set_def_filename( const char *s )
+ {
+ strncpy( def_filename, s, sizeof( def_filename ) );
+ def_filename[sizeof(def_filename)-1] = 0;
+ }
+
+void set_prompt( const char *s )
+ {
+ prompt_on = 1;
+ strncpy( prompt_str, s, sizeof( prompt_str ) );
+ prompt_str[sizeof(prompt_str)-1] = 0;
+ }
+
+void set_verbose( void ) { verbose = 1; }
+
+
+static const line_t *mark[26]; /* line markers */
+static int markno; /* line marker count */
+
+static char mark_line_node( const line_t *lp, int c )
+ {
+ c -= 'a';
+ if( c < 0 || c >= 26 ) { set_error_msg( "Invalid mark character" ); return 0; }
+ if( !mark[c] ) ++markno;
+ mark[c] = lp;
+ return 1;
+ }
+
+
+void unmark_line_node( const line_t *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]);
+ }
+
+
+/* read a shell command from stdin; return substitution status ( -1, 0, +1 ) */
+static int get_shell_command( void )
+ {
+ static char *buf = 0;
+ static int bufsz = 0;
+ const char *s; /* substitution char pointer */
+ int i = 0, len;
+
+ if( restricted() ) { set_error_msg( "Shell access restricted" ); return -1; }
+ s = ibufp = get_extended_line( ibufp, &len, 1 );
+ if( !s ) return -1;
+ if( !resize_buffer( &buf, &bufsz, len + 1 ) ) return -1;
+ buf[i++] = '!'; /* prefix command w/ bang */
+ while( *ibufp != '\n' )
+ {
+ if( *ibufp == '!' )
+ {
+ if( s != ibufp )
+ {
+ if( !resize_buffer( &buf, &bufsz, i + 1 ) ) return -1;
+ buf[i++] = *ibufp++;
+ }
+ else if( !shcmd || ( traditional() && !*( shcmd + 1 ) ) )
+ { set_error_msg( "No previous command" ); return -1; }
+ else
+ {
+ if( !resize_buffer( &buf, &bufsz, i + shcmdlen ) ) return -1;
+ for( s = shcmd + 1; s < shcmd + shcmdlen; ) buf[i++] = *s++;
+ s = ibufp++;
+ }
+ }
+ else if( *ibufp == '%' )
+ {
+ if( !def_filename[0] )
+ { set_error_msg( "No current filename" ); return -1; }
+ len = strlen( s = strip_escapes( def_filename ) );
+ if( !resize_buffer( &buf, &bufsz, i + len ) ) return -1;
+ while( len-- ) buf[i++] = *s++;
+ s = ibufp++;
+ }
+ else
+ {
+ if( !resize_buffer( &buf, &bufsz, i + 2 ) ) return -1;
+ buf[i++] = *ibufp;
+ if( *ibufp++ == '\\' ) buf[i++] = *ibufp++;
+ }
+ }
+ if( !resize_buffer( &shcmd, &shcmdsz, i + 1 ) ) return -1;
+ memcpy( shcmd, buf, i );
+ shcmdlen = i; shcmd[i] = 0;
+ return ( *s == '!' || *s == '%' );
+ }
+
+
+static const char *skip_blanks( const char *s )
+ {
+ while( isspace( (unsigned char)*s ) && *s != '\n' ) ++s;
+ return s;
+ }
+
+
+/* return pointer to copy of filename in the command buffer */
+static const char *get_filename( void )
+ {
+ static char *buf = 0;
+ static int bufsz = 0;
+ int size, n;
+
+ ibufp = skip_blanks( ibufp );
+ if( *ibufp != '\n' )
+ {
+ ibufp = get_extended_line( ibufp, &size, 1 );
+ if( !ibufp ) return 0;
+ if( *ibufp == '!' )
+ {
+ ++ibufp;
+ n = get_shell_command();
+ if( n < 0 ) return 0;
+ if( n ) printf( "%s\n", shcmd + 1 );
+ return shcmd;
+ }
+ else if( size > path_max( 0 ) )
+ { 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, path_max( 0 ) + 1 ) ) return 0;
+ for( n = 0; *ibufp != '\n'; ) buf[n++] = *ibufp++;
+ buf[n] = 0;
+ 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( int *addr_cnt )
+ {
+ const char *hd = ibufp = skip_blanks( ibufp );
+ int addr = current_addr();
+ int first = 1;
+
+ while( 1 )
+ {
+ int n;
+ const unsigned char ch = *ibufp;
+ if( isdigit( ch ) )
+ {
+ if( !first ) { invalid_address(); return -2; };
+ if( !parse_int( &addr, ibufp, &ibufp ) ) return -2;
+ }
+ else switch( ch )
+ {
+ case '+':
+ case '\t':
+ case ' ':
+ case '-': ibufp = skip_blanks( ++ibufp );
+ if( isdigit( (unsigned char)*ibufp ) )
+ {
+ if( !parse_int( &n, ibufp, &ibufp ) ) return -2;
+ addr += ( ( ch == '-' ) ? -n : n );
+ }
+ else if( ch == '+' ) ++addr;
+ else if( ch == '-' ) --addr;
+ break;
+ case '.':
+ case '$': if( !first ) { invalid_address(); return -2; };
+ ++ibufp; addr = ( ( ch == '.' ) ? current_addr() : last_addr() );
+ break;
+ case '/':
+ case '?': if( !first ) { invalid_address(); return -2; };
+ addr = get_matching_node_addr( &ibufp, ch == '/' );
+ if( addr < 0 ) return -2;
+ if( ch == *ibufp ) ++ibufp;
+ break;
+ case '\'':if( !first ) { invalid_address(); return -2; };
+ ++ibufp; addr = get_marked_node_addr( *ibufp++ );
+ if( addr < 0 ) return -2;
+ break;
+ case '%':
+ case ',':
+ case ';': if( first )
+ {
+ ++ibufp; ++*addr_cnt;
+ second_addr = ( ( ch == ';' ) ? current_addr() : 1 );
+ addr = last_addr();
+ break;
+ } /* FALL THROUGH */
+ default: if( ibufp == hd ) return -1; /* EOF */
+ if( addr < 0 || addr > last_addr() )
+ { invalid_address(); return -2; }
+ ++*addr_cnt; return addr;
+ }
+ first = 0;
+ }
+ }
+
+
+/* get line addresses from the command buffer until an invalid address
+ is seen. Return number of addresses read */
+static int extract_addr_range( void )
+ {
+ int addr;
+ int addr_cnt = 0;
+
+ first_addr = second_addr = current_addr();
+ while( ( addr = next_addr( &addr_cnt ) ) >= 0 )
+ {
+ first_addr = second_addr; second_addr = addr;
+ if( *ibufp != ',' && *ibufp != ';' ) break;
+ if( *ibufp++ == ';' ) set_current_addr( addr );
+ }
+ 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 char get_third_addr( int *addr )
+ {
+ int ol1 = first_addr;
+ int ol2 = second_addr;
+
+ int addr_cnt = extract_addr_range(); if( addr_cnt < 0 ) return 0;
+ if( traditional() && addr_cnt == 0 )
+ { set_error_msg( "Destination expected" ); return 0; }
+ if( second_addr < 0 || second_addr > last_addr() )
+ { invalid_address(); return 0; }
+ *addr = second_addr;
+ first_addr = ol1; second_addr = ol2;
+ return 1;
+ }
+
+
+/* return true if address range is valid */
+static char check_addr_range( const int n, const int m, const int addr_cnt )
+ {
+ if( addr_cnt == 0 )
+ {
+ first_addr = ( ( n >= 0 ) ? n : current_addr() );
+ second_addr = ( ( m >= 0 ) ? m : current_addr() );
+ }
+ if( first_addr < 1 || first_addr > second_addr || second_addr > last_addr() )
+ { invalid_address(); return 0; }
+ return 1;
+ }
+
+
+/* verify the command suffix in the command buffer */
+static char get_command_suffix( int *gflagsp )
+ {
+ while( 1 )
+ {
+ const char ch = *ibufp;
+ if( ch == 'l' ) *gflagsp |= GLS;
+ else if( ch == 'n' ) *gflagsp |= GNP;
+ else if( ch == 'p' ) *gflagsp |= GPR;
+ else break;
+ ++ibufp;
+ }
+ if( *ibufp++ != '\n' )
+ { set_error_msg( "Invalid command suffix" ); return 0; }
+ return 1;
+ }
+
+
+static char unexpected_address( const int addr_cnt )
+ {
+ if( addr_cnt > 0 ) { set_error_msg( "Unexpected address" ); return 1; }
+ return 0;
+ }
+
+static char unexpected_command_suffix( const unsigned char ch )
+ {
+ if( !isspace( ch ) )
+ { set_error_msg( "Unexpected command suffix" ); return 1; }
+ return 0;
+ }
+
+
+static char command_s( int *gflagsp, const int addr_cnt, const char 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)*ibufp ) )
+ {
+ if( !parse_int( &snum, ibufp, &ibufp ) ) return 0;
+ sflags |= SGF; gflags &= ~GSG; /* override GSG */
+ }
+ else switch( *ibufp )
+ {
+ case '\n':sflags |= SGF; break;
+ case 'g': sflags |= SGG; ++ibufp; break;
+ case 'p': sflags |= SGP; ++ibufp; break;
+ case 'r': sflags |= SGR; ++ibufp; break;
+ default: if( sflags )
+ { set_error_msg( "Invalid command suffix" ); return 0; }
+ }
+ }
+ while( sflags && *ibufp != '\n' );
+ if( sflags && !prev_pattern() )
+ { set_error_msg( "No previous substitution" ); return 0; }
+ if( sflags & SGG ) snum = 0; /* override numeric arg */
+ if( *ibufp != '\n' && ibufp[1] == '\n' )
+ { set_error_msg( "Invalid pattern delimiter" ); return 0; }
+ if( ( !sflags || ( sflags & SGR ) ) && !new_compiled_pattern( &ibufp ) )
+ return 0;
+ if( !sflags && !extract_subst_tail( &ibufp, &gflags, &snum, isglobal ) )
+ return 0;
+ if( isglobal ) gflags |= GLB;
+ else gflags &= ~GLB;
+ if( sflags & SGG ) gflags ^= GSG;
+ if( sflags & SGP ) { gflags ^= GPR; gflags &= ~( GLS | GNP ); }
+ switch( *ibufp )
+ {
+ case 'l': gflags |= GLS; ++ibufp; break;
+ case 'n': gflags |= GNP; ++ibufp; break;
+ case 'p': gflags |= GPR; ++ibufp; break;
+ }
+ if( !check_addr_range( -1, -1, addr_cnt ) ) return 0;
+ if( !get_command_suffix( gflagsp ) ) return 0;
+ if( !isglobal ) clear_undo_stack();
+ if( !search_and_replace( first_addr, second_addr, gflags, snum, isglobal ) )
+ return 0;
+ if( ( gflags & ( GPR | GLS | GNP ) ) &&
+ !display_lines( current_addr(), current_addr(), gflags ) )
+ return 0;
+ return 1;
+ }
+
+
+static char exec_global( const char *ibufp2, int gflags, const char interact );
+/* execute the next command in command buffer; return error status */
+static int exec_command( const char isglobal )
+ {
+ const char *fnp;
+ int gflags = 0;
+ int addr, c, n;
+
+ const int addr_cnt = extract_addr_range(); if( addr_cnt < 0 ) return ERR;
+ ibufp = skip_blanks( ibufp );
+ c = *ibufp++;
+ switch( c )
+ {
+ case 'a': if( !get_command_suffix( &gflags ) ) return ERR;
+ if( !isglobal ) clear_undo_stack();
+ if( !append_lines( ibufp, second_addr, isglobal ) ) return ERR;
+ ibufp = "";
+ break;
+ case 'c': if( first_addr == 0 ) first_addr = 1;
+ if( second_addr == 0 ) second_addr = 1;
+ if( !check_addr_range( -1, -1, addr_cnt ) ||
+ !get_command_suffix( &gflags ) ) return ERR;
+ if( !isglobal ) clear_undo_stack();
+ if( !delete_lines( first_addr, second_addr, isglobal ) ||
+ !append_lines( ibufp, current_addr(), isglobal ) ) return ERR;
+ ibufp = "";
+ break;
+ case 'd': if( !check_addr_range( -1, -1, addr_cnt ) ||
+ !get_command_suffix( &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() ) return EMOD; /* fall through */
+ case 'E': if( unexpected_address( addr_cnt ) ||
+ unexpected_command_suffix( *ibufp ) ) return ERR;
+ fnp = get_filename();
+ if( !fnp || !get_command_suffix( &gflags ) ||
+ !delete_lines( 1, last_addr(), isglobal ) ) return ERR;
+ if( !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( 0 );
+ break;
+ case 'f': if( unexpected_address( addr_cnt ) ||
+ unexpected_command_suffix( *ibufp ) ) return ERR;
+ fnp = get_filename();
+ if( !fnp ) return ERR;
+ if( fnp[0] == '!' )
+ { set_error_msg( "Invalid redirection" ); return ERR; }
+ if( !get_command_suffix( &gflags ) ) 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( &ibufp, first_addr, second_addr, n ) )
+ return ERR;
+ n = ( c == 'G' || c == 'V' ); /* interact */
+ if( ( n && !get_command_suffix( &gflags ) ) ||
+ !exec_global( ibufp, gflags, n ) )
+ return ERR;
+ break;
+ case 'h':
+ case 'H': if( unexpected_address( addr_cnt ) ||
+ !get_command_suffix( &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( &gflags ) ) return ERR;
+ if( !isglobal ) clear_undo_stack();
+ if( !append_lines( ibufp, second_addr - 1, isglobal ) ) return ERR;
+ ibufp = "";
+ break;
+ case 'j': if( !check_addr_range( -1, current_addr() + 1, addr_cnt ) ||
+ !get_command_suffix( &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 = *ibufp++;
+ if( second_addr == 0 ) { invalid_address(); return ERR; }
+ if( !get_command_suffix( &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_addr_range( -1, -1, addr_cnt ) ||
+ !get_command_suffix( &gflags ) ||
+ !display_lines( first_addr, second_addr, gflags | n ) )
+ return ERR;
+ gflags = 0;
+ break;
+ case 'm': if( !check_addr_range( -1, -1, addr_cnt ) ||
+ !get_third_addr( &addr ) ) return ERR;
+ if( addr >= first_addr && addr < second_addr )
+ { set_error_msg( "Invalid destination" ); return ERR; }
+ if( !get_command_suffix( &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( &gflags ) ) return ERR;
+ if( c == 'P' ) prompt_on = !prompt_on;
+ else return ( ( modified() && !scripted() && c == 'q' ) ? EMOD : -1 );
+ break;
+ case 'r': if( unexpected_command_suffix( *ibufp ) ) return ERR;
+ if( addr_cnt == 0 ) second_addr = last_addr();
+ fnp = get_filename();
+ if( !fnp || !get_command_suffix( &gflags ) ) 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 && addr != last_addr() ) set_modified( 1 );
+ break;
+ case 's': if( !command_s( &gflags, addr_cnt, isglobal ) ) return ERR;
+ break;
+ case 't': if( !check_addr_range( -1, -1, addr_cnt ) ||
+ !get_third_addr( &addr ) ||
+ !get_command_suffix( &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( &gflags ) ||
+ !undo( isglobal ) ) return ERR;
+ break;
+ case 'w':
+ case 'W': n = *ibufp;
+ if( n == 'q' || n == 'Q' ) ++ibufp;
+ if( unexpected_command_suffix( *ibufp ) ) return ERR;
+ fnp = get_filename();
+ if( !fnp ) return ERR;
+ if( addr_cnt == 0 && !last_addr() ) first_addr = second_addr = 0;
+ else if( !check_addr_range( 1, last_addr(), addr_cnt ) ) return ERR;
+ if( !get_command_suffix( &gflags ) ) 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( 0 );
+ else if( modified() && !scripted() && n == 'q' ) return EMOD;
+ if( n == 'q' || n == 'Q' ) return -1;
+ break;
+ case 'x': if( second_addr < 0 || last_addr() < second_addr )
+ { invalid_address(); return ERR; }
+ if( !get_command_suffix( &gflags ) ) return ERR;
+ if( !isglobal ) clear_undo_stack();
+ if( !put_lines( second_addr ) ) return ERR;
+ break;
+ case 'y': if( !check_addr_range( -1, -1, addr_cnt ) ||
+ !get_command_suffix( &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( *ibufp > '0' && *ibufp <= '9' )
+ { if( parse_int( &n, ibufp, &ibufp ) ) set_window_lines( n );
+ else return ERR; }
+ if( !get_command_suffix( &gflags ) ||
+ !display_lines( second_addr, min( last_addr(), second_addr + window_lines() ),
+ gflags ) )
+ return ERR;
+ gflags = 0;
+ break;
+ case '=': if( !get_command_suffix( &gflags ) ) return ERR;
+ printf( "%d\n", addr_cnt ? second_addr : last_addr() );
+ break;
+ case '!': if( unexpected_address( addr_cnt ) ) return ERR;
+ n = get_shell_command();
+ if( n < 0 || !get_command_suffix( &gflags ) ) return ERR;
+ if( n ) printf( "%s\n", shcmd + 1 );
+ system( shcmd + 1 );
+ 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( *ibufp++ != '\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 char exec_global( const char *ibufp2, int gflags, const char interact )
+ {
+ static char *buf = 0;
+ static int bufsz = 0;
+ const line_t *lp = 0;
+ const char *cmd = 0;
+
+ if( !interact )
+ {
+ if( traditional() && !strcmp( ibufp2, "\n" ) )
+ cmd = "p\n"; /* null cmd-list == `p' */
+ else if( !( cmd = get_extended_line( ibufp2, 0, 0 ) ) )
+ return 0;
+ }
+ clear_undo_stack();
+ while( ( lp = next_active_node() ) )
+ {
+ set_current_addr( get_line_node_addr( lp ) );
+ if( current_addr() < 0 ) return 0;
+ if( interact )
+ {
+ /* print current_addr; get a command in global syntax */
+ int len;
+ if( !display_lines( current_addr(), current_addr(), gflags ) )
+ return 0;
+ do { ibufp2 = get_tty_line( &len ); }
+ while( ibufp2 && len > 0 && ibufp2[len-1] != '\n' );
+ if( !ibufp2 ) return 0;
+ if( len == 0 ) { set_error_msg( "Unexpected end-of-file" ); return 0; }
+ if( len == 1 && !strcmp( ibufp2, "\n" ) ) continue;
+ if( len == 2 && !strcmp( ibufp2, "&\n" ) )
+ { if( !cmd ) { set_error_msg( "No previous command" ); return 0; } }
+ else if( !( cmd = get_extended_line( ibufp2, &len, 0 ) ) ) return 0;
+ else
+ {
+ if( !resize_buffer( &buf, &bufsz, len + 1 ) ) return 0;
+ memcpy( buf, cmd, len + 1 );
+ cmd = buf;
+ }
+ }
+ ibufp = cmd;
+ while( *ibufp ) if( exec_command( 1 ) < 0 ) return 0;
+ }
+ return 1;
+ }
+
+
+int main_loop( const char loose )
+ {
+ extern jmp_buf jmp_state;
+ volatile int err_status = 0; /* program exit status */
+ volatile int linenum = 0; /* script line number */
+ int len, status, old_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( 1 )
+ {
+ if( status < 0 && verbose ) fprintf( stderr, "%s\n", errmsg );
+ 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: file modified" );
+ if( is_regular_file( 0 ) )
+ {
+ if( verbose ) fprintf( stderr, "script, line %d: %s\n", linenum, errmsg );
+ return 2;
+ }
+ set_modified( 0 ); status = EMOD; continue;
+ }
+ else if( ibufp[len-1] != '\n' ) /* discard line */
+ { set_error_msg( "Unexpected end-of-file" ); status = ERR; continue; }
+ else ++linenum;
+ old_status = status;
+ status = exec_command( 0 );
+ if( status == 0 ) continue;
+ if( status == -1 ) return err_status;
+ if( status == EMOD )
+ {
+ if( old_status == EMOD ) return err_status;
+ fputs( "?\n", stderr ); /* give warning */
+ set_error_msg( "Warning: file 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;
+ }
+ }
+
+
+void set_error_msg( const char *msg )
+ {
+ if( !msg ) msg = "";
+ strncpy( errmsg, msg, sizeof( errmsg ) );
+ errmsg[sizeof(errmsg)-1] = 0;
+ }
diff --git a/packaging/ed.changes b/packaging/ed.changes
new file mode 100644
index 0000000..4c67bd7
--- /dev/null
+++ b/packaging/ed.changes
@@ -0,0 +1,165 @@
+* Mon Feb 01 2010 Yi Yang <yi.y.yang@intel.com> - 1.4
+- Update to 1.4
+
+* Fri Feb 13 2009 Anas Nashif <anas.nashif@intel.com> 1.2
+- Update to 1.2
+
+* Thur Jan 22 2009 Passion Zhao <passion.zhao@intel.com> 1.1-1
+- update to 1.1, fixes CVE-2008-3916
+
+* Fri Sep 12 2008 vivian zhang <vivian.zhang@intel.com> 0.8
+- add check for the info file before installing it in post/preun
+- add %doc to man/info in spec file
+
+* Tue Feb 19 2008 Fedora Release Engineering <rel-eng@fedoraproject.org> - 0.8-2
+- Autorebuild for GCC 4.3
+
+* Wed Aug 22 2007 Karsten Hopp <karsten@redhat.com> 0.8-1
+- update to 0.8
+
+* Wed Aug 22 2007 Karsten Hopp <karsten@redhat.com> 0.7-2
+- update license tags
+
+* Mon Jul 23 2007 Karsten Hopp <karsten@redhat.com> 0.7-1
+- update to 0.7 to fix an endless loop (#234689)
+- add disttag
+
+* Mon Jul 02 2007 Karsten Hopp <karsten@redhat.com> 0.6-1
+- update to 0.6
+
+* Wed Mar 14 2007 Karsten Hopp <karsten@redhat.com> 0.5-1
+- version 0.5, fixes #228329
+
+* Mon Feb 05 2007 Karsten Hopp <karsten@redhat.com> 0.4-3
+- clean up spec file for merge review (#225717)
+
+* Wed Jan 31 2007 Karsten Hopp <karsten@redhat.com> 0.4-2
+- use RPM_OPT_FLAGS, this fixes debuginfo
+
+* Tue Jan 23 2007 Karsten Hopp <karsten@redhat.com> 0.4-1
+- new upstream version
+
+* Thu Jan 18 2007 Karsten Hopp <karsten@redhat.com> 0.3-3
+- don't abort (un)install scriptlets when _excludedocs is set (Ville Skyttä)
+
+* Thu Jan 18 2007 Karsten Hopp <karsten@redhat.com> 0.3-2
+- fix man page permissions (#222581)
+
+* Mon Nov 13 2006 Karsten Hopp <karsten@redhat.com> 0.3-1
+- update to ed-0.3
+
+* Wed Jul 12 2006 Jesse Keating <jkeating@redhat.com> - 0.2-38.2.2
+- rebuild
+
+* Fri Feb 10 2006 Jesse Keating <jkeating@redhat.com> - 0.2-38.2.1
+- bump again for double-long bug on ppc(64)
+
+* Tue Feb 07 2006 Jesse Keating <jkeating@redhat.com> - 0.2-38.2
+- rebuilt for new gcc4.1 snapshot and glibc changes
+
+* Fri Dec 09 2005 Jesse Keating <jkeating@redhat.com>
+- rebuilt
+
+* Wed Mar 02 2005 Karsten Hopp <karsten@redhat.de> 0.2-38
+- build with gcc-4
+
+* Mon Jan 03 2005 Karsten Hopp <karsten@redhat.de> 0.2-37
+- spec file fix from Marcin Garski (#143723)
+
+* Tue Jun 15 2004 Elliot Lee <sopwith@redhat.com>
+- rebuilt
+
+* Fri Feb 13 2004 Elliot Lee <sopwith@redhat.com>
+- rebuilt
+
+* Tue Jun 17 2003 Karsten Hopp <karsten@redhat.de> 0.2-34
+- rebuild
+
+* Tue Jun 17 2003 Karsten Hopp <karsten@redhat.de> 0.2-33
+- rebuild to fix crt*.o problems
+
+* Wed Jun 04 2003 Elliot Lee <sopwith@redhat.com>
+- rebuilt
+
+* Wed Jan 22 2003 Tim Powers <timp@redhat.com>
+- rebuilt
+
+* Tue Dec 17 2002 Karsten Hopp <karsten@redhat.de>Ä
+- remove regex, use glibc's regex (#79132)
+
+* Thu Dec 12 2002 Tim Powers <timp@redhat.com> 0.2-29
+- rebuild on all arches
+
+* Fri Jun 21 2002 Tim Powers <timp@redhat.com>
+- automated rebuild
+
+* Thu May 23 2002 Tim Powers <timp@redhat.com>
+- automated rebuild
+
+* Mon May 6 2002 Bernhard Rosenkraenzer <bero@redhat.com> 0.2-26
+- Fix build with current toolchain
+
+* Wed Apr 03 2002 Karsten Hopp <karsten@redhat.de>
+- don't use gcc -s
+
+* Fri Feb 22 2002 Karsten Hopp <karsten@redhat.de>
+- bump version
+
+* Wed Jan 09 2002 Tim Powers <timp@redhat.com>
+- automated rebuild
+
+* Mon Oct 15 2001 Karsten Hopp <karsten@redhat.de>
+- add home page (#54602)
+
+* Sat Jul 07 2001 Karsten Hopp <karsten@redhat.de>
+- Copyright -> License
+- fix URL
+
+* Sun Jun 24 2001 Elliot Lee <sopwith@redhat.com>
+- Bump release + rebuild.
+
+* Mon Dec 04 2000 Karsten Hopp <karsten@redhat.de>
+- back out fixes for compiler warnings
+
+* Wed Nov 29 2000 Karsten Hopp <karsten@redhat.de>
+- Security bugfix (mkstemp instead of mktemp) Bugzilla #21470
+
+* Wed Jul 12 2000 Prospector <bugzilla@redhat.com>
+- automatic rebuild
+
+* Sun Jun 18 2000 Than Ngo <than@redhat.de>
+- fix typo
+
+* Sat Jun 17 2000 Than Ngo <than@redhat.de>
+- add %%defattr
+- clean up specfile
+
+* Sat May 20 2000 Ngo Than <than@redhat.de>
+- rebuild for 7.0
+- put man pages and infos in right place
+
+* Thu Feb 03 2000 Preston Brown <pbrown@redhat.com>
+- rebuild to gzip man pages.
+
+* Tue Mar 23 1999 Jeff Johnson <jbj@redhat.com>
+- fix %%post syntax error (#1689).
+
+* Sun Mar 21 1999 Cristian Gafton <gafton@redhat.com>
+- auto rebuild in the new build environment (release 11)
+
+* Wed Feb 24 1999 Preston Brown <pbrown@redhat.com>
+- Injected new description and group.
+
+* Fri Dec 18 1998 Preston Brown <pbrown@redhat.com>
+- bumped spec number for initial rh 6.0 build
+
+* Mon Apr 27 1998 Prospector System <bugs@redhat.com>
+- translations modified for de, fr, tr
+
+* Fri Oct 17 1997 Donnie Barnes <djb@redhat.com>
+- added install-info support
+- added BuildRoot
+- correct URL in Source line
+
+* Mon Jun 02 1997 Erik Troan <ewt@redhat.com>
+- built against glibc
diff --git a/packaging/ed.spec b/packaging/ed.spec
new file mode 100644
index 0000000..5094ecf
--- /dev/null
+++ b/packaging/ed.spec
@@ -0,0 +1,59 @@
+#specfile originally created for Fedora, modified for Moblin Linux
+Summary: The GNU line editor
+Name: ed
+Version: 1.4
+Release: 1
+License: GPLv3+
+Group: Applications/Text
+Source: ftp://ftp.gnu.org/gnu/ed/%{name}-%{version}.tar.gz
+URL: http://www.gnu.org/software/ed/
+Requires(post): /sbin/install-info
+Requires(preun): /sbin/install-info
+Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+
+%description
+Ed is a line-oriented text editor, used to create, display, and modify
+text files (both interactively and via shell scripts). For most
+purposes, ed has been replaced in normal usage by full-screen editors
+(emacs and vi, for example).
+
+Ed was the original UNIX editor, and may be used by some programs. In
+general, however, you probably don't need to install it and you probably
+won't use it.
+
+%prep
+%setup -q
+rm -f stamp-h.in
+
+%build
+%configure --exec-prefix=/
+make %{?_smp_mflags} CFLAGS="$RPM_OPT_FLAGS"
+
+%install
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT%{_mandir}/man1
+make install DESTDIR=$RPM_BUILD_ROOT \
+ bindir=/bin mandir=%{_mandir}/man1
+
+rm -f $RPM_BUILD_ROOT%{_infodir}/dir*
+gzip -9qnf $RPM_BUILD_ROOT%{_infodir}/*
+install -p -m0644 doc/ed.1 $RPM_BUILD_ROOT%{_mandir}/man1
+
+%post
+[ -e %{_infodir}/ed.info.gz ] && /sbin/install-info %{_infodir}/ed.info.gz %{_infodir}/dir --entry="* ed: (ed). The GNU Line Editor." || :
+
+%preun
+if [ $1 = 0 ] ; then
+ [ -e %{_infodir}/ed.info.gz ] && /sbin/install-info --delete %{_infodir}/ed.info.gz %{_infodir}/dir --entry="* ed: (ed). The GNU Line Editor." || :
+fi
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(-,root,root)
+%doc ChangeLog NEWS README TODO AUTHORS COPYING
+/bin/*
+%doc %{_infodir}/ed.info.gz
+%doc %{_mandir}/*/*
+
diff --git a/regex.c b/regex.c
new file mode 100644
index 0000000..7d6f272
--- /dev/null
+++ b/regex.c
@@ -0,0 +1,392 @@
+/* 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 Antonio Diaz Diaz.
+
+ 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 char patlock = 0; /* 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 */
+
+
+char prev_pattern( void ) { return global_pat != 0; }
+
+
+/* translate characters in a string */
+static void translit_text( char *s, int len, char from, char to )
+ {
+ char *p = s;
+
+ while( --len > 0 )
+ {
+ if( *p == from ) *p = to;
+ ++p;
+ }
+ }
+
+
+/* overwrite newlines with ASCII NULs */
+static void newline_to_nul( char *s, int len )
+ { translit_text( s, len, '\n', '\0' ); }
+
+/* overwrite ASCII NULs with newlines */
+static void nul_to_newline( char *s, int len )
+ { translit_text( s, len, '\0', '\n' ); }
+
+
+/* expand a POSIX character class */
+static const char *parse_char_class( const char *s )
+ {
+ char c, d;
+
+ if( *s == '^' ) ++s;
+ if( *s == ']' ) ++s;
+ for( ; *s != ']' && *s != '\n'; ++s )
+ if( *s == '[' && ( ( d = s[1] ) == '.' || d == ':' || d == '=' ) )
+ for( ++s, c = *++s; *s != ']' || c != d; ++s )
+ if( ( c = *s ) == '\n' )
+ return 0;
+ return ( ( *s == ']' ) ? s : 0 );
+ }
+
+
+/* copy a pattern string from the command buffer; return pointer to the copy */
+static char *extract_pattern( const char **ibufpp, const int 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 **ibufpp )
+ {
+ static regex_t *exp = 0;
+ 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;
+ }
+ if( !( exps = extract_pattern( ibufpp, delimiter ) ) ) return 0;
+ /* buffer alloc'd && not reserved */
+ if( exp && !patlock ) regfree( exp );
+ else if( !( exp = ( regex_t *) malloc( sizeof( regex_t ) ) ) )
+ {
+ show_strerror( 0, errno );
+ set_error_msg( "Memory exhausted" );
+ return 0;
+ }
+ patlock = 0;
+ 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 */
+char build_active_list( const char **ibufpp, const int first_addr,
+ const int second_addr, const char match )
+ {
+ regex_t *pat;
+ line_t *lp;
+ int addr;
+ const char delimiter = **ibufpp;
+
+ if( delimiter == ' ' || delimiter == '\n' )
+ { set_error_msg( "Invalid pattern delimiter" ); return 0; }
+ if( !( pat = get_compiled_pattern( ibufpp ) ) ) return 0;
+ 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 *s = get_sbuf_line( lp );
+ if( !s ) return 0;
+ if( isbinary() ) nul_to_newline( s, lp->len );
+ if( !regexec( pat, s, 0, 0, 0 ) == match && !set_active_node( lp ) )
+ return 0;
+ }
+ return 1;
+ }
+
+
+/* return pointer to copy of substitution template in the command buffer */
+static char *extract_subst_template( const char **ibufpp, const char 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 */
+char extract_subst_tail( const char **ibufpp, int *gflagsp, int *snump,
+ const char isglobal )
+ {
+ const char delimiter = **ibufpp;
+
+ *gflagsp = *snump = 0;
+ if( delimiter == '\n' ) { stlen = 0; *gflagsp = GPR; return 1; }
+ if( !extract_subst_template( ibufpp, isglobal ) ) return 0;
+ if( **ibufpp == '\n' ) { *gflagsp = GPR; return 1; }
+ if( **ibufpp == delimiter ) ++(*ibufpp);
+ if( **ibufpp >= '1' && **ibufpp <= '9' ) return parse_int( snump, *ibufpp, ibufpp );
+ if( **ibufpp == 'g' ) { ++(*ibufpp); *gflagsp = GSG; return 1; }
+ return 1;
+ }
+
+
+/* return the address of the next line matching a pattern in a given
+ direction. wrap around begin/end of editor buffer if necessary */
+int get_matching_node_addr( const char **ibufpp, const char forward )
+ {
+ regex_t *pat = get_compiled_pattern( ibufpp );
+ int addr = current_addr();
+
+ if( !pat ) return -1;
+ do {
+ addr = ( forward ? inc_addr( addr ) : dec_addr( addr ) );
+ if( addr )
+ {
+ line_t *lp = search_line_node( addr );
+ char *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;
+ }
+
+
+char new_compiled_pattern( const char **ibufpp )
+ {
+ regex_t *tpat = global_pat;
+
+ disable_interrupts();
+ tpat = get_compiled_pattern( ibufpp );
+ if( !tpat ) { enable_interrupts(); return 0; }
+ if( tpat != global_pat )
+ {
+ if( global_pat ) { regfree( global_pat ); free( global_pat ); }
+ global_pat = tpat;
+ patlock = 1; /* reserve pattern */
+ }
+ enable_interrupts();
+ return 1;
+ }
+
+
+/* modify text according to a substitution template; return offset to
+ end of modified text */
+static int apply_subst_template( const char *boln, const regmatch_t *rm, int off,
+ 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, off + k - j ) ) return -1;
+ while( j < k ) rbuf[off++] = 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, off + k - j ) ) return -1;
+ while( j < k ) rbuf[off++] = boln[j++];
+ }
+ else
+ {
+ if( !resize_buffer( &rbuf, &rbufsz, off + 1 ) ) return -1;
+ rbuf[off++] = *sub;
+ }
+ }
+ if( !resize_buffer( &rbuf, &rbufsz, off + 1 ) ) return -1;
+ rbuf[off] = 0;
+ return off;
+ }
+
+
+/* replace text matched by a pattern according to a substitution
+ template; return pointer to the modified text */
+static int replace_matching_text( const line_t *lp, const int gflags,
+ const int snum )
+ {
+ const int se_max = 30; /* max subexpressions in a regular expression */
+ regmatch_t rm[se_max];
+ char *txt = get_sbuf_line( lp );
+ char *eot;
+ int i = 0, off = 0;
+ char changed = 0;
+
+ 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 = 1; i = rm[0].rm_so;
+ if( !resize_buffer( &rbuf, &rbufsz, off + i ) ) return -1;
+ if( isbinary() ) newline_to_nul( txt, rm[0].rm_eo );
+ memcpy( rbuf + off, txt, i ); off += i;
+ off = apply_subst_template( txt, rm, off, global_pat->re_nsub );
+ if( off < 0 ) return -1;
+ }
+ else
+ {
+ i = rm[0].rm_eo;
+ if( !resize_buffer( &rbuf, &rbufsz, off + i ) ) return -1;
+ if( isbinary() ) newline_to_nul( txt, i );
+ memcpy( rbuf + off, txt, i ); off += 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, off + 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 + off, txt, i );
+ memcpy( rbuf + off + i, "\n", 2 );
+ }
+ return ( changed ? off + i + 1 : 0 );
+ }
+
+
+/* for each line in a range, change text matching a pattern according to
+ a substitution template; return false if error */
+char search_and_replace( const int first_addr, const int second_addr,
+ const int gflags, const int snum, const char isglobal )
+ {
+ int lc;
+ char match_found = 0;
+
+ set_current_addr( first_addr - 1 );
+ for( lc = 0; lc <= second_addr - first_addr; ++lc )
+ {
+ line_t *lp = search_line_node( inc_current_addr() );
+ int len = replace_matching_text( lp, gflags, snum );
+ if( len < 0 ) return 0;
+ if( len )
+ {
+ const char *txt = rbuf;
+ const char *eot = rbuf + len;
+ undo_t *up = 0;
+ disable_interrupts();
+ if( !delete_lines( current_addr(), current_addr(), isglobal ) ) return 0;
+ do {
+ txt = put_sbuf_line( txt, current_addr() );
+ if( !txt ) { enable_interrupts(); return 0; }
+ if( up ) up->tail = search_line_node( current_addr() );
+ else if( !( up = push_undo_atom( UADD, -1, -1 ) ) )
+ { enable_interrupts(); return 0; }
+ }
+ while( txt != eot );
+ enable_interrupts();
+ match_found = 1;
+ }
+ }
+ if( !match_found && !( gflags & GLB ) )
+ { set_error_msg( "No match" ); return 0; }
+ return 1;
+ }
diff --git a/signal.c b/signal.c
new file mode 100644
index 0000000..c047124
--- /dev/null
+++ b/signal.c
@@ -0,0 +1,259 @@
+/* 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 Antonio Diaz Diaz.
+
+ 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 <setjmp.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include "ed.h"
+
+
+jmp_buf jmp_state;
+static int mutex = 0; /* If set, signals stay pending */
+static int window_lines_ = 22; /* scroll length: ws_row - 2 */
+static int window_columns_ = 72;
+static char sighup_pending = 0;
+static char sigint_pending = 0;
+
+
+static void sighup_handler( int signum )
+ {
+ signum = 0; /* keep compiler happy */
+ if( mutex ) sighup_pending = 1;
+ else
+ {
+ char hb[] = "ed.hup";
+ sighup_pending = 0;
+ if( last_addr() && modified() &&
+ write_file( hb, "w", 1, last_addr() ) < 0 )
+ {
+ char *s = getenv( "HOME" );
+ int n = ( s ? strlen( s ) : 0 );
+ int m = ( ( !n || *( s + n - 1 ) != '/' ) ? 1 : 0 );
+ char *hup = ( ( n + m + (int)sizeof( hb ) < path_max( 0 ) ) ?
+ ( char *) malloc( n + m + sizeof( hb ) ) : 0 );
+ if( n && hup ) /* hup filename */
+ {
+ memcpy( hup, s, n );
+ if( m ) memcpy( hup + n, "/", 1 );
+ memcpy( hup + n + m, 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 = 1;
+ else
+ {
+ sigset_t set;
+ sigint_pending = 0;
+ 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 */
+char parse_int( int *i, const char *str, const char **tail )
+ {
+ char *tmp;
+ errno = 0;
+ *i = strtol( str, &tmp, 10 );
+ if( tail ) *tail = tmp;
+ if( tmp == str )
+ {
+ set_error_msg( "Bad numerical result" );
+ *i = 0;
+ return 0;
+ }
+ if( errno == ERANGE )
+ {
+ set_error_msg( "Numerical result out of range" );
+ *i = 0;
+ return 0;
+ }
+ return 1;
+ }
+
+
+/* assure at least a minimum size for buffer `buf' */
+char resize_buffer( char **buf, int *size, 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 0;
+ }
+ *size = new_size;
+ *buf = (char *)new_buf;
+ enable_interrupts();
+ }
+ return 1;
+ }
+
+
+/* assure at least a minimum size for buffer `buf' */
+char resize_line_buffer( const line_t ***buf, int *size, 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 0;
+ }
+ *size = new_size;
+ *buf = (const line_t **)new_buf;
+ enable_interrupts();
+ }
+ return 1;
+ }
+
+
+/* assure at least a minimum size for buffer `buf' */
+char resize_undo_buffer( undo_t **buf, int *size, 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 0;
+ }
+ *size = new_size;
+ *buf = (undo_t *)new_buf;
+ enable_interrupts();
+ }
+ return 1;
+ }
+
+
+/* return unescaped copy of escaped string */
+const char *strip_escapes( const char *s )
+ {
+ static char *buf = 0;
+ static int bufsz = 0;
+ const int len = strlen( s );
+
+ int i = 0;
+
+ if( !resize_buffer( &buf, &bufsz, len + 1 ) ) return 0;
+ /* assert: no trailing escape */
+ while( ( buf[i++] = ( (*s == '\\' ) ? *++s : *s ) ) )
+ s++;
+ return buf;
+ }
diff --git a/testsuite/=.err.posix b/testsuite/=.err.posix
new file mode 100644
index 0000000..6a60559
--- /dev/null
+++ b/testsuite/=.err.posix
@@ -0,0 +1 @@
+1,$=
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.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/a1.err.posix b/testsuite/a1.err.posix
new file mode 100644
index 0000000..e80815f
--- /dev/null
+++ b/testsuite/a1.err.posix
@@ -0,0 +1,3 @@
+1,$a
+hello world
+.
diff --git a/testsuite/a2.err b/testsuite/a2.err
new file mode 100644
index 0000000..ec4b00b
--- /dev/null
+++ b/testsuite/a2.err
@@ -0,0 +1,3 @@
+aa
+hello world
+.
diff --git a/testsuite/a2.pr b/testsuite/a2.pr
new file mode 100644
index 0000000..ec4b00b
--- /dev/null
+++ b/testsuite/a2.pr
@@ -0,0 +1,3 @@
+aa
+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/bang1.d b/testsuite/bang1.d
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/bang1.d
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/bang1.r b/testsuite/bang1.r
new file mode 100644
index 0000000..dcf02b2
--- /dev/null
+++ b/testsuite/bang1.r
@@ -0,0 +1 @@
+okay
diff --git a/testsuite/bang1.t b/testsuite/bang1.t
new file mode 100644
index 0000000..d7b1fea
--- /dev/null
+++ b/testsuite/bang1.t
@@ -0,0 +1,5 @@
+!read one
+hello, world
+a
+okay
+.
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.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/c1.err b/testsuite/c1.err
new file mode 100644
index 0000000..658ec38
--- /dev/null
+++ b/testsuite/c1.err
@@ -0,0 +1,3 @@
+cc
+hello world
+.
diff --git a/testsuite/c1.pr b/testsuite/c1.pr
new file mode 100644
index 0000000..658ec38
--- /dev/null
+++ b/testsuite/c1.pr
@@ -0,0 +1,3 @@
+cc
+hello world
+.
diff --git a/testsuite/check.sh b/testsuite/check.sh
new file mode 100755
index 0000000..85b606b
--- /dev/null
+++ b/testsuite/check.sh
@@ -0,0 +1,104 @@
+#! /bin/sh
+# check script for GNU ed - The GNU line editor
+# Copyright (C) 2006, 2007, 2008, 2009 Antonio Diaz Diaz.
+#
+# This script is free software; you have unlimited permission
+# to copy, distribute and modify it.
+
+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 -r tmp ; fi
+mkdir tmp
+
+# Generate ed test scripts, with extensions .ed and .red, from
+# .t and .err files, respectively.
+echo "building test scripts for ed..."
+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 files, which contain
+# the correct output.
+echo "testing ed..."
+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."
+ if cd "${objdir}" ; then rm -r tmp ; fi
+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..298e1f4
--- /dev/null
+++ b/testsuite/e1.r
@@ -0,0 +1,4 @@
+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..a45f9d2
--- /dev/null
+++ b/testsuite/e1.t
@@ -0,0 +1,2 @@
+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..578a44b
--- /dev/null
+++ b/testsuite/g1.r
@@ -0,0 +1,15 @@
+line5
+help! world
+order
+line 4
+help! world
+order
+line 3
+help! world
+order
+line 2
+help! world
+order
+line 1
+help! world
+order
diff --git a/testsuite/g1.t b/testsuite/g1.t
new file mode 100644
index 0000000..2d0b54f
--- /dev/null
+++ b/testsuite/g1.t
@@ -0,0 +1,6 @@
+g/./m0
+g/./s/$/\
+hello world
+g/hello /s/lo/p!/\
+a\
+order
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.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/i1.err.posix b/testsuite/i1.err.posix
new file mode 100644
index 0000000..aaddede
--- /dev/null
+++ b/testsuite/i1.err.posix
@@ -0,0 +1,3 @@
+1,$i
+hello world
+.
diff --git a/testsuite/i2.err b/testsuite/i2.err
new file mode 100644
index 0000000..b63f5ac
--- /dev/null
+++ b/testsuite/i2.err
@@ -0,0 +1,3 @@
+ii
+hello world
+.
diff --git a/testsuite/i2.pr b/testsuite/i2.pr
new file mode 100644
index 0000000..b63f5ac
--- /dev/null
+++ b/testsuite/i2.pr
@@ -0,0 +1,3 @@
+ii
+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/k1.err.posix b/testsuite/k1.err.posix
new file mode 100644
index 0000000..eba1f3d
--- /dev/null
+++ b/testsuite/k1.err.posix
@@ -0,0 +1 @@
+1,$ka
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/l.d b/testsuite/l.d
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/l.d
diff --git a/testsuite/l.r b/testsuite/l.r
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/l.r
diff --git a/testsuite/l.t b/testsuite/l.t
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/l.t
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/n.d b/testsuite/n.d
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/n.d
diff --git a/testsuite/n.r b/testsuite/n.r
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/n.r
diff --git a/testsuite/n.t b/testsuite/n.t
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/n.t
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/p.d b/testsuite/p.d
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/p.d
diff --git a/testsuite/p.r b/testsuite/p.r
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/p.r
diff --git a/testsuite/p.t b/testsuite/p.t
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testsuite/p.t
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.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/q1.err b/testsuite/q1.err
new file mode 100644
index 0000000..0a7e178
--- /dev/null
+++ b/testsuite/q1.err
@@ -0,0 +1 @@
+.q
diff --git a/testsuite/q1.pr b/testsuite/q1.pr
new file mode 100644
index 0000000..0a7e178
--- /dev/null
+++ b/testsuite/q1.pr
@@ -0,0 +1 @@
+.q
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.err.posix b/testsuite/r1.err.posix
new file mode 100644
index 0000000..269aa7c
--- /dev/null
+++ b/testsuite/r1.err.posix
@@ -0,0 +1 @@
+1,$r r1.err
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.err b/testsuite/r2.err
new file mode 100644
index 0000000..1c44fa3
--- /dev/null
+++ b/testsuite/r2.err
@@ -0,0 +1 @@
+r a-good-book
diff --git a/testsuite/r2.pr b/testsuite/r2.pr
new file mode 100644
index 0000000..1c44fa3
--- /dev/null
+++ b/testsuite/r2.pr
@@ -0,0 +1 @@
+r a-good-book
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..2b28547
--- /dev/null
+++ b/testsuite/t.r
@@ -0,0 +1,16 @@
+line 1
+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/t1.d b/testsuite/t1.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/testsuite/t1.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
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/t1.r b/testsuite/t1.r
new file mode 100644
index 0000000..2b28547
--- /dev/null
+++ b/testsuite/t1.r
@@ -0,0 +1,16 @@
+line 1
+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/t1.t b/testsuite/t1.t
new file mode 100644
index 0000000..6b66163
--- /dev/null
+++ b/testsuite/t1.t
@@ -0,0 +1,3 @@
+1t0
+2,3t2
+,t$
diff --git a/testsuite/t2.d b/testsuite/t2.d
new file mode 100644
index 0000000..92f337e
--- /dev/null
+++ b/testsuite/t2.d
@@ -0,0 +1,5 @@
+line 1
+line 2
+line 3
+line 4
+line5
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/t2.r b/testsuite/t2.r
new file mode 100644
index 0000000..0c75ff5
--- /dev/null
+++ b/testsuite/t2.r
@@ -0,0 +1,6 @@
+line 1
+line5
+line 2
+line 3
+line 4
+line5
diff --git a/testsuite/t2.t b/testsuite/t2.t
new file mode 100644
index 0000000..5175abd
--- /dev/null
+++ b/testsuite/t2.t
@@ -0,0 +1 @@
+t0;/./
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