|
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 #include "nsIServiceManager.h" |
|
6 |
|
7 #include "nsLocalFile.h" // includes platform-specific headers |
|
8 |
|
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 |
|
17 #ifdef XP_WIN |
|
18 #include <string.h> |
|
19 #endif |
|
20 |
|
21 |
|
22 void NS_StartupLocalFile() |
|
23 { |
|
24 nsLocalFile::GlobalInit(); |
|
25 } |
|
26 |
|
27 void NS_ShutdownLocalFile() |
|
28 { |
|
29 nsLocalFile::GlobalShutdown(); |
|
30 } |
|
31 |
|
32 #if !defined(MOZ_WIDGET_COCOA) && !defined(XP_WIN) |
|
33 NS_IMETHODIMP |
|
34 nsLocalFile::InitWithFile(nsIFile *aFile) |
|
35 { |
|
36 if (NS_WARN_IF(!aFile)) |
|
37 return NS_ERROR_INVALID_ARG; |
|
38 |
|
39 nsAutoCString path; |
|
40 aFile->GetNativePath(path); |
|
41 if (path.IsEmpty()) |
|
42 return NS_ERROR_INVALID_ARG; |
|
43 return InitWithNativePath(path); |
|
44 } |
|
45 #endif |
|
46 |
|
47 #define kMaxFilenameLength 255 |
|
48 #define kMaxExtensionLength 100 |
|
49 #define kMaxSequenceNumberLength 5 // "-9999" |
|
50 // requirement: kMaxExtensionLength < kMaxFilenameLength - kMaxSequenceNumberLength |
|
51 |
|
52 NS_IMETHODIMP |
|
53 nsLocalFile::CreateUnique(uint32_t type, uint32_t attributes) |
|
54 { |
|
55 nsresult rv; |
|
56 bool longName; |
|
57 |
|
58 #ifdef XP_WIN |
|
59 nsAutoString pathName, leafName, rootName, suffix; |
|
60 rv = GetPath(pathName); |
|
61 #else |
|
62 nsAutoCString pathName, leafName, rootName, suffix; |
|
63 rv = GetNativePath(pathName); |
|
64 #endif |
|
65 if (NS_FAILED(rv)) |
|
66 return rv; |
|
67 |
|
68 longName = (pathName.Length() + kMaxSequenceNumberLength > |
|
69 kMaxFilenameLength); |
|
70 if (!longName) |
|
71 { |
|
72 rv = Create(type, attributes); |
|
73 if (rv != NS_ERROR_FILE_ALREADY_EXISTS) |
|
74 return rv; |
|
75 } |
|
76 |
|
77 #ifdef XP_WIN |
|
78 rv = GetLeafName(leafName); |
|
79 if (NS_FAILED(rv)) |
|
80 return rv; |
|
81 |
|
82 const int32_t lastDot = leafName.RFindChar(char16_t('.')); |
|
83 #else |
|
84 rv = GetNativeLeafName(leafName); |
|
85 if (NS_FAILED(rv)) |
|
86 return rv; |
|
87 |
|
88 const int32_t lastDot = leafName.RFindChar('.'); |
|
89 #endif |
|
90 |
|
91 if (lastDot == kNotFound) |
|
92 { |
|
93 rootName = leafName; |
|
94 } |
|
95 else |
|
96 { |
|
97 suffix = Substring(leafName, lastDot); // include '.' |
|
98 rootName = Substring(leafName, 0, lastDot); // strip suffix and dot |
|
99 } |
|
100 |
|
101 if (longName) |
|
102 { |
|
103 int32_t maxRootLength = (kMaxFilenameLength - |
|
104 (pathName.Length() - leafName.Length()) - |
|
105 suffix.Length() - kMaxSequenceNumberLength); |
|
106 |
|
107 // We cannot create an item inside a directory whose name is too long. |
|
108 // Also, ensure that at least one character remains after we truncate |
|
109 // the root name, as we don't want to end up with an empty leaf name. |
|
110 if (maxRootLength < 2) |
|
111 return NS_ERROR_FILE_UNRECOGNIZED_PATH; |
|
112 |
|
113 #ifdef XP_WIN |
|
114 // ensure that we don't cut the name in mid-UTF16-character |
|
115 rootName.SetLength(NS_IS_LOW_SURROGATE(rootName[maxRootLength]) ? |
|
116 maxRootLength - 1 : maxRootLength); |
|
117 SetLeafName(rootName + suffix); |
|
118 #else |
|
119 if (NS_IsNativeUTF8()) |
|
120 { |
|
121 // ensure that we don't cut the name in mid-UTF8-character |
|
122 // (assume the name is valid UTF8 to begin with) |
|
123 while (UTF8traits::isInSeq(rootName[maxRootLength])) |
|
124 --maxRootLength; |
|
125 |
|
126 // Another check to avoid ending up with an empty leaf name. |
|
127 if (maxRootLength == 0 && suffix.IsEmpty()) |
|
128 return NS_ERROR_FILE_UNRECOGNIZED_PATH; |
|
129 } |
|
130 |
|
131 rootName.SetLength(maxRootLength); |
|
132 SetNativeLeafName(rootName + suffix); |
|
133 #endif |
|
134 nsresult rv = Create(type, attributes); |
|
135 if (rv != NS_ERROR_FILE_ALREADY_EXISTS) |
|
136 return rv; |
|
137 } |
|
138 |
|
139 for (int indx = 1; indx < 10000; indx++) |
|
140 { |
|
141 // start with "Picture-1.jpg" after "Picture.jpg" exists |
|
142 #ifdef XP_WIN |
|
143 SetLeafName(rootName + |
|
144 NS_ConvertASCIItoUTF16(nsPrintfCString("-%d", indx)) + |
|
145 suffix); |
|
146 #else |
|
147 SetNativeLeafName(rootName + nsPrintfCString("-%d", indx) + suffix); |
|
148 #endif |
|
149 rv = Create(type, attributes); |
|
150 if (NS_SUCCEEDED(rv) || rv != NS_ERROR_FILE_ALREADY_EXISTS) |
|
151 return rv; |
|
152 } |
|
153 |
|
154 // The disk is full, sort of |
|
155 return NS_ERROR_FILE_TOO_BIG; |
|
156 } |
|
157 |
|
158 #if defined(XP_WIN) |
|
159 static const char16_t kPathSeparatorChar = '\\'; |
|
160 #elif defined(XP_UNIX) |
|
161 static const char16_t kPathSeparatorChar = '/'; |
|
162 #else |
|
163 #error Need to define file path separator for your platform |
|
164 #endif |
|
165 |
|
166 static int32_t SplitPath(char16_t *path, char16_t **nodeArray, int32_t arrayLen) |
|
167 { |
|
168 if (*path == 0) |
|
169 return 0; |
|
170 |
|
171 char16_t **nodePtr = nodeArray; |
|
172 if (*path == kPathSeparatorChar) |
|
173 path++; |
|
174 *nodePtr++ = path; |
|
175 |
|
176 for (char16_t *cp = path; *cp != 0; cp++) { |
|
177 if (*cp == kPathSeparatorChar) { |
|
178 *cp++ = 0; |
|
179 if (*cp == 0) |
|
180 break; |
|
181 if (nodePtr - nodeArray >= arrayLen) |
|
182 return -1; |
|
183 *nodePtr++ = cp; |
|
184 } |
|
185 } |
|
186 return nodePtr - nodeArray; |
|
187 } |
|
188 |
|
189 |
|
190 NS_IMETHODIMP |
|
191 nsLocalFile::GetRelativeDescriptor(nsIFile *fromFile, nsACString& _retval) |
|
192 { |
|
193 if (NS_WARN_IF(!fromFile)) |
|
194 return NS_ERROR_INVALID_ARG; |
|
195 const int32_t kMaxNodesInPath = 32; |
|
196 |
|
197 // |
|
198 // _retval will be UTF-8 encoded |
|
199 // |
|
200 |
|
201 nsresult rv; |
|
202 _retval.Truncate(0); |
|
203 |
|
204 nsAutoString thisPath, fromPath; |
|
205 char16_t *thisNodes[kMaxNodesInPath], *fromNodes[kMaxNodesInPath]; |
|
206 int32_t thisNodeCnt, fromNodeCnt, nodeIndex; |
|
207 |
|
208 rv = GetPath(thisPath); |
|
209 if (NS_FAILED(rv)) |
|
210 return rv; |
|
211 rv = fromFile->GetPath(fromPath); |
|
212 if (NS_FAILED(rv)) |
|
213 return rv; |
|
214 |
|
215 // get raw pointer to mutable string buffer |
|
216 char16_t *thisPathPtr; thisPath.BeginWriting(thisPathPtr); |
|
217 char16_t *fromPathPtr; fromPath.BeginWriting(fromPathPtr); |
|
218 |
|
219 thisNodeCnt = SplitPath(thisPathPtr, thisNodes, kMaxNodesInPath); |
|
220 fromNodeCnt = SplitPath(fromPathPtr, fromNodes, kMaxNodesInPath); |
|
221 if (thisNodeCnt < 0 || fromNodeCnt < 0) |
|
222 return NS_ERROR_FAILURE; |
|
223 |
|
224 for (nodeIndex = 0; nodeIndex < thisNodeCnt && nodeIndex < fromNodeCnt; ++nodeIndex) { |
|
225 #ifdef XP_WIN |
|
226 if (_wcsicmp(char16ptr_t(thisNodes[nodeIndex]), char16ptr_t(fromNodes[nodeIndex]))) |
|
227 break; |
|
228 #else |
|
229 if (nsCRT::strcmp(thisNodes[nodeIndex], fromNodes[nodeIndex])) |
|
230 break; |
|
231 #endif |
|
232 } |
|
233 |
|
234 int32_t branchIndex = nodeIndex; |
|
235 for (nodeIndex = branchIndex; nodeIndex < fromNodeCnt; nodeIndex++) |
|
236 _retval.AppendLiteral("../"); |
|
237 for (nodeIndex = branchIndex; nodeIndex < thisNodeCnt; nodeIndex++) { |
|
238 NS_ConvertUTF16toUTF8 nodeStr(thisNodes[nodeIndex]); |
|
239 _retval.Append(nodeStr); |
|
240 if (nodeIndex + 1 < thisNodeCnt) |
|
241 _retval.Append('/'); |
|
242 } |
|
243 |
|
244 return NS_OK; |
|
245 } |
|
246 |
|
247 NS_IMETHODIMP |
|
248 nsLocalFile::SetRelativeDescriptor(nsIFile *fromFile, const nsACString& relativeDesc) |
|
249 { |
|
250 NS_NAMED_LITERAL_CSTRING(kParentDirStr, "../"); |
|
251 |
|
252 nsCOMPtr<nsIFile> targetFile; |
|
253 nsresult rv = fromFile->Clone(getter_AddRefs(targetFile)); |
|
254 if (NS_FAILED(rv)) |
|
255 return rv; |
|
256 |
|
257 // |
|
258 // relativeDesc is UTF-8 encoded |
|
259 // |
|
260 |
|
261 nsCString::const_iterator strBegin, strEnd; |
|
262 relativeDesc.BeginReading(strBegin); |
|
263 relativeDesc.EndReading(strEnd); |
|
264 |
|
265 nsCString::const_iterator nodeBegin(strBegin), nodeEnd(strEnd); |
|
266 nsCString::const_iterator pos(strBegin); |
|
267 |
|
268 nsCOMPtr<nsIFile> parentDir; |
|
269 while (FindInReadable(kParentDirStr, nodeBegin, nodeEnd)) { |
|
270 rv = targetFile->GetParent(getter_AddRefs(parentDir)); |
|
271 if (NS_FAILED(rv)) |
|
272 return rv; |
|
273 if (!parentDir) |
|
274 return NS_ERROR_FILE_UNRECOGNIZED_PATH; |
|
275 targetFile = parentDir; |
|
276 |
|
277 nodeBegin = nodeEnd; |
|
278 pos = nodeEnd; |
|
279 nodeEnd = strEnd; |
|
280 } |
|
281 |
|
282 nodeBegin = nodeEnd = pos; |
|
283 while (nodeEnd != strEnd) { |
|
284 FindCharInReadable('/', nodeEnd, strEnd); |
|
285 targetFile->Append(NS_ConvertUTF8toUTF16(Substring(nodeBegin, nodeEnd))); |
|
286 if (nodeEnd != strEnd) // If there's more left in the string, inc over the '/' nodeEnd is on. |
|
287 ++nodeEnd; |
|
288 nodeBegin = nodeEnd; |
|
289 } |
|
290 |
|
291 return InitWithFile(targetFile); |
|
292 } |