JNIEXPORT void JNICALL
Java_org_apache_subversion_javahl_util_DiffLib_nativeFileDiff(
    JNIEnv* env, jobject jthis,
    jstring joriginal_file,
    jstring jmodified_file,

    jint jignore_space_ordinal,
    jboolean jignore_eol_style,
    jboolean jshow_c_function,

    jstring joriginal_header,
    jstring jmodified_header,
    jstring jheader_encoding,
    jstring jrelative_to_dir,

    jobject jresult_stream)
{
  JNIEntry(DiffLib, nativeFileDiff);

  // Using a "global" request pool since we don't keep a context with
  // its own pool around for these functions.
  SVN::Pool pool;

  Path original(joriginal_file, pool);
  if (JNIUtil::isJavaExceptionThrown())
    return;
  SVN_JNI_ERR(original.error_occurred(),);

  Path modified(jmodified_file, pool);
  if (JNIUtil::isJavaExceptionThrown())
    return;
  SVN_JNI_ERR(modified.error_occurred(),);

  svn_diff_t* diff;
  svn_diff_file_options_t* diff_options =
    svn_diff_file_options_create(pool.getPool());
  diff_options->ignore_space =
    svn_diff_file_ignore_space_t(jignore_space_ordinal);
  diff_options->ignore_eol_style = svn_boolean_t(jignore_eol_style);
  diff_options->show_c_function = svn_boolean_t(jshow_c_function);
  SVN_JNI_ERR(svn_diff_file_diff_2(&diff,
                                   original.c_str(),
                                   modified.c_str(),
                                   diff_options,
                                   pool.getPool()),);

  JNIStringHolder original_header(joriginal_header);
  if (JNIUtil::isJavaExceptionThrown())
    return;

  JNIStringHolder modified_header(jmodified_header);
  if (JNIUtil::isJavaExceptionThrown())
    return;

  JNIStringHolder header_encoding(jheader_encoding);
  if (JNIUtil::isJavaExceptionThrown())
    return;

  JNIStringHolder relative_to_dir(jrelative_to_dir);
  if (JNIUtil::isJavaExceptionThrown())
    return;
  
  OutputStream result_stream(jresult_stream);

  SVN_JNI_ERR(svn_diff_file_output_unified3(
                  result_stream.getStream(pool), diff,
                  original.c_str(), modified.c_str(),
                  original_header.c_str(), modified_header.c_str(),
                  header_encoding.c_str(), relative_to_dir.c_str(),
                  diff_options->show_c_function,
                  pool.getPool()),);
}
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 );
}