Back to index

lightning-sunbird  0.9+nobinonly
nsLocalFileOS2.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code, released
00016  * March 31, 1998.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Henry Sobotka <sobotka@axess.com>
00025  *   IBM Corp.
00026  *   Rich Walsh <dragtext@e-vertise.com>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either of the GNU General Public License Version 2 or later (the "GPL"),
00030  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 // N.B. the ns* & pr* headers below will #include all
00043 // of the standard library headers this file requires
00044 
00045 #include "nsCOMPtr.h"
00046 #include "nsMemory.h"
00047 
00048 #include "nsLocalFile.h"
00049 #include "nsNativeCharsetUtils.h"
00050 
00051 #include "nsISimpleEnumerator.h"
00052 #include "nsIDirectoryEnumerator.h"
00053 #include "nsIComponentManager.h"
00054 #include "prtypes.h"
00055 #include "prio.h"
00056 
00057 #include "nsReadableUtils.h"
00058 #include "nsISupportsPrimitives.h"
00059 #include "nsArray.h"
00060 
00061 //-----------------------------------------------------------------------------
00062 // static helper functions
00063 //-----------------------------------------------------------------------------
00064 
00065 static nsresult ConvertOS2Error(int err)
00066 {
00067     nsresult rv;
00068 
00069     switch (err)
00070     {
00071         case ERROR_FILE_NOT_FOUND:
00072         case ERROR_PATH_NOT_FOUND:
00073         case ERROR_INVALID_DRIVE:
00074             rv = NS_ERROR_FILE_NOT_FOUND;
00075             break;
00076         case ERROR_ACCESS_DENIED:
00077         case ERROR_NOT_SAME_DEVICE:
00078             rv = NS_ERROR_FILE_ACCESS_DENIED;
00079             break;
00080         case ERROR_NOT_ENOUGH_MEMORY:
00081         case ERROR_INVALID_BLOCK:
00082         case ERROR_INVALID_HANDLE:
00083         case ERROR_ARENA_TRASHED:
00084             rv = NS_ERROR_OUT_OF_MEMORY;
00085             break;
00086         case ERROR_CURRENT_DIRECTORY:
00087             rv = NS_ERROR_FILE_DIR_NOT_EMPTY;
00088             break;
00089         case ERROR_WRITE_PROTECT:
00090             rv = NS_ERROR_FILE_READ_ONLY;
00091             break;
00092         case ERROR_HANDLE_DISK_FULL:
00093             rv = NS_ERROR_FILE_TOO_BIG;
00094             break;
00095         case ERROR_FILE_EXISTS:
00096         case ERROR_ALREADY_EXISTS:
00097         case ERROR_CANNOT_MAKE:
00098             rv = NS_ERROR_FILE_ALREADY_EXISTS;
00099             break;
00100         case ERROR_FILENAME_EXCED_RANGE:
00101             rv = NS_ERROR_FILE_NAME_TOO_LONG;
00102             break;
00103         case 0:
00104             rv = NS_OK;
00105         default:
00106             rv = NS_ERROR_FAILURE;
00107     }
00108     return rv;
00109 }
00110 
00111 static void
00112 myLL_L2II(PRInt64 result, PRInt32 *hi, PRInt32 *lo )
00113 {
00114     PRInt64 a64, b64;  // probably could have been done with
00115                        // only one PRInt64, but these are macros,
00116                        // and I am a wimp.
00117 
00118     // shift the hi word to the low word, then push it into a long.
00119     LL_SHR(a64, result, 32);
00120     LL_L2I(*hi, a64);
00121 
00122     // shift the low word to the hi word first, then shift it back.
00123     LL_SHL(b64, result, 32);
00124     LL_SHR(a64, b64, 32);
00125     LL_L2I(*lo, a64);
00126 }
00127 
00128 // Locates the first occurrence of charToSearchFor in the stringToSearch
00129 static unsigned char* PR_CALLBACK
00130 _mbschr(const unsigned char* stringToSearch, int charToSearchFor)
00131 {
00132     const unsigned char* p = stringToSearch;
00133 
00134     do {
00135         if (*p == charToSearchFor)
00136             break;
00137         p  = (const unsigned char*)WinNextChar(0,0,0,(char*)p);
00138     } while (*p);
00139 
00140     // Result is p or NULL
00141     return *p ? (unsigned char*)p : NULL;
00142 }
00143 
00144 // Locates the first occurrence of subString in the stringToSearch
00145 static unsigned char* PR_CALLBACK
00146 _mbsstr(const unsigned char* stringToSearch, const unsigned char* subString)
00147 {
00148     const unsigned char* pStr = stringToSearch;
00149     const unsigned char* pSub = subString;
00150 
00151     do {
00152         while (*pStr && *pStr != *pSub)
00153             pStr = (const unsigned char*)WinNextChar(0,0,0,(char*)pStr);
00154 
00155         if (!*pStr)
00156             break;
00157 
00158         const unsigned char* pNxt = pStr;
00159         do {
00160             pSub = (const unsigned char*)WinNextChar(0,0,0,(char*)pSub);
00161             pNxt = (const unsigned char*)WinNextChar(0,0,0,(char*)pNxt);
00162         } while (*pSub && *pSub == *pNxt);
00163 
00164         if (!*pSub)
00165             break;
00166 
00167         pSub = subString;
00168         pStr = (const unsigned char*)WinNextChar(0,0,0,(char*)pStr);
00169 
00170     } while (*pStr);
00171 
00172     // if we got to the end of pSub, we've found it
00173     return *pSub ? NULL : (unsigned char*)pStr;
00174 }
00175 
00176 // Locates last occurence of charToSearchFor in the stringToSearch
00177 extern unsigned char*
00178 _mbsrchr(const unsigned char* stringToSearch, int charToSearchFor)
00179 {
00180     int length = strlen((const char*)stringToSearch);
00181     const unsigned char* p = stringToSearch+length;
00182 
00183     do {
00184         if (*p == charToSearchFor)
00185             break;
00186         p  = (const unsigned char*)WinPrevChar(0,0,0,(char*)stringToSearch,(char*)p);
00187     } while (p > stringToSearch);
00188 
00189     // Result is p or NULL
00190     return (*p == charToSearchFor) ? (unsigned char*)p : NULL;
00191 }
00192 
00193 // Implement equivalent of Win32 CreateDirectoryA
00194 static nsresult PR_CALLBACK
00195 CreateDirectoryA(PSZ path, PEAOP2 ppEABuf)
00196 {
00197     APIRET rc;
00198     nsresult rv;
00199     FILESTATUS3 pathInfo;
00200 
00201     rc = DosCreateDir(path, ppEABuf);
00202     if (rc != NO_ERROR)
00203     {
00204         rv = ConvertOS2Error(rc);
00205 
00206         // Check if directory already exists and if so,
00207         // reflect that in the return value
00208         rc = DosQueryPathInfo(path, FIL_STANDARD,
00209                               &pathInfo, sizeof(pathInfo));
00210         if (rc == NO_ERROR)
00211             rv = ERROR_FILE_EXISTS;
00212     }
00213     else
00214         rv = rc;
00215 
00216     return rv;
00217 }
00218 
00219 static int isleadbyte(int c)
00220 {
00221     static BOOL bDBCSFilled = FALSE;
00222     // According to the Control Program Guide&Ref, 12 bytes is sufficient
00223     static BYTE DBCSInfo[12] = { 0 };
00224     BYTE *curr;
00225     BOOL retval = FALSE;
00226 
00227     if(!bDBCSFilled)
00228     {
00229         COUNTRYCODE ctrycodeInfo = { 0 };
00230         APIRET rc = NO_ERROR;
00231         ctrycodeInfo.country = 0;     // Current Country
00232         ctrycodeInfo.codepage = 0;    // Current Codepage
00233 
00234         rc = DosQueryDBCSEnv(sizeof(DBCSInfo), &ctrycodeInfo, DBCSInfo);
00235         // we had an error, do something?
00236         if (rc != NO_ERROR)
00237             return FALSE;
00238 
00239         bDBCSFilled=TRUE;
00240     }
00241 
00242     // DBCSInfo returned by DosQueryDBCSEnv is terminated
00243     // with two '0' bytes in a row
00244     curr = DBCSInfo;
00245     while(*curr != 0 && *(curr+1) != 0)
00246     {
00247         if(c >= *curr && c <= *(curr+1))
00248         {
00249             retval=TRUE;
00250             break;
00251         }
00252         curr+=2;
00253     }
00254 
00255     return retval;
00256 }
00257 
00258 //-----------------------------------------------------------------------------
00259 // nsDirEnumerator
00260 //-----------------------------------------------------------------------------
00261 
00262 class nsDirEnumerator : public nsISimpleEnumerator,
00263                         public nsIDirectoryEnumerator
00264 {
00265     public:
00266 
00267         NS_DECL_ISUPPORTS
00268 
00269         nsDirEnumerator() : mDir(nsnull)
00270         {
00271         }
00272 
00273         nsresult Init(nsILocalFile* parent)
00274         {
00275             nsCAutoString filepath;
00276             parent->GetNativeTarget(filepath);
00277 
00278             if (filepath.IsEmpty())
00279             {
00280                 parent->GetNativePath(filepath);
00281             }
00282 
00283             if (filepath.IsEmpty())
00284             {
00285                 return NS_ERROR_UNEXPECTED;
00286             }
00287 
00288             mDir = PR_OpenDir(filepath.get());
00289             if (mDir == nsnull)    // not a directory?
00290                 return NS_ERROR_FAILURE;
00291 
00292             mParent = parent;
00293             return NS_OK;
00294         }
00295 
00296         NS_IMETHOD HasMoreElements(PRBool *result)
00297         {
00298             nsresult rv;
00299             if (mNext == nsnull && mDir)
00300             {
00301                 PRDirEntry* entry = PR_ReadDir(mDir, PR_SKIP_BOTH);
00302                 if (entry == nsnull)
00303                 {
00304                     // end of dir entries
00305 
00306                     PRStatus status = PR_CloseDir(mDir);
00307                     if (status != PR_SUCCESS)
00308                         return NS_ERROR_FAILURE;
00309                     mDir = nsnull;
00310 
00311                     *result = PR_FALSE;
00312                     return NS_OK;
00313                 }
00314 
00315                 nsCOMPtr<nsIFile> file;
00316                 rv = mParent->Clone(getter_AddRefs(file));
00317                 if (NS_FAILED(rv))
00318                     return rv;
00319 
00320                 rv = file->AppendNative(nsDependentCString(entry->name));
00321                 if (NS_FAILED(rv))
00322                     return rv;
00323 
00324                 // make sure the thing exists.  If it does, try the next one.
00325                 PRBool exists;
00326                 rv = file->Exists(&exists);
00327                 if (NS_FAILED(rv) || !exists)
00328                 {
00329                     return HasMoreElements(result);
00330                 }
00331 
00332                 mNext = do_QueryInterface(file);
00333             }
00334             *result = mNext != nsnull;
00335             if (!*result)
00336                 Close();
00337             return NS_OK;
00338         }
00339 
00340         NS_IMETHOD GetNext(nsISupports **result)
00341         {
00342             nsresult rv;
00343             PRBool hasMore;
00344             rv = HasMoreElements(&hasMore);
00345             if (NS_FAILED(rv)) return rv;
00346 
00347             *result = mNext;        // might return nsnull
00348             NS_IF_ADDREF(*result);
00349 
00350             mNext = nsnull;
00351             return NS_OK;
00352         }
00353 
00354         NS_IMETHOD GetNextFile(nsIFile **result)
00355         {
00356             *result = nsnull;
00357             PRBool hasMore = PR_FALSE;
00358             nsresult rv = HasMoreElements(&hasMore);
00359             if (NS_FAILED(rv) || !hasMore)
00360                 return rv;
00361             *result = mNext;
00362             NS_IF_ADDREF(*result);
00363             mNext = nsnull;
00364             return NS_OK;
00365         }
00366 
00367         NS_IMETHOD Close()
00368         {
00369             if (mDir)
00370             {
00371                 PRStatus status = PR_CloseDir(mDir);
00372                 NS_ASSERTION(status == PR_SUCCESS, "close failed");
00373                 if (status != PR_SUCCESS)
00374                     return NS_ERROR_FAILURE;
00375                 mDir = nsnull;
00376             }
00377             return NS_OK;
00378         }
00379 
00380         // dtor can be non-virtual since there are no subclasses, but must be
00381         // public to use the class on the stack.
00382         ~nsDirEnumerator()
00383         {
00384             Close();
00385         }
00386 
00387     protected:
00388         PRDir*                  mDir;
00389         nsCOMPtr<nsILocalFile>  mParent;
00390         nsCOMPtr<nsILocalFile>  mNext;
00391 };
00392 
00393 NS_IMPL_ISUPPORTS2(nsDirEnumerator, nsISimpleEnumerator, nsIDirectoryEnumerator)
00394 
00395 //-----------------------------------------------------------------------------
00396 // nsDriveEnumerator
00397 //-----------------------------------------------------------------------------
00398 
00399 class nsDriveEnumerator : public nsISimpleEnumerator
00400 {
00401 public:
00402     nsDriveEnumerator();
00403     virtual ~nsDriveEnumerator();
00404     NS_DECL_ISUPPORTS
00405     NS_DECL_NSISIMPLEENUMERATOR
00406     nsresult Init();
00407 
00408 private:
00409     // mDrives is a bitmap representing the available drives
00410     // mLetter is incremented each time mDrives is shifted rightward
00411     PRUint32    mDrives;
00412     PRUint8     mLetter;
00413 };
00414 
00415 NS_IMPL_ISUPPORTS1(nsDriveEnumerator, nsISimpleEnumerator)
00416 
00417 nsDriveEnumerator::nsDriveEnumerator()
00418  : mDrives(0), mLetter(0)
00419 {
00420 }
00421 
00422 nsDriveEnumerator::~nsDriveEnumerator()
00423 {
00424 }
00425 
00426 nsresult nsDriveEnumerator::Init()
00427 {
00428     ULONG   ulCurrent;
00429 
00430     // bits 0-25 in mDrives represent each possible drive, A-Z
00431     DosError(FERR_DISABLEHARDERR);
00432     APIRET rc = DosQueryCurrentDisk(&ulCurrent, (PULONG)&mDrives);
00433     DosError(FERR_ENABLEHARDERR);
00434     if (rc)
00435         return NS_ERROR_FAILURE;
00436 
00437     mLetter = 'A';
00438     return NS_OK;
00439 }
00440 
00441 NS_IMETHODIMP nsDriveEnumerator::HasMoreElements(PRBool *aHasMore)
00442 {
00443     // no more bits means no more drives
00444     *aHasMore = (mDrives != 0);
00445     return NS_OK;
00446 }
00447 
00448 NS_IMETHODIMP nsDriveEnumerator::GetNext(nsISupports **aNext)
00449 {
00450     if (!mDrives)
00451     {
00452         *aNext = nsnull;
00453         return NS_OK;
00454     }
00455 
00456     // if bit 0 is off, advance to the next bit that's on
00457     while ((mDrives & 1) == 0)
00458     {
00459         mDrives >>= 1;
00460         mLetter++;
00461     }
00462 
00463     // format a drive string, then advance to the next possible drive
00464     char drive[4] = "x:\\";
00465     drive[0] = mLetter;
00466     mDrives >>= 1;
00467     mLetter++;
00468 
00469     nsILocalFile *file;
00470     nsresult rv = NS_NewNativeLocalFile(nsDependentCString(drive),
00471                                         PR_FALSE, &file);
00472     *aNext = file;
00473 
00474     return rv;
00475 }
00476 
00477 //-----------------------------------------------------------------------------
00478 // class TypeEaEnumerator - a convenience for accessing
00479 // a file's .TYPE extended attribute
00480 //-----------------------------------------------------------------------------
00481 
00482 // this struct describes the first entry for an MVMT or MVST EA;
00483 // .TYPE is supposed to be MVMT but is sometimes malformed as MVST
00484 
00485 typedef struct _MVHDR {
00486     USHORT  usEAType;
00487     USHORT  usCodePage;
00488     USHORT  usNumEntries;
00489     USHORT  usDataType;
00490     USHORT  usDataLth;
00491     char    data[1];
00492 } MVHDR;
00493 
00494 typedef MVHDR *PMVHDR;
00495 
00496 
00497 class TypeEaEnumerator
00498 {
00499 public:
00500     TypeEaEnumerator() : mEaBuf(nsnull) { }
00501     ~TypeEaEnumerator() { if (mEaBuf) NS_Free(mEaBuf); }
00502 
00503     nsresult Init(nsLocalFile * aFile);
00504     char *   GetNext(PRUint32 *lth);
00505 
00506 private:
00507     char *  mEaBuf;
00508     char *  mpCur;
00509     PMVHDR  mpMvh;
00510     USHORT  mLth;
00511     USHORT  mCtr;
00512 };
00513 
00514 
00515 nsresult TypeEaEnumerator::Init(nsLocalFile * aFile)
00516 {
00517 #define EABUFSIZE 512
00518 
00519     // provide a buffer for the results
00520     mEaBuf = (char*)NS_Alloc(EABUFSIZE);
00521     if (!mEaBuf)
00522         return NS_ERROR_OUT_OF_MEMORY;
00523 
00524     PFEA2LIST   pfea2list = (PFEA2LIST)mEaBuf;
00525     pfea2list->cbList = EABUFSIZE;
00526 
00527     // ask for the .TYPE extended attribute
00528     nsresult rv = aFile->GetEA(".TYPE", pfea2list);
00529     if (NS_FAILED(rv))
00530         return rv;
00531 
00532     // point at the data - it starts immediately after the EA's name;
00533     // then confirm the EA is MVMT (correct) or MVST (acceptable)
00534     mpMvh = (PMVHDR)&(pfea2list->list[0].szName[pfea2list->list[0].cbName+1]);
00535     if (mpMvh->usEAType != EAT_MVMT)
00536         if (mpMvh->usEAType != EAT_MVST || mpMvh->usDataType != EAT_ASCII)
00537             return NS_ERROR_FAILURE;
00538 
00539     // init the variables that tell us where we are in the lsit
00540     mLth = 0;
00541     mCtr = 0;
00542     mpCur = (char*)(mpMvh->usEAType == EAT_MVMT ?
00543                     &mpMvh->usDataType : &mpMvh->usDataLth);
00544 
00545     return NS_OK;
00546 }
00547 
00548 
00549 char *   TypeEaEnumerator::GetNext(PRUint32 *lth)
00550 {
00551     char *  result = nsnull;
00552 
00553     // this is a loop so we can skip invalid entries if needed;
00554     // normally, it will break out on the first iteration
00555     while (mCtr++ < mpMvh->usNumEntries) {
00556 
00557         // advance to the next entry
00558         mpCur += mLth;
00559 
00560         // if MVMT, ensure the datatype is OK, then advance
00561         // to the length field present in both formats
00562         if (mpMvh->usEAType == EAT_MVMT) {
00563             if (*((PUSHORT)mpCur) != EAT_ASCII)
00564                 continue;
00565             mpCur += sizeof(USHORT);
00566         }
00567 
00568         // get the data's length, point at the data itself, then exit
00569         mLth = *lth = *((PUSHORT)mpCur);
00570         mpCur += sizeof(USHORT);
00571         result = mpCur;
00572         break;
00573     }
00574 
00575     return result;
00576 }
00577 
00578 //-----------------------------------------------------------------------------
00579 // nsLocalFile <public>
00580 //-----------------------------------------------------------------------------
00581 
00582 nsLocalFile::nsLocalFile()
00583   : mDirty(PR_TRUE)
00584 {
00585 }
00586 
00587 NS_METHOD
00588 nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
00589 {
00590     NS_ENSURE_ARG_POINTER(aInstancePtr);
00591     NS_ENSURE_NO_AGGREGATION(outer);
00592 
00593     nsLocalFile* inst = new nsLocalFile();
00594     if (inst == NULL)
00595         return NS_ERROR_OUT_OF_MEMORY;
00596 
00597     nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
00598     if (NS_FAILED(rv))
00599     {
00600         delete inst;
00601         return rv;
00602     }
00603     return NS_OK;
00604 }
00605 
00606 
00607 //-----------------------------------------------------------------------------
00608 // nsLocalFile::nsISupports
00609 //-----------------------------------------------------------------------------
00610 
00611 NS_IMPL_THREADSAFE_ISUPPORTS3(nsLocalFile, nsILocalFile, nsIFile, nsILocalFileOS2)
00612 
00613 
00614 //-----------------------------------------------------------------------------
00615 // nsLocalFile <private>
00616 //-----------------------------------------------------------------------------
00617 
00618 nsLocalFile::nsLocalFile(const nsLocalFile& other)
00619   : mDirty(PR_TRUE)
00620   , mWorkingPath(other.mWorkingPath)
00621 {
00622 }
00623 
00624 
00625 // Stat the path. After a successful return the path is
00626 // guaranteed valid and the members of mFileInfo64 can be used.
00627 nsresult
00628 nsLocalFile::Stat()
00629 {
00630     // if we aren't dirty then we are already done
00631     if (!mDirty)
00632         return NS_OK;
00633 
00634     // we can't stat anything that isn't a valid NSPR addressable path
00635     if (mWorkingPath.IsEmpty())
00636         return NS_ERROR_FILE_INVALID_PATH;
00637 
00638     // hack designed to work around bug 134796 until it is fixed
00639     char temp[4];
00640     const char *nsprPath = mWorkingPath.get();
00641     if (mWorkingPath.Length() == 2 && mWorkingPath.CharAt(1) == ':')
00642     {
00643         temp[0] = mWorkingPath[0];
00644         temp[1] = mWorkingPath[1];
00645         temp[2] = '\\';
00646         temp[3] = '\0';
00647         nsprPath = temp;
00648     }
00649 
00650     // see if the working path exists
00651     DosError(FERR_DISABLEHARDERR);
00652     PRStatus status = PR_GetFileInfo64(nsprPath, &mFileInfo64);
00653     DosError(FERR_ENABLEHARDERR);
00654     if (status != PR_SUCCESS)
00655         return NS_ERROR_FILE_NOT_FOUND;
00656 
00657     mDirty = PR_FALSE;
00658     return NS_OK;
00659 }
00660 
00661 
00662 //-----------------------------------------------------------------------------
00663 // nsLocalFile::nsIFile,nsILocalFile
00664 //-----------------------------------------------------------------------------
00665 
00666 NS_IMETHODIMP
00667 nsLocalFile::Clone(nsIFile **file)
00668 {
00669     // Just copy-construct ourselves
00670     *file = new nsLocalFile(*this);
00671     if (!*file)
00672       return NS_ERROR_OUT_OF_MEMORY;
00673 
00674     NS_ADDREF(*file);
00675 
00676     return NS_OK;
00677 }
00678 
00679 NS_IMETHODIMP
00680 nsLocalFile::InitWithNativePath(const nsACString &filePath)
00681 {
00682     MakeDirty();
00683 
00684     nsACString::const_iterator begin, end;
00685     filePath.BeginReading(begin);
00686     filePath.EndReading(end);
00687 
00688     // input string must not be empty
00689     if (begin == end)
00690         return NS_ERROR_FAILURE;
00691 
00692     char firstChar = *begin;
00693     char secondChar = *(++begin);
00694 
00695     // just do a sanity check.  if it has any forward slashes, it is not
00696     // a Native path.  Also, it must have a colon at after the first char.
00697 
00698     char *path = nsnull;
00699     PRInt32 pathLen = 0;
00700 
00701     if ( ( (secondChar == ':') && !FindCharInReadable('/', begin, end) ) ||  // normal path
00702          ( (firstChar == '\\') && (secondChar == '\\') ) )  // network path
00703     {
00704         // This is a native path
00705         path = ToNewCString(filePath);
00706         pathLen = filePath.Length();
00707     }
00708 
00709     if (path == nsnull)
00710         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
00711 
00712     // kill any trailing '\' provided it isn't the second char of DBCS
00713     PRInt32 len = pathLen - 1;
00714     if (path[len] == '\\' && !::isleadbyte(path[len-1]))
00715     {
00716         path[len] = '\0';
00717         pathLen = len;
00718     }
00719 
00720     mWorkingPath.Adopt(path, pathLen);
00721     return NS_OK;
00722 }
00723 
00724 NS_IMETHODIMP
00725 nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
00726 {
00727     nsresult rv = Stat();
00728     if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
00729         return rv;
00730 
00731     *_retval = PR_Open(mWorkingPath.get(), flags, mode);
00732     if (*_retval)
00733         return NS_OK;
00734 
00735     return NS_ErrorAccordingToNSPR();
00736 }
00737 
00738 
00739 NS_IMETHODIMP
00740 nsLocalFile::OpenANSIFileDesc(const char *mode, FILE * *_retval)
00741 {
00742     nsresult rv = Stat();
00743     if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
00744         return rv;
00745 
00746     *_retval = fopen(mWorkingPath.get(), mode);
00747     if (*_retval)
00748         return NS_OK;
00749 
00750     return NS_ERROR_FAILURE;
00751 }
00752 
00753 
00754 
00755 NS_IMETHODIMP
00756 nsLocalFile::Create(PRUint32 type, PRUint32 attributes)
00757 {
00758     if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
00759         return NS_ERROR_FILE_UNKNOWN_TYPE;
00760 
00761     nsresult rv = Stat();
00762     if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
00763         return rv;
00764 
00765     // create directories to target
00766     //
00767     // A given local file can be either one of these forms:
00768     //
00769     //   - normal:    X:\some\path\on\this\drive
00770     //                       ^--- start here
00771     //
00772     //   - UNC path:  \\machine\volume\some\path\on\this\drive
00773     //                                     ^--- start here
00774     //
00775     // Skip the first 'X:\' for the first form, and skip the first full
00776     // '\\machine\volume\' segment for the second form.
00777 
00778     unsigned char* path = (unsigned char*) mWorkingPath.BeginWriting();
00779 
00780     if (path[0] == '\\' && path[1] == '\\')
00781     {
00782         // dealing with a UNC path here; skip past '\\machine\'
00783         path = _mbschr(path + 2, '\\');
00784         if (!path)
00785             return NS_ERROR_FILE_INVALID_PATH;
00786         ++path;
00787     }
00788 
00789     // search for first slash after the drive (or volume) name
00790     unsigned char* slash = _mbschr(path, '\\');
00791 
00792     if (slash)
00793     {
00794         // skip the first '\\'
00795         ++slash;
00796         slash = _mbschr(slash, '\\');
00797 
00798         while (slash)
00799         {
00800             *slash = '\0';
00801 
00802             rv = CreateDirectoryA(NS_CONST_CAST(char*, mWorkingPath.get()), NULL);
00803             if (rv) {
00804                 rv = ConvertOS2Error(rv);
00805                 if (rv != NS_ERROR_FILE_ALREADY_EXISTS)
00806                     return rv;
00807             }
00808             *slash = '\\';
00809             ++slash;
00810             slash = _mbschr(slash, '\\');
00811         }
00812     }
00813 
00814     if (type == NORMAL_FILE_TYPE)
00815     {
00816         PRFileDesc* file = PR_Open(mWorkingPath.get(), PR_RDONLY | PR_CREATE_FILE | PR_APPEND | PR_EXCL, attributes);
00817         if (!file)
00818             return NS_ERROR_FILE_ALREADY_EXISTS;
00819 
00820         PR_Close(file);
00821         return NS_OK;
00822     }
00823 
00824     if (type == DIRECTORY_TYPE)
00825     {
00826         rv = CreateDirectoryA(NS_CONST_CAST(char*, mWorkingPath.get()), NULL);
00827         if (rv)
00828             return ConvertOS2Error(rv);
00829         else
00830             return NS_OK;
00831     }
00832 
00833     return NS_ERROR_FILE_UNKNOWN_TYPE;
00834 }
00835 
00836 
00837 NS_IMETHODIMP
00838 nsLocalFile::AppendNative(const nsACString &node)
00839 {
00840     // append this path, multiple components are not permitted
00841     return AppendNativeInternal(PromiseFlatCString(node), PR_FALSE);
00842 }
00843 
00844 NS_IMETHODIMP
00845 nsLocalFile::AppendRelativeNativePath(const nsACString &node)
00846 {
00847     // append this path, multiple components are permitted
00848     return AppendNativeInternal(PromiseFlatCString(node), PR_TRUE);
00849 }
00850 
00851 nsresult
00852 nsLocalFile::AppendNativeInternal(const nsAFlatCString &node, PRBool multipleComponents)
00853 {
00854     if (node.IsEmpty())
00855         return NS_OK;
00856 
00857     // check the relative path for validity
00858     const unsigned char * nodePath = (const unsigned char *) node.get();
00859     if (*nodePath == '\\'                           // can't start with an '\'
00860         || _mbschr(nodePath, '/')                   // can't contain /
00861         || node.Equals(".."))                       // can't be ..
00862         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
00863 
00864     if (multipleComponents)
00865     {
00866         // can't contain .. as a path component. Ensure that the valid components
00867         // "foo..foo", "..foo", and "foo.." are not falsely detected, but the invalid
00868         // paths "..\", "foo\..", "foo\..\foo", "..\foo", etc are.
00869         const unsigned char * doubleDot = _mbsstr(nodePath, (const unsigned char *)"\\..");
00870         while (doubleDot)
00871         {
00872             doubleDot += 3;
00873             if (*doubleDot == '\0' || *doubleDot == '\\')
00874                 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
00875             doubleDot = _mbsstr(doubleDot, (unsigned char *)"\\..");
00876         }
00877         // catches the remaining cases of prefixes (i.e. '..\')
00878         // note: this is a substitute for Win32's _mbsncmp(nodePath, "..\\", 3)
00879         if (*nodePath == '.') {
00880             nodePath = (const unsigned char*)WinNextChar(0,0,0,(char*)nodePath);
00881             if (*nodePath == '.' &&
00882                 *WinNextChar(0,0,0,(char*)nodePath) == '\\')
00883                 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
00884         }
00885     }
00886     else if (_mbschr(nodePath, '\\'))   // single components can't contain '\'
00887         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
00888 
00889     MakeDirty();
00890 
00891     mWorkingPath.Append(NS_LITERAL_CSTRING("\\") + node);
00892 
00893     return NS_OK;
00894 }
00895 
00896 NS_IMETHODIMP
00897 nsLocalFile::Normalize()
00898 {
00899     // XXX See bug 187957 comment 18 for possible problems with this implementation.
00900 
00901     if (mWorkingPath.IsEmpty())
00902         return NS_OK;
00903 
00904     // work in unicode for ease
00905     nsAutoString path;
00906     NS_CopyNativeToUnicode(mWorkingPath, path);
00907 
00908     // find the index of the root backslash for the path. Everything before
00909     // this is considered fully normalized and cannot be ascended beyond
00910     // using ".."  For a local drive this is the first slash (e.g. "c:\").
00911     // For a UNC path it is the slash following the share name
00912     // (e.g. "\\server\share\").
00913     PRInt32 rootIdx = 2;        // default to local drive
00914     if (path.First() == '\\')   // if a share then calculate the rootIdx
00915     {
00916         rootIdx = path.FindChar('\\', 2);   // skip \\ in front of the server
00917         if (rootIdx == kNotFound)
00918             return NS_OK;                   // already normalized
00919         rootIdx = path.FindChar('\\', rootIdx+1);
00920         if (rootIdx == kNotFound)
00921             return NS_OK;                   // already normalized
00922     }
00923     else if (path.CharAt(rootIdx) != '\\')
00924     {
00925         // The path has been specified relative to the current working directory
00926         // for that drive. To normalize it, the current working directory for
00927         // that drive needs to be inserted before the supplied relative path
00928         // which will provide an absolute path (and the rootIdx will still be 2).
00929         char drv[4] = "A:.";
00930         char cwd[CCHMAXPATH];
00931 
00932         drv[0] = mWorkingPath.First();
00933         if (DosQueryPathInfo(drv, FIL_QUERYFULLNAME, cwd, sizeof(cwd)))
00934             return NS_ERROR_FILE_NOT_FOUND;
00935 
00936         nsAutoString currentDir;
00937         NS_CopyNativeToUnicode(nsDependentCString(cwd), currentDir);
00938 
00939         if (currentDir.Last() == '\\')
00940             path.Replace(0, 2, currentDir);
00941         else
00942             path.Replace(0, 2, currentDir + NS_LITERAL_STRING("\\"));
00943     }
00944     NS_POSTCONDITION(0 < rootIdx && rootIdx < (PRInt32)path.Length(), "rootIdx is invalid");
00945     NS_POSTCONDITION(path.CharAt(rootIdx) == '\\', "rootIdx is invalid");
00946 
00947     // if there is nothing following the root path then it is already normalized
00948     if (rootIdx + 1 == (PRInt32)path.Length())
00949         return NS_OK;
00950 
00951     // assign the root
00952     nsAutoString normal;
00953     const PRUnichar * pathBuffer = path.get();  // simplify access to the buffer
00954     normal.SetCapacity(path.Length()); // it won't ever grow longer
00955     normal.Assign(pathBuffer, rootIdx);
00956 
00957     // Normalize the path components. The actions taken are:
00958     //
00959     //  "\\"    condense to single backslash
00960     //  "."     remove from path
00961     //  ".."    up a directory
00962     //  "..."   remove from path (any number of dots > 2)
00963     //
00964     // The last form is something that Windows 95 and 98 supported and
00965     // is a shortcut for changing up multiple directories. Windows XP
00966     // and ilk ignore it in a path, as is done here.
00967     PRInt32 len, begin, end = rootIdx;
00968     while (end < (PRInt32)path.Length())
00969     {
00970         // find the current segment (text between the backslashes) to
00971         // be examined, this will set the following variables:
00972         //  begin == index of first char in segment
00973         //  end   == index 1 char after last char in segment
00974         //  len   == length of segment
00975         begin = end + 1;
00976         end = path.FindChar('\\', begin);
00977         if (end == kNotFound)
00978             end = path.Length();
00979         len = end - begin;
00980 
00981         // ignore double backslashes
00982         if (len == 0)
00983             continue;
00984 
00985         // len != 0, and interesting paths always begin with a dot
00986         if (pathBuffer[begin] == '.')
00987         {
00988             // ignore single dots
00989             if (len == 1)
00990                 continue;
00991 
00992             // handle multiple dots
00993             if (len >= 2 && pathBuffer[begin+1] == '.')
00994             {
00995                 // back up a path component on double dot
00996                 if (len == 2)
00997                 {
00998                     PRInt32 prev = normal.RFindChar('\\');
00999                     if (prev >= rootIdx)
01000                         normal.Truncate(prev);
01001                     continue;
01002                 }
01003 
01004                 // length is > 2 and the first two characters are dots.
01005                 // if the rest of the string is dots, then ignore it.
01006                 int idx = len - 1;
01007                 for (; idx >= 2; --idx)
01008                 {
01009                     if (pathBuffer[begin+idx] != '.')
01010                         break;
01011                 }
01012 
01013                 // this is true if the loop above didn't break
01014                 // and all characters in this segment are dots.
01015                 if (idx < 2)
01016                     continue;
01017             }
01018         }
01019 
01020         // add the current component to the path, including the preceding backslash
01021         normal.Append(pathBuffer + begin - 1, len + 1);
01022     }
01023 
01024     // kill trailing dots and spaces.
01025     PRInt32 filePathLen = normal.Length() - 1;
01026     while(filePathLen > 0 && (normal[filePathLen] == ' ' || normal[filePathLen] == '.'))
01027     {
01028         normal.Truncate(filePathLen--);
01029     }
01030 
01031     NS_CopyUnicodeToNative(normal, mWorkingPath);
01032     MakeDirty();
01033 
01034     return NS_OK;
01035 }
01036 
01037 NS_IMETHODIMP
01038 nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
01039 {
01040     aLeafName.Truncate();
01041 
01042     const char* temp = mWorkingPath.get();
01043     if(temp == nsnull)
01044         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
01045 
01046     const char* leaf = (const char*) _mbsrchr((const unsigned char*) temp, '\\');
01047 
01048     // if the working path is just a node without any lashes.
01049     if (leaf == nsnull)
01050         leaf = temp;
01051     else
01052         leaf++;
01053 
01054     aLeafName.Assign(leaf);
01055     return NS_OK;
01056 }
01057 
01058 NS_IMETHODIMP
01059 nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
01060 {
01061     MakeDirty();
01062 
01063     const unsigned char* temp = (const unsigned char*) mWorkingPath.get();
01064     if(temp == nsnull)
01065         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
01066 
01067     // cannot use nsCString::RFindChar() due to 0x5c problem
01068     PRInt32 offset = (PRInt32) (_mbsrchr(temp, '\\') - temp);
01069     if (offset)
01070     {
01071         mWorkingPath.Truncate(offset+1);
01072     }
01073     mWorkingPath.Append(aLeafName);
01074 
01075     return NS_OK;
01076 }
01077 
01078 
01079 NS_IMETHODIMP
01080 nsLocalFile::GetNativePath(nsACString &_retval)
01081 {
01082     _retval = mWorkingPath;
01083     return NS_OK;
01084 }
01085 
01086 //-----------------------------------------------------------------------------
01087 
01088 // get any single extended attribute for the current file or directory
01089 
01090 nsresult
01091 nsLocalFile::GetEA(const char *eaName, PFEA2LIST pfea2list)
01092 {
01093     // ensure we have an out-buffer whose length is specified
01094     if (!pfea2list || !pfea2list->cbList)
01095         return NS_ERROR_FAILURE;
01096 
01097     // the gea2list's name field is only 1 byte long;
01098     // this expands its allocation to hold a 33 byte name
01099     union {
01100         GEA2LIST    gea2list;
01101         char        dummy[sizeof(GEA2LIST)+32];
01102     };
01103     EAOP2       eaop2;
01104 
01105     eaop2.fpFEA2List = pfea2list;
01106     eaop2.fpGEA2List = &gea2list;
01107 
01108     // fill in the request structure
01109     dummy[sizeof(GEA2LIST)+31] = 0;
01110     gea2list.list[0].oNextEntryOffset = 0;
01111     strcpy(gea2list.list[0].szName, eaName);
01112     gea2list.list[0].cbName = strlen(gea2list.list[0].szName);
01113     gea2list.cbList = sizeof(GEA2LIST) + gea2list.list[0].cbName;
01114 
01115     // see what we get - this will succeed even if the EA doesn't exist
01116     APIRET rc = DosQueryPathInfo(mWorkingPath.get(), FIL_QUERYEASFROMLIST,
01117                                  &eaop2, sizeof(eaop2));
01118     if (rc)
01119         return ConvertOS2Error(rc);
01120 
01121     // if the data length is zero, requested EA doesn't exist
01122     if (!pfea2list->list[0].cbValue)
01123         return NS_ERROR_FAILURE;
01124 
01125     return NS_OK;
01126 }
01127 
01128 
01129 // return an array of file types or null if there are none
01130 
01131 NS_IMETHODIMP
01132 nsLocalFile::GetFileTypes(nsIArray **_retval)
01133 {
01134     NS_ENSURE_ARG(_retval);
01135     *_retval = 0;
01136 
01137     // fetch the .TYPE ea & prepare for enumeration
01138     TypeEaEnumerator typeEnum;
01139     nsresult rv = typeEnum.Init(this);
01140     if (NS_FAILED(rv))
01141         return rv;
01142 
01143     // create an array that's scriptable
01144     nsCOMPtr<nsIMutableArray> mutArray;
01145     rv = NS_NewArray(getter_AddRefs(mutArray));
01146     if (NS_FAILED(rv))
01147         return rv;
01148 
01149     PRInt32  cnt;
01150     PRUint32 lth;
01151     char *   ptr;
01152 
01153     // get each file type, convert to a CString, then add to the array
01154     for (cnt=0, ptr=typeEnum.GetNext(&lth); ptr; ptr=typeEnum.GetNext(&lth)) {
01155         nsCOMPtr<nsISupportsCString> typeString(
01156                     do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv));
01157         if (NS_SUCCEEDED(rv)) {
01158             nsCAutoString temp;
01159             temp.Assign(ptr, lth);
01160             typeString->SetData(temp);
01161             mutArray->AppendElement(typeString, PR_FALSE);
01162             cnt++;
01163         }
01164     }
01165 
01166     // if the array has any contents, addref & return it
01167     if (cnt) {
01168         *_retval = mutArray;
01169         NS_ADDREF(*_retval);
01170         rv = NS_OK;
01171     }
01172     else
01173         rv = NS_ERROR_FAILURE;
01174 
01175     return rv;
01176 }
01177 
01178 
01179 // see if the file is of the requested type
01180 
01181 NS_IMETHODIMP
01182 nsLocalFile::IsFileType(const nsACString& fileType, PRBool *_retval)
01183 {
01184     NS_ENSURE_ARG(_retval);
01185     *_retval = PR_FALSE;
01186 
01187     // fetch the .TYPE ea & prepare for enumeration
01188     TypeEaEnumerator typeEnum;
01189     nsresult rv = typeEnum.Init(this);
01190     if (NS_FAILED(rv))
01191         return rv;
01192 
01193     PRUint32 lth;
01194     char *   ptr;
01195 
01196     // compare each type to the request;  if there's a match, exit
01197     for (ptr = typeEnum.GetNext(&lth); ptr; ptr = typeEnum.GetNext(&lth))
01198         if (fileType.EqualsASCII(ptr, lth)) {
01199             *_retval = PR_TRUE;
01200             break;
01201         }
01202 
01203     return NS_OK;
01204 }
01205 
01206 //-----------------------------------------------------------------------------
01207 
01208 // this struct combines an FEA2LIST, an FEA2, plus additional fields
01209 // needed to write a .TYPE EA in the correct EAT_MVMT format
01210 
01211 #pragma pack(1)
01212     typedef struct _TYPEEA {
01213         struct {
01214             ULONG   ulcbList;
01215             ULONG   uloNextEntryOffset;
01216             BYTE    bfEA;
01217             BYTE    bcbName;
01218             USHORT  uscbValue;
01219             char    chszName[sizeof(".TYPE")];
01220         } hdr;
01221         struct {
01222             USHORT  usEAType;
01223             USHORT  usCodePage;
01224             USHORT  usNumEntries;
01225             USHORT  usDataType;
01226             USHORT  usDataLth;
01227         } info;
01228         char    data[1];
01229     } TYPEEA;
01230 
01231     typedef struct _TYPEEA2 {
01232       USHORT  usDataType;
01233       USHORT  usDataLth;
01234     } TYPEEA2;
01235 #pragma pack()
01236 
01237 
01238 // writes one or more .TYPE extended attributes taken
01239 // from a comma-delimited string
01240 
01241 NS_IMETHODIMP
01242 nsLocalFile::SetFileTypes(const nsACString& fileTypes)
01243 {
01244     if (fileTypes.IsEmpty())
01245         return NS_ERROR_FAILURE;
01246 
01247     PRUint32 cnt = CountCharInReadable(fileTypes, ',');
01248     PRUint32 lth = fileTypes.Length() - cnt + (cnt * sizeof(TYPEEA2));
01249     PRUint32 size = sizeof(TYPEEA) + lth;
01250 
01251     char *   pBuf = (char*)NS_Alloc(size);
01252     if (!pBuf)
01253         return NS_ERROR_OUT_OF_MEMORY;
01254 
01255     TYPEEA * pEA = (TYPEEA *)pBuf;
01256 
01257     // the buffer has an extra byte due to TYPEEA.data[1]
01258     pEA->hdr.ulcbList = size - 1;
01259     pEA->hdr.uloNextEntryOffset = 0;
01260     pEA->hdr.bfEA = 0;
01261     pEA->hdr.bcbName = sizeof(".TYPE") - 1;
01262     pEA->hdr.uscbValue = sizeof(pEA->info) + lth;
01263     strcpy(pEA->hdr.chszName, ".TYPE");
01264 
01265     pEA->info.usEAType = EAT_MVMT;
01266     pEA->info.usCodePage = 0;
01267     pEA->info.usNumEntries = ++cnt;
01268 
01269     nsACString::const_iterator begin, end, delim;
01270     fileTypes.BeginReading(begin);
01271     fileTypes.EndReading(end);
01272     delim = begin;
01273 
01274     // fill in type & length, copy the current type name (which
01275     // is not zero-terminated), then advance the ptr so the next
01276     // iteration can reuse the trailing members of the structure
01277     do {
01278         FindCharInReadable( ',', delim, end);
01279         lth = delim.get() - begin.get();
01280         pEA->info.usDataType = EAT_ASCII;
01281         pEA->info.usDataLth = lth;
01282         memcpy(pEA->data, begin.get(), lth);
01283         pEA = (TYPEEA *)((char*)pEA + lth + sizeof(TYPEEA2));
01284         begin = ++delim;
01285     } while (--cnt);
01286 
01287     // write the EA, then free the buffer
01288     EAOP2 eaop2;
01289     eaop2.fpGEA2List = 0;
01290     eaop2.fpFEA2List = (PFEA2LIST)pBuf;
01291 
01292     int rc = DosSetPathInfo(mWorkingPath.get(), FIL_QUERYEASIZE,
01293                             &eaop2, sizeof(eaop2), 0);
01294     NS_Free(pBuf);
01295 
01296     if (rc)
01297         return ConvertOS2Error(rc);
01298 
01299     return NS_OK;
01300 }
01301 
01302 //-----------------------------------------------------------------------------
01303 
01304 // this struct combines an FEA2LIST, an FEA2, plus additional fields
01305 // needed to write a .SUBJECT EA in the correct EAT_ASCII format;
01306 
01307 #pragma pack(1)
01308     typedef struct _SUBJEA {
01309         struct {
01310             ULONG   ulcbList;
01311             ULONG   uloNextEntryOffset;
01312             BYTE    bfEA;
01313             BYTE    bcbName;
01314             USHORT  uscbValue;
01315             char    chszName[sizeof(".SUBJECT")];
01316         } hdr;
01317         struct {
01318             USHORT  usEAType;
01319             USHORT  usDataLth;
01320         } info;
01321         char    data[1];
01322     } SUBJEA;
01323 #pragma pack()
01324 
01325 // saves the file's source URI in its .SUBJECT extended attribute
01326 
01327 NS_IMETHODIMP
01328 nsLocalFile::SetFileSource(const nsACString& aURI)
01329 {
01330     if (aURI.IsEmpty())
01331         return NS_ERROR_FAILURE;
01332 
01333     // this includes an extra character for the spec's trailing null
01334     PRUint32    lth = sizeof(SUBJEA) + aURI.Length();
01335     char *      pBuf = (char*)NS_Alloc(lth);
01336     if (!pBuf)
01337         return NS_ERROR_OUT_OF_MEMORY;
01338 
01339     SUBJEA *    pEA = (SUBJEA *)pBuf;
01340 
01341     // the trailing null doesn't get saved, so don't include it in the size
01342     pEA->hdr.ulcbList = lth - 1;
01343     pEA->hdr.uloNextEntryOffset = 0;
01344     pEA->hdr.bfEA = 0;
01345     pEA->hdr.bcbName = sizeof(".SUBJECT") - 1;
01346     pEA->hdr.uscbValue = sizeof(pEA->info) + aURI.Length();
01347     strcpy(pEA->hdr.chszName, ".SUBJECT");
01348     pEA->info.usEAType = EAT_ASCII;
01349     pEA->info.usDataLth = aURI.Length();
01350     strcpy(pEA->data, PromiseFlatCString(aURI).get());
01351 
01352     // write the EA, then free the buffer
01353     EAOP2 eaop2;
01354     eaop2.fpGEA2List = 0;
01355     eaop2.fpFEA2List = (PFEA2LIST)pEA;
01356 
01357     int rc = DosSetPathInfo(mWorkingPath.get(), FIL_QUERYEASIZE,
01358                             &eaop2, sizeof(eaop2), 0);
01359     NS_Free(pBuf);
01360 
01361     if (rc)
01362         return ConvertOS2Error(rc);
01363 
01364     return NS_OK;
01365 }
01366 
01367 //-----------------------------------------------------------------------------
01368 
01369 nsresult
01370 nsLocalFile::CopySingleFile(nsIFile *sourceFile, nsIFile *destParent,
01371                             const nsACString &newName, PRBool move)
01372 {
01373     nsresult rv;
01374     nsCAutoString filePath;
01375 
01376     nsCAutoString destPath;
01377     destParent->GetNativeTarget(destPath);
01378 
01379     destPath.Append("\\");
01380 
01381     if (newName.IsEmpty())
01382     {
01383         nsCAutoString aFileName;
01384         sourceFile->GetNativeLeafName(aFileName);
01385         destPath.Append(aFileName);
01386     }
01387     else
01388     {
01389         destPath.Append(newName);
01390     }
01391 
01392     rv = sourceFile->GetNativePath(filePath);
01393 
01394     if (NS_FAILED(rv))
01395         return rv;
01396 
01397     APIRET rc = NO_ERROR;
01398 
01399     if (move)
01400         rc = DosMove(filePath.get(), (PSZ)NS_CONST_CAST(char*, destPath.get()));
01401 
01402     if (!move || rc == ERROR_NOT_SAME_DEVICE || rc == ERROR_ACCESS_DENIED)
01403     {
01404         // will get an error if the destination and source files aren't on
01405         // the same drive.  "MoveFile()" on Windows will go ahead and move
01406         // the file without error, so we need to do the same   IBM-AKR
01407 
01408         do {
01409             rc = DosCopy(filePath.get(), (PSZ)NS_CONST_CAST(char*, destPath.get()), DCPY_EXISTING);
01410             if (rc == ERROR_TOO_MANY_OPEN_FILES) {
01411                 ULONG CurMaxFH = 0;
01412                 LONG ReqCount = 20;
01413                 APIRET rc2;
01414                 rc2 = DosSetRelMaxFH(&ReqCount, &CurMaxFH);
01415                 if (rc2 != NO_ERROR)
01416                     break;
01417             }
01418         } while (rc == ERROR_TOO_MANY_OPEN_FILES);
01419 
01420         // WSOD2 HACK - NETWORK_ACCESS_DENIED
01421         if (rc == 65)
01422         {
01423             CHAR         achProgram[CCHMAXPATH];  // buffer for program name, parameters
01424             RESULTCODES  rescResults;             // buffer for results of dosexecpgm
01425 
01426             strcpy(achProgram, "CMD.EXE  /C ");
01427             strcat(achProgram, """COPY ");
01428             strcat(achProgram, filePath.get());
01429             strcat(achProgram, " ");
01430             strcat(achProgram, (PSZ)NS_CONST_CAST(char*, destPath.get()));
01431             strcat(achProgram, """");
01432             achProgram[strlen(achProgram) + 1] = '\0';
01433             achProgram[7] = '\0';
01434             DosExecPgm(NULL, 0,
01435                        EXEC_SYNC, achProgram, (PSZ)NULL,
01436                        &rescResults, achProgram);
01437             rc = 0; // Assume it worked
01438 
01439         } // rc == 65
01440 
01441         // moving the file is supposed to act like a rename, so delete the
01442         // original file if we got this far without error
01443         if(move && (rc == NO_ERROR))
01444             DosDelete( filePath.get());
01445 
01446     } // !move or ERROR
01447 
01448     if (rc)
01449         rv = ConvertOS2Error(rc);
01450 
01451     return rv;
01452 }
01453 
01454 
01455 nsresult
01456 nsLocalFile::CopyMove(nsIFile *aParentDir, const nsACString &newName, PRBool move)
01457 {
01458     nsCOMPtr<nsIFile> newParentDir = aParentDir;
01459 
01460     nsresult rv  = Stat();
01461     if (NS_FAILED(rv))
01462         return rv;
01463 
01464     if (!newParentDir)
01465     {
01466         // no parent was specified.  We must rename.
01467 
01468         if (newName.IsEmpty())
01469             return NS_ERROR_INVALID_ARG;
01470 
01471         rv = GetParent(getter_AddRefs(newParentDir));
01472         if (NS_FAILED(rv))
01473             return rv;
01474     }
01475 
01476     if (!newParentDir)
01477         return NS_ERROR_FILE_DESTINATION_NOT_DIR;
01478 
01479     // make sure it exists and is a directory.  Create it if not there.
01480     PRBool exists;
01481     newParentDir->Exists(&exists);
01482     if (!exists)
01483     {
01484         rv = newParentDir->Create(DIRECTORY_TYPE, 0644);  // TODO, what permissions should we use
01485         if (NS_FAILED(rv))
01486             return rv;
01487     }
01488     else
01489     {
01490         PRBool isDir;
01491         newParentDir->IsDirectory(&isDir);
01492         if (isDir == PR_FALSE)
01493         {
01494             return NS_ERROR_FILE_DESTINATION_NOT_DIR;
01495         }
01496     }
01497 
01498     // Try different ways to move/copy files/directories
01499     PRBool done = PR_FALSE;
01500     PRBool isDir;
01501     IsDirectory(&isDir);
01502 
01503     // Try to move the file or directory, or try to copy a single file
01504     if (move || !isDir)
01505     {
01506         // when moving things, first try to just MoveFile it,
01507         // even if it is a directory
01508         rv = CopySingleFile(this, newParentDir, newName, move);
01509         done = NS_SUCCEEDED(rv);
01510         // If we are moving a directory and that fails, fallback on directory
01511         // enumeration.  See bug 231300 for details.
01512         if (!done && !(move && isDir))
01513             return rv;
01514     }
01515 
01516     // Not able to copy or move directly, so enumerate it
01517     if (!done)
01518     {
01519         // create a new target destination in the new parentDir;
01520         nsCOMPtr<nsIFile> target;
01521         rv = newParentDir->Clone(getter_AddRefs(target));
01522 
01523         if (NS_FAILED(rv))
01524             return rv;
01525 
01526         nsCAutoString allocatedNewName;
01527         if (newName.IsEmpty())
01528         {
01529             GetNativeLeafName(allocatedNewName);
01530         }
01531         else
01532         {
01533             allocatedNewName = newName;
01534         }
01535 
01536         rv = target->AppendNative(allocatedNewName);
01537         if (NS_FAILED(rv))
01538             return rv;
01539 
01540         allocatedNewName.Truncate();
01541 
01542         // check if the destination directory already exists
01543         target->Exists(&exists);
01544         if (!exists)
01545         {
01546             // if the destination directory cannot be created, return an error
01547             rv = target->Create(DIRECTORY_TYPE, 0644);  // TODO, what permissions should we use
01548             if (NS_FAILED(rv))
01549                 return rv;
01550         }
01551         else
01552         {
01553             // check if the destination directory is writable and empty
01554             PRBool isWritable;
01555 
01556             target->IsWritable(&isWritable);
01557             if (!isWritable)
01558                 return NS_ERROR_FILE_ACCESS_DENIED;
01559 
01560             nsCOMPtr<nsISimpleEnumerator> targetIterator;
01561             rv = target->GetDirectoryEntries(getter_AddRefs(targetIterator));
01562 
01563             PRBool more;
01564             targetIterator->HasMoreElements(&more);
01565             // return error if target directory is not empty
01566             if (more)
01567                 return NS_ERROR_FILE_DIR_NOT_EMPTY;
01568         }
01569 
01570         nsDirEnumerator dirEnum;
01571 
01572         rv = dirEnum.Init(this);
01573         if (NS_FAILED(rv)) {
01574             NS_WARNING("dirEnum initalization failed");
01575             return rv;
01576         }
01577 
01578         PRBool more;
01579         while (NS_SUCCEEDED(dirEnum.HasMoreElements(&more)) && more)
01580         {
01581             nsCOMPtr<nsISupports> item;
01582             nsCOMPtr<nsIFile> file;
01583             dirEnum.GetNext(getter_AddRefs(item));
01584             file = do_QueryInterface(item);
01585             if (file)
01586             {
01587                 if (move)
01588                 {
01589                     rv = file->MoveToNative(target, EmptyCString());
01590                     NS_ENSURE_SUCCESS(rv,rv);
01591                 }
01592                 else
01593                 {
01594                     rv = file->CopyToNative(target, EmptyCString());
01595                     NS_ENSURE_SUCCESS(rv,rv);
01596                 }
01597             }
01598         }
01599         // we've finished moving all the children of this directory
01600         // in the new directory.  so now delete the directory
01601         // note, we don't need to do a recursive delete.
01602         // MoveTo() is recursive.  At this point,
01603         // we've already moved the children of the current folder
01604         // to the new location.  nothing should be left in the folder.
01605         if (move)
01606         {
01607           rv = Remove(PR_FALSE); // recursive
01608           NS_ENSURE_SUCCESS(rv,rv);
01609         }
01610     }
01611 
01612 
01613     // If we moved, we want to adjust this.
01614     if (move)
01615     {
01616         MakeDirty();
01617 
01618         nsCAutoString newParentPath;
01619         newParentDir->GetNativePath(newParentPath);
01620 
01621         if (newParentPath.IsEmpty())
01622             return NS_ERROR_FAILURE;
01623 
01624         if (newName.IsEmpty())
01625         {
01626             nsCAutoString aFileName;
01627             GetNativeLeafName(aFileName);
01628 
01629             InitWithNativePath(newParentPath);
01630             AppendNative(aFileName);
01631         }
01632         else
01633         {
01634             InitWithNativePath(newParentPath);
01635             AppendNative(newName);
01636         }
01637     }
01638 
01639     return NS_OK;
01640 }
01641 
01642 NS_IMETHODIMP
01643 nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString &newName)
01644 {
01645     return CopyMove(newParentDir, newName, PR_FALSE);
01646 }
01647 
01648 NS_IMETHODIMP
01649 nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString &newName)
01650 {
01651     return CopyMove(newParentDir, newName, PR_FALSE);
01652 }
01653 
01654 NS_IMETHODIMP
01655 nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString &newName)
01656 {
01657     return CopyMove(newParentDir, newName, PR_TRUE);
01658 }
01659 
01660 NS_IMETHODIMP
01661 nsLocalFile::Load(PRLibrary * *_retval)
01662 {
01663     PRBool isFile;
01664     nsresult rv = IsFile(&isFile);
01665 
01666     if (NS_FAILED(rv))
01667         return rv;
01668 
01669     if (! isFile)
01670         return NS_ERROR_FILE_IS_DIRECTORY;
01671 
01672     *_retval =  PR_LoadLibrary(mWorkingPath.get());
01673 
01674     if (*_retval)
01675         return NS_OK;
01676 
01677     return NS_ERROR_NULL_POINTER;
01678 }
01679 
01680 NS_IMETHODIMP
01681 nsLocalFile::Remove(PRBool recursive)
01682 {
01683     PRBool isDir = PR_FALSE;
01684 
01685     nsresult rv = IsDirectory(&isDir);
01686     if (NS_FAILED(rv))
01687         return rv;
01688 
01689     if (isDir)
01690     {
01691         if (recursive)
01692         {
01693             nsDirEnumerator dirEnum;
01694 
01695             rv = dirEnum.Init(this);
01696             if (NS_FAILED(rv))
01697                 return rv;
01698 
01699             PRBool more;
01700             while (NS_SUCCEEDED(dirEnum.HasMoreElements(&more)) && more)
01701             {
01702                 nsCOMPtr<nsISupports> item;
01703                 dirEnum.GetNext(getter_AddRefs(item));
01704                 nsCOMPtr<nsIFile> file = do_QueryInterface(item);
01705                 if (file)
01706                     file->Remove(recursive);
01707             }
01708         }
01709         rv = rmdir(mWorkingPath.get());
01710     }
01711     else
01712     {
01713         rv = remove(mWorkingPath.get());
01714     }
01715 
01716     // fixup error code if necessary...
01717     if (rv == (nsresult)-1)
01718         rv = NSRESULT_FOR_ERRNO();
01719 
01720     MakeDirty();
01721     return rv;
01722 }
01723 
01724 NS_IMETHODIMP
01725 nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModifiedTime)
01726 {
01727     NS_ENSURE_ARG(aLastModifiedTime);
01728 
01729     *aLastModifiedTime = 0;
01730     nsresult rv = Stat();
01731     if (NS_FAILED(rv))
01732         return rv;
01733 
01734     // microseconds -> milliseconds
01735     PRInt64 usecPerMsec;
01736     LL_I2L(usecPerMsec, PR_USEC_PER_MSEC);
01737     LL_DIV(*aLastModifiedTime, mFileInfo64.modifyTime, usecPerMsec);
01738     return NS_OK;
01739 }
01740 
01741 
01742 NS_IMETHODIMP
01743 nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModifiedTime)
01744 {
01745     return NS_ERROR_NOT_IMPLEMENTED;
01746 }
01747 
01748 
01749 NS_IMETHODIMP
01750 nsLocalFile::SetLastModifiedTime(PRInt64 aLastModifiedTime)
01751 {
01752     return nsLocalFile::SetModDate(aLastModifiedTime);
01753 }
01754 
01755 
01756 NS_IMETHODIMP
01757 nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModifiedTime)
01758 {
01759     return NS_ERROR_NOT_IMPLEMENTED;
01760 }
01761 
01762 nsresult
01763 nsLocalFile::SetModDate(PRInt64 aLastModifiedTime)
01764 {
01765     nsresult rv = Stat();
01766 
01767     if (NS_FAILED(rv))
01768         return rv;
01769 
01770     PRExplodedTime pret;
01771     FILESTATUS3 pathInfo;
01772 
01773     // Level 1 info
01774     rv = DosQueryPathInfo(mWorkingPath.get(), FIL_STANDARD,
01775                           &pathInfo, sizeof(pathInfo));
01776 
01777     if (NS_FAILED(rv))
01778     {
01779        rv = ConvertOS2Error(rv);
01780        return rv;
01781     }
01782 
01783     // PR_ExplodeTime expects usecs...
01784     PR_ExplodeTime(aLastModifiedTime * PR_USEC_PER_MSEC, PR_LocalTimeParameters, &pret);
01785 
01786     // fdateLastWrite.year is based off of 1980
01787     if (pret.tm_year >= 1980)
01788         pathInfo.fdateLastWrite.year    = pret.tm_year-1980;
01789     else
01790         pathInfo.fdateLastWrite.year    = pret.tm_year;
01791 
01792     // Convert start offset -- OS/2: Jan=1; NSPR: Jan=0
01793     pathInfo.fdateLastWrite.month       = pret.tm_month + 1;
01794     pathInfo.fdateLastWrite.day         = pret.tm_mday;
01795     pathInfo.ftimeLastWrite.hours       = pret.tm_hour;
01796     pathInfo.ftimeLastWrite.minutes     = pret.tm_min;
01797     pathInfo.ftimeLastWrite.twosecs     = pret.tm_sec / 2;
01798 
01799     rv = DosSetPathInfo(mWorkingPath.get(), FIL_STANDARD,
01800                         &pathInfo, sizeof(pathInfo), 0UL);
01801 
01802     if (NS_FAILED(rv))
01803        return rv;
01804 
01805     MakeDirty();
01806     return rv;
01807 }
01808 
01809 NS_IMETHODIMP
01810 nsLocalFile::GetPermissions(PRUint32 *aPermissions)
01811 {
01812     NS_ENSURE_ARG(aPermissions);
01813 
01814     nsresult rv = Stat();
01815     if (NS_FAILED(rv))
01816         return rv;
01817 
01818     PRBool isWritable, isExecutable;
01819     IsWritable(&isWritable);
01820     IsExecutable(&isExecutable);
01821 
01822     *aPermissions = PR_IRUSR|PR_IRGRP|PR_IROTH;         // all read
01823     if (isWritable)
01824         *aPermissions |= PR_IWUSR|PR_IWGRP|PR_IWOTH;    // all write
01825     if (isExecutable)
01826         *aPermissions |= PR_IXUSR|PR_IXGRP|PR_IXOTH;    // all execute
01827 
01828     return NS_OK;
01829 }
01830 
01831 NS_IMETHODIMP
01832 nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink)
01833 {
01834     return NS_ERROR_NOT_IMPLEMENTED;
01835 }
01836 
01837 // the only Unix-style permission OS/2 knows is whether a file is
01838 // writable;  as a matter of policy, a browser shouldn't be able
01839 // to change any of the other DOS-style attributes;  to enforce
01840 // this, we use DosSetPathInfo() rather than chmod()
01841 NS_IMETHODIMP
01842 nsLocalFile::SetPermissions(PRUint32 aPermissions)
01843 {
01844     nsresult rv = Stat();
01845     if (NS_FAILED(rv))
01846         return rv;
01847 
01848     APIRET rc;
01849     FILESTATUS3 pathInfo;
01850 
01851     // Level 1 info
01852     rc = DosQueryPathInfo(mWorkingPath.get(), FIL_STANDARD,
01853                           &pathInfo, sizeof(pathInfo));
01854 
01855     if (rc != NO_ERROR)
01856        return ConvertOS2Error(rc);
01857 
01858     ULONG attr = 0;
01859     if (!(aPermissions & (PR_IWUSR|PR_IWGRP|PR_IWOTH)))
01860         attr = FILE_READONLY;
01861 
01862     if (attr == (pathInfo.attrFile & FILE_READONLY))
01863         return NS_OK;
01864 
01865     pathInfo.attrFile ^= FILE_READONLY;
01866 
01867     rc = DosSetPathInfo(mWorkingPath.get(), FIL_STANDARD,
01868                         &pathInfo, sizeof(pathInfo), 0UL);
01869 
01870     if (rc != NO_ERROR)
01871        return ConvertOS2Error(rc);
01872 
01873     return NS_OK;
01874 }
01875 
01876 NS_IMETHODIMP
01877 nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions)
01878 {
01879     return NS_ERROR_NOT_IMPLEMENTED;
01880 }
01881 
01882 
01883 NS_IMETHODIMP
01884 nsLocalFile::GetFileSize(PRInt64 *aFileSize)
01885 {
01886     NS_ENSURE_ARG(aFileSize);
01887     *aFileSize = 0;
01888 
01889     nsresult rv = Stat();
01890     if (NS_FAILED(rv))
01891         return rv;
01892 
01893     *aFileSize = mFileInfo64.size;
01894     return NS_OK;
01895 }
01896 
01897 
01898 NS_IMETHODIMP
01899 nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSize)
01900 {
01901     return NS_ERROR_NOT_IMPLEMENTED;
01902 }
01903 
01904 
01905 NS_IMETHODIMP
01906 nsLocalFile::SetFileSize(PRInt64 aFileSize)
01907 {
01908     nsresult rv = Stat();
01909     if (NS_FAILED(rv))
01910         return rv;
01911 
01912     APIRET rc;
01913     HFILE hFile;
01914     ULONG actionTaken;
01915 
01916     rc = DosOpen(mWorkingPath.get(),
01917                  &hFile,
01918                  &actionTaken,
01919                  0,
01920                  FILE_NORMAL,
01921                  OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
01922                  OPEN_SHARE_DENYREADWRITE | OPEN_ACCESS_READWRITE,
01923                  NULL);
01924 
01925     if (rc != NO_ERROR)
01926     {
01927         MakeDirty();
01928         return NS_ERROR_FAILURE;
01929     }
01930 
01931     // Seek to new, desired end of file
01932     PRInt32 hi, lo;
01933     myLL_L2II(aFileSize, &hi, &lo );
01934 
01935     rc = DosSetFileSize(hFile, lo);
01936     DosClose(hFile);
01937     MakeDirty();
01938 
01939     if (rc != NO_ERROR)
01940         return NS_ERROR_FAILURE;
01941 
01942     return NS_OK;
01943 }
01944 
01945 NS_IMETHODIMP
01946 nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
01947 {
01948     NS_ENSURE_ARG(aDiskSpaceAvailable);
01949     *aDiskSpaceAvailable = 0;
01950 
01951     nsresult rv = Stat();
01952     if (NS_FAILED(rv))
01953         return rv;
01954 
01955     ULONG ulDriveNo = toupper(mWorkingPath.CharAt(0)) + 1 - 'A';
01956     FSALLOCATE fsAllocate;
01957     APIRET rc = DosQueryFSInfo(ulDriveNo,
01958                                FSIL_ALLOC,
01959                                &fsAllocate,
01960                                sizeof(fsAllocate));
01961 
01962     if (rc != NO_ERROR)
01963        return NS_ERROR_FAILURE;
01964 
01965     *aDiskSpaceAvailable = fsAllocate.cUnitAvail;
01966     *aDiskSpaceAvailable *= fsAllocate.cSectorUnit;
01967     *aDiskSpaceAvailable *= fsAllocate.cbSector;
01968 
01969     return NS_OK;
01970 }
01971 
01972 NS_IMETHODIMP
01973 nsLocalFile::GetParent(nsIFile * *aParent)
01974 {
01975     NS_ENSURE_ARG_POINTER(aParent);
01976 
01977     nsCAutoString parentPath(mWorkingPath);
01978 
01979     // cannot use nsCString::RFindChar() due to 0x5c problem
01980     PRInt32 offset = (PRInt32) (_mbsrchr((const unsigned char *) parentPath.get(), '\\')
01981                      - (const unsigned char *) parentPath.get());
01982     // adding this offset check that was removed in bug 241708 fixes mail
01983     // directories that aren't relative to/underneath the profile dir.
01984     // e.g., on a different drive. Before you remove them, please make
01985     // sure local mail directories that aren't underneath the profile dir work.
01986     if (offset < 0)
01987       return NS_ERROR_FILE_UNRECOGNIZED_PATH;
01988 
01989     if (offset == 1 && parentPath[0] == '\\') {
01990         aParent = nsnull;
01991         return NS_OK;
01992     }
01993     if (offset > 0)
01994         parentPath.Truncate(offset);
01995     else
01996         parentPath.AssignLiteral("\\\\.");
01997 
01998     nsCOMPtr<nsILocalFile> localFile;
01999     nsresult rv = NS_NewNativeLocalFile(parentPath, PR_FALSE, getter_AddRefs(localFile));
02000 
02001     if(NS_SUCCEEDED(rv) && localFile)
02002     {
02003         return CallQueryInterface(localFile, aParent);
02004     }
02005     return rv;
02006 }
02007 
02008 NS_IMETHODIMP
02009 nsLocalFile::Exists(PRBool *_retval)
02010 {
02011     NS_ENSURE_ARG(_retval);
02012     *_retval = PR_FALSE;
02013 
02014     MakeDirty();
02015     nsresult rv = Stat();
02016 
02017     *_retval = NS_SUCCEEDED(rv);
02018     return NS_OK;
02019 }
02020 
02021 NS_IMETHODIMP
02022 nsLocalFile::IsWritable(PRBool *_retval)
02023 {
02024     NS_ENSURE_ARG(_retval);
02025     *_retval = PR_FALSE;
02026 
02027     nsresult rv = Stat();
02028     if (NS_FAILED(rv))
02029         return rv;
02030 
02031     APIRET rc;
02032     FILESTATUS3 pathInfo;
02033 
02034     // Level 1 info
02035     rc = DosQueryPathInfo(mWorkingPath.get(), FIL_STANDARD,
02036                           &pathInfo, sizeof(pathInfo));
02037 
02038     if (rc != NO_ERROR)
02039     {
02040        rc = ConvertOS2Error(rc);
02041        return rc;
02042     }
02043 
02044     // on OS/2, unlike Windows, directories on writable media
02045     // can not be assigned a readonly attribute
02046     *_retval = !((pathInfo.attrFile & FILE_READONLY) != 0);
02047     return NS_OK;
02048 }
02049 
02050 NS_IMETHODIMP
02051 nsLocalFile::IsReadable(PRBool *_retval)
02052 {
02053     NS_ENSURE_ARG(_retval);
02054     *_retval = PR_FALSE;
02055 
02056     nsresult rv = Stat();
02057     if (NS_FAILED(rv))
02058         return rv;
02059 
02060     *_retval = PR_TRUE;
02061     return NS_OK;
02062 }
02063 
02064 
02065 NS_IMETHODIMP
02066 nsLocalFile::IsExecutable(PRBool *_retval)
02067 {
02068     NS_ENSURE_ARG(_retval);
02069     *_retval = PR_FALSE;
02070 
02071     nsresult rv = Stat();
02072     if (NS_FAILED(rv))
02073         return rv;
02074 
02075     // no need to bother if this isn't a file
02076     PRBool isFile;
02077     rv = IsFile(&isFile);
02078     if (NS_FAILED(rv) || !isFile)
02079         return rv;
02080 
02081     nsCAutoString path;
02082     GetNativeTarget(path);
02083 
02084     // get the filename, including the leading backslash
02085     const char* leaf = (const char*) _mbsrchr((const unsigned char*)path.get(), '\\');
02086     if (!leaf)
02087         return NS_OK;
02088 
02089     // eliminate trailing dots & spaces in a DBCS-aware manner
02090     // XXX shouldn't this have been done when the path was set?
02091 
02092     char*   pathEnd = WinPrevChar(0, 0, 0, leaf, strchr(leaf, 0));
02093     while (pathEnd > leaf)
02094     {
02095         if (*pathEnd != ' ' && *pathEnd != '.')
02096             break;
02097         *pathEnd = 0;
02098         pathEnd = WinPrevChar(0, 0, 0, leaf, pathEnd);
02099     }
02100 
02101     if (pathEnd == leaf)
02102         return NS_OK;
02103 
02104     // get the extension, including the dot
02105     char* ext = (char*) _mbsrchr((const unsigned char*)leaf, '.');
02106     if (!ext)
02107         return NS_OK;
02108 
02109     if (stricmp(ext, ".exe") == 0 ||
02110         stricmp(ext, ".cmd") == 0 ||
02111         stricmp(ext, ".com") == 0 ||
02112         stricmp(ext, ".bat") == 0)
02113         *_retval = PR_TRUE;
02114 
02115     return NS_OK;
02116 }
02117 
02118 
02119 NS_IMETHODIMP
02120 nsLocalFile::IsDirectory(PRBool *_retval)
02121 {
02122     NS_ENSURE_ARG(_retval);
02123     *_retval = PR_FALSE;
02124 
02125     nsresult rv = Stat();
02126     if (NS_FAILED(rv))
02127         return rv;
02128 
02129     *_retval = (mFileInfo64.type == PR_FILE_DIRECTORY);
02130     return NS_OK;
02131 }
02132 
02133 NS_IMETHODIMP
02134 nsLocalFile::IsFile(PRBool *_retval)
02135 {
02136     NS_ENSURE_ARG(_retval);
02137     *_retval = PR_FALSE;
02138 
02139     nsresult rv = Stat();
02140     if (NS_FAILED(rv))
02141         return rv;
02142 
02143     *_retval = (mFileInfo64.type == PR_FILE_FILE);
02144     return rv;
02145 }
02146 
02147 NS_IMETHODIMP
02148 nsLocalFile::IsHidden(PRBool *_retval)
02149 {
02150     NS_ENSURE_ARG(_retval);
02151     *_retval = PR_FALSE;
02152 
02153     nsresult rv = Stat();
02154     if (NS_FAILED(rv))
02155         return rv;
02156 
02157     APIRET rc;
02158     FILESTATUS3 pathInfo;
02159 
02160     // Level 1 info
02161     rc = DosQueryPathInfo(mWorkingPath.get(), FIL_STANDARD,
02162                           &pathInfo, sizeof(pathInfo));
02163 
02164     if (rc != NO_ERROR)
02165     {
02166        rc = ConvertOS2Error(rc);
02167        return rc;
02168     }
02169 
02170     *_retval = ((pathInfo.attrFile & FILE_HIDDEN) != 0);
02171     return NS_OK;
02172 }
02173 
02174 
02175 NS_IMETHODIMP
02176 nsLocalFile::IsSymlink(PRBool *_retval)
02177 {
02178     NS_ENSURE_ARG_POINTER(_retval);
02179 
02180     // No Symlinks on OS/2
02181     *_retval = PR_FALSE;
02182     return NS_OK;
02183 }
02184 
02185 NS_IMETHODIMP
02186 nsLocalFile::IsSpecial(PRBool *_retval)
02187 {
02188     NS_ENSURE_ARG(_retval);
02189 
02190     // when implemented, IsSpecial will be used for WPS objects
02191     *_retval = PR_FALSE;
02192     return NS_OK;
02193 }
02194 
02195 NS_IMETHODIMP
02196 nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
02197 {
02198     NS_ENSURE_ARG(inFile);
02199     NS_ENSURE_ARG(_retval);
02200 
02201     nsCAutoString inFilePath;
02202     inFile->GetNativePath(inFilePath);
02203 
02204     *_retval = inFilePath.Equals(mWorkingPath);
02205     return NS_OK;
02206 }
02207 
02208 NS_IMETHODIMP
02209 nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval)
02210 {
02211     *_retval = PR_FALSE;
02212 
02213     nsCAutoString myFilePath;
02214     if ( NS_FAILED(GetNativeTarget(myFilePath)))
02215         GetNativePath(myFilePath);
02216 
02217     PRInt32 myFilePathLen = myFilePath.Length();
02218 
02219     nsCAutoString inFilePath;
02220     if ( NS_FAILED(inFile->GetNativeTarget(inFilePath)))
02221         inFile->GetNativePath(inFilePath);
02222 
02223     if ( strnicmp( myFilePath.get(), inFilePath.get(), myFilePathLen) == 0)
02224     {
02225         // now make sure that the |inFile|'s path has a trailing
02226         // separator.
02227 
02228         if (inFilePath[myFilePathLen] == '\\')
02229         {
02230             *_retval = PR_TRUE;
02231         }
02232 
02233     }
02234 
02235     return NS_OK;
02236 }
02237 
02238 NS_IMETHODIMP
02239 nsLocalFile::GetNativeTarget(nsACString &_retval)
02240 {
02241     _retval = mWorkingPath;
02242     return NS_OK;
02243 }
02244 
02245 NS_IMETHODIMP
02246 nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
02247 {
02248     NS_ENSURE_ARG(aFollowLinks);
02249     *aFollowLinks = PR_FALSE;
02250     return NS_OK;
02251 }
02252 
02253 NS_IMETHODIMP
02254 nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
02255 {
02256     return NS_OK;
02257 }
02258 
02259 NS_IMETHODIMP
02260 nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator * *entries)
02261 {
02262     NS_ENSURE_ARG(entries);
02263     nsresult rv;
02264     *entries = nsnull;
02265 
02266     if (mWorkingPath.EqualsLiteral("\\\\.")) {
02267         nsDriveEnumerator *drives = new nsDriveEnumerator;
02268         if (!drives)
02269             return NS_ERROR_OUT_OF_MEMORY;
02270         NS_ADDREF(drives);
02271         rv = drives->Init();
02272         if (NS_FAILED(rv)) {
02273             NS_RELEASE(drives);
02274             return rv;
02275         }
02276         *entries = drives;
02277         return NS_OK;
02278     }
02279 
02280     PRBool isDir;
02281     rv = IsDirectory(&isDir);
02282     if (NS_FAILED(rv))
02283         return rv;
02284     if (!isDir)
02285         return NS_ERROR_FILE_NOT_DIRECTORY;
02286 
02287     nsDirEnumerator* dirEnum = new nsDirEnumerator();
02288     if (dirEnum == nsnull)
02289         return NS_ERROR_OUT_OF_MEMORY;
02290     NS_ADDREF(dirEnum);
02291     rv = dirEnum->Init(this);
02292     if (NS_FAILED(rv))
02293     {
02294         NS_RELEASE(dirEnum);
02295         return rv;
02296     }
02297 
02298     *entries = dirEnum;
02299     return NS_OK;
02300 }
02301 
02302 NS_IMETHODIMP
02303 nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
02304 {
02305     return GetNativePath(aPersistentDescriptor);
02306 }
02307 
02308 NS_IMETHODIMP
02309 nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
02310 {
02311     return InitWithNativePath(aPersistentDescriptor);
02312 }
02313 
02314 #ifndef OPEN_DEFAULT
02315 #define OPEN_DEFAULT       0
02316 #define OPEN_CONTENTS      1
02317 #endif
02318 
02319 NS_IMETHODIMP
02320 nsLocalFile::Reveal()
02321 {
02322     PRBool isDirectory = PR_FALSE;
02323     nsCAutoString path;
02324 
02325     IsDirectory(&isDirectory);
02326     if (isDirectory)
02327     {
02328         GetNativePath(path);
02329     }
02330     else
02331     {
02332         nsCOMPtr<nsIFile> parent;
02333         GetParent(getter_AddRefs(parent));
02334         if (parent)
02335             parent->GetNativePath(path);
02336     }
02337 
02338     HOBJECT hobject = WinQueryObject(path.get());
02339     WinSetFocus(HWND_DESKTOP, HWND_DESKTOP);
02340     WinOpenObject(hobject, OPEN_DEFAULT, TRUE);
02341 
02342     // we don't care if it succeeded or failed.
02343     return NS_OK;
02344 }
02345 
02346 
02347 NS_IMETHODIMP
02348 nsLocalFile::Launch()
02349 {
02350   HOBJECT hobject = WinQueryObject(mWorkingPath.get());
02351   WinSetFocus(HWND_DESKTOP, HWND_DESKTOP);
02352   WinOpenObject(hobject, OPEN_DEFAULT, TRUE);
02353 
02354   // we don't care if it succeeded or failed.
02355   return NS_OK;
02356 }
02357 
02358 nsresult
02359 NS_NewNativeLocalFile(const nsACString &path, PRBool followLinks, nsILocalFile* *result)
02360 {
02361     nsLocalFile* file = new nsLocalFile();
02362     if (file == nsnull)
02363         return NS_ERROR_OUT_OF_MEMORY;
02364     NS_ADDREF(file);
02365 
02366     if (!path.IsEmpty()) {
02367         nsresult rv = file->InitWithNativePath(path);
02368         if (NS_FAILED(rv)) {
02369             NS_RELEASE(file);
02370             return rv;
02371         }
02372     }
02373 
02374     *result = file;
02375     return NS_OK;
02376 }
02377 
02378 //-----------------------------------------------------------------------------
02379 // UCS2 interface
02380 //-----------------------------------------------------------------------------
02381 
02382 NS_IMETHODIMP
02383 nsLocalFile::InitWithPath(const nsAString &filePath)
02384 {
02385     if (filePath.IsEmpty())
02386         return InitWithNativePath(EmptyCString());
02387 
02388     nsCAutoString tmp;
02389     nsresult rv = NS_CopyUnicodeToNative(filePath, tmp);
02390     if (NS_SUCCEEDED(rv))
02391         return InitWithNativePath(tmp);
02392 
02393     return rv;
02394 }
02395 
02396 NS_IMETHODIMP
02397 nsLocalFile::Append(const nsAString &node)
02398 {
02399     if (node.IsEmpty())
02400         return NS_OK;
02401 
02402     nsCAutoString tmp;
02403     nsresult rv = NS_CopyUnicodeToNative(node, tmp);
02404     if (NS_SUCCEEDED(rv))
02405         return AppendNative(tmp);
02406 
02407     return rv;
02408 }
02409 
02410 NS_IMETHODIMP
02411 nsLocalFile::AppendRelativePath(const nsAString &node)
02412 {
02413     if (node.IsEmpty())
02414         return NS_OK;
02415 
02416     nsCAutoString tmp;
02417     nsresult rv = NS_CopyUnicodeToNative(node, tmp);
02418     if (NS_SUCCEEDED(rv))
02419         return AppendRelativeNativePath(tmp);
02420 
02421     return rv;
02422 }
02423 
02424 NS_IMETHODIMP
02425 nsLocalFile::GetLeafName(nsAString &aLeafName)
02426 {
02427     nsCAutoString tmp;
02428     nsresult rv = GetNativeLeafName(tmp);
02429     if (NS_SUCCEEDED(rv))
02430         rv = NS_CopyNativeToUnicode(tmp, aLeafName);
02431 
02432     return rv;
02433 }
02434 
02435 NS_IMETHODIMP
02436 nsLocalFile::SetLeafName(const nsAString &aLeafName)
02437 {
02438     if (aLeafName.IsEmpty())
02439         return SetNativeLeafName(EmptyCString());
02440 
02441     nsCAutoString tmp;
02442     nsresult rv = NS_CopyUnicodeToNative(aLeafName, tmp);
02443     if (NS_SUCCEEDED(rv))
02444         return SetNativeLeafName(tmp);
02445 
02446     return rv;
02447 }
02448 
02449 NS_IMETHODIMP
02450 nsLocalFile::GetPath(nsAString &_retval)
02451 {
02452     return NS_CopyNativeToUnicode(mWorkingPath, _retval);
02453 }
02454 
02455 NS_IMETHODIMP
02456 nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
02457 {
02458     if (newName.IsEmpty())
02459         return CopyToNative(newParentDir, EmptyCString());
02460 
02461     nsCAutoString tmp;
02462     nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
02463     if (NS_SUCCEEDED(rv))
02464         return CopyToNative(newParentDir, tmp);
02465 
02466     return rv;
02467 }
02468 
02469 NS_IMETHODIMP
02470 nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
02471 {
02472     if (newName.IsEmpty())
02473         return CopyToFollowingLinksNative(newParentDir, EmptyCString());
02474 
02475     nsCAutoString tmp;
02476     nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
02477     if (NS_SUCCEEDED(rv))
02478         return CopyToFollowingLinksNative(newParentDir, tmp);
02479 
02480     return rv;
02481 }
02482 
02483 NS_IMETHODIMP
02484 nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
02485 {
02486     if (newName.IsEmpty())
02487         return MoveToNative(newParentDir, EmptyCString());
02488 
02489     nsCAutoString tmp;
02490     nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
02491     if (NS_SUCCEEDED(rv))
02492         return MoveToNative(newParentDir, tmp);
02493 
02494     return rv;
02495 }
02496 
02497 NS_IMETHODIMP
02498 nsLocalFile::GetTarget(nsAString &_retval)
02499 {
02500     nsCAutoString tmp;
02501     nsresult rv = GetNativeTarget(tmp);
02502     if (NS_SUCCEEDED(rv))
02503         rv = NS_CopyNativeToUnicode(tmp, _retval);
02504 
02505     return rv;
02506 }
02507 
02508 nsresult
02509 NS_NewLocalFile(const nsAString &path, PRBool followLinks, nsILocalFile* *result)
02510 {
02511     nsCAutoString buf;
02512     nsresult rv = NS_CopyUnicodeToNative(path, buf);
02513     if (NS_FAILED(rv)) {
02514         *result = nsnull;
02515         return rv;
02516     }
02517     return NS_NewNativeLocalFile(buf, followLinks, result);
02518 }
02519 
02520 //-----------------------------------------------------------------------------
02521 // nsLocalFile <static members>
02522 //-----------------------------------------------------------------------------
02523 
02524 void
02525 nsLocalFile::GlobalInit()
02526 {
02527 }
02528 
02529 void
02530 nsLocalFile::GlobalShutdown()
02531 {
02532 }
02533 
02534 //-----------------------------------------------------------------------------
02535