From 4068c5e4a3eb0f47ec3cb4ee4fad5dd2edb9de6f Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Mon, 24 Mar 2003 23:14:15 +0000 Subject: Add support for OS/2, contributed by Andreas Buening Also a small patch from Hartmut Becker for VMS. --- ChangeLog | 49 ++++++ default.c | 10 +- dir.c | 4 + function.c | 32 +++- job.c | 511 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------- job.h | 21 +++ main.c | 61 ++++++-- make.h | 47 +++++- read.c | 12 +- remake.c | 8 + variable.c | 56 ++++++- vpath.c | 2 +- 12 files changed, 717 insertions(+), 96 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2b3ebc7..b99e6f7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,52 @@ +2003-02-25 Paul D. Smith + + Port to OS/2 (__EMX__) by Andreas Buening . + + * 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 + + * 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 . + + * 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 * read.c (eval): eval() was not fully reentrant, because the diff --git a/default.c b/default.c index 5b9d62d..d262a30 100644 --- 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 --- 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 diff --git a/function.c b/function.c index 2edac74..361e3a8 100644 --- a/function.c +++ b/function.c @@ -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 --- 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 +int batch_mode_shell = 0; + +#elif defined (__EMX__) + +const char *default_shell = "/bin/sh"; +int batch_mode_shell = 0; + +#elif defined (VMS) + +# include 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 @@ -91,9 +103,7 @@ static int amiga_batch_file; # include "pathstuff.h" #endif /* WINDOWS32 */ -#ifdef HAVE_FCNTL_H -# include -#else +#ifdef __EMX__ # include #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__ */ + /* 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 --- 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 +#else +# include +#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 --- a/main.c +++ b/main.c @@ -37,6 +37,10 @@ MA 02111-1307, USA. */ #include #include "pathstuff.h" #endif +#ifdef __EMX__ +# include +# include +#endif #if defined(MAKE_JOBSERVER) && defined(HAVE_FCNTL_H) # include #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. @@ -1398,6 +1406,7 @@ main (int argc, char **argv, char **envp) bsd_signal (SIGCLD, child_handler); # endif } +#endif #endif /* Let the user send us SIGUSR1 to toggle the -d flag during the run. */ @@ -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 --- 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 #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 --- 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') diff --git a/remake.c b/remake.c index c679d11..e6dac0f 100644 --- 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 diff --git a/variable.c b/variable.c index fe8c4c8..723c950 100644 --- a/variable.c +++ b/variable.c @@ -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 --- 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 -- cgit v1.2.3