Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / xpcom / io / nsLocalFileUnix.cpp
blob6c4b5a593ef66538bbba8e4a07f25ec3d569292e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Mozilla Communicator client code, released
16 * March 31, 1998.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998-1999
21 * the Initial Developer. All Rights Reserved.
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 * Josh Aas <josh@mozilla.com>
34 * Alternatively, the contents of this file may be used under the terms of
35 * either of the GNU General Public License Version 2 or later (the "GPL"),
36 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
37 * in which case the provisions of the GPL or the LGPL are applicable instead
38 * of those above. If you wish to allow use of your version of this file only
39 * under the terms of either the GPL or the LGPL, and not to allow others to
40 * use your version of this file under the terms of the MPL, indicate your
41 * decision by deleting the provisions above and replace them with the notice
42 * and other provisions required by the GPL or the LGPL. If you do not delete
43 * the provisions above, a recipient may use your version of this file under
44 * the terms of any one of the MPL, the GPL or the LGPL.
46 * ***** END LICENSE BLOCK ***** */
48 /**
49 * Implementation of nsIFile for ``Unixy'' systems.
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
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"
85 #ifdef MOZ_WIDGET_GTK2
86 #include "nsIGnomeVFSService.h"
87 #endif
89 #include "nsNativeCharsetUtils.h"
90 #include "nsTraceRefcntImpl.h"
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
102 #define ENSURE_STAT_CACHE() \
103 PR_BEGIN_MACRO \
104 if (!FillStatCache()) \
105 return NSRESULT_FOR_ERRNO(); \
106 PR_END_MACRO
108 #define CHECK_mPath() \
109 PR_BEGIN_MACRO \
110 if (mPath.IsEmpty()) \
111 return NS_ERROR_NOT_INITIALIZED; \
112 PR_END_MACRO
114 /* directory enumerator */
115 class NS_COM
116 nsDirEnumeratorUnix : public nsISimpleEnumerator,
117 public nsIDirectoryEnumerator
119 public:
120 nsDirEnumeratorUnix();
122 // nsISupports interface
123 NS_DECL_ISUPPORTS
125 // nsISimpleEnumerator interface
126 NS_DECL_NSISIMPLEENUMERATOR
128 // nsIDirectoryEnumerator interface
129 NS_DECL_NSIDIRECTORYENUMERATOR
131 NS_IMETHOD Init(nsLocalFile *parent, PRBool ignored);
133 private:
134 ~nsDirEnumeratorUnix();
136 protected:
137 NS_IMETHOD GetNextEntry();
139 DIR *mDir;
140 struct dirent *mEntry;
141 nsCString mParentPath;
144 nsDirEnumeratorUnix::nsDirEnumeratorUnix() :
145 mDir(nsnull),
146 mEntry(nsnull)
150 nsDirEnumeratorUnix::~nsDirEnumeratorUnix()
152 Close();
155 NS_IMPL_ISUPPORTS2(nsDirEnumeratorUnix, nsISimpleEnumerator, nsIDirectoryEnumerator)
157 NS_IMETHODIMP
158 nsDirEnumeratorUnix::Init(nsLocalFile *parent, PRBool resolveSymlinks /*ignored*/)
160 nsCAutoString dirPath;
161 if (NS_FAILED(parent->GetNativePath(dirPath)) ||
162 dirPath.IsEmpty()) {
163 return NS_ERROR_FILE_INVALID_PATH;
166 if (NS_FAILED(parent->GetNativePath(mParentPath)))
167 return NS_ERROR_FAILURE;
169 mDir = opendir(dirPath.get());
170 if (!mDir)
171 return NSRESULT_FOR_ERRNO();
172 return GetNextEntry();
175 NS_IMETHODIMP
176 nsDirEnumeratorUnix::HasMoreElements(PRBool *result)
178 *result = mDir && mEntry;
179 if (!*result)
180 Close();
181 return NS_OK;
184 NS_IMETHODIMP
185 nsDirEnumeratorUnix::GetNext(nsISupports **_retval)
187 nsCOMPtr<nsIFile> file;
188 nsresult rv = GetNextFile(getter_AddRefs(file));
189 if (NS_FAILED(rv))
190 return rv;
191 NS_IF_ADDREF(*_retval = file);
192 return NS_OK;
195 NS_IMETHODIMP
196 nsDirEnumeratorUnix::GetNextEntry()
198 do {
199 errno = 0;
200 mEntry = readdir(mDir);
202 // end of dir or error
203 if (!mEntry)
204 return NSRESULT_FOR_ERRNO();
206 // keep going past "." and ".."
207 } while (mEntry->d_name[0] == '.' &&
208 (mEntry->d_name[1] == '\0' || // .\0
209 (mEntry->d_name[1] == '.' &&
210 mEntry->d_name[2] == '\0'))); // ..\0
211 return NS_OK;
214 NS_IMETHODIMP
215 nsDirEnumeratorUnix::GetNextFile(nsIFile **_retval)
217 nsresult rv;
218 if (!mDir || !mEntry) {
219 *_retval = nsnull;
220 return NS_OK;
223 nsCOMPtr<nsILocalFile> file = new nsLocalFile();
224 if (!file)
225 return NS_ERROR_OUT_OF_MEMORY;
227 if (NS_FAILED(rv = file->InitWithNativePath(mParentPath)) ||
228 NS_FAILED(rv = file->AppendNative(nsDependentCString(mEntry->d_name))))
229 return rv;
231 *_retval = file;
232 NS_ADDREF(*_retval);
233 return GetNextEntry();
236 NS_IMETHODIMP
237 nsDirEnumeratorUnix::Close()
239 if (mDir) {
240 closedir(mDir);
241 mDir = nsnull;
243 return NS_OK;
246 nsLocalFile::nsLocalFile()
250 nsLocalFile::nsLocalFile(const nsLocalFile& other)
251 : mPath(other.mPath)
255 NS_IMPL_THREADSAFE_ISUPPORTS3(nsLocalFile,
256 nsIFile,
257 nsILocalFile,
258 nsIHashable)
260 nsresult
261 nsLocalFile::nsLocalFileConstructor(nsISupports *outer,
262 const nsIID &aIID,
263 void **aInstancePtr)
265 NS_ENSURE_ARG_POINTER(aInstancePtr);
266 NS_ENSURE_NO_AGGREGATION(outer);
268 *aInstancePtr = nsnull;
270 nsCOMPtr<nsIFile> inst = new nsLocalFile();
271 if (!inst)
272 return NS_ERROR_OUT_OF_MEMORY;
273 return inst->QueryInterface(aIID, aInstancePtr);
276 PRBool
277 nsLocalFile::FillStatCache() {
278 #ifdef HAVE_STAT64
279 if (stat64(mPath.get(), &mCachedStat) == -1) {
280 // try lstat it may be a symlink
281 if (lstat64(mPath.get(), &mCachedStat) == -1) {
282 return PR_FALSE;
285 #else
286 if (stat(mPath.get(), &mCachedStat) == -1) {
287 // try lstat it may be a symlink
288 if (lstat(mPath.get(), &mCachedStat) == -1) {
289 return PR_FALSE;
292 #endif
293 return PR_TRUE;
296 NS_IMETHODIMP
297 nsLocalFile::Clone(nsIFile **file)
299 // Just copy-construct ourselves
300 *file = new nsLocalFile(*this);
301 if (!*file)
302 return NS_ERROR_OUT_OF_MEMORY;
304 NS_ADDREF(*file);
306 return NS_OK;
309 NS_IMETHODIMP
310 nsLocalFile::InitWithNativePath(const nsACString &filePath)
312 if (Substring(filePath, 0, 2).EqualsLiteral("~/")) {
313 nsCOMPtr<nsIFile> homeDir;
314 nsCAutoString homePath;
315 if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_HOME_DIR,
316 getter_AddRefs(homeDir))) ||
317 NS_FAILED(homeDir->GetNativePath(homePath))) {
318 return NS_ERROR_FAILURE;
321 mPath = homePath + Substring(filePath, 1, filePath.Length() - 1);
322 } else {
323 if (filePath.IsEmpty() || filePath.First() != '/')
324 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
325 mPath = filePath;
328 // trim off trailing slashes
329 ssize_t len = mPath.Length();
330 while ((len > 1) && (mPath[len - 1] == '/'))
331 --len;
332 mPath.SetLength(len);
334 return NS_OK;
337 NS_IMETHODIMP
338 nsLocalFile::CreateAllAncestors(PRUint32 permissions)
340 // <jband> I promise to play nice
341 char *buffer = mPath.BeginWriting(),
342 *slashp = buffer;
344 #ifdef DEBUG_NSIFILE
345 fprintf(stderr, "nsIFile: before: %s\n", buffer);
346 #endif
348 while ((slashp = strchr(slashp + 1, '/'))) {
350 * Sequences of '/' are equivalent to a single '/'.
352 if (slashp[1] == '/')
353 continue;
356 * If the path has a trailing slash, don't make the last component,
357 * because we'll get EEXIST in Create when we try to build the final
358 * component again, and it's easier to condition the logic here than
359 * there.
361 if (slashp[1] == '\0')
362 break;
364 /* Temporarily NUL-terminate here */
365 *slashp = '\0';
366 #ifdef DEBUG_NSIFILE
367 fprintf(stderr, "nsIFile: mkdir(\"%s\")\n", buffer);
368 #endif
369 int mkdir_result = mkdir(buffer, permissions);
370 int mkdir_errno = errno;
371 if (mkdir_result == -1) {
373 * Always set |errno| to EEXIST if the dir already exists
374 * (we have to do this here since the errno value is not consistent
375 * in all cases - various reasons like different platform,
376 * automounter-controlled dir, etc. can affect it (see bug 125489
377 * for details)).
379 if (access(buffer, F_OK) == 0) {
380 mkdir_errno = EEXIST;
384 /* Put the / back before we (maybe) return */
385 *slashp = '/';
388 * We could get EEXIST for an existing file -- not directory --
389 * with the name of one of our ancestors, but that's OK: we'll get
390 * ENOTDIR when we try to make the next component in the path,
391 * either here on back in Create, and error out appropriately.
393 if (mkdir_result == -1 && mkdir_errno != EEXIST)
394 return nsresultForErrno(mkdir_errno);
397 #ifdef DEBUG_NSIFILE
398 fprintf(stderr, "nsIFile: after: %s\n", buffer);
399 #endif
401 return NS_OK;
404 NS_IMETHODIMP
405 nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
407 *_retval = PR_Open(mPath.get(), flags, mode);
408 if (! *_retval)
409 return NS_ErrorAccordingToNSPR();
411 return NS_OK;
414 NS_IMETHODIMP
415 nsLocalFile::OpenANSIFileDesc(const char *mode, FILE **_retval)
417 *_retval = fopen(mPath.get(), mode);
418 if (! *_retval)
419 return NS_ERROR_FAILURE;
421 return NS_OK;
424 static int
425 do_create(const char *path, PRIntn flags, mode_t mode, PRFileDesc **_retval)
427 *_retval = PR_Open(path, flags, mode);
428 return *_retval ? 0 : -1;
431 static int
432 do_mkdir(const char *path, PRIntn flags, mode_t mode, PRFileDesc **_retval)
434 *_retval = nsnull;
435 return mkdir(path, mode);
438 nsresult
439 nsLocalFile::CreateAndKeepOpen(PRUint32 type, PRIntn flags,
440 PRUint32 permissions, PRFileDesc **_retval)
442 if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
443 return NS_ERROR_FILE_UNKNOWN_TYPE;
445 int result;
446 int (*createFunc)(const char *, PRIntn, mode_t, PRFileDesc **) =
447 (type == NORMAL_FILE_TYPE) ? do_create : do_mkdir;
449 result = createFunc(mPath.get(), flags, permissions, _retval);
450 if (result == -1 && errno == ENOENT) {
452 * If we failed because of missing ancestor components, try to create
453 * them and then retry the original creation.
455 * Ancestor directories get the same permissions as the file we're
456 * creating, with the X bit set for each of (user,group,other) with
457 * an R bit in the original permissions. If you want to do anything
458 * fancy like setgid or sticky bits, do it by hand.
460 int dirperm = permissions;
461 if (permissions & S_IRUSR)
462 dirperm |= S_IXUSR;
463 if (permissions & S_IRGRP)
464 dirperm |= S_IXGRP;
465 if (permissions & S_IROTH)
466 dirperm |= S_IXOTH;
468 #ifdef DEBUG_NSIFILE
469 fprintf(stderr, "nsIFile: perm = %o, dirperm = %o\n", permissions,
470 dirperm);
471 #endif
473 if (NS_FAILED(CreateAllAncestors(dirperm)))
474 return NS_ERROR_FAILURE;
476 #ifdef DEBUG_NSIFILE
477 fprintf(stderr, "nsIFile: Create(\"%s\") again\n", mPath.get());
478 #endif
479 result = createFunc(mPath.get(), flags, permissions, _retval);
481 return NSRESULT_FOR_RETURN(result);
484 NS_IMETHODIMP
485 nsLocalFile::Create(PRUint32 type, PRUint32 permissions)
487 PRFileDesc *junk = nsnull;
488 nsresult rv = CreateAndKeepOpen(type,
489 PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE |
490 PR_EXCL,
491 permissions,
492 &junk);
493 if (junk)
494 PR_Close(junk);
495 return rv;
498 NS_IMETHODIMP
499 nsLocalFile::AppendNative(const nsACString &fragment)
501 if (fragment.IsEmpty())
502 return NS_OK;
504 // only one component of path can be appended
505 nsACString::const_iterator begin, end;
506 if (FindCharInReadable('/', fragment.BeginReading(begin),
507 fragment.EndReading(end)))
508 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
510 return AppendRelativeNativePath(fragment);
513 NS_IMETHODIMP
514 nsLocalFile::AppendRelativeNativePath(const nsACString &fragment)
516 if (fragment.IsEmpty())
517 return NS_OK;
519 // No leading '/'
520 if (fragment.First() == '/')
521 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
523 if (mPath.EqualsLiteral("/"))
524 mPath.Append(fragment);
525 else
526 mPath.Append(NS_LITERAL_CSTRING("/") + fragment);
528 return NS_OK;
531 NS_IMETHODIMP
532 nsLocalFile::Normalize()
534 char resolved_path[PATH_MAX] = "";
535 char *resolved_path_ptr = nsnull;
537 #ifdef XP_BEOS
538 BEntry be_e(mPath.get(), true);
539 BPath be_p;
540 status_t err;
541 if ((err = be_e.GetPath(&be_p)) == B_OK) {
542 resolved_path_ptr = (char *)be_p.Path();
543 PL_strncpyz(resolved_path, resolved_path_ptr, PATH_MAX - 1);
545 #else
546 resolved_path_ptr = realpath(mPath.get(), resolved_path);
547 #endif
548 // if there is an error, the return is null.
549 if (!resolved_path_ptr)
550 return NSRESULT_FOR_ERRNO();
552 mPath = resolved_path;
553 return NS_OK;
556 void
557 nsLocalFile::LocateNativeLeafName(nsACString::const_iterator &begin,
558 nsACString::const_iterator &end)
560 // XXX perhaps we should cache this??
562 mPath.BeginReading(begin);
563 mPath.EndReading(end);
565 nsACString::const_iterator it = end;
566 nsACString::const_iterator stop = begin;
567 --stop;
568 while (--it != stop) {
569 if (*it == '/') {
570 begin = ++it;
571 return;
574 // else, the entire path is the leaf name (which means this
575 // isn't an absolute path... unexpected??)
578 NS_IMETHODIMP
579 nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
581 nsACString::const_iterator begin, end;
582 LocateNativeLeafName(begin, end);
583 aLeafName = Substring(begin, end);
584 return NS_OK;
587 NS_IMETHODIMP
588 nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
590 nsACString::const_iterator begin, end;
591 LocateNativeLeafName(begin, end);
592 mPath.Replace(begin.get() - mPath.get(), Distance(begin, end), aLeafName);
593 return NS_OK;
596 NS_IMETHODIMP
597 nsLocalFile::GetNativePath(nsACString &_retval)
599 _retval = mPath;
600 return NS_OK;
603 nsresult
604 nsLocalFile::GetNativeTargetPathName(nsIFile *newParent,
605 const nsACString &newName,
606 nsACString &_retval)
608 nsresult rv;
609 nsCOMPtr<nsIFile> oldParent;
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;
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;
636 nsACString::const_iterator nameBegin, nameEnd;
637 if (!newName.IsEmpty()) {
638 newName.BeginReading(nameBegin);
639 newName.EndReading(nameEnd);
641 else
642 LocateNativeLeafName(nameBegin, nameEnd);
644 nsCAutoString dirName;
645 if (NS_FAILED(rv = newParent->GetNativePath(dirName)))
646 return rv;
648 _retval = dirName
649 + NS_LITERAL_CSTRING("/")
650 + Substring(nameBegin, nameEnd);
651 return NS_OK;
654 nsresult
655 nsLocalFile::CopyDirectoryTo(nsIFile *newParent)
657 nsresult rv;
659 * dirCheck is used for various boolean test results such as from Equals,
660 * Exists, isDir, etc.
662 PRBool dirCheck, isSymlink;
663 PRUint32 oldPerms;
665 if (NS_FAILED(rv = IsDirectory(&dirCheck)))
666 return rv;
667 if (!dirCheck)
668 return CopyToNative(newParent, EmptyCString());
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;
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;
699 nsCOMPtr<nsISimpleEnumerator> dirIterator;
700 if (NS_FAILED(rv = GetDirectoryEntries(getter_AddRefs(dirIterator))))
701 return rv;
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;
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;
746 return NS_OK;
749 NS_IMETHODIMP
750 nsLocalFile::CopyToNative(nsIFile *newParent, const nsACString &newName)
752 nsresult rv;
753 // check to make sure that this has been initialized properly
754 CHECK_mPath();
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;
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;
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;
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;
789 #ifdef DEBUG_blizzard
790 printf("nsLocalFile::CopyTo() %s -> %s\n", mPath.get(), newPathName.get());
791 #endif
793 // actually create the file.
794 nsLocalFile *newFile = new nsLocalFile();
795 if (!newFile)
796 return NS_ERROR_OUT_OF_MEMORY;
798 nsCOMPtr<nsILocalFile> fileRef(newFile); // release on exit
800 rv = newFile->InitWithNativePath(newPathName);
801 if (NS_FAILED(rv))
802 return rv;
804 // get the old permissions
805 PRUint32 myPerms;
806 GetPermissions(&myPerms);
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.
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;
822 // open the old file, too
823 PRBool specialFile;
824 if (NS_FAILED(rv = IsSpecial(&specialFile))) {
825 PR_Close(newFD);
826 return rv;
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;
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;
845 #ifdef DEBUG_blizzard
846 PRInt32 totalRead = 0;
847 PRInt32 totalWritten = 0;
848 #endif
849 char buf[BUFSIZ];
850 PRInt32 bytesRead;
852 while ((bytesRead = PR_Read(oldFD, buf, BUFSIZ)) > 0) {
853 #ifdef DEBUG_blizzard
854 totalRead += bytesRead;
855 #endif
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;
863 NS_ASSERTION(bytesWritten == bytesRead, "short PR_Write?");
865 #ifdef DEBUG_blizzard
866 totalWritten += bytesWritten;
867 #endif
870 #ifdef DEBUG_blizzard
871 printf("read %d bytes, wrote %d bytes\n",
872 totalRead, totalWritten);
873 #endif
875 // close the files
876 PR_Close(newFD);
877 PR_Close(oldFD);
879 // check for read (or write) error after cleaning up
880 if (bytesRead < 0)
881 return NS_ERROR_OUT_OF_MEMORY;
883 return rv;
886 NS_IMETHODIMP
887 nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParent, const nsACString &newName)
889 return CopyToNative(newParent, newName);
892 NS_IMETHODIMP
893 nsLocalFile::MoveToNative(nsIFile *newParent, const nsACString &newName)
895 nsresult rv;
897 // check to make sure that this has been initialized properly
898 CHECK_mPath();
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;
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();
920 return rv;
923 NS_IMETHODIMP
924 nsLocalFile::Remove(PRBool recursive)
926 CHECK_mPath();
927 ENSURE_STAT_CACHE();
929 PRBool isSymLink;
931 nsresult rv = IsSymlink(&isSymLink);
932 if (NS_FAILED(rv))
933 return rv;
935 if (!recursive && isSymLink)
936 return NSRESULT_FOR_RETURN(unlink(mPath.get()));
938 if (S_ISDIR(mCachedStat.st_mode)) {
939 if (recursive) {
940 nsDirEnumeratorUnix *dir = new nsDirEnumeratorUnix();
941 if (!dir)
942 return NS_ERROR_OUT_OF_MEMORY;
944 nsCOMPtr<nsISimpleEnumerator> dirRef(dir); // release on exit
946 rv = dir->Init(this, PR_FALSE);
947 if (NS_FAILED(rv))
948 return rv;
950 PRBool more;
951 while (dir->HasMoreElements(&more), more) {
952 nsCOMPtr<nsISupports> item;
953 rv = dir->GetNext(getter_AddRefs(item));
954 if (NS_FAILED(rv))
955 return NS_ERROR_FAILURE;
957 nsCOMPtr<nsIFile> file = do_QueryInterface(item, &rv);
958 if (NS_FAILED(rv))
959 return NS_ERROR_FAILURE;
960 if (NS_FAILED(rv = file->Remove(recursive)))
961 return rv;
965 if (rmdir(mPath.get()) == -1)
966 return NSRESULT_FOR_ERRNO();
967 } else {
968 if (unlink(mPath.get()) == -1)
969 return NSRESULT_FOR_ERRNO();
972 return NS_OK;
975 NS_IMETHODIMP
976 nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModTime)
978 CHECK_mPath();
979 NS_ENSURE_ARG(aLastModTime);
981 PRFileInfo64 info;
982 if (PR_GetFileInfo64(mPath.get(), &info) != PR_SUCCESS)
983 return NSRESULT_FOR_ERRNO();
985 // PRTime is a 64 bit value
986 // microseconds -> milliseconds
987 PRInt64 usecPerMsec;
988 LL_I2L(usecPerMsec, PR_USEC_PER_MSEC);
989 LL_DIV(*aLastModTime, info.modifyTime, usecPerMsec);
990 return NS_OK;
993 NS_IMETHODIMP
994 nsLocalFile::SetLastModifiedTime(PRInt64 aLastModTime)
996 CHECK_mPath();
998 int result;
999 if (! LL_IS_ZERO(aLastModTime)) {
1000 ENSURE_STAT_CACHE();
1001 struct utimbuf ut;
1002 ut.actime = mCachedStat.st_atime;
1004 // convert milliseconds to seconds since the unix epoch
1005 double dTime;
1006 LL_L2D(dTime, aLastModTime);
1007 ut.modtime = (time_t) (dTime / PR_MSEC_PER_SEC);
1008 result = utime(mPath.get(), &ut);
1009 } else {
1010 result = utime(mPath.get(), nsnull);
1012 return NSRESULT_FOR_RETURN(result);
1015 NS_IMETHODIMP
1016 nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModTimeOfLink)
1018 CHECK_mPath();
1019 NS_ENSURE_ARG(aLastModTimeOfLink);
1021 struct stat sbuf;
1022 if (lstat(mPath.get(), &sbuf) == -1)
1023 return NSRESULT_FOR_ERRNO();
1024 LL_I2L(*aLastModTimeOfLink, (PRInt32)sbuf.st_mtime);
1026 // lstat returns st_mtime in seconds
1027 PRInt64 msecPerSec;
1028 LL_I2L(msecPerSec, PR_MSEC_PER_SEC);
1029 LL_MUL(*aLastModTimeOfLink, *aLastModTimeOfLink, msecPerSec);
1031 return NS_OK;
1035 * utime(2) may or may not dereference symlinks, joy.
1037 NS_IMETHODIMP
1038 nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModTimeOfLink)
1040 return SetLastModifiedTime(aLastModTimeOfLink);
1044 * Only send back permissions bits: maybe we want to send back the whole
1045 * mode_t to permit checks against other file types?
1048 #define NORMALIZE_PERMS(mode) ((mode)& (S_IRWXU | S_IRWXG | S_IRWXO))
1050 NS_IMETHODIMP
1051 nsLocalFile::GetPermissions(PRUint32 *aPermissions)
1053 NS_ENSURE_ARG(aPermissions);
1054 ENSURE_STAT_CACHE();
1055 *aPermissions = NORMALIZE_PERMS(mCachedStat.st_mode);
1056 return NS_OK;
1059 NS_IMETHODIMP
1060 nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink)
1062 CHECK_mPath();
1063 NS_ENSURE_ARG(aPermissionsOfLink);
1065 struct stat sbuf;
1066 if (lstat(mPath.get(), &sbuf) == -1)
1067 return NSRESULT_FOR_ERRNO();
1068 *aPermissionsOfLink = NORMALIZE_PERMS(sbuf.st_mode);
1069 return NS_OK;
1072 NS_IMETHODIMP
1073 nsLocalFile::SetPermissions(PRUint32 aPermissions)
1075 CHECK_mPath();
1078 * Race condition here: we should use fchmod instead, there's no way to
1079 * guarantee the name still refers to the same file.
1081 if (chmod(mPath.get(), aPermissions) < 0)
1082 return NSRESULT_FOR_ERRNO();
1083 return NS_OK;
1086 NS_IMETHODIMP
1087 nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions)
1089 // There isn't a consistent mechanism for doing this on UNIX platforms. We
1090 // might want to carefully implement this in the future though.
1091 return NS_ERROR_NOT_IMPLEMENTED;
1094 NS_IMETHODIMP
1095 nsLocalFile::GetFileSize(PRInt64 *aFileSize)
1097 NS_ENSURE_ARG_POINTER(aFileSize);
1098 *aFileSize = LL_ZERO;
1099 ENSURE_STAT_CACHE();
1101 #if defined(VMS)
1102 /* Only two record formats can report correct file content size */
1103 if ((mCachedStat.st_fab_rfm != FAB$C_STMLF) &&
1104 (mCachedStat.st_fab_rfm != FAB$C_STMCR)) {
1105 return NS_ERROR_FAILURE;
1107 #endif
1109 if (!S_ISDIR(mCachedStat.st_mode)) {
1110 #ifdef HAVE_STAT64
1111 *aFileSize = mCachedStat.st_size;
1112 #else
1113 LL_UI2L(*aFileSize, (PRUint32)mCachedStat.st_size);
1114 #endif
1116 return NS_OK;
1119 NS_IMETHODIMP
1120 nsLocalFile::SetFileSize(PRInt64 aFileSize)
1122 CHECK_mPath();
1124 PRInt32 size;
1125 LL_L2I(size, aFileSize);
1126 /* XXX truncate64? */
1127 if (truncate(mPath.get(), (off_t)size) == -1)
1128 return NSRESULT_FOR_ERRNO();
1129 return NS_OK;
1132 NS_IMETHODIMP
1133 nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSize)
1135 CHECK_mPath();
1136 NS_ENSURE_ARG(aFileSize);
1138 #ifdef HAVE_LSTAT64
1139 struct stat64 sbuf;
1140 if (lstat64(mPath.get(), &sbuf) == -1)
1141 return NSRESULT_FOR_ERRNO();
1142 *aFileSize = sbuf.st_size;
1143 #else
1144 struct stat sbuf;
1145 if (lstat(mPath.get(), &sbuf) == -1)
1146 return NSRESULT_FOR_ERRNO();
1147 LL_UI2L(*aFileSize, (PRUint32)sbuf.st_size);
1148 #endif
1149 return NS_OK;
1152 NS_IMETHODIMP
1153 nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
1155 NS_ENSURE_ARG_POINTER(aDiskSpaceAvailable);
1157 // These systems have the operations necessary to check disk space.
1159 #if defined(HAVE_SYS_STATFS_H) || defined(HAVE_SYS_STATVFS_H)
1161 // check to make sure that mPath is properly initialized
1162 CHECK_mPath();
1164 struct STATFS fs_buf;
1167 * Members of the STATFS struct that you should know about:
1168 * f_bsize = block size on disk.
1169 * f_bavail = number of free blocks available to a non-superuser.
1170 * f_bfree = number of total free blocks in file system.
1173 if (STATFS(mPath.get(), &fs_buf) < 0) {
1174 // The call to STATFS failed.
1175 #ifdef DEBUG
1176 printf("ERROR: GetDiskSpaceAvailable: STATFS call FAILED. \n");
1177 #endif
1178 return NS_ERROR_FAILURE;
1180 #ifdef DEBUG_DISK_SPACE
1181 printf("DiskSpaceAvailable: %d bytes\n",
1182 fs_buf.f_bsize * (fs_buf.f_bavail - 1));
1183 #endif
1186 * The number of bytes free == The number of free blocks available to
1187 * a non-superuser, minus one as a fudge factor, multiplied by the size
1188 * of the aforementioned blocks.
1190 PRInt64 bsize, bavail;
1192 LL_I2L(bsize, fs_buf.f_bsize);
1193 LL_I2L(bavail, fs_buf.f_bavail - 1);
1194 LL_MUL(*aDiskSpaceAvailable, bsize, bavail);
1195 return NS_OK;
1197 #else
1199 * This platform doesn't have statfs or statvfs. I'm sure that there's
1200 * a way to check for free disk space on platforms that don't have statfs
1201 * (I'm SURE they have df, for example).
1203 * Until we figure out how to do that, lets be honest and say that this
1204 * command isn't implemented properly for these platforms yet.
1206 #ifdef DEBUG
1207 printf("ERROR: GetDiskSpaceAvailable: Not implemented for plaforms without statfs.\n");
1208 #endif
1209 return NS_ERROR_NOT_IMPLEMENTED;
1211 #endif /* HAVE_SYS_STATFS_H or HAVE_SYS_STATVFS_H */
1215 NS_IMETHODIMP
1216 nsLocalFile::GetParent(nsIFile **aParent)
1218 CHECK_mPath();
1219 NS_ENSURE_ARG_POINTER(aParent);
1220 *aParent = nsnull;
1222 // if '/' we are at the top of the volume, return null
1223 if (mPath.Equals("/"))
1224 return NS_OK;
1226 // <brendan, after jband> I promise to play nice
1227 char *buffer = mPath.BeginWriting(),
1228 *slashp = buffer;
1230 // find the last significant slash in buffer
1231 slashp = strrchr(buffer, '/');
1232 NS_ASSERTION(slashp, "non-canonical mPath?");
1233 if (!slashp)
1234 return NS_ERROR_FILE_INVALID_PATH;
1236 // for the case where we are at '/'
1237 if (slashp == buffer)
1238 slashp++;
1240 // temporarily terminate buffer at the last significant slash
1241 char c = *slashp;
1242 *slashp = '\0';
1244 nsCOMPtr<nsILocalFile> localFile;
1245 nsresult rv = NS_NewNativeLocalFile(nsDependentCString(buffer), PR_TRUE,
1246 getter_AddRefs(localFile));
1248 // make buffer whole again
1249 *slashp = c;
1251 if (NS_SUCCEEDED(rv) && localFile)
1252 rv = CallQueryInterface(localFile, aParent);
1253 return rv;
1257 * The results of Exists, isWritable and isReadable are not cached.
1261 #if defined(XP_BEOS) || defined(SOLARIS)
1262 // access() is buggy in BeOS POSIX implementation, at least for BFS, using stat() instead
1263 // see bug 169506, https://bugzilla.mozilla.org/show_bug.cgi?id=169506
1264 // access() problem also exists in Solaris POSIX implementation
1265 // see bug 351595, https://bugzilla.mozilla.org/show_bug.cgi?id=351595
1266 NS_IMETHODIMP
1267 nsLocalFile::Exists(PRBool *_retval)
1269 CHECK_mPath();
1270 NS_ENSURE_ARG_POINTER(_retval);
1271 struct stat buf;
1273 *_retval = (stat(mPath.get(), &buf) == 0);
1274 return NS_OK;
1277 NS_IMETHODIMP
1278 nsLocalFile::IsWritable(PRBool *_retval)
1280 CHECK_mPath();
1281 NS_ENSURE_ARG_POINTER(_retval);
1282 struct stat buf;
1284 *_retval = (stat(mPath.get(), &buf) == 0);
1285 if (*_retval || errno == EACCES) {
1286 *_retval = *_retval && (buf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH ));
1287 return NS_OK;
1289 return NSRESULT_FOR_ERRNO();
1292 NS_IMETHODIMP
1293 nsLocalFile::IsReadable(PRBool *_retval)
1295 CHECK_mPath();
1296 NS_ENSURE_ARG_POINTER(_retval);
1297 struct stat buf;
1299 *_retval = (stat(mPath.get(), &buf) == 0);
1300 if (*_retval || errno == EACCES) {
1301 *_retval = *_retval && (buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH ));
1302 return NS_OK;
1304 return NSRESULT_FOR_ERRNO();
1307 NS_IMETHODIMP
1308 nsLocalFile::IsExecutable(PRBool *_retval)
1310 CHECK_mPath();
1311 NS_ENSURE_ARG_POINTER(_retval);
1312 struct stat buf;
1314 *_retval = (stat(mPath.get(), &buf) == 0);
1315 if (*_retval || errno == EACCES) {
1316 *_retval = *_retval && (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH ));
1317 return NS_OK;
1319 return NSRESULT_FOR_ERRNO();
1321 #else
1323 NS_IMETHODIMP
1324 nsLocalFile::Exists(PRBool *_retval)
1326 CHECK_mPath();
1327 NS_ENSURE_ARG_POINTER(_retval);
1329 *_retval = (access(mPath.get(), F_OK) == 0);
1330 return NS_OK;
1334 NS_IMETHODIMP
1335 nsLocalFile::IsWritable(PRBool *_retval)
1337 CHECK_mPath();
1338 NS_ENSURE_ARG_POINTER(_retval);
1340 *_retval = (access(mPath.get(), W_OK) == 0);
1341 if (*_retval || errno == EACCES)
1342 return NS_OK;
1343 return NSRESULT_FOR_ERRNO();
1346 NS_IMETHODIMP
1347 nsLocalFile::IsReadable(PRBool *_retval)
1349 CHECK_mPath();
1350 NS_ENSURE_ARG_POINTER(_retval);
1352 *_retval = (access(mPath.get(), R_OK) == 0);
1353 if (*_retval || errno == EACCES)
1354 return NS_OK;
1355 return NSRESULT_FOR_ERRNO();
1358 NS_IMETHODIMP
1359 nsLocalFile::IsExecutable(PRBool *_retval)
1361 CHECK_mPath();
1362 NS_ENSURE_ARG_POINTER(_retval);
1364 *_retval = (access(mPath.get(), X_OK) == 0);
1365 if (*_retval || errno == EACCES)
1366 return NS_OK;
1367 return NSRESULT_FOR_ERRNO();
1369 #endif
1370 NS_IMETHODIMP
1371 nsLocalFile::IsDirectory(PRBool *_retval)
1373 NS_ENSURE_ARG_POINTER(_retval);
1374 *_retval = PR_FALSE;
1375 ENSURE_STAT_CACHE();
1376 *_retval = S_ISDIR(mCachedStat.st_mode);
1377 return NS_OK;
1380 NS_IMETHODIMP
1381 nsLocalFile::IsFile(PRBool *_retval)
1383 NS_ENSURE_ARG_POINTER(_retval);
1384 *_retval = PR_FALSE;
1385 ENSURE_STAT_CACHE();
1386 *_retval = S_ISREG(mCachedStat.st_mode);
1387 return NS_OK;
1390 NS_IMETHODIMP
1391 nsLocalFile::IsHidden(PRBool *_retval)
1393 NS_ENSURE_ARG_POINTER(_retval);
1394 nsACString::const_iterator begin, end;
1395 LocateNativeLeafName(begin, end);
1396 *_retval = (*begin == '.');
1397 return NS_OK;
1400 NS_IMETHODIMP
1401 nsLocalFile::IsSymlink(PRBool *_retval)
1403 NS_ENSURE_ARG_POINTER(_retval);
1404 CHECK_mPath();
1406 struct stat symStat;
1407 lstat(mPath.get(), &symStat);
1408 *_retval=S_ISLNK(symStat.st_mode);
1409 return NS_OK;
1412 NS_IMETHODIMP
1413 nsLocalFile::IsSpecial(PRBool *_retval)
1415 NS_ENSURE_ARG_POINTER(_retval);
1416 ENSURE_STAT_CACHE();
1417 *_retval = S_ISCHR(mCachedStat.st_mode) ||
1418 S_ISBLK(mCachedStat.st_mode) ||
1419 #ifdef S_ISSOCK
1420 S_ISSOCK(mCachedStat.st_mode) ||
1421 #endif
1422 S_ISFIFO(mCachedStat.st_mode);
1424 return NS_OK;
1427 NS_IMETHODIMP
1428 nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
1430 NS_ENSURE_ARG(inFile);
1431 NS_ENSURE_ARG_POINTER(_retval);
1432 *_retval = PR_FALSE;
1434 nsresult rv;
1435 nsCAutoString inPath;
1437 if (NS_FAILED(rv = inFile->GetNativePath(inPath)))
1438 return rv;
1440 *_retval = !FILE_STRCMP(inPath.get(), mPath.get());
1441 return NS_OK;
1444 NS_IMETHODIMP
1445 nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval)
1447 CHECK_mPath();
1448 NS_ENSURE_ARG(inFile);
1449 NS_ENSURE_ARG_POINTER(_retval);
1451 nsCAutoString inPath;
1452 nsresult rv;
1454 if (NS_FAILED(rv = inFile->GetNativePath(inPath)))
1455 return rv;
1457 *_retval = PR_FALSE;
1459 ssize_t len = mPath.Length();
1460 if (FILE_STRNCMP(mPath.get(), inPath.get(), len) == 0) {
1461 // Now make sure that the |inFile|'s path has a separator at len,
1462 // which implies that it has more components after len.
1463 if (inPath[len] == '/')
1464 *_retval = PR_TRUE;
1467 return NS_OK;
1470 NS_IMETHODIMP
1471 nsLocalFile::GetNativeTarget(nsACString &_retval)
1473 CHECK_mPath();
1474 _retval.Truncate();
1476 struct stat symStat;
1477 lstat(mPath.get(), &symStat);
1478 if (!S_ISLNK(symStat.st_mode))
1479 return NS_ERROR_FILE_INVALID_PATH;
1481 PRInt64 targetSize64;
1482 if (NS_FAILED(GetFileSizeOfLink(&targetSize64)))
1483 return NS_ERROR_FAILURE;
1485 PRInt32 size;
1486 LL_L2I(size, targetSize64);
1487 char *target = (char *)nsMemory::Alloc(size + 1);
1488 if (!target)
1489 return NS_ERROR_OUT_OF_MEMORY;
1491 if (readlink(mPath.get(), target, (size_t)size) < 0) {
1492 nsMemory::Free(target);
1493 return NSRESULT_FOR_ERRNO();
1495 target[size] = '\0';
1497 nsresult rv;
1498 PRBool isSymlink;
1499 nsCOMPtr<nsIFile> self(this);
1500 nsCOMPtr<nsIFile> parent;
1501 while (NS_SUCCEEDED(rv = self->GetParent(getter_AddRefs(parent)))) {
1502 NS_ASSERTION(parent != nsnull, "no parent?!");
1504 if (target[0] != '/') {
1505 nsCOMPtr<nsILocalFile> localFile(do_QueryInterface(parent, &rv));
1506 if (NS_FAILED(rv))
1507 break;
1508 if (NS_FAILED(rv = localFile->AppendRelativeNativePath(nsDependentCString(target))))
1509 break;
1510 if (NS_FAILED(rv = localFile->GetNativePath(_retval)))
1511 break;
1512 if (NS_FAILED(rv = parent->IsSymlink(&isSymlink)))
1513 break;
1514 self = parent;
1515 } else {
1516 nsCOMPtr<nsILocalFile> localFile;
1517 rv = NS_NewNativeLocalFile(nsDependentCString(target), PR_TRUE,
1518 getter_AddRefs(localFile));
1519 if (NS_FAILED(rv))
1520 break;
1521 if (NS_FAILED(rv = localFile->IsSymlink(&isSymlink)))
1522 break;
1523 _retval = target; // XXX can we avoid this buffer copy?
1524 self = do_QueryInterface(localFile);
1526 if (NS_FAILED(rv) || !isSymlink)
1527 break;
1529 const nsPromiseFlatCString &flatRetval = PromiseFlatCString(_retval);
1531 // strip off any and all trailing '/'
1532 PRInt32 len = strlen(target);
1533 while (target[len-1] == '/' && len > 1)
1534 target[--len] = '\0';
1535 if (lstat(flatRetval.get(), &symStat) < 0) {
1536 rv = NSRESULT_FOR_ERRNO();
1537 break;
1539 if (!S_ISLNK(symStat.st_mode)) {
1540 rv = NS_ERROR_FILE_INVALID_PATH;
1541 break;
1543 size = symStat.st_size;
1544 if (readlink(flatRetval.get(), target, size) < 0) {
1545 rv = NSRESULT_FOR_ERRNO();
1546 break;
1548 target[size] = '\0';
1550 _retval.Truncate();
1553 nsMemory::Free(target);
1555 if (NS_FAILED(rv))
1556 _retval.Truncate();
1557 return rv;
1560 /* attribute PRBool followLinks; */
1561 NS_IMETHODIMP
1562 nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
1564 *aFollowLinks = PR_TRUE;
1565 return NS_OK;
1568 NS_IMETHODIMP
1569 nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
1571 return NS_OK;
1574 NS_IMETHODIMP
1575 nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator **entries)
1577 nsDirEnumeratorUnix *dir = new nsDirEnumeratorUnix();
1578 if (!dir)
1579 return NS_ERROR_OUT_OF_MEMORY;
1581 NS_ADDREF(dir);
1582 nsresult rv = dir->Init(this, PR_FALSE);
1583 if (NS_FAILED(rv)) {
1584 *entries = nsnull;
1585 NS_RELEASE(dir);
1586 } else {
1587 *entries = dir; // transfer reference
1590 return rv;
1593 NS_IMETHODIMP
1594 nsLocalFile::Load(PRLibrary **_retval)
1596 CHECK_mPath();
1597 NS_ENSURE_ARG_POINTER(_retval);
1599 NS_TIMELINE_START_TIMER("PR_LoadLibrary");
1601 #ifdef NS_BUILD_REFCNT_LOGGING
1602 nsTraceRefcntImpl::SetActivityIsLegal(PR_FALSE);
1603 #endif
1605 *_retval = PR_LoadLibrary(mPath.get());
1607 #ifdef NS_BUILD_REFCNT_LOGGING
1608 nsTraceRefcntImpl::SetActivityIsLegal(PR_TRUE);
1609 #endif
1611 NS_TIMELINE_STOP_TIMER("PR_LoadLibrary");
1612 NS_TIMELINE_MARK_TIMER1("PR_LoadLibrary", mPath.get());
1614 if (!*_retval)
1615 return NS_ERROR_FAILURE;
1616 return NS_OK;
1619 NS_IMETHODIMP
1620 nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
1622 return GetNativePath(aPersistentDescriptor);
1625 NS_IMETHODIMP
1626 nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
1628 return InitWithNativePath(aPersistentDescriptor);
1631 #ifdef XP_BEOS
1632 NS_IMETHODIMP
1633 nsLocalFile::Reveal()
1635 BPath bPath(mPath.get());
1636 PRBool isDirectory;
1637 if (NS_FAILED(IsDirectory(&isDirectory)))
1638 return NS_ERROR_FAILURE;
1640 if(!isDirectory)
1641 bPath.GetParent(&bPath);
1642 entry_ref ref;
1643 get_ref_for_path(bPath.Path(),&ref);
1644 BMessage message(B_REFS_RECEIVED);
1645 message.AddRef("refs",&ref);
1646 BMessenger messenger("application/x-vnd.Be-TRAK");
1647 messenger.SendMessage(&message);
1648 return NS_OK;
1651 NS_IMETHODIMP
1652 nsLocalFile::Launch()
1654 entry_ref ref;
1655 get_ref_for_path (mPath.get(), &ref);
1656 be_roster->Launch (&ref);
1658 return NS_OK;
1660 #else
1661 NS_IMETHODIMP
1662 nsLocalFile::Reveal()
1664 #ifdef MOZ_WIDGET_GTK2
1665 nsCOMPtr<nsIGnomeVFSService> vfs = do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID);
1666 if (!vfs)
1667 return NS_ERROR_FAILURE;
1669 PRBool isDirectory;
1670 if (NS_FAILED(IsDirectory(&isDirectory)))
1671 return NS_ERROR_FAILURE;
1673 if (isDirectory) {
1674 return vfs->ShowURIForInput(mPath);
1675 } else {
1676 nsCOMPtr<nsIFile> parentDir;
1677 nsCAutoString dirPath;
1678 if (NS_FAILED(GetParent(getter_AddRefs(parentDir))))
1679 return NS_ERROR_FAILURE;
1680 if (NS_FAILED(parentDir->GetNativePath(dirPath)))
1681 return NS_ERROR_FAILURE;
1683 return vfs->ShowURIForInput(dirPath);
1685 #else
1686 return NS_ERROR_FAILURE;
1687 #endif
1690 NS_IMETHODIMP
1691 nsLocalFile::Launch()
1693 #ifdef MOZ_WIDGET_GTK2
1694 nsCOMPtr<nsIGnomeVFSService> vfs = do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID);
1695 if (!vfs)
1696 return NS_ERROR_FAILURE;
1698 return vfs->ShowURIForInput(mPath);
1699 #else
1700 return NS_ERROR_FAILURE;
1701 #endif
1703 #endif
1705 nsresult
1706 NS_NewNativeLocalFile(const nsACString &path, PRBool followSymlinks, nsILocalFile **result)
1708 nsLocalFile *file = new nsLocalFile();
1709 if (!file)
1710 return NS_ERROR_OUT_OF_MEMORY;
1711 NS_ADDREF(file);
1713 if (!path.IsEmpty()) {
1714 nsresult rv = file->InitWithNativePath(path);
1715 if (NS_FAILED(rv)) {
1716 NS_RELEASE(file);
1717 return rv;
1720 *result = file;
1721 return NS_OK;
1724 //-----------------------------------------------------------------------------
1725 // unicode support
1726 //-----------------------------------------------------------------------------
1728 #define SET_UCS(func, ucsArg) \
1730 nsCAutoString buf; \
1731 nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
1732 if (NS_FAILED(rv)) \
1733 return rv; \
1734 return (func)(buf); \
1737 #define GET_UCS(func, ucsArg) \
1739 nsCAutoString buf; \
1740 nsresult rv = (func)(buf); \
1741 if (NS_FAILED(rv)) return rv; \
1742 return NS_CopyNativeToUnicode(buf, ucsArg); \
1745 #define SET_UCS_2ARGS_2(func, opaqueArg, ucsArg) \
1747 nsCAutoString buf; \
1748 nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
1749 if (NS_FAILED(rv)) \
1750 return rv; \
1751 return (func)(opaqueArg, buf); \
1754 // Unicode interface Wrapper
1755 nsresult
1756 nsLocalFile::InitWithPath(const nsAString &filePath)
1758 SET_UCS(InitWithNativePath, filePath);
1760 nsresult
1761 nsLocalFile::Append(const nsAString &node)
1763 SET_UCS(AppendNative, node);
1765 nsresult
1766 nsLocalFile::AppendRelativePath(const nsAString &node)
1768 SET_UCS(AppendRelativeNativePath, node);
1770 nsresult
1771 nsLocalFile::GetLeafName(nsAString &aLeafName)
1773 GET_UCS(GetNativeLeafName, aLeafName);
1775 nsresult
1776 nsLocalFile::SetLeafName(const nsAString &aLeafName)
1778 SET_UCS(SetNativeLeafName, aLeafName);
1780 nsresult
1781 nsLocalFile::GetPath(nsAString &_retval)
1783 return NS_CopyNativeToUnicode(mPath, _retval);
1785 nsresult
1786 nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
1788 SET_UCS_2ARGS_2(CopyToNative , newParentDir, newName);
1790 nsresult
1791 nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
1793 SET_UCS_2ARGS_2(CopyToFollowingLinksNative , newParentDir, newName);
1795 nsresult
1796 nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
1798 SET_UCS_2ARGS_2(MoveToNative, newParentDir, newName);
1800 nsresult
1801 nsLocalFile::GetTarget(nsAString &_retval)
1803 GET_UCS(GetNativeTarget, _retval);
1806 // nsIHashable
1808 NS_IMETHODIMP
1809 nsLocalFile::Equals(nsIHashable* aOther, PRBool *aResult)
1811 nsCOMPtr<nsIFile> otherFile(do_QueryInterface(aOther));
1812 if (!otherFile) {
1813 *aResult = PR_FALSE;
1814 return NS_OK;
1817 return Equals(otherFile, aResult);
1820 NS_IMETHODIMP
1821 nsLocalFile::GetHashCode(PRUint32 *aResult)
1823 *aResult = nsCRT::HashCode(mPath.get());
1824 return NS_OK;
1827 nsresult
1828 NS_NewLocalFile(const nsAString &path, PRBool followLinks, nsILocalFile* *result)
1830 nsCAutoString buf;
1831 nsresult rv = NS_CopyUnicodeToNative(path, buf);
1832 if (NS_FAILED(rv))
1833 return rv;
1834 return NS_NewNativeLocalFile(buf, followLinks, result);
1837 //-----------------------------------------------------------------------------
1838 // global init/shutdown
1839 //-----------------------------------------------------------------------------
1841 void
1842 nsLocalFile::GlobalInit()
1846 void
1847 nsLocalFile::GlobalShutdown()