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