Hello,
On 2017-03-07 18:16, Dan Kennedy wrote:
On 03/08/2017 12:03 AM, Rob Golsteijn wrote:
I want to report a minor issue for the Sqlite shell. It does not
handle multiline command line arguments in which the second line
contains a dot-command correctly.
If the same statements are passed via stdin they are handled fine.
Tested with Sqlite 3.15.2 on Ubuntu 14.04 using Bash.
Passing statements via stdin works fine:
echo "SELECT 1;
.mode csv
SELECT 1;" | sqlite3 mydb.sq3
Passing the statements via a command line argument gives an error:
sqlite3 mydb.sq3 "SELECT 1;
.mode csv
SELECT 1;"
Error: near ".": syntax error
A work around is:
sqlite3 mydb.sq3 "SELECT 1" ".mode csv" "SELECT 1"
Fortunately most of work is done already. It is enough to dirty patch
``fgets'':
After ``static char mainPrompt[20];'':
======
typedef struct CHNFILE {
int nCmd; /* nof elems in azCmd */
char **azCmd; /* arr to scan */
int nCmdCur; /* nof cur elem */
char *zCmdCur; /* next char to scan */
size_t nLenCmdCur; /* len of cur line */
} CHNFILE;
static CHNFILE chnfile = { 0 };
/*
* nCmd > 0 or UDB (7.1.7)
* azCmd != NULL or UDB (7.1.7)
*/
/* !!!! MT: NOT LOCKED !!!! */
static FILE *chnfileopen(int nCmd, char *azCmd[])
{
assert(0 < nCmd);
assert(NULL != azCmd);
if ( 0 != chnfile.nCmd ) {
return NULL;
}
chnfile.nCmd = nCmd;
chnfile.azCmd = azCmd;
chnfile.nCmdCur = 0;
chnfile.zCmdCur = strchr(azCmd[0], '\n');
chnfile.nLenCmdCur = NULL == chnfile.zCmdCur ? strlen(azCmd[0]) :
chnfile.zCmdCur - azCmd[0];
chnfile.zCmdCur = azCmd[0];
return (void *)&chnfile;
}
/* !!!! MT: NOT LOCKED !!!! */
static char *chnfilegets(char *s, int n, FILE *f)
{
if ( 0 == chnfile.nCmd || (void *)&chnfile != f ) {
return fgets(s, n, f);
}
/* ``shall'' -- UDB (7.1.7) */
assert(NULL != s || 0 >= n || (int)chnfile.nCmd <= chnfile.nCmdCur);
/* nothing to read or EOF */
if ( 0 >= n || (int)chnfile.nCmd <= chnfile.nCmdCur ) {
return NULL;
}
/* Trailing '\0' */
--n;
/* Advance to the next line? */
if ( (size_t)n > chnfile.nLenCmdCur ) {
/* YES: Advance to the next line? */
char *p; /* Temp helper */
memcpy(s, chnfile.zCmdCur, chnfile.nLenCmdCur);
s[chnfile.nLenCmdCur] = '\n';
s[chnfile.nLenCmdCur + 1] = 0;
/* Next line */
if ( '\n' == chnfile.zCmdCur[chnfile.nLenCmdCur] && 0 !=
chnfile.zCmdCur[chnfile.nLenCmdCur + 1] ) {
/* Ends with '\n' [not followed by '\0' ???] */
chnfile.zCmdCur = &chnfile.zCmdCur[chnfile.nLenCmdCur + 1];
p = strchr(chnfile.zCmdCur, '\n');
chnfile.nLenCmdCur = NULL == p ? strlen(chnfile.zCmdCur) : p -
chnfile.zCmdCur;
} else {
/* Next element */
if ( (int)++chnfile.nCmdCur < chnfile.nCmd ) {
chnfile.zCmdCur = chnfile.azCmd[chnfile.nCmdCur];
p = strchr(chnfile.zCmdCur, '\n');
chnfile.nLenCmdCur = NULL == p ? strlen(chnfile.zCmdCur) : p -
chnfile.zCmdCur;
}
}
} else {
/* NO: Advance to the next line? */
memcpy(s, chnfile.zCmdCur, n);
s[n] = 0;
chnfile.zCmdCur = &chnfile.zCmdCur[n];
chnfile.nLenCmdCur -= n;
}
return s;
}
/* !!!! MT: NOT LOCKED !!!! */
static int chnfileclose(FILE *f)
{
if ( 0 == chnfile.nCmd || &chnfile != (void *)f ) {
return fclose(f);
}
chnfile.nCmd = 0;
return 0;
}
#define fgets chnfilegets
#define fclose chnfileclose
======
And replace the final ``if( !readStdin ){'' block in ``main'':
======
FILE *in;
in = chnfileopen(nCmd, azCmd);
if( in==0 ){
utf8_printf(stderr, "Error: cannot process command line\n");
rc = 1;
} else {
rc = process_input(&data, in);
fclose(in);
}
free(azCmd);
======
A bit dirty, but works fine though not extremely tested. A cleaner
solution probably requires a bit more work, but due to a shell's thrift
it is quite easy (there is a simple, non--forking flow: main =>
process_input => one_input_line => local_getline => fgets).
-- best regards
Cezary H. Noweta
_______________________________________________
sqlite-users mailing list
sqlite-users@mailinglists.sqlite.org
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users