From: drh Date: Thu, 4 Jun 2020 18:05:39 +0000 (+0000) Subject: Add support for "box" mode in the CLI: Like "table" except that it uses X-Git-Tag: version-3.33.0~144 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=0908e3853675b41f3070bb8178e51a7b4f2f844e;p=thirdparty%2Fsqlite.git Add support for "box" mode in the CLI: Like "table" except that it uses unicode box-drawing characters instead of ascii-art. FossilOrigin-Name: 6da784c9e174744d6deeb76c553b515b96c1fcb80c55a281e476959ec680fb72 --- diff --git a/manifest b/manifest index d3d3f40fed..e3723f551c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improved\sdisplay\sof\s".mode\stable"\soutput\sfor\sempty\sresult\ssets. -D 2020-06-04T16:54:10.263 +C Add\ssupport\sfor\s"box"\smode\sin\sthe\sCLI:\s\sLike\s"table"\sexcept\sthat\sit\suses\nunicode\sbox-drawing\scharacters\sinstead\sof\sascii-art. +D 2020-06-04T18:05:39.066 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -534,7 +534,7 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384 F src/resolve.c c2008519a0654f1e7490e9281ed0397d0f14bb840d81f0b96946248afcbdb25d F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c 39a00a8bc89596dfb37c16afcbb1d33de5085b9963564b58aafe1566d08c0881 -F src/shell.c.in b8fd54e80021c9aed59c4f8ef7d8a68167aea3b2c5f8cc80e6ca373fb146cdba +F src/shell.c.in 6f7ea57d3f15e7e6a1f7049b6b7e39589dd5fe114e8de034ae9a68bf7722fd40 F src/sqlite.h.in 74342b41e9d68ff9e56b192009046f8dd0aa2bd76ce1a588f330de614ba61de7 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 2d1af80082edffd71c6f96f70ad1ce6a4fb46615ad10291fc77fe0dea9ff0197 @@ -1338,7 +1338,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 1c4713ccec468f9300100d5e1419b414b8dcccc742978ad8942e8bd31d2adc9c +F test/shell1.test fabf21eea2c6bb04dd86dfc7441c7c14841b25e2540c1fffeae815e718625bcc F test/shell2.test e242a9912f44f4c23c3d1d802a83e934e84c853b F test/shell3.test ac8c2b744014c3e9a0e26bfd829ab65f00923dc1a91ffd044863e9423cc91494 F test/shell4.test 1c6aef11daaa2d6830acaba3ac9cbec93fbc1c3d5530743a637f39b3987d08ce @@ -1866,7 +1866,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 362255791f8801e0d9869e36239b8b2cb29c38bf0b86894bd2d159ce46d8447e -R e25e15e7d406e657ad2786c9bbcb9942 +P 7efabd683b79743b407ad71dda56db00fb0d668828bdc342145816b4f1c3bf3a +R 3dac6f750121e41150b03b7c089ed222 U drh -Z acf5bed5cb288b39151d44a020264134 +Z e00f940b0330d0b11d881b13cbeefb56 diff --git a/manifest.uuid b/manifest.uuid index ba2429e47f..93e7eee675 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7efabd683b79743b407ad71dda56db00fb0d668828bdc342145816b4f1c3bf3a \ No newline at end of file +6da784c9e174744d6deeb76c553b515b96c1fcb80c55a281e476959ec680fb72 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index bd78165e6b..e46188e4ec 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -1194,6 +1194,7 @@ struct ShellState { #define MODE_Json 13 /* Output JSON */ #define MODE_Markdown 14 /* Markdown formatting */ #define MODE_Table 15 /* MySQL-style table formatting */ +#define MODE_Box 16 /* Unicode box-drawing characters */ static const char *modeDescr[] = { "line", @@ -1211,7 +1212,8 @@ static const char *modeDescr[] = { "eqp", "json", "markdown", - "table" + "table", + "box" }; /* @@ -1936,7 +1938,7 @@ static void print_dashes(FILE *out, int N){ } /* -** Print a markdown or table-style row separator +** Print a markdown or table-style row separator using ascii-art */ static void print_row_separator( ShellState *p, @@ -1944,11 +1946,15 @@ static void print_row_separator( const char *zSep ){ int i; - for(i=0; i0 ){ + fputs(zSep, p->out); + print_dashes(p->out, p->actualWidth[0]+2); + for(i=1; iout); + print_dashes(p->out, p->actualWidth[i]+2); + } fputs(zSep, p->out); - print_dashes(p->out, p->actualWidth[i]+2); } - fputs(zSep, p->out); fputs("\n", p->out); } @@ -2947,9 +2953,76 @@ static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){ sqlite3_finalize(pQ); } +/* +** UTF8 box-drawing characters. Imagine box lines like this: +** +** 1 +** | +** 4 --+-- 2 +** | +** 3 +** +** Each box characters has between 2 and 4 of the lines leading from +** the center. The characters are here identified by the numbers of +** their corresponding lines. +*/ +#define BOX_24 "\342\224\200" /* U+2500 --- */ +#define BOX_13 "\342\224\202" /* U+2502 | */ +#define BOX_23 "\342\224\214" /* U+250c ,- */ +#define BOX_34 "\342\224\220" /* U+2510 -, */ +#define BOX_12 "\342\224\224" /* U+2514 '- */ +#define BOX_14 "\342\224\230" /* U+2518 -' */ +#define BOX_123 "\342\224\234" /* U+251c |- */ +#define BOX_134 "\342\224\244" /* U+2524 -| */ +#define BOX_234 "\342\224\254" /* U+252c -,- */ +#define BOX_124 "\342\224\264" /* U+2534 -'- */ +#define BOX_1234 "\342\224\274" /* U+253c -|- */ + +/* Draw horizontal line N characters long using unicode box +** characters +*/ +static void print_box_line(FILE *out, int N){ + const char zDash[] = + BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 + BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24; + const int nDash = sizeof(zDash) - 1; + N *= 3; + while( N>nDash ){ + utf8_printf(out, zDash); + N -= nDash; + } + utf8_printf(out, "%.*s", N, zDash); +} + +/* +** Draw a horizontal separator for a MODE_Box table. +*/ +static void print_box_row_separator( + ShellState *p, + int nArg, + const char *zSep1, + const char *zSep2, + const char *zSep3 +){ + int i; + if( nArg>0 ){ + utf8_printf(p->out, "%s", zSep1); + print_box_line(p->out, p->actualWidth[0]+2); + for(i=1; iout, "%s", zSep2); + print_box_line(p->out, p->actualWidth[i]+2); + } + utf8_printf(p->out, "%s", zSep3); + } + fputs("\n", p->out); +} + + + /* ** Run a prepared statement and output the result in one of the -** table-oriented formats: MODE_Column, MODE_Markdown, or MODE_Table. +** table-oriented formats: MODE_Column, MODE_Markdown, MODE_Table, +** or MODE_Box. ** ** This is different from ordinary exec_prepared_stmt() in that ** it has to run the entire query and gather the results into memory @@ -2967,8 +3040,8 @@ static void exec_prepared_stmt_columnar( const char *z; int rc; int i, j, nTotal, w, n; - const char *colSep; - const char *rowSep; + const char *colSep = 0; + const char *rowSep = 0; rc = sqlite3_get_table(p->db, sqlite3_sql(pStmt), &azData, &nRow, &nColumn, &zMsg); @@ -3003,50 +3076,87 @@ static void exec_prepared_stmt_columnar( j = i%nColumn; if( n>p->actualWidth[j] ) p->actualWidth[j] = n; } - if( p->cMode==MODE_Column ){ - colSep = " "; - rowSep = "\n"; - if( p->showHeader ){ + switch( p->cMode ){ + case MODE_Column: { + colSep = " "; + rowSep = "\n"; + if( p->showHeader ){ + for(i=0; iactualWidth[i]; + if( p->colWidth[i]<0 ) w = -w; + utf8_width_print(p->out, w, azData[i]); + fputs(i==nColumn-1?"\n":" ", p->out); + } + for(i=0; iout, p->actualWidth[i]); + fputs(i==nColumn-1?"\n":" ", p->out); + } + } + break; + } + case MODE_Table: { + colSep = " | "; + rowSep = " |\n"; + print_row_separator(p, nColumn, "+"); + fputs("| ", p->out); for(i=0; iactualWidth[i]; - if( p->colWidth[i]<0 ) w = -w; - utf8_width_print(p->out, w, azData[i]); - fputs(i==nColumn-1?"\n":" ", p->out); + n = strlenChar(azData[i]); + utf8_printf(p->out, "%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); + fputs(i==nColumn-1?" |\n":" | ", p->out); } + print_row_separator(p, nColumn, "+"); + break; + } + case MODE_Markdown: { + colSep = " | "; + rowSep = " |\n"; + fputs("| ", p->out); for(i=0; iout, p->actualWidth[i]); - fputs(i==nColumn-1?"\n":" ", p->out); + w = p->actualWidth[i]; + n = strlenChar(azData[i]); + utf8_printf(p->out, "%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); + fputs(i==nColumn-1?" |\n":" | ", p->out); } + print_row_separator(p, nColumn, "|"); + break; + } + case MODE_Box: { + colSep = " " BOX_13 " "; + rowSep = " " BOX_13 "\n"; + print_box_row_separator(p, nColumn, BOX_23, BOX_234, BOX_34); + utf8_printf(p->out, BOX_13 " "); + for(i=0; iactualWidth[i]; + n = strlenChar(azData[i]); + utf8_printf(p->out, "%*s%s%*s%s", + (w-n)/2, "", azData[i], (w-n+1)/2, "", + i==nColumn-1?" "BOX_13"\n":" "BOX_13" "); + } + print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134); + break; } - }else{ - colSep = " | "; - rowSep = " |\n"; - if( p->cMode==MODE_Table ) print_row_separator(p, nColumn, "+"); - fputs("| ", p->out); - for(i=0; iactualWidth[i]; - n = strlenChar(azData[i]); - utf8_printf(p->out, "%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); - fputs(i==nColumn-1?" |\n":" | ", p->out); - } - print_row_separator(p, nColumn, p->cMode==MODE_Table ? "+" : "|"); } for(i=nColumn, j=0; icMode!=MODE_Column ) fputs("| ", p->out); + if( j==0 && p->cMode!=MODE_Column ){ + utf8_printf(p->out, "%s", p->cMode==MODE_Box?BOX_13" ":"| "); + } z = azData[i]; if( z==0 ) z = p->nullValue; w = p->actualWidth[j]; if( p->colWidth[j]<0 ) w = -w; utf8_width_print(p->out, w, z); if( j==nColumn-1 ){ - fputs(rowSep, p->out); + utf8_printf(p->out, "%s", rowSep); j = -1; }else{ - fputs(colSep, p->out); + utf8_printf(p->out, "%s", colSep); } } if( p->cMode==MODE_Table ){ print_row_separator(p, nColumn, "+"); + }else if( p->cMode==MODE_Box ){ + print_box_row_separator(p, nColumn, BOX_12, BOX_124, BOX_14); } sqlite3_free_table(azData); } @@ -3062,6 +3172,7 @@ static void exec_prepared_stmt( if( pArg->cMode==MODE_Column || pArg->cMode==MODE_Table + || pArg->cMode==MODE_Box || pArg->cMode==MODE_Markdown ){ exec_prepared_stmt_columnar(pArg, pStmt); @@ -3115,9 +3226,7 @@ static void exec_prepared_stmt( } } while( SQLITE_ROW == rc ); sqlite3_free(pData); - if( pArg->cMode==MODE_Table ){ - print_row_separator(pArg, nCol, "+"); - }else if( pArg->cMode==MODE_Json ){ + if( pArg->cMode==MODE_Json ){ fputs("]\n", pArg->out); } } @@ -3801,6 +3910,7 @@ static const char *(azHelp[]) = { ".mode MODE ?TABLE? Set output mode", " MODE is one of:", " ascii Columns/rows delimited by 0x1F and 0x1E", + " box Tables using unicode box-drawing characters", " csv Comma-separated values", " column Output in columns. (See .width)", " html HTML code", @@ -8414,13 +8524,15 @@ static int do_meta_command(char *zLine, ShellState *p){ p->mode = MODE_Markdown; }else if( c2=='t' && strncmp(azArg[1],"table",n2)==0 ){ p->mode = MODE_Table; + }else if( c2=='b' && strncmp(azArg[1],"box",n2)==0 ){ + p->mode = MODE_Box; }else if( c2=='j' && strncmp(azArg[1],"json",n2)==0 ){ p->mode = MODE_Json; }else if( nArg==1 ){ raw_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]); }else{ raw_printf(stderr, "Error: mode should be one of: " - "ascii column csv html insert json line list markdown " + "ascii box column csv html insert json line list markdown " "quote table tabs tcl\n"); rc = 1; } @@ -10420,6 +10532,7 @@ static const char zOptions[] = " -ascii set output mode to 'ascii'\n" " -bail stop after hitting an error\n" " -batch force batch I/O\n" + " -box set output mode to 'box'\n" " -column set output mode to 'column'\n" " -cmd COMMAND run \"COMMAND\" before reading stdin\n" " -csv set output mode to 'csv'\n" @@ -10867,6 +10980,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ data.mode = MODE_Markdown; }else if( strcmp(z,"-table")==0 ){ data.mode = MODE_Table; + }else if( strcmp(z,"-box")==0 ){ + data.mode = MODE_Box; }else if( strcmp(z,"-csv")==0 ){ data.mode = MODE_Csv; memcpy(data.colSeparator,",",2); diff --git a/test/shell1.test b/test/shell1.test index a900dc3e1d..b08e111804 100644 --- a/test/shell1.test +++ b/test/shell1.test @@ -199,10 +199,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 column csv html insert json line list markdown quote table tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}} do_test shell1-2.2.6 { catchcmd "test.db" ".mode \'insert FOO" -} {1 {Error: mode should be one of: ascii column csv html insert json line list markdown quote table tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}} # check multiple tokens, and quoted tokens do_test shell1-2.3.1 { @@ -230,7 +230,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 column csv html insert json line list markdown quote table tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}} do_test shell1-2.4.2 { catchcmd "test.db" ".mode csv" } {0 {}} @@ -430,7 +430,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 column csv html insert json line list markdown quote table tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}} do_test shell1-3.13.3 { catchcmd "test.db" ".mode csv" } {0 {}} @@ -463,10 +463,10 @@ do_test shell1-3.13.11 { # don't allow partial mode type matches do_test shell1-3.13.12 { catchcmd "test.db" ".mode l" -} {1 {Error: mode should be one of: ascii column csv html insert json line list markdown quote table tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}} do_test shell1-3.13.13 { catchcmd "test.db" ".mode li" -} {1 {Error: mode should be one of: ascii column csv html insert json line list markdown quote table tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}} do_test shell1-3.13.14 { catchcmd "test.db" ".mode lin" } {0 {}}