Fossil SCM

Latest changes from trunk in preparation for merging.

andybradford 2025-12-04 15:22 cmd-tag-find merge
Commit 2317172a81618df087c1a2c201c9a4b5fab96c8c99a93d1f80ae5772c004effe
--- extsrc/qrf.h
+++ extsrc/qrf.h
@@ -35,10 +35,11 @@
3535
unsigned char bWordWrap; /* Try to wrap on word boundaries */
3636
unsigned char bTextJsonb; /* Render JSONB blobs as JSON text */
3737
unsigned char eDfltAlign; /* Default alignment, no covered by aAlignment */
3838
unsigned char eTitleAlign; /* Alignment for column headers */
3939
unsigned char bSplitColumn; /* Wrap single-column output into many columns */
40
+ unsigned char bBorder; /* Show outer border in Box and Table styles */
4041
short int nWrap; /* Wrap columns wider than this */
4142
short int nScreenWidth; /* Maximum overall table width */
4243
short int nLineLimit; /* Maximum number of lines for any row */
4344
int nCharLimit; /* Maximum number of characters in a cell */
4445
int nWidth; /* Number of entries in aWidth[] */
4546
--- extsrc/qrf.h
+++ extsrc/qrf.h
@@ -35,10 +35,11 @@
35 unsigned char bWordWrap; /* Try to wrap on word boundaries */
36 unsigned char bTextJsonb; /* Render JSONB blobs as JSON text */
37 unsigned char eDfltAlign; /* Default alignment, no covered by aAlignment */
38 unsigned char eTitleAlign; /* Alignment for column headers */
39 unsigned char bSplitColumn; /* Wrap single-column output into many columns */
 
