POSIX ed version 0.6 by Andrew Moore (alm@netcom.com).

This commit is contained in:
Andrew Moore 1993-06-18 13:00:14 +00:00
parent ddd9386e4d
commit 30154ac8a8
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/cvs2svn/branches/unlabeled-1.1.1/; revision=16
148 changed files with 2811 additions and 0 deletions

7
bin/ed/Makefile Normal file
View File

@ -0,0 +1,7 @@
PROG= ed
CFLAGS+=-I${.CURDIR} -DVI_BANG -DDES -DGNU_REGEX -DHAVE_STRING_H=1
SRCS= ed.c re.c buf.c cbc.c regex.c
LINKS= ${BINDIR}/ed ${BINDIR}/red
MLINKS= ed.1 red.1
.include <bsd.prog.mk>

50
bin/ed/POSIX Normal file
View File

@ -0,0 +1,50 @@
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.
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.
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 (\).
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.
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.
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>.
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.
ed runs in restricted mode if invoked as red. This limits editing of
files in the local directory only and prohibits !<shell-cmd> commands.
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.

22
bin/ed/README Normal file
View File

@ -0,0 +1,22 @@
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:
GNU_REGEX - use with GNU regex(3)
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
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.

246
bin/ed/buf.c Normal file
View File

@ -0,0 +1,246 @@
/* 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.
* 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:
* 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.
*/
#ifndef lint
static char sccsid[] = "@(#)buf.c 5.5 (Berkeley) 3/28/93";
#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[];
extern line_t line0;
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 */
/* gettxt: get a line of text from the scratch file; return pointer
to the text */
char *
gettxt(lp)
line_t *lp;
{
int len, ct;
if (lp == &line0)
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 & ~ACTV;
CKBUF(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;
}
extern long curln;
extern long lastln;
/* puttxt: 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)
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");
return NULL;
}
len = (s - cs) & ~ACTV;
/* 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");
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");
return NULL;
}
lp->len = len;
lp->seek = sfseek;
lpqueue(lp);
sfseek += len; /* update file position */
return ++s;
}
/* lpqueue: add a line node in the editor buffer after the current line */
void
lpqueue(lp)
line_t *lp;
{
line_t *cp;
cp = getlp(curln); /* this getlp last! */
insqueue(lp, cp);
lastln++;
curln++;
}
/* getaddr: return line number of pointer */
long
getaddr(lp)
line_t *lp;
{
line_t *cp = &line0;
long n = 0;
while (cp != lp && (cp = cp->next) != &line0)
n++;
return (cp != &line0) ? n : 0;
}
/* getlp: return pointer to a line node in the editor buffer */
line_t *
getlp(n)
long n;
{
static line_t *lp = &line0;
static long on = 0;
spl1();
if (n > on)
if (n <= (on + lastln) >> 1)
for (; on < n; on++)
lp = lp->next;
else {
lp = line0.prev;
for (on = lastln; on > n; on--)
lp = lp->prev;
}
else
if (n >= on >> 1)
for (; on > n; on--)
lp = lp->prev;
else {
lp = &line0;
for (on = 0; on < n; on++)
lp = lp->next;
}
spl0();
return lp;
}
char sfn[15] = ""; /* scratch file name */
/* sbopen: open scratch file */
sbopen()
{
strcpy(sfn, "/tmp/ed.XXXXXX");
if (mktemp(sfn) == NULL || (sfp = fopen(sfn, "w+")) == NULL) {
fprintf(stderr, "%s: %s\n", sfn, strerror(errno));
sprintf(errmsg, "cannot open temp file");
return ERR;
}
return 0;
}
/* sbclose: close scratch file */
sbclose()
{
if (sfp) {
if (fclose(sfp) < 0) {
fprintf(stderr, "%s: %s\n", sfn, strerror(errno));
sprintf(errmsg, "cannot close temp file");
return ERR;
}
sfp = NULL;
unlink(sfn);
}
sfseek = seek_write = 0;
return 0;
}
/* quit: remove scratch file and exit */
void
quit(n)
int n;
{
if (sfp) {
fclose(sfp);
unlink(sfn);
}
exit(n);
}

435
bin/ed/cbc.c Normal file
View File

