Index: src/os.c ================================================================== --- src/os.c +++ src/os.c @@ -338,5 +338,237 @@ sqlite3_mutex_enter(mutex); vfsUnlink(pVfs); sqlite3_mutex_leave(mutex); return SQLITE_OK; } + +#ifndef SQLITE_OMIT_VFS_STDIO +/***************************************************************************** +** The remainder of this file contains a simplified stdio-like interface +** to the VFS layer. +*/ + +/* +** An instance of the following object records the state of an +** open file. This object is opaque to all users - the internal +** structure is only visible to the functions below. +*/ +struct sqlite3_FILE { + char *zFilename; /* Full pathname of the open file */ + sqlite3_int64 iOfst; /* Current offset into the file */ + sqlite3_vfs *pVfs; /* The VFS used for this file */ + u8 alwaysAppend; /* Always append if true */ + sqlite3_file sFile; /* Open file. MUST BE LAST */ +}; + +/* +** This is a helper routine used to translate a URI into a full pathname +** and a pointer to the appropriate VFS. +*/ +static int getFilename(const char *zURI, sqlite3_vfs **ppVfs, char **pzName){ + int rc; + char *zOpen = 0; + char *zFullname = 0; + unsigned int flags; + char *zErrmsg = 0; + sqlite3_vfs *pVfs = 0; + + rc = sqlite3ParseUri(0, zURI, &flags, &pVfs, &zOpen, &zErrmsg); + sqlite3_free(zErrmsg); + if( rc ) goto getFilename_error; + zFullname = sqlite3_malloc( pVfs->mxPathname+1 ); + if( zFullname==0 ){ rc = SQLITE_NOMEM; goto getFilename_error; } + rc = pVfs->xFullPathname(pVfs, zOpen, pVfs->mxPathname, zFullname); + if( rc ) goto getFilename_error; + sqlite3_free(zOpen); + zOpen = 0; + *pzName = sqlite3_realloc(zFullname, sqlite3Strlen30(zFullname)+1); + if( *pzName==0 ) goto getFilename_error; + zFullname = 0; + *ppVfs = pVfs; + return SQLITE_OK; + +getFilename_error: + sqlite3_free(zOpen); + sqlite3_free(zFullname); + *pzName = 0; + *ppVfs = 0; + return rc; +} + +/* +** Open a file for stdio-like reading and writing. The file is identified +** by the URI in the first parameter. The access mode can be "r", "r+", +** "w", "w+", "a", or "a+" with the usual meanings. +** +** On success, a pointer to a new sqlite3_FILE object is returned. On +** failure, NULL is returned. Unfortunately, there is no way to recover +** detailed error information after a failure. +*/ +sqlite3_FILE *sqlite3_fopen(const char *zURI, const char *zMode){ + char *zFile = 0; + sqlite3_vfs *pVfs = 0; + int rc; + int openFlags; + int doTruncate = 0; + int seekEnd = 0; + int alwaysAppend = 0; + int nToAlloc; + sqlite3_FILE *p; + + if( zMode[0]==0 ) return 0; + if( zMode[0]=='r' ){ + if( zMode[1]=='+' ){ + openFlags = SQLITE_OPEN_READWRITE; + }else{ + openFlags = SQLITE_OPEN_READONLY; + } + }else if( zMode[0]=='w' ){ + openFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; + doTruncate = 1; + }else if( zMode[0]=='a' ){ + openFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; + if( zMode[1]=='+' ){ + alwaysAppend = 1; + }else{ + seekEnd = 1; + } + }else{ + return 0; + } + rc = getFilename(zURI, &pVfs, &zFile); + if( rc ) return 0; + nToAlloc = sizeof(*p) + ROUND8(pVfs->szOsFile); + p = sqlite3_malloc( nToAlloc ); + if( p==0 ){ + sqlite3_free(zFile); + return 0; + } + memset(p, 0, nToAlloc); + p->zFilename = zFile; + rc = pVfs->xOpen(pVfs, zFile, &p->sFile, openFlags, &openFlags); + if( rc!=SQLITE_OK ){ + sqlite3_free(zFile); + sqlite3_free(p); + return 0; + } + p->pVfs = pVfs; + p->alwaysAppend = alwaysAppend; + if( seekEnd ) sqlite3_fseek(p, 0, SQLITE_SEEK_END); + if( doTruncate ) sqlite3_ftruncate(p, 0); + return p; +} + +/* +** Close a file perviously opened by sqlite3_fopen(). +*/ +int sqlite3_fclose(sqlite3_FILE *p){ + p->sFile.pMethods->xClose(&p->sFile); + sqlite3_free(p); + return SQLITE_OK; +} + +/* +** Read iAmt bytes from the file p into pBuf. +** +** Return 0 on success or an error code if the full amount could +** not be read. +*/ +int sqlite3_fread( + void *pBuf, /* Write content read into this buffer */ + sqlite3_int64 iAmt, /* Number of bytes to read */ + sqlite3_FILE *p /* Read from this file */ +){ + int rc = p->sFile.pMethods->xRead(&p->sFile, pBuf, iAmt, p->iOfst); + if( rc==SQLITE_OK ){ + p->iOfst += iAmt; + } + return rc; +} + +/* +** Write iAmt bytes from buffer pBuf into the file p. +** +** Return 0 on success or an error code if anything goes wrong. +*/ +int sqlite3_fwrite( + const void *pBuf, /* Take content to be written from this buffer */ + sqlite3_int64 iAmt, /* Number of bytes to write */ + sqlite3_FILE *p /* Write into this file */ +){ + int rc; + + if( p->alwaysAppend ) sqlite3_fseek(p, 0, SQLITE_SEEK_END); + rc = p->sFile.pMethods->xWrite(&p->sFile, pBuf, iAmt, p->iOfst); + if( rc==SQLITE_OK ){ + p->iOfst += iAmt; + } + return rc; +} + +/* +** Truncate an open file to newSize bytes. +*/ +int sqlite3_ftruncate(sqlite3_FILE *p, sqlite3_int64 newSize){ + int rc; + rc = p->sFile.pMethods->xTruncate(&p->sFile, newSize); + return rc; +} + +/* +** Return the current position of the file pointer. +*/ +sqlite3_int64 sqlite3_ftell(sqlite3_FILE *p){ + return p->iOfst; +} + +/* +** Move the file pointer to a new position in the file. +*/ +int sqlite3_fseek(sqlite3_FILE *p, sqlite3_int64 ofst, int whence){ + int rc = SQLITE_OK; + if( whence==SQLITE_SEEK_SET ){ + p->iOfst = ofst; + }else if( whence==SQLITE_SEEK_CUR ){ + p->iOfst += ofst; + }else{ + sqlite3_int64 iCur = 0; + rc = p->sFile.pMethods->xFileSize(&p->sFile, &iCur); + if( rc==SQLITE_OK ){ + p->iOfst = iCur + ofst; + } + } + return rc; +} + +/* +** Rewind the file pointer to the beginning of the file. +*/ +int sqlite3_rewind(sqlite3_FILE *p){ + p->iOfst = 0; + return SQLITE_OK; +} + +/* +** Flush the content of OS cache buffers to disk. (fsync()) +*/ +int sqlite3_fflush(sqlite3_FILE *p){ + return p->sFile.pMethods->xSync(&p->sFile, SQLITE_SYNC_NORMAL); +} + +/* +** Delete the file identified by the URI in the first parameter +*/ +int sqlite3_remove(const char *zURI){ + sqlite3_vfs *pVfs = 0; + char *zFilename = 0; + int rc; + + rc = getFilename(zURI, &pVfs, &zFilename); + if( rc==SQLITE_OK ){ + rc = pVfs->xDelete(pVfs, zFilename, 0); + } + sqlite3_free(zFilename); + return rc; +} + +#endif /* SQLITE_OMIT_VFS_STDIO */ Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -570,10 +570,14 @@ ** implementations will ** want to subclass this object by appending additional fields ** for their own use. The pMethods entry is a pointer to an ** [sqlite3_io_methods] object that defines methods for performing ** I/O operations on the open file. +** +** Do not confuse the low-level sqlite3_file object described here +** and used by the [VFS] with the high-level [sqlite3_FILE object] +** that provides a stdio-like interface for use by application programs. */ typedef struct sqlite3_file sqlite3_file; struct sqlite3_file { const struct sqlite3_io_methods *pMethods; /* Methods for an open file */ }; @@ -6821,11 +6825,200 @@ /* #define SQLITE_IGNORE 2 // Also used by sqlite3_authorizer() callback */ #define SQLITE_FAIL 3 /* #define SQLITE_ABORT 4 // Also an error code */ #define SQLITE_REPLACE 5 +/* +** CAPI3REF: Simplified Interface To The VFS +** KEYWORDS: *sqlite3_FILE {*sqlite3_FILE object} +** +** An instance of this object represents an open file in a [VFS]. +** This object is used by simplified I/O routines such as +** [sqlite3_fopen()] and [sqlite3_fread()] and [sqlite3_fwrite()]. +** +** The sqlite3_FILE object provides a simple means to access +** the filesystem through an SQLite [VFS]. This allows, for example, +** ordinary file I/O to occur through VFS shims such as the quota or +** multiplexor or vfstrace, or for I/O to occur on specialized +** application-specific VFSes. The sqlite3_FILE object also provides +** a safe way for the application to read directly from an SQLite database +** file, without the risk of cancelling posix advisory locks when the +** connection is closed, as would happen using stdio or direct OS +** I/O interfaces. +** +** The simplified I/O routines a similar to the "fopen()" family +** of I/O routines in the standard C library, though there are some +** minor differences. Note in particular that [sqlite3_fread()] and +** [sqlite3_fwrite()] have 3 instead of 4 parameters and return a +** success code rather than the number of elements read or written. +** Also, standard C library numeric datatypes such as size_t and off_t are +** replaced in this interface with the 64-bit signed integer type +** [sqlite3_int64]. +** +** The sqlite3_FILE object is not intended to be a general-purpose +** replacement for stdio, but it can be very useful in some +** circumstances. +** +** Do not confuse the high-level sqlite3_FILE object described here +** with the low-level [sqlite3_file] object used by the [VFS]. +** +** See also: +** [sqlite3_fopen()], +** [sqlite3_fread()], +** [sqlite3_fwrite()], +** [sqlite3_fseek()], +** [sqlite3_ftell()], +** [sqlite3_rewind()], +** [sqlite3_fflush()], +** [sqlite3_ftruncate()], +** [sqlite3_close()], +** [sqlite3_remove()]. +*/ +typedef struct sqlite3_FILE sqlite3_FILE; + +/* +** CAPI3REF: Open A File Object +** +** The sqlite3_fopen(F,M) routine opens an [sqlite3_FILE object] on the +** the file identified by [URI filename] F with mode M. Return a pointer +** to the [sqlite3_FILE object] created, or a NULL pointer if the open failed. +** +** The mode M can be one of the following: +** +**
+**
"r" +**
Open the file read-only. Fail if the file does not already exist. +**
"r+" +**
Open the file read and writing, but fail if the file does not +** already exist. +**
"w" +**
Open for reading and writing. Create the file if it does not +** already exist. If the file does already exist, truncate it to zero +** bytes in size immediately after opening. +**
"w+" +**
This works the same as "w". +**
"a" +**
Open for reading and writing. Create the file if it does not +** already exist. Set the file cursor to the end of the file so that +** any write operations that occur prior to an [sqlite3_fseek()] or +** [sqlite3_rewind()] will append to the file. +**
"a+" +**
Open for reading and writing. Create the file if it does not +** already exist. The file cursor is positioned at the beginning of +** the file for reading purposes. However, any writes on the file +** cause the file cursor to move to the end of the file prior to the +** write (and thus append to the file). +**
+** +** Any value for the mode M other than those listed above cause the +** sqlite3_fopen() call to fail. +** +** See also: +** [sqlite3_fread()], +** [sqlite3_fwrite()], +** [sqlite3_fseek()], +** [sqlite3_ftell()], +** [sqlite3_rewind()], +** [sqlite3_fflush()], +** [sqlite3_close()], +** [sqlite3_remove()]. +*/ +sqlite3_FILE *sqlite3_fopen(const char *zURI, const char *zMode); + +/* +** CAPI3REF: Read and Write A File Object +** +** The [sqlite3_fread(B,N,P)] routine reads N bytes of content from +** [sqlite3_FILE object] P and into buffer B. +** The [sqlite3_fwrite(B,N,P)] routine writes N bytes of content from +** buffer B into [sqlite3_FILE object] P. Both routines return +** SQLITE_OK on success or an [error code] on failure. +** +** Reading and writing always occur beginning at the file cursor. The file +** cursor is advanced by N if the read or write is successful and unchanged +** if the read is not successful. Not that the file cursor is +** unchanged by a partial read ([SQLITE_IOERR_SHORT_READ]). +** If the mode from [sqlite3_fopen()] was "a+" then the file cursor is +** always moved to the end of the file prior to every write. +** +** Note that these routines differ from stdio fread() and fwrite() +** functions in two significant ways: These routines use 3 parameters +** instead of 4; these routines always assume an element size of one byte. +** And these routines return a success code, not the number of elements +** read or written. With the routines described here, and unlike +** fread() and fwrite(), a partial read or write is considered an error. +*/ +int sqlite3_fread(void*, sqlite3_int64, sqlite3_FILE*); +int sqlite3_fwrite(const void*, sqlite3_int64, sqlite3_FILE*); + +/* +** CAPI3REF: Whence Parameter For sqlite3_fseek() +** +** The 3rd parameter to [sqlite3_fseek(P,N,W)] can take on any of the +** following values. +*/ +#define SQLITE_SEEK_SET 0 +#define SQLITE_SEEK_CUR 1 +#define SQLITE_SEEK_END 2 + +/* +** CAPI3REF: Position The Cursor For An sqlite3_FILE object +** +** This routines move the current cursor position in an [sqlite3_FILE object]. +** The sqlite3_fseek(P,N,W) call move the [sqlite3_FILE object] P to a new +** position N relative to W. W is [SQLITE_SEEK_SET] for the beginning of +** the file, [SQLITE_SEEK_CUR] for the cursor position prior to the current +** move, or [SQLITE_SEEK_END] for the end of the file. +** +** The sqlite3_rewind(P) call move the cursor position to the beginning +** of the file. It is equivalent to sqlite3_fseek(P,0,SQLITE_SEEK_SET). +** +** The sqlite3_ftell(P) call returns the current cursor position. +*/ +int sqlite3_fseek(sqlite3_FILE*, sqlite3_int64, int whence); +int sqlite3_rewind(sqlite3_FILE*); +sqlite3_int64 sqlite3_ftell(sqlite3_FILE*); + +/* +** CAPI3REF: Flush OS File Cache Buffers +** +** A call to sqlite3_fflush(P) causes any writes previously made against +** [sqlite3_FILE object] P to be flushed from the operating-system cache +** to nonvolatile storage. +*/ +int sqlite3_fflush(sqlite3_FILE*); + +/* +** CAPI3REF: Truncate An sqlite3_FILE object +** +** The sqlite3_ftruncate(P,N) interface calls the [sqlite3_FILE object] P +** to be truncated to N bytes in size. +** +** It is undefined what happens if N is larger than the current file size. +** Some [VFS] implementations will extend the file. Others will fail. +** Still others will leave the file in an inconsistent state. +*/ +int sqlite3_ftruncate(sqlite3_FILE*, sqlite3_int64); + +/* +** CAPI3REF: Close an sqlite3_FILE object +** +** Every successful call to [sqlite3_fopen()] should be eventually followed +** by a call to this routine to destroy the [sqlite3_FILE object] that +** [sqlite3_fopen()] created. After calling this routine, the +** [sqlite3_FILE object] must not be used again. +*/ +int sqlite3_fclose(sqlite3_FILE*); +/* +** CAPI3REF: Remove A File +** +** The sqlite3_remove(U) interface attempts to delete the file identified +** by the [URI filename] U. Query parameters on U can specify a particular +** [VFS] to use to do the deletion. +*/ +int sqlite3_remove(const char *zURI); /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */