Damjan Jovanovic : start.exe: Search PATH for the executable to launch.

Alexandre Julliard julliard at winehq.org
Mon Aug 19 15:35:12 CDT 2019


Module: wine
Branch: master
Commit: 42fa293d48be89ce06b1aab264c5c0d8cc1b4f53
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=42fa293d48be89ce06b1aab264c5c0d8cc1b4f53

Author: Damjan Jovanovic <damjan.jov at gmail.com>
Date:   Sun Aug 11 16:28:50 2019 +0200

start.exe: Search PATH for the executable to launch.

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>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 programs/start/start.c | 182 +++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 177 insertions(+), 5 deletions(-)

diff --git a/programs/start/start.c b/programs/start/start.c
index b21a0c5..7668330 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-cvs mailing list