40 short int nWrap; /* Wrap columns wider than this */
41 short int nScreenWidth; /* Maximum overall table width */
42 short int nLineLimit; /* Maximum number of lines for any row */
43 int nCharLimit; /* Maximum number of characters in a cell */
44 int nWidth; /* Number of entries in aWidth[] */
45
--- extsrc/qrf.h
+++ extsrc/qrf.h
@@ -35,10 +35,11 @@
35 unsigned char bWordWrap; /* Try to wrap on word boundaries */
36 unsigned char bTextJsonb; /* Render JSONB blobs as JSON text */
37 unsigned char eDfltAlign; /* Default alignment, no covered by aAlignment */
38 unsigned char eTitleAlign; /* Alignment for column headers */
39 unsigned char bSplitColumn; /* Wrap single-column output into many columns */
40 unsigned char bBorder; /* Show outer border in Box and Table styles */
41 short int nWrap; /* Wrap columns wider than this */
42 short int nScreenWidth; /* Maximum overall table width */
43 short int nLineLimit; /* Maximum number of lines for any row */
44 int nCharLimit; /* Maximum number of characters in a cell */
45 int nWidth; /* Number of entries in aWidth[] */
46
+209 -102
--- extsrc/shell.c
+++ extsrc/shell.c
@@ -704,10 +704,11 @@
704704
unsigned char bWordWrap; /* Try to wrap on word boundaries */
705705
unsigned char bTextJsonb; /* Render JSONB blobs as JSON text */
706706
unsigned char eDfltAlign; /* Default alignment, no covered by aAlignment */
707707
unsigned char eTitleAlign; /* Alignment for column headers */
708708
unsigned char bSplitColumn; /* Wrap single-column output into many columns */
709
+ unsigned char bBorder; /* Show outer border in Box and Table styles */
709710
short int nWrap; /* Wrap columns wider than this */
710711
short int nScreenWidth; /* Maximum overall table width */
711712
short int nLineLimit; /* Maximum number of lines for any row */
712713
int nCharLimit; /* Maximum number of characters in a cell */
713714
int nWidth; /* Number of entries in aWidth[] */
@@ -1990,19 +1991,19 @@
19901991
int k; /* Bytes in a VT100 code */
19911992
int n; /* Output column number */
19921993
const unsigned char *z = (const unsigned char*)zIn;
19931994
unsigned char c = 0;
19941995
1995
- if( zIn[0]==0 ){
1996
+ if( z[0]==0 ){
19961997
*pnThis = 0;
19971998
*pnWide = 0;
19981999
*piNext = 0;
19992000
return;
20002001
}
20012002
n = 0;
2002
- for(i=0; n<w; i++){
2003
- c = zIn[i];
2003
+ for(i=0; n<=w; i++){
2004
+ c = z[i];
20042005
if( c>=0xc0 ){
20052006
int u;
20062007
int len = sqlite3_qrf_decode_utf8(&z[i], &u);
20072008
int wcw = sqlite3_qrf_wcwidth(u);
20082009
if( wcw+n>w ) break;
@@ -2009,23 +2010,26 @@
20092010
i += len-1;
20102011
n += wcw;
20112012
continue;
20122013
}
20132014
if( c>=' ' ){
2015
+ if( n==w ) break;
20142016
n++;
20152017
continue;
20162018
}
20172019
if( c==0 || c=='\n' ) break;
2018
- if( c=='\r' && zIn[i+1]=='\n' ){ c = zIn[++i]; break; }
2020
+ if( c=='\r' && z[i+1]=='\n' ){ c = z[++i]; break; }
20192021
if( c=='\t' ){
20202022
int wcw = 8 - (n&7);
20212023
if( n+wcw>w ) break;
20222024
n += wcw;
20232025
continue;
20242026
}
20252027
if( c==0x1b && (k = qrfIsVt100(&z[i]))>0 ){
20262028
i += k-1;
2029
+ }else if( n==w ){
2030
+ break;
20272031
}else{
20282032
n++;
20292033
}
20302034
}
20312035
if( c==0 ){
@@ -2121,40 +2125,10 @@
21212125
}
21222126
}
21232127
sqlite3_str_append(pOut, (const char*)z, i);
21242128
}
21252129
2126
-/*
2127
-** Output horizontally justified text into pOut. The text is the
2128
-** first nVal bytes of zVal. Include nWS bytes of whitespace, either
2129
-** split between both sides, or on the left, or on the right, depending
2130
-** on eAlign.
2131
-*/
2132
-static void qrfPrintAligned(
2133
- sqlite3_str *pOut, /* Append text here */
2134
- const char *zVal, /* Text to append */
2135
- int nVal, /* Use only the first nVal bytes of zVal[] */
2136
- int nWS, /* Whitespace for horizonal alignment */
2137
- unsigned char eAlign /* Alignment type */
2138
-){
2139
- eAlign &= QRF_ALIGN_HMASK;
2140
- if( eAlign==QRF_ALIGN_Center ){
2141
- /* Center the text */
2142
- sqlite3_str_appendchar(pOut, nWS/2, ' ');
2143
- qrfAppendWithTabs(pOut, zVal, nVal);
2144
- sqlite3_str_appendchar(pOut, nWS - nWS/2, ' ');
2145
- }else if( eAlign==QRF_ALIGN_Right){
2146
- /* Right justify the text */
2147
- sqlite3_str_appendchar(pOut, nWS, ' ');
2148
- qrfAppendWithTabs(pOut, zVal, nVal);
2149
- }else{
2150
- /* Left justify the next */
2151
- qrfAppendWithTabs(pOut, zVal, nVal);
2152
- sqlite3_str_appendchar(pOut, nWS, ' ');
2153
- }
2154
-}
2155
-
21562130
/*
21572131
** GCC does not define the offsetof() macro so we'll have to do it
21582132
** ourselves.
21592133
*/
21602134
#ifndef offsetof
@@ -2174,27 +2148,60 @@
21742148
sqlite3_int64 nRow; /* Number of rows */
21752149
sqlite3_int64 nAlloc; /* Number of cells allocated */
21762150
sqlite3_int64 n; /* Number of cells. nCol*nRow */
21772151
char **az; /* Content of all cells */
21782152
int *aiWth; /* Width of each cell */
2153
+ unsigned char *abNum; /* True for each numeric cell */
21792154
struct qrfPerCol { /* Per-column data */
21802155
char *z; /* Cache of text for current row */
21812156
int w; /* Computed width of this column */
21822157
int mxW; /* Maximum natural (unwrapped) width */
21832158
unsigned char e; /* Alignment */
21842159
unsigned char fx; /* Width is fixed */
2160
+ unsigned char bNum; /* True if is numeric */
21852161
} *a; /* One per column */
21862162
};
2163
+
2164
+/*
2165
+** Output horizontally justified text into pOut. The text is the
2166
+** first nVal bytes of zVal. Include nWS bytes of whitespace, either
2167
+** split between both sides, or on the left, or on the right, depending
2168
+** on eAlign.
2169
+*/
2170
+static void qrfPrintAligned(
2171
+ sqlite3_str *pOut, /* Append text here */
2172
+ struct qrfPerCol *pCol, /* Information about the text to print */
2173
+ int nVal, /* Use only the first nVal bytes of zVal[] */
2174
+ int nWS /* Whitespace for horizonal alignment */
2175
+){
2176
+ unsigned char eAlign = pCol->e & QRF_ALIGN_HMASK;
2177
+ if( eAlign==QRF_Auto && pCol->bNum ) eAlign = QRF_ALIGN_Right;
2178
+ if( eAlign==QRF_ALIGN_Center ){
2179
+ /* Center the text */
2180
+ sqlite3_str_appendchar(pOut, nWS/2, ' ');
2181
+ qrfAppendWithTabs(pOut, pCol->z, nVal);
2182
+ sqlite3_str_appendchar(pOut, nWS - nWS/2, ' ');
2183
+ }else if( eAlign==QRF_ALIGN_Right ){
2184
+ /* Right justify the text */
2185
+ sqlite3_str_appendchar(pOut, nWS, ' ');
2186
+ qrfAppendWithTabs(pOut, pCol->z, nVal);
2187
+ }else{
2188
+ /* Left justify the text */
2189
+ qrfAppendWithTabs(pOut, pCol->z, nVal);
2190
+ sqlite3_str_appendchar(pOut, nWS, ' ');
2191
+ }
2192
+}
21872193
21882194
/*
21892195
** Free all the memory allocates in the qrfColData object
21902196
*/
21912197
static void qrfColDataFree(qrfColData *p){
21922198
sqlite3_int64 i;
21932199
for(i=0; i<p->n; i++) sqlite3_free(p->az[i]);
21942200
sqlite3_free(p->az);
21952201
sqlite3_free(p->aiWth);
2202
+ sqlite3_free(p->abNum);
21962203
sqlite3_free(p->a);
21972204
memset(p, 0, sizeof(*p));
21982205
}
21992206
22002207
/*
@@ -2202,10 +2209,11 @@
22022209
** Return non-zero if a memory allocation fails.
22032210
*/
22042211
static int qrfColDataEnlarge(qrfColData *p){
22052212
char **azData;
22062213
int *aiWth;
2214
+ unsigned char *abNum;
22072215
p->nAlloc = 2*p->nAlloc + 10*p->nCol;
22082216
azData = sqlite3_realloc64(p->az, p->nAlloc*sizeof(char*));
22092217
if( azData==0 ){
22102218
qrfOom(p->p);
22112219
qrfColDataFree(p);
@@ -2217,26 +2225,38 @@
22172225
qrfOom(p->p);
22182226
qrfColDataFree(p);
22192227
return 1;
22202228
}
22212229
p->aiWth = aiWth;
2230
+ abNum = sqlite3_realloc64(p->abNum, p->nAlloc);
2231
+ if( abNum==0 ){
2232
+ qrfOom(p->p);
2233
+ qrfColDataFree(p);
2234
+ return 1;
2235
+ }
2236
+ p->abNum = abNum;
22222237
return 0;
22232238
}
22242239
22252240
/*
22262241
** Print a markdown or table-style row separator using ascii-art
22272242
*/
22282243
static void qrfRowSeparator(sqlite3_str *pOut, qrfColData *p, char cSep){
22292244
int i;
22302245
if( p->nCol>0 ){
2231
- sqlite3_str_append(pOut, &cSep, 1);
2246
+ int useBorder = p->p->spec.bBorder!=QRF_No;
2247
+ if( useBorder ){
2248
+ sqlite3_str_append(pOut, &cSep, 1);
2249
+ }
22322250
sqlite3_str_appendchar(pOut, p->a[0].w+p->nMargin, '-');
22332251
for(i=1; i<p->nCol; i++){
22342252
sqlite3_str_append(pOut, &cSep, 1);
22352253
sqlite3_str_appendchar(pOut, p->a[i].w+p->nMargin, '-');
22362254
}
2237
- sqlite3_str_append(pOut, &cSep, 1);
2255
+ if( useBorder ){
2256
+ sqlite3_str_append(pOut, &cSep, 1);
2257
+ }
22382258
}
22392259
sqlite3_str_append(pOut, "\n", 1);
22402260
}
22412261
22422262
/*
@@ -2290,17 +2310,22 @@
22902310
const char *zSep2,
22912311
const char *zSep3
22922312
){
22932313
int i;
22942314
if( p->nCol>0 ){
2295
- sqlite3_str_appendall(pOut, zSep1);
2315
+ int useBorder = p->p->spec.bBorder!=QRF_No;
2316
+ if( useBorder ){
2317
+ sqlite3_str_appendall(pOut, zSep1);
2318
+ }
22962319
qrfBoxLine(pOut, p->a[0].w+p->nMargin);
22972320
for(i=1; i<p->nCol; i++){
22982321
sqlite3_str_appendall(pOut, zSep2);
22992322
qrfBoxLine(pOut, p->a[i].w+p->nMargin);
23002323
}
2301
- sqlite3_str_appendall(pOut, zSep3);
2324
+ if( useBorder ){
2325
+ sqlite3_str_appendall(pOut, zSep3);
2326
+ }
23022327
}
23032328
sqlite3_str_append(pOut, "\n", 1);
23042329
}
23052330
23062331
/*
@@ -2379,10 +2404,11 @@
23792404
static void qrfSplitColumn(qrfColData *pData, Qrf *p){
23802405
int nCol = 1;
23812406
int *aw = 0;
23822407
char **az = 0;
23832408
int *aiWth = 0;
2409
+ unsigned char *abNum = 0;
23842410
int nColNext = 2;
23852411
int w;
23862412
struct qrfPerCol *a = 0;
23872413
sqlite3_int64 nRow = 1;
23882414
sqlite3_int64 i;
@@ -2416,35 +2442,47 @@
24162442
if( a==0 ){
24172443
sqlite3_free(az);
24182444
sqlite3_free(aiWth);
24192445
qrfOom(p);
24202446
return;
2447
+ }
2448
+ abNum = sqlite3_malloc64( nRow*nCol );
2449
+ if( abNum==0 ){
2450
+ sqlite3_free(az);
2451
+ sqlite3_free(aiWth);
2452
+ sqlite3_free(a);
2453
+ qrfOom(p);
2454
+ return;
24212455
}
24222456
for(i=0; i<pData->n; i++){
24232457
sqlite3_int64 j = (i%nRow)*nCol + (i/nRow);
24242458
az[j] = pData->az[i];
2459
+ abNum[j]= pData->abNum[i];
24252460
pData->az[i] = 0;
24262461
aiWth[j] = pData->aiWth[i];
24272462
}
24282463
while( i<nRow*nCol ){
24292464
sqlite3_int64 j = (i%nRow)*nCol + (i/nRow);
24302465
az[j] = sqlite3_mprintf("");
24312466
if( az[j]==0 ) qrfOom(p);
24322467
aiWth[j] = 0;
2468
+ abNum[j] = 0;
24332469
i++;
24342470
}
24352471
for(i=0; i<nCol; i++){
24362472
a[i].fx = a[i].mxW = a[i].w = aw[i];
24372473
a[i].e = pData->a[0].e;
24382474
}
24392475
sqlite3_free(pData->az);
24402476
sqlite3_free(pData->aiWth);
24412477
sqlite3_free(pData->a);
2478
+ sqlite3_free(pData->abNum);
24422479
sqlite3_free(aw);
24432480
pData->az = az;
24442481
pData->aiWth = aiWth;
24452482
pData->a = a;
2483
+ pData->abNum = abNum;
24462484
pData->nCol = nCol;
24472485
pData->n = pData->nAlloc = nRow*nCol;
24482486
for(i=w=0; i<nCol; i++) w += a[i].w;
24492487
pData->nMargin = (p->spec.nScreenWidth - w)/(nCol - 1);
24502488
if( pData->nMargin>5 ) pData->nMargin = 5;
@@ -2464,10 +2502,11 @@
24642502
if( p->spec.nScreenWidth==0 ) return;
24652503
if( p->spec.eStyle==QRF_STYLE_Column ){
24662504
sepW = pData->nCol*2 - 2;
24672505
}else{
24682506
sepW = pData->nCol*3 + 1;
2507
+ if( p->spec.bBorder==QRF_No ) sepW -= 2;
24692508
}
24702509
nCol = pData->nCol;
24712510
for(i=sumW=0; i<nCol; i++) sumW += pData->a[i].w;
24722511
if( p->spec.nScreenWidth >= sumW+sepW ) return;
24732512
@@ -2475,10 +2514,11 @@
24752514
pData->nMargin = 0;
24762515
if( p->spec.eStyle==QRF_STYLE_Column ){
24772516
sepW = pData->nCol - 1;
24782517
}else{
24792518
sepW = pData->nCol + 1;
2519
+ if( p->spec.bBorder==QRF_No ) sepW -= 2;
24802520
}
24812521
targetW = p->spec.nScreenWidth - sepW;
24822522
24832523
#define MIN_SQUOZE 8
24842524
#define MIN_EX_SQUOZE 16
@@ -2547,10 +2587,11 @@
25472587
}
25482588
25492589
/* Initialize the data container */
25502590
memset(&data, 0, sizeof(data));
25512591
data.nCol = p->nCol;
2592
+ data.p = p;
25522593
data.a = sqlite3_malloc64( nColumn*sizeof(struct qrfPerCol) );
25532594
if( data.a==0 ){
25542595
qrfOom(p);
25552596
return;
25562597
}
@@ -2560,10 +2601,11 @@
25602601
25612602
/* Load the column header names and all cell content into data */
25622603
if( p->spec.bTitles==QRF_Yes ){
25632604
unsigned char saved_eText = p->spec.eText;
25642605
p->spec.eText = p->spec.eTitle;
2606
+ memset(data.abNum, 0, nColumn);
25652607
for(i=0; i<nColumn; i++){
25662608
const char *z = (const char*)sqlite3_column_name(p->pStmt,i);
25672609
int nNL = 0;
25682610
int n, w;
25692611
pStr = sqlite3_str_new(p->db);
@@ -2584,14 +2626,16 @@
25842626
}
25852627
for(i=0; i<nColumn; i++){
25862628
char *z;
25872629
int nNL = 0;
25882630
int n, w;
2631
+ int eType = sqlite3_column_type(p->pStmt,i);
25892632
pStr = sqlite3_str_new(p->db);
25902633
qrfRenderValue(p, pStr, i);
25912634
n = sqlite3_str_length(pStr);
25922635
z = data.az[data.n] = sqlite3_str_finish(pStr);
2636
+ data.abNum[data.n] = eType==SQLITE_INTEGER || eType==SQLITE_FLOAT;
25932637
data.aiWth[data.n] = w = qrfDisplayWidth(z, n, &nNL);
25942638
data.n++;
25952639
if( w>data.a[i].mxW ) data.a[i].mxW = w;
25962640
if( nNL ) data.bMultiRow = 1;
25972641
}
@@ -2677,11 +2721,16 @@
26772721
}else{
26782722
rowStart = BOX_13;
26792723
colSep = BOX_13;
26802724
rowSep = BOX_13 "\n";
26812725
}
2682
- qrfBoxSeparator(p->pOut, &data, BOX_23, BOX_234, BOX_34);
2726
+ if( p->spec.bBorder==QRF_No){
2727
+ rowStart += 3;
2728
+ rowSep = "\n";
2729
+ }else{
2730
+ qrfBoxSeparator(p->pOut, &data, BOX_23, BOX_234, BOX_34);
2731
+ }
26832732
break;
26842733
case QRF_STYLE_Table:
26852734
if( data.nMargin ){
26862735
rowStart = "| ";
26872736
colSep = " | ";
@@ -2689,11 +2738,16 @@
26892738
}else{
26902739
rowStart = "|";
26912740
colSep = "|";
26922741
rowSep = "|\n";
26932742
}
2694
- qrfRowSeparator(p->pOut, &data, '+');
2743
+ if( p->spec.bBorder==QRF_No ){
2744
+ rowStart += 1;
2745
+ rowSep = "\n";
2746
+ }else{
2747
+ qrfRowSeparator(p->pOut, &data, '+');
2748
+ }
26952749
break;
26962750
case QRF_STYLE_Column: {
26972751
static const char zSpace[] = " ";
26982752
rowStart = "";
26992753
if( data.nMargin<2 ){
@@ -2721,20 +2775,31 @@
27212775
szRowStart = (int)strlen(rowStart);
27222776
szRowSep = (int)strlen(rowSep);
27232777
szColSep = (int)strlen(colSep);
27242778
27252779
bWW = (p->spec.bWordWrap==QRF_Yes && data.bMultiRow);
2726
- bRTrim = (p->spec.eStyle==QRF_STYLE_Column);
2780
+ if( p->spec.eStyle==QRF_STYLE_Column
2781
+ || (p->spec.bBorder==QRF_No
2782
+ && (p->spec.eStyle==QRF_STYLE_Box || p->spec.eStyle==QRF_STYLE_Table)
2783
+ )
2784
+ ){
2785
+ bRTrim = 1;
2786
+ }else{
2787
+ bRTrim = 0;
2788
+ }
27272789
for(i=0; i<data.n; i+=nColumn){
27282790
int bMore;
27292791
int nRow = 0;
27302792
27312793
/* Draw a single row of the table. This might be the title line
27322794
** (if there is a title line) or a row in the body of the table.
27332795
** The column number will be j. The row number is i/nColumn.
27342796
*/
2735
- for(j=0; j<nColumn; j++){ data.a[j].z = data.az[i+j]; }
2797
+ for(j=0; j<nColumn; j++){
2798
+ data.a[j].z = data.az[i+j];
2799
+ data.a[j].bNum = data.abNum[i+j];
2800
+ }
27362801
do{
27372802
sqlite3_str_append(p->pOut, rowStart, szRowStart);
27382803
bMore = 0;
27392804
for(j=0; j<nColumn; j++){
27402805
int nThis = 0;
@@ -2741,11 +2806,11 @@
27412806
int nWide = 0;
27422807
int iNext = 0;
27432808
int nWS;
27442809
qrfWrapLine(data.a[j].z, data.a[j].w, bWW, &nThis, &nWide, &iNext);
27452810
nWS = data.a[j].w - nWide;
2746
- qrfPrintAligned(p->pOut, data.a[j].z, nThis, nWS, data.a[j].e);
2811
+ qrfPrintAligned(p->pOut, &data.a[j], nThis, nWS);
27472812
data.a[j].z += iNext;
27482813
if( data.a[j].z[0]!=0 ){
27492814
bMore = 1;
27502815
}
27512816
if( j<nColumn-1 ){
@@ -2763,11 +2828,12 @@
27632828
if( data.a[j].z[0]==0 ){
27642829
sqlite3_str_appendchar(p->pOut, data.a[j].w, ' ');
27652830
}else{
27662831
int nE = 3;
27672832
if( nE>data.a[j].w ) nE = data.a[j].w;
2768
- qrfPrintAligned(p->pOut, "...", nE, data.a[j].w-nE, data.a[j].e);
2833
+ data.a[j].z = "...";
2834
+ qrfPrintAligned(p->pOut, &data.a[j], nE, data.a[j].w-nE);
27692835
}
27702836
if( j<nColumn-1 ){
27712837
sqlite3_str_append(p->pOut, colSep, szColSep);
27722838
}else{
27732839
if( bRTrim ) qrfRTrim(p->pOut);
@@ -2824,17 +2890,19 @@
28242890
}
28252891
}
28262892
}
28272893
28282894
/* Draw the line across the bottom of the table */
2829
- switch( p->spec.eStyle ){
2830
- case QRF_STYLE_Box:
2831
- qrfBoxSeparator(p->pOut, &data, BOX_12, BOX_124, BOX_14);
2832
- break;
2833
- case QRF_STYLE_Table:
2834
- qrfRowSeparator(p->pOut, &data, '+');
2835
- break;
2895
+ if( p->spec.bBorder!=QRF_No ){
2896
+ switch( p->spec.eStyle ){
2897
+ case QRF_STYLE_Box:
2898
+ qrfBoxSeparator(p->pOut, &data, BOX_12, BOX_124, BOX_14);
2899
+ break;
2900
+ case QRF_STYLE_Table:
2901
+ qrfRowSeparator(p->pOut, &data, '+');
2902
+ break;
2903
+ }
28362904
}
28372905
qrfWrite(p);
28382906
28392907
qrfColDataFree(&data);
28402908
return;
@@ -6010,17 +6078,20 @@
60106078
}
60116079
/* End of the hashing logic
60126080
*****************************************************************************/
60136081
60146082
/*
6015
-** Implementation of the sha1(X) function.
6083
+** Two SQL functions: sha1(X) and sha1b(X).
60166084
**
6017
-** Return a lower-case hexadecimal rendering of the SHA1 hash of the
6018
-** argument X. If X is a BLOB, it is hashed as is. For all other
6085
+** sha1(X) returns a lower-case hexadecimal rendering of the SHA1 hash
6086
+** of the argument X. If X is a BLOB, it is hashed as is. For all other
60196087
** types of input, X is converted into a UTF-8 string and the string
6020
-** is hash without the trailing 0x00 terminator. The hash of a NULL
6088
+** is hashed without the trailing 0x00 terminator. The hash of a NULL
60216089
** value is NULL.
6090
+**
6091
+** sha1b(X) is the same except that it returns a 20-byte BLOB containing
6092
+** the binary hash instead of a hexadecimal string.
60226093
*/
60236094
static void sha1Func(
60246095
sqlite3_context *context,
60256096
int argc,
60266097
sqlite3_value **argv
@@ -6037,15 +6108,17 @@
60376108
hash_step(&cx, sqlite3_value_blob(argv[0]), nByte);
60386109
}else{
60396110
hash_step(&cx, sqlite3_value_text(argv[0]), nByte);
60406111
}
60416112
if( sqlite3_user_data(context)!=0 ){
6113
+ /* sha1b() - binary result */
60426114
hash_finish(&cx, zOut, 1);
60436115
sqlite3_result_blob(context, zOut, 20, SQLITE_TRANSIENT);
60446116
}else{
6117
+ /* sha1() - hexadecimal text result */
60456118
hash_finish(&cx, zOut, 0);
6046
- sqlite3_result_blob(context, zOut, 40, SQLITE_TRANSIENT);
6119
+ sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT);
60476120
}
60486121
}
60496122
60506123
/*
60516124
** Implementation of the sha1_query(SQL) function.
@@ -24117,19 +24190,20 @@
2411724190
#define MODE_Json 10 /* Output JSON */
2411824191
#define MODE_Line 11 /* One column per line. Blank line between records */
2411924192
#define MODE_List 12 /* One record per line with a separator */
2412024193
#define MODE_Markdown 13 /* Markdown formatting */
2412124194
#define MODE_Off 14 /* No query output shown */
24122
-#define MODE_QBox 15 /* BOX with SQL-quoted content */
24123
-#define MODE_Quote 16 /* Quote values as for SQL */
24124
-#define MODE_Split 17 /* Split-column mode */
24125
-#define MODE_Table 18 /* MySQL-style table formatting */
24126
-#define MODE_Tabs 19 /* Tab-separated values */
24127
-#define MODE_Tcl 20 /* Space-separated list of TCL strings */
24128
-#define MODE_Www 21 /* Full web-page output */
24129
-
24130
-#define MODE_BUILTIN 21 /* Maximum built-in mode */
24195
+#define MODE_Psql 15 /* Similar to psql */
24196
+#define MODE_QBox 16 /* BOX with SQL-quoted content */
24197
+#define MODE_Quote 17 /* Quote values as for SQL */
24198
+#define MODE_Split 18 /* Split-column mode */
24199
+#define MODE_Table 19 /* MySQL-style table formatting */
24200
+#define MODE_Tabs 20 /* Tab-separated values */
24201
+#define MODE_Tcl 21 /* Space-separated list of TCL strings */
24202
+#define MODE_Www 22 /* Full web-page output */
24203
+
24204
+#define MODE_BUILTIN 22 /* Maximum built-in mode */
2413124205
#define MODE_BATCH 50 /* Default mode for batch processing */
2413224206
#define MODE_TTY 51 /* Default mode for interactive processing */
2413324207
#define MODE_USER 75 /* First user-defined mode */
2413424208
#define MODE_N_USER 25 /* Maximum number of user-defined modes */
2413524209
@@ -24146,10 +24220,11 @@
2414624220
unsigned char eHdr; /* Default header encoding. */
2414724221
unsigned char eBlob; /* Default blob encoding. */
2414824222
unsigned char bHdr; /* Show headers by default. 0: n/a, 1: no 2: yes */
2414924223
unsigned char eStyle; /* Underlying QRF style */
2415024224
unsigned char eCx; /* 0: other, 1: line, 2: columnar */
24225
+ unsigned char mFlg; /* Flags. 1=border-off 2=split-column */
2415124226
};
2415224227
2415324228
/* String constants used by built-in modes */
2415424229
static const char *aModeStr[] =
2415524230
/* 0 1 2 3 4 5 6 7 8 */
@@ -24156,33 +24231,34 @@
2415624231
{ 0, "\n", "|", " ", ",", "\r\n", "\036", "\037", "\t",
2415724232
"", "NULL", "null", "\"\"" };
2415824233
/* 9 10 11 12 */
2415924234
2416024235
static const ModeInfo aModeInfo[] = {
24161
-/* zName eCSep eRSep eNull eText eHdr eBlob bHdr eStyle eCx */
24162
- { "ascii", 7, 6, 9, 1, 1, 0, 1, 12, 0 },
24163
- { "box", 0, 0, 9, 1, 1, 0, 2, 1, 2 },
24164
- { "c", 4, 1, 10, 5, 5, 4, 1, 12, 0 },
24165
- { "column", 0, 0, 9, 1, 1, 0, 2, 2, 2 },
24166
- { "count", 0, 0, 0, 0, 0, 0, 0, 3, 0 },
24167
- { "csv", 4, 5, 9, 3, 3, 0, 1, 12, 0 },
24168
- { "html", 0, 0, 9, 4, 4, 0, 2, 7, 0 },
24169
- { "insert", 0, 0, 10, 2, 2, 0, 1, 8, 0 },
24170
- { "jatom", 4, 1, 11, 6, 6, 0, 1, 12, 0 },
24171
- { "jobject", 0, 1, 11, 6, 6, 0, 0, 10, 0 },
24172
- { "json", 0, 0, 11, 6, 6, 0, 0, 9, 0 },
24173
- { "line", 0, 1, 9, 1, 1, 0, 0, 11, 1 },
24174
- { "list", 2, 1, 9, 1, 1, 0, 1, 12, 0 },
24175
- { "markdown", 0, 0, 9, 1, 1, 0, 2, 13, 2 },
24176
- { "off", 0, 0, 0, 0, 0, 0, 0, 14, 0 },
24177
- { "qbox", 0, 0, 10, 2, 1, 0, 2, 1, 2 },
24178
- { "quote", 4, 1, 10, 2, 2, 0, 1, 12, 0 },
24179
- { "split", 0, 0, 9, 1, 1, 0, 1, 2, 2 },
24180
- { "table", 0, 0, 9, 1, 1, 0, 2, 19, 2 },
24181
- { "tabs", 8, 1, 9, 3, 3, 0, 1, 12, 0 },
24182
- { "tcl", 3, 1, 12, 5, 5, 4, 1, 12, 0 },
24183
- { "www", 0, 0, 9, 4, 4, 0, 2, 7, 0 }
24236
+/* zName eCSep eRSep eNull eText eHdr eBlob bHdr eStyle eCx mFlg */
24237
+ { "ascii", 7, 6, 9, 1, 1, 0, 1, 12, 0, 0 },
24238
+ { "box", 0, 0, 9, 1, 1, 0, 2, 1, 2, 0 },
24239
+ { "c", 4, 1, 10, 5, 5, 4, 1, 12, 0, 0 },
24240
+ { "column", 0, 0, 9, 1, 1, 0, 2, 2, 2, 0 },
24241
+ { "count", 0, 0, 0, 0, 0, 0, 0, 3, 0, 0 },
24242
+ { "csv", 4, 5, 9, 3, 3, 0, 1, 12, 0, 0 },
24243
+ { "html", 0, 0, 9, 4, 4, 0, 2, 7, 0, 0 },
24244
+ { "insert", 0, 0, 10, 2, 2, 0, 1, 8, 0, 0 },
24245
+ { "jatom", 4, 1, 11, 6, 6, 0, 1, 12, 0, 0 },
24246
+ { "jobject", 0, 1, 11, 6, 6, 0, 0, 10, 0, 0 },
24247
+ { "json", 0, 0, 11, 6, 6, 0, 0, 9, 0, 0 },
24248
+ { "line", 0, 1, 9, 1, 1, 0, 0, 11, 1, 0 },
24249
+ { "list", 2, 1, 9, 1, 1, 0, 1, 12, 0, 0 },
24250
+ { "markdown", 0, 0, 9, 1, 1, 0, 2, 13, 2, 0 },
24251
+ { "off", 0, 0, 0, 0, 0, 0, 0, 14, 0, 0 },
24252
+ { "psql", 0, 0, 9, 1, 1, 0, 2, 19, 2, 1 },
24253
+ { "qbox", 0, 0, 10, 2, 1, 0, 2, 1, 2, 0 },
24254
+ { "quote", 4, 1, 10, 2, 2, 0, 1, 12, 0, 0 },
24255
+ { "split", 0, 0, 9, 1, 1, 0, 1, 2, 2, 2 },
24256
+ { "table", 0, 0, 9, 1, 1, 0, 2, 19, 2, 0 },
24257
+ { "tabs", 8, 1, 9, 3, 3, 0, 1, 12, 0, 0 },
24258
+ { "tcl", 3, 1, 12, 5, 5, 4, 1, 12, 0, 0 },
24259
+ { "www", 0, 0, 9, 4, 4, 0, 2, 7, 0, 0 }
2418424260
}; /* | / / | / / | | \
2418524261
** | / / | / / | | \_ 2: columnar
2418624262
** Index into aModeStr[] | / / | | 1: line
2418724263
** | / / | | 0: other
2418824264
** | / / | \
@@ -24304,11 +24380,16 @@
2430424380
if( pI->eNull ) modeSetStr(&pM->spec.zNull, aModeStr[pI->eNull]);
2430524381
pM->spec.eText = pI->eText;
2430624382
pM->spec.eBlob = pI->eBlob;
2430724383
pM->spec.bTitles = pI->bHdr;
2430824384
pM->spec.eTitle = pI->eHdr;
24309
- if( eMode==MODE_Split ){
24385
+ if( pI->mFlg & 0x01 ){
24386
+ pM->spec.bBorder = QRF_No;
24387
+ }else{
24388
+ pM->spec.bBorder = QRF_Auto;
24389
+ }
24390
+ if( pI->mFlg & 0x02 ){
2431024391
pM->spec.bSplitColumn = QRF_Yes;
2431124392
pM->bAutoScreenWidth = 1;
2431224393
}else{
2431324394
pM->spec.bSplitColumn = QRF_No;
2431424395
}
@@ -26622,10 +26703,11 @@
2662226703
" meaning \"left\", \"centered\", and \"right\", with\n"
2662326704
" one letter per column starting from the left.\n"
2662426705
" Unspecified alignment defaults to 'L'.\n"
2662526706
" --blob-quote ARG ARG can be \"auto\", \"text\", \"sql\", \"hex\", \"tcl\",\n"
2662626707
" \"json\", or \"size\". Default is \"auto\".\n"
26708
+" --border on|off Show outer border on \"box\" and \"table\" modes.\n"
2662726709
" --charlimit N Set the maximum number of output characters to\n"
2662826710
" show for any single SQL value to N. Longer values\n"
2662926711
" truncated. Zero means \"no limit\".\n"
2663026712
" --colsep STRING Use STRING as the column separator\n"
2663126713
" --escape ESC Enable/disable escaping of control characters\n"
@@ -30505,10 +30587,11 @@
3050530587
** meaning "left", "centered", and "right", with
3050630588
** one letter per column starting from the left.
3050730589
** Unspecified alignment defaults to 'L'.
3050830590
** --blob-quote ARG ARG can be "auto", "text", "sql", "hex", "tcl",
3050930591
** "json", or "size". Default is "auto".
30592
+** --border on|off Show outer border on "box" and "table" modes.
3051030593
** --charlimit N Set the maximum number of output characters to
3051130594
** show for any single SQL value to N. Longer values
3051230595
** truncated. Zero means "no limit".
3051330596
** --colsep STRING Use STRING as the column separator
3051430597
** --escape ESC Enable/disable escaping of control characters
@@ -30622,10 +30705,20 @@
3062230705
/* 0 1 2 3 4 5 6
3062330706
** Must match QRF_BLOB_xxxx values. See also tag-20251124a */
3062430707
if( k>=0 ){
3062530708
p->mode.spec.eBlob = k & 0xff;
3062630709
}
30710
+ chng = 1;
30711
+ }else if( optionMatch(z,"border") ){
30712
+ if( (++i)>=nArg ){
30713
+ dotCmdError(p, i-1, "missing argument", 0);
30714
+ return 1;
30715
+ }
30716
+ k = pickStr(azArg[i], 0, "auto", "off", "on", "");
30717
+ if( k>=0 ){
30718
+ p->mode.spec.bBorder = k & 0x3;
30719
+ }
3062730720
chng = 1;
3062830721
}else if( 0<=(k=pickStr(z,0,"-charlimit","-linelimit","")) ){
3062930722
int w; /* 0 1 */
3063030723
if( i+1>=nArg ){
3063130724
dotCmdError(p, i, "missing argument", 0);
@@ -30844,16 +30937,16 @@
3084430937
zW = azArg[++i];
3084530938
/* Every width value takes at least 2 bytes in the input string to
3084630939
** specify, so strlen(zW) bytes should be plenty of space to hold the
3084730940
** result. */
3084830941
aWidth = malloc( strlen(zW) );
30849
- while( isspace(zW[0]) ) zW++;
30942
+ while( IsSpace(zW[0]) ) zW++;
3085030943
while( zW[0] ){
3085130944
int w = 0;
3085230945
int nDigit = 0;
30853
- k = zW[0]=='-' && isdigit(zW[1]);
30854
- while( isdigit(zW[k]) ){
30946
+ k = zW[0]=='-' && IsDigit(zW[1]);
30947
+ while( IsDigit(zW[k]) ){
3085530948
w = w*10 + zW[k] - '0';
3085630949
if( w>QRF_MAX_WIDTH ){
3085730950
dotCmdError(p,i+1,"width too big",
3085830951
"Maximum column width is %d", QRF_MAX_WIDTH);
3085930952
free(aWidth);
@@ -30870,11 +30963,11 @@
3087030963
}
3087130964
if( zW[0]=='-' ) w = -w;
3087230965
aWidth[nWidth++] = w;
3087330966
zW += k;
3087430967
if( zW[0]==',' ) zW++;
30875
- while( isspace(zW[0]) ) zW++;
30968
+ while( IsSpace(zW[0]) ) zW++;
3087630969
}
3087730970
free(p->mode.spec.aWidth);
3087830971
p->mode.spec.aWidth = aWidth;
3087930972
p->mode.spec.nWidth = nWidth;
3088030973
chng = 1;
@@ -30925,10 +31018,16 @@
3092531018
unsigned char a = p->mode.spec.aAlign[ii];
3092631019
sqlite3_str_appendchar(pDesc, 1, "LLCR"[a&3]);
3092731020
}
3092831021
sqlite3_str_append(pDesc, "\"", 1);
3092931022
}
31023
+ if( bAll
31024
+ || (p->mode.spec.bBorder==QRF_No) != ((pI->mFlg&1)!=0)
31025
+ ){
31026
+ sqlite3_str_appendf(pDesc," --border %s",
31027
+ p->mode.spec.bBorder==QRF_No ? "off" : "on");
31028
+ }
3093031029
if( bAll || p->mode.spec.eBlob!=QRF_BLOB_Auto ){
3093131030
const char *azBQuote[] =
3093231031
{ "auto", "text", "sql", "hex", "tcl", "json", "size" };
3093331032
/* 0 1 2 3 4 5 6
3093431033
** Must match QRF_BLOB_xxxx values. See all instances of tag-20251124a */
@@ -33669,11 +33768,10 @@
3366933768
3367033769
if( (c=='t' && n>1 && cli_strncmp(azArg[0], "tables", n)==0)
3367133770
|| (c=='i' && (cli_strncmp(azArg[0], "indices", n)==0
3367233771
|| cli_strncmp(azArg[0], "indexes", n)==0) )
3367333772
){
33674
- int ii;
3367533773
sqlite3_stmt *pStmt;
3367633774
sqlite3_str *pSql;
3367733775
const char *zPattern = nArg>1 ? azArg[1] : 0;
3367833776
3367933777
open_db(p, 0);
@@ -33691,11 +33789,11 @@
3369133789
rc = 1;
3369233790
sqlite3_finalize(pStmt);
3369333791
goto meta_command_exit;
3369433792
}
3369533793
pSql = sqlite3_str_new(p->db);
33696
- for(ii=0; sqlite3_step(pStmt)==SQLITE_ROW; ii++){
33794
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
3369733795
const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1);
3369833796
if( zDbName==0 ) continue;
3369933797
if( sqlite3_str_length(pSql) ){
3370033798
sqlite3_str_appendall(pSql, " UNION ALL ");
3370133799
}
@@ -35584,11 +35682,12 @@
3558435682
data.pAuxDb->zDbFilename = ":memory:";
3558535683
warnInmemoryDb = argc==1;
3558635684
#else
3558735685
cli_printf(stderr,
3558835686
"%s: Error: no database filename specified\n", Argv0);
35589
- return 1;
35687
+ rc = 1;
35688
+ goto shell_main_exit;
3559035689
#endif
3559135690
}
3559235691
data.out = stdout;
3559335692
if( bEnableVfstrace ){
3559435693
vfstrace_register("trace",0,vfstraceOut, &data, 1);
@@ -35638,10 +35737,12 @@
3563835737
modeChange(&data, MODE_Json);
3563935738
}else if( cli_strcmp(z,"-markdown")==0 ){
3564035739
modeChange(&data, MODE_Markdown);
3564135740
}else if( cli_strcmp(z,"-table")==0 ){
3564235741
modeChange(&data, MODE_Table);
35742
+ }else if( cli_strcmp(z,"-psql")==0 ){
35743
+ modeChange(&data, MODE_Psql);
3564335744
}else if( cli_strcmp(z,"-box")==0 ){
3564435745
modeChange(&data, MODE_Box);
3564535746
}else if( cli_strcmp(z,"-csv")==0 ){
3564635747
modeChange(&data, MODE_Csv);
3564735748
}else if( cli_strcmp(z,"-escape")==0 && i+1<argc ){
@@ -35724,11 +35825,12 @@
3572435825
}else if( cli_strcmp(z,"-bail")==0 ){
3572535826
/* No-op. The bail_on_error flag should already be set. */
3572635827
}else if( cli_strcmp(z,"-version")==0 ){
3572735828
cli_printf(stdout, "%s %s (%d-bit)\n",
3572835829
sqlite3_libversion(), sqlite3_sourceid(), 8*(int)sizeof(char*));
35729
- return 0;
35830
+ rc = 0;
35831
+ goto shell_main_exit;
3573035832
}else if( cli_strcmp(z,"-interactive")==0 ){
3573135833
/* Need to check for interactive override here to so that it can
3573235834
** affect console setup (for Windows only) and testing thereof.
3573335835
*/
3573435836
stdin_is_interactive = 1;
@@ -35781,29 +35883,33 @@
3578135883
** we retain the goofy behavior for historical compatibility. */
3578235884
if( i==argc-1 ) break;
3578335885
z = cmdline_option_value(argc,argv,++i);
3578435886
if( z[0]=='.' ){
3578535887
rc = do_meta_command(z, &data);
35786
- if( rc && bail_on_error ) return rc==2 ? 0 : rc;
35888
+ if( rc && bail_on_error ){
35889
+ if( rc==2 ) rc = 0;
35890
+ goto shell_main_exit;
35891
+ }
3578735892
}else{
3578835893
open_db(&data, 0);
3578935894
rc = shell_exec(&data, z, &zErrMsg);
3579035895
if( zErrMsg!=0 ){
3579135896
shellEmitError(zErrMsg);
3579235897
sqlite3_free(zErrMsg);
35793
- if( bail_on_error ) return rc!=0 ? rc : 1;
35898
+ if( !rc ) rc = 1;
3579435899
}else if( rc!=0 ){
3579535900
cli_printf(stderr,"Error: unable to process SQL \"%s\"\n", z);
35796
- if( bail_on_error ) return rc;
3579735901
}
35902
+ if( bail_on_error ) goto shell_main_exit;
3579835903
}
3579935904
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
3580035905
}else if( cli_strncmp(z, "-A", 2)==0 ){
3580135906
if( nCmd>0 ){
3580235907
cli_printf(stderr,"Error: cannot mix regular SQL or dot-commands"
3580335908
" with \"%s\"\n", z);
35804
- return 1;
35909
+ rc = 1;
35910
+ goto shell_main_exit;
3580535911
}
3580635912
open_db(&data, OPEN_DB_ZIPFILE);
3580735913
if( z[2] ){
3580835914
argv[i] = &z[2];
3580935915
arDotCommand(&data, 1, argv+(i-1), argc-(i-1));
@@ -35819,11 +35925,12 @@
3581935925
}else if( cli_strcmp(z,"-unsafe-testing")==0 ){
3582035926
/* Acted upon in first pass. */
3582135927
}else{
3582235928
cli_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
3582335929
eputz("Use -help for a list of options.\n");
35824
- return 1;
35930
+ rc = 1;
35931
+ goto shell_main_exit;
3582535932
}
3582635933
}
3582735934
3582835935
if( !readStdin ){
3582935936
/* Run all arguments that do not begin with '-' as if they were separate
3583035937
--- extsrc/shell.c
+++ extsrc/shell.c
@@ -704,10 +704,11 @@
704 unsigned char bWordWrap; /* Try to wrap on word boundaries */
705 unsigned char bTextJsonb; /* Render JSONB blobs as JSON text */
706 unsigned char eDfltAlign; /* Default alignment, no covered by aAlignment */
707 unsigned char eTitleAlign; /* Alignment for column headers */
708 unsigned char bSplitColumn; /* Wrap single-column output into many columns */
 
709 short int nWrap; /* Wrap columns wider than this */
710 short int nScreenWidth; /* Maximum overall table width */
711 short int nLineLimit; /* Maximum number of lines for any row */
712 int nCharLimit; /* Maximum number of characters in a cell */
713 int nWidth; /* Number of entries in aWidth[] */
@@ -1990,19 +1991,19 @@
1990 int k; /* Bytes in a VT100 code */
1991 int n; /* Output column number */
1992 const unsigned char *z = (const unsigned char*)zIn;
1993 unsigned char c = 0;
1994
1995 if( zIn[0]==0 ){
1996 *pnThis = 0;
1997 *pnWide = 0;
1998 *piNext = 0;
1999 return;
2000 }
2001 n = 0;
2002 for(i=0; n<w; i++){
2003 c = zIn[i];
2004 if( c>=0xc0 ){
2005 int u;
2006 int len = sqlite3_qrf_decode_utf8(&z[i], &u);
2007 int wcw = sqlite3_qrf_wcwidth(u);
2008 if( wcw+n>w ) break;
@@ -2009,23 +2010,26 @@
2009 i += len-1;
2010 n += wcw;
2011 continue;
2012 }
2013 if( c>=' ' ){
 
2014 n++;
2015 continue;
2016 }
2017 if( c==0 || c=='\n' ) break;
2018 if( c=='\r' && zIn[i+1]=='\n' ){ c = zIn[++i]; break; }
2019 if( c=='\t' ){
2020 int wcw = 8 - (n&7);
2021 if( n+wcw>w ) break;
2022 n += wcw;
2023 continue;
2024 }
2025 if( c==0x1b && (k = qrfIsVt100(&z[i]))>0 ){
2026 i += k-1;
 
 
2027 }else{
2028 n++;
2029 }
2030 }
2031 if( c==0 ){
@@ -2121,40 +2125,10 @@
2121 }
2122 }
2123 sqlite3_str_append(pOut, (const char*)z, i);
2124 }
2125
2126 /*
2127 ** Output horizontally justified text into pOut. The text is the
2128 ** first nVal bytes of zVal. Include nWS bytes of whitespace, either
2129 ** split between both sides, or on the left, or on the right, depending
2130 ** on eAlign.
2131 */
2132 static void qrfPrintAligned(
2133 sqlite3_str *pOut, /* Append text here */
2134 const char *zVal, /* Text to append */
2135 int nVal, /* Use only the first nVal bytes of zVal[] */
2136 int nWS, /* Whitespace for horizonal alignment */
2137 unsigned char eAlign /* Alignment type */
2138 ){
2139 eAlign &= QRF_ALIGN_HMASK;
2140 if( eAlign==QRF_ALIGN_Center ){
2141 /* Center the text */
2142 sqlite3_str_appendchar(pOut, nWS/2, ' ');
2143 qrfAppendWithTabs(pOut, zVal, nVal);
2144 sqlite3_str_appendchar(pOut, nWS - nWS/2, ' ');
2145 }else if( eAlign==QRF_ALIGN_Right){
2146 /* Right justify the text */
2147 sqlite3_str_appendchar(pOut, nWS, ' ');
2148 qrfAppendWithTabs(pOut, zVal, nVal);
2149 }else{
2150 /* Left justify the next */
2151 qrfAppendWithTabs(pOut, zVal, nVal);
2152 sqlite3_str_appendchar(pOut, nWS, ' ');
2153 }
2154 }
2155
2156 /*
2157 ** GCC does not define the offsetof() macro so we'll have to do it
2158 ** ourselves.
2159 */
2160 #ifndef offsetof
@@ -2174,27 +2148,60 @@
2174 sqlite3_int64 nRow; /* Number of rows */
2175 sqlite3_int64 nAlloc; /* Number of cells allocated */
2176 sqlite3_int64 n; /* Number of cells. nCol*nRow */
2177 char **az; /* Content of all cells */
2178 int *aiWth; /* Width of each cell */
 
2179 struct qrfPerCol { /* Per-column data */
2180 char *z; /* Cache of text for current row */
2181 int w; /* Computed width of this column */
2182 int mxW; /* Maximum natural (unwrapped) width */
2183 unsigned char e; /* Alignment */
2184 unsigned char fx; /* Width is fixed */
 
2185 } *a; /* One per column */
2186 };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2187
2188 /*
2189 ** Free all the memory allocates in the qrfColData object
2190 */
2191 static void qrfColDataFree(qrfColData *p){
2192 sqlite3_int64 i;
2193 for(i=0; i<p->n; i++) sqlite3_free(p->az[i]);
2194 sqlite3_free(p->az);
2195 sqlite3_free(p->aiWth);
 
2196 sqlite3_free(p->a);
2197 memset(p, 0, sizeof(*p));
2198 }
2199
2200 /*
@@ -2202,10 +2209,11 @@
2202 ** Return non-zero if a memory allocation fails.
2203 */
2204 static int qrfColDataEnlarge(qrfColData *p){
2205 char **azData;
2206 int *aiWth;
 
2207 p->nAlloc = 2*p->nAlloc + 10*p->nCol;
2208 azData = sqlite3_realloc64(p->az, p->nAlloc*sizeof(char*));
2209 if( azData==0 ){
2210 qrfOom(p->p);
2211 qrfColDataFree(p);
@@ -2217,26 +2225,38 @@
2217 qrfOom(p->p);
2218 qrfColDataFree(p);
2219 return 1;
2220 }
2221 p->aiWth = aiWth;
 
 
 
 
 
 
 
2222 return 0;
2223 }
2224
2225 /*
2226 ** Print a markdown or table-style row separator using ascii-art
2227 */
2228 static void qrfRowSeparator(sqlite3_str *pOut, qrfColData *p, char cSep){
2229 int i;
2230 if( p->nCol>0 ){
2231 sqlite3_str_append(pOut, &cSep, 1);
 
 
 
2232 sqlite3_str_appendchar(pOut, p->a[0].w+p->nMargin, '-');
2233 for(i=1; i<p->nCol; i++){
2234 sqlite3_str_append(pOut, &cSep, 1);
2235 sqlite3_str_appendchar(pOut, p->a[i].w+p->nMargin, '-');
2236 }
2237 sqlite3_str_append(pOut, &cSep, 1);
 
 
2238 }
2239 sqlite3_str_append(pOut, "\n", 1);
2240 }
2241
2242 /*
@@ -2290,17 +2310,22 @@
2290 const char *zSep2,
2291 const char *zSep3
2292 ){
2293 int i;
2294 if( p->nCol>0 ){
2295 sqlite3_str_appendall(pOut, zSep1);
 
 
 
2296 qrfBoxLine(pOut, p->a[0].w+p->nMargin);
2297 for(i=1; i<p->nCol; i++){
2298 sqlite3_str_appendall(pOut, zSep2);
2299 qrfBoxLine(pOut, p->a[i].w+p->nMargin);
2300 }
2301 sqlite3_str_appendall(pOut, zSep3);
 
 
2302 }
2303 sqlite3_str_append(pOut, "\n", 1);
2304 }
2305
2306 /*
@@ -2379,10 +2404,11 @@
2379 static void qrfSplitColumn(qrfColData *pData, Qrf *p){
2380 int nCol = 1;
2381 int *aw = 0;
2382 char **az = 0;
2383 int *aiWth = 0;
 
2384 int nColNext = 2;
2385 int w;
2386 struct qrfPerCol *a = 0;
2387 sqlite3_int64 nRow = 1;
2388 sqlite3_int64 i;
@@ -2416,35 +2442,47 @@
2416 if( a==0 ){
2417 sqlite3_free(az);
2418 sqlite3_free(aiWth);
2419 qrfOom(p);
2420 return;
 
 
 
 
 
 
 
 
2421 }
2422 for(i=0; i<pData->n; i++){
2423 sqlite3_int64 j = (i%nRow)*nCol + (i/nRow);
2424 az[j] = pData->az[i];
 
2425 pData->az[i] = 0;
2426 aiWth[j] = pData->aiWth[i];
2427 }
2428 while( i<nRow*nCol ){
2429 sqlite3_int64 j = (i%nRow)*nCol + (i/nRow);
2430 az[j] = sqlite3_mprintf("");
2431 if( az[j]==0 ) qrfOom(p);
2432 aiWth[j] = 0;
 
2433 i++;
2434 }
2435 for(i=0; i<nCol; i++){
2436 a[i].fx = a[i].mxW = a[i].w = aw[i];
2437 a[i].e = pData->a[0].e;
2438 }
2439 sqlite3_free(pData->az);
2440 sqlite3_free(pData->aiWth);
2441 sqlite3_free(pData->a);
 
2442 sqlite3_free(aw);
2443 pData->az = az;
2444 pData->aiWth = aiWth;
2445 pData->a = a;
 
2446 pData->nCol = nCol;
2447 pData->n = pData->nAlloc = nRow*nCol;
2448 for(i=w=0; i<nCol; i++) w += a[i].w;
2449 pData->nMargin = (p->spec.nScreenWidth - w)/(nCol - 1);
2450 if( pData->nMargin>5 ) pData->nMargin = 5;
@@ -2464,10 +2502,11 @@
2464 if( p->spec.nScreenWidth==0 ) return;
2465 if( p->spec.eStyle==QRF_STYLE_Column ){
2466 sepW = pData->nCol*2 - 2;
2467 }else{
2468 sepW = pData->nCol*3 + 1;
 
2469 }
2470 nCol = pData->nCol;
2471 for(i=sumW=0; i<nCol; i++) sumW += pData->a[i].w;
2472 if( p->spec.nScreenWidth >= sumW+sepW ) return;
2473
@@ -2475,10 +2514,11 @@
2475 pData->nMargin = 0;
2476 if( p->spec.eStyle==QRF_STYLE_Column ){
2477 sepW = pData->nCol - 1;
2478 }else{
2479 sepW = pData->nCol + 1;
 
2480 }
2481 targetW = p->spec.nScreenWidth - sepW;
2482
2483 #define MIN_SQUOZE 8
2484 #define MIN_EX_SQUOZE 16
@@ -2547,10 +2587,11 @@
2547 }
2548
2549 /* Initialize the data container */
2550 memset(&data, 0, sizeof(data));
2551 data.nCol = p->nCol;
 
