Line data Source code
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/. */
6 :
7 : #include "nsIServiceManager.h"
8 :
9 : #include "nsLocalFile.h" // includes platform-specific headers
10 :
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"
18 :
19 : #ifdef XP_WIN
20 : #include <string.h>
21 : #endif
22 :
23 :
24 : void
25 3 : NS_StartupLocalFile()
26 : {
27 3 : nsLocalFile::GlobalInit();
28 3 : }
29 :
30 : void
31 0 : NS_ShutdownLocalFile()
32 : {
33 0 : nsLocalFile::GlobalShutdown();
34 0 : }
35 :
36 : #if !defined(MOZ_WIDGET_COCOA) && !defined(XP_WIN)
37 : NS_IMETHODIMP
38 2 : nsLocalFile::InitWithFile(nsIFile* aFile)
39 : {
40 2 : if (NS_WARN_IF(!aFile)) {
41 0 : return NS_ERROR_INVALID_ARG;
42 : }
43 :
44 4 : nsAutoCString path;
45 2 : aFile->GetNativePath(path);
46 2 : if (path.IsEmpty()) {
47 0 : return NS_ERROR_INVALID_ARG;
48 : }
49 2 : return InitWithNativePath(path);
50 : }
51 : #endif
52 :
53 : #define kMaxFilenameLength 255
54 : #define kMaxExtensionLength 100
55 : #define kMaxSequenceNumberLength 5 // "-9999"
56 : // requirement: kMaxExtensionLength < kMaxFilenameLength - kMaxSequenceNumberLength
57 :
58 : NS_IMETHODIMP
59 6 : nsLocalFile::CreateUnique(uint32_t aType, uint32_t aAttributes)
60 : {
61 : nsresult rv;
62 : bool longName;
63 :
64 : #ifdef XP_WIN
65 : nsAutoString pathName, leafName, rootName, suffix;
66 : rv = GetPath(pathName);
67 : #else
68 12 : nsAutoCString pathName, leafName, rootName, suffix;
69 6 : rv = GetNativePath(pathName);
70 : #endif
71 6 : if (NS_FAILED(rv)) {
72 0 : return rv;
73 : }
74 :
75 6 : longName = (pathName.Length() + kMaxSequenceNumberLength >
76 : kMaxFilenameLength);
77 6 : if (!longName) {
78 6 : rv = Create(aType, aAttributes);
79 6 : if (rv != NS_ERROR_FILE_ALREADY_EXISTS) {
80 0 : return rv;
81 : }
82 : }
83 :
84 : #ifdef XP_WIN
85 : rv = GetLeafName(leafName);
86 : if (NS_FAILED(rv)) {
87 : return rv;
88 : }
89 :
90 : const int32_t lastDot = leafName.RFindChar(char16_t('.'));
91 : #else
92 6 : rv = GetNativeLeafName(leafName);
93 6 : if (NS_FAILED(rv)) {
94 0 : return rv;
95 : }
96 :
97 6 : const int32_t lastDot = leafName.RFindChar('.');
98 : #endif
99 :
100 6 : if (lastDot == kNotFound) {
101 0 : rootName = leafName;
102 : } else {
103 6 : suffix = Substring(leafName, lastDot); // include '.'
104 6 : rootName = Substring(leafName, 0, lastDot); // strip suffix and dot
105 : }
106 :
107 6 : if (longName) {
108 : int32_t maxRootLength = (kMaxFilenameLength -
109 0 : (pathName.Length() - leafName.Length()) -
110 0 : suffix.Length() - kMaxSequenceNumberLength);
111 :
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 0 : if (maxRootLength < 2) {
116 0 : return NS_ERROR_FILE_UNRECOGNIZED_PATH;
117 : }
118 :
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 0 : 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 0 : while (UTF8traits::isInSeq(rootName[maxRootLength])) {
129 0 : --maxRootLength;
130 : }
131 :
132 : // Another check to avoid ending up with an empty leaf name.
133 0 : if (maxRootLength == 0 && suffix.IsEmpty()) {
134 0 : return NS_ERROR_FILE_UNRECOGNIZED_PATH;
135 : }
136 : }
137 :
138 0 : rootName.SetLength(maxRootLength);
139 0 : SetNativeLeafName(rootName + suffix);
140 : #endif
141 0 : nsresult rvCreate = Create(aType, aAttributes);
142 0 : if (rvCreate != NS_ERROR_FILE_ALREADY_EXISTS) {
143 0 : return rvCreate;
144 : }
145 : }
146 :
147 6 : 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 6 : SetNativeLeafName(rootName + nsPrintfCString("-%d", indx) + suffix);
155 : #endif
156 6 : rv = Create(aType, aAttributes);
157 6 : if (NS_SUCCEEDED(rv) || rv != NS_ERROR_FILE_ALREADY_EXISTS) {
158 6 : return rv;
159 : }
160 : }
161 :
162 : // The disk is full, sort of
163 0 : return NS_ERROR_FILE_TOO_BIG;
164 : }
165 :
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
173 :
174 : static void
175 0 : SplitPath(char16_t* aPath, nsTArray<char16_t*>& aNodeArray)
176 : {
177 0 : if (*aPath == 0) {
178 0 : return;
179 : }
180 :
181 0 : if (*aPath == kPathSeparatorChar) {
182 0 : aPath++;
183 : }
184 0 : aNodeArray.AppendElement(aPath);
185 :
186 0 : for (char16_t* cp = aPath; *cp != 0; ++cp) {
187 0 : if (*cp == kPathSeparatorChar) {
188 0 : *cp++ = 0;
189 0 : if (*cp == 0) {
190 0 : break;
191 : }
192 0 : aNodeArray.AppendElement(cp);
193 : }
194 : }
195 : }
196 :
197 :
198 : NS_IMETHODIMP
199 0 : nsLocalFile::GetRelativeDescriptor(nsIFile* aFromFile, nsACString& aResult)
200 : {
201 0 : if (NS_WARN_IF(!aFromFile)) {
202 0 : return NS_ERROR_INVALID_ARG;
203 : }
204 :
205 : //
206 : // aResult will be UTF-8 encoded
207 : //
208 :
209 : nsresult rv;
210 0 : aResult.Truncate(0);
211 :
212 0 : nsAutoString thisPath, fromPath;
213 0 : AutoTArray<char16_t*, 32> thisNodes;
214 0 : AutoTArray<char16_t*, 32> fromNodes;
215 :
216 0 : rv = GetPath(thisPath);
217 0 : if (NS_FAILED(rv)) {
218 0 : return rv;
219 : }
220 0 : rv = aFromFile->GetPath(fromPath);
221 0 : if (NS_FAILED(rv)) {
222 0 : return rv;
223 : }
224 :
225 : // get raw pointer to mutable string buffer
226 : char16_t* thisPathPtr;
227 0 : thisPath.BeginWriting(thisPathPtr);
228 : char16_t* fromPathPtr;
229 0 : fromPath.BeginWriting(fromPathPtr);
230 :
231 0 : SplitPath(thisPathPtr, thisNodes);
232 0 : SplitPath(fromPathPtr, fromNodes);
233 :
234 : size_t nodeIndex;
235 0 : for (nodeIndex = 0;
236 0 : nodeIndex < thisNodes.Length() && nodeIndex < fromNodes.Length();
237 : ++nodeIndex) {
238 : #ifdef XP_WIN
239 : if (_wcsicmp(char16ptr_t(thisNodes[nodeIndex]),
240 : char16ptr_t(fromNodes[nodeIndex]))) {
241 : break;
242 : }
243 : #else
244 0 : if (nsCRT::strcmp(thisNodes[nodeIndex], fromNodes[nodeIndex])) {
245 0 : break;
246 : }
247 : #endif
248 : }
249 :
250 0 : size_t branchIndex = nodeIndex;
251 0 : for (nodeIndex = branchIndex; nodeIndex < fromNodes.Length(); ++nodeIndex) {
252 0 : aResult.AppendLiteral("../");
253 : }
254 0 : for (nodeIndex = branchIndex; nodeIndex < thisNodes.Length(); ++nodeIndex) {
255 0 : NS_ConvertUTF16toUTF8 nodeStr(thisNodes[nodeIndex]);
256 0 : aResult.Append(nodeStr);
257 0 : if (nodeIndex + 1 < thisNodes.Length()) {
258 0 : aResult.Append('/');
259 : }
260 : }
261 :
262 0 : return NS_OK;
263 : }
264 :
265 : NS_IMETHODIMP
266 2 : nsLocalFile::SetRelativeDescriptor(nsIFile* aFromFile,
267 : const nsACString& aRelativeDesc)
268 : {
269 2 : NS_NAMED_LITERAL_CSTRING(kParentDirStr, "../");
270 :
271 4 : nsCOMPtr<nsIFile> targetFile;
272 2 : nsresult rv = aFromFile->Clone(getter_AddRefs(targetFile));
273 2 : if (NS_FAILED(rv)) {
274 0 : return rv;
275 : }
276 :
277 : //
278 : // aRelativeDesc is UTF-8 encoded
279 : //
280 :
281 2 : nsCString::const_iterator strBegin, strEnd;
282 2 : aRelativeDesc.BeginReading(strBegin);
283 2 : aRelativeDesc.EndReading(strEnd);
284 :
285 2 : nsCString::const_iterator nodeBegin(strBegin), nodeEnd(strEnd);
286 2 : nsCString::const_iterator pos(strBegin);
287 :
288 4 : nsCOMPtr<nsIFile> parentDir;
289 2 : while (FindInReadable(kParentDirStr, nodeBegin, nodeEnd)) {
290 0 : rv = targetFile->GetParent(getter_AddRefs(parentDir));
291 0 : if (NS_FAILED(rv)) {
292 0 : return rv;
293 : }
294 0 : if (!parentDir) {
295 0 : return NS_ERROR_FILE_UNRECOGNIZED_PATH;
296 : }
297 0 : targetFile = parentDir;
298 :
299 0 : nodeBegin = nodeEnd;
300 0 : pos = nodeEnd;
301 0 : nodeEnd = strEnd;
302 : }
303 :
304 2 : nodeBegin = nodeEnd = pos;
305 6 : while (nodeEnd != strEnd) {
306 2 : FindCharInReadable('/', nodeEnd, strEnd);
307 2 : targetFile->Append(NS_ConvertUTF8toUTF16(Substring(nodeBegin, nodeEnd)));
308 2 : if (nodeEnd != strEnd) { // If there's more left in the string, inc over the '/' nodeEnd is on.
309 0 : ++nodeEnd;
310 : }
311 2 : nodeBegin = nodeEnd;
312 : }
313 :
314 2 : return InitWithFile(targetFile);
315 : }
316 :
317 : NS_IMETHODIMP
318 0 : nsLocalFile::GetRelativePath(nsIFile* aFromFile, nsACString& aResult)
319 : {
320 0 : return GetRelativeDescriptor(aFromFile, aResult);
321 : }
322 :
323 : NS_IMETHODIMP
324 0 : nsLocalFile::SetRelativePath(nsIFile* aFromFile,
325 : const nsACString& aRelativePath)
326 : {
327 0 : return SetRelativeDescriptor(aFromFile, aRelativePath);
328 9 : }
|