Exemplo n.º 1
0
  std::string
  Client::diff(const Path & tmpPath, const Path & path,
               const Revision & revision1, const Revision & revision2,
               const bool recurse, const bool ignoreAncestry,
               const bool noDiffDeleted) throw(ClientException)
  {
    Pool pool;
    svn_error_t * error;
    apr_status_t status;
    apr_file_t * outfile = nullptr;
    const char * outfileName = nullptr;
    apr_file_t * errfile = nullptr;
    const char * errfileName = nullptr;
    apr_array_header_t * options;
    svn_stringbuf_t * stringbuf;

    // svn_client_diff needs an options array, even if it is empty
    options = apr_array_make(pool, 0, 0);

    // svn_client_diff needs a temporary file to write diff output to
    error = svn_io_open_unique_file(&outfile, &outfileName,
                                    tmpPath.c_str(), ".tmp",
                                    false, pool);

    if (error != nullptr)
    {
      diffCleanup(outfile, outfileName, errfile, errfileName, pool);
      throw ClientException(error);
    }

    // and another one to write errors to
    error = svn_io_open_unique_file(&errfile, &errfileName,
                                    tmpPath.c_str(), ".tmp",
                                    false, pool);

    if (error != nullptr)
    {
      diffCleanup(outfile, outfileName, errfile, errfileName, pool);
      throw ClientException(error);
    }

    // run diff
    error = svn_client_diff(options,
                            path.c_str(), revision1.revision(),
                            path.c_str(), revision2.revision(),
                            recurse, ignoreAncestry, noDiffDeleted,
                            outfile, errfile,
                            *m_context,
                            pool);

    if (error != nullptr)
    {
      diffCleanup(outfile, outfileName, errfile, errfileName, pool);
      throw ClientException(error);
    }

    // then we reopen outfile for reading
    status = apr_file_close(outfile);
    if (status)
    {
      diffCleanup(outfile, outfileName, errfile, errfileName, pool);
      fail(pool, status, "failed to close '%s'", outfileName);
    }

    status = apr_file_open(&outfile, outfileName, APR_READ, APR_OS_DEFAULT, pool);
    if (status)
    {
      diffCleanup(outfile, outfileName, errfile, errfileName, pool);
      fail(pool, status, "failed to open '%s'", outfileName);
    }

    // now we can read the diff output from outfile and return that
    error = svn_stringbuf_from_aprfile(&stringbuf, outfile, pool);

    if (error != nullptr)
    {
      diffCleanup(outfile, outfileName, errfile, errfileName, pool);
      throw ClientException(error);
    }

    diffCleanup(outfile, outfileName, errfile, errfileName, pool);
    return stringbuf->data;
  }
Exemplo n.º 2
0
/* Helper function for run_hook_cmd().  Wait for a hook to finish
   executing and return either SVN_NO_ERROR if the hook script completed
   without error, or an error describing the reason for failure.

   NAME and CMD are the name and path of the hook program, CMD_PROC
   is a pointer to the structure representing the running process,
   and READ_ERRHANDLE is an open handle to the hook's stderr.

   Hooks are considered to have failed if we are unable to wait for the
   process, if we are unable to read from the hook's stderr, if the
   process has failed to exit cleanly (due to a coredump, for example),
   or if the process returned a non-zero return code.

   Any error output returned by the hook's stderr will be included in an
   error message, though the presence of output on stderr is not itself
   a reason to fail a hook. */
