1 : /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1999
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Doug Turner <dougt@netscape.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 : #include "nsIServiceManager.h"
39 :
40 : #include "nsLocalFile.h" // includes platform-specific headers
41 :
42 : #include "nsString.h"
43 : #include "nsCOMPtr.h"
44 : #include "nsReadableUtils.h"
45 : #include "nsPrintfCString.h"
46 : #include "nsCRT.h"
47 : #include "nsNativeCharsetUtils.h"
48 : #include "nsUTF8Utils.h"
49 :
50 : #ifdef XP_WIN
51 : #include <string.h>
52 : #endif
53 :
54 :
55 1419 : void NS_StartupLocalFile()
56 : {
57 1419 : nsLocalFile::GlobalInit();
58 1419 : }
59 :
60 1419 : void NS_ShutdownLocalFile()
61 : {
62 1419 : nsLocalFile::GlobalShutdown();
63 1419 : }
64 :
65 : #if !defined(MOZ_WIDGET_COCOA) && !defined(XP_WIN)
66 : NS_IMETHODIMP
67 940 : nsLocalFile::InitWithFile(nsILocalFile *aFile)
68 : {
69 940 : NS_ENSURE_ARG(aFile);
70 :
71 1880 : nsCAutoString path;
72 940 : aFile->GetNativePath(path);
73 940 : if (path.IsEmpty())
74 0 : return NS_ERROR_INVALID_ARG;
75 940 : return InitWithNativePath(path);
76 : }
77 : #endif
78 :
79 : #define kMaxFilenameLength 255
80 : #define kMaxExtensionLength 100
81 : #define kMaxSequenceNumberLength 5 // "-9999"
82 : // requirement: kMaxExtensionLength < kMaxFilenameLength - kMaxSequenceNumberLength
83 :
84 : NS_IMETHODIMP
85 1231 : nsLocalFile::CreateUnique(PRUint32 type, PRUint32 attributes)
86 : {
87 : nsresult rv;
88 : bool longName;
89 :
90 : #ifdef XP_WIN
91 : nsAutoString pathName, leafName, rootName, suffix;
92 : rv = GetPath(pathName);
93 : #else
94 2462 : nsCAutoString pathName, leafName, rootName, suffix;
95 1231 : rv = GetNativePath(pathName);
96 : #endif
97 1231 : if (NS_FAILED(rv))
98 0 : return rv;
99 :
100 1231 : longName = (pathName.Length() + kMaxSequenceNumberLength >
101 1231 : kMaxFilenameLength);
102 1231 : if (!longName)
103 : {
104 1228 : rv = Create(type, attributes);
105 1228 : if (rv != NS_ERROR_FILE_ALREADY_EXISTS)
106 405 : return rv;
107 : }
108 :
109 : #ifdef XP_WIN
110 : rv = GetLeafName(leafName);
111 : if (NS_FAILED(rv))
112 : return rv;
113 :
114 : const PRInt32 lastDot = leafName.RFindChar(PRUnichar('.'));
115 : #else
116 826 : rv = GetNativeLeafName(leafName);
117 826 : if (NS_FAILED(rv))
118 0 : return rv;
119 :
120 826 : const PRInt32 lastDot = leafName.RFindChar('.');
121 : #endif
122 :
123 826 : if (lastDot == kNotFound)
124 : {
125 12 : rootName = leafName;
126 : }
127 : else
128 : {
129 814 : suffix = Substring(leafName, lastDot); // include '.'
130 814 : rootName = Substring(leafName, 0, lastDot); // strip suffix and dot
131 : }
132 :
133 826 : if (longName)
134 : {
135 : PRInt32 maxRootLength = (kMaxFilenameLength -
136 3 : (pathName.Length() - leafName.Length()) -
137 3 : suffix.Length() - kMaxSequenceNumberLength);
138 :
139 : // We cannot create an item inside a directory whose name is too long.
140 : // Also, ensure that at least one character remains after we truncate
141 : // the root name, as we don't want to end up with an empty leaf name.
142 3 : if (maxRootLength < 2)
143 2 : return NS_ERROR_FILE_UNRECOGNIZED_PATH;
144 :
145 : #ifdef XP_WIN
146 : // ensure that we don't cut the name in mid-UTF16-character
147 : rootName.SetLength(NS_IS_LOW_SURROGATE(rootName[maxRootLength]) ?
148 : maxRootLength - 1 : maxRootLength);
149 : SetLeafName(rootName + suffix);
150 : #else
151 1 : if (NS_IsNativeUTF8())
152 : {
153 : // ensure that we don't cut the name in mid-UTF8-character
154 : // (assume the name is valid UTF8 to begin with)
155 2 : while (UTF8traits::isInSeq(rootName[maxRootLength]))
156 0 : --maxRootLength;
157 :
158 : // Another check to avoid ending up with an empty leaf name.
159 1 : if (maxRootLength == 0 && suffix.IsEmpty())
160 0 : return NS_ERROR_FILE_UNRECOGNIZED_PATH;
161 : }
162 :
163 1 : rootName.SetLength(maxRootLength);
164 1 : SetNativeLeafName(rootName + suffix);
165 : #endif
166 1 : nsresult rv = Create(type, attributes);
167 1 : if (rv != NS_ERROR_FILE_ALREADY_EXISTS)
168 1 : return rv;
169 : }
170 :
171 1549 : for (int indx = 1; indx < 10000; indx++)
172 : {
173 : // start with "Picture-1.jpg" after "Picture.jpg" exists
174 : #ifdef XP_WIN
175 : SetLeafName(rootName +
176 : NS_ConvertASCIItoUTF16(nsPrintfCString("-%d", indx)) +
177 : suffix);
178 : #else
179 1549 : SetNativeLeafName(rootName + nsPrintfCString("-%d", indx) + suffix);
180 : #endif
181 1549 : rv = Create(type, attributes);
182 1549 : if (NS_SUCCEEDED(rv) || rv != NS_ERROR_FILE_ALREADY_EXISTS)
183 823 : return rv;
184 : }
185 :
186 : // The disk is full, sort of
187 0 : return NS_ERROR_FILE_TOO_BIG;
188 : }
189 :
190 : #if defined(XP_WIN) || defined(XP_OS2)
191 : static const PRUnichar kPathSeparatorChar = '\\';
192 : #elif defined(XP_UNIX)
193 : static const PRUnichar kPathSeparatorChar = '/';
194 : #else
195 : #error Need to define file path separator for your platform
196 : #endif
197 :
198 2 : static PRInt32 SplitPath(PRUnichar *path, PRUnichar **nodeArray, PRInt32 arrayLen)
199 : {
200 2 : if (*path == 0)
201 0 : return 0;
202 :
203 2 : PRUnichar **nodePtr = nodeArray;
204 2 : if (*path == kPathSeparatorChar)
205 2 : path++;
206 2 : *nodePtr++ = path;
207 :
208 66 : for (PRUnichar *cp = path; *cp != 0; cp++) {
209 64 : if (*cp == kPathSeparatorChar) {
210 5 : *cp++ = 0;
211 5 : if (*cp == 0)
212 0 : break;
213 5 : if (nodePtr - nodeArray >= arrayLen)
214 0 : return -1;
215 5 : *nodePtr++ = cp;
216 : }
217 : }
218 2 : return nodePtr - nodeArray;
219 : }
220 :
221 :
222 : NS_IMETHODIMP
223 1 : nsLocalFile::GetRelativeDescriptor(nsILocalFile *fromFile, nsACString& _retval)
224 : {
225 1 : NS_ENSURE_ARG_POINTER(fromFile);
226 1 : const PRInt32 kMaxNodesInPath = 32;
227 :
228 : //
229 : // _retval will be UTF-8 encoded
230 : //
231 :
232 : nsresult rv;
233 1 : _retval.Truncate(0);
234 :
235 2 : nsAutoString thisPath, fromPath;
236 : PRUnichar *thisNodes[kMaxNodesInPath], *fromNodes[kMaxNodesInPath];
237 : PRInt32 thisNodeCnt, fromNodeCnt, nodeIndex;
238 :
239 1 : rv = GetPath(thisPath);
240 1 : if (NS_FAILED(rv))
241 0 : return rv;
242 1 : rv = fromFile->GetPath(fromPath);
243 1 : if (NS_FAILED(rv))
244 0 : return rv;
245 :
246 : // get raw pointer to mutable string buffer
247 1 : PRUnichar *thisPathPtr; thisPath.BeginWriting(thisPathPtr);
248 1 : PRUnichar *fromPathPtr; fromPath.BeginWriting(fromPathPtr);
249 :
250 1 : thisNodeCnt = SplitPath(thisPathPtr, thisNodes, kMaxNodesInPath);
251 1 : fromNodeCnt = SplitPath(fromPathPtr, fromNodes, kMaxNodesInPath);
252 1 : if (thisNodeCnt < 0 || fromNodeCnt < 0)
253 0 : return NS_ERROR_FAILURE;
254 :
255 3 : for (nodeIndex = 0; nodeIndex < thisNodeCnt && nodeIndex < fromNodeCnt; ++nodeIndex) {
256 : #ifdef XP_WIN
257 : if (_wcsicmp(thisNodes[nodeIndex], fromNodes[nodeIndex]))
258 : break;
259 : #else
260 3 : if (nsCRT::strcmp(thisNodes[nodeIndex], fromNodes[nodeIndex]))
261 1 : break;
262 : #endif
263 : }
264 :
265 1 : PRInt32 branchIndex = nodeIndex;
266 2 : for (nodeIndex = branchIndex; nodeIndex < fromNodeCnt; nodeIndex++)
267 1 : _retval.AppendLiteral("../");
268 3 : for (nodeIndex = branchIndex; nodeIndex < thisNodeCnt; nodeIndex++) {
269 4 : NS_ConvertUTF16toUTF8 nodeStr(thisNodes[nodeIndex]);
270 2 : _retval.Append(nodeStr);
271 2 : if (nodeIndex + 1 < thisNodeCnt)
272 1 : _retval.Append('/');
273 : }
274 :
275 1 : return NS_OK;
276 : }
277 :
278 : NS_IMETHODIMP
279 1 : nsLocalFile::SetRelativeDescriptor(nsILocalFile *fromFile, const nsACString& relativeDesc)
280 : {
281 2 : NS_NAMED_LITERAL_CSTRING(kParentDirStr, "../");
282 :
283 2 : nsCOMPtr<nsIFile> targetFile;
284 1 : nsresult rv = fromFile->Clone(getter_AddRefs(targetFile));
285 1 : if (NS_FAILED(rv))
286 0 : return rv;
287 :
288 : //
289 : // relativeDesc is UTF-8 encoded
290 : //
291 :
292 1 : nsCString::const_iterator strBegin, strEnd;
293 1 : relativeDesc.BeginReading(strBegin);
294 1 : relativeDesc.EndReading(strEnd);
295 :
296 1 : nsCString::const_iterator nodeBegin(strBegin), nodeEnd(strEnd);
297 1 : nsCString::const_iterator pos(strBegin);
298 :
299 2 : nsCOMPtr<nsIFile> parentDir;
300 3 : while (FindInReadable(kParentDirStr, nodeBegin, nodeEnd)) {
301 1 : rv = targetFile->GetParent(getter_AddRefs(parentDir));
302 1 : if (NS_FAILED(rv))
303 0 : return rv;
304 1 : if (!parentDir)
305 0 : return NS_ERROR_FILE_UNRECOGNIZED_PATH;
306 1 : targetFile = parentDir;
307 :
308 1 : nodeBegin = nodeEnd;
309 1 : pos = nodeEnd;
310 1 : nodeEnd = strEnd;
311 : }
312 :
313 1 : nodeBegin = nodeEnd = pos;
314 4 : while (nodeEnd != strEnd) {
315 2 : FindCharInReadable('/', nodeEnd, strEnd);
316 2 : targetFile->Append(NS_ConvertUTF8toUTF16(Substring(nodeBegin, nodeEnd)));
317 2 : if (nodeEnd != strEnd) // If there's more left in the string, inc over the '/' nodeEnd is on.
318 1 : ++nodeEnd;
319 2 : nodeBegin = nodeEnd;
320 : }
321 :
322 2 : nsCOMPtr<nsILocalFile> targetLocalFile(do_QueryInterface(targetFile));
323 1 : return InitWithFile(targetLocalFile);
324 : }
|