+/*
+ * Extracts the executable or script name from the first string in
+ * cmdline.
+ *
+ * If the name contains blanks then it must be quoted with double quotes,
+ * otherwise quotes are optional. If the name contains blanks then it
+ * will be converted to a short name.
+ *
+ * The optional quotes will be removed. The result is copied to a malloc'ed
+ * buffer and returned through the pexe argument. The pargs parameter is set
+ * to the address of the character in cmdline located after the name.
+ *
+ * The malloc'ed buffer returned in *pexe must be freed by the caller.
+ */
+bool
+GetApplicationName(const char *cmdline, char **pexe, const char **pargs)
+{
+ const char *pExeStart = NULL; /* Start of executable name in cmdline */
+ const char *pExeEnd = NULL; /* Character after executable name (separator) */
+
+ const char *pBasename = NULL; /* Character after last path separator */
+ const char *pExtension = NULL; /* Period at start of extension */
+
+ const char *current = cmdline;
+
+ bool bQuoted = false;
+
+ /* Skip initial whitespace */
+
+ while (*current == ' ' || *current == '\t')
+ {
+ current++;
+ }
+
+ /* Calculate start of name and determine if quoted */
+
+ if (*current == '"') {
+ pExeStart = ++current;
+ bQuoted = true;
+ } else {
+ pExeStart = current;
+ bQuoted = false;
+ }
+
+ *pargs = NULL;
+ *pexe = NULL;
+
+ /*
+ * Scan command line looking for path separators (/ and \\) and the
+ * terminator, either a quote or a blank. The location of the
+ * extension is also noted.
+ */
+
+ for ( ; *current != '\0'; current++)
+ {
+ if (*current == '.') {
+ pExtension = current;
+ } else if (IsPathSeparator(*current) && current[1] != '\0') {
+ pBasename = ¤t[1];
+ pExtension = NULL;
+ }
+
+ /* Check for terminator, either quote or blank */
+ if (bQuoted) {
+ if (*current != '"') {
+ continue;
+ }
+ } else {
+ if (*current != ' ') {
+ continue;
+ }
+ }
+
+ /*
+ * Hit terminator, remember end of name (address of terminator) and
+ * start of arguments
+ */
+ pExeEnd = current;
+
+ if (bQuoted && *current == '"') {
+ *pargs = ¤t[1];
+ } else {
+ *pargs = current;
+ }
+
+ break;
+ }
+
+ if (pBasename == NULL) {
+ pBasename = pExeStart;
+ }
+
+ if (pExeEnd == NULL) {
+ pExeEnd = current;
+ }
+
+ if (*pargs == NULL)
+ {
+ *pargs = current;
+ }
+
+ bool bHasPathSeparators = pExeStart != pBasename;
+
+ /* We have pointers to all the useful parts of the name */
+
+ /* Default extensions in the order cmd.exe uses to search */
+
+ static const char ExtensionList[][5] = { ".com", ".exe", ".bat", ".cmd" };
+ DWORD dwBasePathLength = pExeEnd - pExeStart;
+
+ DWORD dwAltNameLength = 0;
+ char *pPathname = (char *)alloca(MAX_PATHLENGTH + 1);
+ char *pAltPathname = (char *)alloca(MAX_PATHLENGTH + 1);
+
+ pPathname[MAX_PATHLENGTH] = '\0';
+ pAltPathname[MAX_PATHLENGTH] = '\0';
+
+ memcpy(pPathname, pExeStart, dwBasePathLength);
+ pPathname[dwBasePathLength] = '\0';
+
+ if (pExtension == NULL) {
+ /* Try appending extensions */
+ for (int index = 0; index < (int)(sizeof(ExtensionList) / sizeof(ExtensionList[0])); index++) {
+
+ if (!bHasPathSeparators) {
+ /* There are no path separators, search in the standard locations */
+ dwAltNameLength = SearchPath(NULL, pPathname, ExtensionList[index], MAX_PATHLENGTH, pAltPathname, NULL);
+ if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
+ memcpy(pPathname, pAltPathname, dwAltNameLength);
+ pPathname[dwAltNameLength] = '\0';
+ break;
+ }
+ } else {
+ bstrncpy(&pPathname[dwBasePathLength], ExtensionList[index], MAX_PATHLENGTH - dwBasePathLength);
+ if (GetFileAttributes(pPathname) != INVALID_FILE_ATTRIBUTES) {
+ break;
+ }
+ pPathname[dwBasePathLength] = '\0';
+ }
+ }
+ } else if (!bHasPathSeparators) {
+ /* There are no path separators, search in the standard locations */
+ dwAltNameLength = SearchPath(NULL, pPathname, NULL, MAX_PATHLENGTH, pAltPathname, NULL);
+ if (dwAltNameLength > 0 && dwAltNameLength < MAX_PATHLENGTH) {
+ memcpy(pPathname, pAltPathname, dwAltNameLength);
+ pPathname[dwAltNameLength] = '\0';
+ }
+ }
+
+ if (strchr(pPathname, ' ') != NULL) {
+ dwAltNameLength = GetShortPathName(pPathname, pAltPathname, MAX_PATHLENGTH);
+
+ if (dwAltNameLength > 0 && dwAltNameLength <= MAX_PATHLENGTH) {
+ *pexe = (char *)malloc(dwAltNameLength + 1);
+ if (*pexe == NULL) {
+ return false;
+ }
+ memcpy(*pexe, pAltPathname, dwAltNameLength + 1);
+ }
+ }
+
+ if (*pexe == NULL) {
+ DWORD dwPathnameLength = strlen(pPathname);
+ *pexe = (char *)malloc(dwPathnameLength + 1);
+ if (*pexe == NULL) {
+ return false;
+ }
+ memcpy(*pexe, pPathname, dwPathnameLength + 1);
+ }
+
+ return true;
+}
+
+/**
+ * Create the process with WCHAR API
+ */
+static BOOL
+CreateChildProcessW(const char *comspec, const char *cmdLine,
+ PROCESS_INFORMATION *hProcInfo,
+ HANDLE in, HANDLE out, HANDLE err)
+{
+ STARTUPINFOW siStartInfo;
+ BOOL bFuncRetn = FALSE;
+
+ // Set up members of the STARTUPINFO structure.
+ ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
+ siStartInfo.cb = sizeof(siStartInfo);
+ // setup new process to use supplied handles for stdin,stdout,stderr
+
+ siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+ siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
+
+ siStartInfo.hStdInput = in;
+ siStartInfo.hStdOutput = out;
+ siStartInfo.hStdError = err;
+
+ // Convert argument to WCHAR
+ POOLMEM *cmdLine_wchar = get_pool_memory(PM_FNAME);
+ POOLMEM *comspec_wchar = get_pool_memory(PM_FNAME);
+
+ UTF8_2_wchar(&cmdLine_wchar, cmdLine);
+ UTF8_2_wchar(&comspec_wchar, comspec);
+
+ // Create the child process.
+ Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec_wchar, cmdLine_wchar);
+
+ // try to execute program
+ bFuncRetn = p_CreateProcessW((WCHAR*)comspec_wchar,
+ (WCHAR*)cmdLine_wchar,// command line
+ NULL, // process security attributes
+ NULL, // primary thread security attributes
+ TRUE, // handles are inherited
+ 0, // creation flags
+ NULL, // use parent's environment
+ NULL, // use parent's current directory
+ &siStartInfo, // STARTUPINFO pointer
+ hProcInfo); // receives PROCESS_INFORMATION
+ free_pool_memory(cmdLine_wchar);
+ free_pool_memory(comspec_wchar);
+
+ return bFuncRetn;
+}
+
+
+/**
+ * Create the process with ANSI API
+ */
+static BOOL
+CreateChildProcessA(const char *comspec, char *cmdLine,
+ PROCESS_INFORMATION *hProcInfo,
+ HANDLE in, HANDLE out, HANDLE err)
+{
+ STARTUPINFOA siStartInfo;
+ BOOL bFuncRetn = FALSE;
+
+ // Set up members of the STARTUPINFO structure.
+ ZeroMemory( &siStartInfo, sizeof(siStartInfo) );
+ siStartInfo.cb = sizeof(siStartInfo);
+ // setup new process to use supplied handles for stdin,stdout,stderr
+ siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+ siStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;
+
+ siStartInfo.hStdInput = in;
+ siStartInfo.hStdOutput = out;
+ siStartInfo.hStdError = err;
+
+ // Create the child process.
+ Dmsg2(150, "Calling CreateProcess(%s, %s, ...)\n", comspec, cmdLine);
+
+ // try to execute program
+ bFuncRetn = p_CreateProcessA(comspec,
+ cmdLine, // command line
+ NULL, // process security attributes
+ NULL, // primary thread security attributes
+ TRUE, // handles are inherited
+ 0, // creation flags
+ NULL, // use parent's environment
+ NULL, // use parent's current directory
+ &siStartInfo,// STARTUPINFO pointer
+ hProcInfo);// receives PROCESS_INFORMATION
+ return bFuncRetn;
+}