static svn_error_t *
check_hook_result(const char *name, const char *cmd, apr_proc_t *cmd_proc,
                  apr_file_t *read_errhandle, apr_pool_t *pool)
{
  svn_error_t *err, *err2;
  svn_stringbuf_t *native_stderr, *failure_message;
  const char *utf8_stderr;
  int exitcode;
  apr_exit_why_e exitwhy;

  err2 = svn_stringbuf_from_aprfile(&native_stderr, read_errhandle, pool);

  err = svn_io_wait_for_cmd(cmd_proc, cmd, &exitcode, &exitwhy, pool);
  if (err)
    {
      svn_error_clear(err2);
      return svn_error_trace(err);
    }

  if (APR_PROC_CHECK_EXIT(exitwhy) && exitcode == 0)
    {
      /* The hook exited cleanly.  However, if we got an error reading
         the hook's stderr, fail the hook anyway, because this might be
         symptomatic of a more important problem. */
      if (err2)
        {
          return svn_error_createf
            (SVN_ERR_REPOS_HOOK_FAILURE, err2,
             _("'%s' hook succeeded, but error output could not be read"),
             name);
        }

      return SVN_NO_ERROR;
    }

  /* The hook script failed. */

  /* If we got the stderr output okay, try to translate it into UTF-8.
     Ensure there is something sensible in the UTF-8 string regardless. */
  if (!err2)
    {
      err2 = svn_utf_cstring_to_utf8(&utf8_stderr, native_stderr->data, pool);
      if (err2)
        utf8_stderr = _("[Error output could not be translated from the "
                        "native locale to UTF-8.]");
    }
  else
    {
      utf8_stderr = _("[Error output could not be read.]");
    }
  /*### It would be nice to include the text of any translation or read
        error in the messages above before we clear it here. */
  svn_error_clear(err2);

  if (!APR_PROC_CHECK_EXIT(exitwhy))
    {
      failure_message = svn_stringbuf_createf(pool,
        _("'%s' hook failed (did not exit cleanly: "
          "apr_exit_why_e was %d, exitcode was %d).  "),
        name, exitwhy, exitcode);
    }
  else
    {
      const char *action;
      if (strcmp(name, "start-commit") == 0
          || strcmp(name, "pre-commit") == 0)
        action = _("Commit");
      else if (strcmp(name, "pre-revprop-change") == 0)
        action = _("Revprop change");
      else if (strcmp(name, "pre-lock") == 0)
        action = _("Lock");
      else if (strcmp(name, "pre-unlock") == 0)
        action = _("Unlock");
      else
        action = NULL;
      if (action == NULL)
        failure_message = svn_stringbuf_createf(
            pool, _("%s hook failed (exit code %d)"),
            name, exitcode);
      else
        failure_message = svn_stringbuf_createf(
            pool, _("%s blocked by %s hook (exit code %d)"),
            action, name, exitcode);
    }

  if (utf8_stderr[0])
    {
      svn_stringbuf_appendcstr(failure_message,
                               _(" with output:\n"));
      svn_stringbuf_appendcstr(failure_message, utf8_stderr);
    }
  else
    {
      svn_stringbuf_appendcstr(failure_message,
                               _(" with no output."));
    }

  return svn_error_create(SVN_ERR_REPOS_HOOK_FAILURE, err,
                          failure_message->data);
}
Exemplo n.º 3
0
/* NAME, CMD and ARGS are the name, path to and arguments for the hook
   program that is to be run.  The hook's exit status will be checked,
   and if an error occurred the hook's stderr output will be added to
   the returned error.

   If STDIN_HANDLE is non-null, pass it as the hook's stdin, else pass
   no stdin to the hook.

   If RESULT is non-null, set *RESULT to the stdout of the hook or to
   a zero-length string if the hook generates no output on stdout. */
static svn_error_t *
run_hook_cmd(svn_string_t **result,
             const char *name,
             const char *cmd,
             const char **args,
             const char *const *hooks_env,
             apr_file_t *stdin_handle,
             apr_pool_t *pool)
{
  apr_file_t *null_handle;
  apr_status_t apr_err;
  svn_error_t *err;
  apr_proc_t cmd_proc;

  if (result)
    {
      null_handle = NULL;
    }
  else
    {
      /* Redirect stdout to the null device */
        apr_err = apr_file_open(&null_handle, SVN_NULL_DEVICE_NAME, APR_WRITE,
                                APR_OS_DEFAULT, pool);
        if (apr_err)
          return svn_error_wrap_apr
            (apr_err, _("Can't create null stdout for hook '%s'"), cmd);
    }

  err = svn_io_start_cmd3(&cmd_proc, ".", cmd, args, hooks_env, FALSE,
                          FALSE, stdin_handle, result != NULL, null_handle,
                          TRUE, NULL, pool);

  if (err)
    {
      /* CMD_PROC is not safe to use. Bail. */
      return svn_error_createf
        (SVN_ERR_REPOS_HOOK_FAILURE, err, _("Failed to start '%s' hook"), cmd);
    }
  else
    {
      err = check_hook_result(name, cmd, &cmd_proc, cmd_proc.err, pool);
    }

  /* 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 */
  apr_err = apr_file_close(cmd_proc.err);
  if (!err && apr_err)
    return svn_error_wrap_apr
      (apr_err, _("Error closing read end of stderr pipe"));

  if (result)
    {
      svn_stringbuf_t *native_stdout;
      SVN_ERR(svn_stringbuf_from_aprfile(&native_stdout, cmd_proc.out, pool));
      apr_err = apr_file_close(cmd_proc.out);
      if (!err && apr_err)
        return svn_error_wrap_apr
          (apr_err, _("Error closing read end of stderr pipe"));

      *result = svn_stringbuf__morph_into_string(native_stdout);
    }
  else
    {
      apr_err = apr_file_close(null_handle);
      if (!err && apr_err)
        return svn_error_wrap_apr(apr_err, _("Error closing null file"));
    }

  return svn_error_trace(err);
}
Exemplo n.º 4
0
/* NAME, CMD and ARGS are the name, path to and arguments for the hook
   program that is to be run.  The hook's exit status will be checked,
   and if an error occurred the hook's stderr output will be added to
   the returned error.

   If STDIN_HANDLE is non-null, pass it as the hook's stdin, else pass
   no stdin to the hook.

   If RESULT is non-null, set *RESULT to the stdout of the hook or to
   a zero-length string if the hook generates no output on stdout. */
