1 /*
2     file.* - file io classes
3     Copyright (C) 1999-2004  Matthew Mueller <donut AT dakotacom.net>
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 #include "file.h"
23 #include <stdarg.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include "sockstuff.h"
29 #include "strreps.h"
30 #include "path.h"
31 
32 
xxrename(const char * oldpath,const char * newpath)33 void xxrename(const char *oldpath, const char *newpath) {
34 #ifdef WIN32
35 	//On windows rename will not replace an existing file, so check first and remove it.
36 	if (fexists(newpath)) {
37 		if (unlink(newpath))
38 			throw FileEx(Ex_INIT, "rename: unlink %s: %s(%i)\n",newpath,strerror(errno),errno);
39 	}
40 #endif
41 	if (rename(oldpath, newpath))
42 		throw FileEx(Ex_INIT, "rename %s > %s: %s(%i)\n",oldpath,newpath,strerror(errno),errno);
43 }
44 
copyfile(c_file * of,c_file * nf)45 void copyfile(c_file *of, c_file *nf) {
46 	char buf[4096];
47 	int len;
48 	while ((len=of->read(buf, 4096)) != 0)
49 		nf->write(buf, len);
50 }
51 
bfill(uchar * b,int l)52 int c_file_buffy::bfill(uchar *b,int l){
53 	return fileptr->read(b,l);
54 }
55 
c_file(const char * fname)56 c_file::c_file(const char *fname): m_name(fname){
57 	rbuffer=NULL;
58 }
59 
~c_file()60 c_file::~c_file(){
61 //	close();//cant call it here, since doclose is already gone
62 	delete rbuffer;
63 }
64 
vputf(const char * buf,va_list ap)65 ssize_t c_file::vputf(const char *buf, va_list ap){
66 	char *fpbuf;
67 	int i,l;
68 	l=vasprintf(&fpbuf,buf,ap);
69 	try {
70 		i=write(fpbuf,l);
71 	} catch (...) {
72 		free(fpbuf);
73 		throw;
74 	}
75 	free(fpbuf);
76 	return i;
77 }
putf(const char * data,...)78 ssize_t c_file::putf(const char *data,...){
79 	va_list ap;
80 	va_start(ap,data);
81 	ssize_t r=vputf(data, ap);
82 	va_end(ap);
83 	return r;
84 }
read(void * data,size_t len)85 ssize_t c_file::read(void *data,size_t len){
86 	ssize_t i=doread(data,len);
87 	if (i<0) throw FileEx(Ex_INIT,"read %s (%s)", name(), dostrerror());
88 	return i;
89 }
readfull(void * data,size_t len)90 void c_file::readfull(void *data,size_t len){
91 	size_t cur=0;
92 	ssize_t i;
93 	while (cur < len) {
94 		i=doread((char*)data+cur, len-cur);
95 		if (i<0) throw FileEx(Ex_INIT,"readfull %s (%s)", name(), dostrerror());
96 		else if (i==0) throw FileEx(Ex_INIT,"readfull %s: unexpected EOF", name());
97 		cur += i;
98 	}
99 	assert(cur == len);
100 }
write(const void * data,size_t len)101 ssize_t c_file::write(const void *data,size_t len){
102 	size_t sent=0;
103 	ssize_t i;
104 	while (sent < len) {
105 		i=dowrite((char*)data+sent, len-sent);
106 		if (i <= 0)
107 			throw FileEx(Ex_INIT,"write %s: %i!=%i (%s)", name(), sent, len, dostrerror());
108 		sent += i;
109 	}
110 	assert(sent == len);
111 	return sent;
112 }
113 
flush(int local)114 void c_file::flush(int local){
115 //	int i=0;
116 //###########3 dowrite(buffers..)
117 	if (!local) {
118 		int r=doflush();
119 		if (r != 0) throw FileEx(Ex_INIT,"flush %s: %i (%s)", name(), r, dostrerror());
120 	}
121 }
close(void)122 void c_file::close(void){
123 	if (isopen()){
124 		flush(1);
125 		if (doclose() != 0)
126 			throw FileEx(Ex_INIT,"close %s (%s)", name(), dostrerror());
127 	}
128 	if (rbuffer)rbuffer->clearbuf();
129 }
close_noEx(void)130 int c_file::close_noEx(void){
131 	try {
132 		close();
133 		return 0;
134 	} catch (FileEx &e) {
135 		return -1;
136 	}
137 }
initrbuf(void)138 void c_file::initrbuf(void){
139 	if (!rbuffer){
140 		rbuffer=new c_file_buffy(this);
141 	}
142 };
143 
144 
c_file_fd(int dfd,const char * name)145 c_file_fd::c_file_fd(int dfd, const char *name):c_file(name){
146 	fd=::dup(dfd);
147 	if (fd<0)
148 		throw FileEx(Ex_INIT,"dup %s(%i) (%s)", name, dfd, strerror(errno));
149 }
150 
c_file_fd(const char * name,int flags,int mode)151 c_file_fd::c_file_fd(const char *name,int flags, int mode):c_file(name){
152 	fd=::open(name,flags,mode);
153 	if (fd<0)
154 		THROW_OPEN_ERROR("open %s (%s)", name, strerror(errno));
155 		//throw FileEx(Ex_INIT,"open %s (%s)", name, strerror(errno));
156 }
fopen2open(const char * mode)157 int fopen2open(const char *mode){
158 	int m = 0;
159 	switch(mode[0]) {
160 		case 'r': m = strchr(mode,'+')?O_RDWR:O_RDONLY; break;
161 		case 'w': m = (strchr(mode,'+')?O_RDWR:O_WRONLY) | O_CREAT | O_TRUNC; break;
162 		case 'a': m = (strchr(mode,'+')?O_RDWR:O_WRONLY) | O_CREAT | O_APPEND; break;
163 		default:assert(0);
164 	}
165 	if (strchr(mode,'b')) m |= O_BINARY;
166 	return m;
167 }
c_file_fd(const char * name,const char * mode)168 c_file_fd::c_file_fd(const char *name,const char *mode):c_file(name){
169 	int flags=fopen2open(mode);
170 	fd=::open(name,flags,PUBMODE);
171 	if (fd<0)
172 		THROW_OPEN_ERROR("open %s (%s)", name, strerror(errno));
173 }
dostrerror(void)174 const char *c_file_fd::dostrerror(void) {
175 	return strerror(errno);
176 }
doflush(void)177 int c_file_fd::doflush(void){
178 #ifdef HAVE_FSYNC
179 	if (fd>=0)
180 		return fsync(fd);
181 #endif
182 	return 0;
183 }
doclose(void)184 int c_file_fd::doclose(void){
185 	int i=0;
186 	i=::close(fd);
187 	fd=-1;
188 	return i;
189 }
isopen(void) const190 int c_file_fd::isopen(void)const{
191 	return (fd>=0);
192 }
dowrite(const void * data,size_t len)193 ssize_t c_file_fd::dowrite(const void *data,size_t len){
194 	return ::write(fd,(char*)data,len);
195 }
doread(void * data,size_t len)196 ssize_t c_file_fd::doread(void *data,size_t len){
197 	return ::read(fd,data,len);
198 }
seek(int offset,int whence)199 int c_file_fd::seek(int offset, int whence){
200 	int r = lseek(fd, offset, whence);
201 	if (r<0)
202 		throw FileEx(Ex_INIT,"seek %s: %s", name(), dostrerror());
203 	return r;
204 }
205 
206 
207 #ifdef USE_FILE_STREAM
c_file_stream(const char * name,const char * mode)208 int c_file_stream::c_file_stream(const char *name,const char * mode):c_file(name){
209 	if (!(fs=fopen(name,mode)))
210 		THROW_OPEN_ERROR("fopen %s (%s)", name, strerror(errno));
211 }
dostrerror(void)212 const char c_file_stream::*dostrerror(void) {
213 	return strerror(errno);
214 }
doflush(void)215 int c_file_stream::doflush(void){
216 	if (fs)
217 		return fflush(fs);
218 	return 0;
219 }
doclose(void)220 int c_file_stream::doclose(void){
221 	int i=0;
222 	i=fclose(fs);
223 	fs=NULL;
224 	return i;
225 }
isopen(void) const226 int c_file_stream::isopen(void)const{
227 	return (fs!=0);
228 }
dowrite(const void * data,size_t len)229 ssize_t c_file_stream::dowrite(const void *data,size_t len){
230 	return fwrite(data,1,len,fs);
231 }
doread(void * data,size_t len)232 ssize_t c_file_stream::doread(void *data,size_t len){
233 	return fread(data,1,len,fs);
234 }
235 #endif
236 
237 
c_file_tcp(const char * host,const char * port)238 c_file_tcp::c_file_tcp(const char *host,const char * port):c_file(host){
239 	if (m_name.find(':')<0){//this isn't quite right with ipv6 addrs, but its only for error messages so who cares ;)
240 		m_name+=':';
241 		m_name+=port;
242 	}
243 	try {
244 		sock=make_connection(host,port);
245 	} catch (FileEx &e) {
246 		throw FileEx(Ex_INIT,"open %s (%s)", name(), e.getExStr());
247 	}
248 }
dostrerror(void)249 const char *c_file_tcp::dostrerror(void) {
250 	return sock_strerror(sock_errno);
251 }
doflush(void)252 int c_file_tcp::doflush(void){
253 #ifdef HAVE_FSYNC
254 	if (sock_isvalid(sock))
255 		return fsync(sock);
256 #endif
257 	return 0;
258 }
doclose(void)259 int c_file_tcp::doclose(void){
260 	int i=0;
261 	i=sock_close(sock);
262 	sock=SOCK_INVALID;
263 	return i;
264 }
isopen(void) const265 int c_file_tcp::isopen(void)const{
266 	return sock_isvalid(sock);
267 }
dowrite(const void * data,size_t len)268 ssize_t c_file_tcp::dowrite(const void *data,size_t len){
269 	//don't need to use sock_write_ensured since c_file::write handles the looping.
270 	return sock_write(sock,(char*)data, len);
271 }
doread(void * data,size_t len)272 ssize_t c_file_tcp::doread(void *data,size_t len){
273 	return sock_read(sock,data,len);
274 }
datawaiting(void) const275 bool c_file_tcp::datawaiting(void) const {
276 	return sock_datawaiting(sock);
277 }
278