diff options
Diffstat (limited to 'bin/ed')
170 files changed, 5170 insertions, 0 deletions
diff --git a/bin/ed/Makefile b/bin/ed/Makefile new file mode 100644 index 000000000000..5a6a4ea4aee8 --- /dev/null +++ b/bin/ed/Makefile @@ -0,0 +1,16 @@ +PROG= ed +CFLAGS+=-DVI_BANG +SRCS= ed.c re.c buf.c cbc.c +LINKS= ${BINDIR}/ed ${BINDIR}/red +MLINKS= ed.1 red.1 + +.if exists(/usr/lib/libcrypt.a) +CFLAGS+=-DDES +LDADD+= -lcrypt +DPADD+= ${LIBCRYPT} +.endif + +LDADD+= -lgnuregex +DPADD+= /usr/lib/libgnuregex.a + +.include <bsd.prog.mk> diff --git a/bin/ed/POSIX b/bin/ed/POSIX new file mode 100644 index 000000000000..47a80b9e72a0 --- /dev/null +++ b/bin/ed/POSIX @@ -0,0 +1,62 @@ +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. + +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. + +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. diff --git a/bin/ed/README b/bin/ed/README new file mode 100644 index 000000000000..06e302d7b116 --- /dev/null +++ b/bin/ed/README @@ -0,0 +1,21 @@ +ed is an 8-bit-clean, POSIX-compliant line editor. It should work with +any regular expression package that conforms to the POSIX interface +standard, such as GNU regex(3). + +If reliable signals are supported (e.g., POSIX sigaction(2)), it should +compile with little trouble. Otherwise, the macros spl1() and spl0() +should be redefined to disable interrupts. + +The following compiler directives are recognized: +DES - 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. diff --git a/bin/ed/buf.c b/bin/ed/buf.c new file mode 100644 index 000000000000..7c92e5586269 --- /dev/null +++ b/bin/ed/buf.c @@ -0,0 +1,286 @@ +/* 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[]; + +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 */ + +/* 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++; + if (n && cp == &line0) { + sprintf(errmsg, "invalid address"); + return ERR; + } + return n; +} + + +/* 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); +} + + +unsigned char ctab[256]; /* character translation table */ + +/* init_buf: open scratch buffer; initialize line queue */ +void +init_buf() +{ + int i = 0; + + if (sbopen() < 0) + quit(2); + requeue(&line0, &line0); + for (i = 0; i < 256; i++) + ctab[i] = i; +} + + +/* translit: translate characters in a string */ +char * +translit(s, len, from, to) + char *s; + int len; + int from; + int to; +{ + static int i = 0; + + unsigned char *us; + + ctab[i] = i; /* restore table to initial state */ + ctab[i = from] = to; + for (us = (unsigned char *) s; len-- > 0; us++) + *us = ctab[*us]; + return s; +} diff --git a/bin/ed/cbc.c b/bin/ed/cbc.c new file mode 100644 index 000000000000..95ce63cd2967 --- /dev/null +++ b/bin/ed/cbc.c @@ -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 */ diff --git a/bin/ed/ed.1 b/bin/ed/ed.1 new file mode 100644 index 000000000000..e9a318080b39 --- /dev/null +++ b/bin/ed/ed.1 @@ -0,0 +1,984 @@ +.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 or range of lines 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\e\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\e<\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\e>\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\e(\fIre\fR\e)\fR +Defines a subexpression +.IR re . +Subexpressions may be nested. +A subsequent backreference of the form \fI`\en'\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 `\e(.*\e)\e1' 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\e{\fIn,m\fR\e}\fR or \fR\e{\fIn,\fR\e}\fR or \fR\e{\fIn\fR\e}\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`\em'\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 +ed.hup +The file 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 the +.I `u' +(undo) command occurs in a global command list, then +the command list is executed only once. + +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. diff --git a/bin/ed/ed.c b/bin/ed/ed.c new file mode 100644 index 000000000000..180232770d82 --- /dev/null +++ b/bin/ed/ed.c @@ -0,0 +1,2206 @@ +/* ed.c: This file contains the main control and user-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. + */ +/*- + * Kernighan/Plauger, "Software Tools in Pascal," (c) 1981 by + * Addison-Wesley Publishing Company, Inc. Reprinted with permission of + * the publisher. + */ + +#ifndef lint +char copyright1[] = +"@(#) Copyright (c) 1993 The Regents of the University of California.\n\ + All rights reserved.\n"; +char copyright2[] = +"@(#) Kernighan/Plauger, Software Tools in Pascal, (c) 1981 by\n\ + Addison-Wesley Publishing Company, Inc. Reprinted with permission of\n\ + the publisher.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)ed.c 5.5 (Berkeley) 3/28/93"; +#endif /* not lint */ + +/* + * CREDITS + * The buf.c algorithm is attributed to Rodney Ruddock of + * the University of Guelph, Guelph, Ontario. + * + * The cbc.c encryption code is adapted from + * the bdes program by Matt Bishop of Dartmouth College, + * Hanover, NH. + * + * Addison-Wesley Publishing Company generously granted + * permission to distribute this program over Internet. + * + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <setjmp.h> +#include <pwd.h> +#include <sys/ioctl.h> + +#include "ed.h" + +#ifdef _POSIX_SOURCE +sigjmp_buf env; +#else +jmp_buf env; +#endif + +/* static buffers */ +char *shcmd; /* shell command buffer */ +int shcmdsz; /* shell command buffer size */ +int shcmdi; /* shell command buffer index */ +char *cvbuf; /* global command buffer */ +int cvbufsz; /* global command buffer size */ +char *lhbuf; /* lhs buffer */ +int lhbufsz; /* lhs buffer size */ +char *rhbuf; /* rhs buffer */ +int rhbufsz; /* rhs buffer size */ +int rhbufi; /* rhs buffer index */ +char *rbuf; /* regsub buffer */ +int rbufsz; /* regsub buffer size */ +char *sbuf; /* file i/o buffer */ +int sbufsz; /* file i/o buffer size */ +char *ibuf; /* ed command-line buffer */ +int ibufsz; /* ed command-line buffer size */ +char *ibufp; /* pointer to ed command-line buffer */ + +/* global flags */ +int isbinary; /* if set, buffer contains ASCII NULs */ +int modified; /* if set, buffer modified since last write */ +int garrulous = 0; /* if set, print all error messages */ +int scripted = 0; /* if set, suppress diagnostics */ +int des = 0; /* if set, use crypt(3) for i/o */ +int mutex = 0; /* if set, signals set "sigflags" */ +int sigflags = 0; /* if set, signals received while mutex set */ +int sigactive = 0; /* if set, signal handlers are enabled */ +int red = 0; /* if set, restrict shell/directory access */ + +char dfn[MAXFNAME + 1] = ""; /* default filename */ +long curln; /* current address */ +long lastln; /* last address */ +int lineno; /* script line number */ +char *prompt; /* command-line prompt */ +char *dps = "*"; /* default command-line prompt */ + +char *usage = "usage: %s [-] [-sx] [-p string] [name]\n"; + +extern char errmsg[]; +extern int optind; +extern char *optarg; + +/* ed: line editor */ +main(argc, argv) + int argc; + char **argv; +{ + int c, n; + long status = 0; + + red = (n = strlen(argv[0])) > 2 && argv[0][n - 3] == 'r'; +top: + while ((c = getopt(argc, argv, "p:sx")) != EOF) + switch(c) { + case 'p': /* set prompt */ + prompt = optarg; + break; + case 's': /* run script */ + scripted = 1; + break; + case 'x': /* use crypt */ +#ifdef DES + des = getkey(); +#else + fprintf(stderr, "crypt unavailable\n?\n"); +#endif + break; + + default: + fprintf(stderr, usage, argv[0]); + exit(1); + } + argv += optind; + argc -= optind; + if (argc && **argv == '-') { + scripted = 1; + if (argc > 1) { + optind = 1; + goto top; + } + argv++; + argc--; + } + /* assert: reliable signals! */ +#ifdef SIGWINCH + dowinch(SIGWINCH); + if (isatty(0)) signal(SIGWINCH, dowinch); +#endif + signal(SIGHUP, onhup); + signal(SIGQUIT, SIG_IGN); + signal(SIGINT, onintr); +#ifdef _POSIX_SOURCE + if (status = sigsetjmp(env, 1)) +#else + if (status = setjmp(env)) +#endif + { + fputs("\n?\n", stderr); + sprintf(errmsg, "interrupt"); + } else { + init_buf(); + sigactive = 1; /* enable signal handlers */ + if (argc && **argv && ckfn(*argv)) { + if (doread(0, *argv) < 0 && !isatty(0)) + quit(2); + else if (**argv != '!') + strcpy(dfn, *argv); + } else if (argc) { + fputs("?\n", stderr); + if (**argv == '\0') + sprintf(errmsg, "invalid filename"); + if (!isatty(0)) + quit(2); + } + } + for (;;) { + if (status < 0 && garrulous) + fprintf(stderr, "%s\n", errmsg); + if (prompt) { + printf("%s", prompt); + fflush(stdout); + } + if ((n = getline()) < 0) { + status = ERR; + continue; + } else if (n == 0) { + if (modified && !scripted) { + fputs("?\n", stderr); + sprintf(errmsg, "warning: file modified"); + if (!isatty(0)) { + fprintf(stderr, garrulous ? "script, line %d: %s\n" + : "", lineno, errmsg); + quit(2); + } + clearerr(stdin); + modified = 0; + status = EMOD; + continue; + } else + quit(0); + } else if (ibuf[n - 1] != '\n') { + /* discard line */ + sprintf(errmsg, "unexpected end-of-file"); + clearerr(stdin); + status = ERR; + continue; + } + if ((n = getlist()) >= 0 && (status = ckglob()) != 0) { + if (status > 0 && (status = doglob(status)) >= 0) { + curln = status; + continue; + } + } else if ((status = n) >= 0 && (status = docmd(0)) >= 0) { + if (!status || status + && (status = doprint(curln, curln, status)) >= 0) + continue; + } + switch (status) { + case EOF: + quit(0); + case EMOD: + modified = 0; + fputs("?\n", stderr); /* give warning */ + sprintf(errmsg, "warning: file modified"); + if (!isatty(0)) { + fprintf(stderr, garrulous ? "script, line %d: %s\n" : "", lineno, errmsg); + quit(2); + } + break; + case FATAL: + if (!isatty(0)) + fprintf(stderr, garrulous ? "script, line %d: %s\n" : "", lineno, errmsg); + else + fprintf(stderr, garrulous ? "%s\n" : "", errmsg); + quit(3); + default: + fputs("?\n", stderr); + if (!isatty(0)) { + fprintf(stderr, garrulous ? "script, line %d: %s\n" : "", lineno, errmsg); + quit(2); + } + break; + } + } + /*NOTREACHED*/ +} + + +long line1, line2, nlines; + +/* getlist: get line numbers from the command buffer until an illegal + address is seen. return range status */ +getlist() +{ + long num; + + nlines = line2 = 0; + while ((num = getone()) >= 0) { + line1 = line2; + line2 = num; + nlines++; + if (*ibufp != ',' && *ibufp != ';') + break; + else if (*ibufp++ == ';') + curln = num; + } + nlines = min(nlines, 2); + if (nlines == 0) + line2 = curln; + if (nlines <= 1) + line1 = line2; + return (num == ERR) ? ERR : nlines; +} + + +/* getone: return the next line number in the command buffer */ +long +getone() +{ + int c; + long i, num; + + if ((num = getnum(1)) < 0) + return num; + for (;;) { + c = isspace(*ibufp); + skipblanks(); + c = c && isdigit(*ibufp); + if (!c && *ibufp != '+' && *ibufp != '-' && *ibufp != '^') + break; + c = c ? '+' : *ibufp++; + if ((i = getnum(0)) < 0) { + sprintf(errmsg, "invalid address"); + return i; + } + if (c == '+') + num += i; + else num -= i; + } + if (num > lastln || num < 0) { + sprintf(errmsg, "invalid address"); + return ERR; + } + return num; +} + + +/* getnum: return a relative line number from the command buffer */ +long +getnum(first) + int first; +{ + pattern_t *pat; + char c; + + skipblanks(); + if (isdigit(*ibufp)) + return strtol(ibufp, &ibufp, 10); + switch(c = *ibufp) { + case '.': + ibufp++; + return first ? curln : ERR; + case '$': + ibufp++; + return first ? lastln : ERR; + case '/': + case '?': + if ((pat = optpat()) == NULL) + return ERR; + else if (*ibufp == c) + ibufp++; + return first ? patscan(pat, (c == '/') ? 1 : 0) : ERR; + case '^': + case '-': + case '+': + return first ? curln : 1; + case '\'': + ibufp++; + return first ? getmark(*ibufp++) : ERR; + case '%': + case ',': + case ';': + if (first) { + ibufp++; + line2 = (c == ';') ? curln : 1; + nlines++; + return lastln; + } + return 1; + default: + return first ? EOF : 1; + } +} + + +/* 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 */ + + +/* VRFYCMD: verify the command suffix in the command buffer */ +#define VRFYCMD() { \ + int done = 0; \ + do { \ + switch(*ibufp) { \ + case 'p': \ + gflag |= GPR, ibufp++; \ + break; \ + case 'l': \ + gflag |= GLS, ibufp++; \ + break; \ + case 'n': \ + gflag |= GNP, ibufp++; \ + break; \ + default: \ + done++; \ + } \ + } while (!done); \ + if (*ibufp++ != '\n') { \ + sprintf(errmsg, "invalid command suffix"); \ + return ERR; \ + } \ +} + + +/* ckglob: set lines matching a pattern in the command buffer; return + global status */ +ckglob() +{ + pattern_t *pat; + char c, delim; + char *s; + int nomatch; + long n; + line_t *lp; + int gflag = 0; /* print suffix of interactive cmd */ + + if ((c = *ibufp) == 'V' || c == 'G') + gflag = GLB; + else if (c != 'g' && c != 'v') + return 0; + if (ckrange(1, lastln) < 0) + return ERR; + else if ((delim = *++ibufp) == ' ' || delim == '\n') { + sprintf(errmsg, "invalid pattern delimiter"); + return ERR; + } else if ((pat = optpat()) == NULL) + return ERR; + else if (*ibufp == delim) + ibufp++; + if (gflag) + VRFYCMD(); /* get print suffix */ + for (lp = getlp(n = 1); n <= lastln; n++, lp = lp->next) { + if ((s = gettxt(lp)) == NULL) + return ERR; + lp->len &= ~ACTV; /* zero ACTV bit */ + if (isbinary) + s = nultonl(s, lp->len & ~ACTV); + if (line1 <= n && n <= line2 + && (!(nomatch = regexec(pat, s, 0, NULL, 0)) + && (c == 'g' || c == 'G') + || nomatch && (c == 'v' || c == 'V'))) + lp->len |= ACTV; + } + return gflag | GSG; +} + + +/* doglob: apply command list in the command buffer to the active + lines in a range; return command status */ +long +doglob(gflag) + int gflag; +{ + static char *ocmd = NULL; + static int ocmdsz = 0; + + line_t *lp = NULL; + long lc; + int status; + int n; + int interact = gflag & ~GSG; /* GLB & gflag ? */ + char *cmd = NULL; + +#ifdef BACKWARDS + if (!interact) + if (!strcmp(ibufp, "\n")) + cmd = "p\n"; /* null cmd-list == `p' */ + else if ((cmd = getcmdv(&n, 0)) == NULL) + return ERR; +#else + if (!interact && (cmd = getcmdv(&n, 0)) == NULL) + return ERR; +#endif + ureset(); + for (;;) { + for (lp = getlp(lc = 1); lc <= lastln; lc++, lp = lp->next) + if (lp->len & ACTV) /* active line */ + break; + if (lc > lastln) + break; + lp->len ^= ACTV; /* zero ACTV bit */ + curln = lc; + if (interact) { + /* print curln and get a command in global syntax */ + if (doprint(curln, curln, 0) < 0) + return ERR; + while ((n = getline()) > 0 + && ibuf[n - 1] != '\n') + clearerr(stdin); + if (n < 0) + return ERR; + else if (n == 0) { + sprintf(errmsg, "unexpected end-of-file"); + return ERR; + } else if (n == 1 && !strcmp(ibuf, "\n")) + continue; + else if (n == 2 && !strcmp(ibuf, "&\n")) { + if (cmd == NULL) { + sprintf(errmsg, "no previous command"); + return ERR; + } else cmd = ocmd; + } else if ((cmd = getcmdv(&n, 0)) == NULL) + return ERR; + else { + CKBUF(ocmd, ocmdsz, n + 1, ERR); + memcpy(ocmd, cmd, n + 1); + cmd = ocmd; + } + + } + ibufp = cmd; + for (; *ibufp;) + if ((status = getlist()) < 0 + || (status = docmd(1)) < 0 + || (status > 0 + && (status = doprint(curln, curln, status)) < 0)) + return status; + } + return ((interact & ~GLB ) && doprint(curln, curln, interact) < 0) ? ERR : curln; +} + + +#ifdef BACKWARDS +/* GETLINE3: get a legal address from the command buffer */ +#define GETLINE3(num) \ +{ \ + long ol1, ol2; \ +\ + ol1 = line1, ol2 = line2; \ + if (getlist() < 0) \ + return ERR; \ + else if (nlines == 0) { \ + sprintf(errmsg, "destination expected"); \ + return ERR; \ + } else if (line2 < 0 || lastln < line2) { \ + sprintf(errmsg, "invalid address"); \ + return ERR; \ + } \ + num = line2; \ + line1 = ol1, line2 = ol2; \ +} +#else /* BACKWARDS */ +/* GETLINE3: get a legal address from the command buffer */ +#define GETLINE3(num) \ +{ \ + long ol1, ol2; \ +\ + ol1 = line1, ol2 = line2; \ + if (getlist() < 0) \ + return ERR; \ + if (line2 < 0 || lastln < line2) { \ + sprintf(errmsg, "invalid address"); \ + return ERR; \ + } \ + num = line2; \ + line1 = ol1, line2 = ol2; \ +} +#endif + +/* sgflags */ +#define SGG 001 /* complement previous global substitute suffix */ +#define SGP 002 /* complement previous print suffix */ +#define SGR 004 /* use last regex instead of last pat */ +#define SGF 010 /* newline found */ + +long ucurln = -1; /* if >= 0, undo enabled */ +long ulastln = -1; /* if >= 0, undo enabled */ +int patlock = 0; /* if set, pattern not released by optpat() */ + +long rows = 22; /* scroll length: ws_row - 2 */ + +/* docmd: execute the next command in command buffer; return print + request, if any */ +docmd(glob) + int glob; +{ + static pattern_t *pat = NULL; + static int sgflag = 0; + + pattern_t *tpat; + char *fnp; + int gflag = 0; + int sflags = 0; + long num = 0; + int n = 0; + int c; + + skipblanks(); + switch(c = *ibufp++) { + case 'a': + VRFYCMD(); + if (!glob) ureset(); + if (append(line2, glob) < 0) + return ERR; + break; + case 'c': + if (ckrange(curln, curln) < 0) + return ERR; + VRFYCMD(); + if (!glob) ureset(); + if (lndelete(line1, line2) < 0 || append(curln, glob) < 0) + return ERR; + break; + case 'd': + if (ckrange(curln, curln) < 0) + return ERR; + VRFYCMD(); + if (!glob) ureset(); + if (lndelete(line1, line2) < 0) + return ERR; + else if (nextln(curln, lastln) != 0) + curln = nextln(curln, lastln); + modified = 1; + break; + case 'e': + if (modified && !scripted) + return EMOD; + /* fall through */ + case 'E': + if (nlines > 0) { + sprintf(errmsg, "unexpected address"); + return ERR; + } else if (!isspace(*ibufp)) { + sprintf(errmsg, "unexpected command suffix"); + return ERR; + } else if ((fnp = getfn()) == NULL) + return ERR; + VRFYCMD(); + if (lndelete(1, lastln) < 0) + return ERR; + ureset(); + if (sbclose() < 0) + return ERR; + else if (sbopen() < 0) + return FATAL; + if (*fnp && *fnp != '!') strcpy(dfn, fnp); +#ifdef BACKWARDS + if (*fnp == '\0' && *dfn == '\0') { + sprintf(errmsg, "no current filename"); + return ERR; + } +#endif + if (doread(0, *fnp ? fnp : dfn) < 0) + return ERR; + ureset(); + modified = 0; + ucurln = ulastln = -1; + break; + case 'f': + if (nlines > 0) { + sprintf(errmsg, "unexpected address"); + return ERR; + } else if (!isspace(*ibufp)) { + sprintf(errmsg, "unexpected command suffix"); + return ERR; + } else if ((fnp = getfn()) == NULL) + return ERR; + else if (*fnp == '!') { + sprintf(errmsg, "invalid redirection"); + return ERR; + } + VRFYCMD(); + if (*fnp) strcpy(dfn, fnp); + printf("%s\n", esctos(dfn)); + break; + case 'g': + case 'G': + sprintf(errmsg, "cannot nest global commands"); + return ERR; + case 'h': + if (nlines > 0) { + sprintf(errmsg, "unexpected address"); + return ERR; + } + VRFYCMD(); + if (*errmsg) fprintf(stderr, "%s\n", errmsg); + break; + case 'H': + if (nlines > 0) { + sprintf(errmsg, "unexpected address"); + return ERR; + } + VRFYCMD(); + if ((garrulous = 1 - garrulous) && *errmsg) + fprintf(stderr, "%s\n", errmsg); + break; + case 'i': + if (line2 == 0) { + sprintf(errmsg, "invalid address"); + return ERR; + } + VRFYCMD(); + if (!glob) ureset(); + if (append(prevln(line2, lastln), glob) < 0) + return ERR; + break; + case 'j': + if (ckrange(curln, curln + 1) < 0) + return ERR; + VRFYCMD(); + if (!glob) ureset(); + if (line1 != line2 && join(line1, line2) < 0) + return ERR; + break; + case 'k': + c = *ibufp++; + if (line2 == 0) { + sprintf(errmsg, "invalid address"); + return ERR; + } + VRFYCMD(); + if (putmark(c, getlp(line2)) < 0) + return ERR; + break; + case 'l': + if (ckrange(curln, curln) < 0) + return ERR; + VRFYCMD(); + if (doprint(line1, line2, gflag | GLS) < 0) + return ERR; + gflag = 0; + break; + case 'm': + if (ckrange(curln, curln) < 0) + return ERR; + GETLINE3(num); + if (line1 <= num && num < line2) { + sprintf(errmsg, "invalid destination"); + return ERR; + } + VRFYCMD(); + if (!glob) ureset(); + if (move(num, glob) < 0) + return ERR; + else + modified = 1; + break; + case 'n': + if (ckrange(curln, curln) < 0) + return ERR; + VRFYCMD(); + if (doprint(line1, line2, gflag | GNP) < 0) + return ERR; + gflag = 0; + break; + case 'p': + if (ckrange(curln, curln) < 0) + return ERR; + VRFYCMD(); + if (doprint(line1, line2, gflag | GPR) < 0) + return ERR; + gflag = 0; + break; + case 'P': + if (nlines > 0) { + sprintf(errmsg, "unexpected address"); + return ERR; + } + VRFYCMD(); + prompt = prompt ? NULL : optarg ? optarg : dps; + break; + case 'q': + case 'Q': + if (nlines > 0) { + sprintf(errmsg, "unexpected address"); + return ERR; + } + VRFYCMD(); + gflag = (modified && !scripted && c == 'q') ? EMOD : EOF; + break; + case 'r': + if (!isspace(*ibufp)) { + sprintf(errmsg, "unexpected command suffix"); + return ERR; + } else if (nlines == 0) + line2 = lastln; + if ((fnp = getfn()) == NULL) + return ERR; + VRFYCMD(); + if (!glob) ureset(); + if (*dfn == '\0' && *fnp != '!') strcpy(dfn, fnp); +#ifdef BACKWARDS + if (*fnp == '\0' && *dfn == '\0') { + sprintf(errmsg, "no current filename"); + return ERR; + } +#endif + if ((num = doread(line2, *fnp ? fnp : dfn)) < 0) + return ERR; + else if (num && num != lastln) + modified = 1; + break; + case 's': + do { + switch(*ibufp) { + case '\n': + sflags |=SGF; + break; + case 'g': + sflags |= SGG; + ibufp++; + break; + case 'p': + sflags |= SGP; + ibufp++; + break; + case 'r': + sflags |= SGR; + ibufp++; + break; + default: + if (sflags) { + sprintf(errmsg, "invalid command suffix"); + return ERR; + } + } + } while (sflags && *ibufp != '\n'); + if (sflags && !pat) { + sprintf(errmsg, "no previous substitution"); + return ERR; + } else if (!(sflags & SGF)) + sgflag &= 0xff; + if (*ibufp != '\n' && *(ibufp + 1) == '\n') { + sprintf(errmsg, "invalid pattern delimiter"); + return ERR; + } + tpat = pat; + spl1(); + if ((!sflags || (sflags & SGR)) + && (tpat = optpat()) == NULL) + return ERR; + else if (tpat != pat) { + if (pat) { + regfree(pat); + free(pat); + } + pat = tpat; + patlock = 1; /* reserve pattern */ + } else if (pat == NULL) { + /* NOTREACHED */ + sprintf(errmsg, "no previous substitution"); + return ERR; + } + spl0(); + if (!sflags && (sgflag = getrhs(glob)) < 0) + return ERR; + else if (glob) + sgflag |= GLB; + else + sgflag &= ~GLB; + if (sflags & SGG) + sgflag ^= GSG; + if (sflags & SGP) + sgflag ^= GPR, sgflag &= ~(GLS | GNP); + do { + switch(*ibufp) { + case 'p': + sgflag |= GPR, ibufp++; + break; + case 'l': + sgflag |= GLS, ibufp++; + break; + case 'n': + sgflag |= GNP, ibufp++; + break; + default: + n++; + } + } while (!n); + if (ckrange(curln, curln) < 0) + return ERR; + VRFYCMD(); + if (!glob) ureset(); + if ((n = subst(pat, sgflag)) < 0) + return ERR; + else if (n) + modified = 1; + break; + case 't': + if (ckrange(curln, curln) < 0) + return ERR; + GETLINE3(num); + VRFYCMD(); + if (!glob) ureset(); + if (transfer(num) < 0) + return ERR; + modified = 1; + break; + case 'u': + if (nlines > 0) { + sprintf(errmsg, "unexpected address"); + return ERR; + } + VRFYCMD(); + if (undo(glob) < 0) + return ERR; + break; + case 'v': + case 'V': + sprintf(errmsg, "cannot nest global commands"); + return ERR; + case 'w': + case 'W': + if ((n = *ibufp) == 'q' || n == 'Q') { + gflag = EOF; + ibufp++; + } + if (!isspace(*ibufp)) { + sprintf(errmsg, "unexpected command suffix"); + return ERR; + } else if ((fnp = getfn()) == NULL) + return ERR; + if (nlines == 0 && !lastln) + line1 = line2 = 0; + else if (ckrange(1, lastln) < 0) + return ERR; + VRFYCMD(); + if (*dfn == '\0' && *fnp != '!') strcpy(dfn, fnp); +#ifdef BACKWARDS + if (*fnp == '\0' && *dfn == '\0') { + sprintf(errmsg, "no current filename"); + return ERR; + } +#endif + if ((num = dowrite(line1, line2, *fnp ? fnp : dfn, (c == 'W') ? "a" : "w")) < 0) + return ERR; + else if (num == lastln) + modified = 0; + else if (modified && !scripted && n == 'q') + gflag = EMOD; + break; + case 'x': + if (nlines > 0) { + sprintf(errmsg, "unexpected address"); + return ERR; + } + VRFYCMD(); +#ifdef DES + des = getkey(); +#else + sprintf(errmsg, "crypt unavailable"); + return ERR; +#endif + break; + case 'z': +#ifdef BACKWARDS + if (ckrange(line1 = 1, curln + 1) < 0) +#else + if (ckrange(line1 = 1, curln + !glob) < 0) +#endif + return ERR; + else if ('0' < *ibufp && *ibufp <= '9') + rows = strtol(ibufp, &ibufp, 10); + VRFYCMD(); + if (doprint(line2, min(lastln, line2 + rows - 1), gflag) < 0) + return ERR; + gflag = 0; + break; + case '=': + VRFYCMD(); + printf("%d\n", nlines ? line2 : lastln); + break; + case '!': +#ifndef VI_BANG + if (nlines > 0) { + sprintf(errmsg, "unexpected address"); + return ERR; + } +#endif + if ((sflags = getshcmd()) < 0) + return ERR; + VRFYCMD(); + if (sflags) printf("%s\n", shcmd + 1); +#ifdef VI_BANG + if (nlines == 0) { +#endif + system(shcmd + 1); + if (!scripted) printf("!\n"); + break; +#ifdef VI_BANG + } + if (!lastln && !line1 && !line2) { + if (!glob) ureset(); + } else if (ckrange(curln, curln) < 0) + return ERR; + else { + if (!glob) ureset(); + if (lndelete(line1, line2) < 0) + return ERR; + line2 = curln; + modified = 1; + } + if ((num = doread(line2, shcmd)) < 0) + return ERR; + else if (num && num != lastln) + modified = 1; + break; +#endif + case '\n': +#ifdef BACKWARDS + if (ckrange(line1 = 1, curln + 1) < 0 +#else + if (ckrange(line1 = 1, curln + !glob) < 0 +#endif + || doprint(line2, line2, 0) < 0) + return ERR; + break; + default: + sprintf(errmsg, "unknown command"); + return ERR; + } + return gflag; +} + + +/* ckrange: return status of line number range check */ +ckrange(def1, def2) + long def1, def2; +{ + if (nlines == 0) { + line1 = def1; + line2 = def2; + } + if (line1 > line2 || 1 > line1 || line2 > lastln) { + sprintf(errmsg, "invalid address"); + return ERR; + } + return 0; +} + + +/* patscan: return the number of the next line matching a pattern in a + given direction. wrap around begin/end of line queue if necessary */ +long +patscan(pat, dir) + pattern_t *pat; + int dir; +{ + char *s; + long n = curln; + line_t *lp; + + do { + if (n = dir ? nextln(n, lastln) : prevln(n, lastln)) { + if ((s = gettxt(lp = getlp(n))) == NULL) + return ERR; + if (isbinary) + s = nultonl(s, lp->len & ~ACTV); + if (!regexec(pat, s, 0, NULL, 0)) + return n; + } + } while (n != curln); + sprintf(errmsg, "no match"); + return ERR; +} + + +/* getfn: return pointer to copy of filename in the command buffer */ +char * +getfn() +{ + static char *file = NULL; + static int filesz = 0; + + int n; + + if (*ibufp != '\n') { + skipblanks(); + if (*ibufp == '\n') { + sprintf(errmsg, "invalid filename"); + return NULL; + } else if ((ibufp = getcmdv(&n, 1)) == NULL) + return NULL; +#ifdef VI_BANG + else if (*ibufp == '!') { + ibufp++; + if ((n = getshcmd()) < 0) + return NULL; + if (n) printf("%s\n", shcmd + 1); + return shcmd; + } +#endif + else if (n - 1 > MAXFNAME) { + sprintf(errmsg, "filename too long"); + return NULL; + } + } +#ifndef BACKWARDS + else if (*dfn == '\0') { + sprintf(errmsg, "no current filename"); + return NULL; + } +#endif + CKBUF(file, filesz, MAXFNAME + 1, NULL); + for (n = 0; *ibufp != '\n';) + file[n++] = *ibufp++; + file[n] = '\0'; + return ckfn(file); +} + + +/* getrhs: extract substitution template from the command buffer */ +getrhs(glob) + int glob; +{ + char delim; + + if ((delim = *ibufp) == '\n') { + rhbufi = 0; + return GPR; + } else if (makesub(glob) == NULL) + return ERR; + else if (*ibufp == '\n') + return GPR; + else if (*ibufp == delim) + ibufp++; + if ('1' <= *ibufp && *ibufp <= '9') + return (int) strtol(ibufp, &ibufp, 10) << 8; + else if (*ibufp == 'g') { + ibufp++; + return GSG; + } + return 0; +} + + +/* makesub: return pointer to copy of substitution template in the command + buffer */ +char * +makesub(glob) + int glob; +{ + int n = 0; + int i = 0; + char delim = *ibufp++; + char c; + + if (*ibufp == '%' && *(ibufp + 1) == delim) { + ibufp++; + if (!rhbuf) sprintf(errmsg, "no previous substitution"); + return rhbuf; + } + while (*ibufp != delim) { + CKBUF(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 (!glob) { + while ((n = getline()) == 0 + || n > 0 && ibuf[n - 1] != '\n') + clearerr(stdin); + if (n < 0) + return NULL; + } else + /*NOTREACHED*/ + ; + } + CKBUF(rhbuf, rhbufsz, i + 1, NULL); + rhbuf[rhbufi = i] = '\0'; + return rhbuf; +} + + +/* getshcmd: read a shell command up a maximum size from stdin; return + substitution status */ +int +getshcmd() +{ + static char *buf = NULL; + static int n = 0; + + char *s; /* substitution char pointer */ + int i = 0; + int j = 0; + + if (red) { + sprintf(errmsg, "shell access restricted"); + return ERR; + } else if ((s = ibufp = getcmdv(&j, 1)) == NULL) + return ERR; + CKBUF(buf, n, j + 1, ERR); + buf[i++] = '!'; /* prefix command w/ bang */ + while (*ibufp != '\n') + switch (*ibufp) { + default: + CKBUF(buf, n, i + 2, ERR); + buf[i++] = *ibufp; + if (*ibufp++ == '\\') + buf[i++] = *ibufp++; + break; + case '!': + if (s != ibufp) { + CKBUF(buf, n, i + 1, ERR); + buf[i++] = *ibufp++; + } +#ifdef BACKWARDS + else if (shcmd == NULL || *(shcmd + 1) == '\0') +#else + else if (shcmd == NULL) +#endif + { + sprintf(errmsg, "no previous command"); + return ERR; + } else { + CKBUF(buf, n, i + shcmdi, ERR); + for (s = shcmd + 1; s < shcmd + shcmdi;) + buf[i++] = *s++; + s = ibufp++; + } + break; + case '%': + if (*dfn == '\0') { + sprintf(errmsg, "no current filename"); + return ERR; + } + j = strlen(s = esctos(dfn)); + CKBUF(buf, n, i + j, ERR); + while (j--) + buf[i++] = *s++; + s = ibufp++; + break; + } + CKBUF(shcmd, shcmdsz, i + 1, ERR); + memcpy(shcmd, buf, i); + shcmd[shcmdi = i] = '\0'; + return *s == '!' || *s == '%'; +} + + +/* append: insert text from stdin to after line n; stop when either a + single period is read or EOF; return status */ +append(n, glob) + long n; + int glob; +{ + int l; + char *lp = ibuf; + char *eot; + undo_t *up = NULL; + + for (curln = n;;) { + if (!glob) { + if ((l = getline()) < 0) + return ERR; + else if (l == 0 || ibuf[l - 1] != '\n') { + clearerr(stdin); + return l ? EOF : 0; + } + lp = ibuf; + } else if (*(lp = ibufp) == '\0') + return 0; + else { + while (*ibufp++ != '\n') + ; + l = ibufp - lp; + } + if (l == 2 && lp[0] == '.' && lp[1] == '\n') { + return 0; + } + eot = lp + l; + spl1(); + do { + if ((lp = puttxt(lp)) == NULL) { + spl0(); + return ERR; + } else if (up) + up->t = getlp(curln); + else if ((up = upush(UADD, curln, curln)) == NULL) { + spl0(); + return ERR; + } + } while (lp != eot); + spl0(); + modified = 1; + } +} + + +/* subst: change all text matching a pattern in a range of lines according to + a substitution template; return status */ +subst(pat, gflag) + pattern_t *pat; + int gflag; +{ + undo_t *up; + char *txt; + char *eot; + long lc; + int nsubs = 0; + line_t *lp; + int len; + + curln = prevln(line1, lastln); + for (lc = 0; lc <= line2 - line1; lc++) { + lp = getlp(curln = nextln(curln, lastln)); + if ((len = regsub(pat, lp, gflag)) < 0) + return ERR; + else if (len) { + up = NULL; + if (lndelete(curln, curln) < 0) + return ERR; + txt = rbuf; + eot = rbuf + len; + spl1(); + do { + if ((txt = puttxt(txt)) == NULL) { + spl0(); + return ERR; + } else if (up) + up->t = getlp(curln); + else if ((up = upush(UADD, curln, curln)) == 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)) + && doprint(curln, curln, gflag) < 0) + return ERR; + return 1; +} + + +/* regsub: replace text matched by a pattern according to a substitution + template; return pointer to the modified text */ +regsub(pat, lp, gflag) + pattern_t *pat; + line_t *lp; + int gflag; +{ + int off = 0; + int kth = gflag >> 8; /* substitute kth match only */ + int chngd = 0; + int matchno = 0; + int len; + int i = 0; + regmatch_t rm[SE_MAX]; + char *txt; + char *eot; + + if ((txt = gettxt(lp)) == NULL) + return ERR; + len = lp->len & ~ACTV; + eot = txt + len; + if (isbinary) txt = nultonl(txt, len); + if (!regexec(pat, txt, SE_MAX, rm, 0)) { + do { + if (!kth || kth == ++matchno) { + chngd++; + i = rm[0].rm_so; + CKBUF(rbuf, rbufsz, off + i, ERR); + if (isbinary) txt = nltonul(txt, rm[0].rm_eo); + memcpy(rbuf + off, txt, i); + if ((off = catsub(txt, rm, off += i)) < 0) + return ERR; + } else { + i = rm[0].rm_eo; + CKBUF(rbuf, rbufsz, off + i, ERR); + if (isbinary) txt = nltonul(txt, i); + memcpy(rbuf + off, txt, i); + off += i; + } + txt += rm[0].rm_eo; + } while (*txt && (!chngd || (gflag & GSG) && rm[0].rm_eo) + && !regexec(pat, txt, SE_MAX, rm, REG_NOTBOL)); + i = eot - txt; + CKBUF(rbuf, rbufsz, off + i + 2, ERR); + if (i > 0 && !rm[0].rm_eo && (gflag & GSG)) { + sprintf(errmsg, "infinite substitution loop"); + return ERR; + } + if (isbinary) txt = nltonul(txt, i); + memcpy(rbuf + off, txt, i); + memcpy(rbuf + off + i, "\n", 2); + } + return chngd ? off + i + 1 : 0; +} + + +/* join: replace a range of lines with the joined text of those lines */ +join(from, to) + long from; + long to; +{ + static char *buf = NULL; + static int n; + + char *s; + int len = 0; + int size = 0; + line_t *bp, *ep; + + ep = getlp(nextln(to, lastln)); + for (bp = getlp(from); bp != ep; bp = bp->next, size += len) { + if ((s = gettxt(bp)) == NULL) + return ERR; + len = bp->len & ~ACTV; + CKBUF(buf, n, size + len, ERR); + memcpy(buf + size, s, len); + } + CKBUF(buf, n, size + 2, ERR); + memcpy(buf + size, "\n", 2); + if (lndelete(from, to) < 0) + return ERR; + curln = from - 1; + spl1(); + if (puttxt(buf) == NULL + || upush(UADD, curln, curln) == NULL) { + spl0(); + return ERR; + } + spl0(); + modified = 1; + return 0; +} + + +/* move: move a range of lines */ +move(num, glob) + long num; + int glob; +{ + line_t *b1, *a1, *b2, *a2, *lp; + long n = nextln(line2, lastln); + long p = prevln(line1, lastln); + int done = (num == line1 - 1 || num == line2); + + spl1(); + if (done) { + a2 = getlp(n); + b2 = getlp(p); + curln = line2; + } else if (upush(UMOV, p, n) == NULL + || upush(UMOV, num, nextln(num, lastln)) == NULL) { + spl0(); + return ERR; + } else { + a1 = getlp(n); + if (num < line1) + b1 = getlp(p), b2 = getlp(num); /* this getlp last! */ + else b2 = getlp(num), b1 = getlp(p); /* this getlp last! */ + a2 = b2->next; + requeue(b2, b1->next); + requeue(a1->prev, a2); + requeue(b1, a1); + curln = num + ((num < line1) ? line2 - line1 + 1 : 0); + } + if (glob) + for (lp = b2->next; lp != a2; lp = lp->next) + lp->len &= ~ACTV; /* zero ACTV bit */ + spl0(); + return 0; +} + + +/* transfer: copy a range of lines; return status */ +transfer(num) + long num; +{ + line_t *lp; + long nl, nt, lc; + long mid = (num < line2) ? num : line2; + undo_t *up = NULL; + + curln = num; + for (nt = 0, nl = line1; nl <= mid; nl++, nt++) { + spl1(); + if ((lp = lpdup(getlp(nl))) == NULL) { + spl0(); + return ERR; + } + lpqueue(lp); + if (up) + up->t = lp; + else if ((up = upush(UADD, curln, curln)) == NULL) { + spl0(); + return ERR; + } + spl0(); + } + for (nl += nt, lc = line2 + nt; nl <= lc; nl += 2, lc++) { + spl1(); + if ((lp = lpdup(getlp(nl))) == NULL) { + spl0(); + return ERR; + } + lpqueue(lp); + if (up) + up->t = lp; + else if ((up = upush(UADD, curln, curln)) == NULL) { + spl0(); + return ERR; + } + spl0(); + } + return 0; +} + + +/* lndelete: delete a range of lines */ +lndelete(from, to) + long from, to; +{ + line_t *before, *after; + + spl1(); + if (upush(UDEL, from, to) == NULL) { + spl0(); + return ERR; + } + after = getlp(nextln(to, lastln)); + before = getlp(prevln(from, lastln)); /* this getlp last! */ + requeue(before, after); + lastln -= to - from + 1; + curln = prevln(from, lastln); + spl0(); + return 0; +} + + +/* catsub: modify text according to a substitution template; + return offset to end of modified text */ +catsub(boln, rm, off) + char *boln; + regmatch_t *rm; + int off; +{ + int j = 0; + int k = 0; + char *sub = rhbuf; + + for (; sub - rhbuf < rhbufi; sub++) + if (*sub == '&') { + j = rm[0].rm_so; + k = rm[0].rm_eo; + CKBUF(rbuf, rbufsz, off + k - j, ERR); + while (j < k) + rbuf[off++] = boln[j++]; + } else if (*sub == '\\' && '1' <= *++sub && *sub <= '9' + && rm[*sub - '0'].rm_so >= 0 + && rm[*sub - '0'].rm_eo >= 0) { + j = rm[*sub - '0'].rm_so; + k = rm[*sub - '0'].rm_eo; + CKBUF(rbuf, rbufsz, off + k - j, ERR); + while (j < k) + rbuf[off++] = boln[j++]; + } else { + CKBUF(rbuf, rbufsz, off + 1, ERR); + rbuf[off++] = *sub; + } + CKBUF(rbuf, rbufsz, off + 1, ERR); + rbuf[off] = '\0'; + return off; +} + +/* doprint: print a range of lines to stdout */ +doprint(from, to, gflag) + long from; + long to; + int gflag; +{ + line_t *bp; + line_t *ep; + char *s; + + if (!from) { + sprintf(errmsg, "invalid address"); + return ERR; + } + ep = getlp(nextln(to, lastln)); + for (bp = getlp(from); bp != ep; bp = bp->next) { + if ((s = gettxt(bp)) == NULL) + return ERR; + putstr(s, bp->len & ~ACTV, curln = from++, gflag); + } + return 0; +} + + +int cols = 72; /* wrap column: ws_col - 8 */ + +/* putstr: print text to stdout */ +void +putstr(s, l, n, gflag) + char *s; + int l; + long n; + int gflag; +{ + int col = 0; + + if (gflag & GNP) { + printf("%ld\t", n); + col = 8; + } + for (; l--; s++) { + if ((gflag & GLS) && ++col > cols) { + fputs("\\\n", stdout); + col = 1; + } + if (gflag & GLS) { + switch (*s) { + case '\b': + fputs("\\b", stdout); + break; + case '\f': + fputs("\\f", stdout); + break; + case '\n': + fputs("\\n", stdout); + break; + case '\r': + fputs("\\r", stdout); + break; + case '\t': + fputs("\\t", stdout); + break; + case '\v': + fputs("\\v", stdout); + break; + default: + if (*s < 32 || 126 < *s) { + putchar('\\'); + putchar((((unsigned char) *s & 0300) >> 6) + '0'); + putchar((((unsigned char) *s & 070) >> 3) + '0'); + putchar(((unsigned char) *s & 07) + '0'); + col += 2; + } else if (*s == '\\') + fputs("\\\\", stdout); + else { + putchar(*s); + col--; + } + } + col++; + } else + putchar(*s); + } +#ifndef BACKWARDS + if (gflag & GLS) + putchar('$'); +#endif + putchar('\n'); +} + + +int newline_added; /* set if newline appended to input file */ + +/* doread: read a text file into the editor buffer; return line count */ +long +doread(n, fn) + long n; + char *fn; +{ + FILE *fp; + line_t *lp = getlp(n); + unsigned long size = 0; + undo_t *up = NULL; + int len; + + isbinary = newline_added = 0; + if ((fp = (*fn == '!') ? popen(fn + 1, "r") : fopen(esctos(fn), "r")) == NULL) { + fprintf(stderr, "%s: %s\n", fn, strerror(errno)); + sprintf(errmsg, "cannot open input file"); + return ERR; + } else if (des) + desinit(); + for (curln = n; (len = sgetline(fp)) > 0; size += len) { + spl1(); + if (puttxt(sbuf) == NULL) { + spl0(); + return ERR; + } + lp = lp->next; + if (up) + up->t = lp; + else if ((up = upush(UADD, curln, curln)) == NULL) { + spl0(); + return ERR; + } + spl0(); + } + if (((*fn == '!') ? pclose(fp) : fclose(fp)) < 0) { + fprintf(stderr, "%s: %s\n", fn, strerror(errno)); + sprintf(errmsg, "cannot close input file"); + return ERR; + } + if (newline_added && !isbinary) + fputs("newline appended\n", stderr); + if (des) size += 8 - size % 8; + fprintf(stderr, !scripted ? "%lu\n" : "", size); + return (len < 0) ? ERR : curln - n; +} + + +/* dowrite: write the text of a range of lines to a file; return line count */ +long +dowrite(n, m, fn, mode) + long n; + long m; + char *fn; + char *mode; +{ + FILE *fp; + line_t *lp; + unsigned long size = 0; + long lc = n ? m - n + 1 : 0; + char *s = NULL; + int len; + int ct; + + if ((fp = ((*fn == '!') ? popen(fn + 1, "w") : fopen(esctos(fn), mode))) == NULL) { + fprintf(stderr, "%s: %s\n", fn, strerror(errno)); + sprintf(errmsg, "cannot open output file"); + return ERR; + } else if (des) + desinit(); + if (n && !des) + for (lp = getlp(n); n <= m; n++, lp = lp->next) { + if ((s = gettxt(lp)) == NULL) + return ERR; + len = lp->len & ~ACTV; + if (n != lastln || !isbinary || !newline_added) + s[len++] = '\n'; + if ((ct = fwrite(s, sizeof(char), len, fp)) < 0 || ct != len) { + fprintf(stderr, "%s: %s\n", fn, strerror(errno)); + sprintf(errmsg, "cannot write file"); + return ERR; + } + size += len; + } + else if (n) + for (lp = getlp(n); n <= m; n++, lp = lp->next) { + if ((s = gettxt(lp)) == NULL) + return ERR; + len = lp->len & ~ACTV; + while (len--) { + if (desputc(*s++, fp) == EOF && ferror(fp)) { + fprintf(stderr, "%s: %s\n", fn, strerror(errno)); + sprintf(errmsg, "cannot write file"); + return ERR; + } + } + if (n != lastln || !isbinary || !newline_added) { + if (desputc('\n', fp) < 0) { + fprintf(stderr, "%s: %s\n", fn, strerror(errno)); + sprintf(errmsg, "cannot write file"); + return ERR; + } + size++; /* for '\n' */ + } + size += (lp->len & ~ACTV); + } + if (des) { + desflush(fp); /* flush buffer */ + size += 8 - size % 8; /* adjust DES size */ + } + 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 lc; +} + + +#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 */ + +/* upush: return pointer to intialized undo node */ +undo_t * +upush(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 = getlp(to); + ustack[u_p].h = getlp(from); + return ustack + u_p++; + } + /* out of memory - release undo stack */ + fprintf(stderr, "%s\n", strerror(errno)); + sprintf(errmsg, "out of memory"); + ureset(); + 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; \ +} + + +/* undo: undo last change to the editor buffer */ +undo(glob) + int glob; +{ + long n; + long ocurln = curln; + long olastln = lastln; + line_t *lp, *np; + + if (ucurln == -1 || ulastln == -1) { + sprintf(errmsg, "nothing to undo"); + return ERR; + } else if (u_p) + modified = 1; + getlp(0); /* this getlp last! */ + spl1(); + for (n = u_p; n-- > 0;) { + switch(ustack[n].type) { + case UADD: + requeue(ustack[n].h->prev, ustack[n].t->next); + break; + case UDEL: + requeue(ustack[n].h->prev, ustack[n].h); + requeue(ustack[n].t, ustack[n].t->next); + break; + case UMOV: + case VMOV: + requeue(ustack[n - 1].h, ustack[n].h->next); + requeue(ustack[n].t->prev, ustack[n - 1].t); + requeue(ustack[n].h, ustack[n].t); + n--; + break; + default: + /*NOTREACHED*/ + ; + } + ustack[n].type ^= 1; + } + /* reverse undo order */ + for (n = u_p; n-- > (u_p + 1)/ 2;) + USWAP(ustack[n], ustack[u_p - 1 - n]); + if (glob) + for (lp = np = getlp(0); (lp = lp->next) != np;) + lp->len &= ~ACTV; /* zero ACTV bit */ + curln = ucurln, ucurln = ocurln; + lastln = ulastln, ulastln = olastln; + spl0(); + return 0; +} + + +/* ureset: clear the undo stack */ +void +ureset() +{ + line_t *lp, *ep, *tl; + + while (u_p--) + if (ustack[u_p].type == UDEL) { + ep = ustack[u_p].t->next; + for (lp = ustack[u_p].h; lp != ep; lp = tl) { + clrmark(lp); + tl = lp->next; + free(lp); + } + } + u_p = 0; + ucurln = curln; + ulastln = lastln; +} + + +#define MAXMARK 26 /* max number of marks */ + +line_t *mark[MAXMARK]; /* line markers */ +int markno; /* line marker count */ + +/* getmark: return address of a marked line */ +long +getmark(n) + int n; +{ + if (!islower(n)) { + sprintf(errmsg, "invalid mark character"); + return ERR; + } + return getaddr(mark[n - 'a']); +} + + +/* putmark: set a line node mark */ +int +putmark(n, lp) + int n; + line_t *lp; +{ + if (!islower(n)) { + sprintf(errmsg, "invalid mark character"); + return ERR; + } else if (mark[n - 'a'] == NULL) + markno++; + mark[n - 'a'] = lp; + return 0; +} + + +/* clrmark: clear line node marks */ +void +clrmark(lp) + line_t *lp; +{ + int i; + + if (markno) + for (i = 0; i < MAXMARK; i++) + if (mark[i] == lp) { + mark[i] = NULL; + markno--; + } +} + + +/* sgetline: read a line of text up a maximum size from a file; return + line length */ +sgetline(fp) + FILE *fp; +{ + register int c; + register int i = 0; + + while (((c = des ? desgetc(fp) : getc(fp)) != EOF || !feof(fp) && !ferror(fp)) && c != '\n') { + CKBUF(sbuf, sbufsz, i + 1, ERR); + if (!(sbuf[i++] = c)) isbinary = 1; + } + CKBUF(sbuf, sbufsz, i + 2, ERR); + if (c == '\n') + sbuf[i++] = c; + else if (feof(fp) && i) { + sbuf[i++] = '\n'; + newline_added = 1; + } else if (ferror(fp)) { + fprintf(stderr, "%s\n", strerror(errno)); + sprintf(errmsg, "cannot read input file"); + return ERR; + } + sbuf[i] = '\0'; + return (isbinary && newline_added && i) ? --i : i; +} + + +/* getline: read a line of text up a maximum size from stdin; return + line length */ +getline() +{ + register int i = 0; + register int oi = 0; + char c; + + /* Read 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 */ + for (;;) + switch (read(0, &c, 1)) { + default: + oi = 0; + CKBUF(ibuf, ibufsz, i + 2, ERR); + if (!(ibuf[i++] = c)) isbinary = 1; + if (c != '\n') + continue; + lineno++; /* script line no. */ + ibuf[i] = '\0'; + ibufp = ibuf; + return i; + case 0: + if (i != oi) { + oi = i; + continue; + } else if (i) + ibuf[i] = '\0'; + ibufp = ibuf; + return i; + case -1: + fprintf(stderr, "%s\n", strerror(errno)); + sprintf(errmsg, "cannot read standard input"); + clearerr(stdin); + ibufp = NULL; + return ERR; + } +} + + +/* getcmdv: get a command vector */ +char * +getcmdv(sizep, nonl) + int *sizep; + int nonl; +{ + int l, n; + char *t = ibufp; + + while (*t++ != '\n') + ; + if ((l = t - ibufp) < 2 || !oddesc(ibufp, ibufp + l - 1)) { + *sizep = l; + return ibufp; + } + *sizep = -1; + CKBUF(cvbuf, cvbufsz, l, NULL); + memcpy(cvbuf, ibufp, l); + *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */ + if (nonl) l--; /* strip newline */ + for (;;) { + if ((n = getline()) < 0) + return NULL; + else if (n == 0 || ibuf[n - 1] != '\n') { + sprintf(errmsg, "unexpected end-of-file"); + return NULL; + } + CKBUF(cvbuf, cvbufsz, l + n, NULL); + memcpy(cvbuf + l, ibuf, n); + l += n; + if (n < 2 || !oddesc(cvbuf, cvbuf + l - 1)) + break; + *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */ + if (nonl) l--; /* strip newline */ + } + CKBUF(cvbuf, cvbufsz, l + 1, NULL); + cvbuf[l] = '\0'; + *sizep = l; + return cvbuf; +} + + +/* lpdup: return a pointer to a copy of a line node */ +line_t * +lpdup(lp) + line_t *lp; +{ + line_t *np; + + if ((np = (line_t *) malloc(sizeof(line_t))) == NULL) { + fprintf(stderr, "%s\n", strerror(errno)); + sprintf(errmsg, "out of memory"); + return NULL; + } + np->seek = lp->seek; + np->len = (lp->len & ~ACTV); /* zero ACTV bit */ + return np; +} + + +/* oddesc: return the parity of escapes preceding a character in a + string */ +oddesc(s, t) + char *s; + char *t; +{ + return (s == t || *(t - 1) != '\\') ? 0 : !oddesc(s, t - 1); +} + + +/* esctos: return copy of escaped string */ +char * +esctos(s) + char *s; +{ + static char *file = NULL; + static int filesz = 0; + + int i = 0; + + CKBUF(file, filesz, MAXFNAME + 1, NULL); + /* assert: no trailing escape */ + while (file[i++] = (*s == '\\') ? *++s : *s) + s++; + return file; +} + + +void +onhup(signo) + int signo; +{ + if (mutex) + sigflags |= (1 << signo); + else dohup(signo); +} + + +void +onintr(signo) + int signo; +{ + if (mutex) + sigflags |= (1 << signo); + else dointr(signo); +} + + +void +dohup(signo) + int signo; +{ + char *hup = NULL; /* hup filename */ + char *s; + int n; + + if (!sigactive) + quit(1); + sigflags &= ~(1 << signo); + if (lastln && dowrite(1, lastln, "ed.hup", "w") < 0 + && (s = getenv("HOME")) != NULL + && (n = strlen(s)) + 8 <= MAXFNAME /* "ed.hup" + '/' */ + && (hup = (char *) malloc(n + 10)) != NULL) { + strcpy(hup, s); + if (hup[n - 1] != '/') + hup[n] = '/', hup[n+1] = '\0'; + strcat(hup, "ed.hup"); + dowrite(1, lastln, hup, "w"); + } + quit(2); +} + + +void +dointr(signo) + int signo; +{ + if (!sigactive) + quit(1); + sigflags &= ~(1 << signo); +#ifdef _POSIX_SOURCE + siglongjmp(env, -1); +#else + longjmp(env, -1); +#endif +} + + +struct winsize ws; /* window size structure */ + +void +dowinch(signo) + int signo; +{ + sigflags &= ~(1 << signo); + if (ioctl(0, TIOCGWINSZ, (char *) &ws) >= 0) { + if (ws.ws_row > 2) rows = ws.ws_row - 2; + if (ws.ws_col > 8) cols = ws.ws_col - 8; + } +} + + +/* ckfn: return a legal filename */ +char * +ckfn(s) + char *s; +{ + if (red && (*s == '!' || !strcmp(s, "..") || strchr(s, '/'))) { + sprintf(errmsg, "shell access restricted"); + return NULL; + } + return s; +} diff --git a/bin/ed/ed.h b/bin/ed/ed.h new file mode 100644 index 000000000000..b3905370f949 --- /dev/null +++ b/bin/ed/ed.h @@ -0,0 +1,266 @@ +/* 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 (unsigned) (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 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 *)); +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)); + + +extern char *sys_errlist[]; +extern int mutex; +extern int sigflags; diff --git a/bin/ed/re.c b/bin/ed/re.c new file mode 100644 index 000000000000..7553d5f2e332 --- /dev/null +++ b/bin/ed/re.c @@ -0,0 +1,147 @@ +/* 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) == ' ') { + sprintf(errmsg, "invalid pattern delimiter"); + return NULL; + } else if (delim == '\n' || *++ibufp == '\n' || *ibufp == delim) { + if (!exp) 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; + if (n = regcomp(exp, exps, 0)) { + regerror(n, exp, errmsg, sizeof errmsg); + free(exp); + return exp = 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; +} diff --git a/bin/ed/test/=.err b/bin/ed/test/=.err new file mode 100644 index 000000000000..6a6055955b16 --- /dev/null +++ b/bin/ed/test/=.err @@ -0,0 +1 @@ +1,$= diff --git a/bin/ed/test/Makefile b/bin/ed/test/Makefile new file mode 100644 index 000000000000..ca45a5183ebe --- /dev/null +++ b/bin/ed/test/Makefile @@ -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] *~ diff --git a/bin/ed/test/README b/bin/ed/test/README new file mode 100644 index 000000000000..8917f36acec8 --- /dev/null +++ b/bin/ed/test/README @@ -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 bang1-err.ed or bang2.ed will fail, depending on whether or +not ed was compiled with the VI_BANG directive. diff --git a/bin/ed/test/TODO b/bin/ed/test/TODO new file mode 100644 index 000000000000..7a4b74fb7419 --- /dev/null +++ b/bin/ed/test/TODO @@ -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 diff --git a/bin/ed/test/a.d b/bin/ed/test/a.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/a.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/a.r b/bin/ed/test/a.r new file mode 100644 index 000000000000..26257bd3b3c2 --- /dev/null +++ b/bin/ed/test/a.r @@ -0,0 +1,8 @@ +hello world +line 1 +hello world! +line 2 +line 3 +line 4 +line5 +hello world!! diff --git a/bin/ed/test/a.t b/bin/ed/test/a.t new file mode 100644 index 000000000000..ac98c40d085f --- /dev/null +++ b/bin/ed/test/a.t @@ -0,0 +1,9 @@ +0a +hello world +. +2a +hello world! +. +$a +hello world!! +. diff --git a/bin/ed/test/a1.err b/bin/ed/test/a1.err new file mode 100644 index 000000000000..e80815ff50d9 --- /dev/null +++ b/bin/ed/test/a1.err @@ -0,0 +1,3 @@ +1,$a +hello world +. diff --git a/bin/ed/test/a2.err b/bin/ed/test/a2.err new file mode 100644 index 000000000000..ec4b00b40c4c --- /dev/null +++ b/bin/ed/test/a2.err @@ -0,0 +1,3 @@ +aa +hello world +. diff --git a/bin/ed/test/addr1.err b/bin/ed/test/addr1.err new file mode 100644 index 000000000000..29d6383b52c1 --- /dev/null +++ b/bin/ed/test/addr1.err @@ -0,0 +1 @@ +100 diff --git a/bin/ed/test/addr2.err b/bin/ed/test/addr2.err new file mode 100644 index 000000000000..e96acb9254be --- /dev/null +++ b/bin/ed/test/addr2.err @@ -0,0 +1 @@ +-100 diff --git a/bin/ed/test/ascii.d b/bin/ed/test/ascii.d Binary files differnew file mode 100644 index 000000000000..c86626638e0b --- /dev/null +++ b/bin/ed/test/ascii.d diff --git a/bin/ed/test/ascii.r b/bin/ed/test/ascii.r Binary files differnew file mode 100644 index 000000000000..c86626638e0b --- /dev/null +++ b/bin/ed/test/ascii.r diff --git a/bin/ed/test/ascii.t b/bin/ed/test/ascii.t new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/bin/ed/test/ascii.t diff --git a/bin/ed/test/bang1.d b/bin/ed/test/bang1.d new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/bin/ed/test/bang1.d diff --git a/bin/ed/test/bang1.err b/bin/ed/test/bang1.err new file mode 100644 index 000000000000..630af9011c9e --- /dev/null +++ b/bin/ed/test/bang1.err @@ -0,0 +1 @@ +.!date diff --git a/bin/ed/test/bang1.r b/bin/ed/test/bang1.r new file mode 100644 index 000000000000..dcf02b2fb6bb --- /dev/null +++ b/bin/ed/test/bang1.r @@ -0,0 +1 @@ +okay diff --git a/bin/ed/test/bang1.t b/bin/ed/test/bang1.t new file mode 100644 index 000000000000..d7b1fea1f7fc --- /dev/null +++ b/bin/ed/test/bang1.t @@ -0,0 +1,5 @@ +!read one +hello, world +a +okay +. diff --git a/bin/ed/test/bang2.d b/bin/ed/test/bang2.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/bang2.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/bang2.err b/bin/ed/test/bang2.err new file mode 100644 index 000000000000..79d895682220 --- /dev/null +++ b/bin/ed/test/bang2.err @@ -0,0 +1 @@ +!! diff --git a/bin/ed/test/bang2.r b/bin/ed/test/bang2.r new file mode 100644 index 000000000000..65f58a262a1b --- /dev/null +++ b/bin/ed/test/bang2.r @@ -0,0 +1,4 @@ +line 1 +hello world! +line 4 +hello world diff --git a/bin/ed/test/bang2.t b/bin/ed/test/bang2.t new file mode 100644 index 000000000000..14c681d44ade --- /dev/null +++ b/bin/ed/test/bang2.t @@ -0,0 +1,2 @@ +.!echo hello world +2,3!echo hello world! diff --git a/bin/ed/test/c.d b/bin/ed/test/c.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/c.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/c.r b/bin/ed/test/c.r new file mode 100644 index 000000000000..0fb3e4fffc0d --- /dev/null +++ b/bin/ed/test/c.r @@ -0,0 +1,4 @@ +at the top +between top/middle +in the middle +at the bottom diff --git a/bin/ed/test/c.t b/bin/ed/test/c.t new file mode 100644 index 000000000000..ebdd536f8171 --- /dev/null +++ b/bin/ed/test/c.t @@ -0,0 +1,12 @@ +1c +at the top +. +4c +in the middle +. +$c +at the bottom +. +2,3c +between top/middle +. diff --git a/bin/ed/test/c1.err b/bin/ed/test/c1.err new file mode 100644 index 000000000000..658ec38b4649 --- /dev/null +++ b/bin/ed/test/c1.err @@ -0,0 +1,3 @@ +cc +hello world +. diff --git a/bin/ed/test/c2.err b/bin/ed/test/c2.err new file mode 100644 index 000000000000..24b322776a66 --- /dev/null +++ b/bin/ed/test/c2.err @@ -0,0 +1,3 @@ +0c +hello world +. diff --git a/bin/ed/test/ckscripts.sh b/bin/ed/test/ckscripts.sh new file mode 100644 index 000000000000..87a7e9beb560 --- /dev/null +++ b/bin/ed/test/ckscripts.sh @@ -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 diff --git a/bin/ed/test/d.d b/bin/ed/test/d.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/d.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/d.err b/bin/ed/test/d.err new file mode 100644 index 000000000000..f03f6945fbf9 --- /dev/null +++ b/bin/ed/test/d.err @@ -0,0 +1 @@ +dd diff --git a/bin/ed/test/d.r b/bin/ed/test/d.r new file mode 100644 index 000000000000..b7e242c00cda --- /dev/null +++ b/bin/ed/test/d.r @@ -0,0 +1 @@ +line 2 diff --git a/bin/ed/test/d.t b/bin/ed/test/d.t new file mode 100644 index 000000000000..c7c473febdf7 --- /dev/null +++ b/bin/ed/test/d.t @@ -0,0 +1,3 @@ +1d +2;+1d +$d diff --git a/bin/ed/test/e1.d b/bin/ed/test/e1.d new file mode 100644 index 000000000000..3b18e512dba7 --- /dev/null +++ b/bin/ed/test/e1.d @@ -0,0 +1 @@ +hello world diff --git a/bin/ed/test/e1.err b/bin/ed/test/e1.err new file mode 100644 index 000000000000..827cc292b6bb --- /dev/null +++ b/bin/ed/test/e1.err @@ -0,0 +1 @@ +ee e1.err diff --git a/bin/ed/test/e1.r b/bin/ed/test/e1.r new file mode 100644 index 000000000000..e656728bab21 --- /dev/null +++ b/bin/ed/test/e1.r @@ -0,0 +1 @@ +E e1.t diff --git a/bin/ed/test/e1.t b/bin/ed/test/e1.t new file mode 100644 index 000000000000..e656728bab21 --- /dev/null +++ b/bin/ed/test/e1.t @@ -0,0 +1 @@ +E e1.t diff --git a/bin/ed/test/e2.d b/bin/ed/test/e2.d new file mode 100644 index 000000000000..aa44630d22a6 --- /dev/null +++ b/bin/ed/test/e2.d @@ -0,0 +1 @@ +E !echo hello world- diff --git a/bin/ed/test/e2.err b/bin/ed/test/e2.err new file mode 100644 index 000000000000..779a64b54ff9 --- /dev/null +++ b/bin/ed/test/e2.err @@ -0,0 +1 @@ +.e e2.err diff --git a/bin/ed/test/e2.r b/bin/ed/test/e2.r new file mode 100644 index 000000000000..59ebf11fd099 --- /dev/null +++ b/bin/ed/test/e2.r @@ -0,0 +1 @@ +hello world- diff --git a/bin/ed/test/e2.t b/bin/ed/test/e2.t new file mode 100644 index 000000000000..aa44630d22a6 --- /dev/null +++ b/bin/ed/test/e2.t @@ -0,0 +1 @@ +E !echo hello world- diff --git a/bin/ed/test/e3.d b/bin/ed/test/e3.d new file mode 100644 index 000000000000..aa44630d22a6 --- /dev/null +++ b/bin/ed/test/e3.d @@ -0,0 +1 @@ +E !echo hello world- diff --git a/bin/ed/test/e3.err b/bin/ed/test/e3.err new file mode 100644 index 000000000000..80a7fdcf92ee --- /dev/null +++ b/bin/ed/test/e3.err @@ -0,0 +1 @@ +ee.err diff --git a/bin/ed/test/e3.r b/bin/ed/test/e3.r new file mode 100644 index 000000000000..aa44630d22a6 --- /dev/null +++ b/bin/ed/test/e3.r @@ -0,0 +1 @@ +E !echo hello world- diff --git a/bin/ed/test/e3.t b/bin/ed/test/e3.t new file mode 100644 index 000000000000..1c507261389e --- /dev/null +++ b/bin/ed/test/e3.t @@ -0,0 +1 @@ +E diff --git a/bin/ed/test/e4.d b/bin/ed/test/e4.d new file mode 100644 index 000000000000..aa44630d22a6 --- /dev/null +++ b/bin/ed/test/e4.d @@ -0,0 +1 @@ +E !echo hello world- diff --git a/bin/ed/test/e4.r b/bin/ed/test/e4.r new file mode 100644 index 000000000000..aa44630d22a6 --- /dev/null +++ b/bin/ed/test/e4.r @@ -0,0 +1 @@ +E !echo hello world- diff --git a/bin/ed/test/e4.t b/bin/ed/test/e4.t new file mode 100644 index 000000000000..d905d9da82c9 --- /dev/null +++ b/bin/ed/test/e4.t @@ -0,0 +1 @@ +e diff --git a/bin/ed/test/f1.err b/bin/ed/test/f1.err new file mode 100644 index 000000000000..e60975adabb3 --- /dev/null +++ b/bin/ed/test/f1.err @@ -0,0 +1 @@ +.f f1.err diff --git a/bin/ed/test/f2.err b/bin/ed/test/f2.err new file mode 100644 index 000000000000..26d1c5e3a756 --- /dev/null +++ b/bin/ed/test/f2.err @@ -0,0 +1 @@ +ff1.err diff --git a/bin/ed/test/g1.d b/bin/ed/test/g1.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/g1.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/g1.err b/bin/ed/test/g1.err new file mode 100644 index 000000000000..f95ea22232c6 --- /dev/null +++ b/bin/ed/test/g1.err @@ -0,0 +1 @@ +g/./s //x/ diff --git a/bin/ed/test/g1.r b/bin/ed/test/g1.r new file mode 100644 index 000000000000..578a44b6b21c --- /dev/null +++ b/bin/ed/test/g1.r @@ -0,0 +1,15 @@ +line5 +help! world +order +line 4 +help! world +order +line 3 +help! world +order +line 2 +help! world +order +line 1 +help! world +order diff --git a/bin/ed/test/g1.t b/bin/ed/test/g1.t new file mode 100644 index 000000000000..2d0b54f35ad3 --- /dev/null +++ b/bin/ed/test/g1.t @@ -0,0 +1,6 @@ +g/./m0 +g/./s/$/\ +hello world +g/hello /s/lo/p!/\ +a\ +order diff --git a/bin/ed/test/g2.d b/bin/ed/test/g2.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/g2.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/g2.err b/bin/ed/test/g2.err new file mode 100644 index 000000000000..0ff6a5a601dd --- /dev/null +++ b/bin/ed/test/g2.err @@ -0,0 +1 @@ +g//s/./x/ diff --git a/bin/ed/test/g2.r b/bin/ed/test/g2.r new file mode 100644 index 000000000000..3b18e512dba7 --- /dev/null +++ b/bin/ed/test/g2.r @@ -0,0 +1 @@ +hello world diff --git a/bin/ed/test/g2.t b/bin/ed/test/g2.t new file mode 100644 index 000000000000..831ee8367bcf --- /dev/null +++ b/bin/ed/test/g2.t @@ -0,0 +1,2 @@ +g/[2-4]/-1,+1c\ +hello world diff --git a/bin/ed/test/g3.d b/bin/ed/test/g3.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/g3.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/g3.err b/bin/ed/test/g3.err new file mode 100644 index 000000000000..01058d844a98 --- /dev/null +++ b/bin/ed/test/g3.err @@ -0,0 +1 @@ +g diff --git a/bin/ed/test/g3.r b/bin/ed/test/g3.r new file mode 100644 index 000000000000..cc6fbddec23b --- /dev/null +++ b/bin/ed/test/g3.r @@ -0,0 +1,5 @@ +linc 3 +xine 1 +xine 2 +xinc 4 +xinc5 diff --git a/bin/ed/test/g3.t b/bin/ed/test/g3.t new file mode 100644 index 000000000000..2d052a6e840d --- /dev/null +++ b/bin/ed/test/g3.t @@ -0,0 +1,4 @@ +g/./s//x/\ +3m0 +g/./s/e/c/\ +2,3m1 diff --git a/bin/ed/test/g4.d b/bin/ed/test/g4.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/g4.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/g4.r b/bin/ed/test/g4.r new file mode 100644 index 000000000000..350882d82375 --- /dev/null +++ b/bin/ed/test/g4.r @@ -0,0 +1,7 @@ +hello +zine 1 +line 2 +line 3 +line 4 +line5 +world diff --git a/bin/ed/test/g4.t b/bin/ed/test/g4.t new file mode 100644 index 000000000000..ec618166cc37 --- /dev/null +++ b/bin/ed/test/g4.t @@ -0,0 +1,13 @@ +g/./s/./x/\ +u\ +s/./y/\ +u\ +s/./z/\ +u +u +0a +hello +. +$a +world +. diff --git a/bin/ed/test/h.err b/bin/ed/test/h.err new file mode 100644 index 000000000000..a71e506f6dd0 --- /dev/null +++ b/bin/ed/test/h.err @@ -0,0 +1 @@ +.h diff --git a/bin/ed/test/i.d b/bin/ed/test/i.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/i.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/i.r b/bin/ed/test/i.r new file mode 100644 index 000000000000..5f27af09c03d --- /dev/null +++ b/bin/ed/test/i.r @@ -0,0 +1,8 @@ +hello world +hello world! +line 1 +line 2 +line 3 +line 4 +hello world!! +line5 diff --git a/bin/ed/test/i.t b/bin/ed/test/i.t new file mode 100644 index 000000000000..d1d98057d882 --- /dev/null +++ b/bin/ed/test/i.t @@ -0,0 +1,9 @@ +1i +hello world +. +2i +hello world! +. +$i +hello world!! +. diff --git a/bin/ed/test/i1.err b/bin/ed/test/i1.err new file mode 100644 index 000000000000..aaddede2599d --- /dev/null +++ b/bin/ed/test/i1.err @@ -0,0 +1,3 @@ +1,$i +hello world +. diff --git a/bin/ed/test/i2.err b/bin/ed/test/i2.err new file mode 100644 index 000000000000..b63f5ac507f5 --- /dev/null +++ b/bin/ed/test/i2.err @@ -0,0 +1,3 @@ +ii +hello world +. diff --git a/bin/ed/test/i3.err b/bin/ed/test/i3.err new file mode 100644 index 000000000000..6d200c8c97ee --- /dev/null +++ b/bin/ed/test/i3.err @@ -0,0 +1,3 @@ +0i +hello world +. diff --git a/bin/ed/test/j.d b/bin/ed/test/j.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/j.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/j.r b/bin/ed/test/j.r new file mode 100644 index 000000000000..66f36a8f8ab5 --- /dev/null +++ b/bin/ed/test/j.r @@ -0,0 +1,4 @@ +line 1 +line 2line 3 +line 4 +line5 diff --git a/bin/ed/test/j.t b/bin/ed/test/j.t new file mode 100644 index 000000000000..9b5d28ddf178 --- /dev/null +++ b/bin/ed/test/j.t @@ -0,0 +1,2 @@ +1,1j +2,3j diff --git a/bin/ed/test/k.d b/bin/ed/test/k.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/k.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/k.r b/bin/ed/test/k.r new file mode 100644 index 000000000000..eeb38db20c6e --- /dev/null +++ b/bin/ed/test/k.r @@ -0,0 +1,5 @@ +line 3 +hello world +line 4 +line5 +line 2 diff --git a/bin/ed/test/k.t b/bin/ed/test/k.t new file mode 100644 index 000000000000..53d588dd07ad --- /dev/null +++ b/bin/ed/test/k.t @@ -0,0 +1,10 @@ +2ka +1d +'am$ +1ka +0a +hello world +. +'ad +u +'am0 diff --git a/bin/ed/test/k1.err b/bin/ed/test/k1.err new file mode 100644 index 000000000000..eba1f3d8ff1d --- /dev/null +++ b/bin/ed/test/k1.err @@ -0,0 +1 @@ +1,$ka diff --git a/bin/ed/test/k2.err b/bin/ed/test/k2.err new file mode 100644 index 000000000000..b34a18d51958 --- /dev/null +++ b/bin/ed/test/k2.err @@ -0,0 +1 @@ +kA diff --git a/bin/ed/test/k3.err b/bin/ed/test/k3.err new file mode 100644 index 000000000000..70190c473df4 --- /dev/null +++ b/bin/ed/test/k3.err @@ -0,0 +1 @@ +0ka diff --git a/bin/ed/test/k4.err b/bin/ed/test/k4.err new file mode 100644 index 000000000000..345764222947 --- /dev/null +++ b/bin/ed/test/k4.err @@ -0,0 +1,6 @@ +a +hello +. +.ka +'ad +'ap diff --git a/bin/ed/test/l.d b/bin/ed/test/l.d new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/bin/ed/test/l.d diff --git a/bin/ed/test/l.r b/bin/ed/test/l.r new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/bin/ed/test/l.r diff --git a/bin/ed/test/l.t b/bin/ed/test/l.t new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/bin/ed/test/l.t diff --git a/bin/ed/test/m.d b/bin/ed/test/m.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/m.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/m.err b/bin/ed/test/m.err new file mode 100644 index 000000000000..3aec4c32c1f9 --- /dev/null +++ b/bin/ed/test/m.err @@ -0,0 +1,4 @@ +a +hello world +. +1,$m1 diff --git a/bin/ed/test/m.r b/bin/ed/test/m.r new file mode 100644 index 000000000000..186cf5403b9f --- /dev/null +++ b/bin/ed/test/m.r @@ -0,0 +1,5 @@ +line5 +line 1 +line 2 +line 3 +line 4 diff --git a/bin/ed/test/m.t b/bin/ed/test/m.t new file mode 100644 index 000000000000..c39c08872452 --- /dev/null +++ b/bin/ed/test/m.t @@ -0,0 +1,7 @@ +1,2m$ +1,2m$ +1,2m$ +$m0 +$m0 +2,3m1 +2,3m3 diff --git a/bin/ed/test/mkscripts.sh b/bin/ed/test/mkscripts.sh new file mode 100644 index 000000000000..724db0cc05a9 --- /dev/null +++ b/bin/ed/test/mkscripts.sh @@ -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 diff --git a/bin/ed/test/n.d b/bin/ed/test/n.d new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/bin/ed/test/n.d diff --git a/bin/ed/test/n.r b/bin/ed/test/n.r new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/bin/ed/test/n.r diff --git a/bin/ed/test/n.t b/bin/ed/test/n.t new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/bin/ed/test/n.t diff --git a/bin/ed/test/nl.err b/bin/ed/test/nl.err new file mode 100644 index 000000000000..8949a85006c2 --- /dev/null +++ b/bin/ed/test/nl.err @@ -0,0 +1 @@ +,1 diff --git a/bin/ed/test/nl1.d b/bin/ed/test/nl1.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/nl1.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/nl1.r b/bin/ed/test/nl1.r new file mode 100644 index 000000000000..9d8854cd04d5 --- /dev/null +++ b/bin/ed/test/nl1.r @@ -0,0 +1,8 @@ + + +hello world +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/nl1.t b/bin/ed/test/nl1.t new file mode 100644 index 000000000000..ea192e9b829b --- /dev/null +++ b/bin/ed/test/nl1.t @@ -0,0 +1,8 @@ +1 + + +0a + + +hello world +. diff --git a/bin/ed/test/nl2.d b/bin/ed/test/nl2.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/nl2.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/nl2.r b/bin/ed/test/nl2.r new file mode 100644 index 000000000000..fe99e4162863 --- /dev/null +++ b/bin/ed/test/nl2.r @@ -0,0 +1,6 @@ +line 1 +line 2 +line 3 +line 4 +line5 +hello world diff --git a/bin/ed/test/nl2.t b/bin/ed/test/nl2.t new file mode 100644 index 000000000000..73fd27b7e23a --- /dev/null +++ b/bin/ed/test/nl2.t @@ -0,0 +1,4 @@ +a +hello world +. +0;/./ diff --git a/bin/ed/test/p.d b/bin/ed/test/p.d new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/bin/ed/test/p.d diff --git a/bin/ed/test/p.r b/bin/ed/test/p.r new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/bin/ed/test/p.r diff --git a/bin/ed/test/p.t b/bin/ed/test/p.t new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/bin/ed/test/p.t diff --git a/bin/ed/test/q.d b/bin/ed/test/q.d new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/bin/ed/test/q.d diff --git a/bin/ed/test/q.r b/bin/ed/test/q.r new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/bin/ed/test/q.r diff --git a/bin/ed/test/q.t b/bin/ed/test/q.t new file mode 100644 index 000000000000..123a2c8e2cf4 --- /dev/null +++ b/bin/ed/test/q.t @@ -0,0 +1,5 @@ +w q.o +a +hello +. +q diff --git a/bin/ed/test/q1.err b/bin/ed/test/q1.err new file mode 100644 index 000000000000..0a7e178d2062 --- /dev/null +++ b/bin/ed/test/q1.err @@ -0,0 +1 @@ +.q diff --git a/bin/ed/test/r1.d b/bin/ed/test/r1.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/r1.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/r1.err b/bin/ed/test/r1.err new file mode 100644 index 000000000000..269aa7cbcb8c --- /dev/null +++ b/bin/ed/test/r1.err @@ -0,0 +1 @@ +1,$r r1.err diff --git a/bin/ed/test/r1.r b/bin/ed/test/r1.r new file mode 100644 index 000000000000..a3ff506ec7c2 --- /dev/null +++ b/bin/ed/test/r1.r @@ -0,0 +1,7 @@ +line 1 +hello world +line 2 +line 3 +line 4 +line5 +hello world diff --git a/bin/ed/test/r1.t b/bin/ed/test/r1.t new file mode 100644 index 000000000000..d787a923e3f2 --- /dev/null +++ b/bin/ed/test/r1.t @@ -0,0 +1,3 @@ +1;r !echo hello world +1 +r !echo hello world diff --git a/bin/ed/test/r2.d b/bin/ed/test/r2.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/r2.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/r2.err b/bin/ed/test/r2.err new file mode 100644 index 000000000000..1c44fa3ea9ca --- /dev/null +++ b/bin/ed/test/r2.err @@ -0,0 +1 @@ +r a-good-book diff --git a/bin/ed/test/r2.r b/bin/ed/test/r2.r new file mode 100644 index 000000000000..ac152ba9d0a2 --- /dev/null +++ b/bin/ed/test/r2.r @@ -0,0 +1,10 @@ +line 1 +line 2 +line 3 +line 4 +line5 +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/r2.t b/bin/ed/test/r2.t new file mode 100644 index 000000000000..4286f428e3b1 --- /dev/null +++ b/bin/ed/test/r2.t @@ -0,0 +1 @@ +r diff --git a/bin/ed/test/r3.d b/bin/ed/test/r3.d new file mode 100644 index 000000000000..593eec6192b4 --- /dev/null +++ b/bin/ed/test/r3.d @@ -0,0 +1 @@ +r r3.t diff --git a/bin/ed/test/r3.r b/bin/ed/test/r3.r new file mode 100644 index 000000000000..86d5f904fc11 --- /dev/null +++ b/bin/ed/test/r3.r @@ -0,0 +1,2 @@ +r r3.t +r r3.t diff --git a/bin/ed/test/r3.t b/bin/ed/test/r3.t new file mode 100644 index 000000000000..593eec6192b4 --- /dev/null +++ b/bin/ed/test/r3.t @@ -0,0 +1 @@ +r r3.t diff --git a/bin/ed/test/s1.d b/bin/ed/test/s1.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/s1.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/s1.err b/bin/ed/test/s1.err new file mode 100644 index 000000000000..d7ca0cf480d2 --- /dev/null +++ b/bin/ed/test/s1.err @@ -0,0 +1 @@ +s . x diff --git a/bin/ed/test/s1.r b/bin/ed/test/s1.r new file mode 100644 index 000000000000..4eb0980cfe79 --- /dev/null +++ b/bin/ed/test/s1.r @@ -0,0 +1,5 @@ +liene 1 +(liene) (2) +(liene) (3) +liene (4) +(()liene5) diff --git a/bin/ed/test/s1.t b/bin/ed/test/s1.t new file mode 100644 index 000000000000..b0028bb6c921 --- /dev/null +++ b/bin/ed/test/s1.t @@ -0,0 +1,6 @@ +s/\([^ ][^ ]*\)/(\1)/g +2s +/3/s +/\(4\)/sr +/\(.\)/srg +%s/i/&e/ diff --git a/bin/ed/test/s10.err b/bin/ed/test/s10.err new file mode 100644 index 000000000000..0d8d83de19ac --- /dev/null +++ b/bin/ed/test/s10.err @@ -0,0 +1,4 @@ +a +hello +. +s/[h[.]/x/ diff --git a/bin/ed/test/s2.d b/bin/ed/test/s2.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/s2.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/s2.err b/bin/ed/test/s2.err new file mode 100644 index 000000000000..b5c851df7b28 --- /dev/null +++ b/bin/ed/test/s2.err @@ -0,0 +1,4 @@ +a +a +. +s/x*/a/g diff --git a/bin/ed/test/s2.r b/bin/ed/test/s2.r new file mode 100644 index 000000000000..ca305c8d506a --- /dev/null +++ b/bin/ed/test/s2.r @@ -0,0 +1,5 @@ +li(n)e 1 +i(n)e 200 +li(n)e 3 +li(n)e 4 +li(n)e500 diff --git a/bin/ed/test/s2.t b/bin/ed/test/s2.t new file mode 100644 index 000000000000..f36584997c97 --- /dev/null +++ b/bin/ed/test/s2.t @@ -0,0 +1,4 @@ +,s/./(&)/3 +s/$/00 +2s//%/g +s/^l diff --git a/bin/ed/test/s3.d b/bin/ed/test/s3.d new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/bin/ed/test/s3.d diff --git a/bin/ed/test/s3.err b/bin/ed/test/s3.err new file mode 100644 index 000000000000..d68c7d07f99c --- /dev/null +++ b/bin/ed/test/s3.err @@ -0,0 +1 @@ +s/[xyx/a/ diff --git a/bin/ed/test/s3.r b/bin/ed/test/s3.r new file mode 100644 index 000000000000..d6cada2212fd --- /dev/null +++ b/bin/ed/test/s3.r @@ -0,0 +1 @@ +hello world diff --git a/bin/ed/test/s3.t b/bin/ed/test/s3.t new file mode 100644 index 000000000000..fbf880304b7a --- /dev/null +++ b/bin/ed/test/s3.t @@ -0,0 +1,6 @@ +a +hello/[]world +. +s/[/]/ / +s/[[:digit:][]/ / +s/[]]/ / diff --git a/bin/ed/test/s4.err b/bin/ed/test/s4.err new file mode 100644 index 000000000000..35b609fc625c --- /dev/null +++ b/bin/ed/test/s4.err @@ -0,0 +1 @@ +s/\a\b\c/xyz/ diff --git a/bin/ed/test/s5.err b/bin/ed/test/s5.err new file mode 100644 index 000000000000..89104c55232c --- /dev/null +++ b/bin/ed/test/s5.err @@ -0,0 +1 @@ +s//xyz/ diff --git a/bin/ed/test/s6.err b/bin/ed/test/s6.err new file mode 100644 index 000000000000..b4785957bc98 --- /dev/null +++ b/bin/ed/test/s6.err @@ -0,0 +1 @@ +s diff --git a/bin/ed/test/s7.err b/bin/ed/test/s7.err new file mode 100644 index 000000000000..30ba4fded765 --- /dev/null +++ b/bin/ed/test/s7.err @@ -0,0 +1,5 @@ +a +hello world +. +/./ +sr diff --git a/bin/ed/test/s8.err b/bin/ed/test/s8.err new file mode 100644 index 000000000000..5665767c3fa0 --- /dev/null +++ b/bin/ed/test/s8.err @@ -0,0 +1,4 @@ +a +hello +. +s/[h[=]/x/ diff --git a/bin/ed/test/s9.err b/bin/ed/test/s9.err new file mode 100644 index 000000000000..1ff16dd8470f --- /dev/null +++ b/bin/ed/test/s9.err @@ -0,0 +1,4 @@ +a +hello +. +s/[h[:]/x/ diff --git a/bin/ed/test/t.d b/bin/ed/test/t.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/t.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/t.r b/bin/ed/test/t.r new file mode 100644 index 000000000000..2b2854758d5d --- /dev/null +++ b/bin/ed/test/t.r @@ -0,0 +1,16 @@ +line 1 +line 1 +line 1 +line 2 +line 2 +line 3 +line 4 +line5 +line 1 +line 1 +line 1 +line 2 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/t1.d b/bin/ed/test/t1.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/t1.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/t1.err b/bin/ed/test/t1.err new file mode 100644 index 000000000000..c49c556e0e47 --- /dev/null +++ b/bin/ed/test/t1.err @@ -0,0 +1 @@ +tt diff --git a/bin/ed/test/t1.r b/bin/ed/test/t1.r new file mode 100644 index 000000000000..2b2854758d5d --- /dev/null +++ b/bin/ed/test/t1.r @@ -0,0 +1,16 @@ +line 1 +line 1 +line 1 +line 2 +line 2 +line 3 +line 4 +line5 +line 1 +line 1 +line 1 +line 2 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/t1.t b/bin/ed/test/t1.t new file mode 100644 index 000000000000..6b66163793ad --- /dev/null +++ b/bin/ed/test/t1.t @@ -0,0 +1,3 @@ +1t0 +2,3t2 +,t$ diff --git a/bin/ed/test/t2.d b/bin/ed/test/t2.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/t2.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/t2.err b/bin/ed/test/t2.err new file mode 100644 index 000000000000..c202051b7f18 --- /dev/null +++ b/bin/ed/test/t2.err @@ -0,0 +1 @@ +t0;-1 diff --git a/bin/ed/test/t2.r b/bin/ed/test/t2.r new file mode 100644 index 000000000000..0c75ff554ca8 --- /dev/null +++ b/bin/ed/test/t2.r @@ -0,0 +1,6 @@ +line 1 +line5 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/t2.t b/bin/ed/test/t2.t new file mode 100644 index 000000000000..5175abdec90d --- /dev/null +++ b/bin/ed/test/t2.t @@ -0,0 +1 @@ +t0;/./ diff --git a/bin/ed/test/u.d b/bin/ed/test/u.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/u.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/u.err b/bin/ed/test/u.err new file mode 100644 index 000000000000..caa1ba114885 --- /dev/null +++ b/bin/ed/test/u.err @@ -0,0 +1 @@ +.u diff --git a/bin/ed/test/u.r b/bin/ed/test/u.r new file mode 100644 index 000000000000..ad558d82d02d --- /dev/null +++ b/bin/ed/test/u.r @@ -0,0 +1,9 @@ +line 1 +hello +hello world!! +line 2 +line 3 +line 4 +line5 +hello +hello world!! diff --git a/bin/ed/test/u.t b/bin/ed/test/u.t new file mode 100644 index 000000000000..131cb6e25c1b --- /dev/null +++ b/bin/ed/test/u.t @@ -0,0 +1,31 @@ +1;r u.t +u +a +hello +world +. +g/./s//x/\ +a\ +hello\ +world +u +u +u +a +hello world! +. +u +1,$d +u +2,3d +u +c +hello world!! +. +u +u +-1;.,+1j +u +u +u +.,+1t$ diff --git a/bin/ed/test/v.d b/bin/ed/test/v.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/v.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/v.r b/bin/ed/test/v.r new file mode 100644 index 000000000000..714db63e357b --- /dev/null +++ b/bin/ed/test/v.r @@ -0,0 +1,11 @@ +line5 +order +hello world +line 1 +order +line 2 +order +line 3 +order +line 4 +order diff --git a/bin/ed/test/v.t b/bin/ed/test/v.t new file mode 100644 index 000000000000..608a77fb6a43 --- /dev/null +++ b/bin/ed/test/v.t @@ -0,0 +1,6 @@ +v/[ ]/m0 +v/[ ]/s/$/\ +hello world +v/hello /s/lo/p!/\ +a\ +order diff --git a/bin/ed/test/w.d b/bin/ed/test/w.d new file mode 100644 index 000000000000..92f337e977f2 --- /dev/null +++ b/bin/ed/test/w.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/w.r b/bin/ed/test/w.r new file mode 100644 index 000000000000..ac152ba9d0a2 --- /dev/null +++ b/bin/ed/test/w.r @@ -0,0 +1,10 @@ +line 1 +line 2 +line 3 +line 4 +line5 +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/w.t b/bin/ed/test/w.t new file mode 100644 index 000000000000..c2e18bd2f659 --- /dev/null +++ b/bin/ed/test/w.t @@ -0,0 +1,2 @@ +w !cat >\!.z +r \!.z diff --git a/bin/ed/test/w1.err b/bin/ed/test/w1.err new file mode 100644 index 000000000000..e2c8a603f7e3 --- /dev/null +++ b/bin/ed/test/w1.err @@ -0,0 +1 @@ +w /to/some/far-away/place diff --git a/bin/ed/test/w2.err b/bin/ed/test/w2.err new file mode 100644 index 000000000000..9daf89cfa71d --- /dev/null +++ b/bin/ed/test/w2.err @@ -0,0 +1 @@ +ww.o diff --git a/bin/ed/test/w3.err b/bin/ed/test/w3.err new file mode 100644 index 000000000000..39bbf4c95b9b --- /dev/null +++ b/bin/ed/test/w3.err @@ -0,0 +1 @@ +wqp w.o diff --git a/bin/ed/test/x.err b/bin/ed/test/x.err new file mode 100644 index 000000000000..0953f01dd071 --- /dev/null +++ b/bin/ed/test/x.err @@ -0,0 +1 @@ +.x diff --git a/bin/ed/test/z.err b/bin/ed/test/z.err new file mode 100644 index 000000000000..6a51a2d58306 --- /dev/null +++ b/bin/ed/test/z.err @@ -0,0 +1,2 @@ +z +z |
