1 // Copyright (C) 2018 Bruce Guenter <bruce@untroubled.org>
2 //
3 // This program is free software; you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation; either version 2 of the License, or
6 // (at your option) any later version.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16 
17 #include "fdbuf.h"
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <string.h>
21 #include <unistd.h>
22 
23 ///////////////////////////////////////////////////////////////////////////////
24 // Globals
25 ///////////////////////////////////////////////////////////////////////////////
26 fdobuf fout(1);
27 fdobuf ferr(2);
28 
29 ///////////////////////////////////////////////////////////////////////////////
30 // Class fdobuf
31 ///////////////////////////////////////////////////////////////////////////////
fdobuf(int fdesc,bool dc,unsigned bufsz)32 fdobuf::fdobuf(int fdesc, bool dc, unsigned bufsz)
33   : fdbuf(fdesc, dc, bufsz),
34     bufpos(0)
35 {
36 }
37 
fdobuf(const char * filename,int f,int mode,unsigned bufsz)38 fdobuf::fdobuf(const char* filename, int f, int mode, unsigned bufsz)
39   : fdbuf(open(filename, O_WRONLY | f, mode), true, bufsz),
40     bufpos(0)
41 {
42   if(fd == -1) {
43     flags = flag_error;
44     errnum = errno;
45   }
46 }
47 
~fdobuf()48 fdobuf::~fdobuf()
49 {
50   flush();
51 }
52 
close()53 bool fdobuf::close()
54 {
55   if(!flush())
56     return false;
57   lock();
58   bool r = fdbuf::close();
59   unlock();
60   return r;
61 }
62 
operator !() const63 bool fdobuf::operator!() const
64 {
65   return error() || closed();
66 }
67 
nflush(bool withsync)68 bool fdobuf::nflush(bool withsync)
69 {
70   if(flags)
71     return false;
72   while(bufstart < buflength) {
73     ssize_t written = _write(buf+bufstart, buflength-bufstart);
74     if(written < 0) {
75       flags |= flag_error;
76       errnum = errno;
77       return false;
78     }
79     else {
80       bufstart += written;
81       offset += written;
82     }
83   }
84   buflength = 0;
85   bufstart = 0;
86   bufpos = 0;
87   if(withsync && (fsync(fd) == -1)) {
88     flags |= flag_error;
89     errnum = errno;
90     return false;
91   }
92   return true;
93 }
94 
flush()95 bool fdobuf::flush()
96 {
97   lock();
98   bool r = nflush(false);
99   unlock();
100   return r;
101 }
102 
sync()103 bool fdobuf::sync()
104 {
105   lock();
106   bool r = nflush(true);
107   unlock();
108   return r;
109 }
110 
write(char ch)111 bool fdobuf::write(char ch)
112 {
113   if(flags)
114     return false;
115 
116   lock();
117   count = 0;
118   buf[bufpos++] = ch;
119   //if(buflength >= bufsize && !nflush(false)) {
120   //  unlock();
121   //  return false;
122   //}
123   if(bufpos >= buflength)
124     buflength = bufpos;
125   if(buflength >= bufsize && !nflush(false)) {
126     unlock();
127     return false;
128   }
129   count = 1;
130   unlock();
131   return true;
132 }
133 
write_large(const char * data,unsigned datalen)134 bool fdobuf::write_large(const char* data, unsigned datalen)
135 {
136   if(flags)
137     return false;
138 
139   lock();
140   count = 0;
141 
142   if(!nflush(false)) {
143     unlock();
144     return false;
145   }
146 
147   while(datalen > 0) {
148     ssize_t written = _write(data, datalen);
149     if(written < 0) {
150       flags |= flag_error;
151       errnum = errno;
152       unlock();
153       return false;
154     }
155     datalen -= written;
156     data += written;
157     offset += written;
158     count += written;
159   }
160   unlock();
161   return true;
162 }
163 
write(const char * data,unsigned datalen)164 bool fdobuf::write(const char* data, unsigned datalen)
165 {
166   if(datalen >= bufsize)
167     return write_large(data, datalen);
168 
169   if(flags)
170     return false;
171 
172   lock();
173   const char* ptr = data;
174   count = 0;
175   // Amount is the number of bytes available in the buffer
176   unsigned amount = bufsize-bufpos;
177   while(datalen >= amount) {
178     // If we get here, this copy will completely fill the buffer,
179     // requiring a flush
180     memcpy(buf+bufpos, ptr, amount);
181     bufpos = bufsize;
182     buflength = bufsize;
183     datalen -= amount;
184     ptr += amount;
185     if(!nflush(false)) {
186       unlock();
187       return false;
188     }
189     count += amount;
190     amount = bufsize-bufpos;
191   }
192   // At this point, the remaining data will fit into the buffer
193   memcpy(buf+bufpos, ptr, datalen);
194   count += datalen;
195   bufpos += datalen;
196   if(bufpos > buflength) buflength = bufpos;
197   unlock();
198   return true;
199 }
200 
_write(const char * buf,ssize_t len)201 ssize_t fdobuf::_write(const char* buf, ssize_t len)
202 {
203   return ::write(fd, buf, len);
204 }
205 
206 ///////////////////////////////////////////////////////////////////////////////
207 // Manipulators
208 ///////////////////////////////////////////////////////////////////////////////
endl(fdobuf & fd)209 fdobuf& endl(fdobuf& fd)
210 {
211   fd.write("\n", 1);
212   fd.flush();
213   return fd;
214 }
215