Imported Upstream version 1.14.2 upstream/1.14.2
authorDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 19 Oct 2021 07:13:00 +0000 (16:13 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 19 Oct 2021 07:13:00 +0000 (16:13 +0900)
262 files changed:
ChangeLog
INSTALL
Makefile.in
NEWS
README
TODO
buffer.c
carg_parser.c
carg_parser.h
configure
doc/ed.1
doc/ed.info
doc/ed.texi
ed.h
global.c
io.c
main.c
main_loop.c
regex.c
signal.c
testsuite/a.d [deleted file]
testsuite/a.ed [new file with mode: 0644]
testsuite/a.err
testsuite/a.pr [deleted file]
testsuite/a.r
testsuite/a.t [deleted file]
testsuite/addr.d [deleted file]
testsuite/addr.r [deleted file]
testsuite/addr.t [deleted file]
testsuite/addr1.err
testsuite/addr1.pr [deleted file]
testsuite/addr2.err
testsuite/addr2.pr [deleted file]
testsuite/addr3.err [new file with mode: 0644]
testsuite/addr4.err [new file with mode: 0644]
testsuite/ascii.t [deleted file]
testsuite/bang.d [deleted file]
testsuite/bang.r [deleted file]
testsuite/bang.t [deleted file]
testsuite/bang1.err
testsuite/bang1.pr [deleted file]
testsuite/bang2.err
testsuite/bang2.pr [deleted file]
testsuite/c.d [deleted file]
testsuite/c.ed [new file with mode: 0644]
testsuite/c.err
testsuite/c.pr [deleted file]
testsuite/c.r
testsuite/c.t [deleted file]
testsuite/check.sh
testsuite/comment.d [deleted file]
testsuite/comment.r [deleted file]
testsuite/comment.t [deleted file]
testsuite/d.d [deleted file]
testsuite/d.ed [new file with mode: 0644]
testsuite/d.err
testsuite/d.pr [deleted file]
testsuite/d.r
testsuite/d.t [deleted file]
testsuite/e1.d [deleted file]
testsuite/e1.ed [new file with mode: 0644]
testsuite/e1.err
testsuite/e1.pr [deleted file]
testsuite/e1.r
testsuite/e1.t [deleted file]
testsuite/e2.d [deleted file]
testsuite/e2.ed [new file with mode: 0644]
testsuite/e2.err
testsuite/e2.pr [deleted file]
testsuite/e2.r
testsuite/e2.t [deleted file]
testsuite/e3.d [deleted file]
testsuite/e3.ed [new file with mode: 0644]
testsuite/e3.err
testsuite/e3.pr [deleted file]
testsuite/e3.r
testsuite/e3.t [deleted file]
testsuite/e4.d [deleted file]
testsuite/e4.ed [new file with mode: 0644]
testsuite/e4.r
testsuite/e4.t [deleted file]
testsuite/e5.ed [new file with mode: 0644]
testsuite/e5.r [moved from testsuite/ascii.r with 100% similarity]
testsuite/f1.err
testsuite/f1.pr [deleted file]
testsuite/f2.err
testsuite/f2.pr [deleted file]
testsuite/g.ed [new file with mode: 0644]
testsuite/g.r [new file with mode: 0644]
testsuite/g1.d [deleted file]
testsuite/g1.err
testsuite/g1.pr [deleted file]
testsuite/g1.r [deleted file]
testsuite/g1.t [deleted file]
testsuite/g2.d [deleted file]
testsuite/g2.err
testsuite/g2.pr [deleted file]
testsuite/g2.r [deleted file]
testsuite/g2.t [deleted file]
testsuite/g3.d [deleted file]
testsuite/g3.err
testsuite/g3.pr [deleted file]
testsuite/g3.r [deleted file]
testsuite/g3.t [deleted file]
testsuite/g4.d [deleted file]
testsuite/g4.err [new file with mode: 0644]
testsuite/g4.r [deleted file]
testsuite/g4.t [deleted file]
testsuite/g5.d [deleted file]
testsuite/g5.r [deleted file]
testsuite/g5.t [deleted file]
testsuite/h.err
testsuite/h.pr [deleted file]
testsuite/i.d [deleted file]
testsuite/i.ed [new file with mode: 0644]
testsuite/i.err
testsuite/i.pr [deleted file]
testsuite/i.r
testsuite/i.t [deleted file]
testsuite/j.d [deleted file]
testsuite/j.ed [new file with mode: 0644]
testsuite/j.r
testsuite/j.t [deleted file]
testsuite/k.d [deleted file]
testsuite/k.ed [new file with mode: 0644]
testsuite/k.r
testsuite/k.t [deleted file]
testsuite/k1.err [moved from testsuite/k4.err with 66% similarity]
testsuite/k2.err
testsuite/k2.pr [deleted file]
testsuite/k3.err
testsuite/k3.pr [deleted file]
testsuite/k4.pr [deleted file]
testsuite/m.d [deleted file]
testsuite/m.err
testsuite/m.pr [deleted file]
testsuite/m.r [deleted file]
testsuite/m.t [deleted file]
testsuite/m_addr.ed [new file with mode: 0644]
testsuite/m_addr.r [new file with mode: 0644]
testsuite/nl.err [deleted file]
testsuite/nl.pr [deleted file]
testsuite/nl1.d [deleted file]
testsuite/nl1.r [deleted file]
testsuite/nl1.t [deleted file]
testsuite/nl2.d [deleted file]
testsuite/nl2.r [deleted file]
testsuite/nl2.t [deleted file]
testsuite/p1.err [new file with mode: 0644]
testsuite/p2.err [new file with mode: 0644]
testsuite/q.d [deleted file]
testsuite/q.ed [new file with mode: 0644]
testsuite/q.err [deleted file]
testsuite/q.pr [deleted file]
testsuite/q.r
testsuite/q1.err [new file with mode: 0644]
testsuite/q2.err [new file with mode: 0644]
testsuite/q3.err [moved from testsuite/q.t with 52% similarity]
testsuite/q4.err [new file with mode: 0644]
testsuite/q5.err [new file with mode: 0644]
testsuite/q6.err [new file with mode: 0644]
testsuite/q7.err [new file with mode: 0644]
testsuite/q8.err [new file with mode: 0644]
testsuite/r.err
testsuite/r.pr [deleted file]
testsuite/r1.d [deleted file]
testsuite/r1.ed [new file with mode: 0644]
testsuite/r1.r
testsuite/r1.t [deleted file]
testsuite/r2.d [deleted file]
testsuite/r2.ed [new file with mode: 0644]
testsuite/r2.r
testsuite/r2.t [deleted file]
testsuite/r3.d [deleted file]
testsuite/r3.ed [new file with mode: 0644]
testsuite/r3.r
testsuite/r3.t [deleted file]
testsuite/s.ed [new file with mode: 0644]
testsuite/s.r [new file with mode: 0644]
testsuite/s01.err [new file with mode: 0644]
testsuite/s02.err [new file with mode: 0644]
testsuite/s03.err [new file with mode: 0644]
testsuite/s04.err [moved from testsuite/s4.err with 56% similarity]
testsuite/s05.err [new file with mode: 0644]
testsuite/s06.err [new file with mode: 0644]
testsuite/s07.err [new file with mode: 0644]
testsuite/s08.err [moved from testsuite/s8.err with 50% similarity]
testsuite/s09.err [moved from testsuite/s9.err with 50% similarity]
testsuite/s1.d [deleted file]
testsuite/s1.err [deleted file]
testsuite/s1.pr [deleted file]
testsuite/s1.r [deleted file]
testsuite/s1.t [deleted file]
testsuite/s10.err
testsuite/s10.pr [deleted file]
testsuite/s11.err [new file with mode: 0644]
testsuite/s12.err [new file with mode: 0644]
testsuite/s13.err [new file with mode: 0644]
testsuite/s14.err [new file with mode: 0644]
testsuite/s15.err [new file with mode: 0644]
testsuite/s16.err [new file with mode: 0644]
testsuite/s17.err [new file with mode: 0644]
testsuite/s18.err [new file with mode: 0644]
testsuite/s19.err [new file with mode: 0644]
testsuite/s2.d [deleted file]
testsuite/s2.err [deleted file]
testsuite/s2.pr [deleted file]
testsuite/s2.r [deleted file]
testsuite/s2.t [deleted file]
testsuite/s20.err [new file with mode: 0644]
testsuite/s21.err [new file with mode: 0644]
testsuite/s3.d [deleted file]
testsuite/s3.err [deleted file]
testsuite/s3.pr [deleted file]
testsuite/s3.r [deleted file]
testsuite/s3.t [deleted file]
testsuite/s4.pr [deleted file]
testsuite/s5.err [deleted file]
testsuite/s5.pr [deleted file]
testsuite/s6.err [deleted file]
testsuite/s6.pr [deleted file]
testsuite/s7.err [deleted file]
testsuite/s7.pr [deleted file]
testsuite/s8.pr [deleted file]
testsuite/s9.pr [deleted file]
testsuite/t.d [deleted file]
testsuite/t.ed [new file with mode: 0644]
testsuite/t.r
testsuite/t.t [deleted file]
testsuite/t1.err
testsuite/t1.pr [deleted file]
testsuite/t2.err
testsuite/t2.pr [deleted file]
testsuite/test.bin [moved from testsuite/ascii.d with 100% similarity]
testsuite/test.txt [new file with mode: 0644]
testsuite/u.d [deleted file]
testsuite/u.err
testsuite/u.pr [deleted file]
testsuite/u.r [deleted file]
testsuite/u.t [deleted file]
testsuite/v.d [deleted file]
testsuite/v.ed [new file with mode: 0644]
testsuite/v.r
testsuite/v.t [deleted file]
testsuite/w.d [deleted file]
testsuite/w.ed [new file with mode: 0644]
testsuite/w.r
testsuite/w.t [deleted file]
testsuite/w1.err
testsuite/w1.pr [deleted file]
testsuite/w2.err
testsuite/w2.pr [deleted file]
testsuite/w3.err
testsuite/w3.pr [deleted file]
testsuite/x.d [deleted file]
testsuite/x.ed [new file with mode: 0644]
testsuite/x.err
testsuite/x.pr [deleted file]
testsuite/x.r
testsuite/x.t [deleted file]
testsuite/z.err
testsuite/z.pr [deleted file]

index 4e63d63..68ce50d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,38 @@
+2017-02-22  Antonio Diaz Diaz  <antonio@gnu.org>
+
+       * Version 1.14.2 released.
+       * main.c (show_strerror) Revert to using '!scripted' instead of
+         'verbose' to suppress diagnostics.
+
+2017-01-10  Antonio Diaz Diaz  <antonio@gnu.org>
+
+       * Version 1.14.1 released.
+       * Print counts, messages, '?' and '!' to stdout instead of stderr.
+       * buffer.c (append_lines): Fixed current address after empty 'i'.
+       * regex.c (set_subst_regex): Treat missing delimiters consistently.
+         (extract_replacement): Don't replace 'a' with '%' in 's/a/%'.
+         Fixed infinite loop with EOF in the middle of a replacement.
+         Don't accept newlines in replacement in a global command.
+         Last delimiter can't be omitted if not last in command list.
+         (search_and_replace): Set current address to last line modified.
+       * main_loop.c (extract_addresses): Fixed address offsets;
+         '3 ---- 2' was calculated as -2 instead of 1.
+         Accept ranges with the first address omitted.
+         (exec_command): Fixed current address after empty replacement
+         text in 'c' command.
+         Don't clear the modified status after writing the buffer to a
+         shell command. (Reported by Jérôme Frgacic).
+         (get_command_suffix): Don't allow repeated print suffixes.
+         (command_s): Accept suffixes in any order.
+         Don't allow multiple count suffixes.
+         'sp' now toggles all print suffixes.
+         (main_loop): Make EOF on stdin behave as a 'q' command.
+       * ed.texi: Fixed the description of commands 'acegijkmqrsuw'.
+         Documented that ed allows any combination of print suffixes.
+       * testsuite: Improved most tests. Simplified bug reporting.
+       * configure: Avoid warning on some shells when testing for gcc.
+       * Makefile.in: Detect the existence of install-info.
+
 2016-01-24  Antonio Diaz Diaz  <antonio@gnu.org>
 
        * Version 1.13 released.
@@ -231,7 +266,7 @@ Dec 1993  Fran
 
 Copyright (C) 1993 François Pinard
 Copyright (C) 1994 Andrew Moore
-Copyright (C) 2006-2016 Antonio Diaz Diaz.
+Copyright (C) 2006-2017 Antonio Diaz Diaz.
 
 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
diff --git a/INSTALL b/INSTALL
index 18e7c6c..444069b 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -1,7 +1,7 @@
 Requirements
 ------------
 You will need a C compiler and a C library compatible with GNU libc.
-I use gcc 4.9.1 and 4.1.2, but the code should compile with any
+I use gcc 5.3.0 and 4.1.2, but the code should compile with any
 standards compliant compiler.
 Gcc is available at http://gcc.gnu.org.
 
@@ -58,7 +58,7 @@ After running 'configure', you can run 'make' and 'make install' as
 explained above.
 
 
-Copyright (C) 2006-2016 Antonio Diaz Diaz.
+Copyright (C) 2006-2017 Antonio Diaz Diaz.
 
 This file is free documentation: you have unlimited permission to copy,
 distribute and modify it.
index 44d6e15..18be552 100644 (file)
@@ -6,6 +6,7 @@ INSTALL_SCRIPT = $(INSTALL) -m 755
 INSTALL_DATA = $(INSTALL) -m 644
 INSTALL_DIR = $(INSTALL) -d -m 755
 SHELL = /bin/sh
+CAN_RUN_INSTALLINFO = $(SHELL) -c "install-info --version" > /dev/null 2>&1
 
 objs = buffer.o carg_parser.o global.o io.o main.o main_loop.o regex.o signal.o
 
@@ -71,7 +72,9 @@ install-info :
        if [ ! -d "$(DESTDIR)$(infodir)" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(infodir)" ; fi
        -rm -f "$(DESTDIR)$(infodir)/$(pkgname).info"*
        $(INSTALL_DATA) $(VPATH)/doc/$(pkgname).info "$(DESTDIR)$(infodir)/$(program_prefix)$(pkgname).info"
-       -install-info --info-dir="$(DESTDIR)$(infodir)" "$(DESTDIR)$(infodir)/$(program_prefix)$(pkgname).info"
+       -if $(CAN_RUN_INSTALLINFO) ; then \
+               install-info --info-dir="$(DESTDIR)$(infodir)" "$(DESTDIR)$(infodir)/$(program_prefix)$(pkgname).info" ; \
+       fi
 
 install-info-compress : install-info
        lzip -v -9 "$(DESTDIR)$(infodir)/$(pkgname).info"
@@ -95,7 +98,9 @@ uninstall-bin :
        -rm -f "$(DESTDIR)$(bindir)/$(program_prefix)r$(progname)"
 
 uninstall-info :
-       -install-info --info-dir="$(DESTDIR)$(infodir)" --remove "$(DESTDIR)$(infodir)/$(program_prefix)$(pkgname).info"
+       -if $(CAN_RUN_INSTALLINFO) ; then \
+               install-info --info-dir="$(DESTDIR)$(infodir)" --remove "$(DESTDIR)$(infodir)/$(program_prefix)$(pkgname).info" ; \
+       fi
        -rm -f "$(DESTDIR)$(infodir)/$(program_prefix)$(pkgname).info"*
 
 uninstall-man :
@@ -119,14 +124,14 @@ dist : doc
          $(DISTNAME)/doc/$(pkgname).texi \
          $(DISTNAME)/doc/fdl.texi \
          $(DISTNAME)/r$(progname).in \
+         $(DISTNAME)/*.h \
+         $(DISTNAME)/*.c \
          $(DISTNAME)/testsuite/check.sh \
-         $(DISTNAME)/testsuite/*.t \
-         $(DISTNAME)/testsuite/*.d \
+         $(DISTNAME)/testsuite/test.bin \
+         $(DISTNAME)/testsuite/test.txt \
+         $(DISTNAME)/testsuite/*.ed \
          $(DISTNAME)/testsuite/*.r \
-         $(DISTNAME)/testsuite/*.pr \
-         $(DISTNAME)/testsuite/*.err \
-         $(DISTNAME)/*.h \
-         $(DISTNAME)/*.c
+         $(DISTNAME)/testsuite/*.err
        rm -f $(DISTNAME)
        lzip -v -9 $(DISTNAME).tar
 
diff --git a/NEWS b/NEWS
index 22f6186..4ee028f 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,68 @@
-Changes in version 1.13:
+Changes in version 1.14:
 
-A memory leak and a resource leak (file not closed on error) have been
-fixed. (Both issues were reported by Cédric Picard).
+Version 1.14 is the largest bug hunt ever attempted in GNU ed. Other
+goals of version 1.14 are to complete the documentation and to remove
+any gratuitous incompatibilities with the POSIX standard. Thanks to Ori
+Avtalion for initiating all this with a couple bug reports. ;-)
+
+Byte counts, informative messages, command error messages, and the '?'
+and '!' prompts are now written to stdout instead of to stderr. The
+standard error (stderr) is now used only for diagnostic messages.
+
+The current address is now correctly set to the addressed line after an
+empty insert command.
+
+Fixed inconsistent behavior of the substitute command. It incorrectly
+reported 'Invalid pattern delimiter' when the two last delimiters were
+omitted after a null regular expression. Now it consistently reports
+'Missing pattern delimiter' if the two last delimiters are omitted after
+any regular expression (null or not).
+
+'s/a/%' has been fixed. It incorrectly replaced 'a' with '%' instead of
+using the replacement from the last substitution.
+
+An infinite loop, happening when EOF was found in the middle of a
+replacement string, has been fixed.
+
+Ed no longer accepts newlines in the replacement of a 's' command if it
+is part of the command list of a global command, because in this case
+the meaning of the newline becomes ambiguous. For the same reason, the
+last delimiter can't be omitted if the 's' command is not the last
+command in the command list.
+
+The substitute command now correctly sets the current address to the
+address of the last line on which a substitution occurred, and leaves it
+unchanged if no substitution is performed.
+
+A bug in the calculation of address offsets has been fixed. '3 ---- 2'
+was calculated as address -2 instead of the correct address 1.
+
+Address ranges with the first address omitted are now accepted.
+
+The current address is now correctly set to the addressed line (or to
+the new last line if at EOF) after an empty replacement text in the
+change command.
+
+Repeated print suffixes are now rejected. It has been documented that ed
+allows any combination of non-repeated print suffixes and combines their
+effects.
+
+The substitute command now accepts suffixes in any order.
+
+The 'repeat substitution' command now rejects multiple count suffixes.
+
+The 'p' suffix of the 'repeat substitution' command now toggles all the
+print suffixes of the last substitution.
+
+End of file on standard input now behaves as a 'q' command.
+
+The modified status is no longer cleared after writing the buffer to the
+standard input of a shell command. (Reported by Jérôme Frgacic).
+
+The descriptions of the 'a', 'c', 'e', 'g', 'i', 'j', 'k', 'm', 'q',
+'r', 's', 'u' and 'w' commands in the manual have been fixed.
+
+Most tests in the testsuite have been improved. Bug reporting has been
+simplified; only the failed logs and results are kept in the test
+directory, which can then be (tarred, compressed, and) attached to the
+bug report.
diff --git a/README b/README
index 5f43db1..02ca109 100644 (file)
--- a/README
+++ b/README
@@ -53,7 +53,7 @@ EXTENSIONS
 
   * 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,
+      * 's' (i.e., s[1-9rgp]*) 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.
@@ -65,11 +65,11 @@ EXTENSIONS
     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.
+    argument for backslash escapes; i.e., any character preceded by a
+    backslash is interpreted literally. If the first 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
@@ -77,14 +77,10 @@ EXTENSIONS
 
 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.
+  * To support the BSD 's' command (see EXTENSIONS above), substitution
+    patterns cannot be delimited by the digits '1' to '9' or by the
+    characters 'r', 'g' and 'p'. In contrast, POSIX specifies that any
+    character except space and newline can be 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:
@@ -99,11 +95,6 @@ DEVIATIONS
     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.
@@ -111,23 +102,23 @@ DEVIATIONS
 
 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
+The files in the 'testsuite' directory with extensions '.ed', '.r', 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 extensions have the following meanings:
+.ed   Ed script - a list of ed commands.
 .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
+.err  Error - invalid ed commands that should generate an error.
+
+The output of the .ed scripts is written to files with .o extension and
+compared with their corresponding .r result files. The .err scripts
+should exit with non-zero status without altering the contents of the
+buffer.
 
-The output of the tests is written to files with .o and .ro suffixes and
-compared with their corresponding .r and .pr result files.
 If any test fails, the error messages look like:
 
        *** The script u.ed exited abnormally ***
@@ -136,7 +127,7 @@ or:
 
 
 Copyright (C) 1993, 1994 Andrew Moore
-Copyright (C) 2006-2016 Antonio Diaz Diaz.
+Copyright (C) 2006-2017 Antonio Diaz Diaz.
 
 This file is free documentation: you have unlimited permission to copy,
 distribute and modify it.
diff --git a/TODO b/TODO
index 3f777e4..bfc94fa 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,9 +1,6 @@
 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
+1) g/./s^@^@   - okay: NULs in commands
+2) g/./s/^@/   - okay: NULs in patterns
 3) ed -x       - verify: 8-bit clean
 4) ed          - verify: long-line support
 5) ed          - verify: interactive/help mode
index b2121ce..18137d9 100644 (file)
--- a/buffer.c
+++ b/buffer.c
@@ -1,7 +1,7 @@
 /* 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-2016 Antonio Diaz Diaz.
+    Copyright (C) 2006-2017 Antonio Diaz Diaz.
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -33,7 +33,6 @@ 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 */
@@ -56,9 +55,6 @@ 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; }
@@ -81,10 +77,11 @@ static void insert_node( line_t * const lp, line_t * const prev )
 
 
 /* add a line node in the editor buffer after the given line */
-static void add_line_node( line_t * const lp, const int addr )
+static void add_line_node( line_t * const lp )
   {
-  line_t * const prev = search_line_node( addr );
+  line_t * const prev = search_line_node( current_addr_ );
   insert_node( lp, prev );
+  ++current_addr_;
   ++last_addr_;
   }
 
@@ -108,7 +105,7 @@ static line_t * dup_line_node( line_t * const lp )
    line n; stop when either a single period is read or EOF.
    Returns false if insertion fails. */
 bool append_lines( const char ** const ibufpp, const int addr,
-                   const bool isglobal )
+                   bool insert, const bool isglobal )
   {
   int size = 0;
   undo_t * up = 0;
@@ -118,10 +115,9 @@ bool append_lines( const char ** const ibufpp, const int addr,
     {
     if( !isglobal )
       {
-      *ibufpp = get_tty_line( &size );
-      if( !*ibufpp ) return false;
-      if( size == 0 || (*ibufpp)[size-1] != '\n' )
-        { clearerr( stdin ); return ( size == 0 ); }
+      *ibufpp = get_stdin_line( &size );
+      if( !*ibufpp ) return false;                     /* error */
+      if( size <= 0 ) return true;                     /* EOF */
       }
     else
       {
@@ -130,7 +126,8 @@ bool append_lines( const char ** const ibufpp, const int addr,
       }
     if( size == 2 && **ibufpp == '.' ) { *ibufpp += size; return true; }
     disable_interrupts();
-    if( !put_sbuf_line( *ibufpp, size, current_addr_ ) )
+    if( insert ) { insert = false; if( current_addr_ > 0 ) --current_addr_; }
+    if( !put_sbuf_line( *ibufpp, size ) )
       { enable_interrupts(); return false; }
     if( up ) up->tail = search_line_node( current_addr_ );
     else
@@ -202,7 +199,7 @@ bool copy_lines( const int first_addr, const int second_addr, const int addr )
       disable_interrupts();
       lp = dup_line_node( np );
       if( !lp ) { enable_interrupts(); return false; }
-      add_line_node( lp, current_addr_++ );
+      add_line_node( lp );
       if( up ) up->tail = lp;
       else
         {
@@ -230,7 +227,7 @@ bool delete_lines( const int from, const int to, const bool isglobal )
   if( isglobal ) unset_active_nodes( p->q_forw, n );
   link_nodes( p, n );
   last_addr_ -= to - from + 1;
-  current_addr_ = from - 1;
+  current_addr_ = min( from, last_addr_ );
   modified_ = true;
   enable_interrupts();
   return true;
@@ -324,7 +321,7 @@ bool join_lines( const int from, const int to, const bool isglobal )
   if( !delete_lines( from, to, isglobal ) ) return false;
   current_addr_ = from - 1;
   disable_interrupts();
-  if( !put_sbuf_line( buf, size, current_addr_ ) ||
+  if( !put_sbuf_line( buf, size ) ||
       !push_undo_atom( UADD, current_addr_, current_addr_ ) )
     { enable_interrupts(); return false; }
   modified_ = true;
@@ -381,7 +378,7 @@ bool move_lines( const int first_addr, const int second_addr, const int addr,
 /* open scratch file */
 bool open_sbuf( void )
   {
-  isbinary_ = newline_added_ = false;
+  isbinary_ = false; reset_unterminated_line();
   sfp = tmpfile();
   if( !sfp )
     {
@@ -419,7 +416,7 @@ bool put_lines( const int addr )
     disable_interrupts();
     p = dup_line_node( lp );
     if( !p ) { enable_interrupts(); return false; }
-    add_line_node( p, current_addr_++ );
+    add_line_node( p );
     if( up ) up->tail = p;
     else
       {
@@ -435,9 +432,8 @@ bool put_lines( const int addr )
 
 
 /* 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 )
+   editor buffer; return a pointer to the end of the text, or 0 if error */
+const char * put_sbuf_line( const char * const buf, const int size )
   {
   const char * const p = (const char *) memchr( buf, '\n', size );
   line_t * lp;
@@ -467,8 +463,7 @@ const char * put_sbuf_line( const char * const buf, const int size,
   lp = dup_line_node( 0 );
   if( !lp ) return 0;
   lp->pos = sfpos; lp->len = len;
-  add_line_node( lp, addr );
-  ++current_addr_;
+  add_line_node( lp );
   sfpos += len;                                /* update file position */
   return p + 1;
   }
@@ -542,6 +537,7 @@ void clear_undo_stack( void )
         {
         line_t * const lp = bp->q_forw;
         unmark_line_node( bp );
+        unmark_unterminated_line( bp );
         free( bp );
         bp = lp;
         }
index 3d4e89f..6850643 100644 (file)
@@ -1,5 +1,5 @@
 /*  Arg_parser - POSIX/GNU command line argument parser. (C version)
-    Copyright (C) 2006-2016 Antonio Diaz Diaz.
+    Copyright (C) 2006-2017 Antonio Diaz Diaz.
 
     This library is free software. Redistribution and use in source and
     binary forms, with or without modification, are permitted provided
@@ -94,7 +94,7 @@ static char parse_long_option( struct Arg_parser * const ap,
       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 */
+        ambig = 1;             /* Second or later nonexact match found */
       }
 
   if( ambig && !exact )
@@ -230,7 +230,9 @@ char ap_init( struct Arg_parser * const ap,
       }
     else
       {
-      if( !in_order )
+      if( in_order )
+        { if( !push_back_record( ap, 0, argv[argind++] ) ) return 0; }
+      else
         {
         void * tmp = ap_resize_buffer( non_options,
                        ( non_options_size + 1 ) * sizeof *non_options );
@@ -238,7 +240,6 @@ char ap_init( struct Arg_parser * const ap,
         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 );
index e918942..c4ce31d 100644 (file)
@@ -1,5 +1,5 @@
 /*  Arg_parser - POSIX/GNU command line argument parser. (C version)
-    Copyright (C) 2006-2016 Antonio Diaz Diaz.
+    Copyright (C) 2006-2017 Antonio Diaz Diaz.
 
     This library is free software. Redistribution and use in source and
     binary forms, with or without modification, are permitted provided
index 50f3cad..4237efb 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,12 +1,12 @@
 #! /bin/sh
 # configure script for GNU ed - The GNU line editor
-# Copyright (C) 2006-2016 Antonio Diaz Diaz.
+# Copyright (C) 2006-2017 Antonio Diaz Diaz.
 #
 # This configure script is free software: you have unlimited permission
 # to copy, distribute and modify it.
 
 pkgname=ed
-pkgversion=1.13
+pkgversion=1.14.2
 progname=ed
 srctrigger=doc/${pkgname}.texi
 
@@ -27,11 +27,11 @@ CFLAGS='-Wall -W -O2'
 LDFLAGS=
 
 # checking whether we are using GNU C.
-${CC} --version > /dev/null 2>&1
-if [ $? != 0 ] ; then
+/bin/sh -c "${CC} --version" > /dev/null 2>&1 ||
+       {
        CC=cc
-       CFLAGS='-W -O2'
-fi
+       CFLAGS=-O2
+       }
 
 # Loop over all args
 args=
@@ -53,7 +53,10 @@ while [ $# != 0 ] ; do
        # Process the options
        case ${option} in
        --help | -h)
-               echo "Usage: configure [options]"
+               echo "Usage: $0 [OPTION]... [VAR=VALUE]..."
+               echo
+               echo "To assign makefile variables (e.g., CC, CFLAGS...), specify them as"
+               echo "arguments to configure in the form VAR=VALUE."
                echo
                echo "Options: [defaults in brackets]"
                echo "  -h, --help            display this help and exit"
@@ -170,7 +173,7 @@ echo "LDFLAGS = ${LDFLAGS}"
 rm -f Makefile
 cat > Makefile << EOF
 # Makefile for GNU ed - The GNU line editor
-# Copyright (C) 2006-2016 Antonio Diaz Diaz.
+# Copyright (C) 2006-2017 Antonio Diaz Diaz.
 # This file was generated automatically by configure. Don't edit.
 #
 # This Makefile is free software: you have unlimited permission
index ac8b882..66e1331 100644 (file)
--- a/doc/ed.1
+++ b/doc/ed.1
@@ -1,5 +1,5 @@
 .\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.46.1.
-.TH ED "1" "January 2016" "ed 1.13" "User Commands"
+.TH ED "1" "February 2017" "ed 1.14.2" "User Commands"
 .SH NAME
 ed \- line-oriented text editor
 .SH SYNOPSIS
@@ -28,10 +28,10 @@ use STRING as an interactive prompt
 run in restricted mode
 .TP
 \fB\-s\fR, \fB\-\-quiet\fR, \fB\-\-silent\fR
-suppress diagnostics
+suppress diagnostics, byte counts and '!' prompt
 .TP
 \fB\-v\fR, \fB\-\-verbose\fR
-be verbose
+be verbose; equivalent to the 'H' command
 .PP
 Start edit by reading in 'file' if given.
 If 'file' begins with a '!', read output of shell command.
@@ -49,7 +49,7 @@ General help using GNU software: http://www.gnu.org/gethelp
 .SH COPYRIGHT
 Copyright \(co 1994 Andrew L. Moore.
 .br
-Copyright \(co 2016 Antonio Diaz Diaz.
+Copyright \(co 2017 Antonio Diaz Diaz.
 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.
index a239faf..7a5575f 100644 (file)
@@ -5,7 +5,7 @@ START-INFO-DIR-ENTRY
 * Ed: (ed).                     The GNU line editor
 END-INFO-DIR-ENTRY
 
-   Copyright (C) 1993, 1994, 2006-2016 Free Software Foundation, Inc.
+   Copyright (C) 1993, 1994, 2006-2017 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
@@ -18,7 +18,7 @@ File: ed.info,  Node: Top,  Next: Overview,  Up: (dir)
 The GNU ed line editor
 **********************
 
-This manual is for GNU ed (version 1.13, 24 January 2016).
+This manual is for GNU ed (version 1.14.2, 22 February 2017).
 
 
    GNU ed is a line-oriented text editor. It is used to create, display,
@@ -43,7 +43,7 @@ superseded by full-screen editors such as GNU Emacs or GNU Moe.
 * GNU Free Documentation License::  How you can copy and share this manual
 
 
-   Copyright (C) 1993, 1994, 2006-2016 Free Software Foundation, Inc.
+   Copyright (C) 1993, 1994, 2006-2017 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
@@ -277,8 +277,8 @@ is 's/OLD/NEW/'.
      # 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.
+     # The title is now the first line, and the current address has been
+     # set to the address of this line as well.
      ,p
       Sonnet #50
      No more be grieved at that which thou hast done.
@@ -289,9 +289,9 @@ is 's/OLD/NEW/'.
      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.
+   When 'ed' opens a file, the current address is initially set to the
+address of the last line of that file. Similarly, the move command 'm'
+sets the current address to the address of the last line moved.
 
    Related programs or routines are 'vi (1)', 'sed (1)', 'regex (3)',
 'sh (1)'. Relevant documents are:
@@ -355,14 +355,18 @@ prefixed with a bang.
 '-s'
 '--quiet'
 '--silent'
-     Suppresses diagnostics. This should be used if 'ed''s standard
-     input is from a script.
+     Suppresses diagnostics, the printing of byte counts by 'e', 'E',
+     'r' and 'w' commands, and the '!' prompt after a '!' command. This
+     option may be useful if 'ed''s standard input is from a script.
 
 '-v'
 '--verbose'
-     Verbose mode. This may be toggled on and off with the 'H' command.
+     Verbose mode; prints error explanations. This may be toggled on
+     and off with the 'H' command.
 
 
+   Exit status: 0 if no errors occurred; otherwise >0.
+
 \1f
 File: ed.info,  Node: Line addressing,  Next: Regular expressions,  Prev: Invoking ed,  Up: Top
 
@@ -372,25 +376,30 @@ File: ed.info,  Node: Line addressing,  Next: Regular expressions,  Prev: Invoki
 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.
+read, the current address is set to the address of the last line of the
+file. In general, the current address is set to the address of the last
+line affected by a command.
 
    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 a 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.
-
-   In a semicolon-delimited range, the current address ('.') is set to
-the first address before the second address is calculated. This feature
-can be used to set the starting line for searches.
+or a semicolon (';'). In a semicolon-delimited range, the current
+address ('.') is set to the first address before the second address is
+calculated. This feature can be used to set the starting line for
+searches. The value of the first address in a range cannot exceed the
+value of the second.
+
+   Addresses can be omitted on either side of the comma or semicolon
+separator. If only the first address is given in a range, then the
+second address is set to the given address. If only the second address
+is given, the resulting address pairs are '1,addr' and '.;addr'
+respectively. If a 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. It is an error to give any number of addresses to a command that
+requires zero addresses.
 
    A line address is constructed as follows:
 
@@ -423,17 +432,17 @@ can be used to set the starting line for searches.
 
 ';'
      The current through last lines in the buffer. This is equivalent
-     to the address range '.,$'.
+     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.
+     current line, if necessary. A null RE '//' 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.
+     line, if necessary. A null RE '??' repeats the last search.
 
 ''x'
      The apostrophe-x character pair addresses the line previously
@@ -444,14 +453,20 @@ can be used to set the starting line for searches.
    Addresses can be followed by one or more address offsets, optionally
 separated by whitespace. Offsets are constructed as follows:
 
-   * A number adds the indicated number of lines to the address.
-
    * '+' or '-' followed by a number adds or subtracts the indicated
      number of lines to or from the address.
 
    * '+' or '-' not followed by a number adds or subtracts 1 to or from
      the address.
 
+   * A number adds the indicated number of lines to the address.
+
+
+   It is not an error if an intermediate address value is negative or
+greater than the address of the last line in the buffer. It is an error
+if the final address value is negative or greater than the address of
+the last line in the buffer. It is an error if a search for a RE fails
+to find a matching line.
 
 \1f
 File: ed.info,  Node: Regular expressions,  Next: Commands,  Prev: Line addressing,  Up: Top
@@ -612,7 +627,9 @@ 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.
+command. It is not portable to give more than one print suffix, but
+'ed' allows any combination of non-repeated print suffixes and combines
+their effects.
 
    An interrupt (typically <Control-C>) has the effect of aborting the
 current command and returning the editor to command mode.
@@ -622,64 +639,80 @@ 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.
+     Appends text to the buffer after the addressed line. The address
+     '0' (zero) is valid for this command; it places the entered text at
+     the beginning of the buffer. Text is entered in input mode. The
+     current address is set to the address of the last line entered or,
+     if there were none, to the addressed line.
 
 '(.,.)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.
+     the buffer, and text is inserted in their place. Text is entered
+     in input mode. The current address is set to the address of the
+     last line entered or, if there were none, to the new address of
+     the line after the last line deleted; if the lines deleted were
+     originally at the end of the buffer, the current address is set to
+     the address of the new last line; if no lines remain in the
+     buffer, the current address is set to zero.
 
 '(.,.)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.
+     Deletes the addressed lines from the buffer. The current address
+     is set to the new address of the line after the last line deleted;
+     if the lines deleted were originally at the end of the buffer, the
+     current address is set to the address of the new last line; if no
+     lines remain in the buffer, the current address is set to zero.
 
 '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.
+     address is set to the address of the last line in the buffer.
 
-'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.
+     If FILE is prefixed with a bang (!), then it is interpreted as a
+     shell command whose output is to be read, (*note shell escape
+     command:: '!' below). In this case the default filename is
+     unchanged.
+
+     A warning is printed if any changes have been made in the buffer
+     since the last 'w' command that wrote the entire buffer to a file.
 
 '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.
+     except that unwritten changes are discarded without warning.
 
 '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.
+     Global command. The global command makes two passes over the file.
+     On the first pass, all the addressed lines matching a regular
+     expression RE are marked. Then, going sequentially from the
+     beginning of the file to the end of the file, the given
+     COMMAND-LIST is executed for each marked line, with the current
+     address set to the address of that line. Any line modified by the
+     COMMAND-LIST is unmarked. The final value of the current address
+     is the value assigned by the last command in the last COMMAND-LIST
+     executed. If there were no matching lines, the current address is
+     unchanged.
+
+     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'. The '.'
+     terminating the input mode of commands 'a', 'c', and 'i' can be
+     omitted if it would be the last line of COMMAND-LIST. 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.
+     enter a COMMAND-LIST. The final value of the current address is
+     the value assigned by the last command executed. If there were no
+     matching lines, the current address is unchanged.
 
      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
@@ -694,41 +727,48 @@ specified (in parenthesis).
      begin with this command to aid in debugging.
 
 '(.)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.
+     Inserts text in the buffer before the addressed 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 address of the last line entered or, if there were none, to the
+     addressed line.
 
 '(.,.+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.
+     Joins the addressed lines, replacing them by a single line
+     containing their joined text. If only one address is given, this
+     command does nothing. If lines are joined, the current address is
+     set to the address of the joined line. Else, the current address
+     is unchanged.
 
 '(.)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.
+     deleted or otherwise modified. The current address is unchanged.
 
 '(.,.)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.
+     List command. 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. Special characters are
+     printed as escape sequences. The current address is set to the
+     address of 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.
+     the right-hand destination address. The destination address '0'
+     (zero) is valid for this command; it moves the addressed lines to
+     the beginning of the buffer. It is an error if the destination
+     address falls within the range of moved lines. 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.
+     Number command. Prints the addressed lines, preceding each line by
+     its line number and a <tab>. The current address is set to the
+     address of the last line printed.
 
 '(.,.)p'
-     Prints the addressed lines. The current address is set to the last
-     line printed.
+     Prints the addressed lines. The current address is set to the
+     address of the last line printed.
 
 'P'
      Toggles the command prompt on and off. Unless a prompt is
@@ -736,68 +776,89 @@ specified (in parenthesis).
      default turned off.
 
 'q'
-     Quits 'ed'.
+     Quits 'ed'. A warning is printed if any changes have been made in
+     the buffer since the last 'w' command that wrote the entire buffer
+     to a file.
 
 '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.
+     Reads FILE and appends it 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
+     address '0' (zero) is valid for this command; it reads the file at
+     the beginning of the buffer. The current address is set to the
+     address of the last line read or, if there were none, to the
+     addressed line.
+
+     If FILE is prefixed with a bang (!), then it is interpreted as a
+     shell command whose output is to be read, (*note shell escape
+     command:: '!' below). In this case the default filename is
+     unchanged.
 
 '(.,.)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.
+     Substitute command. Replaces text in the addressed lines matching a
+     regular expression RE with REPLACEMENT. By default, only the first
+     match in each line is replaced. The 's' command accepts any
+     combination of the suffixes 'g', 'COUNT', 'l', 'n', and 'p'. If
+     the 'g' (global) suffix is given, then every match is replaced.
+     The 'COUNT' suffix, where COUNT is a positive number, causes only
+     the COUNTth match to be replaced. 'g' and 'COUNT' can't be
+     specified in the same command. 'l', 'n', and 'p' are the usual
+     print suffixes. It is an error if no substitutions are performed
+     on any of the addressed lines. The current address is set to the
+     address of the last line on which a substitution occurred. If a
+     line is split, a substitution is considered to have occurred on
+     each of the new lines. If no substitution is performed, the
+     current address is unchanged.
 
      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.
+     command shown below. If the last delimiter is omitted, then the
+     last line affected is printed as if the print suffix 'p' were
+     specified. The last delimiter can't be omitted if the 's' command
+     is part of a 'g' or 'v' COMMAND-LIST and is not the last command
+     in the list, because the meaning of the following escaped newline
+     becomes ambiguous.
 
      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 ('\').
+     of the matched text. If the corresponding backreference expression
+     does not match, then the character sequence '\M' is replaced by
+     the empty string. If REPLACEMENT consists of a single '%', then
+     REPLACEMENT from the last substitution is used.
+
+     A line can be split by including a newline escaped with a backslash
+     ('\') in REPLACEMENT, except if the 's' command is part of a 'g'
+     or 'v' COMMAND-LIST, because in this case the meaning of the
+     escaped newline becomes ambiguous. Each backslash in REPLACEMENT
+     removes the special meaning (if any) of the following character.
 
 '(.,.)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.
+     the 'g' and 'COUNT' suffixes described above, and any combination
+     of the suffixes 'p' and 'r'. The 'g' suffix toggles the global
+     suffix of the last substitution and resets COUNT to 1. The 'p'
+     suffix toggles the print suffixes of the last substitution. The
+     'r' suffix causes the regular expression of the last search to be
+     used instead of that of the last substitution (if the search
+     happened after the substitution).
 
 '(.,.)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.
+     right-hand destination address. If the destination address is '0'
+     (zero), the lines are copied at the beginning of the buffer. The
+     current address is set to the address of 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.
+     Undoes the effect of the last command that modified anything in the
+     buffer 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
@@ -815,10 +876,13 @@ specified (in parenthesis).
      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.
+     If FILE is prefixed with a bang (!), then it is interpreted as a
+     shell command and the addressed lines are written to its standard
+     input, (*note shell escape command:: '!' below). In this case the
+     default filename is unchanged. Writing the buffer to a shell
+     command does not prevent the warning to the user if an attempt is
+     made to overwrite or discard the buffer via the 'e' or 'q'
+     commands.
 
 '(1,$)wq FILE'
      Writes the addressed lines to FILE, and then executes a 'q'
@@ -826,16 +890,17 @@ specified (in parenthesis).
 
 '(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
+     the 'w' command, except 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.
+     line. The current address is set to the address of 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'
+     buffer is overwritten by subsequent 'c', 'd', 'j', 's', or 'y'
      commands. The current address is unchanged.
 
 '(.+1)zN'
@@ -843,15 +908,16 @@ specified (in parenthesis).
      window size to N. If N is not specified, then the current window
      size is used. Window size defaults to screen size minus two lines,
      or to 22 if screen size can't be determined. The current address
-     is set to the last line printed.
+     is set to the address of 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.
+     Shell escape command. Executes COMMAND via 'sh (1)'. If the first
+     character of COMMAND is '!', then it is replaced by the text of
+     the previous '!COMMAND'. Thus, '!!' repeats 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 address is unchanged.
 
 '(.,.)#'
      Begins a comment; the rest of the line, up to a newline, is
@@ -860,12 +926,13 @@ specified (in parenthesis).
      address is unchanged.
 
 '($)='
-     Prints the line number of the addressed line.
+     Prints the line number of the addressed line. The current address
+     is unchanged.
 
 '(.+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.
+     Null command. 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
@@ -905,8 +972,6 @@ command list is executed only once.
 modified buffer results in an error. If the command is entered a second
 time, it succeeds, but any changes to the buffer are lost.
 
-   Exit status: 0 if no errors occurred; otherwise >0.
-
 \1f
 File: ed.info,  Node: Problems,  Next: GNU Free Documentation License,  Prev: Diagnostics,  Up: Top
 
@@ -1412,16 +1477,17 @@ permit their use in free software.
 \1f
 Tag Table:
 Node: Top\7f535
-Node: Overview\7f2195
-Node: Introduction to line editing\7f4251
-Node: Invoking ed\7f11470
-Node: Line addressing\7f13270
-Node: Regular expressions\7f16347
-Node: Commands\7f21691
-Node: Limitations\7f32963
-Node: Diagnostics\7f33608
-Node: Problems\7f34309
-Node: GNU Free Documentation License\7f34842
+Node: Overview\7f2198
+Node: Introduction to line editing\7f4254
+Node: Invoking ed\7f11527
+Node: Line addressing\7f13529
+Node: Regular expressions\7f17242
+Node: Commands\7f22586
+Ref: shell escape command\7f36495
+Node: Limitations\7f37517
+Node: Diagnostics\7f38162
+Node: Problems\7f38807
+Node: GNU Free Documentation License\7f39340
 \1f
 End Tag Table
 
index 26fb2a9..cdf1f6c 100644 (file)
@@ -6,8 +6,8 @@
 @finalout
 @c %**end of header
 
-@set UPDATED 24 January 2016
-@set VERSION 1.13
+@set UPDATED 22 February 2017
+@set VERSION 1.14.2
 
 @dircategory Basics
 @direntry
@@ -15,7 +15,7 @@
 @end direntry
 
 @copying
-Copyright @copyright{} 1993, 1994, 2006-2016
+Copyright @copyright{} 1993, 1994, 2006-2017
 Free Software Foundation, Inc.
 
 Permission is granted to copy, distribute and/or modify this document
@@ -324,8 +324,8 @@ And loathsome canker lives in sweetest bud.
 # 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.
+# The title is now the first line, and the current address has been
+# set to the address of this line as well.
 ,p
  Sonnet #50
 No more be grieved at that which thou hast done.
@@ -337,9 +337,9 @@ wq sonnet
 $
 @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.
+When @command{ed} opens a file, the current address is initially set to
+the address of the last line of that file. Similarly, the move command
+@samp{m} sets the current address to the address of the last line moved.
 
 Related programs or routines are @command{vi (1)}, @command{sed (1)},
 @command{regex (3)},  @command{sh (1)}. Relevant documents
@@ -409,15 +409,20 @@ current directory and execution of shell commands.
 @item -s
 @itemx --quiet
 @itemx --silent
-Suppresses diagnostics. This should be used if @command{ed}'s standard
+Suppresses diagnostics, the printing of byte counts by @samp{e},
+@samp{E}, @samp{r} and @samp{w} commands, and the @samp{!} prompt after
+a @samp{!} command. This option may be useful 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.
+Verbose mode; prints error explanations. This may be toggled on and off
+with the @samp{H} command.
 
 @end table
 
+Exit status: 0 if no errors occurred; otherwise >0.
+
 
 @node Line addressing
 @chapter Line addressing
@@ -425,26 +430,30 @@ Verbose mode. This may be toggled on and off with the @samp{H} command.
 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.
+first read, the current address is set to the address of the last line
+of the file. In general, the current address is set to the address of
+the last line affected by a command.
 
 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 (@samp{,})
-or a semicolon (@samp{;}). 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.
-
-In a semicolon-delimited range, the current address (@samp{.}) is set to
-the first address before the second address is calculated. This feature
-can be used to set the starting line for searches.
+or a semicolon (@samp{;}). In a semicolon-delimited range, the current
+address (@samp{.}) is set to the first address before the second address
+is calculated. This feature can be used to set the starting line for
+searches. The value of the first address in a range cannot exceed the
+value of the second.
+
+Addresses can be omitted on either side of the comma or semicolon
+separator. If only the first address is given in a range, then the
+second address is set to the given address. If only the second address
+is given, the resulting address pairs are @samp{1,addr} and
+@samp{.;addr} respectively. If a @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. It is an error to give any
+number of addresses to a command that requires zero addresses.
 
 A line address is constructed as follows:
 
@@ -479,17 +488,17 @@ address range @samp{1,$}.
 
 @item ;
 The current through last lines in the buffer. This is equivalent to the
-address range @samp{.,$}.
+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.
+line, if necessary. A null @var{re} @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.
+necessary. A null @var{re} @samp{??} repeats the last search.
 
 @item 'x
 The apostrophe-x character pair addresses the line previously marked by
@@ -503,9 +512,6 @@ separated by whitespace. Offsets are constructed as follows:
 
 @itemize @bullet
 @item
-A number adds the indicated number of lines to the address.
-
-@item
 @samp{+} or @samp{-} followed by a number adds or subtracts the
 indicated number of lines to or from the address.
 
@@ -513,8 +519,17 @@ indicated number of lines to or from the address.
 @samp{+} or @samp{-} not followed by a number adds or subtracts 1 to or
 from the address.
 
+@item
+A number adds the indicated number of lines to the address.
+
 @end itemize
 
+It is not an error if an intermediate address value is negative or
+greater than the address of the last line in the buffer. It is an error
+if the final address value is negative or greater than the address of
+the last line in the buffer. It is an error if a search for a @var{re}
+fails to find a matching line.
+
 
 @node Regular expressions
 @chapter Regular expressions
@@ -688,7 +703,9 @@ 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.
+affected by the command. It is not portable to give more than one print
+suffix, but @command{ed} allows any combination of non-repeated print
+suffixes and combines their effects.
 
 An interrupt (typically @key{Control-C}) has the effect of aborting the
 current command and returning the editor to command mode.
@@ -700,65 +717,78 @@ 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.
+Appends text to the buffer after the addressed line. The address
+@samp{0} (zero) is valid for this command; it places the entered text at
+the beginning of the buffer. Text is entered in input mode. The current
+address is set to the address of the last line entered or, if there were
+none, to the addressed line.
 
 @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.
+buffer, and text is inserted in their place. Text is entered in input
+mode. The current address is set to the address of the last line entered
+or, if there were none, to the new address of the line after the last
+line deleted; if the lines deleted were originally at the end of the
+buffer, the current address is set to the address of the new last line;
+if no lines remain in the buffer, the current address is set to zero.
 
 @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.
+Deletes the addressed lines from the buffer. The current address is set
+to the new address of the line after the last line deleted; if the lines
+deleted were originally at the end of the buffer, the current address is
+set to the address of the new last line; if no lines remain in the
+buffer, the current address is set to zero.
 
 @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.
+the address of the last line in the buffer.
+
+If @var{file} is prefixed with a bang (!), then it is interpreted as a
+shell command whose output is to be read, (@pxref{shell escape command}
+@samp{!} below). In this case the default filename is unchanged.
 
-@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.
+A warning is printed if any changes have been made in the buffer since
+the last @samp{w} command that wrote the entire buffer to a file.
 
 @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.
+Global command. The global command makes two passes over the file. On
+the first pass, all the addressed lines matching a regular expression
+@var{re} are marked. Then, going sequentially from the beginning of the
+file to the end of the file, the given @var{command-list} is executed
+for each marked line, with the current address set to the address of
+that line. Any line modified by the @var{command-list} is unmarked. The
+final value of the current address is the value assigned by the last
+command in the last @var{command-list} executed. If there were no
+matching lines, the current address is unchanged.
+
+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}. The @samp{.} terminating the input mode of commands @samp{a},
+@samp{c}, and @samp{i} can be omitted if it would be the last line of
+@var{command-list}. 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}.
+enter a @var{command-list}. The final value of the current address is
+the value assigned by the last command executed. If there were no
+matching lines, the current address is unchanged.
 
 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{&}
@@ -773,41 +803,47 @@ not printed. It is recommended that ed scripts begin with this command
 to aid in debugging.
 
 @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.
+Inserts text in the buffer before the addressed 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 address of the last line entered or, if there were none, to the
+addressed line.
 
 @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.
+Joins the addressed lines, replacing them by a single line containing
+their joined text. If only one address is given, this command does
+nothing. If lines are joined, the current address is set to the address
+of the joined line. Else, the current address is unchanged.
 
 @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.
+or otherwise modified. The current address is unchanged.
 
 @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.
+List command. 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. Special characters are
+printed as escape sequences. The current address is set to the address
+of 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.
+right-hand destination address. The destination address @samp{0} (zero)
+is valid for this command; it moves the addressed lines to the beginning
+of the buffer. It is an error if the destination address falls within
+the range of moved lines. 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.
+Number command. Prints the addressed lines, preceding each line by its
+line number and a @key{tab}. The current address is set to the address
+of the last line printed.
 
 @item (.,.)p
-Prints the addressed lines. The current address is set to the last line
-printed.
+Prints the addressed lines. The current address is set to the address of
+the last line printed.
 
 @item P
 Toggles the command prompt on and off. Unless a prompt is specified with
@@ -815,69 +851,89 @@ command-line option @samp{-p}, the command prompt is by default turned
 off.
 
 @item q
-Quits @command{ed}.
+Quits @command{ed}. A warning is printed if any changes have been made
+in the buffer since the last @samp{w} command that wrote the entire
+buffer to a file.
 
 @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.
+Reads @var{file} and appends it 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 address
+@samp{0} (zero) is valid for this command; it reads the file at the
+beginning of the buffer. The current address is set to the address of
+the last line read or, if there were none, to the addressed line.
+
+If @var{file} is prefixed with a bang (!), then it is interpreted as a
+shell command whose output is to be read, (@pxref{shell escape command}
+@samp{!} below). In this case the default filename is unchanged.
 
 @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.
+Substitute command. 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. The @samp{s} command accepts any
+combination of the suffixes @samp{g}, @samp{@var{count}}, @samp{l},
+@samp{n}, and @samp{p}. If the @samp{g} (global) suffix is given, then
+every match is replaced. The @samp{@var{count}} suffix, where
+@var{count} is a positive number, causes only the @var{count}th match to
+be replaced. @samp{g} and @samp{@var{count}} can't be specified in the
+same command. @samp{l}, @samp{n}, and @samp{p} are the usual print
+suffixes. It is an error if no substitutions are performed on any of the
+addressed lines. The current address is set to the address of the last
+line on which a substitution occurred. If a line is split, a
+substitution is considered to have occurred on each of the new lines. If
+no substitution is performed, the current address is unchanged.
 
 @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.
+the @samp{s} command shown below. If the last delimiter is omitted, then
+the last line affected is printed as if the print suffix @samp{p} were
+specified. The last delimiter can't be omitted if the @samp{s} command
+is part of a @samp{g} or @samp{v} @var{command-list} and is not the last
+command in the list, because the meaning of the following escaped
+newline becomes ambiguous.
 
 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
+expression of the matched text. If the corresponding backreference
+expression does not match, then the character sequence @samp{\@var{m}}
+is replaced by the empty string. 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{\}).
+used.
+
+A line can be split by including a newline escaped with a backslash
+(@samp{\}) in @var{replacement}, except if the @samp{s} command is part
+of a @samp{g} or @samp{v} @var{command-list}, because in this case the
+meaning of the escaped newline becomes ambiguous. Each backslash in
+@var{replacement} removes the special meaning (if any) of the following
+character.
 
 @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.
+the @samp{g} and @samp{@var{count}} suffixes described above, and any
+combination of the suffixes @samp{p} and @samp{r}. The @samp{g} suffix
+toggles the global suffix of the last substitution and resets
+@var{count} to 1. The @samp{p} suffix toggles the print suffixes of the
+last substitution. The @samp{r} suffix causes the regular expression of
+the last search to be used instead of that of the last substitution (if
+the search happened after the substitution).
 
 @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.
+destination address. If the destination address is @samp{0} (zero), the
+lines are copied at the beginning of the buffer. The current address is
+set to the address of 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.
+Undoes the effect of the last command that modified anything in the
+buffer 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
@@ -895,10 +951,12 @@ 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.
+If @var{file} is prefixed with a bang (!), then it is interpreted as a
+shell command and the addressed lines are written to its standard input,
+(@pxref{shell escape command} @samp{!} below). In this case the default
+filename is unchanged. Writing the buffer to a shell command does not
+prevent the warning to the user if an attempt is made to overwrite or
+discard the buffer via the @samp{e} or @samp{q} commands.
 
 @item (1,$)wq @var{file}
 Writes the addressed lines to @var{file}, and then executes a @samp{q}
@@ -906,33 +964,35 @@ 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
+the @samp{w} command, except 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.
+line. The current address is set to the address of 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.
+overwritten by subsequent @samp{c}, @samp{d}, @samp{j}, @samp{s}, or
+@samp{y} commands. The current address is unchanged.
 
 @item (.+1)z@var{n}
 Scrolls @var{n} lines at a time starting at addressed line, and sets
 window size to @var{n}. If @var{n} is not specified, then the current
 window size is used. Window size defaults to screen size minus two
 lines, or to 22 if screen size can't be determined. The current address
-is set to the last line printed.
+is set to the address of the last line printed.
 
+@anchor{shell escape command}
 @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.
+Shell escape command. Executes @var{command} via @command{sh (1)}. If
+the first character of @var{command} is @samp{!}, then it is replaced by
+the text of the previous @samp{!@var{command}}. Thus, @samp{!!} repeats
+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 address is unchanged.
 
 @item (.,.)#
 Begins a comment; the rest of the line, up to a newline, is ignored. If
@@ -941,12 +1001,13 @@ address is set to that address. Otherwise, the current address is
 unchanged.
 
 @item ($)=
-Prints the line number of the addressed line.
+Prints the line number of the addressed line. The current address is
+unchanged.
 
 @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.
+Null command. 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
 
@@ -983,8 +1044,6 @@ 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.
 
-Exit status: 0 if no errors occurred; otherwise >0.
-
 
 @node Problems
 @chapter Reporting bugs
diff --git a/ed.h b/ed.h
index b7ccffb..2b4e3fa 100644 (file)
--- a/ed.h
+++ b/ed.h
@@ -1,7 +1,7 @@
 /*  Global declarations for the ed editor.  */
 /*  GNU ed - The GNU line editor.
     Copyright (C) 1993, 1994 Andrew Moore, Talke Studio
-    Copyright (C) 2006-2016 Antonio Diaz Diaz.
+    Copyright (C) 2006-2017 Antonio Diaz Diaz.
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -22,13 +22,11 @@ enum Bool { false = 0, true = 1 };
 typedef enum Bool bool;
 #endif
 
-enum Gflags
+enum Pflags                    /* print suffixes */
   {
-  GLB = 0x01,                  /* global command */
-  GLS = 0x02,                  /* list after command */
-  GNP = 0x04,                  /* enumerate after command */
-  GPR = 0x08,                  /* print after command */
-  GSG = 0x10                   /* global substitute */
+  GLS = 0x01,                  /* list after command */
+  GNP = 0x02,                  /* enumerate after command */
+  GPR = 0x04                   /* print after command */
   };
 
 
@@ -60,7 +58,7 @@ undo_t;
 
 /* defined in buffer.c */
 bool append_lines( const char ** const ibufpp, const int addr,
-                   const bool isglobal );
+                   bool insert, 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 );
@@ -77,17 +75,14 @@ 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 );
+const char * put_sbuf_line( const char * const buf, const int size );
 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 );
@@ -101,13 +96,16 @@ 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 );
+const char * get_stdin_line( int * const sizep );
+int linenum( void );
+bool print_lines( int from, const int to, const int pflags );
 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 );
+void reset_unterminated_line( void );
+void unmark_unterminated_line( const line_t * const lp );
 
 /* defined in main.c */
 bool is_regular_file( const int fd );
@@ -128,13 +126,12 @@ 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 );
+bool extract_replacement( const char ** const ibufpp, 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 );
+                         const int snum, const bool isglobal );
+bool set_subst_regex( const char ** const ibufpp );
+bool subst_regex( void );
 
 /* defined in signal.c */
 void disable_interrupts( void );
index 5a9c2e1..d6242dc 100644 (file)
--- a/global.c
+++ b/global.c
@@ -1,7 +1,7 @@
 /* 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-2016 Antonio Diaz Diaz.
+    Copyright (C) 2006-2017 Antonio Diaz Diaz.
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
diff --git a/io.c b/io.c
index bf7c154..d4d2a63 100644 (file)
--- a/io.c
+++ b/io.c
@@ -1,7 +1,7 @@
 /* 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-2016 Antonio Diaz Diaz.
+    Copyright (C) 2006-2017 Antonio Diaz Diaz.
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #include "ed.h"
 
 
+static const line_t * unterminated_line = 0;   /* last line has no '\n' */
+int linenum_ = 0;                              /* script line number */
+
+void reset_unterminated_line( void ) { unterminated_line = 0; }
+
+void unmark_unterminated_line( const line_t * const lp )
+  { if( unterminated_line == lp ) unterminated_line = 0; }
+
+static bool unterminated_last_line( void )
+  { return ( unterminated_line != 0 &&
+             unterminated_line == search_line_node( last_addr() ) ); }
+
+int linenum( void ) { return linenum_; }
+
+
 /* print text to stdout */
-static void put_tty_line( const char * p, int len, const int gflags )
+static void print_line( const char * p, int len, const int pflags )
   {
   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; }
+  if( pflags & GNP ) { printf( "%d\t", current_addr() ); col = 8; }
   while( --len >= 0 )
     {
     const unsigned char ch = *p++;
-    if( !( gflags & GLS ) ) putchar( ch );
+    if( !( pflags & GLS ) ) putchar( ch );
     else
       {
       if( ++col > window_columns() ) { col = 1; fputs( "\\\n", stdout ); }
@@ -55,13 +70,13 @@ static void put_tty_line( const char * p, int len, const int gflags )
         }
       }
     }
-  if( !traditional() && ( gflags & GLS ) ) putchar('$');
+  if( !traditional() && ( pflags & GLS ) ) putchar('$');
   putchar('\n');
   }
 
 
 /* print a range of lines to stdout */
-bool display_lines( int from, const int to, const int gflags )
+bool print_lines( int from, const int to, const int pflags )
   {
   line_t * const ep = search_line_node( inc_addr( to ) );
   line_t * bp = search_line_node( from );
@@ -72,7 +87,7 @@ bool display_lines( int from, const int to, const int gflags )
     const char * const s = get_sbuf_line( bp );
     if( !s ) return false;
     set_current_addr( from++ );
-    put_tty_line( s, bp->len, gflags );
+    print_line( s, bp->len, pflags );
     bp = bp->q_forw;
     }
   return true;
@@ -89,7 +104,8 @@ static bool trailing_escape( const char * const s, int len )
 
 
 /* If *ibufpp contains an escaped newline, get an extended line (one
-   with escaped newlines) from stdin */
+   with escaped newlines) from stdin.
+   Return line length in *lenp, including the trailing newline. */
 bool get_extended_line( const char ** const ibufpp, int * const lenp,
                         const bool strip_escaped_newlines )
   {
@@ -107,10 +123,9 @@ bool get_extended_line( const char ** const ibufpp, int * const lenp,
   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; }
+    const char * const s = get_stdin_line( &len2 );
+    if( !s ) return false;                     /* error */
+    if( len2 <= 0 ) return false;              /* EOF */
     if( !resize_buffer( &buf, &bufsz, len + len2 ) ) return false;
     memcpy( buf + len, s, len2 );
     len += len2;
@@ -127,9 +142,10 @@ bool get_extended_line( const char ** const ibufpp, int * const lenp,
 
 
 /* Read a line of text from stdin.
-   Returns pointer to buffer and line size (including trailing newline
-   if it exists) */
-const char * get_tty_line( int * const sizep )
+   Incomplete lines (lacking the trailing newline) are discarded.
+   Returns pointer to buffer and line size (including trailing newline),
+   or 0 if error, or *sizep = 0 if EOF */
+const char * get_stdin_line( int * const sizep )
   {
   static char * buf = 0;
   static int bufsz = 0;
@@ -138,28 +154,28 @@ const char * get_tty_line( int * const sizep )
   while( true )
     {
     const int c = getchar();
-    if( !resize_buffer( &buf, &bufsz, i + 2 ) )
-      { if( sizep ) *sizep = 0; return 0; }
+    if( !resize_buffer( &buf, &bufsz, i + 2 ) ) { *sizep = 0; return 0; }
     if( c == EOF )
       {
       if( ferror( stdin ) )
         {
         show_strerror( "stdin", errno );
         set_error_msg( "Cannot read stdin" );
-        clearerr( stdin ); if( sizep ) *sizep = 0;
-        return 0;
+        clearerr( stdin );
+        *sizep = 0; return 0;
         }
       if( feof( stdin ) )
         {
+        set_error_msg( "Unexpected end-of-file" );
         clearerr( stdin );
-        buf[i] = 0; if( sizep ) *sizep = i;
+        buf[0] = 0; *sizep = 0; if( i > 0 ) ++linenum_;        /* discard line */
         return buf;
         }
       }
     else
       {
       buf[i++] = c; if( !c ) set_binary(); if( c != '\n' ) continue;
-      buf[i] = 0; if( sizep ) *sizep = i;
+      ++linenum_; buf[i] = 0; *sizep = i;
       return buf;
       }
     }
@@ -170,7 +186,7 @@ const char * get_tty_line( int * const sizep )
    Returns pointer to buffer and line size (including 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 )
+                                      bool * const newline_addedp )
   {
   static char * buf = 0;
   static int bufsz = 0;
@@ -194,7 +210,7 @@ static const char * read_stream_line( FILE * const fp, int * const sizep,
       }
     else if( i )
       {
-      buf[i] = '\n'; buf[i+1] = 0; *newline_added_nowp = true;
+      buf[i] = '\n'; buf[i+1] = 0; *newline_addedp = true;
       if( !isbinary() ) ++i;
       }
     }
@@ -203,7 +219,8 @@ static const char * read_stream_line( FILE * const fp, int * const sizep,
   }
 
 
-/* read a stream into the editor buffer; return total size of data read */
+/* read a stream into the editor buffer;
+   return total size of data read, or -1 if error */
 static long read_stream( FILE * const fp, const int addr )
   {
   line_t * lp = search_line_node( addr );
@@ -211,18 +228,19 @@ static long read_stream( FILE * const fp, const int addr )
   long total_size = 0;
   const bool o_isbinary = isbinary();
   const bool appended = ( addr == last_addr() );
-  bool newline_added_now = false;
+  const bool o_unterminated_last_line = unterminated_last_line();
+  bool newline_added = false;
 
   set_current_addr( addr );
   while( true )
     {
     int size = 0;
-    const char * const s = read_stream_line( fp, &size, &newline_added_now );
+    const char * const s = read_stream_line( fp, &size, &newline_added );
     if( !s ) return -1;
-    if( size > 0 ) total_size += size;
-    else break;
+    if( size <= 0 ) break;
+    total_size += size;
     disable_interrupts();
-    if( !put_sbuf_line( s, size + newline_added_now, current_addr() ) )
+    if( !put_sbuf_line( s, size + newline_added ) )
       { enable_interrupts(); return -1; }
     lp = lp->q_forw;
     if( up ) up->tail = lp;
@@ -233,19 +251,19 @@ static long read_stream( FILE * const fp, const int addr )
       }
     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 )
+  if( addr && appended && total_size && o_unterminated_last_line )
+    fputs( "Newline inserted\n", stdout );             /* before stream */
+  else if( newline_added && ( !appended || !isbinary() ) )
+    fputs( "Newline appended\n", stdout );             /* after stream */
+  if( !appended && isbinary() && !o_isbinary && newline_added )
     ++total_size;
-  if( !total_size ) newline_added_now = true;
-  if( appended && newline_added_now ) set_newline_added();
+  if( appended && isbinary() && ( newline_added || total_size == 0 ) )
+    unterminated_line = search_line_node( last_addr() );
   return total_size;
   }
 
 
-/* read a named file/pipe into the buffer; return line count */
+/* read a named file/pipe into the buffer; return line count, or -1 if error */
 int read_file( const char * const filename, const int addr )
   {
   FILE * fp;
@@ -269,7 +287,7 @@ int read_file( const char * const filename, const int addr )
     set_error_msg( "Cannot close input file" );
     return -1;
     }
-  if( !scripted() ) fprintf( stderr, "%lu\n", size );
+  if( !scripted() ) printf( "%lu\n", size );
   return current_addr() - addr;
   }
 
@@ -286,7 +304,7 @@ static long write_stream( FILE * const fp, int from, const int to )
     char * p = get_sbuf_line( lp );
     if( !p ) return -1;
     len = lp->len;
-    if( from != last_addr() || !isbinary() || !newline_added() )
+    if( from != last_addr() || !isbinary() || !unterminated_last_line() )
       p[len++] = '\n';
     size += len;
     while( --len >= 0 )
@@ -327,6 +345,6 @@ int write_file( const char * const filename, const char * const mode,
     set_error_msg( "Cannot close output file" );
     return -1;
     }
-  if( !scripted() ) fprintf( stderr, "%lu\n", size );
+  if( !scripted() ) printf( "%lu\n", size );
   return ( from && from <= to ) ? to - from + 1 : 0;
   }
diff --git a/main.c b/main.c
index 915aa8b..a0dfc2e 100644 (file)
--- a/main.c
+++ b/main.c
@@ -1,5 +1,5 @@
 /*  GNU ed - The GNU line editor.
-    Copyright (C) 2006-2016 Antonio Diaz Diaz.
+    Copyright (C) 2006-2017 Antonio Diaz Diaz.
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 static const char * const Program_name = "GNU Ed";
 static const char * const program_name = "ed";
-static const char * const program_year = "2016";
+static const char * const program_year = "2017";
 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 scripted_ = false;         /* if set, suppress diagnostics,
+                                          byte counts and '!' prompt */
 static bool traditional_ = false;      /* if set, be backwards compatible */
 
 
@@ -68,8 +69,8 @@ static void show_help( void )
           "  -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"
+          "  -s, --quiet, --silent      suppress diagnostics, byte counts and '!' prompt\n"
+          "  -v, --verbose              be verbose; equivalent to the 'H' command\n"
           "Start edit by reading in 'file' if given.\n"
           "If 'file' begins with a '!', read output of shell command.\n"
           "\nExit status: 0 for a normal exit, 1 for environmental problems (file\n"
@@ -128,7 +129,7 @@ bool is_regular_file( const int fd )
 bool may_access_filename( const char * const name )
   {
   if( restricted_ &&
-      ( *name == '!' || !strcmp( name, ".." ) || strchr( name, '/' ) ) )
+      ( *name == '!' || strcmp( name, ".." ) == 0 || strchr( name, '/' ) ) )
     {
     set_error_msg( "Shell access restricted" );
     return false;
@@ -181,13 +182,14 @@ int main( const int argc, const char * const argv[] )
                 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( strcmp( arg, "-" ) == 0 ) { scripted_ = true; ++argind; continue; }
     if( may_access_filename( arg ) )
       {
       if( read_file( arg, 0 ) < 0 && is_regular_file( 0 ) )
@@ -196,7 +198,7 @@ int main( const int argc, const char * const argv[] )
       }
     else
       {
-      fputs( "?\n", stderr );
+      fputs( "?\n", stdout );
       if( arg[0] ) set_error_msg( "Invalid filename" );
       if( is_regular_file( 0 ) ) return 2;
       }
index a4c8914..bcf31a8 100644 (file)
@@ -1,6 +1,6 @@
 /*  GNU ed - The GNU line editor.
     Copyright (C) 1993, 1994 Andrew Moore, Talke Studio
-    Copyright (C) 2006-2016 Antonio Diaz Diaz.
+    Copyright (C) 2006-2017 Antonio Diaz Diaz.
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -87,66 +87,56 @@ 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 get_line_node_addr( mark[c] );
   }
 
 
 /* Returns 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 char * buf = 0;               /* temporary buffer */
   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;
+  int i = 0, len = 0;
+  bool replacement = false;
 
   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 */
+  if( **ibufpp != '!' ) buf[i++] = '!';                /* prefix command w/ bang */
+  else
+    {
+    if( shcmdlen <= 0 || ( traditional() && !shcmd[1] ) )
+      { set_error_msg( "No previous command" ); return 0; }
+    memcpy( buf, shcmd, shcmdlen );            /* bufsz >= shcmdlen */
+    i += shcmdlen; ++*ibufpp; replacement = true;
+    }
   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( **ibufpp == '%' )
       {
+      const char * p;
       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)++;
+      memcpy( buf + i, p, len );
+      i += len; ++*ibufpp; replacement = true;
       }
     else
       {
       if( !resize_buffer( &buf, &bufsz, i + 2 ) ) return 0;
-      buf[i++] = **ibufpp;
-      if( *(*ibufpp)++ == '\\' ) buf[i++] = *(*ibufpp)++;
+      if( ( buf[i++] = *(*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 );
+  shcmd[i] = 0; shcmdlen = i;
+  if( replacement ) printf( "%s\n", shcmd + 1 );
   return shcmd;
   }
 
@@ -159,7 +149,8 @@ static const char * skip_blanks( const char * p )
 
 
 /* Returns pointer to copy of filename in the command buffer */
-static const char * get_filename( const char ** const ibufpp )
+static const char * get_filename( const char ** const ibufpp,
+                                  const bool traditional_f_command )
   {
   static char * buf = 0;
   static int bufsz = 0;
@@ -172,14 +163,11 @@ static const char * get_filename( const char ** const ibufpp )
     int size = 0;
     if( !get_extended_line( ibufpp, &size, true ) ) return 0;
     if( **ibufpp == '!' )
-      {
-      ++*ibufpp;
-      return get_shell_command( 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] )
+  else if( !traditional_f_command && !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;
@@ -192,90 +180,85 @@ static const char * get_filename( const char ** const ibufpp )
 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 )
+/* Get line addresses from the command buffer until an invalid address
+   is seen. Returns the number of addresses read, or -1 if error.
+   If no addresses are found, both addresses are set to the current address.
+   If one address is found, both addresses are set to that address. */
+static int extract_addresses( const char ** const ibufpp )
   {
-  const char * const s = *ibufpp = skip_blanks( *ibufpp );
-  int addr = current_addr();
   bool first = true;                   /* true == addr, false == offset */
 
+  first_addr = second_addr = -1;       /* set to undefined */
+  *ibufpp = skip_blanks( *ibufpp );
+
   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;
+      if( !parse_int( &n, *ibufpp, ibufpp ) ) return -1;
+      if( first ) { first = false; second_addr = n; } else second_addr += n;
       }
     else switch( ch )
       {
-      case '+':
       case '\t':
-      case ' ':
-      case '-': *ibufpp = skip_blanks( ++*ibufpp );
-                if( isdigit( (unsigned char)**ibufpp ) )
+      case ' ': *ibufpp = skip_blanks( ++*ibufpp ); break;
+      case '+':
+      case '-': if( first ) { first = false; second_addr = current_addr(); }
+                if( isdigit( (unsigned char)(*ibufpp)[1] ) )
                   {
-                  if( !parse_int( &n, *ibufpp, ibufpp ) ) return -2;
-                  addr += ( ( ch == '-' ) ? -n : n );
+                  if( !parse_int( &n, *ibufpp, ibufpp ) ) return -1;
+                  second_addr += n;
                   }
-                else if( ch == '+' ) ++addr;
-                else if( ch == '-' ) --addr;
+                else { ++*ibufpp;
+                       if( ch == '+' ) ++second_addr; else --second_addr; }
                 break;
       case '.':
-      case '$': if( !first ) { invalid_address(); return -2; };
-                ++*ibufpp;
-                addr = ( ( ch == '.' ) ? current_addr() : last_addr() );
+      case '$': if( !first ) { invalid_address(); return -1; };
+                first = false; ++*ibufpp;
+                second_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;
+      case '?': if( !first ) { invalid_address(); return -1; };
+                second_addr = next_matching_node_addr( ibufpp, ch == '/' );
+                if( second_addr < 0 ) return -1;
+                if( ch == **ibufpp ) ++*ibufpp;                /* remove delimiter */
+                first = false; break;
+      case '\'':if( !first ) { invalid_address(); return -1; };
+                first = false; ++*ibufpp;
+                second_addr = get_marked_node_addr( *(*ibufpp)++ );
+                if( second_addr < 0 ) return -1;
                 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;
+                  if( first_addr < 0 )
+                    { first_addr = ( ( ch == ';' ) ? current_addr() : 1 );
+                      second_addr = last_addr(); }
+                  }
+                else
+                  {
+                  if( second_addr < 0 || second_addr > last_addr() )
+                    { invalid_address(); return -1; }
+                  if( ch == ';' ) set_current_addr( second_addr );
+                  first_addr = second_addr; first = true;
+                  }
+                ++*ibufpp;
+                break;
+      default :
+        if( !first && ( second_addr < 0 || second_addr > last_addr() ) )
+          { invalid_address(); return -1; }
+        {
+        int addr_cnt = 0;                      /* limited to 2 */
+        if( second_addr >= 0 ) addr_cnt = ( first_addr >= 0 ) ? 2 : 1;
+        if( addr_cnt <= 0 ) second_addr = current_addr();
+        if( addr_cnt <= 1 ) first_addr = second_addr;
+        return addr_cnt;
+        }
       }
-    first = false;
-    }
-  }
-
-
-/* get line addresses from the command buffer until an invalid address
-   is seen. Returns the 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 );
   }
 
 
@@ -284,7 +267,7 @@ 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 );
+  int addr_cnt = extract_addresses( ibufpp );
 
   if( addr_cnt < 0 ) return false;
   if( traditional() && addr_cnt == 0 )
@@ -297,41 +280,56 @@ static bool get_third_addr( const char ** const ibufpp, int * const addr )
   }
 
 
-/* return true if address range is valid */
+/* set default range and 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( 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 )
+/* set defaults to current_addr and return true if address range is valid */
+static bool check_addr_range2( const int addr_cnt )
   {
   return check_addr_range( current_addr(), current_addr(), addr_cnt );
   }
 
+/* set default second_addr and return true if second_addr is valid */
+static bool check_second_addr( const int addr, const int addr_cnt )
+  {
+  if( addr_cnt == 0 ) second_addr = addr;
+  if( second_addr < 1 || second_addr > last_addr() )
+    { invalid_address(); return false; }
+  return true;
+  }
+
 
-/* verify the command suffix in the command buffer */
+/* verify the command suffixes in the command buffer */
 static bool get_command_suffix( const char ** const ibufpp,
-                                int * const gflagsp )
+                                int * const pflagsp, int * const snump )
   {
+  bool nos_or_rep = !snump;    /* not s command or repeated g/count */
+  bool error = false;
   while( true )
     {
-    const char ch = **ibufpp;
-    if( ch == 'l' ) *gflagsp |= GLS;
-    else if( ch == 'n' ) *gflagsp |= GNP;
-    else if( ch == 'p' ) *gflagsp |= GPR;
+    const unsigned char ch = **ibufpp;
+    if( ch >= '1' && ch <= '9' )
+      {
+      int n = 0;
+      if( nos_or_rep || !parse_int( &n, *ibufpp, ibufpp ) || n <= 0 )
+        { error = true; break; }
+      nos_or_rep = true; *snump = n; continue;
+      }
+    else if( ch == 'g' )
+      { if( nos_or_rep ) break; else { nos_or_rep = true; *snump = 0; } }
+    else if( ch == 'l' ) { if( *pflagsp & GLS ) break; else *pflagsp |= GLS; }
+    else if( ch == 'n' ) { if( *pflagsp & GNP ) break; else *pflagsp |= GNP; }
+    else if( ch == 'p' ) { if( *pflagsp & GPR ) break; else *pflagsp |= GPR; }
     else break;
     ++*ibufpp;
     }
-  if( *(*ibufpp)++ != '\n' )
+  if( error || *(*ibufpp)++ != '\n' )
     { set_error_msg( "Invalid command suffix" ); return false; }
   return true;
   }
