Annotation of embedaddon/sqlite3/src/test_quota.c, revision 1.1.1.1

1.1       misho       1: /*
                      2: ** 2010 September 31
                      3: **
                      4: ** The author disclaims copyright to this source code.  In place of
                      5: ** a legal notice, here is a blessing:
                      6: **
                      7: **    May you do good and not evil.
                      8: **    May you find forgiveness for yourself and forgive others.
                      9: **    May you share freely, never taking more than you give.
                     10: **
                     11: *************************************************************************
                     12: **
                     13: ** This file contains a VFS "shim" - a layer that sits in between the
                     14: ** pager and the real VFS.
                     15: **
                     16: ** This particular shim enforces a quota system on files.  One or more
                     17: ** database files are in a "quota group" that is defined by a GLOB
                     18: ** pattern.  A quota is set for the combined size of all files in the
                     19: ** the group.  A quota of zero means "no limit".  If the total size
                     20: ** of all files in the quota group is greater than the limit, then
                     21: ** write requests that attempt to enlarge a file fail with SQLITE_FULL.
                     22: **
                     23: ** However, before returning SQLITE_FULL, the write requests invoke
                     24: ** a callback function that is configurable for each quota group.
                     25: ** This callback has the opportunity to enlarge the quota.  If the
                     26: ** callback does enlarge the quota such that the total size of all
                     27: ** files within the group is less than the new quota, then the write
                     28: ** continues as if nothing had happened.
                     29: */
                     30: #include "test_quota.h"
                     31: #include <string.h>
                     32: #include <assert.h>
                     33: 
                     34: /*
                     35: ** For an build without mutexes, no-op the mutex calls.
                     36: */
                     37: #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0
                     38: #define sqlite3_mutex_alloc(X)    ((sqlite3_mutex*)8)
                     39: #define sqlite3_mutex_free(X)
                     40: #define sqlite3_mutex_enter(X)
                     41: #define sqlite3_mutex_try(X)      SQLITE_OK
                     42: #define sqlite3_mutex_leave(X)
                     43: #define sqlite3_mutex_held(X)     ((void)(X),1)
                     44: #define sqlite3_mutex_notheld(X)  ((void)(X),1)
                     45: #endif /* SQLITE_THREADSAFE==0 */
                     46: 
                     47: 
                     48: /************************ Object Definitions ******************************/
                     49: 
                     50: /* Forward declaration of all object types */
                     51: typedef struct quotaGroup quotaGroup;
                     52: typedef struct quotaConn quotaConn;
                     53: typedef struct quotaFile quotaFile;
                     54: 
                     55: /*
                     56: ** A "quota group" is a collection of files whose collective size we want
                     57: ** to limit.  Each quota group is defined by a GLOB pattern.
                     58: **
                     59: ** There is an instance of the following object for each defined quota
                     60: ** group.  This object records the GLOB pattern that defines which files
                     61: ** belong to the quota group.  The object also remembers the size limit
                     62: ** for the group (the quota) and the callback to be invoked when the
                     63: ** sum of the sizes of the files within the group goes over the limit.
                     64: **
                     65: ** A quota group must be established (using sqlite3_quota_set(...))
                     66: ** prior to opening any of the database connections that access files
                     67: ** within the quota group.
                     68: */
                     69: struct quotaGroup {
                     70:   const char *zPattern;          /* Filename pattern to be quotaed */
                     71:   sqlite3_int64 iLimit;          /* Upper bound on total file size */
                     72:   sqlite3_int64 iSize;           /* Current size of all files */
                     73:   void (*xCallback)(             /* Callback invoked when going over quota */
                     74:      const char *zFilename,         /* Name of file whose size increases */
                     75:      sqlite3_int64 *piLimit,        /* IN/OUT: The current limit */
                     76:      sqlite3_int64 iSize,           /* Total size of all files in the group */
                     77:      void *pArg                     /* Client data */
                     78:   );
                     79:   void *pArg;                    /* Third argument to the xCallback() */
                     80:   void (*xDestroy)(void*);       /* Optional destructor for pArg */
                     81:   quotaGroup *pNext, **ppPrev;   /* Doubly linked list of all quota objects */
                     82:   quotaFile *pFiles;             /* Files within this group */
                     83: };
                     84: 
                     85: /*
                     86: ** An instance of this structure represents a single file that is part
                     87: ** of a quota group.  A single file can be opened multiple times.  In
                     88: ** order keep multiple openings of the same file from causing the size
                     89: ** of the file to count against the quota multiple times, each file
                     90: ** has a unique instance of this object and multiple open connections
                     91: ** to the same file each point to a single instance of this object.
                     92: */
                     93: struct quotaFile {
                     94:   char *zFilename;                /* Name of this file */
                     95:   quotaGroup *pGroup;             /* Quota group to which this file belongs */
                     96:   sqlite3_int64 iSize;            /* Current size of this file */
                     97:   int nRef;                       /* Number of times this file is open */
                     98:   int deleteOnClose;              /* True to delete this file when it closes */
                     99:   quotaFile *pNext, **ppPrev;     /* Linked list of files in the same group */
                    100: };
                    101: 
                    102: /*
                    103: ** An instance of the following object represents each open connection
                    104: ** to a file that participates in quota tracking.  This object is a 
                    105: ** subclass of sqlite3_file.  The sqlite3_file object for the underlying
                    106: ** VFS is appended to this structure.
                    107: */
                    108: struct quotaConn {
                    109:   sqlite3_file base;              /* Base class - must be first */
                    110:   quotaFile *pFile;               /* The underlying file */
                    111:   /* The underlying VFS sqlite3_file is appended to this object */
                    112: };
                    113: 
                    114: /*
                    115: ** An instance of the following object records the state of an
                    116: ** open file.  This object is opaque to all users - the internal
                    117: ** structure is only visible to the functions below.
                    118: */
                    119: struct quota_FILE {
                    120:   FILE *f;                /* Open stdio file pointer */
                    121:   sqlite3_int64 iOfst;    /* Current offset into the file */
                    122:   quotaFile *pFile;       /* The file record in the quota system */
                    123: };
                    124: 
                    125: 
                    126: /************************* Global Variables **********************************/
                    127: /*
                    128: ** All global variables used by this file are containing within the following
                    129: ** gQuota structure.
                    130: */
                    131: static struct {
                    132:   /* The pOrigVfs is the real, original underlying VFS implementation.
                    133:   ** Most operations pass-through to the real VFS.  This value is read-only
                    134:   ** during operation.  It is only modified at start-time and thus does not
                    135:   ** require a mutex.
                    136:   */
                    137:   sqlite3_vfs *pOrigVfs;
                    138: 
                    139:   /* The sThisVfs is the VFS structure used by this shim.  It is initialized
                    140:   ** at start-time and thus does not require a mutex
                    141:   */
                    142:   sqlite3_vfs sThisVfs;
                    143: 
                    144:   /* The sIoMethods defines the methods used by sqlite3_file objects 
                    145:   ** associated with this shim.  It is initialized at start-time and does
                    146:   ** not require a mutex.
                    147:   **
                    148:   ** When the underlying VFS is called to open a file, it might return 
                    149:   ** either a version 1 or a version 2 sqlite3_file object.  This shim
                    150:   ** has to create a wrapper sqlite3_file of the same version.  Hence
                    151:   ** there are two I/O method structures, one for version 1 and the other
                    152:   ** for version 2.
                    153:   */
                    154:   sqlite3_io_methods sIoMethodsV1;
                    155:   sqlite3_io_methods sIoMethodsV2;
                    156: 
                    157:   /* True when this shim as been initialized.
                    158:   */
                    159:   int isInitialized;
                    160: 
                    161:   /* For run-time access any of the other global data structures in this
                    162:   ** shim, the following mutex must be held.
                    163:   */
                    164:   sqlite3_mutex *pMutex;
                    165: 
                    166:   /* List of quotaGroup objects.
                    167:   */
                    168:   quotaGroup *pGroup;
                    169: 
                    170: } gQuota;
                    171: 
                    172: /************************* Utility Routines *********************************/
                    173: /*
                    174: ** Acquire and release the mutex used to serialize access to the
                    175: ** list of quotaGroups.
                    176: */
                    177: static void quotaEnter(void){ sqlite3_mutex_enter(gQuota.pMutex); }
                    178: static void quotaLeave(void){ sqlite3_mutex_leave(gQuota.pMutex); }
                    179: 
                    180: /* Count the number of open files in a quotaGroup 
                    181: */
                    182: static int quotaGroupOpenFileCount(quotaGroup *pGroup){
                    183:   int N = 0;
                    184:   quotaFile *pFile = pGroup->pFiles;
                    185:   while( pFile ){
                    186:     if( pFile->nRef ) N++;
                    187:     pFile = pFile->pNext;
                    188:   }
                    189:   return N;
                    190: }
                    191: 
                    192: /* Remove a file from a quota group.
                    193: */
                    194: static void quotaRemoveFile(quotaFile *pFile){
                    195:   quotaGroup *pGroup = pFile->pGroup;
                    196:   pGroup->iSize -= pFile->iSize;
                    197:   *pFile->ppPrev = pFile->pNext;
                    198:   if( pFile->pNext ) pFile->pNext->ppPrev = pFile->ppPrev;
                    199:   sqlite3_free(pFile);
                    200: }
                    201: 
                    202: /* Remove all files from a quota group.  It is always the case that
                    203: ** all files will be closed when this routine is called.
                    204: */
                    205: static void quotaRemoveAllFiles(quotaGroup *pGroup){
                    206:   while( pGroup->pFiles ){
                    207:     assert( pGroup->pFiles->nRef==0 );
                    208:     quotaRemoveFile(pGroup->pFiles);
                    209:   }
                    210: }
                    211: 
                    212: 
                    213: /* If the reference count and threshold for a quotaGroup are both
                    214: ** zero, then destroy the quotaGroup.
                    215: */
                    216: static void quotaGroupDeref(quotaGroup *pGroup){
                    217:   if( pGroup->iLimit==0 && quotaGroupOpenFileCount(pGroup)==0 ){
                    218:     quotaRemoveAllFiles(pGroup);
                    219:     *pGroup->ppPrev = pGroup->pNext;
                    220:     if( pGroup->pNext ) pGroup->pNext->ppPrev = pGroup->ppPrev;
                    221:     if( pGroup->xDestroy ) pGroup->xDestroy(pGroup->pArg);
                    222:     sqlite3_free(pGroup);
                    223:   }
                    224: }
                    225: 
                    226: /*
                    227: ** Return TRUE if string z matches glob pattern zGlob.
                    228: **
                    229: ** Globbing rules:
                    230: **
                    231: **      '*'       Matches any sequence of zero or more characters.
                    232: **
                    233: **      '?'       Matches exactly one character.
                    234: **
                    235: **     [...]      Matches one character from the enclosed list of
                    236: **                characters.
                    237: **
                    238: **     [^...]     Matches one character not in the enclosed list.
                    239: **
                    240: **     /          Matches "/" or "\\"
                    241: **
                    242: */
                    243: static int quotaStrglob(const char *zGlob, const char *z){
                    244:   int c, c2, cx;
                    245:   int invert;
                    246:   int seen;
                    247: 
                    248:   while( (c = (*(zGlob++)))!=0 ){
                    249:     if( c=='*' ){
                    250:       while( (c=(*(zGlob++))) == '*' || c=='?' ){
                    251:         if( c=='?' && (*(z++))==0 ) return 0;
                    252:       }
                    253:       if( c==0 ){
                    254:         return 1;
                    255:       }else if( c=='[' ){
                    256:         while( *z && quotaStrglob(zGlob-1,z)==0 ){
                    257:           z++;
                    258:         }
                    259:         return (*z)!=0;
                    260:       }
                    261:       cx = (c=='/') ? '\\' : c;
                    262:       while( (c2 = (*(z++)))!=0 ){
                    263:         while( c2!=c && c2!=cx ){
                    264:           c2 = *(z++);
                    265:           if( c2==0 ) return 0;
                    266:         }
                    267:         if( quotaStrglob(zGlob,z) ) return 1;
                    268:       }
                    269:       return 0;
                    270:     }else if( c=='?' ){
                    271:       if( (*(z++))==0 ) return 0;
                    272:     }else if( c=='[' ){
                    273:       int prior_c = 0;
                    274:       seen = 0;
                    275:       invert = 0;
                    276:       c = *(z++);
                    277:       if( c==0 ) return 0;
                    278:       c2 = *(zGlob++);
                    279:       if( c2=='^' ){
                    280:         invert = 1;
                    281:         c2 = *(zGlob++);
                    282:       }
                    283:       if( c2==']' ){
                    284:         if( c==']' ) seen = 1;
                    285:         c2 = *(zGlob++);
                    286:       }
                    287:       while( c2 && c2!=']' ){
                    288:         if( c2=='-' && zGlob[0]!=']' && zGlob[0]!=0 && prior_c>0 ){
                    289:           c2 = *(zGlob++);
                    290:           if( c>=prior_c && c<=c2 ) seen = 1;
                    291:           prior_c = 0;
                    292:         }else{
                    293:           if( c==c2 ){
                    294:             seen = 1;
                    295:           }
                    296:           prior_c = c2;
                    297:         }
                    298:         c2 = *(zGlob++);
                    299:       }
                    300:       if( c2==0 || (seen ^ invert)==0 ) return 0;
                    301:     }else if( c=='/' ){
                    302:       if( z[0]!='/' && z[0]!='\\' ) return 0;
                    303:       z++;
                    304:     }else{
                    305:       if( c!=(*(z++)) ) return 0;
                    306:     }
                    307:   }
                    308:   return *z==0;
                    309: }
                    310: 
                    311: 
                    312: /* Find a quotaGroup given the filename.
                    313: **
                    314: ** Return a pointer to the quotaGroup object. Return NULL if not found.
                    315: */
                    316: static quotaGroup *quotaGroupFind(const char *zFilename){
                    317:   quotaGroup *p;
                    318:   for(p=gQuota.pGroup; p && quotaStrglob(p->zPattern, zFilename)==0;
                    319:       p=p->pNext){}
                    320:   return p;
                    321: }
                    322: 
                    323: /* Translate an sqlite3_file* that is really a quotaConn* into
                    324: ** the sqlite3_file* for the underlying original VFS.
                    325: */
                    326: static sqlite3_file *quotaSubOpen(sqlite3_file *pConn){
                    327:   quotaConn *p = (quotaConn*)pConn;
                    328:   return (sqlite3_file*)&p[1];
                    329: }
                    330: 
                    331: /* Find a file in a quota group and return a pointer to that file.
                    332: ** Return NULL if the file is not in the group.
                    333: */
                    334: static quotaFile *quotaFindFile(
                    335:   quotaGroup *pGroup,     /* Group in which to look for the file */
                    336:   const char *zName,      /* Full pathname of the file */
                    337:   int createFlag          /* Try to create the file if not found */
                    338: ){
                    339:   quotaFile *pFile = pGroup->pFiles;
                    340:   while( pFile && strcmp(pFile->zFilename, zName)!=0 ){
                    341:     pFile = pFile->pNext;
                    342:   }
                    343:   if( pFile==0 && createFlag ){
                    344:     int nName = strlen(zName);
                    345:     pFile = (quotaFile *)sqlite3_malloc( sizeof(*pFile) + nName + 1 );
                    346:     if( pFile ){
                    347:       memset(pFile, 0, sizeof(*pFile));
                    348:       pFile->zFilename = (char*)&pFile[1];
                    349:       memcpy(pFile->zFilename, zName, nName+1);
                    350:       pFile->pNext = pGroup->pFiles;
                    351:       if( pGroup->pFiles ) pGroup->pFiles->ppPrev = &pFile->pNext;
                    352:       pFile->ppPrev = &pGroup->pFiles;
                    353:       pGroup->pFiles = pFile;
                    354:       pFile->pGroup = pGroup;
                    355:     }
                    356:   }
                    357:   return pFile;
                    358: }
                    359: 
                    360: /*
                    361: ** Figure out if we are dealing with Unix, Windows, or some other
                    362: ** operating system.  After the following block of preprocess macros,
                    363: ** all of SQLITE_OS_UNIX, SQLITE_OS_WIN, SQLITE_OS_OS2, and SQLITE_OS_OTHER 
                    364: ** will defined to either 1 or 0.  One of the four will be 1.  The other 
                    365: ** three will be 0.
                    366: */
                    367: #if defined(SQLITE_OS_OTHER)
                    368: # if SQLITE_OS_OTHER==1
                    369: #   undef SQLITE_OS_UNIX
                    370: #   define SQLITE_OS_UNIX 0
                    371: #   undef SQLITE_OS_WIN
                    372: #   define SQLITE_OS_WIN 0
                    373: #   undef SQLITE_OS_OS2
                    374: #   define SQLITE_OS_OS2 0
                    375: # else
                    376: #   undef SQLITE_OS_OTHER
                    377: # endif
                    378: #endif
                    379: #if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER)
                    380: # define SQLITE_OS_OTHER 0
                    381: # ifndef SQLITE_OS_WIN
                    382: #   if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) \
                    383:                        || defined(__MINGW32__) || defined(__BORLANDC__)
                    384: #     define SQLITE_OS_WIN 1
                    385: #     define SQLITE_OS_UNIX 0
                    386: #     define SQLITE_OS_OS2 0
                    387: #   elif defined(__EMX__) || defined(_OS2) || defined(OS2) \
                    388:                           || defined(_OS2_) || defined(__OS2__)
                    389: #     define SQLITE_OS_WIN 0
                    390: #     define SQLITE_OS_UNIX 0
                    391: #     define SQLITE_OS_OS2 1
                    392: #   else
                    393: #     define SQLITE_OS_WIN 0
                    394: #     define SQLITE_OS_UNIX 1
                    395: #     define SQLITE_OS_OS2 0
                    396: #  endif
                    397: # else
                    398: #  define SQLITE_OS_UNIX 0
                    399: #  define SQLITE_OS_OS2 0
                    400: # endif
                    401: #else
                    402: # ifndef SQLITE_OS_WIN
                    403: #  define SQLITE_OS_WIN 0
                    404: # endif
                    405: #endif
                    406: 
                    407: #if SQLITE_OS_UNIX
                    408: # include <unistd.h>
                    409: #endif
                    410: #if SQLITE_OS_WIN
                    411: # include <windows.h>
                    412: # include <io.h>
                    413: #endif
                    414: 
                    415: /*
                    416: ** Translate UTF8 to MBCS for use in fopen() calls.  Return a pointer to the
                    417: ** translated text..  Call quota_mbcs_free() to deallocate any memory
                    418: ** used to store the returned pointer when done.
                    419: */
                    420: static char *quota_utf8_to_mbcs(const char *zUtf8){
                    421: #if SQLITE_OS_WIN
                    422:   int n;             /* Bytes in zUtf8 */
                    423:   int nWide;         /* number of UTF-16 characters */
                    424:   int nMbcs;         /* Bytes of MBCS */
                    425:   LPWSTR zTmpWide;   /* The UTF16 text */
                    426:   char *zMbcs;       /* The MBCS text */
                    427:   int codepage;      /* Code page used by fopen() */
                    428: 
                    429:   n = strlen(zUtf8);
                    430:   nWide = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, NULL, 0);
                    431:   if( nWide==0 ) return 0;
                    432:   zTmpWide = (LPWSTR)sqlite3_malloc( (nWide+1)*sizeof(zTmpWide[0]) );
                    433:   if( zTmpWide==0 ) return 0;
                    434:   MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zTmpWide, nWide);
                    435:   codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
                    436:   nMbcs = WideCharToMultiByte(codepage, 0, zTmpWide, nWide, 0, 0, 0, 0);
                    437:   zMbcs = nMbcs ? (char*)sqlite3_malloc( nMbcs+1 ) : 0;
                    438:   if( zMbcs ){
                    439:     WideCharToMultiByte(codepage, 0, zTmpWide, nWide, zMbcs, nMbcs, 0, 0);
                    440:   }
                    441:   sqlite3_free(zTmpWide);
                    442:   return zMbcs;
                    443: #else
                    444:   return (char*)zUtf8;  /* No-op on unix */
                    445: #endif  
                    446: }
                    447: 
                    448: /*
                    449: ** Deallocate any memory allocated by quota_utf8_to_mbcs().
                    450: */
                    451: static void quota_mbcs_free(char *zOld){
                    452: #if SQLITE_OS_WIN
                    453:   sqlite3_free(zOld);
                    454: #else
                    455:   /* No-op on unix */
                    456: #endif  
                    457: }
                    458: 
                    459: /************************* VFS Method Wrappers *****************************/
                    460: /*
                    461: ** This is the xOpen method used for the "quota" VFS.
                    462: **
                    463: ** Most of the work is done by the underlying original VFS.  This method
                    464: ** simply links the new file into the appropriate quota group if it is a
                    465: ** file that needs to be tracked.
                    466: */
                    467: static int quotaOpen(
                    468:   sqlite3_vfs *pVfs,          /* The quota VFS */
                    469:   const char *zName,          /* Name of file to be opened */
                    470:   sqlite3_file *pConn,        /* Fill in this file descriptor */
                    471:   int flags,                  /* Flags to control the opening */
                    472:   int *pOutFlags              /* Flags showing results of opening */
                    473: ){
                    474:   int rc;                                    /* Result code */         
                    475:   quotaConn *pQuotaOpen;                     /* The new quota file descriptor */
                    476:   quotaFile *pFile;                          /* Corresponding quotaFile obj */
                    477:   quotaGroup *pGroup;                        /* The group file belongs to */
                    478:   sqlite3_file *pSubOpen;                    /* Real file descriptor */
                    479:   sqlite3_vfs *pOrigVfs = gQuota.pOrigVfs;   /* Real VFS */
                    480: 
                    481:   /* If the file is not a main database file or a WAL, then use the
                    482:   ** normal xOpen method.
                    483:   */
                    484:   if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_WAL))==0 ){
                    485:     return pOrigVfs->xOpen(pOrigVfs, zName, pConn, flags, pOutFlags);
                    486:   }
                    487: 
                    488:   /* If the name of the file does not match any quota group, then
                    489:   ** use the normal xOpen method.
                    490:   */
                    491:   quotaEnter();
                    492:   pGroup = quotaGroupFind(zName);
                    493:   if( pGroup==0 ){
                    494:     rc = pOrigVfs->xOpen(pOrigVfs, zName, pConn, flags, pOutFlags);
                    495:   }else{
                    496:     /* If we get to this point, it means the file needs to be quota tracked.
                    497:     */
                    498:     pQuotaOpen = (quotaConn*)pConn;
                    499:     pSubOpen = quotaSubOpen(pConn);
                    500:     rc = pOrigVfs->xOpen(pOrigVfs, zName, pSubOpen, flags, pOutFlags);
                    501:     if( rc==SQLITE_OK ){
                    502:       pFile = quotaFindFile(pGroup, zName, 1);
                    503:       if( pFile==0 ){
                    504:         quotaLeave();
                    505:         pSubOpen->pMethods->xClose(pSubOpen);
                    506:         return SQLITE_NOMEM;
                    507:       }
                    508:       pFile->deleteOnClose = (flags & SQLITE_OPEN_DELETEONCLOSE)!=0;
                    509:       pFile->nRef++;
                    510:       pQuotaOpen->pFile = pFile;
                    511:       if( pSubOpen->pMethods->iVersion==1 ){
                    512:         pQuotaOpen->base.pMethods = &gQuota.sIoMethodsV1;
                    513:       }else{
                    514:         pQuotaOpen->base.pMethods = &gQuota.sIoMethodsV2;
                    515:       }
                    516:     }
                    517:   }
                    518:   quotaLeave();
                    519:   return rc;
                    520: }
                    521: 
                    522: /*
                    523: ** This is the xDelete method used for the "quota" VFS.
                    524: **
                    525: ** If the file being deleted is part of the quota group, then reduce
                    526: ** the size of the quota group accordingly.  And remove the file from
                    527: ** the set of files in the quota group.
                    528: */
                    529: static int quotaDelete(
                    530:   sqlite3_vfs *pVfs,          /* The quota VFS */
                    531:   const char *zName,          /* Name of file to be deleted */
                    532:   int syncDir                 /* Do a directory sync after deleting */
                    533: ){
                    534:   int rc;                                    /* Result code */         
                    535:   quotaFile *pFile;                          /* Files in the quota */
                    536:   quotaGroup *pGroup;                        /* The group file belongs to */
                    537:   sqlite3_vfs *pOrigVfs = gQuota.pOrigVfs;   /* Real VFS */
                    538: 
                    539:   /* Do the actual file delete */
                    540:   rc = pOrigVfs->xDelete(pOrigVfs, zName, syncDir);
                    541: 
                    542:   /* If the file just deleted is a member of a quota group, then remove
                    543:   ** it from that quota group.
                    544:   */
                    545:   if( rc==SQLITE_OK ){
                    546:     quotaEnter();
                    547:     pGroup = quotaGroupFind(zName);
                    548:     if( pGroup ){
                    549:       pFile = quotaFindFile(pGroup, zName, 0);
                    550:       if( pFile ){
                    551:         if( pFile->nRef ){
                    552:           pFile->deleteOnClose = 1;
                    553:         }else{
                    554:           quotaRemoveFile(pFile);
                    555:           quotaGroupDeref(pGroup);
                    556:         }
                    557:       }
                    558:     }
                    559:     quotaLeave();
                    560:   }
                    561:   return rc;
                    562: }
                    563: 
                    564: 
                    565: /************************ I/O Method Wrappers *******************************/
                    566: 
                    567: /* xClose requests get passed through to the original VFS.  But we
                    568: ** also have to unlink the quotaConn from the quotaFile and quotaGroup.
                    569: ** The quotaFile and/or quotaGroup are freed if they are no longer in use.
                    570: */
                    571: static int quotaClose(sqlite3_file *pConn){
                    572:   quotaConn *p = (quotaConn*)pConn;
                    573:   quotaFile *pFile = p->pFile;
                    574:   sqlite3_file *pSubOpen = quotaSubOpen(pConn);
                    575:   int rc;
                    576:   rc = pSubOpen->pMethods->xClose(pSubOpen);
                    577:   quotaEnter();
                    578:   pFile->nRef--;
                    579:   if( pFile->nRef==0 ){
                    580:     quotaGroup *pGroup = pFile->pGroup;
                    581:     if( pFile->deleteOnClose ){
                    582:       gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0);
                    583:       quotaRemoveFile(pFile);
                    584:     }
                    585:     quotaGroupDeref(pGroup);
                    586:   }
                    587:   quotaLeave();
                    588:   return rc;
                    589: }
                    590: 
                    591: /* Pass xRead requests directory thru to the original VFS without
                    592: ** further processing.
                    593: */
                    594: static int quotaRead(
                    595:   sqlite3_file *pConn,
                    596:   void *pBuf,
                    597:   int iAmt,
                    598:   sqlite3_int64 iOfst
                    599: ){
                    600:   sqlite3_file *pSubOpen = quotaSubOpen(pConn);
                    601:   return pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst);
                    602: }
                    603: 
                    604: /* Check xWrite requests to see if they expand the file.  If they do,
                    605: ** the perform a quota check before passing them through to the
                    606: ** original VFS.
                    607: */
                    608: static int quotaWrite(
                    609:   sqlite3_file *pConn,
                    610:   const void *pBuf,
                    611:   int iAmt,
                    612:   sqlite3_int64 iOfst
                    613: ){
                    614:   quotaConn *p = (quotaConn*)pConn;
                    615:   sqlite3_file *pSubOpen = quotaSubOpen(pConn);
                    616:   sqlite3_int64 iEnd = iOfst+iAmt;
                    617:   quotaGroup *pGroup;
                    618:   quotaFile *pFile = p->pFile;
                    619:   sqlite3_int64 szNew;
                    620: 
                    621:   if( pFile->iSize<iEnd ){
                    622:     pGroup = pFile->pGroup;
                    623:     quotaEnter();
                    624:     szNew = pGroup->iSize - pFile->iSize + iEnd;
                    625:     if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
                    626:       if( pGroup->xCallback ){
                    627:         pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew, 
                    628:                           pGroup->pArg);
                    629:       }
                    630:       if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
                    631:         quotaLeave();
                    632:         return SQLITE_FULL;
                    633:       }
                    634:     }
                    635:     pGroup->iSize = szNew;
                    636:     pFile->iSize = iEnd;
                    637:     quotaLeave();
                    638:   }
                    639:   return pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst);
                    640: }
                    641: 
                    642: /* Pass xTruncate requests thru to the original VFS.  If the
                    643: ** success, update the file size.
                    644: */
                    645: static int quotaTruncate(sqlite3_file *pConn, sqlite3_int64 size){
                    646:   quotaConn *p = (quotaConn*)pConn;
                    647:   sqlite3_file *pSubOpen = quotaSubOpen(pConn);
                    648:   int rc = pSubOpen->pMethods->xTruncate(pSubOpen, size);
                    649:   quotaFile *pFile = p->pFile;
                    650:   quotaGroup *pGroup;
                    651:   if( rc==SQLITE_OK ){
                    652:     quotaEnter();
                    653:     pGroup = pFile->pGroup;
                    654:     pGroup->iSize -= pFile->iSize;
                    655:     pFile->iSize = size;
                    656:     pGroup->iSize += size;
                    657:     quotaLeave();
                    658:   }
                    659:   return rc;
                    660: }
                    661: 
                    662: /* Pass xSync requests through to the original VFS without change
                    663: */
                    664: static int quotaSync(sqlite3_file *pConn, int flags){
                    665:   sqlite3_file *pSubOpen = quotaSubOpen(pConn);
                    666:   return pSubOpen->pMethods->xSync(pSubOpen, flags);
                    667: }
                    668: 
                    669: /* Pass xFileSize requests through to the original VFS but then
                    670: ** update the quotaGroup with the new size before returning.
                    671: */
                    672: static int quotaFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
                    673:   quotaConn *p = (quotaConn*)pConn;
                    674:   sqlite3_file *pSubOpen = quotaSubOpen(pConn);
                    675:   quotaFile *pFile = p->pFile;
                    676:   quotaGroup *pGroup;
                    677:   sqlite3_int64 sz;
                    678:   int rc;
                    679: 
                    680:   rc = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
                    681:   if( rc==SQLITE_OK ){
                    682:     quotaEnter();
                    683:     pGroup = pFile->pGroup;
                    684:     pGroup->iSize -= pFile->iSize;
                    685:     pFile->iSize = sz;
                    686:     pGroup->iSize += sz;
                    687:     quotaLeave();
                    688:     *pSize = sz;
                    689:   }
                    690:   return rc;
                    691: }
                    692: 
                    693: /* Pass xLock requests through to the original VFS unchanged.
                    694: */
                    695: static int quotaLock(sqlite3_file *pConn, int lock){
                    696:   sqlite3_file *pSubOpen = quotaSubOpen(pConn);
                    697:   return pSubOpen->pMethods->xLock(pSubOpen, lock);
                    698: }
                    699: 
                    700: /* Pass xUnlock requests through to the original VFS unchanged.
                    701: */
                    702: static int quotaUnlock(sqlite3_file *pConn, int lock){
                    703:   sqlite3_file *pSubOpen = quotaSubOpen(pConn);
                    704:   return pSubOpen->pMethods->xUnlock(pSubOpen, lock);
                    705: }
                    706: 
                    707: /* Pass xCheckReservedLock requests through to the original VFS unchanged.
                    708: */
                    709: static int quotaCheckReservedLock(sqlite3_file *pConn, int *pResOut){
                    710:   sqlite3_file *pSubOpen = quotaSubOpen(pConn);
                    711:   return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
                    712: }
                    713: 
                    714: /* Pass xFileControl requests through to the original VFS unchanged.
                    715: */
                    716: static int quotaFileControl(sqlite3_file *pConn, int op, void *pArg){
                    717:   sqlite3_file *pSubOpen = quotaSubOpen(pConn);
                    718:   int rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
                    719: #if defined(SQLITE_FCNTL_VFSNAME)
                    720:   if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
                    721:     *(char**)pArg = sqlite3_mprintf("quota/%z", *(char**)pArg);
                    722:   }
                    723: #endif
                    724:   return rc;
                    725: }
                    726: 
                    727: /* Pass xSectorSize requests through to the original VFS unchanged.
                    728: */
                    729: static int quotaSectorSize(sqlite3_file *pConn){
                    730:   sqlite3_file *pSubOpen = quotaSubOpen(pConn);
                    731:   return pSubOpen->pMethods->xSectorSize(pSubOpen);
                    732: }
                    733: 
                    734: /* Pass xDeviceCharacteristics requests through to the original VFS unchanged.
                    735: */
                    736: static int quotaDeviceCharacteristics(sqlite3_file *pConn){
                    737:   sqlite3_file *pSubOpen = quotaSubOpen(pConn);
                    738:   return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen);
                    739: }
                    740: 
                    741: /* Pass xShmMap requests through to the original VFS unchanged.
                    742: */
                    743: static int quotaShmMap(
                    744:   sqlite3_file *pConn,            /* Handle open on database file */
                    745:   int iRegion,                    /* Region to retrieve */
                    746:   int szRegion,                   /* Size of regions */
                    747:   int bExtend,                    /* True to extend file if necessary */
                    748:   void volatile **pp              /* OUT: Mapped memory */
                    749: ){
                    750:   sqlite3_file *pSubOpen = quotaSubOpen(pConn);
                    751:   return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend, pp);
                    752: }
                    753: 
                    754: /* Pass xShmLock requests through to the original VFS unchanged.
                    755: */
                    756: static int quotaShmLock(
                    757:   sqlite3_file *pConn,       /* Database file holding the shared memory */
                    758:   int ofst,                  /* First lock to acquire or release */
                    759:   int n,                     /* Number of locks to acquire or release */
                    760:   int flags                  /* What to do with the lock */
                    761: ){
                    762:   sqlite3_file *pSubOpen = quotaSubOpen(pConn);
                    763:   return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags);
                    764: }
                    765: 
                    766: /* Pass xShmBarrier requests through to the original VFS unchanged.
                    767: */
                    768: static void quotaShmBarrier(sqlite3_file *pConn){
                    769:   sqlite3_file *pSubOpen = quotaSubOpen(pConn);
                    770:   pSubOpen->pMethods->xShmBarrier(pSubOpen);
                    771: }
                    772: 
                    773: /* Pass xShmUnmap requests through to the original VFS unchanged.
                    774: */
                    775: static int quotaShmUnmap(sqlite3_file *pConn, int deleteFlag){
                    776:   sqlite3_file *pSubOpen = quotaSubOpen(pConn);
                    777:   return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag);
                    778: }
                    779: 
                    780: /************************** Public Interfaces *****************************/
                    781: /*
                    782: ** Initialize the quota VFS shim.  Use the VFS named zOrigVfsName
                    783: ** as the VFS that does the actual work.  Use the default if
                    784: ** zOrigVfsName==NULL.  
                    785: **
                    786: ** The quota VFS shim is named "quota".  It will become the default
                    787: ** VFS if makeDefault is non-zero.
                    788: **
                    789: ** THIS ROUTINE IS NOT THREADSAFE.  Call this routine exactly once
                    790: ** during start-up.
                    791: */
                    792: int sqlite3_quota_initialize(const char *zOrigVfsName, int makeDefault){
                    793:   sqlite3_vfs *pOrigVfs;
                    794:   if( gQuota.isInitialized ) return SQLITE_MISUSE;
                    795:   pOrigVfs = sqlite3_vfs_find(zOrigVfsName);
                    796:   if( pOrigVfs==0 ) return SQLITE_ERROR;
                    797:   assert( pOrigVfs!=&gQuota.sThisVfs );
                    798:   gQuota.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
                    799:   if( !gQuota.pMutex ){
                    800:     return SQLITE_NOMEM;
                    801:   }
                    802:   gQuota.isInitialized = 1;
                    803:   gQuota.pOrigVfs = pOrigVfs;
                    804:   gQuota.sThisVfs = *pOrigVfs;
                    805:   gQuota.sThisVfs.xOpen = quotaOpen;
                    806:   gQuota.sThisVfs.xDelete = quotaDelete;
                    807:   gQuota.sThisVfs.szOsFile += sizeof(quotaConn);
                    808:   gQuota.sThisVfs.zName = "quota";
                    809:   gQuota.sIoMethodsV1.iVersion = 1;
                    810:   gQuota.sIoMethodsV1.xClose = quotaClose;
                    811:   gQuota.sIoMethodsV1.xRead = quotaRead;
                    812:   gQuota.sIoMethodsV1.xWrite = quotaWrite;
                    813:   gQuota.sIoMethodsV1.xTruncate = quotaTruncate;
                    814:   gQuota.sIoMethodsV1.xSync = quotaSync;
                    815:   gQuota.sIoMethodsV1.xFileSize = quotaFileSize;
                    816:   gQuota.sIoMethodsV1.xLock = quotaLock;
                    817:   gQuota.sIoMethodsV1.xUnlock = quotaUnlock;
                    818:   gQuota.sIoMethodsV1.xCheckReservedLock = quotaCheckReservedLock;
                    819:   gQuota.sIoMethodsV1.xFileControl = quotaFileControl;
                    820:   gQuota.sIoMethodsV1.xSectorSize = quotaSectorSize;
                    821:   gQuota.sIoMethodsV1.xDeviceCharacteristics = quotaDeviceCharacteristics;
                    822:   gQuota.sIoMethodsV2 = gQuota.sIoMethodsV1;
                    823:   gQuota.sIoMethodsV2.iVersion = 2;
                    824:   gQuota.sIoMethodsV2.xShmMap = quotaShmMap;
                    825:   gQuota.sIoMethodsV2.xShmLock = quotaShmLock;
                    826:   gQuota.sIoMethodsV2.xShmBarrier = quotaShmBarrier;
                    827:   gQuota.sIoMethodsV2.xShmUnmap = quotaShmUnmap;
                    828:   sqlite3_vfs_register(&gQuota.sThisVfs, makeDefault);
                    829:   return SQLITE_OK;
                    830: }
                    831: 
                    832: /*
                    833: ** Shutdown the quota system.
                    834: **
                    835: ** All SQLite database connections must be closed before calling this
                    836: ** routine.
                    837: **
                    838: ** THIS ROUTINE IS NOT THREADSAFE.  Call this routine exactly once while
                    839: ** shutting down in order to free all remaining quota groups.
                    840: */
                    841: int sqlite3_quota_shutdown(void){
                    842:   quotaGroup *pGroup;
                    843:   if( gQuota.isInitialized==0 ) return SQLITE_MISUSE;
                    844:   for(pGroup=gQuota.pGroup; pGroup; pGroup=pGroup->pNext){
                    845:     if( quotaGroupOpenFileCount(pGroup)>0 ) return SQLITE_MISUSE;
                    846:   }
                    847:   while( gQuota.pGroup ){
                    848:     pGroup = gQuota.pGroup;
                    849:     gQuota.pGroup = pGroup->pNext;
                    850:     pGroup->iLimit = 0;
                    851:     assert( quotaGroupOpenFileCount(pGroup)==0 );
                    852:     quotaGroupDeref(pGroup);
                    853:   }
                    854:   gQuota.isInitialized = 0;
                    855:   sqlite3_mutex_free(gQuota.pMutex);
                    856:   sqlite3_vfs_unregister(&gQuota.sThisVfs);
                    857:   memset(&gQuota, 0, sizeof(gQuota));
                    858:   return SQLITE_OK;
                    859: }
                    860: 
                    861: /*
                    862: ** Create or destroy a quota group.
                    863: **
                    864: ** The quota group is defined by the zPattern.  When calling this routine
                    865: ** with a zPattern for a quota group that already exists, this routine
                    866: ** merely updates the iLimit, xCallback, and pArg values for that quota
                    867: ** group.  If zPattern is new, then a new quota group is created.
                    868: **
                    869: ** If the iLimit for a quota group is set to zero, then the quota group
                    870: ** is disabled and will be deleted when the last database connection using
                    871: ** the quota group is closed.
                    872: **
                    873: ** Calling this routine on a zPattern that does not exist and with a
                    874: ** zero iLimit is a no-op.
                    875: **
                    876: ** A quota group must exist with a non-zero iLimit prior to opening
                    877: ** database connections if those connections are to participate in the
                    878: ** quota group.  Creating a quota group does not affect database connections
                    879: ** that are already open.
                    880: */
                    881: int sqlite3_quota_set(
                    882:   const char *zPattern,           /* The filename pattern */
                    883:   sqlite3_int64 iLimit,           /* New quota to set for this quota group */
                    884:   void (*xCallback)(              /* Callback invoked when going over quota */
                    885:      const char *zFilename,         /* Name of file whose size increases */
                    886:      sqlite3_int64 *piLimit,        /* IN/OUT: The current limit */
                    887:      sqlite3_int64 iSize,           /* Total size of all files in the group */
                    888:      void *pArg                     /* Client data */
                    889:   ),
                    890:   void *pArg,                     /* client data passed thru to callback */
                    891:   void (*xDestroy)(void*)         /* Optional destructor for pArg */
                    892: ){
                    893:   quotaGroup *pGroup;
                    894:   quotaEnter();
                    895:   pGroup = gQuota.pGroup;
                    896:   while( pGroup && strcmp(pGroup->zPattern, zPattern)!=0 ){
                    897:     pGroup = pGroup->pNext;
                    898:   }
                    899:   if( pGroup==0 ){
                    900:     int nPattern = strlen(zPattern);
                    901:     if( iLimit<=0 ){
                    902:       quotaLeave();
                    903:       return SQLITE_OK;
                    904:     }
                    905:     pGroup = (quotaGroup *)sqlite3_malloc( sizeof(*pGroup) + nPattern + 1 );
                    906:     if( pGroup==0 ){
                    907:       quotaLeave();
                    908:       return SQLITE_NOMEM;
                    909:     }
                    910:     memset(pGroup, 0, sizeof(*pGroup));
                    911:     pGroup->zPattern = (char*)&pGroup[1];
                    912:     memcpy((char *)pGroup->zPattern, zPattern, nPattern+1);
                    913:     if( gQuota.pGroup ) gQuota.pGroup->ppPrev = &pGroup->pNext;
                    914:     pGroup->pNext = gQuota.pGroup;
                    915:     pGroup->ppPrev = &gQuota.pGroup;
                    916:     gQuota.pGroup = pGroup;
                    917:   }
                    918:   pGroup->iLimit = iLimit;
                    919:   pGroup->xCallback = xCallback;
                    920:   if( pGroup->xDestroy && pGroup->pArg!=pArg ){
                    921:     pGroup->xDestroy(pGroup->pArg);
                    922:   }
                    923:   pGroup->pArg = pArg;
                    924:   pGroup->xDestroy = xDestroy;
                    925:   quotaGroupDeref(pGroup);
                    926:   quotaLeave();
                    927:   return SQLITE_OK;
                    928: }
                    929: 
                    930: /*
                    931: ** Bring the named file under quota management.  Or if it is already under
                    932: ** management, update its size.
                    933: */
                    934: int sqlite3_quota_file(const char *zFilename){
                    935:   char *zFull;
                    936:   sqlite3_file *fd;
                    937:   int rc;
                    938:   int outFlags = 0;
                    939:   sqlite3_int64 iSize;
                    940:   int nAlloc = gQuota.sThisVfs.szOsFile + gQuota.sThisVfs.mxPathname+2;
                    941: 
                    942:   /* Allocate space for a file-handle and the full path for file zFilename */
                    943:   fd = (sqlite3_file *)sqlite3_malloc(nAlloc);
                    944:   if( fd==0 ){
                    945:     rc = SQLITE_NOMEM;
                    946:   }else{
                    947:     zFull = &((char *)fd)[gQuota.sThisVfs.szOsFile];
                    948:     rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
                    949:         gQuota.sThisVfs.mxPathname+1, zFull);
                    950:   }
                    951: 
                    952:   if( rc==SQLITE_OK ){
                    953:     zFull[strlen(zFull)+1] = '\0';
                    954:     rc = quotaOpen(&gQuota.sThisVfs, zFull, fd, 
                    955:                    SQLITE_OPEN_READONLY | SQLITE_OPEN_MAIN_DB, &outFlags);
                    956:     if( rc==SQLITE_OK ){
                    957:       fd->pMethods->xFileSize(fd, &iSize);
                    958:       fd->pMethods->xClose(fd);
                    959:     }else if( rc==SQLITE_CANTOPEN ){
                    960:       quotaGroup *pGroup;
                    961:       quotaFile *pFile;
                    962:       quotaEnter();
                    963:       pGroup = quotaGroupFind(zFull);
                    964:       if( pGroup ){
                    965:         pFile = quotaFindFile(pGroup, zFull, 0);
                    966:         if( pFile ) quotaRemoveFile(pFile);
                    967:       }
                    968:       quotaLeave();
                    969:     }
                    970:   }
                    971: 
                    972:   sqlite3_free(fd);
                    973:   return rc;
                    974: }
                    975: 
                    976: /*
                    977: ** Open a potentially quotaed file for I/O.
                    978: */
                    979: quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode){
                    980:   quota_FILE *p = 0;
                    981:   char *zFull = 0;
                    982:   char *zFullTranslated;
                    983:   int rc;
                    984:   quotaGroup *pGroup;
                    985:   quotaFile *pFile;
                    986: 
                    987:   zFull = (char*)sqlite3_malloc(gQuota.sThisVfs.mxPathname + 1);
                    988:   if( zFull==0 ) return 0;
                    989:   rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
                    990:                                       gQuota.sThisVfs.mxPathname+1, zFull);
                    991:   if( rc ) goto quota_fopen_error;
                    992:   p = (quota_FILE*)sqlite3_malloc(sizeof(*p));
                    993:   if( p==0 ) goto quota_fopen_error;
                    994:   memset(p, 0, sizeof(*p));
                    995:   zFullTranslated = quota_utf8_to_mbcs(zFull);
                    996:   if( zFullTranslated==0 ) goto quota_fopen_error;
                    997:   p->f = fopen(zFullTranslated, zMode);
                    998:   quota_mbcs_free(zFullTranslated);
                    999:   if( p->f==0 ) goto quota_fopen_error;
                   1000:   quotaEnter();
                   1001:   pGroup = quotaGroupFind(zFull);
                   1002:   if( pGroup ){
                   1003:     pFile = quotaFindFile(pGroup, zFull, 1);
                   1004:     if( pFile==0 ){
                   1005:       quotaLeave();
                   1006:       goto quota_fopen_error;
                   1007:     }
                   1008:     pFile->nRef++;
                   1009:     p->pFile = pFile;
                   1010:   }
                   1011:   quotaLeave();
                   1012:   sqlite3_free(zFull);
                   1013:   return p;
                   1014: 
                   1015: quota_fopen_error:
                   1016:   sqlite3_free(zFull);
                   1017:   if( p && p->f ) fclose(p->f);
                   1018:   sqlite3_free(p);
                   1019:   return 0;
                   1020: }
                   1021: 
                   1022: /*
                   1023: ** Read content from a quota_FILE
                   1024: */
                   1025: size_t sqlite3_quota_fread(
                   1026:   void *pBuf,            /* Store the content here */
                   1027:   size_t size,           /* Size of each element */
                   1028:   size_t nmemb,          /* Number of elements to read */
                   1029:   quota_FILE *p          /* Read from this quota_FILE object */
                   1030: ){
                   1031:   return fread(pBuf, size, nmemb, p->f);
                   1032: }
                   1033: 
                   1034: /*
                   1035: ** Write content into a quota_FILE.  Invoke the quota callback and block
                   1036: ** the write if we exceed quota.
                   1037: */
                   1038: size_t sqlite3_quota_fwrite(
                   1039:   void *pBuf,            /* Take content to write from here */
                   1040:   size_t size,           /* Size of each element */
                   1041:   size_t nmemb,          /* Number of elements */
                   1042:   quota_FILE *p          /* Write to this quota_FILE objecct */
                   1043: ){
                   1044:   sqlite3_int64 iOfst;
                   1045:   sqlite3_int64 iEnd;
                   1046:   sqlite3_int64 szNew;
                   1047:   quotaFile *pFile;
                   1048:   
                   1049:   iOfst = ftell(p->f);
                   1050:   iEnd = iOfst + size*nmemb;
                   1051:   pFile = p->pFile;
                   1052:   if( pFile && pFile->iSize<iEnd ){
                   1053:     quotaGroup *pGroup = pFile->pGroup;
                   1054:     quotaEnter();
                   1055:     szNew = pGroup->iSize - pFile->iSize + iEnd;
                   1056:     if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
                   1057:       if( pGroup->xCallback ){
                   1058:         pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew, 
                   1059:                           pGroup->pArg);
                   1060:       }
                   1061:       if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
                   1062:         iEnd = pGroup->iLimit - pGroup->iSize + pFile->iSize;
                   1063:         nmemb = (iEnd - iOfst)/size;
                   1064:         iEnd = iOfst + size*nmemb;
                   1065:         szNew = pGroup->iSize - pFile->iSize + iEnd;
                   1066:       }
                   1067:     }
                   1068:     pGroup->iSize = szNew;
                   1069:     pFile->iSize = iEnd;
                   1070:     quotaLeave();
                   1071:   }
                   1072:   return fwrite(pBuf, size, nmemb, p->f);
                   1073: }
                   1074: 
                   1075: /*
                   1076: ** Close an open quota_FILE stream.
                   1077: */
                   1078: int sqlite3_quota_fclose(quota_FILE *p){
                   1079:   int rc;
                   1080:   quotaFile *pFile;
                   1081:   rc = fclose(p->f);
                   1082:   pFile = p->pFile;
                   1083:   if( pFile ){
                   1084:     quotaEnter();
                   1085:     pFile->nRef--;
                   1086:     if( pFile->nRef==0 ){
                   1087:       quotaGroup *pGroup = pFile->pGroup;
                   1088:       if( pFile->deleteOnClose ){
                   1089:         gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0);
                   1090:         quotaRemoveFile(pFile);
                   1091:       }
                   1092:       quotaGroupDeref(pGroup);
                   1093:     }
                   1094:     quotaLeave();
                   1095:   }
                   1096:   sqlite3_free(p);
                   1097:   return rc;
                   1098: }
                   1099: 
                   1100: /*
                   1101: ** Flush memory buffers for a quota_FILE to disk.
                   1102: */
                   1103: int sqlite3_quota_fflush(quota_FILE *p, int doFsync){
                   1104:   int rc;
                   1105:   rc = fflush(p->f);
                   1106:   if( rc==0 && doFsync ){
                   1107: #if SQLITE_OS_UNIX
                   1108:     rc = fsync(fileno(p->f));
                   1109: #endif
                   1110: #if SQLITE_OS_WIN
                   1111:     rc = _commit(_fileno(p->f));
                   1112: #endif
                   1113:   }
                   1114:   return rc!=0;
                   1115: }
                   1116: 
                   1117: /*
                   1118: ** Seek on a quota_FILE stream.
                   1119: */
                   1120: int sqlite3_quota_fseek(quota_FILE *p, long offset, int whence){
                   1121:   return fseek(p->f, offset, whence);
                   1122: }
                   1123: 
                   1124: /*
                   1125: ** rewind a quota_FILE stream.
                   1126: */
                   1127: void sqlite3_quota_rewind(quota_FILE *p){
                   1128:   rewind(p->f);
                   1129: }
                   1130: 
                   1131: /*
                   1132: ** Tell the current location of a quota_FILE stream.
                   1133: */
                   1134: long sqlite3_quota_ftell(quota_FILE *p){
                   1135:   return ftell(p->f);
                   1136: }
                   1137: 
                   1138: /*
                   1139: ** Remove a managed file.  Update quotas accordingly.
                   1140: */
                   1141: int sqlite3_quota_remove(const char *zFilename){
                   1142:   char *zFull;            /* Full pathname for zFilename */
                   1143:   int nFull;              /* Number of bytes in zFilename */
                   1144:   int rc;                 /* Result code */
                   1145:   quotaGroup *pGroup;     /* Group containing zFilename */
                   1146:   quotaFile *pFile;       /* A file in the group */
                   1147:   quotaFile *pNextFile;   /* next file in the group */
                   1148:   int diff;               /* Difference between filenames */
                   1149:   char c;                 /* First character past end of pattern */
                   1150: 
                   1151:   zFull = (char*)sqlite3_malloc(gQuota.sThisVfs.mxPathname + 1);
                   1152:   if( zFull==0 ) return SQLITE_NOMEM;
                   1153:   rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
                   1154:                                       gQuota.sThisVfs.mxPathname+1, zFull);
                   1155:   if( rc ){
                   1156:     sqlite3_free(zFull);
                   1157:     return rc;
                   1158:   }
                   1159: 
                   1160:   /* Figure out the length of the full pathname.  If the name ends with
                   1161:   ** / (or \ on windows) then remove the trailing /.
                   1162:   */
                   1163:   nFull = strlen(zFull);
                   1164:   if( nFull>0 && (zFull[nFull-1]=='/' || zFull[nFull-1]=='\\') ){
                   1165:     nFull--;
                   1166:     zFull[nFull] = 0;
                   1167:   }
                   1168: 
                   1169:   quotaEnter();
                   1170:   pGroup = quotaGroupFind(zFull);
                   1171:   if( pGroup ){
                   1172:     for(pFile=pGroup->pFiles; pFile && rc==SQLITE_OK; pFile=pNextFile){
                   1173:       pNextFile = pFile->pNext;
                   1174:       diff = memcmp(zFull, pFile->zFilename, nFull);
                   1175:       if( diff==0 && ((c = pFile->zFilename[nFull])==0 || c=='/' || c=='\\') ){
                   1176:         if( pFile->nRef ){
                   1177:           pFile->deleteOnClose = 1;
                   1178:         }else{
                   1179:           rc = gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0);
                   1180:           quotaRemoveFile(pFile);
                   1181:           quotaGroupDeref(pGroup);
                   1182:         }
                   1183:       }
                   1184:     }
                   1185:   }
                   1186:   quotaLeave();
                   1187:   sqlite3_free(zFull);
                   1188:   return rc;
                   1189: }
                   1190:   
                   1191: /***************************** Test Code ***********************************/
                   1192: #ifdef SQLITE_TEST
                   1193: #include <tcl.h>
                   1194: 
                   1195: /*
                   1196: ** Argument passed to a TCL quota-over-limit callback.
                   1197: */
                   1198: typedef struct TclQuotaCallback TclQuotaCallback;
                   1199: struct TclQuotaCallback {
                   1200:   Tcl_Interp *interp;    /* Interpreter in which to run the script */
                   1201:   Tcl_Obj *pScript;      /* Script to be run */
                   1202: };
                   1203: 
                   1204: extern const char *sqlite3TestErrorName(int);
                   1205: 
                   1206: 
                   1207: /*
                   1208: ** This is the callback from a quota-over-limit.
                   1209: */
                   1210: static void tclQuotaCallback(
                   1211:   const char *zFilename,          /* Name of file whose size increases */
                   1212:   sqlite3_int64 *piLimit,         /* IN/OUT: The current limit */
                   1213:   sqlite3_int64 iSize,            /* Total size of all files in the group */
                   1214:   void *pArg                      /* Client data */
                   1215: ){
                   1216:   TclQuotaCallback *p;            /* Callback script object */
                   1217:   Tcl_Obj *pEval;                 /* Script to evaluate */
                   1218:   Tcl_Obj *pVarname;              /* Name of variable to pass as 2nd arg */
                   1219:   unsigned int rnd;               /* Random part of pVarname */
                   1220:   int rc;                         /* Tcl error code */
                   1221: 
                   1222:   p = (TclQuotaCallback *)pArg;
                   1223:   if( p==0 ) return;
                   1224: 
                   1225:   pVarname = Tcl_NewStringObj("::piLimit_", -1);
                   1226:   Tcl_IncrRefCount(pVarname);
                   1227:   sqlite3_randomness(sizeof(rnd), (void *)&rnd);
                   1228:   Tcl_AppendObjToObj(pVarname, Tcl_NewIntObj((int)(rnd&0x7FFFFFFF)));
                   1229:   Tcl_ObjSetVar2(p->interp, pVarname, 0, Tcl_NewWideIntObj(*piLimit), 0);
                   1230: 
                   1231:   pEval = Tcl_DuplicateObj(p->pScript);
                   1232:   Tcl_IncrRefCount(pEval);
                   1233:   Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj(zFilename, -1));
                   1234:   Tcl_ListObjAppendElement(0, pEval, pVarname);
                   1235:   Tcl_ListObjAppendElement(0, pEval, Tcl_NewWideIntObj(iSize));
                   1236:   rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
                   1237: 
                   1238:   if( rc==TCL_OK ){
                   1239:     Tcl_Obj *pLimit = Tcl_ObjGetVar2(p->interp, pVarname, 0, 0);
                   1240:     rc = Tcl_GetWideIntFromObj(p->interp, pLimit, piLimit);
                   1241:     Tcl_UnsetVar(p->interp, Tcl_GetString(pVarname), 0);
                   1242:   }
                   1243: 
                   1244:   Tcl_DecrRefCount(pEval);
                   1245:   Tcl_DecrRefCount(pVarname);
                   1246:   if( rc!=TCL_OK ) Tcl_BackgroundError(p->interp);
                   1247: }
                   1248: 
                   1249: /*
                   1250: ** Destructor for a TCL quota-over-limit callback.
                   1251: */
                   1252: static void tclCallbackDestructor(void *pObj){
                   1253:   TclQuotaCallback *p = (TclQuotaCallback*)pObj;
                   1254:   if( p ){
                   1255:     Tcl_DecrRefCount(p->pScript);
                   1256:     sqlite3_free((char *)p);
                   1257:   }
                   1258: }
                   1259: 
                   1260: /*
                   1261: ** tclcmd: sqlite3_quota_initialize NAME MAKEDEFAULT
                   1262: */
                   1263: static int test_quota_initialize(
                   1264:   void * clientData,
                   1265:   Tcl_Interp *interp,
                   1266:   int objc,
                   1267:   Tcl_Obj *CONST objv[]
                   1268: ){
                   1269:   const char *zName;              /* Name of new quota VFS */
                   1270:   int makeDefault;                /* True to make the new VFS the default */
                   1271:   int rc;                         /* Value returned by quota_initialize() */
                   1272: 
                   1273:   /* Process arguments */
                   1274:   if( objc!=3 ){
                   1275:     Tcl_WrongNumArgs(interp, 1, objv, "NAME MAKEDEFAULT");
                   1276:     return TCL_ERROR;
                   1277:   }
                   1278:   zName = Tcl_GetString(objv[1]);
                   1279:   if( Tcl_GetBooleanFromObj(interp, objv[2], &makeDefault) ) return TCL_ERROR;
                   1280:   if( zName[0]=='\0' ) zName = 0;
                   1281: 
                   1282:   /* Call sqlite3_quota_initialize() */
                   1283:   rc = sqlite3_quota_initialize(zName, makeDefault);
                   1284:   Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
                   1285: 
                   1286:   return TCL_OK;
                   1287: }
                   1288: 
                   1289: /*
                   1290: ** tclcmd: sqlite3_quota_shutdown
                   1291: */
                   1292: static int test_quota_shutdown(
                   1293:   void * clientData,
                   1294:   Tcl_Interp *interp,
                   1295:   int objc,
                   1296:   Tcl_Obj *CONST objv[]
                   1297: ){
                   1298:   int rc;                         /* Value returned by quota_shutdown() */
                   1299: 
                   1300:   if( objc!=1 ){
                   1301:     Tcl_WrongNumArgs(interp, 1, objv, "");
                   1302:     return TCL_ERROR;
                   1303:   }
                   1304: 
                   1305:   /* Call sqlite3_quota_shutdown() */
                   1306:   rc = sqlite3_quota_shutdown();
                   1307:   Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
                   1308: 
                   1309:   return TCL_OK;
                   1310: }
                   1311: 
                   1312: /*
                   1313: ** tclcmd: sqlite3_quota_set PATTERN LIMIT SCRIPT
                   1314: */
                   1315: static int test_quota_set(
                   1316:   void * clientData,
                   1317:   Tcl_Interp *interp,
                   1318:   int objc,
                   1319:   Tcl_Obj *CONST objv[]
                   1320: ){
                   1321:   const char *zPattern;           /* File pattern to configure */
                   1322:   sqlite3_int64 iLimit;           /* Initial quota in bytes */
                   1323:   Tcl_Obj *pScript;               /* Tcl script to invoke to increase quota */
                   1324:   int rc;                         /* Value returned by quota_set() */
                   1325:   TclQuotaCallback *p;            /* Callback object */
                   1326:   int nScript;                    /* Length of callback script */
                   1327:   void (*xDestroy)(void*);        /* Optional destructor for pArg */
                   1328:   void (*xCallback)(const char *, sqlite3_int64 *, sqlite3_int64, void *);
                   1329: 
                   1330:   /* Process arguments */
                   1331:   if( objc!=4 ){
                   1332:     Tcl_WrongNumArgs(interp, 1, objv, "PATTERN LIMIT SCRIPT");
                   1333:     return TCL_ERROR;
                   1334:   }
                   1335:   zPattern = Tcl_GetString(objv[1]);
                   1336:   if( Tcl_GetWideIntFromObj(interp, objv[2], &iLimit) ) return TCL_ERROR;
                   1337:   pScript = objv[3];
                   1338:   Tcl_GetStringFromObj(pScript, &nScript);
                   1339: 
                   1340:   if( nScript>0 ){
                   1341:     /* Allocate a TclQuotaCallback object */
                   1342:     p = (TclQuotaCallback *)sqlite3_malloc(sizeof(TclQuotaCallback));
                   1343:     if( !p ){
                   1344:       Tcl_SetResult(interp, (char *)"SQLITE_NOMEM", TCL_STATIC);
                   1345:       return TCL_OK;
                   1346:     }
                   1347:     memset(p, 0, sizeof(TclQuotaCallback));
                   1348:     p->interp = interp;
                   1349:     Tcl_IncrRefCount(pScript);
                   1350:     p->pScript = pScript;
                   1351:     xDestroy = tclCallbackDestructor;
                   1352:     xCallback = tclQuotaCallback;
                   1353:   }else{
                   1354:     p = 0;
                   1355:     xDestroy = 0;
                   1356:     xCallback = 0;
                   1357:   }
                   1358: 
                   1359:   /* Invoke sqlite3_quota_set() */
                   1360:   rc = sqlite3_quota_set(zPattern, iLimit, xCallback, (void*)p, xDestroy);
                   1361: 
                   1362:   Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
                   1363:   return TCL_OK;
                   1364: }
                   1365: 
                   1366: /*
                   1367: ** tclcmd: sqlite3_quota_file FILENAME
                   1368: */
                   1369: static int test_quota_file(
                   1370:   void * clientData,
                   1371:   Tcl_Interp *interp,
                   1372:   int objc,
                   1373:   Tcl_Obj *CONST objv[]
                   1374: ){
                   1375:   const char *zFilename;          /* File pattern to configure */
                   1376:   int rc;                         /* Value returned by quota_file() */
                   1377: 
                   1378:   /* Process arguments */
                   1379:   if( objc!=2 ){
                   1380:     Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
                   1381:     return TCL_ERROR;
                   1382:   }
                   1383:   zFilename = Tcl_GetString(objv[1]);
                   1384: 
                   1385:   /* Invoke sqlite3_quota_file() */
                   1386:   rc = sqlite3_quota_file(zFilename);
                   1387: 
                   1388:   Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
                   1389:   return TCL_OK;
                   1390: }
                   1391: 
                   1392: /*
                   1393: ** tclcmd:  sqlite3_quota_dump
                   1394: */
                   1395: static int test_quota_dump(
                   1396:   void * clientData,
                   1397:   Tcl_Interp *interp,
                   1398:   int objc,
                   1399:   Tcl_Obj *CONST objv[]
                   1400: ){
                   1401:   Tcl_Obj *pResult;
                   1402:   Tcl_Obj *pGroupTerm;
                   1403:   Tcl_Obj *pFileTerm;
                   1404:   quotaGroup *pGroup;
                   1405:   quotaFile *pFile;
                   1406: 
                   1407:   pResult = Tcl_NewObj();
                   1408:   quotaEnter();
                   1409:   for(pGroup=gQuota.pGroup; pGroup; pGroup=pGroup->pNext){
                   1410:     pGroupTerm = Tcl_NewObj();
                   1411:     Tcl_ListObjAppendElement(interp, pGroupTerm,
                   1412:           Tcl_NewStringObj(pGroup->zPattern, -1));
                   1413:     Tcl_ListObjAppendElement(interp, pGroupTerm,
                   1414:           Tcl_NewWideIntObj(pGroup->iLimit));
                   1415:     Tcl_ListObjAppendElement(interp, pGroupTerm,
                   1416:           Tcl_NewWideIntObj(pGroup->iSize));
                   1417:     for(pFile=pGroup->pFiles; pFile; pFile=pFile->pNext){
                   1418:       int i;
                   1419:       char zTemp[1000];
                   1420:       pFileTerm = Tcl_NewObj();
                   1421:       sqlite3_snprintf(sizeof(zTemp), zTemp, "%s", pFile->zFilename);
                   1422:       for(i=0; zTemp[i]; i++){ if( zTemp[i]=='\\' ) zTemp[i] = '/'; }
                   1423:       Tcl_ListObjAppendElement(interp, pFileTerm,
                   1424:             Tcl_NewStringObj(zTemp, -1));
                   1425:       Tcl_ListObjAppendElement(interp, pFileTerm,
                   1426:             Tcl_NewWideIntObj(pFile->iSize));
                   1427:       Tcl_ListObjAppendElement(interp, pFileTerm,
                   1428:             Tcl_NewWideIntObj(pFile->nRef));
                   1429:       Tcl_ListObjAppendElement(interp, pFileTerm,
                   1430:             Tcl_NewWideIntObj(pFile->deleteOnClose));
                   1431:       Tcl_ListObjAppendElement(interp, pGroupTerm, pFileTerm);
                   1432:     }
                   1433:     Tcl_ListObjAppendElement(interp, pResult, pGroupTerm);
                   1434:   }
                   1435:   quotaLeave();
                   1436:   Tcl_SetObjResult(interp, pResult);
                   1437:   return TCL_OK;
                   1438: }
                   1439: 
                   1440: /*
                   1441: ** tclcmd: sqlite3_quota_fopen FILENAME MODE
                   1442: */
                   1443: static int test_quota_fopen(
                   1444:   void * clientData,
                   1445:   Tcl_Interp *interp,
                   1446:   int objc,
                   1447:   Tcl_Obj *CONST objv[]
                   1448: ){
                   1449:   const char *zFilename;          /* File pattern to configure */
                   1450:   const char *zMode;              /* Mode string */
                   1451:   quota_FILE *p;                  /* Open string object */
                   1452:   char zReturn[50];               /* Name of pointer to return */
                   1453: 
                   1454:   /* Process arguments */
                   1455:   if( objc!=3 ){
                   1456:     Tcl_WrongNumArgs(interp, 1, objv, "FILENAME MODE");
                   1457:     return TCL_ERROR;
                   1458:   }
                   1459:   zFilename = Tcl_GetString(objv[1]);
                   1460:   zMode = Tcl_GetString(objv[2]);
                   1461:   p = sqlite3_quota_fopen(zFilename, zMode);
                   1462:   sqlite3_snprintf(sizeof(zReturn), zReturn, "%p", p);
                   1463:   Tcl_SetResult(interp, zReturn, TCL_VOLATILE);
                   1464:   return TCL_OK;
                   1465: }
                   1466: 
                   1467: /* Defined in test1.c */
                   1468: extern void *sqlite3TestTextToPtr(const char*);
                   1469: 
                   1470: /*
                   1471: ** tclcmd: sqlite3_quota_fread HANDLE SIZE NELEM
                   1472: */
                   1473: static int test_quota_fread(
                   1474:   void * clientData,
                   1475:   Tcl_Interp *interp,
                   1476:   int objc,
                   1477:   Tcl_Obj *CONST objv[]
                   1478: ){
                   1479:   quota_FILE *p;
                   1480:   char *zBuf;
                   1481:   int sz;
                   1482:   int nElem;
                   1483:   int got;
                   1484: 
                   1485:   if( objc!=4 ){
                   1486:     Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE NELEM");
                   1487:     return TCL_ERROR;
                   1488:   }
                   1489:   p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
                   1490:   if( Tcl_GetIntFromObj(interp, objv[2], &sz) ) return TCL_ERROR;
                   1491:   if( Tcl_GetIntFromObj(interp, objv[3], &nElem) ) return TCL_ERROR;
                   1492:   zBuf = (char*)sqlite3_malloc( sz*nElem + 1 );
                   1493:   if( zBuf==0 ){
                   1494:     Tcl_SetResult(interp, "out of memory", TCL_STATIC);
                   1495:     return TCL_ERROR;
                   1496:   }
                   1497:   got = sqlite3_quota_fread(zBuf, sz, nElem, p);
                   1498:   if( got<0 ) got = 0;
                   1499:   zBuf[got*sz] = 0;
                   1500:   Tcl_SetResult(interp, zBuf, TCL_VOLATILE);
                   1501:   sqlite3_free(zBuf);
                   1502:   return TCL_OK;
                   1503: }
                   1504: 
                   1505: /*
                   1506: ** tclcmd: sqlite3_quota_fwrite HANDLE SIZE NELEM CONTENT
                   1507: */
                   1508: static int test_quota_fwrite(
                   1509:   void * clientData,
                   1510:   Tcl_Interp *interp,
                   1511:   int objc,
                   1512:   Tcl_Obj *CONST objv[]
                   1513: ){
                   1514:   quota_FILE *p;
                   1515:   char *zBuf;
                   1516:   int sz;
                   1517:   int nElem;
                   1518:   int got;
                   1519: 
                   1520:   if( objc!=5 ){
                   1521:     Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE NELEM CONTENT");
                   1522:     return TCL_ERROR;
                   1523:   }
                   1524:   p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
                   1525:   if( Tcl_GetIntFromObj(interp, objv[2], &sz) ) return TCL_ERROR;
                   1526:   if( Tcl_GetIntFromObj(interp, objv[3], &nElem) ) return TCL_ERROR;
                   1527:   zBuf = Tcl_GetString(objv[4]);
                   1528:   got = sqlite3_quota_fwrite(zBuf, sz, nElem, p);
                   1529:   Tcl_SetObjResult(interp, Tcl_NewIntObj(got));
                   1530:   return TCL_OK;
                   1531: }
                   1532: 
                   1533: /*
                   1534: ** tclcmd: sqlite3_quota_fclose HANDLE
                   1535: */
                   1536: static int test_quota_fclose(
                   1537:   void * clientData,
                   1538:   Tcl_Interp *interp,
                   1539:   int objc,
                   1540:   Tcl_Obj *CONST objv[]
                   1541: ){
                   1542:   quota_FILE *p;
                   1543:   int rc;
                   1544: 
                   1545:   if( objc!=2 ){
                   1546:     Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
                   1547:     return TCL_ERROR;
                   1548:   }
                   1549:   p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
                   1550:   rc = sqlite3_quota_fclose(p);
                   1551:   Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
                   1552:   return TCL_OK;
                   1553: }
                   1554: 
                   1555: /*
                   1556: ** tclcmd: sqlite3_quota_fflush HANDLE ?HARDSYNC?
                   1557: */
                   1558: static int test_quota_fflush(
                   1559:   void * clientData,
                   1560:   Tcl_Interp *interp,
                   1561:   int objc,
                   1562:   Tcl_Obj *CONST objv[]
                   1563: ){
                   1564:   quota_FILE *p;
                   1565:   int rc;
                   1566:   int doSync = 0;
                   1567: 
                   1568:   if( objc!=2 && objc!=3 ){
                   1569:     Tcl_WrongNumArgs(interp, 1, objv, "HANDLE ?HARDSYNC?");
                   1570:     return TCL_ERROR;
                   1571:   }
                   1572:   p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
                   1573:   if( objc==3 ){
                   1574:     if( Tcl_GetBooleanFromObj(interp, objv[2], &doSync) ) return TCL_ERROR;
                   1575:   }
                   1576:   rc = sqlite3_quota_fflush(p, doSync);
                   1577:   Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
                   1578:   return TCL_OK;
                   1579: }
                   1580: 
                   1581: /*
                   1582: ** tclcmd: sqlite3_quota_fseek HANDLE OFFSET WHENCE
                   1583: */
                   1584: static int test_quota_fseek(
                   1585:   void * clientData,
                   1586:   Tcl_Interp *interp,
                   1587:   int objc,
                   1588:   Tcl_Obj *CONST objv[]
                   1589: ){
                   1590:   quota_FILE *p;
                   1591:   int ofst;
                   1592:   const char *zWhence;
                   1593:   int whence;
                   1594:   int rc;
                   1595: 
                   1596:   if( objc!=4 ){
                   1597:     Tcl_WrongNumArgs(interp, 1, objv, "HANDLE OFFSET WHENCE");
                   1598:     return TCL_ERROR;
                   1599:   }
                   1600:   p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
                   1601:   if( Tcl_GetIntFromObj(interp, objv[2], &ofst) ) return TCL_ERROR;
                   1602:   zWhence = Tcl_GetString(objv[3]);
                   1603:   if( strcmp(zWhence, "SEEK_SET")==0 ){
                   1604:     whence = SEEK_SET;
                   1605:   }else if( strcmp(zWhence, "SEEK_CUR")==0 ){
                   1606:     whence = SEEK_CUR;
                   1607:   }else if( strcmp(zWhence, "SEEK_END")==0 ){
                   1608:     whence = SEEK_END;
                   1609:   }else{
                   1610:     Tcl_AppendResult(interp,
                   1611:            "WHENCE should be SEEK_SET, SEEK_CUR, or SEEK_END", (char*)0);
                   1612:     return TCL_ERROR;
                   1613:   }
                   1614:   rc = sqlite3_quota_fseek(p, ofst, whence);
                   1615:   Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
                   1616:   return TCL_OK;
                   1617: }
                   1618: 
                   1619: /*
                   1620: ** tclcmd: sqlite3_quota_rewind HANDLE
                   1621: */
                   1622: static int test_quota_rewind(
                   1623:   void * clientData,
                   1624:   Tcl_Interp *interp,
                   1625:   int objc,
                   1626:   Tcl_Obj *CONST objv[]
                   1627: ){
                   1628:   quota_FILE *p;
                   1629:   if( objc!=2 ){
                   1630:     Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
                   1631:     return TCL_ERROR;
                   1632:   }
                   1633:   p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
                   1634:   sqlite3_quota_rewind(p);
                   1635:   return TCL_OK;
                   1636: }
                   1637: 
                   1638: /*
                   1639: ** tclcmd: sqlite3_quota_ftell HANDLE
                   1640: */
                   1641: static int test_quota_ftell(
                   1642:   void * clientData,
                   1643:   Tcl_Interp *interp,
                   1644:   int objc,
                   1645:   Tcl_Obj *CONST objv[]
                   1646: ){
                   1647:   quota_FILE *p;
                   1648:   sqlite3_int64 x;
                   1649:   if( objc!=2 ){
                   1650:     Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
                   1651:     return TCL_ERROR;
                   1652:   }
                   1653:   p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
                   1654:   x = sqlite3_quota_ftell(p);
                   1655:   Tcl_SetObjResult(interp, Tcl_NewWideIntObj(x));
                   1656:   return TCL_OK;
                   1657: }
                   1658: 
                   1659: /*
                   1660: ** tclcmd: sqlite3_quota_remove FILENAME
                   1661: */
                   1662: static int test_quota_remove(
                   1663:   void * clientData,
                   1664:   Tcl_Interp *interp,
                   1665:   int objc,
                   1666:   Tcl_Obj *CONST objv[]
                   1667: ){
                   1668:   const char *zFilename;          /* File pattern to configure */
                   1669:   int rc;
                   1670:   if( objc!=2 ){
                   1671:     Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
                   1672:     return TCL_ERROR;
                   1673:   }
                   1674:   zFilename = Tcl_GetString(objv[1]);
                   1675:   rc = sqlite3_quota_remove(zFilename);
                   1676:   Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
                   1677:   return TCL_OK;
                   1678: }
                   1679: 
                   1680: /*
                   1681: ** tclcmd: sqlite3_quota_glob PATTERN TEXT
                   1682: **
                   1683: ** Test the glob pattern matching.  Return 1 if TEXT matches PATTERN
                   1684: ** and return 0 if it does not.
                   1685: */
                   1686: static int test_quota_glob(
                   1687:   void * clientData,
                   1688:   Tcl_Interp *interp,
                   1689:   int objc,
                   1690:   Tcl_Obj *CONST objv[]
                   1691: ){
                   1692:   const char *zPattern;          /* The glob pattern */
                   1693:   const char *zText;             /* Text to compare agains the pattern */
                   1694:   int rc;
                   1695:   if( objc!=3 ){
                   1696:     Tcl_WrongNumArgs(interp, 1, objv, "PATTERN TEXT");
                   1697:     return TCL_ERROR;
                   1698:   }
                   1699:   zPattern = Tcl_GetString(objv[1]);
                   1700:   zText = Tcl_GetString(objv[2]);
                   1701:   rc = quotaStrglob(zPattern, zText);
                   1702:   Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
                   1703:   return TCL_OK;
                   1704: }
                   1705: 
                   1706: /*
                   1707: ** This routine registers the custom TCL commands defined in this
                   1708: ** module.  This should be the only procedure visible from outside
                   1709: ** of this module.
                   1710: */
                   1711: int Sqlitequota_Init(Tcl_Interp *interp){
                   1712:   static struct {
                   1713:      char *zName;
                   1714:      Tcl_ObjCmdProc *xProc;
                   1715:   } aCmd[] = {
                   1716:     { "sqlite3_quota_initialize", test_quota_initialize },
                   1717:     { "sqlite3_quota_shutdown",   test_quota_shutdown },
                   1718:     { "sqlite3_quota_set",        test_quota_set },
                   1719:     { "sqlite3_quota_file",       test_quota_file },
                   1720:     { "sqlite3_quota_dump",       test_quota_dump },
                   1721:     { "sqlite3_quota_fopen",      test_quota_fopen },
                   1722:     { "sqlite3_quota_fread",      test_quota_fread },
                   1723:     { "sqlite3_quota_fwrite",     test_quota_fwrite },
                   1724:     { "sqlite3_quota_fclose",     test_quota_fclose },
                   1725:     { "sqlite3_quota_fflush",     test_quota_fflush },
                   1726:     { "sqlite3_quota_fseek",      test_quota_fseek },
                   1727:     { "sqlite3_quota_rewind",     test_quota_rewind },
                   1728:     { "sqlite3_quota_ftell",      test_quota_ftell },
                   1729:     { "sqlite3_quota_remove",     test_quota_remove },
                   1730:     { "sqlite3_quota_glob",       test_quota_glob },
                   1731:   };
                   1732:   int i;
                   1733: 
                   1734:   for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
                   1735:     Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
                   1736:   }
                   1737: 
                   1738:   return TCL_OK;
                   1739: }
                   1740: #endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>