Qore Programming Language 1.19.1
Loading...
Searching...
No Matches
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 - 2023 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
48static int mkdir(const char* path, mode_t mode) {
49 return mkdir(path);
50}
51#endif
52
53class qore_qd_private {
54protected:
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
150public:
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:50
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
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...
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