VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/io/nsLocalFileUnix.cpp@ 4837

Last change on this file since 4837 was 1, checked in by vboxsync, 54 years ago

import

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

© 2023 Oracle
ContactPrivacy policyTerms of Use