2552 data.a = sqlite3_malloc64( nColumn*sizeof(struct qrfPerCol) );
2553 if( data.a==0 ){
2554 qrfOom(p);
2555 return;
2556 }
@@ -2560,10 +2601,11 @@
2560
2561 /* Load the column header names and all cell content into data */
2562 if( p->spec.bTitles==QRF_Yes ){
2563 unsigned char saved_eText = p->spec.eText;
2564 p->spec.eText = p->spec.eTitle;
 
2565 for(i=0; i<nColumn; i++){
2566 const char *z = (const char*)sqlite3_column_name(p->pStmt,i);
2567 int nNL = 0;
2568 int n, w;
2569 pStr = sqlite3_str_new(p->db);
@@ -2584,14 +2626,16 @@
2584 }
2585 for(i=0; i<nColumn; i++){
2586 char *z;
2587 int nNL = 0;
2588 int n, w;
 
2589 pStr = sqlite3_str_new(p->db);
2590 qrfRenderValue(p, pStr, i);
2591 n = sqlite3_str_length(pStr);
2592 z = data.az[data.n] = sqlite3_str_finish(pStr);
 
2593 data.aiWth[data.n] = w = qrfDisplayWidth(z, n, &nNL);
2594 data.n++;
2595 if( w>data.a[i].mxW ) data.a[i].mxW = w;
2596 if( nNL ) data.bMultiRow = 1;
2597 }
@@ -2677,11 +2721,16 @@
2677 }else{
2678 rowStart = BOX_13;
2679 colSep = BOX_13;
2680 rowSep = BOX_13 "\n";
2681 }
2682 qrfBoxSeparator(p->pOut, &data, BOX_23, BOX_234, BOX_34);
 
 
 
 
 
2683 break;
2684 case QRF_STYLE_Table:
2685 if( data.nMargin ){
2686 rowStart = "| ";
2687 colSep = " | ";
@@ -2689,11 +2738,16 @@
2689 }else{
2690 rowStart = "|";
2691 colSep = "|";
2692 rowSep = "|\n";
2693 }
2694 qrfRowSeparator(p->pOut, &data, '+');
 
 
 
 
 
2695 break;
2696 case QRF_STYLE_Column: {
2697 static const char zSpace[] = " ";
2698 rowStart = "";
2699 if( data.nMargin<2 ){
@@ -2721,20 +2775,31 @@
2721 szRowStart = (int)strlen(rowStart);
2722 szRowSep = (int)strlen(rowSep);
2723 szColSep = (int)strlen(colSep);
2724
2725 bWW = (p->spec.bWordWrap==QRF_Yes && data.bMultiRow);
2726 bRTrim = (p->spec.eStyle==QRF_STYLE_Column);
 
 
 
 
 
 
 
 
2727 for(i=0; i<data.n; i+=nColumn){
2728 int bMore;
2729 int nRow = 0;
2730
2731 /* Draw a single row of the table. This might be the title line
2732 ** (if there is a title line) or a row in the body of the table.
2733 ** The column number will be j. The row number is i/nColumn.
2734 */
2735 for(j=0; j<nColumn; j++){ data.a[j].z = data.az[i+j]; }
 
 
 
2736 do{
2737 sqlite3_str_append(p->pOut, rowStart, szRowStart);
2738 bMore = 0;
2739 for(j=0; j<nColumn; j++){
2740 int nThis = 0;
@@ -2741,11 +2806,11 @@
2741 int nWide = 0;
2742 int iNext = 0;
2743 int nWS;
2744 qrfWrapLine(data.a[j].z, data.a[j].w, bWW, &nThis, &nWide, &iNext);
2745 nWS = data.a[j].w - nWide;
2746 qrfPrintAligned(p->pOut, data.a[j].z, nThis, nWS, data.a[j].e);
2747 data.a[j].z += iNext;
2748 if( data.a[j].z[0]!=0 ){
2749 bMore = 1;
2750 }
2751 if( j<nColumn-1 ){
@@ -2763,11 +2828,12 @@
2763 if( data.a[j].z[0]==0 ){
2764 sqlite3_str_appendchar(p->pOut, data.a[j].w, ' ');
2765 }else{
2766 int nE = 3;
2767 if( nE>data.a[j].w ) nE = data.a[j].w;
2768 qrfPrintAligned(p->pOut, "...", nE, data.a[j].w-nE, data.a[j].e);
 
2769 }
2770 if( j<nColumn-1 ){
2771 sqlite3_str_append(p->pOut, colSep, szColSep);
2772 }else{
2773 if( bRTrim ) qrfRTrim(p->pOut);
@@ -2824,17 +2890,19 @@
2824 }
2825 }
2826 }
2827
2828 /* Draw the line across the bottom of the table */
2829 switch( p->spec.eStyle ){
2830 case QRF_STYLE_Box:
2831 qrfBoxSeparator(p->pOut, &data, BOX_12, BOX_124, BOX_14);
2832 break;
2833 case QRF_STYLE_Table:
2834 qrfRowSeparator(p->pOut, &data, '+');
2835 break;
 
 
2836 }
2837 qrfWrite(p);
2838
2839 qrfColDataFree(&data);
2840 return;
@@ -6010,17 +6078,20 @@
6010 }
6011 /* End of the hashing logic
6012 *****************************************************************************/
6013
6014 /*
6015 ** Implementation of the sha1(X) function.
6016 **
6017 ** Return a lower-case hexadecimal rendering of the SHA1 hash of the
6018 ** argument X. If X is a BLOB, it is hashed as is. For all other
6019 ** types of input, X is converted into a UTF-8 string and the string
6020 ** is hash without the trailing 0x00 terminator. The hash of a NULL
6021 ** value is NULL.
 
 
 
6022 */
6023 static void sha1Func(
6024 sqlite3_context *context,
6025 int argc,
6026 sqlite3_value **argv
@@ -6037,15 +6108,17 @@
6037 hash_step(&cx, sqlite3_value_blob(argv[0]), nByte);
6038 }else{
6039 hash_step(&cx, sqlite3_value_text(argv[0]), nByte);
6040 }
6041 if( sqlite3_user_data(context)!=0 ){
 
6042 hash_finish(&cx, zOut, 1);
6043 sqlite3_result_blob(context, zOut, 20, SQLITE_TRANSIENT);
6044 }else{
 
6045 hash_finish(&cx, zOut, 0);
6046 sqlite3_result_blob(context, zOut, 40, SQLITE_TRANSIENT);
6047 }
6048 }
6049
6050 /*
6051 ** Implementation of the sha1_query(SQL) function.
@@ -24117,19 +24190,20 @@
24117 #define MODE_Json 10 /* Output JSON */
24118 #define MODE_Line 11 /* One column per line. Blank line between records */
24119 #define MODE_List 12 /* One record per line with a separator */
24120 #define MODE_Markdown 13 /* Markdown formatting */
24121 #define MODE_Off 14 /* No query output shown */
24122 #define MODE_QBox 15 /* BOX with SQL-quoted content */
24123 #define MODE_Quote 16 /* Quote values as for SQL */
24124 #define MODE_Split 17 /* Split-column mode */
24125 #define MODE_Table 18 /* MySQL-style table formatting */
24126 #define MODE_Tabs 19 /* Tab-separated values */
24127 #define MODE_Tcl 20 /* Space-separated list of TCL strings */
24128 #define MODE_Www 21 /* Full web-page output */
24129
24130 #define MODE_BUILTIN 21 /* Maximum built-in mode */
 
24131 #define MODE_BATCH 50 /* Default mode for batch processing */
24132 #define MODE_TTY 51 /* Default mode for interactive processing */
24133 #define MODE_USER 75 /* First user-defined mode */
24134 #define MODE_N_USER 25 /* Maximum number of user-defined modes */
24135
@@ -24146,10 +24220,11 @@
24146 unsigned char eHdr; /* Default header encoding. */
24147 unsigned char eBlob; /* Default blob encoding. */
24148 unsigned char bHdr; /* Show headers by default. 0: n/a, 1: no 2: yes */
24149 unsigned char eStyle; /* Underlying QRF style */
24150 unsigned char eCx; /* 0: other, 1: line, 2: columnar */
 
24151 };
24152
24153 /* String constants used by built-in modes */
24154 static const char *aModeStr[] =
24155 /* 0 1 2 3 4 5 6 7 8 */
@@ -24156,33 +24231,34 @@
24156 { 0, "\n", "|", " ", ",", "\r\n", "\036", "\037", "\t",
24157 "", "NULL", "null", "\"\"" };
24158 /* 9 10 11 12 */
24159
24160 static const ModeInfo aModeInfo[] = {
24161 /* zName eCSep eRSep eNull eText eHdr eBlob bHdr eStyle eCx */
24162 { "ascii", 7, 6, 9, 1, 1, 0, 1, 12, 0 },
24163 { "box", 0, 0, 9, 1, 1, 0, 2, 1, 2 },
24164 { "c", 4, 1, 10, 5, 5, 4, 1, 12, 0 },
24165 { "column", 0, 0, 9, 1, 1, 0, 2, 2, 2 },
24166 { "count", 0, 0, 0, 0, 0, 0, 0, 3, 0 },
24167 { "csv", 4, 5, 9, 3, 3, 0, 1, 12, 0 },
24168 { "html", 0, 0, 9, 4, 4, 0, 2, 7, 0 },
24169 { "insert", 0, 0, 10, 2, 2, 0, 1, 8, 0 },
24170 { "jatom", 4, 1, 11, 6, 6, 0, 1, 12, 0 },
24171 { "jobject", 0, 1, 11, 6, 6, 0, 0, 10, 0 },
24172 { "json", 0, 0, 11, 6, 6, 0, 0, 9, 0 },
24173 { "line", 0, 1, 9, 1, 1, 0, 0, 11, 1 },
24174 { "list", 2, 1, 9, 1, 1, 0, 1, 12, 0 },
24175 { "markdown", 0, 0, 9, 1, 1, 0, 2, 13, 2 },
24176 { "off", 0, 0, 0, 0, 0, 0, 0, 14, 0 },
24177 { "qbox", 0, 0, 10, 2, 1, 0, 2, 1, 2 },
24178 { "quote", 4, 1, 10, 2, 2, 0, 1, 12, 0 },
24179 { "split", 0, 0, 9, 1, 1, 0, 1, 2, 2 },
24180 { "table", 0, 0, 9, 1, 1, 0, 2, 19, 2 },
24181 { "tabs", 8, 1, 9, 3, 3, 0, 1, 12, 0 },
24182 { "tcl", 3, 1, 12, 5, 5, 4, 1, 12, 0 },
24183 { "www", 0, 0, 9, 4, 4, 0, 2, 7, 0 }
 
24184 }; /* | / / | / / | | \
24185 ** | / / | / / | | \_ 2: columnar
24186 ** Index into aModeStr[] | / / | | 1: line
24187 ** | / / | | 0: other
24188 ** | / / | \
@@ -24304,11 +24380,16 @@
24304 if( pI->eNull ) modeSetStr(&pM->spec.zNull, aModeStr[pI->eNull]);
24305 pM->spec.eText = pI->eText;
24306 pM->spec.eBlob = pI->eBlob;
24307 pM->spec.bTitles = pI->bHdr;
24308 pM->spec.eTitle = pI->eHdr;
24309 if( eMode==MODE_Split ){
 
 
 
 
 
24310 pM->spec.bSplitColumn = QRF_Yes;
24311 pM->bAutoScreenWidth = 1;
24312 }else{
24313 pM->spec.bSplitColumn = QRF_No;
24314 }
@@ -26622,10 +26703,11 @@
26622 " meaning \"left\", \"centered\", and \"right\", with\n"
26623 " one letter per column starting from the left.\n"
26624 " Unspecified alignment defaults to 'L'.\n"
26625 " --blob-quote ARG ARG can be \"auto\", \"text\", \"sql\", \"hex\", \"tcl\",\n"
26626 " \"json\", or \"size\". Default is \"auto\".\n"
 
26627 " --charlimit N Set the maximum number of output characters to\n"
26628 " show for any single SQL value to N. Longer values\n"
26629 " truncated. Zero means \"no limit\".\n"
26630 " --colsep STRING Use STRING as the column separator\n"
26631 " --escape ESC Enable/disable escaping of control characters\n"
@@ -30505,10 +30587,11 @@
30505 ** meaning "left", "centered", and "right", with
30506 ** one letter per column starting from the left.
30507 ** Unspecified alignment defaults to 'L'.
30508 ** --blob-quote ARG ARG can be "auto", "text", "sql", "hex", "tcl",
30509 ** "json", or "size". Default is "auto".
 
30510 ** --charlimit N Set the maximum number of output characters to
30511 ** show for any single SQL value to N. Longer values
30512 ** truncated. Zero means "no limit".
30513 ** --colsep STRING Use STRING as the column separator
30514 ** --escape ESC Enable/disable escaping of control characters
@@ -30622,10 +30705,20 @@
30622 /* 0 1 2 3 4 5 6
30623 ** Must match QRF_BLOB_xxxx values. See also tag-20251124a */
30624 if( k>=0 ){
30625 p->mode.spec.eBlob = k & 0xff;
30626 }
 
 
 
 
 
 
 
 
 
 
30627 chng = 1;
30628 }else if( 0<=(k=pickStr(z,0,"-charlimit","-linelimit","")) ){
30629 int w; /* 0 1 */
30630 if( i+1>=nArg ){
30631 dotCmdError(p, i, "missing argument", 0);
@@ -30844,16 +30937,16 @@
30844 zW = azArg[++i];
30845 /* Every width value takes at least 2 bytes in the input string to
30846 ** specify, so strlen(zW) bytes should be plenty of space to hold the
30847 ** result. */
30848 aWidth = malloc( strlen(zW) );
30849 while( isspace(zW[0]) ) zW++;
30850 while( zW[0] ){
30851 int w = 0;
30852 int nDigit = 0;
30853 k = zW[0]=='-' && isdigit(zW[1]);
30854 while( isdigit(zW[k]) ){
30855 w = w*10 + zW[k] - '0';
30856 if( w>QRF_MAX_WIDTH ){
30857 dotCmdError(p,i+1,"width too big",
30858 "Maximum column width is %d", QRF_MAX_WIDTH);
30859 free(aWidth);
@@ -30870,11 +30963,11 @@
30870 }
30871 if( zW[0]=='-' ) w = -w;
30872 aWidth[nWidth++] = w;
30873 zW += k;
30874 if( zW[0]==',' ) zW++;
30875 while( isspace(zW[0]) ) zW++;
30876 }
30877 free(p->mode.spec.aWidth);
30878 p->mode.spec.aWidth = aWidth;
30879 p->mode.spec.nWidth = nWidth;
30880 chng = 1;
@@ -30925,10 +31018,16 @@
30925 unsigned char a = p->mode.spec.aAlign[ii];
30926 sqlite3_str_appendchar(pDesc, 1, "LLCR"[a&3]);
30927 }
30928 sqlite3_str_append(pDesc, "\"", 1);
30929 }
 
 
 
 
 
 
30930 if( bAll || p->mode.spec.eBlob!=QRF_BLOB_Auto ){
30931 const char *azBQuote[] =
30932 { "auto", "text", "sql", "hex", "tcl", "json", "size" };
30933 /* 0 1 2 3 4 5 6
30934 ** Must match QRF_BLOB_xxxx values. See all instances of tag-20251124a */
@@ -33669,11 +33768,10 @@
33669
33670 if( (c=='t' && n>1 && cli_strncmp(azArg[0], "tables", n)==0)
33671 || (c=='i' && (cli_strncmp(azArg[0], "indices", n)==0
33672 || cli_strncmp(azArg[0], "indexes", n)==0) )
33673 ){
33674 int ii;
33675 sqlite3_stmt *pStmt;
33676 sqlite3_str *pSql;
33677 const char *zPattern = nArg>1 ? azArg[1] : 0;
33678
33679 open_db(p, 0);
@@ -33691,11 +33789,11 @@
33691 rc = 1;
33692 sqlite3_finalize(pStmt);
33693 goto meta_command_exit;
33694 }
33695 pSql = sqlite3_str_new(p->db);
33696 for(ii=0; sqlite3_step(pStmt)==SQLITE_ROW; ii++){
33697 const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1);
33698 if( zDbName==0 ) continue;
33699 if( sqlite3_str_length(pSql) ){
33700 sqlite3_str_appendall(pSql, " UNION ALL ");
33701 }
@@ -35584,11 +35682,12 @@
35584 data.pAuxDb->zDbFilename = ":memory:";
35585 warnInmemoryDb = argc==1;
35586 #else
35587 cli_printf(stderr,
35588 "%s: Error: no database filename specified\n", Argv0);
35589 return 1;
 
35590 #endif
35591 }
35592 data.out = stdout;
35593 if( bEnableVfstrace ){
35594 vfstrace_register("trace",0,vfstraceOut, &data, 1);
@@ -35638,10 +35737,12 @@
35638 modeChange(&data, MODE_Json);
35639 }else if( cli_strcmp(z,"-markdown")==0 ){
35640 modeChange(&data, MODE_Markdown);
35641 }else if( cli_strcmp(z,"-table")==0 ){
35642 modeChange(&data, MODE_Table);
 
 
35643 }else if( cli_strcmp(z,"-box")==0 ){
35644 modeChange(&data, MODE_Box);
35645 }else if( cli_strcmp(z,"-csv")==0 ){
35646 modeChange(&data, MODE_Csv);
35647 }else if( cli_strcmp(z,"-escape")==0 && i+1<argc ){
@@ -35724,11 +35825,12 @@
35724 }else if( cli_strcmp(z,"-bail")==0 ){
35725 /* No-op. The bail_on_error flag should already be set. */
35726 }else if( cli_strcmp(z,"-version")==0 ){
35727 cli_printf(stdout, "%s %s (%d-bit)\n",
35728 sqlite3_libversion(), sqlite3_sourceid(), 8*(int)sizeof(char*));
35729 return 0;
 
35730 }else if( cli_strcmp(z,"-interactive")==0 ){
35731 /* Need to check for interactive override here to so that it can
35732 ** affect console setup (for Windows only) and testing thereof.
35733 */
35734 stdin_is_interactive = 1;
@@ -35781,29 +35883,33 @@
35781 ** we retain the goofy behavior for historical compatibility. */
35782 if( i==argc-1 ) break;
35783 z = cmdline_option_value(argc,argv,++i);
35784 if( z[0]=='.' ){
35785 rc = do_meta_command(z, &data);
35786 if( rc && bail_on_error ) return rc==2 ? 0 : rc;
 
 
 
35787 }else{
35788 open_db(&data, 0);
35789 rc = shell_exec(&data, z, &zErrMsg);
35790 if( zErrMsg!=0 ){
35791 shellEmitError(zErrMsg);
35792 sqlite3_free(zErrMsg);
35793 if( bail_on_error ) return rc!=0 ? rc : 1;
35794 }else if( rc!=0 ){
35795 cli_printf(stderr,"Error: unable to process SQL \"%s\"\n", z);
35796 if( bail_on_error ) return rc;
35797 }
 
35798 }
35799 #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
35800 }else if( cli_strncmp(z, "-A", 2)==0 ){
35801 if( nCmd>0 ){
35802 cli_printf(stderr,"Error: cannot mix regular SQL or dot-commands"
35803 " with \"%s\"\n", z);
35804 return 1;
 
35805 }
35806 open_db(&data, OPEN_DB_ZIPFILE);
35807 if( z[2] ){
35808 argv[i] = &z[2];
35809 arDotCommand(&data, 1, argv+(i-1), argc-(i-1));
@@ -35819,11 +35925,12 @@
35819 }else if( cli_strcmp(z,"-unsafe-testing")==0 ){
35820 /* Acted upon in first pass. */
35821 }else{
35822 cli_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
35823 eputz("Use -help for a list of options.\n");
35824 return 1;
 
35825 }
35826 }
35827
35828 if( !readStdin ){
35829 /* Run all arguments that do not begin with '-' as if they were separate
35830
--- extsrc/shell.c
+++ extsrc/shell.c
@@ -704,10 +704,11 @@
704 unsigned char bWordWrap; /* Try to wrap on word boundaries */
705 unsigned char bTextJsonb; /* Render JSONB blobs as JSON text */
706 unsigned char eDfltAlign; /* Default alignment, no covered by aAlignment */
707 unsigned char eTitleAlign; /* Alignment for column headers */
708 unsigned char bSplitColumn; /* Wrap single-column output into many columns */
709 unsigned char bBorder; /* Show outer border in Box and Table styles */
710 short int nWrap; /* Wrap columns wider than this */
711 short int nScreenWidth; /* Maximum overall table width */
712 short int nLineLimit; /* Maximum number of lines for any row */
713 int nCharLimit; /* Maximum number of characters in a cell */
714 int nWidth; /* Number of entries in aWidth[] */
@@ -1990,19 +1991,19 @@
1991 int k; /* Bytes in a VT100 code */
1992 int n; /* Output column number */
1993 const unsigned char *z = (const unsigned char*)zIn;
1994 unsigned char c = 0;
1995
1996 if( z[0]==0 ){
1997 *pnThis = 0;
1998 *pnWide = 0;
1999 *piNext = 0;
2000 return;
2001 }
2002 n = 0;
2003 for(i=0; n<=w; i++){
2004 c = z[i];
2005 if( c>=0xc0 ){
2006 int u;
2007 int len = sqlite3_qrf_decode_utf8(&z[i], &u);
2008 int wcw = sqlite3_qrf_wcwidth(u);
2009 if( wcw+n>w ) break;
@@ -2009,23 +2010,26 @@
2010 i += len-1;
2011 n += wcw;
2012 continue;
2013 }
2014 if( c>=' ' ){
2015 if( n==w ) break;
2016 n++;
2017 continue;
2018 }
2019 if( c==0 || c=='\n' ) break;
2020 if( c=='\r' && z[i+1]=='\n' ){ c = z[++i]; break; }
2021 if( c=='\t' ){
2022 int wcw = 8 - (n&7);
2023 if( n+wcw>w ) break;
2024 n += wcw;
2025 continue;
2026 }
2027 if( c==0x1b && (k = qrfIsVt100(&z[i]))>0 ){
2028 i += k-1;
2029 }else if( n==w ){
2030 break;
2031 }else{
2032 n++;
2033 }
2034 }
2035 if( c==0 ){
@@ -2121,40 +2125,10 @@
2125 }
2126 }
2127 sqlite3_str_append(pOut, (const char*)z, i);
2128 }
2129
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2130 /*
2131 ** GCC does not define the offsetof() macro so we'll have to do it
2132 ** ourselves.
2133 */
2134 #ifndef offsetof
@@ -2174,27 +2148,60 @@
2148 sqlite3_int64 nRow; /* Number of rows */
2149 sqlite3_int64 nAlloc; /* Number of cells allocated */
2150 sqlite3_int64 n; /* Number of cells. nCol*nRow */
2151 char **az; /* Content of all cells */
2152 int *aiWth; /* Width of each cell */
2153 unsigned char *abNum; /* True for each numeric cell */
2154 struct qrfPerCol { /* Per-column data */
2155 char *z; /* Cache of text for current row */
2156 int w; /* Computed width of this column */
2157 int mxW; /* Maximum natural (unwrapped) width */
2158 unsigned char e; /* Alignment */
2159 unsigned char fx; /* Width is fixed */
2160 unsigned char bNum; /* True if is numeric */
2161 } *a; /* One per column */
2162 };
2163
2164 /*
2165 ** Output horizontally justified text into pOut. The text is the
2166 ** first nVal bytes of zVal. Include nWS bytes of whitespace, either
2167 ** split between both sides, or on the left, or on the right, depending
2168 ** on eAlign.
2169 */
2170 static void qrfPrintAligned(
2171 sqlite3_str *pOut, /* Append text here */
2172 struct qrfPerCol *pCol, /* Information about the text to print */
2173 int nVal, /* Use only the first nVal bytes of zVal[] */
2174 int nWS /* Whitespace for horizonal alignment */
2175 ){
2176 unsigned char eAlign = pCol->e & QRF_ALIGN_HMASK;
2177 if( eAlign==QRF_Auto && pCol->bNum ) eAlign = QRF_ALIGN_Right;
2178 if( eAlign==QRF_ALIGN_Center ){
2179 /* Center the text */
2180 sqlite3_str_appendchar(pOut, nWS/2, ' ');
2181 qrfAppendWithTabs(pOut, pCol->z, nVal);
2182 sqlite3_str_appendchar(pOut, nWS - nWS/2, ' ');
2183 }else if( eAlign==QRF_ALIGN_Right ){
2184 /* Right justify the text */
2185 sqlite3_str_appendchar(pOut, nWS, ' ');
2186 qrfAppendWithTabs(pOut, pCol->z, nVal);
2187 }else{
2188 /* Left justify the text */
2189 qrfAppendWithTabs(pOut, pCol->z, nVal);
2190 sqlite3_str_appendchar(pOut, nWS, ' ');
2191 }
2192 }
2193
2194 /*
2195 ** Free all the memory allocates in the qrfColData object
2196 */
2197 static void qrfColDataFree(qrfColData *p){
2198 sqlite3_int64 i;
2199 for(i=0; i<p->n; i++) sqlite3_free(p->az[i]);
2200 sqlite3_free(p->az);
2201 sqlite3_free(p->aiWth);
2202 sqlite3_free(p->abNum);
2203 sqlite3_free(p->a);
2204 memset(p, 0, sizeof(*p));
2205 }
2206
2207 /*
@@ -2202,10 +2209,11 @@
2209 ** Return non-zero if a memory allocation fails.
2210 */
2211 static int qrfColDataEnlarge(qrfColData *p){
2212 char **azData;
2213 int *aiWth;
2214 unsigned char *abNum;
2215 p->nAlloc = 2*p->nAlloc + 10*p->nCol;
2216 azData = sqlite3_realloc64(p->az, p->nAlloc*sizeof(char*));
2217 if( azData==0 ){
2218 qrfOom(p->p);
2219 qrfColDataFree(p);
@@ -2217,26 +2225,38 @@
2225 qrfOom(p->p);
2226 qrfColDataFree(p);
2227 return 1;
2228 }
2229 p->aiWth = aiWth;
2230 abNum = sqlite3_realloc64(p->abNum, p->nAlloc);
2231 if( abNum==0 ){
2232 qrfOom(p->p);
2233 qrfColDataFree(p);
2234 return 1;
2235 }
2236 p->abNum = abNum;
2237 return 0;
2238 }
2239
2240 /*
2241 ** Print a markdown or table-style row separator using ascii-art
2242 */
2243 static void qrfRowSeparator(sqlite3_str *pOut, qrfColData *p, char cSep){
2244 int i;
2245 if( p->nCol>0 ){
2246 int useBorder = p->p->spec.bBorder!=QRF_No;
2247 if( useBorder ){
2248 sqlite3_str_append(pOut, &cSep, 1);
2249 }
2250 sqlite3_str_appendchar(pOut, p->a[0].w+p->nMargin, '-');
2251 for(i=1; i<p->nCol; i++){
2252 sqlite3_str_append(pOut, &cSep, 1);
2253 sqlite3_str_appendchar(pOut, p->a[i].w+p->nMargin, '-');
2254 }
2255 if( useBorder ){
2256 sqlite3_str_append(pOut, &cSep, 1);
2257 }
2258 }
2259 sqlite3_str_append(pOut, "\n", 1);
2260 }
2261
2262 /*
@@ -2290,17 +2310,22 @@
2310 const char *zSep2,
2311 const char *zSep3
2312 ){
2313 int i;
2314 if( p->nCol>0 ){
2315 int useBorder = p->p->spec.bBorder!=QRF_No;
2316 if( useBorder ){
2317 sqlite3_str_appendall(pOut, zSep1);
2318 }
2319 qrfBoxLine(pOut, p->a[0].w+p->nMargin);
2320 for(i=1; i<p->nCol; i++){
2321 sqlite3_str_appendall(pOut, zSep2);
2322 qrfBoxLine(pOut, p->a[i].w+p->nMargin);
2323 }
2324 if( useBorder ){
2325 sqlite3_str_appendall(pOut, zSep3);
2326 }
2327 }
2328 sqlite3_str_append(pOut, "\n", 1);
2329 }
2330
2331 /*
@@ -2379,10 +2404,11 @@
2404 static void qrfSplitColumn(qrfColData *pData, Qrf *p){
2405 int nCol = 1;
2406 int *aw = 0;
2407 char **az = 0;
2408 int *aiWth = 0;
2409 unsigned char *abNum = 0;
2410 int nColNext = 2;
2411 int w;
2412 struct qrfPerCol *a = 0;
2413 sqlite3_int64 nRow = 1;
2414 sqlite3_int64 i;
@@ -2416,35 +2442,47 @@
2442 if( a==0 ){
2443 sqlite3_free(az);
2444 sqlite3_free(aiWth);
2445 qrfOom(p);
2446 return;
2447 }
2448 abNum = sqlite3_malloc64( nRow*nCol );
2449 if( abNum==0 ){
2450 sqlite3_free(az);
2451 sqlite3_free(aiWth);
2452 sqlite3_free(a);
2453 qrfOom(p);
2454 return;
2455 }
2456 for(i=0; i<pData->n; i++){
2457 sqlite3_int64 j = (i%nRow)*nCol + (i/nRow);
2458 az[j] = pData->az[i];
2459 abNum[j]= pData->abNum[i];
2460 pData->az[i] = 0;
2461 aiWth[j] = pData->aiWth[i];
2462 }
2463 while( i<nRow*nCol ){
2464 sqlite3_int64 j = (i%nRow)*nCol + (i/nRow);
2465 az[j] = sqlite3_mprintf("");
2466 if( az[j]==0 ) qrfOom(p);
2467 aiWth[j] = 0;
2468 abNum[j] = 0;
2469 i++;
2470 }
2471 for(i=0; i<nCol; i++){
2472 a[i].fx = a[i].mxW = a[i].w = aw[i];
2473 a[i].e = pData->a[0].e;
2474 }
2475 sqlite3_free(pData->az);
2476 sqlite3_free(pData->aiWth);
2477 sqlite3_free(pData->a);
2478 sqlite3_free(pData->abNum);
2479 sqlite3_free(aw);
2480 pData->az = az;
2481 pData->aiWth = aiWth;
2482 pData->a = a;
2483 pData->abNum = abNum;
2484 pData->nCol = nCol;
2485 pData->n = pData->nAlloc = nRow*nCol;
2486 for(i=w=0; i<nCol; i++) w += a[i].w;
2487 pData->nMargin = (p->spec.nScreenWidth - w)/(nCol - 1);
2488 if( pData->nMargin>5 ) pData->nMargin = 5;
@@ -2464,10 +2502,11 @@
2502 if( p->spec.nScreenWidth==0 ) return;
2503 if( p->spec.eStyle==QRF_STYLE_Column ){
2504 sepW = pData->nCol*2 - 2;
2505 }else{
2506 sepW = pData->nCol*3 + 1;
2507 if( p->spec.bBorder==QRF_No ) sepW -= 2;
2508 }
2509 nCol = pData->nCol;
2510 for(i=sumW=0; i<nCol; i++) sumW += pData->a[i].w;
2511 if( p->spec.nScreenWidth >= sumW+sepW ) return;
2512
@@ -2475,10 +2514,11 @@
2514 pData->nMargin = 0;
2515 if( p->spec.eStyle==QRF_STYLE_Column ){
2516 sepW = pData->nCol - 1;
2517 }else{
2518 sepW = pData->nCol + 1;
2519 if( p->spec.bBorder==QRF_No ) sepW -= 2;
2520 }
2521 targetW = p->spec.nScreenWidth - sepW;
2522
2523 #define MIN_SQUOZE 8
2524 #define MIN_EX_SQUOZE 16
@@ -2547,10 +2587,11 @@
2587 }
2588
2589 /* Initialize the data container */
2590 memset(&data, 0, sizeof(data));
2591 data.nCol = p->nCol;
2592 data.p = p;
2593 data.a = sqlite3_malloc64( nColumn*sizeof(struct qrfPerCol) );
2594 if( data.a==0 ){
2595 qrfOom(p);
2596 return;
2597 }
@@ -2560,10 +2601,11 @@
2601
2602 /* Load the column header names and all cell content into data */
2603 if( p->spec.bTitles==QRF_Yes ){
2604 unsigned char saved_eText = p->spec.eText;
2605 p->spec.eText = p->spec.eTitle;
2606 memset(data.abNum, 0, nColumn);
2607 for(i=0; i<nColumn; i++){
2608 const char *z = (const char*)sqlite3_column_name(p->pStmt,i);
2609 int nNL = 0;
2610 int n, w;
2611 pStr = sqlite3_str_new(p->db);
@@ -2584,14 +2626,16 @@
2626 }
2627 for(i=0; i<nColumn; i++){
2628 char *z;
2629 int nNL = 0;
2630 int n, w;
2631 int eType = sqlite3_column_type(p->pStmt,i);
2632 pStr = sqlite3_str_new(p->db);
2633 qrfRenderValue(p, pStr, i);
2634 n = sqlite3_str_length(pStr);
2635 z = data.az[data.n] = sqlite3_str_finish(pStr);
2636 data.abNum[data.n] = eType==SQLITE_INTEGER || eType==SQLITE_FLOAT;
2637 data.aiWth[data.n] = w = qrfDisplayWidth(z, n, &nNL);
2638 data.n++;
2639 if( w>data.a[i].mxW ) data.a[i].mxW = w;
2640 if( nNL ) data.bMultiRow = 1;
2641 }
@@ -2677,11 +2721,16 @@
2721 }else{
2722 rowStart = BOX_13;
2723 colSep = BOX_13;
2724 rowSep = BOX_13 "\n";
2725 }
2726 if( p->spec.bBorder==QRF_No){
2727 rowStart += 3;
2728 rowSep = "\n";
2729 }else{
2730 qrfBoxSeparator(p->pOut, &data, BOX_23, BOX_234, BOX_34);
2731 }
2732 break;
2733 case QRF_STYLE_Table:
2734 if( data.nMargin ){
2735 rowStart = "| ";
2736 colSep = " | ";
@@ -2689,11 +2738,16 @@
2738 }else{
2739 rowStart = "|";
2740 colSep = "|";
2741 rowSep = "|\n";
2742 }
2743 if( p->spec.bBorder==QRF_No ){
2744 rowStart += 1;
2745 rowSep = "\n";
2746 }else{
2747 qrfRowSeparator(p->pOut, &data, '+');
2748 }
2749 break;
2750 case QRF_STYLE_Column: {
2751 static const char zSpace[] = " ";
2752 rowStart = "";
2753 if( data.nMargin<2 ){
@@ -2721,20 +2775,31 @@
2775 szRowStart = (int)strlen(rowStart);
2776 szRowSep = (int)strlen(rowSep);
2777 szColSep = (int)strlen(colSep);
2778
2779 bWW = (p->spec.bWordWrap==QRF_Yes && data.bMultiRow);
2780 if( p->spec.eStyle==QRF_STYLE_Column
2781 || (p->spec.bBorder==QRF_No
2782 && (p->spec.eStyle==QRF_STYLE_Box || p->spec.eStyle==QRF_STYLE_Table)
2783 )
2784 ){
2785 bRTrim = 1;
2786 }else{
2787 bRTrim = 0;
2788 }
2789 for(i=0; i<data.n; i+=nColumn){
2790 int bMore;
2791 int nRow = 0;
2792
2793 /* Draw a single row of the table. This might be the title line
2794 ** (if there is a title line) or a row in the body of the table.
2795 ** The column number will be j. The row number is i/nColumn.
2796 */
2797 for(j=0; j<nColumn; j++){
2798 data.a[j].z = data.az[i+j];
2799 data.a[j].bNum = data.abNum[i+j];
2800 }
2801 do{
2802 sqlite3_str_append(p->pOut, rowStart, szRowStart);
2803 bMore = 0;
2804 for(j=0; j<nColumn; j++){
2805 int nThis = 0;
@@ -2741,11 +2806,11 @@
2806 int nWide = 0;
2807 int iNext = 0;
2808 int nWS;
2809 qrfWrapLine(data.a[j].z, data.a[j].w, bWW, &nThis, &nWide, &iNext);
2810 nWS = data.a[j].w - nWide;
2811 qrfPrintAligned(p->pOut, &data.a[j], nThis, nWS);
2812 data.a[j].z += iNext;
2813 if( data.a[j].z[0]!=0 ){
2814 bMore = 1;
2815 }
2816 if( j<nColumn-1 ){
@@ -2763,11 +2828,12 @@
2828 if( data.a[j].z[0]==0 ){
2829 sqlite3_str_appendchar(p->pOut, data.a[j].w, ' ');
2830 }else{
2831 int nE = 3;
2832 if( nE>data.a[j].w ) nE = data.a[j].w;
2833 data.a[j].z = "...";
2834 qrfPrintAligned(p->pOut, &data.a[j], nE, data.a[j].w-nE);
2835 }
2836 if( j<nColumn-1 ){
2837 sqlite3_str_append(p->pOut, colSep, szColSep);
2838 }else{
2839 if( bRTrim ) qrfRTrim(p->pOut);
@@ -2824,17 +2890,19 @@
2890 }
2891 }
2892 }
2893
2894 /* Draw the line across the bottom of the table */
2895 if( p->spec.bBorder!=QRF_No ){
2896 switch( p->spec.eStyle ){
2897 case QRF_STYLE_Box:
2898 qrfBoxSeparator(p->pOut, &data, BOX_12, BOX_124, BOX_14);
2899 break;
2900 case QRF_STYLE_Table:
2901 qrfRowSeparator(p->pOut, &data, '+');
2902 break;
2903 }
2904 }
2905 qrfWrite(p);
2906
2907 qrfColDataFree(&data);
2908 return;
@@ -6010,17 +6078,20 @@
6078 }
6079 /* End of the hashing logic
6080 *****************************************************************************/
6081
6082 /*
6083 ** Two SQL functions: sha1(X) and sha1b(X).
6084 **
6085 ** sha1(X) returns a lower-case hexadecimal rendering of the SHA1 hash
6086 ** of the argument X. If X is a BLOB, it is hashed as is. For all other
6087 ** types of input, X is converted into a UTF-8 string and the string
6088 ** is hashed without the trailing 0x00 terminator. The hash of a NULL
6089 ** value is NULL.
6090 **
6091 ** sha1b(X) is the same except that it returns a 20-byte BLOB containing
6092 ** the binary hash instead of a hexadecimal string.
6093 */
6094 static void sha1Func(
6095 sqlite3_context *context,
6096 int argc,
6097 sqlite3_value **argv
@@ -6037,15 +6108,17 @@
6108 hash_step(&cx, sqlite3_value_blob(argv[0]), nByte);
6109 }else{
6110 hash_step(&cx, sqlite3_value_text(argv[0]), nByte);
6111 }
6112 if( sqlite3_user_data(context)!=0 ){
6113 /* sha1b() - binary result */
6114 hash_finish(&cx, zOut, 1);
6115 sqlite3_result_blob(context, zOut, 20, SQLITE_TRANSIENT);
6116 }else{
6117 /* sha1() - hexadecimal text result */
6118 hash_finish(&cx, zOut, 0);
6119 sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT);
6120 }
6121 }
6122
6123 /*
6124 ** Implementation of the sha1_query(SQL) function.
@@ -24117,19 +24190,20 @@
24190 #define MODE_Json 10 /* Output JSON */
24191 #define MODE_Line 11 /* One column per line. Blank line between records */
24192 #define MODE_List 12 /* One record per line with a separator */
24193 #define MODE_Markdown 13 /* Markdown formatting */
24194 #define MODE_Off 14 /* No query output shown */
24195 #define MODE_Psql 15 /* Similar to psql */
24196 #define MODE_QBox 16 /* BOX with SQL-quoted content */
24197 #define MODE_Quote 17 /* Quote values as for SQL */
24198 #define MODE_Split 18 /* Split-column mode */
24199 #define MODE_Table 19 /* MySQL-style table formatting */
24200 #define MODE_Tabs 20 /* Tab-separated values */
24201 #define MODE_Tcl 21 /* Space-separated list of TCL strings */
24202 #define MODE_Www 22 /* Full web-page output */
24203
24204 #define MODE_BUILTIN 22 /* Maximum built-in mode */
24205 #define MODE_BATCH 50 /* Default mode for batch processing */
24206 #define MODE_TTY 51 /* Default mode for interactive processing */
24207 #define MODE_USER 75 /* First user-defined mode */
24208 #define MODE_N_USER 25 /* Maximum number of user-defined modes */
24209
@@ -24146,10 +24220,11 @@
24220 unsigned char eHdr; /* Default header encoding. */
24221 unsigned char eBlob; /* Default blob encoding. */
24222 unsigned char bHdr; /* Show headers by default. 0: n/a, 1: no 2: yes */
24223 unsigned char eStyle; /* Underlying QRF style */
24224 unsigned char eCx; /* 0: other, 1: line, 2: columnar */
24225 unsigned char mFlg; /* Flags. 1=border-off 2=split-column */
24226 };
24227
24228 /* String constants used by built-in modes */
24229 static const char *aModeStr[] =
24230 /* 0 1 2 3 4 5 6 7 8 */
@@ -24156,33 +24231,34 @@
24231 { 0, "\n", "|", " ", ",", "\r\n", "\036", "\037", "\t",
24232 "", "NULL", "null", "\"\"" };
24233 /* 9 10 11 12 */
24234
24235 static const ModeInfo aModeInfo[] = {
24236 /* zName eCSep eRSep eNull eText eHdr eBlob bHdr eStyle eCx mFlg */
24237 { "ascii", 7, 6, 9, 1, 1, 0, 1, 12, 0, 0 },
24238 { "box", 0, 0, 9, 1, 1, 0, 2, 1, 2, 0 },
24239 { "c", 4, 1, 10, 5, 5, 4, 1, 12, 0, 0 },
24240 { "column", 0, 0, 9, 1, 1, 0, 2, 2, 2, 0 },
24241 { "count", 0, 0, 0, 0, 0, 0, 0, 3, 0, 0 },
24242 { "csv", 4, 5, 9, 3, 3, 0, 1, 12, 0, 0 },
24243 { "html", 0, 0, 9, 4, 4, 0, 2, 7, 0, 0 },
24244 { "insert", 0, 0, 10, 2, 2, 0, 1, 8, 0, 0 },
24245 { "jatom", 4, 1, 11, 6, 6, 0, 1, 12, 0, 0 },
24246 { "jobject", 0, 1, 11, 6, 6, 0, 0, 10, 0, 0 },
24247 { "json", 0, 0, 11, 6, 6, 0, 0, 9, 0, 0 },
24248 { "line", 0, 1, 9, 1, 1, 0, 0, 11, 1, 0 },
24249 { "list", 2, 1, 9, 1, 1, 0, 1, 12, 0, 0 },
24250 { "markdown", 0, 0, 9, 1, 1, 0, 2, 13, 2, 0 },
24251 { "off", 0, 0, 0, 0, 0, 0, 0, 14, 0, 0 },
24252 { "psql", 0, 0, 9, 1, 1, 0, 2, 19, 2, 1 },
24253 { "qbox", 0, 0, 10, 2, 1, 0, 2, 1, 2, 0 },
24254 { "quote", 4, 1, 10, 2, 2, 0, 1, 12, 0, 0 },
24255 { "split", 0, 0, 9, 1, 1, 0, 1, 2, 2, 2 },
24256 { "table", 0, 0, 9, 1, 1, 0, 2, 19, 2, 0 },
24257 { "tabs", 8, 1, 9, 3, 3, 0, 1, 12, 0, 0 },
24258 { "tcl", 3, 1, 12, 5, 5, 4, 1, 12, 0, 0 },
24259 { "www", 0, 0, 9, 4, 4, 0, 2, 7, 0, 0 }
24260 }; /* | / / | / / | | \
24261 ** | / / | / / | | \_ 2: columnar
24262 ** Index into aModeStr[] | / / | | 1: line
24263 ** | / / | | 0: other
24264 ** | / / | \
@@ -24304,11 +24380,16 @@
24380 if( pI->eNull ) modeSetStr(&pM->spec.zNull, aModeStr[pI->eNull]);
24381 pM->spec.eText = pI->eText;
24382 pM->spec.eBlob = pI->eBlob;
24383 pM->spec.bTitles = pI->bHdr;
24384 pM->spec.eTitle = pI->eHdr;
24385 if( pI->mFlg & 0x01 ){
24386 pM->spec.bBorder = QRF_No;
24387 }else{
24388 pM->spec.bBorder = QRF_Auto;
24389 }
24390 if( pI->mFlg & 0x02 ){
24391 pM->spec.bSplitColumn = QRF_Yes;
24392 pM->bAutoScreenWidth = 1;
24393 }else{
24394 pM->spec.bSplitColumn = QRF_No;
24395 }
@@ -26622,10 +26703,11 @@
26703 " meaning \"left\", \"centered\", and \"right\", with\n"
26704 " one letter per column starting from the left.\n"
26705 " Unspecified alignment defaults to 'L'.\n"
26706 " --blob-quote ARG ARG can be \"auto\", \"text\", \"sql\", \"hex\", \"tcl\",\n"
26707 " \"json\", or \"size\". Default is \"auto\".\n"
26708 " --border on|off Show outer border on \"box\" and \"table\" modes.\n"
26709 " --charlimit N Set the maximum number of output characters to\n"
26710 " show for any single SQL value to N. Longer values\n"
26711 " truncated. Zero means \"no limit\".\n"
26712 " --colsep STRING Use STRING as the column separator\n"
26713 " --escape ESC Enable/disable escaping of control characters\n"
@@ -30505,10 +30587,11 @@
30587 ** meaning "left", "centered", and "right", with
30588 ** one letter per column starting from the left.
30589 ** Unspecified alignment defaults to 'L'.
30590 ** --blob-quote ARG ARG can be "auto", "text", "sql", "hex", "tcl",
30591 ** "json", or "size". Default is "auto".
30592 ** --border on|off Show outer border on "box" and "table" modes.
30593 ** --charlimit N Set the maximum number of output characters to
30594 ** show for any single SQL value to N. Longer values
30595 ** truncated. Zero means "no limit".
30596 ** --colsep STRING Use STRING as the column separator
30597 ** --escape ESC Enable/disable escaping of control characters
@@ -30622,10 +30705,20 @@
30705 /* 0 1 2 3 4 5 6
30706 ** Must match QRF_BLOB_xxxx values. See also tag-20251124a */
30707 if( k>=0 ){
30708 p->mode.spec.eBlob = k & 0xff;
30709 }
30710 chng = 1;
30711 }else if( optionMatch(z,"border") ){
30712 if( (++i)>=nArg ){
30713 dotCmdError(p, i-1, "missing argument", 0);
30714 return 1;
30715 }
30716 k = pickStr(azArg[i], 0, "auto", "off", "on", "");
30717 if( k>=0 ){
30718 p->mode.spec.bBorder = k & 0x3;
30719 }
30720 chng = 1;
30721 }else if( 0<=(k=pickStr(z,0,"-charlimit","-linelimit","")) ){
30722 int w; /* 0 1 */
30723 if( i+1>=nArg ){
30724 dotCmdError(p, i, "missing argument", 0);
@@ -30844,16 +30937,16 @@
30937 zW = azArg[++i];
30938 /* Every width value takes at least 2 bytes in the input string to
30939 ** specify, so strlen(zW) bytes should be plenty of space to hold the
30940 ** result. */
30941 aWidth = malloc( strlen(zW) );
30942 while( IsSpace(zW[0]) ) zW++;
30943 while( zW[0] ){
30944 int w = 0;
30945 int nDigit = 0;
30946 k = zW[0]=='-' && IsDigit(zW[1]);
30947 while( IsDigit(zW[k]) ){
30948 w = w*10 + zW[k] - '0';
30949 if( w>QRF_MAX_WIDTH ){
30950 dotCmdError(p,i+1,"width too big",
30951 "Maximum column width is %d", QRF_MAX_WIDTH);
30952 free(aWidth);
@@ -30870,11 +30963,11 @@
30963 }
30964 if( zW[0]=='-' ) w = -w;
30965 aWidth[nWidth++] = w;
30966 zW += k;
30967 if( zW[0]==',' ) zW++;
30968 while( IsSpace(zW[0]) ) zW++;
30969 }
30970 free(p->mode.spec.aWidth);
30971 p->mode.spec.aWidth = aWidth;
30972 p->mode.spec.nWidth = nWidth;
30973 chng = 1;
@@ -30925,10 +31018,16 @@
31018 unsigned char a = p->mode.spec.aAlign[ii];
31019 sqlite3_str_appendchar(pDesc, 1, "LLCR"[a&3]);
31020 }
31021 sqlite3_str_append(pDesc, "\"", 1);
31022 }
31023 if( bAll
31024 || (p->mode.spec.bBorder==QRF_No) != ((pI->mFlg&1)!=0)
31025 ){
31026 sqlite3_str_appendf(pDesc," --border %s",
31027 p->mode.spec.bBorder==QRF_No ? "off" : "on");
31028 }
31029 if( bAll || p->mode.spec.eBlob!=QRF_BLOB_Auto ){
31030 const char *azBQuote[] =
31031 { "auto", "text", "sql", "hex", "tcl", "json", "size" };
31032 /* 0 1 2 3 4 5 6
31033 ** Must match QRF_BLOB_xxxx values. See all instances of tag-20251124a */
@@ -33669,11 +33768,10 @@
33768
33769 if( (c=='t' && n>1 && cli_strncmp(azArg[0], "tables", n)==0)
33770 || (c=='i' && (cli_strncmp(azArg[0], "indices", n)==0
33771 || cli_strncmp(azArg[0], "indexes", n)==0) )
33772 ){
 
33773 sqlite3_stmt *pStmt;
33774 sqlite3_str *pSql;
33775 const char *zPattern = nArg>1 ? azArg[1] : 0;
33776
33777 open_db(p, 0);
@@ -33691,11 +33789,11 @@
33789 rc = 1;
33790 sqlite3_finalize(pStmt);
33791 goto meta_command_exit;
33792 }
33793 pSql = sqlite3_str_new(p->db);
33794 while( sqlite3_step(pStmt)==SQLITE_ROW ){
33795 const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1);
33796 if( zDbName==0 ) continue;
33797 if( sqlite3_str_length(pSql) ){
33798 sqlite3_str_appendall(pSql, " UNION ALL ");
33799 }
@@ -35584,11 +35682,12 @@
35682 data.pAuxDb->zDbFilename = ":memory:";
35683 warnInmemoryDb = argc==1;
35684 #else
35685 cli_printf(stderr,
35686 "%s: Error: no database filename specified\n", Argv0);
35687 rc = 1;
35688 goto shell_main_exit;
35689 #endif
35690 }
35691 data.out = stdout;
35692 if( bEnableVfstrace ){
35693 vfstrace_register("trace",0,vfstraceOut, &data, 1);
@@ -35638,10 +35737,12 @@
35737 modeChange(&data, MODE_Json);
35738 }else if( cli_strcmp(z,"-markdown")==0 ){
35739 modeChange(&data, MODE_Markdown);
35740 }else if( cli_strcmp(z,"-table")==0 ){
35741 modeChange(&data, MODE_Table);
35742 }else if( cli_strcmp(z,"-psql")==0 ){
35743 modeChange(&data, MODE_Psql);
35744 }else if( cli_strcmp(z,"-box")==0 ){
35745 modeChange(&data, MODE_Box);
35746 }else if( cli_strcmp(z,"-csv")==0 ){
35747 modeChange(&data, MODE_Csv);
35748 }else if( cli_strcmp(z,"-escape")==0 && i+1<argc ){
@@ -35724,11 +35825,12 @@
35825 }else if( cli_strcmp(z,"-bail")==0 ){
35826 /* No-op. The bail_on_error flag should already be set. */
35827 }else if( cli_strcmp(z,"-version")==0 ){
35828 cli_printf(stdout, "%s %s (%d-bit)\n",
35829 sqlite3_libversion(), sqlite3_sourceid(), 8*(int)sizeof(char*));
35830 rc = 0;
35831 goto shell_main_exit;
35832 }else if( cli_strcmp(z,"-interactive")==0 ){
35833 /* Need to check for interactive override here to so that it can
35834 ** affect console setup (for Windows only) and testing thereof.
35835 */
35836 stdin_is_interactive = 1;
@@ -35781,29 +35883,33 @@
35883 ** we retain the goofy behavior for historical compatibility. */
35884 if( i==argc-1 ) break;
35885 z = cmdline_option_value(argc,argv,++i);
35886 if( z[0]=='.' ){
35887 rc = do_meta_command(z, &data);
35888 if( rc && bail_on_error ){
35889 if( rc==2 ) rc = 0;
35890 goto shell_main_exit;
35891 }
35892 }else{
35893 open_db(&data, 0);
35894 rc = shell_exec(&data, z, &zErrMsg);
35895 if( zErrMsg!=0 ){
35896 shellEmitError(zErrMsg);
35897 sqlite3_free(zErrMsg);
35898 if( !rc ) rc = 1;
35899 }else if( rc!=0 ){
35900 cli_printf(stderr,"Error: unable to process SQL \"%s\"\n", z);
 
35901 }
35902 if( bail_on_error ) goto shell_main_exit;
35903 }
35904 #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
35905 }else if( cli_strncmp(z, "-A", 2)==0 ){
35906 if( nCmd>0 ){
35907 cli_printf(stderr,"Error: cannot mix regular SQL or dot-commands"
35908 " with \"%s\"\n", z);
35909 rc = 1;
35910 goto shell_main_exit;
35911 }
35912 open_db(&data, OPEN_DB_ZIPFILE);
35913 if( z[2] ){
35914 argv[i] = &z[2];
35915 arDotCommand(&data, 1, argv+(i-1), argc-(i-1));
@@ -35819,11 +35925,12 @@
35925 }else if( cli_strcmp(z,"-unsafe-testing")==0 ){
35926 /* Acted upon in first pass. */
35927 }else{
35928 cli_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
35929 eputz("Use -help for a list of options.\n");
35930 rc = 1;
35931 goto shell_main_exit;
35932 }
35933 }
35934
35935 if( !readStdin ){
35936 /* Run all arguments that do not begin with '-' as if they were separate
35937
+22 -7
--- extsrc/sqlite3.c
+++ extsrc/sqlite3.c
@@ -16,11 +16,11 @@
1616
** if you want a wrapper to interface SQLite with your choice of programming
1717
** language. The code for the "sqlite3" command-line shell is also in a
1818
** separate file. This file contains only code for the core SQLite library.
1919
**
2020
** The content in this amalgamation comes from Fossil check-in
21
-** d4c1d3e30b774802a7abd5f61807a690fb5b with changes in files:
21
+** 4384c9a108b58a0b8c38c51678aad871f088 with changes in files:
2222
**
2323
**
2424
*/
2525
#ifndef SQLITE_AMALGAMATION
2626
#define SQLITE_CORE 1
@@ -467,14 +467,14 @@
467467
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
468468
** [sqlite_version()] and [sqlite_source_id()].
469469
*/
470470
#define SQLITE_VERSION "3.52.0"
471471
#define SQLITE_VERSION_NUMBER 3052000
472
-#define SQLITE_SOURCE_ID "2025-11-25 13:58:36 d4c1d3e30b774802a7abd5f61807a690fb5be7617459f3dbd7ec1efceb6125d7"
472
+#define SQLITE_SOURCE_ID "2025-11-28 17:56:22 4384c9a108b58a0b8c38c51678aad871f088358b9bff3922299cc7ddb3d247ce"
473473
#define SQLITE_SCM_BRANCH "trunk"
474474
#define SQLITE_SCM_TAGS ""
475
-#define SQLITE_SCM_DATETIME "2025-11-25T13:58:36.873Z"
475
+#define SQLITE_SCM_DATETIME "2025-11-28T17:56:22.595Z"
476476
477477
/*
478478
** CAPI3REF: Run-Time Library Version Numbers
479479
** KEYWORDS: sqlite3_version sqlite3_sourceid
480480
**
@@ -203373,11 +203373,12 @@
203373203373
PendingList *pList;
203374203374
int rc = SQLITE_OK;
203375203375
203376203376
pList = (PendingList *)fts3HashFind(pHash, zToken, nToken);
203377203377
if( pList ){
203378
- p->nPendingData -= (pList->nData + nToken + sizeof(Fts3HashElem));
203378
+ assert( pList->nData+nToken+sizeof(Fts3HashElem) <= (i64)p->nPendingData );
203379
+ p->nPendingData -= (int)(pList->nData + nToken + sizeof(Fts3HashElem));
203379203380
}
203380203381
if( fts3PendingListAppend(&pList, p->iPrevDocid, iCol, iPos, &rc) ){
203381203382
if( pList==fts3HashInsert(pHash, zToken, nToken, pList) ){
203382203383
/* Malloc failed while inserting the new entry. This can only
203383203384
** happen if there was no previous entry for this token.
@@ -203386,11 +203387,13 @@
203386203387
sqlite3_free(pList);
203387203388
rc = SQLITE_NOMEM;
203388203389
}
203389203390
}
203390203391
if( rc==SQLITE_OK ){
203391
- p->nPendingData += (pList->nData + nToken + sizeof(Fts3HashElem));
203392
+ assert( (i64)p->nPendingData + pList->nData + nToken
203393
+ + sizeof(Fts3HashElem) <= 0x3fffffff );
203394
+ p->nPendingData += (int)(pList->nData + nToken + sizeof(Fts3HashElem));
203392203395
}
203393203396
return rc;
203394203397
}
203395203398
203396203399
/*
@@ -208070,11 +208073,11 @@
208070208073
int v;
208071208074
if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
208072208075
v = atoi(&zVal[9]);
208073208076
if( v>=24 && v<=p->nPgsz-35 ) p->nNodeSize = v;
208074208077
rc = SQLITE_OK;
208075
- }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
208078
+ }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 11) ){
208076208079
v = atoi(&zVal[11]);
208077208080
if( v>=64 && v<=FTS3_MAX_PENDING_DATA ) p->nMaxPendingData = v;
208078208081
rc = SQLITE_OK;
208079208082
}else if( nVal>21 && 0==sqlite3_strnicmp(zVal,"test-no-incr-doclist=",21) ){
208080208083
p->bNoIncrDoclist = atoi(&zVal[21]);
@@ -250200,10 +250203,11 @@
250200250203
250201250204
ASSERT_SZLEAF_OK(pIter->pLeaf);
250202250205
while( 1 ){
250203250206
u64 iDelta = 0;
250204250207
250208
+ if( i>=n ) break;
250205250209
if( eDetail==FTS5_DETAIL_NONE ){
250206250210
/* todo */
250207250211
if( i<n && a[i]==0 ){
250208250212
i++;
250209250213
if( i<n && a[i]==0 ) i++;
@@ -261263,11 +261267,11 @@
261263261267
int nArg, /* Number of args */
261264261268
sqlite3_value **apUnused /* Function arguments */
261265261269
){
261266261270
assert( nArg==0 );
261267261271
UNUSED_PARAM2(nArg, apUnused);
261268
- sqlite3_result_text(pCtx, "fts5: 2025-11-25 13:58:36 d4c1d3e30b774802a7abd5f61807a690fb5be7617459f3dbd7ec1efceb6125d7", -1, SQLITE_TRANSIENT);
261272
+ sqlite3_result_text(pCtx, "fts5: 2025-11-28 17:56:22 4384c9a108b58a0b8c38c51678aad871f088358b9bff3922299cc7ddb3d247ce", -1, SQLITE_TRANSIENT);
261269261273
}
261270261274
261271261275
/*
261272261276
** Implementation of fts5_locale(LOCALE, TEXT) function.
261273261277
**
@@ -266084,20 +266088,31 @@
266084266088
266085266089
*ppCsr = (sqlite3_vtab_cursor*)pCsr;
266086266090
return rc;
266087266091
}
266088266092
266093
+/*
266094
+** Restore cursor pCsr to the state it was in immediately after being
266095
+** created by the xOpen() method.
266096
+*/
266089266097
static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){
266098
+ int nCol = pCsr->pFts5->pConfig->nCol;
266090266099
pCsr->rowid = 0;
266091266100
sqlite3Fts5IterClose(pCsr->pIter);
266092266101
sqlite3Fts5StructureRelease(pCsr->pStruct);
266093266102
pCsr->pStruct = 0;
266094266103
pCsr->pIter = 0;
266095266104
sqlite3_free(pCsr->zLeTerm);
266096266105
pCsr->nLeTerm = -1;
266097266106
pCsr->zLeTerm = 0;
266098266107
pCsr->bEof = 0;
266108
+ pCsr->iCol = 0;
266109
+ pCsr->iInstPos = 0;
266110
+ pCsr->iInstOff = 0;
266111
+ pCsr->colUsed = 0;
266112
+ memset(pCsr->aCnt, 0, sizeof(i64)*nCol);
266113
+ memset(pCsr->aDoc, 0, sizeof(i64)*nCol);
266099266114
}
266100266115
266101266116
/*
266102266117
** Close the cursor. For additional information see the documentation
266103266118
** on the xClose method of the virtual table interface.
266104266119
--- extsrc/sqlite3.c
+++ extsrc/sqlite3.c
@@ -16,11 +16,11 @@
16 ** if you want a wrapper to interface SQLite with your choice of programming
17 ** language. The code for the "sqlite3" command-line shell is also in a
18 ** separate file. This file contains only code for the core SQLite library.
19 **
20 ** The content in this amalgamation comes from Fossil check-in
21 ** d4c1d3e30b774802a7abd5f61807a690fb5b with changes in files:
22 **
23 **
24 */
25 #ifndef SQLITE_AMALGAMATION
26 #define SQLITE_CORE 1
@@ -467,14 +467,14 @@
467 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
468 ** [sqlite_version()] and [sqlite_source_id()].
469 */
470 #define SQLITE_VERSION "3.52.0"
471 #define SQLITE_VERSION_NUMBER 3052000
472 #define SQLITE_SOURCE_ID "2025-11-25 13:58:36 d4c1d3e30b774802a7abd5f61807a690fb5be7617459f3dbd7ec1efceb6125d7"
473 #define SQLITE_SCM_BRANCH "trunk"
474 #define SQLITE_SCM_TAGS ""
475 #define SQLITE_SCM_DATETIME "2025-11-25T13:58:36.873Z"
476
477 /*
478 ** CAPI3REF: Run-Time Library Version Numbers
479 ** KEYWORDS: sqlite3_version sqlite3_sourceid
480 **
@@ -203373,11 +203373,12 @@
203373 PendingList *pList;
203374 int rc = SQLITE_OK;
203375
203376 pList = (PendingList *)fts3HashFind(pHash, zToken, nToken);
203377 if( pList ){
203378 p->nPendingData -= (pList->nData + nToken + sizeof(Fts3HashElem));
 
203379 }
203380 if( fts3PendingListAppend(&pList, p->iPrevDocid, iCol, iPos, &rc) ){
203381 if( pList==fts3HashInsert(pHash, zToken, nToken, pList) ){
203382 /* Malloc failed while inserting the new entry. This can only
203383 ** happen if there was no previous entry for this token.
@@ -203386,11 +203387,13 @@
203386 sqlite3_free(pList);
203387 rc = SQLITE_NOMEM;
203388 }
203389 }
203390 if( rc==SQLITE_OK ){
203391 p->nPendingData += (pList->nData + nToken + sizeof(Fts3HashElem));
 
 
203392 }
203393 return rc;
203394 }
203395
203396 /*
@@ -208070,11 +208073,11 @@
208070 int v;
208071 if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
208072 v = atoi(&zVal[9]);
208073 if( v>=24 && v<=p->nPgsz-35 ) p->nNodeSize = v;
208074 rc = SQLITE_OK;
208075 }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
208076 v = atoi(&zVal[11]);
208077 if( v>=64 && v<=FTS3_MAX_PENDING_DATA ) p->nMaxPendingData = v;
208078 rc = SQLITE_OK;
208079 }else if( nVal>21 && 0==sqlite3_strnicmp(zVal,"test-no-incr-doclist=",21) ){
208080 p->bNoIncrDoclist = atoi(&zVal[21]);
@@ -250200,10 +250203,11 @@
250200
250201 ASSERT_SZLEAF_OK(pIter->pLeaf);
250202 while( 1 ){
250203 u64 iDelta = 0;
250204
 
250205 if( eDetail==FTS5_DETAIL_NONE ){
250206 /* todo */
250207 if( i<n && a[i]==0 ){
250208 i++;
250209 if( i<n && a[i]==0 ) i++;
@@ -261263,11 +261267,11 @@
261263 int nArg, /* Number of args */
261264 sqlite3_value **apUnused /* Function arguments */
261265 ){
261266 assert( nArg==0 );
261267 UNUSED_PARAM2(nArg, apUnused);
261268 sqlite3_result_text(pCtx, "fts5: 2025-11-25 13:58:36 d4c1d3e30b774802a7abd5f61807a690fb5be7617459f3dbd7ec1efceb6125d7", -1, SQLITE_TRANSIENT);
261269 }
261270
261271 /*
261272 ** Implementation of fts5_locale(LOCALE, TEXT) function.
261273 **
@@ -266084,20 +266088,31 @@
266084
266085 *ppCsr = (sqlite3_vtab_cursor*)pCsr;
266086 return rc;
266087 }
266088
 
 
 
 
266089 static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){
 
266090 pCsr->rowid = 0;
266091 sqlite3Fts5IterClose(pCsr->pIter);
266092 sqlite3Fts5StructureRelease(pCsr->pStruct);
266093 pCsr->pStruct = 0;
266094 pCsr->pIter = 0;
266095 sqlite3_free(pCsr->zLeTerm);
266096 pCsr->nLeTerm = -1;
266097 pCsr->zLeTerm = 0;
266098 pCsr->bEof = 0;
 
 
 
 
 
 
266099 }
266100
266101 /*
266102 ** Close the cursor. For additional information see the documentation
266103 ** on the xClose method of the virtual table interface.
266104
--- extsrc/sqlite3.c
+++ extsrc/sqlite3.c
@@ -16,11 +16,11 @@
16 ** if you want a wrapper to interface SQLite with your choice of programming
17 ** language. The code for the "sqlite3" command-line shell is also in a
18 ** separate file. This file contains only code for the core SQLite library.
19 **
20 ** The content in this amalgamation comes from Fossil check-in
21 ** 4384c9a108b58a0b8c38c51678aad871f088 with changes in files:
22 **
23 **
24 */
25 #ifndef SQLITE_AMALGAMATION
26 #define SQLITE_CORE 1
@@ -467,14 +467,14 @@
467 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
468 ** [sqlite_version()] and [sqlite_source_id()].
469 */
470 #define SQLITE_VERSION "3.52.0"
471 #define SQLITE_VERSION_NUMBER 3052000
472 #define SQLITE_SOURCE_ID "2025-11-28 17:56:22 4384c9a108b58a0b8c38c51678aad871f088358b9bff3922299cc7ddb3d247ce"
473 #define SQLITE_SCM_BRANCH "trunk"
474 #define SQLITE_SCM_TAGS ""
475 #define SQLITE_SCM_DATETIME "2025-11-28T17:56:22.595Z"
476
477 /*
478 ** CAPI3REF: Run-Time Library Version Numbers
479 ** KEYWORDS: sqlite3_version sqlite3_sourceid
480 **
@@ -203373,11 +203373,12 @@
203373 PendingList *pList;
203374 int rc = SQLITE_OK;
203375
203376 pList = (PendingList *)fts3HashFind(pHash, zToken, nToken);
203377 if( pList ){
203378 assert( pList->nData+nToken+sizeof(Fts3HashElem) <= (i64)p->nPendingData );
203379 p->nPendingData -= (int)(pList->nData + nToken + sizeof(Fts3HashElem));
203380 }
203381 if( fts3PendingListAppend(&pList, p->iPrevDocid, iCol, iPos, &rc) ){
203382 if( pList==fts3HashInsert(pHash, zToken, nToken, pList) ){
203383 /* Malloc failed while inserting the new entry. This can only
203384 ** happen if there was no previous entry for this token.
@@ -203386,11 +203387,13 @@
203387 sqlite3_free(pList);
203388 rc = SQLITE_NOMEM;
203389 }
203390 }
203391 if( rc==SQLITE_OK ){
203392 assert( (i64)p->nPendingData + pList->nData + nToken
203393 + sizeof(Fts3HashElem) <= 0x3fffffff );
203394 p->nPendingData += (int)(pList->nData + nToken + sizeof(Fts3HashElem));
203395 }
203396 return rc;
203397 }
203398
203399 /*
@@ -208070,11 +208073,11 @@
208073 int v;
208074 if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
208075 v = atoi(&zVal[9]);
208076 if( v>=24 && v<=p->nPgsz-35 ) p->nNodeSize = v;
208077 rc = SQLITE_OK;
208078 }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 11) ){
208079 v = atoi(&zVal[11]);
208080 if( v>=64 && v<=FTS3_MAX_PENDING_DATA ) p->nMaxPendingData = v;
208081 rc = SQLITE_OK;
208082 }else if( nVal>21 && 0==sqlite3_strnicmp(zVal,"test-no-incr-doclist=",21) ){
208083 p->bNoIncrDoclist = atoi(&zVal[21]);
@@ -250200,10 +250203,11 @@
250203
250204 ASSERT_SZLEAF_OK(pIter->pLeaf);
250205 while( 1 ){
250206 u64 iDelta = 0;
250207
250208 if( i>=n ) break;
250209 if( eDetail==FTS5_DETAIL_NONE ){
250210 /* todo */
250211 if( i<n && a[i]==0 ){
250212 i++;
250213 if( i<n && a[i]==0 ) i++;
@@ -261263,11 +261267,11 @@
261267 int nArg, /* Number of args */
261268 sqlite3_value **apUnused /* Function arguments */
261269 ){
261270 assert( nArg==0 );
261271 UNUSED_PARAM2(nArg, apUnused);
261272 sqlite3_result_text(pCtx, "fts5: 2025-11-28 17:56:22 4384c9a108b58a0b8c38c51678aad871f088358b9bff3922299cc7ddb3d247ce", -1, SQLITE_TRANSIENT);
261273 }
261274
261275 /*
261276 ** Implementation of fts5_locale(LOCALE, TEXT) function.
261277 **
@@ -266084,20 +266088,31 @@
266088
266089 *ppCsr = (sqlite3_vtab_cursor*)pCsr;
266090 return rc;
266091 }
266092
266093 /*
266094 ** Restore cursor pCsr to the state it was in immediately after being
266095 ** created by the xOpen() method.
266096 */
266097 static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){
266098 int nCol = pCsr->pFts5->pConfig->nCol;
266099 pCsr->rowid = 0;
266100 sqlite3Fts5IterClose(pCsr->pIter);
266101 sqlite3Fts5StructureRelease(pCsr->pStruct);
266102 pCsr->pStruct = 0;
266103 pCsr->pIter = 0;
266104 sqlite3_free(pCsr->zLeTerm);
266105 pCsr->nLeTerm = -1;
266106 pCsr->zLeTerm = 0;
266107 pCsr->bEof = 0;
266108 pCsr->iCol = 0;
266109 pCsr->iInstPos = 0;
266110 pCsr->iInstOff = 0;
266111 pCsr->colUsed = 0;
266112 memset(pCsr->aCnt, 0, sizeof(i64)*nCol);
266113 memset(pCsr->aDoc, 0, sizeof(i64)*nCol);
266114 }
266115
266116 /*
266117 ** Close the cursor. For additional information see the documentation
266118 ** on the xClose method of the virtual table interface.
266119
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,14 +146,14 @@
146146
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147147
** [sqlite_version()] and [sqlite_source_id()].
148148
*/
149149
#define SQLITE_VERSION "3.52.0"
150150
#define SQLITE_VERSION_NUMBER 3052000
151
-#define SQLITE_SOURCE_ID "2025-11-25 13:58:36 d4c1d3e30b774802a7abd5f61807a690fb5be7617459f3dbd7ec1efceb6125d7"
151
+#define SQLITE_SOURCE_ID "2025-11-28 17:56:22 4384c9a108b58a0b8c38c51678aad871f088358b9bff3922299cc7ddb3d247ce"
152152
#define SQLITE_SCM_BRANCH "trunk"
153153
#define SQLITE_SCM_TAGS ""
154
-#define SQLITE_SCM_DATETIME "2025-11-25T13:58:36.873Z"
154
+#define SQLITE_SCM_DATETIME "2025-11-28T17:56:22.595Z"
155155
156156
/*
157157
** CAPI3REF: Run-Time Library Version Numbers
158158
** KEYWORDS: sqlite3_version sqlite3_sourceid
159159
**
160160
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,14 +146,14 @@
146 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147 ** [sqlite_version()] and [sqlite_source_id()].
148 */
149 #define SQLITE_VERSION "3.52.0"
150 #define SQLITE_VERSION_NUMBER 3052000
151 #define SQLITE_SOURCE_ID "2025-11-25 13:58:36 d4c1d3e30b774802a7abd5f61807a690fb5be7617459f3dbd7ec1efceb6125d7"
152 #define SQLITE_SCM_BRANCH "trunk"
153 #define SQLITE_SCM_TAGS ""
154 #define SQLITE_SCM_DATETIME "2025-11-25T13:58:36.873Z"
155
156 /*
157 ** CAPI3REF: Run-Time Library Version Numbers
158 ** KEYWORDS: sqlite3_version sqlite3_sourceid
159 **
160
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,14 +146,14 @@
146 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147 ** [sqlite_version()] and [sqlite_source_id()].
148 */
149 #define SQLITE_VERSION "3.52.0"
150 #define SQLITE_VERSION_NUMBER 3052000
151 #define SQLITE_SOURCE_ID "2025-11-28 17:56:22 4384c9a108b58a0b8c38c51678aad871f088358b9bff3922299cc7ddb3d247ce"
152 #define SQLITE_SCM_BRANCH "trunk"
153 #define SQLITE_SCM_TAGS ""
154 #define SQLITE_SCM_DATETIME "2025-11-28T17:56:22.595Z"
155
156 /*
157 ** CAPI3REF: Run-Time Library Version Numbers
158 ** KEYWORDS: sqlite3_version sqlite3_sourceid
159 **
160
+5
--- src/chat.c
+++ src/chat.c
@@ -238,10 +238,15 @@
238238
@ </div>
239239
@ <div id='chat-messages-wrapper' class='chat-view'>
240240
/* New chat messages get inserted immediately after this element */
241241
@ <span id='message-inject-point'></span>
242242
@ </div>
243
+ @ <div id='chat-zoom' class='hidden chat-view'>
244
+ @ <div id='chat-zoom-content'></div>
245
+ @ <div class='button-bar'><button class='action-close'>Close Zoom</button></div>
246
+ @ </div>
247
+ @ <span id='chat-zoom-marker' class='hidden'><!-- placeholder marker for zoomed msg --></span>
243248
fossil_free(zProjectName);
244249
fossil_free(zInputPlaceholder0);
245250
builtin_fossil_js_bundle_or("popupwidget", "storage", "fetch",
246251
"pikchr", "confirmer", "copybutton",
247252
NULL);
248253
--- src/chat.c
+++ src/chat.c
@@ -238,10 +238,15 @@
238 @ </div>
239 @ <div id='chat-messages-wrapper' class='chat-view'>
240 /* New chat messages get inserted immediately after this element */
241 @ <span id='message-inject-point'></span>
242 @ </div>
 
 
 
 
 
