Download raw body.
vi(1) bug executing buffer with range fixed
Hello,
while using vi(1) I found a bug related to executing buffers that I
managed to fix.
The bug happens also in nvi(1) from ports.
This bug is related to executing the content of a buffer specifying a
range, that is a feature documented in the code, but not in the man
page nor in the reference manual, so I also added some documentations.
The steps that cause the bug:
* copy to a buffer some ex commands, for example to buffer a: s/^/# /
* execute the buffer specifying a range -> :.,+10@a
* try to undo the changes -> u
Undoing the changes will result in just the last line being undone,
and in order to undo all the lines the . will have to be pressed
many times.
The correct behaviour should be as if executing directly :.,+10s/^/# /
then doing u, so restoring all the lines changed.
The execution of a buffer with a range was documented in ex/ex_at.c:
/*
* !!!
* Historically the @ command took a range of lines, and the @ buffer
* was executed once per line. The historic vi could be trashed by
* this because it didn't notice if the underlying file changed, or,
* for that matter, if there were no more lines on which to operate.
* For example, take a 10 line file, load "%delete" into a buffer,
* and enter :8,10@<buffer>.
*
* The solution is a bit tricky. If the user specifies a range, take
* the same approach as for global commands, and discard the command
* if exit or switch to a new file/screen. If the user doesn't specify
* the range, continue to execute after a file/screen switch, which
* means @ buffers are still useful in a multi-screen environment.
*/
Bye,
jerry
diff --git common/screen.h common/screen.h
index 4fdf33bdc26..447cdcfcaa8 100644
--- common/screen.h
+++ common/screen.h
@@ -187,16 +187,17 @@ struct _scr {
#define SC_AT_SET 0x00008000 /* Last at buffer set. */
#define SC_COMEDIT 0x00010000 /* Colon command-line edit window. */
#define SC_EX_GLOBAL 0x00020000 /* Ex: executing a global command. */
-#define SC_EX_SILENT 0x00040000 /* Ex: batch script. */
-#define SC_EX_WAIT_NO 0x00080000 /* Ex: don't wait for the user. */
-#define SC_EX_WAIT_YES 0x00100000 /* Ex: do wait for the user. */
-#define SC_READONLY 0x00200000 /* Persistent readonly state. */
-#define SC_RE_SEARCH 0x00400000 /* Search RE has been compiled. */
-#define SC_RE_SUBST 0x00800000 /* Substitute RE has been compiled. */
-#define SC_SCRIPT 0x01000000 /* Shell script window. */
-#define SC_STATUS 0x02000000 /* Welcome message. */
-#define SC_STATUS_CNT 0x04000000 /* Welcome message plus file count. */
-#define SC_TINPUT 0x08000000 /* Doing text input. */
-#define SC_TINPUT_INFO 0x10000000 /* Doing text input on info line. */
+#define SC_EX_AT 0x00040000 /* Ex: executing a buffer with range. */
+#define SC_EX_SILENT 0x00080000 /* Ex: batch script. */
+#define SC_EX_WAIT_NO 0x00100000 /* Ex: don't wait for the user. */
+#define SC_EX_WAIT_YES 0x00200000 /* Ex: do wait for the user. */
+#define SC_READONLY 0x00400000 /* Persistent readonly state. */
+#define SC_RE_SEARCH 0x00800000 /* Search RE has been compiled. */
+#define SC_RE_SUBST 0x01000000 /* Substitute RE has been compiled. */
+#define SC_SCRIPT 0x02000000 /* Shell script window. */
+#define SC_STATUS 0x04000000 /* Welcome message. */
+#define SC_STATUS_CNT 0x08000000 /* Welcome message plus file count. */
+#define SC_TINPUT 0x10000000 /* Doing text input. */
+#define SC_TINPUT_INFO 0x20000000 /* Doing text input on info line. */
u_int32_t flags;
};
diff --git docs/USD.doc/vi.man/vi.1 docs/USD.doc/vi.man/vi.1
index 585e033bce6..9f737185315 100644
--- docs/USD.doc/vi.man/vi.1
+++ docs/USD.doc/vi.man/vi.1
@@ -1689,8 +1689,16 @@ A comment.
.Xc
Display the selected lines, each preceded with its line number.
.Pp
-.It Cm @ Ar buffer
-.It Cm * Ar buffer
+.It Xo
+.Op Ar range
+.Cm @
+.Ar buffer
+.Xc
+.It Xo
+.Op Ar range
+.Cm *
+.Ar buffer
+.Xc
Execute a buffer.
.Pp
.It Xo
diff --git docs/USD.doc/vi.ref/ex.cmd.roff docs/USD.doc/vi.ref/ex.cmd.roff
index 196340e91fa..b0943e872e1 100644
--- docs/USD.doc/vi.ref/ex.cmd.roff
+++ docs/USD.doc/vi.ref/ex.cmd.roff
@@ -314,9 +314,9 @@ Affected by the
option.
.SE
.KY "@"
-.IP "@ buffer"
+.IP "[range] @ buffer"
.KY "*"
-.Ip "* buffer"
+.Ip "[range] * buffer"
Execute a buffer.
Each line in the named buffer is executed as an
.CO ex
@@ -326,6 +326,8 @@ If no buffer is specified, or if the specified buffer is
or
.QT * ,
the last buffer executed is used.
+If the range is specified, discard the command if switch to a new file/screen.
+Without the range continue to execute after a file/screen switch.
.KY <
.IP "[range] <[< ...] [count] [flags]"
Shift lines left or right.
diff --git ex/ex.c ex/ex.c
index 9aa31ad29fb..82d88f9b525 100644
--- ex/ex.c
+++ ex/ex.c
@@ -1336,7 +1336,7 @@ addr_verify:
* If file state available, and not doing a global command,
* log the start of an action.
*/
- if (sp->ep != NULL && !F_ISSET(sp, SC_EX_GLOBAL))
+ if (sp->ep != NULL && !F_ISSET(sp, SC_EX_GLOBAL | SC_EX_AT))
(void)log_cursor(sp);
/*
@@ -1600,8 +1600,9 @@ rsuccess: tmp = 0;
/* Turn off any file name error information. */
gp->if_name = NULL;
- /* Turn off the global bit. */
+ /* Turn off the global and at bits. */
F_CLR(sp, SC_EX_GLOBAL);
+ F_CLR(sp, SC_EX_AT);
return (tmp);
}
@@ -2056,6 +2057,7 @@ ex_load(SCR *sp)
RANGE *rp;
F_CLR(sp, SC_EX_GLOBAL);
+ F_CLR(sp, SC_EX_AT);
/*
* Lose any exhausted commands. We know that the first command
@@ -2136,6 +2138,8 @@ ex_load(SCR *sp)
if (FL_ISSET(ecp->agv_flags, AGV_GLOBAL | AGV_V))
F_SET(sp, SC_EX_GLOBAL);
+ if (FL_ISSET(ecp->agv_flags, AGV_AT))
+ F_SET(sp, SC_EX_AT);
return (0);
}
diff --git ex/ex_at.c ex/ex_at.c
index dd1b2b90813..87ac4e76c57 100644
--- ex/ex_at.c
+++ ex/ex_at.c
@@ -76,7 +76,7 @@ ex_at(SCR *sp, EXCMD *cmdp)
* The solution is a bit tricky. If the user specifies a range, take
* the same approach as for global commands, and discard the command
* if exit or switch to a new file/screen. If the user doesn't specify
- * the range, continue to execute after a file/screen switch, which
+ * the range, continue to execute after a file/screen switch, which
* means @ buffers are still useful in a multi-screen environment.
*/
CALLOC_RET(sp, ecp, 1, sizeof(EXCMD));
vi(1) bug executing buffer with range fixed