diff options
Diffstat (limited to 'bin/ed')
| -rw-r--r-- | bin/ed/Makefile | 10 | ||||
| -rw-r--r-- | bin/ed/POSIX | 143 | ||||
| -rw-r--r-- | bin/ed/README | 9 | ||||
| -rw-r--r-- | bin/ed/buf.c | 136 | ||||
| -rw-r--r-- | bin/ed/cbc.c | 114 | ||||
| -rw-r--r-- | bin/ed/ed.1 | 80 | ||||
| -rw-r--r-- | bin/ed/ed.c | 2206 | ||||
| -rw-r--r-- | bin/ed/ed.h | 290 | ||||
| -rw-r--r-- | bin/ed/glob.c | 223 | ||||
| -rw-r--r-- | bin/ed/io.c | 375 | ||||
| -rw-r--r-- | bin/ed/main.c | 1432 | ||||
| -rw-r--r-- | bin/ed/re.c | 67 | ||||
| -rw-r--r-- | bin/ed/sub.c | 263 | ||||
| -rw-r--r-- | bin/ed/test/Makefile | 24 | ||||
| -rw-r--r-- | bin/ed/test/README | 11 | ||||
| -rw-r--r-- | bin/ed/test/addr.d | 9 | ||||
| -rw-r--r-- | bin/ed/test/addr.r | 2 | ||||
| -rw-r--r-- | bin/ed/test/addr.t | 5 | ||||
| -rw-r--r-- | bin/ed/test/bang2.r | 4 | ||||
| -rw-r--r-- | bin/ed/test/bang2.t | 2 | ||||
| -rw-r--r-- | bin/ed/test/ckscripts.sh | 20 | ||||
| -rw-r--r-- | bin/ed/test/g5.d (renamed from bin/ed/test/bang2.d) | 2 | ||||
| -rw-r--r-- | bin/ed/test/g5.r | 9 | ||||
| -rw-r--r-- | bin/ed/test/g5.t | 2 | ||||
| -rw-r--r-- | bin/ed/test/mkscripts.sh | 112 | ||||
| -rw-r--r-- | bin/ed/undo.c | 154 |
26 files changed, 3003 insertions, 2701 deletions
diff --git a/bin/ed/Makefile b/bin/ed/Makefile index 5a6a4ea4aee8..a99763cc9202 100644 --- a/bin/ed/Makefile +++ b/bin/ed/Makefile @@ -1,8 +1,7 @@ PROG= ed -CFLAGS+=-DVI_BANG -SRCS= ed.c re.c buf.c cbc.c -LINKS= ${BINDIR}/ed ${BINDIR}/red -MLINKS= ed.1 red.1 +SRCS= buf.c cbc.c glob.c io.c main.c re.c sub.c undo.c +LINKS= ${BINDIR}/ed ${BINDIR}/red +MLINKS= ed.1 red.1 .if exists(/usr/lib/libcrypt.a) CFLAGS+=-DDES @@ -10,7 +9,4 @@ LDADD+= -lcrypt DPADD+= ${LIBCRYPT} .endif -LDADD+= -lgnuregex -DPADD+= /usr/lib/libgnuregex.a - .include <bsd.prog.mk> diff --git a/bin/ed/POSIX b/bin/ed/POSIX index 47a80b9e72a0..e724c12a2e3b 100644 --- a/bin/ed/POSIX +++ b/bin/ed/POSIX @@ -1,62 +1,101 @@ -This version of ed is not strictly POSIX compliant, as described in the -POSIX 1003.2 Draft 11.2 document. BSD commands have been implemented -wherever they do not conflict with the POSIX standard. For backwards -compatibility, the POSIX rule that says a range of addresses cannot be -used where only a single address is expected has been relaxed. +This version of ed(1) is not strictly POSIX compliant, as described in +the POSIX 1003.2 document. The following is a summary of the omissions, +extensions and possible deviations from POSIX 1003.2. -The BSD commands included are: - 1) `s' (i.e., s[rgp]*) to repeat a previous substitution, - 2) `W' for appending text to an existing file, - 3) `wq' for exiting after a write, and - 4) `z' for scrolling through the buffer. -BSD line addressing syntax (i.e., `^' and `%'). is also recognized. +OMISSIONS +--------- +1) Locale(3) is not supported yet. -The POSIX interactive global commands `G' and `V' are extended to support -multiple commands, including `a', `i' and `c'. The command format is the -same as for the global commands `g' and `v', i.e., one command per line -with each line, except for the last, ending in a backslash (\). +2) For backwards compatibility, the POSIX rule that says a range of + addresses cannot be used where only a single address is expected has + been relaxed. -If crypt is available, files can be read and written using DES encryption. -The `x' command prompts the user to enter a key used for encrypting/ -decrypting subsequent reads and writes. If only a newline is entered as -the key, then encryption is disabled. Otherwise, a key is read in the -same manner as a password entry. The key remains in effect until -encryption is disabled. For more information on the encryption algorithm, -see the bdes(1) man page. Encryption/decryption should be fully compatible -with SunOS DES. +3) To support the BSD `s' command (see extension [1] below), + substitution patterns cannot be delimited by numbers or the characters + `r', `g' and `p'. In contrast, POSIX specifies any character expect + space or newline can used as a delimiter. -An extension to the POSIX file commands `E', `e', `r', `W' and `w' is that -<file> arguments are processed for backslash escapes, i.e., any character -preceded by a backslash is interpreted literally. If the first unescaped -character of a <file> argument is a bang (!), then the rest of the line -is interpreted as a shell command, and no escape processing is performed -by ed. +EXTENSIONS +---------- +1) BSD commands have been implemented wherever they do not conflict with + the POSIX standard. The BSD-ism's included are: + i) `s' (i.e., s[n][rgp]*) to repeat a previous substitution, + ii) `W' for appending text to an existing file, + iii) `wq' for exiting after a write, + iv) `z' for scrolling through the buffer, and + v) BSD line addressing syntax (i.e., `^' and `%') is recognized. -The vi editor's bang command syntax is supported, i.e., -(addr1,addr2) !<shell-cmd> replaces the addressed lines with the output of - the command <shell-cmd>. -[rwe] !! reads/writes/edits the previous !<shell-cmd>. +2) If crypt(3) is available, files can be read and written using DES + encryption. The `x' command prompts the user to enter a key used for + encrypting/ decrypting subsequent reads and writes. If only a newline + is entered as the key, then encryption is disabled. Otherwise, a key + is read in the same manner as a password entry. The key remains in + effect until encryption is disabled. For more information on the + encryption algorithm, see the bdes(1) man page. Encryption/decryption + should be fully compatible with SunOS des(1). -If ed is invoked with a name argument prefixed by a bang, then the -remainder of the argument is interpreted as a shell command. To invoke -ed on a file whose name starts with bang, prefix the name with a backslash. +3) The POSIX interactive global commands `G' and `V' are extended to + support multiple commands, including `a', `i' and `c'. The command + format is the same as for the global commands `g' and `v', i.e., one + command per line with each line, except for the last, ending in a + backslash (\). -ed runs in restricted mode if invoked as red. This limits editing of -files in the local directory only and prohibits !<shell-cmd> commands. +4) An extension to the POSIX file commands `E', `e', `r', `W' and `w' is + that <file> arguments are processed for backslash escapes, i.e., any + character preceded by a backslash is interpreted literally. If the + first unescaped character of a <file> argument is a bang (!), then the + rest of the line is interpreted as a shell command, and no escape + processing is performed by ed. -Though ed is not a binary editor, it can be used (if painfully) to edit -binary files. To assist in binary editing, when a file containing at -least one ASCII NUL character is written, a newline is not appended -if it did not already contain one upon reading. +5) For SunOS ed(1) compatibility, ed runs in restricted mode if invoked + as red. This limits editing of files in the local directory only and + prohibits shell commands. -Since the behavior of `u' (undo) within a `g' (global) command list is -not specified by POSIX D11/2, it follows the behavior of the SunOS ed -(this is the best way, I think, in that the alternatives are either too -complicated to implement or too confusing to use): undo forces a global -command list to be executed only once, rather than for each line matching -a global pattern. In addtion, each instance of `u' within a global command -undoes all previous commands (including undo's) in the command list. +DEVIATIONS +---------- +1) Though ed is not a stream editor, it can be used to edit binary files. + To assist in binary editing, when a file containing at least one ASCII + NUL character is written, a newline is not appended if it did not + already contain one upon reading. In particular, reading /dev/null + prior to writing prevents appending a newline to a binary file. -The `m' (move) command within a `g' command list also follows the SunOS -ed implementation: any moved lines are removed from the global command's -`active' list. + For example, to create a file with ed containing a single NUL character: + $ ed file + a + ^@ + . + r /dev/null + wq + + Similarly, to remove a newline from the end of binary `file': + $ ed file + r /dev/null + wq + +2) Since the behavior of `u' (undo) within a `g' (global) command list is + not specified by POSIX, it follows the behavior of the SunOS ed: + undo forces a global command list to be executed only once, rather than + for each line matching a global pattern. In addtion, each instance of + `u' within a global command undoes all previous commands (including + undo's) in the command list. This seems the best way, since the + alternatives are either too complicated to implement or too confusing + to use. + + The global/undo combination is useful for masking errors that + would otherwise cause a script to fail. For instance, an ed script + to remove any occurences of either `censor1' or `censor2' might be + written as: + ed - file <<EOF + 1g/.*/u\ + ,s/censor1//g\ + ,s/censor2//g + ... + +3) The `m' (move) command within a `g' command list also follows the SunOS + ed implementation: any moved lines are removed from the global command's + `active' list. + +4) If ed is invoked with a name argument prefixed by a bang (!), then the + remainder of the argument is interpreted as a shell command. To invoke + ed on a file whose name starts with bang, prefix the name with a + backslash. diff --git a/bin/ed/README b/bin/ed/README index 06e302d7b116..c600c2739498 100644 --- a/bin/ed/README +++ b/bin/ed/README @@ -3,13 +3,14 @@ any regular expression package that conforms to the POSIX interface standard, such as GNU regex(3). If reliable signals are supported (e.g., POSIX sigaction(2)), it should -compile with little trouble. Otherwise, the macros spl1() and spl0() +compile with little trouble. Otherwise, the macros SPL1() and SPL0() should be redefined to disable interrupts. The following compiler directives are recognized: -DES - use to add encryption support (requires crypt(3)) -NO_REALLOC_NULL - use if realloc(3) does not accept a NULL pointer -BACKWARDS - use for backwards compatibility +DES - to add encryption support (requires crypt(3)) +NO_REALLOC_NULL - if realloc(3) does not accept a NULL pointer +BACKWARDS - for backwards compatibility +NEED_INSQUE - if insque(3) is missing The file `POSIX' describes extensions to and deviations from the POSIX standard. diff --git a/bin/ed/buf.c b/bin/ed/buf.c index 7c92e5586269..f5b8045ebb3b 100644 --- a/bin/ed/buf.c +++ b/bin/ed/buf.c @@ -1,12 +1,9 @@ /* buf.c: This file contains the scratch-file buffer rountines for the ed line editor. */ /*- - * Copyright (c) 1992 The Regents of the University of California. + * Copyright (c) 1993 Andrew Moore, Talke Studio. * All rights reserved. * - * This code is derived from software contributed to Berkeley by - * Rodney Ruddock of the University of Guelph. - * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -15,18 +12,11 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) @@ -35,37 +25,32 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ - #ifndef lint -static char sccsid[] = "@(#)buf.c 5.5 (Berkeley) 3/28/93"; +static char *rcsid = "@(#)$Id: buf.c,v 1.4 1994/02/01 00:34:35 alm Exp $"; #endif /* not lint */ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> #include <sys/file.h> -#include <unistd.h> #include "ed.h" -extern char errmsg[]; FILE *sfp; /* scratch file pointer */ -char *sfbuf = NULL; /* scratch file input buffer */ -int sfbufsz = 0; /* scratch file input buffer size */ off_t sfseek; /* scratch file position */ int seek_write; /* seek before writing */ -line_t line0; /* initial node of line queue */ +line_t buffer_head; /* incore buffer */ -/* gettxt: get a line of text from the scratch file; return pointer +/* get_sbuf_line: get a line of text from the scratch file; return pointer to the text */ char * -gettxt(lp) +get_sbuf_line(lp) line_t *lp; { + static char *sfbuf = NULL; /* buffer */ + static int sfbufsz = 0; /* buffer size */ + int len, ct; - if (lp == &line0) + if (lp == &buffer_head) return NULL; seek_write = 1; /* force seek on write */ /* out of position */ @@ -77,8 +62,8 @@ gettxt(lp) return NULL; } } - len = lp->len & ~ACTV; - CKBUF(sfbuf, sfbufsz, len + 1, NULL); + len = lp->len; + REALLOC(sfbuf, sfbufsz, len + 1, NULL); if ((ct = fread(sfbuf, sizeof(char), len, sfp)) < 0 || ct != len) { fprintf(stderr, "%s\n", strerror(errno)); sprintf(errmsg, "cannot read temp file"); @@ -90,13 +75,10 @@ gettxt(lp) } -extern long curln; -extern long lastln; - -/* puttxt: write a line of text to the scratch file and add a line node +/* put_sbuf_line: write a line of text to the scratch file and add a line node to the editor buffer; return a pointer to the end of the text */ char * -puttxt(cs) +put_sbuf_line(cs) char *cs; { line_t *lp; @@ -115,7 +97,7 @@ puttxt(cs) sprintf(errmsg, "line too long"); return NULL; } - len = (s - cs) & ~ACTV; + len = s - cs; /* out of position */ if (seek_write) { if (fseek(sfp, 0L, SEEK_END) < 0) { @@ -126,7 +108,7 @@ puttxt(cs) sfseek = ftell(sfp); seek_write = 0; } - /* assert: spl1() */ + /* assert: SPL1() */ if ((ct = fwrite(cs, sizeof(char), len, sfp)) < 0 || ct != len) { sfseek = -1; fprintf(stderr, "%s\n", strerror(errno)); @@ -135,37 +117,37 @@ puttxt(cs) } lp->len = len; lp->seek = sfseek; - lpqueue(lp); + add_line_node(lp); sfseek += len; /* update file position */ return ++s; } -/* lpqueue: add a line node in the editor buffer after the current line */ +/* add_line_node: add a line node in the editor buffer after the current line */ void -lpqueue(lp) +add_line_node(lp) line_t *lp; { line_t *cp; - cp = getlp(curln); /* this getlp last! */ - insqueue(lp, cp); - lastln++; - curln++; + cp = get_addressed_line_node(current_addr); /* this get_addressed_line_node last! */ + insque(lp, cp); + addr_last++; + current_addr++; } -/* getaddr: return line number of pointer */ +/* get_line_node_addr: return line number of pointer */ long -getaddr(lp) +get_line_node_addr(lp) line_t *lp; { - line_t *cp = &line0; + line_t *cp = &buffer_head; long n = 0; - while (cp != lp && (cp = cp->next) != &line0) + while (cp != lp && (cp = cp->q_forw) != &buffer_head) n++; - if (n && cp == &line0) { + if (n && cp == &buffer_head) { sprintf(errmsg, "invalid address"); return ERR; } @@ -173,43 +155,47 @@ getaddr(lp) } -/* getlp: return pointer to a line node in the editor buffer */ +/* get_addressed_line_node: return pointer to a line node in the editor buffer */ line_t * -getlp(n) +get_addressed_line_node(n) long n; { - static line_t *lp = &line0; + static line_t *lp = &buffer_head; static long on = 0; - spl1(); + SPL1(); if (n > on) - if (n <= (on + lastln) >> 1) + if (n <= (on + addr_last) >> 1) for (; on < n; on++) - lp = lp->next; + lp = lp->q_forw; else { - lp = line0.prev; - for (on = lastln; on > n; on--) - lp = lp->prev; + lp = buffer_head.q_back; + for (on = addr_last; on > n; on--) + lp = lp->q_back; } else if (n >= on >> 1) for (; on > n; on--) - lp = lp->prev; + lp = lp->q_back; else { - lp = &line0; + lp = &buffer_head; for (on = 0; on < n; on++) - lp = lp->next; + lp = lp->q_forw; } - spl0(); + SPL0(); return lp; } +extern int newline_added; + char sfn[15] = ""; /* scratch file name */ -/* sbopen: open scratch file */ -sbopen() +/* open_sbuf: open scratch file */ +int +open_sbuf() { + isbinary = newline_added = 0; strcpy(sfn, "/tmp/ed.XXXXXX"); if (mktemp(sfn) == NULL || (sfp = fopen(sfn, "w+")) == NULL) { fprintf(stderr, "%s: %s\n", sfn, strerror(errno)); @@ -220,8 +206,9 @@ sbopen() } -/* sbclose: close scratch file */ -sbclose() +/* close_sbuf: close scratch file */ +int +close_sbuf() { if (sfp) { if (fclose(sfp) < 0) { @@ -237,7 +224,7 @@ sbclose() } -/* quit: remove scratch file and exit */ +/* quit: remove_lines scratch file and exit */ void quit(n) int n; @@ -252,23 +239,30 @@ quit(n) unsigned char ctab[256]; /* character translation table */ -/* init_buf: open scratch buffer; initialize line queue */ +/* init_buffers: open scratch buffer; initialize line queue */ void -init_buf() +init_buffers() { int i = 0; - if (sbopen() < 0) + /* Read stdin one character at a time to avoid i/o contention + with shell escapes invoked by nonterminal input, e.g., + ed - <<EOF + !cat + hello, world + EOF */ + setbuffer(stdin, stdinbuf, 1); + if (open_sbuf() < 0) quit(2); - requeue(&line0, &line0); + REQUE(&buffer_head, &buffer_head); for (i = 0; i < 256; i++) ctab[i] = i; } -/* translit: translate characters in a string */ +/* translit_text: translate characters in a string */ char * -translit(s, len, from, to) +translit_text(s, len, from, to) char *s; int len; int from; diff --git a/bin/ed/cbc.c b/bin/ed/cbc.c index 95ce63cd2967..9d1d9894583e 100644 --- a/bin/ed/cbc.c +++ b/bin/ed/cbc.c @@ -1,14 +1,10 @@ /* cbc.c: This file contains the encryption routines for the ed line editor */ /*- - * Copyright (c) 1991 The Regents of the University of California. + * Copyright (c) 1993 The Regents of the University of California. * All rights reserved. * - * This code is derived from software contributed to Berkeley by - * Matt Bishop of Dartmouth College. - * - * The United States Government has rights in this work pursuant - * to contract no. NAG 2-680 between the National Aeronautics and - * Space Administration and Dartmouth College. + * Copyright (c) 1993 Andrew Moore, Talke Studio. + * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,37 +33,22 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * from: @(#)bdes.c 5.5 (Berkeley) 6/27/91 */ #ifndef lint -static char sccsid[] = "@(#)cbc.c 5.5 (Berkeley) 6/27/91"; +static char *rcsid = "@(#)$Id: cbc.c,v 1.2 1994/02/01 00:34:36 alm Exp $"; #endif /* not lint */ -/* Author: Matt Bishop - * Department of Mathematics and Computer Science - * Dartmouth College - * Hanover, NH 03755 - * Email: Matt.Bishop@dartmouth.edu - * ...!decvax!dartvax!Matt.Bishop - * - * See Technical Report PCS-TR91-158, Department of Mathematics and Computer - * Science, Dartmouth College, for a detailed description of the implemen- - * tation and differences between it and Sun's. The DES is described in - * FIPS PUB 46, and the modes in FIPS PUB 81 (see either the manual page - * or the technical report for a complete reference). - */ - +#include <sys/types.h> +#include <ctype.h> #include <errno.h> #include <pwd.h> -#include <unistd.h> -#include <stdio.h> -#include <ctype.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> #include "ed.h" + /* * Define a divisor for rand() that yields a uniform distribution in the * range 0-255. @@ -76,7 +57,7 @@ static char sccsid[] = "@(#)cbc.c 5.5 (Berkeley) 6/27/91"; /* * BSD and System V systems offer special library calls that do - * block moves and fills, so if possible we take advantage of them + * block move_liness and fills, so if possible we take advantage of them */ #define MEMCPY(dest,src,len) memcpy((dest),(src),(len)) #define MEMZERO(dest,len) memset((dest), 0, (len)) @@ -84,10 +65,10 @@ static char sccsid[] = "@(#)cbc.c 5.5 (Berkeley) 6/27/91"; /* Hide the calls to the primitive encryption routines. */ #define DES_KEY(buf) \ if (des_setkey(buf)) \ - err("des_setkey"); + des_error("des_setkey"); #define DES_XFORM(buf) \ if (des_cipher(buf, buf, 0L, (inverse ? -1 : 1))) \ - err("des_cipher"); + des_error("des_cipher"); /* * read/write - no error checking @@ -119,14 +100,14 @@ char bits[] = { /* used to extract bits from a char */ }; int pflag; /* 1 to preserve parity bits */ -char des_buf[8]; /* shared buffer for desgetc/desputc */ -int des_ct = 0; /* count for desgetc/desputc */ -int des_n = 0; /* index for desputc/desgetc */ +unsigned char des_buf[8]; /* shared buffer for get_des_char/put_des_char */ +int des_ct = 0; /* count for get_des_char/put_des_char */ +int des_n = 0; /* index for put_des_char/get_des_char */ -/* desinit: initialize DES */ +/* init_des_cipher: initialize DES */ void -desinit() +init_des_cipher() { #ifdef DES int i; @@ -144,28 +125,30 @@ desinit() } -/* desgetc: return next char in an encrypted file */ -desgetc(fp) +/* get_des_char: return next char in an encrypted file */ +int +get_des_char(fp) FILE *fp; { #ifdef DES if (des_n >= des_ct) { des_n = 0; - des_ct = cbcdec(des_buf, fp); + des_ct = cbc_decode(des_buf, fp); } return (des_ct > 0) ? des_buf[des_n++] : EOF; #endif } -/* desputc: write a char to an encrypted file; return char written */ -desputc(c, fp) +/* put_des_char: write a char to an encrypted file; return char written */ +int +put_des_char(c, fp) int c; FILE *fp; { #ifdef DES if (des_n == sizeof des_buf) { - des_ct = cbcenc(des_buf, des_n, fp); + des_ct = cbc_encode(des_buf, des_n, fp); des_n = 0; } return (des_ct >= 0) ? (des_buf[des_n++] = c) : EOF; @@ -173,16 +156,17 @@ desputc(c, fp) } -/* desflush: flush an encrypted file's output; return status */ -desflush(fp) +/* flush_des_file: flush an encrypted file's output; return status */ +int +flush_des_file(fp) FILE *fp; { #ifdef DES if (des_n == sizeof des_buf) { - des_ct = cbcenc(des_buf, des_n, fp); + des_ct = cbc_encode(des_buf, des_n, fp); des_n = 0; } - return (des_ct >= 0 && cbcenc(des_buf, des_n, fp) >= 0) ? 0 : EOF; + return (des_ct >= 0 && cbc_encode(des_buf, des_n, fp) >= 0) ? 0 : EOF; #endif } @@ -190,7 +174,8 @@ desflush(fp) /* * get keyword from tty or stdin */ -getkey() +int +get_keyword() { register char *p; /* used to obtain the key */ Desbuf msgbuf; /* I/O buffer */ @@ -203,9 +188,9 @@ getkey() /* * copy it, nul-padded, into the key area */ - cvtkey(BUFFER(msgbuf), p); + expand_des_key(BUFFER(msgbuf), p); MEMZERO(p, _PASSWORD_LEN); - makekey(msgbuf); + set_des_key(msgbuf); MEMZERO(msgbuf, sizeof msgbuf); return 1; } @@ -213,13 +198,11 @@ getkey() } -extern char errmsg[]; - /* * print a warning message and, possibly, terminate */ void -err(s) +des_error(s) char *s; /* the message */ { (void)sprintf(errmsg, "%s", s ? s : strerror(errno)); @@ -228,7 +211,8 @@ err(s) /* * map a hex character to an integer */ -tobinhex(c, radix) +int +hex_to_binary(c, radix) int c; /* char to be converted */ int radix; /* base (2 to 16) */ { @@ -260,7 +244,7 @@ tobinhex(c, radix) * convert the key to a bit pattern */ void -cvtkey(obuf, ibuf) +expand_des_key(obuf, ibuf) char *obuf; /* bit pattern */ char *ibuf; /* the key itself */ { @@ -276,8 +260,8 @@ cvtkey(obuf, ibuf) * now translate it, bombing on any illegal hex digit */ for (i = 0; ibuf[i] && i < 16; i++) - if ((nbuf[i] = tobinhex((int) ibuf[i], 16)) == -1) - err("bad hex digit in key"); + if ((nbuf[i] = hex_to_binary((int) ibuf[i], 16)) == -1) + des_error("bad hex digit in key"); while (i < 16) nbuf[i++] = 0; for (i = 0; i < 8; i++) @@ -296,8 +280,8 @@ cvtkey(obuf, ibuf) * now translate it, bombing on any illegal binary digit */ for (i = 0; ibuf[i] && i < 16; i++) - if ((nbuf[i] = tobinhex((int) ibuf[i], 2)) == -1) - err("bad binary digit in key"); + if ((nbuf[i] = hex_to_binary((int) ibuf[i], 2)) == -1) + des_error("bad binary digit in key"); while (i < 64) nbuf[i++] = 0; for (i = 0; i < 8; i++) @@ -328,7 +312,7 @@ cvtkey(obuf, ibuf) * DES ignores the low order bit of each character. */ void -makekey(buf) +set_des_key(buf) Desbuf buf; /* key block */ { register int i, j; /* counter in a for loop */ @@ -357,7 +341,8 @@ makekey(buf) /* * This encrypts using the Cipher Block Chaining mode of DES */ -cbcenc(msgbuf, n, fp) +int +cbc_encode(msgbuf, n, fp) char *msgbuf; int n; FILE *fp; @@ -395,7 +380,8 @@ cbcenc(msgbuf, n, fp) /* * This decrypts using the Cipher Block Chaining mode of DES */ -cbcdec(msgbuf, fp) +int +cbc_decode(msgbuf, fp) char *msgbuf; /* I/O buffer */ FILE *fp; /* input file descriptor */ { @@ -419,7 +405,7 @@ cbcdec(msgbuf, fp) if ((c = fgetc(fp)) == EOF) { n = CHAR(msgbuf, 7); if (n < 0 || n > 7) { - err("decryption failed (block corrupted)"); + des_error("decryption failed (block corrupted)"); return EOF; } } else @@ -427,9 +413,9 @@ cbcdec(msgbuf, fp) return n; } if (n > 0) - err("decryption failed (incomplete block)"); + des_error("decryption failed (incomplete block)"); else if (n < 0) - err("cannot read file"); + des_error("cannot read file"); return EOF; } #endif /* DES */ diff --git a/bin/ed/ed.1 b/bin/ed/ed.1 index e9a318080b39..f7f468304ffb 100644 --- a/bin/ed/ed.1 +++ b/bin/ed/ed.1 @@ -1,20 +1,21 @@ .TH ED 1 "21 May 1993" .SH NAME -ed, red \- text editor +.\" ed, red \- text editor +ed \- text editor .SH SYNOPSIS ed [-] [-sx] [-p \fIstring\fR] [\fIfile\fR] -.LP -red [-] [-sx] [-p \fIstring\fR] [\fIfile\fR] +.\" .LP +.\" red [-] [-sx] [-p \fIstring\fR] [\fIfile\fR] .SH DESCRIPTION .B ed is a line-oriented text editor. It is used to create, display, modify and otherwise manipulate text files. -.B red -is a restricted -.BR ed : -it can only edit files in the current -directory and cannot execute shell commands. +.\" .B red +.\" is a restricted +.\" .BR ed : +.\" it can only edit files in the current +.\" directory and cannot execute shell commands. If invoked with a .I file @@ -45,7 +46,7 @@ A typical command might look like: ,s/\fIold\fR/\fInew\fR/g .RE .sp -which replaces all occurences of the string +which replaces all occurrences of the string .I old with .IR new . @@ -134,7 +135,7 @@ The default filename is set to only if it is not prefixed with a bang. .SS LINE ADDRESSING -An address represents the number of line in the buffer. +An address represents the number of a line in the buffer. .B ed maintains a .I current address @@ -165,25 +166,26 @@ and is legal wherever it makes sense. An address range is two addresses separated either by a comma or semi-colon. The value of the first address in a range cannot exceed the -value of the the second. If an +value of the the second. If only one address is given in a range, then +the second address is set to the given address. If an .IR n- tuple of addresses is given where .I n > 2, -then the corresponding range is determined by the last two addresses -in the +then the corresponding range is determined by the last two addresses in +the .IR n- tuple. -If only one address is expected, then the last -address is used. +If only one address is expected, then the last address is used. Each address in a comma-delimited range is interpreted relative to the current address. In a semi-colon-delimited range, the first address is used to set the current address, and the second address is interpreted relative to the first. + The following address symbols are recognized. .TP 8 -\fR.\fR +\&. The current line (address) in the buffer. .TP 8 @@ -511,7 +513,9 @@ The current address is set to the last line read. .RI e \ !command Edits the standard output of .IR `!command' , -executed as described below. +(see +.RI ! command +below). The default filename is unchanged. Any lines in the buffer are deleted before the output of .I command @@ -629,6 +633,12 @@ deleted or otherwise modified. .TP 8 (.,.)l Prints the addressed lines unambiguously. +If a single line fills for than one screen (as might be the case +when viewing a binary file, for instance), a `--More--' +prompt is printed on the last line. +.B ed +waits until the RETURN key is pressed +before displaying the next screen. The current address is set to the last line printed. @@ -689,7 +699,9 @@ Reads to after the addressed line the standard output of .IR `!command' , -executed as described below. +(see the +.RI ! command +below). The default filename is unchanged. The current address is set to the last line read. @@ -707,14 +719,14 @@ matching a regular expression with .IR replacement . By default, only the first match in each line is replaced. -The +If the .I `g' -(global) suffix causes every match to be replaced. +(global) suffix is given, then every match to be replaced. The .I `n' suffix, where .I n -is a postive number, causes only the +is a positive number, causes only the .IR n th match to be replaced. It is an error if no substitutions are performed on any of the addressed @@ -724,7 +736,10 @@ The current address is set the last line affected. .I re and .I replacement -may be delimited by any character other than space and newline. +may be delimited by any character other than space and newline +(see the +.I `s' +command below). If one or two of the last delimiters is omitted, then the last line affected is printed as though the print suffix .I `p' @@ -755,12 +770,18 @@ if they are escaped with a backslash (\\). Repeats the last substitution. This form of the .I `s' -command may be suffixed with -any combination of the characters +command accepts a count suffix +.IR `n' , +or any combination of the characters .IR `r' , .IR `g' , and .IR `p' . +If a count suffix +.I `n' +is given, then only the +.IR n th +match is replaced. The .I `r' suffix causes @@ -840,7 +861,9 @@ command. .RI (1,$)w \ !command Writes the addressed lines to the standard input of .IR `!command' , -executed as described below. +(see the +.RI ! command +below). The default filename and current address are unchanged. .TP 8 @@ -891,13 +914,6 @@ is printed to the standard output. The current line is unchanged. .TP 8 -.RI (.,.)! command -Replaces the addressed lines with the output of -.I `!command' -as described above. -The current address is set to the last line read. - -.TP 8 ($)= Prints the line number of the addressed line. diff --git a/bin/ed/ed.c b/bin/ed/ed.c deleted file mode 100644 index 180232770d82..000000000000 --- a/bin/ed/ed.c +++ /dev/null @@ -1,2206 +0,0 @@ -/* ed.c: This file contains the main control and user-interface routines - for the ed line editor. */ -/*- - * Copyright (c) 1993 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley - * by Andrew Moore, Talke Studio. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -/*- - * Kernighan/Plauger, "Software Tools in Pascal," (c) 1981 by - * Addison-Wesley Publishing Company, Inc. Reprinted with permission of - * the publisher. - */ - -#ifndef lint -char copyright1[] = -"@(#) Copyright (c) 1993 The Regents of the University of California.\n\ - All rights reserved.\n"; -char copyright2[] = -"@(#) Kernighan/Plauger, Software Tools in Pascal, (c) 1981 by\n\ - Addison-Wesley Publishing Company, Inc. Reprinted with permission of\n\ - the publisher.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)ed.c 5.5 (Berkeley) 3/28/93"; -#endif /* not lint */ - -/* - * CREDITS - * The buf.c algorithm is attributed to Rodney Ruddock of - * the University of Guelph, Guelph, Ontario. - * - * The cbc.c encryption code is adapted from - * the bdes program by Matt Bishop of Dartmouth College, - * Hanover, NH. - * - * Addison-Wesley Publishing Company generously granted - * permission to distribute this program over Internet. - * - */ - -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <ctype.h> -#include <setjmp.h> -#include <pwd.h> -#include <sys/ioctl.h> - -#include "ed.h" - -#ifdef _POSIX_SOURCE -sigjmp_buf env; -#else -jmp_buf env; -#endif - -/* static buffers */ -char *shcmd; /* shell command buffer */ -int shcmdsz; /* shell command buffer size */ -int shcmdi; /* shell command buffer index */ -char *cvbuf; /* global command buffer */ -int cvbufsz; /* global command buffer size */ -char *lhbuf; /* lhs buffer */ -int lhbufsz; /* lhs buffer size */ -char *rhbuf; /* rhs buffer */ -int rhbufsz; /* rhs buffer size */ -int rhbufi; /* rhs buffer index */ -char *rbuf; /* regsub buffer */ -int rbufsz; /* regsub buffer size */ -char *sbuf; /* file i/o buffer */ -int sbufsz; /* file i/o buffer size */ -char *ibuf; /* ed command-line buffer */ -int ibufsz; /* ed command-line buffer size */ -char *ibufp; /* pointer to ed command-line buffer */ - -/* global flags */ -int isbinary; /* if set, buffer contains ASCII NULs */ -int modified; /* if set, buffer modified since last write */ -int garrulous = 0; /* if set, print all error messages */ -int scripted = 0; /* if set, suppress diagnostics */ -int des = 0; /* if set, use crypt(3) for i/o */ -int mutex = 0; /* if set, signals set "sigflags" */ -int sigflags = 0; /* if set, signals received while mutex set */ -int sigactive = 0; /* if set, signal handlers are enabled */ -int red = 0; /* if set, restrict shell/directory access */ - -char dfn[MAXFNAME + 1] = ""; /* default filename */ -long curln; /* current address */ -long lastln; /* last address */ -int lineno; /* script line number */ -char *prompt; /* command-line prompt */ -char *dps = "*"; /* default command-line prompt */ - -char *usage = "usage: %s [-] [-sx] [-p string] [name]\n"; - -extern char errmsg[]; -extern int optind; -extern char *optarg; - -/* ed: line editor */ -main(argc, argv) - int argc; - char **argv; -{ - int c, n; - long status = 0; - - red = (n = strlen(argv[0])) > 2 && argv[0][n - 3] == 'r'; -top: - while ((c = getopt(argc, argv, "p:sx")) != EOF) - switch(c) { - case 'p': /* set prompt */ - prompt = optarg; - break; - case 's': /* run script */ - scripted = 1; - break; - case 'x': /* use crypt */ -#ifdef DES - des = getkey(); -#else - fprintf(stderr, "crypt unavailable\n?\n"); -#endif - break; - - default: - fprintf(stderr, usage, argv[0]); - exit(1); - } - argv += optind; - argc -= optind; - if (argc && **argv == '-') { - scripted = 1; - if (argc > 1) { - optind = 1; - goto top; - } - argv++; - argc--; - } - /* assert: reliable signals! */ -#ifdef SIGWINCH - dowinch(SIGWINCH); - if (isatty(0)) signal(SIGWINCH, dowinch); -#endif - signal(SIGHUP, onhup); - signal(SIGQUIT, SIG_IGN); - signal(SIGINT, onintr); -#ifdef _POSIX_SOURCE - if (status = sigsetjmp(env, 1)) -#else - if (status = setjmp(env)) -#endif - { - fputs("\n?\n", stderr); - sprintf(errmsg, "interrupt"); - } else { - init_buf(); - sigactive = 1; /* enable signal handlers */ - if (argc && **argv && ckfn(*argv)) { - if (doread(0, *argv) < 0 && !isatty(0)) - quit(2); - else if (**argv != '!') - strcpy(dfn, *argv); - } else if (argc) { - fputs("?\n", stderr); - if (**argv == '\0') - sprintf(errmsg, "invalid filename"); - if (!isatty(0)) - quit(2); - } - } - for (;;) { - if (status < 0 && garrulous) - fprintf(stderr, "%s\n", errmsg); - if (prompt) { - printf("%s", prompt); - fflush(stdout); - } - if ((n = getline()) < 0) { - status = ERR; - continue; - } else if (n == 0) { - if (modified && !scripted) { - fputs("?\n", stderr); - sprintf(errmsg, "warning: file modified"); - if (!isatty(0)) { - fprintf(stderr, garrulous ? "script, line %d: %s\n" - : "", lineno, errmsg); - quit(2); - } - clearerr(stdin); - modified = 0; - status = EMOD; - continue; - } else - quit(0); - } else if (ibuf[n - 1] != '\n') { - /* discard line */ - sprintf(errmsg, "unexpected end-of-file"); - clearerr(stdin); - status = ERR; - continue; - } - if ((n = getlist()) >= 0 && (status = ckglob()) != 0) { - if (status > 0 && (status = doglob(status)) >= 0) { - curln = status; - continue; - } - } else if ((status = n) >= 0 && (status = docmd(0)) >= 0) { - if (!status || status - && (status = doprint(curln, curln, status)) >= 0) - continue; - } - switch (status) { - case EOF: - quit(0); - case EMOD: - modified = 0; - fputs("?\n", stderr); /* give warning */ - sprintf(errmsg, "warning: file modified"); - if (!isatty(0)) { - fprintf(stderr, garrulous ? "script, line %d: %s\n" : "", lineno, errmsg); - quit(2); - } - break; - case FATAL: - if (!isatty(0)) - fprintf(stderr, garrulous ? "script, line %d: %s\n" : "", lineno, errmsg); - else - fprintf(stderr, garrulous ? "%s\n" : "", errmsg); - quit(3); - default: - fputs("?\n", stderr); - if (!isatty(0)) { - fprintf(stderr, garrulous ? "script, line %d: %s\n" : "", lineno, errmsg); - quit(2); - } - break; - } - } - /*NOTREACHED*/ -} - - -long line1, line2, nlines; - -/* getlist: get line numbers from the command buffer until an illegal - address is seen. return range status */ -getlist() -{ - long num; - - nlines = line2 = 0; - while ((num = getone()) >= 0) { - line1 = line2; - line2 = num; - nlines++; - if (*ibufp != ',' && *ibufp != ';') - break; - else if (*ibufp++ == ';') - curln = num; - } - nlines = min(nlines, 2); - if (nlines == 0) - line2 = curln; - if (nlines <= 1) - line1 = line2; - return (num == ERR) ? ERR : nlines; -} - - -/* getone: return the next line number in the command buffer */ -long -getone() -{ - int c; - long i, num; - - if ((num = getnum(1)) < 0) - return num; - for (;;) { - c = isspace(*ibufp); - skipblanks(); - c = c && isdigit(*ibufp); - if (!c && *ibufp != '+' && *ibufp != '-' && *ibufp != '^') - break; - c = c ? '+' : *ibufp++; - if ((i = getnum(0)) < 0) { - sprintf(errmsg, "invalid address"); - return i; - } - if (c == '+') - num += i; - else num -= i; - } - if (num > lastln || num < 0) { - sprintf(errmsg, "invalid address"); - return ERR; - } - return num; -} - - -/* getnum: return a relative line number from the command buffer */ -long -getnum(first) - int first; -{ - pattern_t *pat; - char c; - - skipblanks(); - if (isdigit(*ibufp)) - return strtol(ibufp, &ibufp, 10); - switch(c = *ibufp) { - case '.': - ibufp++; - return first ? curln : ERR; - case '$': - ibufp++; - return first ? lastln : ERR; - case '/': - case '?': - if ((pat = optpat()) == NULL) - return ERR; - else if (*ibufp == c) - ibufp++; - return first ? patscan(pat, (c == '/') ? 1 : 0) : ERR; - case '^': - case '-': - case '+': - return first ? curln : 1; - case '\'': - ibufp++; - return first ? getmark(*ibufp++) : ERR; - case '%': - case ',': - case ';': - if (first) { - ibufp++; - line2 = (c == ';') ? curln : 1; - nlines++; - return lastln; - } - return 1; - default: - return first ? EOF : 1; - } -} - - -/* gflags */ -#define GLB 001 /* global command */ -#define GPR 002 /* print after command */ -#define GLS 004 /* list after command */ -#define GNP 010 /* enumerate after command */ -#define GSG 020 /* global substitute */ - - -/* VRFYCMD: verify the command suffix in the command buffer */ -#define VRFYCMD() { \ - int done = 0; \ - do { \ - switch(*ibufp) { \ - case 'p': \ - gflag |= GPR, ibufp++; \ - break; \ - case 'l': \ - gflag |= GLS, ibufp++; \ - break; \ - case 'n': \ - gflag |= GNP, ibufp++; \ - break; \ - default: \ - done++; \ - } \ - } while (!done); \ - if (*ibufp++ != '\n') { \ - sprintf(errmsg, "invalid command suffix"); \ - return ERR; \ - } \ -} - - -/* ckglob: set lines matching a pattern in the command buffer; return - global status */ -ckglob() -{ - pattern_t *pat; - char c, delim; - char *s; - int nomatch; - long n; - line_t *lp; - int gflag = 0; /* print suffix of interactive cmd */ - - if ((c = *ibufp) == 'V' || c == 'G') - gflag = GLB; - else if (c != 'g' && c != 'v') - return 0; - if (ckrange(1, lastln) < 0) - return ERR; - else if ((delim = *++ibufp) == ' ' || delim == '\n') { - sprintf(errmsg, "invalid pattern delimiter"); - return ERR; - } else if ((pat = optpat()) == NULL) - return ERR; - else if (*ibufp == delim) - ibufp++; - if (gflag) - VRFYCMD(); /* get print suffix */ - for (lp = getlp(n = 1); n <= lastln; n++, lp = lp->next) { - if ((s = gettxt(lp)) == NULL) - return ERR; - lp->len &= ~ACTV; /* zero ACTV bit */ - if (isbinary) - s = nultonl(s, lp->len & ~ACTV); - if (line1 <= n && n <= line2 - && (!(nomatch = regexec(pat, s, 0, NULL, 0)) - && (c == 'g' || c == 'G') - || nomatch && (c == 'v' || c == 'V'))) - lp->len |= ACTV; - } - return gflag | GSG; -} - - -/* doglob: apply command list in the command buffer to the active - lines in a range; return command status */ -long -doglob(gflag) - int gflag; -{ - static char *ocmd = NULL; - static int ocmdsz = 0; - - line_t *lp = NULL; - long lc; - int status; - int n; - int interact = gflag & ~GSG; /* GLB & gflag ? */ - char *cmd = NULL; - -#ifdef BACKWARDS - if (!interact) - if (!strcmp(ibufp, "\n")) - cmd = "p\n"; /* null cmd-list == `p' */ - else if ((cmd = getcmdv(&n, 0)) == NULL) - return ERR; -#else - if (!interact && (cmd = getcmdv(&n, 0)) == NULL) - return ERR; -#endif - ureset(); - for (;;) { - for (lp = getlp(lc = 1); lc <= lastln; lc++, lp = lp->next) - if (lp->len & ACTV) /* active line */ - break; - if (lc > lastln) - break; - lp->len ^= ACTV; /* zero ACTV bit */ - curln = lc; - if (interact) { - /* print curln and get a command in global syntax */ - if (doprint(curln, curln, 0) < 0) - return ERR; - while ((n = getline()) > 0 - && ibuf[n - 1] != '\n') - clearerr(stdin); - if (n < 0) - return ERR; - else if (n == 0) { - sprintf(errmsg, "unexpected end-of-file"); - return ERR; - } else if (n == 1 && !strcmp(ibuf, "\n")) - continue; - else if (n == 2 && !strcmp(ibuf, "&\n")) { - if (cmd == NULL) { - sprintf(errmsg, "no previous command"); - return ERR; - } else cmd = ocmd; - } else if ((cmd = getcmdv(&n, 0)) == NULL) - return ERR; - else { - CKBUF(ocmd, ocmdsz, n + 1, ERR); - memcpy(ocmd, cmd, n + 1); - cmd = ocmd; - } - - } - ibufp = cmd; - for (; *ibufp;) - if ((status = getlist()) < 0 - || (status = docmd(1)) < 0 - || (status > 0 - && (status = doprint(curln, curln, status)) < 0)) - return status; - } - return ((interact & ~GLB ) && doprint(curln, curln, interact) < 0) ? ERR : curln; -} - - -#ifdef BACKWARDS -/* GETLINE3: get a legal address from the command buffer */ -#define GETLINE3(num) \ -{ \ - long ol1, ol2; \ -\ - ol1 = line1, ol2 = line2; \ - if (getlist() < 0) \ - return ERR; \ - else if (nlines == 0) { \ - sprintf(errmsg, "destination expected"); \ - return ERR; \ - } else if (line2 < 0 || lastln < line2) { \ - sprintf(errmsg, "invalid address"); \ - return ERR; \ - } \ - num = line2; \ - line1 = ol1, line2 = ol2; \ -} -#else /* BACKWARDS */ -/* GETLINE3: get a legal address from the command buffer */ -#define GETLINE3(num) \ -{ \ - long ol1, ol2; \ -\ - ol1 = line1, ol2 = line2; \ - if (getlist() < 0) \ - return ERR; \ - if (line2 < 0 || lastln < line2) { \ - sprintf(errmsg, "invalid address"); \ - return ERR; \ - } \ - num = line2; \ - line1 = ol1, line2 = ol2; \ -} -#endif - -/* sgflags */ -#define SGG 001 /* complement previous global substitute suffix */ -#define SGP 002 /* complement previous print suffix */ -#define SGR 004 /* use last regex instead of last pat */ -#define SGF 010 /* newline found */ - -long ucurln = -1; /* if >= 0, undo enabled */ -long ulastln = -1; /* if >= 0, undo enabled */ -int patlock = 0; /* if set, pattern not released by optpat() */ - -long rows = 22; /* scroll length: ws_row - 2 */ - -/* docmd: execute the next command in command buffer; return print - request, if any */ -docmd(glob) - int glob; -{ - static pattern_t *pat = NULL; - static int sgflag = 0; - - pattern_t *tpat; - char *fnp; - int gflag = 0; - int sflags = 0; - long num = 0; - int n = 0; - int c; - - skipblanks(); - switch(c = *ibufp++) { - case 'a': - VRFYCMD(); - if (!glob) ureset(); - if (append(line2, glob) < 0) - return ERR; - break; - case 'c': - if (ckrange(curln, curln) < 0) - return ERR; - VRFYCMD(); - if (!glob) ureset(); - if (lndelete(line1, line2) < 0 || append(curln, glob) < 0) - return ERR; - break; - case 'd': - if (ckrange(curln, curln) < 0) - return ERR; - VRFYCMD(); - if (!glob) ureset(); - if (lndelete(line1, line2) < 0) - return ERR; - else if (nextln(curln, lastln) != 0) - curln = nextln(curln, lastln); - modified = 1; - break; - case 'e': - if (modified && !scripted) - return EMOD; - /* fall through */ - case 'E': - if (nlines > 0) { - sprintf(errmsg, "unexpected address"); - return ERR; - } else if (!isspace(*ibufp)) { - sprintf(errmsg, "unexpected command suffix"); - return ERR; - } else if ((fnp = getfn()) == NULL) - return ERR; - VRFYCMD(); - if (lndelete(1, lastln) < 0) - return ERR; - ureset(); - if (sbclose() < 0) - return ERR; - else if (sbopen() < 0) - return FATAL; - if (*fnp && *fnp != '!') strcpy(dfn, fnp); -#ifdef BACKWARDS - if (*fnp == '\0' && *dfn == '\0') { - sprintf(errmsg, "no current filename"); - return ERR; - } -#endif - if (doread(0, *fnp ? fnp : dfn) < 0) - return ERR; - ureset(); - modified = 0; - ucurln = ulastln = -1; - break; - case 'f': - if (nlines > 0) { - sprintf(errmsg, "unexpected address"); - return ERR; - } else if (!isspace(*ibufp)) { - sprintf(errmsg, "unexpected command suffix"); - return ERR; - } else if ((fnp = getfn()) == NULL) - return ERR; - else if (*fnp == '!') { - sprintf(errmsg, "invalid redirection"); - return ERR; - } - VRFYCMD(); - if (*fnp) strcpy(dfn, fnp); - printf("%s\n", esctos(dfn)); - break; - case 'g': - case 'G': - sprintf(errmsg, "cannot nest global commands"); - return ERR; - case 'h': - if (nlines > 0) { - sprintf(errmsg, "unexpected address"); - return ERR; - } - VRFYCMD(); - if (*errmsg) fprintf(stderr, "%s\n", errmsg); - break; - case 'H': - if (nlines > 0) { - sprintf(errmsg, "unexpected address"); - return ERR; - } - VRFYCMD(); - if ((garrulous = 1 - garrulous) && *errmsg) - fprintf(stderr, "%s\n", errmsg); - break; - case 'i': - if (line2 == 0) { - sprintf(errmsg, "invalid address"); - return ERR; - } - VRFYCMD(); - if (!glob) ureset(); - if (append(prevln(line2, lastln), glob) < 0) - return ERR; - break; - case 'j': - if (ckrange(curln, curln + 1) < 0) - return ERR; - VRFYCMD(); - if (!glob) ureset(); - if (line1 != line2 && join(line1, line2) < 0) - return ERR; - break; - case 'k': - c = *ibufp++; - if (line2 == 0) { - sprintf(errmsg, "invalid address"); - return ERR; - } - VRFYCMD(); - if (putmark(c, getlp(line2)) < 0) - return ERR; - break; - case 'l': - if (ckrange(curln, curln) < 0) - return ERR; - VRFYCMD(); - if (doprint(line1, line2, gflag | GLS) < 0) - return ERR; - gflag = 0; - break; - case 'm': - if (ckrange(curln, curln) < 0) - return ERR; - GETLINE3(num); - if (line1 <= num && num < line2) { - sprintf(errmsg, "invalid destination"); - return ERR; - } - VRFYCMD(); - if (!glob) ureset(); - if (move(num, glob) < 0) - return ERR; - else - modified = 1; - break; - case 'n': - if (ckrange(curln, curln) < 0) - return ERR; - VRFYCMD(); - if (doprint(line1, line2, gflag | GNP) < 0) - return ERR; - gflag = 0; - break; - case 'p': - if (ckrange(curln, curln) < 0) - return ERR; - VRFYCMD(); - if (doprint(line1, line2, gflag | GPR) < 0) - return ERR; - gflag = 0; - break; - case 'P': - if (nlines > 0) { - sprintf(errmsg, "unexpected address"); - return ERR; - } - VRFYCMD(); - prompt = prompt ? NULL : optarg ? optarg : dps; - break; - case 'q': - case 'Q': - if (nlines > 0) { - sprintf(errmsg, "unexpected address"); - return ERR; - } - VRFYCMD(); - gflag = (modified && !scripted && c == 'q') ? EMOD : EOF; - break; - case 'r': - if (!isspace(*ibufp)) { - sprintf(errmsg, "unexpected command suffix"); - return ERR; - } else if (nlines == 0) - line2 = lastln; - if ((fnp = getfn()) == NULL) - return ERR; - VRFYCMD(); - if (!glob) ureset(); - if (*dfn == '\0' && *fnp != '!') strcpy(dfn, fnp); -#ifdef BACKWARDS - if (*fnp == '\0' && *dfn == '\0') { - sprintf(errmsg, "no current filename"); - return ERR; - } -#endif - if ((num = doread(line2, *fnp ? fnp : dfn)) < 0) - return ERR; - else if (num && num != lastln) - modified = 1; - break; - case 's': - do { - switch(*ibufp) { - case '\n': - sflags |=SGF; - break; - case 'g': - sflags |= SGG; - ibufp++; - break; - case 'p': - sflags |= SGP; - ibufp++; - break; - case 'r': - sflags |= SGR; - ibufp++; - break; - default: - if (sflags) { - sprintf(errmsg, "invalid command suffix"); - return ERR; - } - } - } while (sflags && *ibufp != '\n'); - if (sflags && !pat) { - sprintf(errmsg, "no previous substitution"); - return ERR; - } else if (!(sflags & SGF)) - sgflag &= 0xff; - if (*ibufp != '\n' && *(ibufp + 1) == '\n') { - sprintf(errmsg, "invalid pattern delimiter"); - return ERR; - } - tpat = pat; - spl1(); - if ((!sflags || (sflags & SGR)) - && (tpat = optpat()) == NULL) - return ERR; - else if (tpat != pat) { - if (pat) { - regfree(pat); - free(pat); - } - pat = tpat; - patlock = 1; /* reserve pattern */ - } else if (pat == NULL) { - /* NOTREACHED */ - sprintf(errmsg, "no previous substitution"); - return ERR; - } - spl0(); - if (!sflags && (sgflag = getrhs(glob)) < 0) - return ERR; - else if (glob) - sgflag |= GLB; - else - sgflag &= ~GLB; - if (sflags & SGG) - sgflag ^= GSG; - if (sflags & SGP) - sgflag ^= GPR, sgflag &= ~(GLS | GNP); - do { - switch(*ibufp) { - case 'p': - sgflag |= GPR, ibufp++; - break; - case 'l': - sgflag |= GLS, ibufp++; - break; - case 'n': - sgflag |= GNP, ibufp++; - break; - default: - n++; - } - } while (!n); - if (ckrange(curln, curln) < 0) - return ERR; - VRFYCMD(); - if (!glob) ureset(); - if ((n = subst(pat, sgflag)) < 0) - return ERR; - else if (n) - modified = 1; - break; - case 't': - if (ckrange(curln, curln) < 0) - return ERR; - GETLINE3(num); - VRFYCMD(); - if (!glob) ureset(); - if (transfer(num) < 0) - return ERR; - modified = 1; - break; - case 'u': - if (nlines > 0) { - sprintf(errmsg, "unexpected address"); - return ERR; - } - VRFYCMD(); - if (undo(glob) < 0) - return ERR; - break; - case 'v': - case 'V': - sprintf(errmsg, "cannot nest global commands"); - return ERR; - case 'w': - case 'W': - if ((n = *ibufp) == 'q' || n == 'Q') { - gflag = EOF; - ibufp++; - } - if (!isspace(*ibufp)) { - sprintf(errmsg, "unexpected command suffix"); - return ERR; - } else if ((fnp = getfn()) == NULL) - return ERR; - if (nlines == 0 && !lastln) - line1 = line2 = 0; - else if (ckrange(1, lastln) < 0) - return ERR; - VRFYCMD(); - if (*dfn == '\0' && *fnp != '!') strcpy(dfn, fnp); -#ifdef BACKWARDS - if (*fnp == '\0' && *dfn == '\0') { - sprintf(errmsg, "no current filename"); - return ERR; - } -#endif - if ((num = dowrite(line1, line2, *fnp ? fnp : dfn, (c == 'W') ? "a" : "w")) < 0) - return ERR; - else if (num == lastln) - modified = 0; - else if (modified && !scripted && n == 'q') - gflag = EMOD; - break; - case 'x': - if (nlines > 0) { - sprintf(errmsg, "unexpected address"); - return ERR; - } - VRFYCMD(); -#ifdef DES - des = getkey(); -#else - sprintf(errmsg, "crypt unavailable"); - return ERR; -#endif - break; - case 'z': -#ifdef BACKWARDS - if (ckrange(line1 = 1, curln + 1) < 0) -#else - if (ckrange(line1 = 1, curln + !glob) < 0) -#endif - return ERR; - else if ('0' < *ibufp && *ibufp <= '9') - rows = strtol(ibufp, &ibufp, 10); - VRFYCMD(); - if (doprint(line2, min(lastln, line2 + rows - 1), gflag) < 0) - return ERR; - gflag = 0; - break; - case '=': - VRFYCMD(); - printf("%d\n", nlines ? line2 : lastln); - break; - case '!': -#ifndef VI_BANG - if (nlines > 0) { - sprintf(errmsg, "unexpected address"); - return ERR; - } -#endif - if ((sflags = getshcmd()) < 0) - return ERR; - VRFYCMD(); - if (sflags) printf("%s\n", shcmd + 1); -#ifdef VI_BANG - if (nlines == 0) { -#endif - system(shcmd + 1); - if (!scripted) printf("!\n"); - break; -#ifdef VI_BANG - } - if (!lastln && !line1 && !line2) { - if (!glob) ureset(); - } else if (ckrange(curln, curln) < 0) - return ERR; - else { - if (!glob) ureset(); - if (lndelete(line1, line2) < 0) - return ERR; - line2 = curln; - modified = 1; - } - if ((num = doread(line2, shcmd)) < 0) - return ERR; - else if (num && num != lastln) - modified = 1; - break; -#endif - case '\n': -#ifdef BACKWARDS - if (ckrange(line1 = 1, curln + 1) < 0 -#else - if (ckrange(line1 = 1, curln + !glob) < 0 -#endif - || doprint(line2, line2, 0) < 0) - return ERR; - break; - default: - sprintf(errmsg, "unknown command"); - return ERR; - } - return gflag; -} - - -/* ckrange: return status of line number range check */ -ckrange(def1, def2) - long def1, def2; -{ - if (nlines == 0) { - line1 = def1; - line2 = def2; - } - if (line1 > line2 || 1 > line1 || line2 > lastln) { - sprintf(errmsg, "invalid address"); - return ERR; - } - return 0; -} - - -/* patscan: return the number of the next line matching a pattern in a - given direction. wrap around begin/end of line queue if necessary */ -long -patscan(pat, dir) - pattern_t *pat; - int dir; -{ - char *s; - long n = curln; - line_t *lp; - - do { - if (n = dir ? nextln(n, lastln) : prevln(n, lastln)) { - if ((s = gettxt(lp = getlp(n))) == NULL) - return ERR; - if (isbinary) - s = nultonl(s, lp->len & ~ACTV); - if (!regexec(pat, s, 0, NULL, 0)) - return n; - } - } while (n != curln); - sprintf(errmsg, "no match"); - return ERR; -} - - -/* getfn: return pointer to copy of filename in the command buffer */ -char * -getfn() -{ - static char *file = NULL; - static int filesz = 0; - - int n; - - if (*ibufp != '\n') { - skipblanks(); - if (*ibufp == '\n') { - sprintf(errmsg, "invalid filename"); - return NULL; - } else if ((ibufp = getcmdv(&n, 1)) == NULL) - return NULL; -#ifdef VI_BANG - else if (*ibufp == '!') { - ibufp++; - if ((n = getshcmd()) < 0) - return NULL; - if (n) printf("%s\n", shcmd + 1); - return shcmd; - } -#endif - else if (n - 1 > MAXFNAME) { - sprintf(errmsg, "filename too long"); - return NULL; - } - } -#ifndef BACKWARDS - else if (*dfn == '\0') { - sprintf(errmsg, "no current filename"); - return NULL; - } -#endif - CKBUF(file, filesz, MAXFNAME + 1, NULL); - for (n = 0; *ibufp != '\n';) - file[n++] = *ibufp++; - file[n] = '\0'; - return ckfn(file); -} - - -/* getrhs: extract substitution template from the command buffer */ -getrhs(glob) - int glob; -{ - char delim; - - if ((delim = *ibufp) == '\n') { - rhbufi = 0; - return GPR; - } else if (makesub(glob) == NULL) - return ERR; - else if (*ibufp == '\n') - return GPR; - else if (*ibufp == delim) - ibufp++; - if ('1' <= *ibufp && *ibufp <= '9') - return (int) strtol(ibufp, &ibufp, 10) << 8; - else if (*ibufp == 'g') { - ibufp++; - return GSG; - } - return 0; -} - - -/* makesub: return pointer to copy of substitution template in the command - buffer */ -char * -makesub(glob) - int glob; -{ - int n = 0; - int i = 0; - char delim = *ibufp++; - char c; - - if (*ibufp == '%' && *(ibufp + 1) == delim) { - ibufp++; - if (!rhbuf) sprintf(errmsg, "no previous substitution"); - return rhbuf; - } - while (*ibufp != delim) { - CKBUF(rhbuf, rhbufsz, i + 2, NULL); - if ((c = rhbuf[i++] = *ibufp++) == '\n' && *ibufp == '\0') { - i--, ibufp--; - break; - } else if (c != '\\') - ; - else if ((rhbuf[i++] = *ibufp++) != '\n') - ; - else if (!glob) { - while ((n = getline()) == 0 - || n > 0 && ibuf[n - 1] != '\n') - clearerr(stdin); - if (n < 0) - return NULL; - } else - /*NOTREACHED*/ - ; - } - CKBUF(rhbuf, rhbufsz, i + 1, NULL); - rhbuf[rhbufi = i] = '\0'; - return rhbuf; -} - - -/* getshcmd: read a shell command up a maximum size from stdin; return - substitution status */ -int -getshcmd() -{ - static char *buf = NULL; - static int n = 0; - - char *s; /* substitution char pointer */ - int i = 0; - int j = 0; - - if (red) { - sprintf(errmsg, "shell access restricted"); - return ERR; - } else if ((s = ibufp = getcmdv(&j, 1)) == NULL) - return ERR; - CKBUF(buf, n, j + 1, ERR); - buf[i++] = '!'; /* prefix command w/ bang */ - while (*ibufp != '\n') - switch (*ibufp) { - default: - CKBUF(buf, n, i + 2, ERR); - buf[i++] = *ibufp; - if (*ibufp++ == '\\') - buf[i++] = *ibufp++; - break; - case '!': - if (s != ibufp) { - CKBUF(buf, n, i + 1, ERR); - buf[i++] = *ibufp++; - } -#ifdef BACKWARDS - else if (shcmd == NULL || *(shcmd + 1) == '\0') -#else - else if (shcmd == NULL) -#endif - { - sprintf(errmsg, "no previous command"); - return ERR; - } else { - CKBUF(buf, n, i + shcmdi, ERR); - for (s = shcmd + 1; s < shcmd + shcmdi;) - buf[i++] = *s++; - s = ibufp++; - } - break; - case '%': - if (*dfn == '\0') { - sprintf(errmsg, "no current filename"); - return ERR; - } - j = strlen(s = esctos(dfn)); - CKBUF(buf, n, i + j, ERR); - while (j--) - buf[i++] = *s++; - s = ibufp++; - break; - } - CKBUF(shcmd, shcmdsz, i + 1, ERR); - memcpy(shcmd, buf, i); - shcmd[shcmdi = i] = '\0'; - return *s == '!' || *s == '%'; -} - - -/* append: insert text from stdin to after line n; stop when either a - single period is read or EOF; return status */ -append(n, glob) - long n; - int glob; -{ - int l; - char *lp = ibuf; - char *eot; - undo_t *up = NULL; - - for (curln = n;;) { - if (!glob) { - if ((l = getline()) < 0) - return ERR; - else if (l == 0 || ibuf[l - 1] != '\n') { - clearerr(stdin); - return l ? EOF : 0; - } - lp = ibuf; - } else if (*(lp = ibufp) == '\0') - return 0; - else { - while (*ibufp++ != '\n') - ; - l = ibufp - lp; - } - if (l == 2 && lp[0] == '.' && lp[1] == '\n') { - return 0; - } - eot = lp + l; - spl1(); - do { - if ((lp = puttxt(lp)) == NULL) { - spl0(); - return ERR; - } else if (up) - up->t = getlp(curln); - else if ((up = upush(UADD, curln, curln)) == NULL) { - spl0(); - return ERR; - } - } while (lp != eot); - spl0(); - modified = 1; - } -} - - -/* subst: change all text matching a pattern in a range of lines according to - a substitution template; return status */ -subst(pat, gflag) - pattern_t *pat; - int gflag; -{ - undo_t *up; - char *txt; - char *eot; - long lc; - int nsubs = 0; - line_t *lp; - int len; - - curln = prevln(line1, lastln); - for (lc = 0; lc <= line2 - line1; lc++) { - lp = getlp(curln = nextln(curln, lastln)); - if ((len = regsub(pat, lp, gflag)) < 0) - return ERR; - else if (len) { - up = NULL; - if (lndelete(curln, curln) < 0) - return ERR; - txt = rbuf; - eot = rbuf + len; - spl1(); - do { - if ((txt = puttxt(txt)) == NULL) { - spl0(); - return ERR; - } else if (up) - up->t = getlp(curln); - else if ((up = upush(UADD, curln, curln)) == NULL) { - spl0(); - return ERR; - } - } while (txt != eot); - spl0(); - nsubs++; - } - } - if (nsubs == 0 && !(gflag & GLB)) { - sprintf(errmsg, "no match"); - return ERR; - } else if ((gflag & (GPR | GLS | GNP)) - && doprint(curln, curln, gflag) < 0) - return ERR; - return 1; -} - - -/* regsub: replace text matched by a pattern according to a substitution - template; return pointer to the modified text */ -regsub(pat, lp, gflag) - pattern_t *pat; - line_t *lp; - int gflag; -{ - int off = 0; - int kth = gflag >> 8; /* substitute kth match only */ - int chngd = 0; - int matchno = 0; - int len; - int i = 0; - regmatch_t rm[SE_MAX]; - char *txt; - char *eot; - - if ((txt = gettxt(lp)) == NULL) - return ERR; - len = lp->len & ~ACTV; - eot = txt + len; - if (isbinary) txt = nultonl(txt, len); - if (!regexec(pat, txt, SE_MAX, rm, 0)) { - do { - if (!kth || kth == ++matchno) { - chngd++; - i = rm[0].rm_so; - CKBUF(rbuf, rbufsz, off + i, ERR); - if (isbinary) txt = nltonul(txt, rm[0].rm_eo); - memcpy(rbuf + off, txt, i); - if ((off = catsub(txt, rm, off += i)) < 0) - return ERR; - } else { - i = rm[0].rm_eo; - CKBUF(rbuf, rbufsz, off + i, ERR); - if (isbinary) txt = nltonul(txt, i); - memcpy(rbuf + off, txt, i); - off += i; - } - txt += rm[0].rm_eo; - } while (*txt && (!chngd || (gflag & GSG) && rm[0].rm_eo) - && !regexec(pat, txt, SE_MAX, rm, REG_NOTBOL)); - i = eot - txt; - CKBUF(rbuf, rbufsz, off + i + 2, ERR); - if (i > 0 && !rm[0].rm_eo && (gflag & GSG)) { - sprintf(errmsg, "infinite substitution loop"); - return ERR; - } - if (isbinary) txt = nltonul(txt, i); - memcpy(rbuf + off, txt, i); - memcpy(rbuf + off + i, "\n", 2); - } - return chngd ? off + i + 1 : 0; -} - - -/* join: replace a range of lines with the joined text of those lines */ -join(from, to) - long from; - long to; -{ - static char *buf = NULL; - static int n; - - char *s; - int len = 0; - int size = 0; - line_t *bp, *ep; - - ep = getlp(nextln(to, lastln)); - for (bp = getlp(from); bp != ep; bp = bp->next, size += len) { - if ((s = gettxt(bp)) == NULL) - return ERR; - len = bp->len & ~ACTV; - CKBUF(buf, n, size + len, ERR); - memcpy(buf + size, s, len); - } - CKBUF(buf, n, size + 2, ERR); - memcpy(buf + size, "\n", 2); - if (lndelete(from, to) < 0) - return ERR; - curln = from - 1; - spl1(); - if (puttxt(buf) == NULL - || upush(UADD, curln, curln) == NULL) { - spl0(); - return ERR; - } - spl0(); - modified = 1; - return 0; -} - - -/* move: move a range of lines */ -move(num, glob) - long num; - int glob; -{ - line_t *b1, *a1, *b2, *a2, *lp; - long n = nextln(line2, lastln); - long p = prevln(line1, lastln); - int done = (num == line1 - 1 || num == line2); - - spl1(); - if (done) { - a2 = getlp(n); - b2 = getlp(p); - curln = line2; - } else if (upush(UMOV, p, n) == NULL - || upush(UMOV, num, nextln(num, lastln)) == NULL) { - spl0(); - return ERR; - } else { - a1 = getlp(n); - if (num < line1) - b1 = getlp(p), b2 = getlp(num); /* this getlp last! */ - else b2 = getlp(num), b1 = getlp(p); /* this getlp last! */ - a2 = b2->next; - requeue(b2, b1->next); - requeue(a1->prev, a2); - requeue(b1, a1); - curln = num + ((num < line1) ? line2 - line1 + 1 : 0); - } - if (glob) - for (lp = b2->next; lp != a2; lp = lp->next) - lp->len &= ~ACTV; /* zero ACTV bit */ - spl0(); - return 0; -} - - -/* transfer: copy a range of lines; return status */ -transfer(num) - long num; -{ - line_t *lp; - long nl, nt, lc; - long mid = (num < line2) ? num : line2; - undo_t *up = NULL; - - curln = num; - for (nt = 0, nl = line1; nl <= mid; nl++, nt++) { - spl1(); - if ((lp = lpdup(getlp(nl))) == NULL) { - spl0(); - return ERR; - } - lpqueue(lp); - if (up) - up->t = lp; - else if ((up = upush(UADD, curln, curln)) == NULL) { - spl0(); - return ERR; - } - spl0(); - } - for (nl += nt, lc = line2 + nt; nl <= lc; nl += 2, lc++) { - spl1(); - if ((lp = lpdup(getlp(nl))) == NULL) { - spl0(); - return ERR; - } - lpqueue(lp); - if (up) - up->t = lp; - else if ((up = upush(UADD, curln, curln)) == NULL) { - spl0(); - return ERR; - } - spl0(); - } - return 0; -} - - -/* lndelete: delete a range of lines */ -lndelete(from, to) - long from, to; -{ - line_t *before, *after; - - spl1(); - if (upush(UDEL, from, to) == NULL) { - spl0(); - return ERR; - } - after = getlp(nextln(to, lastln)); - before = getlp(prevln(from, lastln)); /* this getlp last! */ - requeue(before, after); - lastln -= to - from + 1; - curln = prevln(from, lastln); - spl0(); - return 0; -} - - -/* catsub: modify text according to a substitution template; - return offset to end of modified text */ -catsub(boln, rm, off) - char *boln; - regmatch_t *rm; - int off; -{ - int j = 0; - int k = 0; - char *sub = rhbuf; - - for (; sub - rhbuf < rhbufi; sub++) - if (*sub == '&') { - j = rm[0].rm_so; - k = rm[0].rm_eo; - CKBUF(rbuf, rbufsz, off + k - j, ERR); - while (j < k) - rbuf[off++] = boln[j++]; - } else if (*sub == '\\' && '1' <= *++sub && *sub <= '9' - && rm[*sub - '0'].rm_so >= 0 - && rm[*sub - '0'].rm_eo >= 0) { - j = rm[*sub - '0'].rm_so; - k = rm[*sub - '0'].rm_eo; - CKBUF(rbuf, rbufsz, off + k - j, ERR); - while (j < k) - rbuf[off++] = boln[j++]; - } else { - CKBUF(rbuf, rbufsz, off + 1, ERR); - rbuf[off++] = *sub; - } - CKBUF(rbuf, rbufsz, off + 1, ERR); - rbuf[off] = '\0'; - return off; -} - -/* doprint: print a range of lines to stdout */ -doprint(from, to, gflag) - long from; - long to; - int gflag; -{ - line_t *bp; - line_t *ep; - char *s; - - if (!from) { - sprintf(errmsg, "invalid address"); - return ERR; - } - ep = getlp(nextln(to, lastln)); - for (bp = getlp(from); bp != ep; bp = bp->next) { - if ((s = gettxt(bp)) == NULL) - return ERR; - putstr(s, bp->len & ~ACTV, curln = from++, gflag); - } - return 0; -} - - -int cols = 72; /* wrap column: ws_col - 8 */ - -/* putstr: print text to stdout */ -void -putstr(s, l, n, gflag) - char *s; - int l; - long n; - int gflag; -{ - int col = 0; - - if (gflag & GNP) { - printf("%ld\t", n); - col = 8; - } - for (; l--; s++) { - if ((gflag & GLS) && ++col > cols) { - fputs("\\\n", stdout); - col = 1; - } - if (gflag & GLS) { - switch (*s) { - case '\b': - fputs("\\b", stdout); - break; - case '\f': - fputs("\\f", stdout); - break; - case '\n': - fputs("\\n", stdout); - break; - case '\r': - fputs("\\r", stdout); - break; - case '\t': - fputs("\\t", stdout); - break; - case '\v': - fputs("\\v", stdout); - break; - default: - if (*s < 32 || 126 < *s) { - putchar('\\'); - putchar((((unsigned char) *s & 0300) >> 6) + '0'); - putchar((((unsigned char) *s & 070) >> 3) + '0'); - putchar(((unsigned char) *s & 07) + '0'); - col += 2; - } else if (*s == '\\') - fputs("\\\\", stdout); - else { - putchar(*s); - col--; - } - } - col++; - } else - putchar(*s); - } -#ifndef BACKWARDS - if (gflag & GLS) - putchar('$'); -#endif - putchar('\n'); -} - - -int newline_added; /* set if newline appended to input file */ - -/* doread: read a text file into the editor buffer; return line count */ -long -doread(n, fn) - long n; - char *fn; -{ - FILE *fp; - line_t *lp = getlp(n); - unsigned long size = 0; - undo_t *up = NULL; - int len; - - isbinary = newline_added = 0; - if ((fp = (*fn == '!') ? popen(fn + 1, "r") : fopen(esctos(fn), "r")) == NULL) { - fprintf(stderr, "%s: %s\n", fn, strerror(errno)); - sprintf(errmsg, "cannot open input file"); - return ERR; - } else if (des) - desinit(); - for (curln = n; (len = sgetline(fp)) > 0; size += len) { - spl1(); - if (puttxt(sbuf) == NULL) { - spl0(); - return ERR; - } - lp = lp->next; - if (up) - up->t = lp; - else if ((up = upush(UADD, curln, curln)) == NULL) { - spl0(); - return ERR; - } - spl0(); - } - if (((*fn == '!') ? pclose(fp) : fclose(fp)) < 0) { - fprintf(stderr, "%s: %s\n", fn, strerror(errno)); - sprintf(errmsg, "cannot close input file"); - return ERR; - } - if (newline_added && !isbinary) - fputs("newline appended\n", stderr); - if (des) size += 8 - size % 8; - fprintf(stderr, !scripted ? "%lu\n" : "", size); - return (len < 0) ? ERR : curln - n; -} - - -/* dowrite: write the text of a range of lines to a file; return line count */ -long -dowrite(n, m, fn, mode) - long n; - long m; - char *fn; - char *mode; -{ - FILE *fp; - line_t *lp; - unsigned long size = 0; - long lc = n ? m - n + 1 : 0; - char *s = NULL; - int len; - int ct; - - if ((fp = ((*fn == '!') ? popen(fn + 1, "w") : fopen(esctos(fn), mode))) == NULL) { - fprintf(stderr, "%s: %s\n", fn, strerror(errno)); - sprintf(errmsg, "cannot open output file"); - return ERR; - } else if (des) - desinit(); - if (n && !des) - for (lp = getlp(n); n <= m; n++, lp = lp->next) { - if ((s = gettxt(lp)) == NULL) - return ERR; - len = lp->len & ~ACTV; - if (n != lastln || !isbinary || !newline_added) - s[len++] = '\n'; - if ((ct = fwrite(s, sizeof(char), len, fp)) < 0 || ct != len) { - fprintf(stderr, "%s: %s\n", fn, strerror(errno)); - sprintf(errmsg, "cannot write file"); - return ERR; - } - size += len; - } - else if (n) - for (lp = getlp(n); n <= m; n++, lp = lp->next) { - if ((s = gettxt(lp)) == NULL) - return ERR; - len = lp->len & ~ACTV; - while (len--) { - if (desputc(*s++, fp) == EOF && ferror(fp)) { - fprintf(stderr, "%s: %s\n", fn, strerror(errno)); - sprintf(errmsg, "cannot write file"); - return ERR; - } - } - if (n != lastln || !isbinary || !newline_added) { - if (desputc('\n', fp) < 0) { - fprintf(stderr, "%s: %s\n", fn, strerror(errno)); - sprintf(errmsg, "cannot write file"); - return ERR; - } - size++; /* for '\n' */ - } - size += (lp->len & ~ACTV); - } - if (des) { - desflush(fp); /* flush buffer */ - size += 8 - size % 8; /* adjust DES size */ - } - if (((*fn == '!') ? pclose(fp) : fclose(fp)) < 0) { - fprintf(stderr, "%s: %s\n", fn, strerror(errno)); - sprintf(errmsg, "cannot close output file"); - return ERR; - } - fprintf(stderr, !scripted ? "%lu\n" : "", size); - return lc; -} - - -#define USIZE 100 /* undo stack size */ -undo_t *ustack = NULL; /* undo stack */ -long usize = 0; /* stack size variable */ -long u_p = 0; /* undo stack pointer */ - -/* upush: return pointer to intialized undo node */ -undo_t * -upush(type, from, to) - int type; - long from; - long to; -{ - undo_t *t; - -#if defined(sun) || defined(NO_REALLOC_NULL) - if (ustack == NULL - && (ustack = (undo_t *) malloc((usize = USIZE) * sizeof(undo_t))) == NULL) { - fprintf(stderr, "%s\n", strerror(errno)); - sprintf(errmsg, "out of memory"); - return NULL; - } -#endif - t = ustack; - if (u_p < usize - || (t = (undo_t *) realloc(ustack, (usize += USIZE) * sizeof(undo_t))) != NULL) { - ustack = t; - ustack[u_p].type = type; - ustack[u_p].t = getlp(to); - ustack[u_p].h = getlp(from); - return ustack + u_p++; - } - /* out of memory - release undo stack */ - fprintf(stderr, "%s\n", strerror(errno)); - sprintf(errmsg, "out of memory"); - ureset(); - free(ustack); - ustack = NULL; - usize = 0; - return NULL; -} - - -/* USWAP: swap undo nodes */ -#define USWAP(x,y) { \ - undo_t utmp; \ - utmp = x, x = y, y = utmp; \ -} - - -/* undo: undo last change to the editor buffer */ -undo(glob) - int glob; -{ - long n; - long ocurln = curln; - long olastln = lastln; - line_t *lp, *np; - - if (ucurln == -1 || ulastln == -1) { - sprintf(errmsg, "nothing to undo"); - return ERR; - } else if (u_p) - modified = 1; - getlp(0); /* this getlp last! */ - spl1(); - for (n = u_p; n-- > 0;) { - switch(ustack[n].type) { - case UADD: - requeue(ustack[n].h->prev, ustack[n].t->next); - break; - case UDEL: - requeue(ustack[n].h->prev, ustack[n].h); - requeue(ustack[n].t, ustack[n].t->next); - break; - case UMOV: - case VMOV: - requeue(ustack[n - 1].h, ustack[n].h->next); - requeue(ustack[n].t->prev, ustack[n - 1].t); - requeue(ustack[n].h, ustack[n].t); - n--; - break; - default: - /*NOTREACHED*/ - ; - } - ustack[n].type ^= 1; - } - /* reverse undo order */ - for (n = u_p; n-- > (u_p + 1)/ 2;) - USWAP(ustack[n], ustack[u_p - 1 - n]); - if (glob) - for (lp = np = getlp(0); (lp = lp->next) != np;) - lp->len &= ~ACTV; /* zero ACTV bit */ - curln = ucurln, ucurln = ocurln; - lastln = ulastln, ulastln = olastln; - spl0(); - return 0; -} - - -/* ureset: clear the undo stack */ -void -ureset() -{ - line_t *lp, *ep, *tl; - - while (u_p--) - if (ustack[u_p].type == UDEL) { - ep = ustack[u_p].t->next; - for (lp = ustack[u_p].h; lp != ep; lp = tl) { - clrmark(lp); - tl = lp->next; - free(lp); - } - } - u_p = 0; - ucurln = curln; - ulastln = lastln; -} - - -#define MAXMARK 26 /* max number of marks */ - -line_t *mark[MAXMARK]; /* line markers */ -int markno; /* line marker count */ - -/* getmark: return address of a marked line */ -long -getmark(n) - int n; -{ - if (!islower(n)) { - sprintf(errmsg, "invalid mark character"); - return ERR; - } - return getaddr(mark[n - 'a']); -} - - -/* putmark: set a line node mark */ -int -putmark(n, lp) - int n; - line_t *lp; -{ - if (!islower(n)) { - sprintf(errmsg, "invalid mark character"); - return ERR; - } else if (mark[n - 'a'] == NULL) - markno++; - mark[n - 'a'] = lp; - return 0; -} - - -/* clrmark: clear line node marks */ -void -clrmark(lp) - line_t *lp; -{ - int i; - - if (markno) - for (i = 0; i < MAXMARK; i++) - if (mark[i] == lp) { - mark[i] = NULL; - markno--; - } -} - - -/* sgetline: read a line of text up a maximum size from a file; return - line length */ -sgetline(fp) - FILE *fp; -{ - register int c; - register int i = 0; - - while (((c = des ? desgetc(fp) : getc(fp)) != EOF || !feof(fp) && !ferror(fp)) && c != '\n') { - CKBUF(sbuf, sbufsz, i + 1, ERR); - if (!(sbuf[i++] = c)) isbinary = 1; - } - CKBUF(sbuf, sbufsz, i + 2, ERR); - if (c == '\n') - sbuf[i++] = c; - else if (feof(fp) && i) { - sbuf[i++] = '\n'; - newline_added = 1; - } else if (ferror(fp)) { - fprintf(stderr, "%s\n", strerror(errno)); - sprintf(errmsg, "cannot read input file"); - return ERR; - } - sbuf[i] = '\0'; - return (isbinary && newline_added && i) ? --i : i; -} - - -/* getline: read a line of text up a maximum size from stdin; return - line length */ -getline() -{ - register int i = 0; - register int oi = 0; - char c; - - /* Read one character at a time to avoid i/o contention with shell - escapes invoked by nonterminal input, e.g., - ed - <<EOF - !cat - hello, world - EOF */ - for (;;) - switch (read(0, &c, 1)) { - default: - oi = 0; - CKBUF(ibuf, ibufsz, i + 2, ERR); - if (!(ibuf[i++] = c)) isbinary = 1; - if (c != '\n') - continue; - lineno++; /* script line no. */ - ibuf[i] = '\0'; - ibufp = ibuf; - return i; - case 0: - if (i != oi) { - oi = i; - continue; - } else if (i) - ibuf[i] = '\0'; - ibufp = ibuf; - return i; - case -1: - fprintf(stderr, "%s\n", strerror(errno)); - sprintf(errmsg, "cannot read standard input"); - clearerr(stdin); - ibufp = NULL; - return ERR; - } -} - - -/* getcmdv: get a command vector */ -char * -getcmdv(sizep, nonl) - int *sizep; - int nonl; -{ - int l, n; - char *t = ibufp; - - while (*t++ != '\n') - ; - if ((l = t - ibufp) < 2 || !oddesc(ibufp, ibufp + l - 1)) { - *sizep = l; - return ibufp; - } - *sizep = -1; - CKBUF(cvbuf, cvbufsz, l, NULL); - memcpy(cvbuf, ibufp, l); - *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */ - if (nonl) l--; /* strip newline */ - for (;;) { - if ((n = getline()) < 0) - return NULL; - else if (n == 0 || ibuf[n - 1] != '\n') { - sprintf(errmsg, "unexpected end-of-file"); - return NULL; - } - CKBUF(cvbuf, cvbufsz, l + n, NULL); - memcpy(cvbuf + l, ibuf, n); - l += n; - if (n < 2 || !oddesc(cvbuf, cvbuf + l - 1)) - break; - *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */ - if (nonl) l--; /* strip newline */ - } - CKBUF(cvbuf, cvbufsz, l + 1, NULL); - cvbuf[l] = '\0'; - *sizep = l; - return cvbuf; -} - - -/* lpdup: return a pointer to a copy of a line node */ -line_t * -lpdup(lp) - line_t *lp; -{ - line_t *np; - - if ((np = (line_t *) malloc(sizeof(line_t))) == NULL) { - fprintf(stderr, "%s\n", strerror(errno)); - sprintf(errmsg, "out of memory"); - return NULL; - } - np->seek = lp->seek; - np->len = (lp->len & ~ACTV); /* zero ACTV bit */ - return np; -} - - -/* oddesc: return the parity of escapes preceding a character in a - string */ -oddesc(s, t) - char *s; - char *t; -{ - return (s == t || *(t - 1) != '\\') ? 0 : !oddesc(s, t - 1); -} - - -/* esctos: return copy of escaped string */ -char * -esctos(s) - char *s; -{ - static char *file = NULL; - static int filesz = 0; - - int i = 0; - - CKBUF(file, filesz, MAXFNAME + 1, NULL); - /* assert: no trailing escape */ - while (file[i++] = (*s == '\\') ? *++s : *s) - s++; - return file; -} - - -void -onhup(signo) - int signo; -{ - if (mutex) - sigflags |= (1 << signo); - else dohup(signo); -} - - -void -onintr(signo) - int signo; -{ - if (mutex) - sigflags |= (1 << signo); - else dointr(signo); -} - - -void -dohup(signo) - int signo; -{ - char *hup = NULL; /* hup filename */ - char *s; - int n; - - if (!sigactive) - quit(1); - sigflags &= ~(1 << signo); - if (lastln && dowrite(1, lastln, "ed.hup", "w") < 0 - && (s = getenv("HOME")) != NULL - && (n = strlen(s)) + 8 <= MAXFNAME /* "ed.hup" + '/' */ - && (hup = (char *) malloc(n + 10)) != NULL) { - strcpy(hup, s); - if (hup[n - 1] != '/') - hup[n] = '/', hup[n+1] = '\0'; - strcat(hup, "ed.hup"); - dowrite(1, lastln, hup, "w"); - } - quit(2); -} - - -void -dointr(signo) - int signo; -{ - if (!sigactive) - quit(1); - sigflags &= ~(1 << signo); -#ifdef _POSIX_SOURCE - siglongjmp(env, -1); -#else - longjmp(env, -1); -#endif -} - - -struct winsize ws; /* window size structure */ - -void -dowinch(signo) - int signo; -{ - sigflags &= ~(1 << signo); - if (ioctl(0, TIOCGWINSZ, (char *) &ws) >= 0) { - if (ws.ws_row > 2) rows = ws.ws_row - 2; - if (ws.ws_col > 8) cols = ws.ws_col - 8; - } -} - - -/* ckfn: return a legal filename */ -char * -ckfn(s) - char *s; -{ - if (red && (*s == '!' || !strcmp(s, "..") || strchr(s, '/'))) { - sprintf(errmsg, "shell access restricted"); - return NULL; - } - return s; -} diff --git a/bin/ed/ed.h b/bin/ed/ed.h index b3905370f949..1f61428eb73a 100644 --- a/bin/ed/ed.h +++ b/bin/ed/ed.h @@ -1,11 +1,8 @@ /* ed.h: type and constant definitions for the ed editor. */ /* - * Copyright (c) 1993 The Regents of the University of California. + * Copyright (c) 1993 Andrew Moore * All rights reserved. * - * This code is derived from software contributed to Berkeley by - * Andrew Moore, Talke Studio. - * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -14,13 +11,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE @@ -34,22 +24,22 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)ed.h 5.5 (Berkeley) 3/28/93 + * @(#)$Id: ed.h,v 1.5 1994/02/01 00:34:39 alm Exp $ */ -#include <unistd.h> -#include <errno.h> #if defined(BSD) && BSD >= 199103 || defined(__386BSD__) # include <sys/param.h> /* for MAXPATHLEN */ #endif +#include <errno.h> +#ifdef sun +# include <limits.h> +#endif #include <regex.h> #include <signal.h> - -#define BITSPERBYTE 8 -#define BITS(type) (BITSPERBYTE * (int)sizeof(type)) -#define CHARBITS BITS(char) -#define INTBITS BITS(int) -#define INTHIBIT (unsigned) (1 << (INTBITS - 1)) +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> #define ERR (-2) #define EMOD (-3) @@ -59,21 +49,28 @@ # define MAXPATHLEN 255 /* _POSIX_PATH_MAX */ #endif -#define MAXFNAME MAXPATHLEN /* max file name size */ #define MINBUFSZ 512 /* minimum buffer size - must be > 0 */ -#define LINECHARS (INTHIBIT - 1) /* max chars per line */ #define SE_MAX 30 /* max subexpressions in a regular expression */ +#ifdef INT_MAX +# define LINECHARS INT_MAX /* max chars per line */ +#else +# define LINECHARS MAXINT /* max chars per line */ +#endif + +/* gflags */ +#define GLB 001 /* global command */ +#define GPR 002 /* print after command */ +#define GLS 004 /* list after command */ +#define GNP 010 /* enumerate after command */ +#define GSG 020 /* global substitute */ typedef regex_t pattern_t; /* Line node */ typedef struct line { - struct line *next; - struct line *prev; + struct line *q_forw; + struct line *q_back; off_t seek; /* address of line in scratch buffer */ - -#define ACTV INTHIBIT /* active bit: high bit of len */ - int len; /* length of line */ } line_t; @@ -98,87 +95,94 @@ typedef struct undo { # define min(a,b) ((a) < (b) ? (a) : (b)) #endif -/* nextln: return line after l mod k */ -#define nextln(l,k) ((l)+1 > (k) ? 0 : (l)+1) - -/* nextln: return line before l mod k */ -#define prevln(l,k) ((l)-1 < 0 ? (k) : (l)-1) - -#define skipblanks() while (isspace(*ibufp) && *ibufp != '\n') ibufp++ +#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++ +/* SPL1: disable some interrupts (requires reliable signals) */ +#define SPL1() mutex++ -/* spl0: enable all interrupts; check sigflags (requires reliable signals) */ -#define spl0() \ +/* SPL0: enable all interrupts; check sigflags (requires reliable signals) */ +#define SPL0() \ if (--mutex == 0) { \ - if (sigflags & (1 << SIGHUP)) dohup(SIGHUP); \ - if (sigflags & (1 << SIGINT)) dointr(SIGINT); \ + if (sigflags & (1 << (SIGHUP - 1))) handle_hup(SIGHUP); \ + if (sigflags & (1 << (SIGINT - 1))) handle_int(SIGINT); \ +} + +/* STRTOL: convert a string to long */ +#define STRTOL(i, p) { \ + if (((i = strtol(p, &p, 10)) == LONG_MIN || i == LONG_MAX) && \ + errno == ERANGE) { \ + sprintf(errmsg, "number out of range"); \ + i = 0; \ + return ERR; \ + } \ } #if defined(sun) || defined(NO_REALLOC_NULL) -/* CKBUF: assure at least a minimum size for buffer b */ -#define CKBUF(b,n,i,err) \ +/* REALLOC: assure at least a minimum size for buffer b */ +#define REALLOC(b,n,i,err) \ if ((i) > (n)) { \ int ti = (n); \ char *ts; \ - spl1(); \ + SPL1(); \ if ((b) != NULL) { \ if ((ts = (char *) realloc((b), ti += max((i), MINBUFSZ))) == NULL) { \ fprintf(stderr, "%s\n", strerror(errno)); \ sprintf(errmsg, "out of memory"); \ - spl0(); \ + SPL0(); \ return err; \ } \ } else { \ if ((ts = (char *) malloc(ti += max((i), MINBUFSZ))) == NULL) { \ fprintf(stderr, "%s\n", strerror(errno)); \ sprintf(errmsg, "out of memory"); \ - spl0(); \ + SPL0(); \ return err; \ } \ } \ (n) = ti; \ (b) = ts; \ - spl0(); \ + SPL0(); \ } #else /* NO_REALLOC_NULL */ -/* CKBUF: assure at least a minimum size for buffer b */ -#define CKBUF(b,n,i,err) \ +/* REALLOC: assure at least a minimum size for buffer b */ +#define REALLOC(b,n,i,err) \ if ((i) > (n)) { \ int ti = (n); \ char *ts; \ - spl1(); \ + SPL1(); \ if ((ts = (char *) realloc((b), ti += max((i), MINBUFSZ))) == NULL) { \ fprintf(stderr, "%s\n", strerror(errno)); \ sprintf(errmsg, "out of memory"); \ - spl0(); \ + SPL0(); \ return err; \ } \ (n) = ti; \ (b) = ts; \ - spl0(); \ + SPL0(); \ } #endif /* NO_REALLOC_NULL */ -/* requeue: link pred before succ */ -#define requeue(pred, succ) (pred)->next = (succ), (succ)->prev = (pred) +/* REQUE: link pred before succ */ +#define REQUE(pred, succ) (pred)->q_forw = (succ), (succ)->q_back = (pred) -/* insqueue: insert elem in circular queue after pred */ -#define insqueue(elem, pred) \ +#ifdef NEED_INSQUE +/* insque: insert elem in circular queue after pred */ +#define insque(elem, pred) \ { \ - requeue((elem), (pred)->next); \ - requeue((pred), elem); \ + REQUE((elem), (pred)->q_forw); \ + REQUE((pred), elem); \ } -/* remqueue: remove elem from circular queue */ -#define remqueue(elem) requeue((elem)->prev, (elem)->next); +/* remque: remove_lines elem from circular queue */ +#define remque(elem) REQUE((elem)->q_back, (elem)->q_forw); +#endif /* NEED_INSQUE */ -/* nultonl: overwrite ASCII NULs with newlines */ -#define nultonl(s, l) translit(s, l, '\0', '\n') +/* NUL_TO_NEWLINE: overwrite ASCII NULs with newlines */ +#define NUL_TO_NEWLINE(s, l) translit_text(s, l, '\0', '\n') -/* nltonul: overwrite newlines with ASCII NULs */ -#define nltonul(s, l) translit(s, l, '\n', '\0') +/* NEWLINE_TO_NUL: overwrite newlines with ASCII NULs */ +#define NEWLINE_TO_NUL(s, l) translit_text(s, l, '\n', '\0') #ifndef strerror # define strerror(n) sys_errlist[n] @@ -192,75 +196,101 @@ if ((i) > (n)) { \ # endif #endif -/* local function declarations */ -int append __P((long, int)); -int cbcdec __P((char *, FILE *)); -int cbcenc __P((char *, int, FILE *)); -char *ckfn __P((char *)); -int ckglob __P((void)); -int ckrange __P((long, long)); -int desflush __P((FILE *)); -int desgetc __P((FILE *)); -void desinit __P((void)); -int desputc __P((int, FILE *)); -int docmd __P((int)); -void err __P((char *)); -char *ccl __P((char *)); -void clrmark __P((line_t *)); -void cvtkey __P((char *, char *)); -long doglob __P((int)); -void dohup __P((int)); -void dointr __P((int)); -void dowinch __P((int)); -int doprint __P((long, long, int)); -long doread __P((long, char *)); -long dowrite __P((long, long, char *, char *)); -char *esctos __P((char *)); -long patscan __P((pattern_t *, int)); -long getaddr __P((line_t *)); -char *getcmdv __P((int *, int)); -char *getfn __P((void)); -int getkey __P((void)); -char *getlhs __P((int)); -int getline __P((void)); -int getlist __P((void)); -long getmark __P((int)); -long getnum __P((int)); -long getone __P((void)); -line_t *getlp __P((long)); -int getrhs __P((int)); -int getshcmd __P((void)); -char *gettxt __P((line_t *)); -void init_buf __P((void)); -int join __P((long, long)); -int lndelete __P((long, long)); -line_t *lpdup __P((line_t *)); -void lpqueue __P((line_t *)); -void makekey __P((char *)); -char *makesub __P((int)); -int move __P((long, int)); -int oddesc __P((char *, char *)); -void onhup __P((int)); -void onintr __P((int)); -pattern_t *optpat __P((void)); -int putmark __P((int, line_t *)); -void putstr __P((char *, int, long, int)); -char *puttxt __P((char *)); +/* Local Function Declarations */ +void add_line_node __P((line_t *)); +int append_lines __P((long)); +int apply_subst_template __P((char *, regmatch_t *, int, int)); +int build_active_list __P((int)); +int cbc_decode __P((char *, FILE *)); +int cbc_encode __P((char *, int, FILE *)); +int check_addr_range __P((long, long)); +void clear_active_list __P((void)); +void clear_undo_stack __P((void)); +int close_sbuf __P((void)); +int copy_lines __P((long)); +int delete_lines __P((long, long)); +void des_error __P((char *)); +int display_lines __P((long, long, int)); +line_t *dup_line_node __P((line_t *)); +int exec_command __P((void)); +long exec_global __P((int, int)); +void expand_des_key __P((char *, char *)); +int extract_addr_range __P((void)); +char *extract_pattern __P((int)); +int extract_subst_tail __P((int *, int *)); +char *extract_subst_template __P((void)); +int filter_lines __P((long, long, char *)); +int flush_des_file __P((FILE *)); +line_t *get_addressed_line_node __P((long)); +pattern_t *get_compiled_pattern __P((void)); +int get_des_char __P((FILE *)); +char *get_extended_line __P((int *, int)); +char *get_filename __P((void)); +int get_keyword __P((void)); +long get_line_node_addr __P((line_t *)); +long get_matching_node_addr __P((pattern_t *, int)); +long get_marked_node_addr __P((int)); +char *get_sbuf_line __P((line_t *)); +int get_shell_command __P((void)); +int get_stream_line __P((FILE *)); +int get_tty_line __P((void)); +void handle_hup __P((int)); +void handle_int __P((int)); +void handle_winch __P((int)); +int has_trailing_escape __P((char *, char *)); +int hex_to_binary __P((int, int)); +void init_buffers __P((void)); +void init_des_cipher __P((void)); +int is_legal_filename __P((char *)); +int join_lines __P((long, long)); +int mark_line_node __P((line_t *, int)); +int move_lines __P((long)); +line_t *next_active_node __P(()); +long next_addr __P((void)); +int open_sbuf __P((void)); +char *parse_char_class __P((char *)); +int pop_undo_stack __P((void)); +undo_t *push_undo_stack __P((int, long, long)); +int put_des_char __P((int, FILE *)); +char *put_sbuf_line __P((char *)); +int put_stream_line __P((FILE *, char *, int)); +int put_tty_line __P((char *, int, long, int)); void quit __P((int)); -int regsub __P((pattern_t *, line_t *, int)); -int sbclose __P((void)); -int sbopen __P((void)); -int sgetline __P((FILE *)); -int catsub __P((char *, regmatch_t *, int)); -int subst __P((pattern_t *, int)); -int tobinhex __P((int, int)); -int transfer __P((long)); -char *translit __P((char *, int, int, int)); -int undo __P((int)); -undo_t *upush __P((int, long, long)); -void ureset __P((void)); +long read_file __P((char *, long)); +long read_stream __P((FILE *, long)); +int search_and_replace __P((pattern_t *, int, int)); +int set_active_node __P((line_t *)); +void set_des_key __P((char *)); +void signal_hup __P((int)); +void signal_int __P((int)); +char *strip_escapes __P((char *)); +int substitute_matching_text __P((pattern_t *, line_t *, int, int)); +char *translit_text __P((char *, int, int, int)); +void unmark_line_node __P((line_t *)); +void unset_active_nodes __P((line_t *, line_t *)); +long write_file __P((char *, char *, long, long)); +long write_stream __P((FILE *, long, long)); +/* global buffers */ +extern char stdinbuf[]; +extern char *ibuf; +extern char *ibufp; +extern int ibufsz; -extern char *sys_errlist[]; +/* global flags */ +extern int isbinary; +extern int isglobal; +extern int modified; extern int mutex; extern int sigflags; + +/* global vars */ +extern long addr_last; +extern long current_addr; +extern char errmsg[]; +extern long first_addr; +extern int lineno; +extern long second_addr; +#ifdef sun +extern char *sys_errlist[]; +#endif diff --git a/bin/ed/glob.c b/bin/ed/glob.c new file mode 100644 index 000000000000..5c77d224cdff --- /dev/null +++ b/bin/ed/glob.c @@ -0,0 +1,223 @@ +/* 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. + */ + +#ifndef lint +static char *rcsid = "@(#)$Id: glob.c,v 1.1 1994/02/01 00:34:40 alm Exp $"; +#endif /* not lint */ + +#include <sys/ioctl.h> +#include <sys/wait.h> + +#include "ed.h" + + +/* build_active_list: add line matching a pattern to the global-active list */ +int +build_active_list(isgcmd) + 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(interact, gflag) + 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 + if (!interact) + if (!strcmp(ibufp, "\n")) + cmd = "p\n"; /* 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(lp) + 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(np, mp) + line_t *np, *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() +{ + 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() +{ + 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 000000000000..0c63f3dc77fb --- /dev/null +++ b/bin/ed/io.c @@ -0,0 +1,375 @@ +/* io.c: This file contains the i/o routines for the ed line editor */ +/*- + * Copyright (c) 1993 Andrew Moore, Talke Studio. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char *rcsid = "@(#)$Id: io.c,v 1.1 1994/02/01 00:34:41 alm Exp $"; +#endif /* not lint */ + +#include "ed.h" + + +extern int scripted; + +/* read_file: read a named file/pipe into the buffer; return line count */ +long +read_file(fn, n) + char *fn; + long n; +{ + FILE *fp; + long size; + + + fp = (*fn == '!') ? popen(fn + 1, "r") : fopen(strip_escapes(fn), "r"); + if (fp == NULL) { + fprintf(stderr, "%s: %s\n", fn, strerror(errno)); + sprintf(errmsg, "cannot open input file"); + return ERR; + } else if ((size = read_stream(fp, n)) < 0) + return ERR; + else if (((*fn == '!') ? pclose(fp) : fclose(fp)) < 0) { + fprintf(stderr, "%s: %s\n", fn, strerror(errno)); + sprintf(errmsg, "cannot close input file"); + return ERR; + } + fprintf(stderr, !scripted ? "%lu\n" : "", size); + return current_addr - n; +} + + +extern int des; + +char *sbuf; /* file i/o buffer */ +int sbufsz; /* file i/o buffer size */ +int newline_added; /* if set, newline appended to input file */ + +/* read_stream: read a stream into the editor buffer; return status */ +long +read_stream(fp, n) + FILE *fp; + long n; +{ + line_t *lp = get_addressed_line_node(n); + undo_t *up = NULL; + unsigned long size = 0; + int o_newline_added = newline_added; + int o_isbinary = isbinary; + int appended = (n == addr_last); + int len; + + isbinary = newline_added = 0; + if (des) + init_des_cipher(); + for (current_addr = n; (len = get_stream_line(fp)) > 0; size += len) { + SPL1(); + if (put_sbuf_line(sbuf) == NULL) { + SPL0(); + return ERR; + } + lp = lp->q_forw; + if (up) + up->t = lp; + else if ((up = push_undo_stack(UADD, current_addr, + current_addr)) == NULL) { + SPL0(); + return ERR; + } + SPL0(); + } + if (len < 0) + return ERR; + if (appended && size && o_isbinary && o_newline_added) + fputs("newline inserted\n", stderr); + else if (newline_added && (!appended || !isbinary && !o_isbinary)) + fputs("newline appended\n", stderr); + if (isbinary && newline_added && !appended) + size += 1; + if (!size) + newline_added = 1; + newline_added = appended ? newline_added : o_newline_added; + isbinary = isbinary | o_isbinary; + if (des) + size += 8 - size % 8; /* adjust DES size */ + return size; +} + + +/* get_stream_line: read a line of text from a stream; return line length */ +int +get_stream_line(fp) + FILE *fp; +{ + register int c; + register int i = 0; + + while (((c = des ? get_des_char(fp) : getc(fp)) != EOF || !feof(fp) && + !ferror(fp)) && c != '\n') { + REALLOC(sbuf, sbufsz, i + 1, ERR); + if (!(sbuf[i++] = c)) + isbinary = 1; + } + REALLOC(sbuf, sbufsz, i + 2, ERR); + if (c == '\n') + sbuf[i++] = c; + else if (ferror(fp)) { + fprintf(stderr, "%s\n", strerror(errno)); + sprintf(errmsg, "cannot read input file"); + return ERR; + } else if (i) { + sbuf[i++] = '\n'; + newline_added = 1; + } + sbuf[i] = '\0'; + return (isbinary && newline_added && i) ? --i : i; +} + + +/* write_file: write a range of lines to a named file/pipe; return line count */ +long +write_file(fn, mode, n, m) + char *fn; + char *mode; + long n; + long m; +{ + FILE *fp; + long size; + + fp = (*fn == '!') ? popen(fn+1, "w") : fopen(strip_escapes(fn), mode); + if (fp == NULL) { + fprintf(stderr, "%s: %s\n", fn, strerror(errno)); + sprintf(errmsg, "cannot open output file"); + return ERR; + } else if ((size = write_stream(fp, n, m)) < 0) + return ERR; + else if (((*fn == '!') ? pclose(fp) : fclose(fp)) < 0) { + fprintf(stderr, "%s: %s\n", fn, strerror(errno)); + sprintf(errmsg, "cannot close output file"); + return ERR; + } + fprintf(stderr, !scripted ? "%lu\n" : "", size); + return n ? m - n + 1 : 0; +} + + +/* write_stream: write a range of lines to a stream; return status */ +long +write_stream(fp, n, m) + FILE *fp; + long n; + long m; +{ + line_t *lp = get_addressed_line_node(n); + unsigned long size = 0; + char *s; + int len; + + if (des) + init_des_cipher(); + for (; n && n <= m; n++, lp = lp->q_forw) { + if ((s = get_sbuf_line(lp)) == NULL) + return ERR; + len = lp->len; + if (n != addr_last || !isbinary || !newline_added) + s[len++] = '\n'; + if (put_stream_line(fp, s, len) < 0) + return ERR; + size += len; + } + if (des) { + flush_des_file(fp); /* flush buffer */ + size += 8 - size % 8; /* adjust DES size */ + } + return size; +} + + +/* put_stream_line: write a line of text to a stream; return status */ +int +put_stream_line(fp, s, len) + FILE *fp; + char *s; + int len; +{ + while (len--) + if ((des ? put_des_char(*s++, fp) : fputc(*s++, fp)) < 0) { + fprintf(stderr, "%s\n", strerror(errno)); + sprintf(errmsg, "cannot write file"); + return ERR; + } + return 0; +} + +/* get_extended_line: get a an extended line from stdin */ +char * +get_extended_line(sizep, nonl) + int *sizep; + int nonl; +{ + static char *cvbuf = NULL; /* buffer */ + static int cvbufsz = 0; /* buffer size */ + + int l, n; + char *t = ibufp; + + while (*t++ != '\n') + ; + if ((l = t - ibufp) < 2 || !has_trailing_escape(ibufp, ibufp + l - 1)) { + *sizep = l; + return ibufp; + } + *sizep = -1; + REALLOC(cvbuf, cvbufsz, l, NULL); + memcpy(cvbuf, ibufp, l); + *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */ + if (nonl) l--; /* strip newline */ + for (;;) { + if ((n = get_tty_line()) < 0) + return NULL; + else if (n == 0 || ibuf[n - 1] != '\n') { + sprintf(errmsg, "unexpected end-of-file"); + return NULL; + } + REALLOC(cvbuf, cvbufsz, l + n, NULL); + memcpy(cvbuf + l, ibuf, n); + l += n; + if (n < 2 || !has_trailing_escape(cvbuf, cvbuf + l - 1)) + break; + *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */ + if (nonl) l--; /* strip newline */ + } + REALLOC(cvbuf, cvbufsz, l + 1, NULL); + cvbuf[l] = '\0'; + *sizep = l; + return cvbuf; +} + + +/* get_tty_line: read a line of text from stdin; return line length */ +int +get_tty_line() +{ + register int oi = 0; + register int i = 0; + int c; + + for (;;) + switch (c = getchar()) { + default: + oi = 0; + REALLOC(ibuf, ibufsz, i + 2, ERR); + if (!(ibuf[i++] = c)) isbinary = 1; + if (c != '\n') + continue; + lineno++; + ibuf[i] = '\0'; + ibufp = ibuf; + return i; + case EOF: + if (ferror(stdin)) { + fprintf(stderr, "stdin: %s\n", strerror(errno)); + sprintf(errmsg, "cannot read stdin"); + clearerr(stdin); + ibufp = NULL; + return ERR; + } else { + clearerr(stdin); + if (i != oi) { + oi = i; + continue; + } else if (i) + ibuf[i] = '\0'; + ibufp = ibuf; + return i; + } + } +} + + + +#define ESCAPES "\a\b\f\n\r\t\v\\" +#define ESCCHARS "abfnrtv\\" + +extern int rows; +extern int cols; + +/* put_tty_line: print text to stdout */ +int +put_tty_line(s, l, n, gflag) + char *s; + int l; + long n; + int gflag; +{ + int col = 0; + int lc = 0; + char *cp; + + if (gflag & GNP) { + printf("%ld\t", n); + col = 8; + } + for (; l--; s++) { + if ((gflag & GLS) && ++col > cols) { + fputs("\\\n", stdout); + col = 1; +#ifndef BACKWARDS + if (!scripted && !isglobal && ++lc > rows) { + lc = 0; + fputs("Press <RETURN> to continue... ", stdout); + fflush(stdout); + if (get_tty_line() < 0) + return ERR; + } +#endif + } + if (gflag & GLS) { + if (31 < *s && *s < 127 && *s != '\\') + putchar(*s); + else { + putchar('\\'); + col++; + if (*s && (cp = strchr(ESCAPES, *s)) != NULL) + putchar(ESCCHARS[cp - ESCAPES]); + else { + putchar((((unsigned char) *s & 0300) >> 6) + '0'); + putchar((((unsigned char) *s & 070) >> 3) + '0'); + putchar(((unsigned char) *s & 07) + '0'); + col += 2; + } + } + + } else + putchar(*s); + } +#ifndef BACKWARDS + if (gflag & GLS) + putchar('$'); +#endif + putchar('\n'); + return 0; +} diff --git a/bin/ed/main.c b/bin/ed/main.c new file mode 100644 index 000000000000..ddaf513f4337 --- /dev/null +++ b/bin/ed/main.c @@ -0,0 +1,1432 @@ +/* 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. + */ + +#ifndef lint +char *copyright = +"@(#) Copyright (c) 1993 Andrew Moore, Talke Studio. \n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char *rcsid = "@(#)$Id: main.c,v 1.1 1994/02/01 00:34:42 alm Exp $"; +#endif /* not lint */ + +/* + * CREDITS + * + * This program is based on the editor algorithm described in + * Brian W. Kernighan and P. J. Plauger's book "Software Tools + * in Pascal," Addison-Wesley, 1981. + * + * The buffering algorithm is attributed to Rodney Ruddock of + * the University of Guelph, Guelph, Ontario. + * + * The cbc.c encryption code is adapted from + * the bdes program by Matt Bishop of Dartmouth College, + * Hanover, NH. + * + */ + +#include <sys/ioctl.h> +#include <sys/wait.h> +#include <ctype.h> +#include <setjmp.h> +#include <pwd.h> + +#include "ed.h" + + +#ifdef _POSIX_SOURCE +sigjmp_buf env; +#else +jmp_buf env; +#endif + +/* static buffers */ +char stdinbuf[1]; /* stdin buffer */ +char *shcmd; /* shell command buffer */ +int shcmdsz; /* shell command buffer size */ +int shcmdi; /* shell command buffer index */ +char *ibuf; /* ed command-line buffer */ +int ibufsz; /* ed command-line buffer size */ +char *ibufp; /* pointer to ed command-line buffer */ + +/* global flags */ +int des = 0; /* if set, use crypt(3) for i/o */ +int garrulous = 0; /* if set, print all error messages */ +int isbinary; /* if set, buffer contains ASCII NULs */ +int isglobal; /* if set, doing a global command */ +int modified; /* if set, buffer modified since last write */ +int mutex = 0; /* if set, signals set "sigflags" */ +int red = 0; /* if set, restrict shell/directory access */ +int 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 */ +char *prompt; /* command-line prompt */ +char *dps = "*"; /* default command-line prompt */ + +char *usage = "usage: %s [-] [-sx] [-p string] [name]\n"; + +extern char errmsg[]; +extern int optind; +extern char *optarg; + +/* ed: line editor */ +int +main(argc, argv) + int argc; + char **argv; +{ + int c, n; + long status = 0; + + red = (n = strlen(argv[0])) > 2 && argv[0][n - 3] == 'r'; +top: + while ((c = getopt(argc, argv, "p:sx")) != EOF) + switch(c) { + case 'p': /* set prompt */ + prompt = optarg; + break; + case 's': /* run script */ + scripted = 1; + break; + case 'x': /* use crypt */ +#ifdef DES + des = get_keyword(); +#else + fprintf(stderr, "crypt unavailable\n?\n"); +#endif + break; + + default: + fprintf(stderr, usage, argv[0]); + exit(1); + } + argv += optind; + argc -= optind; + if (argc && **argv == '-') { + scripted = 1; + if (argc > 1) { + optind = 1; + goto top; + } + argv++; + argc--; + } + /* assert: reliable signals! */ +#ifdef SIGWINCH + 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)) +#else + if (status = setjmp(env)) +#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 != '!') + strcpy(old_filename, *argv); + } else if (argc) { + fputs("?\n", stderr); + if (**argv == '\0') + sprintf(errmsg, "invalid filename"); + if (!isatty(0)) + quit(2); + } + } + for (;;) { + if (status < 0 && garrulous) + fprintf(stderr, "%s\n", errmsg); + if (prompt) { + printf("%s", prompt); + fflush(stdout); + } + if ((n = 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)) { + fprintf(stderr, garrulous ? + "script, line %d: %s\n" : + "", lineno, errmsg); + quit(2); + } + clearerr(stdin); + modified = 0; + status = EMOD; + continue; + } else + quit(0); + } else if (ibuf[n - 1] != '\n') { + /* discard line */ + sprintf(errmsg, "unexpected end-of-file"); + clearerr(stdin); + status = ERR; + continue; + } + 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)) { + fprintf(stderr, garrulous ? + "script, line %d: %s\n" : + "", lineno, errmsg); + quit(2); + } + break; + case FATAL: + if (!isatty(0)) + fprintf(stderr, garrulous ? + "script, line %d: %s\n" : "", + lineno, errmsg); + else + fprintf(stderr, garrulous ? "%s\n" : "", + errmsg); + quit(3); + default: + fputs("?\n", stderr); + if (!isatty(0)) { + fprintf(stderr, garrulous ? + "script, line %d: %s\n" : "", + lineno, errmsg); + quit(2); + } + break; + } + } + /*NOTREACHED*/ +} + +long 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() +{ + 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(*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() +{ + 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(*ibufp)) { + STRTOL(n, ibufp); + addr += (c == '-' || c == '^') ? -n : n; + } else if (!isspace(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(*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() +{ + extern long u_current_addr; + extern long u_addr_last; + + static pattern_t *pat = NULL; + static int sgflag = 0; + static int 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(*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 != '!') strcpy(old_filename, fnp); +#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(*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) strcpy(old_filename, fnp); + 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')) + 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), 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(*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 != '!') + strcpy(old_filename, fnp); +#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(*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 != '!') + strcpy(old_filename, fnp); +#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("%d\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(n, m) + long n, 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(pat, dir) + 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)) { + 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() +{ + 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() +{ + 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(n) + 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(from, to) + 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(addr) + 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(addr) + 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(from, to) + long from, 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(from, to, gflag) + long from; + long to; + int gflag; +{ + line_t *bp; + line_t *ep; + char *s; + + if (!from) { + sprintf(errmsg, "invalid address"); + return ERR; + } + ep = 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(lp, n) + 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(n) + 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(lp) + 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(lp) + line_t *lp; +{ + line_t *np; + + if ((np = (line_t *) malloc(sizeof(line_t))) == NULL) { + fprintf(stderr, "%s\n", strerror(errno)); + sprintf(errmsg, "out of memory"); + return NULL; + } + np->seek = lp->seek; + np->len = lp->len; + return np; +} + + +/* has_trailing_escape: return the parity of escapes preceding a character + in a string */ +int +has_trailing_escape(s, t) + 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(s) + char *s; +{ + static char *file = NULL; + static int filesz = 0; + + int i = 0; + + REALLOC(file, filesz, MAXPATHLEN + 1, NULL); + /* assert: no trailing escape */ + while (file[i++] = (*s == '\\') ? *++s : *s) + s++; + return file; +} + + +void +signal_hup(signo) + int signo; +{ + if (mutex) + sigflags |= (1 << (signo - 1)); + else handle_hup(signo); +} + + +void +signal_int(signo) + int signo; +{ + if (mutex) + sigflags |= (1 << (signo - 1)); + else handle_int(signo); +} + + +void +handle_hup(signo) + 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(signo) + 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(signo) + 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(s) + 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 index 7553d5f2e332..f4b9f292944b 100644 --- a/bin/ed/re.c +++ b/bin/ed/re.c @@ -1,12 +1,9 @@ /* re.c: This file contains the regular expression interface routines for the ed line editor. */ /*- - * Copyright (c) 1993 The Regents of the University of California. + * Copyright (c) 1993 Andrew Moore, Talke Studio. * All rights reserved. * - * This code is derived from software contributed to Berkeley - * by Andrew Moore, Talke Studio. - * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -15,18 +12,11 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) @@ -37,40 +27,34 @@ */ #ifndef lint -static char sccsid[] = "@(#)re.c 5.5 (Berkeley) 3/28/93"; +static char *rcsid = "@(#)$Id: re.c,v 1.6 1994/02/01 00:34:43 alm Exp $"; #endif /* not lint */ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - #include "ed.h" -extern char *lhbuf; -extern int lhbufsz; -extern char *ibufp; -extern int ibufsz; + extern int patlock; -char errmsg[MAXFNAME + 40] = ""; +char errmsg[MAXPATHLEN + 40] = ""; -/* optpat: return pointer to compiled pattern from command buffer */ +/* get_compiled_pattern: return pointer to compiled pattern from command + buffer */ pattern_t * -optpat() +get_compiled_pattern() { static pattern_t *exp = NULL; char *exps; - char delim; + char delimiter; int n; - if ((delim = *ibufp) == ' ') { + if ((delimiter = *ibufp) == ' ') { sprintf(errmsg, "invalid pattern delimiter"); return NULL; - } else if (delim == '\n' || *++ibufp == '\n' || *ibufp == delim) { + } else if (delimiter == '\n' || *++ibufp == '\n' || *ibufp == delimiter) { if (!exp) sprintf(errmsg, "no previous pattern"); return exp; - } else if ((exps = getlhs(delim)) == NULL) + } else if ((exps = extract_pattern(delimiter)) == NULL) return NULL; /* buffer alloc'd && not reserved */ if (exp && !patlock) @@ -90,23 +74,24 @@ optpat() } -extern int isbinary; - -/* getlhs: copy a pattern string from the command buffer; return pointer - to the copy */ +/* extract_pattern: copy a pattern string from the command buffer; return + pointer to the copy */ char * -getlhs(delim) - int delim; +extract_pattern(delimiter) + int delimiter; { + static char *lhbuf = NULL; /* buffer */ + static int lhbufsz = 0; /* buffer size */ + char *nd; int len; - for (nd = ibufp; *nd != delim && *nd != '\n'; nd++) + for (nd = ibufp; *nd != delimiter && *nd != '\n'; nd++) switch (*nd) { default: break; case '[': - if ((nd = ccl(++nd)) == NULL) { + if ((nd = parse_char_class(++nd)) == NULL) { sprintf(errmsg, "unbalanced brackets ([])"); return NULL; } @@ -119,17 +104,17 @@ getlhs(delim) break; } len = nd - ibufp; - CKBUF(lhbuf, lhbufsz, len + 1, NULL); + REALLOC(lhbuf, lhbufsz, len + 1, NULL); memcpy(lhbuf, ibufp, len); lhbuf[len] = '\0'; ibufp = nd; - return (isbinary) ? nultonl(lhbuf, len) : lhbuf; + return (isbinary) ? NUL_TO_NEWLINE(lhbuf, len) : lhbuf; } -/* ccl: expand a POSIX character class */ +/* parse_char_class: expand a POSIX character class */ char * -ccl(s) +parse_char_class(s) char *s; { int c, d; diff --git a/bin/ed/sub.c b/bin/ed/sub.c new file mode 100644 index 000000000000..c93d258c8cd2 --- /dev/null +++ b/bin/ed/sub.c @@ -0,0 +1,263 @@ +/* sub.c: This file contains the substitution routines for the ed + line editor */ +/*- + * Copyright (c) 1993 Andrew Moore, Talke Studio. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char *rcsid = "@(#)$Id: sub.c,v 1.1 1994/02/01 00:34:44 alm Exp $"; +#endif /* not lint */ + +#include "ed.h" + + +char *rhbuf; /* rhs substitution buffer */ +int rhbufsz; /* rhs substitution buffer size */ +int rhbufi; /* rhs substitution buffer index */ + +/* extract_subst_tail: extract substitution tail from the command buffer */ +int +extract_subst_tail(flagp, np) + int *flagp; + int *np; +{ + char delimiter; + + *flagp = *np = 0; + if ((delimiter = *ibufp) == '\n') { + rhbufi = 0; + *flagp = GPR; + return 0; + } else if (extract_subst_template() == NULL) + return ERR; + else if (*ibufp == '\n') { + *flagp = GPR; + return 0; + } else if (*ibufp == delimiter) + ibufp++; + if ('1' <= *ibufp && *ibufp <= '9') { + STRTOL(*np, ibufp); + return 0; + } else if (*ibufp == 'g') { + ibufp++; + *flagp = GSG; + return 0; + } + return 0; +} + + +/* extract_subst_template: return pointer to copy of substitution template + in the command buffer */ +char * +extract_subst_template() +{ + int n = 0; + int i = 0; + char c; + char delimiter = *ibufp++; + + if (*ibufp == '%' && *(ibufp + 1) == delimiter) { + ibufp++; + if (!rhbuf) sprintf(errmsg, "no previous substitution"); + return rhbuf; + } + while (*ibufp != delimiter) { + REALLOC(rhbuf, rhbufsz, i + 2, NULL); + if ((c = rhbuf[i++] = *ibufp++) == '\n' && *ibufp == '\0') { + i--, ibufp--; + break; + } else if (c != '\\') + ; + else if ((rhbuf[i++] = *ibufp++) != '\n') + ; + else if (!isglobal) { + while ((n = get_tty_line()) == 0 || + n > 0 && ibuf[n - 1] != '\n') + clearerr(stdin); + if (n < 0) + return NULL; + } + } + REALLOC(rhbuf, rhbufsz, i + 1, NULL); + rhbuf[rhbufi = i] = '\0'; + return rhbuf; +} + + +char *rbuf; /* substitute_matching_text buffer */ +int rbufsz; /* substitute_matching_text buffer size */ + +/* search_and_replace: for each line in a range, change text matching a pattern + according to a substitution template; return status */ +int +search_and_replace(pat, gflag, kth) + pattern_t *pat; + int gflag; + int kth; +{ + undo_t *up; + char *txt; + char *eot; + long lc; + int nsubs = 0; + line_t *lp; + int len; + + current_addr = first_addr - 1; + for (lc = 0; lc <= second_addr - first_addr; lc++) { + lp = get_addressed_line_node(++current_addr); + if ((len = substitute_matching_text(pat, lp, gflag, kth)) < 0) + return ERR; + else if (len) { + up = NULL; + if (delete_lines(current_addr, current_addr) < 0) + return ERR; + txt = rbuf; + eot = rbuf + len; + SPL1(); + do { + if ((txt = put_sbuf_line(txt)) == NULL) { + SPL0(); + return ERR; + } else if (up) + up->t = get_addressed_line_node(current_addr); + else if ((up = push_undo_stack(UADD, + current_addr, current_addr)) == NULL) { + SPL0(); + return ERR; + } + } while (txt != eot); + SPL0(); + nsubs++; + } + } + if (nsubs == 0 && !(gflag & GLB)) { + sprintf(errmsg, "no match"); + return ERR; + } else if ((gflag & (GPR | GLS | GNP)) && + display_lines(current_addr, current_addr, gflag) < 0) + return ERR; + return 0; +} + + +/* substitute_matching_text: replace text matched by a pattern according to + a substitution template; return pointer to the modified text */ +int +substitute_matching_text(pat, lp, gflag, kth) + pattern_t *pat; + line_t *lp; + int gflag; + int kth; +{ + int off = 0; + int changed = 0; + int matchno = 0; + int i = 0; + regmatch_t rm[SE_MAX]; + char *txt; + char *eot; + + if ((txt = get_sbuf_line(lp)) == NULL) + return ERR; + if (isbinary) + NUL_TO_NEWLINE(txt, lp->len); + eot = txt + lp->len; + if (!regexec(pat, txt, SE_MAX, rm, 0)) { + do { + if (!kth || kth == ++matchno) { + changed++; + i = rm[0].rm_so; + REALLOC(rbuf, rbufsz, off + i, ERR); + if (isbinary) + NEWLINE_TO_NUL(txt, rm[0].rm_eo); + memcpy(rbuf + off, txt, i); + off += i; + if ((off = apply_subst_template(txt, rm, off, + pat->re_nsub)) < 0) + return ERR; + } else { + i = rm[0].rm_eo; + REALLOC(rbuf, rbufsz, off + i, ERR); + if (isbinary) + NEWLINE_TO_NUL(txt, i); + memcpy(rbuf + off, txt, i); + off += i; + } + txt += rm[0].rm_eo; + } while (*txt && (!changed || (gflag & GSG) && rm[0].rm_eo) && + !regexec(pat, txt, SE_MAX, rm, REG_NOTBOL)); + i = eot - txt; + REALLOC(rbuf, rbufsz, off + i + 2, ERR); + if (i > 0 && !rm[0].rm_eo && (gflag & GSG)) { + sprintf(errmsg, "infinite substitution loop"); + return ERR; + } + if (isbinary) + NEWLINE_TO_NUL(txt, i); + memcpy(rbuf + off, txt, i); + memcpy(rbuf + off + i, "\n", 2); + } + return changed ? off + i + 1 : 0; +} + + +/* apply_subst_template: modify text according to a substitution template; + return offset to end of modified text */ +int +apply_subst_template(boln, rm, off, re_nsub) + char *boln; + regmatch_t *rm; + int off; + int re_nsub; +{ + int j = 0; + int k = 0; + int n; + char *sub = rhbuf; + + for (; sub - rhbuf < rhbufi; sub++) + if (*sub == '&') { + j = rm[0].rm_so; + k = rm[0].rm_eo; + REALLOC(rbuf, rbufsz, off + k - j, ERR); + while (j < k) + rbuf[off++] = boln[j++]; + } else if (*sub == '\\' && '1' <= *++sub && *sub <= '9' && + (n = *sub - '0') <= re_nsub) { + j = rm[n].rm_so; + k = rm[n].rm_eo; + REALLOC(rbuf, rbufsz, off + k - j, ERR); + while (j < k) + rbuf[off++] = boln[j++]; + } else { + REALLOC(rbuf, rbufsz, off + 1, ERR); + rbuf[off++] = *sub; + } + REALLOC(rbuf, rbufsz, off + 1, ERR); + rbuf[off] = '\0'; + return off; +} diff --git a/bin/ed/test/Makefile b/bin/ed/test/Makefile index ca45a5183ebe..84166c141679 100644 --- a/bin/ed/test/Makefile +++ b/bin/ed/test/Makefile @@ -1,17 +1,23 @@ +SHELL= /bin/sh ED= ../obj/ed -all: build test - @echo done +all: check + @: + +check: build test + @if grep -h '\*\*\*' errs.o scripts.o; then :; else \ + echo "tests completed successfully."; \ + fi build: mkscripts.sh - @echo building test scripts... - @chmod +x mkscripts.sh - @./mkscripts.sh ${ED} + @if [ -f errs.o ]; then :; else \ + echo "building test scripts for $(ED) ..."; \ + $(SHELL) mkscripts.sh $(ED); \ + fi test: build ckscripts.sh - @echo running test scripts... - @chmod +x ckscripts.sh - @./ckscripts.sh ${ED} + @echo testing $(ED) ... + @$(SHELL) ckscripts.sh $(ED) clean: - rm -f *.ed *.[oz] *~ + rm -f *.ed *.red *.[oz] *~ diff --git a/bin/ed/test/README b/bin/ed/test/README index 8917f36acec8..ab66b9228cd5 100644 --- a/bin/ed/test/README +++ b/bin/ed/test/README @@ -19,14 +19,6 @@ which look like: or: *** Output u.o of script u.ed is incorrect *** -It is assumed that the ed being tested processes escapes (\) in file names. -This is so that a name starting with bang (!) can be read, via: - r \!file -Without the escape, a POSIX ed would attempt to read the output of -the shell command `file'. If the ed being tested does not support escape -processing on file names, then the script `mkscripts.sh' should be modified -accordingly. - The POSIX requirement that an address range not be used where at most a single address is expected has been relaxed in this version of ed. Therefore, the following scripts which test for compliance with this @@ -36,6 +28,3 @@ a1-err.ed i1-err.ed k1-err.ed r1-err.ed - -In addition, one of bang1-err.ed or bang2.ed will fail, depending on whether or -not ed was compiled with the VI_BANG directive. diff --git a/bin/ed/test/addr.d b/bin/ed/test/addr.d new file mode 100644 index 000000000000..8f7ba1b5d359 --- /dev/null +++ b/bin/ed/test/addr.d @@ -0,0 +1,9 @@ +line 1 +line 2 +line 3 +line 4 +line5 +1ine6 +line7 +line8 +line9 diff --git a/bin/ed/test/addr.r b/bin/ed/test/addr.r new file mode 100644 index 000000000000..04caf17f4228 --- /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 000000000000..750b224ed888 --- /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/bang2.r b/bin/ed/test/bang2.r deleted file mode 100644 index 65f58a262a1b..000000000000 --- a/bin/ed/test/bang2.r +++ /dev/null @@ -1,4 +0,0 @@ -line 1 -hello world! -line 4 -hello world diff --git a/bin/ed/test/bang2.t b/bin/ed/test/bang2.t deleted file mode 100644 index 14c681d44ade..000000000000 --- a/bin/ed/test/bang2.t +++ /dev/null @@ -1,2 +0,0 @@ -.!echo hello world -2,3!echo hello world! diff --git a/bin/ed/test/ckscripts.sh b/bin/ed/test/ckscripts.sh index 87a7e9beb560..edc935d28701 100644 --- a/bin/ed/test/ckscripts.sh +++ b/bin/ed/test/ckscripts.sh @@ -5,26 +5,24 @@ PATH="/bin:/usr/bin:/usr/local/bin/:." ED=$1 -[ X"$ED" = X -o ! -x $ED ] && ED="../ed" [ ! -x $ED ] && { echo "$ED: cannot execute"; exit 1; } -# Run the *-err.ed scripts first, since these don't generate output; -# rename then to *-err.ed~; they exit with non-zero status -for i in *-err.ed; do - echo $i~ +# Run the *.red scripts first, since these don't generate output; +# they exit with non-zero status +for i in *.red; do + echo $i if $i; then - echo "*** The script $i~ exited abnormally ***" + echo "*** The script $i exited abnormally ***" fi - mv $i $i~ done >errs.o 2>&1 # Run the remainding scripts; they exit with zero status for i in *.ed; do - base=`expr $i : '\([^.]*\)'` +# base=`expr $i : '\([^.]*\)'` # base=`echo $i | sed 's/\..*//'` -# base=`$ED - \!"echo \\\\$i" <<-EOF -# s/\..* -# EOF` + base=`$ED - \!"echo $i" <<-EOF + s/\..* + EOF` if $base.ed; then if cmp -s $base.o $base.r; then :; else echo "*** Output $base.o of script $i is incorrect ***" diff --git a/bin/ed/test/bang2.d b/bin/ed/test/g5.d index 92f337e977f2..a92d664bc20a 100644 --- a/bin/ed/test/bang2.d +++ b/bin/ed/test/g5.d @@ -1,5 +1,3 @@ line 1 line 2 line 3 -line 4 -line5 diff --git a/bin/ed/test/g5.r b/bin/ed/test/g5.r new file mode 100644 index 000000000000..15a26758bac2 --- /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 000000000000..e213481d54ce --- /dev/null +++ b/bin/ed/test/g5.t @@ -0,0 +1,2 @@ +g/./1,3t$\ +1d diff --git a/bin/ed/test/mkscripts.sh b/bin/ed/test/mkscripts.sh index 724db0cc05a9..fa3147f56328 100644 --- a/bin/ed/test/mkscripts.sh +++ b/bin/ed/test/mkscripts.sh @@ -3,69 +3,71 @@ PATH="/bin:/usr/bin:/usr/local/bin/:." ED=$1 -[ X"$ED" = X -o ! -x $ED ] && ED="../ed" [ ! -x $ED ] && { echo "$ED: cannot execute"; exit 1; } for i in *.t; do # base=${i%.*} # base=`echo $i | sed 's/\..*//'` - base=`expr $i : '\([^.]*\)'` - ( - echo "#!/bin/sh -" - echo "$ED - <<\EOT" - echo "r \\$base.d" - cat $i - echo "w \\$base.o" - echo EOT - ) >$base.ed - chmod +x $base.ed -# The following is pretty ugly and not appropriate use of ed -# but the point is that it can be done... -# base=`$ED - \!"echo \\\\$i" <<-EOF -# s/\..* -# EOF` -# $ED - <<-EOF -# a -# #!/bin/sh - -# $ED - <<\EOT -# r \\$base.d -# w \\$base.o -# EOT -# . -# -2r \\$i -# w \\$base.ed -# !chmod +x \\$base.ed -# EOF +# base=`expr $i : '\([^.]*\)'` +# ( +# echo "#!/bin/sh -" +# echo "$ED - <<\EOT" +# echo "r $base.d" +# cat $i +# echo "w $base.o" +# echo EOT +# ) >$base.ed +# chmod +x $base.ed +# The following is pretty ugly way of doing the above, and not appropriate +# use of ed but the point is that it can be done... + base=`$ED - \!"echo $i" <<-EOF + s/\..* + EOF` + $ED - <<-EOF + a + #!/bin/sh - + $ED - <<\EOT + H + r $base.d + w $base.o + EOT + . + -2r $i + w $base.ed + !chmod +x $base.ed + EOF done for i in *.err; do # base=${i%.*} # base=`echo $i | sed 's/\..*//'` - base=`expr $i : '\([^.]*\)'` - ( - echo "#!/bin/sh -" - echo "$ED - <<\EOT" - echo H - echo "r \\$base.err" - cat $i - echo "w \\$base.o" - echo EOT - ) >$base-err.ed - chmod +x $base-err.ed -# base=`$ED - \!"echo \\\\$i" <<-EOF -# s/\..* -# EOF` -# $ED - <<-EOF -# a -# #!/bin/sh - -# $ED - <<\EOT -# H -# r \\$base.err -# w \\$base.o -# EOT -# . -# -2r \\$i -# w \\${base}-err.ed -# !chmod +x ${base}-err.ed -# EOF +# base=`expr $i : '\([^.]*\)'` +# ( +# echo "#!/bin/sh -" +# echo "$ED - <<\EOT" +# echo H +# echo "r $base.err" +# cat $i +# echo "w $base.o" +# echo EOT +# ) >$base-err.ed +# chmod +x $base-err.ed +# The following is pretty ugly way of doing the above, and not appropriate +# use of ed but the point is that it can be done... + base=`$ED - \!"echo $i" <<-EOF + s/\..* + EOF` + $ED - <<-EOF + a + #!/bin/sh - + $ED - <<\EOT + H + r $base.err + w $base.o + EOT + . + -2r $i + w ${base}.red + !chmod +x ${base}.red + EOF done diff --git a/bin/ed/undo.c b/bin/ed/undo.c new file mode 100644 index 000000000000..a12e1c48e753 --- /dev/null +++ b/bin/ed/undo.c @@ -0,0 +1,154 @@ +/* undo.c: This file contains the undo routines for the ed line editor */ +/*- + * Copyright (c) 1993 Andrew Moore, Talke Studio. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char *rcsid = "@(#)$Id: undo.c,v 1.1 1994/02/01 00:34:44 alm Exp $"; +#endif /* not lint */ + +#include "ed.h" + + +#define USIZE 100 /* undo stack size */ +undo_t *ustack = NULL; /* undo stack */ +long usize = 0; /* stack size variable */ +long u_p = 0; /* undo stack pointer */ + +/* push_undo_stack: return pointer to intialized undo node */ +undo_t * +push_undo_stack(type, from, to) + int type; + long from; + long to; +{ + undo_t *t; + +#if defined(sun) || defined(NO_REALLOC_NULL) + if (ustack == NULL && + (ustack = (undo_t *) malloc((usize = USIZE) * sizeof(undo_t))) == NULL) { + fprintf(stderr, "%s\n", strerror(errno)); + sprintf(errmsg, "out of memory"); + return NULL; + } +#endif + t = ustack; + if (u_p < usize || + (t = (undo_t *) realloc(ustack, (usize += USIZE) * sizeof(undo_t))) != NULL) { + ustack = t; + ustack[u_p].type = type; + ustack[u_p].t = get_addressed_line_node(to); + ustack[u_p].h = get_addressed_line_node(from); + return ustack + u_p++; + } + /* out of memory - release undo stack */ + fprintf(stderr, "%s\n", strerror(errno)); + sprintf(errmsg, "out of memory"); + clear_undo_stack(); + free(ustack); + ustack = NULL; + usize = 0; + return NULL; +} + + +/* USWAP: swap undo nodes */ +#define USWAP(x,y) { \ + undo_t utmp; \ + utmp = x, x = y, y = utmp; \ +} + + +long u_current_addr = -1; /* if >= 0, undo enabled */ +long u_addr_last = -1; /* if >= 0, undo enabled */ + +/* pop_undo_stack: undo last change to the editor buffer */ +int +pop_undo_stack() +{ + long n; + long o_current_addr = current_addr; + long o_addr_last = addr_last; + + if (u_current_addr == -1 || u_addr_last == -1) { + sprintf(errmsg, "nothing to undo"); + return ERR; + } else if (u_p) + modified = 1; + get_addressed_line_node(0); /* this get_addressed_line_node last! */ + SPL1(); + for (n = u_p; n-- > 0;) { + switch(ustack[n].type) { + case UADD: + REQUE(ustack[n].h->q_back, ustack[n].t->q_forw); + break; + case UDEL: + REQUE(ustack[n].h->q_back, ustack[n].h); + REQUE(ustack[n].t, ustack[n].t->q_forw); + break; + case UMOV: + case VMOV: + REQUE(ustack[n - 1].h, ustack[n].h->q_forw); + REQUE(ustack[n].t->q_back, ustack[n - 1].t); + REQUE(ustack[n].h, ustack[n].t); + n--; + break; + default: + /*NOTREACHED*/ + ; + } + ustack[n].type ^= 1; + } + /* reverse undo stack order */ + for (n = u_p; n-- > (u_p + 1)/ 2;) + USWAP(ustack[n], ustack[u_p - 1 - n]); + if (isglobal) + clear_active_list(); + current_addr = u_current_addr, u_current_addr = o_current_addr; + addr_last = u_addr_last, u_addr_last = o_addr_last; + SPL0(); + return 0; +} + + +/* clear_undo_stack: clear the undo stack */ +void +clear_undo_stack() +{ + line_t *lp, *ep, *tl; + + while (u_p--) + if (ustack[u_p].type == UDEL) { + ep = ustack[u_p].t->q_forw; + for (lp = ustack[u_p].h; lp != ep; lp = tl) { + unmark_line_node(lp); + tl = lp->q_forw; + free(lp); + } + } + u_p = 0; + u_current_addr = current_addr; + u_addr_last = addr_last; +} |
