From 5fd83771641d15c418f747bd343ba6738d3875f7 Mon Sep 17 00:00:00 2001 From: Cameron Katri Date: Sun, 9 May 2021 14:20:58 -0400 Subject: Import macOS userland adv_cmds-176 basic_cmds-55 bootstrap_cmds-116.100.1 developer_cmds-66 diskdev_cmds-667.40.1 doc_cmds-53.60.1 file_cmds-321.40.3 mail_cmds-35 misc_cmds-34 network_cmds-606.40.1 patch_cmds-17 remote_cmds-63 shell_cmds-216.60.1 system_cmds-880.60.2 text_cmds-106 --- text_cmds/ed/POSIX | 101 +++ text_cmds/ed/README | 24 + text_cmds/ed/buf.c | 284 ++++++++ text_cmds/ed/cbc.c | 402 +++++++++++ text_cmds/ed/ed.1 | 1004 +++++++++++++++++++++++++++ text_cmds/ed/ed.h | 282 ++++++++ text_cmds/ed/glbl.c | 218 ++++++ text_cmds/ed/io.c | 341 ++++++++++ text_cmds/ed/main.c | 1455 ++++++++++++++++++++++++++++++++++++++++ text_cmds/ed/re.c | 132 ++++ text_cmds/ed/red.1 | 1 + text_cmds/ed/sub.c | 256 +++++++ text_cmds/ed/test/=.err | 1 + text_cmds/ed/test/README | 32 + text_cmds/ed/test/TODO | 15 + text_cmds/ed/test/a.d | 5 + text_cmds/ed/test/a.r | 8 + text_cmds/ed/test/a.t | 9 + text_cmds/ed/test/a1.err | 3 + text_cmds/ed/test/a2.err | 3 + text_cmds/ed/test/addr.d | 9 + text_cmds/ed/test/addr.r | 2 + text_cmds/ed/test/addr.t | 5 + text_cmds/ed/test/addr1.err | 1 + text_cmds/ed/test/addr2.err | 1 + text_cmds/ed/test/ascii.d.uu | 9 + text_cmds/ed/test/ascii.r.uu | 9 + text_cmds/ed/test/ascii.t | 0 text_cmds/ed/test/bang1.d | 0 text_cmds/ed/test/bang1.err | 1 + text_cmds/ed/test/bang1.r | 1 + text_cmds/ed/test/bang1.t | 5 + text_cmds/ed/test/bang2.err | 1 + text_cmds/ed/test/c.d | 5 + text_cmds/ed/test/c.r | 4 + text_cmds/ed/test/c.t | 12 + text_cmds/ed/test/c1.err | 3 + text_cmds/ed/test/c2.err | 3 + text_cmds/ed/test/ckscripts.sh | 37 + text_cmds/ed/test/d.d | 5 + text_cmds/ed/test/d.err | 1 + text_cmds/ed/test/d.r | 1 + text_cmds/ed/test/d.t | 3 + text_cmds/ed/test/e1.d | 1 + text_cmds/ed/test/e1.err | 1 + text_cmds/ed/test/e1.r | 1 + text_cmds/ed/test/e1.t | 1 + text_cmds/ed/test/e2.d | 1 + text_cmds/ed/test/e2.err | 1 + text_cmds/ed/test/e2.r | 1 + text_cmds/ed/test/e2.t | 1 + text_cmds/ed/test/e3.d | 1 + text_cmds/ed/test/e3.err | 1 + text_cmds/ed/test/e3.r | 1 + text_cmds/ed/test/e3.t | 1 + text_cmds/ed/test/e4.d | 1 + text_cmds/ed/test/e4.r | 1 + text_cmds/ed/test/e4.t | 1 + text_cmds/ed/test/f1.err | 1 + text_cmds/ed/test/f2.err | 1 + text_cmds/ed/test/g1.d | 5 + text_cmds/ed/test/g1.err | 1 + text_cmds/ed/test/g1.r | 15 + text_cmds/ed/test/g1.t | 6 + text_cmds/ed/test/g2.d | 5 + text_cmds/ed/test/g2.err | 1 + text_cmds/ed/test/g2.r | 1 + text_cmds/ed/test/g2.t | 2 + text_cmds/ed/test/g3.d | 5 + text_cmds/ed/test/g3.err | 1 + text_cmds/ed/test/g3.r | 5 + text_cmds/ed/test/g3.t | 4 + text_cmds/ed/test/g4.d | 5 + text_cmds/ed/test/g4.r | 7 + text_cmds/ed/test/g4.t | 13 + text_cmds/ed/test/g5.d | 3 + text_cmds/ed/test/g5.r | 9 + text_cmds/ed/test/g5.t | 2 + text_cmds/ed/test/h.err | 1 + text_cmds/ed/test/i.d | 5 + text_cmds/ed/test/i.r | 8 + text_cmds/ed/test/i.t | 9 + text_cmds/ed/test/i1.err | 3 + text_cmds/ed/test/i2.err | 3 + text_cmds/ed/test/i3.err | 3 + text_cmds/ed/test/j.d | 5 + text_cmds/ed/test/j.r | 4 + text_cmds/ed/test/j.t | 2 + text_cmds/ed/test/k.d | 5 + text_cmds/ed/test/k.r | 5 + text_cmds/ed/test/k.t | 10 + text_cmds/ed/test/k1.err | 1 + text_cmds/ed/test/k2.err | 1 + text_cmds/ed/test/k3.err | 1 + text_cmds/ed/test/k4.err | 6 + text_cmds/ed/test/l.d | 0 text_cmds/ed/test/l.r | 0 text_cmds/ed/test/l.t | 0 text_cmds/ed/test/m.d | 5 + text_cmds/ed/test/m.err | 4 + text_cmds/ed/test/m.r | 5 + text_cmds/ed/test/m.t | 7 + text_cmds/ed/test/mkscripts.sh | 75 +++ text_cmds/ed/test/n.d | 0 text_cmds/ed/test/n.r | 0 text_cmds/ed/test/n.t | 0 text_cmds/ed/test/nl.err | 1 + text_cmds/ed/test/nl1.d | 5 + text_cmds/ed/test/nl1.r | 8 + text_cmds/ed/test/nl1.t | 8 + text_cmds/ed/test/nl2.d | 5 + text_cmds/ed/test/nl2.r | 6 + text_cmds/ed/test/nl2.t | 4 + text_cmds/ed/test/p.d | 0 text_cmds/ed/test/p.r | 0 text_cmds/ed/test/p.t | 0 text_cmds/ed/test/q.d | 0 text_cmds/ed/test/q.r | 0 text_cmds/ed/test/q.t | 5 + text_cmds/ed/test/q1.err | 1 + text_cmds/ed/test/r1.d | 5 + text_cmds/ed/test/r1.err | 1 + text_cmds/ed/test/r1.r | 7 + text_cmds/ed/test/r1.t | 3 + text_cmds/ed/test/r2.d | 5 + text_cmds/ed/test/r2.err | 1 + text_cmds/ed/test/r2.r | 10 + text_cmds/ed/test/r2.t | 1 + text_cmds/ed/test/r3.d | 1 + text_cmds/ed/test/r3.r | 2 + text_cmds/ed/test/r3.t | 1 + text_cmds/ed/test/s1.d | 5 + text_cmds/ed/test/s1.err | 1 + text_cmds/ed/test/s1.r | 5 + text_cmds/ed/test/s1.t | 6 + text_cmds/ed/test/s10.err | 4 + text_cmds/ed/test/s2.d | 5 + text_cmds/ed/test/s2.err | 4 + text_cmds/ed/test/s2.r | 5 + text_cmds/ed/test/s2.t | 4 + text_cmds/ed/test/s3.d | 0 text_cmds/ed/test/s3.err | 1 + text_cmds/ed/test/s3.r | 1 + text_cmds/ed/test/s3.t | 6 + text_cmds/ed/test/s4.err | 1 + text_cmds/ed/test/s5.err | 1 + text_cmds/ed/test/s6.err | 1 + text_cmds/ed/test/s7.err | 5 + text_cmds/ed/test/s8.err | 4 + text_cmds/ed/test/s9.err | 4 + text_cmds/ed/test/t.d | 5 + text_cmds/ed/test/t.r | 16 + text_cmds/ed/test/t1.d | 5 + text_cmds/ed/test/t1.err | 1 + text_cmds/ed/test/t1.r | 16 + text_cmds/ed/test/t1.t | 3 + text_cmds/ed/test/t2.d | 5 + text_cmds/ed/test/t2.err | 1 + text_cmds/ed/test/t2.r | 6 + text_cmds/ed/test/t2.t | 1 + text_cmds/ed/test/u.d | 5 + text_cmds/ed/test/u.err | 1 + text_cmds/ed/test/u.r | 9 + text_cmds/ed/test/u.t | 31 + text_cmds/ed/test/v.d | 5 + text_cmds/ed/test/v.r | 11 + text_cmds/ed/test/v.t | 6 + text_cmds/ed/test/w.d | 5 + text_cmds/ed/test/w.r | 10 + text_cmds/ed/test/w.t | 2 + text_cmds/ed/test/w1.err | 1 + text_cmds/ed/test/w2.err | 1 + text_cmds/ed/test/w3.err | 1 + text_cmds/ed/test/x.err | 1 + text_cmds/ed/test/z.err | 2 + text_cmds/ed/undo.c | 150 +++++ 176 files changed, 5412 insertions(+) create mode 100644 text_cmds/ed/POSIX create mode 100644 text_cmds/ed/README create mode 100644 text_cmds/ed/buf.c create mode 100644 text_cmds/ed/cbc.c create mode 100644 text_cmds/ed/ed.1 create mode 100644 text_cmds/ed/ed.h create mode 100644 text_cmds/ed/glbl.c create mode 100644 text_cmds/ed/io.c create mode 100644 text_cmds/ed/main.c create mode 100644 text_cmds/ed/re.c create mode 100644 text_cmds/ed/red.1 create mode 100644 text_cmds/ed/sub.c create mode 100644 text_cmds/ed/test/=.err create mode 100644 text_cmds/ed/test/README create mode 100644 text_cmds/ed/test/TODO create mode 100644 text_cmds/ed/test/a.d create mode 100644 text_cmds/ed/test/a.r create mode 100644 text_cmds/ed/test/a.t create mode 100644 text_cmds/ed/test/a1.err create mode 100644 text_cmds/ed/test/a2.err create mode 100644 text_cmds/ed/test/addr.d create mode 100644 text_cmds/ed/test/addr.r create mode 100644 text_cmds/ed/test/addr.t create mode 100644 text_cmds/ed/test/addr1.err create mode 100644 text_cmds/ed/test/addr2.err create mode 100644 text_cmds/ed/test/ascii.d.uu create mode 100644 text_cmds/ed/test/ascii.r.uu create mode 100644 text_cmds/ed/test/ascii.t create mode 100644 text_cmds/ed/test/bang1.d create mode 100644 text_cmds/ed/test/bang1.err create mode 100644 text_cmds/ed/test/bang1.r create mode 100644 text_cmds/ed/test/bang1.t create mode 100644 text_cmds/ed/test/bang2.err create mode 100644 text_cmds/ed/test/c.d create mode 100644 text_cmds/ed/test/c.r create mode 100644 text_cmds/ed/test/c.t create mode 100644 text_cmds/ed/test/c1.err create mode 100644 text_cmds/ed/test/c2.err create mode 100755 text_cmds/ed/test/ckscripts.sh create mode 100644 text_cmds/ed/test/d.d create mode 100644 text_cmds/ed/test/d.err create mode 100644 text_cmds/ed/test/d.r create mode 100644 text_cmds/ed/test/d.t create mode 100644 text_cmds/ed/test/e1.d create mode 100644 text_cmds/ed/test/e1.err create mode 100644 text_cmds/ed/test/e1.r create mode 100644 text_cmds/ed/test/e1.t create mode 100644 text_cmds/ed/test/e2.d create mode 100644 text_cmds/ed/test/e2.err create mode 100644 text_cmds/ed/test/e2.r create mode 100644 text_cmds/ed/test/e2.t create mode 100644 text_cmds/ed/test/e3.d create mode 100644 text_cmds/ed/test/e3.err create mode 100644 text_cmds/ed/test/e3.r create mode 100644 text_cmds/ed/test/e3.t create mode 100644 text_cmds/ed/test/e4.d create mode 100644 text_cmds/ed/test/e4.r create mode 100644 text_cmds/ed/test/e4.t create mode 100644 text_cmds/ed/test/f1.err create mode 100644 text_cmds/ed/test/f2.err create mode 100644 text_cmds/ed/test/g1.d create mode 100644 text_cmds/ed/test/g1.err create mode 100644 text_cmds/ed/test/g1.r create mode 100644 text_cmds/ed/test/g1.t create mode 100644 text_cmds/ed/test/g2.d create mode 100644 text_cmds/ed/test/g2.err create mode 100644 text_cmds/ed/test/g2.r create mode 100644 text_cmds/ed/test/g2.t create mode 100644 text_cmds/ed/test/g3.d create mode 100644 text_cmds/ed/test/g3.err create mode 100644 text_cmds/ed/test/g3.r create mode 100644 text_cmds/ed/test/g3.t create mode 100644 text_cmds/ed/test/g4.d create mode 100644 text_cmds/ed/test/g4.r create mode 100644 text_cmds/ed/test/g4.t create mode 100644 text_cmds/ed/test/g5.d create mode 100644 text_cmds/ed/test/g5.r create mode 100644 text_cmds/ed/test/g5.t create mode 100644 text_cmds/ed/test/h.err create mode 100644 text_cmds/ed/test/i.d create mode 100644 text_cmds/ed/test/i.r create mode 100644 text_cmds/ed/test/i.t create mode 100644 text_cmds/ed/test/i1.err create mode 100644 text_cmds/ed/test/i2.err create mode 100644 text_cmds/ed/test/i3.err create mode 100644 text_cmds/ed/test/j.d create mode 100644 text_cmds/ed/test/j.r create mode 100644 text_cmds/ed/test/j.t create mode 100644 text_cmds/ed/test/k.d create mode 100644 text_cmds/ed/test/k.r create mode 100644 text_cmds/ed/test/k.t create mode 100644 text_cmds/ed/test/k1.err create mode 100644 text_cmds/ed/test/k2.err create mode 100644 text_cmds/ed/test/k3.err create mode 100644 text_cmds/ed/test/k4.err create mode 100644 text_cmds/ed/test/l.d create mode 100644 text_cmds/ed/test/l.r create mode 100644 text_cmds/ed/test/l.t create mode 100644 text_cmds/ed/test/m.d create mode 100644 text_cmds/ed/test/m.err create mode 100644 text_cmds/ed/test/m.r create mode 100644 text_cmds/ed/test/m.t create mode 100755 text_cmds/ed/test/mkscripts.sh create mode 100644 text_cmds/ed/test/n.d create mode 100644 text_cmds/ed/test/n.r create mode 100644 text_cmds/ed/test/n.t create mode 100644 text_cmds/ed/test/nl.err create mode 100644 text_cmds/ed/test/nl1.d create mode 100644 text_cmds/ed/test/nl1.r create mode 100644 text_cmds/ed/test/nl1.t create mode 100644 text_cmds/ed/test/nl2.d create mode 100644 text_cmds/ed/test/nl2.r create mode 100644 text_cmds/ed/test/nl2.t create mode 100644 text_cmds/ed/test/p.d create mode 100644 text_cmds/ed/test/p.r create mode 100644 text_cmds/ed/test/p.t create mode 100644 text_cmds/ed/test/q.d create mode 100644 text_cmds/ed/test/q.r create mode 100644 text_cmds/ed/test/q.t create mode 100644 text_cmds/ed/test/q1.err create mode 100644 text_cmds/ed/test/r1.d create mode 100644 text_cmds/ed/test/r1.err create mode 100644 text_cmds/ed/test/r1.r create mode 100644 text_cmds/ed/test/r1.t create mode 100644 text_cmds/ed/test/r2.d create mode 100644 text_cmds/ed/test/r2.err create mode 100644 text_cmds/ed/test/r2.r create mode 100644 text_cmds/ed/test/r2.t create mode 100644 text_cmds/ed/test/r3.d create mode 100644 text_cmds/ed/test/r3.r create mode 100644 text_cmds/ed/test/r3.t create mode 100644 text_cmds/ed/test/s1.d create mode 100644 text_cmds/ed/test/s1.err create mode 100644 text_cmds/ed/test/s1.r create mode 100644 text_cmds/ed/test/s1.t create mode 100644 text_cmds/ed/test/s10.err create mode 100644 text_cmds/ed/test/s2.d create mode 100644 text_cmds/ed/test/s2.err create mode 100644 text_cmds/ed/test/s2.r create mode 100644 text_cmds/ed/test/s2.t create mode 100644 text_cmds/ed/test/s3.d create mode 100644 text_cmds/ed/test/s3.err create mode 100644 text_cmds/ed/test/s3.r create mode 100644 text_cmds/ed/test/s3.t create mode 100644 text_cmds/ed/test/s4.err create mode 100644 text_cmds/ed/test/s5.err create mode 100644 text_cmds/ed/test/s6.err create mode 100644 text_cmds/ed/test/s7.err create mode 100644 text_cmds/ed/test/s8.err create mode 100644 text_cmds/ed/test/s9.err create mode 100644 text_cmds/ed/test/t.d create mode 100644 text_cmds/ed/test/t.r create mode 100644 text_cmds/ed/test/t1.d create mode 100644 text_cmds/ed/test/t1.err create mode 100644 text_cmds/ed/test/t1.r create mode 100644 text_cmds/ed/test/t1.t create mode 100644 text_cmds/ed/test/t2.d create mode 100644 text_cmds/ed/test/t2.err create mode 100644 text_cmds/ed/test/t2.r create mode 100644 text_cmds/ed/test/t2.t create mode 100644 text_cmds/ed/test/u.d create mode 100644 text_cmds/ed/test/u.err create mode 100644 text_cmds/ed/test/u.r create mode 100644 text_cmds/ed/test/u.t create mode 100644 text_cmds/ed/test/v.d create mode 100644 text_cmds/ed/test/v.r create mode 100644 text_cmds/ed/test/v.t create mode 100644 text_cmds/ed/test/w.d create mode 100644 text_cmds/ed/test/w.r create mode 100644 text_cmds/ed/test/w.t create mode 100644 text_cmds/ed/test/w1.err create mode 100644 text_cmds/ed/test/w2.err create mode 100644 text_cmds/ed/test/w3.err create mode 100644 text_cmds/ed/test/x.err create mode 100644 text_cmds/ed/test/z.err create mode 100644 text_cmds/ed/undo.c (limited to 'text_cmds/ed') diff --git a/text_cmds/ed/POSIX b/text_cmds/ed/POSIX new file mode 100644 index 0000000..0a363d6 --- /dev/null +++ b/text_cmds/ed/POSIX @@ -0,0 +1,101 @@ +$FreeBSD: src/bin/ed/POSIX,v 1.8 2000/07/17 10:40:48 sheldonh Exp $ + +This version of ed(1) is not strictly POSIX compliant, as described in +the POSIX 1003.2 document. The following is a summary of the omissions, +extensions and possible deviations from POSIX 1003.2. + +OMISSIONS +--------- +1) 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. + +2) To support the BSD `s' command (see extension [1] below), + substitution patterns cannot be delimited by numbers or the characters + `r', `g' and `p'. In contrast, POSIX specifies any character expect + space or newline can used as a delimiter. + +EXTENSIONS +---------- +1) BSD commands have been implemented wherever they do not conflict with + the POSIX standard. The BSD-ism's included are: + i) `s' (i.e., s[n][rgp]*) to repeat a previous substitution, + ii) `W' for appending text to an existing file, + iii) `wq' for exiting after a write, + iv) `z' for scrolling through the buffer, and + v) BSD line addressing syntax (i.e., `^' and `%') is recognized. + +2) If crypt(3) is available, files can be read and written using DES + encryption. The `x' command prompts the user to enter a key used for + encrypting/ decrypting subsequent reads and writes. If only a newline + is entered as the key, then encryption is disabled. Otherwise, a key + is read in the same manner as a password entry. The key remains in + effect until encryption is disabled. For more information on the + encryption algorithm, see the bdes(1) man page. Encryption/decryption + should be fully compatible with SunOS des(1). + +3) The POSIX interactive global commands `G' and `V' are extended to + support multiple commands, including `a', `i' and `c'. The command + format is the same as for the global commands `g' and `v', i.e., one + command per line with each line, except for the last, ending in a + backslash (\). + +4) An extension to the POSIX file commands `E', `e', `r', `W' and `w' is + that arguments are processed for backslash escapes, i.e., any + character preceded by a backslash is interpreted literally. If the + first unescaped character of a argument is a bang (!), then the + rest of the line is interpreted as a shell command, and no escape + processing is performed by ed. + +5) For SunOS ed(1) compatibility, ed runs in restricted mode if invoked + as red. This limits editing of files in the local directory only and + prohibits shell commands. + +DEVIATIONS +---------- +1) Though ed is not a stream editor, it can be used to edit binary files. + To assist in binary editing, when a file containing at least one ASCII + NUL character is written, a newline is not appended if it did not + already contain one upon reading. In particular, reading /dev/null + prior to writing prevents appending a newline to a binary file. + + For example, to create a file with ed containing a single NUL character: + $ ed file + a + ^@ + . + r /dev/null + wq + + Similarly, to remove a newline from the end of binary `file': + $ ed file + r /dev/null + wq + +2) Since the behavior of `u' (undo) within a `g' (global) command list is + not specified by POSIX, it follows the behavior of the SunOS ed: + undo forces a global command list to be executed only once, rather than + for each line matching a global pattern. In addtion, each instance of + `u' within a global command undoes all previous commands (including + undo's) in the command list. This seems the best way, since the + alternatives are either too complicated to implement or too confusing + to use. + + The global/undo combination is useful for masking errors that + would otherwise cause a script to fail. For instance, an ed script + to remove any occurences of either `censor1' or `censor2' might be + written as: + ed - file < +__FBSDID("$FreeBSD: src/bin/ed/buf.c,v 1.22 2002/06/30 05:13:53 obrien Exp $"); + +#include +#include + +#include "ed.h" + + +FILE *sfp; /* scratch file pointer */ +off_t sfseek; /* scratch file position */ +int seek_write; /* seek before writing */ +line_t buffer_head; /* incore buffer */ + +/* get_sbuf_line: get a line of text from the scratch file; return pointer + to the text */ +char * +get_sbuf_line(line_t *lp) +{ + static char *sfbuf = NULL; /* buffer */ + static int sfbufsz = 0; /* buffer size */ + + int len, ct; + + if (lp == &buffer_head) + return NULL; + seek_write = 1; /* force seek on write */ + /* out of position */ + if (sfseek != lp->seek) { + sfseek = lp->seek; + if (fseeko(sfp, sfseek, SEEK_SET) < 0) { + fprintf(stderr, "%s\n", strerror(errno)); + errmsg = "cannot seek temp file"; + return NULL; + } + } + len = lp->len; + REALLOC(sfbuf, sfbufsz, len + 1, NULL); + if ((ct = fread(sfbuf, sizeof(char), len, sfp)) < 0 || ct != len) { + fprintf(stderr, "%s\n", strerror(errno)); + errmsg = "cannot read temp file"; + return NULL; + } + sfseek += len; /* update file position */ + sfbuf[len] = '\0'; + return sfbuf; +} + + +/* put_sbuf_line: write a line of text to the scratch file and add a line node + to the editor buffer; return a pointer to the end of the text */ +const char * +put_sbuf_line(const char *cs) +{ + line_t *lp; + int len, ct; + const char *s; + + if ((lp = (line_t *) malloc(sizeof(line_t))) == NULL) { + fprintf(stderr, "%s\n", strerror(errno)); + errmsg = "out of memory"; + return NULL; + } + /* assert: cs is '\n' terminated */ + for (s = cs; *s != '\n'; s++) + ; + if (s - cs >= LINECHARS) { + errmsg = "line too long"; + return NULL; + } + len = s - cs; + /* out of position */ + if (seek_write) { + if (fseeko(sfp, (off_t)0, SEEK_END) < 0) { + fprintf(stderr, "%s\n", strerror(errno)); + errmsg = "cannot seek temp file"; + return NULL; + } + sfseek = ftello(sfp); + seek_write = 0; + } + /* assert: SPL1() */ + if ((ct = fwrite(cs, sizeof(char), len, sfp)) < 0 || ct != len) { + sfseek = -1; + fprintf(stderr, "%s\n", strerror(errno)); + errmsg = "cannot write temp file"; + return NULL; + } + lp->len = len; + lp->seek = sfseek; + add_line_node(lp); + sfseek += len; /* update file position */ + return ++s; +} + + +/* add_line_node: add a line node in the editor buffer after the current line */ +void +add_line_node(line_t *lp) +{ + line_t *cp; + + cp = get_addressed_line_node(current_addr); /* this get_addressed_line_node last! */ + INSQUE(lp, cp); + addr_last++; + current_addr++; +} + + +/* get_line_node_addr: return line number of pointer */ +long +get_line_node_addr(line_t *lp) +{ + line_t *cp = &buffer_head; + long n = 0; + + while (cp != lp && (cp = cp->q_forw) != &buffer_head) + n++; + if (n && cp == &buffer_head) { + errmsg = "invalid address"; + return ERR; + } + return n; +} + + +/* get_addressed_line_node: return pointer to a line node in the editor buffer */ +line_t * +get_addressed_line_node(long n) +{ + static line_t *lp = &buffer_head; + static long on = 0; + + SPL1(); + if (n > on) + if (n <= (on + addr_last) >> 1) + for (; on < n; on++) + lp = lp->q_forw; + else { + lp = buffer_head.q_back; + for (on = addr_last; on > n; on--) + lp = lp->q_back; + } + else + if (n >= on >> 1) + for (; on > n; on--) + lp = lp->q_back; + else { + lp = &buffer_head; + for (on = 0; on < n; on++) + lp = lp->q_forw; + } + SPL0(); + return lp; +} + + +extern int newline_added; + +char sfn[15] = ""; /* scratch file name */ + +/* open_sbuf: open scratch file */ +int +open_sbuf(void) +{ + int fd; + int u; + + isbinary = newline_added = 0; + u = umask(077); + strcpy(sfn, "/tmp/ed.XXXXXX"); + if ((fd = mkstemp(sfn)) == -1 || + (sfp = fdopen(fd, "w+")) == NULL) { + if (fd != -1) + close(fd); + perror(sfn); + errmsg = "cannot open temp file"; + umask(u); + return ERR; + } + umask(u); + return 0; +} + + +/* close_sbuf: close scratch file */ +int +close_sbuf(void) +{ + if (sfp) { + if (fclose(sfp) < 0) { + fprintf(stderr, "%s: %s\n", sfn, strerror(errno)); + errmsg = "cannot close temp file"; + return ERR; + } + sfp = NULL; + unlink(sfn); + } + sfseek = seek_write = 0; + return 0; +} + + +/* quit: remove_lines scratch file and exit */ +void +quit(int n) +{ + if (sfp) { + fclose(sfp); + unlink(sfn); + } + exit(n); +} + + +unsigned char ctab[256]; /* character translation table */ + +/* init_buffers: open scratch buffer; initialize line queue */ +void +init_buffers(void) +{ + int i = 0; + + /* Read stdin one character at a time to avoid i/o contention + with shell escapes invoked by nonterminal input, e.g., + ed - < 0; us++) + *us = ctab[*us]; + return s; +} diff --git a/text_cmds/ed/cbc.c b/text_cmds/ed/cbc.c new file mode 100644 index 0000000..22dd0d2 --- /dev/null +++ b/text_cmds/ed/cbc.c @@ -0,0 +1,402 @@ +/* cbc.c: This file contains the encryption routines for the ed line editor */ +/*- + * Copyright (c) 1993 The Regents of the University of California. + * All rights reserved. + * + * 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. + * 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. + */ + +#include +__FBSDID("$FreeBSD: src/bin/ed/cbc.c,v 1.20 2004/04/06 20:06:47 markm Exp $"); + +#include +#include +#include +#ifdef DES +#include +#include +#define ED_DES_INCLUDES +#endif + +#include "ed.h" + + +/* + * BSD and System V systems offer special library calls that do + * block move_liness and fills, so if possible we take advantage of them + */ +#define MEMCPY(dest,src,len) memcpy((dest),(src),(len)) +#define MEMZERO(dest,len) memset((dest), 0, (len)) + +/* Hide the calls to the primitive encryption routines. */ +#define DES_XFORM(buf) \ + DES_ecb_encrypt(buf, buf, &schedule, \ + inverse ? DES_DECRYPT : DES_ENCRYPT); + +/* + * read/write - no error checking + */ +#define READ(buf, n, fp) fread(buf, sizeof(char), n, fp) +#define WRITE(buf, n, fp) fwrite(buf, sizeof(char), n, fp) + +/* + * global variables and related macros + */ + +enum { /* encrypt, decrypt, authenticate */ + MODE_ENCRYPT, MODE_DECRYPT, MODE_AUTHENTICATE +} mode = MODE_ENCRYPT; + +#ifdef DES +DES_cblock ivec; /* initialization vector */ +DES_cblock pvec; /* padding vector */ +#endif + +char bits[] = { /* used to extract bits from a char */ + '\200', '\100', '\040', '\020', '\010', '\004', '\002', '\001' +}; + +int pflag; /* 1 to preserve parity bits */ + +#ifdef DES +DES_key_schedule schedule; /* expanded DES key */ +#endif + +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 */ + +/* init_des_cipher: initialize DES */ +void +init_des_cipher(void) +{ +#ifdef DES + int i; + + des_ct = des_n = 0; + + /* initialize the initialization vector */ + MEMZERO(ivec, 8); + + /* initialize the padding vector */ + for (i = 0; i < 8; i++) + pvec[i] = (char) (arc4random() % 256); +#endif +} + + +/* get_des_char: return next char in an encrypted file */ +int +get_des_char(FILE *fp) +{ +#ifdef DES + if (des_n >= des_ct) { + des_n = 0; + des_ct = cbc_decode(des_buf, fp); + } + return (des_ct > 0) ? des_buf[des_n++] : EOF; +#else + return (getc(fp)); +#endif +} + + +/* put_des_char: write a char to an encrypted file; return char written */ +int +put_des_char(int c, FILE *fp) +{ +#ifdef DES + if (des_n == sizeof des_buf) { + des_ct = cbc_encode(des_buf, des_n, fp); + des_n = 0; + } + return (des_ct >= 0) ? (des_buf[des_n++] = c) : EOF; +#else + return (fputc(c, fp)); +#endif +} + + +/* flush_des_file: flush an encrypted file's output; return status */ +int +flush_des_file(FILE *fp) +{ +#ifdef DES + if (des_n == sizeof des_buf) { + des_ct = cbc_encode(des_buf, des_n, fp); + des_n = 0; + } + return (des_ct >= 0 && cbc_encode(des_buf, des_n, fp) >= 0) ? 0 : EOF; +#else + return (fflush(fp)); +#endif +} + +#ifdef DES +/* + * get keyword from tty or stdin + */ +int +get_keyword(void) +{ + char *p; /* used to obtain the key */ + DES_cblock msgbuf; /* I/O buffer */ + + /* + * get the key + */ + if (*(p = getpass("Enter key: "))) { + + /* + * copy it, nul-padded, into the key area + */ + expand_des_key(msgbuf, p); + MEMZERO(p, _PASSWORD_LEN); + set_des_key(&msgbuf); + MEMZERO(msgbuf, sizeof msgbuf); + return 1; + } + return 0; +} + + +/* + * print a warning message and, possibly, terminate + */ +void +des_error(const char *s) +{ + errmsg = s ? s : strerror(errno); +} + +/* + * map a hex character to an integer + */ +int +hex_to_binary(int c, int radix) +{ + switch(c) { + case '0': return(0x0); + case '1': return(0x1); + case '2': return(radix > 2 ? 0x2 : -1); + case '3': return(radix > 3 ? 0x3 : -1); + case '4': return(radix > 4 ? 0x4 : -1); + case '5': return(radix > 5 ? 0x5 : -1); + case '6': return(radix > 6 ? 0x6 : -1); + case '7': return(radix > 7 ? 0x7 : -1); + case '8': return(radix > 8 ? 0x8 : -1); + case '9': return(radix > 9 ? 0x9 : -1); + case 'A': case 'a': return(radix > 10 ? 0xa : -1); + case 'B': case 'b': return(radix > 11 ? 0xb : -1); + case 'C': case 'c': return(radix > 12 ? 0xc : -1); + case 'D': case 'd': return(radix > 13 ? 0xd : -1); + case 'E': case 'e': return(radix > 14 ? 0xe : -1); + case 'F': case 'f': return(radix > 15 ? 0xf : -1); + } + /* + * invalid character + */ + return(-1); +} + +/* + * convert the key to a bit pattern + * obuf bit pattern + * kbuf the key itself + */ +void +expand_des_key(char *obuf, char *kbuf) +{ + int i, j; /* counter in a for loop */ + int nbuf[64]; /* used for hex/key translation */ + + /* + * leading '0x' or '0X' == hex key + */ + if (kbuf[0] == '0' && (kbuf[1] == 'x' || kbuf[1] == 'X')) { + kbuf = &kbuf[2]; + /* + * now translate it, bombing on any illegal hex digit + */ + for (i = 0; kbuf[i] && i < 16; i++) + if ((nbuf[i] = hex_to_binary((int) kbuf[i], 16)) == -1) + des_error("bad hex digit in key"); + while (i < 16) + nbuf[i++] = 0; + for (i = 0; i < 8; i++) + obuf[i] = + ((nbuf[2*i]&0xf)<<4) | (nbuf[2*i+1]&0xf); + /* preserve parity bits */ + pflag = 1; + return; + } + /* + * leading '0b' or '0B' == binary key + */ + if (kbuf[0] == '0' && (kbuf[1] == 'b' || kbuf[1] == 'B')) { + kbuf = &kbuf[2]; + /* + * now translate it, bombing on any illegal binary digit + */ + for (i = 0; kbuf[i] && i < 16; i++) + if ((nbuf[i] = hex_to_binary((int) kbuf[i], 2)) == -1) + des_error("bad binary digit in key"); + while (i < 64) + nbuf[i++] = 0; + for (i = 0; i < 8; i++) + for (j = 0; j < 8; j++) + obuf[i] = (obuf[i]<<1)|nbuf[8*i+j]; + /* preserve parity bits */ + pflag = 1; + return; + } + /* + * no special leader -- ASCII + */ + (void)strncpy(obuf, kbuf, 8); +} + +/***************** + * DES FUNCTIONS * + *****************/ +/* + * This sets the DES key and (if you're using the deszip version) + * the direction of the transformation. This uses the Sun + * to map the 64-bit key onto the 56 bits that the key schedule + * generation routines use: the old way, which just uses the user- + * supplied 64 bits as is, and the new way, which resets the parity + * bit to be the same as the low-order bit in each character. The + * new way generates a greater variety of key schedules, since many + * systems set the parity (high) bit of each character to 0, and the + * DES ignores the low order bit of each character. + */ +void +set_des_key(DES_cblock *buf) /* key block */ +{ + int i, j; /* counter in a for loop */ + int par; /* parity counter */ + + /* + * if the parity is not preserved, flip it + */ + if (!pflag) { + for (i = 0; i < 8; i++) { + par = 0; + for (j = 1; j < 8; j++) + if ((bits[j] & (*buf)[i]) != 0) + par++; + if ((par & 0x01) == 0x01) + (*buf)[i] &= 0x7f; + else + (*buf)[i] = ((*buf)[i] & 0x7f) | 0x80; + } + } + + DES_set_odd_parity(buf); + DES_set_key(buf, &schedule); +} + + +/* + * This encrypts using the Cipher Block Chaining mode of DES + */ +int +cbc_encode(unsigned char *msgbuf, int n, FILE *fp) +{ + int inverse = 0; /* 0 to encrypt, 1 to decrypt */ + + /* + * do the transformation + */ + if (n == 8) { + for (n = 0; n < 8; n++) + msgbuf[n] ^= ivec[n]; + DES_XFORM((DES_cblock *)msgbuf); + MEMCPY(ivec, msgbuf, 8); + return WRITE(msgbuf, 8, fp); + } + /* + * at EOF or last block -- in either case, the last byte contains + * the character representation of the number of bytes in it + */ +/* + MEMZERO(msgbuf + n, 8 - n); +*/ + /* + * Pad the last block randomly + */ + (void)MEMCPY(msgbuf + n, pvec, 8 - n); + msgbuf[7] = n; + for (n = 0; n < 8; n++) + msgbuf[n] ^= ivec[n]; + DES_XFORM((DES_cblock *)msgbuf); + return WRITE(msgbuf, 8, fp); +} + +/* + * This decrypts using the Cipher Block Chaining mode of DES + * msgbuf I/O buffer + * fp input file descriptor + */ +int +cbc_decode(unsigned char *msgbuf, FILE *fp) +{ + DES_cblock tbuf; /* temp buffer for initialization vector */ + int n; /* number of bytes actually read */ + int c; /* used to test for EOF */ + int inverse = 1; /* 0 to encrypt, 1 to decrypt */ + + if ((n = READ(msgbuf, 8, fp)) == 8) { + /* + * do the transformation + */ + MEMCPY(tbuf, msgbuf, 8); + DES_XFORM((DES_cblock *)msgbuf); + for (c = 0; c < 8; c++) + msgbuf[c] ^= ivec[c]; + MEMCPY(ivec, tbuf, 8); + /* + * if the last one, handle it specially + */ + if ((c = fgetc(fp)) == EOF) { + n = msgbuf[7]; + if (n < 0 || n > 7) { + des_error("decryption failed (block corrupted)"); + return EOF; + } + } else + (void)ungetc(c, fp); + return n; + } + if (n > 0) + des_error("decryption failed (incomplete block)"); + else if (n < 0) + des_error("cannot read file"); + return EOF; +} +#endif /* DES */ diff --git a/text_cmds/ed/ed.1 b/text_cmds/ed/ed.1 new file mode 100644 index 0000000..8c4b6b2 --- /dev/null +++ b/text_cmds/ed/ed.1 @@ -0,0 +1,1004 @@ +.\" $FreeBSD: src/bin/ed/ed.1,v 1.35 2005/01/16 16:41:56 ru Exp $ +.Dd July 3, 2004 +.Dt ED 1 +.Os +.Sh NAME +.Nm ed , +.Nm red +.Nd text editor +.Sh SYNOPSIS +.Nm +.Op Fl +.Op Fl sx +.Op Fl p Ar string +.Op Ar file +.Nm red +.Op Fl +.Op Fl sx +.Op Fl p Ar string +.Op Ar file +.Sh DESCRIPTION +The +.Nm +utility is a line-oriented text editor. +It is used to create, display, modify and otherwise manipulate text +files. +When invoked as +.Nm red , +the editor runs in +.Qq restricted +mode, in which the only difference is that the editor restricts the +use of filenames which start with +.Ql \&! +(interpreted as shell commands by +.Nm ) +or contain a +.Ql \&/ . +Note that editing outside of the current directory is only prohibited +if the user does not have write access to the current directory. +If a user has write access to the current directory, then symbolic +links can be created in the current directory, in which case +.Nm red +will not stop the user from editing the file that the symbolic link +points to. +.Pp +If invoked with a +.Ar file +argument, then a copy of +.Ar file +is read into the editor's buffer. +Changes are made to this copy and not directly to +.Ar file +itself. +Upon quitting +.Nm , +any changes not explicitly saved with a +.Em w +command are lost. +.Pp +Editing is done in two distinct modes: +.Em command +and +.Em input . +When first invoked, +.Nm +is in command mode. +In this mode commands are read from the standard input and +executed to manipulate the contents of the editor buffer. +A typical command might look like: +.Pp +.Sm off +.Cm ,s No / Em old Xo +.No / Em new +.No / Cm g +.Xc +.Sm on +.Pp +which replaces all occurrences of the string +.Em old +with +.Em new . +.Pp +When an input command, such as +.Em a +(append), +.Em i +(insert) or +.Em c +(change), is given, +.Nm +enters input mode. +This is the primary means +of adding text to a file. +In this mode, no commands are available; +instead, the standard input is written +directly to the editor buffer. +Lines consist of text up to and +including a +.Em newline +character. +Input mode is terminated by +entering a single period +.Pq Em .\& +on a line. +.Pp +All +.Nm +commands operate on whole lines or ranges of lines; e.g., +the +.Em d +command deletes lines; the +.Em m +command moves lines, and so on. +It is possible to modify only a portion of a line by means of replacement, +as in the example above. +However even here, the +.Em s +command is applied to whole lines at a time. +.Pp +In general, +.Nm +commands consist of zero or more line addresses, followed by a single +character command and possibly additional parameters; i.e., +commands have the structure: +.Pp +.Sm off +.Xo +.Op Ar address Op , Ar address +.Ar command Op Ar parameters +.Xc +.Sm on +.Pp +The address(es) indicate the line or range of lines to be affected by the +command. +If fewer addresses are given than the command accepts, then +default addresses are supplied. +.Sh OPTIONS +The following options are available: +.Bl -tag -width indent +.It Fl s +Suppress diagnostics. +This should be used if +.Nm Ns 's +standard input is from a script. +.It Fl x +Prompt for an encryption key to be used in subsequent reads and writes +(see the +.Em x +command). +Unsupported on Mac OS X. +.It Fl p Ar string +Specify a command prompt. +This may be toggled on and off with the +.Em P +command. +.It Ar file +Specify the name of a file to read. +If +.Ar file +is prefixed with a +bang (!), then it is interpreted as a shell command. +In this case, +what is read is +the standard output of +.Ar file +executed via +.Xr sh 1 . +To read a file whose name begins with a bang, prefix the +name with a backslash (\\). +The default filename is set to +.Ar file +only if it is not prefixed with a bang. +.El +.Sh LINE ADDRESSING +An address represents the number of a line in the buffer. +The +.Nm +utility maintains a +.Em current address +which is +typically supplied to commands as the default address when none is specified. +When a file is first read, the current address is set to the last line +of the file. +In general, the current address is set to the last line +affected by a command. +.Pp +A line address is +constructed from one of the bases in the list below, optionally followed +by a numeric offset. +The offset may include any combination +of digits, operators (i.e., +.Em + , +.Em - +and +.Em ^ ) +and whitespace. +Addresses are read from left to right, and their values are computed +relative to the current address. +.Pp +One exception to the rule that addresses represent line numbers is the +address +.Em 0 +(zero). +This means "before the first line," +and is legal wherever it makes sense. +.Pp +An address range is two addresses separated either by a comma or +semi-colon. +The value of the first address in a range cannot exceed the +value of the second. +If only one address is given in a range, then +the second address is set to the given address. +If an +.Em n Ns -tuple +of addresses is given where +.Em "n\ >\ 2" , +then the corresponding range is determined by the last two addresses in +the +.Em n Ns -tuple . +If only one address is expected, then the last address is used. +.Pp +Each address in a comma-delimited range is interpreted relative to the +current address. +In a semi-colon-delimited range, the first address is +used to set the current address, and the second address is interpreted +relative to the first. +.Pp +The following address symbols are recognized: +.Bl -tag -width indent +.It . +The current line (address) in the buffer. +.It $ +The last line in the buffer. +.It n +The +.Em n Ns th, +line in the buffer +where +.Em n +is a number in the range +.Em [0,$] . +.It - or ^ +The previous line. +This is equivalent to +.Em -1 +and may be repeated with cumulative effect. +.It -n or ^n +The +.Em n Ns th +previous line, where +.Em n +is a non-negative number. +.It + +The next line. +This is equivalent to +.Em +1 +and may be repeated with cumulative effect. +.It +n +The +.Em n Ns th +next line, where +.Em n +is a non-negative number. +.It , or % +The first through last lines in the buffer. +This is equivalent to +the address range +.Em 1,$ . +.It ; +The current through last lines in the buffer. +This is equivalent to +the address range +.Em .,$ . +.It /re/ +The next line containing the regular expression +.Em re . +The search wraps to the beginning of the buffer and continues down to the +current line, if necessary. +// repeats the last search. +.It ?re? +The +previous line containing the regular expression +.Em re . +The search wraps to the end of the buffer and continues up to the +current line, if necessary. +?? repeats the last search. +.It 'lc +The +line previously marked by a +.Em k +(mark) command, where +.Em lc +is a lower case letter. +.El +.Sh REGULAR EXPRESSIONS +Regular expressions are patterns used in selecting text. +For example, the command: +.Pp +.Sm off +.Cm g No / Em string Xo +.No / +.Xc +.Sm on +.Pp +prints all lines containing +.Em string . +Regular expressions are also +used by the +.Em s +command for selecting old text to be replaced with new. +.Pp +In addition to a specifying string literals, regular expressions can +represent +classes of strings. +Strings thus represented are said to be matched +by the corresponding regular expression. +If it is possible for a regular expression +to match several strings in a line, then the left-most longest match is +the one selected. +.Pp +The following symbols are used in constructing regular expressions: +.Bl -tag -width indent +.It c +Any character +.Em c +not listed below, including +.Ql \&{ , +.Ql \&} , +.Ql \&( , +.Ql \&) , +.Ql < +and +.Ql > , +matches itself. +.It Pf \e c +Any backslash-escaped character +.Em c , +except for +.Ql \&{ , +.Ql \&} , +.Ql \&( , +.Ql \&) , +.Ql < +and +.Ql > , +matches itself. +.It . +Match any single character. +.It Op char-class +Match any single character in +.Em char-class . +To include a +.Ql \&] +in +.Em char-class , +it must be the first character. +A range of characters may be specified by separating the end characters +of the range with a +.Ql - , +e.g., +.Ql a-z +specifies the lower case characters. +The following literal expressions can also be used in +.Em char-class +to specify sets of characters: +.Pp +.Bl -column "[:alnum:]" "[:cntrl:]" "[:lower:]" "[:xdigit:]" -compact +.It [:alnum:] Ta [:cntrl:] Ta [:lower:] Ta [:space:] +.It [:alpha:] Ta [:digit:] Ta [:print:] Ta [:upper:] +.It [:blank:] Ta [:graph:] Ta [:punct:] Ta [:xdigit:] +.El +.Pp +If +.Ql - +appears as the first or last +character of +.Em char-class , +then it matches itself. +All other characters in +.Em char-class +match themselves. +.Pp +Patterns in +.Em char-class +of the form: +.Pp +.Bl -item -compact -offset 2n +.It +.Op \&. Ns Ar col-elm Ns .\& +or, +.It +.Op = Ns Ar col-elm Ns = +.El +.Pp +where +.Ar col-elm +is a +.Em collating element +are interpreted according to the current locale settings +(not currently supported). +See +.Xr regex 3 +and +.Xr re_format 7 +for an explanation of these constructs. +.It Op ^char-class +Match any single character, other than newline, not in +.Em char-class . +.Em Char-class +is defined +as above. +.It ^ +If +.Em ^ +is the first character of a regular expression, then it +anchors the regular expression to the beginning of a line. +Otherwise, it matches itself. +.It $ +If +.Em $ +is the last character of a regular expression, it +anchors the regular expression to the end of a line. +Otherwise, it matches itself. +.It Pf \e < +Anchor the single character regular expression or subexpression +immediately following it to the beginning of a word. +(This may not be available) +.It Pf \e > +Anchor the single character regular expression or subexpression +immediately following it to the end of a word. +(This may not be available) +.It Pf \e (re\e) +Define a subexpression +.Em re . +Subexpressions may be nested. +A subsequent backreference of the form +.Pf \e Em n , +where +.Em n +is a number in the range [1,9], expands to the text matched by the +.Em n Ns th +subexpression. +For example, the regular expression +.Ql \e(.*\e)\e1 +matches any string +consisting of identical adjacent substrings. +Subexpressions are ordered relative to +their left delimiter. +.It * +Match the single character regular expression or subexpression +immediately preceding it zero or more times. +If +.Em * +is the first +character of a regular expression or subexpression, then it matches +itself. +The +.Em * +operator sometimes yields unexpected results. +For example, the regular expression +.Ql b* +matches the beginning of +the string +.Ql abbb +(as opposed to the substring +.Ql bbb ) , +since a null match +is the only left-most match. +.It \e{n,m\e} or \e{n,\e} or \e{n\e} +Match the single character regular expression or subexpression +immediately preceding it at least +.Em n +and at most +.Em m +times. +If +.Em m +is omitted, then it matches at least +.Em n +times. +If the comma is also omitted, then it matches exactly +.Em n +times. +.El +.Pp +Additional regular expression operators may be defined depending on the +particular +.Xr regex 3 +implementation. +.Sh COMMANDS +All +.Nm +commands are single characters, though some require additional parameters. +If a command's parameters extend over several lines, then +each line except for the last +must be terminated with a backslash (\\). +.Pp +In general, at most one command is allowed per line. +However, most commands accept a print suffix, which is any of +.Em p +(print), +.Em l +(list), +or +.Em n +(enumerate), +to print the last line affected by the command. +.Pp +An interrupt (typically ^C) has the effect of aborting the current command +and returning the editor to command mode. +.Pp +The +.Nm +utility +recognizes the following commands. +The commands are shown together with +the default address or address range supplied if none is +specified (in parenthesis). +.Bl -tag -width indent +.It (.)a +Append text to the buffer after the addressed line. +Text is entered in input mode. +The current address is set to last line entered. +.It (.,.)c +Change lines in the buffer. +The addressed lines are deleted +from the buffer, and text is appended in their place. +Text is entered in input mode. +The current address is set to last line entered. +.It (.,.)d +Delete the addressed lines from the buffer. +If there is a line after the deleted range, then the current address is set +to this line. +Otherwise the current address is set to the line +before the deleted range. +.It e Ar file +Edit +.Ar file , +and sets the default filename. +If +.Ar file +is not specified, then the default filename is used. +Any lines in the buffer are deleted before +the new file is read. +The current address is set to the last line read. +.It e Ar !command +Edit the standard output of +.Ar !command , +(see +.Ar !command +below). +The default filename is unchanged. +Any lines in the buffer are deleted before the output of +.Ar command +is read. +The current address is set to the last line read. +.It E Ar file +Edit +.Ar file +unconditionally. +This is similar to the +.Em e +command, +except that unwritten changes are discarded without warning. +The current address is set to the last line read. +.It f Ar file +Set the default filename to +.Ar file . +If +.Ar file +is not specified, then the default unescaped filename is printed. +.It (1,$)g/re/command-list +Apply +.Ar command-list +to each of the addressed lines matching a regular expression +.Ar re . +The current address is set to the +line currently matched before +.Ar command-list +is executed. +At the end of the +.Em g +command, the current address is set to the last line affected by +.Ar command-list . +.Pp +Each command in +.Ar command-list +must be on a separate line, +and every line except for the last must be terminated by a backslash +(\\). +Any commands are allowed, except for +.Em g , +.Em G , +.Em v , +and +.Em V . +A newline alone in +.Ar command-list +is equivalent to a +.Em p +command. +.It (1,$)G/re/ +Interactively edit the addressed lines matching a regular expression +.Ar re . +For each matching line, +the line is printed, +the current address is set, +and the user is prompted to enter a +.Ar command-list . +At the end of the +.Em G +command, the current address +is set to the last line affected by (the last) +.Ar command-list . +.Pp +The format of +.Ar command-list +is the same as that of the +.Em g +command. +A newline alone acts as a null command list. +A single +.Ql & +repeats the last non-null command list. +.It H +Toggle the printing of error explanations. +By default, explanations are not printed. +It is recommended that ed scripts begin with this command to +aid in debugging. +.It h +Print an explanation of the last error. +.It (.)i +Insert text in the buffer before the current line. +Text is entered in input mode. +The current address is set to the last line entered. +.It (.,.+1)j +Join the addressed lines. +The addressed lines are +deleted from the buffer and replaced by a single +line containing their joined text. +The current address is set to the resultant line. +.It (.)klc +Mark a line with a lower case letter +.Em lc . +The line can then be addressed as +.Em 'lc +(i.e., a single quote followed by +.Em lc ) +in subsequent commands. +The mark is not cleared until the line is +deleted or otherwise modified. +.It (.,.)l +Print 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 +.Dq Li --More-- +prompt is printed on the last line. +The +.Nm +utility waits until the RETURN key is pressed +before displaying the next screen. +The current address is set to the last line +printed. +.It (.,.)m(.) +Move lines in the buffer. +The addressed lines are moved to after the +right-hand destination address, which may be the address +.Em 0 +(zero). +The current address is set to the +last line moved. +.It (.,.)n +Print the addressed lines along with +their line numbers. +The current address is set to the last line +printed. +.It (.,.)p +Print the addressed lines. +The current address is set to the last line +printed. +.It P +Toggle the command prompt on and off. +Unless a prompt was specified by with command-line option +.Fl p Ar string , +the command prompt is by default turned off. +.It q +Quit +.Nm . +.It Q +Quit +.Nm +unconditionally. +This is similar to the +.Em q +command, +except that unwritten changes are discarded without warning. +.It ($)r Ar file +Read +.Ar file +to after the addressed line. +If +.Ar file +is not specified, then the default +filename is used. +If there was no default filename prior to the command, +then the default filename is set to +.Ar file . +Otherwise, the default filename is unchanged. +The current address is set to the last line read. +.It ($)r Ar !command +Read +to after the addressed line +the standard output of +.Ar !command , +(see the +.Ar !command +below). +The default filename is unchanged. +The current address is set to the last line read. +.It (.,.)s/re/replacement/ +.It (.,.)s/re/replacement/g +.It (.,.)s/re/replacement/n +Replace text in the addressed lines +matching a regular expression +.Ar re +with +.Ar replacement . +By default, only the first match in each line is replaced. +If the +.Em g +(global) suffix is given, then every match to be replaced. +The +.Em n +suffix, where +.Em n +is a positive number, causes only the +.Em n Ns th +match to be replaced. +It is an error if no substitutions are performed on any of the addressed +lines. +The current address is set the last line affected. +.Pp +.Ar Re +and +.Ar replacement +may be delimited by any character other than space and newline +(see the +.Em s +command below). +If one or two of the last delimiters is omitted, then the last line +affected is printed as though the print suffix +.Em p +were specified. +.Pp +An unescaped +.Ql & +in +.Ar replacement +is replaced by the currently matched text. +The character sequence +.Em \em , +where +.Em m +is a number in the range [1,9], is replaced by the +.Em m th +backreference expression of the matched text. +If +.Ar replacement +consists of a single +.Ql % , +then +.Ar replacement +from the last substitution is used. +Newlines may be embedded in +.Ar replacement +if they are escaped with a backslash (\\). +.It (.,.)s +Repeat the last substitution. +This form of the +.Em s +command accepts a count suffix +.Em n , +or any combination of the characters +.Em r , +.Em g , +and +.Em p . +If a count suffix +.Em n +is given, then only the +.Em n Ns th +match is replaced. +The +.Em r +suffix causes +the regular expression of the last search to be used instead of the +that of the last substitution. +The +.Em g +suffix toggles the global suffix of the last substitution. +The +.Em p +suffix toggles the print suffix of the last substitution +The current address is set to the last line affected. +.It (.,.)t(.) +Copy (i.e., transfer) the addressed lines to after the right-hand +destination address, which may be the address +.Em 0 +(zero). +The current address is set to the last line +copied. +.It u +Undo the last command and restores the current address +to what it was before the command. +The global commands +.Em g , +.Em G , +.Em v , +and +.Em V . +are treated as a single command by undo. +.Em u +is its own inverse. +.It (1,$)v/re/command-list +Apply +.Ar command-list +to each of the addressed lines not matching a regular expression +.Ar re . +This is similar to the +.Em g +command. +.It (1,$)V/re/ +Interactively edit the addressed lines not matching a regular expression +.Ar re . +This is similar to the +.Em G +command. +.It (1,$)w Ar file +Write the addressed lines to +.Ar file . +Any previous contents of +.Ar file +is lost without warning. +If there is no default filename, then the default filename is set to +.Ar file , +otherwise it is unchanged. +If no filename is specified, then the default +filename is used. +The current address is unchanged. +.It (1,$)wq Ar file +Write the addressed lines to +.Ar file , +and then executes a +.Em q +command. +.It (1,$)w Ar !command +Write the addressed lines to the standard input of +.Ar !command , +(see the +.Em !command +below). +The default filename and current address are unchanged. +.It (1,$)W Ar file +Append the addressed lines to the end of +.Ar file . +This is similar to the +.Em w +command, expect that the previous contents of file is not clobbered. +The current address is unchanged. +.It x +Prompt for an encryption key which is used in subsequent reads and +writes. +If a newline alone is entered as the key, then encryption is +turned off. +Otherwise, echoing is disabled while a key is read. +Unsupported on Mac OS X. +.It Pf (.+1)z n +Scroll +.Ar n +lines at a time starting at addressed line. +If +.Ar n +is not specified, then the current window size is used. +The current address is set to the last line printed. +.It !command +Execute +.Ar command +via +.Xr sh 1 . +If the first character of +.Ar command +is +.Ql \&! , +then it is replaced by text of the +previous +.Ar !command . +The +.Nm +utility does not process +.Ar command +for backslash (\\) escapes. +However, an unescaped +.Em % +is replaced by the default filename. +When the shell returns from execution, a +.Ql \&! +is printed to the standard output. +The current line is unchanged. +.It ($)= +Print the line number of the addressed line. +.It (.+1)newline +Print the addressed line, and sets the current address to +that line. +.El +.Sh FILES +.Bl -tag -width /tmp/ed.* -compact +.It /tmp/ed.* +buffer file +.It ed.hup +the file to which +.Nm +attempts to write the buffer if the terminal hangs up +.El +.Sh DIAGNOSTICS +When an error occurs, +.Nm +prints a +.Ql \&? +and either returns to command mode +or exits if its input is from a script. +An explanation of the last error can be +printed with the +.Em h +(help) command. +.Pp +Since the +.Em g +(global) command masks any errors from failed searches and substitutions, +it can be used to perform conditional operations in scripts; e.g., +.Pp +.Sm off +.Cm g No / Em old Xo +.No / Cm s +.No // Em new +.No / +.Xc +.Sm on +.Pp +replaces any occurrences of +.Em old +with +.Em new . +If the +.Em u +(undo) command occurs in a global command list, then +the command list is executed only once. +.Pp +If diagnostics are not disabled, attempting to quit +.Nm +or edit another file before writing a modified buffer +results in an error. +If the command is entered a second time, it succeeds, +but any changes to the buffer are lost. +.Sh SEE ALSO +.Xr sed 1 , +.Xr sh 1 , +.Xr vi 1 , +.Xr regex 3 , +.Xr compat 5 +.Pp +USD:12-13 +.Rs +.%A B. W. Kernighan +.%A P. J. Plauger +.%B Software Tools in Pascal +.%O Addison-Wesley +.%D 1981 +.Re +.Sh LIMITATIONS +The +.Nm +utility processes +.Ar file +arguments for backslash escapes, i.e., in a filename, +any characters preceded by a backslash (\\) are +interpreted literally. +.Pp +If a text (non-binary) file is not terminated by a newline character, +then +.Nm +appends one on reading/writing it. +In the case of a binary file, +.Nm +does not append a newline on reading/writing. +.Pp +per line overhead: 4 ints +.Sh HISTORY +An +.Nm +command appeared in +Version 1 AT&T UNIX. +.Sh BUGS +The +.Nm +utility does not recognize multibyte characters. diff --git a/text_cmds/ed/ed.h b/text_cmds/ed/ed.h new file mode 100644 index 0000000..f35a001 --- /dev/null +++ b/text_cmds/ed/ed.h @@ -0,0 +1,282 @@ +/* ed.h: type and constant definitions for the ed editor. */ +/*- + * Copyright (c) 1993 Andrew Moore + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ed.h,v 1.5 1994/02/01 00:34:39 alm Exp + * $FreeBSD: src/bin/ed/ed.h,v 1.20 2005/01/10 08:39:22 imp Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ERR (-2) +#define EMOD (-3) +#define FATAL (-4) + +#define MINBUFSZ 512 /* minimum buffer size - must be > 0 */ +#define SE_MAX 30 /* max subexpressions in a regular expression */ +#ifdef INT_MAX +# define LINECHARS INT_MAX /* max chars per line */ +#else +# define LINECHARS MAXINT /* max chars per line */ +#endif + +/* gflags */ +#define GLB 001 /* global command */ +#define GPR 002 /* print after command */ +#define GLS 004 /* list after command */ +#define GNP 010 /* enumerate after command */ +#define GSG 020 /* global substitute */ +#define GINT 040 /* interactive cmd: G or V */ + +typedef regex_t pattern_t; + +/* Line node */ +typedef struct line { + struct line *q_forw; + struct line *q_back; + off_t seek; /* address of line in scratch buffer */ + int len; /* length of line */ +} line_t; + + +typedef struct undo { + +/* type of undo nodes */ +#define UADD 0 +#define UDEL 1 +#define UMOV 2 +#define VMOV 3 + + int type; /* command type */ + line_t *h; /* head of list */ + line_t *t; /* tail of list */ +} undo_t; + +#ifndef max +# define max(a,b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef min +# define min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#define INC_MOD(l, k) ((l) + 1 > (k) ? 0 : (l) + 1) +#define DEC_MOD(l, k) ((l) - 1 < 0 ? (k) : (l) - 1) + +/* SPL1: disable some interrupts (requires reliable signals) */ +#define SPL1() mutex++ + +/* SPL0: enable all interrupts; check sigflags (requires reliable signals) */ +#define SPL0() \ +if (--mutex == 0) { \ + if (sigflags & (1 << (SIGHUP - 1))) handle_hup(SIGHUP); \ + if (sigflags & (1 << (SIGINT - 1))) handle_int(SIGINT); \ +} + +/* STRTOL: convert a string to long */ +#define STRTOL(i, p) { \ + if (((i = strtol(p, &p, 10)) == LONG_MIN || i == LONG_MAX) && \ + errno == ERANGE) { \ + errmsg = "number out of range"; \ + i = 0; \ + return ERR; \ + } \ +} + +#if defined(sun) || defined(NO_REALLOC_NULL) +/* REALLOC: assure at least a minimum size for buffer b */ +#define REALLOC(b,n,i,err) \ +if ((i) > (n)) { \ + int ti = (n); \ + char *ts; \ + SPL1(); \ + if ((b) != NULL) { \ + if ((ts = (char *) realloc((b), ti += max((i), MINBUFSZ))) == NULL) { \ + fprintf(stderr, "%s\n", strerror(errno)); \ + errmsg = "out of memory"; \ + SPL0(); \ + return err; \ + } \ + } else { \ + if ((ts = (char *) malloc(ti += max((i), MINBUFSZ))) == NULL) { \ + fprintf(stderr, "%s\n", strerror(errno)); \ + errmsg = "out of memory"; \ + SPL0(); \ + return err; \ + } \ + } \ + (n) = ti; \ + (b) = ts; \ + SPL0(); \ +} +#else /* NO_REALLOC_NULL */ +/* REALLOC: assure at least a minimum size for buffer b */ +#define REALLOC(b,n,i,err) \ +if ((i) > (n)) { \ + int ti = (n); \ + char *ts; \ + SPL1(); \ + if ((ts = (char *) realloc((b), ti += max((i), MINBUFSZ))) == NULL) { \ + fprintf(stderr, "%s\n", strerror(errno)); \ + errmsg = "out of memory"; \ + SPL0(); \ + return err; \ + } \ + (n) = ti; \ + (b) = ts; \ + SPL0(); \ +} +#endif /* NO_REALLOC_NULL */ + +/* REQUE: link pred before succ */ +#define REQUE(pred, succ) (pred)->q_forw = (succ), (succ)->q_back = (pred) + +/* INSQUE: insert elem in circular queue after pred */ +#define INSQUE(elem, pred) \ +{ \ + REQUE((elem), (pred)->q_forw); \ + REQUE((pred), elem); \ +} + +/* REMQUE: remove_lines elem from circular queue */ +#define REMQUE(elem) REQUE((elem)->q_back, (elem)->q_forw); + +/* NUL_TO_NEWLINE: overwrite ASCII NULs with newlines */ +#define NUL_TO_NEWLINE(s, l) translit_text(s, l, '\0', '\n') + +/* NEWLINE_TO_NUL: overwrite newlines with ASCII NULs */ +#define NEWLINE_TO_NUL(s, l) translit_text(s, l, '\n', '\0') + +#ifdef ED_DES_INCLUDES +void des_error(const char *); +void expand_des_key(char *, char *); +void set_des_key(DES_cblock *); +#endif + +/* Other DES support stuff */ +void init_des_cipher(void); +int flush_des_file(FILE *); +int get_des_char(FILE *); +int put_des_char(int, FILE *); + +/* Local Function Declarations */ +void add_line_node(line_t *); +int append_lines(long); +int apply_subst_template(const char *, regmatch_t *, int, int); +int build_active_list(int); +int cbc_decode(unsigned char *, FILE *); +int cbc_encode(unsigned char *, int, FILE *); +int check_addr_range(long, long); +void clear_active_list(void); +void clear_undo_stack(void); +int close_sbuf(void); +int copy_lines(long); +int delete_lines(long, long); +int display_lines(long, long, int); +line_t *dup_line_node(line_t *); +int exec_command(void); +long exec_global(int, int); +int extract_addr_range(void); +char *extract_pattern(int); +int extract_subst_tail(int *, long *); +char *extract_subst_template(void); +int filter_lines(long, long, char *); +line_t *get_addressed_line_node(long); +pattern_t *get_compiled_pattern(void); +char *get_extended_line(int *, int); +char *get_filename(void); +int get_keyword(void); +long get_line_node_addr(line_t *); +long get_matching_node_addr(pattern_t *, int); +long get_marked_node_addr(int); +char *get_sbuf_line(line_t *); +int get_shell_command(void); +int get_stream_line(FILE *); +int get_tty_line(void); +void handle_hup(int); +void handle_int(int); +void handle_winch(int); +int has_trailing_escape(char *, char *); +int hex_to_binary(int, int); +void init_buffers(void); +int is_legal_filename(char *); +int join_lines(long, long); +int mark_line_node(line_t *, int); +int move_lines(long); +line_t *next_active_node(void); +long next_addr(void); +int open_sbuf(void); +char *parse_char_class(char *); +int pop_undo_stack(void); +undo_t *push_undo_stack(int, long, long); +const char *put_sbuf_line(const char *); +int put_stream_line(FILE *, const char *, int); +int put_tty_line(const char *, int, long, int); +void quit(int); +long read_file(char *, long); +long read_stream(FILE *, long); +void replace_marks(line_t *, line_t *); +int search_and_replace(pattern_t *, int, int); +int set_active_node(line_t *); +void signal_hup(int); +void signal_int(int); +char *strip_escapes(char *); +int substitute_matching_text(pattern_t *, line_t *, int, int); +char *translit_text(char *, int, int, int); +void unmark_line_node(line_t *); +void unset_active_nodes(line_t *, line_t *); +long write_file(char *, const char *, long, long); +long write_stream(FILE *, long, long); + +/* global buffers */ +extern char stdinbuf[]; +extern char *ibuf; +extern char *ibufp; +extern int ibufsz; + +/* global flags */ +extern int isbinary; +extern int isglobal; +extern int modified; +extern int mutex; +extern int sigflags; + +/* global vars */ +extern long addr_last; +extern long current_addr; +extern const char *errmsg; +extern long first_addr; +extern int lineno; +extern long second_addr; +extern long u_addr_last; +extern long u_current_addr; +extern int posixly_correct; diff --git a/text_cmds/ed/glbl.c b/text_cmds/ed/glbl.c new file mode 100644 index 0000000..9c6799c --- /dev/null +++ b/text_cmds/ed/glbl.c @@ -0,0 +1,218 @@ +/* glob.c: This file contains the global command routines for the ed line + editor */ +/*- + * Copyright (c) 1993 Andrew Moore, Talke Studio. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: src/bin/ed/glbl.c,v 1.13 2002/06/30 05:13:53 obrien Exp $"); + +#include +#include +#include + +#include "ed.h" + + +/* build_active_list: add line matching a pattern to the global-active list */ +int +build_active_list(int isgcmd) +{ + pattern_t *pat; + line_t *lp; + long n; + char *s; + char delimiter; + + if ((delimiter = *ibufp) == ' ' || delimiter == '\n') { + errmsg = "invalid pattern delimiter"; + return ERR; + } else if ((pat = get_compiled_pattern()) == NULL) + return ERR; + else if (*ibufp == delimiter) + ibufp++; + clear_active_list(); + lp = get_addressed_line_node(first_addr); + for (n = first_addr; n <= second_addr; n++, lp = lp->q_forw) { + if ((s = get_sbuf_line(lp)) == NULL) + return ERR; + if (isbinary) + NUL_TO_NEWLINE(s, lp->len); + if (!regexec(pat, s, 0, NULL, 0) == isgcmd && + set_active_node(lp) < 0) + return ERR; + } + return 0; +} + + +/* exec_global: apply command list in the command buffer to the active + lines in a range; return command status */ +long +exec_global(int interact, int gflag) +{ + static char *ocmd = NULL; + static int ocmdsz = 0; + + line_t *lp = NULL; + int status; + int n; + char *cmd = NULL; + + if (!interact) { + if (posixly_correct) { + if ((cmd = get_extended_line(&n, 0)) == NULL) + return ERR; + } else if (!strcmp(ibufp, "\n")) + cmd = "p\n"; /* null cmd-list == `p' */ + else if ((cmd = get_extended_line(&n, 0)) == NULL) + return ERR; + } + 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 */ + gflag |= GINT; + 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) { + 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) { + errmsg = "no previous command"; + return ERR; + } else cmd = ocmd; + } else if ((cmd = get_extended_line(&n, 0)) == NULL) + return ERR; + else { + REALLOC(ocmd, ocmdsz, n + 1, ERR); + memcpy(ocmd, cmd, n + 1); + cmd = ocmd; + } + + } + ibufp = cmd; + for (; *ibufp;) + if ((status = extract_addr_range()) < 0 || + (status = exec_command()) < 0 || + (status > 0 && (status = display_lines( + current_addr, current_addr, status)) < 0)) + return status; + } + return 0; +} + + +line_t **active_list; /* list of lines active in a global command */ +long active_last; /* index of last active line in active_list */ +long active_size; /* size of active_list */ +long active_ptr; /* active_list index (non-decreasing) */ +long active_ndx; /* active_list index (modulo active_last) */ + +/* set_active_node: add a line node to the global-active list */ +int +set_active_node(line_t *lp) +{ + if (active_last + 1 > active_size) { + int ti = active_size; + line_t **ts; + SPL1(); +#if defined(sun) || defined(NO_REALLOC_NULL) + if (active_list != NULL) { +#endif + if ((ts = (line_t **) realloc(active_list, + (ti += MINBUFSZ) * sizeof(line_t **))) == NULL) { + fprintf(stderr, "%s\n", strerror(errno)); + 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)); + errmsg = "out of memory"; + SPL0(); + return ERR; + } + } +#endif + active_size = ti; + active_list = ts; + SPL0(); + } + active_list[active_last++] = lp; + return 0; +} + + +/* unset_active_nodes: remove a range of lines from the global-active list */ +void +unset_active_nodes(line_t *np, line_t *mp) +{ + line_t *lp; + long i; + + for (lp = np; lp != mp; lp = lp->q_forw) + for (i = 0; i < active_last; i++) + if (active_list[active_ndx] == lp) { + active_list[active_ndx] = NULL; + active_ndx = INC_MOD(active_ndx, active_last - 1); + break; + } else active_ndx = INC_MOD(active_ndx, active_last - 1); +} + + +/* next_active_node: return the next global-active line node */ +line_t * +next_active_node(void) +{ + while (active_ptr < active_last && active_list[active_ptr] == NULL) + active_ptr++; + return (active_ptr < active_last) ? active_list[active_ptr++] : NULL; +} + + +/* clear_active_list: clear the global-active list */ +void +clear_active_list(void) +{ + SPL1(); + active_size = active_last = active_ptr = active_ndx = 0; + free(active_list); + active_list = NULL; + SPL0(); +} diff --git a/text_cmds/ed/io.c b/text_cmds/ed/io.c new file mode 100644 index 0000000..ba8e682 --- /dev/null +++ b/text_cmds/ed/io.c @@ -0,0 +1,341 @@ +/* io.c: This file contains the i/o routines for the ed line editor */ +/*- + * Copyright (c) 1993 Andrew Moore, Talke Studio. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: src/bin/ed/io.c,v 1.14 2003/01/01 18:48:39 schweikh Exp $"); + +#include "ed.h" + + +extern int scripted; + +/* read_file: read a named file/pipe into the buffer; return line count */ +long +read_file(char *fn, long n) +{ + FILE *fp; + long size; + + + fp = (*fn == '!') ? popen(fn + 1, "r") : fopen(strip_escapes(fn), "r"); + if (fp == NULL) { + fprintf(stderr, "%s: %s\n", fn, strerror(errno)); + 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)); + errmsg = "cannot close input file"; + return ERR; + } + fprintf(stdout, !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(FILE *fp, long n) +{ + line_t *lp = get_addressed_line_node(n); + undo_t *up = NULL; + unsigned long size = 0; + int o_newline_added = newline_added; + int o_isbinary = isbinary; + int appended = (n == addr_last); + int len; + + isbinary = newline_added = 0; + if (des) + init_des_cipher(); + for (current_addr = n; (len = get_stream_line(fp)) > 0; size += len) { + SPL1(); + if (put_sbuf_line(sbuf) == NULL) { + SPL0(); + return ERR; + } + lp = lp->q_forw; + if (up) + up->t = lp; + else if ((up = push_undo_stack(UADD, current_addr, + current_addr)) == NULL) { + SPL0(); + return ERR; + } + SPL0(); + } + if (len < 0) + return ERR; + if (appended && size && o_isbinary && o_newline_added) + fputs("newline inserted\n", stderr); + else if (newline_added && (!appended || (!isbinary && !o_isbinary))) + fputs("newline appended\n", stderr); + if (isbinary && newline_added && !appended) + size += 1; + if (!size) + newline_added = 1; + newline_added = appended ? newline_added : o_newline_added; + isbinary = isbinary | o_isbinary; + if (des) + size += 8 - size % 8; /* adjust DES size */ + return size; +} + + +/* get_stream_line: read a line of text from a stream; return line length */ +int +get_stream_line(FILE *fp) +{ + int c; + int i = 0; + + while (((c = des ? get_des_char(fp) : getc(fp)) != EOF || (!feof(fp) && + !ferror(fp))) && c != '\n') { + REALLOC(sbuf, sbufsz, i + 1, ERR); + if (!(sbuf[i++] = c)) + isbinary = 1; + } + REALLOC(sbuf, sbufsz, i + 2, ERR); + if (c == '\n') + sbuf[i++] = c; + else if (ferror(fp)) { + fprintf(stderr, "%s\n", strerror(errno)); + 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(char *fn, const char *mode, long n, long m) +{ + FILE *fp; + long size; + + fp = (*fn == '!') ? popen(fn+1, "w") : fopen(strip_escapes(fn), mode); + if (fp == NULL) { + fprintf(stderr, "%s: %s\n", fn, strerror(errno)); + 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)); + errmsg = "cannot close output file"; + return ERR; + } + fprintf(stdout, !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(FILE *fp, long n, long m) +{ + line_t *lp = get_addressed_line_node(n); + unsigned long size = 0; + char *s; + int len; + + if (des) + init_des_cipher(); + for (; n && n <= m; n++, lp = lp->q_forw) { + if ((s = get_sbuf_line(lp)) == NULL) + return ERR; + len = lp->len; + if (n != addr_last || !isbinary || !newline_added) + s[len++] = '\n'; + if (put_stream_line(fp, s, len) < 0) + return ERR; + size += len; + } + if (des) { + flush_des_file(fp); /* flush buffer */ + size += 8 - size % 8; /* adjust DES size */ + } + return size; +} + + +/* put_stream_line: write a line of text to a stream; return status */ +int +put_stream_line(FILE *fp, const char *s, int len) +{ + while (len--) + if ((des ? put_des_char(*s++, fp) : fputc(*s++, fp)) < 0) { + fprintf(stderr, "%s\n", strerror(errno)); + errmsg = "cannot write file"; + return ERR; + } + return 0; +} + +/* get_extended_line: get an extended line from stdin */ +char * +get_extended_line(int *sizep, int nonl) +{ + static char *cvbuf = NULL; /* buffer */ + static int cvbufsz = 0; /* buffer size */ + + int l, n; + char *t = ibufp; + + while (*t++ != '\n') + ; + if ((l = t - ibufp) < 2 || !has_trailing_escape(ibufp, ibufp + l - 1)) { + *sizep = l; + return ibufp; + } + *sizep = -1; + REALLOC(cvbuf, cvbufsz, l, NULL); + memcpy(cvbuf, ibufp, l); + *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */ + if (nonl) l--; /* strip newline */ + for (;;) { + if ((n = get_tty_line()) < 0) + return NULL; + else if (n == 0 || ibuf[n - 1] != '\n') { + errmsg = "unexpected end-of-file"; + return NULL; + } + REALLOC(cvbuf, cvbufsz, l + n, NULL); + memcpy(cvbuf + l, ibuf, n); + l += n; + if (n < 2 || !has_trailing_escape(cvbuf, cvbuf + l - 1)) + break; + *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */ + if (nonl) l--; /* strip newline */ + } + REALLOC(cvbuf, cvbufsz, l + 1, NULL); + cvbuf[l] = '\0'; + *sizep = l; + return cvbuf; +} + + +/* get_tty_line: read a line of text from stdin; return line length */ +int +get_tty_line(void) +{ + int oi = 0; + int i = 0; + int c; + + for (;;) + switch (c = getchar()) { + default: + oi = 0; + REALLOC(ibuf, ibufsz, i + 2, ERR); + if (!(ibuf[i++] = c)) isbinary = 1; + if (c != '\n') + continue; + lineno++; + ibuf[i] = '\0'; + ibufp = ibuf; + return i; + case EOF: + if (ferror(stdin)) { + fprintf(stderr, "stdin: %s\n", strerror(errno)); + 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(const char *s, int l, long n, int gflag) +{ + int col = 0; + char *cp; + + if ((gflag & GNP) && !(gflag & GINT)) { + printf("%ld\t", n); + col = 8; + } + for (; l--; s++) { + if ((gflag & GLS) && ++col > cols) { + fputs("\\\n", stdout); + col = 1; + } + 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); + } + if (posixly_correct && (gflag & GLS) && !(gflag & GINT)) + putchar('$'); + putchar('\n'); + return 0; +} diff --git a/text_cmds/ed/main.c b/text_cmds/ed/main.c new file mode 100644 index 0000000..0747c54 --- /dev/null +++ b/text_cmds/ed/main.c @@ -0,0 +1,1455 @@ +/* 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 +#if 0 +static const char copyright[] = +"@(#) Copyright (c) 1993 Andrew Moore, Talke Studio. \n\ + All rights reserved.\n"; +#endif +#endif /* not lint */ + +/* + * CREDITS + * + * This program is based on the editor algorithm described in + * Brian W. Kernighan and P. J. Plauger's book "Software Tools + * in Pascal," Addison-Wesley, 1981. + * + * The buffering algorithm is attributed to Rodney Ruddock of + * the University of Guelph, Guelph, Ontario. + * + * The cbc.c encryption code is adapted from + * the bdes program by Matt Bishop of Dartmouth College, + * Hanover, NH. + * + */ + +#include + +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#else +#define COMPAT_MODE(a,b) (1) +#endif /* __APPLE__ */ + +#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 */ +int posixly_correct = 0; /* if set, POSIX behavior as per */ +/* http://www.opengroup.org/onlinepubs/009695399/utilities/ed.html */ + +char old_filename[PATH_MAX + 1] = ""; /* default filename */ +long current_addr; /* current address in editor buffer */ +long addr_last; /* last address in editor buffer */ +int lineno; /* script line number */ +const char *prompt; /* command-line prompt */ +const char *dps = "*"; /* default command-line prompt */ + +const char usage[] = "usage: %s [-] [-sx] [-p string] [file]\n"; + +/* ed: line editor */ +int +main(int argc, char *argv[]) +{ + int c, n; + long status = 0; +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &argc; + (void) &argv; +#endif + + (void)setlocale(LC_ALL, ""); + + posixly_correct = COMPAT_MODE("bin/ed", "Unix2003"); + + red = argv[0]!=NULL && (n = strlen(argv[0])) > 2 && argv[0][n - 3] == 'r'; +top: + while ((c = getopt(argc, argv, "p:sx")) != -1) + switch(c) { + case 'p': /* set prompt */ + prompt = optarg; + break; + case 's': /* run script */ + scripted = 1; + break; + case 'x': /* use crypt */ +#ifdef DES + des = get_keyword(); +#else + fprintf(stderr, "crypt unavailable\n?\n"); +#endif + break; + + default: + fprintf(stderr, usage, red ? "red" : "ed"); + exit(1); + } + argv += optind; + argc -= optind; + if (argc>0 && *argv && !strcmp(*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); + errmsg = "interrupt"; + } else { + init_buffers(); + sigactive = 1; /* enable signal handlers */ + if (argc>0 && *argv && is_legal_filename(*argv)) { + if (read_file(*argv, 0) < 0 && !isatty(0)) + quit(2); + else if (**argv != '!') { + if (strlen(*argv) < sizeof(old_filename)) { + strcpy(old_filename, *argv); + } else { + fprintf(stderr, "%s: filename too long\n", *argv); + quit(2); + } + } + } else if (argc>0) { + fputs("?\n", stdout); + if (**argv == '\0') + 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); + 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 */ + 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 = display_lines(current_addr, current_addr, + status)) >= 0) + continue; + switch (status) { + case EOF: + quit(0); + case EMOD: + modified = 0; + fputs("?\n", stderr); /* give warning */ + 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", stdout); + 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(void) +{ + long addr; + + addr_cnt = 0; + first_addr = second_addr = current_addr; + while ((addr = next_addr()) >= 0) { + addr_cnt++; + first_addr = second_addr; + second_addr = addr; + if (*ibufp != ',' && *ibufp != ';') + break; + else if (*ibufp++ == ';') + current_addr = addr; + } + if ((addr_cnt = min(addr_cnt, 2)) == 1 || second_addr != addr) + first_addr = second_addr; + return (addr == ERR) ? ERR : 0; +} + + +#define SKIP_BLANKS() while (isspace((unsigned char)*ibufp) && *ibufp != '\n') ibufp++ + +#define MUST_BE_FIRST() do { \ + if (!first) { \ + errmsg = "invalid address"; \ + return ERR; \ + } \ +} while (0); + +/* next_addr: return the next line address in the command buffer */ +long +next_addr(void) +{ + const char *hd; + long addr = current_addr; + long n; + int first = 1; + int c; + int add = 0; + + SKIP_BLANKS(); + for (hd = ibufp;; first = 0) + switch (c = *ibufp) { + case '+': + case '\t': + case ' ': + case '-': + case '^': + ibufp++; + SKIP_BLANKS(); + if (isdigit((unsigned char)*ibufp)) { + STRTOL(n, ibufp); + addr += (c == '-' || c == '^') ? -n : n; + } else if (!isspace((unsigned char)c)) + addr += (c == '-' || c == '^') ? -1 : 1; + break; + case '0': case '1': case '2': + case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + STRTOL(n, ibufp); + if (first || !add) /* don't add for ,\d or ;\d */ + addr = n; + else + addr += n; + break; + case '.': + case '$': + MUST_BE_FIRST(); + ibufp++; + addr = (c == '.') ? current_addr : addr_last; + add++; + break; + case '/': + case '?': + MUST_BE_FIRST(); + if ((addr = get_matching_node_addr( + get_compiled_pattern(), c == '/')) < 0) + return ERR; + else if (c == *ibufp) + ibufp++; + add++; + break; + case '\'': + MUST_BE_FIRST(); + ibufp++; + if ((addr = get_marked_node_addr(*ibufp++)) < 0) + return ERR; + add++; + break; + case '%': + case ',': + case ';': + if (first) { + ibufp++; + addr_cnt++; + second_addr = (c == ';') ? current_addr : 1; + addr = addr_last; + break; + } + /* FALLTHROUGH */ + default: + if (ibufp == hd) + return EOF; + else if (addr < 0 || addr_last < addr) { + errmsg = "invalid address"; + return ERR; + } else + return addr; + } + /* NOTREACHED */ +} + + +/* 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 (!posixly_correct && addr_cnt == 0) { \ + errmsg = "destination expected"; \ + return ERR; \ + } else if (second_addr < 0 || addr_last < second_addr) { \ + errmsg = "invalid address"; \ + return ERR; \ + } \ + addr = second_addr; \ + first_addr = ol1, second_addr = ol2; \ +} + + +/* 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') { \ + errmsg = "invalid command suffix"; \ + return ERR; \ + } \ +} + + +/* sflags */ +#define SGG 001 /* complement previous global substitute suffix */ +#define SGP 002 /* complement previous print suffix */ +#define SGR 004 /* use last regex instead of last pat */ +#define SGF 010 /* repeat last substitution */ + +int patlock = 0; /* if set, pattern not freed by get_compiled_pattern() */ + +long rows = 22; /* scroll length: ws_row - 2 */ + +/* exec_command: execute the next command in command buffer; return print + request, if any */ +int +exec_command(void) +{ + static pattern_t *pat = NULL; + static int sgflag = 0; + static long sgnum = 0; + + pattern_t *tpat; + char *fnp; + int gflag = 0; + int sflags = 0; + long addr = 0; + int n = 0; + int c; + + SKIP_BLANKS(); + switch(c = *ibufp++) { + case 'a': + GET_COMMAND_SUFFIX(); + if (!isglobal) clear_undo_stack(); + if (append_lines(second_addr) < 0) + return ERR; + break; + case 'c': + if (first_addr == 0) + first_addr = 1; + 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; + if (current_addr == 0 && addr_last != 0) + current_addr = 1; + 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; + if (current_addr == 0 && addr_last != 0) + current_addr = 1; + break; + case 'e': + if (modified && !scripted) + return EMOD; + /* FALLTHROUGH */ + case 'E': + if (addr_cnt > 0) { + errmsg = "unexpected address"; + return ERR; + } else if (!isspace((unsigned char)*ibufp)) { + 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 != '!') { + if (strlen(fnp) < sizeof(old_filename)) { + strcpy(old_filename, fnp); + } else { + fprintf(stderr, "%s: filename too long\n", fnp); + quit(2); + } + } + + if (!posixly_correct && *fnp == '\0' && *old_filename == '\0') { + errmsg = "no current filename"; + return ERR; + } + 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) { + errmsg = "unexpected address"; + return ERR; + } else if (!isspace((unsigned char)*ibufp)) { + errmsg = "unexpected command suffix"; + return ERR; + } else if ((fnp = get_filename()) == NULL) + return ERR; + else if (*fnp == '!') { + errmsg = "invalid redirection"; + return ERR; + } + GET_COMMAND_SUFFIX(); + if (*fnp) { + if (strlen(fnp) < sizeof(old_filename)) { + strcpy(old_filename, fnp); + } else { + fprintf(stderr, "%s: filename too long\n", fnp); + quit(2); + } + } + printf("%s\n", strip_escapes(old_filename)); + break; + case 'g': + case 'v': + case 'G': + case 'V': + if (isglobal) { + 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++; + n = exec_global(n, gflag); + if (n == EOF) + return EOF; + else if (n < 0) + return ERR; + break; + case 'h': + if (addr_cnt > 0) { + errmsg = "unexpected address"; + return ERR; + } + GET_COMMAND_SUFFIX(); + if (*errmsg) fprintf(stderr, "%s\n", errmsg); + break; + case 'H': + if (addr_cnt > 0) { + 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) { + second_addr = 1; + } + GET_COMMAND_SUFFIX(); + if (!isglobal) clear_undo_stack(); + if (append_lines(second_addr - 1) < 0) + return ERR; + if (u_addr_last == addr_last) /* nothing inserted */ + current_addr = second_addr; + 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) { + 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) { + 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) { + errmsg = "unexpected address"; + return ERR; + } + GET_COMMAND_SUFFIX(); + prompt = prompt ? NULL : optarg ? optarg : dps; + break; + case 'q': + case 'Q': + if (addr_cnt > 0) { + errmsg = "unexpected address"; + return ERR; + } + GET_COMMAND_SUFFIX(); + gflag = (modified && !scripted && c == 'q') ? EMOD : EOF; + break; + case 'r': + if (!isspace((unsigned char)*ibufp)) { + 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 != '!') { + if (strlen(fnp) < sizeof(old_filename)) { + strcpy(old_filename, fnp); + } else { + fprintf(stderr, "%s: filename too long\n", fnp); + quit(2); + } + } + if (!posixly_correct && *fnp == '\0' && *old_filename == '\0') { + errmsg = "no current filename"; + return ERR; + } + if ((addr = read_file(*fnp ? fnp : old_filename, second_addr)) < 0) + return ERR; + else if (addr && addr != addr_last) + modified = 1; + break; + case 's': + /* + * POSIX requires ed to process srabcr123r as + * replace abc with 123 delimiter='r' + */ + if (!posixly_correct) 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) { + errmsg = "invalid command suffix"; + return ERR; + } + } + } while (sflags && *ibufp != '\n'); + if (sflags && !pat) { + errmsg = "no previous substitution"; + return ERR; + } else if (sflags & SGG) + sgnum = 0; /* override numeric arg */ + if (*ibufp != '\n' && *(ibufp + 1) == '\n') { + 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) { + errmsg = "unexpected address"; + return ERR; + } + GET_COMMAND_SUFFIX(); + if (pop_undo_stack() < 0) + return ERR; + break; + case 'w': + case 'W': + if ((n = *ibufp) == 'q' || n == 'Q') { + gflag = EOF; + ibufp++; + } + if (!isspace((unsigned char)*ibufp)) { + 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 != '!') { + if (strlen(fnp) < sizeof(old_filename)) { + strcpy(old_filename, fnp); + } else { + fprintf(stderr, "%s: filename too long\n", fnp); + quit(2); + } + } + if (!posixly_correct && *fnp == '\0' && *old_filename == '\0') { + errmsg = "no current filename"; + return ERR; + } + 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) { + errmsg = "unexpected address"; + return ERR; + } + GET_COMMAND_SUFFIX(); +#ifdef DES + des = get_keyword(); + break; +#else + errmsg = "crypt unavailable"; + return ERR; +#endif + case 'z': + if (check_addr_range(first_addr = 1, current_addr + (posixly_correct ? !isglobal : 1)) < 0) + return ERR; + else if ('0' < *ibufp && *ibufp <= '9') + STRTOL(rows, ibufp); + GET_COMMAND_SUFFIX(); + if (display_lines(second_addr, min(addr_last, + second_addr + rows), gflag) < 0) + return ERR; + gflag = 0; + break; + case '=': + GET_COMMAND_SUFFIX(); + printf("%ld\n", addr_cnt ? second_addr : addr_last); + break; + case '!': + if (addr_cnt > 0) { + errmsg = "unexpected address"; + return ERR; + } else if ((sflags = get_shell_command()) < 0) + return ERR; + GET_COMMAND_SUFFIX(); + if (sflags) printf("%s\n", shcmd + 1); + fflush(stdout); + system(shcmd + 1); + if (!scripted) printf("!\n"); + break; + case '\n': + if (check_addr_range(first_addr = 1, current_addr + (posixly_correct ? !isglobal : 1)) < 0 + || display_lines(second_addr, second_addr, 0) < 0) + return ERR; + break; + default: + errmsg = "unknown command"; + return ERR; + } + return gflag; +} + + +/* check_addr_range: return status of address range check */ +int +check_addr_range(long n, long m) +{ + if (addr_cnt == 0) { + first_addr = n; + second_addr = m; + } + if (first_addr > second_addr || 1 > first_addr || + second_addr > addr_last) { + errmsg = "invalid address"; + return ERR; + } + return 0; +} + + +/* get_matching_node_addr: return the address of the next line matching a + pattern in a given direction. wrap around begin/end of editor buffer if + necessary */ +long +get_matching_node_addr(pattern_t *pat, int dir) +{ + char *s; + long n = current_addr; + line_t *lp; + + if (!pat) return ERR; + do { + if ((n = dir ? INC_MOD(n, addr_last) : DEC_MOD(n, addr_last))) { + 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); + errmsg = "no match"; + return ERR; +} + + +/* get_filename: return pointer to copy of filename in the command buffer */ +char * +get_filename(void) +{ + static char *file = NULL; + static int filesz = 0; + + int n; + + if (*ibufp != '\n') { + SKIP_BLANKS(); + if (*ibufp == '\n') { + 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 > PATH_MAX) { + errmsg = "filename too long"; + return NULL; + } + } + else if (posixly_correct && *old_filename == '\0') { + errmsg = "no current filename"; + return NULL; + } + REALLOC(file, filesz, PATH_MAX + 1, NULL); + for (n = 0; *ibufp != '\n';) + file[n++] = *ibufp++; + file[n] = '\0'; + return is_legal_filename(file) ? file : NULL; +} + + +/* get_shell_command: read a shell command from stdin; return substitution + status */ +int +get_shell_command(void) +{ + static char *buf = NULL; + static int n = 0; + + char *s; /* substitution char pointer */ + int i = 0; + int j = 0; + + if (red) { + 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++; + } + else if (shcmd == NULL || (!posixly_correct && *(shcmd + 1) == '\0')) + { + 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') { + errmsg = "no current filename"; + return ERR; + } + j = strlen(s = strip_escapes(old_filename)); + REALLOC(buf, n, i + j, ERR); + while (j--) + buf[i++] = *s++; + s = ibufp++; + break; + } + REALLOC(shcmd, shcmdsz, i + 1, ERR); + memcpy(shcmd, buf, i); + shcmd[shcmdi = i] = '\0'; + return *s == '!' || *s == '%'; +} + + +/* append_lines: insert text from stdin to after line n; stop when either a + single period is read or EOF; return status */ +int +append_lines(long n) +{ + int l; + const char *lp = ibuf; + const char *eot; + undo_t *up = NULL; + + for (current_addr = n;;) { + if (!isglobal) { + if ((l = get_tty_line()) < 0) + return ERR; + else if (l == 0 || ibuf[l - 1] != '\n') { + clearerr(stdin); + return l ? EOF : 0; + } + lp = ibuf; + } else if (*(lp = ibufp) == '\0') + return 0; + else { + while (*ibufp++ != '\n') + ; + l = ibufp - lp; + } + if (l == 2 && lp[0] == '.' && lp[1] == '\n') { + return 0; + } + eot = lp + l; + SPL1(); + do { + if ((lp = put_sbuf_line(lp)) == NULL) { + SPL0(); + return ERR; + } else if (up) + up->t = get_addressed_line_node(current_addr); + else if ((up = push_undo_stack(UADD, current_addr, + current_addr)) == NULL) { + SPL0(); + return ERR; + } + } while (lp != eot); + modified = 1; + SPL0(); + } + /* NOTREACHED */ +} + + +/* join_lines: replace a range of lines with the joined text of those lines */ +int +join_lines(long from, long to) +{ + static char *buf = NULL; + static int n; + + char *s; + int size = 0; + line_t *bp, *ep; + + ep = get_addressed_line_node(INC_MOD(to, addr_last)); + bp = get_addressed_line_node(from); + for (; bp != ep; bp = bp->q_forw) { + if ((s = get_sbuf_line(bp)) == NULL) + return ERR; + REALLOC(buf, n, size + bp->len, ERR); + memcpy(buf + size, s, bp->len); + size += bp->len; + } + REALLOC(buf, n, size + 2, ERR); + memcpy(buf + size, "\n", 2); + if (delete_lines(from, to) < 0) + return ERR; + current_addr = from - 1; + SPL1(); + if (put_sbuf_line(buf) == NULL || + push_undo_stack(UADD, current_addr, current_addr) == NULL) { + SPL0(); + return ERR; + } + modified = 1; + if (current_addr == 0) + current_addr = 1; + SPL0(); + return 0; +} + + +/* move_lines: move a range of lines */ +int +move_lines(long addr) +{ + line_t *b1, *a1, *b2, *a2; + long n = INC_MOD(second_addr, addr_last); + long p = first_addr - 1; + int done = (addr == first_addr - 1 || addr == second_addr); + + SPL1(); + if (done) { + a2 = get_addressed_line_node(n); + b2 = get_addressed_line_node(p); + current_addr = second_addr; + } else if (push_undo_stack(UMOV, p, n) == NULL || + push_undo_stack(UMOV, addr, INC_MOD(addr, addr_last)) == NULL) { + SPL0(); + return ERR; + } else { + a1 = get_addressed_line_node(n); + if (addr < first_addr) { + b1 = get_addressed_line_node(p); + b2 = get_addressed_line_node(addr); + /* this get_addressed_line_node last! */ + } else { + b2 = get_addressed_line_node(addr); + b1 = get_addressed_line_node(p); + /* this get_addressed_line_node last! */ + } + a2 = b2->q_forw; + REQUE(b2, b1->q_forw); + REQUE(a1->q_back, a2); + REQUE(b1, a1); + current_addr = addr + ((addr < first_addr) ? + second_addr - first_addr + 1 : 0); + } + if (isglobal) + unset_active_nodes(b2->q_forw, a2); + modified = 1; + SPL0(); + return 0; +} + + +/* copy_lines: copy a range of lines; return status */ +int +copy_lines(long addr) +{ + line_t *lp, *np = get_addressed_line_node(first_addr); + undo_t *up = NULL; + long n = second_addr - first_addr + 1; + long m = 0; + + current_addr = addr; + if (first_addr <= addr && addr < second_addr) { + n = addr - first_addr + 1; + m = second_addr - addr; + } + for (; n > 0; n=m, m=0, np = get_addressed_line_node(current_addr + 1)) + for (; n-- > 0; np = np->q_forw) { + SPL1(); + if ((lp = dup_line_node(np)) == NULL) { + SPL0(); + return ERR; + } + add_line_node(lp); + if (up) + up->t = lp; + else if ((up = push_undo_stack(UADD, current_addr, + current_addr)) == NULL) { + SPL0(); + return ERR; + } + modified = 1; + SPL0(); + } + return 0; +} + + +/* delete_lines: delete a range of lines */ +int +delete_lines(long from, long to) +{ + line_t *n, *p; + + SPL1(); + if (push_undo_stack(UDEL, from, to) == NULL) { + SPL0(); + return ERR; + } + n = get_addressed_line_node(INC_MOD(to, addr_last)); + p = get_addressed_line_node(from - 1); + /* this get_addressed_line_node last! */ + if (isglobal) + unset_active_nodes(p->q_forw, n); + REQUE(p, n); + addr_last -= to - from + 1; + current_addr = from - 1; + modified = 1; + SPL0(); + return 0; +} + + +/* display_lines: print a range of lines to stdout */ +int +display_lines(long from, long to, int gflag) +{ + line_t *bp; + line_t *ep; + char *s; + + if (!from) { + errmsg = "invalid address"; + return ERR; + } + ep = get_addressed_line_node(INC_MOD(to, addr_last)); + bp = get_addressed_line_node(from); + for (; bp != ep; bp = bp->q_forw) { + if ((s = get_sbuf_line(bp)) == NULL) + return ERR; + if (put_tty_line(s, bp->len, current_addr = from++, gflag) < 0) + return ERR; + } + return 0; +} + + +#define MAXMARK 26 /* max number of marks */ + +line_t *mark[MAXMARK]; /* line markers */ +int markno; /* line marker count */ + +/* mark_line_node: set a line node mark */ +int +mark_line_node(line_t *lp, int n) +{ + if (!islower((unsigned char)n)) { + errmsg = "invalid mark character"; + return ERR; + } else if (mark[n - 'a'] == NULL) + markno++; + mark[n - 'a'] = lp; + return 0; +} + + +/* get_marked_node_addr: return address of a marked line */ +long +get_marked_node_addr(int n) +{ + if (!islower((unsigned char)n)) { + errmsg = "invalid mark character"; + return ERR; + } + return get_line_node_addr(mark[n - 'a']); +} + +/* Used by search and replace */ +void +replace_marks(line_t *old, line_t *new) +{ + int i; + for (i=0; markno && iseek = lp->seek; + np->len = lp->len; + return np; +} + + +/* has_trailing_escape: return the parity of escapes preceding a character + in a string */ +int +has_trailing_escape(char *s, char *t) +{ + return (s == t || *(t - 1) != '\\') ? 0 : !has_trailing_escape(s, t - 1); +} + + +/* strip_escapes: return copy of escaped string of at most length PATH_MAX */ +char * +strip_escapes(char *s) +{ + static char *file = NULL; + static int filesz = 0; + + int i = 0; + + REALLOC(file, filesz, PATH_MAX, NULL); + while (i < filesz - 1 /* Worry about a possible trailing escape */ + && (file[i++] = (*s == '\\') ? *++s : *s)) + s++; + return file; +} + + +void +signal_hup(int signo) +{ + if (mutex) + sigflags |= (1 << (signo - 1)); + else + handle_hup(signo); +} + + +void +signal_int(int signo) +{ + if (mutex) + sigflags |= (1 << (signo - 1)); + else + handle_int(signo); +} + + +void +handle_hup(int signo) +{ + char *hup = NULL; /* hup filename */ + char *s; + char ed_hup[] = "ed.hup"; + 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 <= PATH_MAX && /* "ed.hup" + '/' */ + (hup = (char *) malloc(n + 10)) != NULL) { + strcpy(hup, s); + if (hup[n - 1] != '/') + hup[n] = '/', hup[n+1] = '\0'; + strcat(hup, "ed.hup"); + write_file(hup, "w", 1, addr_last); + } + quit(2); +} + + +void +handle_int(int signo) +{ + if (!sigactive) + quit(1); + sigflags &= ~(1 << (signo - 1)); +#ifdef _POSIX_SOURCE + siglongjmp(env, -1); +#else + longjmp(env, -1); +#endif +} + + +int cols = 72; /* wrap column */ + +void +handle_winch(int signo) +{ + int save_errno = errno; + + 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; + } + errno = save_errno; +} + + +/* is_legal_filename: return a legal filename */ +int +is_legal_filename(char *s) +{ + if (red && (*s == '!' || !strcmp(s, "..") || strchr(s, '/'))) { + errmsg = "shell access restricted"; + return 0; + } + return 1; +} diff --git a/text_cmds/ed/re.c b/text_cmds/ed/re.c new file mode 100644 index 0000000..43f67ae --- /dev/null +++ b/text_cmds/ed/re.c @@ -0,0 +1,132 @@ +/* re.c: This file contains the regular expression interface routines for + the ed line editor. */ +/*- + * Copyright (c) 1993 Andrew Moore, Talke Studio. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: src/bin/ed/re.c,v 1.20 2003/07/20 10:24:09 ru Exp $"); + +#include "ed.h" + + +extern int patlock; + +const char *errmsg = ""; + +/* get_compiled_pattern: return pointer to compiled pattern from command + buffer */ +pattern_t * +get_compiled_pattern(void) +{ + static pattern_t *expr = NULL; + static char error[1024]; + + char *exprs; + char delimiter; + int n; + + if ((delimiter = *ibufp) == ' ') { + errmsg = "invalid pattern delimiter"; + return NULL; + } else if (delimiter == '\n' || *++ibufp == '\n' || *ibufp == delimiter) { + if (!expr) + errmsg = "no previous pattern"; + return expr; + } else if ((exprs = extract_pattern(delimiter)) == NULL) + return NULL; + /* buffer alloc'd && not reserved */ + if (expr && !patlock) + regfree(expr); + else if ((expr = (pattern_t *) malloc(sizeof(pattern_t))) == NULL) { + fprintf(stderr, "%s\n", strerror(errno)); + errmsg = "out of memory"; + return NULL; + } + patlock = 0; + if ((n = regcomp(expr, exprs, 0))) { + regerror(n, expr, error, sizeof error); + errmsg = error; + free(expr); + return expr = NULL; + } + return expr; +} + + +/* extract_pattern: copy a pattern string from the command buffer; return + pointer to the copy */ +char * +extract_pattern(int delimiter) +{ + static char *lhbuf = NULL; /* buffer */ + static int lhbufsz = 0; /* buffer size */ + + char *nd; + int len; + + for (nd = ibufp; *nd != delimiter && *nd != '\n'; nd++) + switch (*nd) { + default: + break; + case '[': + if ((nd = parse_char_class(++nd)) == NULL) { + errmsg = "unbalanced brackets ([])"; + return NULL; + } + break; + case '\\': + if (*++nd == '\n') { + errmsg = "trailing backslash (\\)"; + return NULL; + } + break; + } + len = nd - ibufp; + REALLOC(lhbuf, lhbufsz, len + 1, NULL); + memcpy(lhbuf, ibufp, len); + lhbuf[len] = '\0'; + ibufp = nd; + return (isbinary) ? NUL_TO_NEWLINE(lhbuf, len) : lhbuf; +} + + +/* parse_char_class: expand a POSIX character class */ +char * +parse_char_class(char *s) +{ + int c, d; + + if (*s == '^') + s++; + if (*s == ']') + s++; + for (; *s != ']' && *s != '\n'; s++) + if (*s == '[' && ((d = *(s+1)) == '.' || d == ':' || d == '=')) + for (s++, c = *++s; *s != ']' || c != d; s++) + if ((c = *s) == '\n') + return NULL; + return (*s == ']') ? s : NULL; +} diff --git a/text_cmds/ed/red.1 b/text_cmds/ed/red.1 new file mode 100644 index 0000000..708251a --- /dev/null +++ b/text_cmds/ed/red.1 @@ -0,0 +1 @@ +.so man1/ed.1 diff --git a/text_cmds/ed/sub.c b/text_cmds/ed/sub.c new file mode 100644 index 0000000..8d89a03 --- /dev/null +++ b/text_cmds/ed/sub.c @@ -0,0 +1,256 @@ +/* sub.c: This file contains the substitution routines for the ed + line editor */ +/*- + * Copyright (c) 1993 Andrew Moore, Talke Studio. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: src/bin/ed/sub.c,v 1.15 2002/06/30 05:13:53 obrien Exp $"); + +#include "ed.h" + + +char *rhbuf; /* rhs substitution buffer */ +int rhbufsz; /* rhs substitution buffer size */ +int rhbufi; /* rhs substitution buffer index */ + +/* extract_subst_tail: extract substitution tail from the command buffer */ +int +extract_subst_tail(int *flagp, long *np) +{ + char delimiter; + + *flagp = *np = 0; + if ((delimiter = *ibufp) == '\n') { + rhbufi = 0; + *flagp = GPR; + return 0; + } else if (extract_subst_template() == NULL) + return ERR; + else if (*ibufp == '\n') { + *flagp = GPR; + return 0; + } else if (*ibufp == delimiter) + ibufp++; + if ('1' <= *ibufp && *ibufp <= '9') { + STRTOL(*np, ibufp); + return 0; + } else if (*ibufp == 'g') { + ibufp++; + *flagp = GSG; + return 0; + } + return 0; +} + + +/* extract_subst_template: return pointer to copy of substitution template + in the command buffer */ +char * +extract_subst_template(void) +{ + int n = 0; + int i = 0; + char c; + char delimiter = *ibufp++; + + if (*ibufp == '%' && *(ibufp + 1) == delimiter) { + ibufp++; + if (!rhbuf) + errmsg = "no previous substitution"; + return rhbuf; + } + while (*ibufp != delimiter) { + REALLOC(rhbuf, rhbufsz, i + 2, NULL); + if ((c = rhbuf[i++] = *ibufp++) == '\n' && *ibufp == '\0') { + i--, ibufp--; + break; + } else if (c != '\\') + ; + else if ((rhbuf[i++] = *ibufp++) != '\n') + ; + else if (!isglobal) { + while ((n = get_tty_line()) == 0 || + (n > 0 && ibuf[n - 1] != '\n')) + clearerr(stdin); + if (n < 0) + return NULL; + } + } + REALLOC(rhbuf, rhbufsz, i + 1, NULL); + rhbuf[rhbufi = i] = '\0'; + return rhbuf; +} + + +char *rbuf; /* substitute_matching_text buffer */ +int rbufsz; /* substitute_matching_text buffer size */ + +/* search_and_replace: for each line in a range, change text matching a pattern + according to a substitution template; return status */ +int +search_and_replace(pattern_t *pat, int gflag, int kth) +{ + undo_t *up; + const char *txt; + const char *eot; + long lc; + long xa = current_addr; + int nsubs = 0; + line_t *lp; + int len; + + current_addr = first_addr - 1; + for (lc = 0; lc <= second_addr - first_addr; lc++) { + lp = get_addressed_line_node(++current_addr); + if ((len = substitute_matching_text(pat, lp, gflag, kth)) < 0) + return ERR; + else if (len) { + up = NULL; + if (delete_lines(current_addr, current_addr) < 0) + return ERR; + txt = rbuf; + eot = rbuf + len; + SPL1(); + do { + if ((txt = put_sbuf_line(txt)) == NULL) { + SPL0(); + return ERR; + } else if (up) + up->t = get_addressed_line_node(current_addr); + else if ((up = push_undo_stack(UADD, + current_addr, current_addr)) == NULL) { + SPL0(); + return ERR; + } + } while (txt != eot); + SPL0(); + nsubs++; + /* 3751351 */ + replace_marks(lp, get_addressed_line_node(current_addr)); + xa = current_addr; + } + } + current_addr = xa; + if (nsubs == 0 && !(gflag & GLB)) { + errmsg = "no match"; + return ERR; + } else if ((gflag & (GPR | GLS | GNP)) && + display_lines(current_addr, current_addr, gflag) < 0) + return ERR; + return 0; +} + + +/* substitute_matching_text: replace text matched by a pattern according to + a substitution template; return pointer to the modified text */ +int +substitute_matching_text(pattern_t *pat, line_t *lp, int gflag, int kth) +{ + int off = 0; + int changed = 0; + int matchno = 0; + int i = 0; + regmatch_t rm[SE_MAX]; + char *txt; + char *eot; + + if ((txt = get_sbuf_line(lp)) == NULL) + return ERR; + if (isbinary) + NUL_TO_NEWLINE(txt, lp->len); + eot = txt + lp->len; + if (!regexec(pat, txt, SE_MAX, rm, 0)) { + do { + if (!kth || kth == ++matchno) { + changed++; + i = rm[0].rm_so; + REALLOC(rbuf, rbufsz, off + i, ERR); + if (isbinary) + NEWLINE_TO_NUL(txt, rm[0].rm_eo); + memcpy(rbuf + off, txt, i); + off += i; + if ((off = apply_subst_template(txt, rm, off, + pat->re_nsub)) < 0) + return ERR; + } else { + i = rm[0].rm_eo; + REALLOC(rbuf, rbufsz, off + i, ERR); + if (isbinary) + NEWLINE_TO_NUL(txt, i); + memcpy(rbuf + off, txt, i); + off += i; + } + txt += rm[0].rm_eo; + } while (*txt && + (!changed || ((gflag & GSG) && rm[0].rm_eo)) && + !regexec(pat, txt, SE_MAX, rm, REG_NOTBOL)); + i = eot - txt; + REALLOC(rbuf, rbufsz, off + i + 2, ERR); + if (i > 0 && !rm[0].rm_eo && (gflag & GSG)) { + 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(const 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/text_cmds/ed/test/=.err b/text_cmds/ed/test/=.err new file mode 100644 index 0000000..6a60559 --- /dev/null +++ b/text_cmds/ed/test/=.err @@ -0,0 +1 @@ +1,$= diff --git a/text_cmds/ed/test/README b/text_cmds/ed/test/README new file mode 100644 index 0000000..e360c4f --- /dev/null +++ b/text_cmds/ed/test/README @@ -0,0 +1,32 @@ +# $FreeBSD: src/bin/ed/test/README,v 1.7 1999/08/27 23:14:17 peter Exp $ + +The files in this directory with suffixes `.t', `.d', `.r' and `.err' are +used for testing ed. To run the tests, set the ED variable in the Makefile +for the path name of the program to be tested (e.g., /bin/ed), and type +`make'. The tests do not exhaustively verify POSIX compliance nor do +they verify correct 8-bit or long line support. + +The test file suffixes have the following meanings: +.t Template - a list of ed commands from which an ed script is + constructed +.d Data - read by an ed script +.r Result - the expected output after processing data via an ed + script. +.err Error - invalid ed commands that should generate an error + +The output of the tests is written to the two files err.o and scripts.o. +At the end of the tests, these files are grep'ed for error messages, +which look like: + *** The script u.ed exited abnormally *** +or: + *** Output u.o of script u.ed is incorrect *** + +The POSIX requirement that an address range not be used where at most +a single address is expected has been relaxed in this version of ed. +Therefore, the following scripts which test for compliance with this +POSIX rule exit abnormally: +=-err.ed +a1-err.ed +i1-err.ed +k1-err.ed +r1-err.ed diff --git a/text_cmds/ed/test/TODO b/text_cmds/ed/test/TODO new file mode 100644 index 0000000..7a4b74f --- /dev/null +++ b/text_cmds/ed/test/TODO @@ -0,0 +1,15 @@ +Some missing tests: +0) g/./s^@^@ - okay: NULs in commands +1) g/./s/^@/ - okay: NULs in patterns +2) a + hello^V^Jworld + . - okay: embedded newlines in insert mode +3) ed "" - error: invalid filename +4) red .. - error: restricted +5) red / - error: restricted +5) red !xx - error: restricted +6) ed -x - verify: 8-bit clean +7) ed - verify: long-line support +8) ed - verify: interactive/help mode +9) G/pat/ - verify: global interactive command +10) V/pat/ - verify: global interactive command diff --git a/text_cmds/ed/test/a.d b/text_cmds/ed/test/a.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/a.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/a.r b/text_cmds/ed/test/a.r new file mode 100644 index 0000000..26257bd --- /dev/null +++ b/text_cmds/ed/test/a.r @@ -0,0 +1,8 @@ +hello world +line 1 +hello world! +line 2 +line 3 +line 4 +line5 +hello world!! diff --git a/text_cmds/ed/test/a.t b/text_cmds/ed/test/a.t new file mode 100644 index 0000000..ac98c40 --- /dev/null +++ b/text_cmds/ed/test/a.t @@ -0,0 +1,9 @@ +0a +hello world +. +2a +hello world! +. +$a +hello world!! +. diff --git a/text_cmds/ed/test/a1.err b/text_cmds/ed/test/a1.err new file mode 100644 index 0000000..e80815f --- /dev/null +++ b/text_cmds/ed/test/a1.err @@ -0,0 +1,3 @@ +1,$a +hello world +. diff --git a/text_cmds/ed/test/a2.err b/text_cmds/ed/test/a2.err new file mode 100644 index 0000000..ec4b00b --- /dev/null +++ b/text_cmds/ed/test/a2.err @@ -0,0 +1,3 @@ +aa +hello world +. diff --git a/text_cmds/ed/test/addr.d b/text_cmds/ed/test/addr.d new file mode 100644 index 0000000..8f7ba1b --- /dev/null +++ b/text_cmds/ed/test/addr.d @@ -0,0 +1,9 @@ +line 1 +line 2 +line 3 +line 4 +line5 +1ine6 +line7 +line8 +line9 diff --git a/text_cmds/ed/test/addr.r b/text_cmds/ed/test/addr.r new file mode 100644 index 0000000..04caf17 --- /dev/null +++ b/text_cmds/ed/test/addr.r @@ -0,0 +1,2 @@ +line 2 +line9 diff --git a/text_cmds/ed/test/addr.t b/text_cmds/ed/test/addr.t new file mode 100644 index 0000000..750b224 --- /dev/null +++ b/text_cmds/ed/test/addr.t @@ -0,0 +1,5 @@ +1 d +1 1 d +1,2,d +1;+ + ,d +1,2;., + 2d diff --git a/text_cmds/ed/test/addr1.err b/text_cmds/ed/test/addr1.err new file mode 100644 index 0000000..29d6383 --- /dev/null +++ b/text_cmds/ed/test/addr1.err @@ -0,0 +1 @@ +100 diff --git a/text_cmds/ed/test/addr2.err b/text_cmds/ed/test/addr2.err new file mode 100644 index 0000000..e96acb9 --- /dev/null +++ b/text_cmds/ed/test/addr2.err @@ -0,0 +1 @@ +-100 diff --git a/text_cmds/ed/test/ascii.d.uu b/text_cmds/ed/test/ascii.d.uu new file mode 100644 index 0000000..0b0a73c --- /dev/null +++ b/text_cmds/ed/test/ascii.d.uu @@ -0,0 +1,9 @@ +begin 644 ascii.d +M``$"`P0%!@<("0H+#`T.#Q`1$A,4%187&!D:&QP='A\@(2(C)"4F)R@I*BLL +M+2XO,#$R,S0U-C'EZ>WQ]?G^`@8*#A(6& +MAXB)BHN,C8Z/D)&2DY25EI>8F9J;G)V>GZ"AHJ.DI::GJ*FJJZRMKJ^PL;*S +MM+6VM[BYNKN\O;Z_P,'"P\3%QL?(R+CY.7FY^CIZNOL[>[O\/'R\_3U]O?X^?K[_/W^_]/4 +` +end diff --git a/text_cmds/ed/test/ascii.r.uu b/text_cmds/ed/test/ascii.r.uu new file mode 100644 index 0000000..9ca88b4 --- /dev/null +++ b/text_cmds/ed/test/ascii.r.uu @@ -0,0 +1,9 @@ +begin 644 ascii.r +M``$"`P0%!@<("0H+#`T.#Q`1$A,4%187&!D:&QP='A\@(2(C)"4F)R@I*BLL +M+2XO,#$R,S0U-C'EZ>WQ]?G^`@8*#A(6& +MAXB)BHN,C8Z/D)&2DY25EI>8F9J;G)V>GZ"AHJ.DI::GJ*FJJZRMKJ^PL;*S +MM+6VM[BYNKN\O;Z_P,'"P\3%QL?(R+CY.7FY^CIZNOL[>[O\/'R\_3U]O?X^?K[_/W^_]/4 +` +end diff --git a/text_cmds/ed/test/ascii.t b/text_cmds/ed/test/ascii.t new file mode 100644 index 0000000..e69de29 diff --git a/text_cmds/ed/test/bang1.d b/text_cmds/ed/test/bang1.d new file mode 100644 index 0000000..e69de29 diff --git a/text_cmds/ed/test/bang1.err b/text_cmds/ed/test/bang1.err new file mode 100644 index 0000000..630af90 --- /dev/null +++ b/text_cmds/ed/test/bang1.err @@ -0,0 +1 @@ +.!date diff --git a/text_cmds/ed/test/bang1.r b/text_cmds/ed/test/bang1.r new file mode 100644 index 0000000..dcf02b2 --- /dev/null +++ b/text_cmds/ed/test/bang1.r @@ -0,0 +1 @@ +okay diff --git a/text_cmds/ed/test/bang1.t b/text_cmds/ed/test/bang1.t new file mode 100644 index 0000000..d7b1fea --- /dev/null +++ b/text_cmds/ed/test/bang1.t @@ -0,0 +1,5 @@ +!read one +hello, world +a +okay +. diff --git a/text_cmds/ed/test/bang2.err b/text_cmds/ed/test/bang2.err new file mode 100644 index 0000000..79d8956 --- /dev/null +++ b/text_cmds/ed/test/bang2.err @@ -0,0 +1 @@ +!! diff --git a/text_cmds/ed/test/c.d b/text_cmds/ed/test/c.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/c.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/c.r b/text_cmds/ed/test/c.r new file mode 100644 index 0000000..0fb3e4f --- /dev/null +++ b/text_cmds/ed/test/c.r @@ -0,0 +1,4 @@ +at the top +between top/middle +in the middle +at the bottom diff --git a/text_cmds/ed/test/c.t b/text_cmds/ed/test/c.t new file mode 100644 index 0000000..ebdd536 --- /dev/null +++ b/text_cmds/ed/test/c.t @@ -0,0 +1,12 @@ +1c +at the top +. +4c +in the middle +. +$c +at the bottom +. +2,3c +between top/middle +. diff --git a/text_cmds/ed/test/c1.err b/text_cmds/ed/test/c1.err new file mode 100644 index 0000000..658ec38 --- /dev/null +++ b/text_cmds/ed/test/c1.err @@ -0,0 +1,3 @@ +cc +hello world +. diff --git a/text_cmds/ed/test/c2.err b/text_cmds/ed/test/c2.err new file mode 100644 index 0000000..24b3227 --- /dev/null +++ b/text_cmds/ed/test/c2.err @@ -0,0 +1,3 @@ +0c +hello world +. diff --git a/text_cmds/ed/test/ckscripts.sh b/text_cmds/ed/test/ckscripts.sh new file mode 100755 index 0000000..2c57449 --- /dev/null +++ b/text_cmds/ed/test/ckscripts.sh @@ -0,0 +1,37 @@ +#!/bin/sh - +# This script runs the .ed scripts generated by mkscripts.sh +# and compares their output against the .r files, which contain +# the correct output +# +# $FreeBSD: src/bin/ed/test/ckscripts.sh,v 1.6 1999/08/27 23:14:18 peter Exp $ + +PATH="/bin:/usr/bin:/usr/local/bin/:." +ED=$1 +[ ! -x $ED ] && { echo "$ED: cannot execute"; exit 1; } + +# Run the *.red scripts first, since these don't generate output; +# they exit with non-zero status +for i in *.red; do + echo $i + if $i; then + echo "*** The script $i exited abnormally ***" + fi +done >errs.o 2>&1 + +# Run the remainding scripts; they exit with zero status +for i in *.ed; do +# base=`expr $i : '\([^.]*\)'` +# base=`echo $i | sed 's/\..*//'` + base=`$ED - \!"echo $i" <<-EOF + s/\..* + EOF` + if $base.ed; then + if cmp -s $base.o $base.r; then :; else + echo "*** Output $base.o of script $i is incorrect ***" + fi + else + echo "*** The script $i exited abnormally ***" + fi +done >scripts.o 2>&1 + +grep -h '\*\*\*' errs.o scripts.o diff --git a/text_cmds/ed/test/d.d b/text_cmds/ed/test/d.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/d.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/d.err b/text_cmds/ed/test/d.err new file mode 100644 index 0000000..f03f694 --- /dev/null +++ b/text_cmds/ed/test/d.err @@ -0,0 +1 @@ +dd diff --git a/text_cmds/ed/test/d.r b/text_cmds/ed/test/d.r new file mode 100644 index 0000000..b7e242c --- /dev/null +++ b/text_cmds/ed/test/d.r @@ -0,0 +1 @@ +line 2 diff --git a/text_cmds/ed/test/d.t b/text_cmds/ed/test/d.t new file mode 100644 index 0000000..c7c473f --- /dev/null +++ b/text_cmds/ed/test/d.t @@ -0,0 +1,3 @@ +1d +2;+1d +$d diff --git a/text_cmds/ed/test/e1.d b/text_cmds/ed/test/e1.d new file mode 100644 index 0000000..3b18e51 --- /dev/null +++ b/text_cmds/ed/test/e1.d @@ -0,0 +1 @@ +hello world diff --git a/text_cmds/ed/test/e1.err b/text_cmds/ed/test/e1.err new file mode 100644 index 0000000..827cc29 --- /dev/null +++ b/text_cmds/ed/test/e1.err @@ -0,0 +1 @@ +ee e1.err diff --git a/text_cmds/ed/test/e1.r b/text_cmds/ed/test/e1.r new file mode 100644 index 0000000..e656728 --- /dev/null +++ b/text_cmds/ed/test/e1.r @@ -0,0 +1 @@ +E e1.t diff --git a/text_cmds/ed/test/e1.t b/text_cmds/ed/test/e1.t new file mode 100644 index 0000000..e656728 --- /dev/null +++ b/text_cmds/ed/test/e1.t @@ -0,0 +1 @@ +E e1.t diff --git a/text_cmds/ed/test/e2.d b/text_cmds/ed/test/e2.d new file mode 100644 index 0000000..aa44630 --- /dev/null +++ b/text_cmds/ed/test/e2.d @@ -0,0 +1 @@ +E !echo hello world- diff --git a/text_cmds/ed/test/e2.err b/text_cmds/ed/test/e2.err new file mode 100644 index 0000000..779a64b --- /dev/null +++ b/text_cmds/ed/test/e2.err @@ -0,0 +1 @@ +.e e2.err diff --git a/text_cmds/ed/test/e2.r b/text_cmds/ed/test/e2.r new file mode 100644 index 0000000..59ebf11 --- /dev/null +++ b/text_cmds/ed/test/e2.r @@ -0,0 +1 @@ +hello world- diff --git a/text_cmds/ed/test/e2.t b/text_cmds/ed/test/e2.t new file mode 100644 index 0000000..aa44630 --- /dev/null +++ b/text_cmds/ed/test/e2.t @@ -0,0 +1 @@ +E !echo hello world- diff --git a/text_cmds/ed/test/e3.d b/text_cmds/ed/test/e3.d new file mode 100644 index 0000000..aa44630 --- /dev/null +++ b/text_cmds/ed/test/e3.d @@ -0,0 +1 @@ +E !echo hello world- diff --git a/text_cmds/ed/test/e3.err b/text_cmds/ed/test/e3.err new file mode 100644 index 0000000..80a7fdc --- /dev/null +++ b/text_cmds/ed/test/e3.err @@ -0,0 +1 @@ +ee.err diff --git a/text_cmds/ed/test/e3.r b/text_cmds/ed/test/e3.r new file mode 100644 index 0000000..aa44630 --- /dev/null +++ b/text_cmds/ed/test/e3.r @@ -0,0 +1 @@ +E !echo hello world- diff --git a/text_cmds/ed/test/e3.t b/text_cmds/ed/test/e3.t new file mode 100644 index 0000000..1c50726 --- /dev/null +++ b/text_cmds/ed/test/e3.t @@ -0,0 +1 @@ +E diff --git a/text_cmds/ed/test/e4.d b/text_cmds/ed/test/e4.d new file mode 100644 index 0000000..aa44630 --- /dev/null +++ b/text_cmds/ed/test/e4.d @@ -0,0 +1 @@ +E !echo hello world- diff --git a/text_cmds/ed/test/e4.r b/text_cmds/ed/test/e4.r new file mode 100644 index 0000000..aa44630 --- /dev/null +++ b/text_cmds/ed/test/e4.r @@ -0,0 +1 @@ +E !echo hello world- diff --git a/text_cmds/ed/test/e4.t b/text_cmds/ed/test/e4.t new file mode 100644 index 0000000..d905d9d --- /dev/null +++ b/text_cmds/ed/test/e4.t @@ -0,0 +1 @@ +e diff --git a/text_cmds/ed/test/f1.err b/text_cmds/ed/test/f1.err new file mode 100644 index 0000000..e60975a --- /dev/null +++ b/text_cmds/ed/test/f1.err @@ -0,0 +1 @@ +.f f1.err diff --git a/text_cmds/ed/test/f2.err b/text_cmds/ed/test/f2.err new file mode 100644 index 0000000..26d1c5e --- /dev/null +++ b/text_cmds/ed/test/f2.err @@ -0,0 +1 @@ +ff1.err diff --git a/text_cmds/ed/test/g1.d b/text_cmds/ed/test/g1.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/g1.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/g1.err b/text_cmds/ed/test/g1.err new file mode 100644 index 0000000..f95ea22 --- /dev/null +++ b/text_cmds/ed/test/g1.err @@ -0,0 +1 @@ +g/./s //x/ diff --git a/text_cmds/ed/test/g1.r b/text_cmds/ed/test/g1.r new file mode 100644 index 0000000..578a44b --- /dev/null +++ b/text_cmds/ed/test/g1.r @@ -0,0 +1,15 @@ +line5 +help! world +order +line 4 +help! world +order +line 3 +help! world +order +line 2 +help! world +order +line 1 +help! world +order diff --git a/text_cmds/ed/test/g1.t b/text_cmds/ed/test/g1.t new file mode 100644 index 0000000..2d0b54f --- /dev/null +++ b/text_cmds/ed/test/g1.t @@ -0,0 +1,6 @@ +g/./m0 +g/./s/$/\ +hello world +g/hello /s/lo/p!/\ +a\ +order diff --git a/text_cmds/ed/test/g2.d b/text_cmds/ed/test/g2.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/g2.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/g2.err b/text_cmds/ed/test/g2.err new file mode 100644 index 0000000..0ff6a5a --- /dev/null +++ b/text_cmds/ed/test/g2.err @@ -0,0 +1 @@ +g//s/./x/ diff --git a/text_cmds/ed/test/g2.r b/text_cmds/ed/test/g2.r new file mode 100644 index 0000000..3b18e51 --- /dev/null +++ b/text_cmds/ed/test/g2.r @@ -0,0 +1 @@ +hello world diff --git a/text_cmds/ed/test/g2.t b/text_cmds/ed/test/g2.t new file mode 100644 index 0000000..831ee83 --- /dev/null +++ b/text_cmds/ed/test/g2.t @@ -0,0 +1,2 @@ +g/[2-4]/-1,+1c\ +hello world diff --git a/text_cmds/ed/test/g3.d b/text_cmds/ed/test/g3.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/g3.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/g3.err b/text_cmds/ed/test/g3.err new file mode 100644 index 0000000..01058d8 --- /dev/null +++ b/text_cmds/ed/test/g3.err @@ -0,0 +1 @@ +g diff --git a/text_cmds/ed/test/g3.r b/text_cmds/ed/test/g3.r new file mode 100644 index 0000000..cc6fbdd --- /dev/null +++ b/text_cmds/ed/test/g3.r @@ -0,0 +1,5 @@ +linc 3 +xine 1 +xine 2 +xinc 4 +xinc5 diff --git a/text_cmds/ed/test/g3.t b/text_cmds/ed/test/g3.t new file mode 100644 index 0000000..2d052a6 --- /dev/null +++ b/text_cmds/ed/test/g3.t @@ -0,0 +1,4 @@ +g/./s//x/\ +3m0 +g/./s/e/c/\ +2,3m1 diff --git a/text_cmds/ed/test/g4.d b/text_cmds/ed/test/g4.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/g4.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/g4.r b/text_cmds/ed/test/g4.r new file mode 100644 index 0000000..350882d --- /dev/null +++ b/text_cmds/ed/test/g4.r @@ -0,0 +1,7 @@ +hello +zine 1 +line 2 +line 3 +line 4 +line5 +world diff --git a/text_cmds/ed/test/g4.t b/text_cmds/ed/test/g4.t new file mode 100644 index 0000000..ec61816 --- /dev/null +++ b/text_cmds/ed/test/g4.t @@ -0,0 +1,13 @@ +g/./s/./x/\ +u\ +s/./y/\ +u\ +s/./z/\ +u +u +0a +hello +. +$a +world +. diff --git a/text_cmds/ed/test/g5.d b/text_cmds/ed/test/g5.d new file mode 100644 index 0000000..a92d664 --- /dev/null +++ b/text_cmds/ed/test/g5.d @@ -0,0 +1,3 @@ +line 1 +line 2 +line 3 diff --git a/text_cmds/ed/test/g5.r b/text_cmds/ed/test/g5.r new file mode 100644 index 0000000..15a2675 --- /dev/null +++ b/text_cmds/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/text_cmds/ed/test/g5.t b/text_cmds/ed/test/g5.t new file mode 100644 index 0000000..e213481 --- /dev/null +++ b/text_cmds/ed/test/g5.t @@ -0,0 +1,2 @@ +g/./1,3t$\ +1d diff --git a/text_cmds/ed/test/h.err b/text_cmds/ed/test/h.err new file mode 100644 index 0000000..a71e506 --- /dev/null +++ b/text_cmds/ed/test/h.err @@ -0,0 +1 @@ +.h diff --git a/text_cmds/ed/test/i.d b/text_cmds/ed/test/i.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/i.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/i.r b/text_cmds/ed/test/i.r new file mode 100644 index 0000000..5f27af0 --- /dev/null +++ b/text_cmds/ed/test/i.r @@ -0,0 +1,8 @@ +hello world +hello world! +line 1 +line 2 +line 3 +line 4 +hello world!! +line5 diff --git a/text_cmds/ed/test/i.t b/text_cmds/ed/test/i.t new file mode 100644 index 0000000..d1d9805 --- /dev/null +++ b/text_cmds/ed/test/i.t @@ -0,0 +1,9 @@ +1i +hello world +. +2i +hello world! +. +$i +hello world!! +. diff --git a/text_cmds/ed/test/i1.err b/text_cmds/ed/test/i1.err new file mode 100644 index 0000000..aaddede --- /dev/null +++ b/text_cmds/ed/test/i1.err @@ -0,0 +1,3 @@ +1,$i +hello world +. diff --git a/text_cmds/ed/test/i2.err b/text_cmds/ed/test/i2.err new file mode 100644 index 0000000..b63f5ac --- /dev/null +++ b/text_cmds/ed/test/i2.err @@ -0,0 +1,3 @@ +ii +hello world +. diff --git a/text_cmds/ed/test/i3.err b/text_cmds/ed/test/i3.err new file mode 100644 index 0000000..6d200c8 --- /dev/null +++ b/text_cmds/ed/test/i3.err @@ -0,0 +1,3 @@ +0i +hello world +. diff --git a/text_cmds/ed/test/j.d b/text_cmds/ed/test/j.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/j.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/j.r b/text_cmds/ed/test/j.r new file mode 100644 index 0000000..66f36a8 --- /dev/null +++ b/text_cmds/ed/test/j.r @@ -0,0 +1,4 @@ +line 1 +line 2line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/j.t b/text_cmds/ed/test/j.t new file mode 100644 index 0000000..9b5d28d --- /dev/null +++ b/text_cmds/ed/test/j.t @@ -0,0 +1,2 @@ +1,1j +2,3j diff --git a/text_cmds/ed/test/k.d b/text_cmds/ed/test/k.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/k.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/k.r b/text_cmds/ed/test/k.r new file mode 100644 index 0000000..eeb38db --- /dev/null +++ b/text_cmds/ed/test/k.r @@ -0,0 +1,5 @@ +line 3 +hello world +line 4 +line5 +line 2 diff --git a/text_cmds/ed/test/k.t b/text_cmds/ed/test/k.t new file mode 100644 index 0000000..53d588d --- /dev/null +++ b/text_cmds/ed/test/k.t @@ -0,0 +1,10 @@ +2ka +1d +'am$ +1ka +0a +hello world +. +'ad +u +'am0 diff --git a/text_cmds/ed/test/k1.err b/text_cmds/ed/test/k1.err new file mode 100644 index 0000000..eba1f3d --- /dev/null +++ b/text_cmds/ed/test/k1.err @@ -0,0 +1 @@ +1,$ka diff --git a/text_cmds/ed/test/k2.err b/text_cmds/ed/test/k2.err new file mode 100644 index 0000000..b34a18d --- /dev/null +++ b/text_cmds/ed/test/k2.err @@ -0,0 +1 @@ +kA diff --git a/text_cmds/ed/test/k3.err b/text_cmds/ed/test/k3.err new file mode 100644 index 0000000..70190c4 --- /dev/null +++ b/text_cmds/ed/test/k3.err @@ -0,0 +1 @@ +0ka diff --git a/text_cmds/ed/test/k4.err b/text_cmds/ed/test/k4.err new file mode 100644 index 0000000..3457642 --- /dev/null +++ b/text_cmds/ed/test/k4.err @@ -0,0 +1,6 @@ +a +hello +. +.ka +'ad +'ap diff --git a/text_cmds/ed/test/l.d b/text_cmds/ed/test/l.d new file mode 100644 index 0000000..e69de29 diff --git a/text_cmds/ed/test/l.r b/text_cmds/ed/test/l.r new file mode 100644 index 0000000..e69de29 diff --git a/text_cmds/ed/test/l.t b/text_cmds/ed/test/l.t new file mode 100644 index 0000000..e69de29 diff --git a/text_cmds/ed/test/m.d b/text_cmds/ed/test/m.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/m.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/m.err b/text_cmds/ed/test/m.err new file mode 100644 index 0000000..3aec4c3 --- /dev/null +++ b/text_cmds/ed/test/m.err @@ -0,0 +1,4 @@ +a +hello world +. +1,$m1 diff --git a/text_cmds/ed/test/m.r b/text_cmds/ed/test/m.r new file mode 100644 index 0000000..186cf54 --- /dev/null +++ b/text_cmds/ed/test/m.r @@ -0,0 +1,5 @@ +line5 +line 1 +line 2 +line 3 +line 4 diff --git a/text_cmds/ed/test/m.t b/text_cmds/ed/test/m.t new file mode 100644 index 0000000..c39c088 --- /dev/null +++ b/text_cmds/ed/test/m.t @@ -0,0 +1,7 @@ +1,2m$ +1,2m$ +1,2m$ +$m0 +$m0 +2,3m1 +2,3m3 diff --git a/text_cmds/ed/test/mkscripts.sh b/text_cmds/ed/test/mkscripts.sh new file mode 100755 index 0000000..2d1a06f --- /dev/null +++ b/text_cmds/ed/test/mkscripts.sh @@ -0,0 +1,75 @@ +#!/bin/sh - +# This script generates ed test scripts (.ed) from .t files +# +# $FreeBSD: src/bin/ed/test/mkscripts.sh,v 1.6 1999/08/27 23:14:20 peter Exp $ + +PATH="/bin:/usr/bin:/usr/local/bin/:." +ED=$1 +[ ! -x $ED ] && { echo "$ED: cannot execute"; exit 1; } + +for i in *.t; do +# base=${i%.*} +# base=`echo $i | sed 's/\..*//'` +# base=`expr $i : '\([^.]*\)'` +# ( +# echo "#!/bin/sh -" +# echo "$ED - <<\EOT" +# echo "r $base.d" +# cat $i +# echo "w $base.o" +# echo EOT +# ) >$base.ed +# chmod +x $base.ed +# The following is pretty ugly way of doing the above, and not appropriate +# use of ed but the point is that it can be done... + base=`$ED - \!"echo $i" <<-EOF + s/\..* + EOF` + $ED - <<-EOF + a + #!/bin/sh - + $ED - <<\EOT + H + r $base.d + w $base.o + EOT + . + -2r $i + w $base.ed + !chmod +x $base.ed + EOF +done + +for i in *.err; do +# base=${i%.*} +# base=`echo $i | sed 's/\..*//'` +# base=`expr $i : '\([^.]*\)'` +# ( +# echo "#!/bin/sh -" +# echo "$ED - <<\EOT" +# echo H +# echo "r $base.err" +# cat $i +# echo "w $base.o" +# echo EOT +# ) >$base-err.ed +# chmod +x $base-err.ed +# The following is pretty ugly way of doing the above, and not appropriate +# use of ed but the point is that it can be done... + base=`$ED - \!"echo $i" <<-EOF + s/\..* + EOF` + $ED - <<-EOF + a + #!/bin/sh - + $ED - <<\EOT + H + r $base.err + w $base.o + EOT + . + -2r $i + w ${base}.red + !chmod +x ${base}.red + EOF +done diff --git a/text_cmds/ed/test/n.d b/text_cmds/ed/test/n.d new file mode 100644 index 0000000..e69de29 diff --git a/text_cmds/ed/test/n.r b/text_cmds/ed/test/n.r new file mode 100644 index 0000000..e69de29 diff --git a/text_cmds/ed/test/n.t b/text_cmds/ed/test/n.t new file mode 100644 index 0000000..e69de29 diff --git a/text_cmds/ed/test/nl.err b/text_cmds/ed/test/nl.err new file mode 100644 index 0000000..8949a85 --- /dev/null +++ b/text_cmds/ed/test/nl.err @@ -0,0 +1 @@ +,1 diff --git a/text_cmds/ed/test/nl1.d b/text_cmds/ed/test/nl1.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/nl1.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/nl1.r b/text_cmds/ed/test/nl1.r new file mode 100644 index 0000000..9d8854c --- /dev/null +++ b/text_cmds/ed/test/nl1.r @@ -0,0 +1,8 @@ + + +hello world +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/nl1.t b/text_cmds/ed/test/nl1.t new file mode 100644 index 0000000..ea192e9 --- /dev/null +++ b/text_cmds/ed/test/nl1.t @@ -0,0 +1,8 @@ +1 + + +0a + + +hello world +. diff --git a/text_cmds/ed/test/nl2.d b/text_cmds/ed/test/nl2.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/nl2.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/nl2.r b/text_cmds/ed/test/nl2.r new file mode 100644 index 0000000..fe99e41 --- /dev/null +++ b/text_cmds/ed/test/nl2.r @@ -0,0 +1,6 @@ +line 1 +line 2 +line 3 +line 4 +line5 +hello world diff --git a/text_cmds/ed/test/nl2.t b/text_cmds/ed/test/nl2.t new file mode 100644 index 0000000..73fd27b --- /dev/null +++ b/text_cmds/ed/test/nl2.t @@ -0,0 +1,4 @@ +a +hello world +. +0;/./ diff --git a/text_cmds/ed/test/p.d b/text_cmds/ed/test/p.d new file mode 100644 index 0000000..e69de29 diff --git a/text_cmds/ed/test/p.r b/text_cmds/ed/test/p.r new file mode 100644 index 0000000..e69de29 diff --git a/text_cmds/ed/test/p.t b/text_cmds/ed/test/p.t new file mode 100644 index 0000000..e69de29 diff --git a/text_cmds/ed/test/q.d b/text_cmds/ed/test/q.d new file mode 100644 index 0000000..e69de29 diff --git a/text_cmds/ed/test/q.r b/text_cmds/ed/test/q.r new file mode 100644 index 0000000..e69de29 diff --git a/text_cmds/ed/test/q.t b/text_cmds/ed/test/q.t new file mode 100644 index 0000000..123a2c8 --- /dev/null +++ b/text_cmds/ed/test/q.t @@ -0,0 +1,5 @@ +w q.o +a +hello +. +q diff --git a/text_cmds/ed/test/q1.err b/text_cmds/ed/test/q1.err new file mode 100644 index 0000000..0a7e178 --- /dev/null +++ b/text_cmds/ed/test/q1.err @@ -0,0 +1 @@ +.q diff --git a/text_cmds/ed/test/r1.d b/text_cmds/ed/test/r1.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/r1.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/r1.err b/text_cmds/ed/test/r1.err new file mode 100644 index 0000000..269aa7c --- /dev/null +++ b/text_cmds/ed/test/r1.err @@ -0,0 +1 @@ +1,$r r1.err diff --git a/text_cmds/ed/test/r1.r b/text_cmds/ed/test/r1.r new file mode 100644 index 0000000..a3ff506 --- /dev/null +++ b/text_cmds/ed/test/r1.r @@ -0,0 +1,7 @@ +line 1 +hello world +line 2 +line 3 +line 4 +line5 +hello world diff --git a/text_cmds/ed/test/r1.t b/text_cmds/ed/test/r1.t new file mode 100644 index 0000000..d787a92 --- /dev/null +++ b/text_cmds/ed/test/r1.t @@ -0,0 +1,3 @@ +1;r !echo hello world +1 +r !echo hello world diff --git a/text_cmds/ed/test/r2.d b/text_cmds/ed/test/r2.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/r2.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/r2.err b/text_cmds/ed/test/r2.err new file mode 100644 index 0000000..1c44fa3 --- /dev/null +++ b/text_cmds/ed/test/r2.err @@ -0,0 +1 @@ +r a-good-book diff --git a/text_cmds/ed/test/r2.r b/text_cmds/ed/test/r2.r new file mode 100644 index 0000000..ac152ba --- /dev/null +++ b/text_cmds/ed/test/r2.r @@ -0,0 +1,10 @@ +line 1 +line 2 +line 3 +line 4 +line5 +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/r2.t b/text_cmds/ed/test/r2.t new file mode 100644 index 0000000..4286f42 --- /dev/null +++ b/text_cmds/ed/test/r2.t @@ -0,0 +1 @@ +r diff --git a/text_cmds/ed/test/r3.d b/text_cmds/ed/test/r3.d new file mode 100644 index 0000000..593eec6 --- /dev/null +++ b/text_cmds/ed/test/r3.d @@ -0,0 +1 @@ +r r3.t diff --git a/text_cmds/ed/test/r3.r b/text_cmds/ed/test/r3.r new file mode 100644 index 0000000..86d5f90 --- /dev/null +++ b/text_cmds/ed/test/r3.r @@ -0,0 +1,2 @@ +r r3.t +r r3.t diff --git a/text_cmds/ed/test/r3.t b/text_cmds/ed/test/r3.t new file mode 100644 index 0000000..593eec6 --- /dev/null +++ b/text_cmds/ed/test/r3.t @@ -0,0 +1 @@ +r r3.t diff --git a/text_cmds/ed/test/s1.d b/text_cmds/ed/test/s1.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/s1.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/s1.err b/text_cmds/ed/test/s1.err new file mode 100644 index 0000000..d7ca0cf --- /dev/null +++ b/text_cmds/ed/test/s1.err @@ -0,0 +1 @@ +s . x diff --git a/text_cmds/ed/test/s1.r b/text_cmds/ed/test/s1.r new file mode 100644 index 0000000..4eb0980 --- /dev/null +++ b/text_cmds/ed/test/s1.r @@ -0,0 +1,5 @@ +liene 1 +(liene) (2) +(liene) (3) +liene (4) +(()liene5) diff --git a/text_cmds/ed/test/s1.t b/text_cmds/ed/test/s1.t new file mode 100644 index 0000000..b0028bb --- /dev/null +++ b/text_cmds/ed/test/s1.t @@ -0,0 +1,6 @@ +s/\([^ ][^ ]*\)/(\1)/g +2s +/3/s +/\(4\)/sr +/\(.\)/srg +%s/i/&e/ diff --git a/text_cmds/ed/test/s10.err b/text_cmds/ed/test/s10.err new file mode 100644 index 0000000..0d8d83d --- /dev/null +++ b/text_cmds/ed/test/s10.err @@ -0,0 +1,4 @@ +a +hello +. +s/[h[.]/x/ diff --git a/text_cmds/ed/test/s2.d b/text_cmds/ed/test/s2.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/s2.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/s2.err b/text_cmds/ed/test/s2.err new file mode 100644 index 0000000..b5c851d --- /dev/null +++ b/text_cmds/ed/test/s2.err @@ -0,0 +1,4 @@ +a +a +. +s/x*/a/g diff --git a/text_cmds/ed/test/s2.r b/text_cmds/ed/test/s2.r new file mode 100644 index 0000000..ca305c8 --- /dev/null +++ b/text_cmds/ed/test/s2.r @@ -0,0 +1,5 @@ +li(n)e 1 +i(n)e 200 +li(n)e 3 +li(n)e 4 +li(n)e500 diff --git a/text_cmds/ed/test/s2.t b/text_cmds/ed/test/s2.t new file mode 100644 index 0000000..f365849 --- /dev/null +++ b/text_cmds/ed/test/s2.t @@ -0,0 +1,4 @@ +,s/./(&)/3 +s/$/00 +2s//%/g +s/^l diff --git a/text_cmds/ed/test/s3.d b/text_cmds/ed/test/s3.d new file mode 100644 index 0000000..e69de29 diff --git a/text_cmds/ed/test/s3.err b/text_cmds/ed/test/s3.err new file mode 100644 index 0000000..d68c7d0 --- /dev/null +++ b/text_cmds/ed/test/s3.err @@ -0,0 +1 @@ +s/[xyx/a/ diff --git a/text_cmds/ed/test/s3.r b/text_cmds/ed/test/s3.r new file mode 100644 index 0000000..d6cada2 --- /dev/null +++ b/text_cmds/ed/test/s3.r @@ -0,0 +1 @@ +hello world diff --git a/text_cmds/ed/test/s3.t b/text_cmds/ed/test/s3.t new file mode 100644 index 0000000..fbf8803 --- /dev/null +++ b/text_cmds/ed/test/s3.t @@ -0,0 +1,6 @@ +a +hello/[]world +. +s/[/]/ / +s/[[:digit:][]/ / +s/[]]/ / diff --git a/text_cmds/ed/test/s4.err b/text_cmds/ed/test/s4.err new file mode 100644 index 0000000..35b609f --- /dev/null +++ b/text_cmds/ed/test/s4.err @@ -0,0 +1 @@ +s/\a\b\c/xyz/ diff --git a/text_cmds/ed/test/s5.err b/text_cmds/ed/test/s5.err new file mode 100644 index 0000000..89104c5 --- /dev/null +++ b/text_cmds/ed/test/s5.err @@ -0,0 +1 @@ +s//xyz/ diff --git a/text_cmds/ed/test/s6.err b/text_cmds/ed/test/s6.err new file mode 100644 index 0000000..b478595 --- /dev/null +++ b/text_cmds/ed/test/s6.err @@ -0,0 +1 @@ +s diff --git a/text_cmds/ed/test/s7.err b/text_cmds/ed/test/s7.err new file mode 100644 index 0000000..30ba4fd --- /dev/null +++ b/text_cmds/ed/test/s7.err @@ -0,0 +1,5 @@ +a +hello world +. +/./ +sr diff --git a/text_cmds/ed/test/s8.err b/text_cmds/ed/test/s8.err new file mode 100644 index 0000000..5665767 --- /dev/null +++ b/text_cmds/ed/test/s8.err @@ -0,0 +1,4 @@ +a +hello +. +s/[h[=]/x/ diff --git a/text_cmds/ed/test/s9.err b/text_cmds/ed/test/s9.err new file mode 100644 index 0000000..1ff16dd --- /dev/null +++ b/text_cmds/ed/test/s9.err @@ -0,0 +1,4 @@ +a +hello +. +s/[h[:]/x/ diff --git a/text_cmds/ed/test/t.d b/text_cmds/ed/test/t.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/t.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/t.r b/text_cmds/ed/test/t.r new file mode 100644 index 0000000..2b28547 --- /dev/null +++ b/text_cmds/ed/test/t.r @@ -0,0 +1,16 @@ +line 1 +line 1 +line 1 +line 2 +line 2 +line 3 +line 4 +line5 +line 1 +line 1 +line 1 +line 2 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/t1.d b/text_cmds/ed/test/t1.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/t1.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/t1.err b/text_cmds/ed/test/t1.err new file mode 100644 index 0000000..c49c556 --- /dev/null +++ b/text_cmds/ed/test/t1.err @@ -0,0 +1 @@ +tt diff --git a/text_cmds/ed/test/t1.r b/text_cmds/ed/test/t1.r new file mode 100644 index 0000000..2b28547 --- /dev/null +++ b/text_cmds/ed/test/t1.r @@ -0,0 +1,16 @@ +line 1 +line 1 +line 1 +line 2 +line 2 +line 3 +line 4 +line5 +line 1 +line 1 +line 1 +line 2 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/t1.t b/text_cmds/ed/test/t1.t new file mode 100644 index 0000000..6b66163 --- /dev/null +++ b/text_cmds/ed/test/t1.t @@ -0,0 +1,3 @@ +1t0 +2,3t2 +,t$ diff --git a/text_cmds/ed/test/t2.d b/text_cmds/ed/test/t2.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/t2.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/t2.err b/text_cmds/ed/test/t2.err new file mode 100644 index 0000000..c202051 --- /dev/null +++ b/text_cmds/ed/test/t2.err @@ -0,0 +1 @@ +t0;-1 diff --git a/text_cmds/ed/test/t2.r b/text_cmds/ed/test/t2.r new file mode 100644 index 0000000..0c75ff5 --- /dev/null +++ b/text_cmds/ed/test/t2.r @@ -0,0 +1,6 @@ +line 1 +line5 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/t2.t b/text_cmds/ed/test/t2.t new file mode 100644 index 0000000..5175abd --- /dev/null +++ b/text_cmds/ed/test/t2.t @@ -0,0 +1 @@ +t0;/./ diff --git a/text_cmds/ed/test/u.d b/text_cmds/ed/test/u.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/u.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/u.err b/text_cmds/ed/test/u.err new file mode 100644 index 0000000..caa1ba1 --- /dev/null +++ b/text_cmds/ed/test/u.err @@ -0,0 +1 @@ +.u diff --git a/text_cmds/ed/test/u.r b/text_cmds/ed/test/u.r new file mode 100644 index 0000000..ad558d8 --- /dev/null +++ b/text_cmds/ed/test/u.r @@ -0,0 +1,9 @@ +line 1 +hello +hello world!! +line 2 +line 3 +line 4 +line5 +hello +hello world!! diff --git a/text_cmds/ed/test/u.t b/text_cmds/ed/test/u.t new file mode 100644 index 0000000..131cb6e --- /dev/null +++ b/text_cmds/ed/test/u.t @@ -0,0 +1,31 @@ +1;r u.t +u +a +hello +world +. +g/./s//x/\ +a\ +hello\ +world +u +u +u +a +hello world! +. +u +1,$d +u +2,3d +u +c +hello world!! +. +u +u +-1;.,+1j +u +u +u +.,+1t$ diff --git a/text_cmds/ed/test/v.d b/text_cmds/ed/test/v.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/v.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/v.r b/text_cmds/ed/test/v.r new file mode 100644 index 0000000..714db63 --- /dev/null +++ b/text_cmds/ed/test/v.r @@ -0,0 +1,11 @@ +line5 +order +hello world +line 1 +order +line 2 +order +line 3 +order +line 4 +order diff --git a/text_cmds/ed/test/v.t b/text_cmds/ed/test/v.t new file mode 100644 index 0000000..608a77f --- /dev/null +++ b/text_cmds/ed/test/v.t @@ -0,0 +1,6 @@ +v/[ ]/m0 +v/[ ]/s/$/\ +hello world +v/hello /s/lo/p!/\ +a\ +order diff --git a/text_cmds/ed/test/w.d b/text_cmds/ed/test/w.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/text_cmds/ed/test/w.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/w.r b/text_cmds/ed/test/w.r new file mode 100644 index 0000000..ac152ba --- /dev/null +++ b/text_cmds/ed/test/w.r @@ -0,0 +1,10 @@ +line 1 +line 2 +line 3 +line 4 +line5 +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/text_cmds/ed/test/w.t b/text_cmds/ed/test/w.t new file mode 100644 index 0000000..c2e18bd --- /dev/null +++ b/text_cmds/ed/test/w.t @@ -0,0 +1,2 @@ +w !cat >\!.z +r \!.z diff --git a/text_cmds/ed/test/w1.err b/text_cmds/ed/test/w1.err new file mode 100644 index 0000000..e2c8a60 --- /dev/null +++ b/text_cmds/ed/test/w1.err @@ -0,0 +1 @@ +w /to/some/far-away/place diff --git a/text_cmds/ed/test/w2.err b/text_cmds/ed/test/w2.err new file mode 100644 index 0000000..9daf89c --- /dev/null +++ b/text_cmds/ed/test/w2.err @@ -0,0 +1 @@ +ww.o diff --git a/text_cmds/ed/test/w3.err b/text_cmds/ed/test/w3.err new file mode 100644 index 0000000..39bbf4c --- /dev/null +++ b/text_cmds/ed/test/w3.err @@ -0,0 +1 @@ +wqp w.o diff --git a/text_cmds/ed/test/x.err b/text_cmds/ed/test/x.err new file mode 100644 index 0000000..0953f01 --- /dev/null +++ b/text_cmds/ed/test/x.err @@ -0,0 +1 @@ +.x diff --git a/text_cmds/ed/test/z.err b/text_cmds/ed/test/z.err new file mode 100644 index 0000000..6a51a2d --- /dev/null +++ b/text_cmds/ed/test/z.err @@ -0,0 +1,2 @@ +z +z diff --git a/text_cmds/ed/undo.c b/text_cmds/ed/undo.c new file mode 100644 index 0000000..2837495 --- /dev/null +++ b/text_cmds/ed/undo.c @@ -0,0 +1,150 @@ +/* undo.c: This file contains the undo routines for the ed line editor */ +/*- + * Copyright (c) 1993 Andrew Moore, Talke Studio. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: src/bin/ed/undo.c,v 1.12 2002/06/30 05:13:53 obrien Exp $"); + +#include "ed.h" + + +#define USIZE 100 /* undo stack size */ +undo_t *ustack = NULL; /* undo stack */ +long usize = 0; /* stack size variable */ +long u_p = 0; /* undo stack pointer */ + +/* push_undo_stack: return pointer to initialized undo node */ +undo_t * +push_undo_stack(int type, long from, long to) +{ + undo_t *t; + +#if defined(sun) || defined(NO_REALLOC_NULL) + if (ustack == NULL && + (ustack = (undo_t *) malloc((usize = USIZE) * sizeof(undo_t))) == NULL) { + fprintf(stderr, "%s\n", strerror(errno)); + 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)); + errmsg = "out of memory"; + clear_undo_stack(); + free(ustack); + ustack = NULL; + usize = 0; + return NULL; +} + + +/* USWAP: swap undo nodes */ +#define USWAP(x,y) { \ + undo_t utmp; \ + utmp = x, x = y, y = utmp; \ +} + + +long u_current_addr = -1; /* if >= 0, undo enabled */ +long u_addr_last = -1; /* if >= 0, undo enabled */ + +/* pop_undo_stack: undo last change to the editor buffer */ +int +pop_undo_stack(void) +{ + long n; + long o_current_addr = current_addr; + long o_addr_last = addr_last; + + if (u_current_addr == -1 || u_addr_last == -1) { + errmsg = "nothing to undo"; + return ERR; + } else if (u_p) + modified = 1; + get_addressed_line_node(0); /* this get_addressed_line_node last! */ + SPL1(); + for (n = u_p; n-- > 0;) { + switch(ustack[n].type) { + case UADD: + REQUE(ustack[n].h->q_back, ustack[n].t->q_forw); + break; + case UDEL: + REQUE(ustack[n].h->q_back, ustack[n].h); + REQUE(ustack[n].t, ustack[n].t->q_forw); + break; + case UMOV: + case VMOV: + REQUE(ustack[n - 1].h, ustack[n].h->q_forw); + REQUE(ustack[n].t->q_back, ustack[n - 1].t); + REQUE(ustack[n].h, ustack[n].t); + n--; + break; + default: + /*NOTREACHED*/ + ; + } + ustack[n].type ^= 1; + } + /* reverse undo stack order */ + for (n = u_p; n-- > (u_p + 1)/ 2;) + USWAP(ustack[n], ustack[u_p - 1 - n]); + if (isglobal) + clear_active_list(); + current_addr = u_current_addr, u_current_addr = o_current_addr; + addr_last = u_addr_last, u_addr_last = o_addr_last; + SPL0(); + return 0; +} + + +/* clear_undo_stack: clear the undo stack */ +void +clear_undo_stack(void) +{ + line_t *lp, *ep, *tl; + + while (u_p--) + if (ustack[u_p].type == UDEL) { + ep = ustack[u_p].t->q_forw; + for (lp = ustack[u_p].h; lp != ep; lp = tl) { + unmark_line_node(lp); + tl = lp->q_forw; + free(lp); + } + } + u_p = 0; + u_current_addr = current_addr; + u_addr_last = addr_last; +} -- cgit v1.2.3-56-ge451