2003-11-27 07:46:36 +00:00
|
|
|
#include "global.h"
|
2003-07-22 07:47:27 +00:00
|
|
|
#include "RageFile.h"
|
|
|
|
|
#include "RageUtil.h"
|
2003-12-04 08:25:59 +00:00
|
|
|
#include "RageFileDriver.h"
|
2003-07-22 07:47:27 +00:00
|
|
|
|
2003-12-04 08:25:59 +00:00
|
|
|
RageFile::RageFile()
|
|
|
|
|
{
|
|
|
|
|
m_File = NULL;
|
|
|
|
|
m_BufUsed = 0;
|
|
|
|
|
m_EOF = false;
|
|
|
|
|
m_FilePos = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-21 07:23:29 +00:00
|
|
|
RageFile::RageFile( const CString& path, int mode )
|
2003-11-30 06:20:25 +00:00
|
|
|
{
|
2003-12-04 08:25:59 +00:00
|
|
|
m_File = NULL;
|
2003-12-03 23:50:53 +00:00
|
|
|
m_BufUsed = 0;
|
2003-12-04 08:25:59 +00:00
|
|
|
m_EOF = false;
|
|
|
|
|
m_FilePos = 0;
|
2003-12-03 23:50:53 +00:00
|
|
|
Open(path, mode);
|
2003-11-30 06:20:25 +00:00
|
|
|
}
|
|
|
|
|
|
2003-12-10 05:04:37 +00:00
|
|
|
RageFile::RageFile( const RageFile &cpy )
|
|
|
|
|
{
|
2003-12-10 05:48:31 +00:00
|
|
|
ResetBuf();
|
|
|
|
|
|
|
|
|
|
/* This will copy the file driver, including its internal file pointer. */
|
2003-12-11 07:13:20 +00:00
|
|
|
m_File = FILEMAN->CopyFileObj( cpy.m_File, *this );
|
2003-12-10 05:04:37 +00:00
|
|
|
m_Path = cpy.m_Path;
|
|
|
|
|
m_Mode = cpy.m_Mode;
|
|
|
|
|
m_Error = cpy.m_Error;
|
|
|
|
|
m_EOF = cpy.m_EOF;
|
|
|
|
|
m_FilePos = cpy.m_FilePos;
|
2003-12-10 05:48:31 +00:00
|
|
|
memcpy( this->m_Buffer, cpy.m_Buffer, cpy.m_BufUsed );
|
|
|
|
|
m_BufUsed = cpy.m_BufUsed;
|
2003-12-10 05:04:37 +00:00
|
|
|
}
|
|
|
|
|
|
2003-12-04 08:25:59 +00:00
|
|
|
CString RageFile::GetPath() const
|
|
|
|
|
{
|
|
|
|
|
if ( !IsOpen() )
|
|
|
|
|
return "";
|
|
|
|
|
|
|
|
|
|
return m_File->GetDisplayPath();
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-21 07:23:29 +00:00
|
|
|
bool RageFile::Open( const CString& path, int mode )
|
2003-11-27 07:46:36 +00:00
|
|
|
{
|
2003-12-04 08:25:59 +00:00
|
|
|
ASSERT( FILEMAN );
|
2003-12-03 23:50:53 +00:00
|
|
|
Close();
|
2003-12-04 08:25:59 +00:00
|
|
|
|
|
|
|
|
m_Path = path;
|
|
|
|
|
FixSlashesInPlace(m_Path);
|
|
|
|
|
|
|
|
|
|
m_Mode = mode;
|
|
|
|
|
m_EOF = false;
|
|
|
|
|
m_FilePos = 0;
|
2003-12-03 23:50:53 +00:00
|
|
|
ResetBuf();
|
|
|
|
|
|
2003-12-21 07:23:29 +00:00
|
|
|
if( (m_Mode&READ) && (m_Mode&WRITE) )
|
|
|
|
|
{
|
|
|
|
|
SetError( "Reading and writing are mutually exclusive" );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( !(m_Mode&READ) && !(m_Mode&WRITE) )
|
|
|
|
|
{
|
|
|
|
|
SetError( "Neither reading nor writing specified" );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-04 08:25:59 +00:00
|
|
|
int error;
|
|
|
|
|
m_File = FILEMAN->Open( path, mode, *this, error );
|
|
|
|
|
|
|
|
|
|
if( m_File == NULL )
|
|
|
|
|
{
|
|
|
|
|
SetError( strerror(error) );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
2003-11-27 07:46:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RageFile::Close()
|
|
|
|
|
{
|
2003-12-11 05:32:35 +00:00
|
|
|
FILEMAN->Close( m_File );
|
2003-12-04 08:25:59 +00:00
|
|
|
m_File = NULL;
|
2003-07-22 07:47:27 +00:00
|
|
|
}
|
|
|
|
|
|
2003-12-03 23:50:53 +00:00
|
|
|
void RageFile::ResetBuf()
|
2003-11-27 07:46:36 +00:00
|
|
|
{
|
2003-12-03 23:50:53 +00:00
|
|
|
m_BufUsed = 0;
|
|
|
|
|
m_pBuf = m_Buffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read up to the next \n, and return it in out. Strip the \n. If the \n is
|
|
|
|
|
* preceded by a \r (DOS newline), strip that, too. */
|
2003-12-04 08:25:59 +00:00
|
|
|
int RageFile::GetLine( CString &out )
|
2003-12-03 23:50:53 +00:00
|
|
|
{
|
|
|
|
|
out = "";
|
|
|
|
|
|
2003-12-04 08:25:59 +00:00
|
|
|
if ( !IsOpen() )
|
|
|
|
|
RageException::Throw("\"%s\" is not open.", m_Path.c_str());
|
|
|
|
|
|
2003-12-21 07:23:29 +00:00
|
|
|
if( !(m_Mode&READ) )
|
2003-12-04 08:25:59 +00:00
|
|
|
RageException::Throw("\"%s\" is not open for reading", GetPath().c_str());
|
2003-12-03 23:50:53 +00:00
|
|
|
|
2003-12-04 08:25:59 +00:00
|
|
|
bool GotData = false;
|
2003-12-03 23:50:53 +00:00
|
|
|
while( 1 )
|
|
|
|
|
{
|
|
|
|
|
bool done = false;
|
|
|
|
|
|
|
|
|
|
/* Find the end of the block we'll move to out. */
|
|
|
|
|
char *p = (char *) memchr( m_pBuf, '\n', m_BufUsed );
|
2003-12-04 08:25:59 +00:00
|
|
|
bool ReAddCR = false;
|
2003-12-03 23:50:53 +00:00
|
|
|
if( p == NULL )
|
2003-12-04 08:25:59 +00:00
|
|
|
{
|
|
|
|
|
/* Hack: If the last character of the buffer is \r, then it's likely that an
|
|
|
|
|
* \r\n has been split across buffers. Move everything else, then move the
|
|
|
|
|
* \r to the beginning of the buffer and handle it the next time around the loop. */
|
|
|
|
|
if( m_pBuf[m_BufUsed-1] == '\r' )
|
|
|
|
|
{
|
|
|
|
|
ReAddCR = true;
|
|
|
|
|
--m_BufUsed;
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-03 23:50:53 +00:00
|
|
|
p = m_pBuf+m_BufUsed; /* everything */
|
2003-12-04 08:25:59 +00:00
|
|
|
}
|
2003-12-03 23:50:53 +00:00
|
|
|
else
|
|
|
|
|
done = true;
|
|
|
|
|
|
|
|
|
|
if( p >= m_pBuf )
|
|
|
|
|
{
|
|
|
|
|
char *RealEnd = p;
|
|
|
|
|
if( done && p > m_pBuf && p[-1] == '\r' )
|
|
|
|
|
--RealEnd; /* not including \r */
|
|
|
|
|
out.append( m_pBuf, RealEnd );
|
|
|
|
|
|
|
|
|
|
if( done )
|
|
|
|
|
++p; /* skip \n */
|
2003-12-04 08:25:59 +00:00
|
|
|
|
|
|
|
|
const int used = p-m_pBuf;
|
|
|
|
|
if( used )
|
|
|
|
|
{
|
|
|
|
|
m_BufUsed -= used;
|
|
|
|
|
m_FilePos += used;
|
|
|
|
|
GotData = true;
|
|
|
|
|
m_pBuf = p;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( ReAddCR )
|
|
|
|
|
{
|
|
|
|
|
ASSERT( m_BufUsed == 0 );
|
|
|
|
|
m_pBuf = m_Buffer;
|
|
|
|
|
m_Buffer[m_BufUsed] = '\r';
|
|
|
|
|
++m_BufUsed;
|
2003-12-03 23:50:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( done )
|
|
|
|
|
break;
|
|
|
|
|
|
2003-12-04 08:25:59 +00:00
|
|
|
/* We need more data. */
|
2003-12-03 23:50:53 +00:00
|
|
|
m_pBuf = m_Buffer;
|
|
|
|
|
|
2003-12-04 08:25:59 +00:00
|
|
|
const int size = m_File->Read( m_pBuf+m_BufUsed, sizeof(m_Buffer)-m_BufUsed );
|
|
|
|
|
|
|
|
|
|
/* If we've read data already, then don't mark EOF yet. Wait until the
|
|
|
|
|
* next time we're called. */
|
|
|
|
|
if( size == 0 && !GotData )
|
|
|
|
|
{
|
|
|
|
|
m_EOF = true;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if( size < 0 )
|
|
|
|
|
return -1; // error
|
|
|
|
|
if( size == 0 )
|
2003-12-03 23:50:53 +00:00
|
|
|
break; // EOF or error
|
|
|
|
|
m_BufUsed += size;
|
|
|
|
|
}
|
2003-12-04 08:25:59 +00:00
|
|
|
return GotData? 1:0;
|
|
|
|
|
}
|
|
|
|
|
|
2004-05-04 00:01:24 +00:00
|
|
|
// Always use "\r\n". Even though the program may be running on Unix, the
|
|
|
|
|
// files written to a memory card are likely to be edited using Windows.
|
|
|
|
|
//#if defined(_WIN32)
|
2003-12-04 04:26:49 +00:00
|
|
|
#define NEWLINE "\r\n"
|
2004-05-04 00:01:24 +00:00
|
|
|
//#else
|
|
|
|
|
//#define NEWLINE "\n"
|
|
|
|
|
//#endif
|
2003-12-04 04:26:49 +00:00
|
|
|
|
|
|
|
|
int RageFile::PutLine( const CString &str )
|
|
|
|
|
{
|
2003-12-04 08:25:59 +00:00
|
|
|
if( Write(str) == -1 )
|
2003-12-04 04:26:49 +00:00
|
|
|
return -1;
|
2003-12-04 08:25:59 +00:00
|
|
|
return Write( CString(NEWLINE) );
|
2003-12-04 04:26:49 +00:00
|
|
|
}
|
|
|
|
|
|
2003-12-04 08:25:59 +00:00
|
|
|
|
2003-12-03 23:50:53 +00:00
|
|
|
int RageFile::Read( void *buffer, size_t bytes )
|
|
|
|
|
{
|
|
|
|
|
if( !IsOpen() )
|
2003-12-04 08:25:59 +00:00
|
|
|
RageException::Throw("\"%s\" is not open.", GetPath().c_str());
|
|
|
|
|
|
2003-12-21 07:23:29 +00:00
|
|
|
if( !(m_Mode&READ) )
|
2003-12-04 08:25:59 +00:00
|
|
|
RageException::Throw("\"%s\" is not open for reading", GetPath().c_str());
|
2003-12-03 23:50:53 +00:00
|
|
|
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
int FromBuffer = min( (int) bytes, m_BufUsed );
|
|
|
|
|
memcpy( buffer, m_pBuf, FromBuffer );
|
2003-12-04 08:25:59 +00:00
|
|
|
|
2003-12-03 23:50:53 +00:00
|
|
|
ret += FromBuffer;
|
2003-12-04 08:25:59 +00:00
|
|
|
m_FilePos += FromBuffer;
|
2003-12-03 23:50:53 +00:00
|
|
|
bytes -= FromBuffer;
|
2003-12-04 08:25:59 +00:00
|
|
|
m_BufUsed -= FromBuffer;
|
|
|
|
|
m_pBuf += FromBuffer;
|
|
|
|
|
|
|
|
|
|
buffer = (char *) buffer + FromBuffer;
|
2003-11-27 07:46:36 +00:00
|
|
|
|
2003-12-03 23:50:53 +00:00
|
|
|
if( bytes )
|
|
|
|
|
{
|
2003-12-04 08:25:59 +00:00
|
|
|
int FromFile = m_File->Read( buffer, bytes );
|
2003-12-03 23:50:53 +00:00
|
|
|
if( FromFile < 0 )
|
|
|
|
|
return -1;
|
2003-12-04 08:25:59 +00:00
|
|
|
if( FromFile == 0 )
|
|
|
|
|
m_EOF = true;
|
2003-12-03 23:50:53 +00:00
|
|
|
ret += FromFile;
|
2003-12-04 08:25:59 +00:00
|
|
|
m_FilePos += FromFile;
|
2003-12-03 23:50:53 +00:00
|
|
|
}
|
2003-12-04 08:25:59 +00:00
|
|
|
|
2003-12-03 23:50:53 +00:00
|
|
|
return ret;
|
2003-11-27 07:46:36 +00:00
|
|
|
}
|
|
|
|
|
|
2003-12-04 08:25:59 +00:00
|
|
|
int RageFile::Seek( int offset )
|
2003-11-27 07:46:36 +00:00
|
|
|
{
|
2003-12-04 08:25:59 +00:00
|
|
|
if( !IsOpen() )
|
|
|
|
|
RageException::Throw("\"%s\" is not open.", GetPath().c_str());
|
|
|
|
|
|
2003-12-21 07:23:29 +00:00
|
|
|
if( !(m_Mode&READ) )
|
2003-12-04 08:25:59 +00:00
|
|
|
RageException::Throw("\"%s\" is not open for reading; can't seek", GetPath().c_str());
|
|
|
|
|
|
|
|
|
|
m_EOF = false;
|
|
|
|
|
|
|
|
|
|
/* If the new position is within the buffer, just eat the buffered data. */
|
|
|
|
|
int FromBuffer = offset - m_FilePos;
|
2003-12-08 03:24:00 +00:00
|
|
|
if( 0 <= FromBuffer && FromBuffer <= m_BufUsed )
|
2003-12-04 08:25:59 +00:00
|
|
|
{
|
|
|
|
|
m_FilePos += FromBuffer;
|
|
|
|
|
m_BufUsed -= FromBuffer;
|
|
|
|
|
m_pBuf += FromBuffer;
|
|
|
|
|
|
|
|
|
|
return m_FilePos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* It's not. Clear the buffer and do a real seek. */
|
|
|
|
|
ResetBuf();
|
|
|
|
|
|
2003-12-08 03:24:00 +00:00
|
|
|
if( offset == 0 )
|
|
|
|
|
{
|
|
|
|
|
m_File->Rewind();
|
2004-02-12 05:24:22 +00:00
|
|
|
m_FilePos = 0;
|
2003-12-08 03:24:00 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-04 08:25:59 +00:00
|
|
|
int pos = m_File->Seek( offset );
|
|
|
|
|
if( pos == -1 )
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
m_FilePos = pos;
|
|
|
|
|
return pos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int RageFile::SeekCur( int offset )
|
|
|
|
|
{
|
|
|
|
|
ASSERT( offset >= 0 );
|
|
|
|
|
|
|
|
|
|
if( !IsOpen() )
|
|
|
|
|
RageException::Throw("\"%s\" is not open.", GetPath().c_str());
|
|
|
|
|
|
2003-12-21 07:23:29 +00:00
|
|
|
if( !(m_Mode&READ) )
|
2003-12-04 08:25:59 +00:00
|
|
|
RageException::Throw("\"%s\" is not open for reading; can't seek", GetPath().c_str());
|
|
|
|
|
|
|
|
|
|
if( !offset || m_EOF )
|
|
|
|
|
return m_FilePos;
|
|
|
|
|
|
|
|
|
|
int FromBuffer = min( offset, m_BufUsed );
|
|
|
|
|
m_FilePos += FromBuffer;
|
|
|
|
|
offset -= FromBuffer;
|
|
|
|
|
m_BufUsed -= FromBuffer;
|
|
|
|
|
m_pBuf += FromBuffer;
|
|
|
|
|
|
|
|
|
|
if( offset )
|
|
|
|
|
{
|
|
|
|
|
int pos = m_File->SeekCur( offset );
|
|
|
|
|
if( pos == -1 )
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
m_FilePos = pos;
|
|
|
|
|
}
|
|
|
|
|
return m_FilePos;
|
|
|
|
|
}
|
|
|
|
|
|
2004-02-17 21:42:06 +00:00
|
|
|
int RageFile::GetFileSize() const
|
2003-12-04 08:25:59 +00:00
|
|
|
{
|
|
|
|
|
if( !IsOpen() )
|
|
|
|
|
RageException::Throw("\"%s\" is not open.", GetPath().c_str());
|
|
|
|
|
|
2003-12-21 07:23:29 +00:00
|
|
|
if( !(m_Mode&READ) )
|
2003-12-04 08:25:59 +00:00
|
|
|
RageException::Throw("\"%s\" is not open for reading; can't GetFileSize", GetPath().c_str());
|
|
|
|
|
|
2004-02-17 21:42:06 +00:00
|
|
|
/* GetFileSize() may need to do non-const-like things--the default implementation reads
|
|
|
|
|
* until the end of file to emulate it. However, it should always restore the state to
|
|
|
|
|
* the way it was, so pretend it's const. */
|
|
|
|
|
return const_cast<RageFileObj*>(m_File)->GetFileSize();
|
2003-11-27 07:46:36 +00:00
|
|
|
}
|
2003-12-03 23:50:53 +00:00
|
|
|
|
2003-12-04 08:25:59 +00:00
|
|
|
void RageFile::Rewind()
|
|
|
|
|
{
|
|
|
|
|
if( !IsOpen() )
|
|
|
|
|
RageException::Throw("\"%s\" is not open.", GetPath().c_str());
|
|
|
|
|
|
2003-12-21 07:23:29 +00:00
|
|
|
if( !(m_Mode&READ) )
|
2003-12-04 08:25:59 +00:00
|
|
|
RageException::Throw("\"%s\" is not open for reading; can't GetFileSize", GetPath().c_str());
|
|
|
|
|
|
|
|
|
|
m_EOF = false;
|
|
|
|
|
|
2003-12-10 05:04:37 +00:00
|
|
|
m_FilePos = 0;
|
2003-12-04 08:25:59 +00:00
|
|
|
m_File->Rewind();
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-21 09:17:39 +00:00
|
|
|
int RageFile::Read( CString &buffer, int bytes )
|
2003-12-10 05:04:37 +00:00
|
|
|
{
|
2003-12-21 18:35:00 +00:00
|
|
|
buffer.erase( buffer.begin(), buffer.end() );
|
2003-12-21 09:17:39 +00:00
|
|
|
buffer.reserve( bytes != -1? bytes: this->GetFileSize() );
|
|
|
|
|
|
|
|
|
|
int ret = 0;
|
|
|
|
|
char buf[4096];
|
|
|
|
|
while( bytes == -1 || ret < bytes )
|
|
|
|
|
{
|
|
|
|
|
int ToRead = sizeof(buf);
|
|
|
|
|
if( bytes != -1 )
|
|
|
|
|
ToRead = min( ToRead, bytes-ret );
|
|
|
|
|
|
|
|
|
|
const int got = Read( buf, ToRead );
|
|
|
|
|
if( got == 0 )
|
|
|
|
|
break;
|
|
|
|
|
if( got == -1 )
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
buffer.append( buf, got );
|
|
|
|
|
ret += got;
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-10 05:04:37 +00:00
|
|
|
return ret;
|
|
|
|
|
}
|
2003-12-04 08:25:59 +00:00
|
|
|
|
2003-12-12 07:47:38 +00:00
|
|
|
int RageFile::Write( const void *buffer, size_t bytes )
|
|
|
|
|
{
|
|
|
|
|
if( !IsOpen() )
|
|
|
|
|
RageException::Throw("\"%s\" is not open.", GetPath().c_str());
|
|
|
|
|
|
2003-12-21 07:23:29 +00:00
|
|
|
if( !(m_Mode&WRITE) )
|
2003-12-12 07:47:38 +00:00
|
|
|
RageException::Throw("\"%s\" is not open for writing", GetPath().c_str());
|
|
|
|
|
|
|
|
|
|
return m_File->Write( buffer, bytes );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-12-04 21:10:16 +00:00
|
|
|
int RageFile::Write( const void *buffer, size_t bytes, int nmemb )
|
|
|
|
|
{
|
|
|
|
|
/* Simple write. We never return partial writes. */
|
|
|
|
|
int ret = Write( buffer, bytes*nmemb ) / bytes;
|
|
|
|
|
if( ret == -1 )
|
|
|
|
|
return -1;
|
|
|
|
|
return ret / bytes;
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-12 07:47:38 +00:00
|
|
|
int RageFile::Flush()
|
|
|
|
|
{
|
2003-12-21 07:23:29 +00:00
|
|
|
if( !m_File )
|
|
|
|
|
{
|
|
|
|
|
SetError( "Not open" );
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-12 07:47:38 +00:00
|
|
|
return m_File->Flush();
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-04 21:10:16 +00:00
|
|
|
int RageFile::Read( void *buffer, size_t bytes, int nmemb )
|
|
|
|
|
{
|
|
|
|
|
const int ret = Read( buffer, bytes*nmemb );
|
|
|
|
|
if( ret == -1 )
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
/* If we're reading 10-byte blocks, and we got 27 bytes, we have 7 extra bytes.
|
|
|
|
|
* Seek back. */
|
|
|
|
|
const int extra = ret % bytes;
|
|
|
|
|
Seek( Tell()-extra );
|
|
|
|
|
|
|
|
|
|
return ret/bytes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int RageFile::Seek( int offset, int whence )
|
|
|
|
|
{
|
|
|
|
|
switch( whence )
|
|
|
|
|
{
|
|
|
|
|
case SEEK_CUR:
|
|
|
|
|
return SeekCur( (int) offset );
|
|
|
|
|
case SEEK_END:
|
|
|
|
|
offset += GetFileSize();
|
|
|
|
|
}
|
|
|
|
|
return Seek( (int) offset );
|
|
|
|
|
}
|
2004-05-06 00:42:06 +00:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2003-2004 Glenn Maynard, Chris Danford, Steve Checkoway
|
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
|
* copy of this software and associated documentation files (the
|
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
|
* distribute, and/or sell copies of the Software, and to permit persons to
|
|
|
|
|
* whom the Software is furnished to do so, provided that the above
|
|
|
|
|
* copyright notice(s) and this permission notice appear in all copies of
|
|
|
|
|
* the Software and that both the above copyright notice(s) and this
|
|
|
|
|
* permission notice appear in supporting documentation.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
|
|
|
|
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
|
|
|
|
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
|
|
|
|
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
|
|
|
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
|
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
*/
|