static svn_error_t *
run_hook_cmd(svn_string_t **result,
             const char *name,
             const char *cmd,
             const char **args,
             apr_hash_t *hooks_env,
             apr_file_t *stdin_handle,
             apr_pool_t *pool)
{
  apr_file_t *null_handle;
  apr_status_t apr_err;
  svn_error_t *err;
  apr_proc_t cmd_proc = {0};
  apr_pool_t *cmd_pool;
  apr_hash_t *hook_env = NULL;

  if (result)
    {
      null_handle = NULL;
    }
  else
    {
      /* Redirect stdout to the null device */
        apr_err = apr_file_open(&null_handle, SVN_NULL_DEVICE_NAME, APR_WRITE,
                                APR_OS_DEFAULT, pool);
        if (apr_err)
          return svn_error_wrap_apr
            (apr_err, _("Can't create null stdout for hook '%s'"), cmd);
    }

  /* Tie resources allocated for the command to a special pool which we can
   * destroy in order to clean up the stderr pipe opened for the process. */
  cmd_pool = svn_pool_create(pool);

  /* Check if a custom environment is defined for this hook, or else
   * whether a default environment is defined. */
  if (hooks_env)
    {
      hook_env = svn_hash_gets(hooks_env, name);
      if (hook_env == NULL)
        hook_env = svn_hash_gets(hooks_env,
                                 SVN_REPOS__HOOKS_ENV_DEFAULT_SECTION);
    }

  err = svn_io_start_cmd3(&cmd_proc, ".", cmd, args,
                          env_from_env_hash(hook_env, pool, pool),
                          FALSE, FALSE, stdin_handle, result != NULL,
                          null_handle, TRUE, NULL, cmd_pool);
  if (!err)
    err = check_hook_result(name, cmd, &cmd_proc, cmd_proc.err, pool);
  else
    {
      /* The command could not be started for some reason. */
      err = svn_error_createf(SVN_ERR_REPOS_BAD_ARGS, err,
                              _("Failed to start '%s' hook"), cmd);
    }

  /* 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 */
  if (!err && result)
    {
      svn_stringbuf_t *native_stdout;
      err = svn_stringbuf_from_aprfile(&native_stdout, cmd_proc.out, pool);
      if (!err)
        *result = svn_stringbuf__morph_into_string(native_stdout);
    }

  /* Close resources allocated by svn_io_start_cmd3(), such as the pipe. */
  svn_pool_destroy(cmd_pool);

  /* Close the null handle. */
  if (null_handle)
    {
      apr_err = apr_file_close(null_handle);
      if (!err && apr_err)
        return svn_error_wrap_apr(apr_err, _("Error closing null file"));
    }

  return svn_error_trace(err);
}
Exemplo n.º 5
0
Py::Object pysvn_client::cmd_diff_peg( const Py::Tuple &a_args, const Py::Dict &a_kws )
{
    static argument_description args_desc[] =
    {
    { true,  name_tmp_path },
    { true,  name_url_or_path },
    { false, name_peg_revision },
    { false, name_revision_start },
    { false, name_revision_end },
    { false, name_recurse },
    { false, name_ignore_ancestry },
    { false, name_diff_deleted },
#if defined( PYSVN_HAS_CLIENT_DIFF_PEG2 )
    { false, name_ignore_content_type },
#endif
#if defined( PYSVN_HAS_CLIENT_DIFF_PEG3 )
    { false, name_header_encoding },
    { false, name_diff_options },
#endif
#if defined( PYSVN_HAS_CLIENT_DIFF_PEG4 )
    { false, name_depth },
    { false, name_relative_to_dir },
    { false, name_changelists },
#endif
    { false, NULL }
    };
    FunctionArguments args( "diff_peg", args_desc, a_args, a_kws );
    args.check();

    std::string tmp_path( args.getUtf8String( name_tmp_path ) );
    std::string path( args.getUtf8String( name_url_or_path ) );
    svn_opt_revision_t revision_start = args.getRevision( name_revision_start, svn_opt_revision_base );
    svn_opt_revision_t revision_end = args.getRevision( name_revision_end, svn_opt_revision_working );
    svn_opt_revision_t peg_revision = args.getRevision( name_peg_revision, revision_end );

    SvnPool pool( m_context );

#if defined( PYSVN_HAS_CLIENT_DIFF_PEG4 )
    svn_depth_t depth = args.getDepth( name_depth, name_recurse, svn_depth_infinity, svn_depth_infinity, svn_depth_files );
    std::string std_relative_to_dir;
    const char *relative_to_dir = NULL;
    if( args.hasArg( name_relative_to_dir ) )
    {
        std_relative_to_dir = args.getBytes( name_relative_to_dir );
        relative_to_dir = std_relative_to_dir.c_str();
    }

    apr_array_header_t *changelists = NULL;

    if( args.hasArg( name_changelists ) )
    {
        changelists = arrayOfStringsFromListOfStrings( args.getArg( name_changelists ), pool );
    }
#else
    bool recurse = args.getBoolean( name_recurse, true );
#endif
    bool ignore_ancestry = args.getBoolean( name_ignore_ancestry, true );
    bool diff_deleted = args.getBoolean( name_diff_deleted, true );
#if defined( PYSVN_HAS_CLIENT_DIFF_PEG2 )
    bool ignore_content_type = args.getBoolean( name_ignore_content_type, false );
#endif

#if defined( PYSVN_HAS_CLIENT_DIFF_PEG3 )
    std::string header_encoding( args.getUtf8String( name_header_encoding, empty_string ) );
    const char *header_encoding_ptr = APR_LOCALE_CHARSET;
    if( !header_encoding.empty() )
        header_encoding_ptr = header_encoding.c_str();

    apr_array_header_t *options = NULL;
    if( args.hasArg( name_diff_options ) )
    {
        options = arrayOfStringsFromListOfStrings( args.getArg( name_diff_options ), pool );
    }
    else
    {
        options = apr_array_make( pool, 0, sizeof( const char * ) );
    }
#else
    apr_array_header_t *options = apr_array_make( pool, 0, sizeof( const char * ) );
#endif

    bool is_url = is_svn_url( path );
    revisionKindCompatibleCheck( is_url, peg_revision, name_peg_revision, name_url_or_path );
    revisionKindCompatibleCheck( is_url, revision_start, name_revision_start, name_url_or_path );
    revisionKindCompatibleCheck( is_url, revision_end, name_revision_end, name_url_or_path );

    svn_stringbuf_t *stringbuf = NULL;

    try
    {
        std::string norm_tmp_path( svnNormalisedIfPath( tmp_path, pool ) );
        std::string norm_path( svnNormalisedIfPath( path, pool ) );

        checkThreadPermission();

        PythonAllowThreads permission( m_context );
        pysvn_apr_file output_file( pool );
        pysvn_apr_file error_file( pool );

        output_file.open_unique_file( norm_tmp_path );
        error_file.open_unique_file( norm_tmp_path );

        // std::cout << "peg_revision "    << peg_revision.kind    << " " << peg_revision.value.number     << std::endl;
        // std::cout << "revision_start "  << revision_start.kind  << " " << revision_start.value.number   << std::endl;
        // std::cout << "revision_end "    << revision_end.kind    << " " << revision_end.value.number     << std::endl;

#if defined( PYSVN_HAS_CLIENT_DIFF_PEG4 )
        svn_error_t *error = svn_client_diff_peg4
            (
            options,
            norm_path.c_str(),
            &peg_revision,
            &revision_start,
            &revision_end,
            relative_to_dir,
            depth,
            ignore_ancestry,
            !diff_deleted,
            ignore_content_type,
            header_encoding_ptr,
            output_file.file(),
            error_file.file(),
            changelists,
            m_context,
            pool
            );
#elif defined( PYSVN_HAS_CLIENT_DIFF_PEG3 )
        svn_error_t *error = svn_client_diff_peg3
            (
            options,
            norm_path.c_str(),
            &peg_revision,
            &revision_start,
            &revision_end,
            recurse,
            ignore_ancestry,
            !diff_deleted,
            ignore_content_type,
            header_encoding_ptr,
            output_file.file(),
            error_file.file(),
            m_context,
            pool
            );
#elif defined( PYSVN_HAS_CLIENT_DIFF_PEG2 )
        svn_error_t *error = svn_client_diff_peg2
            (
            options,
            norm_path.c_str(),
            &peg_revision,
            &revision_start,
            &revision_end,
            recurse,
            ignore_ancestry,
            !diff_deleted,
            ignore_content_type,
            output_file.file(),
            error_file.file(),
            m_context,
            pool
            );
#else
        svn_error_t *error = svn_client_diff_peg
            (
            options,
            norm_path.c_str(),
            &peg_revision,
            &revision_start,
            &revision_end,
            recurse,
            ignore_ancestry,
            !diff_deleted,
            output_file.file(),
            error_file.file(),
            m_context,
            pool
            );
#endif
        permission.allowThisThread();
        if( error != NULL )
            throw SvnException( error );

        output_file.close();

        output_file.open_tmp_file();
        error = svn_stringbuf_from_aprfile( &stringbuf, output_file.file(), pool );
        if( error != NULL )
            throw SvnException( error );
    }
    catch( SvnException &e )
    {
        // use callback error over ClientException
        m_context.checkForError( m_module.client_error );

        throw_client_error( e );
    }

    // cannot convert to Unicode as we have no idea of the encoding of the bytes
    return Py::String( stringbuf->data, (int)stringbuf->len );
}
Exemplo n.º 6
0
static svn_error_t *
test12(const char **msg,
       svn_boolean_t msg_only,
       svn_test_opts_t *opts,
       apr_pool_t *pool)
{
  svn_stringbuf_t *s;
  const char fname[] = "string-test.tmp";
  apr_file_t *file;
  apr_status_t status;
  apr_size_t len;
  int i, repeat;
  const char ftext[] =
    "Just some boring text. Avoiding newlines 'cos I don't know"
    "if any of the Subversion platfoms will mangle them! There's no"
    "need to test newline handling here anyway, it's not relevant.";

  *msg = "create string from file";

  if (msg_only)
    return SVN_NO_ERROR;

  status = apr_file_open(&file, fname, APR_WRITE | APR_TRUNCATE | APR_CREATE,
                         APR_OS_DEFAULT, pool);
  if (status)
    return fail(pool, "opening file");

  repeat = 100;

  /* Some text */
  for (i = 0; i < repeat; ++i)
    {
      status = apr_file_write_full(file, ftext, sizeof(ftext) - 1, &len);
      if (status)
        return fail(pool, "writing file");
    }

  /* A null byte, I don't *think* any of our platforms mangle these */
  status = apr_file_write_full(file, "\0", 1, &len);
  if (status)
    return fail(pool, "writing file");

  /* Some more text */
  for (i = 0; i < repeat; ++i)
    {
      status = apr_file_write_full(file, ftext, sizeof(ftext) - 1, &len);
      if (status)
        return fail(pool, "writing file");
    }

  status = apr_file_close(file);
  if (status)
    return fail(pool, "closing file");

  SVN_ERR(svn_stringbuf_from_file(&s, fname, pool));
  SVN_ERR(check_string_contents(s, ftext, sizeof(ftext) - 1, repeat, pool));

  /* Reset to avoid false positives */
  s = NULL;

  status = apr_file_open(&file, fname, APR_READ, APR_OS_DEFAULT, pool);
  if (status)
    return fail(pool, "opening file");

  SVN_ERR(svn_stringbuf_from_aprfile(&s, file, pool));
  SVN_ERR(check_string_contents(s, ftext, sizeof(ftext) - 1, repeat, pool));

  status = apr_file_close(file);
  if (status)
    return fail(pool, "closing file");

  status = apr_file_remove(fname, pool);
  if (status)
    return fail(pool, "removing file");

  return SVN_NO_ERROR;
}