Index: /release-kits/shared/search4j/compile.bat
===================================================================
--- /release-kits/shared/search4j/compile.bat	(revision 15101)
+++ /release-kits/shared/search4j/compile.bat	(revision 15101)
@@ -0,0 +1,2 @@
+cl /c -GX search4j.cpp 
+link advapi32.lib search4j.obj /out:search4j.exe 
Index: /release-kits/shared/search4j/search4j.cpp
===================================================================
--- /release-kits/shared/search4j/search4j.cpp	(revision 15101)
+++ /release-kits/shared/search4j/search4j.cpp	(revision 15101)
@@ -0,0 +1,522 @@
+/*
+* Search4j utility
+* Command Line utility to search a system for java, launch a jar, or compare java versions
+* base on launch4j
+*/
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <windows.h>
+using namespace std;
+
+
+//minimal launch4j stuff
+#define NO_JAVA_FOUND 0
+#define FOUND_JRE 1
+#define FOUND_SDK 2
+int foundJava = NO_JAVA_FOUND;
+char foundJavaVer[128] = {0};
+
+
+//global variables
+bool verbose = false;
+bool use_minimum = false;
+
+enum JvmProperty {
+	JavaHome,
+	Version,
+	Executable
+};
+
+enum Action {
+	Find,
+	Launch,
+	Compare
+};
+
+void replace_all ( std::string & str, std::string const & pattern, std::string const & replacement ) {
+
+	std::string::size_type start = str.find( pattern, 0 );
+
+	while ( start != str.npos ) {
+		str.replace( start, pattern.size(), replacement );
+		start = str.find( pattern, start+replacement.size() );
+	}
+
+}
+
+int process( string command, string &output ) {
+	
+	FILE   *pPipe;
+
+	/* Run command so that it writes its output to a pipe. Open this
+	* pipe with read text attribute so that we can read it 
+	* like a text file. */
+	char cmd[1024]; strcpy(cmd, command.c_str());
+	//if ( verbose ) cout << "process(): running '" << command << "'" << endl;
+	if( (pPipe = _popen( cmd, "rt" )) == NULL ) {
+		if ( verbose ) cout << "could not start process" << endl;
+		_pclose( pPipe );
+		return -1;
+	}
+	//if ( verbose ) cout << "started process" << endl;
+
+	/* Read pipe until end of file. */
+	while( !feof( pPipe ) ) {
+		char psBuffer[128];
+		//if ( verbose ) cout << "get some data" << endl;
+		if( fgets( psBuffer, 128, pPipe ) != NULL ) {
+			
+			//if ( verbose ) cout << "got: " << psBuffer << endl;
+			output.append( psBuffer );
+			
+		} else {
+			//if ( verbose ) cout << "none left" << endl;
+		}
+		
+	}
+
+	/* Close pipe and return return value of pPipe. */
+	int code = _pclose( pPipe );
+	return code;
+}
+
+class Jvm {
+
+	string javaHome;
+	
+	// version string. era.major.minor_update. eg: 1.4.2_02
+	int era;
+	int major;
+	int minor;
+	int update;
+
+	bool isJdk;
+	bool healthy;	
+
+
+	public:
+	Jvm() {
+		healthy = false;
+	}
+
+	void setJavaHome( string jh ) {
+		healthy = true;
+		javaHome = jh;
+		setVersionFromExeOuput();
+	}
+
+	string getJavaHome() {
+		return javaHome;
+	}
+
+	string getExecutable() {
+		string exec = "";
+		exec.append( javaHome );
+		exec.append( "\\bin\\java.exe" );
+		return exec;
+	}
+
+	string getVersion() {
+		stringstream ss;
+		ss << era << "." << major << "." << minor << "_" << update;
+		return ss.str();
+	}
+	
+	bool check() {
+		return healthy;
+	}
+
+	bool setVersionFromString( string version ) {
+		era = atoi( version.substr(0,1).c_str() );
+		major = atoi( version.substr(2,1).c_str() );
+		minor = atoi( version.substr(4,1).c_str() );
+		update = atoi( version.substr(6,2).c_str() );
+		return true;
+	}
+	
+	int compare( Jvm otherJvm ) {
+		//era
+		if ( era > otherJvm.getEra() )
+			return 1;
+		else if ( era < otherJvm.getEra() )
+			return -1;
+		
+		//major
+		if ( major > otherJvm.getMajor() )
+			return 1;
+		else if ( major < otherJvm.getMajor() )
+			return -1;
+			
+		//minor
+		if ( minor > otherJvm.getMinor() )
+			return 1;
+		else if ( minor < otherJvm.getMinor() )
+			return -1;
+
+		//update
+		if ( update > otherJvm.getUpdate() )
+			return 1;
+		else if ( update < otherJvm.getUpdate() )
+			return -1;
+
+		//all the same so far, must be exactly the same
+		return 0;
+
+	}
+	
+	int getEra() { return era; }
+	int getMajor() { return major; }
+	int getMinor() { return minor; }
+	int getUpdate() { return update; }
+	
+	private:
+	
+	void setVersionFromExeOuput() {
+		string command = "", output = "";
+		command.append( "\"" );
+		command.append( getExecutable() );
+		command.append( "\"" );
+		command.append(" -version 2>&1");
+		int result = process( command, output );
+		if ( result == 0 ) {
+			if ( _stricmp( output.substr( 0, 12 ).c_str() , "java version" ) == 0 || true ) {
+				era = atoi( output.substr(14,1).c_str() );
+				major = atoi( output.substr(16,1).c_str() );
+				minor = atoi( output.substr(18,1).c_str() );
+				update = atoi( output.substr(20,2).c_str() );
+				//if ( verbose) cout << "set version to: " << era << "." << major << "." << minor << "_" << update << endl;
+			} else {
+				healthy=false;
+			}
+		} else {
+			healthy = false;
+		}
+	}
+	
+	
+};
+
+string actionToString( Action a ) {
+	if ( a == Find ) 
+		return "Find";
+	else if ( a == Launch )
+		return "Launch";
+	else if ( a == Compare )
+		return "Compare";
+	return "Unknown";
+}
+
+string jvmPropertyToString( JvmProperty jp ) {
+	if ( jp == JavaHome ) 
+		return "JavaHome";
+	else if ( jp == Version )
+		return "Version";
+	else if ( jp == Executable )
+		return "Executable";
+	return "Unknown";
+}
+
+void usage() {
+	
+	cout
+		<< "-----------------" << endl
+		<< " search4j: usage" << endl
+		<< "-----------------" << endl
+		<< "Three usage methods: find, compare and launch" << endl
+		<< endl
+		<< "find:     find java and print out information about it" << endl
+		<< endl
+		<< "          search4j [-v|-e] [-m VERSION_STRING]" << endl
+		<< "          eg: search4j -m 1.4.1_01 -e" << endl
+		<< endl
+		<< "          by default, print JAVA_HOME. E.g., C:\\Program Files\\jre1.5.0_15, or" << endl
+		<< "          if -v is specified, print the java version string. E.g. 1.5.0_15, or" << endl
+		<< "          if -e is specified, print the path the to the java executable. E.g. C:\\Program Files\\jre1.5.0_15\\bin\\java.exe" << endl
+		<< "          if -m option is used, find a java of the given version or newer, or fail" << endl
+		<< endl
+		<< "compare:  compare the found java with the given java version string" << endl
+		<< endl
+		<< "          search4j -c VERSION_STRING" << endl
+		<< "          eg: search4j -c VERSION_STRING" << endl
+		<< endl
+		<< "          print -1 if found java is older" << endl
+		<< "          print 0 if found java is same" << endl
+		<< "          print 1 if found java is newer" << endl
+		<< endl
+		<< "launch:   launch the given executable jar with the found java" << endl
+		<< endl
+		<< "          search4j -l EXECUTABLE_JAR [-m VERSION_STRING]" << endl
+		<< "          eg: search4j -l greenstone3.jar" << endl
+		<< endl
+		<< "          specify the location of the jar relative to the current directory" << endl
+		<< "          if -m option is used, find a java of the given version or newer" << endl
+		<< endl
+		<< "Global Options:" << endl
+		<< "          --verbose : have search4j print out information about what its doing" << endl
+		<< "          --help : display this usage screen" << endl
+		<< endl
+		;
+}
+
+//another global
+Jvm minimum;
+
+
+void regSearch(HKEY hKey, const char* keyName, int searchType) {
+	DWORD x = 0;
+	unsigned long size = 1024;
+	FILETIME time;
+	char buffer[1024] = {0};
+	while (RegEnumKeyEx(
+				hKey,			// handle to key to enumerate
+				x++,			// index of subkey to enumerate
+				buffer,			// address of buffer for subkey name
+				&size,			// address for size of subkey buffer
+				NULL,			// reserved
+				NULL,			// address of buffer for class string
+				NULL,			// address for size of class buffer
+				&time) == ERROR_SUCCESS) {
+		strcpy(foundJavaVer, buffer);
+		foundJava = searchType;
+		size = 1024;
+	}
+}
+
+
+/*
+* function to find java
+* implements the logic drawn on the dl lab whiteboard in feb 08
+* return a Jvm object which represents the jvm on disk
+*/
+bool find( Jvm &jvm ) {
+	
+	if ( verbose ) cout << "Searching for a JVM" << endl;
+	char *javaHome = "";
+	bool jvmFound = false;
+	
+	if ( !jvmFound ) {
+		//try JAVA_HOME
+		if ( verbose ) cout << " - trying JAVA_HOME: ";
+		javaHome = getenv( "JAVA_HOME" );
+		if ( javaHome != NULL ) {
+			jvm.setJavaHome( javaHome );
+			if ( jvm.check() ) {
+				if ( use_minimum ) {
+					if ( jvm.compare( minimum ) >= 0 ) {
+						jvmFound = true;
+					}
+				} else {
+					jvmFound = true;
+				}
+			}
+		}
+		if ( verbose ) { if( jvmFound ) cout << "yes" << endl; else cout << "no" << endl; }
+	}
+	
+	if ( !jvmFound ) {
+		//try JRE_HOME
+		if ( verbose ) cout << " - trying JRE_HOME: ";
+		javaHome = getenv( "JRE_HOME" );
+		if ( javaHome != NULL ) {
+			jvm.setJavaHome( javaHome );
+			if ( jvm.check() ) {
+				if ( use_minimum ) {
+					if ( jvm.compare( minimum ) >= 0 ) {
+						jvmFound = true;
+					}
+				} else {
+					jvmFound = true;
+				}
+			}
+		}
+		if ( verbose ) { if( jvmFound ) cout << "yes" << endl; else cout << "no" << endl; }
+	}
+	
+	if ( !jvmFound ) {
+		//try the registry - this code based on launch4j code
+		if ( verbose ) cout << " - trying the registry: ";
+		HKEY hKey;
+		const char jre[] = "SOFTWARE\\JavaSoft\\Java Runtime Environment";
+		const char sdk[] = "SOFTWARE\\JavaSoft\\Java Development Kit";
+		
+		if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(jre), 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hKey) == ERROR_SUCCESS ) {
+			regSearch(hKey, jre, FOUND_JRE);
+			RegCloseKey(hKey);
+		}
+		
+		if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(sdk), 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hKey) == ERROR_SUCCESS ) {
+			regSearch(hKey, sdk, FOUND_SDK);
+			RegCloseKey(hKey);
+		}
+		
+		if ( foundJava != NO_JAVA_FOUND ) {
+			char path[1024] = {0};
+			char keyBuffer[1024];
+			unsigned long datatype;
+			unsigned long bufferlength = 1024;
+			if (foundJava == FOUND_JRE)	{
+				strcpy(keyBuffer, jre);
+			} else {
+				strcpy(keyBuffer, sdk);
+			}
+			strcat(keyBuffer, "\\");
+			strcat(keyBuffer, foundJavaVer);
+			if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(keyBuffer), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) {
+				unsigned char buffer[1024] = {0};
+				if (RegQueryValueEx(hKey, "JavaHome", NULL, &datatype, buffer, &bufferlength) == ERROR_SUCCESS) {
+					int i = 0;
+					do {
+						path[i] = buffer[i];
+					} while (path[i++] != 0);
+					if (foundJava == FOUND_SDK) {
+						strcat(path, "\\jre");
+					}
+					jvm.setJavaHome( path );
+					if ( jvm.check() ) {
+						if ( use_minimum ) {
+							if ( jvm.compare( minimum ) >= 0 ) {
+								jvmFound = true;
+							}
+						} else {
+							jvmFound = true;
+						}
+					}
+				}
+				RegCloseKey(hKey);
+			}
+		}
+		if ( verbose ) { if( jvmFound ) cout << "yes" << endl; else cout << "no" << endl; }
+	}
+
+	return jvmFound;
+}
+
+int main ( int argc, char** argv ) {
+	//cout << "testing process()" << endl;
+	//string out = "";
+	//process( "dir", out );
+	//cout << out << endl;
+	//return 0;
+
+
+	JvmProperty jvmProperty = JavaHome;
+	Action action = Find;
+	string arg1 = "";
+	
+	//parse commandline arguments
+	for (int i=1; i<argc; i++) {
+		if ( _stricmp(argv[i], "--verbose") == 0 ) {
+			verbose = true;
+		} else if ( _stricmp(argv[i], "--help") == 0 || _stricmp(argv[i], "--usage") == 0 ) {
+			usage();
+			return 0;
+		} else if ( _stricmp(argv[i], "-v") == 0 ) {
+			jvmProperty = Version;
+		} else if ( _stricmp(argv[i], "-e") == 0 ) {
+			jvmProperty = Executable;
+		} else if ( _stricmp(argv[i], "-c") == 0 ) {
+			action = Compare;
+			if ( i == argc-1 ) {
+				usage();
+				return 0;
+			} else {
+				arg1 = argv[++i];
+			}
+
+			} else if ( _stricmp(argv[i], "-m") == 0 ) {
+			if ( i == argc-1 ) {
+				usage();
+				return 0;
+			} else {
+				if ( !minimum.setVersionFromString( argv[++i] ) ) {
+					usage();
+					return 0;
+				}
+				use_minimum = true;
+			}
+
+		} else if ( _stricmp(argv[i], "-l") == 0 ) {
+			action = Launch;
+			arg1 = argv[++i];
+
+		} else {
+			cout << "unknown option: " << argv[i] << endl;
+			return -1;
+		}
+	}
+	
+	//summarise commandline arguments
+	if ( verbose ) {
+		cout << "Action: " << actionToString( action ) << endl;
+	}
+
+	//find java
+	Jvm foundJvm;
+	bool found = find( foundJvm );
+
+	//check if it was found
+	if ( found ) {
+		if ( verbose ) cout << "Found JVM at '" << foundJvm.getJavaHome() << "'" << endl;
+	} else {
+		//not found - exit with -1
+		if ( verbose ) cout << "JVM not found" << endl;
+		return -1;
+	}
+
+
+	//Find Action
+	//---------------
+	if ( action == Find ) {
+
+		if ( verbose ) cout << "Property to print: " << jvmPropertyToString( jvmProperty ) << endl;		
+
+		//found - print out info about it
+		if ( jvmProperty == JavaHome ) {
+			cout << foundJvm.getJavaHome() << endl;
+		} else if ( jvmProperty == Version ) {
+			cout << foundJvm.getVersion() << endl;
+		} else if ( jvmProperty == Executable ) {
+			cout << foundJvm.getExecutable() << endl;
+		} else {
+			return -1; //should never happen
+		}
+			
+
+	//Compare Action
+	//---------------
+	} else if ( action == Compare ) {
+		
+		if ( verbose ) cout << "Compare to java at: " << arg1 << endl;
+
+		//load a dummy jvm for comparison
+		Jvm givenJvm;
+		if ( !givenJvm.setVersionFromString(arg1) ) {
+			if ( verbose ) cout << "Could not parse that version string" << endl;
+			return -1;
+		}
+		
+		//compare
+		cout << foundJvm.compare( givenJvm );
+		return 0;
+
+		
+	//Launch Action
+	//---------------
+	} else if ( action == Launch ) {
+
+		if ( verbose ) cout << "Jar to launch: " << arg1 << endl;
+		
+		string cmd = "\"", output = "";
+		cmd.append( foundJvm.getExecutable() );
+		cmd.append( "\" -jar " );
+		cmd.append( arg1 );
+		
+		process( cmd, output );
+		
+	}
+	
+	return 0;
+}
