Back to index

lightning-sunbird  0.9+nobinonly
nsLocalFileUnix.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-1999
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Mike Shaver            <shaver@mozilla.org>
00025  *   Christopher Blizzard   <blizzard@mozilla.org>
00026  *   Jason Eager            <jce2@po.cwru.edu>
00027  *   Stuart Parmenter       <pavlov@netscape.com>
00028  *   Brendan Eich           <brendan@mozilla.org>
00029  *   Pete Collins           <petejc@mozdev.org>
00030  *   Paul Ashford           <arougthopher@lizardland.net>
00031  *   Fredrik Holmqvist      <thesuckiestemail@yahoo.se>
00032  *
00033  * Alternatively, the contents of this file may be used under the terms of
00034  * either of the GNU General Public License Version 2 or later (the "GPL"),
00035  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00036  * in which case the provisions of the GPL or the LGPL are applicable instead
00037  * of those above. If you wish to allow use of your version of this file only
00038  * under the terms of either the GPL or the LGPL, and not to allow others to
00039  * use your version of this file under the terms of the MPL, indicate your
00040  * decision by deleting the provisions above and replace them with the notice
00041  * and other provisions required by the GPL or the LGPL. If you do not delete
00042  * the provisions above, a recipient may use your version of this file under
00043  * the terms of any one of the MPL, the GPL or the LGPL.
00044  *
00045  * ***** END LICENSE BLOCK ***** */
00046 
00051 // We're going to need some autoconf loving, I can just tell.
00052 #include <sys/types.h>
00053 #include <sys/stat.h>
00054 #include <unistd.h>
00055 #include <fcntl.h>
00056 #include <errno.h>
00057 #include <utime.h>
00058 #include <dirent.h>
00059 #include <ctype.h>
00060 #include <locale.h>
00061 #ifdef XP_BEOS
00062     #include <Path.h>
00063     #include <Entry.h>
00064     #include <Roster.h>
00065 #endif
00066 #if defined(VMS)
00067     #include <fabdef.h>
00068 #endif
00069 
00070 #include "nsDirectoryServiceDefs.h"
00071 #include "nsCRT.h"
00072 #include "nsCOMPtr.h"
00073 #include "nsMemory.h"
00074 #include "nsIFile.h"
00075 #include "nsString.h"
00076 #include "nsReadableUtils.h"
00077 #include "nsLocalFile.h"
00078 #include "nsIComponentManager.h"
00079 #include "nsXPIDLString.h"
00080 #include "prproces.h"
00081 #include "nsIDirectoryEnumerator.h"
00082 #include "nsISimpleEnumerator.h"
00083 #include "nsITimelineService.h"
00084 
00085 #include "nsNativeCharsetUtils.h"
00086 
00087 // On some platforms file/directory name comparisons need to
00088 // be case-blind.
00089 #if defined(VMS)
00090     #define FILE_STRCMP strcasecmp
00091     #define FILE_STRNCMP strncasecmp
00092 #else
00093     #define FILE_STRCMP strcmp
00094     #define FILE_STRNCMP strncmp
00095 #endif
00096 
00097 #define VALIDATE_STAT_CACHE()                   \
00098     PR_BEGIN_MACRO                              \
00099         if (!mHaveCachedStat) {                 \
00100             FillStatCache();                    \
00101             if (!mHaveCachedStat)               \
00102                  return NSRESULT_FOR_ERRNO();   \
00103         }                                       \
00104     PR_END_MACRO
00105 
00106 #define CHECK_mPath()                           \
00107     PR_BEGIN_MACRO                              \
00108         if (mPath.IsEmpty())                    \
00109             return NS_ERROR_NOT_INITIALIZED;    \
00110     PR_END_MACRO
00111 
00112 /* directory enumerator */
00113 class NS_COM
00114 nsDirEnumeratorUnix : public nsISimpleEnumerator,
00115                       public nsIDirectoryEnumerator
00116 {
00117     public:
00118     nsDirEnumeratorUnix();
00119 
00120     // nsISupports interface
00121     NS_DECL_ISUPPORTS
00122 
00123     // nsISimpleEnumerator interface
00124     NS_DECL_NSISIMPLEENUMERATOR
00125 
00126     // nsIDirectoryEnumerator interface
00127     NS_DECL_NSIDIRECTORYENUMERATOR
00128 
00129     NS_IMETHOD Init(nsLocalFile *parent, PRBool ignored);
00130 
00131     private:
00132     ~nsDirEnumeratorUnix();
00133 
00134     protected:
00135     NS_IMETHOD GetNextEntry();
00136 
00137     DIR           *mDir;
00138     struct dirent *mEntry;
00139     nsCString      mParentPath;
00140 };
00141 
00142 nsDirEnumeratorUnix::nsDirEnumeratorUnix() :
00143                          mDir(nsnull), 
00144                          mEntry(nsnull)
00145 {
00146 }
00147 
00148 nsDirEnumeratorUnix::~nsDirEnumeratorUnix()
00149 {
00150     Close();
00151 }
00152 
00153 NS_IMPL_ISUPPORTS2(nsDirEnumeratorUnix, nsISimpleEnumerator, nsIDirectoryEnumerator)
00154 
00155 NS_IMETHODIMP
00156 nsDirEnumeratorUnix::Init(nsLocalFile *parent, PRBool resolveSymlinks /*ignored*/)
00157 {
00158     nsCAutoString dirPath;
00159     if (NS_FAILED(parent->GetNativePath(dirPath)) ||
00160         dirPath.IsEmpty()) {
00161         return NS_ERROR_FILE_INVALID_PATH;
00162     }
00163 
00164     if (NS_FAILED(parent->GetNativePath(mParentPath)))
00165         return NS_ERROR_FAILURE;
00166 
00167     mDir = opendir(dirPath.get());
00168     if (!mDir)
00169         return NSRESULT_FOR_ERRNO();
00170     return GetNextEntry();
00171 }
00172 
00173 NS_IMETHODIMP
00174 nsDirEnumeratorUnix::HasMoreElements(PRBool *result)
00175 {
00176     *result = mDir && mEntry;
00177     if (!*result)
00178         Close();
00179     return NS_OK;
00180 }
00181 
00182 NS_IMETHODIMP
00183 nsDirEnumeratorUnix::GetNext(nsISupports **_retval)
00184 {
00185     nsCOMPtr<nsIFile> file;
00186     nsresult rv = GetNextFile(getter_AddRefs(file));
00187     if (NS_FAILED(rv))
00188         return rv;
00189     NS_IF_ADDREF(*_retval = file);
00190     return NS_OK;
00191 }
00192 
00193 NS_IMETHODIMP
00194 nsDirEnumeratorUnix::GetNextEntry()
00195 {
00196     do {
00197         errno = 0;
00198         mEntry = readdir(mDir);
00199 
00200         // end of dir or error
00201         if (!mEntry)
00202             return NSRESULT_FOR_ERRNO();
00203 
00204         // keep going past "." and ".."
00205     } while (mEntry->d_name[0] == '.'     &&
00206             (mEntry->d_name[1] == '\0'    ||   // .\0
00207             (mEntry->d_name[1] == '.'     &&
00208             mEntry->d_name[2] == '\0')));      // ..\0
00209     return NS_OK;
00210 }
00211 
00212 NS_IMETHODIMP
00213 nsDirEnumeratorUnix::GetNextFile(nsIFile **_retval)
00214 {
00215     nsresult rv;
00216     if (!mDir || !mEntry) {
00217         *_retval = nsnull;
00218         return NS_OK;
00219     }
00220 
00221     nsCOMPtr<nsILocalFile> file = new nsLocalFile();
00222     if (!file)
00223         return NS_ERROR_OUT_OF_MEMORY;
00224 
00225     if (NS_FAILED(rv = file->InitWithNativePath(mParentPath)) ||
00226         NS_FAILED(rv = file->AppendNative(nsDependentCString(mEntry->d_name))))
00227         return rv;
00228 
00229     *_retval = file;
00230     NS_ADDREF(*_retval);
00231     return GetNextEntry();
00232 }
00233 
00234 NS_IMETHODIMP 
00235 nsDirEnumeratorUnix::Close()
00236 {
00237     if (mDir) {
00238         closedir(mDir);
00239         mDir = nsnull;
00240     }
00241     return NS_OK;
00242 }
00243 
00244 nsLocalFile::nsLocalFile() :
00245     mHaveCachedStat(PR_FALSE)
00246 {
00247 }
00248 
00249 nsLocalFile::nsLocalFile(const nsLocalFile& other)
00250   : mPath(other.mPath)
00251   , mHaveCachedStat(PR_FALSE)
00252 {
00253 }
00254 
00255 NS_IMPL_THREADSAFE_ISUPPORTS2(nsLocalFile,
00256                               nsIFile,
00257                               nsILocalFile)
00258 
00259 nsresult
00260 nsLocalFile::nsLocalFileConstructor(nsISupports *outer, 
00261                                     const nsIID &aIID,
00262                                     void **aInstancePtr)
00263 {
00264     NS_ENSURE_ARG_POINTER(aInstancePtr);
00265     NS_ENSURE_NO_AGGREGATION(outer);
00266 
00267     *aInstancePtr = nsnull;
00268 
00269     nsCOMPtr<nsIFile> inst = new nsLocalFile();
00270     if (!inst)
00271         return NS_ERROR_OUT_OF_MEMORY;
00272     return inst->QueryInterface(aIID, aInstancePtr);
00273 }
00274 
00275 nsresult 
00276 nsLocalFile::FillStatCache() {
00277     if (stat(mPath.get(), &mCachedStat) == -1) {
00278         // try lstat it may be a symlink
00279         if (lstat(mPath.get(), &mCachedStat) == -1) {
00280             return NSRESULT_FOR_ERRNO();
00281         }
00282     }
00283     mHaveCachedStat = PR_TRUE;
00284     return NS_OK;
00285 }
00286 
00287 NS_IMETHODIMP
00288 nsLocalFile::Clone(nsIFile **file)
00289 {
00290     // Just copy-construct ourselves
00291     *file = new nsLocalFile(*this);
00292     if (!*file)
00293       return NS_ERROR_OUT_OF_MEMORY;
00294 
00295     NS_ADDREF(*file);
00296     
00297     return NS_OK;
00298 }
00299 
00300 NS_IMETHODIMP
00301 nsLocalFile::InitWithNativePath(const nsACString &filePath)
00302 {
00303     if (Substring(filePath, 0, 2).EqualsLiteral("~/")) {
00304         nsCOMPtr<nsIFile> homeDir;
00305         nsCAutoString homePath;
00306         if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_HOME_DIR,
00307                                              getter_AddRefs(homeDir))) ||
00308             NS_FAILED(homeDir->GetNativePath(homePath))) {
00309             return NS_ERROR_FAILURE;
00310         }
00311         
00312         mPath = homePath + Substring(filePath, 1, filePath.Length() - 1);
00313     } else {
00314         if (filePath.IsEmpty() || filePath.First() != '/')
00315             return NS_ERROR_FILE_UNRECOGNIZED_PATH;
00316         mPath = filePath;
00317     }
00318 
00319     // trim off trailing slashes
00320     ssize_t len = mPath.Length();
00321     while ((len > 1) && (mPath[len - 1] == '/'))
00322         --len;
00323     mPath.SetLength(len);
00324 
00325     InvalidateCache();
00326     return NS_OK;
00327 }
00328 
00329 NS_IMETHODIMP
00330 nsLocalFile::CreateAllAncestors(PRUint32 permissions)
00331 {
00332     // <jband> I promise to play nice
00333     char *buffer = mPath.BeginWriting(),
00334          *slashp = buffer;
00335 
00336 #ifdef DEBUG_NSIFILE
00337     fprintf(stderr, "nsIFile: before: %s\n", buffer);
00338 #endif
00339 
00340     while ((slashp = strchr(slashp + 1, '/'))) {
00341         /*
00342          * Sequences of '/' are equivalent to a single '/'.
00343          */
00344         if (slashp[1] == '/')
00345             continue;
00346 
00347         /*
00348          * If the path has a trailing slash, don't make the last component,
00349          * because we'll get EEXIST in Create when we try to build the final
00350          * component again, and it's easier to condition the logic here than
00351          * there.
00352          */
00353         if (slashp[1] == '\0')
00354             break;
00355 
00356         /* Temporarily NUL-terminate here */
00357         *slashp = '\0';
00358 #ifdef DEBUG_NSIFILE
00359         fprintf(stderr, "nsIFile: mkdir(\"%s\")\n", buffer);
00360 #endif
00361         int mkdir_result = mkdir(buffer, permissions);
00362         int mkdir_errno  = errno;
00363         if (mkdir_result == -1) {
00364             /*
00365              * Always set |errno| to EEXIST if the dir already exists
00366              * (we have to do this here since the errno value is not consistent
00367              * in all cases - various reasons like different platform,
00368              * automounter-controlled dir, etc. can affect it (see bug 125489
00369              * for details)).
00370              */
00371             if (access(buffer, F_OK) == 0) {
00372                 mkdir_errno = EEXIST;
00373             }
00374         }
00375 
00376         /* Put the / back before we (maybe) return */
00377         *slashp = '/';
00378 
00379         /*
00380          * We could get EEXIST for an existing file -- not directory --
00381          * with the name of one of our ancestors, but that's OK: we'll get
00382          * ENOTDIR when we try to make the next component in the path,
00383          * either here on back in Create, and error out appropriately.
00384          */
00385         if (mkdir_result == -1 && mkdir_errno != EEXIST)
00386             return nsresultForErrno(mkdir_errno);
00387     }
00388 
00389 #ifdef DEBUG_NSIFILE
00390     fprintf(stderr, "nsIFile: after: %s\n", buffer);
00391 #endif
00392 
00393     return NS_OK;
00394 }
00395 
00396 NS_IMETHODIMP
00397 nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
00398 {
00399     *_retval = PR_Open(mPath.get(), flags, mode);
00400     if (! *_retval)
00401         return NS_ErrorAccordingToNSPR();
00402 
00403     return NS_OK;
00404 }
00405 
00406 NS_IMETHODIMP
00407 nsLocalFile::OpenANSIFileDesc(const char *mode, FILE **_retval)
00408 {
00409     *_retval = fopen(mPath.get(), mode);
00410     if (! *_retval)
00411         return NS_ERROR_FAILURE;
00412 
00413     return NS_OK;
00414 }
00415 
00416 static int
00417 do_create(const char *path, PRIntn flags, mode_t mode, PRFileDesc **_retval)
00418 {
00419     *_retval = PR_Open(path, flags, mode);
00420     return *_retval ? 0 : -1;
00421 }
00422 
00423 static int
00424 do_mkdir(const char *path, PRIntn flags, mode_t mode, PRFileDesc **_retval)
00425 {
00426     *_retval = nsnull;
00427     return mkdir(path, mode);
00428 }
00429 
00430 nsresult
00431 nsLocalFile::CreateAndKeepOpen(PRUint32 type, PRIntn flags,
00432                                PRUint32 permissions, PRFileDesc **_retval)
00433 {
00434     if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
00435         return NS_ERROR_FILE_UNKNOWN_TYPE;
00436 
00437     int result;
00438     int (*createFunc)(const char *, PRIntn, mode_t, PRFileDesc **) =
00439         (type == NORMAL_FILE_TYPE) ? do_create : do_mkdir;
00440 
00441     result = createFunc(mPath.get(), flags, permissions, _retval);
00442     if (result == -1 && errno == ENOENT) {
00443         /*
00444          * If we failed because of missing ancestor components, try to create
00445          * them and then retry the original creation.
00446          *
00447          * Ancestor directories get the same permissions as the file we're
00448          * creating, with the X bit set for each of (user,group,other) with
00449          * an R bit in the original permissions.    If you want to do anything
00450          * fancy like setgid or sticky bits, do it by hand.
00451          */
00452         int dirperm = permissions;
00453         if (permissions & S_IRUSR)
00454             dirperm |= S_IXUSR;
00455         if (permissions & S_IRGRP)
00456             dirperm |= S_IXGRP;
00457         if (permissions & S_IROTH)
00458             dirperm |= S_IXOTH;
00459 
00460 #ifdef DEBUG_NSIFILE
00461         fprintf(stderr, "nsIFile: perm = %o, dirperm = %o\n", permissions,
00462                 dirperm);
00463 #endif
00464 
00465         if (NS_FAILED(CreateAllAncestors(dirperm)))
00466             return NS_ERROR_FAILURE;
00467 
00468 #ifdef DEBUG_NSIFILE
00469         fprintf(stderr, "nsIFile: Create(\"%s\") again\n", mPath.get());
00470 #endif
00471         result = createFunc(mPath.get(), flags, permissions, _retval);
00472     }
00473     return NSRESULT_FOR_RETURN(result);
00474 }
00475 
00476 NS_IMETHODIMP
00477 nsLocalFile::Create(PRUint32 type, PRUint32 permissions)
00478 {
00479     PRFileDesc *junk = nsnull;
00480     nsresult rv = CreateAndKeepOpen(type,
00481                                     PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE |
00482                                     PR_EXCL,
00483                                     permissions,
00484                                     &junk);
00485     if (junk)
00486         PR_Close(junk);
00487     return rv;
00488 }
00489 
00490 NS_IMETHODIMP
00491 nsLocalFile::AppendNative(const nsACString &fragment)
00492 {
00493     if (fragment.IsEmpty())
00494         return NS_OK;
00495 
00496     // only one component of path can be appended
00497     nsACString::const_iterator begin, end;
00498     if (FindCharInReadable('/', fragment.BeginReading(begin),
00499                                 fragment.EndReading(end)))
00500         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
00501 
00502     return AppendRelativeNativePath(fragment);
00503 }
00504 
00505 NS_IMETHODIMP
00506 nsLocalFile::AppendRelativeNativePath(const nsACString &fragment)
00507 {
00508     if (fragment.IsEmpty())
00509         return NS_OK;
00510 
00511     // No leading '/' 
00512     if (fragment.First() == '/')
00513         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
00514 
00515     if (mPath.EqualsLiteral("/"))
00516         mPath.Append(fragment);
00517     else
00518         mPath.Append(NS_LITERAL_CSTRING("/") + fragment);
00519 
00520     InvalidateCache();
00521     return NS_OK;
00522 }
00523 
00524 NS_IMETHODIMP
00525 nsLocalFile::Normalize()
00526 {
00527     char    resolved_path[PATH_MAX] = "";
00528     char *resolved_path_ptr = nsnull;
00529 
00530 #ifdef XP_BEOS
00531     BEntry be_e(mPath.get(), true);
00532     BPath be_p;
00533     status_t err;
00534     if ((err = be_e.GetPath(&be_p)) == B_OK) {
00535         resolved_path_ptr = (char *)be_p.Path();
00536         PL_strncpyz(resolved_path, resolved_path_ptr, PATH_MAX - 1);
00537     }
00538 #else
00539     resolved_path_ptr = realpath(mPath.get(), resolved_path);
00540 #endif
00541     // if there is an error, the return is null.
00542     if (!resolved_path_ptr)
00543         return NSRESULT_FOR_ERRNO();
00544 
00545     mPath = resolved_path;
00546     return NS_OK;
00547 }
00548 
00549 void
00550 nsLocalFile::LocateNativeLeafName(nsACString::const_iterator &begin, 
00551                                   nsACString::const_iterator &end)
00552 {
00553     // XXX perhaps we should cache this??
00554 
00555     mPath.BeginReading(begin);
00556     mPath.EndReading(end);
00557     
00558     nsACString::const_iterator it = end;
00559     nsACString::const_iterator stop = begin;
00560     --stop;
00561     while (--it != stop) {
00562         if (*it == '/') {
00563             begin = ++it;
00564             return;
00565         }
00566     }
00567     // else, the entire path is the leaf name (which means this
00568     // isn't an absolute path... unexpected??)
00569 }
00570 
00571 NS_IMETHODIMP
00572 nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
00573 {
00574     nsACString::const_iterator begin, end;
00575     LocateNativeLeafName(begin, end);
00576     aLeafName = Substring(begin, end);
00577     return NS_OK;
00578 }
00579 
00580 NS_IMETHODIMP
00581 nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
00582 {
00583     nsACString::const_iterator begin, end;
00584     LocateNativeLeafName(begin, end);
00585     mPath.Replace(begin.get() - mPath.get(), Distance(begin, end), aLeafName);
00586     InvalidateCache();
00587     return NS_OK;
00588 }
00589 
00590 NS_IMETHODIMP
00591 nsLocalFile::GetNativePath(nsACString &_retval)
00592 {
00593     _retval = mPath;
00594     return NS_OK;
00595 }
00596 
00597 nsresult
00598 nsLocalFile::GetNativeTargetPathName(nsIFile *newParent, 
00599                                      const nsACString &newName,
00600                                      nsACString &_retval)
00601 {
00602     nsresult rv;
00603     nsCOMPtr<nsIFile> oldParent;
00604 
00605     if (!newParent) {
00606         if (NS_FAILED(rv = GetParent(getter_AddRefs(oldParent))))
00607             return rv;
00608         newParent = oldParent.get();
00609     } else {
00610         // check to see if our target directory exists
00611         PRBool targetExists;
00612         if (NS_FAILED(rv = newParent->Exists(&targetExists)))
00613             return rv;
00614 
00615         if (!targetExists) {
00616             // XXX create the new directory with some permissions
00617             rv = newParent->Create(DIRECTORY_TYPE, 0755);
00618             if (NS_FAILED(rv))
00619                 return rv;
00620         } else {
00621             // make sure that the target is actually a directory
00622             PRBool targetIsDirectory;
00623             if (NS_FAILED(rv = newParent->IsDirectory(&targetIsDirectory)))
00624                 return rv;
00625             if (!targetIsDirectory)
00626                 return NS_ERROR_FILE_DESTINATION_NOT_DIR;
00627         }
00628     }
00629 
00630     nsACString::const_iterator nameBegin, nameEnd;
00631     if (!newName.IsEmpty()) {
00632         newName.BeginReading(nameBegin);
00633         newName.EndReading(nameEnd);
00634     }
00635     else
00636         LocateNativeLeafName(nameBegin, nameEnd);
00637 
00638     nsCAutoString dirName;
00639     if (NS_FAILED(rv = newParent->GetNativePath(dirName)))
00640         return rv;
00641 
00642     _retval = dirName
00643             + NS_LITERAL_CSTRING("/")
00644             + Substring(nameBegin, nameEnd);
00645     return NS_OK;
00646 }
00647 
00648 nsresult
00649 nsLocalFile::CopyDirectoryTo(nsIFile *newParent)
00650 {
00651     nsresult rv;
00652     /*
00653      * dirCheck is used for various boolean test results such as from Equals,
00654      * Exists, isDir, etc.
00655      */
00656     PRBool dirCheck, isSymlink;
00657     PRUint32 oldPerms;
00658 
00659     if NS_FAILED((rv = IsDirectory(&dirCheck)))
00660         return rv;
00661     if (!dirCheck)
00662         return CopyToNative(newParent, EmptyCString());
00663     
00664     if (NS_FAILED(rv = Equals(newParent, &dirCheck)))
00665         return rv;
00666     if (dirCheck) { 
00667         // can't copy dir to itself
00668         return NS_ERROR_INVALID_ARG;
00669     }
00670     
00671     if (NS_FAILED(rv = newParent->Exists(&dirCheck))) 
00672         return rv;
00673     if (!dirCheck) {
00674         // get the dirs old permissions
00675         if (NS_FAILED(rv = GetPermissions(&oldPerms)))
00676             return rv;
00677         if (NS_FAILED(rv = newParent->Create(DIRECTORY_TYPE, oldPerms)))
00678             return rv;
00679     } else {    // dir exists lets try to use leaf
00680         nsCAutoString leafName;
00681         if (NS_FAILED(rv = GetNativeLeafName(leafName)))
00682             return rv;
00683         if (NS_FAILED(rv = newParent->AppendNative(leafName)))
00684             return rv;
00685         if (NS_FAILED(rv = newParent->Exists(&dirCheck)))
00686             return rv;
00687         if (dirCheck) 
00688             return NS_ERROR_FILE_ALREADY_EXISTS; // dest exists
00689         if (NS_FAILED(rv = newParent->Create(DIRECTORY_TYPE, oldPerms)))
00690             return rv;
00691     }
00692 
00693     nsCOMPtr<nsISimpleEnumerator> dirIterator;
00694     if (NS_FAILED(rv = GetDirectoryEntries(getter_AddRefs(dirIterator))))
00695         return rv;
00696 
00697     PRBool hasMore = PR_FALSE;
00698     while (dirIterator->HasMoreElements(&hasMore), hasMore) {
00699         nsCOMPtr<nsIFile> entry;
00700         rv = dirIterator->GetNext((nsISupports**)getter_AddRefs(entry));
00701         if (NS_FAILED(rv)) 
00702             continue;
00703         if (NS_FAILED(rv = entry->IsSymlink(&isSymlink)))
00704             return rv;
00705         if (NS_FAILED(rv = entry->IsDirectory(&dirCheck)))
00706             return rv;
00707         if (dirCheck && !isSymlink) {
00708             nsCOMPtr<nsIFile> destClone;
00709             rv = newParent->Clone(getter_AddRefs(destClone));
00710             if (NS_SUCCEEDED(rv)) {
00711                 nsCOMPtr<nsILocalFile> newDir(do_QueryInterface(destClone));
00712                 if (NS_FAILED(rv = entry->CopyToNative(newDir, EmptyCString()))) {
00713 #ifdef DEBUG
00714                     nsresult rv2;
00715                     nsCAutoString pathName;
00716                     if (NS_FAILED(rv2 = entry->GetNativePath(pathName)))
00717                         return rv2;
00718                     printf("Operation not supported: %s\n", pathName.get());
00719 #endif
00720                     if (rv == NS_ERROR_OUT_OF_MEMORY) 
00721                         return rv;
00722                     continue;
00723                 }
00724             }
00725         } else {
00726             if (NS_FAILED(rv = entry->CopyToNative(newParent, EmptyCString()))) {
00727 #ifdef DEBUG
00728                 nsresult rv2;
00729                 nsCAutoString pathName;
00730                 if (NS_FAILED(rv2 = entry->GetNativePath(pathName)))
00731                     return rv2;
00732                 printf("Operation not supported: %s\n", pathName.get());
00733 #endif
00734                 if (rv == NS_ERROR_OUT_OF_MEMORY) 
00735                     return rv;
00736                 continue;
00737             }
00738         }
00739     }
00740     return NS_OK;
00741 }
00742 
00743 NS_IMETHODIMP
00744 nsLocalFile::CopyToNative(nsIFile *newParent, const nsACString &newName)
00745 {
00746     nsresult rv;
00747     // check to make sure that this has been initialized properly
00748     CHECK_mPath();
00749 
00750     // we copy the parent here so 'newParent' remains immutable
00751     nsCOMPtr <nsIFile> workParent;
00752     if (newParent) {
00753         if (NS_FAILED(rv = newParent->Clone(getter_AddRefs(workParent))))
00754             return rv;
00755     } else {
00756         if (NS_FAILED(rv = GetParent(getter_AddRefs(workParent))))
00757             return rv;
00758     }
00759     
00760     // check to see if we are a directory or if we are a file
00761     PRBool isDirectory;
00762     if (NS_FAILED(rv = IsDirectory(&isDirectory)))
00763         return rv;
00764 
00765     nsCAutoString newPathName;
00766     if (isDirectory) {
00767         if (!newName.IsEmpty()) {
00768             if (NS_FAILED(rv = workParent->AppendNative(newName)))
00769                 return rv;
00770         } else {
00771             if (NS_FAILED(rv = GetNativeLeafName(newPathName)))
00772                 return rv;
00773             if (NS_FAILED(rv = workParent->AppendNative(newPathName)))
00774                 return rv;
00775         }
00776         if (NS_FAILED(rv = CopyDirectoryTo(workParent)))
00777             return rv;
00778     } else {
00779         rv = GetNativeTargetPathName(workParent, newName, newPathName);
00780         if (NS_FAILED(rv)) 
00781             return rv;
00782 
00783 #ifdef DEBUG_blizzard
00784         printf("nsLocalFile::CopyTo() %s -> %s\n", mPath.get(), newPathName.get());
00785 #endif
00786 
00787         // actually create the file.
00788         nsLocalFile *newFile = new nsLocalFile();
00789         if (!newFile)
00790             return NS_ERROR_OUT_OF_MEMORY;
00791 
00792         nsCOMPtr<nsILocalFile> fileRef(newFile); // release on exit
00793 
00794         rv = newFile->InitWithNativePath(newPathName);
00795         if (NS_FAILED(rv))
00796             return rv;
00797 
00798         // get the old permissions
00799         PRUint32 myPerms;
00800         GetPermissions(&myPerms);
00801 
00802         // Create the new file with the old file's permissions, even if write
00803         // permission is missing.  We can't create with write permission and
00804         // then change back to myPerm on all filesystems (FAT on Linux, e.g.).
00805         // But we can write to a read-only file on all Unix filesystems if we
00806         // open it successfully for writing.
00807 
00808         PRFileDesc *newFD;
00809         rv = newFile->CreateAndKeepOpen(NORMAL_FILE_TYPE,
00810                                         PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE,
00811                                         myPerms,
00812                                         &newFD);
00813         if (NS_FAILED(rv))
00814             return rv;
00815 
00816         // open the old file, too
00817         PRBool specialFile;
00818         if (NS_FAILED(rv = IsSpecial(&specialFile))) {
00819             PR_Close(newFD);
00820             return rv;
00821         }
00822         if (specialFile) {
00823 #ifdef DEBUG
00824             printf("Operation not supported: %s\n", mPath.get());
00825 #endif
00826             // make sure to clean up properly
00827             PR_Close(newFD);
00828             return NS_OK;
00829         }
00830                
00831         PRFileDesc *oldFD;
00832         rv = OpenNSPRFileDesc(PR_RDONLY, myPerms, &oldFD);
00833         if (NS_FAILED(rv)) {
00834             // make sure to clean up properly
00835             PR_Close(newFD);
00836             return rv;
00837         }
00838 
00839 #ifdef DEBUG_blizzard
00840         PRInt32 totalRead = 0;
00841         PRInt32 totalWritten = 0;
00842 #endif
00843         char buf[BUFSIZ];
00844         PRInt32 bytesRead;
00845         
00846         while ((bytesRead = PR_Read(oldFD, buf, BUFSIZ)) > 0) {
00847 #ifdef DEBUG_blizzard
00848             totalRead += bytesRead;
00849 #endif
00850 
00851             // PR_Write promises never to do a short write
00852             PRInt32 bytesWritten = PR_Write(newFD, buf, bytesRead);
00853             if (bytesWritten < 0) {
00854                 bytesRead = -1;
00855                 break;
00856             }
00857             NS_ASSERTION(bytesWritten == bytesRead, "short PR_Write?");
00858 
00859 #ifdef DEBUG_blizzard
00860             totalWritten += bytesWritten;
00861 #endif
00862         }
00863 
00864 #ifdef DEBUG_blizzard
00865         printf("read %d bytes, wrote %d bytes\n",
00866                  totalRead, totalWritten);
00867 #endif
00868 
00869         // close the files
00870         PR_Close(newFD);
00871         PR_Close(oldFD);
00872 
00873         // check for read (or write) error after cleaning up
00874         if (bytesRead < 0) 
00875             return NS_ERROR_OUT_OF_MEMORY;
00876     }
00877     return rv;
00878 }
00879 
00880 NS_IMETHODIMP
00881 nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParent, const nsACString &newName)
00882 {
00883     return CopyToNative(newParent, newName);
00884 }
00885 
00886 NS_IMETHODIMP
00887 nsLocalFile::MoveToNative(nsIFile *newParent, const nsACString &newName)
00888 {
00889     nsresult rv;
00890 
00891     // check to make sure that this has been initialized properly
00892     CHECK_mPath();
00893 
00894     // check to make sure that we have a new parent
00895     nsCAutoString newPathName;
00896     rv = GetNativeTargetPathName(newParent, newName, newPathName);
00897     if (NS_FAILED(rv))
00898         return rv;
00899 
00900     // try for atomic rename, falling back to copy/delete
00901     if (rename(mPath.get(), newPathName.get()) < 0) {
00902 #ifdef VMS
00903         if (errno == EXDEV || errno == ENXIO) {
00904 #else
00905         if (errno == EXDEV) {
00906 #endif
00907             rv = CopyToNative(newParent, newName);
00908             if (NS_SUCCEEDED(rv))
00909                 rv = Remove(PR_TRUE);
00910         } else {
00911             rv = NSRESULT_FOR_ERRNO();
00912         }
00913     }
00914     return rv;
00915 }
00916 
00917 NS_IMETHODIMP
00918 nsLocalFile::Remove(PRBool recursive)
00919 {
00920     CHECK_mPath();
00921 
00922     VALIDATE_STAT_CACHE();
00923     PRBool isSymLink, isDir;
00924     
00925     nsresult rv = IsSymlink(&isSymLink);
00926     if (NS_FAILED(rv))
00927         return rv;
00928 
00929     if (!recursive && isSymLink)
00930         return NSRESULT_FOR_RETURN(unlink(mPath.get()));
00931     
00932     isDir = S_ISDIR(mCachedStat.st_mode);
00933     InvalidateCache();
00934     if (isDir) {
00935         if (recursive) {
00936             nsDirEnumeratorUnix *dir = new nsDirEnumeratorUnix();
00937             if (!dir)
00938                 return NS_ERROR_OUT_OF_MEMORY;
00939 
00940             nsCOMPtr<nsISimpleEnumerator> dirRef(dir); // release on exit
00941 
00942             rv = dir->Init(this, PR_FALSE);
00943             if (NS_FAILED(rv))
00944                 return rv;
00945 
00946             PRBool more;
00947             while (dir->HasMoreElements(&more), more) {
00948                 nsCOMPtr<nsISupports> item;
00949                 rv = dir->GetNext(getter_AddRefs(item));
00950                 if (NS_FAILED(rv))
00951                     return NS_ERROR_FAILURE;
00952 
00953                 nsCOMPtr<nsIFile> file = do_QueryInterface(item, &rv);
00954                 if (NS_FAILED(rv))
00955                     return NS_ERROR_FAILURE;
00956                 if (NS_FAILED(rv = file->Remove(recursive)))
00957                     return rv;
00958             }
00959         }
00960 
00961         if (rmdir(mPath.get()) == -1)
00962             return NSRESULT_FOR_ERRNO();
00963     } else {
00964         if (unlink(mPath.get()) == -1)
00965             return NSRESULT_FOR_ERRNO();
00966     }
00967 
00968     return NS_OK;
00969 }
00970 
00971 NS_IMETHODIMP
00972 nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModTime)
00973 {
00974     CHECK_mPath();
00975     NS_ENSURE_ARG(aLastModTime);
00976 
00977     PRFileInfo64 info;
00978     if (PR_GetFileInfo64(mPath.get(), &info) != PR_SUCCESS)
00979         return NSRESULT_FOR_ERRNO();
00980 
00981     // PRTime is a 64 bit value
00982     // microseconds -> milliseconds
00983     PRInt64 usecPerMsec;
00984     LL_I2L(usecPerMsec, PR_USEC_PER_MSEC);
00985     LL_DIV(*aLastModTime, info.modifyTime, usecPerMsec);
00986     return NS_OK;
00987 }
00988 
00989 NS_IMETHODIMP
00990 nsLocalFile::SetLastModifiedTime(PRInt64 aLastModTime)
00991 {
00992     CHECK_mPath();
00993 
00994     int result;
00995     if (! LL_IS_ZERO(aLastModTime)) {
00996         VALIDATE_STAT_CACHE();
00997         struct utimbuf ut;
00998         ut.actime = mCachedStat.st_atime;
00999 
01000         // convert milliseconds to seconds since the unix epoch
01001         double dTime;
01002         LL_L2D(dTime, aLastModTime);
01003         ut.modtime = (time_t) (dTime / PR_MSEC_PER_SEC);
01004         result = utime(mPath.get(), &ut);
01005     } else {
01006         result = utime(mPath.get(), nsnull);
01007     }
01008     InvalidateCache();
01009     return NSRESULT_FOR_RETURN(result);
01010 }
01011 
01012 NS_IMETHODIMP
01013 nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModTimeOfLink)
01014 {
01015     CHECK_mPath();
01016     NS_ENSURE_ARG(aLastModTimeOfLink);
01017 
01018     struct stat sbuf;
01019     if (lstat(mPath.get(), &sbuf) == -1)
01020         return NSRESULT_FOR_ERRNO();
01021     LL_I2L(*aLastModTimeOfLink, (PRInt32)sbuf.st_mtime);
01022 
01023     // lstat returns st_mtime in seconds
01024     PRInt64 msecPerSec;
01025     LL_I2L(msecPerSec, PR_MSEC_PER_SEC);
01026     LL_MUL(*aLastModTimeOfLink, *aLastModTimeOfLink, msecPerSec);
01027 
01028     return NS_OK;
01029 }
01030 
01031 /*
01032  * utime(2) may or may not dereference symlinks, joy.
01033  */
01034 NS_IMETHODIMP
01035 nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModTimeOfLink)
01036 {
01037     return SetLastModifiedTime(aLastModTimeOfLink);
01038 }
01039 
01040 /*
01041  * Only send back permissions bits: maybe we want to send back the whole
01042  * mode_t to permit checks against other file types?
01043  */
01044 
01045 #define NORMALIZE_PERMS(mode)    ((mode)& (S_IRWXU | S_IRWXG | S_IRWXO))
01046 
01047 NS_IMETHODIMP
01048 nsLocalFile::GetPermissions(PRUint32 *aPermissions)
01049 {
01050     NS_ENSURE_ARG(aPermissions);
01051     VALIDATE_STAT_CACHE();
01052     *aPermissions = NORMALIZE_PERMS(mCachedStat.st_mode);
01053     return NS_OK;
01054 }
01055 
01056 NS_IMETHODIMP
01057 nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink)
01058 {
01059     CHECK_mPath();
01060     NS_ENSURE_ARG(aPermissionsOfLink);
01061 
01062     struct stat sbuf;
01063     if (lstat(mPath.get(), &sbuf) == -1)
01064         return NSRESULT_FOR_ERRNO();
01065     *aPermissionsOfLink = NORMALIZE_PERMS(sbuf.st_mode);
01066     return NS_OK;
01067 }
01068 
01069 NS_IMETHODIMP
01070 nsLocalFile::SetPermissions(PRUint32 aPermissions)
01071 {
01072     CHECK_mPath();
01073 
01074     InvalidateCache();
01075 
01076     /*
01077      * Race condition here: we should use fchmod instead, there's no way to 
01078      * guarantee the name still refers to the same file.
01079      */
01080     if (chmod(mPath.get(), aPermissions) < 0)
01081         return NSRESULT_FOR_ERRNO();
01082     return NS_OK;
01083 }
01084 
01085 NS_IMETHODIMP
01086 nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions)
01087 {
01088     return SetPermissions(aPermissions);
01089 }
01090 
01091 NS_IMETHODIMP
01092 nsLocalFile::GetFileSize(PRInt64 *aFileSize)
01093 {
01094     NS_ENSURE_ARG_POINTER(aFileSize);
01095     *aFileSize = LL_ZERO;
01096     VALIDATE_STAT_CACHE();
01097 
01098 #if defined(VMS)
01099     /* Only two record formats can report correct file content size */
01100     if ((mCachedStat.st_fab_rfm != FAB$C_STMLF) &&
01101         (mCachedStat.st_fab_rfm != FAB$C_STMCR)) {
01102         return NS_ERROR_FAILURE;
01103     }
01104 #endif
01105 
01106     /* XXX autoconf for and use stat64 if available */
01107     if (!S_ISDIR(mCachedStat.st_mode)) {
01108         LL_UI2L(*aFileSize, (PRUint32)mCachedStat.st_size);
01109     }
01110     return NS_OK;
01111 }
01112 
01113 NS_IMETHODIMP
01114 nsLocalFile::SetFileSize(PRInt64 aFileSize)
01115 {
01116     CHECK_mPath();
01117 
01118     PRInt32 size;
01119     LL_L2I(size, aFileSize);
01120     /* XXX truncate64? */
01121     InvalidateCache();
01122     if (truncate(mPath.get(), (off_t)size) == -1)
01123         return NSRESULT_FOR_ERRNO();
01124     return NS_OK;
01125 }
01126 
01127 NS_IMETHODIMP
01128 nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSize)
01129 {
01130     CHECK_mPath();
01131     NS_ENSURE_ARG(aFileSize);
01132 
01133     struct stat sbuf;
01134     if (lstat(mPath.get(), &sbuf) == -1)
01135         return NSRESULT_FOR_ERRNO();
01136     /* XXX autoconf for and use lstat64 if available */
01137     LL_UI2L(*aFileSize, (PRUint32)sbuf.st_size);
01138     return NS_OK;
01139 }
01140 
01141 NS_IMETHODIMP
01142 nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
01143 {
01144     NS_ENSURE_ARG_POINTER(aDiskSpaceAvailable);
01145 
01146     // These systems have the operations necessary to check disk space.
01147 
01148 #if defined(HAVE_SYS_STATFS_H) || defined(HAVE_SYS_STATVFS_H)
01149 
01150     // check to make sure that mPath is properly initialized
01151     CHECK_mPath();
01152 
01153     struct STATFS fs_buf;
01154 
01155     /* 
01156      * Members of the STATFS struct that you should know about:
01157      * f_bsize = block size on disk.
01158      * f_bavail = number of free blocks available to a non-superuser.
01159      * f_bfree = number of total free blocks in file system.
01160      */
01161 
01162     if (STATFS(mPath.get(), &fs_buf) < 0) {
01163         // The call to STATFS failed.
01164 #ifdef DEBUG
01165         printf("ERROR: GetDiskSpaceAvailable: STATFS call FAILED. \n");
01166 #endif
01167         return NS_ERROR_FAILURE;
01168     }
01169 #ifdef DEBUG_DISK_SPACE
01170     printf("DiskSpaceAvailable: %d bytes\n",
01171          fs_buf.f_bsize * (fs_buf.f_bavail - 1));
01172 #endif
01173 
01174     /* 
01175      * The number of bytes free == The number of free blocks available to
01176      * a non-superuser, minus one as a fudge factor, multiplied by the size
01177      * of the aforementioned blocks.
01178      */
01179     PRInt64 bsize, bavail;
01180 
01181     LL_I2L(bsize, fs_buf.f_bsize);
01182     LL_I2L(bavail, fs_buf.f_bavail - 1);
01183     LL_MUL(*aDiskSpaceAvailable, bsize, bavail);
01184     return NS_OK;
01185 
01186 #else
01187     /*
01188      * This platform doesn't have statfs or statvfs.  I'm sure that there's
01189      * a way to check for free disk space on platforms that don't have statfs
01190      * (I'm SURE they have df, for example).
01191      *
01192      * Until we figure out how to do that, lets be honest and say that this
01193      * command isn't implemented properly for these platforms yet.
01194      */
01195 #ifdef DEBUG
01196     printf("ERROR: GetDiskSpaceAvailable: Not implemented for plaforms without statfs.\n");
01197 #endif
01198     return NS_ERROR_NOT_IMPLEMENTED;
01199 
01200 #endif /* HAVE_SYS_STATFS_H or HAVE_SYS_STATVFS_H */
01201 
01202 }
01203 
01204 NS_IMETHODIMP
01205 nsLocalFile::GetParent(nsIFile **aParent)
01206 {
01207     CHECK_mPath();
01208     NS_ENSURE_ARG_POINTER(aParent);
01209     *aParent       = nsnull;
01210 
01211     // if '/' we are at the top of the volume, return null
01212     if (mPath.Equals("/"))
01213         return  NS_OK;
01214  
01215     // <brendan, after jband> I promise to play nice
01216     char *buffer   = mPath.BeginWriting(),
01217          *slashp   = buffer;
01218 
01219     // find the last significant slash in buffer
01220     slashp = strrchr(buffer, '/');
01221     NS_ASSERTION(slashp, "non-canonical mPath?");
01222     if (!slashp)
01223         return NS_ERROR_FILE_INVALID_PATH;
01224 
01225     // for the case where we are at '/'
01226     if (slashp == buffer)
01227         slashp++;
01228 
01229     // temporarily terminate buffer at the last significant slash
01230     char c = *slashp;
01231     *slashp = '\0';
01232 
01233     nsCOMPtr<nsILocalFile> localFile;
01234     nsresult rv = NS_NewNativeLocalFile(nsDependentCString(buffer), PR_TRUE,
01235                                         getter_AddRefs(localFile));
01236 
01237     // make buffer whole again
01238     *slashp = c;
01239 
01240     if (NS_SUCCEEDED(rv) && localFile)
01241         rv = CallQueryInterface(localFile, aParent);
01242     return rv;
01243 }
01244 
01245 /*
01246  * The results of Exists, isWritable and isReadable are not cached.
01247  */
01248 
01249 
01250 #if defined(XP_BEOS) || defined(SOLARIS)
01251 // access() is buggy in BeOS POSIX implementation, at least for BFS, using stat() instead
01252 // see bug 169506, https://bugzilla.mozilla.org/show_bug.cgi?id=169506
01253 // access() problem also exists in Solaris POSIX implementation
01254 // see bug 351595, https://bugzilla.mozilla.org/show_bug.cgi?id=351595
01255 NS_IMETHODIMP
01256 nsLocalFile::Exists(PRBool *_retval)
01257 {
01258     CHECK_mPath();
01259     NS_ENSURE_ARG_POINTER(_retval);
01260     struct stat buf;
01261 
01262     *_retval = (stat(mPath.get(), &buf) == 0);
01263     return NS_OK;
01264 }
01265 
01266 NS_IMETHODIMP
01267 nsLocalFile::IsWritable(PRBool *_retval)
01268 {
01269     CHECK_mPath();
01270     NS_ENSURE_ARG_POINTER(_retval);
01271     struct stat buf;
01272 
01273     *_retval = (stat(mPath.get(), &buf) == 0);
01274     if (*_retval || errno == EACCES) {
01275         *_retval = *_retval && (buf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH ));
01276         return NS_OK;
01277     }
01278     return NSRESULT_FOR_ERRNO();
01279 }
01280 
01281 NS_IMETHODIMP
01282 nsLocalFile::IsReadable(PRBool *_retval)
01283 {
01284     CHECK_mPath();
01285     NS_ENSURE_ARG_POINTER(_retval);
01286     struct stat buf;
01287 
01288     *_retval = (stat(mPath.get(), &buf) == 0);
01289     if (*_retval || errno == EACCES) {
01290         *_retval = *_retval && (buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH ));
01291         return NS_OK;
01292     }
01293     return NSRESULT_FOR_ERRNO();
01294 }
01295 
01296 NS_IMETHODIMP
01297 nsLocalFile::IsExecutable(PRBool *_retval)
01298 {
01299     CHECK_mPath();
01300     NS_ENSURE_ARG_POINTER(_retval);
01301     struct stat buf;
01302 
01303     *_retval = (stat(mPath.get(), &buf) == 0);
01304     if (*_retval || errno == EACCES) {
01305         *_retval = *_retval && (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH ));
01306         return NS_OK;
01307     }
01308     return NSRESULT_FOR_ERRNO();
01309 }
01310 #else
01311 
01312 NS_IMETHODIMP
01313 nsLocalFile::Exists(PRBool *_retval)
01314 {
01315     CHECK_mPath();
01316     NS_ENSURE_ARG_POINTER(_retval);
01317 
01318     *_retval = (access(mPath.get(), F_OK) == 0);
01319     return NS_OK;
01320 }
01321 
01322 
01323 NS_IMETHODIMP
01324 nsLocalFile::IsWritable(PRBool *_retval)
01325 {
01326     CHECK_mPath();
01327     NS_ENSURE_ARG_POINTER(_retval);
01328 
01329     *_retval = (access(mPath.get(), W_OK) == 0);
01330     if (*_retval || errno == EACCES)
01331         return NS_OK;
01332     return NSRESULT_FOR_ERRNO();
01333 }
01334 
01335 NS_IMETHODIMP
01336 nsLocalFile::IsReadable(PRBool *_retval)
01337 {
01338     CHECK_mPath();
01339     NS_ENSURE_ARG_POINTER(_retval);
01340 
01341     *_retval = (access(mPath.get(), R_OK) == 0);
01342     if (*_retval || errno == EACCES)
01343         return NS_OK;
01344     return NSRESULT_FOR_ERRNO();
01345 }
01346 
01347 NS_IMETHODIMP
01348 nsLocalFile::IsExecutable(PRBool *_retval)
01349 {
01350     CHECK_mPath();
01351     NS_ENSURE_ARG_POINTER(_retval);
01352 
01353     *_retval = (access(mPath.get(), X_OK) == 0);
01354     if (*_retval || errno == EACCES)
01355         return NS_OK;
01356     return NSRESULT_FOR_ERRNO();
01357 }
01358 #endif
01359 NS_IMETHODIMP
01360 nsLocalFile::IsDirectory(PRBool *_retval)
01361 {
01362     NS_ENSURE_ARG_POINTER(_retval);
01363     *_retval = PR_FALSE;
01364     VALIDATE_STAT_CACHE();
01365     *_retval = S_ISDIR(mCachedStat.st_mode);
01366     return NS_OK;
01367 }
01368 
01369 NS_IMETHODIMP
01370 nsLocalFile::IsFile(PRBool *_retval)
01371 {
01372     NS_ENSURE_ARG_POINTER(_retval);
01373     *_retval = PR_FALSE;
01374     VALIDATE_STAT_CACHE();
01375     *_retval = S_ISREG(mCachedStat.st_mode);
01376     return NS_OK;
01377 }
01378 
01379 NS_IMETHODIMP
01380 nsLocalFile::IsHidden(PRBool *_retval)
01381 {
01382     NS_ENSURE_ARG_POINTER(_retval);
01383     nsACString::const_iterator begin, end;
01384     LocateNativeLeafName(begin, end);
01385     *_retval = (*begin == '.');
01386     return NS_OK;
01387 }
01388 
01389 NS_IMETHODIMP
01390 nsLocalFile::IsSymlink(PRBool *_retval)
01391 {
01392     NS_ENSURE_ARG_POINTER(_retval);
01393     CHECK_mPath();
01394 
01395     struct stat symStat;
01396     lstat(mPath.get(), &symStat);
01397     *_retval=S_ISLNK(symStat.st_mode);
01398     return NS_OK;
01399 }
01400 
01401 NS_IMETHODIMP
01402 nsLocalFile::IsSpecial(PRBool *_retval)
01403 {
01404     NS_ENSURE_ARG_POINTER(_retval);
01405     VALIDATE_STAT_CACHE();
01406     *_retval = S_ISCHR(mCachedStat.st_mode)      ||
01407                  S_ISBLK(mCachedStat.st_mode)    ||
01408 #ifdef S_ISSOCK
01409                  S_ISSOCK(mCachedStat.st_mode)   ||
01410 #endif
01411                  S_ISFIFO(mCachedStat.st_mode);
01412 
01413     return NS_OK;
01414 }
01415 
01416 NS_IMETHODIMP
01417 nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
01418 {
01419     NS_ENSURE_ARG(inFile);
01420     NS_ENSURE_ARG_POINTER(_retval);
01421     *_retval = PR_FALSE;
01422 
01423     nsresult rv;
01424     nsCAutoString inPath;
01425 
01426     if (NS_FAILED(rv = inFile->GetNativePath(inPath)))
01427         return rv;
01428 
01429     *_retval = !FILE_STRCMP(inPath.get(), mPath.get());
01430     return NS_OK;
01431 }
01432 
01433 NS_IMETHODIMP
01434 nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval)
01435 {
01436     CHECK_mPath();
01437     NS_ENSURE_ARG(inFile);
01438     NS_ENSURE_ARG_POINTER(_retval);
01439 
01440     nsCAutoString inPath;
01441     nsresult rv;
01442 
01443     if (NS_FAILED(rv = inFile->GetNativePath(inPath)))
01444         return rv;
01445 
01446     *_retval = PR_FALSE;
01447 
01448     ssize_t len = mPath.Length();
01449     if (FILE_STRNCMP(mPath.get(), inPath.get(), len) == 0) {
01450         // Now make sure that the |inFile|'s path has a separator at len,
01451         // which implies that it has more components after len.
01452         if (inPath[len] == '/')
01453             *_retval = PR_TRUE;
01454     }
01455 
01456     return NS_OK;
01457 }
01458 
01459 NS_IMETHODIMP
01460 nsLocalFile::GetNativeTarget(nsACString &_retval)
01461 {
01462     CHECK_mPath();
01463     _retval.Truncate();
01464 
01465     struct stat symStat;
01466     lstat(mPath.get(), &symStat);
01467     if (!S_ISLNK(symStat.st_mode))
01468         return NS_ERROR_FILE_INVALID_PATH;
01469 
01470     PRInt64 targetSize64;
01471     if (NS_FAILED(GetFileSizeOfLink(&targetSize64)))
01472         return NS_ERROR_FAILURE;
01473 
01474     PRInt32 size;
01475     LL_L2I(size, targetSize64);
01476     char *target = (char *)nsMemory::Alloc(size + 1);
01477     if (!target)
01478         return NS_ERROR_OUT_OF_MEMORY;
01479 
01480     if (readlink(mPath.get(), target, (size_t)size) < 0) {
01481         nsMemory::Free(target);
01482         return NSRESULT_FOR_ERRNO();
01483     }
01484     target[size] = '\0';
01485 
01486     nsresult rv;
01487     PRBool isSymlink;
01488     nsCOMPtr<nsIFile> self(this);
01489     nsCOMPtr<nsIFile> parent;
01490     while (NS_SUCCEEDED(rv = self->GetParent(getter_AddRefs(parent)))) {
01491         NS_ASSERTION(parent != nsnull, "no parent?!");
01492 
01493         if (target[0] != '/') {
01494             nsCOMPtr<nsILocalFile> localFile(do_QueryInterface(parent, &rv));
01495             if (NS_FAILED(rv))
01496                 break;
01497             if (NS_FAILED(rv = localFile->AppendRelativeNativePath(nsDependentCString(target))))
01498                 break;
01499             if (NS_FAILED(rv = localFile->GetNativePath(_retval)))
01500                 break;
01501             if (NS_FAILED(rv = parent->IsSymlink(&isSymlink)))
01502                 break;
01503             self = parent;
01504         } else {
01505             nsCOMPtr<nsILocalFile> localFile;
01506             rv = NS_NewNativeLocalFile(nsDependentCString(target), PR_TRUE,
01507                                        getter_AddRefs(localFile));
01508             if (NS_FAILED(rv))
01509                 break;
01510             if (NS_FAILED(rv = localFile->IsSymlink(&isSymlink)))
01511                 break;
01512             _retval = target; // XXX can we avoid this buffer copy?
01513             self = do_QueryInterface(localFile);
01514         }
01515         if (NS_FAILED(rv) || !isSymlink)
01516             break;
01517 
01518         const nsPromiseFlatCString &flatRetval = PromiseFlatCString(_retval);
01519 
01520         // strip off any and all trailing '/'
01521         PRInt32 len = strlen(target);
01522         while (target[len-1] == '/' && len > 1)
01523             target[--len] = '\0';
01524         if (lstat(flatRetval.get(), &symStat) < 0) {
01525             rv = NSRESULT_FOR_ERRNO();
01526             break;
01527         }
01528         if (!S_ISLNK(symStat.st_mode)) {
01529             rv = NS_ERROR_FILE_INVALID_PATH;
01530             break;
01531         }
01532         size = symStat.st_size;
01533         if (readlink(flatRetval.get(), target, size) < 0) {
01534             rv = NSRESULT_FOR_ERRNO();
01535             break;
01536         }
01537         target[size] = '\0';
01538 
01539         _retval.Truncate();
01540     }
01541 
01542     nsMemory::Free(target);
01543 
01544     if (NS_FAILED(rv))
01545         _retval.Truncate();
01546     return rv;
01547 }
01548 
01549 /* attribute PRBool followLinks; */
01550 NS_IMETHODIMP
01551 nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
01552 {
01553     *aFollowLinks = PR_TRUE;
01554     return NS_OK;
01555 }
01556 
01557 NS_IMETHODIMP
01558 nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
01559 {
01560     return NS_OK;
01561 }
01562 
01563 NS_IMETHODIMP
01564 nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator **entries)
01565 {
01566     nsDirEnumeratorUnix *dir = new nsDirEnumeratorUnix();
01567     if (!dir)
01568         return NS_ERROR_OUT_OF_MEMORY;
01569 
01570     NS_ADDREF(dir);
01571     nsresult rv = dir->Init(this, PR_FALSE);
01572     if (NS_FAILED(rv)) {
01573         *entries = nsnull;
01574         NS_RELEASE(dir);
01575     } else {
01576         *entries = dir; // transfer reference
01577     }
01578 
01579     return rv;
01580 }
01581 
01582 NS_IMETHODIMP
01583 nsLocalFile::Load(PRLibrary **_retval)
01584 {
01585     CHECK_mPath();
01586     NS_ENSURE_ARG_POINTER(_retval);
01587 
01588     NS_TIMELINE_START_TIMER("PR_LoadLibrary");
01589 
01590     *_retval = PR_LoadLibrary(mPath.get());
01591 
01592     NS_TIMELINE_STOP_TIMER("PR_LoadLibrary");
01593     NS_TIMELINE_MARK_TIMER1("PR_LoadLibrary", mPath.get());
01594 
01595     if (!*_retval)
01596         return NS_ERROR_FAILURE;
01597     return NS_OK;
01598 }
01599 
01600 NS_IMETHODIMP
01601 nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
01602 {
01603     return GetNativePath(aPersistentDescriptor);
01604 }
01605 
01606 NS_IMETHODIMP
01607 nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
01608 {
01609     return InitWithNativePath(aPersistentDescriptor);
01610 }
01611 
01612 #ifdef XP_BEOS
01613 NS_IMETHODIMP
01614 nsLocalFile::Reveal()
01615 {
01616     BPath bPath(mPath.get());
01617     PRBool isDirectory;
01618     if (NS_FAILED(IsDirectory(&isDirectory)))
01619         return NS_ERROR_FAILURE;
01620   
01621     if(!isDirectory)
01622         bPath.GetParent(&bPath);
01623     entry_ref ref;
01624     get_ref_for_path(bPath.Path(),&ref);
01625     BMessage message(B_REFS_RECEIVED);
01626     message.AddRef("refs",&ref);
01627     BMessenger messenger("application/x-vnd.Be-TRAK");
01628     messenger.SendMessage(&message);
01629     return NS_OK;
01630 }
01631 
01632 NS_IMETHODIMP
01633 nsLocalFile::Launch()
01634 {
01635     entry_ref ref;
01636     get_ref_for_path (mPath.get(), &ref);
01637     be_roster->Launch (&ref);
01638 
01639     return NS_OK;
01640 }
01641 #else
01642 NS_IMETHODIMP
01643 nsLocalFile::Reveal()
01644 {
01645     return NS_ERROR_FAILURE;
01646 }
01647 
01648 NS_IMETHODIMP
01649 nsLocalFile::Launch()
01650 {
01651     return NS_ERROR_FAILURE;
01652 }
01653 #endif
01654 
01655 nsresult
01656 NS_NewNativeLocalFile(const nsACString &path, PRBool followSymlinks, nsILocalFile **result)
01657 {
01658     nsLocalFile *file = new nsLocalFile();
01659     if (!file)
01660         return NS_ERROR_OUT_OF_MEMORY;
01661     NS_ADDREF(file);
01662 
01663     if (!path.IsEmpty()) {
01664         nsresult rv = file->InitWithNativePath(path);
01665         if (NS_FAILED(rv)) {
01666             NS_RELEASE(file);
01667             return rv;
01668         }
01669     }
01670     *result = file;
01671     return NS_OK;
01672 }
01673 
01674 //-----------------------------------------------------------------------------
01675 // unicode support
01676 //-----------------------------------------------------------------------------
01677 
01678 #define SET_UCS(func, ucsArg) \
01679     { \
01680         nsCAutoString buf; \
01681         nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
01682         if (NS_FAILED(rv)) \
01683             return rv; \
01684         return (func)(buf); \
01685     }
01686 
01687 #define GET_UCS(func, ucsArg) \
01688     { \
01689         nsCAutoString buf; \
01690         nsresult rv = (func)(buf); \
01691         if (NS_FAILED(rv)) return rv; \
01692         return NS_CopyNativeToUnicode(buf, ucsArg); \
01693     }
01694 
01695 #define SET_UCS_2ARGS_2(func, opaqueArg, ucsArg) \
01696     { \
01697         nsCAutoString buf; \
01698         nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
01699         if (NS_FAILED(rv)) \
01700             return rv; \
01701         return (func)(opaqueArg, buf); \
01702     }
01703 
01704 // Unicode interface Wrapper
01705 nsresult  
01706 nsLocalFile::InitWithPath(const nsAString &filePath)
01707 {
01708     SET_UCS(InitWithNativePath, filePath);
01709 }
01710 nsresult  
01711 nsLocalFile::Append(const nsAString &node)
01712 {
01713     SET_UCS(AppendNative, node);
01714 }
01715 nsresult  
01716 nsLocalFile::AppendRelativePath(const nsAString &node)
01717 {
01718     SET_UCS(AppendRelativeNativePath, node);
01719 }
01720 nsresult  
01721 nsLocalFile::GetLeafName(nsAString &aLeafName)
01722 {
01723     GET_UCS(GetNativeLeafName, aLeafName);
01724 }
01725 nsresult  
01726 nsLocalFile::SetLeafName(const nsAString &aLeafName)
01727 {
01728     SET_UCS(SetNativeLeafName, aLeafName);
01729 }
01730 nsresult  
01731 nsLocalFile::GetPath(nsAString &_retval)
01732 {
01733     return NS_CopyNativeToUnicode(mPath, _retval);
01734 }
01735 nsresult  
01736 nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
01737 {
01738     SET_UCS_2ARGS_2(CopyToNative , newParentDir, newName);
01739 }
01740 nsresult  
01741 nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
01742 {
01743     SET_UCS_2ARGS_2(CopyToFollowingLinksNative , newParentDir, newName);
01744 }
01745 nsresult  
01746 nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
01747 {
01748     SET_UCS_2ARGS_2(MoveToNative, newParentDir, newName);
01749 }
01750 nsresult
01751 nsLocalFile::GetTarget(nsAString &_retval)
01752 {   
01753     GET_UCS(GetNativeTarget, _retval);
01754 }
01755 nsresult 
01756 NS_NewLocalFile(const nsAString &path, PRBool followLinks, nsILocalFile* *result)
01757 {
01758     nsCAutoString buf;
01759     nsresult rv = NS_CopyUnicodeToNative(path, buf);
01760     if (NS_FAILED(rv))
01761         return rv;
01762     return NS_NewNativeLocalFile(buf, followLinks, result);
01763 }
01764 
01765 //-----------------------------------------------------------------------------
01766 // global init/shutdown
01767 //-----------------------------------------------------------------------------
01768 
01769 void
01770 nsLocalFile::GlobalInit()
01771 {
01772 }
01773 
01774 void
01775 nsLocalFile::GlobalShutdown()
01776 {
01777 }