This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: CYGWIN file input redirection
- From: Eli Zaretskii <eliz at gnu dot org>
- To: gdb-patches at sourceware dot org
- Cc: samsurfer117 at gmail dot com
- Date: Sat, 22 Oct 2016 12:30:33 +0300
- Subject: Re: CYGWIN file input redirection
- Authentication-results: sourceware.org; auth=none
- References: <CABcp_VQmLT=8WdXFQiJ8RohLh3obNnY6MLdEH8OQ0q6bm1PQ0A@mail.gmail.com> <83twcxn3ne.fsf@gnu.org> <8360ot3kzq.fsf@gnu.org> <83zim523ch.fsf@gnu.org>
- Reply-to: Eli Zaretskii <eliz at gnu dot org>
Ping! Is this OK to push to master, please?
> Date: Sat, 15 Oct 2016 15:39:42 +0300
> From: Eli Zaretskii <eliz@gnu.org>
> CC: samsurfer117@gmail.com
>
> > Date: Sat, 15 Oct 2016 14:33:13 +0300
> > From: Eli Zaretskii <eliz@gnu.org>
> > CC: samsurfer117@gmail.com
> >
> > > Date: Fri, 30 Sep 2016 12:22:13 +0300
> > > From: Eli Zaretskii <eliz@gnu.org>
> > > CC: gdb-patches@sourceware.org
> > >
> > > > How can we fix this bug?
> > >
> > > Someone needs to write and submit the code to (a) parse the command
> > > line, like "run < foo >> bar 2> baz" and glean the redirections from
> > > it; and (b) actually implement the redirection in the CreateProcess
> > > call.
> >
> > Guess what? someone just did.
> >
> > The patches below implement redirection support for native debugging
> > of MinGW programs. (I reused some of the code I wrote years ago for
> > the DJGPP project.)
> >
> > OK to push to master?
>
> Sorry, forgot to attach the ChangeLog entries. Here's the patch again
> with the logs:
>
> 2016-10-15 Eli Zaretskii <eliz@gnu.org>
>
> Support command-line redirection for Windows native debugging
>
> * windows-nat.c (redir_open, redir_set_redirection)
> (redirect_inferior_handles) [!__CYGWIN__]: New functions for
> redirecting standard handles of the inferior.
> (windows_create_inferior) [!__CYGWIN__]: Use them to redirect
> standard handles of the inferior based on redirection symbols on
> the command line.
>
> --- gdb/windows-nat.c~1 2016-10-09 12:37:04.538125000 +0300
> +++ gdb/windows-nat.c 2016-10-15 14:27:51.966125000 +0300
> @@ -2054,6 +2054,166 @@ clear_win32_environment (char **env)
> }
> #endif
>
> +#ifndef __CYGWIN__
> +
> +/* Support routines for redirecting standard handles of the inferior. */
> +
> +static int
> +redir_open (const char *redir_string, HANDLE *inp, HANDLE *out, HANDLE *err)
> +{
> + HANDLE *hdl;
> + int mode, fd;
> + const char *fname = redir_string + 1;;
> +
> + switch (*redir_string)
> + {
> + case '<':
> + hdl = inp;
> + mode = O_RDONLY;
> + break;
> + case '1': case '2':
> + fname++;
> + /* FALLTHROUGH */
> + case '>':
> + hdl = (*redir_string == '2') ? err : out;
> + mode = O_WRONLY | O_CREAT;
> + if (*fname == '>')
> + {
> + fname++;
> + mode |= O_APPEND;
> + }
> + else
> + mode |= O_TRUNC;
> + break;
> + default:
> + return -1;
> + }
> + fname++; /* skip the separator space */
> + fd = _open (fname, mode, _S_IREAD | _S_IWRITE);
> + if (fd < 0)
> + return -1;
> + /* _open just sets a flag for O_APPEND, which won't be passed to the
> + inferior, so we need to actually move the file pointer. */
> + if ((mode & O_APPEND) != 0)
> + _lseek (fd, 0L, SEEK_END);
> + *hdl = (HANDLE)_get_osfhandle (fd);
> + return 0;
> +}
> +
> +static int
> +redir_set_redirection (const char *s, HANDLE *inp, HANDLE *out, HANDLE *err)
> +{
> + char buf[__PMAX + 5]; /* extra space for redirection string */
> + char *d = buf;
> + const char *start = s;
> + int quote = 0;
> +
> + *d++ = *s++; /* copy the 1st character, < or > or a digit */
> + if ((*start == '>' || *start == '1' || *start == '2')
> + && *s == '>')
> + {
> + *d++ = *s++;
> + if (*s == '>' && *start != '>')
> + *d++ = *s++;
> + }
> + while (isspace (*s)) /* skip whitespace before file name */
> + s++;
> + *d++ = ' '; /* separate file name with a single space */
> +
> + /* Copy the file name. */
> + while (*s)
> + {
> + /* Remove quoting characters from the file name in buf[]. */
> + if (*s == '"') /* could support '..' quoting here */
> + {
> + if (!quote)
> + quote = *s++;
> + else if (*s == quote)
> + {
> + quote = 0;
> + s++;
> + }
> + else
> + *d++ = *s++;
> + }
> + else if (*s == '\\')
> + {
> + if (s[1] == '"') /* could support '..' here */
> + s++;
> + *d++ = *s++;
> + }
> + else if (isspace (*s) && !quote)
> + break;
> + else
> + *d++ = *s++;
> + if (d - buf >= sizeof (buf) - 1)
> + {
> + errno = ENAMETOOLONG;
> + return 0;
> + }
> + }
> + *d = '\0';
> +
> + /* Windows doesn't allow redirection characters in file names, so we
> + can bail out early if they use them, or if there's no target file
> + name after the redirection symbol. */
> + if (d[-1] == '>' || d[-1] == '<')
> + {
> + errno = ENOENT;
> + return 0;
> + }
> + if (redir_open (buf, inp, out, err) == 0)
> + return s - start;
> + return 0;
> +}
> +
> +static bool
> +redirect_inferior_handles (const char *cmd_orig, char *cmd,
> + HANDLE *inp, HANDLE *out, HANDLE *err)
> +{
> + const char *s = cmd_orig;
> + char *d = cmd;
> + int quote = 0;
> + bool retval = false;
> +
> + while (isspace (*s))
> + *d++ = *s++;
> +
> + while (*s)
> + {
> + if (*s == '"') /* could also support '..' quoting here */
> + {
> + if (!quote)
> + quote = *s;
> + else if (*s == quote)
> + quote = 0;
> + }
> + else if (*s == '\\')
> + {
> + if (s[1] == '"') /* escaped quote char */
> + s++;
> + }
> + else if (!quote)
> + {
> + if (*s == '<' || *s == '>'
> + || ((*s == '1' || *s == '2') && s[1] == '>'))
> + {
> + int skip = redir_set_redirection (s, inp, out, err);
> +
> + if (skip <= 0)
> + return false;
> + retval = true;
> + s += skip;
> + }
> + }
> + if (*s)
> + *d++ = *s++;
> + }
> + *d = '\0';
> + return retval;
> +}
> +#endif /* !__CYGWIN__ */
> +
> /* Start an inferior windows child process and sets inferior_ptid to its pid.
> EXEC_FILE is the file to run.
> ALLARGS is a string containing the arguments to the program.
> @@ -2076,20 +2236,24 @@ windows_create_inferior (struct target_o
> size_t len;
> int tty;
> int ostdin, ostdout, ostderr;
> -#else
> +#else /* !__CYGWIN__ */
> char real_path[__PMAX];
> char shell[__PMAX]; /* Path to shell */
> char *toexec;
> - char *args;
> - size_t args_len;
> - HANDLE tty;
> + char *args, *allargs_copy;
> + size_t args_len, allargs_len;
> + HANDLE tty = INVALID_HANDLE_VALUE;
> + HANDLE inf_stdin = INVALID_HANDLE_VALUE;
> + HANDLE inf_stdout = INVALID_HANDLE_VALUE;
> + HANDLE inf_stderr = INVALID_HANDLE_VALUE;
> + bool redirected = false;
> char *w32env;
> char *temp;
> size_t envlen;
> int i;
> size_t envsize;
> char **env;
> -#endif
> +#endif /* !__CYGWIN__ */
> PROCESS_INFORMATION pi;
> BOOL ret;
> DWORD flags = 0;
> @@ -2121,7 +2285,7 @@ windows_create_inferior (struct target_o
> error (_("Error starting executable: %d"), errno);
> cygallargs = (wchar_t *) alloca (len * sizeof (wchar_t));
> mbstowcs (cygallargs, allargs, len);
> -#else
> +#else /* !__USEWIDE */
> cygallargs = allargs;
> #endif
> }
> @@ -2137,12 +2301,12 @@ windows_create_inferior (struct target_o
> + mbstowcs (NULL, allargs, 0) + 2;
> cygallargs = (wchar_t *) alloca (len * sizeof (wchar_t));
> swprintf (cygallargs, len, L" -c 'exec %s %s'", exec_file, allargs);
> -#else
> +#else /* !__USEWIDE */
> len = (sizeof (" -c 'exec '") + strlen (exec_file)
> + strlen (allargs) + 2);
> cygallargs = (char *) alloca (len);
> xsnprintf (cygallargs, len, " -c 'exec %s %s'", exec_file, allargs);
> -#endif
> +#endif /* __USEWIDE */
> toexec = shell;
> flags |= DEBUG_PROCESS;
> }
> @@ -2153,12 +2317,12 @@ windows_create_inferior (struct target_o
> wcscpy (args, toexec);
> wcscat (args, L" ");
> wcscat (args, cygallargs);
> -#else
> +#else /* !__USEWIDE */
> args = (cygwin_buf_t *) alloca (strlen (toexec) + strlen (cygallargs) + 2);
> strcpy (args, toexec);
> strcat (args, " ");
> strcat (args, cygallargs);
> -#endif
> +#endif /* !__USEWIDE */
>
> #ifdef CW_CVT_ENV_TO_WINENV
> /* First try to create a direct Win32 copy of the POSIX environment. */
> @@ -2167,7 +2331,7 @@ windows_create_inferior (struct target_o
> flags |= CREATE_UNICODE_ENVIRONMENT;
> else
> /* If that fails, fall back to old method tweaking GDB's environment. */
> -#endif
> +#endif /* CW_CVT_ENV_TO_WINENV */
> {
> /* Reset all Win32 environment variables to avoid leftover on next run. */
> clear_win32_environment (environ);
> @@ -2232,21 +2396,26 @@ windows_create_inferior (struct target_o
> close (ostdout);
> close (ostderr);
> }
> -#else
> - toexec = exec_file;
> - /* Build the command line, a space-separated list of tokens where
> - the first token is the name of the module to be executed.
> - To avoid ambiguities introduced by spaces in the module name,
> - we quote it. */
> - args_len = strlen (toexec) + 2 /* quotes */ + strlen (allargs) + 2;
> - args = (char *) alloca (args_len);
> - xsnprintf (args, args_len, "\"%s\" %s", toexec, allargs);
> -
> - flags |= DEBUG_ONLY_THIS_PROCESS;
> -
> - if (!inferior_io_terminal)
> - tty = INVALID_HANDLE_VALUE;
> - else
> +#else /* !__CYGWIN__ */
> + allargs_len = strlen (allargs);
> + allargs_copy = strcpy ((char *)alloca (allargs_len + 1), allargs);
> + if (strpbrk (allargs_copy, "<>"))
> + {
> + int e = errno;
> + errno = 0;
> + redirected =
> + redirect_inferior_handles (allargs, allargs_copy,
> + &inf_stdin, &inf_stdout, &inf_stderr);
> + if (errno)
> + warning (_("Error in redirection: %s."), strerror (errno));
> + else
> + errno = e;
> + allargs_len = strlen (allargs_copy);
> + }
> + if (inferior_io_terminal
> + && !(inf_stdin != INVALID_HANDLE_VALUE
> + && inf_stdout != INVALID_HANDLE_VALUE
> + && inf_stderr != INVALID_HANDLE_VALUE))
> {
> SECURITY_ATTRIBUTES sa;
> sa.nLength = sizeof(sa);
> @@ -2257,15 +2426,32 @@ windows_create_inferior (struct target_o
> if (tty == INVALID_HANDLE_VALUE)
> warning (_("Warning: Failed to open TTY %s, error %#x."),
> inferior_io_terminal, (unsigned) GetLastError ());
> - else
> - {
> - si.hStdInput = tty;
> - si.hStdOutput = tty;
> - si.hStdError = tty;
> - si.dwFlags |= STARTF_USESTDHANDLES;
> - }
> + }
> + if (redirected || tty != INVALID_HANDLE_VALUE)
> + {
> + si.hStdInput = inf_stdin == INVALID_HANDLE_VALUE ? tty : inf_stdin;
> + if (si.hStdInput == INVALID_HANDLE_VALUE)
> + si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
> + si.hStdOutput = inf_stdout == INVALID_HANDLE_VALUE ? tty : inf_stdout;
> + if (si.hStdOutput == INVALID_HANDLE_VALUE)
> + si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
> + si.hStdError = inf_stderr == INVALID_HANDLE_VALUE ? tty : inf_stderr;
> + if (si.hStdError == INVALID_HANDLE_VALUE)
> + si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
> + si.dwFlags |= STARTF_USESTDHANDLES;
> }
>
> + toexec = exec_file;
> + /* Build the command line, a space-separated list of tokens where
> + the first token is the name of the module to be executed.
> + To avoid ambiguities introduced by spaces in the module name,
> + we quote it. */
> + args_len = strlen (toexec) + 2 /* quotes */ + allargs_len + 2;
> + args = (char *) alloca (args_len);
> + xsnprintf (args, args_len, "\"%s\" %s", toexec, allargs_copy);
> +
> + flags |= DEBUG_ONLY_THIS_PROCESS;
> +
> /* CreateProcess takes the environment list as a null terminated set of
> strings (i.e. two nulls terminate the list). */
>
> @@ -2304,7 +2490,13 @@ windows_create_inferior (struct target_o
> &pi);
> if (tty != INVALID_HANDLE_VALUE)
> CloseHandle (tty);
> -#endif
> + if (inf_stdin != INVALID_HANDLE_VALUE)
> + CloseHandle (inf_stdin);
> + if (inf_stdout != INVALID_HANDLE_VALUE)
> + CloseHandle (inf_stdout);
> + if (inf_stderr != INVALID_HANDLE_VALUE)
> + CloseHandle (inf_stderr);
> +#endif /* !__CYGWIN__ */
>
> if (!ret)
> error (_("Error creating process %s, (error %u)."),
>