.include <bsd.own.mk>
-SUBDIR= mkdir rm rmdir date
+SUBDIR= date ed mkdir rm rmdir
.include <bsd.subdir.mk>
--- /dev/null
+# $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>
--- /dev/null
+$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.
--- /dev/null
+$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.
--- /dev/null
+/* $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;
+}
--- /dev/null
+/* $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 */
--- /dev/null
+.\" $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 .
--- /dev/null
+/* $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
--- /dev/null
+/* $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();
+}
--- /dev/null
+/* $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;
+}
--- /dev/null
+/* $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;
+}
--- /dev/null
+/* $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;
+}
--- /dev/null
+/* $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;
+}
--- /dev/null
+# $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>
--- /dev/null
+$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
--- /dev/null
+$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
--- /dev/null
+hello world
+line 1
+hello world!
+line 2
+line 3
+line 4
+line5
+hello world!!
--- /dev/null
+0a
+hello world
+.
+2a
+hello world!
+.
+$a
+hello world!!
+.
--- /dev/null
+1,$a
+hello world
+.
--- /dev/null
+aa
+hello world
+.
--- /dev/null
+line 2
+line9
--- /dev/null
+1 d
+1 1 d
+1,2,d
+1;+ + ,d
+1,2;., + 2d
--- /dev/null
+!read one
+hello, world
+a
+okay
+.
--- /dev/null
+at the top
+between top/middle
+in the middle
+at the bottom
--- /dev/null
+1c
+at the top
+.
+4c
+in the middle
+.
+$c
+at the bottom
+.
+2,3c
+between top/middle
+.
--- /dev/null
+cc
+hello world
+.
--- /dev/null
+0c
+hello world
+.
--- /dev/null
+#!/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
--- /dev/null
+1d
+2;+1d
+$d
--- /dev/null
+hello world-
--- /dev/null
+E !echo hello world-
--- /dev/null
+E !echo hello world-
--- /dev/null
+E !echo hello world-
--- /dev/null
+g/./s //x/
--- /dev/null
+line5
+help! world
+order
+line 4
+help! world
+order
+line 3
+help! world
+order
+line 2
+help! world
+order
+line 1
+help! world
+order
--- /dev/null
+g/./m0
+g/./s/$/\
+hello world
+g/hello /s/lo/p!/\
+a\
+order
--- /dev/null
+hello world
--- /dev/null
+g/[2-4]/-1,+1c\
+hello world
--- /dev/null
+linc 3
+xine 1
+xine 2
+xinc 4
+xinc5
--- /dev/null
+g/./s//x/\
+3m0
+g/./s/e/c/\
+2,3m1
--- /dev/null
+hello
+zine 1
+line 2
+line 3
+line 4
+line5
+world
--- /dev/null
+g/./s/./x/\
+u\
+s/./y/\
+u\
+s/./z/\
+u
+u
+0a
+hello
+.
+$a
+world
+.
--- /dev/null
+line 1
+line 2
+line 3
+line 2
+line 3
+line 1
+line 3
+line 1
+line 2
--- /dev/null
+g/./1,3t$\
+1d
--- /dev/null
+hello world
+hello world!
+line 1
+line 2
+line 3
+line 4
+hello world!!
+line5
--- /dev/null
+1i
+hello world
+.
+2i
+hello world!
+.
+$i
+hello world!!
+.
--- /dev/null
+1,$i
+hello world
+.
--- /dev/null
+ii
+hello world
+.
--- /dev/null
+0i
+hello world
+.
--- /dev/null
+line 1
+line 2line 3
+line 4
+line5
--- /dev/null
+1,1j
+2,3j
--- /dev/null
+line 3
+hello world
+line 4
+line5
+line 2
--- /dev/null
+2ka
+1d
+'am$
+1ka
+0a
+hello world
+.
+'ad
+u
+'am0
--- /dev/null
+a
+hello
+.
+.ka
+'ad
+'ap
--- /dev/null
+a
+hello world
+.
+1,$m1
--- /dev/null
+line5
+line 1
+line 2
+line 3
+line 4
--- /dev/null
+1,2m$
+1,2m$
+1,2m$
+$m0
+$m0
+2,3m1
+2,3m3
--- /dev/null
+#!/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
--- /dev/null
+
+
+hello world
+line 1
+line 2
+line 3
+line 4
+line5
--- /dev/null
+1
+
+
+0a
+
+
+hello world
+.
--- /dev/null
+line 1
+line 2
+line 3
+line 4
+line5
+hello world
--- /dev/null
+a
+hello world
+.
+0;/./
--- /dev/null
+w q.o
+a
+hello
+.
+q
--- /dev/null
+1,$r r1.err
--- /dev/null
+line 1
+hello world
+line 2
+line 3
+line 4
+line5
+hello world
--- /dev/null
+1;r !echo hello world
+1
+r !echo hello world
--- /dev/null
+r a-good-book
--- /dev/null
+line 1
+line 2
+line 3
+line 4
+line5
+line 1
+line 2
+line 3
+line 4
+line5
--- /dev/null
+r r3.t
+r r3.t
--- /dev/null
+liene 1
+(liene) (2)
+(liene) (3)
+liene (4)
+(()liene5)
--- /dev/null
+s/\([^ ][^ ]*\)/(\1)/g
+2s
+/3/s
+/\(4\)/sr
+/\(.\)/srg
+%s/i/&e/
--- /dev/null
+a
+hello
+.
+s/[h[.]/x/
--- /dev/null
+a
+a
+.
+s/x*/a/g
--- /dev/null
+li(n)e 1
+i(n)e 200
+li(n)e 3
+li(n)e 4
+li(n)e500
--- /dev/null
+,s/./(&)/3
+s/$/00
+2s//%/g
+s/^l
--- /dev/null
+hello world
--- /dev/null
+a
+hello/[]world
+.
+s/[/]/ /
+s/[[:digit:][]/ /
+s/[]]/ /
--- /dev/null
+s/\a\b\c/xyz/
--- /dev/null
+a
+hello world
+.
+/./
+sr
--- /dev/null
+a
+hello
+.
+s/[h[=]/x/
--- /dev/null
+a
+hello
+.
+s/[h[:]/x/
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+1t0
+2,3t2
+,t$
--- /dev/null
+line 1
+line5
+line 2
+line 3
+line 4
+line5
--- /dev/null
+line 1
+hello
+hello world!!
+line 2
+line 3
+line 4
+line5
+hello
+hello world!!
--- /dev/null
+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$
--- /dev/null
+line5
+order
+hello world
+line 1
+order
+line 2
+order
+line 3
+order
+line 4
+order
--- /dev/null
+v/[ ]/m0
+v/[ ]/s/$/\
+hello world
+v/hello /s/lo/p!/\
+a\
+order
--- /dev/null
+line 1
+line 2
+line 3
+line 4
+line5
+line 1
+line 2
+line 3
+line 4
+line5
--- /dev/null
+w !cat >\!.z
+r \!.z
--- /dev/null
+w /to/some/far-away/place
--- /dev/null
+/* $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;
+}
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 \
+++ /dev/null
-PROG= ed
-MAN=
-
-.include <bsd.prog.mk>
+++ /dev/null
-/* 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;
- }
- }
-}
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 \
+++ /dev/null
-.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).
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