]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add "--wordwrap on/off" option for CLI columnar modes, qwbox shortcut
authorlarrybr <larrybr@noemail.net>
Tue, 1 Feb 2022 02:50:45 +0000 (02:50 +0000)
committerlarrybr <larrybr@noemail.net>
Tue, 1 Feb 2022 02:50:45 +0000 (02:50 +0000)
FossilOrigin-Name: 10dbc278708cd2cce7fef90738082dbe31750d93e44b5fa5413a9a32dae7703a

manifest
manifest.uuid
src/shell.c.in
test/shell1.test

index 9d4ac64543fad91005a0eff8facc6a314e4bff7b..6128f7de2e7d2972f9ac79769961adfd88497aaa 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C CLI:\sIn\s".mode\scolumn"\soutput,\sif\sany\srow\scontains\sa\snewline\sor\swraps,\sthen\nput\sa\ssingle\sblank\sline\sin\sbetween\seach\spair\sof\srows\sto\sprovide\sadditional\nvisual\sseparately.
-D 2022-02-01T00:00:08.128
+C Add\s"--wordwrap\son/off"\soption\sfor\sCLI\scolumnar\smodes,\sqwbox\sshortcut
+D 2022-02-01T02:50:45.569
 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 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9
 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
 F src/select.c a6d2d4bed279d7fe4fcedaf297eaf6441e8e17c6e3947a32d24d23be52ac02f2
-F src/shell.c.in 8ae0e33c265c14a6932233b2eb37b090a67a3cb816671da66d26319b1d98cbfd
+F src/shell.c.in 4f5e0a9f38aa648ca529efb66f338308262b47a72b6c5c95a1f704619fa1aef0
 F src/sqlite.h.in eaade58049152dac850d57415bcced885ca27ae9582f8aea2cfb7f1db78a521b
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h 5d54cf13d3406d8eb65d921a0d3c349de6126b732e695e79ecd4830ce86b4f8a
@@ -1386,7 +1386,7 @@ F test/sharedA.test 49d87ec54ab640fbbc3786ee3c01de94aaa482a3a9f834ad3fe92770eb69
 F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e
 F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939
 F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
-F test/shell1.test ce2f370886645f38fabdde44976c14a004400f166edea8fdd9741079b645fef6
+F test/shell1.test 1859ba21623b6a804ab2527b2a7c10b54513e8c4e40cec2b462d0e9f082d4fec
 F test/shell2.test f00a0501c00583cbc46f7510e1d713366326b2b3e63d06d15937284171a8787c
 F test/shell3.test cb4b835a901742c9719437a89171172ecc4a8823ad97349af8e4e841e6f82566
 F test/shell4.test 8f6c0fce4abed19a8a7f7262517149812a04caa905d01bdc8f5e92573504b759
@@ -1942,8 +1942,9 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 070fae3a09cea675e722340870cb2bee9e1ac96954d3baacfdb7f5400ddb1b20
-R da80598e94d04369e4a04ce4c1915aa8
-U drh
-Z 366282a4b771bc969cd1f7f43a0d9728
+P fd42f4c304079356358e606dd96d4b84cf211c4334c586118b99fe9ad20e20ea
+Q +f51a17b6271a8dd7c48725e4ec2df1fde0460866c81c7225dc27216ab389591e
+R 9664e5ccc659386bbaeacc1cefed4830
+U larrybr
+Z c1cf64e5874e5b6ab186498f765ac9eb
 # Remove this line to create a well-formed Fossil manifest.
index 30a779ddff3bb0795c91af635cbb03ea0a56be34..53a5bb21c2ffbe3c861380a675ead96738073925 100644 (file)
@@ -1 +1 @@
-fd42f4c304079356358e606dd96d4b84cf211c4334c586118b99fe9ad20e20ea
\ No newline at end of file
+10dbc278708cd2cce7fef90738082dbe31750d93e44b5fa5413a9a32dae7703a
\ No newline at end of file
index f59122c71722f96e12bcec3649c41d1d50640e5f..25afa79910edd07ad5b40ec4961b087ebb2a2a69 100644 (file)
@@ -1066,6 +1066,16 @@ struct EQPGraph {
   char zPrefix[100];    /* Graph prefix */
 };
 
+/* Parameters affecting columnar mode result display (defaulting together) */
+typedef struct ColModeOpts {
+  int iWrap;            /* In columnar modes, wrap lines reaching this limit */
+  u8 bQuote;            /* Quote results for .mode box and table */
+  u8 bWordWrap;         /* In columnar modes, wrap at word boundaries  */
+} ColModeOpts;
+#define ColModeOpts_default { 60, 0, 0 }
+#define ColModeOpts_default_qbox { 60, 1, 0 }
+#define ColModeOpts_default_qwbox { 60, 1, 1 }
+
 /*
 ** State information about the database connection is contained in an
 ** instance of the following structure.
@@ -1084,8 +1094,7 @@ struct ShellState {
   u8 eTraceType;         /* SHELL_TRACE_* value for type of trace */
   u8 bSafeMode;          /* True to prohibit unsafe operations */
   u8 bSafeModePersist;   /* The long-term value of bSafeMode */
