home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / gnustuff / tos / g__~1 / gplibs17.zoo / filebuf.cc < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-02  |  16.3 KB  |  633 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/stat.h>
  21. #include <fcntl.h>
  22. #include <errno.h>
  23.  
  24. // An fstream can be in at most one of put mode, get mode, or putback mode.
  25. // Putback mode is a variant of get mode.
  26.  
  27. // In a filebuf, there is only one current position, instead of two
  28. // separate get and put pointers.  In get mode, the current posistion
  29. // is that of gptr(); in put mode that of pptr().
  30.  
  31. // The position in the buffer that corresponds to the position
  32. // in external file system is file_ptr().
  33. // This is normally egptr(), except in putback mode, when it is _save_egptr.
  34. // If the field _fb._offset is >= 0, it gives the offset in
  35. // the file as a whole corresponding to eGptr(). (???)
  36.  
  37. // PUT MODE:
  38. // If a filebuf is in put mode, pbase() is non-NULL and equal to base().
  39. // Also, epptr() == ebuf().
  40. // Also, eback() == gptr() && gptr() == egptr().
  41. // The un-flushed character are those between pbase() and pptr().
  42. // GET MODE:
  43. // If a filebuf is in get or putback mode, eback() != egptr().
  44. // In get mode, the unread characters are between gptr() and egptr().
  45. // The OS file position corresponds to that of egptr().
  46. // PUTBACK MODE:
  47. // Putback mode is used to remember "excess" characters that have
  48. // been sputbackc'd in a separate putback buffer.
  49. // In putback mode, the get buffer points to the special putback buffer.
  50. // The unread characters are the characters between gptr() and egptr()
  51. // in the putback buffer, as well as the area between save_gptr()
  52. // and save_egptr(), which point into the original reserve buffer.
  53. // (The pointers save_gptr() and save_egptr() are the values
  54. // of gptr() and egptr() at the time putback mode was entered.)
  55. // The OS position corresponds to that of save_egptr().
  56. //
  57. // LINE BUFFERED OUTPUT:
  58. // During line buffered output, pbase()==base() && epptr()==base().
  59. // However, ptr() may be anywhere between base() and ebuf().
  60. // This forces a call to filebuf::overflow(int C) on every put.
  61. // If there is more space in the buffer, and C is not a '\n',
  62. // then C is inserted, and pptr() incremented.
  63. //
  64. // UNBUFFERED STREAMS:
  65. // If a filebuf is unbuffered(), the _shortbuf[1] is used as the buffer.
  66.  
  67. #define CLOSED_FILEBUF_FLAGS \
  68.   (_S_IS_FILEBUF+_S_NO_READS+_S_NO_WRITES+_S_TIED_PUT_GET)
  69.  
  70. void filebuf::init()
  71. {
  72.     _fb._offset = 0;
  73.  
  74.     _link_in();
  75.     _fb._fileno = -1;
  76. }
  77.  
  78. filebuf::filebuf() : backupbuf(CLOSED_FILEBUF_FLAGS)
  79. {
  80.     init();
  81. }
  82.  
  83. filebuf::filebuf(int fd) : backupbuf(CLOSED_FILEBUF_FLAGS)
  84. {
  85.     init();
  86.     attach(fd);
  87. }
  88.  
  89. filebuf::filebuf(int fd, char* p, _G_size_t len) : backupbuf(CLOSED_FILEBUF_FLAGS)
  90. {
  91.     init();
  92.     attach(fd);
  93.     setbuf(p, len);
  94. }
  95.  
  96. filebuf::~filebuf()
  97. {
  98.     if (!(xflags() & _S_DELETE_DONT_CLOSE))
  99.     close();
  100.  
  101.     _un_link();
  102. }
  103.  
  104. #ifdef atarist
  105. extern "C" int __default_mode__;
  106. #endif
  107.  
  108. filebuf* filebuf::open(const char *filename, ios::openmode mode, int prot)
  109. {
  110.     if (is_open())
  111.     return NULL;
  112.     int posix_mode;
  113.     int read_write;
  114. #ifdef atarist
  115.     int rw_mode = (__default_mode__) ? _S_IS_BINARY : 0;
  116. #endif
  117.     if (mode & ios::app)
  118.     mode |= ios::out;
  119.     if ((mode & (ios::in|ios::out)) == (ios::in|ios::out)) {
  120.     posix_mode = O_RDWR;
  121.     read_write = 0;
  122.     }
  123.     else if (mode & ios::out)
  124.     posix_mode = O_WRONLY, read_write = _S_NO_READS;
  125.     else if (mode & (int)ios::in)
  126.     posix_mode = O_RDONLY, read_write = _S_NO_WRITES;
  127.     else
  128.     posix_mode = 0, read_write = _S_NO_READS+_S_NO_WRITES;
  129.     if ((mode & (int)ios::trunc) || mode == (int)ios::out)
  130.     posix_mode |= O_TRUNC;
  131.     if (mode & ios::app)
  132.     posix_mode |= O_APPEND, read_write |= _S_IS_APPENDING;
  133.     if (!(mode & (int)ios::nocreate) && mode != ios::in)
  134.     posix_mode |= O_CREAT;
  135.     if (mode & (int)ios::noreplace)
  136.     posix_mode |= O_EXCL;
  137.     int fd = ::open(filename, posix_mode, prot);
  138.     if (fd < 0)
  139.     return NULL;
  140.     _fb._fileno = fd;
  141. #ifdef atarist
  142.     if(mode & (int)ios::binary)
  143.     rw_mode |= _S_IS_BINARY;
  144.     else if(mode & (int)ios::text)
  145.     rw_mode &= ~_S_IS_BINARY;
  146.     read_write |= rw_mode;
  147.     xsetflags(read_write, (_S_NO_READS+_S_NO_WRITES+_S_IS_APPENDING)|rw_mode);
  148. #else
  149.     xsetflags(read_write, _S_NO_READS+_S_NO_WRITES+_S_IS_APPENDING);
  150. #endif
  151.     if (mode & (ios::ate|ios::app)) {
  152.     if (seekoff(0, ios::end) == EOF)
  153.         return NULL;
  154.     }
  155.     _link_in();
  156.     return this;
  157. }
  158.  
  159. filebuf* filebuf::open(const char *filename, const char *mode)
  160. {
  161.     if (is_open())
  162.     return NULL;
  163.     int oflags = 0, omode;
  164.     int read_write;
  165.     int oprot = 0666;
  166. #ifdef atarist
  167.     int rw_mode = (__default_mode__)? _S_IS_BINARY : 0;
  168. #endif
  169.     switch (*mode++) {
  170.       case 'r':
  171.     omode = O_RDONLY;
  172.     read_write = _S_NO_WRITES;
  173.     break;
  174.       case 'w':
  175.     omode = O_WRONLY;
  176.     oflags = O_CREAT|O_TRUNC;
  177.     read_write = _S_NO_READS;
  178.     break;
  179.       case 'a':
  180.     omode = O_WRONLY;
  181.     oflags = O_CREAT|O_APPEND;
  182.     read_write = _S_NO_READS|_S_IS_APPENDING;
  183.     break;
  184. #ifdef atarist
  185.       case 'b':
  186.     rw_mode |= _S_IS_BINARY;
  187.         break;
  188.       case 't':
  189.     rw_mode &= ~_S_IS_BINARY;
  190.     break;
  191. #endif
  192.       default:
  193.     errno = EINVAL;
  194.     return NULL;
  195.     }
  196.     if (mode[0] == '+' || (mode[0] == 'b' && mode[1] == '+')) {
  197.     omode = O_RDWR;
  198.     read_write = 0;
  199.     }
  200.     int fdesc = ::open(filename, omode|oflags, oprot);
  201.     if (fdesc < 0)
  202.     return NULL;
  203.     _fb._fileno = fdesc;
  204. #ifdef atarist
  205.     read_write |= rw_mode;
  206.     xsetflags(read_write, (_S_NO_READS+_S_NO_WRITES+_S_IS_APPENDING)|rw_mode);
  207. #else
  208.     xsetflags(read_write, _S_NO_READS+_S_NO_WRITES+_S_IS_APPENDING);
  209. #endif
  210.     if (read_write & _S_IS_APPENDING)
  211.     if (seekoff(0, ios::end) == EOF)
  212.         return NULL;
  213.     _link_in();
  214.     return this;
  215. }
  216.  
  217. filebuf* filebuf::attach(int fd)
  218. {
  219.     if (is_open())
  220.     return NULL;
  221.     _fb._fileno = fd;
  222. #ifdef atarist
  223.     xsetflags((__default_mode__)? _S_IS_BINARY : 0,
  224.       (_S_NO_READS+_S_NO_WRITES) | ((__default_mode__) ? _S_IS_BINARY : 0));
  225. #else
  226.     xsetflags(0, _S_NO_READS+_S_NO_WRITES);
  227. #endif
  228.     return this;
  229. }
  230.  
  231. streambuf* filebuf::setbuf(char* p, _G_size_t len)
  232. {
  233.     if (streambuf::setbuf(p, len) == NULL)
  234.     return NULL;
  235.     setp(_base, _base);
  236.     setg(_base, _base, _base);
  237.     return this;
  238. }
  239.  
  240. int filebuf::overflow(int c)
  241. {
  242.     if (xflags() & _S_NO_WRITES) // SET ERROR
  243.     return EOF;
  244.     // Allocate a buffer if needed.
  245.     if (base() == NULL) {
  246.     doallocbuf();
  247.     if (xflags() & _S_LINE_BUF+_S_UNBUFFERED) setp(_base, _base);
  248.     else setp(_base, _ebuf);
  249.     setg(_base, _base, _base);
  250.     _flags |= _S_CURRENTLY_PUTTING;
  251.     }
  252.     // If currently reading, switch to writing.
  253.     else if ((_flags & _S_CURRENTLY_PUTTING) == 0) {
  254.     if (xflags() & _S_LINE_BUF+_S_UNBUFFERED) setp(gptr(), gptr());
  255.     else setp(gptr(), ebuf());
  256.     setg(egptr(), egptr(), egptr());
  257.     _flags |= _S_CURRENTLY_PUTTING;
  258.     }
  259.     if (c == EOF)
  260.     return do_flush();
  261.     if (pptr() == ebuf() ) // Buffer is really full
  262.     if (do_flush() == EOF)
  263.         return EOF;
  264.     xput_char(c);
  265.     if (unbuffered() || (linebuffered() && c == '\n'))
  266.     if (do_flush() == EOF)
  267.         return EOF;
  268.     return (unsigned char)c;
  269. }
  270.  
  271. int filebuf::underflow()
  272. {
  273. #if 0
  274.     /* SysV does not make this test; take it out for compatibility */
  275.     if (fp->_flags & __SEOF)
  276.     return (EOF);
  277. #endif
  278.  
  279.     if (xflags() & _S_NO_READS)
  280.     return EOF;
  281.     if (gptr() < egptr())
  282.     return *(unsigned char*)gptr();
  283.     allocbuf();
  284.  
  285.     // FIXME This can/should be moved to __streambuf ??
  286.     if ((xflags() & _S_LINE_BUF) || unbuffered()) {
  287.     // Flush all line buffered files before reading.
  288.     streambuf::flush_all_linebuffered();
  289.     }
  290. #if 1
  291.     if (pptr() > pbase())
  292.     if (do_flush()) return EOF;
  293. #endif
  294.  
  295.     _G_ssize_t count = sys_read(base(), ebuf() - base());
  296.     if (count <= 0) {
  297.     if (count == 0)
  298.         xsetflags(_S_EOF_SEEN);
  299.     else
  300.         xsetflags(_S_ERR_SEEN), count = 0;
  301.     }
  302.     setg(base(), base(), base() + count);
  303.     setp(base(), base());
  304.     if (count == 0)
  305.     return EOF;
  306.     if (_fb._offset >= 0)
  307.     _fb._offset += count;
  308.     return *(unsigned char*)gptr();
  309. }
  310.  
  311. int filebuf::do_write(const char *data, _G_size_t to_do)
  312. {
  313.     if (to_do == 0)
  314.     return 0;
  315.     if (xflags() & _S_IS_APPENDING) {
  316.     // On a system without a proper O_APPEND implementation,
  317.     // you would need to sys_seek(0, ios::end) here, but is
  318.     // is not needed nor desirable for Unix- or Posix-like systems.
  319.     // Instead, just indicate that offset (before and after) is
  320.     // unpredictable.
  321.     _fb._offset = -1;
  322.     }
  323.     else if (egptr() != pbase()) {
  324.     long new_pos = sys_seek(pbase()-egptr(), ios::cur);
  325.     if (new_pos == -1)
  326.         return EOF;
  327.     _fb._offset = new_pos;
  328.     }
  329.     _G_ssize_t count = sys_write(data, to_do);
  330.     if (_cur_column)
  331.     _cur_column = __adjust_column(_cur_column - 1, data, to_do) + 1;
  332.     if (count != to_do)
  333.     return EOF;
  334.     setg(base(), base(), base());
  335.     if (xflags() & _S_LINE_BUF+_S_UNBUFFERED) setp(base(), base());
  336.     else setp(base(), ebuf());
  337.     return 0;
  338. }
  339.  
  340. int filebuf::sync()
  341. {
  342. //    char* ptr = cur_ptr();
  343.     if (pptr() > pbase())
  344.     if (do_flush()) return EOF;
  345.     if (gptr() != egptr()) {
  346.     streampos delta = gptr() - egptr();
  347.     if (in_backup())
  348.         delta -= eGptr() - Gbase();
  349.     if (sys_seek(delta, ios::cur) == EOF)
  350.         return EOF;
  351.     }
  352.     // FIXME: Cleanup - can this be shared?
  353. //    setg(base(), ptr, ptr);
  354.     return 0;
  355. }
  356.  
  357. #ifdef atarist
  358. # ifndef S_ISREG
  359. #  define S_ISREG(X) (((X) & S_IFMT) == S_IFREG)
  360. # endif
  361. #endif
  362.  
  363. streampos filebuf::seekoff(streamoff offset, _seek_dir dir, int mode)
  364. {
  365.     streampos result, new_offset, delta;
  366.     _G_ssize_t count;
  367.  
  368.     if (mode == 0) // Don't move any pointers.
  369.     dir = ios::cur, offset = 0;
  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.     if (base() == NULL) {
  381.     doallocbuf();
  382.     setp(base(), base());
  383.     setg(base(), base(), base());
  384.     }
  385.     switch (dir) {
  386.       case ios::cur:
  387.     if (_fb._offset < 0) {
  388.         _fb._offset = sys_seek(0, ios::cur);
  389.         if (_fb._offset < 0)
  390.         return EOF;
  391.     }
  392.     // Make offset absolute, assuming current pointer is file_ptr().
  393.     offset += _fb._offset;
  394.  
  395.     offset -= _egptr - _gptr;
  396.     if (in_backup())
  397.         offset -= _other_egptr - _other_gbase;
  398.     dir = ios::beg;
  399.     break;
  400.       case ios::beg:
  401.     break;
  402.       case ios::end:
  403.     struct stat st;
  404.     if (sys_stat(&st) == 0 && S_ISREG(st.st_mode)) {
  405.         offset += st.st_size;
  406.         dir = ios::beg;
  407.     }
  408.     else
  409.         goto dumb;
  410.     }
  411.     // At this point, dir==ios::beg.
  412.  
  413.     // If destination is within current buffer, optimize:
  414.     if (_fb._offset >= 0 && _eback != NULL) {
  415.     // Offset relative to start of main get area.
  416.     _G_fpos_t rel_offset = offset - _fb._offset
  417.         + (eGptr()-Gbase());
  418.     if (rel_offset >= 0) {
  419.         if (in_backup())
  420.         switch_to_main_get_area();
  421.         if (rel_offset <= _egptr - _eback) {
  422.         setg(base(), base() + rel_offset, egptr());
  423.         setp(base(), base());
  424.         return offset;
  425.         }
  426.         // If we have streammarkers, seek forward by reading ahead.
  427.         if (have_markers()) {
  428.         long to_skip = rel_offset - (_gptr - _eback);
  429.         if (ignore(to_skip) != to_skip)
  430.             goto dumb;
  431.         return offset;
  432.         }
  433.     }
  434.     if (rel_offset < 0 && rel_offset >= Bbase() - Bptr()) {
  435.         if (!in_backup())
  436.         switch_to_backup_area();
  437.         gbump(_egptr + rel_offset - gptr());
  438.         return offset;
  439.     }
  440.     }
  441.  
  442.     unsave_markers();
  443.  
  444.     // Try to seek to a block boundary, to improve kernel page management.
  445.     new_offset = offset & ~(ebuf() - base() - 1);
  446.     delta = offset - new_offset;
  447.     if (delta > ebuf() - base()) {
  448.     new_offset = offset;
  449.     delta = 0;
  450.     }
  451.     result = sys_seek(new_offset, ios::beg);
  452.     if (result < 0)
  453.     return EOF;
  454.     if (delta == 0)
  455.     count = 0;
  456.     else {
  457.     count = sys_read(base(), ebuf()-base());
  458.     if (count < delta) {
  459.         // We weren't allowed to read, but try to seek the remainder.
  460.         offset = count == EOF ? delta : delta-count;
  461.         dir = ios::cur;
  462.         goto dumb;
  463.     }
  464.     }
  465.     setg(base(), base()+delta, base()+count);
  466.     setp(base(), base());
  467.     _fb._offset = result + count;
  468.     xflags(xflags() & ~ _S_EOF_SEEN);
  469.     return offset;
  470.   dumb:
  471.     unsave_markers();
  472.     result = sys_seek(offset, dir);
  473.     if (result != EOF) {
  474.     xflags(xflags() & ~_S_EOF_SEEN);
  475.     }
  476.     _fb._offset = result;
  477.     setg(base(), base(), base());
  478.     setp(base(), base());
  479.     return result;
  480. }
  481.  
  482. filebuf* filebuf::close()
  483. {
  484.     if (!is_open())
  485.     return NULL;
  486.  
  487.     // This flushes as well as switching mode.
  488.     if (pptr() > pbase() || put_mode())
  489.     if (switch_to_get_mode()) return NULL;
  490.  
  491.     unsave_markers();
  492.  
  493.     int status = sys_close();
  494.  
  495.     // Free buffer.
  496.     setb(NULL, NULL, 0);
  497.     setg(NULL, NULL, NULL);
  498.     setp(NULL, NULL);
  499.  
  500.     _un_link();
  501.     _flags = _IO_MAGIC|CLOSED_FILEBUF_FLAGS;
  502.     _fb._fileno = EOF;
  503.     _fb._offset = 0;
  504.  
  505.     return status < 0 ? NULL : this;
  506. }
  507.  
  508. _G_ssize_t filebuf::sys_read(char* buf, size_t size)
  509. {
  510.     for (;;) {
  511. #ifdef atarist
  512.     _G_ssize_t count = ::_read(_fb._fileno, buf, size);
  513. #else
  514.     _G_ssize_t count = ::read(_fb._fileno, buf, size);
  515. #endif
  516.     if (count != -1 || errno != EINTR)
  517.         return count;
  518.     }
  519. }
  520.  
  521. _G_fpos_t filebuf::sys_seek(_G_fpos_t offset, _seek_dir dir)
  522. {
  523.     return ::lseek(fd(), offset, (int)dir);
  524. }
  525.  
  526. _G_ssize_t filebuf::sys_write(const void *buf, _G_size_t n)
  527. {
  528.     long to_do = n;
  529.     while (to_do > 0) {
  530. #ifdef atarist
  531.     _G_ssize_t count = ::_write(fd(), buf, to_do);
  532. #else
  533.     _G_ssize_t count = ::write(fd(), buf, to_do);
  534. #endif
  535.     if (count == EOF) {
  536.         if (errno == EINTR)
  537.         continue;
  538.         else {
  539.         _flags |= _S_ERR_SEEN;
  540.         break;
  541.         }
  542.     }
  543.     to_do -= count;
  544.     buf = (void*)((char*)buf + count);
  545.     }
  546.     n -= to_do;
  547.     if (_fb._offset >= 0)
  548.     _fb._offset += n;
  549.     return n;
  550. }
  551.  
  552. int filebuf::sys_stat(void* st)
  553. {
  554.     return ::_fstat(fd(), (struct stat*)st);
  555. }
  556.  
  557. int filebuf::sys_close()
  558. {
  559.     return ::close(fd());
  560. }
  561.  
  562. _G_size_t filebuf::xsputn(const char *s, _G_size_t n)
  563. {
  564.     if (n == 0)
  565.     return 0;
  566.     // This is an optimized implementation.
  567.     // If the amount to be written straddles a block boundary
  568.     // (or the filebuf is unbuffered), use sys_write directly.
  569.  
  570.     _G_size_t to_do = n;
  571.     int must_flush = 0;
  572.     // First figure out how much space is available in the buffer.
  573.     _G_size_t count = _epptr - _pptr; // Space available.
  574.     if (linebuffered() && (_flags & _S_CURRENTLY_PUTTING)) {
  575.     count =_ebuf - _pptr;
  576.     if (count >= n) {
  577.         for (register const char *p = s + n; p > s; ) {
  578.         if (*--p == '\n') {
  579.             count = p - s + 1;
  580.             must_flush = 1;
  581.             break;
  582.         }
  583.         }
  584.     }
  585.     }
  586.     // Then fill the buffer.
  587.     if (count > 0) {
  588.     if (count > to_do)
  589.         count = to_do;
  590.     if (count > 20) {
  591.         memcpy(pptr(), s, count);
  592.         s += count;
  593.     }
  594.     else {
  595.         register char *p = pptr();;
  596.         for (register _G_size_t i = count+1; --i > 0; ) *p++ = *s++;
  597.     }
  598.     pbump(count);
  599.     to_do -= count;
  600.     }
  601.     if (to_do + must_flush > 0) {
  602.     // Next flush the (full) buffer.
  603.     if (__overflow(this, EOF) == EOF)
  604.         return n - to_do;
  605.  
  606.     // Try to maintain alignment: write a whole number of blocks.
  607.     // dont_write is what gets left over.
  608.     _G_size_t block_size = _ebuf - _base;
  609.     _G_size_t dont_write = block_size >= 128 ? to_do % block_size : 0;
  610.  
  611.     _G_ssize_t count = to_do - dont_write;
  612.     if (do_write(s, count) == EOF)
  613.         return n - to_do;
  614.     to_do = dont_write;
  615.  
  616.     // Now write out the remainder.  Normally, this will fit in the
  617.     // buffer, but it's somewhat messier for line-buffered files,
  618.     // so we let streambuf::sputn handle the general case.
  619.     if (dont_write)
  620.         to_do -= streambuf::sputn(s+count, dont_write);
  621.     }
  622.     return n - to_do;
  623. }
  624.  
  625. _G_size_t filebuf::xsgetn(char *s, _G_size_t n)
  626. {
  627.     // FIXME: OPTIMIZE THIS (specifically, when unbuffered()).
  628.     return streambuf::xsgetn(s, n);
  629. }
  630.  
  631. // Non-ANSI AT&T-ism:  Default open protection.
  632. const int filebuf::openprot = 0644;
  633.