Qore Programming Language  0.9.4.6
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 - 2017 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 
133  return 0;
134  }
135 
136  // unlocked
137  DLLLOCAL std::string getPathIntern(const char *sub) const {
138  if (!dirname.empty())
139  return dirname + QORE_DIR_SEP_STR + std::string(sub);
140  return std::string(sub);
141  }
142 
143  // check if the path in dirname exists
144  // return 0 if the path exists
145  // return errno of the opendir function
146  // unlocked
147  DLLLOCAL int checkPathIntern() const {
148  return dirname.empty() ? -1 : verifyDirectory(dirname);
149  }
150 
151 public:
152  DLLLOCAL qore_qd_private(ExceptionSink *xsink, const QoreEncoding *cs, const char *dir) : enc(cs) {
153  if (dir) {
154  dirname = dir;
155  return;
156  }
157 
158  // set the directory to the cwd
159  char *cwd = (char*)malloc(sizeof(char)*QORE_PATH_MAX);
160  if (!cwd) { // error in malloc
161  xsink->outOfMemory();
162  return;
163  }
164  ON_BLOCK_EXIT(free, cwd);
165 
166  if (getcwd(cwd, (size_t)QORE_PATH_MAX))
167  dirname = cwd;
168  }
169 
170  DLLLOCAL qore_qd_private(ExceptionSink *xsink, const qore_qd_private &old) {
171  AutoLocker al(old.m);
172  enc = old.enc;
173  dirname = old.dirname;
174  }
175 
176  DLLLOCAL QoreStringNode *get_dir_string() const {
177  AutoLocker al(m);
178  return !dirname.empty() ? new QoreStringNode(dirname, enc) : 0;
179  }
180 
181  DLLLOCAL std::string getPath(const char *sub) const {
182  AutoLocker al(m);
183 
184  return getPathIntern(sub);
185  }
186 
187  DLLLOCAL int checkPath() const {
188  AutoLocker al(m);
189 
190  return checkPathIntern();
191  }
192 
193  DLLLOCAL int chdir(const char *ndir, ExceptionSink *xsink) {
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  xsink->raiseException("DIR-CHDIR-ERROR", "cannot change to relative directory because no current directory is set");
218  return -1;
219  }
220 
221  ds = dirname + QORE_DIR_SEP + std::string(ndir);
222  }
223  else
224  ds = ndir;
225 
226  ds = normalizePath(ds);
227  dirname = ds;
228 
229  //printd(5, "qore_qd_private::chdir() ndir: '%s' ds: '%s'\n", ndir, ds.c_str());
230 
231  return checkPathIntern();
232  }
233 
234  DLLLOCAL int mkdir(const char *subdir, int mode, ExceptionSink *xsink) const {
235  assert(subdir);
236  AutoLocker al(m);
237 
238  std::string path = getPathIntern(subdir);
239  if (::mkdir(path.c_str(), mode)) {
240  xsink->raiseErrnoException("DIR-MKDIR-FAILURE", errno, "error creating directory '%s'", path.c_str());
241  return -1;
242  }
243  return 0;
244  }
245 
246  DLLLOCAL int rmdir(const char *subdir, ExceptionSink *xsink) const {
247  assert(subdir);
248  AutoLocker al(m);
249 
250  std::string path = getPathIntern(subdir);
251  if (::rmdir(path.c_str())) {
252  xsink->raiseErrnoException("DIR-RMDIR-FAILURE", errno, "error removing directory '%s'", path.c_str());
253  return -1;
254  }
255 
256  return 0;
257  }
258 
259  DLLLOCAL QoreListNode *list(ExceptionSink *xsink, int stat_filter, const QoreString *regex, int regex_options, bool full) const;
260 
261  DLLLOCAL int create(int mode, ExceptionSink *xsink) const {
262  AutoLocker al(m);
263 
264  if (dirname.empty()) {
265  xsink->raiseException("DIR-CREATE-ERROR", "cannot create directory; no directory is set");
266  return -1;
267  }
268 
269 #ifdef _Q_WINDOWS
270  // flag UNC paths for special processing, because the first two components of UNC paths designate the server location and cannot be created with mkdir()
271  bool unc = (dirname[0] == '/' || dirname[0] == '\\')
272  && (dirname[1] == '/' || dirname[1] == '\\')
273  && (dirname[2] != '/' && dirname[2] != '\\');
274 #endif
275 
276  // split the directory in its subdirectories tree
277  name_vec_t dirs;
278  tokenize(dirname, dirs);
279 
280  // iterate through all directories and try to create them if
281  // they do not exist (should happen only on the first level)
282  name_vec_t::iterator it;
283  std::string path;
284  int cnt = 0;
285 
286 #ifdef _Q_WINDOWS
287  // issue #2529: we have to use q_absolute_path_windows() here and not in the loop
288  bool abs = q_absolute_path_windows(dirname.c_str());
289 #endif
290 
291  for (it = dirs.begin(); it < dirs.end(); it++) {
292 #ifdef _Q_WINDOWS
293  if (it == dirs.begin() && abs)
294  path += *it;
295  else
296 #endif
297  path += QORE_DIR_SEP_STR + (*it); // the actual path
298 #ifdef _Q_WINDOWS
299  // ignore the first two components of UNC paths
300  if (unc && cnt < 2) {
301  ++cnt;
302  continue;
303  }
304 #endif
305  if (verifyDirectory(path)) { // not existing
306  if (::mkdir(path.c_str(), mode)) { // failed
307  xsink->raiseErrnoException("DIR-CREATE-FAILURE", errno, "cannot mkdir '%s'", path.c_str());
308  return -1;
309  }
310  cnt++;
311  }
312  }
313 
314  return cnt;
315  }
316 
317  DLLLOCAL int chmod(int mode, ExceptionSink *xsink) const {
318  AutoLocker al(m);
319 
320  if (dirname.empty()) {
321  xsink->raiseException("DIR-CHMOD-ERROR", "cannot change directory mode; no directory is set");
322  return -1;
323  }
324 
325  if (::chmod(dirname.c_str(), mode)) {
326  xsink->raiseErrnoException("DIR-CHMOD-FAILURE", errno, "error in Dir::chmod() on '%s'", dirname.c_str());
327  return -1;
328  }
329 
330  return 0;
331  }
332 
333 #ifdef HAVE_PWD_H
334  DLLLOCAL int chown(uid_t uid, gid_t gid, ExceptionSink *xsink) const {
335  AutoLocker al(m);
336 
337  if (dirname.empty()) {
338  xsink->raiseException("DIR-CHOWN-ERROR", "cannot change directory ownership; no directory is set");
339  return -1;
340  }
341 
342  if (::chown(dirname.c_str(), uid, gid)) {
343  xsink->raiseErrnoException("DIR-CHOWN-FAILURE", errno, "error in Dir::chown() on '%s'", dirname.c_str());
344  return 0;
345  }
346 
347  return 0;
348  }
349 #endif
350 
351  DLLLOCAL QoreListNode *stat(ExceptionSink *xsink) const {
352  AutoLocker al(m);
353 
354  if (dirname.empty()) {
355  xsink->raiseException("DIR-STAT-ERROR", "cannot stat; no directory is set");
356  return 0;
357  }
358 
359  hashdecl stat sbuf;
360  if (::stat(dirname.c_str(), &sbuf)) {
361  xsink->raiseErrnoException("DIR-STAT-FAILURE", errno, "stat() call failed on '%s'", dirname.c_str());
362  return 0;
363  }
364 
365  return stat_to_list(sbuf);
366  }
367 
368  DLLLOCAL QoreHashNode *hstat(ExceptionSink *xsink) const;
369 
370 #ifdef Q_HAVE_STATVFS
371  DLLLOCAL QoreHashNode *statvfs(ExceptionSink *xsink) const {
372  AutoLocker al(m);
373 
374  if (dirname.empty()) {
375  xsink->raiseException("DIR-STATVFS-ERROR", "cannot execute File::statvfs(); no directory is set");
376  return 0;
377  }
378 
379  hashdecl statvfs vfs;
380  if (::statvfs(dirname.c_str(), &vfs)) {
381  xsink->raiseErrnoException("DIR-STATVFS-FAILURE", errno, "statvfs() call failed on '%s'", dirname.c_str());
382  return 0;
383  }
384 
385  return statvfs_to_hash(vfs);
386  }
387 #endif
388 
389  const QoreEncoding *getEncoding() const {
390  return enc;
391  }
392 
393 #ifdef HAVE_LSTAT
394  DLLLOCAL static int lstat(const char* str, struct stat& buf, ExceptionSink *xsink) {
395  int rc = ::lstat(str, &buf);
396  if (rc) {
397  xsink->raiseErrnoException("DIR-READ-FAILURE", errno, "lstat() failed on '%s'", str);
398  return -1;
399  }
400  return 0;
401  }
402 #endif
403 
404  DLLLOCAL static int stat(const char* str, struct stat& buf, ExceptionSink *xsink) {
405  int rc = ::stat(str, &buf);
406  if (rc) {
407  xsink->raiseErrnoException("DIR-READ-FAILURE", errno, "stat() failed on '%s'", str);
408  return -1;
409  }
410  return 0;
411  }
412 
413  // tokenizes the string (path) and recreates it
414  DLLLOCAL static const std::string normalizePath(const std::string& odir) {
415 #ifdef _Q_WINDOWS
416  // flag UNC paths for special processing, because otherwise they will be normalized to a single leading backslash
417  bool unc = (odir[0] == '/' || odir[0] == '\\')
418  && (odir[1] == '/' || odir[1] == '\\')
419  && (odir[2] != '/' && odir[2] != '\\');
420 #endif
421 
422  // tokenize the string
423  name_vec_t ptoken, dirs;
424  tokenize(odir, ptoken);
425 
426  // push them to the new path
427  for (name_vec_t::iterator it = ptoken.begin(), et = ptoken.end(); it != et; ++it) {
428  std::string d = *it;
429  if (d == "." || d == "") // ignore
430  continue;
431 
432  if (d == ".." && !dirs.empty()) // step back one step
433  dirs.pop_back();
434  else
435  dirs.push_back(d);
436  }
437 
438  // create string out of rest..
439  std::string ret;
440 #ifdef _Q_WINDOWS
441  if (unc)
442  ret += '\\';
443  // issue #2529: we have to use q_absolute_path_windows() here and not in the loop
444  bool abs = q_absolute_path_windows(odir.c_str());
445 #endif
446  for (name_vec_t::iterator it = dirs.begin(), et = dirs.end(); it != et; ++it) {
447 #ifdef _Q_WINDOWS
448  if (it == dirs.begin() && abs)
449  ret += *it;
450  else
451 #endif
452  ret += QORE_DIR_SEP_STR + (*it);
453  }
454 
455  //printd(5, "qore_qd_private::normalizePath() odir: '%s' ret: '%s' unc: %d\n", odir.c_str(), ret.c_str(), unc);
456  return ret;
457  }
458 };
459 
460 #endif
DLLEXPORT void outOfMemory()
intended to be used to handle out of memory errors FIXME: not yet fully implemented ...
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
DLLEXPORT AbstractQoreNode * raiseException(const char *err, const char *fmt,...)
appends a Qore-language exception to the list
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...
Qore&#39;s string type supported by the QoreEncoding class.
Definition: QoreString.h:81
Qore&#39;s string value type, reference counted, dynamically-allocated only.
Definition: QoreStringNode.h:50
provides a safe and exception-safe way to hold locks in Qore, only to be used on the stack...
Definition: QoreThreadLock.h:128
This is the list container type in Qore, dynamically allocated only, reference counted.
Definition: QoreListNode.h:52
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:46
std::vector< std::string > name_vec_t
vector of parameter names for parameter lists
Definition: common.h:257
DLLEXPORT bool q_absolute_path_windows(const char *path)
returns true if the given string is an absolute path on Windows systems
provides a mutually-exclusive thread lock
Definition: QoreThreadLock.h:47
DLLEXPORT bool q_absolute_path(const char *path)
returns true if the given string is an absolute path on the current platform