1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_plugin.hxx"
26 
27 #include "util.hxx"
28 
29 #include "osl/process.h"
30 #include "osl/security.hxx"
31 #include "osl/thread.hxx"
32 #include "osl/file.hxx"
33 #include "osl/module.hxx"
34 #include "rtl/byteseq.hxx"
35 #include "rtl/ustrbuf.hxx"
36 #include "rtl/instance.hxx"
37 #include "boost/scoped_array.hpp"
38 #include "com/sun/star/uno/Sequence.hxx"
39 #include <utility>
40 #include <algorithm>
41 #include <map>
42 
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>
53 
54 #include "sunjre.hxx"
55 #include "vendorlist.hxx"
56 #include "diagnostics.h"
57 using namespace rtl;
58 using namespace osl;
59 using namespace std;
60 
61 #define CHAR_POINTER(oustr) ::rtl::OUStringToOString(oustr,RTL_TEXTENCODING_UTF8).pData->buffer
62 #define OUSTR(x) ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(x) )
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
67 
68 #ifdef UNX
69 namespace {
70 char const *g_arJavaNames[] = {
71     "",
72     "j2re",
73     "j2se",
74     "j2sdk",
75     "jdk",
76     "jre",
77     "java",
78     "Home",
79     "IBMJava2-ppc-142"
80 };
81 /* These are directory names which could contain multiple java installations.
82  */
83 char const *g_arCollectDirs[] = {
84     "",
85     "j2re/",
86     "j2se/",
87     "j2sdk/",
88     "jdk/",
89     "jre/",
90     "java/",
91     "jvm/"
92 };
93 
94 struct JavaSearchPathEntry {
95     int searchImmediateContents; // More thorough, too slow for /usr/bin and /usr/lib
96     char const *path;
97 };
98 
99 /* These are directories in which a java installation is
100    looked for.
101 */
102 struct JavaSearchPathEntry g_arSearchPaths[] = {
103 #ifdef MACOSX
104     { 0, "" },
105     { 0, "Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin" },
106     { 0, "System/Library/Frameworks/JavaVM.framework/Versions/1.4.2/" },
107 #else
108     { 0, "" },
109     { 1, "usr/" },
110     { 1, "usr/local/" },
111     { 0, "usr/local/IBMJava2-ppc-142" },
112     { 0, "usr/local/j2sdk1.3.1" },
113 #ifdef X86_64
114     { 0, "usr/lib64/" },
115 #endif
116     { 0, "usr/lib/" },
117     { 0, "usr/bin/" }
118 #endif
119 };
120 }
121 #endif //  UNX
122 
123 namespace jfw_plugin
124 {
125 extern VendorSupportMapEntry gVendorMap[];
126 
127 bool getSDKInfoFromRegistry(vector<OUString> & vecHome);
128 bool getJREInfoFromRegistry(vector<OUString>& vecJavaHome);
129 bool decodeOutput(const rtl::OString& s, rtl::OUString* out);
130 
131 
132 
133 namespace
134 {
getLibraryLocation()135     rtl::OUString getLibraryLocation()
136     {
137         rtl::OUString libraryFileUrl;
138         OSL_VERIFY(osl::Module::getUrlFromAddress((void *)(sal_IntPtr)getLibraryLocation, libraryFileUrl));
139         return getDirFromFile(libraryFileUrl);
140     }
141 
142     struct InitBootstrap
143     {
operator ()jfw_plugin::__anonc82bb3750211::InitBootstrap144         rtl::Bootstrap * operator()(const OUString& sIni)
145         {
146             static rtl::Bootstrap aInstance(sIni);
147             return & aInstance;
148 
149         }
150    };
151 
152    struct InitBootstrapData
153    {
operator ()jfw_plugin::__anonc82bb3750211::InitBootstrapData154        OUString const & operator()()
155        {
156            //  osl::Guard<osl::Mutex> g(osl::GetGlobalMutex());
157            static OUString sIni;
158             rtl::OUStringBuffer buf( 255);
159             buf.append( getLibraryLocation());
160             buf.appendAscii( SAL_CONFIGFILE("/sunjavaplugin") );
161             sIni = buf.makeStringAndClear();
162             JFW_TRACE2(OUSTR("[Java framework] sunjavaplugin: "
163                              "Using configuration file \n") +  sIni);
164             return sIni;
165         }
166    };
167 }
168 
getBootstrap()169 rtl::Bootstrap * getBootstrap()
170 {
171     return rtl_Instance< rtl::Bootstrap, InitBootstrap,
172         ::osl::MutexGuard, ::osl::GetGlobalMutex,
173         OUString, InitBootstrapData >::create(
174             InitBootstrap(), ::osl::GetGlobalMutex(), InitBootstrapData());
175 }
176 
177 
178 
179 
180 class FileHandleGuard
181 {
182 public:
183     inline FileHandleGuard(oslFileHandle & rHandle) SAL_THROW(()):
184         m_rHandle(rHandle) {}
185 
186     inline ~FileHandleGuard() SAL_THROW(());
187 
getHandle()188     inline oslFileHandle & getHandle() SAL_THROW(()) { return m_rHandle; }
189 
190 private:
191     oslFileHandle & m_rHandle;
192 
193     FileHandleGuard(FileHandleGuard &); // not implemented
194     void operator =(FileHandleGuard); // not implemented
195 };
196 
~FileHandleGuard()197 inline FileHandleGuard::~FileHandleGuard() SAL_THROW(())
198 {
199 	if (m_rHandle != 0)
200 	{
201 		if (osl_closeFile(m_rHandle) != osl_File_E_None)
202         {
203             OSL_ENSURE(false, "unexpected situation");
204         }
205 	}
206 }
207 
208 
209 class FileHandleReader
210 {
211 public:
212     enum Result
213     {
214         RESULT_OK,
215         RESULT_EOF,
216         RESULT_ERROR
217     };
218 
219     inline FileHandleReader(oslFileHandle & rHandle) SAL_THROW(()):
220         m_aGuard(rHandle), m_nSize(0), m_nIndex(0), m_bLf(false) {}
221 
222     Result readLine(rtl::OString * pLine) SAL_THROW(());
223 
224 private:
225     enum { BUFFER_SIZE = 1024 };
226 
227     sal_Char m_aBuffer[BUFFER_SIZE];
228     FileHandleGuard m_aGuard;
229     int m_nSize;
230     int m_nIndex;
231     bool m_bLf;
232 };
233 
234 FileHandleReader::Result
readLine(rtl::OString * pLine)235 FileHandleReader::readLine(rtl::OString * pLine)
236     SAL_THROW(())
237 {
238     OSL_ENSURE(pLine, "specification violation");
239 
240     for (bool bEof = true;; bEof = false)
241     {
242         if (m_nIndex == m_nSize)
243         {
244             sal_uInt64 nRead = 0;
245             switch (osl_readFile(
246                         m_aGuard.getHandle(), m_aBuffer, sizeof(m_aBuffer), &nRead))
247             {
248             case osl_File_E_PIPE: //HACK! for windows
249                 nRead = 0;
250             case osl_File_E_None:
251                 if (nRead == 0)
252                 {
253                     m_bLf = false;
254                     return bEof ? RESULT_EOF : RESULT_OK;
255                 }
256                 m_nIndex = 0;
257                 m_nSize = static_cast< int >(nRead);
258                 break;
259             case osl_File_E_INTR:
260                 continue;
261 
262             default:
263                 return RESULT_ERROR;
264             }
265         }
266 
267         if (m_bLf && m_aBuffer[m_nIndex] == 0x0A)
268             ++m_nIndex;
269         m_bLf = false;
270 
271         int nStart = m_nIndex;
272         while (m_nIndex != m_nSize)
273             switch (m_aBuffer[m_nIndex++])
274             {
275             case 0x0D:
276                 m_bLf = true;
277             case 0x0A:
278                 *pLine += rtl::OString(m_aBuffer + nStart,
279                                        m_nIndex - 1 - nStart);
280                     //TODO! check for overflow, and not very efficient
281                 return RESULT_OK;
282             }
283 
284         *pLine += rtl::OString(m_aBuffer + nStart, m_nIndex - nStart);
285             //TODO! check for overflow, and not very efficient
286     }
287 }
288 
289 class AsynchReader: public Thread
290 {
291     size_t  m_nDataSize;
292     boost::scoped_array<sal_Char> m_arData;
293 
294     bool m_bError;
295     bool m_bDone;
296     FileHandleGuard m_aGuard;
297 
298     void SAL_CALL run();
299 public:
300 
301     AsynchReader(oslFileHandle & rHandle);
302 #if OSL_DEBUG_LEVEL >= 2
303     /** only call this function after this thread has finished.
304 
305         That is, call join on this instance and then call getData.
306 
307      */
308     OString getData();
309 #endif
310 };
311 
AsynchReader(oslFileHandle & rHandle)312 AsynchReader::AsynchReader(oslFileHandle & rHandle):
313     m_nDataSize(0), m_bError(false), m_bDone(false), m_aGuard(rHandle)
314 {
315 }
316 
317 #if OSL_DEBUG_LEVEL >= 2
getData()318 OString AsynchReader::getData()
319 {
320     OSL_ASSERT(isRunning() == sal_False );
321     return OString(m_arData.get(), m_nDataSize);
322 }
323 #endif
324 
run()325 void AsynchReader::run()
326 {
327     const sal_uInt64 BUFFER_SIZE = 4096;
328     sal_Char aBuffer[BUFFER_SIZE];
329     while (true)
330     {
331         sal_uInt64 nRead;
332         //the function blocks until something could be read or the pipe closed.
333         switch (osl_readFile(
334                     m_aGuard.getHandle(), aBuffer, BUFFER_SIZE, &nRead))
335         {
336         case osl_File_E_PIPE: //HACK! for windows
337             nRead = 0;
338         case osl_File_E_None:
339             break;
340         default:
341             m_bError = true;
342             return;
343         }
344 
345         if (nRead == 0)
346         {
347             m_bDone = true;
348             break;
349         }
350         else if (nRead <= BUFFER_SIZE)
351         {
352             //Save the data we have in m_arData into a temporary array
353             boost::scoped_array<sal_Char> arTmp( new sal_Char[m_nDataSize]);
354             memcpy(arTmp.get(), m_arData.get(), m_nDataSize);
355             //Enlarge m_arData to hold the newly read data
356             m_arData.reset(new sal_Char[(size_t)(m_nDataSize + nRead)]);
357             //Copy back the data that was already in m_arData
358             memcpy(m_arData.get(), arTmp.get(), m_nDataSize);
359             //Add the newly read data to m_arData
360             memcpy(m_arData.get() + m_nDataSize, aBuffer, (size_t) nRead);
361             m_nDataSize += (size_t) nRead;
362         }
363     }
364 }
365 
366 
getJavaProps(const OUString & exePath,std::vector<std::pair<rtl::OUString,rtl::OUString>> & props,bool * bProcessRun)367 bool getJavaProps(const OUString & exePath,
368                   std::vector<std::pair<rtl::OUString, rtl::OUString> >& props,
369                   bool * bProcessRun)
370 {
371     bool ret = false;
372 
373     OSL_ASSERT( exePath.getLength() > 0);
374     OUString usStartDir;
375     //We need to set the CLASSPATH in case the office is started from
376     //a different directory. The JREProperties.class is expected to reside
377     //next to the plugin.
378     rtl::OUString sThisLib;
379     if (osl_getModuleURLFromAddress((void *) (sal_IntPtr)& getJavaProps,
380                                     & sThisLib.pData) == sal_False)
381         return false;
382     sThisLib = getDirFromFile(sThisLib);
383     OUString sClassPath;
384     if (osl_getSystemPathFromFileURL(sThisLib.pData, & sClassPath.pData)
385         != osl_File_E_None)
386         return false;
387 
388     //check if we shall examine a Java for accessibility support
389     //If the bootstrap variable is "1" then we pass the argument
390     //"noaccessibility" to JREProperties.class. This will prevent
391     //that it calls   java.awt.Toolkit.getDefaultToolkit();
392     OUString sValue;
393     getBootstrap()->getFrom(OUSTR("JFW_PLUGIN_DO_NOT_CHECK_ACCESSIBILITY"), sValue);
394 
395     //prepare the arguments
396     sal_Int32 cArgs = 3;
397     OUString arg1 = OUString(RTL_CONSTASCII_USTRINGPARAM("-classpath"));// + sClassPath;
398     OUString arg2 = sClassPath;
399     OUString arg3(RTL_CONSTASCII_USTRINGPARAM("JREProperties"));
400     OUString arg4 = OUSTR("noaccessibility");
401     rtl_uString *args[4] = {arg1.pData, arg2.pData, arg3.pData};
402 
403     // Only add the fourth param if the bootstrap parameter is set.
404     if (sValue.equals(OUString::valueOf((sal_Int32) 1)))
405     {
406         args[3] = arg4.pData;
407         cArgs = 4;
408     }
409 
410     oslProcess javaProcess= 0;
411     oslFileHandle fileOut= 0;
412     oslFileHandle fileErr= 0;
413 
414     FileHandleReader stdoutReader(fileOut);
415     AsynchReader stderrReader(fileErr);
416 
417     JFW_TRACE2(OUSTR("\n[Java framework] Executing: ") + exePath + OUSTR(".\n"));
418     oslProcessError procErr =
419         osl_executeProcess_WithRedirectedIO( exePath.pData,//usExe.pData,
420                                              args,
421                                              cArgs,                 //sal_uInt32   nArguments,
422                                              osl_Process_HIDDEN, //oslProcessOption Options,
423                                              NULL, //oslSecurity Security,
424                                              usStartDir.pData,//usStartDir.pData,//usWorkDir.pData, //rtl_uString *strWorkDir,
425                                              NULL, //rtl_uString *strEnvironment[],
426                                              0, //  sal_uInt32   nEnvironmentVars,
427                                              &javaProcess, //oslProcess *pProcess,
428                                              NULL,//oslFileHandle *pChildInputWrite,
429                                              &fileOut,//oslFileHandle *pChildOutputRead,
430                                              &fileErr);//oslFileHandle *pChildErrorRead);
431 
432     if( procErr != osl_Process_E_None)
433     {
434         JFW_TRACE2("[Java framework] Execution failed. \n");
435         *bProcessRun = false;
436         return ret;
437     }
438     else
439     {
440         JFW_TRACE2("[Java framework] Java executed successfully.\n");
441         *bProcessRun = true;
442     }
443 
444     //Start asynchronous reading (different thread) of error stream
445     stderrReader.create();
446 
447     //Use this thread to read output stream
448     FileHandleReader::Result rs = FileHandleReader::RESULT_OK;
449     while (1)
450     {
451         OString aLine;
452         rs = stdoutReader.readLine( & aLine);
453         if (rs != FileHandleReader::RESULT_OK)
454             break;
455 //         JFW_TRACE2(OString("[Java framework] line:\" ")
456 //                + aLine + OString(" \".\n"));
457         OUString sLine;
458         if (!decodeOutput(aLine, &sLine))
459             continue;
460         JFW_TRACE2(OString("[Java framework]:\" ")
461                + OString( CHAR_POINTER(sLine)) + OString(" \".\n"));
462         sLine = sLine.trim();
463         if (sLine.getLength() == 0)
464             continue;
465         //The JREProperties class writes key value pairs, separated by '='
466         sal_Int32 index = sLine.indexOf('=', 0);
467         OSL_ASSERT(index != -1);
468         OUString sKey = sLine.copy(0, index);
469         OUString sVal = sLine.copy(index + 1);
470 
471         props.push_back(std::make_pair(sKey, sVal));
472     }
473 
474     if (rs != FileHandleReader::RESULT_ERROR && props.size()>0)
475         ret = true;
476 
477     //process error stream data
478     stderrReader.join();
479     JFW_TRACE2(OString("[Java framework]  Java wrote to stderr:\" ")
480                + stderrReader.getData() + OString(" \".\n"));
481 
482     TimeValue waitMax= {5 ,0};
483     procErr = osl_joinProcessWithTimeout(javaProcess, &waitMax);
484     OSL_ASSERT(procErr == osl_Process_E_None);
485     osl_freeProcessHandle(javaProcess);
486     return ret;
487 }
488 
489 /* converts the properties printed by JREProperties.class into
490     readable strings. The strings are encoded as integer values separated
491     by spaces.
492  */
decodeOutput(const rtl::OString & s,rtl::OUString * out)493 bool decodeOutput(const rtl::OString& s, rtl::OUString* out)
494 {
495     OSL_ASSERT(out != 0);
496     OUStringBuffer buff(512);
497     sal_Int32 nIndex = 0;
498     do
499     {
500         OString aToken = s.getToken( 0, ' ', nIndex );
501         if (aToken.getLength())
502         {
503             for (sal_Int32 i = 0; i < aToken.getLength(); ++i)
504             {
505                 if (aToken[i] < '0' || aToken[i] > '9')
506                     return false;
507             }
508             sal_Unicode value = (sal_Unicode)(aToken.toInt32());
509             buff.append(value);
510         }
511     } while (nIndex >= 0);
512 
513     *out = buff.makeStringAndClear();
514 //    JFW_TRACE2(*out);
515     return true;
516 }
517 
518 
519 #if defined WNT
createJavaInfoFromWinReg(std::vector<rtl::Reference<VendorBase>> & vecInfos)520 void createJavaInfoFromWinReg(std::vector<rtl::Reference<VendorBase> > & vecInfos)
521 {
522         // Get Java s from registry
523     std::vector<OUString> vecJavaHome;
524     if(getSDKInfoFromRegistry(vecJavaHome))
525     {
526         // create impl objects
527         typedef std::vector<OUString>::iterator ItHome;
528         for(ItHome it_home= vecJavaHome.begin(); it_home != vecJavaHome.end();
529             it_home++)
530         {
531             getJREInfoByPath(*it_home, vecInfos);
532         }
533     }
534 
535     vecJavaHome.clear();
536     if(getJREInfoFromRegistry(vecJavaHome))
537     {
538         typedef std::vector<OUString>::iterator ItHome;
539         for(ItHome it_home= vecJavaHome.begin(); it_home != vecJavaHome.end();
540             it_home++)
541         {
542             getJREInfoByPath(*it_home, vecInfos);
543         }
544    }
545 }
546 
547 
getJavaInfoFromRegistry(const wchar_t * szRegKey,vector<OUString> & vecJavaHome)548 bool getJavaInfoFromRegistry(const wchar_t* szRegKey,
549                              vector<OUString>& vecJavaHome)
550 {
551     HKEY    hRoot;
552     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_ENUMERATE_SUB_KEYS, &hRoot)
553         == ERROR_SUCCESS)
554     {
555         DWORD dwIndex = 0;
556 		const DWORD BUFFSIZE = 1024;
557         wchar_t bufVersion[BUFFSIZE];
558 //		char bufVersion[BUFFSIZE];
559 		DWORD nNameLen = BUFFSIZE;
560         FILETIME fileTime;
561         nNameLen = sizeof(bufVersion);
562 
563         // Iterate over all subkeys of HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment
564         while (RegEnumKeyExW(hRoot, dwIndex, bufVersion, &nNameLen, NULL, NULL, NULL, &fileTime) != ERROR_NO_MORE_ITEMS)
565         {
566             HKEY    hKey;
567             // Open a Java Runtime Environment sub key, e.g. "1.4.0"
568             if (RegOpenKeyExW(hRoot, bufVersion, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
569             {
570                 DWORD   dwType;
571                 DWORD   dwTmpPathLen= 0;
572                 // Get the path to the JavaHome every JRE entry
573                 // Find out how long the string for JavaHome is and allocate memory to hold the path
574                 if( RegQueryValueExW(hKey, L"JavaHome", 0, &dwType, NULL, &dwTmpPathLen)== ERROR_SUCCESS)
575                 {
576                     char* szTmpPath= (char *) malloc( dwTmpPathLen);
577                     // Get the path for the runtime lib
578                     if(RegQueryValueExW(hKey, L"JavaHome", 0, &dwType, (unsigned char*) szTmpPath, &dwTmpPathLen) == ERROR_SUCCESS)
579                     {
580                         // There can be several version entries referring with the same JavaHome,e.g 1.4 and 1.4.1
581                         OUString usHome((sal_Unicode*) szTmpPath);
582                         // check if there is already an entry with the same JavaHomeruntime lib
583                         // if so, we use the one with the more accurate version
584                         bool bAppend= true;
585                         OUString usHomeUrl;
586                         if (osl_getFileURLFromSystemPath(usHome.pData, & usHomeUrl.pData) ==
587                             osl_File_E_None)
588                         {
589                             //iterate over the vector with java home strings
590                             typedef vector<OUString>::iterator ItHome;
591                             for(ItHome itHome= vecJavaHome.begin();
592                                 itHome != vecJavaHome.end(); itHome++)
593                             {
594                                 if(usHomeUrl.equals(*itHome))
595                                 {
596                                     bAppend= false;
597                                     break;
598                                 }
599                             }
600                             // Save the home dir
601                             if(bAppend)
602                             {
603                                 vecJavaHome.push_back(usHomeUrl);
604                             }
605                         }
606                     }
607                     free( szTmpPath);
608                     RegCloseKey(hKey);
609                 }
610             }
611             dwIndex ++;
612             nNameLen = BUFFSIZE;
613         }
614         RegCloseKey(hRoot);
615     }
616     return true;
617 }
618 
619 
620 
getSDKInfoFromRegistry(vector<OUString> & vecHome)621 bool getSDKInfoFromRegistry(vector<OUString> & vecHome)
622 {
623     return getJavaInfoFromRegistry(HKEY_SUN_SDK, vecHome);
624 }
625 
getJREInfoFromRegistry(vector<OUString> & vecJavaHome)626 bool getJREInfoFromRegistry(vector<OUString>& vecJavaHome)
627 {
628     return getJavaInfoFromRegistry(HKEY_SUN_JRE, vecJavaHome);
629 }
630 
631 #endif // WNT
632 
bubbleSortVersion(vector<rtl::Reference<VendorBase>> & vec)633 void bubbleSortVersion(vector<rtl::Reference<VendorBase> >& vec)
634 {
635     if(vec.size() == 0)
636         return;
637     int size= vec.size() - 1;
638     int cIter= 0;
639     // sort for version
640     for(int i= 0; i < size; i++)
641     {
642         for(int j= size; j > 0 + cIter; j--)
643         {
644             rtl::Reference<VendorBase>& cur= vec.at(j);
645             rtl::Reference<VendorBase>& next= vec.at(j-1);
646 
647             int nCmp = 0;
648             // comparing invalid SunVersion s is possible, they will be less than a
649             // valid version
650 
651 			//check if version of current is recognized, by comparing it with itself
652             try
653             {
654                 cur->compareVersions(cur->getVersion());
655             }
656             catch (MalformedVersionException &)
657             {
658                 nCmp = -1; // current < next
659             }
660             //The version of cur is valid, now compare with the second version
661             if (nCmp == 0)
662             {
663                 try
664                 {
665                     nCmp = cur->compareVersions(next->getVersion());
666                 }
667                 catch (MalformedVersionException & )
668                 {
669                     //The second version is invalid, therefor it is regardes less.
670                     nCmp = 1;
671                 }
672             }
673             if(nCmp == 1) // cur > next
674             {
675                 rtl::Reference<VendorBase> less = next;
676                 vec.at(j-1)= cur;
677                 vec.at(j)= less;
678             }
679         }
680         cIter++;
681     }
682 }
683 
684 
getJREInfoFromBinPath(const rtl::OUString & path,vector<rtl::Reference<VendorBase>> & vecInfos)685 bool getJREInfoFromBinPath(
686     const rtl::OUString& path, vector<rtl::Reference<VendorBase> > & vecInfos)
687 {
688     // file:///c:/jre/bin
689     //map:       jre/bin/java.exe
690     bool ret = false;
691     vector<pair<OUString, OUString> > props;
692 
693     for ( sal_Int32 pos = 0;
694           gVendorMap[pos].sVendorName != NULL; ++pos )
695     {
696         vector<OUString> vecPaths;
697         getJavaExePaths_func pFunc = gVendorMap[pos].getJavaFunc;
698 
699         int size = 0;
700         char const* const* arExePaths = (*pFunc)(&size);
701         vecPaths = getVectorFromCharArray(arExePaths, size);
702 
703         //make sure argument path does not end with '/'
704         OUString sBinPath = path;
705         if (path.lastIndexOf('/') == (path.getLength() - 1))
706             sBinPath = path.copy(0, path.getLength() - 1);
707 
708         typedef vector<OUString>::const_iterator c_it;
709         for (c_it i = vecPaths.begin(); i != vecPaths.end(); i++)
710         {
711             //the map contains e.g. jre/bin/java.exe
712             //get the directory where the executable is contained
713             OUString sHome;
714             sal_Int32 index = i->lastIndexOf('/');
715             if (index == -1)
716             {
717                 //map contained only : "java.exe, then the argument
718                 //path is already the home directory
719                 sHome = sBinPath;
720             }
721             else
722             {
723                 // jre/bin/jre -> jre/bin
724                 OUString sMapPath(i->getStr(), index);
725                 index = sBinPath.lastIndexOf(sMapPath);
726                 if (index != -1
727                     && (index + sMapPath.getLength() == sBinPath.getLength())
728                     && sBinPath[index - 1] == '/')
729                 {
730                     sHome = OUString(sBinPath.getStr(), index - 1);
731                 }
732             }
733             if (sHome.getLength() > 0)
734             {
735                 ret = getJREInfoByPath(sHome, vecInfos);
736                 if (ret)
737                     break;
738             }
739         }
740         if (ret)
741             break;
742     }
743     return ret;
744 }
745 
getAllJREInfos()746 vector<Reference<VendorBase> > getAllJREInfos()
747 {
748     vector<Reference<VendorBase> > vecInfos;
749 
750 #if defined WNT
751     // Get Javas from the registry
752     createJavaInfoFromWinReg(vecInfos);
753 #endif // WNT
754 
755     createJavaInfoFromJavaHome(vecInfos);
756     //this function should be called after createJavaInfoDirScan.
757     //Otherwise in SDKs Java may be started twice
758  	createJavaInfoFromPath(vecInfos);
759 
760 #ifdef UNX
761     createJavaInfoDirScan(vecInfos);
762 #endif
763 
764     bubbleSortVersion(vecInfos);
765     return vecInfos;
766 }
767 
768 
getVectorFromCharArray(char const * const * ar,int size)769 vector<OUString> getVectorFromCharArray(char const * const * ar, int size)
770 {
771     vector<OUString> vec;
772     for( int i = 0; i < size; i++)
773     {
774         OUString s(ar[i], strlen(ar[i]), RTL_TEXTENCODING_UTF8);
775         vec.push_back(s);
776     }
777     return vec;
778 }
getJREInfoByPath(const rtl::OUString & path,std::vector<rtl::Reference<VendorBase>> & vecInfos)779 bool getJREInfoByPath(const rtl::OUString& path,
780                       std::vector<rtl::Reference<VendorBase> > & vecInfos)
781 {
782     bool ret = false;
783 
784     rtl::Reference<VendorBase> aInfo = getJREInfoByPath(path);
785     if (aInfo.is())
786     {
787         ret = true;
788         vector<rtl::Reference<VendorBase> >::const_iterator it_impl= std::find_if(
789             vecInfos.begin(),vecInfos.end(), InfoFindSame(aInfo->getHome()));
790         if(it_impl == vecInfos.end())
791         {
792             vecInfos.push_back(aInfo);
793         }
794     }
795     return ret;
796 }
797 
798 /** Checks if the path is a directory. Links are resolved.
799     In case of an error the returned string has the length 0.
800     Otherwise the returned string is the "resolved" file URL.
801  */
resolveDirPath(const OUString & path)802 OUString resolveDirPath(const OUString & path)
803 {
804     OUString ret;
805     OUString sResolved;
806     //getAbsoluteFileURL also resolves links
807     if (File::getAbsoluteFileURL(
808             OUSTR("file:///"), path, sResolved) != File::E_None)
809         return OUString();
810 
811     //check if this is a valid path and if it is a directory
812     DirectoryItem item;
813     if (DirectoryItem::get(sResolved, item) == File::E_None)
814     {
815         FileStatus status(FileStatusMask_Type |
816                           FileStatusMask_LinkTargetURL |
817                           FileStatusMask_FileURL);
818 
819         if (item.getFileStatus(status) == File::E_None
820             && status.getFileType() == FileStatus::Directory)
821         {
822             ret = sResolved;
823         }
824     }
825     else
826         return OUString();
827     return ret;
828 }
829 /** Checks if the path is a file. If it is a link to a file than
830     it is resolved.
831  */
resolveFilePath(const OUString & path)832 OUString resolveFilePath(const OUString & path)
833 {
834     OUString ret;
835     OUString sResolved;
836 
837     if (File::getAbsoluteFileURL(
838             OUSTR("file:///"), path, sResolved) != File::E_None)
839         return OUString();
840 
841     //check if this is a valid path to a file or and if it is a link
842     DirectoryItem item;
843     if (DirectoryItem::get(sResolved, item) == File::E_None)
844     {
845         FileStatus status(FileStatusMask_Type |
846                           FileStatusMask_LinkTargetURL |
847                           FileStatusMask_FileURL);
848         if (item.getFileStatus(status) == File::E_None
849             && status.getFileType() == FileStatus::Regular)
850         {
851             ret = sResolved;
852         }
853     }
854     else
855         return OUString();
856 
857     return ret;
858 }
859 
getJREInfoByPath(const OUString & path)860 rtl::Reference<VendorBase> getJREInfoByPath(
861     const OUString& path)
862 {
863     rtl::Reference<VendorBase> ret;
864     static vector<OUString> vecBadPaths;
865 
866     static map<OUString, rtl::Reference<VendorBase> > mapJREs;
867     typedef map<OUString, rtl::Reference<VendorBase> >::const_iterator MapIt;
868     typedef map<OUString, rtl::Reference<VendorBase> > MAPJRE;
869     OUString sFilePath;
870     typedef vector<OUString>::const_iterator cit_path;
871     vector<pair<OUString, OUString> > props;
872 
873     OUString sResolvedDir = resolveDirPath(path);
874     // If this path is invalid then there is no chance to find a JRE here
875     if (sResolvedDir.getLength() == 0)
876         return 0;
877 
878     //check if the directory path is good, that is a JRE was already recognized.
879     //Then we need not detect it again
880     //For example, a sun JKD contains <jdk>/bin/java and <jdk>/jre/bin/java.
881     //When <jdk>/bin/java has been found then we need not find <jdk>/jre/bin/java.
882     //Otherwise we would execute java two times for evers JDK found.
883     MapIt entry2 = find_if(mapJREs.begin(), mapJREs.end(),
884                            SameOrSubDirJREMap(sResolvedDir));
885     if (entry2 != mapJREs.end())
886     {
887         JFW_TRACE2(OUSTR("[Java framework] sunjavaplugin" SAL_DLLEXTENSION ": ")
888                    + OUSTR("JRE found again (detected before): ") + sResolvedDir
889                    + OUSTR(".\n"));
890         return entry2->second;
891     }
892 
893     for ( sal_Int32 pos = 0;
894           gVendorMap[pos].sVendorName != NULL; ++pos )
895     {
896         vector<OUString> vecPaths;
897         getJavaExePaths_func pFunc = gVendorMap[pos].getJavaFunc;
898 
899         int size = 0;
900         char const* const* arExePaths = (*pFunc)(&size);
901         vecPaths = getVectorFromCharArray(arExePaths, size);
902 
903         bool bBreak = false;
904         typedef vector<OUString>::const_iterator c_it;
905         for (c_it i = vecPaths.begin(); i != vecPaths.end(); i++)
906         {
907             //if the path is a link, then resolve it
908             //check if the executable exists at all
909 
910             //path can be only "file:///". Then do not append a '/'
911             //sizeof counts the terminating 0
912             OUString sFullPath;
913             if (path.getLength() == sizeof("file:///") - 1)
914                 sFullPath = sResolvedDir + (*i);
915             else
916                 sFullPath = sResolvedDir +
917                 OUString(RTL_CONSTASCII_USTRINGPARAM("/")) + (*i);
918 
919 
920             sFilePath = resolveFilePath(sFullPath);
921 
922             if (sFilePath.getLength() == 0)
923             {
924                 //The file path (to java exe) is not valid
925                 cit_path ifull = find(vecBadPaths.begin(), vecBadPaths.end(), sFullPath);
926                 if (ifull == vecBadPaths.end())
927                     vecBadPaths.push_back(sFullPath);
928                 continue;
929             }
930 
931             cit_path ifile = find(vecBadPaths.begin(), vecBadPaths.end(), sFilePath);
932             if (ifile != vecBadPaths.end())
933                 continue;
934 
935             MapIt entry =  mapJREs.find(sFilePath);
936             if (entry != mapJREs.end())
937             {
938                 JFW_TRACE2(OUSTR("[Java framework] sunjavaplugin" SAL_DLLEXTENSION ": ")
939                    + OUSTR("JRE found again (detected before): ") + sFilePath
940                    + OUSTR(".\n"));
941 
942                 return entry->second;
943             }
944 
945             bool bProcessRun= false;
946             if (getJavaProps(sFilePath, props, & bProcessRun) == false)
947             {
948                 //The java executable could not be run or the system properties
949                 //could not be retrieved. We can assume that this java is corrupt.
950                 vecBadPaths.push_back(sFilePath);
951                 //If there was a java executable, that could be run but we did not get
952                 //the system properties, then we also assume that the whole Java installation
953                 //does not work. In a jdk there are two executables. One in jdk/bin and the other
954                 //in jdk/jre/bin. We do not search any further, because we assume that if one java
955                 //does not work then the other does not work as well. This saves us to run java
956                 //again which is quite costly.
957                 if (bProcessRun == true)
958                 {
959                     // 1.3.1 special treatment: jdk/bin/java and /jdk/jre/bin/java are links to
960                     //a script, named .java_wrapper. The script starts jdk/bin/sparc/native_threads/java
961                     //or jdk/jre/bin/sparc/native_threads/java. The script uses the name with which it was
962                     //invoked to build the path to the executable. It we start the script directy as .java_wrapper
963                     //then it tries to start a jdk/.../native_threads/.java_wrapper. Therefore the link, which
964                     //is named java, must be used to start the script.
965                     getJavaProps(sFullPath, props, & bProcessRun);
966                     // Either we found a working 1.3.1
967                     //Or the java is broken. In both cases we stop searchin under this "root" directory
968                     bBreak = true;
969                     break;
970                 }
971                 //sFilePath is no working java executable. We continue with another possible
972                 //path.
973                 else
974                 {
975                     continue;
976                 }
977             }
978             //sFilePath is a java and we could get the system properties. We proceed with this
979             //java.
980             else
981             {
982                 bBreak = true;
983                 break;
984             }
985         }
986         if (bBreak)
987             break;
988     }
989 
990     if (props.size() == 0)
991         return rtl::Reference<VendorBase>();
992 
993     //find java.vendor property
994     typedef vector<pair<OUString, OUString> >::const_iterator c_ip;
995     OUString sVendor(RTL_CONSTASCII_USTRINGPARAM("java.vendor"));
996     OUString sVendorName;
997 
998     for (c_ip i = props.begin(); i != props.end(); i++)
999     {
1000         if (sVendor.equals(i->first))
1001         {
1002             sVendorName = i->second;
1003             break;
1004         }
1005     }
1006 
1007     if (sVendorName.getLength() > 0)
1008     {
1009         //find the creator func for the respective vendor name
1010         for ( sal_Int32 c = 0;
1011               gVendorMap[c].sVendorName != NULL; ++c )
1012         {
1013             OUString sNameMap(gVendorMap[c].sVendorName, strlen(gVendorMap[c].sVendorName),
1014                               RTL_TEXTENCODING_ASCII_US);
1015             if (sNameMap.equals(sVendorName))
1016             {
1017                 ret = createInstance(gVendorMap[c].createFunc, props);
1018                 break;
1019             }
1020         }
1021         if ( !ret.is() )
1022         {
1023             // As of Java 8u222 the vendor name can be anything, so try the most generic one
1024             OUString genericVendorName(RTL_CONSTASCII_USTRINGPARAM("OpenJDK"));
1025             for ( sal_Int32 c = 0;
1026                   gVendorMap[c].sVendorName != NULL; ++c )
1027             {
1028                 OUString sNameMap(gVendorMap[c].sVendorName, strlen(gVendorMap[c].sVendorName),
1029                                   RTL_TEXTENCODING_ASCII_US);
1030                 if (sNameMap.equals(genericVendorName))
1031                 {
1032                     for (vector<pair<OUString, OUString> >::iterator i = props.begin(); i != props.end(); i++)
1033                     {
1034                         if (sVendor.equals(i->first))
1035                             i->second = genericVendorName;
1036                     }
1037                     ret = createInstance(gVendorMap[c].createFunc, props);
1038                     break;
1039                 }
1040             }
1041         }
1042     }
1043     if (ret.is() == false)
1044         vecBadPaths.push_back(sFilePath);
1045     else
1046     {
1047         JFW_TRACE2(OUSTR("[Java framework] sunjavaplugin" SAL_DLLEXTENSION ": ")
1048                    + OUSTR("Found JRE: ") + sResolvedDir
1049                    + OUSTR(" \n at: ") + path + OUSTR(".\n"));
1050 
1051         mapJREs.insert(MAPJRE::value_type(sResolvedDir, ret));
1052         mapJREs.insert(MAPJRE::value_type(sFilePath, ret));
1053     }
1054 
1055     return ret;
1056 }
1057 
createInstance(createInstance_func pFunc,vector<pair<OUString,OUString>> properties)1058 Reference<VendorBase> createInstance(createInstance_func pFunc,
1059                                      vector<pair<OUString, OUString> > properties)
1060 {
1061 
1062     Reference<VendorBase> aBase = (*pFunc)();
1063     if (aBase.is())
1064     {
1065         if (aBase->initialize(properties) == false)
1066             aBase = 0;
1067     }
1068     return aBase;
1069 }
1070 
getDirFromFile(const OUString & usFilePath)1071 inline OUString getDirFromFile(const OUString& usFilePath)
1072 {
1073     sal_Int32 index= usFilePath.lastIndexOf('/');
1074     return OUString(usFilePath.getStr(), index);
1075 }
1076 
createJavaInfoFromPath(vector<rtl::Reference<VendorBase>> & vecInfos)1077 void createJavaInfoFromPath(vector<rtl::Reference<VendorBase> >& vecInfos)
1078 {
1079 // Get Java from PATH environment variable
1080     static OUString sCurDir(RTL_CONSTASCII_USTRINGPARAM("."));
1081     static OUString sParentDir(RTL_CONSTASCII_USTRINGPARAM(".."));
1082     char *szPath= getenv("PATH");
1083     if(szPath)
1084     {
1085         OUString usAllPath(szPath, strlen(szPath), osl_getThreadTextEncoding());
1086         sal_Int32 nIndex = 0;
1087         do
1088         {
1089             OUString usToken = usAllPath.getToken( 0, SAL_PATHSEPARATOR, nIndex );
1090             OUString usTokenUrl;
1091             if(File::getFileURLFromSystemPath(usToken, usTokenUrl) == File::E_None)
1092             {
1093                 if(usTokenUrl.getLength())
1094                 {
1095                     OUString usBin;
1096                     // "."
1097                     if(usTokenUrl.equals(sCurDir))
1098                     {
1099                         OUString usWorkDirUrl;
1100                         if(osl_Process_E_None == osl_getProcessWorkingDir(&usWorkDirUrl.pData))
1101                             usBin= usWorkDirUrl;
1102                     }
1103                     // ".."
1104                     else if(usTokenUrl.equals(sParentDir))
1105                     {
1106                         OUString usWorkDir;
1107                         if(osl_Process_E_None == osl_getProcessWorkingDir(&usWorkDir.pData))
1108                             usBin= getDirFromFile(usWorkDir);
1109                     }
1110                     else
1111                     {
1112                         usBin = usTokenUrl;
1113                     }
1114                     if(usBin.getLength())
1115                     {
1116                         getJREInfoFromBinPath(usBin, vecInfos);
1117                     }
1118                 }
1119             }
1120         }
1121         while ( nIndex >= 0 );
1122     }
1123 }
1124 
createJavaInfoFromJavaHome(vector<rtl::Reference<VendorBase>> & vecInfos)1125 void createJavaInfoFromJavaHome(vector<rtl::Reference<VendorBase> >& vecInfos)
1126 {
1127     // Get Java from JAVA_HOME environment
1128     char *szJavaHome= getenv("JAVA_HOME");
1129     if(szJavaHome)
1130     {
1131         OUString sHome(szJavaHome,strlen(szJavaHome),osl_getThreadTextEncoding());
1132         OUString sHomeUrl;
1133         if(File::getFileURLFromSystemPath(sHome, sHomeUrl) == File::E_None)
1134         {
1135             getJREInfoByPath(sHomeUrl, vecInfos);
1136         }
1137     }
1138 }
1139 
makeDriveLetterSame(OUString * fileURL)1140 bool makeDriveLetterSame(OUString * fileURL)
1141 {
1142     bool ret = false;
1143     DirectoryItem item;
1144     if (DirectoryItem::get(*fileURL, item) == File::E_None)
1145     {
1146         FileStatus status(FileStatusMask_FileURL);
1147         if (item.getFileStatus(status) == File::E_None)
1148         {
1149             *fileURL = status.getFileURL();
1150             ret = true;
1151         }
1152     }
1153     return ret;
1154 }
1155 
1156 #ifdef UNX
1157 #ifdef SOLARIS
1158 
createJavaInfoDirScan(vector<rtl::Reference<VendorBase>> & vecInfos)1159 void createJavaInfoDirScan(vector<rtl::Reference<VendorBase> >& vecInfos)
1160 {
1161     JFW_TRACE2(OUSTR("\n[Java framework] Checking \"/usr/jdk/latest\"\n"));
1162     getJREInfoByPath(OUSTR("file:////usr/jdk/latest"), vecInfos);
1163 }
1164 
1165 #else
createJavaInfoDirScan(vector<rtl::Reference<VendorBase>> & vecInfos)1166 void createJavaInfoDirScan(vector<rtl::Reference<VendorBase> >& vecInfos)
1167 {
1168     OUString excMessage = OUSTR("[Java framework] sunjavaplugin: "
1169                                 "Error in function createJavaInfoDirScan in util.cxx.");
1170     int cJavaNames= sizeof(g_arJavaNames) / sizeof(char*);
1171     boost::scoped_array<OUString> sarJavaNames(new OUString[cJavaNames]);
1172     OUString *arNames = sarJavaNames.get();
1173     for(int i= 0; i < cJavaNames; i++)
1174         arNames[i] = OUString(g_arJavaNames[i], strlen(g_arJavaNames[i]),
1175                               RTL_TEXTENCODING_UTF8);
1176 
1177     int cSearchPaths= sizeof(g_arSearchPaths) / sizeof(g_arSearchPaths[0]);
1178     boost::scoped_array<OUString> sarPathNames(new OUString[cSearchPaths]);
1179     OUString *arPaths = sarPathNames.get();
1180     for(int c = 0; c < cSearchPaths; c++)
1181         arPaths[c] = OUString(g_arSearchPaths[c].path, strlen(g_arSearchPaths[c].path),
1182                                RTL_TEXTENCODING_UTF8);
1183 
1184     int cCollectDirs = sizeof(g_arCollectDirs) / sizeof(char*);
1185     boost::scoped_array<OUString> sarCollectDirs(new OUString[cCollectDirs]);
1186     OUString *arCollectDirs = sarCollectDirs.get();
1187     for(int d = 0; d < cCollectDirs; d++)
1188         arCollectDirs[d] = OUString(g_arCollectDirs[d], strlen(g_arCollectDirs[d]),
1189                                RTL_TEXTENCODING_UTF8);
1190 
1191 
1192 
1193     OUString usFile(RTL_CONSTASCII_USTRINGPARAM("file:///"));
1194     for( int ii = 0; ii < cSearchPaths; ii ++)
1195     {
1196         OUString usDir1(usFile + arPaths[ii]);
1197         DirectoryItem item;
1198         if(DirectoryItem::get(usDir1, item) == File::E_None)
1199         {
1200             for(int j= 0; j < cCollectDirs; j++)
1201             {
1202                 OUString usDir2(usDir1 + arCollectDirs[j]);
1203                 // prevent that we scan the whole /usr/bin, /usr/lib, etc directories
1204                 if (g_arSearchPaths[ii].searchImmediateContents || arCollectDirs[j] != OUString())
1205                 {
1206                     //usr/java/xxx
1207                     //Examin every subdirectory
1208                     Directory aCollectionDir(usDir2);
1209 
1210                     Directory::RC openErr = aCollectionDir.open();
1211                     switch (openErr)
1212                     {
1213                     case File::E_None:
1214                         break;
1215                     case File::E_NOENT:
1216                     case File::E_NOTDIR:
1217                         continue;
1218                     case File::E_ACCES:
1219                         JFW_TRACE2(OUSTR("[Java framework] sunjavaplugin: "
1220                                          "Could not read directory ") + usDir2 +
1221                                    OUSTR(" because of missing access rights."));
1222                         continue;
1223                     default:
1224                         JFW_TRACE2(OUSTR("[Java framework] sunjavaplugin: "
1225                                          "Could not read directory ")
1226                                    + usDir2 + OUSTR(". Osl file error: ")
1227                                    + OUString::valueOf((sal_Int32) openErr));
1228                         continue;
1229                     }
1230 
1231                     DirectoryItem curIt;
1232                     File::RC errNext = File::E_None;
1233                     while( (errNext = aCollectionDir.getNextItem(curIt)) == File::E_None)
1234                     {
1235                         FileStatus aStatus(FileStatusMask_FileURL);
1236                         File::RC errStatus = File::E_None;
1237                         if ((errStatus = curIt.getFileStatus(aStatus)) != File::E_None)
1238                         {
1239                             JFW_TRACE2(excMessage + OUSTR("getFileStatus failed with error ")
1240                                 + OUString::valueOf((sal_Int32) errStatus));
1241                             continue;
1242                         }
1243                         JFW_TRACE2(OUSTR("[Java framework] sunjavaplugin: "
1244                                          "Checking if directory: ") + aStatus.getFileURL() +
1245                                    OUSTR(" is a Java. \n"));
1246 
1247                         getJREInfoByPath(aStatus.getFileURL(),vecInfos);
1248                     }
1249 
1250                     JFW_ENSURE(errNext == File::E_None || errNext == File::E_NOENT,
1251                                 OUSTR("[Java framework] sunjavaplugin: "
1252                                       "Error while iterating over contens of ")
1253                                 + usDir2 + OUSTR(". Osl file error: ")
1254                                 + OUString::valueOf((sal_Int32) openErr));
1255                 }
1256             }
1257             for (int j= 0; j < cJavaNames; j++)
1258             {
1259                 //When we look directly into a dir like /usr/bin, /usr/lib, etc. then we only
1260                 //look for certain java directories, such as jre, jdk, etc. Whe do not want
1261                 //to examine the whole directory because of performance reasons.
1262 
1263                 //  usFile          arNames[j]
1264                 // <------>        <->
1265                 // file:///usr/lib/jvm
1266                 //         <------>
1267                 //          arPaths[ii] (usDir1)
1268                 //
1269                 OUString usDir3(usDir1 + arNames[j]);
1270 
1271                 DirectoryItem item3;
1272                 if(DirectoryItem::get(usDir3, item3) == File::E_None)
1273                 {
1274                     //remove trailing '/'
1275                     sal_Int32 islash = usDir3.lastIndexOf('/');
1276                     if (islash == usDir3.getLength() - 1
1277                         && (islash
1278                             > RTL_CONSTASCII_LENGTH("file://")))
1279                         usDir3 = usDir3.copy(0, islash);
1280                     getJREInfoByPath(usDir3,vecInfos);
1281                 }
1282             }
1283         }
1284     }
1285 }
1286 #endif // ifdef SOLARIS
1287 #endif // ifdef UNX
1288 }
1289