gecko-dev/xpcom/io/nsFileSpecOS2.cpp

475 строки
12 KiB
C++

/*
* 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 the Mozilla OS/2 libraries.
*
* The Initial Developer of the Original Code is John Fairhurst,
* <john_fairhurst@iname.com>. Portions created by John Fairhurst are
* Copyright (C) 1998 John Fairhurst. All Rights Reserved.
*
* Contributor(s):
*
*/
// This file is #include-d by nsFileSpec.cpp and contains OS/2 specific
// routines.
//
// Let me try & summarise what's going on here:
//
// nsFileSpec: "C:\foo\bar\b az.ext"
//
// nsFilePath: "/C|/foo/bar/b az.ext"
//
// nsFileURL: "file:///C|/foo/bar/b%20az.ext"
//
// We don't share the Windoze code 'cos it delves into the Win32 api.
// When things stabilize, we should push to merge some of the duplicated stuff.
//
#define INCL_DOSERRORS
#define INCL_DOS
#define INCL_WINWORKPLACE
#include <os2.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <ctype.h>
#include <io.h>
// General helper routines --------------------------------------------------
// Takes a "unix path" in the nsFilePath sense, full or relative, and returns
// the os/2 equivalent.
//
// OS/2 [==dos]-style paths are unaffected.
void nsFileSpecHelpers::UnixToNative( nsSimpleCharString &ioPath)
{
if( !ioPath.IsEmpty())
{
// Skip any leading slash
char *c = (char*) ioPath;
if( '/' == *c)
{
nsSimpleCharString tmp = c + 1;
ioPath = tmp;
}
// flip slashes
for( c = (char*)ioPath; *c; c++)
if( *c == '/') *c = '\\';
// do the drive portion
if( ioPath[1] == '|')
ioPath[1] = ':';
// yucky specialcase for drive roots: "H:" is not valid, has to be "H:\"
if( ioPath[2] == '\0')
{
char dl = ioPath[0];
ioPath = " :\\";
ioPath[0] = dl;
}
}
}
// Take a FQPN (os/2-style) and return a "unix path" in the nsFilePath sense
void nsFileSpecHelpers::NativeToUnix( nsSimpleCharString &ioPath)
{
if( !ioPath.IsEmpty())
{
// unix path is one character longer
nsSimpleCharString result( "/");
result += ioPath;
// flip slashes
for( char *c = result; *c; c++)
if( *c == '\\') *c = '/';
// colon to pipe
result[2] = '|';
ioPath = result;
}
}
// Canonify, make absolute, and check whether directories exist.
// The path given is a NATIVE path.
void nsFileSpecHelpers::Canonify( nsSimpleCharString &ioPath, PRBool inMakeDirs)
{
PRUint32 lenstr = ioPath.Length();
if( lenstr)
{
char &lastchar = ioPath[lenstr - 1];
// Strip off any trailing backslash UNLESS it's the backslash that
// comes after "X:". Note also that "\" is valid. Sheesh.
//
if( lastchar == '\\' && (lenstr != 3 || ioPath[1] != ':') && lenstr != 1)
lastchar = '\0';
// Canonify path: makes absolute and does the right thing with ".." etc.
char full_native[ CCHMAXPATH] = "";
DosQueryPathInfo( (char*) ioPath, FIL_QUERYFULLNAME,
full_native, CCHMAXPATH);
ioPath = full_native;
// if required, make directories.
if( inMakeDirs)
{
nsSimpleCharString unix_path( ioPath);
nsFileSpecHelpers::NativeToUnix( unix_path);
nsFileSpecHelpers::MakeAllDirectories( unix_path, 0700 /* hmm */);
}
}
}
// nsFileSpec <-> nsFilePath ------------------------------------------------
// We assume that input is valid. Garbage in, garbage out.
void nsFileSpec::operator = ( const nsFilePath &inPath)
{
mPath = (const char*) inPath;
nsFileSpecHelpers::UnixToNative( mPath);
}
void nsFilePath::operator = ( const nsFileSpec &inSpec)
{
mPath = inSpec.mPath;
nsFileSpecHelpers::NativeToUnix(mPath);
}
// nsFilePath constructor
nsFilePath::nsFilePath( const nsFileSpec &inSpec)
: mPath(0)
{
*this = inSpec;
}
// nsFileSpec implementation ------------------------------------------
nsFileSpec::nsFileSpec( const nsFilePath &inPath) : mPath(0)
{
*this = inPath;
}
void nsFileSpec::SetLeafName( const char *inLeafName)
{
mPath.LeafReplace( '\\', inLeafName);
}
char *nsFileSpec::GetLeafName() const
{
return mPath.GetLeaf( '\\');
}
PRBool nsFileSpec::Exists() const
{
struct stat st;
return 0 == stat( mPath, &st);
}
// These stat tests are done somewhat verbosely 'cos the VACPP version
// of sys/stat.h is sadly lacking in #defines.
PRBool nsFileSpec::IsFile() const
{
struct stat st;
return (0 == stat( mPath, &st)) && (S_IFREG == (st.st_mode & S_IFREG));
}
PRBool nsFileSpec::IsDirectory() const
{
struct stat st;
return (0 == stat( mPath, &st)) && (S_IFDIR == (st.st_mode & S_IFDIR));
}
// Really should factor out DosQPI() call to an internal GetFS3() method and then use
// here, in IsDirectory(), IsFile(), GetModDate(), GetFileSize() [and a future IsReadOnly()]
// and lose the clumsy stat() calls. Exists() too.
PRBool nsFileSpec::IsHidden() const
{
FILESTATUS3 fs3;
APIRET rc;
PRBool bHidden = PR_FALSE; // XXX how do I return an error?
rc = DosQueryPathInfo( mPath, FIL_STANDARD, &fs3, sizeof fs3);
if( !rc)
bHidden = fs3.attrFile & FILE_HIDDEN ? PR_TRUE : PR_FALSE;
return bHidden;
}
// On FAT or HPFS there's no such thing as a symlink; it's possible that JFS
// (new with Warp Server for e-business) does know what they are. Someone
// with a recent toolkit should check it out, but this will be OK for now.
PRBool nsFileSpec::IsSymlink() const
{
return PR_FALSE;
}
nsresult nsFileSpec::ResolveSymlink(PRBool& wasAliased)
{
return NS_OK;
}
void nsFileSpec::GetModDate( TimeStamp& outStamp) const
{
struct stat st;
if( stat( mPath, &st) == 0)
outStamp = st.st_mtime;
else
outStamp = 0;
}
PRUint32 nsFileSpec::GetFileSize() const
{
struct stat st;
PRUint32 size = 0;
if( stat( mPath, &st) == 0)
size = (PRUint32) st.st_size;
return size;
}
// Okay, this is a really weird place to put this method!
// And it ought to return a PRInt64.
//
PRInt64 nsFileSpec::GetDiskSpaceAvailable() const
{
ULONG ulDriveNo = toupper(mPath[0]) + 1 - 'A';
FSALLOCATE fsAllocate = { 0 };
APIRET rc = NO_ERROR;
PRUint32 cbAvail = UINT_MAX; // XXX copy windows...
rc = DosQueryFSInfo( ulDriveNo, FSIL_ALLOC,
&fsAllocate, sizeof fsAllocate);
if( NO_ERROR == rc)
{
// XXX check for overflows and do UINT_MAX if necessary
cbAvail = fsAllocate.cUnitAvail *
fsAllocate.cSectorUnit *
fsAllocate.cbSector;
}
PRInt64 int64;
LL_I2L(int64 , cbAvail);
return int64;
}
void nsFileSpec::GetParent( nsFileSpec &outSpec) const
{
outSpec.mPath = mPath;
char *slash = strrchr( (char *) outSpec.mPath, '\\'); // XXX wrong for dbcs
if( slash)
*slash = '\0';
}
void nsFileSpec::operator += ( const char *inRelativePath)
{
if( !inRelativePath || mPath.IsEmpty())
return;
if( mPath[mPath.Length() - 1] == '\\')
mPath += "x";
else
mPath +="\\x";
// If it's a (unix) relative path, make it native
nsSimpleCharString relPath = inRelativePath;
nsFileSpecHelpers::UnixToNative( relPath);
SetLeafName( relPath);
}
void nsFileSpec::CreateDirectory( int /*mode*/)
{
// Note that mPath is canonical.
// This means that all directories above us are meant to exist.
PR_MkDir( mPath, PR_CREATE_FILE);
}
void nsFileSpec::Delete( PRBool inRecursive) const
{
// Un-readonly ourselves
chmod( mPath, S_IREAD | S_IWRITE);
if( IsDirectory())
{
if( inRecursive)
{
for( nsDirectoryIterator i(*this, PR_FALSE); i.Exists(); i++)
{
nsFileSpec &child = (nsFileSpec &) i;
child.Delete( inRecursive);
}
}
PR_RmDir( mPath);
}
else
{
PR_Delete( mPath);
}
}
// XXX what format is this string? Who knows!
nsresult nsFileSpec::Rename( const char *inNewName)
{
nsresult rc = NS_FILE_FAILURE;
if( inNewName == nsnull)
rc = NS_ERROR_NULL_POINTER;
else if( !strchr( inNewName, '/') && !strchr( inNewName, '\\')) // XXX DBCS
{
// PR_Rename just calls DosMove() on what you give it.
char *aLocal = PL_strdup( mPath);
SetLeafName( inNewName);
APIRET ret = DosMove( aLocal, (const char*) mPath);
if( NO_ERROR == ret)
{
delete [] aLocal;
rc = NS_OK;
}
else
{
#ifdef DEBUG
printf( "DosMove %s %s returned %d\n", (char*)mPath, aLocal, ret);
#endif
mPath = aLocal;
}
}
return rc;
}
nsresult nsFileSpec::CopyToDir( const nsFileSpec &inParentDirectory) const
{
// Copy the file this filespec represents into the given directory.
nsresult rc = NS_FILE_FAILURE;
if( !IsDirectory() && inParentDirectory.IsDirectory())
{
char *myLeaf = GetLeafName();
nsSimpleCharString copyTo( inParentDirectory.GetCString());
copyTo += "\\";
copyTo += myLeaf;
delete [] myLeaf;
APIRET ret = DosCopy( (const char*) mPath, (const char*) copyTo, DCPY_EXISTING);
if( NO_ERROR == ret)
rc = NS_OK;
#ifdef DEBUG
else
printf( "DosCopy %s %s returned %dl\n",
(const char*) mPath, (const char*) copyTo, ret);
#endif
}
return rc;
}
// XXX not sure about the semantics of this method...
nsresult nsFileSpec::MoveToDir( const nsFileSpec &aParentDirectory)
{
// Copy first & then delete self to avoid drive-clashes
nsresult rc = CopyToDir( aParentDirectory);
if( NS_SUCCEEDED(rc))
{
Delete( PR_FALSE); // XXX why no return code ?
*this = aParentDirectory + GetLeafName();
}
return rc;
}
nsresult nsFileSpec::Execute( const char *inArgs) const
{
// Running arbitrary programs in OS/2 seems soooo hard: getting a single
// routine which deals correctly with PM, VIO, DOS & various Win 3.1
// types would be so nice.
//
// The method here looks quite elegant, but makes me shiver with
// unease: we create a new program object for the program and its
// parameters, get the shell to open it, and then destroy the object.
//
// I can't get either DosStartSession() or WinStartApp() to open
// all types of thing, including varieties of Win3.1 programs.
//
nsresult rc = NS_FILE_FAILURE;
char setupstring[ CCHMAXPATH * 2];
sprintf( setupstring, "EXENAME=%s;PARAMETERS=%s;CCVIEW=NO",
(const char *)mPath, inArgs);
HOBJECT hObject = WinCreateObject( "WPProgram", "Title", setupstring,
"<WP_NOWHERE>", CO_UPDATEIFEXISTS);
if( 0 != hObject)
{
// Doing this twice gives focus to the opened object.
// (no, this doesn't alter the object's settings - there's rather
// unexpected hackery in wpSetup)
WinSetObjectData( hObject, "OPEN=DEFAULT");
WinSetObjectData( hObject, "OPEN=DEFAULT");
WinDestroyObject( hObject);
rc = NS_OK;
}
return rc;
}
// nsDirectoryIterator ------------------------------------------------------
nsDirectoryIterator::nsDirectoryIterator( const nsFileSpec &aDirectory,
PRBool resolveSymlinks)
: mCurrent( aDirectory),
mDir( nsnull),
mExists(PR_FALSE),
mResoveSymLinks(resolveSymlinks)
{
mDir = PR_OpenDir( aDirectory);
mCurrent += "dummy";
++(*this);
}
nsDirectoryIterator::~nsDirectoryIterator()
{
if( mDir)
PR_CloseDir( mDir);
}
nsDirectoryIterator &nsDirectoryIterator::operator ++ ()
{
mExists = PR_FALSE;
if( !mDir)
return *this;
PRDirEntry *entry = PR_ReadDir( mDir, PR_SKIP_BOTH);
if( entry)
{
mExists = PR_TRUE;
mCurrent.SetLeafName( entry->name);
if (mResoveSymLinks)
{
PRBool ignore;
mCurrent.ResolveSymlink(ignore);
}
}
return *this;
}
nsDirectoryIterator& nsDirectoryIterator::operator -- ()
{
return ++(*this); // can't go backwards without much pain & suffering.
}