E-MailRelay

gdirectory_unix.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2024 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 gdirectory_unix.cpp
19///
20
21#include "gdef.h"
22#include "gdirectory.h"
23#include "gprocess.h"
24#include "gdatetime.h"
25#include "gfile.h"
26#include "glog.h"
27#include <sstream>
28#include <sys/stat.h>
29#include <sys/types.h>
30#include <dirent.h>
31#include <unistd.h>
32#include <fcntl.h>
33
34 namespace G
35{
36 class DirectoryIteratorImp ;
37}
38
39//| \class G::DirectoryIteratorImp
40/// A pimple-pattern implementation class for DirectoryIterator using
41/// opendir()/readdir().
42///
43 class G::DirectoryIteratorImp
44{
45 public:
46 explicit DirectoryIteratorImp( const Directory & dir ) ;
47 ~DirectoryIteratorImp() ;
48 bool isDir() const ;
49 bool isLink() const ;
50 bool more() ;
51 bool error() const ;
52 std::string sizeString() const ;
53 Path filePath() const ;
54 std::string fileName() const ;
55
56 public:
57 DirectoryIteratorImp( const DirectoryIteratorImp & ) = delete ;
58 DirectoryIteratorImp( DirectoryIteratorImp && ) = delete ;
59 DirectoryIteratorImp & operator=( const DirectoryIteratorImp & ) = delete ;
60 DirectoryIteratorImp & operator=( DirectoryIteratorImp && ) = delete ;
61
62 private:
63 DIR * m_d {nullptr} ;
64 struct dirent * m_dp {nullptr} ;
65 Directory m_dir ;
66 bool m_error {true} ;
67 bool m_is_dir {false} ;
68 bool m_is_link {false} ;
69} ;
70
71//
72
73 int G::Directory::usable( bool for_creation ) const
74{
75 if( m_path.empty() )
76 return ENOTDIR ;
77
78 // use opendir("foo/.") rather than opendir("foo") to verify
79 // that any contained files can be stat()ed -- ie. that all
80 // directory parts in the m_path have '--x'
81 std::string path_dot = m_path.str() + (m_path.str()=="/"?"":"/") + "." ;
82
83 DIR * p = ::opendir( path_dot.c_str() ) ;
84 int error = p == nullptr ? Process::errno_() : 0 ;
85 if( p != nullptr )
86 ::closedir( p ) ;
87
88 if( error == 0 && for_creation )
89 {
90 // (not definitive -- see also GNU/Linux ::euidaccess())
91 int rc = ::access( m_path.cstr() , W_OK ) ;
92 error = rc == 0 ? 0 : Process::errno_() ;
93 }
94 return error ;
95}
96
97 bool G::Directory::writeable( const std::string & filename ) const
98{
99 Path path( m_path , filename.empty() ? tmp() : filename ) ;
100 return File::probe( path ) ;
101}
102
103// ===
104
105 G::DirectoryIterator::DirectoryIterator( const Directory & dir ) :
106 m_imp(std::make_unique<DirectoryIteratorImp>(dir))
107{
108}
109
110 G::DirectoryIterator::~DirectoryIterator()
111= default ;
112
113 bool G::DirectoryIterator::error() const
114{
115 return m_imp->error() ;
116}
117
118 bool G::DirectoryIterator::more()
119{
120 return m_imp->more() ;
121}
122
123 G::Path G::DirectoryIterator::filePath() const
124{
125 return m_imp->filePath() ;
126}
127
128std::string G::DirectoryIterator::fileName() const
129{
130 return m_imp->fileName() ;
131}
132
133 bool G::DirectoryIterator::isLink() const
134{
135 return m_imp->isLink() ;
136}
137
138 bool G::DirectoryIterator::isDir() const
139{
140 return m_imp->isDir() ;
141}
142
143#ifndef G_LIB_SMALL
144std::string G::DirectoryIterator::sizeString() const
145{
146 return m_imp->sizeString() ;
147}
148#endif
149
150// ===
151
152G::DirectoryIteratorImp::DirectoryIteratorImp( const Directory & dir ) :
153 m_dir(dir)
154{
155 m_d = ::opendir( dir.path().cstr() ) ;
156 m_error = m_d == nullptr ; // NOLINT
157}
158
159 bool G::DirectoryIteratorImp::error() const
160{
161 return m_error ;
162}
163
164 bool G::DirectoryIteratorImp::more()
165{
166 while( !m_error )
167 {
168 m_dp = ::readdir( m_d ) ;
169 m_error = m_dp == nullptr ;
170 if( m_error )
171 break ;
172
173 static_assert( sizeof(dirent::d_name) > 2U , "" ) ;
174 bool special = m_dp->d_name[0] == '.' && (
175 ( m_dp->d_name[1] == '\0' ) ||
176 ( m_dp->d_name[1] == '.' && m_dp->d_name[2] == '\0' ) ) ;
177
178 if( special )
179 {
180 m_is_dir = true ;
181 m_is_link = false ;
182 }
183 #if GCONFIG_HAVE_DIRENT_D_TYPE
184 else if( m_dp->d_type != DT_UNKNOWN )
185 {
186 m_is_dir = m_dp->d_type == DT_DIR ;
187 m_is_link = m_dp->d_type == DT_LNK ;
188 if( m_is_link )
189 m_is_dir = File::isDirectory(filePath(),std::nothrow) ;
190 }
191 #endif
192 else
193 {
194 auto statbuf = File::stat( filePath() , /*symlink_nofollow=*/true ) ;
195 m_error = statbuf.error != 0 ;
196 m_is_dir = !m_error && statbuf.is_dir ;
197 m_is_link = !m_error && statbuf.is_link ;
198 if( m_is_link )
199 {
200 statbuf = File::stat( filePath() , /*symlink_nofollow=*/false ) ;
201 m_is_dir = statbuf.error != 0 && statbuf.is_dir ;
202 }
203 }
204 if( !special )
205 break ;
206 }
207 return !m_error ;
208}
209
210 G::Path G::DirectoryIteratorImp::filePath() const
211{
212 return m_dir.path() / fileName() ;
213}
214
215std::string G::DirectoryIteratorImp::fileName() const
216{
217 return m_dp == nullptr ? std::string() : std::string(m_dp->d_name) ;
218}
219
220 bool G::DirectoryIteratorImp::isDir() const
221{
222 return m_is_dir ;
223}
224
225 bool G::DirectoryIteratorImp::isLink() const
226{
227 return m_is_link ;
228}
229
230G::DirectoryIteratorImp::~DirectoryIteratorImp()
231{
232 if( m_d != nullptr )
233 ::closedir( m_d ) ;
234}
235
236std::string G::DirectoryIteratorImp::sizeString() const
237{
238 std::string s = File::sizeString( filePath() ) ;
239 return s.empty() ? std::string(1U,'0') : s ;
240}
241
G::DirectoryIterator::DirectoryIterator
DirectoryIterator(const Directory &dir)
Constructor taking a directory reference.
Definition: gdirectory_unix.cpp:105
G::DirectoryIterator::fileName
std::string fileName() const
Returns the name of the current item.
Definition: gdirectory_unix.cpp:128
G::DirectoryIterator::error
bool error() const
Returns true on error. The caller should stop the iteration.
Definition: gdirectory_unix.cpp:113
G::DirectoryIterator::sizeString
std::string sizeString() const
Returns the file size as a decimal string.
Definition: gdirectory_unix.cpp:144
G::DirectoryIterator::isLink
bool isLink() const
Returns true if the current item is a symlink.
Definition: gdirectory_unix.cpp:133
G::DirectoryIterator::~DirectoryIterator
~DirectoryIterator()
Destructor.
G::DirectoryIterator::isDir
bool isDir() const
Returns true if the current item is a directory or a symlink to a directory.
Definition: gdirectory_unix.cpp:138
G::DirectoryIterator::more
bool more()
Returns true if more and advances by one.
Definition: gdirectory_unix.cpp:118
G::DirectoryIterator::filePath
Path filePath() const
Returns the path of the current item.
Definition: gdirectory_unix.cpp:123
G::Directory
An encapsulation of a file system directory that works with G::DirectoryIterator.
Definition: gdirectory.h:48
G::Directory::usable
int usable(bool for_creating_files=false) const
Returns zero if the object represents a valid directory with permissions that dont disallow reading o...
Definition: gdirectory_unix.cpp:73
G::Directory::writeable
bool writeable(const std::string &probe_filename=tmp()) const
Tries to create and then delete an empty test file in the directory.
Definition: gdirectory_unix.cpp:97
G::Directory::path
Path path() const
Returns the directory's path, as passed in to the ctor.
Definition: gdirectory.cpp:50
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:179
G::File::sizeString
static std::string sizeString(const Path &file)
Returns the file's size in string format.
Definition: gfile.cpp:199
G::File::stat
static Stat stat(const Path &path, bool symlink_nofollow=false)
Returns a file status structure.
Definition: gfile.cpp:174
G::File::probe
static bool probe(const Path &) noexcept
Creates and deletes a temporary probe file.
Definition: gfile_unix.cpp:120
G::Path
A Path object represents a file system path.
Definition: gpath.h:82
G::Path::cstr
const value_type * cstr() const noexcept
Returns the path's c-string.
Definition: gpath.h:249
G::Path::str
std::string str() const
Returns the path string.
Definition: gpath.h:243
G::Path::empty
bool empty() const noexcept
Returns true if the path is empty.
Definition: gpath.h:237
G::Process::errno_
static int errno_(const SignalSafe &=G::SignalSafe()) noexcept
Returns the process's current 'errno' value.
Definition: gprocess_unix.cpp:125
gdatetime.h
gdirectory.h
gfile.h
glog.h
gprocess.h
G
Low-level classes.
Definition: garg.h:36
std
STL namespace.