Skip to content

Commit 0908e38

Browse files
author
drh
committed
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
1 parent 634af38 commit 0908e38

File tree

4 files changed

+165
-50
lines changed

4 files changed

+165
-50
lines changed

manifest

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
C Improved\sdisplay\sof\s".mode\stable"\soutput\sfor\sempty\sresult\ssets.
2-
D 2020-06-04T16:54:10.263
1+
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.
2+
D 2020-06-04T18:05:39.066
33
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
44
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
55
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -534,7 +534,7 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
534534
F src/resolve.c c2008519a0654f1e7490e9281ed0397d0f14bb840d81f0b96946248afcbdb25d
535535
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
536536
F src/select.c 39a00a8bc89596dfb37c16afcbb1d33de5085b9963564b58aafe1566d08c0881
537-
F src/shell.c.in b8fd54e80021c9aed59c4f8ef7d8a68167aea3b2c5f8cc80e6ca373fb146cdba
537+
F src/shell.c.in 6f7ea57d3f15e7e6a1f7049b6b7e39589dd5fe114e8de034ae9a68bf7722fd40
538538
F src/sqlite.h.in 74342b41e9d68ff9e56b192009046f8dd0aa2bd76ce1a588f330de614ba61de7
539539
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
540540
F src/sqlite3ext.h 2d1af80082edffd71c6f96f70ad1ce6a4fb46615ad10291fc77fe0dea9ff0197
@@ -1338,7 +1338,7 @@ F test/sharedA.test 49d87ec54ab640fbbc3786ee3c01de94aaa482a3a9f834ad3fe92770eb69
13381338
F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e
13391339
F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939
13401340
F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
1341-
F test/shell1.test 1c4713ccec468f9300100d5e1419b414b8dcccc742978ad8942e8bd31d2adc9c
1341+
F test/shell1.test fabf21eea2c6bb04dd86dfc7441c7c14841b25e2540c1fffeae815e718625bcc
13421342
F test/shell2.test e242a9912f44f4c23c3d1d802a83e934e84c853b
13431343
F test/shell3.test ac8c2b744014c3e9a0e26bfd829ab65f00923dc1a91ffd044863e9423cc91494
13441344
F test/shell4.test 1c6aef11daaa2d6830acaba3ac9cbec93fbc1c3d5530743a637f39b3987d08ce
@@ -1866,7 +1866,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
18661866
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
18671867
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
18681868
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
1869-
P 362255791f8801e0d9869e36239b8b2cb29c38bf0b86894bd2d159ce46d8447e
1870-
R e25e15e7d406e657ad2786c9bbcb9942
1869+
P 7efabd683b79743b407ad71dda56db00fb0d668828bdc342145816b4f1c3bf3a
1870+
R 3dac6f750121e41150b03b7c089ed222
18711871
U drh
1872-
Z acf5bed5cb288b39151d44a020264134
1872+
Z e00f940b0330d0b11d881b13cbeefb56

manifest.uuid

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
7efabd683b79743b407ad71dda56db00fb0d668828bdc342145816b4f1c3bf3a
1+
6da784c9e174744d6deeb76c553b515b96c1fcb80c55a281e476959ec680fb72

src/shell.c.in

Lines changed: 151 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,6 +1194,7 @@ struct ShellState {
11941194
#define MODE_Json 13 /* Output JSON */
11951195
#define MODE_Markdown 14 /* Markdown formatting */
11961196
#define MODE_Table 15 /* MySQL-style table formatting */
1197+
#define MODE_Box 16 /* Unicode box-drawing characters */
11971198

11981199
static const char *modeDescr[] = {
11991200
"line",
@@ -1211,7 +1212,8 @@ static const char *modeDescr[] = {
12111212
"eqp",
12121213
"json",
12131214
"markdown",
1214-
"table"
1215+
"table",
1216+
"box"
12151217
};
12161218

12171219
/*
@@ -1936,19 +1938,23 @@ static void print_dashes(FILE *out, int N){
19361938
}
19371939

19381940
/*
1939-
** Print a markdown or table-style row separator
1941+
** Print a markdown or table-style row separator using ascii-art
19401942
*/
19411943
static void print_row_separator(
19421944
ShellState *p,
19431945
int nArg,
19441946
const char *zSep
19451947
){
19461948
int i;
1947-
for(i=0; i<nArg; i++){
1949+
if( nArg>0 ){
1950+
fputs(zSep, p->out);
1951+
print_dashes(p->out, p->actualWidth[0]+2);
1952+
for(i=1; i<nArg; i++){
1953+
fputs(zSep, p->out);
1954+
print_dashes(p->out, p->actualWidth[i]+2);
1955+
}
19481956
fputs(zSep, p->out);
1949-
print_dashes(p->out, p->actualWidth[i]+2);
19501957
}
1951-
fputs(zSep, p->out);
19521958
fputs("\n", p->out);
19531959
}
19541960

@@ -2947,9 +2953,76 @@ static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){
29472953
sqlite3_finalize(pQ);
29482954
}
29492955

2956+
/*
2957+
** UTF8 box-drawing characters. Imagine box lines like this:
2958+
**
2959+
** 1
2960+
** |
2961+
** 4 --+-- 2
2962+
** |
2963+
** 3
2964+
**
2965+
** Each box characters has between 2 and 4 of the lines leading from
2966+
** the center. The characters are here identified by the numbers of
2967+
** their corresponding lines.
2968+
*/
2969+
#define BOX_24 "\342\224\200" /* U+2500 --- */
2970+
#define BOX_13 "\342\224\202" /* U+2502 | */
2971+
#define BOX_23 "\342\224\214" /* U+250c ,- */
2972+
#define BOX_34 "\342\224\220" /* U+2510 -, */
2973+
#define BOX_12 "\342\224\224" /* U+2514 '- */
2974+
#define BOX_14 "\342\224\230" /* U+2518 -' */
2975+
#define BOX_123 "\342\224\234" /* U+251c |- */
2976+
#define BOX_134 "\342\224\244" /* U+2524 -| */
2977+
#define BOX_234 "\342\224\254" /* U+252c -,- */
2978+
#define BOX_124 "\342\224\264" /* U+2534 -'- */
2979+
#define BOX_1234 "\342\224\274" /* U+253c -|- */
2980+
2981+
/* Draw horizontal line N characters long using unicode box
2982+
** characters
2983+
*/
2984+
static void print_box_line(FILE *out, int N){
2985+
const char zDash[] =
2986+
BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24
2987+
BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24;
2988+
const int nDash = sizeof(zDash) - 1;
2989+
N *= 3;
2990+
while( N>nDash ){
2991+
utf8_printf(out, zDash);
2992+
N -= nDash;
2993+
}
2994+
utf8_printf(out, "%.*s", N, zDash);
2995+
}
2996+
2997+
/*
2998+
** Draw a horizontal separator for a MODE_Box table.
2999+
*/
3000+
static void print_box_row_separator(
3001+
ShellState *p,
3002+
int nArg,
3003+
const char *zSep1,
3004+
const char *zSep2,
3005+
const char *zSep3
3006+
){
3007+
int i;
3008+
if( nArg>0 ){
3009+
utf8_printf(p->out, "%s", zSep1);
3010+
print_box_line(p->out, p->actualWidth[0]+2);
3011+
for(i=1; i<nArg; i++){
3012+
utf8_printf(p->out, "%s", zSep2);
3013+
print_box_line(p->out, p->actualWidth[i]+2);
3014+
}
3015+
utf8_printf(p->out, "%s", zSep3);
3016+
}
3017+
fputs("\n", p->out);
3018+
}
3019+
3020+
3021+
29503022
/*
29513023
** Run a prepared statement and output the result in one of the
2952-
** table-oriented formats: MODE_Column, MODE_Markdown, or MODE_Table.
3024+
** table-oriented formats: MODE_Column, MODE_Markdown, MODE_Table,
3025+
** or MODE_Box.
29533026
**
29543027
** This is different from ordinary exec_prepared_stmt() in that
29553028
** it has to run the entire query and gather the results into memory
@@ -2967,8 +3040,8 @@ static void exec_prepared_stmt_columnar(
29673040
const char *z;
29683041
int rc;
29693042
int i, j, nTotal, w, n;
2970-
const char *colSep;
2971-
const char *rowSep;
3043+
const char *colSep = 0;
3044+
const char *rowSep = 0;
29723045

29733046
rc = sqlite3_get_table(p->db, sqlite3_sql(pStmt),
29743047
&azData, &nRow, &nColumn, &zMsg);
@@ -3003,50 +3076,87 @@ static void exec_prepared_stmt_columnar(
30033076
j = i%nColumn;
30043077
if( n>p->actualWidth[j] ) p->actualWidth[j] = n;
30053078
}
3006-
if( p->cMode==MODE_Column ){
3007-
colSep = " ";
3008-
rowSep = "\n";
3009-
if( p->showHeader ){
3079+
switch( p->cMode ){
3080+
case MODE_Column: {
3081+
colSep = " ";
3082+
rowSep = "\n";
3083+
if( p->showHeader ){
3084+
for(i=0; i<nColumn; i++){
3085+
w = p->actualWidth[i];
3086+
if( p->colWidth[i]<0 ) w = -w;
3087+
utf8_width_print(p->out, w, azData[i]);
3088+
fputs(i==nColumn-1?"\n":" ", p->out);
3089+
}
3090+
for(i=0; i<nColumn; i++){
3091+
print_dashes(p->out, p->actualWidth[i]);
3092+
fputs(i==nColumn-1?"\n":" ", p->out);
3093+
}
3094+
}
3095+
break;
3096+
}
3097+
case MODE_Table: {
3098+
colSep = " | ";
3099+
rowSep = " |\n";
3100+
print_row_separator(p, nColumn, "+");
3101+
fputs("| ", p->out);
30103102
for(i=0; i<nColumn; i++){
30113103
w = p->actualWidth[i];
3012-
if( p->colWidth[i]<0 ) w = -w;
3013-
utf8_width_print(p->out, w, azData[i]);
3014-
fputs(i==nColumn-1?"\n":" ", p->out);
3104+
n = strlenChar(azData[i]);
3105+
utf8_printf(p->out, "%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, "");
3106+
fputs(i==nColumn-1?" |\n":" | ", p->out);
30153107
}
3108+
print_row_separator(p, nColumn, "+");
3109+
break;
3110+
}
3111+
case MODE_Markdown: {
3112+
colSep = " | ";
3113+
rowSep = " |\n";
3114+
fputs("| ", p->out);
30163115
for(i=0; i<nColumn; i++){
3017-
print_dashes(p->out, p->actualWidth[i]);
3018-
fputs(i==nColumn-1?"\n":" ", p->out);
3116+
w = p->actualWidth[i];
3117+
n = strlenChar(azData[i]);
3118+
utf8_printf(p->out, "%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, "");
3119+
fputs(i==nColumn-1?" |\n":" | ", p->out);
30193120
}
3121+
print_row_separator(p, nColumn, "|");
3122+
break;
3123+
}
3124+
case MODE_Box: {
3125+
colSep = " " BOX_13 " ";
3126+
rowSep = " " BOX_13 "\n";
3127+
print_box_row_separator(p, nColumn, BOX_23, BOX_234, BOX_34);
3128+
utf8_printf(p->out, BOX_13 " ");
3129+
for(i=0; i<nColumn; i++){
3130+
w = p->actualWidth[i];
3131+
n = strlenChar(azData[i]);
3132+
utf8_printf(p->out, "%*s%s%*s%s",
3133+
(w-n)/2, "", azData[i], (w-n+1)/2, "",
3134+
i==nColumn-1?" "BOX_13"\n":" "BOX_13" ");
3135+
}
3136+
print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134);
3137+
break;
30203138
}
3021-
}else{
3022-
colSep = " | ";
3023-
rowSep = " |\n";
3024-
if( p->cMode==MODE_Table ) print_row_separator(p, nColumn, "+");
3025-
fputs("| ", p->out);
3026-
for(i=0; i<nColumn; i++){
3027-
w = p->actualWidth[i];
3028-
n = strlenChar(azData[i]);
3029-
utf8_printf(p->out, "%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, "");
3030-
fputs(i==nColumn-1?" |\n":" | ", p->out);
3031-
}
3032-
print_row_separator(p, nColumn, p->cMode==MODE_Table ? "+" : "|");
30333139
}
30343140
for(i=nColumn, j=0; i<nTotal; i++, j++){
3035-
if( j==0 && p->cMode!=MODE_Column ) fputs("| ", p->out);
3141+
if( j==0 && p->cMode!=MODE_Column ){
3142+
utf8_printf(p->out, "%s", p->cMode==MODE_Box?BOX_13" ":"| ");
3143+
}
30363144
z = azData[i];
30373145
if( z==0 ) z = p->nullValue;
30383146
w = p->actualWidth[j];
30393147
if( p->colWidth[j]<0 ) w = -w;
30403148
utf8_width_print(p->out, w, z);
30413149
if( j==nColumn-1 ){
3042-
fputs(rowSep, p->out);
3150+
utf8_printf(p->out, "%s", rowSep);
30433151
j = -1;
30443152
}else{
3045-
fputs(colSep, p->out);
3153+
utf8_printf(p->out, "%s", colSep);
30463154
}
30473155
}
30483156
if( p->cMode==MODE_Table ){
30493157
print_row_separator(p, nColumn, "+");
3158+
}else if( p->cMode==MODE_Box ){
3159+
print_box_row_separator(p, nColumn, BOX_12, BOX_124, BOX_14);
30503160
}
30513161
sqlite3_free_table(azData);
30523162
}
@@ -3062,6 +3172,7 @@ static void exec_prepared_stmt(
30623172

30633173
if( pArg->cMode==MODE_Column
30643174
|| pArg->cMode==MODE_Table
3175+
|| pArg->cMode==MODE_Box
30653176
|| pArg->cMode==MODE_Markdown
30663177
){
30673178
exec_prepared_stmt_columnar(pArg, pStmt);
@@ -3115,9 +3226,7 @@ static void exec_prepared_stmt(
31153226
}
31163227
} while( SQLITE_ROW == rc );
31173228
sqlite3_free(pData);
3118-
if( pArg->cMode==MODE_Table ){
3119-
print_row_separator(pArg, nCol, "+");
3120-
}else if( pArg->cMode==MODE_Json ){
3229+
if( pArg->cMode==MODE_Json ){
31213230
fputs("]\n", pArg->out);
31223231
}
31233232
}
@@ -3801,6 +3910,7 @@ static const char *(azHelp[]) = {
38013910
".mode MODE ?TABLE? Set output mode",
38023911
" MODE is one of:",
38033912
" ascii Columns/rows delimited by 0x1F and 0x1E",
3913+
" box Tables using unicode box-drawing characters",
38043914
" csv Comma-separated values",
38053915
" column Output in columns. (See .width)",
38063916
" html HTML <table> code",
@@ -8414,13 +8524,15 @@ static int do_meta_command(char *zLine, ShellState *p){
84148524
p->mode = MODE_Markdown;
84158525
}else if( c2=='t' && strncmp(azArg[1],"table",n2)==0 ){
84168526
p->mode = MODE_Table;
8527+
}else if( c2=='b' && strncmp(azArg[1],"box",n2)==0 ){
8528+
p->mode = MODE_Box;
84178529
}else if( c2=='j' && strncmp(azArg[1],"json",n2)==0 ){
84188530
p->mode = MODE_Json;
84198531
}else if( nArg==1 ){
84208532
raw_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]);
84218533
}else{
84228534
raw_printf(stderr, "Error: mode should be one of: "
8423-
"ascii column csv html insert json line list markdown "
8535+
"ascii box column csv html insert json line list markdown "
84248536
"quote table tabs tcl\n");
84258537
rc = 1;
84268538
}
@@ -10420,6 +10532,7 @@ static const char zOptions[] =
1042010532
" -ascii set output mode to 'ascii'\n"
1042110533
" -bail stop after hitting an error\n"
1042210534
" -batch force batch I/O\n"
10535+
" -box set output mode to 'box'\n"
1042310536
" -column set output mode to 'column'\n"
1042410537
" -cmd COMMAND run \"COMMAND\" before reading stdin\n"
1042510538
" -csv set output mode to 'csv'\n"
@@ -10867,6 +10980,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
1086710980
data.mode = MODE_Markdown;
1086810981
}else if( strcmp(z,"-table")==0 ){
1086910982
data.mode = MODE_Table;
10983+
}else if( strcmp(z,"-box")==0 ){
10984+
data.mode = MODE_Box;
1087010985
}else if( strcmp(z,"-csv")==0 ){
1087110986
data.mode = MODE_Csv;
1087210987
memcpy(data.colSeparator,",",2);

test/shell1.test

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -199,10 +199,10 @@ do_test shell1-2.2.4 {
199199
} {0 {}}
200200
do_test shell1-2.2.5 {
201201
catchcmd "test.db" ".mode \"insert FOO"
202-
} {1 {Error: mode should be one of: ascii column csv html insert json line list markdown quote table tabs tcl}}
202+
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}}
203203
do_test shell1-2.2.6 {
204204
catchcmd "test.db" ".mode \'insert FOO"
205-
} {1 {Error: mode should be one of: ascii column csv html insert json line list markdown quote table tabs tcl}}
205+
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}}
206206

207207
# check multiple tokens, and quoted tokens
208208
do_test shell1-2.3.1 {
@@ -230,7 +230,7 @@ do_test shell1-2.3.7 {
230230
# check quoted args are unquoted
231231
do_test shell1-2.4.1 {
232232
catchcmd "test.db" ".mode FOO"
233-
} {1 {Error: mode should be one of: ascii column csv html insert json line list markdown quote table tabs tcl}}
233+
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}}
234234
do_test shell1-2.4.2 {
235235
catchcmd "test.db" ".mode csv"
236236
} {0 {}}
@@ -430,7 +430,7 @@ do_test shell1-3.13.1 {
430430
} {0 {current output mode: list}}
431431
do_test shell1-3.13.2 {
432432
catchcmd "test.db" ".mode FOO"
433-
} {1 {Error: mode should be one of: ascii column csv html insert json line list markdown quote table tabs tcl}}
433+
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}}
434434
do_test shell1-3.13.3 {
435435
catchcmd "test.db" ".mode csv"
436436
} {0 {}}
@@ -463,10 +463,10 @@ do_test shell1-3.13.11 {
463463
# don't allow partial mode type matches
464464
do_test shell1-3.13.12 {
465465
catchcmd "test.db" ".mode l"
466-
} {1 {Error: mode should be one of: ascii column csv html insert json line list markdown quote table tabs tcl}}
466+
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}}
467467
do_test shell1-3.13.13 {
468468
catchcmd "test.db" ".mode li"
469-
} {1 {Error: mode should be one of: ascii column csv html insert json line list markdown quote table tabs tcl}}
469+
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}}
470470
do_test shell1-3.13.14 {
471471
catchcmd "test.db" ".mode lin"
472472
} {0 {}}

0 commit comments

Comments
 (0)