243 fossil_free(zProjectName);
244 fossil_free(zInputPlaceholder0);
245 builtin_fossil_js_bundle_or("popupwidget", "storage", "fetch",
246 "pikchr", "confirmer", "copybutton",
247 NULL);
248
--- src/chat.c
+++ src/chat.c
@@ -238,10 +238,15 @@
238 @ </div>
239 @ <div id='chat-messages-wrapper' class='chat-view'>
240 /* New chat messages get inserted immediately after this element */
241 @ <span id='message-inject-point'></span>
242 @ </div>
243 @ <div id='chat-zoom' class='hidden chat-view'>
244 @ <div id='chat-zoom-content'></div>
245 @ <div class='button-bar'><button class='action-close'>Close Zoom</button></div>
246 @ </div>
247 @ <span id='chat-zoom-marker' class='hidden'><!-- placeholder marker for zoomed msg --></span>
248 fossil_free(zProjectName);
249 fossil_free(zInputPlaceholder0);
250 builtin_fossil_js_bundle_or("popupwidget", "storage", "fetch",
251 "pikchr", "confirmer", "copybutton",
252 NULL);
253
+4 -5
--- src/default.css
+++ src/default.css
@@ -1895,15 +1895,14 @@
18951895
div.markdown ol.footnotes > li.fn-joined > sup.fn-joined,
18961896
table.numbered-lines > tbody > tr,
18971897
tr.diffskip > td.chunkctrl,
18981898
#fossil-status-bar,
18991899
.monospace {
1900
- font-family: "Source Code Pro", "Menlo", "Monaco", "Consolas",
1901
- "Andale Mono", "Ubuntu Mono", "Deja Vu Sans Mono",
1902
- "Letter Gothic", "Letter Gothic Std", "Prestige Elite Std",
1903
- "Courier", "Courier New",
1904
- monospace;
1900
+ font-family: "MesloLGSDZ Nerd Font Mono", /* 2025 hotness */
1901
+ "Source Code Pro", /* 2012 hotness */
1902
+ "Menlo", "Monaco", "SF Mono", /* Safari reverted to Courier in 2022 */
1903
+ monospace; /* let OS/browser default take over */
19051904
}
19061905
19071906
div.markdown > ol.footnotes {
19081907
font-size: 90%;
19091908
}
19101909
--- src/default.css
+++ src/default.css
@@ -1895,15 +1895,14 @@
1895 div.markdown ol.footnotes > li.fn-joined > sup.fn-joined,
1896 table.numbered-lines > tbody > tr,
1897 tr.diffskip > td.chunkctrl,
1898 #fossil-status-bar,
1899 .monospace {
1900 font-family: "Source Code Pro", "Menlo", "Monaco", "Consolas",
1901 "Andale Mono", "Ubuntu Mono", "Deja Vu Sans Mono",
1902 "Letter Gothic", "Letter Gothic Std", "Prestige Elite Std",
1903 "Courier", "Courier New",
1904 monospace;
1905 }
1906
1907 div.markdown > ol.footnotes {
1908 font-size: 90%;
1909 }
1910
--- src/default.css
+++ src/default.css
@@ -1895,15 +1895,14 @@
1895 div.markdown ol.footnotes > li.fn-joined > sup.fn-joined,
1896 table.numbered-lines > tbody > tr,
1897 tr.diffskip > td.chunkctrl,
1898 #fossil-status-bar,
1899 .monospace {
1900 font-family: "MesloLGSDZ Nerd Font Mono", /* 2025 hotness */
1901 "Source Code Pro", /* 2012 hotness */
1902 "Menlo", "Monaco", "SF Mono", /* Safari reverted to Courier in 2022 */
1903 monospace; /* let OS/browser default take over */
 
1904 }
1905
1906 div.markdown > ol.footnotes {
1907 font-size: 90%;
1908 }
1909
+8 -5
--- src/file.c
+++ src/file.c
@@ -2560,11 +2560,11 @@
25602560
** entries, even if the provided glob would match them.
25612561
*/
25622562
int file_directory_list(
25632563
const char *zDir, /* Directory to get a listing of */
25642564
const char *zGlob, /* Only list objects matching this pattern */
2565
- int omitDotFiles, /* Omit files that begin with "." if true */
2565
+ int omitDotFiles, /* 0: skip "." and "..", 1: no .-files, 2: keep all */
25662566
int nLimit, /* Find at most this many files. 0 means "all" */
25672567
char ***pazList /* OUT: Write the list here, if not NULL */
25682568
){
25692569
void *zNative;
25702570
DIR *d;
@@ -2577,15 +2577,18 @@
25772577
struct dirent *pEntry;
25782578
n = 0;
25792579
while( (pEntry=readdir(d))!=0 ){
25802580
char *zUtf8 = 0;
25812581
if( pEntry->d_name[0]==0 ) continue;
2582
- if( pEntry->d_name[0]=='.' &&
2583
- (omitDotFiles
2584
- /* Skip the special "." and ".." entries. */
2582
+ if( pEntry->d_name[0]=='.'
2583
+ && omitDotFiles<2
2584
+ && (omitDotFiles==1
2585
+ /* Skip the special "." and ".." entries unless omitDotFiles>=2 */
25852586
|| pEntry->d_name[1]==0
2586
- || (pEntry->d_name[1]=='.' && pEntry->d_name[2]==0))){
2587
+ || (pEntry->d_name[1]=='.' && pEntry->d_name[2]==0)
2588
+ )
2589
+ ){
25872590
continue;
25882591
}
25892592
if( zGlob ){
25902593
int rc;
25912594
zUtf8 = fossil_path_to_utf8(pEntry->d_name);
25922595
--- src/file.c
+++ src/file.c
@@ -2560,11 +2560,11 @@
2560 ** entries, even if the provided glob would match them.
2561 */
2562 int file_directory_list(
2563 const char *zDir, /* Directory to get a listing of */
2564 const char *zGlob, /* Only list objects matching this pattern */
2565 int omitDotFiles, /* Omit files that begin with "." if true */
2566 int nLimit, /* Find at most this many files. 0 means "all" */
2567 char ***pazList /* OUT: Write the list here, if not NULL */
2568 ){
2569 void *zNative;
2570 DIR *d;
@@ -2577,15 +2577,18 @@
2577 struct dirent *pEntry;
2578 n = 0;
2579 while( (pEntry=readdir(d))!=0 ){
2580 char *zUtf8 = 0;
2581 if( pEntry->d_name[0]==0 ) continue;
2582 if( pEntry->d_name[0]=='.' &&
2583 (omitDotFiles
2584 /* Skip the special "." and ".." entries. */
 
2585 || pEntry->d_name[1]==0
2586 || (pEntry->d_name[1]=='.' && pEntry->d_name[2]==0))){
 
 
2587 continue;
2588 }
2589 if( zGlob ){
2590 int rc;
2591 zUtf8 = fossil_path_to_utf8(pEntry->d_name);
2592
--- src/file.c
+++ src/file.c
@@ -2560,11 +2560,11 @@
2560 ** entries, even if the provided glob would match them.
2561 */
2562 int file_directory_list(
2563 const char *zDir, /* Directory to get a listing of */
2564 const char *zGlob, /* Only list objects matching this pattern */
2565 int omitDotFiles, /* 0: skip "." and "..", 1: no .-files, 2: keep all */
2566 int nLimit, /* Find at most this many files. 0 means "all" */
2567 char ***pazList /* OUT: Write the list here, if not NULL */
2568 ){
2569 void *zNative;
2570 DIR *d;
@@ -2577,15 +2577,18 @@
2577 struct dirent *pEntry;
2578 n = 0;
2579 while( (pEntry=readdir(d))!=0 ){
2580 char *zUtf8 = 0;
2581 if( pEntry->d_name[0]==0 ) continue;
2582 if( pEntry->d_name[0]=='.'
2583 && omitDotFiles<2
2584 && (omitDotFiles==1
2585 /* Skip the special "." and ".." entries unless omitDotFiles>=2 */
2586 || pEntry->d_name[1]==0
2587 || (pEntry->d_name[1]=='.' && pEntry->d_name[2]==0)
2588 )
2589 ){
2590 continue;
2591 }
2592 if( zGlob ){
2593 int rc;
2594 zUtf8 = fossil_path_to_utf8(pEntry->d_name);
2595
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -142,10 +142,13 @@
142142
loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */,
143143
inputArea: E1("#chat-input-area"),
144144
inputLineWrapper: E1('#chat-input-line-wrapper'),
145145
fileSelectWrapper: E1('#chat-input-file-area'),
146146
viewMessages: E1('#chat-messages-wrapper'),
147
+ viewZoom: E1('#chat-zoom'),
148
+ zoomContent: E1('#chat-zoom-content'),
149
+ zoomMarker: E1('#chat-zoom-marker'),
147150
btnSubmit: E1('#chat-button-submit'),
148151
btnAttach: E1('#chat-button-attach'),
149152
inputX: E1('#chat-input-field-x'),
150153
input1: E1('#chat-input-field-single'),
151154
inputM: E1('#chat-input-field-multi'),
@@ -590,10 +593,14 @@
590593
all other elements in that list. Returns e.
591594
*/
592595
setCurrentView: function(e){
593596
if(e===this.e.currentView){
594597
return e;
598
+ }
599
+ if( e!==this.e.viewZoom && this.e.zoomedMsg ){
600
+ this.zoomMessage(null, e);
601
+ return this.e.currentView;
595602
}
596603
this.e.views.forEach(function(E){
597604
if(e!==E) D.addClass(E,'hidden');
598605
});
599606
this.e.currentView = e;
@@ -600,10 +607,33 @@
600607
if(this.e.currentView.$beforeShow) this.e.currentView.$beforeShow();
601608
D.removeClass(e,'hidden');
602609
this.animate(this.e.currentView, 'anim-fade-in-fast');
603610
return this.e.currentView;
604611
},
612
+
613
+ /**
614
+ Makes message element eMsg the content of this.e.viewZoom.
615
+ */
616
+ zoomMessage: function(eMsg,nextView){
617
+ const marker = this.e.zoomMarker;
618
+ if( !eMsg || eMsg===this.e.zoomedMsg ){
619
+ if( this.e.zoomedMsg ){
620
+ marker.parentNode.insertBefore(this.e.zoomedMsg, marker);
621
+ delete this.e.zoomedMsg;
622
+ }
623
+ this.setCurrentView(nextView || this.e.viewMessages);
624
+ return;
625
+ }
626
+ console.log("zoom message",eMsg);
627
+ if( this.e.zoomedMsg ){
628
+ marker.parentNode.insertBefore(this.e.zoomedMsg, marker);
629
+ }
630
+ this.e.viewMessages.insertBefore(marker, eMsg);
631
+ this.e.zoomContent.appendChild(eMsg);
632
+ this.e.zoomedMsg = eMsg;
633
+ this.setCurrentView(this.e.viewZoom);
634
+ },
605635
/**
606636
Updates the "active user list" view if we are not currently
607637
batch-loading messages and if the active user list UI element
608638
is active.
609639
*/
@@ -1349,15 +1379,16 @@
13491379
has since been resolved by emiting a stricter
13501380
format. */
13511381
// Date doesn't work, so dumb it down...
13521382
D.append(this.e, D.append(D.span(), eMsg.dataset.timestamp," zulu"));
13531383
}
1354
- const toolbar = D.addClass(D.div(), 'toolbar');
1384
+ const toolbar = D.addClass(D.div(), 'toolbar', 'hide-in-zoom');
13551385
D.append(this.e, toolbar);
1386
+ const self = this;
1387
+
13561388
const btnDeleteLocal = D.button("Delete locally");
13571389
D.append(toolbar, btnDeleteLocal);
1358
- const self = this;
13591390
btnDeleteLocal.addEventListener('click', function(){
13601391
self.hide();
13611392
Chat.deleteMessageElem(eMsg)
13621393
});
13631394
if( eMsg.classList.contains('notification') ){
@@ -1381,11 +1412,11 @@
13811412
self.hide();
13821413
Chat.deleteMessage(eMsg);
13831414
}
13841415
});
13851416
}
1386
- const toolbar3 = D.addClass(D.div(), 'toolbar');
1417
+ const toolbar3 = D.addClass(D.div(), 'toolbar', 'hide-in-zoom');
13871418
D.append(this.e, toolbar3);
13881419
D.append(toolbar3, D.button(
13891420
"Locally remove all previous messages",
13901421
function(){
13911422
self.hide();
@@ -1444,10 +1475,15 @@
14441475
})
14451476
)
14461477
);
14471478
}/*jump-to button*/
14481479
}
1480
+ const btnZoom = D.button("Zoom");
1481
+ D.append(toolbar2, btnZoom);
1482
+ btnZoom.addEventListener('click', function(){
1483
+ Chat.zoomMessage(eMsg);
1484
+ });
14491485
const tab = eMsg.querySelector('.message-widget-tab');
14501486
D.append(tab, this.e);
14511487
D.removeClass(this.e, 'hidden');
14521488
Chat.animate(this.e, 'anim-fade-in-fast');
14531489
}/*refresh()*/,
@@ -2433,10 +2469,19 @@
24332469
ev.stopPropagation();
24342470
Chat.setCurrentView(Chat.e.viewMessages);
24352471
return false;
24362472
}, false);
24372473
})()/*search view setup*/;
2474
+
2475
+ (function(){/*Set up the zoom view */
2476
+ Chat.e.viewZoom.querySelector('button.action-close').addEventListener('click', function(ev){
2477
+ ev.preventDefault();
2478
+ ev.stopPropagation();
2479
+ Chat.zoomMessage(null);
2480
+ return false;
2481
+ }, false);
2482
+ })()/*zoom view setup*/;
24382483
24392484
/** Callback for poll() to inject new content into the page. jx ==
24402485
the response from /chat-poll. If atEnd is true, the message is
24412486
appended to the end of the chat list (for loading older
24422487
messages), else the beginning (the default). */
24432488
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -142,10 +142,13 @@
142 loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */,
143 inputArea: E1("#chat-input-area"),
144 inputLineWrapper: E1('#chat-input-line-wrapper'),
145 fileSelectWrapper: E1('#chat-input-file-area'),
146 viewMessages: E1('#chat-messages-wrapper'),
 
 
 
