488 lines
9.8 KiB
C++
488 lines
9.8 KiB
C++
/*******************************************************************************
|
|
* fileinputoutput.cpp
|
|
*
|
|
* This module implements the classes handling file input and output.
|
|
*
|
|
* ---------------------------------------------------------------------------
|
|
* Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
|
|
* Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd.
|
|
*
|
|
* POV-Ray is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* POV-Ray is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
* ---------------------------------------------------------------------------
|
|
* POV-Ray is based on the popular DKB raytracer version 2.12.
|
|
* DKBTrace was originally written by David K. Buck.
|
|
* DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
|
|
* ---------------------------------------------------------------------------
|
|
* $File: //depot/public/povray/3.x/source/base/fileinputoutput.cpp $
|
|
* $Revision: #1 $
|
|
* $Change: 6069 $
|
|
* $DateTime: 2013/11/06 11:59:40 $
|
|
* $Author: chrisc $
|
|
*******************************************************************************/
|
|
|
|
#include <cstdlib>
|
|
#include <cstdarg>
|
|
#include <cstring>
|
|
|
|
// configbase.h must always be the first POV file included within base *.cpp files
|
|
#include "base/configbase.h"
|
|
|
|
#include "base/fileinputoutput.h"
|
|
#include "base/stringutilities.h"
|
|
#include "base/platformbase.h"
|
|
#include "base/pointer.h"
|
|
|
|
// All the builtin fonts must be declared here
|
|
#include "base/font/crystal.h"
|
|
#include "base/font/cyrvetic.h"
|
|
#include "base/font/povlogo.h"
|
|
#include "base/font/timrom.h"
|
|
|
|
// this must be the last file included
|
|
#include "base/povdebug.h"
|
|
|
|
|
|
namespace pov_base
|
|
{
|
|
|
|
IOBase::IOBase(unsigned int dir, unsigned int type)
|
|
{
|
|
filetype = type;
|
|
direction = dir;
|
|
fail = true;
|
|
f = NULL;
|
|
}
|
|
|
|
IOBase::~IOBase()
|
|
{
|
|
close();
|
|
}
|
|
|
|
bool IOBase::open(const UCS2String& name, unsigned int Flags /* = 0 */)
|
|
{
|
|
char mode[8];
|
|
|
|
close();
|
|
filename = name;
|
|
|
|
if((Flags & append) == 0)
|
|
{
|
|
switch(direction)
|
|
{
|
|
case input:
|
|
strcpy(mode, "r");
|
|
break;
|
|
case output:
|
|
strcpy(mode, "w");
|
|
break;
|
|
case io:
|
|
strcpy(mode, "w+");
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// we cannot use append mode here, since "a" mode is totally incompatible with any
|
|
// output file format that requires in-place updates(i.e. writing to any location
|
|
// other than the end of the file). BMP files are in this category. In theory, "r+"
|
|
// can do anything "a" can do(with appropriate use of seek()) so append mode should
|
|
// not be needed.
|
|
strcpy(mode, "r+");
|
|
}
|
|
|
|
strcat(mode, "b");
|
|
|
|
f = NULL;
|
|
if(pov_stricmp(UCS2toASCIIString(name).c_str(), "stdin") == 0)
|
|
{
|
|
if(direction != input ||(Flags & append) != 0)
|
|
return false;
|
|
f = stdin;
|
|
}
|
|
else if(pov_stricmp(UCS2toASCIIString(name).c_str(), "stdout") == 0)
|
|
{
|
|
if(direction != output ||(Flags & append) != 0)
|
|
return false;
|
|
f = stdout;
|
|
}
|
|
else if(pov_stricmp(UCS2toASCIIString(name).c_str(), "stderr") == 0)
|
|
{
|
|
if(direction != output ||(Flags & append) != 0)
|
|
return false;
|
|
f = stderr;
|
|
}
|
|
else
|
|
{
|
|
if((f = POV_UCS2_FOPEN(name, mode)) == NULL)
|
|
{
|
|
if((Flags & append) == 0)
|
|
return false;
|
|
|
|
// to maintain traditional POV +c(continue) mode compatibility, if
|
|
// the open for append of an existing file fails, we allow a new file
|
|
// to be created.
|
|
mode [0] = 'w';
|
|
if((f = POV_UCS2_FOPEN(name, mode)) == NULL)
|
|
return false;
|
|
}
|
|
}
|
|
fail = false;
|
|
|
|
if((Flags & append) != 0)
|
|
{
|
|
if(!seekg(0, seek_end))
|
|
{
|
|
close();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IOBase::close(void)
|
|
{
|
|
if(f != NULL)
|
|
{
|
|
if (f != stdout && f != stderr && f != stdin)
|
|
fclose(f);
|
|
f = NULL;
|
|
}
|
|
fail = true;
|
|
return true;
|
|
}
|
|
|
|
IOBase& IOBase::flush(void)
|
|
{
|
|
if(f != NULL)
|
|
fflush(f);
|
|
return *this;
|
|
}
|
|
|
|
IOBase& IOBase::read(void *buffer, size_t count)
|
|
{
|
|
if(!fail && count > 0)
|
|
fail = fread(buffer, count, 1, f) != 1;
|
|
return *this;
|
|
}
|
|
|
|
IOBase& IOBase::write(const void *buffer, size_t count)
|
|
{
|
|
if(!fail && count > 0)
|
|
fail = fwrite(buffer, count, 1, f) != 1;
|
|
return *this;
|
|
}
|
|
|
|
// Strictly speaking, this should -not- be called seekg, since 'seekg'(an iostreams
|
|
// term) applies only to an input stream, and therefore the use of this name here
|
|
// implies that only the input stream will be affected on streams opened for I/O
|
|
//(which is not the case with fseek, since fseek moves the pointer for output too).
|
|
// However, the macintosh code seems to need it to be called seekg, so it is ...
|
|
IOBase& IOBase::seekg(POV_LONG pos, unsigned int whence /* = seek_set */)
|
|
{
|
|
if(!fail)
|
|
fail = fseek(f, pos, whence) != 0;
|
|
return *this;
|
|
}
|
|
|
|
IStream::IStream(const unsigned int stype) : IOBase(input, stype)
|
|
{
|
|
}
|
|
|
|
IStream::~IStream()
|
|
{
|
|
}
|
|
|
|
int IStream::Read_Short(void)
|
|
{
|
|
short result;
|
|
read(&result, sizeof(short));
|
|
return result;
|
|
}
|
|
|
|
int IStream::Read_Int(void)
|
|
{
|
|
int result;
|
|
read(&result, sizeof(int));
|
|
return result;
|
|
}
|
|
|
|
IStream& IStream::UnRead_Byte(int c)
|
|
{
|
|
if(!fail)
|
|
fail = ungetc(c, f) != c;
|
|
return *this;
|
|
}
|
|
|
|
IStream& IStream::getline(char *s, size_t buflen)
|
|
{
|
|
int chr = 0;
|
|
|
|
if(feof(f) != 0)
|
|
fail = true;
|
|
|
|
if(!fail && buflen > 0)
|
|
{
|
|
while(buflen > 1)
|
|
{
|
|
chr = fgetc(f);
|
|
if(chr == EOF)
|
|
break;
|
|
else if(chr == 10)
|
|
{
|
|
chr = fgetc(f);
|
|
if(chr != 13)
|
|
ungetc(chr, f);
|
|
break;
|
|
}
|
|
else if(chr == 13)
|
|
{
|
|
chr = fgetc(f);
|
|
if(chr != 10)
|
|
ungetc(chr, f);
|
|
break;
|
|
}
|
|
*s = chr;
|
|
s++;
|
|
buflen--;
|
|
}
|
|
*s = 0;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
/*
|
|
* Default to povlogo.ttf (0)
|
|
* 1 : TimeRoman (timrom.ttf), Serif
|
|
* 2 : Cyrvetita (cyrvetic.ttf), Sans-Serif
|
|
* 3 : Crystal (crystal.ttf), monospace sans serif
|
|
*
|
|
* To add a font, check first its license
|
|
*/
|
|
IMemStream::IMemStream(const int font_id):IStream(POV_File_Font_TTF)
|
|
{
|
|
switch(font_id)
|
|
{
|
|
case 1:
|
|
start = &font_timrom[0];
|
|
size = sizeof(font_timrom);
|
|
break;
|
|
case 2:
|
|
start = &font_cyrvetic[0];
|
|
size = sizeof(font_cyrvetic);
|
|
break;
|
|
case 3:
|
|
start = &font_crystal[0];
|
|
size = sizeof(font_crystal);
|
|
break;
|
|
default:
|
|
start = &font_povlogo[0];
|
|
size = sizeof(font_povlogo);
|
|
break;
|
|
}
|
|
pos = 0;
|
|
fail= false;
|
|
}
|
|
|
|
IMemStream::~IMemStream()
|
|
{
|
|
// [jg] more to do here (?)
|
|
}
|
|
|
|
OStream::OStream(const unsigned int stype) : IOBase(output, stype)
|
|
{
|
|
}
|
|
|
|
OStream::~OStream()
|
|
{
|
|
}
|
|
|
|
void OStream::printf(const char *format, ...)
|
|
{
|
|
va_list marker;
|
|
char buffer[1024];
|
|
|
|
va_start(marker, format);
|
|
vsnprintf(buffer, 1023, format, marker);
|
|
va_end(marker);
|
|
|
|
*this << buffer;
|
|
}
|
|
|
|
IStream *NewIStream(const Path& p, const unsigned int stype)
|
|
{
|
|
Pointer<IStream> istreamptr(POV_PLATFORM_BASE.CreateIStream(stype));
|
|
|
|
if(istreamptr == NULL)
|
|
return NULL;
|
|
|
|
if (POV_ALLOW_FILE_READ(p().c_str(), stype) == false) // TODO FIXME - this is handled by the frontend, but that code isn't completely there yet [trf]
|
|
{
|
|
string str ("IO Restrictions prohibit read access to '") ;
|
|
str += UCS2toASCIIString(p());
|
|
str += "'";
|
|
throw POV_EXCEPTION(kCannotOpenFileErr, str);
|
|
}
|
|
if(istreamptr->open(p().c_str()) == 0)
|
|
return NULL;
|
|
|
|
return istreamptr.release();
|
|
}
|
|
|
|
OStream *NewOStream(const Path& p, const unsigned int stype, const bool sappend)
|
|
{
|
|
Pointer<OStream> ostreamptr(POV_PLATFORM_BASE.CreateOStream(stype));
|
|
unsigned int Flags = IOBase::none;
|
|
|
|
if(ostreamptr == NULL)
|
|
return NULL;
|
|
|
|
if(sappend)
|
|
Flags |= IOBase::append;
|
|
|
|
if (POV_ALLOW_FILE_WRITE(p().c_str(), stype) == false) // TODO FIXME - this is handled by the frontend, but that code isn't completely there yet [trf]
|
|
{
|
|
string str ("IO Restrictions prohibit write access to '") ;
|
|
str += UCS2toASCIIString(p());
|
|
str += "'";
|
|
throw POV_EXCEPTION(kCannotOpenFileErr, str);
|
|
}
|
|
if(ostreamptr->open(p().c_str(), Flags) == 0)
|
|
return NULL;
|
|
|
|
return ostreamptr.release();
|
|
}
|
|
|
|
UCS2String GetFileExtension(const Path& p)
|
|
{
|
|
UCS2String::size_type pos = p.GetFile().find_last_of('.');
|
|
|
|
if(pos != UCS2String::npos)
|
|
return UCS2String(p.GetFile(), pos);
|
|
|
|
return UCS2String();
|
|
}
|
|
|
|
UCS2String GetFileName(const Path& p)
|
|
{
|
|
UCS2String::size_type pos = p.GetFile().find_last_of('.');
|
|
|
|
if(pos != UCS2String::npos)
|
|
return UCS2String(p.GetFile(), 0, pos);
|
|
|
|
return p.GetFile();
|
|
}
|
|
|
|
bool CheckIfFileExists(const Path& p)
|
|
{
|
|
FILE *tempf = POV_UCS2_FOPEN(p().c_str(), "r");
|
|
|
|
if(tempf != NULL)
|
|
fclose(tempf);
|
|
else
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
POV_LONG GetFileLength(const Path& p)
|
|
{
|
|
FILE *tempf = POV_UCS2_FOPEN(p().c_str(), "rb");
|
|
POV_LONG result = -1;
|
|
|
|
if(tempf != NULL)
|
|
{
|
|
fseek(tempf, 0, SEEK_END);
|
|
result = ftell(tempf);
|
|
fclose(tempf);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
IOBase& IMemStream::read(void *buffer, size_t count)
|
|
{
|
|
if ((!fail)&&(pos+count<= size))
|
|
{
|
|
memcpy(buffer,&start[pos],count);
|
|
pos+= count;
|
|
}
|
|
else
|
|
{
|
|
fail = true;
|
|
}
|
|
return *this;
|
|
}
|
|
int IMemStream::Read_Byte()
|
|
{
|
|
int v;
|
|
if (fail)
|
|
{
|
|
v = EOF;
|
|
}
|
|
else
|
|
{
|
|
v = start[pos++];
|
|
fail = !(pos<size);
|
|
}
|
|
return v;
|
|
}
|
|
|
|
IStream& IMemStream::UnRead_Byte(int c)
|
|
{
|
|
pos--;
|
|
fail = !(pos<size);
|
|
return *this;
|
|
}
|
|
IStream& IMemStream::getline(char *s,size_t buflen)
|
|
{
|
|
// Not needed for font
|
|
return *this;
|
|
}
|
|
POV_LONG IMemStream::tellg()
|
|
{
|
|
return pos;
|
|
}
|
|
|
|
IOBase& IMemStream::seekg(POV_LONG posi, unsigned int whence)
|
|
{
|
|
switch(whence)
|
|
{
|
|
case seek_set:
|
|
pos = posi;
|
|
break;
|
|
case seek_cur:
|
|
pos += posi;
|
|
break;
|
|
case seek_end:
|
|
pos = size - posi;
|
|
break;
|
|
}
|
|
fail = !(pos<size);
|
|
return *this;
|
|
}
|
|
bool IMemStream::open(const UCS2String &name, unsigned int Flags)
|
|
{
|
|
// Not needed for font
|
|
return true;
|
|
}
|
|
|
|
bool IMemStream::close()
|
|
{
|
|
// Not needed for font
|
|
return true;
|
|
}
|
|
}
|