Evo C++ Library v0.5.1
sysio_dir.h
Go to the documentation of this file.
1 // Evo C++ Library
2 /* Copyright 2019 Justin Crowell
3 Distributed under the BSD 2-Clause License -- see included file LICENSE.txt for details.
4 */
6 
7 #pragma once
8 #ifndef INCL_evo_impl_sysio_dir_h
9 #define INCL_evo_impl_sysio_dir_h
10 
11 #include "sys.h"
12 #include <sys/types.h>
13 
14 #if defined(_WIN32)
15  // Windows
16  #include <io.h>
17 #else
18  // Linux/Unix
19  #include <dirent.h>
20  #if !defined(EVO_USE_READDIR_R)
21  #if (defined(__GNUC__) && __GNUC__ >= 6) || (defined(__clang_major__) && __clang_major__ >= 4)
22  #define EVO_USE_READDIR_R 0 // Use thread-safe readdir()
23  #else
24  #define EVO_USE_READDIR_R 1 // Use thread-safe variation readdir_r() up until deprecated by glibc 2.24+
25  #endif
26  #endif
27 #endif
28 
29 namespace evo {
32 
34 
40 struct SysDir {
41 #if defined(_WIN32)
42  // Windows
43  typedef intptr_t Handle;
44 
45  struct _finddata_t context;
46  char* filepath;
47  const char* firstfile;
48 
49  SysDir() {
50  handle = -1;
51  filepath = NULL;
52  firstfile = NULL;
53  }
54 
55  ~SysDir()
56  { close(); }
57 
58  Error open(const char* path) {
59  close();
60  if (path != NULL) { // NULL used by seek() to re-open same filepath
61  // Append wildcard to path, save in case of seek()
62  const size_t pathlen = strlen(path);
63  filepath = (char*)::malloc(pathlen+3);
64  memcpy(filepath, path, pathlen);
65  memcpy(filepath+pathlen, "\\*", 3);
66  }
67 
68  handle = _findfirst(filepath, &context);
69  Error err;
70  if (handle == -1) {
71  switch (errno) {
72  case ENOENT: err = ENotFound; break;
73  default: err = EFail; break;
74  }
75  firstfile = NULL;
76  } else {
77  err = ENone;
78  firstfile = context.name;
79  }
80  return err;
81  }
82 
83  void close() {
84  if (handle != -1) {
85  _findclose(handle);
86  handle = -1;
87  }
88  if (filepath != NULL) {
89  ::free(filepath);
90  filepath = NULL;
91  }
92  firstfile = NULL;
93  }
94 
95  void seek() {
96  if (handle != -1) {
97  _findclose(handle);
98  handle = -1;
99  }
100  firstfile = NULL;
101  open(NULL);
102  }
103 
104  template<class TStr>
105  bool read(TStr& entry) {
106  if (handle != -1) {
107  if (firstfile != NULL) {
108  entry.set(firstfile);
109  firstfile = NULL;
110  return true;
111  } else {
112  for (;;) {
113  if (_findnext(handle, &context) != 0) {
114  close();
115  break;
116  }
117  if (context.name[0] == '.' &&
118  (context.name[1] == '\0' ||
119  (context.name[1] == '.' && context.name[2] == '\0')
120  )
121  )
122  continue; // skip current/parent dir
123  entry.set(context.name);
124  return true;
125  }
126  }
127  }
128  entry.set();
129  return false;
130  }
131 
132 #else
133  // Linux/Unix
134  typedef DIR* Handle;
135 
137  SysDir() {
138  #if EVO_USE_READDIR_R
139  buffer = NULL;
140  #endif
141  handle = NULL;
142  }
143 
146  { close(); }
147 
152  Error open(const char* path) {
153  close();
154  handle = ::opendir(path);
155  Error err;
156  if (handle == NULL) {
157  switch (errno) {
158  case EACCES: err = EAccess; break;
159  case ENOTDIR: // fallthrough
160  case ENOENT: err = ENotFound; break;
161  default: err = EFail; break;
162  }
163  } else {
164  err = ENone;
165  #if EVO_USE_READDIR_R
166  // See readdir_r() manpage for size calculation
167  buffer = (char*)::malloc(offsetof(struct dirent, d_name) + pathconf(path, _PC_NAME_MAX) + 1);
168  #endif
169  }
170  return err;
171  }
172 
174  void close() {
175  #if EVO_USE_READDIR_R
176  if (buffer != NULL) {
177  ::free(buffer);
178  buffer = NULL;
179  }
180  #endif
181  if (handle) {
182  ::closedir(handle);
183  handle = NULL;
184  }
185  }
186 
190  void seek()
191  { if (handle) ::rewinddir(handle); }
192 
201  template<class TStr>
202  bool read(TStr& entry) {
203  if (handle) {
204  #if EVO_USE_READDIR_R
205  assert( buffer != NULL );
206  struct dirent* result = NULL;
207  if (::readdir_r(handle, (struct dirent*)buffer, &result) == 0 && result != NULL) {
208  entry = result->d_name;
209  return true;
210  }
211  #else
212  struct dirent* result = ::readdir(handle);
213  if (result != NULL) {
214  entry = result->d_name;
215  return true;
216  }
217  #endif
218  }
219  entry.set();
220  return false;
221  }
222 
226  bool chdir() {
227  if (handle != NULL) {
228  const int fd = ::dirfd(handle);
229  return (fd >= 0 && ::fchdir(fd) == 0);
230  }
231  return false;
232  }
233 
234 private:
235 #if EVO_USE_READDIR_R
236  char* buffer;
237 #endif
238 
239 public:
240 #endif
241 
242  Handle handle;
243 };
244 
246 
247 }
248 #endif
Permission denied.
Definition: sys.h:1135
Error open(const char *path)
Open directory for reading.
Definition: sysio_dir.h:152
Evo implementation detail for system portability – this is included by most Evo headers, include this via: include <evo/type.h>.
void seek()
Seek to beginning of directory.
Definition: sysio_dir.h:190
DIR * Handle
System directory handle.
Definition: sysio_dir.h:134
Evo I/O streams and Console I/O.
System directory reader (used internally).
Definition: sysio_dir.h:40
Resource not found.
Definition: sys.h:1137
No error.
Definition: sys.h:1115
Error
General Evo error code stored in exceptions, or used directly when exceptions are disabled...
Definition: sys.h:1113
bool read(TStr &entry)
Read next directory entry.
Definition: sysio_dir.h:202
SysDir()
Constructor.
Definition: sysio_dir.h:137
Evo C++ Library namespace.
Definition: alg.h:11
void close()
Close currently open directory, if any.
Definition: sysio_dir.h:174
bool chdir()
Change current working directory to currently open directory.
Definition: sysio_dir.h:226
Operation failed.
Definition: sys.h:1124
Handle handle
System directory handle.
Definition: sysio_dir.h:242
~SysDir()
Destructor.
Definition: sysio_dir.h:145