-  u8 bQuote;             /* Quote results for .mode box and table */
-  int iWrap;             /* Wrap lines this long or longer in some output modes */
+  ColModeOpts cmOpts;    /* Option values affecting columnar mode output */
   unsigned statsOn;      /* True to display memory stats before each finalize */
   unsigned mEqpLines;    /* Mask of veritical lines in the EQP output graph */
   int inputNesting;      /* Track nesting level of .read and other redirects */
@@ -3173,16 +3182,18 @@ static void print_box_row_separator(
 **
 ** Compute characters to display on the first line of z[].  Stop at the
 ** first \r, \n, or \f.  Expand \t into spaces.  Return a copy (obtained
-** from malloc()) of that first line.  Write anything to display
-** on the next line into *pzTail.  If this is the last line, write a NULL
-** into *pzTail.
+** from malloc()) of that first line, which caller should free sometime.
+** Write anything to display on the next line into *pzTail.  If this is
+** the last line, write a NULL into *pzTail. (*pzTail is not allocated.)
 */
 static char *translateForDisplayAndDup(
   const unsigned char *z,
   const unsigned char **pzTail,
-  int mxWidth
+  int mxWidth,
+  u8 bWordWrap
 ){
-  int i, j, n;
+  int i, j, n; /* in-index, code-skip, code-count */
+  int iLastWhite = 0, nLastWhite = 0;
   unsigned char *zOut;
   if( z==0 ){
     *pzTail = 0;
@@ -3194,6 +3205,10 @@ static char *translateForDisplayAndDup(
   while( n<mxWidth ){
     if( z[i]>=' ' ){
       n++;
+      if( IsSpace(z[i]) ){
+        iLastWhite = i;
+        nLastWhite = n;
+      }
       do{ i++; j++; }while( (z[i]&0xc0)==0x80 );
       continue;
     }
@@ -3207,6 +3222,11 @@ static char *translateForDisplayAndDup(
     }
     break;
   }
+  if( bWordWrap && iLastWhite>0 && n>=mxWidth ){
+    /* Will word wrap only if it is requested and can do any good. */
+    mxWidth = nLastWhite;
+    i = iLastWhite;
+  }
   if( n>=mxWidth && z[i]>=' ' ){
    *pzTail = &z[i];
   }else if( z[i]=='\r' && z[i+1]=='\n' ){
@@ -3312,7 +3332,7 @@ static void exec_prepared_stmt_columnar(
   azNextLine = sqlite3_malloc64( nColumn*sizeof(char*) );
   shell_check_oom((void*)azNextLine);
   memset((void*)azNextLine, 0, nColumn*sizeof(char*) );
-  if( p->bQuote ){
+  if( p->cmOpts.bQuote ){
     azQuoted = sqlite3_malloc64( nColumn*sizeof(char*) );
     shell_check_oom(azQuoted);
     memset(azQuoted, 0, nColumn*sizeof(char*) );
@@ -3334,11 +3354,15 @@ static void exec_prepared_stmt_columnar(
   }
   for(i=0; i<nColumn; i++){
     const unsigned char *zNotUsed;
+    u8 bw = 0;
     int wx = p->colWidth[i];
-    if( wx==0 ) wx = p->iWrap;
+    if( wx==0 ){
+      wx = p->cmOpts.iWrap;
+      bw = p->cmOpts.bWordWrap;
+    }
     if( wx<0 ) wx = -wx;
     uz = (const unsigned char*)sqlite3_column_name(pStmt,i);
-    azData[i] = translateForDisplayAndDup(uz, &zNotUsed, wx);
+    azData[i] = translateForDisplayAndDup(uz, &zNotUsed, wx, bw);
   }
   do{
     int useNextLine = bNextLine;
@@ -3353,19 +3377,24 @@ static void exec_prepared_stmt_columnar(
     abRowDiv[nRow] = 1;
     nRow++;
     for(i=0; i<nColumn; i++){
+      u8 bw = 0;
       int wx = p->colWidth[i];
-      if( wx==0 ) wx = p->iWrap;
+      if( wx==0 ){
+        wx = p->cmOpts.iWrap;
+        bw = p->cmOpts.bWordWrap;
+      }
       if( wx<0 ) wx = -wx;
       if( useNextLine ){
         uz = azNextLine[i];
-      }else if( p->bQuote ){
+      }else if( p->cmOpts.bQuote ){
         sqlite3_free(azQuoted[i]);
         azQuoted[i] = quoted_column(pStmt,i);
         uz = (const unsigned char*)azQuoted[i];
       }else{
         uz = (const unsigned char*)sqlite3_column_text(pStmt,i);
       }
-      azData[nRow*nColumn + i] = translateForDisplayAndDup(uz, &azNextLine[i], wx);
+      azData[nRow*nColumn + i]
+        = translateForDisplayAndDup(uz, &azNextLine[i], wx, bw);
       if( azNextLine[i] ){
         bNextLine = 1;
         abRowDiv[nRow-1] = 0;
@@ -4278,16 +4307,18 @@ static const char *(azHelp[]) = {
   "     list        Values delimited by \"|\"",
   "     markdown    Markdown table format",
   "     qbox        Shorthand for \"box --width 60 --quote\"",
+  "     qwbox       Shorthand for \"box --width 60 --wordwrap on --quote\"",
   "     quote       Escape answers as for SQL",
   "     table       ASCII-art table",
   "     tabs        Tab-separated values",
   "     tcl         TCL list elements",
-  "   OPTIONS: (value for columnar modes only):",
-  "     --wrap N    Wrap output lines longer than N character",
-  "     --quote     Quote output text as SQL literals",
-  "     --noquote   Do not quote output text",
-  "     TABLE       The name of SQL table used for \"insert\" mode",
-  ".nonce STRING            Disable safe mode for one command if the nonce matches",
+  "   OPTIONS: (for columnar modes or insert mode):",
+  "     --wrap N       Wrap output lines to no longer than N characters",
+  "     --wordwrap B   Wrap or not at word boundaries per B (on/off) ",
+  "     --quote        Quote output text as SQL literals",
+  "     --noquote      Do not quote output text",
+  "     TABLE          The name of SQL table used for \"insert\" mode",
+  ".nonce STRING            Suspend safe mode for one command if nonce matches",
   ".nullvalue STRING        Use STRING in place of NULL values",
   ".once ?OPTIONS? ?FILE?   Output for the next SQL command only to FILE",
   "     If FILE begins with '|' then open as a pipe",
@@ -9070,21 +9101,31 @@ static int do_meta_command(char *zLine, ShellState *p){
     const char *zMode = 0;
     const char *zTabname = 0;
     int i, n2;
-    int bQuoteChng = 0;
-    int bWrapChng = 0;
+    ColModeOpts cmOpts = ColModeOpts_default;
     for(i=1; i<nArg; i++){
       const char *z = azArg[i];
       if( optionMatch(z,"wrap") && i+1<nArg ){
-        p->iWrap = integerValue(azArg[++i]);
-        bWrapChng = 1;
+        cmOpts.iWrap = integerValue(azArg[++i]);
+      }else if( optionMatch(z,"wordwrap") && i+1<nArg ){
+        cmOpts.bWordWrap = (u8)booleanValue(azArg[++i]);
       }else if( optionMatch(z,"quote") ){
-        p->bQuote = 1;
-        bQuoteChng = 1;
+        cmOpts.bQuote = 1;
       }else if( optionMatch(z,"noquote") ){
-        p->bQuote = 0;
-        bQuoteChng = 1;
+        cmOpts.bQuote = 0;
       }else if( zMode==0 ){
         zMode = z;
+        /* Apply defaults for qbox and qwbox pseudo-modes. If that
+         * overwrites already-set values, user was informed of this.
+         */
+        if( strcmp(z, "qbox")==0 ){
+          ColModeOpts cmo = ColModeOpts_default_qbox;
+          zMode = "box";
+          cmOpts = cmo;
+        }else if( strcmp(z, "qwbox")==0 ){
+          ColModeOpts cmo = ColModeOpts_default_qwbox;
+          zMode = "box";
+          cmOpts = cmo;
+        }
       }else if( zTabname==0 ){
         zTabname = z;
       }else if( z[0]=='-' ){
@@ -9092,6 +9133,7 @@ static int do_meta_command(char *zLine, ShellState *p){
         utf8_printf(stderr, "options:\n"
                             "  --noquote\n"
                             "  --quote\n"
+                            "  --wordwrap on/off\n"
                             "  --wrap N\n");
         rc = 1;
         goto meta_command_exit;
@@ -9105,12 +9147,15 @@ static int do_meta_command(char *zLine, ShellState *p){
       if( p->mode==MODE_Column
        || (p->mode>=MODE_Markdown && p->mode<=MODE_Box)
       ){
-        raw_printf(p->out, "current output mode: %s --wrap %d --%squote\n",
-                   modeDescr[p->mode], p->iWrap, p->bQuote ? "" : "no");
+        raw_printf
+          (p->out,
+           "current output mode: %s --wrap %d --wordwrap %s --%squote\n",
+           modeDescr[p->mode], p->cmOpts.iWrap,
+           p->cmOpts.bWordWrap ? "on" : "off",
+           p->cmOpts.bQuote ? "" : "no");
       }else{
         raw_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]);
       }
-      bWrapChng = bQuoteChng = 1;
       zMode = modeDescr[p->mode];
     }
     n2 = strlen30(zMode);
@@ -9123,8 +9168,7 @@ static int do_meta_command(char *zLine, ShellState *p){
         p->showHeader = 1;
       }
       sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
-      if( !bWrapChng ) p->iWrap = 0;
-      if( !bQuoteChng ) p->bQuote = 0;
+      p->cmOpts = cmOpts;
     }else if( strncmp(zMode,"list",n2)==0 ){
       p->mode = MODE_List;
       sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Column);
@@ -9155,20 +9199,13 @@ static int do_meta_command(char *zLine, ShellState *p){
       sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record);
     }else if( strncmp(zMode,"markdown",n2)==0 ){
       p->mode = MODE_Markdown;
-      if( !bWrapChng ) p->iWrap = 0;
-      if( !bQuoteChng ) p->bQuote = 0;
+      p->cmOpts = cmOpts;
     }else if( strncmp(zMode,"table",n2)==0 ){
       p->mode = MODE_Table;
-      if( !bWrapChng ) p->iWrap = 0;
-      if( !bQuoteChng ) p->bQuote = 0;
+      p->cmOpts = cmOpts;
     }else if( strncmp(zMode,"box",n2)==0 ){
       p->mode = MODE_Box;
-      if( !bWrapChng ) p->iWrap = 0;
-      if( !bQuoteChng ) p->bQuote = 0;
-    }else if( strcmp(zMode,"qbox")==0 ){
-      p->mode = MODE_Box;
-      if( !bWrapChng ) p->iWrap = 60;
-      if( !bQuoteChng ) p->bQuote = 1;
+      p->cmOpts = cmOpts;
     }else if( strncmp(zMode,"count",n2)==0 ){
       p->mode = MODE_Count;
     }else if( strncmp(zMode,"off",n2)==0 ){
@@ -9178,7 +9215,7 @@ static int do_meta_command(char *zLine, ShellState *p){
     }else{
       raw_printf(stderr, "Error: mode should be one of: "
          "ascii box column csv html insert json line list markdown "
-         "qbox quote table tabs tcl\n");
+         "qbox quote qwbox table tabs tcl\n");
       rc = 1;
     }
     p->cMode = p->mode;
@@ -10329,8 +10366,11 @@ static int do_meta_command(char *zLine, ShellState *p){
     if( p->mode==MODE_Column
      || (p->mode>=MODE_Markdown && p->mode<=MODE_Box)
     ){
-      utf8_printf(p->out, "%12.12s: %s --wrap %d --%squote\n", "mode",
-                  modeDescr[p->mode], p->iWrap, p->bQuote ? "" : "no");
+      utf8_printf
+        (p->out, "%12.12s: %s --wrap %d --wordwrap %s --%squote\n", "mode",
+         modeDescr[p->mode], p->cmOpts.iWrap,
+         p->cmOpts.bWordWrap ? "on" : "off",
+         p->cmOpts.bQuote ? "" : "no");
     }else{
       utf8_printf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]);
     }
index 745990a7c9227ccc405cd114880a76a5e3d0e427..122d38dfa79d7467cd75eb555947005238cce244 100644 (file)
@@ -205,10 +205,10 @@ do_test shell1-2.2.4 {
 } {0 {}}
 do_test shell1-2.2.5 {
   catchcmd "test.db" ".mode \"insert FOO"
-} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}}
+} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote qwbox table tabs tcl}}
 do_test shell1-2.2.6 {
   catchcmd "test.db" ".mode \'insert FOO"
-} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}}
+} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote qwbox table tabs tcl}}
 
 # check multiple tokens, and quoted tokens
 do_test shell1-2.3.1 {
@@ -236,7 +236,7 @@ do_test shell1-2.3.7 {
 # check quoted args are unquoted
 do_test shell1-2.4.1 {
   catchcmd "test.db" ".mode FOO"
-} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}}
+} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote qwbox table tabs tcl}}
 do_test shell1-2.4.2 {
   catchcmd "test.db" ".mode csv"
 } {0 {}}
@@ -437,7 +437,7 @@ do_test shell1-3.13.1 {
 } {0 {current output mode: list}}
 do_test shell1-3.13.2 {
   catchcmd "test.db" ".mode FOO"
-} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}}
+} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote qwbox table tabs tcl}}
 do_test shell1-3.13.3 {
   catchcmd "test.db" ".mode csv"
 } {0 {}}