Add "--wordwrap on/off" option for CLI columnar modes, qwbox shortcut

FossilOrigin-Name: 10dbc278708cd2cce7fef90738082dbe31750d93e44b5fa5413a9a32dae7703a
This commit is contained in:
larrybr 2022-02-01 02:50:45 +00:00
parent 5aabdaebd5
commit cc4d55c9cb
4 changed files with 99 additions and 58 deletions

View File

@ -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.

View File

@ -1 +1 @@
fd42f4c304079356358e606dd96d4b84cf211c4334c586118b99fe9ad20e20ea
10dbc278708cd2cce7fef90738082dbe31750d93e44b5fa5413a9a32dae7703a

View 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]);
}

View 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 {}}