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  * $File: //depot/povray/3.6-release/source/base/textstreambuffer.cpp $
25  * $Revision: #2 $
26  * $Change: 2939 $
27  * $DateTime: 2004/07/04 13:43:26 $
28  * $Author: root $
29  * $Log$
30  *****************************************************************************/
31 
32 #include <algorithm>
33 
34 #include "configbase.h"
35 
36 #include "textstreambuffer.h"
37 
38 #include "povms.h"
39 #include "pov_err.h"
40 
41 BEGIN_POV_BASE_NAMESPACE
42 
TextStreamBuffer(size_t buffersize,unsigned int wrapwidth)43 TextStreamBuffer::TextStreamBuffer(size_t buffersize, unsigned int wrapwidth)
44 {
45 	boffset = 0;
46 	bsize = buffersize;
47 	wrap = wrapwidth;
48 	curline = 0;
49 	if(POVMSUtil_TempAlloc((void **)&buffer, bsize) != kNoErr)
50 		throw int(kOutOfMemoryErr);
51 }
52 
~TextStreamBuffer()53 TextStreamBuffer::~TextStreamBuffer()
54 {
55 	boffset = 0;
56 	bsize = 0;
57 	wrap = 0;
58 	curline = 0;
59 	if(buffer != NULL)
60 		(void)POVMSUtil_TempFree((void *)buffer);
61 	buffer = NULL;
62 }
63 
printf(const char * format,...)64 void TextStreamBuffer::printf(const char *format, ...)
65 {
66 	va_list marker;
67 
68 	va_start(marker, format);
69 	vsnprintf(&buffer[boffset], bsize - boffset - 1, format, marker);
70 	va_end(marker);
71 
72 	// direct output
73 	directflush(&buffer[boffset], strlen(&buffer[boffset]));
74 
75 	boffset = strlen(buffer);
76 
77 	// line buffered output
78 	lineflush();
79 }
80 
print(const char * str)81 void TextStreamBuffer::print(const char *str)
82 {
83 	printf("%s", str);
84 }
85 
puts(const char * str)86 void TextStreamBuffer::puts(const char *str)
87 {
88 	printf("%s\n", str);
89 }
90 
putc(int chr)91 void TextStreamBuffer::putc(int chr)
92 {
93 	printf("%c", chr);
94 }
95 
printfile(const char * filename,unsigned long offset,int lines)96 void TextStreamBuffer::printfile(const char *filename, unsigned long offset, int lines)
97 {
98 	FILE *file = fopen(filename, "r");
99 
100 	if(file != NULL)
101 	{
102 		fseek(file, offset, SEEK_SET);
103 		printfile(file, lines);
104 		fclose(file);
105 	}
106 }
107 
printfile(FILE * file,int lines)108 void TextStreamBuffer::printfile(FILE *file, int lines)
109 {
110 	if(file != NULL)
111 	{
112 		bool stopposset = (lines < 0); // only if walking backwards stop at current position
113 		long stoppos = long(ftell(file));
114 		int chr = 0;
115 
116 		if(lines < 0)
117 		{
118 			int lineoffset = lines;
119 
120 			// NOTE: This will walk back one line too far! However, it only walks
121 			// back to the end of that line. Thus, the next step will walk forward
122 			// again to the beginning of the right line, which is the desired
123 			// position. Do not change this behavior without testing! [trf]
124 			for(long pos = long(ftell(file)) - 1; (lineoffset < 1) && (pos >= 0); pos--)
125 			{
126 				// WARNING: Expensive way to walk backward through a file, but will only
127 				// be used when problems are encountered anyway, and then it most likely
128 				// does not matter if the output of the message takes a tiny bit longer!
129 				fseek(file, pos, SEEK_SET);
130 
131 				chr = fgetc(file);
132 
133 				if((chr == 10) || (chr == 13))
134 				{
135 					chr = fgetc(file);
136 					if(!((chr == 10) || (chr == 13)))
137 					 	ungetc(chr, file);
138 					lineoffset++;
139 				}
140 				else if(chr == EOF)
141 					break;
142 			}
143 
144 			// beginning of file was previously reached
145 			if(lineoffset < 1)
146 				fseek(file, 0, SEEK_SET);
147 
148 			while(lineoffset > 0)
149 			{
150 				chr = fgetc(file);
151 
152 				if((chr == 10) || (chr == 13))
153 				{
154 					chr = fgetc(file);
155 					if(!((chr == 10) || (chr == 13)))
156 					 	ungetc(chr, file);
157 					lineoffset--;
158 				}
159 				else if(chr == EOF)
160 					break;
161 			}
162 
163 			// make number of lines to output positive for next step
164 			lines = -lines;
165 		}
166 
167 		while(lines > 0)
168 		{
169 			chr = fgetc(file);
170 
171 			if((stopposset == true) && (stoppos == (long(ftell(file)) - 1))) // only if walking backwards stop at initial position
172 				break;
173 
174 			// count newlines in file and replace newlines with system specific newline charcater
175 			if((chr == 10) || (chr == 13))
176 			{
177 				chr = fgetc(file);
178 				if(!((chr == 10) || (chr == 13)))
179 				 	ungetc(chr, file);
180 				 else
181 				 {
182 					if((stopposset == true) && (stoppos == (long(ftell(file)) - 1))) // only if walking backwards stop at initial position
183 						break;
184 				}
185 				printf("\n");
186 				lines--;
187 			}
188 			else if(chr == EOF)
189 				break;
190 			else
191 				printf("%c", chr);
192 		}
193 	}
194 }
195 
flush()196 void TextStreamBuffer::flush()
197 {
198 	if(curline > 0)
199 		directoutput("\n", 1);
200 	curline = 0;
201 
202 	lineflush();
203 	if(boffset > 0)
204 		lineoutput(buffer, boffset);
205 	boffset = 0;
206 }
207 
lineoutput(const char * str,unsigned int chars)208 void TextStreamBuffer::lineoutput(const char *str, unsigned int chars)
209 {
210 	// by default output to stdout
211 	fwrite(str, sizeof(char), chars, stdout);
212 	fprintf(stdout, "\n");
213 	fflush(stdout);
214 }
215 
directoutput(const char *,unsigned int)216 void TextStreamBuffer::directoutput(const char *, unsigned int)
217 {
218 	// does nothing by default
219 }
220 
rawoutput(const char *,unsigned int)221 void TextStreamBuffer::rawoutput(const char *, unsigned int)
222 {
223 	// does nothing by default
224 }
225 
lineflush()226 void TextStreamBuffer::lineflush()
227 {
228 	unsigned int lasti = 0;
229 	unsigned int ii = 0;
230 	unsigned int i = 0;
231 
232 	// output all complete lines in the buffer
233 	while(i < boffset)
234 	{
235 		if((buffer[i] == '\n') || (buffer[i] == '\r'))
236 		{
237 			lineoutput(&buffer[lasti], i - lasti);
238 			lasti = i + 1;
239 		}
240 		else if(i - lasti >= wrap)
241 		{
242 			// track back to last space up to 1/4 in the line to wrap
243 			for(ii = 0; ii < min((wrap / 4), i); ii++)
244 			{
245 				if(isspace(buffer[i - ii]))
246 					break;
247 			}
248 
249 			// if no space was found in the last 1/4 of the line to wrap, split it at the end anyway
250 			if(ii == min((wrap / 4), i))
251 				ii = 0;
252 			i -= ii;
253 
254 			lineoutput(&buffer[lasti], i - lasti);
255 			lasti = i;
256 			continue;
257 		}
258 		i++;
259 	}
260 
261 	// remove all completely output lines
262 	memmove(buffer, &buffer[lasti], bsize - lasti);
263 	boffset = boffset - lasti;
264 }
265 
directflush(const char * str,unsigned int chars)266 void TextStreamBuffer::directflush(const char *str, unsigned int chars)
267 {
268 	unsigned int ii = 0;
269 	unsigned int i = 0;
270 
271 	rawoutput(str, chars);
272 
273 	for(i = 0; i < chars; i++)
274 	{
275 		if((str[i] == '\n') || (str[i] == '\r'))
276 		{
277 			i++;
278 			directoutput(str, i);
279 			str += i;
280 			chars -= i;
281 			i = 0;
282 			curline = 0;
283 		}
284 		else if(curline + i >= wrap)
285 		{
286 			// track back to last space up to 1/4 in the line to wrap
287 			for(ii = 0; ii < min((wrap / 4), i); ii++)
288 			{
289 				if(isspace(str[i - ii]))
290 					break;
291 			}
292 
293 			// if no space was found in the last 1/4 of the line to wrap, split it at the end anyway
294 			if(ii == min((wrap / 4), i))
295 				ii = 0;
296 			i -= ii;
297 
298 			directoutput(str, i);
299 			directoutput("\n", 1);
300 
301 			str += i;
302 			chars -= i;
303 			i = 0;
304 			curline = 0;
305 		}
306 	}
307 
308 	if(chars > 0)
309 	{
310 		directoutput(str, chars);
311 		curline += chars;
312 	}
313 }
314 
315 END_POV_BASE_NAMESPACE
316