147 btnSubmit: E1('#chat-button-submit'),
148 btnAttach: E1('#chat-button-attach'),
149 inputX: E1('#chat-input-field-x'),
150 input1: E1('#chat-input-field-single'),
151 inputM: E1('#chat-input-field-multi'),
@@ -590,10 +593,14 @@
590 all other elements in that list. Returns e.
591 */
592 setCurrentView: function(e){
593 if(e===this.e.currentView){
594 return e;
 
 
 
 
595 }
596 this.e.views.forEach(function(E){
597 if(e!==E) D.addClass(E,'hidden');
598 });
599 this.e.currentView = e;
@@ -600,10 +607,33 @@
600 if(this.e.currentView.$beforeShow) this.e.currentView.$beforeShow();
601 D.removeClass(e,'hidden');
602 this.animate(this.e.currentView, 'anim-fade-in-fast');
603 return this.e.currentView;
604 },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
605 /**
606 Updates the "active user list" view if we are not currently
607 batch-loading messages and if the active user list UI element
608 is active.
609 */
@@ -1349,15 +1379,16 @@
1349 has since been resolved by emiting a stricter
1350 format. */
1351 // Date doesn't work, so dumb it down...
1352 D.append(this.e, D.append(D.span(), eMsg.dataset.timestamp," zulu"));
1353 }
1354 const toolbar = D.addClass(D.div(), 'toolbar');
1355 D.append(this.e, toolbar);
 
 
1356 const btnDeleteLocal = D.button("Delete locally");
1357 D.append(toolbar, btnDeleteLocal);
1358 const self = this;
1359 btnDeleteLocal.addEventListener('click', function(){
1360 self.hide();
1361 Chat.deleteMessageElem(eMsg)
1362 });
1363 if( eMsg.classList.contains('notification') ){
@@ -1381,11 +1412,11 @@
1381 self.hide();
1382 Chat.deleteMessage(eMsg);
1383 }
1384 });
1385 }
1386 const toolbar3 = D.addClass(D.div(), 'toolbar');
1387 D.append(this.e, toolbar3);
1388 D.append(toolbar3, D.button(
1389 "Locally remove all previous messages",
1390 function(){
1391 self.hide();
@@ -1444,10 +1475,15 @@
1444 })
1445 )
1446 );
1447 }/*jump-to button*/
1448 }
 
 
 
 
 