@@ -351,119 +349,121 @@ static bool unexpected_command_suffix( const unsigned char ch )
   }
 
 
-static bool command_s( const char ** const ibufpp, int * const gflagsp,
+static bool command_s( const char ** const ibufpp, int * const pflagsp,
                        const int addr_cnt, const bool isglobal )
   {
-  static int gflags = 0;
-  static int snum = 0;
+  static int pflags = 0;       /* print suffixes */
+  static int gmask = GPR;      /* the print suffixes to be toggled */
+  static int snum = 1; /* > 0 count, <= 0 global substitute */
   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;
+    SGR = 0x04,                /* use regex of last search (if newer) */
+    SGF = 0x08
+    } sflags = 0;      /* if sflags != 0, repeat last substitution */
 
+  if( !check_addr_range2( addr_cnt ) ) return false;
   do {
-    if( isdigit( (unsigned char)**ibufpp ) )
+    bool error = false;
+    if( **ibufpp >= '1' && **ibufpp <= '9' )
       {
-      if( !parse_int( &snum, *ibufpp, ibufpp ) ) return false;
-      sflags |= SGF; gflags &= ~GSG;           /* override GSG */
+      int n = 0;
+      if( ( sflags & SGG ) || !parse_int( &n, *ibufpp, ibufpp ) || n <= 0 )
+        error = true;
+      else
+        { sflags |= SGG; snum = n; }
       }
     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; }
+      case 'g': if( sflags & SGG ) error = true;
+                else { sflags |= SGG; snum = !snum; ++*ibufpp; }
+                break;
+      case 'p': if( sflags & SGP ) error = true;
+                else { sflags |= SGP; ++*ibufpp; } break;
+      case 'r': if( sflags & SGR ) error = true;
+                else { sflags |= SGR; ++*ibufpp; } break;
+      default : if( sflags ) error = true;
       }
