Index | Thread | Search

From:
jerry <mail@jerryfletcher.org>
Subject:
vi(1) source file that switches screen bug fixed
To:
tech@openbsd.org
Date:
Wed, 17 Sep 2025 16:42:00 +0200

Download raw body.

Thread
  • jerry:

    vi(1) source file that switches screen bug fixed

Hello,

while using vi(1) I found a bug that I managed to fix.
The bug happens also in nvi(1) from ports.

Steps to replicate the bug:
* create a file that contains ex commands, with the last one being one
  that switches to a new screen -> example file source-test that
  contains the only line "Edit source-test"
* open with vi an other file
* :source source-test
* execute as first command after sourcing the file one that will fail
  and print an error -> example :next
* the command will print: 'source-test, 2: No more files to edit.', it
  will fail as if it was called by the file just sourced

When the commands being sourced do not switch screen, once there are no
other commands to source, ex_load will be called again, and it will
clear the ex command structure.  When a screen switch happens as
explained in this comment, the function returns and will be recalled
after the switch.
From ex/ex.c:
* Otherwise, if we've changed underlying files, it's not a problem,
* we continue with the rest of the ex command(s), operating on the
* new file.  However, if we switch screens (either by exiting or by
* an explicit command), we have no way of knowing where to put output
* messages, and, since we don't control screens here, we could screw
* up the upper layers, (e.g. we could exit/reenter a screen multiple
* times).  So, return and continue after we've got a new screen.
*/

The patch provided clear the name of the ex command structure before
switching screen when there are no more commands to run.

Bye,
jerry

diff --git ex/ex.c ex/ex.c
index 9aa31ad29fb..383f5be2a2c 100644
--- ex/ex.c
+++ ex/ex.c
@@ -1557,8 +1557,20 @@ addr_verify:
 			}
 		if (at_found || gv_found)
 			goto discard;
-		if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE | SC_SSWITCH))
+		if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE | SC_SSWITCH)) {
+			/*
+			 * If there are no more commands, discard any
+			 * allocated source name.
+			 */
+			ecp = LIST_FIRST(&gp->ecq);
+			if (ecp->clen == 0 && ecp->save_cmdlen == 0 &&
+			    F_ISSET(ecp, E_NAMEDISCARD) &&
+			    ecp->if_name != NULL) {
+				free(ecp->if_name);
+				ecp->if_name = NULL;
+			}
 			goto rsuccess;
+		}
 	}
 
 	goto loop;
@@ -2068,7 +2080,8 @@ ex_load(SCR *sp)
 		 * the beginning of the command stack.
 		 */
 		if ((ecp = LIST_FIRST(&gp->ecq)) == &gp->excmd) {
-			if (F_ISSET(ecp, E_NAMEDISCARD)) {
+			if (F_ISSET(ecp, E_NAMEDISCARD) &&
+			    ecp->if_name != NULL) {
 				free(ecp->if_name);
 				ecp->if_name = NULL;
 			}