Add support for OS/2, contributed by Andreas Buening <andreas.buening@nexgo.de>
authorPaul Smith <psmith@gnu.org>
Mon, 24 Mar 2003 23:14:15 +0000 (23:14 +0000)
committerPaul Smith <psmith@gnu.org>
Mon, 24 Mar 2003 23:14:15 +0000 (23:14 +0000)
Also a small patch from Hartmut Becker <Hartmut.Becker@compaq.com> for VMS.

12 files changed:
ChangeLog
default.c
dir.c
function.c
job.c
job.h
main.c
make.h
read.c
remake.c
variable.c
vpath.c

index 2b3ebc7..b99e6f7 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,52 @@
+2003-02-25  Paul D. Smith  <psmith@gnu.org>
+
+       Port to OS/2 (__EMX__) by Andreas Buening <andreas.buening@nexgo.de>.
+
+       * job.c (_is_unixy_shell) [OS/2]: New function.
+       Set default shell to /bin/sh.
+       (reap_children): Close the job_rfd pipe here since we don't use a
+       SIGCHLD handler.
+       (set_child_handler_action_flags): define this to empty on OS/2.
+       (start_job_command): Close the jobserver pipe and use
+       child_execute_job() instead of fork/exec.
+       (child_execute_job): Rewrite to handle stdin/stdout FDs and spawn
+       rather than exec'ing, then reconfigure stdin/stdout.
+       (exec_command): Rewrite to use spawn instead of exec.  Return the
+       PID of the child.
+
+       * main.c (main) [OS/2]: Call initialize_main().  Handle argv[0] as
+       in DOS.  Handle the TEMP environment variable as in DOS.  Don't
+       use a SIGCHLD handler on OS/2.  Choose a shell as in DOS.  Don't
+       use -j in DOS mode.  Use child_execute_job() instead of
+       exec_command().
+
+       * function.c (func_shell) [OS/2]: Can't use fork/exec on OS/2: use
+       spawn() instead.
+
+       * job.h [OS/2]: Move CLOSE_ON_EXEC here from job.c.  Add
+       prototypes that return values.
+
+       * remake.c (f_mtime) [OS/2]: Handle FAT timestamp offsets for OS/2.
+
+       * read.c (readline) [OS/2]: Don't handle CRLF specially on OS/2.
+       * default.c (default_suffixes) [OS/2]: Set proper default suffixes
+       for OS/2.
+       * vpath.c (construct_vpath_list) [OS/2]: Handle OS/2 paths like
+       DOS paths.
+
+2003-02-24  Paul D. Smith  <psmith@gnu.org>
+
+       * default.c [VMS]: New default rules for .cxx -> .obj compiles.
+       * job.c (child_execute_job) [VMS]: New code for handling spawn().
+       (child_execute_job) [VMS]: Handle error status properly.
+       Patches provided by Hartmut Becker <Hartmut.Becker@compaq.com>.
+
+       * function.c (func_shell): Use EINTRLOOP() while reading from the
+       subshell pipe (Fixes bug #2502).
+       * job.c (free_child): Use EINTRLOOP() while writing tokens to the
+       jobserver pipe.
+       * main.c (main): Ditto.
+
 2003-01-30  Paul D. Smith  <psmith@gnu.org>
 
        * read.c (eval): eval() was not fully reentrant, because the
index 5b9d62d..d262a30 100644 (file)
--- a/default.c
+++ b/default.c
@@ -27,8 +27,8 @@ Boston, MA 02111-1307, USA.  */
 
 /* Define GCC_IS_NATIVE if gcc is the native development environment on
    your system (gcc/bison/flex vs cc/yacc/lex).  */
-#ifdef __MSDOS__
-#define GCC_IS_NATIVE
+#if defined(__MSDOS__) || defined(__EMX__)
+# define GCC_IS_NATIVE
 #endif
 
 
@@ -41,6 +41,10 @@ static char default_suffixes[]
   = ".exe .olb .ln .obj .c .cxx .cc .pas .p .for .f .r .y .l .mar \
 .s .ss .i .ii .mod .sym .def .h .info .dvi .tex .texinfo .texi .txinfo \
 .w .ch .cweb .web .com .sh .elc .el";
+#elif defined(__EMX__)
+  = ".out .a .ln .o .c .cc .C .cpp .p .f .F .r .y .l .s .S \
+.mod .sym .def .h .info .dvi .tex .texinfo .texi .txinfo \
+.w .ch .web .sh .elc .el .obj .exe .dll .lib";
 #else
   = ".out .a .ln .o .c .cc .C .cpp .p .f .F .r .y .l .s .S \
 .mod .sym .def .h .info .dvi .tex .texinfo .texi .txinfo \
@@ -153,6 +157,8 @@ static char *default_suffix_rules[] =
     "$(COMPILE.cc)/noprep/noobj/machine /list=$@ $<",
     ".cc.obj",
     "$(COMPILE.cc) /obj=$@ $<",
+    ".cxx.obj",
+    "$(COMPILE.cxx) /obj=$@ $<",
     ".for.obj",
     "$(COMPILE.for) /obj=$@ $<",
     ".pas.obj",
diff --git a/dir.c b/dir.c
index 7db0697..1de62e5 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -589,6 +589,10 @@ dir_contents_file_exists_p (struct directory_contents *dir, char *filename)
   filename = downcase (filename);
 #endif
 
+#ifdef __EMX__
+  _fnlwr(filename); /* lower case for FAT drives */
+#endif
+
 #ifdef VMS
   filename = vmsify (filename,0);
 #endif
index 2edac74..361e3a8 100644 (file)
@@ -1450,6 +1450,7 @@ func_shell (char *o, char **argv, const char *funcname)
     error_prefix = "";
 
 #ifdef WINDOWS32
+
   windows32_openpipe (pipedes, &pid, command_argv, envp);
 
   if (pipedes[0] < 0) {
@@ -1458,31 +1459,46 @@ func_shell (char *o, char **argv, const char *funcname)
 
        return o;
   } else
-#else /* WINDOWS32 */
 
-# ifdef __MSDOS__
+#elif defined(__MSDOS__)
+
   fpipe = msdos_openpipe (pipedes, &pid, argv[0]);
   if (pipedes[0] < 0)
     {
       perror_with_name (error_prefix, "pipe");
       return o;
     }
-# else
+
+#else
+
   if (pipe (pipedes) < 0)
     {
       perror_with_name (error_prefix, "pipe");
       return o;
     }
 
+# ifdef __EMX__
+
+  /* close some handles that are unnecessary for the child process */
+  CLOSE_ON_EXEC(pipedes[1]);
+  CLOSE_ON_EXEC(pipedes[0]);
+  /* Never use fork()/exec() here! Use spawn() instead in exec_command() */
+  pid = child_execute_job (0, pipedes[1], command_argv, envp);
+  if (pid < 0)
+    perror_with_name (error_prefix, "spawn");
+
+# else /* ! __EMX__ */
+
   pid = vfork ();
   if (pid < 0)
     perror_with_name (error_prefix, "fork");
   else if (pid == 0)
     child_execute_job (0, pipedes[1], command_argv, envp);
   else
-# endif /* ! __MSDOS__ */
 
-#endif /* WINDOWS32 */
+# endif
+
+#endif
     {
       /* We are the parent.  */
 
@@ -1517,7 +1533,7 @@ func_shell (char *o, char **argv, const char *funcname)
              buffer = (char *) xrealloc (buffer, maxlen + 1);
            }
 
-         cc = read (pipedes[0], &buffer[i], maxlen - i);
+         EINTRLOOP (cc, read (pipedes[0], &buffer[i], maxlen - i));
          if (cc <= 0)
            break;
        }
