E-MailRelay

gfile.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2021 Graeme Walker <graeme_walker@users.sourceforge.net>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program 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
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16// ===
17///
18/// \file gfile.cpp
19///
20
21#include "gdef.h"
22#include "glimits.h"
23#include "gfile.h"
24#include "gprocess.h"
25#include "glog.h"
26#include <iostream>
27#include <cstdio>
28
29 bool G::File::remove( const Path & path , std::nothrow_t ) noexcept
30{
31 int rc = std::remove( path.cstr() ) ;
32 return rc == 0 ;
33}
34
35 void G::File::remove( const Path & path )
36{
37 int rc = std::remove( path.cstr() ) ;
38 int e = Process::errno_() ;
39 if( rc != 0 )
40 {
41 G_WARNING( "G::File::remove: cannot delete file [" << path << "]: " << Process::strerror(e) ) ;
42 throw CannotRemove( path.str() , Process::strerror(e) ) ;
43 }
44}
45
46 bool G::File::rename( const Path & from , const Path & to , std::nothrow_t ) noexcept
47{
48 bool ok = 0 == std::rename( from.cstr() , to.cstr() ) ;
49 if( !ok )
50 {
51 std::remove( to.cstr() ) ;
52 ok = 0 == std::rename( from.cstr() , to.cstr() ) ;
53 }
54 return ok ;
55}
56
57 void G::File::rename( const Path & from , const Path & to , bool ignore_missing )
58{
59 bool is_missing = false ;
60 bool ok = rename( from.cstr() , to.cstr() , is_missing ) ;
61 if( !ok && !(is_missing && ignore_missing) )
62 {
63 throw CannotRename( std::string() + "[" + from.str() + "] to [" + to.str() + "]" ) ;
64 }
65 G_DEBUG( "G::File::rename: \"" << from << "\" -> \"" << to << "\": success=" << ok ) ;
66}
67
68 bool G::File::rename( const char * from , const char * to , bool & enoent ) noexcept
69{
70 bool ok = 0 == std::rename( from , to ) ;
71 int error = Process::errno_() ;
72 enoent = !ok && error == ENOENT ;
73 return ok ;
74}
75
76 void G::File::copy( const Path & from , const Path & to )
77{
78 std::string reason = copy( from , to , 0 ) ;
79 if( !reason.empty() )
80 throw CannotCopy( std::string() + "[" + from.str() + "] to [" + to.str() + "]: " + reason ) ;
81}
82
83 bool G::File::copy( const Path & from , const Path & to , std::nothrow_t )
84{
85 return copy(from,to,0).empty() ;
86}
87
88 bool G::File::copyInto( const Path & from , const Path & to_dir , std::nothrow_t )
89{
90 G::Path to = to_dir + from.basename() ;
91 bool ok = copy(from,to,0).empty() ;
92 if( ok && isExecutable(from,std::nothrow) )
93 ok = chmodx( to , std::nothrow ) ;
94 return ok ;
95}
96
97std::string G::File::copy( const Path & from , const Path & to , int )
98{
99 std::ifstream in ; open( in , from ) ;
100 if( !in.good() )
101 return "cannot open input file" ;
102
103 std::ofstream out ; open( out , to ) ;
104 if( !out.good() )
105 return "cannot open output file" ;
106
107 out << in.rdbuf() ;
108
109 if( in.fail() )
110 return "read error" ;
111
112 //bool empty = in.tellg() == std::streampos(0) ; // not uclibc++
113 bool empty = false ;
114
115 in.close() ;
116 out.close() ;
117
118 if( out.fail() && !empty )
119 return "write error" ;
120
121 return std::string() ;
122}
123
124 void G::File::copy( std::istream & in , std::ostream & out , std::streamsize limit , std::string::size_type block )
125{
126 std::ios_base::iostate in_state = in.rdstate() ;
127
128 block = block ? block : static_cast<std::string::size_type>(limits::file_buffer) ;
129 std::vector<char> buffer ;
130 buffer.reserve( block ) ;
131
132 const auto b = static_cast<std::streamsize>(block) ;
133 std::streamsize size = 0U ;
134 while( ( limit == 0U || size < limit ) && in.good() && out.good() )
135 {
136 std::streamsize request = limit == 0U || (limit-size) > b ? b : (limit-size) ;
137 in.read( &buffer[0] , request ) ;
138 std::streamsize result = in.gcount() ;
139 if( result == 0U )
140 break ;
141 out.write( &buffer[0] , result ) ;
142 size += result ;
143 }
144
145 out.flush() ;
146
147 // restore the input failbit because it might have been set by us reading an incomplete block at eof
148 in.clear( (in.rdstate() & ~std::ios_base::failbit) | (in_state & std::ios_base::failbit) ) ;
149}
150
151 bool G::File::exists( const Path & path )
152{
153 return path.empty() ? false : exists( path , false , true ) ;
154}
155
156 bool G::File::exists( const Path & path , std::nothrow_t )
157{
158 return path.empty() ? false : exists( path , false , false ) ;
159}
160
161 bool G::File::exists( const Path & path , bool error_return_value , bool do_throw )
162{
163 bool enoent = false ;
164 bool eaccess = false ;
165 bool rc = existsImp( path.cstr() , enoent , eaccess ) ; // o/s-specific
166 if( !rc && enoent )
167 {
168 return false ;
169 }
170 else if( !rc && do_throw )
171 {
172 throw StatError( path.str() , eaccess?"permission denied":"" ) ;
173 }
174 else if( !rc )
175 {
176 return error_return_value ;
177 }
178 return true ;
179}
180
181 bool G::File::isLink( const Path & path , std::nothrow_t )
182{
183 Stat s = statImp( path.cstr() ) ;
184 return 0 == s.error && s.is_link ;
185}
186
187 bool G::File::isDirectory( const Path & path , std::nothrow_t )
188{
189 Stat s = statImp( path.cstr() ) ;
190 return 0 == s.error && s.is_dir ;
191}
192
193 bool G::File::isExecutable( const Path & path , std::nothrow_t )
194{
195 Stat s = statImp( path.cstr() ) ;
196 return 0 == s.error && s.is_executable ;
197}
198
199 bool G::File::isEmpty( const Path & path , std::nothrow_t )
200{
201 Stat s = statImp( path.cstr() ) ;
202 return 0 == s.error && s.is_empty ;
203}
204
205std::string G::File::sizeString( const Path & path )
206{
207 Stat s = statImp( path.cstr() ) ;
208 return s.error ? std::string() : std::to_string(s.size) ;
209}
210
211 G::SystemTime G::File::time( const Path & path )
212{
213 Stat s = statImp( path.cstr() ) ;
214 if( s.error )
215 throw TimeError( path.str() , Process::strerror(s.error) ) ;
216 return SystemTime( s.mtime_s , s.mtime_us ) ;
217}
218
219 G::SystemTime G::File::time( const Path & path , std::nothrow_t )
220{
221 Stat s = statImp( path.cstr() ) ;
222 if( s.error )
223 return SystemTime( 0 ) ;
224 return SystemTime( s.mtime_s , s.mtime_us ) ;
225}
226
227 bool G::File::chmodx( const Path & path , std::nothrow_t )
228{
229 return chmodx( path , false ) ;
230}
231
232 void G::File::chmodx( const Path & path )
233{
234 chmodx( path , true ) ;
235}
236
237 bool G::File::mkdir( const Path & dir , std::nothrow_t )
238{
239 return 0 == mkdirImp( dir ) ;
240}
241
242 void G::File::mkdir( const Path & dir )
243{
244 int e = mkdirImp( dir ) ;
245 if( e )
246 throw CannotMkdir( dir.str() , Process::strerror(e) ) ;
247}
248
249 bool G::File::mkdirsr( int * ep , const Path & path , int limit )
250{
251 // (recursive)
252
253 if( limit == 0 )
254 return false ;
255
256 if( exists(path) )
257 return true ;
258
259 if( path.str().empty() )
260 return true ;
261
262 if( !mkdirsr( ep , path.dirname() , limit-1 ) ) // (recursion)
263 return false ;
264
265 int e = mkdirImp( path ) ;
266 if( e )
267 *ep = e ;
268 else
269 chmodx( path , std::nothrow ) ;
270
271 return e == 0 ;
272}
273
274 bool G::File::mkdirs( const Path & path , std::nothrow_t , int limit )
275{
276 int e = 0 ;
277 bool ok = mkdirsr( &e , path , limit ) ;
278 return ok || e == EEXIST ;
279}
280
281 void G::File::mkdirs( const Path & path , int limit )
282{
283 int e = 0 ;
284 if( !mkdirsr(&e,path,limit) && e != EEXIST )
285 throw CannotMkdir( path.str() , e ? G::Process::strerror(e) : std::string() ) ;
286}
287
288 int G::File::compare( const Path & path_1 , const Path & path_2 , bool ignore_whitespace )
289{
290 std::ifstream file_1 ; open( file_1 , path_1 ) ;
291 std::ifstream file_2 ; open( file_2 , path_2 ) ;
292 constexpr int eof = std::char_traits<char>::eof() ; // EOF
293 if( !file_1.good() && !file_2.good() ) return -1 ;
294 if( !file_1.good() ) return -1 ;
295 if( !file_2.good() ) return 1 ;
296 int result = 0 ;
297 int a = eof ;
298 int b = eof ;
299 auto isspace = [](int c){ return c == ' ' || c == '\t' || c == '\n' || c == '\r' ; } ;
300 for(;;)
301 {
302 do { a = file_1.get() ; } while( ignore_whitespace && isspace(a) ) ;
303 do { b = file_2.get() ; } while( ignore_whitespace && isspace(b) ) ;
304 if( a == eof && b == eof )
305 break ;
306 if( a != b )
307 {
308 result = a < b ? -1 : 1 ;
309 break ;
310 }
311 }
312 return result ;
313}
314
G::File::isExecutable
static bool isExecutable(const Path &, std::nothrow_t)
Returns true if the path is probably executable by the calling process.
Definition: gfile.cpp:193
G::File::time
static SystemTime time(const Path &file)
Returns the file's timestamp. Throws on error.
Definition: gfile.cpp:211
G::File::isEmpty
static bool isEmpty(const Path &file, std::nothrow_t)
Returns true if the file size is zero.
Definition: gfile.cpp:199
G::File::isDirectory
static bool isDirectory(const Path &path, std::nothrow_t)
Returns true if the path exists() and is a directory.
Definition: gfile.cpp:187
G::File::sizeString
static std::string sizeString(const Path &file)
Returns the file's size in string format.
Definition: gfile.cpp:205
G::File::rename
static bool rename(const Path &from, const Path &to, std::nothrow_t) noexcept
Renames the file.
Definition: gfile.cpp:46
G::File::remove
static bool remove(const Path &path, std::nothrow_t) noexcept
Deletes the file or directory. Returns false on error.
Definition: gfile.cpp:29
G::File::exists
static bool exists(const Path &file)
Returns true if the file (directory, device etc.) exists.
Definition: gfile.cpp:151
G::File::chmodx
static void chmodx(const Path &file)
Makes the file executable. Throws on error.
Definition: gfile.cpp:232
G::File::isLink
static bool isLink(const Path &path, std::nothrow_t)
Returns true if the path is an existing symlink.
Definition: gfile.cpp:181
G::File::mkdirs
static bool mkdirs(const Path &dir, std::nothrow_t, int=100)
Creates a directory and all necessary parents.
Definition: gfile.cpp:274
G::File::copy
static bool copy(const Path &from, const Path &to, std::nothrow_t)
Copies a file. Returns false on error.
Definition: gfile.cpp:83
G::File::copyInto
static bool copyInto(const Path &from, const Path &to_dir, std::nothrow_t)
Copies a file into a directory and does a chmodx() if necessary.
Definition: gfile.cpp:88
G::File::mkdir
static bool mkdir(const Path &dir, std::nothrow_t)
Creates a directory.
Definition: gfile.cpp:237
G::File::compare
static int compare(const Path &, const Path &, bool ignore_whitespace=false)
Compares the contents of the two files. Returns 0, 1 or -1.
Definition: gfile.cpp:288
G::Path
A Path object represents a file system path.
Definition: gpath.h:72
G::Path::cstr
const char * cstr() const noexcept
Returns the path string.
Definition: gpath.h:221
G::Path::basename
std::string basename() const
Returns the rightmost part of the path, ignoring "." parts.
Definition: gpath.cpp:328
G::Path::dirname
Path dirname() const
Returns the path without the rightmost part, ignoring "." parts.
Definition: gpath.cpp:336
G::Path::str
std::string str() const
Returns the path string.
Definition: gpath.h:215
G::Path::empty
bool empty() const noexcept
Returns true if size() is zero.
Definition: gpath.h:203
G::Process::strerror
static std::string strerror(int errno_)
Translates an 'errno' value into a meaningful diagnostic string.
Definition: gprocess_unix.cpp:127
G::Process::errno_
static int errno_(const SignalSafe &=G::SignalSafe()) noexcept
Returns the process's current 'errno' value.
Definition: gprocess_unix.cpp:115
G::SystemTime
Represents a unix-epoch time with microsecond resolution.
Definition: gdatetime.h:125
gfile.h
glimits.h
glog.h
gprocess.h
G::File::Stat
A portable 'struct stat'.
Definition: gfile.h:64