os_os2.c

Go to the documentation of this file.
00001 /*
00002 ** 2006 Feb 14
00003 **
00004 ** The author disclaims copyright to this source code.  In place of
00005 ** a legal notice, here is a blessing:
00006 **
00007 **    May you do good and not evil.
00008 **    May you find forgiveness for yourself and forgive others.
00009 **    May you share freely, never taking more than you give.
00010 **
00011 ******************************************************************************
00012 **
00013 ** This file contains code that is specific to OS/2.
00014 **
00015 ** $Id: os_os2.c,v 1.58 2008/11/07 00:06:18 drh Exp $
00016 */
00017 
00018 #include "sqliteInt.h"
00019 
00020 #if SQLITE_OS_OS2
00021 
00022 /*
00023 ** A Note About Memory Allocation:
00024 **
00025 ** This driver uses malloc()/free() directly rather than going through
00026 ** the SQLite-wrappers sqlite3_malloc()/sqlite3_free().  Those wrappers
00027 ** are designed for use on embedded systems where memory is scarce and
00028 ** malloc failures happen frequently.  OS/2 does not typically run on
00029 ** embedded systems, and when it does the developers normally have bigger
00030 ** problems to worry about than running out of memory.  So there is not
00031 ** a compelling need to use the wrappers.
00032 **
00033 ** But there is a good reason to not use the wrappers.  If we use the
00034 ** wrappers then we will get simulated malloc() failures within this
00035 ** driver.  And that causes all kinds of problems for our tests.  We
00036 ** could enhance SQLite to deal with simulated malloc failures within
00037 ** the OS driver, but the code to deal with those failure would not
00038 ** be exercised on Linux (which does not need to malloc() in the driver)
00039 ** and so we would have difficulty writing coverage tests for that
00040 ** code.  Better to leave the code out, we think.
00041 **
00042 ** The point of this discussion is as follows:  When creating a new
00043 ** OS layer for an embedded system, if you use this file as an example,
00044 ** avoid the use of malloc()/free().  Those routines work ok on OS/2
00045 ** desktops but not so well in embedded systems.
00046 */
00047 
00048 /*
00049 ** Macros used to determine whether or not to use threads.
00050 */
00051 #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE
00052 # define SQLITE_OS2_THREADS 1
00053 #endif
00054 
00055 /*
00056 ** Include code that is common to all os_*.c files
00057 */
00058 #include "os_common.h"
00059 
00060 /*
00061 ** The os2File structure is subclass of sqlite3_file specific for the OS/2
00062 ** protability layer.
00063 */
00064 typedef struct os2File os2File;
00065 struct os2File {
00066   const sqlite3_io_methods *pMethod;  /* Always the first entry */
00067   HFILE h;                  /* Handle for accessing the file */
00068   char* pathToDel;          /* Name of file to delete on close, NULL if not */
00069   unsigned char locktype;   /* Type of lock currently held on this file */
00070 };
00071 
00072 #define LOCK_TIMEOUT 10L /* the default locking timeout */
00073 
00074 /*****************************************************************************
00075 ** The next group of routines implement the I/O methods specified
00076 ** by the sqlite3_io_methods object.
00077 ******************************************************************************/
00078 
00079 /*
00080 ** Close a file.
00081 */
00082 static int os2Close( sqlite3_file *id ){
00083   APIRET rc = NO_ERROR;
00084   os2File *pFile;
00085   if( id && (pFile = (os2File*)id) != 0 ){
00086     OSTRACE2( "CLOSE %d\n", pFile->h );
00087     rc = DosClose( pFile->h );
00088     pFile->locktype = NO_LOCK;
00089     if( pFile->pathToDel != NULL ){
00090       rc = DosForceDelete( (PSZ)pFile->pathToDel );
00091       free( pFile->pathToDel );
00092       pFile->pathToDel = NULL;
00093     }
00094     id = 0;
00095     OpenCounter( -1 );
00096   }
00097 
00098   return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
00099 }
00100 
00101 /*
00102 ** Read data from a file into a buffer.  Return SQLITE_OK if all
00103 ** bytes were read successfully and SQLITE_IOERR if anything goes
00104 ** wrong.
00105 */
00106 static int os2Read(
00107   sqlite3_file *id,               /* File to read from */
00108   void *pBuf,                     /* Write content into this buffer */
00109   int amt,                        /* Number of bytes to read */
00110   sqlite3_int64 offset            /* Begin reading at this offset */
00111 ){
00112   ULONG fileLocation = 0L;
00113   ULONG got;
00114   os2File *pFile = (os2File*)id;
00115   assert( id!=0 );
00116   SimulateIOError( return SQLITE_IOERR_READ );
00117   OSTRACE3( "READ %d lock=%d\n", pFile->h, pFile->locktype );
00118   if( DosSetFilePtr(pFile->h, offset, FILE_BEGIN, &fileLocation) != NO_ERROR ){
00119     return SQLITE_IOERR;
00120   }
00121   if( DosRead( pFile->h, pBuf, amt, &got ) != NO_ERROR ){
00122     return SQLITE_IOERR_READ;
00123   }
00124   if( got == (ULONG)amt )
00125     return SQLITE_OK;
00126   else {
00127     /* Unread portions of the input buffer must be zero-filled */
00128     memset(&((char*)pBuf)[got], 0, amt-got);
00129     return SQLITE_IOERR_SHORT_READ;
00130   }
00131 }
00132 
00133 /*
00134 ** Write data from a buffer into a file.  Return SQLITE_OK on success
00135 ** or some other error code on failure.
00136 */
00137 static int os2Write(
00138   sqlite3_file *id,               /* File to write into */
00139   const void *pBuf,               /* The bytes to be written */
00140   int amt,                        /* Number of bytes to write */
00141   sqlite3_int64 offset            /* Offset into the file to begin writing at */
00142 ){
00143   ULONG fileLocation = 0L;
00144   APIRET rc = NO_ERROR;
00145   ULONG wrote;
00146   os2File *pFile = (os2File*)id;
00147   assert( id!=0 );
00148   SimulateIOError( return SQLITE_IOERR_WRITE );
00149   SimulateDiskfullError( return SQLITE_FULL );
00150   OSTRACE3( "WRITE %d lock=%d\n", pFile->h, pFile->locktype );
00151   if( DosSetFilePtr(pFile->h, offset, FILE_BEGIN, &fileLocation) != NO_ERROR ){
00152     return SQLITE_IOERR;
00153   }
00154   assert( amt>0 );
00155   while( amt > 0 &&
00156          ( rc = DosWrite( pFile->h, (PVOID)pBuf, amt, &wrote ) ) == NO_ERROR &&
00157          wrote > 0
00158   ){
00159     amt -= wrote;
00160     pBuf = &((char*)pBuf)[wrote];
00161   }
00162 
00163   return ( rc != NO_ERROR || amt > (int)wrote ) ? SQLITE_FULL : SQLITE_OK;
00164 }
00165 
00166 /*
00167 ** Truncate an open file to a specified size
00168 */
00169 static int os2Truncate( sqlite3_file *id, i64 nByte ){
00170   APIRET rc = NO_ERROR;
00171   os2File *pFile = (os2File*)id;
00172   OSTRACE3( "TRUNCATE %d %lld\n", pFile->h, nByte );
00173   SimulateIOError( return SQLITE_IOERR_TRUNCATE );
00174   rc = DosSetFileSize( pFile->h, nByte );
00175   return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR_TRUNCATE;
00176 }
00177 
00178 #ifdef SQLITE_TEST
00179 /*
00180 ** Count the number of fullsyncs and normal syncs.  This is used to test
00181 ** that syncs and fullsyncs are occuring at the right times.
00182 */
00183 int sqlite3_sync_count = 0;
00184 int sqlite3_fullsync_count = 0;
00185 #endif
00186 
00187 /*
00188 ** Make sure all writes to a particular file are committed to disk.
00189 */
00190 static int os2Sync( sqlite3_file *id, int flags ){
00191   os2File *pFile = (os2File*)id;
00192   OSTRACE3( "SYNC %d lock=%d\n", pFile->h, pFile->locktype );
00193 #ifdef SQLITE_TEST
00194   if( flags & SQLITE_SYNC_FULL){
00195     sqlite3_fullsync_count++;
00196   }
00197   sqlite3_sync_count++;
00198 #endif
00199   return DosResetBuffer( pFile->h ) == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
00200 }
00201 
00202 /*
00203 ** Determine the current size of a file in bytes
00204 */
00205 static int os2FileSize( sqlite3_file *id, sqlite3_int64 *pSize ){
00206   APIRET rc = NO_ERROR;
00207   FILESTATUS3 fsts3FileInfo;
00208   memset(&fsts3FileInfo, 0, sizeof(fsts3FileInfo));
00209   assert( id!=0 );
00210   SimulateIOError( return SQLITE_IOERR_FSTAT );
00211   rc = DosQueryFileInfo( ((os2File*)id)->h, FIL_STANDARD, &fsts3FileInfo, sizeof(FILESTATUS3) );
00212   if( rc == NO_ERROR ){
00213     *pSize = fsts3FileInfo.cbFile;
00214     return SQLITE_OK;
00215   }else{
00216     return SQLITE_IOERR_FSTAT;
00217   }
00218 }
00219 
00220 /*
00221 ** Acquire a reader lock.
00222 */
00223 static int getReadLock( os2File *pFile ){
00224   FILELOCK  LockArea,
00225             UnlockArea;
00226   APIRET res;
00227   memset(&LockArea, 0, sizeof(LockArea));
00228   memset(&UnlockArea, 0, sizeof(UnlockArea));
00229   LockArea.lOffset = SHARED_FIRST;
00230   LockArea.lRange = SHARED_SIZE;
00231   UnlockArea.lOffset = 0L;
00232   UnlockArea.lRange = 0L;
00233   res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 1L );
00234   OSTRACE3( "GETREADLOCK %d res=%d\n", pFile->h, res );
00235   return res;
00236 }
00237 
00238 /*
00239 ** Undo a readlock
00240 */
00241 static int unlockReadLock( os2File *id ){
00242   FILELOCK  LockArea,
00243             UnlockArea;
00244   APIRET res;
00245   memset(&LockArea, 0, sizeof(LockArea));
00246   memset(&UnlockArea, 0, sizeof(UnlockArea));
00247   LockArea.lOffset = 0L;
00248   LockArea.lRange = 0L;
00249   UnlockArea.lOffset = SHARED_FIRST;
00250   UnlockArea.lRange = SHARED_SIZE;
00251   res = DosSetFileLocks( id->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 1L );
00252   OSTRACE3( "UNLOCK-READLOCK file handle=%d res=%d?\n", id->h, res );
00253   return res;
00254 }
00255 
00256 /*
00257 ** Lock the file with the lock specified by parameter locktype - one
00258 ** of the following:
00259 **
00260 **     (1) SHARED_LOCK
00261 **     (2) RESERVED_LOCK
00262 **     (3) PENDING_LOCK
00263 **     (4) EXCLUSIVE_LOCK
00264 **
00265 ** Sometimes when requesting one lock state, additional lock states
00266 ** are inserted in between.  The locking might fail on one of the later
00267 ** transitions leaving the lock state different from what it started but
00268 ** still short of its goal.  The following chart shows the allowed
00269 ** transitions and the inserted intermediate states:
00270 **
00271 **    UNLOCKED -> SHARED
00272 **    SHARED -> RESERVED
00273 **    SHARED -> (PENDING) -> EXCLUSIVE
00274 **    RESERVED -> (PENDING) -> EXCLUSIVE
00275 **    PENDING -> EXCLUSIVE
00276 **
00277 ** This routine will only increase a lock.  The os2Unlock() routine
00278 ** erases all locks at once and returns us immediately to locking level 0.
00279 ** It is not possible to lower the locking level one step at a time.  You
00280 ** must go straight to locking level 0.
00281 */
00282 static int os2Lock( sqlite3_file *id, int locktype ){
00283   int rc = SQLITE_OK;       /* Return code from subroutines */
00284   APIRET res = NO_ERROR;    /* Result of an OS/2 lock call */
00285   int newLocktype;       /* Set pFile->locktype to this value before exiting */
00286   int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */
00287   FILELOCK  LockArea,
00288             UnlockArea;
00289   os2File *pFile = (os2File*)id;
00290   memset(&LockArea, 0, sizeof(LockArea));
00291   memset(&UnlockArea, 0, sizeof(UnlockArea));
00292   assert( pFile!=0 );
00293   OSTRACE4( "LOCK %d %d was %d\n", pFile->h, locktype, pFile->locktype );
00294 
00295   /* If there is already a lock of this type or more restrictive on the
00296   ** os2File, do nothing. Don't use the end_lock: exit path, as
00297   ** sqlite3_mutex_enter() hasn't been called yet.
00298   */
00299   if( pFile->locktype>=locktype ){
00300     OSTRACE3( "LOCK %d %d ok (already held)\n", pFile->h, locktype );
00301     return SQLITE_OK;
00302   }
00303 
00304   /* Make sure the locking sequence is correct
00305   */
00306   assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
00307   assert( locktype!=PENDING_LOCK );
00308   assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK );
00309 
00310   /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or
00311   ** a SHARED lock.  If we are acquiring a SHARED lock, the acquisition of
00312   ** the PENDING_LOCK byte is temporary.
00313   */
00314   newLocktype = pFile->locktype;
00315   if( pFile->locktype==NO_LOCK
00316       || (locktype==EXCLUSIVE_LOCK && pFile->locktype==RESERVED_LOCK)
00317   ){
00318     LockArea.lOffset = PENDING_BYTE;
00319     LockArea.lRange = 1L;
00320     UnlockArea.lOffset = 0L;
00321     UnlockArea.lRange = 0L;
00322 
00323     /* wait longer than LOCK_TIMEOUT here not to have to try multiple times */
00324     res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, 100L, 0L );
00325     if( res == NO_ERROR ){
00326       gotPendingLock = 1;
00327       OSTRACE3( "LOCK %d pending lock boolean set.  res=%d\n", pFile->h, res );
00328     }
00329   }
00330 
00331   /* Acquire a shared lock
00332   */
00333   if( locktype==SHARED_LOCK && res == NO_ERROR ){
00334     assert( pFile->locktype==NO_LOCK );
00335     res = getReadLock(pFile);
00336     if( res == NO_ERROR ){
00337       newLocktype = SHARED_LOCK;
00338     }
00339     OSTRACE3( "LOCK %d acquire shared lock. res=%d\n", pFile->h, res );
00340   }
00341 
00342   /* Acquire a RESERVED lock
00343   */
00344   if( locktype==RESERVED_LOCK && res == NO_ERROR ){
00345     assert( pFile->locktype==SHARED_LOCK );
00346     LockArea.lOffset = RESERVED_BYTE;
00347     LockArea.lRange = 1L;
00348     UnlockArea.lOffset = 0L;
00349     UnlockArea.lRange = 0L;
00350     res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
00351     if( res == NO_ERROR ){
00352       newLocktype = RESERVED_LOCK;
00353     }
00354     OSTRACE3( "LOCK %d acquire reserved lock. res=%d\n", pFile->h, res );
00355   }
00356 
00357   /* Acquire a PENDING lock
00358   */
00359   if( locktype==EXCLUSIVE_LOCK && res == NO_ERROR ){
00360     newLocktype = PENDING_LOCK;
00361     gotPendingLock = 0;
00362     OSTRACE2( "LOCK %d acquire pending lock. pending lock boolean unset.\n", pFile->h );
00363   }
00364 
00365   /* Acquire an EXCLUSIVE lock
00366   */
00367   if( locktype==EXCLUSIVE_LOCK && res == NO_ERROR ){
00368     assert( pFile->locktype>=SHARED_LOCK );
00369     res = unlockReadLock(pFile);
00370     OSTRACE2( "unreadlock = %d\n", res );
00371     LockArea.lOffset = SHARED_FIRST;
00372     LockArea.lRange = SHARED_SIZE;
00373     UnlockArea.lOffset = 0L;
00374     UnlockArea.lRange = 0L;
00375     res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
00376     if( res == NO_ERROR ){
00377       newLocktype = EXCLUSIVE_LOCK;
00378     }else{
00379       OSTRACE2( "OS/2 error-code = %d\n", res );
00380       getReadLock(pFile);
00381     }
00382     OSTRACE3( "LOCK %d acquire exclusive lock.  res=%d\n", pFile->h, res );
00383   }
00384 
00385   /* If we are holding a PENDING lock that ought to be released, then
00386   ** release it now.
00387   */
00388   if( gotPendingLock && locktype==SHARED_LOCK ){
00389     int r;
00390     LockArea.lOffset = 0L;
00391     LockArea.lRange = 0L;
00392     UnlockArea.lOffset = PENDING_BYTE;
00393     UnlockArea.lRange = 1L;
00394     r = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
00395     OSTRACE3( "LOCK %d unlocking pending/is shared. r=%d\n", pFile->h, r );
00396   }
00397 
00398   /* Update the state of the lock has held in the file descriptor then
00399   ** return the appropriate result code.
00400   */
00401   if( res == NO_ERROR ){
00402     rc = SQLITE_OK;
00403   }else{
00404     OSTRACE4( "LOCK FAILED %d trying for %d but got %d\n", pFile->h,
00405               locktype, newLocktype );
00406     rc = SQLITE_BUSY;
00407   }
00408   pFile->locktype = newLocktype;
00409   OSTRACE3( "LOCK %d now %d\n", pFile->h, pFile->locktype );
00410   return rc;
00411 }
00412 
00413 /*
00414 ** This routine checks if there is a RESERVED lock held on the specified
00415 ** file by this or any other process. If such a lock is held, return
00416 ** non-zero, otherwise zero.
00417 */
00418 static int os2CheckReservedLock( sqlite3_file *id, int *pOut ){
00419   int r = 0;
00420   os2File *pFile = (os2File*)id;
00421   assert( pFile!=0 );
00422   if( pFile->locktype>=RESERVED_LOCK ){
00423     r = 1;
00424     OSTRACE3( "TEST WR-LOCK %d %d (local)\n", pFile->h, r );
00425   }else{
00426     FILELOCK  LockArea,
00427               UnlockArea;
00428     APIRET rc = NO_ERROR;
00429     memset(&LockArea, 0, sizeof(LockArea));
00430     memset(&UnlockArea, 0, sizeof(UnlockArea));
00431     LockArea.lOffset = RESERVED_BYTE;
00432     LockArea.lRange = 1L;
00433     UnlockArea.lOffset = 0L;
00434     UnlockArea.lRange = 0L;
00435     rc = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
00436     OSTRACE3( "TEST WR-LOCK %d lock reserved byte rc=%d\n", pFile->h, rc );
00437     if( rc == NO_ERROR ){
00438       APIRET rcu = NO_ERROR; /* return code for unlocking */
00439       LockArea.lOffset = 0L;
00440       LockArea.lRange = 0L;
00441       UnlockArea.lOffset = RESERVED_BYTE;
00442       UnlockArea.lRange = 1L;
00443       rcu = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
00444       OSTRACE3( "TEST WR-LOCK %d unlock reserved byte r=%d\n", pFile->h, rcu );
00445     }
00446     r = !(rc == NO_ERROR);
00447     OSTRACE3( "TEST WR-LOCK %d %d (remote)\n", pFile->h, r );
00448   }
00449   *pOut = r;
00450   return SQLITE_OK;
00451 }
00452 
00453 /*
00454 ** Lower the locking level on file descriptor id to locktype.  locktype
00455 ** must be either NO_LOCK or SHARED_LOCK.
00456 **
00457 ** If the locking level of the file descriptor is already at or below
00458 ** the requested locking level, this routine is a no-op.
00459 **
00460 ** It is not possible for this routine to fail if the second argument
00461 ** is NO_LOCK.  If the second argument is SHARED_LOCK then this routine
00462 ** might return SQLITE_IOERR;
00463 */
00464 static int os2Unlock( sqlite3_file *id, int locktype ){
00465   int type;
00466   os2File *pFile = (os2File*)id;
00467   APIRET rc = SQLITE_OK;
00468   APIRET res = NO_ERROR;
00469   FILELOCK  LockArea,
00470             UnlockArea;
00471   memset(&LockArea, 0, sizeof(LockArea));
00472   memset(&UnlockArea, 0, sizeof(UnlockArea));
00473   assert( pFile!=0 );
00474   assert( locktype<=SHARED_LOCK );
00475   OSTRACE4( "UNLOCK %d to %d was %d\n", pFile->h, locktype, pFile->locktype );
00476   type = pFile->locktype;
00477   if( type>=EXCLUSIVE_LOCK ){
00478     LockArea.lOffset = 0L;
00479     LockArea.lRange = 0L;
00480     UnlockArea.lOffset = SHARED_FIRST;
00481     UnlockArea.lRange = SHARED_SIZE;
00482     res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
00483     OSTRACE3( "UNLOCK %d exclusive lock res=%d\n", pFile->h, res );
00484     if( locktype==SHARED_LOCK && getReadLock(pFile) != NO_ERROR ){
00485       /* This should never happen.  We should always be able to
00486       ** reacquire the read lock */
00487       OSTRACE3( "UNLOCK %d to %d getReadLock() failed\n", pFile->h, locktype );
00488       rc = SQLITE_IOERR_UNLOCK;
00489     }
00490   }
00491   if( type>=RESERVED_LOCK ){
00492     LockArea.lOffset = 0L;
00493     LockArea.lRange = 0L;
00494     UnlockArea.lOffset = RESERVED_BYTE;
00495     UnlockArea.lRange = 1L;
00496     res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
00497     OSTRACE3( "UNLOCK %d reserved res=%d\n", pFile->h, res );
00498   }
00499   if( locktype==NO_LOCK && type>=SHARED_LOCK ){
00500     res = unlockReadLock(pFile);
00501     OSTRACE5( "UNLOCK %d is %d want %d res=%d\n", pFile->h, type, locktype, res );
00502   }
00503   if( type>=PENDING_LOCK ){
00504     LockArea.lOffset = 0L;
00505     LockArea.lRange = 0L;
00506     UnlockArea.lOffset = PENDING_BYTE;
00507     UnlockArea.lRange = 1L;
00508     res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
00509     OSTRACE3( "UNLOCK %d pending res=%d\n", pFile->h, res );
00510   }
00511   pFile->locktype = locktype;
00512   OSTRACE3( "UNLOCK %d now %d\n", pFile->h, pFile->locktype );
00513   return rc;
00514 }
00515 
00516 /*
00517 ** Control and query of the open file handle.
00518 */
00519 static int os2FileControl(sqlite3_file *id, int op, void *pArg){
00520   switch( op ){
00521     case SQLITE_FCNTL_LOCKSTATE: {
00522       *(int*)pArg = ((os2File*)id)->locktype;
00523       OSTRACE3( "FCNTL_LOCKSTATE %d lock=%d\n", ((os2File*)id)->h, ((os2File*)id)->locktype );
00524       return SQLITE_OK;
00525     }
00526   }
00527   return SQLITE_ERROR;
00528 }
00529 
00530 /*
00531 ** Return the sector size in bytes of the underlying block device for
00532 ** the specified file. This is almost always 512 bytes, but may be
00533 ** larger for some devices.
00534 **
00535 ** SQLite code assumes this function cannot fail. It also assumes that
00536 ** if two files are created in the same file-system directory (i.e.
00537 ** a database and its journal file) that the sector size will be the
00538 ** same for both.
00539 */
00540 static int os2SectorSize(sqlite3_file *id){
00541   return SQLITE_DEFAULT_SECTOR_SIZE;
00542 }
00543 
00544 /*
00545 ** Return a vector of device characteristics.
00546 */
00547 static int os2DeviceCharacteristics(sqlite3_file *id){
00548   return 0;
00549 }
00550 
00551 
00552 /*
00553 ** Character set conversion objects used by conversion routines.
00554 */
00555 static UconvObject ucUtf8 = NULL; /* convert between UTF-8 and UCS-2 */
00556 static UconvObject uclCp = NULL;  /* convert between local codepage and UCS-2 */
00557 
00558 /*
00559 ** Helper function to initialize the conversion objects from and to UTF-8.
00560 */
00561 static void initUconvObjects( void ){
00562   if( UniCreateUconvObject( UTF_8, &ucUtf8 ) != ULS_SUCCESS )
00563     ucUtf8 = NULL;
00564   if ( UniCreateUconvObject( (UniChar *)L"@path=yes", &uclCp ) != ULS_SUCCESS )
00565     uclCp = NULL;
00566 }
00567 
00568 /*
00569 ** Helper function to free the conversion objects from and to UTF-8.
00570 */
00571 static void freeUconvObjects( void ){
00572   if ( ucUtf8 )
00573     UniFreeUconvObject( ucUtf8 );
00574   if ( uclCp )
00575     UniFreeUconvObject( uclCp );
00576   ucUtf8 = NULL;
00577   uclCp = NULL;
00578 }
00579 
00580 /*
00581 ** Helper function to convert UTF-8 filenames to local OS/2 codepage.
00582 ** The two-step process: first convert the incoming UTF-8 string
00583 ** into UCS-2 and then from UCS-2 to the current codepage.
00584 ** The returned char pointer has to be freed.
00585 */
00586 static char *convertUtf8PathToCp( const char *in ){
00587   UniChar tempPath[CCHMAXPATH];
00588   char *out = (char *)calloc( CCHMAXPATH, 1 );
00589 
00590   if( !out )
00591     return NULL;
00592 
00593   if( !ucUtf8 || !uclCp )
00594     initUconvObjects();
00595 
00596   /* determine string for the conversion of UTF-8 which is CP1208 */
00597   if( UniStrToUcs( ucUtf8, tempPath, (char *)in, CCHMAXPATH ) != ULS_SUCCESS )
00598     return out; /* if conversion fails, return the empty string */
00599 
00600   /* conversion for current codepage which can be used for paths */
00601   UniStrFromUcs( uclCp, out, tempPath, CCHMAXPATH );
00602 
00603   return out;
00604 }
00605 
00606 /*
00607 ** Helper function to convert filenames from local codepage to UTF-8.
00608 ** The two-step process: first convert the incoming codepage-specific
00609 ** string into UCS-2 and then from UCS-2 to the codepage of UTF-8.
00610 ** The returned char pointer has to be freed.
00611 **
00612 ** This function is non-static to be able to use this in shell.c and
00613 ** similar applications that take command line arguments.
00614 */
00615 char *convertCpPathToUtf8( const char *in ){
00616   UniChar tempPath[CCHMAXPATH];
00617   char *out = (char *)calloc( CCHMAXPATH, 1 );
00618 
00619   if( !out )
00620     return NULL;
00621 
00622   if( !ucUtf8 || !uclCp )
00623     initUconvObjects();
00624 
00625   /* conversion for current codepage which can be used for paths */
00626   if( UniStrToUcs( uclCp, tempPath, (char *)in, CCHMAXPATH ) != ULS_SUCCESS )
00627     return out; /* if conversion fails, return the empty string */
00628 
00629   /* determine string for the conversion of UTF-8 which is CP1208 */
00630   UniStrFromUcs( ucUtf8, out, tempPath, CCHMAXPATH );
00631 
00632   return out;
00633 }
00634 
00635 /*
00636 ** This vector defines all the methods that can operate on an
00637 ** sqlite3_file for os2.
00638 */
00639 static const sqlite3_io_methods os2IoMethod = {
00640   1,                        /* iVersion */
00641   os2Close,
00642   os2Read,
00643   os2Write,
00644   os2Truncate,
00645   os2Sync,
00646   os2FileSize,
00647   os2Lock,
00648   os2Unlock,
00649   os2CheckReservedLock,
00650   os2FileControl,
00651   os2SectorSize,
00652   os2DeviceCharacteristics
00653 };
00654 
00655 /***************************************************************************
00656 ** Here ends the I/O methods that form the sqlite3_io_methods object.
00657 **
00658 ** The next block of code implements the VFS methods.
00659 ****************************************************************************/
00660 
00661 /*
00662 ** Create a temporary file name in zBuf.  zBuf must be big enough to
00663 ** hold at pVfs->mxPathname characters.
00664 */
00665 static int getTempname(int nBuf, char *zBuf ){
00666   static const unsigned char zChars[] =
00667     "abcdefghijklmnopqrstuvwxyz"
00668     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
00669     "0123456789";
00670   int i, j;
00671   char zTempPathBuf[3];
00672   PSZ zTempPath = (PSZ)&zTempPathBuf;
00673   if( sqlite3_temp_directory ){
00674     zTempPath = sqlite3_temp_directory;
00675   }else{
00676     if( DosScanEnv( (PSZ)"TEMP", &zTempPath ) ){
00677       if( DosScanEnv( (PSZ)"TMP", &zTempPath ) ){
00678         if( DosScanEnv( (PSZ)"TMPDIR", &zTempPath ) ){
00679            ULONG ulDriveNum = 0, ulDriveMap = 0;
00680            DosQueryCurrentDisk( &ulDriveNum, &ulDriveMap );
00681            sprintf( (char*)zTempPath, "%c:", (char)( 'A' + ulDriveNum - 1 ) );
00682         }
00683       }
00684     }
00685   }
00686   /* Strip off a trailing slashes or backslashes, otherwise we would get *
00687    * multiple (back)slashes which causes DosOpen() to fail.              *
00688    * Trailing spaces are not allowed, either.                            */
00689   j = strlen(zTempPath);
00690   while( j > 0 && ( zTempPath[j-1] == '\\' || zTempPath[j-1] == '/'
00691                     || zTempPath[j-1] == ' ' ) ){
00692     j--;
00693   }
00694   zTempPath[j] = '\0';
00695   if( !sqlite3_temp_directory ){
00696     char *zTempPathUTF = convertCpPathToUtf8( zTempPath );
00697     sqlite3_snprintf( nBuf-30, zBuf,
00698                       "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPathUTF );
00699     free( zTempPathUTF );
00700   }else{
00701     sqlite3_snprintf( nBuf-30, zBuf,
00702                       "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath );
00703   }
00704   j = strlen( zBuf );
00705   sqlite3_randomness( 20, &zBuf[j] );
00706   for( i = 0; i < 20; i++, j++ ){
00707     zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
00708   }
00709   zBuf[j] = 0;
00710   OSTRACE2( "TEMP FILENAME: %s\n", zBuf );
00711   return SQLITE_OK;
00712 }
00713 
00714 
00715 /*
00716 ** Turn a relative pathname into a full pathname.  Write the full
00717 ** pathname into zFull[].  zFull[] will be at least pVfs->mxPathname
00718 ** bytes in size.
00719 */
00720 static int os2FullPathname(
00721   sqlite3_vfs *pVfs,          /* Pointer to vfs object */
00722   const char *zRelative,      /* Possibly relative input path */
00723   int nFull,                  /* Size of output buffer in bytes */
00724   char *zFull                 /* Output buffer */
00725 ){
00726   char *zRelativeCp = convertUtf8PathToCp( zRelative );
00727   char zFullCp[CCHMAXPATH] = "\0";
00728   char *zFullUTF;
00729   APIRET rc = DosQueryPathInfo( zRelativeCp, FIL_QUERYFULLNAME, zFullCp,
00730                                 CCHMAXPATH );
00731   free( zRelativeCp );
00732   zFullUTF = convertCpPathToUtf8( zFullCp );
00733   sqlite3_snprintf( nFull, zFull, zFullUTF );
00734   free( zFullUTF );
00735   return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
00736 }
00737 
00738 
00739 /*
00740 ** Open a file.
00741 */
00742 static int os2Open(
00743   sqlite3_vfs *pVfs,            /* Not used */
00744   const char *zName,            /* Name of the file */
00745   sqlite3_file *id,             /* Write the SQLite file handle here */
00746   int flags,                    /* Open mode flags */
00747   int *pOutFlags                /* Status return flags */
00748 ){
00749   HFILE h;
00750   ULONG ulFileAttribute = FILE_NORMAL;
00751   ULONG ulOpenFlags = 0;
00752   ULONG ulOpenMode = 0;
00753   os2File *pFile = (os2File*)id;
00754   APIRET rc = NO_ERROR;
00755   ULONG ulAction;
00756   char *zNameCp;
00757   char zTmpname[CCHMAXPATH+1];    /* Buffer to hold name of temp file */
00758 
00759   /* If the second argument to this function is NULL, generate a 
00760   ** temporary file name to use 
00761   */
00762   if( !zName ){
00763     int rc = getTempname(CCHMAXPATH+1, zTmpname);
00764     if( rc!=SQLITE_OK ){
00765       return rc;
00766     }
00767     zName = zTmpname;
00768   }
00769 
00770 
00771   memset( pFile, 0, sizeof(*pFile) );
00772 
00773   OSTRACE2( "OPEN want %d\n", flags );
00774 
00775   if( flags & SQLITE_OPEN_READWRITE ){
00776     ulOpenMode |= OPEN_ACCESS_READWRITE;
00777     OSTRACE1( "OPEN read/write\n" );
00778   }else{
00779     ulOpenMode |= OPEN_ACCESS_READONLY;
00780     OSTRACE1( "OPEN read only\n" );
00781   }
00782 
00783   if( flags & SQLITE_OPEN_CREATE ){
00784     ulOpenFlags |= OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW;
00785     OSTRACE1( "OPEN open new/create\n" );
00786   }else{
00787     ulOpenFlags |= OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW;
00788     OSTRACE1( "OPEN open existing\n" );
00789   }
00790 
00791   if( flags & SQLITE_OPEN_MAIN_DB ){
00792     ulOpenMode |= OPEN_SHARE_DENYNONE;
00793     OSTRACE1( "OPEN share read/write\n" );
00794   }else{
00795     ulOpenMode |= OPEN_SHARE_DENYWRITE;
00796     OSTRACE1( "OPEN share read only\n" );
00797   }
00798 
00799   if( flags & SQLITE_OPEN_DELETEONCLOSE ){
00800     char pathUtf8[CCHMAXPATH];
00801 #ifdef NDEBUG /* when debugging we want to make sure it is deleted */
00802     ulFileAttribute = FILE_HIDDEN;
00803 #endif
00804     os2FullPathname( pVfs, zName, CCHMAXPATH, pathUtf8 );
00805     pFile->pathToDel = convertUtf8PathToCp( pathUtf8 );
00806     OSTRACE1( "OPEN hidden/delete on close file attributes\n" );
00807   }else{
00808     pFile->pathToDel = NULL;
00809     OSTRACE1( "OPEN normal file attribute\n" );
00810   }
00811 
00812   /* always open in random access mode for possibly better speed */
00813   ulOpenMode |= OPEN_FLAGS_RANDOM;
00814   ulOpenMode |= OPEN_FLAGS_FAIL_ON_ERROR;
00815   ulOpenMode |= OPEN_FLAGS_NOINHERIT;
00816 
00817   zNameCp = convertUtf8PathToCp( zName );
00818   rc = DosOpen( (PSZ)zNameCp,
00819                 &h,
00820                 &ulAction,
00821                 0L,
00822                 ulFileAttribute,
00823                 ulOpenFlags,
00824                 ulOpenMode,
00825                 (PEAOP2)NULL );
00826   free( zNameCp );
00827   if( rc != NO_ERROR ){
00828     OSTRACE7( "OPEN Invalid handle rc=%d: zName=%s, ulAction=%#lx, ulAttr=%#lx, ulFlags=%#lx, ulMode=%#lx\n",
00829               rc, zName, ulAction, ulFileAttribute, ulOpenFlags, ulOpenMode );
00830     if( pFile->pathToDel )
00831       free( pFile->pathToDel );
00832     pFile->pathToDel = NULL;
00833     if( flags & SQLITE_OPEN_READWRITE ){
00834       OSTRACE2( "OPEN %d Invalid handle\n", ((flags | SQLITE_OPEN_READONLY) & ~SQLITE_OPEN_READWRITE) );
00835       return os2Open( pVfs, zName, id,
00836                       ((flags | SQLITE_OPEN_READONLY) & ~SQLITE_OPEN_READWRITE),
00837                       pOutFlags );
00838     }else{
00839       return SQLITE_CANTOPEN;
00840     }
00841   }
00842 
00843   if( pOutFlags ){
00844     *pOutFlags = flags & SQLITE_OPEN_READWRITE ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY;
00845   }
00846 
00847   pFile->pMethod = &os2IoMethod;
00848   pFile->h = h;
00849   OpenCounter(+1);
00850   OSTRACE3( "OPEN %d pOutFlags=%d\n", pFile->h, pOutFlags );
00851   return SQLITE_OK;
00852 }
00853 
00854 /*
00855 ** Delete the named file.
00856 */
00857 static int os2Delete(
00858   sqlite3_vfs *pVfs,                     /* Not used on os2 */
00859   const char *zFilename,                 /* Name of file to delete */
00860   int syncDir                            /* Not used on os2 */
00861 ){
00862   APIRET rc = NO_ERROR;
00863   char *zFilenameCp = convertUtf8PathToCp( zFilename );
00864   SimulateIOError( return SQLITE_IOERR_DELETE );
00865   rc = DosDelete( (PSZ)zFilenameCp );
00866   free( zFilenameCp );
00867   OSTRACE2( "DELETE \"%s\"\n", zFilename );
00868   return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR_DELETE;
00869 }
00870 
00871 /*
00872 ** Check the existance and status of a file.
00873 */
00874 static int os2Access(
00875   sqlite3_vfs *pVfs,        /* Not used on os2 */
00876   const char *zFilename,    /* Name of file to check */
00877   int flags,                /* Type of test to make on this file */
00878   int *pOut                 /* Write results here */
00879 ){
00880   FILESTATUS3 fsts3ConfigInfo;
00881   APIRET rc = NO_ERROR;
00882   char *zFilenameCp = convertUtf8PathToCp( zFilename );
00883 
00884   memset( &fsts3ConfigInfo, 0, sizeof(fsts3ConfigInfo) );
00885   rc = DosQueryPathInfo( (PSZ)zFilenameCp, FIL_STANDARD,
00886                          &fsts3ConfigInfo, sizeof(FILESTATUS3) );
00887   free( zFilenameCp );
00888   OSTRACE4( "ACCESS fsts3ConfigInfo.attrFile=%d flags=%d rc=%d\n",
00889             fsts3ConfigInfo.attrFile, flags, rc );
00890   switch( flags ){
00891     case SQLITE_ACCESS_READ:
00892     case SQLITE_ACCESS_EXISTS:
00893       rc = (rc == NO_ERROR);
00894       OSTRACE3( "ACCESS %s access of read and exists  rc=%d\n", zFilename, rc );
00895       break;
00896     case SQLITE_ACCESS_READWRITE:
00897       rc = (rc == NO_ERROR) && ( (fsts3ConfigInfo.attrFile & FILE_READONLY) == 0 );
00898       OSTRACE3( "ACCESS %s access of read/write  rc=%d\n", zFilename, rc );
00899       break;
00900     default:
00901       assert( !"Invalid flags argument" );
00902   }
00903   *pOut = rc;
00904   return SQLITE_OK;
00905 }
00906 
00907 
00908 #ifndef SQLITE_OMIT_LOAD_EXTENSION
00909 /*
00910 ** Interfaces for opening a shared library, finding entry points
00911 ** within the shared library, and closing the shared library.
00912 */
00913 /*
00914 ** Interfaces for opening a shared library, finding entry points
00915 ** within the shared library, and closing the shared library.
00916 */
00917 static void *os2DlOpen(sqlite3_vfs *pVfs, const char *zFilename){
00918   UCHAR loadErr[256];
00919   HMODULE hmod;
00920   APIRET rc;
00921   char *zFilenameCp = convertUtf8PathToCp(zFilename);
00922   rc = DosLoadModule((PSZ)loadErr, sizeof(loadErr), zFilenameCp, &hmod);
00923   free(zFilenameCp);
00924   return rc != NO_ERROR ? 0 : (void*)hmod;
00925 }
00926 /*
00927 ** A no-op since the error code is returned on the DosLoadModule call.
00928 ** os2Dlopen returns zero if DosLoadModule is not successful.
00929 */
00930 static void os2DlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){
00931 /* no-op */
00932 }
00933 static void *os2DlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol){
00934   PFN pfn;
00935   APIRET rc;
00936   rc = DosQueryProcAddr((HMODULE)pHandle, 0L, zSymbol, &pfn);
00937   if( rc != NO_ERROR ){
00938     /* if the symbol itself was not found, search again for the same
00939      * symbol with an extra underscore, that might be needed depending
00940      * on the calling convention */
00941     char _zSymbol[256] = "_";
00942     strncat(_zSymbol, zSymbol, 255);
00943     rc = DosQueryProcAddr((HMODULE)pHandle, 0L, _zSymbol, &pfn);
00944   }
00945   return rc != NO_ERROR ? 0 : (void*)pfn;
00946 }
00947 static void os2DlClose(sqlite3_vfs *pVfs, void *pHandle){
00948   DosFreeModule((HMODULE)pHandle);
00949 }
00950 #else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */
00951   #define os2DlOpen 0
00952   #define os2DlError 0
00953   #define os2DlSym 0
00954   #define os2DlClose 0
00955 #endif
00956 
00957 
00958 /*
00959 ** Write up to nBuf bytes of randomness into zBuf.
00960 */
00961 static int os2Randomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf ){
00962   ULONG sizeofULong = sizeof(ULONG);
00963   int n = 0;
00964   if( sizeof(DATETIME) <= nBuf - n ){
00965     DATETIME x;
00966     DosGetDateTime(&x);
00967     memcpy(&zBuf[n], &x, sizeof(x));
00968     n += sizeof(x);
00969   }
00970 
00971   if( sizeofULong <= nBuf - n ){
00972     PPIB ppib;
00973     DosGetInfoBlocks(NULL, &ppib);
00974     memcpy(&zBuf[n], &ppib->pib_ulpid, sizeofULong);
00975     n += sizeofULong;
00976   }
00977 
00978   if( sizeofULong <= nBuf - n ){
00979     PTIB ptib;
00980     DosGetInfoBlocks(&ptib, NULL);
00981     memcpy(&zBuf[n], &ptib->tib_ptib2->tib2_ultid, sizeofULong);
00982     n += sizeofULong;
00983   }
00984 
00985   /* if we still haven't filled the buffer yet the following will */
00986   /* grab everything once instead of making several calls for a single item */
00987   if( sizeofULong <= nBuf - n ){
00988     ULONG ulSysInfo[QSV_MAX];
00989     DosQuerySysInfo(1L, QSV_MAX, ulSysInfo, sizeofULong * QSV_MAX);
00990 
00991     memcpy(&zBuf[n], &ulSysInfo[QSV_MS_COUNT - 1], sizeofULong);
00992     n += sizeofULong;
00993 
00994     if( sizeofULong <= nBuf - n ){
00995       memcpy(&zBuf[n], &ulSysInfo[QSV_TIMER_INTERVAL - 1], sizeofULong);
00996       n += sizeofULong;
00997     }
00998     if( sizeofULong <= nBuf - n ){
00999       memcpy(&zBuf[n], &ulSysInfo[QSV_TIME_LOW - 1], sizeofULong);
01000       n += sizeofULong;
01001     }
01002     if( sizeofULong <= nBuf - n ){
01003       memcpy(&zBuf[n], &ulSysInfo[QSV_TIME_HIGH - 1], sizeofULong);
01004       n += sizeofULong;
01005     }
01006     if( sizeofULong <= nBuf - n ){
01007       memcpy(&zBuf[n], &ulSysInfo[QSV_TOTAVAILMEM - 1], sizeofULong);
01008       n += sizeofULong;
01009     }
01010   }
01011 
01012   return n;
01013 }
01014 
01015 /*
01016 ** Sleep for a little while.  Return the amount of time slept.
01017 ** The argument is the number of microseconds we want to sleep.
01018 ** The return value is the number of microseconds of sleep actually
01019 ** requested from the underlying operating system, a number which
01020 ** might be greater than or equal to the argument, but not less
01021 ** than the argument.
01022 */
01023 static int os2Sleep( sqlite3_vfs *pVfs, int microsec ){
01024   DosSleep( (microsec/1000) );
01025   return microsec;
01026 }
01027 
01028 /*
01029 ** The following variable, if set to a non-zero value, becomes the result
01030 ** returned from sqlite3OsCurrentTime().  This is used for testing.
01031 */
01032 #ifdef SQLITE_TEST
01033 int sqlite3_current_time = 0;
01034 #endif
01035 
01036 /*
01037 ** Find the current time (in Universal Coordinated Time).  Write the
01038 ** current time and date as a Julian Day number into *prNow and
01039 ** return 0.  Return 1 if the time and date cannot be found.
01040 */
01041 int os2CurrentTime( sqlite3_vfs *pVfs, double *prNow ){
01042   double now;
01043   SHORT minute; /* needs to be able to cope with negative timezone offset */
01044   USHORT second, hour,
01045          day, month, year;
01046   DATETIME dt;
01047   DosGetDateTime( &dt );
01048   second = (USHORT)dt.seconds;
01049   minute = (SHORT)dt.minutes + dt.timezone;
01050   hour = (USHORT)dt.hours;
01051   day = (USHORT)dt.day;
01052   month = (USHORT)dt.month;
01053   year = (USHORT)dt.year;
01054 
01055   /* Calculations from http://www.astro.keele.ac.uk/~rno/Astronomy/hjd.html
01056      http://www.astro.keele.ac.uk/~rno/Astronomy/hjd-0.1.c */
01057   /* Calculate the Julian days */
01058   now = day - 32076 +
01059     1461*(year + 4800 + (month - 14)/12)/4 +
01060     367*(month - 2 - (month - 14)/12*12)/12 -
01061     3*((year + 4900 + (month - 14)/12)/100)/4;
01062 
01063   /* Add the fractional hours, mins and seconds */
01064   now += (hour + 12.0)/24.0;
01065   now += minute/1440.0;
01066   now += second/86400.0;
01067   *prNow = now;
01068 #ifdef SQLITE_TEST
01069   if( sqlite3_current_time ){
01070     *prNow = sqlite3_current_time/86400.0 + 2440587.5;
01071   }
01072 #endif
01073   return 0;
01074 }
01075 
01076 static int os2GetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
01077   return 0;
01078 }
01079 
01080 /*
01081 ** Initialize and deinitialize the operating system interface.
01082 */
01083 int sqlite3_os_init(void){
01084   static sqlite3_vfs os2Vfs = {
01085     1,                 /* iVersion */
01086     sizeof(os2File),   /* szOsFile */
01087     CCHMAXPATH,        /* mxPathname */
01088     0,                 /* pNext */
01089     "os2",             /* zName */
01090     0,                 /* pAppData */
01091 
01092     os2Open,           /* xOpen */
01093     os2Delete,         /* xDelete */
01094     os2Access,         /* xAccess */
01095     os2FullPathname,   /* xFullPathname */
01096     os2DlOpen,         /* xDlOpen */
01097     os2DlError,        /* xDlError */
01098     os2DlSym,          /* xDlSym */
01099     os2DlClose,        /* xDlClose */
01100     os2Randomness,     /* xRandomness */
01101     os2Sleep,          /* xSleep */
01102     os2CurrentTime,    /* xCurrentTime */
01103     os2GetLastError    /* xGetLastError */
01104   };
01105   sqlite3_vfs_register(&os2Vfs, 1);
01106   initUconvObjects();
01107   return SQLITE_OK;
01108 }
01109 int sqlite3_os_end(void){
01110   freeUconvObjects();
01111   return SQLITE_OK;
01112 }
01113 
01114 #endif /* SQLITE_OS_OS2 */

ContextLogger2—ContextLogger2 Logger Daemon Internals—Generated on Mon May 2 13:49:55 2011 by Doxygen 1.6.1