more writable vfs stuff
This commit is contained in:
parent
9a7af0f435
commit
5496c4a9ec
|
|
@ -224,11 +224,15 @@ class file:
|
|||
xreadlines = readlines
|
||||
|
||||
def seek(self, offset, whence = 0):
|
||||
print "seek(%s)" % (offset)
|
||||
if self.__stream:
|
||||
print "clear"
|
||||
self.__stream.clear() # clear eof flag
|
||||
if self.__reader:
|
||||
print "seekg"
|
||||
self.__stream.seekg(offset, whence)
|
||||
if self.__writer:
|
||||
print "seekp"
|
||||
self.__stream.seekp(offset, whence)
|
||||
|
||||
def tell(self):
|
||||
|
|
|
|||
|
|
@ -216,6 +216,42 @@ seek_write(streamsize pos, const char *buffer, streamsize num_bytes,
|
|||
release();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: OStreamWrapper::seek_eof_write
|
||||
// Access: Public
|
||||
// Description: Atomically seeks to the end of the file, and writes a
|
||||
// number of bytes to the stream. Returns whether a
|
||||
// failure condition was detected by the operation.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void OStreamWrapper::
|
||||
seek_eof_write(const char *buffer, streamsize num_bytes, bool &fail) {
|
||||
acquire();
|
||||
_ostream->clear();
|
||||
_ostream->seekp(0, ios::end);
|
||||
_ostream->write(buffer, num_bytes);
|
||||
fail = _ostream->fail();
|
||||
release();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: OStreamWrapper::seek_ppos_eof
|
||||
// Access: Public
|
||||
// Description: Atomically seeks to EOF and returns the ppos there;
|
||||
// that is, returns the file size. Note that the EOF
|
||||
// might have been moved in another thread by the time
|
||||
// this method returns.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
streamsize OStreamWrapper::
|
||||
seek_ppos_eof() {
|
||||
streamsize pos;
|
||||
acquire();
|
||||
_ostream->seekp(0, ios::end);
|
||||
pos = _ostream->tellp();
|
||||
release();
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: StreamWrapper::Destructor
|
||||
// Access: Published
|
||||
|
|
|
|||
|
|
@ -91,7 +91,9 @@ public:
|
|||
void write(const char *buffer, streamsize num_bytes);
|
||||
void write(const char *buffer, streamsize num_bytes, bool &fail);
|
||||
void seek_write(streamsize pos, const char *buffer, streamsize num_bytes, bool &fail);
|
||||
void seek_eof_write(const char *buffer, streamsize num_bytes, bool &fail);
|
||||
INLINE bool put(char c);
|
||||
streamsize seek_ppos_eof();
|
||||
|
||||
private:
|
||||
ostream *_ostream;
|
||||
|
|
|
|||
|
|
@ -234,11 +234,9 @@ collapse_equivalent_materials() {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
bool EggData::
|
||||
write_egg(Filename filename) {
|
||||
filename.unlink();
|
||||
filename.set_text();
|
||||
|
||||
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
||||
|
||||
filename.set_text();
|
||||
vfs->delete_file(filename);
|
||||
ostream *file = vfs->open_write_file(filename, true, true);
|
||||
if (file == (ostream *)NULL) {
|
||||
egg_cat.error() << "Unable to open " << filename << " for writing.\n";
|
||||
|
|
|
|||
|
|
@ -63,8 +63,10 @@
|
|||
virtualFile.I virtualFileList.I virtualFileList.h virtualFileMount.h \
|
||||
virtualFileComposite.h virtualFileComposite.I virtualFile.h \
|
||||
virtualFileMount.I virtualFileMountMultifile.h \
|
||||
virtualFileMountMultifile.I virtualFileMountSystem.h \
|
||||
virtualFileMountSystem.I virtualFileSimple.h virtualFileSimple.I \
|
||||
virtualFileMountMultifile.I \
|
||||
virtualFileMountRamdisk.h virtualFileMountRamdisk.I \
|
||||
virtualFileMountSystem.h virtualFileMountSystem.I \
|
||||
virtualFileSimple.h virtualFileSimple.I \
|
||||
virtualFileSystem.h virtualFileSystem.I \
|
||||
weakPointerCallback.I weakPointerCallback.h \
|
||||
weakPointerTo.I weakPointerTo.h \
|
||||
|
|
@ -116,7 +118,9 @@
|
|||
vector_uchar.cxx vector_float.cxx \
|
||||
virtualFileComposite.cxx virtualFile.cxx virtualFileList.cxx \
|
||||
virtualFileMount.cxx \
|
||||
virtualFileMountMultifile.cxx virtualFileMountSystem.cxx \
|
||||
virtualFileMountMultifile.cxx \
|
||||
virtualFileMountRamdisk.cxx \
|
||||
virtualFileMountSystem.cxx \
|
||||
virtualFileSimple.cxx virtualFileSystem.cxx \
|
||||
weakPointerCallback.cxx \
|
||||
weakPointerTo.cxx \
|
||||
|
|
@ -180,8 +184,10 @@
|
|||
virtualFile.I virtualFileList.I virtualFileList.h virtualFileMount.h \
|
||||
virtualFileComposite.h virtualFileComposite.I virtualFile.h \
|
||||
virtualFileMount.I virtualFileMountMultifile.h \
|
||||
virtualFileMountMultifile.I virtualFileMountSystem.h \
|
||||
virtualFileMountSystem.I virtualFileSimple.h virtualFileSimple.I \
|
||||
virtualFileMountMultifile.I \
|
||||
virtualFileMountRamdisk.h virtualFileMountRamdisk.I \
|
||||
virtualFileMountSystem.h virtualFileMountSystem.I \
|
||||
virtualFileSimple.h virtualFileSimple.I \
|
||||
virtualFileSystem.h virtualFileSystem.I \
|
||||
weakPointerCallback.I weakPointerCallback.h \
|
||||
weakPointerTo.I weakPointerTo.h \
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include "virtualFileComposite.h"
|
||||
#include "virtualFileMount.h"
|
||||
#include "virtualFileMountMultifile.h"
|
||||
#include "virtualFileMountRamdisk.h"
|
||||
#include "virtualFileMountSystem.h"
|
||||
#include "virtualFileSimple.h"
|
||||
#include "fileReference.h"
|
||||
|
|
@ -106,6 +107,7 @@ init_libexpress() {
|
|||
VirtualFileComposite::init_type();
|
||||
VirtualFileMount::init_type();
|
||||
VirtualFileMountMultifile::init_type();
|
||||
VirtualFileMountRamdisk::init_type();
|
||||
VirtualFileMountSystem::init_type();
|
||||
VirtualFileSimple::init_type();
|
||||
FileReference::init_type();
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include "virtualFileList.cxx"
|
||||
#include "virtualFileMount.cxx"
|
||||
#include "virtualFileMountMultifile.cxx"
|
||||
#include "virtualFileMountRamdisk.cxx"
|
||||
#include "virtualFileMountSystem.cxx"
|
||||
#include "virtualFileSimple.cxx"
|
||||
#include "virtualFileSystem.cxx"
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ ISubStream(IStreamWrapper *source, streampos start, streampos end) : istream(&_b
|
|||
INLINE ISubStream &ISubStream::
|
||||
open(IStreamWrapper *source, streampos start, streampos end) {
|
||||
clear((ios_iostate)0);
|
||||
_buf.open(source, start, end);
|
||||
_buf.open(source, NULL, start, end, false);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
@ -64,3 +64,103 @@ close() {
|
|||
return *this;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: OSubStream::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE OSubStream::
|
||||
OSubStream() : ostream(&_buf) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: OSubStream::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE OSubStream::
|
||||
OSubStream(OStreamWrapper *dest, streampos start, streampos end, bool append) : ostream(&_buf) {
|
||||
open(dest, start, end, append);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: OSubStream::open
|
||||
// Access: Public
|
||||
// Description: Starts the SubStream reading from the indicated
|
||||
// dest, with the first character being the character
|
||||
// at position "start" within the dest, for end -
|
||||
// start total characters. The character at "end"
|
||||
// within the dest will never be read; this will
|
||||
// appear to be EOF.
|
||||
//
|
||||
// If end is zero, it indicates that the OSubStream will
|
||||
// continue until the end of the dest stream.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE OSubStream &OSubStream::
|
||||
open(OStreamWrapper *dest, streampos start, streampos end, bool append) {
|
||||
clear((ios_iostate)0);
|
||||
_buf.open(NULL, dest, start, end, append);
|
||||
return *this;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: OSubStream::close
|
||||
// Access: Public
|
||||
// Description: Resets the SubStream to empty, but does not actually
|
||||
// close the dest ostream.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE OSubStream &OSubStream::
|
||||
close() {
|
||||
_buf.close();
|
||||
return *this;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: SubStream::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE SubStream::
|
||||
SubStream() : iostream(&_buf) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: SubStream::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE SubStream::
|
||||
SubStream(StreamWrapper *nested, streampos start, streampos end, bool append) : iostream(&_buf) {
|
||||
open(nested, start, end, append);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: SubStream::open
|
||||
// Access: Public
|
||||
// Description: Starts the SubStream reading and writing from the
|
||||
// indicated nested stream, within the indicated range.
|
||||
// "end" is the first character outside of the range.
|
||||
//
|
||||
// If end is zero, it indicates that the SubStream will
|
||||
// continue until the end of the nested stream.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE SubStream &SubStream::
|
||||
open(StreamWrapper *nested, streampos start, streampos end, bool append) {
|
||||
clear((ios_iostate)0);
|
||||
_buf.open(nested, nested, start, end, append);
|
||||
return *this;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: SubStream::close
|
||||
// Access: Public
|
||||
// Description: Resets the SubStream to empty, but does not actually
|
||||
// close the nested ostream.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE SubStream &SubStream::
|
||||
close() {
|
||||
_buf.close();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,47 @@ private:
|
|||
SubStreamBuf _buf;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : OSubStream
|
||||
// Description : An ostream object that presents a subwindow into
|
||||
// another ostream. The first character written to this
|
||||
// stream will be the "start" character in the dest
|
||||
// istream; no characters may be written to character
|
||||
// "end" or later (unless end is zero).
|
||||
//
|
||||
// The dest stream must be one that we can randomly
|
||||
// seek within. The resulting OSubStream will also
|
||||
// support arbitrary seeks.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDAEXPRESS OSubStream : public ostream {
|
||||
PUBLISHED:
|
||||
INLINE OSubStream();
|
||||
INLINE OSubStream(OStreamWrapper *dest, streampos start, streampos end, bool append = false);
|
||||
|
||||
INLINE OSubStream &open(OStreamWrapper *dest, streampos start, streampos end, bool append = false);
|
||||
INLINE OSubStream &close();
|
||||
|
||||
private:
|
||||
SubStreamBuf _buf;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : SubStream
|
||||
// Description : Combined ISubStream and OSubStream for bidirectional
|
||||
// I/O.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDAEXPRESS SubStream : public iostream {
|
||||
PUBLISHED:
|
||||
INLINE SubStream();
|
||||
INLINE SubStream(StreamWrapper *nested, streampos start, streampos end, bool append = false);
|
||||
|
||||
INLINE SubStream &open(StreamWrapper *nested, streampos start, streampos end, bool append = false);
|
||||
INLINE SubStream &close();
|
||||
|
||||
private:
|
||||
SubStreamBuf _buf;
|
||||
};
|
||||
|
||||
#include "subStream.I"
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
typedef int streamsize;
|
||||
#endif /* HAVE_STREAMSIZE */
|
||||
|
||||
static const size_t substream_buffer_size = 4096;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: SubStreamBuf::Constructor
|
||||
// Access: Public
|
||||
|
|
@ -28,6 +30,7 @@ typedef int streamsize;
|
|||
SubStreamBuf::
|
||||
SubStreamBuf() {
|
||||
_source = (IStreamWrapper *)NULL;
|
||||
_dest = (OStreamWrapper *)NULL;
|
||||
|
||||
// _start is the streampos of the first byte of the SubStream within
|
||||
// its parent stream.
|
||||
|
|
@ -38,28 +41,29 @@ SubStreamBuf() {
|
|||
// continues to the end of the parent stream, wherever that is.
|
||||
_end = 0;
|
||||
|
||||
// _cur is the streampos of the end of the read buffer (that is,
|
||||
// egptr()) within the parent stream. By comparing _cur to gpos(),
|
||||
// we can determine the actual current file position.
|
||||
_cur = 0;
|
||||
|
||||
// _unused counts the number of bytes at the beginning of the buffer
|
||||
// that are unused. Usually this is 0 after a read, but when we
|
||||
// reach the end of the file we might not need the whole buffer to
|
||||
// read the last bit, so the first part of the buffer is unused.
|
||||
// This is important to prevent us from inadvertently seeking into
|
||||
// the unused part of the buffer.
|
||||
_unused = 0;
|
||||
// _gpos is the streampos of the end of the read buffer (that is,
|
||||
// egptr()) within the parent stream. By comparing _gpos to gpos(),
|
||||
// we can determine the actual current file position. _ppos is the
|
||||
// similar pos, for the write pointer.
|
||||
_gpos = 0;
|
||||
_ppos = 0;
|
||||
|
||||
#ifdef PHAVE_IOSTREAM
|
||||
// The new-style iostream library doesn't seem to support allocate().
|
||||
_buffer = (char *)PANDA_MALLOC_ARRAY(4096);
|
||||
char *ebuf = _buffer + 4096;
|
||||
setg(_buffer, ebuf, ebuf);
|
||||
_buffer = (char *)PANDA_MALLOC_ARRAY(substream_buffer_size * 2);
|
||||
char *ebuf = _buffer + substream_buffer_size * 2;
|
||||
char *mbuf = _buffer + substream_buffer_size;
|
||||
setg(_buffer, mbuf, mbuf);
|
||||
setp(mbuf, ebuf);
|
||||
|
||||
#else
|
||||
allocate();
|
||||
setg(base(), ebuf(), ebuf());
|
||||
// Chop the buffer in half. The bottom half goes to the get buffer;
|
||||
// the top half goes to the put buffer.
|
||||
char *b = base();
|
||||
char *t = ebuf();
|
||||
char *m = b + (t - b) / 2;
|
||||
setg(b, m, m);
|
||||
setp(b, m);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -82,14 +86,14 @@ SubStreamBuf::
|
|||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void SubStreamBuf::
|
||||
open(IStreamWrapper *source, streampos start, streampos end) {
|
||||
open(IStreamWrapper *source, OStreamWrapper *dest, streampos start, streampos end, bool append) {
|
||||
_source = source;
|
||||
_dest = dest;
|
||||
_start = start;
|
||||
_end = end;
|
||||
_cur = _start;
|
||||
|
||||
// Initially, the entire buffer is unused. Duh.
|
||||
_unused = egptr() - eback();
|
||||
_append = append;
|
||||
_gpos = _start;
|
||||
_ppos = _start;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -99,11 +103,19 @@ open(IStreamWrapper *source, streampos start, streampos end) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
void SubStreamBuf::
|
||||
close() {
|
||||
// Make sure the write buffer is flushed.
|
||||
sync();
|
||||
|
||||
_source = (IStreamWrapper *)NULL;
|
||||
_dest = (OStreamWrapper *)NULL;
|
||||
_start = 0;
|
||||
_end = 0;
|
||||
_cur = 0;
|
||||
_unused = 0;
|
||||
|
||||
_gpos = 0;
|
||||
_ppos = 0;
|
||||
|
||||
pbump(pbase() - pptr());
|
||||
gbump(egptr() - gptr());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -112,55 +124,110 @@ close() {
|
|||
// Description: Implements seeking within the stream.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
streampos SubStreamBuf::
|
||||
seekoff(streamoff off, ios_seekdir dir, ios_openmode mode) {
|
||||
// Invariant: _cur points to the file location of the buffer at
|
||||
// egptr().
|
||||
seekoff(streamoff off, ios_seekdir dir, ios_openmode which) {
|
||||
streampos result = -1;
|
||||
|
||||
// Use this to determine the actual file position right now.
|
||||
size_t n = egptr() - gptr();
|
||||
streampos cur_pos = _cur - (streampos)n;
|
||||
streampos new_pos = cur_pos;
|
||||
// Sync the iostream buffer first.
|
||||
sync();
|
||||
|
||||
// Now adjust the data pointer appropriately.
|
||||
|
||||
// Casting this to int to prevent GCC 3.2 compiler warnings. Very
|
||||
// suspicious, need to investigate further.
|
||||
switch ((int)dir) {
|
||||
case ios::beg:
|
||||
new_pos = _start + off;
|
||||
break;
|
||||
|
||||
case ios::cur:
|
||||
new_pos = cur_pos + off;
|
||||
break;
|
||||
|
||||
case ios::end:
|
||||
if (_end == (streampos)0) {
|
||||
// If the end of the file is unspecified, we have to seek to
|
||||
// find it.
|
||||
new_pos = _source->seek_gpos_eof() + off;
|
||||
|
||||
} else {
|
||||
new_pos = _end + off;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
new_pos = max(_start, new_pos);
|
||||
streamsize delta = new_pos - cur_pos;
|
||||
|
||||
if (gptr() + delta >= eback() + _unused && gptr() + delta <= egptr()) {
|
||||
// If we can get away with just bumping the gptr within the
|
||||
// buffer, do so.
|
||||
gbump(delta);
|
||||
|
||||
} else {
|
||||
// Otherwise, empty the buffer and force it to call underflow().
|
||||
if (which & ios::in) {
|
||||
// Determine the current file position.
|
||||
size_t n = egptr() - gptr();
|
||||
gbump(n);
|
||||
_cur = new_pos;
|
||||
_gpos -= n;
|
||||
nassertr(_gpos >= 0, EOF);
|
||||
streampos cur_pos = _gpos;
|
||||
streampos new_pos = cur_pos;
|
||||
|
||||
// Now adjust the data pointer appropriately.
|
||||
switch (dir) {
|
||||
case ios::beg:
|
||||
new_pos = (streampos)off + _start;
|
||||
break;
|
||||
|
||||
case ios::cur:
|
||||
new_pos = (streampos)((streamoff)cur_pos + off);
|
||||
break;
|
||||
|
||||
case ios::end:
|
||||
if (_end == (streampos)0) {
|
||||
// If the end of the file is unspecified, we have to seek to
|
||||
// find it.
|
||||
new_pos = _source->seek_gpos_eof() + off;
|
||||
|
||||
} else {
|
||||
new_pos = _end + off;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Shouldn't get here.
|
||||
break;
|
||||
}
|
||||
|
||||
if (new_pos < _start) {
|
||||
// Can't seek before beginning of file.
|
||||
return EOF;
|
||||
}
|
||||
|
||||
if (_end != 0 && new_pos > _end) {
|
||||
// Can't seek past end of file.
|
||||
return EOF;
|
||||
}
|
||||
|
||||
_gpos = new_pos;
|
||||
nassertr(_gpos >= 0, EOF);
|
||||
result = new_pos - _start;
|
||||
}
|
||||
|
||||
return new_pos - _start;
|
||||
if (which & ios::out) {
|
||||
// Determine the current file position.
|
||||
size_t n = pptr() - pbase();
|
||||
streampos cur_pos = _ppos + (streamoff)n;
|
||||
streampos new_pos = cur_pos;
|
||||
|
||||
// Now adjust the data pointer appropriately.
|
||||
switch (dir) {
|
||||
case ios::beg:
|
||||
new_pos = (streampos)off + _start;
|
||||
break;
|
||||
|
||||
case ios::cur:
|
||||
new_pos = (streampos)((streamoff)cur_pos + off);
|
||||
break;
|
||||
|
||||
case ios::end:
|
||||
if (_end == (streampos)0) {
|
||||
// If the end of the file is unspecified, we have to seek to
|
||||
// find it.
|
||||
new_pos = _dest->seek_ppos_eof() + off;
|
||||
|
||||
} else {
|
||||
new_pos = _end + off;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Shouldn't get here.
|
||||
break;
|
||||
}
|
||||
|
||||
if (new_pos < _start) {
|
||||
// Can't seek before beginning of file.
|
||||
return EOF;
|
||||
}
|
||||
|
||||
if (_end != 0 && new_pos > _end) {
|
||||
// Can't seek past end of file.
|
||||
return EOF;
|
||||
}
|
||||
|
||||
_ppos = new_pos;
|
||||
nassertr(_ppos >= 0, EOF);
|
||||
result = new_pos - _start;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -178,8 +245,8 @@ seekoff(streamoff off, ios_seekdir dir, ios_openmode mode) {
|
|||
// function as well.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
streampos SubStreamBuf::
|
||||
seekpos(streampos pos, ios_openmode mode) {
|
||||
return seekoff(pos, ios::beg, mode);
|
||||
seekpos(streampos pos, ios_openmode which) {
|
||||
return seekoff(pos, ios::beg, which);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -189,8 +256,48 @@ seekpos(streampos pos, ios_openmode mode) {
|
|||
// internal buffer is filled, plus one character.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
int SubStreamBuf::
|
||||
overflow(int c) {
|
||||
// We don't support ostream.
|
||||
overflow(int ch) {
|
||||
bool okflag = true;
|
||||
|
||||
size_t n = pptr() - pbase();
|
||||
if (n != 0) {
|
||||
if (_end != 0 && _ppos + (streampos)n > _end) {
|
||||
// Don't allow reading past the end of the file.
|
||||
n = (size_t)(_end - _ppos);
|
||||
if (n == 0) {
|
||||
// No room.
|
||||
return EOF;
|
||||
}
|
||||
}
|
||||
|
||||
nassertr(_dest != NULL, EOF);
|
||||
bool fail = false;
|
||||
if (_append) {
|
||||
_dest->seek_eof_write(pbase(), n, fail);
|
||||
} else {
|
||||
_dest->seek_write(_ppos, pbase(), n, fail);
|
||||
}
|
||||
_ppos += n;
|
||||
pbump(-(int)n);
|
||||
if (fail) {
|
||||
okflag = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (okflag && ch != EOF) {
|
||||
if (pptr() != epptr()) {
|
||||
// Store the extra character back in the buffer.
|
||||
*(pptr()) = ch;
|
||||
pbump(1);
|
||||
} else {
|
||||
// No room to store ch.
|
||||
okflag = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!okflag) {
|
||||
return EOF;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -202,8 +309,23 @@ overflow(int c) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
int SubStreamBuf::
|
||||
sync() {
|
||||
size_t n = egptr() - gptr();
|
||||
gbump(n);
|
||||
size_t n = pptr() - pbase();
|
||||
|
||||
if (n != 0) {
|
||||
nassertr(_dest != NULL, EOF);
|
||||
bool fail = false;
|
||||
if (_append) {
|
||||
_dest->seek_eof_write(pbase(), n, fail);
|
||||
} else {
|
||||
_dest->seek_write(_ppos, pbase(), n, fail);
|
||||
}
|
||||
_ppos += n;
|
||||
pbump(-(int)n);
|
||||
|
||||
if (fail) {
|
||||
return EOF;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -218,54 +340,48 @@ int SubStreamBuf::
|
|||
underflow() {
|
||||
// Sometimes underflow() is called even if the buffer is not empty.
|
||||
if (gptr() >= egptr()) {
|
||||
if (_end != (streampos)0 && _cur >= _end) {
|
||||
// We're done.
|
||||
return EOF;
|
||||
}
|
||||
|
||||
sync();
|
||||
|
||||
// Mark the buffer filled (with buffer_size bytes).
|
||||
size_t buffer_size = egptr() - eback();
|
||||
size_t num_bytes;
|
||||
if (_end == (streampos)0 || _end - _cur > (streampos)buffer_size) {
|
||||
// We have enough bytes in the input stream to fill our buffer.
|
||||
num_bytes = buffer_size;
|
||||
} else {
|
||||
// We won't quite fill the buffer.
|
||||
num_bytes = (size_t)(_end - _cur);
|
||||
gbump(-(int)buffer_size);
|
||||
|
||||
streamsize num_bytes = buffer_size;
|
||||
if (_end != 0 && _gpos + (streampos)num_bytes > _end) {
|
||||
// Don't allow reading past the end of the file.
|
||||
streamsize new_num_bytes = _end - _gpos;
|
||||
if (new_num_bytes == 0) {
|
||||
gbump(buffer_size);
|
||||
return EOF;
|
||||
}
|
||||
|
||||
// We won't be filling the entire buffer. Fill in only at the
|
||||
// end of the buffer.
|
||||
size_t delta = num_bytes - new_num_bytes;
|
||||
gbump(delta);
|
||||
num_bytes = new_num_bytes;
|
||||
nassertr(egptr() - gptr() == num_bytes, EOF);
|
||||
}
|
||||
|
||||
gbump(-(int)num_bytes);
|
||||
nassertr(gptr() + num_bytes <= egptr(), EOF);
|
||||
|
||||
|
||||
nassertr(_source != NULL, EOF);
|
||||
streamsize read_count;
|
||||
bool eof;
|
||||
_source->seek_read(_cur, gptr(), num_bytes, read_count, eof);
|
||||
_source->seek_read(_gpos, gptr(), num_bytes, read_count, eof);
|
||||
_gpos += read_count;
|
||||
|
||||
if (read_count != (streamsize)num_bytes) {
|
||||
if (read_count != num_bytes) {
|
||||
// Oops, we didn't read what we thought we would.
|
||||
if (read_count == 0) {
|
||||
_unused = buffer_size;
|
||||
if (_end != (streampos)0) {
|
||||
_end = _cur;
|
||||
}
|
||||
gbump(num_bytes);
|
||||
return EOF;
|
||||
}
|
||||
|
||||
// Slide what we did read to the top of the buffer.
|
||||
nassertr(read_count < (int)num_bytes, EOF);
|
||||
nassertr(read_count < num_bytes, EOF);
|
||||
size_t delta = num_bytes - read_count;
|
||||
memmove(gptr() + delta, gptr(), read_count);
|
||||
gbump(delta);
|
||||
}
|
||||
|
||||
// Now record whatever bytes at the beginning of the buffer are
|
||||
// unused, so we won't try to seek into that area.
|
||||
_unused = buffer_size - read_count;
|
||||
|
||||
// Invariant: _cur points to the file location of the buffer at
|
||||
// egptr().
|
||||
|
||||
_cur += read_count;
|
||||
}
|
||||
|
||||
return (unsigned char)*gptr();
|
||||
|
|
|
|||
|
|
@ -27,11 +27,11 @@ public:
|
|||
SubStreamBuf();
|
||||
virtual ~SubStreamBuf();
|
||||
|
||||
void open(IStreamWrapper *source, streampos start, streampos end);
|
||||
void open(IStreamWrapper *source, OStreamWrapper *dest, streampos start, streampos end, bool append);
|
||||
void close();
|
||||
|
||||
virtual streampos seekoff(streamoff off, ios_seekdir dir, ios_openmode mode);
|
||||
virtual streampos seekpos(streampos pos, ios_openmode mode);
|
||||
virtual streampos seekoff(streamoff off, ios_seekdir dir, ios_openmode which);
|
||||
virtual streampos seekpos(streampos pos, ios_openmode which);
|
||||
|
||||
protected:
|
||||
virtual int overflow(int c);
|
||||
|
|
@ -40,10 +40,12 @@ protected:
|
|||
|
||||
private:
|
||||
IStreamWrapper *_source;
|
||||
OStreamWrapper *_dest;
|
||||
streampos _start;
|
||||
streampos _end;
|
||||
streampos _cur;
|
||||
size_t _unused;
|
||||
bool _append;
|
||||
streampos _gpos;
|
||||
streampos _ppos;
|
||||
char *_buffer;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,53 @@ is_writable() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFile::delete_file
|
||||
// Access: Public
|
||||
// Description: Attempts to delete this file or directory. This can
|
||||
// remove a single file or an empty directory. It will
|
||||
// not remove a nonempty directory. Returns true on
|
||||
// success, false on failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFile::
|
||||
delete_file() {
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFile::rename_file
|
||||
// Access: Public
|
||||
// Description: Attempts to move or rename this file or directory.
|
||||
// If the original file is an ordinary file, it will
|
||||
// quietly replace any already-existing file in the new
|
||||
// filename (but not a directory). If the original file
|
||||
// is a directory, the new filename must not already
|
||||
// exist.
|
||||
//
|
||||
// If the file is a directory, the new filename must be
|
||||
// within the same mount point. If the file is an
|
||||
// ordinary file, the new filename may be anywhere; but
|
||||
// if it is not within the same mount point then the
|
||||
// rename operation is automatically performed as a
|
||||
// two-step copy-and-delete operation.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFile::
|
||||
rename_file(VirtualFile *new_file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFile::copy_file
|
||||
// Access: Public
|
||||
// Description: Attempts to copy the contents of this file to the
|
||||
// indicated file. Returns true on success, false on
|
||||
// failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFile::
|
||||
copy_file(VirtualFile *new_file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFile::scan_directory
|
||||
// Access: Published
|
||||
|
|
@ -349,6 +396,28 @@ get_system_info(SubfileInfo &info) {
|
|||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFile::atomic_compare_and_exchange_contents
|
||||
// Access: Public, Virtual
|
||||
// Description: See Filename::atomic_compare_and_exchange_contents().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFile::
|
||||
atomic_compare_and_exchange_contents(string &orig_contents,
|
||||
const string &old_contents,
|
||||
const string &new_contents) {
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFile::atomic_read_contents
|
||||
// Access: Public, Virtual
|
||||
// Description: See Filename::atomic_read_contents().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFile::
|
||||
atomic_read_contents(string &contents) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFile::read_file
|
||||
// Access: Public
|
||||
|
|
|
|||
|
|
@ -48,6 +48,10 @@ PUBLISHED:
|
|||
virtual bool is_regular_file() const;
|
||||
virtual bool is_writable() const;
|
||||
|
||||
BLOCKING virtual bool delete_file();
|
||||
BLOCKING virtual bool rename_file(VirtualFile *new_file);
|
||||
BLOCKING virtual bool copy_file(VirtualFile *new_file);
|
||||
|
||||
BLOCKING PT(VirtualFileList) scan_directory() const;
|
||||
|
||||
void output(ostream &out) const;
|
||||
|
|
@ -75,6 +79,9 @@ PUBLISHED:
|
|||
virtual bool get_system_info(SubfileInfo &info);
|
||||
|
||||
public:
|
||||
virtual bool atomic_compare_and_exchange_contents(string &orig_contents, const string &old_contents, const string &new_contents);
|
||||
virtual bool atomic_read_contents(string &contents) const;
|
||||
|
||||
INLINE void set_original_filename(const Filename &filename);
|
||||
bool read_file(string &result, bool auto_unwrap) const;
|
||||
virtual bool read_file(pvector<unsigned char> &result, bool auto_unwrap) const;
|
||||
|
|
|
|||
|
|
@ -75,6 +75,48 @@ create_file(const Filename &file) {
|
|||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMount::delete_file
|
||||
// Access: Public, Virtual
|
||||
// Description: Attempts to delete the indicated file or directory
|
||||
// within the mount. This can remove a single file or
|
||||
// an empty directory. It will not remove a nonempty
|
||||
// directory. Returns true on success, false on
|
||||
// failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMount::
|
||||
delete_file(const Filename &file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMount::rename_file
|
||||
// Access: Public
|
||||
// Description: Attempts to rename the contents of the indicated file
|
||||
// to the indicated file. Both filenames will be within
|
||||
// the mount. Returns true on success, false on
|
||||
// failure. If this returns false, this will be
|
||||
// attempted again with a copy-and-delete operation.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMount::
|
||||
rename_file(const Filename &orig_filename, const Filename &new_filename) {
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMount::copy_file
|
||||
// Access: Public
|
||||
// Description: Attempts to copy the contents of the indicated file
|
||||
// to the indicated file. Both filenames will be within
|
||||
// the mount. Returns true on success, false on
|
||||
// failure. If this returns false, the copy will be
|
||||
// performed by explicit read-and-write operations.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMount::
|
||||
copy_file(const Filename &orig_filename, const Filename &new_filename) {
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMount::make_directory
|
||||
// Access: Public, Virtual
|
||||
|
|
@ -324,6 +366,28 @@ get_system_info(const Filename &file, SubfileInfo &info) {
|
|||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMount::atomic_compare_and_exchange_contents
|
||||
// Access: Public, Virtual
|
||||
// Description: See Filename::atomic_compare_and_exchange_contents().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMount::
|
||||
atomic_compare_and_exchange_contents(const Filename &file, string &orig_contents,
|
||||
const string &old_contents,
|
||||
const string &new_contents) {
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMount::atomic_read_contents
|
||||
// Access: Public, Virtual
|
||||
// Description: See Filename::atomic_read_contents().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMount::
|
||||
atomic_read_contents(const Filename &file, string &contents) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMount::output
|
||||
// Access: Public, Virtual
|
||||
|
|
|
|||
|
|
@ -48,6 +48,9 @@ public:
|
|||
virtual bool has_file(const Filename &file) const=0;
|
||||
virtual bool create_file(const Filename &file);
|
||||
virtual bool make_directory(const Filename &file);
|
||||
virtual bool delete_file(const Filename &file);
|
||||
virtual bool rename_file(const Filename &orig_filename, const Filename &new_filename);
|
||||
virtual bool copy_file(const Filename &orig_filename, const Filename &new_filename);
|
||||
virtual bool is_directory(const Filename &file) const=0;
|
||||
virtual bool is_regular_file(const Filename &file) const=0;
|
||||
virtual bool is_writable(const Filename &file) const;
|
||||
|
|
@ -78,6 +81,9 @@ public:
|
|||
virtual bool scan_directory(vector_string &contents,
|
||||
const Filename &dir) const=0;
|
||||
|
||||
virtual bool atomic_compare_and_exchange_contents(const Filename &file, string &orig_contents, const string &old_contents, const string &new_contents);
|
||||
virtual bool atomic_read_contents(const Filename &file, string &contents) const;
|
||||
|
||||
PUBLISHED:
|
||||
virtual void output(ostream &out) const;
|
||||
virtual void write(ostream &out) const;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
// Filename: virtualFileMountRamdisk.I
|
||||
// Created by: drose (19Sep11)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
//
|
||||
// All use of this software is subject to the terms of the revised BSD
|
||||
// license. You should have received a copy of this license along
|
||||
// with this source code in a file named "LICENSE."
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::FileBase::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE VirtualFileMountRamdisk::FileBase::
|
||||
FileBase(const string &basename) : _basename(basename), _timestamp(0) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::FileBase::operator <
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool VirtualFileMountRamdisk::FileBase::
|
||||
operator < (const FileBase &other) const {
|
||||
return _basename < other._basename;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::File::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE VirtualFileMountRamdisk::File::
|
||||
File(const string &basename) :
|
||||
FileBase(basename),
|
||||
_wrapper(_data)
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::Directory::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE VirtualFileMountRamdisk::Directory::
|
||||
Directory(const string &basename) : FileBase(basename) {
|
||||
}
|
||||
|
|
@ -0,0 +1,707 @@
|
|||
// Filename: virtualFileMountRamdisk.cxx
|
||||
// Created by: drose (19Sep11)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
//
|
||||
// All use of this software is subject to the terms of the revised BSD
|
||||
// license. You should have received a copy of this license along
|
||||
// with this source code in a file named "LICENSE."
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "virtualFileMountRamdisk.h"
|
||||
#include "subStream.h"
|
||||
#include "dcast.h"
|
||||
|
||||
TypeHandle VirtualFileMountRamdisk::_type_handle;
|
||||
TypeHandle VirtualFileMountRamdisk::FileBase::_type_handle;
|
||||
TypeHandle VirtualFileMountRamdisk::File::_type_handle;
|
||||
TypeHandle VirtualFileMountRamdisk::Directory::_type_handle;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
VirtualFileMountRamdisk::
|
||||
VirtualFileMountRamdisk() : _root("") {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::has_file
|
||||
// Access: Public, Virtual
|
||||
// Description: Returns true if the indicated file exists within the
|
||||
// mount system.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMountRamdisk::
|
||||
has_file(const Filename &file) const {
|
||||
((VirtualFileMountRamdisk *)this)->_lock.acquire();
|
||||
PT(FileBase) f = _root.do_find_file(file);
|
||||
((VirtualFileMountRamdisk *)this)->_lock.release();
|
||||
return (f != NULL);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::create_file
|
||||
// Access: Public, Virtual
|
||||
// Description: Attempts to create the indicated file within the
|
||||
// mount, if it does not already exist. Returns true on
|
||||
// success (or if the file already exists), or false if
|
||||
// it cannot be created.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMountRamdisk::
|
||||
create_file(const Filename &file) {
|
||||
_lock.acquire();
|
||||
PT(File) f = _root.do_create_file(file);
|
||||
_lock.release();
|
||||
return (f != NULL);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::delete_file
|
||||
// Access: Public, Virtual
|
||||
// Description: Attempts to delete the indicated file or directory
|
||||
// within the mount. This can remove a single file or
|
||||
// an empty directory. It will not remove a nonempty
|
||||
// directory. Returns true on success, false on
|
||||
// failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMountRamdisk::
|
||||
delete_file(const Filename &file) {
|
||||
_lock.acquire();
|
||||
PT(FileBase) f = _root.do_delete_file(file);
|
||||
_lock.release();
|
||||
return (f != NULL);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::rename_file
|
||||
// Access: Public
|
||||
// Description: Attempts to rename the contents of the indicated file
|
||||
// to the indicated file. Both filenames will be within
|
||||
// the mount. Returns true on success, false on
|
||||
// failure. If this returns false, this will be
|
||||
// attempted again with a copy-and-delete operation.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMountRamdisk::
|
||||
rename_file(const Filename &orig_filename, const Filename &new_filename) {
|
||||
_lock.acquire();
|
||||
PT(FileBase) orig_fb = _root.do_find_file(orig_filename);
|
||||
if (orig_fb == NULL) {
|
||||
_lock.release();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (orig_fb->is_directory()) {
|
||||
// Rename the directory.
|
||||
Directory *orig_d = DCAST(Directory, orig_fb);
|
||||
PT(Directory) new_d = _root.do_make_directory(new_filename);
|
||||
if (new_d == NULL || !new_d->_files.empty()) {
|
||||
_lock.release();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (express_cat.is_debug()) {
|
||||
express_cat.debug()
|
||||
<< "Renaming ramdisk directory " << orig_filename << " to " << new_filename << "\n";
|
||||
}
|
||||
|
||||
new_d->_files.swap(orig_d->_files);
|
||||
_root.do_delete_file(orig_filename);
|
||||
_lock.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Rename the file.
|
||||
File *orig_f = DCAST(File, orig_fb);
|
||||
PT(File) new_f = _root.do_create_file(new_filename);
|
||||
if (new_f == NULL) {
|
||||
_lock.release();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (express_cat.is_debug()) {
|
||||
express_cat.debug()
|
||||
<< "Renaming ramdisk file " << orig_filename << " to " << new_filename << "\n";
|
||||
}
|
||||
|
||||
new_f->_data.str(orig_f->_data.str());
|
||||
_root.do_delete_file(orig_filename);
|
||||
|
||||
_lock.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::copy_file
|
||||
// Access: Public
|
||||
// Description: Attempts to copy the contents of the indicated file
|
||||
// to the indicated file. Both filenames will be within
|
||||
// the mount. Returns true on success, false on
|
||||
// failure. If this returns false, the copy will be
|
||||
// performed by explicit read-and-write operations.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMountRamdisk::
|
||||
copy_file(const Filename &orig_filename, const Filename &new_filename) {
|
||||
_lock.acquire();
|
||||
PT(FileBase) orig_fb = _root.do_find_file(orig_filename);
|
||||
if (orig_fb == NULL || orig_fb->is_directory()) {
|
||||
_lock.release();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy the file.
|
||||
File *orig_f = DCAST(File, orig_fb);
|
||||
PT(File) new_f = _root.do_create_file(new_filename);
|
||||
if (new_f == NULL) {
|
||||
_lock.release();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (express_cat.is_debug()) {
|
||||
express_cat.debug()
|
||||
<< "Copying ramdisk file " << orig_filename << " to " << new_filename << "\n";
|
||||
}
|
||||
|
||||
new_f->_data.str(orig_f->_data.str());
|
||||
|
||||
_lock.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::make_directory
|
||||
// Access: Public, Virtual
|
||||
// Description: Attempts to create the indicated file within the
|
||||
// mount, if it does not already exist. Returns true on
|
||||
// success, or false if it cannot be created. If the
|
||||
// directory already existed prior to this call, may
|
||||
// return either true or false.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMountRamdisk::
|
||||
make_directory(const Filename &file) {
|
||||
_lock.acquire();
|
||||
PT(Directory) f = _root.do_make_directory(file);
|
||||
_lock.release();
|
||||
return (f != NULL);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::is_directory
|
||||
// Access: Public, Virtual
|
||||
// Description: Returns true if the indicated file exists within the
|
||||
// mount system and is a directory.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMountRamdisk::
|
||||
is_directory(const Filename &file) const {
|
||||
((VirtualFileMountRamdisk *)this)->_lock.acquire();
|
||||
PT(FileBase) f = _root.do_find_file(file);
|
||||
((VirtualFileMountRamdisk *)this)->_lock.release();
|
||||
return (f != NULL && f->is_directory());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::is_regular_file
|
||||
// Access: Public, Virtual
|
||||
// Description: Returns true if the indicated file exists within the
|
||||
// mount system and is a regular file.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMountRamdisk::
|
||||
is_regular_file(const Filename &file) const {
|
||||
((VirtualFileMountRamdisk *)this)->_lock.acquire();
|
||||
PT(FileBase) f = _root.do_find_file(file);
|
||||
((VirtualFileMountRamdisk *)this)->_lock.release();
|
||||
return (f != NULL && !f->is_directory());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::is_writable
|
||||
// Access: Public, Virtual
|
||||
// Description: Returns true if the named file or directory may be
|
||||
// written to, false otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMountRamdisk::
|
||||
is_writable(const Filename &file) const {
|
||||
return has_file(file);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::open_read_file
|
||||
// Access: Public, Virtual
|
||||
// Description: Opens the file for reading, if it exists. Returns a
|
||||
// newly allocated istream on success (which you should
|
||||
// eventually delete when you are done reading).
|
||||
// Returns NULL on failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
istream *VirtualFileMountRamdisk::
|
||||
open_read_file(const Filename &file) const {
|
||||
((VirtualFileMountRamdisk *)this)->_lock.acquire();
|
||||
PT(FileBase) f = _root.do_find_file(file);
|
||||
((VirtualFileMountRamdisk *)this)->_lock.release();
|
||||
if (f == (FileBase *)NULL || f->is_directory()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
File *f2 = DCAST(File, f);
|
||||
return new ISubStream(&f2->_wrapper, 0, 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::open_write_file
|
||||
// Access: Published, Virtual
|
||||
// Description: Opens the file for writing. Returns a newly
|
||||
// allocated ostream on success (which you should
|
||||
// eventually delete when you are done writing).
|
||||
// Returns NULL on failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
ostream *VirtualFileMountRamdisk::
|
||||
open_write_file(const Filename &file, bool truncate) {
|
||||
_lock.acquire();
|
||||
PT(File) f = _root.do_create_file(file);
|
||||
_lock.release();
|
||||
if (f == (File *)NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (truncate) {
|
||||
// Reset to an empty string.
|
||||
f->_data.str(string());
|
||||
}
|
||||
|
||||
return new OSubStream(&f->_wrapper, 0, 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::open_append_file
|
||||
// Access: Published
|
||||
// Description: Works like open_write_file(), but the file is opened
|
||||
// in append mode. Like open_write_file, the returned
|
||||
// pointer should eventually be passed to
|
||||
// close_write_file().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
ostream *VirtualFileMountRamdisk::
|
||||
open_append_file(const Filename &file) {
|
||||
_lock.acquire();
|
||||
PT(File) f = _root.do_create_file(file);
|
||||
_lock.release();
|
||||
if (f == (File *)NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return new OSubStream(&f->_wrapper, 0, 0, true);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::open_read_write_file
|
||||
// Access: Published, Virtual
|
||||
// Description: Opens the file for writing. Returns a newly
|
||||
// allocated iostream on success (which you should
|
||||
// eventually delete when you are done writing).
|
||||
// Returns NULL on failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
iostream *VirtualFileMountRamdisk::
|
||||
open_read_write_file(const Filename &file, bool truncate) {
|
||||
_lock.acquire();
|
||||
PT(File) f = _root.do_create_file(file);
|
||||
_lock.release();
|
||||
if (f == (File *)NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return new SubStream(&f->_wrapper, 0, 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::open_read_append_file
|
||||
// Access: Published, Virtual
|
||||
// Description: Works like open_read_write_file(), but the file is opened
|
||||
// in append mode. Like open_read_write_file, the returned
|
||||
// pointer should eventually be passed to
|
||||
// close_read_write_file().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
iostream *VirtualFileMountRamdisk::
|
||||
open_read_append_file(const Filename &file) {
|
||||
_lock.acquire();
|
||||
PT(FileBase) f = _root.do_find_file(file);
|
||||
_lock.release();
|
||||
if (f == (FileBase *)NULL || f->is_directory()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
File *f2 = DCAST(File, f);
|
||||
return new SubStream(&f2->_wrapper, 0, 0, true);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::get_file_size
|
||||
// Access: Published, Virtual
|
||||
// Description: Returns the current size on disk (or wherever it is)
|
||||
// of the already-open file. Pass in the stream that
|
||||
// was returned by open_read_file(); some
|
||||
// implementations may require this stream to determine
|
||||
// the size.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
off_t VirtualFileMountRamdisk::
|
||||
get_file_size(const Filename &file, istream *stream) const {
|
||||
((VirtualFileMountRamdisk *)this)->_lock.acquire();
|
||||
PT(FileBase) f = _root.do_find_file(file);
|
||||
((VirtualFileMountRamdisk *)this)->_lock.release();
|
||||
if (f == (FileBase *)NULL || f->is_directory()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
File *f2 = DCAST(File, f);
|
||||
return f2->_data.str().length();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::get_file_size
|
||||
// Access: Published, Virtual
|
||||
// Description: Returns the current size on disk (or wherever it is)
|
||||
// of the file before it has been opened.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
off_t VirtualFileMountRamdisk::
|
||||
get_file_size(const Filename &file) const {
|
||||
((VirtualFileMountRamdisk *)this)->_lock.acquire();
|
||||
PT(FileBase) f = _root.do_find_file(file);
|
||||
((VirtualFileMountRamdisk *)this)->_lock.release();
|
||||
if (f == (FileBase *)NULL || f->is_directory()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
File *f2 = DCAST(File, f);
|
||||
return f2->_data.str().length();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::get_timestamp
|
||||
// Access: Published, Virtual
|
||||
// Description: Returns a time_t value that represents the time the
|
||||
// file was last modified, to within whatever precision
|
||||
// the operating system records this information (on a
|
||||
// Windows95 system, for instance, this may only be
|
||||
// accurate to within 2 seconds).
|
||||
//
|
||||
// If the timestamp cannot be determined, either because
|
||||
// it is not supported by the operating system or
|
||||
// because there is some error (such as file not found),
|
||||
// returns 0.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
time_t VirtualFileMountRamdisk::
|
||||
get_timestamp(const Filename &file) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::scan_directory
|
||||
// Access: Public, Virtual
|
||||
// Description: Fills the given vector up with the list of filenames
|
||||
// that are local to this directory, if the filename is
|
||||
// a directory. Returns true if successful, or false if
|
||||
// the file is not a directory or cannot be read.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMountRamdisk::
|
||||
scan_directory(vector_string &contents, const Filename &dir) const {
|
||||
((VirtualFileMountRamdisk *)this)->_lock.acquire();
|
||||
PT(FileBase) f = _root.do_find_file(dir);
|
||||
if (f == (FileBase *)NULL || !f->is_directory()) {
|
||||
((VirtualFileMountRamdisk *)this)->_lock.release();
|
||||
return false;
|
||||
}
|
||||
|
||||
Directory *f2 = DCAST(Directory, f);
|
||||
bool result = f2->do_scan_directory(contents);
|
||||
|
||||
((VirtualFileMountRamdisk *)this)->_lock.release();
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::atomic_compare_and_exchange_contents
|
||||
// Access: Public, Virtual
|
||||
// Description: See Filename::atomic_compare_and_exchange_contents().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMountRamdisk::
|
||||
atomic_compare_and_exchange_contents(const Filename &file, string &orig_contents,
|
||||
const string &old_contents,
|
||||
const string &new_contents) {
|
||||
_lock.acquire();
|
||||
PT(FileBase) f = _root.do_find_file(file);
|
||||
if (f == (FileBase *)NULL || f->is_directory()) {
|
||||
_lock.release();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool retval = false;
|
||||
File *f2 = DCAST(File, f);
|
||||
orig_contents = f2->_data.str();
|
||||
if (orig_contents == old_contents) {
|
||||
f2->_data.str(new_contents);
|
||||
retval = true;
|
||||
}
|
||||
|
||||
_lock.release();
|
||||
return retval;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::atomic_read_contents
|
||||
// Access: Public, Virtual
|
||||
// Description: See Filename::atomic_read_contents().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMountRamdisk::
|
||||
atomic_read_contents(const Filename &file, string &contents) const {
|
||||
((VirtualFileMountRamdisk *)this)->_lock.acquire();
|
||||
PT(FileBase) f = _root.do_find_file(file);
|
||||
if (f == (FileBase *)NULL || f->is_directory()) {
|
||||
((VirtualFileMountRamdisk *)this)->_lock.release();
|
||||
return false;
|
||||
}
|
||||
|
||||
File *f2 = DCAST(File, f);
|
||||
contents = f2->_data.str();
|
||||
|
||||
((VirtualFileMountRamdisk *)this)->_lock.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::output
|
||||
// Access: Public, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void VirtualFileMountRamdisk::
|
||||
output(ostream &out) const {
|
||||
out << "VirtualFileMountRamdisk";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::FileBase::Destructor
|
||||
// Access: Public, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
VirtualFileMountRamdisk::FileBase::
|
||||
~FileBase() {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::FileBase::is_directory
|
||||
// Access: Public, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMountRamdisk::FileBase::
|
||||
is_directory() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::Directory::is_directory
|
||||
// Access: Public, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMountRamdisk::Directory::
|
||||
is_directory() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::Directory::do_find_file
|
||||
// Access: Public
|
||||
// Description: Recursively search for the file with the indicated
|
||||
// name in this directory hierarchy.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
PT(VirtualFileMountRamdisk::FileBase) VirtualFileMountRamdisk::Directory::
|
||||
do_find_file(const string &filename) const {
|
||||
size_t slash = filename.find('/');
|
||||
if (slash == string::npos) {
|
||||
// Search for a file within the local directory.
|
||||
FileBase tfile(filename);
|
||||
tfile.local_object();
|
||||
Files::iterator fi = _files.find(&tfile);
|
||||
if (fi != _files.end()) {
|
||||
return (*fi);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// A nested directory. Search for the directory name, then recurse.
|
||||
string dirname = filename.substr(0, slash);
|
||||
string remainder = filename.substr(slash + 1);
|
||||
FileBase tfile(dirname);
|
||||
tfile.local_object();
|
||||
Files::iterator fi = _files.find(&tfile);
|
||||
if (fi != _files.end()) {
|
||||
PT(FileBase) file = (*fi);
|
||||
if (file->is_directory()) {
|
||||
return DCAST(Directory, file.p())->do_find_file(remainder);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::Directory::do_create_file
|
||||
// Access: Public
|
||||
// Description: Recursively search for the file with the indicated
|
||||
// name in this directory hierarchy. If not found,
|
||||
// creates a new file.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
PT(VirtualFileMountRamdisk::File) VirtualFileMountRamdisk::Directory::
|
||||
do_create_file(const string &filename) {
|
||||
size_t slash = filename.find('/');
|
||||
if (slash == string::npos) {
|
||||
// Search for a file within the local directory.
|
||||
FileBase tfile(filename);
|
||||
tfile.local_object();
|
||||
Files::iterator fi = _files.find(&tfile);
|
||||
if (fi != _files.end()) {
|
||||
PT(FileBase) file = (*fi);
|
||||
if (!file->is_directory()) {
|
||||
return DCAST(File, file.p());
|
||||
}
|
||||
// Cannot create: a directory by the same name already exists.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create a new file.
|
||||
if (express_cat.is_debug()) {
|
||||
express_cat.debug()
|
||||
<< "Making ramdisk file " << filename << "\n";
|
||||
}
|
||||
PT(File) file = new File(filename);
|
||||
_files.insert(file.p());
|
||||
return file;
|
||||
}
|
||||
|
||||
// A nested directory. Search for the directory name, then recurse.
|
||||
string dirname = filename.substr(0, slash);
|
||||
string remainder = filename.substr(slash + 1);
|
||||
FileBase tfile(dirname);
|
||||
tfile.local_object();
|
||||
Files::iterator fi = _files.find(&tfile);
|
||||
if (fi != _files.end()) {
|
||||
PT(FileBase) file = (*fi);
|
||||
if (file->is_directory()) {
|
||||
return DCAST(Directory, file.p())->do_create_file(remainder);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::Directory::do_make_directory
|
||||
// Access: Public
|
||||
// Description: Recursively search for the file with the indicated
|
||||
// name in this directory hierarchy. If not found,
|
||||
// creates a new directory.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
PT(VirtualFileMountRamdisk::Directory) VirtualFileMountRamdisk::Directory::
|
||||
do_make_directory(const string &filename) {
|
||||
size_t slash = filename.find('/');
|
||||
if (slash == string::npos) {
|
||||
// Search for a file within the local directory.
|
||||
FileBase tfile(filename);
|
||||
tfile.local_object();
|
||||
Files::iterator fi = _files.find(&tfile);
|
||||
if (fi != _files.end()) {
|
||||
PT(FileBase) file = (*fi);
|
||||
if (file->is_directory()) {
|
||||
return DCAST(Directory, file.p());
|
||||
}
|
||||
// Cannot create: a file by the same name already exists.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create a new directory.
|
||||
if (express_cat.is_debug()) {
|
||||
express_cat.debug()
|
||||
<< "Making ramdisk directory " << filename << "\n";
|
||||
}
|
||||
PT(Directory) file = new Directory(filename);
|
||||
_files.insert(file.p());
|
||||
return file;
|
||||
}
|
||||
|
||||
// A nested directory. Search for the directory name, then recurse.
|
||||
string dirname = filename.substr(0, slash);
|
||||
string remainder = filename.substr(slash + 1);
|
||||
FileBase tfile(dirname);
|
||||
tfile.local_object();
|
||||
Files::iterator fi = _files.find(&tfile);
|
||||
if (fi != _files.end()) {
|
||||
PT(FileBase) file = (*fi);
|
||||
if (file->is_directory()) {
|
||||
return DCAST(Directory, file.p())->do_make_directory(remainder);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::Directory::do_delete_file
|
||||
// Access: Public
|
||||
// Description: Recursively search for the file with the indicated
|
||||
// name in this directory hierarchy, and removes it.
|
||||
// Returns the removed FileBase object.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
PT(VirtualFileMountRamdisk::FileBase) VirtualFileMountRamdisk::Directory::
|
||||
do_delete_file(const string &filename) {
|
||||
size_t slash = filename.find('/');
|
||||
if (slash == string::npos) {
|
||||
// Search for a file within the local directory.
|
||||
FileBase tfile(filename);
|
||||
tfile.local_object();
|
||||
Files::iterator fi = _files.find(&tfile);
|
||||
if (fi != _files.end()) {
|
||||
PT(FileBase) file = (*fi);
|
||||
if (file->is_directory()) {
|
||||
Directory *dir = DCAST(Directory, file.p());
|
||||
if (!dir->_files.empty()) {
|
||||
// Can't delete a nonempty directory.
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
_files.erase(fi);
|
||||
return file;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// A nested directory. Search for the directory name, then recurse.
|
||||
string dirname = filename.substr(0, slash);
|
||||
string remainder = filename.substr(slash + 1);
|
||||
FileBase tfile(dirname);
|
||||
tfile.local_object();
|
||||
Files::iterator fi = _files.find(&tfile);
|
||||
if (fi != _files.end()) {
|
||||
PT(FileBase) file = (*fi);
|
||||
if (file->is_directory()) {
|
||||
return DCAST(Directory, file.p())->do_delete_file(remainder);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountRamdisk::Directory::do_scan_directory
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMountRamdisk::Directory::
|
||||
do_scan_directory(vector_string &contents) const {
|
||||
Files::const_iterator fi;
|
||||
for (fi = _files.begin(); fi != _files.end(); ++fi) {
|
||||
FileBase *file = (*fi);
|
||||
contents.push_back(file->_basename);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
// Filename: virtualFileMountRamdisk.h
|
||||
// Created by: drose (19Sep11)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
//
|
||||
// All use of this software is subject to the terms of the revised BSD
|
||||
// license. You should have received a copy of this license along
|
||||
// with this source code in a file named "LICENSE."
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef VIRTUALFILEMOUNTRAMDISK_H
|
||||
#define VIRTUALFILEMOUNTRAMDISK_H
|
||||
|
||||
#include "pandabase.h"
|
||||
|
||||
#include "virtualFileMount.h"
|
||||
#include "mutexImpl.h"
|
||||
#include "streamWrapper.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : VirtualFileMountRamdisk
|
||||
// Description : Simulates an actual directory on disk with in-memory
|
||||
// storage. This is useful mainly for performing high
|
||||
// level functions that expect disk I/O without actually
|
||||
// writing files to disk. Naturally, there are
|
||||
// significant limits to the size of the files that may
|
||||
// be written with this system; and "files" written here
|
||||
// are not automatically persistent between sessions.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDAEXPRESS VirtualFileMountRamdisk : public VirtualFileMount {
|
||||
PUBLISHED:
|
||||
VirtualFileMountRamdisk();
|
||||
|
||||
public:
|
||||
virtual bool has_file(const Filename &file) const;
|
||||
virtual bool create_file(const Filename &file);
|
||||
virtual bool make_directory(const Filename &file);
|
||||
virtual bool delete_file(const Filename &file);
|
||||
virtual bool rename_file(const Filename &orig_filename, const Filename &new_filename);
|
||||
virtual bool copy_file(const Filename &orig_filename, const Filename &new_filename);
|
||||
virtual bool is_directory(const Filename &file) const;
|
||||
virtual bool is_regular_file(const Filename &file) const;
|
||||
virtual bool is_writable(const Filename &file) const;
|
||||
|
||||
virtual istream *open_read_file(const Filename &file) const;
|
||||
virtual ostream *open_write_file(const Filename &file, bool truncate);
|
||||
virtual ostream *open_append_file(const Filename &file);
|
||||
virtual iostream *open_read_write_file(const Filename &file, bool truncate);
|
||||
virtual iostream *open_read_append_file(const Filename &file);
|
||||
|
||||
virtual off_t get_file_size(const Filename &file, istream *stream) const;
|
||||
virtual off_t get_file_size(const Filename &file) const;
|
||||
virtual time_t get_timestamp(const Filename &file) const;
|
||||
|
||||
virtual bool scan_directory(vector_string &contents,
|
||||
const Filename &dir) const;
|
||||
|
||||
virtual bool atomic_compare_and_exchange_contents(const Filename &file, string &orig_contents, const string &old_contents, const string &new_contents);
|
||||
virtual bool atomic_read_contents(const Filename &file, string &contents) const;
|
||||
|
||||
virtual void output(ostream &out) const;
|
||||
|
||||
private:
|
||||
class FileBase;
|
||||
class File;
|
||||
class Directory;
|
||||
|
||||
class FileBase : public TypedReferenceCount {
|
||||
public:
|
||||
INLINE FileBase(const string &basename);
|
||||
virtual ~FileBase();
|
||||
INLINE bool operator < (const FileBase &other) const;
|
||||
|
||||
virtual bool is_directory() const;
|
||||
|
||||
string _basename;
|
||||
time_t _timestamp;
|
||||
|
||||
public:
|
||||
virtual TypeHandle get_type() const {
|
||||
return get_class_type();
|
||||
}
|
||||
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
|
||||
static TypeHandle get_class_type() {
|
||||
return _type_handle;
|
||||
}
|
||||
static void init_type() {
|
||||
TypedReferenceCount::init_type();
|
||||
register_type(_type_handle, "VirtualFileMountRamdisk::FileBase",
|
||||
TypedReferenceCount::get_class_type());
|
||||
}
|
||||
|
||||
private:
|
||||
static TypeHandle _type_handle;
|
||||
};
|
||||
|
||||
class File : public FileBase {
|
||||
public:
|
||||
INLINE File(const string &basename);
|
||||
|
||||
stringstream _data;
|
||||
StreamWrapper _wrapper;
|
||||
|
||||
public:
|
||||
virtual TypeHandle get_type() const {
|
||||
return get_class_type();
|
||||
}
|
||||
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
|
||||
static TypeHandle get_class_type() {
|
||||
return _type_handle;
|
||||
}
|
||||
static void init_type() {
|
||||
FileBase::init_type();
|
||||
register_type(_type_handle, "VirtualFileMountRamdisk::File",
|
||||
FileBase::get_class_type());
|
||||
}
|
||||
|
||||
private:
|
||||
static TypeHandle _type_handle;
|
||||
};
|
||||
|
||||
typedef pset<PT(FileBase), indirect_less<FileBase *> > Files;
|
||||
|
||||
class Directory : public FileBase {
|
||||
public:
|
||||
INLINE Directory(const string &basename);
|
||||
|
||||
virtual bool is_directory() const;
|
||||
|
||||
PT(FileBase) do_find_file(const string &filename) const;
|
||||
PT(File) do_create_file(const string &filename);
|
||||
PT(Directory) do_make_directory(const string &filename);
|
||||
PT(FileBase) do_delete_file(const string &filename);
|
||||
bool do_scan_directory(vector_string &contents) const;
|
||||
|
||||
Files _files;
|
||||
|
||||
public:
|
||||
virtual TypeHandle get_type() const {
|
||||
return get_class_type();
|
||||
}
|
||||
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
|
||||
static TypeHandle get_class_type() {
|
||||
return _type_handle;
|
||||
}
|
||||
static void init_type() {
|
||||
FileBase::init_type();
|
||||
register_type(_type_handle, "VirtualFileMountRamdisk::Directory",
|
||||
FileBase::get_class_type());
|
||||
}
|
||||
|
||||
private:
|
||||
static TypeHandle _type_handle;
|
||||
};
|
||||
|
||||
Directory _root;
|
||||
MutexImpl _lock;
|
||||
|
||||
public:
|
||||
virtual TypeHandle get_type() const {
|
||||
return get_class_type();
|
||||
}
|
||||
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
|
||||
static TypeHandle get_class_type() {
|
||||
return _type_handle;
|
||||
}
|
||||
static void init_type() {
|
||||
VirtualFileMount::init_type();
|
||||
register_type(_type_handle, "VirtualFileMountRamdisk",
|
||||
VirtualFileMount::get_class_type());
|
||||
FileBase::init_type();
|
||||
File::init_type();
|
||||
Directory::init_type();
|
||||
}
|
||||
|
||||
private:
|
||||
static TypeHandle _type_handle;
|
||||
};
|
||||
|
||||
#include "virtualFileMountRamdisk.I"
|
||||
|
||||
#endif
|
||||
|
|
@ -59,6 +59,53 @@ create_file(const Filename &file) {
|
|||
return pathname.open_write(stream, false);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountSystem::delete_file
|
||||
// Access: Public, Virtual
|
||||
// Description: Attempts to delete the indicated file or directory
|
||||
// within the mount. This can remove a single file or
|
||||
// an empty directory. It will not remove a nonempty
|
||||
// directory. Returns true on success, false on
|
||||
// failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMountSystem::
|
||||
delete_file(const Filename &file) {
|
||||
Filename pathname(_physical_filename, file);
|
||||
return pathname.unlink() || pathname.rmdir();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountSystem::rename_file
|
||||
// Access: Public
|
||||
// Description: Attempts to rename the contents of the indicated file
|
||||
// to the indicated file. Both filenames will be within
|
||||
// the mount. Returns true on success, false on
|
||||
// failure. If this returns false, this will be
|
||||
// attempted again with a copy-and-delete operation.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMountSystem::
|
||||
rename_file(const Filename &orig_filename, const Filename &new_filename) {
|
||||
Filename orig_pathname(_physical_filename, orig_filename);
|
||||
Filename new_pathname(_physical_filename, new_filename);
|
||||
return orig_pathname.rename_to(new_pathname);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountSystem::copy_file
|
||||
// Access: Public
|
||||
// Description: Attempts to copy the contents of the indicated file
|
||||
// to the indicated file. Both filenames will be within
|
||||
// the mount. Returns true on success, false on
|
||||
// failure. If this returns false, the copy will be
|
||||
// performed by explicit read-and-write operations.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMountSystem::
|
||||
copy_file(const Filename &orig_filename, const Filename &new_filename) {
|
||||
Filename orig_pathname(_physical_filename, orig_filename);
|
||||
Filename new_pathname(_physical_filename, new_filename);
|
||||
return orig_pathname.copy_to(new_pathname);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountSystem::make_directory
|
||||
// Access: Public, Virtual
|
||||
|
|
@ -383,6 +430,46 @@ scan_directory(vector_string &contents, const Filename &dir) const {
|
|||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountSystem::atomic_compare_and_exchange_contents
|
||||
// Access: Public, Virtual
|
||||
// Description: See Filename::atomic_compare_and_exchange_contents().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMountSystem::
|
||||
atomic_compare_and_exchange_contents(const Filename &file, string &orig_contents,
|
||||
const string &old_contents,
|
||||
const string &new_contents) {
|
||||
#ifdef WIN32
|
||||
// First ensure that the file exists to validate its case.
|
||||
if (VirtualFileSystem::get_global_ptr()->vfs_case_sensitive) {
|
||||
if (!has_file(file)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif // WIN32
|
||||
Filename pathname(_physical_filename, file);
|
||||
return pathname.atomic_compare_and_exchange_contents(orig_contents, old_contents, new_contents);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountSystem::atomic_read_contents
|
||||
// Access: Public, Virtual
|
||||
// Description: See Filename::atomic_read_contents().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileMountSystem::
|
||||
atomic_read_contents(const Filename &file, string &contents) const {
|
||||
#ifdef WIN32
|
||||
// First ensure that the file exists to validate its case.
|
||||
if (VirtualFileSystem::get_global_ptr()->vfs_case_sensitive) {
|
||||
if (!has_file(file)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif // WIN32
|
||||
Filename pathname(_physical_filename, file);
|
||||
return pathname.atomic_read_contents(contents);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileMountSystem::output
|
||||
// Access: Public, Virtual
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@ public:
|
|||
virtual bool has_file(const Filename &file) const;
|
||||
virtual bool create_file(const Filename &file);
|
||||
virtual bool make_directory(const Filename &file);
|
||||
virtual bool delete_file(const Filename &file);
|
||||
virtual bool rename_file(const Filename &orig_filename, const Filename &new_filename);
|
||||
virtual bool copy_file(const Filename &orig_filename, const Filename &new_filename);
|
||||
virtual bool is_directory(const Filename &file) const;
|
||||
virtual bool is_regular_file(const Filename &file) const;
|
||||
virtual bool is_writable(const Filename &file) const;
|
||||
|
|
@ -52,6 +55,9 @@ public:
|
|||
virtual bool scan_directory(vector_string &contents,
|
||||
const Filename &dir) const;
|
||||
|
||||
virtual bool atomic_compare_and_exchange_contents(const Filename &file, string &orig_contents, const string &old_contents, const string &new_contents);
|
||||
virtual bool atomic_read_contents(const Filename &file, string &contents) const;
|
||||
|
||||
virtual void output(ostream &out) const;
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -99,6 +99,115 @@ is_writable() const {
|
|||
return _mount->is_writable(_local_filename);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileSimple::delete_file
|
||||
// Access: Public
|
||||
// Description: Attempts to delete this file or directory. This can
|
||||
// remove a single file or an empty directory. It will
|
||||
// not remove a nonempty directory. Returns true on
|
||||
// success, false on failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileSimple::
|
||||
delete_file() {
|
||||
return _mount->delete_file(_local_filename);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileSimple::rename_file
|
||||
// Access: Public
|
||||
// Description: Attempts to move or rename this file or directory.
|
||||
// If the original file is an ordinary file, it will
|
||||
// quietly replace any already-existing file in the new
|
||||
// filename (but not a directory). If the original file
|
||||
// is a directory, the new filename must not already
|
||||
// exist.
|
||||
//
|
||||
// If the file is a directory, the new filename must be
|
||||
// within the same mount point. If the file is an
|
||||
// ordinary file, the new filename may be anywhere; but
|
||||
// if it is not within the same mount point then the
|
||||
// rename operation is automatically performed as a
|
||||
// two-step copy-and-delete operation.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileSimple::
|
||||
rename_file(VirtualFile *new_file) {
|
||||
if (new_file->is_of_type(VirtualFileSimple::get_class_type())) {
|
||||
VirtualFileSimple *new_file_simple = DCAST(VirtualFileSimple, new_file);
|
||||
if (new_file_simple->_mount == _mount) {
|
||||
// Same mount pount.
|
||||
if (_mount->rename_file(_local_filename, new_file_simple->_local_filename)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Different mount point, or the mount doesn't support renaming. Do
|
||||
// it by hand.
|
||||
if (is_regular_file() && !new_file->is_directory()) {
|
||||
// copy-and-delete.
|
||||
new_file->delete_file();
|
||||
if (copy_file(new_file)) {
|
||||
delete_file();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileSimple::copy_file
|
||||
// Access: Public
|
||||
// Description: Attempts to copy the contents of this file to the
|
||||
// indicated file. Returns true on success, false on
|
||||
// failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileSimple::
|
||||
copy_file(VirtualFile *new_file) {
|
||||
if (new_file->is_of_type(VirtualFileSimple::get_class_type())) {
|
||||
VirtualFileSimple *new_file_simple = DCAST(VirtualFileSimple, new_file);
|
||||
if (new_file_simple->_mount == _mount) {
|
||||
// Same mount pount.
|
||||
if (_mount->copy_file(_local_filename, new_file_simple->_local_filename)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Different mount point, or the mount doesn't support copying. Do
|
||||
// it by hand.
|
||||
ostream *out = new_file->open_write_file(false, true);
|
||||
istream *in = open_read_file(false);
|
||||
|
||||
static const size_t buffer_size = 4096;
|
||||
char buffer[buffer_size];
|
||||
|
||||
in->read(buffer, buffer_size);
|
||||
size_t count = in->gcount();
|
||||
while (count != 0) {
|
||||
out->write(buffer, count);
|
||||
if (out->fail()) {
|
||||
new_file->close_write_file(out);
|
||||
close_read_file(in);
|
||||
new_file->delete_file();
|
||||
return false;
|
||||
}
|
||||
in->read(buffer, buffer_size);
|
||||
count = in->gcount();
|
||||
}
|
||||
|
||||
if (!in->eof()) {
|
||||
new_file->close_write_file(out);
|
||||
close_read_file(in);
|
||||
new_file->delete_file();
|
||||
return false;
|
||||
}
|
||||
|
||||
new_file->close_write_file(out);
|
||||
close_read_file(in);
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileSimple::open_read_file
|
||||
// Access: Published, Virtual
|
||||
|
|
@ -192,7 +301,7 @@ open_append_file() {
|
|||
// work around compiler issues.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void VirtualFileSimple::
|
||||
close_write_file(ostream *stream) const {
|
||||
close_write_file(ostream *stream) {
|
||||
_mount->close_write_file(stream);
|
||||
}
|
||||
|
||||
|
|
@ -295,6 +404,28 @@ get_system_info(SubfileInfo &info) {
|
|||
return _mount->get_system_info(_local_filename, info);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileSimple::atomic_compare_and_exchange_contents
|
||||
// Access: Public, Virtual
|
||||
// Description: See Filename::atomic_compare_and_exchange_contents().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileSimple::
|
||||
atomic_compare_and_exchange_contents(string &orig_contents,
|
||||
const string &old_contents,
|
||||
const string &new_contents) {
|
||||
return _mount->atomic_compare_and_exchange_contents(_local_filename, orig_contents, old_contents, new_contents);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileSimple::atomic_read_contents
|
||||
// Access: Public, Virtual
|
||||
// Description: See Filename::atomic_read_contents().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileSimple::
|
||||
atomic_read_contents(string &contents) const {
|
||||
return _mount->atomic_read_contents(_local_filename, contents);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileSimple::read_file
|
||||
// Access: Public, Virtual
|
||||
|
|
|
|||
|
|
@ -44,11 +44,15 @@ PUBLISHED:
|
|||
virtual bool is_writable() const;
|
||||
INLINE bool is_implicit_pz_file() const;
|
||||
|
||||
virtual bool delete_file();
|
||||
virtual bool rename_file(VirtualFile *new_file);
|
||||
virtual bool copy_file(VirtualFile *new_file);
|
||||
|
||||
virtual istream *open_read_file(bool auto_unwrap) const;
|
||||
virtual void close_read_file(istream *stream) const;
|
||||
virtual ostream *open_write_file(bool auto_wrap, bool truncate);
|
||||
virtual ostream *open_append_file();
|
||||
virtual void close_write_file(ostream *stream) const;
|
||||
virtual void close_write_file(ostream *stream);
|
||||
virtual iostream *open_read_write_file(bool truncate);
|
||||
virtual iostream *open_read_append_file();
|
||||
virtual void close_read_write_file(iostream *stream);
|
||||
|
|
@ -59,6 +63,9 @@ PUBLISHED:
|
|||
virtual bool get_system_info(SubfileInfo &info);
|
||||
|
||||
public:
|
||||
virtual bool atomic_compare_and_exchange_contents(string &orig_contents, const string &old_contents, const string &new_contents);
|
||||
virtual bool atomic_read_contents(string &contents) const;
|
||||
|
||||
virtual bool read_file(pvector<unsigned char> &result, bool auto_unwrap) const;
|
||||
virtual bool write_file(const unsigned char *data, size_t data_size, bool auto_wrap);
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include "virtualFileComposite.h"
|
||||
#include "virtualFileMount.h"
|
||||
#include "virtualFileMountMultifile.h"
|
||||
#include "virtualFileMountRamdisk.h"
|
||||
#include "virtualFileMountSystem.h"
|
||||
#include "streamWrapper.h"
|
||||
#include "dSearchPath.h"
|
||||
|
|
@ -510,9 +511,37 @@ get_cwd() const {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileSystem::
|
||||
make_directory(const Filename &filename) {
|
||||
((VirtualFileSystem *)this)->_lock.acquire();
|
||||
_lock.acquire();
|
||||
PT(VirtualFile) result = do_get_file(filename, OF_make_directory);
|
||||
((VirtualFileSystem *)this)->_lock.release();
|
||||
_lock.release();
|
||||
return result->is_directory();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileSystem::make_directory_full
|
||||
// Access: Published
|
||||
// Description: Attempts to create a directory within the file
|
||||
// system. Will also create any intervening directories
|
||||
// needed. Returns true on success, false on failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileSystem::
|
||||
make_directory_full(const Filename &filename) {
|
||||
_lock.acquire();
|
||||
|
||||
// First, make sure everything up to the last path is known. We
|
||||
// don't care too much if any of these fail; maybe they failed
|
||||
// because the directory was already there.
|
||||
string dirname = filename;
|
||||
size_t slash = dirname.find('/', 1);
|
||||
while (slash != string::npos) {
|
||||
Filename component(dirname.substr(0, slash));
|
||||
do_get_file(component, OF_make_directory);
|
||||
slash = dirname.find('/', slash + 1);
|
||||
}
|
||||
|
||||
// Now make the last one, and check the return value.
|
||||
PT(VirtualFile) result = do_get_file(filename, OF_make_directory);
|
||||
_lock.release();
|
||||
return result->is_directory();
|
||||
}
|
||||
|
||||
|
|
@ -594,6 +623,82 @@ find_file(const Filename &filename, const DSearchPath &searchpath,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileSystem::delete_file
|
||||
// Access: Public
|
||||
// Description: Attempts to delete the indicated file or directory.
|
||||
// This can remove a single file or an empty directory.
|
||||
// It will not remove a nonempty directory. Returns
|
||||
// true on success, false on failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileSystem::
|
||||
delete_file(const Filename &filename) {
|
||||
PT(VirtualFile) file = get_file(filename, true);
|
||||
if (file == (VirtualFile *)NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return file->delete_file();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileSystem::rename_file
|
||||
// Access: Public
|
||||
// Description: Attempts to move or rename the indicated file or
|
||||
// directory. If the original file is an ordinary file,
|
||||
// it will quietly replace any already-existing file in
|
||||
// the new filename (but not a directory). If the
|
||||
// original file is a directory, the new filename must
|
||||
// not already exist.
|
||||
//
|
||||
// If the file is a directory, the new filename must be
|
||||
// within the same mount point. If the file is an
|
||||
// ordinary file, the new filename may be anywhere; but
|
||||
// if it is not within the same mount point then the
|
||||
// rename operation is automatically performed as a
|
||||
// two-step copy-and-delete operation.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileSystem::
|
||||
rename_file(const Filename &orig_filename, const Filename &new_filename) {
|
||||
_lock.acquire();
|
||||
PT(VirtualFile) orig_file = do_get_file(orig_filename, OF_status_only);
|
||||
if (orig_file == (VirtualFile *)NULL) {
|
||||
_lock.release();
|
||||
return false;
|
||||
}
|
||||
|
||||
PT(VirtualFile) new_file = do_get_file(new_filename, OF_status_only | OF_allow_nonexist);
|
||||
if (new_file == (VirtualFile *)NULL) {
|
||||
_lock.release();
|
||||
return false;
|
||||
}
|
||||
|
||||
_lock.release();
|
||||
|
||||
return orig_file->rename_file(new_file);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileSystem::copy_file
|
||||
// Access: Public
|
||||
// Description: Attempts to copy the contents of the indicated file
|
||||
// to the indicated file. Returns true on success,
|
||||
// false on failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileSystem::
|
||||
copy_file(const Filename &orig_filename, const Filename &new_filename) {
|
||||
PT(VirtualFile) orig_file = get_file(orig_filename, true);
|
||||
if (orig_file == (VirtualFile *)NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PT(VirtualFile) new_file = get_file(new_filename, true);
|
||||
if (new_file == (VirtualFile *)NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return orig_file->copy_file(new_file);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileSystem::resolve_filename
|
||||
|
|
@ -779,23 +884,37 @@ get_global_ptr() {
|
|||
mount_desc = ExecutionEnvironment::expand_string(mount_desc);
|
||||
Filename physical_filename = Filename::from_os_specific(mount_desc);
|
||||
|
||||
int flags = 0;
|
||||
int flags;
|
||||
string password;
|
||||
|
||||
// Split the options up by commas.
|
||||
size_t p = 0;
|
||||
size_t q = options.find(',', p);
|
||||
while (q != string::npos) {
|
||||
parse_option(options.substr(p, q - p),
|
||||
flags, password);
|
||||
p = q + 1;
|
||||
q = options.find(',', p);
|
||||
}
|
||||
parse_option(options.substr(p), flags, password);
|
||||
|
||||
parse_options(options, flags, password);
|
||||
_global_ptr->mount(physical_filename, mount_point, flags, password);
|
||||
}
|
||||
}
|
||||
|
||||
ConfigVariableString vfs_mount_ramdisk
|
||||
("vfs-mount-ramdisk", "",
|
||||
PRC_DESC("vfs-mount-ramdisk mount-point [options]"));
|
||||
if (!vfs_mount_ramdisk.empty()) {
|
||||
string mount_point = vfs_mount_ramdisk;
|
||||
string options;
|
||||
|
||||
size_t space = mount_point.rfind(' ');
|
||||
if (space != string::npos) {
|
||||
// If there's a space, we have the optional options field.
|
||||
options = mount_point.substr(space + 1);
|
||||
while (space > 0 && isspace(mount_point[space - 1])) {
|
||||
--space;
|
||||
}
|
||||
mount_point = mount_point.substr(0, space);
|
||||
}
|
||||
|
||||
int flags;
|
||||
string password;
|
||||
parse_options(options, flags, password);
|
||||
|
||||
PT(VirtualFileMount) ramdisk = new VirtualFileMountRamdisk;
|
||||
_global_ptr->mount(ramdisk, mount_point, flags);
|
||||
}
|
||||
}
|
||||
|
||||
return _global_ptr;
|
||||
|
|
@ -1037,6 +1156,38 @@ close_read_write_file(iostream *stream) {
|
|||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileSystem::atomic_compare_and_exchange_contents
|
||||
// Access: Public
|
||||
// Description: See Filename::atomic_compare_and_exchange_contents().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileSystem::
|
||||
atomic_compare_and_exchange_contents(const Filename &filename, string &orig_contents,
|
||||
const string &old_contents,
|
||||
const string &new_contents) {
|
||||
PT(VirtualFile) file = create_file(filename);
|
||||
if (file == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return file->atomic_compare_and_exchange_contents(orig_contents, old_contents, new_contents);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileSystem::atomic_read_contents
|
||||
// Access: Public
|
||||
// Description: See Filename::atomic_read_contents().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool VirtualFileSystem::
|
||||
atomic_read_contents(const Filename &filename, string &contents) const {
|
||||
PT(VirtualFile) file = get_file(filename, false);
|
||||
if (file == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return file->atomic_read_contents(contents);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileSystem::scan_mount_points
|
||||
// Access: Public
|
||||
|
|
@ -1081,6 +1232,30 @@ scan_mount_points(vector_string &names, const Filename &path) const {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileSystem::parse_options
|
||||
// Access: Public, Static
|
||||
// Description: Parses all of the option flags in the options list on
|
||||
// the vfs-mount Config.prc line.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void VirtualFileSystem::
|
||||
parse_options(const string &options, int &flags, string &password) {
|
||||
flags = 0;
|
||||
password = string();
|
||||
|
||||
// Split the options up by commas.
|
||||
size_t p = 0;
|
||||
size_t q = options.find(',', p);
|
||||
while (q != string::npos) {
|
||||
parse_option(options.substr(p, q - p),
|
||||
flags, password);
|
||||
p = q + 1;
|
||||
q = options.find(',', p);
|
||||
}
|
||||
parse_option(options.substr(p), flags, password);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: VirtualFileSystem::parse_option
|
||||
// Access: Public, Static
|
||||
|
|
@ -1148,7 +1323,9 @@ do_mount(VirtualFileMount *mount, const Filename &mount_point, int flags) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
PT(VirtualFile) VirtualFileSystem::
|
||||
do_get_file(const Filename &filename, int open_flags) const {
|
||||
nassertr(!filename.empty(), NULL);
|
||||
if (filename.empty()) {
|
||||
return NULL;
|
||||
}
|
||||
Filename pathname(filename);
|
||||
if (pathname.is_local()) {
|
||||
pathname = Filename(_cwd, filename);
|
||||
|
|
@ -1262,7 +1439,7 @@ consider_match(PT(VirtualFile) &found_file, VirtualFileComposite *&composite_fil
|
|||
int open_flags) const {
|
||||
PT(VirtualFile) vfile =
|
||||
mount->make_virtual_file(local_filename, original_filename, false, open_flags);
|
||||
if (!vfile->has_file()) {
|
||||
if (!vfile->has_file() && ((open_flags & OF_allow_nonexist) == 0)) {
|
||||
// Keep looking.
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,12 +69,16 @@ PUBLISHED:
|
|||
BLOCKING bool chdir(const Filename &new_directory);
|
||||
BLOCKING Filename get_cwd() const;
|
||||
BLOCKING bool make_directory(const Filename &filename);
|
||||
BLOCKING bool make_directory_full(const Filename &filename);
|
||||
|
||||
BLOCKING PT(VirtualFile) get_file(const Filename &filename, bool status_only = false) const;
|
||||
BLOCKING PT(VirtualFile) create_file(const Filename &filename);
|
||||
BLOCKING PT(VirtualFile) find_file(const Filename &filename,
|
||||
const DSearchPath &searchpath,
|
||||
bool status_only = false) const;
|
||||
BLOCKING bool delete_file(const Filename &filename);
|
||||
BLOCKING bool rename_file(const Filename &orig_filename, const Filename &new_filename);
|
||||
BLOCKING bool copy_file(const Filename &orig_filename, const Filename &new_filename);
|
||||
|
||||
BLOCKING bool resolve_filename(Filename &filename, const DSearchPath &searchpath,
|
||||
const string &default_extension = string()) const;
|
||||
|
|
@ -115,14 +119,19 @@ PUBLISHED:
|
|||
BLOCKING static void close_read_write_file(iostream *stream);
|
||||
|
||||
public:
|
||||
bool atomic_compare_and_exchange_contents(const Filename &filename, string &orig_contents, const string &old_contents, const string &new_contents);
|
||||
bool atomic_read_contents(const Filename &filename, string &contents) const;
|
||||
|
||||
INLINE bool read_file(const Filename &filename, string &result, bool auto_unwrap) const;
|
||||
INLINE bool read_file(const Filename &filename, pvector<unsigned char> &result, bool auto_unwrap) const;
|
||||
INLINE bool write_file(const Filename &filename, const unsigned char *data, size_t data_size, bool auto_wrap);
|
||||
|
||||
void scan_mount_points(vector_string &names, const Filename &path) const;
|
||||
|
||||
|
||||
static void parse_options(const string &options,
|
||||
int &flags, string &password);
|
||||
static void parse_option(const string &option,
|
||||
int &flags, string &password);
|
||||
int &flags, string &password);
|
||||
|
||||
public:
|
||||
// These flags are passed to do_get_file() and
|
||||
|
|
@ -132,6 +141,7 @@ public:
|
|||
OF_status_only = 0x0001,
|
||||
OF_create_file = 0x0002,
|
||||
OF_make_directory = 0x0004,
|
||||
OF_allow_nonexist = 0x0008,
|
||||
};
|
||||
|
||||
// These are declared as class instances, instead of as globals, to
|
||||
|
|
|
|||
|
|
@ -222,7 +222,8 @@ open_write(const Filename &bam_filename, bool report_errors) {
|
|||
|
||||
loader_cat.info() << "Writing " << bam_filename << "\n";
|
||||
|
||||
bam_filename.unlink();
|
||||
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
||||
vfs->delete_file(bam_filename);
|
||||
if (!_dout.open(bam_filename)) {
|
||||
if (report_errors) {
|
||||
loader_cat.error() << "Unable to open " << bam_filename << "\n";
|
||||
|
|
|
|||
|
|
@ -118,22 +118,19 @@ set_root(const Filename &root) {
|
|||
flush_index();
|
||||
_root = root;
|
||||
|
||||
// For now, the filename must be a directory. Maybe eventually we
|
||||
// will support writing caches to a Panda multifile (though maybe it
|
||||
// would be better to implement this kind of thing at a lower level,
|
||||
// via a writable VFS, in which case the specified root filename
|
||||
// will still be a "directory").
|
||||
if (!root.is_directory()) {
|
||||
Filename dirname(_root, Filename("."));
|
||||
dirname.make_dir();
|
||||
// The root filename must be a directory.
|
||||
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
||||
if (!vfs->is_directory(_root)) {
|
||||
vfs->make_directory_full(_root);
|
||||
}
|
||||
nassertv(root.is_directory());
|
||||
|
||||
delete _index;
|
||||
_index = new BamCacheIndex;
|
||||
_index_stale_since = 0;
|
||||
read_index();
|
||||
check_cache_size();
|
||||
|
||||
nassertv(vfs->is_directory(_root));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -193,6 +190,7 @@ lookup(const Filename &source_filename, const string &cache_extension) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
bool BamCache::
|
||||
store(BamCacheRecord *record) {
|
||||
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
||||
ReMutexHolder holder(_lock);
|
||||
nassertr(!record->_cache_pathname.empty(), false);
|
||||
nassertr(record->has_data(), false);
|
||||
|
|
@ -227,7 +225,7 @@ store(BamCacheRecord *record) {
|
|||
if (!dout.open(temp_pathname)) {
|
||||
util_cat.error()
|
||||
<< "Could not write cache file: " << temp_pathname << "\n";
|
||||
temp_pathname.unlink();
|
||||
vfs->delete_file(temp_pathname);
|
||||
emergency_read_only();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -235,14 +233,14 @@ store(BamCacheRecord *record) {
|
|||
if (!dout.write_header(_bam_header)) {
|
||||
util_cat.error()
|
||||
<< "Unable to write to " << temp_pathname << "\n";
|
||||
temp_pathname.unlink();
|
||||
vfs->delete_file(temp_pathname);
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
BamWriter writer(&dout);
|
||||
if (!writer.init()) {
|
||||
temp_pathname.unlink();
|
||||
vfs->delete_file(temp_pathname);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -257,12 +255,12 @@ store(BamCacheRecord *record) {
|
|||
}
|
||||
|
||||
if (!writer.write_object(record)) {
|
||||
temp_pathname.unlink();
|
||||
vfs->delete_file(temp_pathname);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!writer.write_object(record->get_data())) {
|
||||
temp_pathname.unlink();
|
||||
vfs->delete_file(temp_pathname);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -275,13 +273,13 @@ store(BamCacheRecord *record) {
|
|||
dout.close();
|
||||
|
||||
// Now move the file into place.
|
||||
if (!temp_pathname.rename_to(cache_pathname) && temp_pathname.exists()) {
|
||||
cache_pathname.unlink();
|
||||
if (!temp_pathname.rename_to(cache_pathname)) {
|
||||
if (!vfs->rename_file(temp_pathname, cache_pathname) && vfs->exists(temp_pathname)) {
|
||||
vfs->delete_file(cache_pathname);
|
||||
if (!vfs->rename_file(temp_pathname, cache_pathname)) {
|
||||
util_cat.error()
|
||||
<< "Unable to rename " << temp_pathname << " to "
|
||||
<< cache_pathname << "\n";
|
||||
temp_pathname.unlink();
|
||||
vfs->delete_file(temp_pathname);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -351,15 +349,17 @@ flush_index() {
|
|||
|
||||
// Now atomically write the name of this index file to the index
|
||||
// reference file.
|
||||
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
||||
Filename index_ref_pathname(_root, Filename("index_name.txt"));
|
||||
string old_index = _index_ref_contents;
|
||||
string new_index = temp_pathname.get_basename() + "\n";
|
||||
string orig_index;
|
||||
if (index_ref_pathname.atomic_compare_and_exchange_contents(orig_index, old_index, new_index)) {
|
||||
|
||||
if (vfs->atomic_compare_and_exchange_contents(index_ref_pathname, orig_index, old_index, new_index)) {
|
||||
// We successfully wrote our version of the index, and no other
|
||||
// process beat us to it. Our index is now the official one.
|
||||
// Remove the old index.
|
||||
_index_pathname.unlink();
|
||||
vfs->delete_file(_index_pathname);
|
||||
_index_pathname = temp_pathname;
|
||||
_index_ref_contents = new_index;
|
||||
_index_stale_since = 0;
|
||||
|
|
@ -369,7 +369,7 @@ flush_index() {
|
|||
// Shoot, some other process updated the index while we were
|
||||
// trying to update it, and they beat us to it. We have to merge,
|
||||
// and try again.
|
||||
temp_pathname.unlink();
|
||||
vfs->delete_file(temp_pathname);
|
||||
_index_pathname = Filename(_root, Filename(trim(orig_index)));
|
||||
_index_ref_contents = orig_index;
|
||||
read_index();
|
||||
|
|
@ -411,7 +411,8 @@ read_index() {
|
|||
if (old_index_pathname == _index_pathname) {
|
||||
// Nope, we just couldn't read it. Delete it and build a new
|
||||
// one.
|
||||
_index_pathname.unlink();
|
||||
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
||||
vfs->delete_file(_index_pathname);
|
||||
rebuild_index();
|
||||
flush_index();
|
||||
return;
|
||||
|
|
@ -428,9 +429,10 @@ read_index() {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
bool BamCache::
|
||||
read_index_pathname(Filename &index_pathname, string &index_ref_contents) const {
|
||||
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
||||
index_ref_contents.clear();
|
||||
Filename index_ref_pathname(_root, Filename("index_name.txt"));
|
||||
if (!index_ref_pathname.atomic_read_contents(index_ref_contents)) {
|
||||
if (!vfs->atomic_read_contents(index_ref_pathname, index_ref_contents)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -555,8 +557,10 @@ merge_index(BamCacheIndex *new_index) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
void BamCache::
|
||||
rebuild_index() {
|
||||
vector_string contents;
|
||||
if (!_root.scan_directory(contents)) {
|
||||
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
||||
|
||||
PT(VirtualFileList) contents = vfs->scan_directory(_root);
|
||||
if (contents == NULL) {
|
||||
util_cat.error()
|
||||
<< "Unable to read directory " << _root << ", caching disabled.\n";
|
||||
set_active(false);
|
||||
|
|
@ -566,9 +570,10 @@ rebuild_index() {
|
|||
delete _index;
|
||||
_index = new BamCacheIndex;
|
||||
|
||||
vector_string::const_iterator ci;
|
||||
for (ci = contents.begin(); ci != contents.end(); ++ci) {
|
||||
Filename filename(*ci);
|
||||
int num_files = contents->get_num_files();
|
||||
for (int ci = 0; ci < num_files; ++ci) {
|
||||
VirtualFile *file = contents->get_file(ci);
|
||||
Filename filename = file->get_filename();
|
||||
if (filename.get_extension() == "bam" ||
|
||||
filename.get_extension() == "txo") {
|
||||
Filename pathname(_root, filename);
|
||||
|
|
@ -576,7 +581,7 @@ rebuild_index() {
|
|||
PT(BamCacheRecord) record = do_read_record(pathname, false);
|
||||
if (record == (BamCacheRecord *)NULL) {
|
||||
// Well, it was invalid, so blow it away.
|
||||
pathname.unlink();
|
||||
file->delete_file();
|
||||
|
||||
} else {
|
||||
record->_record_access_time = record->_recorded_time;
|
||||
|
|
@ -585,7 +590,7 @@ rebuild_index() {
|
|||
if (!inserted) {
|
||||
util_cat.info()
|
||||
<< "Multiple cache files defining " << record->get_source_pathname() << "\n";
|
||||
pathname.unlink();
|
||||
file->delete_file();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -646,8 +651,9 @@ check_cache_size() {
|
|||
// Never mind; the cache is empty.
|
||||
break;
|
||||
}
|
||||
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
||||
Filename cache_pathname(_root, record->get_cache_filename());
|
||||
cache_pathname.unlink();
|
||||
vfs->delete_file(cache_pathname);
|
||||
}
|
||||
mark_index_stale();
|
||||
}
|
||||
|
|
@ -722,30 +728,31 @@ do_read_index(const Filename &index_pathname) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
bool BamCache::
|
||||
do_write_index(const Filename &index_pathname, const BamCacheIndex *index) {
|
||||
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
||||
DatagramOutputFile dout;
|
||||
if (!dout.open(index_pathname)) {
|
||||
util_cat.error()
|
||||
<< "Could not write index file: " << index_pathname << "\n";
|
||||
index_pathname.unlink();
|
||||
vfs->delete_file(index_pathname);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!dout.write_header(_bam_header)) {
|
||||
util_cat.error()
|
||||
<< "Unable to write to " << index_pathname << "\n";
|
||||
index_pathname.unlink();
|
||||
vfs->delete_file(index_pathname);
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
BamWriter writer(&dout);
|
||||
if (!writer.init()) {
|
||||
index_pathname.unlink();
|
||||
vfs->delete_file(index_pathname);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!writer.write_object(index)) {
|
||||
index_pathname.unlink();
|
||||
vfs->delete_file(index_pathname);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -788,6 +795,7 @@ PT(BamCacheRecord) BamCache::
|
|||
read_record(const Filename &source_pathname,
|
||||
const Filename &cache_filename,
|
||||
int pass) {
|
||||
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
||||
Filename cache_pathname(_root, cache_filename);
|
||||
if (pass != 0) {
|
||||
ostringstream strm;
|
||||
|
|
@ -806,7 +814,7 @@ read_record(const Filename &source_pathname,
|
|||
PT(BamCacheRecord) record = do_read_record(cache_pathname, true);
|
||||
if (record == (BamCacheRecord *)NULL) {
|
||||
// Well, it was invalid, so blow it away, and make a new one.
|
||||
cache_pathname.unlink();
|
||||
vfs->delete_file(cache_pathname);
|
||||
remove_from_index(source_pathname);
|
||||
|
||||
PT(BamCacheRecord) record =
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "partGroup.h"
|
||||
#include "cardMaker.h"
|
||||
#include "bamCache.h"
|
||||
#include "virtualFileSystem.h"
|
||||
#include "panda_getopt.h"
|
||||
|
||||
// By including checkPandaVersion.h, we guarantee that runtime
|
||||
|
|
@ -324,11 +325,12 @@ main(int argc, char *argv[]) {
|
|||
window->load_models(framework.get_models(), argc, argv);
|
||||
|
||||
if (delete_models) {
|
||||
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
||||
for (int i = 1; i < argc && argv[i] != (char *)NULL; i++) {
|
||||
Filename model = Filename::from_os_specific(argv[i]);
|
||||
if (model.exists()) {
|
||||
if (vfs->exists(model)) {
|
||||
nout << "Deleting " << model << "\n";
|
||||
model.unlink();
|
||||
vfs->delete_file(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue