summaryrefslogtreecommitdiff
path: root/lib/fdbuf/fdobuf.cc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/fdbuf/fdobuf.cc')
-rw-r--r--lib/fdbuf/fdobuf.cc214
1 files changed, 214 insertions, 0 deletions
diff --git a/lib/fdbuf/fdobuf.cc b/lib/fdbuf/fdobuf.cc
new file mode 100644
index 0000000..e1a6031
--- /dev/null
+++ b/lib/fdbuf/fdobuf.cc
@@ -0,0 +1,214 @@
+// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#include "fdbuf.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+///////////////////////////////////////////////////////////////////////////////
+// Globals
+///////////////////////////////////////////////////////////////////////////////
+fdobuf fout(1);
+fdobuf ferr(2);
+
+///////////////////////////////////////////////////////////////////////////////
+// Class fdobuf
+///////////////////////////////////////////////////////////////////////////////
+fdobuf::fdobuf(int fdesc, bool dc, unsigned bufsz)
+ : fdbuf(fdesc, dc, bufsz),
+ bufpos(0)
+{
+}
+
+fdobuf::fdobuf(const char* filename, int f, int mode, unsigned bufsz)
+ : fdbuf(open(filename, O_WRONLY | f, mode), true, bufsz),
+ bufpos(0)
+{
+ if(fd == -1) {
+ flags = flag_error;
+ errnum = errno;
+ }
+}
+
+fdobuf::~fdobuf()
+{
+ flush();
+}
+
+bool fdobuf::close()
+{
+ if(!flush())
+ return false;
+ lock();
+ bool r = fdbuf::close();
+ unlock();
+ return r;
+}
+
+bool fdobuf::operator!() const
+{
+ return error() || closed();
+}
+
+bool fdobuf::nflush(bool withsync)
+{
+ if(flags)
+ return false;
+ while(bufstart < buflength) {
+ ssize_t written = _write(buf+bufstart, buflength-bufstart);
+ if(written < 0) {
+ flags |= flag_error;
+ errnum = errno;
+ return false;
+ }
+ else {
+ bufstart += written;
+ offset += written;
+ }
+ }
+ buflength = 0;
+ bufstart = 0;
+ bufpos = 0;
+ if(withsync && (fsync(fd) == -1)) {
+ flags |= flag_error;
+ errnum = errno;
+ return false;
+ }
+ return true;
+}
+
+bool fdobuf::flush()
+{
+ lock();
+ bool r = nflush(false);
+ unlock();
+ return r;
+}
+
+bool fdobuf::sync()
+{
+ lock();
+ bool r = nflush(true);
+ unlock();
+ return r;
+}
+
+bool fdobuf::write(char ch)
+{
+ if(flags)
+ return false;
+
+ lock();
+ count = 0;
+ buf[bufpos++] = ch;
+ //if(buflength >= bufsize && !nflush(false)) {
+ // unlock();
+ // return false;
+ //}
+ if(bufpos >= buflength)
+ buflength = bufpos;
+ if(buflength >= bufsize && !nflush(false)) {
+ unlock();
+ return false;
+ }
+ count = 1;
+ unlock();
+ return true;
+}
+
+bool fdobuf::write_large(const char* data, unsigned datalen)
+{
+ if(flags)
+ return false;
+
+ lock();
+ count = 0;
+
+ if(!nflush(false)) {
+ unlock();
+ return false;
+ }
+
+ while(datalen > 0) {
+ ssize_t written = _write(data, datalen);
+ if(written < 0) {
+ flags |= flag_error;
+ errnum = errno;
+ unlock();
+ return false;
+ }
+ datalen -= written;
+ data += written;
+ offset += written;
+ count += written;
+ }
+ unlock();
+ return true;
+}
+
+bool fdobuf::write(const char* data, unsigned datalen)
+{
+ if(datalen >= bufsize)
+ return write_large(data, datalen);
+
+ if(flags)
+ return false;
+
+ lock();
+ const char* ptr = data;
+ count = 0;
+ // Amount is the number of bytes available in the buffer
+ unsigned amount = bufsize-bufpos;
+ while(datalen >= amount) {
+ // If we get here, this copy will completely fill the buffer,
+ // requiring a flush
+ memcpy(buf+bufpos, ptr, amount);
+ bufpos = bufsize;
+ buflength = bufsize;
+ datalen -= amount;
+ ptr += amount;
+ if(!nflush(false)) {
+ unlock();
+ return false;
+ }
+ count += amount;
+ amount = bufsize-bufpos;
+ }
+ // At this point, the remaining data will fit into the buffer
+ memcpy(buf+bufpos, ptr, datalen);
+ count += datalen;
+ bufpos += datalen;
+ if(bufpos > buflength) buflength = bufpos;
+ unlock();
+ return true;
+}
+
+ssize_t fdobuf::_write(const char* buf, ssize_t len)
+{
+ return ::write(fd, buf, len);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Manipulators
+///////////////////////////////////////////////////////////////////////////////
+fdobuf& endl(fdobuf& fd)
+{
+ fd.write("\n", 1);
+ fd.flush();
+ return fd;
+}