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