On x86 compilers without fastcall, simulate it when invoking traces and un-simulate...
[wine-gecko.git] / xpcom / io / nsLocalFileOS2.cpp
blobe6d29bb77003269e28985fc7e1b127947738bf48
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Mozilla Communicator client code, released
16 * March 31, 1998.
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.
23 * Contributor(s):
24 * Henry Sobotka <sobotka@axess.com>
25 * IBM Corp.
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
45 #include "nsCOMPtr.h"
46 #include "nsMemory.h"
48 #include "nsLocalFile.h"
49 #include "nsNativeCharsetUtils.h"
51 #include "nsISimpleEnumerator.h"
52 #include "nsIDirectoryEnumerator.h"
53 #include "nsIComponentManager.h"
54 #include "prtypes.h"
55 #include "prio.h"
57 #include "nsReadableUtils.h"
58 #include "nsISupportsPrimitives.h"
59 #include "nsIMutableArray.h"
60 #include "nsTraceRefcntImpl.h"
62 #define CHECK_mWorkingPath() \
63 PR_BEGIN_MACRO \
64 if (mWorkingPath.IsEmpty()) \
65 return NS_ERROR_NOT_INITIALIZED; \
66 PR_END_MACRO
68 //-----------------------------------------------------------------------------
69 // static helper functions
70 //-----------------------------------------------------------------------------
72 static nsresult ConvertOS2Error(int err)
74 nsresult rv;
76 switch (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;
82 break;
83 case ERROR_ACCESS_DENIED:
84 case ERROR_NOT_SAME_DEVICE:
85 rv = NS_ERROR_FILE_ACCESS_DENIED;
86 break;
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;
92 break;
93 case ERROR_CURRENT_DIRECTORY:
94 rv = NS_ERROR_FILE_DIR_NOT_EMPTY;
95 break;
96 case ERROR_WRITE_PROTECT:
97 rv = NS_ERROR_FILE_READ_ONLY;
98 break;
99 case ERROR_HANDLE_DISK_FULL:
100 rv = NS_ERROR_FILE_TOO_BIG;
101 break;
102 case ERROR_FILE_EXISTS:
103 case ERROR_ALREADY_EXISTS:
104 case ERROR_CANNOT_MAKE:
105 rv = NS_ERROR_FILE_ALREADY_EXISTS;
106 break;
107 case ERROR_FILENAME_EXCED_RANGE:
108 rv = NS_ERROR_FILE_NAME_TOO_LONG;
109 break;
110 case 0:
111 rv = NS_OK;
112 default:
113 rv = NS_ERROR_FAILURE;
115 return rv;
118 static void
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,
123 // and I am a wimp.
125 // shift the hi word to the low word, then push it into a long.
126 LL_SHR(a64, result, 32);
127 LL_L2I(*hi, a64);
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);
132 LL_L2I(*lo, a64);
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;
141 do {
142 if (*p == charToSearchFor)
143 break;
144 p = (const unsigned char*)WinNextChar(0,0,0,(char*)p);
145 } while (*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;
158 do {
159 while (*pStr && *pStr != *pSub)
160 pStr = (const unsigned char*)WinNextChar(0,0,0,(char*)pStr);
162 if (!*pStr)
163 break;
165 const unsigned char* pNxt = pStr;
166 do {
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);
171 if (!*pSub)
172 break;
174 pSub = subString;
175 pStr = (const unsigned char*)WinNextChar(0,0,0,(char*)pStr);
177 } while (*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;
190 do {
191 if (*p == charToSearchFor)
192 break;
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)
204 APIRET rc;
205 nsresult rv;
206 FILESTATUS3 pathInfo;
208 rc = DosCreateDir(path, ppEABuf);
209 if (rc != NO_ERROR)
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));
217 if (rc == NO_ERROR)
218 rv = ERROR_FILE_EXISTS;
220 else
221 rv = rc;
223 return rv;
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 };
231 BYTE *curr;
232 BOOL retval = FALSE;
234 if(!bDBCSFilled)
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?
243 if (rc != NO_ERROR)
244 return FALSE;
246 bDBCSFilled=TRUE;
249 // DBCSInfo returned by DosQueryDBCSEnv is terminated
250 // with two '0' bytes in a row
251 curr = DBCSInfo;
252 while(*curr != 0 && *(curr+1) != 0)
254 if(c >= *curr && c <= *(curr+1))
256 retval=TRUE;
257 break;
259 curr+=2;
262 return retval;
265 //-----------------------------------------------------------------------------
266 // nsDirEnumerator
267 //-----------------------------------------------------------------------------
269 class nsDirEnumerator : public nsISimpleEnumerator,
270 public nsIDirectoryEnumerator
272 public:
274 NS_DECL_ISUPPORTS
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;
299 mParent = parent;
300 return NS_OK;
303 NS_IMETHOD HasMoreElements(PRBool *result)
305 nsresult rv;
306 if (mNext == nsnull && mDir)
308 PRDirEntry* entry = PR_ReadDir(mDir, PR_SKIP_BOTH);
309 if (entry == nsnull)
311 // end of dir entries
313 PRStatus status = PR_CloseDir(mDir);
314 if (status != PR_SUCCESS)
315 return NS_ERROR_FAILURE;
316 mDir = nsnull;
318 *result = PR_FALSE;
319 return NS_OK;
322 nsCOMPtr<nsIFile> file;
323 rv = mParent->Clone(getter_AddRefs(file));
324 if (NS_FAILED(rv))
325 return rv;
327 rv = file->AppendNative(nsDependentCString(entry->name));
328 if (NS_FAILED(rv))
329 return rv;
331 // make sure the thing exists. If it does, try the next one.
332 PRBool exists;
333 rv = file->Exists(&exists);
334 if (NS_FAILED(rv) || !exists)
336 return HasMoreElements(result);
339 mNext = do_QueryInterface(file);
341 *result = mNext != nsnull;
342 if (!*result)
343 Close();
344 return NS_OK;
347 NS_IMETHOD GetNext(nsISupports **result)
349 nsresult rv;
350 PRBool hasMore;
351 rv = HasMoreElements(&hasMore);
352 if (NS_FAILED(rv)) return rv;
354 *result = mNext; // might return nsnull
355 NS_IF_ADDREF(*result);
357 mNext = nsnull;
358 return NS_OK;
361 NS_IMETHOD GetNextFile(nsIFile **result)
363 *result = nsnull;
364 PRBool hasMore = PR_FALSE;
365 nsresult rv = HasMoreElements(&hasMore);
366 if (NS_FAILED(rv) || !hasMore)
367 return rv;
368 *result = mNext;
369 NS_IF_ADDREF(*result);
370 mNext = nsnull;
371 return NS_OK;
374 NS_IMETHOD Close()
376 if (mDir)
378 PRStatus status = PR_CloseDir(mDir);
379 NS_ASSERTION(status == PR_SUCCESS, "close failed");
380 if (status != PR_SUCCESS)
381 return NS_ERROR_FAILURE;
382 mDir = nsnull;
384 return NS_OK;
387 // dtor can be non-virtual since there are no subclasses, but must be
388 // public to use the class on the stack.
389 ~nsDirEnumerator()
391 Close();
394 protected:
395 PRDir* mDir;
396 nsCOMPtr<nsILocalFile> mParent;
397 nsCOMPtr<nsILocalFile> mNext;
400 NS_IMPL_ISUPPORTS2(nsDirEnumerator, nsISimpleEnumerator, nsIDirectoryEnumerator)
402 //-----------------------------------------------------------------------------
403 // nsDriveEnumerator
404 //-----------------------------------------------------------------------------
406 class nsDriveEnumerator : public nsISimpleEnumerator
408 public:
409 nsDriveEnumerator();
410 virtual ~nsDriveEnumerator();
411 NS_DECL_ISUPPORTS
412 NS_DECL_NSISIMPLEENUMERATOR
413 nsresult Init();
415 private:
416 // mDrives is a bitmap representing the available drives
417 // mLetter is incremented each time mDrives is shifted rightward
418 PRUint32 mDrives;
419 PRUint8 mLetter;
422 NS_IMPL_ISUPPORTS1(nsDriveEnumerator, nsISimpleEnumerator)
424 nsDriveEnumerator::nsDriveEnumerator()
425 : mDrives(0), mLetter(0)
429 nsDriveEnumerator::~nsDriveEnumerator()
433 nsresult nsDriveEnumerator::Init()
435 ULONG ulCurrent;
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);
441 if (rc)
442 return NS_ERROR_FAILURE;
444 mLetter = 'A';
445 return NS_OK;
448 NS_IMETHODIMP nsDriveEnumerator::HasMoreElements(PRBool *aHasMore)
450 // no more bits means no more drives
451 *aHasMore = (mDrives != 0);
452 return NS_OK;
455 NS_IMETHODIMP nsDriveEnumerator::GetNext(nsISupports **aNext)
457 if (!mDrives)
459 *aNext = nsnull;
460 return NS_OK;
463 // if bit 0 is off, advance to the next bit that's on
464 while ((mDrives & 1) == 0)
466 mDrives >>= 1;
467 mLetter++;
470 // format a drive string, then advance to the next possible drive
471 char drive[4] = "x:\\";
472 drive[0] = mLetter;
473 mDrives >>= 1;
474 mLetter++;
476 nsILocalFile *file;
477 nsresult rv = NS_NewNativeLocalFile(nsDependentCString(drive),
478 PR_FALSE, &file);
479 *aNext = file;
481 return rv;
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 {
493 USHORT usEAType;
494 USHORT usCodePage;
495 USHORT usNumEntries;
496 USHORT usDataType;
497 USHORT usDataLth;
498 char data[1];
499 } MVHDR;
501 typedef MVHDR *PMVHDR;
504 class TypeEaEnumerator
506 public:
507 TypeEaEnumerator() : mEaBuf(nsnull) { }
508 ~TypeEaEnumerator() { if (mEaBuf) NS_Free(mEaBuf); }
510 nsresult Init(nsLocalFile * aFile);
511 char * GetNext(PRUint32 *lth);
513 private:
514 char * mEaBuf;
515 char * mpCur;
516 PMVHDR mpMvh;
517 USHORT mLth;
518 USHORT mCtr;
522 nsresult TypeEaEnumerator::Init(nsLocalFile * aFile)
524 #define EABUFSIZE 512
526 // provide a buffer for the results
527 mEaBuf = (char*)NS_Alloc(EABUFSIZE);
528 if (!mEaBuf)
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);
536 if (NS_FAILED(rv))
537 return rv;
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
547 mLth = 0;
548 mCtr = 0;
549 mpCur = (char*)(mpMvh->usEAType == EAT_MVMT ?
550 &mpMvh->usDataType : &mpMvh->usDataLth);
552 return NS_OK;
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
565 mpCur += mLth;
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)
571 continue;
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);
578 result = mpCur;
579 break;
582 return result;
585 //-----------------------------------------------------------------------------
586 // nsLocalFile <public>
587 //-----------------------------------------------------------------------------
589 nsLocalFile::nsLocalFile()
590 : mDirty(PR_TRUE)
594 NS_METHOD
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();
601 if (inst == NULL)
602 return NS_ERROR_OUT_OF_MEMORY;
604 nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
605 if (NS_FAILED(rv))
607 delete inst;
608 return rv;
610 return NS_OK;
614 //-----------------------------------------------------------------------------
615 // nsLocalFile::nsISupports
616 //-----------------------------------------------------------------------------
618 NS_IMPL_THREADSAFE_ISUPPORTS4(nsLocalFile,
619 nsILocalFile,
620 nsIFile,
621 nsILocalFileOS2,
622 nsIHashable)
625 //-----------------------------------------------------------------------------
626 // nsLocalFile <private>
627 //-----------------------------------------------------------------------------
629 nsLocalFile::nsLocalFile(const nsLocalFile& other)
630 : mDirty(PR_TRUE)
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.
638 nsresult
639 nsLocalFile::Stat()
641 // if we aren't dirty then we are already done
642 if (!mDirty)
643 return NS_OK;
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
650 char temp[4];
651 const char *nsprPath = mWorkingPath.get();
652 if (mWorkingPath.Length() == 2 && mWorkingPath.CharAt(1) == ':')
654 temp[0] = mWorkingPath[0];
655 temp[1] = mWorkingPath[1];
656 temp[2] = '\\';
657 temp[3] = '\0';
658 nsprPath = temp;
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;
668 mDirty = PR_FALSE;
669 return NS_OK;
673 //-----------------------------------------------------------------------------
674 // nsLocalFile::nsIFile,nsILocalFile
675 //-----------------------------------------------------------------------------
677 NS_IMETHODIMP
678 nsLocalFile::Clone(nsIFile **file)
680 // Just copy-construct ourselves
681 *file = new nsLocalFile(*this);
682 if (!*file)
683 return NS_ERROR_OUT_OF_MEMORY;
685 NS_ADDREF(*file);
687 return NS_OK;
690 NS_IMETHODIMP
691 nsLocalFile::InitWithNativePath(const nsACString &filePath)
693 MakeDirty();
695 nsACString::const_iterator begin, end;
696 filePath.BeginReading(begin);
697 filePath.EndReading(end);
699 // input string must not be empty
700 if (begin == end)
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.
709 char *path = nsnull;
710 PRInt32 pathLen = 0;
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();
720 if (path == nsnull)
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]))
727 path[len] = '\0';
728 pathLen = len;
731 mWorkingPath.Adopt(path, pathLen);
732 return NS_OK;
735 NS_IMETHODIMP
736 nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
738 nsresult rv = Stat();
739 if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
740 return rv;
742 *_retval = PR_Open(mWorkingPath.get(), flags, mode);
743 if (*_retval)
744 return NS_OK;
746 return NS_ErrorAccordingToNSPR();
750 NS_IMETHODIMP
751 nsLocalFile::OpenANSIFileDesc(const char *mode, FILE * *_retval)
753 nsresult rv = Stat();
754 if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
755 return rv;
757 *_retval = fopen(mWorkingPath.get(), mode);
758 if (*_retval)
759 return NS_OK;
761 return NS_ERROR_FAILURE;
766 NS_IMETHODIMP
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)
774 return rv;
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
781 // ^--- start here
783 // - UNC path: \\machine\volume\some\path\on\this\drive
784 // ^--- start here
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, '\\');
795 if (!path)
796 return NS_ERROR_FILE_INVALID_PATH;
797 ++path;
800 // search for first slash after the drive (or volume) name
801 unsigned char* slash = _mbschr(path, '\\');
803 if (slash)
805 // skip the first '\\'
806 ++slash;
807 slash = _mbschr(slash, '\\');
809 while (slash)
811 *slash = '\0';
813 rv = CreateDirectoryA(const_cast<char*>(mWorkingPath.get()), NULL);
814 if (rv) {
815 rv = ConvertOS2Error(rv);
816 if (rv != NS_ERROR_FILE_ALREADY_EXISTS)
817 return rv;
819 *slash = '\\';
820 ++slash;
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);
828 if (!file)
829 return NS_ERROR_FILE_ALREADY_EXISTS;
831 PR_Close(file);
832 return NS_OK;
835 if (type == DIRECTORY_TYPE)
837 rv = CreateDirectoryA(const_cast<char*>(mWorkingPath.get()), NULL);
838 if (rv)
839 return ConvertOS2Error(rv);
840 else
841 return NS_OK;
844 return NS_ERROR_FILE_UNKNOWN_TYPE;
848 NS_IMETHODIMP
849 nsLocalFile::AppendNative(const nsACString &node)
851 // append this path, multiple components are not permitted
852 return AppendNativeInternal(PromiseFlatCString(node), PR_FALSE);
855 NS_IMETHODIMP
856 nsLocalFile::AppendRelativeNativePath(const nsACString &node)
858 // append this path, multiple components are permitted
859 return AppendNativeInternal(PromiseFlatCString(node), PR_TRUE);
862 nsresult
863 nsLocalFile::AppendNativeInternal(const nsAFlatCString &node, PRBool multipleComponents)
865 if (node.IsEmpty())
866 return NS_OK;
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 *)"\\..");
881 while (doubleDot)
883 doubleDot += 3;
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;
900 MakeDirty();
902 mWorkingPath.Append(NS_LITERAL_CSTRING("\\") + node);
904 return NS_OK;
907 NS_IMETHODIMP
908 nsLocalFile::Normalize()
910 // XXX See bug 187957 comment 18 for possible problems with this implementation.
912 if (mWorkingPath.IsEmpty())
913 return NS_OK;
915 // work in unicode for ease
916 nsAutoString path;
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).
940 char drv[4] = "A:.";
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);
952 else
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())
960 return NS_OK;
962 // assign the root
963 nsAutoString normal;
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
986 begin = end + 1;
987 end = path.FindChar('\\', begin);
988 if (end == kNotFound)
989 end = path.Length();
990 len = end - begin;
992 // ignore double backslashes
993 if (len == 0)
994 continue;
996 // len != 0, and interesting paths always begin with a dot
997 if (pathBuffer[begin] == '.')
999 // ignore single dots
1000 if (len == 1)
1001 continue;
1003 // handle multiple dots
1004 if (len >= 2 && pathBuffer[begin+1] == '.')
1006 // back up a path component on double dot
1007 if (len == 2)
1009 PRInt32 prev = normal.RFindChar('\\');
1010 if (prev >= rootIdx)
1011 normal.Truncate(prev);
1012 continue;
1015 // length is > 2 and the first two characters are dots.
1016 // if the rest of the string is dots, then ignore it.
1017 int idx = len - 1;
1018 for (; idx >= 2; --idx)
1020 if (pathBuffer[begin+idx] != '.')
1021 break;
1024 // this is true if the loop above didn't break
1025 // and all characters in this segment are dots.
1026 if (idx < 2)
1027 continue;
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);
1043 MakeDirty();
1045 return NS_OK;
1048 NS_IMETHODIMP
1049 nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
1051 aLeafName.Truncate();
1053 const char* temp = mWorkingPath.get();
1054 if(temp == nsnull)
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.
1060 if (leaf == nsnull)
1061 leaf = temp;
1062 else
1063 leaf++;
1065 aLeafName.Assign(leaf);
1066 return NS_OK;
1069 NS_IMETHODIMP
1070 nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
1072 MakeDirty();
1074 const unsigned char* temp = (const unsigned char*) mWorkingPath.get();
1075 if(temp == nsnull)
1076 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
1078 // cannot use nsCString::RFindChar() due to 0x5c problem
1079 PRInt32 offset = (PRInt32) (_mbsrchr(temp, '\\') - temp);
1080 if (offset)
1082 mWorkingPath.Truncate(offset+1);
1084 mWorkingPath.Append(aLeafName);
1086 return NS_OK;
1090 NS_IMETHODIMP
1091 nsLocalFile::GetNativePath(nsACString &_retval)
1093 _retval = mWorkingPath;
1094 return NS_OK;
1097 //-----------------------------------------------------------------------------
1099 // get any single extended attribute for the current file or directory
1101 nsresult
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
1110 union {
1111 GEA2LIST gea2list;
1112 char dummy[sizeof(GEA2LIST)+32];
1114 EAOP2 eaop2;
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));
1129 if (rc)
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;
1136 return NS_OK;
1140 // return an array of file types or null if there are none
1142 NS_IMETHODIMP
1143 nsLocalFile::GetFileTypes(nsIArray **_retval)
1145 NS_ENSURE_ARG(_retval);
1146 *_retval = 0;
1148 // fetch the .TYPE ea & prepare for enumeration
1149 TypeEaEnumerator typeEnum;
1150 nsresult rv = typeEnum.Init(this);
1151 if (NS_FAILED(rv))
1152 return rv;
1154 // create an array that's scriptable
1155 nsCOMPtr<nsIMutableArray> mutArray =
1156 do_CreateInstance(NS_ARRAY_CONTRACTID);
1157 NS_ENSURE_STATE(mutArray);
1159 PRInt32 cnt;
1160 PRUint32 lth;
1161 char * ptr;
1163 // get each file type, convert to a CString, then add to the array
1164 for (cnt=0, ptr=typeEnum.GetNext(&lth); ptr; ptr=typeEnum.GetNext(&lth)) {
1165 nsCOMPtr<nsISupportsCString> typeString(
1166 do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv));
1167 if (NS_SUCCEEDED(rv)) {
1168 nsCAutoString temp;
1169 temp.Assign(ptr, lth);
1170 typeString->SetData(temp);
1171 mutArray->AppendElement(typeString, PR_FALSE);
1172 cnt++;
1176 // if the array has any contents, addref & return it
1177 if (cnt) {
1178 *_retval = mutArray;
1179 NS_ADDREF(*_retval);
1180 rv = NS_OK;
1182 else
1183 rv = NS_ERROR_FAILURE;
1185 return rv;
1189 // see if the file is of the requested type
1191 NS_IMETHODIMP
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);
1200 if (NS_FAILED(rv))
1201 return rv;
1203 PRUint32 lth;
1204 char * ptr;
1206 // compare each type to the request; if there's a match, exit
1207 for (ptr = typeEnum.GetNext(&lth); ptr; ptr = typeEnum.GetNext(&lth))
1208 if (fileType.EqualsASCII(ptr, lth)) {
1209 *_retval = PR_TRUE;
1210 break;
1213 return NS_OK;
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
1220 #pragma pack(1)
1221 typedef struct _TYPEEA {
1222 struct {
1223 ULONG ulcbList;
1224 ULONG uloNextEntryOffset;
1225 BYTE bfEA;
1226 BYTE bcbName;
1227 USHORT uscbValue;
1228 char chszName[sizeof(".TYPE")];
1229 } hdr;
1230 struct {
1231 USHORT usEAType;
1232 USHORT usCodePage;
1233 USHORT usNumEntries;
1234 USHORT usDataType;
1235 USHORT usDataLth;
1236 } info;
1237 char data[1];
1238 } TYPEEA;
1240 typedef struct _TYPEEA2 {
1241 USHORT usDataType;
1242 USHORT usDataLth;
1243 } TYPEEA2;
1244 #pragma pack()
1246 // writes one or more .TYPE extended attributes taken
1247 // from a comma-delimited string
1248 NS_IMETHODIMP
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);
1259 if (!pBuf)
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;
1267 pEA->hdr.bfEA = 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);
1279 delim = begin;
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
1284 do {
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));
1291 begin = ++delim;
1292 } while (--cnt);
1294 // write the EA, then free the buffer
1295 EAOP2 eaop2;
1296 eaop2.fpGEA2List = 0;
1297 eaop2.fpFEA2List = (PFEA2LIST)pBuf;
1299 int rc = DosSetPathInfo(mWorkingPath.get(), FIL_QUERYEASIZE,
1300 &eaop2, sizeof(eaop2), 0);
1301 NS_Free(pBuf);
1303 if (rc)
1304 return ConvertOS2Error(rc);
1306 return NS_OK;
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;
1313 #pragma pack(1)
1314 typedef struct _SUBJEA {
1315 struct {
1316 ULONG ulcbList;
1317 ULONG uloNextEntryOffset;
1318 BYTE bfEA;
1319 BYTE bcbName;
1320 USHORT uscbValue;
1321 char chszName[sizeof(".SUBJECT")];
1322 } hdr;
1323 struct {
1324 USHORT usEAType;
1325 USHORT usDataLth;
1326 } info;
1327 char data[1];
1328 } SUBJEA;
1329 #pragma pack()
1331 // saves the file's source URI in its .SUBJECT extended attribute
1332 NS_IMETHODIMP
1333 nsLocalFile::SetFileSource(const nsACString& aURI)
1335 if (aURI.IsEmpty())
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);
1341 if (!pBuf)
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;
1349 pEA->hdr.bfEA = 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
1358 EAOP2 eaop2;
1359 eaop2.fpGEA2List = 0;
1360 eaop2.fpFEA2List = (PFEA2LIST)pEA;
1362 int rc = DosSetPathInfo(mWorkingPath.get(), FIL_QUERYEASIZE,
1363 &eaop2, sizeof(eaop2), 0);
1364 NS_Free(pBuf);
1366 if (rc)
1367 return ConvertOS2Error(rc);
1369 return NS_OK;
1372 //-----------------------------------------------------------------------------
1374 nsresult
1375 nsLocalFile::CopySingleFile(nsIFile *sourceFile, nsIFile *destParent,
1376 const nsACString &newName, PRBool move)
1378 nsresult rv;
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);
1392 else
1394 destPath.Append(newName);
1397 rv = sourceFile->GetNativePath(filePath);
1399 if (NS_FAILED(rv))
1400 return rv;
1402 APIRET rc = NO_ERROR;
1404 if (move)
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
1413 do {
1414 rc = DosCopy(filePath.get(), (PSZ)const_cast<char*>(destPath.get()), DCPY_EXISTING);
1415 if (rc == ERROR_TOO_MANY_OPEN_FILES) {
1416 ULONG CurMaxFH = 0;
1417 LONG ReqCount = 20;
1418 APIRET rc2;
1419 rc2 = DosSetRelMaxFH(&ReqCount, &CurMaxFH);
1420 if (rc2 != NO_ERROR)
1421 break;
1423 } while (rc == ERROR_TOO_MANY_OPEN_FILES);
1425 // WSOD2 HACK - NETWORK_ACCESS_DENIED
1426 if (rc == 65)
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';
1439 DosExecPgm(NULL, 0,
1440 EXEC_SYNC, achProgram, (PSZ)NULL,
1441 &rescResults, achProgram);
1442 rc = 0; // Assume it worked
1444 } // rc == 65
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());
1451 } // !move or ERROR
1453 if (rc)
1454 rv = ConvertOS2Error(rc);
1456 return rv;
1460 nsresult
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();
1469 if (NS_FAILED(rv))
1470 return rv;
1472 if (!newParentDir)
1474 // no parent was specified. We must rename.
1476 if (newName.IsEmpty())
1477 return NS_ERROR_INVALID_ARG;
1479 rv = GetParent(getter_AddRefs(newParentDir));
1480 if (NS_FAILED(rv))
1481 return rv;
1484 if (!newParentDir)
1485 return NS_ERROR_FILE_DESTINATION_NOT_DIR;
1487 // make sure it exists and is a directory. Create it if not there.
1488 PRBool exists;
1489 newParentDir->Exists(&exists);
1490 if (!exists)
1492 rv = newParentDir->Create(DIRECTORY_TYPE, 0644); // TODO, what permissions should we use
1493 if (NS_FAILED(rv))
1494 return rv;
1496 else
1498 PRBool isDir;
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;
1508 PRBool isDir;
1509 IsDirectory(&isDir);
1511 // Try to move the file or directory, or try to copy a single file
1512 if (move || !isDir)
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))
1521 return rv;
1524 // Not able to copy or move directly, so enumerate it
1525 if (!done)
1527 // create a new target destination in the new parentDir;
1528 nsCOMPtr<nsIFile> target;
1529 rv = newParentDir->Clone(getter_AddRefs(target));
1531 if (NS_FAILED(rv))
1532 return rv;
1534 nsCAutoString allocatedNewName;
1535 if (newName.IsEmpty())
1537 GetNativeLeafName(allocatedNewName);
1539 else
1541 allocatedNewName = newName;
1544 rv = target->AppendNative(allocatedNewName);
1545 if (NS_FAILED(rv))
1546 return rv;
1548 allocatedNewName.Truncate();
1550 // check if the destination directory already exists
1551 target->Exists(&exists);
1552 if (!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
1556 if (NS_FAILED(rv))
1557 return rv;
1559 else
1561 // check if the destination directory is writable and empty
1562 PRBool isWritable;
1564 target->IsWritable(&isWritable);
1565 if (!isWritable)
1566 return NS_ERROR_FILE_ACCESS_DENIED;
1568 nsCOMPtr<nsISimpleEnumerator> targetIterator;
1569 rv = target->GetDirectoryEntries(getter_AddRefs(targetIterator));
1571 PRBool more;
1572 targetIterator->HasMoreElements(&more);
1573 // return error if target directory is not empty
1574 if (more)
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");
1583 return rv;
1586 PRBool more;
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);
1593 if (file)
1595 if (move)
1597 rv = file->MoveToNative(target, EmptyCString());
1598 NS_ENSURE_SUCCESS(rv,rv);
1600 else
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.
1613 if (move)
1615 rv = Remove(PR_FALSE); // recursive
1616 NS_ENSURE_SUCCESS(rv,rv);
1621 // If we moved, we want to adjust this.
1622 if (move)
1624 MakeDirty();
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);
1640 else
1642 InitWithNativePath(newParentPath);
1643 AppendNative(newName);
1647 return NS_OK;
1650 NS_IMETHODIMP
1651 nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString &newName)
1653 return CopyMove(newParentDir, newName, PR_FALSE);
1656 NS_IMETHODIMP
1657 nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString &newName)
1659 return CopyMove(newParentDir, newName, PR_FALSE);
1662 NS_IMETHODIMP
1663 nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString &newName)
1665 return CopyMove(newParentDir, newName, PR_TRUE);
1668 NS_IMETHODIMP
1669 nsLocalFile::Load(PRLibrary * *_retval)
1671 // Check we are correctly initialized.
1672 CHECK_mWorkingPath();
1674 PRBool isFile;
1675 nsresult rv = IsFile(&isFile);
1677 if (NS_FAILED(rv))
1678 return rv;
1680 if (!isFile)
1681 return NS_ERROR_FILE_IS_DIRECTORY;
1683 #ifdef NS_BUILD_REFCNT_LOGGING
1684 nsTraceRefcntImpl::SetActivityIsLegal(PR_FALSE);
1685 #endif
1687 *_retval = PR_LoadLibrary(mWorkingPath.get());
1689 #ifdef NS_BUILD_REFCNT_LOGGING
1690 nsTraceRefcntImpl::SetActivityIsLegal(PR_TRUE);
1691 #endif
1693 if (*_retval)
1694 return NS_OK;
1696 return NS_ERROR_NULL_POINTER;
1699 NS_IMETHODIMP
1700 nsLocalFile::Remove(PRBool recursive)
1702 // Check we are correctly initialized.
1703 CHECK_mWorkingPath();
1705 PRBool isDir = PR_FALSE;
1707 nsresult rv = IsDirectory(&isDir);
1708 if (NS_FAILED(rv))
1709 return rv;
1711 if (isDir)
1713 if (recursive)
1715 nsDirEnumerator dirEnum;
1717 rv = dirEnum.Init(this);
1718 if (NS_FAILED(rv))
1719 return rv;
1721 PRBool more;
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);
1727 if (file)
1728 file->Remove(recursive);
1731 rv = rmdir(mWorkingPath.get());
1733 else
1735 rv = remove(mWorkingPath.get());
1738 // fixup error code if necessary...
1739 if (rv == (nsresult)-1)
1740 rv = NSRESULT_FOR_ERRNO();
1742 MakeDirty();
1743 return rv;
1746 NS_IMETHODIMP
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();
1756 if (NS_FAILED(rv))
1757 return rv;
1759 // microseconds -> milliseconds
1760 PRInt64 usecPerMsec;
1761 LL_I2L(usecPerMsec, PR_USEC_PER_MSEC);
1762 LL_DIV(*aLastModifiedTime, mFileInfo64.modifyTime, usecPerMsec);
1763 return NS_OK;
1767 NS_IMETHODIMP
1768 nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModifiedTime)
1770 return NS_ERROR_NOT_IMPLEMENTED;
1774 NS_IMETHODIMP
1775 nsLocalFile::SetLastModifiedTime(PRInt64 aLastModifiedTime)
1777 // Check we are correctly initialized.
1778 CHECK_mWorkingPath();
1780 return nsLocalFile::SetModDate(aLastModifiedTime);
1784 NS_IMETHODIMP
1785 nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModifiedTime)
1787 return NS_ERROR_NOT_IMPLEMENTED;
1790 nsresult
1791 nsLocalFile::SetModDate(PRInt64 aLastModifiedTime)
1793 nsresult rv = Stat();
1795 if (NS_FAILED(rv))
1796 return rv;
1798 PRExplodedTime pret;
1799 FILESTATUS3 pathInfo;
1801 // Level 1 info
1802 rv = DosQueryPathInfo(mWorkingPath.get(), FIL_STANDARD,
1803 &pathInfo, sizeof(pathInfo));
1805 if (NS_FAILED(rv))
1807 rv = ConvertOS2Error(rv);
1808 return 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;
1817 else
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);
1830 if (NS_FAILED(rv))
1831 return rv;
1833 MakeDirty();
1834 return rv;
1837 NS_IMETHODIMP
1838 nsLocalFile::GetPermissions(PRUint32 *aPermissions)
1840 NS_ENSURE_ARG(aPermissions);
1842 nsresult rv = Stat();
1843 if (NS_FAILED(rv))
1844 return rv;
1846 PRBool isWritable, isExecutable;
1847 IsWritable(&isWritable);
1848 IsExecutable(&isExecutable);
1850 *aPermissions = PR_IRUSR|PR_IRGRP|PR_IROTH; // all read
1851 if (isWritable)
1852 *aPermissions |= PR_IWUSR|PR_IWGRP|PR_IWOTH; // all write
1853 if (isExecutable)
1854 *aPermissions |= PR_IXUSR|PR_IXGRP|PR_IXOTH; // all execute
1856 return NS_OK;
1859 NS_IMETHODIMP
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()
1869 NS_IMETHODIMP
1870 nsLocalFile::SetPermissions(PRUint32 aPermissions)
1872 // Check we are correctly initialized.
1873 CHECK_mWorkingPath();
1875 nsresult rv = Stat();
1876 if (NS_FAILED(rv))
1877 return rv;
1879 APIRET rc;
1880 FILESTATUS3 pathInfo;
1882 // Level 1 info
1883 rc = DosQueryPathInfo(mWorkingPath.get(), FIL_STANDARD,
1884 &pathInfo, sizeof(pathInfo));
1886 if (rc != NO_ERROR)
1887 return ConvertOS2Error(rc);
1889 ULONG attr = 0;
1890 if (!(aPermissions & (PR_IWUSR|PR_IWGRP|PR_IWOTH)))
1891 attr = FILE_READONLY;
1893 if (attr == (pathInfo.attrFile & FILE_READONLY))
1894 return NS_OK;
1896 pathInfo.attrFile ^= FILE_READONLY;
1898 rc = DosSetPathInfo(mWorkingPath.get(), FIL_STANDARD,
1899 &pathInfo, sizeof(pathInfo), 0UL);
1901 if (rc != NO_ERROR)
1902 return ConvertOS2Error(rc);
1904 return NS_OK;
1907 NS_IMETHODIMP
1908 nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions)
1910 return NS_ERROR_NOT_IMPLEMENTED;
1914 NS_IMETHODIMP
1915 nsLocalFile::GetFileSize(PRInt64 *aFileSize)
1917 // Check we are correctly initialized.
1918 CHECK_mWorkingPath();
1920 NS_ENSURE_ARG(aFileSize);
1921 *aFileSize = 0;
1923 nsresult rv = Stat();
1924 if (NS_FAILED(rv))
1925 return rv;
1927 *aFileSize = mFileInfo64.size;
1928 return NS_OK;
1932 NS_IMETHODIMP
1933 nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSize)
1935 return NS_ERROR_NOT_IMPLEMENTED;
1939 NS_IMETHODIMP
1940 nsLocalFile::SetFileSize(PRInt64 aFileSize)
1942 nsresult rv = Stat();
1943 if (NS_FAILED(rv))
1944 return rv;
1946 APIRET rc;
1947 HFILE hFile;
1948 ULONG actionTaken;
1950 rc = DosOpen(mWorkingPath.get(),
1951 &hFile,
1952 &actionTaken,
1954 FILE_NORMAL,
1955 OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
1956 OPEN_SHARE_DENYREADWRITE | OPEN_ACCESS_READWRITE,
1957 NULL);
1959 if (rc != NO_ERROR)
1961 MakeDirty();
1962 return NS_ERROR_FAILURE;
1965 // Seek to new, desired end of file
1966 PRInt32 hi, lo;
1967 myLL_L2II(aFileSize, &hi, &lo );
1969 rc = DosSetFileSize(hFile, lo);
1970 DosClose(hFile);
1971 MakeDirty();
1973 if (rc != NO_ERROR)
1974 return NS_ERROR_FAILURE;
1976 return NS_OK;
1979 NS_IMETHODIMP
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();
1989 if (NS_FAILED(rv))
1990 return rv;
1992 ULONG ulDriveNo = toupper(mWorkingPath.CharAt(0)) + 1 - 'A';
1993 FSALLOCATE fsAllocate;
1994 APIRET rc = DosQueryFSInfo(ulDriveNo,
1995 FSIL_ALLOC,
1996 &fsAllocate,
1997 sizeof(fsAllocate));
1999 if (rc != NO_ERROR)
2000 return NS_ERROR_FAILURE;
2002 *aDiskSpaceAvailable = fsAllocate.cUnitAvail;
2003 *aDiskSpaceAvailable *= fsAllocate.cSectorUnit;
2004 *aDiskSpaceAvailable *= fsAllocate.cbSector;
2006 return NS_OK;
2009 NS_IMETHODIMP
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.
2026 if (offset < 0)
2027 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
2029 if (offset == 1 && parentPath[0] == '\\') {
2030 aParent = nsnull;
2031 return NS_OK;
2033 if (offset > 0)
2034 parentPath.Truncate(offset);
2035 else
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);
2045 return rv;
2048 NS_IMETHODIMP
2049 nsLocalFile::Exists(PRBool *_retval)
2051 // Check we are correctly initialized.
2052 CHECK_mWorkingPath();
2054 NS_ENSURE_ARG(_retval);
2055 *_retval = PR_FALSE;
2057 MakeDirty();
2058 nsresult rv = Stat();
2060 *_retval = NS_SUCCEEDED(rv);
2061 return NS_OK;
2064 NS_IMETHODIMP
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();
2074 if (NS_FAILED(rv))
2075 return rv;
2077 APIRET rc;
2078 FILESTATUS3 pathInfo;
2080 // Level 1 info
2081 rc = DosQueryPathInfo(mWorkingPath.get(), FIL_STANDARD,
2082 &pathInfo, sizeof(pathInfo));
2084 if (rc != NO_ERROR)
2086 rc = ConvertOS2Error(rc);
2087 return 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);
2093 return NS_OK;
2096 NS_IMETHODIMP
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();
2106 if (NS_FAILED(rv))
2107 return rv;
2109 *_retval = PR_TRUE;
2110 return NS_OK;
2114 NS_IMETHODIMP
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();
2124 if (NS_FAILED(rv))
2125 return rv;
2127 // no need to bother if this isn't a file
2128 PRBool isFile;
2129 rv = IsFile(&isFile);
2130 if (NS_FAILED(rv) || !isFile)
2131 return rv;
2133 nsCAutoString path;
2134 GetNativeTarget(path);
2136 // get the filename, including the leading backslash
2137 const char* leaf = (const char*) _mbsrchr((const unsigned char*)path.get(), '\\');
2138 if (!leaf)
2139 return NS_OK;
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 != '.')
2148 break;
2149 *pathEnd = 0;
2150 pathEnd = WinPrevChar(0, 0, 0, leaf, pathEnd);
2153 if (pathEnd == leaf)
2154 return NS_OK;
2156 // get the extension, including the dot
2157 char* ext = (char*) _mbsrchr((const unsigned char*)leaf, '.');
2158 if (!ext)
2159 return NS_OK;
2161 if (stricmp(ext, ".exe") == 0 ||
2162 stricmp(ext, ".cmd") == 0 ||
2163 stricmp(ext, ".com") == 0 ||
2164 stricmp(ext, ".bat") == 0)
2165 *_retval = PR_TRUE;
2167 return NS_OK;
2171 NS_IMETHODIMP
2172 nsLocalFile::IsDirectory(PRBool *_retval)
2174 NS_ENSURE_ARG(_retval);
2175 *_retval = PR_FALSE;
2177 nsresult rv = Stat();
2178 if (NS_FAILED(rv))
2179 return rv;
2181 *_retval = (mFileInfo64.type == PR_FILE_DIRECTORY);
2182 return NS_OK;
2185 NS_IMETHODIMP
2186 nsLocalFile::IsFile(PRBool *_retval)
2188 NS_ENSURE_ARG(_retval);
2189 *_retval = PR_FALSE;
2191 nsresult rv = Stat();
2192 if (NS_FAILED(rv))
2193 return rv;
2195 *_retval = (mFileInfo64.type == PR_FILE_FILE);
2196 return rv;
2199 NS_IMETHODIMP
2200 nsLocalFile::IsHidden(PRBool *_retval)
2202 NS_ENSURE_ARG(_retval);
2203 *_retval = PR_FALSE;
2205 nsresult rv = Stat();
2206 if (NS_FAILED(rv))
2207 return rv;
2209 APIRET rc;
2210 FILESTATUS3 pathInfo;
2212 // Level 1 info
2213 rc = DosQueryPathInfo(mWorkingPath.get(), FIL_STANDARD,
2214 &pathInfo, sizeof(pathInfo));
2216 if (rc != NO_ERROR)
2218 rc = ConvertOS2Error(rc);
2219 return rc;
2222 *_retval = ((pathInfo.attrFile & FILE_HIDDEN) != 0);
2223 return NS_OK;
2227 NS_IMETHODIMP
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;
2237 return NS_OK;
2240 NS_IMETHODIMP
2241 nsLocalFile::IsSpecial(PRBool *_retval)
2243 NS_ENSURE_ARG(_retval);
2245 // when implemented, IsSpecial will be used for WPS objects
2246 *_retval = PR_FALSE;
2247 return NS_OK;
2250 NS_IMETHODIMP
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);
2260 return NS_OK;
2263 NS_IMETHODIMP
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
2284 // separator.
2286 if (inFilePath[myFilePathLen] == '\\')
2288 *_retval = PR_TRUE;
2293 return NS_OK;
2296 NS_IMETHODIMP
2297 nsLocalFile::GetNativeTarget(nsACString &_retval)
2299 // Check we are correctly initialized.
2300 CHECK_mWorkingPath();
2302 _retval = mWorkingPath;
2303 return NS_OK;
2306 NS_IMETHODIMP
2307 nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
2309 NS_ENSURE_ARG(aFollowLinks);
2310 *aFollowLinks = PR_FALSE;
2311 return NS_OK;
2314 NS_IMETHODIMP
2315 nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
2317 return NS_OK;
2320 NS_IMETHODIMP
2321 nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator * *entries)
2323 NS_ENSURE_ARG(entries);
2324 nsresult rv;
2325 *entries = nsnull;
2327 if (mWorkingPath.EqualsLiteral("\\\\.")) {
2328 nsDriveEnumerator *drives = new nsDriveEnumerator;
2329 if (!drives)
2330 return NS_ERROR_OUT_OF_MEMORY;
2331 NS_ADDREF(drives);
2332 rv = drives->Init();
2333 if (NS_FAILED(rv)) {
2334 NS_RELEASE(drives);
2335 return rv;
2337 *entries = drives;
2338 return NS_OK;
2341 PRBool isDir;
2342 rv = IsDirectory(&isDir);
2343 if (NS_FAILED(rv))
2344 return rv;
2345 if (!isDir)
2346 return NS_ERROR_FILE_NOT_DIRECTORY;
2348 nsDirEnumerator* dirEnum = new nsDirEnumerator();
2349 if (dirEnum == nsnull)
2350 return NS_ERROR_OUT_OF_MEMORY;
2351 NS_ADDREF(dirEnum);
2352 rv = dirEnum->Init(this);
2353 if (NS_FAILED(rv))
2355 NS_RELEASE(dirEnum);
2356 return rv;
2359 *entries = dirEnum;
2360 return NS_OK;
2363 NS_IMETHODIMP
2364 nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
2366 return GetNativePath(aPersistentDescriptor);
2369 NS_IMETHODIMP
2370 nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
2372 return InitWithNativePath(aPersistentDescriptor);
2375 #ifndef OPEN_DEFAULT
2376 #define OPEN_DEFAULT 0
2377 #define OPEN_CONTENTS 1
2378 #endif
2380 NS_IMETHODIMP
2381 nsLocalFile::Reveal()
2383 PRBool isDirectory = PR_FALSE;
2384 nsCAutoString path;
2386 IsDirectory(&isDirectory);
2387 if (isDirectory)
2389 GetNativePath(path);
2391 else
2393 nsCOMPtr<nsIFile> parent;
2394 GetParent(getter_AddRefs(parent));
2395 if (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.
2404 return NS_OK;
2408 NS_IMETHODIMP
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.
2416 return NS_OK;
2419 nsresult
2420 NS_NewNativeLocalFile(const nsACString &path, PRBool followLinks, nsILocalFile* *result)
2422 nsLocalFile* file = new nsLocalFile();
2423 if (file == nsnull)
2424 return NS_ERROR_OUT_OF_MEMORY;
2425 NS_ADDREF(file);
2427 if (!path.IsEmpty()) {
2428 nsresult rv = file->InitWithNativePath(path);
2429 if (NS_FAILED(rv)) {
2430 NS_RELEASE(file);
2431 return rv;
2435 *result = file;
2436 return NS_OK;
2439 //-----------------------------------------------------------------------------
2440 // UCS2 interface
2441 //-----------------------------------------------------------------------------
2443 NS_IMETHODIMP
2444 nsLocalFile::InitWithPath(const nsAString &filePath)
2446 if (filePath.IsEmpty())
2447 return InitWithNativePath(EmptyCString());
2449 nsCAutoString tmp;
2450 nsresult rv = NS_CopyUnicodeToNative(filePath, tmp);
2451 if (NS_SUCCEEDED(rv))
2452 return InitWithNativePath(tmp);
2454 return rv;
2457 NS_IMETHODIMP
2458 nsLocalFile::Append(const nsAString &node)
2460 if (node.IsEmpty())
2461 return NS_OK;
2463 nsCAutoString tmp;
2464 nsresult rv = NS_CopyUnicodeToNative(node, tmp);
2465 if (NS_SUCCEEDED(rv))
2466 return AppendNative(tmp);
2468 return rv;
2471 NS_IMETHODIMP
2472 nsLocalFile::AppendRelativePath(const nsAString &node)
2474 if (node.IsEmpty())
2475 return NS_OK;
2477 nsCAutoString tmp;
2478 nsresult rv = NS_CopyUnicodeToNative(node, tmp);
2479 if (NS_SUCCEEDED(rv))
2480 return AppendRelativeNativePath(tmp);
2482 return rv;
2485 NS_IMETHODIMP
2486 nsLocalFile::GetLeafName(nsAString &aLeafName)
2488 nsCAutoString tmp;
2489 nsresult rv = GetNativeLeafName(tmp);
2490 if (NS_SUCCEEDED(rv))
2491 rv = NS_CopyNativeToUnicode(tmp, aLeafName);
2493 return rv;
2496 NS_IMETHODIMP
2497 nsLocalFile::SetLeafName(const nsAString &aLeafName)
2499 if (aLeafName.IsEmpty())
2500 return SetNativeLeafName(EmptyCString());
2502 nsCAutoString tmp;
2503 nsresult rv = NS_CopyUnicodeToNative(aLeafName, tmp);
2504 if (NS_SUCCEEDED(rv))
2505 return SetNativeLeafName(tmp);
2507 return rv;
2510 NS_IMETHODIMP
2511 nsLocalFile::GetPath(nsAString &_retval)
2513 return NS_CopyNativeToUnicode(mWorkingPath, _retval);
2516 NS_IMETHODIMP
2517 nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
2519 if (newName.IsEmpty())
2520 return CopyToNative(newParentDir, EmptyCString());
2522 nsCAutoString tmp;
2523 nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
2524 if (NS_SUCCEEDED(rv))
2525 return CopyToNative(newParentDir, tmp);
2527 return rv;
2530 NS_IMETHODIMP
2531 nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
2533 if (newName.IsEmpty())
2534 return CopyToFollowingLinksNative(newParentDir, EmptyCString());
2536 nsCAutoString tmp;
2537 nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
2538 if (NS_SUCCEEDED(rv))
2539 return CopyToFollowingLinksNative(newParentDir, tmp);
2541 return rv;
2544 NS_IMETHODIMP
2545 nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
2547 if (newName.IsEmpty())
2548 return MoveToNative(newParentDir, EmptyCString());
2550 nsCAutoString tmp;
2551 nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
2552 if (NS_SUCCEEDED(rv))
2553 return MoveToNative(newParentDir, tmp);
2555 return rv;
2558 NS_IMETHODIMP
2559 nsLocalFile::GetTarget(nsAString &_retval)
2561 nsCAutoString tmp;
2562 nsresult rv = GetNativeTarget(tmp);
2563 if (NS_SUCCEEDED(rv))
2564 rv = NS_CopyNativeToUnicode(tmp, _retval);
2566 return rv;
2569 // nsIHashable
2571 NS_IMETHODIMP
2572 nsLocalFile::Equals(nsIHashable* aOther, PRBool *aResult)
2574 nsCOMPtr<nsIFile> otherfile(do_QueryInterface(aOther));
2575 if (!otherfile) {
2576 *aResult = PR_FALSE;
2577 return NS_OK;
2580 return Equals(otherfile, aResult);
2583 NS_IMETHODIMP
2584 nsLocalFile::GetHashCode(PRUint32 *aResult)
2586 *aResult = nsCRT::HashCode(mWorkingPath.get());
2587 return NS_OK;
2590 nsresult
2591 NS_NewLocalFile(const nsAString &path, PRBool followLinks, nsILocalFile* *result)
2593 nsCAutoString buf;
2594 nsresult rv = NS_CopyUnicodeToNative(path, buf);
2595 if (NS_FAILED(rv)) {
2596 *result = nsnull;
2597 return rv;
2599 return NS_NewNativeLocalFile(buf, followLinks, result);
2602 //-----------------------------------------------------------------------------
2603 // nsLocalFile <static members>
2604 //-----------------------------------------------------------------------------
2606 void
2607 nsLocalFile::GlobalInit()
2611 void
2612 nsLocalFile::GlobalShutdown()
2616 //-----------------------------------------------------------------------------