update emoji autocorrect entries from po-files
[LibreOffice.git] / jvmfwk / plugins / sunmajor / pluginlib / util.cxx
blobc72668d78070ea792cad4e419e150293a075e4d6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
21 #include <config_folders.h>
23 #include "util.hxx"
25 #include "osl/process.h"
26 #include "osl/security.hxx"
27 #include "osl/file.hxx"
28 #include "osl/module.hxx"
29 #include <osl/diagnose.h>
30 #include <osl/getglobalmutex.hxx>
31 #include "rtl/byteseq.hxx"
32 #include "rtl/ustrbuf.hxx"
33 #include "rtl/instance.hxx"
34 #include "salhelper/linkhelper.hxx"
35 #include "salhelper/thread.hxx"
36 #include "boost/noncopyable.hpp"
37 #include "boost/scoped_array.hpp"
38 #include "com/sun/star/uno/Sequence.hxx"
39 #include <utility>
40 #include <algorithm>
41 #include <map>
43 #if defined WNT
44 #if defined _MSC_VER
45 #pragma warning(push, 1)
46 #endif
47 #include <windows.h>
48 #if defined _MSC_VER
49 #pragma warning(pop)
50 #endif
51 #endif
52 #include <string.h>
54 #include "sunjre.hxx"
55 #include "vendorlist.hxx"
56 #include "diagnostics.h"
58 using namespace osl;
59 using namespace std;
61 using ::rtl::Reference;
63 #ifdef WNT
64 #define HKEY_SUN_JRE L"Software\\JavaSoft\\Java Runtime Environment"
65 #define HKEY_SUN_SDK L"Software\\JavaSoft\\Java Development Kit"
66 #endif
68 #ifdef UNX
69 #if !(defined MACOSX && defined X86_64)
70 namespace {
71 char const *g_arJavaNames[] = {
72 "",
73 "j2re",
74 "j2se",
75 "j2sdk",
76 "jdk",
77 "jre",
78 "java",
79 "Home",
80 "IBMJava2-ppc-142"
82 /* These are directory names which could contain multiple java installations.
84 char const *g_arCollectDirs[] = {
85 "",
86 #ifndef JVM_ONE_PATH_CHECK
87 "j2re/",
88 "j2se/",
89 "j2sdk/",
90 "jdk/",
91 "jre/",
92 "java/",
93 #endif
94 "jvm/"
97 /* These are directories in which a java installation is
98 looked for.
100 char const *g_arSearchPaths[] = {
101 #ifdef MACOSX
103 "Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin",
104 "System/Library/Frameworks/JavaVM.framework/Versions/1.4.2/"
105 #else
106 #ifndef JVM_ONE_PATH_CHECK
108 "usr/",
109 "usr/local/",
110 "usr/local/IBMJava2-ppc-142",
111 "usr/local/j2sdk1.3.1",
112 #ifdef X86_64
113 "usr/lib64/",
114 #endif
115 "usr/lib/",
116 "usr/bin/"
117 #else
118 JVM_ONE_PATH_CHECK
119 #endif
120 #endif
123 #endif
124 #endif // UNX
126 namespace jfw_plugin
128 extern VendorSupportMapEntry gVendorMap[];
130 #if defined WNT
131 bool getSDKInfoFromRegistry(vector<OUString> & vecHome);
132 bool getJREInfoFromRegistry(vector<OUString>& vecJavaHome);
133 #endif
135 bool decodeOutput(const OString& s, OUString* out);
139 namespace
142 bool addJREInfo(
143 rtl::Reference<VendorBase> const & info,
144 std::vector<rtl::Reference<VendorBase>> & infos)
146 auto i(
147 std::find_if(
148 infos.begin(), infos.end(), InfoFindSame(info->getHome())));
149 if (i == infos.end()) {
150 infos.push_back(info);
151 return true;
152 } else {
153 return false;
157 bool getAndAddJREInfoByPath(
158 const OUString& path,
159 std::vector<rtl::Reference<VendorBase> > & allInfos,
160 std::vector<rtl::Reference<VendorBase> > & addedInfos)
162 rtl::Reference<VendorBase> aInfo = getJREInfoByPath(path);
163 if (aInfo.is()) {
164 if (addJREInfo(aInfo, allInfos)) {
165 addedInfos.push_back(aInfo);
167 return true;
168 } else {
169 return false;
173 OUString getLibraryLocation()
175 OUString libraryFileUrl;
176 OSL_VERIFY(osl::Module::getUrlFromAddress(reinterpret_cast<void *>(getLibraryLocation), libraryFileUrl));
177 return getDirFromFile(libraryFileUrl);
180 struct InitBootstrap
182 rtl::Bootstrap * operator()(const OUString& sIni)
184 static rtl::Bootstrap aInstance(sIni);
185 return & aInstance;
190 struct InitBootstrapData
192 OUString const & operator()()
194 static OUString sIni;
195 OUStringBuffer buf( 255);
196 buf.append( getLibraryLocation());
197 #ifdef MACOSX
198 buf.appendAscii( "/../" LIBO_ETC_FOLDER );
199 #endif
200 buf.appendAscii( SAL_CONFIGFILE("/sunjavaplugin") );
201 sIni = buf.makeStringAndClear();
202 JFW_TRACE2("Using configuration file " << sIni);
203 return sIni;
208 rtl::Bootstrap * getBootstrap()
210 return rtl_Instance< rtl::Bootstrap, InitBootstrap,
211 ::osl::MutexGuard, ::osl::GetGlobalMutex,
212 OUString, InitBootstrapData >::create(
213 InitBootstrap(), ::osl::GetGlobalMutex(), InitBootstrapData());
219 class FileHandleGuard: private boost::noncopyable
221 public:
222 inline FileHandleGuard(oslFileHandle & rHandle):
223 m_rHandle(rHandle) {}
225 inline ~FileHandleGuard();
227 inline oslFileHandle & getHandle() { return m_rHandle; }
229 private:
230 oslFileHandle & m_rHandle;
233 inline FileHandleGuard::~FileHandleGuard()
235 if (m_rHandle != 0)
237 if (osl_closeFile(m_rHandle) != osl_File_E_None)
239 OSL_FAIL("unexpected situation");
245 class FileHandleReader
247 public:
248 enum Result
250 RESULT_OK,
251 RESULT_EOF,
252 RESULT_ERROR
255 inline FileHandleReader(oslFileHandle & rHandle):
256 m_aGuard(rHandle), m_nSize(0), m_nIndex(0), m_bLf(false) {}
258 Result readLine(OString * pLine);
260 private:
261 enum { BUFFER_SIZE = 1024 };
263 sal_Char m_aBuffer[BUFFER_SIZE];
264 FileHandleGuard m_aGuard;
265 int m_nSize;
266 int m_nIndex;
267 bool m_bLf;
270 FileHandleReader::Result
271 FileHandleReader::readLine(OString * pLine)
273 OSL_ENSURE(pLine, "specification violation");
275 for (bool bEof = true;; bEof = false)
277 if (m_nIndex == m_nSize)
279 sal_uInt64 nRead = 0;
280 switch (osl_readFile(
281 m_aGuard.getHandle(), m_aBuffer, sizeof(m_aBuffer), &nRead))
283 case osl_File_E_PIPE: //HACK! for windows
284 nRead = 0;
285 //fall-through
286 case osl_File_E_None:
287 if (nRead == 0)
289 m_bLf = false;
290 return bEof ? RESULT_EOF : RESULT_OK;
292 m_nIndex = 0;
293 m_nSize = static_cast< int >(nRead);
294 break;
295 case osl_File_E_INTR:
296 continue;
298 default:
299 return RESULT_ERROR;
303 if (m_bLf && m_aBuffer[m_nIndex] == 0x0A)
304 ++m_nIndex;
305 m_bLf = false;
307 int nStart = m_nIndex;
308 while (m_nIndex != m_nSize)
309 switch (m_aBuffer[m_nIndex++])
311 case 0x0D:
312 m_bLf = true;
313 case 0x0A:
314 *pLine += OString(m_aBuffer + nStart,
315 m_nIndex - 1 - nStart);
316 //TODO! check for overflow, and not very efficient
317 return RESULT_OK;
320 *pLine += OString(m_aBuffer + nStart, m_nIndex - nStart);
321 //TODO! check for overflow, and not very efficient
325 class AsynchReader: public salhelper::Thread
327 size_t m_nDataSize;
328 boost::scoped_array<sal_Char> m_arData;
330 bool m_bError;
331 bool m_bDone;
332 FileHandleGuard m_aGuard;
334 virtual ~AsynchReader() {}
336 void execute() SAL_OVERRIDE;
337 public:
339 AsynchReader(oslFileHandle & rHandle);
341 /** only call this function after this thread has finished.
343 That is, call join on this instance and then call getData.
346 OString getData();
349 AsynchReader::AsynchReader(oslFileHandle & rHandle):
350 Thread("jvmfwkAsyncReader"), m_nDataSize(0), m_bError(false),
351 m_bDone(false), m_aGuard(rHandle)
355 OString AsynchReader::getData()
357 return OString(m_arData.get(), m_nDataSize);
360 void AsynchReader::execute()
362 const sal_uInt64 BUFFER_SIZE = 4096;
363 sal_Char aBuffer[BUFFER_SIZE];
364 while (true)
366 sal_uInt64 nRead;
367 //the function blocks until something could be read or the pipe closed.
368 switch (osl_readFile(
369 m_aGuard.getHandle(), aBuffer, BUFFER_SIZE, &nRead))
371 case osl_File_E_PIPE: //HACK! for windows
372 nRead = 0;
373 case osl_File_E_None:
374 break;
375 default:
376 m_bError = true;
377 return;
380 if (nRead == 0)
382 m_bDone = true;
383 break;
385 else if (nRead <= BUFFER_SIZE)
387 //Save the data we have in m_arData into a temporary array
388 boost::scoped_array<sal_Char> arTmp( new sal_Char[m_nDataSize]);
389 memcpy(arTmp.get(), m_arData.get(), m_nDataSize);
390 //Enlarge m_arData to hold the newly read data
391 m_arData.reset(new sal_Char[(size_t)(m_nDataSize + nRead)]);
392 //Copy back the data that was already in m_arData
393 memcpy(m_arData.get(), arTmp.get(), m_nDataSize);
394 //Add the newly read data to m_arData
395 memcpy(m_arData.get() + m_nDataSize, aBuffer, (size_t) nRead);
396 m_nDataSize += (size_t) nRead;
401 static bool isEnvVarSetToOne(const OUString &aVar)
403 OUString aValue;
404 getBootstrap()->getFrom(aVar, aValue);
405 return aValue == "1";
408 bool getJavaProps(const OUString & exePath,
409 #ifdef JVM_ONE_PATH_CHECK
410 const OUString & homePath,
411 #endif
412 std::vector<std::pair<OUString, OUString> >& props,
413 bool * bProcessRun)
415 bool ret = false;
417 OSL_ASSERT(!exePath.isEmpty());
418 OUString usStartDir;
419 //We need to set the CLASSPATH in case the office is started from
420 //a different directory. The JREProperties.class is expected to reside
421 //next to the plugin, except on OS X where it is in ../Resources/java relative
422 //to the plugin.
423 OUString sThisLib;
424 if (osl_getModuleURLFromAddress(reinterpret_cast<void *>(&getJavaProps),
425 & sThisLib.pData) == sal_False)
427 return false;
429 sThisLib = getDirFromFile(sThisLib);
430 OUString sClassPath;
431 if (osl_getSystemPathFromFileURL(sThisLib.pData, & sClassPath.pData)
432 != osl_File_E_None)
434 return false;
437 #ifdef MACOSX
438 if (sClassPath.endsWith("/"))
439 sClassPath += "../Resources/java/";
440 else
441 sClassPath += "/../Resources/java";
442 #endif
444 //check if we shall examine a Java for accessibility support
445 //If the bootstrap variable is "1" then we pass the argument
446 //"noaccessibility" to JREProperties.class. This will prevent
447 //that it calls java.awt.Toolkit.getDefaultToolkit();
448 bool bNoAccessibility = isEnvVarSetToOne("JFW_PLUGIN_DO_NOT_CHECK_ACCESSIBILITY");
450 //prepare the arguments
451 sal_Int32 cArgs = 3;
452 OUString arg1 = "-classpath";// + sClassPath;
453 OUString arg2 = sClassPath;
454 OUString arg3("JREProperties");
455 OUString arg4 = "noaccessibility";
456 rtl_uString *args[4] = {arg1.pData, arg2.pData, arg3.pData};
458 #ifdef UNX
459 // Java is no longer required for a11y - we use atk directly.
460 bNoAccessibility = !isEnvVarSetToOne("JFW_PLUGIN_FORCE_ACCESSIBILITY");
461 #endif
463 // Only add the fourth param if the bootstrap parameter is set.
464 if (bNoAccessibility)
466 args[3] = arg4.pData;
467 cArgs = 4;
470 oslProcess javaProcess= 0;
471 oslFileHandle fileOut= 0;
472 oslFileHandle fileErr= 0;
474 FileHandleReader stdoutReader(fileOut);
475 rtl::Reference< AsynchReader > stderrReader(new AsynchReader(fileErr));
477 JFW_TRACE2("Executing: " + exePath);
478 oslProcessError procErr =
479 osl_executeProcess_WithRedirectedIO( exePath.pData,//usExe.pData,
480 args,
481 cArgs, //sal_uInt32 nArguments,
482 osl_Process_HIDDEN, //oslProcessOption Options,
483 NULL, //oslSecurity Security,
484 usStartDir.pData,//usStartDir.pData,//usWorkDir.pData, //rtl_uString *strWorkDir,
485 NULL, //rtl_uString *strEnvironment[],
486 0, // sal_uInt32 nEnvironmentVars,
487 &javaProcess, //oslProcess *pProcess,
488 NULL,//oslFileHandle *pChildInputWrite,
489 &fileOut,//oslFileHandle *pChildOutputRead,
490 &fileErr);//oslFileHandle *pChildErrorRead);
492 if( procErr != osl_Process_E_None)
494 JFW_TRACE2("Execution failed");
495 *bProcessRun = false;
496 SAL_WARN("jfw",
497 "osl_executeProcess failed (" << ret << "): \"" << exePath << "\"");
498 return ret;
500 else
502 JFW_TRACE2("Java executed successfully");
503 *bProcessRun = true;
506 //Start asynchronous reading (different thread) of error stream
507 stderrReader->launch();
509 //Use this thread to read output stream
510 FileHandleReader::Result rs = FileHandleReader::RESULT_OK;
511 while (true)
513 OString aLine;
514 rs = stdoutReader.readLine( & aLine);
515 if (rs != FileHandleReader::RESULT_OK)
516 break;
517 OUString sLine;
518 if (!decodeOutput(aLine, &sLine))
519 continue;
520 JFW_TRACE2(" \"" << sLine << " \"");
521 sLine = sLine.trim();
522 if (sLine.isEmpty())
523 continue;
524 //The JREProperties class writes key value pairs, separated by '='
525 sal_Int32 index = sLine.indexOf('=', 0);
526 OSL_ASSERT(index != -1);
527 OUString sKey = sLine.copy(0, index);
528 OUString sVal = sLine.copy(index + 1);
530 #ifdef JVM_ONE_PATH_CHECK
531 //replace absolute path by linux distro link
532 OUString sHomeProperty("java.home");
533 if(sHomeProperty.equals(sKey))
535 sVal = homePath + "/jre";
537 #endif
539 props.push_back(std::make_pair(sKey, sVal));
542 if (rs != FileHandleReader::RESULT_ERROR && !props.empty())
543 ret = true;
545 //process error stream data
546 stderrReader->join();
547 JFW_TRACE2("Java wrote to stderr:\" "
548 << stderrReader->getData().getStr() << " \"");
550 TimeValue waitMax= {5 ,0};
551 procErr = osl_joinProcessWithTimeout(javaProcess, &waitMax);
552 OSL_ASSERT(procErr == osl_Process_E_None);
553 osl_freeProcessHandle(javaProcess);
554 return ret;
557 /* converts the properties printed by JREProperties.class into
558 readable strings. The strings are encoded as integer values separated
559 by spaces.
561 bool decodeOutput(const OString& s, OUString* out)
563 OSL_ASSERT(out != 0);
564 OUStringBuffer buff(512);
565 sal_Int32 nIndex = 0;
568 OString aToken = s.getToken( 0, ' ', nIndex );
569 if (!aToken.isEmpty())
571 for (sal_Int32 i = 0; i < aToken.getLength(); ++i)
573 if (aToken[i] < '0' || aToken[i] > '9')
574 return false;
576 sal_Unicode value = (sal_Unicode)(aToken.toInt32());
577 buff.append(value);
579 } while (nIndex >= 0);
581 *out = buff.makeStringAndClear();
582 return true;
586 #if defined WNT
587 void addJavaInfoFromWinReg(
588 std::vector<rtl::Reference<VendorBase> > & allInfos,
589 std::vector<rtl::Reference<VendorBase> > & addedInfos)
591 // Get Java s from registry
592 std::vector<OUString> vecJavaHome;
593 if(getSDKInfoFromRegistry(vecJavaHome))
595 // create impl objects
596 typedef std::vector<OUString>::iterator ItHome;
597 for(ItHome it_home= vecJavaHome.begin(); it_home != vecJavaHome.end();
598 ++it_home)
600 getAndAddJREInfoByPath(*it_home, allInfos, addedInfos);
604 vecJavaHome.clear();
605 if(getJREInfoFromRegistry(vecJavaHome))
607 typedef std::vector<OUString>::iterator ItHome;
608 for(ItHome it_home= vecJavaHome.begin(); it_home != vecJavaHome.end();
609 ++it_home)
611 getAndAddJREInfoByPath(*it_home, allInfos, addedInfos);
617 bool getJavaInfoFromRegistry(const wchar_t* szRegKey,
618 vector<OUString>& vecJavaHome)
620 HKEY hRoot;
621 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_ENUMERATE_SUB_KEYS, &hRoot)
622 == ERROR_SUCCESS)
624 DWORD dwIndex = 0;
625 const DWORD BUFFSIZE = 1024;
626 wchar_t bufVersion[BUFFSIZE];
627 DWORD nNameLen = BUFFSIZE;
628 FILETIME fileTime;
629 nNameLen = sizeof(bufVersion);
631 // Iterate over all subkeys of HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment
632 while (RegEnumKeyExW(hRoot, dwIndex, bufVersion, &nNameLen, NULL, NULL, NULL, &fileTime) != ERROR_NO_MORE_ITEMS)
634 HKEY hKey;
635 // Open a Java Runtime Environment sub key, e.g. "1.4.0"
636 if (RegOpenKeyExW(hRoot, bufVersion, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
638 DWORD dwType;
639 DWORD dwTmpPathLen= 0;
640 // Get the path to the JavaHome every JRE entry
641 // Find out how long the string for JavaHome is and allocate memory to hold the path
642 if( RegQueryValueExW(hKey, L"JavaHome", 0, &dwType, NULL, &dwTmpPathLen)== ERROR_SUCCESS)
644 char* szTmpPath= (char *) malloc( dwTmpPathLen);
645 // Get the path for the runtime lib
646 if(RegQueryValueExW(hKey, L"JavaHome", 0, &dwType, (unsigned char*) szTmpPath, &dwTmpPathLen) == ERROR_SUCCESS)
648 // There can be several version entries referring with the same JavaHome,e.g 1.4 and 1.4.1
649 OUString usHome((sal_Unicode*) szTmpPath);
650 // check if there is already an entry with the same JavaHomeruntime lib
651 // if so, we use the one with the more accurate version
652 bool bAppend= true;
653 OUString usHomeUrl;
654 if (osl_getFileURLFromSystemPath(usHome.pData, & usHomeUrl.pData) ==
655 osl_File_E_None)
657 //iterate over the vector with java home strings
658 typedef vector<OUString>::iterator ItHome;
659 for(ItHome itHome= vecJavaHome.begin();
660 itHome != vecJavaHome.end(); ++itHome)
662 if(usHomeUrl.equals(*itHome))
664 bAppend= false;
665 break;
668 // Save the home dir
669 if(bAppend)
671 vecJavaHome.push_back(usHomeUrl);
675 free( szTmpPath);
676 RegCloseKey(hKey);
679 dwIndex ++;
680 nNameLen = BUFFSIZE;
682 RegCloseKey(hRoot);
684 return true;
689 bool getSDKInfoFromRegistry(vector<OUString> & vecHome)
691 return getJavaInfoFromRegistry(HKEY_SUN_SDK, vecHome);
694 bool getJREInfoFromRegistry(vector<OUString>& vecJavaHome)
696 return getJavaInfoFromRegistry(HKEY_SUN_JRE, vecJavaHome);
699 #endif // WNT
701 void bubbleSortVersion(vector<rtl::Reference<VendorBase> >& vec)
703 if(vec.empty())
704 return;
705 int size= vec.size() - 1;
706 int cIter= 0;
707 // sort for version
708 for(int i= 0; i < size; i++)
710 for(int j= size; j > 0 + cIter; j--)
712 rtl::Reference<VendorBase>& cur= vec.at(j);
713 rtl::Reference<VendorBase>& next= vec.at(j-1);
715 int nCmp = 0;
716 // comparing invalid SunVersion s is possible, they will be less than a
717 // valid version
719 //check if version of current is recognized, by comparing it with itself
722 (void)cur->compareVersions(cur->getVersion());
724 catch (MalformedVersionException &)
726 nCmp = -1; // current < next
728 //The version of cur is valid, now compare with the second version
729 if (nCmp == 0)
733 nCmp = cur->compareVersions(next->getVersion());
735 catch (MalformedVersionException & )
737 //The second version is invalid, therefore it regards less.
738 nCmp = 1;
741 if(nCmp == 1) // cur > next
743 rtl::Reference<VendorBase> less = next;
744 vec.at(j-1)= cur;
745 vec.at(j)= less;
748 ++cIter;
753 void addJREInfoFromBinPath(
754 const OUString& path, vector<rtl::Reference<VendorBase>> & allInfos,
755 std::vector<rtl::Reference<VendorBase>> & addedInfos)
757 // file:///c:/jre/bin
758 //map: jre/bin/java.exe
760 for ( sal_Int32 pos = 0;
761 gVendorMap[pos].sVendorName != NULL; ++pos )
763 vector<OUString> vecPaths;
764 getJavaExePaths_func pFunc = gVendorMap[pos].getJavaFunc;
766 int size = 0;
767 char const* const* arExePaths = (*pFunc)(&size);
768 vecPaths = getVectorFromCharArray(arExePaths, size);
770 //make sure argument path does not end with '/'
771 OUString sBinPath = path;
772 if (path.endsWith("/"))
773 sBinPath = path.copy(0, path.getLength() - 1);
775 typedef vector<OUString>::const_iterator c_it;
776 for (c_it i = vecPaths.begin(); i != vecPaths.end(); ++i)
778 //the map contains e.g. jre/bin/java.exe
779 //get the directory where the executable is contained
780 OUString sHome;
781 sal_Int32 index = i->lastIndexOf('/');
782 if (index == -1)
784 //map contained only : "java.exe, then the argument
785 //path is already the home directory
786 sHome = sBinPath;
788 else
790 // jre/bin/jre -> jre/bin
791 OUString sMapPath(i->getStr(), index);
792 index = sBinPath.lastIndexOf(sMapPath);
793 if (index != -1
794 && (index + sMapPath.getLength() == sBinPath.getLength())
795 && sBinPath[index - 1] == '/')
797 sHome = sBinPath.copy(index - 1);
800 if (!sHome.isEmpty()
801 && getAndAddJREInfoByPath(path, allInfos, addedInfos))
803 return;
809 vector<Reference<VendorBase> > addAllJREInfos(
810 bool checkJavaHomeAndPath,
811 std::vector<rtl::Reference<VendorBase>> & allInfos)
813 vector<Reference<VendorBase> > addedInfos;
815 #if defined WNT
816 // Get Javas from the registry
817 addJavaInfoFromWinReg(allInfos, addedInfos);
818 #endif // WNT
820 if (checkJavaHomeAndPath) {
821 addJavaInfoFromJavaHome(allInfos, addedInfos);
822 //this function should be called after addJavaInfosDirScan.
823 //Otherwise in SDKs Java may be started twice
824 addJavaInfosFromPath(allInfos, addedInfos);
827 #ifdef UNX
828 addJavaInfosDirScan(allInfos, addedInfos);
829 #endif
831 bubbleSortVersion(addedInfos);
832 return addedInfos;
836 vector<OUString> getVectorFromCharArray(char const * const * ar, int size)
838 vector<OUString> vec;
839 for( int i = 0; i < size; i++)
841 OUString s(ar[i], strlen(ar[i]), RTL_TEXTENCODING_UTF8);
842 vec.push_back(s);
844 return vec;
847 /** Checks if the path is a directory. Links are resolved.
848 In case of an error the returned string has the length 0.
849 Otherwise the returned string is the "resolved" file URL.
851 OUString resolveDirPath(const OUString & path)
853 OUString ret;
854 salhelper::LinkResolver aResolver(osl_FileStatus_Mask_Type |
855 osl_FileStatus_Mask_FileURL);
856 if (aResolver.fetchFileStatus(path) == osl::FileBase::E_None)
858 //check if this is a directory
859 if (aResolver.m_aStatus.getFileType() == FileStatus::Directory)
861 #ifndef JVM_ONE_PATH_CHECK
862 ret = aResolver.m_aStatus.getFileURL();
863 #else
864 ret = path;
865 #endif
868 return ret;
870 /** Checks if the path is a file. If it is a link to a file than
871 it is resolved.
873 OUString resolveFilePath(const OUString & path)
875 OUString ret;
876 salhelper::LinkResolver aResolver(osl_FileStatus_Mask_Type |
877 osl_FileStatus_Mask_FileURL);
878 if (aResolver.fetchFileStatus(path) == osl::FileBase::E_None)
880 //check if this is a file
881 if (aResolver.m_aStatus.getFileType() == FileStatus::Regular)
883 #ifndef JVM_ONE_PATH_CHECK
884 ret = aResolver.m_aStatus.getFileURL();
885 #else
886 ret = path;
887 #endif
890 return ret;
893 rtl::Reference<VendorBase> getJREInfoByPath(
894 const OUString& path)
896 rtl::Reference<VendorBase> ret;
897 static vector<OUString> vecBadPaths;
899 static map<OUString, rtl::Reference<VendorBase> > mapJREs;
900 typedef map<OUString, rtl::Reference<VendorBase> >::const_iterator MapIt;
901 typedef map<OUString, rtl::Reference<VendorBase> > MAPJRE;
902 OUString sFilePath;
903 typedef vector<OUString>::const_iterator cit_path;
904 vector<pair<OUString, OUString> > props;
906 OUString sResolvedDir = resolveDirPath(path);
907 // If this path is invalid then there is no chance to find a JRE here
908 if (sResolvedDir.isEmpty())
910 return 0;
913 //check if the directory path is good, that is a JRE was already recognized.
914 //Then we need not detect it again
915 //For example, a sun JKD contains <jdk>/bin/java and <jdk>/jre/bin/java.
916 //When <jdk>/bin/java has been found then we need not find <jdk>/jre/bin/java.
917 //Otherwise we would execute java two times for evers JDK found.
918 MapIt entry2 = find_if(mapJREs.begin(), mapJREs.end(),
919 SameOrSubDirJREMap(sResolvedDir));
920 if (entry2 != mapJREs.end())
922 JFW_TRACE2("JRE found again (detected before): " << sResolvedDir);
923 return entry2->second;
926 for ( sal_Int32 pos = 0;
927 gVendorMap[pos].sVendorName != NULL; ++pos )
929 vector<OUString> vecPaths;
930 getJavaExePaths_func pFunc = gVendorMap[pos].getJavaFunc;
932 int size = 0;
933 char const* const* arExePaths = (*pFunc)(&size);
934 vecPaths = getVectorFromCharArray(arExePaths, size);
936 bool bBreak = false;
937 typedef vector<OUString>::const_iterator c_it;
938 for (c_it i = vecPaths.begin(); i != vecPaths.end(); ++i)
940 //if the path is a link, then resolve it
941 //check if the executable exists at all
943 //path can be only "file:///". Then do not append a '/'
944 //sizeof counts the terminating 0
945 OUString sFullPath;
946 if (path.getLength() == sizeof("file:///") - 1)
947 sFullPath = sResolvedDir + (*i);
948 else
949 sFullPath = sResolvedDir + "/" + (*i);
951 sFilePath = resolveFilePath(sFullPath);
953 if (sFilePath.isEmpty())
955 //The file path (to java exe) is not valid
956 cit_path ifull = find(vecBadPaths.begin(), vecBadPaths.end(), sFullPath);
957 if (ifull == vecBadPaths.end())
959 vecBadPaths.push_back(sFullPath);
961 continue;
964 cit_path ifile = find(vecBadPaths.begin(), vecBadPaths.end(), sFilePath);
965 if (ifile != vecBadPaths.end())
967 continue;
970 MapIt entry = mapJREs.find(sFilePath);
971 if (entry != mapJREs.end())
973 JFW_TRACE2("JRE found again (detected before): " << sFilePath);
975 return entry->second;
978 bool bProcessRun= false;
979 if (!getJavaProps(sFilePath,
980 #ifdef JVM_ONE_PATH_CHECK
981 sResolvedDir,
982 #endif
983 props, & bProcessRun))
985 //The java executable could not be run or the system properties
986 //could not be retrieved. We can assume that this java is corrupt.
987 vecBadPaths.push_back(sFilePath);
988 //If there was a java executable, that could be run but we did not get
989 //the system properties, then we also assume that the whole Java installation
990 //does not work. In a jdk there are two executables. One in jdk/bin and the other
991 //in jdk/jre/bin. We do not search any further, because we assume that if one java
992 //does not work then the other does not work as well. This saves us to run java
993 //again which is quite costly.
994 if (bProcessRun)
996 // 1.3.1 special treatment: jdk/bin/java and /jdk/jre/bin/java are links to
997 //a script, named .java_wrapper. The script starts jdk/bin/sparc/native_threads/java
998 //or jdk/jre/bin/sparc/native_threads/java. The script uses the name with which it was
999 //invoked to build the path to the executable. It we start the script directy as .java_wrapper
1000 //then it tries to start a jdk/.../native_threads/.java_wrapper. Therefore the link, which
1001 //is named java, must be used to start the script.
1002 getJavaProps(sFullPath,
1003 #ifdef JVM_ONE_PATH_CHECK
1004 sResolvedDir,
1005 #endif
1006 props, & bProcessRun);
1007 // Either we found a working 1.3.1
1008 //Or the java is broken. In both cases we stop searchin under this "root" directory
1009 bBreak = true;
1010 break;
1012 //sFilePath is no working java executable. We continue with another possible
1013 //path.
1014 else
1016 continue;
1019 //sFilePath is a java and we could get the system properties. We proceed with this
1020 //java.
1021 else
1023 bBreak = true;
1024 break;
1027 if (bBreak)
1028 break;
1031 if (props.empty())
1033 return rtl::Reference<VendorBase>();
1036 //find java.vendor property
1037 typedef vector<pair<OUString, OUString> >::const_iterator c_ip;
1038 OUString sVendor("java.vendor");
1039 OUString sVendorName;
1041 for (c_ip i = props.begin(); i != props.end(); ++i)
1043 if (sVendor.equals(i->first))
1045 sVendorName = i->second;
1046 break;
1050 if (!sVendorName.isEmpty())
1052 //find the creator func for the respective vendor name
1053 for ( sal_Int32 c = 0;
1054 gVendorMap[c].sVendorName != NULL; ++c )
1056 OUString sNameMap(gVendorMap[c].sVendorName, strlen(gVendorMap[c].sVendorName),
1057 RTL_TEXTENCODING_ASCII_US);
1058 if (sNameMap.equals(sVendorName))
1060 ret = createInstance(gVendorMap[c].createFunc, props);
1061 break;
1065 if (!ret.is())
1067 vecBadPaths.push_back(sFilePath);
1069 else
1071 JFW_TRACE2("Found JRE: " << sResolvedDir << " at: " << path);
1073 mapJREs.insert(MAPJRE::value_type(sResolvedDir, ret));
1074 mapJREs.insert(MAPJRE::value_type(sFilePath, ret));
1077 return ret;
1080 Reference<VendorBase> createInstance(createInstance_func pFunc,
1081 vector<pair<OUString, OUString> > properties)
1084 Reference<VendorBase> aBase = (*pFunc)();
1085 if (aBase.is())
1087 if (!aBase->initialize(properties))
1088 aBase = 0;
1090 return aBase;
1093 inline OUString getDirFromFile(const OUString& usFilePath)
1095 sal_Int32 index = usFilePath.lastIndexOf('/');
1096 return usFilePath.copy(0, index);
1099 void addJavaInfosFromPath(
1100 std::vector<rtl::Reference<VendorBase>> & allInfos,
1101 std::vector<rtl::Reference<VendorBase>> & addedInfos)
1103 #if !defined JVM_ONE_PATH_CHECK
1104 // Get Java from PATH environment variable
1105 char *szPath= getenv("PATH");
1106 if(szPath)
1108 OUString usAllPath(szPath, strlen(szPath), osl_getThreadTextEncoding());
1109 sal_Int32 nIndex = 0;
1112 OUString usToken = usAllPath.getToken( 0, SAL_PATHSEPARATOR, nIndex );
1113 OUString usTokenUrl;
1114 if(File::getFileURLFromSystemPath(usToken, usTokenUrl) == File::E_None)
1116 if(!usTokenUrl.isEmpty())
1118 OUString usBin;
1119 if(usTokenUrl == ".")
1121 OUString usWorkDirUrl;
1122 if(osl_Process_E_None == osl_getProcessWorkingDir(&usWorkDirUrl.pData))
1123 usBin= usWorkDirUrl;
1125 else if(usTokenUrl == "..")
1127 OUString usWorkDir;
1128 if(osl_Process_E_None == osl_getProcessWorkingDir(&usWorkDir.pData))
1129 usBin= getDirFromFile(usWorkDir);
1131 else
1133 usBin = usTokenUrl;
1135 if(!usBin.isEmpty())
1137 addJREInfoFromBinPath(usBin, allInfos, addedInfos);
1142 while ( nIndex >= 0 );
1144 #endif
1148 void addJavaInfoFromJavaHome(
1149 std::vector<rtl::Reference<VendorBase>> & allInfos,
1150 std::vector<rtl::Reference<VendorBase>> & addedInfos)
1152 #if !defined JVM_ONE_PATH_CHECK
1153 // Get Java from JAVA_HOME environment
1155 // Note that on OS X is it not normal at all to have a JAVA_HOME environment
1156 // variable. We set it in our build environment for build-time programs, though,
1157 // so it is set when running unit tests that involve Java functionality. (Which affects
1158 // at least CppunitTest_dbaccess_dialog_save, too, and not only the JunitTest ones.)
1159 char *szJavaHome= getenv("JAVA_HOME");
1160 if(szJavaHome)
1162 OUString sHome(szJavaHome, strlen(szJavaHome), osl_getThreadTextEncoding());
1163 OUString sHomeUrl;
1164 if(File::getFileURLFromSystemPath(sHome, sHomeUrl) == File::E_None)
1166 getAndAddJREInfoByPath(sHomeUrl, allInfos, addedInfos);
1169 #endif
1172 bool makeDriveLetterSame(OUString * fileURL)
1174 bool ret = false;
1175 DirectoryItem item;
1176 if (DirectoryItem::get(*fileURL, item) == File::E_None)
1178 FileStatus status(osl_FileStatus_Mask_FileURL);
1179 if (item.getFileStatus(status) == File::E_None)
1181 *fileURL = status.getFileURL();
1182 ret = true;
1185 return ret;
1188 #ifdef UNX
1189 #ifdef SOLARIS
1191 void addJavaInfosDirScan(
1192 std::vector<rtl::Reference<VendorBase>> & allInfos,
1193 std::vector<rtl::Reference<VendorBase>> & addedInfos)
1195 JFW_TRACE2("Checking /usr/jdk/latest");
1196 getAndAddJREInfoByPath("file:////usr/jdk/latest", allInfos, addedInfos);
1199 #elif defined MACOSX && defined X86_64
1201 void addJavaInfosDirScan(
1202 std::vector<rtl::Reference<VendorBase>> & allInfos,
1203 std::vector<rtl::Reference<VendorBase>> & addedInfos)
1205 // Oracle Java 7
1206 getAndAddJREInfoByPath("file:///Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home", allInfos, addedInfos);
1209 #else
1210 void addJavaInfosDirScan(
1211 std::vector<rtl::Reference<VendorBase>> & allInfos,
1212 std::vector<rtl::Reference<VendorBase>> & addedInfos)
1214 OUString excMessage = "[Java framework] sunjavaplugin: "
1215 "Error in function addJavaInfosDirScan in util.cxx.";
1216 int cJavaNames= sizeof(g_arJavaNames) / sizeof(char*);
1217 boost::scoped_array<OUString> sarJavaNames(new OUString[cJavaNames]);
1218 OUString *arNames = sarJavaNames.get();
1219 for(int i= 0; i < cJavaNames; i++)
1220 arNames[i] = OUString(g_arJavaNames[i], strlen(g_arJavaNames[i]),
1221 RTL_TEXTENCODING_UTF8);
1223 int cSearchPaths= sizeof(g_arSearchPaths) / sizeof(char*);
1224 boost::scoped_array<OUString> sarPathNames(new OUString[cSearchPaths]);
1225 OUString *arPaths = sarPathNames.get();
1226 for(int c = 0; c < cSearchPaths; c++)
1227 arPaths[c] = OUString(g_arSearchPaths[c], strlen(g_arSearchPaths[c]),
1228 RTL_TEXTENCODING_UTF8);
1230 int cCollectDirs = sizeof(g_arCollectDirs) / sizeof(char*);
1231 boost::scoped_array<OUString> sarCollectDirs(new OUString[cCollectDirs]);
1232 OUString *arCollectDirs = sarCollectDirs.get();
1233 for(int d = 0; d < cCollectDirs; d++)
1234 arCollectDirs[d] = OUString(g_arCollectDirs[d], strlen(g_arCollectDirs[d]),
1235 RTL_TEXTENCODING_UTF8);
1239 OUString usFile("file:///");
1240 for( int ii = 0; ii < cSearchPaths; ii ++)
1242 OUString usDir1(usFile + arPaths[ii]);
1243 DirectoryItem item;
1244 if(DirectoryItem::get(usDir1, item) == File::E_None)
1246 for(int j= 0; j < cCollectDirs; j++)
1248 OUString usDir2(usDir1 + arCollectDirs[j]);
1249 // prevent that we scan the whole /usr, /usr/lib, etc directories
1250 if (!arCollectDirs[j].isEmpty())
1252 //usr/java/xxx
1253 //Examin every subdirectory
1254 Directory aCollectionDir(usDir2);
1256 Directory::RC openErr = aCollectionDir.open();
1257 switch (openErr)
1259 case File::E_None:
1260 break;
1261 case File::E_NOENT:
1262 case File::E_NOTDIR:
1263 continue;
1264 case File::E_ACCES:
1265 JFW_TRACE2("Could not read directory " << usDir2 << " because of missing access rights");
1266 continue;
1267 default:
1268 JFW_TRACE2("Could not read directory " << usDir2 << ". Osl file error: " << openErr);
1269 continue;
1272 DirectoryItem curIt;
1273 File::RC errNext = File::E_None;
1274 while( (errNext = aCollectionDir.getNextItem(curIt)) == File::E_None)
1276 FileStatus aStatus(osl_FileStatus_Mask_FileURL);
1277 File::RC errStatus = File::E_None;
1278 if ((errStatus = curIt.getFileStatus(aStatus)) != File::E_None)
1280 JFW_TRACE2(excMessage + "getFileStatus failed with error " << errStatus);
1281 continue;
1283 JFW_TRACE2("Checking if directory: " << aStatus.getFileURL() << " is a Java");
1285 getAndAddJREInfoByPath(
1286 aStatus.getFileURL(), allInfos, addedInfos);
1289 JFW_ENSURE(errNext == File::E_None || errNext == File::E_NOENT,
1290 "[Java framework] sunjavaplugin: "
1291 "Error while iterating over contens of "
1292 + usDir2 + ". Osl file error: "
1293 + OUString::number(openErr));
1295 else
1297 //usr/java
1298 //When we look directly into a dir like /usr, /usr/lib, etc. then we only
1299 //look for certain java directories, such as jre, jdk, etc. Whe do not want
1300 //to examine the whole directory because of performance reasons.
1301 DirectoryItem item2;
1302 if(DirectoryItem::get(usDir2, item2) == File::E_None)
1304 for( int k= 0; k < cJavaNames; k++)
1306 // /usr/java/j2re1.4.0
1307 OUString usDir3(usDir2 + arNames[k]);
1309 DirectoryItem item3;
1310 if(DirectoryItem::get(usDir3, item) == File::E_None)
1312 //remove trailing '/'
1313 sal_Int32 islash = usDir3.lastIndexOf('/');
1314 if (islash == usDir3.getLength() - 1
1315 && (islash
1316 > RTL_CONSTASCII_LENGTH("file://")))
1317 usDir3 = usDir3.copy(0, islash);
1318 getAndAddJREInfoByPath(
1319 usDir3, allInfos, addedInfos);
1328 #endif // ifdef SOLARIS
1329 #endif // ifdef UNX
1332 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */