@@ -1194,6 +1194,7 @@ struct ShellState {
1194
1194
#define MODE_Json 13 /* Output JSON */
1195
1195
#define MODE_Markdown 14 /* Markdown formatting */
1196
1196
#define MODE_Table 15 /* MySQL-style table formatting */
1197
+ #define MODE_Box 16 /* Unicode box-drawing characters */
1197
1198
1198
1199
static const char *modeDescr[] = {
1199
1200
"line",
@@ -1211,7 +1212,8 @@ static const char *modeDescr[] = {
1211
1212
"eqp",
1212
1213
"json",
1213
1214
"markdown",
1214
- "table"
1215
+ "table",
1216
+ "box"
1215
1217
};
1216
1218
1217
1219
/*
@@ -1936,19 +1938,23 @@ static void print_dashes(FILE *out, int N){
1936
1938
}
1937
1939
1938
1940
/*
1939
- ** Print a markdown or table-style row separator
1941
+ ** Print a markdown or table-style row separator using ascii-art
1940
1942
*/
1941
1943
static void print_row_separator(
1942
1944
ShellState *p,
1943
1945
int nArg,
1944
1946
const char *zSep
1945
1947
){
1946
1948
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
+ }
1948
1956
fputs(zSep, p->out);
1949
- print_dashes(p->out, p->actualWidth[i]+2);
1950
1957
}
1951
- fputs(zSep, p->out);
1952
1958
fputs("\n", p->out);
1953
1959
}
1954
1960
@@ -2947,9 +2953,76 @@ static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){
2947
2953
sqlite3_finalize(pQ);
2948
2954
}
2949
2955
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
+
2950
3022
/*
2951
3023
** 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.
2953
3026
**
2954
3027
** This is different from ordinary exec_prepared_stmt() in that
2955
3028
** it has to run the entire query and gather the results into memory
@@ -2967,8 +3040,8 @@ static void exec_prepared_stmt_columnar(
2967
3040
const char *z;
2968
3041
int rc;
2969
3042
int i, j, nTotal, w, n;
2970
- const char *colSep;
2971
- const char *rowSep;
3043
+ const char *colSep = 0 ;
3044
+ const char *rowSep = 0 ;
2972
3045
2973
3046
rc = sqlite3_get_table(p->db, sqlite3_sql(pStmt),
2974
3047
&azData, &nRow, &nColumn, &zMsg);
@@ -3003,50 +3076,87 @@ static void exec_prepared_stmt_columnar(
3003
3076
j = i%nColumn;
3004
3077
if( n>p->actualWidth[j] ) p->actualWidth[j] = n;
3005
3078
}
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);
3010
3102
for(i=0; i<nColumn; i++){
3011
3103
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);
3015
3107
}
3108
+ print_row_separator(p, nColumn, "+");
3109
+ break;
3110
+ }
3111
+ case MODE_Markdown: {
3112
+ colSep = " | ";
3113
+ rowSep = " |\n";
3114
+ fputs("| ", p->out);
3016
3115
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);
3019
3120
}
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;
3020
3138
}
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 ? "+" : "|");
3033
3139
}
3034
3140
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
+ }
3036
3144
z = azData[i];
3037
3145
if( z==0 ) z = p->nullValue;
3038
3146
w = p->actualWidth[j];
3039
3147
if( p->colWidth[j]<0 ) w = -w;
3040
3148
utf8_width_print(p->out, w, z);
3041
3149
if( j==nColumn-1 ){
3042
- fputs(rowSep, p->out);
3150
+ utf8_printf( p->out, "%s", rowSep );
3043
3151
j = -1;
3044
3152
}else{
3045
- fputs(colSep, p->out);
3153
+ utf8_printf( p->out, "%s", colSep );
3046
3154
}
3047
3155
}
3048
3156
if( p->cMode==MODE_Table ){
3049
3157
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);
3050
3160
}
3051
3161
sqlite3_free_table(azData);
3052
3162
}
@@ -3062,6 +3172,7 @@ static void exec_prepared_stmt(
3062
3172
3063
3173
if( pArg->cMode==MODE_Column
3064
3174
|| pArg->cMode==MODE_Table
3175
+ || pArg->cMode==MODE_Box
3065
3176
|| pArg->cMode==MODE_Markdown
3066
3177
){
3067
3178
exec_prepared_stmt_columnar(pArg, pStmt);
@@ -3115,9 +3226,7 @@ static void exec_prepared_stmt(
3115
3226
}
3116
3227
} while( SQLITE_ROW == rc );
3117
3228
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 ){
3121
3230
fputs("]\n", pArg->out);
3122
3231
}
3123
3232
}
@@ -3801,6 +3910,7 @@ static const char *(azHelp[]) = {
3801
3910
".mode MODE ?TABLE? Set output mode",
3802
3911
" MODE is one of:",
3803
3912
" ascii Columns/rows delimited by 0x1F and 0x1E",
3913
+ " box Tables using unicode box-drawing characters",
3804
3914
" csv Comma-separated values",
3805
3915
" column Output in columns. (See .width)",
3806
3916
" html HTML <table> code",
@@ -8414,13 +8524,15 @@ static int do_meta_command(char *zLine, ShellState *p){
8414
8524
p->mode = MODE_Markdown;
8415
8525
}else if( c2=='t' && strncmp(azArg[1],"table",n2)==0 ){
8416
8526
p->mode = MODE_Table;
8527
+ }else if( c2=='b' && strncmp(azArg[1],"box",n2)==0 ){
8528
+ p->mode = MODE_Box;
8417
8529
}else if( c2=='j' && strncmp(azArg[1],"json",n2)==0 ){
8418
8530
p->mode = MODE_Json;
8419
8531
}else if( nArg==1 ){
8420
8532
raw_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]);
8421
8533
}else{
8422
8534
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 "
8424
8536
"quote table tabs tcl\n");
8425
8537
rc = 1;
8426
8538
}
@@ -10420,6 +10532,7 @@ static const char zOptions[] =
10420
10532
" -ascii set output mode to 'ascii'\n"
10421
10533
" -bail stop after hitting an error\n"
10422
10534
" -batch force batch I/O\n"
10535
+ " -box set output mode to 'box'\n"
10423
10536
" -column set output mode to 'column'\n"
10424
10537
" -cmd COMMAND run \"COMMAND\" before reading stdin\n"
10425
10538
" -csv set output mode to 'csv'\n"
@@ -10867,6 +10980,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
10867
10980
data.mode = MODE_Markdown;
10868
10981
}else if( strcmp(z,"-table")==0 ){
10869
10982
data.mode = MODE_Table;
10983
+ }else if( strcmp(z,"-box")==0 ){
10984
+ data.mode = MODE_Box;
10870
10985
}else if( strcmp(z,"-csv")==0 ){
10871
10986
data.mode = MODE_Csv;
10872
10987
memcpy(data.colSeparator,",",2);
0 commit comments