--- /dev/null
- assert(na==1);
+/*
+** 2022-11-18
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+**
+** This is a SQLite extension for converting in either direction
+** between a (binary) blob and base64 text. Base64 can transit a
+** sane USASCII channel unmolested. It also plays nicely in CSV or
+** written as TCL brace-enclosed literals or SQL string literals,
+** and can be used unmodified in XML-like documents.
+**
+** This is an independent implementation of conversions specified in
+** RFC 4648, done on the above date by the author (Larry Brasfield)
+** who thereby has the right to put this into the public domain.
+**
+** The conversions meet RFC 4648 requirements, provided that this
+** C source specifies that line-feeds are included in the encoded
+** data to limit visible line lengths to 72 characters and to
+** terminate any encoded blob having non-zero length.
+**
+** Length limitations are not imposed except that the runtime
+** SQLite string or blob length limits are respected. Otherwise,
+** any length binary sequence can be represented and recovered.
+** Generated base64 sequences, with their line-feeds included,
+** can be concatenated; the result converted back to binary will
+** be the concatenation of the represented binary sequences.
+**
+** This SQLite3 extension creates a function, base64(x), which
+** either: converts text x containing base64 to a returned blob;
+** or converts a blob x to returned text containing base64. An
+** error will be thrown for other input argument types.
+**
+** This code relies on UTF-8 encoding only with respect to the
+** meaning of the first 128 (7-bit) codes matching that of USASCII.
+** It will fail miserably if somehow made to try to convert EBCDIC.
+** Because it is table-driven, it could be enhanced to handle that,
+** but the world and SQLite have moved on from that anachronism.
+**
+** To build the extension:
+** Set shell variable SQDIR=<your favorite SQLite checkout directory>
+** *Nix: gcc -O2 -shared -I$SQDIR -fPIC -o base64.so base64.c
+** OSX: gcc -O2 -dynamiclib -fPIC -I$SQDIR -o base64.dylib base64.c
+** Win32: gcc -O2 -shared -I%SQDIR% -o base64.dll base64.c
+** Win32: cl /Os -I%SQDIR% base64.c -link -dll -out:base64.dll
+*/
+
+#include <assert.h>
+
+#ifndef SQLITE_SHELL_EXTFUNCS /* Guard for #include as built-in extension. */
+#include "sqlite3ext.h"
+#endif
+
+SQLITE_EXTENSION_INIT1;
+
+#define PC 0x80 /* pad character */
+#define WS 0x81 /* whitespace */
+#define ND 0x82 /* Not above or digit-value */
+#define PAD_CHAR '='
+
+typedef unsigned char ubyte;
+
+static const ubyte b64DigitValues[128] = {
+ /* HT LF VT FF CR */
+ ND,ND,ND,ND, ND,ND,ND,ND, ND,WS,WS,WS, WS,WS,ND,ND,
+ /* US */
+ ND,ND,ND,ND, ND,ND,ND,ND, ND,ND,ND,ND, ND,ND,ND,ND,
+ /*sp + / */
+ WS,ND,ND,ND, ND,ND,ND,ND, ND,ND,ND,62, ND,ND,ND,63,
+ /* 0 1 5 9 = */
+ 52,53,54,55, 56,57,58,59, 60,61,ND,ND, ND,PC,ND,ND,
+ /* A O */
+ ND, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
+ /* P Z */
+ 15,16,17,18, 19,20,21,22, 23,24,25,ND, ND,ND,ND,ND,
+ /* a o */
+ ND,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
+ /* p z */
+ 41,42,43,44, 45,46,47,48, 49,50,51,ND, ND,ND,ND,ND
+};
+
+static const char b64Numerals[64+1]
+= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+#define BX_DV_PROTO(c) ((((ubyte)(c))<0x80)? b64DigitValues[c] : 0x80)
+#define IS_BX_DIGIT(bdp) (((ubyte)(bdp))<0x80)
+#define IS_BX_WS(bdp) ((bdp)==WS)
+#define IS_BX_PAD(bdp) ((bdp)==PC)
+#define BX_NUMERAL(dv) (b64Numerals[dv])
+/* Width of base64 lines. Should be an integer multiple of 4. */
+#define B64_DARK_MAX 72
+
+/* Encode a byte buffer into base64 text. If pSep!=0, it's a C string
+** to be appended to encoded groups to limit their length to B64_DARK_MAX
+** or to terminate the last group (to aid concatenation.)
+*/
+static char* toBase64( ubyte *pIn, int nbIn, char *pOut, char *pSep ){
+ int nCol = 0;
+ *pOut = 0;
+ while( nbIn > 0 ){
+ static signed char ncio[] = { 0, 2, 3, 4 };
+ int nbi = (nbIn > 3)? 3 : nbIn;
+ signed char nc;
+ int nbe;
+ unsigned long qv = (ubyte)*pIn++;
+ for( nbe=1; nbe<3; ++nbe ){
+ ubyte b = (nbe<nbi)? *pIn++ : 0;
+ qv = (qv<<8) | b;
+ }
+ nc = ncio[nbi];
+ nbIn -= nbi;
+ for( nbe=3; nbe>=0; --nbe ){
+ char ce = (nbe<nc)? BX_NUMERAL((ubyte)(qv & 0x3f)) : PAD_CHAR;
+ qv >>= 6;
+ pOut[nbe] = ce;
+ }
+ pOut += 4;
+ if( pSep && ((nCol += 4)>=B64_DARK_MAX || nbIn<=0) ){
+ char *p = pSep;
+ while( *p ) *pOut++ = *p++;
+ nCol = 0;
+ }
+ *pOut = 0;
+ }
+ return pOut;
+}
+
+/* Skip over text which is not base64 numeral(s). */
+static char * skipNonB64( char *s ){
+ char c;
+ while( (c = *s) && !IS_BX_DIGIT(BX_DV_PROTO(c)) ) ++s;
+ return s;
+}
+
+/* Decode base64 text into a byte buffer. */
+static ubyte* fromBase64( char *pIn, int ncIn, ubyte *pOut ){
+ if( ncIn>0 && pIn[ncIn-1]=='\n' ) --ncIn;
+ while( ncIn>0 && *pIn!=PAD_CHAR ){
+ static signed char nboi[] = { 0, 0, 1, 2, 3 };
+ char *pUse = skipNonB64(pIn);
+ unsigned long qv = 0L;
+ int nti, nbo, nac;
+ ncIn -= (pUse - pIn);
+ pIn = pUse;
+ nti = (ncIn>4)? 4 : ncIn;
+ ncIn -= nti;
+ nbo = nboi[nti];
+ if( nbo==0 ) break;
+ for( nac=0; nac<4; ++nac ){
+ char c = (nac<nti)? *pIn++ : b64Numerals[0];
+ ubyte bdp = BX_DV_PROTO(c);
+ switch( bdp ){
+ case ND:
+ /* Treat non-digits as pad, but they terminate decode too. */
+ ncIn = 0;
+ /* fall thru */
+ case WS:
+ /* Treat whitespace as pad */
+ /* fall thru */
+ case PC:
+ bdp = 0;
+ --nbo;
+ /* fall thru */
+ default: /* bdp is the digit value. */
+ qv = qv<<6 | bdp;
+ break;
+ }
+ }
+ nti = 2;
+ while( nbo-- > 0 ){
+ *pOut++ = (qv >> (8*nti--))&0xff;
+ }
+ }
+ return pOut;
+}
+
+/* This function does the work for the SQLite base64(x) UDF. */
+static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
+ int nb, nc, nv = sqlite3_value_bytes(av[0]);
+ int nvMax = sqlite3_limit(sqlite3_context_db_handle(context),
+ SQLITE_LIMIT_LENGTH, -1);
+ char *cBuf;
+ ubyte *bBuf;
- sqlite3_result_error(context, "blob expanded to base64 too big.", -1);
++ assert(na==1);
+ switch( sqlite3_value_type(av[0]) ){
+ case SQLITE_BLOB:
+ nb = nv;
+ nc = 4*(nv+2/3); /* quads needed */
+ nc += (nc+(B64_DARK_MAX-1))/B64_DARK_MAX + 1; /* LFs and a 0-terminator */
+ if( nvMax < nc ){
- sqlite3_result_error(context, "blob from base64 may be too big.", -1);
++ sqlite3_result_error(context, "blob expanded to base64 too big", -1);
++ return;
+ }
+ cBuf = sqlite3_malloc(nc);
+ if( !cBuf ) goto memFail;
+ bBuf = (ubyte*)sqlite3_value_blob(av[0]);
+ nc = (int)(toBase64(bBuf, nb, cBuf, "\n") - cBuf);
+ sqlite3_result_text(context, cBuf, nc, sqlite3_free);
+ break;
+ case SQLITE_TEXT:
+ nc = nv;
+ nb = 3*((nv+3)/4); /* may overestimate due to LF and padding */
+ if( nvMax < nb ){
- sqlite3_result_error(context, "base64 accepts only blob or text.", -1);
- break;
++ sqlite3_result_error(context, "blob from base64 may be too big", -1);
++ return;
+ }else if( nb<1 ){
+ nb = 1;
+ }
+ bBuf = sqlite3_malloc(nb);
+ if( !bBuf ) goto memFail;
+ cBuf = (char *)sqlite3_value_text(av[0]);
+ nb = (int)(fromBase64(cBuf, nc, bBuf) - bBuf);
+ sqlite3_result_blob(context, bBuf, nb, sqlite3_free);
+ break;
+ default:
++ sqlite3_result_error(context, "base64 accepts only blob or text", -1);
++ return;
+ }
+ return;
+ memFail:
+ sqlite3_result_error(context, "base64 OOM", -1);
+}
+
+/*
+** Establish linkage to running SQLite library.
+*/
+#ifndef SQLITE_SHELL_EXTFUNCS
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_base_init
+#else
+static int sqlite3_base64_init
+#endif
+(sqlite3 *db, char **pzErr, const sqlite3_api_routines *pApi){
+ SQLITE_EXTENSION_INIT2(pApi);
+ (void)pzErr;
+ return sqlite3_create_function
+ (db, "base64", 1,
+ SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|SQLITE_DIRECTONLY|SQLITE_UTF8,
+ 0, base64, 0, 0);
+}
+
+/*
+** Define some macros to allow this extension to be built into the shell
+** conveniently, in conjunction with use of SQLITE_SHELL_EXTFUNCS. This
+** allows shell.c, as distributed, to have this extension built in.
+*/
+#define BASE64_INIT(db) sqlite3_base64_init(db, 0, 0)
+#define BASE64_EXPOSE(db, pzErr) /* Not needed, ..._init() does this. */
--- /dev/null
- sqlite3_result_error(context, "is_base85 accepts only text or NULL.", -1);
- break;
+/*
+** 2022-11-16
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+**
+** This is a utility for converting binary to base85 or vice-versa.
+** It can be built as a standalone program or an SQLite3 extension.
+**
+** Much like base64 representations, base85 can be sent through a
+** sane USASCII channel unmolested. It also plays nicely in CSV or
+** written as TCL brace-enclosed literals or SQL string literals.
+** It is not suited for unmodified use in XML-like documents.
+**
+** The encoding used resembles Ascii85, but was devised by the author
+** (Larry Brasfield) before Mozilla, Adobe, ZMODEM or other Ascii85
+** variant sources existed, in the 1984 timeframe on a VAX mainframe.
+** Further, this is an independent implementation of a base85 system.
+** Hence, the author has rightfully put this into the public domain.
+**
+** Base85 numerals are taken from the set of 7-bit USASCII codes,
+** excluding control characters and Space ! " ' ( ) { | } ~ Del
+** in code order representing digit values 0 to 84 (base 10.)
+**
+** Groups of 4 bytes, interpreted as big-endian 32-bit values,
+** are represented as 5-digit base85 numbers with MS to LS digit
+** order. Groups of 1-3 bytes are represented with 2-4 digits,
+** still big-endian but 8-24 bit values. (Using big-endian yields
+** the simplest transition to byte groups smaller than 4 bytes.
+** These byte groups can also be considered base-256 numbers.)
+** Groups of 0 bytes are represented with 0 digits and vice-versa.
+** No pad characters are used; Encoded base85 numeral sequence
+** (aka "group") length maps 1-to-1 to the decoded binary length.
+**
+** Any character not in the base85 numeral set delimits groups.
+** When base85 is streamed or stored in containers of indefinite
+** size, newline is used to separate it into sub-sequences of no
+** more than 80 digits so that fgets() can be used to read it.
+**
+** Length limitations are not imposed except that the runtime
+** SQLite string or blob length limits are respected. Otherwise,
+** any length binary sequence can be represented and recovered.
+** Base85 sequences can be concatenated by separating them with
+** a non-base85 character; the conversion to binary will then
+** be the concatenation of the represented binary sequences.
+
+** The standalone program either converts base85 on stdin to create
+** a binary file or converts a binary file to base85 on stdout.
+** Read or make it blurt its help for invocation details.
+**
+** The SQLite3 extension creates a function, base85(x), which will
+** either convert text base85 to a blob or a blob to text base85
+** and return the result (or throw an error for other types.)
+** Unless built with OMIT_BASE85_CHECKER defined, it also creates a
+** function, is_base85(t), which returns 1 iff the text t contains
+** nothing other than base85 numerals and whitespace, or 0 otherwise.
+**
+** To build the extension:
+** Set shell variable SQDIR=<your favorite SQLite checkout directory>
+** and variable OPTS to -DOMIT_BASE85_CHECKER if is_base85() unwanted.
+** *Nix: gcc -O2 -shared -I$SQDIR $OPTS -fPIC -o base85.so base85.c
+** OSX: gcc -O2 -dynamiclib -fPIC -I$SQDIR $OPTS -o base85.dylib base85.c
+** Win32: gcc -O2 -shared -I%SQDIR% %OPTS% -o base85.dll base85.c
+** Win32: cl /Os -I%SQDIR% %OPTS% base85.c -link -dll -out:base85.dll
+**
+** To build the standalone program, define PP symbol BASE85_STANDALONE. Eg.
+** *Nix or OSX: gcc -O2 -DBASE85_STANDALONE base85.c -o base85
+** Win32: gcc -O2 -DBASE85_STANDALONE -o base85.exe base85.c
+** Win32: cl /Os /MD -DBASE85_STANDALONE base85.c
+*/
+
+#include <stdio.h>
+#include <memory.h>
+#include <string.h>
+#include <assert.h>
+#ifndef OMIT_BASE85_CHECKER
+# include <ctype.h>
+#endif
+
+#ifndef BASE85_STANDALONE
+
+#ifndef SQLITE_SHELL_EXTFUNCS /* Guard for #include as built-in extension. */
+# include "sqlite3ext.h"
+#endif
+
+SQLITE_EXTENSION_INIT1;
+
+#else
+
+# ifdef _WIN32
+# include <io.h>
+# include <fcntl.h>
+# else
+# define setmode(fd,m)
+# endif
+
+static char *zHelp =
+ "Usage: base85 <dirFlag> <binFile>\n"
+ " <dirFlag> is either -r to read or -w to write <binFile>,\n"
+ " content to be converted to/from base85 on stdout/stdin.\n"
+ " <binFile> names a binary file to be rendered or created.\n"
+ " Or, the name '-' refers to the stdin or stdout stream.\n"
+ ;
+
+static void sayHelp(){
+ printf("%s", zHelp);
+}
+#endif
+
+/* Classify c according to interval within USASCII set w.r.t. base85
+ * Values of 1 and 3 are base85 numerals. Values of 0, 2, or 4 are not.
+ */
+#define B85_CLASS( c ) (((c)>='#')+((c)>'&')+((c)>='*')+((c)>'z'))
+
+/* Provide digitValue to b85Numeral offset as a function of above class. */
+static unsigned char b85_cOffset[] = { 0, '#', 0, '*'-4, 0 };
+#define B85_DNOS( c ) b85_cOffset[B85_CLASS(c)]
+
+/* Say whether c is a base85 numeral. */
+#define IS_B85( c ) (B85_CLASS(c) & 1)
+
+#if 0 /* Not used, */
+static unsigned char base85DigitValue( char c ){
+ unsigned char dv = (unsigned char)(c - '#');
+ if( dv>87 ) return 0xff;
+ return (dv > 3)? dv-3 : dv;
+}
+#endif
+
+/* Width of base64 lines. Should be an integer multiple of 5. */
+#define B85_DARK_MAX 80
+
+
+static char * skipNonB85( char *s ){
+ char c;
+ while( (c = *s) && !IS_B85(c) ) ++s;
+ return s;
+}
+
+/* Convert small integer, known to be in 0..84 inclusive, to base85 numeral.*/
+static char base85Numeral( unsigned char b ){
+ return (b < 4)? (char)(b + '#') : (char)(b - 4 + '*');
+}
+
+/* Encode a byte buffer into base85 text. If pSep!=0, it's a C string
+** to be appended to encoded groups to limit their length to B85_DARK_MAX
+** or to terminate the last group (to aid concatenation.)
+*/
+static char* toBase85( unsigned char *pIn, int nbIn, char *pOut, char *pSep ){
+ int nCol = 0;
+ *pOut = 0;
+ while( nbIn > 0 ){
+ static signed char ncio[] = { 0, 2, 3, 4, 5 };
+ int nbi = (nbIn > 4)? 4 : nbIn;
+ unsigned long qv = 0L;
+ int nbe = 0;
+ signed char nco;
+ while( nbe++ < nbi ){
+ qv = (qv<<8) | *pIn++;
+ }
+ nco = ncio[nbi];
+ nbIn -= nbi;
+ while( nco > 0 ){
+ unsigned char dv = (unsigned char)(qv % 85);
+ qv /= 85;
+ pOut[--nco] = base85Numeral(dv);
+ }
+ pOut += ncio[nbi];
+ if( pSep && ((nCol += ncio[nbi])>=B85_DARK_MAX || nbIn<=0) ){
+ char *p = pSep;
+ while( *p ) *pOut++ = *p++;
+ nCol = 0;
+ }
+ *pOut = 0;
+ }
+ return pOut;
+}
+
+/* Decode base85 text into a byte buffer. */
+static unsigned char* fromBase85( char *pIn, int ncIn, unsigned char *pOut ){
+ if( ncIn>0 && pIn[ncIn-1]=='\n' ) --ncIn;
+ while( ncIn>0 ){
+ static signed char nboi[] = { 0, 0, 1, 2, 3, 4 };
+ char *pUse = skipNonB85(pIn);
+ unsigned long qv = 0L;
+ int nti, nbo;
+ ncIn -= (pUse - pIn);
+ if( ncIn==0 ) break;
+ pIn = pUse;
+ nti = (ncIn>5)? 5 : ncIn;
+ nbo = nboi[nti];
+ while( nti>0 ){
+ char c = *pIn++;
+ unsigned char cdo = B85_DNOS(c);
+ --ncIn;
+ if( cdo==0 ) break;
+ qv = 85 * qv + c - cdo;
+ --nti;
+ }
+ nbo -= nti;
+ while( nbo-- > 0 ){
+ *pOut++ = (qv >> (8*nbo))&0xff;
+ }
+ }
+ return pOut;
+}
+
+#ifndef OMIT_BASE85_CHECKER
+/* Say whether input char sequence is all (base85 and/or whitespace).*/
+static int allBase85( char *p, int len ){
+ char c;
+ while( len-- > 0 && (c = *p++) != 0 ){
+ if( !IS_B85(c) && !isspace(c) ) return 0;
+ }
+ return 1;
+}
+#endif
+
+#ifndef BASE85_STANDALONE
+
+# ifndef OMIT_BASE85_CHECKER
+/* This function does the work for the SQLite is_base85(t) UDF. */
+static void is_base85(sqlite3_context *context, int na, sqlite3_value *av[]){
+ assert(na==1);
+ switch( sqlite3_value_type(av[0]) ){
+ case SQLITE_TEXT:
+ {
+ int rv = allBase85( (char *)sqlite3_value_text(av[0]),
+ sqlite3_value_bytes(av[0]) );
+ sqlite3_result_int(context, rv);
+ }
+ break;
+ case SQLITE_NULL:
+ sqlite3_result_null(context);
+ break;
+ default:
- sqlite3_result_error(context, "blob expanded to base85 too big.", -1);
++ sqlite3_result_error(context, "is_base85 accepts only text or NULL", -1);
++ return;
+ }
+}
+# endif
+
+/* This function does the work for the SQLite base85(x) UDF. */
+static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
+ int nb, nc, nv = sqlite3_value_bytes(av[0]);
+ int nvMax = sqlite3_limit(sqlite3_context_db_handle(context),
+ SQLITE_LIMIT_LENGTH, -1);
+ char *cBuf;
+ unsigned char *bBuf;
+ assert(na==1);
+ switch( sqlite3_value_type(av[0]) ){
+ case SQLITE_BLOB:
+ nb = nv;
+ /* ulongs tail newlines tailenc+nul*/
+ nc = 5*(nv/4) + nv%4 + nv/64+1 + 2;
+ if( nvMax < nc ){
- sqlite3_result_error(context, "blob from base85 may be too big.", -1);
++ sqlite3_result_error(context, "blob expanded to base85 too big", -1);
++ return;
+ }
+ cBuf = sqlite3_malloc(nc);
+ if( !cBuf ) goto memFail;
+ bBuf = (unsigned char*)sqlite3_value_blob(av[0]);
+ nc = (int)(toBase85(bBuf, nb, cBuf, "\n") - cBuf);
+ sqlite3_result_text(context, cBuf, nc, sqlite3_free);
+ break;
+ case SQLITE_TEXT:
+ nc = nv;
+ nb = 4*(nv/5) + nv%5; /* may overestimate */
+ if( nvMax < nb ){
- break;
++ sqlite3_result_error(context, "blob from base85 may be too big", -1);
++ return;
+ }else if( nb<1 ){
+ nb = 1;
+ }
+ bBuf = sqlite3_malloc(nb);
+ if( !bBuf ) goto memFail;
+ cBuf = (char *)sqlite3_value_text(av[0]);
+ nb = (int)(fromBase85(cBuf, nc, bBuf) - bBuf);
+ sqlite3_result_blob(context, bBuf, nb, sqlite3_free);
+ break;
+ default:
+ sqlite3_result_error(context, "base85 accepts only blob or text.", -1);
++ return;
+ }
+ return;
+ memFail:
+ sqlite3_result_error(context, "base85 OOM", -1);
+}
+
+/*
+** Establish linkage to running SQLite library.
+*/
+#ifndef SQLITE_SHELL_EXTFUNCS
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_base_init
+#else
+static int sqlite3_base85_init
+#endif
+(sqlite3 *db, char **pzErr, const sqlite3_api_routines *pApi){
+ SQLITE_EXTENSION_INIT2(pApi);
+ (void)pzErr;
+# ifndef OMIT_BASE85_CHECKER
+ {
+ int rc = sqlite3_create_function
+ (db, "is_base85", 1,
+ SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|SQLITE_UTF8,
+ 0, is_base85, 0, 0);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+# endif
+ return sqlite3_create_function
+ (db, "base85", 1,
+ SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|SQLITE_DIRECTONLY|SQLITE_UTF8,
+ 0, base85, 0, 0);
+}
+
+/*
+** Define some macros to allow this extension to be built into the shell
+** conveniently, in conjunction with use of SQLITE_SHELL_EXTFUNCS. This
+** allows shell.c, as distributed, to have this extension built in.
+*/
+# define BASE85_INIT(db) sqlite3_base85_init(db, 0, 0)
+# define BASE85_EXPOSE(db, pzErr) /* Not needed, ..._init() does this. */
+
+#else /* standalone program */
+
+int main(int na, char *av[]){
+ int cin;
+ int rc = 0;
+ unsigned char bBuf[4*(B85_DARK_MAX/5)];
+ char cBuf[5*(sizeof(bBuf)/4)+2];
+ size_t nio;
+# ifndef OMIT_BASE85_CHECKER
+ int b85Clean = 1;
+# endif
+ char rw;
+ FILE *fb = 0, *foc = 0;
+ char fmode[3] = "xb";
+ if( na < 3 || av[1][0]!='-' || (rw = av[1][1])==0 || (rw!='r' && rw!='w') ){
+ sayHelp();
+ return 0;
+ }
+ fmode[0] = rw;
+ if( av[2][0]=='-' && av[2][1]==0 ){
+ switch( rw ){
+ case 'r':
+ fb = stdin;
+ setmode(fileno(stdin), O_BINARY);
+ break;
+ case 'w':
+ fb = stdout;
+ setmode(fileno(stdout), O_BINARY);
+ break;
+ }
+ }else{
+ fb = fopen(av[2], fmode);
+ foc = fb;
+ }
+ if( !fb ){
+ fprintf(stderr, "Cannot open %s for %c\n", av[2], rw);
+ rc = 1;
+ }else{
+ switch( rw ){
+ case 'r':
+ while( (nio = fread( bBuf, 1, sizeof(bBuf), fb))>0 ){
+ toBase85( bBuf, (int)nio, cBuf, 0 );
+ fprintf(stdout, "%s\n", cBuf);
+ }
+ break;
+ case 'w':
+ while( 0 != fgets(cBuf, sizeof(cBuf), stdin) ){
+ int nc = strlen(cBuf);
+ size_t nbo = fromBase85( cBuf, nc, bBuf ) - bBuf;
+ if( 1 != fwrite(bBuf, nbo, 1, fb) ) rc = 1;
+# ifndef OMIT_BASE85_CHECKER
+ b85Clean &= allBase85( cBuf, nc );
+# endif
+ }
+ break;
+ default:
+ sayHelp();
+ rc = 1;
+ }
+ if( foc ) fclose(foc);
+ }
+# ifndef OMIT_BASE85_CHECKER
+ if( !b85Clean ){
+ fprintf(stderr, "Base85 input had non-base85 dark or control content.\n");
+ }
+# endif
+ return rc;
+}
+
+#endif
- C Sync\sw/trunk.\sReady\sfor\smerge\sto\strunk.
- D 2022-11-22T19:12:24.820
-C Avoid\snaming\scollision\sbetween\sthe\ssha1\sand\sshathree\sextensions.
-D 2022-11-22T20:04:00.704
++C Add\smore\sbaseNN\stests,\sget\soversize\serror\strapping\sworking,\sand\ssync\sw/trunk
++D 2022-11-22T22:46:41.866
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F ext/misc/amatch.c e3ad5532799cee9a97647f483f67f43b38796b84b5a8c60594fe782a4338f358
F ext/misc/anycollseq.c 5ffdfde9829eeac52219136ad6aa7cd9a4edb3b15f4f2532de52f4a22525eddb
F ext/misc/appendvfs.c 9642c7a194a2a25dca7ad3e36af24a0a46d7702168c4ad7e59c9f9b0e16a3824
- F ext/misc/base64.c e18d1d35c5eaadb189aff7b5abe7d73655f46396031ce006d24c5fa2f7ee74e8
- F ext/misc/base85.c 2c680ca7733f9a86f5d292fec71d10777290e68e7ae59d90597ae75fc44a88b6
++F ext/misc/base64.c 0472f388e1a6c168912a363dfbe88d2d588325fba7ce6101f726d01b54fe6d3b
++F ext/misc/base85.c 9005549904fc06ec2f3ff96970709f92f76e2d9ec2b785553ac32908ddc1baa0
+F ext/misc/basexx.c 678dcc83894f78c26fd3662b322886777cc26bf2b40809236cd2abdad532a33c
F ext/misc/blobio.c a867c4c4617f6ec223a307ebfe0eabb45e0992f74dd47722b96f3e631c0edb2a
F ext/misc/btreeinfo.c d28ce349b40054eaa9473e835837bad7a71deec33ba13e39f963d50933bfa0f9
F ext/misc/carray.c b752f46411e4e47e34dce6f0c88bc8e51bb821ba9e49bfcd882506451c928f69
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
F src/resolve.c efea4e5fbecfd6d0a9071b0be0d952620991673391b6ffaaf4c277b0bb674633
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
- F src/select.c 3fc60a8f0b54db15e86584c5fd68dbf63c20fe86886a39267ce7dfc17b68853d
+ F src/select.c 4c48373abb4e67129c36bc15d1f5a99a0dfd9534afeb539a2169a09ae91ccec9
-F src/shell.c.in 7d1705f139e6762e8c0fe254a8ebf3ab77aec6d8366f033cdd5f5ebadefbbb20
+F src/shell.c.in 6bb8e3b54f079e5373199e2fb0ebe8acf5cd4dcac4bbfe2467b1cddbf15ced80
F src/sqlite.h.in 100fc660c2f19961b8ed8437b9d53d687de2f8eb2b96437ec6da216adcb643ca
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h c4b9fa7a7e2bcdf850cfeb4b8a91d5ec47b7a00033bc996fd2ee96cbf2741f5f
F test/backup_malloc.test 0c9abdf74c51e7bedb66d504cd684f28d4bd4027
F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
F test/badutf2.test f310fd3b24a491b6b77bccdf14923b85d6ebcce751068c180d93a6b8ff854399
- F test/basexx1.test 46454750d40888174eebcd4206c9d16cfeb7a0ffa3f04a00ab2e69a6140f1587
++F test/basexx1.test 9b12557d2b5bd017f9f8a8698239438ced5899c3ee55d9a549f74d90b16e51a6
F test/bc_common.tcl b5e42d80305be95697e6370e015af571e5333a1c
F test/bestindex1.test 856a453dff8c68b4568601eed5a8b5e20b4763af9229f3947c215729ed878db0
F test/bestindex2.test 394ff8fbf34703391247116d6a44e1c50ee7282236ee77909044573cefc37bc0
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
- P 4e4334547795f659b4a5dc5fdaf842535643750a5f1ce1af799c526931a473e4 861af465fda8485dfff143dc45c659b884d826aaec2ebaa941566404d1fe427b
- R cae00067ec49a50025b1793060d59681
-P db07471c531766a8eec1d5b41c9fd0283b5e64ee13166dc3391f70a1e1946121
-R 028f4214066f285c3501b4fa202e9f4b
-U mistachkin
-Z 9f8febbd78f0b476308550c8b424fd90
++P ff67460e1a3d21c9ca7cbd171fbc5e6cbdb3797de359887f851776b73b732fdf 9ec923b5dc24d6082da8d42bc0ee8ab1c418912625c0c56de9627be2c818ef98
++R 36565c16629af64673142ebb24820a85
+U larrybr
- Z d8609990d08ea7644109ee0cd1dbe1fb
++Z 3e47c0e49ec42d7b0a58333b563577b4
# Remove this line to create a well-formed Fossil manifest.
- ff67460e1a3d21c9ca7cbd171fbc5e6cbdb3797de359887f851776b73b732fdf
-9ec923b5dc24d6082da8d42bc0ee8ab1c418912625c0c56de9627be2c818ef98
++03819e9368fd9f78f351147a1dc865743f9634893e43a9d1e3d7cbaf4c966069
--- /dev/null
- # ToDo: See if extension code for this can be eliminated.
+# 2022 November 22
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix basexx
+
+if {[catch {load_static_extension db basexx} error]} {
+ puts "Skipping basexx tests, hit load error: $error"
+ finish_test; return
+}
+
+# Empty blobs encode to empty strings.
+do_execsql_test 100 {
+ SELECT base64(x'')||base85(x'');
+} {{}}
+
+# Empty strings decode to empty blobs.
+do_execsql_test 101 {
+ SELECT hex(x'01'||base64('')||base85('')||x'02');
+} {0102}
+
+# Basic base64 encoding
+do_execsql_test 102 {
+ SELECT base64(x'000102030405');
+ SELECT base64(x'0001020304');
+ SELECT base64(x'00010203');
+} {{AAECAwQF
+} {AAECAwQ=
+} {AAECAw==
+}}
+
+# Basic base64 decoding with pad chars
+do_execsql_test 103 {
+ SELECT hex(base64('AAECAwQF'));
+ SELECT hex(base64('AAECAwQ='));
+ SELECT hex(base64('AAECAw=='));
+} {000102030405 0001020304 00010203}
+
+# Basic base64 decoding without pad chars and with whitespace
+do_execsql_test 104 {
+ SELECT hex(base64(' AAECAwQF '));
+ SELECT hex(base64(' AAECAwQ'));
+ SELECT hex(base64('AAECAw '));
+} {000102030405 0001020304 00010203}
+
+# Basic base85 encoding
+do_execsql_test 105 {
+ SELECT base85(x'000102030405');
+ SELECT base85(x'0001020304');
+ SELECT base85(x'00010203');
+} {{##/2,#2/
+} {##/2,#*
+} {##/2,
+}}
+
+# Basic base85 decoding with and without whitespace
+do_execsql_test 106 {
+ SELECT hex(base85('##/2,#2/'));
+ SELECT hex(base85('##/2,#*'));
+ SELECT hex(base85('##/2,'));
+ SELECT hex(base85(' ##/2,#2/ '));
+ SELECT hex(base85(' ##/2,#*'));
+ SELECT hex(base85('##/2, '));
+} {000102030405 0001020304 00010203 000102030405 0001020304 00010203}
+
+# Round-trip some random blobs.
+do_execsql_test 107 {
+ CREATE TEMP TABLE rb( len int, b blob ) STRICT;
+ INSERT INTO rb(len) VALUES (1),(2),(3),(4),(5),(150),(151),(152),(153),(1054);
+ UPDATE rb SET b = randomblob(len);
+ SELECT len, base64(base64(b))=b, base85(base85(b))=b
+ FROM rb ORDER BY len;
+} {1 1 1 2 1 1 3 1 1 4 1 1 5 1 1 150 1 1 151 1 1 152 1 1 153 1 1 1054 1 1}
+
+# Same round-trip but with space or junk prepended and/or appended or not.
+do_execsql_test 108 {
+ CREATE TEMP TABLE junk(j text, rank int);
+ INSERT INTO junk VALUES ('',0),(' ',1),('~',2);
+ SELECT len, base64(j.j||base64(b)||j.j)=b, base85(j.j||base85(b)||j.j)=b
+ FROM rb r, junk j WHERE j.rank=(r.len+r.len/25)%3 ORDER BY len;
+} {1 1 1 2 1 1 3 1 1 4 1 1 5 1 1 150 1 1 151 1 1 152 1 1 153 1 1 1054 1 1}
+
+# Exercise the fail-on-too-large result feature.
- sqlite3_limit db SQLITE_LIMIT_LENGTH 1060
+
+set inLimit [sqlite3_limit db SQLITE_LIMIT_LENGTH -1]
- } {1 {string or blob too big}}
++sqlite3_limit db SQLITE_LIMIT_LENGTH 1300
+
+do_catchsql_test 109 {
+ SELECT len, base64(b) FROM rb WHERE len>200;
- } {1 {string or blob too big}}
++} {1 {blob expanded to base64 too big}}
+
+do_catchsql_test 110 {
+ SELECT len, base85(b) FROM rb WHERE len>200;
- do_execsql_test 111 {
- SELECT is_base85(base85(x'123456')), is_base85('~');
- } {1 0}
++} {1 {blob expanded to base85 too big}}
++
++do_catchsql_test 111 {
++ SELECT length(base85(b))=1335 FROM rb WHERE len=1054;
++} {1 {blob expanded to base85 too big}}
+
+sqlite3_limit db SQLITE_LIMIT_LENGTH $inLimit
+
++# Exercise is_base85(t)
++
++do_execsql_test 112 {
++ SELECT is_base85(' '||base85(x'123456')||char(10)),
++ is_base85('#$%&*+,-./0123456789:;<=>?@'
++ ||'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
++ ||'[\]^_`'
++ ||'abcdefghijklmnopqrstuvwxyz'),
++ is_base85('!'), is_base85('"'), is_base85(''''), is_base85('('),
++ is_base85(')'), is_base85(char(123)), is_base85('|'), is_base85(char(125)),
++ is_base85('~'), is_base85(char(127));
++} {1 1 0 0 0 0 0 0 0 0 0 0}
++
++do_execsql_test 113 {
++ SELECT is_base85(NULL) IS NULL;
++} {1}
++
++do_catchsql_test 114 {
++ SELECT is_base85(1);
++} {1 {is_base85 accepts only text or NULL}}
++
++do_catchsql_test 115 {
++ SELECT is_base85(1.1);
++} {1 {is_base85 accepts only text or NULL}}
++
++do_catchsql_test 116 {
++ SELECT is_base85(x'00');
++} {1 {is_base85 accepts only text or NULL}}
+
+finish_test