home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / gnustuff / tos / g__~1 / gplibs15.zoo / filebuf.cc < prev    next >
Encoding:
C/C++ Source or Header  |  1992-10-01  |  15.3 KB  |  608 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/types.h>
  20. #include <sys/file.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 corresponding to eGptr(). (???)
  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. #define CLOSED_FILEBUF_FLAGS \
  69.   (_S_IS_FILEBUF+_S_NO_READS+_S_NO_WRITES+_S_TIED_PUT_GET)
  70.  
  71. void filebuf::init()
  72. {
  73.     _fb._offset = 0;
  74.  
  75.     _link_in();
  76.     _fb._fileno = -1;
  77. }
  78.  
  79. void streambuf::_un_link()
  80. {
  81.     if (_flags & _S_LINKED) {
  82.     streambuf **f;
  83.     for (f = &_list_all; *f != NULL; f = &(*f)->xchain()) {
  84.         if (*f == this) {
  85.         *f = xchain();
  86.         break;
  87.         }
  88.     }
  89.     _flags &= ~_S_LINKED;
  90.     }
  91. }
  92.  
  93. void streambuf::_link_in()
  94. {
  95.     if ((_flags & _S_LINKED) == 0) {
  96.     _flags |= _S_LINKED;
  97.     xchain() = _list_all;
  98.     _list_all = this;
  99.     }
  100. }
  101.  
  102.  
  103. filebuf::filebuf() : backupbuf(CLOSED_FILEBUF_FLAGS)
  104. {
  105.     init();
  106. }
  107.  
  108. filebuf::filebuf(int fd) : backupbuf(CLOSED_FILEBUF_FLAGS)
  109. {
  110.     init();
  111.     attach(fd);
  112. }
  113.  
  114. filebuf::filebuf(int fd, char* p, size_t len) : backupbuf(CLOSED_FILEBUF_FLAGS)
  115. {
  116.     init();
  117.     attach(fd);
  118.     setbuf(p, len);
  119. }
  120.  
  121. filebuf::~filebuf()
  122. {
  123.     if (!(xflags() & _S_DELETE_DONT_CLOSE))
  124.     close();
  125.  
  126.     _un_link();
  127. }
  128.  
  129. #ifdef atarist
  130. extern "C" extern int __default_mode__;
  131. #endif
  132.  
  133. filebuf* filebuf::open(const char *filename, int mode, int prot)
  134. {
  135.     if (is_open())
  136.     return NULL;
  137.     int posix_mode;
  138.     int read_write;
  139. #ifdef atarist
  140.     int rw_mode = (__default_mode__) ? _S_IS_BINARY : 0;
  141. #endif
  142.     if ((mode & (ios::in|ios::out)) == (ios::in|ios::out)) {
  143.     posix_mode = O_RDWR;
  144.     read_write = 0;
  145.     }
  146.     else if (mode & (ios::out|ios::app))
  147.     posix_mode = O_WRONLY, read_write = _S_NO_READS;
  148.     else if (mode & (int)ios::in)
  149.     posix_mode = O_RDONLY, read_write = _S_NO_WRITES;
  150.     else
  151.     posix_mode = 0, read_write = _S_NO_READS+_S_NO_WRITES;
  152.     if ((mode & (int)ios::trunc) || mode == (int)ios::out)
  153.     posix_mode |= O_TRUNC;
  154.     if (mode & ios::app)
  155.     posix_mode |= O_APPEND;
  156.     if (!(mode & (int)ios::nocreate) && mode != ios::in)
  157.     posix_mode |= O_CREAT;
  158.     if (mode & (int)ios::noreplace)
  159.     posix_mode |= O_EXCL;
  160.     int fd = ::open(filename, posix_mode, prot);
  161.     if (fd < 0)
  162.     return NULL;
  163.     _fb._fileno = fd;
  164. #ifdef atarist
  165.     if(mode & (int)ios::binary)
  166.     rw_mode |= _S_IS_BINARY;
  167.     else if(mode & (int)ios::text)
  168.     rw_mode &= ~_S_IS_BINARY;
  169.     read_write |= rw_mode;
  170.     xsetflags(read_write, (_S_NO_READS+_S_NO_WRITES)|rw_mode);
  171. #else
  172.     xsetflags(read_write, _S_NO_READS+_S_NO_WRITES);
  173. #endif
  174.     if (mode & ios::ate) {
  175.     if (seekoff(0, ios::end) == EOF)
  176.         return NULL;
  177.     }
  178.     _link_in();
  179.     return this;
  180. }
  181.  
  182. filebuf* filebuf::open(const char *filename, const char *mode)
  183. {
  184.     if (is_open())
  185.     return NULL;
  186.     int oflags = 0, omode;
  187.     int read_write;
  188.     int oprot = 0666;
  189. #ifdef atarist
  190.     int rw_mode = (__default_mode__)? _S_IS_BINARY : 0;
  191. #endif
  192.     switch (*mode++) {
  193.       case 'r':
  194.     omode = O_RDONLY;
  195.     read_write = _S_NO_WRITES;
  196.     break;
  197.       case 'w':
  198.     omode = O_WRONLY;
  199.     oflags = O_CREAT|O_TRUNC;
  200.     read_write = _S_NO_READS;
  201.     break;
  202.       case 'a':
  203.     omode = O_WRONLY;
  204.     oflags = O_CREAT|O_APPEND;
  205.     read_write = _S_NO_READS;
  206.     break;
  207. #ifdef atarist
  208.       case 'b':
  209.     rw_mode |= _S_IS_BINARY;
  210.         break;
  211.       case 't':
  212.     rw_mode &= ~_S_IS_BINARY;
  213.     break;
  214. #endif
  215.       default:
  216.     errno = EINVAL;
  217.     return NULL;
  218.     }
  219.     if (mode[0] == '+' || (mode[0] == 'b' && mode[1] == '+')) {
  220.     omode = O_RDWR;
  221.     read_write = 0;
  222.     }
  223.     int fdesc = ::open(filename, omode|oflags, oprot);
  224.     if (fdesc < 0)
  225.     return NULL;
  226.     _fb._fileno = fdesc;
  227. #ifdef atarist
  228.     read_write |= rw_mode;
  229.     xsetflags(read_write, (_S_NO_READS+_S_NO_WRITES)|rw_mode);
  230. #else
  231.     xsetflags(read_write, _S_NO_READS+_S_NO_WRITES);
  232. #endif
  233.     _link_in();
  234.     return this;
  235. }
  236.  
  237. filebuf* filebuf::attach(int fd)
  238. {
  239.     if (is_open())
  240.     return NULL;
  241.     _fb._fileno = fd;
  242. #ifdef atarist
  243.     xsetflags(_S_DELETE_DONT_CLOSE |((__default_mode__)? _S_IS_BINARY : 0),
  244.           (_S_DELETE_DONT_CLOSE+_S_NO_READS+_S_NO_WRITES) |
  245.            ((__default_mode__)? _S_IS_BINARY : 0));
  246. #else
  247.     xsetflags(_S_DELETE_DONT_CLOSE,
  248.           _S_DELETE_DONT_CLOSE+_S_NO_READS+_S_NO_WRITES);
  249. #endif
  250.     return this;
  251. }
  252.  
  253. int filebuf::overflow(int c)
  254. {
  255.     if (xflags() & _S_NO_WRITES) // SET ERROR
  256.     return EOF;
  257.     // Allocate a buffer if needed.
  258.     if (base() == NULL) {
  259.     doallocbuf();
  260.     if (xflags() & _S_LINE_BUF+_S_UNBUFFERED) setp(_base, _base);
  261.     else setp(_base, _ebuf);
  262.     setg(_base, _base, _base);
  263.     _flags |= _S_CURRENTLY_PUTTING;
  264.     }
  265.     // If currently reading, switch to writing.
  266.     else if ((_flags & _S_CURRENTLY_PUTTING) == 0) {
  267.     if (xflags() & _S_LINE_BUF+_S_UNBUFFERED) setp(gptr(), gptr());
  268.     else setp(gptr(), ebuf());
  269.     setg(egptr(), egptr(), egptr());
  270.     _flags |= _S_CURRENTLY_PUTTING;
  271.     }
  272.     if (c == EOF)
  273.     return do_flush();
  274.     if (pptr() == ebuf() ) // Buffer is really full
  275.     if (do_flush() == EOF)
  276.         return EOF;
  277.     xput_char(c);
  278.     if (unbuffered() || (linebuffered() && c == '\n'))
  279.     if (do_flush() == EOF)
  280.         return EOF;
  281.     return (unsigned char)c;
  282. }
  283.  
  284. int filebuf::underflow()
  285. {
  286. #if 0
  287.     /* SysV does not make this test; take it out for compatibility */
  288.     if (fp->_flags & __SEOF)
  289.     return (EOF);
  290. #endif
  291.  
  292.    if (xflags() & _S_NO_READS)
  293.     return EOF;
  294.     if (gptr() < egptr())
  295.     return *(unsigned char*)gptr();
  296.     allocbuf();
  297.  
  298.     // FIXME This can/should be moved to __streambuf ??
  299.     if ((xflags() & _S_LINE_BUF) || unbuffered()) {
  300.     // Flush all line buffered files before reading.
  301.     streambuf::flush_all_linebuffered();
  302.     }
  303. #if 1
  304.     if (pptr() > pbase())
  305.     if (do_flush()) return EOF;
  306. #endif
  307.  
  308.     _G_ssize_t count = sys_read(base(), ebuf() - base());
  309.     if (count <= 0) {
  310.     if (count == 0)
  311.         xsetflags(_S_EOF_SEEN);
  312.     else
  313.         xsetflags(_S_ERR_SEEN), count = 0;
  314.     }
  315.     setg(base(), base(), base() + count);
  316.     setp(base(), base());
  317.     if (count == 0)
  318.     return EOF;
  319.     if (_fb._offset >= 0)
  320.     _fb._offset += count;
  321.     return *(unsigned char*)gptr();
  322. }
  323.  
  324. int filebuf::do_write(const char *data, int to_do)
  325. {
  326.     if (to_do == 0)
  327.     return 0;
  328.     if (egptr() != pbase()) {
  329.     long new_pos = sys_seek(pbase()-egptr(), ios::cur);
  330.     if (new_pos == -1)
  331.         return EOF;
  332.     _fb._offset = new_pos;
  333.     }
  334.     _G_ssize_t count = sys_write(data, to_do);
  335.     if (_cur_column)
  336.     _cur_column = __adjust_column(_cur_column - 1, data, to_do) + 1;
  337.     if (count != to_do)
  338.     return EOF;
  339.     setg(base(), base(), base());
  340.     if (xflags() & _S_LINE_BUF+_S_UNBUFFERED) setp(base(), base());
  341.     else setp(base(), ebuf());
  342.     return 0;
  343. }
  344.  
  345. int filebuf::sync()
  346. {
  347. //    char* ptr = cur_ptr();
  348.     if (pptr() > pbase())
  349.     if (do_flush()) return EOF;
  350.     if (gptr() != egptr()) {
  351.     streampos delta = gptr() - egptr();
  352.     if (in_backup())
  353.         delta -= eGptr() - Gbase();
  354.     if (sys_seek(delta, ios::cur) == EOF)
  355.         return EOF;
  356.     }
  357.     // FIXME: Cleanup - can this be shared?
  358. //    setg(base(), ptr, ptr);
  359.     return 0;
  360. }
  361.  
  362. #ifdef atarist
  363. # ifndef S_ISREG
  364. #  define S_ISREG(X) (((X) & S_IFMT) == S_IFREG)
  365. # endif
  366. #endif
  367.  
  368. streampos filebuf::seekoff(streamoff offset, _seek_dir dir, int mode)
  369. {
  370.     streampos result, new_offset, delta;
  371.     _G_ssize_t count;
  372.  
  373.     // Flush unwritten characters.
  374.     // (This may do an unneeded write if we seek within the buffer.
  375.     // But to be able to switch to reading, we would need to set
  376.     // egptr to ptr.  That can't be done in the current design,
  377.     // which assumes file_ptr() is eGptr.  Anyway, since we probably
  378.     // end up flushing when we close(), it doesn't make much difference.)
  379.     if (pptr() > pbase() || put_mode())
  380.     if (switch_to_get_mode()) return EOF;
  381.  
  382.     if (base() == NULL) {
  383.     doallocbuf();
  384.     setp(base(), base());
  385.     setg(base(), base(), base());
  386.     }
  387.     switch (dir) {
  388.       case ios::cur:
  389.     if (_fb._offset < 0) {
  390.         _fb._offset = sys_seek(0, ios::cur);
  391.         if (_fb._offset < 0)
  392.         return EOF;
  393.     }
  394.     // Make offset absolute, assuming current pointer is file_ptr().
  395.     offset += _fb._offset;
  396.  
  397.     offset -= _egptr - _gptr;
  398.     if (in_backup())
  399.         offset -= _other_egptr - _other_gbase;
  400.     dir = ios::beg;
  401.     break;
  402.       case ios::beg:
  403.     break;
  404.       case ios::end:
  405.     struct stat st;
  406.     if (sys_stat(&st) == 0 && S_ISREG(st.st_mode)) {
  407.         offset += st.st_size;
  408.         dir = ios::beg;
  409.     }
  410.     else
  411.         goto dumb;
  412.     }
  413.     // At this point, dir==ios::beg.
  414.  
  415.     // If destination is within current buffer, optimize:
  416.     if (_fb._offset >= 0 && _eback != NULL) {
  417.     // Offset relative to start of main get area.
  418.     fpos_t rel_offset = offset - _fb._offset
  419.         + (eGptr()-Gbase());
  420.     if (rel_offset >= 0) {
  421.         if (in_backup())
  422.         switch_to_main_get_area();
  423.         if (rel_offset <= _egptr - _eback) {
  424.         setg(base(), base() + rel_offset, egptr());
  425.         setp(base(), base());
  426.         return offset;
  427.         }
  428.         // If we have streammarkers, seek forward by reading ahead.
  429.         if (have_markers()) {
  430.         int to_skip = rel_offset - (_gptr - _eback);
  431.         if (ignore(to_skip) != to_skip)
  432.             goto dumb;
  433.         return offset;
  434.         }
  435.     }
  436.     if (rel_offset < 0 && rel_offset >= Bbase() - Bptr()) {
  437.         if (!in_backup())
  438.         switch_to_backup_area();
  439.         gbump(_egptr + rel_offset - gptr());
  440.         return offset;
  441.     }
  442.     }
  443.  
  444.     unsave_markers();
  445.  
  446.     // Try to seek to a block boundary, to improve kernel page management.
  447.     new_offset = offset & ~(ebuf() - base() - 1);
  448.     delta = offset - new_offset;
  449.     if (delta > ebuf() - base()) {
  450.     new_offset = offset;
  451.     delta = 0;
  452.     }
  453.     result = sys_seek(new_offset, ios::beg);
  454.     if (result < 0)
  455.     return EOF;
  456.     if (delta == 0)
  457.     count = 0;
  458.     else {
  459.     count = sys_read(base(), ebuf()-base());
  460.     if (count < delta) {
  461.         // We weren't allowed to read, but try to seek the remainder.
  462.         offset = count == EOF ? delta : delta-count;
  463.         dir = ios::cur;
  464.         goto dumb;
  465.     }
  466.     }
  467.     setg(base(), base()+delta, base()+count);
  468.     setp(base(), base());
  469.     _fb._offset = result + count;
  470.     xflags(xflags() & ~ _S_EOF_SEEN);
  471.     return offset;
  472.   dumb:
  473.     unsave_markers();
  474.     result = sys_seek(offset, dir);
  475.     if (result != EOF) {
  476.     xflags(xflags() & ~_S_EOF_SEEN);
  477.     }
  478.     _fb._offset = result;
  479.     setg(base(), base(), base());
  480.     setp(base(), base());
  481.     return result;
  482. }
  483.  
  484. filebuf* filebuf::close()
  485. {
  486.     if (!is_open())
  487.     return NULL;
  488.  
  489.     // This flushes as well as switching mode.
  490.     if (pptr() > pbase() || put_mode())
  491.     if (switch_to_get_mode()) return NULL;
  492.  
  493.     unsave_markers();
  494.  
  495.     int status = sys_close();
  496.  
  497.     // Free buffer.
  498.     setb(NULL, NULL, 0);
  499.     setg(NULL, NULL, NULL);
  500.     setp(NULL, NULL);
  501.  
  502.     _un_link();
  503.     _flags = _IO_MAGIC|CLOSED_FILEBUF_FLAGS;
  504.     _fb._fileno = EOF;
  505.     _fb._offset = 0;
  506.  
  507.     return status < 0 ? NULL : this;
  508. }
  509.  
  510. _G_ssize_t filebuf::sys_read(char* buf, size_t size)
  511. {
  512.     for (;;) {
  513.     _G_ssize_t count = ::read(_fb._fileno, buf, size);
  514.     if (count != -1 || errno != EINTR)
  515.         return count;
  516.     }
  517. }
  518.  
  519. fpos_t filebuf::sys_seek(fpos_t offset, _seek_dir dir)
  520. {
  521.     return ::lseek(fd(), offset, (int)dir);
  522. }
  523.  
  524. _G_ssize_t filebuf::sys_write(const void *buf, long n)
  525. {
  526.     long to_do = n;
  527.     while (to_do > 0) {
  528.     _G_ssize_t count = ::write(fd(), buf, to_do);
  529.     if (count == EOF) {
  530.         if (errno == EINTR)
  531.         continue;
  532.         else
  533.         break;
  534.     }
  535.     to_do -= count;
  536.     buf += count;
  537.     }
  538.     n -= to_do;
  539.     if (_fb._offset >= 0)
  540.     _fb._offset += n;
  541.     return n;
  542. }
  543.  
  544. int filebuf::sys_stat(void* st)
  545. {
  546.     return ::_fstat(fd(), (struct stat*)st);
  547. }
  548.  
  549. int filebuf::sys_close()
  550. {
  551.     return ::close(fd());
  552. }
  553.  
  554. size_t filebuf::sputn(const char *s, size_t n)
  555. {
  556.     if (n <= 0)
  557.     return 0;
  558.     // This is an optimized implementation.
  559.     // If the amount to be written straddles a block boundary
  560.     // (or the filebuf is unbuffered), use sys_write directly.
  561.  
  562.     // First fill the buffer.
  563.     int to_do = n;
  564.     int count = _epptr - _pptr; // Space available.
  565.     if (count > 0) {
  566.     if (count > to_do)
  567.         count = to_do;
  568.     if (count > 20) {
  569.         memcpy(pptr(), s, count);
  570.         s += count;
  571.     }
  572.     else {
  573.         register char *p = pptr();;
  574.         for (register int i = count; --i >= 0; ) *p++ = *s++;
  575.     }
  576.     pbump(count);
  577.     to_do -= count;
  578.     }
  579.     if (to_do > 0) {
  580.     // Next flush the (full) buffer.
  581.     if (__overflow(this, EOF) == EOF)
  582.         return n - to_do;
  583.  
  584.     // Try to maintain alignment: write a whole number of blocks.
  585.     // dont_write is what gets left over.
  586.     int block_size = _ebuf - _base;
  587.     int dont_write = block_size >= 128 ? to_do % block_size : 0;
  588.  
  589.     _G_ssize_t count = to_do - dont_write;
  590.     if (do_write(s, count) == EOF)
  591.         return n - to_do;
  592.     to_do = dont_write;
  593.  
  594.     // Now write out the remainder.  Normally, this will fit in the
  595.     // buffer, but it's somewhat messier for line-buffered files,
  596.     // so we let streambuf::sputn handle the general case.
  597.     if (dont_write)
  598.         to_do -= streambuf::sputn(s+count, dont_write);
  599.     }
  600.     return n - to_do;
  601. }
  602.  
  603. size_t filebuf::sgetn(char *s, size_t n)
  604. {
  605.     // FIXME: OPTIMIZE THIS (specifically, when unbuffered()).
  606.     return streambuf::sgetn(s, n);
  607. }
  608.