diff options
Diffstat (limited to 'kopete/plugins/statistics/sqlite/os_win.c')
| -rw-r--r-- | kopete/plugins/statistics/sqlite/os_win.c | 747 | 
1 files changed, 747 insertions, 0 deletions
diff --git a/kopete/plugins/statistics/sqlite/os_win.c b/kopete/plugins/statistics/sqlite/os_win.c new file mode 100644 index 00000000..f6e3e3ea --- /dev/null +++ b/kopete/plugins/statistics/sqlite/os_win.c @@ -0,0 +1,747 @@ +/* +** 2004 May 22 +** +** The author disclaims copyright to this source code.  In place of +** a legal notice, here is a blessing: +** +**    May you do good and not evil. +**    May you find forgiveness for yourself and forgive others. +**    May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file contains code that is specific to windows. +*/ +#include "sqliteInt.h" +#include "os.h" +#if OS_WIN               /* This file is used for windows only */ + +#include <winbase.h> + +/* +** Macros used to determine whether or not to use threads. +*/ +#if defined(THREADSAFE) && THREADSAFE +# define SQLITE_W32_THREADS 1 +#endif + +/* +** Include code that is common to all os_*.c files +*/ +#include "os_common.h" + +/* +** Delete the named file +*/ +int sqlite3OsDelete(const char *zFilename){ +  DeleteFileA(zFilename); +  TRACE2("DELETE \"%s\"\n", zFilename); +  return SQLITE_OK; +} + +/* +** Return TRUE if the named file exists. +*/ +int sqlite3OsFileExists(const char *zFilename){ +  return GetFileAttributesA(zFilename) != 0xffffffff; +} + +/* +** Attempt to open a file for both reading and writing.  If that +** fails, try opening it read-only.  If the file does not exist, +** try to create it. +** +** On success, a handle for the open file is written to *id +** and *pReadonly is set to 0 if the file was opened for reading and +** writing or 1 if the file was opened read-only.  The function returns +** SQLITE_OK. +** +** On failure, the function returns SQLITE_CANTOPEN and leaves +** *id and *pReadonly unchanged. +*/ +int sqlite3OsOpenReadWrite( +  const char *zFilename, +  OsFile *id, +  int *pReadonly +){ +  HANDLE h; +  assert( !id->isOpen ); +  h = CreateFileA(zFilename, +     GENERIC_READ | GENERIC_WRITE, +     FILE_SHARE_READ | FILE_SHARE_WRITE, +     NULL, +     OPEN_ALWAYS, +     FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, +     NULL +  ); +  if( h==INVALID_HANDLE_VALUE ){ +    h = CreateFileA(zFilename, +       GENERIC_READ, +       FILE_SHARE_READ, +       NULL, +       OPEN_ALWAYS, +       FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, +       NULL +    ); +    if( h==INVALID_HANDLE_VALUE ){ +      return SQLITE_CANTOPEN; +    } +    *pReadonly = 1; +  }else{ +    *pReadonly = 0; +  } +  id->h = h; +  id->locktype = NO_LOCK; +  id->sharedLockByte = 0; +  id->isOpen = 1; +  OpenCounter(+1); +  TRACE3("OPEN R/W %d \"%s\"\n", h, zFilename); +  return SQLITE_OK; +} + + +/* +** Attempt to open a new file for exclusive access by this process. +** The file will be opened for both reading and writing.  To avoid +** a potential security problem, we do not allow the file to have +** previously existed.  Nor do we allow the file to be a symbolic +** link. +** +** If delFlag is true, then make arrangements to automatically delete +** the file when it is closed. +** +** On success, write the file handle into *id and return SQLITE_OK. +** +** On failure, return SQLITE_CANTOPEN. +*/ +int sqlite3OsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){ +  HANDLE h; +  int fileflags; +  assert( !id->isOpen ); +  if( delFlag ){ +    fileflags = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_RANDOM_ACCESS  +                     | FILE_FLAG_DELETE_ON_CLOSE; +  }else{ +    fileflags = FILE_FLAG_RANDOM_ACCESS; +  } +  h = CreateFileA(zFilename, +     GENERIC_READ | GENERIC_WRITE, +     0, +     NULL, +     CREATE_ALWAYS, +     fileflags, +     NULL +  ); +  if( h==INVALID_HANDLE_VALUE ){ +    return SQLITE_CANTOPEN; +  } +  id->h = h; +  id->locktype = NO_LOCK; +  id->sharedLockByte = 0; +  id->isOpen = 1; +  OpenCounter(+1); +  TRACE3("OPEN EX %d \"%s\"\n", h, zFilename); +  return SQLITE_OK; +} + +/* +** Attempt to open a new file for read-only access. +** +** On success, write the file handle into *id and return SQLITE_OK. +** +** On failure, return SQLITE_CANTOPEN. +*/ +int sqlite3OsOpenReadOnly(const char *zFilename, OsFile *id){ +  HANDLE h; +  assert( !id->isOpen ); +  h = CreateFileA(zFilename, +     GENERIC_READ, +     0, +     NULL, +     OPEN_EXISTING, +     FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, +     NULL +  ); +  if( h==INVALID_HANDLE_VALUE ){ +    return SQLITE_CANTOPEN; +  } +  id->h = h; +  id->locktype = NO_LOCK; +  id->sharedLockByte = 0; +  id->isOpen = 1; +  OpenCounter(+1); +  TRACE3("OPEN RO %d \"%s\"\n", h, zFilename); +  return SQLITE_OK; +} + +/* +** Attempt to open a file descriptor for the directory that contains a +** file.  This file descriptor can be used to fsync() the directory +** in order to make sure the creation of a new file is actually written +** to disk. +** +** This routine is only meaningful for Unix.  It is a no-op under +** windows since windows does not support hard links. +** +** On success, a handle for a previously open file is at *id is +** updated with the new directory file descriptor and SQLITE_OK is +** returned. +** +** On failure, the function returns SQLITE_CANTOPEN and leaves +** *id unchanged. +*/ +int sqlite3OsOpenDirectory( +  const char *zDirname, +  OsFile *id +){ +  return SQLITE_OK; +} + +/* +** If the following global variable points to a string which is the +** name of a directory, then that directory will be used to store +** temporary files. +*/ +const char *sqlite3_temp_directory = 0; + +/* +** Create a temporary file name in zBuf.  zBuf must be big enough to +** hold at least SQLITE_TEMPNAME_SIZE characters. +*/ +int sqlite3OsTempFileName(char *zBuf){ +  static char zChars[] = +    "abcdefghijklmnopqrstuvwxyz" +    "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +    "0123456789"; +  int i, j; +  char zTempPath[SQLITE_TEMPNAME_SIZE]; +  if( sqlite3_temp_directory ){ +    strncpy(zTempPath, sqlite3_temp_directory, SQLITE_TEMPNAME_SIZE-30); +    zTempPath[SQLITE_TEMPNAME_SIZE-30] = 0; +  }else{ +    GetTempPathA(SQLITE_TEMPNAME_SIZE-30, zTempPath); +  } +  for(i=strlen(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){} +  zTempPath[i] = 0; +  for(;;){ +    sprintf(zBuf, "%s\\"TEMP_FILE_PREFIX, zTempPath); +    j = strlen(zBuf); +    sqlite3Randomness(15, &zBuf[j]); +    for(i=0; i<15; i++, j++){ +      zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; +    } +    zBuf[j] = 0; +    if( !sqlite3OsFileExists(zBuf) ) break; +  } +  TRACE2("TEMP FILENAME: %s\n", zBuf); +  return SQLITE_OK;  +} + +/* +** Close a file. +*/ +int sqlite3OsClose(OsFile *id){ +  if( id->isOpen ){ +    TRACE2("CLOSE %d\n", id->h); +    CloseHandle(id->h); +    OpenCounter(-1); +    id->isOpen = 0; +  } +  return SQLITE_OK; +} + +/* +** Read data from a file into a buffer.  Return SQLITE_OK if all +** bytes were read successfully and SQLITE_IOERR if anything goes +** wrong. +*/ +int sqlite3OsRead(OsFile *id, void *pBuf, int amt){ +  DWORD got; +  assert( id->isOpen ); +  SimulateIOError(SQLITE_IOERR); +  TRACE3("READ %d lock=%d\n", id->h, id->locktype); +  if( !ReadFile(id->h, pBuf, amt, &got, 0) ){ +    got = 0; +  } +  if( got==(DWORD)amt ){ +    return SQLITE_OK; +  }else{ +    return SQLITE_IOERR; +  } +} + +/* +** Write data from a buffer into a file.  Return SQLITE_OK on success +** or some other error code on failure. +*/ +int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){ +  int rc; +  DWORD wrote; +  assert( id->isOpen ); +  SimulateIOError(SQLITE_IOERR); +  SimulateDiskfullError; +  TRACE3("WRITE %d lock=%d\n", id->h, id->locktype); +  while( amt>0 && (rc = WriteFile(id->h, pBuf, amt, &wrote, 0))!=0 && wrote>0 ){ +    amt -= wrote; +    pBuf = &((char*)pBuf)[wrote]; +  } +  if( !rc || amt>(int)wrote ){ +    return SQLITE_FULL; +  } +  return SQLITE_OK; +} + +/* +** Move the read/write pointer in a file. +*/ +int sqlite3OsSeek(OsFile *id, i64 offset){ +  LONG upperBits = offset>>32; +  LONG lowerBits = offset & 0xffffffff; +  DWORD rc; +  assert( id->isOpen ); +  SEEK(offset/1024 + 1); +  rc = SetFilePointer(id->h, lowerBits, &upperBits, FILE_BEGIN); +  TRACE3("SEEK %d %lld\n", id->h, offset); +  return SQLITE_OK; +} + +/* +** Make sure all writes to a particular file are committed to disk. +*/ +int sqlite3OsSync(OsFile *id){ +  assert( id->isOpen ); +  TRACE3("SYNC %d lock=%d\n", id->h, id->locktype); +  if( FlushFileBuffers(id->h) ){ +    return SQLITE_OK; +  }else{ +    return SQLITE_IOERR; +  } +} + +/* +** Sync the directory zDirname. This is a no-op on operating systems other +** than UNIX. +*/ +int sqlite3OsSyncDirectory(const char *zDirname){ +  SimulateIOError(SQLITE_IOERR); +  return SQLITE_OK; +} + +/* +** Truncate an open file to a specified size +*/ +int sqlite3OsTruncate(OsFile *id, i64 nByte){ +  LONG upperBits = nByte>>32; +  assert( id->isOpen ); +  TRACE3("TRUNCATE %d %lld\n", id->h, nByte); +  SimulateIOError(SQLITE_IOERR); +  SetFilePointer(id->h, nByte, &upperBits, FILE_BEGIN); +  SetEndOfFile(id->h); +  return SQLITE_OK; +} + +/* +** Determine the current size of a file in bytes +*/ +int sqlite3OsFileSize(OsFile *id, i64 *pSize){ +  DWORD upperBits, lowerBits; +  assert( id->isOpen ); +  SimulateIOError(SQLITE_IOERR); +  lowerBits = GetFileSize(id->h, &upperBits); +  *pSize = (((i64)upperBits)<<32) + lowerBits; +  return SQLITE_OK; +} + +/* +** Return true (non-zero) if we are running under WinNT, Win2K or WinXP. +** Return false (zero) for Win95, Win98, or WinME. +** +** Here is an interesting observation:  Win95, Win98, and WinME lack +** the LockFileEx() API.  But we can still statically link against that +** API as long as we don't call it win running Win95/98/ME.  A call to +** this routine is used to determine if the host is Win95/98/ME or +** WinNT/2K/XP so that we will know whether or not we can safely call +** the LockFileEx() API. +*/ +static int isNT(void){ +  static int osType = 0;   /* 0=unknown 1=win95 2=winNT */ +  if( osType==0 ){ +    OSVERSIONINFO sInfo; +    sInfo.dwOSVersionInfoSize = sizeof(sInfo); +    GetVersionEx(&sInfo); +    osType = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1; +  } +  return osType==2; +} + +/* +** Acquire a reader lock. +** Different API routines are called depending on whether or not this +** is Win95 or WinNT. +*/ +static int getReadLock(OsFile *id){ +  int res; +  if( isNT() ){ +    OVERLAPPED ovlp; +    ovlp.Offset = SHARED_FIRST; +    ovlp.OffsetHigh = 0; +    ovlp.hEvent = 0; +    res = LockFileEx(id->h, LOCKFILE_FAIL_IMMEDIATELY, 0, SHARED_SIZE,0,&ovlp); +  }else{ +    int lk; +    sqlite3Randomness(sizeof(lk), &lk); +    id->sharedLockByte = (lk & 0x7fffffff)%(SHARED_SIZE - 1); +    res = LockFile(id->h, SHARED_FIRST+id->sharedLockByte, 0, 1, 0); +  } +  return res; +} + +/* +** Undo a readlock +*/ +static int unlockReadLock(OsFile *id){ +  int res; +  if( isNT() ){ +    res = UnlockFile(id->h, SHARED_FIRST, 0, SHARED_SIZE, 0); +  }else{ +    res = UnlockFile(id->h, SHARED_FIRST + id->sharedLockByte, 0, 1, 0); +  } +  return res; +} + +/* +** Lock the file with the lock specified by parameter locktype - one +** of the following: +** +**     (1) SHARED_LOCK +**     (2) RESERVED_LOCK +**     (3) PENDING_LOCK +**     (4) EXCLUSIVE_LOCK +** +** Sometimes when requesting one lock state, additional lock states +** are inserted in between.  The locking might fail on one of the later +** transitions leaving the lock state different from what it started but +** still short of its goal.  The following chart shows the allowed +** transitions and the inserted intermediate states: +** +**    UNLOCKED -> SHARED +**    SHARED -> RESERVED +**    SHARED -> (PENDING) -> EXCLUSIVE +**    RESERVED -> (PENDING) -> EXCLUSIVE +**    PENDING -> EXCLUSIVE +** +** This routine will only increase a lock.  The sqlite3OsUnlock() routine +** erases all locks at once and returns us immediately to locking level 0. +** It is not possible to lower the locking level one step at a time.  You +** must go straight to locking level 0. +*/ +int sqlite3OsLock(OsFile *id, int locktype){ +  int rc = SQLITE_OK;    /* Return code from subroutines */ +  int res = 1;           /* Result of a windows lock call */ +  int newLocktype;       /* Set id->locktype to this value before exiting */ +  int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */ + +  assert( id->isOpen ); +  TRACE5("LOCK %d %d was %d(%d)\n", +          id->h, locktype, id->locktype, id->sharedLockByte); + +  /* If there is already a lock of this type or more restrictive on the +  ** OsFile, do nothing. Don't use the end_lock: exit path, as +  ** sqlite3OsEnterMutex() hasn't been called yet. +  */ +  if( id->locktype>=locktype ){ +    return SQLITE_OK; +  } + +  /* Make sure the locking sequence is correct +  */ +  assert( id->locktype!=NO_LOCK || locktype==SHARED_LOCK ); +  assert( locktype!=PENDING_LOCK ); +  assert( locktype!=RESERVED_LOCK || id->locktype==SHARED_LOCK ); + +  /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or +  ** a SHARED lock.  If we are acquiring a SHARED lock, the acquisition of +  ** the PENDING_LOCK byte is temporary. +  */ +  newLocktype = id->locktype; +  if( id->locktype==NO_LOCK +   || (locktype==EXCLUSIVE_LOCK && id->locktype==RESERVED_LOCK) +  ){ +    int cnt = 3; +    while( cnt-->0 && (res = LockFile(id->h, PENDING_BYTE, 0, 1, 0))==0 ){ +      /* Try 3 times to get the pending lock.  The pending lock might be +      ** held by another reader process who will release it momentarily. +      */ +      TRACE2("could not get a PENDING lock. cnt=%d\n", cnt); +      Sleep(1); +    } +    gotPendingLock = res; +  } + +  /* Acquire a shared lock +  */ +  if( locktype==SHARED_LOCK && res ){ +    assert( id->locktype==NO_LOCK ); +    res = getReadLock(id); +    if( res ){ +      newLocktype = SHARED_LOCK; +    } +  } + +  /* Acquire a RESERVED lock +  */ +  if( locktype==RESERVED_LOCK && res ){ +    assert( id->locktype==SHARED_LOCK ); +    res = LockFile(id->h, RESERVED_BYTE, 0, 1, 0); +    if( res ){ +      newLocktype = RESERVED_LOCK; +    } +  } + +  /* Acquire a PENDING lock +  */ +  if( locktype==EXCLUSIVE_LOCK && res ){ +    newLocktype = PENDING_LOCK; +    gotPendingLock = 0; +  } + +  /* Acquire an EXCLUSIVE lock +  */ +  if( locktype==EXCLUSIVE_LOCK && res ){ +    assert( id->locktype>=SHARED_LOCK ); +    res = unlockReadLock(id); +    TRACE2("unreadlock = %d\n", res); +    res = LockFile(id->h, SHARED_FIRST, 0, SHARED_SIZE, 0); +    if( res ){ +      newLocktype = EXCLUSIVE_LOCK; +    }else{ +      TRACE2("error-code = %d\n", GetLastError()); +    } +  } + +  /* If we are holding a PENDING lock that ought to be released, then +  ** release it now. +  */ +  if( gotPendingLock && locktype==SHARED_LOCK ){ +    UnlockFile(id->h, PENDING_BYTE, 0, 1, 0); +  } + +  /* Update the state of the lock has held in the file descriptor then +  ** return the appropriate result code. +  */ +  if( res ){ +    rc = SQLITE_OK; +  }else{ +    TRACE4("LOCK FAILED %d trying for %d but got %d\n", id->h, +           locktype, newLocktype); +    rc = SQLITE_BUSY; +  } +  id->locktype = newLocktype; +  return rc; +} + +/* +** This routine checks if there is a RESERVED lock held on the specified +** file by this or any other process. If such a lock is held, return +** non-zero, otherwise zero. +*/ +int sqlite3OsCheckReservedLock(OsFile *id){ +  int rc; +  assert( id->isOpen ); +  if( id->locktype>=RESERVED_LOCK ){ +    rc = 1; +    TRACE3("TEST WR-LOCK %d %d (local)\n", id->h, rc); +  }else{ +    rc = LockFile(id->h, RESERVED_BYTE, 0, 1, 0); +    if( rc ){ +      UnlockFile(id->h, RESERVED_BYTE, 0, 1, 0); +    } +    rc = !rc; +    TRACE3("TEST WR-LOCK %d %d (remote)\n", id->h, rc); +  } +  return rc; +} + +/* +** Lower the locking level on file descriptor id to locktype.  locktype +** must be either NO_LOCK or SHARED_LOCK. +** +** If the locking level of the file descriptor is already at or below +** the requested locking level, this routine is a no-op. +** +** It is not possible for this routine to fail if the second argument +** is NO_LOCK.  If the second argument is SHARED_LOCK then this routine +** might return SQLITE_IOERR; +*/ +int sqlite3OsUnlock(OsFile *id, int locktype){ +  int type; +  int rc = SQLITE_OK; +  assert( id->isOpen ); +  assert( locktype<=SHARED_LOCK ); +  TRACE5("UNLOCK %d to %d was %d(%d)\n", id->h, locktype, +          id->locktype, id->sharedLockByte); +  type = id->locktype; +  if( type>=EXCLUSIVE_LOCK ){ +    UnlockFile(id->h, SHARED_FIRST, 0, SHARED_SIZE, 0); +    if( locktype==SHARED_LOCK && !getReadLock(id) ){ +      /* This should never happen.  We should always be able to +      ** reacquire the read lock */ +      rc = SQLITE_IOERR; +    } +  } +  if( type>=RESERVED_LOCK ){ +    UnlockFile(id->h, RESERVED_BYTE, 0, 1, 0); +  } +  if( locktype==NO_LOCK && type>=SHARED_LOCK ){ +    unlockReadLock(id); +  } +  if( type>=PENDING_LOCK ){ +    UnlockFile(id->h, PENDING_BYTE, 0, 1, 0); +  } +  id->locktype = locktype; +  return rc; +} + +/* +** Get information to seed the random number generator.  The seed +** is written into the buffer zBuf[256].  The calling function must +** supply a sufficiently large buffer. +*/ +int sqlite3OsRandomSeed(char *zBuf){ +  /* We have to initialize zBuf to prevent valgrind from reporting +  ** errors.  The reports issued by valgrind are incorrect - we would +  ** prefer that the randomness be increased by making use of the +  ** uninitialized space in zBuf - but valgrind errors tend to worry +  ** some users.  Rather than argue, it seems easier just to initialize +  ** the whole array and silence valgrind, even if that means less randomness +  ** in the random seed. +  ** +  ** When testing, initializing zBuf[] to zero is all we do.  That means +  ** that we always use the same random number sequence.* This makes the +  ** tests repeatable. +  */ +  memset(zBuf, 0, 256); +  GetSystemTime((LPSYSTEMTIME)zBuf); +  return SQLITE_OK; +} + +/* +** Sleep for a little while.  Return the amount of time slept. +*/ +int sqlite3OsSleep(int ms){ +  Sleep(ms); +  return ms; +} + +/* +** Static variables used for thread synchronization +*/ +static int inMutex = 0; +#ifdef SQLITE_W32_THREADS +  static CRITICAL_SECTION cs; +#endif + +/* +** The following pair of routine implement mutual exclusion for +** multi-threaded processes.  Only a single thread is allowed to +** executed code that is surrounded by EnterMutex() and LeaveMutex(). +** +** SQLite uses only a single Mutex.  There is not much critical +** code and what little there is executes quickly and without blocking. +*/ +void sqlite3OsEnterMutex(){ +#ifdef SQLITE_W32_THREADS +  static int isInit = 0; +  while( !isInit ){ +    static long lock = 0; +    if( InterlockedIncrement(&lock)==1 ){ +      InitializeCriticalSection(&cs); +      isInit = 1; +    }else{ +      Sleep(1); +    } +  } +  EnterCriticalSection(&cs); +#endif +  assert( !inMutex ); +  inMutex = 1; +} +void sqlite3OsLeaveMutex(){ +  assert( inMutex ); +  inMutex = 0; +#ifdef SQLITE_W32_THREADS +  LeaveCriticalSection(&cs); +#endif +} + +/* +** Turn a relative pathname into a full pathname.  Return a pointer +** to the full pathname stored in space obtained from sqliteMalloc(). +** The calling function is responsible for freeing this space once it +** is no longer needed. +*/ +char *sqlite3OsFullPathname(const char *zRelative){ +  char *zNotUsed; +  char *zFull; +  int nByte; +  nByte = GetFullPathNameA(zRelative, 0, 0, &zNotUsed) + 1; +  zFull = sqliteMalloc( nByte ); +  if( zFull==0 ) return 0; +  GetFullPathNameA(zRelative, nByte, zFull, &zNotUsed); +  return zFull; +} + +/* +** The following variable, if set to a non-zero value, becomes the result +** returned from sqlite3OsCurrentTime().  This is used for testing. +*/ +#ifdef SQLITE_TEST +int sqlite3_current_time = 0; +#endif + +/* +** Find the current time (in Universal Coordinated Time).  Write the +** current time and date as a Julian Day number into *prNow and +** return 0.  Return 1 if the time and date cannot be found. +*/ +int sqlite3OsCurrentTime(double *prNow){ +  FILETIME ft; +  /* FILETIME structure is a 64-bit value representing the number of  +     100-nanosecond intervals since January 1, 1601 (= JD 2305813.5).  +  */ +  double now; +  GetSystemTimeAsFileTime( &ft ); +  now = ((double)ft.dwHighDateTime) * 4294967296.0;  +  *prNow = (now + ft.dwLowDateTime)/864000000000.0 + 2305813.5; +#ifdef SQLITE_TEST +  if( sqlite3_current_time ){ +    *prNow = sqlite3_current_time/86400.0 + 2440587.5; +  } +#endif +  return 0; +} + +/* +** Find the time that the file was last modified.  Write the +** modification time and date as a Julian Day number into *prNow and +** return SQLITE_OK.  Return SQLITE_ERROR if the modification +** time cannot be found. +*/ +int sqlite3OsFileModTime(OsFile *id, double *prMTime){ +  int rc; +  FILETIME ft; +  /* FILETIME structure is a 64-bit value representing the number of  +  ** 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5).  +  */ +  if( GetFileTime(id->h, 0, 0, &ft) ){ +    double t; +    t = ((double)ft.dwHighDateTime) * 4294967296.0;  +    *prMTime = (t + ft.dwLowDateTime)/864000000000.0 + 2305813.5; +    rc = SQLITE_OK; +  }else{ +    rc = SQLITE_ERROR; +  } +  return rc; +} + +#endif /* OS_WIN */  | 
