---
ed.1 | 204 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
ed.c | 250 ++++++++++++++++++++++++++++++++++---------------------------------
2 files changed, 331 insertions(+), 123 deletions(-)
diff --git a/ed.1 b/ed.1
index 93e3012..520ac5f 100644
--- a/ed.1
+++ b/ed.1
_AT_@ -6,4 +6,206 @@
.Nd text editor
.Sh SYNOPSIS
.Nm
-is the standard text editor.
+.Op Fl s
+.Op Fl p Ar string
+.Op Ar file
+.Sh DESCRIPTION
+.Nm
+is the standard text editor. It performs line-oriented operations on a buffer;
+The buffer's contents are manipulated in command mode and text is written to the
+buffer in input mode. Command mode is the default. To exit input mode enter a
+dot ('.') on a line of its own.
+
+If
+.Nm
+is invoked with a file as an argument, it will simulate an edit command and read
+the file's contents into a buffer. Changes to this buffer are local to
+.Nm
+until a write command is given.
+
+.Nm
+uses the basic regular expression syntax and allows any character but space and
+newline to be used as a delimiter in regular expressions.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl s
+Suppress diagnostic messages
+.It Fl p Ar string
+Use
+.Ar string
+as a prompt when in command mode
+.El
+.Sh EXTENDED DESCRIPTION
+.Ss Addresses
+Commands operate on addresses. Addresses are used to refer to lines
+within the buffer. Address ranges may have spaces before and after the separator.
+Unless otherwise specified, 0 is an invalid address. The following symbols are
+valid addresses:
+.Bl -tag -width Ds
+.It n
+The nth line.
+.It .
+The current line, or "dot".
+.It $
+The last line.
+.It +
+The next line.
+.It +n
+The nth next line.
+.It ^ or -
+The previous line.
+.It ^n or -n
+The nth previous line.
+.It x,y
+The range of lines from x to y. The default value of x is 1, and the default
+value of y is $.
+.It x;y
+As above, except that the current line is set to x. Omitting x in this case uses
+the current line as the default value.
+.It /re/
+The next line matching re.
+.It ?re?
+The last line matching re.
+.It 'c
+The line marked by c. See k below.
+.El
+.Ss Commands.
+.Nm
+expects to see one command per line, with the following exception: commands may
+be suffixed with either a list, number, or print command. These suffixed
+commands are run after the command they're suffixed to has executed.
+
+The following is the list of commands that
+.Nm
+knows about. The parentheses contain the default addresses that a command uses.
+.Bl -tag -width Ds
+.It (.)a
+Append text after the addressed line. The dot is set to the last line
+entered. If no text was entered, the dot is set to the addressed line. An
+address of 0 appends to the start of the buffer.
+.It (.,.)c
+Delete the addressed lines and then accept input to replace them. The dot
+is set to the last line entered. If no text was entered, the dot is set to
+the line before the deleted lines.
+.It (.,.)d
+Delete the addressed lines. If there is a line after the deleted range, the
+dot is set to it. Otherwise, the dot is set to the line before the deleted range.
+.It e Ar file
+Delete the contents of the buffer and load in
+.Ar file
+for editing, printing the bytes read to standard output. If no filename is
+given,
+.Nm
+uses the currently remembered filename. The remembered filename is set to
+.Ar file
+for later use.
+.It E Ar file
+As above, but without warning if the current buffer has unsaved changes.
+.It f Ar file
+Set the currently remembered filename to
+.Ar
+file
+, or print the currently remembered filename if
+.Ar
+file is omitted.
+.It (1,$)g/re/command
+Apply command to lines matching re. The dot is set to the matching line before
+command is executed. When each matching line has been operated on, the dot is
+set to the last line operated on. If no lines match then the dot remains
+unchanged. The command used may not be g, G, v, or V.
+.It (1,$)G/re/
+Interactively edit the range of line addresses that match re. The dot is set to
+the matching line and printed before a command is input. When each matching line
+has been operated on, the dot is set to the last line operated on. If no lines
+match then the dot remains unchanged. The command used may not be a, c, i, g,
+G, v, or V.
+.It h
+Print the reason for the most recent error.
+.It H
+Toggle error explanations. If on, the above behaviour is produced on all
+subsequent errors.
+.It (.)i
+Insert text into the buffer before the addressed line. The dot is set to the
+last line entered. If no text was entered, the dot is set to the addressed line
+.It (.,.+1)j
+Join two lines together. If only one address is given, nothing happens. The dot
+is set to the newly joined line.
+.It (.)kc
+Mark the line with the lower case character c. The dot is unchanged.
+.It (.,.)l
+Unambiguously print the addressed lines. The dot is set to the last line written.
+.It (.,.)m(.)
+Move lines in the buffer to the line address on the right hand side. An address
+of 0 on the right hand side moves to the start of the buffer. The dot is set to
+the last line moved.
+.It (.,.)n
+Print the addressed lines and their numbers. The dot is set to the last line
+printed.
+.It (.,.)p
+Print the addressed lines. The dot is set to the last line printed.
+.It P
+Toggle the prompt. Defaults to off, but is switched on if the -p flag is used.
+.It q
+Quit
+.Nm
+, warning if there are unsaved changes.
+.It Q
+As above, but without warning if the current buffer has unsaved changes.
+.It ($)r Ar file
+Read in
+.Ar file
+and append it to the current buffer, printing the bytes read to standard output.
+The currently remembered filename isn't changed unless it's empty. An address of
+0 reads the file into the start of the buffer.
+.It (.,.)s/re/replacement/flags
+Substitute re for replacement in lines matching re. An & within replacement is
+replaced with the whole string matched by re. Backrefs can be used with the form
+\\n, where n is a positive non-zero integer. When % is the only character in
+replacement, it is substituted for the replacement string from the last
+substitute command. If a newline is part of replacement then the matched string
+is split into two lines; this cannot be done as part of a g or v command. If
+flags contains an integer n, then the nth match is replaced. If flags contains
+g, all matches are replaced. The dot is set to the last line matched.
+.It (.,.)t(.)
+As m, but copying instead of moving. The dot is set to the last line added.
+.It u
+Undo the last change. The dot is set to whatever it was before the undone
+command was performed.
+.It (1.$)v/re/command
+As with g, but operating on lines that don't match re.
+.It (1.$)V/re/
+As with G, but operating on lines that don't match re.
+.It (1,$)w Ar file
+Write the addressed lines to
+.Ar file
+, overwriting its previous contents if the file exists, and print the number of
+bytes written. If no filename is given the currently remembered filename will be
+used instead. The dot is unchanged.
+.It (1,$)W Ar file
+As above, but instead of overwriting the contents of
+.Ar file
+the addressed lines are appended to
+.Ar file
+instead.
+.It (.+1)\\n
+Print the addressed line. Sets the dot to that line.
+.It ($)=
+Print the line number of the addressed line. The dot is unchanged.
+.It &
+Repeat the last command.
+.It ! Ar command
+Execute
+.Ar command
+using sh. If the first character of
+.Ar command
+is '!' then it is replaced with the text of the previous command. An unescaped %
+is replaced with the currently remembered filename. ! does not process escape
+characters. When
+.Ar command
+returns a '!' is printed. The dot is unchanged.
+.El
+.Sh BUGS
+g and v operate on single commands rather than lists delimited with '\\'.
+.Sh SEE ALSO
+.Xr sed 1
+.Xr regexp 3
diff --git a/ed.c b/ed.c
index 184ed30..496b24f 100644
--- a/ed.c
+++ b/ed.c
_AT_@ -119,7 +119,8 @@ addchar(char c, char *t, size_t *capacity, size_t *size)
if (siz >= cap &&
(cap > SIZE_MAX - LINESIZE ||
(t = realloc(t, cap += LINESIZE)) == NULL))
- error("out of memory");
+ error("out of memory");
+
t[siz++] = c;
*size = siz;
*capacity = cap;
_AT_@ -297,13 +298,17 @@ undo(void)
}
static void
-inject(char *s)
+inject(char *s, int join)
{
int off, k, begin, end;
- begin = getindex(curln);
- end = getindex(nextln(curln));
-
+ if (join) {
+ begin = getindex(curln-1);
+ end = getindex(nextln(curln-1));
+ } else {
+ begin = getindex(curln);
+ end = getindex(nextln(curln));
+ }
while (*s) {
k = makeline(s, &off);
s += off;
_AT_@ -634,7 +639,7 @@ doread(char *fname)
s[n-1] = '\n';
s[n] = '\0';
}
- inject(s);
+ inject(s, 0);
}
if (optdiag)
printf("%zu\n", cnt);
_AT_@ -751,7 +756,7 @@ append(int num)
while (getline(&s, &len, stdin) > 0) {
if (*s == '.' && s[1] == '\n')
break;
- inject(s);
+ inject(s, 0);
}
free(s);
}
_AT_@ -767,7 +772,7 @@ delete(int from, int to)
lfrom = getindex(prevln(from));
lto = getindex(nextln(to));
lastln -= to - from + 1;
- curln = (from > lastln) ? lastln : from;;
+ curln = (from > lastln) ? lastln : from;
relink(lto, lfrom, lto, lfrom);
}
_AT_@ -803,18 +808,20 @@ join(void)
int i;
char *t, c;
size_t len = 0, cap = 0;
- static char *s;
+ char *s;
- free(s);
for (s = NULL, i = line1; i <= line2; i = nextln(i)) {
for (t = gettxt(i); (c = *t) != '\n'; ++t)
s = addchar(*t, s, &cap, &len);
+ /* prevent infinite loop when there are only two lines */
+ if (i == line2)
+ break;
}
s = addchar('\n', s, &cap, &len);
s = addchar('\0', s, &cap, &len);
delete(line1, line2);
- inject(s);
+ inject(s, 1);
free(s);
}
_AT_@ -841,7 +848,7 @@ copy(int where)
curln = where;
for (i = line1; i <= line2; ++i)
- inject(gettxt(i));
+ inject(gettxt(i), 0);
}
static void
_AT_@ -1021,7 +1028,7 @@ subline(int num, int nth)
addpost(&s, &cap, &siz);
delete(num, num);
curln = prevln(num);
- inject(s);
+ inject(s, 0);
}
static void
_AT_@ -1170,9 +1177,8 @@ repeat:
case 'j':
chkprint(1);
deflines(curln, curln+1);
- if (!line1)
- goto bad_address;
- join();
+ if (line1 != line2)
+ join();
break;
case 'z':
if (nlines > 1)
_AT_@ -1299,126 +1305,126 @@ chkglobal(void)
return 1;
}
-static void
-doglobal(void)
-{
- int i, k;
+ static void
+ doglobal(void)
+ {
+ int i, k;
- skipblank();
- cmdsiz = 0;
- gflag = 1;
- if (uflag)
- chkprint(0);
-
- for (i = 1; i <= lastln; i++) {
- k = getindex(i);
- if (!zero[k].global)
- continue;
- curln = i;
- nlines = 0;
- if (uflag) {
- line1 = line2 = i;
- pflag = 0;
- doprint();
+ skipblank();
+ cmdsiz = 0;
+ gflag = 1;
+ if (uflag)
+ chkprint(0);
+
+ for (i = 1; i <= lastln; i++) {
+ k = getindex(i);
+ if (!zero[k].global)
+ continue;
+ curln = i;
+ nlines = 0;
+ if (uflag) {
+ line1 = line2 = i;
+ pflag = 0;
+ doprint();
+ }
+ docmd();
}
- docmd();
+ discard(); /* cover the case of not matching anything */
}
- discard(); /* cover the case of not matching anything */
-}
-static void
-usage(void)
-{
- eprintf("usage: %s [-s] [-p] [file]\n", argv0);
-}
-
-static void
-sigintr(int n)
-{
- signal(SIGINT, sigintr);
- error("interrupt");
-}
+ static void
+ usage(void)
+ {
+ eprintf("usage: %s [-s] [-p] [file]\n", argv0);
+ }
-static void
-sighup(int dummy)
-{
- int n;
- char *home = getenv("HOME"), fname[FILENAME_MAX];
+ static void
+ sigintr(int n)
+ {
+ signal(SIGINT, sigintr);
+ error("interrupt");
+ }
- if (modflag) {
- line1 = nextln(0);
- line2 = lastln;
- if (!setjmp(savesp)) {
- dowrite("ed.hup", 1);
- } else if (home && !setjmp(savesp)) {
- n = snprintf(fname,
- sizeof(fname), "%s/%s", home, "ed.hup");
- if (n < sizeof(fname) && n > 0)
- dowrite(fname, 1);
+ static void
+ sighup(int dummy)
+ {
+ int n;
+ char *home = getenv("HOME"), fname[FILENAME_MAX];
+
+ if (modflag) {
+ line1 = nextln(0);
+ line2 = lastln;
+ if (!setjmp(savesp)) {
+ dowrite("ed.hup", 1);
+ } else if (home && !setjmp(savesp)) {
+ n = snprintf(fname,
+ sizeof(fname), "%s/%s", home, "ed.hup");
+ if (n < sizeof(fname) && n > 0)
+ dowrite(fname, 1);
+ }
}
+ exstatus = 1;
+ quit();
}
- exstatus = 1;
- quit();
-}
-static void
-edit(void)
-{
- setjmp(savesp);
- for (;;) {
- newcmd = 1;
- ocurln = curln;
- cmdsiz = 0;
- repidx = -1;
- if (optprompt)
- fputs(prompt, stdout);
- getlst();
- chkglobal() ? doglobal() : docmd();
+ static void
+ edit(void)
+ {
+ setjmp(savesp);
+ for (;;) {
+ newcmd = 1;
+ ocurln = curln;
+ cmdsiz = 0;
+ repidx = -1;
+ if (optprompt)
+ fputs(prompt, stdout);
+ getlst();
+ chkglobal() ? doglobal() : docmd();
+ }
}
-}
-static void
-init(char *fname)
-{
- size_t len;
+ static void
+ init(char *fname)
+ {
+ size_t len;
- if (setjmp(savesp))
- return;
- setscratch();
- if (!fname)
- return;
- if ((len = strlen(fname)) >= FILENAME_MAX || len == 0)
- error("incorrect filename");
- memcpy(savfname, fname, len);
- doread(fname);
- clearundo();
-}
+ if (setjmp(savesp))
+ return;
+ setscratch();
+ if (!fname)
+ return;
+ if ((len = strlen(fname)) >= FILENAME_MAX || len == 0)
+ error("incorrect filename");
+ memcpy(savfname, fname, len);
+ doread(fname);
+ clearundo();
+ }
-int
-main(int argc, char *argv[])
-{
- ARGBEGIN {
- case 'p':
- prompt = EARGF(usage());
- optprompt = 1;
- break;
- case 's':
- optdiag = 0;
- break;
- default:
- usage();
- } ARGEND
+ int
+ main(int argc, char *argv[])
+ {
+ ARGBEGIN {
+ case 'p':
+ prompt = EARGF(usage());
+ optprompt = 1;
+ break;
+ case 's':
+ optdiag = 0;
+ break;
+ default:
+ usage();
+ } ARGEND
- if (argc > 1)
- usage();
+ if (argc > 1)
+ usage();
- signal(SIGINT, sigintr);
- signal(SIGHUP, sighup);
- signal(SIGQUIT, SIG_IGN);
+ signal(SIGINT, sigintr);
+ signal(SIGHUP, sighup);
+ signal(SIGQUIT, SIG_IGN);
- init(*argv);
- edit();
+ init(*argv);
+ edit();
- /* not reached */
- return 0;
-}
+ /* not reached */
+ return 0;
+ }
--
2.9.2
Received on Tue Sep 20 2016 - 08:50:22 CEST
This archive was generated by hypermail 2.3.0 : Tue Sep 20 2016 - 09:00:16 CEST