1449 const tab = eMsg.querySelector('.message-widget-tab');
1450 D.append(tab, this.e);
1451 D.removeClass(this.e, 'hidden');
1452 Chat.animate(this.e, 'anim-fade-in-fast');
1453 }/*refresh()*/,
@@ -2433,10 +2469,19 @@
2433 ev.stopPropagation();
2434 Chat.setCurrentView(Chat.e.viewMessages);
2435 return false;
2436 }, false);
2437 })()/*search view setup*/;
 
 
 
 
 
 
 
 
 
2438
2439 /** Callback for poll() to inject new content into the page. jx ==
2440 the response from /chat-poll. If atEnd is true, the message is
2441 appended to the end of the chat list (for loading older
2442 messages), else the beginning (the default). */
2443
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -142,10 +142,13 @@
142 loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */,
143 inputArea: E1("#chat-input-area"),
144 inputLineWrapper: E1('#chat-input-line-wrapper'),
145 fileSelectWrapper: E1('#chat-input-file-area'),
146 viewMessages: E1('#chat-messages-wrapper'),
147 viewZoom: E1('#chat-zoom'),
148 zoomContent: E1('#chat-zoom-content'),
149 zoomMarker: E1('#chat-zoom-marker'),
150 btnSubmit: E1('#chat-button-submit'),
151 btnAttach: E1('#chat-button-attach'),
152 inputX: E1('#chat-input-field-x'),
153 input1: E1('#chat-input-field-single'),
154 inputM: E1('#chat-input-field-multi'),
@@ -590,10 +593,14 @@
593 all other elements in that list. Returns e.
594 */
595 setCurrentView: function(e){
596 if(e===this.e.currentView){
597 return e;
598 }
599 if( e!==this.e.viewZoom && this.e.zoomedMsg ){
600 this.zoomMessage(null, e);
601 return this.e.currentView;
602 }
603 this.e.views.forEach(function(E){
604 if(e!==E) D.addClass(E,'hidden');
605 });
606 this.e.currentView = e;
@@ -600,10 +607,33 @@
607 if(this.e.currentView.$beforeShow) this.e.currentView.$beforeShow();
608 D.removeClass(e,'hidden');
609 this.animate(this.e.currentView, 'anim-fade-in-fast');
610 return this.e.currentView;
611 },
612
613 /**
614 Makes message element eMsg the content of this.e.viewZoom.
615 */
616 zoomMessage: function(eMsg,nextView){
617 const marker = this.e.zoomMarker;
618 if( !eMsg || eMsg===this.e.zoomedMsg ){
619 if( this.e.zoomedMsg ){
620 marker.parentNode.insertBefore(this.e.zoomedMsg, marker);
621 delete this.e.zoomedMsg;
622 }
623 this.setCurrentView(nextView || this.e.viewMessages);
624 return;
625 }
626 console.log("zoom message",eMsg);
627 if( this.e.zoomedMsg ){
628 marker.parentNode.insertBefore(this.e.zoomedMsg, marker);
629 }
630 this.e.viewMessages.insertBefore(marker, eMsg);
631 this.e.zoomContent.appendChild(eMsg);
632 this.e.zoomedMsg = eMsg;
633 this.setCurrentView(this.e.viewZoom);
634 },
635 /**
636 Updates the "active user list" view if we are not currently
637 batch-loading messages and if the active user list UI element
638 is active.
639 */
@@ -1349,15 +1379,16 @@
1379 has since been resolved by emiting a stricter
1380 format. */
1381 // Date doesn't work, so dumb it down...
1382 D.append(this.e, D.append(D.span(), eMsg.dataset.timestamp," zulu"));
1383 }
1384 const toolbar = D.addClass(D.div(), 'toolbar', 'hide-in-zoom');
1385 D.append(this.e, toolbar);
1386 const self = this;
1387
1388 const btnDeleteLocal = D.button("Delete locally");
1389 D.append(toolbar, btnDeleteLocal);
 
1390 btnDeleteLocal.addEventListener('click', function(){
1391 self.hide();
1392 Chat.deleteMessageElem(eMsg)
1393 });
1394 if( eMsg.classList.contains('notification') ){
@@ -1381,11 +1412,11 @@
1412 self.hide();
1413 Chat.deleteMessage(eMsg);
1414 }
1415 });
1416 }
1417 const toolbar3 = D.addClass(D.div(), 'toolbar', 'hide-in-zoom');
1418 D.append(this.e, toolbar3);
1419 D.append(toolbar3, D.button(
1420 "Locally remove all previous messages",
1421 function(){
1422 self.hide();
@@ -1444,10 +1475,15 @@
1475 })
1476 )
1477 );
1478 }/*jump-to button*/
1479 }
1480 const btnZoom = D.button("Zoom");
1481 D.append(toolbar2, btnZoom);
1482 btnZoom.addEventListener('click', function(){
1483 Chat.zoomMessage(eMsg);
1484 });
1485 const tab = eMsg.querySelector('.message-widget-tab');
1486 D.append(tab, this.e);
1487 D.removeClass(this.e, 'hidden');
1488 Chat.animate(this.e, 'anim-fade-in-fast');
1489 }/*refresh()*/,
@@ -2433,10 +2469,19 @@
2469 ev.stopPropagation();
2470 Chat.setCurrentView(Chat.e.viewMessages);
2471 return false;
2472 }, false);
2473 })()/*search view setup*/;
2474
2475 (function(){/*Set up the zoom view */
2476 Chat.e.viewZoom.querySelector('button.action-close').addEventListener('click', function(ev){
2477 ev.preventDefault();
2478 ev.stopPropagation();
2479 Chat.zoomMessage(null);
2480 return false;
2481 }, false);
2482 })()/*zoom view setup*/;
2483
2484 /** Callback for poll() to inject new content into the page. jx ==
2485 the response from /chat-poll. If atEnd is true, the message is
2486 appended to the end of the chat list (for loading older
2487 messages), else the beginning (the default). */
2488
--- src/style.chat.css
+++ src/style.chat.css
@@ -462,20 +462,42 @@
462462
Note that setting flex shrink to 0 breaks/disables scrolling!*/;
463463
margin-bottom: 0.2em;
464464
}
465465
body.chat #chat-config,
466466
body.chat #chat-search,
467
-body.chat #chat-preview {
467
+body.chat #chat-preview,
468
+body.chat #chat-zoom {
468469
/* /chat configuration widget */
469470
display: flex;
470471
flex-direction: column;
471472
overflow: auto;
472473
padding: 0;
473474
margin: 0;
474475
align-items: stretch;
475476
min-height: 6em;
476477
}
478
+body.chat #chat-zoom {
479
+ justify-content: space-between;
480
+}
481
+body.chat #chat-zoom-content {
482
+ display: flex;
483
+ overflow: auto;
484
+}
485
+body.chat #chat-zoom-content > .message-widget {
486
+ flex-grow: 1;
487
+}
488
+body.chat #chat-zoom-content > .message-widget > .message-widget-content {
489
+ width: 99%;
490
+}
491
+body.chat #chat-zoom-content > .message-widget .toolbar.hide-in-zoom {
492
+ /* The various Delete buttons misinteract with zoom mode's moving-around
493
+ of message widgets, so hide them in zoom mode. */
494
+ position: absolute !important;
495
+ opacity: 0 !important;
496
+ pointer-events: none !important;
497
+ display: none !important;
498
+}
477499
body.chat #chat-config #chat-config-options {
478500
/* /chat config options go here */
479501
flex: 1 1 auto;
480502
display: flex;
481503
flex-direction: column;
482504
--- src/style.chat.css
+++ src/style.chat.css
@@ -462,20 +462,42 @@
462 Note that setting flex shrink to 0 breaks/disables scrolling!*/;
463 margin-bottom: 0.2em;
464 }
465 body.chat #chat-config,
466 body.chat #chat-search,
467 body.chat #chat-preview {
 
