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-2000
21 * the Initial Developer. All Rights Reserved.
24 * Henry Sobotka <sobotka@axess.com>
26 * Rich Walsh <dragtext@e-vertise.com>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either of the GNU General Public License Version 2 or later (the "GPL"),
30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
42 // N.B. the ns* & pr* headers below will #include all
43 // of the standard library headers this file requires
48 #include "nsLocalFile.h"
49 #include "nsNativeCharsetUtils.h"
51 #include "nsISimpleEnumerator.h"
52 #include "nsIDirectoryEnumerator.h"
53 #include "nsIComponentManager.h"
57 #include "nsReadableUtils.h"
58 #include "nsISupportsPrimitives.h"
59 #include "nsIMutableArray.h"
60 #include "nsTraceRefcntImpl.h"
62 #define CHECK_mWorkingPath() \
64 if (mWorkingPath.IsEmpty()) \
65 return NS_ERROR_NOT_INITIALIZED; \
68 //-----------------------------------------------------------------------------
69 // static helper functions
70 //-----------------------------------------------------------------------------
72 static nsresult
ConvertOS2Error(int err
)
78 case ERROR_FILE_NOT_FOUND
:
79 case ERROR_PATH_NOT_FOUND
:
80 case ERROR_INVALID_DRIVE
:
81 rv
= NS_ERROR_FILE_NOT_FOUND
;
83 case ERROR_ACCESS_DENIED
:
84 case ERROR_NOT_SAME_DEVICE
:
85 rv
= NS_ERROR_FILE_ACCESS_DENIED
;
87 case ERROR_NOT_ENOUGH_MEMORY
:
88 case ERROR_INVALID_BLOCK
:
89 case ERROR_INVALID_HANDLE
:
90 case ERROR_ARENA_TRASHED
:
91 rv
= NS_ERROR_OUT_OF_MEMORY
;
93 case ERROR_CURRENT_DIRECTORY
:
94 rv
= NS_ERROR_FILE_DIR_NOT_EMPTY
;
96 case ERROR_WRITE_PROTECT
:
97 rv
= NS_ERROR_FILE_READ_ONLY
;
99 case ERROR_HANDLE_DISK_FULL
:
100 rv
= NS_ERROR_FILE_TOO_BIG
;
102 case ERROR_FILE_EXISTS
:
103 case ERROR_ALREADY_EXISTS
:
104 case ERROR_CANNOT_MAKE
:
105 rv
= NS_ERROR_FILE_ALREADY_EXISTS
;
107 case ERROR_FILENAME_EXCED_RANGE
:
108 rv
= NS_ERROR_FILE_NAME_TOO_LONG
;
113 rv
= NS_ERROR_FAILURE
;
119 myLL_L2II(PRInt64 result
, PRInt32
*hi
, PRInt32
*lo
)
121 PRInt64 a64
, b64
; // probably could have been done with
122 // only one PRInt64, but these are macros,
125 // shift the hi word to the low word, then push it into a long.
126 LL_SHR(a64
, result
, 32);
129 // shift the low word to the hi word first, then shift it back.
130 LL_SHL(b64
, result
, 32);
131 LL_SHR(a64
, b64
, 32);
135 // Locates the first occurrence of charToSearchFor in the stringToSearch
136 static unsigned char* PR_CALLBACK
137 _mbschr(const unsigned char* stringToSearch
, int charToSearchFor
)
139 const unsigned char* p
= stringToSearch
;
142 if (*p
== charToSearchFor
)
144 p
= (const unsigned char*)WinNextChar(0,0,0,(char*)p
);
147 // Result is p or NULL
148 return *p
? (unsigned char*)p
: NULL
;
151 // Locates the first occurrence of subString in the stringToSearch
152 static unsigned char* PR_CALLBACK
153 _mbsstr(const unsigned char* stringToSearch
, const unsigned char* subString
)
155 const unsigned char* pStr
= stringToSearch
;
156 const unsigned char* pSub
= subString
;
159 while (*pStr
&& *pStr
!= *pSub
)
160 pStr
= (const unsigned char*)WinNextChar(0,0,0,(char*)pStr
);
165 const unsigned char* pNxt
= pStr
;
167 pSub
= (const unsigned char*)WinNextChar(0,0,0,(char*)pSub
);
168 pNxt
= (const unsigned char*)WinNextChar(0,0,0,(char*)pNxt
);
169 } while (*pSub
&& *pSub
== *pNxt
);
175 pStr
= (const unsigned char*)WinNextChar(0,0,0,(char*)pStr
);
179 // if we got to the end of pSub, we've found it
180 return *pSub
? NULL
: (unsigned char*)pStr
;
183 // Locates last occurence of charToSearchFor in the stringToSearch
184 NS_EXPORT
unsigned char*
185 _mbsrchr(const unsigned char* stringToSearch
, int charToSearchFor
)
187 int length
= strlen((const char*)stringToSearch
);
188 const unsigned char* p
= stringToSearch
+length
;
191 if (*p
== charToSearchFor
)
193 p
= (const unsigned char*)WinPrevChar(0,0,0,(char*)stringToSearch
,(char*)p
);
194 } while (p
> stringToSearch
);
196 // Result is p or NULL
197 return (*p
== charToSearchFor
) ? (unsigned char*)p
: NULL
;
200 // Implement equivalent of Win32 CreateDirectoryA
201 static nsresult PR_CALLBACK
202 CreateDirectoryA(PSZ path
, PEAOP2 ppEABuf
)
206 FILESTATUS3 pathInfo
;
208 rc
= DosCreateDir(path
, ppEABuf
);
211 rv
= ConvertOS2Error(rc
);
213 // Check if directory already exists and if so,
214 // reflect that in the return value
215 rc
= DosQueryPathInfo(path
, FIL_STANDARD
,
216 &pathInfo
, sizeof(pathInfo
));
218 rv
= ERROR_FILE_EXISTS
;
226 static int isleadbyte(int c
)
228 static BOOL bDBCSFilled
= FALSE
;
229 // According to the Control Program Guide&Ref, 12 bytes is sufficient
230 static BYTE DBCSInfo
[12] = { 0 };
236 COUNTRYCODE ctrycodeInfo
= { 0 };
237 APIRET rc
= NO_ERROR
;
238 ctrycodeInfo
.country
= 0; // Current Country
239 ctrycodeInfo
.codepage
= 0; // Current Codepage
241 rc
= DosQueryDBCSEnv(sizeof(DBCSInfo
), &ctrycodeInfo
, DBCSInfo
);
242 // we had an error, do something?
249 // DBCSInfo returned by DosQueryDBCSEnv is terminated
250 // with two '0' bytes in a row
252 while(*curr
!= 0 && *(curr
+1) != 0)
254 if(c
>= *curr
&& c
<= *(curr
+1))
265 //-----------------------------------------------------------------------------
267 //-----------------------------------------------------------------------------
269 class nsDirEnumerator
: public nsISimpleEnumerator
,
270 public nsIDirectoryEnumerator
276 nsDirEnumerator() : mDir(nsnull
)
280 nsresult
Init(nsILocalFile
* parent
)
282 nsCAutoString filepath
;
283 parent
->GetNativeTarget(filepath
);
285 if (filepath
.IsEmpty())
287 parent
->GetNativePath(filepath
);
290 if (filepath
.IsEmpty())
292 return NS_ERROR_UNEXPECTED
;
295 mDir
= PR_OpenDir(filepath
.get());
296 if (mDir
== nsnull
) // not a directory?
297 return NS_ERROR_FAILURE
;
303 NS_IMETHOD
HasMoreElements(PRBool
*result
)
306 if (mNext
== nsnull
&& mDir
)
308 PRDirEntry
* entry
= PR_ReadDir(mDir
, PR_SKIP_BOTH
);
311 // end of dir entries
313 PRStatus status
= PR_CloseDir(mDir
);
314 if (status
!= PR_SUCCESS
)
315 return NS_ERROR_FAILURE
;
322 nsCOMPtr
<nsIFile
> file
;
323 rv
= mParent
->Clone(getter_AddRefs(file
));
327 rv
= file
->AppendNative(nsDependentCString(entry
->name
));
331 // make sure the thing exists. If it does, try the next one.
333 rv
= file
->Exists(&exists
);
334 if (NS_FAILED(rv
) || !exists
)
336 return HasMoreElements(result
);
339 mNext
= do_QueryInterface(file
);
341 *result
= mNext
!= nsnull
;
347 NS_IMETHOD
GetNext(nsISupports
**result
)
351 rv
= HasMoreElements(&hasMore
);
352 if (NS_FAILED(rv
)) return rv
;
354 *result
= mNext
; // might return nsnull
355 NS_IF_ADDREF(*result
);
361 NS_IMETHOD
GetNextFile(nsIFile
**result
)
364 PRBool hasMore
= PR_FALSE
;
365 nsresult rv
= HasMoreElements(&hasMore
);
366 if (NS_FAILED(rv
) || !hasMore
)
369 NS_IF_ADDREF(*result
);
378 PRStatus status
= PR_CloseDir(mDir
);
379 NS_ASSERTION(status
== PR_SUCCESS
, "close failed");
380 if (status
!= PR_SUCCESS
)
381 return NS_ERROR_FAILURE
;
387 // dtor can be non-virtual since there are no subclasses, but must be
388 // public to use the class on the stack.
396 nsCOMPtr
<nsILocalFile
> mParent
;
397 nsCOMPtr
<nsILocalFile
> mNext
;
400 NS_IMPL_ISUPPORTS2(nsDirEnumerator
, nsISimpleEnumerator
, nsIDirectoryEnumerator
)
402 //-----------------------------------------------------------------------------
404 //-----------------------------------------------------------------------------
406 class nsDriveEnumerator
: public nsISimpleEnumerator
410 virtual ~nsDriveEnumerator();
412 NS_DECL_NSISIMPLEENUMERATOR
416 // mDrives is a bitmap representing the available drives
417 // mLetter is incremented each time mDrives is shifted rightward
422 NS_IMPL_ISUPPORTS1(nsDriveEnumerator
, nsISimpleEnumerator
)
424 nsDriveEnumerator::nsDriveEnumerator()
425 : mDrives(0), mLetter(0)
429 nsDriveEnumerator::~nsDriveEnumerator()
433 nsresult
nsDriveEnumerator::Init()
437 // bits 0-25 in mDrives represent each possible drive, A-Z
438 DosError(FERR_DISABLEHARDERR
);
439 APIRET rc
= DosQueryCurrentDisk(&ulCurrent
, (PULONG
)&mDrives
);
440 DosError(FERR_ENABLEHARDERR
);
442 return NS_ERROR_FAILURE
;
448 NS_IMETHODIMP
nsDriveEnumerator::HasMoreElements(PRBool
*aHasMore
)
450 // no more bits means no more drives
451 *aHasMore
= (mDrives
!= 0);
455 NS_IMETHODIMP
nsDriveEnumerator::GetNext(nsISupports
**aNext
)
463 // if bit 0 is off, advance to the next bit that's on
464 while ((mDrives
& 1) == 0)
470 // format a drive string, then advance to the next possible drive
471 char drive
[4] = "x:\\";
477 nsresult rv
= NS_NewNativeLocalFile(nsDependentCString(drive
),
484 //-----------------------------------------------------------------------------
485 // class TypeEaEnumerator - a convenience for accessing
486 // a file's .TYPE extended attribute
487 //-----------------------------------------------------------------------------
489 // this struct describes the first entry for an MVMT or MVST EA;
490 // .TYPE is supposed to be MVMT but is sometimes malformed as MVST
492 typedef struct _MVHDR
{
501 typedef MVHDR
*PMVHDR
;
504 class TypeEaEnumerator
507 TypeEaEnumerator() : mEaBuf(nsnull
) { }
508 ~TypeEaEnumerator() { if (mEaBuf
) NS_Free(mEaBuf
); }
510 nsresult
Init(nsLocalFile
* aFile
);
511 char * GetNext(PRUint32
*lth
);
522 nsresult
TypeEaEnumerator::Init(nsLocalFile
* aFile
)
524 #define EABUFSIZE 512
526 // provide a buffer for the results
527 mEaBuf
= (char*)NS_Alloc(EABUFSIZE
);
529 return NS_ERROR_OUT_OF_MEMORY
;
531 PFEA2LIST pfea2list
= (PFEA2LIST
)mEaBuf
;
532 pfea2list
->cbList
= EABUFSIZE
;
534 // ask for the .TYPE extended attribute
535 nsresult rv
= aFile
->GetEA(".TYPE", pfea2list
);
539 // point at the data - it starts immediately after the EA's name;
540 // then confirm the EA is MVMT (correct) or MVST (acceptable)
541 mpMvh
= (PMVHDR
)&(pfea2list
->list
[0].szName
[pfea2list
->list
[0].cbName
+1]);
542 if (mpMvh
->usEAType
!= EAT_MVMT
)
543 if (mpMvh
->usEAType
!= EAT_MVST
|| mpMvh
->usDataType
!= EAT_ASCII
)
544 return NS_ERROR_FAILURE
;
546 // init the variables that tell us where we are in the lsit
549 mpCur
= (char*)(mpMvh
->usEAType
== EAT_MVMT
?
550 &mpMvh
->usDataType
: &mpMvh
->usDataLth
);
556 char * TypeEaEnumerator::GetNext(PRUint32
*lth
)
558 char * result
= nsnull
;
560 // this is a loop so we can skip invalid entries if needed;
561 // normally, it will break out on the first iteration
562 while (mCtr
++ < mpMvh
->usNumEntries
) {
564 // advance to the next entry
567 // if MVMT, ensure the datatype is OK, then advance
568 // to the length field present in both formats
569 if (mpMvh
->usEAType
== EAT_MVMT
) {
570 if (*((PUSHORT
)mpCur
) != EAT_ASCII
)
572 mpCur
+= sizeof(USHORT
);
575 // get the data's length, point at the data itself, then exit
576 mLth
= *lth
= *((PUSHORT
)mpCur
);
577 mpCur
+= sizeof(USHORT
);
585 //-----------------------------------------------------------------------------
586 // nsLocalFile <public>
587 //-----------------------------------------------------------------------------
589 nsLocalFile::nsLocalFile()
595 nsLocalFile::nsLocalFileConstructor(nsISupports
* outer
, const nsIID
& aIID
, void* *aInstancePtr
)
597 NS_ENSURE_ARG_POINTER(aInstancePtr
);
598 NS_ENSURE_NO_AGGREGATION(outer
);
600 nsLocalFile
* inst
= new nsLocalFile();
602 return NS_ERROR_OUT_OF_MEMORY
;
604 nsresult rv
= inst
->QueryInterface(aIID
, aInstancePtr
);
614 //-----------------------------------------------------------------------------
615 // nsLocalFile::nsISupports
616 //-----------------------------------------------------------------------------
618 NS_IMPL_THREADSAFE_ISUPPORTS4(nsLocalFile
,
625 //-----------------------------------------------------------------------------
626 // nsLocalFile <private>
627 //-----------------------------------------------------------------------------
629 nsLocalFile::nsLocalFile(const nsLocalFile
& other
)
631 , mWorkingPath(other
.mWorkingPath
)
636 // Stat the path. After a successful return the path is
637 // guaranteed valid and the members of mFileInfo64 can be used.
641 // if we aren't dirty then we are already done
645 // we can't stat anything that isn't a valid NSPR addressable path
646 if (mWorkingPath
.IsEmpty())
647 return NS_ERROR_FILE_INVALID_PATH
;
649 // hack designed to work around bug 134796 until it is fixed
651 const char *nsprPath
= mWorkingPath
.get();
652 if (mWorkingPath
.Length() == 2 && mWorkingPath
.CharAt(1) == ':')
654 temp
[0] = mWorkingPath
[0];
655 temp
[1] = mWorkingPath
[1];
661 // see if the working path exists
662 DosError(FERR_DISABLEHARDERR
);
663 PRStatus status
= PR_GetFileInfo64(nsprPath
, &mFileInfo64
);
664 DosError(FERR_ENABLEHARDERR
);
665 if (status
!= PR_SUCCESS
)
666 return NS_ERROR_FILE_NOT_FOUND
;
673 //-----------------------------------------------------------------------------
674 // nsLocalFile::nsIFile,nsILocalFile
675 //-----------------------------------------------------------------------------
678 nsLocalFile::Clone(nsIFile
**file
)
680 // Just copy-construct ourselves
681 *file
= new nsLocalFile(*this);
683 return NS_ERROR_OUT_OF_MEMORY
;
691 nsLocalFile::InitWithNativePath(const nsACString
&filePath
)
695 nsACString::const_iterator begin
, end
;
696 filePath
.BeginReading(begin
);
697 filePath
.EndReading(end
);
699 // input string must not be empty
701 return NS_ERROR_FAILURE
;
703 char firstChar
= *begin
;
704 char secondChar
= *(++begin
);
706 // just do a sanity check. if it has any forward slashes, it is not
707 // a Native path. Also, it must have a colon at after the first char.
712 if ( ( (secondChar
== ':') && !FindCharInReadable('/', begin
, end
) ) || // normal path
713 ( (firstChar
== '\\') && (secondChar
== '\\') ) ) // network path
715 // This is a native path
716 path
= ToNewCString(filePath
);
717 pathLen
= filePath
.Length();
721 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
723 // kill any trailing '\' provided it isn't the second char of DBCS
724 PRInt32 len
= pathLen
- 1;
725 if (path
[len
] == '\\' && !::isleadbyte(path
[len
-1]))
731 mWorkingPath
.Adopt(path
, pathLen
);
736 nsLocalFile::OpenNSPRFileDesc(PRInt32 flags
, PRInt32 mode
, PRFileDesc
**_retval
)
738 nsresult rv
= Stat();
739 if (NS_FAILED(rv
) && rv
!= NS_ERROR_FILE_NOT_FOUND
)
742 *_retval
= PR_Open(mWorkingPath
.get(), flags
, mode
);
746 return NS_ErrorAccordingToNSPR();
751 nsLocalFile::OpenANSIFileDesc(const char *mode
, FILE * *_retval
)
753 nsresult rv
= Stat();
754 if (NS_FAILED(rv
) && rv
!= NS_ERROR_FILE_NOT_FOUND
)
757 *_retval
= fopen(mWorkingPath
.get(), mode
);
761 return NS_ERROR_FAILURE
;
767 nsLocalFile::Create(PRUint32 type
, PRUint32 attributes
)
769 if (type
!= NORMAL_FILE_TYPE
&& type
!= DIRECTORY_TYPE
)
770 return NS_ERROR_FILE_UNKNOWN_TYPE
;
772 nsresult rv
= Stat();
773 if (NS_FAILED(rv
) && rv
!= NS_ERROR_FILE_NOT_FOUND
)
776 // create directories to target
778 // A given local file can be either one of these forms:
780 // - normal: X:\some\path\on\this\drive
783 // - UNC path: \\machine\volume\some\path\on\this\drive
786 // Skip the first 'X:\' for the first form, and skip the first full
787 // '\\machine\volume\' segment for the second form.
789 unsigned char* path
= (unsigned char*) mWorkingPath
.BeginWriting();
791 if (path
[0] == '\\' && path
[1] == '\\')
793 // dealing with a UNC path here; skip past '\\machine\'
794 path
= _mbschr(path
+ 2, '\\');
796 return NS_ERROR_FILE_INVALID_PATH
;
800 // search for first slash after the drive (or volume) name
801 unsigned char* slash
= _mbschr(path
, '\\');
805 // skip the first '\\'
807 slash
= _mbschr(slash
, '\\');
813 rv
= CreateDirectoryA(const_cast<char*>(mWorkingPath
.get()), NULL
);
815 rv
= ConvertOS2Error(rv
);
816 if (rv
!= NS_ERROR_FILE_ALREADY_EXISTS
)
821 slash
= _mbschr(slash
, '\\');
825 if (type
== NORMAL_FILE_TYPE
)
827 PRFileDesc
* file
= PR_Open(mWorkingPath
.get(), PR_RDONLY
| PR_CREATE_FILE
| PR_APPEND
| PR_EXCL
, attributes
);
829 return NS_ERROR_FILE_ALREADY_EXISTS
;
835 if (type
== DIRECTORY_TYPE
)
837 rv
= CreateDirectoryA(const_cast<char*>(mWorkingPath
.get()), NULL
);
839 return ConvertOS2Error(rv
);
844 return NS_ERROR_FILE_UNKNOWN_TYPE
;
849 nsLocalFile::AppendNative(const nsACString
&node
)
851 // append this path, multiple components are not permitted
852 return AppendNativeInternal(PromiseFlatCString(node
), PR_FALSE
);
856 nsLocalFile::AppendRelativeNativePath(const nsACString
&node
)
858 // append this path, multiple components are permitted
859 return AppendNativeInternal(PromiseFlatCString(node
), PR_TRUE
);
863 nsLocalFile::AppendNativeInternal(const nsAFlatCString
&node
, PRBool multipleComponents
)
868 // check the relative path for validity
869 const unsigned char * nodePath
= (const unsigned char *) node
.get();
870 if (*nodePath
== '\\' // can't start with an '\'
871 || _mbschr(nodePath
, '/') // can't contain /
872 || node
.Equals("..")) // can't be ..
873 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
875 if (multipleComponents
)
877 // can't contain .. as a path component. Ensure that the valid components
878 // "foo..foo", "..foo", and "foo.." are not falsely detected, but the invalid
879 // paths "..\", "foo\..", "foo\..\foo", "..\foo", etc are.
880 const unsigned char * doubleDot
= _mbsstr(nodePath
, (const unsigned char *)"\\..");
884 if (*doubleDot
== '\0' || *doubleDot
== '\\')
885 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
886 doubleDot
= _mbsstr(doubleDot
, (unsigned char *)"\\..");
888 // catches the remaining cases of prefixes (i.e. '..\')
889 // note: this is a substitute for Win32's _mbsncmp(nodePath, "..\\", 3)
890 if (*nodePath
== '.') {
891 nodePath
= (const unsigned char*)WinNextChar(0,0,0,(char*)nodePath
);
892 if (*nodePath
== '.' &&
893 *WinNextChar(0,0,0,(char*)nodePath
) == '\\')
894 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
897 else if (_mbschr(nodePath
, '\\')) // single components can't contain '\'
898 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
902 mWorkingPath
.Append(NS_LITERAL_CSTRING("\\") + node
);
908 nsLocalFile::Normalize()
910 // XXX See bug 187957 comment 18 for possible problems with this implementation.
912 if (mWorkingPath
.IsEmpty())
915 // work in unicode for ease
917 NS_CopyNativeToUnicode(mWorkingPath
, path
);
919 // find the index of the root backslash for the path. Everything before
920 // this is considered fully normalized and cannot be ascended beyond
921 // using ".." For a local drive this is the first slash (e.g. "c:\").
922 // For a UNC path it is the slash following the share name
923 // (e.g. "\\server\share\").
924 PRInt32 rootIdx
= 2; // default to local drive
925 if (path
.First() == '\\') // if a share then calculate the rootIdx
927 rootIdx
= path
.FindChar('\\', 2); // skip \\ in front of the server
928 if (rootIdx
== kNotFound
)
929 return NS_OK
; // already normalized
930 rootIdx
= path
.FindChar('\\', rootIdx
+1);
931 if (rootIdx
== kNotFound
)
932 return NS_OK
; // already normalized
934 else if (path
.CharAt(rootIdx
) != '\\')
936 // The path has been specified relative to the current working directory
937 // for that drive. To normalize it, the current working directory for
938 // that drive needs to be inserted before the supplied relative path
939 // which will provide an absolute path (and the rootIdx will still be 2).
941 char cwd
[CCHMAXPATH
];
943 drv
[0] = mWorkingPath
.First();
944 if (DosQueryPathInfo(drv
, FIL_QUERYFULLNAME
, cwd
, sizeof(cwd
)))
945 return NS_ERROR_FILE_NOT_FOUND
;
947 nsAutoString currentDir
;
948 NS_CopyNativeToUnicode(nsDependentCString(cwd
), currentDir
);
950 if (currentDir
.Last() == '\\')
951 path
.Replace(0, 2, currentDir
);
953 path
.Replace(0, 2, currentDir
+ NS_LITERAL_STRING("\\"));
955 NS_POSTCONDITION(0 < rootIdx
&& rootIdx
< (PRInt32
)path
.Length(), "rootIdx is invalid");
956 NS_POSTCONDITION(path
.CharAt(rootIdx
) == '\\', "rootIdx is invalid");
958 // if there is nothing following the root path then it is already normalized
959 if (rootIdx
+ 1 == (PRInt32
)path
.Length())
964 const PRUnichar
* pathBuffer
= path
.get(); // simplify access to the buffer
965 normal
.SetCapacity(path
.Length()); // it won't ever grow longer
966 normal
.Assign(pathBuffer
, rootIdx
);
968 // Normalize the path components. The actions taken are:
970 // "\\" condense to single backslash
971 // "." remove from path
972 // ".." up a directory
973 // "..." remove from path (any number of dots > 2)
975 // The last form is something that Windows 95 and 98 supported and
976 // is a shortcut for changing up multiple directories. Windows XP
977 // and ilk ignore it in a path, as is done here.
978 PRInt32 len
, begin
, end
= rootIdx
;
979 while (end
< (PRInt32
)path
.Length())
981 // find the current segment (text between the backslashes) to
982 // be examined, this will set the following variables:
983 // begin == index of first char in segment
984 // end == index 1 char after last char in segment
985 // len == length of segment
987 end
= path
.FindChar('\\', begin
);
988 if (end
== kNotFound
)
992 // ignore double backslashes
996 // len != 0, and interesting paths always begin with a dot
997 if (pathBuffer
[begin
] == '.')
999 // ignore single dots
1003 // handle multiple dots
1004 if (len
>= 2 && pathBuffer
[begin
+1] == '.')
1006 // back up a path component on double dot
1009 PRInt32 prev
= normal
.RFindChar('\\');
1010 if (prev
>= rootIdx
)
1011 normal
.Truncate(prev
);
1015 // length is > 2 and the first two characters are dots.
1016 // if the rest of the string is dots, then ignore it.
1018 for (; idx
>= 2; --idx
)
1020 if (pathBuffer
[begin
+idx
] != '.')
1024 // this is true if the loop above didn't break
1025 // and all characters in this segment are dots.
1031 // add the current component to the path, including the preceding backslash
1032 normal
.Append(pathBuffer
+ begin
- 1, len
+ 1);
1035 // kill trailing dots and spaces.
1036 PRInt32 filePathLen
= normal
.Length() - 1;
1037 while(filePathLen
> 0 && (normal
[filePathLen
] == ' ' || normal
[filePathLen
] == '.'))
1039 normal
.Truncate(filePathLen
--);
1042 NS_CopyUnicodeToNative(normal
, mWorkingPath
);
1049 nsLocalFile::GetNativeLeafName(nsACString
&aLeafName
)
1051 aLeafName
.Truncate();
1053 const char* temp
= mWorkingPath
.get();
1055 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
1057 const char* leaf
= (const char*) _mbsrchr((const unsigned char*) temp
, '\\');
1059 // if the working path is just a node without any lashes.
1065 aLeafName
.Assign(leaf
);
1070 nsLocalFile::SetNativeLeafName(const nsACString
&aLeafName
)
1074 const unsigned char* temp
= (const unsigned char*) mWorkingPath
.get();
1076 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
1078 // cannot use nsCString::RFindChar() due to 0x5c problem
1079 PRInt32 offset
= (PRInt32
) (_mbsrchr(temp
, '\\') - temp
);
1082 mWorkingPath
.Truncate(offset
+1);
1084 mWorkingPath
.Append(aLeafName
);
1091 nsLocalFile::GetNativePath(nsACString
&_retval
)
1093 _retval
= mWorkingPath
;
1097 //-----------------------------------------------------------------------------
1099 // get any single extended attribute for the current file or directory
1102 nsLocalFile::GetEA(const char *eaName
, PFEA2LIST pfea2list
)
1104 // ensure we have an out-buffer whose length is specified
1105 if (!pfea2list
|| !pfea2list
->cbList
)
1106 return NS_ERROR_FAILURE
;
1108 // the gea2list's name field is only 1 byte long;
1109 // this expands its allocation to hold a 33 byte name
1112 char dummy
[sizeof(GEA2LIST
)+32];
1116 eaop2
.fpFEA2List
= pfea2list
;
1117 eaop2
.fpGEA2List
= &gea2list
;
1119 // fill in the request structure
1120 dummy
[sizeof(GEA2LIST
)+31] = 0;
1121 gea2list
.list
[0].oNextEntryOffset
= 0;
1122 strcpy(gea2list
.list
[0].szName
, eaName
);
1123 gea2list
.list
[0].cbName
= strlen(gea2list
.list
[0].szName
);
1124 gea2list
.cbList
= sizeof(GEA2LIST
) + gea2list
.list
[0].cbName
;
1126 // see what we get - this will succeed even if the EA doesn't exist
1127 APIRET rc
= DosQueryPathInfo(mWorkingPath
.get(), FIL_QUERYEASFROMLIST
,
1128 &eaop2
, sizeof(eaop2
));
1130 return ConvertOS2Error(rc
);
1132 // if the data length is zero, requested EA doesn't exist
1133 if (!pfea2list
->list
[0].cbValue
)
1134 return NS_ERROR_FAILURE
;
1140 // return an array of file types or null if there are none
1143 nsLocalFile::GetFileTypes(nsIArray
**_retval
)
1145 NS_ENSURE_ARG(_retval
);
1148 // fetch the .TYPE ea & prepare for enumeration
1149 TypeEaEnumerator typeEnum
;
1150 nsresult rv
= typeEnum
.Init(this);
1154 // create an array that's scriptable
1155 nsCOMPtr
<nsIMutableArray
> mutArray
=
1156 do_CreateInstance(NS_ARRAY_CONTRACTID
);
1157 NS_ENSURE_STATE(mutArray
);
1163 // get each file type, convert to a CString, then add to the array
1164 for (cnt
=0, ptr
=typeEnum
.GetNext(<h
); ptr
; ptr
=typeEnum
.GetNext(<h
)) {
1165 nsCOMPtr
<nsISupportsCString
> typeString(
1166 do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID
, &rv
));
1167 if (NS_SUCCEEDED(rv
)) {
1169 temp
.Assign(ptr
, lth
);
1170 typeString
->SetData(temp
);
1171 mutArray
->AppendElement(typeString
, PR_FALSE
);
1176 // if the array has any contents, addref & return it
1178 *_retval
= mutArray
;
1179 NS_ADDREF(*_retval
);
1183 rv
= NS_ERROR_FAILURE
;
1189 // see if the file is of the requested type
1192 nsLocalFile::IsFileType(const nsACString
& fileType
, PRBool
*_retval
)
1194 NS_ENSURE_ARG(_retval
);
1195 *_retval
= PR_FALSE
;
1197 // fetch the .TYPE ea & prepare for enumeration
1198 TypeEaEnumerator typeEnum
;
1199 nsresult rv
= typeEnum
.Init(this);
1206 // compare each type to the request; if there's a match, exit
1207 for (ptr
= typeEnum
.GetNext(<h
); ptr
; ptr
= typeEnum
.GetNext(<h
))
1208 if (fileType
.EqualsASCII(ptr
, lth
)) {
1216 //-----------------------------------------------------------------------------
1218 // this struct combines an FEA2LIST, an FEA2, plus additional fields
1219 // needed to write a .TYPE EA in the correct EAT_MVMT format
1221 typedef struct _TYPEEA
{
1224 ULONG uloNextEntryOffset
;
1228 char chszName
[sizeof(".TYPE")];
1233 USHORT usNumEntries
;
1240 typedef struct _TYPEEA2
{
1246 // writes one or more .TYPE extended attributes taken
1247 // from a comma-delimited string
1249 nsLocalFile::SetFileTypes(const nsACString
& fileTypes
)
1251 if (fileTypes
.IsEmpty())
1252 return NS_ERROR_FAILURE
;
1254 PRUint32 cnt
= CountCharInReadable(fileTypes
, ',');
1255 PRUint32 lth
= fileTypes
.Length() - cnt
+ (cnt
* sizeof(TYPEEA2
));
1256 PRUint32 size
= sizeof(TYPEEA
) + lth
;
1258 char *pBuf
= (char*)NS_Alloc(size
);
1260 return NS_ERROR_OUT_OF_MEMORY
;
1262 TYPEEA
*pEA
= (TYPEEA
*)pBuf
;
1264 // the buffer has an extra byte due to TYPEEA.data[1]
1265 pEA
->hdr
.ulcbList
= size
- 1;
1266 pEA
->hdr
.uloNextEntryOffset
= 0;
1268 pEA
->hdr
.bcbName
= sizeof(".TYPE") - 1;
1269 pEA
->hdr
.uscbValue
= sizeof(pEA
->info
) + lth
;
1270 strcpy(pEA
->hdr
.chszName
, ".TYPE");
1272 pEA
->info
.usEAType
= EAT_MVMT
;
1273 pEA
->info
.usCodePage
= 0;
1274 pEA
->info
.usNumEntries
= ++cnt
;
1276 nsACString::const_iterator begin
, end
, delim
;
1277 fileTypes
.BeginReading(begin
);
1278 fileTypes
.EndReading(end
);
1281 // fill in type & length, copy the current type name (which
1282 // is not zero-terminated), then advance the ptr so the next
1283 // iteration can reuse the trailing members of the structure
1285 FindCharInReadable( ',', delim
, end
);
1286 lth
= delim
.get() - begin
.get();
1287 pEA
->info
.usDataType
= EAT_ASCII
;
1288 pEA
->info
.usDataLth
= lth
;
1289 memcpy(pEA
->data
, begin
.get(), lth
);
1290 pEA
= (TYPEEA
*)((char*)pEA
+ lth
+ sizeof(TYPEEA2
));
1294 // write the EA, then free the buffer
1296 eaop2
.fpGEA2List
= 0;
1297 eaop2
.fpFEA2List
= (PFEA2LIST
)pBuf
;
1299 int rc
= DosSetPathInfo(mWorkingPath
.get(), FIL_QUERYEASIZE
,
1300 &eaop2
, sizeof(eaop2
), 0);
1304 return ConvertOS2Error(rc
);
1309 //-----------------------------------------------------------------------------
1311 // this struct combines an FEA2LIST, an FEA2, plus additional fields
1312 // needed to write a .SUBJECT EA in the correct EAT_ASCII format;
1314 typedef struct _SUBJEA
{
1317 ULONG uloNextEntryOffset
;
1321 char chszName
[sizeof(".SUBJECT")];
1331 // saves the file's source URI in its .SUBJECT extended attribute
1333 nsLocalFile::SetFileSource(const nsACString
& aURI
)
1336 return NS_ERROR_FAILURE
;
1338 // this includes an extra character for the spec's trailing null
1339 PRUint32 lth
= sizeof(SUBJEA
) + aURI
.Length();
1340 char * pBuf
= (char*)NS_Alloc(lth
);
1342 return NS_ERROR_OUT_OF_MEMORY
;
1344 SUBJEA
*pEA
= (SUBJEA
*)pBuf
;
1346 // the trailing null doesn't get saved, so don't include it in the size
1347 pEA
->hdr
.ulcbList
= lth
- 1;
1348 pEA
->hdr
.uloNextEntryOffset
= 0;
1350 pEA
->hdr
.bcbName
= sizeof(".SUBJECT") - 1;
1351 pEA
->hdr
.uscbValue
= sizeof(pEA
->info
) + aURI
.Length();
1352 strcpy(pEA
->hdr
.chszName
, ".SUBJECT");
1353 pEA
->info
.usEAType
= EAT_ASCII
;
1354 pEA
->info
.usDataLth
= aURI
.Length();
1355 strcpy(pEA
->data
, PromiseFlatCString(aURI
).get());
1357 // write the EA, then free the buffer
1359 eaop2
.fpGEA2List
= 0;
1360 eaop2
.fpFEA2List
= (PFEA2LIST
)pEA
;
1362 int rc
= DosSetPathInfo(mWorkingPath
.get(), FIL_QUERYEASIZE
,
1363 &eaop2
, sizeof(eaop2
), 0);
1367 return ConvertOS2Error(rc
);
1372 //-----------------------------------------------------------------------------
1375 nsLocalFile::CopySingleFile(nsIFile
*sourceFile
, nsIFile
*destParent
,
1376 const nsACString
&newName
, PRBool move
)
1379 nsCAutoString filePath
;
1381 nsCAutoString destPath
;
1382 destParent
->GetNativeTarget(destPath
);
1384 destPath
.Append("\\");
1386 if (newName
.IsEmpty())
1388 nsCAutoString aFileName
;
1389 sourceFile
->GetNativeLeafName(aFileName
);
1390 destPath
.Append(aFileName
);
1394 destPath
.Append(newName
);
1397 rv
= sourceFile
->GetNativePath(filePath
);
1402 APIRET rc
= NO_ERROR
;
1405 rc
= DosMove(filePath
.get(), (PSZ
)const_cast<char*>(destPath
.get()));
1407 if (!move
|| rc
== ERROR_NOT_SAME_DEVICE
|| rc
== ERROR_ACCESS_DENIED
)
1409 // will get an error if the destination and source files aren't on
1410 // the same drive. "MoveFile()" on Windows will go ahead and move
1411 // the file without error, so we need to do the same IBM-AKR
1414 rc
= DosCopy(filePath
.get(), (PSZ
)const_cast<char*>(destPath
.get()), DCPY_EXISTING
);
1415 if (rc
== ERROR_TOO_MANY_OPEN_FILES
) {
1419 rc2
= DosSetRelMaxFH(&ReqCount
, &CurMaxFH
);
1420 if (rc2
!= NO_ERROR
)
1423 } while (rc
== ERROR_TOO_MANY_OPEN_FILES
);
1425 // WSOD2 HACK - NETWORK_ACCESS_DENIED
1428 CHAR achProgram
[CCHMAXPATH
]; // buffer for program name, parameters
1429 RESULTCODES rescResults
; // buffer for results of dosexecpgm
1431 strcpy(achProgram
, "CMD.EXE /C ");
1432 strcat(achProgram
, """COPY ");
1433 strcat(achProgram
, filePath
.get());
1434 strcat(achProgram
, " ");
1435 strcat(achProgram
, (PSZ
)const_cast<char*>(destPath
.get()));
1436 strcat(achProgram
, """");
1437 achProgram
[strlen(achProgram
) + 1] = '\0';
1438 achProgram
[7] = '\0';
1440 EXEC_SYNC
, achProgram
, (PSZ
)NULL
,
1441 &rescResults
, achProgram
);
1442 rc
= 0; // Assume it worked
1446 // moving the file is supposed to act like a rename, so delete the
1447 // original file if we got this far without error
1448 if(move
&& (rc
== NO_ERROR
))
1449 DosDelete( filePath
.get());
1454 rv
= ConvertOS2Error(rc
);
1461 nsLocalFile::CopyMove(nsIFile
*aParentDir
, const nsACString
&newName
, PRBool move
)
1463 // Check we are correctly initialized.
1464 CHECK_mWorkingPath();
1466 nsCOMPtr
<nsIFile
> newParentDir
= aParentDir
;
1468 nsresult rv
= Stat();
1474 // no parent was specified. We must rename.
1476 if (newName
.IsEmpty())
1477 return NS_ERROR_INVALID_ARG
;
1479 rv
= GetParent(getter_AddRefs(newParentDir
));
1485 return NS_ERROR_FILE_DESTINATION_NOT_DIR
;
1487 // make sure it exists and is a directory. Create it if not there.
1489 newParentDir
->Exists(&exists
);
1492 rv
= newParentDir
->Create(DIRECTORY_TYPE
, 0644); // TODO, what permissions should we use
1499 newParentDir
->IsDirectory(&isDir
);
1500 if (isDir
== PR_FALSE
)
1502 return NS_ERROR_FILE_DESTINATION_NOT_DIR
;
1506 // Try different ways to move/copy files/directories
1507 PRBool done
= PR_FALSE
;
1509 IsDirectory(&isDir
);
1511 // Try to move the file or directory, or try to copy a single file
1514 // when moving things, first try to just MoveFile it,
1515 // even if it is a directory
1516 rv
= CopySingleFile(this, newParentDir
, newName
, move
);
1517 done
= NS_SUCCEEDED(rv
);
1518 // If we are moving a directory and that fails, fallback on directory
1519 // enumeration. See bug 231300 for details.
1520 if (!done
&& !(move
&& isDir
))
1524 // Not able to copy or move directly, so enumerate it
1527 // create a new target destination in the new parentDir;
1528 nsCOMPtr
<nsIFile
> target
;
1529 rv
= newParentDir
->Clone(getter_AddRefs(target
));
1534 nsCAutoString allocatedNewName
;
1535 if (newName
.IsEmpty())
1537 GetNativeLeafName(allocatedNewName
);
1541 allocatedNewName
= newName
;
1544 rv
= target
->AppendNative(allocatedNewName
);
1548 allocatedNewName
.Truncate();
1550 // check if the destination directory already exists
1551 target
->Exists(&exists
);
1554 // if the destination directory cannot be created, return an error
1555 rv
= target
->Create(DIRECTORY_TYPE
, 0644); // TODO, what permissions should we use
1561 // check if the destination directory is writable and empty
1564 target
->IsWritable(&isWritable
);
1566 return NS_ERROR_FILE_ACCESS_DENIED
;
1568 nsCOMPtr
<nsISimpleEnumerator
> targetIterator
;
1569 rv
= target
->GetDirectoryEntries(getter_AddRefs(targetIterator
));
1572 targetIterator
->HasMoreElements(&more
);
1573 // return error if target directory is not empty
1575 return NS_ERROR_FILE_DIR_NOT_EMPTY
;
1578 nsDirEnumerator dirEnum
;
1580 rv
= dirEnum
.Init(this);
1581 if (NS_FAILED(rv
)) {
1582 NS_WARNING("dirEnum initialization failed");
1587 while (NS_SUCCEEDED(dirEnum
.HasMoreElements(&more
)) && more
)
1589 nsCOMPtr
<nsISupports
> item
;
1590 nsCOMPtr
<nsIFile
> file
;
1591 dirEnum
.GetNext(getter_AddRefs(item
));
1592 file
= do_QueryInterface(item
);
1597 rv
= file
->MoveToNative(target
, EmptyCString());
1598 NS_ENSURE_SUCCESS(rv
,rv
);
1602 rv
= file
->CopyToNative(target
, EmptyCString());
1603 NS_ENSURE_SUCCESS(rv
,rv
);
1607 // we've finished moving all the children of this directory
1608 // in the new directory. so now delete the directory
1609 // note, we don't need to do a recursive delete.
1610 // MoveTo() is recursive. At this point,
1611 // we've already moved the children of the current folder
1612 // to the new location. nothing should be left in the folder.
1615 rv
= Remove(PR_FALSE
); // recursive
1616 NS_ENSURE_SUCCESS(rv
,rv
);
1621 // If we moved, we want to adjust this.
1626 nsCAutoString newParentPath
;
1627 newParentDir
->GetNativePath(newParentPath
);
1629 if (newParentPath
.IsEmpty())
1630 return NS_ERROR_FAILURE
;
1632 if (newName
.IsEmpty())
1634 nsCAutoString aFileName
;
1635 GetNativeLeafName(aFileName
);
1637 InitWithNativePath(newParentPath
);
1638 AppendNative(aFileName
);
1642 InitWithNativePath(newParentPath
);
1643 AppendNative(newName
);
1651 nsLocalFile::CopyToNative(nsIFile
*newParentDir
, const nsACString
&newName
)
1653 return CopyMove(newParentDir
, newName
, PR_FALSE
);
1657 nsLocalFile::CopyToFollowingLinksNative(nsIFile
*newParentDir
, const nsACString
&newName
)
1659 return CopyMove(newParentDir
, newName
, PR_FALSE
);
1663 nsLocalFile::MoveToNative(nsIFile
*newParentDir
, const nsACString
&newName
)
1665 return CopyMove(newParentDir
, newName
, PR_TRUE
);
1669 nsLocalFile::Load(PRLibrary
* *_retval
)
1671 // Check we are correctly initialized.
1672 CHECK_mWorkingPath();
1675 nsresult rv
= IsFile(&isFile
);
1681 return NS_ERROR_FILE_IS_DIRECTORY
;
1683 #ifdef NS_BUILD_REFCNT_LOGGING
1684 nsTraceRefcntImpl::SetActivityIsLegal(PR_FALSE
);
1687 *_retval
= PR_LoadLibrary(mWorkingPath
.get());
1689 #ifdef NS_BUILD_REFCNT_LOGGING
1690 nsTraceRefcntImpl::SetActivityIsLegal(PR_TRUE
);
1696 return NS_ERROR_NULL_POINTER
;
1700 nsLocalFile::Remove(PRBool recursive
)
1702 // Check we are correctly initialized.
1703 CHECK_mWorkingPath();
1705 PRBool isDir
= PR_FALSE
;
1707 nsresult rv
= IsDirectory(&isDir
);
1715 nsDirEnumerator dirEnum
;
1717 rv
= dirEnum
.Init(this);
1722 while (NS_SUCCEEDED(dirEnum
.HasMoreElements(&more
)) && more
)
1724 nsCOMPtr
<nsISupports
> item
;
1725 dirEnum
.GetNext(getter_AddRefs(item
));
1726 nsCOMPtr
<nsIFile
> file
= do_QueryInterface(item
);
1728 file
->Remove(recursive
);
1731 rv
= rmdir(mWorkingPath
.get());
1735 rv
= remove(mWorkingPath
.get());
1738 // fixup error code if necessary...
1739 if (rv
== (nsresult
)-1)
1740 rv
= NSRESULT_FOR_ERRNO();
1747 nsLocalFile::GetLastModifiedTime(PRInt64
*aLastModifiedTime
)
1749 // Check we are correctly initialized.
1750 CHECK_mWorkingPath();
1752 NS_ENSURE_ARG(aLastModifiedTime
);
1754 *aLastModifiedTime
= 0;
1755 nsresult rv
= Stat();
1759 // microseconds -> milliseconds
1760 PRInt64 usecPerMsec
;
1761 LL_I2L(usecPerMsec
, PR_USEC_PER_MSEC
);
1762 LL_DIV(*aLastModifiedTime
, mFileInfo64
.modifyTime
, usecPerMsec
);
1768 nsLocalFile::GetLastModifiedTimeOfLink(PRInt64
*aLastModifiedTime
)
1770 return NS_ERROR_NOT_IMPLEMENTED
;
1775 nsLocalFile::SetLastModifiedTime(PRInt64 aLastModifiedTime
)
1777 // Check we are correctly initialized.
1778 CHECK_mWorkingPath();
1780 return nsLocalFile::SetModDate(aLastModifiedTime
);
1785 nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModifiedTime
)
1787 return NS_ERROR_NOT_IMPLEMENTED
;
1791 nsLocalFile::SetModDate(PRInt64 aLastModifiedTime
)
1793 nsresult rv
= Stat();
1798 PRExplodedTime pret
;
1799 FILESTATUS3 pathInfo
;
1802 rv
= DosQueryPathInfo(mWorkingPath
.get(), FIL_STANDARD
,
1803 &pathInfo
, sizeof(pathInfo
));
1807 rv
= ConvertOS2Error(rv
);
1811 // PR_ExplodeTime expects usecs...
1812 PR_ExplodeTime(aLastModifiedTime
* PR_USEC_PER_MSEC
, PR_LocalTimeParameters
, &pret
);
1814 // fdateLastWrite.year is based off of 1980
1815 if (pret
.tm_year
>= 1980)
1816 pathInfo
.fdateLastWrite
.year
= pret
.tm_year
-1980;
1818 pathInfo
.fdateLastWrite
.year
= pret
.tm_year
;
1820 // Convert start offset -- OS/2: Jan=1; NSPR: Jan=0
1821 pathInfo
.fdateLastWrite
.month
= pret
.tm_month
+ 1;
1822 pathInfo
.fdateLastWrite
.day
= pret
.tm_mday
;
1823 pathInfo
.ftimeLastWrite
.hours
= pret
.tm_hour
;
1824 pathInfo
.ftimeLastWrite
.minutes
= pret
.tm_min
;
1825 pathInfo
.ftimeLastWrite
.twosecs
= pret
.tm_sec
/ 2;
1827 rv
= DosSetPathInfo(mWorkingPath
.get(), FIL_STANDARD
,
1828 &pathInfo
, sizeof(pathInfo
), 0UL);
1838 nsLocalFile::GetPermissions(PRUint32
*aPermissions
)
1840 NS_ENSURE_ARG(aPermissions
);
1842 nsresult rv
= Stat();
1846 PRBool isWritable
, isExecutable
;
1847 IsWritable(&isWritable
);
1848 IsExecutable(&isExecutable
);
1850 *aPermissions
= PR_IRUSR
|PR_IRGRP
|PR_IROTH
; // all read
1852 *aPermissions
|= PR_IWUSR
|PR_IWGRP
|PR_IWOTH
; // all write
1854 *aPermissions
|= PR_IXUSR
|PR_IXGRP
|PR_IXOTH
; // all execute
1860 nsLocalFile::GetPermissionsOfLink(PRUint32
*aPermissionsOfLink
)
1862 return NS_ERROR_NOT_IMPLEMENTED
;
1865 // the only Unix-style permission OS/2 knows is whether a file is
1866 // writable; as a matter of policy, a browser shouldn't be able
1867 // to change any of the other DOS-style attributes; to enforce
1868 // this, we use DosSetPathInfo() rather than chmod()
1870 nsLocalFile::SetPermissions(PRUint32 aPermissions
)
1872 // Check we are correctly initialized.
1873 CHECK_mWorkingPath();
1875 nsresult rv
= Stat();
1880 FILESTATUS3 pathInfo
;
1883 rc
= DosQueryPathInfo(mWorkingPath
.get(), FIL_STANDARD
,
1884 &pathInfo
, sizeof(pathInfo
));
1887 return ConvertOS2Error(rc
);
1890 if (!(aPermissions
& (PR_IWUSR
|PR_IWGRP
|PR_IWOTH
)))
1891 attr
= FILE_READONLY
;
1893 if (attr
== (pathInfo
.attrFile
& FILE_READONLY
))
1896 pathInfo
.attrFile
^= FILE_READONLY
;
1898 rc
= DosSetPathInfo(mWorkingPath
.get(), FIL_STANDARD
,
1899 &pathInfo
, sizeof(pathInfo
), 0UL);
1902 return ConvertOS2Error(rc
);
1908 nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions
)
1910 return NS_ERROR_NOT_IMPLEMENTED
;
1915 nsLocalFile::GetFileSize(PRInt64
*aFileSize
)
1917 // Check we are correctly initialized.
1918 CHECK_mWorkingPath();
1920 NS_ENSURE_ARG(aFileSize
);
1923 nsresult rv
= Stat();
1927 *aFileSize
= mFileInfo64
.size
;
1933 nsLocalFile::GetFileSizeOfLink(PRInt64
*aFileSize
)
1935 return NS_ERROR_NOT_IMPLEMENTED
;
1940 nsLocalFile::SetFileSize(PRInt64 aFileSize
)
1942 nsresult rv
= Stat();
1950 rc
= DosOpen(mWorkingPath
.get(),
1955 OPEN_ACTION_FAIL_IF_NEW
| OPEN_ACTION_OPEN_IF_EXISTS
,
1956 OPEN_SHARE_DENYREADWRITE
| OPEN_ACCESS_READWRITE
,
1962 return NS_ERROR_FAILURE
;
1965 // Seek to new, desired end of file
1967 myLL_L2II(aFileSize
, &hi
, &lo
);
1969 rc
= DosSetFileSize(hFile
, lo
);
1974 return NS_ERROR_FAILURE
;
1980 nsLocalFile::GetDiskSpaceAvailable(PRInt64
*aDiskSpaceAvailable
)
1982 // Check we are correctly initialized.
1983 CHECK_mWorkingPath();
1985 NS_ENSURE_ARG(aDiskSpaceAvailable
);
1986 *aDiskSpaceAvailable
= 0;
1988 nsresult rv
= Stat();
1992 ULONG ulDriveNo
= toupper(mWorkingPath
.CharAt(0)) + 1 - 'A';
1993 FSALLOCATE fsAllocate
;
1994 APIRET rc
= DosQueryFSInfo(ulDriveNo
,
1997 sizeof(fsAllocate
));
2000 return NS_ERROR_FAILURE
;
2002 *aDiskSpaceAvailable
= fsAllocate
.cUnitAvail
;
2003 *aDiskSpaceAvailable
*= fsAllocate
.cSectorUnit
;
2004 *aDiskSpaceAvailable
*= fsAllocate
.cbSector
;
2010 nsLocalFile::GetParent(nsIFile
* *aParent
)
2012 // Check we are correctly initialized.
2013 CHECK_mWorkingPath();
2015 NS_ENSURE_ARG_POINTER(aParent
);
2017 nsCAutoString
parentPath(mWorkingPath
);
2019 // cannot use nsCString::RFindChar() due to 0x5c problem
2020 PRInt32 offset
= (PRInt32
) (_mbsrchr((const unsigned char *) parentPath
.get(), '\\')
2021 - (const unsigned char *) parentPath
.get());
2022 // adding this offset check that was removed in bug 241708 fixes mail
2023 // directories that aren't relative to/underneath the profile dir.
2024 // e.g., on a different drive. Before you remove them, please make
2025 // sure local mail directories that aren't underneath the profile dir work.
2027 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
2029 if (offset
== 1 && parentPath
[0] == '\\') {
2034 parentPath
.Truncate(offset
);
2036 parentPath
.AssignLiteral("\\\\.");
2038 nsCOMPtr
<nsILocalFile
> localFile
;
2039 nsresult rv
= NS_NewNativeLocalFile(parentPath
, PR_FALSE
, getter_AddRefs(localFile
));
2041 if(NS_SUCCEEDED(rv
) && localFile
)
2043 return CallQueryInterface(localFile
, aParent
);
2049 nsLocalFile::Exists(PRBool
*_retval
)
2051 // Check we are correctly initialized.
2052 CHECK_mWorkingPath();
2054 NS_ENSURE_ARG(_retval
);
2055 *_retval
= PR_FALSE
;
2058 nsresult rv
= Stat();
2060 *_retval
= NS_SUCCEEDED(rv
);
2065 nsLocalFile::IsWritable(PRBool
*_retval
)
2067 // Check we are correctly initialized.
2068 CHECK_mWorkingPath();
2070 NS_ENSURE_ARG(_retval
);
2071 *_retval
= PR_FALSE
;
2073 nsresult rv
= Stat();
2078 FILESTATUS3 pathInfo
;
2081 rc
= DosQueryPathInfo(mWorkingPath
.get(), FIL_STANDARD
,
2082 &pathInfo
, sizeof(pathInfo
));
2086 rc
= ConvertOS2Error(rc
);
2090 // on OS/2, unlike Windows, directories on writable media
2091 // can not be assigned a readonly attribute
2092 *_retval
= !((pathInfo
.attrFile
& FILE_READONLY
) != 0);
2097 nsLocalFile::IsReadable(PRBool
*_retval
)
2099 // Check we are correctly initialized.
2100 CHECK_mWorkingPath();
2102 NS_ENSURE_ARG(_retval
);
2103 *_retval
= PR_FALSE
;
2105 nsresult rv
= Stat();
2115 nsLocalFile::IsExecutable(PRBool
*_retval
)
2117 // Check we are correctly initialized.
2118 CHECK_mWorkingPath();
2120 NS_ENSURE_ARG(_retval
);
2121 *_retval
= PR_FALSE
;
2123 nsresult rv
= Stat();
2127 // no need to bother if this isn't a file
2129 rv
= IsFile(&isFile
);
2130 if (NS_FAILED(rv
) || !isFile
)
2134 GetNativeTarget(path
);
2136 // get the filename, including the leading backslash
2137 const char* leaf
= (const char*) _mbsrchr((const unsigned char*)path
.get(), '\\');
2141 // eliminate trailing dots & spaces in a DBCS-aware manner
2142 // XXX shouldn't this have been done when the path was set?
2144 char* pathEnd
= WinPrevChar(0, 0, 0, leaf
, strchr(leaf
, 0));
2145 while (pathEnd
> leaf
)
2147 if (*pathEnd
!= ' ' && *pathEnd
!= '.')
2150 pathEnd
= WinPrevChar(0, 0, 0, leaf
, pathEnd
);
2153 if (pathEnd
== leaf
)
2156 // get the extension, including the dot
2157 char* ext
= (char*) _mbsrchr((const unsigned char*)leaf
, '.');
2161 if (stricmp(ext
, ".exe") == 0 ||
2162 stricmp(ext
, ".cmd") == 0 ||
2163 stricmp(ext
, ".com") == 0 ||
2164 stricmp(ext
, ".bat") == 0)
2172 nsLocalFile::IsDirectory(PRBool
*_retval
)
2174 NS_ENSURE_ARG(_retval
);
2175 *_retval
= PR_FALSE
;
2177 nsresult rv
= Stat();
2181 *_retval
= (mFileInfo64
.type
== PR_FILE_DIRECTORY
);
2186 nsLocalFile::IsFile(PRBool
*_retval
)
2188 NS_ENSURE_ARG(_retval
);
2189 *_retval
= PR_FALSE
;
2191 nsresult rv
= Stat();
2195 *_retval
= (mFileInfo64
.type
== PR_FILE_FILE
);
2200 nsLocalFile::IsHidden(PRBool
*_retval
)
2202 NS_ENSURE_ARG(_retval
);
2203 *_retval
= PR_FALSE
;
2205 nsresult rv
= Stat();
2210 FILESTATUS3 pathInfo
;
2213 rc
= DosQueryPathInfo(mWorkingPath
.get(), FIL_STANDARD
,
2214 &pathInfo
, sizeof(pathInfo
));
2218 rc
= ConvertOS2Error(rc
);
2222 *_retval
= ((pathInfo
.attrFile
& FILE_HIDDEN
) != 0);
2228 nsLocalFile::IsSymlink(PRBool
*_retval
)
2230 // Check we are correctly initialized.
2231 CHECK_mWorkingPath();
2233 NS_ENSURE_ARG_POINTER(_retval
);
2235 // No Symlinks on OS/2
2236 *_retval
= PR_FALSE
;
2241 nsLocalFile::IsSpecial(PRBool
*_retval
)
2243 NS_ENSURE_ARG(_retval
);
2245 // when implemented, IsSpecial will be used for WPS objects
2246 *_retval
= PR_FALSE
;
2251 nsLocalFile::Equals(nsIFile
*inFile
, PRBool
*_retval
)
2253 NS_ENSURE_ARG(inFile
);
2254 NS_ENSURE_ARG(_retval
);
2256 nsCAutoString inFilePath
;
2257 inFile
->GetNativePath(inFilePath
);
2259 *_retval
= inFilePath
.Equals(mWorkingPath
);
2264 nsLocalFile::Contains(nsIFile
*inFile
, PRBool recur
, PRBool
*_retval
)
2266 // Check we are correctly initialized.
2267 CHECK_mWorkingPath();
2269 *_retval
= PR_FALSE
;
2271 nsCAutoString myFilePath
;
2272 if ( NS_FAILED(GetNativeTarget(myFilePath
)))
2273 GetNativePath(myFilePath
);
2275 PRInt32 myFilePathLen
= myFilePath
.Length();
2277 nsCAutoString inFilePath
;
2278 if ( NS_FAILED(inFile
->GetNativeTarget(inFilePath
)))
2279 inFile
->GetNativePath(inFilePath
);
2281 if ( strnicmp( myFilePath
.get(), inFilePath
.get(), myFilePathLen
) == 0)
2283 // now make sure that the |inFile|'s path has a trailing
2286 if (inFilePath
[myFilePathLen
] == '\\')
2297 nsLocalFile::GetNativeTarget(nsACString
&_retval
)
2299 // Check we are correctly initialized.
2300 CHECK_mWorkingPath();
2302 _retval
= mWorkingPath
;
2307 nsLocalFile::GetFollowLinks(PRBool
*aFollowLinks
)
2309 NS_ENSURE_ARG(aFollowLinks
);
2310 *aFollowLinks
= PR_FALSE
;
2315 nsLocalFile::SetFollowLinks(PRBool aFollowLinks
)
2321 nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator
* *entries
)
2323 NS_ENSURE_ARG(entries
);
2327 if (mWorkingPath
.EqualsLiteral("\\\\.")) {
2328 nsDriveEnumerator
*drives
= new nsDriveEnumerator
;
2330 return NS_ERROR_OUT_OF_MEMORY
;
2332 rv
= drives
->Init();
2333 if (NS_FAILED(rv
)) {
2342 rv
= IsDirectory(&isDir
);
2346 return NS_ERROR_FILE_NOT_DIRECTORY
;
2348 nsDirEnumerator
* dirEnum
= new nsDirEnumerator();
2349 if (dirEnum
== nsnull
)
2350 return NS_ERROR_OUT_OF_MEMORY
;
2352 rv
= dirEnum
->Init(this);
2355 NS_RELEASE(dirEnum
);
2364 nsLocalFile::GetPersistentDescriptor(nsACString
&aPersistentDescriptor
)
2366 return GetNativePath(aPersistentDescriptor
);
2370 nsLocalFile::SetPersistentDescriptor(const nsACString
&aPersistentDescriptor
)
2372 return InitWithNativePath(aPersistentDescriptor
);
2375 #ifndef OPEN_DEFAULT
2376 #define OPEN_DEFAULT 0
2377 #define OPEN_CONTENTS 1
2381 nsLocalFile::Reveal()
2383 PRBool isDirectory
= PR_FALSE
;
2386 IsDirectory(&isDirectory
);
2389 GetNativePath(path
);
2393 nsCOMPtr
<nsIFile
> parent
;
2394 GetParent(getter_AddRefs(parent
));
2396 parent
->GetNativePath(path
);
2399 HOBJECT hobject
= WinQueryObject(path
.get());
2400 WinSetFocus(HWND_DESKTOP
, HWND_DESKTOP
);
2401 WinOpenObject(hobject
, OPEN_DEFAULT
, TRUE
);
2403 // we don't care if it succeeded or failed.
2409 nsLocalFile::Launch()
2411 HOBJECT hobject
= WinQueryObject(mWorkingPath
.get());
2412 WinSetFocus(HWND_DESKTOP
, HWND_DESKTOP
);
2413 WinOpenObject(hobject
, OPEN_DEFAULT
, TRUE
);
2415 // we don't care if it succeeded or failed.
2420 NS_NewNativeLocalFile(const nsACString
&path
, PRBool followLinks
, nsILocalFile
* *result
)
2422 nsLocalFile
* file
= new nsLocalFile();
2424 return NS_ERROR_OUT_OF_MEMORY
;
2427 if (!path
.IsEmpty()) {
2428 nsresult rv
= file
->InitWithNativePath(path
);
2429 if (NS_FAILED(rv
)) {
2439 //-----------------------------------------------------------------------------
2441 //-----------------------------------------------------------------------------
2444 nsLocalFile::InitWithPath(const nsAString
&filePath
)
2446 if (filePath
.IsEmpty())
2447 return InitWithNativePath(EmptyCString());
2450 nsresult rv
= NS_CopyUnicodeToNative(filePath
, tmp
);
2451 if (NS_SUCCEEDED(rv
))
2452 return InitWithNativePath(tmp
);
2458 nsLocalFile::Append(const nsAString
&node
)
2464 nsresult rv
= NS_CopyUnicodeToNative(node
, tmp
);
2465 if (NS_SUCCEEDED(rv
))
2466 return AppendNative(tmp
);
2472 nsLocalFile::AppendRelativePath(const nsAString
&node
)
2478 nsresult rv
= NS_CopyUnicodeToNative(node
, tmp
);
2479 if (NS_SUCCEEDED(rv
))
2480 return AppendRelativeNativePath(tmp
);
2486 nsLocalFile::GetLeafName(nsAString
&aLeafName
)
2489 nsresult rv
= GetNativeLeafName(tmp
);
2490 if (NS_SUCCEEDED(rv
))
2491 rv
= NS_CopyNativeToUnicode(tmp
, aLeafName
);
2497 nsLocalFile::SetLeafName(const nsAString
&aLeafName
)
2499 if (aLeafName
.IsEmpty())
2500 return SetNativeLeafName(EmptyCString());
2503 nsresult rv
= NS_CopyUnicodeToNative(aLeafName
, tmp
);
2504 if (NS_SUCCEEDED(rv
))
2505 return SetNativeLeafName(tmp
);
2511 nsLocalFile::GetPath(nsAString
&_retval
)
2513 return NS_CopyNativeToUnicode(mWorkingPath
, _retval
);
2517 nsLocalFile::CopyTo(nsIFile
*newParentDir
, const nsAString
&newName
)
2519 if (newName
.IsEmpty())
2520 return CopyToNative(newParentDir
, EmptyCString());
2523 nsresult rv
= NS_CopyUnicodeToNative(newName
, tmp
);
2524 if (NS_SUCCEEDED(rv
))
2525 return CopyToNative(newParentDir
, tmp
);
2531 nsLocalFile::CopyToFollowingLinks(nsIFile
*newParentDir
, const nsAString
&newName
)
2533 if (newName
.IsEmpty())
2534 return CopyToFollowingLinksNative(newParentDir
, EmptyCString());
2537 nsresult rv
= NS_CopyUnicodeToNative(newName
, tmp
);
2538 if (NS_SUCCEEDED(rv
))
2539 return CopyToFollowingLinksNative(newParentDir
, tmp
);
2545 nsLocalFile::MoveTo(nsIFile
*newParentDir
, const nsAString
&newName
)
2547 if (newName
.IsEmpty())
2548 return MoveToNative(newParentDir
, EmptyCString());
2551 nsresult rv
= NS_CopyUnicodeToNative(newName
, tmp
);
2552 if (NS_SUCCEEDED(rv
))
2553 return MoveToNative(newParentDir
, tmp
);
2559 nsLocalFile::GetTarget(nsAString
&_retval
)
2562 nsresult rv
= GetNativeTarget(tmp
);
2563 if (NS_SUCCEEDED(rv
))
2564 rv
= NS_CopyNativeToUnicode(tmp
, _retval
);
2572 nsLocalFile::Equals(nsIHashable
* aOther
, PRBool
*aResult
)
2574 nsCOMPtr
<nsIFile
> otherfile(do_QueryInterface(aOther
));
2576 *aResult
= PR_FALSE
;
2580 return Equals(otherfile
, aResult
);
2584 nsLocalFile::GetHashCode(PRUint32
*aResult
)
2586 *aResult
= nsCRT::HashCode(mWorkingPath
.get());
2591 NS_NewLocalFile(const nsAString
&path
, PRBool followLinks
, nsILocalFile
* *result
)
2594 nsresult rv
= NS_CopyUnicodeToNative(path
, buf
);
2595 if (NS_FAILED(rv
)) {
2599 return NS_NewNativeLocalFile(buf
, followLinks
, result
);
2602 //-----------------------------------------------------------------------------
2603 // nsLocalFile <static members>
2604 //-----------------------------------------------------------------------------
2607 nsLocalFile::GlobalInit()
2612 nsLocalFile::GlobalShutdown()
2616 //-----------------------------------------------------------------------------