mirror of
https://github.com/sqlite/sqlite.git
synced 2026-10-23 21:49:02 +03:00
Improvements to the non-printing character delimiter logic in the CLI
prompt, for improved accuracy and for improved testability. FossilOrigin-Name: f5649795cd8d7a1f270037ef0b212ea605c1665ec05a75a6da0b250f2aeab070
This commit is contained in:
14
manifest
14
manifest
@@ -1,5 +1,5 @@
|
||||
C Change\sthe\snon-printing\scharacter\sescapes\sfor\seditline\sto\sU+0001\sand\nU+0002.\s\sAlso\sinsert\sthose\sescapes\sfor\sreadline.\s\sReadline\sdoes\snot\nneed\sit\s(because\sit\scan\sfigure\sthem\sout\son\sits\sown)\sbut\sthey\sare\sneeded\nwhen\seditline\sis\sbeing\sused\sin\sreadline-compatibility\smode,\sand\swe\shave\nno\sway\sof\sdistinguishing\sbetween\seditline\sand\sreadline\sin\sthat\scase,\sso\nwe\smight\sas\swell\sinclude\sthem.
|
||||
D 2026-05-02T23:40:40.608
|
||||
C Improvements\sto\sthe\snon-printing\scharacter\sdelimiter\slogic\sin\sthe\sCLI\nprompt,\sfor\simproved\saccuracy\sand\sfor\simproved\stestability.
|
||||
D 2026-05-03T15:53:52.474
|
||||
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
@@ -736,7 +736,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
|
||||
F src/resolve.c fcc406bfb055bee9954ee77c023f4a2a66a24bcdf1573516a72280811a269c20
|
||||
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
|
||||
F src/select.c 4c05cde130f26991b7411d8c6809e0630625e18078742c963a047b4b9cc01d49
|
||||
F src/shell.c.in 28e4b97a6746973c935b8510c7926ee9fe3d03bb35056064e9a3b7cb29c73178
|
||||
F src/shell.c.in e0eb5d8c91b0fa29dc25b4511e46c28d800a11e94d8763a58610983e4a120810
|
||||
F src/sqlite.h.in 39d2e09114d2bdb7afd998f4a469c8f8cd065f8093835a7d0422f260fc78fb4f
|
||||
F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
|
||||
F src/sqlite3ext.h 9788c301f95370fa30e808861f1d2e6f022a816ddbe2a4f67486784c1b31db2e
|
||||
@@ -1624,7 +1624,7 @@ F test/sharedA.test 64bdd21216dda2c6a3bd3475348ccdc108160f34682c97f2f51c19fc0e21
|
||||
F test/sharedB.test 1a84863d7a2204e0d42f2e1606577c5e92e4473fa37ea0f5bdf829e4bf8ee707
|
||||
F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939
|
||||
F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
|
||||
F test/shell-prompt.sql 868ca1711677ada45720532fe23dbbbe881f3612dc29692dac45b19129648b3f
|
||||
F test/shell-prompt.sql 96c055ac09aff7f96b7e2bca8f0192c36ed34d11df4cf4a58b9d4540b387ff3f
|
||||
F test/shell1.test c84eff209f93ad17ccdf7e1634969fc8231684254edeb21d9b13d67c3179cdb5
|
||||
F test/shell2.test dc541d2681503e55466a24d35a4cbf8ca5b90b8fcdef37fc4db07373a67d31d3
|
||||
F test/shell3.test 91efdd545097a61a1f72cf79c9ad5b49da080f3f10282eaf4c3c272cd1012db2
|
||||
@@ -2203,8 +2203,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee
|
||||
F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
|
||||
P 63050bee6e5ed46cead14bd5a02b0cb6a7dbcdcb6a56601b90c10cf1c8d84efc
|
||||
R 3d27d61ea4b8d5c891af38a2c8c58401
|
||||
P b9545b0f0b6fa0a777a064e1410b17f52cb49d853c188f9b8b3a772bd810ece3
|
||||
R 2722a106bcabd9853ab1b6613af68b1b
|
||||
U drh
|
||||
Z f6ed14e12eebf843b747f8d53d83a51a
|
||||
Z 4349d7842f8a2c527da152c639edc6d0
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
||||
@@ -1 +1 @@
|
||||
b9545b0f0b6fa0a777a064e1410b17f52cb49d853c188f9b8b3a772bd810ece3
|
||||
f5649795cd8d7a1f270037ef0b212ea605c1665ec05a75a6da0b250f2aeab070
|
||||
|
||||
@@ -382,6 +382,7 @@ struct ShellState {
|
||||
u8 nPopOutput; /* Revert .output settings when reaching zero */
|
||||
u8 nPopMode; /* Revert .mode settings when reaching zero */
|
||||
u8 enableTimer; /* Enable the timer. 2: permanently 1: only once */
|
||||
u8 bDelimitNonprint; /* Add \001...\002 around non-printing in prompts */
|
||||
int inputNesting; /* Track nesting level of .read and other redirects */
|
||||
double prevTimer; /* Last reported timer value */
|
||||
double tmProgress; /* --timeout option for .progress */
|
||||
@@ -1036,9 +1037,9 @@ static const char *prompt_filename(ShellState *p, const char *zMemoryName){
|
||||
}
|
||||
if( zFN==0 || zFN[0]==0 ){
|
||||
zFN = p->pAuxDb->zDbFilename;
|
||||
if( zFN==0 || zFN[0]==0 || cli_strcmp(zFN,":memory:")==0 ){
|
||||
zFN = zMemoryName;
|
||||
}
|
||||
}
|
||||
if( zFN==0 || zFN[0]==0 || cli_strcmp(zFN,":memory:")==0 ){
|
||||
zFN = zMemoryName;
|
||||
}
|
||||
return zFN;
|
||||
}
|
||||
@@ -1082,6 +1083,22 @@ static const char *prompt_user(void){
|
||||
return z;
|
||||
}
|
||||
|
||||
/* If z[] begins with one or more ANSI X3.64 (VT100) escape sequences
|
||||
** return the number of bytes in all such escape sequences. Return
|
||||
** zero if there are no valid escape sequences.
|
||||
*/
|
||||
static int nAnsiEscape(const char *z){
|
||||
int i = 0;
|
||||
while( z[i]=='\033' && z[i+1]=='[' ){
|
||||
int k = i+2;
|
||||
while( z[k]>=0x30 && z[k]<=0x3f ){ k++; }
|
||||
while( z[k]>=0x20 && z[k]<=0x2f ){ k++; }
|
||||
if( z[k]<0x40 || z[k]>0x7e ) break;
|
||||
i = k+1;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
** Expand escapes in the given input prompt string. Return the
|
||||
** expanded prompt in memory obtained from sqlite3_malloc(). The
|
||||
@@ -1357,36 +1374,26 @@ static char *expand_prompt(
|
||||
sqlite3_str_appendchar(pOut, 1, '\0');
|
||||
}
|
||||
|
||||
#if HAVE_EDITLINE || HAVE_READLINE
|
||||
/* Editline does not recognize ANSI X3.64 escape sequences. So we have
|
||||
** to find them all and enclose them inside '\1'...'\2' delimiters.
|
||||
** Readline does not require this crutch, but it does not hurt to include
|
||||
** it, for the cases where editline is running in readline compatibility
|
||||
** mode.
|
||||
** to find them all and enclose them inside '\001'...'\002' delimiters.
|
||||
** Some versions of readline also require this, but others do not.
|
||||
*/
|
||||
if( strstr(sqlite3_str_value(pOut),"\033[")!=0 ){
|
||||
if( p->bDelimitNonprint && strstr(sqlite3_str_value(pOut),"\033[")!=0 ){
|
||||
char *zOrig = sqlite3_str_finish(pOut);
|
||||
int n;
|
||||
pOut = sqlite3_str_new(0);
|
||||
for(i=0; zOrig[i]; i++){
|
||||
if( zOrig[i]=='\033' && zOrig[i+1]=='[' ){
|
||||
int k = i+2;
|
||||
if( zOrig[i]=='\033' && (n = nAnsiEscape(zOrig+i))>0 ){
|
||||
sqlite3_str_appendchar(pOut, 1, 1);
|
||||
while( 1 /*exit-by-break*/ ){
|
||||
for( ; zOrig[k]>=0x20 && zOrig[k]<=0x3f; k++){}
|
||||
if( zOrig[k]>=0x40 ) k++;
|
||||
if( zOrig[k]!='\033' || zOrig[k+1]!='[' || zOrig[k+2]==0 ) break;
|
||||
k += 2;
|
||||
}
|
||||
sqlite3_str_append(pOut, &zOrig[i], k-i);
|
||||
sqlite3_str_append(pOut, &zOrig[i], n);
|
||||
sqlite3_str_appendchar(pOut, 1, 2);
|
||||
i = k-1;
|
||||
i += n-1;
|
||||
}else{
|
||||
sqlite3_str_appendchar(pOut, 1, zOrig[i]);
|
||||
}
|
||||
}
|
||||
sqlite3_free(zOrig);
|
||||
}
|
||||
#endif
|
||||
|
||||
return sqlite3_str_finish(pOut);
|
||||
}
|
||||
@@ -1762,9 +1769,7 @@ static void shellAddSchemaName(
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function: shell_prompt_test(PROMPT)
|
||||
** shell_prompt_test(PROMPT,PRIOR)
|
||||
** shell_prompt_test(PROMPT,PRIOR,FILENAME)
|
||||
** SQL function: shell_prompt_test(PROMPT,PRIOR,FILENAME,FLAGS)
|
||||
**
|
||||
** Return the shell prompt, with escapes expanded, for testing purposes.
|
||||
** The first argument is the raw (unexpanded) prompt string. Or if the
|
||||
@@ -1772,7 +1777,15 @@ static void shellAddSchemaName(
|
||||
** configured. If the second argument exists and is not NULL, then the
|
||||
** second argument is understood to be prior incomplete text and a
|
||||
** continuation prompt is generated. If a third argument is provided,
|
||||
** it is assumed to be the full pathname of the database file.
|
||||
** it is assumed to be the full pathname of the database file. The
|
||||
** fourth argument, if provided, is an integer of flags:
|
||||
**
|
||||
** 0x0001 Always insert \001..\002 delimiters around ANSI escapes
|
||||
** 0x0002 Never insert \001..\002 delimiters
|
||||
**
|
||||
** This function is for testing purposes only. The interface may change.
|
||||
** The function itself might be renamed or removed in future releases. Do
|
||||
** not use this function in applications.
|
||||
*/
|
||||
static void shellExpandPrompt(
|
||||
sqlite3_context *pCtx,
|
||||
@@ -1786,7 +1799,10 @@ static void shellExpandPrompt(
|
||||
int mSavedFlgs;
|
||||
const char *zFName;
|
||||
char *zRes;
|
||||
int mFlags;
|
||||
char bSavedDelimit = p->bDelimitNonprint;
|
||||
|
||||
if( nVal<1 ) return;
|
||||
if( nVal<2
|
||||
|| (zPrior = (const char*)sqlite3_value_text(apVal[1]))==0
|
||||
|| zPrior[0]==0
|
||||
@@ -1803,7 +1819,14 @@ static void shellExpandPrompt(
|
||||
p->pAuxDb->zDbFilename = zFName;
|
||||
p->pAuxDb->mFlgs |= 0x001;
|
||||
}
|
||||
mFlags = nVal>=4 ? sqlite3_value_int(apVal[3]) : 0;
|
||||
if( mFlags & 0x0001 ){
|
||||
p->bDelimitNonprint = 1;
|
||||
}else if( mFlags & 0x0002 ){
|
||||
p->bDelimitNonprint = 0;
|
||||
}
|
||||
zRes = expand_prompt(p, zPrior, zPrompt);
|
||||
p->bDelimitNonprint = bSavedDelimit;
|
||||
p->pAuxDb->zDbFilename = zSavedDbFile;
|
||||
p->pAuxDb->mFlgs = mSavedFlgs;
|
||||
sqlite3_result_text(pCtx, zRes, -1, SQLITE_TRANSIENT);
|
||||
@@ -5071,11 +5094,7 @@ static void open_db(ShellState *p, int openFlags){
|
||||
sqlite3_create_function(p->db, "edit", 2, SQLITE_UTF8, 0,
|
||||
editFunc, 0, 0);
|
||||
#endif
|
||||
sqlite3_create_function(p->db, "shell_prompt_test", 1, SQLITE_UTF8,
|
||||
p, shellExpandPrompt, 0, 0);
|
||||
sqlite3_create_function(p->db, "shell_prompt_test", 2, SQLITE_UTF8,
|
||||
p, shellExpandPrompt, 0, 0);
|
||||
sqlite3_create_function(p->db, "shell_prompt_test", 3, SQLITE_UTF8,
|
||||
sqlite3_create_function(p->db, "shell_prompt_test", -1, SQLITE_UTF8,
|
||||
p, shellExpandPrompt, 0, 0);
|
||||
sqlite3_create_function(p->db, "shell_temp_filename", 1, SQLITE_UTF8,
|
||||
p, shellTempFilenameFunc, 0, 0);
|
||||
@@ -13389,6 +13408,16 @@ static void main_init(ShellState *p) {
|
||||
sqlite3_config(SQLITE_CONFIG_URI, 1);
|
||||
sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
|
||||
globalShellState = p;
|
||||
#if HAVE_EDITLINE || HAVE_READLINE
|
||||
/* Editline requires \001...\002 delimiters around ANSI x3.64 escapes in
|
||||
** prompt strings. Readline does sometimes, depending on how it is
|
||||
** compiled and installed. */
|
||||
p->bDelimitNonprint = 1;
|
||||
#else
|
||||
/* No \001...\002 escapes required for linenoise or when not using a
|
||||
** command-line editing library */
|
||||
p->bDelimitNonprint = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -95,25 +95,26 @@ SELECT shell_prompt_test(NULL);
|
||||
SELECT shell_prompt_test(NULL,'SELECT');
|
||||
.check --glob ' *;*-> ';
|
||||
.testcase 1002
|
||||
SELECT shell_prompt_test(NULL,'SELECT ((("');
|
||||
SELECT shell_prompt_test(NULL,'SELECT ((("',null,2);
|
||||
.check --glob ' *[ m]")));*-> ';
|
||||
.testcase 1003
|
||||
SELECT shell_prompt_test(NULL,'SELECT ((()[');
|
||||
SELECT shell_prompt_test(NULL,'SELECT ((()[',null,2);
|
||||
.check --glob ' *[ m]]));*-> ';
|
||||
.testcase 1004
|
||||
SELECT shell_prompt_test(NULL,'SELECT ''');
|
||||
SELECT shell_prompt_test(NULL,'SELECT ''',null,2);
|
||||
.check --glob " *[ m]';*-> ";
|
||||
.testcase 1005
|
||||
SELECT shell_prompt_test(NULL,'CREATE TRIGGER t1 BEGIN');
|
||||
SELECT shell_prompt_test(NULL,'CREATE TRIGGER t1 BEGIN',null,2);
|
||||
.check --glob " *[ m];END;*-> ";
|
||||
.testcase 1006
|
||||
SELECT shell_prompt_test(NULL,'CREATE TRIGGER t1 BEGIN SELECT ((([');
|
||||
SELECT shell_prompt_test(NULL,'CREATE TRIGGER t1 BEGIN SELECT ((([',null,2);
|
||||
.check --glob " *[ m]])));END;*-> ";
|
||||
.testcase 1007
|
||||
SELECT shell_prompt_test(NULL,'CREATE TRIGGER t1 BEGIN SELECT ((/*a(((''bc');
|
||||
SELECT shell_prompt_test(NULL,'CREATE TRIGGER t1 BEGIN SELECT ((/*a(((''bc',
|
||||
null,2);
|
||||
.check --glob " *[ m][*]/));END;*-> ";
|
||||
.testcase 1008
|
||||
SELECT shell_prompt_test(NULL,'CREATE TRIGGER t1 BEGIN SELECT 1;');
|
||||
SELECT shell_prompt_test(NULL,'CREATE TRIGGER t1 BEGIN SELECT 1;',null,2);
|
||||
.check --glob " *[ m]END;*-> ";
|
||||
|
||||
.testcase 2000
|
||||
@@ -146,3 +147,31 @@ SELECT shell_prompt_test('(/A-/v)');
|
||||
.testcase 3002
|
||||
SELECT shell_prompt_test('(/A-/D%Y-%m-%dT%H:%M:%S/D)');
|
||||
.check --glob '(SQLite-20#-#-#T#:#:#)'
|
||||
|
||||
.testcase 4000
|
||||
.mode list -rowsep '' -escape ascii
|
||||
SELECT shell_prompt_test('</myes/:no/;>',NULL,':memory:');
|
||||
.check <yes>
|
||||
.testcase 4001
|
||||
.mode list -rowsep '' -escape ascii
|
||||
SELECT shell_prompt_test('</myes/:no/;>',NULL,'t1.db');
|
||||
.check <no>
|
||||
|
||||
.testcase 5000
|
||||
SELECT shell_prompt_test('abc/e[0mxyz',NULL,NULL,1);
|
||||
.check abc^A^[[0m^Bxyz
|
||||
.testcase 5001
|
||||
SELECT shell_prompt_test('abc/e[0mxyz',NULL,NULL,2);
|
||||
.check abc^[[0mxyz
|
||||
.testcase 5002
|
||||
SELECT shell_prompt_test('/e[0mxyz',NULL,NULL,1);
|
||||
.check ^A^[[0m^Bxyz
|
||||
.testcase 5003
|
||||
SELECT shell_prompt_test('abc/e[0m',NULL,NULL,1);
|
||||
.check abc^A^[[0m^B
|
||||
.testcase 5004
|
||||
SELECT shell_prompt_test('abc/e[1;32m/e[0m/e[/exyz',NULL,NULL,1);
|
||||
.check abc^A^[[1;32m^[[0m^B^[[^[xyz
|
||||
.testcase 5005
|
||||
SELECT shell_prompt_test('abc/e[1,32m/e[0m/e[/exyz',NULL,NULL,1);
|
||||
.check abc^[[1,32m^A^[[0m^B^[[^[xyz
|
||||
|
||||
Reference in New Issue
Block a user