@ -0,0 +1,435 @@
/* cbc.c: This file contains the encryption routines for the ed line editor */
/*-
* Copyright (c) 1991 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.
*
* 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.
*/
#ifndef lint
static char sccsid[] = "@(#)cbc.c 5.5 (Berkeley) 6/27/91";
#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 <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.
*/
#define RAND_DIV (((unsigned) RAND_MAX + 1) >> 8)
/*
* BSD and System V systems offer special library calls that do
* block moves 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)) \
err("des_setkey");
#define DES_XFORM(buf) \
if (des_cipher(buf, buf, 0L, (inverse ? -1 : 1))) \
err("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)
/*
* global variables and related macros
*/
enum { /* encrypt, decrypt, authenticate */
MODE_ENCRYPT, MODE_DECRYPT, MODE_AUTHENTICATE
} mode = MODE_ENCRYPT;
Desbuf ivec; /* initialization vector */
Desbuf pvec; /* padding vector */
char bits[] = { /* used to extract bits from a char */
'\200', '\100', '\040', '\020', '\010', '\004', '\002', '\001'
};
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 */
/* desinit: initialize DES */
void
desinit()
{
#ifdef DES
int i;
des_ct = des_n = 0;
/* initialize the initialization vctor */
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
}
/* desgetc: return next char in an encrypted file */
desgetc(fp)
FILE *fp;
{
#ifdef DES
if (des_n >= des_ct) {
des_n = 0;
des_ct = cbcdec(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)
int c;
FILE *fp;
{
#ifdef DES
if (des_n == sizeof des_buf) {
des_ct = cbcenc(des_buf, des_n, fp);
des_n = 0;
}
return (des_ct >= 0) ? (des_buf[des_n++] = c) : EOF;
#endif
}
/* desflush: flush an encrypted file's output; return status */
desflush(fp)
FILE *fp;
{
#ifdef DES
if (des_n == sizeof des_buf) {
des_ct = cbcenc(des_buf, des_n, fp);
des_n = 0;
}
return (des_ct >= 0 && cbcenc(des_buf, des_n, fp) >= 0) ? 0 : EOF;
#endif
}
#ifdef DES
/*
* get keyword from tty or stdin
*/
getkey()
{
register 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
*/
cvtkey(BUFFER(msgbuf), p);
MEMZERO(p, _PASSWORD_LEN);
makekey(msgbuf);
MEMZERO(msgbuf, sizeof msgbuf);
return 1;
}
return 0;
}
extern char errmsg[];
/*
* print a warning message and, possibly, terminate
*/
void
err(s)
char *s; /* the message */
{
(void)sprintf(errmsg, "%s", s ? s : strerror(errno));
}
/*
* map a hex character to an integer
*/
tobinhex(c, radix)
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
*/
void
cvtkey(obuf, ibuf)
char *obuf; /* bit pattern */
char *ibuf; /* the key itself */
{
register int i, j; /* counter in a for loop */
int nbuf[64]; /* used for hex/key translation */
/*
* leading '0x' or '0X' == hex key
*/
if (ibuf[0] == '0' && (ibuf[1] == 'x' || ibuf[1] == 'X')) {
ibuf = &ibuf[2];
/*
* 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");
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 (ibuf[0] == '0' && (ibuf[1] == 'b' || ibuf[1] == 'B')) {
ibuf = &ibuf[2];
/*
* 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");
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, ibuf, 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.
*/
void
makekey(buf)
Desbuf buf; /* key block */
{
register int i, j; /* counter in a for loop */
register 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
*/
cbcenc(msgbuf, n, fp)
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
*/
cbcdec(msgbuf, fp)
char *msgbuf; /* I/O buffer */
FILE *fp; /* input file descriptor */
{
Desbuf ibuf; /* temp buffer for initialization vector */
register int n; /* number of bytes actually read */
register 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(ibuf), BUFFER(msgbuf), 8);
DES_XFORM(UBUFFER(msgbuf));
for (c = 0; c < 8; c++)
UCHAR(msgbuf, c) ^= UCHAR(ivec, c);
MEMCPY(BUFFER(ivec), BUFFER(ibuf), 8);
/*
* if the last one, handle it specially
*/
if ((c = fgetc(fp)) == EOF) {
n = CHAR(msgbuf, 7);
if (n < 0 || n > 7) {
err("decryption failed (block corrupted)");
return EOF;
}
} else
(void)ungetc(c, fp);
return n;
}
if (n > 0)
err("decryption failed (incomplete block)");
else if (n < 0)
err("cannot read file");
return EOF;
}
#endif /* DES */

980
bin/ed/ed.1 Normal file
View File

@ -0,0 +1,980 @@
.TH ED 1 "21 May 1993"
.SH NAME
ed, red \- text editor
.SH SYNOPSIS
ed [-] [-sx] [-p \fIstring\fR] [\fIfile\fR]
.LP
red [-] [-sx] [-p \fIstring\fR] [\fIfile\fR]
.SH DESCRIPTION
.B ed
is a line-oriented text editor.
It is used to create, display, modify and otherwise manipulate text
files.
.B red
is a restricted
.BR ed :
it can only edit files in the current
directory and cannot execute shell commands.
If invoked with a
.I file
argument, then a copy of
.I file
is read into the editor's buffer.
Changes are made to this copy and not directly to
.I file
itself.
Upon quitting
.BR ed ,
any changes not explicitly saved with a
.I `w'
command are lost.
Editing is done in two distinct modes:
.I command
and
.IR input .
When first invoked,
.B ed
is in command mode.
In this mode commands are read from the standard input and
executed to manipulate the contents of the editor buffer.
A typical command might look like:
.sp
.RS
,s/\fIold\fR/\fInew\fR/g
.RE
.sp
which replaces all occurences of the string
.I old
with
.IR new .
When an input command, such as
.I `a'
(append),
.I `i'
(insert) or
.I `c'
(change), is given,
.B ed
enters input mode. This is the primary means
of adding text to a file.
In this mode, no commands are available;
instead, the standard input is written
directly to the editor buffer. Lines consist of text up to and
including a
.IR newline
character.
Input mode is terminated by
entering a single period (\fI.\fR) on a line.
All
.B ed
commands operate on whole lines or ranges of lines; e.g.,
the
.I `d'
command deletes lines; the
.I `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
.I `s'
command is applied to whole lines at a time.
In general,
.B ed
commands consist of zero or more line addresses, followed by a single
character command and possibly additional parameters; i.e.,
commands have the structure:
.sp
.RS
.I [address [,address]]command[parameters]
.RE
.sp
The address(es) indicate the line(s) to be affected by the command.
If fewer addresses are given than the command accepts, then default
addresses are supplied.
.SS OPTIONS
.TP 8
-s
Suppresses diagnostics. This should be used if
.BR ed 's
standard input is from a script.
.TP 8
-x
Prompts for an encryption key to be used in subsequent reads and writes
(see the
.I `x'
command).
.TP 8
.RI \-p \ string
Specifies a command prompt. This may be toggled on and off with the
.I `P'
command.
.TP 8
.I file
Specifies the name of a file to read. If
.I file
is prefixed with a
bang (!), then it is interpreted as a shell command. In this case,
what is read is
the standard output of
.I file
executed via
.IR sh (1).
To read a file whose name begins with a bang, prefix the
name with a backslash (\\).
The default filename is set to
.I file
only if it is not prefixed with a bang.
.SS LINE ADDRESSING
An address represents the number of line in the buffer.
.B ed
maintains a
.I current address
which is
typically supplied to commands as the default address when none is specified.
When a file is first read, the current address is set to the last line
of the file. In general, the current address is set to the last line
affected by a command.
A line address is
constructed from one of the bases in the list below, optionally followed
by a numeric offset. The offset may include any combination
of digits, operators (i.e.,
.IR + ,
.I -
and
.IR ^ )
and whitespace.
Addresses are read from left to right, and their values are computed
relative to the current address.
One exception to the rule that addresses represent line numbers is the
address
.I 0
(zero).
This means "before the first line,"
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
.IR n- tuple
of addresses is given where
.I n > 2,
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.
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
$
The last line in the buffer.
.TP 8
n
The
.IR n th,
line in the buffer
where
.I n
is a number in the range
.I [0,$].
.TP 8
- or ^
The previous line.
This is equivalent to
.I -1
and may be repeated with cumulative effect.
.TP 8
-\fIn\fR or ^\fIn\fR
The
.IR n th
previous line, where
.I n
is a non-negative number.
.TP 8
+
The
next line.
This is equivalent to
.I +1
and may be repeated with cumulative effect.
.TP 8
+\fIn\fR or whitespace\fIn\fR
The
.IR n th
next line, where
.I n
is a non-negative number.
.I whitespace
followed by a number
.I n
is interpreted as
.IR +n .
.TP 8
, \fRor\fB %
The first through last lines in the buffer. This is equivalent to
the address range
.I 1,$.
.TP 8
;
The
current through last lines in the buffer. This is equivalent to
the address range
.I .,$.
.TP 8
.RI / re/
The
next line containing the regular expression
.IR re .
The search wraps to the beginning of the buffer and continues down to the
current line, if necessary.
// repeats the last search.
.TP 8
.RI ? re?
The
previous line containing the regular expression
.IR re .
The search wraps to the end of the buffer and continues up to the
current line, if necessary.
?? repeats the last search.
.TP 8
.RI \' lc
The
line previously marked by a
.I `k'
(mark) command, where
.I lc
is a lower case letter.
.SS REGULAR EXPRESSIONS
Regular expressions are patterns used in selecting text.
For example, the
.B ed
command
.sp
.RS
g/\fIstring\fR/
.RE
.sp
prints all lines containing
.IR string .
Regular expressions are also
used by the
.I `s'
command for selecting old text to be replaced with new.
In addition to a specifying string literals, regular expressions can
represent
classes of strings. Strings thus represented are said to be matched
by the corresponding regular expression.
If it is possible for a regular expression
to match several strings in a line, then the left-most longest match is
the one selected.
The following symbols are used in constructing regular expressions:
.TP 8
c
Any character
.I c
not listed below, including `{', '}', `(', `)', `<' and `>',
matches itself.
.TP 8
\fR\\\fIc\fR
Any backslash-escaped character
.IR c ,
except for `{', '}', `(', `)', `<' and `>',
matches itself.
.TP 8
\fR.\fR
Matches any single character.
.TP 8
.I [char-class]
Matches any single character in
.IR char-class .
To include a `]'
in
.IR char-class ,
it must be the first character.
A range of characters may be specified by separating the end characters
of the range with a `-', e.g., `a-z' specifies the lower case characters.
The following literal expressions can also be used in
.I char-class
to specify sets of characters:
.sp
\ \ [:alnum:]\ \ [:cntrl:]\ \ [:lower:]\ \ [:space:]
.PD 0
\ \ [:alpha:]\ \ [:digit:]\ \ [:print:]\ \ [:upper:]
.PD 0
\ \ [:blank:]\ \ [:graph:]\ \ [:punct:]\ \ [:xdigit:]
.sp
If `-' appears as the first or last
character of
.IR char-class ,
then it matches itself.
All other characters in
.I char-class
match themselves.
.sp
Patterns in
.I char-class
of the form:
.sp
\ \ [.\fIcol-elm\fR.] or,
.PD 0
\ \ [=\fIcol-elm\fR=]
.sp
where
.I col-elm
is a
.I collating element
are interpreted according to
.IR locale (5)
(not currently supported).
See
.IR regex (3)
for an explanation of these constructs.
.TP 8
[^\fIchar-class\fR]
Matches any single character, other than newline, not in
.IR char-class .
.IR char-class
is defined
as above.
.TP 8
^
If `^' is the first character of a regular expression, then it
anchors the regular expression to the beginning of a line.
Otherwise, it matches itself.
.TP 8
$
If `$' is the last character of a regular expression, it
anchors the regular expression to the end of a line.
Otherwise, it matches itself.
.TP 8
\fR\\<\fR
Anchors the single character regular expression or subexpression
immediately following it to the beginning of a word.
(This may not be available)
.TP 8
\fR\\>\fR
Anchors the single character regular expression or subexpression
immediately following it to the end of a word.
(This may not be available)
.TP 8
\fR\\(\fIre\fR\\)\fR
Defines a subexpression
.IR re .
Subexpressions may be nested.
A subsequent backreference of the form \fI`\\n'\fR, where
.I n
is a number in the range [1,9], expands to the text matched by the
.IR n th
subexpression.
For example, the regular expression `\\(.*\\)\\1' matches any string
consisting of identical adjacent substrings.
Subexpressions are ordered relative to
their left delimiter.
.TP 8
*
Matches the single character regular expression or subexpression
immediately preceding it zero or more times. If '*' is the first
character of a regular expression or subexpression, then it matches
itself. The `*' operator sometimes yields unexpected results.
For example, the regular expression `b*' matches the beginning of
the string `abbb' (as opposed to the substring `bbb'), since a null match
is the only left-most match.
.TP 8
\fR\\{\fIn,m\fR\\}\fR or \fR\\{\fIn,\fR\\}\fR or \fR\\{\fIn\fR\\}\fR
Matches the single character regular expression or subexpression
immediately preceding it at least
.I n
and at most
.I m
times.
If
.I m
is omitted, then it matches at least
.I n
times.
If the comma is also omitted, then it matches exactly
.I n
times.
.LP
Additional regular expression operators may be defined depending on the
particular
.IR regex (3)
implementation.
.SS COMMANDS
All
.B ed
commands are single characters, though some require additonal parameters.
If a command's parameters extend over several lines, then
each line except for the last
must be terminated with a backslash (\\).
In general, at most one command is allowed per line.
However, most commands accept a print suffix, which is any of
.I `p'
(print),
.I `l'
(list) ,
or
.I `n'
(enumerate),
to print the last line affected by the command.
An interrupt (typically ^C) has the effect of aborting the current command
and returning the editor to command mode.
.B ed
recognizes the following commands. The commands are shown together with
the default address or address range supplied if none is
specified (in parenthesis).
.TP 8
(.)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.
.TP 8
(.,.)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.
.TP 8
(.,.)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.
.TP 8
.RI e \ file
Edits
.IR file ,
and sets the default filename.
If
.I 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.
.TP 8
.RI e \ !command
Edits the standard output of
.IR `!command' ,
executed as described below.
The default filename is unchanged.
Any lines in the buffer are deleted before the output of
.I command
is read.
The current address is set to the last line read.
.TP 8
.RI E \ file
Edits
.I file
unconditionally.
This is similar to the
.I e
command,
except that unwritten changes are discarded without warning.
The current address is set to the last line read.
.TP 8
.RI f \ file
Sets the default filename to
.IR file .
If
.I file
is not specified, then the default unescaped filename is printed.
.TP 8
.RI (1,$)g /re/command-list
Applies
.I command-list
to each of the addressed lines matching a regular expression
.IR re .
The current address is set to the
line currently matched before
.I command-list
is executed.
At the end of the
.I `g'
command, the current address is set to the last line affected by
.IR command-list .
Each command in
.I command-list
must be on a separate line,
and every line except for the last must be terminated by a backslash
(\\).
Any commands are allowed, except for
.IR `g' ,
.IR `G' ,
.IR `v' ,
and
.IR `V' .
A newline alone in
.I command-list
is equivalent to a
.I `p'
command.
.TP 8
.RI (1,$)G /re/
Interactively edits the addressed lines matching a regular expression
.IR re.
For each matching line,
the line is printed,
the current address is set,
and the user is prompted to enter a
.IR command-list .
At the end of the
.I `G'
command, the current address
is set to the last line affected by (the last)
.IR command-list .
The format of
.I command-list
is the same as that of the
.I `g'
command. A newline alone acts as a null command list.
A single `&' repeats the last non-null command list.
.TP 8
H
Toggles the printing of error explanations.
By default, explanations are not printed.
It is recommended that ed scripts begin with this command to
aid in debugging.
.TP 8
h
Prints an explanation of the last error.
.TP 8
(.)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.
.TP 8
(.,.+1)j
Joins the addressed lines. The addressed lines are
deleted from the buffer and replaced by a single
line containing their joined text.
The current address is set to the resultant line.
.TP 8
.RI (.)k lc
Marks a line with a lower case letter
.IR lc .
The line can then be addressed as
.I 'lc
(i.e., a single quote followed by
.I lc
) in subsequent commands. The mark is not cleared until the line is
deleted or otherwise modified.
.TP 8
(.,.)l
Prints the addressed lines unambiguously.
The current address is set to the last line
printed.
.TP 8
(.,.)m(.)
Moves lines in the buffer. The addressed lines are moved to after the
right-hand destination address, which may be the address
.IR 0
(zero).
The current address is set to the
last line moved.
.TP 8
(.,.)n
Prints the addressed lines along with
their line numbers. The current address is set to the last line
printed.
.TP 8
(.,.)p
Prints the addressed lines. The current address is set to the last line
printed.
.TP 8
P
Toggles the command prompt on and off.
Unless a prompt was specified by with command-line option
\fI-p string\fR, the command prompt is by default turned off.
.TP 8
q
Quits ed.
.TP 8
Q
Quits ed unconditionally.
This is similar to the
.I q
command,
except that unwritten changes are discarded without warning.
.TP 8
.RI ($)r \ file
Reads
.I file
to after the addressed line. If
.I 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
.IR file .
Otherwise, the default filename is unchanged.
The current address is set to the last line read.
.TP 8
.RI ($)r \ !command
Reads
to after the addressed line
the standard output of
.IR `!command' ,
executed as described below.
The default filename is unchanged.
The current address is set to the last line read.
.HP
.RI (.,.)s /re/replacement/
.PD 0
.HP
.RI (.,.)s /re/replacement/\fRg\fR
.HP
.RI (.,.)s /re/replacement/n
.br
Replaces text in the addressed lines
matching a regular expression
.I re
with
.IR replacement .
By default, only the first match in each line is replaced.
The
.I `g'
(global) suffix causes every match to be replaced.
The
.I `n'
suffix, where
.I n
is a postive number, causes only the
.IR n th
match to be replaced.
It is an error if no substitutions are performed on any of the addressed
lines.
The current address is set the last line affected.
.I re
and
.I replacement
may be delimited by any character other than space and newline.
If one or two of the last delimiters is omitted, then the last line
affected is printed as though the print suffix
.I `p'
were specified.
An unescaped `&' in
.I replacement
is replaced by the currently matched text.
The character sequence
\fI`\\m'\fR,
where
.I m
is a number in the range [1,9], is replaced by the
.IR m th
backreference expression of the matched text.
If
.I replacement
consists of a single `%', then
.I replacement
from the last substitution is used.
Newlines may be embedded in
.I replacement
if they are escaped with a backslash (\\).
.TP 8
(.,.)s
Repeats the last substitution.
This form of the
.I `s'
command may be suffixed with
any combination of the characters
.IR `r' ,
.IR `g' ,
and
.IR `p' .
The
.I `r'
suffix causes
the regular expression of the last search to be used instead of the
that of the last substitution.
The
.I `g'
suffix toggles the global suffix of the last substitution.
The
.I `p'
suffix toggles the print suffix of the last substitution
The current address is set to the last line affected.
.TP 8
(.,.)t(.)
Copies (i.e., transfers) the addressed lines to after the right-hand
destination address, which may be the address
.IR 0
(zero).
The current address is set to the last line
copied.
.TP 8
u
Undoes the last command and restores the current address
to what it was before the command.
The global commands
.IR `g' ,
.IR `G' ,
.IR `v' ,
and
.IR `V' .
are treated as a single command by undo.
.I `u'
is its own inverse.
.TP 8
.RI (1,$)v /pat/command-list
Applies
.I command-list
to each of the addressed lines not matching a regular expression
.IR re .
This is similar to the
.I `g'
command.
.TP 8
.RI (1,$)V /re/
Interactively edits the addressed lines not matching a regular expression
.IR re.
This is similar to the
.I `G'
command.
.TP 8
.RI (1,$)w \ file
Writes the addressed lines to
.IR file .
Any previous contents of
.I file
is lost without warning.
If there is no default filename, then the default filename is set to
.IR file,
otherwise it is unchanged. If no filename is specified, then the default
filename is used.
The current address is unchanged.
.TP 8
.RI (1,$)wq \ file
Writes the addressed lines to
.IR file ,
and then executes a
.I `q'
command.
.TP 8
.RI (1,$)w \ !command
Writes the addressed lines to the standard input of
.IR `!command' ,
executed as described below.
The default filename and current address are unchanged.
.TP 8
.RI (1,$)W \ file
Appends the addressed lines to the end of
.IR file .
This is similar to the
.I `w'
command, expect that the previous contents of file is not clobbered.
The current address is unchanged.
.TP 8
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 bdes(1) algorithm.
.TP 8
.RI (.+1)z n
Scrolls
.I n
lines at a time starting at addressed line. If
.I n
is not specified, then the current window size is used.
The current address is set to the last line printed.
.TP 8
.RI ! command
Executes
.I command
via
.IR sh (1).
If the first character of
.I command
is `!', then it is replaced by text of the
previous
.IR `!command' .
.B ed
does not process
.I command
for backslash (\\) escapes.
However, an unescaped
.I `%'
is replaced by the default filename.
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.
.TP 8
(.+1)newline
Prints the addressed line, and sets the current address to
that line.
.SH FILES
.TP 20
/tmp/ed.*
Buffer file
.PD 0
.TP 20
\fR./ed.hup\fR, $HOME/ed.hup
First and second files to which
.B ed
attempts to write the buffer if the terminal hangs up.
.SH SEE ALSO
.IR vi (1),
.IR sed (1),
.IR regex (3),
.IR bdes (1),
.IR sh (1).
USD:12-13
B. W. Kernighan and P. J. Plauger,
.I Software Tools in Pascal ,
Addison-Wesley, 1981.
.SH LIMITATIONS
.B ed
processes
.I file
arguments for backslash escapes, i.e., in a filename,
any characters preceded by a backslash (\\) are
interpreted literally.
If a text (non-binary) file is not terminated by a newline character,
then
.B ed
appends one on reading/writing it. In the case of a binary file,
.B ed
does not append a newline on reading/writing.
per line overhead: 4 ints
.SH DIAGNOSTICS
When an error occurs,
.B ed
prints a `?' 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
.I `h'
(help) command.
Since the
.I `g'
(global) command masks any errors from failed searches and substitutions,
it can be used to perform conditional operations in scripts; e.g.,
.sp
.RS
g/\fIold\fR/s//\fInew\fR/
.RE
.sp
replaces any occurrences of
.I old
with
.IR new .
If diagnostics are not disabled, attempting to quit
.B ed
or edit another file before writing a modified buffer
results in an error.
If the command is entered a second time, it succeeds,
but any changes to the buffer are lost.

263
bin/ed/ed.h Normal file
View File

@ -0,0 +1,263 @@
/* ed.h: type and constant definitions for the ed editor. */
/*
* Copyright (c) 1993 The Regents of the University of California.
* 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:
* 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.
*
* @(#)ed.h 5.5 (Berkeley) 3/28/93
*/
#include <unistd.h>
#include <errno.h>
#if defined(BSD) && BSD >= 199103 || defined(__386BSD__)
# include <sys/param.h> /* for MAXPATHLEN */
#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 (1 << (INTBITS - 1))
#define ERR (-2)
#define EMOD (-3)
#define FATAL (-4)
#ifndef MAXPATHLEN
# 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 */
typedef regex_t pattern_t;
/* Line node */
typedef struct line {
struct line *next;
struct line *prev;
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;
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
/* nextln: return line after l mod k */
#define nextln(l,k) ((l)+1 > (k) ? 0 : (l)+1)
/* nextln: return line before l mod k */
#define prevln(l,k) ((l)-1 < 0 ? (k) : (l)-1)
#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() \
if (--mutex == 0) { \
if (sigflags & (1 << SIGHUP)) dohup(SIGHUP); \
if (sigflags & (1 << SIGINT)) dointr(SIGINT); \
}
#if defined(sun) || defined(NO_REALLOC_NULL)
/* CKBUF: assure at least a minimum size for buffer b */
#define CKBUF(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 */
/* CKBUF: assure at least a minimum size for buffer b */
#define CKBUF(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 */
/* requeue: link pred before succ */
#define requeue(pred, succ) (pred)->next = (succ), (succ)->prev = (pred)
/* insqueue: insert elem in circular queue after pred */
#define insqueue(elem, pred) \
{ \
requeue((elem), (pred)->next); \
requeue((pred), elem); \
}
/* remqueue: remove elem from circular queue */
#define remqueue(elem) requeue((elem)->prev, (elem)->next);
/* nultonl: overwrite ASCII NULs with newlines */
#define nultonl(s, l) translit(s, l, '\0', '\n')
/* nltonul: overwrite newlines with ASCII NULs */
#define nltonul(s, l) translit(s, l, '\n', '\0')
#ifndef strerror
# define strerror(n) sys_errlist[n]
#endif
#ifndef __P
# ifndef __STDC__
# define __P(proto) ()
# else
# define __P(proto) proto
# 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 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 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));
char *translit __P((char *, int, int, int));
int move __P((long));
int oddesc __P((char *, char *));
void onhup __P((int));
void onintr __P((int));
pattern_t *optpat __P((void));
void putstr __P((char *, int, long, int));
char *puttxt __P((char *));
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));
int undo __P((void));
undo_t *upush __P((int, long, long));
void ureset __P((void));
extern char *sys_errlist[];
extern int mutex;
extern int sigflags;

156
bin/ed/re.c Normal file
View File

@ -0,0 +1,156 @@
/* 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.
* 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:
* 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.
*/
#ifndef lint
static char sccsid[] = "@(#)re.c 5.5 (Berkeley) 3/28/93";
#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] = "";
/* optpat: return pointer to compiled pattern from command buffer */
pattern_t *
optpat()
{
static pattern_t *exp = NULL;
char *exps;
char delim;
int n;
if ((delim = *ibufp) == '\n') {
if (!exp) sprintf(errmsg, "no previous pattern");
return exp;
} else if (delim == ' ' || *++ibufp == '\n') {
sprintf(errmsg, "invalid pattern delimiter");
return NULL;
} else if (*ibufp == delim) {
sprintf(errmsg, "no previous pattern");
return exp;
} else if ((exps = getlhs(delim)) == NULL)
return NULL;
/* buffer alloc'd && not reserved */
if (exp && !patlock)
regfree(exp);
else if ((exp = (pattern_t *) malloc(sizeof(pattern_t))) == NULL) {
fprintf(stderr, "%s\n", strerror(errno));
sprintf(errmsg, "out of memory");
return NULL;
}
patlock = 0;
#ifdef GNU_REGEX
/* initialize pattern buffer */
exp->buffer = NULL;
exp->allocated = 0L;
exp->fastmap = 0; /* not used by GNU regex after 0.12 */
exp->translate = 0;
#endif
if (n = regcomp(exp, exps, 0)) {
regerror(n, exp, errmsg, sizeof errmsg);
return NULL;
}
return exp;
}
extern int isbinary;
/* getlhs: copy a pattern string from the command buffer; return pointer
to the copy */
char *
getlhs(delim)
int delim;
{
char *nd;
int len;
for (nd = ibufp; *nd != delim && *nd != '\n'; nd++)
switch (*nd) {
default:
break;
case '[':
if ((nd = ccl(++nd)) == NULL) {
sprintf(errmsg, "unbalanced brackets ([])");
return NULL;
}
break;
case '\\':
if (*++nd == '\n') {
sprintf(errmsg, "trailing backslash (\\)");
return NULL;
}
break;
}
len = nd - ibufp;
CKBUF(lhbuf, lhbufsz, len + 1, NULL);
memcpy(lhbuf, ibufp, len);
lhbuf[len] = '\0';
ibufp = nd;
return (isbinary) ? nultonl(lhbuf, len) : lhbuf;
}
/* ccl: expand a POSIX character class */
char *
ccl(s)
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;
}

1
bin/ed/test/=.err Normal file
View File

@ -0,0 +1 @@
1,$=

17
bin/ed/test/Makefile Normal file
View File

@ -0,0 +1,17 @@
ED= ../obj/ed
all: build test
@echo done
build: mkscripts.sh
@echo building test scripts...
@chmod +x mkscripts.sh
@mkscripts.sh ${ED}
test: build ckscripts.sh
@echo running test scripts...
@chmod +x ckscripts.sh
@ckscripts.sh ${ED}
clean:
rm -f *.ed *.[oz] *~

41
bin/ed/test/README Normal file
View File

@ -0,0 +1,41 @@
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 ***
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
POSIX rule exit abnormally:
=-err.ed
a1-err.ed
i1-err.ed
k1-err.ed
r1-err.ed
In addition, one of !1-err.ed or !2.ed will fail, depending on whether or
not ed was compiled with the VI_BANG directive.

15
bin/ed/test/TODO Normal file
View File

@ -0,0 +1,15 @@
Some missing tests:
0) g/./s^@^@ - okay: NULs in commands
1) g/./s/^@/ - okay: NULs in patterns
2) a
hello^V^Jworld
. - okay: embedded newlines in insert mode
3) ed "" - error: invalid filename
4) red .. - error: restricted
5) red / - error: restricted
5) red !xx - error: restricted
6) ed -x - verify: 8-bit clean
7) ed - verify: long-line support
8) ed - verify: interactive/help mode
9) G/pat/ - verify: global interactive command
10) V/pat/ - verify: global interactive command

5
bin/ed/test/a.d Normal file
View File

@ -0,0 +1,5 @@
line 1
line 2
line 3
line 4
line5

8
bin/ed/test/a.r Normal file
View File

@ -0,0 +1,8 @@
hello world
line 1
hello world!
line 2
line 3
line 4
line5
hello world!!

9
bin/ed/test/a.t Normal file
View File

@ -0,0 +1,9 @@
0a
hello world
.
2a
hello world!
.
$a
hello world!!
.

3
bin/ed/test/a1.err Normal file
View File

@ -0,0 +1,3 @@
1,$a
hello world
.

3
bin/ed/test/a2.err Normal file
View File

@ -0,0 +1,3 @@
aa
hello world
.

1
bin/ed/test/addr1.err Normal file
View File

@ -0,0 +1 @@
100

1
bin/ed/test/addr2.err Normal file
View File

@ -0,0 +1 @@
-100

BIN
bin/ed/test/ascii.d Normal file

Binary file not shown.

BIN
bin/ed/test/ascii.r Normal file

Binary file not shown.

0
bin/ed/test/ascii.t Normal file
View File

5
bin/ed/test/c.d Normal file
View File

@ -0,0 +1,5 @@
line 1
line 2
line 3
line 4
line5

4
bin/ed/test/c.r Normal file
View File

@ -0,0 +1,4 @@
at the top
between top/middle
in the middle
at the bottom

12
bin/ed/test/c.t Normal file
View File

@ -0,0 +1,12 @@
1c
at the top
.
4c
in the middle
.
$c
at the bottom
.
2,3c
between top/middle
.

3
bin/ed/test/c1.err Normal file
View File

@ -0,0 +1,3 @@
cc
hello world
.

3
bin/ed/test/c2.err Normal file
View File

@ -0,0 +1,3 @@
0c
hello world
.

37
bin/ed/test/ckscripts.sh Normal file
View File

@ -0,0 +1,37 @@
#!/bin/sh -
# 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" = 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~
if $i; then
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=`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

5
bin/ed/test/d.d Normal file
View File

@ -0,0 +1,5 @@
line 1
line 2
line 3
line 4
line5

1
bin/ed/test/d.err Normal file
View File

@ -0,0 +1 @@
dd

1
bin/ed/test/d.r Normal file
View File

@ -0,0 +1 @@
line 2

3
bin/ed/test/d.t Normal file
View File

@ -0,0 +1,3 @@
1d
2;+1d
$d

1
bin/ed/test/e1.d Normal file
View File

@ -0,0 +1 @@
hello world

1
bin/ed/test/e1.err Normal file
View File

@ -0,0 +1 @@
ee e1.err

1
bin/ed/test/e1.r Normal file
View File

@ -0,0 +1 @@
E e1.t

1
bin/ed/test/e1.t Normal file
View File

@ -0,0 +1 @@
E e1.t

1
bin/ed/test/e2.d Normal file
View File

@ -0,0 +1 @@
E !echo hello world-

1
bin/ed/test/e2.err Normal file
View File

@ -0,0 +1 @@
.e e2.err

1
bin/ed/test/e2.r Normal file
View File

@ -0,0 +1 @@
hello world-

1
bin/ed/test/e2.t Normal file
View File

@ -0,0 +1 @@
E !echo hello world-

1
bin/ed/test/e3.d Normal file
View File

@ -0,0 +1 @@
E !echo hello world-

1
bin/ed/test/e3.err Normal file
View File

@ -0,0 +1 @@
ee.err

1
bin/ed/test/e3.r Normal file
View File

@ -0,0 +1 @@
E !echo hello world-

1
bin/ed/test/e3.t Normal file
View File

@ -0,0 +1 @@
E

1
bin/ed/test/e4.d Normal file
View File

@ -0,0 +1 @@
E !echo hello world-

1
bin/ed/test/e4.r Normal file
View File

@ -0,0 +1 @@
E !echo hello world-

1
bin/ed/test/e4.t Normal file
View File

@ -0,0 +1 @@
e

1
bin/ed/test/f1.err Normal file
View File

@ -0,0 +1 @@
.f f1.err

1
bin/ed/test/f2.err Normal file
View File

@ -0,0 +1 @@
ff1.err

5
bin/ed/test/g1.d Normal file
View File

@ -0,0 +1,5 @@
line 1
line 2
line 3
line 4
line5

1
bin/ed/test/g1.err Normal file
View File

@ -0,0 +1 @@
g/./s //x/

15
bin/ed/test/g1.r Normal file
View File

@ -0,0 +1,15 @@
line5
help! world
order
line 4
help! world
order
line 3
help! world
order
line 2
help! world
order
line 1
help! world
order

6
bin/ed/test/g1.t Normal file
View File

@ -0,0 +1,6 @@
g/./m0
g/./s/$/\
hello world
g/hello /s/lo/p!/\
a\
order

5
bin/ed/test/g2.d Normal file
View File

@ -0,0 +1,5 @@
line 1
line 2
line 3
line 4
line5

1
bin/ed/test/g2.err Normal file
View File

@ -0,0 +1 @@
g//s/./x/

1
bin/ed/test/g2.r Normal file
View File

@ -0,0 +1 @@
hello world

2
bin/ed/test/g2.t Normal file
View File

@ -0,0 +1,2 @@
g/[2-4]/-1,+1c\
hello world

1
bin/ed/test/g3.err Normal file
View File

@ -0,0 +1 @@
g

1
bin/ed/test/h.err Normal file
View File

@ -0,0 +1 @@
.h

5
bin/ed/test/i.d Normal file
View File

@ -0,0 +1,5 @@
line 1
line 2
line 3
line 4
line5

8
bin/ed/test/i.r Normal file
View File

@ -0,0 +1,8 @@
hello world
hello world!
line 1
line 2
line 3
line 4
hello world!!
line5

9
bin/ed/test/i.t Normal file
View File

@ -0,0 +1,9 @@
1i
hello world
.
2i
hello world!
.
$i
hello world!!
.

3
bin/ed/test/i1.err Normal file
View File

@ -0,0 +1,3 @@
1,$i
hello world
.

3
bin/ed/test/i2.err Normal file
View File

@ -0,0 +1,3 @@
ii
hello world
.

3
bin/ed/test/i3.err Normal file
View File

@ -0,0 +1,3 @@
0i
hello world
.

5
bin/ed/test/j.d Normal file
View File

@ -0,0 +1,5 @@
line 1
line 2
line 3
line 4
line5

4
bin/ed/test/j.r Normal file
View File

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

2
bin/ed/test/j.t Normal file
View File

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

5
bin/ed/test/k.d Normal file
View File

@ -0,0 +1,5 @@
line 1
line 2
line 3
line 4
line5

5
bin/ed/test/k.r Normal file
View File

@ -0,0 +1,5 @@
line 3
hello world
line 4
line5
line 2

10
bin/ed/test/k.t Normal file
View File

@ -0,0 +1,10 @@
2ka
1d
'am$
1ka
0a
hello world
.
'ad
u
'am0

1
bin/ed/test/k1.err Normal file
View File

@ -0,0 +1 @@
1,$ka

1
bin/ed/test/k2.err Normal file
View File

@ -0,0 +1 @@
kA

1
bin/ed/test/k3.err Normal file
View File

@ -0,0 +1 @@
0ka

6
bin/ed/test/k4.err Normal file
View File

@ -0,0 +1,6 @@
a
hello
.
.ka
'ad
'ap

0
bin/ed/test/l.d Normal file
View File

0
bin/ed/test/l.r Normal file
View File

0
bin/ed/test/l.t Normal file
View File

5
bin/ed/test/m.d Normal file
View File

@ -0,0 +1,5 @@
line 1
line 2
line 3
line 4
line5

4
bin/ed/test/m.err Normal file
View File

@ -0,0 +1,4 @@
a
hello world
.
1,$m1

5
bin/ed/test/m.r Normal file
View File

@ -0,0 +1,5 @@
line5
line 1
line 2
line 3
line 4

7
bin/ed/test/m.t Normal file
View File

@ -0,0 +1,7 @@
1,2m$
1,2m$
1,2m$
$m0
$m0
2,3m1
2,3m3

71
bin/ed/test/mkscripts.sh Normal file
View File

@ -0,0 +1,71 @@
#!/bin/sh -
# This script generates ed test scripts (.ed) from .t files
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
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
done

0
bin/ed/test/n.d Normal file
View File

0
bin/ed/test/n.r Normal file
View File

0
bin/ed/test/n.t Normal file
View File

0
bin/ed/test/p.d Normal file
View File

0
bin/ed/test/p.r Normal file
View File

0
bin/ed/test/p.t Normal file
View File

0
bin/ed/test/q.d Normal file
View File

0
bin/ed/test/q.r Normal file
View File

5
bin/ed/test/q.t Normal file
View File

@ -0,0 +1,5 @@
w q.o
a
hello
.
q

1
bin/ed/test/q1.err Normal file
View File

@ -0,0 +1 @@
.q

5
bin/ed/test/r1.d Normal file
View File

@ -0,0 +1,5 @@
line 1
line 2
line 3
line 4
line5

1
bin/ed/test/r1.err Normal file
View File

@ -0,0 +1 @@
1,$r r1.err

7
bin/ed/test/r1.r Normal file
View File

@ -0,0 +1,7 @@
line 1
hello world
line 2
line 3
line 4
line5
hello world

3
bin/ed/test/r1.t Normal file
View File

@ -0,0 +1,3 @@
1;r !echo hello world
1
r !echo hello world

5
bin/ed/test/r2.d Normal file
View File

@ -0,0 +1,5 @@
line 1
line 2
line 3
line 4
line5

1
bin/ed/test/r2.err Normal file
View File

@ -0,0 +1 @@
r a-good-book

10
bin/ed/test/r2.r Normal file
View File

@ -0,0 +1,10 @@
line 1
line 2
line 3
line 4
line5
line 1
line 2
line 3
line 4
line5

Some files were not shown because too many files have changed in this diff Show More