]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
New CLI prompt escape sequences: /r, /A, /v, /V, and /D../D.
authordrh <>
Thu, 23 Apr 2026 15:05:46 +0000 (15:05 +0000)
committerdrh <>
Thu, 23 Apr 2026 15:05:46 +0000 (15:05 +0000)
FossilOrigin-Name: e7c7acd1a4e5cb0f8e68f119352480c1aa9dd3c2b289c92f88e9e697a2e440e0

manifest
manifest.uuid
src/shell.c.in
test/shell-prompt.sql

index 933c581773249aeb24d58ea6d712364d7a24f41c..445e8014d5bcad2e3462cd52eb2a6d2be335b49b 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sharmless\scompiler\swarning.
-D 2026-04-23T14:49:21.443
+C New\sCLI\sprompt\sescape\ssequences:\s\s/r,\s/A,\s/v,\s/V,\sand\s/D../D.
+D 2026-04-23T15:05:46.973
 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 928ff887f2a7c64275182060d94d06fdddbe32226c569781cf7e7edc6f58d7fd
 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
 F src/select.c 4c05cde130f26991b7411d8c6809e0630625e18078742c963a047b4b9cc01d49
-F src/shell.c.in 20e359ac77d07e5dc636e526ef85da692384228e7997d7ec28568125a028a8f7
+F src/shell.c.in ae78db9539da5f8ada1ac2761b77b865e56ec2ba9145461e56de6eb762aeef18
 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 43ed98433bfde83171b5d06c300525154f4cab9230570a326ca606aaef351db6
+F test/shell-prompt.sql c3517832b8570025619707bf5883847f6894e958bd92a5e0d2e2f157f768c2a1
 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 3fa0500b964869c45f7b49717680b791049f7cd8c47a06a337dd22351d2002f9
-R 71b891a74b506696ae0597a74a57a86c
+P 706373dcbe43752cf21604f278f96fb3015a36089ad64f069bb3a64bc526d137
+R d97f1546f6e3e8815d53cf5cfb6143b0
 U drh
-Z 6b30685bdb9a615afb7f74468ca1c1e4
+Z b0844759ca50f5075fba7f38fd3b6ac9
 # Remove this line to create a well-formed Fossil manifest.
