Bumping manifests a=b2g-bump
[gecko.git] / xpcom / io / nsLocalFileCommon.cpp
blob5317db4d3470738e15ef03ed84275ce877d0f825
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 "nsIServiceManager.h"
9 #include "nsLocalFile.h" // includes platform-specific headers
11 #include "nsString.h"
12 #include "nsCOMPtr.h"
13 #include "nsReadableUtils.h"
14 #include "nsPrintfCString.h"
15 #include "nsCRT.h"
16 #include "nsNativeCharsetUtils.h"
17 #include "nsUTF8Utils.h"
19 #ifdef XP_WIN
20 #include <string.h>
21 #endif
24 void
25 NS_StartupLocalFile()
27 nsLocalFile::GlobalInit();
30 void
31 NS_ShutdownLocalFile()
33 nsLocalFile::GlobalShutdown();
36 #if !defined(MOZ_WIDGET_COCOA) && !defined(XP_WIN)
37 NS_IMETHODIMP
38 nsLocalFile::InitWithFile(nsIFile* aFile)
40 if (NS_WARN_IF(!aFile)) {
41 return NS_ERROR_INVALID_ARG;
44 nsAutoCString path;
45 aFile->GetNativePath(path);
46 if (path.IsEmpty()) {
47 return NS_ERROR_INVALID_ARG;
49 return InitWithNativePath(path);
51 #endif
53 #define kMaxFilenameLength 255
54 #define kMaxExtensionLength 100
55 #define kMaxSequenceNumberLength 5 // "-9999"
56 // requirement: kMaxExtensionLength < kMaxFilenameLength - kMaxSequenceNumberLength
58 NS_IMETHODIMP
59 nsLocalFile::CreateUnique(uint32_t aType, uint32_t aAttributes)
61 nsresult rv;
62 bool longName;
64 #ifdef XP_WIN
65 nsAutoString pathName, leafName, rootName, suffix;
66 rv = GetPath(pathName);
67 #else
68 nsAutoCString pathName, leafName, rootName, suffix;
69 rv = GetNativePath(pathName);
70 #endif
71 if (NS_FAILED(rv)) {
72 return rv;
75 longName = (pathName.Length() + kMaxSequenceNumberLength >
76 kMaxFilenameLength);
77 if (!longName) {
78 rv = Create(aType, aAttributes);
79 if (rv != NS_ERROR_FILE_ALREADY_EXISTS) {
80 return rv;
84 #ifdef XP_WIN
85 rv = GetLeafName(leafName);
86 if (NS_FAILED(rv)) {
87 return rv;
90 const int32_t lastDot = leafName.RFindChar(char16_t('.'));
91 #else
92 rv = GetNativeLeafName(leafName);
93 if (NS_FAILED(rv)) {
94 return rv;
97 const int32_t lastDot = leafName.RFindChar('.');
98 #endif
100 if (lastDot == kNotFound) {
101 rootName = leafName;
102 } else {
103 suffix = Substring(leafName, lastDot); // include '.'
104 rootName = Substring(leafName, 0, lastDot); // strip suffix and dot
107 if (longName) {
108 int32_t maxRootLength = (kMaxFilenameLength -
109 (pathName.Length() - leafName.Length()) -
110 suffix.Length() - kMaxSequenceNumberLength);
112 // We cannot create an item inside a directory whose name is too long.
113 // Also, ensure that at least one character remains after we truncate
114 // the root name, as we don't want to end up with an empty leaf name.
115 if (maxRootLength < 2) {
116 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
119 #ifdef XP_WIN
120 // ensure that we don't cut the name in mid-UTF16-character
121 rootName.SetLength(NS_IS_LOW_SURROGATE(rootName[maxRootLength]) ?
122 maxRootLength - 1 : maxRootLength);
123 SetLeafName(rootName + suffix);
124 #else
125 if (NS_IsNativeUTF8()) {
126 // ensure that we don't cut the name in mid-UTF8-character
127 // (assume the name is valid UTF8 to begin with)
128 while (UTF8traits::isInSeq(rootName[maxRootLength])) {
129 --maxRootLength;
132 // Another check to avoid ending up with an empty leaf name.
133 if (maxRootLength == 0 && suffix.IsEmpty()) {
134 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
138 rootName.SetLength(maxRootLength);
139 SetNativeLeafName(rootName + suffix);
140 #endif
141 nsresult rv = Create(aType, aAttributes);
142 if (rv != NS_ERROR_FILE_ALREADY_EXISTS) {
143 return rv;
147 for (int indx = 1; indx < 10000; ++indx) {
148 // start with "Picture-1.jpg" after "Picture.jpg" exists
149 #ifdef XP_WIN
150 SetLeafName(rootName +
151 NS_ConvertASCIItoUTF16(nsPrintfCString("-%d", indx)) +
152 suffix);
153 #else
154 SetNativeLeafName(rootName + nsPrintfCString("-%d", indx) + suffix);
155 #endif
156 rv = Create(aType, aAttributes);
157 if (NS_SUCCEEDED(rv) || rv != NS_ERROR_FILE_ALREADY_EXISTS) {
158 return rv;
162 // The disk is full, sort of
163 return NS_ERROR_FILE_TOO_BIG;
166 #if defined(XP_WIN)
167 static const char16_t kPathSeparatorChar = '\\';
168 #elif defined(XP_UNIX)
169 static const char16_t kPathSeparatorChar = '/';
170 #else
171 #error Need to define file path separator for your platform
172 #endif
174 static int32_t
175 SplitPath(char16_t* aPath, char16_t** aNodeArray, int32_t aArrayLen)
177 if (*aPath == 0) {
178 return 0;
181 char16_t** nodePtr = aNodeArray;
182 if (*aPath == kPathSeparatorChar) {
183 aPath++;
185 *nodePtr++ = aPath;
187 for (char16_t* cp = aPath; *cp != 0; ++cp) {
188 if (*cp == kPathSeparatorChar) {
189 *cp++ = 0;
190 if (*cp == 0) {
191 break;
193 if (nodePtr - aNodeArray >= aArrayLen) {
194 return -1;
196 *nodePtr++ = cp;
199 return nodePtr - aNodeArray;
203 NS_IMETHODIMP
204 nsLocalFile::GetRelativeDescriptor(nsIFile* aFromFile, nsACString& aResult)
206 if (NS_WARN_IF(!aFromFile)) {
207 return NS_ERROR_INVALID_ARG;
209 const int32_t kMaxNodesInPath = 32;
212 // aResult will be UTF-8 encoded
215 nsresult rv;
216 aResult.Truncate(0);
218 nsAutoString thisPath, fromPath;
219 char16_t* thisNodes[kMaxNodesInPath];
220 char16_t* fromNodes[kMaxNodesInPath];
221 int32_t thisNodeCnt, fromNodeCnt, nodeIndex;
223 rv = GetPath(thisPath);
224 if (NS_FAILED(rv)) {
225 return rv;
227 rv = aFromFile->GetPath(fromPath);
228 if (NS_FAILED(rv)) {
229 return rv;
232 // get raw pointer to mutable string buffer
233 char16_t* thisPathPtr;
234 thisPath.BeginWriting(thisPathPtr);
235 char16_t* fromPathPtr;
236 fromPath.BeginWriting(fromPathPtr);
238 thisNodeCnt = SplitPath(thisPathPtr, thisNodes, kMaxNodesInPath);
239 fromNodeCnt = SplitPath(fromPathPtr, fromNodes, kMaxNodesInPath);
240 if (thisNodeCnt < 0 || fromNodeCnt < 0) {
241 return NS_ERROR_FAILURE;
244 for (nodeIndex = 0; nodeIndex < thisNodeCnt &&
245 nodeIndex < fromNodeCnt; ++nodeIndex) {
246 #ifdef XP_WIN
247 if (_wcsicmp(char16ptr_t(thisNodes[nodeIndex]),
248 char16ptr_t(fromNodes[nodeIndex]))) {
249 break;
251 #else
252 if (nsCRT::strcmp(thisNodes[nodeIndex], fromNodes[nodeIndex])) {
253 break;
255 #endif
258 int32_t branchIndex = nodeIndex;
259 for (nodeIndex = branchIndex; nodeIndex < fromNodeCnt; ++nodeIndex) {
260 aResult.AppendLiteral("../");
262 for (nodeIndex = branchIndex; nodeIndex < thisNodeCnt; nodeIndex++) {
263 NS_ConvertUTF16toUTF8 nodeStr(thisNodes[nodeIndex]);
264 aResult.Append(nodeStr);
265 if (nodeIndex + 1 < thisNodeCnt) {
266 aResult.Append('/');
270 return NS_OK;
273 NS_IMETHODIMP
274 nsLocalFile::SetRelativeDescriptor(nsIFile* aFromFile,
275 const nsACString& aRelativeDesc)
277 NS_NAMED_LITERAL_CSTRING(kParentDirStr, "../");
279 nsCOMPtr<nsIFile> targetFile;
280 nsresult rv = aFromFile->Clone(getter_AddRefs(targetFile));
281 if (NS_FAILED(rv)) {
282 return rv;
286 // aRelativeDesc is UTF-8 encoded
289 nsCString::const_iterator strBegin, strEnd;
290 aRelativeDesc.BeginReading(strBegin);
291 aRelativeDesc.EndReading(strEnd);
293 nsCString::const_iterator nodeBegin(strBegin), nodeEnd(strEnd);
294 nsCString::const_iterator pos(strBegin);
296 nsCOMPtr<nsIFile> parentDir;
297 while (FindInReadable(kParentDirStr, nodeBegin, nodeEnd)) {
298 rv = targetFile->GetParent(getter_AddRefs(parentDir));
299 if (NS_FAILED(rv)) {
300 return rv;
302 if (!parentDir) {
303 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
305 targetFile = parentDir;
307 nodeBegin = nodeEnd;
308 pos = nodeEnd;
309 nodeEnd = strEnd;
312 nodeBegin = nodeEnd = pos;
313 while (nodeEnd != strEnd) {
314 FindCharInReadable('/', nodeEnd, strEnd);
315 targetFile->Append(NS_ConvertUTF8toUTF16(Substring(nodeBegin, nodeEnd)));
316 if (nodeEnd != strEnd) { // If there's more left in the string, inc over the '/' nodeEnd is on.
317 ++nodeEnd;
319 nodeBegin = nodeEnd;
322 return InitWithFile(targetFile);