Imported Upstream version 1.6 upstream/1.6
authorAnas Nashif <anas.nashif@intel.com>
Wed, 7 Nov 2012 06:56:34 +0000 (22:56 -0800)
committerAnas Nashif <anas.nashif@intel.com>
Wed, 7 Nov 2012 06:56:34 +0000 (22:56 -0800)
211 files changed:
AUTHORS [new file with mode: 0644]
COPYING [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile.in [new file with mode: 0644]
NEWS [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
buffer.c [new file with mode: 0644]
carg_parser.c [new file with mode: 0644]
carg_parser.h [new file with mode: 0644]
configure [new file with mode: 0755]
doc/ed.1 [new file with mode: 0644]
doc/ed.info [new file with mode: 0644]
doc/ed.texinfo [new file with mode: 0644]
doc/fdl.texinfo [new file with mode: 0644]
ed.h [new file with mode: 0644]
global.c [new file with mode: 0644]
io.c [new file with mode: 0644]
main.c [new file with mode: 0644]
main_loop.c [new file with mode: 0644]
red.in [new file with mode: 0644]
regex.c [new file with mode: 0644]
signal.c [new file with mode: 0644]
testsuite/a.d [new file with mode: 0644]
testsuite/a.err [new file with mode: 0644]
testsuite/a.pr [new file with mode: 0644]
testsuite/a.r [new file with mode: 0644]
testsuite/a.t [new file with mode: 0644]
testsuite/addr.d [new file with mode: 0644]
testsuite/addr.r [new file with mode: 0644]
testsuite/addr.t [new file with mode: 0644]
testsuite/addr1.err [new file with mode: 0644]
testsuite/addr1.pr [new file with mode: 0644]
testsuite/addr2.err [new file with mode: 0644]
testsuite/addr2.pr [new file with mode: 0644]
testsuite/ascii.d [new file with mode: 0644]
testsuite/ascii.r [new file with mode: 0644]
testsuite/ascii.t [new file with mode: 0644]
testsuite/bang.d [new file with mode: 0644]
testsuite/bang.r [new file with mode: 0644]
testsuite/bang.t [new file with mode: 0644]
testsuite/bang1.err [new file with mode: 0644]
testsuite/bang1.pr [new file with mode: 0644]
testsuite/bang2.err [new file with mode: 0644]
testsuite/bang2.pr [new file with mode: 0644]
testsuite/c.d [new file with mode: 0644]
testsuite/c.err [new file with mode: 0644]
testsuite/c.pr [new file with mode: 0644]
testsuite/c.r [new file with mode: 0644]
testsuite/c.t [new file with mode: 0644]
testsuite/check.sh [new file with mode: 0755]
testsuite/comment.d [new file with mode: 0644]
testsuite/comment.r [new file with mode: 0644]
testsuite/comment.t [new file with mode: 0644]
testsuite/d.d [new file with mode: 0644]
testsuite/d.err [new file with mode: 0644]
testsuite/d.pr [new file with mode: 0644]
testsuite/d.r [new file with mode: 0644]
testsuite/d.t [new file with mode: 0644]
testsuite/e1.d [new file with mode: 0644]
testsuite/e1.err [new file with mode: 0644]
testsuite/e1.pr [new file with mode: 0644]
testsuite/e1.r [new file with mode: 0644]
testsuite/e1.t [new file with mode: 0644]
testsuite/e2.d [new file with mode: 0644]
testsuite/e2.err [new file with mode: 0644]
testsuite/e2.pr [new file with mode: 0644]
testsuite/e2.r [new file with mode: 0644]
testsuite/e2.t [new file with mode: 0644]
testsuite/e3.d [new file with mode: 0644]
testsuite/e3.err [new file with mode: 0644]
testsuite/e3.pr [new file with mode: 0644]
testsuite/e3.r [new file with mode: 0644]
testsuite/e3.t [new file with mode: 0644]
testsuite/e4.d [new file with mode: 0644]
testsuite/e4.r [new file with mode: 0644]
testsuite/e4.t [new file with mode: 0644]
testsuite/f1.err [new file with mode: 0644]
testsuite/f1.pr [new file with mode: 0644]
testsuite/f2.err [new file with mode: 0644]
testsuite/f2.pr [new file with mode: 0644]
testsuite/g1.d [new file with mode: 0644]
testsuite/g1.err [new file with mode: 0644]
testsuite/g1.pr [new file with mode: 0644]
testsuite/g1.r [new file with mode: 0644]
testsuite/g1.t [new file with mode: 0644]
testsuite/g2.d [new file with mode: 0644]
testsuite/g2.err [new file with mode: 0644]
testsuite/g2.pr [new file with mode: 0644]
testsuite/g2.r [new file with mode: 0644]
testsuite/g2.t [new file with mode: 0644]
testsuite/g3.d [new file with mode: 0644]
testsuite/g3.err [new file with mode: 0644]
testsuite/g3.pr [new file with mode: 0644]
testsuite/g3.r [new file with mode: 0644]
testsuite/g3.t [new file with mode: 0644]
testsuite/g4.d [new file with mode: 0644]
testsuite/g4.r [new file with mode: 0644]
testsuite/g4.t [new file with mode: 0644]
testsuite/g5.d [new file with mode: 0644]
testsuite/g5.r [new file with mode: 0644]
testsuite/g5.t [new file with mode: 0644]
testsuite/h.err [new file with mode: 0644]
testsuite/h.pr [new file with mode: 0644]
testsuite/i.d [new file with mode: 0644]
testsuite/i.err [new file with mode: 0644]
testsuite/i.pr [new file with mode: 0644]
testsuite/i.r [new file with mode: 0644]
testsuite/i.t [new file with mode: 0644]
testsuite/j.d [new file with mode: 0644]
testsuite/j.r [new file with mode: 0644]
testsuite/j.t [new file with mode: 0644]
testsuite/k.d [new file with mode: 0644]
testsuite/k.r [new file with mode: 0644]
testsuite/k.t [new file with mode: 0644]
testsuite/k2.err [new file with mode: 0644]
testsuite/k2.pr [new file with mode: 0644]
testsuite/k3.err [new file with mode: 0644]
testsuite/k3.pr [new file with mode: 0644]
testsuite/k4.err [new file with mode: 0644]
testsuite/k4.pr [new file with mode: 0644]
testsuite/m.d [new file with mode: 0644]
testsuite/m.err [new file with mode: 0644]
testsuite/m.pr [new file with mode: 0644]
testsuite/m.r [new file with mode: 0644]
testsuite/m.t [new file with mode: 0644]
testsuite/nl.err [new file with mode: 0644]
testsuite/nl.pr [new file with mode: 0644]
testsuite/nl1.d [new file with mode: 0644]
testsuite/nl1.r [new file with mode: 0644]
testsuite/nl1.t [new file with mode: 0644]
testsuite/nl2.d [new file with mode: 0644]
testsuite/nl2.r [new file with mode: 0644]
testsuite/nl2.t [new file with mode: 0644]
testsuite/q.d [new file with mode: 0644]
testsuite/q.err [new file with mode: 0644]
testsuite/q.pr [new file with mode: 0644]
testsuite/q.r [new file with mode: 0644]
testsuite/q.t [new file with mode: 0644]
testsuite/r.err [new file with mode: 0644]
testsuite/r.pr [new file with mode: 0644]
testsuite/r1.d [new file with mode: 0644]
testsuite/r1.r [new file with mode: 0644]
testsuite/r1.t [new file with mode: 0644]
testsuite/r2.d [new file with mode: 0644]
testsuite/r2.r [new file with mode: 0644]
testsuite/r2.t [new file with mode: 0644]
testsuite/r3.d [new file with mode: 0644]
testsuite/r3.r [new file with mode: 0644]
testsuite/r3.t [new file with mode: 0644]
testsuite/s1.d [new file with mode: 0644]
testsuite/s1.err [new file with mode: 0644]
testsuite/s1.pr [new file with mode: 0644]
testsuite/s1.r [new file with mode: 0644]
testsuite/s1.t [new file with mode: 0644]
testsuite/s10.err [new file with mode: 0644]
testsuite/s10.pr [new file with mode: 0644]
testsuite/s2.d [new file with mode: 0644]
testsuite/s2.err [new file with mode: 0644]
testsuite/s2.pr [new file with mode: 0644]
testsuite/s2.r [new file with mode: 0644]
testsuite/s2.t [new file with mode: 0644]
testsuite/s3.d [new file with mode: 0644]
testsuite/s3.err [new file with mode: 0644]
testsuite/s3.pr [new file with mode: 0644]
testsuite/s3.r [new file with mode: 0644]
testsuite/s3.t [new file with mode: 0644]
testsuite/s4.err [new file with mode: 0644]
testsuite/s4.pr [new file with mode: 0644]
testsuite/s5.err [new file with mode: 0644]
testsuite/s5.pr [new file with mode: 0644]
testsuite/s6.err [new file with mode: 0644]
testsuite/s6.pr [new file with mode: 0644]
testsuite/s7.err [new file with mode: 0644]
testsuite/s7.pr [new file with mode: 0644]
testsuite/s8.err [new file with mode: 0644]
testsuite/s8.pr [new file with mode: 0644]
testsuite/s9.err [new file with mode: 0644]
testsuite/s9.pr [new file with mode: 0644]
testsuite/t.d [new file with mode: 0644]
testsuite/t.r [new file with mode: 0644]
testsuite/t.t [new file with mode: 0644]
testsuite/t1.err [new file with mode: 0644]
testsuite/t1.pr [new file with mode: 0644]
testsuite/t2.err [new file with mode: 0644]
testsuite/t2.pr [new file with mode: 0644]
testsuite/u.d [new file with mode: 0644]
testsuite/u.err [new file with mode: 0644]
testsuite/u.pr [new file with mode: 0644]
testsuite/u.r [new file with mode: 0644]
testsuite/u.t [new file with mode: 0644]
testsuite/v.d [new file with mode: 0644]
testsuite/v.r [new file with mode: 0644]
testsuite/v.t [new file with mode: 0644]
testsuite/w.d [new file with mode: 0644]
testsuite/w.r [new file with mode: 0644]
testsuite/w.t [new file with mode: 0644]
testsuite/w1.err [new file with mode: 0644]
testsuite/w1.pr [new file with mode: 0644]
testsuite/w2.err [new file with mode: 0644]
testsuite/w2.pr [new file with mode: 0644]
testsuite/w3.err [new file with mode: 0644]
testsuite/w3.pr [new file with mode: 0644]
testsuite/x.d [new file with mode: 0644]
testsuite/x.err [new file with mode: 0644]
testsuite/x.pr [new file with mode: 0644]
testsuite/x.r [new file with mode: 0644]
testsuite/x.t [new file with mode: 0644]
testsuite/z.err [new file with mode: 0644]
testsuite/z.pr [new file with mode: 0644]

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