+    if( error ) { set_error_msg( "Invalid command suffix" ); return false; }
     }
   while( sflags && **ibufpp != '\n' );
-  if( sflags && !prev_pattern() )
+  if( sflags && !subst_regex() )
     { 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 ) )
+  if( ( !sflags || ( sflags & SGR ) ) && !set_subst_regex( ibufpp ) )
     return false;
-  if( isglobal ) gflags |= GLB;
-  else gflags &= ~GLB;
-  if( sflags & SGG ) gflags ^= GSG;
-  if( sflags & SGP ) { gflags ^= GPR; gflags &= ~( GLS | GNP ); }
-  switch( **ibufpp )
+  if( !sflags )
     {
-    case 'l': gflags |= GLS; ++*ibufpp; break;
-    case 'n': gflags |= GNP; ++*ibufpp; break;
-    case 'p': gflags |= GPR; ++*ibufpp; break;
+    const char delimiter = **ibufpp;
+    pflags = 0; snum = 1;
+    if( !extract_replacement( ibufpp, isglobal ) ) return false;
+    if( **ibufpp == '\n' ) pflags = GPR;       /* omitted last delimiter */
+    else if( **ibufpp == delimiter ) ++*ibufpp;                /* skip delimiter */
+    if( !get_command_suffix( ibufpp, &pflags, &snum ) ) return false;
+    gmask = pflags & ( GPR | GLS | GNP ); if( gmask == 0 ) gmask = GPR;
     }
