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