nsLocalFileOS2.cpp revision 677833bc953b6cb418c701facbdcf4aa18d6c44e
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998-2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Henry Sobotka <sobotka@axess.com>
* IBM Corp.
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsCOMPtr.h"
#include "nsMemory.h"
#include "nsLocalFile.h"
#include "nsNativeCharsetUtils.h"
#include "nsISimpleEnumerator.h"
#include "nsIComponentManager.h"
#include "prtypes.h"
#include "prio.h"
#include <ctype.h> // needed for toupper
#include <string.h>
#include "nsXPIDLString.h"
#include "nsReadableUtils.h"
#include "prproces.h"
#include "prthread.h"
static unsigned char* PR_CALLBACK
_mbschr( const unsigned char* stringToSearch, int charToSearchFor);
extern unsigned char*
_mbsrchr( const unsigned char* stringToSearch, int charToSearchFor);
static nsresult PR_CALLBACK
CreateDirectoryA( PSZ path, PEAOP2 ppEABuf);
static int isleadbyte(int c);
#include <unistd.h>
#include <io.h>
static nsresult ConvertOS2Error(int err)
{
nsresult rv;
switch (err)
{
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
case ERROR_INVALID_DRIVE:
rv = NS_ERROR_FILE_NOT_FOUND;
break;
case ERROR_ACCESS_DENIED:
case ERROR_NOT_SAME_DEVICE:
rv = NS_ERROR_FILE_ACCESS_DENIED;
break;
case ERROR_NOT_ENOUGH_MEMORY:
case ERROR_INVALID_BLOCK:
case ERROR_INVALID_HANDLE:
case ERROR_ARENA_TRASHED:
rv = NS_ERROR_OUT_OF_MEMORY;
break;
case ERROR_CURRENT_DIRECTORY:
rv = NS_ERROR_FILE_DIR_NOT_EMPTY;
break;
case ERROR_WRITE_PROTECT:
rv = NS_ERROR_FILE_READ_ONLY;
break;
case ERROR_HANDLE_DISK_FULL:
rv = NS_ERROR_FILE_TOO_BIG;
break;
case ERROR_FILE_EXISTS:
case ERROR_ALREADY_EXISTS:
case ERROR_CANNOT_MAKE:
rv = NS_ERROR_FILE_ALREADY_EXISTS;
break;
case 0:
rv = NS_OK;
default:
rv = NS_ERROR_FAILURE;
}
return rv;
}
static void
myLL_L2II(PRInt64 result, PRInt32 *hi, PRInt32 *lo )
{
PRInt64 a64, b64; // probably could have been done with
// only one PRInt64, but these are macros,
// and I am a wimp.
// shift the hi word to the low word, then push it into a long.
LL_SHR(a64, result, 32);
LL_L2I(*hi, a64);
// shift the low word to the hi word first, then shift it back.
LL_SHL(b64, result, 32);
LL_SHR(a64, b64, 32);
LL_L2I(*lo, a64);
}
class nsDirEnumerator : public nsISimpleEnumerator
{
public:
NS_DECL_ISUPPORTS
nsDirEnumerator() : mDir(nsnull)
{
}
nsresult Init(nsILocalFile* parent)
{
nsCAutoString filepath;
parent->GetNativeTarget(filepath);
if (filepath.IsEmpty())
{
parent->GetNativePath(filepath);
}
if (filepath.IsEmpty())
{
return NS_ERROR_UNEXPECTED;
}
mDir = PR_OpenDir(filepath.get());
if (mDir == nsnull) // not a directory?
return NS_ERROR_FAILURE;
mParent = parent;
return NS_OK;
}
NS_IMETHOD HasMoreElements(PRBool *result)
{
nsresult rv;
if (mNext == nsnull && mDir)
{
PRDirEntry* entry = PR_ReadDir(mDir, PR_SKIP_BOTH);
if (entry == nsnull)
{
// end of dir entries
PRStatus status = PR_CloseDir(mDir);
if (status != PR_SUCCESS)
return NS_ERROR_FAILURE;
mDir = nsnull;
*result = PR_FALSE;
return NS_OK;
}
nsCOMPtr<nsIFile> file;
rv = mParent->Clone(getter_AddRefs(file));
if (NS_FAILED(rv))
return rv;
rv = file->AppendNative(nsDependentCString(entry->name));
if (NS_FAILED(rv))
return rv;
// make sure the thing exists. If it does, try the next one.
PRBool exists;
rv = file->Exists(&exists);
if (NS_FAILED(rv) || !exists)
{
return HasMoreElements(result);
}
mNext = do_QueryInterface(file);
}
*result = mNext != nsnull;
return NS_OK;
}
NS_IMETHOD GetNext(nsISupports **result)
{
nsresult rv;
PRBool hasMore;
rv = HasMoreElements(&hasMore);
if (NS_FAILED(rv)) return rv;
*result = mNext; // might return nsnull
NS_IF_ADDREF(*result);
mNext = nsnull;
return NS_OK;
}
private:
~nsDirEnumerator()
{
if (mDir)
{
PRStatus status = PR_CloseDir(mDir);
NS_ASSERTION(status == PR_SUCCESS, "close failed");
}
}
protected:
PRDir* mDir;
nsCOMPtr<nsILocalFile> mParent;
nsCOMPtr<nsILocalFile> mNext;
};
NS_IMPL_ISUPPORTS1(nsDirEnumerator, nsISimpleEnumerator)
nsLocalFile::nsLocalFile()
{
MakeDirty();
}
nsLocalFile::nsLocalFile(const nsLocalFile& other)
: mDirty(other.mDirty)
, mWorkingPath(other.mWorkingPath)
, mFileInfo64(other.mFileInfo64)
{
}
/* nsISupports interface implementation. */
NS_IMPL_THREADSAFE_ISUPPORTS2(nsLocalFile, nsILocalFile, nsIFile)
NS_METHOD
nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
{
NS_ENSURE_ARG_POINTER(aInstancePtr);
NS_ENSURE_NO_AGGREGATION(outer);
nsLocalFile* inst = new nsLocalFile();
if (inst == NULL)
return NS_ERROR_OUT_OF_MEMORY;
nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
if (NS_FAILED(rv))
{
delete inst;
return rv;
}
return NS_OK;
}
// This function resets any cached information about the file.
void
nsLocalFile::MakeDirty()
{
mDirty = PR_TRUE;
}
nsresult
nsLocalFile::Stat()
{
if (!mDirty)
return NS_OK;
char temp[4];
const char* workingFilePath = mWorkingPath.get();
const char* nsprPath = workingFilePath;
if (mWorkingPath.Length() == 2 && mWorkingPath.CharAt(1) == ':') {
temp[0] = workingFilePath[0];
temp[1] = workingFilePath[1];
temp[2] = '\\';
temp[3] = '\0';
nsprPath = temp;
}
DosError(FERR_DISABLEHARDERR);
PRStatus status = PR_GetFileInfo64(nsprPath, &mFileInfo64);
DosError(FERR_ENABLEHARDERR);
if ( status == PR_SUCCESS )
return NS_OK;
return NS_ERROR_FILE_NOT_FOUND;
}
NS_IMETHODIMP
nsLocalFile::Clone(nsIFile **file)
{
// Just copy-construct ourselves
*file = new nsLocalFile(*this);
if (!*file)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*file);
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::InitWithNativePath(const nsACString &filePath)
{
MakeDirty();
nsACString::const_iterator begin, end;
filePath.BeginReading(begin);
filePath.EndReading(end);
// input string must not be empty
if (begin == end)
return NS_ERROR_FAILURE;
char firstChar = *begin;
char secondChar = *(++begin);
// just do a sanity check. if it has any forward slashes, it is not a Native path
// on windows. Also, it must have a colon at after the first char.
char *path = nsnull;
PRInt32 pathLen = 0;
if ( ( (secondChar == ':') && !FindCharInReadable('/', begin, end) ) || // normal path
( (firstChar == '\\') && (secondChar == '\\') ) ) // network path
{
// This is a native path
path = ToNewCString(filePath);
pathLen = filePath.Length();
}
if (path == nsnull)
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
// kill any trailing '\' provided it isn't the second char of DBCS
PRInt32 len = pathLen - 1;
if (path[len] == '\\' && !::isleadbyte(path[len-1]))
{
path[len] = '\0';
pathLen = len;
}
mWorkingPath.Adopt(path, pathLen);
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
{
nsresult rv = Stat();
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
return rv;
*_retval = PR_Open(mWorkingPath.get(), flags, mode);
if (*_retval)
return NS_OK;
return NS_ErrorAccordingToNSPR();
}
NS_IMETHODIMP
nsLocalFile::OpenANSIFileDesc(const char *mode, FILE * *_retval)
{
nsresult rv = Stat();
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
return rv;
*_retval = fopen(mWorkingPath.get(), mode);
if (*_retval)
return NS_OK;
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsLocalFile::Create(PRUint32 type, PRUint32 attributes)
{
if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
return NS_ERROR_FILE_UNKNOWN_TYPE;
nsresult rv = Stat();
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
return rv;
// create nested directories to target
unsigned char* slash = _mbschr((const unsigned char*) mWorkingPath.get(), '\\');
if (slash)
{
// skip the first '\\'
++slash;
slash = _mbschr(slash, '\\');
while (slash)
{
*slash = '\0';
rv = CreateDirectoryA(NS_CONST_CAST(char*, mWorkingPath.get()), NULL);
if (rv) {
rv = ConvertOS2Error(rv);
if (rv != NS_ERROR_FILE_ALREADY_EXISTS) return rv;
}
*slash = '\\';
++slash;
slash = _mbschr(slash, '\\');
}
}
if (type == NORMAL_FILE_TYPE)
{
PRFileDesc* file = PR_Open(mWorkingPath.get(), PR_RDONLY | PR_CREATE_FILE | PR_APPEND | PR_EXCL, attributes);
if (!file) return NS_ERROR_FILE_ALREADY_EXISTS;
PR_Close(file);
return NS_OK;
}
if (type == DIRECTORY_TYPE)
{
rv = CreateDirectoryA(NS_CONST_CAST(char*, mWorkingPath.get()), NULL);
if (rv)
return ConvertOS2Error(rv);
else
return NS_OK;
}
return NS_ERROR_FILE_UNKNOWN_TYPE;
}
NS_IMETHODIMP
nsLocalFile::AppendNative(const nsACString &node)
{
if (node.IsEmpty())
return NS_OK;
// Append only one component. Check for subdirs.
// XXX can we avoid the PromiseFlatCString call?
if (_mbschr((const unsigned char*) PromiseFlatCString(node).get(), '\\') != nsnull)
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
return AppendRelativeNativePath(node);
}
NS_IMETHODIMP
nsLocalFile::AppendRelativeNativePath(const nsACString &node)
{
// Cannot start with a / or have .. or have / anywhere
nsACString::const_iterator begin, end;
node.BeginReading(begin);
node.EndReading(end);
if (node.IsEmpty() || FindCharInReadable('/', begin, end))
{
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
}
MakeDirty();
mWorkingPath.Append(NS_LITERAL_CSTRING("\\") + node);
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::Normalize()
{
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
{
aLeafName.Truncate();
const char* temp = mWorkingPath.get();
if(temp == nsnull)
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
const char* leaf = (const char*) _mbsrchr((const unsigned char*) temp, '\\');
// if the working path is just a node without any lashes.
if (leaf == nsnull)
leaf = temp;
else
leaf++;
aLeafName.Assign(leaf);
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
{
MakeDirty();
const unsigned char* temp = (const unsigned char*) mWorkingPath.get();
if(temp == nsnull)
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
// cannot use nsCString::RFindChar() due to 0x5c problem
PRInt32 offset = (PRInt32) (_mbsrchr(temp, '\\') - temp);
if (offset)
{
mWorkingPath.Truncate(offset+1);
}
mWorkingPath.Append(aLeafName);
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::GetNativePath(nsACString &_retval)
{
_retval = mWorkingPath;
return NS_OK;
}
nsresult
nsLocalFile::CopySingleFile(nsIFile *sourceFile, nsIFile *destParent, const nsACString &newName, PRBool move)
{
nsresult rv;
nsCAutoString filePath;
nsCAutoString destPath;
destParent->GetNativeTarget(destPath);
destPath.Append("\\");
if (newName.IsEmpty())
{
nsCAutoString aFileName;
sourceFile->GetNativeLeafName(aFileName);
destPath.Append(aFileName);
}
else
{
destPath.Append(newName);
}
rv = sourceFile->GetNativePath(filePath);
if (NS_FAILED(rv))
return rv;
APIRET rc = NO_ERROR;
if( move )
{
rc = DosMove(filePath.get(), (PSZ)NS_CONST_CAST(char*, destPath.get()));
}
if (!move || rc == ERROR_NOT_SAME_DEVICE || rc == ERROR_ACCESS_DENIED) {
/* will get an error if the destination and source files aren't on the
* same drive. "MoveFile()" on Windows will go ahead and move the
* file without error, so we need to do the same IBM-AKR
*/
do {
rc = DosCopy(filePath.get(), (PSZ)NS_CONST_CAST(char*, destPath.get()), DCPY_EXISTING);
if (rc == ERROR_TOO_MANY_OPEN_FILES) {
ULONG CurMaxFH = 0;
LONG ReqCount = 20;
APIRET rc2;
rc2 = DosSetRelMaxFH(&ReqCount, &CurMaxFH);
if (rc2 != NO_ERROR) {
break;
}
}
} while (rc == ERROR_TOO_MANY_OPEN_FILES);
/* WSOD2 HACK */
if (rc == 65) { // NETWORK_ACCESS_DENIED
CHAR achProgram[CCHMAXPATH]; // buffer for program name, parameters
RESULTCODES rescResults; // buffer for results of dosexecpgm
strcpy(achProgram, "CMD.EXE /C ");
strcat(achProgram, """COPY ");
strcat(achProgram, filePath.get());
strcat(achProgram, " ");
strcat(achProgram, (PSZ)NS_CONST_CAST(char*, destPath.get()));
strcat(achProgram, """");
achProgram[strlen(achProgram) + 1] = '\0';
achProgram[7] = '\0';
DosExecPgm(NULL, 0,
EXEC_SYNC, achProgram, (PSZ)NULL,
&rescResults, achProgram);
rc = 0; // Assume it worked
} /* rc == 65 */
/* moving the file is supposed to act like a rename, so delete the
* original file if we got this far without error
*/
if( move && (rc == NO_ERROR) )
{
DosDelete( filePath.get() );
}
} /* !move or ERROR */
if (rc)
rv = ConvertOS2Error(rc);
return rv;
}
nsresult
nsLocalFile::CopyMove(nsIFile *aParentDir, const nsACString &newName, PRBool move)
{
nsCOMPtr<nsIFile> newParentDir = aParentDir;
nsresult rv = Stat();
if (NS_FAILED(rv))
return rv;
if (!newParentDir)
{
// no parent was specified. We must rename.
if (newName.IsEmpty())
return NS_ERROR_INVALID_ARG;
rv = GetParent(getter_AddRefs(newParentDir));
if (NS_FAILED(rv))
return rv;
}
if (!newParentDir)
return NS_ERROR_FILE_DESTINATION_NOT_DIR;
// make sure it exists and is a directory. Create it if not there.
PRBool exists;
newParentDir->Exists(&exists);
if (exists == PR_FALSE)
{
rv = newParentDir->Create(DIRECTORY_TYPE, 0644); // TODO, what permissions should we use
if (NS_FAILED(rv))
return rv;
}
else
{
PRBool isDir;
newParentDir->IsDirectory(&isDir);
if (isDir == PR_FALSE)
{
return NS_ERROR_FILE_DESTINATION_NOT_DIR;
}
}
// check to see if we are a directory, if so enumerate it.
PRBool isDir;
IsDirectory(&isDir);
if (!isDir)
{
rv = CopySingleFile(this, newParentDir, newName, move);
if (NS_FAILED(rv))
return rv;
}
else
{
// create a new target destination in the new parentDir;
nsCOMPtr<nsIFile> target;
rv = newParentDir->Clone(getter_AddRefs(target));
if (NS_FAILED(rv))
return rv;
nsCAutoString allocatedNewName;
if (newName.IsEmpty())
{
GetNativeLeafName(allocatedNewName);// this should be the leaf name of the
}
else
{
allocatedNewName = newName;
}
rv = target->AppendNative(allocatedNewName);
if (NS_FAILED(rv))
return rv;
allocatedNewName.Truncate();
target->Create(DIRECTORY_TYPE, 0644); // TODO, what permissions should we use
if (NS_FAILED(rv))
return rv;
nsDirEnumerator* dirEnum = new nsDirEnumerator();
if (!dirEnum)
return NS_ERROR_OUT_OF_MEMORY;
rv = dirEnum->Init(this);
nsCOMPtr<nsISimpleEnumerator> iterator = do_QueryInterface(dirEnum);
PRBool more;
iterator->HasMoreElements(&more);
while (more)
{
nsCOMPtr<nsISupports> item;
nsCOMPtr<nsIFile> file;
iterator->GetNext(getter_AddRefs(item));
file = do_QueryInterface(item);
PRBool isDir, isLink;
file->IsDirectory(&isDir);
if (move)
{
rv = file->MoveToNative(target, nsCString());
}
else
{
rv = file->CopyToNative(target, nsCString());
}
iterator->HasMoreElements(&more);
}
// we've finished moving all the children of this directory
// in the new directory. so now delete the directory
// note, we don't need to do a recursive delete.
// MoveTo() is recursive. At this point,
// we've already moved the children of the current folder
// to the new location. nothing should be left in the folder.
if (move)
{
rv = Remove(PR_FALSE);
NS_ENSURE_SUCCESS(rv,rv);
}
}
// If we moved, we want to adjust this.
if (move)
{
MakeDirty();
nsCAutoString newParentPath;
newParentDir->GetNativePath(newParentPath);
if (newParentPath.IsEmpty())
return NS_ERROR_FAILURE;
if (newName.IsEmpty())
{
nsCAutoString aFileName;
GetNativeLeafName(aFileName);
InitWithNativePath(newParentPath);
AppendNative(aFileName);
}
else
{
InitWithNativePath(newParentPath);
AppendNative(newName);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString &newName)
{
return CopyMove(newParentDir, newName, PR_FALSE);
}
NS_IMETHODIMP
nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString &newName)
{
return CopyMove(newParentDir, newName, PR_FALSE);
}
NS_IMETHODIMP
nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString &newName)
{
return CopyMove(newParentDir, newName, PR_TRUE);
}
NS_IMETHODIMP
nsLocalFile::Load(PRLibrary * *_retval)
{
PRBool isFile;
nsresult rv = IsFile(&isFile);
if (NS_FAILED(rv))
return rv;
if (! isFile)
return NS_ERROR_FILE_IS_DIRECTORY;
*_retval = PR_LoadLibrary(mWorkingPath.get());
if (*_retval)
return NS_OK;
return NS_ERROR_NULL_POINTER;
}
NS_IMETHODIMP
nsLocalFile::Remove(PRBool recursive)
{
PRBool isDir;
nsresult rv = IsDirectory(&isDir);
if (NS_FAILED(rv))
return rv;
const char *filePath = mWorkingPath.get();
if (isDir)
{
if (recursive)
{
nsDirEnumerator* dirEnum = new nsDirEnumerator();
if (dirEnum == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
rv = dirEnum->Init(this);
nsCOMPtr<nsISimpleEnumerator> iterator = do_QueryInterface(dirEnum);
PRBool more;
iterator->HasMoreElements(&more);
while (more)
{
nsCOMPtr<nsISupports> item;
nsCOMPtr<nsIFile> file;
iterator->GetNext(getter_AddRefs(item));
file = do_QueryInterface(item);
file->Remove(recursive);
iterator->HasMoreElements(&more);
}
}
rv = rmdir(filePath) == -1 ? NSRESULT_FOR_ERRNO() : NS_OK;
}
else
{
rv = remove(filePath) == -1 ? NSRESULT_FOR_ERRNO() : NS_OK;
}
MakeDirty();
return rv;
}
NS_IMETHODIMP
nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModifiedTime)
{
NS_ENSURE_ARG(aLastModifiedTime);
*aLastModifiedTime = 0;
nsresult rv = Stat();
if (NS_FAILED(rv))
return rv;
// microseconds -> milliseconds
*aLastModifiedTime = mFileInfo64.modifyTime / PR_USEC_PER_MSEC;
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModifiedTime)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsLocalFile::SetLastModifiedTime(PRInt64 aLastModifiedTime)
{
return nsLocalFile::SetModDate(aLastModifiedTime);
}
NS_IMETHODIMP
nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModifiedTime)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
nsLocalFile::SetModDate(PRInt64 aLastModifiedTime)
{
nsresult rv = Stat();
if (NS_FAILED(rv))
return rv;
const char *filePath = mWorkingPath.get();
PRExplodedTime pret;
FILESTATUS3 pathInfo;
rv = DosQueryPathInfo(filePath,
FIL_STANDARD, // Level 1 info
&pathInfo,
sizeof(pathInfo));
if (NS_FAILED(rv))
{
rv = ConvertOS2Error(rv);
return rv;
}
// PR_ExplodeTime expects usecs...
PR_ExplodeTime(aLastModifiedTime * PR_USEC_PER_MSEC, PR_LocalTimeParameters, &pret);
/* fdateLastWrite.year is based off of 1980 */
if (pret.tm_year >= 1980)
pathInfo.fdateLastWrite.year = pret.tm_year-1980;
else
pathInfo.fdateLastWrite.year = pret.tm_year;
pathInfo.fdateLastWrite.month = pret.tm_month + 1; // Convert start offset -- Win32: Jan=1; NSPR: Jan=0
// ???? OS2TODO st.wDayOfWeek = pret.tm_wday;
pathInfo.fdateLastWrite.day = pret.tm_mday;
pathInfo.ftimeLastWrite.hours = pret.tm_hour;
pathInfo.ftimeLastWrite.minutes = pret.tm_min;
pathInfo.ftimeLastWrite.twosecs = pret.tm_sec / 2; // adjust for twosecs?
// ??? OS2TODO st.wMilliseconds = pret.tm_usec/1000;
rv = DosSetPathInfo(filePath,
FIL_STANDARD, // Level 1 info
&pathInfo,
sizeof(pathInfo),
0UL);
if (NS_FAILED(rv))
return rv;
MakeDirty();
return rv;
}
NS_IMETHODIMP
nsLocalFile::GetPermissions(PRUint32 *aPermissions)
{
nsresult rv = Stat();
if (NS_FAILED(rv))
return rv;
const char *filePath = mWorkingPath.get();
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsLocalFile::SetPermissions(PRUint32 aPermissions)
{
nsresult rv = Stat();
if (NS_FAILED(rv))
return rv;
const char *filePath = mWorkingPath.get();
if( chmod(filePath, aPermissions) == -1 )
return NS_ERROR_FAILURE;
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsLocalFile::GetFileSize(PRInt64 *aFileSize)
{
NS_ENSURE_ARG(aFileSize);
*aFileSize = 0;
nsresult rv = Stat();
if (NS_FAILED(rv))
return rv;
*aFileSize = mFileInfo64.size;
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::SetFileSize(PRInt64 aFileSize)
{
nsresult rv = Stat();
if (NS_FAILED(rv))
return rv;
const char *filePath = mWorkingPath.get();
APIRET rc;
HFILE hFile;
ULONG actionTaken;
rc = DosOpen(filePath,
&hFile,
&actionTaken,
0,
FILE_NORMAL,
OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
OPEN_SHARE_DENYREADWRITE | OPEN_ACCESS_READWRITE,
NULL);
if (rc != NO_ERROR)
{
MakeDirty();
return NS_ERROR_FAILURE;
}
// Seek to new, desired end of file
PRInt32 hi, lo;
myLL_L2II(aFileSize, &hi, &lo );
rc = DosSetFileSize(hFile, lo);
if (rc == NO_ERROR)
DosClose(hFile);
else
goto error;
MakeDirty();
return NS_OK;
error:
MakeDirty();
DosClose(hFile);
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSize)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
{
NS_ENSURE_ARG(aDiskSpaceAvailable);
ULONG ulDriveNo = toupper(mWorkingPath.CharAt(0)) + 1 - 'A';
FSALLOCATE fsAllocate;
APIRET rc = DosQueryFSInfo(ulDriveNo,
FSIL_ALLOC,
&fsAllocate,
sizeof(fsAllocate));
if (rc != NO_ERROR)
return NS_ERROR_FAILURE;
*aDiskSpaceAvailable = fsAllocate.cUnitAvail;
*aDiskSpaceAvailable *= fsAllocate.cSectorUnit;
*aDiskSpaceAvailable *= fsAllocate.cbSector;
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::GetParent(nsIFile * *aParent)
{
NS_ENSURE_ARG_POINTER(aParent);
nsCAutoString parentPath(mWorkingPath);
// cannot use nsCString::RFindChar() due to 0x5c problem
PRInt32 offset = (PRInt32) (_mbsrchr((const unsigned char *) parentPath.get(), '\\')
- (const unsigned char *) parentPath.get());
if (offset < 0)
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
parentPath.Truncate(offset);
nsCOMPtr<nsILocalFile> localFile;
nsresult rv = NS_NewNativeLocalFile(parentPath, PR_TRUE, getter_AddRefs(localFile));
if(NS_SUCCEEDED(rv) && localFile)
{
return CallQueryInterface(localFile, aParent);
}
return rv;
}
NS_IMETHODIMP
nsLocalFile::Exists(PRBool *_retval)
{
NS_ENSURE_ARG(_retval);
MakeDirty();
*_retval = NS_SUCCEEDED(Stat());
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::IsWritable(PRBool *_retval)
{
NS_ENSURE_ARG(_retval);
*_retval = PR_FALSE;
nsresult rv = Stat();
if (NS_FAILED(rv))
return rv;
const char *workingFilePath = mWorkingPath.get();
APIRET rc;
FILESTATUS3 pathInfo;
rc = DosQueryPathInfo(workingFilePath,
FIL_STANDARD, // Level 1 info
&pathInfo,
sizeof(pathInfo));
if (rc != NO_ERROR)
{
rc = ConvertOS2Error(rc);
return rc;
}
*_retval = !((pathInfo.attrFile & FILE_READONLY) != 0);
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::IsReadable(PRBool *_retval)
{
NS_ENSURE_ARG(_retval);
*_retval = PR_FALSE;
nsresult rv = Stat();
if (NS_FAILED(rv))
return rv;
*_retval = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::IsExecutable(PRBool *_retval)
{
NS_ENSURE_ARG(_retval);
*_retval = PR_FALSE;
nsresult rv = Stat();
if (NS_FAILED(rv))
return rv;
nsCAutoString path;
GetNativeTarget(path);
const char* leaf = (const char*) _mbsrchr((const unsigned char*) path.get(), '\\');
if ( (strstr(leaf, ".bat") != nsnull) ||
(strstr(leaf, ".exe") != nsnull) ||
(strstr(leaf, ".cmd") != nsnull) ||
(strstr(leaf, ".com") != nsnull) ) {
*_retval = PR_TRUE;
} else {
*_retval = PR_FALSE;
}
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::IsDirectory(PRBool *_retval)
{
NS_ENSURE_ARG(_retval);
*_retval = PR_FALSE;
nsresult rv = Stat();
if (NS_FAILED(rv))
return rv;
*_retval = (mFileInfo64.type == PR_FILE_DIRECTORY);
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::IsFile(PRBool *_retval)
{
NS_ENSURE_ARG(_retval);
*_retval = PR_FALSE;
nsresult rv = Stat();
if (NS_FAILED(rv))
return rv;
*_retval = (mFileInfo64.type == PR_FILE_FILE);
return rv;
}
NS_IMETHODIMP
nsLocalFile::IsHidden(PRBool *_retval)
{
NS_ENSURE_ARG(_retval);
*_retval = PR_FALSE;
nsresult rv = Stat();
if (NS_FAILED(rv))
return rv;
const char *workingFilePath = mWorkingPath.get();
APIRET rc;
FILESTATUS3 pathInfo;
rc = DosQueryPathInfo(workingFilePath,
FIL_STANDARD, // Level 1 info
&pathInfo,
sizeof(pathInfo));
if (rc != NO_ERROR)
{
rc = ConvertOS2Error(rc);
return rc;
}
*_retval = ((pathInfo.attrFile & FILE_HIDDEN) != 0);
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::IsSymlink(PRBool *_retval)
{
NS_ENSURE_ARG_POINTER(_retval);
// No Symlinks on OS/2
*_retval = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::IsSpecial(PRBool *_retval)
{
NS_ENSURE_ARG(_retval);
*_retval = PR_FALSE;
nsresult rv = Stat();
if (NS_FAILED(rv))
return rv;
const char *workingFilePath = mWorkingPath.get();
APIRET rc;
FILESTATUS3 pathInfo;
rc = DosQueryPathInfo(workingFilePath,
FIL_STANDARD, // Level 1 info
&pathInfo,
sizeof(pathInfo));
if (rc != NO_ERROR)
{
rc = ConvertOS2Error(rc);
return rc;
}
*_retval = ((pathInfo.attrFile & FILE_SYSTEM) != 0);
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
{
NS_ENSURE_ARG(inFile);
NS_ENSURE_ARG(_retval);
*_retval = PR_FALSE;
nsCAutoString inFilePath;
inFile->GetNativePath(inFilePath);
*_retval = inFilePath.Equals(mWorkingPath);
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval)
{
*_retval = PR_FALSE;
nsCAutoString myFilePath;
if ( NS_FAILED(GetNativeTarget(myFilePath)))
GetNativePath(myFilePath);
PRInt32 myFilePathLen = myFilePath.Length();
nsCAutoString inFilePath;
if ( NS_FAILED(inFile->GetNativeTarget(inFilePath)))
inFile->GetNativePath(inFilePath);
if ( strnicmp( myFilePath.get(), inFilePath.get(), myFilePathLen) == 0)
{
// now make sure that the |inFile|'s path has a trailing
// separator.
if (inFilePath[myFilePathLen] == '\\')
{
*_retval = PR_TRUE;
}
}
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::GetNativeTarget(nsACString &_retval)
{
_retval = mWorkingPath;
return NS_OK;
}
/* attribute PRBool followLinks; */
NS_IMETHODIMP
nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
{
*aFollowLinks = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
{
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator * *entries)
{
nsresult rv;
*entries = nsnull;
PRBool isDir;
rv = IsDirectory(&isDir);
if (NS_FAILED(rv))
return rv;
if (!isDir)
return NS_ERROR_FILE_NOT_DIRECTORY;
nsDirEnumerator* dirEnum = new nsDirEnumerator();
if (dirEnum == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(dirEnum);
rv = dirEnum->Init(this);
if (NS_FAILED(rv))
{
NS_RELEASE(dirEnum);
return rv;
}
*entries = dirEnum;
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
{
return GetNativePath(aPersistentDescriptor);
}
NS_IMETHODIMP
nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
{
return InitWithNativePath(aPersistentDescriptor);
}
#ifndef OPEN_DEFAULT
#define OPEN_DEFAULT 0
#define OPEN_CONTENTS 1
#endif
NS_IMETHODIMP
nsLocalFile::Reveal()
{
PRBool isDirectory = PR_FALSE;
nsCAutoString path;
IsDirectory(&isDirectory);
if (isDirectory)
{
GetNativePath(path);
}
else
{
nsCOMPtr<nsIFile> parent;
GetParent(getter_AddRefs(parent));
if (parent)
parent->GetNativePath(path);
}
HOBJECT hobject = WinQueryObject(path.get());
WinSetFocus(HWND_DESKTOP, HWND_DESKTOP);
WinOpenObject( hobject, OPEN_CONTENTS, TRUE);
// we don't care if it succeeded or failed.
return NS_OK;
}
NS_IMETHODIMP
nsLocalFile::Launch()
{
HOBJECT hobject = WinQueryObject(mWorkingPath.get());
WinSetFocus(HWND_DESKTOP, HWND_DESKTOP);
WinOpenObject( hobject, OPEN_DEFAULT, TRUE);
// we don't care if it succeeded or failed.
return NS_OK;
}
nsresult
NS_NewNativeLocalFile(const nsACString &path, PRBool followLinks, nsILocalFile* *result)
{
nsLocalFile* file = new nsLocalFile();
if (file == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(file);
if (!path.IsEmpty()) {
nsresult rv = file->InitWithNativePath(path);
if (NS_FAILED(rv)) {
NS_RELEASE(file);
return rv;
}
}
*result = file;
return NS_OK;
}
// Locates the first occurrence of charToSearchFor in the stringToSearch
static unsigned char* PR_CALLBACK
_mbschr( const unsigned char* stringToSearch, int charToSearchFor)
{
const unsigned char* p = stringToSearch;
do {
if (*p == charToSearchFor)
break;
p = (const unsigned char*)WinNextChar(0,0,0,(char*)p);
} while (*p); /* enddo */
// Result is p or NULL
return *p ? (unsigned char*)p : NULL;
}
// Locates last occurence of charToSearchFor in the stringToSearch
extern unsigned char*
_mbsrchr( const unsigned char* stringToSearch, int charToSearchFor)
{
int length = strlen((const char*)stringToSearch);
const unsigned char* p = stringToSearch+length;
do {
if (*p == charToSearchFor)
break;
p = (const unsigned char*)WinPrevChar(0,0,0,(char*)stringToSearch,(char*)p);
} while (p > stringToSearch); /* enddo */
// Result is p or NULL
return (*p == charToSearchFor) ? (unsigned char*)p : NULL;
}
// Implement equivalent of Win32 CreateDirectoryA
static nsresult PR_CALLBACK
CreateDirectoryA( PSZ path, PEAOP2 ppEABuf)
{
APIRET rc;
nsresult rv;
FILESTATUS3 pathInfo;
rc = DosCreateDir( path, ppEABuf );
if (rc != NO_ERROR) {
rv = ConvertOS2Error(rc);
// Check if directory already exists and if so, reflect that in the return value
rc = DosQueryPathInfo(path,
FIL_STANDARD, // Level 1 info
&pathInfo,
sizeof(pathInfo));
if (rc == NO_ERROR)
rv = ERROR_FILE_EXISTS;
}
else
rv = rc;
return rv;
}
static int isleadbyte(int c)
{
static BOOL bDBCSFilled=FALSE;
static BYTE DBCSInfo[12] = { 0 }; /* According to the Control Program Guide&Ref,
12 bytes is sufficient */
BYTE *curr;
BOOL retval = FALSE;
if( !bDBCSFilled ) {
COUNTRYCODE ctrycodeInfo = { 0 };
APIRET rc = NO_ERROR;
ctrycodeInfo.country = 0; /* Current Country */
ctrycodeInfo.codepage = 0; /* Current Codepage */
rc = DosQueryDBCSEnv( sizeof( DBCSInfo ),
&ctrycodeInfo,
DBCSInfo );
if( rc != NO_ERROR ) {
/* we had an error, do something? */
return FALSE;
}
bDBCSFilled=TRUE;
}
curr = DBCSInfo;
/* DBCSInfo returned by DosQueryDBCSEnv is terminated with two '0' bytes in a row */
while(( *curr != 0 ) && ( *(curr+1) != 0)) {
if(( c >= *curr ) && ( c <= *(curr+1) )) {
retval=TRUE;
break;
}
curr+=2;
}
return retval;
}
NS_IMETHODIMP
nsLocalFile::InitWithPath(const nsAString &filePath)
{
if (filePath.IsEmpty())
return InitWithNativePath(nsCString());
nsCAutoString tmp;
nsresult rv = NS_CopyUnicodeToNative(filePath, tmp);
if (NS_SUCCEEDED(rv))
return InitWithNativePath(tmp);
return rv;
}
NS_IMETHODIMP
nsLocalFile::Append(const nsAString &node)
{
if (node.IsEmpty())
return NS_OK;
nsCAutoString tmp;
nsresult rv = NS_CopyUnicodeToNative(node, tmp);
if (NS_SUCCEEDED(rv))
return AppendNative(tmp);
return rv;
}
NS_IMETHODIMP
nsLocalFile::AppendRelativePath(const nsAString &node)
{
if (node.IsEmpty())
return NS_OK;
nsCAutoString tmp;
nsresult rv = NS_CopyUnicodeToNative(node, tmp);
if (NS_SUCCEEDED(rv))
return AppendRelativeNativePath(tmp);
return rv;
}
NS_IMETHODIMP
nsLocalFile::GetLeafName(nsAString &aLeafName)
{
nsCAutoString tmp;
nsresult rv = GetNativeLeafName(tmp);
if (NS_SUCCEEDED(rv))
rv = NS_CopyNativeToUnicode(tmp, aLeafName);
return rv;
}
NS_IMETHODIMP
nsLocalFile::SetLeafName(const nsAString &aLeafName)
{
if (aLeafName.IsEmpty())
return SetNativeLeafName(nsCString());
nsCAutoString tmp;
nsresult rv = NS_CopyUnicodeToNative(aLeafName, tmp);
if (NS_SUCCEEDED(rv))
return SetNativeLeafName(tmp);
return rv;
}
NS_IMETHODIMP
nsLocalFile::GetPath(nsAString &_retval)
{
return NS_CopyNativeToUnicode(mWorkingPath, _retval);
}
NS_IMETHODIMP
nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
{
if (newName.IsEmpty())
return CopyToNative(newParentDir, nsCString());
nsCAutoString tmp;
nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
if (NS_SUCCEEDED(rv))
return CopyToNative(newParentDir, tmp);
return rv;
}
NS_IMETHODIMP
nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
{
if (newName.IsEmpty())
return CopyToFollowingLinksNative(newParentDir, nsCString());
nsCAutoString tmp;
nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
if (NS_SUCCEEDED(rv))
return CopyToFollowingLinksNative(newParentDir, tmp);
return rv;
}
NS_IMETHODIMP
nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
{
if (newName.IsEmpty())
return MoveToNative(newParentDir, nsCString());
nsCAutoString tmp;
nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
if (NS_SUCCEEDED(rv))
return MoveToNative(newParentDir, tmp);
return rv;
}
NS_IMETHODIMP
nsLocalFile::GetTarget(nsAString &_retval)
{
nsCAutoString tmp;
nsresult rv = GetNativeTarget(tmp);
if (NS_SUCCEEDED(rv))
rv = NS_CopyNativeToUnicode(tmp, _retval);
return rv;
}
nsresult
NS_NewLocalFile(const nsAString &path, PRBool followLinks, nsILocalFile* *result)
{
nsCAutoString buf;
nsresult rv = NS_CopyUnicodeToNative(path, buf);
if (NS_FAILED(rv)) {
*result = nsnull;
return rv;
}
return NS_NewNativeLocalFile(buf, followLinks, result);
}
//----------------------------------------------------------------------------
// global init/shutdown
//----------------------------------------------------------------------------
void
nsLocalFile::GlobalInit()
{
}
void
nsLocalFile::GlobalShutdown()
{
}