Index: subversion/include/svn_io.h
===================================================================
--- subversion/include/svn_io.h (revision 18637)
+++ subversion/include/svn_io.h (working copy)
@@ -860,6 +860,41 @@
apr_file_t *errfile,
apr_pool_t *pool);
+#ifdef AS400
+/** Alternative to svn_io_start_cmd() and svn_io_wait_for_cmd() for running
+ * unix-type qsh(ell) scripts on OS400. Overcomes limitation of
+ * apr_proc_wait() on OS400 where child process always appears to exit
+ * successfully.
+ *
+ * Invoke @a cmd with @a args. @a args is a list of utf8-encoded
+ * (const char *)'s, terminated by @c NULL. @c ARGS[0] is the full
+ * path to the program, though it need not be the same as @a cmd.
+ *
+ * If @a exitcode is not @c NULL, @a *exitcode will contain the exit code
+ * of the process upon return, and if @a exitwhy is not @c NULL, @a
+ * *exitwhy will indicate why the process terminated. If @a exitwhy is
+ * @c NULL, and the exit reason is not @c APR_PROC_CHECK_EXIT(), or if
+ * @a exitcode is @c NULL and the exit code is non-zero, then an
+ * @c SVN_ERR_EXTERNAL_PROGRAM error will be returned.
+ *
+ * If @a read_stdout is @c TRUE, any stdout from the command is appended
+ * to @a *err_stream.
+ *
+ * If @a read_stderr is @c TRUE, any stderr from the command is appended
+ * to @a *err_stream.
+ *
+ * All allocations are done in @a pool.
+ */
+svn_error_t *svn_io_run_os400_cmd(const char *cmd,
+ const char *const *args,
+ int *exitcode,
+ apr_exit_why_e *exitwhy,
+ svn_boolean_t read_stdout,
+ svn_boolean_t read_stderr,
+ svn_stringbuf_t **err_stream,
+ apr_pool_t *pool);
+#endif /* AS400 */
+
/** Invoke @c the configured diff program, with @a user_args (an array
* of utf8-encoded @a num_user_args arguments), if they are specified,
* or "-u" if they are not.
Index: subversion/libsvn_repos/hooks.c
===================================================================
--- subversion/libsvn_repos/hooks.c (revision 18637)
+++ subversion/libsvn_repos/hooks.c (working copy)
@@ -60,6 +60,7 @@
apr_exit_why_e exitwhy;
apr_proc_t cmd_proc;
+#ifndef AS400
/* Create a pipe to access stderr of the child. */
apr_err = apr_file_pipe_create(&read_errhandle, &write_errhandle, pool);
if (apr_err)
@@ -95,7 +96,13 @@
err = svn_io_start_cmd(&cmd_proc, ".", cmd, args, FALSE,
stdin_handle, null_handle, write_errhandle, pool);
+#else
+ /* The functionality of svn_io_start_cmd() and svn_io_wait_for_cmd() is
+ * handled in svn_io_run_as400_cmd() on OS400. */
+ err = NULL;
+#endif /* AS400 */
+#ifndef AS400
/* This seems to be done automatically if we pass the third parameter of
apr_procattr_child_in/out_set(), but svn_io_run_cmd()'s interface does
not support those parameters. We need to close the write end of the
@@ -104,6 +111,7 @@
if (!err && apr_err)
return svn_error_wrap_apr
(apr_err, _("Error closing write end of stderr pipe"));
+#endif /* AS400 */
if (err)
{
@@ -116,9 +124,17 @@
const char *error;
svn_error_t *err2;
+#ifndef AS400
err2 = svn_stringbuf_from_aprfile(&native_error, read_errhandle, pool);
err = svn_io_wait_for_cmd(&cmd_proc, cmd, &exitcode, &exitwhy, pool);
+#else
+ err2 = SVN_NO_ERROR;
+
+ err = svn_io_run_os400_cmd(cmd, args, &exitcode, &exitwhy, FALSE,
+ TRUE, &native_error, pool);
+#endif /* AS400 */
+
if (! err)
{
if (! APR_PROC_CHECK_EXIT(exitwhy) || exitcode != 0)
@@ -150,6 +166,7 @@
}
}
+#ifndef AS400
/* Hooks are fallible, and so hook failure is "expected" to occur at
times. When such a failure happens we still want to close the pipe
and null file */
@@ -161,6 +178,7 @@
apr_err = apr_file_close(null_handle);
if (!err && apr_err)
return svn_error_wrap_apr(apr_err, _("Error closing null file"));
+#endif
return err;
}
Index: subversion/libsvn_subr/io.c
===================================================================
--- subversion/libsvn_subr/io.c (revision 18637)
+++ subversion/libsvn_subr/io.c (working copy)
@@ -42,6 +42,10 @@
#include
#include
+#if AS400
+#include /* For spawn() and struct inheritance. */
+#endif
+
#include "svn_types.h"
#include "svn_path.h"
#include "svn_string.h"
@@ -54,6 +58,7 @@
#ifdef AS400
#define SVN_UTF_UTOE_XLATE_HANDLE "svn-utf-utoe-xlate-handle"
+#define SVN_UTF_ETOU_XLATE_HANDLE "svn-utf-etou-xlate-handle"
#endif
/*
@@ -2058,7 +2063,164 @@
}
+#if AS400
svn_error_t *
+svn_io_run_os400_cmd(const char *cmd,
+ const char *const *args,
+ int *exitcode,
+ apr_exit_why_e *exitwhy,
+ svn_boolean_t read_stdout,
+ svn_boolean_t read_stderr,
+ svn_stringbuf_t **err_stream,
+ apr_pool_t *pool)
+{
+ int rc, fd_map[3], ignoreFds[2], useFds[2], exitcode_val;
+ char buffer[20];
+ const char **native_args = NULL;
+ struct inheritance xmp_inherit = {0};
+ pid_t child_pid, wait_rv;
+ svn_stringbuf_t *script_output = svn_stringbuf_create("", pool);
+ apr_size_t args_arr_size = 0, i = 0;
+ apr_exit_why_e exitwhy_val;
+#pragma convert(0)
+ /* Even with the UTF support in V5R4 a few functions don't support utf-8
+ * args, including spawn(), which takes this char array. */
+ char *xmp_envp[2] = {"QIBM_USE_DESCRIPTOR_STDIO=Y", NULL};
+#pragma convert(1208)
+
+ *err_stream = svn_stringbuf_create("", pool);
+
+ /* Find number of elements in args array. */
+ while (args[args_arr_size] != NULL)
+ args_arr_size++;
+
+ /* Allocate memory for the native_args string array plus one for
+ * the ending null element. */
+ native_args = apr_palloc(pool, sizeof(char *) * args_arr_size + 1);
+
+ /* Convert utf-8 args to ebcdic for use by spawn(). */
+ while(args[i] != NULL)
+ {
+ SVN_ERR(svn_utf_cstring_from_utf8_ex((const char**)(&(native_args[i])),
+ args[i++], (const char *)0,
+ SVN_UTF_UTOE_XLATE_HANDLE,
+ pool));
+ }
+
+ /* Make the last element in the array a NULL pointer as required
+ * by spawn. */
+ native_args[args_arr_size] = NULL;
+
+ /* Get two data pipes, allowing stdout and stderr to be separate. */
+ if (pipe(ignoreFds) != 0 || pipe(useFds) != 0)
+ {
+ return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
+ "Error piping hook script %s.", cmd);
+ }
+
+ /* Map stdin, stdout, stderr. */
+ fd_map[0] = ignoreFds[1];
+ fd_map[1] = read_stdout ? useFds[1] : ignoreFds[1];
+ fd_map[2] = read_stderr ? useFds[1] : ignoreFds[1];
+
+ /* Spawn the command... */
+ if ((child_pid = spawn(native_args[0], 3, fd_map, &xmp_inherit,
+ native_args, xmp_envp)) == -1)
+ {
+ return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
+ "Error spawning process for hook script %s.",
+ cmd);
+ }
+
+ /* ...and wait for it to finish. */
+ if ((wait_rv = waitpid(child_pid, &exitcode_val, 0)) == -1)
+ {
+ return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
+ "Error waiting for process completion of " \
+ "hook script %s.", cmd);
+ }
+
+ close(ignoreFds[1]);
+ close(useFds[1]);
+
+ /* Create svn_stringbuf containing any messages the script sent to
+ * stderr and/or stdout. */
+ while ((rc = read(useFds[0], buffer, sizeof(buffer))) > 0)
+ {
+ buffer[rc] = '\0';
+ svn_stringbuf_appendcstr(script_output, buffer);
+ }
+
+ if (script_output->len > 1)
+ {
+ const char* script_out_utf8;
+ SVN_ERR(svn_utf_cstring_to_utf8_ex(&script_out_utf8,
+ script_output->data,
+ (const char*)0,
+ SVN_UTF_ETOU_XLATE_HANDLE,
+ pool));
+ svn_stringbuf_appendcstr (*err_stream, script_out_utf8);
+ }
+
+ close(ignoreFds[0]);
+ close(useFds[0]);
+
+ if (exitwhy)
+ {
+ /* waitpid() doesn't explicity give us apr_exit_why_e *exitwhy so we do
+ * our best to determine it's value. */
+ if (WIFEXITED(*exitcode))
+ {
+ exitwhy_val = APR_PROC_EXIT;
+ }
+ else if (WIFSIGNALED(*exitcode))
+ {
+ exitwhy_val = APR_PROC_SIGNAL;
+ }
+ else if (WIFEXCEPTION(*exitcode))
+ {
+ return svn_error_createf
+ (SVN_ERR_EXTERNAL_PROGRAM, NULL,
+ _("Process '%s' failed unexpectedly with OS400 exception %d"),
+ cmd, WEXCEPTNUMBER(*exitcode));
+ }
+ else if (WIFSTOPPED(*exitcode))
+ {
+ return svn_error_createf
+ (SVN_ERR_EXTERNAL_PROGRAM, NULL,
+ _("Process '%s' stopped unexpectedly by signal %d"),
+ cmd, WSTOPSIG(*exitcode));
+ }
+ else
+ {
+ return svn_error_createf
+ (SVN_ERR_EXTERNAL_PROGRAM, NULL,
+ _("Process '%s' failed unexpectedly with %d"),
+ cmd, APR_EGENERAL);
+ }
+ }
+
+ if (exitwhy)
+ *exitwhy = exitwhy_val;
+ else if (! APR_PROC_CHECK_EXIT(exitwhy_val))
+ return svn_error_createf
+ (SVN_ERR_EXTERNAL_PROGRAM, NULL,
+ _("Process '%s' failed (exitwhy %d)"), cmd, exitwhy_val);
+
+ if (exitcode)
+ *exitcode = exitcode_val;
+ else if (exitcode_val != 0)
+ {
+ return svn_error_createf
+ (SVN_ERR_EXTERNAL_PROGRAM, NULL,
+ _("Process '%s' returned error exitcode %d"), cmd, exitcode_val);
+ }
+ return SVN_NO_ERROR;
+}
+#endif
+
+
+svn_error_t *
svn_io_run_diff(const char *dir,
const char *const *user_args,
int num_user_args,