@@ -1531,8 +1547,8 @@ func_shell (char *o, char **argv, const char *funcname)
       (void) close (pipedes[0]);
 #endif
 
-      /* Loop until child_handler sets shell_function_completed
-        to the status of our child shell.  */
+      /* Loop until child_handler or reap_children()  sets
+         shell_function_completed to the status of our child shell.  */
       while (shell_function_completed == 0)
        reap_children (1, 0);
 
diff --git a/job.c b/job.c
index efdf4dd..25988a9 100644 (file)
--- a/job.c
+++ b/job.c
@@ -32,31 +32,43 @@ Boston, MA 02111-1307, USA.  */
 
 /* Default shell to use.  */
 #ifdef WINDOWS32
+
 char *default_shell = "sh.exe";
 int no_default_sh_exe = 1;
 int batch_mode_shell = 1;
-#else  /* WINDOWS32 */
-# ifdef _AMIGA
+
+#elif defined (_AMIGA)
+
 char default_shell[] = "";
 extern int MyExecute (char **);
-# else /* _AMIGA */
-#  ifdef __MSDOS__
+int batch_mode_shell = 0;
+
+#elif defined (__MSDOS__)
+
 /* The default shell is a pointer so we can change it if Makefile
    says so.  It is without an explicit path so we get a chance
    to search the $PATH for it (since MSDOS doesn't have standard
    directories we could trust).  */
 char *default_shell = "command.com";
-#  else  /* __MSDOS__ */
-#   ifdef VMS
-#    include <descrip.h>
+int batch_mode_shell = 0;
+
+#elif defined (__EMX__)
+
+const char *default_shell = "/bin/sh";
+int batch_mode_shell = 0;
+
+#elif defined (VMS)
+
+# include <descrip.h>
 char default_shell[] = "";
-#   else
+int batch_mode_shell = 0;
+
+#else
+
 char default_shell[] = "/bin/sh";
-#   endif /* VMS */
-#  endif /* __MSDOS__ */
 int batch_mode_shell = 0;
-# endif /* _AMIGA */
-#endif /* WINDOWS32 */
+
+#endif
 
 #ifdef __MSDOS__
 # include <process.h>
@@ -91,9 +103,7 @@ static int amiga_batch_file;
 # include "pathstuff.h"
 #endif /* WINDOWS32 */
 
-#ifdef HAVE_FCNTL_H
-# include <fcntl.h>
-#else
+#ifdef __EMX__
 # include <sys/file.h>
 #endif
 
@@ -157,17 +167,6 @@ extern int wait ();
 
 #endif /* Don't have `union wait'.  */
 
-/* How to set close-on-exec for a file descriptor.  */
-
-#if !defined F_SETFD
-# define CLOSE_ON_EXEC(_d)
-#else
-# ifndef FD_CLOEXEC
-#  define FD_CLOEXEC 1
-# endif
-# define CLOSE_ON_EXEC(_d) (void) fcntl ((_d), F_SETFD, FD_CLOEXEC)
-#endif
-
 #ifdef VMS
 static int vms_jobsefnmask = 0;
 #endif /* !VMS */
@@ -228,11 +227,57 @@ int unixy_shell = 1;
 /*
  * The macro which references this function is defined in make.h.
  */
-int w32_kill(int pid, int sig)
+int
+w32_kill(int pid, int sig)
 {
   return ((process_kill(pid, sig) == TRUE) ? 0 : -1);
 }
 #endif /* WINDOWS32 */
+
+#ifdef __EMX__
+/* returns whether path is assumed to be a unix like shell. */
+int
+_is_unixy_shell (const char *path)
+{
+  /* list of non unix shells */
+  const char *known_os2shells[] = {
+    "cmd.exe",
+    "cmd",
+    "4os2.exe",
+    "4os2",
+    "4dos.exe",
+    "4dos",
+    "command.com",
+    "command",
+    NULL
+  };
+
+  /* find the rightmost '/' or '\\' */
+  const char *name = strrchr (path, '/');
+  const char *p = strrchr (path, '\\');
+  unsigned i;
+
+  if (name && p)    /* take the max */
+    name = (name > p) ? name : p;
+  else if (p)       /* name must be 0 */
+    name = p;
+  else if (!name)   /* name and p must be 0 */
+    name = path;
+
+  if (*name == '/' || *name == '\\') name++;
+
+  i = 0;
+  while (known_os2shells[i] != NULL) {
+    if (stricmp (name, known_os2shells[i]) == 0) /* strcasecmp() */
+      return 0; /* not a unix shell */
+    i++;
+  }
+
+  /* in doubt assume a unix like shell */
+  return 1;
+}
+#endif /* __EMX__ */
+
 \f
 /* Write an error message describing the exit status given in
    EXIT_CODE, EXIT_SIG, and COREDUMP, for the target TARGET_NAME.
@@ -384,6 +429,7 @@ handle_apos (char *p)
 
 static unsigned int dead_children = 0;
 
+#ifndef __EMX__ /* Don't use SIGCHLD handler on OS/2. */
 RETSIGTYPE
 child_handler (int sig)
 {
@@ -397,7 +443,7 @@ child_handler (int sig)
 
   DB (DB_JOBS, (_("Got a SIGCHLD; %u unreaped children.\n"), dead_children));
 }