index c05a2d1eeaae51edd1038abac8b29af4c71a6f02..3d05c96a113fd4518f6e7d40530535d26be22ec4 100644 (file)
@@ -1 +1 @@
-706373dcbe43752cf21604f278f96fb3015a36089ad64f069bb3a64bc526d137
+e7c7acd1a4e5cb0f8e68f119352480c1aa9dd3c2b289c92f88e9e697a2e440e0
index 6217223e223b8c140d4c533a2f583833167901da..52233de2959faea685d886bc98c4084e3afef6f6 100644 (file)
@@ -921,12 +921,25 @@ static char *local_getline(char *zLine, FILE *in){
 ** The default prompts.
 */
 #ifndef SQLITE_PS1
-# define SQLITE_PS1 "SQLite /f> "
+# define SQLITE_PS1 "/A /f> "
 #endif
 #ifndef SQLITE_PS2
 # define SQLITE_PS2 "/B.../H> "
 #endif
 
+/*
+** Redefinable escape value for the prompt string.
+*/
+#ifndef SQLITE_PS_APP
+# define SQLITE_PS_APP "SQLite"                      /* Expansion for /A */
+#endif
+#ifndef SQLITE_PS_RELEASE
+# define SQLITE_PS_VERSION_RELEASE shellRelease()    /* Expansion for /v */
+#endif
+#ifndef SQLITE_PS_PATCH
+# define SQLITE_PS_VERSION_PATCH   shellPatch()      /* Expansion for /V */
+#endif
+
 /*
 ** Return the raw (unexpanded) prompt string.  This will be the
 ** first of the following that exist:
@@ -976,6 +989,20 @@ static const char *prompt_filename(ShellState *p){
   return zFN;
 }
 
+/*
+** Return strings appropriate to substitute for /V and /v
+*/
+static const char *shellPatch(void){ return sqlite3_libversion(); }
+static const char *shellRelease(void){
+  static char zRel[16];
+  const char *zF = shellPatch();
+  const char *zD = strrchr(zF,'.');
+  int i = (int)(zD - zF);
+  memcpy(zRel,zF,i);
+  zRel[i] = 0;
+  return zRel;
+}
+
 /*
 ** Expand escapes in the given input prompt string.  Return the
 ** expanded prompt in memory obtained from sqlite3_malloc().  The
@@ -997,12 +1024,13 @@ static char *expand_prompt(
   sqlite3_str *pOut = sqlite3_str_new(0);
   int i;
   char c;
-  int onoff = 1;
+  unsigned int mOff = 0;    /* Bitmask of FALSE for if/then/else */
   int idxSpace = -1;
+  int iDate = -1;
   for(i=0; zPrompt[i]; i++){
     if( zPrompt[i]!='/' ) continue;
     if( i>0 ){
-      if( onoff ) sqlite3_str_append(pOut, zPrompt, i);
+      if( !mOff ) sqlite3_str_append(pOut, zPrompt, i);
       zPrompt += i;
       i = 0;
     }
@@ -1025,14 +1053,36 @@ static char *expand_prompt(
       while( i<=2 && zPrompt[i+1]>='0' && zPrompt[i+1]<='7' ){
         v = v*8 + zPrompt[++i] - '0';
       }
-      if( onoff ) sqlite3_str_appendchar(pOut, 1, v);
+      if( !mOff ) sqlite3_str_appendchar(pOut, 1, v);
       zPrompt += i+1;
       i = -1;
       continue;
     }
     if( c=='e' ){
       /* /e is shorthand for /033 which is U+001B "Escape" */
-      if( onoff ) sqlite3_str_append(pOut, "\033", 1);
+      if( !mOff ) sqlite3_str_append(pOut, "\033", 1);
+      zPrompt += 2;
+      i = -1;
+      continue;
+    }
+    if( c=='A' ){
+      /* /A expands to the application name */
+      if( !mOff ) sqlite3_str_appendall(pOut, SQLITE_PS_APP);
+      zPrompt += 2;
+      i = -1;
+      continue;
+    }
+    if( c=='V' ){
+      /* /V expands to the version number with patch level */
+      if( !mOff ) sqlite3_str_appendall(pOut, SQLITE_PS_VERSION_PATCH);
+      zPrompt += 2;
+      i = -1;
+      continue;
+    }
+    if( c=='v' ){
+      /* /v expands to the version number without the patch level */
+      /* /V expands to the version number with patch level */
+      if( !mOff ) sqlite3_str_appendall(pOut, SQLITE_PS_VERSION_RELEASE);
       zPrompt += 2;
       i = -1;
       continue;
@@ -1052,22 +1102,29 @@ static char *expand_prompt(
     **         .prompt '/e[1;/x31/:34/;m~f>/e[0m '
     */
     if( c==':' ){
-      /* toggle display on/off */
-      onoff = !onoff;
+      /* ELSE: toggle display on/off */
+      mOff ^= 1;
       zPrompt += 2;
       i = -1;
       continue;
     }
     if( c==';' ){
-      /* Turn display on */
-      onoff = 1;
+      /* ENDIF: Turn display on */
+      mOff >>= 1;
       zPrompt += 2;
       i = -1;
       continue;
     }
     if( c=='x' ){
       /* /x turns display off not in a transaction, on if in txn */
-      onoff = p->db && !sqlite3_get_autocommit(p->db);
+      mOff = (mOff<<1) | (p->db==0 || sqlite3_get_autocommit(p->db)!=0);
+      zPrompt += 2;
+      i = -1;
+      continue;
+    }
+    if( c=='r' ){
+      /* /r turns display off if database is read/write, on if read-only */
+      mOff = (mOff<<1) | (p->db==0 || sqlite3_db_readonly(p->db,0)==0);
       zPrompt += 2;
       i = -1;
       continue;
@@ -1077,7 +1134,7 @@ static char *expand_prompt(
       /* /f becomes the tail of the database filename */
       /* /F becomes the full pathname */
       /* /~ becomes the full pathname relative to $HOME */
-      if( onoff ){
+      if( !mOff ){
         const char *zFN = prompt_filename(p);
         if( c=='f' ){
 #ifdef _WIN32
@@ -1106,7 +1163,7 @@ static char *expand_prompt(
 
     if( c=='H' ){
       /* /H becomes text needed to terminate current input */
-      if( onoff ){
+      if( !mOff ){
         sqlite3_int64 R = zPrior ? sqlite3_incomplete(zPrior) : 0;
         int cc = (R>>16)&0xff;
         int nParen = R>>32;
@@ -1140,18 +1197,44 @@ static char *expand_prompt(
       /* /B is a no-op for the main prompt.  For the continuation prompt,
       ** /B expands to zero or more spaces to make the continuation prompt
       ** at least as wide as the main prompt. */
-      if( onoff ) idxSpace = sqlite3_str_length(pOut);
+      if( !mOff ) idxSpace = sqlite3_str_length(pOut);
+      zPrompt += 2;
+      i = -1;
+      continue;
+    }
+
+    if( c=='D' ){
+      /* /D.../D replaces all of the text between the two /D escapes with
+      ** the result from strftime(). */
+      if( iDate<0 ){
+        iDate = sqlite3_str_length(pOut);
+      }else{
+        if( !mOff ){
+          time_t now;
+          char zBuf[200];
+          zBuf[0] = 0;
+          time(&now);
+          strftime(zBuf, sizeof(zBuf)-1, sqlite3_str_value(pOut)+iDate, 
+                   localtime(&now));
+          zBuf[199] = 0;
+          sqlite3_str_truncate(pOut, iDate);
+          sqlite3_str_appendall(pOut, zBuf);
+        }
+        iDate = -1;
+      }
       zPrompt += 2;
       i = -1;
       continue;
     }
 
-    /* No match to a known escape.  Generate an error. */
-    if( onoff ) sqlite3_str_appendf(pOut,"UNKNOWN(\"/%c\")",c);
+    /* No match to a known escape.  Generate an error. The mOff flag
+    ** is ignored for this output, so that errors appear even if they
+    ** are in an unused branch. */
+    sqlite3_str_appendf(pOut,"UNKNOWN(\"/%c\")",c);
     zPrompt += 2;
     i = -1;
   }
-  if( i>0 && onoff ){
+  if( i>0 && !mOff ){
     sqlite3_str_append(pOut, zPrompt, i);
   }
 
index c1f3bfa6cfcb33368a2cc703b6bb1d43b05dedd0..ebb00114752ce1ccdfdac4f2ec0a58f1097ebe6d 100644 (file)
@@ -29,7 +29,7 @@
 .testcase 110
 .prompt --show
 .check <<END
-Main prompt:  'SQLite /f> '
+Main prompt:  '/A /f> '
 Continuation: '/B.../H> '
 END
 .testcase 111
@@ -48,7 +48,7 @@ END
 .testcase 113
 .prompt --reset --show
 .check <<END
-Main prompt:  'SQLite /f> '
+Main prompt:  '/A /f> '
 Continuation: '/B.../H> '
 END
 
@@ -137,4 +137,12 @@ SELECT shell_prompt_test(NULL);
 SELECT shell_prompt_test(NULL);
 .check 'SQLite test.db> ';
 
-.testcase
+.testcase 3000
+SELECT shell_prompt_test('(/A-/V)');
+.check --glob '(SQLite-3.#.#)'
+.testcase 3001
+SELECT shell_prompt_test('(/A-/v)');
+.check --glob '(SQLite-3.#)'
+.testcase 3002
+SELECT shell_prompt_test('(/A-/D%Y-%m-%dT%H:%M:%S/D)');
+.check --glob '(SQLite-20#-#-#T#:#:#)'