From: drh <> Date: Wed, 22 Oct 2025 18:09:54 +0000 (+0000) Subject: Code in place to do column formats. Compiles, but does not work. X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f32be465e83dec50ae82f68141424a0f6bc4cfbe;p=thirdparty%2Fsqlite.git Code in place to do column formats. Compiles, but does not work. This is an incremental check-in. FossilOrigin-Name: a05e021354ff5868f75ac01d384c3aaaedb7c483dd8f9533f6ab9fbb66b5f077 --- diff --git a/ext/qrf/qrf.c b/ext/qrf/qrf.c index 5a020a6632..9e7fbf59c9 100644 --- a/ext/qrf/qrf.c +++ b/ext/qrf/qrf.c @@ -9,11 +9,12 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** Implementation of the Result-Format or "resfmt" utility library for SQLite. -** See the resfmt.md documentation for additional information. +** Implementation of the Result-Format or "qrf" utility library for SQLite. +** See the qrf.md documentation for additional information. */ #include "qrf.h" #include +#include /* ** Private state information. Subject to change from one release to the @@ -28,16 +29,17 @@ struct Qrf { int iErr; /* Error code */ int nCol; /* Number of output columns */ sqlite3_int64 nRow; /* Number of rows handled so far */ - sqlite3_qrf_spec spec; /* Copy of the original spec */ + int *actualWidth; /* Actual width of each column */ + sqlite3_qrf_spec spec; /* Copy of the original spec */ }; /* ** Set an error code and error message. */ -static void resfmtError( - Qrf *p, /* Query result state */ - int iCode, /* Error code */ - const char *zFormat, /* Message format (or NULL) */ +static void qrfError( + Qrf *p, /* Query result state */ + int iCode, /* Error code */ + const char *zFormat, /* Message format (or NULL) */ ... ){ p->iErr = iCode; @@ -56,15 +58,25 @@ static void resfmtError( /* ** Out-of-memory error. */ -static void resfmtOom(Qrf *p){ - resfmtError(p, SQLITE_NOMEM, "out of memory"); +static void qrfOom(Qrf *p){ + qrfError(p, SQLITE_NOMEM, "out of memory"); +} + +/* +** Reset the prepared statement. +*/ +static void qrfResetStmt(Qrf *p){ + int rc = sqlite3_reset(p->pStmt); + if( rc!=SQLITE_OK && p->iErr==SQLITE_OK ){ + qrfError(p, rc, "%s", sqlite3_errmsg(p->db)); + } } /* ** If xWrite is defined, send all content of pOut to xWrite and ** reset pOut. */ -static void resfmtWrite(Qrf *p){ +static void qrfWrite(Qrf *p){ int n; if( p->spec.xWrite && (n = sqlite3_str_length(p->pOut))>0 ){ p->spec.xWrite(p->spec.pWriteArg, @@ -74,6 +86,160 @@ static void resfmtWrite(Qrf *p){ } } +/* Lookup table to estimate the number of columns consumed by a Unicode +** character. +*/ +static const struct { + unsigned char w; /* Width of the character in columns */ + int iFirst; /* First character in a span having this width */ +} aQrfUWidth[] = { + /* {1, 0x00000}, */ + {0, 0x00300}, {1, 0x00370}, {0, 0x00483}, {1, 0x00487}, {0, 0x00488}, + {1, 0x0048a}, {0, 0x00591}, {1, 0x005be}, {0, 0x005bf}, {1, 0x005c0}, + {0, 0x005c1}, {1, 0x005c3}, {0, 0x005c4}, {1, 0x005c6}, {0, 0x005c7}, + {1, 0x005c8}, {0, 0x00600}, {1, 0x00604}, {0, 0x00610}, {1, 0x00616}, + {0, 0x0064b}, {1, 0x0065f}, {0, 0x00670}, {1, 0x00671}, {0, 0x006d6}, + {1, 0x006e5}, {0, 0x006e7}, {1, 0x006e9}, {0, 0x006ea}, {1, 0x006ee}, + {0, 0x0070f}, {1, 0x00710}, {0, 0x00711}, {1, 0x00712}, {0, 0x00730}, + {1, 0x0074b}, {0, 0x007a6}, {1, 0x007b1}, {0, 0x007eb}, {1, 0x007f4}, + {0, 0x00901}, {1, 0x00903}, {0, 0x0093c}, {1, 0x0093d}, {0, 0x00941}, + {1, 0x00949}, {0, 0x0094d}, {1, 0x0094e}, {0, 0x00951}, {1, 0x00955}, + {0, 0x00962}, {1, 0x00964}, {0, 0x00981}, {1, 0x00982}, {0, 0x009bc}, + {1, 0x009bd}, {0, 0x009c1}, {1, 0x009c5}, {0, 0x009cd}, {1, 0x009ce}, + {0, 0x009e2}, {1, 0x009e4}, {0, 0x00a01}, {1, 0x00a03}, {0, 0x00a3c}, + {1, 0x00a3d}, {0, 0x00a41}, {1, 0x00a43}, {0, 0x00a47}, {1, 0x00a49}, + {0, 0x00a4b}, {1, 0x00a4e}, {0, 0x00a70}, {1, 0x00a72}, {0, 0x00a81}, + {1, 0x00a83}, {0, 0x00abc}, {1, 0x00abd}, {0, 0x00ac1}, {1, 0x00ac6}, + {0, 0x00ac7}, {1, 0x00ac9}, {0, 0x00acd}, {1, 0x00ace}, {0, 0x00ae2}, + {1, 0x00ae4}, {0, 0x00b01}, {1, 0x00b02}, {0, 0x00b3c}, {1, 0x00b3d}, + {0, 0x00b3f}, {1, 0x00b40}, {0, 0x00b41}, {1, 0x00b44}, {0, 0x00b4d}, + {1, 0x00b4e}, {0, 0x00b56}, {1, 0x00b57}, {0, 0x00b82}, {1, 0x00b83}, + {0, 0x00bc0}, {1, 0x00bc1}, {0, 0x00bcd}, {1, 0x00bce}, {0, 0x00c3e}, + {1, 0x00c41}, {0, 0x00c46}, {1, 0x00c49}, {0, 0x00c4a}, {1, 0x00c4e}, + {0, 0x00c55}, {1, 0x00c57}, {0, 0x00cbc}, {1, 0x00cbd}, {0, 0x00cbf}, + {1, 0x00cc0}, {0, 0x00cc6}, {1, 0x00cc7}, {0, 0x00ccc}, {1, 0x00cce}, + {0, 0x00ce2}, {1, 0x00ce4}, {0, 0x00d41}, {1, 0x00d44}, {0, 0x00d4d}, + {1, 0x00d4e}, {0, 0x00dca}, {1, 0x00dcb}, {0, 0x00dd2}, {1, 0x00dd5}, + {0, 0x00dd6}, {1, 0x00dd7}, {0, 0x00e31}, {1, 0x00e32}, {0, 0x00e34}, + {1, 0x00e3b}, {0, 0x00e47}, {1, 0x00e4f}, {0, 0x00eb1}, {1, 0x00eb2}, + {0, 0x00eb4}, {1, 0x00eba}, {0, 0x00ebb}, {1, 0x00ebd}, {0, 0x00ec8}, + {1, 0x00ece}, {0, 0x00f18}, {1, 0x00f1a}, {0, 0x00f35}, {1, 0x00f36}, + {0, 0x00f37}, {1, 0x00f38}, {0, 0x00f39}, {1, 0x00f3a}, {0, 0x00f71}, + {1, 0x00f7f}, {0, 0x00f80}, {1, 0x00f85}, {0, 0x00f86}, {1, 0x00f88}, + {0, 0x00f90}, {1, 0x00f98}, {0, 0x00f99}, {1, 0x00fbd}, {0, 0x00fc6}, + {1, 0x00fc7}, {0, 0x0102d}, {1, 0x01031}, {0, 0x01032}, {1, 0x01033}, + {0, 0x01036}, {1, 0x01038}, {0, 0x01039}, {1, 0x0103a}, {0, 0x01058}, + {1, 0x0105a}, {2, 0x01100}, {0, 0x01160}, {1, 0x01200}, {0, 0x0135f}, + {1, 0x01360}, {0, 0x01712}, {1, 0x01715}, {0, 0x01732}, {1, 0x01735}, + {0, 0x01752}, {1, 0x01754}, {0, 0x01772}, {1, 0x01774}, {0, 0x017b4}, + {1, 0x017b6}, {0, 0x017b7}, {1, 0x017be}, {0, 0x017c6}, {1, 0x017c7}, + {0, 0x017c9}, {1, 0x017d4}, {0, 0x017dd}, {1, 0x017de}, {0, 0x0180b}, + {1, 0x0180e}, {0, 0x018a9}, {1, 0x018aa}, {0, 0x01920}, {1, 0x01923}, + {0, 0x01927}, {1, 0x01929}, {0, 0x01932}, {1, 0x01933}, {0, 0x01939}, + {1, 0x0193c}, {0, 0x01a17}, {1, 0x01a19}, {0, 0x01b00}, {1, 0x01b04}, + {0, 0x01b34}, {1, 0x01b35}, {0, 0x01b36}, {1, 0x01b3b}, {0, 0x01b3c}, + {1, 0x01b3d}, {0, 0x01b42}, {1, 0x01b43}, {0, 0x01b6b}, {1, 0x01b74}, + {0, 0x01dc0}, {1, 0x01dcb}, {0, 0x01dfe}, {1, 0x01e00}, {0, 0x0200b}, + {1, 0x02010}, {0, 0x0202a}, {1, 0x0202f}, {0, 0x02060}, {1, 0x02064}, + {0, 0x0206a}, {1, 0x02070}, {0, 0x020d0}, {1, 0x020f0}, {2, 0x02329}, + {1, 0x0232b}, {2, 0x02e80}, {0, 0x0302a}, {2, 0x03030}, {1, 0x0303f}, + {2, 0x03040}, {0, 0x03099}, {2, 0x0309b}, {1, 0x0a4d0}, {0, 0x0a806}, + {1, 0x0a807}, {0, 0x0a80b}, {1, 0x0a80c}, {0, 0x0a825}, {1, 0x0a827}, + {2, 0x0ac00}, {1, 0x0d7a4}, {2, 0x0f900}, {1, 0x0fb00}, {0, 0x0fb1e}, + {1, 0x0fb1f}, {0, 0x0fe00}, {2, 0x0fe10}, {1, 0x0fe1a}, {0, 0x0fe20}, + {1, 0x0fe24}, {2, 0x0fe30}, {1, 0x0fe70}, {0, 0x0feff}, {2, 0x0ff00}, + {1, 0x0ff61}, {2, 0x0ffe0}, {1, 0x0ffe7}, {0, 0x0fff9}, {1, 0x0fffc}, + {0, 0x10a01}, {1, 0x10a04}, {0, 0x10a05}, {1, 0x10a07}, {0, 0x10a0c}, + {1, 0x10a10}, {0, 0x10a38}, {1, 0x10a3b}, {0, 0x10a3f}, {1, 0x10a40}, + {0, 0x1d167}, {1, 0x1d16a}, {0, 0x1d173}, {1, 0x1d183}, {0, 0x1d185}, + {1, 0x1d18c}, {0, 0x1d1aa}, {1, 0x1d1ae}, {0, 0x1d242}, {1, 0x1d245}, + {2, 0x20000}, {1, 0x2fffe}, {2, 0x30000}, {1, 0x3fffe}, {0, 0xe0001}, + {1, 0xe0002}, {0, 0xe0020}, {1, 0xe0080}, {0, 0xe0100}, {1, 0xe01f0} +}; + +/* +** Return an estimate of the width, in columns, for the single Unicode +** character c. For normal characters, the answer is always 1. But the +** estimate might be 0 or 2 for zero-width and double-width characters. +** +** Different display devices display unicode using different widths. So +** it is impossible to know that true display width with 100% accuracy. +** Inaccuracies in the width estimates might cause columns to be misaligned. +** Unfortunately, there is nothing we can do about that. +*/ +int sqlite3_qrf_wcwidth(int c){ + int iFirst, iLast; + + /* Fast path for common characters */ + if( c<=0x300 ) return 1; + + /* The general case */ + iFirst = 0; + iLast = sizeof(aQrfUWidth)/sizeof(aQrfUWidth[0]) - 1; + while( iFirst c ){ + iLast = iMid - 1; + }else{ + return aQrfUWidth[iMid].w; + } + } + if( aQrfUWidth[iLast].iFirst > c ) return aQrfUWidth[iFirst].w; + return aQrfUWidth[iLast].w; +} + +/* +** Compute the value and length of a multi-byte UTF-8 character that +** begins at z[0]. Return the length. Write the Unicode value into *pU. +** +** This routine only works for *multi-byte* UTF-8 characters. It does +** not attempt to detect illegal characters. +*/ +int sqlite3_qrf_decode_utf8(const unsigned char *z, int *pU){ + if( (z[0] & 0xe0)==0xc0 && (z[1] & 0xc0)==0x80 ){ + *pU = ((z[0] & 0x1f)<<6) | (z[1] & 0x3f); + return 2; + } + if( (z[0] & 0xf0)==0xe0 && (z[1] & 0xc0)==0x80 && (z[2] & 0xc0)==0x80 ){ + *pU = ((z[0] & 0x0f)<<12) | ((z[1] & 0x3f)<<6) | (z[2] & 0x3f); + return 3; + } + if( (z[0] & 0xf8)==0xf0 && (z[1] & 0xc0)==0x80 && (z[2] & 0xc0)==0x80 + && (z[3] & 0xc0)==0x80 + ){ + *pU = ((z[0] & 0x0f)<<18) | ((z[1] & 0x3f)<<12) | ((z[2] & 0x3f))<<6 + | (z[3] & 0x3f); + return 4; + } + *pU = 0; + return 1; +} + +/* +** Return the length of a string in display characters. +** Multibyte UTF8 characters count as a single character +** for single-width characters, or as two characters for +** double-width characters. +*/ +static int qrfDisplayLength(const char *z){ + int n = 0; + while( *z ){ + if( (0x80&z[0])==0 ){ + n++; + z++; + }else{ + int u = 0; + int len = sqlite3_qrf_decode_utf8((const unsigned char*)z, &u); + z += len; + n += sqlite3_qrf_wcwidth(u); + } + } + return n; +} + /* ** Escape the input string if it is needed and in accordance with ** eEscape, which is either QRF_ESC_Ascii or QRF_ESC_Symbol. @@ -89,7 +255,7 @@ static void resfmtWrite(Qrf *p){ ** The caller is responsible for freeing *ppFree if it is non-NULL in order ** to reclaim memory. */ -static void resfmtEscape( +static void qrfEscape( int eEscape, /* QRF_ESC_Ascii or QRF_ESC_Symbol */ sqlite3_str *pStr, /* String to be escaped */ int iStart /* Begin escapding on this byte of pStr */ @@ -159,7 +325,7 @@ static void resfmtEscape( ** If a field contains any character identified by a 1 in the following ** array, then the string must be quoted for CSV. */ -static const char resfmtCsvQuote[] = { +static const char qrfCsvQuote[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, @@ -179,27 +345,27 @@ static const char resfmtCsvQuote[] = { }; /* -** Encode text appropriately and append it to p->pOut. +** Encode text appropriately and append it to pOut. */ -static void resfmtEncodeText(Qrf *p, const char *zTxt){ - int iStart = sqlite3_str_length(p->pOut); +static void qrfEncodeText(Qrf *p, sqlite3_str *pOut, const char *zTxt){ + int iStart = sqlite3_str_length(pOut); switch( p->spec.eQuote ){ case QRF_TXT_Sql: { - sqlite3_str_appendf(p->pOut, "%Q", zTxt); + sqlite3_str_appendf(pOut, "%Q", zTxt); break; } case QRF_TXT_Csv: { unsigned int i; for(i=0; zTxt[i]; i++){ - if( resfmtCsvQuote[((const unsigned char*)zTxt)[i]] ){ + if( qrfCsvQuote[((const unsigned char*)zTxt)[i]] ){ i = 0; break; } } if( i==0 || strstr(zTxt, p->spec.zColumnSep)!=0 ){ - sqlite3_str_appendf(p->pOut, "\"%w\"", zTxt); + sqlite3_str_appendf(pOut, "\"%w\"", zTxt); }else{ - sqlite3_str_appendall(p->pOut, zTxt); + sqlite3_str_appendall(pOut, zTxt); } break; } @@ -214,14 +380,14 @@ static void resfmtEncodeText(Qrf *p, const char *zTxt){ i++; } if( i>0 ){ - sqlite3_str_append(p->pOut, (const char*)z, i); + sqlite3_str_append(pOut, (const char*)z, i); } switch( z[i] ){ - case '>': sqlite3_str_append(p->pOut, "<", 4); break; - case '&': sqlite3_str_append(p->pOut, "&", 5); break; - case '<': sqlite3_str_append(p->pOut, "<", 4); break; - case '"': sqlite3_str_append(p->pOut, """, 6); break; - case '\'': sqlite3_str_append(p->pOut, "'", 5); break; + case '>': sqlite3_str_append(pOut, "<", 4); break; + case '&': sqlite3_str_append(pOut, "&", 5); break; + case '<': sqlite3_str_append(pOut, "<", 4); break; + case '"': sqlite3_str_append(pOut, """, 6); break; + case '\'': sqlite3_str_append(pOut, "'", 5); break; default: i--; } z += i + 1; @@ -231,49 +397,49 @@ static void resfmtEncodeText(Qrf *p, const char *zTxt){ case QRF_TXT_Tcl: case QRF_TXT_Json: { const unsigned char *z = (const unsigned char*)zTxt; - sqlite3_str_append(p->pOut, "\"", 1); + sqlite3_str_append(pOut, "\"", 1); while( *z ){ unsigned int i; for(i=0; z[i]>=0x20 && z[i]!='\\' && z[i]!='"'; i++){} if( i>0 ){ - sqlite3_str_append(p->pOut, (const char*)z, i); + sqlite3_str_append(pOut, (const char*)z, i); } switch( z[i] ){ - case '"': sqlite3_str_append(p->pOut, "\\\"", 2); break; - case '\\': sqlite3_str_append(p->pOut, "\\\\", 2); break; - case '\b': sqlite3_str_append(p->pOut, "\\b", 2); break; - case '\f': sqlite3_str_append(p->pOut, "\\f", 2); break; - case '\n': sqlite3_str_append(p->pOut, "\\n", 2); break; - case '\r': sqlite3_str_append(p->pOut, "\\r", 2); break; - case '\t': sqlite3_str_append(p->pOut, "\\t", 2); break; + case '"': sqlite3_str_append(pOut, "\\\"", 2); break; + case '\\': sqlite3_str_append(pOut, "\\\\", 2); break; + case '\b': sqlite3_str_append(pOut, "\\b", 2); break; + case '\f': sqlite3_str_append(pOut, "\\f", 2); break; + case '\n': sqlite3_str_append(pOut, "\\n", 2); break; + case '\r': sqlite3_str_append(pOut, "\\r", 2); break; + case '\t': sqlite3_str_append(pOut, "\\t", 2); break; default: { if( p->spec.eQuote==QRF_TXT_Json ){ - sqlite3_str_appendf(p->pOut, "\\u%04x", z[i]); + sqlite3_str_appendf(pOut, "\\u%04x", z[i]); }else{ - sqlite3_str_appendf(p->pOut, "\\%03o", z[i]); + sqlite3_str_appendf(pOut, "\\%03o", z[i]); } break; } } z += i + 1; } - sqlite3_str_append(p->pOut, "\"", 1); + sqlite3_str_append(pOut, "\"", 1); break; } default: { - sqlite3_str_appendall(p->pOut, zTxt); + sqlite3_str_appendall(pOut, zTxt); break; } } if( p->spec.eEscape!=QRF_ESC_Off ){ - resfmtEscape(p->spec.eEscape, p->pOut, iStart); + qrfEscape(p->spec.eEscape, pOut, iStart); } } /* -** Render value pVal into p->pOut +** Render value pVal into pOut */ -static void resfmtRenderValue(Qrf *p, int iCol){ +static void qrfRenderValue(Qrf *p, sqlite3_str *pOut, int iCol){ if( p->spec.xRender ){ sqlite3_value *pVal; char *z; @@ -281,23 +447,23 @@ static void resfmtRenderValue(Qrf *p, int iCol){ z = p->spec.xRender(p->spec.pRenderArg, pVal); sqlite3_value_free(pVal); if( z ){ - sqlite3_str_appendall(p->pOut, z); + sqlite3_str_appendall(pOut, z); sqlite3_free(z); return; } } switch( sqlite3_column_type(p->pStmt,iCol) ){ case SQLITE_INTEGER: { - sqlite3_str_appendf(p->pOut, "%lld", sqlite3_column_int64(p->pStmt,iCol)); + sqlite3_str_appendf(pOut, "%lld", sqlite3_column_int64(p->pStmt,iCol)); break; } case SQLITE_FLOAT: { if( p->spec.zFloatFmt ){ double r = sqlite3_column_double(p->pStmt,iCol); - sqlite3_str_appendf(p->pOut, p->spec.zFloatFmt, r); + sqlite3_str_appendf(pOut, p->spec.zFloatFmt, r); }else{ const char *zTxt = (const char*)sqlite3_column_text(p->pStmt,iCol); - sqlite3_str_appendall(p->pOut, zTxt); + sqlite3_str_appendall(pOut, zTxt); } break; } @@ -311,16 +477,16 @@ static void resfmtRenderValue(Qrf *p, int iCol){ char *zVal; const unsigned char *a = sqlite3_column_blob(p->pStmt,iCol); if( p->spec.eBlob==QRF_BLOB_Sql ){ - sqlite3_str_append(p->pOut, "x'", 2); + sqlite3_str_append(pOut, "x'", 2); } - iStart = sqlite3_str_length(p->pOut); - sqlite3_str_appendchar(p->pOut, nBlob, ' '); - sqlite3_str_appendchar(p->pOut, nBlob, ' '); + iStart = sqlite3_str_length(pOut); + sqlite3_str_appendchar(pOut, nBlob, ' '); + sqlite3_str_appendchar(pOut, nBlob, ' '); if( p->spec.eBlob==QRF_BLOB_Sql ){ - sqlite3_str_appendchar(p->pOut, 1, '\''); + sqlite3_str_appendchar(pOut, 1, '\''); } - if( sqlite3_str_errcode(p->pOut) ) return; - zVal = sqlite3_str_value(p->pOut); + if( sqlite3_str_errcode(pOut) ) return; + zVal = sqlite3_str_value(pOut); for(i=0, j=iStart; i>4)&0xf]; @@ -336,14 +502,14 @@ static void resfmtRenderValue(Qrf *p, int iCol){ char *zVal; const unsigned char *a = sqlite3_column_blob(p->pStmt,iCol); int szC = p->spec.eBlob==QRF_BLOB_Json ? 6 : 4; - sqlite3_str_append(p->pOut, "\"", 1); - iStart = sqlite3_str_length(p->pOut); + sqlite3_str_append(pOut, "\"", 1); + iStart = sqlite3_str_length(pOut); for(i=szC; i>0; i--){ - sqlite3_str_appendchar(p->pOut, nBlob, ' '); + sqlite3_str_appendchar(pOut, nBlob, ' '); } - sqlite3_str_appendchar(p->pOut, 1, '"'); - if( sqlite3_str_errcode(p->pOut) ) return; - zVal = sqlite3_str_value(p->pOut); + sqlite3_str_appendchar(pOut, 1, '"'); + if( sqlite3_str_errcode(pOut) ) return; + zVal = sqlite3_str_value(pOut); for(i=0, j=iStart; ipStmt,iCol); - resfmtEncodeText(p, zTxt); + qrfEncodeText(p, pOut, zTxt); } } break; } case SQLITE_NULL: { - sqlite3_str_appendall(p->pOut, p->spec.zNull); + sqlite3_str_appendall(pOut, p->spec.zNull); break; } case SQLITE_TEXT: { const char *zTxt = (const char*)sqlite3_column_text(p->pStmt,iCol); - resfmtEncodeText(p, zTxt); + qrfEncodeText(p, pOut, zTxt); + break; + } + } +} + +/* +** Check to see if z[] is a valid VT100 escape. If it is, then +** return the number of bytes in the escape sequence. Return 0 if +** z[] is not a VT100 escape. +** +** This routine assumes that z[0] is \033 (ESC). +*/ +static int qrfIsVt100(const unsigned char *z){ + int i; + if( z[1]!='[' ) return 0; + i = 2; + while( z[i]>=0x30 && z[i]<=0x3f ){ i++; } + while( z[i]>=0x20 && z[i]<=0x2f ){ i++; } + if( z[i]<0x40 || z[i]>0x7e ) return 0; + return i+1; +} + +/* +** z[] is a line of text that is to be displayed the box or table or +** similar tabular formats. z[] might contain control characters such +** as \n, \t, \f, or \r. +** +** Compute characters to display on the first line of z[]. Stop at the +** first \r, \n, or \f. Expand \t into spaces. Return a copy (obtained +** from sqlite3_malloc()) of that first line. The caller is responsible +** for eventually freeing that line of text. +** +** Write anything to display on the next line into *pzTail. If this is +** the last line, write a NULL into *pzTail. (*pzTail is not allocated.) +*/ +static char *qrfTableCell( + Qrf *p, /* To access display settings */ + const unsigned char *z, /* Input text to be transformed */ + const unsigned char **pzTail, /* OUT: Tail of the input for next line */ + int mxWidth, /* Max width. 0 means no limit */ + int bWordWrap /* If true, avoid breaking mid-word */ +){ + int i; /* Input bytes consumed */ + int j; /* Output bytes generated */ + int k; /* Input bytes to be displayed */ + int n; /* Output column number */ + unsigned char *zOut; /* Output text */ + + if( z==0 ){ + *pzTail = 0; + return 0; + } + if( mxWidth<0 ) mxWidth = -mxWidth; + if( mxWidth==0 ) mxWidth = 1000000; + i = j = n = 0; + while( n=0xc0 ){ + int u; + int len = sqlite3_qrf_decode_utf8(&z[i], &u); + i += len; + j += len; + n += sqlite3_qrf_wcwidth(u); + continue; + } + if( c>=' ' ){ + n++; + i++; + j++; + continue; + } + if( c==0 || c=='\n' || (c=='\r' && z[i+1]=='\n') ) break; + if( c=='\t' ){ + do{ + n++; + j++; + }while( (n&7)!=0 && n0 ){ + i += k; + j += k; + }else{ + n++; + j += 3; + i++; + } + } + if( n>=mxWidth && bWordWrap ){ + /* Perhaps try to back up to a better place to break the line */ + for(k=i; k>i/2; k--){ + if( isspace(z[k-1]) ) break; + } + if( k<=i/2 ){ + for(k=i; k>i/2; k--){ + if( isalnum(z[k-1])!=isalnum(z[k]) && (z[k]&0xc0)!=0x80 ) break; + } + } + if( k<=i/2 ){ + k = i; + }else{ + i = k; + while( z[i]==' ' ) i++; + } + }else{ + k = i; + } + if( n>=mxWidth && z[i]>=' ' ){ + *pzTail = &z[i]; + }else if( z[i]=='\r' && z[i+1]=='\n' ){ + *pzTail = z[i+2] ? &z[i+2] : 0; + }else if( z[i]==0 || z[i+1]==0 ){ + *pzTail = 0; + }else{ + *pzTail = &z[i+1]; + } + zOut = sqlite3_malloc64( j+1 ); + if( zOut==0 ){ + qrfOom(p); + return 0; + } + i = j = n = 0; + while( i=0xc0 ){ + int u; + int len = sqlite3_qrf_decode_utf8(&z[i], &u); + do{ zOut[j++] = z[i++]; }while( (--len)>0 ); + n += sqlite3_qrf_wcwidth(u); + continue; + } + if( c>=' ' ){ + n++; + zOut[j++] = z[i++]; + continue; + } + if( c==0 ) break; + if( z[i]=='\t' ){ + do{ + n++; + zOut[j++] = ' '; + }while( (n&7)!=0 && nmxW ){ + w= mxW; + } + aw = w<0 ? -w : w; + if( zUtf==0 ) zUtf = ""; + while( (c = a[i])!=0 ){ + if( (c&0xc0)==0xc0 ){ + int u; + int len = sqlite3_qrf_decode_utf8(a+i, &u); + int x = sqlite3_qrf_wcwidth(u); + if( x+n>aw ){ + break; + } + i += len; + n += x; + }else if( c==0x1b && (k = qrfIsVt100(&a[i]))>0 ){ + i += k; + }else if( n>=aw ){ + break; + }else{ + n++; + i++; + } + } + if( n>=aw ){ + sqlite3_str_append(pOut, zUtf, i); + }else if( w<0 ){ + if( aw>n ) sqlite3_str_appendchar(pOut, aw-n, ' '); + sqlite3_str_append(pOut, zUtf, i); + }else{ + sqlite3_str_append(pOut, zUtf, i); + if( aw>n ) sqlite3_str_appendchar(pOut, aw-n, ' '); + } +} + +/* +** Print a markdown or table-style row separator using ascii-art +*/ +static void qrfRowSeparator(Qrf *p, const char *zSep){ + int i; + if( p->nCol>0 ){ + int nSep = (int)(strlen(zSep)&0x3fffffff); + sqlite3_str_append(p->pOut, zSep, nSep); + sqlite3_str_appendchar(p->pOut, p->actualWidth[0]+2, '-'); + for(i=1; inCol; i++){ + sqlite3_str_append(p->pOut, zSep, nSep); + sqlite3_str_appendchar(p->pOut, p->actualWidth[i]+2, '-'); + } + sqlite3_str_append(p->pOut, zSep, nSep); + } + sqlite3_str_append(p->pOut, "\n", 1); +} + + +/* +** UTF8 box-drawing characters. Imagine box lines like this: +** +** 1 +** | +** 4 --+-- 2 +** | +** 3 +** +** Each box characters has between 2 and 4 of the lines leading from +** the center. The characters are here identified by the numbers of +** their corresponding lines. +*/ +#define BOX_24 "\342\224\200" /* U+2500 --- */ +#define BOX_13 "\342\224\202" /* U+2502 | */ +#define BOX_23 "\342\224\214" /* U+250c ,- */ +#define BOX_34 "\342\224\220" /* U+2510 -, */ +#define BOX_12 "\342\224\224" /* U+2514 '- */ +#define BOX_14 "\342\224\230" /* U+2518 -' */ +#define BOX_123 "\342\224\234" /* U+251c |- */ +#define BOX_134 "\342\224\244" /* U+2524 -| */ +#define BOX_234 "\342\224\254" /* U+252c -,- */ +#define BOX_124 "\342\224\264" /* U+2534 -'- */ +#define BOX_1234 "\342\224\274" /* U+253c -|- */ + +/* Draw horizontal line N characters long using unicode box +** characters +*/ +static void qrfBoxLine(sqlite3_str *pOut, int N){ + const char zDash[] = + BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 + BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24; + const int nDash = sizeof(zDash) - 1; + N *= 3; + while( N>nDash ){ + sqlite3_str_append(pOut, zDash, nDash); + N -= nDash; + } + sqlite3_str_append(pOut, zDash, N); +} + +/* +** Draw a horizontal separator for a QRF_MODE_Box table. +*/ +static void qrfBoxSeparator( + Qrf *p, + const char *zSep1, + const char *zSep2, + const char *zSep3 +){ + int i; + if( p->nCol>0 ){ + sqlite3_str_appendall(p->pOut, zSep1); + qrfBoxLine(p->pOut, p->actualWidth[0]+2); + for(i=1; inCol; i++){ + sqlite3_str_appendall(p->pOut, zSep2); + qrfBoxLine(p->pOut, p->actualWidth[i]+2); + } + sqlite3_str_appendall(p->pOut, zSep3); + } + sqlite3_str_append(p->pOut, "\n", 1); +} + + +/* +** Columnar modes require that the entire query be evaluated first, with +** results written into memory, so that we can compute appropriate column +** widths. +*/ +static void qrfColumnar(Qrf *p){ + sqlite3_int64 nRow = 0; /* Rows displayed. nRow>=p->nRow */ + char **azData = 0; /* Text for all cells */ + int *aiDspyWth = 0; /* Display width of each cell */ + sqlite3_int64 nAlloc = 0; /* Slots allocated for azData[], aiDspyWth[] */ + char *abRowDiv = 0; /* Row is a continuation or is complete */ + const unsigned char *uz; + const char *z; + sqlite3_str **aCol = 0; /* Temporary storage for column data */ + sqlite3_int64 i, j, k, nData; + int rc, nTotal, w, n; + const char *colSep = 0; + const char *rowSep = 0; + const unsigned char **azNextLine = 0; + int bNextLine = 0; + int bMultiLineRowExists = 0; + int bw = p->spec.bWordWrap; + int nColumn = p->nCol; + + rc = sqlite3_step(p->pStmt); + if( rc!=SQLITE_ROW || nColumn==0 ){ + return; /* No output */ + } + + /* Initialize data structures */ + nAlloc = nColumn*4; + azData = sqlite3_malloc64( nAlloc*sizeof(char*) ); + if( azData==0 ){ qrfOom(p); goto qrf_column_end; } + aiDspyWth = sqlite3_malloc64( nAlloc*sizeof(int) ); + if( aiDspyWth==0 ){ qrfOom(p); goto qrf_column_end; } + azNextLine = sqlite3_malloc64( nColumn*sizeof(char*) ); + if( azNextLine==0 ){ qrfOom(p); goto qrf_column_end; } + memset((void*)azNextLine, 0, nColumn*sizeof(char*) ); + aCol = sqlite3_malloc64( nColumn*sizeof(aCol[0]) ); + if( aCol==0 ){ qrfOom(p); goto qrf_column_end; } + for(i=0; idb); + if( aCol[i]==0 ){ qrfOom(p); goto qrf_column_end; } + } + abRowDiv = sqlite3_malloc64( nAlloc/nColumn ); + if( abRowDiv==0 ){ qrfOom(p); goto qrf_column_end; } + p->actualWidth = sqlite3_malloc64( sizeof(int)*nColumn ); + if( p->actualWidth==0 ){ qrfOom(p); goto qrf_column_end; } + for(i=0; ispec.nWidth && ispec.aWidth[i]; + if( w<0 ) w = -w; + p->actualWidth[i] = w; + } + while( iactualWidth[i++] = 0; + } + + /* Capture the column names as the first row of data */ + for(i=0; iactualWidth[i]; + if( wx==0 ){ + wx = p->spec.mxWidth; + } + if( wx<0 ) wx = -wx; + uz = (const unsigned char*)sqlite3_column_name(p->pStmt,i); + if( uz==0 ) uz = (unsigned char*)""; + qrfEncodeText(p, aCol[i], (const char*)uz); + uz = (unsigned char*)sqlite3_str_value(aCol[i]); + azData[i] = qrfTableCell(p, uz, &zNotUsed, wx, bw); + if( p->iErr ) goto qrf_column_end; + aiDspyWth[i] = qrfDisplayLength(azData[i]); + if( aiDspyWth[i]>p->actualWidth[i] ){ + p->actualWidth[i] = aiDspyWth[i]; + } + sqlite3_str_reset(aCol[i]); + } + + /* Capture column content for all rows */ + k = nColumn; + do{ + p->nRow++; + do{ + int useNextLine = bNextLine; + bNextLine = 0; + if( (nRow+2)*nColumn >= nAlloc ){ + char **azNewData; + char *abNewDiv; + int *aiNewWidth; + + nAlloc *= 2; + azNewData = sqlite3_realloc64(azData, nAlloc*sizeof(char*)); + if( azNewData==0 ){ qrfOom(p); goto qrf_column_end; } + azData = azNewData; + abNewDiv = sqlite3_realloc64(abRowDiv, nAlloc/nColumn); + if( abNewDiv==0 ){ qrfOom(p); goto qrf_column_end; } + abRowDiv = abNewDiv; + aiNewWidth = sqlite3_realloc64(aiDspyWth, nAlloc*sizeof(int)); + if( aiNewWidth==0 ){ qrfOom(p); goto qrf_column_end; } + aiDspyWth = aiNewWidth; + } + abRowDiv[nRow] = 1; + nRow++; + for(i=0; ispec.nWidth ? p->spec.aWidth[i] : 0; + if( wx==0 ){ + wx = p->spec.mxWidth; + } + if( wx<0 ) wx = -wx; + if( useNextLine ){ + uz = azNextLine[i]; + if( uz==0 ) uz = (unsigned char*)""; + }else{ + sqlite3_str_reset(aCol[i]); + qrfRenderValue(p, aCol[i], i); + uz = (unsigned char*)sqlite3_str_value(aCol[i]); + if( uz==0 ){ qrfOom(p); goto qrf_column_end; } + } + azData[k] = qrfTableCell(p, uz, &azNextLine[i], wx, bw); + if( p->iErr ) goto qrf_column_end; + aiDspyWth[k] = qrfDisplayLength(azData[k]); + if( aiDspyWth[k]>p->actualWidth[i] ){ + p->actualWidth[i] = aiDspyWth[k]; + } + if( azNextLine[i] ){ + bNextLine = 1; + abRowDiv[nRow-1] = 0; + bMultiLineRowExists = 1; + } + } + }while( bNextLine ); + }while( sqlite3_step(p->pStmt)==SQLITE_ROW ); + if( sqlite3_is_interrupted(p->db) ) goto qrf_column_end; + + /* Generate the column titles */ + switch( p->spec.eFormat ){ + case QRF_MODE_Column: { + colSep = " "; + rowSep = "\n"; + if( p->spec.bShowCNames ){ + for(i=0; iactualWidth[i]; + if( p->spec.aWidth[i]<0 ) w = -w; + qrfWidthPrint(p, p->pOut, w, azData[i]); + sqlite3_str_append(p->pOut, i==nColumn-1?"\n":" ", 1); + } + for(i=0; ipOut, p->actualWidth[i], '-'); + sqlite3_str_append(p->pOut, i==nColumn-1?"\n":" ", 1); + } + } + break; + } + case QRF_MODE_Table: { + colSep = " | "; + rowSep = " |\n"; + qrfRowSeparator(p, "+"); + sqlite3_str_append(p->pOut, "| ", 2); + for(i=0; iactualWidth[i]; + n = aiDspyWth[i]; + sqlite3_str_appendchar(p->pOut, (w-n)/2, ' '); + sqlite3_str_appendall(p->pOut, azData[i]); + sqlite3_str_appendchar(p->pOut, (w-n+1)/2, ' '); + sqlite3_str_append(p->pOut, i==nColumn-1?" |\n":" | ", 3); + } + qrfRowSeparator(p, "+"); + break; + } + case QRF_MODE_Markdown: { + colSep = " | "; + rowSep = " |\n"; + sqlite3_str_append(p->pOut, "| ", 2); + for(i=0; iactualWidth[i]; + n = aiDspyWth[i]; + sqlite3_str_appendchar(p->pOut, (w-n)/2, ' '); + sqlite3_str_appendall(p->pOut, azData[i]); + sqlite3_str_appendchar(p->pOut, (w-n+1)/2, ' '); + sqlite3_str_append(p->pOut, i==nColumn-1 ? " |\n" : " | ", 3); + } + qrfRowSeparator(p, "|"); + break; + } + case QRF_MODE_Box: { + colSep = " " BOX_13 " "; + rowSep = " " BOX_13 "\n"; + qrfBoxSeparator(p, BOX_23, BOX_234, BOX_34); + sqlite3_str_appendall(p->pOut, BOX_13 " "); + for(i=0; iactualWidth[i]; + n = aiDspyWth[i]; + sqlite3_str_appendchar(p->pOut, (w-n)/2, ' '); + sqlite3_str_appendall(p->pOut, azData[i]); + sqlite3_str_appendchar(p->pOut, (w-n+1)/2, ' '); + sqlite3_str_appendall(p->pOut, i==nColumn-1?" "BOX_13"\n":" "BOX_13" "); + } + qrfBoxSeparator(p, BOX_123, BOX_1234, BOX_134); break; } } + + /* Render the body of the table */ + nTotal = nColumn*(nRow+1); + for(i=nColumn, j=0; ispec.eFormat!=QRF_MODE_Column ){ + sqlite3_str_appendall(p->pOut, + p->spec.eFormat==QRF_MODE_Box ? BOX_13" " : "| "); + } + z = azData[i]; + if( z==0 ) z = ""; + w = p->actualWidth[j]; + if( p->spec.aWidth[j]<0 ) w = -w; + qrfWidthPrint(p, p->pOut, w, z); + if( j==nColumn-1 ){ + sqlite3_str_appendall(p->pOut, rowSep); + if( bMultiLineRowExists && abRowDiv[i/nColumn-1] && i+1spec.eFormat ){ + case QRF_MODE_Table: + qrfRowSeparator(p, "+"); + break; + case QRF_MODE_Box: + qrfBoxSeparator(p, BOX_123, BOX_1234, BOX_134); + break; + case QRF_MODE_Column: + sqlite3_str_append(p->pOut, "\n", 1); + break; + } + } + j = -1; + qrfWrite(p); + if( sqlite3_is_interrupted(p->db) ) break; + }else{ + sqlite3_str_appendall(p->pOut, colSep); + } + } + if( p->spec.eFormat==QRF_MODE_Table ){ + qrfRowSeparator(p, "+"); + }else if( p->spec.eFormat==QRF_MODE_Box ){ + qrfBoxSeparator(p, BOX_12, BOX_124, BOX_14); + } + +qrf_column_end: + /* Free allocated memory */ + if( aCol ){ + for(i=0; iiVersion */ + size_t sz; /* Size of pSpec[], based on pSpec->iVersion */ memset(p, 0, sizeof(*p)); p->pzErr = pzErr; if( pSpec->iVersion!=1 ){ - resfmtError(p, SQLITE_ERROR, + qrfError(p, SQLITE_ERROR, "unusable sqlite3_qrf_spec.iVersion (%d)", pSpec->iVersion); return; @@ -402,7 +1110,7 @@ static void resfmtInitialize( p->db = sqlite3_db_handle(pStmt); p->pOut = sqlite3_str_new(p->db); if( p->pOut==0 ){ - resfmtOom(p); + qrfOom(p); return; } p->iErr = 0; @@ -417,7 +1125,7 @@ static void resfmtInitialize( case QRF_TXT_Csv: p->spec.eBlob = QRF_BLOB_Tcl; break; case QRF_TXT_Tcl: p->spec.eBlob = QRF_BLOB_Tcl; break; case QRF_TXT_Json: p->spec.eBlob = QRF_BLOB_Json; break; - default: p->spec.eBlob = QRF_BLOB_Text; break; + default: p->spec.eBlob = QRF_BLOB_Text; break; } } switch( p->spec.eFormat ){ @@ -432,7 +1140,7 @@ static void resfmtInitialize( /* ** Render a single row of output. */ -static void resfmtDoOneRow(Qrf *p){ +static void qrfOneSimpleRow(Qrf *p){ int i; switch( p->spec.eFormat ){ case QRF_MODE_Off: @@ -445,17 +1153,17 @@ static void resfmtDoOneRow(Qrf *p){ for(i=0; inCol; i++){ const char *zCName = sqlite3_column_name(p->pStmt, i); if( i>0 ) sqlite3_str_appendall(p->pOut, p->spec.zColumnSep); - resfmtEncodeText(p, zCName); + qrfEncodeText(p, p->pOut, zCName); } sqlite3_str_appendall(p->pOut, p->spec.zRowSep); - resfmtWrite(p); + qrfWrite(p); } for(i=0; inCol; i++){ if( i>0 ) sqlite3_str_appendall(p->pOut, p->spec.zColumnSep); - resfmtRenderValue(p, i); + qrfRenderValue(p, p->pOut, i); } sqlite3_str_appendall(p->pOut, p->spec.zRowSep); - resfmtWrite(p); + qrfWrite(p); break; } } @@ -465,11 +1173,11 @@ static void resfmtDoOneRow(Qrf *p){ /* ** Finish rendering the results */ -static void resfmtFinalize(Qrf *p){ +static void qrfFinalize(Qrf *p){ switch( p->spec.eFormat ){ case QRF_MODE_Count: { sqlite3_str_appendf(p->pOut, "%lld\n", p->nRow); - resfmtWrite(p); + qrfWrite(p); break; } } @@ -478,6 +1186,7 @@ static void resfmtFinalize(Qrf *p){ }else if( p->pOut ){ sqlite3_free(sqlite3_str_finish(p->pOut)); } + if( p->actualWidth ) sqlite3_free(p->actualWidth); } /* @@ -495,16 +1204,27 @@ int sqlite3_format_query_result( if( pStmt==0 ) return SQLITE_OK; /* No-op */ if( pSpec==0 ) return SQLITE_MISUSE; - resfmtInitialize(&qrf, pStmt, pSpec, pzErr); - while( qrf.iErr==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ - resfmtDoOneRow(&qrf); - } - if( qrf.iErr==SQLITE_OK ){ - int rc = sqlite3_reset(qrf.pStmt); - if( rc!=SQLITE_OK ){ - resfmtError(&qrf, rc, "%s", sqlite3_errmsg(qrf.db)); + qrfInitialize(&qrf, pStmt, pSpec, pzErr); + switch( qrf.spec.eFormat ){ + case QRF_MODE_Box: + case QRF_MODE_Column: + case QRF_MODE_Markdown: + case QRF_MODE_Table: { + /* Columnar modes require that the entire query be evaluated and the + ** results stored in memory, so that we can compute column widths */ + qrfColumnar(&qrf); + break; + } + default: { + /* Non-columnar modes where the output can occur after each row + ** of result is received */ + while( qrf.iErr==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ + qrfOneSimpleRow(&qrf); + } + break; } } - resfmtFinalize(&qrf); + qrfResetStmt(&qrf); + qrfFinalize(&qrf); return qrf.iErr; } diff --git a/manifest b/manifest index 8a59437516..1a581d66c7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sva_list\smisuse\sfix. -D 2025-10-22T12:59:34.472 +C Code\sin\splace\sto\sdo\scolumn\sformats.\s\sCompiles,\sbut\sdoes\snot\swork.\nThis\sis\san\sincremental\scheck-in. +D 2025-10-22T18:09:54.467 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -417,7 +417,7 @@ F ext/misc/windirent.h 02211ce51f3034c675f2dbf4d228194d51b3ee05734678bad5106fff6 F ext/misc/zipfile.c 09e6e3a3ff40a99677de3c0bc6569bd5f4709b1844ac3d1c1452a456c5a62f1c F ext/misc/zorder.c bddff2e1b9661a90c95c2a9a9c7ecd8908afab5763256294dd12d609d4664eee F ext/qrf/qrf-tester.c 59cb5b1ed889ba57836d0fbb07b583fdfdfe409b1344f4f119fc13dff3a7356a -F ext/qrf/qrf.c aea2ed76f530818706ab99cc7b8bddc05725b581116dbfac87428215b65ea49d +F ext/qrf/qrf.c 8bd0adcf1eea3eb10d57efbbbc4dd25fc3783cd857db9543648177d0920db6aa F ext/qrf/qrf.h e9585fbadc45e8182faf208a0f86984d5130d89742364424fd1c39f12457f013 F ext/qrf/qrf.md 4eea619191dab7bbf483eff3fe3b074a07d7c8c50bc86070a4485797d386d1ff F ext/rbu/rbu.c 801450b24eaf14440d8fd20385aacc751d5c9d6123398df41b1b5aa804bf4ce8 @@ -2175,8 +2175,8 @@ F tool/version-info.c 33d0390ef484b3b1cb685d59362be891ea162123cea181cb8e6d2cf6dd F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 8646a2ee224819cf459989aa03bd1b9e1a77fc1f8bee8fd45dba778c63467d61 3d044121768548df59a94bbbe4ee360d6464d1416a5cbe7437f4333b06496381 -R a0cefa8d831fc9721f1ccf4dcb767699 +P e33ca68fee5bb6c5195f5a8fef231dcf08433806f5f44b855c2c670d37df6ac7 +R 4d8b08236a565a7b1259c528870b0576 U drh -Z a74d0a3608476365ed4fc9d5191c691c +Z c5eef447fa054545ce6feb7b549f988b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6c3f3c2154..70d8b6fd41 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e33ca68fee5bb6c5195f5a8fef231dcf08433806f5f44b855c2c670d37df6ac7 +a05e021354ff5868f75ac01d384c3aaaedb7c483dd8f9533f6ab9fbb66b5f077