VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gfile.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2017 Graeme Walker
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 // 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 , const G::File::NoThrow & )
30 {
31  bool rc = 0 == std::remove( path.str().c_str() ) ;
32  G_DEBUG( "G::File::remove: \"" << path << "\": success=" << rc ) ;
33  return rc ;
34 }
35 
36 void G::File::remove( const Path & path )
37 {
38  if( 0 != std::remove( path.str().c_str() ) )
39  {
40  //int error = G::Process::errno_() ;
41  throw CannotRemove( path.str() ) ;
42  }
43  G_DEBUG( "G::File::remove: \"" << path << "\"" ) ;
44 }
45 
46 bool G::File::rename( const Path & from , const Path & to , const NoThrow & )
47 {
48  bool rc = 0 == std::rename( from.str().c_str() , to.str().c_str() ) ;
49  G_DEBUG( "G::File::rename: \"" << from << "\" -> \"" << to << "\": success=" << rc ) ;
50  return rc ;
51 }
52 
53 void G::File::rename( const Path & from , const Path & to )
54 {
55  if( 0 != std::rename( from.str().c_str() , to.str().c_str() ) )
56  {
57  //int error = G::Process::errno_() ;
58  throw CannotRename( std::string() + "[" + from.str() + "] to [" + to.str() + "]" ) ;
59  }
60  G_DEBUG( "G::File::rename: \"" << from << "\" -> \"" << to << "\"" ) ;
61 }
62 
63 void G::File::copy( const Path & from , const Path & to )
64 {
65  std::string reason = copy( from , to , 0 ) ;
66  if( !reason.empty() )
67  throw CannotCopy( std::string() + "[" + from.str() + "] to [" + to.str() + "]: " + reason ) ;
68 }
69 
70 bool G::File::copy( const Path & from , const Path & to , const NoThrow & )
71 {
72  return copy(from,to,0).empty() ;
73 }
74 
75 std::string G::File::copy( const Path & from , const Path & to , int )
76 {
77  std::ifstream in( from.str().c_str() , std::ios::binary | std::ios::in ) ;
78  if( !in.good() )
79  return "cannot open input file" ;
80 
81  std::ofstream out( to.str().c_str() , std::ios::binary | std::ios::out | std::ios::trunc ) ;
82  if( !out.good() )
83  return "cannot open output file" ;
84 
85  out << in.rdbuf() ;
86 
87  if( in.fail() )
88  return "read error" ;
89 
90  in.close() ;
91  out.close() ;
92 
93  if( out.fail() )
94  return "write error" ;
95 
96  return std::string() ;
97 }
98 
99 void G::File::copy( std::istream & in , std::ostream & out , std::streamsize limit , std::string::size_type block )
100 {
101  std::ios_base::iostate in_state = in.rdstate() ;
102 
103  block = block ? block : static_cast<std::string::size_type>(limits::file_buffer) ;
104  std::vector<char> buffer ;
105  buffer.reserve( block ) ;
106 
107  const std::streamsize b = static_cast<std::streamsize>(block) ;
108  std::streamsize size = 0U ;
109  while( ( limit == 0U || size < limit ) && in.good() && out.good() )
110  {
111  std::streamsize request = limit == 0U || (limit-size) > b ? b : (limit-size) ;
112  in.read( &buffer[0] , request ) ;
113  std::streamsize result = in.gcount() ;
114  if( result == 0U )
115  break ;
116  out.write( &buffer[0] , result ) ;
117  size += result ;
118  }
119 
120  out.flush() ;
121 
122  // restore the input failbit because it might have been set by us reading an incomplete block at eof
123  in.clear( (in.rdstate() & ~std::ios_base::failbit) | (in_state & std::ios_base::failbit) ) ;
124 }
125 
126 void G::File::mkdir( const Path & dir )
127 {
128  if( ! mkdir( dir , NoThrow() ) )
129  throw CannotMkdir( dir.str() ) ;
130 }
131 
132 bool G::File::exists( const Path & path )
133 {
134  return exists( path , false , true ) ;
135 }
136 
137 bool G::File::exists( const Path & path , const NoThrow & )
138 {
139  return exists( path , false , false ) ;
140 }
141 
142 bool G::File::exists( const Path & path , bool on_error , bool do_throw )
143 {
144  bool enoent = false ;
145  bool eaccess = false ;
146  bool rc = exists( path.str().c_str() , enoent , eaccess ) ; // o/s-specific implementation
147  if( !rc && enoent )
148  {
149  return false ;
150  }
151  else if( !rc && do_throw )
152  {
153  throw StatError( path.str() , eaccess?"permission denied":"" ) ;
154  }
155  else if( !rc )
156  {
157  return on_error ;
158  }
159  return true ;
160 }
161 
162 bool G::File::chmodx( const Path & path , const NoThrow & )
163 {
164  return chmodx( path , false ) ;
165 }
166 
167 void G::File::chmodx( const Path & path )
168 {
169  chmodx( path , true ) ;
170 }
171 
172 bool G::File::mkdirs( const Path & path , const NoThrow & , int limit )
173 {
174  // (recursive)
175 
176  G_DEBUG( "File::mkdirs: " << path ) ;
177  if( limit == 0 )
178  return false ;
179 
180  // use a trial mkdir() on the our way towards the root to avoid
181  // problems with the windows "virtual store" mis-feature
182  const bool mkdir_trial = true ;
183  if( mkdir_trial )
184  {
185  if( mkdir(path,NoThrow()) )
186  {
187  G_DEBUG( "File::mkdirs: mkdir(" << path << ") -> ok" ) ;
188  chmodx( path , NoThrow() ) ;
189  return true ;
190  }
191  }
192 
193  if( exists(path) )
194  return true ;
195 
196  if( path.str().empty() )
197  return true ;
198 
199  if( ! mkdirs( path.dirname() , NoThrow() , limit-1 ) ) // (recursion)
200  return false ;
201 
202  G_DEBUG( "File::mkdirs: mkdir(" << path << ")" ) ;
203  bool ok = mkdir( path , NoThrow() ) ;
204  if( ok )
205  chmodx( path , NoThrow() ) ;
206 
207  G_DEBUG( "File::mkdirs: mkdir(" << path << ") -> " << (ok?"ok":"failed") ) ;
208  return ok ;
209 }
210 
211 void G::File::mkdirs( const Path & path , int limit )
212 {
213  if( ! mkdirs(path,NoThrow(),limit) )
214  throw CannotMkdir(path.str()) ;
215 }
216 
217 void G::File::create( const Path & path )
218 {
219  std::ofstream f( path.str().c_str() , std::ios_base::out | std::ios_base::app ) ;
220  f.close() ;
221  if( !exists(path) ) // race
222  throw CannotCreate( path.str() ) ;
223 }
224 
225 /// \file gfile.cpp
std::string str() const
Returns the path string.
Definition: gpath.cpp:290
static bool copy(const Path &from, const Path &to, const NoThrow &)
Copies a file. Returns false on error.
Definition: gfile.cpp:70
static void create(const Path &)
Creates an empty file. Throws on error.
Definition: gfile.cpp:217
static bool rename(const Path &from, const Path &to, const NoThrow &)
Renames the file. Returns false on error.
Definition: gfile.cpp:46
static bool mkdirs(const Path &dir, const NoThrow &, int=100)
Creates a directory and all necessary parents.
Definition: gfile.cpp:172
static bool exists(const Path &file)
Returns true if the file (directory, device etc.) exists.
Definition: gfile.cpp:132
Path dirname() const
Returns the path without the rightmost part, ignoring "." parts.
Definition: gpath.cpp:318
static bool mkdir(const Path &dir, const NoThrow &)
Creates a directory. Returns false on error.
Definition: gfile_unix.cpp:45
static bool remove(const Path &path, const NoThrow &)
Deletes the file or directory. Returns false on error.
Definition: gfile.cpp:29
An overload discriminator class for File methods.
Definition: gfile.h:53
static void chmodx(const Path &file)
Makes the file executable.
Definition: gfile.cpp:167
A Path object represents a file system path.
Definition: gpath.h:72