#if SQLITE_TEST
#include "sqliteInt.h"
#if defined(INCLUDE_SQLITE_TCL_H)
# include "sqlite_tcl.h"
#else
# include "tcl.h"
#endif
#ifndef SQLITE_OMIT_DISKIO
typedef struct CrashFile CrashFile;
typedef struct CrashGlobal CrashGlobal;
typedef struct WriteBuffer WriteBuffer;
struct WriteBuffer {
i64 iOffset;
int nBuf;
u8 *zBuf;
CrashFile *pFile;
WriteBuffer *pNext;
};
struct CrashFile {
const sqlite3_io_methods *pMethod;
sqlite3_file *pRealFile;
char *zName;
int flags;
u8 *zData;
int nData;
i64 iSize;
};
struct CrashGlobal {
WriteBuffer *pWriteList;
WriteBuffer *pWriteListEnd;
int iSectorSize;
int iDeviceCharacteristics;
int iCrash;
char zCrashFile[500];
};
static CrashGlobal g = {0, 0, SQLITE_DEFAULT_SECTOR_SIZE, 0, 0};
static int sqlite3CrashTestEnable = 0;
static void *crash_malloc(int nByte){
return (void *)Tcl_AttemptAlloc((size_t)nByte);
}
static void crash_free(void *p){
Tcl_Free(p);
}
static void *crash_realloc(void *p, int n){
return (void *)Tcl_AttemptRealloc(p, (size_t)n);
}
static int writeDbFile(CrashFile *p, u8 *z, i64 iAmt, i64 iOff){
int rc = SQLITE_OK;
int iSkip = 0;
if( (iAmt-iSkip)>0 ){
rc = sqlite3OsWrite(p->pRealFile, &z[iSkip], (int)(iAmt-iSkip), iOff+iSkip);
}
return rc;
}
static int writeListSync(CrashFile *pFile, int isCrash){
int rc = SQLITE_OK;
int iDc = g.iDeviceCharacteristics;
WriteBuffer *pWrite;
WriteBuffer **ppPtr;
WriteBuffer *pFinal = 0;
if( !isCrash ){
for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext){
if( pWrite->pFile==pFile ){
pFinal = pWrite;
}
}
}else if( iDc&(SQLITE_IOCAP_SEQUENTIAL|SQLITE_IOCAP_SAFE_APPEND) ){
int nWrite = 0;
int iFinal;
for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext) nWrite++;
sqlite3_randomness(sizeof(int), &iFinal);
iFinal = ((iFinal<0)?-1*iFinal:iFinal)%nWrite;
for(pWrite=g.pWriteList; iFinal>0; pWrite=pWrite->pNext) iFinal--;
pFinal = pWrite;
}
#ifdef TRACE_CRASHTEST
if( pFile ){
printf("Sync %s (is %s crash)\n", pFile->zName, (isCrash?"a":"not a"));
}
#endif
ppPtr = &g.pWriteList;
for(pWrite=*ppPtr; rc==SQLITE_OK && pWrite; pWrite=*ppPtr){
sqlite3_file *pRealFile = pWrite->pFile->pRealFile;
int eAction = 0;
if( !isCrash ){
eAction = 2;
if( (pWrite->pFile==pFile || iDc&SQLITE_IOCAP_SEQUENTIAL) ){
eAction = 1;
}
}else{
char random;
sqlite3_randomness(1, &random);
if( (iDc&SQLITE_IOCAP_ATOMIC) || (pWrite->zBuf==0) ){
random &= 0x01;
}
if( (iDc&SQLITE_IOCAP_SEQUENTIAL && pWrite!=pFinal) ){
random = 0;
}
if( iDc&SQLITE_IOCAP_SAFE_APPEND && pWrite->zBuf ){
i64 iSize;
sqlite3OsFileSize(pRealFile, &iSize);
if( iSize==pWrite->iOffset ){
random = 0;
}
}
if( (random&0x06)==0x06 ){
eAction = 3;
}else{
eAction = ((random&0x01)?2:1);
}
}
switch( eAction ){
case 1: {
if( pWrite->zBuf ){
rc = writeDbFile(
pWrite->pFile, pWrite->zBuf, pWrite->nBuf, pWrite->iOffset
);
}else{
rc = sqlite3OsTruncate(pRealFile, pWrite->iOffset);
}
*ppPtr = pWrite->pNext;
#ifdef TRACE_CRASHTEST
if( isCrash ){
printf("Writing %d bytes @ %d (%s)\n",
pWrite->nBuf, (int)pWrite->iOffset, pWrite->pFile->zName
);
}
#endif
crash_free(pWrite);
break;
}
case 2: {
ppPtr = &pWrite->pNext;
#ifdef TRACE_CRASHTEST
if( isCrash ){
printf("Omiting %d bytes @ %d (%s)\n",
pWrite->nBuf, (int)pWrite->iOffset, pWrite->pFile->zName
);
}
#endif
break;
}
case 3: {
u8 *zGarbage;
int iFirst = (int)(pWrite->iOffset/g.iSectorSize);
int iLast = (int)((pWrite->iOffset+pWrite->nBuf-1)/g.iSectorSize);
assert(pWrite->zBuf);
#ifdef TRACE_CRASHTEST
printf("Trashing %d sectors (%d bytes) @ %lld (sector %d) (%s)\n",
1+iLast-iFirst, (1+iLast-iFirst)*g.iSectorSize,
pWrite->iOffset, iFirst, pWrite->pFile->zName
);
#endif
zGarbage = crash_malloc(g.iSectorSize);
if( zGarbage ){
sqlite3_int64 i;
for(i=iFirst; rc==SQLITE_OK && i<=iLast; i++){
sqlite3_randomness(g.iSectorSize, zGarbage);
rc = writeDbFile(
pWrite->pFile, zGarbage, g.iSectorSize, i*g.iSectorSize
);
}
crash_free(zGarbage);
}else{
rc = SQLITE_NOMEM;
}
ppPtr = &pWrite->pNext;
break;
}
default:
assert(!"Cannot happen");
}
if( pWrite==pFinal ) break;
}
if( rc==SQLITE_OK && isCrash ){
exit(-1);
}
for(pWrite=g.pWriteList; pWrite && pWrite->pNext; pWrite=pWrite->pNext);
g.pWriteListEnd = pWrite;
return rc;
}
static int writeListAppend(
sqlite3_file *pFile,
sqlite3_int64 iOffset,
const u8 *zBuf,
int nBuf
){
WriteBuffer *pNew;
assert((zBuf && nBuf) || (!nBuf && !zBuf));
pNew = (WriteBuffer *)crash_malloc(sizeof(WriteBuffer) + nBuf);
if( pNew==0 ){
fprintf(stderr, "out of memory in the crash simulator\n");
}
memset(pNew, 0, sizeof(WriteBuffer)+nBuf);
pNew->iOffset = iOffset;
pNew->nBuf = nBuf;
pNew->pFile = (CrashFile *)pFile;
if( zBuf ){
pNew->zBuf = (u8 *)&pNew[1];
memcpy(pNew->zBuf, zBuf, nBuf);
}
if( g.pWriteList ){
assert(g.pWriteListEnd);
g.pWriteListEnd->pNext = pNew;
}else{
g.pWriteList = pNew;
}
g.pWriteListEnd = pNew;
return SQLITE_OK;
}
static int cfClose(sqlite3_file *pFile){
CrashFile *pCrash = (CrashFile *)pFile;
writeListSync(pCrash, 0);
sqlite3OsClose(pCrash->pRealFile);
return SQLITE_OK;
}
static int cfRead(
sqlite3_file *pFile,
void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
CrashFile *pCrash = (CrashFile *)pFile;
int nCopy = (int)MIN((i64)iAmt, (pCrash->iSize - iOfst));
if( nCopy>0 ){
memcpy(zBuf, &pCrash->zData[iOfst], nCopy);
}
if( nCopy<iAmt ){
return SQLITE_IOERR_SHORT_READ;
}
return SQLITE_OK;
}
static int cfWrite(
sqlite3_file *pFile,
const void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
CrashFile *pCrash = (CrashFile *)pFile;
if( iAmt+iOfst>pCrash->iSize ){
pCrash->iSize = (int)(iAmt+iOfst);
}
while( pCrash->iSize>pCrash->nData ){
u8 *zNew;
int nNew = (pCrash->nData*2) + 4096;
zNew = crash_realloc(pCrash->zData, nNew);
if( !zNew ){
return SQLITE_NOMEM;
}
memset(&zNew[pCrash->nData], 0, nNew-pCrash->nData);
pCrash->nData = nNew;
pCrash->zData = zNew;
}
memcpy(&pCrash->zData[iOfst], zBuf, iAmt);
return writeListAppend(pFile, iOfst, zBuf, iAmt);
}
static int cfTruncate(sqlite3_file *pFile, sqlite_int64 size){
CrashFile *pCrash = (CrashFile *)pFile;
assert(size>=0);
if( pCrash->iSize>size ){
pCrash->iSize = (int)size;
}
return writeListAppend(pFile, size, 0, 0);
}
static int cfSync(sqlite3_file *pFile, int flags){
CrashFile *pCrash = (CrashFile *)pFile;
int isCrash = 0;
const char *zName = pCrash->zName;
const char *zCrashFile = g.zCrashFile;
int nName = (int)strlen(zName);
int nCrashFile = (int)strlen(zCrashFile);
if( nCrashFile>0 && zCrashFile[nCrashFile-1]=='*' ){
nCrashFile--;
if( nName>nCrashFile ) nName = nCrashFile;
}
#ifdef TRACE_CRASHTEST
printf("cfSync(): nName = %d, nCrashFile = %d, zName = %s, zCrashFile = %s\n",
nName, nCrashFile, zName, zCrashFile);
#endif
if( nName==nCrashFile && 0==memcmp(zName, zCrashFile, nName) ){
#ifdef TRACE_CRASHTEST
printf("cfSync(): name matched, g.iCrash = %d\n", g.iCrash);
#endif
if( (--g.iCrash)==0 ) isCrash = 1;
}
return writeListSync(pCrash, isCrash);
}
static int cfFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
CrashFile *pCrash = (CrashFile *)pFile;
*pSize = (i64)pCrash->iSize;
return SQLITE_OK;
}
static int cfLock(sqlite3_file *pFile, int eLock){
return sqlite3OsLock(((CrashFile *)pFile)->pRealFile, eLock);
}
static int cfUnlock(sqlite3_file *pFile, int eLock){
return sqlite3OsUnlock(((CrashFile *)pFile)->pRealFile, eLock);
}
static int cfCheckReservedLock(sqlite3_file *pFile, int *pResOut){
return sqlite3OsCheckReservedLock(((CrashFile *)pFile)->pRealFile, pResOut);
}
static int cfFileControl(sqlite3_file *pFile, int op, void *pArg){
if( op==SQLITE_FCNTL_SIZE_HINT ){
CrashFile *pCrash = (CrashFile *)pFile;
i64 nByte = *(i64 *)pArg;
if( nByte>pCrash->iSize ){
if( SQLITE_OK==writeListAppend(pFile, nByte, 0, 0) ){
pCrash->iSize = (int)nByte;
}
}
return SQLITE_OK;
}
return sqlite3OsFileControl(((CrashFile *)pFile)->pRealFile, op, pArg);
}
static int cfSectorSize(sqlite3_file *pFile){
return g.iSectorSize;
}
static int cfDeviceCharacteristics(sqlite3_file *pFile){
return g.iDeviceCharacteristics;
}
static int cfShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
sqlite3_file *pReal = ((CrashFile*)pFile)->pRealFile;
return pReal->pMethods->xShmLock(pReal, ofst, n, flags);
}
static void cfShmBarrier(sqlite3_file *pFile){
sqlite3_file *pReal = ((CrashFile*)pFile)->pRealFile;
pReal->pMethods->xShmBarrier(pReal);
}
static int cfShmUnmap(sqlite3_file *pFile, int delFlag){
sqlite3_file *pReal = ((CrashFile*)pFile)->pRealFile;
return pReal->pMethods->xShmUnmap(pReal, delFlag);
}
static int cfShmMap(
sqlite3_file *pFile,
int iRegion,
int sz,
int w,
void volatile **pp
){
sqlite3_file *pReal = ((CrashFile*)pFile)->pRealFile;
return pReal->pMethods->xShmMap(pReal, iRegion, sz, w, pp);
}
static const sqlite3_io_methods CrashFileVtab = {
2,
cfClose,
cfRead,
cfWrite,
cfTruncate,
cfSync,
cfFileSize,
cfLock,
cfUnlock,
cfCheckReservedLock,
cfFileControl,
cfSectorSize,
cfDeviceCharacteristics,
cfShmMap,
cfShmLock,
cfShmBarrier,
cfShmUnmap
};
struct crashAppData {
sqlite3_vfs *pOrig;
};
static int cfOpen(
sqlite3_vfs *pCfVfs,
const char *zName,
sqlite3_file *pFile,
int flags,
int *pOutFlags
){
sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
int rc;
CrashFile *pWrapper = (CrashFile *)pFile;
sqlite3_file *pReal = (sqlite3_file*)&pWrapper[1];
memset(pWrapper, 0, sizeof(CrashFile));
rc = sqlite3OsOpen(pVfs, zName, pReal, flags, pOutFlags);
if( rc==SQLITE_OK ){
i64 iSize;
pWrapper->pMethod = &CrashFileVtab;
pWrapper->zName = (char *)zName;
pWrapper->pRealFile = pReal;
rc = sqlite3OsFileSize(pReal, &iSize);
pWrapper->iSize = (int)iSize;
pWrapper->flags = flags;
}
if( rc==SQLITE_OK ){
pWrapper->nData = (int)(4096 + pWrapper->iSize);
pWrapper->zData = crash_malloc(pWrapper->nData);
if( pWrapper->zData ){
i64 iOff;
memset(pWrapper->zData, 0, pWrapper->nData);
for(iOff=0; iOff<pWrapper->iSize; iOff += 512){
int nRead = (int)(pWrapper->iSize - iOff);
if( nRead>512 ) nRead = 512;
rc = sqlite3OsRead(pReal, &pWrapper->zData[iOff], nRead, iOff);
}
}else{
rc = SQLITE_NOMEM;
}
}
if( rc!=SQLITE_OK && pWrapper->pMethod ){
sqlite3OsClose(pFile);
}
return rc;
}
static int cfDelete(sqlite3_vfs *pCfVfs, const char *zPath, int dirSync){
sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
return pVfs->xDelete(pVfs, zPath, dirSync);
}
static int cfAccess(
sqlite3_vfs *pCfVfs,
const char *zPath,
int flags,
int *pResOut
){
sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
return pVfs->xAccess(pVfs, zPath, flags, pResOut);
}
static int cfFullPathname(
sqlite3_vfs *pCfVfs,
const char *zPath,
int nPathOut,
char *zPathOut
){
sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
return pVfs->xFullPathname(pVfs, zPath, nPathOut, zPathOut);
}
static void *cfDlOpen(sqlite3_vfs *pCfVfs, const char *zPath){
sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
return pVfs->xDlOpen(pVfs, zPath);
}
static void cfDlError(sqlite3_vfs *pCfVfs, int nByte, char *zErrMsg){
sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
pVfs->xDlError(pVfs, nByte, zErrMsg);
}
static void (*cfDlSym(sqlite3_vfs *pCfVfs, void *pH, const char *zSym))(void){
sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
return pVfs->xDlSym(pVfs, pH, zSym);
}
static void cfDlClose(sqlite3_vfs *pCfVfs, void *pHandle){
sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
pVfs->xDlClose(pVfs, pHandle);
}
static int cfRandomness(sqlite3_vfs *pCfVfs, int nByte, char *zBufOut){
sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
return pVfs->xRandomness(pVfs, nByte, zBufOut);
}
static int cfSleep(sqlite3_vfs *pCfVfs, int nMicro){
sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
return pVfs->xSleep(pVfs, nMicro);
}
static int cfCurrentTime(sqlite3_vfs *pCfVfs, double *pTimeOut){
sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
return pVfs->xCurrentTime(pVfs, pTimeOut);
}
static int cfGetLastError(sqlite3_vfs *pCfVfs, int n, char *z){
sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
return pVfs->xGetLastError(pVfs, n, z);
}
static int processDevSymArgs(
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[],
int *piDeviceChar,
int *piSectorSize
){
struct DeviceFlag {
char *zName;
int iValue;
} aFlag[] = {
{ "atomic", SQLITE_IOCAP_ATOMIC },
{ "atomic512", SQLITE_IOCAP_ATOMIC512 },
{ "atomic1k", SQLITE_IOCAP_ATOMIC1K },
{ "atomic2k", SQLITE_IOCAP_ATOMIC2K },
{ "atomic4k", SQLITE_IOCAP_ATOMIC4K },
{ "atomic8k", SQLITE_IOCAP_ATOMIC8K },
{ "atomic16k", SQLITE_IOCAP_ATOMIC16K },
{ "atomic32k", SQLITE_IOCAP_ATOMIC32K },
{ "atomic64k", SQLITE_IOCAP_ATOMIC64K },
{ "sequential", SQLITE_IOCAP_SEQUENTIAL },
{ "safe_append", SQLITE_IOCAP_SAFE_APPEND },
{ "powersafe_overwrite", SQLITE_IOCAP_POWERSAFE_OVERWRITE },
{ "batch-atomic", SQLITE_IOCAP_BATCH_ATOMIC },
{ 0, 0 }
};
int i;
int iDc = 0;
int iSectorSize = 0;
int setSectorsize = 0;
int setDeviceChar = 0;
for(i=0; i<objc; i+=2){
int nOpt;
char *zOpt = Tcl_GetStringFromObj(objv[i], &nOpt);
if( (nOpt>11 || nOpt<2 || strncmp("-sectorsize", zOpt, nOpt))
&& (nOpt>16 || nOpt<2 || strncmp("-characteristics", zOpt, nOpt))
){
Tcl_AppendResult(interp,
"Bad option: \"", zOpt,
"\" - must be \"-characteristics\" or \"-sectorsize\"", 0
);
return TCL_ERROR;
}
if( i==objc-1 ){
Tcl_AppendResult(interp, "Option requires an argument: \"", zOpt, "\"",0);
return TCL_ERROR;
}
if( zOpt[1]=='s' ){
if( Tcl_GetIntFromObj(interp, objv[i+1], &iSectorSize) ){
return TCL_ERROR;
}
setSectorsize = 1;
}else{
int j;
Tcl_Obj **apObj;
int nObj;
if( Tcl_ListObjGetElements(interp, objv[i+1], &nObj, &apObj) ){
return TCL_ERROR;
}
for(j=0; j<nObj; j++){
int rc;
int iChoice;
Tcl_Obj *pFlag = Tcl_DuplicateObj(apObj[j]);
Tcl_IncrRefCount(pFlag);
Tcl_UtfToLower(Tcl_GetString(pFlag));
rc = Tcl_GetIndexFromObjStruct(
interp, pFlag, aFlag, sizeof(aFlag[0]), "no such flag", 0, &iChoice
);
Tcl_DecrRefCount(pFlag);
if( rc ){
return TCL_ERROR;
}
iDc |= aFlag[iChoice].iValue;
}
setDeviceChar = 1;
}
}
if( setDeviceChar ){
*piDeviceChar = iDc;
}
if( setSectorsize ){
*piSectorSize = iSectorSize;
}
return TCL_OK;
}
static int SQLITE_TCLAPI crashNowCmd(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
if( objc!=1 ){
Tcl_WrongNumArgs(interp, 1, objv, "");
return TCL_ERROR;
}
writeListSync(0, 1);
assert( 0 );
return TCL_OK;
}
static int SQLITE_TCLAPI crashEnableCmd(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int isEnable;
int isDefault = 0;
static sqlite3_vfs crashVfs = {
2,
0,
0,
0,
"crash",
0,
cfOpen,
cfDelete,
cfAccess,
cfFullPathname,
cfDlOpen,
cfDlError,
cfDlSym,
cfDlClose,
cfRandomness,
cfSleep,
cfCurrentTime,
cfGetLastError,
0,
};
if( objc!=2 && objc!=3 ){
Tcl_WrongNumArgs(interp, 1, objv, "ENABLE ?DEFAULT?");
return TCL_ERROR;
}
if( Tcl_GetBooleanFromObj(interp, objv[1], &isEnable) ){
return TCL_ERROR;
}
if( objc==3 && Tcl_GetBooleanFromObj(interp, objv[2], &isDefault) ){
return TCL_ERROR;
}
if( (isEnable && crashVfs.pAppData) || (!isEnable && !crashVfs.pAppData) ){
return TCL_OK;
}
if( crashVfs.pAppData==0 ){
sqlite3_vfs *pOriginalVfs = sqlite3_vfs_find(0);
crashVfs.mxPathname = pOriginalVfs->mxPathname;
crashVfs.pAppData = (void *)pOriginalVfs;
crashVfs.szOsFile = sizeof(CrashFile) + pOriginalVfs->szOsFile;
sqlite3_vfs_register(&crashVfs, isDefault);
}else{
crashVfs.pAppData = 0;
sqlite3_vfs_unregister(&crashVfs);
}
return TCL_OK;
}
static int SQLITE_TCLAPI crashParamsObjCmd(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int iDelay;
const char *zCrashFile;
int nCrashFile, iDc, iSectorSize;
iDc = -1;
iSectorSize = -1;
if( objc<3 ){
Tcl_WrongNumArgs(interp, 1, objv, "?OPTIONS? DELAY CRASHFILE");
goto error;
}
zCrashFile = Tcl_GetStringFromObj(objv[objc-1], &nCrashFile);
if( nCrashFile>=sizeof(g.zCrashFile) ){
Tcl_AppendResult(interp, "Filename is too long: \"", zCrashFile, "\"", 0);
goto error;
}
if( Tcl_GetIntFromObj(interp, objv[objc-2], &iDelay) ){
goto error;
}
if( processDevSymArgs(interp, objc-3, &objv[1], &iDc, &iSectorSize) ){
return TCL_ERROR;
}
if( iDc>=0 ){
g.iDeviceCharacteristics = iDc;
}
if( iSectorSize>=0 ){
g.iSectorSize = iSectorSize;
}
g.iCrash = iDelay;
memcpy(g.zCrashFile, zCrashFile, nCrashFile+1);
sqlite3CrashTestEnable = 1;
return TCL_OK;
error:
return TCL_ERROR;
}
static int SQLITE_TCLAPI devSymObjCmd(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
void devsym_register(int iDeviceChar, int iSectorSize);
int iDc = -1;
int iSectorSize = -1;
if( processDevSymArgs(interp, objc-1, &objv[1], &iDc, &iSectorSize) ){
return TCL_ERROR;
}
devsym_register(iDc, iSectorSize);
return TCL_OK;
}
static int SQLITE_TCLAPI writeCrashObjCmd(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
void devsym_crash_on_write(int);
int nWrite = 0;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "NWRITE");
return TCL_ERROR;
}
if( Tcl_GetIntFromObj(interp, objv[1], &nWrite) ){
return TCL_ERROR;
}
devsym_crash_on_write(nWrite);
return TCL_OK;
}
static int SQLITE_TCLAPI dsUnregisterObjCmd(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
void devsym_unregister(void);
if( objc!=1 ){
Tcl_WrongNumArgs(interp, 1, objv, "");
return TCL_ERROR;
}
devsym_unregister();
return TCL_OK;
}
static int SQLITE_TCLAPI jtObjCmd(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int jt_register(char *, int);
char *zParent = 0;
if( objc!=2 && objc!=3 ){
Tcl_WrongNumArgs(interp, 1, objv, "?-default? PARENT-VFS");
return TCL_ERROR;
}
zParent = Tcl_GetString(objv[1]);
if( objc==3 ){
if( strcmp(zParent, "-default") ){
Tcl_AppendResult(interp,
"bad option \"", zParent, "\": must be -default", 0
);
return TCL_ERROR;
}
zParent = Tcl_GetString(objv[2]);
}
if( !(*zParent) ){
zParent = 0;
}
if( jt_register(zParent, objc==3) ){
Tcl_AppendResult(interp, "Error in jt_register", 0);
return TCL_ERROR;
}
return TCL_OK;
}
static int SQLITE_TCLAPI jtUnregisterObjCmd(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
void jt_unregister(void);
if( objc!=1 ){
Tcl_WrongNumArgs(interp, 1, objv, "");
return TCL_ERROR;
}
jt_unregister();
return TCL_OK;
}
#endif
int Sqlitetest6_Init(Tcl_Interp *interp){
#ifndef SQLITE_OMIT_DISKIO
Tcl_CreateObjCommand(interp, "sqlite3_crash_enable", crashEnableCmd, 0, 0);
Tcl_CreateObjCommand(interp, "sqlite3_crashparams", crashParamsObjCmd, 0, 0);
Tcl_CreateObjCommand(interp, "sqlite3_crash_now", crashNowCmd, 0, 0);
Tcl_CreateObjCommand(interp, "sqlite3_simulate_device", devSymObjCmd, 0, 0);
Tcl_CreateObjCommand(interp, "sqlite3_crash_on_write", writeCrashObjCmd,0,0);
Tcl_CreateObjCommand(interp, "unregister_devsim", dsUnregisterObjCmd, 0, 0);
Tcl_CreateObjCommand(interp, "register_jt_vfs", jtObjCmd, 0, 0);
Tcl_CreateObjCommand(interp, "unregister_jt_vfs", jtUnregisterObjCmd, 0, 0);
#endif
return TCL_OK;
}
#endif