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