Bug 376959, follow symlinks when doing Repack verification, r=preed
[mozilla-1.9.git] / xpcom / obsolete / nsFileSpec.cpp
blobaca54a688ff6d4afb792bcb1855e42fe6241d18f
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
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/
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.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include "nsFileSpec.h"
40 #include "nsDebug.h"
41 #include "nsEscape.h"
42 #include "nsMemory.h"
44 #include "prtypes.h"
45 #include "plstr.h"
46 #include "plbase64.h"
47 #include "prmem.h"
49 #include "nsCOMPtr.h"
50 #include "nsIServiceManager.h"
51 #include "nsILocalFile.h"
53 #include <string.h>
54 #include <stdio.h>
56 #if defined(XP_WIN)
57 #include <mbstring.h>
58 #endif
60 #ifdef XP_OS2
61 extern unsigned char* _mbsrchr( const unsigned char*, int);
62 #endif
64 // return pointer to last instance of the given separator
65 static inline char *GetLastSeparator(const char *str, char sep)
67 #if defined(XP_WIN) || defined(XP_OS2)
68 return (char*) _mbsrchr((const unsigned char *) str, sep);
69 #else
70 return (char*) strrchr(str, sep);
71 #endif
74 #ifdef XP_MACOSX
75 #include <sys/stat.h>
76 #include <Aliases.h>
77 #include <TextUtils.h>
78 #endif
80 //========================================================================================
81 // class nsSimpleCharString
82 //========================================================================================
84 //----------------------------------------------------------------------------------------
85 nsSimpleCharString::nsSimpleCharString()
86 //----------------------------------------------------------------------------------------
87 : mData(nsnull)
90 } // nsSimpleCharString::nsSimpleCharString
92 //----------------------------------------------------------------------------------------
93 nsSimpleCharString::nsSimpleCharString(const char* inString)
94 //----------------------------------------------------------------------------------------
95 : mData(nsnull)
97 if (inString)
98 CopyFrom(inString, strlen(inString));
99 } // nsSimpleCharString::nsSimpleCharString
101 //----------------------------------------------------------------------------------------
102 nsSimpleCharString::nsSimpleCharString(const nsString& inString)
103 //----------------------------------------------------------------------------------------
104 : mData(nsnull)
106 *this = inString;
107 } // nsSimpleCharString::nsSimpleCharString
109 //----------------------------------------------------------------------------------------
110 nsSimpleCharString::nsSimpleCharString(const nsSimpleCharString& inOther)
111 //----------------------------------------------------------------------------------------
113 mData = inOther.mData;
114 AddRefData();
115 } // nsSimpleCharString::nsSimpleCharString
117 //----------------------------------------------------------------------------------------
118 nsSimpleCharString::nsSimpleCharString(const char* inData, PRUint32 inLength)
119 //----------------------------------------------------------------------------------------
120 : mData(nsnull)
122 CopyFrom(inData, inLength);
123 } // nsSimpleCharString::nsSimpleCharString
125 //----------------------------------------------------------------------------------------
126 nsSimpleCharString::~nsSimpleCharString()
127 //----------------------------------------------------------------------------------------
129 ReleaseData();
130 } // nsSimpleCharString::nsSimpleCharString
132 //----------------------------------------------------------------------------------------
133 void nsSimpleCharString::operator = (const char* inString)
134 //----------------------------------------------------------------------------------------
136 if (inString)
137 CopyFrom(inString, strlen(inString));
138 else
139 SetToEmpty();
140 } // nsSimpleCharString::operator =
142 //----------------------------------------------------------------------------------------
143 void nsSimpleCharString::operator = (const nsString& inString)
144 //----------------------------------------------------------------------------------------
146 PRUint32 len = inString.Length();
147 ReallocData(len);
148 if (!mData)
149 return;
150 nsFixedCString dataString(mData->mString, len + 1);
151 LossyCopyUTF16toASCII(inString, dataString);
152 NS_ASSERTION(dataString.get() == mData->mString, "buffer too small");
153 } // nsSimpleCharString::operator =
155 //----------------------------------------------------------------------------------------
156 void nsSimpleCharString::operator = (const nsSimpleCharString& inOther)
157 //----------------------------------------------------------------------------------------
159 if (mData == inOther.mData)
160 return;
161 ReleaseData();
162 mData = inOther.mData;
163 AddRefData();
164 } // nsSimpleCharString::operator =
166 //----------------------------------------------------------------------------------------
167 void nsSimpleCharString::operator += (const char* inOther)
168 //----------------------------------------------------------------------------------------
170 if (!inOther)
171 return;
172 int newLength = Length() + strlen(inOther);
173 ReallocData(newLength);
174 strcat(mData->mString, inOther);
175 } // nsSimpleCharString::operator =
177 //----------------------------------------------------------------------------------------
178 nsSimpleCharString nsSimpleCharString::operator + (const char* inOther) const
179 //----------------------------------------------------------------------------------------
181 nsSimpleCharString result(*this);
182 result += inOther;
183 return result;
184 } // nsSimpleCharString::operator =
186 //----------------------------------------------------------------------------------------
187 void nsSimpleCharString::Catenate(const char* inString1, const char* inString2)
188 //----------------------------------------------------------------------------------------
190 if (!inString2)
192 *this += inString1;
193 return;
195 int newLength = Length() + strlen(inString1) + strlen(inString2);
196 ReallocData(newLength);
197 strcat(mData->mString, inString1);
198 strcat(mData->mString, inString2);
199 } // nsSimpleCharString::operator =
201 //----------------------------------------------------------------------------------------
202 void nsSimpleCharString::CopyFrom(const char* inData, PRUint32 inLength)
203 //----------------------------------------------------------------------------------------
205 if (!inData)
206 return;
207 ReallocData(inLength);
208 if (!mData)
209 return;
210 if (inLength != 0) {
211 memcpy(mData->mString, inData, inLength);
213 mData->mString[inLength] = '\0';
214 } // nsSimpleCharString::CopyFrom
216 //----------------------------------------------------------------------------------------
217 void nsSimpleCharString::SetToEmpty()
218 //----------------------------------------------------------------------------------------
220 ReleaseData();
221 } // nsSimpleCharString::SetToEmpty
223 //----------------------------------------------------------------------------------------
224 void nsSimpleCharString::Unescape()
225 //----------------------------------------------------------------------------------------
227 if (!mData)
228 return;
229 ReallocData(mData->mLength);
230 if (!mData)
231 return;
232 nsUnescape(mData->mString);
233 mData->mLength = strlen(mData->mString);
234 } // nsSimpleCharString::Unescape
237 //----------------------------------------------------------------------------------------
238 void nsSimpleCharString::AddRefData()
239 //----------------------------------------------------------------------------------------
241 if (mData)
242 ++mData->mRefCount;
243 } // nsSimpleCharString::AddRefData
245 //----------------------------------------------------------------------------------------
246 void nsSimpleCharString::ReleaseData()
247 //----------------------------------------------------------------------------------------
249 if (!mData)
250 return;
251 NS_ASSERTION(mData->mRefCount > 0, "String deleted too many times!");
252 if (--mData->mRefCount == 0)
253 PR_Free(mData);
254 mData = nsnull;
255 } // nsSimpleCharString::ReleaseData
257 //----------------------------------------------------------------------------------------
258 inline PRUint32 CalculateAllocLength(PRUint32 logicalLength)
259 // Round up to the next multiple of 256.
260 //----------------------------------------------------------------------------------------
262 return ((1 + (logicalLength >> 8)) << 8);
265 //----------------------------------------------------------------------------------------
266 void nsSimpleCharString::ReallocData(PRUint32 inLength)
267 // Reallocate mData to a new length. Since this presumably precedes a change to the string,
268 // we want to detach ourselves if the data is shared by another string, even if the length
269 // requested would not otherwise require a reallocation.
270 //----------------------------------------------------------------------------------------
272 PRUint32 newAllocLength = CalculateAllocLength(inLength);
273 PRUint32 oldAllocLength = CalculateAllocLength(Length());
274 if (mData)
276 NS_ASSERTION(mData->mRefCount > 0, "String deleted too many times!");
277 if (mData->mRefCount == 1)
279 // We are the sole owner, so just change its length, if necessary.
280 if (newAllocLength > oldAllocLength)
281 mData = (Data*)PR_Realloc(mData, newAllocLength + sizeof(Data));
282 mData->mLength = inLength;
283 mData->mString[inLength] = '\0'; // we may be truncating
284 return;
287 PRUint32 copyLength = Length();
288 if (inLength < copyLength)
289 copyLength = inLength;
290 Data* newData = (Data*)PR_Malloc(newAllocLength + sizeof(Data));
291 // If data was already allocated when we get to here, then we are cloning the data
292 // from a shared pointer.
293 if (mData)
295 memcpy(newData, mData, sizeof(Data) + copyLength);
296 mData->mRefCount--; // Say goodbye
298 else
299 newData->mString[0] = '\0';
301 mData = newData;
302 mData->mRefCount = 1;
303 mData->mLength = inLength;
304 } // nsSimpleCharString::ReleaseData
307 //========================================================================================
308 NS_NAMESPACE nsFileSpecHelpers
309 //========================================================================================
311 enum
312 { kMaxFilenameLength = 31 // should work on Macintosh, Unix, and Win32.
313 , kMaxAltDigitLength = 5
314 , kMaxCoreLeafNameLength = (kMaxFilenameLength - (kMaxAltDigitLength + 1))
316 NS_NAMESPACE_PROTOTYPE void Canonify(nsSimpleCharString& ioPath, PRBool inMakeDirs);
317 NS_NAMESPACE_PROTOTYPE void MakeAllDirectories(const char* inPath, int mode);
318 #if defined(XP_WIN) || defined(XP_OS2)
319 NS_NAMESPACE_PROTOTYPE void NativeToUnix(nsSimpleCharString& ioPath);
320 NS_NAMESPACE_PROTOTYPE void UnixToNative(nsSimpleCharString& ioPath);
321 #endif
322 } NS_NAMESPACE_END
324 //----------------------------------------------------------------------------------------
325 nsresult ns_file_convert_result(PRInt32 nativeErr)
326 //----------------------------------------------------------------------------------------
328 return nativeErr ?
329 NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES,((nativeErr)&0xFFFF))
330 : NS_OK;
333 //----------------------------------------------------------------------------------------
334 void nsSimpleCharString::LeafReplace(char inSeparator, const char* inLeafName)
335 //----------------------------------------------------------------------------------------
337 // Find the existing leaf name
338 if (IsEmpty())
339 return;
340 if (!inLeafName)
342 SetToEmpty();
343 return;
345 char* chars = mData->mString;
346 char* lastSeparator = GetLastSeparator(chars, inSeparator);
347 int oldLength = Length();
348 PRBool trailingSeparator = (lastSeparator + 1 == chars + oldLength);
349 if (trailingSeparator)
351 char savedCh = *lastSeparator;
352 char *savedLastSeparator = lastSeparator;
353 *lastSeparator = '\0';
354 lastSeparator = GetLastSeparator(chars, inSeparator);
355 *savedLastSeparator = savedCh;
357 if (lastSeparator)
358 lastSeparator++; // point at the trailing string
359 else
360 lastSeparator = chars; // the full monty
362 PRUint32 savedLastSeparatorOffset = (lastSeparator - chars);
363 int newLength =
364 (lastSeparator - chars) + strlen(inLeafName) + (trailingSeparator != 0);
365 ReallocData(newLength);
367 chars = mData->mString; // it might have moved.
368 chars[savedLastSeparatorOffset] = '\0'; // strip the current leaf name
370 strcat(chars, inLeafName);
371 if (trailingSeparator)
373 // If the original ended in a slash, then the new one should, too.
374 char sepStr[2] = "/";
375 *sepStr = inSeparator;
376 strcat(chars, sepStr);
378 } // nsSimpleCharString::LeafReplace
380 //----------------------------------------------------------------------------------------
381 char* nsSimpleCharString::GetLeaf(char inSeparator) const
382 // Returns a pointer to an allocated string representing the leaf.
383 //----------------------------------------------------------------------------------------
385 if (IsEmpty())
386 return nsnull;
388 char* chars = mData->mString;
389 const char* lastSeparator = GetLastSeparator(chars, inSeparator);
390 // If there was no separator, then return a copy of our path.
391 if (!lastSeparator)
392 return nsCRT::strdup(*this);
394 // So there's at least one separator. What's just after it?
395 // If the separator was not the last character, return the trailing string.
396 const char* leafPointer = lastSeparator + 1;
397 if (*leafPointer)
398 return nsCRT::strdup(leafPointer);
400 // So now, separator was the last character. Poke in a null instead.
401 *(char*)lastSeparator = '\0'; // Should use const_cast, but Unix has old compiler.
402 leafPointer = GetLastSeparator(chars, inSeparator);
403 char* result = leafPointer ? nsCRT::strdup(++leafPointer) : nsCRT::strdup(chars);
404 // Restore the poked null before returning.
405 *(char*)lastSeparator = inSeparator;
406 #if defined(XP_WIN) || defined(XP_OS2)
407 // If it's a drive letter use the colon notation.
408 if (!leafPointer && result[1] == '|' && result[2] == 0)
409 result[1] = ':';
410 #endif
411 return result;
412 } // nsSimpleCharString::GetLeaf
414 #if (defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS))
416 //----------------------------------------------------------------------------------------
417 void nsFileSpecHelpers::MakeAllDirectories(const char* inPath, int mode)
418 // Make the path a valid one by creating all the intermediate directories. Does NOT
419 // make the leaf into a directory. This should be a unix path.
420 //----------------------------------------------------------------------------------------
422 if (!inPath)
423 return;
425 char* pathCopy = nsCRT::strdup( inPath );
426 if (!pathCopy)
427 return;
429 const char kSeparator = '/'; // I repeat: this should be a unix-style path.
430 const int kSkipFirst = 1;
432 #if defined(XP_WIN) || defined(XP_OS2)
433 // Either this is a relative path, or we ensure that it has
434 // a drive letter specifier.
435 NS_ASSERTION( pathCopy[0] != '/' || (pathCopy[1] && (pathCopy[2] == '|' || pathCopy[2] == '/')),
436 "Not a UNC path and no drive letter!" );
437 #endif
438 char* currentStart = pathCopy;
439 char* currentEnd = strchr(currentStart + kSkipFirst, kSeparator);
440 if (currentEnd)
442 nsFileSpec spec;
443 *currentEnd = '\0';
445 #if defined(XP_WIN) || defined(XP_OS2)
447 if we have a drive letter path, we must make sure that the inital path has a '/' on it, or
448 Canonify will turn "/c|" into a path relative to the running executable.
450 if (pathCopy[0] == '/' && pathCopy[1] && pathCopy[2] == '|')
452 char* startDir = (char*)PR_Malloc(strlen(pathCopy) + 2);
453 strcpy(startDir, pathCopy);
454 strcat(startDir, "/");
456 spec = nsFilePath(startDir, PR_FALSE);
458 PR_Free(startDir);
460 else
462 // move after server name and share name in UNC path
463 if (pathCopy[0] == '/' &&
464 currentEnd == currentStart+kSkipFirst)
466 *currentEnd = '/';
467 currentStart = strchr(pathCopy+2, kSeparator);
468 currentStart = strchr(currentStart+1, kSeparator);
469 currentEnd = strchr(currentStart+1, kSeparator);
470 if (currentEnd)
471 *currentEnd = '\0';
473 spec = nsFilePath(pathCopy, PR_FALSE);
475 #else
476 spec = nsFilePath(pathCopy, PR_FALSE);
477 #endif
480 // If the node doesn't exist, and it is not the initial node in a full path,
481 // then make a directory (We cannot make the initial (volume) node).
482 if (!spec.Exists() && *currentStart != kSeparator)
483 spec.CreateDirectory(mode);
485 currentStart = ++currentEnd;
486 currentEnd = strchr(currentStart, kSeparator);
487 if (!currentEnd)
488 break;
490 *currentEnd = '\0';
492 spec += currentStart; // "lengthen" the path, adding the next node.
493 } while (currentEnd);
495 nsCRT::free(pathCopy);
496 } // nsFileSpecHelpers::MakeAllDirectories
498 #endif // XP_UNIX || XP_WIN || XP_OS2 || XP_BEOS
500 #if defined(XP_WIN)
501 #include "nsFileSpecWin.cpp" // Windows-specific implementations
502 #elif defined(XP_BEOS)
503 #include "nsFileSpecBeOS.cpp" // BeOS-specific implementations
504 #elif defined(XP_UNIX)
505 #include "nsFileSpecUnix.cpp" // Unix-specific implementations
506 #elif defined(XP_OS2)
507 #include "nsFileSpecOS2.cpp" // OS/2-specific implementations
508 #endif
510 //========================================================================================
511 // nsFileURL implementation
512 //========================================================================================
514 //----------------------------------------------------------------------------------------
515 nsFileURL::nsFileURL(const char* inString, PRBool inCreateDirs)
516 //----------------------------------------------------------------------------------------
518 if (!inString)
519 return;
520 NS_ASSERTION(strstr(inString, kFileURLPrefix) == inString, "Not a URL!");
521 // Make canonical and absolute. Since it's a parameter to this constructor,
522 // inString is escaped. We want to make an nsFilePath, which requires
523 // an unescaped string.
524 nsSimpleCharString unescapedPath(inString + kFileURLPrefixLength);
525 unescapedPath.Unescape();
526 nsFilePath path(unescapedPath, inCreateDirs);
527 *this = path;
528 } // nsFileURL::nsFileURL
530 //----------------------------------------------------------------------------------------
531 nsFileURL::nsFileURL(const nsString& inString, PRBool inCreateDirs)
532 //----------------------------------------------------------------------------------------
534 NS_LossyConvertUTF16toASCII cstring(inString);
535 if (!inString.Length())
536 return;
537 NS_ASSERTION(strstr(cstring.get(), kFileURLPrefix) == cstring.get(),
538 "Not a URL!");
539 // Make canonical and absolute. Since it's a parameter to this constructor,
540 // inString is escaped. We want to make an nsFilePath, which requires
541 // an unescaped string.
542 nsSimpleCharString unescapedPath(cstring.get() + kFileURLPrefixLength);
543 unescapedPath.Unescape();
544 nsFilePath path(unescapedPath, inCreateDirs);
545 *this = path;
546 } // nsFileURL::nsFileURL
548 //----------------------------------------------------------------------------------------
549 nsFileURL::nsFileURL(const nsFileURL& inOther)
550 //----------------------------------------------------------------------------------------
551 : mURL(inOther.mURL)
553 } // nsFileURL::nsFileURL
555 //----------------------------------------------------------------------------------------
556 nsFileURL::nsFileURL(const nsFilePath& inOther)
557 //----------------------------------------------------------------------------------------
559 *this = inOther;
560 } // nsFileURL::nsFileURL
562 //----------------------------------------------------------------------------------------
563 nsFileURL::nsFileURL(const nsFileSpec& inOther)
564 //----------------------------------------------------------------------------------------
566 *this = inOther;
567 } // nsFileURL::nsFileURL
569 //----------------------------------------------------------------------------------------
570 nsFileURL::~nsFileURL()
571 //----------------------------------------------------------------------------------------
575 //----------------------------------------------------------------------------------------
576 void nsFileURL::operator = (const char* inString)
577 //----------------------------------------------------------------------------------------
579 // XXX is this called by nsFileSpecImpl.cpp::SetURLString?
580 // if so, there's a bug...
582 mURL = inString;
583 NS_ASSERTION(strstr(inString, kFileURLPrefix) == inString, "Not a URL!");
584 } // nsFileURL::operator =
586 //----------------------------------------------------------------------------------------
587 void nsFileURL::operator +=(const char* inRelativeUnixPath)
588 //----------------------------------------------------------------------------------------
590 char* escapedPath = nsEscape(inRelativeUnixPath, url_Path);
591 mURL += escapedPath;
592 nsCRT::free(escapedPath);
593 } // nsFileURL::operator +=
595 //----------------------------------------------------------------------------------------
596 nsFileURL nsFileURL::operator +(const char* inRelativeUnixPath) const
597 //----------------------------------------------------------------------------------------
599 nsFileURL result(*this);
600 result += inRelativeUnixPath;
601 return result;
602 } // nsFileURL::operator +
604 //----------------------------------------------------------------------------------------
605 void nsFileURL::operator = (const nsFileURL& inOther)
606 //----------------------------------------------------------------------------------------
608 mURL = inOther.mURL;
609 } // nsFileURL::operator =
611 //----------------------------------------------------------------------------------------
612 void nsFileURL::operator = (const nsFilePath& inOther)
613 //----------------------------------------------------------------------------------------
615 mURL = kFileURLPrefix;
616 char* original = (char*)(const char*)inOther; // we shall modify, but restore.
617 if (!original || !*original) return;
618 #if defined(XP_WIN) || defined(XP_OS2)
619 // because we don't want to escape the '|' character, change it to a letter.
620 // Note that a UNC path will not have a '|' character.
621 char oldchar = original[2];
622 original[2] = 'x';
623 char* escapedPath = nsEscape(original, url_Path);
624 original[2] = escapedPath[2] = oldchar; // restore it
625 #else
626 char* escapedPath = nsEscape(original, url_Path);
627 #endif
628 if (escapedPath)
629 mURL += escapedPath;
630 nsCRT::free(escapedPath);
631 } // nsFileURL::operator =
633 //----------------------------------------------------------------------------------------
634 void nsFileURL::operator = (const nsFileSpec& inOther)
635 //----------------------------------------------------------------------------------------
637 *this = nsFilePath(inOther);
638 if (mURL[mURL.Length() - 1] != '/' && inOther.IsDirectory())
639 mURL += "/";
640 } // nsFileURL::operator =
643 //========================================================================================
644 // nsFilePath implementation
645 //========================================================================================
647 //----------------------------------------------------------------------------------------
648 nsFilePath::nsFilePath(const nsFilePath& inPath)
649 //----------------------------------------------------------------------------------------
650 : mPath(inPath.mPath)
654 //----------------------------------------------------------------------------------------
655 nsFilePath::nsFilePath(const char* inString, PRBool inCreateDirs)
656 //----------------------------------------------------------------------------------------
657 : mPath(inString)
659 if (mPath.IsEmpty())
660 return;
662 NS_ASSERTION(strstr(inString, kFileURLPrefix) != inString, "URL passed as path");
664 #if defined(XP_WIN) || defined(XP_OS2)
665 nsFileSpecHelpers::UnixToNative(mPath);
666 #endif
667 // Make canonical and absolute.
668 nsFileSpecHelpers::Canonify(mPath, inCreateDirs);
669 #if defined(XP_WIN) || defined(XP_OS2)
670 // Assert native path is of one of these forms:
671 // - regular: X:\some\path
672 // - UNC: \\some_machine\some\path
673 NS_ASSERTION( mPath[1] == ':' || (mPath[0] == '\\' && mPath[1] == '\\'),
674 "unexpected canonical path" );
675 nsFileSpecHelpers::NativeToUnix(mPath);
676 #endif
679 //----------------------------------------------------------------------------------------
680 nsFilePath::nsFilePath(const nsString& inString, PRBool inCreateDirs)
681 //----------------------------------------------------------------------------------------
682 : mPath(inString)
684 if (mPath.IsEmpty())
685 return;
687 NS_ASSERTION(strstr((const char*)mPath, kFileURLPrefix) != (const char*)mPath, "URL passed as path");
688 #if defined(XP_WIN) || defined(XP_OS2)
689 nsFileSpecHelpers::UnixToNative(mPath);
690 #endif
691 // Make canonical and absolute.
692 nsFileSpecHelpers::Canonify(mPath, inCreateDirs);
693 #if defined(XP_WIN) || defined(XP_OS2)
694 NS_ASSERTION( mPath[1] == ':' || (mPath[0] == '\\' && mPath[1] == '\\'),
695 "unexpected canonical path" );
696 nsFileSpecHelpers::NativeToUnix(mPath);
697 #endif
700 //----------------------------------------------------------------------------------------
701 nsFilePath::nsFilePath(const nsFileURL& inOther)
702 //----------------------------------------------------------------------------------------
704 mPath = (const char*)inOther.mURL + kFileURLPrefixLength;
705 mPath.Unescape();
708 #if (defined XP_UNIX || defined XP_BEOS)
709 //----------------------------------------------------------------------------------------
710 nsFilePath::nsFilePath(const nsFileSpec& inOther)
711 //----------------------------------------------------------------------------------------
712 : mPath(inOther.mPath)
715 #endif // XP_UNIX || XP_BEOS
717 //----------------------------------------------------------------------------------------
718 nsFilePath::~nsFilePath()
719 //----------------------------------------------------------------------------------------
723 #if (defined XP_UNIX || defined XP_BEOS)
724 //----------------------------------------------------------------------------------------
725 void nsFilePath::operator = (const nsFileSpec& inOther)
726 //----------------------------------------------------------------------------------------
728 // XXX bug here, again if.
730 mPath = inOther.mPath;
732 #endif // XP_UNIX
734 //----------------------------------------------------------------------------------------
735 void nsFilePath::operator = (const char* inString)
736 //----------------------------------------------------------------------------------------
739 NS_ASSERTION(strstr(inString, kFileURLPrefix) != inString, "URL passed as path");
740 mPath = inString;
741 if (mPath.IsEmpty())
742 return;
743 #if defined(XP_WIN) || defined(XP_OS2)
744 nsFileSpecHelpers::UnixToNative(mPath);
745 #endif
746 // Make canonical and absolute.
747 nsFileSpecHelpers::Canonify(mPath, PR_FALSE /* XXX? */);
748 #if defined(XP_WIN) || defined(XP_OS2)
749 nsFileSpecHelpers::NativeToUnix(mPath);
750 #endif
753 //----------------------------------------------------------------------------------------
754 void nsFilePath::operator = (const nsFileURL& inOther)
755 //----------------------------------------------------------------------------------------
757 mPath = (const char*)nsFilePath(inOther);
760 //----------------------------------------------------------------------------------------
761 void nsFilePath::operator = (const nsFilePath& inOther)
762 //----------------------------------------------------------------------------------------
764 mPath = inOther.mPath;
767 //----------------------------------------------------------------------------------------
768 void nsFilePath::operator +=(const char* inRelativeUnixPath)
769 //----------------------------------------------------------------------------------------
771 NS_ASSERTION(inRelativeUnixPath, "Attempt append relative path with null path");
773 char* escapedPath = nsEscape(inRelativeUnixPath, url_Path);
774 mPath += escapedPath;
775 nsCRT::free(escapedPath);
776 } // nsFilePath::operator +=
778 //----------------------------------------------------------------------------------------
779 nsFilePath nsFilePath::operator +(const char* inRelativeUnixPath) const
780 //----------------------------------------------------------------------------------------
782 NS_ASSERTION(inRelativeUnixPath, "Attempt append relative path with null path");
784 nsFilePath resultPath(*this);
785 resultPath += inRelativeUnixPath;
786 return resultPath;
787 } // nsFilePath::operator +
790 //========================================================================================
791 // nsFileSpec implementation
792 //========================================================================================
794 //----------------------------------------------------------------------------------------
795 nsFileSpec::nsFileSpec()
796 //----------------------------------------------------------------------------------------
797 : mError(NS_OK) // XXX shouldn't this be NS_ERROR_NOT_INITIALIZED?
799 // NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!");
802 //----------------------------------------------------------------------------------------
803 void nsFileSpec::Clear()
804 //----------------------------------------------------------------------------------------
806 mPath.SetToEmpty();
807 mError = NS_ERROR_NOT_INITIALIZED;
810 //----------------------------------------------------------------------------------------
811 nsFileSpec::~nsFileSpec()
812 //----------------------------------------------------------------------------------------
814 // mPath cleans itself up
817 //----------------------------------------------------------------------------------------
818 nsFileSpec::nsFileSpec(const nsPersistentFileDescriptor& inDescriptor)
819 //----------------------------------------------------------------------------------------
821 *this = inDescriptor;
824 //----------------------------------------------------------------------------------------
825 nsFileSpec::nsFileSpec(const nsFileURL& inURL)
826 //----------------------------------------------------------------------------------------
828 *this = nsFilePath(inURL); // convert to unix path first
831 //----------------------------------------------------------------------------------------
832 void nsFileSpec::MakeUnique(const char* inSuggestedLeafName, PRBool inCreateFile)
833 //----------------------------------------------------------------------------------------
835 if (inSuggestedLeafName && *inSuggestedLeafName)
836 SetLeafName(inSuggestedLeafName);
837 MakeUnique(inCreateFile);
838 } // nsFileSpec::MakeUnique
840 //----------------------------------------------------------------------------------------
841 void nsFileSpec::MakeUnique(PRBool inCreateFile)
842 //----------------------------------------------------------------------------------------
844 // XXX: updated path starts empty. In case of error this will cause
845 // any callers to fail badly, but that seems better than letting them
846 // re-use the default name which has failed to be unique.
847 nsCAutoString path;
848 nsCOMPtr<nsILocalFile> localFile;
849 NS_NewNativeLocalFile(nsDependentCString(*this), PR_TRUE, getter_AddRefs(localFile));
850 if (localFile)
852 nsresult rv;
854 if (inCreateFile)
855 rv = localFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
856 else
857 rv = localFile->CreateUnique(nsIFile::DIRECTORY_TYPE, 0700);
859 if (NS_SUCCEEDED(rv))
860 localFile->GetNativePath(path);
863 NS_ASSERTION(!path.IsEmpty(), "MakeUnique() failed!");
864 *this = path.get(); // reset the filepath to point to the unique location
866 } // nsFileSpec::MakeUnique
868 //----------------------------------------------------------------------------------------
869 void nsFileSpec::operator = (const nsFileURL& inURL)
870 //----------------------------------------------------------------------------------------
872 *this = nsFilePath(inURL); // convert to unix path first
875 //----------------------------------------------------------------------------------------
876 void nsFileSpec::operator = (const nsPersistentFileDescriptor& inDescriptor)
877 //----------------------------------------------------------------------------------------
880 nsCAutoString data;
881 inDescriptor.GetData(data);
883 #ifdef XP_MACOSX
884 // Decode descriptor into a Handle (which is actually an AliasHandle)
885 char* decodedData = PL_Base64Decode(data.get(), data.Length(), nsnull);
886 Handle aliasH = nsnull;
887 mError = NS_FILE_RESULT(::PtrToHand(decodedData, &aliasH, (data.Length() * 3) / 4));
888 PR_Free(decodedData);
889 if (NS_FAILED(mError))
890 return; // not enough memory?
892 Boolean changed;
893 FSRef fileRef;
894 mError = NS_FILE_RESULT(::FSResolveAlias(nsnull, (AliasHandle)aliasH, &fileRef, &changed));
895 ::DisposeHandle(aliasH);
897 UInt8 pathBuf[PATH_MAX];
898 mError = NS_FILE_RESULT(::FSRefMakePath(&fileRef, pathBuf, PATH_MAX));
899 if (NS_FAILED(mError))
900 return;
901 mPath = (const char*)pathBuf;
902 #else
903 mPath = data.get();
904 mError = NS_OK;
905 #endif
908 //========================================================================================
909 // UNIX & WIN nsFileSpec implementation
910 //========================================================================================
912 #if (defined XP_UNIX || defined XP_BEOS)
913 //----------------------------------------------------------------------------------------
914 nsFileSpec::nsFileSpec(const nsFilePath& inPath)
915 //----------------------------------------------------------------------------------------
916 : mPath((const char*)inPath)
917 , mError(NS_OK)
919 // NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!");
922 //----------------------------------------------------------------------------------------
923 void nsFileSpec::operator = (const nsFilePath& inPath)
924 //----------------------------------------------------------------------------------------
926 mPath = (const char*)inPath;
927 mError = NS_OK;
929 #endif // XP_UNIX || XP_BEOS
931 #if (defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS))
932 //----------------------------------------------------------------------------------------
933 nsFileSpec::nsFileSpec(const nsFileSpec& inSpec)
934 //----------------------------------------------------------------------------------------
935 : mPath(inSpec.mPath)
936 , mError(NS_OK)
938 // NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!");
941 //----------------------------------------------------------------------------------------
942 nsFileSpec::nsFileSpec(const char* inString, PRBool inCreateDirs)
943 //----------------------------------------------------------------------------------------
944 : mPath(inString)
945 , mError(NS_OK)
947 // NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!");
948 // Make canonical and absolute.
949 nsFileSpecHelpers::Canonify(mPath, inCreateDirs);
952 //----------------------------------------------------------------------------------------
953 nsFileSpec::nsFileSpec(const nsString& inString, PRBool inCreateDirs)
954 //----------------------------------------------------------------------------------------
955 : mPath(inString)
956 , mError(NS_OK)
958 // NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!");
959 // Make canonical and absolute.
960 nsFileSpecHelpers::Canonify(mPath, inCreateDirs);
963 //----------------------------------------------------------------------------------------
964 void nsFileSpec::operator = (const nsFileSpec& inSpec)
965 //----------------------------------------------------------------------------------------
967 mPath = inSpec.mPath;
968 mError = inSpec.Error();
971 //----------------------------------------------------------------------------------------
972 void nsFileSpec::operator = (const char* inString)
973 //----------------------------------------------------------------------------------------
975 mPath = inString;
976 // Make canonical and absolute.
977 nsFileSpecHelpers::Canonify(mPath, PR_FALSE /* XXX? */);
978 mError = NS_OK;
980 #endif // XP_UNIX,XP_WIN,XP_OS2,XP_BEOS
982 //----------------------------------------------------------------------------------------
983 nsFileSpec nsFileSpec::operator + (const char* inRelativePath) const
984 //----------------------------------------------------------------------------------------
986 NS_ASSERTION(inRelativePath, "Attempt to append name with a null string");
988 nsFileSpec resultSpec = *this;
989 resultSpec += inRelativePath;
990 return resultSpec;
991 } // nsFileSpec::operator +
993 //----------------------------------------------------------------------------------------
994 PRBool nsFileSpec::operator == (const nsFileSpec& inOther) const
995 //----------------------------------------------------------------------------------------
997 PRBool amEmpty = mPath.IsEmpty();
998 PRBool heEmpty = inOther.mPath.IsEmpty();
999 if (amEmpty) // we're the same if he's empty...
1000 return heEmpty;
1001 if (heEmpty) // ('cuz I'm not...)
1002 return PR_FALSE;
1004 nsSimpleCharString str = mPath;
1005 nsSimpleCharString inStr = inOther.mPath;
1007 // Length() is size of buffer, not length of string
1008 PRUint32 strLast = str.Length() - 1, inLast = inStr.Length() - 1;
1009 #if defined(XP_WIN) || defined(XP_OS2)
1010 #define DIR_SEPARATOR '\\' // XXX doesn't NSPR have this?
1011 /* windows does not care about case. */
1012 #ifdef XP_OS2
1013 #define DIR_STRCMP strcmp
1014 #else
1015 #define DIR_STRCMP _stricmp
1016 #endif
1017 #else
1018 #define DIR_SEPARATOR '/'
1019 #if defined(VMS)
1020 #define DIR_STRCMP strcasecmp
1021 #else
1022 #define DIR_STRCMP strcmp
1023 #endif
1024 #endif
1025 if(str[strLast] == DIR_SEPARATOR)
1026 str[strLast] = '\0';
1028 if(inStr[inLast] == DIR_SEPARATOR)
1029 inStr[inLast] = '\0';
1031 if (DIR_STRCMP(str, inStr) == 0)
1032 return PR_TRUE;
1033 #undef DIR_SEPARATOR
1034 #undef DIR_STRCMP
1035 return PR_FALSE;
1038 //----------------------------------------------------------------------------------------
1039 PRBool nsFileSpec::operator != (const nsFileSpec& inOther) const
1040 //----------------------------------------------------------------------------------------
1042 return (! (*this == inOther) );
1045 //----------------------------------------------------------------------------------------
1046 // This is the only automatic conversion to const char*
1047 // that is provided, and it allows the
1048 // path to be "passed" to NSPR file routines. This practice
1049 // is VERY EVIL and should only be used to support legacy
1050 // code. Using it guarantees bugs on Macintosh. The path is NOT allocated, so do
1051 // not even think of deleting (or freeing) it.
1052 const char* nsFileSpec::GetCString() const
1053 //----------------------------------------------------------------------------------------
1055 return mPath;
1058 //----------------------------------------------------------------------------------------
1059 // Is our spec a child of the provided parent?
1060 PRBool nsFileSpec::IsChildOf(nsFileSpec &possibleParent)
1061 //----------------------------------------------------------------------------------------
1063 nsFileSpec iter = *this, parent;
1064 #ifdef DEBUG
1065 int depth = 0;
1066 #endif
1067 while (1) {
1068 #ifdef DEBUG
1069 // sanity
1070 NS_ASSERTION(depth < 100, "IsChildOf has lost its little mind");
1071 if (depth > 100)
1072 return PR_FALSE;
1073 #endif
1074 if (iter == possibleParent)
1075 return PR_TRUE;
1077 iter.GetParent(parent); // shouldn't this be an error on parent?
1078 if (iter.Failed())
1079 return PR_FALSE;
1081 if (iter == parent) // hit bottom
1082 return PR_FALSE;
1084 iter = parent;
1085 #ifdef DEBUG
1086 depth++;
1087 #endif
1090 // not reached, but I bet some compiler will whine
1091 return PR_FALSE;
1095 //========================================================================================
1096 // class nsPersistentFileDescriptor
1097 //========================================================================================
1099 //----------------------------------------------------------------------------------------
1100 nsPersistentFileDescriptor::nsPersistentFileDescriptor(const nsPersistentFileDescriptor& inDesc)
1101 //----------------------------------------------------------------------------------------
1102 : mDescriptorString(inDesc.mDescriptorString)
1104 } // nsPersistentFileDescriptor::nsPersistentFileDescriptor
1106 //----------------------------------------------------------------------------------------
1107 void nsPersistentFileDescriptor::operator = (const nsPersistentFileDescriptor& inDesc)
1108 //----------------------------------------------------------------------------------------
1110 mDescriptorString = inDesc.mDescriptorString;
1111 } // nsPersistentFileDescriptor::operator =
1113 //----------------------------------------------------------------------------------------
1114 nsPersistentFileDescriptor::nsPersistentFileDescriptor(const nsFileSpec& inSpec)
1115 //----------------------------------------------------------------------------------------
1117 *this = inSpec;
1118 } // nsPersistentFileDescriptor::nsPersistentFileDescriptor
1120 //----------------------------------------------------------------------------------------
1121 void nsPersistentFileDescriptor::operator = (const nsFileSpec& inSpec)
1122 //----------------------------------------------------------------------------------------
1124 #ifdef XP_MACOSX
1125 if (inSpec.Error())
1126 return;
1128 FSRef fileRef;
1129 Boolean isDir;
1130 OSErr err = ::FSPathMakeRef((const UInt8*)inSpec.GetCString(), &fileRef, &isDir);
1131 if (err != noErr)
1132 return;
1134 AliasHandle aliasH;
1135 err = ::FSNewAlias(nsnull, &fileRef, &aliasH);
1136 if (err != noErr)
1137 return;
1139 PRUint32 bytes = ::GetHandleSize((Handle) aliasH);
1140 ::HLock((Handle)aliasH);
1141 char* buf = PL_Base64Encode((const char*)*aliasH, bytes, nsnull);
1142 ::DisposeHandle((Handle) aliasH);
1144 mDescriptorString = buf;
1145 PR_Free(buf);
1146 #else
1147 mDescriptorString = inSpec.GetCString();
1148 #endif // XP_MACOSX
1149 } // nsPersistentFileDescriptor::operator =
1151 //----------------------------------------------------------------------------------------
1152 nsPersistentFileDescriptor::~nsPersistentFileDescriptor()
1153 //----------------------------------------------------------------------------------------
1155 } // nsPersistentFileDescriptor::~nsPersistentFileDescriptor
1157 //----------------------------------------------------------------------------------------
1158 void nsPersistentFileDescriptor::GetData(nsAFlatCString& outData) const
1159 //----------------------------------------------------------------------------------------
1161 outData.Assign(mDescriptorString, mDescriptorString.Length());
1164 //----------------------------------------------------------------------------------------
1165 void nsPersistentFileDescriptor::SetData(const nsAFlatCString& inData)
1166 //----------------------------------------------------------------------------------------
1168 mDescriptorString.CopyFrom(inData.get(), inData.Length());
1171 //----------------------------------------------------------------------------------------
1172 void nsPersistentFileDescriptor::SetData(const char* inData, PRInt32 inSize)
1173 //----------------------------------------------------------------------------------------
1175 mDescriptorString.CopyFrom(inData, inSize);
1178 //========================================================================================
1179 // class nsNSPRPath
1180 //========================================================================================
1182 //----------------------------------------------------------------------------------------
1183 nsNSPRPath::operator const char*() const
1184 // NSPR expects a UNIX path on unix and Macintosh, but a native path on windows. NSPR
1185 // cannot be changed, so we have to do the dirty work.
1186 //----------------------------------------------------------------------------------------
1188 #if defined(XP_WIN) || defined(XP_OS2)
1189 if (!modifiedNSPRPath)
1191 // If this is the first call, initialize modifiedNSPRPath. Start by cloning
1192 // mFilePath, but strip the leading separator, if present
1193 const char* unixPath = (const char*)mFilePath;
1194 if (!unixPath)
1195 return nsnull;
1197 ((nsNSPRPath*)this)->modifiedNSPRPath
1198 = nsCRT::strdup(*unixPath == '/' ? unixPath + 1: unixPath);
1200 // Replace the bar
1201 if (modifiedNSPRPath[1] == '|')
1202 modifiedNSPRPath[1] = ':';
1204 // Remove the ending separator only if it is not the last separator
1205 int len = strlen(modifiedNSPRPath);
1206 if (modifiedNSPRPath[len - 1 ] == '/' && modifiedNSPRPath[len - 2 ] != ':')
1207 modifiedNSPRPath[len - 1 ] = '\0';
1209 return modifiedNSPRPath;
1210 #else
1211 return (const char*)mFilePath;
1212 #endif
1215 //----------------------------------------------------------------------------------------
1216 nsNSPRPath::~nsNSPRPath()
1217 //----------------------------------------------------------------------------------------
1219 #if defined(XP_WIN) || defined(XP_OS2)
1220 if (modifiedNSPRPath)
1221 nsCRT::free(modifiedNSPRPath);
1222 #endif
1226 nsresult
1227 NS_FileSpecToIFile(nsFileSpec* fileSpec, nsILocalFile* *result)
1229 nsresult rv;
1231 nsCOMPtr<nsILocalFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
1233 if (!file) return NS_ERROR_FAILURE;
1235 rv = file->InitWithNativePath(nsDependentCString(fileSpec->GetNativePathCString()));
1237 if (NS_FAILED(rv)) return rv;
1239 *result = file;
1240 NS_ADDREF(*result);
1241 return NS_OK;