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