Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 : #include "path.h"
23 :
24 : #ifndef NO_QT_SUPPORT
25 : #include <QDir>
26 : #endif
27 :
28 : #include "global/stringutils.h"
29 : #include "fileinfo.h"
30 :
31 : using namespace muse;
32 : using namespace muse::io;
33 :
34 14006 : path_t::path_t(const String& s)
35 14006 : : m_path(s.toStdString())
36 : {
37 14006 : }
38 :
39 1254 : path_t::path_t(const std::string& s)
40 1254 : : m_path(s)
41 : {
42 1254 : }
43 :
44 927 : path_t::path_t(std::string&& s)
45 927 : : m_path(std::move(s))
46 : {
47 927 : }
48 :
49 22113 : path_t::path_t(const char* s)
50 44226 : : m_path(s ? s : "")
51 : {
52 22113 : }
53 :
54 1501 : bool path_t::empty() const
55 : {
56 1501 : return m_path.empty();
57 : }
58 :
59 576 : size_t path_t::size() const
60 : {
61 576 : return m_path.size();
62 : }
63 :
64 4 : bool path_t::hasSuffix(const char* str) const
65 : {
66 4 : return io::FileInfo::suffix(m_path).toLower() == str;
67 : }
68 :
69 362 : path_t path_t::appendingComponent(const path_t& other) const
70 : {
71 724 : if ((!m_path.empty() && m_path.back() == '/')
72 724 : || (!other.m_path.empty() && other.m_path.front() == '/')) {
73 0 : return m_path + other.m_path;
74 : }
75 :
76 362 : return m_path + '/' + other.m_path;
77 : }
78 :
79 0 : path_t path_t::appendingSuffix(const path_t& suffix) const
80 : {
81 0 : if (!suffix.m_path.empty() && suffix.m_path.front() == '.') {
82 0 : return m_path + suffix.m_path;
83 : }
84 :
85 0 : return m_path + '.' + suffix.m_path;
86 : }
87 :
88 12729 : String path_t::toString() const
89 : {
90 12729 : return String::fromStdString(m_path);
91 : }
92 :
93 26940 : const std::string& path_t::toStdString() const
94 : {
95 26940 : return m_path;
96 : }
97 :
98 1 : const char* path_t::c_str() const
99 : {
100 1 : return m_path.c_str();
101 : }
102 :
103 : #ifndef NO_QT_SUPPORT
104 2115 : path_t::path_t(const QString& s)
105 2115 : : m_path(s.toStdString())
106 : {
107 2115 : }
108 :
109 4 : path_t::path_t(const QUrl& u)
110 4 : : m_path(u.toLocalFile().toStdString())
111 : {
112 4 : }
113 :
114 32590 : QString path_t::toQString() const
115 : {
116 32590 : return QString::fromStdString(m_path);
117 : }
118 :
119 0 : QUrl path_t::toQUrl() const
120 : {
121 0 : return QUrl::fromLocalFile(QString::fromStdString(m_path));
122 : }
123 :
124 0 : std::wstring path_t::toStdWString() const
125 : {
126 0 : return QString::fromStdString(m_path).toStdWString();
127 : }
128 :
129 : #endif
130 :
131 4240 : std::string muse::io::suffix(const io::path_t& path)
132 : {
133 4240 : return FileInfo::suffix(path).toLower().toStdString();
134 : }
135 :
136 1238 : io::path_t muse::io::filename(const io::path_t& path, bool includingExtension)
137 : {
138 1238 : FileInfo fi(path);
139 2476 : return includingExtension ? fi.fileName() : fi.completeBaseName();
140 1238 : }
141 :
142 56 : io::path_t muse::io::basename(const io::path_t& path)
143 : {
144 56 : FileInfo fi(path);
145 112 : return fi.baseName();
146 56 : }
147 :
148 98 : io::path_t muse::io::completeBasename(const io::path_t& path)
149 : {
150 98 : FileInfo fi(path);
151 196 : return fi.completeBaseName();
152 98 : }
153 :
154 0 : io::path_t muse::io::absolutePath(const path_t& path)
155 : {
156 0 : return FileInfo(path).absolutePath();
157 : }
158 :
159 2 : io::path_t muse::io::absoluteFilePath(const path_t& path)
160 : {
161 2 : return FileInfo(path).absoluteFilePath();
162 : }
163 :
164 141 : io::path_t muse::io::dirpath(const io::path_t& path)
165 : {
166 141 : return FileInfo(path).dir().path();
167 : }
168 :
169 1040 : io::path_t muse::io::absoluteDirpath(const io::path_t& path)
170 : {
171 1040 : return FileInfo(path).dir().absolutePath();
172 : }
173 :
174 0 : bool muse::io::isAbsolute(const path_t& path)
175 : {
176 0 : return FileInfo(path).isAbsolute();
177 : }
178 :
179 56 : bool muse::io::isAllowedFileName(const path_t& fn_)
180 : {
181 56 : std::string fn = basename(fn_).toStdString();
182 56 : if (fn.empty()) {
183 0 : return false;
184 : }
185 :
186 : // Windows filenames are not case sensitive.
187 56 : fn = String::fromStdString(fn).toUpper().toStdString();
188 :
189 56 : static const std::string illegal="<>:\"|?*";
190 :
191 469 : for (const char& c : fn) {
192 : // Check for control characters
193 414 : if (c > 0 && c < 32) {
194 1 : return false;
195 : }
196 :
197 : // Check for illegal characters
198 3307 : for (const char& ilc : illegal) {
199 2894 : if (c == ilc) {
200 1 : return false;
201 : }
202 : }
203 : }
204 :
205 : // Check for device names in filenames
206 : static const std::vector<std::string> devices = {
207 : "CON", "PRN", "AUX", "NUL",
208 : "COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
209 : "LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"
210 59 : };
211 :
212 1375 : for (const std::string& s : devices) {
213 1320 : if (fn == s) {
214 0 : return false;
215 : }
216 : }
217 :
218 : // Check for trailing periods or spaces
219 55 : if (fn.back() == '.' || fn.back() == ' ') {
220 0 : return false;
221 : }
222 :
223 : // Check for pathnames that are too long
224 55 : if (fn.size() > 96) {
225 0 : return false;
226 : }
227 :
228 : // Since we are checking for a filename, it mustn't be a directory
229 55 : if (fn.back() == '\\') {
230 0 : return false;
231 : }
232 :
233 55 : return true;
234 56 : }
235 :
236 0 : io::path_t muse::io::escapeFileName(const io::path_t& fn_)
237 : {
238 : //
239 : // special characters in filenames are a constant source
240 : // of trouble, this replaces some of them common in german:
241 : //
242 0 : String fn = fn_.toString();
243 0 : fn = fn.simplified();
244 0 : fn = fn.replace(' ', '_');
245 0 : fn = fn.replace('\n', '_');
246 0 : fn = fn.replace(Char(0xe4), u"ae"); // ä
247 0 : fn = fn.replace(Char(0xf6), u"oe"); // ö
248 0 : fn = fn.replace(Char(0xfc), u"ue"); // ü
249 0 : fn = fn.replace(Char(0xdf), u"ss"); // ß
250 0 : fn = fn.replace(Char(0xc4), u"Ae"); // Ä
251 0 : fn = fn.replace(Char(0xd6), u"Oe"); // Ö
252 0 : fn = fn.replace(Char(0xdc), u"Ue"); // Ü
253 0 : fn = fn.replace(Char(0x266d), u"b"); // musical flat sign, happen in instrument names, so can happen in part (file) names
254 0 : fn = fn.replace(Char(0x266f), u"#"); // musical sharp sign, can happen in titles, so can happen in score (file) names
255 : //FAT/NTFS special chars
256 0 : fn = fn.replace('<', '_')
257 0 : .replace('>', '_')
258 0 : .replace(':', '_')
259 0 : .replace('"', '_')
260 0 : .replace('/', '_')
261 0 : .replace('\\', '_')
262 0 : .replace('|', '_')
263 0 : .replace('?', '_')
264 0 : .replace('*', '_');
265 :
266 0 : return fn;
267 0 : }
268 :
269 0 : paths_t muse::io::pathsFromString(const std::string& str, const std::string& delim)
270 : {
271 0 : if (str.empty()) {
272 0 : return {};
273 : }
274 :
275 0 : std::vector<std::string> strs;
276 0 : strings::split(str, strs, delim);
277 :
278 0 : paths_t ps;
279 0 : ps.reserve(strs.size());
280 0 : for (const std::string& s : strs) {
281 0 : ps.push_back(path_t(s));
282 : }
283 0 : return ps;
284 0 : }
285 :
286 0 : std::string muse::io::pathsToString(const paths_t& ps, const std::string& delim)
287 : {
288 0 : std::string result;
289 0 : bool first = true;
290 0 : for (const path_t& _path: ps) {
291 0 : if (!first) {
292 0 : result += delim;
293 : }
294 0 : first = false;
295 0 : result += _path.toStdString();
296 : }
297 :
298 0 : return result;
299 0 : }
300 :
301 3 : path_t muse::io::toNativeSeparators(const path_t& path)
302 : {
303 : #ifndef NO_QT_SUPPORT
304 3 : return QDir::toNativeSeparators(path.toQString());
305 : #else
306 : return path;
307 : #endif
308 : }
|