// -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- // if this code looks shitty, that's because it is. people are likely to have // the durndest things in their CLASSPATH and QTJAVA environment variables. // mostly because installers often mangle them without the user knowing. // so who knows where and when the quotes will show up. the code below is // based on a couple years of trial and error with processing releases. // For revision 0102, a lot of changes were made to deal with stripping // the quotes from the PATH, CLASSPATH, and QTJAVA environment variables. // Any elements of the PATH and CLASSPATH that don't exist (whether files // or directories) are also stripped out before being set. // (Bug 112) // The size of all of the strings was made sort of ambiguously large, since // 1) nothing is hurt by allocating an extra few bytes temporarily and // 2) if the user has a long path, and it gets copied five times over for the // CLASSPATH, the program runs the risk of crashing. Bad bad. // TODO this code leaks memory all over the place because nothing has been // done to properly handle creation/deletion of new strings. // TODO switch to unicode versions of all methods in order to better support // running on non-English (non-Roman especially) versions of Windows. #define ARGS_FILE_PATH "\\lib\\args.txt" #include #include #include void removeLineEndings(char *what); char *scrubPath(char *incoming); char *mallocChars(int count); void removeQuotes(char *quoted); void removeTrailingSlash(char *slashed); int STDCALL WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmd, int nShow) { // command line that was passed to this application char *incoming_cmd_line = (char *)malloc(strlen(lpCmd + 1) * sizeof(char)); strcpy(incoming_cmd_line, lpCmd); // get the full path to the application that was launched, // drop the app name but keep the path char *exe_directory = (char *)malloc(MAX_PATH * sizeof(char)); //*exe_directory = 0; GetModuleFileName(NULL, exe_directory, MAX_PATH); // remove the application name *(strrchr(exe_directory, '\\')) = '\0'; // open the file that contains the main class name and java args char *args_file_path = (char*) malloc(strlen(exe_directory) * sizeof(char) + strlen(ARGS_FILE_PATH) * sizeof(char) + 1); strcpy(args_file_path, exe_directory); strcat(args_file_path, ARGS_FILE_PATH); char java_args[512]; char java_main_class[512]; char jar_list[512]; char *app_classpath = (char *)malloc(10 * strlen(exe_directory) + 4096); FILE *argsfile = fopen(args_file_path, "r"); if (argsfile == NULL) { sprintf(app_classpath, "This program is missing the \"lib\" folder, " "which should be located at\n%s", exe_directory); MessageBox(NULL, app_classpath, "Folder Missing", MB_OK); return 0; /* sprintf(app_classpath, "%s\\lib;" "%s\\lib\\pde.jar;" "%s\\lib\\core.jar;" "%s\\lib\\jna.jar;" "%s\\lib\\ecj.jar;" "%s\\lib\\antlr.jar;", exe_directory, exe_directory, exe_directory, exe_directory, exe_directory, exe_directory); */ } else { fgets(java_args, 511, argsfile); removeLineEndings(java_args); fgets(java_main_class, 511, argsfile); removeLineEndings(java_main_class); fgets(jar_list, 511, argsfile); removeLineEndings(jar_list); //MessageBox(NULL, java_args, "args", MB_OK); //MessageBox(NULL, java_main_class, "class", MB_OK); //MessageBox(NULL, jar_list, "jarlist", MB_OK); app_classpath[0] = 0; char *jar = (char*) strtok(jar_list, ","); while (jar != NULL) { char entry[1024]; sprintf(entry, "%s\\lib\\%s;", exe_directory, jar); strcat(app_classpath, entry); jar = (char*) strtok(NULL, ","); } fclose(argsfile); } // char *cp = (char *)malloc(10 * strlen(exe_directory) + 4096); // test to see if running with a java runtime nearby or not char *testpath = (char *)malloc(MAX_PATH * sizeof(char)); *testpath = 0; strcpy(testpath, exe_directory); strcat(testpath, "\\java\\bin\\java.exe"); FILE *fp = fopen(testpath, "rb"); int local_jre_installed = (fp != NULL); //char *rt_jar = (fp == NULL) ? "" : "java\\lib\\rt.jar;"; if (fp != NULL) fclose(fp); // argh! this was probably causing trouble //const char *envClasspath = getenv("CLASSPATH"); //char *env_classpath = (char *)malloc(16384 * sizeof(char)); // ignoring CLASSPATH for now, because it's not needed // and causes more trouble than it's worth [0060] //env_classpath[0] = 0; /* // keep this code around since may be re-enabled later if (getenv("CLASSPATH") != NULL) { strcpy(env_classpath, getenv("CLASSPATH")); if (env_classpath[0] == '\"') { // starting quote in classpath.. yech env_classpath++; // shitty.. i know.. int len = strlen(env_classpath); if (env_classpath[len-1] == '\"') { env_classpath[len-1] = 0; } else { // a starting quote but no ending quote.. ugh // maybe throw an error } } int last = strlen(env_classpath); env_classpath[last++] = ';'; env_classpath[last] = 0; } else { env_classpath[0] = 0; } */ char *qtjava_path = (char *)malloc(16384 * sizeof(char)); qtjava_path[0] = 0; if (getenv("QTJAVA") != NULL) { //char *qtjava_temp = (char *)malloc(16384 * sizeof(char)); strcpy(qtjava_path, getenv("QTJAVA")); removeQuotes(qtjava_path); /* //MessageBox(NULL, qtjava_temp, "QTJAVA", MB_OK); if (qtjava_temp[0] == '\"') { // has quotes // remove quotes by subsetting string by two int qtjava_repaired_length = strlen(qtjava_temp) - 2; strncpy(qtjava_path, &qtjava_temp[1], qtjava_repaired_length); // terminate the string since strncpy ain't gonna do it qtjava_path[qtjava_repaired_length] = 0; //MessageBox(NULL, qtjava_path, "QTJAVA", MB_OK); } else { strcpy(qtjava_path, getenv("QTJAVA")); } */ FILE *fp = fopen(qtjava_path, "rb"); if (fp != NULL) { fclose(fp); // found it, all set strcat(qtjava_path, ";"); // add path separator //MessageBox(NULL, "found 1", "msg", MB_OK); //MessageBox(NULL, qtjava_path, "QTJAVA after strcat", MB_OK); } else { qtjava_path[0] = 0; // not a valid path } //} else { //MessageBox(NULL, "no qtjava set", "mess", MB_OK); } if (qtjava_path[0] == 0) { // not set yet //if (getenv("WINDIR") == NULL) { // uh-oh.. serious problem.. gonna have to report this // but hopefully WINDIR is set on win98 too //} else { strcpy(qtjava_path, getenv("WINDIR")); strcat(qtjava_path, "\\SYSTEM32\\QTJava.zip"); FILE *fp = fopen(qtjava_path, "rb"); if (fp != NULL) { fclose(fp); // found it, all set strcat(qtjava_path, ";"); // add path separator //MessageBox(NULL, "found 2", "msg", MB_OK); } else { qtjava_path[0] = 0; // not a valid path } } if (qtjava_path[0] == 0) { strcpy(qtjava_path, getenv("WINDIR")); strcat(qtjava_path, "\\SYSTEM\\QTJava.zip"); fp = fopen(qtjava_path, "rb"); if (fp != NULL) { fclose(fp); // found it, all set strcat(qtjava_path, ";"); // add path separator //MessageBox(NULL, "found 3", "msg", MB_OK); } else { // doesn't seem to be installed, which is a problem. // but the error will be reported by the pde qtjava_path[0] = 0; } } // don't put quotes around contents of cp, even though %s might have // spaces in it. don't put quotes in it, because it's setting the // environment variable for CLASSPATH, not being included on the // command line. so setting the env var it's ok to have spaces, // and the quotes prevent javax.comm.properties from being found. strcpy(cp, app_classpath); if (local_jre_installed) { char *local_jre = mallocChars(64 + strlen(exe_directory) * 2); sprintf(local_jre, "%s\\java\\lib\\rt.jar;%s\\java\\lib\\tools.jar;", exe_directory, exe_directory); strcat(cp, local_jre); } strcat(cp, qtjava_path); char *clean_cp = scrubPath(cp); //if (!SetEnvironmentVariable("CLASSPATH", cp)) { if (!SetEnvironmentVariable("CLASSPATH", clean_cp)) { MessageBox(NULL, "Could not set CLASSPATH environment variable", "Processing Error", MB_OK); return 0; } //MessageBox(NULL, "2", "checking", MB_OK); //char *env_path = (char *)malloc(strlen(getenv("PATH")) * sizeof(char)); int env_path_length = strlen(getenv("PATH")); char *env_path = mallocChars(env_path_length); strcpy(env_path, getenv("PATH")); char *clean_path; // need to add the local jre to the path for 'java mode' in the env if (local_jre_installed) { char *path_to_clean = mallocChars(env_path_length + strlen(exe_directory) + 30); sprintf(path_to_clean, "%s\\java\\bin;%s", exe_directory, env_path); clean_path = scrubPath(path_to_clean); } else { clean_path = scrubPath(getenv("PATH")); } //MessageBox(NULL, clean_path, "after scrubbing PATH", MB_OK); //MessageBox(NULL, "3", "checking", MB_OK); if (!SetEnvironmentVariable("PATH", clean_path)) { MessageBox(NULL, "Could not set PATH environment variable", "Processing Error", MB_OK); return 0; } // what gets put together to pass to jre char *outgoing_cmd_line = (char *)malloc(16384 * sizeof(char)); // prepend the args for -mx and -ms strcpy(outgoing_cmd_line, java_args); strcat(outgoing_cmd_line, " "); // add the name of the class to execute and a space before the next arg strcat(outgoing_cmd_line, java_main_class); strcat(outgoing_cmd_line, " "); // append additional incoming stuff (document names), if any strcat(outgoing_cmd_line, incoming_cmd_line); //MessageBox(NULL, outgoing_cmd_line, "cmd_line", MB_OK); char *executable = (char *)malloc((strlen(exe_directory) + 256) * sizeof(char)); // exe_directory is the name path to the current application if (local_jre_installed) { strcpy(executable, exe_directory); // copy in the path for javaw, relative to launcher.exe strcat(executable, "\\java\\bin\\javaw.exe"); } else { strcpy(executable, "javaw.exe"); } SHELLEXECUTEINFO ShExecInfo; // set up the execution info ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); ShExecInfo.fMask = 0; ShExecInfo.hwnd = 0; ShExecInfo.lpVerb = "open"; ShExecInfo.lpFile = executable; ShExecInfo.lpParameters = outgoing_cmd_line; ShExecInfo.lpDirectory = exe_directory; ShExecInfo.nShow = SW_SHOWNORMAL; ShExecInfo.hInstApp = NULL; if (!ShellExecuteEx(&ShExecInfo)) { MessageBox(NULL, "Error calling ShellExecuteEx()", "Processing Error", MB_OK); return 0; } if (reinterpret_cast(ShExecInfo.hInstApp) <= 32) { // some type of error occurred switch (reinterpret_cast(ShExecInfo.hInstApp)) { case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: MessageBox(NULL, "A required file could not be found. \n" "You may need to install a Java runtime\n" "or re-install Processing.", "Processing Error", MB_OK); break; case 0: case SE_ERR_OOM: MessageBox(NULL, "Not enough memory or resources to run at" " this time.", "Processing Error", MB_OK); break; default: MessageBox(NULL, "There is a problem with your installation.\n" "If the problem persists, re-install the program.", "Processing Error", MB_OK); break; } } return 0; } void removeLineEndings(char *what) { int index = strlen(what) - 1; while (index >= 0) { if ((what[index] == 10) || (what[index] == 13)) { what[index] = 0; --index; } else { return; } } } // take a PATH environment variable, split on semicolons, // remove extraneous quotes, perhaps even make 8.3 syntax if necessary char *scrubPath(char *incoming) { char *cleaned = mallocChars(strlen(incoming) * 2); int found_so_far = 0; char *p = (char*) strtok(incoming, ";"); while (p != NULL) { char entry[1024]; /* if (*p == '\"') { // if this segment of the path contains quotes, remove them int fixed_length = strlen(p) - 2; strncpy(entry, &p[1], fixed_length); entry[fixed_length] = 0; //MessageBox(NULL, entry, "clipped", MB_OK); // if it doesn't actually end with a quote, then the person // is screwed anyway.. they can deal with that themselves } else { strcpy(entry, p); } */ strcpy(entry, p); removeQuotes(entry); // a trailing slash will cause FindFirstFile to fail.. grr [0109] removeTrailingSlash(entry); //MessageBox(NULL, entry, "entry", MB_OK); // if this path doesn't exist, don't add it WIN32_FIND_DATA find_file_data; HANDLE hfind = FindFirstFile(entry, &find_file_data); if (hfind != INVALID_HANDLE_VALUE) { if (found_so_far) strcat(cleaned, ";"); strcat(cleaned, entry); //MessageBox(NULL, cleaned, "cleaned so far", MB_OK); FindClose(hfind); found_so_far = 1; //} else { //MessageBox(NULL, entry, "removing", MB_OK); } // grab the next entry p = (char*) strtok(NULL, ";"); } //MessageBox(NULL, cleaned, "scrubPath", MB_OK); return cleaned; } // eventually make this handle unicode char *mallocChars(int count) { // add one for the terminator char *outgoing = (char*) malloc(count * sizeof(char) + 1); outgoing[0] = 0; // for safety return outgoing; } void removeQuotes(char *quoted) { int len = strlen(quoted); // remove quote at the front if (quoted[0] == '\"') { for (int i = 0; i < len - 1; i++) { quoted[i] = quoted[i+1]; } len--; quoted[len] = 0; } // remove quote at the end if (len > 1) { if (quoted[len - 1] == '\"') { len--; quoted[len] = 0; } } } void removeTrailingSlash(char *slashed) { int len = strlen(slashed); if (len > 1) { if (slashed[len - 1] == '\\') { len--; slashed[len] = 0; } } }