Qore Programming Language  1.12.0
qore_qd_private.h
1 /* -*- mode: c++; indent-tabs-mode: nil -*- */
2 /*
3  qore_qd_private.h
4 
5  Qore Programming Language
6 
7  Copyright (C) 2003 - 2022 Qore Technologies, s.r.o.
8 
9  Permission is hereby granted, free of charge, to any person obtaining a
10  copy of this software and associated documentation files (the "Software"),
11  to deal in the Software without restriction, including without limitation
12  the rights to use, copy, modify, merge, publish, distribute, sublicense,
13  and/or sell copies of the Software, and to permit persons to whom the
14  Software is furnished to do so, subject to the following conditions:
15 
16  The above copyright notice and this permission notice shall be included in
17  all copies or substantial portions of the Software.
18 
19  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25  DEALINGS IN THE SOFTWARE.
26 
27  Note that the Qore library is released under a choice of three open-source
28  licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more
29  information.
30 */
31 
32 #ifndef _QORE_QORE_QD_PRIVATE_H
33 
34 #define _QORE_QORE_QD_PRIVATE_H
35 
36 #include <cerrno>
37 #include <cstdio>
38 #include <cstdlib>
39 #include <cstring>
40 #include <dirent.h>
41 #include <string>
42 #include <sys/file.h>
43 #include <sys/stat.h>
44 #include <sys/types.h>
45 #include <unistd.h>
46 
47 #ifdef _Q_WINDOWS
48 static int mkdir(const char* path, mode_t mode) {
49  return mkdir(path);
50 }
51 #endif
52 
53 class qore_qd_private {
54 protected:
55  const QoreEncoding* enc;
56  std::string dirname;
57  mutable QoreThreadLock m;
58 
59  DLLLOCAL static bool is_dir_sep(const std::string& str) {
60  for (std::string::size_type i = 0, e = str.size(); i < e; ++i) {
61 #ifdef _Q_WINDOWS
62  if (str[i] != '\\' && str[i] != '/')
63  return false;
64 #else
65  if (str[i] != QORE_DIR_SEP)
66  return false;
67 #endif
68  }
69  return true;
70  }
71 
72  DLLLOCAL static std::string::size_type get_first_non_dir_sep(const std::string& str, std::string::size_type pos = 0) {
73  for (std::string::size_type i = pos, e = str.size(); i < e; ++i) {
74 #ifdef _Q_WINDOWS
75  if (str[i] != '\\' && str[i] != '/')
76  return i;
77 #else
78  if (str[i] != QORE_DIR_SEP)
79  return i;
80 #endif
81  }
82  return std::string::npos;
83  }
84 
85  DLLLOCAL static std::string::size_type get_first_dir_sep(const std::string& str, std::string::size_type pos = 0) {
86  for (std::string::size_type i = pos, e = str.size(); i < e; ++i) {
87 #ifdef _Q_WINDOWS
88  if (str[i] == '\\' || str[i] == '/')
89  return i;
90 #else
91  if (str[i] == QORE_DIR_SEP)
92  return i;
93 #endif
94  }
95  return std::string::npos;
96  }
97 
98  // tokenize the strings by directory separators
99  DLLLOCAL static void tokenize(const std::string& str, name_vec_t& tokens) {
100  // accommodate case when the string consists of only the delimiter (ex: "/")
101  if (is_dir_sep(str)) {
102  tokens.push_back(QORE_DIR_SEP_STR);
103  return;
104  }
105 
106  // Skip delimiters at beginning.
107  std::string::size_type lastPos = get_first_non_dir_sep(str, 0);
108  // Find first "non-delimiter".
109  std::string::size_type pos = get_first_dir_sep(str, lastPos);
110 
111  while (std::string::npos != pos || std::string::npos != lastPos) {
112  // Found a token, add it to the vector.
113  tokens.push_back(str.substr(lastPos, pos - lastPos));
114  // Skip delimiters. Note the "not_of"
115  lastPos = get_first_non_dir_sep(str, pos);
116  // Find next "non-delimiter"
117  pos = get_first_dir_sep(str, lastPos);
118  }
119  }
120 
121  // check if the given directory is accessible
122  // return errno of opendir function
123  // unlocked
124  DLLLOCAL static int verifyDirectory(const std::string &dir) {
125  DIR* dptr;
126  dptr = opendir(dir.c_str());
127  if (!dptr)
128  return errno;
129 
130  // free again
131  closedir(dptr);
132  return 0;
133  }
134 
135  // unlocked
136  DLLLOCAL std::string getPathIntern(const char* sub) const {
137  if (!dirname.empty())
138  return dirname + QORE_DIR_SEP_STR + std::string(sub);
139  return std::string(sub);
140  }
141 
142  // check if the path in dirname exists
143  // return 0 if the path exists
144  // return errno of the opendir function
145  // unlocked
146  DLLLOCAL int checkPathIntern() const {
147  return dirname.empty() ? -1 : verifyDirectory(dirname);
148  }
149 
150 public:
151  DLLLOCAL qore_qd_private(ExceptionSink* xsink, const QoreEncoding* cs, const char* dir) : enc(cs) {
152  if (dir) {
153  dirname = dir;
154  return;
155  }
156 
157  // set the directory to the cwd
158  char* cwd = (char*)malloc(sizeof(char)*QORE_PATH_MAX);
159  if (!cwd) { // error in malloc
160  xsink->outOfMemory();
161  return;
162  }
163  ON_BLOCK_EXIT(free, cwd);
164 
165  if (getcwd(cwd, (size_t)QORE_PATH_MAX))
166  dirname = cwd;
167  }
168 
169  DLLLOCAL qore_qd_private(ExceptionSink* xsink, const qore_qd_private &old) {
170  AutoLocker al(old.m);
171  enc = old.enc;
172  dirname = old.dirname;
173  }
174 
175  DLLLOCAL QoreStringNode* get_dir_string() const {
176  AutoLocker al(m);
177  return !dirname.empty() ? new QoreStringNode(dirname, enc) : 0;
178  }
179 
180  DLLLOCAL std::string getPath(const char* sub) const {
181  AutoLocker al(m);
182 
183  return getPathIntern(sub);
184  }
185 
186  DLLLOCAL int checkPath() const {
187  AutoLocker al(m);
188 
189  return checkPathIntern();
190  }
191 
192  // returns 0 = OK, > 0 = dir does not exist, -1 = error
193  DLLLOCAL int chdir(const char* ndir, ExceptionSink* xsink = nullptr) {
194  assert(ndir);
195 
196  //printd(5, "qore_qd_private::chdir() ndir: '%s' dirname: '%s'\n", ndir, dirname.c_str());
197 
198  // if changing to the current directory, then ignore
199  if (ndir[0] == '.') {
200  const char* p = ndir + 1;
201 #ifdef _Q_WINDOWS
202  while (*p && (*p == '\\' || *p == '/'))
203 #else
204  while (*p && *p == QORE_DIR_SEP)
205 #endif
206  ++p;
207  if (!*p)
208  return 0;
209  }
210 
211  // if relative path then join with the old path and strip the path
212  std::string ds;
213 
214  AutoLocker al(m);
215  if (!q_absolute_path(ndir)) {
216  if (dirname.empty()) {
217  if (xsink) {
218  xsink->raiseException("DIR-CHDIR-ERROR", "cannot change to relative directory because no current directory is set");
219  }
220  return -1;
221  }
222 
223  ds = dirname + QORE_DIR_SEP + std::string(ndir);
224  } else
225  ds = ndir;
226 
227  ds = normalizePath(ds);
228  dirname = ds;
229 
230  //printd(5, "qore_qd_private::chdir() ndir: '%s' ds: '%s'\n", ndir, ds.c_str());
231 
232  return checkPathIntern();
233  }
234 
235  DLLLOCAL int mkdir(const char* subdir, int mode, ExceptionSink* xsink) const {
236  assert(subdir);
237  AutoLocker al(m);
238 
239  std::string path = getPathIntern(subdir);
240  if (::mkdir(path.c_str(), mode)) {
241  xsink->raiseErrnoException("DIR-MKDIR-FAILURE", errno, "error creating directory '%s'", path.c_str());
242  return -1;
243  }
244  return 0;
245  }
246 
247  DLLLOCAL int rmdir(const char* subdir, ExceptionSink* xsink) const {
248  assert(subdir);
249  AutoLocker al(m);
250 
251  std::string path = getPathIntern(subdir);
252  if (::rmdir(path.c_str())) {
253  xsink->raiseErrnoException("DIR-RMDIR-FAILURE", errno, "error removing directory '%s'", path.c_str());
254  return -1;
255  }
256 
257  return 0;
258  }
259 
260  DLLLOCAL QoreListNode* list(ExceptionSink* xsink, int stat_filter, const QoreString* regex, int regex_options, bool full) const;
261 
262  DLLLOCAL int create(int mode, ExceptionSink* xsink = nullptr) const {
263  AutoLocker al(m);
264 
265  if (dirname.empty()) {
266  if (xsink) {
267  xsink->raiseException("DIR-CREATE-ERROR", "cannot create directory; no directory is set");
268  }
269  return -1;
270  }
271 
272 #ifdef _Q_WINDOWS
273  // flag UNC paths for special processing, because the first two components of UNC paths designate the server location and cannot be created with mkdir()
274  bool unc = (dirname[0] == '/' || dirname[0] == '\\')
275  && (dirname[1] == '/' || dirname[1] == '\\')
276  && (dirname[2] != '/' && dirname[2] != '\\');
277 #endif
278 
279  // split the directory in its subdirectories tree
280  name_vec_t dirs;
281  tokenize(dirname, dirs);
282 
283  // iterate through all directories and try to create them if
284  // they do not exist (should happen only on the first level)
285  name_vec_t::iterator it;
286  std::string path;
287  int cnt = 0;
288 
289 #ifdef _Q_WINDOWS
290  // issue #2529: we have to use q_absolute_path_windows() here and not in the loop
291  bool abs = q_absolute_path_windows(dirname.c_str());
292 #endif
293 
294  for (it = dirs.begin(); it < dirs.end(); it++) {
295 #ifdef _Q_WINDOWS
296  if (it == dirs.begin() && abs)
297  path += *it;
298  else
299 #endif
300  path += QORE_DIR_SEP_STR + (*it); // the actual path
301 #ifdef _Q_WINDOWS
302  // ignore the first two components of UNC paths
303  if (unc && cnt < 2) {
304  ++cnt;
305  continue;
306  }
307 #endif
308  if (verifyDirectory(path)) { // not existing
309  if (::mkdir(path.c_str(), mode)) { // failed
310  if (xsink) {
311  xsink->raiseErrnoException("DIR-CREATE-FAILURE", errno, "cannot mkdir '%s'", path.c_str());
312  }
313  return -1;
314  }
315  cnt++;
316  }
317  }
318 
319  return cnt;
320  }
321 
322  DLLLOCAL int chmod(int mode, ExceptionSink* xsink) const {
323  AutoLocker al(m);
324 
325  if (dirname.empty()) {
326  xsink->raiseException("DIR-CHMOD-ERROR", "cannot change directory mode; no directory is set");
327  return -1;
328  }
329 
330  if (::chmod(dirname.c_str(), mode)) {
331  xsink->raiseErrnoException("DIR-CHMOD-FAILURE", errno, "error in Dir::chmod() on '%s'", dirname.c_str());
332  return -1;
333  }
334 
335  return 0;
336  }
337 
338 #ifdef HAVE_PWD_H
339  DLLLOCAL int chown(uid_t uid, gid_t gid, ExceptionSink* xsink) const {
340  AutoLocker al(m);
341 
342  if (dirname.empty()) {
343  xsink->raiseException("DIR-CHOWN-ERROR", "cannot change directory ownership; no directory is set");
344  return -1;
345  }
346 
347  if (::chown(dirname.c_str(), uid, gid)) {
348  xsink->raiseErrnoException("DIR-CHOWN-FAILURE", errno, "error in Dir::chown() on '%s'", dirname.c_str());
349  return 0;
350  }
351 
352  return 0;
353  }
354 #endif
355 
356  DLLLOCAL QoreListNode* stat(ExceptionSink* xsink) const {
357  AutoLocker al(m);
358 
359  if (dirname.empty()) {
360  xsink->raiseException("DIR-STAT-ERROR", "cannot stat; no directory is set");
361  return 0;
362  }
363 
364  hashdecl stat sbuf;
365  if (::stat(dirname.c_str(), &sbuf)) {
366  xsink->raiseErrnoException("DIR-STAT-FAILURE", errno, "stat() call failed on '%s'", dirname.c_str());
367  return 0;
368  }
369 
370  return stat_to_list(sbuf);
371  }
372 
373  DLLLOCAL QoreHashNode* hstat(ExceptionSink* xsink) const;
374 
375 #ifdef Q_HAVE_STATVFS
376  DLLLOCAL QoreHashNode* statvfs(ExceptionSink* xsink) const {
377  AutoLocker al(m);
378 
379  if (dirname.empty()) {
380  xsink->raiseException("DIR-STATVFS-ERROR", "cannot execute File::statvfs(); no directory is set");
381  return nullptr;
382  }
383 
384  hashdecl statvfs vfs;
385  if (::statvfs(dirname.c_str(), &vfs)) {
386  xsink->raiseErrnoException("DIR-STATVFS-FAILURE", errno, "statvfs() call failed on '%s'", dirname.c_str());
387  return nullptr;
388  }
389 
390  return statvfs_to_hash(vfs);
391  }
392 #endif
393 
394  const QoreEncoding* getEncoding() const {
395  return enc;
396  }
397 
398 #ifdef HAVE_LSTAT
399  DLLLOCAL static int lstat(const char* str, struct stat& buf, ExceptionSink* xsink) {
400  int rc = ::lstat(str, &buf);
401  if (rc) {
402  xsink->raiseErrnoException("DIR-READ-FAILURE", errno, "lstat() failed on '%s'", str);
403  return -1;
404  }
405  return 0;
406  }
407 #endif
408 
409  DLLLOCAL static int stat(const char* str, struct stat& buf, ExceptionSink* xsink) {
410  int rc = ::stat(str, &buf);
411  if (rc) {
412  xsink->raiseErrnoException("DIR-READ-FAILURE", errno, "stat() failed on '%s'", str);
413  return -1;
414  }
415  return 0;
416  }
417 
418  // tokenizes the string (path) and recreates it
419  DLLLOCAL static const std::string normalizePath(const std::string& odir) {
420 #ifdef _Q_WINDOWS
421  // flag UNC paths for special processing, because otherwise they will be normalized to a single leading backslash
422  bool unc = (odir[0] == '/' || odir[0] == '\\')
423  && (odir[1] == '/' || odir[1] == '\\')
424  && (odir[2] != '/' && odir[2] != '\\');
425 #endif
426 
427  // tokenize the string
428  name_vec_t ptoken, dirs;
429  tokenize(odir, ptoken);
430 
431  // push them to the new path
432  for (name_vec_t::iterator it = ptoken.begin(), et = ptoken.end(); it != et; ++it) {
433  std::string d = *it;
434  if (d == "." || d == "") // ignore
435  continue;
436 
437  if (d == ".." && !dirs.empty()) // step back one step
438  dirs.pop_back();
439  else
440  dirs.push_back(d);
441  }
442 
443  // create string out of rest..
444  std::string ret;
445 #ifdef _Q_WINDOWS
446  if (unc)
447  ret += '\\';
448  // issue #2529: we have to use q_absolute_path_windows() here and not in the loop
449  bool abs = q_absolute_path_windows(odir.c_str());
450 #endif
451  for (name_vec_t::iterator it = dirs.begin(), et = dirs.end(); it != et; ++it) {
452 #ifdef _Q_WINDOWS
453  if (it == dirs.begin() && abs)
454  ret += *it;
455  else
456 #endif
457  ret += QORE_DIR_SEP_STR + (*it);
458  }
459 
460  //printd(5, "qore_qd_private::normalizePath() odir: '%s' ret: '%s' unc: %d\n", odir.c_str(), ret.c_str(), unc);
461  return ret;
462  }
463 };
464 
465 #endif
DLLEXPORT bool q_absolute_path_windows(const char *path)
returns true if the given string is an absolute path on Windows systems
DLLEXPORT bool q_absolute_path(const char *path)
returns true if the given string is an absolute path on the current platform
provides a safe and exception-safe way to hold locks in Qore, only to be used on the stack,...
Definition: QoreThreadLock.h:136
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:48
DLLEXPORT AbstractQoreNode * raiseErrnoException(const char *err, int en, const char *fmt,...)
appends a Qore-language exception to the list and appends the result of strerror(errno) to the descri...
DLLEXPORT void outOfMemory()
intended to be used to handle out of memory errors
DLLEXPORT AbstractQoreNode * raiseException(const char *err, const char *fmt,...)
appends a Qore-language exception to the list
defines string encoding functions in Qore
Definition: QoreEncoding.h:83
This is the hash or associative list container type in Qore, dynamically allocated only,...
Definition: QoreHashNode.h:50
This is the list container type in Qore, dynamically allocated only, reference counted.
Definition: QoreListNode.h:52
Qore's string type supported by the QoreEncoding class.
Definition: QoreString.h:93
Qore's string value type, reference counted, dynamically-allocated only.
Definition: QoreStringNode.h:50
provides a mutually-exclusive thread lock
Definition: QoreThreadLock.h:49
std::vector< std::string > name_vec_t
vector of parameter names for parameter lists
Definition: common.h:257