On x86 compilers without fastcall, simulate it when invoking traces and un-simulate...
[wine-gecko.git] / xpcom / io / nsLocalFileUnix.cpp
blob8ab912f09823cef3fe48c307b60ea5437853eca2
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>
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.
45 * ***** END LICENSE BLOCK ***** */
47 /**
48 * Implementation of nsIFile for ``Unixy'' systems.
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
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 VALIDATE_STAT_CACHE() \
103 PR_BEGIN_MACRO \
104 if (!mHaveCachedStat) { \
105 FillStatCache(); \
106 if (!mHaveCachedStat) \
107 return NSRESULT_FOR_ERRNO(); \
109 PR_END_MACRO
111 #define CHECK_mPath() \
112 PR_BEGIN_MACRO \
113 if (mPath.IsEmpty()) \
114 return NS_ERROR_NOT_INITIALIZED; \
115 PR_END_MACRO
117 /* directory enumerator */
118 class NS_COM
119 nsDirEnumeratorUnix : public nsISimpleEnumerator,
120 public nsIDirectoryEnumerator
122 public:
123 nsDirEnumeratorUnix();
125 // nsISupports interface
126 NS_DECL_ISUPPORTS
128 // nsISimpleEnumerator interface
129 NS_DECL_NSISIMPLEENUMERATOR
131 // nsIDirectoryEnumerator interface
132 NS_DECL_NSIDIRECTORYENUMERATOR
134 NS_IMETHOD Init(nsLocalFile *parent, PRBool ignored);
136 private:
137 ~nsDirEnumeratorUnix();
139 protected:
140 NS_IMETHOD GetNextEntry();
142 DIR *mDir;
143 struct dirent *mEntry;
144 nsCString mParentPath;
147 nsDirEnumeratorUnix::nsDirEnumeratorUnix() :
148 mDir(nsnull),
149 mEntry(nsnull)
153 nsDirEnumeratorUnix::~nsDirEnumeratorUnix()
155 Close();
158 NS_IMPL_ISUPPORTS2(nsDirEnumeratorUnix, nsISimpleEnumerator, nsIDirectoryEnumerator)
160 NS_IMETHODIMP
161 nsDirEnumeratorUnix::Init(nsLocalFile *parent, PRBool resolveSymlinks /*ignored*/)
163 nsCAutoString dirPath;
164 if (NS_FAILED(parent->GetNativePath(dirPath)) ||
165 dirPath.IsEmpty()) {
166 return NS_ERROR_FILE_INVALID_PATH;
169 if (NS_FAILED(parent->GetNativePath(mParentPath)))
170 return NS_ERROR_FAILURE;
172 mDir = opendir(dirPath.get());
173 if (!mDir)
174 return NSRESULT_FOR_ERRNO();
175 return GetNextEntry();
178 NS_IMETHODIMP
179 nsDirEnumeratorUnix::HasMoreElements(PRBool *result)
181 *result = mDir && mEntry;
182 if (!*result)
183 Close();
184 return NS_OK;
187 NS_IMETHODIMP
188 nsDirEnumeratorUnix::GetNext(nsISupports **_retval)
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;
198 NS_IMETHODIMP
199 nsDirEnumeratorUnix::GetNextEntry()
201 do {
202 errno = 0;
203 mEntry = readdir(mDir);
205 // end of dir or error
206 if (!mEntry)
207 return NSRESULT_FOR_ERRNO();
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;
217 NS_IMETHODIMP
218 nsDirEnumeratorUnix::GetNextFile(nsIFile **_retval)
220 nsresult rv;
221 if (!mDir || !mEntry) {
222 *_retval = nsnull;
223 return NS_OK;
226 nsCOMPtr<nsILocalFile> file = new nsLocalFile();
227 if (!file)
228 return NS_ERROR_OUT_OF_MEMORY;
230 if (NS_FAILED(rv = file->InitWithNativePath(mParentPath)) ||
231 NS_FAILED(rv = file->AppendNative(nsDependentCString(mEntry->d_name))))
232 return rv;
234 *_retval = file;
235 NS_ADDREF(*_retval);
236 return GetNextEntry();
239 NS_IMETHODIMP
240 nsDirEnumeratorUnix::Close()
242 if (mDir) {
243 closedir(mDir);
244 mDir = nsnull;
246 return NS_OK;
249 nsLocalFile::nsLocalFile() :
250 mHaveCachedStat(PR_FALSE)
254 nsLocalFile::nsLocalFile(const nsLocalFile& other)
255 : mPath(other.mPath)
256 , mHaveCachedStat(PR_FALSE)
260 NS_IMPL_THREADSAFE_ISUPPORTS3(nsLocalFile,
261 nsIFile,
262 nsILocalFile,
263 nsIHashable)
265 nsresult
266 nsLocalFile::nsLocalFileConstructor(nsISupports *outer,
267 const nsIID &aIID,
268 void **aInstancePtr)
270 NS_ENSURE_ARG_POINTER(aInstancePtr);
271 NS_ENSURE_NO_AGGREGATION(outer);
273 *aInstancePtr = nsnull;
275 nsCOMPtr<nsIFile> inst = new nsLocalFile();
276 if (!inst)
277 return NS_ERROR_OUT_OF_MEMORY;
278 return inst->QueryInterface(aIID, aInstancePtr);
281 nsresult
282 nsLocalFile::FillStatCache() {
283 #ifdef HAVE_STAT64
284 if (stat64(mPath.get(), &mCachedStat) == -1) {
285 // try lstat it may be a symlink
286 if (lstat64(mPath.get(), &mCachedStat) == -1) {
287 return NSRESULT_FOR_ERRNO();
290 #else
291 if (stat(mPath.get(), &mCachedStat) == -1) {
292 // try lstat it may be a symlink
293 if (lstat(mPath.get(), &mCachedStat) == -1) {
294 return NSRESULT_FOR_ERRNO();
297 #endif
298 mHaveCachedStat = PR_TRUE;
299 return NS_OK;
302 NS_IMETHODIMP
303 nsLocalFile::Clone(nsIFile **file)
305 // Just copy-construct ourselves
306 *file = new nsLocalFile(*this);
307 if (!*file)
308 return NS_ERROR_OUT_OF_MEMORY;
310 NS_ADDREF(*file);
312 return NS_OK;
315 NS_IMETHODIMP
316 nsLocalFile::InitWithNativePath(const nsACString &filePath)
318 if (Substring(filePath, 0, 2).EqualsLiteral("~/")) {
319 nsCOMPtr<nsIFile> homeDir;
320 nsCAutoString homePath;
321 if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_HOME_DIR,
322 getter_AddRefs(homeDir))) ||
323 NS_FAILED(homeDir->GetNativePath(homePath))) {
324 return NS_ERROR_FAILURE;
327 mPath = homePath + Substring(filePath, 1, filePath.Length() - 1);
328 } else {
329 if (filePath.IsEmpty() || filePath.First() != '/')
330 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
331 mPath = filePath;
334 // trim off trailing slashes
335 ssize_t len = mPath.Length();
336 while ((len > 1) && (mPath[len - 1] == '/'))
337 --len;
338 mPath.SetLength(len);
340 InvalidateCache();
341 return NS_OK;
344 NS_IMETHODIMP
345 nsLocalFile::CreateAllAncestors(PRUint32 permissions)
347 // <jband> I promise to play nice
348 char *buffer = mPath.BeginWriting(),
349 *slashp = buffer;
351 #ifdef DEBUG_NSIFILE
352 fprintf(stderr, "nsIFile: before: %s\n", buffer);
353 #endif
355 while ((slashp = strchr(slashp + 1, '/'))) {
357 * Sequences of '/' are equivalent to a single '/'.
359 if (slashp[1] == '/')
360 continue;
363 * If the path has a trailing slash, don't make the last component,
364 * because we'll get EEXIST in Create when we try to build the final
365 * component again, and it's easier to condition the logic here than
366 * there.
368 if (slashp[1] == '\0')
369 break;
371 /* Temporarily NUL-terminate here */
372 *slashp = '\0';
373 #ifdef DEBUG_NSIFILE
374 fprintf(stderr, "nsIFile: mkdir(\"%s\")\n", buffer);
375 #endif
376 int mkdir_result = mkdir(buffer, permissions);
377 int mkdir_errno = errno;
378 if (mkdir_result == -1) {
380 * Always set |errno| to EEXIST if the dir already exists
381 * (we have to do this here since the errno value is not consistent
382 * in all cases - various reasons like different platform,
383 * automounter-controlled dir, etc. can affect it (see bug 125489
384 * for details)).
386 if (access(buffer, F_OK) == 0) {
387 mkdir_errno = EEXIST;
391 /* Put the / back before we (maybe) return */
392 *slashp = '/';
395 * We could get EEXIST for an existing file -- not directory --
396 * with the name of one of our ancestors, but that's OK: we'll get
397 * ENOTDIR when we try to make the next component in the path,
398 * either here on back in Create, and error out appropriately.
400 if (mkdir_result == -1 && mkdir_errno != EEXIST)
401 return nsresultForErrno(mkdir_errno);
404 #ifdef DEBUG_NSIFILE
405 fprintf(stderr, "nsIFile: after: %s\n", buffer);
406 #endif
408 return NS_OK;
411 NS_IMETHODIMP
412 nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
414 *_retval = PR_Open(mPath.get(), flags, mode);
415 if (! *_retval)
416 return NS_ErrorAccordingToNSPR();
418 return NS_OK;
421 NS_IMETHODIMP
422 nsLocalFile::OpenANSIFileDesc(const char *mode, FILE **_retval)
424 *_retval = fopen(mPath.get(), mode);
425 if (! *_retval)
426 return NS_ERROR_FAILURE;
428 return NS_OK;
431 static int
432 do_create(const char *path, PRIntn flags, mode_t mode, PRFileDesc **_retval)
434 *_retval = PR_Open(path, flags, mode);
435 return *_retval ? 0 : -1;
438 static int
439 do_mkdir(const char *path, PRIntn flags, mode_t mode, PRFileDesc **_retval)
441 *_retval = nsnull;
442 return mkdir(path, mode);
445 nsresult
446 nsLocalFile::CreateAndKeepOpen(PRUint32 type, PRIntn flags,
447 PRUint32 permissions, PRFileDesc **_retval)
449 if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
450 return NS_ERROR_FILE_UNKNOWN_TYPE;
452 int result;
453 int (*createFunc)(const char *, PRIntn, mode_t, PRFileDesc **) =
454 (type == NORMAL_FILE_TYPE) ? do_create : do_mkdir;
456 result = createFunc(mPath.get(), flags, permissions, _retval);
457 if (result == -1 && errno == ENOENT) {
459 * If we failed because of missing ancestor components, try to create
460 * them and then retry the original creation.
462 * Ancestor directories get the same permissions as the file we're
463 * creating, with the X bit set for each of (user,group,other) with
464 * an R bit in the original permissions. If you want to do anything
465 * fancy like setgid or sticky bits, do it by hand.
467 int dirperm = permissions;
468 if (permissions & S_IRUSR)
469 dirperm |= S_IXUSR;
470 if (permissions & S_IRGRP)
471 dirperm |= S_IXGRP;
472 if (permissions & S_IROTH)
473 dirperm |= S_IXOTH;
475 #ifdef DEBUG_NSIFILE
476 fprintf(stderr, "nsIFile: perm = %o, dirperm = %o\n", permissions,
477 dirperm);
478 #endif
480 if (NS_FAILED(CreateAllAncestors(dirperm)))
481 return NS_ERROR_FAILURE;
483 #ifdef DEBUG_NSIFILE
484 fprintf(stderr, "nsIFile: Create(\"%s\") again\n", mPath.get());
485 #endif
486 result = createFunc(mPath.get(), flags, permissions, _retval);
488 return NSRESULT_FOR_RETURN(result);
491 NS_IMETHODIMP
492 nsLocalFile::Create(PRUint32 type, PRUint32 permissions)
494 PRFileDesc *junk = nsnull;
495 nsresult rv = CreateAndKeepOpen(type,
496 PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE |
497 PR_EXCL,
498 permissions,
499 &junk);
500 if (junk)
501 PR_Close(junk);
502 return rv;
505 NS_IMETHODIMP
506 nsLocalFile::AppendNative(const nsACString &fragment)
508 if (fragment.IsEmpty())
509 return NS_OK;
511 // only one component of path can be appended
512 nsACString::const_iterator begin, end;
513 if (FindCharInReadable('/', fragment.BeginReading(begin),
514 fragment.EndReading(end)))
515 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
517 return AppendRelativeNativePath(fragment);
520 NS_IMETHODIMP
521 nsLocalFile::AppendRelativeNativePath(const nsACString &fragment)
523 if (fragment.IsEmpty())
524 return NS_OK;
526 // No leading '/'
527 if (fragment.First() == '/')
528 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
530 if (mPath.EqualsLiteral("/"))
531 mPath.Append(fragment);
532 else
533 mPath.Append(NS_LITERAL_CSTRING("/") + fragment);
535 InvalidateCache();
536 return NS_OK;
539 NS_IMETHODIMP
540 nsLocalFile::Normalize()
542 char resolved_path[PATH_MAX] = "";
543 char *resolved_path_ptr = nsnull;
545 #ifdef XP_BEOS
546 BEntry be_e(mPath.get(), true);
547 BPath be_p;
548 status_t err;
549 if ((err = be_e.GetPath(&be_p)) == B_OK) {
550 resolved_path_ptr = (char *)be_p.Path();
551 PL_strncpyz(resolved_path, resolved_path_ptr, PATH_MAX - 1);
553 #else
554 resolved_path_ptr = realpath(mPath.get(), resolved_path);
555 #endif
556 // if there is an error, the return is null.
557 if (!resolved_path_ptr)
558 return NSRESULT_FOR_ERRNO();
560 mPath = resolved_path;
561 return NS_OK;
564 void
565 nsLocalFile::LocateNativeLeafName(nsACString::const_iterator &begin,
566 nsACString::const_iterator &end)
568 // XXX perhaps we should cache this??
570 mPath.BeginReading(begin);
571 mPath.EndReading(end);
573 nsACString::const_iterator it = end;
574 nsACString::const_iterator stop = begin;
575 --stop;
576 while (--it != stop) {
577 if (*it == '/') {
578 begin = ++it;
579 return;
582 // else, the entire path is the leaf name (which means this
583 // isn't an absolute path... unexpected??)
586 NS_IMETHODIMP
587 nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
589 nsACString::const_iterator begin, end;
590 LocateNativeLeafName(begin, end);
591 aLeafName = Substring(begin, end);
592 return NS_OK;
595 NS_IMETHODIMP
596 nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
598 nsACString::const_iterator begin, end;
599 LocateNativeLeafName(begin, end);
600 mPath.Replace(begin.get() - mPath.get(), Distance(begin, end), aLeafName);
601 InvalidateCache();
602 return NS_OK;
605 NS_IMETHODIMP
606 nsLocalFile::GetNativePath(nsACString &_retval)
608 _retval = mPath;
609 return NS_OK;
612 nsresult
613 nsLocalFile::GetNativeTargetPathName(nsIFile *newParent,
614 const nsACString &newName,
615 nsACString &_retval)
617 nsresult rv;
618 nsCOMPtr<nsIFile> oldParent;
620 if (!newParent) {
621 if (NS_FAILED(rv = GetParent(getter_AddRefs(oldParent))))
622 return rv;
623 newParent = oldParent.get();
624 } else {
625 // check to see if our target directory exists
626 PRBool targetExists;
627 if (NS_FAILED(rv = newParent->Exists(&targetExists)))
628 return rv;
630 if (!targetExists) {
631 // XXX create the new directory with some permissions
632 rv = newParent->Create(DIRECTORY_TYPE, 0755);
633 if (NS_FAILED(rv))
634 return rv;
635 } else {
636 // make sure that the target is actually a directory
637 PRBool targetIsDirectory;
638 if (NS_FAILED(rv = newParent->IsDirectory(&targetIsDirectory)))
639 return rv;
640 if (!targetIsDirectory)
641 return NS_ERROR_FILE_DESTINATION_NOT_DIR;
645 nsACString::const_iterator nameBegin, nameEnd;
646 if (!newName.IsEmpty()) {
647 newName.BeginReading(nameBegin);
648 newName.EndReading(nameEnd);
650 else
651 LocateNativeLeafName(nameBegin, nameEnd);
653 nsCAutoString dirName;
654 if (NS_FAILED(rv = newParent->GetNativePath(dirName)))
655 return rv;
657 _retval = dirName
658 + NS_LITERAL_CSTRING("/")
659 + Substring(nameBegin, nameEnd);
660 return NS_OK;
663 nsresult
664 nsLocalFile::CopyDirectoryTo(nsIFile *newParent)
666 nsresult rv;
668 * dirCheck is used for various boolean test results such as from Equals,
669 * Exists, isDir, etc.
671 PRBool dirCheck, isSymlink;
672 PRUint32 oldPerms;
674 if (NS_FAILED(rv = IsDirectory(&dirCheck)))
675 return rv;
676 if (!dirCheck)
677 return CopyToNative(newParent, EmptyCString());
679 if (NS_FAILED(rv = Equals(newParent, &dirCheck)))
680 return rv;
681 if (dirCheck) {
682 // can't copy dir to itself
683 return NS_ERROR_INVALID_ARG;
686 if (NS_FAILED(rv = newParent->Exists(&dirCheck)))
687 return rv;
688 // get the dirs old permissions
689 if (NS_FAILED(rv = GetPermissions(&oldPerms)))
690 return rv;
691 if (!dirCheck) {
692 if (NS_FAILED(rv = newParent->Create(DIRECTORY_TYPE, oldPerms)))
693 return rv;
694 } else { // dir exists lets try to use leaf
695 nsCAutoString leafName;
696 if (NS_FAILED(rv = GetNativeLeafName(leafName)))
697 return rv;
698 if (NS_FAILED(rv = newParent->AppendNative(leafName)))
699 return rv;
700 if (NS_FAILED(rv = newParent->Exists(&dirCheck)))
701 return rv;
702 if (dirCheck)
703 return NS_ERROR_FILE_ALREADY_EXISTS; // dest exists
704 if (NS_FAILED(rv = newParent->Create(DIRECTORY_TYPE, oldPerms)))
705 return rv;
708 nsCOMPtr<nsISimpleEnumerator> dirIterator;
709 if (NS_FAILED(rv = GetDirectoryEntries(getter_AddRefs(dirIterator))))
710 return rv;
712 PRBool hasMore = PR_FALSE;
713 while (dirIterator->HasMoreElements(&hasMore), hasMore) {
714 nsCOMPtr<nsIFile> entry;
715 rv = dirIterator->GetNext((nsISupports**)getter_AddRefs(entry));
716 if (NS_FAILED(rv))
717 continue;
718 if (NS_FAILED(rv = entry->IsSymlink(&isSymlink)))
719 return rv;
720 if (NS_FAILED(rv = entry->IsDirectory(&dirCheck)))
721 return rv;
722 if (dirCheck && !isSymlink) {
723 nsCOMPtr<nsIFile> destClone;
724 rv = newParent->Clone(getter_AddRefs(destClone));
725 if (NS_SUCCEEDED(rv)) {
726 nsCOMPtr<nsILocalFile> newDir(do_QueryInterface(destClone));
727 if (NS_FAILED(rv = entry->CopyToNative(newDir, EmptyCString()))) {
728 #ifdef DEBUG
729 nsresult rv2;
730 nsCAutoString pathName;
731 if (NS_FAILED(rv2 = entry->GetNativePath(pathName)))
732 return rv2;
733 printf("Operation not supported: %s\n", pathName.get());
734 #endif
735 if (rv == NS_ERROR_OUT_OF_MEMORY)
736 return rv;
737 continue;
740 } else {
741 if (NS_FAILED(rv = entry->CopyToNative(newParent, EmptyCString()))) {
742 #ifdef DEBUG
743 nsresult rv2;
744 nsCAutoString pathName;
745 if (NS_FAILED(rv2 = entry->GetNativePath(pathName)))
746 return rv2;
747 printf("Operation not supported: %s\n", pathName.get());
748 #endif
749 if (rv == NS_ERROR_OUT_OF_MEMORY)
750 return rv;
751 continue;
755 return NS_OK;
758 NS_IMETHODIMP
759 nsLocalFile::CopyToNative(nsIFile *newParent, const nsACString &newName)
761 nsresult rv;
762 // check to make sure that this has been initialized properly
763 CHECK_mPath();
765 // we copy the parent here so 'newParent' remains immutable
766 nsCOMPtr <nsIFile> workParent;
767 if (newParent) {
768 if (NS_FAILED(rv = newParent->Clone(getter_AddRefs(workParent))))
769 return rv;
770 } else {
771 if (NS_FAILED(rv = GetParent(getter_AddRefs(workParent))))
772 return rv;
775 // check to see if we are a directory or if we are a file
776 PRBool isDirectory;
777 if (NS_FAILED(rv = IsDirectory(&isDirectory)))
778 return rv;
780 nsCAutoString newPathName;
781 if (isDirectory) {
782 if (!newName.IsEmpty()) {
783 if (NS_FAILED(rv = workParent->AppendNative(newName)))
784 return rv;
785 } else {
786 if (NS_FAILED(rv = GetNativeLeafName(newPathName)))
787 return rv;
788 if (NS_FAILED(rv = workParent->AppendNative(newPathName)))
789 return rv;
791 if (NS_FAILED(rv = CopyDirectoryTo(workParent)))
792 return rv;
793 } else {
794 rv = GetNativeTargetPathName(workParent, newName, newPathName);
795 if (NS_FAILED(rv))
796 return rv;
798 #ifdef DEBUG_blizzard
799 printf("nsLocalFile::CopyTo() %s -> %s\n", mPath.get(), newPathName.get());
800 #endif
802 // actually create the file.
803 nsLocalFile *newFile = new nsLocalFile();
804 if (!newFile)
805 return NS_ERROR_OUT_OF_MEMORY;
807 nsCOMPtr<nsILocalFile> fileRef(newFile); // release on exit
809 rv = newFile->InitWithNativePath(newPathName);
810 if (NS_FAILED(rv))
811 return rv;
813 // get the old permissions
814 PRUint32 myPerms;
815 GetPermissions(&myPerms);
817 // Create the new file with the old file's permissions, even if write
818 // permission is missing. We can't create with write permission and
819 // then change back to myPerm on all filesystems (FAT on Linux, e.g.).
820 // But we can write to a read-only file on all Unix filesystems if we
821 // open it successfully for writing.
823 PRFileDesc *newFD;
824 rv = newFile->CreateAndKeepOpen(NORMAL_FILE_TYPE,
825 PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE,
826 myPerms,
827 &newFD);
828 if (NS_FAILED(rv))
829 return rv;
831 // open the old file, too
832 PRBool specialFile;
833 if (NS_FAILED(rv = IsSpecial(&specialFile))) {
834 PR_Close(newFD);
835 return rv;
837 if (specialFile) {
838 #ifdef DEBUG
839 printf("Operation not supported: %s\n", mPath.get());
840 #endif
841 // make sure to clean up properly
842 PR_Close(newFD);
843 return NS_OK;
846 PRFileDesc *oldFD;
847 rv = OpenNSPRFileDesc(PR_RDONLY, myPerms, &oldFD);
848 if (NS_FAILED(rv)) {
849 // make sure to clean up properly
850 PR_Close(newFD);
851 return rv;
854 #ifdef DEBUG_blizzard
855 PRInt32 totalRead = 0;
856 PRInt32 totalWritten = 0;
857 #endif
858 char buf[BUFSIZ];
859 PRInt32 bytesRead;
861 while ((bytesRead = PR_Read(oldFD, buf, BUFSIZ)) > 0) {
862 #ifdef DEBUG_blizzard
863 totalRead += bytesRead;
864 #endif
866 // PR_Write promises never to do a short write
867 PRInt32 bytesWritten = PR_Write(newFD, buf, bytesRead);
868 if (bytesWritten < 0) {
869 bytesRead = -1;
870 break;
872 NS_ASSERTION(bytesWritten == bytesRead, "short PR_Write?");
874 #ifdef DEBUG_blizzard
875 totalWritten += bytesWritten;
876 #endif
879 #ifdef DEBUG_blizzard
880 printf("read %d bytes, wrote %d bytes\n",
881 totalRead, totalWritten);
882 #endif
884 // close the files
885 PR_Close(newFD);
886 PR_Close(oldFD);
888 // check for read (or write) error after cleaning up
889 if (bytesRead < 0)
890 return NS_ERROR_OUT_OF_MEMORY;
892 return rv;
895 NS_IMETHODIMP
896 nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParent, const nsACString &newName)
898 return CopyToNative(newParent, newName);
901 NS_IMETHODIMP
902 nsLocalFile::MoveToNative(nsIFile *newParent, const nsACString &newName)
904 nsresult rv;
906 // check to make sure that this has been initialized properly
907 CHECK_mPath();
909 // check to make sure that we have a new parent
910 nsCAutoString newPathName;
911 rv = GetNativeTargetPathName(newParent, newName, newPathName);
912 if (NS_FAILED(rv))
913 return rv;
915 // try for atomic rename, falling back to copy/delete
916 if (rename(mPath.get(), newPathName.get()) < 0) {
917 #ifdef VMS
918 if (errno == EXDEV || errno == ENXIO) {
919 #else
920 if (errno == EXDEV) {
921 #endif
922 rv = CopyToNative(newParent, newName);
923 if (NS_SUCCEEDED(rv))
924 rv = Remove(PR_TRUE);
925 } else {
926 rv = NSRESULT_FOR_ERRNO();
929 return rv;
932 NS_IMETHODIMP
933 nsLocalFile::Remove(PRBool recursive)
935 CHECK_mPath();
937 VALIDATE_STAT_CACHE();
938 PRBool isSymLink, isDir;
940 nsresult rv = IsSymlink(&isSymLink);
941 if (NS_FAILED(rv))
942 return rv;
944 if (!recursive && isSymLink)
945 return NSRESULT_FOR_RETURN(unlink(mPath.get()));
947 isDir = S_ISDIR(mCachedStat.st_mode);
948 InvalidateCache();
949 if (isDir) {
950 if (recursive) {
951 nsDirEnumeratorUnix *dir = new nsDirEnumeratorUnix();
952 if (!dir)
953 return NS_ERROR_OUT_OF_MEMORY;
955 nsCOMPtr<nsISimpleEnumerator> dirRef(dir); // release on exit
957 rv = dir->Init(this, PR_FALSE);
958 if (NS_FAILED(rv))
959 return rv;
961 PRBool more;
962 while (dir->HasMoreElements(&more), more) {
963 nsCOMPtr<nsISupports> item;
964 rv = dir->GetNext(getter_AddRefs(item));
965 if (NS_FAILED(rv))
966 return NS_ERROR_FAILURE;
968 nsCOMPtr<nsIFile> file = do_QueryInterface(item, &rv);
969 if (NS_FAILED(rv))
970 return NS_ERROR_FAILURE;
971 if (NS_FAILED(rv = file->Remove(recursive)))
972 return rv;
976 if (rmdir(mPath.get()) == -1)
977 return NSRESULT_FOR_ERRNO();
978 } else {
979 if (unlink(mPath.get()) == -1)
980 return NSRESULT_FOR_ERRNO();
983 return NS_OK;
986 NS_IMETHODIMP
987 nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModTime)
989 CHECK_mPath();
990 NS_ENSURE_ARG(aLastModTime);
992 PRFileInfo64 info;
993 if (PR_GetFileInfo64(mPath.get(), &info) != PR_SUCCESS)
994 return NSRESULT_FOR_ERRNO();
996 // PRTime is a 64 bit value
997 // microseconds -> milliseconds
998 PRInt64 usecPerMsec;
999 LL_I2L(usecPerMsec, PR_USEC_PER_MSEC);
1000 LL_DIV(*aLastModTime, info.modifyTime, usecPerMsec);
1001 return NS_OK;
1004 NS_IMETHODIMP
1005 nsLocalFile::SetLastModifiedTime(PRInt64 aLastModTime)
1007 CHECK_mPath();
1009 int result;
1010 if (! LL_IS_ZERO(aLastModTime)) {
1011 VALIDATE_STAT_CACHE();
1012 struct utimbuf ut;
1013 ut.actime = mCachedStat.st_atime;
1015 // convert milliseconds to seconds since the unix epoch
1016 double dTime;
1017 LL_L2D(dTime, aLastModTime);
1018 ut.modtime = (time_t) (dTime / PR_MSEC_PER_SEC);
1019 result = utime(mPath.get(), &ut);
1020 } else {
1021 result = utime(mPath.get(), nsnull);
1023 InvalidateCache();
1024 return NSRESULT_FOR_RETURN(result);
1027 NS_IMETHODIMP
1028 nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModTimeOfLink)
1030 CHECK_mPath();
1031 NS_ENSURE_ARG(aLastModTimeOfLink);
1033 struct stat sbuf;
1034 if (lstat(mPath.get(), &sbuf) == -1)
1035 return NSRESULT_FOR_ERRNO();
1036 LL_I2L(*aLastModTimeOfLink, (PRInt32)sbuf.st_mtime);
1038 // lstat returns st_mtime in seconds
1039 PRInt64 msecPerSec;
1040 LL_I2L(msecPerSec, PR_MSEC_PER_SEC);
1041 LL_MUL(*aLastModTimeOfLink, *aLastModTimeOfLink, msecPerSec);
1043 return NS_OK;
1047 * utime(2) may or may not dereference symlinks, joy.
1049 NS_IMETHODIMP
1050 nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModTimeOfLink)
1052 return SetLastModifiedTime(aLastModTimeOfLink);
1056 * Only send back permissions bits: maybe we want to send back the whole
1057 * mode_t to permit checks against other file types?
1060 #define NORMALIZE_PERMS(mode) ((mode)& (S_IRWXU | S_IRWXG | S_IRWXO))
1062 NS_IMETHODIMP
1063 nsLocalFile::GetPermissions(PRUint32 *aPermissions)
1065 NS_ENSURE_ARG(aPermissions);
1066 VALIDATE_STAT_CACHE();
1067 *aPermissions = NORMALIZE_PERMS(mCachedStat.st_mode);
1068 return NS_OK;
1071 NS_IMETHODIMP
1072 nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink)
1074 CHECK_mPath();
1075 NS_ENSURE_ARG(aPermissionsOfLink);
1077 struct stat sbuf;
1078 if (lstat(mPath.get(), &sbuf) == -1)
1079 return NSRESULT_FOR_ERRNO();
1080 *aPermissionsOfLink = NORMALIZE_PERMS(sbuf.st_mode);
1081 return NS_OK;
1084 NS_IMETHODIMP
1085 nsLocalFile::SetPermissions(PRUint32 aPermissions)
1087 CHECK_mPath();
1089 InvalidateCache();
1092 * Race condition here: we should use fchmod instead, there's no way to
1093 * guarantee the name still refers to the same file.
1095 if (chmod(mPath.get(), aPermissions) < 0)
1096 return NSRESULT_FOR_ERRNO();
1097 return NS_OK;
1100 NS_IMETHODIMP
1101 nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions)
1103 return SetPermissions(aPermissions);
1106 NS_IMETHODIMP
1107 nsLocalFile::GetFileSize(PRInt64 *aFileSize)
1109 NS_ENSURE_ARG_POINTER(aFileSize);
1110 *aFileSize = LL_ZERO;
1111 VALIDATE_STAT_CACHE();
1113 #if defined(VMS)
1114 /* Only two record formats can report correct file content size */
1115 if ((mCachedStat.st_fab_rfm != FAB$C_STMLF) &&
1116 (mCachedStat.st_fab_rfm != FAB$C_STMCR)) {
1117 return NS_ERROR_FAILURE;
1119 #endif
1121 if (!S_ISDIR(mCachedStat.st_mode)) {
1122 #ifdef HAVE_STAT64
1123 *aFileSize = mCachedStat.st_size;
1124 #else
1125 LL_UI2L(*aFileSize, (PRUint32)mCachedStat.st_size);
1126 #endif
1128 return NS_OK;
1131 NS_IMETHODIMP
1132 nsLocalFile::SetFileSize(PRInt64 aFileSize)
1134 CHECK_mPath();
1136 PRInt32 size;
1137 LL_L2I(size, aFileSize);
1138 /* XXX truncate64? */
1139 InvalidateCache();
1140 if (truncate(mPath.get(), (off_t)size) == -1)
1141 return NSRESULT_FOR_ERRNO();
1142 return NS_OK;
1145 NS_IMETHODIMP
1146 nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSize)
1148 CHECK_mPath();
1149 NS_ENSURE_ARG(aFileSize);
1151 #ifdef HAVE_LSTAT64
1152 struct stat64 sbuf;
1153 if (lstat64(mPath.get(), &sbuf) == -1)
1154 return NSRESULT_FOR_ERRNO();
1155 *aFileSize = sbuf.st_size;
1156 #else
1157 struct stat sbuf;
1158 if (lstat(mPath.get(), &sbuf) == -1)
1159 return NSRESULT_FOR_ERRNO();
1160 LL_UI2L(*aFileSize, (PRUint32)sbuf.st_size);
1161 #endif
1162 return NS_OK;
1165 NS_IMETHODIMP
1166 nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
1168 NS_ENSURE_ARG_POINTER(aDiskSpaceAvailable);
1170 // These systems have the operations necessary to check disk space.
1172 #if defined(HAVE_SYS_STATFS_H) || defined(HAVE_SYS_STATVFS_H)
1174 // check to make sure that mPath is properly initialized
1175 CHECK_mPath();
1177 struct STATFS fs_buf;
1180 * Members of the STATFS struct that you should know about:
1181 * f_bsize = block size on disk.
1182 * f_bavail = number of free blocks available to a non-superuser.
1183 * f_bfree = number of total free blocks in file system.
1186 if (STATFS(mPath.get(), &fs_buf) < 0) {
1187 // The call to STATFS failed.
1188 #ifdef DEBUG
1189 printf("ERROR: GetDiskSpaceAvailable: STATFS call FAILED. \n");
1190 #endif
1191 return NS_ERROR_FAILURE;
1193 #ifdef DEBUG_DISK_SPACE
1194 printf("DiskSpaceAvailable: %d bytes\n",
1195 fs_buf.f_bsize * (fs_buf.f_bavail - 1));
1196 #endif
1199 * The number of bytes free == The number of free blocks available to
1200 * a non-superuser, minus one as a fudge factor, multiplied by the size
1201 * of the aforementioned blocks.
1203 PRInt64 bsize, bavail;
1205 LL_I2L(bsize, fs_buf.f_bsize);
1206 LL_I2L(bavail, fs_buf.f_bavail - 1);
1207 LL_MUL(*aDiskSpaceAvailable, bsize, bavail);
1208 return NS_OK;
1210 #else
1212 * This platform doesn't have statfs or statvfs. I'm sure that there's
1213 * a way to check for free disk space on platforms that don't have statfs
1214 * (I'm SURE they have df, for example).
1216 * Until we figure out how to do that, lets be honest and say that this
1217 * command isn't implemented properly for these platforms yet.
1219 #ifdef DEBUG
1220 printf("ERROR: GetDiskSpaceAvailable: Not implemented for plaforms without statfs.\n");
1221 #endif
1222 return NS_ERROR_NOT_IMPLEMENTED;
1224 #endif /* HAVE_SYS_STATFS_H or HAVE_SYS_STATVFS_H */
1228 NS_IMETHODIMP
1229 nsLocalFile::GetParent(nsIFile **aParent)
1231 CHECK_mPath();
1232 NS_ENSURE_ARG_POINTER(aParent);
1233 *aParent = nsnull;
1235 // if '/' we are at the top of the volume, return null
1236 if (mPath.Equals("/"))
1237 return NS_OK;
1239 // <brendan, after jband> I promise to play nice
1240 char *buffer = mPath.BeginWriting(),
1241 *slashp = buffer;
1243 // find the last significant slash in buffer
1244 slashp = strrchr(buffer, '/');
1245 NS_ASSERTION(slashp, "non-canonical mPath?");
1246 if (!slashp)
1247 return NS_ERROR_FILE_INVALID_PATH;
1249 // for the case where we are at '/'
1250 if (slashp == buffer)
1251 slashp++;
1253 // temporarily terminate buffer at the last significant slash
1254 char c = *slashp;
1255 *slashp = '\0';
1257 nsCOMPtr<nsILocalFile> localFile;
1258 nsresult rv = NS_NewNativeLocalFile(nsDependentCString(buffer), PR_TRUE,
1259 getter_AddRefs(localFile));
1261 // make buffer whole again
1262 *slashp = c;
1264 if (NS_SUCCEEDED(rv) && localFile)
1265 rv = CallQueryInterface(localFile, aParent);
1266 return rv;
1270 * The results of Exists, isWritable and isReadable are not cached.
1274 #if defined(XP_BEOS) || defined(SOLARIS)
1275 // access() is buggy in BeOS POSIX implementation, at least for BFS, using stat() instead
1276 // see bug 169506, https://bugzilla.mozilla.org/show_bug.cgi?id=169506
1277 // access() problem also exists in Solaris POSIX implementation
1278 // see bug 351595, https://bugzilla.mozilla.org/show_bug.cgi?id=351595
1279 NS_IMETHODIMP
1280 nsLocalFile::Exists(PRBool *_retval)
1282 CHECK_mPath();
1283 NS_ENSURE_ARG_POINTER(_retval);
1284 struct stat buf;
1286 *_retval = (stat(mPath.get(), &buf) == 0);
1287 return NS_OK;
1290 NS_IMETHODIMP
1291 nsLocalFile::IsWritable(PRBool *_retval)
1293 CHECK_mPath();
1294 NS_ENSURE_ARG_POINTER(_retval);
1295 struct stat buf;
1297 *_retval = (stat(mPath.get(), &buf) == 0);
1298 if (*_retval || errno == EACCES) {
1299 *_retval = *_retval && (buf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH ));
1300 return NS_OK;
1302 return NSRESULT_FOR_ERRNO();
1305 NS_IMETHODIMP
1306 nsLocalFile::IsReadable(PRBool *_retval)
1308 CHECK_mPath();
1309 NS_ENSURE_ARG_POINTER(_retval);
1310 struct stat buf;
1312 *_retval = (stat(mPath.get(), &buf) == 0);
1313 if (*_retval || errno == EACCES) {
1314 *_retval = *_retval && (buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH ));
1315 return NS_OK;
1317 return NSRESULT_FOR_ERRNO();
1320 NS_IMETHODIMP
1321 nsLocalFile::IsExecutable(PRBool *_retval)
1323 CHECK_mPath();
1324 NS_ENSURE_ARG_POINTER(_retval);
1325 struct stat buf;
1327 *_retval = (stat(mPath.get(), &buf) == 0);
1328 if (*_retval || errno == EACCES) {
1329 *_retval = *_retval && (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH ));
1330 return NS_OK;
1332 return NSRESULT_FOR_ERRNO();
1334 #else
1336 NS_IMETHODIMP
1337 nsLocalFile::Exists(PRBool *_retval)
1339 CHECK_mPath();
1340 NS_ENSURE_ARG_POINTER(_retval);
1342 *_retval = (access(mPath.get(), F_OK) == 0);
1343 return NS_OK;
1347 NS_IMETHODIMP
1348 nsLocalFile::IsWritable(PRBool *_retval)
1350 CHECK_mPath();
1351 NS_ENSURE_ARG_POINTER(_retval);
1353 *_retval = (access(mPath.get(), W_OK) == 0);
1354 if (*_retval || errno == EACCES)
1355 return NS_OK;
1356 return NSRESULT_FOR_ERRNO();
1359 NS_IMETHODIMP
1360 nsLocalFile::IsReadable(PRBool *_retval)
1362 CHECK_mPath();
1363 NS_ENSURE_ARG_POINTER(_retval);
1365 *_retval = (access(mPath.get(), R_OK) == 0);
1366 if (*_retval || errno == EACCES)
1367 return NS_OK;
1368 return NSRESULT_FOR_ERRNO();
1371 NS_IMETHODIMP
1372 nsLocalFile::IsExecutable(PRBool *_retval)
1374 CHECK_mPath();
1375 NS_ENSURE_ARG_POINTER(_retval);
1377 *_retval = (access(mPath.get(), X_OK) == 0);
1378 if (*_retval || errno == EACCES)
1379 return NS_OK;
1380 return NSRESULT_FOR_ERRNO();
1382 #endif
1383 NS_IMETHODIMP
1384 nsLocalFile::IsDirectory(PRBool *_retval)
1386 NS_ENSURE_ARG_POINTER(_retval);
1387 *_retval = PR_FALSE;
1388 VALIDATE_STAT_CACHE();
1389 *_retval = S_ISDIR(mCachedStat.st_mode);
1390 return NS_OK;
1393 NS_IMETHODIMP
1394 nsLocalFile::IsFile(PRBool *_retval)
1396 NS_ENSURE_ARG_POINTER(_retval);
1397 *_retval = PR_FALSE;
1398 VALIDATE_STAT_CACHE();
1399 *_retval = S_ISREG(mCachedStat.st_mode);
1400 return NS_OK;
1403 NS_IMETHODIMP
1404 nsLocalFile::IsHidden(PRBool *_retval)
1406 NS_ENSURE_ARG_POINTER(_retval);
1407 nsACString::const_iterator begin, end;
1408 LocateNativeLeafName(begin, end);
1409 *_retval = (*begin == '.');
1410 return NS_OK;
1413 NS_IMETHODIMP
1414 nsLocalFile::IsSymlink(PRBool *_retval)
1416 NS_ENSURE_ARG_POINTER(_retval);
1417 CHECK_mPath();
1419 struct stat symStat;
1420 lstat(mPath.get(), &symStat);
1421 *_retval=S_ISLNK(symStat.st_mode);
1422 return NS_OK;
1425 NS_IMETHODIMP
1426 nsLocalFile::IsSpecial(PRBool *_retval)
1428 NS_ENSURE_ARG_POINTER(_retval);
1429 VALIDATE_STAT_CACHE();
1430 *_retval = S_ISCHR(mCachedStat.st_mode) ||
1431 S_ISBLK(mCachedStat.st_mode) ||
1432 #ifdef S_ISSOCK
1433 S_ISSOCK(mCachedStat.st_mode) ||
1434 #endif
1435 S_ISFIFO(mCachedStat.st_mode);
1437 return NS_OK;
1440 NS_IMETHODIMP
1441 nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
1443 NS_ENSURE_ARG(inFile);
1444 NS_ENSURE_ARG_POINTER(_retval);
1445 *_retval = PR_FALSE;
1447 nsresult rv;
1448 nsCAutoString inPath;
1450 if (NS_FAILED(rv = inFile->GetNativePath(inPath)))
1451 return rv;
1453 *_retval = !FILE_STRCMP(inPath.get(), mPath.get());
1454 return NS_OK;
1457 NS_IMETHODIMP
1458 nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval)
1460 CHECK_mPath();
1461 NS_ENSURE_ARG(inFile);
1462 NS_ENSURE_ARG_POINTER(_retval);
1464 nsCAutoString inPath;
1465 nsresult rv;
1467 if (NS_FAILED(rv = inFile->GetNativePath(inPath)))
1468 return rv;
1470 *_retval = PR_FALSE;
1472 ssize_t len = mPath.Length();
1473 if (FILE_STRNCMP(mPath.get(), inPath.get(), len) == 0) {
1474 // Now make sure that the |inFile|'s path has a separator at len,
1475 // which implies that it has more components after len.
1476 if (inPath[len] == '/')
1477 *_retval = PR_TRUE;
1480 return NS_OK;
1483 NS_IMETHODIMP
1484 nsLocalFile::GetNativeTarget(nsACString &_retval)
1486 CHECK_mPath();
1487 _retval.Truncate();
1489 struct stat symStat;
1490 lstat(mPath.get(), &symStat);
1491 if (!S_ISLNK(symStat.st_mode))
1492 return NS_ERROR_FILE_INVALID_PATH;
1494 PRInt64 targetSize64;
1495 if (NS_FAILED(GetFileSizeOfLink(&targetSize64)))
1496 return NS_ERROR_FAILURE;
1498 PRInt32 size;
1499 LL_L2I(size, targetSize64);
1500 char *target = (char *)nsMemory::Alloc(size + 1);
1501 if (!target)
1502 return NS_ERROR_OUT_OF_MEMORY;
1504 if (readlink(mPath.get(), target, (size_t)size) < 0) {
1505 nsMemory::Free(target);
1506 return NSRESULT_FOR_ERRNO();
1508 target[size] = '\0';
1510 nsresult rv;
1511 PRBool isSymlink;
1512 nsCOMPtr<nsIFile> self(this);
1513 nsCOMPtr<nsIFile> parent;
1514 while (NS_SUCCEEDED(rv = self->GetParent(getter_AddRefs(parent)))) {
1515 NS_ASSERTION(parent != nsnull, "no parent?!");
1517 if (target[0] != '/') {
1518 nsCOMPtr<nsILocalFile> localFile(do_QueryInterface(parent, &rv));
1519 if (NS_FAILED(rv))
1520 break;
1521 if (NS_FAILED(rv = localFile->AppendRelativeNativePath(nsDependentCString(target))))
1522 break;
1523 if (NS_FAILED(rv = localFile->GetNativePath(_retval)))
1524 break;
1525 if (NS_FAILED(rv = parent->IsSymlink(&isSymlink)))
1526 break;
1527 self = parent;
1528 } else {
1529 nsCOMPtr<nsILocalFile> localFile;
1530 rv = NS_NewNativeLocalFile(nsDependentCString(target), PR_TRUE,
1531 getter_AddRefs(localFile));
1532 if (NS_FAILED(rv))
1533 break;
1534 if (NS_FAILED(rv = localFile->IsSymlink(&isSymlink)))
1535 break;
1536 _retval = target; // XXX can we avoid this buffer copy?
1537 self = do_QueryInterface(localFile);
1539 if (NS_FAILED(rv) || !isSymlink)
1540 break;
1542 const nsPromiseFlatCString &flatRetval = PromiseFlatCString(_retval);
1544 // strip off any and all trailing '/'
1545 PRInt32 len = strlen(target);
1546 while (target[len-1] == '/' && len > 1)
1547 target[--len] = '\0';
1548 if (lstat(flatRetval.get(), &symStat) < 0) {
1549 rv = NSRESULT_FOR_ERRNO();
1550 break;
1552 if (!S_ISLNK(symStat.st_mode)) {
1553 rv = NS_ERROR_FILE_INVALID_PATH;
1554 break;
1556 size = symStat.st_size;
1557 if (readlink(flatRetval.get(), target, size) < 0) {
1558 rv = NSRESULT_FOR_ERRNO();
1559 break;
1561 target[size] = '\0';
1563 _retval.Truncate();
1566 nsMemory::Free(target);
1568 if (NS_FAILED(rv))
1569 _retval.Truncate();
1570 return rv;
1573 /* attribute PRBool followLinks; */
1574 NS_IMETHODIMP
1575 nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
1577 *aFollowLinks = PR_TRUE;
1578 return NS_OK;
1581 NS_IMETHODIMP
1582 nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
1584 return NS_OK;
1587 NS_IMETHODIMP
1588 nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator **entries)
1590 nsDirEnumeratorUnix *dir = new nsDirEnumeratorUnix();
1591 if (!dir)
1592 return NS_ERROR_OUT_OF_MEMORY;
1594 NS_ADDREF(dir);
1595 nsresult rv = dir->Init(this, PR_FALSE);
1596 if (NS_FAILED(rv)) {
1597 *entries = nsnull;
1598 NS_RELEASE(dir);
1599 } else {
1600 *entries = dir; // transfer reference
1603 return rv;
1606 NS_IMETHODIMP
1607 nsLocalFile::Load(PRLibrary **_retval)
1609 CHECK_mPath();
1610 NS_ENSURE_ARG_POINTER(_retval);
1612 NS_TIMELINE_START_TIMER("PR_LoadLibrary");
1614 #ifdef NS_BUILD_REFCNT_LOGGING
1615 nsTraceRefcntImpl::SetActivityIsLegal(PR_FALSE);
1616 #endif
1618 *_retval = PR_LoadLibrary(mPath.get());
1620 #ifdef NS_BUILD_REFCNT_LOGGING
1621 nsTraceRefcntImpl::SetActivityIsLegal(PR_TRUE);
1622 #endif
1624 NS_TIMELINE_STOP_TIMER("PR_LoadLibrary");
1625 NS_TIMELINE_MARK_TIMER1("PR_LoadLibrary", mPath.get());
1627 if (!*_retval)
1628 return NS_ERROR_FAILURE;
1629 return NS_OK;
1632 NS_IMETHODIMP
1633 nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
1635 return GetNativePath(aPersistentDescriptor);
1638 NS_IMETHODIMP
1639 nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
1641 return InitWithNativePath(aPersistentDescriptor);
1644 #ifdef XP_BEOS
1645 NS_IMETHODIMP
1646 nsLocalFile::Reveal()
1648 BPath bPath(mPath.get());
1649 PRBool isDirectory;
1650 if (NS_FAILED(IsDirectory(&isDirectory)))
1651 return NS_ERROR_FAILURE;
1653 if(!isDirectory)
1654 bPath.GetParent(&bPath);
1655 entry_ref ref;
1656 get_ref_for_path(bPath.Path(),&ref);
1657 BMessage message(B_REFS_RECEIVED);
1658 message.AddRef("refs",&ref);
1659 BMessenger messenger("application/x-vnd.Be-TRAK");
1660 messenger.SendMessage(&message);
1661 return NS_OK;
1664 NS_IMETHODIMP
1665 nsLocalFile::Launch()
1667 entry_ref ref;
1668 get_ref_for_path (mPath.get(), &ref);
1669 be_roster->Launch (&ref);
1671 return NS_OK;
1673 #else
1674 NS_IMETHODIMP
1675 nsLocalFile::Reveal()
1677 #ifdef MOZ_WIDGET_GTK2
1678 nsCOMPtr<nsIGnomeVFSService> vfs = do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID);
1679 if (!vfs)
1680 return NS_ERROR_FAILURE;
1682 PRBool isDirectory;
1683 if (NS_FAILED(IsDirectory(&isDirectory)))
1684 return NS_ERROR_FAILURE;
1686 if (isDirectory) {
1687 return vfs->ShowURIForInput(mPath);
1688 } else {
1689 nsCOMPtr<nsIFile> parentDir;
1690 nsCAutoString dirPath;
1691 if (NS_FAILED(GetParent(getter_AddRefs(parentDir))))
1692 return NS_ERROR_FAILURE;
1693 if (NS_FAILED(parentDir->GetNativePath(dirPath)))
1694 return NS_ERROR_FAILURE;
1696 return vfs->ShowURIForInput(dirPath);
1698 #else
1699 return NS_ERROR_FAILURE;
1700 #endif
1703 NS_IMETHODIMP
1704 nsLocalFile::Launch()
1706 #ifdef MOZ_WIDGET_GTK2
1707 nsCOMPtr<nsIGnomeVFSService> vfs = do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID);
1708 if (!vfs)
1709 return NS_ERROR_FAILURE;
1711 return vfs->ShowURIForInput(mPath);
1712 #else
1713 return NS_ERROR_FAILURE;
1714 #endif
1716 #endif
1718 nsresult
1719 NS_NewNativeLocalFile(const nsACString &path, PRBool followSymlinks, nsILocalFile **result)
1721 nsLocalFile *file = new nsLocalFile();
1722 if (!file)
1723 return NS_ERROR_OUT_OF_MEMORY;
1724 NS_ADDREF(file);
1726 if (!path.IsEmpty()) {
1727 nsresult rv = file->InitWithNativePath(path);
1728 if (NS_FAILED(rv)) {
1729 NS_RELEASE(file);
1730 return rv;
1733 *result = file;
1734 return NS_OK;
1737 //-----------------------------------------------------------------------------
1738 // unicode support
1739 //-----------------------------------------------------------------------------
1741 #define SET_UCS(func, ucsArg) \
1743 nsCAutoString buf; \
1744 nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
1745 if (NS_FAILED(rv)) \
1746 return rv; \
1747 return (func)(buf); \
1750 #define GET_UCS(func, ucsArg) \
1752 nsCAutoString buf; \
1753 nsresult rv = (func)(buf); \
1754 if (NS_FAILED(rv)) return rv; \
1755 return NS_CopyNativeToUnicode(buf, ucsArg); \
1758 #define SET_UCS_2ARGS_2(func, opaqueArg, ucsArg) \
1760 nsCAutoString buf; \
1761 nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
1762 if (NS_FAILED(rv)) \
1763 return rv; \
1764 return (func)(opaqueArg, buf); \
1767 // Unicode interface Wrapper
1768 nsresult
1769 nsLocalFile::InitWithPath(const nsAString &filePath)
1771 SET_UCS(InitWithNativePath, filePath);
1773 nsresult
1774 nsLocalFile::Append(const nsAString &node)
1776 SET_UCS(AppendNative, node);
1778 nsresult
1779 nsLocalFile::AppendRelativePath(const nsAString &node)
1781 SET_UCS(AppendRelativeNativePath, node);
1783 nsresult
1784 nsLocalFile::GetLeafName(nsAString &aLeafName)
1786 GET_UCS(GetNativeLeafName, aLeafName);
1788 nsresult
1789 nsLocalFile::SetLeafName(const nsAString &aLeafName)
1791 SET_UCS(SetNativeLeafName, aLeafName);
1793 nsresult
1794 nsLocalFile::GetPath(nsAString &_retval)
1796 return NS_CopyNativeToUnicode(mPath, _retval);
1798 nsresult
1799 nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
1801 SET_UCS_2ARGS_2(CopyToNative , newParentDir, newName);
1803 nsresult
1804 nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
1806 SET_UCS_2ARGS_2(CopyToFollowingLinksNative , newParentDir, newName);
1808 nsresult
1809 nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
1811 SET_UCS_2ARGS_2(MoveToNative, newParentDir, newName);
1813 nsresult
1814 nsLocalFile::GetTarget(nsAString &_retval)
1816 GET_UCS(GetNativeTarget, _retval);
1819 // nsIHashable
1821 NS_IMETHODIMP
1822 nsLocalFile::Equals(nsIHashable* aOther, PRBool *aResult)
1824 nsCOMPtr<nsIFile> otherFile(do_QueryInterface(aOther));
1825 if (!otherFile) {
1826 *aResult = PR_FALSE;
1827 return NS_OK;
1830 return Equals(otherFile, aResult);
1833 NS_IMETHODIMP
1834 nsLocalFile::GetHashCode(PRUint32 *aResult)
1836 *aResult = nsCRT::HashCode(mPath.get());
1837 return NS_OK;
1840 nsresult
1841 NS_NewLocalFile(const nsAString &path, PRBool followLinks, nsILocalFile* *result)
1843 nsCAutoString buf;
1844 nsresult rv = NS_CopyUnicodeToNative(path, buf);
1845 if (NS_FAILED(rv))
1846 return rv;
1847 return NS_NewNativeLocalFile(buf, followLinks, result);
1850 //-----------------------------------------------------------------------------
1851 // global init/shutdown
1852 //-----------------------------------------------------------------------------
1854 void
1855 nsLocalFile::GlobalInit()
1859 void
1860 nsLocalFile::GlobalShutdown()