1/****************************************************************************
2 * Copyright (C) 2013 Woboq UG (haftungsbeschraenkt)
3 * Olivier Goffart <contact at woboq.com>
4 * https://woboq.com/codebrowser.html
5 *
6 * This file is part of the Woboq Code Browser.
7 *
8 * Commercial License Usage:
9 * Licensees holding valid commercial licenses provided by Woboq may use
10 * this file in accordance with the terms contained in a written agreement
11 * between the licensee and Woboq.
12 * For further information see https://woboq.com/codebrowser.html
13 *
14 * Alternatively, this work may be used under a Creative Commons
15 * Attribution-NonCommercial-ShareAlike 3.0 (CC-BY-NC-SA 3.0) License.
16 * http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US
17 * This license does not allow you to use the code browser to assist the
18 * development of your commercial software. If you intent to do so, consider
19 * purchasing a commercial licence.
20 ****************************************************************************/
21
22#include "filesystem.h"
23
24#include <clang/Basic/Version.h>
25#include <llvm/ADT/SmallString.h>
26#include <llvm/ADT/Twine.h>
27#include <llvm/Support/FileSystem.h>
28#include <llvm/Support/Path.h>
29
30#include <iostream>
31
32#ifndef _WIN32
33#include <sys/stat.h>
34#include <unistd.h>
35#else
36#include <windows.h> // MAX_PATH
37#endif
38
39void make_forward_slashes(char *str)
40{
41 char *c = strchr(s: str, c: '\\');
42 while (c) {
43 *c = '/';
44 c = strchr(s: c + 1, c: '\\');
45 }
46}
47void make_forward_slashes(std::string &str)
48{
49 std::replace(first: str.begin(), last: str.end(), old_value: '\\', new_value: '/');
50}
51
52// ATTENTION: Keep in sync with ECMAScript function of the same name in .js files and
53// `escapeAttrForFilename` generator.cpp
54void replace_invalid_filename_chars(std::string &str)
55{
56 std::replace(first: str.begin(), last: str.end(), old_value: ':', new_value: '.');
57}
58
59std::error_code canonicalize(const llvm::Twine &path, llvm::SmallVectorImpl<char> &result)
60{
61 std::string p = path.str();
62#if CLANG_VERSION_MAJOR >= 5
63 llvm::sys::fs::real_path(path, output&: result);
64#else
65#ifdef PATH_MAX
66 int path_max = PATH_MAX;
67#elif defined(MAX_PATH)
68 unsigned int path_max = MAX_PATH;
69#else
70 int path_max = pathconf(p.c_str(), _PC_PATH_MAX);
71 if (path_max <= 0)
72 path_max = 4096;
73#endif
74
75 result.resize(path_max);
76 realpath(p.c_str(), result.data());
77
78 result.resize(strlen(result.data()));
79#endif
80
81#ifdef _WIN32
82 // Make sure we use forward slashes to make sure folder detection works as expected everywhere
83 make_forward_slashes(result.data());
84#endif
85
86 return {};
87}
88
89#if (CLANG_VERSION_MAJOR >= 3 && CLANG_VERSION_MINOR >= 8) || CLANG_VERSION_MAJOR > 3
90std::error_code create_directories(const llvm::Twine &path)
91{
92 using namespace llvm::sys::fs;
93 auto defaultPerms = perms::all_all & ~perms::group_write & ~perms::others_write;
94
95 return llvm::sys::fs::create_directories(path, IgnoreExisting: true, Perms: defaultPerms);
96}
97#else
98/* Based on the one from Support/Unix/PathV2 but with different default rights */
99static std::error_code create_directory(const llvm::Twine &path)
100{
101 using namespace llvm;
102 SmallString<128> path_storage;
103 StringRef p = path.toNullTerminatedStringRef(path_storage);
104
105 if (::mkdir(p.begin(), 0755) == -1) {
106 if (errno != static_cast<int>(std::errc::file_exists))
107 return { errno, std::system_category() };
108 }
109 return {};
110}
111
112std::error_code create_directories(const llvm::Twine &path)
113{
114 using namespace llvm;
115 using namespace llvm::sys;
116 SmallString<128> path_storage;
117 StringRef p = path.toStringRef(path_storage);
118 StringRef parent = path::parent_path(p);
119 if (!parent.empty()) {
120 bool parent_exists;
121#if CLANG_VERSION_MAJOR == 3 && CLANG_VERSION_MINOR <= 5
122 if (auto ec = fs::exists(parent, parent_exists)) {
123#if CLANG_VERSION_MAJOR == 3 && CLANG_VERSION_MINOR <= 4
124 return std::error_code(ec.value(), std::system_category());
125#else
126 return ec;
127#endif
128 }
129#else
130 parent_exists = fs::exists(parent);
131#endif
132
133 if (!parent_exists)
134 if (auto ec = create_directories(parent))
135 return ec;
136 }
137
138 return create_directory(p);
139}
140#endif
141
142/**
143 * https://svn.boost.org/trac/boost/ticket/1976#comment:2
144 *
145 * "The idea: uncomplete(/foo/new, /foo/bar) => ../new
146 * The use case for this is any time you get a full path (from an open dialog, perhaps)
147 * and want to store a relative path so that the group of files can be moved to a different
148 * directory without breaking the paths. An IDE would be a simple example, so that the
149 * project file could be safely checked out of subversion."
150 *
151 * ALGORITHM:
152 * iterate path and base
153 * compare all elements so far of path and base
154 * whilst they are the same, no write to output
155 * when they change, or one runs out:
156 * write to output, ../ times the number of remaining elements in base
157 * write to output, the remaining elements in path
158 */
159std::string naive_uncomplete(llvm::StringRef base, llvm::StringRef path)
160{
161 using namespace llvm;
162 if (sys::path::has_root_path(path)) {
163 if (sys::path::root_path(path) != sys::path::root_path(path: base)) {
164 return std::string(path);
165 } else {
166 return naive_uncomplete(base: sys::path::relative_path(path: base), path: sys::path::relative_path(path));
167 }
168 } else {
169 if (sys::path::has_root_path(path: base)) {
170 std::cerr << "naive_uncomplete(" << base.str() << "," << path.str()
171 << "): cannot uncomplete a path relative path from a rooted base"
172 << std::endl;
173 return std::string(path);
174 } else {
175 auto path_it = sys::path::begin(path);
176 auto path_it_end = sys::path::end(path);
177 auto base_it = sys::path::begin(path: base);
178 auto base_it_end = sys::path::end(path: base);
179 while (path_it != path_it_end && base_it != base_it_end) {
180 if (*path_it != *base_it)
181 break;
182 ++path_it;
183 ++base_it;
184 }
185 llvm::SmallString<128> result;
186 for (; base_it != base_it_end; ++base_it) {
187 sys::path::append(path&: result, a: "..");
188 }
189 sys::path::append(path&: result, begin: path_it, end: path_it_end);
190 return std::string(result.str());
191 }
192 }
193}
194

source code of codebrowser/generator/filesystem.cpp