Cygwin's execlp() does not work with an empty $PATH element

Takashi Yano takashi.yano@nifty.ne.jp
Mon Jun 27 11:16:36 GMT 2022


On Sun, 26 Jun 2022 15:09:34 +0000
"Lavrentiev, Anton \(NIH/NLM/NCBI\) \[C\] wrote:
> An empty PATH element (":xxx" or "xxx::xxx" or "xxx:") is to be considered as the current directory (from the very first days of Unix).
> 
> However, Cygwin does not seem to obey the rule.
> 
> Consider the following simple C program:
> 
> $ cat hello.c
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> #include <unistd.h>
> 
> int main(int argc, const char* argv[])
> {
>     if (argc < 2) {
>         const char* prog = strrchr(argv[0], '/');
>         if (!prog++)
>            prog = argv[0];
>         execlp(prog, prog, "Hello", NULL);  // execute just by the program name
>         perror("exec");
>         return 1;
>     }
>     printf("%s\n", argv[1]);
>     return 0;
> }
> 
> Now compare the execution on Linux and Cygwin:
> 
> Linux:
> 
> $ gcc -Wall -o hello hello.c
> $ hello
> bash: hello: command not found
> $ ./hello
> exec: No such file or directory
> $ PATH=".:$PATH" ./hello
> Hello
> $ PATH=":$PATH" ./hello
> Hello
> $ PATH="${PATH}:" ./hello
> Hello
> 
> Cygwin:
> 
> $ gcc -Wall -o hello hello.c
> $ hello
> -bash: hello: command not found
> $ ./hello
> exec: No such file or directory
> $ PATH=".:$PATH" ./hello
> Hello
> $ PATH=":$PATH" ./hello
> exec: No such file or directory
> $ PATH="${PATH}:" ./hello
> exec: No such file or directory
> 
> As you can see, the execution failed when an empty PATH element was added on Cygwin
> (yet it was perfectly fine on Linux).

How about the following patch?

diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
index e0f1247e1..8fe9d089f 100644
--- a/winsup/cygwin/spawn.cc
+++ b/winsup/cygwin/spawn.cc
@@ -118,8 +118,10 @@ find_exec (const char *name, path_conv& buf, const char *search,
      the name of an environment variable. */
   if (strchr (search, '/'))
     *stpncpy (tmp, search, NT_MAX_PATH - 1) = '\0';
-  else if (has_slash || isdrive (name) || !(path = getenv (search)) || !*path)
+  else if (has_slash || isdrive (name))
     goto errout;
+  else if (!(path = getenv (search)) || !*path)
+    strcpy (tmp, "."); /* Search the current directory when PATH is absent */
   else
     *stpncpy (tmp, path, NT_MAX_PATH - 1) = '\0';
 
@@ -134,7 +136,9 @@ find_exec (const char *name, path_conv& buf, const char *search,
 	 already tried that.  */
       if ((opt & FE_CWD) && (tmp_path[0] == '\0'
 			     || (tmp_path[0] == '.' && tmp_path[1] == '\0')))
-	continue;
+	goto next;
+      else if (tmp_path[0] == '\0') /* An empty path means the current dir. */
+	eotmp = stpcpy (tmp_path, ".");
 
       *eotmp++ = '/';
       stpcpy (eotmp, name);
@@ -146,7 +150,7 @@ find_exec (const char *name, path_conv& buf, const char *search,
       if ((suffix = perhaps_suffix (tmp_path, buf, err1, opt)) != NULL)
 	{
 	  if (buf.has_acls () && check_file_access (buf, X_OK, true))
-	    continue;
+	    goto next;
 	  /* Overwrite potential symlink target with original path.
 	     See comment preceeding this method. */
 	  buf.set_posix (tmp_path);
@@ -154,8 +158,13 @@ find_exec (const char *name, path_conv& buf, const char *search,
 	  goto out;
 	}
 
+next:
+      if (*path == '\0')
+	break;
+      if (*path == ':')
+	path++;
     }
-  while (*path && *++path);
+  while (true);
 
  errout:
   /* Couldn't find anything in the given path.

-- 
Takashi Yano <takashi.yano@nifty.ne.jp>


More information about the Cygwin mailing list