-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
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
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
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.
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 */
}
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;
}
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
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);
}
}
/*
-** 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
** 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,
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
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);
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);
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
}
/*
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
.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