From 85fb541ac0008dcbc68735070a412b3554fd67d5 Mon Sep 17 00:00:00 2001 From: drh <> Date: Wed, 10 Dec 2025 19:04:49 +0000 Subject: [PATCH] Various improvements to the .mode command of the CLI, and especially to how it handles the --limits option. FossilOrigin-Name: f883b854976d2aa679c87edce3b577f38b5fa518f707706320d1ddb38365e2dd --- ext/qrf/qrf.c | 15 +++--- manifest | 22 ++++----- manifest.uuid | 2 +- src/shell.c.in | 54 ++++++++++++---------- test/modeA.clitest | 111 +++++++++++++++++++++++++++++++++++++++++++-- test/shell1.test | 2 +- test/shellA.test | 6 +-- 7 files changed, 164 insertions(+), 48 deletions(-) diff --git a/ext/qrf/qrf.c b/ext/qrf/qrf.c index 465054ad1e..8785158c4b 100644 --- a/ext/qrf/qrf.c +++ b/ext/qrf/qrf.c @@ -957,11 +957,14 @@ static const char *qrfJsonbToJson(Qrf *p, int iCol){ static int qrfTitleLimit(char *zIn, int N){ unsigned char *z = (unsigned char*)zIn; int n = 0; - unsigned char *zEllipse = 0; - while( z[0] ){ + unsigned char *zEllipsis = 0; + while( 1 /*exit-by-break*/ ){ if( z[0]<' ' ){ int k; - if( z[0]=='\033' && (k = qrfIsVt100(z))>0 ){ + if( z[0]==0 ){ + zEllipsis = 0; + break; + }else if( z[0]=='\033' && (k = qrfIsVt100(z))>0 ){ z += k; }else if( z[0]=='\t' ){ z[0] = ' '; @@ -971,20 +974,20 @@ static int qrfTitleLimit(char *zIn, int N){ z++; } }else if( (0x80&z[0])==0 ){ - if( n>=(N-3) && zEllipse==0 ) zEllipse = z; + if( n>=(N-3) && zEllipsis==0 ) zEllipsis = z; if( n==N ){ z[0] = 0; break; } n++; z++; }else{ int u = 0; int len = sqlite3_qrf_decode_utf8(z, &u); - if( n+len>(N-3) && zEllipse==0 ) zEllipse = z; + if( n+len>(N-3) && zEllipsis==0 ) zEllipsis = z; if( n+len>N ){ z[0] = 0; break; } z += len; n += sqlite3_qrf_wcwidth(u); } } - if( zEllipse ) memcpy(zEllipse,"...",4); + if( zEllipsis && N>=3 ) memcpy(zEllipsis,"...",4); return n; } diff --git a/manifest b/manifest index 2368536040..f448e298aa 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C In\sfiddle,\sremove\sthe\sdefault\s.dot-commands\sit\sruns,\sas\sthey\sdo\snot\sreflect\scurrent\sshell\sdefaults. -D 2025-12-10T16:20:44.840 +C Various\simprovements\sto\sthe\s.mode\scommand\sof\sthe\sCLI,\sand\sespecially\sto\show\nit\shandles\sthe\s--limits\soption. +D 2025-12-10T19:04:49.444 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -418,7 +418,7 @@ F ext/misc/zipfile.c d792ed9b936ddfe9a210ecc893352afaee306c63b084187f6e44951f6f6 F ext/misc/zorder.c bddff2e1b9661a90c95c2a9a9c7ecd8908afab5763256294dd12d609d4664eee F ext/qrf/README.md ea2cfe3526cb39d1f32c4537499a403e100aff16bda796cb92254b63d913de4e F ext/qrf/dev-notes.md e68a6d91ce4c7eb296ef2daadc2bb79c95c317ad15b9fafe40850c67b29c2430 -F ext/qrf/qrf.c 03191b452a83aa970f43f0e3bac25082e1f5b71da5cab2f547789fa5fb0fbf28 +F ext/qrf/qrf.c 773c21962652f6ebc2cf9a7c4decad595e576569b169a85654ad8f3097cc91e9 F ext/qrf/qrf.h 2ac14b0aaacf44636d8c81051bfeab4afae50a98fbb2e10ff5aed0c28a87b2b2 F ext/rbu/rbu.c 801450b24eaf14440d8fd20385aacc751d5c9d6123398df41b1b5aa804bf4ce8 F ext/rbu/rbu1.test 25870dd7db7eb5597e2b4d6e29e7a7e095abf332660f67d89959552ce8f8f255 @@ -737,7 +737,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 8d53771eb51a4ab5f970150c3a70969d8db79cd04a8774c2d296bbcf471a0dd0 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 344518c1bba9c4636bf651b7642304abd2e7075ba35feb4bae42a51e5efe991f -F src/shell.c.in 9fffb35a832b2b2baab518730a11a1c689c9c39a69efdf2748e22f1b87397e79 +F src/shell.c.in bc997c6f1b642eb1b9bbfbbb3bebe640c92bd9c657307f7ae944d48dbc94eaa0 F src/sqlite.h.in 706cacea5308b0244fb6cec92e08310fb427a125375c64137cc1f878ae4cf5c0 F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479 F src/sqlite3ext.h 5d5330f5f8461f5ce74960436ddcfa53ecd09c2b8b23901e22ae38aec3243998 @@ -1441,7 +1441,7 @@ F test/mmap4.test 2e2b4e32555b58da15176e6fe750f17c9dcf7f93 F test/mmapcorrupt.test 470fb44fe92e99c1d23701d156f8c17865f5b027063c9119dcfdb842791f4465 F test/mmapfault.test d4c9eff9cd8c2dc14bc43e71e042f175b0a26fe3 F test/mmapwarm.test 2272005969cd17a910077bd5082f70bc1fefad9a875afec7fc9af483898ecaf3 -F test/modeA.clitest c06845925e37a4aeaadcc9f4878127cc8b5500e848c5bebdcd167a84ad8d55c9 +F test/modeA.clitest aa4c12c45cf454a224258f42041677fbbd1c3d09e254900b8edbffc7c2b4c17f F test/multiplex.test d74c034e52805f6de8cc5432cef8c9eb774bb64ec29b83a22effc8ca4dac1f08 F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a F test/multiplex3.test fac575e0b1b852025575a6a8357701d80933e98b5d2fe6d35ddaa68f92f6a1f7 @@ -1610,7 +1610,7 @@ F test/sharedA.test 64bdd21216dda2c6a3bd3475348ccdc108160f34682c97f2f51c19fc0e21 F test/sharedB.test 1a84863d7a2204e0d42f2e1606577c5e92e4473fa37ea0f5bdf829e4bf8ee707 F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939 F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304 -F test/shell1.test 4e1b57c43b6246fb3e617903992b704ce3798c437b01cbf1e7a5e75060fdb7ea +F test/shell1.test 00b86323fdbd4faaa739b68d5570fc298ebd6ea6c03b1258daf5e99a56a8b572 F test/shell2.test 9d55aa53ca36c6889dedf5a23d849793afbd2dbe62d512ce066cb293a8a66a88 F test/shell3.test 840192774cc4edf7653520c0434a311c7477b9bc324abbc7bd2887915792fa8c F test/shell4.test e25580a792b7b54560c3a76b6968bd8189261f38979fe28e6bc6312c5db280db @@ -1619,7 +1619,7 @@ F test/shell6.test e3b883b61d4916b6906678a35f9d19054861123ad91b856461e0a456273bd F test/shell7.test 43fd8e511c533bab5232e95c7b4be93b243451709e89582600d4b6e67693d5c3 F test/shell8.test 641cf21a99c59404c24e3062923734951c4099a6b6b6520de00cf7a1249ee871 F test/shell9.test 8742a5b390cdcef6369f5aa223e415aa4255a4129ef249b177887dc635a87209 -F test/shellA.test 6319e4ede6658aa71b8d5265bf96071bcccdafe93853b56e670f64345a590029 +F test/shellA.test afbe388ca4c9f7239808ff8dfc6da5c3573dae7a3120f1dcc3d127a0c5e44de5 F test/shellB.test de879b1ea7c25daf1a06b2c882b45a5d002e6580c81c57169ce47084cc6afb6b F test/shmlock.test 9f1f729a7fe2c46c88b156af819ac9b72c0714ac6f7246638a73c5752b5fd13c F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3 @@ -2184,8 +2184,8 @@ F tool/version-info.c 33d0390ef484b3b1cb685d59362be891ea162123cea181cb8e6d2cf6dd F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 291677ebb3e5c2fe00e4340d8d3661fde6bd18494f1d4e56da1efcf613ab21b6 -R d47a8f3aa36b972329f3c3ae399a3f1e -U stephan -Z 013a3e934a07512683fd4b3fb35ca291 +P 27b6aeda5d70e3480b1448b06277a19395b30a205f3f3bac5ce0e4102e3849ed +R 18c9a9871ac7348fc8fa3c2691559943 +U drh +Z 9f2923d3fed41ec33bf29c12acaf6248 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5344405339..e9140b6ec4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -27b6aeda5d70e3480b1448b06277a19395b30a205f3f3bac5ce0e4102e3849ed +f883b854976d2aa679c87edce3b577f38b5fa518f707706320d1ddb38365e2dd diff --git a/src/shell.c.in b/src/shell.c.in index 8c875cdf46..30c9ceda10 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -7685,8 +7685,11 @@ static int modeTitleDsply(ShellState *p, int bAll){ ** any single SQL value to N. Longer values are ** truncated. Zero means "no limit". Only works ** in "line" mode and in columnar modes. -** --limits L,C Shorthand for "--linelimit L --charlimit C". -** Or "off" to mean "0,0". Or "on" for "5,300". +** --limits L,C,T Shorthand for "--linelimit L --charlimit C +** --titlelimit T". The ",T" can be omitted in which +** case the --titlelimit is unchanged. The argument +** can also be "off" to mean "0,0,0" or "on" to +** mean "5,300,20". ** --list List available modes ** --null STRING Render SQL NULL values as the given string ** --once Setting changes to the right are reverted after @@ -7858,15 +7861,16 @@ static int dotCmdMode(ShellState *p){ p->mode.spec.nLineLimit = 0; p->mode.spec.nCharLimit = 0; }else{ - int L, C; - int nNum = sscanf(azArg[i], "%d,%d", &L, &C); - if( nNum!=2 || L<0 || C<0 ){ - dotCmdError(p, i, "bad argument", "Should be \"L,C\" where L and C" - " are unsigned integers"); + int L, C, T = 0; + int nNum = sscanf(azArg[i], "%d,%d,%d", &L, &C, &T); + if( nNum<2 || L<0 || C<0 || T<0){ + dotCmdError(p, i, "bad argument", "Should be \"L,C,T\" where L, C" + " and T are unsigned integers"); return 1; } p->mode.spec.nLineLimit = L; p->mode.spec.nCharLimit = C; + if( nNum==3 ) p->mode.spec.nTitleLimit = T; } chng = 1; }else if( optionMatch(z,"list") ){ @@ -8123,9 +8127,6 @@ static int dotCmdMode(ShellState *p){ u8 e = p->mode.spec.eBlob; sqlite3_str_appendf(pDesc, " --blob-quote %s", azBQuote[e]); } - if( !bAll && p->mode.spec.nLineLimit==0 && p->mode.spec.nCharLimit>0 ){ - sqlite3_str_appendf(pDesc, " --charlimit %d",p->mode.spec.nCharLimit); - } zSetting = aModeStr[pI->eCSep]; if( bAll || (zSetting && cli_strcmp(zSetting,p->mode.spec.zColumnSep)!=0) ){ sqlite3_str_appendf(pDesc, " --colsep "); @@ -8134,16 +8135,26 @@ static int dotCmdMode(ShellState *p){ if( bAll || p->mode.spec.eEsc!=QRF_Auto ){ sqlite3_str_appendf(pDesc, " --escape %s",qrfEscNames[p->mode.spec.eEsc]); } - if( bAll || (p->mode.spec.nLineLimit>0 && p->mode.spec.nCharLimit>0) ){ - if( p->mode.spec.nLineLimit==0 && p->mode.spec.nCharLimit==0 ){ + if( bAll + || (p->mode.spec.nLineLimit>0 && pI->eCx>0) + || p->mode.spec.nCharLimit>0 + || (p->mode.spec.nTitleLimit>0 && pI->eCx>0) + ){ + if( p->mode.spec.nLineLimit==0 + && p->mode.spec.nCharLimit==0 + && p->mode.spec.nTitleLimit==0 + ){ sqlite3_str_appendf(pDesc, " --limits off"); + }else if( p->mode.spec.nLineLimit==5 + && p->mode.spec.nCharLimit==300 + && p->mode.spec.nTitleLimit==20 + ){ + sqlite3_str_appendf(pDesc, " --limits on"); }else{ - sqlite3_str_appendf(pDesc, " --limits %d,%d", - p->mode.spec.nLineLimit, p->mode.spec.nCharLimit); + sqlite3_str_appendf(pDesc, " --limits %d,%d,%d", + p->mode.spec.nLineLimit, p->mode.spec.nCharLimit, + p->mode.spec.nTitleLimit); } - }else - if( p->mode.spec.nCharLimit==0 && p->mode.spec.nLineLimit>0 && pI->eCx>0 ){ - sqlite3_str_appendf(pDesc, " --linelimit %d",p->mode.spec.nLineLimit); } zSetting = aModeStr[pI->eNull]; if( bAll || (zSetting && cli_strcmp(zSetting,p->mode.spec.zNull)!=0) ){ @@ -8164,9 +8175,9 @@ static int dotCmdMode(ShellState *p){ || (pI->eCx && (p->mode.spec.nScreenWidth>0 || p->mode.bAutoScreenWidth)) ){ if( p->mode.bAutoScreenWidth ){ - sqlite3_str_appendall(pDesc, " --screenwidth auto"); + sqlite3_str_appendall(pDesc, " --sw auto"); }else{ - sqlite3_str_appendf(pDesc," --screenwidth %d", + sqlite3_str_appendf(pDesc," --sw %d", p->mode.spec.nScreenWidth); } } @@ -8178,9 +8189,6 @@ static int dotCmdMode(ShellState *p){ sqlite3_str_appendf(pDesc," --textjsonb %s", p->mode.spec.bTextJsonb==QRF_Yes ? "on" : "off"); } - if( bAll || (p->mode.spec.nTitleLimit>0 && pI->eCx!=0) ){ - sqlite3_str_appendf(pDesc, " --titlelimit %d", p->mode.spec.nTitleLimit); - } k = modeTitleDsply(p, bAll); if( k==1 ){ sqlite3_str_appendall(pDesc, " --titles off"); @@ -8213,7 +8221,7 @@ static int dotCmdMode(ShellState *p){ if( !bAll ) sqlite3_str_append(pDesc, " --ww", 5); } zDesc = sqlite3_str_finish(pDesc); - cli_printf(p->out, "current output mode: %s\n", zDesc); + cli_printf(p->out, ".mode %s\n", zDesc); fflush(p->out); sqlite3_free(zDesc); } diff --git a/test/modeA.clitest b/test/modeA.clitest index e4dbbe953c..d5f2185bdc 100644 --- a/test/modeA.clitest +++ b/test/modeA.clitest @@ -79,14 +79,13 @@ END .output memory .mode .output --verify END -current output mode: qbox --limits 5,300 --quote relaxed --screenwidth auto --textjsonb on --titlelimit 20 +.mode qbox --limits on --quote relaxed --sw auto --textjsonb on END .output memory .mode -v .output --verify END -current output mode: qbox --align "" --border on --blob-quote auto --colsep "" --escape auto --limits 5,300 --null "NULL" --quote relaxed --rowsep "" --screenwidth auto --tablename "" --textjsonb on --titlelimit 20 --titles on --widths "" --wordwrap off --wrap 10 +.mode qbox --align "" --border on --blob-quote auto --colsep "" --escape auto --limits on --null "NULL" --quote relaxed --rowsep "" --sw auto --tablename "" --textjsonb on --titles on --widths "" --wordwrap off --wrap 10 END - .output memory --error-prefix "Error:" .mode foo .output --verify END @@ -120,3 +119,109 @@ EXPLAIN SELECT * FROM t1; EXPLAIN QUERY PLAN SELECT * FROM t1; .output --glob "* id │ parent │ notused │ detail *" .explain auto + +# Test cases for limit settings in the .mode command. +.output memory +.mode box --reset +.mode +.output --verify END +.mode box +END +.output memory +.mode --limits 5,300,20 +.mode +.output --verify END +.mode box --limits on +END +.output memory +.mode --limits 5,300,19 +.mode +.output --verify END +.mode box --limits 5,300,19 +END +.output memory +.mode --limits 0,0,0 +.mode -v +.output --verify END +.mode box --align "" --border on --blob-quote auto --colsep "" --escape auto --limits off --null "" --quote off --rowsep "" --sw 0 --tablename "" --textjsonb off --titles on --widths "" --wordwrap off +END + +.output memory +.mode --linelimit 123 +.mode +.output --verify END +.mode box --limits 123,0,0 +END + +.output memory +.mode --linelimit 0 -charlimit 123 +.mode +.output --verify END +.mode box --limits 0,123,0 +END + +.output memory +.mode --charlimit 0 -titlelimit 123 +.mode +.output --verify END +.mode box --limits 0,0,123 +END + +.output memory +.mode list +.mode +.output --verify END +.mode list +END + +.output memory +.mode -limits 0,123,0 +.mode +.output --verify END +.mode list --limits 0,123,0 +END + +.output memory +.mode -limits 123,0,0 +.mode +.output --verify END +.mode list +END + +# --titlelimit functionality +# +.output memory +.mode line --limits off --titlelimit 20 +SELECT a AS 'abcdefghijklmnopqrstuvwxyz', b FROM t2 WHERE c=3; +.output --verify END +abcdefghijklmnopq...: The quick fox jumps over the lazy brown dog + b: 2 +END +.output memory +.mode line --titlelimit 10 +SELECT a AS 'abcdefghijklmnopqrstuvwxyz', b FROM t2 WHERE c=3; +.output --verify END +abcdefg...: The quick fox jumps over the lazy brown dog + b: 2 +END +.output memory +.mode line --titlelimit 2 +SELECT a AS 'abcdefghijklmnopqrstuvwxyz', b FROM t2 WHERE c=3; +.output --verify END +ab: The quick fox jumps over the lazy brown dog + b: 2 +END +.output memory +.mode line --titlelimit 4 +SELECT a AS 'abcd', b FROM t2 WHERE c=3; +.output --verify END +abcd: The quick fox jumps over the lazy brown dog + b: 2 +END +.output memory +.mode line --titlelimit 3 +SELECT a AS 'abcd', b FROM t2 WHERE c=3; +.output --verify END +...: The quick fox jumps over the lazy brown dog + b: 2 +END diff --git a/test/shell1.test b/test/shell1.test index 58f0d6ac4e..c5715784c3 100644 --- a/test/shell1.test +++ b/test/shell1.test @@ -456,7 +456,7 @@ do_test shell1-3.12.3 { # tcl TCL list elements do_test shell1-3.13.1 { catchcmd "test.db" ".mode batch\n.mode" -} {0 {current output mode: list}} +} {0 {.mode list}} do_test shell1-3.13.2 { catchcmd "test.db" ".mode FOO" } {1 {line 1: .mode FOO diff --git a/test/shellA.test b/test/shellA.test index e6b7bcd33f..45e5e2c911 100644 --- a/test/shellA.test +++ b/test/shellA.test @@ -124,13 +124,13 @@ do_test shellA-2.1 { } do_test shellA-2.2 { exec {*}$CLI -noinit test.db --quote {.mode -v} -} {/*current output mode: quote* --escape auto*/} +} {/*.mode quote* --escape auto*/} do_test shellA-2.3 { exec {*}$CLI -noinit -compat 20250101 test.db --quote --escape SYMBOL {.mode} -} {current output mode: quote --escape symbol} +} {.mode quote --escape symbol} do_test shellA-2.4 { exec {*}$CLI -noinit -compat 20250101 test.db --quote --escape OFF {.mode} -} {current output mode: quote --escape off} +} {.mode quote --escape off} # ".mode line" -- 2.47.3