468 /* /chat configuration widget */
469 display: flex;
470 flex-direction: column;
471 overflow: auto;
472 padding: 0;
473 margin: 0;
474 align-items: stretch;
475 min-height: 6em;
476 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
477 body.chat #chat-config #chat-config-options {
478 /* /chat config options go here */
479 flex: 1 1 auto;
480 display: flex;
481 flex-direction: column;
482
--- src/style.chat.css
+++ src/style.chat.css
@@ -462,20 +462,42 @@
462 Note that setting flex shrink to 0 breaks/disables scrolling!*/;
463 margin-bottom: 0.2em;
464 }
465 body.chat #chat-config,
466 body.chat #chat-search,
467 body.chat #chat-preview,
468 body.chat #chat-zoom {
469 /* /chat configuration widget */
470 display: flex;
471 flex-direction: column;
472 overflow: auto;
473 padding: 0;
474 margin: 0;
475 align-items: stretch;
476 min-height: 6em;
477 }
478 body.chat #chat-zoom {
479 justify-content: space-between;
480 }
481 body.chat #chat-zoom-content {
482 display: flex;
483 overflow: auto;
484 }
485 body.chat #chat-zoom-content > .message-widget {
486 flex-grow: 1;
487 }
488 body.chat #chat-zoom-content > .message-widget > .message-widget-content {
489 width: 99%;
490 }
491 body.chat #chat-zoom-content > .message-widget .toolbar.hide-in-zoom {
492 /* The various Delete buttons misinteract with zoom mode's moving-around
493 of message widgets, so hide them in zoom mode. */
494 position: absolute !important;
495 opacity: 0 !important;
496 pointer-events: none !important;
497 display: none !important;
498 }
499 body.chat #chat-config #chat-config-options {
500 /* /chat config options go here */
501 flex: 1 1 auto;
502 display: flex;
503 flex-direction: column;
504
+2 -2
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -494,14 +494,14 @@
494494
@ html "<td class='tktDspValue' colspan='3'>Deleted</td></tr>\n"
495495
@ }
496496
@ }
497497
@
498498
@ if {[capexpr {n}]} {
499
-@ submenu link "Copy Ticket" /tktnew/$tkt_uuid
499
+@ submenu link "Copy Ticket" $baseurl/tktnew/$tkt_uuid
500500
@ }
501501
@ if {[capexpr {nk}]} {
502
-@ submenu link "Edit Wiki" /wikiedit?name=ticket/$tkt_uuid
502
+@ submenu link "Edit Wiki" $baseurl/wikiedit?name=ticket/$tkt_uuid
503503
@ }
504504
@ </th1>
505505
@ <tr><td class="tktDspLabel">Title:</td>
506506
@ <td class="tktDspValue" colspan="3">
507507
@ $<title>
508508
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -494,14 +494,14 @@
494 @ html "<td class='tktDspValue' colspan='3'>Deleted</td></tr>\n"
495 @ }
496 @ }
497 @
498 @ if {[capexpr {n}]} {
499 @ submenu link "Copy Ticket" /tktnew/$tkt_uuid
500 @ }
501 @ if {[capexpr {nk}]} {
502 @ submenu link "Edit Wiki" /wikiedit?name=ticket/$tkt_uuid
503 @ }
504 @ </th1>
505 @ <tr><td class="tktDspLabel">Title:</td>
506 @ <td class="tktDspValue" colspan="3">
507 @ $<title>
508
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -494,14 +494,14 @@
494 @ html "<td class='tktDspValue' colspan='3'>Deleted</td></tr>\n"
495 @ }
496 @ }
497 @
498 @ if {[capexpr {n}]} {
499 @ submenu link "Copy Ticket" $baseurl/tktnew/$tkt_uuid
500 @ }
501 @ if {[capexpr {nk}]} {
502 @ submenu link "Edit Wiki" $baseurl/wikiedit?name=ticket/$tkt_uuid
503 @ }
504 @ </th1>
505 @ <tr><td class="tktDspLabel">Title:</td>
506 @ <td class="tktDspValue" colspan="3">
507 @ $<title>
508
+72 -20
--- src/xsystem.c
+++ src/xsystem.c
@@ -88,10 +88,12 @@
8888
#define LS_MTIME 0x004 /* -t Sort by mtime, newest first */
8989
#define LS_SIZE 0x008 /* -S Sort by size, largest first */
9090
#define LS_COMMA 0x010 /* -m Comma-separated list */
9191
#define LS_DIRONLY 0x020 /* -d Show just directory name, not content */
9292
#define LS_ALL 0x040 /* -a Show all entries */
93
+#define LS_COLOR 0x080 /* Colorize the output */
94
+#define LS_COLUMNS 0x100 /* -C Split column output */
9395
9496
/* xWrite() callback from QRF
9597
*/
9698
static int xsystem_write(void *NotUsed, const char *zText, sqlite3_int64 n){
9799
fossil_puts(zText, 0, (int)n);
@@ -115,12 +117,13 @@
115117
int i;
116118
const char *zPrefix;
117119
switch( file_isdir(zName, ExtFILE) ){
118120
case 1: { /* A directory */
119121
if( (mFlags & LS_DIRONLY)==0 ){
122
+ int omitDots = (mFlags & LS_ALL)!=0 ? 2 : 1;
120123
azList = 0;
121
- nList = file_directory_list(zName, 0, (mFlags & LS_ALL)==0, 0, &azList);
124
+ nList = file_directory_list(zName, 0, omitDots, 0, &azList);
122125
zPrefix = fossil_strcmp(zName,".") ? zName : 0;
123126
break;
124127
}
125128
}
126129
case 2: { /* A file */
@@ -138,11 +141,22 @@
138141
for(i=0; i<nList; i++){
139142
char *zFile = zPrefix ? mprintf("%s/%s",zPrefix,azList[i]) : azList[i];
140143
int mode = file_mode(zFile, ExtFILE);
141144
sqlite3_int64 sz = file_size(zFile, ExtFILE);
142145
sqlite3_int64 mtime = file_mtime(zFile, ExtFILE);
143
- sqlite3_bind_text(pStmt, 1, zFile, -1, SQLITE_TRANSIENT);
146
+#ifdef _WIN32
147
+ if( (mFlags & LS_ALL)==0 ){
148
+ wchar_t *zMbcs = fossil_utf8_to_path(zFile, 1);
149
+ DWORD attr = GetFileAttributesW(zMbcs);
150
+ fossil_path_free(zMbcs);
151
+ if( attr & FILE_ATTRIBUTE_HIDDEN ){
152
+ if( zPrefix ) fossil_free(zFile);
153
+ continue;
154
+ }
155
+ }
156
+#endif
157
+ sqlite3_bind_text(pStmt, 1, azList[i], -1, SQLITE_TRANSIENT);
144158
sqlite3_bind_int64(pStmt, 2, mtime);
145159
sqlite3_bind_int64(pStmt, 3, sz);
146160
sqlite3_bind_int(pStmt, 4, mode);
147161
sqlite3_bind_int64(pStmt, 5, strlen(zFile));
148162
/* TODO: wcwidth()------^^^^^^ */
@@ -173,11 +187,11 @@
173187
if( mFlags & LS_REVERSE ) i += 3;
174188
return zSortTypes[i];
175189
}
176190
177191
/*
178
-** colorize_fn(fn,mode)
192
+** color(fn,mode)
179193
**
180194
** SQL function to colorize a filename based on its mode.
181195
*/
182196
static void colorNameFunc(
183197
sqlite3_context *context,
@@ -201,12 +215,22 @@
201215
}
202216
sqlite3_str_appendall(pOut, zName);
203217
if( (iMode & 040100)!=0 ){
204218
sqlite3_str_appendall(pOut, "\033[0m");
205219
}
206
- sqlite3_result_text(context, sqlite3_str_finish(pOut), -1, sqlite3_free);
220
+ sqlite3_result_text(context, sqlite3_str_value(pOut), -1, SQLITE_TRANSIENT);
221
+ sqlite3_str_free(pOut);
222
+}
223
+/* Alternative implementation that does *not* introduce color */
224
+static void nocolorNameFunc(
225
+ sqlite3_context *context,
226
+ int argc,
227
+ sqlite3_value **argv
228
+){
229
+ sqlite3_result_value(context, argv[0]);
207230
}
231
+
208232
209233
210234
/*
211235
** Show ls output information for content in the LS table
212236
*/
@@ -213,11 +237,15 @@
213237
static void xsystem_ls_render(
214238
sqlite3 *db,
215239
int mFlags
216240
){
217241
sqlite3_stmt *pStmt;
218
- sqlite3_create_function(db, "color", 2, SQLITE_UTF8, 0, colorNameFunc,0,0);
242
+ if( mFlags & LS_COLOR ){
243
+ sqlite3_create_function(db, "color",2,SQLITE_UTF8,0,colorNameFunc,0,0);
244
+ }else{
245
+ sqlite3_create_function(db, "color",2,SQLITE_UTF8,0,nocolorNameFunc,0,0);
246
+ }
219247
if( (mFlags & LS_LONG)!=0 ){
220248
/* Long mode */
221249
char *zSql;
222250
int szSz = 8;
223251
sqlite3_prepare_v2(db, "SELECT length(max(size)) FROM ls", -1, &pStmt, 0);
@@ -295,14 +323,16 @@
295323
char *zSql;
296324
memset(&spec, 0, sizeof(spec));
297325
spec.iVersion = 1;
298326
spec.xWrite = xsystem_write;
299327
spec.eStyle = QRF_STYLE_Column;
300
- spec.bSplitColumn = QRF_Yes;
301328
spec.bTitles = QRF_No;
302329
spec.eEsc = QRF_No;
303
- spec.nScreenWidth = terminal_get_width(80);
330
+ if( mFlags & LS_COLUMNS ){
331
+ spec.nScreenWidth = terminal_get_width(80);
332
+ spec.bSplitColumn = QRF_Yes;
333
+ }
304334
zSql = mprintf("SELECT color(fn,mode) FROM ls ORDER BY %s",
305335
xsystem_ls_orderby(mFlags));
306336
sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
307337
fossil_free(zSql);
308338
sqlite3_format_query_result(pStmt, &spec, 0);
@@ -313,10 +343,12 @@
313343
314344
/* List files "ls"
315345
** Options:
316346
**
317347
** -a Show files that begin with "."
348
+** -C List by colums
349
+** --color=WHEN Colorize output?
318350
** -d Show just directory names, not content
319351
** -l Long listing
320352
** -m Comma-separated list
321353
** -r Reverse sort
322354
** -S Sort by size, largest first
@@ -327,10 +359,11 @@
327359
sqlite3 *db;
328360
sqlite3_stmt *pStmt = 0;
329361
int mFlags = 0;
330362
int nFile = 0;
331363
int nDir = 0;
364
+ int bAutoColor = 1;
332365
int needBlankLine = 0;
333366
rc = sqlite3_open(":memory:", &db);
334367
if( rc || db==0 ){
335368
fossil_fatal("Cannot open in-memory database");
336369
}
@@ -341,22 +374,35 @@
341374
fossil_fatal("Cannot prepare INSERT statement");
342375
}
343376
for(i=1; i<argc; i++){
344377
const char *z = argv[i];
345378
if( z[0]=='-' ){
346
- int k;
347
- for(k=1; z[k]; k++){
348
- switch( z[k] ){
349
- case 'a': mFlags |= LS_ALL; break;
350
- case 'd': mFlags |= LS_DIRONLY; break;
351
- case 'l': mFlags |= LS_LONG; break;
352
- case 'm': mFlags |= LS_COMMA; break;
353
- case 'r': mFlags |= LS_REVERSE; break;
354
- case 'S': mFlags |= LS_SIZE; break;
355
- case 't': mFlags |= LS_MTIME; break;
356
- default: {
357
- fossil_fatal("unknown option: -%c", z[k]);
379
+ if( z[1]=='-' ){
380
+ if( strncmp(z,"--color",7)==0 ){
381
+ if( z[7]==0 || strcmp(&z[7],"=always")==0 ){
382
+ mFlags |= LS_COLOR;
383
+ }else if( strcmp(&z[7],"=never")==0 ){
384
+ bAutoColor = 0;
385
+ }
386
+ }else{
387
+ fossil_fatal("unknown option: %s", z);
388
+ }
389
+ }else{
390
+ int k;
391
+ for(k=1; z[k]; k++){
392
+ switch( z[k] ){
393
+ case 'a': mFlags |= LS_ALL; break;
394
+ case 'd': mFlags |= LS_DIRONLY; break;
395
+ case 'l': mFlags |= LS_LONG; break;
396
+ case 'm': mFlags |= LS_COMMA; break;
397
+ case 'r': mFlags |= LS_REVERSE; break;
398
+ case 'S': mFlags |= LS_SIZE; break;
399
+ case 't': mFlags |= LS_MTIME; break;
400
+ case 'C': mFlags |= LS_COLUMNS; break;
401
+ default: {
402
+ fossil_fatal("unknown option: -%c", z[k]);
403
+ }
358404
}
359405
}
360406
}
361407
}else{
362408
if( (mFlags & LS_DIRONLY)==0 && file_isdir(z, ExtFILE)==1 ){
@@ -364,10 +410,14 @@
364410
}else{
365411
nFile++;
366412
xsystem_ls_insert(pStmt, z, mFlags);
367413
}
368414
}
415
+ }
416
+ if( fossil_isatty(1) ){
417
+ if( bAutoColor ) mFlags |= LS_COLOR;
418
+ mFlags |= LS_COLUMNS;
369419
}
370420
if( nFile>0 ){
371421
xsystem_ls_render(db, mFlags);
372422
needBlankLine = 1;
373423
}else if( nDir==0 ){
@@ -407,16 +457,18 @@
407457
},
408458
{ "ls", xsystem_ls,
409459
"[OPTIONS] [PATH] ...\n"
410460
"Options:\n"
411461
" -a Show files that begin with '.'\n"
462
+ " -C Split columns\n"
412463
" -d Show just directory names, not content\n"
413464
" -l Long listing\n"
414465
" -m Comma-separated list\n"
415466
" -r Reverse sort order\n"
416467
" -S Sort by size, largest first\n"
417468
" -t Sort by mtime, newest first\n"
469
+ " --color[=WHEN] Colorize output?\n"
418470
},
419471
{ "pwd", xsystem_pwd,
420472
"\n"
421473
"Show the Present Working Directory name\n"
422474
},
@@ -435,11 +487,11 @@
435487
/*
436488
** COMMAND: system
437489
**
438490
** Usage: %fossil system COMMAND ARGS...
439491
**
440
-** Often abbreviated as just "fossil sys", this command provides primative,
492
+** Often abbreviated as just "fossil sys", this command provides primitive,
441493
** low-level unix-like commands for use on systems that lack those commands
442494
** natively.
443495
**
444496
** Type "fossil sys help" for a list of available commands.
445497
**
446498
--- src/xsystem.c
+++ src/xsystem.c
@@ -88,10 +88,12 @@
88 #define LS_MTIME 0x004 /* -t Sort by mtime, newest first */
89 #define LS_SIZE 0x008 /* -S Sort by size, largest first */
90 #define LS_COMMA 0x010 /* -m Comma-separated list */
91 #define LS_DIRONLY 0x020 /* -d Show just directory name, not content */
92 #define LS_ALL 0x040 /* -a Show all entries */
 
 
93
94 /* xWrite() callback from QRF
95 */
96 static int xsystem_write(void *NotUsed, const char *zText, sqlite3_int64 n){
97 fossil_puts(zText, 0, (int)n);
@@ -115,12 +117,13 @@
115 int i;
116 const char *zPrefix;
117 switch( file_isdir(zName, ExtFILE) ){
118 case 1: { /* A directory */
119 if( (mFlags & LS_DIRONLY)==0 ){
 
120 azList = 0;
121 nList = file_directory_list(zName, 0, (mFlags & LS_ALL)==0, 0, &azList);
122 zPrefix = fossil_strcmp(zName,".") ? zName : 0;
123 break;
124 }
125 }
126 case 2: { /* A file */
@@ -138,11 +141,22 @@
138 for(i=0; i<nList; i++){
139 char *zFile = zPrefix ? mprintf("%s/%s",zPrefix,azList[i]) : azList[i];
140 int mode = file_mode(zFile, ExtFILE);
141 sqlite3_int64 sz = file_size(zFile, ExtFILE);
142 sqlite3_int64 mtime = file_mtime(zFile, ExtFILE);
143 sqlite3_bind_text(pStmt, 1, zFile, -1, SQLITE_TRANSIENT);
 
 
 
 
 
 
 
 
 
 
 
144 sqlite3_bind_int64(pStmt, 2, mtime);
145 sqlite3_bind_int64(pStmt, 3, sz);
146 sqlite3_bind_int(pStmt, 4, mode);
147 sqlite3_bind_int64(pStmt, 5, strlen(zFile));
148 /* TODO: wcwidth()------^^^^^^ */
@@ -173,11 +187,11 @@
173 if( mFlags & LS_REVERSE ) i += 3;
174 return zSortTypes[i];
175 }
176
177 /*
178 ** colorize_fn(fn,mode)
179 **
180 ** SQL function to colorize a filename based on its mode.
181 */
182 static void colorNameFunc(
183 sqlite3_context *context,
@@ -201,12 +215,22 @@
201 }
202 sqlite3_str_appendall(pOut, zName);
203 if( (iMode & 040100)!=0 ){
204 sqlite3_str_appendall(pOut, "\033[0m");
205 }
206 sqlite3_result_text(context, sqlite3_str_finish(pOut), -1, sqlite3_free);
 
 
 
 
 
 
 
 
 
207 }
 
208
209
210 /*
211 ** Show ls output information for content in the LS table
212 */
@@ -213,11 +237,15 @@
213 static void xsystem_ls_render(
214 sqlite3 *db,
215 int mFlags
216 ){
217 sqlite3_stmt *pStmt;
218 sqlite3_create_function(db, "color", 2, SQLITE_UTF8, 0, colorNameFunc,0,0);
 
 
 
 
219 if( (mFlags & LS_LONG)!=0 ){
220 /* Long mode */
221 char *zSql;
222 int szSz = 8;
223 sqlite3_prepare_v2(db, "SELECT length(max(size)) FROM ls", -1, &pStmt, 0);
@@ -295,14 +323,16 @@
295 char *zSql;
296 memset(&spec, 0, sizeof(spec));
297 spec.iVersion = 1;
298 spec.xWrite = xsystem_write;
299 spec.eStyle = QRF_STYLE_Column;
300 spec.bSplitColumn = QRF_Yes;
301 spec.bTitles = QRF_No;
302 spec.eEsc = QRF_No;
303 spec.nScreenWidth = terminal_get_width(80);
 
 
 
304 zSql = mprintf("SELECT color(fn,mode) FROM ls ORDER BY %s",
305 xsystem_ls_orderby(mFlags));
306 sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
307 fossil_free(zSql);
308 sqlite3_format_query_result(pStmt, &spec, 0);
@@ -313,10 +343,12 @@
313
314 /* List files "ls"
315 ** Options:
316 **
317 ** -a Show files that begin with "."
 
 
318 ** -d Show just directory names, not content
319 ** -l Long listing
320 ** -m Comma-separated list
321 ** -r Reverse sort
322 ** -S Sort by size, largest first
@@ -327,10 +359,11 @@
327 sqlite3 *db;
328 sqlite3_stmt *pStmt = 0;
329 int mFlags = 0;
330 int nFile = 0;
331 int nDir = 0;
 
332 int needBlankLine = 0;
333 rc = sqlite3_open(":memory:", &db);
334 if( rc || db==0 ){
335 fossil_fatal("Cannot open in-memory database");
336 }
@@ -341,22 +374,35 @@
341 fossil_fatal("Cannot prepare INSERT statement");
342 }
343 for(i=1; i<argc; i++){
344 const char *z = argv[i];
345 if( z[0]=='-' ){
346 int k;
347 for(k=1; z[k]; k++){
348 switch( z[k] ){
349 case 'a': mFlags |= LS_ALL; break;
350 case 'd': mFlags |= LS_DIRONLY; break;
351 case 'l': mFlags |= LS_LONG; break;
352 case 'm': mFlags |= LS_COMMA; break;
353 case 'r': mFlags |= LS_REVERSE; break;
354 case 'S': mFlags |= LS_SIZE; break;
355 case 't': mFlags |= LS_MTIME; break;
356 default: {
357 fossil_fatal("unknown option: -%c", z[k]);
 
 
 
 
 
 
 
 
 
 
 
 
 
358 }
359 }
360 }
361 }else{
362 if( (mFlags & LS_DIRONLY)==0 && file_isdir(z, ExtFILE)==1 ){
@@ -364,10 +410,14 @@
364 }else{
365 nFile++;
366 xsystem_ls_insert(pStmt, z, mFlags);
367 }
368 }
 
 
 
 
369 }
370 if( nFile>0 ){
371 xsystem_ls_render(db, mFlags);
372 needBlankLine = 1;
373 }else if( nDir==0 ){
@@ -407,16 +457,18 @@
407 },
408 { "ls", xsystem_ls,
409 "[OPTIONS] [PATH] ...\n"
410 "Options:\n"
411 " -a Show files that begin with '.'\n"
 
412 " -d Show just directory names, not content\n"
413 " -l Long listing\n"
414 " -m Comma-separated list\n"
415 " -r Reverse sort order\n"
416 " -S Sort by size, largest first\n"
417 " -t Sort by mtime, newest first\n"
 
418 },
419 { "pwd", xsystem_pwd,
420 "\n"
421 "Show the Present Working Directory name\n"
422 },
@@ -435,11 +487,11 @@
435 /*
436 ** COMMAND: system
437 **
438 ** Usage: %fossil system COMMAND ARGS...
439 **
440 ** Often abbreviated as just "fossil sys", this command provides primative,
441 ** low-level unix-like commands for use on systems that lack those commands
442 ** natively.
443 **
444 ** Type "fossil sys help" for a list of available commands.
445 **
446
--- src/xsystem.c
+++ src/xsystem.c
@@ -88,10 +88,12 @@
88 #define LS_MTIME 0x004 /* -t Sort by mtime, newest first */
89 #define LS_SIZE 0x008 /* -S Sort by size, largest first */
90 #define LS_COMMA 0x010 /* -m Comma-separated list */
91 #define LS_DIRONLY 0x020 /* -d Show just directory name, not content */
92 #define LS_ALL 0x040 /* -a Show all entries */
93 #define LS_COLOR 0x080 /* Colorize the output */
94 #define LS_COLUMNS 0x100 /* -C Split column output */
95
96 /* xWrite() callback from QRF
97 */
98 static int xsystem_write(void *NotUsed, const char *zText, sqlite3_int64 n){
99 fossil_puts(zText, 0, (int)n);
@@ -115,12 +117,13 @@
117 int i;
118 const char *zPrefix;
119 switch( file_isdir(zName, ExtFILE) ){
120 case 1: { /* A directory */
121 if( (mFlags & LS_DIRONLY)==0 ){
122 int omitDots = (mFlags & LS_ALL)!=0 ? 2 : 1;
123 azList = 0;
124 nList = file_directory_list(zName, 0, omitDots, 0, &azList);
125 zPrefix = fossil_strcmp(zName,".") ? zName : 0;
126 break;
127 }
128 }
129 case 2: { /* A file */
@@ -138,11 +141,22 @@
141 for(i=0; i<nList; i++){
142 char *zFile = zPrefix ? mprintf("%s/%s",zPrefix,azList[i]) : azList[i];
143 int mode = file_mode(zFile, ExtFILE);
144 sqlite3_int64 sz = file_size(zFile, ExtFILE);
145 sqlite3_int64 mtime = file_mtime(zFile, ExtFILE);
146 #ifdef _WIN32
147 if( (mFlags & LS_ALL)==0 ){
148 wchar_t *zMbcs = fossil_utf8_to_path(zFile, 1);
149 DWORD attr = GetFileAttributesW(zMbcs);
150 fossil_path_free(zMbcs);
151 if( attr & FILE_ATTRIBUTE_HIDDEN ){
152 if( zPrefix ) fossil_free(zFile);
153 continue;
154 }
155 }
156 #endif
157 sqlite3_bind_text(pStmt, 1, azList[i], -1, SQLITE_TRANSIENT);
158 sqlite3_bind_int64(pStmt, 2, mtime);
159 sqlite3_bind_int64(pStmt, 3, sz);
160 sqlite3_bind_int(pStmt, 4, mode);
161 sqlite3_bind_int64(pStmt, 5, strlen(zFile));
162 /* TODO: wcwidth()------^^^^^^ */
@@ -173,11 +187,11 @@
187 if( mFlags & LS_REVERSE ) i += 3;
188 return zSortTypes[i];
189 }
190
191 /*
192 ** color(fn,mode)
193 **
194 ** SQL function to colorize a filename based on its mode.
195 */
196 static void colorNameFunc(
197 sqlite3_context *context,
@@ -201,12 +215,22 @@
215 }
216 sqlite3_str_appendall(pOut, zName);
217 if( (iMode & 040100)!=0 ){
218 sqlite3_str_appendall(pOut, "\033[0m");
219 }
220 sqlite3_result_text(context, sqlite3_str_value(pOut), -1, SQLITE_TRANSIENT);
221 sqlite3_str_free(pOut);
222 }
223 /* Alternative implementation that does *not* introduce color */
224 static void nocolorNameFunc(
225 sqlite3_context *context,
226 int argc,
227 sqlite3_value **argv
228 ){
229 sqlite3_result_value(context, argv[0]);
230 }
231
232
233
234 /*
235 ** Show ls output information for content in the LS table
236 */
@@ -213,11 +237,15 @@
237 static void xsystem_ls_render(
238 sqlite3 *db,
239 int mFlags
240 ){
241 sqlite3_stmt *pStmt;
242 if( mFlags & LS_COLOR ){
243 sqlite3_create_function(db, "color",2,SQLITE_UTF8,0,colorNameFunc,0,0);
244 }else{
245 sqlite3_create_function(db, "color",2,SQLITE_UTF8,0,nocolorNameFunc,0,0);
246 }
247 if( (mFlags & LS_LONG)!=0 ){
248 /* Long mode */
249 char *zSql;
250 int szSz = 8;
251 sqlite3_prepare_v2(db, "SELECT length(max(size)) FROM ls", -1, &pStmt, 0);
@@ -295,14 +323,16 @@
323 char *zSql;
324 memset(&spec, 0, sizeof(spec));
325 spec.iVersion = 1;
326 spec.xWrite = xsystem_write;
327 spec.eStyle = QRF_STYLE_Column;
 
328 spec.bTitles = QRF_No;
329 spec.eEsc = QRF_No;
330 if( mFlags & LS_COLUMNS ){
331 spec.nScreenWidth = terminal_get_width(80);
332 spec.bSplitColumn = QRF_Yes;
333 }
334 zSql = mprintf("SELECT color(fn,mode) FROM ls ORDER BY %s",
335 xsystem_ls_orderby(mFlags));
336 sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
337 fossil_free(zSql);
338 sqlite3_format_query_result(pStmt, &spec, 0);
@@ -313,10 +343,12 @@
343
344 /* List files "ls"
345 ** Options:
346 **
347 ** -a Show files that begin with "."
348 ** -C List by colums
349 ** --color=WHEN Colorize output?
350 ** -d Show just directory names, not content
351 ** -l Long listing
352 ** -m Comma-separated list
353 ** -r Reverse sort
354 ** -S Sort by size, largest first
@@ -327,10 +359,11 @@
359 sqlite3 *db;
360 sqlite3_stmt *pStmt = 0;
361 int mFlags = 0;
362 int nFile = 0;
363 int nDir = 0;
364 int bAutoColor = 1;
365 int needBlankLine = 0;
366 rc = sqlite3_open(":memory:", &db);
367 if( rc || db==0 ){
368 fossil_fatal("Cannot open in-memory database");
369 }
@@ -341,22 +374,35 @@
374 fossil_fatal("Cannot prepare INSERT statement");
375 }
376 for(i=1; i<argc; i++){
377 const char *z = argv[i];
378 if( z[0]=='-' ){
379 if( z[1]=='-' ){
380 if( strncmp(z,"--color",7)==0 ){
381 if( z[7]==0 || strcmp(&z[7],"=always")==0 ){
382 mFlags |= LS_COLOR;
383 }else if( strcmp(&z[7],"=never")==0 ){
384 bAutoColor = 0;
385 }
386 }else{
387 fossil_fatal("unknown option: %s", z);
388 }
389 }else{
390 int k;
391 for(k=1; z[k]; k++){
392 switch( z[k] ){
393 case 'a': mFlags |= LS_ALL; break;
394 case 'd': mFlags |= LS_DIRONLY; break;
395 case 'l': mFlags |= LS_LONG; break;
396 case 'm': mFlags |= LS_COMMA; break;
397 case 'r': mFlags |= LS_REVERSE; break;
398 case 'S': mFlags |= LS_SIZE; break;
399 case 't': mFlags |= LS_MTIME; break;
400 case 'C': mFlags |= LS_COLUMNS; break;
401 default: {
402 fossil_fatal("unknown option: -%c", z[k]);
403 }
404 }
405 }
406 }
407 }else{
408 if( (mFlags & LS_DIRONLY)==0 && file_isdir(z, ExtFILE)==1 ){
@@ -364,10 +410,14 @@
410 }else{
411 nFile++;
412 xsystem_ls_insert(pStmt, z, mFlags);
413 }
414 }
415 }
416 if( fossil_isatty(1) ){
417 if( bAutoColor ) mFlags |= LS_COLOR;
418 mFlags |= LS_COLUMNS;
419 }
420 if( nFile>0 ){
421 xsystem_ls_render(db, mFlags);
422 needBlankLine = 1;
423 }else if( nDir==0 ){
@@ -407,16 +457,18 @@
457 },
458 { "ls", xsystem_ls,
459 "[OPTIONS] [PATH] ...\n"
460 "Options:\n"
461 " -a Show files that begin with '.'\n"
462 " -C Split columns\n"
463 " -d Show just directory names, not content\n"
464 " -l Long listing\n"
465 " -m Comma-separated list\n"
466 " -r Reverse sort order\n"
467 " -S Sort by size, largest first\n"
468 " -t Sort by mtime, newest first\n"
469 " --color[=WHEN] Colorize output?\n"
470 },
471 { "pwd", xsystem_pwd,
472 "\n"
473 "Show the Present Working Directory name\n"
474 },
@@ -435,11 +487,11 @@
487 /*
488 ** COMMAND: system
489 **
490 ** Usage: %fossil system COMMAND ARGS...
491 **
492 ** Often abbreviated as just "fossil sys", this command provides primitive,
493 ** low-level unix-like commands for use on systems that lack those commands
494 ** natively.
495 **
496 ** Type "fossil sys help" for a list of available commands.
497 **
498
--- www/changes.wiki
+++ www/changes.wiki
@@ -48,10 +48,12 @@
4848
"<tt>-u|--for-user</tt>" option.
4949
<li> The [/help/open|open command]'s new "<tt>--reopen REPOFILE</tt>" flag
5050
can be used to fix a checkout after moving its repository file.
5151
<li> Update internal Unicode character tables, used in regular expression
5252
handling, from version 13 to 17.
53
+ <li> Add a zoom-message option to [/help/www/chat|/chat] to better support
54
+ pikchr diagrams.
5355
</ol>
5456
5557
<h2 id='v2_27'>Changes for version 2.27 (2025-09-30)</h2><ol>
5658
<li> Close a potential Denial-of-Service attack against any public-facing Fossil
5759
server involving exponential behavior in Fossil's regexp implementation.
5860
--- www/changes.wiki
+++ www/changes.wiki
@@ -48,10 +48,12 @@
48 "<tt>-u|--for-user</tt>" option.
49 <li> The [/help/open|open command]'s new "<tt>--reopen REPOFILE</tt>" flag
50 can be used to fix a checkout after moving its repository file.
51 <li> Update internal Unicode character tables, used in regular expression
52 handling, from version 13 to 17.
 
 
53 </ol>
54
55 <h2 id='v2_27'>Changes for version 2.27 (2025-09-30)</h2><ol>
56 <li> Close a potential Denial-of-Service attack against any public-facing Fossil
57 server involving exponential behavior in Fossil's regexp implementation.
58
--- www/changes.wiki
+++ www/changes.wiki
@@ -48,10 +48,12 @@
48 "<tt>-u|--for-user</tt>" option.
49 <li> The [/help/open|open command]'s new "<tt>--reopen REPOFILE</tt>" flag
50 can be used to fix a checkout after moving its repository file.
51 <li> Update internal Unicode character tables, used in regular expression
52 handling, from version 13 to 17.
53 <li> Add a zoom-message option to [/help/www/chat|/chat] to better support
54 pikchr diagrams.
55 </ol>
56
57 <h2 id='v2_27'>Changes for version 2.27 (2025-09-30)</h2><ol>
58 <li> Close a potential Denial-of-Service attack against any public-facing Fossil
59 server involving exponential behavior in Fossil's regexp implementation.
60

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button