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
13 #include "nsReadableUtils.h"
14 #include "nsPrintfCString.h"
16 #include "nsNativeCharsetUtils.h"
17 #include "nsUTF8Utils.h"
27 nsLocalFile::GlobalInit();
31 NS_ShutdownLocalFile()
33 nsLocalFile::GlobalShutdown();
36 #if !defined(MOZ_WIDGET_COCOA) && !defined(XP_WIN)
38 nsLocalFile::InitWithFile(nsIFile
* aFile
)
40 if (NS_WARN_IF(!aFile
)) {
41 return NS_ERROR_INVALID_ARG
;
45 aFile
->GetNativePath(path
);
47 return NS_ERROR_INVALID_ARG
;
49 return InitWithNativePath(path
);
53 #define kMaxFilenameLength 255
54 #define kMaxExtensionLength 100
55 #define kMaxSequenceNumberLength 5 // "-9999"
56 // requirement: kMaxExtensionLength < kMaxFilenameLength - kMaxSequenceNumberLength
59 nsLocalFile::CreateUnique(uint32_t aType
, uint32_t aAttributes
)
65 nsAutoString pathName
, leafName
, rootName
, suffix
;
66 rv
= GetPath(pathName
);
68 nsAutoCString pathName
, leafName
, rootName
, suffix
;
69 rv
= GetNativePath(pathName
);
75 longName
= (pathName
.Length() + kMaxSequenceNumberLength
>
78 rv
= Create(aType
, aAttributes
);
79 if (rv
!= NS_ERROR_FILE_ALREADY_EXISTS
) {
85 rv
= GetLeafName(leafName
);
90 const int32_t lastDot
= leafName
.RFindChar(char16_t('.'));
92 rv
= GetNativeLeafName(leafName
);
97 const int32_t lastDot
= leafName
.RFindChar('.');
100 if (lastDot
== kNotFound
) {
103 suffix
= Substring(leafName
, lastDot
); // include '.'
104 rootName
= Substring(leafName
, 0, lastDot
); // strip suffix and dot
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
;
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
);
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
])) {
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
);
141 nsresult rv
= Create(aType
, aAttributes
);
142 if (rv
!= NS_ERROR_FILE_ALREADY_EXISTS
) {
147 for (int indx
= 1; indx
< 10000; ++indx
) {
148 // start with "Picture-1.jpg" after "Picture.jpg" exists
150 SetLeafName(rootName
+
151 NS_ConvertASCIItoUTF16(nsPrintfCString("-%d", indx
)) +
154 SetNativeLeafName(rootName
+ nsPrintfCString("-%d", indx
) + suffix
);
156 rv
= Create(aType
, aAttributes
);
157 if (NS_SUCCEEDED(rv
) || rv
!= NS_ERROR_FILE_ALREADY_EXISTS
) {
162 // The disk is full, sort of
163 return NS_ERROR_FILE_TOO_BIG
;
167 static const char16_t kPathSeparatorChar
= '\\';
168 #elif defined(XP_UNIX)
169 static const char16_t kPathSeparatorChar
= '/';
171 #error Need to define file path separator for your platform
175 SplitPath(char16_t
* aPath
, char16_t
** aNodeArray
, int32_t aArrayLen
)
181 char16_t
** nodePtr
= aNodeArray
;
182 if (*aPath
== kPathSeparatorChar
) {
187 for (char16_t
* cp
= aPath
; *cp
!= 0; ++cp
) {
188 if (*cp
== kPathSeparatorChar
) {
193 if (nodePtr
- aNodeArray
>= aArrayLen
) {
199 return nodePtr
- aNodeArray
;
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
218 nsAutoString thisPath
, fromPath
;
219 char16_t
* thisNodes
[kMaxNodesInPath
];
220 char16_t
* fromNodes
[kMaxNodesInPath
];
221 int32_t thisNodeCnt
, fromNodeCnt
, nodeIndex
;
223 rv
= GetPath(thisPath
);
227 rv
= aFromFile
->GetPath(fromPath
);
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
) {
247 if (_wcsicmp(char16ptr_t(thisNodes
[nodeIndex
]),
248 char16ptr_t(fromNodes
[nodeIndex
]))) {
252 if (nsCRT::strcmp(thisNodes
[nodeIndex
], fromNodes
[nodeIndex
])) {
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
) {
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
));
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
));
303 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
305 targetFile
= parentDir
;
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.
322 return InitWithFile(targetFile
);