]> Zhao Yanbai Git Server - minix.git/commitdiff
Import NetBSD ed(1)
authorAndy Kosela <akosela@andykosela.com>
Fri, 9 Mar 2012 23:17:50 +0000 (23:17 +0000)
committerBen Gras <ben@minix3.org>
Wed, 14 Mar 2012 15:52:31 +0000 (16:52 +0100)
147 files changed:
bin/Makefile
bin/ed/Makefile [new file with mode: 0644]
bin/ed/POSIX [new file with mode: 0644]
bin/ed/README [new file with mode: 0644]
bin/ed/buf.c [new file with mode: 0644]
bin/ed/cbc.c [new file with mode: 0644]
bin/ed/ed.1 [new file with mode: 0644]
bin/ed/ed.h [new file with mode: 0644]
bin/ed/glbl.c [new file with mode: 0644]
bin/ed/io.c [new file with mode: 0644]
bin/ed/main.c [new file with mode: 0644]
bin/ed/re.c [new file with mode: 0644]
bin/ed/sub.c [new file with mode: 0644]
bin/ed/test/=.err [new file with mode: 0644]
bin/ed/test/Makefile [new file with mode: 0644]
bin/ed/test/README [new file with mode: 0644]
bin/ed/test/TODO [new file with mode: 0644]
bin/ed/test/a.r [new file with mode: 0644]
bin/ed/test/a.t [new file with mode: 0644]
bin/ed/test/a1.err [new file with mode: 0644]
bin/ed/test/a2.err [new file with mode: 0644]
bin/ed/test/addr.r [new file with mode: 0644]
bin/ed/test/addr.t [new file with mode: 0644]
bin/ed/test/addr1.err [new file with mode: 0644]
bin/ed/test/addr2.err [new file with mode: 0644]
bin/ed/test/ascii.r [new file with mode: 0644]
bin/ed/test/ascii.t [new file with mode: 0644]
bin/ed/test/bang1.err [new file with mode: 0644]
bin/ed/test/bang1.r [new file with mode: 0644]
bin/ed/test/bang1.t [new file with mode: 0644]
bin/ed/test/bang2.err [new file with mode: 0644]
bin/ed/test/c.r [new file with mode: 0644]
bin/ed/test/c.t [new file with mode: 0644]
bin/ed/test/c1.err [new file with mode: 0644]
bin/ed/test/c2.err [new file with mode: 0644]
bin/ed/test/ckscripts.sh [new file with mode: 0755]
bin/ed/test/d.err [new file with mode: 0644]
bin/ed/test/d.r [new file with mode: 0644]
bin/ed/test/d.t [new file with mode: 0644]
bin/ed/test/e1.err [new file with mode: 0644]
bin/ed/test/e1.r [new file with mode: 0644]
bin/ed/test/e1.t [new file with mode: 0644]
bin/ed/test/e2.err [new file with mode: 0644]
bin/ed/test/e2.r [new file with mode: 0644]
bin/ed/test/e2.t [new file with mode: 0644]
bin/ed/test/e3.err [new file with mode: 0644]
bin/ed/test/e3.r [new file with mode: 0644]
bin/ed/test/e3.t [new file with mode: 0644]
bin/ed/test/e4.r [new file with mode: 0644]
bin/ed/test/e4.t [new file with mode: 0644]
bin/ed/test/f1.err [new file with mode: 0644]
bin/ed/test/f2.err [new file with mode: 0644]
bin/ed/test/g1.err [new file with mode: 0644]
bin/ed/test/g1.r [new file with mode: 0644]
bin/ed/test/g1.t [new file with mode: 0644]
bin/ed/test/g2.err [new file with mode: 0644]
bin/ed/test/g2.r [new file with mode: 0644]
bin/ed/test/g2.t [new file with mode: 0644]
bin/ed/test/g3.err [new file with mode: 0644]
bin/ed/test/g3.r [new file with mode: 0644]
bin/ed/test/g3.t [new file with mode: 0644]
bin/ed/test/g4.r [new file with mode: 0644]
bin/ed/test/g4.t [new file with mode: 0644]
bin/ed/test/g5.r [new file with mode: 0644]
bin/ed/test/g5.t [new file with mode: 0644]
bin/ed/test/h.err [new file with mode: 0644]
bin/ed/test/i.r [new file with mode: 0644]
bin/ed/test/i.t [new file with mode: 0644]
bin/ed/test/i1.err [new file with mode: 0644]
bin/ed/test/i2.err [new file with mode: 0644]
bin/ed/test/i3.err [new file with mode: 0644]
bin/ed/test/j.r [new file with mode: 0644]
bin/ed/test/j.t [new file with mode: 0644]
bin/ed/test/k.r [new file with mode: 0644]
bin/ed/test/k.t [new file with mode: 0644]
bin/ed/test/k1.err [new file with mode: 0644]
bin/ed/test/k2.err [new file with mode: 0644]
bin/ed/test/k3.err [new file with mode: 0644]
bin/ed/test/k4.err [new file with mode: 0644]
bin/ed/test/l.r [new file with mode: 0644]
bin/ed/test/l.t [new file with mode: 0644]
bin/ed/test/m.err [new file with mode: 0644]
bin/ed/test/m.r [new file with mode: 0644]
bin/ed/test/m.t [new file with mode: 0644]
bin/ed/test/mkscripts.sh [new file with mode: 0755]
bin/ed/test/n.r [new file with mode: 0644]
bin/ed/test/n.t [new file with mode: 0644]
bin/ed/test/nl.err [new file with mode: 0644]
bin/ed/test/nl1.r [new file with mode: 0644]
bin/ed/test/nl1.t [new file with mode: 0644]
bin/ed/test/nl2.r [new file with mode: 0644]
bin/ed/test/nl2.t [new file with mode: 0644]
bin/ed/test/p.r [new file with mode: 0644]
bin/ed/test/p.t [new file with mode: 0644]
bin/ed/test/q.r [new file with mode: 0644]
bin/ed/test/q.t [new file with mode: 0644]
bin/ed/test/q1.err [new file with mode: 0644]
bin/ed/test/r1.err [new file with mode: 0644]
bin/ed/test/r1.r [new file with mode: 0644]
bin/ed/test/r1.t [new file with mode: 0644]
bin/ed/test/r2.err [new file with mode: 0644]
bin/ed/test/r2.r [new file with mode: 0644]
bin/ed/test/r2.t [new file with mode: 0644]
bin/ed/test/r3.r [new file with mode: 0644]
bin/ed/test/r3.t [new file with mode: 0644]
bin/ed/test/s1.err [new file with mode: 0644]
bin/ed/test/s1.r [new file with mode: 0644]
bin/ed/test/s1.t [new file with mode: 0644]
bin/ed/test/s10.err [new file with mode: 0644]
bin/ed/test/s2.err [new file with mode: 0644]
bin/ed/test/s2.r [new file with mode: 0644]
bin/ed/test/s2.t [new file with mode: 0644]
bin/ed/test/s3.err [new file with mode: 0644]
bin/ed/test/s3.r [new file with mode: 0644]
bin/ed/test/s3.t [new file with mode: 0644]
bin/ed/test/s4.err [new file with mode: 0644]
bin/ed/test/s5.err [new file with mode: 0644]
bin/ed/test/s6.err [new file with mode: 0644]
bin/ed/test/s7.err [new file with mode: 0644]
bin/ed/test/s8.err [new file with mode: 0644]
bin/ed/test/s9.err [new file with mode: 0644]
bin/ed/test/t.r [new file with mode: 0644]
bin/ed/test/t1.err [new file with mode: 0644]
bin/ed/test/t1.r [new file with mode: 0644]
bin/ed/test/t1.t [new file with mode: 0644]
bin/ed/test/t2.err [new file with mode: 0644]
bin/ed/test/t2.r [new file with mode: 0644]
bin/ed/test/t2.t [new file with mode: 0644]
bin/ed/test/u.err [new file with mode: 0644]
bin/ed/test/u.r [new file with mode: 0644]
bin/ed/test/u.t [new file with mode: 0644]
bin/ed/test/v.r [new file with mode: 0644]
bin/ed/test/v.t [new file with mode: 0644]
bin/ed/test/w.r [new file with mode: 0644]
bin/ed/test/w.t [new file with mode: 0644]
bin/ed/test/w1.err [new file with mode: 0644]
bin/ed/test/w2.err [new file with mode: 0644]
bin/ed/test/w3.err [new file with mode: 0644]
bin/ed/test/x.err [new file with mode: 0644]
bin/ed/test/z.err [new file with mode: 0644]
bin/ed/undo.c [new file with mode: 0644]
commands/Makefile
commands/ed/Makefile [deleted file]
commands/ed/ed.c [deleted file]
man/man1/Makefile
man/man1/ed.1 [deleted file]
tools/nbsd_ports

index b58d3008121e2dbcd3e646ade409de2636e56aa6..00c99c87577b3866f0c615f748a5e2a4d314da54 100644 (file)
@@ -2,6 +2,6 @@
 
 .include <bsd.own.mk>
 
-SUBDIR= mkdir rm rmdir date
+SUBDIR= date ed mkdir rm rmdir
 
 .include <bsd.subdir.mk>
