Index: Makefile.in ================================================================== --- Makefile.in +++ Makefile.in @@ -400,10 +400,11 @@ $(TOP)/src/test_superlock.c \ $(TOP)/src/test_syscall.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ + $(TOP)/src/test_windirent.c \ $(TOP)/src/test_wsd.c \ $(TOP)/ext/fts3/fts3_term.c \ $(TOP)/ext/fts3/fts3_test.c \ $(TOP)/ext/rbu/test_rbu.c Index: Makefile.msc ================================================================== --- Makefile.msc +++ Makefile.msc @@ -1069,10 +1069,11 @@ $(TOP)\src\test_superlock.c \ $(TOP)\src\test_syscall.c \ $(TOP)\src\test_tclvar.c \ $(TOP)\src\test_thread.c \ $(TOP)\src\test_vfs.c \ + $(TOP)\src\test_windirent.c \ $(TOP)\src\test_wsd.c \ $(TOP)\ext\fts3\fts3_term.c \ $(TOP)\ext\fts3\fts3_test.c \ $(TOP)\ext\rbu\test_rbu.c Index: main.mk ================================================================== --- main.mk +++ main.mk @@ -310,10 +310,11 @@ $(TOP)/src/test_superlock.c \ $(TOP)/src/test_syscall.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ + $(TOP)/src/test_windirent.c \ $(TOP)/src/test_wsd.c # Extensions to be statically loaded. # TESTSRC += \ Index: src/test_fs.c ================================================================== --- src/test_fs.c +++ src/test_fs.c @@ -71,13 +71,20 @@ #include #if SQLITE_OS_UNIX # include # include +# ifndef DIRENT +# define DIRENT dirent +# endif #endif #if SQLITE_OS_WIN # include +# include "test_windirent.h" +# ifndef S_ISREG +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +# endif #endif #ifndef SQLITE_OMIT_VIRTUALTABLE typedef struct fs_vtab fs_vtab; @@ -114,11 +121,11 @@ struct FsdirCsr { sqlite3_vtab_cursor base; char *zDir; /* Buffer containing directory scanned */ DIR *pDir; /* Open directory */ sqlite3_int64 iRowid; - struct dirent entry; /* Current entry */ + struct DIRENT entry; /* Current entry */ }; /* ** This function is the implementation of both the xConnect and xCreate ** methods of the fsdir virtual table. @@ -218,11 +225,11 @@ */ static int fsdirNext(sqlite3_vtab_cursor *cur){ FsdirCsr *pCsr = (FsdirCsr*)cur; if( pCsr->pDir ){ - struct dirent *pRes = 0; + struct DIRENT *pRes = 0; readdir_r(pCsr->pDir, &pCsr->entry, &pRes); if( pRes==0 ){ closedir(pCsr->pDir); pCsr->pDir = 0; } @@ -459,19 +466,38 @@ FstreeCsr *pCsr = (FstreeCsr*)pVtabCursor; FstreeVtab *pTab = (FstreeVtab*)(pCsr->base.pVtab); int rc; const char *zSql = "WITH r(d) AS (" -" SELECT CASE WHEN dir='/' THEN '' ELSE dir END || '/' || name " -" FROM fsdir WHERE dir=? AND name NOT LIKE '.%'" +" SELECT CASE WHEN dir=?2 THEN ?3 ELSE dir END || '/' || name " +" FROM fsdir WHERE dir=?1 AND name NOT LIKE '.%'" " UNION ALL" " SELECT dir || '/' || name FROM r, fsdir WHERE dir=d AND name NOT LIKE '.%'" ") SELECT d FROM r;"; - const char *zDir = "/"; - int nDir = 1; - char aWild[2] = {'\0', '\0' }; + char *zRoot; + int nRoot; + char *zPrefix; + int nPrefix; + const char *zDir; + int nDir; + char aWild[2] = { '\0', '\0' }; + +#if SQLITE_OS_WIN + zRoot = sqlite3_mprintf("%s%c", getenv("SystemDrive"), '/'); + nRoot = strlen(zRoot); + zPrefix = sqlite3_mprintf("%s", getenv("SystemDrive")); + nPrefix = strlen(zPrefix); +#else + zRoot = "/"; + nRoot = 1; + zPrefix = ""; + nPrefix = 0; +#endif + + zDir = zRoot; + nDir = nRoot; fstreeCloseFd(pCsr); sqlite3_finalize(pCsr->pStmt); pCsr->pStmt = 0; rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0); @@ -488,21 +514,28 @@ aWild[0] = '_'; aWild[1] = '%'; break; } - if( zQuery[0]=='/' ){ + if( sqlite3_strnicmp(zQuery, zPrefix, nPrefix)==0 ){ int i; - for(i=1; zQuery[i]; i++){ + for(i=nPrefix; zQuery[i]; i++){ if( zQuery[i]==aWild[0] || zQuery[i]==aWild[1] ) break; if( zQuery[i]=='/' ) nDir = i; } zDir = zQuery; } } sqlite3_bind_text(pCsr->pStmt, 1, zDir, nDir, SQLITE_TRANSIENT); + sqlite3_bind_text(pCsr->pStmt, 2, zRoot, nRoot, SQLITE_TRANSIENT); + sqlite3_bind_text(pCsr->pStmt, 3, zPrefix, nPrefix, SQLITE_TRANSIENT); + +#if SQLITE_OS_WIN + sqlite3_free(zPrefix); + sqlite3_free(zRoot); +#endif return fstreeNext(pVtabCursor); } /* ADDED src/test_windirent.c Index: src/test_windirent.c ================================================================== --- /dev/null +++ src/test_windirent.c @@ -0,0 +1,157 @@ +/* +** 2015 November 30 +** +** 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 file contains code to implement most of the opendir() family of +** POSIX functions on Win32 using the MSVCRT. +*/ + +#if defined(_WIN32) && defined(_MSC_VER) + +#include "test_windirent.h" + +/* +** Implementation of the POSIX opendir() function using the MSVCRT. +*/ +LPDIR opendir( + const char *dirname +){ + struct _finddata_t data; + LPDIR dirp = (LPDIR)sqlite3_malloc(sizeof(DIR)); + SIZE_T namesize = sizeof(data.name) / sizeof(data.name[0]); + + if( dirp==NULL ) return NULL; + memset(dirp, 0, sizeof(DIR)); + + /* TODO: Remove this if Unix-style root paths are not used. */ + if( sqlite3_stricmp(dirname, "/")==0 ){ + dirname = getenv("SystemDrive"); + } + + _snprintf(data.name, namesize, "%s\\*", dirname); + dirp->d_handle = _findfirst(data.name, &data); + + if( dirp->d_handle==BAD_INTPTR_T ){ + closedir(dirp); + return NULL; + } + + /* TODO: Remove this block to allow hidden and system files. */ + if( data.attrib&_A_HIDDEN || data.attrib&_A_SYSTEM ){ + if( _findnext(dirp->d_handle, &data)==-1 ){ + closedir(dirp); + return NULL; + } + } + + dirp->d_first.d_attributes = data.attrib; + strncpy(dirp->d_first.d_name, data.name, NAME_MAX); + dirp->d_first.d_name[NAME_MAX] = '\0'; + + return dirp; +} + +/* +** Implementation of the POSIX readdir() function using the MSVCRT. +*/ +LPDIRENT readdir( + LPDIR dirp +){ + struct _finddata_t data; + + if( dirp==NULL ) return NULL; + + if( dirp->d_first.d_ino==0 ){ + dirp->d_first.d_ino++; + dirp->d_next.d_ino++; + + return &dirp->d_first; + } + +next: + + if( _findnext(dirp->d_handle, &data)==-1 ) return NULL; + + /* TODO: Remove this block to allow hidden and system files. */ + if( data.attrib&_A_HIDDEN ) goto next; + if( data.attrib&_A_SYSTEM ) goto next; + + dirp->d_next.d_ino++; + dirp->d_next.d_attributes = data.attrib; + strncpy(dirp->d_next.d_name, data.name, NAME_MAX); + dirp->d_next.d_name[NAME_MAX] = '\0'; + + return &dirp->d_next; +} + +/* +** Implementation of the POSIX readdir_r() function using the MSVCRT. +*/ +INT readdir_r( + LPDIR dirp, + LPDIRENT entry, + LPDIRENT *result +){ + struct _finddata_t data; + + if( dirp==NULL ) return EBADF; + + if( dirp->d_first.d_ino==0 ){ + dirp->d_first.d_ino++; + dirp->d_next.d_ino++; + + entry->d_ino = dirp->d_first.d_ino; + entry->d_attributes = dirp->d_first.d_attributes; + strncpy(entry->d_name, dirp->d_first.d_name, NAME_MAX); + entry->d_name[NAME_MAX] = '\0'; + + *result = entry; + return 0; + } + +next: + + if( _findnext(dirp->d_handle, &data)==-1 ){ + *result = NULL; + return ENOENT; + } + + /* TODO: Remove this block to allow hidden and system files. */ + if( data.attrib&_A_HIDDEN ) goto next; + if( data.attrib&_A_SYSTEM ) goto next; + + entry->d_ino = (ino_t)-1; /* not available */ + entry->d_attributes = data.attrib; + strncpy(entry->d_name, data.name, NAME_MAX); + entry->d_name[NAME_MAX] = '\0'; + + *result = entry; + return 0; +} + +/* +** Implementation of the POSIX closedir() function using the MSVCRT. +*/ +INT closedir( + LPDIR dirp +){ + INT result = 0; + + if( dirp==NULL ) return EINVAL; + + if( dirp->d_handle!=NULL_INTPTR_T && dirp->d_handle!=BAD_INTPTR_T ){ + result = _findclose(dirp->d_handle); + } + + sqlite3_free(dirp); + return result; +} + +#endif /* defined(WIN32) && defined(_MSC_VER) */ ADDED src/test_windirent.h Index: src/test_windirent.h ================================================================== --- /dev/null +++ src/test_windirent.h @@ -0,0 +1,105 @@ +/* +** 2015 November 30 +** +** 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 file contains declarations for most of the opendir() family of +** POSIX functions on Win32 using the MSVCRT. +*/ + +#if defined(_WIN32) && defined(_MSC_VER) + +/* +** We need several data types from the Windows SDK header. +*/ + +#define WIN32_LEAN_AND_MEAN +#include "windows.h" + +/* +** We need several support functions from the SQLite core. +*/ + +#include "sqlite3.h" + +/* +** We need several things from the ANSI and MSVCRT headers. +*/ + +#include +#include +#include +#include +#include + +/* +** We may need to provide the "ino_t" type. +*/ + +#ifndef INO_T_DEFINED + #define INO_T_DEFINED + typedef unsigned short ino_t; +#endif + +/* +** We need to define "NAME_MAX" if it was not present in "limits.h". +*/ + +#ifndef NAME_MAX +# ifdef FILENAME_MAX +# define NAME_MAX (FILENAME_MAX) +# else +# define NAME_MAX (260) +# endif +#endif + +/* +** We need to define "NULL_INTPTR_T" and "BAD_INTPTR_T". +*/ + +#ifndef NULL_INTPTR_T +# define NULL_INTPTR_T ((intptr_t)(0)) +#endif + +#ifndef BAD_INTPTR_T +# define BAD_INTPTR_T ((intptr_t)(-1)) +#endif + +/* +** We need to provide the necessary structures and related types. +*/ + +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. */ +}; + +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". */ +}; + +/* +** Finally, we can provide the function prototypes for the opendir(), +** readdir(), readdir_r(), and closedir() POSIX functions. +*/ + +extern LPDIR opendir(const char *dirname); +extern LPDIRENT readdir(LPDIR dirp); +extern INT readdir_r(LPDIR dirp, LPDIRENT entry, LPDIRENT *result); +extern INT closedir(LPDIR dirp); + +#endif /* defined(WIN32) && defined(_MSC_VER) */ Index: test/vtabH.test ================================================================== --- test/vtabH.test +++ test/vtabH.test @@ -106,33 +106,63 @@ } } #------------------------------------------------------------------------- # -if {$::tcl_platform(platform)=="unix"} { +if {1} { reset_db register_fs_module db do_execsql_test 3.0 { SELECT name FROM fsdir WHERE dir = '.' AND name = 'test.db'; SELECT name FROM fsdir WHERE dir = '.' AND name = '.' } {test.db .} + proc list_root_files {} { + if {$::tcl_platform(platform) eq "windows"} { + set res [list] + foreach name [glob -directory $::env(SystemDrive)/ -- *] { + if {[string index [file tail $name] 0] eq "."} continue + lappend res $name + } + return $res + } else { + return [exec ls -U /] + } + } + + proc list_files { pattern } { + if {$::tcl_platform(platform) eq "windows"} { + set res [list] + foreach name [glob -nocomplain $pattern] { + if {[string index [file tail $name] 0] eq "."} continue + lappend res $name + } + return $res + } else { + return [glob -nocomplain $pattern] + } + } + # Read the first 5 entries from the root directory. # set res [list] - foreach p [lrange [exec ls -U /] 0 4] { - lappend res "/$p" + foreach p [lrange [list_root_files] 0 4] { + if {$::tcl_platform(platform) eq "windows"} { + lappend res $p + } else { + lappend res "/$p" + } } do_execsql_test 3.1 { SELECT path FROM fstree LIMIT 5; } $res # Read all entries in the current directory. # proc contents {pattern} { set res [list] - foreach f [glob -nocomplain $pattern] { + foreach f [list_files $pattern] { lappend res $f if {[file isdir $f]} { set res [concat $res [contents "$f/*"]] } }