1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 /*
7 * Robin J. Maxwell 11-22-96
8 * Fredrik Roubert <roubert@google.com> 2010-07-23
9 * Matt Austern <austern@google.com> 2010-07-23
10 */
11
12 #include "prstrms.h"
13
14 #include <cstdio>
15 #include <cstring>
16 #include <ios>
17 #include <new>
18
19 using std::ios_base;
20 using std::iostream;
21 using std::istream;
22 using std::nothrow;
23 using std::ostream;
24 using std::streambuf;
25 using std::streamsize;
26
27
PRfilebuf()28 PRfilebuf::PRfilebuf():
29 _fd(NULL),
30 _opened(false),
31 _allocated(false),
32 _unbuffered(false),
33 _user_buf(false),
34 _buf_base(NULL),
35 _buf_end(NULL) { }
36
37
PRfilebuf(PRFileDesc * fd)38 PRfilebuf::PRfilebuf(PRFileDesc *fd):
39 _fd(fd),
40 _opened(false),
41 _allocated(false),
42 _unbuffered(false),
43 _user_buf(false),
44 _buf_base(NULL),
45 _buf_end(NULL) { }
46
47
PRfilebuf(PRFileDesc * fd,char_type * ptr,streamsize len)48 PRfilebuf::PRfilebuf(PRFileDesc *fd, char_type *ptr, streamsize len):
49 _fd(fd),
50 _opened(false),
51 _allocated(false),
52 _unbuffered(false),
53 _user_buf(false),
54 _buf_base(NULL),
55 _buf_end(NULL)
56 {
57 setbuf(ptr, len);
58 }
59
60
~PRfilebuf()61 PRfilebuf::~PRfilebuf()
62 {
63 if (_opened) {
64 close();
65 } else {
66 sync();
67 }
68 if (_allocated) {
69 delete _buf_base;
70 }
71 }
72
73
open(const char * name,ios_base::openmode flags,PRIntn mode)74 PRfilebuf *PRfilebuf::open(
75 const char *name, ios_base::openmode flags, PRIntn mode)
76 {
77 if (_fd != NULL) {
78 return NULL; // Error if already open.
79 }
80
81 // Translate flags argument.
82 PRIntn prflags = 0;
83 bool ate = (flags & ios_base::ate) != 0;
84 flags &= ~(ios_base::ate | ios_base::binary);
85
86 // TODO: The flag PR_CREATE_FILE should probably be used for the cases
87 // (out), (out|app), (out|trunc) and (in|out|trunc) as the C++ standard
88 // specifies that these cases should open files 'as if by using fopen with
89 // "w"'. But adding that flag here will cause the unit test to leave files
90 // behind after running (which might or might not be an error in the unit
91 // test) so the matter needs further investigation before any changes are
92 // made. The old prstreams implementation used the non-standard flag
93 // ios::nocreate to control the use of PR_CREATE_FILE.
94
95 if (flags == (ios_base::out)) {
96 prflags = PR_WRONLY | PR_TRUNCATE;
97 } else if (flags == (ios_base::out | ios_base::app)) {
98 prflags = PR_RDWR | PR_APPEND;
99 } else if (flags == (ios_base::out | ios_base::trunc)) {
100 prflags = PR_WRONLY | PR_TRUNCATE;
101 } else if (flags == (ios_base::in)) {
102 prflags = PR_RDONLY;
103 } else if (flags == (ios_base::in | ios_base::out)) {
104 prflags = PR_RDWR;
105 } else if (flags == (ios_base::in | ios_base::out | ios_base::trunc)) {
106 prflags = PR_RDWR | PR_TRUNCATE;
107 } else {
108 return NULL; // Unrecognized flag combination.
109 }
110
111 if ((_fd = PR_Open(name, prflags, mode)) == NULL) {
112 return NULL;
113 }
114
115 _opened = true;
116
117 if (ate &&
118 seekoff(0, ios_base::end, flags) == pos_type(traits_type::eof())) {
119 close();
120 return NULL;
121 }
122
123 return this;
124 }
125
126
attach(PRFileDesc * fd)127 PRfilebuf *PRfilebuf::attach(PRFileDesc *fd)
128 {
129 if (_fd != NULL) {
130 return NULL; // Error if already open.
131 }
132
133 _opened = false;
134 _fd = fd;
135 return this;
136 }
137
138
close()139 PRfilebuf *PRfilebuf::close()
140 {
141 if (_fd == NULL) {
142 return NULL;
143 }
144
145 int status = sync();
146
147 if (PR_Close(_fd) == PR_FAILURE ||
148 traits_type::eq_int_type(status, traits_type::eof())) {
149 return NULL;
150 }
151
152 _fd = NULL;
153 return this;
154 }
155
156
setbuf(char_type * ptr,streamsize len)157 streambuf *PRfilebuf::setbuf(char_type *ptr, streamsize len)
158 {
159 if (is_open() && _buf_end) {
160 return NULL;
161 }
162
163 if (!ptr || len <= 0) {
164 _unbuffered = true;
165 } else {
166 setb(ptr, ptr + len, false);
167 }
168
169 return this;
170 }
171
172
seekoff(off_type offset,ios_base::seekdir dir,ios_base::openmode)173 streambuf::pos_type PRfilebuf::seekoff(
174 off_type offset, ios_base::seekdir dir, ios_base::openmode /*flags*/)
175 {
176 if (PR_GetDescType(_fd) != PR_DESC_FILE) {
177 return traits_type::eof();
178 }
179
180 PRSeekWhence whence;
181 PRInt64 pos;
182
183 switch (dir) {
184 case ios_base::beg: whence = PR_SEEK_SET; break;
185 case ios_base::cur: whence = PR_SEEK_CUR; break;
186 case ios_base::end: whence = PR_SEEK_END; break;
187 default:
188 return traits_type::eof(); // This should never happen.
189 }
190
191 if (traits_type::eq_int_type(sync(), traits_type::eof())) {
192 return traits_type::eof();
193 }
194
195 if ((pos = PR_Seek64(_fd, offset, whence)) == -1) {
196 return traits_type::eof();
197 }
198
199 return pos;
200 }
201
202
sync()203 int PRfilebuf::sync()
204 {
205 if (_fd == NULL) {
206 return traits_type::eof();
207 }
208
209 if (!_unbuffered) {
210 // Sync write area.
211 PRInt32 waiting;
212 if ((waiting = pptr() - pbase()) != 0) {
213 PRInt32 nout;
214 if ((nout = PR_Write(_fd, pbase(), waiting)) != waiting) {
215 if (nout > 0) {
216 // Should set _pptr -= nout.
217 pbump(-nout);
218 memmove(pbase(), pbase() + nout, waiting - nout);
219 }
220 return traits_type::eof();
221 }
222 }
223 setp(NULL, NULL); // Empty put area.
224
225 if (PR_GetDescType(_fd) == PR_DESC_FILE) {
226 // Sockets can't seek; don't need this.
227 PROffset64 avail;
228 if ((avail = in_avail()) > 0) {
229 if (PR_Seek64(_fd, -avail, PR_SEEK_CUR) != -1) {
230 return traits_type::eof();
231 }
232 }
233 }
234 setg(NULL, NULL, NULL); // Empty get area.
235 }
236
237 return 0;
238 }
239
240
underflow()241 streambuf::int_type PRfilebuf::underflow()
242 {
243 PRInt32 count;
244 char_type byte;
245
246 if (gptr() != NULL && gptr() < egptr()) {
247 return traits_type::to_int_type(*gptr());
248 }
249
250 // Make sure there is a reserve area.
251 if (!_unbuffered && _buf_base == NULL && !allocate()) {
252 return traits_type::eof();
253 }
254
255 // Sync before new buffer created below.
256 if (traits_type::eq_int_type(sync(), traits_type::eof())) {
257 return traits_type::eof();
258 }
259
260 if (_unbuffered) {
261 if (PR_Read(_fd, &byte, 1) <= 0) {
262 return traits_type::eof();
263 }
264
265 return traits_type::to_int_type(byte);
266 }
267
268 if ((count = PR_Read(_fd, _buf_base, _buf_end - _buf_base)) <= 0) {
269 return traits_type::eof(); // Reached EOF.
270 }
271
272 setg(_buf_base, _buf_base, _buf_base + count);
273 return traits_type::to_int_type(*gptr());
274 }
275
276
overflow(int_type c)277 streambuf::int_type PRfilebuf::overflow(int_type c)
278 {
279 // Make sure there is a reserve area.
280 if (!_unbuffered && _buf_base == NULL && !allocate()) {
281 return traits_type::eof();
282 }
283
284 // Sync before new buffer created below.
285 if (traits_type::eq_int_type(sync(), traits_type::eof())) {
286 return traits_type::eof();
287 }
288
289 if (!_unbuffered) {
290 setp(_buf_base, _buf_end);
291 }
292
293 if (!traits_type::eq_int_type(c, traits_type::eof())) {
294 // Extract the byte to be written.
295 // (Required on big-endian architectures.)
296 char_type byte = traits_type::to_char_type(c);
297 if (!_unbuffered && pptr() < epptr()) { // Guard against recursion.
298 return sputc(byte);
299 } else {
300 if (PR_Write(_fd, &byte, 1) != 1) {
301 return traits_type::eof();
302 }
303 }
304 }
305
306 return traits_type::not_eof(c);
307 }
308
309
allocate()310 bool PRfilebuf::allocate()
311 {
312 char_type *buf = new(nothrow) char_type[BUFSIZ];
313 if (buf == NULL) {
314 return false;
315 }
316
317 setb(buf, buf + BUFSIZ, true);
318 return true;
319 }
320
321
setb(char_type * buf_base,char_type * buf_end,bool user_buf)322 void PRfilebuf::setb(char_type *buf_base, char_type *buf_end, bool user_buf)
323 {
324 if (_buf_base && !_user_buf) {
325 delete[] _buf_base;
326 }
327
328 _buf_base = buf_base;
329 _buf_end = buf_end;
330 _user_buf = user_buf;
331 }
332
333
PRifstream()334 PRifstream::PRifstream():
335 istream(NULL),
336 _filebuf()
337 {
338 init(&_filebuf);
339 }
340
341
PRifstream(PRFileDesc * fd)342 PRifstream::PRifstream(PRFileDesc *fd):
343 istream(NULL),
344 _filebuf(fd)
345 {
346 init(&_filebuf);
347 }
348
349
PRifstream(PRFileDesc * fd,char_type * ptr,streamsize len)350 PRifstream::PRifstream(PRFileDesc *fd, char_type *ptr, streamsize len):
351 istream(NULL),
352 _filebuf(fd, ptr, len)
353 {
354 init(&_filebuf);
355 }
356
357
PRifstream(const char * name,openmode flags,PRIntn mode)358 PRifstream::PRifstream(const char *name, openmode flags, PRIntn mode):
359 istream(NULL),
360 _filebuf()
361 {
362 init(&_filebuf);
363 if (!_filebuf.open(name, flags | in, mode)) {
364 setstate(failbit);
365 }
366 }
367
368
~PRifstream()369 PRifstream::~PRifstream() { }
370
371
open(const char * name,openmode flags,PRIntn mode)372 void PRifstream::open(const char *name, openmode flags, PRIntn mode)
373 {
374 if (is_open() || !_filebuf.open(name, flags | in, mode)) {
375 setstate(failbit);
376 }
377 }
378
379
attach(PRFileDesc * fd)380 void PRifstream::attach(PRFileDesc *fd)
381 {
382 if (!_filebuf.attach(fd)) {
383 setstate(failbit);
384 }
385 }
386
387
close()388 void PRifstream::close()
389 {
390 if (_filebuf.close() == NULL) {
391 setstate(failbit);
392 }
393 }
394
395
PRofstream()396 PRofstream::PRofstream():
397 ostream(NULL),
398 _filebuf()
399 {
400 init(&_filebuf);
401 }
402
403
PRofstream(PRFileDesc * fd)404 PRofstream::PRofstream(PRFileDesc *fd):
405 ostream(NULL),
406 _filebuf(fd)
407 {
408 init(&_filebuf);
409 }
410
411
PRofstream(PRFileDesc * fd,char_type * ptr,streamsize len)412 PRofstream::PRofstream(PRFileDesc *fd, char_type *ptr, streamsize len):
413 ostream(NULL),
414 _filebuf(fd, ptr, len)
415 {
416 init(&_filebuf);
417 }
418
419
PRofstream(const char * name,openmode flags,PRIntn mode)420 PRofstream::PRofstream(const char *name, openmode flags, PRIntn mode):
421 ostream(NULL),
422 _filebuf()
423 {
424 init(&_filebuf);
425 if (!_filebuf.open(name, flags | out, mode)) {
426 setstate(failbit);
427 }
428 }
429
430
~PRofstream()431 PRofstream::~PRofstream() { }
432
433
open(const char * name,openmode flags,PRIntn mode)434 void PRofstream::open(const char *name, openmode flags, PRIntn mode)
435 {
436 if (is_open() || !_filebuf.open(name, flags | out, mode)) {
437 setstate(failbit);
438 }
439 }
440
441
attach(PRFileDesc * fd)442 void PRofstream::attach(PRFileDesc *fd)
443 {
444 if (!_filebuf.attach(fd)) {
445 setstate(failbit);
446 }
447 }
448
449
close()450 void PRofstream::close()
451 {
452 if (_filebuf.close() == NULL) {
453 setstate(failbit);
454 }
455 }
456
457
PRfstream()458 PRfstream::PRfstream():
459 iostream(NULL),
460 _filebuf()
461 {
462 init(&_filebuf);
463 }
464
465
PRfstream(PRFileDesc * fd)466 PRfstream::PRfstream(PRFileDesc *fd):
467 iostream(NULL),
468 _filebuf(fd)
469 {
470 init(&_filebuf);
471 }
472
473
PRfstream(PRFileDesc * fd,char_type * ptr,streamsize len)474 PRfstream::PRfstream(PRFileDesc *fd, char_type *ptr, streamsize len):
475 iostream(NULL),
476 _filebuf(fd, ptr, len)
477 {
478 init(&_filebuf);
479 }
480
481
PRfstream(const char * name,openmode flags,PRIntn mode)482 PRfstream::PRfstream(const char *name, openmode flags, PRIntn mode):
483 iostream(NULL),
484 _filebuf()
485 {
486 init(&_filebuf);
487 if (!_filebuf.open(name, flags | in | out, mode)) {
488 setstate(failbit);
489 }
490 }
491
492
~PRfstream()493 PRfstream::~PRfstream() { }
494
495
open(const char * name,openmode flags,PRIntn mode)496 void PRfstream::open(const char *name, openmode flags, PRIntn mode)
497 {
498 if (is_open() || !_filebuf.open(name, flags | in | out, mode)) {
499 setstate(failbit);
500 }
501 }
502
503
attach(PRFileDesc * fd)504 void PRfstream::attach(PRFileDesc *fd)
505 {
506 if (!_filebuf.attach(fd)) {
507 setstate(failbit);
508 }
509 }
510
511
close()512 void PRfstream::close()
513 {
514 if (_filebuf.close() == NULL) {
515 setstate(failbit);
516 }
517 }
518