-  if( !check_current_addr( addr_cnt ) ||
-      !get_command_suffix( ibufpp, gflagsp ) ) return false;
+  else if( sflags & SGP ) pflags ^= gmask;
+  *pflagsp = pflags;
   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 ) )
+  if( !search_and_replace( first_addr, second_addr, snum, isglobal ) )
     return false;
   return true;
   }
 
 
-static bool exec_global( const char ** const ibufpp, const int gflags,
+static bool exec_global( const char ** const ibufpp, const int pflags,
                          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;
+  const char * fnp;                            /* filename */
+  int pflags = 0;                              /* print suffixes */
   int addr, c, n;
-  const int addr_cnt = extract_addr_range( ibufpp );
+  const int addr_cnt = extract_addresses( 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;
+    case 'a': if( !get_command_suffix( ibufpp, &pflags, 0 ) ) return ERR;
               if( !isglobal ) clear_undo_stack();
-              if( !append_lines( ibufpp, second_addr, isglobal ) ) return ERR;
+              if( !append_lines( ibufpp, second_addr, false, 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( !check_addr_range2( addr_cnt ) ||
+                  !get_command_suffix( ibufpp, &pflags, 0 ) ) return ERR;
               if( !isglobal ) clear_undo_stack();
               if( !delete_lines( first_addr, second_addr, isglobal ) ||
-                  !append_lines( ibufpp, current_addr(), isglobal ) ) return ERR;
+                  !append_lines( ibufpp, current_addr(),
+                                 current_addr() >= first_addr, isglobal ) )
+                return ERR;
               break;
-    case 'd': if( !check_current_addr( addr_cnt ) ||
-                  !get_command_suffix( ibufpp, &gflags ) ) return ERR;
+    case 'd': if( !check_addr_range2( addr_cnt ) ||
+                  !get_command_suffix( ibufpp, &pflags, 0 ) ) 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( modified() && prev_status != EMOD ) return EMOD;
+              /* fall through */
     case 'E': if( unexpected_address( addr_cnt ) ||
                   unexpected_command_suffix( **ibufpp ) ) return ERR;
-              fnp = get_filename( ibufpp );
+              fnp = get_filename( ibufpp, false );
               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 );
+              fnp = get_filename( ibufpp, traditional() );
               if( !fnp ) return ERR;
               if( fnp[0] == '!' )
                 { set_error_msg( "Invalid redirection" ); return ERR; }
@@ -480,49 +480,48 @@ static int exec_command( const char ** const ibufpp, const int prev_status,
                   !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 ) )
+              if( ( n && !get_command_suffix( ibufpp, &pflags, 0 ) ) ||
+                  !exec_global( ibufpp, pflags, n ) )
                 return ERR;
               break;
     case 'h':
     case 'H': if( unexpected_address( addr_cnt ) ||
-                  !get_command_suffix( ibufpp, &gflags ) ) return ERR;
+                  !get_command_suffix( ibufpp, &pflags, 0 ) ) return ERR;
               if( c == 'H' ) verbose = !verbose;
               if( ( c == 'h' || verbose ) && errmsg[0] )
-                fprintf( stderr, "%s\n", errmsg );
+                printf( "%s\n", errmsg );
               break;
-    case 'i': if( second_addr == 0 ) second_addr = 1;
-              if( !get_command_suffix( ibufpp, &gflags ) ) return ERR;
+    case 'i': if( !get_command_suffix( ibufpp, &pflags, 0 ) ) return ERR;
               if( !isglobal ) clear_undo_stack();
-              if( !append_lines( ibufpp, second_addr - 1, isglobal ) )
+              if( !append_lines( ibufpp, second_addr, true, isglobal ) )
                 return ERR;
               break;
     case 'j': if( !check_addr_range( current_addr(), current_addr() + 1, addr_cnt ) ||
-                  !get_command_suffix( ibufpp, &gflags ) ) return ERR;
+                  !get_command_suffix( ibufpp, &pflags, 0 ) ) return ERR;
               if( !isglobal ) clear_undo_stack();
