/*********************************************************************** * * Copyright (C) 2006 Novell, Inc. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; version 2.1 * of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, Novell, Inc. * * To contact Novell about this file by physical or electronic mail, * you may find current contact information at www.novell.com. * * Author: Greg Richardson * ***********************************************************************/ // CommandLauncher.cpp : Defines the entry point for the application. // #include "stdafx.h" #include "CommandLauncher.h" #include "string.h" #include #include #include #include #include WCHAR ** G_rgArgs; // Command line arguments int G_cArg; // Count of command line arguments //FILE * G_pf; // Forward declarations of functions included in this code module: int processArguments(LPTSTR lpCmdLine); void freeArgs(); int countArgs(LPTSTR lpCmdLine); int addArg(int iArg, LPTSTR lpString, int cChar); void log(LPTSTR szMessage); _TCHAR * errorMessage(int err); int executeCommand( int cArg, _TCHAR* rgArg[]); #define ERROR_NO_ERROR 0 #define ERROR_MEMORY_ALLOCATION_FAILED -1 #define ERROR_INVALID_NUMBER_OF_PARAMETERS -2 #define ERROR_EXEC_E2BIG -3 #define ERROR_EXEC_EACCES -4 #define ERROR_EXEC_EINVAL -5 #define ERROR_EXEC_EMFILE -6 #define ERROR_EXEC_ENOENT -7 #define ERROR_EXEC_ENOEXEC -8 #define ERROR_EXEC_ENOMEM -9 #define ERROR_EXEC_UNKNOWN -10 #define ERROR_STRCPY_FAILED -11 #define ERROR_JAVA_EXE_ARG_MISSING -12 #define ERROR_JAVA_CLASSPATH_OPTION_ARG_MISSING -13 #define ERROR_JAVA_CLASSPATH_ARG_MISSING -14 #define ERROR_BAD_COMMAND_LINE -15 int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hInstance); UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); UNREFERENCED_PARAMETER(nCmdShow); int rc; //_wfopen_s(&G_pf, L"C:\\CommandLauncher.log", L"a+"); // Process the command line if (ERROR_NO_ERROR != (rc = processArguments(lpCmdLine))) { return rc; } rc = executeCommand(G_cArg, G_rgArgs); log(errorMessage(rc)); //fwprintf(G_pf, L"CommandLauncher: return = %d\n", rc); //fclose(G_pf); return rc; } // -cp class k1=v1 k2=k2 int processArguments(LPTSTR lpCmdLine) { int iArg; int iChar; int iAssignment; int iClassStart; int iClassEnd; int iClassPathStart; int iClassPathEnd; int rc; bool fClasspathOptionFound = false; int iKeyStart; int iValueEnd; //fwprintf( G_pf, L"current command line = %s\n", lpCmdLine); // Validate the command line if (NULL == lpCmdLine || (WCHAR)0 == *lpCmdLine) { return ERROR_BAD_COMMAND_LINE; } // Count the arguments on the command line. TThe name of this executable // is not included in the count. G_cArg = countArgs(lpCmdLine); // Make sure we got enough to exec something. There must be at least the // path to jave.exe, the classpath option, the classpath and a class. if (G_cArg < 4) { return ERROR_INVALID_NUMBER_OF_PARAMETERS; } // Allocate an array of wide string for the arguments, add 1 for a NULL at // the end of the array G_rgArgs = (WCHAR**)malloc((G_cArg + 1) * sizeof(WCHAR *)); if (NULL == G_rgArgs) { return ERROR_MEMORY_ALLOCATION_FAILED; } // Null out the array memset(G_rgArgs, 0, (G_cArg + 1) * sizeof(WCHAR *)); // Find the java.exe argument iChar = 0; for (iChar = 0; 0 != lpCmdLine[iChar + 4]; iChar++) { if (0 == _wcsnicmp(lpCmdLine + iChar, L".exe", 4)) { break; } } if (0 == lpCmdLine[iChar + 4]) { rc = ERROR_JAVA_EXE_ARG_MISSING; goto ErrorOut; } // Add the java.exe argument if (ERROR_NO_ERROR != (rc = addArg(0, lpCmdLine, iChar + 4))) { goto ErrorOut; } // Move past the java.exe argument iChar += 4; // Move past any spaces for (;L' ' == lpCmdLine[iChar]; iChar++) { // Intentionally left blank } // Find the classpath argument for (; 0 != lpCmdLine[iChar + 3]; iChar++) { if (0 == _wcsnicmp(lpCmdLine + iChar, L"-cp", 3)) { fClasspathOptionFound = true; break; } } if (!fClasspathOptionFound) { rc = ERROR_JAVA_CLASSPATH_OPTION_ARG_MISSING; goto ErrorOut; } if (0 == lpCmdLine[iChar + 3]) { rc = ERROR_JAVA_CLASSPATH_ARG_MISSING; goto ErrorOut; } // Add the classpath option argument if (ERROR_NO_ERROR != (rc = addArg(1, L"-cp", iChar + 3))) { goto ErrorOut; } // Move past the classpath option argument iChar += 3; // Move past any spaces for (;L' ' == lpCmdLine[iChar]; iChar++) { // Intentionally left blank } // The classpath is next. It can have spaces in it so we need to work // backards from the first key/value pair, or the end of the line if // there are no key/value pairs. iClassPathStart = iChar; // Find the location of the next '=' for (; 0 != lpCmdLine[iChar] && L'=' != lpCmdLine[iChar]; iChar++) { // Intentially left blank } // If there was a key/value pair - move to the start of the key if (L'=' == lpCmdLine[iChar]) { iAssignment = iChar; // Move back to the previous space. This should put us at the // beginning of the first key/value pair. Assume that all args // are property key/value pairs and property keys have no spaces. for (; L' ' != lpCmdLine[iChar] && iChar >= 0; iChar--) { // Intentially left blank } } else { iAssignment = 0; iChar--; } // Move past any spaces (moving toward the start of the line) for (;L' ' == lpCmdLine[iChar]; iChar--) { // Intentionally left blank } // This should put us at the end of the class to be executed iClassEnd = iChar; // Move to the previous space (moving toward the start of the line) for (;L' ' != lpCmdLine[iChar]; iChar--) { // Intentionally left blank } iClassStart = iChar + 1; // Add the class argument if (ERROR_NO_ERROR != (rc = addArg(3, lpCmdLine + iClassStart, iClassEnd - iClassStart + 1))) { goto ErrorOut; } // Move past any spaces (moving toward the start of the line) for (;L' ' == lpCmdLine[iChar]; iChar--) { // Intentionally left blank } // This should put us at the end of the classpath iClassPathEnd = iChar; // Add the class path argument if (ERROR_NO_ERROR != (rc = addArg(2, lpCmdLine + iClassPathStart, iClassPathEnd - iClassPathStart + 1))) { goto ErrorOut; } // Are the any key/value pairs? if (0 != iAssignment) { iArg = 4; while (0 != lpCmdLine[iAssignment]) { iKeyStart = iAssignment; // Move back to the previous space. This should put us at the // beginning of the current next key/value pair. Assume that all args // are property key/value pairs and property keys have no spaces. for (; L' ' != lpCmdLine[iKeyStart] && iKeyStart > 0; iKeyStart--) { // Intentially left blank } if (L' ' == lpCmdLine[iKeyStart]) { iKeyStart++; } // Find the location of the next '=' iValueEnd = iAssignment + 1; for (; 0 != lpCmdLine[iValueEnd] && L'=' != lpCmdLine[iValueEnd]; iValueEnd++) { // Intentially left blank } // If there was a property... if (L'=' == lpCmdLine[iValueEnd]) { iAssignment = iValueEnd; // Move back to the previous space. This should put us at the // beginning of the next key/value pair. Assume that all args // are property key/value pairs and property keys have no spaces. for (; L' ' != lpCmdLine[iValueEnd] && iValueEnd >= 0; iValueEnd--) { // Intentially left blank } } else { // We have reached the end of the command line - back off from the // null terminator. iAssignment = iValueEnd; iValueEnd--; } // Move thorugh any spaces for (; L' ' == lpCmdLine[iValueEnd] && iValueEnd >= 0; iValueEnd--) { // Intentially left blank } // Add the key/value pair if (ERROR_NO_ERROR != (rc = addArg(iArg, lpCmdLine + iKeyStart, iValueEnd - iKeyStart + 1))) { goto ErrorOut; } // Go on to the next arg iArg++; } } return ERROR_NO_ERROR; ErrorOut: freeArgs(); log(errorMessage(rc)); return rc; } void freeArgs() { int iArg; if (NULL != G_rgArgs) { for (iArg = 0; iArg < G_cArg; iArg++) { if (NULL != G_rgArgs[iArg]) { free(G_rgArgs[iArg]); G_rgArgs[iArg] = NULL; } } free(G_rgArgs); G_rgArgs = NULL; } } int countArgs(LPTSTR lpCmdLine) { int cArgument; // Check if the exe to execute is the only argument. Assume that all additional // arguments have an '=' in them. for (cArgument = 4; *lpCmdLine != (WCHAR)0; lpCmdLine++) { if (*lpCmdLine == L'=') { cArgument++; } } return cArgument; } int addArg(int iArg, LPTSTR lpString, int cChar) { int cb = (cChar + 3) * sizeof(WCHAR); // count of bytes // Allocate space for the new arg G_rgArgs[iArg] = (WCHAR *)malloc(cb); if (NULL == G_rgArgs[iArg]) { return ERROR_MEMORY_ALLOCATION_FAILED; } // Null out the argument memset(G_rgArgs[iArg], 0, cb); // Add a starting quote // G_rgArgs[iArg][0] = L'\"'; // Copy the arg if (0 != wcsncpy_s(G_rgArgs[iArg], cChar + 1, lpString, cChar)) { return ERROR_STRCPY_FAILED; } // Add a terminating quote // G_rgArgs[iArg][cb-1] = L'\"'; return ERROR_NO_ERROR; } void log(LPTSTR szMessage) { /* LPTSTR szT = L""; if (NULL == szMessage) szMessage = szT; fwprintf(G_pf, L"JavaLauncher: %s\n", szMessage); */ } _TCHAR * errorMessage(int err) { switch (err) { case ERROR_NO_ERROR: return L"No error\n"; case ERROR_MEMORY_ALLOCATION_FAILED: return L"Memory allocation failed\n"; case ERROR_INVALID_NUMBER_OF_PARAMETERS: return L"Invalid number of parameters\n"; case ERROR_EXEC_E2BIG: return L"_exec: The space required for the arguments and environment settings exceeds 32 KB.\n"; case ERROR_EXEC_EACCES: return L"_exec: The specified file has a locking or sharing violation.\n"; case ERROR_EXEC_EINVAL: return L"_exec: Invalid parameter.\n"; case ERROR_EXEC_EMFILE: return L"_exec: Too many files open (the specified file must be opened to determine whether it is executable).\n"; case ERROR_EXEC_ENOENT: return L"_exec: The file or path not found.\n"; case ERROR_EXEC_ENOEXEC: return L"_exec: The specified file is not executable or has an invalid executable-file format.\n"; case ERROR_EXEC_ENOMEM: return L"_exec: Not enough memory is available to execute the new process; the available memory has been corrupted; or an invalid block exists, indicating that the calling process was not allocated properly.\n"; case ERROR_EXEC_UNKNOWN: return L"Unknown _exec error.\n"; case ERROR_STRCPY_FAILED: return L"String copy failed.\n"; case ERROR_JAVA_CLASSPATH_OPTION_ARG_MISSING: return L"Classpath option \"-cp\" missing\n"; case ERROR_JAVA_CLASSPATH_ARG_MISSING: return L"Classpath argument missing\n"; case ERROR_BAD_COMMAND_LINE: return L"Bad command line\n"; default: return L"Unknown error.\n"; } } int executeCommand( int cArg, _TCHAR* rgArg[] ) { int i; // Looping variable int rc = ERROR_NO_ERROR; // Return code //fwprintf( G_pf, L"Arg count = %d\n", cArg); //for (i = 0; i < cArg; i++) //{ // fwprintf(G_pf, L"rgArg[%d] (%s)\n", i, rgArg[i]); //} // exec the command // if (-1 == _wexecv( rgArg[0], rgArg)) if (-1 == _wspawnv(_P_WAIT, rgArg[0], rgArg)) { switch (errno) { case E2BIG: // The space required for the arguments and environment settings exceeds 32 KB. rc = ERROR_EXEC_E2BIG; break; case EACCES: // The specified file has a locking or sharing violation. rc = ERROR_EXEC_EACCES; break; case EINVAL: // Invalid parameter. rc = ERROR_EXEC_EINVAL; break; case EMFILE: // Too many files open (the specified file must be opened to determine whether it is executable). rc = ERROR_EXEC_EMFILE; break; case ENOENT: // The file or path not found. rc = ERROR_EXEC_ENOENT; break; case ENOEXEC: // The specified file is not executable or has an invalid executable-file format. rc = ERROR_EXEC_ENOEXEC; break; case ENOMEM: // Not enough memory is available to execute the new process; the available memory has been // corrupted; or an invalid block exists, indicating that the calling process was not allocated // properly. rc = ERROR_EXEC_ENOMEM; break; default: rc = ERROR_EXEC_UNKNOWN; break; } } //fwprintf(G_pf, L"ExecuteCommand returning %d\n", rc); return rc; }