Description: fix broken EDIT command in isql
Bug: http://tracker.firebirdsql.org/browse/CORE-3990
Origin: upstream, http://sourceforge.net/p/firebird/code/57714/
Origin: upstream, http://sourceforge.net/p/firebird/code/57716/
--- a/src/isql/isql.epp
+++ b/src/isql/isql.epp
@@ -4176,27 +4176,6 @@ static void do_isql()
 
 	M__trans = 0;
 
-	// File used to edit sessions
-	{ // scope
-		const Firebird::PathName filename = TempFile::create(SCRATCH);
-		const char* Tmpfile = filename.c_str();
-		FILE* f = fopen(Tmpfile, "w+"); // It was w+b
-#ifdef UNIX
-		unlink(Tmpfile);
-#endif
-		if (f)
-			Filelist->Ofp().init(f, Tmpfile);
-		else
-		{
-			// If we can't open a temp file then bail
-
-			ISQL_msg_get(FILE_OPEN_ERR, errbuf, SafeArg() << Tmpfile);
-			STDERROUT(errbuf);
-			Exit_value = FINI_ERROR;
-			return;
-		}
-	}
-
 #if defined(_MSC_VER) && _MSC_VER >= 1400
 	_set_output_format(_TWO_DIGIT_EXPONENT);
 #endif
@@ -4388,9 +4367,9 @@ static void do_isql()
 	gds_trans = 0;
 
 	InputDevices::indev& Ofp = Filelist->Ofp();
-	// Should have a valid Temp file pointer
-	fb_assert(Ofp.indev_fpointer);
-	Ofp.drop();
+	// Does it have a valid Temp file pointer?
+	if (Ofp.indev_fpointer)
+		Ofp.drop();
 
 	if (global_sqlda)
 		ISQL_FREE(global_sqlda);
@@ -4506,6 +4485,28 @@ static processing_state edit(const TEXT*
 		Filelist->insertIfp();
 		// Close the file, edit it, then reopen and read from the top
 		InputDevices::indev& Ofp = Filelist->Ofp();
+
+		if (!Ofp.indev_fpointer)
+		{
+			// File used to edit sessions
+			const Firebird::PathName filename = TempFile::create(SCRATCH);
+			const char* Tmpfile = filename.c_str();
+			FILE* f = fopen(Tmpfile, "w+"); // It was w+b
+			if (f)
+			{
+				Ofp.init(f, Tmpfile);
+				Filelist->commandsToFile(f);
+			}
+			else
+			{
+				// If we can't open a temp file then bail
+				TEXT errbuf[MSG_LENGTH];
+				ISQL_msg_get(FILE_OPEN_ERR, errbuf, SafeArg() << Tmpfile);
+				STDERROUT(errbuf);
+				return ps_ERR;
+			}
+		}
+
 		Ofp.close();
 		const char* Tmpfile = Ofp.fileName();
 		gds__edit(Tmpfile, 0);
@@ -4647,7 +4648,8 @@ static processing_state escape(const TEX
 	//_flushall();
 	// Save Ofp position in case it's being used as input. See EDIT command.
 	fpos_t OfpPos = 0;
-	Filelist->Ofp().getPos(&OfpPos);
+	if (Ofp.indev_fpointer)
+		Filelist->Ofp().getPos(&OfpPos);
 	fflush(NULL); // Flush only output buffers.
 	const char* emptyCmd = "%ComSpec%";
 #else
@@ -4663,7 +4665,7 @@ static processing_state escape(const TEX
 #ifdef WIN_NT
 	// If we are reading from the temp file, restore the read position because
 	// it's opened in r+ mode in this case, that's R/W.
-	if (Filelist->sameInputAndOutput())
+	if (Ofp.indev_fpointer && Filelist->sameInputAndOutput())
 		Filelist->Ofp().setPos(&OfpPos);
 #endif
 
@@ -5564,8 +5566,9 @@ static processing_state get_statement(TE
 
 			// If this is not tmpfile, close it
 
-			if (!Filelist->sameInputAndOutput())
-				Filelist->Ifp().close();
+			if (Filelist->Ofp().indev_fpointer)
+				if (!Filelist->sameInputAndOutput())
+					Filelist->Ifp().close();
 
 			// Reset to previous after other input
 
--- a/src/isql/InputDevices.cpp
+++ b/src/isql/InputDevices.cpp
@@ -226,11 +226,18 @@ void InputDevices::saveCommand(const cha
 	if (m_ifp.indev_fpointer == stdin)
 	{
 		FILE* f = m_ofp.indev_fpointer;
-		fb_assert(f);
-		fputs(statement, f);
-		fputs(term, f);
-		// Add newline to make the file more readable.
-		fputc('\n', f);
+		if (f)
+		{
+			fputs(statement, f);
+			fputs(term, f);
+			// Add newline to make the file more readable.
+			fputc('\n', f);
+		}
+		else
+		{
+			Command* command = new Command(statement, term);
+			commands.add(command);
+		}
 	}
 }
 
@@ -249,3 +256,27 @@ void InputDevices::gotoEof()
 		fseek(m_ifp.indev_fpointer, 0, SEEK_END);
 }
 
+InputDevices::Command::Command(const char* statement, const char* term)
+	: m_statement(getPool())
+{
+	m_statement = statement;
+	m_statement += term;
+}
+
+void InputDevices::Command::toFile(FILE* f)
+{
+	fputs(m_statement.c_str(), f);
+	// Add newline to make the file more readable.
+	fputc('\n', f);
+}
+
+void InputDevices::commandsToFile(FILE* f)
+{
+	for (unsigned n = 0; n < commands.getCount(); ++n)
+	{
+		commands[n]->toFile(f);
+		delete commands[n];
+	}
+
+	commands.clear();
+}
--- a/src/isql/InputDevices.h
+++ b/src/isql/InputDevices.h
@@ -35,6 +35,8 @@
 // Do not confuse this "Ofp" with the user defined redirection of isql output to a file.
 // The logic could be simpler but changing Borland code is tricky here.
 
+#include "../common/classes/array.h"
+
 class InputDevices
 {
 public:
@@ -78,12 +80,24 @@ public:
 	void saveCommand(const char* statement, const char* term);
 	bool readingStdin() const;
 	void gotoEof();
+	void commandsToFile(FILE* fpointer);
 
 private:
 	size_t m_count;
 	indev* m_head;
 	indev m_ifp;
 	indev m_ofp;
+
+	class Command : public Firebird::GlobalStorage
+	{
+	public:
+		Command(const char* statement, const char* term);
+		void toFile(FILE* fpointer);
+	private:
+		Firebird::string m_statement;
+	};
+
+	Firebird::HalfStaticArray<Command*, 32> commands;
 };
 
 
@@ -100,12 +114,12 @@ inline void InputDevices::indev::close()
 
 
 inline InputDevices::InputDevices()
-	: m_count(0), m_head(0), m_ifp(0, ""), m_ofp(0, "")
+	: m_count(0), m_head(0), m_ifp(0, ""), m_ofp(0, ""), commands(*getDefaultMemoryPool())
 {
 }
 
-inline InputDevices::InputDevices(Firebird::MemoryPool&)
-	: m_count(0), m_head(0), m_ifp(0, ""), m_ofp(0, "")
+inline InputDevices::InputDevices(Firebird::MemoryPool& p)
+	: m_count(0), m_head(0), m_ifp(0, ""), m_ofp(0, ""), commands(p)
 {
 }
 
