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
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 // This file is included by nsFile.cp, and includes the Macintosh-specific
49 #include "MoreFilesExtras.h"
54 #include <TextUtils.h>
55 #include <Processes.h>
56 #include <limits.h> // ULONG_MAX
58 #include "nsFileSpec.h"
60 #include "nsXPIDLString.h"
63 const unsigned char* kAliasHavenFolderName
= "\pnsAliasHaven";
65 //========================================================================================
66 namespace MacFileHelpers
67 //========================================================================================
69 inline void PLstrcpy(Str255 dst
, ConstStr255Param src
)
71 memcpy(dst
, src
, 1 + src
[0]);
74 void PLstrcpy(Str255 dst
, const char* src
, int inMaxLen
=255);
75 void PLstrncpy(Str255 dst
, const char* src
, int inMaxLen
);
77 void SwapSlashColon(char * s
);
78 OSErr
FSSpecFromUnixPath(
79 const char * unixPath
,
83 Boolean allowPartial
= false,
84 Boolean createDirs
= false);
85 char* MacPathFromUnixPath(
89 char* inPath
, // NOT const - gets clobbered
92 OSErr
FSSpecFromPathname(
93 const char* inPathNamePtr
,
95 Boolean inCreateDirs
);
96 char* PathNameFromFSSpec(
97 const FSSpec
& inSpec
);
98 OSErr
CreateFolderInFolder(
99 short refNum
, // Parent directory/volume
101 ConstStr255Param folderName
, // Name of the new folder
102 short& outRefNum
, // Volume of the created folder
105 // Some routines to support an "alias haven" directory. Aliases in this directory
106 // are never resolved. There is a ResolveAlias here that respects that. This is
107 // to support attaching of aliases in mail.
108 void EnsureAliasHaven();
109 void SetNoResolve(Boolean inResolve
);
110 PRBool
IsAliasSafe(const FSSpec
& inSpec
);
111 OSErr
MakeAliasSafe(FSSpec
& inOutSpec
);
112 OSErr
ResolveAliasFile(FSSpec
& inOutSpec
, Boolean
& wasAliased
);
114 Boolean sNoResolve
= false;
115 long sAliasHavenDirID
= 0;
116 short sAliasHavenVRefNum
= 0;
117 } // namespace MacFileHelpers
119 //----------------------------------------------------------------------------------------
120 void MacFileHelpers::PLstrcpy(Str255 dst
, const char* src
, int inMax
)
121 //----------------------------------------------------------------------------------------
123 int srcLength
= strlen(src
);
124 NS_ASSERTION(srcLength
<= inMax
, "Oops, string is too long!");
125 if (srcLength
> inMax
)
128 memcpy(&dst
[1], src
, srcLength
);
131 //----------------------------------------------------------------------------------------
132 void MacFileHelpers::PLstrncpy(Str255 dst
, const char* src
, int inMax
)
133 //----------------------------------------------------------------------------------------
135 int srcLength
= strlen(src
);
136 if (srcLength
> inMax
)
139 memcpy(&dst
[1], src
, srcLength
);
142 //-----------------------------------
143 void MacFileHelpers::SwapSlashColon(char * s
)
144 //-----------------------------------
156 } // MacFileHelpers::SwapSlashColon
158 //-----------------------------------
159 char* MacFileHelpers::EncodeMacPath(
160 char* inPath
, // NOT const, gets clobbered
161 Boolean prependSlash
,
163 // Transforms Macintosh style path into Unix one
164 // Method: Swap ':' and '/', hex escape the result
165 //-----------------------------------
167 if (inPath
== nsnull
)
169 int pathSize
= strlen(inPath
);
171 // XP code sometimes chokes if there's a final slash in the unix path.
172 // Since correct mac paths to folders and volumes will end in ':', strip this
174 char* c
= inPath
+ pathSize
- 1;
181 char * newPath
= nsnull
;
182 char * finalPath
= nsnull
;
186 newPath
= new char[pathSize
+ 2];
187 newPath
[0] = ':'; // It will be converted to '/'
188 memcpy(&newPath
[1], inPath
, pathSize
+ 1);
192 newPath
= new char[pathSize
+ 1];
193 strcpy(newPath
, inPath
);
197 SwapSlashColon( newPath
);
200 finalPath
= nsEscape(newPath
, url_Path
);
208 } // MacFileHelpers::EncodeMacPath
210 //----------------------------------------------------------------------------------------
211 inline void MacFileHelpers::SetNoResolve(Boolean inResolve
)
212 //----------------------------------------------------------------------------------------
214 sNoResolve
= inResolve
;
215 } // MacFileHelpers::SetNoResolve
217 //----------------------------------------------------------------------------------------
218 OSErr
MacFileHelpers::MakeAliasSafe(FSSpec
& inOutSpec
)
219 // Pass in the spec of an alias. This copies the file to the safe haven folder, and
220 // returns the spec of the copy to the caller
221 //----------------------------------------------------------------------------------------
224 nsFileSpec
dstDirSpec(sAliasHavenVRefNum
, sAliasHavenDirID
, "\p");
226 // Make sure its name is unique
227 nsFileSpec
havenSpec(sAliasHavenVRefNum
, sAliasHavenDirID
, "\pG'day");
228 if (havenSpec
.Valid())
229 havenSpec
.MakeUnique(inOutSpec
.name
);
230 // Copy the file into the haven directory
231 if (havenSpec
.Valid())
233 OSErr err
= ::FSpFileCopy(
236 havenSpec
.GetLeafPName(),
238 // Return the spec of the copy to the caller.
241 inOutSpec
= havenSpec
;
244 } // MacFileHelpers::MakeAliasSafe
246 //----------------------------------------------------------------------------------------
247 char* MacFileHelpers::MacPathFromUnixPath(const char* unixPath
, Boolean hexDecode
)
248 //----------------------------------------------------------------------------------------
250 // Relying on the fact that the unix path is always longer than the mac path:
251 size_t len
= strlen(unixPath
);
252 char* result
= new char[len
+ 2]; // ... but allow for the initial colon in a partial name
253 // REMEMBER: at the end we call SwapSlashColon, so bear that in mind when you see
254 // this code using '/' as a separator in what is supposed to be a Macintosh path!
258 const char* src
= unixPath
;
259 if (*src
== '/') // * full path
261 else if (strchr(src
, '/') && *src
!= '.')
263 // * partial path, and not just a leaf name. The '.' test is there because
264 // the loop below will add sufficient colons in that case.
267 // Copy src to dst, but watch out for .. and .
271 char cprev
= c
; // remember the previous char (initially /)
273 if (c
== '.' && cprev
== '/')
275 char* dstSaved
= dst
;
276 // Special cases: "." and "..". Convert to ':' and '::'
277 *dst
++ = '/'; // . becomes :
281 *dst
++ = '/'; // .. becomes ::
286 // ../ becomes :: so just skip the slash
287 // ./ becomes : " " " " "
292 // Oh. A file called ".foo" or "..foo"
293 // Back up and just do the normal thing.
294 src
-= (dst
- dstSaved
);
296 // Since c is not '/', we won't do this stuff on the
301 else if (c
== '/' && cprev
== '/')
303 // Hmm. A 'run on' path with two slashes right next to each other.
304 // This is an illegal path, but, hey, we'll be tough and try to
305 // deal with it (especially since '::' has loaded semantics in
312 nsUnescape(result
); // Hex Decode
313 MacFileHelpers::SwapSlashColon(result
);
316 } // MacFileHelpers::MacPathFromUnixPath
318 //----------------------------------------------------------------------------------------
319 OSErr
MacFileHelpers::FSSpecFromPathname(
320 const char* inPathNamePtr
,
321 FSSpec
& ioSpec
, // used as in-parameter for a relative path.
322 Boolean inCreateDirs
)
323 // FSSpecFromPathname reverses PathNameFromFSSpec.
324 // It returns a FSSpec given a c string which is a mac pathname.
325 //----------------------------------------------------------------------------------------
328 // Simplify this routine to use FSMakeFSSpec if length < 255. Otherwise use the MoreFiles
329 // routine FSpLocationFromFullPath, which allocates memory, to handle longer pathnames.
331 short inVRefNum
= ioSpec
.vRefNum
;
332 long inParID
= ioSpec
.parID
;
334 size_t inLength
= strlen(inPathNamePtr
);
335 bool isRelative
= (strchr(inPathNamePtr
, ':') == 0 || *inPathNamePtr
== ':');
337 // Attempt to catch people sending unix paths in to routines that expect native ones.
338 NS_ASSERTION(strchr(inPathNamePtr
, '/') == 0,
339 "Possible unix path where native path is required");
344 MacFileHelpers::PLstrcpy(pascalpath
, inPathNamePtr
);
346 err
= ::FSMakeFSSpec(inVRefNum
, inParID
, pascalpath
, &ioSpec
);
348 err
= ::FSMakeFSSpec(0, 0, pascalpath
, &ioSpec
);
350 else if (!isRelative
)
351 err
= FSpLocationFromFullPath(inLength
, inPathNamePtr
, &ioSpec
);
355 if ((err
== dirNFErr
|| err
== bdNamErr
) && inCreateDirs
)
357 const char* path
= inPathNamePtr
;
360 ioSpec
.vRefNum
= inVRefNum
;
361 ioSpec
.parID
= inParID
;
369 // Locate the colon that terminates the node.
370 // But if we've a partial path (starting with a colon), find the second one.
371 const char* nextColon
= strchr(path
+ (*path
== ':'), ':');
372 // Well, if there are no more colons, point to the end of the string.
374 nextColon
= path
+ strlen(path
);
376 // Make a pascal string out of this node. Include initial
377 // and final colon, if any!
379 MacFileHelpers::PLstrncpy(ppath
, path
, nextColon
- path
+ 1);
381 // Use this string as a relative path using the directory created
382 // on the previous round (or directory 0,0 on the first round).
383 err
= ::FSMakeFSSpec(ioSpec
.vRefNum
, ioSpec
.parID
, ppath
, &ioSpec
);
385 // If this was the leaf node, then we are done.
389 // Since there's more to go, we have to get the directory ID, which becomes
390 // the parID for the next round.
393 // The directory (or perhaps a file) exists. Find its dirID.
396 err
= ::FSpGetDirectoryID(&ioSpec
, &dirID
, &isDirectory
);
398 return dupFNErr
; // oops! a file exists with that name.
401 ioSpec
.parID
= dirID
;
403 else if (err
== fnfErr
)
405 // If we got "file not found", then
406 // we need to create a directory.
407 err
= ::FSpDirCreate(&ioSpec
, smCurrentScript
, &ioSpec
.parID
);
408 // For some reason, this usually returns fnfErr, even though it works.
414 path
= nextColon
; // next round
418 } // MacFileHelpers::FSSpecFromPathname
420 //----------------------------------------------------------------------------------------
421 OSErr
MacFileHelpers::CreateFolderInFolder(
422 short refNum
, // Parent directory/volume
424 ConstStr255Param folderName
, // Name of the new folder
425 short& outRefNum
, // Volume of the created folder
427 // Creates a folder named 'folderName' inside a folder.
428 // The errors returned are same as PBDirCreate
429 //----------------------------------------------------------------------------------------
432 hpb
.ioVRefNum
= refNum
;
434 hpb
.ioNamePtr
= (StringPtr
)&folderName
;
436 OSErr err
= PBDirCreateSync((HParmBlkPtr
)&hpb
);
439 outRefNum
= hpb
.ioVRefNum
;
440 outDirID
= hpb
.ioDirID
;
448 } // MacFileHelpers::CreateFolderInFolder
450 //----------------------------------------------------------------------------------------
451 void MacFileHelpers::EnsureAliasHaven()
452 //----------------------------------------------------------------------------------------
454 // Alias Haven is a directory in which we never resolve aliases.
455 if (sAliasHavenVRefNum
!= 0)
460 if (FindFolder(0, kTemporaryFolderType
, true, & temp
.vRefNum
, &temp
.parID
) == noErr
)
462 CreateFolderInFolder(
463 temp
.vRefNum
, // Parent directory/volume
465 kAliasHavenFolderName
, // Name of the new folder
466 sAliasHavenVRefNum
, // Volume of the created folder
469 } // MacFileHelpers::EnsureAliasHaven
471 //----------------------------------------------------------------------------------------
472 PRBool
MacFileHelpers::IsAliasSafe(const FSSpec
& inSpec
)
473 // Returns true if the alias is in the alias haven directory, or if alias resolution
474 // has been turned off.
475 //----------------------------------------------------------------------------------------
478 || (inSpec
.parID
== sAliasHavenDirID
&& inSpec
.vRefNum
== sAliasHavenVRefNum
);
479 } // MacFileHelpers::IsAliasSafe
481 //----------------------------------------------------------------------------------------
482 OSErr
MacFileHelpers::ResolveAliasFile(FSSpec
& inOutSpec
, Boolean
& wasAliased
)
483 //----------------------------------------------------------------------------------------
486 if (IsAliasSafe(inOutSpec
))
489 return ::ResolveAliasFile(&inOutSpec
, TRUE
, &dummy
, &wasAliased
);
490 } // MacFileHelpers::ResolveAliasFile
492 //-----------------------------------
493 OSErr
MacFileHelpers::FSSpecFromUnixPath(
494 const char * unixPath
,
497 Boolean resolveAlias
,
498 Boolean allowPartial
,
500 // File spec from URL. Reverses GetURLFromFileSpec
501 // Its input is only the <path> part of the URL
502 // JRM 97/01/08 changed this so that if it's a partial path (doesn't start with '/'),
503 // then it is combined with inOutSpec's vRefNum and parID to form a new spec.
504 //-----------------------------------
506 if (unixPath
== nsnull
)
508 char* macPath
= MacPathFromUnixPath(unixPath
, hexDecode
);
515 NS_ASSERTION(*unixPath
== '/' /*full path*/, "Not a full Unix path!");
517 err
= FSSpecFromPathname(macPath
, ioSpec
, createDirs
);
521 if (err
== noErr
&& resolveAlias
) // Added
522 err
= MacFileHelpers::ResolveAliasFile(ioSpec
, dummy
);
524 NS_ASSERTION(err
==noErr
||err
==fnfErr
||err
==dirNFErr
||err
==nsvErr
, "Not a path!");
526 } // MacFileHelpers::FSSpecFromLocalUnixPath
528 //-----------------------------------
529 char* MacFileHelpers::PathNameFromFSSpec( const FSSpec
& inSpec
)
530 // Returns a full pathname to the given file
531 // Returned value is allocated with new [], and must be freed with delete []
532 // For consistency and to work under OS X this creates an nsILocalFileMac and has it do the work.
533 //-----------------------------------
538 FSSpec nonConstSpec
= inSpec
;
540 nsCOMPtr
<nsILocalFileMac
> macFile
;
542 rv
= NS_NewLocalFileWithFSSpec(&nonConstSpec
, PR_TRUE
, getter_AddRefs(macFile
));
543 if (NS_FAILED(rv
)) return nsnull
;
544 nsCOMPtr
<nsILocalFile
> localFile(do_QueryInterface(macFile
, &rv
));
545 if (NS_FAILED(rv
)) return nsnull
;
546 rv
= localFile
->GetNativePath(path
);
547 if (NS_FAILED(rv
)) return nsnull
;
548 PRInt32 strLen
= path
.Length();
549 result
= new char [strLen
+ 1];
550 if (!result
) return nsnull
;
551 memcpy(result
, path
.get(), strLen
);
552 result
[ strLen
] = 0;
555 } // MacFileHelpers::PathNameFromFSSpec
560 //========================================================================================
561 // Macintosh nsFileSpec implementation
562 //========================================================================================
564 //----------------------------------------------------------------------------------------
565 nsFileSpec::nsFileSpec()
566 //----------------------------------------------------------------------------------------
568 // NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!");
572 //----------------------------------------------------------------------------------------
573 nsFileSpec::nsFileSpec(const FSSpec
& inSpec
, PRBool resolveAlias
)
574 //----------------------------------------------------------------------------------------
578 // NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!");
582 ResolveSymlink(dummy
);
586 //----------------------------------------------------------------------------------------
587 void nsFileSpec::operator = (const FSSpec
& inSpec
)
588 //----------------------------------------------------------------------------------------
594 //----------------------------------------------------------------------------------------
595 nsFileSpec::nsFileSpec(const nsFileSpec
& inSpec
)
596 //----------------------------------------------------------------------------------------
597 : mSpec(inSpec
.mSpec
)
598 , mError(inSpec
.Error())
600 // NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!");
603 //----------------------------------------------------------------------------------------
604 nsFileSpec::nsFileSpec(const char* inNativePathString
, PRBool inCreateDirs
)
605 //----------------------------------------------------------------------------------------
607 // NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!");
608 Clear(); // this sets mError to NS_ERROR_NOT_INITIALIZED
610 if (inNativePathString
)
612 mError
= NS_FILE_RESULT(MacFileHelpers::FSSpecFromPathname(
613 inNativePathString
, mSpec
, inCreateDirs
));
614 if (mError
== NS_FILE_RESULT(fnfErr
))
618 } // nsFileSpec::nsFileSpec
620 //----------------------------------------------------------------------------------------
621 nsFileSpec::nsFileSpec(const nsString
& inNativePathString
, PRBool inCreateDirs
)
622 //----------------------------------------------------------------------------------------
624 // NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!");
625 Clear(); // this sets mError to NS_ERROR_NOT_INITIALIZED
627 mError
= NS_FILE_RESULT(
628 MacFileHelpers::FSSpecFromPathname(
629 NS_LossyConvertUTF16toASCII(inNativePathString
).get(),
630 mSpec
, inCreateDirs
));
631 if (mError
== NS_FILE_RESULT(fnfErr
))
634 } // nsFileSpec::nsFileSpec
636 //----------------------------------------------------------------------------------------
637 nsFileSpec::nsFileSpec(short vRefNum
, long parID
, ConstStr255Param fileName
, PRBool resolveAlias
)
638 //----------------------------------------------------------------------------------------
640 // NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!");
641 mError
= NS_FILE_RESULT(::FSMakeFSSpec(vRefNum
, parID
, fileName
, &mSpec
));
642 if (mError
== NS_FILE_RESULT(fnfErr
))
648 ResolveSymlink(dummy
);
652 //----------------------------------------------------------------------------------------
653 nsFileSpec::nsFileSpec(const nsFilePath
& inPath
)
654 //----------------------------------------------------------------------------------------
656 // NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!");
657 *this = inPath
.GetFileSpec();
660 //----------------------------------------------------------------------------------------
661 void nsFileSpec::operator = (const char* inString
)
662 //----------------------------------------------------------------------------------------
664 Clear(); // this sets mError to NS_ERROR_NOT_INITIALIZED
668 mError
= NS_FILE_RESULT(MacFileHelpers::FSSpecFromPathname(inString
, mSpec
, true));
669 if (mError
== NS_FILE_RESULT(fnfErr
))
673 } // nsFileSpec::operator =
675 //----------------------------------------------------------------------------------------
676 void nsFileSpec::operator = (const nsFileSpec
& inSpec
)
677 //----------------------------------------------------------------------------------------
680 mSpec
.vRefNum
= inSpec
.mSpec
.vRefNum
;
681 mSpec
.parID
= inSpec
.mSpec
.parID
;
683 PRInt32 copySize
= inSpec
.mSpec
.name
[0] + 1;
684 if (copySize
> sizeof(inSpec
.mSpec
.name
))
685 copySize
= sizeof(inSpec
.mSpec
.name
);
686 memcpy(mSpec
.name
, inSpec
.mSpec
.name
, copySize
);
687 mError
= inSpec
.Error(); // note that the error is propagated
688 } // nsFileSpec::operator =
690 //----------------------------------------------------------------------------------------
691 void nsFileSpec::operator = (const nsFilePath
& inPath
)
692 //----------------------------------------------------------------------------------------
694 *this = inPath
.GetFileSpec();
695 } // nsFileSpec::operator =
697 //----------------------------------------------------------------------------------------
698 inline void nsFileSpec::Clear()
699 //----------------------------------------------------------------------------------------
705 mError
= NS_ERROR_NOT_INITIALIZED
;
708 //----------------------------------------------------------------------------------------
709 PRBool
nsFileSpec::Exists() const
710 //----------------------------------------------------------------------------------------
712 if (NS_FAILED(mError
)) return PR_FALSE
;
714 return ::FSMakeFSSpec(mSpec
.vRefNum
, mSpec
.parID
, mSpec
.name
, &temp
) == noErr
;
715 } // nsFileSpec::Exists()
717 //----------------------------------------------------------------------------------------
718 void nsFileSpec::GetModDate(TimeStamp
& outStamp
) const
719 //----------------------------------------------------------------------------------------
722 if (GetCatInfo(pb
) == noErr
)
723 outStamp
= ((DirInfo
*)&pb
)->ioDrMdDat
; // The mod date is in the same spot for files and dirs.
726 } // nsFileSpec::GetModDate
728 //----------------------------------------------------------------------------------------
729 PRUint32
nsFileSpec::GetFileSize() const
730 //----------------------------------------------------------------------------------------
733 if (noErr
== GetCatInfo(pb
))
734 return (PRUint32
)((HFileInfo
*)&pb
)->ioFlLgLen
;
736 } // nsFileSpec::GetFileSize
738 //----------------------------------------------------------------------------------------
739 void nsFileSpec::SetLeafName(const char* inLeafName
)
740 // In leaf name can actually be a partial path...
741 //----------------------------------------------------------------------------------------
743 NS_ASSERTION(inLeafName
, "Attempt to set leaf name with a null string");
749 // what about long relative paths? Hmm? We don't have a routine for this anywhere.
751 MacFileHelpers::PLstrcpy(partialPath
, inLeafName
);
752 mError
= NS_FILE_RESULT(
753 ::FSMakeFSSpec(mSpec
.vRefNum
, mSpec
.parID
, partialPath
, &mSpec
));
754 if (mError
== NS_FILE_RESULT(fnfErr
))
758 } // nsFileSpec::SetLeafName
760 //----------------------------------------------------------------------------------------
761 char* nsFileSpec::GetLeafName() const
762 // Result needs to be nsCRT::free()ed.
763 //----------------------------------------------------------------------------------------
765 char leaf
[sizeof(mSpec
.name
)];
766 memcpy(leaf
, &mSpec
.name
[1], mSpec
.name
[0]);
767 leaf
[mSpec
.name
[0]] = '\0';
768 return nsCRT::strdup(leaf
);
769 } // nsFileSpec::GetLeafName
771 //----------------------------------------------------------------------------------------
772 void nsFileSpec::MakeAliasSafe()
773 //----------------------------------------------------------------------------------------
776 mError
= NS_FILE_RESULT(MacFileHelpers::MakeAliasSafe(mSpec
));
777 } // nsFileSpec::MakeAliasSafe
779 //----------------------------------------------------------------------------------------
780 void nsFileSpec::MakeUnique(ConstStr255Param inSuggestedLeafName
)
781 //----------------------------------------------------------------------------------------
784 if (inSuggestedLeafName
[0] > 0)
785 MacFileHelpers::PLstrcpy(mSpec
.name
, inSuggestedLeafName
);
788 } // nsFileSpec::MakeUnique
790 //----------------------------------------------------------------------------------------
791 PRBool
nsFileSpec::IsFile() const
792 //----------------------------------------------------------------------------------------
796 return (noErr
== ::FSpGetDirectoryID(&mSpec
, &dirID
, &isDirectory
) && !isDirectory
);
797 } // nsFileSpec::IsFile
799 //----------------------------------------------------------------------------------------
800 PRBool
nsFileSpec::IsDirectory() const
801 //----------------------------------------------------------------------------------------
805 return (noErr
== ::FSpGetDirectoryID(&mSpec
, &dirID
, &isDirectory
) && isDirectory
);
806 } // nsFileSpec::IsDirectory
808 //----------------------------------------------------------------------------------------
809 PRBool
nsFileSpec::IsHidden() const
810 //----------------------------------------------------------------------------------------
813 PRBool hidden
= PR_FALSE
;
815 if (noErr
== GetCatInfo(cInfo
))
816 if (cInfo
.hFileInfo
.ioFlFndrInfo
.fdFlags
& kIsInvisible
)
820 } // nsFileSpec::IsHidden
822 //----------------------------------------------------------------------------------------
823 PRBool
nsFileSpec::IsSymlink() const
824 //----------------------------------------------------------------------------------------
827 PRBool hidden
= PR_FALSE
;
829 if (noErr
== GetCatInfo(cInfo
))
830 if (cInfo
.hFileInfo
.ioFlFndrInfo
.fdFlags
& kIsAlias
)
834 } // nsFileSpec::IsSymlink
836 //----------------------------------------------------------------------------------------
837 nsresult
nsFileSpec::ResolveSymlink(PRBool
& wasAliased
)
838 //----------------------------------------------------------------------------------------
840 Boolean wasAliased2
; // Type conversion Boolean <--> PRBool
841 OSErr err
= MacFileHelpers::ResolveAliasFile(mSpec
, wasAliased2
);
844 mError
= NS_FILE_RESULT(err
);
845 wasAliased
= PR_TRUE
;
848 wasAliased
= PR_FALSE
;
851 } // nsFileSpec::ResolveSymlink
853 //----------------------------------------------------------------------------------------
854 void nsFileSpec::GetParent(nsFileSpec
& outSpec
) const
855 //----------------------------------------------------------------------------------------
857 if (NS_SUCCEEDED(mError
))
858 outSpec
.mError
= NS_FILE_RESULT(::FSMakeFSSpec(mSpec
.vRefNum
, mSpec
.parID
, nsnull
, outSpec
));
859 } // nsFileSpec::GetParent
861 //----------------------------------------------------------------------------------------
862 void nsFileSpec::operator += (const char* inRelativePath
)
863 //----------------------------------------------------------------------------------------
865 NS_ASSERTION(inRelativePath
, "Attempt to append relative path with null path");
867 // Invalidate the path cache string, since we're changing ourselves.
870 // if we are already bad, don't allow appendage
871 if (NS_FAILED(Error()))
873 NS_WARNING("trying to append to a bad nsFileSpec");
877 // Find the dirID of the directory described by this spec
880 mError
= NS_FILE_RESULT(::FSpGetDirectoryID(&mSpec
, &dirID
, &isDirectory
));
881 if (NS_FAILED(mError
) || !isDirectory
|| !inRelativePath
)
883 // mSpec.vRefNum is already correct.
886 // Next, determine if it is a UNIX or Mac style path. Now, Macintosh relative paths
887 // are either leaf names (in which the distinction between unix and macintosh
888 // relative paths disappears) or they start with a colon. If we find an initial colon,
889 // then assume it's a macintosh path.
890 // If it is a UNIX path (including just a leaf name), we will also look for ':' and
891 // assert if we find one.
892 if (*inRelativePath
!= ':')
894 // Looks like a UNIX path (including possibly just a leaf name)
895 NS_ASSERTION(strchr(inRelativePath
, ':') == nsnull
, "Can not determine path type");
896 // Convert unix path (which is unencoded) to a spec
897 mError
= NS_FILE_RESULT(
898 MacFileHelpers::FSSpecFromUnixPath(inRelativePath
, mSpec
, false, false, true, true));
902 // We must be a mac path!
903 mError
= NS_FILE_RESULT(MacFileHelpers::FSSpecFromPathname(inRelativePath
, mSpec
, true));
905 if (mError
== NS_FILE_RESULT(fnfErr
))
908 } // nsFileSpec::operator +=
910 //----------------------------------------------------------------------------------------
911 void nsFileSpec::CreateDirectory(int /* unix mode */)
912 //----------------------------------------------------------------------------------------
915 OSErr err
= ::FSpDirCreate(&mSpec
, smCurrentScript
, &ignoredDirID
);
916 // it's OK if the dir already exists
917 if (err
!= noErr
&& IsDirectory())
920 mError
= NS_FILE_RESULT(err
);
922 } // nsFileSpec::CreateDirectory
924 //----------------------------------------------------------------------------------------
925 void nsFileSpec::Delete(PRBool inRecursive
) const
926 //----------------------------------------------------------------------------------------
930 nsresult
& mutableError
= const_cast<nsFileSpec
*>(this)->mError
;
934 anErr
= ::DeleteDirectory(
937 const_cast<unsigned char*>(mSpec
.name
));
940 anErr
= ::FSpDelete(&mSpec
);
942 if (anErr
== fnfErr
) // deleting a file that doesn't exist isn't an error!
945 mutableError
= NS_FILE_RESULT(anErr
);
947 } // nsFileSpec::Delete
949 //----------------------------------------------------------------------------------------
950 void nsFileSpec::RecursiveCopy(nsFileSpec newDir
) const
951 //----------------------------------------------------------------------------------------
955 if (!(newDir
.Exists()))
957 newDir
.CreateDirectory();
960 for (nsDirectoryIterator
i(*this, PR_FALSE
); i
.Exists(); i
++)
962 nsFileSpec
& child
= (nsFileSpec
&)i
;
964 if (child
.IsDirectory())
966 nsFileSpec
tmpDirSpec(newDir
);
968 char *leafname
= child
.GetLeafName();
969 tmpDirSpec
+= leafname
;
970 nsCRT::free(leafname
);
972 child
.RecursiveCopy(tmpDirSpec
);
976 child
.RecursiveCopy(newDir
);
982 nsFileSpec
& filePath
= (nsFileSpec
&) *this;
984 if (!(newDir
.Exists()))
986 newDir
.CreateDirectory();
989 filePath
.CopyToDir(newDir
);
991 } // nsFileSpec::RecursiveCopy
993 //----------------------------------------------------------------------------------------
994 nsresult
nsFileSpec::Truncate(PRInt32 aNewLength
) const
995 //----------------------------------------------------------------------------------------
1000 // First see if we have an internal error set
1001 if (NS_FAILED(mError
))
1004 // Need to open the file to trunc
1005 if (::FSpOpenDF(&mSpec
, fsWrPerm
, &refNum
) != noErr
)
1006 return NS_FILE_FAILURE
;
1008 err
= ::SetEOF(refNum
, aNewLength
);
1010 // Close the file unless we got an error that it was already closed
1011 if (err
!= fnOpnErr
)
1012 (void)::FSClose(refNum
);
1015 return NS_FILE_FAILURE
;
1018 } // nsFileSpec::Truncate
1020 //----------------------------------------------------------------------------------------
1021 nsresult
nsFileSpec::Rename(const char* inNewName
)
1022 //----------------------------------------------------------------------------------------
1024 NS_ASSERTION(inNewName
, "Attempt to rename with null new name");
1026 if (strchr(inNewName
, '/'))
1027 return NS_FILE_FAILURE
; // no relative paths here!
1030 MacFileHelpers::PLstrcpy(pName
, inNewName
);
1031 if (::FSpRename(&mSpec
, pName
) != noErr
)
1032 return NS_FILE_FAILURE
;
1033 SetLeafName(inNewName
);
1035 } // nsFileSpec::Rename
1037 //----------------------------------------------------------------------------------------
1038 nsresult
nsFileSpec::CopyToDir(const nsFileSpec
& newParentDir
) const
1039 //----------------------------------------------------------------------------------------
1041 // We can only copy into a directory, and (for now) can not copy entire directories
1043 if (!newParentDir
.IsDirectory() || (IsDirectory() ) )
1044 return NS_FILE_FAILURE
;
1046 nsresult rv
= NS_FILE_RESULT(::FSpFileCopy(&mSpec
,
1047 &newParentDir
.mSpec
,
1048 const_cast<StringPtr
>(GetLeafPName()),
1055 } // nsFileSpec::CopyToDir
1057 //----------------------------------------------------------------------------------------
1058 nsresult
nsFileSpec::MoveToDir(const nsFileSpec
& newParentDir
)
1059 //----------------------------------------------------------------------------------------
1061 // We can only move into a directory
1063 if (!newParentDir
.IsDirectory())
1064 return NS_FILE_FAILURE
;
1066 nsresult result
= NS_FILE_RESULT(::FSpMoveRenameCompat(&mSpec
,
1067 &newParentDir
.mSpec
,
1068 const_cast<StringPtr
>(GetLeafPName())));
1070 if ( NS_SUCCEEDED(result
) )
1072 char* leafName
= GetLeafName();
1073 *this = newParentDir
+ leafName
;
1074 nsCRT::free(leafName
);
1077 } // nsFileSpec::MoveToDir
1079 //----------------------------------------------------------------------------------------
1080 nsresult
nsFileSpec::Execute(const char* /*args - how can this be cross-platform? problem! */ ) const
1081 //----------------------------------------------------------------------------------------
1084 return NS_FILE_FAILURE
;
1086 LaunchParamBlockRec launchThis
;
1087 launchThis
.launchAppSpec
= const_cast<FSSpec
*>(&mSpec
);
1088 launchThis
.launchAppParameters
= nsnull
; // args;
1089 /* launch the thing */
1090 launchThis
.launchBlockID
= extendedBlock
;
1091 launchThis
.launchEPBLength
= extendedBlockLen
;
1092 launchThis
.launchFileFlags
= nsnull
;
1093 launchThis
.launchControlFlags
= launchContinue
+ launchNoFileFlags
+ launchUseMinimum
;
1094 launchThis
.launchControlFlags
+= launchDontSwitch
;
1096 nsresult result
= NS_FILE_RESULT(::LaunchApplication(&launchThis
));
1099 } // nsFileSpec::Execute
1101 //----------------------------------------------------------------------------------------
1102 OSErr
nsFileSpec::GetCatInfo(CInfoPBRec
& outInfo
) const
1103 //----------------------------------------------------------------------------------------
1105 DirInfo
*dipb
=(DirInfo
*)&outInfo
;
1106 dipb
->ioCompletion
= nsnull
;
1107 dipb
->ioFDirIndex
= 0; // use dirID and name
1108 dipb
->ioVRefNum
= mSpec
.vRefNum
;
1109 dipb
->ioDrDirID
= mSpec
.parID
;
1110 dipb
->ioNamePtr
= const_cast<nsFileSpec
*>(this)->mSpec
.name
;
1111 return PBGetCatInfoSync(&outInfo
);
1112 } // nsFileSpec::GetCatInfo()
1114 //----------------------------------------------------------------------------------------
1115 OSErr
nsFileSpec::SetFileTypeAndCreator(OSType type
, OSType creator
)
1116 //----------------------------------------------------------------------------------------
1119 OSErr err
= ::FSpGetFInfo(&mSpec
, &info
);
1123 info
.fdCreator
= creator
;
1124 err
= ::FSpSetFInfo(&mSpec
, &info
);
1128 //----------------------------------------------------------------------------------------
1129 OSErr
nsFileSpec::GetFileTypeAndCreator(OSType
* type
, OSType
* creator
)
1130 //----------------------------------------------------------------------------------------
1133 OSErr err
= ::FSpGetFInfo(&mSpec
, &info
);
1136 *type
= info
.fdType
;
1137 *creator
= info
.fdCreator
;
1141 //----------------------------------------------------------------------------------------
1142 PRInt64
nsFileSpec::GetDiskSpaceAvailable() const
1143 //----------------------------------------------------------------------------------------
1145 PRInt64 space64Bits
;
1147 LL_I2L(space64Bits
, LONG_MAX
);
1150 pb
.ioCompletion
= nsnull
;
1152 pb
.ioNamePtr
= nsnull
;
1153 pb
.ioVRefNum
= mSpec
.vRefNum
;
1155 // PBXGetVolInfoSync works on HFS+ volumes too!
1156 OSErr err
= ::PBXGetVolInfoSync(&pb
);
1160 #ifdef HAVE_LONG_LONG
1161 space64Bits
= pb
.ioVFreeBytes
;
1163 const UnsignedWide
& freeBytes
= UInt64ToUnsignedWide(pb
.ioVFreeBytes
);
1164 space64Bits
.lo
= freeBytes
.lo
;
1165 space64Bits
.hi
= freeBytes
.hi
;
1170 } // nsFileSpec::GetDiskSpace()
1172 //----------------------------------------------------------------------------------------
1173 const char* nsFileSpec::GetCString() const
1174 // This is the only conversion to const char* that is provided, and it allows the
1175 // path to be "passed" to NSPR file routines. This practice is VERY EVIL and should only
1176 // be used to support legacy code. Using it guarantees bugs on Macintosh. The string is
1177 // cached and freed by the nsFileSpec destructor, so do not delete (or free) it.
1178 //----------------------------------------------------------------------------------------
1180 if (mPath
.IsEmpty())
1182 char* path
= MacFileHelpers::PathNameFromFSSpec(mSpec
);
1184 const_cast<nsFileSpec
*>(this)->mPath
= path
; // operator =() copies the string!!!
1187 const_cast<nsFileSpec
*>(this)->mError
= NS_ERROR_OUT_OF_MEMORY
;
1195 //========================================================================================
1196 // Macintosh nsFilePath implementation
1197 //========================================================================================
1199 //----------------------------------------------------------------------------------------
1200 static void AssignFromPath(nsFilePath
& ioPath
, const char* inString
, PRBool inCreateDirs
)
1201 //----------------------------------------------------------------------------------------
1203 NS_ASSERTION(inString
, "AssignFromPath called with null inString");
1204 NS_ASSERTION(strstr(inString
, kFileURLPrefix
) != inString
, "URL passed as path");
1210 MacFileHelpers::FSSpecFromUnixPath(
1214 true, // resolve alias
1217 // Now we have a spec,
1218 // Invoke operator = (const nsFileSpec&) to do the rest.
1219 // Why didn't we just say mPath = inString to get the path? Well, we want it to be
1220 // canonical and absolute.
1224 //----------------------------------------------------------------------------------------
1225 nsFilePath::nsFilePath(const char* inString
, PRBool inCreateDirs
)
1226 //----------------------------------------------------------------------------------------
1228 AssignFromPath(*this, inString
, inCreateDirs
);
1229 } //nsFilePath::nsFilePath
1231 //----------------------------------------------------------------------------------------
1232 nsFilePath::nsFilePath(const nsString
& inString
, PRBool inCreateDirs
)
1233 //----------------------------------------------------------------------------------------
1235 AssignFromPath(*this, NS_LossyConvertUTF16toASCII(inString
).get(),
1239 //----------------------------------------------------------------------------------------
1240 void nsFilePath::operator = (const char* inString
)
1241 //----------------------------------------------------------------------------------------
1243 AssignFromPath(*this, inString
, PR_FALSE
);
1246 //----------------------------------------------------------------------------------------
1247 nsFilePath::nsFilePath(const nsFileSpec
& inSpec
)
1248 //----------------------------------------------------------------------------------------
1253 //----------------------------------------------------------------------------------------
1254 nsFilePath::nsFilePath(const nsFileURL
& inOther
)
1255 //----------------------------------------------------------------------------------------
1260 //----------------------------------------------------------------------------------------
1261 void nsFilePath::operator = (const nsFileSpec
& inSpec
)
1262 //----------------------------------------------------------------------------------------
1264 char * path
= MacFileHelpers::PathNameFromFSSpec(inSpec
);
1265 path
= MacFileHelpers::EncodeMacPath(path
, true, false);
1269 } // nsFilePath::operator =
1271 //----------------------------------------------------------------------------------------
1272 void nsFilePath::operator = (const nsFileURL
& inOther
)
1273 //----------------------------------------------------------------------------------------
1275 char * path
= MacFileHelpers::PathNameFromFSSpec(inOther
.mFileSpec
);
1276 path
= MacFileHelpers::EncodeMacPath(path
, true, false);
1279 mFileSpec
= inOther
.GetFileSpec();
1284 //========================================================================================
1285 // nsFileURL implementation
1286 //========================================================================================
1288 //----------------------------------------------------------------------------------------
1289 nsFileURL::nsFileURL(const char* inString
, PRBool inCreateDirs
)
1290 //----------------------------------------------------------------------------------------
1293 NS_ASSERTION(inString
, "nsFileURL constructed with null inString");
1294 NS_ASSERTION(strstr(inString
, kFileURLPrefix
) == inString
, "Not a URL!");
1295 mFileSpec
.mError
= NS_FILE_RESULT(MacFileHelpers::FSSpecFromUnixPath(
1296 inString
+ kFileURLPrefixLength
,
1298 true, // need to decode
1299 false, // resolve alias
1300 false, // must be a full path
1302 if (mFileSpec
.mError
== NS_FILE_RESULT(fnfErr
))
1303 mFileSpec
.mError
= NS_OK
;
1304 } // nsFileURL::nsFileURL
1306 //----------------------------------------------------------------------------------------
1307 nsFileURL::nsFileURL(const nsString
& inString
, PRBool inCreateDirs
)
1308 //----------------------------------------------------------------------------------------
1311 NS_LossyConvertUTF16toASCII
cstring(inString
);
1312 mURL
= cstring
.get();
1313 NS_ASSERTION(strstr(cstring
.get(), kFileURLPrefix
) == cstring
.get(),
1315 mFileSpec
.mError
= NS_FILE_RESULT(MacFileHelpers::FSSpecFromUnixPath(
1316 cstring
.get() + kFileURLPrefixLength
,
1318 true, // need to decode
1319 false, // resolve alias
1320 false, // must be a full path
1322 if (mFileSpec
.mError
== NS_FILE_RESULT(fnfErr
))
1323 mFileSpec
.mError
= NS_OK
;
1324 } // nsFileURL::nsFileURL
1326 //----------------------------------------------------------------------------------------
1327 nsFileURL::nsFileURL(const nsFilePath
& inOther
)
1328 //----------------------------------------------------------------------------------------
1330 *this = inOther
.GetFileSpec();
1331 } // nsFileURL::nsFileURL
1333 //----------------------------------------------------------------------------------------
1334 nsFileURL::nsFileURL(const nsFileSpec
& inOther
)
1335 //----------------------------------------------------------------------------------------
1338 } // nsFileURL::nsFileURL
1340 //----------------------------------------------------------------------------------------
1341 void nsFileURL::operator = (const nsFilePath
& inOther
)
1342 //----------------------------------------------------------------------------------------
1344 *this = inOther
.GetFileSpec();
1345 } // nsFileURL::operator =
1347 //----------------------------------------------------------------------------------------
1348 void nsFileURL::operator = (const nsFileSpec
& inOther
)
1349 //----------------------------------------------------------------------------------------
1351 mFileSpec
= inOther
;
1352 char* path
= MacFileHelpers::PathNameFromFSSpec( mFileSpec
);
1353 char* encodedPath
= MacFileHelpers::EncodeMacPath(path
, true, true);
1354 nsSimpleCharString
encodedURL(kFileURLPrefix
);
1355 encodedURL
+= encodedPath
;
1356 nsCRT::free(encodedPath
);
1358 if (encodedURL
[encodedURL
.Length() - 1] != '/' && inOther
.IsDirectory())
1360 } // nsFileURL::operator =
1362 //----------------------------------------------------------------------------------------
1363 void nsFileURL::operator = (const char* inString
)
1364 //----------------------------------------------------------------------------------------
1366 NS_ASSERTION(inString
, "nsFileURL operator= constructed with null inString");
1369 NS_ASSERTION(strstr(inString
, kFileURLPrefix
) == inString
, "Not a URL!");
1370 mFileSpec
.mError
= NS_FILE_RESULT(MacFileHelpers::FSSpecFromUnixPath(
1371 inString
+ kFileURLPrefixLength
,
1373 true, // need to decode
1374 true, // resolve alias
1375 false, // must be a full path
1376 false)); // don't create dirs.
1377 if (mFileSpec
.mError
== NS_FILE_RESULT(fnfErr
))
1378 mFileSpec
.mError
= NS_OK
;
1379 } // nsFileURL::operator =
1383 //========================================================================================
1384 // nsDirectoryIterator
1385 //========================================================================================
1387 //----------------------------------------------------------------------------------------
1388 nsDirectoryIterator::nsDirectoryIterator(
1389 const nsFileSpec
& inDirectory
1390 , PRBool resolveSymLinks
)
1391 //----------------------------------------------------------------------------------------
1392 : mCurrent(inDirectory
)
1394 , mResoveSymLinks(resolveSymLinks
)
1398 OSErr err
= inDirectory
.GetCatInfo(pb
);
1400 // test that we have got a directory back, not a file
1401 DirInfo
* dipb
= (DirInfo
*)&pb
;
1402 if (err
!= noErr
|| !( dipb
->ioFlAttrib
& 0x0010))
1404 // Sorry about this, there seems to be a bug in CWPro 4:
1405 FSSpec
& currentSpec
= mCurrent
.nsFileSpec::operator FSSpec
&();
1406 mVRefNum
= currentSpec
.vRefNum
;
1407 mParID
= dipb
->ioDrDirID
;
1408 mMaxIndex
= pb
.dirInfo
.ioDrNmFls
;
1409 mIndex
= 0; // ready to increment
1410 ++(*this); // the pre-increment operator
1412 } // nsDirectoryIterator::nsDirectoryIterator
1414 //----------------------------------------------------------------------------------------
1415 OSErr
nsDirectoryIterator::SetToIndex()
1416 //----------------------------------------------------------------------------------------
1419 DirInfo
*dipb
=(DirInfo
*)&cipb
;
1421 dipb
->ioCompletion
= nsnull
;
1422 dipb
->ioFDirIndex
= mIndex
;
1423 // Sorry about this, there seems to be a bug in CWPro 4:
1424 FSSpec
& currentSpec
= mCurrent
.nsFileSpec::operator FSSpec
&();
1425 dipb
->ioVRefNum
= mVRefNum
; /* Might need to use vRefNum, not sure*/
1426 dipb
->ioDrDirID
= mParID
;
1427 dipb
->ioNamePtr
= objectName
;
1428 OSErr err
= PBGetCatInfoSync(&cipb
);
1431 err
= FSMakeFSSpec(mVRefNum
, mParID
, objectName
, &temp
);
1432 mCurrent
= temp
; // use the operator: it clears the string cache.
1433 mExists
= err
== noErr
;
1435 if (mExists
&& mResoveSymLinks
)
1438 mCurrent
.ResolveSymlink(ignore
);
1441 } // nsDirectoryIterator::SetToIndex()
1443 //----------------------------------------------------------------------------------------
1444 nsDirectoryIterator
& nsDirectoryIterator::operator -- ()
1445 //----------------------------------------------------------------------------------------
1448 while (--mIndex
> 0)
1450 OSErr err
= SetToIndex();
1455 } // nsDirectoryIterator::operator --
1457 //----------------------------------------------------------------------------------------
1458 nsDirectoryIterator
& nsDirectoryIterator::operator ++ ()
1459 //----------------------------------------------------------------------------------------
1462 if (mIndex
>= 0) // probably trying to use a file as a directory!
1463 while (++mIndex
<= mMaxIndex
)
1465 OSErr err
= SetToIndex();
1470 } // nsDirectoryIterator::operator ++