-              if( first_addr != second_addr &&
+              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 ) ||
+              if( !get_command_suffix( ibufpp, &pflags, 0 ) ||
                   !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 ) )
+              if( !check_addr_range2( addr_cnt ) ||
+                  !get_command_suffix( ibufpp, &pflags, 0 ) ||
+                  !print_lines( first_addr, second_addr, pflags | n ) )
                 return ERR;
-              gflags = 0;
+              pflags = 0;
               break;
-    case 'm': if( !check_current_addr( addr_cnt ) ||
+    case 'm': if( !check_addr_range2( 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( !get_command_suffix( ibufpp, &pflags, 0 ) ) return ERR;
               if( !isglobal ) clear_undo_stack();
               if( !move_lines( first_addr, second_addr, addr, isglobal ) )
                 return ERR;
@@ -530,83 +529,77 @@ static int exec_command( const char ** const ibufpp, const int prev_status,
     case 'P':
     case 'q':
     case 'Q': if( unexpected_address( addr_cnt ) ||
-                  !get_command_suffix( ibufpp, &gflags ) ) return ERR;
+                  !get_command_suffix( ibufpp, &pflags, 0 ) ) return ERR;
               if( c == 'P' ) prompt_on = !prompt_on;
-              else if( modified() && !scripted() && c == 'q' &&
-                       prev_status != EMOD ) return EMOD;
+              else if( c == 'q' && modified() && 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 );
+              fnp = get_filename( ibufpp, false );
               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; }
+              if( !isglobal ) clear_undo_stack();
               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 ) )
+    case 's': if( !command_s( ibufpp, &pflags, addr_cnt, isglobal ) )
                 return ERR;
               break;
-    case 't': if( !check_current_addr( addr_cnt ) ||
+    case 't': if( !check_addr_range2( addr_cnt ) ||
                   !get_third_addr( ibufpp, &addr ) ||
-                  !get_command_suffix( ibufpp, &gflags ) ) return ERR;
+                  !get_command_suffix( ibufpp, &pflags, 0 ) ) 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 ) ||
+                  !get_command_suffix( ibufpp, &pflags, 0 ) ||
                   !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 );
+              fnp = get_filename( ibufpp, false );
               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( addr == last_addr() && fnp[0] != '!' ) set_modified( false );
+              else if( n == 'q' && modified() && prev_status != EMOD )
+                return EMOD;
               if( n == 'q' || n == 'Q' ) return QUIT;
               break;
-    case 'x': if( second_addr < 0 || last_addr() < second_addr )
+    case 'x': if( second_addr < 0 || second_addr > last_addr() )
                 { invalid_address(); return ERR; }
-              if( !get_command_suffix( ibufpp, &gflags ) ) return ERR;
+              if( !get_command_suffix( ibufpp, &pflags, 0 ) ) 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 ) ||
+    case 'y': if( !check_addr_range2( addr_cnt ) ||
+                  !get_command_suffix( ibufpp, &pflags, 0 ) ||
                   !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 ) )
+    case 'z': if( !check_second_addr( current_addr() + !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,
+              if( !get_command_suffix( ibufpp, &pflags, 0 ) ||
+                  !print_lines( second_addr,
                     min( last_addr(), second_addr + window_lines() - 1 ),
-                    gflags ) )
+                    pflags ) )
                 return ERR;
-              gflags = 0;
+              pflags = 0;
               break;
-    case '=': if( !get_command_suffix( ibufpp, &gflags ) ) return ERR;
+    case '=': if( !get_command_suffix( ibufpp, &pflags, 0 ) ) return ERR;
               printf( "%d\n", addr_cnt ? second_addr : last_addr() );
               break;
     case '!': if( unexpected_address( addr_cnt ) ) return ERR;
@@ -616,17 +609,16 @@ static int exec_command( const char ** const ibufpp, const int prev_status,
                 { set_error_msg( "Can't create shell process" ); return ERR; }
               if( !scripted() ) fputs( "!\n", stdout );
               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 ) )
+    case '\n': if( !check_second_addr( current_addr() +
+                     ( traditional() || !isglobal ), addr_cnt ) ||
+                   !print_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 ) )
+  if( pflags && !print_lines( current_addr(), current_addr(), pflags ) )
     return ERR;
   return 0;
   }
