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