aboutsummaryrefslogtreecommitdiff
path: root/ext/misc/fileio.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/misc/fileio.c')
-rw-r--r--ext/misc/fileio.c222
1 files changed, 131 insertions, 91 deletions
diff --git a/ext/misc/fileio.c b/ext/misc/fileio.c
index db6935700..d3c00b4b1 100644
--- a/ext/misc/fileio.c
+++ b/ext/misc/fileio.c
@@ -42,7 +42,7 @@ SQLITE_EXTENSION_INIT1
#include <utime.h>
-#define FSDIR_SCHEMA "CREATE TABLE x(name,mode,mtime,data,dir HIDDEN)"
+#define FSDIR_SCHEMA "(name,mode,mtime,data,path HIDDEN,dir HIDDEN)"
static void readFileContents(sqlite3_context *ctx, const char *zName){
FILE *in;
@@ -166,29 +166,36 @@ static void writefileFunc(
#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 eType; /* One of FSDIR_DIR or FSDIR_ENTRY */
- DIR *pDir; /* From opendir() */
+
+ 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 *zDir; /* Directory to read */
- int nDir; /* Value of strlen(zDir) */
char *zPath; /* Path to current entry */
- int bEof;
sqlite3_int64 iRowid; /* Current rowid */
};
typedef struct fsdir_tab fsdir_tab;
struct fsdir_tab {
sqlite3_vtab base; /* Base class - must be first */
- int eType; /* One of FSDIR_DIR or FSDIR_ENTRY */
};
-#define FSDIR_DIR 0
-#define FSDIR_ENTRY 1
-
/*
** Construct a new fsdir virtual table object.
*/
@@ -202,12 +209,11 @@ static int fsdirConnect(
fsdir_tab *pNew = 0;
int rc;
- rc = sqlite3_declare_vtab(db, FSDIR_SCHEMA);
+ 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));
- pNew->eType = (pAux==0 ? FSDIR_DIR : FSDIR_ENTRY);
}
*ppVtab = (sqlite3_vtab*)pNew;
return rc;
@@ -229,56 +235,107 @@ static int fsdirOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
- pCur->eType = ((fsdir_tab*)p)->eType;
+ 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);
+ 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;
- if( pCur->pDir ) closedir(pCur->pDir);
- sqlite3_free(pCur->zDir);
- sqlite3_free(pCur->zPath);
+
+ fsdirResetCursor(pCur);
+ sqlite3_free(pCur->aLvl);
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);
+}
+
+
/*
** Advance an fsdir_cursor to its next row of output.
*/
static int fsdirNext(sqlite3_vtab_cursor *cur){
fsdir_cursor *pCur = (fsdir_cursor*)cur;
- struct dirent *pEntry;
+ mode_t m = pCur->sStat.st_mode;
- if( pCur->eType==FSDIR_ENTRY ){
- pCur->bEof = 1;
- return SQLITE_OK;
+ 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;
+ }
}
- sqlite3_free(pCur->zPath);
- pCur->zPath = 0;
-
- while( 1 ){
- pEntry = readdir(pCur->pDir);
+ while( pCur->iLvl>=0 ){
+ FsdirLevel *pLvl = &pCur->aLvl[pCur->iLvl];
+ struct dirent *pEntry = readdir(pLvl->pDir);
if( pEntry ){
- if( strcmp(pEntry->d_name, ".")
- && strcmp(pEntry->d_name, "..")
- ){
- pCur->zPath = sqlite3_mprintf("%s/%s", pCur->zDir, pEntry->d_name);
- if( pCur->zPath==0 ) return SQLITE_NOMEM;
- lstat(pCur->zPath, &pCur->sStat);
- break;
+ if( pEntry->d_name[0]=='.' ){
+ if( pEntry->d_name[1]=='.' && pEntry->d_name[2]=='\0' ) continue;
+ if( pEntry->d_name[1]=='\0' ) continue;
}
- }else{
- pCur->bEof = 1;
- break;
+ 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--;
}
- pCur->iRowid++;
+ /* EOF */
+ sqlite3_free(pCur->zPath);
+ pCur->zPath = 0;
return SQLITE_OK;
}
@@ -294,13 +351,7 @@ static int fsdirColumn(
fsdir_cursor *pCur = (fsdir_cursor*)cur;
switch( i ){
case 0: { /* name */
- const char *zName;
- if( pCur->eType==FSDIR_DIR ){
- zName = &pCur->zPath[pCur->nDir+1];
- }else{
- zName = pCur->zPath;
- }
- sqlite3_result_text(ctx, zName, -1, SQLITE_TRANSIENT);
+ sqlite3_result_text(ctx, &pCur->zPath[pCur->nBase], -1, SQLITE_TRANSIENT);
break;
}
@@ -308,11 +359,11 @@ static int fsdirColumn(
sqlite3_result_int64(ctx, pCur->sStat.st_mode);
break;
- case 2: /* mode */
+ case 2: /* mtime */
sqlite3_result_int64(ctx, pCur->sStat.st_mtime);
break;
- case 3: {
+ case 3: { /* data */
mode_t m = pCur->sStat.st_mode;
if( S_ISDIR(m) ){
sqlite3_result_null(ctx);
@@ -361,14 +412,7 @@ static int fsdirRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
*/
static int fsdirEof(sqlite3_vtab_cursor *cur){
fsdir_cursor *pCur = (fsdir_cursor*)cur;
- return pCur->bEof;
-}
-
-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);
+ return (pCur->zPath==0);
}
/*
@@ -382,51 +426,38 @@ static int fsdirFilter(
const char *zDir = 0;
fsdir_cursor *pCur = (fsdir_cursor*)cur;
- sqlite3_free(pCur->zDir);
- pCur->iRowid = 0;
- pCur->zDir = 0;
- pCur->bEof = 0;
- if( pCur->pDir ){
- closedir(pCur->pDir);
- pCur->pDir = 0;
- }
+ fsdirResetCursor(pCur);
if( idxNum==0 ){
fsdirSetErrmsg(pCur, "table function fsdir requires an argument");
return SQLITE_ERROR;
}
- assert( argc==1 );
+ 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;
}
-
- pCur->zDir = sqlite3_mprintf("%s", zDir);
- if( pCur->zDir==0 ){
- return SQLITE_NOMEM;
+ if( argc==2 ){
+ pCur->zBase = (const char*)sqlite3_value_text(argv[1]);
}
-
- if( pCur->eType==FSDIR_ENTRY ){
- int rc = lstat(pCur->zDir, &pCur->sStat);
- if( rc ){
- fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zDir);
- }else{
- pCur->zPath = sqlite3_mprintf("%s", pCur->zDir);
- if( pCur->zPath==0 ) return SQLITE_NOMEM;
- }
- return SQLITE_OK;
+ if( pCur->zBase ){
+ pCur->nBase = strlen(pCur->zBase)+1;
+ pCur->zPath = sqlite3_mprintf("%s/%s", pCur->zBase, zDir);
}else{
- pCur->nDir = strlen(pCur->zDir);
- pCur->pDir = opendir(zDir);
- if( pCur->pDir==0 ){
- fsdirSetErrmsg(pCur, "error in opendir(\"%s\")", zDir);
- return SQLITE_ERROR;
- }
+ pCur->zPath = sqlite3_mprintf("%s", zDir);
+ }
- return fsdirNext(cur);
+ 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;
}
/*
@@ -450,24 +481,33 @@ static int fsdirBestIndex(
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 ) continue;
- break;
+ if( pConstraint->iColumn==4 ) idx4 = i;
+ if( pConstraint->iColumn==5 ) idx5 = i;
}
- if( i<pIdxInfo->nConstraint ){
- pIdxInfo->aConstraintUsage[i].omit = 1;
- pIdxInfo->aConstraintUsage[i].argvIndex = 1;
- pIdxInfo->idxNum = 1;
- pIdxInfo->estimatedCost = 10.0;
- }else{
+ 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;