1 /* -*- Mode: C++; tab-width: 2; 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.
23 * Masayuki Nakano <masayuki@d-toybox.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 // This file is included by nsFileSpec.cp, and includes the Windows-specific
52 #if (_MSC_VER == 1100) || defined(__GNUC__)
55 DEFINE_OLEGUID(IID_IPersistFile
, 0x0000010BL
, 0, 0);
64 #define CreateDirectoryW CreateDirectory
66 #define CreateDirectoryA CreateDirectory
70 //----------------------------------------------------------------------------------------
71 void nsFileSpecHelpers::Canonify(nsSimpleCharString
& ioPath
, PRBool inMakeDirs
)
72 // Canonify, make absolute, and check whether directories exist. This
73 // takes a (possibly relative) native path and converts it into a
74 // fully qualified native path.
75 //----------------------------------------------------------------------------------------
80 NS_ASSERTION(strchr((const char*)ioPath
, '/') == 0,
81 "This smells like a Unix path. Native path expected! "
85 const int mode
= 0755;
86 nsSimpleCharString unixStylePath
= ioPath
;
87 nsFileSpecHelpers::NativeToUnix(unixStylePath
);
88 nsFileSpecHelpers::MakeAllDirectories((const char*)unixStylePath
, mode
);
90 char buffer
[_MAX_PATH
];
93 char* canonicalPath
= _fullpath(buffer
, ioPath
, _MAX_PATH
);
97 NS_ASSERTION( canonicalPath
[0] != '\0', "Uh oh...couldn't convert" );
98 if (canonicalPath
[0] == '\0')
101 ioPath
= canonicalPath
;
102 } // nsFileSpecHelpers::Canonify
104 //----------------------------------------------------------------------------------------
105 void nsFileSpecHelpers::UnixToNative(nsSimpleCharString
& ioPath
)
106 // This just does string manipulation. It doesn't check reality, or canonify, or
108 //----------------------------------------------------------------------------------------
110 // Allow for relative or absolute. We can do this in place, because the
111 // native path is never longer.
113 if (ioPath
.IsEmpty())
116 // Strip initial slash for an absolute path
117 char* src
= (char*)ioPath
;
120 // allocate new string by copying from ioPath[1]
121 nsSimpleCharString temp
= src
+ 1;
125 // Since it was an absolute path, check for the drive letter
126 char* colonPointer
= src
+ 2;
127 if (strstr(src
, "|/") == colonPointer
)
129 // allocate new string by copying from ioPath[1]
130 nsSimpleCharString temp
= src
+ 1;
134 // Convert '/' to '\'. (Must check EVERY character: consider UNC path
136 for (src
= (char*)ioPath
; *src
; ++src
)
142 } // nsFileSpecHelpers::UnixToNative
144 //----------------------------------------------------------------------------------------
145 void nsFileSpecHelpers::NativeToUnix(nsSimpleCharString
& ioPath
)
146 // This just does string manipulation. It doesn't check reality, or canonify, or
147 // anything. The unix path is longer, so we can't do it in place.
148 //----------------------------------------------------------------------------------------
150 if (ioPath
.IsEmpty())
153 // Convert the drive-letter separator, if present
154 nsSimpleCharString
temp("/");
156 char* cp
= (char*)ioPath
+ 1;
157 if (strstr(cp
, ":\\") == cp
)
158 *cp
= '|'; // absolute path
160 if (cp
[0] == '\\') // unc path
163 temp
[0] = '\0'; // relative path
165 // Convert '\' to '/'
168 if(IsDBCSLeadByte(*cp
) && *(cp
+1) != nsnull
)
176 // Add the slash in front.
181 //----------------------------------------------------------------------------------------
182 nsFileSpec::nsFileSpec(const nsFilePath
& inPath
)
183 //----------------------------------------------------------------------------------------
185 // NS_ASSERTION(0, "nsFileSpec is unsupported - use nsIFile!");
189 //----------------------------------------------------------------------------------------
190 void nsFileSpec::operator = (const nsFilePath
& inPath
)
191 //----------------------------------------------------------------------------------------
193 mPath
= (const char*)inPath
;
194 nsFileSpecHelpers::UnixToNative(mPath
);
196 } // nsFileSpec::operator =
198 //----------------------------------------------------------------------------------------
199 nsFilePath::nsFilePath(const nsFileSpec
& inSpec
)
200 //----------------------------------------------------------------------------------------
203 } // nsFilePath::nsFilePath
205 //----------------------------------------------------------------------------------------
206 void nsFilePath::operator = (const nsFileSpec
& inSpec
)
207 //----------------------------------------------------------------------------------------
209 mPath
= inSpec
.mPath
;
210 nsFileSpecHelpers::NativeToUnix(mPath
);
211 } // nsFilePath::operator =
213 //----------------------------------------------------------------------------------------
214 void nsFileSpec::SetLeafName(const char* inLeafName
)
215 //----------------------------------------------------------------------------------------
217 NS_ASSERTION(inLeafName
, "Attempt to SetLeafName with a null string");
218 mPath
.LeafReplace('\\', inLeafName
);
219 } // nsFileSpec::SetLeafName
221 //----------------------------------------------------------------------------------------
222 char* nsFileSpec::GetLeafName() const
223 //----------------------------------------------------------------------------------------
225 return mPath
.GetLeaf('\\');
226 } // nsFileSpec::GetLeafName
228 //----------------------------------------------------------------------------------------
229 PRBool
nsFileSpec::Exists() const
230 //----------------------------------------------------------------------------------------
233 return !mPath
.IsEmpty() && 0 == stat(nsNSPRPath(*this), &st
);
234 } // nsFileSpec::Exists
236 //----------------------------------------------------------------------------------------
237 void nsFileSpec::GetModDate(TimeStamp
& outStamp
) const
238 //----------------------------------------------------------------------------------------
241 if (!mPath
.IsEmpty() && stat(nsNSPRPath(*this), &st
) == 0)
242 outStamp
= st
.st_mtime
;
245 } // nsFileSpec::GetModDate
247 //----------------------------------------------------------------------------------------
248 PRUint32
nsFileSpec::GetFileSize() const
249 //----------------------------------------------------------------------------------------
252 if (!mPath
.IsEmpty() && stat(nsNSPRPath(*this), &st
) == 0)
253 return (PRUint32
)st
.st_size
;
255 } // nsFileSpec::GetFileSize
257 //----------------------------------------------------------------------------------------
258 PRBool
nsFileSpec::IsFile() const
259 //----------------------------------------------------------------------------------------
262 return !mPath
.IsEmpty() && 0 == stat(nsNSPRPath(*this), &st
) && (_S_IFREG
& st
.st_mode
);
263 } // nsFileSpec::IsFile
265 //----------------------------------------------------------------------------------------
266 PRBool
nsFileSpec::IsDirectory() const
267 //----------------------------------------------------------------------------------------
270 return !mPath
.IsEmpty() && 0 == stat(nsNSPRPath(*this), &st
) && (_S_IFDIR
& st
.st_mode
);
271 } // nsFileSpec::IsDirectory
273 //----------------------------------------------------------------------------------------
274 PRBool
nsFileSpec::IsHidden() const
275 //----------------------------------------------------------------------------------------
277 PRBool hidden
= PR_FALSE
;
278 if (!mPath
.IsEmpty())
280 DWORD attr
= GetFileAttributes(mPath
);
281 if (FILE_ATTRIBUTE_HIDDEN
& attr
)
286 // nsFileSpec::IsHidden
288 //----------------------------------------------------------------------------------------
289 PRBool
nsFileSpec::IsSymlink() const
290 //----------------------------------------------------------------------------------------
295 PRBool isSymlink
= PR_FALSE
;
298 // Get a pointer to the IShellLink interface.
299 hres
= CoCreateInstance(CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
, IID_IShellLink
, (void**)&psl
);
304 // Get a pointer to the IPersistFile interface.
305 hres
= psl
->QueryInterface(IID_IPersistFile
, (void**)&ppf
);
310 // Ensure that the string is Unicode.
311 MultiByteToWideChar(CP_ACP
, 0, mPath
, -1, wsz
, MAX_PATH
);
313 // Load the shortcut.
314 hres
= ppf
->Load(wsz
, STGM_READ
);
320 // Release the pointer to the IPersistFile interface.
324 // Release the pointer to the IShellLink interface.
334 //----------------------------------------------------------------------------------------
335 nsresult
nsFileSpec::ResolveSymlink(PRBool
& wasSymlink
)
336 //----------------------------------------------------------------------------------------
338 wasSymlink
= PR_FALSE
; // assume failure
349 // Get a pointer to the IShellLink interface.
350 hres
= CoCreateInstance(CLSID_ShellLink
, NULL
, CLSCTX_INPROC_SERVER
, IID_IShellLink
, (void**)&psl
);
355 // Get a pointer to the IPersistFile interface.
356 hres
= psl
->QueryInterface(IID_IPersistFile
, (void**)&ppf
);
361 // Ensure that the string is Unicode.
362 MultiByteToWideChar(CP_ACP
, 0, mPath
, -1, wsz
, MAX_PATH
);
364 // Load the shortcut.
365 hres
= ppf
->Load(wsz
, STGM_READ
);
368 wasSymlink
= PR_TRUE
;
371 hres
= psl
->Resolve(nsnull
, SLR_NO_UI
);
374 char szGotPath
[MAX_PATH
];
377 // Get the path to the link target.
378 hres
= psl
->GetPath( szGotPath
, MAX_PATH
, &wfd
, SLGP_UNCPRIORITY
);
382 // Here we modify the nsFileSpec;
389 // It wasn't a shortcut. Oh well. Leave it like it was.
393 // Release the pointer to the IPersistFile interface.
396 // Release the pointer to the IShellLink interface.
405 return NS_FILE_FAILURE
;
413 //----------------------------------------------------------------------------------------
414 void nsFileSpec::GetParent(nsFileSpec
& outSpec
) const
415 //----------------------------------------------------------------------------------------
417 outSpec
.mPath
= mPath
;
418 char* chars
= (char*)outSpec
.mPath
;
419 chars
[outSpec
.mPath
.Length() - 1] = '\0'; // avoid trailing separator, if any
420 unsigned char* cp
= _mbsrchr((unsigned char*)chars
, '\\');
422 outSpec
.mPath
.SetLength(cp
- (unsigned char*)chars
); // truncate.
423 } // nsFileSpec::GetParent
425 //----------------------------------------------------------------------------------------
426 void nsFileSpec::operator += (const char* inRelativePath
)
427 //----------------------------------------------------------------------------------------
429 NS_ASSERTION(inRelativePath
, "Attempt to do += with a null string");
431 if (!inRelativePath
|| mPath
.IsEmpty())
434 if (mPath
[mPath
.Length() - 1] == '\\')
439 // If it's a (unix) relative path, make it native
440 nsSimpleCharString dosPath
= inRelativePath
;
441 nsFileSpecHelpers::UnixToNative(dosPath
);
442 SetLeafName(dosPath
);
443 } // nsFileSpec::operator +=
445 //----------------------------------------------------------------------------------------
446 void nsFileSpec::CreateDirectory(int /*mode*/)
447 //----------------------------------------------------------------------------------------
449 // Note that mPath is canonical!
450 if (!mPath
.IsEmpty())
451 mkdir(nsNSPRPath(*this));
452 } // nsFileSpec::CreateDirectory
454 //----------------------------------------------------------------------------------------
455 void nsFileSpec::Delete(PRBool inRecursive
) const
456 //----------------------------------------------------------------------------------------
462 for (nsDirectoryIterator
i(*this, PR_FALSE
); i
.Exists(); i
++)
464 nsFileSpec
& child
= (nsFileSpec
&)i
;
465 child
.Delete(inRecursive
);
468 rmdir(nsNSPRPath(*this));
470 else if (!mPath
.IsEmpty())
472 remove(nsNSPRPath(*this));
474 } // nsFileSpec::Delete
477 //----------------------------------------------------------------------------------------
478 void nsFileSpec::RecursiveCopy(nsFileSpec newDir
) const
479 //----------------------------------------------------------------------------------------
483 if (!(newDir
.Exists()))
485 newDir
.CreateDirectory();
488 for (nsDirectoryIterator
i(*this, PR_FALSE
); i
.Exists(); i
++)
490 nsFileSpec
& child
= (nsFileSpec
&)i
;
492 if (child
.IsDirectory())
494 nsFileSpec
tmpDirSpec(newDir
);
496 char *leafname
= child
.GetLeafName();
497 tmpDirSpec
+= leafname
;
498 nsCRT::free(leafname
);
500 child
.RecursiveCopy(tmpDirSpec
);
504 child
.RecursiveCopy(newDir
);
508 else if (!mPath
.IsEmpty())
510 nsFileSpec
& filePath
= (nsFileSpec
&) *this;
512 if (!(newDir
.Exists()))
514 newDir
.CreateDirectory();
517 filePath
.CopyToDir(newDir
);
519 } // nsFileSpec::RecursiveCopy
521 //----------------------------------------------------------------------------------------
523 nsFileSpec::Truncate(PRInt32 aNewFileLength
) const
524 //----------------------------------------------------------------------------------------
529 // Leave it to Microsoft to open an existing file with a function
530 // named "CreateFile".
531 hFile
= CreateFile(mPath
,
536 FILE_ATTRIBUTE_NORMAL
,
538 if (hFile
== INVALID_HANDLE_VALUE
)
539 return NS_FILE_FAILURE
;
541 // Seek to new, desired end of file
542 status
= SetFilePointer(hFile
, aNewFileLength
, NULL
, FILE_BEGIN
);
543 if (status
== 0xffffffff)
546 // Truncate file at current cursor position
547 if (!SetEndOfFile(hFile
))
550 if (!CloseHandle(hFile
))
551 return NS_FILE_FAILURE
;
557 return NS_FILE_FAILURE
;
559 } // nsFileSpec::Truncate
561 //----------------------------------------------------------------------------------------
562 nsresult
nsFileSpec::Rename(const char* inNewName
)
563 //----------------------------------------------------------------------------------------
565 NS_ASSERTION(inNewName
, "Attempt to Rename with a null string");
567 // This function should not be used to move a file on disk.
568 if (strchr(inNewName
, '/'))
569 return NS_FILE_FAILURE
;
571 char* oldPath
= nsCRT::strdup(mPath
);
573 SetLeafName(inNewName
);
575 if (PR_Rename(oldPath
, mPath
) != NS_OK
)
577 // Could not rename, set back to the original.
579 return NS_FILE_FAILURE
;
582 nsCRT::free(oldPath
);
585 } // nsFileSpec::Rename
587 //----------------------------------------------------------------------------------------
588 nsresult
nsFileSpec::CopyToDir(const nsFileSpec
& inParentDirectory
) const
589 //----------------------------------------------------------------------------------------
591 // We can only copy into a directory, and (for now) can not copy entire directories
592 if (inParentDirectory
.IsDirectory() && (! IsDirectory() ) )
594 char *leafname
= GetLeafName();
595 nsSimpleCharString
destPath(inParentDirectory
.GetCString());
597 destPath
+= leafname
;
598 nsCRT::free(leafname
);
600 // CopyFile returns non-zero if succeeds
601 int copyOK
= CopyFile(GetCString(), destPath
, PR_TRUE
);
605 return NS_FILE_FAILURE
;
606 } // nsFileSpec::CopyToDir
608 //----------------------------------------------------------------------------------------
609 nsresult
nsFileSpec::MoveToDir(const nsFileSpec
& inNewParentDirectory
)
610 //----------------------------------------------------------------------------------------
612 // We can only copy into a directory, and (for now) can not copy entire directories
613 if (inNewParentDirectory
.IsDirectory() && (! IsDirectory() ) )
615 char *leafname
= GetLeafName();
616 nsSimpleCharString
destPath(inNewParentDirectory
.GetCString());
618 destPath
+= leafname
;
619 nsCRT::free(leafname
);
621 // MoveFile returns non-zero if succeeds
622 int copyOK
= MoveFile(GetCString(), destPath
);
626 *this = inNewParentDirectory
+ GetLeafName();
631 return NS_FILE_FAILURE
;
632 } // nsFileSpec::MoveToDir
634 //----------------------------------------------------------------------------------------
635 nsresult
nsFileSpec::Execute(const char* inArgs
) const
636 //----------------------------------------------------------------------------------------
641 nsSimpleCharString fileNameWithArgs
= "\"";
642 fileNameWithArgs
+= mPath
+ "\" " + inArgs
;
643 int execResult
= WinExec( fileNameWithArgs
, SW_NORMAL
);
648 return NS_FILE_FAILURE
;
649 } // nsFileSpec::Execute
652 //----------------------------------------------------------------------------------------
653 PRInt64
nsFileSpec::GetDiskSpaceAvailable() const
654 //----------------------------------------------------------------------------------------
659 LL_I2L(int64
, LONG_MAX
);
661 char aDrive
[_MAX_DRIVE
+ 2];
662 _splitpath( (const char*)mPath
, aDrive
, NULL
, NULL
, NULL
);
664 if (aDrive
[0] == '\0')
666 // The back end is always trying to pass us paths that look
667 // like /c|/netscape/mail. See if we've got one of them
668 if (mPath
.Length() > 2 && mPath
[0] == '/' && mPath
[2] == '|')
670 aDrive
[0] = mPath
[1];
676 // Return bogus large number and hope for the best
681 strcat(aDrive
, "\\");
684 DWORD dwSecPerClus
, dwBytesPerSec
, dwFreeClus
, dwTotalClus
;
685 ULARGE_INTEGER liFreeBytesAvailableToCaller
, liTotalNumberOfBytes
, liTotalNumberOfFreeBytes
;
688 BOOL (WINAPI
* getDiskFreeSpaceExA
)(LPCTSTR lpDirectoryName
,
689 PULARGE_INTEGER lpFreeBytesAvailableToCaller
,
690 PULARGE_INTEGER lpTotalNumberOfBytes
,
691 PULARGE_INTEGER lpTotalNumberOfFreeBytes
) = NULL
;
693 HINSTANCE hInst
= LoadLibrary("KERNEL32.DLL");
694 NS_ASSERTION(hInst
!= NULL
, "COULD NOT LOAD KERNEL32.DLL");
697 getDiskFreeSpaceExA
= (BOOL (WINAPI
*)(LPCTSTR lpDirectoryName
,
698 PULARGE_INTEGER lpFreeBytesAvailableToCaller
,
699 PULARGE_INTEGER lpTotalNumberOfBytes
,
700 PULARGE_INTEGER lpTotalNumberOfFreeBytes
))
701 GetProcAddress(hInst
, "GetDiskFreeSpaceExA");
705 if (getDiskFreeSpaceExA
&& (*getDiskFreeSpaceExA
)(aDrive
,
706 &liFreeBytesAvailableToCaller
,
707 &liTotalNumberOfBytes
,
708 &liTotalNumberOfFreeBytes
))
710 nBytes
= (double)(signed __int64
)liFreeBytesAvailableToCaller
.QuadPart
;
712 else if ( GetDiskFreeSpace(aDrive
, &dwSecPerClus
, &dwBytesPerSec
, &dwFreeClus
, &dwTotalClus
))
714 nBytes
= (double)dwFreeClus
*(double)dwSecPerClus
*(double) dwBytesPerSec
;
716 return (PRInt64
)nBytes
;
724 //========================================================================================
725 // nsDirectoryIterator
726 //========================================================================================
728 //----------------------------------------------------------------------------------------
729 nsDirectoryIterator::nsDirectoryIterator(const nsFileSpec
& inDirectory
, PRBool resolveSymlink
)
730 //----------------------------------------------------------------------------------------
731 : mCurrent(inDirectory
)
733 , mStarting(inDirectory
)
735 , mResoveSymLinks(resolveSymlink
)
737 mDir
= PR_OpenDir(inDirectory
);
739 mStarting
+= "dummy";
741 } // nsDirectoryIterator::nsDirectoryIterator
743 //----------------------------------------------------------------------------------------
744 nsDirectoryIterator::~nsDirectoryIterator()
745 //----------------------------------------------------------------------------------------
749 } // nsDirectoryIterator::nsDirectoryIterator
751 //----------------------------------------------------------------------------------------
752 nsDirectoryIterator
& nsDirectoryIterator::operator ++ ()
753 //----------------------------------------------------------------------------------------
758 PRDirEntry
* entry
= PR_ReadDir(mDir
, PR_SKIP_BOTH
); // Ignore '.' && '..'
762 mCurrent
= mStarting
;
763 mCurrent
.SetLeafName(entry
->name
);
767 mCurrent
.ResolveSymlink(ignore
);
771 } // nsDirectoryIterator::operator ++
773 //----------------------------------------------------------------------------------------
774 nsDirectoryIterator
& nsDirectoryIterator::operator -- ()
775 //----------------------------------------------------------------------------------------
777 return ++(*this); // can't do it backwards.
778 } // nsDirectoryIterator::operator --