@@ -634,7 +626,7 @@ static int exec_command( const char ** const ibufpp, const int prev_status,
 
 /* 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,
+static bool exec_global( const char ** const ibufpp, const int pflags,
                          const bool interactive )
   {
   static char * buf = 0;
@@ -643,7 +635,7 @@ static bool exec_global( const char ** const ibufpp, const int gflags,
 
   if( !interactive )
     {
-    if( traditional() && !strcmp( *ibufpp, "\n" ) )
+    if( traditional() && strcmp( *ibufpp, "\n" ) == 0 )
       cmd = "p\n";                     /* null cmd_list == 'p' */
     else
       {
@@ -661,16 +653,14 @@ static bool exec_global( const char ** const ibufpp, const int gflags,
     if( interactive )
       {
       /* print current_addr; get a command in global syntax */
-      int len;
-      if( !display_lines( current_addr(), current_addr(), gflags ) )
+      int len = 0;
+      if( !print_lines( current_addr(), current_addr(), pflags ) )
         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" ) )
+      *ibufpp = get_stdin_line( &len );
+      if( !*ibufpp ) return false;                     /* error */
+      if( len <= 0 ) return false;                     /* EOF */
+      if( len == 1 && strcmp( *ibufpp, "\n" ) == 0 ) continue;
+      if( len == 2 && strcmp( *ibufpp, "&\n" ) == 0 )
         { if( !cmd ) { set_error_msg( "No previous command" ); return false; } }
       else
         {
@@ -687,76 +677,46 @@ static bool exec_global( const char ** const ibufpp, const int gflags,
   }
 
 
+static void script_error( void )
+  {
+  if( verbose ) fprintf( stderr, "script, line %d: %s\n", linenum(), errmsg );
+  }
+
+
 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;
+  int len = 0, status;
 
   disable_interrupts();
   set_signals();
   status = setjmp( jmp_state );
   if( !status ) enable_interrupts();
-  else { status = -1; fputs( "\n?\n", stderr ); set_error_msg( "Interrupt" ); }
+  else { status = -1; fputs( "\n?\n", stdout ); set_error_msg( "Interrupt" ); }
 
   while( true )
     {
-    fflush( stdout );
-    if( status < 0 && verbose )
-      { fprintf( stderr, "%s\n", errmsg ); fflush( stderr ); }
+    fflush( stdout ); fflush( stderr );
+    if( status < 0 && verbose ) { printf( "%s\n", errmsg ); fflush( stdout ); }
     if( prompt_on ) { fputs( prompt_str, stdout ); fflush( stdout ); }
-    ibufp = get_tty_line( &len );
-    if( !ibufp ) return err_status;
-    if( !len )
+    ibufp = get_stdin_line( &len );
+    if( !ibufp ) return 2;                     /* an error happened */
+    if( len <= 0 )                             /* EOF on stdin ('q') */
       {
-      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;
+      if( !modified() || status == EMOD ) status = QUIT;
+      else { status = EMOD; if( !loose ) err_status = 2; }
       }
-    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 );
+    else 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;
+    fputs( "?\n", stdout );                    /* give warning */
+    if( !loose && err_status == 0 ) err_status = 1;
+    if( status == EMOD ) set_error_msg( "Warning: buffer modified" );
+    if( is_regular_file( 0 ) )
+      { script_error(); return ( ( status == FATAL ) ? 1 : err_status ); }
+    if( status == FATAL )
+      { if( verbose ) { printf( "%s\n", errmsg ); } return 1; }
     }
   }
diff --git a/regex.c b/regex.c
index 2ca49ce..15e3323 100644 (file)
--- a/regex.c
+++ b/regex.c
@@ -1,7 +1,7 @@
 /* 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-2016 Antonio Diaz Diaz.
+    Copyright (C) 2006-2017 Antonio Diaz Diaz.
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #include "ed.h"
 
 
-static regex_t * global_pat = 0;
-static bool patlock = false;   /* if set, pattern not freed by get_compiled_pattern */
+static regex_t * subst_regex_ = 0;     /* regex of previous substitution */
 
-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;                /* replacement buffer */
+static int rbufsz = 0;         /* replacement buffer size */
+static int rlen = 0;           /* replacement 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; }
+bool subst_regex( void ) { return subst_regex_ != 0; }
 
 
 /* translate characters in a string */
@@ -107,62 +103,73 @@ static char * extract_pattern( const char ** const ibufpp, const char delimiter
   }
 
 
-/* return pointer to compiled pattern from command buffer */
-static regex_t * get_compiled_pattern( const char ** const ibufpp )
+/* return pointer to compiled regex from command buffer, or to previous
+   compiled regex if empty RE. return 0 if error */
+static regex_t * get_compiled_regex( const char ** const ibufpp,
+                                     const bool test_delimiter )
   {
+  static regex_t store[2];             /* space for two compiled regexes */
   static regex_t * exp = 0;
-  const char * exps;
+  const char * pat;
   const char delimiter = **ibufpp;
   int n;
 
   if( delimiter == ' ' )
     { set_error_msg( "Invalid pattern delimiter" ); return 0; }
-  if( delimiter == '\n' || *++*ibufpp == '\n' || **ibufpp == delimiter )
+  if( delimiter == '\n' || *++*ibufpp == delimiter ||
+      ( **ibufpp == '\n' && !test_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 );
+  pat = extract_pattern( ibufpp, delimiter );
+  if( !pat ) return 0;
+  if( test_delimiter && delimiter != **ibufpp )
+    { set_error_msg( "Missing pattern delimiter" ); return 0; }
+  /* exp compiled && not copied */
+  if( exp && exp != subst_regex_ ) regfree( exp );
+  else exp = ( &store[0] != subst_regex_ ) ? &store[0] : &store[1];
+  n = regcomp( exp, pat, 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 set_subst_regex( const char ** const ibufpp )
+  {
+  regex_t * exp;
+
+  disable_interrupts();
+  exp = get_compiled_regex( ibufpp, true );
+  if( exp && exp != subst_regex_ )
+    {
+    if( subst_regex_ ) regfree( subst_regex_ );
+    subst_regex_ = exp;
+    }
+  enable_interrupts();
+  return ( exp ? true : false );
+  }
+
+
+/* add line matching a regular expression 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 regex_t * exp;
   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;
+  exp = get_compiled_regex( ibufpp, false );
+  if( !exp ) return false;
   if( **ibufpp == delimiter ) ++*ibufpp;
   clear_active_list();
   lp = search_line_node( first_addr );
@@ -171,73 +178,21 @@ bool build_active_list( const char ** const ibufpp, const int first_addr,
     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 ) )
+    if( match == !regexec( exp, s, 0, 0, 0 ) && !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 */
+/* return the address of the next line matching a regular expression 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 );
+  const regex_t * const exp = get_compiled_regex( ibufpp, false );
   int addr = current_addr();
 
-  if( !pat ) return -1;
+  if( !exp ) return -1;
   do {
     addr = ( forward ? inc_addr( addr ) : dec_addr( addr ) );
     if( addr )
@@ -246,7 +201,7 @@ int next_matching_node_addr( const char ** const ibufpp, const bool forward )
       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;
+      if( !regexec( exp, s, 0, 0, 0 ) ) return addr;
       }
     }
   while( addr != current_addr() );
@@ -255,134 +210,170 @@ int next_matching_node_addr( const char ** const ibufpp, const bool forward )
   }
 
 
-bool new_compiled_pattern( const char ** const ibufpp )
+/* extract substitution replacement from the command buffer */
+bool extract_replacement( const char ** const ibufpp, const bool isglobal )
   {
-  regex_t * tpat;
+  static char * buf = 0;               /* temporary buffer */
+  static int bufsz = 0;
+  int i = 0;
+  const char delimiter = **ibufpp;
 
-  disable_interrupts();
-  tpat = get_compiled_pattern( ibufpp );
-  if( tpat && tpat != global_pat )
+  if( delimiter == '\n' )
+    { set_error_msg( "Missing pattern delimiter" ); return false; }
+  ++*ibufpp;
+  if( **ibufpp == '%' && ( (*ibufpp)[1] == delimiter || (*ibufpp)[1] == '\n' ) )
+    {
+    ++*ibufpp;
+    if( !rbuf ) { set_error_msg( "No previous substitution" ); return false; }
+    return true;
+    }
+  while( **ibufpp != delimiter )
     {
-    if( global_pat ) { regfree( global_pat ); free( global_pat ); }
-    global_pat = tpat;
-    patlock = true;            /* reserve pattern */
+    if( **ibufpp == '\n' )
+      {
+      if( isglobal && (*ibufpp)[1] != 0 )
+        { set_error_msg( "Invalid newline substitution" ); return false; }
+      break;
+      }
+    if( !resize_buffer( &buf, &bufsz, i + 2 ) ) return false;
+    if( ( buf[i++] = *(*ibufpp)++ ) == '\\' &&
+        ( buf[i++] = *(*ibufpp)++ ) == '\n' && !isglobal )
+      {
+      /* not reached if isglobal; in command-list, newlines are unescaped */
+      int size = 0;
+      *ibufpp = get_stdin_line( &size );
+      if( !*ibufpp ) return false;                     /* error */
+      if( size <= 0 ) return false;                    /* EOF */
+      }
     }
+  /* make sure that buf gets allocated if empty replacement */
+  if( !resize_buffer( &buf, &bufsz, i + 1 ) ) return false;
+  buf[i] = 0;
+  disable_interrupts();
+  { char * p = buf; buf = rbuf; rbuf = p;              /* swap buffers */
+    rlen = i; i = bufsz; bufsz = rbufsz; rbufsz = i; }
   enable_interrupts();
-  return ( tpat ? true : false );
+  return true;
   }
 
 
-/* modify text according to a substitution template; return offset to
-   end of modified text */
-static int apply_subst_template( const char * const boln,
+/* Produce replacement text from matched text and replacement template.
+   Return new offset to end of replacement text, or -1 if error. */
+static int replace_matched_text( char ** txtbufp, int * const txtbufszp,
+                                 const char * const txt,
                                  const regmatch_t * const rm, int offset,
                                  const int re_nsub )
   {
-  const char * sub = stbuf;
+  const char * sub = rbuf;
 
-  for( ; sub - stbuf < stlen; ++sub )
+  for( ; sub - rbuf < rlen; ++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++];
+      if( !resize_buffer( txtbufp, txtbufszp, offset + k - j ) ) return -1;
+      while( j < k ) (*txtbufp)[offset++] = txt[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++];
+      if( !resize_buffer( txtbufp, txtbufszp, offset + k - j ) ) return -1;
+      while( j < k ) (*txtbufp)[offset++] = txt[j++];
       }
     else
       {
-      if( !resize_buffer( &rbuf, &rbufsz, offset + 1 ) ) return -1;
-      rbuf[offset++] = *sub;
+      if( !resize_buffer( txtbufp, txtbufszp, offset + 1 ) ) return -1;
+      (*txtbufp)[offset++] = *sub;
       }
     }
-  if( !resize_buffer( &rbuf, &rbufsz, offset + 1 ) ) return -1;
-  rbuf[offset] = 0;
+  if( !resize_buffer( txtbufp, txtbufszp, offset + 1 ) ) return -1;
+  (*txtbufp)[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 )
+/* Produce new text with one or all matches replaced in a line.
+   Return size of the new line text, 0 if no change, -1 if error */
+static int line_replace( char ** txtbufp, int * const txtbufszp,
+                         const line_t * const lp, 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;
+  const bool global = ( snum <= 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 ) )
+  if( !regexec( subst_regex_, txt, se_max, rm, 0 ) )
     {
     int matchno = 0;
     do {
-      if( !snum || snum == ++matchno )
+      if( global || snum == ++matchno )
         {
         changed = true; i = rm[0].rm_so;
-        if( !resize_buffer( &rbuf, &rbufsz, offset + i ) ) return -1;
+        if( !resize_buffer( txtbufp, txtbufszp, 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 );
+        memcpy( *txtbufp + offset, txt, i ); offset += i;
+        offset = replace_matched_text( txtbufp, txtbufszp, txt, rm, offset,
+                                       subst_regex_->re_nsub );
         if( offset < 0 ) return -1;
         }
       else
         {
         i = rm[0].rm_eo;
-        if( !resize_buffer( &rbuf, &rbufsz, offset + i ) ) return -1;
+        if( !resize_buffer( txtbufp, txtbufszp, offset + i ) ) return -1;
         if( isbinary() ) newline_to_nul( txt, i );
-        memcpy( rbuf + offset, txt, i ); offset += i;
+        memcpy( *txtbufp + 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 ) );
+    while( *txt && ( !changed || ( global && rm[0].rm_eo ) ) &&
+           !regexec( subst_regex_, 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 ) )
+    if( !resize_buffer( txtbufp, txtbufszp, offset + i + 2 ) ) return -1;
+    if( global && i > 0 && !rm[0].rm_eo )
       { 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 );
+    memcpy( *txtbufp + offset, txt, i );               /* tail copy */
+    memcpy( *txtbufp + 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 */
+/* for each line in a range, change text matching a regular expression
+   according to a substitution template (replacement); 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 )
+                         const int snum, const bool isglobal )
   {
+  static char * txtbuf = 0;            /* new text of line buffer */
+  static int txtbufsz = 0;             /* new text of line buffer size */
+  int addr = first_addr;
   int lc;
   bool match_found = false;
 
-  set_current_addr( first_addr - 1 );
-  for( lc = 0; lc <= second_addr - first_addr; ++lc )
+  for( lc = 0; lc <= second_addr - first_addr; ++lc, ++addr )
     {
-    const line_t * const lp = search_line_node( inc_current_addr() );
-    const int size = replace_matching_text( lp, gflags, snum );
+    const line_t * const lp = search_line_node( addr );
+    const int size = line_replace( &txtbuf, &txtbufsz, lp, snum );
     if( size < 0 ) return false;
     if( size )
       {
-      const char * txt = rbuf;
-      const char * const eot = rbuf + size;
+      const char * txt = txtbuf;
+      const char * const eot = txtbuf + size;
       undo_t * up = 0;
       disable_interrupts();
-      if( !delete_lines( current_addr(), current_addr(), isglobal ) )
+      if( !delete_lines( addr, addr, isglobal ) )
         { enable_interrupts(); return false; }
+      set_current_addr( addr - 1 );
       do {
-        txt = put_sbuf_line( txt, size, current_addr() );
+        txt = put_sbuf_line( txt, eot - txt );
         if( !txt ) { enable_interrupts(); return false; }
         if( up ) up->tail = search_line_node( current_addr() );
         else
@@ -393,10 +384,11 @@ bool search_and_replace( const int first_addr, const int second_addr,
         }
       while( txt != eot );
       enable_interrupts();
+      addr = current_addr();
       match_found = true;
       }
     }
-  if( !match_found && !( gflags & GLB ) )
+  if( !match_found && !isglobal )
     { set_error_msg( "No match" ); return false; }
   return true;
   }
index 63ba1cc..6d7fcdb 100644 (file)
--- a/signal.c
+++ b/signal.c
@@ -1,7 +1,7 @@
 /* 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-2016 Antonio Diaz Diaz.
+    Copyright (C) 2006-2017 Antonio Diaz Diaz.
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -161,7 +161,7 @@ bool parse_int( int * const i, const char * const str, const char ** const tail
     *i = 0;
     return false;
     }
-  if( errno == ERANGE || li > INT_MAX || li < INT_MIN )
+  if( errno == ERANGE || li > INT_MAX || li < -INT_MAX )
     {
     set_error_msg( "Numerical result out of range" );
     *i = 0;
diff --git a/testsuite/a.d b/testsuite/a.d
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
diff --git a/testsuite/a.ed b/testsuite/a.ed
new file mode 100644 (file)
index 0000000..314edc3
--- /dev/null
@@ -0,0 +1,37 @@
+H
+# empty append at address 0 should set the current address to 0
+0a
+.
++4a
+# this is not a comment
+.
+# empty append at current address should not modify the current address
+a
+.
+a
+hello world!
+.
+a
+hello world!!
+.
+0a
+hello world!!!
+.
+!read one      # shell escape should not modify the current address
+text for the read shell command above
+a
+shell escape marker
+.
+$a
+hello world!!!!
+.
+u
+u
+a
+hello world!!!!!
+.
+a
+to be undone
+.
+u
+w out.o
index ec4b00b..7d6029a 100644 (file)
@@ -1,3 +1,5 @@
-aa
+H
+ag
 hello world
 .
+w out.ro
diff --git a/testsuite/a.pr b/testsuite/a.pr
deleted file mode 100644 (file)
index ec4b00b..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-aa
-hello world
-.
index 26257bd..9434d34 100644 (file)
@@ -1,8 +1,20 @@
-hello world
-line 1
+hello world!!!
+shell escape marker
+This natural inequality of the two powers of population and of
+production in the earth, and that great law of our nature which must
+constantly keep their effects equal, form the great difficulty that to
+me appears insurmountable in the way to the perfectibility of society.
+# this is not a comment
 hello world!
-line 2
-line 3
-line 4
-line5
 hello world!!
+All other arguments are of slight and subordinate consideration in
+comparison of this. I see no way by which man can escape from the weight
+of this law which pervades all animated nature. No fancied equality, no
+agrarian regulations in their utmost extent, could remove the pressure
+of it even for a single century. And it appears, therefore, to be
+decisive against the possible existence of a society, all the members of
+which should live in ease, happiness, and comparative leisure; and feel
+no anxiety about providing the means of subsistence for themselves and
+their families.
+hello world!!!!
+hello world!!!!!
diff --git a/testsuite/a.t b/testsuite/a.t
deleted file mode 100644 (file)
index ac98c40..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-0a
-hello world
-.
-2a
-hello world!
-.
-$a
-hello world!!
-.
diff --git a/testsuite/addr.d b/testsuite/addr.d
deleted file mode 100644 (file)
index 8f7ba1b..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
-1ine6
-line7
-line8
-line9
diff --git a/testsuite/addr.r b/testsuite/addr.r
deleted file mode 100644 (file)
index 04caf17..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-line 2
-line9
diff --git a/testsuite/addr.t b/testsuite/addr.t
deleted file mode 100644 (file)
index 750b224..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-1 d
-1 1 d
-1,2,d
-1;+ + ,d
-1,2;., + 2d
index 29d6383..7b40b33 100644 (file)
@@ -1 +1,3 @@
+H
 100
+w out.ro
diff --git a/testsuite/addr1.pr b/testsuite/addr1.pr
deleted file mode 100644 (file)
index 29d6383..0000000
+++ /dev/null
@@ -1 +0,0 @@
-100
index e96acb9..da4ffa9 100644 (file)
@@ -1 +1,3 @@
+H
 -100
+w out.ro
diff --git a/testsuite/addr2.pr b/testsuite/addr2.pr
deleted file mode 100644 (file)
index e96acb9..0000000
+++ /dev/null
@@ -1 +0,0 @@
--100
diff --git a/testsuite/addr3.err b/testsuite/addr3.err
new file mode 100644 (file)
index 0000000..e5a7fb7
--- /dev/null
@@ -0,0 +1,3 @@
+H
+0p
+w out.ro
diff --git a/testsuite/addr4.err b/testsuite/addr4.err
new file mode 100644 (file)
index 0000000..7f4f4ac
--- /dev/null
@@ -0,0 +1,3 @@
+H
+2,1p
+w out.ro
diff --git a/testsuite/ascii.t b/testsuite/ascii.t
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/testsuite/bang.d b/testsuite/bang.d
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
diff --git a/testsuite/bang.r b/testsuite/bang.r
deleted file mode 100644 (file)
index 237f034..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-line 1
-line 2
-line 3
-okay
-line 4
-line5
diff --git a/testsuite/bang.t b/testsuite/bang.t
deleted file mode 100644 (file)
index 1e6152e..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-3p
-!read one
-hello, world
-a
-okay
-.
index 630af90..afce43f 100644 (file)
@@ -1 +1,3 @@
+H
 .!date
+w out.ro
diff --git a/testsuite/bang1.pr b/testsuite/bang1.pr
deleted file mode 100644 (file)
index 630af90..0000000
+++ /dev/null
@@ -1 +0,0 @@
-.!date
index 79d8956..3493119 100644 (file)
@@ -1 +1,3 @@
+H
 !!
+w out.ro
diff --git a/testsuite/bang2.pr b/testsuite/bang2.pr
deleted file mode 100644 (file)
index 79d8956..0000000
+++ /dev/null
@@ -1 +0,0 @@
-!!
diff --git a/testsuite/c.d b/testsuite/c.d
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
diff --git a/testsuite/c.ed b/testsuite/c.ed
new file mode 100644 (file)
index 0000000..98b8a3b
--- /dev/null
@@ -0,0 +1,25 @@
+H
+0c
+at the top
+.
+4c
+in the middle
+.
+5c
+.
+c
+after the middle
+.
+$c
+at the bottom
+.
+u
+u
+-5,10c
+between middle/bottom
+.
+c
+to be undone
+.
+u
+w out.o
index 658ec38..2775d49 100644 (file)
@@ -1,3 +1,5 @@
-cc
+H
+c0
 hello world
 .
+w out.ro
diff --git a/testsuite/c.pr b/testsuite/c.pr
deleted file mode 100644 (file)
index 658ec38..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-cc
-hello world
-.
index 0fb3e4f..4fc5e90 100644 (file)
@@ -1,4 +1,9 @@
 at the top
-between top/middle
+production in the earth, and that great law of our nature which must
+constantly keep their effects equal, form the great difficulty that to
 in the middle
+after the middle
+of this law which pervades all animated nature. No fancied equality, no
+between middle/bottom
+no anxiety about providing the means of subsistence for themselves and
 at the bottom
diff --git a/testsuite/c.t b/testsuite/c.t
deleted file mode 100644 (file)
index 5fba319..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-0c
-at the top
-.
-4c
-in the middle
-.
-$c
-at the bottom
-.
-2,3c
-between top/middle
-.
index 81ee128..1be15e1 100755 (executable)
@@ -1,6 +1,6 @@
 #! /bin/sh
 # check script for GNU ed - The GNU line editor
-# Copyright (C) 2006-2016 Antonio Diaz Diaz.
+# Copyright (C) 2006-2017 Antonio Diaz Diaz.
 #
 # This script is free software; you have unlimited permission
 # to copy, distribute and modify it.
@@ -10,6 +10,7 @@ export LC_ALL
 objdir=`pwd`
 testdir=`cd "$1" ; pwd`
 ED="${objdir}"/ed
+framework_failure() { echo "failure in testing framework" ; exit 1 ; }
 
 if [ ! -f "${ED}" ] || [ ! -x "${ED}" ] ; then
        echo "${ED}: cannot execute"
@@ -18,88 +19,84 @@ fi
 
 if [ -d tmp ] ; then rm -rf tmp ; fi
 mkdir tmp
+cd "${objdir}"/tmp || framework_failure
 
-# 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 H
-       echo "r ${testdir}/${base}.d"
-       cat "$i"
-       echo "w ${base}.o"
-       ) > "${objdir}/tmp/${base}.ed"
-done
-
-for i in *.err ; do
-       base=`echo "$i" | sed 's/\.err$//'`
-       (
-       echo H
-       echo "r ${testdir}/${base}.err"
-       cat "$i"
-       echo "w ${base}.ro"
-       ) > "${objdir}/tmp/${base}.red"
-done
-
-
-cd "${objdir}"/tmp
+cat "${testdir}"/test.txt > test.txt || framework_failure
+cat "${testdir}"/test.bin > test.bin || framework_failure
+touch zero || framework_failure
 fail=0
 
 printf "testing ed-%s...\n" "$2"
 
-# Run the .ed and .red scripts just generated
-# and compare their output against the .r and .pr files, which contain
-# the correct output.
-
-# Run the *.red scripts first, since these don't generate output;
-# they exit with non-zero status
-for i in *.red ; do
-       if "${ED}" -s < "$i" > /dev/null 2>&1 ; then
+# Run the .err scripts first with a regular file connected to standard
+# input, since these don't generate output; they exit with non-zero status.
+for i in "${testdir}"/*.err ; do
+       if "${ED}" -s test.txt < "$i" > /dev/null 2>&1 ; then
                echo "*** The script $i exited abnormally ***"
                fail=127
        fi
 done
 
-# 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 > /dev/null 2>&1 ; then
+# Run the .err scripts again with a regular file connected to standard
+# input, but with '--loose-exit-status'; they should exit with zero status.
+for i in "${testdir}"/*.err ; do
+       if "${ED}" -sl test.txt < "$i" > /dev/null 2>&1 ; then
+               true
+       else
+               echo "*** The script $i failed '--loose-exit-status' ***"
+               fail=127
+       fi
+done
+
+# Run the .err scripts again as pipes - these should exit with non-zero
+# status without altering the contents of the buffer; the produced
+# 'out.ro' must be identical to 'test.txt'.
+for i in "${testdir}"/*.err ; do
+       base=`echo "$i" | sed 's,^.*/,,;s,\.err$,,'`    # remove dir and ext
+       if cat "$i" | "${ED}" -s test.txt > /dev/null 2>&1 ; then
                echo "*** The piped script $i exited abnormally ***"
                fail=127
        else
-               if cmp -s ${base}.ro "${testdir}"/${base}.pr ; then
+               if cmp -s out.ro test.txt ; then
                        true
                else
+                       mv -f out.ro ${base}.ro
                        echo "*** Output ${base}.ro of piped script $i is incorrect ***"
                        fail=127
                fi
        fi
+       rm -f out.ro
 done
 
-# Run the remaining scripts; they exit with zero status
-for i in *.ed ; do
-       base=`echo "$i" | sed 's/\.ed$//'`
-       if "${ED}" -s < ${base}.ed > /dev/null 2>&1 ; then
-               if cmp -s ${base}.o "${testdir}"/${base}.r ; then
+# Run the .ed scripts and compare their output against the .r files,
+# which contain the correct output.
+# The .ed scripts should exit with zero status.
+for i in "${testdir}"/*.ed ; do
+       base=`echo "$i" | sed 's,^.*/,,;s,\.ed$,,'`     # remove dir and ext
+       if "${ED}" -s test.txt < "$i" > /dev/null 2> out.log ; then
+               if cmp -s out.o "${testdir}"/${base}.r ; then
                        true
                else
+                       mv -f out.o ${base}.o
                        echo "*** Output ${base}.o of script $i is incorrect ***"
                        fail=127
                fi
        else
+               mv -f out.log ${base}.log
                echo "*** The script $i exited abnormally ***"
                fail=127
        fi
+       rm -f out.o out.log
 done
 
+rm -f test.txt test.bin zero
+
 if [ ${fail} = 0 ] ; then
        echo "tests completed successfully."
        cd "${objdir}" && rm -r tmp
 else
        echo "tests failed."
+       echo "Please, send a bug report to bug-ed@gnu.org."
+       echo "Include the (compressed) contents of '${objdir}/tmp' in the report."
 fi
 exit ${fail}
diff --git a/testsuite/comment.d b/testsuite/comment.d
deleted file mode 100644 (file)
index ec1e35a..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-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
deleted file mode 100644 (file)
index ae60d9d..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-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
deleted file mode 100644 (file)
index ab873f8..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# 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
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
diff --git a/testsuite/d.ed b/testsuite/d.ed
new file mode 100644 (file)
index 0000000..0312989
--- /dev/null
@@ -0,0 +1,12 @@
+H
+2 d
+. d
+$ d
+-5 d
+u
+u
++3,+4d
+# to be undone
+1,$d
+u
+w out.o
index f03f694..16cbbba 100644 (file)
@@ -1 +1,3 @@
-dd
+H
+d1
+w out.ro
diff --git a/testsuite/d.pr b/testsuite/d.pr
deleted file mode 100644 (file)
index f03f694..0000000
+++ /dev/null
@@ -1 +0,0 @@
-dd
index b7e242c..59e352f 100644 (file)
@@ -1 +1,7 @@
-line 2
+This natural inequality of the two powers of population and of
+me appears insurmountable in the way to the perfectibility of society.
+All other arguments are of slight and subordinate consideration in
+comparison of this. I see no way by which man can escape from the weight
+agrarian regulations in their utmost extent, could remove the pressure
+of it even for a single century. And it appears, therefore, to be
+decisive against the possible existence of a society, all the members of
diff --git a/testsuite/d.t b/testsuite/d.t
deleted file mode 100644 (file)
index c7c473f..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-1d
-2;+1d
-$d
diff --git a/testsuite/e1.d b/testsuite/e1.d
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
diff --git a/testsuite/e1.ed b/testsuite/e1.ed
new file mode 100644 (file)
index 0000000..61d8f52
--- /dev/null
@@ -0,0 +1,3 @@
+H
+e test.bin
+w out.o
index 827cc29..2b7728c 100644 (file)
@@ -1 +1,3 @@
-ee e1.err
+H
+ee test.bin
+w out.ro
diff --git a/testsuite/e1.pr b/testsuite/e1.pr
deleted file mode 100644 (file)
index 827cc29..0000000
+++ /dev/null
@@ -1 +0,0 @@
-ee e1.err
index 9692412..c866266 100644 (file)
Binary files a/testsuite/e1.r and b/testsuite/e1.r differ
diff --git a/testsuite/e1.t b/testsuite/e1.t
deleted file mode 100644 (file)
index 4a348d5..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-3d
-e e1.ed
-1,2d
diff --git a/testsuite/e2.d b/testsuite/e2.d
deleted file mode 100644 (file)
index aa44630..0000000
+++ /dev/null
@@ -1 +0,0 @@
-E !echo hello world-
diff --git a/testsuite/e2.ed b/testsuite/e2.ed
new file mode 100644 (file)
index 0000000..73b8253
--- /dev/null
@@ -0,0 +1,4 @@
+H
+f test.bin
+e
+w out.o
index 779a64b..06e88ec 100644 (file)
@@ -1 +1,3 @@
-.e e2.err
+H
+.e test.bin
+w out.ro
diff --git a/testsuite/e2.pr b/testsuite/e2.pr
deleted file mode 100644 (file)
index 779a64b..0000000
+++ /dev/null
@@ -1 +0,0 @@
-.e e2.err
index 59ebf11..c866266 100644 (file)
Binary files a/testsuite/e2.r and b/testsuite/e2.r differ
diff --git a/testsuite/e2.t b/testsuite/e2.t
deleted file mode 100644 (file)
index aa44630..0000000
+++ /dev/null
@@ -1 +0,0 @@
-E !echo hello world-
diff --git a/testsuite/e3.d b/testsuite/e3.d
deleted file mode 100644 (file)
index aa44630..0000000
+++ /dev/null
@@ -1 +0,0 @@
-E !echo hello world-
diff --git a/testsuite/e3.ed b/testsuite/e3.ed
new file mode 100644 (file)
index 0000000..36ca4ac
--- /dev/null
@@ -0,0 +1,5 @@
+H
+e test.bin
+1d
+E !cat test.bin
+w out.o
index 80a7fdc..6d36baa 100644 (file)
@@ -1 +1,3 @@
-ee.err
+H
+etest.bin
+w out.ro
diff --git a/testsuite/e3.pr b/testsuite/e3.pr
deleted file mode 100644 (file)
index 80a7fdc..0000000
+++ /dev/null
@@ -1 +0,0 @@
-ee.err
index aa44630..c866266 100644 (file)
Binary files a/testsuite/e3.r and b/testsuite/e3.r differ
diff --git a/testsuite/e3.t b/testsuite/e3.t
deleted file mode 100644 (file)
index 1c50726..0000000
+++ /dev/null
@@ -1 +0,0 @@
-E
diff --git a/testsuite/e4.d b/testsuite/e4.d
deleted file mode 100644 (file)
index aa44630..0000000
+++ /dev/null
@@ -1 +0,0 @@
-E !echo hello world-
diff --git a/testsuite/e4.ed b/testsuite/e4.ed
new file mode 100644 (file)
index 0000000..5ce96b7
--- /dev/null
@@ -0,0 +1,5 @@
+H
+e test.bin
+# modifying the last line of a binary file adds a newline
+$s/x/x/
+w out.o
index aa44630..fb695e1 100644 (file)
Binary files a/testsuite/e4.r and b/testsuite/e4.r differ
diff --git a/testsuite/e4.t b/testsuite/e4.t
deleted file mode 100644 (file)
index d905d9d..0000000
+++ /dev/null
@@ -1 +0,0 @@
-e
diff --git a/testsuite/e5.ed b/testsuite/e5.ed
new file mode 100644 (file)
index 0000000..5c548be
--- /dev/null
@@ -0,0 +1,7 @@
+H
+e test.bin
+# modifying the last line of a binary file adds a newline
+$s/x/x/
+# but undo restores the line to its previous state
+u
+w out.o
similarity index 100%
rename from testsuite/ascii.r
rename to testsuite/e5.r
index e60975a..00c37e2 100644 (file)
@@ -1 +1,3 @@
-.f f1.err
+H
+.f test.bin
+w out.ro
diff --git a/testsuite/f1.pr b/testsuite/f1.pr
deleted file mode 100644 (file)
index e60975a..0000000
+++ /dev/null
@@ -1 +0,0 @@
-.f f1.err
index 26d1c5e..b35cc86 100644 (file)
@@ -1 +1,3 @@
-ff1.err
+H
+ftest.bin
+w out.ro
diff --git a/testsuite/f2.pr b/testsuite/f2.pr
deleted file mode 100644 (file)
index 26d1c5e..0000000
+++ /dev/null
@@ -1 +0,0 @@
-ff1.err
diff --git a/testsuite/g.ed b/testsuite/g.ed
new file mode 100644 (file)
index 0000000..eefe72f
--- /dev/null
@@ -0,0 +1,32 @@
+H
+g/./m0
+g//a\
+hello world
+# lines beginning with a `#' should be ignored
+g/hello /# including in global commands \
+s/lo/p!/\
+a\
+order\
+.\
+# and in the command list \
+i\
+caos\
+.\
+-1s/l/L
+u
+u
+17,33g/[A-I]/-1d\
++1c\
+hello world\
+.\
+47
+;d
+# don't change current address if no match
+g/xxx/1d
+;j
+g/heLp! world//caos/d\
+-;/order/;d
+# to be undone
+g/./s//x/g
+u
+w out.o
diff --git a/testsuite/g.r b/testsuite/g.r
new file mode 100644 (file)
index 0000000..a50255d
--- /dev/null
@@ -0,0 +1,28 @@
+their families.
+heLp! world
+no anxiety about providing the means of subsistence for themselves and
+heLp! world
+which should live in ease, happiness, and comparative leisure; and feel
+heLp! world
+decisive against the possible existence of a society, all the members of
+heLp! world
+of it even for a single century. And it appears, therefore, to be
+hello world
+caos
+agrarian regulations in their utmost extent, could remove the pressure
+heLp! world
+of this law which pervades all animated nature. No fancied equality, no
+heLp! world
+comparison of this. I see no way by which man can escape from the weight
+hello world
+caos
+All other arguments are of slight and subordinate consideration in
+hello world
+caos
+me appears insurmountable in the way to the perfectibility of society.
+heLp! world
+constantly keep their effects equal, form the great difficulty that to
+heLp! world
+production in the earth, and that great law of our nature which must
+heLp! world
+This natural inequality of the two powers of population and of
diff --git a/testsuite/g1.d b/testsuite/g1.d
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
index f95ea22..70da494 100644 (file)
@@ -1 +1,3 @@
-g/./s //x/
+H
+g
+w out.ro
diff --git a/testsuite/g1.pr b/testsuite/g1.pr
deleted file mode 100644 (file)
index f95ea22..0000000
+++ /dev/null
@@ -1 +0,0 @@
-g/./s //x/
diff --git a/testsuite/g1.r b/testsuite/g1.r
deleted file mode 100644 (file)
index 9185f3a..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-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
deleted file mode 100644 (file)
index 8523db1..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-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
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
index 0ff6a5a..698103d 100644 (file)
@@ -1 +1,3 @@
-g//s/./x/
+H
+g//d
+w out.ro
diff --git a/testsuite/g2.pr b/testsuite/g2.pr
deleted file mode 100644 (file)
index 0ff6a5a..0000000
+++ /dev/null
@@ -1 +0,0 @@
-g//s/./x/
diff --git a/testsuite/g2.r b/testsuite/g2.r
deleted file mode 100644 (file)
index 3b18e51..0000000
+++ /dev/null
@@ -1 +0,0 @@
-hello world
diff --git a/testsuite/g2.t b/testsuite/g2.t
deleted file mode 100644 (file)
index 831ee83..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-g/[2-4]/-1,+1c\
-hello world
diff --git a/testsuite/g3.d b/testsuite/g3.d
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
index 01058d8..2f5ca12 100644 (file)
@@ -1 +1,3 @@
-g
+H
+g/./s //x/
+w out.ro
diff --git a/testsuite/g3.pr b/testsuite/g3.pr
deleted file mode 100644 (file)
index 01058d8..0000000
+++ /dev/null
@@ -1 +0,0 @@
-g
diff --git a/testsuite/g3.r b/testsuite/g3.r
deleted file mode 100644 (file)
index cc6fbdd..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-linc 3
-xine 1
-xine 2
-xinc 4
-xinc5
diff --git a/testsuite/g3.t b/testsuite/g3.t
deleted file mode 100644 (file)
index 2d052a6..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-g/./s//x/\
-3m0
-g/./s/e/c/\
-2,3m1
diff --git a/testsuite/g4.d b/testsuite/g4.d
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
diff --git a/testsuite/g4.err b/testsuite/g4.err
new file mode 100644 (file)
index 0000000..8797183
--- /dev/null
@@ -0,0 +1,7 @@
+H
+# newlines in replacement are ambiguous; last delimiter can't be omitted
+g/./s/y/y\
+f text.bin
+# the previous s command should not set replacement
+s/./%/
+w out.ro
diff --git a/testsuite/g4.r b/testsuite/g4.r
deleted file mode 100644 (file)
index 350882d..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-hello
-zine 1
-line 2
-line 3
-line 4
-line5
-world
diff --git a/testsuite/g4.t b/testsuite/g4.t
deleted file mode 100644 (file)
index ec61816..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-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
deleted file mode 100644 (file)
index a92d664..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-line 1
-line 2
-line 3
diff --git a/testsuite/g5.r b/testsuite/g5.r
deleted file mode 100644 (file)
index 15a2675..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-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
deleted file mode 100644 (file)
index e213481..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-g/./1,3t$\
-1d
index a71e506..02c6d23 100644 (file)
@@ -1 +1,3 @@
+H
 .h
+w out.ro
diff --git a/testsuite/h.pr b/testsuite/h.pr
deleted file mode 100644 (file)
index a71e506..0000000
+++ /dev/null
@@ -1 +0,0 @@
-.h
diff --git a/testsuite/i.d b/testsuite/i.d
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
diff --git a/testsuite/i.ed b/testsuite/i.ed
new file mode 100644 (file)
index 0000000..579c0ff
--- /dev/null
@@ -0,0 +1,37 @@
+H
+# empty insert at address 0 should set the current address to 0
+0i
+.
++4i
+# this is not a comment
+.
+# empty insert at current address should not modify the current address
+i
+.
+i
+hello world!
+.
+i
+hello world!!
+.
+0i
+hello world!!!
+.
+i
+
+second line
+
+.
+$i
+hello world!!!!
+.
+u
+u
+i
+hello world!!!!!
+.
+i
+to be undone
+.
+u
+w out.o
index b63f5ac..47073f6 100644 (file)
@@ -1,3 +1,5 @@
+H
 ii
 hello world
 .
+w out.ro
diff --git a/testsuite/i.pr b/testsuite/i.pr
deleted file mode 100644 (file)
index b63f5ac..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-ii
-hello world
-.
index 5f27af0..e1a20ba 100644 (file)
@@ -1,8 +1,22 @@
-hello world
-hello world!
-line 1
-line 2
-line 3
-line 4
+
+second line
+
+hello world!!!
+This natural inequality of the two powers of population and of
+production in the earth, and that great law of our nature which must
+constantly keep their effects equal, form the great difficulty that to
 hello world!!
-line5
+hello world!
+# this is not a comment
+me appears insurmountable in the way to the perfectibility of society.
+All other arguments are of slight and subordinate consideration in
+comparison of this. I see no way by which man can escape from the weight
+of this law which pervades all animated nature. No fancied equality, no
+agrarian regulations in their utmost extent, could remove the pressure
+of it even for a single century. And it appears, therefore, to be
+decisive against the possible existence of a society, all the members of
+which should live in ease, happiness, and comparative leisure; and feel
+no anxiety about providing the means of subsistence for themselves and
+hello world!!!!!
+hello world!!!!
+their families.
diff --git a/testsuite/i.t b/testsuite/i.t
deleted file mode 100644 (file)
index 6de2233..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-0i
-hello world
-.
-2i
-hello world!
-.
-$i
-hello world!!
-.
diff --git a/testsuite/j.d b/testsuite/j.d
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
diff --git a/testsuite/j.ed b/testsuite/j.ed
new file mode 100644 (file)
index 0000000..4827cb5
--- /dev/null
@@ -0,0 +1,14 @@
+H
+2,3j
+j
+7
+u
+u
+3j
+j
+3,3j
+j
+# to be undone
+1,$j
+u
+w out.o
index 66f36a8..5eef11c 100644 (file)
@@ -1,4 +1,9 @@
-line 1
-line 2line 3
-line 4
-line5
+This natural inequality of the two powers of population and of
+production in the earth, and that great law of our nature which mustconstantly keep their effects equal, form the great difficulty that tome appears insurmountable in the way to the perfectibility of society.
+All other arguments are of slight and subordinate consideration in
+comparison of this. I see no way by which man can escape from the weight
+of this law which pervades all animated nature. No fancied equality, no
+agrarian regulations in their utmost extent, could remove the pressure
+of it even for a single century. And it appears, therefore, to bedecisive against the possible existence of a society, all the members ofwhich should live in ease, happiness, and comparative leisure; and feel
+no anxiety about providing the means of subsistence for themselves and
+their families.
diff --git a/testsuite/j.t b/testsuite/j.t
deleted file mode 100644 (file)
index 9b5d28d..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-1,1j
-2,3j
diff --git a/testsuite/k.d b/testsuite/k.d
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
diff --git a/testsuite/k.ed b/testsuite/k.ed
new file mode 100644 (file)
index 0000000..493759e
--- /dev/null
@@ -0,0 +1,12 @@
+H
+7l
+2ka
+4kb
+9kc
+13kd
+i
+hello world
+.
+'a,'bd
+'c,'dd
+w out.o
index eeb38db..ab1a79e 100644 (file)
@@ -1,5 +1,6 @@
-line 3
+This natural inequality of the two powers of population and of
+All other arguments are of slight and subordinate consideration in
+comparison of this. I see no way by which man can escape from the weight
 hello world
-line 4
-line5
-line 2
+of this law which pervades all animated nature. No fancied equality, no
+agrarian regulations in their utmost extent, could remove the pressure
diff --git a/testsuite/k.t b/testsuite/k.t
deleted file mode 100644 (file)
index 53d588d..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-2ka
-1d
-'am$
-1ka
-0a
-hello world
-.
-'ad
-u
-'am0
similarity index 66%
rename from testsuite/k4.err
rename to testsuite/k1.err
index 3457642..bde56ce 100644 (file)
@@ -1,6 +1,8 @@
+H
 a
 hello
 .
 .ka
 'ad
 'ap
+w out.ro
index b34a18d..dfca298 100644 (file)
@@ -1 +1,3 @@
+H
 kA
+w out.ro
diff --git a/testsuite/k2.pr b/testsuite/k2.pr
deleted file mode 100644 (file)
index b34a18d..0000000
+++ /dev/null
@@ -1 +0,0 @@
-kA
index 70190c4..f59619f 100644 (file)
@@ -1 +1,3 @@
+H
 0ka
+w out.ro
diff --git a/testsuite/k3.pr b/testsuite/k3.pr
deleted file mode 100644 (file)
index 70190c4..0000000
+++ /dev/null
@@ -1 +0,0 @@
-0ka
diff --git a/testsuite/k4.pr b/testsuite/k4.pr
deleted file mode 100644 (file)
index 3457642..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-a
-hello
-.
-.ka
-'ad
-'ap
diff --git a/testsuite/m.d b/testsuite/m.d
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
index 3aec4c3..a5260ec 100644 (file)
@@ -1,4 +1,3 @@
-a
-hello world
-.
-1,$m1
+H
+1,$m5
+w out.ro
diff --git a/testsuite/m.pr b/testsuite/m.pr
deleted file mode 100644 (file)
index edbb96f..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-a
-hello world
-.
-1,$m1
-hello world
diff --git a/testsuite/m.r b/testsuite/m.r
deleted file mode 100644 (file)
index 186cf54..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line5
-line 1
-line 2
-line 3
-line 4
diff --git a/testsuite/m.t b/testsuite/m.t
deleted file mode 100644 (file)
index c39c088..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-1,2m$
-1,2m$
-1,2m$
-$m0
-$m0
-2,3m1
-2,3m3
diff --git a/testsuite/m_addr.ed b/testsuite/m_addr.ed
new file mode 100644 (file)
index 0000000..28c77e5
--- /dev/null
@@ -0,0 +1,20 @@
+H
+3 ---- 2,1 2 3m;,,;;
+-2,.m7
+. ++,+++m.
+-1,m.
+.-4m-2
+-2m-1
+1,2;3,4,$;5,6,7
+;.m0
+u
+u
++;m7
+-m-6
++1m+4
+-3m?all?
+/their/-m-2
+# to be undone
+,1m$
+u
+,w out.o
diff --git a/testsuite/m_addr.r b/testsuite/m_addr.r
new file mode 100644 (file)
index 0000000..5051d68
--- /dev/null
@@ -0,0 +1,13 @@
+their families.
+no anxiety about providing the means of subsistence for themselves and
+which should live in ease, happiness, and comparative leisure; and feel
+decisive against the possible existence of a society, all the members of
+of it even for a single century. And it appears, therefore, to be
+agrarian regulations in their utmost extent, could remove the pressure
+of this law which pervades all animated nature. No fancied equality, no
+comparison of this. I see no way by which man can escape from the weight
+All other arguments are of slight and subordinate consideration in
+me appears insurmountable in the way to the perfectibility of society.
+constantly keep their effects equal, form the great difficulty that to
+production in the earth, and that great law of our nature which must
+This natural inequality of the two powers of population and of
diff --git a/testsuite/nl.err b/testsuite/nl.err
deleted file mode 100644 (file)
index 8949a85..0000000
+++ /dev/null
@@ -1 +0,0 @@
-,1
diff --git a/testsuite/nl.pr b/testsuite/nl.pr
deleted file mode 100644 (file)
index 8949a85..0000000
+++ /dev/null
@@ -1 +0,0 @@
-,1
diff --git a/testsuite/nl1.d b/testsuite/nl1.d
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
diff --git a/testsuite/nl1.r b/testsuite/nl1.r
deleted file mode 100644 (file)
index 9d8854c..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-hello world
-line 1
-line 2
-line 3
-line 4
-line5
diff --git a/testsuite/nl1.t b/testsuite/nl1.t
deleted file mode 100644 (file)
index ea192e9..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-1
-
-
-0a
-
-
-hello world
-.
diff --git a/testsuite/nl2.d b/testsuite/nl2.d
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
diff --git a/testsuite/nl2.r b/testsuite/nl2.r
deleted file mode 100644 (file)
index fe99e41..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
-hello world
diff --git a/testsuite/nl2.t b/testsuite/nl2.t
deleted file mode 100644 (file)
index 73fd27b..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-a
-hello world
-.
-0;/./
diff --git a/testsuite/p1.err b/testsuite/p1.err
new file mode 100644 (file)
index 0000000..9791c80
--- /dev/null
@@ -0,0 +1,3 @@
+H
+ppp
+w out.ro
diff --git a/testsuite/p2.err b/testsuite/p2.err
new file mode 100644 (file)
index 0000000..056c914
--- /dev/null
@@ -0,0 +1,3 @@
+H
+.P
+w out.ro
diff --git a/testsuite/q.d b/testsuite/q.d
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/testsuite/q.ed b/testsuite/q.ed
new file mode 100644 (file)
index 0000000..eb98c7d
--- /dev/null
@@ -0,0 +1,7 @@
+H
+w out.o
+a
+hello
+.
+Q
+w out.o
diff --git a/testsuite/q.err b/testsuite/q.err
deleted file mode 100644 (file)
index 0a7e178..0000000
+++ /dev/null
@@ -1 +0,0 @@
-.q
diff --git a/testsuite/q.pr b/testsuite/q.pr
deleted file mode 100644 (file)
index 0a7e178..0000000
+++ /dev/null
@@ -1 +0,0 @@
-.q
index e69de29..f707ae0 100644 (file)
@@ -0,0 +1,13 @@
+This natural inequality of the two powers of population and of
+production in the earth, and that great law of our nature which must
+constantly keep their effects equal, form the great difficulty that to
+me appears insurmountable in the way to the perfectibility of society.
+All other arguments are of slight and subordinate consideration in
+comparison of this. I see no way by which man can escape from the weight
+of this law which pervades all animated nature. No fancied equality, no
+agrarian regulations in their utmost extent, could remove the pressure
+of it even for a single century. And it appears, therefore, to be
+decisive against the possible existence of a society, all the members of
+which should live in ease, happiness, and comparative leisure; and feel
+no anxiety about providing the means of subsistence for themselves and
+their families.
diff --git a/testsuite/q1.err b/testsuite/q1.err
new file mode 100644 (file)
index 0000000..dd1066f
--- /dev/null
@@ -0,0 +1,3 @@
+H
+.q
+w out.ro
diff --git a/testsuite/q2.err b/testsuite/q2.err
new file mode 100644 (file)
index 0000000..ca2939e
--- /dev/null
@@ -0,0 +1,3 @@
+H
+.Q
+w out.ro
similarity index 52%
rename from testsuite/q.t
rename to testsuite/q3.err
index 123a2c8..fbe1aa8 100644 (file)
@@ -1,4 +1,5 @@
-w q.o
+H
+w out.ro
 a
 hello
 .
diff --git a/testsuite/q4.err b/testsuite/q4.err
new file mode 100644 (file)
index 0000000..2510100
--- /dev/null
@@ -0,0 +1,6 @@
+H
+w out.ro
+a
+hello
+.
+# EOF should behave as a 'q' command
diff --git a/testsuite/q5.err b/testsuite/q5.err
new file mode 100644 (file)
index 0000000..14d7aaa
--- /dev/null
@@ -0,0 +1,7 @@
+H
+w out.ro
+a
+hello
+.
+# EOF in the middle of a command should behave as a 'q' command
+w out.ro
\ No newline at end of file
diff --git a/testsuite/q6.err b/testsuite/q6.err
new file mode 100644 (file)
index 0000000..b99a455
--- /dev/null
@@ -0,0 +1,4 @@
+H
+w out.ro
+a
+# EOF in input mode should behave as a 'q' command
diff --git a/testsuite/q7.err b/testsuite/q7.err
new file mode 100644 (file)
index 0000000..ca8da06
--- /dev/null
@@ -0,0 +1,5 @@
+H
+w out.ro
+a
+hello
+# EOF in the middle of a line in input mode should behave as a 'q' command
\ No newline at end of file
diff --git a/testsuite/q8.err b/testsuite/q8.err
new file mode 100644 (file)
index 0000000..8450355
--- /dev/null
@@ -0,0 +1,7 @@
+H
+w out.ro
+a
+hello
+.
+w !wc
+q
index 1c44fa3..b41bbd5 100644 (file)
@@ -1 +1,3 @@
+H
 r a-good-book
+w out.ro
diff --git a/testsuite/r.pr b/testsuite/r.pr
deleted file mode 100644 (file)
index 1c44fa3..0000000
+++ /dev/null
@@ -1 +0,0 @@
-r a-good-book
diff --git a/testsuite/r1.d b/testsuite/r1.d
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
diff --git a/testsuite/r1.ed b/testsuite/r1.ed
new file mode 100644 (file)
index 0000000..e63660d
--- /dev/null
@@ -0,0 +1,16 @@
+H
+7r test.bin
+u
+u
+.r
+# empty read at current address should not modify the current address
+.r zero
+f test.bin
+-7r
+0r !echo hello world
+1
+r !! %
+# to be undone
+r test.txt
+u
+w out.o
index a3ff506..15718c6 100644 (file)
Binary files a/testsuite/r1.r and b/testsuite/r1.r differ
diff --git a/testsuite/r1.t b/testsuite/r1.t
deleted file mode 100644 (file)
index d787a92..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-1;r !echo hello world
-1
-r !echo hello world
diff --git a/testsuite/r2.d b/testsuite/r2.d
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
diff --git a/testsuite/r2.ed b/testsuite/r2.ed
new file mode 100644 (file)
index 0000000..0b7d20e
--- /dev/null
@@ -0,0 +1,4 @@
+H
+# appending a binary file does not add a newline
+r test.bin
+w out.o
index ac152ba..7f3bdea 100644 (file)
Binary files a/testsuite/r2.r and b/testsuite/r2.r differ
diff --git a/testsuite/r2.t b/testsuite/r2.t
deleted file mode 100644 (file)
index 4286f42..0000000
+++ /dev/null
@@ -1 +0,0 @@
-r
diff --git a/testsuite/r3.d b/testsuite/r3.d
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
diff --git a/testsuite/r3.ed b/testsuite/r3.ed
new file mode 100644 (file)
index 0000000..2e67568
--- /dev/null
@@ -0,0 +1,6 @@
+H
+# appending a binary file does not add a newline
+r test.bin
+# but undo does not remove any newline already present
+u
+w out.o
index 9a1567d..f707ae0 100644 (file)
@@ -1,3 +1,13 @@
-r r3.ed
-1;/H/+d
-w r3.o
+This natural inequality of the two powers of population and of
+production in the earth, and that great law of our nature which must
+constantly keep their effects equal, form the great difficulty that to
+me appears insurmountable in the way to the perfectibility of society.
+All other arguments are of slight and subordinate consideration in
+comparison of this. I see no way by which man can escape from the weight
+of this law which pervades all animated nature. No fancied equality, no
+agrarian regulations in their utmost extent, could remove the pressure
+of it even for a single century. And it appears, therefore, to be
+decisive against the possible existence of a society, all the members of
+which should live in ease, happiness, and comparative leisure; and feel
+no anxiety about providing the means of subsistence for themselves and
+their families.
diff --git a/testsuite/r3.t b/testsuite/r3.t
deleted file mode 100644 (file)
index cdc206d..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-r r3.ed
-1;/H/+d
diff --git a/testsuite/s.ed b/testsuite/s.ed
new file mode 100644 (file)
index 0000000..a643f1e
--- /dev/null
@@ -0,0 +1,58 @@
+H
+s/fam//
+s/il/%/
+s/ies/off\&%1spr\ing/
+1s/of/01/2
++10s/and/02/g
+# set current address to last modified line (6)
+,s/way/03
+u
+u
+s,man,04,1p
+10szallzf&z1
+-3s!no!%&%!
+9s'it'&05&'gp
+12s|of|%|p1
+s/\([^ ][^ ]*\)/(\1)/pg
+2s
+/4/sp
+/\(No\)/sr
+/\(.\)/sgpr
+,s&$&$&
+5s//%/lg
+,s%^%^%
+5i
+hello/[]world
+.
+s/[/]/ /
+s/[[:digit:][]/ /
+s/[]]/ /
+7s=\((03)\) =\1\
+=
+-1s($($(
+2s/a/1/l
+s/a/2/n
+s/a/3/p
+s/a/4/ln
+s/e/5/lp
+s/e/6/np
+s/e/7/lnp
+s/i/8
+s/u/%/
+s/u/%
+3s/ /_/lnp3
+s0e0103
+4sg
+6s5
+9sp
+10s
+11sg
+12sg
+,s/ (nature)/$\
+(nature)/
++12s/ (for)/$\
+(for)/
+# to be undone
+,s/./x/g
+u
+w out.o
diff --git a/testsuite/s.r b/testsuite/s.r
new file mode 100644 (file)
index 0000000..c2b20b9
--- /dev/null
@@ -0,0 +1,17 @@
+^This natural inequality of the two powers 01 population and of$
+^(prod8ct8on) (in) (th5) (61rth,) (2nd) (th3t) (gr74t) (law) (of) (o8r)$
+(nature) (which) (must)$
+^constantly keep th1ir_effects equal, form the great difficulty that to$
+^m1 app1ars insurmountabl1 in th1 03 to th1 p1rf1ctibility of soci1ty.$
+hello   world
+^All other arguments are of slight and subordinate consid1ration in$$
+^(comparison) (of) (this.) (I) (see) (no) (03)$
+(by) (which) (04) (can) (escape) (from) (the) (weight)$
+^of this law which pervades all animated nature. (No) fanci1d equality, %no%$
+^(a)grarian regulations in their utmost extent, could r1move the pressure$
+^of it05it 1v1n for a singl1 c1ntury. And it05it app1ars, th1r1for1, to b1$
+^d1cisive against the possible existence of a society, fall the members of$
+^which should live in ease, happiness, 02 comparative leisure; 02 feel$
+^(no) (anxiety) (about) (providing) (the) (means) (of05of) (subsistence)$
+(for) (themselves) (and)$
+^their off&%1spring.$
diff --git a/testsuite/s01.err b/testsuite/s01.err
new file mode 100644 (file)
index 0000000..c0fc270
--- /dev/null
@@ -0,0 +1,3 @@
+H
+s . x
+w out.ro
diff --git a/testsuite/s02.err b/testsuite/s02.err
new file mode 100644 (file)
index 0000000..7cb57da
--- /dev/null
@@ -0,0 +1,3 @@
+H
+s/x*/a/g
+w out.ro
diff --git a/testsuite/s03.err b/testsuite/s03.err
new file mode 100644 (file)
index 0000000..9fee8b1
--- /dev/null
@@ -0,0 +1,3 @@
+H
+s/[xyx/a/
+w out.ro
similarity index 56%
rename from testsuite/s4.err
rename to testsuite/s04.err
index 35b609f..b920b16 100644 (file)
@@ -1 +1,3 @@
+H
 s/\a\b\c/xyz/
+w out.ro
diff --git a/testsuite/s05.err b/testsuite/s05.err
new file mode 100644 (file)
index 0000000..6eed8ca
--- /dev/null
@@ -0,0 +1,3 @@
+H
+s//xyz/
+w out.ro
diff --git a/testsuite/s06.err b/testsuite/s06.err
new file mode 100644 (file)
index 0000000..45b338a
--- /dev/null
@@ -0,0 +1,3 @@
+H
+s
+w out.ro
diff --git a/testsuite/s07.err b/testsuite/s07.err
new file mode 100644 (file)
index 0000000..fd86a9c
--- /dev/null
@@ -0,0 +1,4 @@
+H
+/./
+sr
+w out.ro
similarity index 50%
rename from testsuite/s8.err
rename to testsuite/s08.err
index 5665767..9b1e2f4 100644 (file)
@@ -1,4 +1,3 @@
-a
-hello
-.
+H
 s/[h[=]/x/
+w out.ro
similarity index 50%
rename from testsuite/s9.err
rename to testsuite/s09.err
index 1ff16dd..a75b2c1 100644 (file)
@@ -1,4 +1,3 @@
-a
-hello
-.
+H
 s/[h[:]/x/
+w out.ro
diff --git a/testsuite/s1.d b/testsuite/s1.d
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
diff --git a/testsuite/s1.err b/testsuite/s1.err
deleted file mode 100644 (file)
index d7ca0cf..0000000
+++ /dev/null
@@ -1 +0,0 @@
-s . x
diff --git a/testsuite/s1.pr b/testsuite/s1.pr
deleted file mode 100644 (file)
index d7ca0cf..0000000
+++ /dev/null
@@ -1 +0,0 @@
-s . x
diff --git a/testsuite/s1.r b/testsuite/s1.r
deleted file mode 100644 (file)
index 4eb0980..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-liene 1
-(liene) (2)
-(liene) (3)
-liene (4)
-(()liene5)
diff --git a/testsuite/s1.t b/testsuite/s1.t
deleted file mode 100644 (file)
index cca3dcc..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-s/\([^ ][^ ]*\)/(\1)/g
-2s
-/3/s
-/\(4\)/sr
-/\(.\)/srg
-,s/i/&e/
index 0d8d83d..8069299 100644 (file)
@@ -1,4 +1,3 @@
-a
-hello
-.
+H
 s/[h[.]/x/
+w out.ro
diff --git a/testsuite/s10.pr b/testsuite/s10.pr
deleted file mode 100644 (file)
index 1304de1..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-a
-hello
-.
-s/[h[.]/x/
-hello
diff --git a/testsuite/s11.err b/testsuite/s11.err
new file mode 100644 (file)
index 0000000..3a3d913
--- /dev/null
@@ -0,0 +1,8 @@
+H
+# Missing pattern delimiter must not alter RE nor REPLACEMENT
+s/i/1/g
+s/1/i/
+s/
+s/1
+s//%/g
+w out.ro
diff --git a/testsuite/s12.err b/testsuite/s12.err
new file mode 100644 (file)
index 0000000..90dee78
--- /dev/null
@@ -0,0 +1,3 @@
+H
+s/./&/g3
+w out.ro
diff --git a/testsuite/s13.err b/testsuite/s13.err
new file mode 100644 (file)
index 0000000..82e3194
--- /dev/null
@@ -0,0 +1,3 @@
+H
+s/./&/3g
+w out.ro
diff --git a/testsuite/s14.err b/testsuite/s14.err
new file mode 100644 (file)
index 0000000..18555c5
--- /dev/null
@@ -0,0 +1,4 @@
+H
+s/./&/
+sg3
+w out.ro
diff --git a/testsuite/s15.err b/testsuite/s15.err
new file mode 100644 (file)
index 0000000..f4341a9
--- /dev/null
@@ -0,0 +1,4 @@
+H
+s/./&/
+s3g
+w out.ro
diff --git a/testsuite/s16.err b/testsuite/s16.err
new file mode 100644 (file)
index 0000000..4f6fba7
--- /dev/null
@@ -0,0 +1,3 @@
+H
+s3.3&3p
+w out.ro
diff --git a/testsuite/s17.err b/testsuite/s17.err
new file mode 100644 (file)
index 0000000..225e2a3
--- /dev/null
@@ -0,0 +1,3 @@
+H
+sg.g&gp
+w out.ro
diff --git a/testsuite/s18.err b/testsuite/s18.err
new file mode 100644 (file)
index 0000000..5cb703d
--- /dev/null
@@ -0,0 +1,3 @@
+H
+sr.r&rp
+w out.ro
diff --git a/testsuite/s19.err b/testsuite/s19.err
new file mode 100644 (file)
index 0000000..0dda461
--- /dev/null
@@ -0,0 +1,3 @@
+H
+sp.p&p
+w out.ro
diff --git a/testsuite/s2.d b/testsuite/s2.d
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
diff --git a/testsuite/s2.err b/testsuite/s2.err
deleted file mode 100644 (file)
index b5c851d..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-a
-a
-.
-s/x*/a/g
diff --git a/testsuite/s2.pr b/testsuite/s2.pr
deleted file mode 100644 (file)
index 949ac99..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-a
-a
-.
-s/x*/a/g
-a
diff --git a/testsuite/s2.r b/testsuite/s2.r
deleted file mode 100644 (file)
index ca305c8..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-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
deleted file mode 100644 (file)
index f365849..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-,s/./(&)/3
-s/$/00
-2s//%/g
-s/^l
diff --git a/testsuite/s20.err b/testsuite/s20.err
new file mode 100644 (file)
index 0000000..f48b2ff
--- /dev/null
@@ -0,0 +1,5 @@
+H
+$m7
+,s/xxx/yyy/
+.m$
+w out.ro
diff --git a/testsuite/s21.err b/testsuite/s21.err
new file mode 100644 (file)
index 0000000..c76c519
--- /dev/null
@@ -0,0 +1,4 @@
+H
+w out.ro
+s/a/EOF in the middle of a replacement with newlines \
+should behave as a 'q' command
\ No newline at end of file
diff --git a/testsuite/s3.d b/testsuite/s3.d
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/testsuite/s3.err b/testsuite/s3.err
deleted file mode 100644 (file)
index d68c7d0..0000000
+++ /dev/null
@@ -1 +0,0 @@
-s/[xyx/a/
diff --git a/testsuite/s3.pr b/testsuite/s3.pr
deleted file mode 100644 (file)
index d68c7d0..0000000
+++ /dev/null
@@ -1 +0,0 @@
-s/[xyx/a/
diff --git a/testsuite/s3.r b/testsuite/s3.r
deleted file mode 100644 (file)
index d6cada2..0000000
+++ /dev/null
@@ -1 +0,0 @@
-hello   world
diff --git a/testsuite/s3.t b/testsuite/s3.t
deleted file mode 100644 (file)
index fbf8803..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-a
-hello/[]world
-.
-s/[/]/ /
-s/[[:digit:][]/ /
-s/[]]/ /
diff --git a/testsuite/s4.pr b/testsuite/s4.pr
deleted file mode 100644 (file)
index 35b609f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-s/\a\b\c/xyz/
diff --git a/testsuite/s5.err b/testsuite/s5.err
deleted file mode 100644 (file)
index 89104c5..0000000
+++ /dev/null
@@ -1 +0,0 @@
-s//xyz/
diff --git a/testsuite/s5.pr b/testsuite/s5.pr
deleted file mode 100644 (file)
index 89104c5..0000000
+++ /dev/null
@@ -1 +0,0 @@
-s//xyz/
diff --git a/testsuite/s6.err b/testsuite/s6.err
deleted file mode 100644 (file)
index b478595..0000000
+++ /dev/null
@@ -1 +0,0 @@
-s
diff --git a/testsuite/s6.pr b/testsuite/s6.pr
deleted file mode 100644 (file)
index b478595..0000000
+++ /dev/null
@@ -1 +0,0 @@
-s
diff --git a/testsuite/s7.err b/testsuite/s7.err
deleted file mode 100644 (file)
index 30ba4fd..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-a
-hello world
-.
-/./
-sr
diff --git a/testsuite/s7.pr b/testsuite/s7.pr
deleted file mode 100644 (file)
index 47a94c3..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-a
-hello world
-.
-/./
-sr
-hello world
diff --git a/testsuite/s8.pr b/testsuite/s8.pr
deleted file mode 100644 (file)
index ec6a965..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-a
-hello
-.
-s/[h[=]/x/
-hello
diff --git a/testsuite/s9.pr b/testsuite/s9.pr
deleted file mode 100644 (file)
index f22b3b3..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-a
-hello
-.
-s/[h[:]/x/
-hello
diff --git a/testsuite/t.d b/testsuite/t.d
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
diff --git a/testsuite/t.ed b/testsuite/t.ed
new file mode 100644 (file)
index 0000000..3399b45
--- /dev/null
@@ -0,0 +1,11 @@
+H
+1t0
+2,3t2
+,t$
+u
+u
+t0;/./
+# to be undone
+,t0
+u
+w out.o
index b7e0a71..b90a437 100644 (file)
@@ -1,17 +1,33 @@
-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
+This natural inequality of the two powers of population and of
+their families.
+This natural inequality of the two powers of population and of
+This natural inequality of the two powers of population and of
+production in the earth, and that great law of our nature which must
+production in the earth, and that great law of our nature which must
+constantly keep their effects equal, form the great difficulty that to
+me appears insurmountable in the way to the perfectibility of society.
+All other arguments are of slight and subordinate consideration in
+comparison of this. I see no way by which man can escape from the weight
+of this law which pervades all animated nature. No fancied equality, no
+agrarian regulations in their utmost extent, could remove the pressure
+of it even for a single century. And it appears, therefore, to be
+decisive against the possible existence of a society, all the members of
+which should live in ease, happiness, and comparative leisure; and feel
+no anxiety about providing the means of subsistence for themselves and
+their families.
+This natural inequality of the two powers of population and of
+This natural inequality of the two powers of population and of
+This natural inequality of the two powers of population and of
+production in the earth, and that great law of our nature which must
+production in the earth, and that great law of our nature which must
+constantly keep their effects equal, form the great difficulty that to
+me appears insurmountable in the way to the perfectibility of society.
+All other arguments are of slight and subordinate consideration in
+comparison of this. I see no way by which man can escape from the weight
+of this law which pervades all animated nature. No fancied equality, no
+agrarian regulations in their utmost extent, could remove the pressure
+of it even for a single century. And it appears, therefore, to be
+decisive against the possible existence of a society, all the members of
+which should live in ease, happiness, and comparative leisure; and feel
+no anxiety about providing the means of subsistence for themselves and
+their families.
diff --git a/testsuite/t.t b/testsuite/t.t
deleted file mode 100644 (file)
index bb42163..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-1t0
-2,3t2
-,t$
-t0;/./
index c49c556..02a9033 100644 (file)
@@ -1 +1,3 @@
+H
 tt
+w out.ro
diff --git a/testsuite/t1.pr b/testsuite/t1.pr
deleted file mode 100644 (file)
index c49c556..0000000
+++ /dev/null
@@ -1 +0,0 @@
-tt
index c202051..fdd99ba 100644 (file)
@@ -1 +1,3 @@
+H
 t0;-1
+w out.ro
diff --git a/testsuite/t2.pr b/testsuite/t2.pr
deleted file mode 100644 (file)
index c202051..0000000
+++ /dev/null
@@ -1 +0,0 @@
-t0;-1
similarity index 100%
rename from testsuite/ascii.d
rename to testsuite/test.bin
diff --git a/testsuite/test.txt b/testsuite/test.txt
new file mode 100644 (file)
index 0000000..f707ae0
--- /dev/null
@@ -0,0 +1,13 @@
+This natural inequality of the two powers of population and of
+production in the earth, and that great law of our nature which must
+constantly keep their effects equal, form the great difficulty that to
+me appears insurmountable in the way to the perfectibility of society.
+All other arguments are of slight and subordinate consideration in
+comparison of this. I see no way by which man can escape from the weight
+of this law which pervades all animated nature. No fancied equality, no
+agrarian regulations in their utmost extent, could remove the pressure
+of it even for a single century. And it appears, therefore, to be
+decisive against the possible existence of a society, all the members of
+which should live in ease, happiness, and comparative leisure; and feel
+no anxiety about providing the means of subsistence for themselves and
+their families.
diff --git a/testsuite/u.d b/testsuite/u.d
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
index caa1ba1..205f56c 100644 (file)
@@ -1 +1,3 @@
+H
 .u
+w out.ro
diff --git a/testsuite/u.pr b/testsuite/u.pr
deleted file mode 100644 (file)
index caa1ba1..0000000
+++ /dev/null
@@ -1 +0,0 @@
-.u
diff --git a/testsuite/u.r b/testsuite/u.r
deleted file mode 100644 (file)
index ad558d8..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-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
deleted file mode 100644 (file)
index 1b9eb15..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-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
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
diff --git a/testsuite/v.ed b/testsuite/v.ed
new file mode 100644 (file)
index 0000000..023b7eb
--- /dev/null
@@ -0,0 +1,13 @@
+H
+v/[w]/m0
+u
+u
+v/o/a\
+hello world
+v/hello /s/lo/p!/\
+a\
+order
+# to be undone
+v/z/s/./x/g
+u
+w out.o
index 714db63..840aa7c 100644 (file)
@@ -1,11 +1,27 @@
-line5
+their families.
 order
 hello world
-line 1
+no anxiety about providing the means of subsistence for themselves and
 order
-line 2
+decisive against the possible existence of a society, all the members of
 order
-line 3
+of it even for a single century. And it appears, therefore, to be
 order
-line 4
+agrarian regulations in their utmost extent, could remove the pressure
+order
+All other arguments are of slight and subordinate consideration in
+order
+constantly keep their effects equal, form the great difficulty that to
+order
+This natural inequality of the two powers of population and of
+order
+production in the earth, and that great law of our nature which must
+order
+me appears insurmountable in the way to the perfectibility of society.
+order
+comparison of this. I see no way by which man can escape from the weight
+order
+of this law which pervades all animated nature. No fancied equality, no
+order
+which should live in ease, happiness, and comparative leisure; and feel
 order
diff --git a/testsuite/v.t b/testsuite/v.t
deleted file mode 100644 (file)
index 608a77f..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-v/[ ]/m0
-v/[ ]/s/$/\
-hello world
-v/hello /s/lo/p!/\
-a\
-order
diff --git a/testsuite/w.d b/testsuite/w.d
deleted file mode 100644 (file)
index 92f337e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line5
diff --git a/testsuite/w.ed b/testsuite/w.ed
new file mode 100644 (file)
index 0000000..ce32199
--- /dev/null
@@ -0,0 +1,7 @@
+H
+5w \!.z
+6W ./!.z
+0r !cat < ./!.z
+r \!.z
+!rm -f ./!.z
+wq out.o
index ac152ba..9157ecb 100644 (file)
@@ -1,10 +1,17 @@
-line 1
-line 2
-line 3
-line 4
-line5
-line 1
-line 2
-line 3
-line 4
-line5
+All other arguments are of slight and subordinate consideration in
+comparison of this. I see no way by which man can escape from the weight
+This natural inequality of the two powers of population and of
+production in the earth, and that great law of our nature which must
+constantly keep their effects equal, form the great difficulty that to
+me appears insurmountable in the way to the perfectibility of society.
+All other arguments are of slight and subordinate consideration in
+comparison of this. I see no way by which man can escape from the weight
+of this law which pervades all animated nature. No fancied equality, no
+agrarian regulations in their utmost extent, could remove the pressure
+of it even for a single century. And it appears, therefore, to be
+decisive against the possible existence of a society, all the members of
+which should live in ease, happiness, and comparative leisure; and feel
+no anxiety about providing the means of subsistence for themselves and
+their families.
+All other arguments are of slight and subordinate consideration in
+comparison of this. I see no way by which man can escape from the weight
diff --git a/testsuite/w.t b/testsuite/w.t
deleted file mode 100644 (file)
index c2e18bd..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-w !cat >\!.z
-r \!.z
index e2c8a60..5185887 100644 (file)
@@ -1 +1,3 @@
-w /to/some/far-away/place
+H
+w to/some/far-away/place
+w out.ro
diff --git a/testsuite/w1.pr b/testsuite/w1.pr
deleted file mode 100644 (file)
index e2c8a60..0000000
+++ /dev/null
@@ -1 +0,0 @@
-w /to/some/far-away/place
index 9daf89c..a55f36c 100644 (file)
@@ -1 +1,3 @@
-ww.o
+H
+wbad_write
+w out.ro
diff --git a/testsuite/w2.pr b/testsuite/w2.pr
deleted file mode 100644 (file)
index 9daf89c..0000000
+++ /dev/null
@@ -1 +0,0 @@
-ww.o
index 39bbf4c..bd42097 100644 (file)
@@ -1 +1,3 @@
-wqp w.o
+H
+wqp bad_write
+w out.ro
diff --git a/testsuite/w3.pr b/testsuite/w3.pr
deleted file mode 100644 (file)
index 39bbf4c..0000000
+++ /dev/null
@@ -1 +0,0 @@
-wqp w.o
diff --git a/testsuite/x.d b/testsuite/x.d
deleted file mode 100644 (file)
index 94c99a3..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-line 1
-line 2
-line 3
-line 4
-line 5
diff --git a/testsuite/x.ed b/testsuite/x.ed
new file mode 100644 (file)
index 0000000..8c1349c
--- /dev/null
@@ -0,0 +1,17 @@
+H
+2,4y
+$x
+3x
+u
+u
+,y
+8y
+$x
+16
+y
+x
+# to be undone
+,y
+x
+u
+w out.o
index 898398e..c75f849 100644 (file)
@@ -1,10 +1,3 @@
-2,3y
-$x
-0x
-,y
-$x
-2
-y
-x
-E addr1.ro
+H
 x
+w out.ro
diff --git a/testsuite/x.pr b/testsuite/x.pr
deleted file mode 100644 (file)
index 29d6383..0000000
+++ /dev/null
@@ -1 +0,0 @@
-100
index 7b8b7c0..89cbcb0 100644 (file)
@@ -1,19 +1,21 @@
-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
+This natural inequality of the two powers of population and of
+production in the earth, and that great law of our nature which must
+constantly keep their effects equal, form the great difficulty that to
+production in the earth, and that great law of our nature which must
+constantly keep their effects equal, form the great difficulty that to
+me appears insurmountable in the way to the perfectibility of society.
+me appears insurmountable in the way to the perfectibility of society.
+All other arguments are of slight and subordinate consideration in
+comparison of this. I see no way by which man can escape from the weight
+of this law which pervades all animated nature. No fancied equality, no
+agrarian regulations in their utmost extent, could remove the pressure
+of it even for a single century. And it appears, therefore, to be
+decisive against the possible existence of a society, all the members of
+which should live in ease, happiness, and comparative leisure; and feel
+no anxiety about providing the means of subsistence for themselves and
+their families.
+their families.
+production in the earth, and that great law of our nature which must
+constantly keep their effects equal, form the great difficulty that to
+me appears insurmountable in the way to the perfectibility of society.
+All other arguments are of slight and subordinate consideration in
diff --git a/testsuite/x.t b/testsuite/x.t
deleted file mode 100644 (file)
index 0e868b8..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-2,3y
-$x
-0x
-,y
-$x
-2
-y
-x
index 6a51a2d..fe297c0 100644 (file)
@@ -1,2 +1,4 @@
+H
+$z
 z
-z
+w out.ro
diff --git a/testsuite/z.pr b/testsuite/z.pr
deleted file mode 100644 (file)
index 6a51a2d..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-z
-z