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