Fixed range address bug: 1,2, == 2,2 not 2,.

Overhauled the name space,  reworked some modules and removed the
obsolescent Addison-Wesley copyright.
This commit is contained in:
Andrew Moore 1994-02-01 00:36:28 +00:00
parent a301c9d5d4
commit 95e6217e73
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=1057
22 changed files with 2772 additions and 477 deletions

View file

@ -1,8 +1,7 @@
PROG= ed
CFLAGS+=-DVI_BANG
SRCS= ed.c re.c buf.c cbc.c
LINKS= ${BINDIR}/ed ${BINDIR}/red
MLINKS= ed.1 red.1
SRCS= buf.c cbc.c glob.c io.c main.c re.c sub.c undo.c
LINKS= ${BINDIR}/ed ${BINDIR}/red
MLINKS= ed.1 red.1
.if exists(/usr/lib/libcrypt.a)
CFLAGS+=-DDES
@ -10,7 +9,4 @@ LDADD+= -lcrypt
DPADD+= ${LIBCRYPT}
.endif
LDADD+= -lgnuregex
DPADD+= /usr/lib/libgnuregex.a
.include <bsd.prog.mk>

View file

@ -1,62 +1,101 @@
This version of ed is not strictly POSIX compliant, as described in the
POSIX 1003.2 Draft 11.2 document. BSD commands have been implemented
wherever they do not conflict with the POSIX standard. 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.
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.
The BSD commands included are:
1) `s' (i.e., s[rgp]*) to repeat a previous substitution,
2) `W' for appending text to an existing file,
3) `wq' for exiting after a write, and
4) `z' for scrolling through the buffer.
BSD line addressing syntax (i.e., `^' and `%'). is also recognized.
OMISSIONS
---------
1) Locale(3) is not supported yet.
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 (\).
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.
If crypt 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.
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.
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.
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.
The vi editor's bang command syntax is supported, i.e.,
(addr1,addr2) !<shell-cmd> replaces the addressed lines with the output of
the command <shell-cmd>.
[rwe] !! reads/writes/edits the previous !<shell-cmd>.
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).
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.
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 (\).
ed runs in restricted mode if invoked as red. This limits editing of
files in the local directory only and prohibits !<shell-cmd> commands.
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.
Though ed is not a binary editor, it can be used (if painfully) 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.
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.
Since the behavior of `u' (undo) within a `g' (global) command list is
not specified by POSIX D11/2, it follows the behavior of the SunOS ed
(this is the best way, I think, in that the alternatives are either too
complicated to implement or too confusing to use): 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.
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.
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.
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 occurences 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.

View file

@ -3,13 +3,14 @@ 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()
compile with little trouble. Otherwise, the macros SPL1() and SPL0()
should be redefined to disable interrupts.
The following compiler directives are recognized:
DES - use to add encryption support (requires crypt(3))
NO_REALLOC_NULL - use if realloc(3) does not accept a NULL pointer
BACKWARDS - use for backwards compatibility
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.

View file

