/*******************************************************************************
* textstreambuffer.cpp
*
* This module contains the basic C++ text stream buffer.
*
* ---------------------------------------------------------------------------
* 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 .
* ---------------------------------------------------------------------------
* 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/textstreambuffer.cpp $
* $Revision: #1 $
* $Change: 6069 $
* $DateTime: 2013/11/06 11:59:40 $
* $Author: chrisc $
*******************************************************************************/
#include
#include
// configbase.h must always be the first POV file included within base *.cpp files
#include "configbase.h"
#include "textstreambuffer.h"
#include "povms.h"
#include "pov_err.h"
// this must be the last file included
#include "base/povdebug.h"
namespace pov_base
{
TextStreamBuffer::TextStreamBuffer(size_t buffersize, unsigned int wrapwidth)
{
boffset = 0;
bsize = buffersize;
wrap = wrapwidth;
curline = 0;
if(POVMSUtil_TempAlloc((void **)&buffer, bsize) != kNoErr)
throw POV_EXCEPTION_CODE(kOutOfMemoryErr);
}
TextStreamBuffer::~TextStreamBuffer()
{
boffset = 0;
bsize = 0;
wrap = 0;
curline = 0;
if(buffer != NULL)
(void)POVMSUtil_TempFree((void *)buffer);
buffer = NULL;
}
void TextStreamBuffer::printf(const char *format, ...)
{
va_list marker;
va_start(marker, format);
vsnprintf(&buffer[boffset], bsize - boffset - 1, format, marker);
va_end(marker);
// direct output
directflush(&buffer[boffset], strlen(&buffer[boffset]));
boffset = strlen(buffer);
// line buffered output
lineflush();
}
void TextStreamBuffer::print(const char *str)
{
printf("%s", str);
}
void TextStreamBuffer::puts(const char *str)
{
printf("%s\n", str);
}
void TextStreamBuffer::putc(int chr)
{
printf("%c", chr);
}
void TextStreamBuffer::printfile(const char *filename, POV_LONG offset, POV_LONG lines)
{
FILE *file = fopen(filename, "r");
if(file != NULL)
{
fseek(file, offset, SEEK_SET);
printfile(file, lines);
fclose(file);
}
}
void TextStreamBuffer::printfile(FILE *file, POV_LONG lines)
{
if(file != NULL)
{
bool stopposset = (lines < 0); // only if walking backwards stop at current position
POV_LONG stoppos = (POV_LONG)(ftell(file));
int chr = 0;
if(lines < 0)
{
POV_LONG lineoffset = lines;
// NOTE: This will walk back one line too far! However, it only walks
// back to the end of that line. Thus, the next step will walk forward
// again to the beginning of the right line, which is the desired
// position. Do not change this behavior without testing! [trf]
for(POV_LONG pos = (POV_LONG)(ftell(file)) - 1; (lineoffset < 1) && (pos >= 0); pos--)
{
// WARNING: Expensive way to walk backward through a file, but will only
// be used when problems are encountered anyway, and then it most likely
// does not matter if the output of the message takes a tiny bit longer!
fseek(file, pos, SEEK_SET);
chr = fgetc(file);
if((chr == 10) || (chr == 13))
{
chr = fgetc(file);
if(!((chr == 10) || (chr == 13)))
ungetc(chr, file);
lineoffset++;
}
else if(chr == EOF)
break;
}
// beginning of file was previously reached
if(lineoffset < 1)
fseek(file, 0, SEEK_SET);
while(lineoffset > 0)
{
chr = fgetc(file);
if((chr == 10) || (chr == 13))
{
chr = fgetc(file);
if(!((chr == 10) || (chr == 13)))
ungetc(chr, file);
lineoffset--;
}
else if(chr == EOF)
break;
}
// make number of lines to output positive for next step
lines = -lines;
}
while(lines > 0)
{
chr = fgetc(file);
if((stopposset == true) && (stoppos == ((POV_LONG)(ftell(file)) - 1))) // only if walking backwards stop at initial position
break;
// count newlines in file and replace newlines with system specific newline charcater
if((chr == 10) || (chr == 13))
{
chr = fgetc(file);
if(!((chr == 10) || (chr == 13)))
ungetc(chr, file);
else
{
if((stopposset == true) && (stoppos == ((POV_LONG)(ftell(file)) - 1))) // only if walking backwards stop at initial position
break;
}
printf("\n");
lines--;
}
else if(chr == EOF)
break;
else
printf("%c", chr);
}
}
}
void TextStreamBuffer::flush()
{
if(curline > 0)
directoutput("\n", 1);
curline = 0;
lineflush();
if(boffset > 0)
lineoutput(buffer, boffset);
boffset = 0;
}
void TextStreamBuffer::lineoutput(const char *str, unsigned int chars)
{
// by default output to stdout
fwrite(str, sizeof(char), chars, stdout);
fprintf(stdout, "\n");
fflush(stdout);
}
void TextStreamBuffer::directoutput(const char *, unsigned int)
{
// does nothing by default
}
void TextStreamBuffer::rawoutput(const char *, unsigned int)
{
// does nothing by default
}
void TextStreamBuffer::lineflush()
{
unsigned int lasti = 0;
unsigned int ii = 0;
unsigned int i = 0;
// output all complete lines in the buffer
while(i < boffset)
{
if((buffer[i] == '\n') || (buffer[i] == '\r'))
{
lineoutput(&buffer[lasti], i - lasti);
lasti = i + 1;
}
else if(i - lasti >= wrap)
{
// track back to last space up to 1/4 in the line to wrap
for(ii = 0; ii < min((wrap / 4), i); ii++)
{
if(isspace(buffer[i - ii]))
break;
}
// if no space was found in the last 1/4 of the line to wrap, split it at the end anyway
if(ii == min((wrap / 4), i))
ii = 0;
i -= ii;
lineoutput(&buffer[lasti], i - lasti);
lasti = i;
continue;
}
i++;
}
if(lasti > 0)
{
// remove all completely output lines
boffset -= lasti;
memmove(buffer, &buffer[lasti], boffset);
}
}
void TextStreamBuffer::directflush(const char *str, unsigned int chars)
{
unsigned int ii = 0;
unsigned int i = 0;
rawoutput(str, chars);
for(i = 0; i < chars; i++)
{
if((str[i] == '\n') || (str[i] == '\r'))
{
i++;
directoutput(str, i);
str += i;
chars -= i;
i = 0;
curline = 0;
}
else if(curline + i >= wrap)
{
// track back to last space up to 1/4 in the line to wrap
for(ii = 0; ii < min((wrap / 4), i); ii++)
{
if(isspace(str[i - ii]))
break;
}
// if no space was found in the last 1/4 of the line to wrap, split it at the end anyway
if(ii == min((wrap / 4), i))
ii = 0;
i -= ii;
directoutput(str, i);
directoutput("\n", 1);
str += i;
chars -= i;
i = 0;
curline = 0;
}
}
if(chars > 0)
{
directoutput(str, chars);
curline += chars;
}
}
}