1 /*******************************************************************************
2  * textstreambuffer.cpp
3  *
4  * This module contains the basic C++ text stream buffer.
5  *
6  * ---------------------------------------------------------------------------
7  * Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
8  * Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd.
9  *
10  * POV-Ray is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU Affero General Public License as
12  * published by the Free Software Foundation, either version 3 of the
13  * License, or (at your option) any later version.
14  *
15  * POV-Ray is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Affero General Public License for more details.
19  *
20  * You should have received a copy of the GNU Affero General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  * ---------------------------------------------------------------------------
23  * POV-Ray is based on the popular DKB raytracer version 2.12.
24  * DKBTrace was originally written by David K. Buck.
25  * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
26  * ---------------------------------------------------------------------------
27  * $File: //depot/public/povray/3.x/source/base/textstreambuffer.cpp $
28  * $Revision: #1 $
29  * $Change: 6069 $
30  * $DateTime: 2013/11/06 11:59:40 $
31  * $Author: chrisc $
32  *******************************************************************************/
33 
34 #include <algorithm>
35 #include <cstring>
36 
37 // configbase.h must always be the first POV file included within base *.cpp files
38 #include "configbase.h"
39 
40 #include "textstreambuffer.h"
41 
42 #include "povms.h"
43 #include "pov_err.h"
44 
45 // this must be the last file included
46 #include "base/povdebug.h"
47 
48 namespace pov_base
49 {
50 
TextStreamBuffer(size_t buffersize,unsigned int wrapwidth)51 TextStreamBuffer::TextStreamBuffer(size_t buffersize, unsigned int wrapwidth)
52 {
53 	boffset = 0;
54 	bsize = buffersize;
55 	wrap = wrapwidth;
56 	curline = 0;
57 	if(POVMSUtil_TempAlloc((void **)&buffer, bsize) != kNoErr)
58 		throw POV_EXCEPTION_CODE(kOutOfMemoryErr);
59 }
60 
~TextStreamBuffer()61 TextStreamBuffer::~TextStreamBuffer()
62 {
63 	boffset = 0;
64 	bsize = 0;
65 	wrap = 0;
66 	curline = 0;
67 	if(buffer != NULL)
68 		(void)POVMSUtil_TempFree((void *)buffer);
69 	buffer = NULL;
70 }
71 
printf(const char * format,...)72 void TextStreamBuffer::printf(const char *format, ...)
73 {
74 	va_list marker;
75 
76 	va_start(marker, format);
77 	vsnprintf(&buffer[boffset], bsize - boffset - 1, format, marker);
78 	va_end(marker);
79 
80 	// direct output
81 	directflush(&buffer[boffset], strlen(&buffer[boffset]));
82 
83 	boffset = strlen(buffer);
84 
85 	// line buffered output
86 	lineflush();
87 }
88 
print(const char * str)89 void TextStreamBuffer::print(const char *str)
90 {
91 	printf("%s", str);
92 }
93 
puts(const char * str)94 void TextStreamBuffer::puts(const char *str)
95 {
96 	printf("%s\n", str);
97 }
98 
putc(int chr)99 void TextStreamBuffer::putc(int chr)
100 {
101 	printf("%c", chr);
102 }
103 
printfile(const char * filename,POV_LONG offset,POV_LONG lines)104 void TextStreamBuffer::printfile(const char *filename, POV_LONG offset, POV_LONG lines)
105 {
106 	FILE *file = fopen(filename, "r");
107 
108 	if(file != NULL)
109 	{
110 		fseek(file, offset, SEEK_SET);
111 		printfile(file, lines);
112 		fclose(file);
113 	}
114 }
115 
printfile(FILE * file,POV_LONG lines)116 void TextStreamBuffer::printfile(FILE *file, POV_LONG lines)
117 {
118 	if(file != NULL)
119 	{
120 		bool stopposset = (lines < 0); // only if walking backwards stop at current position
121 		POV_LONG stoppos = (POV_LONG)(ftell(file));
122 		int chr = 0;
123 
124 		if(lines < 0)
125 		{
126 			POV_LONG lineoffset = lines;
127 
128 			// NOTE: This will walk back one line too far! However, it only walks
129 			// back to the end of that line. Thus, the next step will walk forward
130 			// again to the beginning of the right line, which is the desired
131 			// position. Do not change this behavior without testing! [trf]
132 			for(POV_LONG pos = (POV_LONG)(ftell(file)) - 1; (lineoffset < 1) && (pos >= 0); pos--)
133 			{
134 				// WARNING: Expensive way to walk backward through a file, but will only
135 				// be used when problems are encountered anyway, and then it most likely
136 				// does not matter if the output of the message takes a tiny bit longer!
137 				fseek(file, pos, SEEK_SET);
138 
139 				chr = fgetc(file);
140 
141 				if((chr == 10) || (chr == 13))
142 				{
143 					chr = fgetc(file);
144 					if(!((chr == 10) || (chr == 13)))
145 						ungetc(chr, file);
146 					lineoffset++;
147 				}
148 				else if(chr == EOF)
149 					break;
150 			}
151 
152 			// beginning of file was previously reached
153 			if(lineoffset < 1)
154 				fseek(file, 0, SEEK_SET);
155 
156 			while(lineoffset > 0)
157 			{
158 				chr = fgetc(file);
159 
160 				if((chr == 10) || (chr == 13))
161 				{
162 					chr = fgetc(file);
163 					if(!((chr == 10) || (chr == 13)))
164 						ungetc(chr, file);
165 					lineoffset--;
166 				}
167 				else if(chr == EOF)
168 					break;
169 			}
170 
171 			// make number of lines to output positive for next step
172 			lines = -lines;
173 		}
174 
175 		while(lines > 0)
176 		{
177 			chr = fgetc(file);
178 
179 			if((stopposset == true) && (stoppos == ((POV_LONG)(ftell(file)) - 1))) // only if walking backwards stop at initial position
180 				break;
181 
182 			// count newlines in file and replace newlines with system specific newline charcater
183 			if((chr == 10) || (chr == 13))
184 			{
185 				chr = fgetc(file);
186 				if(!((chr == 10) || (chr == 13)))
187 					ungetc(chr, file);
188 				else
189 				{
190 					if((stopposset == true) && (stoppos == ((POV_LONG)(ftell(file)) - 1))) // only if walking backwards stop at initial position
191 						break;
192 				}
193 				printf("\n");
194 				lines--;
195 			}
196 			else if(chr == EOF)
197 				break;
198 			else
199 				printf("%c", chr);
200 		}
201 	}
202 }
203 
flush()204 void TextStreamBuffer::flush()
205 {
206 	if(curline > 0)
207 		directoutput("\n", 1);
208 	curline = 0;
209 
210 	lineflush();
211 	if(boffset > 0)
212 		lineoutput(buffer, boffset);
213 	boffset = 0;
214 }
215 
lineoutput(const char * str,unsigned int chars)216 void TextStreamBuffer::lineoutput(const char *str, unsigned int chars)
217 {
218 	// by default output to stdout
219 	fwrite(str, sizeof(char), chars, stdout);
220 	fprintf(stdout, "\n");
221 	fflush(stdout);
222 }
223 
directoutput(const char *,unsigned int)224 void TextStreamBuffer::directoutput(const char *, unsigned int)
225 {
226 	// does nothing by default
227 }
228 
rawoutput(const char *,unsigned int)229 void TextStreamBuffer::rawoutput(const char *, unsigned int)
230 {
231 	// does nothing by default
232 }
233 
lineflush()234 void TextStreamBuffer::lineflush()
235 {
236 	unsigned int lasti = 0;
237 	unsigned int ii = 0;
238 	unsigned int i = 0;
239 
240 	// output all complete lines in the buffer
241 	while(i < boffset)
242 	{
243 		if((buffer[i] == '\n') || (buffer[i] == '\r'))
244 		{
245 			lineoutput(&buffer[lasti], i - lasti);
246 			lasti = i + 1;
247 		}
248 		else if(i - lasti >= wrap)
249 		{
250 			// track back to last space up to 1/4 in the line to wrap
251 			for(ii = 0; ii < min((wrap / 4), i); ii++)
252 			{
253 				if(isspace(buffer[i - ii]))
254 					break;
255 			}
256 
257 			// if no space was found in the last 1/4 of the line to wrap, split it at the end anyway
258 			if(ii == min((wrap / 4), i))
259 				ii = 0;
260 			i -= ii;
261 
262 			lineoutput(&buffer[lasti], i - lasti);
263 			lasti = i;
264 			continue;
265 		}
266 		i++;
267 	}
268 
269 	if(lasti > 0)
270 	{
271 		// remove all completely output lines
272 		boffset -= lasti;
273 		memmove(buffer, &buffer[lasti], boffset);
274 	}
275 }
276 
directflush(const char * str,unsigned int chars)277 void TextStreamBuffer::directflush(const char *str, unsigned int chars)
278 {
279 	unsigned int ii = 0;
280 	unsigned int i = 0;
281 
282 	rawoutput(str, chars);
283 
284 	for(i = 0; i < chars; i++)
285 	{
286 		if((str[i] == '\n') || (str[i] == '\r'))
287 		{
288 			i++;
289 			directoutput(str, i);
290 			str += i;
291 			chars -= i;
292 			i = 0;
293 			curline = 0;
294 		}
295 		else if(curline + i >= wrap)
296 		{
297 			// track back to last space up to 1/4 in the line to wrap
298 			for(ii = 0; ii < min((wrap / 4), i); ii++)
299 			{
300 				if(isspace(str[i - ii]))
301 					break;
302 			}
303 
304 			// if no space was found in the last 1/4 of the line to wrap, split it at the end anyway
305 			if(ii == min((wrap / 4), i))
306 				ii = 0;
307 			i -= ii;
308 
309 			directoutput(str, i);
310 			directoutput("\n", 1);
311 
312 			str += i;
313 			chars -= i;
314 			i = 0;
315 			curline = 0;
316 		}
317 	}
318 
319 	if(chars > 0)
320 	{
321 		directoutput(str, chars);
322 		curline += chars;
323 	}
324 }
325 
326 }
327