diff --git a/bin/ed/Makefile b/bin/ed/Makefile
new file mode 100644 (file)
index 0000000..4bd271f
--- /dev/null
@@ -0,0 +1,20 @@
+#      $NetBSD: Makefile,v 1.36 2009/07/26 01:58:20 dholland Exp $
+
+.include <bsd.own.mk>
+
+PROG=  ed
+CPPFLAGS+=-DBACKWARDS
+
+.if (${MKCRYPTO} != "no")
+CPPFLAGS+=-DDES
+.endif
+
+SRCS=  buf.c cbc.c glbl.c io.c main.c re.c sub.c undo.c
+
+LDADD+= -lcrypt
+DPADD+= ${LIBCRYPT}
+
+#LINKS=  ${BINDIR}/ed ${BINDIR}/red
+#MLINKS= ed.1 red.1
+
+.include <bsd.prog.mk>
diff --git a/bin/ed/POSIX b/bin/ed/POSIX
new file mode 100644 (file)
index 0000000..6bea801
--- /dev/null
@@ -0,0 +1,103 @@
+$NetBSD: POSIX,v 1.10 1999/11/18 19:16:34 kristerw Exp $
+
+This version of ed(1) is not strictly POSIX compliant, as described in
+the POSIX 1003.2 document.  The following is a summary of the omissions,
+extensions and possible deviations from POSIX 1003.2.
+
+OMISSIONS
+---------
+1) Locale(3) is not supported yet.
+
+2) 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.
+
+3) To support the BSD `s' command (see extension [1] below),
+   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.
+
+EXTENSIONS
+----------
+1) BSD commands have been implemented wherever they do not conflict with
+   the POSIX standard.  The BSD-ism's included are:
+       i) `s' (i.e., s[n][rgp]*) to repeat a previous substitution,
+       ii) `W' for appending text to an existing file,
+       iii) `wq' for exiting after a write,
+       iv) `z' for scrolling through the buffer, and
+       v) BSD line addressing syntax (i.e., `^' and `%')  is recognized.
+
+2) If crypt(3) is available, files can be read and written using DES
+   encryption.  The `x' command prompts the user to enter a key used for
+   encrypting/ decrypting subsequent reads and writes.  If only a newline
+   is entered as the key, then encryption is disabled.  Otherwise, a key
+   is read in the same manner as a password entry.  The key remains in
+   effect until encryption is disabled.  For more information on the
+   encryption algorithm, see the bdes(1) man page.  Encryption/decryption
+   should be fully compatible with SunOS des(1).
+
+3) The POSIX interactive global commands `G' and `V' are extended to 
+   support multiple commands, including `a', `i' and `c'.  The command
+   format is the same as for the global commands `g' and `v', i.e., one
+   command per line with each line, except for the last, ending in a
+   backslash (\).
+
+4) An extension to the POSIX file commands `E', `e', `r', `W' and `w' is
+   that <file> arguments are processed 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 ed.
+
+5) For SunOS ed(1) compatibility, ed runs in restricted mode if invoked
+   as red.  This limits editing of files in the local directory only and
+   prohibits shell commands.
+
+DEVIATIONS
+----------
+1) Though ed is not a stream editor, it can be used to edit binary files.
+   To assist in binary editing, when a file containing at least one ASCII
+   NUL character is written, a newline is not appended if it did not
+   already contain one upon reading.  In particular, reading /dev/null
+   prior to writing prevents appending a newline to a binary file.
+
+   For example, to create a file with ed containing a single NUL character:
+      $ ed file
+      a
+      ^@
+      .
+      r /dev/null
+      wq
+
+    Similarly, to remove a newline from the end of binary `file':
+      $ ed file
+      r /dev/null
+      wq
+
+2) Since the behavior of `u' (undo) within a `g' (global) command list is
+   not specified by POSIX, it follows the behavior of the SunOS ed:
+   undo forces a global command list to be executed only once, rather than
+   for each line matching a global pattern.  In addtion, each instance of
+   `u' within a global command undoes all previous commands (including
+   undo's) in the command list.  This seems the best way, since the
+   alternatives are either too complicated to implement or too confusing
+   to use.  
+
+   The global/undo combination is useful for masking errors that
+   would otherwise cause a script to fail.  For instance, an ed script
+   to remove any occurrences of either `censor1' or `censor2' might be
+   written as:
+       ed - file <<EOF
+       1g/.*/u\
+       ,s/censor1//g\
+       ,s/censor2//g
+       ...
+
+3) The `m' (move) command within a `g' command list also follows the SunOS
+   ed implementation: any moved lines are removed from the global command's
+   `active' list.
+
+4) If 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
+   backslash.
diff --git a/bin/ed/README b/bin/ed/README
new file mode 100644 (file)
index 0000000..285bc13
--- /dev/null
@@ -0,0 +1,24 @@
+$NetBSD: README,v 1.9 1995/03/21 09:04:33 cgd Exp $
+
+ed is an 8-bit-clean, POSIX-compliant line editor.  It should work with
+any regular expression package that conforms to the POSIX interface
+standard, such as GNU regex(3).
+
+If reliable signals are supported (e.g., POSIX sigaction(2)), it should
+compile with little trouble.  Otherwise, the macros SPL1() and SPL0()
+should be redefined to disable interrupts.
+
+The following compiler directives are recognized:
+DES            - to add encryption support (requires crypt(3))
+NO_REALLOC_NULL        - if realloc(3) does not accept a NULL pointer
+BACKWARDS      - for backwards compatibility
+NEED_INSQUE    - if insque(3) is missing
+
+The file `POSIX' describes extensions to and deviations from the POSIX
+standard.
+
+The ./test directory contains regression tests for ed. The README
+file in that directory explains how to run these.
+
+For a description of the ed algorithm, see Kernighan and Plauger's book
+"Software Tools in Pascal," Addison-Wesley, 1981.
diff --git a/bin/ed/buf.c b/bin/ed/buf.c
new file mode 100644 (file)
index 0000000..fec7cb5
--- /dev/null
@@ -0,0 +1,319 @@
+/*     $NetBSD: buf.c,v 1.26 2006/03/17 14:37:14 rumble Exp $  */
+
+/* buf.c: This file contains the scratch-file buffer routines for the
+   ed line editor. */
+/*-
+ * Copyright (c) 1993 Andrew Moore, Talke Studio.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char *rcsid = "@(#)buf.c,v 1.4 1994/02/01 00:34:35 alm Exp";
+#else
+__RCSID("$NetBSD: buf.c,v 1.26 2006/03/17 14:37:14 rumble Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include <paths.h>
+#include <stdio.h>
+#include <err.h>
+
+#include "ed.h"
+
+
+FILE *sfp;                             /* scratch file pointer */
+off_t sfseek;                          /* scratch file position */
+int seek_write;                                /* seek before writing */
+line_t buffer_head;                    /* incore buffer */
+
+/* get_sbuf_line: get a line of text from the scratch file; return pointer
+   to the text */
+char *
+get_sbuf_line(line_t *lp)
+{
+       static char *sfbuf = NULL;      /* buffer */
+       static int sfbufsz = 0;         /* buffer size */
+
+       int len, ct;
+
+       if (lp == &buffer_head)
+               return NULL;
+       seek_write = 1;                         /* force seek on write */
+       /* out of position */
+       if (sfseek != lp->seek) {
+               sfseek = lp->seek;
+               if (fseek(sfp, sfseek, SEEK_SET) < 0) {
+                       fprintf(stderr, "%s\n", strerror(errno));
+                       sprintf(errmsg, "cannot seek temp file");
+                       return NULL;
+               }
+       }
+       len = lp->len;
+       REALLOC(sfbuf, sfbufsz, len + 1, NULL);
+       if ((ct = fread(sfbuf, sizeof(char), len, sfp)) <  0 || ct != len) {
+               fprintf(stderr, "%s\n", strerror(errno));
+               sprintf(errmsg, "cannot read temp file");
+               return NULL;
+       }
+       sfseek += len;                          /* update file position */
+       sfbuf[len] = '\0';
+       return sfbuf;
+}
+
+
+/* put_sbuf_line: 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 */
+char *
+put_sbuf_line(char *cs)
+{
+       line_t *lp;
+       int len, ct;
+       char *s;
+
+       if ((lp = (line_t *) malloc(sizeof(line_t))) == NULL) {
+               fprintf(stderr, "%s\n", strerror(errno));
+               sprintf(errmsg, "out of memory");
+               return NULL;
+       }
+       /* assert: cs is '\n' terminated */
+       for (s = cs; *s != '\n'; s++)
+               ;
+       if (s - cs >= LINECHARS) {
+               sprintf(errmsg, "line too long");
+               free(lp);
+               return NULL;
+       }
+       len = s - cs;
+       /* out of position */
+       if (seek_write) {
+               if (fseek(sfp, 0L, SEEK_END) < 0) {
+                       fprintf(stderr, "%s\n", strerror(errno));
+                       sprintf(errmsg, "cannot seek temp file");
+                       free(lp);
+                       return NULL;
+               }
+               sfseek = ftell(sfp);
+               seek_write = 0;
+       }
+       /* assert: SPL1() */
+       if ((ct = fwrite(cs, sizeof(char), len, sfp)) < 0 || ct != len) {
+               sfseek = -1;
+               fprintf(stderr, "%s\n", strerror(errno));
+               sprintf(errmsg, "cannot write temp file");
+               free(lp);
+               return NULL;
+       }
+       lp->len = len;
+       lp->seek  = sfseek;
+       add_line_node(lp);
+       sfseek += len;                  /* update file position */
+       return ++s;
+}
+
+
+/* add_line_node: add a line node in the editor buffer after the current line */
+void
+add_line_node(line_t *lp)
+{
+       line_t *cp;
+
+       cp = get_addressed_line_node(current_addr); /* this get_addressed_line_node last! */
+       INSQUE(lp, cp);
+       addr_last++;
+       current_addr++;
+}
+
+
+/* get_line_node_addr: return line number of pointer */
+long
+get_line_node_addr(line_t *lp)
+{
+       line_t *cp = &buffer_head;
+       long n = 0;
+
+       while (cp != lp && (cp = cp->q_forw) != &buffer_head)
+               n++;
+       if (n && cp == &buffer_head) {
+               sprintf(errmsg, "invalid address");
+               return ERR;
+       }
+       return n;
+}
+
+
+/* get_addressed_line_node: return pointer to a line node in the editor buffer */
+line_t *
+get_addressed_line_node(long n)
+{
+       static line_t *lp = &buffer_head;
+       static long on = 0;
+
+       SPL1();
+       if (n > on) {
+               if (n <= (on + addr_last) >> 1) {
+                       for (; on < n; on++)
+                               lp = lp->q_forw;
+               } else {
+                       lp = buffer_head.q_back;
+                       for (on = addr_last; on > n; on--)
+                               lp = lp->q_back;
+               }
+       } else {
+               if (n >= on >> 1) {
+                       for (; on > n; on--)
+                               lp = lp->q_back;
+               } else {
+                       lp = &buffer_head;
+                       for (on = 0; on < n; on++)
+                               lp = lp->q_forw;
+               }
+       }
+       SPL0();
+       return lp;
+}
+
+
+char *sfn = NULL;                              /* scratch file name */
+
+/* open_sbuf: open scratch file */
+int
+open_sbuf(void)
+{
+       int u, fd;
+       const char *tmp;
+       size_t s;
+
+       isbinary = newline_added = 0;
+       fd = -1;
+       u = umask(077);
+
+       if ((tmp = getenv("TMPDIR")) == NULL)
+               tmp = _PATH_TMP;
+       
+       if ((s = strlen(tmp)) == 0 || tmp[s - 1] == '/')
+               (void)asprintf(&sfn, "%sed.XXXXXX", tmp);
+       else
+               (void)asprintf(&sfn, "%s/ed.XXXXXX", tmp);
+       if (sfn == NULL) {
+               warn(NULL);
+               sprintf(errmsg, "could not allocate memory");
+               umask(u);
+               return ERR;
+       }
+               
+
+       if ((fd = mkstemp(sfn)) == -1 || (sfp = fdopen(fd, "w+")) == NULL) {
+               if (fd != -1)
+                       close(fd);
+               warn("%s", sfn);
+               sprintf(errmsg, "cannot open temp file");
+               umask(u);
+               return ERR;
+       }
+       umask(u);
+       return 0;
+}
+
+
+/* close_sbuf: close scratch file */
+int
+close_sbuf(void)
+{
+       if (sfp) {
+               if (fclose(sfp) < 0) {
+                       fprintf(stderr, "%s: %s\n", sfn, strerror(errno));
+                       sprintf(errmsg, "cannot close temp file");
+                       return ERR;
+               }
+               sfp = NULL;
+               if (sfn) {
+                       unlink(sfn);
+                       free(sfn);
+                       sfn = NULL;
+               }
+       }
+       sfseek = seek_write = 0;
+       return 0;
+}
+
+
+/* quit: remove_lines scratch file and exit */
+void
+quit(int n)
+{
+       if (sfp) {
+               fclose(sfp);
+               if (sfn) {
+                       unlink(sfn);
+                       free(sfn);
+                       sfn = NULL;
+               }
+       }
+       exit(n);
+       /* NOTREACHED */
+}
+
+
+unsigned char ctab[256];               /* character translation table */
+
+/* init_buffers: open scratch buffer; initialize line queue */
+void
+init_buffers(void)
+{
+       int i = 0;
+
+       /* Read stdin one character at a time to avoid i/o contention 
+          with shell escapes invoked by nonterminal input, e.g.,
+          ed - <<EOF
+          !cat
+          hello, world
+          EOF */
+       setbuffer(stdin, stdinbuf, 1);
+       if (open_sbuf() < 0)
+               quit(2);
+       REQUE(&buffer_head, &buffer_head);
+       for (i = 0; i < 256; i++)
+               ctab[i] = i;
+}
+
+
+/* translit_text: translate characters in a string */
+char *
+translit_text(char *s, int len, int from, int to)
+{
+       static int i = 0;
+
+       unsigned char *us;
+
+       ctab[i] = i;                    /* restore table to initial state */
+       ctab[i = from] = to;
+       for (us = (unsigned char *) s; len-- > 0; us++)
+               *us = ctab[*us];
+       return s;
+}
diff --git a/bin/ed/cbc.c b/bin/ed/cbc.c
new file mode 100644 (file)
index 0000000..b444306
--- /dev/null
@@ -0,0 +1,460 @@
+/*     $NetBSD: cbc.c,v 1.22 2010/06/09 19:20:18 riz Exp $     */
+
+/* cbc.c: This file contains the encryption routines for the ed line editor */
+/*-
+ * Copyright (c) 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     from: @(#)bdes.c        5.5 (Berkeley) 6/27/91
+ */
+
+/*-
+ * Copyright (c) 1993 Andrew Moore, Talke Studio.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     from: @(#)bdes.c        5.5 (Berkeley) 6/27/91
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char *rcsid = "@(#)cbc.c,v 1.2 1994/02/01 00:34:36 alm Exp";
+#else
+__RCSID("$NetBSD: cbc.c,v 1.22 2010/06/09 19:20:18 riz Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <errno.h>
+#include <pwd.h>
+#ifdef DES
+#include <time.h>
+#endif
+
+#include "ed.h"
+
+
+/*
+ * Define a divisor for rand() that yields a uniform distribution in the
+ * range 0-255.
+ */
+#define        RAND_DIV (((unsigned) RAND_MAX + 1) >> 8)
+
+/*
+ * BSD and System V systems offer special library calls that do
+ * block move_liness and fills, so if possible we take advantage of them
+ */
+#define        MEMCPY(dest,src,len)    memcpy((dest),(src),(len))
+#define        MEMZERO(dest,len)       memset((dest), 0, (len))
+
+/* Hide the calls to the primitive encryption routines. */
+#define        DES_KEY(buf) \
+       if (des_setkey(buf)) \
+               des_error("des_setkey");
+#define        DES_XFORM(buf) \
+       if (des_cipher(buf, buf, 0L, (inverse ? -1 : 1))) \
+               des_error("des_cipher");
+
+/*
+ * read/write - no error checking
+ */
+#define        READ(buf, n, fp)        fread(buf, sizeof(char), n, fp)
+#define WRITE(buf, n, fp)      fwrite(buf, sizeof(char), n, fp)
+
+/*
+ * some things to make references easier
+ */
+typedef char Desbuf[8];
+#define        CHAR(x,i)       (x[i])
+#define        UCHAR(x,i)      (x[i])
+#define        BUFFER(x)       (x)
+#define        UBUFFER(x)      (x)
+
+#ifdef DES
+/*
+ * global variables and related macros
+ */
+
+static Desbuf ivec;                    /* initialization vector */
+static Desbuf pvec;                    /* padding vector */
+static char bits[] = {                 /* used to extract bits from a char */
+       '\200', '\100', '\040', '\020', '\010', '\004', '\002', '\001'
+};
+static int pflag;                      /* 1 to preserve parity bits */
+
+static char des_buf[8];        /* shared buffer for get_des_char/put_des_char */
+static int des_ct = 0;         /* count for get_des_char/put_des_char */
+static int des_n = 0;          /* index for put_des_char/get_des_char */
+#endif
+
+
+#ifdef DES
+static void des_error(const char *);
+static int hex_to_binary(int, int);
+static void expand_des_key(char *, char *);
+static void set_des_key(char *);
+static int cbc_decode(char *, FILE *);
+static int cbc_encode(char *, int, FILE *);
+#endif
+
+
+/* init_des_cipher: initialize DES */
+void
+init_des_cipher(void)
+{
+#ifdef DES
+       int i;
+
+       des_ct = des_n = 0;
+
+       /* initialize the initialization vector */
+       MEMZERO(ivec, 8);
+
+       /* intialize the padding vector */
+       srand((unsigned) time((time_t *) 0));
+       for (i = 0; i < 8; i++)
+               CHAR(pvec, i) = (char) (rand()/RAND_DIV);
+#endif
+}
+
+
+/* get_des_char: return next char in an encrypted file */
+int
+get_des_char(FILE *fp)
+{
+#ifdef DES
+       if (des_n >= des_ct) {
+               des_n = 0;
+               des_ct = cbc_decode(des_buf, fp);
+       }
+       return (des_ct > 0) ? (unsigned char) des_buf[des_n++] : EOF;
+#else
+       return EOF;
+#endif
+}
+
+
+/* put_des_char: write a char to an encrypted file; return char written */
+int
+put_des_char(int c, FILE *fp)
+{
+#ifdef DES
+       if (des_n == sizeof des_buf) {
+               des_ct = cbc_encode(des_buf, des_n, fp);
+               des_n = 0;
+       }
+       return (des_ct >= 0) ? (unsigned char) (des_buf[des_n++] = c) : EOF;
+#else
+       return EOF;
+#endif
+}
+
+
+/* flush_des_file: flush an encrypted file's output; return status */
+int
+flush_des_file(FILE *fp)
+{
+#ifdef DES
+       if (des_n == sizeof des_buf) {
+               des_ct = cbc_encode(des_buf, des_n, fp);
+               des_n = 0;
+       }
+       return (des_ct >= 0 && cbc_encode(des_buf, des_n, fp) >= 0) ? 0 : EOF;
+#else
+       return EOF;
+#endif
+}
+
+#ifdef DES
+/*
+ * get keyword from tty or stdin
+ */
+int
+get_keyword(void)
+{
+       char *p;                /* used to obtain the key */
+       Desbuf msgbuf;                  /* I/O buffer */
+
+       /*
+        * get the key
+        */
+       if (*(p = getpass("Enter key: "))) {
+
+               /*
+                * copy it, nul-padded, into the key area
+                */
+               expand_des_key(BUFFER(msgbuf), p);
+               MEMZERO(p, _PASSWORD_LEN);
+               set_des_key(msgbuf);
+               MEMZERO(msgbuf, sizeof msgbuf);
+               return 1;
+       }
+       return 0;
+}
+
+
+/*
+ * print a warning message and, possibly, terminate
+ */
+static void
+des_error(const char *s /* the message */)
+{
+       (void)sprintf(errmsg, "%s", s ? s : strerror(errno));
+}
+
+/*
+ * map a hex character to an integer
+ */
+static int
+hex_to_binary(int c /* char to be converted */,
+             int radix /* base (2 to 16) */)
+{
+       switch(c) {
+       case '0':               return(0x0);
+       case '1':               return(0x1);
+       case '2':               return(radix > 2 ? 0x2 : -1);
+       case '3':               return(radix > 3 ? 0x3 : -1);
+       case '4':               return(radix > 4 ? 0x4 : -1);
+       case '5':               return(radix > 5 ? 0x5 : -1);
+       case '6':               return(radix > 6 ? 0x6 : -1);
+       case '7':               return(radix > 7 ? 0x7 : -1);
+       case '8':               return(radix > 8 ? 0x8 : -1);
+       case '9':               return(radix > 9 ? 0x9 : -1);
+       case 'A': case 'a':     return(radix > 10 ? 0xa : -1);
+       case 'B': case 'b':     return(radix > 11 ? 0xb : -1);
+       case 'C': case 'c':     return(radix > 12 ? 0xc : -1);
+       case 'D': case 'd':     return(radix > 13 ? 0xd : -1);
+       case 'E': case 'e':     return(radix > 14 ? 0xe : -1);
+       case 'F': case 'f':     return(radix > 15 ? 0xf : -1);
+       }
+       /*
+        * invalid character
+        */
+       return(-1);
+}
+
+/*
+ * convert the key to a bit pattern
+ */
+static void
+expand_des_key(char *obuf /* bit pattern */, char *inbuf /* the key itself */)
+{
+       int i, j;                       /* counter in a for loop */
+       int nbuf[64];                   /* used for hex/key translation */
+
+       /*
+        * leading '0x' or '0X' == hex key
+        */
+       if (inbuf[0] == '0' && (inbuf[1] == 'x' || inbuf[1] == 'X')) {
+               inbuf = &inbuf[2];
+               /*
+                * now translate it, bombing on any illegal hex digit
+                */
+               for (i = 0; inbuf[i] && i < 16; i++)
+                       if ((nbuf[i] = hex_to_binary((int) inbuf[i], 16)) == -1)
+                               des_error("bad hex digit in key");
+               while (i < 16)
+                       nbuf[i++] = 0;
+               for (i = 0; i < 8; i++)
+                       obuf[i] =
+                           ((nbuf[2*i]&0xf)<<4) | (nbuf[2*i+1]&0xf);
+               /* preserve parity bits */
+               pflag = 1;
+               return;
+       }
+       /*
+        * leading '0b' or '0B' == binary key
+        */
+       if (inbuf[0] == '0' && (inbuf[1] == 'b' || inbuf[1] == 'B')) {
+               inbuf = &inbuf[2];
+               /*
+                * now translate it, bombing on any illegal binary digit
+                */
+               for (i = 0; inbuf[i] && i < 16; i++)
+                       if ((nbuf[i] = hex_to_binary((int) inbuf[i], 2)) == -1)
+                               des_error("bad binary digit in key");
+               while (i < 64)
+                       nbuf[i++] = 0;
+               for (i = 0; i < 8; i++)
+                       for (j = 0; j < 8; j++)
+                               obuf[i] = (obuf[i]<<1)|nbuf[8*i+j];
+               /* preserve parity bits */
+               pflag = 1;
+               return;
+       }
+       /*
+        * no special leader -- ASCII
+        */
+       (void)strncpy(obuf, inbuf, 8);
+}
+
+/*****************
+ * DES FUNCTIONS *
+ *****************/
+/*
+ * This sets the DES key and (if you're using the deszip version)
+ * the direction of the transformation.  This uses the Sun
+ * to map the 64-bit key onto the 56 bits that the key schedule
+ * generation routines use: the old way, which just uses the user-
+ * supplied 64 bits as is, and the new way, which resets the parity
+ * bit to be the same as the low-order bit in each character.  The
+ * new way generates a greater variety of key schedules, since many
+ * systems set the parity (high) bit of each character to 0, and the
+ * DES ignores the low order bit of each character.
+ */
+static void
+set_des_key(Desbuf buf /* key block */)
+{
+       int i, j;                               /* counter in a for loop */
+       int par;                                /* parity counter */
+
+       /*
+        * if the parity is not preserved, flip it
+        */
+       if (!pflag) {
+               for (i = 0; i < 8; i++) {
+                       par = 0;
+                       for (j = 1; j < 8; j++)
+                               if ((bits[j]&UCHAR(buf, i)) != 0)
+                                       par++;
+                       if ((par&01) == 01)
+                               UCHAR(buf, i) = UCHAR(buf, i)&0177;
+                       else
+                               UCHAR(buf, i) = (UCHAR(buf, i)&0177)|0200;
+               }
+       }
+
+       DES_KEY(UBUFFER(buf));
+}
+
+
+/*
+ * This encrypts using the Cipher Block Chaining mode of DES
+ */
+static int
+cbc_encode(char *msgbuf, int n, FILE *fp)
+{
+       int inverse = 0;        /* 0 to encrypt, 1 to decrypt */
+
+       /*
+        * do the transformation
+        */
+       if (n == 8) {
+               for (n = 0; n < 8; n++)
+                       CHAR(msgbuf, n) ^= CHAR(ivec, n);
+               DES_XFORM(UBUFFER(msgbuf));
+               MEMCPY(BUFFER(ivec), BUFFER(msgbuf), 8);
+               return WRITE(BUFFER(msgbuf), 8, fp);
+       }
+       /*
+        * at EOF or last block -- in either case, the last byte contains
+        * the character representation of the number of bytes in it
+        */
+/*
+       MEMZERO(msgbuf +  n, 8 - n);
+*/
+       /*
+        *  Pad the last block randomly
+        */
+       (void)MEMCPY(BUFFER(msgbuf + n), BUFFER(pvec), 8 - n);
+       CHAR(msgbuf, 7) = n;
+       for (n = 0; n < 8; n++)
+               CHAR(msgbuf, n) ^= CHAR(ivec, n);
+       DES_XFORM(UBUFFER(msgbuf));
+       return WRITE(BUFFER(msgbuf), 8, fp);
+}
+
+/*
+ * This decrypts using the Cipher Block Chaining mode of DES
+ */
+static int
+cbc_decode(char *msgbuf /* I/O buffer */,
+          FILE *fp /* input file descriptor */)
+{
+       Desbuf inbuf;   /* temp buffer for initialization vector */
+       int n;          /* number of bytes actually read */
+       int c;          /* used to test for EOF */
+       int inverse = 1;        /* 0 to encrypt, 1 to decrypt */
+
+       if ((n = READ(BUFFER(msgbuf), 8, fp)) == 8) {
+               /*
+                * do the transformation
+                */
+               MEMCPY(BUFFER(inbuf), BUFFER(msgbuf), 8);
+               DES_XFORM(UBUFFER(msgbuf));
+               for (c = 0; c < 8; c++)
+                       UCHAR(msgbuf, c) ^= UCHAR(ivec, c);
+               MEMCPY(BUFFER(ivec), BUFFER(inbuf), 8);
+               /*
+                * if the last one, handle it specially
+                */
+               if ((c = fgetc(fp)) == EOF) {
+                       n = CHAR(msgbuf, 7);
+                       if (n < 0 || n > 7) {
+                               des_error("decryption failed (block corrupted)");
+                               return EOF;
+                       }
+               } else
+                       (void)ungetc(c, fp);
+               return n;
+       }
+       if (n > 0)
+               des_error("decryption failed (incomplete block)");
+       else if (n < 0)
+               des_error("cannot read file");
+       return EOF;
+}
+#endif /* DES */
diff --git a/bin/ed/ed.1 b/bin/ed/ed.1
new file mode 100644 (file)
index 0000000..c39e91b
--- /dev/null
@@ -0,0 +1,972 @@
+.\"    $NetBSD: ed.1,v 1.30 2010/05/14 02:09:58 joerg Exp $
+.\"    $OpenBSD: ed.1,v 1.42 2003/07/27 13:25:43 jmc Exp $
+.\"
+.\" Copyright (c) 1993 Andrew Moore, Talke Studio.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd January 23, 2002
+.Dt ED 1
+.Os
+.Sh NAME
+.Nm ed
+.Nd text editor
+.Sh SYNOPSIS
+.Nm
+.Op Fl
+.Op Fl Esx
+.Op Fl p Ar string
+.Op Ar file
+.Sh DESCRIPTION
+.Nm
+is a line-oriented text editor.
+It is used to create, display, modify, and otherwise manipulate text files.
+If invoked with a
+.Ar file
+argument, then a copy of
+.Ar file
+is read into the editor's buffer.
+Changes are made to this copy and not directly to
+.Ar file
+itself.
+Upon quitting
+.Nm ,
+any changes not explicitly saved with a
+.Ic w
+command are lost.
+.Pp
+Editing is done in two distinct modes:
+.Em command
+and
+.Em input .
+When first invoked,
+.Nm
+is in command mode.
+In this mode, commands are read from the standard input and
+executed to manipulate the contents of the editor buffer.
+.Pp
+A typical command might look like:
+.Bd -literal -offset indent
+,s/old/new/g
+.Ed
+.Pp
+which replaces all occurrences of the string
+.Pa old
+with
+.Pa new .
+.Pp
+When an input command, such as
+.Ic a
+(append),
+.Ic i
+(insert),
+or
+.Ic c
+(change) is given,
+.Nm
+enters input mode.
+This is the primary means of adding text to a file.
+In this mode, no commands are available;
+instead, the standard input is written directly to the editor buffer.
+Lines consist of text up to and including a newline character.
+Input mode is terminated by entering a single period
+.Pq Ql \&.
+on a line.
+.Pp
+All
+.Nm
+commands operate on whole lines or ranges of lines; e.g.,
+the
+.Ic d
+command deletes lines; the
+.Ic m
+command moves lines, and so on.
+It is possible to modify only a portion of a line by means of replacement,
+as in the example above.
+However, even here, the
+.Ic s
+command is applied to whole lines at a time.
+.Pp
+In general,
+.Nm
+commands consist of zero or more line addresses, followed by a single
+character command and possibly additional parameters; i.e.,
+commands have the structure:
+.Bd -literal -offset indent
+[address [,address]]command[parameters]
+.Ed
+.Pp
+The address(es) indicate the line or range of lines to be affected by the
+command.
+If fewer addresses are given than the command accepts, then
+default addresses are supplied.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl
+Same as the
+.Fl s
+option (deprecated).
+.It Fl E
+Enables the use of extended regular expressions instead of the basic
+regular expressions that are normally used.
+.It Fl p Ar string
+Specifies a command prompt.
+This may be toggled on and off with the
+.Ic P
+command.
+.It Fl s
+Suppress diagnostics.
+This should be used if
+.Nm
+standard input is from a script.
+.It Fl x
+Prompt for an encryption key to be used in subsequent reads and writes
+(see the
+.Ic x
+command).
+.It Ar file
+Specifies the name of a file to read.
+If
+.Ar file
+is prefixed with a
+bang
+.Pq Ql \&! ,
+then it is interpreted as a shell command.
+In this case, what is read is the standard output of
+.Ar file
+executed via
+.Xr sh 1 .
+To read a file whose name begins with a bang, prefix the
+name with a backslash
+.Pq Ql \e .
+The default filename is set to
+.Ar file
+only if it is not prefixed with a bang.
+.El
+.Ss LINE ADDRESSING
+An address represents the number of a line in the buffer.
+.Nm
+maintains a
+.Em 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.
+.Pp
+A line address is
+constructed from one of the bases in the list below, optionally followed
+by a numeric offset.
+The offset may include any combination of digits, operators (i.e.,
+.Sq + ,
+.Sq - ,
+and
+.Sq ^ ) ,
+and whitespace.
+Addresses are read from left to right, and their values are computed
+relative to the current address.
+.Pp
+One exception to the rule that addresses represent line numbers is the
+address
+.Em 0
+(zero).
+This means
+.Dq before the first line ,
+and is legal wherever it makes sense.
+.Pp
+An address range is two addresses separated either by a comma or semi-colon.
+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
+.Em n Ns No -tuple
+of addresses is given where
+.Em n \*[Gt] 2 ,
+then the corresponding range is determined by the last two addresses in the
+.Em n Ns No -tuple.
+If only one address is expected, then the last address is used.
+.Pp
+Each address in a comma-delimited range is interpreted relative to the
+current address.
+In a semi-colon-delimited range, the first address is
+used to set the current address, and the second address is interpreted
+relative to the first.
+.Pp
+The following address symbols are recognized:
+.Bl -tag -width Ds
+.It Em \&.
+The current line (address) in the buffer.
+.It Em $
+The last line in the buffer.
+.It Em n
+The
+.Em n Ns No th
+line in the buffer where
+.Em n
+is a number in the range
+.Em [0,$] .
+.It Em - No or Em ^
+The previous line.
+This is equivalent to
+.Em -1
+and may be repeated with cumulative effect.
+.It Em -n No or Em ^n
+The
+.Em n Ns No th
+previous line, where
+.Em n
+is a non-negative number.
+.It Em +
+The next line.
+This is equivalent to
+.Em +1
+and may be repeated with cumulative effect.
+.It Em +n
+The
+.Em n Ns No th
+next line, where
+.Em n
+is a non-negative number.
+.It Em whitespace Em n
+.Em whitespace
+followed by a number
+.Em n
+is interpreted as
+.Sq Em +n .
+.It Em \&, No or Em %
+The first through last lines in the buffer.
+This is equivalent to the address range
+.Em 1,$ .
+.It Em \&;
+The current through last lines in the buffer.
+This is equivalent to the address range
+.Em .,$ .
+.It Em / Ns Ar re Ns Em /
+The next line containing the regular expression
+.Ar re .
+The search wraps to the beginning of the buffer and continues down to the
+current line, if necessary.
+.Em //
+repeats the last search.
+.It Em \&? Ns Ar re Ns Em \&?
+The previous line containing the regular expression
+.Ar re .
+The search wraps to the end of the buffer and continues up to the
+current line, if necessary.
+.Em ??
+repeats the last search.
+.It Em \&\' Ns Ar lc
+The line previously marked by a
+.Ic k
+(mark) command, where
+.Ar lc
+is a lower case letter.
+.El
+.Ss REGULAR EXPRESSIONS
+Regular expressions are patterns used in selecting text.
+For example, the
+.Nm
+command
+.Bd -literal -offset indent
+g/string/
+.Ed
+.Pp
+prints all lines containing
+.Em string .
+Regular expressions are also used by the
+.Ic s
+command for selecting old text to be replaced with new.
+.Pp
+In addition to specifying string literals, regular expressions can
+represent classes of strings.
+Strings thus represented are said to be matched by the
+corresponding regular expression.
+If it is possible for a regular expression to match several strings in
+a line, then the leftmost longest match is the one selected.
+.Pp
+The following symbols are used in constructing regular expressions:
+.Bl -tag -width Dsasdfsd
+.It Em c
+Any character
+.Em c
+not listed below, including
+.Sq { ,
+.Sq } ,
+.Sq \&( ,
+.Sq \&) ,
+.Sq \*[Lt] ,
+and
+.Sq \*[Gt]
+matches itself.
+.It Em \ec
+Any backslash-escaped character
+.Em c ,
+except for
+.Sq { ,
+.Sq } ,
+.Sq \&( ,
+.Sq \&) ,
+.Sq \*[Lt] ,
+and
+.Sq \*[Gt]
+matches itself.
+.It Em \&.
+Matches any single character.
+.It Em [char-class]
+Matches any single character in the character class
+.Em char-class .
+See
+.Sx CHARACTER CLASSES
+below for further information.
+.It Em [^char-class]
+Matches any single character, other than newline, not in the
+character class
+.Em char-class .
+.It Em ^
+If
+.Em ^
+is the first character of a regular expression, then it
+anchors the regular expression to the beginning of a line.
+Otherwise, it matches itself.
+.It Em $
+If
+.Em $
+is the last character of a regular expression,
+it anchors the regular expression to the end of a line.
+Otherwise, it matches itself.
+.It Em \e\*[Lt]
+Anchors the single character regular expression or subexpression
+immediately following it to the beginning of a word.
+(This may not be available.)
+.It Em \e\*[Gt]
+Anchors the single character regular expression or subexpression
+immediately following it to the end of a word.
+(This may not be available.)
+.It Em \e( Ns Ar re Ns Em \e)
+Defines a subexpression
+.Ar re .
+Subexpressions may be nested.
+A subsequent backreference of the form
+.Em \en ,
+where
+.Em n
+is a number in the range [1,9], expands to the text matched by the
+.Em n Ns No th
+subexpression.
+For example, the regular expression
+.Em \e(.*\e)\e1
+matches any string consisting of identical adjacent substrings.
+Subexpressions are ordered relative to their left delimiter.
+.It Em *
+Matches the single character regular expression or subexpression
+immediately preceding it zero or more times.
+If
+.Em *
+is the first character of a regular expression or subexpression,
+then it matches itself.
+The
+.Em *
+operator sometimes yields unexpected results.
+For example, the regular expression
+.Em b*
+matches the beginning of the string
+.Em abbb
+(as opposed to the substring
+.Em bbb ) ,
+since a null match is the only leftmost match.
+.Sm off
+.It Em \e{ No n,m Em \e}\ \e{ No n, Em \e}\ \& Em \e{ No n Em \e}
+.Sm on
+Matches the single character regular expression or subexpression
+immediately preceding it at least
+.Em n
+and at most
+.Em m
+times.
+If
+.Em m
+is omitted, then it matches at least
+.Em n
+times.
+If the comma is also omitted, then it matches exactly
+.Em n
+times.
+.El
+.Pp
+Additional regular expression operators may be defined depending on the
+particular
+.Xr regex 3
+implementation.
+.Ss CHARACTER CLASSES
+A character class specifies a set of characters. It is written within
+square brackets
+.Pq []
+and in its most basic form contains just the characters in the set.
+.Pp
+To include a
+.Sq \&]
+in a character class, it must be the first character.
+A range of characters may be specified by separating the end characters
+of the range with a
+.Sq \&- ,
+e.g.,
+.Sq a-z
+specifies the lower case characters.
+.Pp
+The following literals can also be used within character classes as
+shorthand for particular sets of characters:
+.Bl -tag -offset indent -compact -width [:blahblah:]
+.It [:alnum:]
+Alphanumeric characters.
+.It [:cntrl:]
+Control characters.
+.It [:lower:]
+Lowercase alphabetic characters.
+.It [:space:]
+Whitespace (space, tab, newline, form feed, etc.)
+.It [:alpha:]
+Alphabetic characters.
+.It [:digit:]
+Numeric characters (digits).
+.It [:print:]
+Printable characters.
+.It [:upper:]
+Uppercase alphabetic characters.
+.It [:blank:]
+Blank characters (space and tab).
+.It [:graph:]
+Graphical characters (printing nonblank characters).
+.It [:punct:]
+Punctuation characters.
+.It [:xdigit:]
+Hexadecimal digits.
+.El
+If
+.Sq \&-
+appears as the first or last character of a character class, then
+it matches itself.
+All other characters in a character class match themselves.
+.Pp
+Patterns in
+a character class
+of the form
+.Em [.col-elm.]
+or
+.Em [=col-elm=]
+where
+.Em col-elm
+is a
+.Em collating element
+are interpreted according to
+.Xr locale 5
+(not currently supported).
+See
+.Xr regex 3
+for an explanation of these constructs.
+.Ss COMMANDS
+All
+.Nm
+commands are single characters, though some require additional parameters.
+If a command's parameters extend over several lines, then
+each line except for the last must be terminated with a backslash
+.Pq Ql \e .
+.Pp
+In general, at most one command is allowed per line.
+However, most commands accept a print suffix, which is any of
+.Ic p
+(print),
+.Ic l
+(list),
+or
+.Ic n
+(enumerate), to print the last line affected by the command.
+.Pp
+An interrupt (typically ^C) has the effect of aborting the current command
+and returning the editor to command mode.
+.Pp
+.Nm
+recognizes the following commands.
+The commands are shown together with
+the default address or address range supplied if none is
+specified (in parentheses), and other possible arguments on the right.
+.Bl -tag -width Dxxs
+.It (.) Ns Ic a
+Appends text to the buffer after the addressed line.
+Text is entered in input mode.
+The current address is set to last line entered.
+.It (.,.) Ns Ic 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.
+.It (.,.) Ns Ic 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.
+.It Ic e Ar file
+Edits
+.Ar file ,
+and sets the default filename.
+If
+.Ar 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.
+.It Ic e Ar !command
+Edits the standard output of
+.Ar command ,
+(see
+.Ic \&! Ar command
+below).
+The default filename is unchanged.
+Any lines in the buffer are deleted before the output of
+.Em command
+is read.
+The current address is set to the last line read.
+.It Ic E Ar file
+Edits
+.Ar file
+unconditionally.
+This is similar to the
+.Ic e
+command, except that unwritten changes are discarded without warning.
+The current address is set to the last line read.
+.It Ic f Ar file
+Sets the default filename to
+.Ar file .
+If
+.Ar file
+is not specified, then the default unescaped filename is printed.
+.It (1,$) Ns Ic g Ns Ar /re/command-list
+Applies
+.Ar command-list
+to each of the addressed lines matching a regular expression
+.Ar re .
+The current address is set to the line currently matched before
+.Ar command-list
+is executed.
+At the end of the
+.Ic g
+command, the current address is set to the last line affected by
+.Ar command-list .
+.Pp
+Each command in
+.Ar command-list
+must be on a separate line,
+and every line except for the last must be terminated by a backslash
+.Pq Sq \e .
+Any commands are allowed, except for
+.Ic g ,
+.Ic G ,
+.Ic v ,
+and
+.Ic V .
+A newline alone in
+.Ar command-list
+is equivalent to a
+.Ic p
+command.
+.It (1,$) Ns Ic G Ns Ar /re/
+Interactively edits the addressed lines matching a regular expression
+.Ar re .
+For each matching line, the line is printed, the current address is set,
+and the user is prompted to enter a
+.Ar command-list .
+At the end of the
+.Ic G
+command, the current address is set to the last line affected by (the last)
+.Ar command-list .
+.Pp
+The format of
+.Ar command-list
+is the same as that of the
+.Ic g
+command.
+A newline alone acts as a null command list.
+A single
+.Sq \*[Am]
+repeats the last non-null command list.
+.It Ic H
+Toggles the printing of error explanations.
+By default, explanations are not printed.
+It is recommended that
+.Nm
+scripts begin with this command to aid in debugging.
+.It Ic h
+Prints an explanation of the last error.
+.It (.) Ns Ic i
+Inserts text in the buffer before the current line.
+Text is entered in input mode.
+The current address is set to the last line entered.
+.It (.,.+1) Ns Ic 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.
+.It (.) Ns Ic k Ns Ar lc
+Marks a line with a lower case letter
+.Ar lc .
+The line can then be addressed as
+.Ar \&'lc
+(i.e., a single quote followed by
+.Ar lc )
+in subsequent commands.
+The mark is not cleared until the line is deleted or otherwise modified.
+.It (.,.) Ns Ic l
+Prints the addressed lines unambiguously.
+If a single line fills more than one screen (as might be the case
+when viewing a binary file, for instance), a
+.Dq --More--
+prompt is printed on the last line.
+.Nm
+waits until the RETURN key is pressed before displaying the next screen.
+The current address is set to the last line printed.
+.It (.,.) Ns Ic m Ns No (.)
+Moves lines in the buffer.
+The addressed lines are moved to after the
+right-hand destination address, which may be the address
+.Em 0
+(zero).
+The current address is set to the last line moved.
+.It (.,.) Ns Ic n
+Prints the addressed lines along with their line numbers.
+The current address is set to the last line printed.
+.It (.,.) Ns Ic p
+Prints the addressed lines.
+The current address is set to the last line printed.
+.It Ic P
+Toggles the command prompt on and off.
+Unless a prompt was specified with the command-line option
+.Fl p Ar string ,
+the command prompt is by default turned off.
+.It Ic q
+Quits
+.Nm .
+.It Ic Q
+Quits
+.Nm
+unconditionally.
+This is similar to the
+.Ic q
+command, except that unwritten changes are discarded without warning.
+.It ($) Ns Ic r Ar file
+Reads
+.Ar file
+to after the addressed line.
+If
+.Ar file
+is not specified, then the default filename is used.
+If there was no default filename prior to the command,
+then the default filename is set to
+.Ar file .
+Otherwise, the default filename is unchanged.
+The current address is set to the last line read.
+.It ($) Ns Ic r Ar !command
+Reads to after the addressed line the standard output of
+.Ar command ,
+(see the
+.Ic \&!
+command below).
+The default filename is unchanged.
+The current address is set to the last line read.
+.Sm off
+.It (.,.) Ic s Ar /re/replacement/ , \ (.,.) \
+Ic s Ar /re/replacement/ Em g , Ar \ (.,.) \
+Ic s Ar /re/replacement/ Em n
+.Sm on
+Replaces text in the addressed lines matching a regular expression
+.Ar re
+with
+.Ar replacement .
+By default, only the first match in each line is replaced.
+If the
+.Em g
+(global) suffix is given, then every match to be replaced.
+The
+.Em n
+suffix, where
+.Em n
+is a positive number, causes only the
+.Em n Ns No 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 the last line affected.
+.Pp
+.Ar re
+and
+.Ar replacement
+may be delimited by any character other than space and newline
+(see the
+.Ic s
+command below).
+If one or two of the last delimiters is omitted, then the last line
+affected is printed as though the print suffix
+.Em p
+were specified.
+.Pp
+An unescaped
+.Ql \*[Am]
+in
+.Ar replacement
+is replaced by the currently matched text.
+The character sequence
+.Em \em ,
+where
+.Em m
+is a number in the range [1,9], is replaced by the
+.Em m Ns No th
+backreference expression of the matched text.
+If
+.Ar replacement
+consists of a single
+.Ql % ,
+then
+.Ar replacement
+from the last substitution is used.
+Newlines may be embedded in
+.Ar replacement
+if they are escaped with a backslash
+.Pq Ql \e .
+.It (.,.) Ns Ic s
+Repeats the last substitution.
+This form of the
+.Ic s
+command accepts a count suffix
+.Em n ,
+or any combination of the characters
+.Em r ,
+.Em g ,
+and
+.Em p .
+If a count suffix
+.Em n
+is given, then only the
+.Em n Ns No th
+match is replaced.
+The
+.Em r
+suffix causes
+the regular expression of the last search to be used instead of
+that of the last substitution.
+The
+.Em g
+suffix toggles the global suffix of the last substitution.
+The
+.Em p
+suffix toggles the print suffix of the last substitution.
+The current address is set to the last line affected.
+.It (.,.) Ns Ic t Ns No (.)
+Copies (i.e., transfers) the addressed lines to after the right-hand
+destination address, which may be the address
+.Em 0
+(zero).
+The current address is set to the last line copied.
+.It Ic u
+Undoes the last command and restores the current address
+to what it was before the command.
+The global commands
+.Ic g ,
+.Ic G ,
+.Ic v ,
+and
+.Ic V
+are treated as a single command by undo.
+.Ic u
+is its own inverse.
+.It (1,$) Ns Ic v Ns Ar /re/command-list
+Applies
+.Ar command-list
+to each of the addressed lines not matching a regular expression
+.Ar re .
+This is similar to the
+.Ic g
+command.
+.It (1,$) Ns Ic V Ns Ar /re/
+Interactively edits the addressed lines not matching a regular expression
+.Ar re .
+This is similar to the
+.Ic G
+command.
+.It (1,$) Ns Ic w Ar file
+Writes the addressed lines to
+.Ar file .
+Any previous contents of
+.Ar file
+are lost without warning.
+If there is no default filename, then the default filename is set to
+.Ar file ,
+otherwise it is unchanged.
+If no filename is specified, then the default filename is used.
+The current address is unchanged.
+.It (1,$) Ns Ic wq Ar file
+Writes the addressed lines to
+.Ar file ,
+and then executes a
+.Ic q
+command.
+.It (1,$) Ns Ic w Ar !command
+Writes the addressed lines to the standard input of
+.Ar command ,
+(see the
+.Ic \&!
+command below).
+The default filename and current address are unchanged.
+.It (1,$) Ns Ic W Ar file
+Appends the addressed lines to the end of
+.Ar file .
+This is similar to the
+.Ic w
+command, except that the previous contents of file are not clobbered.
+The current address is unchanged.
+.It Ic x
+Prompts for an encryption key which is used in subsequent reads and writes.
+If a newline alone is entered as the key, then encryption is turned off.
+Otherwise, echoing is disabled while a key is read.
+Encryption/decryption is done using the
+.Xr bdes 1
+algorithm.
+.It (.+1) Ns Ic z Ns Ar n
+Scrolls
+.Ar n
+lines at a time starting at addressed line.
+If
+.Ar n
+is not specified, then the current window size is used.
+The current address is set to the last line printed.
+.It ($) Ns Ic =
+Prints the line number of the addressed line.
+.It (.+1) Ns Ic newline
+Prints the addressed line, and sets the current address to that line.
+.It Ic \&! Ns Ar command
+Executes
+.Ar command
+via
+.Xr sh 1 .
+If the first character of
+.Ar command
+is
+.Ic \&! ,
+then it is replaced by text of the previous
+.Ic !command .
+.Nm
+does not process
+.Ar command
+for
+.Sq \e
+(backslash) escapes.
+However, an unescaped
+.Sq %
+is replaced by the default filename.
+When the shell returns from execution, a
+.Sq \&!
+is printed to the standard output.
+The current line is unchanged.
+.El
+.Sh LIMITATIONS
+.Nm
+processes
+.Em file
+arguments for backslash escapes, i.e., in a filename,
+any characters preceded by a backslash
+.Pq Ql \e
+are interpreted literally.
+.Pp
+If a text (non-binary) file is not terminated by a newline character,
+then
+.Nm
+appends one on reading/writing it.
+In the case of a binary file,
+.Nm
+does not append a newline on reading/writing.
+.Sh ENVIRONMENT
+.Bl -tag -width iTMPDIR
+.It Ev TMPDIR
+The location used to store temporary files.
+.El
+.Sh FILES
+.Bl -tag -width /tmp/ed.* -compact
+.It Pa /tmp/ed.*
+buffer file
+.It Pa ed.hup
+where
+.Nm
+attempts to write the buffer if the terminal hangs up
+.El
+.Sh DIAGNOSTICS
+When an error occurs,
+.Nm
+prints a
+.Dq \&?
+and either returns to command mode or exits if its input is from a script.
+An explanation of the last error can be printed with the
+.Ic h
+(help) command.
+.Pp
+Since the
+.Ic g
+(global) command masks any errors from failed searches and substitutions,
+it can be used to perform conditional operations in scripts; e.g.,
+.Bd -literal -offset indent
+g/old/s//new/
+.Ed
+.Pp
+replaces any occurrences of
+.Em old
+with
+.Em new .
+.Pp
+If the
+.Ic u
+(undo) command occurs in a global command list, then
+the command list is executed only once.
+.Pp
+If diagnostics are not disabled, attempting to quit
+.Nm
+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.
+.Sh SEE ALSO
+.Xr bdes 1 ,
+.Xr sed 1 ,
+.Xr sh 1 ,
+.Xr vi 1 ,
+.Xr regex 3
+.Pp
+USD:09-10
+.Rs
+.%A B. W. Kernighan
+.%A P. J. Plauger
+.%B Software Tools in Pascal
+.%I Addison-Wesley
+.%D 1981
+.Re
+.Sh HISTORY
+An
+.Nm
+command appeared in
+.At v1 .
diff --git a/bin/ed/ed.h b/bin/ed/ed.h
new file mode 100644 (file)
index 0000000..d14c438
--- /dev/null
@@ -0,0 +1,293 @@
+/*     $NetBSD: ed.h,v 1.35 2011/08/29 14:51:18 joerg Exp $    */
+
+/* ed.h: type and constant definitions for the ed editor. */
+/*
+ * Copyright (c) 1993 Andrew Moore
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)ed.h,v 1.5 1994/02/01 00:34:39 alm Exp
+ */
+#include <sys/types.h>
+#if defined(BSD) && BSD >= 199103 || defined(__386BSD__)
+# include <sys/param.h>                /* for MAXPATHLEN */
+#endif
+#include <errno.h>
+#if defined(sun) || defined(__NetBSD__) || defined(__APPLE__) || \
+    defined(__minix)
+# include <limits.h>
+#endif
+#include <regex.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define ERR            (-2)
+#define EMOD           (-3)
+#define FATAL          (-4)
+
+#ifndef MAXPATHLEN
+# define MAXPATHLEN 255                /* _POSIX_PATH_MAX */
+#endif
+
+#define MINBUFSZ 512           /* minimum buffer size - must be > 0 */
+#define SE_MAX 30              /* max subexpressions in a regular expression */
+#ifdef INT_MAX
+# define LINECHARS INT_MAX     /* max chars per line */
+#else
+# define LINECHARS MAXINT      /* max chars per line */
+#endif
+
+/* gflags */
+#define GLB 001                /* global command */
+#define GPR 002                /* print after command */
+#define GLS 004                /* list after command */
+#define GNP 010                /* enumerate after command */
+#define GSG 020                /* global substitute */
+
+typedef regex_t pattern_t;
+
+/* Line node */
+typedef struct line {
+       struct line     *q_forw;
+       struct line     *q_back;
+       off_t           seek;           /* address of line in scratch buffer */
+       int             len;            /* length of line */
+} line_t;
+
+
+typedef struct undo {
+
+/* type of undo nodes */
+#define UADD   0
+#define UDEL   1
+#define UMOV   2
+#define VMOV   3
+
+       int type;                       /* command type */
+       line_t  *h;                     /* head of list */
+       line_t  *t;                     /* tail of list */
+} undo_t;
+
+#ifndef max
+# define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+#ifndef min
+# define min(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define INC_MOD(l, k)  ((l) + 1 > (k) ? 0 : (l) + 1)
+#define DEC_MOD(l, k)  ((l) - 1 < 0 ? (k) : (l) - 1)
+
+/* SPL1: disable some interrupts (requires reliable signals) */
+#define SPL1() mutex++
+
+/* SPL0: enable all interrupts; check sigflags (requires reliable signals) */
+#define SPL0() \
+if (--mutex == 0) { \
+       if (sigflags & (1 << (SIGHUP - 1))) handle_hup(SIGHUP); \
+       if (sigflags & (1 << (SIGINT - 1))) handle_int(SIGINT); \
+}
+
+/* STRTOL: convert a string to long */
+#define STRTOL(i, p) { \
+       errno = 0 ; \
+       if (((i = strtol(p, &p, 10)) == LONG_MIN || i == LONG_MAX) && \
+           errno == ERANGE) { \
+               sprintf(errmsg, "number out of range"); \
+               i = 0; \
+               return ERR; \
+       } \
+}
+
+#if defined(sun) || defined(NO_REALLOC_NULL)
+/* REALLOC: assure at least a minimum size for buffer b */
+#define REALLOC(b,n,i,err) \
+if ((i) > (n)) { \
+       int ti = (n); \
+       char *ts; \
+       SPL1(); \
+       if ((b) != NULL) { \
+               if ((ts = (char *) realloc((b), ti += max((i), MINBUFSZ))) == NULL) { \
+                       fprintf(stderr, "%s\n", strerror(errno)); \
+                       sprintf(errmsg, "out of memory"); \
+                       SPL0(); \
+                       return err; \
+               } \
+       } else { \
+               if ((ts = (char *) malloc(ti += max((i), MINBUFSZ))) == NULL) { \
+                       fprintf(stderr, "%s\n", strerror(errno)); \
+                       sprintf(errmsg, "out of memory"); \
+                       SPL0(); \
+                       return err; \
+               } \
+       } \
+       (n) = ti; \
+       (b) = ts; \
+       SPL0(); \
+}
+#else /* NO_REALLOC_NULL */
+/* REALLOC: assure at least a minimum size for buffer b */
+#define REALLOC(b,n,i,err) \
+if ((i) > (n)) { \
+       int ti = (n); \
+       char *ts; \
+       SPL1(); \
+       if ((ts = (char *) realloc((b), ti += max((i), MINBUFSZ))) == NULL) { \
+               fprintf(stderr, "%s\n", strerror(errno)); \
+               sprintf(errmsg, "out of memory"); \
+               SPL0(); \
+               return err; \
+       } \
+       (n) = ti; \
+       (b) = ts; \
+       SPL0(); \
+}
+#endif /* NO_REALLOC_NULL */
+
+/* REQUE: link pred before succ */
+#define REQUE(pred, succ) (pred)->q_forw = (succ), (succ)->q_back = (pred)
+
+/* INSQUE: insert elem in circular queue after pred */
+#define INSQUE(elem, pred) \
+{ \
+       REQUE((elem), (pred)->q_forw); \
+       REQUE((pred), elem); \
+}
+
+/* remque: remove_lines elem from circular queue */
+#define REMQUE(elem) REQUE((elem)->q_back, (elem)->q_forw);
+
+/* NUL_TO_NEWLINE: overwrite ASCII NULs with newlines */
+#define NUL_TO_NEWLINE(s, l) translit_text(s, l, '\0', '\n')
+
+/* NEWLINE_TO_NUL: overwrite newlines with ASCII NULs */
+#define NEWLINE_TO_NUL(s, l) translit_text(s, l, '\n', '\0')
+
+#if defined(sun) && !defined(__SVR4)
+# define strerror(n) sys_errlist[n]
+#endif
+
+/* Local Function Declarations */
+void add_line_node(line_t *);
+int append_lines(long);
+int apply_subst_template(char *, regmatch_t *, int, int);
+int build_active_list(int);
+int check_addr_range(long, long);
+void clear_active_list(void);
+void clear_undo_stack(void);
+int close_sbuf(void);
+int copy_lines(long);
+int delete_lines(long, long);
+int display_lines(long, long, int);
+line_t *dup_line_node(line_t *);
+int exec_command(void);
+long exec_global(int, int);
+int extract_addr_range(void);
+char *extract_pattern(int);
+int extract_subst_tail(int *, long *);
+char *extract_subst_template(void);
+int filter_lines(long, long, char *);
+int flush_des_file(FILE *);
+line_t *get_addressed_line_node(long);
+pattern_t *get_compiled_pattern(void);
+int get_des_char(FILE *);
+char *get_extended_line(int *, int);
+char *get_filename(void);
+int get_keyword(void);
+long get_line_node_addr(line_t *);
+long get_matching_node_addr(pattern_t *, int);
+long get_marked_node_addr(int);
+char *get_sbuf_line(line_t *);
+int get_shell_command(void);
+int get_stream_line(FILE *);
+int get_tty_line(void);
+__dead void handle_hup(int);
+__dead void handle_int(int);
+void handle_winch(int);
+int has_trailing_escape(char *, char *);
+void init_buffers(void);
+void init_des_cipher(void);
+int is_legal_filename(char *);
+int join_lines(long, long);
+int mark_line_node(line_t *, int);
+int move_lines(long);
+line_t *next_active_node(void);
+long next_addr(void);
+int open_sbuf(void);
+char *parse_char_class(char *);
+int pop_undo_stack(void);
+undo_t *push_undo_stack(int, long, long);
+int put_des_char(int, FILE *);
+char *put_sbuf_line(char *);
+int put_stream_line(FILE *, char *, int);
+int put_tty_line(char *, int, long, int);
+__dead void quit(int);
+long read_file(char *, long);
+long read_stream(FILE *, long);
+int search_and_replace(pattern_t *, int, int);
+int set_active_node(line_t *);
+void signal_hup(int);
+void signal_int(int);
+char *strip_escapes(const char *);
+int substitute_matching_text(pattern_t *, line_t *, int, int);
+char *translit_text(char *, int, int, int);
+void unmark_line_node(line_t *);
+void unset_active_nodes(line_t *, line_t *);
+long write_file(const char *, const char *, long, long);
+long write_stream(FILE *, long, long);
+
+/* global buffers */
+extern char stdinbuf[];
+extern char *ibuf;
+extern char *ibufp;
+extern int ibufsz;
+
+/* global flags */
+extern int isbinary;
+extern int isglobal;
+extern int modified;
+extern int mutex;
+extern int sigflags;
+
+/* global vars */
+extern long addr_last;
+extern long current_addr;
+extern long first_addr;
+extern int lineno;
+extern long second_addr;
+extern long rows;
+extern int cols;
+extern int scripted;
+extern int ere;
+extern int des;
+extern int newline_added;      /* io.c */
+extern int patlock;
+extern char errmsg[];          /* re.c */
+extern long u_current_addr;    /* undo.c */
+extern long u_addr_last;       /* undo.c */
+#if defined(sun) && !defined(__SVR4)
+extern char *sys_errlist[];
+#endif
diff --git a/bin/ed/glbl.c b/bin/ed/glbl.c
new file mode 100644 (file)
index 0000000..207245c
--- /dev/null
@@ -0,0 +1,227 @@
+/*     $NetBSD: glbl.c,v 1.6 2005/06/26 19:10:49 christos Exp $        */
+
+/* glob.c: This file contains the global command routines for the ed line
+   editor */
+/*-
+ * Copyright (c) 1993 Andrew Moore, Talke Studio.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char *rcsid = "@(#)glob.c,v 1.1 1994/02/01 00:34:40 alm Exp";
+#else
+__RCSID("$NetBSD: glbl.c,v 1.6 2005/06/26 19:10:49 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+
+#include "ed.h"
+
+
+/* build_active_list:  add line matching a pattern to the global-active list */
+int
+build_active_list(int isgcmd)
+{
+       pattern_t *pat;
+       line_t *lp;
+       long n;
+       char *s;
+       char delimiter;
+
+       if ((delimiter = *ibufp) == ' ' || delimiter == '\n') {
+               sprintf(errmsg, "invalid pattern delimiter");
+               return ERR;
+       } else if ((pat = get_compiled_pattern()) == NULL)
+               return ERR;
+       else if (*ibufp == delimiter)
+               ibufp++;
+       clear_active_list();
+       lp = get_addressed_line_node(first_addr);
+       for (n = first_addr; n <= second_addr; n++, lp = lp->q_forw) {
+               if ((s = get_sbuf_line(lp)) == NULL)
+                       return ERR;
+               if (isbinary)
+                       NUL_TO_NEWLINE(s, lp->len);
+               if (!regexec(pat, s, 0, NULL, 0) == isgcmd &&
+                   set_active_node(lp) < 0)
+                       return ERR;
+       }
+       return 0;
+}
+
+
+/* exec_global: apply command list in the command buffer to the active
+   lines in a range; return command status */
+long
+exec_global(int interact, int gflag)
+{
+       static char *ocmd = NULL;
+       static int ocmdsz = 0;
+
+       line_t *lp = NULL;
+       int status;
+       int n;
+       char *cmd = NULL;
+#ifdef BACKWARDS
+       char cmdp[] = "p\n";
+
+       if (!interact) {
+               if (!strcmp(ibufp, "\n"))
+                       cmd = cmdp;             /* null cmd-list == `p' */
+               else if ((cmd = get_extended_line(&n, 0)) == NULL)
+                       return ERR;
+       }
+#else
+       if (!interact && (cmd = get_extended_line(&n, 0)) == NULL)
+               return ERR;
+#endif
+       clear_undo_stack();
+       while ((lp = next_active_node()) != NULL) {
+               if ((current_addr = get_line_node_addr(lp)) < 0)
+                       return ERR;
+               if (interact) {
+                       /* print current_addr; get a command in global syntax */
+                       if (display_lines(current_addr, current_addr, gflag) < 0)
+                               return ERR;
+                       while ((n = get_tty_line()) > 0 &&
+                           ibuf[n - 1] != '\n')
+                               clearerr(stdin);
+                       if (n < 0)
+                               return ERR;
+                       else if (n == 0) {
+                               sprintf(errmsg, "unexpected end-of-file");
+                               return ERR;
+                       } else if (n == 1 && !strcmp(ibuf, "\n"))
+                               continue;
+                       else if (n == 2 && !strcmp(ibuf, "&\n")) {
+                               if (cmd == NULL) {
+                                       sprintf(errmsg, "no previous command");
+                                       return ERR;
+                               } else cmd = ocmd;
+                       } else if ((cmd = get_extended_line(&n, 0)) == NULL)
+                               return ERR;
+                       else {
+                               REALLOC(ocmd, ocmdsz, n + 1, ERR);
+                               memcpy(ocmd, cmd, n + 1);
+                               cmd = ocmd;
+                       }
+
+               }
+               ibufp = cmd;
+               for (; *ibufp;)
+                       if ((status = extract_addr_range()) < 0 ||
+                           (status = exec_command()) < 0 ||
+                           (status > 0 && (status = display_lines(
+                           current_addr, current_addr, status))) < 0)
+                               return status;
+       }
+       return 0;
+}
+
+
+line_t **active_list;          /* list of lines active in a global command */
+long active_last;              /* index of last active line in active_list */
+long active_size;              /* size of active_list */
+long active_ptr;               /* active_list index (non-decreasing) */
+long active_ndx;               /* active_list index (modulo active_last) */
+
+/* set_active_node: add a line node to the global-active list */
+int
+set_active_node(line_t *lp)
+{
+       if (active_last + 1 > active_size) {
+               int ti = active_size;
+               line_t **ts;
+               SPL1();
+#if defined(sun) || defined(NO_REALLOC_NULL)
+               if (active_list != NULL) {
+#endif
+                       if ((ts = (line_t **) realloc(active_list, 
+                           (ti += MINBUFSZ) * sizeof(line_t **))) == NULL) {
+                               fprintf(stderr, "%s\n", strerror(errno));
+                               sprintf(errmsg, "out of memory");
+                               SPL0();
+                               return ERR;
+                       }
+#if defined(sun) || defined(NO_REALLOC_NULL)
+               } else {
+                       if ((ts = (line_t **) malloc((ti += MINBUFSZ) * 
+                           sizeof(line_t **))) == NULL) {
+                               fprintf(stderr, "%s\n", strerror(errno));
+                               sprintf(errmsg, "out of memory");
+                               SPL0();
+                               return ERR;
+                       }
+               }
+#endif
+               active_size = ti;
+               active_list = ts;
+               SPL0();
+       }
+       active_list[active_last++] = lp;
+       return 0;
+}
+
+
+/* unset_active_nodes: remove a range of lines from the global-active list */
+void
+unset_active_nodes(line_t *np, line_t *mp)
+{
+       line_t *lp;
+       long i;
+
+       for (lp = np; lp != mp; lp = lp->q_forw)
+               for (i = 0; i < active_last; i++)
+                       if (active_list[active_ndx] == lp) {
+                               active_list[active_ndx] = NULL;
+                               active_ndx = INC_MOD(active_ndx, active_last - 1);
+                               break;
+                       } else  active_ndx = INC_MOD(active_ndx, active_last - 1);
+}
+
+
+/* next_active_node: return the next global-active line node */
+line_t *
+next_active_node(void)
+{
+       while (active_ptr < active_last && active_list[active_ptr] == NULL)
+               active_ptr++;
+       return (active_ptr < active_last) ? active_list[active_ptr++] : NULL;
+}
+
+
+/* clear_active_list: clear the global-active list */
+void
+clear_active_list(void)
+{
+       SPL1();
+       active_size = active_last = active_ptr = active_ndx = 0;
+       free(active_list);
+       active_list = NULL;
+       SPL0();
+}
diff --git a/bin/ed/io.c b/bin/ed/io.c
new file mode 100644 (file)
index 0000000..432e483
--- /dev/null
@@ -0,0 +1,358 @@
+/*     $NetBSD: io.c,v 1.9 2011/05/23 23:13:10 joerg Exp $     */
+
+/* io.c: This file contains the i/o routines for the ed line editor */
+/*-
+ * Copyright (c) 1993 Andrew Moore, Talke Studio.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char *rcsid = "@(#)io.c,v 1.1 1994/02/01 00:34:41 alm Exp";
+#else
+__RCSID("$NetBSD: io.c,v 1.9 2011/05/23 23:13:10 joerg Exp $");
+#endif
+#endif /* not lint */
+
+#include "ed.h"
+
+
+/* read_file: read a named file/pipe into the buffer; return line count */
+long
+read_file(char *fn, long n)
+{
+       FILE *fp;
+       long size;
+
+
+       fp = (*fn == '!') ? popen(fn + 1, "r") : fopen(strip_escapes(fn), "r");
+       if (fp == NULL) {
+               fprintf(stderr, "%s: %s\n", fn, strerror(errno));
+               sprintf(errmsg, "cannot open input file");
+               return ERR;
+       } else if ((size = read_stream(fp, n)) < 0)
+               return ERR;
+        else if (((*fn == '!') ?  pclose(fp) : fclose(fp)) < 0) {
+               fprintf(stderr, "%s: %s\n", fn, strerror(errno));
+               sprintf(errmsg, "cannot close input file");
+               return ERR;
+       }
+       if (!scripted)
+               fprintf(stderr, "%lu\n", size);
+       return current_addr - n;
+}
+
+
+char *sbuf;                    /* file i/o buffer */
+int sbufsz;                    /* file i/o buffer size */
+int newline_added;             /* if set, newline appended to input file */
+
+/* read_stream: read a stream into the editor buffer; return status */
+long
+read_stream(FILE *fp, long n)
+{
+       line_t *lp = get_addressed_line_node(n);
+       undo_t *up = NULL;
+       unsigned long size = 0;
+       int o_newline_added = newline_added;
+       int o_isbinary = isbinary;
+       int appended = (n == addr_last);
+       int len;
+
+       isbinary = newline_added = 0;
+       if (des)
+               init_des_cipher();
+       for (current_addr = n; (len = get_stream_line(fp)) > 0; size += len) {
+               SPL1();
+               if (put_sbuf_line(sbuf) == NULL) {
+                       SPL0();
+                       return ERR;
+               }
+               lp = lp->q_forw;
+               if (up)
+                       up->t = lp;
+               else if ((up = push_undo_stack(UADD, current_addr,
+                   current_addr)) == NULL) {
+                       SPL0();
+                       return ERR;
+               }
+               SPL0();
+       }
+       if (len < 0)
+               return ERR;
+       if (appended && size && o_isbinary && o_newline_added)
+               fputs("newline inserted\n", stderr);
+       else if (newline_added && (!appended || (!isbinary && !o_isbinary)))
+               fputs("newline appended\n", stderr);
+       if (isbinary && newline_added && !appended)
+               size += 1;
+       if (!size)
+               newline_added = 1;
+       newline_added = appended ? newline_added : o_newline_added;
+       isbinary = isbinary | o_isbinary;
+       if (des)
+               size += 8 - size % 8;                   /* adjust DES size */
+       return size;
+}
+
+
+/* get_stream_line: read a line of text from a stream; return line length */
+int
+get_stream_line(FILE *fp)
+{
+       int c;
+       int i = 0;
+
+       while (((c = des ? get_des_char(fp) : getc(fp)) != EOF || (!feof(fp) &&
+           !ferror(fp))) && c != '\n') {
+               REALLOC(sbuf, sbufsz, i + 1, ERR);
+               if (!(sbuf[i++] = c))
+                       isbinary = 1;
+       }
+       REALLOC(sbuf, sbufsz, i + 2, ERR);
+       if (c == '\n')
+               sbuf[i++] = c;
+       else if (ferror(fp)) {
+               fprintf(stderr, "%s\n", strerror(errno));
+               sprintf(errmsg, "cannot read input file");
+               return ERR;
+       } else if (i) {
+               sbuf[i++] = '\n';
+               newline_added = 1;
+       }
+       sbuf[i] = '\0';
+       return (isbinary && newline_added && i) ? --i : i;
+}
+
+
+/* write_file: write a range of lines to a named file/pipe; return line count */
+long
+write_file(const char *fn, const char *mode, long n, long m)
+{
+       FILE *fp;
+       long size;
+
+       fp = (*fn == '!') ? popen(fn+1, "w") : fopen(strip_escapes(fn), mode);
+       if (fp == NULL) {
+               fprintf(stderr, "%s: %s\n", fn, strerror(errno));
+               sprintf(errmsg, "cannot open output file");
+               return ERR;
+       } else if ((size = write_stream(fp, n, m)) < 0)
+               return ERR;
+        else if (((*fn == '!') ?  pclose(fp) : fclose(fp)) < 0) {
+               fprintf(stderr, "%s: %s\n", fn, strerror(errno));
+               sprintf(errmsg, "cannot close output file");
+               return ERR;
+       }
+       if (!scripted)
+               fprintf(stderr, "%lu\n", size);
+       return n ? m - n + 1 : 0;
+}
+
+
+/* write_stream: write a range of lines to a stream; return status */
+long
+write_stream(FILE *fp, long n, long m)
+{
+       line_t *lp = get_addressed_line_node(n);
+       unsigned long size = 0;
+       char *s;
+       int len;
+
+       if (des)
+               init_des_cipher();
+       for (; n && n <= m; n++, lp = lp->q_forw) {
+               if ((s = get_sbuf_line(lp)) == NULL)
+                       return ERR;
+               len = lp->len;
+               if (n != addr_last || !isbinary || !newline_added)
+                       s[len++] = '\n';
+               if (put_stream_line(fp, s, len) < 0)
+                       return ERR;
+               size += len;
+       }
+       if (des) {
+               flush_des_file(fp);                     /* flush buffer */
+               size += 8 - size % 8;                   /* adjust DES size */
+       }
+       return size;
+}
+
+
+/* put_stream_line: write a line of text to a stream; return status */
+int
+put_stream_line(FILE *fp, char *s, int len)
+{
+       while (len--)
+               if ((des ? put_des_char(*s++, fp) : fputc(*s++, fp)) < 0) {
+                       fprintf(stderr, "%s\n", strerror(errno));
+                       sprintf(errmsg, "cannot write file");
+                       return ERR;
+               }
+       return 0;
+}
+
+/* get_extended_line: get a an extended line from stdin */
+char *
+get_extended_line(int *sizep, int nonl)
+{
+       static char *cvbuf = NULL;              /* buffer */
+       static int cvbufsz = 0;                 /* buffer size */
+
+       int l, n;
+       char *t = ibufp;
+
+       while (*t++ != '\n')
+               ;
+       if ((l = t - ibufp) < 2 || !has_trailing_escape(ibufp, ibufp + l - 1)) {
+               *sizep = l;
+               return ibufp;
+       }
+       *sizep = -1;
+       REALLOC(cvbuf, cvbufsz, l, NULL);
+       memcpy(cvbuf, ibufp, l);
+       *(cvbuf + --l - 1) = '\n';      /* strip trailing esc */
+       if (nonl) l--;                  /* strip newline */
+       for (;;) {
+               if ((n = get_tty_line()) < 0)
+                       return NULL;
+               else if (n == 0 || ibuf[n - 1] != '\n') {
+                       sprintf(errmsg, "unexpected end-of-file");
+                       return NULL;
+               }
+               REALLOC(cvbuf, cvbufsz, l + n, NULL);
+               memcpy(cvbuf + l, ibuf, n);
+               l += n;
+               if (n < 2 || !has_trailing_escape(cvbuf, cvbuf + l - 1))
+                       break;
+               *(cvbuf + --l - 1) = '\n';      /* strip trailing esc */
+               if (nonl) l--;                  /* strip newline */
+       }
+       REALLOC(cvbuf, cvbufsz, l + 1, NULL);
+       cvbuf[l] = '\0';
+       *sizep = l;
+       return cvbuf;
+}
+
+
+/* get_tty_line: read a line of text from stdin; return line length */
+int
+get_tty_line(void)
+{
+       int oi = 0;
+       int i = 0;
+       int c;
+
+       for (;;)
+               switch (c = getchar()) {
+               default:
+                       oi = 0;
+                       REALLOC(ibuf, ibufsz, i + 2, ERR);
+                       if (!(ibuf[i++] = c)) isbinary = 1;
+                       if (c != '\n')
+                               continue;
+                       lineno++;
+                       ibuf[i] = '\0';
+                       ibufp = ibuf;
+                       return i;
+               case EOF:
+                       if (ferror(stdin)) {
+                               fprintf(stderr, "stdin: %s\n", strerror(errno));
+                               sprintf(errmsg, "cannot read stdin");
+                               clearerr(stdin);
+                               ibufp = NULL;
+                               return ERR;
+                       } else {
+                               clearerr(stdin);
+                               if (i != oi) {
+                                       oi = i;
+                                       continue;
+                               } else if (i)
+                                       ibuf[i] = '\0';
+                               ibufp = ibuf;
+                               return i;
+                       }
+               }
+}
+
+
+
+#define ESCAPES "\a\b\f\n\r\t\v\\"
+#define ESCCHARS "abfnrtv\\"
+
+/* put_tty_line: print text to stdout */
+int
+put_tty_line(char *s, int l, long n, int gflag)
+{
+       int col = 0;
+       char *cp;
+#ifndef BACKWARDS
+       int lc = 0;
+#endif
+
+       if (gflag & GNP) {
+               printf("%ld\t", n);
+               col = 8;
+       }
+       for (; l--; s++) {
+               if ((gflag & GLS) && ++col > cols) {
+                       fputs("\\\n", stdout);
+                       col = 1;
+#ifndef BACKWARDS
+                       if (!scripted && !isglobal && ++lc > rows) {
+                               lc = 0;
+                               fputs("Press <RETURN> to continue... ", stdout);
+                               fflush(stdout);
+                               if (get_tty_line() < 0)
+                                       return ERR;
+                       }
+#endif
+               }
+               if (gflag & GLS) {
+                       if (31 < *s && *s < 127 && *s != '\\')
+                               putchar(*s);
+                       else {
+                               putchar('\\');
+                               col++;
+                               if (*s && (cp = strchr(ESCAPES, *s)) != NULL)
+                                       putchar(ESCCHARS[cp - ESCAPES]);
+                               else {
+                                       putchar((((unsigned char) *s & 0300) >> 6) + '0');
+                                       putchar((((unsigned char) *s & 070) >> 3) + '0');
+                                       putchar(((unsigned char) *s & 07) + '0');
+                                       col += 2;
+                               }
+                       }
+
+               } else
+                       putchar(*s);
+       }
+#ifndef BACKWARDS
+       if (gflag & GLS)
+               putchar('$');
+#endif
+       putchar('\n');
+       return 0;
+}
diff --git a/bin/ed/main.c b/bin/ed/main.c
new file mode 100644 (file)
index 0000000..a9a7727
--- /dev/null
@@ -0,0 +1,1425 @@
+/*     $NetBSD: main.c,v 1.25 2011/08/21 08:40:31 christos Exp $       */
+
+/* main.c: This file contains the main control and user-interface routines
+   for the ed line editor. */
+/*-
+ * Copyright (c) 1993 Andrew Moore, Talke Studio.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT(
+"@(#) Copyright (c) 1993 Andrew Moore, Talke Studio.\
+ All rights reserved.");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char *rcsid = "@(#)main.c,v 1.1 1994/02/01 00:34:42 alm Exp";
+#else
+__RCSID("$NetBSD: main.c,v 1.25 2011/08/21 08:40:31 christos Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * CREDITS
+ *
+ *     This program is based on the editor algorithm described in
+ *     Brian W. Kernighan and P. J. Plauger's book "Software Tools 
+ *     in Pascal," Addison-Wesley, 1981.
+ *
+ *     The buffering algorithm is attributed to Rodney Ruddock of
+ *     the University of Guelph, Guelph, Ontario.
+ *
+ *     The cbc.c encryption code is adapted from
+ *     the bdes program by Matt Bishop of Dartmouth College,
+ *     Hanover, NH.
+ *
+ */
+
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <ctype.h>
+#include <setjmp.h>
+#include <pwd.h>
+
+#include "ed.h"
+
+
+#ifdef _POSIX_SOURCE
+sigjmp_buf env;
+#else
+jmp_buf env;
+#endif
+
+/* static buffers */
+char stdinbuf[1];              /* stdin buffer */
+char *shcmd;                   /* shell command buffer */
+int shcmdsz;                   /* shell command buffer size */
+int shcmdi;                    /* shell command buffer index */
+char *ibuf;                    /* ed command-line buffer */
+int ibufsz;                    /* ed command-line buffer size */
+char *ibufp;                   /* pointer to ed command-line buffer */
+
+/* global flags */
+int des = 0;                   /* if set, use crypt(3) for i/o */
+int garrulous = 0;             /* if set, print all error messages */
+int isbinary;                  /* if set, buffer contains ASCII NULs */
+int isglobal;                  /* if set, doing a global command */
+int modified;                  /* if set, buffer modified since last write */
+int mutex = 0;                 /* if set, signals set "sigflags" */
+int red = 0;                   /* if set, restrict shell/directory access */
+int ere = 0;                   /* if set, use extended regexes */
+int scripted = 0;              /* if set, suppress diagnostics */
+int sigflags = 0;              /* if set, signals received while mutex set */
+int sigactive = 0;             /* if set, signal handlers are enabled */
+
+char old_filename[MAXPATHLEN + 1] = "";        /* default filename */
+long current_addr;             /* current address in editor buffer */
+long addr_last;                        /* last address in editor buffer */
+int lineno;                    /* script line number */
+const char *prompt;                    /* command-line prompt */
+const char *dps = "*";         /* default command-line prompt */
+
+
+static const char usage[] = "Usage: %s [-] [-sxE] [-p string] [name]\n";
+
+/* ed: line editor */
+int
+main(int ac, char *av[])
+{
+       int c, n;
+       long status = 0;
+       volatile int argc = ac;
+       char ** volatile argv = av;
+
+       red = (n = strlen(argv[0])) > 2 && argv[0][n - 3] == 'r';
+top:
+       while ((c = getopt(argc, argv, "p:sxE")) != -1)
+               switch(c) {
+               case 'p':                               /* set prompt */
+                       prompt = optarg;
+                       break;
+               case 's':                               /* run script */
+                       scripted = 1;
+                       break;
+               case 'x':                               /* use crypt */
+#ifdef DES
+                       des = get_keyword();
+#else
+                       fprintf(stderr, "crypt unavailable\n?\n");
+#endif
+                       break;
+
+               case 'E':
+                       ere = REG_EXTENDED;
+                       break;
+               default:
+                       fprintf(stderr, usage, getprogname());
+                       exit(1);
+                       /* NOTREACHED */
+               }
+       argv += optind;
+       argc -= optind;
+       if (argc && **argv == '-') {
+               scripted = 1;
+               if (argc > 1) {
+                       optind = 1;
+                       goto top;
+               }
+               argv++;
+               argc--;
+       }
+       /* assert: reliable signals! */
+#ifdef SIGWINCH
+       handle_winch(SIGWINCH);
+       if (isatty(0)) signal(SIGWINCH, handle_winch);
+#endif
+       signal(SIGHUP, signal_hup);
+       signal(SIGQUIT, SIG_IGN);
+       signal(SIGINT, signal_int);
+#ifdef _POSIX_SOURCE
+       if ((status = sigsetjmp(env, 1)) != 0)
+#else
+       if ((status = setjmp(env)) != 0)
+#endif
+       {
+               fputs("\n?\n", stderr);
+               sprintf(errmsg, "interrupt");
+       } else {
+               init_buffers();
+               sigactive = 1;                  /* enable signal handlers */
+               if (argc && **argv && is_legal_filename(*argv)) {
+                       if (read_file(*argv, 0) < 0 && !isatty(0))
+                               quit(2);
+                       else if (**argv != '!')
+                               strlcpy(old_filename, *argv,
+                                   sizeof(old_filename) - 2);
+               } else if (argc) {
+                       fputs("?\n", stderr);
+                       if (**argv == '\0')
+                               sprintf(errmsg, "invalid filename");
+                       if (!isatty(0))
+                               quit(2);
+               }
+       }
+       for (;;) {
+               if (status < 0 && garrulous)
+                       fprintf(stderr, "%s\n", errmsg);
+               if (prompt) {
+                       printf("%s", prompt);
+                       fflush(stdout);
+               }
+               if ((n = get_tty_line()) < 0) {
+                       status = ERR;
+                       continue;
+               } else if (n == 0) {
+                       if (modified && !scripted) {
+                               fputs("?\n", stderr);
+                               sprintf(errmsg, "warning: file modified");
+                               if (!isatty(0)) {
+                                       if (garrulous) {
+                                               fprintf(stderr,
+                                                   "script, line %d: %s\n",
+                                                   lineno, errmsg);
+                                       }
+                                       quit(2);
+                               }
+                               clearerr(stdin);
+                               modified = 0;
+                               status = EMOD;
+                               continue;
+                       } else
+                               quit(0);
+               } else if (ibuf[n - 1] != '\n') {
+                       /* discard line */
+                       sprintf(errmsg, "unexpected end-of-file");
+                       clearerr(stdin);
+                       status = ERR;
+                       continue;
+               }
+               isglobal = 0;
+               if ((status = extract_addr_range()) >= 0 &&
+                   (status = exec_command()) >= 0)
+                       if (!status || (status &&
+                           (status = display_lines(current_addr, current_addr,
+                               status))) >= 0)
+                               continue;
+               switch (status) {
+               case EOF:
+                       quit(0);
+               case EMOD:
+                       modified = 0;
+                       fputs("?\n", stderr);           /* give warning */
+                       sprintf(errmsg, "warning: file modified");
+                       if (!isatty(0)) {
+                               if (garrulous) {
+                                       fprintf(stderr,
+                                           "script, line %d: %s\n",
+                                           lineno, errmsg);
+                               }
+                               quit(2);
+                       }
+                       break;
+               case FATAL:
+                       if (garrulous) {
+                               if (!isatty(0)) {
+                                       fprintf(stderr,
+                                           "script, line %d: %s\n",
+                                           lineno, errmsg);
+                               } else {
+                                       fprintf(stderr, "%s\n", errmsg);
+                               }
+                       }
+                       quit(3);
+               default:
+                       fputs("?\n", stderr);
+                       if (!isatty(0)) {
+                               if (garrulous) {
+                                       fprintf(stderr, "script, line %d: %s\n",
+                                           lineno, errmsg);
+                               }
+                               quit(2);
+                       }
+                       break;
+               }
+       }
+       /* NOTREACHED */
+}
+
+long first_addr, second_addr, addr_cnt;
+
+/* extract_addr_range: get line addresses from the command buffer until an 
+   illegal address is seen; return status */
+int
+extract_addr_range(void)
+{
+       long addr;
+
+       addr_cnt = 0;
+       first_addr = second_addr = current_addr;
+       while ((addr = next_addr()) >= 0) {
+               addr_cnt++;
+               first_addr = second_addr;
+               second_addr = addr;
+               if (*ibufp != ',' && *ibufp != ';')
+                       break;
+               else if (*ibufp++ == ';')
+                       current_addr = addr;
+       }
+       if ((addr_cnt = min(addr_cnt, 2)) == 1 || second_addr != addr)
+               first_addr = second_addr;
+       return (addr == ERR) ? ERR : 0;
+}
+
+
+#define        SKIP_BLANKS() while (isspace((unsigned char)*ibufp) && *ibufp != '\n') \
+       ibufp++
+
+#define MUST_BE_FIRST() \
+       if (!first) { sprintf(errmsg, "invalid address"); return ERR; }
+
+/*  next_addr: return the next line address in the command buffer */
+long
+next_addr(void)
+{
+       char *hd;
+       long addr = current_addr;
+       long n;
+       int first = 1;
+       int c;
+
+       SKIP_BLANKS();
+       for (hd = ibufp;; first = 0)
+               switch (c = *ibufp) {
+               case '+':
+               case '\t':
+               case ' ':
+               case '-':
+               case '^':
+                       ibufp++;
+                       SKIP_BLANKS();
+                       if (isdigit((unsigned char)*ibufp)) {
+                               STRTOL(n, ibufp);
+                               addr += (c == '-' || c == '^') ? -n : n;
+                       } else if (!isspace((unsigned char)c))
+                               addr += (c == '-' || c == '^') ? -1 : 1;
+                       break;
+               case '0': case '1': case '2':
+               case '3': case '4': case '5':
+               case '6': case '7': case '8': case '9':
+                       MUST_BE_FIRST();
+                       STRTOL(addr, ibufp);
+                       break;
+               case '.':
+               case '$':
+                       MUST_BE_FIRST();
+                       ibufp++;
+                       addr = (c == '.') ? current_addr : addr_last;
+                       break;
+               case '/':
+               case '?':
+                       MUST_BE_FIRST();
+                       if ((addr = get_matching_node_addr(
+                           get_compiled_pattern(), c == '/')) < 0)
+                               return ERR;
+                       else if (c == *ibufp)
+                               ibufp++;
+                       break;
+               case '\'':
+                       MUST_BE_FIRST();
+                       ibufp++;
+                       if ((addr = get_marked_node_addr((unsigned char)*ibufp++)) < 0)
+                               return ERR;
+                       break;
+               case '%':
+               case ',':
+               case ';':
+                       if (first) {
+                               ibufp++;
+                               addr_cnt++;
+                               second_addr = (c == ';') ? current_addr : 1;
+                               addr = addr_last;
+                               break;
+                       }
+                       /* FALL THROUGH */
+               default:
+                       if (ibufp == hd)
+                               return EOF;
+                       else if (addr < 0 || addr_last < addr) {
+                               sprintf(errmsg, "invalid address");
+                               return ERR;
+                       } else
+                               return addr;
+               }
+       /* NOTREACHED */
+}
+
+
+#ifdef BACKWARDS
+/* GET_THIRD_ADDR: get a legal address from the command buffer */
+#define GET_THIRD_ADDR(addr) \
+{ \
+       long ol1, ol2; \
+\
+       ol1 = first_addr, ol2 = second_addr; \
+       if (extract_addr_range() < 0) \
+               return ERR; \
+       else if (addr_cnt == 0) { \
+               sprintf(errmsg, "destination expected"); \
+               return ERR; \
+       } else if (second_addr < 0 || addr_last < second_addr) { \
+               sprintf(errmsg, "invalid address"); \
+               return ERR; \
+       } \
+       addr = second_addr; \
+       first_addr = ol1, second_addr = ol2; \
+}
+#else  /* BACKWARDS */
+/* GET_THIRD_ADDR: get a legal address from the command buffer */
+#define GET_THIRD_ADDR(addr) \
+{ \
+       long ol1, ol2; \
+\
+       ol1 = first_addr, ol2 = second_addr; \
+       if (extract_addr_range() < 0) \
+               return ERR; \
+       if (second_addr < 0 || addr_last < second_addr) { \
+               sprintf(errmsg, "invalid address"); \
+               return ERR; \
+       } \
+       addr = second_addr; \
+       first_addr = ol1, second_addr = ol2; \
+}
+#endif
+
+
+/* GET_COMMAND_SUFFIX: verify the command suffix in the command buffer */
+#define GET_COMMAND_SUFFIX() { \
+       int done = 0; \
+       do { \
+               switch(*ibufp) { \
+               case 'p': \
+                       gflag |= GPR, ibufp++; \
+                       break; \
+               case 'l': \
+                       gflag |= GLS, ibufp++; \
+                       break; \
+               case 'n': \
+                       gflag |= GNP, ibufp++; \
+                       break; \
+               default: \
+                       done++; \
+               } \
+       } while (!done); \
+       if (*ibufp++ != '\n') { \
+               sprintf(errmsg, "invalid command suffix"); \
+               return ERR; \
+       } \
+}
+
+
+/* sflags */
+#define SGG 001                /* complement previous global substitute suffix */
+#define SGP 002                /* complement previous print suffix */
+#define SGR 004                /* use last regex instead of last pat */
+#define SGF 010                /* repeat last substitution */
+
+int patlock = 0;       /* if set, pattern not freed by get_compiled_pattern() */
+
+long rows = 22;                /* scroll length: ws_row - 2 */
+
+/* exec_command: execute the next command in command buffer; return print
+   request, if any */
+int
+exec_command(void)
+{
+       static pattern_t *pat = NULL;
+       static int sgflag = 0;
+       static long sgnum = 0;
+
+       pattern_t *tpat;
+       char *fnp;
+       int gflag = 0;
+       int sflags = 0;
+       long addr = 0;
+       int n = 0;
+       int c;
+
+       SKIP_BLANKS();
+       switch(c = *ibufp++) {
+       case 'a':
+               GET_COMMAND_SUFFIX();
+               if (!isglobal) clear_undo_stack();
+               if (append_lines(second_addr) < 0)
+                       return ERR;
+               break;
+       case 'c':
+               if (check_addr_range(current_addr, current_addr) < 0)
+                       return ERR;
+               GET_COMMAND_SUFFIX();
+               if (!isglobal) clear_undo_stack();
+               if (delete_lines(first_addr, second_addr) < 0 ||
+                   append_lines(current_addr) < 0)
+                       return ERR;
+               break;
+       case 'd':
+               if (check_addr_range(current_addr, current_addr) < 0)
+                       return ERR;
+               GET_COMMAND_SUFFIX();
+               if (!isglobal) clear_undo_stack();
+               if (delete_lines(first_addr, second_addr) < 0)
+                       return ERR;
+               else if ((addr = INC_MOD(current_addr, addr_last)) != 0)
+                       current_addr = addr;
+               break;
+       case 'e':
+               if (modified && !scripted)
+                       return EMOD;
+               /* fall through */
+       case 'E':
+               if (addr_cnt > 0) {
+                       sprintf(errmsg, "unexpected address");
+                       return ERR;
+               } else if (!isspace((unsigned char)*ibufp)) {
+                       sprintf(errmsg, "unexpected command suffix");
+                       return ERR;
+               } else if ((fnp = get_filename()) == NULL)
+                       return ERR;
+               GET_COMMAND_SUFFIX();
+               if (delete_lines(1, addr_last) < 0)
+                       return ERR;
+               clear_undo_stack();
+               if (close_sbuf() < 0)
+                       return ERR;
+               else if (open_sbuf() < 0)
+                       return FATAL;
+               if (*fnp && *fnp != '!') strlcpy(old_filename, fnp,
+                       sizeof(old_filename) - 2);
+#ifdef BACKWARDS
+               if (*fnp == '\0' && *old_filename == '\0') {
+                       sprintf(errmsg, "no current filename");
+                       return ERR;
+               }
+#endif
+               if (read_file(*fnp ? fnp : old_filename, 0) < 0)
+                       return ERR;
+               clear_undo_stack();
+               modified = 0;
+               u_current_addr = u_addr_last = -1;
+               break;
+       case 'f':
+               if (addr_cnt > 0) {
+                       sprintf(errmsg, "unexpected address");
+                       return ERR;
+               } else if (!isspace((unsigned char)*ibufp)) {
+                       sprintf(errmsg, "unexpected command suffix");
+                       return ERR;
+               } else if ((fnp = get_filename()) == NULL)
+                       return ERR;
+               else if (*fnp == '!') {
+                       sprintf(errmsg, "invalid redirection");
+                       return ERR;
+               }
+               GET_COMMAND_SUFFIX();
+               if (*fnp) strlcpy(old_filename, fnp, sizeof(old_filename) - 2);
+               printf("%s\n", strip_escapes(old_filename));
+               break;
+       case 'g':
+       case 'v':
+       case 'G':
+       case 'V':
+               if (isglobal) {
+                       sprintf(errmsg, "cannot nest global commands");
+                       return ERR;
+               } else if (check_addr_range(1, addr_last) < 0)
+                       return ERR;
+               else if (build_active_list(c == 'g' || c == 'G') < 0)
+                       return ERR;
+               else if ((n = (c == 'G' || c == 'V')) != 0)
+                       GET_COMMAND_SUFFIX();
+               isglobal++;
+               if (exec_global(n, gflag) < 0)
+                       return ERR; 
+               break;
+       case 'h':
+               if (addr_cnt > 0) {
+                       sprintf(errmsg, "unexpected address");
+                       return ERR;
+               }
+               GET_COMMAND_SUFFIX();
+               if (*errmsg) fprintf(stderr, "%s\n", errmsg);
+               break;
+       case 'H':
+               if (addr_cnt > 0) {
+                       sprintf(errmsg, "unexpected address");
+                       return ERR;
+               }
+               GET_COMMAND_SUFFIX();
+               if ((garrulous = 1 - garrulous) && *errmsg)
+                       fprintf(stderr, "%s\n", errmsg);
+               break;
+       case 'i':
+               if (second_addr == 0) {
+                       sprintf(errmsg, "invalid address");
+                       return ERR;
+               }
+               GET_COMMAND_SUFFIX();
+               if (!isglobal) clear_undo_stack();
+               if (append_lines(second_addr - 1) < 0)
+                       return ERR;
+               break;
+       case 'j':
+               if (check_addr_range(current_addr, current_addr + 1) < 0)
+                       return ERR;
+               GET_COMMAND_SUFFIX();
+               if (!isglobal) clear_undo_stack();
+               if (first_addr != second_addr &&
+                   join_lines(first_addr, second_addr) < 0)
+                       return ERR;
+               break;
+       case 'k':
+               c = *ibufp++;
+               if (second_addr == 0) {
+                       sprintf(errmsg, "invalid address");
+                       return ERR;
+               }
+               GET_COMMAND_SUFFIX();
+               if (mark_line_node(get_addressed_line_node(second_addr), (unsigned char)c) < 0)
+                       return ERR;
+               break;
+       case 'l':
+               if (check_addr_range(current_addr, current_addr) < 0)
+                       return ERR;
+               GET_COMMAND_SUFFIX();
+               if (display_lines(first_addr, second_addr, gflag | GLS) < 0)
+                       return ERR;
+               gflag = 0;
+               break;
+       case 'm':
+               if (check_addr_range(current_addr, current_addr) < 0)
+                       return ERR;
+               GET_THIRD_ADDR(addr);
+               if (first_addr <= addr && addr < second_addr) {
+                       sprintf(errmsg, "invalid destination");
+                       return ERR;
+               }
+               GET_COMMAND_SUFFIX();
+               if (!isglobal) clear_undo_stack();
+               if (move_lines(addr) < 0)
+                       return ERR;
+               break;
+       case 'n':
+               if (check_addr_range(current_addr, current_addr) < 0)
+                       return ERR;
+               GET_COMMAND_SUFFIX();
+               if (display_lines(first_addr, second_addr, gflag | GNP) < 0)
+                       return ERR;
+               gflag = 0;
+               break;
+       case 'p':
+               if (check_addr_range(current_addr, current_addr) < 0)
+                       return ERR;
+               GET_COMMAND_SUFFIX();
+               if (display_lines(first_addr, second_addr, gflag | GPR) < 0)
+                       return ERR;
+               gflag = 0;
+               break;
+       case 'P':
+               if (addr_cnt > 0) {
+                       sprintf(errmsg, "unexpected address");
+                       return ERR;
+               }
+               GET_COMMAND_SUFFIX();
+               prompt = prompt ? NULL : optarg ? optarg : dps;
+               break;
+       case 'q':
+       case 'Q':
+               if (addr_cnt > 0) {
+                       sprintf(errmsg, "unexpected address");
+                       return ERR;
+               }
+               GET_COMMAND_SUFFIX();
+               gflag =  (modified && !scripted && c == 'q') ? EMOD : EOF;
+               break;
+       case 'r':
+               if (!isspace((unsigned char)*ibufp)) {
+                       sprintf(errmsg, "unexpected command suffix");
+                       return ERR;
+               } else if (addr_cnt == 0)
+                       second_addr = addr_last;
+               if ((fnp = get_filename()) == NULL)
+                       return ERR;
+               GET_COMMAND_SUFFIX();
+               if (!isglobal) clear_undo_stack();
+               if (*old_filename == '\0' && *fnp != '!')
+                       strlcpy(old_filename, fnp, sizeof(old_filename) - 2);
+#ifdef BACKWARDS
+               if (*fnp == '\0' && *old_filename == '\0') {
+                       sprintf(errmsg, "no current filename");
+                       return ERR;
+               }
+#endif
+               if ((addr = read_file(*fnp ? fnp : old_filename, second_addr)) < 0)
+                       return ERR;
+               else if (addr && addr != addr_last)
+                       modified = 1;
+               break;
+       case 's':
+               do {
+                       switch(*ibufp) {
+                       case '\n':
+                               sflags |=SGF;
+                               break;
+                       case 'g':
+                               sflags |= SGG;
+                               ibufp++;
+                               break;
+                       case 'p':
+                               sflags |= SGP;
+                               ibufp++;
+                               break;
+                       case 'r':
+                               sflags |= SGR;
+                               ibufp++;
+                               break;
+                       case '0': case '1': case '2': case '3': case '4': 
+                       case '5': case '6': case '7': case '8': case '9':
+                               STRTOL(sgnum, ibufp);
+                               sflags |= SGF;
+                               sgflag &= ~GSG;         /* override GSG */
+                               break;
+                       default:
+                               if (sflags) {
+                                       sprintf(errmsg, "invalid command suffix");
+                                       return ERR;
+                               }
+                       }
+               } while (sflags && *ibufp != '\n');
+               if (sflags && !pat) {
+                       sprintf(errmsg, "no previous substitution");
+                       return ERR;
+               } else if (sflags & SGG)
+                       sgnum = 0;              /* override numeric arg */
+               if (*ibufp != '\n' && *(ibufp + 1) == '\n') {
+                       sprintf(errmsg, "invalid pattern delimiter");
+                       return ERR;
+               }
+               tpat = pat;
+               SPL1();
+               if ((!sflags || (sflags & SGR)) &&
+                   (tpat = get_compiled_pattern()) == NULL) {
+                       SPL0();
+                       return ERR;
+               } else if (tpat != pat) {
+                       if (pat) {
+                               regfree(pat);
+                               free(pat);
+                       }
+                       pat = tpat;
+                       patlock = 1;            /* reserve pattern */
+               }
+               SPL0();
+               if (!sflags && extract_subst_tail(&sgflag, &sgnum) < 0)
+                       return ERR;
+               else if (isglobal)
+                       sgflag |= GLB;
+               else
+                       sgflag &= ~GLB;
+               if (sflags & SGG)
+                       sgflag ^= GSG;
+               if (sflags & SGP)
+                       sgflag ^= GPR, sgflag &= ~(GLS | GNP);
+               do {
+                       switch(*ibufp) {
+                       case 'p':
+                               sgflag |= GPR, ibufp++;
+                               break;
+                       case 'l':
+                               sgflag |= GLS, ibufp++;
+                               break;
+                       case 'n':
+                               sgflag |= GNP, ibufp++;
+                               break;
+                       default:
+                               n++;
+                       }
+               } while (!n);
+               if (check_addr_range(current_addr, current_addr) < 0)
+                       return ERR;
+               GET_COMMAND_SUFFIX();
+               if (!isglobal) clear_undo_stack();
+               if (search_and_replace(pat, sgflag, sgnum) < 0)
+                       return ERR;
+               break;
+       case 't':
+               if (check_addr_range(current_addr, current_addr) < 0)
+                       return ERR;
+               GET_THIRD_ADDR(addr);
+               GET_COMMAND_SUFFIX();
+               if (!isglobal) clear_undo_stack();
+               if (copy_lines(addr) < 0)
+                       return ERR;
+               break;
+       case 'u':
+               if (addr_cnt > 0) {
+                       sprintf(errmsg, "unexpected address");
+                       return ERR;
+               }
+               GET_COMMAND_SUFFIX();
+               if (pop_undo_stack() < 0)
+                       return ERR;
+               break;
+       case 'w':
+       case 'W':
+               if ((n = *ibufp) == 'q' || n == 'Q') {
+                       gflag = EOF;
+                       ibufp++;
+               }
+               if (!isspace((unsigned char)*ibufp)) {
+                       sprintf(errmsg, "unexpected command suffix");
+                       return ERR;
+               } else if ((fnp = get_filename()) == NULL)
+                       return ERR;
+               if (addr_cnt == 0 && !addr_last)
+                       first_addr = second_addr = 0;
+               else if (check_addr_range(1, addr_last) < 0)
+                       return ERR;
+               GET_COMMAND_SUFFIX();
+               if (*old_filename == '\0' && *fnp != '!')
+                       strlcpy(old_filename, fnp, sizeof(old_filename) - 2);
+#ifdef BACKWARDS
+               if (*fnp == '\0' && *old_filename == '\0') {
+                       sprintf(errmsg, "no current filename");
+                       return ERR;
+               }
+#endif
+               if ((addr = write_file(*fnp ? fnp : old_filename, 
+                   (c == 'W') ? "a" : "w", first_addr, second_addr)) < 0)
+                       return ERR;
+               else if (addr == addr_last)
+                       modified = 0;
+               else if (modified && !scripted && n == 'q')
+                       gflag = EMOD;
+               break;
+       case 'x':
+               if (addr_cnt > 0) {
+                       sprintf(errmsg, "unexpected address");
+                       return ERR;
+               }
+               GET_COMMAND_SUFFIX();
+#ifdef DES
+               des = get_keyword();
+#else
+               sprintf(errmsg, "crypt unavailable");
+               return ERR;
+#endif
+               break;
+       case 'z':
+#ifdef BACKWARDS
+               if (check_addr_range(first_addr = 1, current_addr + 1) < 0)
+#else
+               if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0)
+#endif
+                       return ERR;
+               else if ('0' < *ibufp && *ibufp <= '9')
+                       STRTOL(rows, ibufp);
+               GET_COMMAND_SUFFIX();
+               if (display_lines(second_addr, min(addr_last,
+                   second_addr + rows), gflag) < 0)
+                       return ERR;
+               gflag = 0;
+               break;
+       case '=':
+               GET_COMMAND_SUFFIX();
+               printf("%ld\n", addr_cnt ? second_addr : addr_last);
+               break;
+       case '!':
+               if (addr_cnt > 0) {
+                       sprintf(errmsg, "unexpected address");
+                       return ERR;
+               } else if ((sflags = get_shell_command()) < 0)
+                       return ERR;
+               GET_COMMAND_SUFFIX();
+               if (sflags) printf("%s\n", shcmd + 1);
+               system(shcmd + 1);
+               if (!scripted) printf("!\n");
+               break;
+       case '\n':
+#ifdef BACKWARDS
+               if (check_addr_range(first_addr = 1, current_addr + 1) < 0
+#else
+               if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0
+#endif
+                || display_lines(second_addr, second_addr, 0) < 0)
+                       return ERR;
+               break;
+       default:
+               sprintf(errmsg, "unknown command");
+               return ERR;
+       }
+       return gflag;
+}
+
+
+/* check_addr_range: return status of address range check */
+int
+check_addr_range(long n, long m)
+{
+       if (addr_cnt == 0) {
+               first_addr = n;
+               second_addr = m;
+       }
+       if (first_addr > second_addr || 1 > first_addr ||
+           second_addr > addr_last) {
+               sprintf(errmsg, "invalid address");
+               return ERR;
+       }
+       return 0;
+}
+
+
+/* get_matching_node_addr: return the address of the next line matching a 
+   pattern in a given direction.  wrap around begin/end of editor buffer if
+   necessary */
+long
+get_matching_node_addr(pattern_t *pat, int dir)
+{
+       char *s;
+       long n = current_addr;
+       line_t *lp;
+
+       if (!pat) return ERR;
+       do {
+               if ((n = dir ? INC_MOD(n, addr_last) :
+                   DEC_MOD(n, addr_last)) != 0) {
+                       lp = get_addressed_line_node(n);
+                       if ((s = get_sbuf_line(lp)) == NULL)
+                               return ERR;
+                       if (isbinary)
+                               NUL_TO_NEWLINE(s, lp->len);
+                       if (!regexec(pat, s, 0, NULL, 0))
+                               return n;
+               }
+       } while (n != current_addr);
+       sprintf(errmsg, "no match");
+       return  ERR;
+}
+
+
+/* get_filename: return pointer to copy of filename in the command buffer */
+char *
+get_filename(void)
+{
+       static char *file = NULL;
+       static int filesz = 0;
+
+       int n;
+
+       if (*ibufp != '\n') {
+               SKIP_BLANKS();
+               if (*ibufp == '\n') {
+                       sprintf(errmsg, "invalid filename");
+                       return NULL;
+               } else if ((ibufp = get_extended_line(&n, 1)) == NULL)
+                       return NULL;
+               else if (*ibufp == '!') {
+                       ibufp++;
+                       if ((n = get_shell_command()) < 0)
+                               return NULL;
+                       if (n) printf("%s\n", shcmd + 1);
+                       return shcmd;
+               } else if (n - 1 > MAXPATHLEN) {
+                       sprintf(errmsg, "filename too long");
+                       return  NULL;
+               }
+       }
+#ifndef BACKWARDS
+       else if (*old_filename == '\0') {
+               sprintf(errmsg, "no current filename");
+               return  NULL;
+       }
+#endif
+       REALLOC(file, filesz, MAXPATHLEN + 1, NULL);
+       for (n = 0; *ibufp != '\n';)
+               file[n++] = *ibufp++;
+       file[n] = '\0';
+       return is_legal_filename(file) ? file : NULL;
+}
+
+
+/* get_shell_command: read a shell command from stdin; return substitution
+   status */
+int
+get_shell_command(void)
+{
+       static char *buf = NULL;
+       static int n = 0;
+
+       char *s;                        /* substitution char pointer */
+       int i = 0;
+       int j = 0;
+
+       if (red) {
+               sprintf(errmsg, "shell access restricted");
+               return ERR;
+       } else if ((s = ibufp = get_extended_line(&j, 1)) == NULL)
+               return ERR;
+       REALLOC(buf, n, j + 1, ERR);
+       buf[i++] = '!';                 /* prefix command w/ bang */
+       while (*ibufp != '\n')
+               switch (*ibufp) {
+               default:
+                       REALLOC(buf, n, i + 2, ERR);
+                       buf[i++] = *ibufp;
+                       if (*ibufp++ == '\\')
+                               buf[i++] = *ibufp++;
+                       break;
+               case '!':
+                       if (s != ibufp) {
+                               REALLOC(buf, n, i + 1, ERR);
+                               buf[i++] = *ibufp++;
+                       }
+#ifdef BACKWARDS
+                       else if (shcmd == NULL || *(shcmd + 1) == '\0')
+#else
+                       else if (shcmd == NULL)
+#endif
+                       {
+                               sprintf(errmsg, "no previous command");
+                               return ERR;
+                       } else {
+                               REALLOC(buf, n, i + shcmdi, ERR);
+                               for (s = shcmd + 1; s < shcmd + shcmdi;)
+                                       buf[i++] = *s++;
+                               s = ibufp++;
+                       }
+                       break;
+               case '%':
+                       if (*old_filename  == '\0') {
+                               sprintf(errmsg, "no current filename");
+                               return ERR;
+                       }
+                       j = strlen(s = strip_escapes(old_filename));
+                       REALLOC(buf, n, i + j, ERR);
+                       while (j--)
+                               buf[i++] = *s++;
+                       s = ibufp++;
+                       break;
+               }
+       REALLOC(shcmd, shcmdsz, i + 1, ERR);
+       memcpy(shcmd, buf, i);
+       shcmd[shcmdi = i] = '\0';
+       return *s == '!' || *s == '%';
+}
+
+
+/* append_lines: insert text from stdin to after line n; stop when either a
+   single period is read or EOF; return status */
+int
+append_lines(long n)
+{
+       int l;
+       char *lp = ibuf;
+       char *eot;
+       undo_t *up = NULL;
+
+       for (current_addr = n;;) {
+               if (!isglobal) {
+                       if ((l = get_tty_line()) < 0)
+                               return ERR;
+                       else if (l == 0 || ibuf[l - 1] != '\n') {
+                               clearerr(stdin);
+                               return  l ? EOF : 0;
+                       }
+                       lp = ibuf;
+               } else if (*(lp = ibufp) == '\0')
+                       return 0;
+               else {
+                       while (*ibufp++ != '\n')
+                               ;
+                       l = ibufp - lp;
+               }
+               if (l == 2 && lp[0] == '.' && lp[1] == '\n') {
+                       return 0;
+               }
+               eot = lp + l;
+               SPL1();
+               do {
+                       if ((lp = put_sbuf_line(lp)) == NULL) {
+                               SPL0();
+                               return ERR;
+                       } else if (up)
+                               up->t = get_addressed_line_node(current_addr);
+                       else if ((up = push_undo_stack(UADD, current_addr,
+                           current_addr)) == NULL) {
+                               SPL0();
+                               return ERR;
+                       }
+               } while (lp != eot);
+               modified = 1;
+               SPL0();
+       }
+       /* NOTREACHED */
+}
+
+
+/* join_lines: replace a range of lines with the joined text of those lines */
+int
+join_lines(long from, long to)
+{
+       static char *buf = NULL;
+       static int n;
+
+       char *s;
+       int size = 0;
+       line_t *bp, *ep;
+
+       ep = get_addressed_line_node(INC_MOD(to, addr_last));
+       bp = get_addressed_line_node(from);
+       for (; bp != ep; bp = bp->q_forw) {
+               if ((s = get_sbuf_line(bp)) == NULL)
+                       return ERR;
+               REALLOC(buf, n, size + bp->len, ERR);
+               memcpy(buf + size, s, bp->len);
+               size += bp->len;
+       }
+       REALLOC(buf, n, size + 2, ERR);
+       memcpy(buf + size, "\n", 2);
+       if (delete_lines(from, to) < 0)
+               return ERR;
+       current_addr = from - 1;
+       SPL1();
+       if (put_sbuf_line(buf) == NULL ||
+           push_undo_stack(UADD, current_addr, current_addr) == NULL) {
+               SPL0();
+               return ERR;
+       }
+       modified = 1;
+       SPL0();
+       return 0;
+}
+
+
+/* move_lines: move a range of lines */
+int
+move_lines(long addr)
+{
+       line_t *b1, *a1, *b2, *a2;
+       long n = INC_MOD(second_addr, addr_last);
+       long p = first_addr - 1;
+       int done = (addr == first_addr - 1 || addr == second_addr);
+
+       SPL1();
+       if (done) {
+               a2 = get_addressed_line_node(n);
+               b2 = get_addressed_line_node(p);
+               current_addr = second_addr;
+       } else if (push_undo_stack(UMOV, p, n) == NULL ||
+           push_undo_stack(UMOV, addr, INC_MOD(addr, addr_last)) == NULL) {
+               SPL0();
+               return ERR;
+       } else {
+               a1 = get_addressed_line_node(n);
+               if (addr < first_addr) {
+                       b1 = get_addressed_line_node(p);
+                       b2 = get_addressed_line_node(addr);
+                                       /* this get_addressed_line_node last! */
+               } else {
+                       b2 = get_addressed_line_node(addr);
+                       b1 = get_addressed_line_node(p);
+                                       /* this get_addressed_line_node last! */
+               }
+               a2 = b2->q_forw;
+               REQUE(b2, b1->q_forw);
+               REQUE(a1->q_back, a2);
+               REQUE(b1, a1);
+               current_addr = addr + ((addr < first_addr) ? 
+                   second_addr - first_addr + 1 : 0);
+       }
+       if (isglobal)
+               unset_active_nodes(b2->q_forw, a2);
+       modified = 1;
+       SPL0();
+       return 0;
+}
+
+
+/* copy_lines: copy a range of lines; return status */
+int
+copy_lines(long addr)
+{
+       line_t *lp, *np = get_addressed_line_node(first_addr);
+       undo_t *up = NULL;
+       long n = second_addr - first_addr + 1;
+       long m = 0;
+
+       current_addr = addr;
+       if (first_addr <= addr && addr < second_addr) {
+               n =  addr - first_addr + 1;
+               m = second_addr - addr;
+       }
+       for (; n > 0; n=m, m=0, np = get_addressed_line_node(current_addr + 1))
+               for (; n-- > 0; np = np->q_forw) {
+                       SPL1();
+                       if ((lp = dup_line_node(np)) == NULL) {
+                               SPL0();
+                               return ERR;
+                       }
+                       add_line_node(lp);
+                       if (up)
+                               up->t = lp;
+                       else if ((up = push_undo_stack(UADD, current_addr,
+                           current_addr)) == NULL) {
+                               SPL0();
+                               return ERR;
+                       }
+                       modified = 1;
+                       SPL0();
+               }
+       return 0;
+}
+
+
+/* delete_lines: delete a range of lines */
+int
+delete_lines(long from, long to)
+{
+       line_t *n, *p;
+
+       SPL1();
+       if (push_undo_stack(UDEL, from, to) == NULL) {
+               SPL0();
+               return ERR;
+       }
+       n = get_addressed_line_node(INC_MOD(to, addr_last));
+       p = get_addressed_line_node(from - 1);
+                                       /* this get_addressed_line_node last! */
+       if (isglobal)
+               unset_active_nodes(p->q_forw, n);
+       REQUE(p, n);
+       addr_last -= to - from + 1;
+       current_addr = from - 1;
+       modified = 1;
+       SPL0();
+       return 0;
+}
+
+
+/* display_lines: print a range of lines to stdout */
+int
+display_lines(long from, long to, int gflag)
+{
+       line_t *bp;
+       line_t *ep;
+       char *s;
+
+       if (!from) {
+               sprintf(errmsg, "invalid address");
+               return ERR;
+       }
+       ep = get_addressed_line_node(INC_MOD(to, addr_last));
+       bp = get_addressed_line_node(from);
+       for (; bp != ep; bp = bp->q_forw) {
+               if ((s = get_sbuf_line(bp)) == NULL)
+                       return ERR;
+               if (put_tty_line(s, bp->len, current_addr = from++, gflag) < 0)
+                       return ERR;
+       }
+       return 0;
+}
+
+
+#define MAXMARK 26                     /* max number of marks */
+
+line_t *mark[MAXMARK];                 /* line markers */
+int markno;                            /* line marker count */
+
+/* mark_line_node: set a line node mark */
+int
+mark_line_node(line_t *lp, int n)
+{
+       if (!islower(n)) {
+               sprintf(errmsg, "invalid mark character");
+               return ERR;
+       } else if (mark[n - 'a'] == NULL)
+               markno++;
+       mark[n - 'a'] = lp;
+       return 0;
+}
+
+
+/* get_marked_node_addr: return address of a marked line */
+long
+get_marked_node_addr(int n)
+{
+       if (!islower(n)) {
+               sprintf(errmsg, "invalid mark character");
+               return ERR;
+       }
+       return get_line_node_addr(mark[n - 'a']);
+}
+
+
+/* unmark_line_node: clear line node mark */
+void
+unmark_line_node(line_t *lp)
+{
+       int i;
+
+       for (i = 0; markno && i < MAXMARK; i++)
+               if (mark[i] == lp) {
+                       mark[i] = NULL;
+                       markno--;
+               }
+}
+
+
+/* dup_line_node: return a pointer to a copy of a line node */
+line_t *
+dup_line_node(line_t *lp)
+{
+       line_t *np;
+
+       if ((np = (line_t *) malloc(sizeof(line_t))) == NULL) {
+               fprintf(stderr, "%s\n", strerror(errno));
+               sprintf(errmsg, "out of memory");
+               return NULL;
+       }
+       np->seek = lp->seek;
+       np->len = lp->len;
+       return np;
+}
+
+
+/* has_trailing_escape:  return the parity of escapes preceding a character
+   in a string */
+int
+has_trailing_escape(char *s, char *t)
+{
+    return (s == t || *(t - 1) != '\\') ? 0 : !has_trailing_escape(s, t - 1);
+}
+
+
+/* strip_escapes: return copy of escaped string of at most length MAXPATHLEN */
+char *
+strip_escapes(const char *s)
+{
+       static char *file = NULL;
+       static int filesz = 0;
+
+       int i = 0;
+
+       REALLOC(file, filesz, MAXPATHLEN + 1, NULL);
+       while ((i < (filesz - 1)) &&
+              (file[i++] = (*s == '\\') != '\0' ? *++s : *s))
+               s++;
+       file[filesz - 1] = '\0';
+       return file;
+}
+
+
+void
+signal_hup(int signo)
+{
+       if (mutex)
+               sigflags |= (1 << (signo - 1));
+       else    handle_hup(signo);
+}
+
+
+void
+signal_int(int signo)
+{
+       if (mutex)
+               sigflags |= (1 << (signo - 1));
+       else    handle_int(signo);
+}
+
+
+void
+handle_hup(int signo)
+{
+       char *hup = NULL;               /* hup filename */
+       char *s;
+       int n;
+
+       if (!sigactive)
+               quit(1);
+       sigflags &= ~(1 << (signo - 1));
+       if (addr_last && write_file("ed.hup", "w", 1, addr_last) < 0 &&
+           (s = getenv("HOME")) != NULL &&
+           (n = strlen(s)) + 8 <= MAXPATHLEN &&        /* "ed.hup" + '/' */
+           (hup = (char *) malloc(n + 10)) != NULL) {
+               strcpy(hup, s);
+               if (hup[n - 1] != '/')
+                       hup[n] = '/', hup[n+1] = '\0';
+               strcat(hup, "ed.hup");
+               write_file(hup, "w", 1, addr_last);
+       }
+       quit(2);
+}
+
+
+void
+handle_int(int signo)
+{
+       if (!sigactive)
+               quit(1);
+       sigflags &= ~(1 << (signo - 1));
+#ifdef _POSIX_SOURCE
+       siglongjmp(env, -1);
+#else
+       longjmp(env, -1);
+#endif
+}
+
+
+int cols = 72;                         /* wrap column */
+
+void
+handle_winch(int signo)
+{
+       struct winsize ws;              /* window size structure */
+
+       sigflags &= ~(1 << (signo - 1));
+       if (ioctl(0, TIOCGWINSZ, (char *) &ws) >= 0) {
+               if (ws.ws_row > 2) rows = ws.ws_row - 2;
+               if (ws.ws_col > 8) cols = ws.ws_col - 8;
+       }
+}
+
+
+/* is_legal_filename: return a legal filename */
+int
+is_legal_filename(char *s)
+{
+       if (red && (*s == '!' || !strcmp(s, "..") || strchr(s, '/'))) {
+               sprintf(errmsg, "shell access restricted");
+               return 0;
+       }
+       return 1;
+}
diff --git a/bin/ed/re.c b/bin/ed/re.c
new file mode 100644 (file)
index 0000000..a2696ad
--- /dev/null
@@ -0,0 +1,135 @@
+/*     $NetBSD: re.c,v 1.19 2005/02/17 16:29:26 xtraeme Exp $  */
+
+/* re.c: This file contains the regular expression interface routines for
+   the ed line editor. */
+/*-
+ * Copyright (c) 1993 Andrew Moore, Talke Studio.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char *rcsid = "@(#)re.c,v 1.6 1994/02/01 00:34:43 alm Exp";
+#else
+__RCSID("$NetBSD: re.c,v 1.19 2005/02/17 16:29:26 xtraeme Exp $");
+#endif
+#endif /* not lint */
+
+#include "ed.h"
+
+
+char errmsg[MAXPATHLEN + 40] = "";
+
+/* get_compiled_pattern: return pointer to compiled pattern from command 
+   buffer */
+pattern_t *
+get_compiled_pattern(void)
+{
+       static pattern_t *expr = NULL;
+
+       char *exps;
+       char delimiter;
+       int n;
+
+       if ((delimiter = *ibufp) == ' ') {
+               sprintf(errmsg, "invalid pattern delimiter");
+               return NULL;
+       } else if (delimiter == '\n' || *++ibufp == '\n' || *ibufp == delimiter) {
+               if (!expr) sprintf(errmsg, "no previous pattern");
+               return expr;
+       } else if ((exps = extract_pattern(delimiter)) == NULL)
+               return NULL;
+       /* buffer alloc'd && not reserved */
+       if (expr && !patlock)
+               regfree(expr);
+       else if ((expr = (pattern_t *) malloc(sizeof(pattern_t))) == NULL) {
+               fprintf(stderr, "%s\n", strerror(errno));
+               sprintf(errmsg, "out of memory");
+               return NULL;
+       }
+       patlock = 0;
+       if ((n = regcomp(expr, exps, ere)) != 0) {
+               regerror(n, expr, errmsg, sizeof errmsg);
+               free(expr);
+               return expr = NULL;
+       }
+       return expr;
+}
+
+
+/* extract_pattern: copy a pattern string from the command buffer; return
+   pointer to the copy */
+char *
+extract_pattern(int delimiter)
+{
+       static char *lhbuf = NULL;      /* buffer */
+       static int lhbufsz = 0;         /* buffer size */
+
+       char *nd;
+       int len;
+
+       for (nd = ibufp; *nd != delimiter && *nd != '\n'; nd++)
+               switch (*nd) {
+               default:
+                       break;
+               case '[':
+                       if ((nd = parse_char_class(++nd)) == NULL) {
+                               sprintf(errmsg, "unbalanced brackets ([])");
+                               return NULL;
+                       }
+                       break;
+               case '\\':
+                       if (*++nd == '\n') {
+                               sprintf(errmsg, "trailing backslash (\\)");
+                               return NULL;
+                       }
+                       break;
+               }
+       len = nd - ibufp;
+       REALLOC(lhbuf, lhbufsz, len + 1, NULL);
+       memcpy(lhbuf, ibufp, len);
+       lhbuf[len] = '\0';
+       ibufp = nd;
+       return (isbinary) ? NUL_TO_NEWLINE(lhbuf, len) : lhbuf;
+}
+
+
+/* parse_char_class: expand a POSIX character class */
+char *
+parse_char_class(char *s)
+{
+       int c, d;
+
+       if (*s == '^')
+               s++;
+       if (*s == ']')
+               s++;
+       for (; *s != ']' && *s != '\n'; s++)
+               if (*s == '[' && ((d = *(s+1)) == '.' || d == ':' || d == '='))
+                       for (s++, c = *++s; *s != ']' || c != d; s++)
+                               if ((c = *s) == '\n')
+                                       return NULL;
+       return  (*s == ']') ? s : NULL;
+}
diff --git a/bin/ed/sub.c b/bin/ed/sub.c
new file mode 100644 (file)
index 0000000..1b51c6e
--- /dev/null
@@ -0,0 +1,260 @@
+/*     $NetBSD: sub.c,v 1.6 2005/02/17 16:29:26 xtraeme Exp $  */
+
+/* sub.c: This file contains the substitution routines for the ed 
+   line editor */
+/*-
+ * Copyright (c) 1993 Andrew Moore, Talke Studio.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char *rcsid = "@(#)sub.c,v 1.1 1994/02/01 00:34:44 alm Exp";
+#else
+__RCSID("$NetBSD: sub.c,v 1.6 2005/02/17 16:29:26 xtraeme Exp $");
+#endif
+#endif /* not lint */
+
+#include "ed.h"
+
+
+char *rhbuf;                   /* rhs substitution buffer */
+int rhbufsz;                   /* rhs substitution buffer size */
+int rhbufi;                    /* rhs substitution buffer index */
+
+/* extract_subst_tail: extract substitution tail from the command buffer */
+int
+extract_subst_tail(int *flagp, long *np)
+{
+       char delimiter;
+
+       *flagp = *np = 0;
+       if ((delimiter = *ibufp) == '\n') {
+               rhbufi = 0;
+               *flagp = GPR;
+               return 0;
+       } else if (extract_subst_template() == NULL)
+               return  ERR;
+       else if (*ibufp == '\n') {
+               *flagp = GPR;
+               return 0;
+       } else if (*ibufp == delimiter)
+               ibufp++;
+       if ('1' <= *ibufp && *ibufp <= '9') {
+               STRTOL(*np, ibufp);
+               return 0;
+       } else if (*ibufp == 'g') {
+               ibufp++;
+               *flagp = GSG;
+               return 0;
+       }
+       return 0;
+}
+
+
+/* extract_subst_template: return pointer to copy of substitution template
+   in the command buffer */
+char *
+extract_subst_template(void)
+{
+       int n = 0;
+       int i = 0;
+       char c;
+       char delimiter = *ibufp++;
+
+       if (*ibufp == '%' && *(ibufp + 1) == delimiter) {
+               ibufp++;
+               if (!rhbuf) sprintf(errmsg, "no previous substitution");
+               return rhbuf;
+       }
+       while (*ibufp != delimiter) {
+               REALLOC(rhbuf, rhbufsz, i + 2, NULL);
+               if ((c = rhbuf[i++] = *ibufp++) == '\n' && *ibufp == '\0') {
+                       i--, ibufp--;
+                       break;
+               } else if (c != '\\')
+                       ;
+               else if ((rhbuf[i++] = *ibufp++) != '\n')
+                       ;
+               else if (!isglobal) {
+                       while ((n = get_tty_line()) == 0 ||
+                           (n > 0 && ibuf[n - 1] != '\n'))
+                               clearerr(stdin);
+                       if (n < 0)
+                               return NULL;
+               }
+       }
+       REALLOC(rhbuf, rhbufsz, i + 1, NULL);
+       rhbuf[rhbufi = i] = '\0';
+       return  rhbuf;
+}
+
+
+char *rbuf;                    /* substitute_matching_text buffer */
+int rbufsz;                    /* substitute_matching_text buffer size */
+
+/* search_and_replace: for each line in a range, change text matching a pattern
+   according to a substitution template; return status  */
+int
+search_and_replace(pattern_t *pat, int gflag, int kth)
+{
+       undo_t *up;
+       char *txt;
+       char *eot;
+       long lc;
+       long xa = current_addr;
+       int nsubs = 0;
+       line_t *lp;
+       int len;
+
+       current_addr = first_addr - 1;
+       for (lc = 0; lc <= second_addr - first_addr; lc++) {
+               lp = get_addressed_line_node(++current_addr);
+               if ((len = substitute_matching_text(pat, lp, gflag, kth)) < 0)
+                       return ERR;
+               else if (len) {
+                       up = NULL;
+                       if (delete_lines(current_addr, current_addr) < 0)
+                               return ERR;
+                       txt = rbuf;
+                       eot = rbuf + len;
+                       SPL1();
+                       do {
+                               if ((txt = put_sbuf_line(txt)) == NULL) {
+                                       SPL0();
+                                       return ERR;
+                               } else if (up)
+                                       up->t = get_addressed_line_node(current_addr);
+                               else if ((up = push_undo_stack(UADD,
+                                   current_addr, current_addr)) == NULL) {
+                                       SPL0();
+                                       return ERR;
+                               }
+                       } while (txt != eot);
+                       SPL0();
+                       nsubs++;
+                       xa = current_addr;
+               }
+       }
+       current_addr = xa;
+       if  (nsubs == 0 && !(gflag & GLB)) {
+               sprintf(errmsg, "no match");
+               return ERR;
+       } else if ((gflag & (GPR | GLS | GNP)) &&
+           display_lines(current_addr, current_addr, gflag) < 0)
+               return ERR;
+       return 0;
+}
+
+
+/* substitute_matching_text: replace text matched by a pattern according to
+   a substitution template; return pointer to the modified text */
+int
+substitute_matching_text(pattern_t *pat, line_t *lp, int gflag, int kth)
+{
+       int off = 0;
+       int changed = 0;
+       int matchno = 0;
+       int i = 0;
+       regmatch_t rm[SE_MAX];
+       char *txt;
+       char *eot;
+
+       if ((txt = get_sbuf_line(lp)) == NULL)
+               return ERR;
+       if (isbinary) 
+               NUL_TO_NEWLINE(txt, lp->len);
+       eot = txt + lp->len;
+       if (!regexec(pat, txt, SE_MAX, rm, 0)) {
+               do {
+                       if (!kth || kth == ++matchno) {
+                               changed++;
+                               i = rm[0].rm_so;
+                               REALLOC(rbuf, rbufsz, off + i, ERR);
+                               if (isbinary)
+                                       NEWLINE_TO_NUL(txt, rm[0].rm_eo);
+                               memcpy(rbuf + off, txt, i);
+                               off += i;
+                               if ((off = apply_subst_template(txt, rm, off,
+                                   pat->re_nsub)) < 0)
+                                       return ERR;
+                       } else {
+                               i = rm[0].rm_eo;
+                               REALLOC(rbuf, rbufsz, off + i, ERR);
+                               if (isbinary)
+                                       NEWLINE_TO_NUL(txt, i);
+                               memcpy(rbuf + off, txt, i);
+                               off += i;
+                       }
+                       txt += rm[0].rm_eo;
+               } while (*txt && (!changed || ((gflag & GSG) && rm[0].rm_eo))
+                   && !regexec(pat, txt, SE_MAX, rm, REG_NOTBOL));
+               i = eot - txt;
+               REALLOC(rbuf, rbufsz, off + i + 2, ERR);
+               if (i > 0 && !rm[0].rm_eo && (gflag & GSG)) {
+                       sprintf(errmsg, "infinite substitution loop");
+                       return  ERR;
+               }
+               if (isbinary)
+                       NEWLINE_TO_NUL(txt, i);
+               memcpy(rbuf + off, txt, i);
+               memcpy(rbuf + off + i, "\n", 2);
+       }
+       return changed ? off + i + 1 : 0;
+}
+
+
+/* apply_subst_template: modify text according to a substitution template;
+   return offset to end of modified text */
+int
+apply_subst_template(char *boln, regmatch_t *rm, int off, int re_nsub)
+{
+       int j = 0;
+       int k = 0;
+       int n;
+       char *sub = rhbuf;
+
+       for (; sub - rhbuf < rhbufi; sub++)
+               if (*sub == '&') {
+                       j = rm[0].rm_so;
+                       k = rm[0].rm_eo;
+                       REALLOC(rbuf, rbufsz, off + k - j, ERR);
+                       while (j < k)
+                               rbuf[off++] = boln[j++];
+               } else if (*sub == '\\' && '1' <= *++sub && *sub <= '9' &&
+                   (n = *sub - '0') <= re_nsub) {
+                       j = rm[n].rm_so;
+                       k = rm[n].rm_eo;
+                       REALLOC(rbuf, rbufsz, off + k - j, ERR);
+                       while (j < k)
+                               rbuf[off++] = boln[j++];
+               } else {
+                       REALLOC(rbuf, rbufsz, off + 1, ERR);
+                       rbuf[off++] = *sub;
+               }
+       REALLOC(rbuf, rbufsz, off + 1, ERR);
+       rbuf[off] = '\0';
+       return off;
+}
diff --git a/bin/ed/test/=.err b/bin/ed/test/=.err
new file mode 100644 (file)
index 0000000..6a60559
--- /dev/null
@@ -0,0 +1 @@
+1,$=
diff --git a/bin/ed/test/Makefile b/bin/ed/test/Makefile
new file mode 100644 (file)
index 0000000..fe631a8
--- /dev/null
@@ -0,0 +1,28 @@
+#      $NetBSD: Makefile,v 1.12 2003/10/26 03:50:07 lukem Exp $
+
+.include <bsd.own.mk>
+
+ED?=   ../obj/ed
+
+all: check
+       @:
+
+check: build test
+       @if grep -h '\*\*\*' errs.o scripts.o; then :; else \
+               echo "tests completed successfully."; \
+       fi
+
+build: mkscripts.sh
+       @if [ -f errs.o ]; then :; else \
+               echo "building test scripts for $(ED) ..."; \
+               ${HOST_SH} ${.CURDIR}/mkscripts.sh $(ED); \
+       fi
+
+test: build ckscripts.sh
+       @echo testing $(ED) ...
+       @${HOST_SH} ckscripts.sh $(ED)
+
+clean:
+       rm -f *.ed *.red *.[oz] *~
+
+.include <bsd.prog.mk>
diff --git a/bin/ed/test/README b/bin/ed/test/README
new file mode 100644 (file)
index 0000000..73d7f2e
--- /dev/null
@@ -0,0 +1,32 @@
+$NetBSD: README,v 1.8 1995/03/21 09:05:18 cgd Exp $
+
+The files in this directory with suffixes `.t', `.d', `.r' and `.err' are
+used for testing ed.  To run the tests, set the ED variable in the Makefile
+for the path name of the program to be tested (e.g., /bin/ed), and type
+`make'.  The tests do not exhaustively verify POSIX compliance nor do
+they verify correct 8-bit or long line support.
+
+The test file suffixes have the following meanings:
+.t    Template - a list of ed commands from which an ed script is
+      constructed
+.d    Data - read by an ed script
+.r    Result - the expected output after processing data via an ed
+      script.
+.err  Error - invalid ed commands that should generate an error
+
+The output of the tests is written to the two files err.o and scripts.o.
+At the end of the tests, these files are grep'ed for error messages,
+which look like:
+       *** The script u.ed exited abnormally ***
+or:
+       *** Output u.o of script u.ed is incorrect ***
+
+The POSIX requirement that an address range not be used where at most
+a single address is expected has been relaxed in this version of ed.
+Therefore, the  following scripts  which test for compliance with this
+POSIX rule exit abnormally:
+=-err.ed
+a1-err.ed
+i1-err.ed
+k1-err.ed
+r1-err.ed
diff --git a/bin/ed/test/TODO b/bin/ed/test/TODO
new file mode 100644 (file)
index 0000000..c516d1e
--- /dev/null
@@ -0,0 +1,17 @@
+$NetBSD: TODO,v 1.3 1995/03/21 09:05:20 cgd Exp $
+
+Some missing tests:
+0) g/./s^@^@   - okay: NULs in commands
+1) g/./s/^@/   - okay: NULs in patterns
+2) a
+   hello^V^Jworld      
+   .           - okay: embedded newlines in insert mode
+3) ed ""       - error: invalid filename
+4) red ..      - error: restricted
+5) red /       - error: restricted
+5) red !xx     - error: restricted
+6) ed -x       - verify: 8-bit clean
+7) ed          - verify: long-line support
+8) ed          - verify: interactive/help mode
+9) G/pat/      - verify: global interactive command
+10) V/pat/     - verify: global interactive command
diff --git a/bin/ed/test/a.r b/bin/ed/test/a.r
new file mode 100644 (file)
index 0000000..26257bd
--- /dev/null
@@ -0,0 +1,8 @@
+hello world
+line 1
+hello world!
+line 2
+line 3
+line 4
+line5
+hello world!!
diff --git a/bin/ed/test/a.t b/bin/ed/test/a.t
new file mode 100644 (file)
index 0000000..ac98c40
--- /dev/null
@@ -0,0 +1,9 @@
+0a
+hello world
+.
+2a
+hello world!
+.
+$a
+hello world!!
+.
diff --git a/bin/ed/test/a1.err b/bin/ed/test/a1.err
new file mode 100644 (file)
index 0000000..e80815f
--- /dev/null
@@ -0,0 +1,3 @@
+1,$a
+hello world
+.
diff --git a/bin/ed/test/a2.err b/bin/ed/test/a2.err
new file mode 100644 (file)
index 0000000..ec4b00b
--- /dev/null
@@ -0,0 +1,3 @@
+aa
+hello world
+.
diff --git a/bin/ed/test/addr.r b/bin/ed/test/addr.r
new file mode 100644 (file)
index 0000000..04caf17
--- /dev/null
@@ -0,0 +1,2 @@
+line 2
+line9
diff --git a/bin/ed/test/addr.t b/bin/ed/test/addr.t
new file mode 100644 (file)
index 0000000..750b224
--- /dev/null
@@ -0,0 +1,5 @@
+1 d
+1 1 d
+1,2,d
+1;+ + ,d
+1,2;., + 2d
diff --git a/bin/ed/test/addr1.err b/bin/ed/test/addr1.err
new file mode 100644 (file)
index 0000000..29d6383
--- /dev/null
@@ -0,0 +1 @@
+100
diff --git a/bin/ed/test/addr2.err b/bin/ed/test/addr2.err
new file mode 100644 (file)
index 0000000..e96acb9
--- /dev/null
@@ -0,0 +1 @@
+-100
diff --git a/bin/ed/test/ascii.r b/bin/ed/test/ascii.r
new file mode 100644 (file)
index 0000000..c866266
Binary files /dev/null and b/bin/ed/test/ascii.r differ
diff --git a/bin/ed/test/ascii.t b/bin/ed/test/ascii.t
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/bin/ed/test/bang1.err b/bin/ed/test/bang1.err
new file mode 100644 (file)
index 0000000..630af90
--- /dev/null
@@ -0,0 +1 @@
+.!date
diff --git a/bin/ed/test/bang1.r b/bin/ed/test/bang1.r
new file mode 100644 (file)
index 0000000..dcf02b2
--- /dev/null
@@ -0,0 +1 @@
+okay
diff --git a/bin/ed/test/bang1.t b/bin/ed/test/bang1.t
new file mode 100644 (file)
index 0000000..d7b1fea
--- /dev/null
@@ -0,0 +1,5 @@
+!read one
+hello, world
+a
+okay
+.
diff --git a/bin/ed/test/bang2.err b/bin/ed/test/bang2.err
new file mode 100644 (file)
index 0000000..79d8956
--- /dev/null
@@ -0,0 +1 @@
+!!
diff --git a/bin/ed/test/c.r b/bin/ed/test/c.r
new file mode 100644 (file)
index 0000000..0fb3e4f
--- /dev/null
@@ -0,0 +1,4 @@
+at the top
+between top/middle
+in the middle
+at the bottom
diff --git a/bin/ed/test/c.t b/bin/ed/test/c.t
new file mode 100644 (file)
index 0000000..ebdd536
--- /dev/null
@@ -0,0 +1,12 @@
+1c
+at the top
+.
+4c
+in the middle
+.
+$c
+at the bottom
+.
+2,3c
+between top/middle
+.
diff --git a/bin/ed/test/c1.err b/bin/ed/test/c1.err
new file mode 100644 (file)
index 0000000..658ec38
--- /dev/null
@@ -0,0 +1,3 @@
+cc
+hello world
+.
diff --git a/bin/ed/test/c2.err b/bin/ed/test/c2.err
new file mode 100644 (file)
index 0000000..24b3227
--- /dev/null
@@ -0,0 +1,3 @@
+0c
+hello world
+.
diff --git a/bin/ed/test/ckscripts.sh b/bin/ed/test/ckscripts.sh
new file mode 100755 (executable)
index 0000000..86a19b1
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/sh -
+#      $NetBSD: ckscripts.sh,v 1.9 1995/04/23 10:07:34 cgd Exp $
+#
+# This script runs the .ed scripts generated by mkscripts.sh
+# and compares their output against the .r files, which contain
+# the correct output
+
+PATH="/bin:/usr/bin:/usr/local/bin/:."
+ED=$1
+[ ! -x $ED ] && { echo "$ED: cannot execute"; exit 1; }
+
+# Run the *.red scripts first, since these don't generate output;
+# they exit with non-zero status
+for i in *.red; do
+       echo $i
+       if $i; then
+               echo "*** The script $i exited abnormally  ***"
+       fi
+done >errs.o 2>&1
+
+# Run the remainding scripts; they exit with zero status
+for i in *.ed; do
+#      base=`expr $i : '\([^.]*\)'`
+#      base=`echo $i | sed 's/\..*//'`
+       base=`$ED - \!"echo $i" <<-EOF
+               s/\..*
+       EOF`
+       if $base.ed; then
+               if cmp -s $base.o $base.r; then :; else
+                       echo "*** Output $base.o of script $i is incorrect ***"
+               fi
+       else
+               echo "*** The script $i exited abnormally ***"
+       fi
+done >scripts.o 2>&1
+
+grep -h '\*\*\*' errs.o scripts.o
diff --git a/bin/ed/test/d.err b/bin/ed/test/d.err
new file mode 100644 (file)
index 0000000..f03f694
--- /dev/null
@@ -0,0 +1 @@
+dd
diff --git a/bin/ed/test/d.r b/bin/ed/test/d.r
new file mode 100644 (file)
index 0000000..b7e242c
--- /dev/null
@@ -0,0 +1 @@
+line 2
diff --git a/bin/ed/test/d.t b/bin/ed/test/d.t
new file mode 100644 (file)
index 0000000..c7c473f
--- /dev/null
@@ -0,0 +1,3 @@
+1d
+2;+1d
+$d
diff --git a/bin/ed/test/e1.err b/bin/ed/test/e1.err
new file mode 100644 (file)
index 0000000..827cc29
--- /dev/null
@@ -0,0 +1 @@
+ee e1.err
diff --git a/bin/ed/test/e1.r b/bin/ed/test/e1.r
new file mode 100644 (file)
index 0000000..e656728
--- /dev/null
@@ -0,0 +1 @@
+E e1.t
diff --git a/bin/ed/test/e1.t b/bin/ed/test/e1.t
new file mode 100644 (file)
index 0000000..e656728
--- /dev/null
@@ -0,0 +1 @@
+E e1.t
diff --git a/bin/ed/test/e2.err b/bin/ed/test/e2.err
new file mode 100644 (file)
index 0000000..779a64b
--- /dev/null
@@ -0,0 +1 @@
+.e e2.err
diff --git a/bin/ed/test/e2.r b/bin/ed/test/e2.r
new file mode 100644 (file)
index 0000000..59ebf11
--- /dev/null
@@ -0,0 +1 @@
+hello world-
diff --git a/bin/ed/test/e2.t b/bin/ed/test/e2.t
new file mode 100644 (file)
index 0000000..aa44630
--- /dev/null
@@ -0,0 +1 @@
+E !echo hello world-
diff --git a/bin/ed/test/e3.err b/bin/ed/test/e3.err
new file mode 100644 (file)
index 0000000..80a7fdc
--- /dev/null
@@ -0,0 +1 @@
+ee.err
diff --git a/bin/ed/test/e3.r b/bin/ed/test/e3.r
new file mode 100644 (file)
index 0000000..aa44630
--- /dev/null
@@ -0,0 +1 @@
+E !echo hello world-
diff --git a/bin/ed/test/e3.t b/bin/ed/test/e3.t
new file mode 100644 (file)
index 0000000..1c50726
--- /dev/null
@@ -0,0 +1 @@
+E
diff --git a/bin/ed/test/e4.r b/bin/ed/test/e4.r
new file mode 100644 (file)
index 0000000..aa44630
--- /dev/null
@@ -0,0 +1 @@
+E !echo hello world-
diff --git a/bin/ed/test/e4.t b/bin/ed/test/e4.t
new file mode 100644 (file)
index 0000000..d905d9d
--- /dev/null
@@ -0,0 +1 @@
+e
diff --git a/bin/ed/test/f1.err b/bin/ed/test/f1.err
new file mode 100644 (file)
index 0000000..e60975a
--- /dev/null
@@ -0,0 +1 @@
+.f f1.err
diff --git a/bin/ed/test/f2.err b/bin/ed/test/f2.err
new file mode 100644 (file)
index 0000000..26d1c5e
--- /dev/null
@@ -0,0 +1 @@
+ff1.err
diff --git a/bin/ed/test/g1.err b/bin/ed/test/g1.err
new file mode 100644 (file)
index 0000000..f95ea22
--- /dev/null
@@ -0,0 +1 @@
+g/./s //x/
diff --git a/bin/ed/test/g1.r b/bin/ed/test/g1.r
new file mode 100644 (file)
index 0000000..578a44b
--- /dev/null
@@ -0,0 +1,15 @@
+line5
+help! world
+order
+line 4
+help! world
+order
+line 3
+help! world
+order
+line 2
+help! world
+order
+line 1
+help! world
+order
diff --git a/bin/ed/test/g1.t b/bin/ed/test/g1.t
new file mode 100644 (file)
index 0000000..2d0b54f
--- /dev/null
@@ -0,0 +1,6 @@
+g/./m0
+g/./s/$/\
+hello world
+g/hello /s/lo/p!/\
+a\
+order
diff --git a/bin/ed/test/g2.err b/bin/ed/test/g2.err
new file mode 100644 (file)
index 0000000..0ff6a5a
--- /dev/null
@@ -0,0 +1 @@
+g//s/./x/
diff --git a/bin/ed/test/g2.r b/bin/ed/test/g2.r
new file mode 100644 (file)
index 0000000..3b18e51
--- /dev/null
@@ -0,0 +1 @@
+hello world
diff --git a/bin/ed/test/g2.t b/bin/ed/test/g2.t
new file mode 100644 (file)
index 0000000..831ee83
--- /dev/null
@@ -0,0 +1,2 @@
+g/[2-4]/-1,+1c\
+hello world
diff --git a/bin/ed/test/g3.err b/bin/ed/test/g3.err
new file mode 100644 (file)
index 0000000..01058d8
--- /dev/null
@@ -0,0 +1 @@
+g
diff --git a/bin/ed/test/g3.r b/bin/ed/test/g3.r
new file mode 100644 (file)
index 0000000..cc6fbdd
--- /dev/null
@@ -0,0 +1,5 @@
+linc 3
+xine 1
+xine 2
+xinc 4
+xinc5
diff --git a/bin/ed/test/g3.t b/bin/ed/test/g3.t
new file mode 100644 (file)
index 0000000..2d052a6
--- /dev/null
@@ -0,0 +1,4 @@
+g/./s//x/\
+3m0
+g/./s/e/c/\
+2,3m1
diff --git a/bin/ed/test/g4.r b/bin/ed/test/g4.r
new file mode 100644 (file)
index 0000000..350882d
--- /dev/null
@@ -0,0 +1,7 @@
+hello
+zine 1
+line 2
+line 3
+line 4
+line5
+world
diff --git a/bin/ed/test/g4.t b/bin/ed/test/g4.t
new file mode 100644 (file)
index 0000000..ec61816
--- /dev/null
@@ -0,0 +1,13 @@
+g/./s/./x/\
+u\
+s/./y/\
+u\
+s/./z/\
+u
+u
+0a
+hello
+.
+$a
+world
+.
diff --git a/bin/ed/test/g5.r b/bin/ed/test/g5.r
new file mode 100644 (file)
index 0000000..15a2675
--- /dev/null
@@ -0,0 +1,9 @@
+line 1
+line 2
+line 3
+line 2
+line 3
+line 1
+line 3
+line 1
+line 2
diff --git a/bin/ed/test/g5.t b/bin/ed/test/g5.t
new file mode 100644 (file)
index 0000000..e213481
--- /dev/null
@@ -0,0 +1,2 @@
+g/./1,3t$\
+1d
diff --git a/bin/ed/test/h.err b/bin/ed/test/h.err
new file mode 100644 (file)
index 0000000..a71e506
--- /dev/null
@@ -0,0 +1 @@
+.h
diff --git a/bin/ed/test/i.r b/bin/ed/test/i.r
new file mode 100644 (file)
index 0000000..5f27af0
--- /dev/null
@@ -0,0 +1,8 @@
+hello world
+hello world!
+line 1
+line 2
+line 3
+line 4
+hello world!!
+line5
diff --git a/bin/ed/test/i.t b/bin/ed/test/i.t
new file mode 100644 (file)
index 0000000..d1d9805
--- /dev/null
@@ -0,0 +1,9 @@
+1i
+hello world
+.
+2i
+hello world!
+.
+$i
+hello world!!
+.
diff --git a/bin/ed/test/i1.err b/bin/ed/test/i1.err
new file mode 100644 (file)
index 0000000..aaddede
--- /dev/null
@@ -0,0 +1,3 @@
+1,$i
+hello world
+.
diff --git a/bin/ed/test/i2.err b/bin/ed/test/i2.err
new file mode 100644 (file)
index 0000000..b63f5ac
--- /dev/null
@@ -0,0 +1,3 @@
+ii
+hello world
+.
diff --git a/bin/ed/test/i3.err b/bin/ed/test/i3.err
new file mode 100644 (file)
index 0000000..6d200c8
--- /dev/null
@@ -0,0 +1,3 @@
+0i
+hello world
+.
diff --git a/bin/ed/test/j.r b/bin/ed/test/j.r
new file mode 100644 (file)
index 0000000..66f36a8
--- /dev/null
@@ -0,0 +1,4 @@
+line 1
+line 2line 3
+line 4
+line5
diff --git a/bin/ed/test/j.t b/bin/ed/test/j.t
new file mode 100644 (file)
index 0000000..9b5d28d
--- /dev/null
@@ -0,0 +1,2 @@
+1,1j
+2,3j
diff --git a/bin/ed/test/k.r b/bin/ed/test/k.r
new file mode 100644 (file)
index 0000000..eeb38db
--- /dev/null
@@ -0,0 +1,5 @@
+line 3
+hello world
+line 4
+line5
+line 2
diff --git a/bin/ed/test/k.t b/bin/ed/test/k.t
new file mode 100644 (file)
index 0000000..53d588d
--- /dev/null
@@ -0,0 +1,10 @@
+2ka
+1d
+'am$
+1ka
+0a
+hello world
+.
+'ad
+u
+'am0
diff --git a/bin/ed/test/k1.err b/bin/ed/test/k1.err
new file mode 100644 (file)
index 0000000..eba1f3d
--- /dev/null
@@ -0,0 +1 @@
+1,$ka
diff --git a/bin/ed/test/k2.err b/bin/ed/test/k2.err
new file mode 100644 (file)
index 0000000..b34a18d
--- /dev/null
@@ -0,0 +1 @@
+kA
diff --git a/bin/ed/test/k3.err b/bin/ed/test/k3.err
new file mode 100644 (file)
index 0000000..70190c4
--- /dev/null
@@ -0,0 +1 @@
+0ka
diff --git a/bin/ed/test/k4.err b/bin/ed/test/k4.err
new file mode 100644 (file)
index 0000000..3457642
--- /dev/null
@@ -0,0 +1,6 @@
+a
+hello
+.
+.ka
+'ad
+'ap
diff --git a/bin/ed/test/l.r b/bin/ed/test/l.r
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/bin/ed/test/l.t b/bin/ed/test/l.t
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/bin/ed/test/m.err b/bin/ed/test/m.err
new file mode 100644 (file)
index 0000000..3aec4c3
--- /dev/null
@@ -0,0 +1,4 @@
+a
+hello world
+.
+1,$m1
diff --git a/bin/ed/test/m.r b/bin/ed/test/m.r
new file mode 100644 (file)
index 0000000..186cf54
--- /dev/null
@@ -0,0 +1,5 @@
+line5
+line 1
+line 2
+line 3
+line 4
diff --git a/bin/ed/test/m.t b/bin/ed/test/m.t
new file mode 100644 (file)
index 0000000..c39c088
--- /dev/null
@@ -0,0 +1,7 @@
+1,2m$
+1,2m$
+1,2m$
+$m0
+$m0
+2,3m1
+2,3m3
diff --git a/bin/ed/test/mkscripts.sh b/bin/ed/test/mkscripts.sh
new file mode 100755 (executable)
index 0000000..5e1a095
--- /dev/null
@@ -0,0 +1,75 @@
+#!/bin/sh -
+#      $NetBSD: mkscripts.sh,v 1.10 1995/04/23 10:07:36 cgd Exp $
+#
+# This script generates ed test scripts (.ed) from .t files
+
+PATH="/bin:/usr/bin:/usr/local/bin/:."
+ED=$1
+[ ! -x $ED ] && { echo "$ED: cannot execute"; exit 1; }
+
+for i in *.t; do
+#      base=${i%.*}
+#      base=`echo $i | sed 's/\..*//'`
+#      base=`expr $i : '\([^.]*\)'`
+#      (
+#      echo "#!/bin/sh -"
+#      echo "$ED - <<\EOT"
+#      echo "r $base.d"
+#      cat $i
+#      echo "w $base.o"
+#      echo EOT
+#      ) >$base.ed
+#      chmod +x $base.ed
+# The following is pretty ugly way of doing the above, and not appropriate 
+# use of ed  but the point is that it can be done...
+       base=`$ED - \!"echo $i" <<-EOF
+               s/\..*
+       EOF`
+       $ED - <<-EOF
+               a
+               #!/bin/sh -
+               $ED - <<\EOT
+               H
+               r $base.d
+               w $base.o
+               EOT
+               .
+               -2r $i
+               w $base.ed
+               !chmod +x $base.ed
+       EOF
+done
+
+for i in *.err; do
+#      base=${i%.*}
+#      base=`echo $i | sed 's/\..*//'`
+#      base=`expr $i : '\([^.]*\)'`
+#      (
+#      echo "#!/bin/sh -"
+#      echo "$ED - <<\EOT"
+#      echo H
+#      echo "r $base.err"
+#      cat $i
+#      echo "w $base.o"
+#      echo EOT
+#      ) >$base-err.ed
+#      chmod +x $base-err.ed
+# The following is pretty ugly way of doing the above, and not appropriate 
+# use of ed  but the point is that it can be done...
+       base=`$ED - \!"echo $i" <<-EOF
+               s/\..*
+       EOF`
+       $ED - <<-EOF
+               a
+               #!/bin/sh -
+               $ED - <<\EOT
+               H
+               r $base.err
+               w $base.o
+               EOT
+               .
+               -2r $i
+               w ${base}.red
+               !chmod +x ${base}.red
+       EOF
+done
diff --git a/bin/ed/test/n.r b/bin/ed/test/n.r
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/bin/ed/test/n.t b/bin/ed/test/n.t
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/bin/ed/test/nl.err b/bin/ed/test/nl.err
new file mode 100644 (file)
index 0000000..8949a85
--- /dev/null
@@ -0,0 +1 @@
+,1
diff --git a/bin/ed/test/nl1.r b/bin/ed/test/nl1.r
new file mode 100644 (file)
index 0000000..9d8854c
--- /dev/null
@@ -0,0 +1,8 @@
+
+
+hello world
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/bin/ed/test/nl1.t b/bin/ed/test/nl1.t
new file mode 100644 (file)
index 0000000..ea192e9
--- /dev/null
@@ -0,0 +1,8 @@
+1
+
+
+0a
+
+
+hello world
+.
diff --git a/bin/ed/test/nl2.r b/bin/ed/test/nl2.r
new file mode 100644 (file)
index 0000000..fe99e41
--- /dev/null
@@ -0,0 +1,6 @@
+line 1
+line 2
+line 3
+line 4
+line5
+hello world
diff --git a/bin/ed/test/nl2.t b/bin/ed/test/nl2.t
new file mode 100644 (file)
index 0000000..73fd27b
--- /dev/null
@@ -0,0 +1,4 @@
+a
+hello world
+.
+0;/./
diff --git a/bin/ed/test/p.r b/bin/ed/test/p.r
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/bin/ed/test/p.t b/bin/ed/test/p.t
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/bin/ed/test/q.r b/bin/ed/test/q.r
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/bin/ed/test/q.t b/bin/ed/test/q.t
new file mode 100644 (file)
index 0000000..123a2c8
--- /dev/null
@@ -0,0 +1,5 @@
+w q.o
+a
+hello
+.
+q
diff --git a/bin/ed/test/q1.err b/bin/ed/test/q1.err
new file mode 100644 (file)
index 0000000..0a7e178
--- /dev/null
@@ -0,0 +1 @@
+.q
diff --git a/bin/ed/test/r1.err b/bin/ed/test/r1.err
new file mode 100644 (file)
index 0000000..269aa7c
--- /dev/null
@@ -0,0 +1 @@
+1,$r r1.err
diff --git a/bin/ed/test/r1.r b/bin/ed/test/r1.r
new file mode 100644 (file)
index 0000000..a3ff506
--- /dev/null
@@ -0,0 +1,7 @@
+line 1
+hello world
+line 2
+line 3
+line 4
+line5
+hello world
diff --git a/bin/ed/test/r1.t b/bin/ed/test/r1.t
new file mode 100644 (file)
index 0000000..d787a92
--- /dev/null
@@ -0,0 +1,3 @@
+1;r !echo hello world
+1
+r !echo hello world
diff --git a/bin/ed/test/r2.err b/bin/ed/test/r2.err
new file mode 100644 (file)
index 0000000..1c44fa3
--- /dev/null
@@ -0,0 +1 @@
+r a-good-book
diff --git a/bin/ed/test/r2.r b/bin/ed/test/r2.r
new file mode 100644 (file)
index 0000000..ac152ba
--- /dev/null
@@ -0,0 +1,10 @@
+line 1
+line 2
+line 3
+line 4
+line5
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/bin/ed/test/r2.t b/bin/ed/test/r2.t
new file mode 100644 (file)
index 0000000..4286f42
--- /dev/null
@@ -0,0 +1 @@
+r
diff --git a/bin/ed/test/r3.r b/bin/ed/test/r3.r
new file mode 100644 (file)
index 0000000..86d5f90
--- /dev/null
@@ -0,0 +1,2 @@
+r r3.t
+r r3.t
diff --git a/bin/ed/test/r3.t b/bin/ed/test/r3.t
new file mode 100644 (file)
index 0000000..593eec6
--- /dev/null
@@ -0,0 +1 @@
+r r3.t
diff --git a/bin/ed/test/s1.err b/bin/ed/test/s1.err
new file mode 100644 (file)
index 0000000..d7ca0cf
--- /dev/null
@@ -0,0 +1 @@
+s . x
diff --git a/bin/ed/test/s1.r b/bin/ed/test/s1.r
new file mode 100644 (file)
index 0000000..4eb0980
--- /dev/null
@@ -0,0 +1,5 @@
+liene 1
+(liene) (2)
+(liene) (3)
+liene (4)
+(()liene5)
diff --git a/bin/ed/test/s1.t b/bin/ed/test/s1.t
new file mode 100644 (file)
index 0000000..b0028bb
--- /dev/null
@@ -0,0 +1,6 @@
+s/\([^ ][^ ]*\)/(\1)/g
+2s
+/3/s
+/\(4\)/sr
+/\(.\)/srg
+%s/i/&e/
diff --git a/bin/ed/test/s10.err b/bin/ed/test/s10.err
new file mode 100644 (file)
index 0000000..0d8d83d
--- /dev/null
@@ -0,0 +1,4 @@
+a
+hello
+.
+s/[h[.]/x/
diff --git a/bin/ed/test/s2.err b/bin/ed/test/s2.err
new file mode 100644 (file)
index 0000000..b5c851d
--- /dev/null
@@ -0,0 +1,4 @@
+a
+a
+.
+s/x*/a/g
diff --git a/bin/ed/test/s2.r b/bin/ed/test/s2.r
new file mode 100644 (file)
index 0000000..ca305c8
--- /dev/null
@@ -0,0 +1,5 @@
+li(n)e 1
+i(n)e 200
+li(n)e 3
+li(n)e 4
+li(n)e500
diff --git a/bin/ed/test/s2.t b/bin/ed/test/s2.t
new file mode 100644 (file)
index 0000000..f365849
--- /dev/null
@@ -0,0 +1,4 @@
+,s/./(&)/3
+s/$/00
+2s//%/g
+s/^l
diff --git a/bin/ed/test/s3.err b/bin/ed/test/s3.err
new file mode 100644 (file)
index 0000000..d68c7d0
--- /dev/null
@@ -0,0 +1 @@
+s/[xyx/a/
diff --git a/bin/ed/test/s3.r b/bin/ed/test/s3.r
new file mode 100644 (file)
index 0000000..d6cada2
--- /dev/null
@@ -0,0 +1 @@
+hello   world
diff --git a/bin/ed/test/s3.t b/bin/ed/test/s3.t
new file mode 100644 (file)
index 0000000..fbf8803
--- /dev/null
@@ -0,0 +1,6 @@
+a
+hello/[]world
+.
+s/[/]/ /
+s/[[:digit:][]/ /
+s/[]]/ /
diff --git a/bin/ed/test/s4.err b/bin/ed/test/s4.err
new file mode 100644 (file)
index 0000000..35b609f
--- /dev/null
@@ -0,0 +1 @@
+s/\a\b\c/xyz/
diff --git a/bin/ed/test/s5.err b/bin/ed/test/s5.err
new file mode 100644 (file)
index 0000000..89104c5
--- /dev/null
@@ -0,0 +1 @@
+s//xyz/
diff --git a/bin/ed/test/s6.err b/bin/ed/test/s6.err
new file mode 100644 (file)
index 0000000..b478595
--- /dev/null
@@ -0,0 +1 @@
+s
diff --git a/bin/ed/test/s7.err b/bin/ed/test/s7.err
new file mode 100644 (file)
index 0000000..30ba4fd
--- /dev/null
@@ -0,0 +1,5 @@
+a
+hello world
+.
+/./
+sr
diff --git a/bin/ed/test/s8.err b/bin/ed/test/s8.err
new file mode 100644 (file)
index 0000000..5665767
--- /dev/null
@@ -0,0 +1,4 @@
+a
+hello
+.
+s/[h[=]/x/
diff --git a/bin/ed/test/s9.err b/bin/ed/test/s9.err
new file mode 100644 (file)
index 0000000..1ff16dd
--- /dev/null
@@ -0,0 +1,4 @@
+a
+hello
+.
+s/[h[:]/x/
diff --git a/bin/ed/test/t.r b/bin/ed/test/t.r
new file mode 100644 (file)
index 0000000..2b28547
--- /dev/null
@@ -0,0 +1,16 @@
+line 1
+line 1
+line 1
+line 2
+line 2
+line 3
+line 4
+line5
+line 1
+line 1
+line 1
+line 2
+line 2
+line 3
+line 4
+line5
diff --git a/bin/ed/test/t1.err b/bin/ed/test/t1.err
new file mode 100644 (file)
index 0000000..c49c556
--- /dev/null
@@ -0,0 +1 @@
+tt
diff --git a/bin/ed/test/t1.r b/bin/ed/test/t1.r
new file mode 100644 (file)
index 0000000..2b28547
--- /dev/null
@@ -0,0 +1,16 @@
+line 1
+line 1
+line 1
+line 2
+line 2
+line 3
+line 4
+line5
+line 1
+line 1
+line 1
+line 2
+line 2
+line 3
+line 4
+line5
diff --git a/bin/ed/test/t1.t b/bin/ed/test/t1.t
new file mode 100644 (file)
index 0000000..6b66163
--- /dev/null
@@ -0,0 +1,3 @@
+1t0
+2,3t2
+,t$
diff --git a/bin/ed/test/t2.err b/bin/ed/test/t2.err
new file mode 100644 (file)
index 0000000..c202051
--- /dev/null
@@ -0,0 +1 @@
+t0;-1
diff --git a/bin/ed/test/t2.r b/bin/ed/test/t2.r
new file mode 100644 (file)
index 0000000..0c75ff5
--- /dev/null
@@ -0,0 +1,6 @@
+line 1
+line5
+line 2
+line 3
+line 4
+line5
diff --git a/bin/ed/test/t2.t b/bin/ed/test/t2.t
new file mode 100644 (file)
index 0000000..5175abd
--- /dev/null
@@ -0,0 +1 @@
+t0;/./
diff --git a/bin/ed/test/u.err b/bin/ed/test/u.err
new file mode 100644 (file)
index 0000000..caa1ba1
--- /dev/null
@@ -0,0 +1 @@
+.u
diff --git a/bin/ed/test/u.r b/bin/ed/test/u.r
new file mode 100644 (file)
index 0000000..ad558d8
--- /dev/null
@@ -0,0 +1,9 @@
+line 1
+hello
+hello world!!
+line 2
+line 3
+line 4
+line5
+hello
+hello world!!
diff --git a/bin/ed/test/u.t b/bin/ed/test/u.t
new file mode 100644 (file)
index 0000000..131cb6e
--- /dev/null
@@ -0,0 +1,31 @@
+1;r u.t
+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/bin/ed/test/v.r b/bin/ed/test/v.r
new file mode 100644 (file)
index 0000000..714db63
--- /dev/null
@@ -0,0 +1,11 @@
+line5
+order
+hello world
+line 1
+order
+line 2
+order
+line 3
+order
+line 4
+order
diff --git a/bin/ed/test/v.t b/bin/ed/test/v.t
new file mode 100644 (file)
index 0000000..608a77f
--- /dev/null
@@ -0,0 +1,6 @@
+v/[ ]/m0
+v/[ ]/s/$/\
+hello world
+v/hello /s/lo/p!/\
+a\
+order
diff --git a/bin/ed/test/w.r b/bin/ed/test/w.r
new file mode 100644 (file)
index 0000000..ac152ba
--- /dev/null
@@ -0,0 +1,10 @@
+line 1
+line 2
+line 3
+line 4
+line5
+line 1
+line 2
+line 3
+line 4
+line5
diff --git a/bin/ed/test/w.t b/bin/ed/test/w.t
new file mode 100644 (file)
index 0000000..c2e18bd
--- /dev/null
@@ -0,0 +1,2 @@
+w !cat >\!.z
+r \!.z
diff --git a/bin/ed/test/w1.err b/bin/ed/test/w1.err
new file mode 100644 (file)
index 0000000..e2c8a60
--- /dev/null
@@ -0,0 +1 @@
+w /to/some/far-away/place
diff --git a/bin/ed/test/w2.err b/bin/ed/test/w2.err
new file mode 100644 (file)
index 0000000..9daf89c
--- /dev/null
@@ -0,0 +1 @@
+ww.o
diff --git a/bin/ed/test/w3.err b/bin/ed/test/w3.err
new file mode 100644 (file)
index 0000000..39bbf4c
--- /dev/null
@@ -0,0 +1 @@
+wqp w.o
diff --git a/bin/ed/test/x.err b/bin/ed/test/x.err
new file mode 100644 (file)
index 0000000..0953f01
--- /dev/null
@@ -0,0 +1 @@
+.x
diff --git a/bin/ed/test/z.err b/bin/ed/test/z.err
new file mode 100644 (file)
index 0000000..6a51a2d
--- /dev/null
@@ -0,0 +1,2 @@
+z
+z
diff --git a/bin/ed/undo.c b/bin/ed/undo.c
new file mode 100644 (file)
index 0000000..daa2a42
--- /dev/null
@@ -0,0 +1,158 @@
+/*     $NetBSD: undo.c,v 1.5 2007/03/17 13:51:46 msaitoh Exp $ */
+
+/* undo.c: This file contains the undo routines for the ed line editor */
+/*-
+ * Copyright (c) 1993 Andrew Moore, Talke Studio.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char *rcsid = "@(#)undo.c,v 1.1 1994/02/01 00:34:44 alm Exp";
+#else
+__RCSID("$NetBSD: undo.c,v 1.5 2007/03/17 13:51:46 msaitoh Exp $");
+#endif
+#endif /* not lint */
+
+#include "ed.h"
+
+
+#define USIZE 100                              /* undo stack size */
+undo_t *ustack = NULL;                         /* undo stack */
+long usize = 0;                                        /* stack size variable */
+long u_p = 0;                                  /* undo stack pointer */
+
+/* push_undo_stack: return pointer to initialized undo node */
+undo_t *
+push_undo_stack(int type, long from, long to)
+{
+       undo_t *t;
+
+#if defined(sun) || defined(NO_REALLOC_NULL)
+       if (ustack == NULL &&
+           (ustack = (undo_t *) malloc((usize = USIZE) * sizeof(undo_t))) == NULL) {
+               fprintf(stderr, "%s\n", strerror(errno));
+               sprintf(errmsg, "out of memory");
+               return NULL;
+       }
+#endif
+       t = ustack;
+       if (u_p < usize ||
+           (t = (undo_t *) realloc(ustack, (usize += USIZE) * sizeof(undo_t))) != NULL) {
+               ustack = t;
+               ustack[u_p].type = type;
+               ustack[u_p].t = get_addressed_line_node(to);
+               ustack[u_p].h = get_addressed_line_node(from);
+               return ustack + u_p++;
+       }
+       /* out of memory - release undo stack */
+       fprintf(stderr, "%s\n", strerror(errno));
+       sprintf(errmsg, "out of memory");
+       clear_undo_stack();
+       free(ustack);
+       ustack = NULL;
+       usize = 0;
+       return NULL;
+}
+
+
+/* USWAP: swap undo nodes */
+#define USWAP(x,y) { \
+       undo_t utmp; \
+       utmp = x, x = y, y = utmp; \
+}
+
+
+long u_current_addr = -1;      /* if >= 0, undo enabled */
+long u_addr_last = -1;         /* if >= 0, undo enabled */
+
+/* pop_undo_stack: undo last change to the editor buffer */
+int
+pop_undo_stack(void)
+{
+       long n;
+       long o_current_addr = current_addr;
+       long o_addr_last = addr_last;
+
+       if (u_current_addr == -1 || u_addr_last == -1) {
+               sprintf(errmsg, "nothing to undo");
+               return ERR;
+       } else if (u_p)
+               modified = 1;
+       get_addressed_line_node(0);     /* this get_addressed_line_node last! */
+       SPL1();
+       for (n = u_p; n-- > 0;) {
+               switch(ustack[n].type) {
+               case UADD:
+                       REQUE(ustack[n].h->q_back, ustack[n].t->q_forw);
+                       break;
+               case UDEL:
+                       REQUE(ustack[n].h->q_back, ustack[n].h);
+                       REQUE(ustack[n].t, ustack[n].t->q_forw);
+                       break;
+               case UMOV:
+               case VMOV:
+                       REQUE(ustack[n - 1].h, ustack[n].h->q_forw);
+                       REQUE(ustack[n].t->q_back, ustack[n - 1].t);
+                       REQUE(ustack[n].h, ustack[n].t);
+                       n--;
+                       break;
+               default:
+                       /*NOTREACHED*/
+                       ;
+               }
+               ustack[n].type ^= 1;
+       }
+       /* reverse undo stack order */
+       for (n = u_p; n-- > (u_p + 1)/ 2;)
+               USWAP(ustack[n], ustack[u_p - 1 - n]);
+       if (isglobal)
+               clear_active_list();
+       current_addr = u_current_addr, u_current_addr = o_current_addr;
+       addr_last = u_addr_last, u_addr_last = o_addr_last;
+       SPL0();
+       return 0;
+}
+
+
+/* clear_undo_stack: clear the undo stack */
+void
+clear_undo_stack(void)
+{
+       line_t *lp, *ep, *tl;
+
+       while (u_p--)
+               if (ustack[u_p].type == UDEL) {
+                       ep = ustack[u_p].t->q_forw;
+                       for (lp = ustack[u_p].h; lp != ep; lp = tl) {
+                               unmark_line_node(lp);
+                               tl = lp->q_forw;
+                               free(lp);
+                       }
+               }
+       u_p = 0;
+       u_current_addr = current_addr;
+       u_addr_last = addr_last;
+}
index 69d5b5444a95f684f7df79eee87713c67f116873..b6e82fcd3f09df70cca03490025605ac23720be1 100644 (file)
@@ -10,7 +10,7 @@ SUBDIR=       add_route arp ash at awk \
        comm compress cp crc cron crontab cut \
        dd decomp16 DESCRIBE dev2name devsize df dhcpd \
        dhrystone diff dirname diskctl dumpcore \
-       ed eject elvis env expand factor fbdctl file \
+       eject elvis env expand factor fbdctl file \
        find finger fingerd fix fold format fortune fsck.mfs \
        ftp101 gcore gcov-pull getty grep head hexdump host \
        hostaddr id ifconfig ifdef install \
diff --git a/commands/ed/Makefile b/commands/ed/Makefile
deleted file mode 100644 (file)
index 383cd12..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-PROG=  ed
-MAN=
-
-.include <bsd.prog.mk>
diff --git a/commands/ed/ed.c b/commands/ed/ed.c
deleted file mode 100644 (file)
index cd58e61..0000000
+++ /dev/null
@@ -1,2198 +0,0 @@
-/* Copyright 1987 Brian Beattie Rights Reserved.
- *
- * Permission to copy and/or distribute granted under the
- * following conditions:
- *
- * 1). No charge may be made other than resonable charges
- *     for reproduction.
- *
- * 2). This notice must remain intact.
- *
- * 3). No further restrictions may be added.
- *
- */
-
-/*     This program used to be in many little pieces, with this makefile:
-.SUFFIXES:     .c .s
-
-CFLAGS = -F
-
-OBJS = append.s catsub.s ckglob.s deflt.s del.s docmd.s doglob.s\
-  doprnt.s doread.s dowrite.s ed.s egets.s find.s getfn.s getlst.s\
-  getnum.s getone.s getptr.s getrhs.s gettxt.s ins.s join.s maksub.s\
-  move.s optpat.s set.s setbuf.s subst.s getpat.s matchs.s amatch.s\
-  unmkpat.s omatch.s makepat.s bitmap.s dodash.s esc.s System.s
-
-ed:    $(OBJS)
-  cc -T. -i -o ed $(OBJS)
-*/
-
-#include <sys/types.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/wait.h>
-#include <stdio.h>
-
-/****************************/
-
-/*     tools.h */
-/*
- *     #defines for non-printing ASCII characters
- */
-
-#define NUL    0x00            /* ^@ */
-#define EOS    0x00            /* end of string */
-#define SOH    0x01            /* ^A */
-#define STX    0x02            /* ^B */
-#define ETX    0x03            /* ^C */
-#define EOT    0x04            /* ^D */
-#define ENQ    0x05            /* ^E */
-#define ACK    0x06            /* ^F */
-#define BEL    0x07            /* ^G */
-#define BS     0x08            /* ^H */
-#define HT     0x09            /* ^I */
-#define LF     0x0a            /* ^J */
-#define NL     '\n'
-#define VT     0x0b            /* ^K */
-#define FF     0x0c            /* ^L */
-#define CR     0x0d            /* ^M */
-#define SO     0x0e            /* ^N */
-#define SI     0x0f            /* ^O */
-#define DLE    0x10            /* ^P */
-#define DC1    0x11            /* ^Q */
-#define DC2    0x12            /* ^R */
-#define DC3    0x13            /* ^S */
-#define DC4    0x14            /* ^T */
-#define NAK    0x15            /* ^U */
-#define SYN    0x16            /* ^V */
-#define ETB    0x17            /* ^W */
-#define CAN    0x18            /* ^X */
-#define EM     0x19            /* ^Y */
-#define SUB    0x1a            /* ^Z */
-#define ESC    0x1b            /* ^[ */
-#define FS     0x1c            /* ^\ */
-#define GS     0x1d            /* ^] */
-#define RS     0x1e            /* ^^ */
-#define US     0x1f            /* ^_ */
-#define SP     0x20            /* space */
-#define DEL    0x7f            /* DEL */
-
-
-#define TRUE   1
-#define FALSE  0
-#define ERR    -2
-
-
-/*     Definitions of meta-characters used in pattern matching
- *     routines.  LITCHAR & NCCL are only used as token identifiers;
- *     all the others are also both token identifier and actual symbol
- *     used in the regular expression.
- */
-
-
-#define BOL    '^'
-#define EOL    '$'
-#define ANY    '.'
-#define LITCHAR        'L'
-#define        ESCAPE  '\\'
-#define CCL    '['             /* Character class: [...] */
-#define CCLEND ']'
-#define NEGATE '^'
-#define NCCL   '!'             /* Negative character class [^...] */
-#define CLOSURE        '*'
-#define OR_SYM '|'
-#define DITTO  '&'
-#define OPEN   '('
-#define CLOSE  ')'
-
-/* Largest permitted size for an expanded character class.  (i.e. the class
- * [a-z] will expand into 26 symbols; [a-z0-9] will expand into 36.)
- */
-#define CLS_SIZE       128
-
-/*
- *     Tokens are used to hold pattern templates. (see makepat())
- */
-typedef char BITMAP;
-
-typedef struct token {
-  char tok;
-  char lchar;
-  BITMAP *bitmap;
-  struct token *next;
-} TOKEN;
-
-#define TOKSIZE sizeof (TOKEN)
-
-/*
- *     An absolute maximun for strings.
- */
-
-#define MAXSTR 132             /* Maximum numbers of characters in a line */
-
-
-/* Macros */
-#define max(a,b)       ((a>b)?a:b)
-#define min(a,b)       ((a<b)?a:b)
-#define toupper(c)     (c>='a'&&c<='z'?c-32:c)
-
-/*     ed.h    */
-#define FATAL  (ERR-1)
-struct line {
-  int l_stat;                  /* empty, mark */
-  struct line *l_prev;
-  struct line *l_next;
-  char l_buff[1];
-};
-
-typedef struct line LINE;
-
-#define LINFREE        1               /* entry not in use */
-#define LGLOB  2               /* line marked global */
-
-                               /* max number of chars per line */
-#define MAXLINE        (sizeof(int) == 2 ? 256 : 8192)
-#define MAXPAT 256             /* max number of chars per replacement
-                                * pattern */
-                               /* max file name size */
-#define MAXFNAME (sizeof(int) == 2 ? 256 : 1024)
-
-extern LINE line0;
-extern int curln, lastln, line1, line2, nlines;
-extern int nflg;               /* print line number flag */
-extern int lflg;               /* print line in verbose mode */
-extern char *inptr;            /* tty input buffer */
-extern char linbuf[], *linptr; /* current line */
-extern int truncflg;           /* truncate long line flag */
-extern int eightbit;           /* save eighth bit */
-extern int nonascii;           /* count of non-ascii chars read */
-extern int nullchar;           /* count of null chars read */
-extern int truncated;          /* count of lines truncated */
-extern int fchanged;           /* file changed */
-
-#define nextln(l)      ((l)+1 > lastln ? 0 : (l)+1)
-#define prevln(l)      ((l)-1 < 0 ? lastln : (l)-1)
-
-/*     amatch.c        */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-
-_PROTOTYPE(int main, (int argc, char **argv));
-_PROTOTYPE(static char *match, (char *lin, TOKEN *pat, char *boln));
-_PROTOTYPE(char *amatch, (char *lin, TOKEN *pat, char *boln));
-_PROTOTYPE(int append, (int line, int glob));
-_PROTOTYPE(BITMAP *makebitmap, (unsigned size));
-_PROTOTYPE(int setbit, (unsigned c, char *map, unsigned val));
-_PROTOTYPE(int testbit, (unsigned c, char *map));
-_PROTOTYPE(char *catsub, (char *from, char *to, char *sub, char *new, char *newend));
-_PROTOTYPE(int ckglob, (void));
-_PROTOTYPE(int deflt, (int def1, int def2));
-_PROTOTYPE(int del, (int from, int to));
-_PROTOTYPE(int docmd, (int glob));
-_PROTOTYPE(int dolst, (int line1, int line2));
-_PROTOTYPE(char *dodash, (int delim, char *src, char *map));
-_PROTOTYPE(int doglob, (void));
-_PROTOTYPE(int doprnt, (int from, int to));
-_PROTOTYPE(void prntln, (char *str, int vflg, int lin));
-_PROTOTYPE(void putcntl, (int c, FILE *stream));
-_PROTOTYPE(int doread, (int lin, char *fname));
-_PROTOTYPE(int dowrite, (int from, int to, char *fname, int apflg));
-_PROTOTYPE(void intr, (int sig));
-_PROTOTYPE(int egets, (char *str, int size, FILE *stream));
-_PROTOTYPE(int esc, (char **s));
-_PROTOTYPE(int find, (TOKEN *pat, int dir));
-_PROTOTYPE(char *getfn, (void));
-_PROTOTYPE(int getlst, (void));
-_PROTOTYPE(int getnum, (int first));
-_PROTOTYPE(int getone, (void));
-_PROTOTYPE(TOKEN *getpat, (char *arg));
-_PROTOTYPE(LINE *getptr, (int num));
-_PROTOTYPE(int getrhs, (char *sub));
-_PROTOTYPE(char *gettxt, (int num));
-_PROTOTYPE(int ins, (char *str));
-_PROTOTYPE(int System, (char *c));
-_PROTOTYPE(int join, (int first, int last));
-_PROTOTYPE(TOKEN *makepat, (char *arg, int delim));
-_PROTOTYPE(char *maksub, (char *sub, int subsz));
-_PROTOTYPE(char *matchs, (char *line, TOKEN *pat, int ret_endp));
-_PROTOTYPE(int move, (int num));
-_PROTOTYPE(int transfer, (int num));
-_PROTOTYPE(int omatch, (char **linp, TOKEN *pat, char *boln));
-_PROTOTYPE(TOKEN *optpat, (void));
-_PROTOTYPE(int set, (void));
-_PROTOTYPE(int show, (void));
-_PROTOTYPE(void relink, (LINE *a, LINE *x, LINE *y, LINE *b));
-_PROTOTYPE(void clrbuf, (void));
-_PROTOTYPE(void set_buf, (void));
-_PROTOTYPE(int subst, (TOKEN *pat, char *sub, int gflg, int pflag));
-_PROTOTYPE(void unmakepat, (TOKEN *head));
-
-/*     Scans throught the pattern template looking for a match
- * with lin.  Each element of lin is compared with the template
- * until either a mis-match is found or the end of the template
- * is reached.  In the former case a 0 is returned; in the latter,
- * a pointer into lin (pointing to the character following the
- * matched pattern) is returned.
- *
- *     "lin"   is a pointer to the line being searched.
- *     "pat"   is a pointer to a template made by makepat().
- *     "boln"  is a pointer into "lin" which points at the
- *                     character at the beginning of the line.
- */
-
-char *paropen[9], *parclose[9];
-int between, parnum;
-
-char *amatch(lin, pat, boln)
-char *lin;
-TOKEN *pat;
-char *boln;
-{
-  between = 0;
-  parnum = 0;
-
-  lin = match(lin, pat, boln);
-
-  if (between) return 0;
-
-  while (parnum < 9) {
-       paropen[parnum] = parclose[parnum] = "";
-       parnum++;
-  }
-  return lin;
-}
-
-static char *match(lin, pat, boln)
-char *lin;
-TOKEN *pat;
-char *boln;
-{
-  register char *bocl, *rval, *strstart;
-
-  if (pat == 0) return 0;
-
-  strstart = lin;
-
-  while (pat) {
-       if (pat->tok == CLOSURE && pat->next) {
-               /* Process a closure: first skip over the closure
-                * token to the object to be repeated.  This object
-                * can be a character class. */
-
-               pat = pat->next;
-
-               /* Now match as many occurrences of the closure
-                * pattern as possible. */
-               bocl = lin;
-
-               while (*lin && omatch(&lin, pat, boln));
-
-               /* 'Lin' now points to the character that made made
-                * us fail.  Now go on to process the rest of the
-                * string.  A problem here is a character following
-                * the closure which could have been in the closure.
-                * For example, in the pattern "[a-z]*t" (which
-                * matches any lower-case word ending in a t), the
-                * final 't' will be sucked up in the while loop.
-                * So, if the match fails, we back up a notch and try
-                * to match the rest of the string again, repeating
-                * this process recursively until we get back to the
-                * beginning of the closure.  The recursion goes, at
-                * most two levels deep. */
-
-               if (pat = pat->next) {
-                       int savbtwn = between;
-                       int savprnm = parnum;
-
-                       while (bocl <= lin) {
-                               if (rval = match(lin, pat, boln)) {
-                                       /* Success */
-                                       return(rval);
-                               } else {
-                                       --lin;
-                                       between = savbtwn;
-                                       parnum = savprnm;
-                               }
-                       }
-                       return(0);      /* match failed */
-               }
-       } else if (pat->tok == OPEN) {
-               if (between || parnum >= 9) return 0;
-               paropen[parnum] = lin;
-               between = 1;
-               pat = pat->next;
-       } else if (pat->tok == CLOSE) {
-               if (!between) return 0;
-               parclose[parnum++] = lin;
-               between = 0;
-               pat = pat->next;
-       } else if (omatch(&lin, pat, boln)) {
-               pat = pat->next;
-       } else {
-               return(0);
-       }
-  }
-
-  /* Note that omatch() advances lin to point at the next character to
-   * be matched.  Consequently, when we reach the end of the template,
-   * lin will be pointing at the character following the last character
-   * matched.  The exceptions are templates containing only a BOLN or
-   * EOLN token.  In these cases omatch doesn't advance.
-   * 
-   * A philosophical point should be mentioned here.  Is $ a position or a
-   * character? (i.e. does $ mean the EOL character itself or does it
-   * mean the character at the end of the line.)  I decided here to
-   * make it mean the former, in order to make the behavior of match()
-   * consistent.  If you give match the pattern ^$ (match all lines
-   * consisting only of an end of line) then, since something has to be
-   * returned, a pointer to the end of line character itself is
-   * returned. */
-
-  return((char *) max(strstart, lin));
-}
-
-/*     append.c        */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-int append(line, glob)
-int line, glob;
-{
-  int stat;
-  char lin[MAXLINE];
-
-  if (glob) return(ERR);
-  curln = line;
-  while (1) {
-       if (nflg) printf("%6d. ", curln + 1);
-
-       if (fgets(lin, MAXLINE, stdin) == NULL) return(EOF);
-       if (lin[0] == '.' && lin[1] == '\n') return (0);
-       stat = ins(lin);
-       if (stat < 0) return(ERR);
-
-  }
-}
-
-/*     bitmap.c        */
-/*
- *     BITMAP.C -      makebitmap, setbit, testbit
- *                     bit-map manipulation routines.
- *
- *     Copyright (c) Allen I. Holub, all rights reserved.  This program may
- *             for copied for personal, non-profit use only.
- *
- */
-
-#ifdef DEBUG
-/* #include <stdio.h> */
-#endif
-
-/* #include "tools.h" */
-
-
-BITMAP *makebitmap(size)
-unsigned size;
-{
-  /* Make a bit map with "size" bits.  The first entry in the map is an
-   * "unsigned int" representing the maximum bit.  The map itself is
-   * concatenated to this integer. Return a pointer to a map on
-   * success, 0 if there's not enough memory. */
-
-  unsigned *map, numbytes;
-
-  numbytes = (size >> 3) + ((size & 0x07) ? 1 : 0);
-
-#ifdef DEBUG
-  printf("Making a %d bit map (%d bytes required)\n", size, numbytes);
-#endif
-
-  if (map = (unsigned *) malloc(numbytes + sizeof(unsigned))) {
-       *map = size;
-       memset(map + 1, 0, numbytes);
-  }
-
-  return((BITMAP *) map);
-}
-
-int setbit(c, map, val)
-unsigned c, val;
-char *map;
-{
-  /* Set bit c in the map to val. If c > map-size, 0 is returned, else
-   * 1 is returned. */
-
-  if (c >= *(unsigned *) map)  /* if c >= map size */
-       return 0;
-
-  map += sizeof(unsigned);     /* skip past size */
-
-  if (val)
-       map[c >> 3] |= 1 << (c & 0x07);
-  else
-       map[c >> 3] &= ~(1 << (c & 0x07));
-
-  return 1;
-}
-
-int testbit(c, map)
-unsigned c;
-char *map;
-{
-  /* Return 1 if the bit corresponding to c in map is set. 0 if it is not. */
-
-  if (c >= *(unsigned *) map) return 0;
-
-  map += sizeof(unsigned);
-
-  return(map[c >> 3] & (1 << (c & 0x07)));
-}
-
-/*     catsub.c        */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-extern char *paropen[9], *parclose[9];
-
-char *catsub(from, to, sub, new, newend)
-char *from, *to, *sub, *new, *newend;
-{
-  char *cp, *cp2;
-
-  for (cp = new; *sub != EOS && cp < newend;) {
-       if (*sub == DITTO) for (cp2 = from; cp2 < to;) {
-                       *cp++ = *cp2++;
-                       if (cp >= newend) break;
-               }
-       else if (*sub == ESCAPE) {
-               sub++;
-               if ('1' <= *sub && *sub <= '9') {
-                       char *parcl = parclose[*sub - '1'];
-
-                       for (cp2 = paropen[*sub - '1']; cp2 < parcl;) {
-                               *cp++ = *cp2++;
-                               if (cp >= newend) break;
-                       }
-               } else
-                       *cp++ = *sub;
-       } else
-               *cp++ = *sub;
-
-       sub++;
-  }
-
-  return(cp);
-}
-
-/*     ckglob.c        */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-int ckglob()
-{
-  TOKEN *glbpat;
-  char c, delim;
-  char lin[MAXLINE];
-  int num;
-  LINE *ptr;
-
-  c = *inptr;
-
-  if (c != 'g' && c != 'v') return(0);
-
-  if (deflt(1, lastln) < 0) return(ERR);
-
-  delim = *++inptr;
-  if (delim <= ' ') return(ERR);
-
-  glbpat = optpat();
-
-  if (*inptr == delim) inptr++;
-
-  ptr = getptr(1);
-  for (num = 1; num <= lastln; num++) {
-       ptr->l_stat &= ~LGLOB;
-       if (line1 <= num && num <= line2) {
-               strcpy(lin, ptr->l_buff);
-               strcat(lin, "\n");
-               if (matchs(lin, glbpat, 0)) {
-                       if (c == 'g') ptr->l_stat |= LGLOB;
-               } else {
-                       if (c == 'v') ptr->l_stat |= LGLOB;
-               }
-       }
-       ptr = ptr->l_next;
-  }
-  return(1);
-}
-
-/*     deflt.c */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-int deflt(def1, def2)
-int def1, def2;
-{
-  if (nlines == 0) {
-       line1 = def1;
-       line2 = def2;
-  }
-  if (line1 > line2 || line1 <= 0) return(ERR);
-  return(0);
-}
-
-/*     del.c   */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-int del(from, to)
-int from, to;
-{
-  LINE *first, *last, *next, *tmp;
-
-  if (from < 1) from = 1;
-  first = getptr(prevln(from));
-  last = getptr(nextln(to));
-  next = first->l_next;
-  while (next != last && next != &line0) {
-       tmp = next->l_next;
-       free((char *) next);
-       next = tmp;
-  }
-  relink(first, last, first, last);
-  lastln -= (to - from) + 1;
-  curln = prevln(from);
-  return(0);
-}
-
-/*     docmd.c */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-char fname[MAXFNAME];
-int fchanged;
-extern int nofname;
-
-extern int mark[];
-
-int docmd(glob)
-int glob;
-{
-  static char rhs[MAXPAT];
-  TOKEN *subpat;
-  int c, err, line3;
-  int apflg, pflag, gflag;
-  int nchng;
-  char *fptr;
-
-  pflag = FALSE;
-  while (*inptr == SP && *inptr == HT) inptr++;
-
-  c = *inptr++;
-
-  switch (c) {
-      case NL:
-       if (nlines == 0) {
-               if ((line2 = nextln(curln)) == 0) return(ERR);
-       }
-       curln = line2;
-       return(1);
-       break;
-
-      case '=':        printf("%d\n", line2);  break;
-
-      case 'a':
-       if (*inptr != NL || nlines > 1) return(ERR);
-
-       if (append(line1, glob) < 0) return(ERR);;
-       fchanged = TRUE;
-       break;
-
-      case 'c':
-       if (*inptr != NL) return(ERR);
-
-       if (deflt(curln, curln) < 0) return(ERR);
-
-       if (del(line1, line2) < 0) return(ERR);
-       if (append(curln, glob) < 0) return (ERR);
-       fchanged = TRUE;
-       break;
-
-      case 'd':
-       if (*inptr != NL) return(ERR);
-
-       if (deflt(curln, curln) < 0) return(ERR);
-
-       if (del(line1, line2) < 0) return(ERR);
-       if (nextln(curln) != 0) curln = nextln(curln);
-       fchanged = TRUE;
-       break;
-
-      case 'e':
-       if (nlines > 0) return(ERR);
-       if (fchanged) {
-               fchanged = FALSE;
-               return(ERR);
-       }
-
-       /* FALL THROUGH */
-      case 'E':
-       if (nlines > 0) return(ERR);
-
-       if (*inptr != ' ' && *inptr != HT && *inptr != NL) return(ERR);
-
-       if ((fptr = getfn()) == NULL) return(ERR);
-
-       clrbuf();
-       if ((err = doread(0, fptr)) < 0) return(err);
-
-       strcpy(fname, fptr);
-       fchanged = FALSE;
-       break;
-
-      case 'f':
-       if (nlines > 0) return(ERR);
-
-       if (*inptr != ' ' && *inptr != HT && *inptr != NL) return(ERR);
-
-       if ((fptr = getfn()) == NULL) return(ERR);
-
-       if (nofname)
-               printf("%s\n", fname);
-       else
-               strcpy(fname, fptr);
-       break;
-
-      case 'i':
-       if (*inptr != NL || nlines > 1) return(ERR);
-
-       if (append(prevln(line1), glob) < 0) return(ERR);
-       fchanged = TRUE;
-       break;
-
-      case 'j':
-       if (*inptr != NL || deflt(curln, curln + 1) < 0) return(ERR);
-
-       if (join(line1, line2) < 0) return(ERR);
-       break;
-
-      case 'k':
-       while (*inptr == ' ' || *inptr == HT) inptr++;
-
-       if (*inptr < 'a' || *inptr > 'z') return ERR;
-       c = *inptr++;
-
-       if (*inptr != ' ' && *inptr != HT && *inptr != NL) return(ERR);
-
-       mark[c - 'a'] = line1;
-       break;
-
-      case 'l':
-       if (*inptr != NL) return(ERR);
-       if (deflt(curln, curln) < 0) return (ERR);
-       if (dolst(line1, line2) < 0) return (ERR);
-       break;
-
-      case 'm':
-       if ((line3 = getone()) < 0) return(ERR);
-       if (deflt(curln, curln) < 0) return (ERR);
-       if (move(line3) < 0) return (ERR);
-       fchanged = TRUE;
-       break;
-
-      case 'P':
-      case 'p':
-       if (*inptr != NL) return(ERR);
-       if (deflt(curln, curln) < 0) return (ERR);
-       if (doprnt(line1, line2) < 0) return (ERR);
-       break;
-
-      case 'q':
-       if (fchanged) {
-               fchanged = FALSE;
-               return(ERR);
-       }
-
-       /* FALL THROUGH */
-      case 'Q':
-       if (*inptr == NL && nlines == 0 && !glob)
-               return(EOF);
-       else
-               return(ERR);
-
-      case 'r':
-       if (nlines > 1) return(ERR);
-
-       if (nlines == 0) line2 = lastln;
-
-       if (*inptr != ' ' && *inptr != HT && *inptr != NL) return(ERR);
-
-       if ((fptr = getfn()) == NULL) return(ERR);
-
-       if ((err = doread(line2, fptr)) < 0) return(err);
-       fchanged = TRUE;
-       break;
-
-      case 's':
-       if (*inptr == 'e') return(set());
-       while (*inptr == SP || *inptr == HT) inptr++;
-       if ((subpat = optpat()) == NULL) return (ERR);
-       if ((gflag = getrhs(rhs)) < 0) return (ERR);
-       if (*inptr == 'p') pflag++;
-       if (deflt(curln, curln) < 0) return (ERR);
-       if ((nchng = subst(subpat, rhs, gflag, pflag)) < 0) return (ERR);
-       if (nchng) fchanged = TRUE;
-       break;
-
-      case 't':
-       if ((line3 = getone()) < 0) return(ERR);
-       if (deflt(curln, curln) < 0) return (ERR);
-       if (transfer(line3) < 0) return (ERR);
-       fchanged = TRUE;
-       break;
-
-      case 'W':
-      case 'w':
-       apflg = (c == 'W');
-
-       if (*inptr != ' ' && *inptr != HT && *inptr != NL) return(ERR);
-
-       if ((fptr = getfn()) == NULL) return(ERR);
-
-       if (deflt(1, lastln) < 0) return(ERR);
-       if (dowrite(line1, line2, fptr, apflg) < 0) return (ERR);
-       fchanged = FALSE;
-       break;
-
-      case 'x':
-       if (*inptr == NL && nlines == 0 && !glob) {
-               if ((fptr = getfn()) == NULL) return(ERR);
-               if (dowrite(1, lastln, fptr, 0) >= 0) return (EOF);
-       }
-       return(ERR);
-
-      case 'z':
-       if (deflt(curln, curln) < 0) return(ERR);
-
-       switch (*inptr) {
-           case '-':
-               if (doprnt(line1 - 21, line1) < 0) return(ERR);
-               break;
-
-           case '.':
-               if (doprnt(line1 - 11, line1 + 10) < 0) return(ERR);
-               break;
-
-           case '+':
-           case '\n':
-               if (doprnt(line1, line1 + 21) < 0) return(ERR);
-               break;
-       }
-       break;
-
-      default: return(ERR);
-}
-  return(0);
-}
-
-int dolst(line1, line2)
-int line1, line2;
-{
-  int oldlflg = lflg, p;
-
-  lflg = 1;
-  p = doprnt(line1, line2);
-  lflg = oldlflg;
-
-  return p;
-}
-
-/*     dodash.c        */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-
-/*     Expand the set pointed to by *src into dest.
- *     Stop at delim.  Return 0 on error or size of
- *     character class on success.  Update *src to
- *     point at delim.  A set can have one element
- *     {x} or several elements ( {abcdefghijklmnopqrstuvwxyz}
- *     and {a-z} are equivalent ).  Note that the dash
- *     notation is expanded as sequential numbers.
- *     This means (since we are using the ASCII character
- *     set) that a-Z will contain the entire alphabet
- *     plus the symbols: [\]^_`.  The maximum number of
- *     characters in a character class is defined by maxccl.
- */
-char *dodash(delim, src, map)
-int delim;
-char *src, *map;
-{
-
-  register int first, last;
-  char *start;
-
-  start = src;
-
-  while (*src && *src != delim) {
-       if (*src != '-') setbit(esc(&src), map, 1);
-
-       else if (src == start || *(src + 1) == delim)
-               setbit('-', map, 1);
-       else {
-               src++;
-
-               if (*src < *(src - 2)) {
-                       first = *src;
-                       last = *(src - 2);
-               } else {
-                       first = *(src - 2);
-                       last = *src;
-               }
-
-               while (++first <= last) setbit(first, map, 1);
-
-       }
-       src++;
-  }
-  return(src);
-}
-
-/*     doglob.c        */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-int doglob()
-{
-  int lin, stat;
-  char *cmd;
-  LINE *ptr;
-
-  cmd = inptr;
-
-  while (1) {
-       ptr = getptr(1);
-       for (lin = 1; lin <= lastln; lin++) {
-               if (ptr->l_stat & LGLOB) break;
-               ptr = ptr->l_next;
-       }
-       if (lin > lastln) break;
-
-       ptr->l_stat &= ~LGLOB;
-       curln = lin;
-       inptr = cmd;
-       if ((stat = getlst()) < 0) return(stat);
-       if ((stat = docmd(1)) < 0) return (stat);
-  }
-  return(curln);
-}
-
-/*     doprnt.c        */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-int doprnt(from, to)
-int from, to;
-{
-  int i;
-  LINE *lptr;
-
-  from = from < 1 ? 1 : from;
-  to = to > lastln ? lastln : to;
-
-  if (to != 0) {
-       lptr = getptr(from);
-       for (i = from; i <= to; i++) {
-               prntln(lptr->l_buff, lflg, (nflg ? i : 0));
-               lptr = lptr->l_next;
-       }
-       curln = to;
-  }
-  return(0);
-}
-
-void prntln(str, vflg, lin)
-char *str;
-int vflg, lin;
-{
-  if (lin) printf("%7d ", lin);
-  while (*str && *str != NL) {
-       if (*str < ' ' || *str >= 0x7f) {
-               switch (*str) {
-                   case '\t':
-                       if (vflg)
-                               putcntl(*str, stdout);
-                       else
-                               putc(*str, stdout);
-                       break;
-
-                   case DEL:
-                       putc('^', stdout);
-                       putc('?', stdout);
-                       break;
-
-                   default:
-                       putcntl(*str, stdout);
-                       break;
-               }
-       } else
-               putc(*str, stdout);
-       str++;
-  }
-  if (vflg) putc('$', stdout);
-  putc('\n', stdout);
-}
-
-void putcntl(c, stream)
-char c;
-FILE *stream;
-{
-  putc('^', stream);
-  putc((c & 31) | '@', stream);
-}
-
-/*     doread.c        */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-extern int diag;
-
-int doread(lin, fname)
-int lin;
-char *fname;
-{
-  FILE *fp;
-  int err;
-  long bytes;
-  int lines;
-  static char str[MAXLINE];
-
-  err = 0;
-  nonascii = nullchar = truncated = 0;
-
-  if (diag) printf("\"%s\" ", fname);
-  if ((fp = fopen(fname, "r")) == NULL) {
-       printf("file open err\n");
-       return(ERR);
-  }
-  curln = lin;
-  for (lines = 0, bytes = 0; (err = egets(str, MAXLINE, fp)) > 0;) {
-       bytes += strlen(str);
-       if (ins(str) < 0) {
-               printf("file insert error\n");
-               err++;
-               break;
-       }
-       lines++;
-  }
-  fclose(fp);
-  if (err < 0) return(err);
-  if (diag) {
-       printf("%d lines %ld bytes", lines, bytes);
-       if (nonascii) printf(" [%d non-ascii]", nonascii);
-       if (nullchar) printf(" [%d nul]", nullchar);
-       if (truncated) printf(" [%d lines truncated]", truncated);
-       printf("\n");
-  }
-  return(err);
-}
-
-/*     dowrite.c       */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-int dowrite(from, to, fname, apflg)
-int from, to;
-char *fname;
-int apflg;
-{
-  FILE *fp;
-  int lin, err;
-  int lines;
-  long bytes;
-  char *str;
-  LINE *lptr;
-
-  err = 0;
-
-  lines = bytes = 0;
-  if (diag) printf("\"%s\" ", fname);
-  if ((fp = fopen(fname, (apflg ? "a" : "w"))) == NULL) {
-       printf("file open error\n");
-       return(ERR);
-  }
-  lptr = getptr(from);
-  for (lin = from; lin <= to; lin++) {
-       str = lptr->l_buff;
-       lines++;
-       bytes += strlen(str) + 1;
-       if (fputs(str, fp) == EOF) {
-               printf("file write error\n");
-               err++;
-               break;
-       }
-       fputc('\n', fp);
-       lptr = lptr->l_next;
-  }
-  if (diag) printf("%d lines %ld bytes\n", lines, bytes);
-  fclose(fp);
-  return(err);
-}
-
-/*     ed.c    */
-/* Copyright 1987 Brian Beattie Rights Reserved.
- *
- * Permission to copy and/or distribute granted under the
- * following conditions:
- *
- * 1). No charge may be made other than resonable charges
- *     for reproduction.
- *
- * 2). This notice must remain intact.
- *
- * 3). No further restrictions may be added.
- *
- */
-/* #include <stdio.h> */
-/* #include <signal.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-#include <setjmp.h>
-jmp_buf env;
-
-LINE line0;
-int curln = 0;
-int lastln = 0;
-char *inptr;
-static char inlin[MAXLINE];
-int nflg, lflg;
-int line1, line2, nlines;
-extern char fname[];
-int version = 1;
-int diag = 1;
-
-void intr(sig)
-int sig;
-{
-  printf("?\n");
-  longjmp(env, 1);
-}
-
-int main(argc, argv)
-int argc;
-char **argv;
-{
-  int stat, i, doflush;
-
-  set_buf();
-  doflush = isatty(1);
-
-  if (argc > 1 && (strcmp(argv[1], "-") == 0 || strcmp(argv[1], "-s") == 0)) {
-       diag = 0;
-       argc--;
-       argv++;
-  }
-  if (argc > 1) {
-       for (i = 1; i < argc; i++) {
-               if (doread(0, argv[i]) == 0) {
-                       curln = 1;
-                       strcpy(fname, argv[i]);
-                       break;
-               }
-       }
-  }
-  while (1) {
-       setjmp(env);
-       if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, intr);
-
-       if (doflush) fflush(stdout);
-
-       if (fgets(inlin, sizeof(inlin), stdin) == NULL) {
-               break;
-       }
-       for (;;) {
-               inptr = strchr(inlin, EOS);
-               if (inptr >= inlin+2 && inptr[-2] == '\\' && inptr[-1] == NL) {
-                       inptr[-1] = 'n';
-                       if (fgets(inptr, sizeof(inlin) - (inptr - inlin),
-                                               stdin) == NULL) break;
-               } else {
-                       break;
-               }
-       }
-       if (*inlin == '!') {
-               if ((inptr = strchr(inlin, NL)) != NULL) *inptr = EOS;
-               System(inlin + 1);
-               continue;
-       }
-       inptr = inlin;
-       if (getlst() >= 0)
-               if ((stat = ckglob()) != 0) {
-                       if (stat >= 0 && (stat = doglob()) >= 0) {
-                               curln = stat;
-                               continue;
-                       }
-               } else {
-                       if ((stat = docmd(0)) >= 0) {
-                               if (stat == 1) doprnt(curln, curln);
-                               continue;
-                       }
-               }
-       if (stat == EOF) {
-               exit(0);
-       }
-       if (stat == FATAL) {
-               fputs("FATAL ERROR\n", stderr);
-               exit(1);
-       }
-       printf("?\n");
-  }
-  return(0);
-}
-
-/*     egets.c */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-int eightbit = 1;              /* save eight bit */
-int nonascii, nullchar, truncated;
-int egets(str, size, stream)
-char *str;
-int size;
-FILE *stream;
-{
-  int c, count;
-  char *cp;
-
-  for (count = 0, cp = str; size > count;) {
-       c = getc(stream);
-       if (c == EOF) {
-               *cp++ = '\n';
-               *cp = EOS;
-               if (count) {
-                       printf("[Incomplete last line]\n");
-               }
-               return(count);
-       }
-       if (c == NL) {
-               *cp++ = c;
-               *cp = EOS;
-               return(++count);
-       }
-       if (c > 127) {
-               if (!eightbit)  /* if not saving eighth bit */
-                       c = c & 127;    /* strip eigth bit */
-               nonascii++;     /* count it */
-       }
-       if (c) {
-               *cp++ = c;      /* not null, keep it */
-               count++;
-       } else
-               nullchar++;     /* count nulls */
-  }
-  str[count - 1] = EOS;
-  if (c != NL) {
-       printf("truncating line\n");
-       truncated++;
-       while ((c = getc(stream)) != EOF)
-               if (c == NL) break;
-  }
-  return(count);
-}
-
-/*     esc.c   */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-
-/* Map escape sequences into their equivalent symbols.  Returns the
- * correct ASCII character.  If no escape prefix is present then s
- * is untouched and *s is returned, otherwise **s is advanced to point
- * at the escaped character and the translated character is returned.
- */
-int esc(s)
-char **s;
-{
-  register int rval;
-
-
-  if (**s != ESCAPE) {
-       rval = **s;
-  } else {
-       (*s)++;
-
-       switch (toupper(**s)) {
-           case '\000':        rval = ESCAPE;  break;
-           case 'S':   rval = ' ';     break;
-           case 'N':   rval = '\n';    break;
-           case 'T':   rval = '\t';    break;
-           case 'B':   rval = '\b';    break;
-           case 'R':   rval = '\r';    break;
-           default:    rval = **s;     break;
-       }
-  }
-
-  return(rval);
-}
-
-/*     find.c  */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-int find(pat, dir)
-TOKEN *pat;
-int dir;
-{
-  int i, num;
-  char lin[MAXLINE];
-  LINE *ptr;
-
-  num = curln;
-  ptr = getptr(curln);
-  num = (dir ? nextln(num) : prevln(num));
-  ptr = (dir ? ptr->l_next : ptr->l_prev);
-  for (i = 0; i < lastln; i++) {
-       if (num == 0) {
-               num = (dir ? nextln(num) : prevln(num));
-               ptr = (dir ? ptr->l_next : ptr->l_prev);
-       }
-       strcpy(lin, ptr->l_buff);
-       strcat(lin, "\n");
-       if (matchs(lin, pat, 0)) {
-               return(num);
-       }
-       num = (dir ? nextln(num) : prevln(num));
-       ptr = (dir ? ptr->l_next : ptr->l_prev);
-  }
-  return(ERR);
-}
-
-/*     getfn.c */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-extern char fname[MAXFNAME];
-int nofname;
-
-char *getfn()
-{
-  static char file[256];
-  char *cp;
-
-  if (*inptr == NL) {
-       nofname = TRUE;
-       strcpy(file, fname);
-  } else {
-       nofname = FALSE;
-       while (*inptr == SP || *inptr == HT) inptr++;
-
-       cp = file;
-       while (*inptr && *inptr != NL && *inptr != SP && *inptr != HT) {
-               *cp++ = *inptr++;
-       }
-       *cp = '\0';
-
-       if (strlen(file) == 0) {
-               printf("bad file name\n");
-               return(NULL);
-       }
-  }
-
-  if (strlen(file) == 0) {
-       printf("no file name\n");
-       return(NULL);
-  }
-  return(file);
-}
-
-/*     getlst.c        */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-int getlst()
-{
-  int num;
-
-  line2 = 0;
-  for (nlines = 0; (num = getone()) >= 0;) {
-       line1 = line2;
-       line2 = num;
-       nlines++;
-       if (*inptr != ',' && *inptr != ';') break;
-       if (*inptr == ';') curln = num;
-       inptr++;
-  }
-  nlines = min(nlines, 2);
-  if (nlines == 0) line2 = curln;
-  if (nlines <= 1) line1 = line2;
-
-  if (num == ERR)
-       return(num);
-  else
-       return(nlines);
-}
-
-/*     getnum.c        */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-int mark['z' - 'a' + 1];
-
-int getnum(first)
-int first;
-{
-  TOKEN *srchpat;
-  int num;
-  char c;
-
-  while (*inptr == SP || *inptr == HT) inptr++;
-
-  if (*inptr >= '0' && *inptr <= '9') {        /* line number */
-       for (num = 0; *inptr >= '0' && *inptr <= '9';) {
-               num = (num * 10) + *inptr - '0';
-               inptr++;
-       }
-       return num;
-  }
-  switch (c = *inptr) {
-      case '.':
-       inptr++;
-       return(curln);
-
-      case '$':
-       inptr++;
-       return(lastln);
-
-      case '/':
-      case '?':
-       srchpat = optpat();
-       if (*inptr == c) inptr++;
-       return(find(srchpat, c == '/' ? 1 : 0));
-
-      case '-':
-      case '+':
-       return(first ? curln : 1);
-
-      case '\'':
-       inptr++;
-       if (*inptr < 'a' || *inptr > 'z') return(EOF);
-
-       return mark[*inptr++ - 'a'];
-
-      default:
-       return(first ? EOF : 1);/* unknown address */
-  }
-}
-
-/*     getone.c        */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-#define FIRST 1
-#define NOTFIRST 0
-
-int getone()
-{
-  int c, i, num;
-
-  if ((num = getnum(FIRST)) >= 0) {
-       while (1) {
-               while (*inptr == SP || *inptr == HT) inptr++;
-
-               if (*inptr != '+' && *inptr != '-') break;
-               c = *inptr++;
-
-               if ((i = getnum(NOTFIRST)) < 0) return(i);
-
-               if (c == '+') {
-                       num += i;
-               } else {
-                       num -= i;
-               }
-       }
-  }
-  return(num > lastln ? ERR : num);
-}
-
-/*     getpat.c        */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-
-/* Translate arg into a TOKEN string */
-TOKEN *
- getpat(arg)
-char *arg;
-{
-
-  return(makepat(arg, '\000'));
-}
-
-/*     getptr.c        */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-LINE *
- getptr(num)
-int num;
-{
-  LINE *ptr;
-  int j;
-
-  if (2 * num > lastln && num <= lastln) {     /* high line numbers */
-       ptr = line0.l_prev;
-       for (j = lastln; j > num; j--) ptr = ptr->l_prev;
-  } else {                     /* low line numbers */
-       ptr = &line0;
-       for (j = 0; j < num; j++) ptr = ptr->l_next;
-  }
-  return(ptr);
-}
-
-/*     getrhs.c        */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-int getrhs(sub)
-char *sub;
-{
-  if (inptr[0] == NL || inptr[1] == NL)        /* check for eol */
-       return(ERR);
-
-  if (maksub(sub, MAXPAT) == NULL) return(ERR);
-
-  inptr++;                     /* skip over delimter */
-  while (*inptr == SP || *inptr == HT) inptr++;
-  if (*inptr == 'g') {
-       inptr++;
-       return(1);
-  }
-  return(0);
-}
-
-/*     gettxt.c        */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-char *
- gettxt(num)
-int num;
-{
-  LINE *lin;
-  static char txtbuf[MAXLINE];
-
-  lin = getptr(num);
-  strcpy(txtbuf, lin->l_buff);
-  strcat(txtbuf, "\n");
-  return(txtbuf);
-}
-
-/*     ins.c   */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-int ins(str)
-char *str;
-{
-  char buf[MAXLINE], *cp;
-  LINE *new, *cur, *nxt;
-
-  cp = buf;
-  while (1) {
-       if ((*cp = *str++) == NL) *cp = EOS;
-       if (*cp) {
-               cp++;
-               continue;
-       }
-       if ((new = (LINE *) malloc(sizeof(LINE) + strlen(buf))) == NULL)
-               return(ERR);    /* no memory */
-
-       new->l_stat = 0;
-       strcpy(new->l_buff, buf);       /* build new line */
-       cur = getptr(curln);    /* get current line */
-       nxt = cur->l_next;      /* get next line */
-       relink(cur, new, new, nxt);     /* add to linked list */
-       relink(new, nxt, cur, new);
-       lastln++;
-       curln++;
-
-       if (*str == EOS)        /* end of line ? */
-               return(1);
-
-       cp = buf;
-  }
-}
-
-/*     join.c  */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-extern int fchanged;
-
-int join(first, last)
-int first, last;
-{
-  char buf[MAXLINE];
-  char *cp = buf, *str;
-  int num;
-
-  if (first <= 0 || first > last || last > lastln) return(ERR);
-  if (first == last) {
-       curln = first;
-       return 0;
-  }
-  for (num = first; num <= last; num++) {
-       str = gettxt(num);
-
-       while (*str != NL && cp < buf + MAXLINE - 1) *cp++ = *str++;
-
-       if (cp == buf + MAXLINE - 1) {
-               printf("line too long\n");
-               return(ERR);
-       }
-  }
-  *cp++ = NL;
-  *cp = EOS;
-  del(first, last);
-  curln = first - 1;
-  ins(buf);
-  fchanged = TRUE;
-  return 0;
-}
-
-/*     makepat.c       */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-
-/* Make a pattern template from the strinng pointed to by arg.  Stop
- * when delim or '\000' or '\n' is found in arg.  Return a pointer to
- * the pattern template.
- *
- * The pattern template used here are somewhat different than those
- * used in the "Software Tools" book; each token is a structure of
- * the form TOKEN (see tools.h).  A token consists of an identifier,
- * a pointer to a string, a literal character and a pointer to another
- * token.  This last is 0 if there is no subsequent token.
- *
- * The one strangeness here is caused (again) by CLOSURE which has
- * to be put in front of the previous token.  To make this insertion a
- * little easier, the 'next' field of the last to point at the chain
- * (the one pointed to by 'tail) is made to point at the previous node.
- * When we are finished, tail->next is set to 0.
- */
-TOKEN *
- makepat(arg, delim)
-char *arg;
-int delim;
-{
-  TOKEN *head, *tail, *ntok;
-  int error;
-
-  /* Check for characters that aren't legal at the beginning of a template. */
-
-  if (*arg == '\0' || *arg == delim || *arg == '\n' || *arg == CLOSURE)
-       return(0);
-
-  error = 0;
-  tail = head = NULL;
-
-  while (*arg && *arg != delim && *arg != '\n' && !error) {
-       ntok = (TOKEN *) malloc(TOKSIZE);
-       ntok->lchar = '\000';
-       ntok->next = 0;
-
-       switch (*arg) {
-           case ANY:   ntok->tok = ANY;        break;
-
-           case BOL:
-               if (head == 0)  /* then this is the first symbol */
-                       ntok->tok = BOL;
-               else
-                       ntok->tok = LITCHAR;
-               ntok->lchar = BOL;
-               break;
-
-           case EOL:
-               if (*(arg + 1) == delim || *(arg + 1) == '\000' ||
-                   *(arg + 1) == '\n') {
-                       ntok->tok = EOL;
-               } else {
-                       ntok->tok = LITCHAR;
-                       ntok->lchar = EOL;
-               }
-               break;
-
-           case CLOSURE:
-               if (head != 0) {
-                       switch (tail->tok) {
-                           case BOL:
-                           case EOL:
-                           case CLOSURE:
-                               return(0);
-
-                           default:
-                               ntok->tok = CLOSURE;
-                       }
-               }
-               break;
-
-           case CCL:
-
-               if (*(arg + 1) == NEGATE) {
-                       ntok->tok = NCCL;
-                       arg += 2;
-               } else {
-                       ntok->tok = CCL;
-                       arg++;
-               }
-
-               if (ntok->bitmap = makebitmap(CLS_SIZE))
-                       arg = dodash(CCLEND, arg, ntok->bitmap);
-               else {
-                       fprintf(stderr, "Not enough memory for pat\n");
-                       error = 1;
-               }
-               break;
-
-           default:
-               if (*arg == ESCAPE && *(arg + 1) == OPEN) {
-                       ntok->tok = OPEN;
-                       arg++;
-               } else if (*arg == ESCAPE && *(arg + 1) == CLOSE) {
-                       ntok->tok = CLOSE;
-                       arg++;
-               } else {
-                       ntok->tok = LITCHAR;
-                       ntok->lchar = esc(&arg);
-               }
-       }
-
-       if (error || ntok == 0) {
-               unmakepat(head);
-               return(0);
-       } else if (head == 0) {
-               /* This is the first node in the chain. */
-
-               ntok->next = 0;
-               head = tail = ntok;
-       } else if (ntok->tok != CLOSURE) {
-               /* Insert at end of list (after tail) */
-
-               tail->next = ntok;
-               ntok->next = tail;
-               tail = ntok;
-       } else if (head != tail) {
-               /* More than one node in the chain.  Insert the
-                * CLOSURE node immediately in front of tail. */
-
-               (tail->next)->next = ntok;
-               ntok->next = tail;
-       } else {
-               /* Only one node in the chain,  Insert the CLOSURE
-                * node at the head of the linked list. */
-
-               ntok->next = head;
-               tail->next = ntok;
-               head = ntok;
-       }
-       arg++;
-  }
-
-  tail->next = 0;
-  return(head);
-}
-
-/*     maksub.c        */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-char *
- maksub(sub, subsz)
-char *sub;
-int subsz;
-{
-  int size;
-  char delim, *cp;
-
-  size = 0;
-  cp = sub;
-
-  delim = *inptr++;
-  for (size = 0; *inptr != delim && *inptr != NL && size < subsz; size++) {
-       if (*inptr == '&') {
-               *cp++ = DITTO;
-               inptr++;
-       } else if ((*cp++ = *inptr++) == ESCAPE) {
-               if (size >= subsz) return(NULL);
-
-               switch (toupper(*inptr)) {
-                   case NL:    *cp++ = ESCAPE;         break;
-                       break;
-                   case 'S':
-                       *cp++ = SP;
-                       inptr++;
-                       break;
-                   case 'N':
-                       *cp++ = NL;
-                       inptr++;
-                       break;
-                   case 'T':
-                       *cp++ = HT;
-                       inptr++;
-                       break;
-                   case 'B':
-                       *cp++ = BS;
-                       inptr++;
-                       break;
-                   case 'R':
-                       *cp++ = CR;
-                       inptr++;
-                       break;
-                   case '0':{
-                               int i = 3;
-                               *cp = 0;
-                               do {
-                                       if (*++inptr < '0' || *inptr > '7')
-                                               break;
-
-                                       *cp = (*cp << 3) | (*inptr - '0');
-                               } while (--i != 0);
-                               cp++;
-                       } break;
-                   default:    *cp++ = *inptr++;       break;
-               }
-       }
-  }
-  if (size >= subsz) return(NULL);
-
-  *cp = EOS;
-  return(sub);
-}
-
-/*     matchs.c        */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-
-/* Compares line and pattern.  Line is a character string while pat
- * is a pattern template made by getpat().
- * Returns:
- *     1. A zero if no match was found.
- *
- *     2. A pointer to the last character satisfing the match
- *        if ret_endp is non-zero.
- *
- *     3. A pointer to the beginning of the matched string if
- *        ret_endp is zero.
- *
- * e.g.:
- *
- *     matchs ("1234567890", getpat("4[0-9]*7), 0);
- * will return a pointer to the '4', while:
- *
- *     matchs ("1234567890", getpat("4[0-9]*7), 1);
- * will return a pointer to the '7'.
- */
-char *
- matchs(line, pat, ret_endp)
-char *line;
-TOKEN *pat;
-int ret_endp;
-{
-
-  char *rval, *bptr;
-  char *line2;
-  TOKEN *pat2;
-  char c;
-  short ok;
-
-  bptr = line;
-
-  while (*line) {
-
-       if (pat && pat->tok == LITCHAR) {
-               while (*line) {
-                       pat2 = pat;
-                       line2 = line;
-                       if (*line2 != pat2->lchar) {
-                               c = pat2->lchar;
-                               while (*line2 && *line2 != c) ++line2;
-                               line = line2;
-                               if (*line2 == '\0') break;
-                       }
-                       ok = 1;
-                       ++line2;
-                       pat2 = pat2->next;
-                       while (pat2 && pat2->tok == LITCHAR) {
-                               if (*line2 != pat2->lchar) {
-                                       ok = 0;
-                                       break;
-                               }
-                               ++line2;
-                               pat2 = pat2->next;
-                       }
-                       if (!pat2) {
-                               if (ret_endp)
-                                       return(--line2);
-                               else
-                                       return(line);
-                       } else if (ok)
-                               break;
-                       ++line;
-               }
-               if (*line == '\0') return(0);
-       } else {
-               line2 = line;
-               pat2 = pat;
-       }
-       if ((rval = amatch(line2, pat2, bptr)) == 0) {
-               if (pat && pat->tok == BOL) break;
-               line++;
-       } else {
-               if (rval > bptr && rval > line)
-                       rval--; /* point to last char matched */
-               rval = ret_endp ? rval : line;
-               break;
-       }
-  }
-  return(rval);
-}
-
-/*     move.c  */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-int move(num)
-int num;
-{
-  LINE *k0, *k1, *k2, *k3;
-
-  if (line1 <= 0 || line2 < line1 || (line1 <= num && num <= line2))
-       return(ERR);
-  k0 = getptr(prevln(line1));
-  k1 = getptr(line1);
-  k2 = getptr(line2);
-  k3 = getptr(nextln(line2));
-
-  relink(k0, k3, k0, k3);
-  lastln -= line2 - line1 + 1;
-
-  if (num > line1) num -= line2 - line1 + 1;
-
-  curln = num + (line2 - line1 + 1);
-
-  k0 = getptr(num);
-  k3 = getptr(nextln(num));
-
-  relink(k0, k1, k2, k3);
-  relink(k2, k3, k0, k1);
-  lastln += line2 - line1 + 1;
-
-  return(1);
-}
-
-int transfer(num)
-int num;
-{
-  int mid, lin, ntrans;
-
-  if (line1 <= 0 || line1 > line2) return(ERR);
-
-  mid = num < line2 ? num : line2;
-
-  curln = num;
-  ntrans = 0;
-
-  for (lin = line1; lin <= mid; lin++) {
-       ins(gettxt(lin));
-       ntrans++;
-  }
-  lin += ntrans;
-  line2 += ntrans;
-
-  for (; lin <= line2; lin += 2) {
-       ins(gettxt(lin));
-       line2++;
-  }
-  return(1);
-}
-
-/*     omatch.c        */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-
-/* Match one pattern element, pointed at by pat, with the character at
- * **linp.  Return non-zero on match.  Otherwise, return 0.  *Linp is
- * advanced to skip over the matched character; it is not advanced on
- * failure.  The amount of advance is 0 for patterns that match null
- * strings, 1 otherwise.  "boln" should point at the position that will
- * match a BOL token.
- */
-int omatch(linp, pat, boln)
-char **linp;
-TOKEN *pat;
-char *boln;
-{
-
-  register int advance;
-
-  advance = -1;
-
-  if (**linp) {
-       switch (pat->tok) {
-           case LITCHAR:
-               if (**linp == pat->lchar) advance = 1;
-               break;
-
-           case BOL:
-               if (*linp == boln) advance = 0;
-               break;
-
-           case ANY:
-               if (**linp != '\n') advance = 1;
-               break;
-
-           case EOL:
-               if (**linp == '\n') advance = 0;
-               break;
-
-           case CCL:
-               if (testbit(**linp, pat->bitmap)) advance = 1;
-               break;
-
-           case NCCL:
-               if (!testbit(**linp, pat->bitmap)) advance = 1;
-               break;
-       }
-  }
-  if (advance >= 0) *linp += advance;
-
-  return(++advance);
-}
-
-/*     optpat.c        */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-TOKEN *oldpat;
-
-TOKEN *
- optpat()
-{
-  char delim, str[MAXPAT], *cp;
-
-  delim = *inptr++;
-  cp = str;
-  while (*inptr != delim && *inptr != NL) {
-       if (*inptr == ESCAPE && inptr[1] != NL) *cp++ = *inptr++;
-       *cp++ = *inptr++;
-  }
-
-  *cp = EOS;
-  if (*str == EOS) return(oldpat);
-  if (oldpat) unmakepat(oldpat);
-  oldpat = getpat(str);
-  return(oldpat);
-}
-
-/*     set.c   */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-struct tbl {
-  char *t_str;
-  int *t_ptr;
-  int t_val;
-} *t, tbl[] = {
-
-  "number", &nflg, TRUE,
-  "nonumber", &nflg, FALSE,
-  "list", &lflg, TRUE,
-  "nolist", &lflg, FALSE,
-  "eightbit", &eightbit, TRUE,
-  "noeightbit", &eightbit, FALSE,
-  0
-};
-
-int set()
-{
-  char word[16];
-  int i;
-
-  inptr++;
-  if (*inptr != 't') {
-       if (*inptr != SP && *inptr != HT && *inptr != NL) return(ERR);
-  } else
-       inptr++;
-
-  if (*inptr == NL) return(show());
-  /* Skip white space */
-  while (*inptr == SP || *inptr == HT) inptr++;
-
-  for (i = 0; *inptr != SP && *inptr != HT && *inptr != NL;)
-       word[i++] = *inptr++;
-  word[i] = EOS;
-  for (t = tbl; t->t_str; t++) {
-       if (strcmp(word, t->t_str) == 0) {
-               *t->t_ptr = t->t_val;
-               return(0);
-       }
-  }
-  return(0);
-}
-
-int show()
-{
-  extern int version;
-
-  printf("ed version %d.%d\n", version / 100, version % 100);
-  printf("number %s, list %s\n", nflg ? "ON" : "OFF", lflg ? "ON" : "OFF");
-  return(0);
-}
-
-/*     setbuf.c        */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-void relink(a, x, y, b)
-LINE *a, *x, *y, *b;
-{
-  x->l_prev = a;
-  y->l_next = b;
-}
-
-void clrbuf()
-{
-  del(1, lastln);
-}
-
-void set_buf()
-{
-  relink(&line0, &line0, &line0, &line0);
-  curln = lastln = 0;
-}
-
-/*     subst.c */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-/* #include "ed.h" */
-
-int subst(pat, sub, gflg, pflag)
-TOKEN *pat;
-char *sub;
-int gflg, pflag;
-{
-  int lin, chngd, nchngd;
-  char *txtptr, *txt;
-  char *lastm, *m, *new, buf[MAXLINE];
-
-  if (line1 <= 0) return(ERR);
-  nchngd = 0;                  /* reset count of lines changed */
-  for (lin = line1; lin <= line2; lin++) {
-       txt = txtptr = gettxt(lin);
-       new = buf;
-       chngd = 0;
-       lastm = NULL;
-       while (*txtptr) {
-               if (gflg || !chngd)
-                       m = amatch(txtptr, pat, txt);
-               else
-                       m = NULL;
-               if (m != NULL && lastm != m) {
-                       chngd++;
-                       new = catsub(txtptr, m, sub, new,
-                                    buf + MAXLINE);
-                       lastm = m;
-               }
-               if (m == NULL || m == txtptr) {
-                       *new++ = *txtptr++;
-               } else {
-                       txtptr = m;
-               }
-       }
-       if (chngd) {
-               if (new >= buf + MAXLINE) return(ERR);
-               *new++ = EOS;
-               del(lin, lin);
-               ins(buf);
-               nchngd++;
-               if (pflag) doprnt(curln, curln);
-       }
-  }
-  if (nchngd == 0 && !gflg) {
-       return(ERR);
-  }
-  return(nchngd);
-}
-
-/*     System.c        */
-#define SHELL  "/bin/sh"
-#define SHELL2 "/usr/bin/sh"
-
-int System(c)
-char *c;
-{
-  int pid, status;
-
-  switch (pid = fork()) {
-      case -1:
-       return -1;
-      case 0:
-       execl(SHELL, "sh", "-c", c, (char *) 0);
-       execl(SHELL2, "sh", "-c", c, (char *) 0);
-       exit(-1);
-      default: while (wait(&status) != pid);
-}
-  return status;
-}
-
-/*     unmkpat.c       */
-/* #include <stdio.h> */
-/* #include "tools.h" */
-
-/* Free up the memory usde for token string */
-void unmakepat(head)
-TOKEN *head;
-{
-
-  register TOKEN *old_head;
-
-  while (head) {
-       switch (head->tok) {
-           case CCL:
-           case NCCL:
-               free(head->bitmap);
-               /* Fall through to default */
-
-           default:
-               old_head = head;
-               head = head->next;
-               free((char *) old_head);
-               break;
-       }
-  }
-}
index 8c03176b1427757fb1820d631244a56629262c24..cc0dfcad45e77a6b4179e5e4b489f38f6251e4f9 100644 (file)
@@ -4,7 +4,7 @@ MAN=    ash.1 at.1 banner.1 basename.1 \
        chmod.1 cksum.1 clear.1 cmp.1 comm.1 compress.1 \
        cp.1 crc.1 crontab.1 ctags.1 dd.1 dev2name.1 \
        df.1 dhrystone.1 dosdir.1 dosread.1 doswrite.1 \
-       dumpcore.1 echo.1 ed.1 eject.1 elvis.1 elvrec.1 \
+       dumpcore.1 echo.1 eject.1 elvis.1 elvrec.1 \
        env.1 expand.1 expr.1 factor.1 file.1 \
        finger.1 flexdoc.1 fmt.1 fold.1 format.1 fortune.1 \
        fsck.mfs.1 head.1 host.1 hostaddr.1 ifdef.1 \
diff --git a/man/man1/ed.1 b/man/man1/ed.1
deleted file mode 100644 (file)
index 44b7c62..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-.TH ED 1
-.SH NAME
-ed \- editor
-.SH SYNOPSIS
-\fBed \fIfile\fR
-.br
-.de FL
-.TP
-\\fB\\$1\\fR
-\\$2
-..
-.de EX
-.TP 20
-\\fB\\$1\\fR
-# \\$2
-..
-.SH OPTIONS
-.FL "\-" "Suppress line/byte count messages (for in scripts)"
-.SH EXAMPLES
-.EX "ed prog.c" "Edit \fIprog.c\fR"
-.EX "echo '1,$p' | ed - file" "Odd way to write 'cat file'"
-.SH DESCRIPTION
-.PP
-\fIEd\fR is functionally equivalent to the standard V7 editor, ed.  
-It supports the following commands:
-.PP
-.nf
-.ta 0.5i 0.95i 
-       (.)     a: append
-       (.,.)   c: change
-       (.,.)   d: delete
-               e: edit new file"
-               f: print name of edited file"
-       (1,$)   g: global command
-       (.)     i: insert
-       (.,.+1) j: join lines together
-       (.)     k: mark
-       (.)     l: print with special characters in octal
-       (.,.)   m: move
-       (.,.)   p: print
-               q: quit editor"
-       (.)     r: read in new file
-       (.,.)   s: substitute
-       (1,$)   v: like g, except select lines that do not match
-       (1,$)   w: write out edited file
-.fi
-Many of the commands can take one or two addresses, as indicated above.  The
-defaults are shown in parentheses.  Thus \fIa\fR appends to the current 
-line, and \fIg\fR works on the whole file as default.  
-The dot refers to the current line.
-Below is a sample editing session with comments given following the # symbol.
-.PP
-.nf
-.ta 0.5i 2.5i
-       ed prog.c       # Edit prog.c
-       3,20p   # Print lines 3 through 20
-       /whole/ # Find next occurence of \fIwhole\fR
-       s/whole/while/  # Replace \fIwhole\fR by \fIwhile\fR
-       g/Buf/s//BUF/g  # Replace \fIBuf\fR by \fIBUF\fR everywhere
-       w       # Write the file back
-       q       # Exit the editor
-.fi
-\fIEd\fR is provided for its sentimental value.
-If you want a line-oriented editor, try \fIex\fR.
-If you want a good editor, use \fIelvis\fR or \fImined\fR.
-.SH "SEE ALSO"
-.BR elvis (1),
-.BR mined (1x).
index bf059ec0b40ef3ef93876e7a248a1f2c1d48e4be..ea9bd4c052eb96a9edbef26e9603750a63a2574d 100644 (file)
@@ -26,6 +26,7 @@
 2008/07/20 00:52:40,bin/mkdir
 2011/08/29 14:48:46,bin/rm
 2011/08/29 14:49:38,bin/rmdir
+2012/01/16 18:47:57,bin/ed
 2011/06/09 21:23:29,sbin/fsck
 2009/04/11 12:10:02,usr.bin/chpass
 2010/05/14 17:14:28,usr.bin/m4