f3a1836cea
2008/01/28 09:53:07 rene 1.14.14.1: more gcc 4.3.0 things
1245 lines
41 KiB
C++
1245 lines
41 KiB
C++
/*************************************************************************
|
|
*
|
|
* OpenOffice.org - a multi-platform office productivity suite
|
|
*
|
|
* $RCSfile: util.cxx,v $
|
|
*
|
|
* $Revision: 1.15 $
|
|
*
|
|
* last change: $Author: ihi $ $Date: 2008-02-04 13:46:06 $
|
|
*
|
|
* The Contents of this file are made available subject to
|
|
* the terms of GNU Lesser General Public License Version 2.1.
|
|
*
|
|
*
|
|
* GNU Lesser General Public License Version 2.1
|
|
* =============================================
|
|
* Copyright 2005 by Sun Microsystems, Inc.
|
|
* 901 San Antonio Road, Palo Alto, CA 94303, USA
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License version 2.1, as published by the Free Software Foundation.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
|
* MA 02111-1307 USA
|
|
*
|
|
************************************************************************/
|
|
|
|
// MARKER(update_precomp.py): autogen include statement, do not remove
|
|
#include "precompiled_jvmfwk.hxx"
|
|
|
|
#include "util.hxx"
|
|
|
|
#include "osl/process.h"
|
|
#include "osl/security.hxx"
|
|
#include "osl/thread.hxx"
|
|
#include "osl/file.hxx"
|
|
#include "osl/module.hxx"
|
|
#include "rtl/byteseq.hxx"
|
|
#include "rtl/ustrbuf.hxx"
|
|
#include "rtl/instance.hxx"
|
|
#include "boost/scoped_array.hpp"
|
|
#include "com/sun/star/uno/Sequence.hxx"
|
|
#include <utility>
|
|
#include <algorithm>
|
|
#include <map>
|
|
|
|
#if defined WNT
|
|
#if defined _MSC_VER
|
|
#pragma warning(push, 1)
|
|
#endif
|
|
#include <windows.h>
|
|
#if defined _MSC_VER
|
|
#pragma warning(pop)
|
|
#endif
|
|
#endif
|
|
#include <string.h>
|
|
|
|
#include "sunjre.hxx"
|
|
#include "vendorlist.hxx"
|
|
#include "diagnostics.h"
|
|
using namespace rtl;
|
|
using namespace osl;
|
|
using namespace std;
|
|
|
|
#define OUSTR(x) ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(x) )
|
|
#ifdef WNT
|
|
#define HKEY_SUN_JRE L"Software\\JavaSoft\\Java Runtime Environment"
|
|
#define HKEY_SUN_SDK L"Software\\JavaSoft\\Java Development Kit"
|
|
#endif
|
|
|
|
#ifdef UNX
|
|
namespace {
|
|
char const *g_arJavaNames[] = {
|
|
"",
|
|
"j2re",
|
|
"j2se",
|
|
"j2sdk",
|
|
"jdk",
|
|
"jre",
|
|
"java",
|
|
"Home",
|
|
"IBMJava2-ppc-142"
|
|
};
|
|
/* These are directory names which could contain multiple java installations.
|
|
*/
|
|
char const *g_arCollectDirs[] = {
|
|
"",
|
|
"j2re/",
|
|
"j2se/",
|
|
"j2sdk/",
|
|
"jdk/",
|
|
"jre/",
|
|
"java/",
|
|
"jvm/"
|
|
};
|
|
|
|
/* These are directories in which a java installation is
|
|
looked for.
|
|
*/
|
|
char const *g_arSearchPaths[] = {
|
|
#ifdef MACOSX
|
|
"",
|
|
"System/Library/Frameworks/JavaVM.framework/Versions/1.4.2/"
|
|
#else
|
|
"",
|
|
"usr/",
|
|
"usr/local/",
|
|
"usr/local/IBMJava2-ppc-142",
|
|
"usr/local/j2sdk1.3.1",
|
|
#ifdef X86_64
|
|
"usr/lib64/",
|
|
#endif
|
|
"usr/lib/",
|
|
"usr/bin/"
|
|
#endif
|
|
};
|
|
}
|
|
#endif // UNX
|
|
|
|
namespace jfw_plugin
|
|
{
|
|
extern VendorSupportMapEntry gVendorMap[];
|
|
|
|
bool getSDKInfoFromRegistry(vector<OUString> & vecHome);
|
|
bool getJREInfoFromRegistry(vector<OUString>& vecJavaHome);
|
|
rtl::OUString decodeOutput(const rtl::OString& s);
|
|
|
|
|
|
|
|
namespace
|
|
{
|
|
rtl::OUString getLibraryLocation()
|
|
{
|
|
rtl::OUString libraryFileUrl;
|
|
OSL_VERIFY(osl::Module::getUrlFromAddress((void *)(sal_IntPtr)getLibraryLocation, libraryFileUrl));
|
|
return getDirFromFile(libraryFileUrl);
|
|
}
|
|
|
|
struct InitBootstrap
|
|
{
|
|
rtl::Bootstrap * operator()(const OUString& sIni)
|
|
{
|
|
static rtl::Bootstrap aInstance(sIni);
|
|
return & aInstance;
|
|
|
|
}
|
|
};
|
|
|
|
struct InitBootstrapData
|
|
{
|
|
OUString const & operator()()
|
|
{
|
|
// osl::Guard<osl::Mutex> g(osl::GetGlobalMutex());
|
|
static OUString sIni;
|
|
rtl::OUStringBuffer buf( 255);
|
|
buf.append( getLibraryLocation());
|
|
buf.appendAscii( SAL_CONFIGFILE("/sunjavaplugin") );
|
|
sIni = buf.makeStringAndClear();
|
|
JFW_TRACE2(OUSTR("[Java framework] sunjavaplugin: "
|
|
"Using configuration file \n") + sIni);
|
|
return sIni;
|
|
}
|
|
};
|
|
}
|
|
|
|
rtl::Bootstrap * getBootstrap()
|
|
{
|
|
return rtl_Instance< rtl::Bootstrap, InitBootstrap,
|
|
::osl::MutexGuard, ::osl::GetGlobalMutex,
|
|
OUString, InitBootstrapData >::create(
|
|
InitBootstrap(), ::osl::GetGlobalMutex(), InitBootstrapData());
|
|
}
|
|
|
|
|
|
|
|
|
|
class FileHandleGuard
|
|
{
|
|
public:
|
|
inline FileHandleGuard(oslFileHandle & rHandle) SAL_THROW(()):
|
|
m_rHandle(rHandle) {}
|
|
|
|
inline ~FileHandleGuard() SAL_THROW(());
|
|
|
|
inline oslFileHandle & getHandle() SAL_THROW(()) { return m_rHandle; }
|
|
|
|
private:
|
|
oslFileHandle & m_rHandle;
|
|
|
|
FileHandleGuard(FileHandleGuard &); // not implemented
|
|
void operator =(FileHandleGuard); // not implemented
|
|
};
|
|
|
|
inline FileHandleGuard::~FileHandleGuard() SAL_THROW(())
|
|
{
|
|
if (m_rHandle != 0)
|
|
{
|
|
if (osl_closeFile(m_rHandle) != osl_File_E_None)
|
|
{
|
|
OSL_ENSURE(false, "unexpected situation");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
class FileHandleReader
|
|
{
|
|
public:
|
|
enum Result
|
|
{
|
|
RESULT_OK,
|
|
RESULT_EOF,
|
|
RESULT_ERROR
|
|
};
|
|
|
|
inline FileHandleReader(oslFileHandle & rHandle) SAL_THROW(()):
|
|
m_aGuard(rHandle), m_nSize(0), m_nIndex(0), m_bLf(false) {}
|
|
|
|
Result readLine(rtl::OString * pLine) SAL_THROW(());
|
|
|
|
private:
|
|
enum { BUFFER_SIZE = 1024 };
|
|
|
|
sal_Char m_aBuffer[BUFFER_SIZE];
|
|
FileHandleGuard m_aGuard;
|
|
int m_nSize;
|
|
int m_nIndex;
|
|
bool m_bLf;
|
|
};
|
|
|
|
FileHandleReader::Result
|
|
FileHandleReader::readLine(rtl::OString * pLine)
|
|
SAL_THROW(())
|
|
{
|
|
OSL_ENSURE(pLine, "specification violation");
|
|
|
|
for (bool bEof = true;; bEof = false)
|
|
{
|
|
if (m_nIndex == m_nSize)
|
|
{
|
|
sal_uInt64 nRead;
|
|
switch (osl_readFile(
|
|
m_aGuard.getHandle(), m_aBuffer, BUFFER_SIZE, &nRead))
|
|
{
|
|
case osl_File_E_PIPE: //HACK! for windows
|
|
nRead = 0;
|
|
case osl_File_E_None:
|
|
if (nRead == 0)
|
|
{
|
|
m_bLf = false;
|
|
return bEof ? RESULT_EOF : RESULT_OK;
|
|
}
|
|
m_nIndex = 0;
|
|
m_nSize = static_cast< int >(nRead);
|
|
break;
|
|
case osl_File_E_INTR:
|
|
continue;
|
|
|
|
default:
|
|
return RESULT_ERROR;
|
|
}
|
|
}
|
|
|
|
if (m_bLf && m_aBuffer[m_nIndex] == 0x0A)
|
|
++m_nIndex;
|
|
m_bLf = false;
|
|
|
|
int nStart = m_nIndex;
|
|
while (m_nIndex != m_nSize)
|
|
switch (m_aBuffer[m_nIndex++])
|
|
{
|
|
case 0x0D:
|
|
m_bLf = true;
|
|
case 0x0A:
|
|
*pLine += rtl::OString(m_aBuffer + nStart,
|
|
m_nIndex - 1 - nStart);
|
|
//TODO! check for overflow, and not very efficient
|
|
return RESULT_OK;
|
|
}
|
|
|
|
*pLine += rtl::OString(m_aBuffer + nStart, m_nIndex - nStart);
|
|
//TODO! check for overflow, and not very efficient
|
|
}
|
|
}
|
|
|
|
class AsynchReader: public Thread
|
|
{
|
|
size_t m_nDataSize;
|
|
boost::scoped_array<sal_Char> m_arData;
|
|
|
|
bool m_bError;
|
|
bool m_bDone;
|
|
FileHandleGuard m_aGuard;
|
|
|
|
void SAL_CALL run();
|
|
public:
|
|
|
|
AsynchReader(oslFileHandle & rHandle);
|
|
|
|
/** only call this function after this thread has finished.
|
|
|
|
That is, call join on this instance and then call getData.
|
|
|
|
*/
|
|
OString getData();
|
|
};
|
|
|
|
AsynchReader::AsynchReader(oslFileHandle & rHandle):
|
|
m_nDataSize(0), m_bError(false), m_bDone(false), m_aGuard(rHandle)
|
|
{
|
|
}
|
|
|
|
OString AsynchReader::getData()
|
|
{
|
|
OSL_ASSERT(isRunning() == sal_False );
|
|
return OString(m_arData.get(), m_nDataSize);
|
|
}
|
|
|
|
void AsynchReader::run()
|
|
{
|
|
const sal_uInt64 BUFFER_SIZE = 4096;
|
|
sal_Char aBuffer[BUFFER_SIZE];
|
|
while (true)
|
|
{
|
|
sal_uInt64 nRead;
|
|
//the function blocks until something could be read or the pipe closed.
|
|
switch (osl_readFile(
|
|
m_aGuard.getHandle(), aBuffer, BUFFER_SIZE, &nRead))
|
|
{
|
|
case osl_File_E_PIPE: //HACK! for windows
|
|
nRead = 0;
|
|
case osl_File_E_None:
|
|
break;
|
|
default:
|
|
m_bError = true;
|
|
return;
|
|
}
|
|
|
|
if (nRead == 0)
|
|
{
|
|
m_bDone = true;
|
|
break;
|
|
}
|
|
else if (nRead <= BUFFER_SIZE)
|
|
{
|
|
//Save the data we have in m_arData into a temporary array
|
|
boost::scoped_array<sal_Char> arTmp( new sal_Char[m_nDataSize]);
|
|
memcpy(arTmp.get(), m_arData.get(), m_nDataSize);
|
|
//Enlarge m_arData to hold the newly read data
|
|
m_arData.reset(new sal_Char[(size_t)(m_nDataSize + nRead)]);
|
|
//Copy back the data that was already in m_arData
|
|
memcpy(m_arData.get(), arTmp.get(), m_nDataSize);
|
|
//Add the newly read data to m_arData
|
|
memcpy(m_arData.get() + m_nDataSize, aBuffer, (size_t) nRead);
|
|
m_nDataSize += (size_t) nRead;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool getJavaProps(const OUString & exePath,
|
|
std::vector<std::pair<rtl::OUString, rtl::OUString> >& props,
|
|
bool * bProcessRun)
|
|
{
|
|
bool ret = false;
|
|
|
|
OSL_ASSERT( exePath.getLength() > 0);
|
|
OUString usStartDir;
|
|
//We need to set the CLASSPATH in case the office is started from
|
|
//a different directory. The JREProperties.class is expected to reside
|
|
//next to the plugin.
|
|
rtl::OUString sThisLib;
|
|
if (osl_getModuleURLFromAddress((void *) (sal_IntPtr)& getJavaProps,
|
|
& sThisLib.pData) == sal_False)
|
|
return false;
|
|
sThisLib = getDirFromFile(sThisLib);
|
|
OUString sClassPath;
|
|
if (osl_getSystemPathFromFileURL(sThisLib.pData, & sClassPath.pData)
|
|
!= osl_File_E_None)
|
|
return false;
|
|
|
|
//check if we shall examine a Java for accessibility support
|
|
//If the bootstrap variable is "1" then we pass the argument
|
|
//"noaccessibility" to JREProperties.class. This will prevent
|
|
//that it calls java.awt.Toolkit.getDefaultToolkit();
|
|
OUString sValue;
|
|
getBootstrap()->getFrom(OUSTR("JFW_PLUGIN_DO_NOT_CHECK_ACCESSIBILITY"), sValue);
|
|
|
|
//prepare the arguments
|
|
sal_Int32 cArgs = 3;
|
|
OUString arg1 = OUString(RTL_CONSTASCII_USTRINGPARAM("-classpath"));// + sClassPath;
|
|
OUString arg2 = sClassPath;
|
|
OUString arg3(RTL_CONSTASCII_USTRINGPARAM("JREProperties"));
|
|
OUString arg4 = OUSTR("noaccessibility");
|
|
rtl_uString *args[4] = {arg1.pData, arg2.pData, arg3.pData};
|
|
|
|
// Only add the fourth param if the bootstrap parameter is set.
|
|
if (sValue.equals(OUString::valueOf((sal_Int32) 1)))
|
|
{
|
|
args[3] = arg4.pData;
|
|
cArgs = 4;
|
|
}
|
|
|
|
oslProcess javaProcess= 0;
|
|
oslFileHandle fileOut= 0;
|
|
oslFileHandle fileErr= 0;
|
|
|
|
FileHandleReader stdoutReader(fileOut);
|
|
AsynchReader stderrReader(fileErr);
|
|
|
|
JFW_TRACE2(OUSTR("\n[Java framework] Executing: ") + exePath + OUSTR(".\n"));
|
|
oslProcessError procErr =
|
|
osl_executeProcess_WithRedirectedIO( exePath.pData,//usExe.pData,
|
|
args,
|
|
cArgs, //sal_uInt32 nArguments,
|
|
osl_Process_HIDDEN, //oslProcessOption Options,
|
|
NULL, //oslSecurity Security,
|
|
usStartDir.pData,//usStartDir.pData,//usWorkDir.pData, //rtl_uString *strWorkDir,
|
|
NULL, //rtl_uString *strEnvironment[],
|
|
0, // sal_uInt32 nEnvironmentVars,
|
|
&javaProcess, //oslProcess *pProcess,
|
|
NULL,//oslFileHandle *pChildInputWrite,
|
|
&fileOut,//oslFileHandle *pChildOutputRead,
|
|
&fileErr);//oslFileHandle *pChildErrorRead);
|
|
|
|
if( procErr != osl_Process_E_None)
|
|
{
|
|
JFW_TRACE2("[Java framework] Execution failed. \n");
|
|
*bProcessRun = false;
|
|
return ret;
|
|
}
|
|
else
|
|
{
|
|
JFW_TRACE2("[Java framework] Java executed successfully.\n");
|
|
*bProcessRun = true;
|
|
}
|
|
|
|
//Start asynchronous reading (different thread) of error stream
|
|
stderrReader.create();
|
|
|
|
//Use this thread to read output stream
|
|
FileHandleReader::Result rs = FileHandleReader::RESULT_OK;
|
|
while (1)
|
|
{
|
|
OString aLine;
|
|
rs = stdoutReader.readLine( & aLine);
|
|
if (rs != FileHandleReader::RESULT_OK)
|
|
break;
|
|
OUString sLine = decodeOutput(aLine);
|
|
sLine = sLine.trim();
|
|
if (sLine.getLength() == 0)
|
|
continue;
|
|
//The JREProperties class writes key value pairs, separated by '='
|
|
sal_Int32 index = sLine.indexOf('=', 0);
|
|
OSL_ASSERT(index != -1);
|
|
OUString sKey = sLine.copy(0, index);
|
|
OUString sVal = sLine.copy(index + 1);
|
|
|
|
props.push_back(std::make_pair(sKey, sVal));
|
|
}
|
|
|
|
if (rs != FileHandleReader::RESULT_ERROR && props.size()>0)
|
|
ret = true;
|
|
|
|
//process error stream data
|
|
stderrReader.join();
|
|
JFW_TRACE2(OString("[Java framework] Java wrote to stderr:\" ")
|
|
+ stderrReader.getData() + OString(" \".\n"));
|
|
|
|
TimeValue waitMax= {5 ,0};
|
|
procErr = osl_joinProcessWithTimeout(javaProcess, &waitMax);
|
|
OSL_ASSERT(procErr == osl_Process_E_None);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* converts the properties printed by JREProperties.class into
|
|
readable strings. The strings are encoded as integer values separated
|
|
by spaces.
|
|
*/
|
|
rtl::OUString decodeOutput(const rtl::OString& s)
|
|
{
|
|
OUString sEncoded = OStringToOUString(s, RTL_TEXTENCODING_ASCII_US);
|
|
OUStringBuffer buff(512);
|
|
sal_Int32 nIndex = 0;
|
|
do
|
|
{
|
|
OUString aToken = sEncoded.getToken( 0, ' ', nIndex );
|
|
if (aToken.getLength())
|
|
{
|
|
sal_Unicode value = (sal_Unicode) aToken.toInt32();
|
|
buff.append(value);
|
|
}
|
|
} while (nIndex >= 0);
|
|
return buff.makeStringAndClear();
|
|
}
|
|
|
|
|
|
#if defined WNT
|
|
void createJavaInfoFromWinReg(std::vector<rtl::Reference<VendorBase> > & vecInfos)
|
|
{
|
|
// Get Java s from registry
|
|
std::vector<OUString> vecJavaHome;
|
|
if(getSDKInfoFromRegistry(vecJavaHome))
|
|
{
|
|
// create impl objects
|
|
typedef std::vector<OUString>::iterator ItHome;
|
|
for(ItHome it_home= vecJavaHome.begin(); it_home != vecJavaHome.end();
|
|
it_home++)
|
|
{
|
|
getJREInfoByPath(*it_home, vecInfos);
|
|
}
|
|
}
|
|
|
|
vecJavaHome.clear();
|
|
if(getJREInfoFromRegistry(vecJavaHome))
|
|
{
|
|
typedef std::vector<OUString>::iterator ItHome;
|
|
for(ItHome it_home= vecJavaHome.begin(); it_home != vecJavaHome.end();
|
|
it_home++)
|
|
{
|
|
getJREInfoByPath(*it_home, vecInfos);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool getJavaInfoFromRegistry(const wchar_t* szRegKey,
|
|
vector<OUString>& vecJavaHome)
|
|
{
|
|
HKEY hRoot;
|
|
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_ENUMERATE_SUB_KEYS, &hRoot)
|
|
== ERROR_SUCCESS)
|
|
{
|
|
DWORD dwIndex = 0;
|
|
const DWORD BUFFSIZE = 1024;
|
|
wchar_t bufVersion[BUFFSIZE];
|
|
// char bufVersion[BUFFSIZE];
|
|
DWORD nNameLen = BUFFSIZE;
|
|
FILETIME fileTime;
|
|
nNameLen = sizeof(bufVersion);
|
|
|
|
// Iterate over all subkeys of HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment
|
|
while (RegEnumKeyExW(hRoot, dwIndex, bufVersion, &nNameLen, NULL, NULL, NULL, &fileTime) != ERROR_NO_MORE_ITEMS)
|
|
{
|
|
HKEY hKey;
|
|
// Open a Java Runtime Environment sub key, e.g. "1.4.0"
|
|
if (RegOpenKeyExW(hRoot, bufVersion, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
|
|
{
|
|
DWORD dwType;
|
|
DWORD dwTmpPathLen= 0;
|
|
// Get the path to the JavaHome every JRE entry
|
|
// Find out how long the string for JavaHome is and allocate memory to hold the path
|
|
if( RegQueryValueExW(hKey, L"JavaHome", 0, &dwType, NULL, &dwTmpPathLen)== ERROR_SUCCESS)
|
|
{
|
|
char* szTmpPath= (char *) malloc( dwTmpPathLen);
|
|
// Get the path for the runtime lib
|
|
if(RegQueryValueExW(hKey, L"JavaHome", 0, &dwType, (unsigned char*) szTmpPath, &dwTmpPathLen) == ERROR_SUCCESS)
|
|
{
|
|
// There can be several version entries refering with the same JavaHome,e.g 1.4 and 1.4.1
|
|
OUString usHome((sal_Unicode*) szTmpPath);
|
|
// check if there is already an entry with the same JavaHomeruntime lib
|
|
// if so, we use the one with the more accurate version
|
|
bool bAppend= true;
|
|
OUString usHomeUrl;
|
|
if (osl_getFileURLFromSystemPath(usHome.pData, & usHomeUrl.pData) ==
|
|
osl_File_E_None)
|
|
{
|
|
//iterate over the vector with java home strings
|
|
typedef vector<OUString>::iterator ItHome;
|
|
for(ItHome itHome= vecJavaHome.begin();
|
|
itHome != vecJavaHome.end(); itHome++)
|
|
{
|
|
if(usHomeUrl.equals(*itHome))
|
|
{
|
|
bAppend= false;
|
|
break;
|
|
}
|
|
}
|
|
// Save the home dir
|
|
if(bAppend)
|
|
{
|
|
vecJavaHome.push_back(usHomeUrl);
|
|
}
|
|
}
|
|
}
|
|
free( szTmpPath);
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
dwIndex ++;
|
|
nNameLen = BUFFSIZE;
|
|
}
|
|
RegCloseKey(hRoot);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
bool getSDKInfoFromRegistry(vector<OUString> & vecHome)
|
|
{
|
|
return getJavaInfoFromRegistry(HKEY_SUN_SDK, vecHome);
|
|
}
|
|
|
|
bool getJREInfoFromRegistry(vector<OUString>& vecJavaHome)
|
|
{
|
|
return getJavaInfoFromRegistry(HKEY_SUN_JRE, vecJavaHome);
|
|
}
|
|
|
|
#endif // WNT
|
|
|
|
void bubbleSortVersion(vector<rtl::Reference<VendorBase> >& vec)
|
|
{
|
|
if(vec.size() == 0)
|
|
return;
|
|
int size= vec.size() - 1;
|
|
int cIter= 0;
|
|
// sort for version
|
|
for(int i= 0; i < size; i++)
|
|
{
|
|
for(int j= size; j > 0 + cIter; j--)
|
|
{
|
|
rtl::Reference<VendorBase>& cur= vec.at(j);
|
|
rtl::Reference<VendorBase>& next= vec.at(j-1);
|
|
|
|
int nCmp = 0;
|
|
// comparing invalid SunVersion s is possible, they will be less than a
|
|
// valid version
|
|
|
|
//check if version of current is recognized, by comparing it with itself
|
|
try
|
|
{
|
|
cur->compareVersions(cur->getVersion());
|
|
}
|
|
catch (MalformedVersionException &)
|
|
{
|
|
nCmp = -1; // current < next
|
|
}
|
|
//The version of cur is valid, now compare with the second version
|
|
if (nCmp == 0)
|
|
{
|
|
try
|
|
{
|
|
nCmp = cur->compareVersions(next->getVersion());
|
|
}
|
|
catch (MalformedVersionException & )
|
|
{
|
|
//The second version is invalid, therefor it is regardes less.
|
|
nCmp = 1;
|
|
}
|
|
}
|
|
if(nCmp == 1) // cur > next
|
|
{
|
|
rtl::Reference<VendorBase> less = next;
|
|
vec.at(j-1)= cur;
|
|
vec.at(j)= less;
|
|
}
|
|
}
|
|
cIter++;
|
|
}
|
|
}
|
|
|
|
|
|
bool getJREInfoFromBinPath(
|
|
const rtl::OUString& path, vector<rtl::Reference<VendorBase> > & vecInfos)
|
|
{
|
|
// file:///c:/jre/bin
|
|
//map: jre/bin/java.exe
|
|
bool ret = false;
|
|
vector<pair<OUString, OUString> > props;
|
|
|
|
for ( sal_Int32 pos = 0;
|
|
gVendorMap[pos].sVendorName != NULL; ++pos )
|
|
{
|
|
vector<OUString> vecPaths;
|
|
getJavaExePaths_func pFunc = gVendorMap[pos].getJavaFunc;
|
|
|
|
int size = 0;
|
|
char const* const* arExePaths = (*pFunc)(&size);
|
|
vecPaths = getVectorFromCharArray(arExePaths, size);
|
|
|
|
//make sure argument path does not end with '/'
|
|
OUString sBinPath = path;
|
|
if (path.lastIndexOf('/') == (path.getLength() - 1))
|
|
sBinPath = path.copy(0, path.getLength() - 1);
|
|
|
|
typedef vector<OUString>::const_iterator c_it;
|
|
for (c_it i = vecPaths.begin(); i != vecPaths.end(); i++)
|
|
{
|
|
//the map contains e.g. jre/bin/java.exe
|
|
//get the directory where the executable is contained
|
|
OUString sHome;
|
|
sal_Int32 index = i->lastIndexOf('/');
|
|
if (index == -1)
|
|
{
|
|
//map contained only : "java.exe, then the argument
|
|
//path is already the home directory
|
|
sHome = sBinPath;
|
|
}
|
|
else
|
|
{
|
|
// jre/bin/jre -> jre/bin
|
|
OUString sMapPath(i->getStr(), index);
|
|
index = sBinPath.lastIndexOf(sMapPath);
|
|
if (index != -1
|
|
&& (index + sMapPath.getLength() == sBinPath.getLength())
|
|
&& sBinPath[index - 1] == '/')
|
|
{
|
|
sHome = OUString(sBinPath.getStr(), index - 1);
|
|
}
|
|
}
|
|
if (sHome.getLength() > 0)
|
|
{
|
|
ret = getJREInfoByPath(sHome, vecInfos);
|
|
if (ret)
|
|
break;
|
|
}
|
|
}
|
|
if (ret)
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
vector<Reference<VendorBase> > getAllJREInfos()
|
|
{
|
|
vector<Reference<VendorBase> > vecInfos;
|
|
|
|
#if defined WNT
|
|
// Get Javas from the registry
|
|
createJavaInfoFromWinReg(vecInfos);
|
|
#endif // WNT
|
|
|
|
createJavaInfoFromJavaHome(vecInfos);
|
|
//this function should be called after createJavaInfoDirScan.
|
|
//Otherwise in SDKs Java may be started twice
|
|
createJavaInfoFromPath(vecInfos);
|
|
|
|
#ifdef UNX
|
|
createJavaInfoDirScan(vecInfos);
|
|
#endif
|
|
bubbleSortVersion(vecInfos);
|
|
return vecInfos;
|
|
}
|
|
|
|
|
|
vector<OUString> getVectorFromCharArray(char const * const * ar, int size)
|
|
{
|
|
vector<OUString> vec;
|
|
for( int i = 0; i < size; i++)
|
|
{
|
|
OUString s(ar[i], strlen(ar[i]), RTL_TEXTENCODING_UTF8);
|
|
vec.push_back(s);
|
|
}
|
|
return vec;
|
|
}
|
|
bool getJREInfoByPath(const rtl::OUString& path,
|
|
std::vector<rtl::Reference<VendorBase> > & vecInfos)
|
|
{
|
|
bool ret = false;
|
|
|
|
rtl::Reference<VendorBase> aInfo = getJREInfoByPath(path);
|
|
if (aInfo.is())
|
|
{
|
|
ret = true;
|
|
vector<rtl::Reference<VendorBase> >::const_iterator it_impl= std::find_if(
|
|
vecInfos.begin(),vecInfos.end(), InfoFindSame(aInfo->getHome()));
|
|
if(it_impl == vecInfos.end())
|
|
{
|
|
vecInfos.push_back(aInfo);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/** Checks if the path is a directory. Links are resolved.
|
|
In case of an error the returned string has the length 0.
|
|
Otherwise the returned string is the "resolved" file URL.
|
|
*/
|
|
OUString resolveDirPath(const OUString & path)
|
|
{
|
|
OUString ret;
|
|
OUString sResolved;
|
|
if (File::getAbsoluteFileURL(
|
|
rtl::OUString(), path, sResolved) != File::E_None)
|
|
return OUString();
|
|
|
|
//check if this is a valid path and if it is a directory
|
|
DirectoryItem item;
|
|
if (DirectoryItem::get(sResolved, item) == File::E_None)
|
|
{
|
|
FileStatus status(FileStatusMask_Type |
|
|
FileStatusMask_LinkTargetURL |
|
|
FileStatusMask_FileURL);
|
|
|
|
if (item.getFileStatus(status) == File::E_None
|
|
&& status.getFileType() == FileStatus::Directory)
|
|
{
|
|
ret = sResolved;
|
|
}
|
|
}
|
|
else
|
|
return OUString();
|
|
return ret;
|
|
}
|
|
/** Checks if the path is a file. If it is a link to a file than
|
|
it is resolved.
|
|
*/
|
|
OUString resolveFilePath(const OUString & path)
|
|
{
|
|
OUString ret;
|
|
OUString sResolved;
|
|
|
|
if (File::getAbsoluteFileURL(
|
|
rtl::OUString(), path, sResolved) != File::E_None)
|
|
return OUString();
|
|
|
|
//check if this is a valid path to a file or and if it is a link
|
|
DirectoryItem item;
|
|
if (DirectoryItem::get(sResolved, item) == File::E_None)
|
|
{
|
|
FileStatus status(FileStatusMask_Type |
|
|
FileStatusMask_LinkTargetURL |
|
|
FileStatusMask_FileURL);
|
|
if (item.getFileStatus(status) == File::E_None
|
|
&& status.getFileType() == FileStatus::Regular)
|
|
{
|
|
ret = sResolved;
|
|
}
|
|
}
|
|
else
|
|
return OUString();
|
|
|
|
return ret;
|
|
}
|
|
|
|
rtl::Reference<VendorBase> getJREInfoByPath(
|
|
const OUString& path)
|
|
{
|
|
rtl::Reference<VendorBase> ret;
|
|
static vector<OUString> vecBadPaths;
|
|
|
|
static map<OUString, rtl::Reference<VendorBase> > mapJREs;
|
|
typedef map<OUString, rtl::Reference<VendorBase> >::const_iterator MapIt;
|
|
typedef map<OUString, rtl::Reference<VendorBase> > MAPJRE;
|
|
OUString sFilePath;
|
|
typedef vector<OUString>::const_iterator cit_path;
|
|
vector<pair<OUString, OUString> > props;
|
|
|
|
OUString sResolvedDir = resolveDirPath(path);
|
|
// If this path is invalid then there is no chance to find a JRE here
|
|
if (sResolvedDir.getLength() == 0)
|
|
return 0;
|
|
|
|
//check if the directory path is good, that is a JRE was already recognized.
|
|
//Then we need not detect it again
|
|
//For example, a sun JKD contains <jdk>/bin/java and <jdk>/jre/bin/java.
|
|
//When <jdk>/bin/java has been found then we need not find <jdk>/jre/bin/java.
|
|
//Otherwise we would execute java two times for evers JDK found.
|
|
MapIt entry2 = find_if(mapJREs.begin(), mapJREs.end(),
|
|
SameOrSubDirJREMap(sResolvedDir));
|
|
if (entry2 != mapJREs.end())
|
|
{
|
|
JFW_TRACE2(OUSTR("[Java framework] sunjavaplugin"SAL_DLLEXTENSION ": ")
|
|
+ OUSTR("JRE found again (detected before): ") + sResolvedDir
|
|
+ OUSTR(".\n"));
|
|
return entry2->second;
|
|
}
|
|
|
|
for ( sal_Int32 pos = 0;
|
|
gVendorMap[pos].sVendorName != NULL; ++pos )
|
|
{
|
|
vector<OUString> vecPaths;
|
|
getJavaExePaths_func pFunc = gVendorMap[pos].getJavaFunc;
|
|
|
|
int size = 0;
|
|
char const* const* arExePaths = (*pFunc)(&size);
|
|
vecPaths = getVectorFromCharArray(arExePaths, size);
|
|
|
|
bool bBreak = false;
|
|
typedef vector<OUString>::const_iterator c_it;
|
|
for (c_it i = vecPaths.begin(); i != vecPaths.end(); i++)
|
|
{
|
|
//if the path is a link, then resolve it
|
|
//check if the executable exists at all
|
|
|
|
//path can be only "file:///". Then do not append a '/'
|
|
//sizeof counts the terminating 0
|
|
OUString sFullPath;
|
|
if (path.getLength() == sizeof("file:///") - 1)
|
|
sFullPath = sResolvedDir + (*i);
|
|
else
|
|
sFullPath = sResolvedDir +
|
|
OUString(RTL_CONSTASCII_USTRINGPARAM("/")) + (*i);
|
|
|
|
|
|
sFilePath = resolveFilePath(sFullPath);
|
|
|
|
if (sFilePath.getLength() == 0)
|
|
{
|
|
//The file path (to java exe) is not valid
|
|
cit_path ifull = find(vecBadPaths.begin(), vecBadPaths.end(), sFullPath);
|
|
if (ifull == vecBadPaths.end())
|
|
vecBadPaths.push_back(sFullPath);
|
|
continue;
|
|
}
|
|
|
|
cit_path ifile = find(vecBadPaths.begin(), vecBadPaths.end(), sFilePath);
|
|
if (ifile != vecBadPaths.end())
|
|
continue;
|
|
|
|
MapIt entry = mapJREs.find(sFilePath);
|
|
if (entry != mapJREs.end())
|
|
{
|
|
JFW_TRACE2(OUSTR("[Java framework] sunjavaplugin"SAL_DLLEXTENSION ": ")
|
|
+ OUSTR("JRE found again (detected before): ") + sFilePath
|
|
+ OUSTR(".\n"));
|
|
|
|
return entry->second;
|
|
}
|
|
|
|
bool bProcessRun= false;
|
|
if (getJavaProps(sFilePath, props, & bProcessRun) == false)
|
|
{
|
|
//The java executable could not be run or the system properties
|
|
//could not be retrieved. We can assume that this java is corrupt.
|
|
vecBadPaths.push_back(sFilePath);
|
|
//If there was a java executable, that could be run but we did not get
|
|
//the system properties, then we also assume that the whole Java installation
|
|
//does not work. In a jdk there are two executables. One in jdk/bin and the other
|
|
//in jdk/jre/bin. We do not search any further, because we assume that if one java
|
|
//does not work then the other does not work as well. This saves us to run java
|
|
//again which is quite costly.
|
|
if (bProcessRun == true)
|
|
{
|
|
// 1.3.1 special treatment: jdk/bin/java and /jdk/jre/bin/java are links to
|
|
//a script, named .java_wrapper. The script starts jdk/bin/sparc/native_threads/java
|
|
//or jdk/jre/bin/sparc/native_threads/java. The script uses the name with which it was
|
|
//invoked to build the path to the executable. It we start the script directy as .java_wrapper
|
|
//then it tries to start a jdk/.../native_threads/.java_wrapper. Therefore the link, which
|
|
//is named java, must be used to start the script.
|
|
getJavaProps(sFullPath, props, & bProcessRun);
|
|
// Either we found a working 1.3.1
|
|
//Or the java is broken. In both cases we stop searchin under this "root" directory
|
|
bBreak = true;
|
|
break;
|
|
}
|
|
//sFilePath is no working java executable. We continue with another possible
|
|
//path.
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
//sFilePath is a java and we could get the system properties. We proceed with this
|
|
//java.
|
|
else
|
|
{
|
|
bBreak = true;
|
|
break;
|
|
}
|
|
}
|
|
if (bBreak)
|
|
break;
|
|
}
|
|
|
|
if (props.size() == 0)
|
|
return rtl::Reference<VendorBase>();
|
|
|
|
//find java.vendor property
|
|
typedef vector<pair<OUString, OUString> >::const_iterator c_ip;
|
|
OUString sVendor(RTL_CONSTASCII_USTRINGPARAM("java.vendor"));
|
|
OUString sVendorName;
|
|
|
|
for (c_ip i = props.begin(); i != props.end(); i++)
|
|
{
|
|
if (sVendor.equals(i->first))
|
|
{
|
|
sVendorName = i->second;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (sVendorName.getLength() > 0)
|
|
{
|
|
//find the creator func for the respective vendor name
|
|
for ( sal_Int32 c = 0;
|
|
gVendorMap[c].sVendorName != NULL; ++c )
|
|
{
|
|
OUString sNameMap(gVendorMap[c].sVendorName, strlen(gVendorMap[c].sVendorName),
|
|
RTL_TEXTENCODING_ASCII_US);
|
|
if (sNameMap.equals(sVendorName))
|
|
{
|
|
ret = createInstance(gVendorMap[c].createFunc, props);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (ret.is() == false)
|
|
vecBadPaths.push_back(sFilePath);
|
|
else
|
|
{
|
|
JFW_TRACE2(OUSTR("[Java framework] sunjavaplugin"SAL_DLLEXTENSION ": ")
|
|
+ OUSTR("Found JRE: ") + sResolvedDir
|
|
+ OUSTR(" \n at: ") + path + OUSTR(".\n"));
|
|
|
|
mapJREs.insert(MAPJRE::value_type(sResolvedDir, ret));
|
|
mapJREs.insert(MAPJRE::value_type(sFilePath, ret));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
Reference<VendorBase> createInstance(createInstance_func pFunc,
|
|
vector<pair<OUString, OUString> > properties)
|
|
{
|
|
|
|
Reference<VendorBase> aBase = (*pFunc)();
|
|
if (aBase.is())
|
|
{
|
|
if (aBase->initialize(properties) == false)
|
|
aBase = 0;
|
|
}
|
|
return aBase;
|
|
}
|
|
|
|
inline OUString getDirFromFile(const OUString& usFilePath)
|
|
{
|
|
sal_Int32 index= usFilePath.lastIndexOf('/');
|
|
return OUString(usFilePath.getStr(), index);
|
|
}
|
|
|
|
void createJavaInfoFromPath(vector<rtl::Reference<VendorBase> >& vecInfos)
|
|
{
|
|
// Get Java from PATH environment variable
|
|
static OUString sCurDir(RTL_CONSTASCII_USTRINGPARAM("."));
|
|
static OUString sParentDir(RTL_CONSTASCII_USTRINGPARAM(".."));
|
|
char *szPath= getenv("PATH");
|
|
if(szPath)
|
|
{
|
|
OUString usAllPath(szPath, strlen(szPath), osl_getThreadTextEncoding());
|
|
sal_Int32 nIndex = 0;
|
|
do
|
|
{
|
|
OUString usToken = usAllPath.getToken( 0, SAL_PATHSEPARATOR, nIndex );
|
|
OUString usTokenUrl;
|
|
if(File::getFileURLFromSystemPath(usToken, usTokenUrl) == File::E_None)
|
|
{
|
|
if(usTokenUrl.getLength())
|
|
{
|
|
OUString usBin;
|
|
// "."
|
|
if(usTokenUrl.equals(sCurDir))
|
|
{
|
|
OUString usWorkDirUrl;
|
|
if(osl_Process_E_None == osl_getProcessWorkingDir(&usWorkDirUrl.pData))
|
|
usBin= usWorkDirUrl;
|
|
}
|
|
// ".."
|
|
else if(usTokenUrl.equals(sParentDir))
|
|
{
|
|
OUString usWorkDir;
|
|
if(osl_Process_E_None == osl_getProcessWorkingDir(&usWorkDir.pData))
|
|
usBin= getDirFromFile(usWorkDir);
|
|
}
|
|
else
|
|
{
|
|
usBin = usTokenUrl;
|
|
}
|
|
if(usBin.getLength())
|
|
{
|
|
getJREInfoFromBinPath(usBin, vecInfos);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while ( nIndex >= 0 );
|
|
}
|
|
}
|
|
|
|
void createJavaInfoFromJavaHome(vector<rtl::Reference<VendorBase> >& vecInfos)
|
|
{
|
|
// Get Java from JAVA_HOME environment
|
|
char *szJavaHome= getenv("JAVA_HOME");
|
|
if(szJavaHome)
|
|
{
|
|
OUString sHome(szJavaHome,strlen(szJavaHome),osl_getThreadTextEncoding());
|
|
OUString sHomeUrl;
|
|
if(File::getFileURLFromSystemPath(sHome, sHomeUrl) == File::E_None)
|
|
{
|
|
getJREInfoByPath(sHomeUrl, vecInfos);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool makeDriveLetterSame(OUString * fileURL)
|
|
{
|
|
bool ret = false;
|
|
DirectoryItem item;
|
|
if (DirectoryItem::get(*fileURL, item) == File::E_None)
|
|
{
|
|
FileStatus status(FileStatusMask_FileURL);
|
|
if (item.getFileStatus(status) == File::E_None)
|
|
{
|
|
*fileURL = status.getFileURL();
|
|
ret = true;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#ifdef UNX
|
|
void createJavaInfoDirScan(vector<rtl::Reference<VendorBase> >& vecInfos)
|
|
{
|
|
OUString excMessage = OUSTR("[Java framework] sunjavaplugin: "
|
|
"Error in function createJavaInfoDirScan in util.cxx.");
|
|
int cJavaNames= sizeof(g_arJavaNames) / sizeof(char*);
|
|
boost::scoped_array<OUString> sarJavaNames(new OUString[cJavaNames]);
|
|
OUString *arNames = sarJavaNames.get();
|
|
for(int i= 0; i < cJavaNames; i++)
|
|
arNames[i] = OUString(g_arJavaNames[i], strlen(g_arJavaNames[i]),
|
|
RTL_TEXTENCODING_UTF8);
|
|
|
|
int cSearchPaths= sizeof(g_arSearchPaths) / sizeof(char*);
|
|
boost::scoped_array<OUString> sarPathNames(new OUString[cSearchPaths]);
|
|
OUString *arPaths = sarPathNames.get();
|
|
for(int c = 0; c < cSearchPaths; c++)
|
|
arPaths[c] = OUString(g_arSearchPaths[c], strlen(g_arSearchPaths[c]),
|
|
RTL_TEXTENCODING_UTF8);
|
|
|
|
int cCollectDirs = sizeof(g_arCollectDirs) / sizeof(char*);
|
|
boost::scoped_array<OUString> sarCollectDirs(new OUString[cCollectDirs]);
|
|
OUString *arCollectDirs = sarCollectDirs.get();
|
|
for(int d = 0; d < cCollectDirs; d++)
|
|
arCollectDirs[d] = OUString(g_arCollectDirs[d], strlen(g_arCollectDirs[d]),
|
|
RTL_TEXTENCODING_UTF8);
|
|
|
|
|
|
|
|
OUString usFile(RTL_CONSTASCII_USTRINGPARAM("file:///"));
|
|
for( int ii = 0; ii < cSearchPaths; ii ++)
|
|
{
|
|
OUString usDir1(usFile + arPaths[ii]);
|
|
DirectoryItem item;
|
|
if(DirectoryItem::get(usDir1, item) == File::E_None)
|
|
{
|
|
for(int j= 0; j < cCollectDirs; j++)
|
|
{
|
|
OUString usDir2(usDir1 + arCollectDirs[j]);
|
|
// prevent that we scan the whole /usr, /usr/lib, etc directories
|
|
if (arCollectDirs[j] != OUString())
|
|
{
|
|
//usr/java/xxx
|
|
//Examin every subdirectory
|
|
Directory aCollectionDir(usDir2);
|
|
|
|
Directory::RC openErr = aCollectionDir.open();
|
|
switch (openErr)
|
|
{
|
|
case File::E_None:
|
|
break;
|
|
case File::E_NOENT:
|
|
case File::E_NOTDIR:
|
|
continue;
|
|
case File::E_ACCES:
|
|
JFW_TRACE2(OUSTR("[Java framework] sunjavaplugin: "
|
|
"Could not read directory ") + usDir2 +
|
|
OUSTR(" because of missing access rights."));
|
|
continue;
|
|
default:
|
|
JFW_TRACE2(OUSTR("[Java framework] sunjavaplugin: "
|
|
"Could not read directory ")
|
|
+ usDir2 + OUSTR(". Osl file error: ")
|
|
+ OUString::valueOf((sal_Int32) openErr));
|
|
continue;
|
|
}
|
|
|
|
DirectoryItem curIt;
|
|
File::RC errNext = File::E_None;
|
|
while( (errNext = aCollectionDir.getNextItem(curIt)) == File::E_None)
|
|
{
|
|
FileStatus aStatus(FileStatusMask_FileURL);
|
|
File::RC errStatus = File::E_None;
|
|
if ((errStatus = curIt.getFileStatus(aStatus)) != File::E_None)
|
|
{
|
|
JFW_TRACE2(excMessage + OUSTR("getFileStatus failed with error ")
|
|
+ OUString::valueOf((sal_Int32) errStatus));
|
|
continue;
|
|
}
|
|
JFW_TRACE2(OUSTR("[Java framework] sunjavaplugin: "
|
|
"Checking if directory: ") + aStatus.getFileURL() +
|
|
OUSTR(" is a Java. \n"));
|
|
|
|
getJREInfoByPath(aStatus.getFileURL(),vecInfos);
|
|
}
|
|
|
|
JFW_ENSURE(errNext == File::E_None || errNext == File::E_NOENT,
|
|
OUSTR("[Java framework] sunjavaplugin: "
|
|
"Error while iterating over contens of ")
|
|
+ usDir2 + OUSTR(". Osl file error: ")
|
|
+ OUString::valueOf((sal_Int32) openErr));
|
|
}
|
|
else
|
|
{
|
|
//usr/java
|
|
//When we look directly into a dir like /usr, /usr/lib, etc. then we only
|
|
//look for certain java directories, such as jre, jdk, etc. Whe do not want
|
|
//to examine the whole directory because of performance reasons.
|
|
DirectoryItem item2;
|
|
if(DirectoryItem::get(usDir2, item2) == File::E_None)
|
|
{
|
|
for( int k= 0; k < cJavaNames; k++)
|
|
{
|
|
// /usr/java/j2re1.4.0
|
|
OUString usDir3(usDir2 + arNames[k]);
|
|
|
|
DirectoryItem item3;
|
|
if(DirectoryItem::get(usDir3, item) == File::E_None)
|
|
{
|
|
//remove trailing '/'
|
|
sal_Int32 islash = usDir3.lastIndexOf('/');
|
|
if (islash == usDir3.getLength() - 1
|
|
&& (islash
|
|
> RTL_CONSTASCII_LENGTH("file://")))
|
|
usDir3 = usDir3.copy(0, islash);
|
|
getJREInfoByPath(usDir3,vecInfos);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|