| Index: third_party/sqlite/src/src/test_quota.c | 
| diff --git a/third_party/sqlite/src/src/test_quota.c b/third_party/sqlite/src/src/test_quota.c | 
| index 3c6db4d7cdd86fc90a8077b3f04d5093e53ebcfc..80ebd0589e1eca1ad9cf4494fa31a4b7c0634f7c 100644 | 
| --- a/third_party/sqlite/src/src/test_quota.c | 
| +++ b/third_party/sqlite/src/src/test_quota.c | 
| @@ -27,7 +27,7 @@ | 
| ** files within the group is less than the new quota, then the write | 
| ** continues as if nothing had happened. | 
| */ | 
| -#include "sqlite3.h" | 
| +#include "test_quota.h" | 
| #include <string.h> | 
| #include <assert.h> | 
|  | 
| @@ -44,6 +44,16 @@ | 
| #define sqlite3_mutex_notheld(X)  ((void)(X),1) | 
| #endif /* SQLITE_THREADSAFE==0 */ | 
|  | 
| +#include "os_setup.h" | 
| + | 
| +#if SQLITE_OS_UNIX | 
| +# include <unistd.h> | 
| +#endif | 
| +#if SQLITE_OS_WIN | 
| +# include "os_win.h" | 
| +# include <io.h> | 
| +#endif | 
| + | 
|  | 
| /************************ Object Definitions ******************************/ | 
|  | 
| @@ -95,6 +105,7 @@ struct quotaFile { | 
| quotaGroup *pGroup;             /* Quota group to which this file belongs */ | 
| sqlite3_int64 iSize;            /* Current size of this file */ | 
| int nRef;                       /* Number of times this file is open */ | 
| +  int deleteOnClose;              /* True to delete this file when it closes */ | 
| quotaFile *pNext, **ppPrev;     /* Linked list of files in the same group */ | 
| }; | 
|  | 
| @@ -110,6 +121,21 @@ struct quotaConn { | 
| /* The underlying VFS sqlite3_file is appended to this object */ | 
| }; | 
|  | 
| +/* | 
| +** An instance of the following object records the state of an | 
| +** open file.  This object is opaque to all users - the internal | 
| +** structure is only visible to the functions below. | 
| +*/ | 
| +struct quota_FILE { | 
| +  FILE *f;                /* Open stdio file pointer */ | 
| +  sqlite3_int64 iOfst;    /* Current offset into the file */ | 
| +  quotaFile *pFile;       /* The file record in the quota system */ | 
| +#if SQLITE_OS_WIN | 
| +  char *zMbcsName;        /* Full MBCS pathname of the file */ | 
| +#endif | 
| +}; | 
| + | 
| + | 
| /************************* Global Variables **********************************/ | 
| /* | 
| ** All global variables used by this file are containing within the following | 
| @@ -164,12 +190,45 @@ static struct { | 
| static void quotaEnter(void){ sqlite3_mutex_enter(gQuota.pMutex); } | 
| static void quotaLeave(void){ sqlite3_mutex_leave(gQuota.pMutex); } | 
|  | 
| +/* Count the number of open files in a quotaGroup | 
| +*/ | 
| +static int quotaGroupOpenFileCount(quotaGroup *pGroup){ | 
| +  int N = 0; | 
| +  quotaFile *pFile = pGroup->pFiles; | 
| +  while( pFile ){ | 
| +    if( pFile->nRef ) N++; | 
| +    pFile = pFile->pNext; | 
| +  } | 
| +  return N; | 
| +} | 
| + | 
| +/* Remove a file from a quota group. | 
| +*/ | 
| +static void quotaRemoveFile(quotaFile *pFile){ | 
| +  quotaGroup *pGroup = pFile->pGroup; | 
| +  pGroup->iSize -= pFile->iSize; | 
| +  *pFile->ppPrev = pFile->pNext; | 
| +  if( pFile->pNext ) pFile->pNext->ppPrev = pFile->ppPrev; | 
| +  sqlite3_free(pFile); | 
| +} | 
| + | 
| +/* Remove all files from a quota group.  It is always the case that | 
| +** all files will be closed when this routine is called. | 
| +*/ | 
| +static void quotaRemoveAllFiles(quotaGroup *pGroup){ | 
| +  while( pGroup->pFiles ){ | 
| +    assert( pGroup->pFiles->nRef==0 ); | 
| +    quotaRemoveFile(pGroup->pFiles); | 
| +  } | 
| +} | 
| + | 
|  | 
| /* If the reference count and threshold for a quotaGroup are both | 
| ** zero, then destroy the quotaGroup. | 
| */ | 
| static void quotaGroupDeref(quotaGroup *pGroup){ | 
| -  if( pGroup->pFiles==0 && pGroup->iLimit==0 ){ | 
| +  if( pGroup->iLimit==0 && quotaGroupOpenFileCount(pGroup)==0 ){ | 
| +    quotaRemoveAllFiles(pGroup); | 
| *pGroup->ppPrev = pGroup->pNext; | 
| if( pGroup->pNext ) pGroup->pNext->ppPrev = pGroup->ppPrev; | 
| if( pGroup->xDestroy ) pGroup->xDestroy(pGroup->pArg); | 
| @@ -191,9 +250,11 @@ static void quotaGroupDeref(quotaGroup *pGroup){ | 
| ** | 
| **     [^...]     Matches one character not in the enclosed list. | 
| ** | 
| +**     /          Matches "/" or "\\" | 
| +** | 
| */ | 
| static int quotaStrglob(const char *zGlob, const char *z){ | 
| -  int c, c2; | 
| +  int c, c2, cx; | 
| int invert; | 
| int seen; | 
|  | 
| @@ -210,8 +271,9 @@ static int quotaStrglob(const char *zGlob, const char *z){ | 
| } | 
| return (*z)!=0; | 
| } | 
| +      cx = (c=='/') ? '\\' : c; | 
| while( (c2 = (*(z++)))!=0 ){ | 
| -        while( c2!=c ){ | 
| +        while( c2!=c && c2!=cx ){ | 
| c2 = *(z++); | 
| if( c2==0 ) return 0; | 
| } | 
| @@ -249,6 +311,9 @@ static int quotaStrglob(const char *zGlob, const char *z){ | 
| c2 = *(zGlob++); | 
| } | 
| if( c2==0 || (seen ^ invert)==0 ) return 0; | 
| +    }else if( c=='/' ){ | 
| +      if( z[0]!='/' && z[0]!='\\' ) return 0; | 
| +      z++; | 
| }else{ | 
| if( c!=(*(z++)) ) return 0; | 
| } | 
| @@ -276,6 +341,78 @@ static sqlite3_file *quotaSubOpen(sqlite3_file *pConn){ | 
| return (sqlite3_file*)&p[1]; | 
| } | 
|  | 
| +/* Find a file in a quota group and return a pointer to that file. | 
| +** Return NULL if the file is not in the group. | 
| +*/ | 
| +static quotaFile *quotaFindFile( | 
| +  quotaGroup *pGroup,     /* Group in which to look for the file */ | 
| +  const char *zName,      /* Full pathname of the file */ | 
| +  int createFlag          /* Try to create the file if not found */ | 
| +){ | 
| +  quotaFile *pFile = pGroup->pFiles; | 
| +  while( pFile && strcmp(pFile->zFilename, zName)!=0 ){ | 
| +    pFile = pFile->pNext; | 
| +  } | 
| +  if( pFile==0 && createFlag ){ | 
| +    int nName = (int)(strlen(zName) & 0x3fffffff); | 
| +    pFile = (quotaFile *)sqlite3_malloc( sizeof(*pFile) + nName + 1 ); | 
| +    if( pFile ){ | 
| +      memset(pFile, 0, sizeof(*pFile)); | 
| +      pFile->zFilename = (char*)&pFile[1]; | 
| +      memcpy(pFile->zFilename, zName, nName+1); | 
| +      pFile->pNext = pGroup->pFiles; | 
| +      if( pGroup->pFiles ) pGroup->pFiles->ppPrev = &pFile->pNext; | 
| +      pFile->ppPrev = &pGroup->pFiles; | 
| +      pGroup->pFiles = pFile; | 
| +      pFile->pGroup = pGroup; | 
| +    } | 
| +  } | 
| +  return pFile; | 
| +} | 
| +/* | 
| +** Translate UTF8 to MBCS for use in fopen() calls.  Return a pointer to the | 
| +** translated text..  Call quota_mbcs_free() to deallocate any memory | 
| +** used to store the returned pointer when done. | 
| +*/ | 
| +static char *quota_utf8_to_mbcs(const char *zUtf8){ | 
| +#if SQLITE_OS_WIN | 
| +  size_t n;          /* Bytes in zUtf8 */ | 
| +  int nWide;         /* number of UTF-16 characters */ | 
| +  int nMbcs;         /* Bytes of MBCS */ | 
| +  LPWSTR zTmpWide;   /* The UTF16 text */ | 
| +  char *zMbcs;       /* The MBCS text */ | 
| +  int codepage;      /* Code page used by fopen() */ | 
| + | 
| +  n = strlen(zUtf8); | 
| +  nWide = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, NULL, 0); | 
| +  if( nWide==0 ) return 0; | 
| +  zTmpWide = (LPWSTR)sqlite3_malloc( (nWide+1)*sizeof(zTmpWide[0]) ); | 
| +  if( zTmpWide==0 ) return 0; | 
| +  MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zTmpWide, nWide); | 
| +  codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP; | 
| +  nMbcs = WideCharToMultiByte(codepage, 0, zTmpWide, nWide, 0, 0, 0, 0); | 
| +  zMbcs = nMbcs ? (char*)sqlite3_malloc( nMbcs+1 ) : 0; | 
| +  if( zMbcs ){ | 
| +    WideCharToMultiByte(codepage, 0, zTmpWide, nWide, zMbcs, nMbcs, 0, 0); | 
| +  } | 
| +  sqlite3_free(zTmpWide); | 
| +  return zMbcs; | 
| +#else | 
| +  return (char*)zUtf8;  /* No-op on unix */ | 
| +#endif | 
| +} | 
| + | 
| +/* | 
| +** Deallocate any memory allocated by quota_utf8_to_mbcs(). | 
| +*/ | 
| +static void quota_mbcs_free(char *zOld){ | 
| +#if SQLITE_OS_WIN | 
| +  sqlite3_free(zOld); | 
| +#else | 
| +  /* No-op on unix */ | 
| +#endif | 
| +} | 
| + | 
| /************************* VFS Method Wrappers *****************************/ | 
| /* | 
| ** This is the xOpen method used for the "quota" VFS. | 
| @@ -319,25 +456,13 @@ static int quotaOpen( | 
| pSubOpen = quotaSubOpen(pConn); | 
| rc = pOrigVfs->xOpen(pOrigVfs, zName, pSubOpen, flags, pOutFlags); | 
| if( rc==SQLITE_OK ){ | 
| -      for(pFile=pGroup->pFiles; pFile && strcmp(pFile->zFilename, zName); | 
| -          pFile=pFile->pNext){} | 
| +      pFile = quotaFindFile(pGroup, zName, 1); | 
| if( pFile==0 ){ | 
| -        int nName = strlen(zName); | 
| -        pFile = sqlite3_malloc( sizeof(*pFile) + nName + 1 ); | 
| -        if( pFile==0 ){ | 
| -          quotaLeave(); | 
| -          pSubOpen->pMethods->xClose(pSubOpen); | 
| -          return SQLITE_NOMEM; | 
| -        } | 
| -        memset(pFile, 0, sizeof(*pFile)); | 
| -        pFile->zFilename = (char*)&pFile[1]; | 
| -        memcpy(pFile->zFilename, zName, nName+1); | 
| -        pFile->pNext = pGroup->pFiles; | 
| -        if( pGroup->pFiles ) pGroup->pFiles->ppPrev = &pFile->pNext; | 
| -        pFile->ppPrev = &pGroup->pFiles; | 
| -        pGroup->pFiles = pFile; | 
| -        pFile->pGroup = pGroup; | 
| +        quotaLeave(); | 
| +        pSubOpen->pMethods->xClose(pSubOpen); | 
| +        return SQLITE_NOMEM; | 
| } | 
| +      pFile->deleteOnClose = (flags & SQLITE_OPEN_DELETEONCLOSE)!=0; | 
| pFile->nRef++; | 
| pQuotaOpen->pFile = pFile; | 
| if( pSubOpen->pMethods->iVersion==1 ){ | 
| @@ -351,6 +476,49 @@ static int quotaOpen( | 
| return rc; | 
| } | 
|  | 
| +/* | 
| +** This is the xDelete method used for the "quota" VFS. | 
| +** | 
| +** If the file being deleted is part of the quota group, then reduce | 
| +** the size of the quota group accordingly.  And remove the file from | 
| +** the set of files in the quota group. | 
| +*/ | 
| +static int quotaDelete( | 
| +  sqlite3_vfs *pVfs,          /* The quota VFS */ | 
| +  const char *zName,          /* Name of file to be deleted */ | 
| +  int syncDir                 /* Do a directory sync after deleting */ | 
| +){ | 
| +  int rc;                                    /* Result code */ | 
| +  quotaFile *pFile;                          /* Files in the quota */ | 
| +  quotaGroup *pGroup;                        /* The group file belongs to */ | 
| +  sqlite3_vfs *pOrigVfs = gQuota.pOrigVfs;   /* Real VFS */ | 
| + | 
| +  /* Do the actual file delete */ | 
| +  rc = pOrigVfs->xDelete(pOrigVfs, zName, syncDir); | 
| + | 
| +  /* If the file just deleted is a member of a quota group, then remove | 
| +  ** it from that quota group. | 
| +  */ | 
| +  if( rc==SQLITE_OK ){ | 
| +    quotaEnter(); | 
| +    pGroup = quotaGroupFind(zName); | 
| +    if( pGroup ){ | 
| +      pFile = quotaFindFile(pGroup, zName, 0); | 
| +      if( pFile ){ | 
| +        if( pFile->nRef ){ | 
| +          pFile->deleteOnClose = 1; | 
| +        }else{ | 
| +          quotaRemoveFile(pFile); | 
| +          quotaGroupDeref(pGroup); | 
| +        } | 
| +      } | 
| +    } | 
| +    quotaLeave(); | 
| +  } | 
| +  return rc; | 
| +} | 
| + | 
| + | 
| /************************ I/O Method Wrappers *******************************/ | 
|  | 
| /* xClose requests get passed through to the original VFS.  But we | 
| @@ -367,11 +535,11 @@ static int quotaClose(sqlite3_file *pConn){ | 
| pFile->nRef--; | 
| if( pFile->nRef==0 ){ | 
| quotaGroup *pGroup = pFile->pGroup; | 
| -    pGroup->iSize -= pFile->iSize; | 
| -    if( pFile->pNext ) pFile->pNext->ppPrev = pFile->ppPrev; | 
| -    *pFile->ppPrev = pFile->pNext; | 
| +    if( pFile->deleteOnClose ){ | 
| +      gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0); | 
| +      quotaRemoveFile(pFile); | 
| +    } | 
| quotaGroupDeref(pGroup); | 
| -    sqlite3_free(pFile); | 
| } | 
| quotaLeave(); | 
| return rc; | 
| @@ -504,7 +672,13 @@ static int quotaCheckReservedLock(sqlite3_file *pConn, int *pResOut){ | 
| */ | 
| static int quotaFileControl(sqlite3_file *pConn, int op, void *pArg){ | 
| sqlite3_file *pSubOpen = quotaSubOpen(pConn); | 
| -  return pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg); | 
| +  int rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg); | 
| +#if defined(SQLITE_FCNTL_VFSNAME) | 
| +  if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){ | 
| +    *(char**)pArg = sqlite3_mprintf("quota/%z", *(char**)pArg); | 
| +  } | 
| +#endif | 
| +  return rc; | 
| } | 
|  | 
| /* Pass xSectorSize requests through to the original VFS unchanged. | 
| @@ -586,6 +760,7 @@ int sqlite3_quota_initialize(const char *zOrigVfsName, int makeDefault){ | 
| gQuota.pOrigVfs = pOrigVfs; | 
| gQuota.sThisVfs = *pOrigVfs; | 
| gQuota.sThisVfs.xOpen = quotaOpen; | 
| +  gQuota.sThisVfs.xDelete = quotaDelete; | 
| gQuota.sThisVfs.szOsFile += sizeof(quotaConn); | 
| gQuota.sThisVfs.zName = "quota"; | 
| gQuota.sIoMethodsV1.iVersion = 1; | 
| @@ -617,19 +792,20 @@ int sqlite3_quota_initialize(const char *zOrigVfsName, int makeDefault){ | 
| ** All SQLite database connections must be closed before calling this | 
| ** routine. | 
| ** | 
| -** THIS ROUTINE IS NOT THREADSAFE.  Call this routine exactly one while | 
| +** THIS ROUTINE IS NOT THREADSAFE.  Call this routine exactly once while | 
| ** shutting down in order to free all remaining quota groups. | 
| */ | 
| int sqlite3_quota_shutdown(void){ | 
| quotaGroup *pGroup; | 
| if( gQuota.isInitialized==0 ) return SQLITE_MISUSE; | 
| for(pGroup=gQuota.pGroup; pGroup; pGroup=pGroup->pNext){ | 
| -    if( pGroup->pFiles ) return SQLITE_MISUSE; | 
| +    if( quotaGroupOpenFileCount(pGroup)>0 ) return SQLITE_MISUSE; | 
| } | 
| while( gQuota.pGroup ){ | 
| pGroup = gQuota.pGroup; | 
| gQuota.pGroup = pGroup->pNext; | 
| pGroup->iLimit = 0; | 
| +    assert( quotaGroupOpenFileCount(pGroup)==0 ); | 
| quotaGroupDeref(pGroup); | 
| } | 
| gQuota.isInitialized = 0; | 
| @@ -678,12 +854,12 @@ int sqlite3_quota_set( | 
| pGroup = pGroup->pNext; | 
| } | 
| if( pGroup==0 ){ | 
| -    int nPattern = strlen(zPattern); | 
| +    int nPattern = (int)(strlen(zPattern) & 0x3fffffff); | 
| if( iLimit<=0 ){ | 
| quotaLeave(); | 
| return SQLITE_OK; | 
| } | 
| -    pGroup = sqlite3_malloc( sizeof(*pGroup) + nPattern + 1 ); | 
| +    pGroup = (quotaGroup *)sqlite3_malloc( sizeof(*pGroup) + nPattern + 1 ); | 
| if( pGroup==0 ){ | 
| quotaLeave(); | 
| return SQLITE_NOMEM; | 
| @@ -708,6 +884,397 @@ int sqlite3_quota_set( | 
| return SQLITE_OK; | 
| } | 
|  | 
| +/* | 
| +** Bring the named file under quota management.  Or if it is already under | 
| +** management, update its size. | 
| +*/ | 
| +int sqlite3_quota_file(const char *zFilename){ | 
| +  char *zFull; | 
| +  sqlite3_file *fd; | 
| +  int rc; | 
| +  int outFlags = 0; | 
| +  sqlite3_int64 iSize; | 
| +  int nAlloc = gQuota.sThisVfs.szOsFile + gQuota.sThisVfs.mxPathname+2; | 
| + | 
| +  /* Allocate space for a file-handle and the full path for file zFilename */ | 
| +  fd = (sqlite3_file *)sqlite3_malloc(nAlloc); | 
| +  if( fd==0 ){ | 
| +    rc = SQLITE_NOMEM; | 
| +  }else{ | 
| +    zFull = &((char *)fd)[gQuota.sThisVfs.szOsFile]; | 
| +    rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename, | 
| +        gQuota.sThisVfs.mxPathname+1, zFull); | 
| +  } | 
| + | 
| +  if( rc==SQLITE_OK ){ | 
| +    zFull[strlen(zFull)+1] = '\0'; | 
| +    rc = quotaOpen(&gQuota.sThisVfs, zFull, fd, | 
| +                   SQLITE_OPEN_READONLY | SQLITE_OPEN_MAIN_DB, &outFlags); | 
| +    if( rc==SQLITE_OK ){ | 
| +      fd->pMethods->xFileSize(fd, &iSize); | 
| +      fd->pMethods->xClose(fd); | 
| +    }else if( rc==SQLITE_CANTOPEN ){ | 
| +      quotaGroup *pGroup; | 
| +      quotaFile *pFile; | 
| +      quotaEnter(); | 
| +      pGroup = quotaGroupFind(zFull); | 
| +      if( pGroup ){ | 
| +        pFile = quotaFindFile(pGroup, zFull, 0); | 
| +        if( pFile ) quotaRemoveFile(pFile); | 
| +      } | 
| +      quotaLeave(); | 
| +    } | 
| +  } | 
| + | 
| +  sqlite3_free(fd); | 
| +  return rc; | 
| +} | 
| + | 
| +/* | 
| +** Open a potentially quotaed file for I/O. | 
| +*/ | 
| +quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode){ | 
| +  quota_FILE *p = 0; | 
| +  char *zFull = 0; | 
| +  char *zFullTranslated = 0; | 
| +  int rc; | 
| +  quotaGroup *pGroup; | 
| +  quotaFile *pFile; | 
| + | 
| +  zFull = (char*)sqlite3_malloc(gQuota.sThisVfs.mxPathname + 1); | 
| +  if( zFull==0 ) return 0; | 
| +  rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename, | 
| +                                      gQuota.sThisVfs.mxPathname+1, zFull); | 
| +  if( rc ) goto quota_fopen_error; | 
| +  p = (quota_FILE*)sqlite3_malloc(sizeof(*p)); | 
| +  if( p==0 ) goto quota_fopen_error; | 
| +  memset(p, 0, sizeof(*p)); | 
| +  zFullTranslated = quota_utf8_to_mbcs(zFull); | 
| +  if( zFullTranslated==0 ) goto quota_fopen_error; | 
| +  p->f = fopen(zFullTranslated, zMode); | 
| +  if( p->f==0 ) goto quota_fopen_error; | 
| +  quotaEnter(); | 
| +  pGroup = quotaGroupFind(zFull); | 
| +  if( pGroup ){ | 
| +    pFile = quotaFindFile(pGroup, zFull, 1); | 
| +    if( pFile==0 ){ | 
| +      quotaLeave(); | 
| +      goto quota_fopen_error; | 
| +    } | 
| +    pFile->nRef++; | 
| +    p->pFile = pFile; | 
| +  } | 
| +  quotaLeave(); | 
| +  sqlite3_free(zFull); | 
| +#if SQLITE_OS_WIN | 
| +  p->zMbcsName = zFullTranslated; | 
| +#endif | 
| +  return p; | 
| + | 
| +quota_fopen_error: | 
| +  quota_mbcs_free(zFullTranslated); | 
| +  sqlite3_free(zFull); | 
| +  if( p && p->f ) fclose(p->f); | 
| +  sqlite3_free(p); | 
| +  return 0; | 
| +} | 
| + | 
| +/* | 
| +** Read content from a quota_FILE | 
| +*/ | 
| +size_t sqlite3_quota_fread( | 
| +  void *pBuf,            /* Store the content here */ | 
| +  size_t size,           /* Size of each element */ | 
| +  size_t nmemb,          /* Number of elements to read */ | 
| +  quota_FILE *p          /* Read from this quota_FILE object */ | 
| +){ | 
| +  return fread(pBuf, size, nmemb, p->f); | 
| +} | 
| + | 
| +/* | 
| +** Write content into a quota_FILE.  Invoke the quota callback and block | 
| +** the write if we exceed quota. | 
| +*/ | 
| +size_t sqlite3_quota_fwrite( | 
| +  const void *pBuf,      /* Take content to write from here */ | 
| +  size_t size,           /* Size of each element */ | 
| +  size_t nmemb,          /* Number of elements */ | 
| +  quota_FILE *p          /* Write to this quota_FILE objecct */ | 
| +){ | 
| +  sqlite3_int64 iOfst; | 
| +  sqlite3_int64 iEnd; | 
| +  sqlite3_int64 szNew; | 
| +  quotaFile *pFile; | 
| +  size_t rc; | 
| + | 
| +  iOfst = ftell(p->f); | 
| +  iEnd = iOfst + size*nmemb; | 
| +  pFile = p->pFile; | 
| +  if( pFile && pFile->iSize<iEnd ){ | 
| +    quotaGroup *pGroup = pFile->pGroup; | 
| +    quotaEnter(); | 
| +    szNew = pGroup->iSize - pFile->iSize + iEnd; | 
| +    if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){ | 
| +      if( pGroup->xCallback ){ | 
| +        pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew, | 
| +                          pGroup->pArg); | 
| +      } | 
| +      if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){ | 
| +        iEnd = pGroup->iLimit - pGroup->iSize + pFile->iSize; | 
| +        nmemb = (size_t)((iEnd - iOfst)/size); | 
| +        iEnd = iOfst + size*nmemb; | 
| +        szNew = pGroup->iSize - pFile->iSize + iEnd; | 
| +      } | 
| +    } | 
| +    pGroup->iSize = szNew; | 
| +    pFile->iSize = iEnd; | 
| +    quotaLeave(); | 
| +  }else{ | 
| +    pFile = 0; | 
| +  } | 
| +  rc = fwrite(pBuf, size, nmemb, p->f); | 
| + | 
| +  /* If the write was incomplete, adjust the file size and group size | 
| +  ** downward */ | 
| +  if( rc<nmemb && pFile ){ | 
| +    size_t nWritten = rc; | 
| +    sqlite3_int64 iNewEnd = iOfst + size*nWritten; | 
| +    if( iNewEnd<iEnd ) iNewEnd = iEnd; | 
| +    quotaEnter(); | 
| +    pFile->pGroup->iSize += iNewEnd - pFile->iSize; | 
| +    pFile->iSize = iNewEnd; | 
| +    quotaLeave(); | 
| +  } | 
| +  return rc; | 
| +} | 
| + | 
| +/* | 
| +** Close an open quota_FILE stream. | 
| +*/ | 
| +int sqlite3_quota_fclose(quota_FILE *p){ | 
| +  int rc; | 
| +  quotaFile *pFile; | 
| +  rc = fclose(p->f); | 
| +  pFile = p->pFile; | 
| +  if( pFile ){ | 
| +    quotaEnter(); | 
| +    pFile->nRef--; | 
| +    if( pFile->nRef==0 ){ | 
| +      quotaGroup *pGroup = pFile->pGroup; | 
| +      if( pFile->deleteOnClose ){ | 
| +        gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0); | 
| +        quotaRemoveFile(pFile); | 
| +      } | 
| +      quotaGroupDeref(pGroup); | 
| +    } | 
| +    quotaLeave(); | 
| +  } | 
| +#if SQLITE_OS_WIN | 
| +  quota_mbcs_free(p->zMbcsName); | 
| +#endif | 
| +  sqlite3_free(p); | 
| +  return rc; | 
| +} | 
| + | 
| +/* | 
| +** Flush memory buffers for a quota_FILE to disk. | 
| +*/ | 
| +int sqlite3_quota_fflush(quota_FILE *p, int doFsync){ | 
| +  int rc; | 
| +  rc = fflush(p->f); | 
| +  if( rc==0 && doFsync ){ | 
| +#if SQLITE_OS_UNIX | 
| +    rc = fsync(fileno(p->f)); | 
| +#endif | 
| +#if SQLITE_OS_WIN | 
| +    rc = _commit(_fileno(p->f)); | 
| +#endif | 
| +  } | 
| +  return rc!=0; | 
| +} | 
| + | 
| +/* | 
| +** Seek on a quota_FILE stream. | 
| +*/ | 
| +int sqlite3_quota_fseek(quota_FILE *p, long offset, int whence){ | 
| +  return fseek(p->f, offset, whence); | 
| +} | 
| + | 
| +/* | 
| +** rewind a quota_FILE stream. | 
| +*/ | 
| +void sqlite3_quota_rewind(quota_FILE *p){ | 
| +  rewind(p->f); | 
| +} | 
| + | 
| +/* | 
| +** Tell the current location of a quota_FILE stream. | 
| +*/ | 
| +long sqlite3_quota_ftell(quota_FILE *p){ | 
| +  return ftell(p->f); | 
| +} | 
| + | 
| +/* | 
| +** Test the error indicator for the given file. | 
| +*/ | 
| +int sqlite3_quota_ferror(quota_FILE *p){ | 
| +  return ferror(p->f); | 
| +} | 
| + | 
| +/* | 
| +** Truncate a file to szNew bytes. | 
| +*/ | 
| +int sqlite3_quota_ftruncate(quota_FILE *p, sqlite3_int64 szNew){ | 
| +  quotaFile *pFile = p->pFile; | 
| +  int rc; | 
| +  if( (pFile = p->pFile)!=0 && pFile->iSize<szNew ){ | 
| +    quotaGroup *pGroup; | 
| +    if( pFile->iSize<szNew ){ | 
| +      /* This routine cannot be used to extend a file that is under | 
| +      ** quota management.  Only true truncation is allowed. */ | 
| +      return -1; | 
| +    } | 
| +    pGroup = pFile->pGroup; | 
| +    quotaEnter(); | 
| +    pGroup->iSize += szNew - pFile->iSize; | 
| +    quotaLeave(); | 
| +  } | 
| +#if SQLITE_OS_UNIX | 
| +  rc = ftruncate(fileno(p->f), szNew); | 
| +#endif | 
| +#if SQLITE_OS_WIN | 
| +#  if defined(__MINGW32__) && defined(SQLITE_TEST) | 
| +     /* _chsize_s() is missing from MingW (as of 2012-11-06).  Use | 
| +     ** _chsize() as a work-around for testing purposes. */ | 
| +     rc = _chsize(_fileno(p->f), (long)szNew); | 
| +#  else | 
| +     rc = _chsize_s(_fileno(p->f), szNew); | 
| +#  endif | 
| +#endif | 
| +  if( pFile && rc==0 ){ | 
| +    quotaGroup *pGroup = pFile->pGroup; | 
| +    quotaEnter(); | 
| +    pGroup->iSize += szNew - pFile->iSize; | 
| +    pFile->iSize = szNew; | 
| +    quotaLeave(); | 
| +  } | 
| +  return rc; | 
| +} | 
| + | 
| +/* | 
| +** Determine the time that the given file was last modified, in | 
| +** seconds size 1970.  Write the result into *pTime.  Return 0 on | 
| +** success and non-zero on any kind of error. | 
| +*/ | 
| +int sqlite3_quota_file_mtime(quota_FILE *p, time_t *pTime){ | 
| +  int rc; | 
| +#if SQLITE_OS_UNIX | 
| +  struct stat buf; | 
| +  rc = fstat(fileno(p->f), &buf); | 
| +#endif | 
| +#if SQLITE_OS_WIN | 
| +  struct _stati64 buf; | 
| +  rc = _stati64(p->zMbcsName, &buf); | 
| +#endif | 
| +  if( rc==0 ) *pTime = buf.st_mtime; | 
| +  return rc; | 
| +} | 
| + | 
| +/* | 
| +** Return the true size of the file, as reported by the operating | 
| +** system. | 
| +*/ | 
| +sqlite3_int64 sqlite3_quota_file_truesize(quota_FILE *p){ | 
| +  int rc; | 
| +#if SQLITE_OS_UNIX | 
| +  struct stat buf; | 
| +  rc = fstat(fileno(p->f), &buf); | 
| +#endif | 
| +#if SQLITE_OS_WIN | 
| +  struct _stati64 buf; | 
| +  rc = _stati64(p->zMbcsName, &buf); | 
| +#endif | 
| +  return rc==0 ? buf.st_size : -1; | 
| +} | 
| + | 
| +/* | 
| +** Return the size of the file, as it is known to the quota subsystem. | 
| +*/ | 
| +sqlite3_int64 sqlite3_quota_file_size(quota_FILE *p){ | 
| +  return p->pFile ? p->pFile->iSize : -1; | 
| +} | 
| + | 
| +/* | 
| +** Determine the amount of data in bytes available for reading | 
| +** in the given file. | 
| +*/ | 
| +long sqlite3_quota_file_available(quota_FILE *p){ | 
| +  FILE* f = p->f; | 
| +  long pos1, pos2; | 
| +  int rc; | 
| +  pos1 = ftell(f); | 
| +  if ( pos1 < 0 ) return -1; | 
| +  rc = fseek(f, 0, SEEK_END); | 
| +  if ( rc != 0 ) return -1; | 
| +  pos2 = ftell(f); | 
| +  if ( pos2 < 0 ) return -1; | 
| +  rc = fseek(f, pos1, SEEK_SET); | 
| +  if ( rc != 0 ) return -1; | 
| +  return pos2 - pos1; | 
| +} | 
| + | 
| +/* | 
| +** Remove a managed file.  Update quotas accordingly. | 
| +*/ | 
| +int sqlite3_quota_remove(const char *zFilename){ | 
| +  char *zFull;            /* Full pathname for zFilename */ | 
| +  size_t nFull;           /* Number of bytes in zFilename */ | 
| +  int rc;                 /* Result code */ | 
| +  quotaGroup *pGroup;     /* Group containing zFilename */ | 
| +  quotaFile *pFile;       /* A file in the group */ | 
| +  quotaFile *pNextFile;   /* next file in the group */ | 
| +  int diff;               /* Difference between filenames */ | 
| +  char c;                 /* First character past end of pattern */ | 
| + | 
| +  zFull = (char*)sqlite3_malloc(gQuota.sThisVfs.mxPathname + 1); | 
| +  if( zFull==0 ) return SQLITE_NOMEM; | 
| +  rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename, | 
| +                                      gQuota.sThisVfs.mxPathname+1, zFull); | 
| +  if( rc ){ | 
| +    sqlite3_free(zFull); | 
| +    return rc; | 
| +  } | 
| + | 
| +  /* Figure out the length of the full pathname.  If the name ends with | 
| +  ** / (or \ on windows) then remove the trailing /. | 
| +  */ | 
| +  nFull = strlen(zFull); | 
| +  if( nFull>0 && (zFull[nFull-1]=='/' || zFull[nFull-1]=='\\') ){ | 
| +    nFull--; | 
| +    zFull[nFull] = 0; | 
| +  } | 
| + | 
| +  quotaEnter(); | 
| +  pGroup = quotaGroupFind(zFull); | 
| +  if( pGroup ){ | 
| +    for(pFile=pGroup->pFiles; pFile && rc==SQLITE_OK; pFile=pNextFile){ | 
| +      pNextFile = pFile->pNext; | 
| +      diff = strncmp(zFull, pFile->zFilename, nFull); | 
| +      if( diff==0 && ((c = pFile->zFilename[nFull])==0 || c=='/' || c=='\\') ){ | 
| +        if( pFile->nRef ){ | 
| +          pFile->deleteOnClose = 1; | 
| +        }else{ | 
| +          rc = gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0); | 
| +          quotaRemoveFile(pFile); | 
| +          quotaGroupDeref(pGroup); | 
| +        } | 
| +      } | 
| +    } | 
| +  } | 
| +  quotaLeave(); | 
| +  sqlite3_free(zFull); | 
| +  return rc; | 
| +} | 
|  | 
| /***************************** Test Code ***********************************/ | 
| #ifdef SQLITE_TEST | 
| @@ -722,7 +1289,7 @@ struct TclQuotaCallback { | 
| Tcl_Obj *pScript;      /* Script to be run */ | 
| }; | 
|  | 
| -extern const char *sqlite3TestErrorName(int); | 
| +extern const char *sqlite3ErrName(int); | 
|  | 
|  | 
| /* | 
| @@ -757,8 +1324,10 @@ static void tclQuotaCallback( | 
| rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL); | 
|  | 
| if( rc==TCL_OK ){ | 
| +    Tcl_WideInt x; | 
| Tcl_Obj *pLimit = Tcl_ObjGetVar2(p->interp, pVarname, 0, 0); | 
| -    rc = Tcl_GetWideIntFromObj(p->interp, pLimit, piLimit); | 
| +    rc = Tcl_GetWideIntFromObj(p->interp, pLimit, &x); | 
| +    *piLimit = x; | 
| Tcl_UnsetVar(p->interp, Tcl_GetString(pVarname), 0); | 
| } | 
|  | 
| @@ -802,7 +1371,7 @@ static int test_quota_initialize( | 
|  | 
| /* Call sqlite3_quota_initialize() */ | 
| rc = sqlite3_quota_initialize(zName, makeDefault); | 
| -  Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); | 
| +  Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); | 
|  | 
| return TCL_OK; | 
| } | 
| @@ -825,7 +1394,7 @@ static int test_quota_shutdown( | 
|  | 
| /* Call sqlite3_quota_shutdown() */ | 
| rc = sqlite3_quota_shutdown(); | 
| -  Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); | 
| +  Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); | 
|  | 
| return TCL_OK; | 
| } | 
| @@ -840,7 +1409,7 @@ static int test_quota_set( | 
| Tcl_Obj *CONST objv[] | 
| ){ | 
| const char *zPattern;           /* File pattern to configure */ | 
| -  sqlite3_int64 iLimit;           /* Initial quota in bytes */ | 
| +  Tcl_WideInt iLimit;             /* Initial quota in bytes */ | 
| Tcl_Obj *pScript;               /* Tcl script to invoke to increase quota */ | 
| int rc;                         /* Value returned by quota_set() */ | 
| TclQuotaCallback *p;            /* Callback object */ | 
| @@ -880,7 +1449,33 @@ static int test_quota_set( | 
| /* Invoke sqlite3_quota_set() */ | 
| rc = sqlite3_quota_set(zPattern, iLimit, xCallback, (void*)p, xDestroy); | 
|  | 
| -  Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); | 
| +  Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); | 
| +  return TCL_OK; | 
| +} | 
| + | 
| +/* | 
| +** tclcmd: sqlite3_quota_file FILENAME | 
| +*/ | 
| +static int test_quota_file( | 
| +  void * clientData, | 
| +  Tcl_Interp *interp, | 
| +  int objc, | 
| +  Tcl_Obj *CONST objv[] | 
| +){ | 
| +  const char *zFilename;          /* File pattern to configure */ | 
| +  int rc;                         /* Value returned by quota_file() */ | 
| + | 
| +  /* Process arguments */ | 
| +  if( objc!=2 ){ | 
| +    Tcl_WrongNumArgs(interp, 1, objv, "FILENAME"); | 
| +    return TCL_ERROR; | 
| +  } | 
| +  zFilename = Tcl_GetString(objv[1]); | 
| + | 
| +  /* Invoke sqlite3_quota_file() */ | 
| +  rc = sqlite3_quota_file(zFilename); | 
| + | 
| +  Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); | 
| return TCL_OK; | 
| } | 
|  | 
| @@ -910,13 +1505,19 @@ static int test_quota_dump( | 
| Tcl_ListObjAppendElement(interp, pGroupTerm, | 
| Tcl_NewWideIntObj(pGroup->iSize)); | 
| for(pFile=pGroup->pFiles; pFile; pFile=pFile->pNext){ | 
| +      int i; | 
| +      char zTemp[1000]; | 
| pFileTerm = Tcl_NewObj(); | 
| +      sqlite3_snprintf(sizeof(zTemp), zTemp, "%s", pFile->zFilename); | 
| +      for(i=0; zTemp[i]; i++){ if( zTemp[i]=='\\' ) zTemp[i] = '/'; } | 
| Tcl_ListObjAppendElement(interp, pFileTerm, | 
| -            Tcl_NewStringObj(pFile->zFilename, -1)); | 
| +            Tcl_NewStringObj(zTemp, -1)); | 
| Tcl_ListObjAppendElement(interp, pFileTerm, | 
| Tcl_NewWideIntObj(pFile->iSize)); | 
| Tcl_ListObjAppendElement(interp, pFileTerm, | 
| Tcl_NewWideIntObj(pFile->nRef)); | 
| +      Tcl_ListObjAppendElement(interp, pFileTerm, | 
| +            Tcl_NewWideIntObj(pFile->deleteOnClose)); | 
| Tcl_ListObjAppendElement(interp, pGroupTerm, pFileTerm); | 
| } | 
| Tcl_ListObjAppendElement(interp, pResult, pGroupTerm); | 
| @@ -927,6 +1528,408 @@ static int test_quota_dump( | 
| } | 
|  | 
| /* | 
| +** tclcmd: sqlite3_quota_fopen FILENAME MODE | 
| +*/ | 
| +static int test_quota_fopen( | 
| +  void * clientData, | 
| +  Tcl_Interp *interp, | 
| +  int objc, | 
| +  Tcl_Obj *CONST objv[] | 
| +){ | 
| +  const char *zFilename;          /* File pattern to configure */ | 
| +  const char *zMode;              /* Mode string */ | 
| +  quota_FILE *p;                  /* Open string object */ | 
| +  char zReturn[50];               /* Name of pointer to return */ | 
| + | 
| +  /* Process arguments */ | 
| +  if( objc!=3 ){ | 
| +    Tcl_WrongNumArgs(interp, 1, objv, "FILENAME MODE"); | 
| +    return TCL_ERROR; | 
| +  } | 
| +  zFilename = Tcl_GetString(objv[1]); | 
| +  zMode = Tcl_GetString(objv[2]); | 
| +  p = sqlite3_quota_fopen(zFilename, zMode); | 
| +  sqlite3_snprintf(sizeof(zReturn), zReturn, "%p", p); | 
| +  Tcl_SetResult(interp, zReturn, TCL_VOLATILE); | 
| +  return TCL_OK; | 
| +} | 
| + | 
| +/* Defined in test1.c */ | 
| +extern void *sqlite3TestTextToPtr(const char*); | 
| + | 
| +/* | 
| +** tclcmd: sqlite3_quota_fread HANDLE SIZE NELEM | 
| +*/ | 
| +static int test_quota_fread( | 
| +  void * clientData, | 
| +  Tcl_Interp *interp, | 
| +  int objc, | 
| +  Tcl_Obj *CONST objv[] | 
| +){ | 
| +  quota_FILE *p; | 
| +  char *zBuf; | 
| +  int sz; | 
| +  int nElem; | 
| +  size_t got; | 
| + | 
| +  if( objc!=4 ){ | 
| +    Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE NELEM"); | 
| +    return TCL_ERROR; | 
| +  } | 
| +  p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); | 
| +  if( Tcl_GetIntFromObj(interp, objv[2], &sz) ) return TCL_ERROR; | 
| +  if( Tcl_GetIntFromObj(interp, objv[3], &nElem) ) return TCL_ERROR; | 
| +  zBuf = (char*)sqlite3_malloc( sz*nElem + 1 ); | 
| +  if( zBuf==0 ){ | 
| +    Tcl_SetResult(interp, "out of memory", TCL_STATIC); | 
| +    return TCL_ERROR; | 
| +  } | 
| +  got = sqlite3_quota_fread(zBuf, sz, nElem, p); | 
| +  zBuf[got*sz] = 0; | 
| +  Tcl_SetResult(interp, zBuf, TCL_VOLATILE); | 
| +  sqlite3_free(zBuf); | 
| +  return TCL_OK; | 
| +} | 
| + | 
| +/* | 
| +** tclcmd: sqlite3_quota_fwrite HANDLE SIZE NELEM CONTENT | 
| +*/ | 
| +static int test_quota_fwrite( | 
| +  void * clientData, | 
| +  Tcl_Interp *interp, | 
| +  int objc, | 
| +  Tcl_Obj *CONST objv[] | 
| +){ | 
| +  quota_FILE *p; | 
| +  char *zBuf; | 
| +  int sz; | 
| +  int nElem; | 
| +  size_t got; | 
| + | 
| +  if( objc!=5 ){ | 
| +    Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE NELEM CONTENT"); | 
| +    return TCL_ERROR; | 
| +  } | 
| +  p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); | 
| +  if( Tcl_GetIntFromObj(interp, objv[2], &sz) ) return TCL_ERROR; | 
| +  if( Tcl_GetIntFromObj(interp, objv[3], &nElem) ) return TCL_ERROR; | 
| +  zBuf = Tcl_GetString(objv[4]); | 
| +  got = sqlite3_quota_fwrite(zBuf, sz, nElem, p); | 
| +  Tcl_SetObjResult(interp, Tcl_NewWideIntObj(got)); | 
| +  return TCL_OK; | 
| +} | 
| + | 
| +/* | 
| +** tclcmd: sqlite3_quota_fclose HANDLE | 
| +*/ | 
| +static int test_quota_fclose( | 
| +  void * clientData, | 
| +  Tcl_Interp *interp, | 
| +  int objc, | 
| +  Tcl_Obj *CONST objv[] | 
| +){ | 
| +  quota_FILE *p; | 
| +  int rc; | 
| + | 
| +  if( objc!=2 ){ | 
| +    Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); | 
| +    return TCL_ERROR; | 
| +  } | 
| +  p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); | 
| +  rc = sqlite3_quota_fclose(p); | 
| +  Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); | 
| +  return TCL_OK; | 
| +} | 
| + | 
| +/* | 
| +** tclcmd: sqlite3_quota_fflush HANDLE ?HARDSYNC? | 
| +*/ | 
| +static int test_quota_fflush( | 
| +  void * clientData, | 
| +  Tcl_Interp *interp, | 
| +  int objc, | 
| +  Tcl_Obj *CONST objv[] | 
| +){ | 
| +  quota_FILE *p; | 
| +  int rc; | 
| +  int doSync = 0; | 
| + | 
| +  if( objc!=2 && objc!=3 ){ | 
| +    Tcl_WrongNumArgs(interp, 1, objv, "HANDLE ?HARDSYNC?"); | 
| +    return TCL_ERROR; | 
| +  } | 
| +  p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); | 
| +  if( objc==3 ){ | 
| +    if( Tcl_GetBooleanFromObj(interp, objv[2], &doSync) ) return TCL_ERROR; | 
| +  } | 
| +  rc = sqlite3_quota_fflush(p, doSync); | 
| +  Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); | 
| +  return TCL_OK; | 
| +} | 
| + | 
| +/* | 
| +** tclcmd: sqlite3_quota_fseek HANDLE OFFSET WHENCE | 
| +*/ | 
| +static int test_quota_fseek( | 
| +  void * clientData, | 
| +  Tcl_Interp *interp, | 
| +  int objc, | 
| +  Tcl_Obj *CONST objv[] | 
| +){ | 
| +  quota_FILE *p; | 
| +  int ofst; | 
| +  const char *zWhence; | 
| +  int whence; | 
| +  int rc; | 
| + | 
| +  if( objc!=4 ){ | 
| +    Tcl_WrongNumArgs(interp, 1, objv, "HANDLE OFFSET WHENCE"); | 
| +    return TCL_ERROR; | 
| +  } | 
| +  p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); | 
| +  if( Tcl_GetIntFromObj(interp, objv[2], &ofst) ) return TCL_ERROR; | 
| +  zWhence = Tcl_GetString(objv[3]); | 
| +  if( strcmp(zWhence, "SEEK_SET")==0 ){ | 
| +    whence = SEEK_SET; | 
| +  }else if( strcmp(zWhence, "SEEK_CUR")==0 ){ | 
| +    whence = SEEK_CUR; | 
| +  }else if( strcmp(zWhence, "SEEK_END")==0 ){ | 
| +    whence = SEEK_END; | 
| +  }else{ | 
| +    Tcl_AppendResult(interp, | 
| +           "WHENCE should be SEEK_SET, SEEK_CUR, or SEEK_END", (char*)0); | 
| +    return TCL_ERROR; | 
| +  } | 
| +  rc = sqlite3_quota_fseek(p, ofst, whence); | 
| +  Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); | 
| +  return TCL_OK; | 
| +} | 
| + | 
| +/* | 
| +** tclcmd: sqlite3_quota_rewind HANDLE | 
| +*/ | 
| +static int test_quota_rewind( | 
| +  void * clientData, | 
| +  Tcl_Interp *interp, | 
| +  int objc, | 
| +  Tcl_Obj *CONST objv[] | 
| +){ | 
| +  quota_FILE *p; | 
| +  if( objc!=2 ){ | 
| +    Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); | 
| +    return TCL_ERROR; | 
| +  } | 
| +  p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); | 
| +  sqlite3_quota_rewind(p); | 
| +  return TCL_OK; | 
| +} | 
| + | 
| +/* | 
| +** tclcmd: sqlite3_quota_ftell HANDLE | 
| +*/ | 
| +static int test_quota_ftell( | 
| +  void * clientData, | 
| +  Tcl_Interp *interp, | 
| +  int objc, | 
| +  Tcl_Obj *CONST objv[] | 
| +){ | 
| +  quota_FILE *p; | 
| +  sqlite3_int64 x; | 
| +  if( objc!=2 ){ | 
| +    Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); | 
| +    return TCL_ERROR; | 
| +  } | 
| +  p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); | 
| +  x = sqlite3_quota_ftell(p); | 
| +  Tcl_SetObjResult(interp, Tcl_NewWideIntObj(x)); | 
| +  return TCL_OK; | 
| +} | 
| + | 
| +/* | 
| +** tclcmd: sqlite3_quota_ftruncate HANDLE SIZE | 
| +*/ | 
| +static int test_quota_ftruncate( | 
| +  void * clientData, | 
| +  Tcl_Interp *interp, | 
| +  int objc, | 
| +  Tcl_Obj *CONST objv[] | 
| +){ | 
| +  quota_FILE *p; | 
| +  sqlite3_int64 x; | 
| +  Tcl_WideInt w; | 
| +  int rc; | 
| +  if( objc!=3 ){ | 
| +    Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE"); | 
| +    return TCL_ERROR; | 
| +  } | 
| +  p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); | 
| +  if( Tcl_GetWideIntFromObj(interp, objv[2], &w) ) return TCL_ERROR; | 
| +  x = (sqlite3_int64)w; | 
| +  rc = sqlite3_quota_ftruncate(p, x); | 
| +  Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); | 
| +  return TCL_OK; | 
| +} | 
| + | 
| +/* | 
| +** tclcmd: sqlite3_quota_file_size HANDLE | 
| +*/ | 
| +static int test_quota_file_size( | 
| +  void * clientData, | 
| +  Tcl_Interp *interp, | 
| +  int objc, | 
| +  Tcl_Obj *CONST objv[] | 
| +){ | 
| +  quota_FILE *p; | 
| +  sqlite3_int64 x; | 
| +  if( objc!=2 ){ | 
| +    Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); | 
| +    return TCL_ERROR; | 
| +  } | 
| +  p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); | 
| +  x = sqlite3_quota_file_size(p); | 
| +  Tcl_SetObjResult(interp, Tcl_NewWideIntObj(x)); | 
| +  return TCL_OK; | 
| +} | 
| + | 
| +/* | 
| +** tclcmd: sqlite3_quota_file_truesize HANDLE | 
| +*/ | 
| +static int test_quota_file_truesize( | 
| +  void * clientData, | 
| +  Tcl_Interp *interp, | 
| +  int objc, | 
| +  Tcl_Obj *CONST objv[] | 
| +){ | 
| +  quota_FILE *p; | 
| +  sqlite3_int64 x; | 
| +  if( objc!=2 ){ | 
| +    Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); | 
| +    return TCL_ERROR; | 
| +  } | 
| +  p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); | 
| +  x = sqlite3_quota_file_truesize(p); | 
| +  Tcl_SetObjResult(interp, Tcl_NewWideIntObj(x)); | 
| +  return TCL_OK; | 
| +} | 
| + | 
| +/* | 
| +** tclcmd: sqlite3_quota_file_mtime HANDLE | 
| +*/ | 
| +static int test_quota_file_mtime( | 
| +  void * clientData, | 
| +  Tcl_Interp *interp, | 
| +  int objc, | 
| +  Tcl_Obj *CONST objv[] | 
| +){ | 
| +  quota_FILE *p; | 
| +  time_t t; | 
| +  if( objc!=2 ){ | 
| +    Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); | 
| +    return TCL_ERROR; | 
| +  } | 
| +  p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); | 
| +  t = 0; | 
| +  sqlite3_quota_file_mtime(p, &t); | 
| +  Tcl_SetObjResult(interp, Tcl_NewWideIntObj(t)); | 
| +  return TCL_OK; | 
| +} | 
| + | 
| + | 
| +/* | 
| +** tclcmd: sqlite3_quota_remove FILENAME | 
| +*/ | 
| +static int test_quota_remove( | 
| +  void * clientData, | 
| +  Tcl_Interp *interp, | 
| +  int objc, | 
| +  Tcl_Obj *CONST objv[] | 
| +){ | 
| +  const char *zFilename;          /* File pattern to configure */ | 
| +  int rc; | 
| +  if( objc!=2 ){ | 
| +    Tcl_WrongNumArgs(interp, 1, objv, "FILENAME"); | 
| +    return TCL_ERROR; | 
| +  } | 
| +  zFilename = Tcl_GetString(objv[1]); | 
| +  rc = sqlite3_quota_remove(zFilename); | 
| +  Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); | 
| +  return TCL_OK; | 
| +} | 
| + | 
| +/* | 
| +** tclcmd: sqlite3_quota_glob PATTERN TEXT | 
| +** | 
| +** Test the glob pattern matching.  Return 1 if TEXT matches PATTERN | 
| +** and return 0 if it does not. | 
| +*/ | 
| +static int test_quota_glob( | 
| +  void * clientData, | 
| +  Tcl_Interp *interp, | 
| +  int objc, | 
| +  Tcl_Obj *CONST objv[] | 
| +){ | 
| +  const char *zPattern;          /* The glob pattern */ | 
| +  const char *zText;             /* Text to compare agains the pattern */ | 
| +  int rc; | 
| +  if( objc!=3 ){ | 
| +    Tcl_WrongNumArgs(interp, 1, objv, "PATTERN TEXT"); | 
| +    return TCL_ERROR; | 
| +  } | 
| +  zPattern = Tcl_GetString(objv[1]); | 
| +  zText = Tcl_GetString(objv[2]); | 
| +  rc = quotaStrglob(zPattern, zText); | 
| +  Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); | 
| +  return TCL_OK; | 
| +} | 
| + | 
| +/* | 
| +** tclcmd: sqlite3_quota_file_available HANDLE | 
| +** | 
| +** Return the number of bytes from the current file point to the end of | 
| +** the file. | 
| +*/ | 
| +static int test_quota_file_available( | 
| +  void * clientData, | 
| +  Tcl_Interp *interp, | 
| +  int objc, | 
| +  Tcl_Obj *CONST objv[] | 
| +){ | 
| +  quota_FILE *p; | 
| +  sqlite3_int64 x; | 
| +  if( objc!=2 ){ | 
| +    Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); | 
| +    return TCL_ERROR; | 
| +  } | 
| +  p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); | 
| +  x = sqlite3_quota_file_available(p); | 
| +  Tcl_SetObjResult(interp, Tcl_NewWideIntObj(x)); | 
| +  return TCL_OK; | 
| +} | 
| + | 
| +/* | 
| +** tclcmd: sqlite3_quota_ferror HANDLE | 
| +** | 
| +** Return true if the file handle is in the error state. | 
| +*/ | 
| +static int test_quota_ferror( | 
| +  void * clientData, | 
| +  Tcl_Interp *interp, | 
| +  int objc, | 
| +  Tcl_Obj *CONST objv[] | 
| +){ | 
| +  quota_FILE *p; | 
| +  int x; | 
| +  if( objc!=2 ){ | 
| +    Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); | 
| +    return TCL_ERROR; | 
| +  } | 
| +  p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); | 
| +  x = sqlite3_quota_ferror(p); | 
| +  Tcl_SetObjResult(interp, Tcl_NewIntObj(x)); | 
| +  return TCL_OK; | 
| +} | 
| + | 
| +/* | 
| ** This routine registers the custom TCL commands defined in this | 
| ** module.  This should be the only procedure visible from outside | 
| ** of this module. | 
| @@ -936,10 +1939,27 @@ int Sqlitequota_Init(Tcl_Interp *interp){ | 
| char *zName; | 
| Tcl_ObjCmdProc *xProc; | 
| } aCmd[] = { | 
| -    { "sqlite3_quota_initialize", test_quota_initialize }, | 
| -    { "sqlite3_quota_shutdown", test_quota_shutdown }, | 
| -    { "sqlite3_quota_set", test_quota_set }, | 
| -    { "sqlite3_quota_dump", test_quota_dump }, | 
| +    { "sqlite3_quota_initialize",    test_quota_initialize }, | 
| +    { "sqlite3_quota_shutdown",      test_quota_shutdown }, | 
| +    { "sqlite3_quota_set",           test_quota_set }, | 
| +    { "sqlite3_quota_file",          test_quota_file }, | 
| +    { "sqlite3_quota_dump",          test_quota_dump }, | 
| +    { "sqlite3_quota_fopen",         test_quota_fopen }, | 
| +    { "sqlite3_quota_fread",         test_quota_fread }, | 
| +    { "sqlite3_quota_fwrite",        test_quota_fwrite }, | 
| +    { "sqlite3_quota_fclose",        test_quota_fclose }, | 
| +    { "sqlite3_quota_fflush",        test_quota_fflush }, | 
| +    { "sqlite3_quota_fseek",         test_quota_fseek }, | 
| +    { "sqlite3_quota_rewind",        test_quota_rewind }, | 
| +    { "sqlite3_quota_ftell",         test_quota_ftell }, | 
| +    { "sqlite3_quota_ftruncate",     test_quota_ftruncate }, | 
| +    { "sqlite3_quota_file_size",     test_quota_file_size }, | 
| +    { "sqlite3_quota_file_truesize", test_quota_file_truesize }, | 
| +    { "sqlite3_quota_file_mtime",    test_quota_file_mtime }, | 
| +    { "sqlite3_quota_remove",        test_quota_remove }, | 
| +    { "sqlite3_quota_glob",          test_quota_glob }, | 
| +    { "sqlite3_quota_file_available",test_quota_file_available }, | 
| +    { "sqlite3_quota_ferror",        test_quota_ferror }, | 
| }; | 
| int i; | 
|  | 
|  |