Bug 1690340 - Part 2: Use the new naming for the developer tools menu items. r=jdescottes
[gecko.git] / xpcom / io / nsLocalFileCommon.cpp
blob82092138debd4557a5a9d64837056ba354117605
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsLocalFile.h" // includes platform-specific headers
9 #include "nsString.h"
10 #include "nsCOMPtr.h"
11 #include "nsReadableUtils.h"
12 #include "nsPrintfCString.h"
13 #include "nsCRT.h"
14 #include "nsNativeCharsetUtils.h"
15 #include "nsUTF8Utils.h"
16 #include "nsArray.h"
17 #include "nsLocalFileCommon.h"
19 #ifdef XP_WIN
20 # include <string.h>
21 #endif
23 // Extensions that should be considered 'executable', ie will not allow users
24 // to open immediately without first saving to disk, and potentially provoke
25 // other warnings. PLEASE read the longer comment in
26 // toolkit/components/reputationservice/ApplicationReputation.cpp
27 // before modifying this list!
28 /* static */
29 const char* const sExecutableExts[] = {
30 // clang-format off
31 ".accda", // MS Access database
32 ".accdb", // MS Access database
33 ".accde", // MS Access database
34 ".accdr", // MS Access database
35 ".ad",
36 ".ade", // access project extension
37 ".adp",
38 ".air", // Adobe AIR installer
39 ".app", // executable application
40 ".application", // from bug 348763
41 ".asp",
42 ".bas",
43 ".bat",
44 ".cer", // Signed certificate file
45 ".chm",
46 ".cmd",
47 ".com",
48 ".cpl",
49 ".crt",
50 ".der",
51 ".exe",
52 ".fileloc", // Apple finder internet location data file
53 ".fxp", // FoxPro compiled app
54 ".hlp",
55 ".hta",
56 ".inf",
57 ".ins",
58 ".isp",
59 ".jar", // java application bundle
60 ".jnlp",
61 ".js",
62 ".jse",
63 ".lnk",
64 ".mad", // Access Module Shortcut
65 ".maf", // Access
66 ".mag", // Access Diagram Shortcut
67 ".mam", // Access Macro Shortcut
68 ".maq", // Access Query Shortcut
69 ".mar", // Access Report Shortcut
70 ".mas", // Access Stored Procedure
71 ".mat", // Access Table Shortcut
72 ".mau", // Media Attachment Unit
73 ".mav", // Access View Shortcut
74 ".maw", // Access Data Access Page
75 ".mda", // Access Add-in, MDA Access 2 Workgroup
76 ".mdb",
77 ".mde",
78 ".mdt", // Access Add-in Data
79 ".mdw", // Access Workgroup Information
80 ".mdz", // Access Wizard Template
81 ".msc",
82 ".msh", // Microsoft Shell
83 ".msh1", // Microsoft Shell
84 ".msh1xml", // Microsoft Shell
85 ".msh2", // Microsoft Shell
86 ".msh2xml", // Microsoft Shell
87 ".mshxml", // Microsoft Shell
88 ".msi",
89 ".msp",
90 ".mst",
91 ".ops", // Office Profile Settings
92 ".pcd",
93 ".pif",
94 ".plg", // Developer Studio Build Log
95 ".prf", // windows system file
96 ".prg",
97 ".pst",
98 ".reg",
99 ".scf", // Windows explorer command
100 ".scr",
101 ".sct",
102 ".settingcontent-ms",
103 ".shb",
104 ".shs",
105 ".url",
106 ".vb",
107 ".vbe",
108 ".vbs",
109 ".vdx",
110 ".vsd",
111 ".vsdm",
112 ".vsdx",
113 ".vsmacros", // Visual Studio .NET Binary-based Macro Project
114 ".vss",
115 ".vssm",
116 ".vssx",
117 ".vst",
118 ".vstm",
119 ".vstx",
120 ".vsw",
121 ".vsx",
122 ".vtx",
123 ".webloc", // MacOS website location file
124 ".ws",
125 ".wsc",
126 ".wsf",
127 ".wsh"
128 // clang-format on
131 #if !defined(MOZ_WIDGET_COCOA) && !defined(XP_WIN)
132 NS_IMETHODIMP
133 nsLocalFile::InitWithFile(nsIFile* aFile) {
134 if (NS_WARN_IF(!aFile)) {
135 return NS_ERROR_INVALID_ARG;
138 nsAutoCString path;
139 aFile->GetNativePath(path);
140 if (path.IsEmpty()) {
141 return NS_ERROR_INVALID_ARG;
143 return InitWithNativePath(path);
145 #endif
147 #define kMaxFilenameLength 255
148 #define kMaxExtensionLength 100
149 #define kMaxSequenceNumberLength 5 // "-9999"
150 // requirement: kMaxExtensionLength <
151 // kMaxFilenameLength - kMaxSequenceNumberLength
153 NS_IMETHODIMP
154 nsLocalFile::CreateUnique(uint32_t aType, uint32_t aAttributes) {
155 nsresult rv;
156 bool longName;
158 #ifdef XP_WIN
159 nsAutoString pathName, leafName, rootName, suffix;
160 rv = GetPath(pathName);
161 #else
162 nsAutoCString pathName, leafName, rootName, suffix;
163 rv = GetNativePath(pathName);
164 #endif
165 if (NS_FAILED(rv)) {
166 return rv;
169 auto FailedBecauseExists = [&](nsresult aRv) {
170 if (aRv == NS_ERROR_FILE_ACCESS_DENIED) {
171 bool exists;
172 return NS_SUCCEEDED(Exists(&exists)) && exists;
174 return aRv == NS_ERROR_FILE_ALREADY_EXISTS;
177 longName =
178 (pathName.Length() + kMaxSequenceNumberLength > kMaxFilenameLength);
179 if (!longName) {
180 rv = Create(aType, aAttributes);
181 if (!FailedBecauseExists(rv)) {
182 return rv;
186 #ifdef XP_WIN
187 rv = GetLeafName(leafName);
188 if (NS_FAILED(rv)) {
189 return rv;
192 const int32_t lastDot = leafName.RFindChar(char16_t('.'));
193 #else
194 rv = GetNativeLeafName(leafName);
195 if (NS_FAILED(rv)) {
196 return rv;
199 const int32_t lastDot = leafName.RFindChar('.');
200 #endif
202 if (lastDot == kNotFound) {
203 rootName = leafName;
204 } else {
205 suffix = Substring(leafName, lastDot); // include '.'
206 rootName = Substring(leafName, 0, lastDot); // strip suffix and dot
209 if (longName) {
210 int32_t maxRootLength =
211 (kMaxFilenameLength - (pathName.Length() - leafName.Length()) -
212 suffix.Length() - kMaxSequenceNumberLength);
214 // We cannot create an item inside a directory whose name is too long.
215 // Also, ensure that at least one character remains after we truncate
216 // the root name, as we don't want to end up with an empty leaf name.
217 if (maxRootLength < 2) {
218 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
221 #ifdef XP_WIN
222 // ensure that we don't cut the name in mid-UTF16-character
223 rootName.SetLength(NS_IS_LOW_SURROGATE(rootName[maxRootLength])
224 ? maxRootLength - 1
225 : maxRootLength);
226 SetLeafName(rootName + suffix);
227 #else
228 if (NS_IsNativeUTF8()) {
229 // ensure that we don't cut the name in mid-UTF8-character
230 // (assume the name is valid UTF8 to begin with)
231 while (UTF8traits::isInSeq(rootName[maxRootLength])) {
232 --maxRootLength;
235 // Another check to avoid ending up with an empty leaf name.
236 if (maxRootLength == 0 && suffix.IsEmpty()) {
237 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
241 rootName.SetLength(maxRootLength);
242 SetNativeLeafName(rootName + suffix);
243 #endif
244 nsresult rvCreate = Create(aType, aAttributes);
245 if (!FailedBecauseExists(rvCreate)) {
246 return rvCreate;
250 for (int indx = 1; indx < 10000; ++indx) {
251 // start with "Picture-1.jpg" after "Picture.jpg" exists
252 #ifdef XP_WIN
253 SetLeafName(rootName +
254 NS_ConvertASCIItoUTF16(nsPrintfCString("-%d", indx)) + suffix);
255 #else
256 SetNativeLeafName(rootName + nsPrintfCString("-%d", indx) + suffix);
257 #endif
258 rv = Create(aType, aAttributes);
259 if (NS_SUCCEEDED(rv) || !FailedBecauseExists(rv)) {
260 return rv;
264 // The disk is full, sort of
265 return NS_ERROR_FILE_TOO_BIG;
268 #if defined(XP_WIN)
269 static const char16_t kPathSeparatorChar = '\\';
270 #elif defined(XP_UNIX)
271 static const char16_t kPathSeparatorChar = '/';
272 #else
273 # error Need to define file path separator for your platform
274 #endif
276 static void SplitPath(char16_t* aPath, nsTArray<char16_t*>& aNodeArray) {
277 if (*aPath == 0) {
278 return;
281 if (*aPath == kPathSeparatorChar) {
282 aPath++;
284 aNodeArray.AppendElement(aPath);
286 for (char16_t* cp = aPath; *cp != 0; ++cp) {
287 if (*cp == kPathSeparatorChar) {
288 *cp++ = 0;
289 if (*cp == 0) {
290 break;
292 aNodeArray.AppendElement(cp);
297 NS_IMETHODIMP
298 nsLocalFile::GetRelativeDescriptor(nsIFile* aFromFile, nsACString& aResult) {
299 if (NS_WARN_IF(!aFromFile)) {
300 return NS_ERROR_INVALID_ARG;
304 // aResult will be UTF-8 encoded
307 nsresult rv;
308 aResult.Truncate(0);
310 nsAutoString thisPath, fromPath;
311 AutoTArray<char16_t*, 32> thisNodes;
312 AutoTArray<char16_t*, 32> fromNodes;
314 rv = GetPath(thisPath);
315 if (NS_FAILED(rv)) {
316 return rv;
318 rv = aFromFile->GetPath(fromPath);
319 if (NS_FAILED(rv)) {
320 return rv;
323 // get raw pointer to mutable string buffer
324 char16_t* thisPathPtr = thisPath.BeginWriting();
325 char16_t* fromPathPtr = fromPath.BeginWriting();
327 SplitPath(thisPathPtr, thisNodes);
328 SplitPath(fromPathPtr, fromNodes);
330 size_t nodeIndex;
331 for (nodeIndex = 0;
332 nodeIndex < thisNodes.Length() && nodeIndex < fromNodes.Length();
333 ++nodeIndex) {
334 #ifdef XP_WIN
335 if (_wcsicmp(char16ptr_t(thisNodes[nodeIndex]),
336 char16ptr_t(fromNodes[nodeIndex]))) {
337 break;
339 #else
340 if (nsCRT::strcmp(thisNodes[nodeIndex], fromNodes[nodeIndex])) {
341 break;
343 #endif
346 size_t branchIndex = nodeIndex;
347 for (nodeIndex = branchIndex; nodeIndex < fromNodes.Length(); ++nodeIndex) {
348 aResult.AppendLiteral("../");
350 StringJoinAppend(aResult, "/"_ns, mozilla::Span{thisNodes}.From(branchIndex),
351 [](nsACString& dest, const auto& thisNode) {
352 // XXX(Bug 1682869) We wouldn't need to reconstruct a
353 // nsDependentString here if SplitPath already returned
354 // nsDependentString. In fact, it seems SplitPath might be
355 // replaced by ParseString?
356 AppendUTF16toUTF8(nsDependentString{thisNode}, dest);
359 return NS_OK;
362 NS_IMETHODIMP
363 nsLocalFile::SetRelativeDescriptor(nsIFile* aFromFile,
364 const nsACString& aRelativeDesc) {
365 constexpr auto kParentDirStr = "../"_ns;
367 nsCOMPtr<nsIFile> targetFile;
368 nsresult rv = aFromFile->Clone(getter_AddRefs(targetFile));
369 if (NS_FAILED(rv)) {
370 return rv;
374 // aRelativeDesc is UTF-8 encoded
377 nsCString::const_iterator strBegin, strEnd;
378 aRelativeDesc.BeginReading(strBegin);
379 aRelativeDesc.EndReading(strEnd);
381 nsCString::const_iterator nodeBegin(strBegin), nodeEnd(strEnd);
382 nsCString::const_iterator pos(strBegin);
384 nsCOMPtr<nsIFile> parentDir;
385 while (FindInReadable(kParentDirStr, nodeBegin, nodeEnd)) {
386 rv = targetFile->GetParent(getter_AddRefs(parentDir));
387 if (NS_FAILED(rv)) {
388 return rv;
390 if (!parentDir) {
391 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
393 targetFile = parentDir;
395 nodeBegin = nodeEnd;
396 pos = nodeEnd;
397 nodeEnd = strEnd;
400 nodeBegin = nodeEnd = pos;
401 while (nodeEnd != strEnd) {
402 FindCharInReadable('/', nodeEnd, strEnd);
403 targetFile->Append(NS_ConvertUTF8toUTF16(Substring(nodeBegin, nodeEnd)));
404 if (nodeEnd != strEnd) { // If there's more left in the string, inc over
405 // the '/' nodeEnd is on.
406 ++nodeEnd;
408 nodeBegin = nodeEnd;
411 return InitWithFile(targetFile);
414 NS_IMETHODIMP
415 nsLocalFile::GetRelativePath(nsIFile* aFromFile, nsACString& aResult) {
416 return GetRelativeDescriptor(aFromFile, aResult);
419 NS_IMETHODIMP
420 nsLocalFile::SetRelativePath(nsIFile* aFromFile,
421 const nsACString& aRelativePath) {
422 return SetRelativeDescriptor(aFromFile, aRelativePath);