From: larrybr Date: Mon, 24 Jan 2022 00:22:06 +0000 (+0000) Subject: CLI prepared for reading string input X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=10e2866c99929eacc45b709089a18b7e649e73a2;p=thirdparty%2Fsqlite.git CLI prepared for reading string input FossilOrigin-Name: 073ded4d185d0133b07b4548dd78f4ad5eb17ce1f5c457d89fee23281420bbff --- diff --git a/manifest b/manifest index 792037e6b7..ffc845b2ce 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Factor\sout\sDB\sprotection\slift\sand\srestore\sin\sCLI -D 2022-01-23T20:54:25.776 +C CLI\sprepared\sfor\sreading\sstring\sinput +D 2022-01-24T00:22:06.344 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -553,7 +553,7 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c F src/resolve.c 359bc0e445d427583d2ab6110433a5dc777f64a0ecdf8d24826d8b475233ead9 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c ab5717255420972e69b9b9ce4d1c4730fe82cfbdc14b7743e389a8bdb79ca027 -F src/shell.c.in 8c152178d813b1565d13e01a4e9f48a75185b11756dafc36c353aaff2fff68a8 +F src/shell.c.in e457581785607722cd12c79cbfe9e75b8385c194cf2705727b15c00b8870eb5a F src/sqlite.h.in 31c2c8d737814369bd3b71f3849c4a97ef7ede0aa3ce976ecb11632fa5f1f863 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 5d54cf13d3406d8eb65d921a0d3c349de6126b732e695e79ecd4830ce86b4f8a @@ -1941,8 +1941,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 8735caf13fc7e30da2fc5dc90921acbb3109f965c63dbf69fa70ef19c02308c0 -R 6253b464049656a2fb8c9361c4f1fbc9 +P 4462ed896459ee0312f1989d9a813c65150347ebdd15b59b2aa86bbce3abec1c +R 4304075de837dc32a0991c68c10dfb6b U larrybr -Z 54f5d49a44974e01c687a7b7befc32d9 +Z 3d97e11411e5c32cab39ea8a9a85a826 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5ff7000aa1..97851dc5f1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4462ed896459ee0312f1989d9a813c65150347ebdd15b59b2aa86bbce3abec1c \ No newline at end of file +073ded4d185d0133b07b4548dd78f4ad5eb17ce1f5c457d89fee23281420bbff \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index d365a4ff80..fbfb3c6a9d 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -677,6 +677,33 @@ static char *local_getline(char *zLine, FILE *in){ return zLine; } +/* +** Arrange for shell input from either a FILE or a string. +** See getline_from(...) for applicable invariants. It is +** the only modifier of data within this struct, except for +** initialization which is either {0, zS, 0} or {pF, 0, 0}, +** left to whatever routine is switching input sources. +*/ +typedef struct InSource { + FILE *inFile; /* Will be 0 when input is to be taken from string. */ + char *zStrIn; /* Will be 0 when no input is available from string. */ + int iReadOffset; /* Offset into ZStrIn where next "read" to be done. */ +} InSource; + +/* +** Read single lines of input text from source selected by pInSource. +** This function presents an interface similar to local_getline(...), +** to which it defers for FILE input. (This function may be considered +** to be an adapter except for the source parameter.) +*/ +static char *getline_from( char *zPrior, InSource *pSource ){ + char *zResult = 0; + if( pSource->inFile!=0 ){ + zResult = local_getline(zPrior, pSource->inFile); + } + return zResult; +} + /* ** Retrieve a single line of input text. ** @@ -689,13 +716,13 @@ static char *local_getline(char *zLine, FILE *in){ ** ** The result is stored in space obtained from malloc() and must either ** be freed by the caller or else passed back into this routine via the -** zPrior argument for reuse. +** zPrior argument for reuse (and eventual free by the caller.) */ -static char *one_input_line(FILE *in, char *zPrior, int isContinuation){ +static char *one_input_line(InSource *pInSrc, char *zPrior, int isContinuation){ char *zPrompt; char *zResult; - if( in!=0 ){ - zResult = local_getline(zPrior, in); + if( pInSrc!=0 ){ + zResult = local_getline(zPrior, pInSrc->inFile); }else{ zPrompt = isContinuation ? continuePrompt : mainPrompt; #if SHELL_USE_LOCAL_GETLINE @@ -1066,6 +1093,9 @@ struct EQPGraph { char zPrefix[100]; /* Graph prefix */ }; +/* Input source switching is done through one of these (defined below) */ +typedef struct InSource InSource; + /* ** State information about the database connection is contained in an ** instance of the following structure. @@ -1090,7 +1120,7 @@ struct ShellState { int cnt; /* Number of records displayed so far */ int lineno; /* Line number of last line read from in */ int openFlags; /* Additional flags to open. (SQLITE_OPEN_NOFOLLOW) */ - FILE *in; /* Read commands from this stream */ + InSource *pInSource; /* Read commands and SQL from this stream source */ FILE *out; /* Write results here */ FILE *traceOut; /* Output for sqlite3_trace() */ int nErr; /* Number of errors seen */ @@ -4491,7 +4521,8 @@ int deduceDatabaseType(const char *zName, int dfltZip){ /* ** Reconstruct an in-memory database using the output from the "dbtotxt" ** program. Read content from the file in p->aAuxDb[].zDbFilename. -** If p->aAuxDb[].zDbFilename is 0, then read from standard input. +** If p->aAuxDb[].zDbFilename is 0, then read from either the present +** input file, or standard input if there is no file open for input. */ static unsigned char *readHexDb(ShellState *p, int *pnData){ unsigned char *a = 0; @@ -4501,7 +4532,7 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){ int iOffset = 0; int j, k; int rc; - FILE *in; + FILE *in = 0; const char *zDbFilename = p->pAuxDb->zDbFilename; unsigned int x[16]; char zLine[1000]; @@ -4513,7 +4544,7 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){ } nLine = 0; }else{ - in = p->in; + if( p->pInSource!=0 ) in = p->pInSource->inFile; nLine = p->lineno; if( in==0 ) in = stdin; } @@ -4553,22 +4584,20 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){ } } *pnData = n; - if( in!=p->in ){ - fclose(in); - }else{ - p->lineno = nLine; - } + if( zDbFilename ) fclose(in); + else p->lineno = nLine; return a; readHexDb_error: - if( in!=p->in ){ + if( zDbFilename ){ fclose(in); }else{ - while( fgets(zLine, sizeof(zLine), p->in)!=0 ){ - nLine++; + int nLinePass = nLine; + while( fgets(zLine, sizeof(zLine), in)!=0 ){ + nLinePass++; if(strncmp(zLine, "| end ", 6)==0 ) break; } - p->lineno = nLine; + p->lineno = nLinePass; } sqlite3_free(a); utf8_printf(stderr,"Error on line %d of --hexdb input\n", nLine); @@ -9402,8 +9431,8 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 ){ - FILE *inSaved = p->in; - int savedLineno = p->lineno; + FILE *inUse = 0; + int (*fCloser)(FILE *) = 0; failIfSafeMode(p, "cannot run .read in safe mode"); if( nArg!=2 ){ raw_printf(stderr, "Usage: .read FILE\n"); @@ -9414,26 +9443,33 @@ static int do_meta_command(char *zLine, ShellState *p){ #ifdef SQLITE_OMIT_POPEN raw_printf(stderr, "Error: pipes are not supported in this OS\n"); rc = 1; - p->out = stdout; + p->out = stdout; /* This is likely not needed. To be investigated. */ #else - p->in = popen(azArg[1]+1, "r"); - if( p->in==0 ){ + inUse = popen(azArg[1]+1, "r"); + if( inUse==0 ){ utf8_printf(stderr, "Error: cannot open \"%s\"\n", azArg[1]); rc = 1; }else{ - rc = process_input(p); - pclose(p->in); + fCloser = pclose; } #endif - }else if( (p->in = openChrSource(azArg[1]))==0 ){ + }else if( (inUse = openChrSource(azArg[1]))==0 ){ utf8_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); rc = 1; }else{ + fCloser = fclose; + } + if( inUse!=0 ){ + InSource *pInSrcSave = p->pInSource; + int savedLineno = p->lineno; + InSource inSourceDivert = {inUse, 0, 0}; + p->pInSource = &inSourceDivert; rc = process_input(p); - fclose(p->in); + assert(fCloser!=0); + fCloser(inUse); + p->pInSource = pInSrcSave; + p->lineno = savedLineno; } - p->in = inSaved; - p->lineno = savedLineno; }else if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 ){ @@ -10939,8 +10975,9 @@ static int line_is_complete(char *zSql, int nSql){ /* ** Run a single line of SQL. Return the number of errors. +** bAltIn indicates that input has been redirected in some way. */ -static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){ +static int runOneSqlLine(ShellState *p, char *zSql, int bAltIn, int startline){ int rc; char *zErrMsg = 0; @@ -10952,7 +10989,7 @@ static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){ END_TIMER; if( rc || zErrMsg ){ char zPrefix[100]; - if( in!=0 || !stdin_is_interactive ){ + if( bAltIn || !stdin_is_interactive ){ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "Error: near line %d:", startline); }else{ @@ -10998,16 +11035,18 @@ static int process_input(ShellState *p){ QuickScanState qss = QSS_Start; /* Accumulated line status (so far) */ p->lineno = 0; - while( errCnt==0 || !bail_on_error || (p->in==0 && stdin_is_interactive) ){ + while( errCnt==0 + || !bail_on_error + || (p->pInSource==0 && stdin_is_interactive) ){ fflush(p->out); - zLine = one_input_line(p->in, zLine, nSql>0); + zLine = one_input_line(p->pInSource, zLine, nSql>0); if( zLine==0 ){ /* End of input */ - if( p->in==0 && stdin_is_interactive ) printf("\n"); + if( p->pInSource==0 && stdin_is_interactive ) printf("\n"); break; } if( seenInterrupt ){ - if( p->in!=0 ) break; + if( p->pInSource!=0 ) break; seenInterrupt = 0; } p->lineno++; @@ -11058,7 +11097,7 @@ static int process_input(ShellState *p){ nSql += nLine; } if( nSql && QSS_SEMITERM(qss) && sqlite3_complete(zSql) ){ - errCnt += runOneSqlLine(p, zSql, p->in, startline); + errCnt += runOneSqlLine(p, zSql, p->pInSource!=0, startline); nSql = 0; if( p->outCount ){ output_reset(p); @@ -11075,7 +11114,7 @@ static int process_input(ShellState *p){ } } if( nSql && QSS_PLAINDARK(qss) ){ - errCnt += runOneSqlLine(p, zSql, p->in, startline); + errCnt += runOneSqlLine(p, zSql, p->pInSource!=0, startline); } free(zSql); free(zLine); @@ -11164,8 +11203,7 @@ static void process_sqliterc( char *home_dir = NULL; const char *sqliterc = sqliterc_override; char *zBuf = 0; - FILE *inSaved = p->in; - int savedLineno = p->lineno; + FILE *inUse; if (sqliterc == NULL) { home_dir = find_home_dir(0); @@ -11178,19 +11216,25 @@ static void process_sqliterc( shell_check_oom(zBuf); sqliterc = zBuf; } - p->in = fopen(sqliterc,"rb"); - if( p->in ){ + inUse = fopen(sqliterc,"rb"); + if( inUse!=0 ){ + InSource *pInSrcSave = p->pInSource; + int savedLineno = p->lineno; + InSource inSourceDivert = {inUse, 0, 0}; + int rc; + p->pInSource = &inSourceDivert; if( stdin_is_interactive ){ utf8_printf(stderr,"-- Loading resources from %s\n",sqliterc); } - if( process_input(p) && bail_on_error ) exit(1); - fclose(p->in); + rc = process_input(p); + fclose(inUse); + p->pInSource = pInSrcSave; + p->lineno = savedLineno; + if( rc!=0 && bail_on_error ) exit(1); }else if( sqliterc_override!=0 ){ utf8_printf(stderr,"cannot open: \"%s\"\n", sqliterc); if( bail_on_error ) exit(1); } - p->in = inSaved; - p->lineno = savedLineno; sqlite3_free(zBuf); } @@ -11356,6 +11400,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ #endif char *zErrMsg = 0; ShellState data; + InSource inputSource = { stdin, 0, 0}; const char *zInitFile = 0; int i; int rc = 0; @@ -11882,7 +11927,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ #elif HAVE_LINENOISE linenoiseSetCompletionCallback(linenoise_completion); #endif - data.in = 0; + data.pInSource = 0; /* read from stdin interactively */ rc = process_input(&data); if( zHistory ){ shell_stifle_history(2000); @@ -11890,7 +11935,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ free(zHistory); } }else{ - data.in = stdin; + data.pInSource = &inputSource; /* read from stdin without prompts */ rc = process_input(&data); } }