@ -1,12 +1,9 @@
/* buf.c: This file contains the scratch-file buffer rountines for the
ed line editor. */
/*-
* Copyright (c) 1992 The Regents of the University of California.
* Copyright (c) 1993 Andrew Moore, Talke Studio.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Rodney Ruddock of the University of Guelph.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -15,18 +12,11 @@
* 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
* 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 REGENTS OR CONTRIBUTORS BE LIABLE
* 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)
@ -35,37 +25,32 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static char sccsid[] = "@(#)buf.c 5.5 (Berkeley) 3/28/93";
static char *rcsid = "@(#)$Id: buf.c,v 1.3 1993/12/14 16:19:56 alm Exp $";
#endif /* not lint */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <unistd.h>
#include "ed.h"
extern char errmsg[];
FILE *sfp; /* scratch file pointer */
char *sfbuf = NULL; /* scratch file input buffer */
int sfbufsz = 0; /* scratch file input buffer size */
off_t sfseek; /* scratch file position */
int seek_write; /* seek before writing */
line_t line0; /* initial node of line queue */
line_t buffer_head; /* incore buffer */
/* gettxt: get a line of text from the scratch file; return pointer
/* get_sbuf_line: get a line of text from the scratch file; return pointer
to the text */
char *
gettxt(lp)
get_sbuf_line(lp)
line_t *lp;
{
static char *sfbuf = NULL; /* buffer */
static int sfbufsz = 0; /* buffer size */
int len, ct;
if (lp == &line0)
if (lp == &buffer_head)
return NULL;
seek_write = 1; /* force seek on write */
/* out of position */
@ -77,8 +62,8 @@ gettxt(lp)
return NULL;
}
}
len = lp->len & ~ACTV;
CKBUF(sfbuf, sfbufsz, len + 1, 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");
@ -90,13 +75,10 @@ gettxt(lp)
}
extern long curln;
extern long lastln;
/* puttxt: write a line of text to the scratch file and add a line node
/* 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 *
puttxt(cs)
put_sbuf_line(cs)
char *cs;
{
line_t *lp;
@ -115,7 +97,7 @@ puttxt(cs)
sprintf(errmsg, "line too long");
return NULL;
}
len = (s - cs) & ~ACTV;
len = s - cs;
/* out of position */
if (seek_write) {
if (fseek(sfp, 0L, SEEK_END) < 0) {
@ -126,7 +108,7 @@ puttxt(cs)
sfseek = ftell(sfp);
seek_write = 0;
}
/* assert: spl1() */
/* assert: SPL1() */
if ((ct = fwrite(cs, sizeof(char), len, sfp)) < 0 || ct != len) {
sfseek = -1;
fprintf(stderr, "%s\n", strerror(errno));
@ -135,37 +117,37 @@ puttxt(cs)
}
lp->len = len;
lp->seek = sfseek;
lpqueue(lp);
add_line_node(lp);
sfseek += len; /* update file position */
return ++s;
}
/* lpqueue: add a line node in the editor buffer after the current line */
/* add_line_node: add a line node in the editor buffer after the current line */
void
lpqueue(lp)
add_line_node(lp)
line_t *lp;
{
line_t *cp;
cp = getlp(curln); /* this getlp last! */
insqueue(lp, cp);
lastln++;
curln++;
cp = get_addressed_line_node(current_addr); /* this get_addressed_line_node last! */
insque(lp, cp);
addr_last++;
current_addr++;
}
/* getaddr: return line number of pointer */
/* get_line_node_addr: return line number of pointer */
long
getaddr(lp)
get_line_node_addr(lp)
line_t *lp;
{
line_t *cp = &line0;
line_t *cp = &buffer_head;
long n = 0;
while (cp != lp && (cp = cp->next) != &line0)
while (cp != lp && (cp = cp->q_forw) != &buffer_head)
n++;
if (n && cp == &line0) {
if (n && cp == &buffer_head) {
sprintf(errmsg, "invalid address");
return ERR;
}
@ -173,43 +155,47 @@ getaddr(lp)
}
/* getlp: return pointer to a line node in the editor buffer */
/* get_addressed_line_node: return pointer to a line node in the editor buffer */
line_t *
getlp(n)
get_addressed_line_node(n)
long n;
{
static line_t *lp = &line0;
static line_t *lp = &buffer_head;
static long on = 0;
spl1();
SPL1();
if (n > on)
if (n <= (on + lastln) >> 1)
if (n <= (on + addr_last) >> 1)
for (; on < n; on++)
lp = lp->next;
lp = lp->q_forw;
else {
lp = line0.prev;
for (on = lastln; on > n; on--)
lp = lp->prev;
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->prev;
lp = lp->q_back;
else {
lp = &line0;
lp = &buffer_head;
for (on = 0; on < n; on++)
lp = lp->next;
lp = lp->q_forw;
}
spl0();
SPL0();
return lp;
}
extern int newline_added;
char sfn[15] = ""; /* scratch file name */
/* sbopen: open scratch file */
sbopen()
/* open_sbuf: open scratch file */
int
open_sbuf()
{
isbinary = newline_added = 0;
strcpy(sfn, "/tmp/ed.XXXXXX");
if (mktemp(sfn) == NULL || (sfp = fopen(sfn, "w+")) == NULL) {
fprintf(stderr, "%s: %s\n", sfn, strerror(errno));
@ -220,8 +206,9 @@ sbopen()
}
/* sbclose: close scratch file */
sbclose()
/* close_sbuf: close scratch file */
int
close_sbuf()
{
if (sfp) {
if (fclose(sfp) < 0) {
@ -237,7 +224,7 @@ sbclose()
}
/* quit: remove scratch file and exit */
/* quit: remove_lines scratch file and exit */
void
quit(n)
int n;
@ -252,23 +239,30 @@ quit(n)
unsigned char ctab[256]; /* character translation table */
/* init_buf: open scratch buffer; initialize line queue */
/* init_buffers: open scratch buffer; initialize line queue */
void
init_buf()
init_buffers()
{
int i = 0;
if (sbopen() < 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);
requeue(&line0, &line0);
REQUE(&buffer_head, &buffer_head);
for (i = 0; i < 256; i++)
ctab[i] = i;
}
/* translit: translate characters in a string */
/* translit_text: translate characters in a string */
char *
translit(s, len, from, to)
translit_text(s, len, from, to)
char *s;
int len;
int from;

View file

@ -1,14 +1,10 @@
/* cbc.c: This file contains the encryption routines for the ed line editor */
/*-
* Copyright (c) 1991 The Regents of the University of California.
* Copyright (c) 1993 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Matt Bishop of Dartmouth College.
*
* The United States Government has rights in this work pursuant
* to contract no. NAG 2-680 between the National Aeronautics and
* Space Administration and Dartmouth College.
* 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
@ -37,37 +33,22 @@
* 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
*/
#ifndef lint
static char sccsid[] = "@(#)cbc.c 5.5 (Berkeley) 6/27/91";
static char *rcsid = "@(#)$Id: cbc.c,v 1.3 1993/12/14 18:01:10 alm Exp $";
#endif /* not lint */
/* Author: Matt Bishop
* Department of Mathematics and Computer Science
* Dartmouth College
* Hanover, NH 03755
* Email: Matt.Bishop@dartmouth.edu
* ...!decvax!dartvax!Matt.Bishop
*
* See Technical Report PCS-TR91-158, Department of Mathematics and Computer
* Science, Dartmouth College, for a detailed description of the implemen-
* tation and differences between it and Sun's. The DES is described in
* FIPS PUB 46, and the modes in FIPS PUB 81 (see either the manual page
* or the technical report for a complete reference).
*/
#include <sys/types.h>
#include <ctype.h>
#include <errno.h>
#include <pwd.h>
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "ed.h"
/*
* Define a divisor for rand() that yields a uniform distribution in the
* range 0-255.
@ -76,7 +57,7 @@ static char sccsid[] = "@(#)cbc.c 5.5 (Berkeley) 6/27/91";
/*
* BSD and System V systems offer special library calls that do
* block moves and fills, so if possible we take advantage of them
* 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))
@ -84,10 +65,10 @@ static char sccsid[] = "@(#)cbc.c 5.5 (Berkeley) 6/27/91";
/* Hide the calls to the primitive encryption routines. */
#define DES_KEY(buf) \
if (des_setkey(buf)) \
err("des_setkey");
des_error("des_setkey");
#define DES_XFORM(buf) \
if (des_cipher(buf, buf, 0L, (inverse ? -1 : 1))) \
err("des_cipher");
des_error("des_cipher");
/*
* read/write - no error checking
@ -119,14 +100,14 @@ char bits[] = { /* used to extract bits from a char */
};
int pflag; /* 1 to preserve parity bits */
char des_buf[8]; /* shared buffer for desgetc/desputc */
int des_ct = 0; /* count for desgetc/desputc */
int des_n = 0; /* index for desputc/desgetc */
unsigned char des_buf[8]; /* shared buffer for get_des_char/put_des_char */
int des_ct = 0; /* count for get_des_char/put_des_char */
int des_n = 0; /* index for put_des_char/get_des_char */
/* desinit: initialize DES */
/* init_des_cipher: initialize DES */
void
desinit()
init_des_cipher()
{
#ifdef DES
int i;
@ -144,28 +125,30 @@ desinit()
}
/* desgetc: return next char in an encrypted file */
desgetc(fp)
/* get_des_char: return next char in an encrypted file */
int
get_des_char(fp)
FILE *fp;
{
#ifdef DES
if (des_n >= des_ct) {
des_n = 0;
des_ct = cbcdec(des_buf, fp);
des_ct = cbc_decode(des_buf, fp);
}
return (des_ct > 0) ? des_buf[des_n++] : EOF;
#endif
}
/* desputc: write a char to an encrypted file; return char written */
desputc(c, fp)
/* put_des_char: write a char to an encrypted file; return char written */
int
put_des_char(c, fp)
int c;
FILE *fp;
{
#ifdef DES
if (des_n == sizeof des_buf) {
des_ct = cbcenc(des_buf, des_n, fp);
des_ct = cbc_encode(des_buf, des_n, fp);
des_n = 0;
}
return (des_ct >= 0) ? (des_buf[des_n++] = c) : EOF;
@ -173,16 +156,17 @@ desputc(c, fp)
}
/* desflush: flush an encrypted file's output; return status */
desflush(fp)
/* flush_des_file: flush an encrypted file's output; return status */
int
flush_des_file(fp)
FILE *fp;
{
#ifdef DES
if (des_n == sizeof des_buf) {
des_ct = cbcenc(des_buf, des_n, fp);
des_ct = cbc_encode(des_buf, des_n, fp);
des_n = 0;
}
return (des_ct >= 0 && cbcenc(des_buf, des_n, fp) >= 0) ? 0 : EOF;
return (des_ct >= 0 && cbc_encode(des_buf, des_n, fp) >= 0) ? 0 : EOF;
#endif
}
@ -190,7 +174,8 @@ desflush(fp)
/*
* get keyword from tty or stdin
*/
getkey()
int
get_keyword()
{
register char *p; /* used to obtain the key */
Desbuf msgbuf; /* I/O buffer */
@ -203,9 +188,9 @@ getkey()
/*
* copy it, nul-padded, into the key area
*/
cvtkey(BUFFER(msgbuf), p);
expand_des_key(BUFFER(msgbuf), p);
MEMZERO(p, _PASSWORD_LEN);
makekey(msgbuf);
set_des_key(msgbuf);
MEMZERO(msgbuf, sizeof msgbuf);
return 1;
}
@ -213,13 +198,11 @@ getkey()
}
extern char errmsg[];
/*
* print a warning message and, possibly, terminate
*/
void
err(s)
des_error(s)
char *s; /* the message */
{
(void)sprintf(errmsg, "%s", s ? s : strerror(errno));
@ -228,7 +211,8 @@ err(s)
/*
* map a hex character to an integer
*/
tobinhex(c, radix)
int
hex_to_binary(c, radix)
int c; /* char to be converted */
int radix; /* base (2 to 16) */
{
@ -260,7 +244,7 @@ tobinhex(c, radix)
* convert the key to a bit pattern
*/
void
cvtkey(obuf, ibuf)
expand_des_key(obuf, ibuf)
char *obuf; /* bit pattern */
char *ibuf; /* the key itself */
{
@ -276,8 +260,8 @@ cvtkey(obuf, ibuf)
* now translate it, bombing on any illegal hex digit
*/
for (i = 0; ibuf[i] && i < 16; i++)
if ((nbuf[i] = tobinhex((int) ibuf[i], 16)) == -1)
err("bad hex digit in key");
if ((nbuf[i] = hex_to_binary((int) ibuf[i], 16)) == -1)
des_error("bad hex digit in key");
while (i < 16)
nbuf[i++] = 0;
for (i = 0; i < 8; i++)
@ -296,8 +280,8 @@ cvtkey(obuf, ibuf)
* now translate it, bombing on any illegal binary digit
*/
for (i = 0; ibuf[i] && i < 16; i++)
if ((nbuf[i] = tobinhex((int) ibuf[i], 2)) == -1)
err("bad binary digit in key");
if ((nbuf[i] = hex_to_binary((int) ibuf[i], 2)) == -1)
des_error("bad binary digit in key");
while (i < 64)
nbuf[i++] = 0;
for (i = 0; i < 8; i++)
@ -328,7 +312,7 @@ cvtkey(obuf, ibuf)
* DES ignores the low order bit of each character.
*/
void
makekey(buf)
set_des_key(buf)
Desbuf buf; /* key block */
{
register int i, j; /* counter in a for loop */
@ -357,7 +341,8 @@ makekey(buf)
/*
* This encrypts using the Cipher Block Chaining mode of DES
*/
cbcenc(msgbuf, n, fp)
int
cbc_encode(msgbuf, n, fp)
char *msgbuf;
int n;
FILE *fp;
@ -395,7 +380,8 @@ cbcenc(msgbuf, n, fp)
/*
* This decrypts using the Cipher Block Chaining mode of DES
*/
cbcdec(msgbuf, fp)
int
cbc_decode(msgbuf, fp)
char *msgbuf; /* I/O buffer */
FILE *fp; /* input file descriptor */
{
@ -419,7 +405,7 @@ cbcdec(msgbuf, fp)
if ((c = fgetc(fp)) == EOF) {
n = CHAR(msgbuf, 7);
if (n < 0 || n > 7) {
err("decryption failed (block corrupted)");
des_error("decryption failed (block corrupted)");
return EOF;
}
} else
@ -427,9 +413,9 @@ cbcdec(msgbuf, fp)
return n;
}
if (n > 0)
err("decryption failed (incomplete block)");
des_error("decryption failed (incomplete block)");
else if (n < 0)
err("cannot read file");
des_error("cannot read file");
return EOF;
}
#endif /* DES */

View file

@ -134,7 +134,7 @@ The default filename is set to
only if it is not prefixed with a bang.
.SS LINE ADDRESSING
An address represents the number of line in the buffer.
An address represents the number of a line in the buffer.
.B ed
maintains a
.I current address
@ -165,25 +165,26 @@ and is legal wherever it makes sense.
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 the second. If an
value of the the second. If only one address is given in a range, then
the second address is set to the given address. If an
.IR n- tuple
of addresses is given where
.I n > 2,
then the corresponding range is determined by the last two addresses
in the
then the corresponding range is determined by the last two addresses in
the
.IR n- tuple.
If only one address is expected, then the last
address is used.
If only one address is expected, then the last address is used.
Each address in a comma-delimited range is interpreted relative to the
current address. In a semi-colon-delimited range, the first address is
used to set the current address, and the second address is interpreted
relative to the first.
The following address symbols are recognized.
.TP 8
\fR.\fR
\&.
The current line (address) in the buffer.
.TP 8
@ -511,7 +512,9 @@ The current address is set to the last line read.
.RI e \ !command
Edits the standard output of
.IR `!command' ,
executed as described below.
(see
.RI ! command
below).
The default filename is unchanged.
Any lines in the buffer are deleted before the output of
.I command
@ -629,6 +632,12 @@ deleted or otherwise modified.
.TP 8
(.,.)l
Prints the addressed lines unambiguously.
If a single line fills for than one screen (as might be the case
when viewing a binary file, for instance), a `--More--'
prompt is printed on the last line.
.B ed
waits until the RETURN key is pressed
before displaying the next screen.
The current address is set to the last line
printed.
@ -689,7 +698,9 @@ Reads
to after the addressed line
the standard output of
.IR `!command' ,
executed as described below.
(see the
.RI ! command
below).
The default filename is unchanged.
The current address is set to the last line read.
@ -707,9 +718,9 @@ matching a regular expression
with
.IR replacement .
By default, only the first match in each line is replaced.
The
If the
.I `g'
(global) suffix causes every match to be replaced.
(global) suffix is given, then every match to be replaced.
The
.I `n'
suffix, where
@ -724,7 +735,10 @@ The current address is set the last line affected.
.I re
and
.I replacement
may be delimited by any character other than space and newline.
may be delimited by any character other than space and newline
(see the
.I `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
.I `p'
@ -755,12 +769,18 @@ if they are escaped with a backslash (\\).
Repeats the last substitution.
This form of the
.I `s'
command may be suffixed with
any combination of the characters
command accepts a count suffix
.IR `n' ,
or any combination of the characters
.IR `r' ,
.IR `g' ,
and
.IR `p' .
If a count suffix
.I `n'
is given, then only the
.IR n th
match is replaced.
The
.I `r'
suffix causes
@ -840,7 +860,9 @@ command.
.RI (1,$)w \ !command
Writes the addressed lines to the standard input of
.IR `!command' ,
executed as described below.
(see the
.RI ! command
below).
The default filename and current address are unchanged.
.TP 8
@ -890,13 +912,6 @@ When the shell returns from execution, a `!'
is printed to the standard output.
The current line is unchanged.
.TP 8
.RI (.,.)! command
Replaces the addressed lines with the output of
.I `!command'
as described above.
The current address is set to the last line read.
.TP 8
($)=
Prints the line number of the addressed line.

View file

@ -1,11 +1,8 @@
/* ed.h: type and constant definitions for the ed editor. */
/*
* Copyright (c) 1993 The Regents of the University of California.
* Copyright (c) 1993 Andrew Moore
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Andrew Moore, Talke Studio.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -14,13 +11,6 @@
* 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
@ -34,22 +24,22 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ed.h 5.5 (Berkeley) 3/28/93
* @(#)$Id: ed.h,v 1.4 1993/12/15 15:22:02 alm Exp alm $
*/
#include <unistd.h>
#include <errno.h>
#if defined(BSD) && BSD >= 199103 || defined(__386BSD__)
# include <sys/param.h> /* for MAXPATHLEN */
#endif
#include <errno.h>
#ifdef sun
# include <limits.h>
#endif
#include <regex.h>
#include <signal.h>
#define BITSPERBYTE 8
#define BITS(type) (BITSPERBYTE * (int)sizeof(type))
#define CHARBITS BITS(char)
#define INTBITS BITS(int)
#define INTHIBIT (unsigned) (1 << (INTBITS - 1))
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define ERR (-2)
#define EMOD (-3)
@ -59,21 +49,28 @@
# define MAXPATHLEN 255 /* _POSIX_PATH_MAX */
#endif
#define MAXFNAME MAXPATHLEN /* max file name size */
#define MINBUFSZ 512 /* minimum buffer size - must be > 0 */
#define LINECHARS (INTHIBIT - 1) /* max chars per line */
#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 *next;
struct line *prev;
struct line *q_forw;
struct line *q_back;
off_t seek; /* address of line in scratch buffer */
#define ACTV INTHIBIT /* active bit: high bit of len */
int len; /* length of line */
} line_t;
@ -98,87 +95,94 @@ typedef struct undo {
# define min(a,b) ((a) < (b) ? (a) : (b))
#endif
/* nextln: return line after l mod k */
#define nextln(l,k) ((l)+1 > (k) ? 0 : (l)+1)
#define INC_MOD(l, k) ((l) + 1 > (k) ? 0 : (l) + 1)
#define DEC_MOD(l, k) ((l) - 1 < 0 ? (k) : (l) - 1)
/* nextln: return line before l mod k */
#define prevln(l,k) ((l)-1 < 0 ? (k) : (l)-1)
/* SPL1: disable some interrupts (requires reliable signals) */
#define SPL1() mutex++
#define skipblanks() while (isspace(*ibufp) && *ibufp != '\n') ibufp++
/* spl1: disable some interrupts (requires reliable signals) */
#define spl1() mutex++
/* spl0: enable all interrupts; check sigflags (requires reliable signals) */
#define spl0() \
/* SPL0: enable all interrupts; check sigflags (requires reliable signals) */
#define SPL0() \
if (--mutex == 0) { \
if (sigflags & (1 << SIGHUP)) dohup(SIGHUP); \
if (sigflags & (1 << SIGINT)) dointr(SIGINT); \
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) { \
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)
/* CKBUF: assure at least a minimum size for buffer b */
#define CKBUF(b,n,i,err) \
/* 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(); \
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(); \
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(); \
SPL0(); \
return err; \
} \
} \
(n) = ti; \
(b) = ts; \
spl0(); \
SPL0(); \
}
#else /* NO_REALLOC_NULL */
/* CKBUF: assure at least a minimum size for buffer b */
#define CKBUF(b,n,i,err) \
/* 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(); \
SPL1(); \
if ((ts = (char *) realloc((b), ti += max((i), MINBUFSZ))) == NULL) { \
fprintf(stderr, "%s\n", strerror(errno)); \
sprintf(errmsg, "out of memory"); \
spl0(); \
SPL0(); \
return err; \
} \
(n) = ti; \
(b) = ts; \
spl0(); \
SPL0(); \
}
#endif /* NO_REALLOC_NULL */
/* requeue: link pred before succ */
#define requeue(pred, succ) (pred)->next = (succ), (succ)->prev = (pred)
/* REQUE: link pred before succ */
#define REQUE(pred, succ) (pred)->q_forw = (succ), (succ)->q_back = (pred)
/* insqueue: insert elem in circular queue after pred */
#define insqueue(elem, pred) \
#ifdef NEED_INSQUE
/* insque: insert elem in circular queue after pred */
#define insque(elem, pred) \
{ \
requeue((elem), (pred)->next); \
requeue((pred), elem); \
REQUE((elem), (pred)->q_forw); \
REQUE((pred), elem); \
}
/* remqueue: remove elem from circular queue */
#define remqueue(elem) requeue((elem)->prev, (elem)->next);
/* remque: remove_lines elem from circular queue */
#define remque(elem) REQUE((elem)->q_back, (elem)->q_forw);
#endif /* NEED_INSQUE */
/* nultonl: overwrite ASCII NULs with newlines */
#define nultonl(s, l) translit(s, l, '\0', '\n')
/* NUL_TO_NEWLINE: overwrite ASCII NULs with newlines */
#define NUL_TO_NEWLINE(s, l) translit_text(s, l, '\0', '\n')
/* nltonul: overwrite newlines with ASCII NULs */
#define nltonul(s, l) translit(s, l, '\n', '\0')
/* NEWLINE_TO_NUL: overwrite newlines with ASCII NULs */
#define NEWLINE_TO_NUL(s, l) translit_text(s, l, '\n', '\0')
#ifndef strerror
# define strerror(n) sys_errlist[n]
@ -192,75 +196,101 @@ if ((i) > (n)) { \
# endif
#endif
/* local function declarations */
int append __P((long, int));
int cbcdec __P((char *, FILE *));
int cbcenc __P((char *, int, FILE *));
char *ckfn __P((char *));
int ckglob __P((void));
int ckrange __P((long, long));
int desflush __P((FILE *));
int desgetc __P((FILE *));
void desinit __P((void));
int desputc __P((int, FILE *));
int docmd __P((int));
void err __P((char *));
char *ccl __P((char *));
void clrmark __P((line_t *));
void cvtkey __P((char *, char *));
long doglob __P((int));
void dohup __P((int));
void dointr __P((int));
void dowinch __P((int));
int doprint __P((long, long, int));
long doread __P((long, char *));
long dowrite __P((long, long, char *, char *));
char *esctos __P((char *));
long patscan __P((pattern_t *, int));
long getaddr __P((line_t *));
char *getcmdv __P((int *, int));
char *getfn __P((void));
int getkey __P((void));
char *getlhs __P((int));
int getline __P((void));
int getlist __P((void));
long getmark __P((int));
long getnum __P((int));
long getone __P((void));
line_t *getlp __P((long));
int getrhs __P((int));
int getshcmd __P((void));
char *gettxt __P((line_t *));
void init_buf __P((void));
int join __P((long, long));
int lndelete __P((long, long));
line_t *lpdup __P((line_t *));
void lpqueue __P((line_t *));
void makekey __P((char *));
char *makesub __P((int));
int move __P((long, int));
int oddesc __P((char *, char *));
void onhup __P((int));
void onintr __P((int));
pattern_t *optpat __P((void));
int putmark __P((int, line_t *));
void putstr __P((char *, int, long, int));
char *puttxt __P((char *));
/* Local Function Declarations */
void add_line_node __P((line_t *));
int append_lines __P((long));
int apply_subst_template __P((char *, regmatch_t *, int, int));
int build_active_list __P((int));
int cbc_decode __P((char *, FILE *));
int cbc_encode __P((char *, int, FILE *));
int check_addr_range __P((long, long));
void clear_active_list __P((void));
void clear_undo_stack __P((void));
int close_sbuf __P((void));
int copy_lines __P((long));
int delete_lines __P((long, long));
void des_error __P((char *));
int display_lines __P((long, long, int));
line_t *dup_line_node __P((line_t *));
int exec_command __P((void));
long exec_global __P((int, int));
void expand_des_key __P((char *, char *));
int extract_addr_range __P((void));
char *extract_pattern __P((int));
int extract_subst_tail __P((int *, int *));
char *extract_subst_template __P((void));
int filter_lines __P((long, long, char *));
int flush_des_file __P((FILE *));
line_t *get_addressed_line_node __P((long));
pattern_t *get_compiled_pattern __P((void));
int get_des_char __P((FILE *));
char *get_extended_line __P((int *, int));
char *get_filename __P((void));
int get_keyword __P((void));
long get_line_node_addr __P((line_t *));
long get_matching_node_addr __P((pattern_t *, int));
long get_marked_node_addr __P((int));
char *get_sbuf_line __P((line_t *));
int get_shell_command __P((void));
int get_stream_line __P((FILE *));
int get_tty_line __P((void));
void handle_hup __P((int));
void handle_int __P((int));
void handle_winch __P((int));
int has_trailing_escape __P((char *, char *));
int hex_to_binary __P((int, int));
void init_buffers __P((void));
void init_des_cipher __P((void));
int is_legal_filename __P((char *));
int join_lines __P((long, long));
int mark_line_node __P((line_t *, int));
int move_lines __P((long));
line_t *next_active_node __P(());
long next_addr __P((void));
int open_sbuf __P((void));
char *parse_char_class __P((char *));
int pop_undo_stack __P((void));
undo_t *push_undo_stack __P((int, long, long));
int put_des_char __P((int, FILE *));
char *put_sbuf_line __P((char *));
int put_stream_line __P((FILE *, char *, int));
int put_tty_line __P((char *, int, long, int));
void quit __P((int));
int regsub __P((pattern_t *, line_t *, int));
int sbclose __P((void));
int sbopen __P((void));
int sgetline __P((FILE *));
int catsub __P((char *, regmatch_t *, int));
int subst __P((pattern_t *, int));
int tobinhex __P((int, int));
int transfer __P((long));
char *translit __P((char *, int, int, int));
int undo __P((int));
undo_t *upush __P((int, long, long));
void ureset __P((void));
long read_file __P((char *, long));
long read_stream __P((FILE *, long));
int search_and_replace __P((pattern_t *, int, int));
int set_active_node __P((line_t *));
void set_des_key __P((char *));
void signal_hup __P((int));
void signal_int __P((int));
char *strip_escapes __P((char *));
int substitute_matching_text __P((pattern_t *, line_t *, int, int));
char *translit_text __P((char *, int, int, int));
void unmark_line_node __P((line_t *));
void unset_active_nodes __P((line_t *, line_t *));
long write_file __P((char *, char *, long, long));
long write_stream __P((FILE *, long, long));
/* global buffers */
extern char stdinbuf[];
extern char *ibuf;
extern char *ibufp;
extern int ibufsz;
extern char *sys_errlist[];
/* 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 char errmsg[];
extern long first_addr;
extern int lineno;
extern long second_addr;
#ifdef sun
extern char *sys_errlist[];
#endif

375
bin/ed/io.c Normal file
View file

@ -0,0 +1,375 @@
/* 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.
*/
#ifndef lint
static char *rcsid = "@(#)$Id: io.c,v 1.3 1993/12/14 16:19:56 alm Exp $";
#endif /* not lint */
#include "ed.h"
extern int scripted;
/* read_file: read a named file/pipe into the buffer; return line count */
long
read_file(fn, n)
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;
}
fprintf(stderr, !scripted ? "%lu\n" : "", size);
return current_addr - n;
}
extern int des;
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(fp, n)
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(fp)
FILE *fp;
{
register int c;
register 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(fn, mode, n, m)
char *fn;
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;
}
fprintf(stderr, !scripted ? "%lu\n" : "", size);
return n ? m - n + 1 : 0;
}
/* write_stream: write a range of lines to a stream; return status */
long
write_stream(fp, n, m)
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(fp, s, len)
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(sizep, nonl)
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()
{
register int oi = 0;
register 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\\"
extern int rows;
extern int cols;
/* put_tty_line: print text to stdout */
int
put_tty_line(s, l, n, gflag)
char *s;
int l;
long n;
int gflag;
{
int col = 0;
int lc = 0;
char *cp;
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;
}

1432
bin/ed/main.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,12 +1,9 @@
/* re.c: This file contains the regular expression interface routines for
the ed line editor. */
/*-
* Copyright (c) 1993 The Regents of the University of California.
* Copyright (c) 1993 Andrew Moore, Talke Studio.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley
* by Andrew Moore, Talke Studio.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -15,18 +12,11 @@
* 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
* 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 REGENTS OR CONTRIBUTORS BE LIABLE
* 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)
@ -37,40 +27,34 @@
*/
#ifndef lint
static char sccsid[] = "@(#)re.c 5.5 (Berkeley) 3/28/93";
static char *rcsid = "@(#)$Id: re.c,v 1.2 1993/12/14 16:19:56 alm Exp $";
#endif /* not lint */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ed.h"
extern char *lhbuf;
extern int lhbufsz;
extern char *ibufp;
extern int ibufsz;
extern int patlock;
char errmsg[MAXFNAME + 40] = "";
char errmsg[MAXPATHLEN + 40] = "";
/* optpat: return pointer to compiled pattern from command buffer */
/* get_compiled_pattern: return pointer to compiled pattern from command
buffer */
pattern_t *
optpat()
get_compiled_pattern()
{
static pattern_t *exp = NULL;
char *exps;
char delim;
char delimiter;
int n;
if ((delim = *ibufp) == ' ') {
if ((delimiter = *ibufp) == ' ') {
sprintf(errmsg, "invalid pattern delimiter");
return NULL;
} else if (delim == '\n' || *++ibufp == '\n' || *ibufp == delim) {
} else if (delimiter == '\n' || *++ibufp == '\n' || *ibufp == delimiter) {
if (!exp) sprintf(errmsg, "no previous pattern");
return exp;
} else if ((exps = getlhs(delim)) == NULL)
} else if ((exps = extract_pattern(delimiter)) == NULL)
return NULL;
/* buffer alloc'd && not reserved */
if (exp && !patlock)
@ -90,23 +74,24 @@ optpat()
}
extern int isbinary;
/* getlhs: copy a pattern string from the command buffer; return pointer
to the copy */
/* extract_pattern: copy a pattern string from the command buffer; return
pointer to the copy */
char *
getlhs(delim)
int delim;
extract_pattern(delimiter)
int delimiter;
{
static char *lhbuf = NULL; /* buffer */
static int lhbufsz = 0; /* buffer size */
char *nd;
int len;
for (nd = ibufp; *nd != delim && *nd != '\n'; nd++)
for (nd = ibufp; *nd != delimiter && *nd != '\n'; nd++)
switch (*nd) {
default:
break;
case '[':
if ((nd = ccl(++nd)) == NULL) {
if ((nd = parse_char_class(++nd)) == NULL) {
sprintf(errmsg, "unbalanced brackets ([])");
return NULL;
}
@ -119,17 +104,17 @@ getlhs(delim)
break;
}
len = nd - ibufp;
CKBUF(lhbuf, lhbufsz, len + 1, NULL);
REALLOC(lhbuf, lhbufsz, len + 1, NULL);
memcpy(lhbuf, ibufp, len);
lhbuf[len] = '\0';
ibufp = nd;
return (isbinary) ? nultonl(lhbuf, len) : lhbuf;
return (isbinary) ? NUL_TO_NEWLINE(lhbuf, len) : lhbuf;
}
/* ccl: expand a POSIX character class */
/* parse_char_class: expand a POSIX character class */
char *
ccl(s)
parse_char_class(s)
char *s;
{
int c, d;

263
bin/ed/sub.c Normal file
View file

@ -0,0 +1,263 @@
/* 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.
*/
#ifndef lint
static char *rcsid = "@(#)$Id: sub.c,v 1.3 1993/12/15 15:22:02 alm Exp alm $";
#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(flagp, np)
int *flagp;
int *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()
{
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(pat, gflag, kth)
pattern_t *pat;
int gflag;
int kth;
{
undo_t *up;
char *txt;
char *eot;
long lc;
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++;
}
}
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(pat, lp, gflag, kth)
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(boln, rm, off, re_nsub)
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;
}

View file

@ -1,17 +1,23 @@
SHELL= /bin/sh
ED= ../obj/ed
all: build test
@echo done
all: check
@:
check: build test
@if grep -h '\*\*\*' errs.o scripts.o; then :; else \
echo "tests completed successfully."; \
fi
build: mkscripts.sh
@echo building test scripts...
@chmod +x mkscripts.sh
@./mkscripts.sh ${ED}
@if [ -f errs.o ]; then :; else \
echo "building test scripts for $(ED) ..."; \
$(SHELL) mkscripts.sh $(ED); \
fi
test: build ckscripts.sh
@echo running test scripts...
@chmod +x ckscripts.sh
@./ckscripts.sh ${ED}
@echo testing $(ED) ...
@$(SHELL) ckscripts.sh $(ED)
clean:
rm -f *.ed *.[oz] *~
rm -f *.ed *.red *.[oz] *~

View file

@ -19,14 +19,6 @@ which look like:
or:
*** Output u.o of script u.ed is incorrect ***
It is assumed that the ed being tested processes escapes (\) in file names.
This is so that a name starting with bang (!) can be read, via:
r \!file
Without the escape, a POSIX ed would attempt to read the output of
the shell command `file'. If the ed being tested does not support escape
processing on file names, then the script `mkscripts.sh' should be modified
accordingly.
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
@ -36,6 +28,3 @@ a1-err.ed
i1-err.ed
k1-err.ed
r1-err.ed
In addition, one of bang1-err.ed or bang2.ed will fail, depending on whether or
not ed was compiled with the VI_BANG directive.

9
bin/ed/test/addr.d Normal file
View file

@ -0,0 +1,9 @@
line 1
line 2
line 3
line 4
line5
1ine6
line7
line8
line9

2
bin/ed/test/addr.r Normal file
View file

@ -0,0 +1,2 @@
line 2
line9

5
bin/ed/test/addr.t Normal file
View file

@ -0,0 +1,5 @@
1 d
1 1 d
1,2,d
1;+ + ,d
1,2;., + 2d

View file

@ -5,26 +5,24 @@
PATH="/bin:/usr/bin:/usr/local/bin/:."
ED=$1
[ X"$ED" = X -o ! -x $ED ] && ED="../ed"
[ ! -x $ED ] && { echo "$ED: cannot execute"; exit 1; }
# Run the *-err.ed scripts first, since these don't generate output;
# rename then to *-err.ed~; they exit with non-zero status
for i in *-err.ed; do
echo $i~
# 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 ***"
echo "*** The script $i exited abnormally ***"
fi
mv $i $i~
done >errs.o 2>&1
# Run the remainding scripts; they exit with zero status
for i in *.ed; do
base=`expr $i : '\([^.]*\)'`
# base=`expr $i : '\([^.]*\)'`
# base=`echo $i | sed 's/\..*//'`
# base=`$ED - \!"echo \\\\$i" <<-EOF
# s/\..*
# EOF`
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 ***"

3
bin/ed/test/g5.d Normal file
View file

@ -0,0 +1,3 @@
line 1
line 2
line 3

9
bin/ed/test/g5.r Normal file
View file

@ -0,0 +1,9 @@
line 1
line 2
line 3
line 2
line 3
line 1
line 3
line 1
line 2

2
bin/ed/test/g5.t Normal file
View file

@ -0,0 +1,2 @@
g/./1,3t$\
1d

View file

@ -3,69 +3,71 @@
PATH="/bin:/usr/bin:/usr/local/bin/:."
ED=$1
[ X"$ED" = X -o ! -x $ED ] && ED="../ed"
[ ! -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 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
# r \\$base.d
# w \\$base.o
# EOT
# .
# -2r \\$i
# w \\$base.ed
# !chmod +x \\$base.ed
# EOF
# 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
# 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}-err.ed
# !chmod +x ${base}-err.ed
# EOF
# 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

154
bin/ed/undo.c Normal file
View file

@ -0,0 +1,154 @@
/* 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.
*/
#ifndef lint
static char *rcsid = "@(#)$Id: undo.c,v 1.2 1993/12/14 16:19:56 alm Exp $";
#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 intialized undo node */
undo_t *
push_undo_stack(type, from, to)
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()
{
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()
{
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;
}