home *** CD-ROM | disk | FTP | other *** search
/ GEMini Atari / GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso / files / gnu / gplibs07 / filebuf.cc < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-24  |  14.2 KB  |  554 lines

  1. //    This is part of the iostream library, providing input/output for C++.
  2. //    Copyright (C) 1991, 1992 Per Bothner.
  3. //
  4. //    This library is free software; you can redistribute it and/or
  5. //    modify it under the terms of the GNU Library General Public
  6. //    License as published by the Free Software Foundation; either
  7. //    version 2 of the License, or (at your option) any later version.
  8. //
  9. //    This library is distributed in the hope that it will be useful,
  10. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12. //    Library General Public License for more details.
  13. //
  14. //    You should have received a copy of the GNU Library General Public
  15. //    License along with this library; if not, write to the Free
  16. //    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. #include "ioprivat.h"
  19. #include <sys/file.h>
  20. #include <sys/types.h>
  21. #include <sys/stat.h>
  22. #include <fcntl.h>
  23. #include <errno.h>
  24.  
  25. // An fstream can be in at most one of put mode, get mode, or putback mode.
  26. // Putback mode is a variant of get mode.
  27.  
  28. // In a filebuf, there is only one current position, instead of two
  29. // separate get and put pointers.  In get mode, the current posistion
  30. // is that of gptr(); in put mode that of pptr().
  31.  
  32. // The position in the buffer that corresponds to the position
  33. // in external file system is file_ptr().
  34. // This is normally egptr(), except in putback mode, when it is _save_egptr.
  35. // If the field _fb._offset is >= 0, it gives the offset in
  36. // the file as a whole of the start of the buffer (base()).
  37.  
  38. // PUT MODE:
  39. // If a filebuf is in put mode, pbase() is non-NULL and equal to base().
  40. // Also, epptr() == ebuf().
  41. // Also, eback() == gptr() && gptr() == egptr().
  42. // The un-flushed character are those between pbase() and pptr().
  43. // GET MODE:
  44. // If a filebuf is in get or putback mode, eback() != egptr().
  45. // In get mode, the unread characters are between gptr() and egptr().
  46. // The OS file position corresponds to that of egptr().
  47. // PUTBACK MODE:
  48. // Putback mode is used to remember "excess" characters that have
  49. // been sputbackc'd in a separate putback buffer.
  50. // In putback mode, the get buffer points to the special putback buffer.
  51. // The unread characters are the characters between gptr() and egptr()
  52. // in the putback buffer, as well as the area between save_gptr()
  53. // and save_egptr(), which point into the original reserve buffer.
  54. // (The pointers save_gptr() and save_egptr() are the values
  55. // of gptr() and egptr() at the time putback mode was entered.)
  56. // The OS position corresponds to that of save_egptr().
  57. //
  58. // LINE BUFFERED OUTPUT:
  59. // During line buffered output, pbase()==base() && epptr()==base().
  60. // However, ptr() may be anywhere between base() and ebuf().
  61. // This forces a call to filebuf::overflow(int C) on every put.
  62. // If there is more space in the buffer, and C is not a '\n',
  63. // then C is inserted, and pptr() incremented.
  64. //
  65. // UNBUFFERED STREAMS:
  66. // If a filebuf is unbuffered(), the _shortbuf[1] is used as the buffer.
  67.  
  68. void filebuf::init()
  69. {
  70.     _fb._fake = 0;
  71.     _fb._offset = 0;
  72.     _fb._fake = 0;
  73.     xsetflags(_S_IS_FILEBUF);
  74.     _fb._save_gptr = NULL;
  75.  
  76.     _link_in();
  77.     _fb._fileno = -1;
  78. }
  79.  
  80. void streambuf::_un_link()
  81. {
  82.     if (_flags & _S_LINKED) {
  83.     streambuf **f;
  84.     for (f = &_list_all; *f != NULL; f = &(*f)->xchain()) {
  85.         if (*f == this) {
  86.         *f = xchain();
  87.         break;
  88.         }
  89.     }
  90.     _flags &= _S_LINKED;
  91.     }
  92. }
  93.  
  94. void streambuf::_link_in()
  95. {
  96.     if (_flags & _S_LINKED) // Already linked.
  97.     abort();
  98.     _flags |= _S_LINKED;
  99.     xchain() = _list_all;
  100.     _list_all = this;
  101. }
  102.  
  103.  
  104. filebuf::filebuf()
  105. {
  106.     init();
  107. }
  108.  
  109. filebuf::filebuf(int fd)
  110. {
  111.     init();
  112.     attach(fd);
  113. }
  114.  
  115. filebuf::filebuf(int fd, char* p, size_t len)
  116. {
  117.     init();
  118.     attach(fd);
  119.     setbuf(p, len);
  120. }
  121.  
  122. filebuf::~filebuf()
  123. {
  124.     if (!(xflags() & _S_DELETE_DONT_CLOSE))
  125.     close();
  126.  
  127.     _un_link();
  128. }
  129.  
  130. filebuf* filebuf::open(const char *filename, int mode, int prot)
  131. {
  132.     if (is_open())
  133.     return NULL;
  134.     int posix_mode;
  135.     int read_write;
  136.     if ((mode & (ios::in|ios::out)) == (ios::in|ios::out))
  137.     posix_mode = O_RDWR, read_write = _S_CAN_READ+_S_CAN_WRITE;
  138.     else if (mode & (ios::out|ios::app))
  139.     posix_mode = O_WRONLY, read_write = _S_CAN_WRITE;
  140.     else if (mode & (int)ios::in)
  141.     posix_mode = O_RDONLY, read_write = _S_CAN_READ;
  142.     else
  143.     posix_mode = 0, read_write = 0;
  144.     if ((mode & (int)ios::trunc) || mode == (int)ios::out)
  145.     posix_mode |= O_TRUNC;
  146.     if (mode & ios::app)
  147.     posix_mode |= O_APPEND;
  148.     if (!(mode & (int)ios::nocreate) && mode != ios::in)
  149.     posix_mode |= O_CREAT;
  150.     if (mode & (int)ios::noreplace)
  151.     posix_mode |= O_EXCL;
  152.     int fd = ::open(filename, posix_mode, prot);
  153.     if (fd < 0)
  154.     return NULL;
  155.     _fb._fileno = fd;
  156.     xsetflags(read_write);
  157.     if (mode & ios::ate) {
  158.     if (seekoff(0, ios::end) == EOF)
  159.         return NULL;
  160.     }
  161.     return this;
  162. }
  163.  
  164. filebuf* filebuf::open(const char *filename, const char *mode)
  165. {
  166.     if (is_open())
  167.     return NULL;
  168.     int oflags = 0, omode;
  169.     int read_write;
  170.     int oprot = 0666;
  171.     switch (*mode++) {
  172.       case 'r':
  173.     omode = O_RDONLY;
  174.     read_write = _S_CAN_READ;
  175.     break;
  176.       case 'w':
  177.     omode = O_WRONLY;
  178.     oflags = O_CREAT|O_TRUNC;
  179.     read_write = _S_CAN_WRITE;
  180.     break;
  181.       case 'a':
  182.     omode = O_WRONLY;
  183.     oflags = O_CREAT|O_APPEND;
  184.     read_write = _S_CAN_WRITE;
  185.     break;
  186.       default:
  187.     errno = EINVAL;
  188.     return NULL;
  189.     }
  190.     if (mode[0] == '+' || (mode[0] == 'b' && mode[1] == '+')) {
  191.     omode = O_RDWR;
  192.     read_write = _S_CAN_READ+_S_CAN_WRITE;
  193.     }
  194.     int fdesc = ::open(filename, omode|oflags, oprot);
  195.     if (fdesc < 0)
  196.     return NULL;
  197.     _fb._fileno = fdesc;
  198.     xsetflags(read_write);
  199.     return this;
  200. }
  201.  
  202. filebuf* filebuf::attach(int fd)
  203. {
  204.     if (is_open())
  205.     return NULL;
  206.     _fb._fileno = fd;
  207.     xsetflags(_S_CAN_READ|_S_CAN_WRITE|_S_DELETE_DONT_CLOSE);
  208.     return this;
  209. }
  210.  
  211. int filebuf::overflow(int c)
  212. {
  213.     if (pptr() == pbase() && c == EOF)
  214.     return 0;
  215.     if ((xflags() & _S_CAN_WRITE) == 0) // SET ERROR
  216.     return EOF;
  217.     if (is_reading()) {
  218.     if (pptr() != gptr() && pptr() > pbase())
  219.         if (do_flush())
  220.         return EOF;
  221.     setp(gptr(), ebuf());
  222.     setg(egptr(), egptr(), egptr());
  223.     }
  224.     if (allocate() > 0) {
  225.     if (xflags() & _S_LINE_BUF) setp(base(), base());
  226.     else setp(base(), ebuf());
  227.     setg(pbase(), pbase(), pbase());
  228.     }
  229.     int flush_only;
  230.     if (c == EOF) flush_only = 1, c = 0;
  231.     else flush_only = 0;
  232.     if (epptr() == pbase()) { // Line buffering
  233.     if (pptr() < ebuf() && !flush_only) {
  234.         xput_char(c);
  235.         if (c != '\n')
  236.         return (unsigned char)c;
  237.         else
  238.         flush_only = 1;
  239.     }
  240.     }
  241.     size_t to_do = out_waiting();
  242.     if (to_do > 0) {
  243.     char *ptr = pbase();
  244.     for (;;) {
  245.         size_t count = sys_write(ptr, to_do);
  246.         if (count == EOF)
  247.         return EOF;
  248.         _fb._offset += count;
  249.         to_do -= count;
  250.         if (to_do == 0)
  251.         break;
  252.         ptr += count;
  253.     }
  254.     if (xflags() & _S_LINE_BUF) setp(pbase(), pbase());
  255.     else setp(pbase(), epptr());
  256.     setg(egptr(), egptr(), egptr());
  257.     }
  258.     if (flush_only)
  259.     return c;
  260.     else
  261.     return sputc(c);
  262. }
  263.  
  264. int filebuf::underflow()
  265. {
  266. #if 0
  267.     /* SysV does not make this test; take it out for compatibility */
  268.     if (fp->_flags & __SEOF)
  269.     return (EOF);
  270. #endif
  271.  
  272.     if ((xflags() & _S_CAN_READ) == 0) // SET ERROR
  273.     return EOF;
  274.   retry:
  275.     if (gptr() < egptr())
  276.     return *(unsigned char*)gptr();
  277.     if (_fb._save_gptr) { // Free old putback buffer.
  278.     if (eback() != _fb._shortbuf)
  279.         FREE_BUF(eback());
  280.     _fb._save_gptr = NULL;
  281.     setg(base(), _fb._save_gptr, _fb._save_egptr); // Restore get area.
  282.     goto retry;
  283.     }
  284.  
  285.     allocate();
  286. #if 0
  287.     /* if not already reading, have to be reading and writing */
  288.     if ((fp->_flags & __SRD) == 0) {
  289.     if ((fp->_flags & __SRW) == 0)
  290.         return (EOF);
  291.     /* switch to reading */
  292.     if (fp->_flags & __SWR) {
  293.         if (fflush(fp))
  294.         return (EOF);
  295.         fp->_flags &= ~__SWR;
  296.         fp->_w = 0;
  297.         fp->_lbfsize = 0;
  298.     }
  299.     fp->_flags |= __SRD;
  300.     } else {
  301.     // We were reading.  If there is an ungetc buffer,
  302.     // we must have been reading from that.  Drop it,
  303.     // restoring the previous buffer (if any).  If there
  304.     // is anything in that buffer, return.
  305.     if (HASUB(fp)) {
  306.         FREEUB(fp);
  307.         if ((fp->_r = fp->_ur) != 0) {
  308.         fp->_p = fp->_up;
  309.         return (0);
  310.         }
  311.     }
  312.     }
  313. #endif
  314.     if ((xflags() & _S_LINE_BUF) || unbuffered()) {
  315.     // Flush all line buffered files before reading.
  316.     streambuf::flush_all_linebuffered();
  317.     }
  318.     if (pptr() > pbase())
  319.     if (do_flush()) return EOF;
  320.     long count = sys_read(base(), ebuf() - base());
  321.     if (count <= 0) {
  322.     if (count == 0)
  323.         xsetflags(_S_EOF_SEEN);
  324.     else
  325.         xsetflags(_S_ERR_SEEN), count = 0;
  326.     return EOF;
  327.     }
  328.     _fb._offset += count;
  329.     setg(base(), base(), base() + count);
  330.     return *(unsigned char*)gptr();
  331. }
  332.  
  333. int filebuf::pbackfail(int c)
  334. {
  335.     if (pbase() != NULL) { // is writing()
  336.     overflow(EOF);
  337.     // FIXME: check for writing!
  338.     }
  339.     if (_fb._save_gptr == NULL) { // No putback buffer.
  340.     _fb._save_gptr = gptr(); _fb._save_egptr = egptr();
  341.     setg(_fb._shortbuf, _fb._shortbuf+1, _fb._shortbuf+1);
  342.     }
  343.     else { // Increase size of existing putback buffer.
  344.     size_t new_size;
  345.     size_t old_size = egptr() - eback();
  346.     new_size = eback() == _fb._shortbuf ? 128 : 2 * old_size;
  347.     char* new_buf = ALLOC_BUF(new_size);
  348.     memcpy(new_buf+(new_size-old_size), eback(), old_size);
  349.     if (eback() != _fb._shortbuf)
  350.         FREE_BUF(eback());
  351.     setg(new_buf, new_buf+(new_size-old_size), new_buf+new_size);
  352.     }
  353.     gbump(-1);
  354.     *gptr() = c;
  355.     return (unsigned char)c;
  356. }
  357.  
  358. int filebuf::do_flush()
  359. {
  360.     if (egptr() != pbase()) {
  361.     long new_pos = sys_seek(pbase()-egptr(), ios::cur);
  362.     if (new_pos == -1)
  363.         return EOF;
  364.     }
  365.     long to_do = pptr()-pbase();
  366.     char* ptr = pbase();
  367.     while (to_do > 0) {
  368.     size_t count = sys_write(ptr, to_do);
  369.     if (count == EOF)
  370.         return EOF;
  371.     if (_fb._offset >= 0)
  372.         _fb._offset += count;
  373.     to_do -= count;
  374.     ptr += count;
  375.     }
  376.     setg(base(), pptr(), pptr());
  377.     setp(base(), base());
  378.     return 0;
  379. }
  380.  
  381. int filebuf::sync()
  382. {
  383. //    char* ptr = cur_ptr();
  384.     if (pptr() > pbase())
  385.     do_flush();
  386.     if (gptr() != egptr()) {
  387.     if (sys_seek(gptr() - egptr(), ios::cur) == EOF)
  388.         return EOF;
  389.     }
  390.     // FIXME: Handle putback mode!
  391. //    setg(base(), ptr, ptr);
  392.     return 0;
  393. }
  394.  
  395. streampos filebuf::seekoff(streamoff offset, _seek_dir dir, int mode)
  396. {
  397.     fpos_t result, new_offset, delta;
  398.     long count;
  399.  
  400.     // Flush unwritten characters.
  401.     // (This may do an unneeded write if we seek within the buffer.
  402.     // But to be able to switch to reading, we would need to set
  403.     // egptr to ptr.  That can't be done in the current design,
  404.     // which assumes file_ptr() is egptr.  Anyway, since we probably
  405.     // end up flushing when we close(), it doesn't make much difference.)
  406.     if (pptr() > pbase())
  407.     do_flush();
  408.  
  409.     if (unbuffered() || base() == NULL)
  410.     goto dumb;
  411.     // FIXME: What if buffer already allocated even though unbuffered()
  412.     switch (dir) {
  413.       case ios::cur:
  414.     if (_fb._offset < 0) {
  415.         _fb._offset = sys_seek(0, ios::cur);
  416.         if (_fb._offset < 0)
  417.         return EOF;
  418.     }
  419.     // Make offset absolute, assuming current pointer is file_ptr().
  420.     offset += _fb._offset;
  421.     // Now adjust for unread/unflushed characters:
  422.     // FIXME - this stuff needs more thought.
  423. #if 0
  424.     offset += cur_ptr() - file_ptr();
  425.     // FIXME - might be confused if there is a putback buffer.
  426. #else
  427.     offset -= egptr() - gptr(); // Subtract unread characters, if any.
  428.     if (_fb._save_gptr) // Putback mode
  429.         offset -= _fb._save_egptr - _fb._save_gptr;
  430.     // Normally, egptr()==pbase(), but if we earlier switched from
  431.     // get to put mode, the following will (correctly) decrease offset.
  432.     // However, it won't work if there is a putback buffer - FIXME!
  433.     offset += pptr() - egptr(); // Add unflushed characters, in put mode.
  434. #endif
  435.     dir = ios::beg;
  436.     break;
  437.       case ios::beg:
  438.     break;
  439.       case ios::end:
  440.     struct stat st;
  441.     if (sys_stat(&st) == 0 && (st.st_mode & S_IFMT) == S_IFREG) {
  442.         offset += st.st_size;
  443.         dir = ios::beg;
  444.     }
  445.     else
  446.         goto dumb;
  447.     }
  448.     // At this point, dir==ios::beg.
  449.     // FIXME: Handle case of there being a putback buffer!
  450.     // If destination is within current buffer, optimize:
  451.     if (_fb._offset >= 0) {
  452.     fpos_t rel_offset = offset - _fb._offset
  453.         + (file_ptr()-base()); // Offset relative to base().
  454.     if (rel_offset >= 0 && rel_offset <= egptr() - base()) {
  455.         setg(base(), base() + rel_offset, egptr());
  456.         setp(base(), base());
  457.         return offset;
  458.         }
  459.     }
  460.  
  461.     // Try to seek to a block boundary, to improve kernal page management.
  462.     new_offset = offset & ~(ebuf() - base() - 1);
  463.     delta = offset - new_offset;
  464.     if (delta > ebuf() - base()) {
  465.     new_offset = offset;
  466.     delta = 0;
  467.     }
  468.     result = sys_seek(new_offset, ios::beg);
  469.     if (result < 0)
  470.     return EOF;
  471.     _fb._offset = result;
  472.     count = sys_read(base(), ebuf()-base());
  473.     if (count < 0)
  474.     return EOF;
  475.     setg(base(), base(), base()+count);
  476.     xflags(xflags() & ~ _S_EOF_SEEN);
  477.     return result;
  478.   dumb:
  479.     if (_fb._save_gptr != NULL) { // Get rid of putback buffer.
  480.     if (eback() != _fb._shortbuf)
  481.         FREE_BUF(eback());
  482.     _fb._save_gptr = NULL;
  483.     }
  484.     result = sys_seek(offset, dir);
  485.     if (result != EOF) {
  486.     _flags &= ~_S_EOF_SEEN;
  487.     }
  488.     char* start = unbuffered() ? _fb._shortbuf : base();
  489.     setg(start, start, start);
  490.     setp(start, start);
  491.     return result;
  492. }
  493.  
  494. filebuf* filebuf::close()
  495. {
  496.     if (!is_open())
  497.     return NULL;
  498.  
  499.     // Flush.
  500.     overflow(EOF);
  501.  
  502.     /* Free the buffer's storage.  */
  503.     // Or should that be done only on destruction???
  504.     if (_base != NULL && !(_flags & _S_USER_BUF)) {
  505.     FREE_BUF(_base);
  506.     _base = NULL;
  507.     }
  508.  
  509.     int status = sys_close();
  510.  
  511.     _un_link();
  512.     _flags = _IO_MAGIC | _S_IS_FILEBUF;
  513.     _fb._fileno = EOF;
  514.  
  515.     return status < 0 ? NULL : this;
  516. }
  517.  
  518. long filebuf::sys_read(char* buf, size_t size)
  519. {
  520.     return ::read(_fb._fileno, buf, size);
  521. }
  522.  
  523. long filebuf::sys_seek(long offset, _seek_dir dir)
  524. {
  525.     return ::lseek(fd(), offset, (int)dir);
  526. }
  527.  
  528. long filebuf::sys_write(const void *buf, long n)
  529. {
  530.     return ::write(fd(), buf, n);
  531. }
  532.  
  533. int filebuf::sys_stat(void* st)
  534. {
  535.     return ::_fstat(fd(), (struct stat*)st);
  536. }
  537.  
  538. int filebuf::sys_close()
  539. {
  540.     return ::close(fd());
  541. }
  542.  
  543. size_t filebuf::sputn(const char *s, size_t n)
  544. {
  545.     // FIXME: OPTIMIZE THIS (specifically, when unbuffered()).
  546.     return streambuf::sputn(s, n);
  547. }
  548.  
  549. size_t filebuf::sgetn(char *s, size_t n)
  550. {
  551.     // FIXME: OPTIMIZE THIS (specifically, when unbuffered()).
  552.     return streambuf::sgetn(s, n);
  553. }
  554.