source: main/trunk/search4j/libsearch4j.cpp@ 35698

Last change on this file since 35698 was 35698, checked in by davidb, 3 years ago

search4j extended to try to find 'java/javac' on PATH after all other ways have failed. Tested on Linux with g++. At this point not clear if dirname() (which is POSIX compliant) is available in VisualStudio.

File size: 14.0 KB
Line 
1/*
2* Library of functions for the Search4j utility
3*/
4
5#include "libsearch4j.h"
6
7#ifdef WINDOWS
8//minimal launch4j stuff
9#define NO_JAVA_FOUND 0
10#define FOUND_JRE 1
11#define FOUND_SDK 2
12
13//windows functions
14#define popen _popen
15#define pclose _pclose
16#define strcmp _stricmp
17
18#endif /* WINDOWS */
19
20void replace_all ( std::string & str, std::string const & pattern, std::string const & replacement ) {
21
22 std::string::size_type start = str.find( pattern, 0 );
23
24 while ( start != str.npos ) {
25 str.replace( start, pattern.size(), replacement );
26 start = str.find( pattern, start+replacement.size() );
27 }
28
29}
30
31
32int process( string command, bool render ) {
33
34 #ifdef WINDOWS
35
36 STARTUPINFO si;
37 PROCESS_INFORMATION pi;
38 memset(&pi, 0, sizeof(pi));
39 memset(&si, 0, sizeof(si));
40 si.cb = sizeof(si);
41
42 DWORD dwExitCode = -1;
43 char* cmd = (char*)command.c_str();
44
45 bool result;
46 if ( render ) {
47 result = CreateProcess(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
48 } else {
49 result = CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
50 }
51
52 if ( result ) {
53 WaitForSingleObject(pi.hProcess, INFINITE);
54 GetExitCodeProcess(pi.hProcess, &dwExitCode);
55 CloseHandle(pi.hThread);
56 CloseHandle(pi.hProcess);
57 }
58 return dwExitCode;
59
60 #else
61
62 return system( command.c_str() );
63
64 #endif
65}
66
67
68
69int process_and_catch_output( string command, string& output ) {
70
71 FILE *pPipe;
72
73 char cmd[1024];
74 strcpy(cmd, command.c_str());
75 strcat(cmd, " 2>&1" );
76
77 //if ( verbose ) cout << "process(): running '" << command << "'" << endl;
78#ifdef WINDOWS
79 if( (pPipe = _popen( cmd, "rt" )) == NULL ) {
80 _pclose( pPipe );
81#else
82 if( (pPipe = popen( cmd, "r" )) == NULL ) {
83 pclose( pPipe );
84#endif
85 return -1;
86 }
87 //if ( verbose ) cout << "started process" << endl;
88
89 /* Read pipe until end of file. */
90 while( !feof( pPipe ) ) {
91 char psBuffer[512];
92 //if ( verbose ) cout << "get some data" << endl;
93 if( fgets( psBuffer, sizeof(psBuffer), pPipe ) != NULL ) {
94
95 //if ( verbose ) cout << "got: " << psBuffer << endl;
96 output.append( psBuffer );
97
98 } else {
99 //if ( verbose ) cout << "none left" << endl;
100 }
101
102 }
103
104 /* Close pipe and return return value of pPipe. */
105#ifdef WINDOWS
106 int code = _pclose( pPipe );
107#else
108 int code = pclose( pPipe );
109#endif
110
111 return code;
112
113}
114
115//Jvm class methods
116
117Jvm::Jvm()
118 : era_(1), major_(0), minor_(0), update_(0),
119 javaHome_(),
120 isJdk_(false), healthy_(false)
121{}
122
123Jvm::~Jvm()
124{
125 // javaHome automatically destructed
126}
127
128
129void Jvm::setJavaHome( string jh ) {
130 healthy_ = true;
131 javaHome_ = jh;
132 setVersionFromExeOuput();
133 setIsJdkFromJavacPresence();
134}
135
136string Jvm::getJavaHome() {
137 return javaHome_;
138}
139
140string Jvm::getExecutable() {
141 string exec = "";
142 exec.append( javaHome_ );
143
144 #ifdef WINDOWS
145 exec.append( "\\bin\\java.exe" );
146 #endif
147
148 #ifndef WINDOWS
149 exec.append( "/bin/java" );
150 #endif
151
152 return exec;
153}
154
155#ifdef WINDOWS
156string Jvm::getWinExecutable() {
157 string exec = "";
158 exec.append( javaHome_ );
159 exec.append( "\\bin\\javaw.exe" );
160 return exec;
161}
162#endif
163
164
165string Jvm::getVersion() {
166 stringstream ss;
167 ss << era_ << "." << major_ << "." << minor_ << "_" << update_;
168 return ss.str();
169}
170
171bool Jvm::check() {
172 return healthy_;
173}
174
175bool Jvm::setVersionFromString( string version ) {
176 era_ = atoi( version.substr(0,1).c_str() );
177 if (version[3] == '.') {
178 // Parsing something like 1.8.0_24
179 major_ = atoi( version.substr(2,1).c_str() );
180 if(version.length() >= 5) {
181 minor_ = atoi( version.substr(4,1).c_str() );
182 } else {
183 minor_ = 0;
184 }
185 if(version.length() >= 7) {
186 update_ = atoi( version.substr(6,2).c_str() );
187 } else {
188 update_ = 0;
189 }
190 }
191 else {
192 // Parsing something like 1.8.0_242
193 major_ = atoi( version.substr(2,2).c_str() );
194 if(version.length() >= 6) {
195 minor_ = atoi( version.substr(5,1).c_str() );
196 } else {
197 minor_ = 0;
198 }
199 if(version.length() >= 8) {
200 update_ = atoi( version.substr(7,2).c_str() );
201 } else {
202 update_ = 0;
203 }
204
205 }
206
207 return true;
208}
209
210int Jvm::compare( Jvm otherJvm ) {
211 //era
212 if ( era_ > otherJvm.getEra() )
213 return 1;
214 else if ( era_ < otherJvm.getEra() )
215 return -1;
216
217 //major
218 if ( major_ > otherJvm.getMajor() )
219 return 1;
220 else if ( major_ < otherJvm.getMajor() )
221 return -1;
222
223 //minor
224 if ( minor_ > otherJvm.getMinor() )
225 return 1;
226 else if ( minor_ < otherJvm.getMinor() )
227 return -1;
228
229 //update
230 if ( update_ > otherJvm.getUpdate() )
231 return 1;
232 else if ( update_ < otherJvm.getUpdate() )
233 return -1;
234
235 //all the same so far, must be exactly the same
236 return 0;
237
238}
239
240int Jvm::getEra() { return era_; }
241int Jvm::getMajor() { return major_; }
242int Jvm::getMinor() { return minor_; }
243int Jvm::getUpdate() { return update_; }
244
245bool Jvm::getIsJdk() { return isJdk_; }
246bool Jvm::getIsJre() { return !isJdk_; }
247
248void Jvm::setVersionFromExeOuput() {
249 string command = "", output = "";
250 command.append( "\"" );
251 command.append( getExecutable() );
252 command.append( "\"" );
253 command.append(" -version");
254
255 //cerr << "command: " << command << endl;
256 int result = process_and_catch_output( command, output );
257 //cerr << "output: " << output << endl;
258
259 if ( result == 0 ) {
260 // 22 is the length of sentinel string plus typical version value
261 int max_search_length = output.length() - 22;
262 int caret = 0;
263 bool found = false;
264 while (caret < max_search_length && !found) {
265 const int to_match_len = 8;
266 if ( strcmp( output.substr( caret, to_match_len ).c_str() , " version" ) == 0 ) {
267 if (output[caret+to_match_len+3] == '.') {
268 // Version of the form 1.8.0_24
269 era_ = atoi( output.substr(caret + to_match_len + 2,1).c_str() );
270 major_ = atoi( output.substr(caret + to_match_len + 4,1).c_str() );
271 minor_ = atoi( output.substr(caret + to_match_len + 6,1).c_str() );
272 update_ = atoi( output.substr(caret + to_match_len + 8,2).c_str() );
273 }
274 else {
275 // Version of the form 11.0.6
276 era_ = 1;
277 major_ = atoi( output.substr(caret + to_match_len + 2,2).c_str() );
278 minor_ = atoi( output.substr(caret + to_match_len + 5,1).c_str() );
279 update_ = atoi( output.substr(caret + to_match_len + 7,2).c_str() );
280
281 }
282 found = true;
283 }
284 else {
285 caret++;
286 }
287 }
288 if (!found) {
289 healthy_ = false;
290 }
291 }
292 else {
293 healthy_ = false;
294 }
295}
296void Jvm::setIsJdkFromJavacPresence() {
297 string javacFile = "";
298 javacFile.append( javaHome_ );
299
300 #ifdef WINDOWS
301 javacFile.append( "\\bin\\javac.exe" );
302 #endif
303 #ifndef WINDOWS
304 javacFile.append( "/bin/javac" );
305 #endif
306 struct stat stFileInfo;
307 isJdk_ = ( stat(javacFile.c_str(),&stFileInfo) == 0 );
308}
309
310//end of Jvm class methods
311
312
313#ifdef WINDOWS
314int regSearch(HKEY hKey, const char* keyName, int searchType, char* foundJavaVer ) {
315 DWORD x = 0;
316 unsigned long size = 1024;
317 FILETIME time;
318 char buffer[1024] = {0};
319
320 int foundJava = NO_JAVA_FOUND;
321
322 while (RegEnumKeyEx(
323 hKey, // handle to key to enumerate
324 x++, // index of subkey to enumerate
325 buffer, // address of buffer for subkey name
326 &size, // address for size of subkey buffer
327 NULL, // reserved
328 NULL, // address of buffer for class string
329 NULL, // address for size of class buffer
330 &time) == ERROR_SUCCESS) {
331 strcpy(foundJavaVer, buffer);
332 foundJava = searchType;
333 size = 1024;
334 }
335
336 return foundJava;
337
338}
339#endif
340
341/*
342* function to find java
343* implements the logic drawn on the dl lab whiteboard in feb 08
344* return a Jvm object which represents the jvm on disk
345*/
346bool find( Jvm &jvm, bool use_minimum, const Jvm& minimum, bool jreOnly, bool jdkOnly, string phint, string hint, bool verbose ) {
347
348 if ( verbose ) cout << "Searching for a JVM" << endl;
349
350 char *javaHomeEnv = NULL;
351 bool jvmFound = false;
352
353 if ( !jvmFound ) {
354
355 //try the priority hint
356 if ( verbose ) cout << " - trying priority hint: ";
357 if ( strcmp(phint.c_str(),"") != 0 ) {
358 if ( verbose ) cout << "(" << phint << ") ";
359 jvm.setJavaHome( phint );
360 if ( jvm.check() ) {
361 if ( use_minimum ) {
362 if ( jvm.compare( minimum ) >= 0 ) {
363 jvmFound = true;
364 }
365 } else {
366 jvmFound = true;
367 }
368 }
369 if ( (jreOnly && !jvm.getIsJre() ) || (jdkOnly && !jvm.getIsJdk()) ) {
370 jvmFound = false;
371 }
372
373 }
374 if ( verbose ) { if( jvmFound ) cout << "yes" << endl; else cout << "no" << endl; }
375 }
376
377 if ( !jvmFound ) {
378
379 //try JAVA_HOME
380 if ( verbose ) cout << " - trying JAVA_HOME: ";
381 javaHomeEnv = getenv( "JAVA_HOME" );
382 if ( javaHomeEnv != NULL ) {
383 if ( verbose ) cout << "(" << javaHomeEnv << ") ";
384 jvm.setJavaHome( javaHomeEnv );
385 if ( jvm.check() ) {
386 if ( use_minimum ) {
387 if ( jvm.compare( minimum ) >= 0 ) {
388 jvmFound = true;
389 }
390 } else {
391 jvmFound = true;
392 }
393 }
394 if ( (jreOnly && !jvm.getIsJre() ) || (jdkOnly && !jvm.getIsJdk()) ) {
395 jvmFound = false;
396 }
397
398 }
399 if ( verbose ) { if( jvmFound ) cout << "yes" << endl; else cout << "no" << endl; }
400 }
401
402 if ( !jvmFound ) {
403
404 //try JRE_HOME
405 if ( verbose ) cout << " - trying JRE_HOME: ";
406 javaHomeEnv = getenv( "JRE_HOME" );
407 if ( javaHomeEnv != NULL ) {
408 if ( verbose ) cout << "(" << javaHomeEnv << ") ";
409 jvm.setJavaHome( javaHomeEnv );
410 if ( jvm.check() ) {
411 if ( use_minimum ) {
412 if ( jvm.compare( minimum ) >= 0 ) {
413 jvmFound = true;
414 }
415 } else {
416 jvmFound = true;
417 }
418 }
419 if ( (jreOnly && !jvm.getIsJre() ) || (jdkOnly && !jvm.getIsJdk()) ) {
420 jvmFound = false;
421 }
422
423 }
424 if ( verbose ) { if( jvmFound ) cout << "yes" << endl; else cout << "no" << endl; }
425 }
426
427 #ifdef WINDOWS
428 if ( !jvmFound ) {
429
430 //try the registry - this code based on launch4j code
431 char foundJavaVer[8192] = {0};
432 int foundJava = NO_JAVA_FOUND;
433
434 if ( verbose ) cout << " - trying the registry: "; cout.flush();
435 HKEY hKey;
436 const char jre[] = "SOFTWARE\\JavaSoft\\Java Runtime Environment";
437 const char sdk[] = "SOFTWARE\\JavaSoft\\Java Development Kit";
438
439 if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(jre), 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hKey) == ERROR_SUCCESS ) {
440 foundJava = regSearch(hKey, jre, FOUND_JRE, foundJavaVer);
441 RegCloseKey(hKey);
442 }
443
444 if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(sdk), 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hKey) == ERROR_SUCCESS ) {
445 foundJava = regSearch(hKey, sdk, FOUND_SDK, foundJavaVer);
446 RegCloseKey(hKey);
447 }
448
449 if ( foundJava != NO_JAVA_FOUND ) {
450 char path[1024] = {0};
451 char keyBuffer[1024];
452 unsigned long datatype;
453 unsigned long bufferlength = 1024;
454 if (foundJava == FOUND_JRE) {
455 strcpy(keyBuffer, jre);
456 } else {
457 strcpy(keyBuffer, sdk);
458 }
459
460 strcat(keyBuffer, "\\");
461 strcat(keyBuffer, foundJavaVer);
462
463 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(keyBuffer), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) {
464 unsigned char buffer[1024] = {0};
465 if (RegQueryValueEx(hKey, "JavaHome", NULL, &datatype, buffer, &bufferlength) == ERROR_SUCCESS) {
466 int i = 0;
467 do {
468 path[i] = buffer[i];
469 } while (path[i++] != 0);
470 if (foundJava == FOUND_SDK) {
471 strcat(path, "\\jre");
472 }
473 if ( verbose ) cerr << "path: " << path << endl ;
474 jvm.setJavaHome( path );
475 if ( jvm.check() ) {
476 if ( use_minimum ) {
477 if ( jvm.compare( minimum ) >= 0 ) {
478 jvmFound = true;
479 }
480 } else {
481 jvmFound = true;
482 }
483 }
484 if ( (jreOnly && !jvm.getIsJre() ) || (jdkOnly && !jvm.getIsJdk()) ) {
485 jvmFound = false;
486 }
487 }
488 RegCloseKey(hKey);
489 }
490 }
491 if ( verbose ) { if( jvmFound ) cout << "yes" << endl; else cout << "no" << endl; }
492 }
493 #endif
494
495 if ( !jvmFound ) {
496
497 //try the hint
498 if ( verbose ) cout << " - trying hint: ";
499 if ( strcmp(hint.c_str(),"") != 0 ) {
500 if ( verbose ) cout << "(" << hint << ") ";
501 jvm.setJavaHome( hint );
502 if ( jvm.check() ) {
503 if ( use_minimum ) {
504 if ( jvm.compare( minimum ) >= 0 ) {
505 jvmFound = true;
506 }
507 } else {
508 jvmFound = true;
509 }
510 }
511 if ( (jreOnly && !jvm.getIsJre() ) || (jdkOnly && !jvm.getIsJdk()) ) {
512 jvmFound = false;
513 }
514
515 }
516 if ( verbose ) { if( jvmFound ) cout << "yes" << endl; else cout << "no" << endl; }
517 }
518
519
520
521 if ( !jvmFound ) {
522 //look for it on PATH
523 if ( verbose ) cout << " - Looking on PATH (possibly yielding a pseudo, but passable, JAVA_HOME such as /usr): ";
524
525#ifdef WINDOWS
526 // The following is the DOS equivalent to 'which java', however note that
527 // 'where' returns a list of all possible matches (one per line)
528 const char* cmd = "where java 2>nul";
529#else
530 const char* cmd = "which java 2>/dev/null";
531#endif
532 char full_path_java[512] = { '\0' }; // empty string
533
534 // ****
535 // Consider replacing the following code with a call to process_and_catch_output(command, output)
536 // but would need extending to have extra (optional) argument 'only_first_list' which defaults to 'false'
537
538 FILE* PIN = popen(cmd,"r");
539 if (PIN == NULL) {
540 cerr << "Failed to run the command to locate 'java' on PATH" << endl;
541 cerr << "Command run was: " << cmd << endl;
542 }
543 else {
544 // Only need the first line of output
545
546 // Returns number of chars returned. OK to ignore here
547 if (fgets(full_path_java, sizeof(full_path_java), PIN) == NULL) {
548 // Not strictly necessary (as already initialized to be the empty string when declared)
549 // but done to void the warning g++ gives about not checking the return value
550 full_path_java[0] = '\0';
551 }
552 }
553
554 // Not all implmentations of 'which' are coded to return 0 if a program match is found
555 // => safer just to look to see if a non-empty string is returned
556
557 int ignore_exit_val = pclose(PIN);
558
559 if ( strcmp(full_path_java,"") != 0) {
560 // go two directories up from where 'java' was found
561 javaHomeEnv = dirname(dirname(full_path_java));
562
563 // Logic from here same as for other search4j java/javac testing
564 if ( verbose ) cout << "(" << javaHomeEnv << ") ";
565 jvm.setJavaHome( javaHomeEnv );
566 if ( jvm.check() ) {
567 if ( use_minimum ) {
568 if ( jvm.compare( minimum ) >= 0 ) {
569 jvmFound = true;
570 }
571 } else {
572 jvmFound = true;
573 }
574 }
575 if ( (jreOnly && !jvm.getIsJre() ) || (jdkOnly && !jvm.getIsJdk()) ) {
576 jvmFound = false;
577 }
578
579 }
580 if ( verbose ) { if( jvmFound ) cout << "yes" << endl; else cout << "no" << endl; }
581 }
582
583
584 return jvmFound;
585}
Note: See TracBrowser for help on using the repository browser.