| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | #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> |
| | # include <sys/time.h> |
| | # define STRUCT_STAT struct stat |
| | #else |
| | # include "windirent.h" |
| | # include <direct.h> |
| | # define STRUCT_STAT struct _stat |
| | # define chmod(path,mode) fileio_chmod(path,mode) |
| | # define mkdir(path,mode) fileio_mkdir(path) |
| | #endif |
| | #include <time.h> |
| | #include <errno.h> |
| |
|
| | |
| | |
| | |
| | |
| | #ifndef _SQLITE3_STDIO_H_ |
| | # define sqlite3_fopen fopen |
| | #endif |
| |
|
| | |
| | |
| | |
| | |
| | #define FSDIR_SCHEMA "(name,mode,mtime,data,level,path HIDDEN,dir HIDDEN)" |
| |
|
| | #define FSDIR_COLUMN_NAME 0 |
| | #define FSDIR_COLUMN_MODE 1 |
| | #define FSDIR_COLUMN_MTIME 2 |
| | #define FSDIR_COLUMN_DATA 3 |
| | #define FSDIR_COLUMN_LEVEL 4 |
| | #define FSDIR_COLUMN_PATH 5 |
| | #define FSDIR_COLUMN_DIR 6 |
| |
|
| | |
| | |
| | |
| | #if defined(_WIN32) || defined(WIN32) |
| | static int fileio_chmod(const char *zPath, int pmode){ |
| | sqlite3_int64 sz = strlen(zPath); |
| | wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) ); |
| | int rc; |
| | if( b1==0 ) return -1; |
| | sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz); |
| | b1[sz] = 0; |
| | rc = _wchmod(b1, pmode); |
| | sqlite3_free(b1); |
| | return rc; |
| | } |
| | #endif |
| |
|
| | |
| | |
| | |
| | #if defined(_WIN32) || defined(WIN32) |
| | static int fileio_mkdir(const char *zPath){ |
| | sqlite3_int64 sz = strlen(zPath); |
| | wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) ); |
| | int rc; |
| | if( b1==0 ) return -1; |
| | sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz); |
| | b1[sz] = 0; |
| | rc = _wmkdir(b1); |
| | sqlite3_free(b1); |
| | return rc; |
| | } |
| | #endif |
| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static void readFileContents(sqlite3_context *ctx, const char *zName){ |
| | FILE *in; |
| | sqlite3_int64 nIn; |
| | void *pBuf; |
| | sqlite3 *db; |
| | int mxBlob; |
| |
|
| | in = sqlite3_fopen(zName, "rb"); |
| | if( in==0 ){ |
| | |
| | return; |
| | } |
| | fseek(in, 0, SEEK_END); |
| | nIn = ftell(in); |
| | rewind(in); |
| | db = sqlite3_context_db_handle(ctx); |
| | mxBlob = sqlite3_limit(db, SQLITE_LIMIT_LENGTH, -1); |
| | if( nIn>mxBlob ){ |
| | sqlite3_result_error_code(ctx, SQLITE_TOOBIG); |
| | fclose(in); |
| | return; |
| | } |
| | pBuf = sqlite3_malloc64( nIn ? nIn : 1 ); |
| | if( pBuf==0 ){ |
| | sqlite3_result_error_nomem(ctx); |
| | fclose(in); |
| | return; |
| | } |
| | if( nIn==(sqlite3_int64)fread(pBuf, 1, (size_t)nIn, in) ){ |
| | sqlite3_result_blob64(ctx, pBuf, nIn, sqlite3_free); |
| | }else{ |
| | sqlite3_result_error_code(ctx, SQLITE_IOERR); |
| | sqlite3_free(pBuf); |
| | } |
| | fclose(in); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | static void readfileFunc( |
| | sqlite3_context *context, |
| | int argc, |
| | sqlite3_value **argv |
| | ){ |
| | const char *zName; |
| | (void)(argc); |
| | zName = (const char*)sqlite3_value_text(argv[0]); |
| | if( zName==0 ) return; |
| | readFileContents(context, zName); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | 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); |
| | } |
| |
|
| | #if defined(_WIN32) |
| | |
| | |
| | |
| | |
| | static sqlite3_uint64 fileTimeToUnixTime( |
| | LPFILETIME pFileTime |
| | ){ |
| | SYSTEMTIME epochSystemTime; |
| | ULARGE_INTEGER epochIntervals; |
| | FILETIME epochFileTime; |
| | ULARGE_INTEGER fileIntervals; |
| |
|
| | memset(&epochSystemTime, 0, sizeof(SYSTEMTIME)); |
| | epochSystemTime.wYear = 1970; |
| | epochSystemTime.wMonth = 1; |
| | epochSystemTime.wDay = 1; |
| | SystemTimeToFileTime(&epochSystemTime, &epochFileTime); |
| | epochIntervals.LowPart = epochFileTime.dwLowDateTime; |
| | epochIntervals.HighPart = epochFileTime.dwHighDateTime; |
| |
|
| | fileIntervals.LowPart = pFileTime->dwLowDateTime; |
| | fileIntervals.HighPart = pFileTime->dwHighDateTime; |
| |
|
| | return (fileIntervals.QuadPart - epochIntervals.QuadPart) / 10000000; |
| | } |
| |
|
| |
|
| | #if defined(FILEIO_WIN32_DLL) && (defined(_WIN32) || defined(WIN32)) |
| | # |
| | # undef sqlite3_win32_utf8_to_unicode |
| | # define sqlite3_win32_utf8_to_unicode utf8_to_utf16 |
| | # |
| | LPWSTR utf8_to_utf16(const char *z){ |
| | int nAllot = MultiByteToWideChar(CP_UTF8, 0, z, -1, NULL, 0); |
| | LPWSTR rv = sqlite3_malloc(nAllot * sizeof(WCHAR)); |
| | if( rv!=0 && 0 < MultiByteToWideChar(CP_UTF8, 0, z, -1, rv, nAllot) ) |
| | return rv; |
| | sqlite3_free(rv); |
| | return 0; |
| | } |
| | #endif |
| |
|
| | |
| | |
| | |
| | |
| | |
| | static void statTimesToUtc( |
| | const char *zPath, |
| | STRUCT_STAT *pStatBuf |
| | ){ |
| | HANDLE hFindFile; |
| | WIN32_FIND_DATAW fd; |
| | LPWSTR zUnicodeName; |
| | extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*); |
| | zUnicodeName = sqlite3_win32_utf8_to_unicode(zPath); |
| | if( zUnicodeName ){ |
| | memset(&fd, 0, sizeof(WIN32_FIND_DATAW)); |
| | hFindFile = FindFirstFileW(zUnicodeName, &fd); |
| | if( hFindFile!=NULL ){ |
| | pStatBuf->st_ctime = (time_t)fileTimeToUnixTime(&fd.ftCreationTime); |
| | pStatBuf->st_atime = (time_t)fileTimeToUnixTime(&fd.ftLastAccessTime); |
| | pStatBuf->st_mtime = (time_t)fileTimeToUnixTime(&fd.ftLastWriteTime); |
| | FindClose(hFindFile); |
| | } |
| | sqlite3_free(zUnicodeName); |
| | } |
| | } |
| | #endif |
| |
|
| | |
| | |
| | |
| | |
| | |
| | static int fileStat( |
| | const char *zPath, |
| | STRUCT_STAT *pStatBuf |
| | ){ |
| | #if defined(_WIN32) |
| | sqlite3_int64 sz = strlen(zPath); |
| | wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) ); |
| | int rc; |
| | if( b1==0 ) return 1; |
| | sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz); |
| | b1[sz] = 0; |
| | rc = _wstat(b1, pStatBuf); |
| | if( rc==0 ) statTimesToUtc(zPath, pStatBuf); |
| | sqlite3_free(b1); |
| | return rc; |
| | #else |
| | return stat(zPath, pStatBuf); |
| | #endif |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | static int fileLinkStat( |
| | const char *zPath, |
| | STRUCT_STAT *pStatBuf |
| | ){ |
| | #if defined(_WIN32) |
| | return fileStat(zPath, pStatBuf); |
| | #else |
| | return lstat(zPath, pStatBuf); |
| | #endif |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static int makeDirectory( |
| | const char *zFile |
| | ){ |
| | 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 = fileStat(zCopy, &sStat); |
| | if( rc2!=0 ){ |
| | if( mkdir(zCopy, 0777) ) rc = SQLITE_ERROR; |
| | }else{ |
| | if( !S_ISDIR(sStat.st_mode) ) rc = SQLITE_ERROR; |
| | } |
| | zCopy[i] = '/'; |
| | i++; |
| | } |
| |
|
| | sqlite3_free(zCopy); |
| | } |
| |
|
| | return rc; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | static int writeFile( |
| | sqlite3_context *pCtx, |
| | const char *zFile, |
| | sqlite3_value *pData, |
| | mode_t mode, |
| | sqlite3_int64 mtime |
| | ){ |
| | if( zFile==0 ) return 1; |
| | #if !defined(_WIN32) && !defined(WIN32) |
| | if( S_ISLNK(mode) ){ |
| | const char *zTo = (const char*)sqlite3_value_text(pData); |
| | if( zTo==0 ) return 1; |
| | unlink(zFile); |
| | if( symlink(zTo, zFile)<0 ) return 1; |
| | }else |
| | #endif |
| | { |
| | if( S_ISDIR(mode) ){ |
| | if( mkdir(zFile, mode) ){ |
| | |
| | |
| | |
| | |
| | STRUCT_STAT sStat; |
| | if( errno!=EEXIST |
| | || 0!=fileStat(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 = sqlite3_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) |
| | |
| | FILETIME lastAccess; |
| | FILETIME lastWrite; |
| | SYSTEMTIME currentTime; |
| | LONGLONG intervals; |
| | HANDLE hFile; |
| | LPWSTR zUnicodeName; |
| | extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*); |
| |
|
| | GetSystemTime(¤tTime); |
| | SystemTimeToFileTime(¤tTime, &lastAccess); |
| | intervals = (mtime*10000000) + 116444736000000000; |
| | lastWrite.dwLowDateTime = (DWORD)intervals; |
| | lastWrite.dwHighDateTime = intervals >> 32; |
| | zUnicodeName = sqlite3_win32_utf8_to_unicode(zFile); |
| | if( zUnicodeName==0 ){ |
| | return 1; |
| | } |
| | hFile = CreateFileW( |
| | zUnicodeName, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, |
| | FILE_FLAG_BACKUP_SEMANTICS, NULL |
| | ); |
| | sqlite3_free(zUnicodeName); |
| | if( hFile!=INVALID_HANDLE_VALUE ){ |
| | BOOL bResult = SetFileTime(hFile, NULL, &lastAccess, &lastWrite); |
| | CloseHandle(hFile); |
| | return !bResult; |
| | }else{ |
| | return 1; |
| | } |
| | #elif defined(AT_FDCWD) && 0 |
| | |
| | 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 |
| | |
| | |
| | |
| | |
| | |
| | if( 0==S_ISLNK(mode) ){ |
| | struct timeval times[2]; |
| | times[0].tv_usec = times[1].tv_usec = 0; |
| | times[0].tv_sec = time(0); |
| | times[1].tv_sec = mtime; |
| | if( utimes(zFile, times) ){ |
| | return 1; |
| | } |
| | } |
| | #endif |
| | } |
| |
|
| | return 0; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | static void writefileFunc( |
| | sqlite3_context *context, |
| | int argc, |
| | sqlite3_value **argv |
| | ){ |
| | 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; |
| | } |
| |
|
| | zFile = (const char*)sqlite3_value_text(argv[0]); |
| | if( zFile==0 ) return; |
| | 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)==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); |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | static void lsModeFunc( |
| | sqlite3_context *context, |
| | int argc, |
| | sqlite3_value **argv |
| | ){ |
| | int i; |
| | int iMode = sqlite3_value_int(argv[0]); |
| | char z[16]; |
| | (void)argc; |
| | if( S_ISLNK(iMode) ){ |
| | z[0] = 'l'; |
| | }else if( S_ISREG(iMode) ){ |
| | z[0] = '-'; |
| | }else if( S_ISDIR(iMode) ){ |
| | z[0] = 'd'; |
| | }else{ |
| | z[0] = '?'; |
| | } |
| | for(i=0; i<3; i++){ |
| | int m = (iMode >> ((2-i)*3)); |
| | char *a = &z[1 + i*3]; |
| | a[0] = (m & 0x4) ? 'r' : '-'; |
| | a[1] = (m & 0x2) ? 'w' : '-'; |
| | a[2] = (m & 0x1) ? 'x' : '-'; |
| | } |
| | z[10] = '\0'; |
| | sqlite3_result_text(context, z, -1, SQLITE_TRANSIENT); |
| | } |
| |
|
| | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| |
|
| | |
| | |
| | |
| | typedef struct fsdir_cursor fsdir_cursor; |
| | typedef struct FsdirLevel FsdirLevel; |
| |
|
| | struct FsdirLevel { |
| | DIR *pDir; |
| | char *zDir; |
| | }; |
| |
|
| | struct fsdir_cursor { |
| | sqlite3_vtab_cursor base; |
| |
|
| | int nLvl; |
| | int mxLvl; |
| | int iLvl; |
| | FsdirLevel *aLvl; |
| |
|
| | const char *zBase; |
| | int nBase; |
| |
|
| | STRUCT_STAT sStat; |
| | char *zPath; |
| | sqlite3_int64 iRowid; |
| | }; |
| |
|
| | typedef struct fsdir_tab fsdir_tab; |
| | struct fsdir_tab { |
| | sqlite3_vtab base; |
| | }; |
| |
|
| | |
| | |
| | |
| | static int fsdirConnect( |
| | sqlite3 *db, |
| | void *pAux, |
| | int argc, const char *const*argv, |
| | sqlite3_vtab **ppVtab, |
| | char **pzErr |
| | ){ |
| | fsdir_tab *pNew = 0; |
| | int rc; |
| | (void)pAux; |
| | (void)argc; |
| | (void)argv; |
| | (void)pzErr; |
| | 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)); |
| | sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY); |
| | } |
| | *ppVtab = (sqlite3_vtab*)pNew; |
| | return rc; |
| | } |
| |
|
| | |
| | |
| | |
| | static int fsdirDisconnect(sqlite3_vtab *pVtab){ |
| | sqlite3_free(pVtab); |
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | static int fsdirOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ |
| | fsdir_cursor *pCur; |
| | (void)p; |
| | 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; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | 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); |
| | sqlite3_free(pCur->aLvl); |
| | pCur->aLvl = 0; |
| | pCur->zPath = 0; |
| | pCur->zBase = 0; |
| | pCur->nBase = 0; |
| | pCur->nLvl = 0; |
| | pCur->iLvl = -1; |
| | pCur->iRowid = 1; |
| | } |
| |
|
| | |
| | |
| | |
| | static int fsdirClose(sqlite3_vtab_cursor *cur){ |
| | fsdir_cursor *pCur = (fsdir_cursor*)cur; |
| |
|
| | fsdirResetCursor(pCur); |
| | sqlite3_free(pCur); |
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | 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); |
| | } |
| |
|
| |
|
| | |
| | |
| | |
| | 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) && pCur->iLvl+3<pCur->mxLvl ){ |
| | |
| | int iNew = pCur->iLvl + 1; |
| | FsdirLevel *pLvl; |
| | if( iNew>=pCur->nLvl ){ |
| | int nNew = iNew+1; |
| | sqlite3_int64 nByte = nNew*sizeof(FsdirLevel); |
| | FsdirLevel *aNew = (FsdirLevel*)sqlite3_realloc64(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", pLvl->zDir); |
| | 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( fileLinkStat(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--; |
| | } |
| |
|
| | |
| | sqlite3_free(pCur->zPath); |
| | pCur->zPath = 0; |
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | static int fsdirColumn( |
| | sqlite3_vtab_cursor *cur, |
| | sqlite3_context *ctx, |
| | int i |
| | ){ |
| | fsdir_cursor *pCur = (fsdir_cursor*)cur; |
| | switch( i ){ |
| | case FSDIR_COLUMN_NAME: { |
| | sqlite3_result_text(ctx, &pCur->zPath[pCur->nBase], -1, SQLITE_TRANSIENT); |
| | break; |
| | } |
| |
|
| | case FSDIR_COLUMN_MODE: |
| | sqlite3_result_int64(ctx, pCur->sStat.st_mode); |
| | break; |
| |
|
| | case FSDIR_COLUMN_MTIME: |
| | sqlite3_result_int64(ctx, pCur->sStat.st_mtime); |
| | break; |
| |
|
| | case FSDIR_COLUMN_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; |
| | sqlite3_int64 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_malloc64(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); |
| | } |
| | break; |
| | } |
| | case FSDIR_COLUMN_LEVEL: |
| | sqlite3_result_int(ctx, pCur->iLvl+2); |
| | break; |
| | case FSDIR_COLUMN_PATH: |
| | default: { |
| | |
| | |
| | break; |
| | } |
| | } |
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | static int fsdirRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ |
| | fsdir_cursor *pCur = (fsdir_cursor*)cur; |
| | *pRowid = pCur->iRowid; |
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | static int fsdirEof(sqlite3_vtab_cursor *cur){ |
| | fsdir_cursor *pCur = (fsdir_cursor*)cur; |
| | return (pCur->zPath==0); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | 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; |
| | int i; |
| | (void)idxStr; |
| | fsdirResetCursor(pCur); |
| |
|
| | if( idxNum==0 ){ |
| | fsdirSetErrmsg(pCur, "table function fsdir requires an argument"); |
| | return SQLITE_ERROR; |
| | } |
| |
|
| | assert( (idxNum & 0x01)!=0 && argc>0 ); |
| | zDir = (const char*)sqlite3_value_text(argv[0]); |
| | if( zDir==0 ){ |
| | fsdirSetErrmsg(pCur, "table function fsdir requires a non-NULL argument"); |
| | return SQLITE_ERROR; |
| | } |
| | i = 1; |
| | if( (idxNum & 0x02)!=0 ){ |
| | assert( argc>i ); |
| | pCur->zBase = (const char*)sqlite3_value_text(argv[i++]); |
| | } |
| | if( (idxNum & 0x0c)!=0 ){ |
| | assert( argc>i ); |
| | pCur->mxLvl = sqlite3_value_int(argv[i++]); |
| | if( idxNum & 0x08 ) pCur->mxLvl++; |
| | if( pCur->mxLvl<=0 ) pCur->mxLvl = 1000000000; |
| | }else{ |
| | pCur->mxLvl = 1000000000; |
| | } |
| | 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( fileLinkStat(pCur->zPath, &pCur->sStat) ){ |
| | fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zPath); |
| | return SQLITE_ERROR; |
| | } |
| |
|
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static int fsdirBestIndex( |
| | sqlite3_vtab *tab, |
| | sqlite3_index_info *pIdxInfo |
| | ){ |
| | int i; |
| | int idxPath = -1; |
| | int idxDir = -1; |
| | int idxLevel = -1; |
| | int idxLevelEQ = 0; |
| | int omitLevel = 0; |
| | int seenPath = 0; |
| | int seenDir = 0; |
| | const struct sqlite3_index_constraint *pConstraint; |
| |
|
| | (void)tab; |
| | pConstraint = pIdxInfo->aConstraint; |
| | for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ |
| | if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){ |
| | switch( pConstraint->iColumn ){ |
| | case FSDIR_COLUMN_PATH: { |
| | if( pConstraint->usable ){ |
| | idxPath = i; |
| | seenPath = 0; |
| | }else if( idxPath<0 ){ |
| | seenPath = 1; |
| | } |
| | break; |
| | } |
| | case FSDIR_COLUMN_DIR: { |
| | if( pConstraint->usable ){ |
| | idxDir = i; |
| | seenDir = 0; |
| | }else if( idxDir<0 ){ |
| | seenDir = 1; |
| | } |
| | break; |
| | } |
| | case FSDIR_COLUMN_LEVEL: { |
| | if( pConstraint->usable && idxLevel<0 ){ |
| | idxLevel = i; |
| | idxLevelEQ = 0x08; |
| | omitLevel = 0; |
| | } |
| | break; |
| | } |
| | } |
| | }else |
| | if( pConstraint->iColumn==FSDIR_COLUMN_LEVEL |
| | && pConstraint->usable |
| | && idxLevel<0 |
| | ){ |
| | if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE ){ |
| | idxLevel = i; |
| | idxLevelEQ = 0x08; |
| | omitLevel = 1; |
| | }else if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ){ |
| | idxLevel = i; |
| | idxLevelEQ = 0x04; |
| | omitLevel = 1; |
| | } |
| | } |
| | } |
| | if( seenPath || seenDir ){ |
| | |
| | return SQLITE_CONSTRAINT; |
| | } |
| |
|
| | if( idxPath<0 ){ |
| | pIdxInfo->idxNum = 0; |
| | |
| | |
| | pIdxInfo->estimatedRows = 0x7fffffff; |
| | }else{ |
| | pIdxInfo->aConstraintUsage[idxPath].omit = 1; |
| | pIdxInfo->aConstraintUsage[idxPath].argvIndex = 1; |
| | pIdxInfo->idxNum = 0x01; |
| | pIdxInfo->estimatedCost = 1.0e9; |
| | i = 2; |
| | if( idxDir>=0 ){ |
| | pIdxInfo->aConstraintUsage[idxDir].omit = 1; |
| | pIdxInfo->aConstraintUsage[idxDir].argvIndex = i++; |
| | pIdxInfo->idxNum |= 0x02; |
| | pIdxInfo->estimatedCost /= 1.0e4; |
| | } |
| | if( idxLevel>=0 ){ |
| | pIdxInfo->aConstraintUsage[idxLevel].omit = omitLevel; |
| | pIdxInfo->aConstraintUsage[idxLevel].argvIndex = i++; |
| | pIdxInfo->idxNum |= idxLevelEQ; |
| | pIdxInfo->estimatedCost /= 1.0e4; |
| | } |
| | } |
| |
|
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | static int fsdirRegister(sqlite3 *db){ |
| | static sqlite3_module fsdirModule = { |
| | 0, |
| | 0, |
| | fsdirConnect, |
| | fsdirBestIndex, |
| | fsdirDisconnect, |
| | 0, |
| | fsdirOpen, |
| | fsdirClose, |
| | fsdirFilter, |
| | fsdirNext, |
| | fsdirEof, |
| | fsdirColumn, |
| | fsdirRowid, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0 |
| | }; |
| |
|
| | int rc = sqlite3_create_module(db, "fsdir", &fsdirModule, 0); |
| | return rc; |
| | } |
| | #else |
| | # define fsdirRegister(x) SQLITE_OK |
| | #endif |
| |
|
| | #ifdef _WIN32 |
| | __declspec(dllexport) |
| | #endif |
| | int sqlite3_fileio_init( |
| | sqlite3 *db, |
| | char **pzErrMsg, |
| | const sqlite3_api_routines *pApi |
| | ){ |
| | int rc = SQLITE_OK; |
| | SQLITE_EXTENSION_INIT2(pApi); |
| | (void)pzErrMsg; |
| | rc = sqlite3_create_function(db, "readfile", 1, |
| | SQLITE_UTF8|SQLITE_DIRECTONLY, 0, |
| | readfileFunc, 0, 0); |
| | if( rc==SQLITE_OK ){ |
| | rc = sqlite3_create_function(db, "writefile", -1, |
| | SQLITE_UTF8|SQLITE_DIRECTONLY, 0, |
| | writefileFunc, 0, 0); |
| | } |
| | if( rc==SQLITE_OK ){ |
| | rc = sqlite3_create_function(db, "lsmode", 1, SQLITE_UTF8, 0, |
| | lsModeFunc, 0, 0); |
| | } |
| | if( rc==SQLITE_OK ){ |
| | rc = fsdirRegister(db); |
| | } |
| | return rc; |
| | } |
| |
|