-
+#endif  /* !__EMX__ */
 
 extern int shell_function_pid, shell_function_completed;
 
@@ -527,6 +573,18 @@ reap_children (int block, int err)
              exit_code = WEXITSTATUS (status);
              exit_sig = WIFSIGNALED (status) ? WTERMSIG (status) : 0;
              coredump = WCOREDUMP (status);
+
+#ifdef __EMX__
+             /* the SIGCHLD handler must not be used on OS/2 because, unlike
+                on UNIX systems, it had to call wait() itself.  Therefore
+                job_rfd has to be closed here.  */
+             if (job_rfd >= 0)
+               {
+                 close (job_rfd);
+                 job_rfd = -1;
+               }
+#endif
+
            }
          else
            {
@@ -773,10 +831,12 @@ free_child (struct child *child)
   if (job_fds[1] >= 0 && children)
     {
       char token = '+';
+      int r;
 
       /* Write a job token back to the pipe.  */
 
-      if (write (job_fds[1], &token, 1) != 1)
+      EINTRLOOP (r, write (job_fds[1], &token, 1));
+      if (r != 1)
        pfatal_with_name (_("write jobserver"));
 
       DB (DB_JOBS, (_("Released token for child 0x%08lx (%s).\n"),
@@ -832,6 +892,10 @@ unblock_sigs (void)
 #endif
 
 #ifdef MAKE_JOBSERVER
+# ifdef __EMX__
+/* Never install the SIGCHLD handler for EMX!!! */
+#  define set_child_handler_action_flags(x)
+# else
 /* Set the child handler action flags to FLAGS.  */
 static void
 set_child_handler_action_flags (int flags)
@@ -847,6 +911,7 @@ set_child_handler_action_flags (int flags)
   sigaction (SIGCLD, &sa, NULL);
 #endif
 }
+#endif  /* !__EMX__ */
 #endif
 
 
@@ -996,7 +1061,7 @@ start_job_command (struct child *child)
 
 #if !defined(VMS) && !defined(_AMIGA)
   if (
-#ifdef __MSDOS__
+#if defined __MSDOS__ || defined (__EMX__)
       unixy_shell      /* the test is complicated and we already did it */
 #else
       (argv[0] && !strcmp (argv[0], "/bin/sh"))
@@ -1119,6 +1184,40 @@ start_job_command (struct child *child)
 #else
 
       parent_environ = environ;
+
+# ifdef __EMX__
+      /* If we aren't running a recursive command and we have a jobserver
+         pipe, close it before exec'ing.  */
+      if (!(flags & COMMANDS_RECURSE) && job_fds[0] >= 0)
+       {
+         CLOSE_ON_EXEC (job_fds[0]);
+         CLOSE_ON_EXEC (job_fds[1]);
+       }
+      if (job_rfd >= 0)
+       CLOSE_ON_EXEC (job_rfd);
+
+      /* Never use fork()/exec() here! Use spawn() instead in exec_command() */
+      child->pid = child_execute_job (child->good_stdin ? 0 : bad_stdin, 1,
+                                      argv, child->environment);
+      if (child->pid < 0)
+       {
+         /* spawn failed!  */
+         unblock_sigs ();
+         perror_with_name ("spawn", "");
+         goto error;
+       }
+
+      /* undo CLOSE_ON_EXEC() after the child process has been started */
+      if (!(flags & COMMANDS_RECURSE) && job_fds[0] >= 0)
+       {
+         fcntl (job_fds[0], F_SETFD, 0);
+         fcntl (job_fds[1], F_SETFD, 0);
+       }
+      if (job_rfd >= 0)
+       fcntl (job_rfd, F_SETFD, 0);
+
+#else  /* !__EMX__ */
+
       child->pid = vfork ();
       environ = parent_environ;        /* Restore value child may have clobbered.  */
       if (child->pid == 0)
@@ -1146,6 +1245,7 @@ start_job_command (struct child *child)
          perror_with_name ("vfork", "");
          goto error;
        }
+# endif  /* !__EMX__ */
 #endif /* !VMS */
     }
 
@@ -2175,10 +2275,13 @@ child_execute_job (char *argv, struct child *child)
                      &child->pid, &child->cstatus, &child->efn,
                      0, 0,
                      0, 0, 0);
-  pidToAbort= child->pid;
-  status= sys$waitfr (child->efn);
-  pidToAbort= 0;
-  vmsHandleChildTerm(child);
+  if (status & 1)
+    {
+      pidToAbort= child->pid;
+      status= sys$waitfr (child->efn);
+      pidToAbort= 0;
+      vmsHandleChildTerm(child);
+    }
 #else
   status = lib$spawn (&cmddsc,
                      (ifiledsc.dsc$w_length == 0)?0:&ifiledsc,
@@ -2194,6 +2297,14 @@ child_execute_job (char *argv, struct child *child)
     {
       printf (_("Error spawning, %d\n") ,status);
       fflush (stdout);
+      switch (status)
+        {
+        case 0x1c:
+          errno = EPROCLIM;
+          break;
+        default:
+          errno = EFAIL;
+        }
     }
 
   if (comname && !ISDB (DB_JOBS))
@@ -2204,12 +2315,60 @@ child_execute_job (char *argv, struct child *child)
 
 #else /* !VMS */
 
-#if !defined (_AMIGA) && !defined (__MSDOS__)
+/* EMX: Start a child process. This function returns the new pid.  */
+# if defined __MSDOS__ ||  defined __EMX__
+int
+child_execute_job (int stdin_fd, int stdout_fd, char **argv, char **envp)
+{
+  int pid;
+  /* stdin_fd == 0 means: nothing to do for stdin;
+     stdout_fd == 1 means: nothing to do for stdout */
+  int save_stdin = (stdin_fd != 0) ? dup (0) : 0;
+  int save_stdout = (stdout_fd != 1) ? dup (1): 1;
+
+  /* < 0 only if dup() failed */
+  if (save_stdin < 0)
+    fatal (NILF, _("could not duplicate stdin\n"));
+  if (save_stdout < 0)
+    fatal (NILF, _("could not duplicate stdout\n"));
+
+  /* Close unnecessary file handles for the child.  */
+  if (save_stdin != 0)
+    CLOSE_ON_EXEC (save_stdin);
+  if (save_stdout != 1)
+    CLOSE_ON_EXEC (save_stdout);
+
+  /* Connect the pipes to the child process.  */
+  if (stdin_fd != 0)
+    (void) dup2 (stdin_fd, 0);
+  if (stdout_fd != 1)
+    (void) dup2 (stdout_fd, 1);
+
+  /* stdin_fd and stdout_fd must be closed on exit because we are
+     still in the parent process */
+  if (stdin_fd != 0)
+    CLOSE_ON_EXEC (stdin_fd);
+  if (stdout_fd != 1)
+    CLOSE_ON_EXEC (stdout_fd);
+
+  /* Run the command.  */
+  pid = exec_command (argv, envp);
+
+  /* Restore stdout/stdin of the parent process.  */
+  if (stdin_fd != 0 && dup2 (save_stdin, 0) != 0)
+    fatal (NILF, _("restoring of stdin failed\n"));
+  if (stdout_fd != 1 && dup2 (save_stdout, 1) != 1)
+    fatal (NILF, _("restoring of stdout failed\n"));
+
+  return pid;
+}
+
+#elif !defined (_AMIGA) && !defined (__MSDOS__)
+
 /* UNIX:
    Replace the current process with one executing the command in ARGV.
    STDIN_FD and STDOUT_FD are used as the process's stdin and stdout; ENVP is
    the environment of the new program.  This function does not return.  */
-
 void
 child_execute_job (int stdin_fd, int stdout_fd, char **argv, char **envp)
 {
@@ -2233,7 +2392,12 @@ child_execute_job (int stdin_fd, int stdout_fd, char **argv, char **envp)
 /* Replace the current process with one running the command in ARGV,
    with environment ENVP.  This function does not return.  */
 
-void
+/* EMX: This function returns the pid of the child process.  */
+# ifdef __EMX__
+int
+# else
+ void
+# endif
 exec_command (char **argv, char **envp)
 {
 #ifdef VMS
@@ -2301,13 +2465,33 @@ exec_command (char **argv, char **envp)
 
 #else  /* !WINDOWS32 */
 
+# ifdef __EMX__
+  int pid;
+# endif
+
   /* Be the user, permanently.  */
   child_access ();
 
+# ifdef __EMX__
+
+  /* Run the program.  */
+  pid = spawnvpe (P_NOWAIT, argv[0], argv, envp);
+
+  if (pid >= 0)
+    return pid;
+
+  /* the file might have a strange shell extension */
+  if (errno == ENOENT)
+    errno = ENOEXEC;
+
+# else
+
   /* Run the program.  */
   environ = envp;
   execvp (argv[0], argv);
 
+# endif /* !__EMX__ */
+
   switch (errno)
     {
     case ENOENT:
@@ -2321,7 +2505,14 @@ exec_command (char **argv, char **envp)
        char **new_argv;
        int argc;
 
+# ifdef __EMX__
+        /* Do not use $SHELL from the environment */
+       shell = lookup_variable ("SHELL", 5);
+       if (shell)
+         shell = shell->value;
+# else
        shell = getenv ("SHELL");
+# endif
        if (shell == 0)
          shell = default_shell;
 
@@ -2338,7 +2529,13 @@ exec_command (char **argv, char **envp)
            --argc;
          }
 
+# ifdef __EMX__
+       pid = spawnvpe (P_NOWAIT, shell, new_argv, envp);
+       if (pid >= 0)
+          break;
+# else
        execvp (shell, new_argv);
+# endif
        if (errno == ENOENT)
          error (NILF, _("%s: Shell program not found"), shell);
        else
@@ -2346,12 +2543,23 @@ exec_command (char **argv, char **envp)
        break;
       }
 
+# ifdef __EMX__
+    case EINVAL:
+      /* this nasty error was driving me nuts :-( */
+      error (NILF, _("spawnvpe: environment space might be exhausted"));
+      /* FALLTHROUGH */
+# endif
+
     default:
       perror_with_name ("execvp: ", argv[0]);
       break;
     }
 
+# ifdef __EMX__
+  return pid;
+# else
   _exit (127);
+# endif
 #endif /* !WINDOWS32 */
 #endif /* !VMS */
 }
@@ -2428,16 +2636,43 @@ construct_command_argv_internal (char *line, char **restp, char *shell,
 
   char *sh_chars;
   char **sh_cmds;
-#else
-#ifdef _AMIGA
+#elif defined (__EMX__)
+  static char sh_chars_dos[] = "*?[];|<>%^&()";
+  static char *sh_cmds_dos[] = { "break", "call", "cd", "chcp", "chdir", "cls",
+                                "copy", "ctty", "date", "del", "dir", "echo",
+                                "erase", "exit", "for", "goto", "if", "md",
+                                "mkdir", "path", "pause", "prompt", "rd",
+                                "rmdir", "rem", "ren", "rename", "set",
+                                "shift", "time", "type", "ver", "verify",
+                                "vol", ":", 0 };
+
+  static char sh_chars_os2[] = "*?[];|<>%^()\"'&";
+  static char *sh_cmds_os2[] = { "call", "cd", "chcp", "chdir", "cls", "copy",
+                            "date", "del", "detach", "dir", "echo",
+                            "endlocal", "erase", "exit", "for", "goto", "if",
+                            "keys", "md", "mkdir", "move", "path", "pause",
+                            "prompt", "rd", "rem", "ren", "rename", "rmdir",
+                            "set", "setlocal", "shift", "start", "time",
+                             "type", "ver", "verify", "vol", ":", 0 };
+
+  static char sh_chars_sh[]  = "#;\"*?[]&|<>(){}$`^";
+  static char *sh_cmds_sh[]  = { "echo", "cd", "eval", "exec", "exit", "login",
+                                "logout", "set", "umask", "wait", "while",
+                                "for", "case", "if", ":", ".", "break",
+                                "continue", "export", "read", "readonly",
+                                "shift", "times", "trap", "switch", "unset",
+                                 0 };
+  char *sh_chars;
+  char **sh_cmds;
+
+#elif defined (_AMIGA)
   static char sh_chars[] = "#;\"|<>()?*$`";
   static char *sh_cmds[] = { "cd", "eval", "if", "delete", "echo", "copy",
                             "rename", "set", "setenv", "date", "makedir",
                             "skip", "else", "endif", "path", "prompt",
                             "unset", "unsetenv", "version",
                             0 };
-#else
-#ifdef WINDOWS32
+#elif defined (WINDOWS32)
   static char sh_chars_dos[] = "\"|&<>";
   static char *sh_cmds_dos[] = { "break", "call", "cd", "chcp", "chdir", "cls",
                             "copy", "ctty", "date", "del", "dir", "echo",
@@ -2464,8 +2699,6 @@ construct_command_argv_internal (char *line, char **restp, char *shell,
                             "case", "if", ":", ".", "break", "continue",
                             "export", "read", "readonly", "shift", "times",
                             "trap", "switch", 0 };
-#endif /* WINDOWS32 */
-#endif /* Amiga */
 #endif /* __MSDOS__ */
   register int i;
   register char *p;
@@ -2513,7 +2746,7 @@ construct_command_argv_internal (char *line, char **restp, char *shell,
   if (slow_flag)
     goto slow;
 #else  /* not WINDOWS32 */
-#ifdef __MSDOS__
+#if defined (__MSDOS__) || defined (__EMX__)
   else if (stricmp (shell, default_shell))
     {
       extern int _is_unixy_shell (const char *_path);
@@ -2521,6 +2754,10 @@ construct_command_argv_internal (char *line, char **restp, char *shell,
       message (1, _("$SHELL changed (was `%s', now `%s')"), default_shell, shell);
       unixy_shell = _is_unixy_shell (shell);
       default_shell = shell;
+      /* we must allocate a copy of shell: construct_command_argv() will free
+       * shell after this function returns.  */
+      default_shell = xmalloc (strlen (shell) + 1);
+      strcpy (default_shell, shell);
     }
   if (unixy_shell)
     {
@@ -2531,11 +2768,17 @@ construct_command_argv_internal (char *line, char **restp, char *shell,
     {
       sh_chars = sh_chars_dos;
       sh_cmds  = sh_cmds_dos;
-    }
-#else  /* not __MSDOS__ */
+# ifdef __EMX__
+      if (_osmode == OS2_MODE)
+        {
+          sh_chars = sh_chars_os2;
+          sh_cmds = sh_cmds_os2;
+        }
+# endif
+#else  /* !__MSDOS__ */
   else if (strcmp (shell, default_shell))
     goto slow;
-#endif /* not __MSDOS__ */
+#endif /* !__MSDOS__ && !__EMX__ */
 #endif /* not WINDOWS32 */
 
   if (ifs != 0)
@@ -2838,6 +3081,11 @@ construct_command_argv_internal (char *line, char **restp, char *shell,
                                      + (line_len * 2) + 1);
     char *command_ptr = NULL; /* used for batch_mode_shell mode */
 
+# ifdef __EMX__ /* is this necessary? */
+    if (!unixy_shell)
+      minus_c[1] = '/'; /* " /c " */
+# endif
+
     ap = new_line;
     bcopy (shell, ap, shell_len);
     ap += shell_len;
@@ -2951,18 +3199,118 @@ construct_command_argv_internal (char *line, char **restp, char *shell,
       new_argv = construct_command_argv_internal (new_line, (char **) NULL,
                                                   (char *) 0, (char *) 0,
                                                   (char **) 0);
-#ifdef  __MSDOS__
+# ifdef __EMX__
+    else if (!unixy_shell)
+      {
+       /* new_line is local, must not be freed therefore */
+       char *p, *q;
+       int quote;
+       size_t index;
+       size_t len;
+
+       /* handle quotes
+          We have to remove all double quotes and to split the line
+          into distinct arguments because of the strange handling
+          of builtin commands by cmd: 'echo "bla"' prints "bla"
+          (with quotes) while 'c:\bin\echo.exe "bla"' prints bla
+          (without quotes). Some programs like autoconf rely
+          on the second behaviour. */
+
+       len = strlen (new_line) + 1;
+
+       /* More than 1 arg per character is impossible.  */
+       new_argv = (char **) xmalloc (len * sizeof (char *));
+
+       /* All the args can fit in a buffer as big as new_line is.   */
+       new_argv[0] = (char *) xmalloc (len);
+
+       index = 0;
+       quote = 0;
+       q = new_line;
+       p = new_argv[index];
+       while(*q != '\0')
+         {
+           /* searching for closing quote */
+           if (quote)
+             {
+               if (*q == quote)
+                 {
+                   /* remove the quote */
+                   while(*q == quote) /* do not ask */
+                     q++;
+                   quote = 0;
+                 }
+               else /* normal character: copy it */
+                 *p++ = *q++;
+             }
+
+           /* searching for opening quote */
+           else if (*q == '\"'
+#  ifndef NO_CMD_DEFAULT
+                    || *q == '\''
+#  endif
+                    )
+             {
+               /* remove opening quote */
+               quote = *q;
+               while(*q == quote) /* do not ask */
+                 q++;
+             }
+
+           /* spaces outside of a quoted string: remove them
+              and start a new argument */
+           else if (*q == ' ' || *q == '\t')
+             {
+               *p++ = '\0'; /* trailing '\0' for last argument */
+
+               /* remove all successive spaces */
+               do
+                 {
+                   q++;
+                 }
+               while(*q == ' ' || *q == '\t');
+
+               /* start new argument */
+               index++;
+               new_argv[index] = p;
+             }
+
+           /* normal character (no space) outside a quoted string*/
+           else
+             *p++ = *q++;
+         } /* end while() */
+
+       *p = '\0'; /* trailing '\0' for the last argument */
+       new_argv[index + 1] = NULL;
+
+#  ifndef NO_CMD_DEFAULT
+       /* special case: echo x="y"
+          (e.g. autoconf uses this to determine whether make works)
+          this is pure idioty but cmd works this way:
+          if 'echo' and 'x="y"' are two different arguments cmd
+          will print '"x="y""' but if they are only one argument
+          cmd will print 'bla="blurb"' as it should be
+          note: if we do not allow cmd to be the default shell
+          we do not need this kind of voodoo */
+       if (index == 3 && strcasecmp(new_argv[2], "echo") == 0)
+         {
+           new_argv[2][4] = ' ';
+           new_argv[3] = NULL;
+         }
+#  endif
+      }
+#elif defined(__MSDOS__)
     else
       {
-      /* With MSDOS shells, we must construct the command line here
-         instead of recursively calling ourselves, because we
-         cannot backslash-escape the special characters (see above).  */
-      new_argv = (char **) xmalloc (sizeof (char *));
-      line_len = strlen (new_line) - shell_len - sizeof (minus_c) + 1;
-      new_argv[0] = xmalloc (line_len + 1);
-      strncpy (new_argv[0],
-               new_line + shell_len + sizeof (minus_c) - 1, line_len);
-      new_argv[0][line_len] = '\0';
+        /* With MSDOS shells, we must construct the command line here
+           instead of recursively calling ourselves, because we
+           cannot backslash-escape the special characters (see above).  */
+        new_argv = (char **) xmalloc (sizeof (char *));
+        line_len = strlen (new_line) - shell_len - sizeof (minus_c) + 1;
+        new_argv[0] = xmalloc (line_len + 1);
+        strncpy (new_argv[0],
+                 new_line + shell_len + sizeof (minus_c) - 1, line_len);
+        new_argv[0][line_len] = '\0';
       }
 #else
     else
@@ -3049,10 +3397,55 @@ construct_command_argv (char *line, char **restp, struct file *file,
      * is not confused.
      */
     if (shell) {
-      char *p = w32ify(shell, 0);
-      strcpy(shell, p);
+      char *p = w32ify (shell, 0);
+      strcpy (shell, p);
     }
 #endif
+#ifdef __EMX__
+    {
+      static const char *unixroot = NULL;
+      static const char *last_shell = "";
+      static int init = 0;
+      if (init == 0)
+       {
+         unixroot = getenv ("UNIXROOT");
+         /* unixroot must be NULL or not empty */
+         if (unixroot && unixroot[0] == '\0') unixroot = NULL;
+         init = 1;
+       }
+
+      /* if we have an unixroot drive and if shell is not default_shell
+         (which means it's either cmd.exe or the test has already been
+         performed) and if shell is an absolute path without drive letter,
+         try whether it exists e.g.: if "/bin/sh" does not exist use
+         "$UNIXROOT/bin/sh" instead.  */
+      if (unixroot && shell && strcmp (shell, last_shell) != 0
+         && (shell[0] == '/' || shell[0] == '\\'))
+       {
+         /* trying a new shell, check whether it exists */
+         size_t size = strlen (shell);
+         char *buf = xmalloc (size + 7);
+         memcpy (buf, shell, size);
+         memcpy (buf + size, ".exe", 5); /* including the trailing '\0' */
+          if (access (shell, F_OK) != 0 && access (buf, F_OK) != 0)
+           {
+             /* try the same for the unixroot drive */
+             memmove (buf + 2, buf, size + 5);
+             buf[0] = unixroot[0];
+             buf[1] = unixroot[1];
+             if (access (buf, F_OK) == 0)
+               /* we have found a shell! */
+               /* free(shell); */
+               shell = buf;
+             else
+               free (buf);
+           }
+         else
+            free (buf);
+       }
+    }
+#endif __EMX__
+
     ifs = allocated_variable_expand_for_file ("$(IFS)", file);
 
     warn_undefined_variables_flag = save;
diff --git a/job.h b/job.h
index 00e9599..85176ea 100644 (file)
--- a/job.h
+++ b/job.h
@@ -20,6 +20,23 @@ Boston, MA 02111-1307, USA.  */
 #ifndef SEEN_JOB_H
 #define SEEN_JOB_H
 
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#else
+# include <sys/file.h>
+#endif
+
+/* How to set close-on-exec for a file descriptor.  */
+
+#if !defined F_SETFD
+# define CLOSE_ON_EXEC(_d)
+#else
+# ifndef FD_CLOEXEC
+#  define FD_CLOEXEC 1
+# endif
+# define CLOSE_ON_EXEC(_d) (void) fcntl ((_d), F_SETFD, FD_CLOEXEC)
+#endif
+
 /* Structure describing a running or dead child process.  */
 
 struct child
@@ -57,11 +74,15 @@ extern void start_waiting_jobs PARAMS ((void));
 extern char **construct_command_argv PARAMS ((char *line, char **restp, struct file *file, char** batch_file));
 #ifdef VMS
 extern int child_execute_job PARAMS ((char *argv, struct child *child));
+#elif defined(__EMX__)
+extern int child_execute_job PARAMS ((int stdin_fd, int stdout_fd, char **argv, char **envp));
 #else
 extern void child_execute_job PARAMS ((int stdin_fd, int stdout_fd, char **argv, char **envp));
 #endif
 #ifdef _AMIGA
 extern void exec_command PARAMS ((char **argv));
+#elif defined(__EMX__)
+extern int exec_command PARAMS ((char **argv, char **envp));
 #else
 extern void exec_command PARAMS ((char **argv, char **envp));
 #endif
diff --git a/main.c b/main.c
index 50b44d2..2b48230 100644 (file)
--- a/main.c
+++ b/main.c
@@ -37,6 +37,10 @@ MA 02111-1307, USA.  */
 #include <windows.h>
 #include "pathstuff.h"
 #endif
+#ifdef __EMX__
+# include <sys/types.h>
+# include <sys/wait.h>
+#endif
 #if defined(MAKE_JOBSERVER) && defined(HAVE_FCNTL_H)
 # include <fcntl.h>
 #endif
@@ -846,6 +850,9 @@ main (int argc, char **argv, char **envp)
   no_default_sh_exe = 1;
 #endif
 
+  /* Needed for OS/2 */
+  initialize_main(&argc, &argv);
+
   default_goal_file = 0;
   reading_file = 0;
 
@@ -952,7 +959,7 @@ main (int argc, char **argv, char **envp)
 #else
       program = strrchr (argv[0], '/');
 #endif
-#ifdef __MSDOS__
+#if defined(__MSDOS__) || defined(__EMX__)
       if (program == 0)
        program = strrchr (argv[0], '\\');
       else
@@ -1136,7 +1143,7 @@ main (int argc, char **argv, char **envp)
       strneq(argv[0], "//", 2))
     argv[0] = xstrdup(w32ify(argv[0],1));
 #else /* WINDOWS32 */
-#ifdef __MSDOS__
+#if defined (__MSDOS__) || defined (__EMX__)
   if (strchr (argv[0], '\\'))
     {
       char *p;
@@ -1325,7 +1332,7 @@ main (int argc, char **argv, char **envp)
 #define DEFAULT_TMPFILE     "GmXXXXXX"
 
            if (((tmpdir = getenv ("TMPDIR")) == NULL || *tmpdir == '\0')
-#if defined __MSDOS__ || defined(WINDOWS32)
+#if defined (__MSDOS__) || defined (WINDOWS32) || defined (__EMX__)
                 /* These are also used commonly on these platforms.  */
                 && ((tmpdir = getenv ("TEMP")) == NULL || *tmpdir == '\0')
                 && ((tmpdir = getenv ("TMP")) == NULL || *tmpdir == '\0')
@@ -1376,6 +1383,7 @@ main (int argc, char **argv, char **envp)
          }
     }
 
+#ifndef __EMX__ /* Don't use a SIGCHLD handler for OS/2 */
 #if defined(MAKE_JOBSERVER) || !defined(HAVE_WAIT_NOHANG)
   /* Set up to handle children dying.  This must be done before
      reading in the makefiles so that `shell' function calls will work.
@@ -1399,6 +1407,7 @@ main (int argc, char **argv, char **envp)
 # endif
   }
 #endif
+#endif
 
   /* Let the user send us SIGUSR1 to toggle the -d flag during the run.  */
 #ifdef SIGUSR1
@@ -1448,7 +1457,7 @@ main (int argc, char **argv, char **envp)
   }
 #endif /* WINDOWS32 */
 
-#ifdef __MSDOS__
+#if defined (__MSDOS__) || defined (__EMX__)
   /* We need to know what kind of shell we will be using.  */
   {
     extern int _is_unixy_shell (const char *_path);
@@ -1468,7 +1477,7 @@ main (int argc, char **argv, char **envp)
          default_shell = shell_path;
       }
   }
-#endif /* __MSDOS__ */
+#endif /* __MSDOS__ || __EMX__ */
 
   /* Decode switches again, in case the variables were set by the makefile.  */
   decode_env_switches ("MAKEFLAGS", 9);
@@ -1476,8 +1485,12 @@ main (int argc, char **argv, char **envp)
   decode_env_switches ("MFLAGS", 6);
 #endif
 
-#ifdef __MSDOS__
-  if (job_slots != 1)
+#if defined (__MSDOS__) || defined (__EMX__)
+  if (job_slots != 1
+# ifdef __EMX__
+      && _osmode != OS2_MODE /* turn off -j if we are in DOS mode */
+# endif
+      )
     {
       error (NILF,
              _("Parallel jobs (-j) are not supported on this platform."));
@@ -1557,8 +1570,13 @@ main (int argc, char **argv, char **envp)
          want job_slots to be 0 to indicate we're using the jobserver.  */
 
       while (--job_slots)
-        if (write (job_fds[1], &c, 1) != 1)
-         pfatal_with_name (_("init jobserver pipe"));
+        {
+          int r;
+
+          EINTRLOOP (r, write (job_fds[1], &c, 1));
+          if (r != 1)
+            pfatal_with_name (_("init jobserver pipe"));
+        }
 
       /* Fill in the jobserver_fds struct for our children.  */
 
@@ -1880,11 +1898,30 @@ main (int argc, char **argv, char **envp)
           if (job_rfd >= 0)
             close (job_rfd);
 
-#ifndef _AMIGA
-         exec_command (nargv, environ);
-#else
+#ifdef _AMIGA
          exec_command (nargv);
          exit (0);
+#elif defined (__EMX__)
+         {
+           /* It is not possible to use execve() here because this
+              would cause the parent process to be terminated with
+              exit code 0 before the child process has been terminated.
+              Therefore it may be the best solution simply to spawn the
+              child process including all file handles and to wait for its
+              termination. */
+           int pid;
+           int status;
+           pid = child_execute_job(0, 1, nargv, environ);
+
+           /* is this loop really necessary? */
+           do {
+             pid = wait(&status);
+           } while(pid <= 0);
+           /* use the exit code of the child process */
+           exit(WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE);
+         }
+#else
+         exec_command (nargv, environ);
 #endif
          /* NOTREACHED */
 
diff --git a/make.h b/make.h
index 3ac47d1..5d003de 100644 (file)
--- a/make.h
+++ b/make.h
@@ -349,15 +349,12 @@ extern int strcmpi (const char *,const char *);
 #define S_(msg1,msg2,num)   ngettext (msg1,msg2,num)
 
 /* Handle other OSs.  */
-
-#if defined(__MSDOS__) || defined(WINDOWS32)
+#if defined(HAVE_DOS_PATHS)
 # define PATH_SEPARATOR_CHAR ';'
+#elif defined(VMS)
+# define PATH_SEPARATOR_CHAR ','
 #else
-# if defined(VMS)
-#  define PATH_SEPARATOR_CHAR ','
-# else
-#  define PATH_SEPARATOR_CHAR ':'
-# endif
+# define PATH_SEPARATOR_CHAR ':'
 #endif
 
 #ifdef WINDOWS32
@@ -538,6 +535,42 @@ extern int handling_fatal_signal;
 #include <dmalloc.h>
 #endif
 
+#ifndef initialize_main
+# ifdef __EMX__
+#  define initialize_main(pargc, pargv) \
+                          { _wildcard(pargc, pargv); _response(pargc, pargv); }
+# else
+#  define initialize_main(pargc, pargv)
+# endif
+#endif
+
+
+#ifdef __EMX__
+# if !HAVE_STRCASECMP
+#  define strcasecmp stricmp
+# endif
+
+# if !defined chdir
+#  define chdir _chdir2
+# endif
+# if !defined getcwd
+#  define getcwd _getcwd2
+# endif
+
+/* NO_CHDIR2 causes make not to use _chdir2() and _getcwd2() instead of
+   chdir() and getcwd(). This avoids some error messages for the
+   make testsuite but restricts the drive letter support. */
+# ifdef NO_CHDIR2
+#  warning NO_CHDIR2: usage of drive letters restricted
+#  undef chdir
+#  undef getcwd
+# endif
+#endif
+
+#ifndef initialize_main
+# define initialize_main(pargc, pargv)
+#endif
+
 
 /* Some systems (like Solaris, PTX, etc.) do not support the SA_RESTART flag
    properly according to POSIX.  So, we try to wrap common system calls with
diff --git a/read.c b/read.c
index 47d727b..40ff3d6 100644 (file)
--- a/read.c
+++ b/read.c
@@ -561,12 +561,16 @@ eval (struct ebuffer *ebuf, int set_default)
            }
        }
 
-      /* This line is not a shell command line.  Don't worry about tabs.  */
+      /* This line is not a shell command line.  Don't worry about tabs.
+         Get more space if we need it; we don't need to preserve the current
+         contents of the buffer.  */
 
       if (collapsed_length < linelen+1)
        {
          collapsed_length = linelen+1;
-         collapsed = (char *) xrealloc (collapsed, collapsed_length);
+          if (collapsed)
+            free ((char *)collapsed);
+         collapsed = (char *) xmalloc (collapsed_length);
        }
       strcpy (collapsed, line);
       /* Collapse continuation lines.  */
@@ -1150,7 +1154,7 @@ eval (struct ebuffer *ebuf, int set_default)
 
           do {
             check_again = 0;
-            /* For DOS paths, skip a "C:\..." or a "C:/..." */
+            /* For DOS-style paths, skip a "C:\..." or a "C:/..." */
             if (p != 0 && (p[1] == '\\' || p[1] == '/') &&
                 isalpha ((unsigned char)p[-1]) &&
                 (p == p2 + 1 || strchr (" \t:(", p[-2]) != 0)) {
@@ -2559,7 +2563,7 @@ readline (struct ebuffer *ebuf)
       /* We got a newline, so add one to the count of lines.  */
       ++nlines;
 
-#if !defined(WINDOWS32) && !defined(__MSDOS__)
+#if !defined(WINDOWS32) && !defined(__MSDOS__) && !defined(__EMX__)
       /* Check to see if the line was really ended with CRLF; if so ignore
          the CR.  */
       if ((p - start) > 1 && p[-2] == '\r')
index c679d11..e6dac0f 100644 (file)
--- a/remake.c
+++ b/remake.c
@@ -1200,6 +1200,14 @@ f_mtime (struct file *file, int search)
        FILE_TIMESTAMP adjustment = FAT_ADJ_OFFSET << FILE_TIMESTAMP_LO_BITS;
        if (ORDINARY_MTIME_MIN + adjustment <= adjusted_mtime)
          adjusted_mtime -= adjustment;
+#elif defined(__EMX__)
+       /* FAT filesystems round time to the nearest even second!
+          Allow for any file (NTFS or FAT) to perhaps suffer from this
+          brain damage.  */
+       FILE_TIMESTAMP adjustment = (((FILE_TIMESTAMP_S (adjusted_mtime) & 1) == 0
+                      && FILE_TIMESTAMP_NS (adjusted_mtime) == 0)
+                     ? (FILE_TIMESTAMP) 1 << FILE_TIMESTAMP_LO_BITS
+                     : 0);
 #endif
 
        /* If the file's time appears to be in the future, update our
index fe8c4c8..723c950 100644 (file)
@@ -544,7 +544,7 @@ merge_variable_set_lists (struct variable_set_list **setlist0,
 void
 define_automatic_variables (void)
 {
-#ifdef WINDOWS32
+#if defined(WINDOWS32) || defined(__EMX__)
   extern char* default_shell;
 #else
   extern char default_shell[];
@@ -585,6 +585,54 @@ define_automatic_variables (void)
          (void) define_variable (shell_str, shlen, comp->value, o_env, 0);
       }
   }
+#elif defined(__EMX__)
+  {
+    static char shell_str[] = "SHELL";
+    const int shlen = sizeof (shell_str) - 1;
+    struct variable *shell = lookup_variable (shell_str, shlen);
+    struct variable *replace = lookup_variable ("MAKESHELL", 9);
+
+    /* if $MAKESHELL is defined in the environment assume o_env_override */
+    if (replace && *replace->value && replace->origin == o_env)
+      replace->origin = o_env_override;
+
+    /* if $MAKESHELL is not defined use $SHELL but only if the variable
+       did not come from the environment */
+    if (!replace || !*replace->value)
+      if (shell && *shell->value && (shell->origin == o_env
+         || shell->origin == o_env_override))
+       {
+         /* overwrite whatever we got from the environment */
+         free(shell->value);
+         shell->value = xstrdup (default_shell);
+         shell->origin = o_default;
+       }
+
+    /* Some people do not like cmd to be used as the default
+       if $SHELL is not defined in the Makefile.
+       With -DNO_CMD_DEFAULT you can turn off this behaviour */
+# ifndef NO_CMD_DEFAULT
+    /* otherwise use $COMSPEC */
+    if (!replace || !*replace->value)
+      replace = lookup_variable ("COMSPEC", 7);
+
+    /* otherwise use $OS2_SHELL */
+    if (!replace || !*replace->value)
+      replace = lookup_variable ("OS2_SHELL", 9);
+# else
+#   warning NO_CMD_DEFAULT: GNU make will not use CMD.EXE as default shell
+# endif
+
+    if (replace && *replace->value)
+      /* overwrite $SHELL */
+      (void) define_variable (shell_str, shlen, replace->value,
+                             replace->origin, 0);
+    else
+      /* provide a definition if there is none */
+      (void) define_variable (shell_str, shlen, default_shell,
+                             o_default, 0);
+  }
+
 #endif
 
   /* This won't override any definition, but it
@@ -594,8 +642,10 @@ define_automatic_variables (void)
 
   /* On MSDOS we do use SHELL from environment, since
      it isn't a standard environment variable on MSDOS,
-     so whoever sets it, does that on purpose.  */
-#ifndef __MSDOS__
+     so whoever sets it, does that on purpose.
+     On OS/2 we do not use SHELL from environment but
+     we have already handled that problem above. */
+#if !defined(__MSDOS__) && !defined(__EMX__)
   /* Don't let SHELL come from the environment.  */
   if (*v->value == '\0' || v->origin == o_env || v->origin == o_env_override)
     {
diff --git a/vpath.c b/vpath.c
index 5e04d08..7dae82d 100644 (file)
--- a/vpath.c
+++ b/vpath.c
@@ -247,7 +247,7 @@ construct_vpath_list (char *pattern, char *dirpath)
       len = p - v;
       /* Make sure there's no trailing slash,
         but still allow "/" as a directory.  */
-#ifdef __MSDOS__
+#if defined(__MSDOS__) || defined(__EMX__)
       /* We need also to leave alone a trailing slash in "d:/".  */
       if (len > 3 || (len > 1 && v[1] != ':'))
 #endif