Add the ".ar" command to the command-line shell.
FossilOrigin-Name: 148b8aee78e40cab9a758a920589bd3ca8fc1c45cc93598bc50d96b85cd17e6c
This commit is contained in:
commit
b2b7274e10
1
.fossil-settings/empty-dirs
Normal file
1
.fossil-settings/empty-dirs
Normal file
@ -0,0 +1 @@
|
||||
compat
|
1
.fossil-settings/ignore-glob
Normal file
1
.fossil-settings/ignore-glob
Normal file
@ -0,0 +1 @@
|
||||
compat/*
|
@ -994,7 +994,12 @@ SHELL_SRC = \
|
||||
$(TOP)/src/shell.c.in \
|
||||
$(TOP)/ext/misc/shathree.c \
|
||||
$(TOP)/ext/misc/fileio.c \
|
||||
$(TOP)/ext/misc/completion.c
|
||||
$(TOP)/ext/misc/completion.c \
|
||||
$(TOP)/ext/misc/sqlar.c \
|
||||
$(TOP)/ext/expert/sqlite3expert.c \
|
||||
$(TOP)/ext/expert/sqlite3expert.h \
|
||||
$(TOP)/ext/misc/zipfile.c \
|
||||
$(TOP)/src/test_windirent.c
|
||||
|
||||
shell.c: $(SHELL_SRC) $(TOP)/tool/mkshellc.tcl
|
||||
$(TCLSH_CMD) $(TOP)/tool/mkshellc.tcl >shell.c
|
||||
|
137
Makefile.msc
137
Makefile.msc
@ -92,6 +92,29 @@ SPLIT_AMALGAMATION = 0
|
||||
!ENDIF
|
||||
|
||||
# <<mark>>
|
||||
# Set this non-0 to have this makefile assume the Tcl shell executable
|
||||
# (tclsh*.exe) is available in the PATH. By default, this is disabled
|
||||
# for compatibility with older build environments. This setting only
|
||||
# applies if TCLSH_CMD is not set manually.
|
||||
#
|
||||
!IFNDEF USE_TCLSH_IN_PATH
|
||||
USE_TCLSH_IN_PATH = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to use zlib, possibly compiling it from source code.
|
||||
#
|
||||
!IFNDEF USE_ZLIB
|
||||
USE_ZLIB = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to build zlib from source code. This is enabled by
|
||||
# default and in that case it will be assumed that the ZLIBDIR macro
|
||||
# points to the top-level source code directory for zlib.
|
||||
#
|
||||
!IFNDEF BUILD_ZLIB
|
||||
BUILD_ZLIB = 1
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to use the International Components for Unicode (ICU).
|
||||
#
|
||||
!IFNDEF USE_ICU
|
||||
@ -612,6 +635,15 @@ SHELL_CORE_DEP =
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
# <<mark>>
|
||||
# If zlib support is enabled, add the dependencies for it.
|
||||
#
|
||||
!IF $(USE_ZLIB)!=0 && $(BUILD_ZLIB)!=0
|
||||
SHELL_CORE_DEP = zlib $(SHELL_CORE_DEP)
|
||||
TESTFIXTURE_DEP = zlib $(TESTFIXTURE_DEP)
|
||||
!ENDIF
|
||||
# <</mark>>
|
||||
|
||||
# This is the core library that the shell executable should link with.
|
||||
#
|
||||
!IFNDEF SHELL_CORE_LIB
|
||||
@ -802,12 +834,16 @@ RCC = $(RCC) -DSQLITE_WIN32_MALLOC_VALIDATE=1
|
||||
# prior to running nmake in order to match the actual installed location and
|
||||
# version on this machine.
|
||||
#
|
||||
!IFNDEF TCLDIR
|
||||
TCLDIR = $(TOP)\compat\tcl
|
||||
!ENDIF
|
||||
|
||||
!IFNDEF TCLINCDIR
|
||||
TCLINCDIR = c:\tcl\include
|
||||
TCLINCDIR = $(TCLDIR)\include
|
||||
!ENDIF
|
||||
|
||||
!IFNDEF TCLLIBDIR
|
||||
TCLLIBDIR = c:\tcl\lib
|
||||
TCLLIBDIR = $(TCLDIR)\lib
|
||||
!ENDIF
|
||||
|
||||
!IFNDEF LIBTCL
|
||||
@ -819,7 +855,32 @@ LIBTCLSTUB = tclstub86.lib
|
||||
!ENDIF
|
||||
|
||||
!IFNDEF LIBTCLPATH
|
||||
LIBTCLPATH = c:\tcl\bin
|
||||
LIBTCLPATH = $(TCLDIR)\bin
|
||||
!ENDIF
|
||||
|
||||
# The locations of the zlib header and library files. These variables
|
||||
# (ZLIBINCDIR, ZLIBLIBDIR, and ZLIBLIB) may be overridden via the environment
|
||||
# prior to running nmake in order to match the actual installed (or source
|
||||
# code) location on this machine.
|
||||
#
|
||||
!IFNDEF ZLIBDIR
|
||||
ZLIBDIR = $(TOP)\compat\zlib
|
||||
!ENDIF
|
||||
|
||||
!IFNDEF ZLIBINCDIR
|
||||
ZLIBINCDIR = $(ZLIBDIR)
|
||||
!ENDIF
|
||||
|
||||
!IFNDEF ZLIBLIBDIR
|
||||
ZLIBLIBDIR = $(ZLIBDIR)
|
||||
!ENDIF
|
||||
|
||||
!IFNDEF ZLIBLIB
|
||||
!IF $(DYNAMIC_SHELL)!=0
|
||||
ZLIBLIB = zdll.lib
|
||||
!ELSE
|
||||
ZLIBLIB = zlib.lib
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
# The locations of the ICU header and library files. These variables
|
||||
@ -827,12 +888,16 @@ LIBTCLPATH = c:\tcl\bin
|
||||
# prior to running nmake in order to match the actual installed location on
|
||||
# this machine.
|
||||
#
|
||||
!IFNDEF ICUDIR
|
||||
ICUDIR = $(TOP)\compat\icu
|
||||
!ENDIF
|
||||
|
||||
!IFNDEF ICUINCDIR
|
||||
ICUINCDIR = c:\icu\include
|
||||
ICUINCDIR = $(ICUDIR)\include
|
||||
!ENDIF
|
||||
|
||||
!IFNDEF ICULIBDIR
|
||||
ICULIBDIR = c:\icu\lib
|
||||
ICULIBDIR = $(ICUDIR)\lib
|
||||
!ENDIF
|
||||
|
||||
!IFNDEF LIBICU
|
||||
@ -845,7 +910,11 @@ LIBICU = icuuc.lib icuin.lib
|
||||
# specific Tcl shell to use.
|
||||
#
|
||||
!IFNDEF TCLSH_CMD
|
||||
!IF $(USE_TCLSH_IN_PATH)!=0 || !EXIST("$(TCLDIR)\bin\tclsh.exe")
|
||||
TCLSH_CMD = tclsh
|
||||
!ELSE
|
||||
TCLSH_CMD = $(TCLDIR)\bin\tclsh.exe
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
# <</mark>>
|
||||
|
||||
@ -951,6 +1020,15 @@ BCC = $(BCC) -Zi
|
||||
!ENDIF
|
||||
|
||||
# <<mark>>
|
||||
# If zlib support is enabled, add the compiler options for it.
|
||||
#
|
||||
!IF $(USE_ZLIB)!=0
|
||||
TCC = $(TCC) -DSQLITE_HAVE_ZLIB=1
|
||||
RCC = $(RCC) -DSQLITE_HAVE_ZLIB=1
|
||||
TCC = $(TCC) -I$(ZLIBINCDIR)
|
||||
RCC = $(RCC) -I$(ZLIBINCDIR)
|
||||
!ENDIF
|
||||
|
||||
# If ICU support is enabled, add the compiler options for it.
|
||||
#
|
||||
!IF $(USE_ICU)!=0
|
||||
@ -1071,8 +1149,15 @@ LDFLAGS = $(LDOPTS)
|
||||
# Start with the Tcl related linker options.
|
||||
#
|
||||
!IF $(NO_TCL)==0
|
||||
LTLIBPATHS = /LIBPATH:$(TCLLIBDIR)
|
||||
LTLIBS = $(LTLIBS) $(LIBTCL)
|
||||
TCLLIBPATHS = $(TCLLIBPATHS) /LIBPATH:$(TCLLIBDIR)
|
||||
TCLLIBS = $(TCLLIBS) $(LIBTCL)
|
||||
!ENDIF
|
||||
|
||||
# If zlib support is enabled, add the linker options for it.
|
||||
#
|
||||
!IF $(USE_ZLIB)!=0
|
||||
LTLIBPATHS = $(LTLIBPATHS) /LIBPATH:$(ZLIBLIBDIR)
|
||||
LTLIBS = $(LTLIBS) $(ZLIBLIB)
|
||||
!ENDIF
|
||||
|
||||
# If ICU support is enabled, add the linker options for it.
|
||||
@ -1424,6 +1509,12 @@ TESTEXT = \
|
||||
$(TOP)\ext\misc\unionvtab.c \
|
||||
$(TOP)\ext\misc\wholenumber.c
|
||||
|
||||
# If use of zlib is enabled, add the "zipfile.c" source file.
|
||||
#
|
||||
!IF $(USE_ZLIB)!=0
|
||||
TESTEXT = $(TESTEXT) $(TOP)\ext\misc\zipfile.c
|
||||
!ENDIF
|
||||
|
||||
# Source code to the library files needed by the test fixture
|
||||
# (non-amalgamation)
|
||||
#
|
||||
@ -1545,7 +1636,15 @@ ALL_TCL_TARGETS =
|
||||
# This is the default Makefile target. The objects listed here
|
||||
# are what get build when you type just "make" with no arguments.
|
||||
#
|
||||
all: dll libsqlite3.lib shell $(ALL_TCL_TARGETS)
|
||||
core: dll libsqlite3.lib shell
|
||||
|
||||
# Targets that require the Tcl library.
|
||||
#
|
||||
tcl: $(ALL_TCL_TARGETS)
|
||||
|
||||
# This Makefile target builds all of the standard binaries.
|
||||
#
|
||||
all: core tcl
|
||||
|
||||
# Dynamic link library section.
|
||||
#
|
||||
@ -1940,7 +2039,7 @@ tclsqlite-shell.lo: $(TOP)\src\tclsqlite.c $(HDR) $(SQLITE_TCL_DEP)
|
||||
$(LTCOMPILE) $(NO_WARN) -DTCLSH -DBUILD_sqlite -I$(TCLINCDIR) -c $(TOP)\src\tclsqlite.c
|
||||
|
||||
tclsqlite3.exe: tclsqlite-shell.lo $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS)
|
||||
$(LTLINK) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /OUT:$@ tclsqlite-shell.lo $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
|
||||
$(LTLINK) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) /OUT:$@ tclsqlite-shell.lo $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS)
|
||||
|
||||
# Rules to build opcodes.c and opcodes.h
|
||||
#
|
||||
@ -1985,11 +2084,23 @@ SHELL_SRC = \
|
||||
$(TOP)\src\shell.c.in \
|
||||
$(TOP)\ext\misc\shathree.c \
|
||||
$(TOP)\ext\misc\fileio.c \
|
||||
$(TOP)\ext\misc\completion.c
|
||||
$(TOP)\ext\misc\completion.c \
|
||||
$(TOP)\ext\expert\sqlite3expert.c \
|
||||
$(TOP)\ext\expert\sqlite3expert.h \
|
||||
$(TOP)\src\test_windirent.c
|
||||
|
||||
# If use of zlib is enabled, add the "zipfile.c" source file.
|
||||
#
|
||||
!IF $(USE_ZLIB)!=0
|
||||
SHELL_SRC = $(SHELL_SRC) $(TOP)\ext\misc\sqlar.c
|
||||
SHELL_SRC = $(SHELL_SRC) $(TOP)\ext\misc\zipfile.c
|
||||
!ENDIF
|
||||
|
||||
shell.c: $(SHELL_SRC) $(TOP)\tool\mkshellc.tcl
|
||||
$(TCLSH_CMD) $(TOP)\tool\mkshellc.tcl > shell.c
|
||||
|
||||
zlib:
|
||||
pushd $(ZLIBDIR) && $(MAKE) /f win32\Makefile.msc clean $(ZLIBLIB) && popd
|
||||
|
||||
# Rules to build the extension objects.
|
||||
#
|
||||
@ -2167,11 +2278,11 @@ sqlite_tcl.h:
|
||||
| $(TCLSH_CMD) $(TOP)\tool\replace.tcl exact "Tcl_HashEntry *(*createProc)" "Tcl_HashEntry *(SQLITE_TCLAPI *createProc)" >> $(SQLITETCLH)
|
||||
!ENDIF
|
||||
|
||||
testfixture.exe: $(TESTFIXTURE_SRC) $(SQLITE3H) $(LIBRESOBJS) $(HDR) $(SQLITE_TCL_DEP)
|
||||
testfixture.exe: $(TESTFIXTURE_SRC) $(TESTFIXTURE_DEP) $(SQLITE3H) $(LIBRESOBJS) $(HDR) $(SQLITE_TCL_DEP)
|
||||
$(LTLINK) -DSQLITE_NO_SYNC=1 $(TESTFIXTURE_FLAGS) \
|
||||
-DBUILD_sqlite -I$(TCLINCDIR) \
|
||||
$(TESTFIXTURE_SRC) \
|
||||
/link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
|
||||
/link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS)
|
||||
|
||||
extensiontest: testfixture.exe testloadext.dll
|
||||
@set PATH=$(LIBTCLPATH);$(PATH)
|
||||
@ -2221,7 +2332,7 @@ sqlite3_analyzer.c: $(SQLITE3C) $(SQLITE3H) $(TOP)\src\tclsqlite.c $(TOP)\tool\s
|
||||
|
||||
sqlite3_analyzer.exe: sqlite3_analyzer.c $(LIBRESOBJS)
|
||||
$(LTLINK) $(NO_WARN) -DBUILD_sqlite -I$(TCLINCDIR) sqlite3_analyzer.c \
|
||||
/link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
|
||||
/link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS)
|
||||
|
||||
sqlite3_expert.exe: $(SQLITE3C) $(TOP)\ext\expert\sqlite3expert.h $(TOP)\ext\expert\sqlite3expert.c $(TOP)\ext\expert\expert.c
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\ext\expert\sqlite3expert.c $(TOP)\ext\expert\expert.c $(SQLITE3C) $(TLIBS)
|
||||
|
@ -561,6 +561,7 @@ SHELL_CORE_DEP =
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
|
||||
# This is the core library that the shell executable should link with.
|
||||
#
|
||||
!IFNDEF SHELL_CORE_LIB
|
||||
|
@ -433,7 +433,7 @@ static int SQLITE_TCLAPI xF5tApi(
|
||||
int iVal;
|
||||
int bClear;
|
||||
if( Tcl_GetBooleanFromObj(interp, objv[2], &bClear) ) return TCL_ERROR;
|
||||
iVal = ((char*)p->pApi->xGetAuxdata(p->pFts, bClear) - (char*)0);
|
||||
iVal = (int)((char*)p->pApi->xGetAuxdata(p->pFts, bClear) - (char*)0);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(iVal));
|
||||
break;
|
||||
}
|
||||
|
@ -11,11 +11,120 @@
|
||||
******************************************************************************
|
||||
**
|
||||
** This SQLite extension implements SQL functions readfile() and
|
||||
** writefile().
|
||||
** writefile(), and eponymous virtual type "fsdir".
|
||||
**
|
||||
** WRITEFILE(FILE, DATA [, MODE [, MTIME]]):
|
||||
**
|
||||
** If neither of the optional arguments is present, then this UDF
|
||||
** function writes blob DATA to file FILE. If successful, the number
|
||||
** of bytes written is returned. If an error occurs, NULL is returned.
|
||||
**
|
||||
** If the first option argument - MODE - is present, then it must
|
||||
** be passed an integer value that corresponds to a POSIX mode
|
||||
** value (file type + permissions, as returned in the stat.st_mode
|
||||
** field by the stat() system call). Three types of files may
|
||||
** be written/created:
|
||||
**
|
||||
** regular files: (mode & 0170000)==0100000
|
||||
** symbolic links: (mode & 0170000)==0120000
|
||||
** directories: (mode & 0170000)==0040000
|
||||
**
|
||||
** For a directory, the DATA is ignored. For a symbolic link, it is
|
||||
** interpreted as text and used as the target of the link. For a
|
||||
** regular file, it is interpreted as a blob and written into the
|
||||
** named file. Regardless of the type of file, its permissions are
|
||||
** set to (mode & 0777) before returning.
|
||||
**
|
||||
** If the optional MTIME argument is present, then it is interpreted
|
||||
** as an integer - the number of seconds since the unix epoch. The
|
||||
** modification-time of the target file is set to this value before
|
||||
** returning.
|
||||
**
|
||||
** If three or more arguments are passed to this function and an
|
||||
** error is encountered, an exception is raised.
|
||||
**
|
||||
** READFILE(FILE):
|
||||
**
|
||||
** Read and return the contents of file FILE (type blob) from disk.
|
||||
**
|
||||
** FSDIR:
|
||||
**
|
||||
** Used as follows:
|
||||
**
|
||||
** SELECT * FROM fsdir($path [, $dir]);
|
||||
**
|
||||
** Parameter $path is an absolute or relative pathname. If the file that it
|
||||
** refers to does not exist, it is an error. If the path refers to a regular
|
||||
** file or symbolic link, it returns a single row. Or, if the path refers
|
||||
** to a directory, it returns one row for the directory, and one row for each
|
||||
** file within the hierarchy rooted at $path.
|
||||
**
|
||||
** Each row has the following columns:
|
||||
**
|
||||
** name: Path to file or directory (text value).
|
||||
** mode: Value of stat.st_mode for directory entry (an integer).
|
||||
** mtime: Value of stat.st_mtime for directory entry (an integer).
|
||||
** data: For a regular file, a blob containing the file data. For a
|
||||
** symlink, a text value containing the text of the link. For a
|
||||
** directory, NULL.
|
||||
**
|
||||
** If a non-NULL value is specified for the optional $dir parameter and
|
||||
** $path is a relative path, then $path is interpreted relative to $dir.
|
||||
** And the paths returned in the "name" column of the table are also
|
||||
** relative to directory $dir.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#if !defined(_WIN32) && !defined(WIN32)
|
||||
# include <unistd.h>
|
||||
# include <dirent.h>
|
||||
# include <utime.h>
|
||||
#else
|
||||
# include "windows.h"
|
||||
# include <io.h>
|
||||
# include <direct.h>
|
||||
# include "test_windirent.h"
|
||||
# define dirent DIRENT
|
||||
# define timespec TIMESPEC
|
||||
# define stat _stat
|
||||
# define mkdir(path,mode) _mkdir(path)
|
||||
# define lstat(path,buf) _stat(path,buf)
|
||||
#endif
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
#define FSDIR_SCHEMA "(name,mode,mtime,data,path HIDDEN,dir HIDDEN)"
|
||||
|
||||
/*
|
||||
** Set the result stored by context ctx to a blob containing the
|
||||
** contents of file zName.
|
||||
*/
|
||||
static void readFileContents(sqlite3_context *ctx, const char *zName){
|
||||
FILE *in;
|
||||
long nIn;
|
||||
void *pBuf;
|
||||
|
||||
in = fopen(zName, "rb");
|
||||
if( in==0 ) return;
|
||||
fseek(in, 0, SEEK_END);
|
||||
nIn = ftell(in);
|
||||
rewind(in);
|
||||
pBuf = sqlite3_malloc( nIn );
|
||||
if( pBuf && 1==fread(pBuf, nIn, 1, in) ){
|
||||
sqlite3_result_blob(ctx, pBuf, nIn, sqlite3_free);
|
||||
}else{
|
||||
sqlite3_free(pBuf);
|
||||
}
|
||||
fclose(in);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the "readfile(X)" SQL function. The entire content
|
||||
@ -28,58 +137,607 @@ static void readfileFunc(
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zName;
|
||||
FILE *in;
|
||||
long nIn;
|
||||
void *pBuf;
|
||||
|
||||
(void)(argc); /* Unused parameter */
|
||||
zName = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zName==0 ) return;
|
||||
in = fopen(zName, "rb");
|
||||
if( in==0 ) return;
|
||||
fseek(in, 0, SEEK_END);
|
||||
nIn = ftell(in);
|
||||
rewind(in);
|
||||
pBuf = sqlite3_malloc( nIn );
|
||||
if( pBuf && 1==fread(pBuf, nIn, 1, in) ){
|
||||
sqlite3_result_blob(context, pBuf, nIn, sqlite3_free);
|
||||
}else{
|
||||
sqlite3_free(pBuf);
|
||||
}
|
||||
fclose(in);
|
||||
readFileContents(context, zName);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the "writefile(X,Y)" SQL function. The argument Y
|
||||
** is written into file X. The number of bytes written is returned. Or
|
||||
** NULL is returned if something goes wrong, such as being unable to open
|
||||
** file X for writing.
|
||||
** Set the error message contained in context ctx to the results of
|
||||
** vprintf(zFmt, ...).
|
||||
*/
|
||||
static void ctxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){
|
||||
char *zMsg = 0;
|
||||
va_list ap;
|
||||
va_start(ap, zFmt);
|
||||
zMsg = sqlite3_vmprintf(zFmt, ap);
|
||||
sqlite3_result_error(ctx, zMsg, -1);
|
||||
sqlite3_free(zMsg);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/*
|
||||
** Argument zFile is the name of a file that will be created and/or written
|
||||
** by SQL function writefile(). This function ensures that the directory
|
||||
** zFile will be written to exists, creating it if required. The permissions
|
||||
** for any path components created by this function are set to (mode&0777).
|
||||
**
|
||||
** If an OOM condition is encountered, SQLITE_NOMEM is returned. Otherwise,
|
||||
** SQLITE_OK is returned if the directory is successfully created, or
|
||||
** SQLITE_ERROR otherwise.
|
||||
*/
|
||||
static int makeDirectory(
|
||||
const char *zFile,
|
||||
mode_t mode
|
||||
){
|
||||
char *zCopy = sqlite3_mprintf("%s", zFile);
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
if( zCopy==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
int nCopy = (int)strlen(zCopy);
|
||||
int i = 1;
|
||||
|
||||
while( rc==SQLITE_OK ){
|
||||
struct stat sStat;
|
||||
int rc2;
|
||||
|
||||
for(; zCopy[i]!='/' && i<nCopy; i++);
|
||||
if( i==nCopy ) break;
|
||||
zCopy[i] = '\0';
|
||||
|
||||
rc2 = stat(zCopy, &sStat);
|
||||
if( rc2!=0 ){
|
||||
if( mkdir(zCopy, mode & 0777) ) rc = SQLITE_ERROR;
|
||||
}else{
|
||||
if( !S_ISDIR(sStat.st_mode) ) rc = SQLITE_ERROR;
|
||||
}
|
||||
zCopy[i] = '/';
|
||||
i++;
|
||||
}
|
||||
|
||||
sqlite3_free(zCopy);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function does the work for the writefile() UDF. Refer to
|
||||
** header comments at the top of this file for details.
|
||||
*/
|
||||
static int writeFile(
|
||||
sqlite3_context *pCtx, /* Context to return bytes written in */
|
||||
const char *zFile, /* File to write */
|
||||
sqlite3_value *pData, /* Data to write */
|
||||
mode_t mode, /* MODE parameter passed to writefile() */
|
||||
sqlite3_int64 mtime /* MTIME parameter (or -1 to not set time) */
|
||||
){
|
||||
#if !defined(_WIN32) && !defined(WIN32)
|
||||
if( S_ISLNK(mode) ){
|
||||
const char *zTo = (const char*)sqlite3_value_text(pData);
|
||||
if( symlink(zTo, zFile)<0 ) return 1;
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
if( S_ISDIR(mode) ){
|
||||
if( mkdir(zFile, mode) ){
|
||||
/* The mkdir() call to create the directory failed. This might not
|
||||
** be an error though - if there is already a directory at the same
|
||||
** path and either the permissions already match or can be changed
|
||||
** to do so using chmod(), it is not an error. */
|
||||
struct stat sStat;
|
||||
if( errno!=EEXIST
|
||||
|| 0!=stat(zFile, &sStat)
|
||||
|| !S_ISDIR(sStat.st_mode)
|
||||
|| ((sStat.st_mode&0777)!=(mode&0777) && 0!=chmod(zFile, mode&0777))
|
||||
){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
sqlite3_int64 nWrite = 0;
|
||||
const char *z;
|
||||
int rc = 0;
|
||||
FILE *out = fopen(zFile, "wb");
|
||||
if( out==0 ) return 1;
|
||||
z = (const char*)sqlite3_value_blob(pData);
|
||||
if( z ){
|
||||
sqlite3_int64 n = fwrite(z, 1, sqlite3_value_bytes(pData), out);
|
||||
nWrite = sqlite3_value_bytes(pData);
|
||||
if( nWrite!=n ){
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
fclose(out);
|
||||
if( rc==0 && mode && chmod(zFile, mode & 0777) ){
|
||||
rc = 1;
|
||||
}
|
||||
if( rc ) return 2;
|
||||
sqlite3_result_int64(pCtx, nWrite);
|
||||
}
|
||||
}
|
||||
|
||||
if( mtime>=0 ){
|
||||
#if !defined(_WIN32) && !defined(WIN32)
|
||||
struct timespec times[2];
|
||||
times[0].tv_nsec = times[1].tv_nsec = 0;
|
||||
times[0].tv_sec = time(0);
|
||||
times[1].tv_sec = mtime;
|
||||
if( utimensat(AT_FDCWD, zFile, times, AT_SYMLINK_NOFOLLOW) ){
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
FILETIME lastAccess;
|
||||
FILETIME lastWrite;
|
||||
SYSTEMTIME currentTime;
|
||||
LONGLONG intervals;
|
||||
HANDLE hFile;
|
||||
GetSystemTime(¤tTime);
|
||||
SystemTimeToFileTime(¤tTime, &lastAccess);
|
||||
intervals = Int32x32To64(mtime, 10000000) + 116444736000000000;
|
||||
lastWrite.dwLowDateTime = (DWORD)intervals;
|
||||
lastWrite.dwHighDateTime = intervals >> 32;
|
||||
hFile = CreateFile(
|
||||
zFile, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS, NULL
|
||||
);
|
||||
if( hFile!=INVALID_HANDLE_VALUE ){
|
||||
BOOL bResult = SetFileTime(hFile, NULL, &lastAccess, &lastWrite);
|
||||
CloseHandle(hFile);
|
||||
return !bResult;
|
||||
}else{
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the "writefile(W,X[,Y[,Z]]])" SQL function.
|
||||
** Refer to header comments at the top of this file for details.
|
||||
*/
|
||||
static void writefileFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
FILE *out;
|
||||
const char *z;
|
||||
sqlite3_int64 rc;
|
||||
const char *zFile;
|
||||
mode_t mode = 0;
|
||||
int res;
|
||||
sqlite3_int64 mtime = -1;
|
||||
|
||||
if( argc<2 || argc>4 ){
|
||||
sqlite3_result_error(context,
|
||||
"wrong number of arguments to function writefile()", -1
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
(void)(argc); /* Unused parameter */
|
||||
zFile = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zFile==0 ) return;
|
||||
out = fopen(zFile, "wb");
|
||||
if( out==0 ) return;
|
||||
z = (const char*)sqlite3_value_blob(argv[1]);
|
||||
if( z==0 ){
|
||||
rc = 0;
|
||||
}else{
|
||||
rc = fwrite(z, 1, sqlite3_value_bytes(argv[1]), out);
|
||||
if( argc>=3 ){
|
||||
mode = (mode_t)sqlite3_value_int(argv[2]);
|
||||
}
|
||||
if( argc==4 ){
|
||||
mtime = sqlite3_value_int64(argv[3]);
|
||||
}
|
||||
|
||||
res = writeFile(context, zFile, argv[1], mode, mtime);
|
||||
if( res==1 && errno==ENOENT ){
|
||||
if( makeDirectory(zFile, mode)==SQLITE_OK ){
|
||||
res = writeFile(context, zFile, argv[1], mode, mtime);
|
||||
}
|
||||
}
|
||||
|
||||
if( argc>2 && res!=0 ){
|
||||
if( S_ISLNK(mode) ){
|
||||
ctxErrorMsg(context, "failed to create symlink: %s", zFile);
|
||||
}else if( S_ISDIR(mode) ){
|
||||
ctxErrorMsg(context, "failed to create directory: %s", zFile);
|
||||
}else{
|
||||
ctxErrorMsg(context, "failed to write file: %s", zFile);
|
||||
}
|
||||
}
|
||||
fclose(out);
|
||||
sqlite3_result_int64(context, rc);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
/*
|
||||
** Cursor type for recursively iterating through a directory structure.
|
||||
*/
|
||||
typedef struct fsdir_cursor fsdir_cursor;
|
||||
typedef struct FsdirLevel FsdirLevel;
|
||||
|
||||
struct FsdirLevel {
|
||||
DIR *pDir; /* From opendir() */
|
||||
char *zDir; /* Name of directory (nul-terminated) */
|
||||
};
|
||||
|
||||
struct fsdir_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
|
||||
int nLvl; /* Number of entries in aLvl[] array */
|
||||
int iLvl; /* Index of current entry */
|
||||
FsdirLevel *aLvl; /* Hierarchy of directories being traversed */
|
||||
|
||||
const char *zBase;
|
||||
int nBase;
|
||||
|
||||
struct stat sStat; /* Current lstat() results */
|
||||
char *zPath; /* Path to current entry */
|
||||
sqlite3_int64 iRowid; /* Current rowid */
|
||||
};
|
||||
|
||||
typedef struct fsdir_tab fsdir_tab;
|
||||
struct fsdir_tab {
|
||||
sqlite3_vtab base; /* Base class - must be first */
|
||||
};
|
||||
|
||||
/*
|
||||
** Construct a new fsdir virtual table object.
|
||||
*/
|
||||
static int fsdirConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
fsdir_tab *pNew = 0;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_declare_vtab(db, "CREATE TABLE x" FSDIR_SCHEMA);
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = (fsdir_tab*)sqlite3_malloc( sizeof(*pNew) );
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
}
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This method is the destructor for fsdir vtab objects.
|
||||
*/
|
||||
static int fsdirDisconnect(sqlite3_vtab *pVtab){
|
||||
sqlite3_free(pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Constructor for a new fsdir_cursor object.
|
||||
*/
|
||||
static int fsdirOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
fsdir_cursor *pCur;
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
pCur->iLvl = -1;
|
||||
*ppCursor = &pCur->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Reset a cursor back to the state it was in when first returned
|
||||
** by fsdirOpen().
|
||||
*/
|
||||
static void fsdirResetCursor(fsdir_cursor *pCur){
|
||||
int i;
|
||||
for(i=0; i<=pCur->iLvl; i++){
|
||||
FsdirLevel *pLvl = &pCur->aLvl[i];
|
||||
if( pLvl->pDir ) closedir(pLvl->pDir);
|
||||
sqlite3_free(pLvl->zDir);
|
||||
}
|
||||
sqlite3_free(pCur->zPath);
|
||||
pCur->aLvl = 0;
|
||||
pCur->zPath = 0;
|
||||
pCur->zBase = 0;
|
||||
pCur->nBase = 0;
|
||||
pCur->iLvl = -1;
|
||||
pCur->iRowid = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Destructor for an fsdir_cursor.
|
||||
*/
|
||||
static int fsdirClose(sqlite3_vtab_cursor *cur){
|
||||
fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
||||
|
||||
fsdirResetCursor(pCur);
|
||||
sqlite3_free(pCur->aLvl);
|
||||
sqlite3_free(pCur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the error message for the virtual table associated with cursor
|
||||
** pCur to the results of vprintf(zFmt, ...).
|
||||
*/
|
||||
static void fsdirSetErrmsg(fsdir_cursor *pCur, const char *zFmt, ...){
|
||||
va_list ap;
|
||||
va_start(ap, zFmt);
|
||||
pCur->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Advance an fsdir_cursor to its next row of output.
|
||||
*/
|
||||
static int fsdirNext(sqlite3_vtab_cursor *cur){
|
||||
fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
||||
mode_t m = pCur->sStat.st_mode;
|
||||
|
||||
pCur->iRowid++;
|
||||
if( S_ISDIR(m) ){
|
||||
/* Descend into this directory */
|
||||
int iNew = pCur->iLvl + 1;
|
||||
FsdirLevel *pLvl;
|
||||
if( iNew>=pCur->nLvl ){
|
||||
int nNew = iNew+1;
|
||||
int nByte = nNew*sizeof(FsdirLevel);
|
||||
FsdirLevel *aNew = (FsdirLevel*)sqlite3_realloc(pCur->aLvl, nByte);
|
||||
if( aNew==0 ) return SQLITE_NOMEM;
|
||||
memset(&aNew[pCur->nLvl], 0, sizeof(FsdirLevel)*(nNew-pCur->nLvl));
|
||||
pCur->aLvl = aNew;
|
||||
pCur->nLvl = nNew;
|
||||
}
|
||||
pCur->iLvl = iNew;
|
||||
pLvl = &pCur->aLvl[iNew];
|
||||
|
||||
pLvl->zDir = pCur->zPath;
|
||||
pCur->zPath = 0;
|
||||
pLvl->pDir = opendir(pLvl->zDir);
|
||||
if( pLvl->pDir==0 ){
|
||||
fsdirSetErrmsg(pCur, "cannot read directory: %s", pCur->zPath);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
while( pCur->iLvl>=0 ){
|
||||
FsdirLevel *pLvl = &pCur->aLvl[pCur->iLvl];
|
||||
struct dirent *pEntry = readdir(pLvl->pDir);
|
||||
if( pEntry ){
|
||||
if( pEntry->d_name[0]=='.' ){
|
||||
if( pEntry->d_name[1]=='.' && pEntry->d_name[2]=='\0' ) continue;
|
||||
if( pEntry->d_name[1]=='\0' ) continue;
|
||||
}
|
||||
sqlite3_free(pCur->zPath);
|
||||
pCur->zPath = sqlite3_mprintf("%s/%s", pLvl->zDir, pEntry->d_name);
|
||||
if( pCur->zPath==0 ) return SQLITE_NOMEM;
|
||||
if( lstat(pCur->zPath, &pCur->sStat) ){
|
||||
fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zPath);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
closedir(pLvl->pDir);
|
||||
sqlite3_free(pLvl->zDir);
|
||||
pLvl->pDir = 0;
|
||||
pLvl->zDir = 0;
|
||||
pCur->iLvl--;
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
sqlite3_free(pCur->zPath);
|
||||
pCur->zPath = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return values of columns for the row at which the series_cursor
|
||||
** is currently pointing.
|
||||
*/
|
||||
static int fsdirColumn(
|
||||
sqlite3_vtab_cursor *cur, /* The cursor */
|
||||
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
||||
int i /* Which column to return */
|
||||
){
|
||||
fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
||||
switch( i ){
|
||||
case 0: { /* name */
|
||||
sqlite3_result_text(ctx, &pCur->zPath[pCur->nBase], -1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
|
||||
case 1: /* mode */
|
||||
sqlite3_result_int64(ctx, pCur->sStat.st_mode);
|
||||
break;
|
||||
|
||||
case 2: /* mtime */
|
||||
sqlite3_result_int64(ctx, pCur->sStat.st_mtime);
|
||||
break;
|
||||
|
||||
case 3: { /* data */
|
||||
mode_t m = pCur->sStat.st_mode;
|
||||
if( S_ISDIR(m) ){
|
||||
sqlite3_result_null(ctx);
|
||||
#if !defined(_WIN32) && !defined(WIN32)
|
||||
}else if( S_ISLNK(m) ){
|
||||
char aStatic[64];
|
||||
char *aBuf = aStatic;
|
||||
int nBuf = 64;
|
||||
int n;
|
||||
|
||||
while( 1 ){
|
||||
n = readlink(pCur->zPath, aBuf, nBuf);
|
||||
if( n<nBuf ) break;
|
||||
if( aBuf!=aStatic ) sqlite3_free(aBuf);
|
||||
nBuf = nBuf*2;
|
||||
aBuf = sqlite3_malloc(nBuf);
|
||||
if( aBuf==0 ){
|
||||
sqlite3_result_error_nomem(ctx);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_result_text(ctx, aBuf, n, SQLITE_TRANSIENT);
|
||||
if( aBuf!=aStatic ) sqlite3_free(aBuf);
|
||||
#endif
|
||||
}else{
|
||||
readFileContents(ctx, pCur->zPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the rowid for the current row. In this implementation, the
|
||||
** first row returned is assigned rowid value 1, and each subsequent
|
||||
** row a value 1 more than that of the previous.
|
||||
*/
|
||||
static int fsdirRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
||||
*pRowid = pCur->iRowid;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if the cursor has been moved off of the last
|
||||
** row of output.
|
||||
*/
|
||||
static int fsdirEof(sqlite3_vtab_cursor *cur){
|
||||
fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
||||
return (pCur->zPath==0);
|
||||
}
|
||||
|
||||
/*
|
||||
** xFilter callback.
|
||||
*/
|
||||
static int fsdirFilter(
|
||||
sqlite3_vtab_cursor *cur,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
const char *zDir = 0;
|
||||
fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
||||
|
||||
fsdirResetCursor(pCur);
|
||||
|
||||
if( idxNum==0 ){
|
||||
fsdirSetErrmsg(pCur, "table function fsdir requires an argument");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
assert( argc==idxNum && (argc==1 || argc==2) );
|
||||
zDir = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zDir==0 ){
|
||||
fsdirSetErrmsg(pCur, "table function fsdir requires a non-NULL argument");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
if( argc==2 ){
|
||||
pCur->zBase = (const char*)sqlite3_value_text(argv[1]);
|
||||
}
|
||||
if( pCur->zBase ){
|
||||
pCur->nBase = (int)strlen(pCur->zBase)+1;
|
||||
pCur->zPath = sqlite3_mprintf("%s/%s", pCur->zBase, zDir);
|
||||
}else{
|
||||
pCur->zPath = sqlite3_mprintf("%s", zDir);
|
||||
}
|
||||
|
||||
if( pCur->zPath==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
if( lstat(pCur->zPath, &pCur->sStat) ){
|
||||
fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zPath);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** SQLite will invoke this method one or more times while planning a query
|
||||
** that uses the generate_series virtual table. This routine needs to create
|
||||
** a query plan for each invocation and compute an estimated cost for that
|
||||
** plan.
|
||||
**
|
||||
** In this implementation idxNum is used to represent the
|
||||
** query plan. idxStr is unused.
|
||||
**
|
||||
** The query plan is represented by bits in idxNum:
|
||||
**
|
||||
** (1) start = $value -- constraint exists
|
||||
** (2) stop = $value -- constraint exists
|
||||
** (4) step = $value -- constraint exists
|
||||
** (8) output in descending order
|
||||
*/
|
||||
static int fsdirBestIndex(
|
||||
sqlite3_vtab *tab,
|
||||
sqlite3_index_info *pIdxInfo
|
||||
){
|
||||
int i; /* Loop over constraints */
|
||||
int idx4 = -1;
|
||||
int idx5 = -1;
|
||||
|
||||
const struct sqlite3_index_constraint *pConstraint;
|
||||
pConstraint = pIdxInfo->aConstraint;
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
||||
if( pConstraint->usable==0 ) continue;
|
||||
if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
|
||||
if( pConstraint->iColumn==4 ) idx4 = i;
|
||||
if( pConstraint->iColumn==5 ) idx5 = i;
|
||||
}
|
||||
|
||||
if( idx4<0 ){
|
||||
pIdxInfo->idxNum = 0;
|
||||
pIdxInfo->estimatedCost = (double)(((sqlite3_int64)1) << 50);
|
||||
}else{
|
||||
pIdxInfo->aConstraintUsage[idx4].omit = 1;
|
||||
pIdxInfo->aConstraintUsage[idx4].argvIndex = 1;
|
||||
if( idx5>=0 ){
|
||||
pIdxInfo->aConstraintUsage[idx5].omit = 1;
|
||||
pIdxInfo->aConstraintUsage[idx5].argvIndex = 2;
|
||||
pIdxInfo->idxNum = 2;
|
||||
pIdxInfo->estimatedCost = 10.0;
|
||||
}else{
|
||||
pIdxInfo->idxNum = 1;
|
||||
pIdxInfo->estimatedCost = 100.0;
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register the "fsdir" virtual table.
|
||||
*/
|
||||
static int fsdirRegister(sqlite3 *db){
|
||||
static sqlite3_module fsdirModule = {
|
||||
0, /* iVersion */
|
||||
0, /* xCreate */
|
||||
fsdirConnect, /* xConnect */
|
||||
fsdirBestIndex, /* xBestIndex */
|
||||
fsdirDisconnect, /* xDisconnect */
|
||||
0, /* xDestroy */
|
||||
fsdirOpen, /* xOpen - open a cursor */
|
||||
fsdirClose, /* xClose - close a cursor */
|
||||
fsdirFilter, /* xFilter - configure scan constraints */
|
||||
fsdirNext, /* xNext - advance a cursor */
|
||||
fsdirEof, /* xEof - check for end of scan */
|
||||
fsdirColumn, /* xColumn - read data */
|
||||
fsdirRowid, /* xRowid - read data */
|
||||
0, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
0, /* xRename */
|
||||
};
|
||||
|
||||
int rc = sqlite3_create_module(db, "fsdir", &fsdirModule, 0);
|
||||
return rc;
|
||||
}
|
||||
#else /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
# define fsdirRegister(x) SQLITE_OK
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
@ -95,8 +753,11 @@ int sqlite3_fileio_init(
|
||||
rc = sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0,
|
||||
readfileFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0,
|
||||
rc = sqlite3_create_function(db, "writefile", -1, SQLITE_UTF8, 0,
|
||||
writefileFunc, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fsdirRegister(db);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
113
ext/misc/sqlar.c
Normal file
113
ext/misc/sqlar.c
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
** 2017-12-17
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** Utility functions sqlar_compress() and sqlar_uncompress(). Useful
|
||||
** for working with sqlar archives and used by the shell tool's built-in
|
||||
** sqlar support.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <zlib.h>
|
||||
|
||||
/*
|
||||
** Implementation of the "sqlar_compress(X)" SQL function.
|
||||
**
|
||||
** If the type of X is SQLITE_BLOB, and compressing that blob using
|
||||
** zlib utility function compress() yields a smaller blob, return the
|
||||
** compressed blob. Otherwise, return a copy of X.
|
||||
*/
|
||||
static void sqlarCompressFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
assert( argc==1 );
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){
|
||||
const Bytef *pData = sqlite3_value_blob(argv[0]);
|
||||
uLong nData = sqlite3_value_bytes(argv[0]);
|
||||
uLongf nOut = compressBound(nData);
|
||||
Bytef *pOut;
|
||||
|
||||
pOut = (Bytef*)sqlite3_malloc(nOut);
|
||||
if( pOut==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
}else{
|
||||
if( Z_OK!=compress(pOut, &nOut, pData, nData) ){
|
||||
sqlite3_result_error(context, "error in compress()", -1);
|
||||
}else if( nOut<nData ){
|
||||
sqlite3_result_blob(context, pOut, nOut, SQLITE_TRANSIENT);
|
||||
}else{
|
||||
sqlite3_result_value(context, argv[0]);
|
||||
}
|
||||
sqlite3_free(pOut);
|
||||
}
|
||||
}else{
|
||||
sqlite3_result_value(context, argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the "sqlar_uncompress(X,SZ)" SQL function
|
||||
**
|
||||
** Parameter SZ is interpreted as an integer. If it is less than or
|
||||
** equal to zero, then this function returns a copy of X. Or, if
|
||||
** SZ is equal to the size of X when interpreted as a blob, also
|
||||
** return a copy of X. Otherwise, decompress blob X using zlib
|
||||
** utility function uncompress() and return the results (another
|
||||
** blob).
|
||||
*/
|
||||
static void sqlarUncompressFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
uLong nData;
|
||||
uLongf sz;
|
||||
|
||||
assert( argc==2 );
|
||||
sz = sqlite3_value_int(argv[1]);
|
||||
|
||||
if( sz<=0 || sz==(nData = sqlite3_value_bytes(argv[0])) ){
|
||||
sqlite3_result_value(context, argv[0]);
|
||||
}else{
|
||||
const Bytef *pData= sqlite3_value_blob(argv[0]);
|
||||
Bytef *pOut = sqlite3_malloc(sz);
|
||||
if( Z_OK!=uncompress(pOut, &sz, pData, nData) ){
|
||||
sqlite3_result_error(context, "error in uncompress()", -1);
|
||||
}else{
|
||||
sqlite3_result_blob(context, pOut, sz, SQLITE_TRANSIENT);
|
||||
}
|
||||
sqlite3_free(pOut);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_sqlar_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "sqlar_compress", 1, SQLITE_UTF8, 0,
|
||||
sqlarCompressFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "sqlar_uncompress", 2, SQLITE_UTF8, 0,
|
||||
sqlarUncompressFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
@ -799,7 +799,7 @@ static void unionConfigureVtab(
|
||||
zVal = zOpt;
|
||||
if( *zVal==':' ) zVal++;
|
||||
while( union_isidchar(*zVal) ) zVal++;
|
||||
nOpt = zVal-zOpt;
|
||||
nOpt = (int)(zVal-zOpt);
|
||||
|
||||
while( union_isspace(*zVal) ) zVal++;
|
||||
if( *zVal=='=' ){
|
||||
|
1418
ext/misc/zipfile.c
Normal file
1418
ext/misc/zipfile.c
Normal file
File diff suppressed because it is too large
Load Diff
6
main.mk
6
main.mk
@ -370,6 +370,7 @@ TESTSRC += \
|
||||
$(TOP)/ext/misc/unionvtab.c \
|
||||
$(TOP)/ext/misc/wholenumber.c \
|
||||
$(TOP)/ext/misc/vfslog.c \
|
||||
$(TOP)/ext/misc/zipfile.c \
|
||||
$(TOP)/ext/fts5/fts5_tcl.c \
|
||||
$(TOP)/ext/fts5/fts5_test_mi.c \
|
||||
$(TOP)/ext/fts5/fts5_test_tok.c
|
||||
@ -695,8 +696,11 @@ SHELL_SRC = \
|
||||
$(TOP)/ext/misc/shathree.c \
|
||||
$(TOP)/ext/misc/fileio.c \
|
||||
$(TOP)/ext/misc/completion.c \
|
||||
$(TOP)/ext/misc/sqlar.c \
|
||||
$(TOP)/ext/expert/sqlite3expert.c \
|
||||
$(TOP)/ext/expert/sqlite3expert.h
|
||||
$(TOP)/ext/expert/sqlite3expert.h \
|
||||
$(TOP)/ext/misc/zipfile.c \
|
||||
$(TOP)/src/test_windirent.c
|
||||
|
||||
shell.c: $(SHELL_SRC) $(TOP)/tool/mkshellc.tcl
|
||||
tclsh $(TOP)/tool/mkshellc.tcl >shell.c
|
||||
|
39
manifest
39
manifest
@ -1,8 +1,10 @@
|
||||
C Fix\san\sLSM\sproblem\scausing\sthe\swrong\samount\sof\s"auto-work"\sto\sbe\sperformed\nunder\sfairly\sobscure\scircumstances.
|
||||
D 2018-01-05T11:34:18.736
|
||||
F Makefile.in 1b11037c5ed3399a79433cc82c59b5e36a7b3a3e4e195bb27640d0d2145e03e1
|
||||
C Add\sthe\s".ar"\scommand\sto\sthe\scommand-line\sshell.
|
||||
D 2018-01-05T19:01:05.842
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F Makefile.in f5c6285ac43b2e567d6cf463dff3744da960a19f2cf141744b4472842a97681e
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc f68b4f9b83cfeb057b6265e0288ad653f319e2ceacca731e0f22e19617829a89
|
||||
F Makefile.msc 2335db317697a3ea33ec3a258be9f46c973fcfd4e7cffe6798c6f3d55bc83d5b
|
||||
F README.md eeae1e552f93ef72ef7c5b8f6647b368a001c28820ad1df179d3dae602bef681
|
||||
F VERSION 0c10cdfed866fdd2d80434f64f042c3330f1daaed12e54287beb104f04b3faaf
|
||||
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
|
||||
@ -11,7 +13,7 @@ F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
|
||||
F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2
|
||||
F autoconf/INSTALL 83e4a25da9fd053c7b3665eaaaf7919707915903
|
||||
F autoconf/Makefile.am 66c0befa511f0d95ba229e180067cf0357a9ebf8b3201b06d683c5ba6220fb39
|
||||
F autoconf/Makefile.msc b88a70dee8453cc353e5d6df172d60a11a0af905710a24b1e6be80f8fea6e96b
|
||||
F autoconf/Makefile.msc 2b4b5e5ff7e7a9806ebdd4b441f8fb54695a3628701a97ae53860e67e872acc3
|
||||
F autoconf/README.first 6c4f34fe115ff55d4e8dbfa3cecf04a0188292f7
|
||||
F autoconf/README.txt 4f04b0819303aabaa35fff5f7b257fb0c1ef95f1
|
||||
F autoconf/configure.ac 8dd08ca564279fff091c9bfdd2599d8f992c9f1f70c5396de2126ad2bd1b3bed
|
||||
@ -114,7 +116,7 @@ F ext/fts5/fts5_hash.c 32be400cf761868c9db33efe81a06eb19a17c5402ad477ee9efb51301
|
||||
F ext/fts5/fts5_index.c 5fe14375a29e8a7aa8f3e863babe180a19269206c254c8f47b216821d4ac1e15
|
||||
F ext/fts5/fts5_main.c 24868f88ab2a865defbba7a92eebeb726cc991eb092b71b5f5508f180c72605b
|
||||
F ext/fts5/fts5_storage.c fb5ef3c27073f67ade2e1bea08405f9e43f68f5f3676ed0ab7013bce5ba10be6
|
||||
F ext/fts5/fts5_tcl.c a7df39442ae674dde877cf06fe02ebb7658e69c179a4d223241c90df4f14b54e
|
||||
F ext/fts5/fts5_tcl.c b470467be4c5cab2d8b026992c05d86cd2293e7d8c4a10ba56d5f4f707981097
|
||||
F ext/fts5/fts5_test_mi.c 65864ba1e5c34a61d409c4c587e0bbe0466eb4f8f478d85dc42a92caad1338e6
|
||||
F ext/fts5/fts5_test_tok.c ffd657dd67e7fcdb31bf63fb60b6d867299a581d0f46e97086abacd66c2a9b26
|
||||
F ext/fts5/fts5_tokenize.c 2ce7b44183538ec46b7907726262ee43ffdd39a8
|
||||
@ -274,7 +276,7 @@ F ext/misc/compress.c 122faa92d25033d6c3f07c39231de074ab3d2e83
|
||||
F ext/misc/csv.c 1a009b93650732e22334edc92459c4630b9fa703397cbb3c8ca279921a36ca11
|
||||
F ext/misc/dbdump.c 3509fa6b8932d04e932d6b6b827b6a82ca362781b8e8f3c77336f416793e215e
|
||||
F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2
|
||||
F ext/misc/fileio.c b1aa06c0f1dac277695d4529e5e976c65ab5678dcbb53a0304deaa8adc44b332
|
||||
F ext/misc/fileio.c f16e2ef87928b768883b259b6c53a67076e98e8daa0bbb624910406d59630b26
|
||||
F ext/misc/fuzzer.c 7c64b8197bb77b7d64eff7cac7848870235d4c25
|
||||
F ext/misc/ieee754.c f190d0cc5182529acb15babd177781be1ac1718c
|
||||
F ext/misc/json1.c dbe086615b9546c156bf32b9378fc09383b58bd17513b866cfd24c1e15281984
|
||||
@ -291,14 +293,16 @@ F ext/misc/sha1.c 0b9e9b855354910d3ca467bf39099d570e73db56
|
||||
F ext/misc/shathree.c 9e960ba50483214c6a7a4b1517f8d8cef799e9db381195178c3fd3ad207e10c0
|
||||
F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
|
||||
F ext/misc/spellfix.c 41cf26c6b89fcaa8798ae10ae64d39c1f1d9d6995152e545bd491c13058b8fac
|
||||
F ext/misc/sqlar.c d355cd8b6e7280d2f61d4737672922acb512a2ab1cee52399ffb88980476e31c
|
||||
F ext/misc/stmt.c 6f16443abb3551e3f5813bb13ba19a30e7032830015b0f92fe0c0453045c0a11
|
||||
F ext/misc/totype.c 4a167594e791abeed95e0a8db028822b5e8fe512
|
||||
F ext/misc/unionvtab.c de36c2c45583d68f99e45b392311967066b02e2651d05697da783698b245b387
|
||||
F ext/misc/unionvtab.c 2aa94902ea646e1aaf6c05eac944a14276cddd67735b2ad856030ffffbb6626c
|
||||
F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95
|
||||
F ext/misc/vfsstat.c bf10ef0bc51e1ad6756629e1edb142f7a8db1178
|
||||
F ext/misc/vtablog.c 31d0d8f4406795679dcd3a67917c213d3a2a5fb3ea5de35f6e773491ed7e13c9
|
||||
F ext/misc/vtshim.c 1976e6dd68dd0d64508c91a6dfab8e75f8aaf6cd
|
||||
F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
|
||||
F ext/misc/zipfile.c 8075df9296beeebc344567927d114c6d3201110a29110013388d233fa7d4fb2c
|
||||
F ext/rbu/rbu.c ea7d1b7eb44c123a2a619332e19fe5313500705c4a58aaa1887905c0d83ffc2e
|
||||
F ext/rbu/rbu1.test 43836fac8c7179a358eaf38a8a1ef3d6e6285842
|
||||
F ext/rbu/rbu10.test 1846519a438697f45e9dcb246908af81b551c29e1078d0304fae83f1fed7e9ee
|
||||
@ -401,7 +405,7 @@ F ext/userauth/userauth.c 3410be31283abba70255d71fd24734e017a4497f
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
|
||||
F main.mk 59744c818d349d170ff56cdbdfb5af0e0a2029db18ce2824fcd1b0a3fa317d84
|
||||
F main.mk 0c0ed98340ad4b7ebdfe587144a13cb12dfe3f09c16a6af5b4d4a7f3a881f2f8
|
||||
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
|
||||
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
|
||||
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
|
||||
@ -479,7 +483,7 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
|
||||
F src/resolve.c bbee7e31d369a18a2f4836644769882e9c5d40ef4a3af911db06410b65cb3730
|
||||
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
|
||||
F src/select.c 8b22abe193e4d8243befa2038e4ae2405802fed1c446e5e502d11f652e09ba74
|
||||
F src/shell.c.in a418ddceef7a2789f18bdc2bcdd481b2562fe4a7754b8009c8dd33d5a67da332
|
||||
F src/shell.c.in 17fc28661aae277767db63fa90e644b8c9bf1242fbd167dcc2e7af0f0e620bb7
|
||||
F src/sqlite.h.in 1f1a2da222ec57465794e8984d77f32d0bd0da80cdc136beadda461a0be9d80c
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h c02d628cca67f3889c689d82d25c3eb45e2c155db08e4c6089b5840d64687d34
|
||||
@ -488,7 +492,7 @@ F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6
|
||||
F src/status.c 9737ed017279a9e0c5da748701c3c7bf1e8ae0dae459aad20dd64fcff97a7e35
|
||||
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
|
||||
F src/tclsqlite.c 1833388c01e3b77f4c712185ee7250b9423ee0981ce6ae7e401e47db0319a696
|
||||
F src/test1.c 8ef15f7a357f85dfc41c6c748ce9c947b4f676e01bb5ae6a45bee4923dff8b51
|
||||
F src/test1.c b52f9e7fe62016d357c3266fcfa0793cc1883d3cb2b11dfa39fcba2e70b0305c
|
||||
F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
|
||||
F src/test3.c b8434949dfb8aff8dfa082c8b592109e77844c2135ed3c492113839b6956255b
|
||||
F src/test4.c 18ec393bb4d0ad1de729f0b94da7267270f3d8e6
|
||||
@ -537,7 +541,7 @@ F src/test_thread.c 911d15fb14e19c0c542bdc8aabf981c2f10a4858
|
||||
F src/test_vfs.c f0186261a24de2671d080bcd8050732f0cb64f6e
|
||||
F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698
|
||||
F src/test_windirent.c 17f91f5f2aa1bb7328abb49414c363b5d2a9d3ff
|
||||
F src/test_windirent.h 5d67483a55442e31e1bde0f4a230e6e932ad5906
|
||||
F src/test_windirent.h 5afeb2b1e2920d5149573a4c30a69cba91f2e8a80e00941b738036fca39aca61
|
||||
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
|
||||
F src/tokenize.c 1003d6d90c6783206c711f0a9397656fa5b055209f4d092caa43bb3bf5215db5
|
||||
@ -1220,6 +1224,7 @@ F test/shell4.test 89ad573879a745974ff2df20ff97c5d6ffffbd5d
|
||||
F test/shell5.test 23939a4c51f0421330ea61dbd3c74f9c215f5f8d3d1a94846da6ffc777a35458
|
||||
F test/shell6.test 1ceb51b2678c472ba6cf1e5da96679ce8347889fe2c3bf93a0e0fa73f00b00d3
|
||||
F test/shell7.test 115132f66d0463417f408562cc2cf534f6bbc6d83a6d50f0072a9eb171bae97f
|
||||
F test/shell8.test 7585847402452d594f0e5f93430d34ed63b2f34ca7e956f63db157f9327c6896
|
||||
F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3
|
||||
F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5
|
||||
F test/shrink.test 1b4330b1fd9e818c04726d45cb28db73087535ce
|
||||
@ -1592,6 +1597,7 @@ F test/wordcount.c cb589cec469a1d90add05b1f8cee75c7210338d87a5afd65260ed5c0f4bbf
|
||||
F test/writecrash.test f1da7f7adfe8d7f09ea79b42e5ca6dcc41102f27f8e334ad71539501ddd910cc
|
||||
F test/zeroblob.test 3857870fe681b8185654414a9bccfde80b62a0fa
|
||||
F test/zerodamage.test e59a56443d6298ecf7435f618f0b27654f0c849e
|
||||
F test/zipfile.test d4f342ca918fd4d7981a12c1523f5041074cc592f25fecce4ee11446cc984f56
|
||||
F tool/GetFile.cs a15e08acb5dd7539b75ba23501581d7c2b462cb5
|
||||
F tool/GetTclKit.bat 8995df40c4209808b31f24de0b58f90930239a234f7591e3675d45bfbb990c5d
|
||||
F tool/Replace.cs 02c67258801c2fb5f63231e0ac0f220b4b36ba91
|
||||
@ -1688,7 +1694,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 05346f83d587e6049da0e8ec5f62c749daa9e80359cf48f8c37e333e6a1e7d2a
|
||||
R 3aef1a930039ecc41e90c5782e1f14e5
|
||||
U dan
|
||||
Z 26afacfab65aa4f19216c821cd8450d4
|
||||
P a4876672edea4e96103efd2463ce9a34a0b994a8744c941660940578aafbd454 1d6cee9ad448b10e69f351ef9dbec09110c5b189cba8734e637f41abe8f35bf8
|
||||
R af65ab601703ff43220f8cbd5a0328ec
|
||||
T +closed 1d6cee9ad448b10e69f351ef9dbec09110c5b189cba8734e637f41abe8f35bf8
|
||||
U drh
|
||||
Z 35e4e889b7dd1f4bdfa8de9e35b21bb1
|
||||
|
@ -1 +1 @@
|
||||
a4876672edea4e96103efd2463ce9a34a0b994a8744c941660940578aafbd454
|
||||
148b8aee78e40cab9a758a920589bd3ca8fc1c45cc93598bc50d96b85cd17e6c
|
790
src/shell.c.in
790
src/shell.c.in
@ -73,8 +73,9 @@
|
||||
# include <pwd.h>
|
||||
# endif
|
||||
# include <unistd.h>
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#if HAVE_READLINE
|
||||
# include <readline/readline.h>
|
||||
@ -874,9 +875,18 @@ static void shellAddSchemaName(
|
||||
#define SQLITE_EXTENSION_INIT1
|
||||
#define SQLITE_EXTENSION_INIT2(X) (void)(X)
|
||||
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
INCLUDE test_windirent.c
|
||||
#define dirent DIRENT
|
||||
#define timespec TIMESPEC
|
||||
#endif
|
||||
INCLUDE ../ext/misc/shathree.c
|
||||
INCLUDE ../ext/misc/fileio.c
|
||||
INCLUDE ../ext/misc/completion.c
|
||||
#ifdef SQLITE_HAVE_ZLIB
|
||||
INCLUDE ../ext/misc/zipfile.c
|
||||
INCLUDE ../ext/misc/sqlar.c
|
||||
#endif
|
||||
INCLUDE ../ext/expert/sqlite3expert.h
|
||||
INCLUDE ../ext/expert/sqlite3expert.c
|
||||
|
||||
@ -1945,7 +1955,7 @@ static void displayLinuxIoStats(FILE *out){
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<ArraySize(aTrans); i++){
|
||||
int n = (int)strlen(aTrans[i].zPattern);
|
||||
int n = strlen30(aTrans[i].zPattern);
|
||||
if( strncmp(aTrans[i].zPattern, z, n)==0 ){
|
||||
utf8_printf(out, "%-36s %s", aTrans[i].zDesc, &z[n]);
|
||||
break;
|
||||
@ -3079,6 +3089,10 @@ static void open_db(ShellState *p, int keepAlive){
|
||||
sqlite3_fileio_init(p->db, 0, 0);
|
||||
sqlite3_shathree_init(p->db, 0, 0);
|
||||
sqlite3_completion_init(p->db, 0, 0);
|
||||
#ifdef SQLITE_HAVE_ZLIB
|
||||
sqlite3_zipfile_init(p->db, 0, 0);
|
||||
sqlite3_sqlar_init(p->db, 0, 0);
|
||||
#endif
|
||||
sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0,
|
||||
shellAddSchemaName, 0, 0);
|
||||
sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0,
|
||||
@ -3120,7 +3134,7 @@ static char **readline_completion(const char *zText, int iStart, int iEnd){
|
||||
** Linenoise completion callback
|
||||
*/
|
||||
static void linenoise_completion(const char *zLine, linenoiseCompletions *lc){
|
||||
int nLine = (int)strlen(zLine);
|
||||
int nLine = strlen30(zLine);
|
||||
int i, iStart;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
char *zSql;
|
||||
@ -3346,7 +3360,7 @@ static int sql_trace_callback(
|
||||
UNUSED_PARAMETER(pP);
|
||||
if( f ){
|
||||
const char *z = (const char*)pX;
|
||||
int i = (int)strlen(z);
|
||||
int i = strlen30(z);
|
||||
while( i>0 && z[i-1]==';' ){ i--; }
|
||||
utf8_printf(f, "%.*s;\n", i, z);
|
||||
}
|
||||
@ -3535,7 +3549,7 @@ static void tryToCloneData(
|
||||
char *zInsert = 0;
|
||||
int rc;
|
||||
int i, j, n;
|
||||
int nTable = (int)strlen(zTable);
|
||||
int nTable = strlen30(zTable);
|
||||
int k = 0;
|
||||
int cnt = 0;
|
||||
const int spinRate = 10000;
|
||||
@ -3556,7 +3570,7 @@ static void tryToCloneData(
|
||||
}
|
||||
sqlite3_snprintf(200+nTable,zInsert,
|
||||
"INSERT OR IGNORE INTO \"%s\" VALUES(?", zTable);
|
||||
i = (int)strlen(zInsert);
|
||||
i = strlen30(zInsert);
|
||||
for(j=1; j<n; j++){
|
||||
memcpy(zInsert+i, ",?", 2);
|
||||
i += 2;
|
||||
@ -4138,7 +4152,7 @@ static int lintFkeyIndexes(
|
||||
const char *zGlobIPK = "SEARCH TABLE * USING INTEGER PRIMARY KEY (rowid=?)";
|
||||
|
||||
for(i=2; i<nArg; i++){
|
||||
int n = (int)strlen(azArg[i]);
|
||||
int n = strlen30(azArg[i]);
|
||||
if( n>1 && sqlite3_strnicmp("-verbose", azArg[i], n)==0 ){
|
||||
bVerbose = 1;
|
||||
}
|
||||
@ -4241,7 +4255,7 @@ static int lintDotCommand(
|
||||
int nArg /* Number of entries in azArg[] */
|
||||
){
|
||||
int n;
|
||||
n = (nArg>=2 ? (int)strlen(azArg[1]) : 0);
|
||||
n = (nArg>=2 ? strlen30(azArg[1]) : 0);
|
||||
if( n<1 || sqlite3_strnicmp(azArg[1], "fkey-indexes", n) ) goto usage;
|
||||
return lintFkeyIndexes(pState, azArg, nArg);
|
||||
|
||||
@ -4252,6 +4266,745 @@ static int lintDotCommand(
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
static void shellPrepare(
|
||||
sqlite3 *db,
|
||||
int *pRc,
|
||||
const char *zSql,
|
||||
sqlite3_stmt **ppStmt
|
||||
){
|
||||
*ppStmt = 0;
|
||||
if( *pRc==SQLITE_OK ){
|
||||
int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
raw_printf(stderr, "sql error: %s (%d)\n",
|
||||
sqlite3_errmsg(db), sqlite3_errcode(db)
|
||||
);
|
||||
*pRc = rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void shellPreparePrintf(
|
||||
sqlite3 *db,
|
||||
int *pRc,
|
||||
sqlite3_stmt **ppStmt,
|
||||
const char *zFmt,
|
||||
...
|
||||
){
|
||||
*ppStmt = 0;
|
||||
if( *pRc==SQLITE_OK ){
|
||||
va_list ap;
|
||||
char *z;
|
||||
va_start(ap, zFmt);
|
||||
z = sqlite3_vmprintf(zFmt, ap);
|
||||
if( z==0 ){
|
||||
*pRc = SQLITE_NOMEM;
|
||||
}else{
|
||||
shellPrepare(db, pRc, z, ppStmt);
|
||||
sqlite3_free(z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void shellFinalize(
|
||||
int *pRc,
|
||||
sqlite3_stmt *pStmt
|
||||
){
|
||||
if( pStmt ){
|
||||
sqlite3 *db = sqlite3_db_handle(pStmt);
|
||||
int rc = sqlite3_finalize(pStmt);
|
||||
if( *pRc==SQLITE_OK ){
|
||||
if( rc!=SQLITE_OK ){
|
||||
raw_printf(stderr, "SQL error: %s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
*pRc = rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void shellReset(
|
||||
int *pRc,
|
||||
sqlite3_stmt *pStmt
|
||||
){
|
||||
int rc = sqlite3_reset(pStmt);
|
||||
if( *pRc==SQLITE_OK ){
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3 *db = sqlite3_db_handle(pStmt);
|
||||
raw_printf(stderr, "SQL error: %s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
*pRc = rc;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Structure representing a single ".ar" command.
|
||||
*/
|
||||
typedef struct ArCommand ArCommand;
|
||||
struct ArCommand {
|
||||
int eCmd; /* An AR_CMD_* value */
|
||||
const char *zFile; /* --file argument, or NULL */
|
||||
const char *zDir; /* --directory argument, or NULL */
|
||||
int bVerbose; /* True if --verbose */
|
||||
int bZip; /* True if --zip */
|
||||
int nArg; /* Number of command arguments */
|
||||
char **azArg; /* Array of command arguments */
|
||||
};
|
||||
|
||||
/*
|
||||
** Print a usage message for the .ar command to stderr and return SQLITE_ERROR.
|
||||
*/
|
||||
static int arUsage(FILE *f){
|
||||
raw_printf(f,
|
||||
"\n"
|
||||
"Usage: .ar [OPTION...] [FILE...]\n"
|
||||
"The .ar command manages sqlar archives.\n"
|
||||
"\n"
|
||||
"Examples:\n"
|
||||
" .ar -cf archive.sar foo bar # Create archive.sar from files foo and bar\n"
|
||||
" .ar -tf archive.sar # List members of archive.sar\n"
|
||||
" .ar -xvf archive.sar # Verbosely extract files from archive.sar\n"
|
||||
"\n"
|
||||
"Each command line must feature exactly one command option:\n"
|
||||
" -c, --create Create a new archive\n"
|
||||
" -u, --update Update or add files to an existing archive\n"
|
||||
" -t, --list List contents of archive\n"
|
||||
" -x, --extract Extract files from archive\n"
|
||||
"\n"
|
||||
"And zero or more optional options:\n"
|
||||
" -v, --verbose Print each filename as it is processed\n"
|
||||
" -f FILE, --file FILE Operate on archive FILE (default is current db)\n"
|
||||
" -C DIR, --directory DIR Change to directory DIR to read/extract files\n"
|
||||
"\n"
|
||||
"See also: http://sqlite.org/cli.html#sqlar_archive_support\n"
|
||||
"\n"
|
||||
);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
** Print an error message for the .ar command to stderr and return
|
||||
** SQLITE_ERROR.
|
||||
*/
|
||||
static int arErrorMsg(const char *zFmt, ...){
|
||||
va_list ap;
|
||||
char *z;
|
||||
va_start(ap, zFmt);
|
||||
z = sqlite3_vmprintf(zFmt, ap);
|
||||
va_end(ap);
|
||||
raw_printf(stderr, "Error: %s (try \".ar --help\")\n", z);
|
||||
sqlite3_free(z);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
** Values for ArCommand.eCmd.
|
||||
*/
|
||||
#define AR_CMD_CREATE 1
|
||||
#define AR_CMD_EXTRACT 2
|
||||
#define AR_CMD_LIST 3
|
||||
#define AR_CMD_UPDATE 4
|
||||
#define AR_CMD_HELP 5
|
||||
|
||||
/*
|
||||
** Other (non-command) switches.
|
||||
*/
|
||||
#define AR_SWITCH_VERBOSE 6
|
||||
#define AR_SWITCH_FILE 7
|
||||
#define AR_SWITCH_DIRECTORY 8
|
||||
#define AR_SWITCH_ZIP 9
|
||||
|
||||
static int arProcessSwitch(ArCommand *pAr, int eSwitch, const char *zArg){
|
||||
switch( eSwitch ){
|
||||
case AR_CMD_CREATE:
|
||||
case AR_CMD_EXTRACT:
|
||||
case AR_CMD_LIST:
|
||||
case AR_CMD_UPDATE:
|
||||
case AR_CMD_HELP:
|
||||
if( pAr->eCmd ){
|
||||
return arErrorMsg("multiple command options");
|
||||
}
|
||||
pAr->eCmd = eSwitch;
|
||||
break;
|
||||
|
||||
case AR_SWITCH_VERBOSE:
|
||||
pAr->bVerbose = 1;
|
||||
break;
|
||||
case AR_SWITCH_ZIP:
|
||||
pAr->bZip = 1;
|
||||
break;
|
||||
|
||||
case AR_SWITCH_FILE:
|
||||
pAr->zFile = zArg;
|
||||
break;
|
||||
case AR_SWITCH_DIRECTORY:
|
||||
pAr->zDir = zArg;
|
||||
break;
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Parse the command line for an ".ar" command. The results are written into
|
||||
** structure (*pAr). SQLITE_OK is returned if the command line is parsed
|
||||
** successfully, otherwise an error message is written to stderr and
|
||||
** SQLITE_ERROR returned.
|
||||
*/
|
||||
static int arParseCommand(
|
||||
char **azArg, /* Array of arguments passed to dot command */
|
||||
int nArg, /* Number of entries in azArg[] */
|
||||
ArCommand *pAr /* Populate this object */
|
||||
){
|
||||
struct ArSwitch {
|
||||
char cShort;
|
||||
const char *zLong;
|
||||
int eSwitch;
|
||||
int bArg;
|
||||
} aSwitch[] = {
|
||||
{ 'c', "create", AR_CMD_CREATE, 0 },
|
||||
{ 'x', "extract", AR_CMD_EXTRACT, 0 },
|
||||
{ 't', "list", AR_CMD_LIST, 0 },
|
||||
{ 'u', "update", AR_CMD_UPDATE, 0 },
|
||||
{ 'h', "help", AR_CMD_HELP, 0 },
|
||||
{ 'v', "verbose", AR_SWITCH_VERBOSE, 0 },
|
||||
{ 'f', "file", AR_SWITCH_FILE, 1 },
|
||||
{ 'C', "directory", AR_SWITCH_DIRECTORY, 1 },
|
||||
{ 'z', "zip", AR_SWITCH_ZIP, 0 }
|
||||
};
|
||||
int nSwitch = sizeof(aSwitch) / sizeof(struct ArSwitch);
|
||||
struct ArSwitch *pEnd = &aSwitch[nSwitch];
|
||||
|
||||
if( nArg<=1 ){
|
||||
return arUsage(stderr);
|
||||
}else{
|
||||
char *z = azArg[1];
|
||||
memset(pAr, 0, sizeof(ArCommand));
|
||||
|
||||
if( z[0]!='-' ){
|
||||
/* Traditional style [tar] invocation */
|
||||
int i;
|
||||
int iArg = 2;
|
||||
for(i=0; z[i]; i++){
|
||||
const char *zArg = 0;
|
||||
struct ArSwitch *pOpt;
|
||||
for(pOpt=&aSwitch[0]; pOpt<pEnd; pOpt++){
|
||||
if( z[i]==pOpt->cShort ) break;
|
||||
}
|
||||
if( pOpt==pEnd ){
|
||||
return arErrorMsg("unrecognized option: %c", z[i]);
|
||||
}
|
||||
if( pOpt->bArg ){
|
||||
if( iArg>=nArg ){
|
||||
return arErrorMsg("option requires an argument: %c",z[i]);
|
||||
}
|
||||
zArg = azArg[iArg++];
|
||||
}
|
||||
if( arProcessSwitch(pAr, pOpt->eSwitch, zArg) ) return SQLITE_ERROR;
|
||||
}
|
||||
pAr->nArg = nArg-iArg;
|
||||
if( pAr->nArg>0 ){
|
||||
pAr->azArg = &azArg[iArg];
|
||||
}
|
||||
}else{
|
||||
/* Non-traditional invocation */
|
||||
int iArg;
|
||||
for(iArg=1; iArg<nArg; iArg++){
|
||||
int n;
|
||||
z = azArg[iArg];
|
||||
if( z[0]!='-' ){
|
||||
/* All remaining command line words are command arguments. */
|
||||
pAr->azArg = &azArg[iArg];
|
||||
pAr->nArg = nArg-iArg;
|
||||
break;
|
||||
}
|
||||
n = strlen30(z);
|
||||
|
||||
if( z[1]!='-' ){
|
||||
int i;
|
||||
/* One or more short options */
|
||||
for(i=1; i<n; i++){
|
||||
const char *zArg = 0;
|
||||
struct ArSwitch *pOpt;
|
||||
for(pOpt=&aSwitch[0]; pOpt<pEnd; pOpt++){
|
||||
if( z[i]==pOpt->cShort ) break;
|
||||
}
|
||||
if( pOpt==pEnd ){
|
||||
return arErrorMsg("unrecognized option: %c\n", z[i]);
|
||||
}
|
||||
if( pOpt->bArg ){
|
||||
if( i<(n-1) ){
|
||||
zArg = &z[i+1];
|
||||
i = n;
|
||||
}else{
|
||||
if( iArg>=(nArg-1) ){
|
||||
return arErrorMsg("option requires an argument: %c\n",z[i]);
|
||||
}
|
||||
zArg = azArg[++iArg];
|
||||
}
|
||||
}
|
||||
if( arProcessSwitch(pAr, pOpt->eSwitch, zArg) ) return SQLITE_ERROR;
|
||||
}
|
||||
}else if( z[2]=='\0' ){
|
||||
/* A -- option, indicating that all remaining command line words
|
||||
** are command arguments. */
|
||||
pAr->azArg = &azArg[iArg+1];
|
||||
pAr->nArg = nArg-iArg-1;
|
||||
break;
|
||||
}else{
|
||||
/* A long option */
|
||||
const char *zArg = 0; /* Argument for option, if any */
|
||||
struct ArSwitch *pMatch = 0; /* Matching option */
|
||||
struct ArSwitch *pOpt; /* Iterator */
|
||||
for(pOpt=&aSwitch[0]; pOpt<pEnd; pOpt++){
|
||||
const char *zLong = pOpt->zLong;
|
||||
if( (n-2)<=strlen30(zLong) && 0==memcmp(&z[2], zLong, n-2) ){
|
||||
if( pMatch ){
|
||||
return arErrorMsg("ambiguous option: %s",z);
|
||||
}else{
|
||||
pMatch = pOpt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( pMatch==0 ){
|
||||
return arErrorMsg("unrecognized option: %s", z);
|
||||
}
|
||||
if( pMatch->bArg ){
|
||||
if( iArg>=(nArg-1) ){
|
||||
return arErrorMsg("option requires an argument: %s", z);
|
||||
}
|
||||
zArg = azArg[++iArg];
|
||||
}
|
||||
if( arProcessSwitch(pAr, pMatch->eSwitch, zArg) ) return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function assumes that all arguments within the ArCommand.azArg[]
|
||||
** array refer to archive members, as for the --extract or --list commands.
|
||||
** It checks that each of them are present. If any specified file is not
|
||||
** present in the archive, an error is printed to stderr and an error
|
||||
** code returned. Otherwise, if all specified arguments are present in
|
||||
** the archive, SQLITE_OK is returned.
|
||||
**
|
||||
** This function strips any trailing '/' characters from each argument.
|
||||
** This is consistent with the way the [tar] command seems to work on
|
||||
** Linux.
|
||||
*/
|
||||
static int arCheckEntries(sqlite3 *db, ArCommand *pAr){
|
||||
int rc = SQLITE_OK;
|
||||
if( pAr->nArg ){
|
||||
int i;
|
||||
sqlite3_stmt *pTest = 0;
|
||||
|
||||
shellPreparePrintf(db, &rc, &pTest, "SELECT name FROM %s WHERE name=?1",
|
||||
pAr->bZip ? "zipfile(?2)" : "sqlar"
|
||||
);
|
||||
if( rc==SQLITE_OK && pAr->bZip ){
|
||||
sqlite3_bind_text(pTest, 2, pAr->zFile, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){
|
||||
char *z = pAr->azArg[i];
|
||||
int n = strlen30(z);
|
||||
int bOk = 0;
|
||||
while( n>0 && z[n-1]=='/' ) n--;
|
||||
z[n] = '\0';
|
||||
sqlite3_bind_text(pTest, 1, z, -1, SQLITE_STATIC);
|
||||
if( SQLITE_ROW==sqlite3_step(pTest) ){
|
||||
bOk = 1;
|
||||
}
|
||||
shellReset(&rc, pTest);
|
||||
if( rc==SQLITE_OK && bOk==0 ){
|
||||
raw_printf(stderr, "not found in archive: %s\n", z);
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
shellFinalize(&rc, pTest);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Format a WHERE clause that can be used against the "sqlar" table to
|
||||
** identify all archive members that match the command arguments held
|
||||
** in (*pAr). Leave this WHERE clause in (*pzWhere) before returning.
|
||||
** The caller is responsible for eventually calling sqlite3_free() on
|
||||
** any non-NULL (*pzWhere) value.
|
||||
*/
|
||||
static void arWhereClause(
|
||||
int *pRc,
|
||||
ArCommand *pAr,
|
||||
char **pzWhere /* OUT: New WHERE clause */
|
||||
){
|
||||
char *zWhere = 0;
|
||||
if( *pRc==SQLITE_OK ){
|
||||
if( pAr->nArg==0 ){
|
||||
zWhere = sqlite3_mprintf("1");
|
||||
}else{
|
||||
int i;
|
||||
const char *zSep = "";
|
||||
for(i=0; i<pAr->nArg; i++){
|
||||
const char *z = pAr->azArg[i];
|
||||
zWhere = sqlite3_mprintf(
|
||||
"%z%s name = '%q' OR name BETWEEN '%q/' AND '%q0'",
|
||||
zWhere, zSep, z, z, z
|
||||
);
|
||||
if( zWhere==0 ){
|
||||
*pRc = SQLITE_NOMEM;
|
||||
break;
|
||||
}
|
||||
zSep = " OR ";
|
||||
}
|
||||
}
|
||||
}
|
||||
*pzWhere = zWhere;
|
||||
}
|
||||
|
||||
/*
|
||||
** Argument zMode must point to a buffer at least 11 bytes in size. This
|
||||
** function populates this buffer with the string interpretation of
|
||||
** the unix file mode passed as the second argument (e.g. "drwxr-xr-x").
|
||||
*/
|
||||
static void shellModeToString(char *zMode, int mode){
|
||||
int i;
|
||||
|
||||
/* Magic numbers copied from [man 2 stat] */
|
||||
if( mode & 0040000 ){
|
||||
zMode[0] = 'd';
|
||||
}else if( (mode & 0120000)==0120000 ){
|
||||
zMode[0] = 'l';
|
||||
}else{
|
||||
zMode[0] = '-';
|
||||
}
|
||||
|
||||
for(i=0; i<3; i++){
|
||||
int m = (mode >> ((2-i)*3));
|
||||
char *a = &zMode[1 + i*3];
|
||||
a[0] = (m & 0x4) ? 'r' : '-';
|
||||
a[1] = (m & 0x2) ? 'w' : '-';
|
||||
a[2] = (m & 0x1) ? 'x' : '-';
|
||||
}
|
||||
zMode[10] = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of .ar "lisT" command.
|
||||
*/
|
||||
static int arListCommand(ShellState *p, sqlite3 *db, ArCommand *pAr){
|
||||
const char *zSql = "SELECT %s FROM %s WHERE %s";
|
||||
const char *zTbl = (pAr->bZip ? "zipfile(?)" : "sqlar");
|
||||
const char *azCols[] = {
|
||||
"name",
|
||||
"mode, sz, datetime(mtime, 'unixepoch'), name"
|
||||
};
|
||||
|
||||
char *zWhere = 0;
|
||||
sqlite3_stmt *pSql = 0;
|
||||
int rc;
|
||||
|
||||
rc = arCheckEntries(db, pAr);
|
||||
arWhereClause(&rc, pAr, &zWhere);
|
||||
|
||||
shellPreparePrintf(db, &rc, &pSql, zSql, azCols[pAr->bVerbose], zTbl, zWhere);
|
||||
if( rc==SQLITE_OK && pAr->bZip ){
|
||||
sqlite3_bind_text(pSql, 1, pAr->zFile, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
|
||||
if( pAr->bVerbose ){
|
||||
char zMode[11];
|
||||
shellModeToString(zMode, sqlite3_column_int(pSql, 0));
|
||||
|
||||
raw_printf(p->out, "%s % 10d %s %s\n", zMode,
|
||||
sqlite3_column_int(pSql, 1),
|
||||
sqlite3_column_text(pSql, 2),
|
||||
sqlite3_column_text(pSql, 3)
|
||||
);
|
||||
}else{
|
||||
raw_printf(p->out, "%s\n", sqlite3_column_text(pSql, 0));
|
||||
}
|
||||
}
|
||||
|
||||
shellFinalize(&rc, pSql);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Implementation of .ar "eXtract" command.
|
||||
*/
|
||||
static int arExtractCommand(ShellState *p, sqlite3 *db, ArCommand *pAr){
|
||||
const char *zSql1 =
|
||||
"SELECT "
|
||||
" :1 || name, "
|
||||
" writefile(?1 || name, %s, mode, mtime) "
|
||||
"FROM %s WHERE (%s) AND (data IS NULL OR ?2 = 0)";
|
||||
|
||||
const char *azExtraArg[] = {
|
||||
"sqlar_uncompress(data, sz)",
|
||||
"zipfile_uncompress(data, sz, method)"
|
||||
};
|
||||
const char *azSource[] = {
|
||||
"sqlar", "zipfile(?3)"
|
||||
};
|
||||
|
||||
|
||||
|
||||
sqlite3_stmt *pSql = 0;
|
||||
int rc = SQLITE_OK;
|
||||
char *zDir = 0;
|
||||
char *zWhere = 0;
|
||||
int i;
|
||||
|
||||
/* If arguments are specified, check that they actually exist within
|
||||
** the archive before proceeding. And formulate a WHERE clause to
|
||||
** match them. */
|
||||
rc = arCheckEntries(db, pAr);
|
||||
arWhereClause(&rc, pAr, &zWhere);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
if( pAr->zDir ){
|
||||
zDir = sqlite3_mprintf("%s/", pAr->zDir);
|
||||
}else{
|
||||
zDir = sqlite3_mprintf("");
|
||||
}
|
||||
if( zDir==0 ) rc = SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
shellPreparePrintf(db, &rc, &pSql, zSql1,
|
||||
azExtraArg[pAr->bZip], azSource[pAr->bZip], zWhere
|
||||
);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_text(pSql, 1, zDir, -1, SQLITE_STATIC);
|
||||
if( pAr->bZip ){
|
||||
sqlite3_bind_text(pSql, 3, pAr->zFile, -1, SQLITE_STATIC);
|
||||
}
|
||||
|
||||
/* Run the SELECT statement twice. The first time, writefile() is called
|
||||
** for all archive members that should be extracted. The second time,
|
||||
** only for the directories. This is because the timestamps for
|
||||
** extracted directories must be reset after they are populated (as
|
||||
** populating them changes the timestamp). */
|
||||
for(i=0; i<2; i++){
|
||||
sqlite3_bind_int(pSql, 2, i);
|
||||
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
|
||||
if( i==0 && pAr->bVerbose ){
|
||||
raw_printf(p->out, "%s\n", sqlite3_column_text(pSql, 0));
|
||||
}
|
||||
}
|
||||
shellReset(&rc, pSql);
|
||||
}
|
||||
shellFinalize(&rc, pSql);
|
||||
}
|
||||
|
||||
sqlite3_free(zDir);
|
||||
sqlite3_free(zWhere);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Implementation of .ar "create" and "update" commands.
|
||||
**
|
||||
** Create the "sqlar" table in the database if it does not already exist.
|
||||
** Then add each file in the azFile[] array to the archive. Directories
|
||||
** are added recursively. If argument bVerbose is non-zero, a message is
|
||||
** printed on stdout for each file archived.
|
||||
**
|
||||
** The create command is the same as update, except that it drops
|
||||
** any existing "sqlar" table before beginning.
|
||||
*/
|
||||
static int arCreateUpdate(
|
||||
ShellState *p, /* Shell state pointer */
|
||||
sqlite3 *db,
|
||||
ArCommand *pAr, /* Command arguments and options */
|
||||
int bUpdate
|
||||
){
|
||||
const char *zSql = "SELECT name, mode, mtime, data FROM fsdir(?, ?)";
|
||||
const char *zCreate =
|
||||
"CREATE TABLE IF NOT EXISTS sqlar("
|
||||
"name TEXT PRIMARY KEY, -- name of the file\n"
|
||||
"mode INT, -- access permissions\n"
|
||||
"mtime INT, -- last modification time\n"
|
||||
"sz INT, -- original file size\n"
|
||||
"data BLOB -- compressed content\n"
|
||||
")";
|
||||
const char *zDrop = "DROP TABLE IF EXISTS sqlar";
|
||||
const char *zInsert = "REPLACE INTO sqlar VALUES(?,?,?,?,sqlar_compress(?))";
|
||||
|
||||
sqlite3_stmt *pStmt = 0; /* Directory traverser */
|
||||
sqlite3_stmt *pInsert = 0; /* Compilation of zInsert */
|
||||
int i; /* For iterating through azFile[] */
|
||||
int rc; /* Return code */
|
||||
|
||||
assert( pAr->bZip==0 );
|
||||
|
||||
rc = sqlite3_exec(db, "SAVEPOINT ar;", 0, 0, 0);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
if( bUpdate==0 ){
|
||||
rc = sqlite3_exec(db, zDrop, 0, 0, 0);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
}
|
||||
|
||||
rc = sqlite3_exec(db, zCreate, 0, 0, 0);
|
||||
shellPrepare(db, &rc, zInsert, &pInsert);
|
||||
shellPrepare(db, &rc, zSql, &pStmt);
|
||||
sqlite3_bind_text(pStmt, 2, pAr->zDir, -1, SQLITE_STATIC);
|
||||
|
||||
for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){
|
||||
sqlite3_bind_text(pStmt, 1, pAr->azArg[i], -1, SQLITE_STATIC);
|
||||
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
int sz;
|
||||
const char *zName = (const char*)sqlite3_column_text(pStmt, 0);
|
||||
int mode = sqlite3_column_int(pStmt, 1);
|
||||
unsigned int mtime = sqlite3_column_int(pStmt, 2);
|
||||
|
||||
if( pAr->bVerbose ){
|
||||
raw_printf(p->out, "%s\n", zName);
|
||||
}
|
||||
|
||||
sqlite3_bind_text(pInsert, 1, zName, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_int(pInsert, 2, mode);
|
||||
sqlite3_bind_int64(pInsert, 3, (sqlite3_int64)mtime);
|
||||
|
||||
if( S_ISDIR(mode) ){
|
||||
sz = 0;
|
||||
sqlite3_bind_null(pInsert, 5);
|
||||
}else{
|
||||
sqlite3_bind_value(pInsert, 5, sqlite3_column_value(pStmt, 3));
|
||||
if( S_ISLNK(mode) ){
|
||||
sz = -1;
|
||||
}else{
|
||||
sz = sqlite3_column_bytes(pStmt, 3);
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_bind_int(pInsert, 4, sz);
|
||||
sqlite3_step(pInsert);
|
||||
rc = sqlite3_reset(pInsert);
|
||||
}
|
||||
shellReset(&rc, pStmt);
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_exec(db, "ROLLBACK TO ar; RELEASE ar;", 0, 0, 0);
|
||||
}else{
|
||||
rc = sqlite3_exec(db, "RELEASE ar;", 0, 0, 0);
|
||||
}
|
||||
shellFinalize(&rc, pStmt);
|
||||
shellFinalize(&rc, pInsert);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of .ar "Create" command.
|
||||
**
|
||||
** Create the "sqlar" table in the database if it does not already exist.
|
||||
** Then add each file in the azFile[] array to the archive. Directories
|
||||
** are added recursively. If argument bVerbose is non-zero, a message is
|
||||
** printed on stdout for each file archived.
|
||||
*/
|
||||
static int arCreateCommand(
|
||||
ShellState *p, /* Shell state pointer */
|
||||
sqlite3 *db,
|
||||
ArCommand *pAr /* Command arguments and options */
|
||||
){
|
||||
return arCreateUpdate(p, db, pAr, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of .ar "Update" command.
|
||||
*/
|
||||
static int arUpdateCmd(ShellState *p, sqlite3 *db, ArCommand *pAr){
|
||||
return arCreateUpdate(p, db, pAr, 1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Implementation of ".ar" dot command.
|
||||
*/
|
||||
static int arDotCommand(
|
||||
ShellState *pState, /* Current shell tool state */
|
||||
char **azArg, /* Array of arguments passed to dot command */
|
||||
int nArg /* Number of entries in azArg[] */
|
||||
){
|
||||
ArCommand cmd;
|
||||
int rc;
|
||||
rc = arParseCommand(azArg, nArg, &cmd);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3 *db = 0; /* Database handle to use as archive */
|
||||
|
||||
if( cmd.bZip ){
|
||||
if( cmd.zFile==0 ){
|
||||
raw_printf(stderr, "zip format requires a --file switch\n");
|
||||
return SQLITE_ERROR;
|
||||
}else
|
||||
if( cmd.eCmd==AR_CMD_CREATE || cmd.eCmd==AR_CMD_UPDATE ){
|
||||
raw_printf(stderr, "zip archives are read-only\n");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
db = pState->db;
|
||||
}else if( cmd.zFile ){
|
||||
int flags;
|
||||
if( cmd.eCmd==AR_CMD_CREATE || cmd.eCmd==AR_CMD_UPDATE ){
|
||||
flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
|
||||
}else{
|
||||
flags = SQLITE_OPEN_READONLY;
|
||||
}
|
||||
rc = sqlite3_open_v2(cmd.zFile, &db, flags, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
raw_printf(stderr, "cannot open file: %s (%s)\n",
|
||||
cmd.zFile, sqlite3_errmsg(db)
|
||||
);
|
||||
sqlite3_close(db);
|
||||
return rc;
|
||||
}
|
||||
sqlite3_fileio_init(db, 0, 0);
|
||||
#ifdef SQLITE_HAVE_ZLIB
|
||||
sqlite3_sqlar_init(db, 0, 0);
|
||||
#endif
|
||||
}else{
|
||||
db = pState->db;
|
||||
}
|
||||
|
||||
switch( cmd.eCmd ){
|
||||
case AR_CMD_CREATE:
|
||||
rc = arCreateCommand(pState, db, &cmd);
|
||||
break;
|
||||
|
||||
case AR_CMD_EXTRACT:
|
||||
rc = arExtractCommand(pState, db, &cmd);
|
||||
break;
|
||||
|
||||
case AR_CMD_LIST:
|
||||
rc = arListCommand(pState, db, &cmd);
|
||||
break;
|
||||
|
||||
case AR_CMD_HELP:
|
||||
arUsage(pState->out);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert( cmd.eCmd==AR_CMD_UPDATE );
|
||||
rc = arUpdateCmd(pState, db, &cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
if( cmd.zFile ){
|
||||
sqlite3_close(db);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of ".expert" dot command.
|
||||
*/
|
||||
@ -4272,7 +5025,7 @@ static int expertDotCommand(
|
||||
char *z = azArg[i];
|
||||
int n;
|
||||
if( z[0]=='-' && z[1]=='-' ) z++;
|
||||
n = strlen(z);
|
||||
n = strlen30(z);
|
||||
if( n>=2 && 0==strncmp(z, "-verbose", n) ){
|
||||
pState->expert.bVerbose = 1;
|
||||
}
|
||||
@ -4310,7 +5063,6 @@ static int expertDotCommand(
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** If an input line begins with "." then invoke this routine to
|
||||
** process that line.
|
||||
@ -4374,6 +5126,13 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
}else
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
if( c=='a' && strncmp(azArg[0], "ar", n)==0 ){
|
||||
open_db(p, 0);
|
||||
rc = arDotCommand(p, azArg, nArg);
|
||||
}else
|
||||
#endif
|
||||
|
||||
if( (c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0)
|
||||
|| (c=='s' && n>=3 && strncmp(azArg[0], "save", n)==0)
|
||||
){
|
||||
@ -5149,7 +5908,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
|
||||
if( c=='m' && strncmp(azArg[0], "mode", n)==0 ){
|
||||
const char *zMode = nArg>=2 ? azArg[1] : "";
|
||||
int n2 = (int)strlen(zMode);
|
||||
int n2 = strlen30(zMode);
|
||||
int c2 = zMode[0];
|
||||
if( c2=='l' && n2>2 && strncmp(azArg[1],"lines",n2)==0 ){
|
||||
p->mode = MODE_Line;
|
||||
@ -6388,8 +7147,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
rc = sqlite3_user_authenticate(p->db, azArg[2], azArg[3],
|
||||
(int)strlen(azArg[3]));
|
||||
rc = sqlite3_user_authenticate(p->db, azArg[2], azArg[3], strlen30(azArg[3]));
|
||||
if( rc ){
|
||||
utf8_printf(stderr, "Authentication failed for user %s\n", azArg[2]);
|
||||
rc = 1;
|
||||
@ -6400,8 +7158,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
rc = sqlite3_user_add(p->db, azArg[2],
|
||||
azArg[3], (int)strlen(azArg[3]),
|
||||
rc = sqlite3_user_add(p->db, azArg[2], azArg[3], strlen30(azArg[3]),
|
||||
booleanValue(azArg[4]));
|
||||
if( rc ){
|
||||
raw_printf(stderr, "User-Add failed: %d\n", rc);
|
||||
@ -6413,8 +7170,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
rc = sqlite3_user_change(p->db, azArg[2],
|
||||
azArg[3], (int)strlen(azArg[3]),
|
||||
rc = sqlite3_user_change(p->db, azArg[2], azArg[3], strlen30(azArg[3]),
|
||||
booleanValue(azArg[4]));
|
||||
if( rc ){
|
||||
raw_printf(stderr, "User-Edit failed: %d\n", rc);
|
||||
|
@ -6960,6 +6960,9 @@ static int SQLITE_TCLAPI tclLoadStaticExtensionCmd(
|
||||
extern int sqlite3_totype_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_wholenumber_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_unionvtab_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
#ifdef SQLITE_HAVE_ZLIB
|
||||
extern int sqlite3_zipfile_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
#endif
|
||||
static const struct {
|
||||
const char *zExtName;
|
||||
int (*pInit)(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
@ -6981,6 +6984,9 @@ static int SQLITE_TCLAPI tclLoadStaticExtensionCmd(
|
||||
{ "totype", sqlite3_totype_init },
|
||||
{ "unionvtab", sqlite3_unionvtab_init },
|
||||
{ "wholenumber", sqlite3_wholenumber_init },
|
||||
#ifdef SQLITE_HAVE_ZLIB
|
||||
{ "zipfile", sqlite3_zipfile_init },
|
||||
#endif
|
||||
};
|
||||
sqlite3 *db;
|
||||
const char *zName;
|
||||
|
@ -37,6 +37,33 @@
|
||||
#include <errno.h>
|
||||
#include <io.h>
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
/*
|
||||
** We may need several defines that should have been in "sys/stat.h".
|
||||
*/
|
||||
|
||||
#ifndef S_ISREG
|
||||
#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
|
||||
#endif
|
||||
|
||||
#ifndef S_ISDIR
|
||||
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
|
||||
#endif
|
||||
|
||||
#ifndef S_ISLNK
|
||||
#define S_ISLNK(mode) (0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** We may need to provide the "mode_t" type.
|
||||
*/
|
||||
|
||||
#ifndef MODE_T_DEFINED
|
||||
#define MODE_T_DEFINED
|
||||
typedef unsigned short mode_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** We may need to provide the "ino_t" type.
|
||||
@ -75,22 +102,27 @@
|
||||
** We need to provide the necessary structures and related types.
|
||||
*/
|
||||
|
||||
#ifndef DIRENT_DEFINED
|
||||
#define DIRENT_DEFINED
|
||||
typedef struct DIRENT DIRENT;
|
||||
typedef struct DIR DIR;
|
||||
typedef DIRENT *LPDIRENT;
|
||||
typedef DIR *LPDIR;
|
||||
|
||||
struct DIRENT {
|
||||
ino_t d_ino; /* Sequence number, do not use. */
|
||||
unsigned d_attributes; /* Win32 file attributes. */
|
||||
char d_name[NAME_MAX + 1]; /* Name within the directory. */
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef DIR_DEFINED
|
||||
#define DIR_DEFINED
|
||||
typedef struct DIR DIR;
|
||||
typedef DIR *LPDIR;
|
||||
struct DIR {
|
||||
intptr_t d_handle; /* Value returned by "_findfirst". */
|
||||
DIRENT d_first; /* DIRENT constructed based on "_findfirst". */
|
||||
DIRENT d_next; /* DIRENT constructed based on "_findnext". */
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Provide a macro, for use by the implementation, to determine if a
|
||||
|
168
test/shell8.test
Normal file
168
test/shell8.test
Normal file
@ -0,0 +1,168 @@
|
||||
# 2017 December 9
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Test the shell tool ".ar" command.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix shell8
|
||||
set CLI [test_find_cli]
|
||||
|
||||
proc populate_dir {dirname spec} {
|
||||
# First delete the current tree, if one exists.
|
||||
file delete -force $dirname
|
||||
|
||||
# Recreate the root of the new tree.
|
||||
file mkdir $dirname
|
||||
|
||||
# Add each file to the new tree.
|
||||
foreach {f d} $spec {
|
||||
set path [file join $dirname $f]
|
||||
file mkdir [file dirname $path]
|
||||
set fd [open $path w]
|
||||
puts -nonewline $fd $d
|
||||
close $fd
|
||||
}
|
||||
}
|
||||
|
||||
proc dir_to_list {dirname {n -1}} {
|
||||
if {$n<0} {set n [llength [file split $dirname]]}
|
||||
|
||||
set res [list]
|
||||
foreach f [glob -nocomplain $dirname/*] {
|
||||
set mtime [file mtime $f]
|
||||
if {$::tcl_platform(platform)!="windows"} {
|
||||
set perm [file attributes $f -perm]
|
||||
} else {
|
||||
set perm 0
|
||||
}
|
||||
set relpath [file join {*}[lrange [file split $f] $n end]]
|
||||
lappend res
|
||||
if {[file isdirectory $f]} {
|
||||
lappend res [list $relpath / $mtime $perm]
|
||||
lappend res {*}[dir_to_list $f]
|
||||
} else {
|
||||
set fd [open $f]
|
||||
set data [read $fd]
|
||||
close $fd
|
||||
lappend res [list $relpath $data $mtime $perm]
|
||||
}
|
||||
}
|
||||
lsort $res
|
||||
}
|
||||
|
||||
proc dir_compare {d1 d2} {
|
||||
set l1 [dir_to_list $d1]
|
||||
set l2 [dir_to_list $d1]
|
||||
string compare $l1 $l2
|
||||
}
|
||||
|
||||
foreach {tn tcl} {
|
||||
1 {
|
||||
set c1 ".ar c ar1"
|
||||
set x1 ".ar x"
|
||||
|
||||
set c2 ".ar cC ar1 ."
|
||||
set x2 ".ar Cx ar3"
|
||||
|
||||
set c3 ".ar cCf ar1 test_xyz.db ."
|
||||
set x3 ".ar Cfx ar3 test_xyz.db"
|
||||
}
|
||||
|
||||
2 {
|
||||
set c1 ".ar -c ar1"
|
||||
set x1 ".ar -x"
|
||||
|
||||
set c2 ".ar -cC ar1 ."
|
||||
set x2 ".ar -xC ar3"
|
||||
|
||||
set c3 ".ar -cCar1 -ftest_xyz.db ."
|
||||
set x3 ".ar -x -C ar3 -f test_xyz.db"
|
||||
}
|
||||
|
||||
3 {
|
||||
set c1 ".ar --create ar1"
|
||||
set x1 ".ar --extract"
|
||||
|
||||
set c2 ".ar --directory ar1 --create ."
|
||||
set x2 ".ar --extract --dir ar3"
|
||||
|
||||
set c3 ".ar --creat --dir ar1 --file test_xyz.db ."
|
||||
set x3 ".ar --e --d ar3 --f test_xyz.db"
|
||||
}
|
||||
|
||||
4 {
|
||||
set c1 ".ar --cr ar1"
|
||||
set x1 ".ar --e"
|
||||
|
||||
set c2 ".ar -C ar1 -c ."
|
||||
set x2 ".ar -x -C ar3"
|
||||
|
||||
set c3 ".ar -c --directory ar1 --file test_xyz.db ."
|
||||
set x3 ".ar -x --directory ar3 --file test_xyz.db"
|
||||
}
|
||||
} {
|
||||
eval $tcl
|
||||
|
||||
# Populate directory "ar1" with some files.
|
||||
#
|
||||
populate_dir ar1 {
|
||||
file1 "abcd"
|
||||
file2 "efgh"
|
||||
dir1/file3 "ijkl"
|
||||
}
|
||||
set expected [dir_to_list ar1]
|
||||
|
||||
do_test 1.$tn.1 {
|
||||
catchcmd test_ar.db $c1
|
||||
file delete -force ar1
|
||||
catchcmd test_ar.db $x1
|
||||
dir_to_list ar1
|
||||
} $expected
|
||||
|
||||
do_test 1.$tn.2 {
|
||||
file delete -force ar3
|
||||
catchcmd test_ar.db $c2
|
||||
catchcmd test_ar.db $x2
|
||||
dir_to_list ar3
|
||||
} $expected
|
||||
|
||||
do_test 1.$tn.3 {
|
||||
file delete -force ar3
|
||||
file delete -force test_xyz.db
|
||||
catchcmd ":memory:" $c3
|
||||
catchcmd ":memory:" $x3
|
||||
dir_to_list ar3
|
||||
} $expected
|
||||
|
||||
# This is a repeat of test 1.$tn.1, except that there is a 2 second
|
||||
# pause between creating the archive and extracting its contents.
|
||||
# This is to test that timestamps are set correctly.
|
||||
#
|
||||
# Because it is slow, only do this for $tn==1.
|
||||
if {$tn==1} {
|
||||
do_test 1.$tn.1 {
|
||||
catchcmd test_ar.db $c1
|
||||
file delete -force ar1
|
||||
after 2000
|
||||
catchcmd test_ar.db $x1
|
||||
dir_to_list ar1
|
||||
} $expected
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
77
test/zipfile.test
Normal file
77
test/zipfile.test
Normal file
@ -0,0 +1,77 @@
|
||||
# 2017 December 9
|
||||
#
|
||||
# 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 zipfile
|
||||
|
||||
load_static_extension db zipfile
|
||||
|
||||
forcedelete test.zip
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE temp.zz USING zipfile('test.zip');
|
||||
PRAGMA table_info(zz);
|
||||
} {
|
||||
0 name {} 0 {} 0
|
||||
1 mode {} 0 {} 0
|
||||
2 mtime {} 0 {} 0
|
||||
3 sz {} 0 {} 0
|
||||
4 data {} 0 {} 0
|
||||
5 method {} 0 {} 0
|
||||
}
|
||||
|
||||
do_execsql_test 1.1.1 {
|
||||
INSERT INTO zz VALUES('f.txt', '-rw-r--r--', 1000000000, 5, 'abcde', 0);
|
||||
}
|
||||
do_execsql_test 1.1.2 {
|
||||
INSERT INTO zz VALUES('g.txt', '-rw-r--r--', 1000000002, 5, '12345', 0);
|
||||
}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
SELECT name, mtime, data FROM zipfile('test.zip')
|
||||
} {
|
||||
f.txt 1000000000 abcde
|
||||
g.txt 1000000002 12345
|
||||
}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
INSERT INTO zz VALUES('h.txt',
|
||||
'-rw-r--r--', 1000000004, 20, 'aaaaaaaaaabbbbbbbbbb', NULL
|
||||
);
|
||||
}
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
SELECT name, mtime, zipfile_uncompress(data, sz, method), method
|
||||
FROM zipfile('test.zip');
|
||||
} {
|
||||
f.txt 1000000000 abcde 0
|
||||
g.txt 1000000002 12345 0
|
||||
h.txt 1000000004 aaaaaaaaaabbbbbbbbbb 8
|
||||
}
|
||||
|
||||
do_execsql_test 1.5.1 {
|
||||
BEGIN;
|
||||
INSERT INTO zz VALUES('i.txt', '-rw-r--r--', 1000000006, 5, 'zxcvb', 0);
|
||||
SELECT name FROM zz;
|
||||
COMMIT;
|
||||
} {f.txt g.txt h.txt i.txt}
|
||||
do_execsql_test 1.5.2 {
|
||||
SELECT name FROM zz;
|
||||
} {f.txt g.txt h.txt i.txt}
|
||||
|
||||
do_execsql_test 1.6.0 {
|
||||
DELETE FROM zz WHERE name='g.txt';
|
||||
SELECT name FROM zz;
|
||||
} {f.txt h.txt i.txt}
|
||||
|
||||
finish_test
|
||||
|
Loading…
Reference in New Issue
Block a user