[PATCH v2 5/5] start.exe: search PATH for the executable to launch

Damjan Jovanovic damjan.jov at gmail.com
Sun Aug 11 09:28:50 CDT 2019


We want to launch all executables through CreateProcess(), but
need to provide the full path, as the searching CreateProcess()
does internally when the application name is NULL
is more limited: it only searches for .EXE files.

Signed-off-by: Damjan Jovanovic <damjan.jov at gmail.com>
---
 programs/start/start.c | 182 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 177 insertions(+), 5 deletions(-)
-------------- next part --------------
diff --git a/programs/start/start.c b/programs/start/start.c
index b21a0c56ec..7668330c7f 100644
--- a/programs/start/start.c
+++ b/programs/start/start.c
@@ -191,6 +191,165 @@ static void parse_title(const WCHAR *arg, WCHAR *title, int size)
     title[next] = '\0';
 }
 
+static BOOL search_path(const WCHAR *firstParam, WCHAR **full_path)
+{
+    /* Copied from WCMD_run_program() in programs/cmd/wcmdmain.c */
+
+#define MAXSTRING 8192
+    static const WCHAR slashW[] = {'\\','\0',};
+    static const WCHAR envPath[] = {'P','A','T','H','\0'};
+    static const WCHAR envPathExt[] = {'P','A','T','H','E','X','T','\0'};
+    static const WCHAR dfltPathExt[] = {'.','b','a','t',';',
+                                        '.','c','o','m',';',
+                                        '.','c','m','d',';',
+                                        '.','e','x','e','\0'};
+    static const WCHAR delims[] = {'/','\\',':','\0'};
+
+    WCHAR  temp[MAX_PATH];
+    WCHAR  pathtosearch[MAXSTRING];
+    WCHAR *pathposn;
+    WCHAR  stemofsearch[MAX_PATH];    /* maximum allowed executable name is
+                                         MAX_PATH, including null character */
+    WCHAR *lastSlash;
+    WCHAR  pathext[MAXSTRING];
+    BOOL  extensionsupplied = FALSE;
+    DWORD len;
+
+    /* Calculate the search path and stem to search for */
+    if (wcspbrk (firstParam, delims) == NULL) {  /* No explicit path given, search path */
+        static const WCHAR curDir[] = {'.',';','\0'};
+        lstrcpyW(pathtosearch, curDir);
+        len = GetEnvironmentVariableW(envPath, &pathtosearch[2], ARRAY_SIZE(pathtosearch)-2);
+        if ((len == 0) || (len >= ARRAY_SIZE(pathtosearch) - 2)) {
+            static const WCHAR curDir[] = {'.','\0'};
+            lstrcpyW (pathtosearch, curDir);
+        }
+        if (wcschr(firstParam, '.') != NULL) extensionsupplied = TRUE;
+        if (lstrlenW(firstParam) >= MAX_PATH) {
+            return FALSE;
+        }
+
+        lstrcpyW(stemofsearch, firstParam);
+
+    } else {
+
+        /* Convert eg. ..\fred to include a directory by removing file part */
+        GetFullPathNameW(firstParam, ARRAY_SIZE(pathtosearch), pathtosearch, NULL);
+        lastSlash = wcsrchr(pathtosearch, '\\');
+        if (lastSlash && wcschr(lastSlash, '.') != NULL) extensionsupplied = TRUE;
+        lstrcpyW(stemofsearch, lastSlash+1);
+
+        /* Reduce pathtosearch to a path with trailing '\' to support c:\a.bat and
+           c:\windows\a.bat syntax                                                 */
+        if (lastSlash) *(lastSlash + 1) = 0x00;
+    }
+
+    /* Now extract PATHEXT */
+    len = GetEnvironmentVariableW(envPathExt, pathext, ARRAY_SIZE(pathext));
+    if ((len == 0) || (len >= ARRAY_SIZE(pathext))) {
+        lstrcpyW (pathext, dfltPathExt);
+    }
+
+    /* Loop through the search path, dir by dir */
+    pathposn = pathtosearch;
+    WINE_TRACE("Searching in '%s' for '%s'\n", wine_dbgstr_w(pathtosearch),
+               wine_dbgstr_w(stemofsearch));
+    while (pathposn) {
+        WCHAR  thisDir[MAX_PATH] = {'\0'};
+        int    length            = 0;
+        WCHAR *pos               = NULL;
+        BOOL  found             = FALSE;
+        BOOL inside_quotes      = FALSE;
+
+        /* Work on the first directory on the search path */
+        pos = pathposn;
+        while ((inside_quotes || *pos != ';') && *pos != 0)
+        {
+            if (*pos == '"')
+                inside_quotes = !inside_quotes;
+            pos++;
+        }
+
+        if (*pos) { /* Reached semicolon */
+            memcpy(thisDir, pathposn, (pos-pathposn) * sizeof(WCHAR));
+            thisDir[(pos-pathposn)] = 0x00;
+            pathposn = pos+1;
+        } else {    /* Reached string end */
+            lstrcpyW(thisDir, pathposn);
+            pathposn = NULL;
+        }
+
+        /* Remove quotes */
+        length = lstrlenW(thisDir);
+        if (thisDir[length - 1] == '"')
+            thisDir[length - 1] = 0;
+
+        if (*thisDir != '"')
+            lstrcpyW(temp, thisDir);
+        else
+            lstrcpyW(temp, thisDir + 1);
+
+        /* Since you can have eg. ..\.. on the path, need to expand
+           to full information                                      */
+        GetFullPathNameW(temp, MAX_PATH, thisDir, NULL);
+
+        /* 1. If extension supplied, see if that file exists */
+        lstrcatW(thisDir, slashW);
+        lstrcatW(thisDir, stemofsearch);
+        pos = &thisDir[lstrlenW(thisDir)]; /* Pos = end of name */
+
+        /* 1. If extension supplied, see if that file exists */
+        if (extensionsupplied) {
+            if (GetFileAttributesW(thisDir) != INVALID_FILE_ATTRIBUTES) {
+               found = TRUE;
+            }
+        }
+
+        /* 2. Any .* matches? */
+        if (!found) {
+            HANDLE          h;
+            WIN32_FIND_DATAW finddata;
+            static const WCHAR allFiles[] = {'.','*','\0'};
+
+            lstrcatW(thisDir,allFiles);
+            h = FindFirstFileW(thisDir, &finddata);
+            FindClose(h);
+            if (h != INVALID_HANDLE_VALUE) {
+
+                WCHAR *thisExt = pathext;
+
+                /* 3. Yes - Try each path ext */
+                while (thisExt) {
+                    WCHAR *nextExt = wcschr(thisExt, ';');
+
+                    if (nextExt) {
+                        memcpy(pos, thisExt, (nextExt-thisExt) * sizeof(WCHAR));
+                        pos[(nextExt-thisExt)] = 0x00;
+                        thisExt = nextExt+1;
+                    } else {
+                        lstrcpyW(pos, thisExt);
+                        thisExt = NULL;
+                    }
+
+                    if (GetFileAttributesW(thisDir) != INVALID_FILE_ATTRIBUTES) {
+                        found = TRUE;
+                        thisExt = NULL;
+                    }
+                }
+            }
+        }
+
+        if (found) {
+            int needed_size = lstrlenW(thisDir) + 1;
+            *full_path = HeapAlloc(GetProcessHeap(), 0, needed_size * sizeof(WCHAR));
+            if (*full_path)
+                lstrcpyW(*full_path, thisDir);
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
 int wmain (int argc, WCHAR *argv[])
 {
 	SHELLEXECUTEINFOW sei;
@@ -200,7 +359,9 @@ int wmain (int argc, WCHAR *argv[])
         BOOL unix_mode = FALSE;
         BOOL progid_open = FALSE;
 	WCHAR *title = NULL;
+	const WCHAR *file;
 	WCHAR *dos_filename = NULL;
+	WCHAR *fullpath = NULL;
 	WCHAR *parent_directory = NULL;
 	DWORD binary_type;
 
@@ -359,10 +520,10 @@ int wmain (int argc, WCHAR *argv[])
 	if (i == argc) {
 		if (progid_open || unix_mode)
 			usage();
-		sei.lpFile = cmdW;
+		file = cmdW;
 	}
 	else
-		sei.lpFile = argv[i++];
+		file = argv[i++];
 
 	args = build_args( argc - i, &argv[i] );
 	sei.lpParameters = args;
@@ -377,10 +538,10 @@ int wmain (int argc, WCHAR *argv[])
 		if (!wine_get_dos_file_name_ptr)
 			fatal_string(STRING_UNIXFAIL);
 
-		multibyte_unixpath_len = WideCharToMultiByte(CP_UNIXCP, 0, sei.lpFile, -1, NULL, 0, NULL, NULL);
+		multibyte_unixpath_len = WideCharToMultiByte(CP_UNIXCP, 0, file, -1, NULL, 0, NULL, NULL);
 		multibyte_unixpath = HeapAlloc(GetProcessHeap(), 0, multibyte_unixpath_len);
 
-		WideCharToMultiByte(CP_UNIXCP, 0, sei.lpFile, -1, multibyte_unixpath, multibyte_unixpath_len, NULL, NULL);
+		WideCharToMultiByte(CP_UNIXCP, 0, file, -1, multibyte_unixpath, multibyte_unixpath_len, NULL, NULL);
 
 		dos_filename = wine_get_dos_file_name_ptr(multibyte_unixpath);
 
@@ -393,6 +554,16 @@ int wmain (int argc, WCHAR *argv[])
 		if (!sei.lpDirectory)
 			sei.lpDirectory = parent_directory = get_parent_dir(dos_filename);
 		sei.fMask &= ~SEE_MASK_FLAG_NO_UI;
+	} else {
+		if (search_path(file, &fullpath)) {
+			if (fullpath != NULL) {
+				sei.lpFile = fullpath;
+			} else {
+				fatal_string_error(STRING_EXECFAIL, ERROR_OUTOFMEMORY, file);
+			}
+		} else {
+			sei.lpFile = file;
+		}
 	}
 
         if (GetBinaryTypeW(sei.lpFile, &binary_type)) {
@@ -413,7 +584,7 @@ int wmain (int argc, WCHAR *argv[])
                     startup_info.lpTitle = title;
 
                     if (!CreateProcessW(
-                            NULL, /* lpApplicationName */
+                            sei.lpFile, /* lpApplicationName */
                             commandline, /* lpCommandLine */
                             NULL, /* lpProcessAttributes */
                             NULL, /* lpThreadAttributes */
@@ -484,6 +655,7 @@ int wmain (int argc, WCHAR *argv[])
 done:
 	HeapFree( GetProcessHeap(), 0, args );
 	HeapFree( GetProcessHeap(), 0, dos_filename );
+	HeapFree( GetProcessHeap(), 0, fullpath );
 	HeapFree( GetProcessHeap(), 0, parent_directory );
 	HeapFree( GetProcessHeap(), 0, title );
 


More information about the wine-devel mailing list