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 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 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 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 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 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 libVersionPoint()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 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 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 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 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 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 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 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 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 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 334 PRifstream::PRifstream(): 335 istream(NULL), 336 _filebuf() 337 { 338 init(&_filebuf); 339 } 340 341 342 PRifstream::PRifstream(PRFileDesc *fd): 343 istream(NULL), 344 _filebuf(fd) 345 { 346 init(&_filebuf); 347 } 348 349 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 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 369 PRifstream::~PRifstream() { } 370 371 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 380 void PRifstream::attach(PRFileDesc *fd) 381 { 382 if (!_filebuf.attach(fd)) { 383 setstate(failbit); 384 } 385 } 386 387 388 void PRifstream::close() 389 { 390 if (_filebuf.close() == NULL) { 391 setstate(failbit); 392 } 393 } 394 395 396 PRofstream::PRofstream(): 397 ostream(NULL), 398 _filebuf() 399 { 400 init(&_filebuf); 401 } 402 403 404 PRofstream::PRofstream(PRFileDesc *fd): 405 ostream(NULL), 406 _filebuf(fd) 407 { 408 init(&_filebuf); 409 } 410 411 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 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 431 PRofstream::~PRofstream() { } 432 433 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 442 void PRofstream::attach(PRFileDesc *fd) 443 { 444 if (!_filebuf.attach(fd)) { 445 setstate(failbit); 446 } 447 } 448 449 450 void PRofstream::close() 451 { 452 if (_filebuf.close() == NULL) { 453 setstate(failbit); 454 } 455 } 456 457 458 PRfstream::PRfstream(): 459 iostream(NULL), 460 _filebuf() 461 { 462 init(&_filebuf); 463 } 464 465 466 PRfstream::PRfstream(PRFileDesc *fd): 467 iostream(NULL), 468 _filebuf(fd) 469 { 470 init(&_filebuf); 471 } 472 473 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 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 493 PRfstream::~PRfstream() { } 494 495 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 504 void PRfstream::attach(PRFileDesc *fd) 505 { 506 if (!_filebuf.attach(fd)) { 507 setstate(failbit); 508 } 509 } 510 511 512 void PRfstream::close() 513 { 514 if (_filebuf.close() == NULL) { 515 setstate(failbit); 516 } 517 } 518