-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
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
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
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.
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.
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 */
**
** 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;
while( n<mxWidth ){
if( z[i]>=' ' ){
n++;
+ if( IsSpace(z[i]) ){
+ iLastWhite = i;
+ nLastWhite = n;
+ }
do{ i++; j++; }while( (z[i]&0xc0)==0x80 );
continue;
}
}
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' ){
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*) );
}
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;
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;
" 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",
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]=='-' ){
utf8_printf(stderr, "options:\n"
" --noquote\n"
" --quote\n"
+ " --wordwrap on/off\n"
" --wrap N\n");
rc = 1;
goto meta_command_exit;
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);
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);
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 ){
}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;
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]);
}