 			}
 		}
 #endif
-
 #ifndef OMIT_LOAD_EXTENSION
 		else if (c == 'l' && !strncmp(args[0], "load", n) && argsLength >= 2)
 		{
-			const char *zFile, *zProc;
-			char *zErrMsg = 0;
-			zFile = args[1];
-			zProc = argsLength>=3 ? args[2] : 0;
-			open_db(p);
-			rc = sqlite3_load_extension(p->db, zFile, zProc, &zErrMsg);
-			if( rc!=SQLITE_OK ){
-				_fprintf(stderr, "Error: %s\n", zErrMsg);
-				sqlite3_free(zErrMsg);
+			const char *file = args[1];
+			const char *proc = (argsLength >= 3 ? args[2] : 0);
+			OpenCtx(p);
+			char *errMsg = 0;
+			rc = sqlite3_load_extension(p->Ctx, file, proc, &errMsg);
+			if (rc != RC_OK)
+			{
+				_fprintf(stderr, "Error: %s\n", errMsg);
+				_free(errMsg);
 				rc = 1;
 			}
 		}
 #endif
 		else if (c == 'l' && !strncmp(args[0], "log", n) && argsLength >= 2)
 		{
-			const char *zFile = args[1];
-			output_file_close(p->pLog);
-			p->pLog = output_file_open(zFile);
+			const char *file = args[1];
+			OutputFileClose(p->Log);
+			p->Log = OutputFileOpen(file);
 		}
 		else if (c == 'm' && !strncmp(args[0], "mode", n) && argsLength == 2)
 		{
 			int n2 = _strlen(args[1]);
-			if( (n2==4 && strncmp(args[1],"line",n2)==0)
-				||
-				(n2==5 && strncmp(args[1],"lines",n2)==0) ){
-					p->mode = MODE_Line;
-			}else if( (n2==6 && strncmp(args[1],"column",n2)==0)
-				||
-				(n2==7 && strncmp(args[1],"columns",n2)==0) ){
-					p->mode = MODE_Column;
-			}else if( n2==4 && strncmp(args[1],"list",n2)==0 ){
-				p->mode = MODE_List;
-			}else if( n2==4 && strncmp(args[1],"html",n2)==0 ){
-				p->mode = MODE_Html;
-			}else if( n2==3 && strncmp(args[1],"tcl",n2)==0 ){
-				p->mode = MODE_Tcl;
-				sqlite3_snprintf(sizeof(p->separator), p->separator, " ");
-			}else if( n2==3 && strncmp(args[1],"csv",n2)==0 ){
-				p->mode = MODE_Csv;
-				sqlite3_snprintf(sizeof(p->separator), p->separator, ",");
-			}else if( n2==4 && strncmp(args[1],"tabs",n2)==0 ){
-				p->mode = MODE_List;
-				sqlite3_snprintf(sizeof(p->separator), p->separator, "\t");
-			}else if( n2==6 && strncmp(args[1],"insert",n2)==0 ){
-				p->mode = MODE_Insert;
-				set_table_name(p, "table");
-			}else {
-				_fprintf(stderr,"Error: mode should be one of: "
-					"column csv html insert line list tabs tcl\n");
+			if ((n2 == 4 && !strncmp(args[1],"line",n2)) || (n2 == 5 && !strncmp(args[1],"lines",n2))) p->Mode = MODE_Line;
+			else if ((n2 == 6 && !strncmp(args[1],"column",n2)) || (n2 == 7 && !strncmp(args[1],"columns",n2))) p->Mode = MODE_Column;
+			else if (n2 == 4 && !strncmp(args[1],"list",n2)) p->Mode = MODE_List;
+			else if (n2 == 4 && !strncmp(args[1],"html",n2)) p->Mode = MODE_Html;
+			else if (n2 == 3 && !strncmp(args[1],"tcl",n2)) { p->Mode = MODE_Tcl; _snprintf(sizeof(p->Separator), p->Separator, " "); }
+			else if (n2 == 3 && !strncmp(args[1],"csv",n2)) { p->Mode = MODE_Csv; _snprintf(sizeof(p->Separator), p->Separator, ","); }
+			else if (n2 == 4 && !strncmp(args[1],"tabs",n2)) { p->Mode = MODE_List; _snprintf(sizeof(p->Separator), p->Separator, "\t"); }
+			else if (n2 == 6 && !strncmp(args[1],"insert",n2)) { p->Mode = MODE_Insert; SetTableName(p, "table"); }
+			else
+			{
+				_fprintf(stderr,"Error: mode should be one of: column csv html insert line list tabs tcl\n");
 				rc = 1;
 			}
 		}
 		else if (c == 'm' && !strncmp(args[0], "mode", n) && argsLength == 3)
 		{
 			int n2 = _strlen(args[1]);
-			if( n2==6 && strncmp(args[1],"insert",n2)==0 ){
-				p->mode = MODE_Insert;
-				set_table_name(p, args[2]);
-			}else {
-				_fprintf(stderr, "Error: invalid arguments: "
-					" \"%s\". Enter \".help\" for help\n", args[2]);
+			if (n2 == 6 && !strncmp(args[1],"insert",n2)) { p->Mode = MODE_Insert; SetTableName(p, args[2]); }
+			else
+			{
+				_fprintf(stderr, "Error: invalid arguments:  \"%s\". Enter \".help\" for help\n", args[2]);
 				rc = 1;
 			}
 		}
 		else if (c == 'n' && !strncmp(args[0], "nullvalue", n) && argsLength == 2)
 		{
-			sqlite3_snprintf(sizeof(p->nullvalue), p->nullvalue,
-				"%.*s", (int)_lengthof(p->nullvalue)-1, args[1]);
+			_snprintf(sizeof(p->NullValue), p->NullValue, "%.*s", (int)_lengthof(p->NullValue)-1, args[1]);
 		}
 		else if (c == 'o' && !strncmp(args[0], "output", n) && argsLength == 2)
 		{
-			if( p->outfile[0]=='|' ){
-				pclose(p->Out);
-			}else{
-				output_file_close(p->Out);
-			}
-			p->outfile[0] = 0;
-			if( args[1][0]=='|' ){
+			if (p->Outfile[0] == '|') pclose(p->Out);
+			else OutputFileClose(p->Out);
+			p->Outfile[0] = 0;
+			if (args[1][0] == '|')
+			{
 				p->Out = popen(&args[1][1], "w");
-				if( p->Out==0 ){
+				if (!p->Out)
+				{
 					_fprintf(stderr,"Error: cannot open pipe \"%s\"\n", &args[1][1]);
 					p->Out = stdout;
 					rc = 1;
-				}else{
-					sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", args[1]);
-				}
-			}else{
-				p->Out = output_file_open(args[1]);
-				if( p->Out==0 ){
-					if( strcmp(args[1],"off")!=0 ){
-						_fprintf(stderr,"Error: cannot write to \"%s\"\n", args[1]);
-					}
+				}
+				else
+					_snprintf(sizeof(p->Outfile), p->Outfile, "%s", args[1]);
+			}
+			else
+			{
+				p->Out = OutputFileOpen(args[1]);
+				if (!p->Out)
+				{
+					if (strcmp(args[1], "off"))
+						_fprintf(stderr, "Error: cannot write to \"%s\"\n", args[1]);
 					p->Out = stdout;
 					rc = 1;
-				} else {
-					sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", args[1]);
-				}
+				}
+				else
+					_snprintf(sizeof(p->Outfile), p->Outfile, "%s", args[1]);
 			}
 		}
 		else if (c == 'p' && n >= 3 && !strncmp(args[0], "print", n))
 		{
-			int i;
-			for(i=1; i<argsLength; i++){
-				if( i>1 ) _fprintf(p->Out, " ");
+			for (int i = 1; i < argsLength; i++)
+			{
+				if (i > 1) _fprintf(p->Out, " ");
 				_fprintf(p->Out, "%s", args[i]);
 			}
 			_fprintf(p->Out, "\n");
 		}
 		else if (c == 'p' && !strncmp(args[0], "prompt", n) && (argsLength == 2 || argsLength == 3))
 		{
-			if( argsLength >= 2) {
-				strncpy(mainPrompt,args[1],(int)_lengthof(mainPrompt)-1);
-			}
-			if( argsLength >= 3) {
-				strncpy(continuePrompt,args[2],(int)_lengthof(continuePrompt)-1);
-			}
+			if (argsLength >= 2)
+				strncpy(_mainPrompt, args[1], (int)_lengthof(_mainPrompt)-1);
+			if (argsLength >= 3)
+				strncpy(_continuePrompt, args[2], (int)_lengthof(_continuePrompt)-1);
 		}
 		else if (c == 'q' && !strncmp(args[0], "quit", n) && argsLength == 1)
 		{

	}

	// Close an output file, assuming it is not stderr or stdout
	static void output_file_close(FILE *f)
	{
		if (f && f != stdout && f != stderr) fclose(f);
	}

	// Try to open an output file.  The names "stdout" and "stderr" are recognized and do the right thing.  NULL is returned if the output filename is "off".
	static FILE *output_file_open(const char *file)
	{
		FILE *f;
		if (!_strcmp(file, "stdout")) f = stdout;

					"WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%'"
					"ORDER BY substr(type,2,1),"
					" CASE type WHEN 'view' THEN rowid ELSE name END",
					callback, &data, &errMsg);
			}
			if (errMsg) { _fprintf(stderr,"Error: %s\n", errMsg); _free(errMsg); rc = 1; }
			else if (rc != RC_OK) { _fprintf(stderr,"Error: querying schema information\n"); rc = 1; }
			else rc = 0;
		}
		else if (c == 's' && !strncmp(args[0], "separator", n) && argsLength == 2)
		{
			_snprintf(sizeof(p->Separator), p->Separator, "%.*s", (int)sizeof(p->Separator)-1, args[1]);
		}
		else if (c == 's' && !strncmp(args[0], "show", n) && argsLength == 1)
		{
			_fprintf(p->Out,"%9.9s: %s\n","echo", p->EchoOn ? "on" : "off");
			_fprintf(p->Out,"%9.9s: %s\n","explain", p->ExplainPrev.Valid ? "on" :"off");
			_fprintf(p->Out,"%9.9s: %s\n","headers", p->ShowHeader ? "on" : "off");
			_fprintf(p->Out,"%9.9s: %s\n","mode", modeDescr[p->Mode]);
			_fprintf(p->Out,"%9.9s: ", "nullvalue");
			OutputCString(p->Out, p->NullValue);
			_fprintf(p->Out, "\n");
			_fprintf(p->Out,"%9.9s: %s\n","output", _strlen(p->Outfile) ? p->Outfile : "stdout");
			_fprintf(p->Out,"%9.9s: ", "separator");
			OutputCString(p->Out, p->Separator);
			_fprintf(p->Out, "\n");
			_fprintf(p->Out,"%9.9s: %s\n","stats", p->StatsOn ? "on" : "off");
			_fprintf(p->Out,"%9.9s: ","width");
			for (int i = 0; i < (int)_lengthof(p->ColWidth) && p->ColWidth[i] != 0; i++)
				_fprintf(p->Out, "%d ", p->ColWidth[i]);
			_fprintf(p->Out,"\n");
		}
		else if (c == 's' && !strncmp(args[0], "stats", n) && argsLength > 1 && argsLength < 3)
		{
			p->StatsOn = BooleanValue(args[1]);
		}
		else if (c == 't' && n > 1 && !strncmp(args[0], "tables", n) && argsLength < 3)
		{
			OpenCtx(p);
			Vdbe *stmt;
			rc = Prepare::PreparevV(p->Ctx, "PRAGMA database_list", -1, &stmt, 0);
			if (rc) return rc;
			char *sql = _mprintf(
				"SELECT name FROM sqlite_master"
				" WHERE type IN ('table','view')"
				"   AND name NOT LIKE 'sqlite_%%'"
				"   AND name LIKE ?1");
			while (stmt->Step() == RC_ROW)
			{
				const char *dbName = (const char *)Vdbe::Column_Text(stmt, 1);
				if (!dbName || !strcmp(dbName, "main")) continue;
				if (!strcmp(dbName, "temp"))
					sql = _mprintf(
					"%z UNION ALL "
					"SELECT 'temp.' || name FROM sqlite_temp_master"
					" WHERE type IN ('table','view')"
					"   AND name NOT LIKE 'sqlite_%%'"
					"   AND name LIKE ?1", sql);
				else
					sql = sqlite3_mprintf(
					"%z UNION ALL "
					"SELECT '%q.' || name FROM \"%w\".sqlite_master"
					" WHERE type IN ('table','view')"
					"   AND name NOT LIKE 'sqlite_%%'"
					"   AND name LIKE ?1", sql, dbName, dbName);
			}
			Vdbe::Finalize(stmt);
			sql = _mprintf("%z ORDER BY 1", sql);
			rc = Prepare::PrepareV2(p->Ctx, sql, -1, &stmt, 0);
			_free(sql);
			if (rc) return rc;
			int rows, allocs;
			rows = allocs = 0;
			char **newResults = nullptr;
			if (argsLength > 1)
				Vdbe::Bind_Text(stmt, 1, args[1], -1, DESTRUCTOR_TRANSIENT);
			else
				Vdbe::Bind_Text(stmt, 1, "%", -1, DESTRUCTOR_STATIC);
			while (stmt->Step() == RC_ROW)
			{
				if (rows >= allocs)
				{
					int n = allocs*2 + 10;
					char **newResults = _realloc(results, sizeof(results[0])*n);
					if (!newResults)
					{
						_fprintf(stderr, "Error: out_ of memory\n");
						break;
					}
					allocs = n;
					results = newResults;
				}
				results[rows] = _mprintf("%s", Vdbe::Column_Text(stmt, 0));
				if (results[rows]) rows++;
			}
			Vdbe::Finalize(stmt);        
			if (rows > 0)
			{
				int i;
				int maxlen = 0;
				for (i = 0; i < rows; i++)
				{
					int len = _strlen(results[i]);
					if (len > maxlen) maxlen = len;
				}
				int printCols = 80/(maxlen+2);
				if (printCols < 1) printCols = 1;
				int printRows = (rows + printCols - 1)/printCols;
				for (i = 0; i < printRows; i++)
				{
					for (int j = i; j < rows; j += pintRows)
					{
						char *sp = (j < printRow ? "" : "  ");
						printf("%s%-*s", sp, maxlen, (results[j] ? rResults[j] : ""));
					}
					printf("\n");
				}
			}
			for (int ii = 0; ii < rows; ii++) _free(results[ii]);
			_free(results);
		}
		else if (c == 't' && n >= 8 && !strncmp(args[0], "testctrl", n) && argsLength >= 2)
		{
			static const struct
			{
				const char *CtrlName;   // Name of a test-control option
				int CtrlCode;           // Integer code for that option
			} _ctrls[] = {
				{ "prng_save",             TESTCTRL_PRNG_SAVE              },
				{ "prng_restore",          TESTCTRL_PRNG_RESTORE           },
				{ "prng_reset",            TESTCTRL_PRNG_RESET             },
				{ "bitvec_test",           TESTCTRL_BITVEC_TEST            },
				{ "fault_install",         TESTCTRL_FAULT_INSTALL          },
				{ "benign_malloc_hooks",   TESTCTRL_BENIGN_MALLOC_HOOKS    },
				{ "pending_byte",          TESTCTRL_PENDING_BYTE           },
				{ "assert",                TESTCTRL_ASSERT                 },
				{ "always",                TESTCTRL_ALWAYS                 },
				{ "reserve",               TESTCTRL_RESERVE                },
				{ "optimizations",         TESTCTRL_OPTIMIZATIONS          },
				{ "iskeyword",             TESTCTRL_ISKEYWORD              },
				{ "scratchmalloc",         TESTCTRL_SCRATCHMALLOC          },
			};
			int testctrl = -1;
			int rc = 0;
			int i, n;
			OpenCtx(p);
			// convert testctrl text option to value. allow any unique prefix of the option name, or a numerical value.
			int n = _strlen(args[1]);
			for (i = 0; i < (int)_lengthof(_ctrls); i++)
			{
				if (!strncmp(args[1], _ctrls[i].CtrlName, n))
				{
					if (testctrl < 0)
						testctrl = _ctrls[i].CtrlCode;
					else
					{
						_fprintf(stderr, "ambiguous option name: \"%s\"\n", args[1]);
						testctrl = -1;
						break;
					}
				}
			}
			if (testctrl < 0) testctrl = atoi(args[1]);
			if ((testctrl < TESTCTRL_FIRST) || (testctrl > TESTCTRL_LAST))
				_fprintf(stderr,"Error: invalid testctrl option: %s\n", args[1]);
			else
			{
				switch (testctrl)
				{
				case TESTCTRL_OPTIMIZATIONS:
				case TESTCTRL_RESERVE:
					// sqlite3_test_control(int, db, int)
					if (argsLength == 3)
					{
						int opt = (int)strtol(args[2], 0, 0);
						rc = sqlite3_test_control(testctrl, p->Ctx, opt);
						printf("%d (0x%08x)\n", rc, rc);
					}
					else
						_fprintf(stderr,"Error: testctrl %s takes a single int option\n", args[1]);
					break;
				case TESTCTRL_PRNG_SAVE:
				case TESTCTRL_PRNG_RESTORE:
				case TESTCTRL_PRNG_RESET:
					// sqlite3_test_control(int)
					if (argsLength == 2)
					{
						rc = sqlite3_test_control(testctrl);
						printf("%d (0x%08x)\n", rc, rc);
					}
					else
						_fprintf(stderr,"Error: testctrl %s takes no options\n", args[1]);
					break;
				case TESTCTRL_PENDING_BYTE:
					// sqlite3_test_control(int, uint)
					if (argsLength == 3)
					{
						unsigned int opt = (unsigned int)atoi(args[2]);
						rc = sqlite3_test_control(testctrl, opt);
						printf("%d (0x%08x)\n", rc, rc);
					}
					else
						_fprintf(stderr,"Error: testctrl %s takes a single unsigned int option\n", args[1]);
					break;
				case TESTCTRL_ASSERT:
				case TESTCTRL_ALWAYS:
					// sqlite3_test_control(int, int)
					if (argsLength == 3)
					{
						int opt = atoi(args[2]);        
						rc = sqlite3_test_control(testctrl, opt);
						printf("%d (0x%08x)\n", rc, rc);
					}
					else
						_fprintf(stderr,"Error: testctrl %s takes a single int option\n", args[1]);
					break;
#ifdef SQLITE_N_KEYWORD
				case TESTCTRL_ISKEYWORD:
					// sqlite3_test_control(int, char *)
					if (argsLength == 3)
					{
						const char *opt = args[2];
						rc = sqlite3_test_control(testctrl, opt);
						printf("%d (0x%08x)\n", rc, rc);
					}
					else
						_fprintf(stderr,"Error: testctrl %s takes a single char * option\n", args[1]);
					break;
#endif
				case TESTCTRL_BITVEC_TEST:         
				case TESTCTRL_FAULT_INSTALL:       
				case TESTCTRL_BENIGN_MALLOC_HOOKS: 
				case TESTCTRL_SCRATCHMALLOC:       
				default:
					_fprintf(stderr,"Error: CLI support for testctrl %s not implemented\n", args[1]);
					break;
				}
			}
		}
		else if (c == 't' && n > 4 && !strncmp(args[0], "timeout", n) && argsLength == 2)
		{
			OpenCtx(p);
			sqlite3_busy_timeout(p->Ctx, atoi(args[1]));
		}
		else if (HAS_TIMER && c == 't' && n >= 5 && !strncmp(args[0], "timer", n) && argsLength == 2)
		{
			enableTimer = BooleanValue(args[1]);
		}
		else if (c == 't' && !strncmp(args[0], "trace", n) && argsLength > 1)
		{
			OpenCtx(p);
			OutputFileClose(p->traceOut);
			p->TraceOut = OutputFileOpen(args[1]);
#if !defined(OMIT_TRACE) && !defined(OMIT_FLOATING_POINT)
			if (p->TraceOut == 0)
				sqlite3_trace(p->Ctx, 0, 0);
			else
				sqlite3_trace(p->Ctx, SqlTraceCallback, p->TraceOut);
#endif
		}
		else if (c == 'v' && !strncmp(args[0], "version", n))
		{
			printf("SQLite %s %s\n", sqlite3_libversion(), sqlite3_sourceid());
		}
		else if (c == 'v' && !strncmp(args[0], "vfsname", n))
		{
			const char *dbName = (argsLength == 2 ? args[1] : "main");
			char *vfsName = 0;
			if (p->Ctx)
			{
				sqlite3_file_control(p->Ctx, dbName, SQLITE_FCNTL_VFSNAME, &vfsName);
				if (vfsName)
				{
					printf("%s\n", vfsName);
					_free(vfsName);
				}
			}
		}
