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
15 * The Original Code is Mozilla Communicator client code, released
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998-1999
21 * the Initial Developer. All Rights Reserved.
24 * Doug Turner <dougt@netscape.com>
25 * Dean Tessman <dean_tessman@hotmail.com>
26 * Brodie Thiesfield <brofield@jellycan.com>
27 * Jungshik Shin <jshin@i18nl10n.com>
29 * Alternatively, the contents of this file may be used under the terms of
30 * either of the GNU General Public License Version 2 or later (the "GPL"),
31 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 * in which case the provisions of the GPL or the LGPL are applicable instead
33 * of those above. If you wish to allow use of your version of this file only
34 * under the terms of either the GPL or the LGPL, and not to allow others to
35 * use your version of this file under the terms of the MPL, indicate your
36 * decision by deleting the provisions above and replace them with the notice
37 * and other provisions required by the GPL or the LGPL. If you do not delete
38 * the provisions above, a recipient may use your version of this file under
39 * the terms of any one of the MPL, the GPL or the LGPL.
41 * ***** END LICENSE BLOCK ***** */
47 #include "nsLocalFile.h"
48 #include "nsIDirectoryEnumerator.h"
49 #include "nsNativeCharsetUtils.h"
51 #include "nsISimpleEnumerator.h"
52 #include "nsIComponentManager.h"
55 #include "private/pprio.h" // To get PR_ImportFile
58 #include "nsHashKeys.h"
60 #include "nsXPIDLString.h"
61 #include "nsReadableUtils.h"
75 #include "nsXPIDLString.h"
77 #include "nsITimelineService.h"
79 #include "nsAutoLock.h"
80 #include "SpecialSystemDirectory.h"
82 #include "nsTraceRefcntImpl.h"
84 #define CHECK_mWorkingPath() \
86 if (mWorkingPath.IsEmpty()) \
87 return NS_ERROR_NOT_INITIALIZED; \
90 // _mbsstr isn't declared in w32api headers but it's there in the libs
93 unsigned char *_mbsstr( const unsigned char *str
,
94 const unsigned char *substr
);
98 class nsDriveEnumerator
: public nsISimpleEnumerator
102 virtual ~nsDriveEnumerator();
104 NS_DECL_NSISIMPLEENUMERATOR
107 /* mDrives and mLetter share data
109 * HasMoreElements reads mLetter.
110 * GetNext advances mLetter.
116 //----------------------------------------------------------------------------
117 // short cut resolver
118 //----------------------------------------------------------------------------
120 class ShortcutResolver
124 // nonvirtual since we're not subclassed
128 nsresult
Resolve(const WCHAR
* in
, WCHAR
* out
);
132 IPersistFile
* mPersistFile
;
133 // Win 95 and 98 don't have IShellLinkW
134 IShellLinkW
* mShellLink
;
137 ShortcutResolver::ShortcutResolver()
140 mPersistFile
= nsnull
;
144 ShortcutResolver::~ShortcutResolver()
147 PR_DestroyLock(mLock
);
149 // Release the pointer to the IPersistFile interface.
151 mPersistFile
->Release();
153 // Release the pointer to the IShellLink interface.
155 mShellLink
->Release();
161 ShortcutResolver::Init()
163 CoInitialize(NULL
); // FIX: we should probably move somewhere higher up during startup
165 mLock
= PR_NewLock();
167 return NS_ERROR_FAILURE
;
170 hres
= CoCreateInstance(CLSID_ShellLink
,
172 CLSCTX_INPROC_SERVER
,
174 (void**)&(mShellLink
));
177 // Get a pointer to the IPersistFile interface.
178 hres
= mShellLink
->QueryInterface(IID_IPersistFile
,
179 (void**)&mPersistFile
);
182 if (mPersistFile
== nsnull
|| mShellLink
== nsnull
)
183 return NS_ERROR_FAILURE
;
188 // |out| must be an allocated buffer of size MAX_PATH
190 ShortcutResolver::Resolve(const WCHAR
* in
, WCHAR
* out
)
192 nsAutoLock
lock(mLock
);
194 // see if we can Load the path.
195 HRESULT hres
= mPersistFile
->Load(in
, STGM_READ
);
198 return NS_ERROR_FAILURE
;
201 hres
= mShellLink
->Resolve(nsnull
, SLR_NO_UI
);
204 return NS_ERROR_FAILURE
;
206 // Get the path to the link target.
207 hres
= mShellLink
->GetPath(out
, MAX_PATH
, NULL
, SLGP_UNCPRIORITY
);
210 return NS_ERROR_FAILURE
;
214 static ShortcutResolver
* gResolver
= nsnull
;
216 static nsresult
NS_CreateShortcutResolver()
218 gResolver
= new ShortcutResolver();
220 return NS_ERROR_OUT_OF_MEMORY
;
222 return gResolver
->Init();
225 static void NS_DestroyShortcutResolver()
233 //-----------------------------------------------------------------------------
234 // static helper functions
235 //-----------------------------------------------------------------------------
237 // certainly not all the error that can be
238 // encountered, but many of them common ones
239 static nsresult
ConvertWinError(DWORD winErr
)
245 case ERROR_FILE_NOT_FOUND
:
246 case ERROR_PATH_NOT_FOUND
:
247 case ERROR_INVALID_DRIVE
:
248 rv
= NS_ERROR_FILE_NOT_FOUND
;
250 case ERROR_ACCESS_DENIED
:
251 case ERROR_NOT_SAME_DEVICE
:
252 rv
= NS_ERROR_FILE_ACCESS_DENIED
;
254 case ERROR_NOT_ENOUGH_MEMORY
:
255 case ERROR_INVALID_BLOCK
:
256 case ERROR_INVALID_HANDLE
:
257 case ERROR_ARENA_TRASHED
:
258 rv
= NS_ERROR_OUT_OF_MEMORY
;
260 case ERROR_CURRENT_DIRECTORY
:
261 rv
= NS_ERROR_FILE_DIR_NOT_EMPTY
;
263 case ERROR_WRITE_PROTECT
:
264 rv
= NS_ERROR_FILE_READ_ONLY
;
266 case ERROR_HANDLE_DISK_FULL
:
267 rv
= NS_ERROR_FILE_TOO_BIG
;
269 case ERROR_FILE_EXISTS
:
270 case ERROR_ALREADY_EXISTS
:
271 case ERROR_CANNOT_MAKE
:
272 rv
= NS_ERROR_FILE_ALREADY_EXISTS
;
274 case ERROR_FILENAME_EXCED_RANGE
:
275 rv
= NS_ERROR_FILE_NAME_TOO_LONG
;
281 rv
= NS_ERROR_FAILURE
;
287 // definition of INVALID_SET_FILE_POINTER from VC.NET header files
288 // it doesn't appear to be defined by VC6
289 #ifndef INVALID_SET_FILE_POINTER
290 # define INVALID_SET_FILE_POINTER ((DWORD)-1)
292 // same goes for INVALID_FILE_ATTRIBUTES
293 #ifndef INVALID_FILE_ATTRIBUTES
294 # define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
297 // as suggested in the MSDN documentation on SetFilePointer
299 MyFileSeek64(HANDLE aHandle
, __int64 aDistance
, DWORD aMoveMethod
)
303 li
.QuadPart
= aDistance
;
304 li
.LowPart
= SetFilePointer(aHandle
, li
.LowPart
, &li
.HighPart
, aMoveMethod
);
305 if (li
.LowPart
== INVALID_SET_FILE_POINTER
&& GetLastError() != NO_ERROR
)
314 IsShortcutPath(const nsAString
&path
)
316 // Under Windows, the shortcuts are just files with a ".lnk" extension.
317 // Note also that we don't resolve links in the middle of paths.
318 // i.e. "c:\foo.lnk\bar.txt" is invalid.
319 NS_ABORT_IF_FALSE(!path
.IsEmpty(), "don't pass an empty string");
320 PRInt32 len
= path
.Length();
321 return (StringTail(path
, 4).LowerCaseEqualsASCII(".lnk"));
324 //-----------------------------------------------------------------------------
325 // We need the following three definitions to make |OpenFile| convert a file
326 // handle to an NSPR file descriptor correctly when |O_APPEND| flag is
327 // specified. It is defined in a private header of NSPR (primpl.h) we can't
328 // include. As a temporary workaround until we decide how to extend
329 // |PR_ImportFile|, we define it here. Currently, |_PR_HAVE_PEEK_BUFFER|
330 // and |PR_STRICT_ADDR_LEN| are not defined for the 'w95'-dependent portion
331 // of NSPR so that fields of |PRFilePrivate| #ifdef'd by them are not copied.
332 // Similarly, |_MDFileDesc| is taken from nsprpub/pr/include/md/_win95.h.
333 // In an unlikely case we switch to 'NT'-dependent NSPR AND this temporary
334 // workaround last beyond the switch, |PRFilePrivate| and |_MDFileDesc|
335 // need to be changed to match the definitions for WinNT.
336 //-----------------------------------------------------------------------------
347 struct PRFilePrivate
{
350 _PRTriStateBool inheritable
;
352 PRIntn lockCount
; /* 0: not locked
353 * -1: a native lockfile call is in progress
354 * > 0: # times the file is locked */
359 //-----------------------------------------------------------------------------
360 // Six static methods defined below (OpenFile, FileTimeToPRTime, GetFileInfo,
361 // OpenDir, CloseDir, ReadDir) should go away once the corresponding
362 // UTF-16 APIs are implemented on all the supported platforms (or at least
363 // Windows 9x/ME) in NSPR. Currently, they're only implemented on
364 // Windows NT4 or later. (bug 330665)
365 //-----------------------------------------------------------------------------
367 // copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} :
368 // PR_Open and _PR_MD_OPEN
370 OpenFile(const nsAFlatString
&name
, PRIntn osflags
, PRIntn mode
,
373 // XXX : 'mode' is not translated !!!
378 if (osflags
& PR_SYNC
) flag6
= FILE_FLAG_WRITE_THROUGH
;
380 if (osflags
& PR_RDONLY
|| osflags
& PR_RDWR
)
381 access
|= GENERIC_READ
;
382 if (osflags
& PR_WRONLY
|| osflags
& PR_RDWR
)
383 access
|= GENERIC_WRITE
;
385 if ( osflags
& PR_CREATE_FILE
&& osflags
& PR_EXCL
)
387 else if (osflags
& PR_CREATE_FILE
) {
388 if (osflags
& PR_TRUNCATE
)
389 flags
= CREATE_ALWAYS
;
393 if (osflags
& PR_TRUNCATE
)
394 flags
= TRUNCATE_EXISTING
;
396 flags
= OPEN_EXISTING
;
399 HANDLE file
= ::CreateFileW(name
.get(), access
,
400 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
401 NULL
, flags
, flag6
, NULL
);
403 if (file
== INVALID_HANDLE_VALUE
) {
405 return ConvertWinError(GetLastError());
408 *fd
= PR_ImportFile((PROsfd
) file
);
410 // On Windows, _PR_HAVE_O_APPEND is not defined so that we have to
411 // add it manually. (see |PR_Open| in nsprpub/pr/src/io/prfile.c)
412 (*fd
)->secret
->appendMode
= (PR_APPEND
& osflags
) ? PR_TRUE
: PR_FALSE
;
416 nsresult rv
= NS_ErrorAccordingToNSPR();
423 // copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} :
424 // PR_FileTimeToPRTime and _PR_FileTimeToPRTime
426 void FileTimeToPRTime(const FILETIME
*filetime
, PRTime
*prtm
)
429 const PRTime _pr_filetime_offset
= 116444736000000000LL;
431 const PRTime _pr_filetime_offset
= 116444736000000000i64
;
434 PR_ASSERT(sizeof(FILETIME
) == sizeof(PRTime
));
435 ::CopyMemory(prtm
, filetime
, sizeof(PRTime
));
437 *prtm
= (*prtm
- _pr_filetime_offset
) / 10LL;
439 *prtm
= (*prtm
- _pr_filetime_offset
) / 10i64
;
443 // copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} with some
444 // changes : PR_GetFileInfo64, _PR_MD_GETFILEINFO64
446 GetFileInfo(const nsAFlatString
&name
, PRFileInfo64
*info
)
448 WIN32_FILE_ATTRIBUTE_DATA fileData
;
450 if (name
.IsEmpty() || name
.FindCharInSet(L
"?*") != kNotFound
)
451 return NS_ERROR_INVALID_ARG
;
453 if (!::GetFileAttributesExW(name
.get(), GetFileExInfoStandard
, &fileData
))
454 return ConvertWinError(GetLastError());
456 if (fileData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
457 info
->type
= PR_FILE_DIRECTORY
;
459 info
->type
= PR_FILE_FILE
;
462 info
->size
= fileData
.nFileSizeHigh
;
463 info
->size
= (info
->size
<< 32) + fileData
.nFileSizeLow
;
465 FileTimeToPRTime(&fileData
.ftLastWriteTime
, &info
->modifyTime
);
467 if (0 == fileData
.ftCreationTime
.dwLowDateTime
&&
468 0 == fileData
.ftCreationTime
.dwHighDateTime
) {
469 info
->creationTime
= info
->modifyTime
;
471 FileTimeToPRTime(&fileData
.ftCreationTime
, &info
->creationTime
);
480 WIN32_FIND_DATAW data
;
485 OpenDir(const nsAFlatString
&name
, nsDir
* *dir
)
487 NS_ENSURE_ARG_POINTER(dir
);
490 if (name
.Length() + 3 >= MAX_PATH
)
491 return NS_ERROR_FILE_NAME_TOO_LONG
;
493 nsDir
*d
= PR_NEW(nsDir
);
495 return NS_ERROR_OUT_OF_MEMORY
;
497 nsAutoString
filename(name
);
499 //If 'name' ends in a slash or backslash, do not append
501 if (filename
.Last() == L
'/' || filename
.Last() == L
'\\')
502 filename
.AppendASCII("*");
504 filename
.AppendASCII("\\*");
506 filename
.ReplaceChar(L
'/', L
'\\');
508 d
->handle
= ::FindFirstFileW(filename
.get(), &(d
->data
) );
510 if ( d
->handle
== INVALID_HANDLE_VALUE
)
513 return ConvertWinError(GetLastError());
515 d
->firstEntry
= PR_TRUE
;
522 ReadDir(nsDir
*dir
, PRDirFlags flags
, nsString
& name
)
531 dir
->firstEntry
= PR_FALSE
;
534 rv
= ::FindNextFileW(dir
->handle
, &(dir
->data
));
539 const PRUnichar
*fileName
;
541 fileName
= (dir
)->data
.cFileName
;
543 if ((flags
& PR_SKIP_DOT
) &&
544 (fileName
[0] == L
'.') && (fileName
[1] == L
'\0'))
546 if ((flags
& PR_SKIP_DOT_DOT
) &&
547 (fileName
[0] == L
'.') && (fileName
[1] == L
'.') &&
548 (fileName
[2] == L
'\0'))
551 DWORD attrib
= dir
->data
.dwFileAttributes
;
552 if ((flags
& PR_SKIP_HIDDEN
) && (attrib
& FILE_ATTRIBUTE_HIDDEN
))
555 if (fileName
== tmp
.get())
562 DWORD err
= GetLastError();
563 return err
== ERROR_NO_MORE_FILES
? NS_OK
: ConvertWinError(err
);
571 BOOL isOk
= FindClose(d
->handle
);
573 return isOk
? NS_OK
: ConvertWinError(GetLastError());
576 //-----------------------------------------------------------------------------
578 //-----------------------------------------------------------------------------
580 class nsDirEnumerator
: public nsISimpleEnumerator
,
581 public nsIDirectoryEnumerator
587 nsDirEnumerator() : mDir(nsnull
)
591 nsresult
Init(nsILocalFile
* parent
)
593 nsAutoString filepath
;
594 parent
->GetTarget(filepath
);
596 if (filepath
.IsEmpty())
598 parent
->GetPath(filepath
);
601 if (filepath
.IsEmpty())
603 return NS_ERROR_UNEXPECTED
;
606 nsresult rv
= OpenDir(filepath
, &mDir
);
614 NS_IMETHOD
HasMoreElements(PRBool
*result
)
617 if (mNext
== nsnull
&& mDir
)
620 rv
= ReadDir(mDir
, PR_SKIP_BOTH
, name
);
625 // end of dir entries
626 if (NS_FAILED(CloseDir(mDir
)))
627 return NS_ERROR_FAILURE
;
635 nsCOMPtr
<nsIFile
> file
;
636 rv
= mParent
->Clone(getter_AddRefs(file
));
640 rv
= file
->Append(name
);
644 // make sure the thing exists. If it does, try the next one.
646 rv
= file
->Exists(&exists
);
647 if (NS_FAILED(rv
) || !exists
)
649 return HasMoreElements(result
);
652 mNext
= do_QueryInterface(file
);
654 *result
= mNext
!= nsnull
;
660 NS_IMETHOD
GetNext(nsISupports
**result
)
664 rv
= HasMoreElements(&hasMore
);
665 if (NS_FAILED(rv
)) return rv
;
667 *result
= mNext
; // might return nsnull
668 NS_IF_ADDREF(*result
);
674 NS_IMETHOD
GetNextFile(nsIFile
**result
)
677 PRBool hasMore
= PR_FALSE
;
678 nsresult rv
= HasMoreElements(&hasMore
);
679 if (NS_FAILED(rv
) || !hasMore
)
682 NS_IF_ADDREF(*result
);
691 nsresult rv
= CloseDir(mDir
);
692 NS_ASSERTION(NS_SUCCEEDED(rv
), "close failed");
694 return NS_ERROR_FAILURE
;
700 // dtor can be non-virtual since there are no subclasses, but must be
701 // public to use the class on the stack.
709 nsCOMPtr
<nsILocalFile
> mParent
;
710 nsCOMPtr
<nsILocalFile
> mNext
;
713 NS_IMPL_ISUPPORTS2(nsDirEnumerator
, nsISimpleEnumerator
, nsIDirectoryEnumerator
)
716 //-----------------------------------------------------------------------------
717 // nsLocalFile <public>
718 //-----------------------------------------------------------------------------
720 nsLocalFile::nsLocalFile()
722 , mFollowSymlinks(PR_FALSE
)
727 nsLocalFile::nsLocalFileConstructor(nsISupports
* outer
, const nsIID
& aIID
, void* *aInstancePtr
)
729 NS_ENSURE_ARG_POINTER(aInstancePtr
);
730 NS_ENSURE_NO_AGGREGATION(outer
);
732 nsLocalFile
* inst
= new nsLocalFile();
734 return NS_ERROR_OUT_OF_MEMORY
;
736 nsresult rv
= inst
->QueryInterface(aIID
, aInstancePtr
);
746 //-----------------------------------------------------------------------------
747 // nsLocalFile::nsISupports
748 //-----------------------------------------------------------------------------
750 NS_IMPL_THREADSAFE_ISUPPORTS4(nsLocalFile
,
757 //-----------------------------------------------------------------------------
758 // nsLocalFile <private>
759 //-----------------------------------------------------------------------------
761 nsLocalFile::nsLocalFile(const nsLocalFile
& other
)
763 , mFollowSymlinks(other
.mFollowSymlinks
)
764 , mWorkingPath(other
.mWorkingPath
)
768 // Resolve the shortcut file from mWorkingPath and write the path
769 // it points to into mResolvedPath.
771 nsLocalFile::ResolveShortcut()
774 // we can't do anything without the resolver
776 return NS_ERROR_FAILURE
;
778 mResolvedPath
.SetLength(MAX_PATH
);
779 if (mResolvedPath
.Length() != MAX_PATH
)
780 return NS_ERROR_OUT_OF_MEMORY
;
782 PRUnichar
*resolvedPath
= mResolvedPath
.BeginWriting();
784 // resolve this shortcut
785 nsresult rv
= gResolver
->Resolve(mWorkingPath
.get(), resolvedPath
);
787 size_t len
= NS_FAILED(rv
) ? 0 : wcslen(resolvedPath
);
788 mResolvedPath
.SetLength(len
);
796 // Resolve any shortcuts and stat the resolved path. After a successful return
797 // the path is guaranteed valid and the members of mFileInfo64 can be used.
799 nsLocalFile::ResolveAndStat()
801 // if we aren't dirty then we are already done
805 // we can't resolve/stat anything that isn't a valid NSPR addressable path
806 if (mWorkingPath
.IsEmpty())
807 return NS_ERROR_FILE_INVALID_PATH
;
809 // this is usually correct
810 mResolvedPath
.Assign(mWorkingPath
);
812 // slutty hack designed to work around bug 134796 until it is fixed
813 nsAutoString
nsprPath(mWorkingPath
.get());
814 if (mWorkingPath
.Length() == 2 && mWorkingPath
.CharAt(1) == L
':')
815 nsprPath
.AppendASCII("\\");
817 // first we will see if the working path exists. If it doesn't then
818 // there is nothing more that can be done
819 if (NS_FAILED(GetFileInfo(nsprPath
, &mFileInfo64
)))
820 return NS_ERROR_FILE_NOT_FOUND
;
822 // if this isn't a shortcut file or we aren't following symlinks then we're done
824 || mFileInfo64
.type
!= PR_FILE_FILE
825 || !IsShortcutPath(mWorkingPath
))
831 // we need to resolve this shortcut to what it points to, this will
832 // set mResolvedPath. Even if it fails we need to have the resolved
833 // path equal to working path for those functions that always use
834 // the resolved path.
835 nsresult rv
= ResolveShortcut();
838 mResolvedPath
.Assign(mWorkingPath
);
842 // get the details of the resolved path
843 if (NS_FAILED(GetFileInfo(mResolvedPath
, &mFileInfo64
)))
844 return NS_ERROR_FILE_NOT_FOUND
;
851 //-----------------------------------------------------------------------------
852 // nsLocalFile::nsIFile,nsILocalFile
853 //-----------------------------------------------------------------------------
856 nsLocalFile::Clone(nsIFile
**file
)
858 // Just copy-construct ourselves
859 *file
= new nsLocalFile(*this);
861 return NS_ERROR_OUT_OF_MEMORY
;
869 nsLocalFile::InitWithFile(nsILocalFile
*aFile
)
871 NS_ENSURE_ARG(aFile
);
874 aFile
->GetPath(path
);
876 return NS_ERROR_INVALID_ARG
;
877 return InitWithPath(path
);
881 nsLocalFile::InitWithPath(const nsAString
&filePath
)
885 nsAString::const_iterator begin
, end
;
886 filePath
.BeginReading(begin
);
887 filePath
.EndReading(end
);
889 // input string must not be empty
891 return NS_ERROR_FAILURE
;
893 PRUnichar firstChar
= *begin
;
894 PRUnichar secondChar
= *(++begin
);
896 // just do a sanity check. if it has any forward slashes, it is not a Native path
897 // on windows. Also, it must have a colon at after the first char.
899 PRUnichar
*path
= nsnull
;
902 if ( ( (secondChar
== L
':') && !FindCharInReadable(L
'/', begin
, end
) ) || // normal path
904 ( (firstChar
== L
'\\') ) // wince absolute path or network path
906 ( (firstChar
== L
'\\') && (secondChar
== L
'\\') ) // network path
910 // This is a native path
911 path
= ToNewUnicode(filePath
);
912 pathLen
= filePath
.Length();
915 if (path
== nsnull
) {
916 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
919 // kill any trailing '\'
920 PRInt32 len
= pathLen
- 1;
921 if (path
[len
] == L
'\\')
927 mWorkingPath
.Adopt(path
, pathLen
);
933 nsLocalFile::OpenNSPRFileDesc(PRInt32 flags
, PRInt32 mode
, PRFileDesc
**_retval
)
935 nsresult rv
= ResolveAndStat();
936 if (NS_FAILED(rv
) && rv
!= NS_ERROR_FILE_NOT_FOUND
)
939 return OpenFile(mResolvedPath
, flags
, mode
, _retval
);
944 nsLocalFile::OpenANSIFileDesc(const char *mode
, FILE * *_retval
)
946 nsresult rv
= ResolveAndStat();
947 if (NS_FAILED(rv
) && rv
!= NS_ERROR_FILE_NOT_FOUND
)
950 *_retval
= _wfopen(mResolvedPath
.get(), NS_ConvertASCIItoUTF16(mode
).get());
954 return NS_ERROR_FAILURE
;
960 nsLocalFile::Create(PRUint32 type
, PRUint32 attributes
)
962 if (type
!= NORMAL_FILE_TYPE
&& type
!= DIRECTORY_TYPE
)
963 return NS_ERROR_FILE_UNKNOWN_TYPE
;
965 nsresult rv
= ResolveAndStat();
966 if (NS_FAILED(rv
) && rv
!= NS_ERROR_FILE_NOT_FOUND
)
969 // create directories to target
971 // A given local file can be either one of these forms:
973 // - normal: X:\some\path\on\this\drive
976 // - UNC path: \\machine\volume\some\path\on\this\drive
979 // Skip the first 'X:\' for the first form, and skip the first full
980 // '\\machine\volume\' segment for the second form.
982 PRUnichar
* path
= mResolvedPath
.BeginWriting();
984 if (path
[0] == L
'\\' && path
[1] == L
'\\')
989 // dealing with a UNC path here; skip past '\\machine\'
990 path
= wcschr(path
+ 2, L
'\\');
992 return NS_ERROR_FILE_INVALID_PATH
;
997 // search for first slash after the drive (or volume) name
998 PRUnichar
* slash
= wcschr(path
, L
'\\');
1002 // skip the first '\\'
1004 slash
= wcschr(slash
, L
'\\');
1010 if (!::CreateDirectoryW(mResolvedPath
.get(), NULL
)) {
1011 rv
= ConvertWinError(GetLastError());
1012 // perhaps the base path already exists, or perhaps we don't have
1013 // permissions to create the directory. NOTE: access denied could
1014 // occur on a parent directory even though it exists.
1015 if (rv
!= NS_ERROR_FILE_ALREADY_EXISTS
&&
1016 rv
!= NS_ERROR_FILE_ACCESS_DENIED
)
1021 slash
= wcschr(slash
, L
'\\');
1025 if (type
== NORMAL_FILE_TYPE
)
1028 rv
= OpenFile(mResolvedPath
,
1029 PR_RDONLY
| PR_CREATE_FILE
| PR_APPEND
| PR_EXCL
, attributes
,
1036 if (type
== DIRECTORY_TYPE
)
1038 if (!::CreateDirectoryW(mResolvedPath
.get(), NULL
))
1039 return ConvertWinError(GetLastError());
1044 return NS_ERROR_FILE_UNKNOWN_TYPE
;
1049 nsLocalFile::Append(const nsAString
&node
)
1051 // append this path, multiple components are not permitted
1052 return AppendInternal(PromiseFlatString(node
), PR_FALSE
);
1056 nsLocalFile::AppendRelativePath(const nsAString
&node
)
1058 // append this path, multiple components are permitted
1059 return AppendInternal(PromiseFlatString(node
), PR_TRUE
);
1064 nsLocalFile::AppendInternal(const nsAFlatString
&node
, PRBool multipleComponents
)
1069 // check the relative path for validity
1070 if (node
.First() == L
'\\' // can't start with an '\'
1071 || node
.FindChar(L
'/') != kNotFound
// can't contain /
1072 || node
.EqualsASCII("..")) // can't be ..
1073 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
1075 #ifndef WINCE // who cares?
1076 if (multipleComponents
)
1078 // can't contain .. as a path component. Ensure that the valid components
1079 // "foo..foo", "..foo", and "foo.." are not falsely detected,
1080 // but the invalid paths "..\", "foo\..", "foo\..\foo",
1081 // "..\foo", etc are.
1082 NS_NAMED_LITERAL_STRING(doubleDot
, "\\..");
1083 nsAString::const_iterator start
, end
, offset
;
1084 node
.BeginReading(start
);
1085 node
.EndReading(end
);
1087 while (FindInReadable(doubleDot
, start
, offset
))
1089 if (offset
== end
|| *offset
== L
'\\')
1090 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
1095 // catches the remaining cases of prefixes
1096 if (StringBeginsWith(node
, NS_LITERAL_STRING("..\\")))
1097 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
1099 // single components can't contain '\'
1100 else if (node
.FindChar(L
'\\') != kNotFound
)
1101 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
1106 mWorkingPath
.Append(NS_LITERAL_STRING("\\") + node
);
1111 #define TOUPPER(u) (((u) >= L'a' && (u) <= L'z') ? \
1112 (u) - (L'a' - L'A') : (u))
1115 nsLocalFile::Normalize()
1118 // XXX See bug 187957 comment 18 for possible problems with this implementation.
1120 if (mWorkingPath
.IsEmpty())
1123 nsAutoString
path(mWorkingPath
);
1125 // find the index of the root backslash for the path. Everything before
1126 // this is considered fully normalized and cannot be ascended beyond
1127 // using ".." For a local drive this is the first slash (e.g. "c:\").
1128 // For a UNC path it is the slash following the share name
1129 // (e.g. "\\server\share\").
1130 PRInt32 rootIdx
= 2; // default to local drive
1131 if (path
.First() == L
'\\') // if a share then calculate the rootIdx
1133 rootIdx
= path
.FindChar(L
'\\', 2); // skip \\ in front of the server
1134 if (rootIdx
== kNotFound
)
1135 return NS_OK
; // already normalized
1136 rootIdx
= path
.FindChar(L
'\\', rootIdx
+1);
1137 if (rootIdx
== kNotFound
)
1138 return NS_OK
; // already normalized
1140 else if (path
.CharAt(rootIdx
) != L
'\\')
1142 // The path has been specified relative to the current working directory
1143 // for that drive. To normalize it, the current working directory for
1144 // that drive needs to be inserted before the supplied relative path
1145 // which will provide an absolute path (and the rootIdx will still be 2).
1146 WCHAR cwd
[MAX_PATH
];
1148 int drive
= TOUPPER(path
.First()) - 'A' + 1;
1149 /* We need to worry about IPH, for details read bug 419326.
1150 * _getdrives - http://msdn2.microsoft.com/en-us/library/xdhk0xd2.aspx
1151 * uses a bitmask, bit 0 is 'a:'
1152 * _chdrive - http://msdn2.microsoft.com/en-us/library/0d1409hb.aspx
1153 * _getdcwd - http://msdn2.microsoft.com/en-us/library/7t2zk3s4.aspx
1154 * take an int, 1 is 'a:'.
1156 * Because of this, we need to do some math. Subtract 1 to convert from
1157 * _chdrive/_getdcwd format to _getdrives drive numbering.
1158 * Shift left x bits to convert from integer indexing to bitfield indexing.
1159 * And of course, we need to find out if the drive is in the bitmask.
1161 * If we're really unlucky, we can still lose, but only if the user
1162 * manages to eject the drive between our call to _getdrives() and
1163 * our *calls* to _wgetdcwd.
1165 if (!((1 << (drive
- 1)) & _getdrives()))
1166 return NS_ERROR_FILE_INVALID_PATH
;
1167 if (!_wgetdcwd(drive
, pcwd
, MAX_PATH
))
1168 pcwd
= _wgetdcwd(drive
, 0, 0);
1170 return NS_ERROR_OUT_OF_MEMORY
;
1172 nsAutoString
currentDir(pcwd
);
1176 if (currentDir
.Last() == '\\')
1177 path
.Replace(0, 2, currentDir
);
1179 path
.Replace(0, 2, currentDir
+ NS_LITERAL_STRING("\\"));
1181 NS_POSTCONDITION(0 < rootIdx
&& rootIdx
< (PRInt32
)path
.Length(), "rootIdx is invalid");
1182 NS_POSTCONDITION(path
.CharAt(rootIdx
) == '\\', "rootIdx is invalid");
1184 // if there is nothing following the root path then it is already normalized
1185 if (rootIdx
+ 1 == (PRInt32
)path
.Length())
1189 const PRUnichar
* pathBuffer
= path
.get(); // simplify access to the buffer
1190 mWorkingPath
.SetCapacity(path
.Length()); // it won't ever grow longer
1191 mWorkingPath
.Assign(pathBuffer
, rootIdx
);
1193 // Normalize the path components. The actions taken are:
1195 // "\\" condense to single backslash
1196 // "." remove from path
1197 // ".." up a directory
1198 // "..." remove from path (any number of dots > 2)
1200 // The last form is something that Windows 95 and 98 supported and
1201 // is a shortcut for changing up multiple directories. Windows XP
1202 // and ilk ignore it in a path, as is done here.
1203 PRInt32 len
, begin
, end
= rootIdx
;
1204 while (end
< (PRInt32
)path
.Length())
1206 // find the current segment (text between the backslashes) to
1207 // be examined, this will set the following variables:
1208 // begin == index of first char in segment
1209 // end == index 1 char after last char in segment
1210 // len == length of segment
1212 end
= path
.FindChar('\\', begin
);
1213 if (end
== kNotFound
)
1214 end
= path
.Length();
1217 // ignore double backslashes
1221 // len != 0, and interesting paths always begin with a dot
1222 if (pathBuffer
[begin
] == '.')
1224 // ignore single dots
1228 // handle multiple dots
1229 if (len
>= 2 && pathBuffer
[begin
+1] == L
'.')
1231 // back up a path component on double dot
1234 PRInt32 prev
= mWorkingPath
.RFindChar('\\');
1235 if (prev
>= rootIdx
)
1236 mWorkingPath
.Truncate(prev
);
1240 // length is > 2 and the first two characters are dots.
1241 // if the rest of the string is dots, then ignore it.
1243 for (; idx
>= 2; --idx
)
1245 if (pathBuffer
[begin
+idx
] != L
'.')
1249 // this is true if the loop above didn't break
1250 // and all characters in this segment are dots.
1256 // add the current component to the path, including the preceding backslash
1257 mWorkingPath
.Append(pathBuffer
+ begin
- 1, len
+ 1);
1260 // kill trailing dots and spaces.
1261 PRInt32 filePathLen
= mWorkingPath
.Length() - 1;
1262 while(filePathLen
> 0 && (mWorkingPath
[filePathLen
] == L
' ' ||
1263 mWorkingPath
[filePathLen
] == L
'.'))
1265 mWorkingPath
.Truncate(filePathLen
--);
1276 nsLocalFile::GetLeafName(nsAString
&aLeafName
)
1278 aLeafName
.Truncate();
1280 if(mWorkingPath
.IsEmpty())
1281 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
1283 PRInt32 offset
= mWorkingPath
.RFindChar(L
'\\');
1285 // if the working path is just a node without any lashes.
1286 if (offset
== kNotFound
)
1287 aLeafName
= mWorkingPath
;
1289 aLeafName
= Substring(mWorkingPath
, offset
+ 1);
1295 nsLocalFile::SetLeafName(const nsAString
&aLeafName
)
1299 if(mWorkingPath
.IsEmpty())
1300 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
1302 // cannot use nsCString::RFindChar() due to 0x5c problem
1303 PRInt32 offset
= mWorkingPath
.RFindChar(L
'\\');
1306 mWorkingPath
.Truncate(offset
+1);
1308 mWorkingPath
.Append(aLeafName
);
1315 nsLocalFile::GetPath(nsAString
&_retval
)
1317 _retval
= mWorkingPath
;
1322 nsLocalFile::GetCanonicalPath(nsAString
&aResult
)
1325 aResult
.Assign(mShortWorkingPath
);
1335 nsLocalFile::GetVersionInfoField(const char* aField
, nsAString
& _retval
)
1337 nsresult rv
= ResolveAndStat();
1341 rv
= NS_ERROR_FAILURE
;
1343 // Cast away const-ness here because WinAPI functions don't understand it,
1344 // the path is used for [in] parameters only however so it's safe.
1345 WCHAR
*path
= const_cast<WCHAR
*>(mFollowSymlinks
? mResolvedPath
.get()
1346 : mWorkingPath
.get());
1349 DWORD size
= ::GetFileVersionInfoSizeW(path
, &dummy
);
1353 void* ver
= calloc(size
, 1);
1355 return NS_ERROR_OUT_OF_MEMORY
;
1357 if (::GetFileVersionInfoW(path
, 0, size
, ver
))
1359 LANGANDCODEPAGE
* translate
= nsnull
;
1361 BOOL queryResult
= ::VerQueryValueW(ver
, L
"\\VarFileInfo\\Translation",
1362 (void**)&translate
, &pageCount
);
1363 if (queryResult
&& translate
)
1365 for (PRInt32 i
= 0; i
< 2; ++i
)
1367 PRUnichar subBlock
[MAX_PATH
];
1368 _snwprintf(subBlock
, MAX_PATH
,
1369 L
"\\StringFileInfo\\%04x%04x\\%s",
1370 (i
== 0 ? translate
[0].wLanguage
1371 : ::GetUserDefaultLangID()),
1372 translate
[0].wCodePage
,
1373 NS_ConvertASCIItoUTF16(
1374 nsDependentCString(aField
)).get());
1375 subBlock
[MAX_PATH
- 1] = 0;
1376 LPVOID value
= nsnull
;
1378 queryResult
= ::VerQueryValueW(ver
, subBlock
, &value
, &size
);
1379 if (queryResult
&& value
)
1381 _retval
.Assign(static_cast<PRUnichar
*>(value
));
1382 if (!_retval
.IsEmpty())
1397 nsLocalFile::CopySingleFile(nsIFile
*sourceFile
, nsIFile
*destParent
,
1398 const nsAString
&newName
,
1399 PRBool followSymlinks
, PRBool move
)
1402 nsAutoString filePath
;
1404 // get the path that we are going to copy to.
1405 // Since windows does not know how to auto
1406 // resolve shortcuts, we must work with the
1408 nsAutoString destPath
;
1409 destParent
->GetTarget(destPath
);
1411 destPath
.AppendASCII("\\");
1413 if (newName
.IsEmpty())
1415 nsAutoString aFileName
;
1416 sourceFile
->GetLeafName(aFileName
);
1417 destPath
.Append(aFileName
);
1421 destPath
.Append(newName
);
1427 rv
= sourceFile
->GetTarget(filePath
);
1428 if (filePath
.IsEmpty())
1429 rv
= sourceFile
->GetPath(filePath
);
1433 rv
= sourceFile
->GetPath(filePath
);
1442 copyOK
= ::CopyFileW(filePath
.get(), destPath
.get(), PR_TRUE
);
1444 copyOK
= ::MoveFileExW(filePath
.get(), destPath
.get(),
1445 MOVEFILE_REPLACE_EXISTING
|
1446 MOVEFILE_COPY_ALLOWED
|
1447 MOVEFILE_WRITE_THROUGH
);
1449 if (!copyOK
) // CopyFile and MoveFileEx return zero at failure.
1450 rv
= ConvertWinError(GetLastError());
1452 if(move
) //Set security permissions to inherit from parent.
1455 if (InitializeAcl(&empty_acl
, (DWORD
) sizeof(ACL
), ACL_REVISION
))
1457 ::SetNamedSecurityInfoW((LPWSTR
)destPath
.get(), SE_FILE_OBJECT
,
1458 DACL_SECURITY_INFORMATION
|
1459 UNPROTECTED_DACL_SECURITY_INFORMATION
,
1460 NULL
, NULL
, &empty_acl
, NULL
);
1469 nsLocalFile::CopyMove(nsIFile
*aParentDir
, const nsAString
&newName
, PRBool followSymlinks
, PRBool move
)
1471 nsCOMPtr
<nsIFile
> newParentDir
= aParentDir
;
1472 // check to see if this exists, otherwise return an error.
1473 // we will check this by resolving. If the user wants us
1474 // to follow links, then we are talking about the target,
1475 // hence we can use the |followSymlinks| parameter.
1476 nsresult rv
= ResolveAndStat();
1482 // no parent was specified. We must rename.
1484 if (newName
.IsEmpty())
1485 return NS_ERROR_INVALID_ARG
;
1487 rv
= GetParent(getter_AddRefs(newParentDir
));
1493 return NS_ERROR_FILE_DESTINATION_NOT_DIR
;
1495 // make sure it exists and is a directory. Create it if not there.
1497 newParentDir
->Exists(&exists
);
1500 rv
= newParentDir
->Create(DIRECTORY_TYPE
, 0644); // TODO, what permissions should we use
1507 newParentDir
->IsDirectory(&isDir
);
1508 if (isDir
== PR_FALSE
)
1513 newParentDir
->IsSymlink(&isLink
);
1516 nsAutoString target
;
1517 newParentDir
->GetTarget(target
);
1519 nsCOMPtr
<nsILocalFile
> realDest
= new nsLocalFile();
1520 if (realDest
== nsnull
)
1521 return NS_ERROR_OUT_OF_MEMORY
;
1523 rv
= realDest
->InitWithPath(target
);
1528 return CopyMove(realDest
, newName
, followSymlinks
, move
);
1533 return NS_ERROR_FILE_DESTINATION_NOT_DIR
;
1538 // Try different ways to move/copy files/directories
1539 PRBool done
= PR_FALSE
;
1541 IsDirectory(&isDir
);
1543 IsSymlink(&isSymlink
);
1545 // Try to move the file or directory, or try to copy a single file (or non-followed symlink)
1546 if (move
|| !isDir
|| (isSymlink
&& !followSymlinks
))
1548 // Copy/Move single file, or move a directory
1549 rv
= CopySingleFile(this, newParentDir
, newName
, followSymlinks
, move
);
1550 done
= NS_SUCCEEDED(rv
);
1551 // If we are moving a directory and that fails, fallback on directory
1552 // enumeration. See bug 231300 for details.
1553 if (!done
&& !(move
&& isDir
))
1557 // Not able to copy or move directly, so enumerate it
1560 // create a new target destination in the new parentDir;
1561 nsCOMPtr
<nsIFile
> target
;
1562 rv
= newParentDir
->Clone(getter_AddRefs(target
));
1567 nsAutoString allocatedNewName
;
1568 if (newName
.IsEmpty())
1576 PRInt32 offset
= temp
.RFindChar(L
'\\');
1577 if (offset
== kNotFound
)
1578 allocatedNewName
= temp
;
1580 allocatedNewName
= Substring(temp
, offset
+ 1);
1584 GetLeafName(allocatedNewName
);// this should be the leaf name of the
1589 allocatedNewName
= newName
;
1592 rv
= target
->Append(allocatedNewName
);
1596 allocatedNewName
.Truncate();
1598 // check if the destination directory already exists
1599 target
->Exists(&exists
);
1602 // if the destination directory cannot be created, return an error
1603 rv
= target
->Create(DIRECTORY_TYPE
, 0644); // TODO, what permissions should we use
1609 // check if the destination directory is writable and empty
1612 target
->IsWritable(&isWritable
);
1614 return NS_ERROR_FILE_ACCESS_DENIED
;
1616 nsCOMPtr
<nsISimpleEnumerator
> targetIterator
;
1617 rv
= target
->GetDirectoryEntries(getter_AddRefs(targetIterator
));
1620 targetIterator
->HasMoreElements(&more
);
1621 // return error if target directory is not empty
1623 return NS_ERROR_FILE_DIR_NOT_EMPTY
;
1626 nsDirEnumerator dirEnum
;
1628 rv
= dirEnum
.Init(this);
1629 if (NS_FAILED(rv
)) {
1630 NS_WARNING("dirEnum initialization failed");
1635 while (NS_SUCCEEDED(dirEnum
.HasMoreElements(&more
)) && more
)
1637 nsCOMPtr
<nsISupports
> item
;
1638 nsCOMPtr
<nsIFile
> file
;
1639 dirEnum
.GetNext(getter_AddRefs(item
));
1640 file
= do_QueryInterface(item
);
1643 PRBool isDir
, isLink
;
1645 file
->IsDirectory(&isDir
);
1646 file
->IsSymlink(&isLink
);
1651 return NS_ERROR_FAILURE
;
1653 rv
= file
->MoveTo(target
, EmptyString());
1654 NS_ENSURE_SUCCESS(rv
,rv
);
1659 rv
= file
->CopyToFollowingLinks(target
, EmptyString());
1661 rv
= file
->CopyTo(target
, EmptyString());
1662 NS_ENSURE_SUCCESS(rv
,rv
);
1666 // we've finished moving all the children of this directory
1667 // in the new directory. so now delete the directory
1668 // note, we don't need to do a recursive delete.
1669 // MoveTo() is recursive. At this point,
1670 // we've already moved the children of the current folder
1671 // to the new location. nothing should be left in the folder.
1674 rv
= Remove(PR_FALSE
/* recursive */);
1675 NS_ENSURE_SUCCESS(rv
,rv
);
1680 // If we moved, we want to adjust this.
1685 nsAutoString newParentPath
;
1686 newParentDir
->GetPath(newParentPath
);
1688 if (newParentPath
.IsEmpty())
1689 return NS_ERROR_FAILURE
;
1691 if (newName
.IsEmpty())
1693 nsAutoString aFileName
;
1694 GetLeafName(aFileName
);
1696 InitWithPath(newParentPath
);
1701 InitWithPath(newParentPath
);
1710 nsLocalFile::CopyTo(nsIFile
*newParentDir
, const nsAString
&newName
)
1712 return CopyMove(newParentDir
, newName
, PR_FALSE
, PR_FALSE
);
1716 nsLocalFile::CopyToFollowingLinks(nsIFile
*newParentDir
, const nsAString
&newName
)
1718 return CopyMove(newParentDir
, newName
, PR_TRUE
, PR_FALSE
);
1722 nsLocalFile::MoveTo(nsIFile
*newParentDir
, const nsAString
&newName
)
1724 return CopyMove(newParentDir
, newName
, PR_FALSE
, PR_TRUE
);
1729 nsLocalFile::Load(PRLibrary
* *_retval
)
1731 // Check we are correctly initialized.
1732 CHECK_mWorkingPath();
1735 nsresult rv
= IsFile(&isFile
);
1741 return NS_ERROR_FILE_IS_DIRECTORY
;
1743 NS_TIMELINE_START_TIMER("PR_LoadLibraryWithFlags");
1745 #ifdef NS_BUILD_REFCNT_LOGGING
1746 nsTraceRefcntImpl::SetActivityIsLegal(PR_FALSE
);
1750 libSpec
.value
.pathname_u
= mResolvedPath
.get();
1751 libSpec
.type
= PR_LibSpec_PathnameU
;
1752 *_retval
= PR_LoadLibraryWithFlags(libSpec
, 0);
1754 #ifdef NS_BUILD_REFCNT_LOGGING
1755 nsTraceRefcntImpl::SetActivityIsLegal(PR_TRUE
);
1758 NS_TIMELINE_STOP_TIMER("PR_LoadLibraryWithFlags");
1759 NS_TIMELINE_MARK_TIMER1("PR_LoadLibraryWithFlags",
1760 NS_ConvertUTF16toUTF8(mResolvedPath
).get());
1764 return NS_ERROR_NULL_POINTER
;
1768 nsLocalFile::Remove(PRBool recursive
)
1772 // if the working path points to a shortcut, then we will only
1773 // delete the shortcut itself. even if the shortcut points to
1774 // a directory, we will not recurse into that directory or
1775 // delete that directory itself. likewise, if the shortcut
1776 // points to a normal file, we will not delete the real file.
1777 // this is done to be consistent with the other platforms that
1778 // behave this way. we do this even if the followLinks attribute
1779 // is set to true. this helps protect against misuse that could
1780 // lead to security bugs (e.g., bug 210588).
1782 // Since shortcut files are no longer permitted to be used as unix-like
1783 // symlinks interspersed in the path (e.g. "c:/file.lnk/foo/bar.txt")
1784 // this processing is a lot simpler. Even if the shortcut file is
1785 // pointing to a directory, only the mWorkingPath value is used and so
1786 // only the shortcut file will be deleted.
1788 // Check we are correctly initialized.
1789 CHECK_mWorkingPath();
1791 PRBool isDir
, isLink
;
1795 rv
= IsSymlink(&isLink
);
1799 // only check to see if we have a directory if it isn't a link
1802 rv
= IsDirectory(&isDir
);
1811 nsDirEnumerator dirEnum
;
1813 rv
= dirEnum
.Init(this);
1818 while (NS_SUCCEEDED(dirEnum
.HasMoreElements(&more
)) && more
)
1820 nsCOMPtr
<nsISupports
> item
;
1821 dirEnum
.GetNext(getter_AddRefs(item
));
1822 nsCOMPtr
<nsIFile
> file
= do_QueryInterface(item
);
1824 file
->Remove(recursive
);
1827 rv
= _wrmdir(mWorkingPath
.get());
1831 rv
= _wremove(mWorkingPath
.get());
1834 // fixup error code if necessary...
1835 if (rv
== (nsresult
)-1)
1836 rv
= NSRESULT_FOR_ERRNO();
1843 nsLocalFile::GetLastModifiedTime(PRInt64
*aLastModifiedTime
)
1845 // Check we are correctly initialized.
1846 CHECK_mWorkingPath();
1848 NS_ENSURE_ARG(aLastModifiedTime
);
1850 // get the modified time of the target as determined by mFollowSymlinks
1851 // If PR_TRUE, then this will be for the target of the shortcut file,
1852 // otherwise it will be for the shortcut file itself (i.e. the same
1853 // results as GetLastModifiedTimeOfLink)
1855 nsresult rv
= ResolveAndStat();
1859 // microseconds -> milliseconds
1860 PRInt64 usecPerMsec
;
1861 LL_I2L(usecPerMsec
, PR_USEC_PER_MSEC
);
1862 LL_DIV(*aLastModifiedTime
, mFileInfo64
.modifyTime
, usecPerMsec
);
1868 nsLocalFile::GetLastModifiedTimeOfLink(PRInt64
*aLastModifiedTime
)
1870 // Check we are correctly initialized.
1871 CHECK_mWorkingPath();
1873 NS_ENSURE_ARG(aLastModifiedTime
);
1875 // The caller is assumed to have already called IsSymlink
1876 // and to have found that this file is a link.
1879 nsresult rv
= GetFileInfo(mWorkingPath
, &info
);
1883 // microseconds -> milliseconds
1884 PRInt64 usecPerMsec
;
1885 LL_I2L(usecPerMsec
, PR_USEC_PER_MSEC
);
1886 LL_DIV(*aLastModifiedTime
, info
.modifyTime
, usecPerMsec
);
1892 nsLocalFile::SetLastModifiedTime(PRInt64 aLastModifiedTime
)
1894 // Check we are correctly initialized.
1895 CHECK_mWorkingPath();
1897 nsresult rv
= ResolveAndStat();
1901 // set the modified time of the target as determined by mFollowSymlinks
1902 // If PR_TRUE, then this will be for the target of the shortcut file,
1903 // otherwise it will be for the shortcut file itself (i.e. the same
1904 // results as SetLastModifiedTimeOfLink)
1906 rv
= SetModDate(aLastModifiedTime
, mResolvedPath
.get());
1907 if (NS_SUCCEEDED(rv
))
1915 nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModifiedTime
)
1917 // The caller is assumed to have already called IsSymlink
1918 // and to have found that this file is a link.
1920 nsresult rv
= SetModDate(aLastModifiedTime
, mWorkingPath
.get());
1921 if (NS_SUCCEEDED(rv
))
1928 nsLocalFile::SetModDate(PRInt64 aLastModifiedTime
, const PRUnichar
*filePath
)
1930 HANDLE file
= ::CreateFileW(filePath
, // pointer to name of the file
1931 GENERIC_WRITE
, // access (write) mode
1933 NULL
, // pointer to security attributes
1934 OPEN_EXISTING
, // how to create
1935 0, // file attributes
1938 if (file
== INVALID_HANDLE_VALUE
)
1940 return ConvertWinError(GetLastError());
1945 PRExplodedTime pret
;
1947 // PR_ExplodeTime expects usecs...
1948 PR_ExplodeTime(aLastModifiedTime
* PR_USEC_PER_MSEC
, PR_LocalTimeParameters
, &pret
);
1949 st
.wYear
= pret
.tm_year
;
1950 st
.wMonth
= pret
.tm_month
+ 1; // Convert start offset -- Win32: Jan=1; NSPR: Jan=0
1951 st
.wDayOfWeek
= pret
.tm_wday
;
1952 st
.wDay
= pret
.tm_mday
;
1953 st
.wHour
= pret
.tm_hour
;
1954 st
.wMinute
= pret
.tm_min
;
1955 st
.wSecond
= pret
.tm_sec
;
1956 st
.wMilliseconds
= pret
.tm_usec
/1000;
1958 nsresult rv
= NS_OK
;
1959 // if at least one of these fails...
1960 if (!(SystemTimeToFileTime(&st
, &lft
) != 0 &&
1961 LocalFileTimeToFileTime(&lft
, &ft
) != 0 &&
1962 SetFileTime(file
, NULL
, &ft
, &ft
) != 0))
1964 rv
= ConvertWinError(GetLastError());
1972 nsLocalFile::GetPermissions(PRUint32
*aPermissions
)
1974 NS_ENSURE_ARG(aPermissions
);
1976 // get the permissions of the target as determined by mFollowSymlinks
1977 // If PR_TRUE, then this will be for the target of the shortcut file,
1978 // otherwise it will be for the shortcut file itself (i.e. the same
1979 // results as GetPermissionsOfLink)
1980 nsresult rv
= ResolveAndStat();
1984 PRBool isWritable
, isExecutable
;
1985 IsWritable(&isWritable
);
1986 IsExecutable(&isExecutable
);
1988 *aPermissions
= PR_IRUSR
|PR_IRGRP
|PR_IROTH
; // all read
1990 *aPermissions
|= PR_IWUSR
|PR_IWGRP
|PR_IWOTH
; // all write
1992 *aPermissions
|= PR_IXUSR
|PR_IXGRP
|PR_IXOTH
; // all execute
1998 nsLocalFile::GetPermissionsOfLink(PRUint32
*aPermissions
)
2000 // Check we are correctly initialized.
2001 CHECK_mWorkingPath();
2003 NS_ENSURE_ARG(aPermissions
);
2005 // The caller is assumed to have already called IsSymlink
2006 // and to have found that this file is a link. It is not
2007 // possible for a link file to be executable.
2009 DWORD word
= ::GetFileAttributesW(mWorkingPath
.get());
2010 if (word
== INVALID_FILE_ATTRIBUTES
)
2011 return NS_ERROR_FILE_INVALID_PATH
;
2013 PRBool isWritable
= !(word
& FILE_ATTRIBUTE_READONLY
);
2014 *aPermissions
= PR_IRUSR
|PR_IRGRP
|PR_IROTH
; // all read
2016 *aPermissions
|= PR_IWUSR
|PR_IWGRP
|PR_IWOTH
; // all write
2023 nsLocalFile::SetPermissions(PRUint32 aPermissions
)
2025 // Check we are correctly initialized.
2026 CHECK_mWorkingPath();
2028 // set the permissions of the target as determined by mFollowSymlinks
2029 // If PR_TRUE, then this will be for the target of the shortcut file,
2030 // otherwise it will be for the shortcut file itself (i.e. the same
2031 // results as SetPermissionsOfLink)
2032 nsresult rv
= ResolveAndStat();
2036 // windows only knows about the following permissions
2038 if (aPermissions
& (PR_IRUSR
|PR_IRGRP
|PR_IROTH
)) // any read
2040 if (aPermissions
& (PR_IWUSR
|PR_IWGRP
|PR_IWOTH
)) // any write
2043 if (_wchmod(mResolvedPath
.get(), mode
) == -1)
2044 return NS_ERROR_FAILURE
;
2050 nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions
)
2052 // The caller is assumed to have already called IsSymlink
2053 // and to have found that this file is a link.
2055 // windows only knows about the following permissions
2057 if (aPermissions
& (PR_IRUSR
|PR_IRGRP
|PR_IROTH
)) // any read
2059 if (aPermissions
& (PR_IWUSR
|PR_IWGRP
|PR_IWOTH
)) // any write
2062 if (_wchmod(mWorkingPath
.get(), mode
) == -1)
2063 return NS_ERROR_FAILURE
;
2070 nsLocalFile::GetFileSize(PRInt64
*aFileSize
)
2072 NS_ENSURE_ARG(aFileSize
);
2074 nsresult rv
= ResolveAndStat();
2078 *aFileSize
= mFileInfo64
.size
;
2084 nsLocalFile::GetFileSizeOfLink(PRInt64
*aFileSize
)
2086 // Check we are correctly initialized.
2087 CHECK_mWorkingPath();
2089 NS_ENSURE_ARG(aFileSize
);
2091 // The caller is assumed to have already called IsSymlink
2092 // and to have found that this file is a link.
2095 if (NS_FAILED(GetFileInfo(mWorkingPath
, &info
)))
2096 return NS_ERROR_FILE_INVALID_PATH
;
2098 *aFileSize
= info
.size
;
2103 nsLocalFile::SetFileSize(PRInt64 aFileSize
)
2105 // Check we are correctly initialized.
2106 CHECK_mWorkingPath();
2108 nsresult rv
= ResolveAndStat();
2112 HANDLE hFile
= ::CreateFileW(mResolvedPath
.get(),// pointer to name of the file
2113 GENERIC_WRITE
, // access (write) mode
2114 FILE_SHARE_READ
, // share mode
2115 NULL
, // pointer to security attributes
2116 OPEN_EXISTING
, // how to create
2117 FILE_ATTRIBUTE_NORMAL
, // file attributes
2119 if (hFile
== INVALID_HANDLE_VALUE
)
2121 return ConvertWinError(GetLastError());
2124 // seek the file pointer to the new, desired end of file
2125 // and then truncate the file at that position
2126 rv
= NS_ERROR_FAILURE
;
2127 aFileSize
= MyFileSeek64(hFile
, aFileSize
, FILE_BEGIN
);
2128 if (aFileSize
!= -1 && SetEndOfFile(hFile
))
2139 nsLocalFile::GetDiskSpaceAvailable(PRInt64
*aDiskSpaceAvailable
)
2141 // Check we are correctly initialized.
2142 CHECK_mWorkingPath();
2145 NS_ENSURE_ARG(aDiskSpaceAvailable
);
2149 ULARGE_INTEGER liFreeBytesAvailableToCaller
, liTotalNumberOfBytes
;
2150 if (::GetDiskFreeSpaceExW(mResolvedPath
.get(), &liFreeBytesAvailableToCaller
,
2151 &liTotalNumberOfBytes
, NULL
))
2153 *aDiskSpaceAvailable
= liFreeBytesAvailableToCaller
.QuadPart
;
2158 *aDiskSpaceAvailable
= 0;
2163 nsLocalFile::GetParent(nsIFile
* *aParent
)
2165 // Check we are correctly initialized.
2166 CHECK_mWorkingPath();
2168 NS_ENSURE_ARG_POINTER(aParent
);
2170 // A two-character path must be a drive such as C:, so it has no parent
2171 if (mWorkingPath
.Length() == 2) {
2176 PRInt32 offset
= mWorkingPath
.RFindChar(PRUnichar('\\'));
2177 // adding this offset check that was removed in bug 241708 fixes mail
2178 // directories that aren't relative to/underneath the profile dir.
2179 // e.g., on a different drive. Before you remove them, please make
2180 // sure local mail directories that aren't underneath the profile dir work.
2181 if (offset
== kNotFound
)
2182 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
2184 // A path of the form \\NAME is a top-level path and has no parent
2185 if (offset
== 1 && mWorkingPath
[0] == L
'\\') {
2190 nsAutoString
parentPath(mWorkingPath
);
2193 parentPath
.Truncate(offset
);
2195 parentPath
.AssignLiteral("\\\\.");
2197 nsCOMPtr
<nsILocalFile
> localFile
;
2198 nsresult rv
= NS_NewLocalFile(parentPath
, mFollowSymlinks
, getter_AddRefs(localFile
));
2200 if(NS_SUCCEEDED(rv
) && localFile
)
2202 return CallQueryInterface(localFile
, aParent
);
2208 nsLocalFile::Exists(PRBool
*_retval
)
2210 // Check we are correctly initialized.
2211 CHECK_mWorkingPath();
2213 NS_ENSURE_ARG(_retval
);
2214 *_retval
= PR_FALSE
;
2217 nsresult rv
= ResolveAndStat();
2218 *_retval
= NS_SUCCEEDED(rv
);
2224 nsLocalFile::IsWritable(PRBool
*aIsWritable
)
2226 // Check we are correctly initialized.
2227 CHECK_mWorkingPath();
2229 //TODO: extend to support NTFS file permissions
2231 // The read-only attribute on a FAT directory only means that it can't
2232 // be deleted. It is still possible to modify the contents of the directory.
2233 nsresult rv
= IsDirectory(aIsWritable
);
2239 // writable if the file doesn't have the readonly attribute
2240 rv
= HasFileAttribute(FILE_ATTRIBUTE_READONLY
, aIsWritable
);
2243 *aIsWritable
= !*aIsWritable
;
2249 nsLocalFile::IsReadable(PRBool
*_retval
)
2251 // Check we are correctly initialized.
2252 CHECK_mWorkingPath();
2254 NS_ENSURE_ARG(_retval
);
2255 *_retval
= PR_FALSE
;
2257 nsresult rv
= ResolveAndStat();
2267 nsLocalFile::IsExecutable(PRBool
*_retval
)
2269 // Check we are correctly initialized.
2270 CHECK_mWorkingPath();
2272 NS_ENSURE_ARG(_retval
);
2273 *_retval
= PR_FALSE
;
2277 // only files can be executables
2279 rv
= IsFile(&isFile
);
2285 //TODO: shouldn't we be checking mFollowSymlinks here?
2287 rv
= IsSymlink(&symLink
);
2297 // kill trailing dots and spaces.
2298 PRInt32 filePathLen
= path
.Length() - 1;
2299 while(filePathLen
> 0 && (path
[filePathLen
] == L
' ' || path
[filePathLen
] == L
'.'))
2301 path
.Truncate(filePathLen
--);
2305 PRInt32 dotIdx
= path
.RFindChar(PRUnichar('.'));
2306 if ( dotIdx
!= kNotFound
) {
2307 // Convert extension to lower case.
2308 PRUnichar
*p
= path
.BeginWriting();
2309 for( p
+= dotIdx
+ 1; *p
; p
++ )
2310 *p
+= (*p
>= L
'A' && *p
<= L
'Z') ? 'a' - 'A' : 0;
2312 // Search for any of the set of executable extensions.
2313 static const char * const executableExts
[] = {
2315 "ade", // access project extension
2317 "app", // executable application
2318 "application", // from bug 348763
2328 "fxp", // FoxPro compiled app
2337 "mad", // Access Module Shortcut
2339 "mag", // Access Diagram Shortcut
2340 "mam", // Access Macro Shortcut
2341 "maq", // Access Query Shortcut
2342 "mar", // Access Report Shortcut
2343 "mas", // Access Stored Procedure
2344 "mat", // Access Table Shortcut
2345 "mau", // Media Attachment Unit
2346 "mav", // Access View Shortcut
2347 "maw", // Access Data Access Page
2348 "mda", // Access Add-in, MDA Access 2 Workgroup
2351 "mdt", // Access Add-in Data
2352 "mdw", // Access Workgroup Information
2353 "mdz", // Access Wizard Template
2355 "msh", // Microsoft Shell
2356 "mshxml", // Microsoft Shell
2360 "ops", // Office Profile Settings
2363 "plg", // Developer Studio Build Log
2364 "prf", // windows system file
2368 "scf", // Windows explorer command
2378 "vsmacros", // Visual Studio .NET Binary-based Macro Project
2386 nsDependentSubstring ext
= Substring(path
, dotIdx
+ 1);
2387 for ( int i
= 0; i
< NS_ARRAY_LENGTH(executableExts
); i
++ ) {
2388 if ( ext
.EqualsASCII(executableExts
[i
])) {
2389 // Found a match. Set result and quit.
2401 nsLocalFile::IsDirectory(PRBool
*_retval
)
2403 NS_ENSURE_ARG(_retval
);
2405 nsresult rv
= ResolveAndStat();
2409 *_retval
= (mFileInfo64
.type
== PR_FILE_DIRECTORY
);
2414 nsLocalFile::IsFile(PRBool
*_retval
)
2416 NS_ENSURE_ARG(_retval
);
2418 nsresult rv
= ResolveAndStat();
2422 *_retval
= (mFileInfo64
.type
== PR_FILE_FILE
);
2427 nsLocalFile::IsHidden(PRBool
*_retval
)
2429 return HasFileAttribute(FILE_ATTRIBUTE_HIDDEN
, _retval
);
2433 nsLocalFile::HasFileAttribute(DWORD fileAttrib
, PRBool
*_retval
)
2435 NS_ENSURE_ARG(_retval
);
2437 nsresult rv
= ResolveAndStat();
2441 // get the file attributes for the correct item depending on following symlinks
2442 const PRUnichar
*filePath
= mFollowSymlinks
?
2443 mResolvedPath
.get() : mWorkingPath
.get();
2444 DWORD word
= ::GetFileAttributesW(filePath
);
2446 *_retval
= ((word
& fileAttrib
) != 0);
2451 nsLocalFile::IsSymlink(PRBool
*_retval
)
2453 // Check we are correctly initialized.
2454 CHECK_mWorkingPath();
2456 NS_ENSURE_ARG(_retval
);
2458 // unless it is a valid shortcut path it's not a symlink
2459 if (!IsShortcutPath(mWorkingPath
))
2461 *_retval
= PR_FALSE
;
2465 // we need to know if this is a file or directory
2466 nsresult rv
= ResolveAndStat();
2470 // it's only a shortcut if it is a file
2471 *_retval
= (mFileInfo64
.type
== PR_FILE_FILE
);
2476 nsLocalFile::IsSpecial(PRBool
*_retval
)
2478 return HasFileAttribute(FILE_ATTRIBUTE_SYSTEM
, _retval
);
2482 nsLocalFile::Equals(nsIFile
*inFile
, PRBool
*_retval
)
2484 NS_ENSURE_ARG(inFile
);
2485 NS_ENSURE_ARG(_retval
);
2489 nsCOMPtr
<nsILocalFileWin
> lf(do_QueryInterface(inFile
));
2491 *_retval
= PR_FALSE
;
2495 nsAutoString inFilePath
;
2496 lf
->GetCanonicalPath(inFilePath
);
2499 *_retval
= _wcsicmp(mShortWorkingPath
.get(), inFilePath
.get()) == 0;
2506 nsLocalFile::Contains(nsIFile
*inFile
, PRBool recur
, PRBool
*_retval
)
2508 // Check we are correctly initialized.
2509 CHECK_mWorkingPath();
2511 *_retval
= PR_FALSE
;
2513 nsAutoString myFilePath
;
2514 if (NS_FAILED(GetTarget(myFilePath
)))
2515 GetPath(myFilePath
);
2517 PRUint32 myFilePathLen
= myFilePath
.Length();
2519 nsAutoString inFilePath
;
2520 if (NS_FAILED(inFile
->GetTarget(inFilePath
)))
2521 inFile
->GetPath(inFilePath
);
2523 // make sure that the |inFile|'s path has a trailing separator.
2524 if (inFilePath
.Length() >= myFilePathLen
&& inFilePath
[myFilePathLen
] == L
'\\')
2526 if (_wcsnicmp(myFilePath
.get(), inFilePath
.get(), myFilePathLen
) == 0)
2538 nsLocalFile::GetTarget(nsAString
&_retval
)
2541 #if STRICT_FAKE_SYMLINKS
2544 nsresult rv
= IsSymlink(&symLink
);
2550 return NS_ERROR_FILE_INVALID_PATH
;
2555 _retval
= mResolvedPath
;
2560 /* attribute PRBool followLinks; */
2562 nsLocalFile::GetFollowLinks(PRBool
*aFollowLinks
)
2564 *aFollowLinks
= mFollowSymlinks
;
2568 nsLocalFile::SetFollowLinks(PRBool aFollowLinks
)
2571 mFollowSymlinks
= aFollowLinks
;
2577 nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator
* *entries
)
2582 if (mWorkingPath
.EqualsLiteral("\\\\.")) {
2583 nsDriveEnumerator
*drives
= new nsDriveEnumerator
;
2585 return NS_ERROR_OUT_OF_MEMORY
;
2587 rv
= drives
->Init();
2588 if (NS_FAILED(rv
)) {
2597 rv
= IsDirectory(&isDir
);
2601 return NS_ERROR_FILE_NOT_DIRECTORY
;
2603 nsDirEnumerator
* dirEnum
= new nsDirEnumerator();
2604 if (dirEnum
== nsnull
)
2605 return NS_ERROR_OUT_OF_MEMORY
;
2607 rv
= dirEnum
->Init(this);
2610 NS_RELEASE(dirEnum
);
2619 nsLocalFile::GetPersistentDescriptor(nsACString
&aPersistentDescriptor
)
2621 CopyUTF16toUTF8(mWorkingPath
, aPersistentDescriptor
);
2626 nsLocalFile::SetPersistentDescriptor(const nsACString
&aPersistentDescriptor
)
2628 if (IsUTF8(aPersistentDescriptor
))
2629 return InitWithPath(NS_ConvertUTF8toUTF16(aPersistentDescriptor
));
2631 return InitWithNativePath(aPersistentDescriptor
);
2635 nsLocalFile::Reveal()
2637 // make sure mResolvedPath is set
2638 nsresult rv
= ResolveAndStat();
2639 if (NS_FAILED(rv
) && rv
!= NS_ERROR_FILE_NOT_FOUND
)
2642 // use the full path to explorer for security
2643 nsCOMPtr
<nsILocalFile
> winDir
;
2644 rv
= GetSpecialSystemDirectory(Win_WindowsDirectory
, getter_AddRefs(winDir
));
2645 NS_ENSURE_SUCCESS(rv
, rv
);
2646 nsAutoString explorerPath
;
2647 rv
= winDir
->GetPath(explorerPath
);
2648 NS_ENSURE_SUCCESS(rv
, rv
);
2649 explorerPath
.Append(L
"\\explorer.exe");
2651 // Always open a new window for files because Win2K doesn't appear to select
2652 // the file if a window showing that folder was already open. If the resolved
2653 // path is a directory then instead of opening the parent and selecting it,
2654 // we open the directory itself.
2655 nsAutoString explorerParams
;
2656 if (mFileInfo64
.type
!= PR_FILE_DIRECTORY
) // valid because we ResolveAndStat above
2657 explorerParams
.Append(L
"/n,/select,");
2658 explorerParams
.Append(L
'\"');
2659 explorerParams
.Append(mResolvedPath
);
2660 explorerParams
.Append(L
'\"');
2662 if (::ShellExecuteW(NULL
, L
"open", explorerPath
.get(), explorerParams
.get(),
2663 NULL
, SW_SHOWNORMAL
) <= (HINSTANCE
) 32)
2664 return NS_ERROR_FAILURE
;
2671 nsLocalFile::Launch()
2673 const nsString
&path
= mWorkingPath
;
2675 // use the app registry name to launch a shell execute....
2676 LONG r
= (LONG
) ::ShellExecuteW(NULL
, NULL
, path
.get(), NULL
, NULL
,
2679 // if the file has no association, we launch windows' "what do you want to do" dialog
2680 if (r
== SE_ERR_NOASSOC
) {
2681 nsAutoString shellArg
;
2682 shellArg
.Assign(NS_LITERAL_STRING("shell32.dll,OpenAs_RunDLL ") + path
);
2683 r
= (LONG
) ::ShellExecuteW(NULL
, NULL
, L
"RUNDLL32.EXE", shellArg
.get(),
2684 NULL
, SW_SHOWNORMAL
);
2690 return NS_ERROR_OUT_OF_MEMORY
;
2691 case ERROR_FILE_NOT_FOUND
:
2692 return NS_ERROR_FILE_NOT_FOUND
;
2693 case ERROR_PATH_NOT_FOUND
:
2694 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
2695 case ERROR_BAD_FORMAT
:
2696 return NS_ERROR_FILE_CORRUPTED
;
2697 case SE_ERR_ACCESSDENIED
:
2698 return NS_ERROR_FILE_ACCESS_DENIED
;
2699 case SE_ERR_ASSOCINCOMPLETE
:
2700 case SE_ERR_NOASSOC
:
2701 return NS_ERROR_UNEXPECTED
;
2702 case SE_ERR_DDEBUSY
:
2703 case SE_ERR_DDEFAIL
:
2704 case SE_ERR_DDETIMEOUT
:
2705 return NS_ERROR_NOT_AVAILABLE
;
2706 case SE_ERR_DLLNOTFOUND
:
2707 return NS_ERROR_FAILURE
;
2709 return NS_ERROR_FILE_IS_LOCKED
;
2711 return NS_ERROR_FILE_EXECUTION_FAILED
;
2719 NS_NewLocalFile(const nsAString
&path
, PRBool followLinks
, nsILocalFile
* *result
)
2721 nsLocalFile
* file
= new nsLocalFile();
2723 return NS_ERROR_OUT_OF_MEMORY
;
2726 file
->SetFollowLinks(followLinks
);
2728 if (!path
.IsEmpty()) {
2729 nsresult rv
= file
->InitWithPath(path
);
2730 if (NS_FAILED(rv
)) {
2740 //-----------------------------------------------------------------------------
2741 // Native (lossy) interface
2742 //-----------------------------------------------------------------------------
2745 nsLocalFile::InitWithNativePath(const nsACString
&filePath
)
2748 nsresult rv
= NS_CopyNativeToUnicode(filePath
, tmp
);
2749 if (NS_SUCCEEDED(rv
))
2750 return InitWithPath(tmp
);
2756 nsLocalFile::AppendNative(const nsACString
&node
)
2759 nsresult rv
= NS_CopyNativeToUnicode(node
, tmp
);
2760 if (NS_SUCCEEDED(rv
))
2767 nsLocalFile::AppendRelativeNativePath(const nsACString
&node
)
2770 nsresult rv
= NS_CopyNativeToUnicode(node
, tmp
);
2771 if (NS_SUCCEEDED(rv
))
2772 return AppendRelativePath(tmp
);
2778 nsLocalFile::GetNativeLeafName(nsACString
&aLeafName
)
2780 //NS_WARNING("This API is lossy. Use GetLeafName !");
2782 nsresult rv
= GetLeafName(tmp
);
2783 if (NS_SUCCEEDED(rv
))
2784 rv
= NS_CopyUnicodeToNative(tmp
, aLeafName
);
2790 nsLocalFile::SetNativeLeafName(const nsACString
&aLeafName
)
2793 nsresult rv
= NS_CopyNativeToUnicode(aLeafName
, tmp
);
2794 if (NS_SUCCEEDED(rv
))
2795 return SetLeafName(tmp
);
2802 nsLocalFile::GetNativePath(nsACString
&_retval
)
2804 //NS_WARNING("This API is lossy. Use GetPath !");
2806 nsresult rv
= GetPath(tmp
);
2807 if (NS_SUCCEEDED(rv
))
2808 rv
= NS_CopyUnicodeToNative(tmp
, _retval
);
2815 nsLocalFile::GetNativeCanonicalPath(nsACString
&aResult
)
2817 NS_WARNING("This method is lossy. Use GetCanoincailPath !");
2819 NS_CopyUnicodeToNative(mShortWorkingPath
, aResult
);
2825 nsLocalFile::CopyToNative(nsIFile
*newParentDir
, const nsACString
&newName
)
2827 // Check we are correctly initialized.
2828 CHECK_mWorkingPath();
2830 if (newName
.IsEmpty())
2831 return CopyTo(newParentDir
, EmptyString());
2834 nsresult rv
= NS_CopyNativeToUnicode(newName
, tmp
);
2835 if (NS_SUCCEEDED(rv
))
2836 return CopyTo(newParentDir
, tmp
);
2842 nsLocalFile::CopyToFollowingLinksNative(nsIFile
*newParentDir
, const nsACString
&newName
)
2844 if (newName
.IsEmpty())
2845 return CopyToFollowingLinks(newParentDir
, EmptyString());
2848 nsresult rv
= NS_CopyNativeToUnicode(newName
, tmp
);
2849 if (NS_SUCCEEDED(rv
))
2850 return CopyToFollowingLinks(newParentDir
, tmp
);
2856 nsLocalFile::MoveToNative(nsIFile
*newParentDir
, const nsACString
&newName
)
2858 // Check we are correctly initialized.
2859 CHECK_mWorkingPath();
2861 if (newName
.IsEmpty())
2862 return MoveTo(newParentDir
, EmptyString());
2865 nsresult rv
= NS_CopyNativeToUnicode(newName
, tmp
);
2866 if (NS_SUCCEEDED(rv
))
2867 return MoveTo(newParentDir
, tmp
);
2873 nsLocalFile::GetNativeTarget(nsACString
&_retval
)
2875 // Check we are correctly initialized.
2876 CHECK_mWorkingPath();
2878 NS_WARNING("This API is lossy. Use GetTarget !");
2880 nsresult rv
= GetTarget(tmp
);
2881 if (NS_SUCCEEDED(rv
))
2882 rv
= NS_CopyUnicodeToNative(tmp
, _retval
);
2888 NS_NewNativeLocalFile(const nsACString
&path
, PRBool followLinks
, nsILocalFile
* *result
)
2891 nsresult rv
= NS_CopyNativeToUnicode(path
, buf
);
2892 if (NS_FAILED(rv
)) {
2896 return NS_NewLocalFile(buf
, followLinks
, result
);
2900 nsLocalFile::EnsureShortPath()
2902 if (!mShortWorkingPath
.IsEmpty())
2905 mShortWorkingPath
.Assign(mWorkingPath
);
2907 WCHAR thisshort
[MAX_PATH
];
2908 DWORD thisr
= ::GetShortPathNameW(mWorkingPath
.get(), thisshort
,
2910 // If an error occured (thisr == 0) thisshort is uninitialized memory!
2911 if (thisr
!= 0 && thisr
< sizeof(thisshort
))
2912 mShortWorkingPath
.Assign(thisshort
);
2914 mShortWorkingPath
.Assign(mWorkingPath
);
2921 nsLocalFile::Equals(nsIHashable
* aOther
, PRBool
*aResult
)
2923 nsCOMPtr
<nsIFile
> otherfile(do_QueryInterface(aOther
));
2925 *aResult
= PR_FALSE
;
2929 return Equals(otherfile
, aResult
);
2933 nsLocalFile::GetHashCode(PRUint32
*aResult
)
2935 // In order for short and long path names to hash to the same value we
2936 // always hash on the short pathname.
2939 *aResult
= HashString(mShortWorkingPath
);
2943 //-----------------------------------------------------------------------------
2944 // nsLocalFile <static members>
2945 //-----------------------------------------------------------------------------
2948 nsLocalFile::GlobalInit()
2951 nsresult rv
= NS_CreateShortcutResolver();
2952 NS_ASSERTION(NS_SUCCEEDED(rv
), "Shortcut resolver could not be created");
2957 nsLocalFile::GlobalShutdown()
2960 NS_DestroyShortcutResolver();
2964 NS_IMPL_ISUPPORTS1(nsDriveEnumerator
, nsISimpleEnumerator
)
2966 nsDriveEnumerator::nsDriveEnumerator()
2971 nsDriveEnumerator::~nsDriveEnumerator()
2975 nsresult
nsDriveEnumerator::Init()
2980 /* If the length passed to GetLogicalDriveStrings is smaller
2981 * than the length of the string it would return, it returns
2982 * the length required for the string. */
2983 DWORD length
= GetLogicalDriveStrings(0, 0);
2984 /* The string is null terminated */
2985 if (!EnsureStringLength(mDrives
, length
+1))
2986 return NS_ERROR_OUT_OF_MEMORY
;
2987 if (!GetLogicalDriveStrings(length
, mDrives
.BeginWriting()))
2988 return NS_ERROR_FAILURE
;
2989 mLetter
= mDrives
.get();
2994 NS_IMETHODIMP
nsDriveEnumerator::HasMoreElements(PRBool
*aHasMore
)
2999 *aHasMore
= *mLetter
!= '\0';
3004 NS_IMETHODIMP
nsDriveEnumerator::GetNext(nsISupports
**aNext
)
3008 nsresult rv
= NS_NewLocalFile(NS_LITERAL_STRING("\\"), PR_FALSE
, &file
);
3011 /* GetLogicalDrives stored in mLetter is a concatenation
3012 * of null terminated strings, followed by a null terminator. */
3017 NS_ConvertASCIItoUTF16
drive(mLetter
);
3018 mLetter
+= drive
.Length() + 1;
3021 NS_NewLocalFile(drive
, PR_FALSE
, &file
);