source: pkg/viola/main/xulrunner/branches/upstream/current/xpcom/io/nsLocalFileUnix.cpp @ 4081

Revision 4081, 48.4 KB checked in by alanbach-guest, 6 years ago (diff)

[svn-inject] Installing original source of xulrunner

Line 
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 
2/* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is Mozilla Communicator client code, released
16 * March 31, 1998.
17 *
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998-1999
21 * the Initial Developer. All Rights Reserved.
22 *
23 * Contributor(s):
24 *   Mike Shaver            <shaver@mozilla.org>
25 *   Christopher Blizzard   <blizzard@mozilla.org>
26 *   Jason Eager            <jce2@po.cwru.edu>
27 *   Stuart Parmenter       <pavlov@netscape.com>
28 *   Brendan Eich           <brendan@mozilla.org>
29 *   Pete Collins           <petejc@mozdev.org>
30 *   Paul Ashford           <arougthopher@lizardland.net>
31 *   Fredrik Holmqvist      <thesuckiestemail@yahoo.se>
32 *
33 * Alternatively, the contents of this file may be used under the terms of
34 * either of the GNU General Public License Version 2 or later (the "GPL"),
35 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
36 * in which case the provisions of the GPL or the LGPL are applicable instead
37 * of those above. If you wish to allow use of your version of this file only
38 * under the terms of either the GPL or the LGPL, and not to allow others to
39 * use your version of this file under the terms of the MPL, indicate your
40 * decision by deleting the provisions above and replace them with the notice
41 * and other provisions required by the GPL or the LGPL. If you do not delete
42 * the provisions above, a recipient may use your version of this file under
43 * the terms of any one of the MPL, the GPL or the LGPL.
44 *
45 * ***** END LICENSE BLOCK ***** */
46
47/**
48 * Implementation of nsIFile for ``Unixy'' systems.
49 */
50
51// We're going to need some autoconf loving, I can just tell.
52#include <sys/types.h>
53#include <sys/stat.h>
54#include <unistd.h>
55#include <fcntl.h>
56#include <errno.h>
57#include <utime.h>
58#include <dirent.h>
59#include <ctype.h>
60#include <locale.h>
61#ifdef XP_BEOS
62    #include <Path.h>
63    #include <Entry.h>
64    #include <Roster.h>
65#endif
66#if defined(VMS)
67    #include <fabdef.h>
68#endif
69
70#include "nsDirectoryServiceDefs.h"
71#include "nsCRT.h"
72#include "nsCOMPtr.h"
73#include "nsMemory.h"
74#include "nsIFile.h"
75#include "nsString.h"
76#include "nsReadableUtils.h"
77#include "nsLocalFile.h"
78#include "nsIComponentManager.h"
79#include "nsXPIDLString.h"
80#include "prproces.h"
81#include "nsIDirectoryEnumerator.h"
82#include "nsISimpleEnumerator.h"
83#include "nsITimelineService.h"
84
85#ifdef MOZ_WIDGET_GTK2
86#include "nsIGnomeVFSService.h"
87#endif
88
89#include "nsNativeCharsetUtils.h"
90#include "nsTraceRefcntImpl.h"
91
92// On some platforms file/directory name comparisons need to
93// be case-blind.
94#if defined(VMS)
95    #define FILE_STRCMP strcasecmp
96    #define FILE_STRNCMP strncasecmp
97#else
98    #define FILE_STRCMP strcmp
99    #define FILE_STRNCMP strncmp
100#endif
101
102#define VALIDATE_STAT_CACHE()                   \
103    PR_BEGIN_MACRO                              \
104        if (!mHaveCachedStat) {                 \
105            FillStatCache();                    \
106            if (!mHaveCachedStat)               \
107                 return NSRESULT_FOR_ERRNO();   \
108        }                                       \
109    PR_END_MACRO
110
111#define CHECK_mPath()                           \
112    PR_BEGIN_MACRO                              \
113        if (mPath.IsEmpty())                    \
114            return NS_ERROR_NOT_INITIALIZED;    \
115    PR_END_MACRO
116
117/* directory enumerator */
118class NS_COM
119nsDirEnumeratorUnix : public nsISimpleEnumerator,
120                      public nsIDirectoryEnumerator
121{
122    public:
123    nsDirEnumeratorUnix();
124
125    // nsISupports interface
126    NS_DECL_ISUPPORTS
127
128    // nsISimpleEnumerator interface
129    NS_DECL_NSISIMPLEENUMERATOR
130
131    // nsIDirectoryEnumerator interface
132    NS_DECL_NSIDIRECTORYENUMERATOR
133
134    NS_IMETHOD Init(nsLocalFile *parent, PRBool ignored);
135
136    private:
137    ~nsDirEnumeratorUnix();
138
139    protected:
140    NS_IMETHOD GetNextEntry();
141
142    DIR           *mDir;
143    struct dirent *mEntry;
144    nsCString      mParentPath;
145};
146
147nsDirEnumeratorUnix::nsDirEnumeratorUnix() :
148                         mDir(nsnull), 
149                         mEntry(nsnull)
150{
151}
152
153nsDirEnumeratorUnix::~nsDirEnumeratorUnix()
154{
155    Close();
156}
157
158NS_IMPL_ISUPPORTS2(nsDirEnumeratorUnix, nsISimpleEnumerator, nsIDirectoryEnumerator)
159
160NS_IMETHODIMP
161nsDirEnumeratorUnix::Init(nsLocalFile *parent, PRBool resolveSymlinks /*ignored*/)
162{
163    nsCAutoString dirPath;
164    if (NS_FAILED(parent->GetNativePath(dirPath)) ||
165        dirPath.IsEmpty()) {
166        return NS_ERROR_FILE_INVALID_PATH;
167    }
168
169    if (NS_FAILED(parent->GetNativePath(mParentPath)))
170        return NS_ERROR_FAILURE;
171
172    mDir = opendir(dirPath.get());
173    if (!mDir)
174        return NSRESULT_FOR_ERRNO();
175    return GetNextEntry();
176}
177
178NS_IMETHODIMP
179nsDirEnumeratorUnix::HasMoreElements(PRBool *result)
180{
181    *result = mDir && mEntry;
182    if (!*result)
183        Close();
184    return NS_OK;
185}
186
187NS_IMETHODIMP
188nsDirEnumeratorUnix::GetNext(nsISupports **_retval)
189{
190    nsCOMPtr<nsIFile> file;
191    nsresult rv = GetNextFile(getter_AddRefs(file));
192    if (NS_FAILED(rv))
193        return rv;
194    NS_IF_ADDREF(*_retval = file);
195    return NS_OK;
196}
197
198NS_IMETHODIMP
199nsDirEnumeratorUnix::GetNextEntry()
200{
201    do {
202        errno = 0;
203        mEntry = readdir(mDir);
204
205        // end of dir or error
206        if (!mEntry)
207            return NSRESULT_FOR_ERRNO();
208
209        // keep going past "." and ".."
210    } while (mEntry->d_name[0] == '.'     &&
211            (mEntry->d_name[1] == '\0'    ||   // .\0
212            (mEntry->d_name[1] == '.'     &&
213            mEntry->d_name[2] == '\0')));      // ..\0
214    return NS_OK;
215}
216
217NS_IMETHODIMP
218nsDirEnumeratorUnix::GetNextFile(nsIFile **_retval)
219{
220    nsresult rv;
221    if (!mDir || !mEntry) {
222        *_retval = nsnull;
223        return NS_OK;
224    }
225
226    nsCOMPtr<nsILocalFile> file = new nsLocalFile();
227    if (!file)
228        return NS_ERROR_OUT_OF_MEMORY;
229
230    if (NS_FAILED(rv = file->InitWithNativePath(mParentPath)) ||
231        NS_FAILED(rv = file->AppendNative(nsDependentCString(mEntry->d_name))))
232        return rv;
233
234    *_retval = file;
235    NS_ADDREF(*_retval);
236    return GetNextEntry();
237}
238
239NS_IMETHODIMP
240nsDirEnumeratorUnix::Close()
241{
242    if (mDir) {
243        closedir(mDir);
244        mDir = nsnull;
245    }
246    return NS_OK;
247}
248
249nsLocalFile::nsLocalFile() :
250    mHaveCachedStat(PR_FALSE)
251{
252}
253
254nsLocalFile::nsLocalFile(const nsLocalFile& other)
255  : mPath(other.mPath)
256  , mHaveCachedStat(PR_FALSE)
257{
258}
259
260NS_IMPL_THREADSAFE_ISUPPORTS3(nsLocalFile,
261                              nsIFile,
262                              nsILocalFile,
263                              nsIHashable)
264
265nsresult
266nsLocalFile::nsLocalFileConstructor(nsISupports *outer, 
267                                    const nsIID &aIID,
268                                    void **aInstancePtr)
269{
270    NS_ENSURE_ARG_POINTER(aInstancePtr);
271    NS_ENSURE_NO_AGGREGATION(outer);
272
273    *aInstancePtr = nsnull;
274
275    nsCOMPtr<nsIFile> inst = new nsLocalFile();
276    if (!inst)
277        return NS_ERROR_OUT_OF_MEMORY;
278    return inst->QueryInterface(aIID, aInstancePtr);
279}
280
281nsresult
282nsLocalFile::FillStatCache() {
283    if (stat(mPath.get(), &mCachedStat) == -1) {
284        // try lstat it may be a symlink
285        if (lstat(mPath.get(), &mCachedStat) == -1) {
286            return NSRESULT_FOR_ERRNO();
287        }
288    }
289    mHaveCachedStat = PR_TRUE;
290    return NS_OK;
291}
292
293NS_IMETHODIMP
294nsLocalFile::Clone(nsIFile **file)
295{
296    // Just copy-construct ourselves
297    *file = new nsLocalFile(*this);
298    if (!*file)
299      return NS_ERROR_OUT_OF_MEMORY;
300
301    NS_ADDREF(*file);
302   
303    return NS_OK;
304}
305
306NS_IMETHODIMP
307nsLocalFile::InitWithNativePath(const nsACString &filePath)
308{
309    if (Substring(filePath, 0, 2).EqualsLiteral("~/")) {
310        nsCOMPtr<nsIFile> homeDir;
311        nsCAutoString homePath;
312        if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_HOME_DIR,
313                                             getter_AddRefs(homeDir))) ||
314            NS_FAILED(homeDir->GetNativePath(homePath))) {
315            return NS_ERROR_FAILURE;
316        }
317       
318        mPath = homePath + Substring(filePath, 1, filePath.Length() - 1);
319    } else {
320        if (filePath.IsEmpty() || filePath.First() != '/')
321            return NS_ERROR_FILE_UNRECOGNIZED_PATH;
322        mPath = filePath;
323    }
324
325    // trim off trailing slashes
326    ssize_t len = mPath.Length();
327    while ((len > 1) && (mPath[len - 1] == '/'))
328        --len;
329    mPath.SetLength(len);
330
331    InvalidateCache();
332    return NS_OK;
333}
334
335NS_IMETHODIMP
336nsLocalFile::CreateAllAncestors(PRUint32 permissions)
337{
338    // <jband> I promise to play nice
339    char *buffer = mPath.BeginWriting(),
340         *slashp = buffer;
341
342#ifdef DEBUG_NSIFILE
343    fprintf(stderr, "nsIFile: before: %s\n", buffer);
344#endif
345
346    while ((slashp = strchr(slashp + 1, '/'))) {
347        /*
348         * Sequences of '/' are equivalent to a single '/'.
349         */
350        if (slashp[1] == '/')
351            continue;
352
353        /*
354         * If the path has a trailing slash, don't make the last component,
355         * because we'll get EEXIST in Create when we try to build the final
356         * component again, and it's easier to condition the logic here than
357         * there.
358         */
359        if (slashp[1] == '\0')
360            break;
361
362        /* Temporarily NUL-terminate here */
363        *slashp = '\0';
364#ifdef DEBUG_NSIFILE
365        fprintf(stderr, "nsIFile: mkdir(\"%s\")\n", buffer);
366#endif
367        int mkdir_result = mkdir(buffer, permissions);
368        int mkdir_errno  = errno;
369        if (mkdir_result == -1) {
370            /*
371             * Always set |errno| to EEXIST if the dir already exists
372             * (we have to do this here since the errno value is not consistent
373             * in all cases - various reasons like different platform,
374             * automounter-controlled dir, etc. can affect it (see bug 125489
375             * for details)).
376             */
377            if (access(buffer, F_OK) == 0) {
378                mkdir_errno = EEXIST;
379            }
380        }
381
382        /* Put the / back before we (maybe) return */
383        *slashp = '/';
384
385        /*
386         * We could get EEXIST for an existing file -- not directory --
387         * with the name of one of our ancestors, but that's OK: we'll get
388         * ENOTDIR when we try to make the next component in the path,
389         * either here on back in Create, and error out appropriately.
390         */
391        if (mkdir_result == -1 && mkdir_errno != EEXIST)
392            return nsresultForErrno(mkdir_errno);
393    }
394
395#ifdef DEBUG_NSIFILE
396    fprintf(stderr, "nsIFile: after: %s\n", buffer);
397#endif
398
399    return NS_OK;
400}
401
402NS_IMETHODIMP
403nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
404{
405    *_retval = PR_Open(mPath.get(), flags, mode);
406    if (! *_retval)
407        return NS_ErrorAccordingToNSPR();
408
409    return NS_OK;
410}
411
412NS_IMETHODIMP
413nsLocalFile::OpenANSIFileDesc(const char *mode, FILE **_retval)
414{
415    *_retval = fopen(mPath.get(), mode);
416    if (! *_retval)
417        return NS_ERROR_FAILURE;
418
419    return NS_OK;
420}
421
422static int
423do_create(const char *path, PRIntn flags, mode_t mode, PRFileDesc **_retval)
424{
425    *_retval = PR_Open(path, flags, mode);
426    return *_retval ? 0 : -1;
427}
428
429static int
430do_mkdir(const char *path, PRIntn flags, mode_t mode, PRFileDesc **_retval)
431{
432    *_retval = nsnull;
433    return mkdir(path, mode);
434}
435
436nsresult
437nsLocalFile::CreateAndKeepOpen(PRUint32 type, PRIntn flags,
438                               PRUint32 permissions, PRFileDesc **_retval)
439{
440    if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
441        return NS_ERROR_FILE_UNKNOWN_TYPE;
442
443    int result;
444    int (*createFunc)(const char *, PRIntn, mode_t, PRFileDesc **) =
445        (type == NORMAL_FILE_TYPE) ? do_create : do_mkdir;
446
447    result = createFunc(mPath.get(), flags, permissions, _retval);
448    if (result == -1 && errno == ENOENT) {
449        /*
450         * If we failed because of missing ancestor components, try to create
451         * them and then retry the original creation.
452         *
453         * Ancestor directories get the same permissions as the file we're
454         * creating, with the X bit set for each of (user,group,other) with
455         * an R bit in the original permissions.    If you want to do anything
456         * fancy like setgid or sticky bits, do it by hand.
457         */
458        int dirperm = permissions;
459        if (permissions & S_IRUSR)
460            dirperm |= S_IXUSR;
461        if (permissions & S_IRGRP)
462            dirperm |= S_IXGRP;
463        if (permissions & S_IROTH)
464            dirperm |= S_IXOTH;
465
466#ifdef DEBUG_NSIFILE
467        fprintf(stderr, "nsIFile: perm = %o, dirperm = %o\n", permissions,
468                dirperm);
469#endif
470
471        if (NS_FAILED(CreateAllAncestors(dirperm)))
472            return NS_ERROR_FAILURE;
473
474#ifdef DEBUG_NSIFILE
475        fprintf(stderr, "nsIFile: Create(\"%s\") again\n", mPath.get());
476#endif
477        result = createFunc(mPath.get(), flags, permissions, _retval);
478    }
479    return NSRESULT_FOR_RETURN(result);
480}
481
482NS_IMETHODIMP
483nsLocalFile::Create(PRUint32 type, PRUint32 permissions)
484{
485    PRFileDesc *junk = nsnull;
486    nsresult rv = CreateAndKeepOpen(type,
487                                    PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE |
488                                    PR_EXCL,
489                                    permissions,
490                                    &junk);
491    if (junk)
492        PR_Close(junk);
493    return rv;
494}
495
496NS_IMETHODIMP
497nsLocalFile::AppendNative(const nsACString &fragment)
498{
499    if (fragment.IsEmpty())
500        return NS_OK;
501
502    // only one component of path can be appended
503    nsACString::const_iterator begin, end;
504    if (FindCharInReadable('/', fragment.BeginReading(begin),
505                                fragment.EndReading(end)))
506        return NS_ERROR_FILE_UNRECOGNIZED_PATH;
507
508    return AppendRelativeNativePath(fragment);
509}
510
511NS_IMETHODIMP
512nsLocalFile::AppendRelativeNativePath(const nsACString &fragment)
513{
514    if (fragment.IsEmpty())
515        return NS_OK;
516
517    // No leading '/'
518    if (fragment.First() == '/')
519        return NS_ERROR_FILE_UNRECOGNIZED_PATH;
520
521    if (mPath.EqualsLiteral("/"))
522        mPath.Append(fragment);
523    else
524        mPath.Append(NS_LITERAL_CSTRING("/") + fragment);
525
526    InvalidateCache();
527    return NS_OK;
528}
529
530NS_IMETHODIMP
531nsLocalFile::Normalize()
532{
533    char    resolved_path[PATH_MAX] = "";
534    char *resolved_path_ptr = nsnull;
535
536#ifdef XP_BEOS
537    BEntry be_e(mPath.get(), true);
538    BPath be_p;
539    status_t err;
540    if ((err = be_e.GetPath(&be_p)) == B_OK) {
541        resolved_path_ptr = (char *)be_p.Path();
542        PL_strncpyz(resolved_path, resolved_path_ptr, PATH_MAX - 1);
543    }
544#else
545    resolved_path_ptr = realpath(mPath.get(), resolved_path);
546#endif
547    // if there is an error, the return is null.
548    if (!resolved_path_ptr)
549        return NSRESULT_FOR_ERRNO();
550
551    mPath = resolved_path;
552    return NS_OK;
553}
554
555void
556nsLocalFile::LocateNativeLeafName(nsACString::const_iterator &begin, 
557                                  nsACString::const_iterator &end)
558{
559    // XXX perhaps we should cache this??
560
561    mPath.BeginReading(begin);
562    mPath.EndReading(end);
563   
564    nsACString::const_iterator it = end;
565    nsACString::const_iterator stop = begin;
566    --stop;
567    while (--it != stop) {
568        if (*it == '/') {
569            begin = ++it;
570            return;
571        }
572    }
573    // else, the entire path is the leaf name (which means this
574    // isn't an absolute path... unexpected??)
575}
576
577NS_IMETHODIMP
578nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
579{
580    nsACString::const_iterator begin, end;
581    LocateNativeLeafName(begin, end);
582    aLeafName = Substring(begin, end);
583    return NS_OK;
584}
585
586NS_IMETHODIMP
587nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
588{
589    nsACString::const_iterator begin, end;
590    LocateNativeLeafName(begin, end);
591    mPath.Replace(begin.get() - mPath.get(), Distance(begin, end), aLeafName);
592    InvalidateCache();
593    return NS_OK;
594}
595
596NS_IMETHODIMP
597nsLocalFile::GetNativePath(nsACString &_retval)
598{
599    _retval = mPath;
600    return NS_OK;
601}
602
603nsresult
604nsLocalFile::GetNativeTargetPathName(nsIFile *newParent, 
605                                     const nsACString &newName,
606                                     nsACString &_retval)
607{
608    nsresult rv;
609    nsCOMPtr<nsIFile> oldParent;
610
611    if (!newParent) {
612        if (NS_FAILED(rv = GetParent(getter_AddRefs(oldParent))))
613            return rv;
614        newParent = oldParent.get();
615    } else {
616        // check to see if our target directory exists
617        PRBool targetExists;
618        if (NS_FAILED(rv = newParent->Exists(&targetExists)))
619            return rv;
620
621        if (!targetExists) {
622            // XXX create the new directory with some permissions
623            rv = newParent->Create(DIRECTORY_TYPE, 0755);
624            if (NS_FAILED(rv))
625                return rv;
626        } else {
627            // make sure that the target is actually a directory
628            PRBool targetIsDirectory;
629            if (NS_FAILED(rv = newParent->IsDirectory(&targetIsDirectory)))
630                return rv;
631            if (!targetIsDirectory)
632                return NS_ERROR_FILE_DESTINATION_NOT_DIR;
633        }
634    }
635
636    nsACString::const_iterator nameBegin, nameEnd;
637    if (!newName.IsEmpty()) {
638        newName.BeginReading(nameBegin);
639        newName.EndReading(nameEnd);
640    }
641    else
642        LocateNativeLeafName(nameBegin, nameEnd);
643
644    nsCAutoString dirName;
645    if (NS_FAILED(rv = newParent->GetNativePath(dirName)))
646        return rv;
647
648    _retval = dirName
649            + NS_LITERAL_CSTRING("/")
650            + Substring(nameBegin, nameEnd);
651    return NS_OK;
652}
653
654nsresult
655nsLocalFile::CopyDirectoryTo(nsIFile *newParent)
656{
657    nsresult rv;
658    /*
659     * dirCheck is used for various boolean test results such as from Equals,
660     * Exists, isDir, etc.
661     */
662    PRBool dirCheck, isSymlink;
663    PRUint32 oldPerms;
664
665    if (NS_FAILED(rv = IsDirectory(&dirCheck)))
666        return rv;
667    if (!dirCheck)
668        return CopyToNative(newParent, EmptyCString());
669   
670    if (NS_FAILED(rv = Equals(newParent, &dirCheck)))
671        return rv;
672    if (dirCheck) { 
673        // can't copy dir to itself
674        return NS_ERROR_INVALID_ARG;
675    }
676   
677    if (NS_FAILED(rv = newParent->Exists(&dirCheck))) 
678        return rv;
679    // get the dirs old permissions
680    if (NS_FAILED(rv = GetPermissions(&oldPerms)))
681        return rv;
682    if (!dirCheck) {
683        if (NS_FAILED(rv = newParent->Create(DIRECTORY_TYPE, oldPerms)))
684            return rv;
685    } else {    // dir exists lets try to use leaf
686        nsCAutoString leafName;
687        if (NS_FAILED(rv = GetNativeLeafName(leafName)))
688            return rv;
689        if (NS_FAILED(rv = newParent->AppendNative(leafName)))
690            return rv;
691        if (NS_FAILED(rv = newParent->Exists(&dirCheck)))
692            return rv;
693        if (dirCheck) 
694            return NS_ERROR_FILE_ALREADY_EXISTS; // dest exists
695        if (NS_FAILED(rv = newParent->Create(DIRECTORY_TYPE, oldPerms)))
696            return rv;
697    }
698
699    nsCOMPtr<nsISimpleEnumerator> dirIterator;
700    if (NS_FAILED(rv = GetDirectoryEntries(getter_AddRefs(dirIterator))))
701        return rv;
702
703    PRBool hasMore = PR_FALSE;
704    while (dirIterator->HasMoreElements(&hasMore), hasMore) {
705        nsCOMPtr<nsIFile> entry;
706        rv = dirIterator->GetNext((nsISupports**)getter_AddRefs(entry));
707        if (NS_FAILED(rv)) 
708            continue;
709        if (NS_FAILED(rv = entry->IsSymlink(&isSymlink)))
710            return rv;
711        if (NS_FAILED(rv = entry->IsDirectory(&dirCheck)))
712            return rv;
713        if (dirCheck && !isSymlink) {
714            nsCOMPtr<nsIFile> destClone;
715            rv = newParent->Clone(getter_AddRefs(destClone));
716            if (NS_SUCCEEDED(rv)) {
717                nsCOMPtr<nsILocalFile> newDir(do_QueryInterface(destClone));
718                if (NS_FAILED(rv = entry->CopyToNative(newDir, EmptyCString()))) {
719#ifdef DEBUG
720                    nsresult rv2;
721                    nsCAutoString pathName;
722                    if (NS_FAILED(rv2 = entry->GetNativePath(pathName)))
723                        return rv2;
724                    printf("Operation not supported: %s\n", pathName.get());
725#endif
726                    if (rv == NS_ERROR_OUT_OF_MEMORY) 
727                        return rv;
728                    continue;
729                }
730            }
731        } else {
732            if (NS_FAILED(rv = entry->CopyToNative(newParent, EmptyCString()))) {
733#ifdef DEBUG
734                nsresult rv2;
735                nsCAutoString pathName;
736                if (NS_FAILED(rv2 = entry->GetNativePath(pathName)))
737                    return rv2;
738                printf("Operation not supported: %s\n", pathName.get());
739#endif
740                if (rv == NS_ERROR_OUT_OF_MEMORY) 
741                    return rv;
742                continue;
743            }
744        }
745    }
746    return NS_OK;
747}
748
749NS_IMETHODIMP
750nsLocalFile::CopyToNative(nsIFile *newParent, const nsACString &newName)
751{
752    nsresult rv;
753    // check to make sure that this has been initialized properly
754    CHECK_mPath();
755
756    // we copy the parent here so 'newParent' remains immutable
757    nsCOMPtr <nsIFile> workParent;
758    if (newParent) {
759        if (NS_FAILED(rv = newParent->Clone(getter_AddRefs(workParent))))
760            return rv;
761    } else {
762        if (NS_FAILED(rv = GetParent(getter_AddRefs(workParent))))
763            return rv;
764    }
765   
766    // check to see if we are a directory or if we are a file
767    PRBool isDirectory;
768    if (NS_FAILED(rv = IsDirectory(&isDirectory)))
769        return rv;
770
771    nsCAutoString newPathName;
772    if (isDirectory) {
773        if (!newName.IsEmpty()) {
774            if (NS_FAILED(rv = workParent->AppendNative(newName)))
775                return rv;
776        } else {
777            if (NS_FAILED(rv = GetNativeLeafName(newPathName)))
778                return rv;
779            if (NS_FAILED(rv = workParent->AppendNative(newPathName)))
780                return rv;
781        }
782        if (NS_FAILED(rv = CopyDirectoryTo(workParent)))
783            return rv;
784    } else {
785        rv = GetNativeTargetPathName(workParent, newName, newPathName);
786        if (NS_FAILED(rv)) 
787            return rv;
788
789#ifdef DEBUG_blizzard
790        printf("nsLocalFile::CopyTo() %s -> %s\n", mPath.get(), newPathName.get());
791#endif
792
793        // actually create the file.
794        nsLocalFile *newFile = new nsLocalFile();
795        if (!newFile)
796            return NS_ERROR_OUT_OF_MEMORY;
797
798        nsCOMPtr<nsILocalFile> fileRef(newFile); // release on exit
799
800        rv = newFile->InitWithNativePath(newPathName);
801        if (NS_FAILED(rv))
802            return rv;
803
804        // get the old permissions
805        PRUint32 myPerms;
806        GetPermissions(&myPerms);
807
808        // Create the new file with the old file's permissions, even if write
809        // permission is missing.  We can't create with write permission and
810        // then change back to myPerm on all filesystems (FAT on Linux, e.g.).
811        // But we can write to a read-only file on all Unix filesystems if we
812        // open it successfully for writing.
813
814        PRFileDesc *newFD;
815        rv = newFile->CreateAndKeepOpen(NORMAL_FILE_TYPE,
816                                        PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE,
817                                        myPerms,
818                                        &newFD);
819        if (NS_FAILED(rv))
820            return rv;
821
822        // open the old file, too
823        PRBool specialFile;
824        if (NS_FAILED(rv = IsSpecial(&specialFile))) {
825            PR_Close(newFD);
826            return rv;
827        }
828        if (specialFile) {
829#ifdef DEBUG
830            printf("Operation not supported: %s\n", mPath.get());
831#endif
832            // make sure to clean up properly
833            PR_Close(newFD);
834            return NS_OK;
835        }
836               
837        PRFileDesc *oldFD;
838        rv = OpenNSPRFileDesc(PR_RDONLY, myPerms, &oldFD);
839        if (NS_FAILED(rv)) {
840            // make sure to clean up properly
841            PR_Close(newFD);
842            return rv;
843        }
844
845#ifdef DEBUG_blizzard
846        PRInt32 totalRead = 0;
847        PRInt32 totalWritten = 0;
848#endif
849        char buf[BUFSIZ];
850        PRInt32 bytesRead;
851       
852        while ((bytesRead = PR_Read(oldFD, buf, BUFSIZ)) > 0) {
853#ifdef DEBUG_blizzard
854            totalRead += bytesRead;
855#endif
856
857            // PR_Write promises never to do a short write
858            PRInt32 bytesWritten = PR_Write(newFD, buf, bytesRead);
859            if (bytesWritten < 0) {
860                bytesRead = -1;
861                break;
862            }
863            NS_ASSERTION(bytesWritten == bytesRead, "short PR_Write?");
864
865#ifdef DEBUG_blizzard
866            totalWritten += bytesWritten;
867#endif
868        }
869
870#ifdef DEBUG_blizzard
871        printf("read %d bytes, wrote %d bytes\n",
872                 totalRead, totalWritten);
873#endif
874
875        // close the files
876        PR_Close(newFD);
877        PR_Close(oldFD);
878
879        // check for read (or write) error after cleaning up
880        if (bytesRead < 0) 
881            return NS_ERROR_OUT_OF_MEMORY;
882    }
883    return rv;
884}
885
886NS_IMETHODIMP
887nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParent, const nsACString &newName)
888{
889    return CopyToNative(newParent, newName);
890}
891
892NS_IMETHODIMP
893nsLocalFile::MoveToNative(nsIFile *newParent, const nsACString &newName)
894{
895    nsresult rv;
896
897    // check to make sure that this has been initialized properly
898    CHECK_mPath();
899
900    // check to make sure that we have a new parent
901    nsCAutoString newPathName;
902    rv = GetNativeTargetPathName(newParent, newName, newPathName);
903    if (NS_FAILED(rv))
904        return rv;
905
906    // try for atomic rename, falling back to copy/delete
907    if (rename(mPath.get(), newPathName.get()) < 0) {
908#ifdef VMS
909        if (errno == EXDEV || errno == ENXIO) {
910#else
911        if (errno == EXDEV) {
912#endif
913            rv = CopyToNative(newParent, newName);
914            if (NS_SUCCEEDED(rv))
915                rv = Remove(PR_TRUE);
916        } else {
917            rv = NSRESULT_FOR_ERRNO();
918        }
919    }
920    return rv;
921}
922
923NS_IMETHODIMP
924nsLocalFile::Remove(PRBool recursive)
925{
926    CHECK_mPath();
927
928    VALIDATE_STAT_CACHE();
929    PRBool isSymLink, isDir;
930   
931    nsresult rv = IsSymlink(&isSymLink);
932    if (NS_FAILED(rv))
933        return rv;
934
935    if (!recursive && isSymLink)
936        return NSRESULT_FOR_RETURN(unlink(mPath.get()));
937   
938    isDir = S_ISDIR(mCachedStat.st_mode);
939    InvalidateCache();
940    if (isDir) {
941        if (recursive) {
942            nsDirEnumeratorUnix *dir = new nsDirEnumeratorUnix();
943            if (!dir)
944                return NS_ERROR_OUT_OF_MEMORY;
945
946            nsCOMPtr<nsISimpleEnumerator> dirRef(dir); // release on exit
947
948            rv = dir->Init(this, PR_FALSE);
949            if (NS_FAILED(rv))
950                return rv;
951
952            PRBool more;
953            while (dir->HasMoreElements(&more), more) {
954                nsCOMPtr<nsISupports> item;
955                rv = dir->GetNext(getter_AddRefs(item));
956                if (NS_FAILED(rv))
957                    return NS_ERROR_FAILURE;
958
959                nsCOMPtr<nsIFile> file = do_QueryInterface(item, &rv);
960                if (NS_FAILED(rv))
961                    return NS_ERROR_FAILURE;
962                if (NS_FAILED(rv = file->Remove(recursive)))
963                    return rv;
964            }
965        }
966
967        if (rmdir(mPath.get()) == -1)
968            return NSRESULT_FOR_ERRNO();
969    } else {
970        if (unlink(mPath.get()) == -1)
971            return NSRESULT_FOR_ERRNO();
972    }
973
974    return NS_OK;
975}
976
977NS_IMETHODIMP
978nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModTime)
979{
980    CHECK_mPath();
981    NS_ENSURE_ARG(aLastModTime);
982
983    PRFileInfo64 info;
984    if (PR_GetFileInfo64(mPath.get(), &info) != PR_SUCCESS)
985        return NSRESULT_FOR_ERRNO();
986
987    // PRTime is a 64 bit value
988    // microseconds -> milliseconds
989    PRInt64 usecPerMsec;
990    LL_I2L(usecPerMsec, PR_USEC_PER_MSEC);
991    LL_DIV(*aLastModTime, info.modifyTime, usecPerMsec);
992    return NS_OK;
993}
994
995NS_IMETHODIMP
996nsLocalFile::SetLastModifiedTime(PRInt64 aLastModTime)
997{
998    CHECK_mPath();
999
1000    int result;
1001    if (! LL_IS_ZERO(aLastModTime)) {
1002        VALIDATE_STAT_CACHE();
1003        struct utimbuf ut;
1004        ut.actime = mCachedStat.st_atime;
1005
1006        // convert milliseconds to seconds since the unix epoch
1007        double dTime;
1008        LL_L2D(dTime, aLastModTime);
1009        ut.modtime = (time_t) (dTime / PR_MSEC_PER_SEC);
1010        result = utime(mPath.get(), &ut);
1011    } else {
1012        result = utime(mPath.get(), nsnull);
1013    }
1014    InvalidateCache();
1015    return NSRESULT_FOR_RETURN(result);
1016}
1017
1018NS_IMETHODIMP
1019nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModTimeOfLink)
1020{
1021    CHECK_mPath();
1022    NS_ENSURE_ARG(aLastModTimeOfLink);
1023
1024    struct stat sbuf;
1025    if (lstat(mPath.get(), &sbuf) == -1)
1026        return NSRESULT_FOR_ERRNO();
1027    LL_I2L(*aLastModTimeOfLink, (PRInt32)sbuf.st_mtime);
1028
1029    // lstat returns st_mtime in seconds
1030    PRInt64 msecPerSec;
1031    LL_I2L(msecPerSec, PR_MSEC_PER_SEC);
1032    LL_MUL(*aLastModTimeOfLink, *aLastModTimeOfLink, msecPerSec);
1033
1034    return NS_OK;
1035}
1036
1037/*
1038 * utime(2) may or may not dereference symlinks, joy.
1039 */
1040NS_IMETHODIMP
1041nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModTimeOfLink)
1042{
1043    return SetLastModifiedTime(aLastModTimeOfLink);
1044}
1045
1046/*
1047 * Only send back permissions bits: maybe we want to send back the whole
1048 * mode_t to permit checks against other file types?
1049 */
1050
1051#define NORMALIZE_PERMS(mode)    ((mode)& (S_IRWXU | S_IRWXG | S_IRWXO))
1052
1053NS_IMETHODIMP
1054nsLocalFile::GetPermissions(PRUint32 *aPermissions)
1055{
1056    NS_ENSURE_ARG(aPermissions);
1057    VALIDATE_STAT_CACHE();
1058    *aPermissions = NORMALIZE_PERMS(mCachedStat.st_mode);
1059    return NS_OK;
1060}
1061
1062NS_IMETHODIMP
1063nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink)
1064{
1065    CHECK_mPath();
1066    NS_ENSURE_ARG(aPermissionsOfLink);
1067
1068    struct stat sbuf;
1069    if (lstat(mPath.get(), &sbuf) == -1)
1070        return NSRESULT_FOR_ERRNO();
1071    *aPermissionsOfLink = NORMALIZE_PERMS(sbuf.st_mode);
1072    return NS_OK;
1073}
1074
1075NS_IMETHODIMP
1076nsLocalFile::SetPermissions(PRUint32 aPermissions)
1077{
1078    CHECK_mPath();
1079
1080    InvalidateCache();
1081
1082    /*
1083     * Race condition here: we should use fchmod instead, there's no way to
1084     * guarantee the name still refers to the same file.
1085     */
1086    if (chmod(mPath.get(), aPermissions) < 0)
1087        return NSRESULT_FOR_ERRNO();
1088    return NS_OK;
1089}
1090
1091NS_IMETHODIMP
1092nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions)
1093{
1094    return SetPermissions(aPermissions);
1095}
1096
1097NS_IMETHODIMP
1098nsLocalFile::GetFileSize(PRInt64 *aFileSize)
1099{
1100    NS_ENSURE_ARG_POINTER(aFileSize);
1101    *aFileSize = LL_ZERO;
1102    VALIDATE_STAT_CACHE();
1103
1104#if defined(VMS)
1105    /* Only two record formats can report correct file content size */
1106    if ((mCachedStat.st_fab_rfm != FAB$C_STMLF) &&
1107        (mCachedStat.st_fab_rfm != FAB$C_STMCR)) {
1108        return NS_ERROR_FAILURE;
1109    }
1110#endif
1111
1112    /* XXX autoconf for and use stat64 if available */
1113    if (!S_ISDIR(mCachedStat.st_mode)) {
1114        LL_UI2L(*aFileSize, (PRUint32)mCachedStat.st_size);
1115    }
1116    return NS_OK;
1117}
1118
1119NS_IMETHODIMP
1120nsLocalFile::SetFileSize(PRInt64 aFileSize)
1121{
1122    CHECK_mPath();
1123
1124    PRInt32 size;
1125    LL_L2I(size, aFileSize);
1126    /* XXX truncate64? */
1127    InvalidateCache();
1128    if (truncate(mPath.get(), (off_t)size) == -1)
1129        return NSRESULT_FOR_ERRNO();
1130    return NS_OK;
1131}
1132
1133NS_IMETHODIMP
1134nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSize)
1135{
1136    CHECK_mPath();
1137    NS_ENSURE_ARG(aFileSize);
1138
1139    struct stat sbuf;
1140    if (lstat(mPath.get(), &sbuf) == -1)
1141        return NSRESULT_FOR_ERRNO();
1142    /* XXX autoconf for and use lstat64 if available */
1143    LL_UI2L(*aFileSize, (PRUint32)sbuf.st_size);
1144    return NS_OK;
1145}
1146
1147NS_IMETHODIMP
1148nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
1149{
1150    NS_ENSURE_ARG_POINTER(aDiskSpaceAvailable);
1151
1152    // These systems have the operations necessary to check disk space.
1153
1154#if defined(HAVE_SYS_STATFS_H) || defined(HAVE_SYS_STATVFS_H)
1155
1156    // check to make sure that mPath is properly initialized
1157    CHECK_mPath();
1158
1159    struct STATFS fs_buf;
1160
1161    /*
1162     * Members of the STATFS struct that you should know about:
1163     * f_bsize = block size on disk.
1164     * f_bavail = number of free blocks available to a non-superuser.
1165     * f_bfree = number of total free blocks in file system.
1166     */
1167
1168    if (STATFS(mPath.get(), &fs_buf) < 0) {
1169        // The call to STATFS failed.
1170#ifdef DEBUG
1171        printf("ERROR: GetDiskSpaceAvailable: STATFS call FAILED. \n");
1172#endif
1173        return NS_ERROR_FAILURE;
1174    }
1175#ifdef DEBUG_DISK_SPACE
1176    printf("DiskSpaceAvailable: %d bytes\n",
1177         fs_buf.f_bsize * (fs_buf.f_bavail - 1));
1178#endif
1179
1180    /*
1181     * The number of bytes free == The number of free blocks available to
1182     * a non-superuser, minus one as a fudge factor, multiplied by the size
1183     * of the aforementioned blocks.
1184     */
1185    PRInt64 bsize, bavail;
1186
1187    LL_I2L(bsize, fs_buf.f_bsize);
1188    LL_I2L(bavail, fs_buf.f_bavail - 1);
1189    LL_MUL(*aDiskSpaceAvailable, bsize, bavail);
1190    return NS_OK;
1191
1192#else
1193    /*
1194     * This platform doesn't have statfs or statvfs.  I'm sure that there's
1195     * a way to check for free disk space on platforms that don't have statfs
1196     * (I'm SURE they have df, for example).
1197     *
1198     * Until we figure out how to do that, lets be honest and say that this
1199     * command isn't implemented properly for these platforms yet.
1200     */
1201#ifdef DEBUG
1202    printf("ERROR: GetDiskSpaceAvailable: Not implemented for plaforms without statfs.\n");
1203#endif
1204    return NS_ERROR_NOT_IMPLEMENTED;
1205
1206#endif /* HAVE_SYS_STATFS_H or HAVE_SYS_STATVFS_H */
1207
1208}
1209
1210NS_IMETHODIMP
1211nsLocalFile::GetParent(nsIFile **aParent)
1212{
1213    CHECK_mPath();
1214    NS_ENSURE_ARG_POINTER(aParent);
1215    *aParent       = nsnull;
1216
1217    // if '/' we are at the top of the volume, return null
1218    if (mPath.Equals("/"))
1219        return  NS_OK;
1220 
1221    // <brendan, after jband> I promise to play nice
1222    char *buffer   = mPath.BeginWriting(),
1223         *slashp   = buffer;
1224
1225    // find the last significant slash in buffer
1226    slashp = strrchr(buffer, '/');
1227    NS_ASSERTION(slashp, "non-canonical mPath?");
1228    if (!slashp)
1229        return NS_ERROR_FILE_INVALID_PATH;
1230
1231    // for the case where we are at '/'
1232    if (slashp == buffer)
1233        slashp++;
1234
1235    // temporarily terminate buffer at the last significant slash
1236    char c = *slashp;
1237    *slashp = '\0';
1238
1239    nsCOMPtr<nsILocalFile> localFile;
1240    nsresult rv = NS_NewNativeLocalFile(nsDependentCString(buffer), PR_TRUE,
1241                                        getter_AddRefs(localFile));
1242
1243    // make buffer whole again
1244    *slashp = c;
1245
1246    if (NS_SUCCEEDED(rv) && localFile)
1247        rv = CallQueryInterface(localFile, aParent);
1248    return rv;
1249}
1250
1251/*
1252 * The results of Exists, isWritable and isReadable are not cached.
1253 */
1254
1255
1256#if defined(XP_BEOS) || defined(SOLARIS)
1257// access() is buggy in BeOS POSIX implementation, at least for BFS, using stat() instead
1258// see bug 169506, https://bugzilla.mozilla.org/show_bug.cgi?id=169506
1259// access() problem also exists in Solaris POSIX implementation
1260// see bug 351595, https://bugzilla.mozilla.org/show_bug.cgi?id=351595
1261NS_IMETHODIMP
1262nsLocalFile::Exists(PRBool *_retval)
1263{
1264    CHECK_mPath();
1265    NS_ENSURE_ARG_POINTER(_retval);
1266    struct stat buf;
1267
1268    *_retval = (stat(mPath.get(), &buf) == 0);
1269    return NS_OK;
1270}
1271
1272NS_IMETHODIMP
1273nsLocalFile::IsWritable(PRBool *_retval)
1274{
1275    CHECK_mPath();
1276    NS_ENSURE_ARG_POINTER(_retval);
1277    struct stat buf;
1278
1279    *_retval = (stat(mPath.get(), &buf) == 0);
1280    if (*_retval || errno == EACCES) {
1281        *_retval = *_retval && (buf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH ));
1282        return NS_OK;
1283    }
1284    return NSRESULT_FOR_ERRNO();
1285}
1286
1287NS_IMETHODIMP
1288nsLocalFile::IsReadable(PRBool *_retval)
1289{
1290    CHECK_mPath();
1291    NS_ENSURE_ARG_POINTER(_retval);
1292    struct stat buf;
1293
1294    *_retval = (stat(mPath.get(), &buf) == 0);
1295    if (*_retval || errno == EACCES) {
1296        *_retval = *_retval && (buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH ));
1297        return NS_OK;
1298    }
1299    return NSRESULT_FOR_ERRNO();
1300}
1301
1302NS_IMETHODIMP
1303nsLocalFile::IsExecutable(PRBool *_retval)
1304{
1305    CHECK_mPath();
1306    NS_ENSURE_ARG_POINTER(_retval);
1307    struct stat buf;
1308
1309    *_retval = (stat(mPath.get(), &buf) == 0);
1310    if (*_retval || errno == EACCES) {
1311        *_retval = *_retval && (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH ));
1312        return NS_OK;
1313    }
1314    return NSRESULT_FOR_ERRNO();
1315}
1316#else
1317
1318NS_IMETHODIMP
1319nsLocalFile::Exists(PRBool *_retval)
1320{
1321    CHECK_mPath();
1322    NS_ENSURE_ARG_POINTER(_retval);
1323
1324    *_retval = (access(mPath.get(), F_OK) == 0);
1325    return NS_OK;
1326}
1327
1328
1329NS_IMETHODIMP
1330nsLocalFile::IsWritable(PRBool *_retval)
1331{
1332    CHECK_mPath();
1333    NS_ENSURE_ARG_POINTER(_retval);
1334
1335    *_retval = (access(mPath.get(), W_OK) == 0);
1336    if (*_retval || errno == EACCES)
1337        return NS_OK;
1338    return NSRESULT_FOR_ERRNO();
1339}
1340
1341NS_IMETHODIMP
1342nsLocalFile::IsReadable(PRBool *_retval)
1343{
1344    CHECK_mPath();
1345    NS_ENSURE_ARG_POINTER(_retval);
1346
1347    *_retval = (access(mPath.get(), R_OK) == 0);
1348    if (*_retval || errno == EACCES)
1349        return NS_OK;
1350    return NSRESULT_FOR_ERRNO();
1351}
1352
1353NS_IMETHODIMP
1354nsLocalFile::IsExecutable(PRBool *_retval)
1355{
1356    CHECK_mPath();
1357    NS_ENSURE_ARG_POINTER(_retval);
1358
1359    *_retval = (access(mPath.get(), X_OK) == 0);
1360    if (*_retval || errno == EACCES)
1361        return NS_OK;
1362    return NSRESULT_FOR_ERRNO();
1363}
1364#endif
1365NS_IMETHODIMP
1366nsLocalFile::IsDirectory(PRBool *_retval)
1367{
1368    NS_ENSURE_ARG_POINTER(_retval);
1369    *_retval = PR_FALSE;
1370    VALIDATE_STAT_CACHE();
1371    *_retval = S_ISDIR(mCachedStat.st_mode);
1372    return NS_OK;
1373}
1374
1375NS_IMETHODIMP
1376nsLocalFile::IsFile(PRBool *_retval)
1377{
1378    NS_ENSURE_ARG_POINTER(_retval);
1379    *_retval = PR_FALSE;
1380    VALIDATE_STAT_CACHE();
1381    *_retval = S_ISREG(mCachedStat.st_mode);
1382    return NS_OK;
1383}
1384
1385NS_IMETHODIMP
1386nsLocalFile::IsHidden(PRBool *_retval)
1387{
1388    NS_ENSURE_ARG_POINTER(_retval);
1389    nsACString::const_iterator begin, end;
1390    LocateNativeLeafName(begin, end);
1391    *_retval = (*begin == '.');
1392    return NS_OK;
1393}
1394
1395NS_IMETHODIMP
1396nsLocalFile::IsSymlink(PRBool *_retval)
1397{
1398    NS_ENSURE_ARG_POINTER(_retval);
1399    CHECK_mPath();
1400
1401    struct stat symStat;
1402    lstat(mPath.get(), &symStat);
1403    *_retval=S_ISLNK(symStat.st_mode);
1404    return NS_OK;
1405}
1406
1407NS_IMETHODIMP
1408nsLocalFile::IsSpecial(PRBool *_retval)
1409{
1410    NS_ENSURE_ARG_POINTER(_retval);
1411    VALIDATE_STAT_CACHE();
1412    *_retval = S_ISCHR(mCachedStat.st_mode)      ||
1413                 S_ISBLK(mCachedStat.st_mode)    ||
1414#ifdef S_ISSOCK
1415                 S_ISSOCK(mCachedStat.st_mode)   ||
1416#endif
1417                 S_ISFIFO(mCachedStat.st_mode);
1418
1419    return NS_OK;
1420}
1421
1422NS_IMETHODIMP
1423nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
1424{
1425    NS_ENSURE_ARG(inFile);
1426    NS_ENSURE_ARG_POINTER(_retval);
1427    *_retval = PR_FALSE;
1428
1429    nsresult rv;
1430    nsCAutoString inPath;
1431
1432    if (NS_FAILED(rv = inFile->GetNativePath(inPath)))
1433        return rv;
1434
1435    *_retval = !FILE_STRCMP(inPath.get(), mPath.get());
1436    return NS_OK;
1437}
1438
1439NS_IMETHODIMP
1440nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval)
1441{
1442    CHECK_mPath();
1443    NS_ENSURE_ARG(inFile);
1444    NS_ENSURE_ARG_POINTER(_retval);
1445
1446    nsCAutoString inPath;
1447    nsresult rv;
1448
1449    if (NS_FAILED(rv = inFile->GetNativePath(inPath)))
1450        return rv;
1451
1452    *_retval = PR_FALSE;
1453
1454    ssize_t len = mPath.Length();
1455    if (FILE_STRNCMP(mPath.get(), inPath.get(), len) == 0) {
1456        // Now make sure that the |inFile|'s path has a separator at len,
1457        // which implies that it has more components after len.
1458        if (inPath[len] == '/')
1459            *_retval = PR_TRUE;
1460    }
1461
1462    return NS_OK;
1463}
1464
1465NS_IMETHODIMP
1466nsLocalFile::GetNativeTarget(nsACString &_retval)
1467{
1468    CHECK_mPath();
1469    _retval.Truncate();
1470
1471    struct stat symStat;
1472    lstat(mPath.get(), &symStat);
1473    if (!S_ISLNK(symStat.st_mode))
1474        return NS_ERROR_FILE_INVALID_PATH;
1475
1476    PRInt64 targetSize64;
1477    if (NS_FAILED(GetFileSizeOfLink(&targetSize64)))
1478        return NS_ERROR_FAILURE;
1479
1480    PRInt32 size;
1481    LL_L2I(size, targetSize64);
1482    char *target = (char *)nsMemory::Alloc(size + 1);
1483    if (!target)
1484        return NS_ERROR_OUT_OF_MEMORY;
1485
1486    if (readlink(mPath.get(), target, (size_t)size) < 0) {
1487        nsMemory::Free(target);
1488        return NSRESULT_FOR_ERRNO();
1489    }
1490    target[size] = '\0';
1491
1492    nsresult rv;
1493    PRBool isSymlink;
1494    nsCOMPtr<nsIFile> self(this);
1495    nsCOMPtr<nsIFile> parent;
1496    while (NS_SUCCEEDED(rv = self->GetParent(getter_AddRefs(parent)))) {
1497        NS_ASSERTION(parent != nsnull, "no parent?!");
1498
1499        if (target[0] != '/') {
1500            nsCOMPtr<nsILocalFile> localFile(do_QueryInterface(parent, &rv));
1501            if (NS_FAILED(rv))
1502                break;
1503            if (NS_FAILED(rv = localFile->AppendRelativeNativePath(nsDependentCString(target))))
1504                break;
1505            if (NS_FAILED(rv = localFile->GetNativePath(_retval)))
1506                break;
1507            if (NS_FAILED(rv = parent->IsSymlink(&isSymlink)))
1508                break;
1509            self = parent;
1510        } else {
1511            nsCOMPtr<nsILocalFile> localFile;
1512            rv = NS_NewNativeLocalFile(nsDependentCString(target), PR_TRUE,
1513                                       getter_AddRefs(localFile));
1514            if (NS_FAILED(rv))
1515                break;
1516            if (NS_FAILED(rv = localFile->IsSymlink(&isSymlink)))
1517                break;
1518            _retval = target; // XXX can we avoid this buffer copy?
1519            self = do_QueryInterface(localFile);
1520        }
1521        if (NS_FAILED(rv) || !isSymlink)
1522            break;
1523
1524        const nsPromiseFlatCString &flatRetval = PromiseFlatCString(_retval);
1525
1526        // strip off any and all trailing '/'
1527        PRInt32 len = strlen(target);
1528        while (target[len-1] == '/' && len > 1)
1529            target[--len] = '\0';
1530        if (lstat(flatRetval.get(), &symStat) < 0) {
1531            rv = NSRESULT_FOR_ERRNO();
1532            break;
1533        }
1534        if (!S_ISLNK(symStat.st_mode)) {
1535            rv = NS_ERROR_FILE_INVALID_PATH;
1536            break;
1537        }
1538        size = symStat.st_size;
1539        if (readlink(flatRetval.get(), target, size) < 0) {
1540            rv = NSRESULT_FOR_ERRNO();
1541            break;
1542        }
1543        target[size] = '\0';
1544
1545        _retval.Truncate();
1546    }
1547
1548    nsMemory::Free(target);
1549
1550    if (NS_FAILED(rv))
1551        _retval.Truncate();
1552    return rv;
1553}
1554
1555/* attribute PRBool followLinks; */
1556NS_IMETHODIMP
1557nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
1558{
1559    *aFollowLinks = PR_TRUE;
1560    return NS_OK;
1561}
1562
1563NS_IMETHODIMP
1564nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
1565{
1566    return NS_OK;
1567}
1568
1569NS_IMETHODIMP
1570nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator **entries)
1571{
1572    nsDirEnumeratorUnix *dir = new nsDirEnumeratorUnix();
1573    if (!dir)
1574        return NS_ERROR_OUT_OF_MEMORY;
1575
1576    NS_ADDREF(dir);
1577    nsresult rv = dir->Init(this, PR_FALSE);
1578    if (NS_FAILED(rv)) {
1579        *entries = nsnull;
1580        NS_RELEASE(dir);
1581    } else {
1582        *entries = dir; // transfer reference
1583    }
1584
1585    return rv;
1586}
1587
1588NS_IMETHODIMP
1589nsLocalFile::Load(PRLibrary **_retval)
1590{
1591    CHECK_mPath();
1592    NS_ENSURE_ARG_POINTER(_retval);
1593
1594    NS_TIMELINE_START_TIMER("PR_LoadLibrary");
1595
1596#ifdef NS_BUILD_REFCNT_LOGGING
1597    nsTraceRefcntImpl::SetActivityIsLegal(PR_FALSE);
1598#endif
1599
1600    *_retval = PR_LoadLibrary(mPath.get());
1601
1602#ifdef NS_BUILD_REFCNT_LOGGING
1603    nsTraceRefcntImpl::SetActivityIsLegal(PR_TRUE);
1604#endif
1605
1606    NS_TIMELINE_STOP_TIMER("PR_LoadLibrary");
1607    NS_TIMELINE_MARK_TIMER1("PR_LoadLibrary", mPath.get());
1608
1609    if (!*_retval)
1610        return NS_ERROR_FAILURE;
1611    return NS_OK;
1612}
1613
1614NS_IMETHODIMP
1615nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
1616{
1617    return GetNativePath(aPersistentDescriptor);
1618}
1619
1620NS_IMETHODIMP
1621nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
1622{
1623    return InitWithNativePath(aPersistentDescriptor);
1624}
1625
1626#ifdef XP_BEOS
1627NS_IMETHODIMP
1628nsLocalFile::Reveal()
1629{
1630    BPath bPath(mPath.get());
1631    PRBool isDirectory;
1632    if (NS_FAILED(IsDirectory(&isDirectory)))
1633        return NS_ERROR_FAILURE;
1634 
1635    if(!isDirectory)
1636        bPath.GetParent(&bPath);
1637    entry_ref ref;
1638    get_ref_for_path(bPath.Path(),&ref);
1639    BMessage message(B_REFS_RECEIVED);
1640    message.AddRef("refs",&ref);
1641    BMessenger messenger("application/x-vnd.Be-TRAK");
1642    messenger.SendMessage(&message);
1643    return NS_OK;
1644}
1645
1646NS_IMETHODIMP
1647nsLocalFile::Launch()
1648{
1649    entry_ref ref;
1650    get_ref_for_path (mPath.get(), &ref);
1651    be_roster->Launch (&ref);
1652
1653    return NS_OK;
1654}
1655#else
1656NS_IMETHODIMP
1657nsLocalFile::Reveal()
1658{
1659#ifdef MOZ_WIDGET_GTK2
1660    nsCOMPtr<nsIGnomeVFSService> vfs = do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID);
1661    if (!vfs)
1662        return NS_ERROR_FAILURE;
1663
1664    PRBool isDirectory;
1665    if (NS_FAILED(IsDirectory(&isDirectory)))
1666        return NS_ERROR_FAILURE;
1667
1668    if (isDirectory) {
1669        return vfs->ShowURIForInput(mPath);
1670    } else {
1671        nsCOMPtr<nsIFile> parentDir;
1672        nsCAutoString dirPath;
1673        if (NS_FAILED(GetParent(getter_AddRefs(parentDir))))
1674            return NS_ERROR_FAILURE;
1675        if (NS_FAILED(parentDir->GetNativePath(dirPath)))
1676            return NS_ERROR_FAILURE;
1677
1678        return vfs->ShowURIForInput(dirPath);
1679    }
1680#else
1681    return NS_ERROR_FAILURE;
1682#endif
1683}
1684
1685NS_IMETHODIMP
1686nsLocalFile::Launch()
1687{
1688#ifdef MOZ_WIDGET_GTK2
1689    nsCOMPtr<nsIGnomeVFSService> vfs = do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID);
1690    if (!vfs)
1691        return NS_ERROR_FAILURE;
1692
1693    return vfs->ShowURIForInput(mPath);
1694#else
1695    return NS_ERROR_FAILURE;
1696#endif
1697}
1698#endif
1699
1700nsresult
1701NS_NewNativeLocalFile(const nsACString &path, PRBool followSymlinks, nsILocalFile **result)
1702{
1703    nsLocalFile *file = new nsLocalFile();
1704    if (!file)
1705        return NS_ERROR_OUT_OF_MEMORY;
1706    NS_ADDREF(file);
1707
1708    if (!path.IsEmpty()) {
1709        nsresult rv = file->InitWithNativePath(path);
1710        if (NS_FAILED(rv)) {
1711            NS_RELEASE(file);
1712            return rv;
1713        }
1714    }
1715    *result = file;
1716    return NS_OK;
1717}
1718
1719//-----------------------------------------------------------------------------
1720// unicode support
1721//-----------------------------------------------------------------------------
1722
1723#define SET_UCS(func, ucsArg) \
1724    { \
1725        nsCAutoString buf; \
1726        nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
1727        if (NS_FAILED(rv)) \
1728            return rv; \
1729        return (func)(buf); \
1730    }
1731
1732#define GET_UCS(func, ucsArg) \
1733    { \
1734        nsCAutoString buf; \
1735        nsresult rv = (func)(buf); \
1736        if (NS_FAILED(rv)) return rv; \
1737        return NS_CopyNativeToUnicode(buf, ucsArg); \
1738    }
1739
1740#define SET_UCS_2ARGS_2(func, opaqueArg, ucsArg) \
1741    { \
1742        nsCAutoString buf; \
1743        nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
1744        if (NS_FAILED(rv)) \
1745            return rv; \
1746        return (func)(opaqueArg, buf); \
1747    }
1748
1749// Unicode interface Wrapper
1750nsresult 
1751nsLocalFile::InitWithPath(const nsAString &filePath)
1752{
1753    SET_UCS(InitWithNativePath, filePath);
1754}
1755nsresult 
1756nsLocalFile::Append(const nsAString &node)
1757{
1758    SET_UCS(AppendNative, node);
1759}
1760nsresult 
1761nsLocalFile::AppendRelativePath(const nsAString &node)
1762{
1763    SET_UCS(AppendRelativeNativePath, node);
1764}
1765nsresult 
1766nsLocalFile::GetLeafName(nsAString &aLeafName)
1767{
1768    GET_UCS(GetNativeLeafName, aLeafName);
1769}
1770nsresult 
1771nsLocalFile::SetLeafName(const nsAString &aLeafName)
1772{
1773    SET_UCS(SetNativeLeafName, aLeafName);
1774}
1775nsresult 
1776nsLocalFile::GetPath(nsAString &_retval)
1777{
1778    return NS_CopyNativeToUnicode(mPath, _retval);
1779}
1780nsresult 
1781nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
1782{
1783    SET_UCS_2ARGS_2(CopyToNative , newParentDir, newName);
1784}
1785nsresult 
1786nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
1787{
1788    SET_UCS_2ARGS_2(CopyToFollowingLinksNative , newParentDir, newName);
1789}
1790nsresult 
1791nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
1792{
1793    SET_UCS_2ARGS_2(MoveToNative, newParentDir, newName);
1794}
1795nsresult
1796nsLocalFile::GetTarget(nsAString &_retval)
1797{   
1798    GET_UCS(GetNativeTarget, _retval);
1799}
1800
1801// nsIHashable
1802
1803NS_IMETHODIMP
1804nsLocalFile::Equals(nsIHashable* aOther, PRBool *aResult)
1805{
1806    nsCOMPtr<nsIFile> otherFile(do_QueryInterface(aOther));
1807    if (!otherFile) {
1808        *aResult = PR_FALSE;
1809        return NS_OK;
1810    }
1811
1812    return Equals(otherFile, aResult);
1813}
1814
1815NS_IMETHODIMP
1816nsLocalFile::GetHashCode(PRUint32 *aResult)
1817{
1818    *aResult = nsCRT::HashCode(mPath.get());
1819    return NS_OK;
1820}
1821
1822nsresult
1823NS_NewLocalFile(const nsAString &path, PRBool followLinks, nsILocalFile* *result)
1824{
1825    nsCAutoString buf;
1826    nsresult rv = NS_CopyUnicodeToNative(path, buf);
1827    if (NS_FAILED(rv))
1828        return rv;
1829    return NS_NewNativeLocalFile(buf, followLinks, result);
1830}
1831
1832//-----------------------------------------------------------------------------
1833// global init/shutdown
1834//-----------------------------------------------------------------------------
1835
1836void
1837nsLocalFile::GlobalInit()
1838{
1839}
1840
1841void
1842nsLocalFile::GlobalShutdown()
1843{
1844}
Note: See TracBrowser for help on using the repository browser.