[hackers] [sbase][PATCH] ed: bugfixes to do with j, wrote ed.1.

From: Thomas Mannay <audiobarrier_AT_openmailbox.org>
Date: Tue, 20 Sep 2016 07:50:22 +0100

---
 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