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