123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621 |
- #include "dirut.h"
- #include "iterator.h"
- #include "filelist.h"
- #include "fts.h"
- #include "pathsplit.h"
- #include "path.h"
- #include <util/generic/yexception.h>
- #include <util/system/compiler.h>
- #include <util/system/fs.h>
- #include <util/system/maxlen.h>
- #include <util/system/yassert.h>
- void SlashFolderLocal(TString& folder) {
- if (!folder)
- return;
- #ifdef _win32_
- size_t pos;
- while ((pos = folder.find('/')) != TString::npos)
- folder.replace(pos, 1, LOCSLASH_S);
- #endif
- if (folder[folder.size() - 1] != LOCSLASH_C)
- folder.append(LOCSLASH_S);
- }
- #ifndef _win32_
- bool correctpath(TString& folder) {
- return resolvepath(folder, "/");
- }
- bool resolvepath(TString& folder, const TString& home) {
- Y_ASSERT(home && home.at(0) == '/');
- if (!folder) {
- return false;
- }
- // may be from windows
- char* ptr = folder.begin();
- while ((ptr = strchr(ptr, '\\')) != nullptr)
- *ptr = '/';
- if (folder.at(0) == '~') {
- if (folder.length() == 1 || folder.at(1) == '/') {
- folder = GetHomeDir() + (folder.data() + 1);
- } else {
- char* buf = (char*)alloca(folder.length() + 1);
- strcpy(buf, folder.data() + 1);
- char* p = strchr(buf, '/');
- if (p)
- *p++ = 0;
- passwd* pw = getpwnam(buf);
- if (pw) {
- folder = pw->pw_dir;
- folder += "/";
- if (p)
- folder += p;
- } else {
- return false; // unknown user
- }
- }
- }
- int len = folder.length() + home.length() + 1;
- char* path = (char*)alloca(len);
- if (folder.at(0) != '/') {
- strcpy(path, home.data());
- strcpy(strrchr(path, '/') + 1, folder.data()); // the last char must be '/' if it's a dir
- } else {
- strcpy(path, folder.data());
- }
- len = strlen(path) + 1;
- // grabbed from url.cpp
- char* newpath = (char*)alloca(len + 2);
- const char** pp = (const char**)alloca(len * sizeof(char*));
- int i = 0;
- for (char* s = path; s;) {
- pp[i++] = s;
- s = strchr(s, '/');
- if (s)
- *s++ = 0;
- }
- for (int j = 1; j < i;) {
- const char*& p = pp[j];
- if (strcmp(p, ".") == 0 || strcmp(p, "") == 0) {
- if (j == i - 1) {
- p = "";
- break;
- } else {
- memmove(pp + j, pp + j + 1, (i - j - 1) * sizeof(p));
- --i;
- }
- } else if (strcmp(p, "..") == 0) {
- if (j == i - 1) {
- if (j == 1) {
- p = "";
- } else {
- --i;
- pp[j - 1] = "";
- }
- break;
- } else {
- if (j == 1) {
- memmove(pp + j, pp + j + 1, (i - j - 1) * sizeof(p));
- --i;
- } else {
- memmove(pp + j - 1, pp + j + 1, (i - j - 1) * sizeof(p));
- i -= 2;
- --j;
- }
- }
- } else
- ++j;
- }
- char* s = newpath;
- for (int k = 0; k < i; k++) {
- s = strchr(strcpy(s, pp[k]), 0);
- *s++ = '/';
- }
- *(--s) = 0;
- folder = newpath;
- return true;
- }
- #else
- using dir_type = enum {
- dt_empty,
- dt_error,
- dt_up,
- dt_dir
- };
- // precondition: *ptr != '\\' || *ptr == 0 (cause dt_error)
- // postcondition: *ptr != '\\'
- template <typename T>
- static int next_dir(T*& ptr) {
- int has_blank = 0;
- int has_dot = 0;
- int has_letter = 0;
- int has_ctrl = 0;
- while (*ptr && *ptr != '\\') {
- int c = (unsigned char)*ptr++;
- switch (c) {
- case ' ':
- ++has_blank;
- break;
- case '.':
- ++has_dot;
- break;
- case '/':
- case ':':
- case '*':
- case '?':
- case '"':
- case '<':
- case '>':
- case '|':
- ++has_ctrl;
- break;
- default:
- if (c == 127 || c < ' ')
- ++has_ctrl;
- else
- ++has_letter;
- }
- }
- if (*ptr)
- ++ptr;
- if (has_ctrl)
- return dt_error;
- if (has_letter)
- return dt_dir;
- if (has_dot && has_blank)
- return dt_error;
- if (has_dot == 1)
- return dt_empty;
- if (has_dot == 2)
- return dt_up;
- return dt_error;
- }
- using disk_type = enum {
- dk_noflags = 0,
- dk_unc = 1,
- dk_hasdrive = 2,
- dk_fromroot = 4,
- dk_error = 8
- };
- // root slash (if any) - part of disk
- template <typename T>
- static int skip_disk(T*& ptr) {
- int result = dk_noflags;
- if (!*ptr)
- return result;
- if (ptr[0] == '\\' && ptr[1] == '\\') {
- result |= dk_unc | dk_fromroot;
- ptr += 2;
- if (next_dir(ptr) != dt_dir)
- return dk_error; // has no host name
- if (next_dir(ptr) != dt_dir)
- return dk_error; // has no share name
- } else {
- if (*ptr && *(ptr + 1) == ':') {
- result |= dk_hasdrive;
- ptr += 2;
- }
- if (*ptr == '\\' || *ptr == '/') {
- ++ptr;
- result |= dk_fromroot;
- }
- }
- return result;
- }
- int correctpath(char* cpath, const char* path) {
- if (!path || !*path) {
- *cpath = 0;
- return 1;
- }
- char* ptr = (char*)path;
- char* cptr = cpath;
- int counter = 0;
- while (*ptr) {
- char c = *ptr;
- if (c == '/')
- c = '\\';
- if (c == '\\')
- ++counter;
- else
- counter = 0;
- if (counter <= 1) {
- *cptr = c;
- ++cptr;
- }
- ++ptr;
- }
- *cptr = 0;
- // replace '/' by '\'
- int dk = skip_disk(cpath);
- if (dk == dk_error)
- return 0;
- char* ptr1 = ptr = cpath;
- int level = 0;
- while (*ptr) {
- switch (next_dir(ptr)) {
- case dt_dir:
- ++level;
- break;
- case dt_empty:
- memmove(ptr1, ptr, strlen(ptr) + 1);
- ptr = ptr1;
- break;
- case dt_up:
- --level;
- if (level >= 0) {
- *--ptr1 = 0;
- ptr1 = strrchr(cpath, '\\');
- if (!ptr1)
- ptr1 = cpath;
- else
- ++ptr1;
- memmove(ptr1, ptr, strlen(ptr) + 1);
- ptr = ptr1;
- break;
- } else if (level == -1 && (dk & dk_hasdrive)) {
- if (*ptr && *(ptr + 1) == ':' && *(cpath - 2) == ':') {
- memmove(cpath - 3, ptr, strlen(ptr) + 1);
- return 1;
- }
- }
- if (dk & dk_fromroot)
- return 0;
- break;
- case dt_error:
- default:
- return 0;
- }
- ptr1 = ptr;
- }
- if ((ptr > cpath || ptr == cpath && dk & dk_unc) && *(ptr - 1) == '\\')
- *(ptr - 1) = 0;
- return 1;
- }
- static inline int normchar(unsigned char c) {
- return (c < 'a' || c > 'z') ? c : c - 32;
- }
- static inline char* strslashcat(char* a, const char* b) {
- size_t len = strlen(a);
- if (len && a[len - 1] != '\\')
- a[len++] = '\\';
- strcpy(a + len, b);
- return a;
- }
- int resolvepath(char* apath, const char* rpath, const char* cpath) {
- const char* redisk = rpath;
- if (!rpath || !*rpath)
- return 0;
- int rdt = skip_disk(redisk);
- if (rdt == dk_error)
- return 0;
- if (rdt & dk_unc || rdt & dk_hasdrive && rdt & dk_fromroot) {
- return correctpath(apath, rpath);
- }
- const char* cedisk = cpath;
- if (!cpath || !*cpath)
- return 0;
- int cdt = skip_disk(cedisk);
- if (cdt == dk_error)
- return 0;
- char* tpath = (char*)alloca(strlen(rpath) + strlen(cpath) + 3);
- // rdt&dk_hasdrive && !rdt&dk_fromroot
- if (rdt & dk_hasdrive) {
- if (!(cdt & dk_fromroot))
- return 0;
- if (cdt & dk_hasdrive && normchar(*rpath) != normchar(*cpath))
- return 0;
- memcpy(tpath, rpath, 2);
- memcpy(tpath + 2, cedisk, strlen(cedisk) + 1);
- strslashcat(tpath, redisk);
- // !rdt&dk_hasdrive && rdt&dk_fromroot
- } else if (rdt & dk_fromroot) {
- if (!(cdt & dk_hasdrive) && !(cdt & dk_unc))
- return 0;
- memcpy(tpath, cpath, cedisk - cpath);
- tpath[cedisk - cpath] = 0;
- strslashcat(tpath, redisk);
- // !rdt&dk_hasdrive && !rdt&dk_fromroot
- } else {
- if (!(cdt & dk_fromroot) || !(cdt & dk_hasdrive) && !(cdt & dk_unc))
- return 0;
- strslashcat(strcpy(tpath, cpath), redisk);
- }
- return correctpath(apath, tpath);
- }
- bool correctpath(TString& filename) {
- char* ptr = (char*)alloca(filename.size() + 2);
- if (correctpath(ptr, filename.data())) {
- filename = ptr;
- return true;
- }
- return false;
- }
- bool resolvepath(TString& folder, const TString& home) {
- char* ptr = (char*)alloca(folder.size() + 3 + home.size());
- if (resolvepath(ptr, folder.data(), home.data())) {
- folder = ptr;
- return true;
- }
- return false;
- }
- #endif // !defined _win32_
- char GetDirectorySeparator() {
- return LOCSLASH_C;
- }
- const char* GetDirectorySeparatorS() {
- return LOCSLASH_S;
- }
- void RemoveDirWithContents(TString dirName) {
- SlashFolderLocal(dirName);
- TDirIterator dir(dirName, TDirIterator::TOptions(FTS_NOSTAT));
- for (auto it = dir.begin(); it != dir.end(); ++it) {
- switch (it->fts_info) {
- case FTS_F:
- case FTS_DEFAULT:
- case FTS_DP:
- case FTS_SL:
- case FTS_SLNONE:
- if (!NFs::Remove(it->fts_path))
- ythrow TSystemError() << "error while removing " << it->fts_path;
- break;
- }
- }
- }
- int mkpath(char* path, int mode) {
- return NFs::MakeDirectoryRecursive(path, NFs::EFilePermission(mode)) ? 0 : -1;
- }
- // Implementation of realpath in FreeBSD (version 9.0 and less) and GetFullPathName in Windows
- // did not require last component of the file name to exist (other implementations will fail
- // if it does not). Use RealLocation if that behaviour is required.
- TString RealPath(const TString& path) {
- TTempBuf result;
- Y_ASSERT(result.Size() > MAX_PATH); //TMP_BUF_LEN > MAX_PATH
- #ifdef _win_
- if (GetFullPathName(path.data(), result.Size(), result.Data(), nullptr) == 0)
- #else
- if (realpath(path.data(), result.Data()) == nullptr)
- #endif
- ythrow TFileError() << "RealPath failed \"" << path << "\"";
- return result.Data();
- }
- TString RealLocation(const TString& path) {
- if (NFs::Exists(path))
- return RealPath(path);
- TString dirpath = GetDirName(path);
- if (NFs::Exists(dirpath))
- return RealPath(dirpath) + GetDirectorySeparatorS() + GetFileNameComponent(path.data());
- ythrow TFileError() << "RealLocation failed \"" << path << "\"";
- }
- int MakeTempDir(char path[/*FILENAME_MAX*/], const char* prefix) {
- int ret;
- TString sysTmp;
- #ifdef _win32_
- if (!prefix || *prefix == '/') {
- #else
- if (!prefix) {
- #endif
- sysTmp = GetSystemTempDir();
- prefix = sysTmp.data();
- }
- if ((ret = ResolvePath(prefix, nullptr, path, 1)) != 0)
- return ret;
- if (!TFileStat(path).IsDir())
- return ENOENT;
- if ((strlcat(path, "tmpXXXXXX", FILENAME_MAX) > FILENAME_MAX - 100))
- return EINVAL;
- if (!(mkdtemp(path)))
- return errno ? errno : EINVAL;
- strcat(path, LOCSLASH_S);
- return 0;
- }
- bool IsDir(const TString& path) {
- return TFileStat(path).IsDir();
- }
- TString GetHomeDir() {
- TString s(getenv("HOME"));
- if (!s) {
- #ifndef _win32_
- passwd* pw = nullptr;
- s = getenv("USER");
- if (s)
- pw = getpwnam(s.data());
- else
- pw = getpwuid(getuid());
- if (pw)
- s = pw->pw_dir;
- else
- #endif
- {
- char* cur_dir = getcwd(nullptr, 0);
- s = cur_dir;
- free(cur_dir);
- }
- }
- return s;
- }
- void MakeDirIfNotExist(const char* path, int mode) {
- if (!NFs::MakeDirectory(path, NFs::EFilePermission(mode)) && !NFs::Exists(path)) {
- ythrow TSystemError() << "failed to create directory " << path;
- }
- }
- void MakePathIfNotExist(const char* path, int mode) {
- NFs::MakeDirectoryRecursive(path, NFs::EFilePermission(mode));
- if (!NFs::Exists(path) || !TFileStat(path).IsDir()) {
- ythrow TSystemError() << "failed to create directory " << path;
- }
- }
- const char* GetFileNameComponent(const char* f) {
- const char* p = strrchr(f, LOCSLASH_C);
- #ifdef _win_
- // "/" is also valid char separator on Windows
- const char* p2 = strrchr(f, '/');
- if (p2 > p)
- p = p2;
- #endif
- if (p) {
- return p + 1;
- }
- return f;
- }
- TString GetSystemTempDir() {
- #ifdef _win_
- char buffer[1024];
- DWORD size = GetTempPath(1024, buffer);
- if (!size) {
- ythrow TSystemError() << "failed to get system temporary directory";
- }
- return TString(buffer, size);
- #else
- const char* var = "TMPDIR";
- const char* def = "/tmp";
- const char* r = getenv(var);
- const char* result = r ? r : def;
- return result[0] == '/' ? result : ResolveDir(result);
- #endif
- }
- TString ResolveDir(const char* path) {
- return ResolvePath(path, true);
- }
- bool SafeResolveDir(const char* path, TString& result) {
- try {
- result = ResolvePath(path, true);
- return true;
- } catch (...) {
- return false;
- }
- }
- TString GetDirName(const TString& path) {
- return TFsPath(path).Dirname();
- }
- #ifdef _win32_
- char* realpath(const char* pathname, char resolved_path[MAXPATHLEN]) {
- // partial implementation: no path existence check
- return _fullpath(resolved_path, pathname, MAXPATHLEN - 1);
- }
- #endif
- TString GetBaseName(const TString& path) {
- return TFsPath(path).Basename();
- }
- static bool IsAbsolutePath(const char* str) {
- return str && TPathSplitTraitsLocal::IsAbsolutePath(TStringBuf(str, NStringPrivate::GetStringLengthWithLimit(str, 3)));
- }
- int ResolvePath(const char* rel, const char* abs, char res[/*MAXPATHLEN*/], bool isdir) {
- char t[MAXPATHLEN * 2 + 3];
- size_t len;
- *res = 0;
- if (!rel || !*rel)
- return EINVAL;
- if (!IsAbsolutePath(rel) && IsAbsolutePath(abs)) {
- len = strlcpy(t, abs, sizeof(t));
- if (len >= sizeof(t) - 3)
- return EINVAL;
- if (t[len - 1] != LOCSLASH_C)
- t[len++] = LOCSLASH_C;
- len += strlcpy(t + len, rel, sizeof(t) - len);
- } else {
- len = strlcpy(t, rel, sizeof(t));
- }
- if (len >= sizeof(t) - 3)
- return EINVAL;
- if (isdir && t[len - 1] != LOCSLASH_C) {
- t[len++] = LOCSLASH_C;
- t[len] = 0;
- }
- if (!realpath(t, res)) {
- if (!isdir && realpath(GetDirName(t).data(), res)) {
- len = strlen(res);
- if (res[len - 1] != LOCSLASH_C) {
- res[len++] = LOCSLASH_C;
- res[len] = 0;
- }
- strcpy(res + len, GetBaseName(t).data());
- return 0;
- }
- return errno ? errno : ENOENT;
- }
- if (isdir) {
- len = strlen(res);
- if (res[len - 1] != LOCSLASH_C) {
- res[len++] = LOCSLASH_C;
- res[len] = 0;
- }
- }
- return 0;
- }
- TString ResolvePath(const char* rel, const char* abs, bool isdir) {
- char buf[PATH_MAX];
- if (ResolvePath(rel, abs, buf, isdir))
- ythrow yexception() << "cannot resolve path: \"" << rel << "\"";
- return buf;
- }
- TString ResolvePath(const char* path, bool isDir) {
- return ResolvePath(path, nullptr, isDir);
- }
- TString StripFileComponent(const TString& fileName) {
- TString dir = IsDir(fileName) ? fileName : GetDirName(fileName);
- if (!dir.empty() && dir.back() != GetDirectorySeparator()) {
- dir.append(GetDirectorySeparator());
- }
- return dir;
- }
|