You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2013/10/15 10:52:18 UTC

svn commit: r1532250 [36/37] - in /subversion/branches/cache-server: ./ build/ build/ac-macros/ build/generator/ build/generator/swig/ build/generator/templates/ contrib/client-side/emacs/ contrib/hook-scripts/ contrib/server-side/fsfsfixer/ contrib/se...

Modified: subversion/branches/cache-server/tools/client-side/svn-bench/svn-bench.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/client-side/svn-bench/svn-bench.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/client-side/svn-bench/svn-bench.c (original)
+++ subversion/branches/cache-server/tools/client-side/svn-bench/svn-bench.c Tue Oct 15 08:52:06 2013
@@ -66,7 +66,9 @@ typedef enum svn_cl__longopt_t {
   opt_with_revprop,
   opt_with_all_revprops,
   opt_with_no_revprops,
-  opt_trust_server_cert
+  opt_auto_moves,
+  opt_trust_server_cert,
+  opt_changelist
 } svn_cl__longopt_t;
 
 
@@ -147,6 +149,10 @@ const apr_getopt_option_t svn_cl__option
                     N_("set revision property ARG in new revision\n"
                        "                             "
                        "using the name[=value] format")},
+  {"auto-moves",    opt_auto_moves, 0,
+                    N_("attempt to interpret matching unique DEL+ADD\n"
+                       "                             "
+                       "pairs as moves")},
   {"use-merge-history", 'g', 0,
                     N_("use/display additional information from merge\n"
                        "                             "
@@ -255,10 +261,20 @@ const svn_opt_subcommand_desc2_t svn_cl_
      "  behavior, which can be useful for determining branchpoints.\n"),
     {'r', 'q', 'v', 'g', 'c', opt_targets, opt_stop_on_copy,
      'l', opt_with_all_revprops, opt_with_no_revprops, opt_with_revprop,
-     'x',},
+     opt_auto_moves, 'x',},
     {{opt_with_revprop, N_("retrieve revision property ARG")},
      {'c', N_("the change made in revision ARG")}} },
 
+  { "null-info", svn_cl__null_info, {0}, N_
+    ("Display information about a local or remote item.\n"
+     "usage: info [TARGET[@REV]...]\n"
+     "\n"
+     "  Print information about each TARGET (default: '.').\n"
+     "  TARGET may be either a working-copy path or URL.  If specified, REV\n"
+     "  determines in which revision the target is first looked up.\n"),
+    {'r', 'R', opt_depth, opt_targets, opt_changelist}
+  },
+
   { NULL, NULL, {0}, NULL, {0} }
 };
 
@@ -278,7 +294,7 @@ check_lib_versions(void)
     };
   SVN_VERSION_DEFINE(my_version);
 
-  return svn_ver_check_list(&my_version, checklist);
+  return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
 }
 
 
@@ -534,6 +550,9 @@ sub_main(int argc, const char *argv[], a
                                                 TRUE, pool);
         }
         break;
+      case 'R':
+        opt_state.depth = svn_depth_infinity;
+        break;
       case 'N':
         descend = FALSE;
         break;
@@ -618,6 +637,9 @@ sub_main(int argc, const char *argv[], a
       case 'g':
         opt_state.use_merge_history = TRUE;
         break;
+      case opt_auto_moves:
+        opt_state.auto_moves = TRUE;
+        break;
       default:
         /* Hmmm. Perhaps this would be a good place to squirrel away
            opts that commands like svn diff might need. Hmmm indeed. */

Modified: subversion/branches/cache-server/tools/dev/aprerr.txt
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/dev/aprerr.txt?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/dev/aprerr.txt (original)
+++ subversion/branches/cache-server/tools/dev/aprerr.txt Tue Oct 15 08:52:06 2013
@@ -1,3 +1,4 @@
+# This file is used by which-error.py and gen_base.py:write_errno_table()
 APR_SUCCESS = 0
 SOCBASEERR = 10000
 SOCEPERM = 10001

Modified: subversion/branches/cache-server/tools/dev/build-svn-deps-win.pl
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/dev/build-svn-deps-win.pl?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/dev/build-svn-deps-win.pl (original)
+++ subversion/branches/cache-server/tools/dev/build-svn-deps-win.pl Tue Oct 15 08:52:06 2013
@@ -51,7 +51,6 @@
 # Usage/help output from the usual flags/on error input.
 # Make SQLITE_VER friendly since we're using no dots right now.
 # Work out the fixes to the projects' sources and contribute them back.
-# Allow selection of Release/Debug builds.
 # Allow selection of Arch (x86 and x64)
 # ZLib support for OpenSSL (have to patch openssl)
 # Use CMake zlib build instead.
@@ -74,6 +73,8 @@ our $CMAKE = 'cmake';
 our $NMAKE = 'nmake';
 # Use the .com version so we get output, the .exe doesn't produce any output
 our $DEVENV = 'devenv.com';
+our $VCUPGRADE = 'vcupgrade';
+our $PYTHON = 'python';
 
 # Versions of the dependencies we will use
 # Change these if you want but these are known to work with
@@ -82,12 +83,13 @@ our $HTTPD_VER = '2.4.4';
 our $APR_VER = '1.4.6';
 our $APU_VER = '1.5.2'; # apr-util version
 our $API_VER = '1.2.1'; # arp-iconv version
-our $ZLIB_VER = '1.2.7';
+our $ZLIB_VER = '1.2.8';
 our $OPENSSL_VER = '1.0.1e';
 our $PCRE_VER = '8.32';
 our $BDB_VER = '5.3.21';
 our $SQLITE_VER = '3071602';
-our $SERF_VER = '1.2.0';
+our $SERF_VER = '1.2.1';
+our $NEON_VER = '0.29.6';
 
 # Sources for files to download
 our $AWK_URL = 'http://www.cs.princeton.edu/~bwk/btl.mirror/awk95.exe';
@@ -101,6 +103,8 @@ our $PCRE_URL;
 our $BDB_URL;
 our $SQLITE_URL;
 our $SERF_URL;
+our $NEON_URL;
+our $PROJREF_URL = 'https://downloads.redhoundsoftware.com/blog/ProjRef.py';
 
 # Location of the already downloaded file.
 # by default these are undefined and set by the downloader.
@@ -118,6 +122,8 @@ our $PCRE_FILE;
 our $BDB_FILE;
 our $SQLITE_FILE;
 our $SERF_FILE;
+our $NEON_FILE;
+our $PROJREF_FILE;
 
 # Various directories we use
 our $TOPDIR = Cwd::cwd(); # top of our tree
@@ -125,6 +131,12 @@ our $INSTDIR; # where we install to
 our $BLDDIR; # directory where we actually build
 our $SRCDIR; # directory where we store package files
 
+# Some other options
+our $VS_VER;
+our $NEON;
+our $SVN_VER = '1.9.x';
+our $DEBUG = 0;
+
 # Utility function to remove dots from a string
 sub remove_dots {
   my $in = shift;
@@ -143,6 +155,16 @@ sub set_default {
   }
 }
 
+sub set_svn_ver_defaults {
+  my ($svn_major, $svn_minor, $svn_patch) = $SVN_VER =~ /^(\d+)\.(\d+)\.(.+)$/;
+
+  if ($svn_major > 1 or ($svn_major == 1 and $svn_minor >= 8)) {
+    $NEON=0 unless defined($NEON);
+  } else {
+    $NEON=1 unless defined($NEON);
+  }
+}
+
 # Any variables with defaults that reference other values
 # should be set here.  This defers setting of the default until runtime in these cases.
 sub set_defaults {
@@ -156,9 +178,11 @@ sub set_defaults {
   set_default(\$BDB_URL, "http://download.oracle.com/berkeley-db/db-5.3.21.zip");
   set_default(\$SQLITE_URL, "http://www.sqlite.org/2013/sqlite-amalgamation-$SQLITE_VER.zip");
   set_default(\$SERF_URL, "http://serf.googlecode.com/files/serf-$SERF_VER.zip");
+  set_default(\$NEON_URL, "http://www.webdav.org/neon/neon-$NEON_VER.tar.gz");
   set_default(\$INSTDIR, $TOPDIR);
   set_default(\$BLDDIR, "$TOPDIR\\build");
   set_default(\$SRCDIR, "$TOPDIR\\sources");
+  set_svn_ver_defaults();
 }
 
 #################################
@@ -277,6 +301,27 @@ sub modify_file_in_place {
   close(OUT);
 }
 
+sub check_vs_ver {
+  return if defined($VS_VER);
+
+  # using the vcupgrade command here because it has a consistent name and version
+  # numbering across versions including express versions.
+  my $help_output = `"$VCUPGRADE" /?`;
+  my ($major_version) = $help_output =~ /Version (\d+)\./s;
+
+  if (defined($major_version)) {
+    if ($major_version eq '11') {
+      $VS_VER = '2012';
+      return;
+    } elsif ($major_version eq '10') {
+      $VS_VER = '2010';
+      return;
+    }
+  }
+
+  die("Visual Studio Version Not Supported");
+}
+
 ##################
 # TREE STRUCTURE #
 ##################
@@ -306,6 +351,7 @@ sub clean_structure {
   rmtree($INCDIR);
   rmtree($LIBDIR);
   rmtree("$INSTDIR\\serf");
+  rmtree("$INSTDIR\\neon");
   rmtree("$INSTDIR\\sqlite-amalgamation");
 
   # Dirs created indirectly by the install targets
@@ -363,6 +409,10 @@ sub download_dependencies {
   unless(-x "$BINDIR\\awk.exe") { # skip the copy if it exists
     copy_or_die($AWK_FILE, "$BINDIR\\awk.exe");
   }
+  download_file($PROJREF_URL, "$SRCDIR\\ProjRef.py", \$PROJREF_FILE);
+  unless(-x "$BINDIR\\ProjRef.py") { # skip the copy if it exists
+    copy_or_die($PROJREF_FILE, $BINDIR);
+  }
   download_file($BDB_URL, "$SRCDIR\\db.zip", \$BDB_FILE);
   download_file($ZLIB_URL, "$SRCDIR\\zlib.zip", \$ZLIB_FILE);
   download_file($OPENSSL_URL, "$SRCDIR\\openssl.tar.gz", \$OPENSSL_FILE);
@@ -373,6 +423,7 @@ sub download_dependencies {
   download_file($PCRE_URL, "$SRCDIR\\pcre.zip", \$PCRE_FILE);
   download_file($SQLITE_URL, "$SRCDIR\\sqlite-amalgamation.zip", \$SQLITE_FILE);
   download_file($SERF_URL, "$SRCDIR\\serf.zip", \$SERF_FILE);
+  download_file($NEON_URL, "$SRCDIR\\neon.tar.gz", \$NEON_FILE) if $NEON;
 }
 
 ##############
@@ -437,6 +488,8 @@ sub extract_dependencies {
                "$INSTDIR\\sqlite-amalgamation");
   extract_file($SERF_FILE, $INSTDIR,
                "$INSTDIR\\serf-$SERF_VER", "$INSTDIR\\serf");
+  extract_file($NEON_FILE, $INSTDIR,
+               "$INSTDIR\\neon-$NEON_VER", "$INSTDIR\\neon") if $NEON;
 }
 
 #########
@@ -447,7 +500,7 @@ sub build_pcre {
   chdir_or_die("$SRCLIB\\pcre");
   my $pcre_generator = 'NMake Makefiles';
   # Have to use RelWithDebInfo since httpd looks for the pdb files
-  my $pcre_build_type = '-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo';
+  my $pcre_build_type = '-DCMAKE_BUILD_TYPE:STRING=' . ($DEBUG ? 'Debug' : 'RelWithDebInfo');
   my $pcre_shared_libs = '-DBUILD_SHARED_LIBS:BOOL=ON';
   my $pcre_install_prefix = "-DCMAKE_INSTALL_PREFIX:PATH=$INSTDIR";
   my $cmake_cmd = qq("$CMAKE" -G "$pcre_generator" "$pcre_build_type" "$pcre_shared_libs" "$pcre_install_prefix" .); 
@@ -462,7 +515,7 @@ sub build_pcre {
 # build generates, it it doesn't match that then Subversion will fail to build.
 sub build_zlib {
   chdir_or_die("$SRCLIB\\zlib");
-  $ENV{CC_OPTS} = '/MD /02 /Zi';
+  $ENV{CC_OPTS} = $DEBUG ? '/MDd /Gm /ZI /Od /GZ /D_DEBUG' : '/MD /02 /Zi';
   $ENV{COMMON_CC_OPTS} = '/nologo /W3 /DWIN32 /D_WINDOWS';
   
   system_or_die("Failure building zilb", qq("$NMAKE" /nologo -f win32\\Makefile.msc STATICLIB=zlibstat.lib all));
@@ -481,8 +534,8 @@ sub build_openssl {
   # remove the no-asm below and use ms\do_nasm.bat instead.
   
   # TODO: Enable openssl to use zlib.  openssl needs some patching to do
-	# this since it wants to look for zlib as zlib1.dll and as the httpd
-	# build instructions note you probably don't want to dynamic link zlib.
+  # this since it wants to look for zlib as zlib1.dll and as the httpd
+  # build instructions note you probably don't want to dynamic link zlib.
   
   # TODO: OpenSSL requires perl on the path since it uses perl without a full
   # path in the batch file and the makefiles.  Probably should determine
@@ -491,8 +544,9 @@ sub build_openssl {
   # The apache build docs suggest no-rc5 no-idea enable-mdc2 on top of what
   # is used below, the primary driver behind that is patents, but I believe
   # the rc5 and idea patents have expired.
+  my $platform = $DEBUG ? 'debug-VC-WIN32' : 'VC-WIN32';
   system_or_die("Failure configuring openssl",
-                qq("$PERL" Configure no-asm "--prefix=$INSTDIR" VC-WIN32));
+                qq("$PERL" Configure no-asm "--prefix=$INSTDIR" $platform));
   system_or_die("Failure building openssl (bat)", 'ms\do_ms.bat');
   system_or_die("Failure building openssl (nmake)", qq("$NMAKE" /f ms\\ntdll.mak));
   system_or_die("Failure testing openssl", qq("$NMAKE" /f ms\\ntdll.mak test));
@@ -507,6 +561,8 @@ sub build_openssl {
 # Visual Studio whining about its backup step.
 sub upgrade_solution {
   my $file = shift;
+  my $interactive = shift;
+  my $flags = "";
 
   my ($basename, $directories) = fileparse($file, qr/\.[^.]*$/);
   my $sln = $directories . $basename . '.sln';
@@ -519,7 +575,15 @@ sub upgrade_solution {
     close(SLN);
   }
   print "Upgrading $file (this may take a while)\n";
-  system_or_die("Failure upgrading $file", qq("$DEVENV" $file /Upgrade));
+  $flags = " /Upgrade" unless $interactive;
+  system_or_die("Failure upgrading $file", qq("$DEVENV" "$file"$flags));
+  if ($interactive) {
+    print "Can't do automatic upgrade, doing interactive upgrade\n";
+    print "IDE will load, choose to convert all projects, exit the IDE and\n";
+    print "save the resulting solution file\n\n";
+    print "Press Enter to Continue\n";
+    <>;
+  }
 }
 
 # Run the lineends.pl script
@@ -539,6 +603,9 @@ sub httpd_fix_makefile {
 
   modify_file_in_place($file, sub {
       s/\.vcproj/.vcxproj/i;
+      # below fixes that installd breaks when trying to install pcre because 
+      # dll is named pcred.dll when a Debug build. 
+      s/^(\s*copy srclib\\pcre\\pcre\.\$\(src_dll\)\s+"\$\(inst_dll\)"\s+<\s*\.y\s*)$/!IF EXISTS("srclib\\pcre\\pcre\.\$(src_dll)")\n$1!ENDIF\n!IF EXISTS("srclib\\pcre\\pcred\.\$(src_dll)")\n\tcopy srclib\\pcre\\pcred.\$(src_dll)\t\t\t"\$(inst_dll)" <.y\n!ENDIF\n/;
     });
 }
 
@@ -588,8 +655,9 @@ sub get_output_file {
 # Find the name of the bdb library we've installed in our LIBDIR.
 sub find_bdb_lib {
   my $result;
+  my $debug = $DEBUG ? 'd' : '';
   find(sub {
-         if (not defined($result) and /^libdb\d+\.lib$/) {
+         if (not defined($result) and /^libdb\d+$debug\.lib$/) {
            $result = $_;
          }
        }, $LIBDIR);
@@ -626,9 +694,25 @@ sub httpd_enable_bdb {
   insert_dependency_in_proj('support\htdbm.vcxproj', $bdb_lib, '.bdb');
 }
 
+# Apply the same fix as found in r1486937 on httpd 2.4.x branch.
+sub httpd_fix_debug {
+  my ($httpd_major, $httpd_minor, $httpd_patch) = $HTTPD_VER =~ /^(\d+)\.(\d+)\.(.+)$/;
+  return unless ($httpd_major <= 2 && $httpd_minor <= 4 && $httpd_patch < 5);
+
+  modify_file_in_place('libhttpd.dsp', sub {
+      s/^(!MESSAGE "libhttpd - Win32 Debug" \(based on "Win32 \(x86\) Dynamic-Link Library"\))$/$1\n!MESSAGE "libhttpd - Win32 Lexical" (based on "Win32 (x86) Dynamic-Link Library")/;
+      s/^(# Begin Group "headers")$/# Name "libhttpd - Win32 Lexical"\n$1/;
+    }, '.lexical');
+}
+
 sub build_httpd {
   chdir_or_die($HTTPD);
 
+  my $vs_2012 = $VS_VER eq '2012';
+  my $vs_2010 = $VS_VER eq '2010';
+
+  httpd_fix_debug();
+
   # I don't think cvtdsp.pl is necessary with Visual Studio 2012
   # but it shouldn't hurt anything either.  Including it allows
   # for the possibility that this may work for older Visual Studio
@@ -636,48 +720,54 @@ sub build_httpd {
   system_or_die("Failure converting DSP files",
                 qq("$PERL" srclib\\apr\\build\\cvtdsp.pl -2005));
 
-  upgrade_solution('Apache.dsw');
+  upgrade_solution('Apache.dsw', $vs_2010);
   httpd_enable_bdb();
   httpd_fix_makefile('Makefile.win');
 
-  # Turn off pre-compiled headers for apr-iconv to avoid:
-  # LNK2011: http://msdn.microsoft.com/en-us/library/3ay26wa2(v=vs.110).aspx
-  disable_pch('srclib\apr-iconv\build\modules.mk.win'); 
-
-  # ApacheMonitor build fails due a duplicate manifest, turn off
-  # GenerateManifest
-  insert_property_group('support\win32\ApacheMonitor.vcxproj',
-                        '<GenerateManifest>false</GenerateManifest>');
-
-  # Modules randomly fail due to an error about the CL.read.1.tlog file
-  # already existing.  This is really because of the intermediate dirs
-  # being shared between modules, but for the time being this works around
-  # it.
+  # Modules and support projects randomly fail due to an error about the
+  # CL.read.1.tlog file already existing.  This is really because of the
+  # intermediate dirs being shared between modules, but for the time being
+  # this works around it.
   find(sub {
          if (/\.vcxproj$/) {
            insert_property_group($_, '<TrackFileAccess>false</TrackFileAccess>')
          }
-       }, 'modules');
+       }, 'modules', 'support');
 
-  # The APR libraries have projects named libapr but produce output named libapr-1
-  # The problem with this is in newer versions of Visual Studio TargetName defaults
-  # to the project name and not the basename of the output.  Since the PDB file
-  # is named based on the TargetName the pdb file ends up being named libapr.pdb
-  # instead of libapr-1.pdb.  The below call fixes this by explicitly providing
-  # a TargetName definition and shuts up some warnings about this problem as well.
-  # Without this fix the install fails when it tries to copy libapr-1.pdb.
-  # See this thread for details of the changes:
-  # http://social.msdn.microsoft.com/Forums/en-US/vcprerelease/thread/3c03e730-6a0e-4ee4-a0d6-6a5c3ce4343c
-  find(sub {
-         return unless (/\.vcxproj$/);
-         my $output_file = get_output_file($_);
-         return unless (defined($output_file));
-         my ($project_name) = fileparse($_, qr/\.[^.]*$/);
-         my ($old_style_target_name) = fileparse($output_file, qr/\.[^.]*$/);
-         return if ($old_style_target_name eq $project_name);
-         insert_property_group($_,
-           "<TargetName>$old_style_target_name</TargetName>", '.torig');
-       }, "$SRCLIB\\apr", "$SRCLIB\\apr-util", "$SRCLIB\\apr-iconv");
+  if ($vs_2012) {
+    # Turn off pre-compiled headers for apr-iconv to avoid:
+    # LNK2011: http://msdn.microsoft.com/en-us/library/3ay26wa2(v=vs.110).aspx
+    disable_pch('srclib\apr-iconv\build\modules.mk.win');
+
+    # ApacheMonitor build fails due a duplicate manifest, turn off
+    # GenerateManifest
+    insert_property_group('support\win32\ApacheMonitor.vcxproj',
+                          '<GenerateManifest>false</GenerateManifest>',
+                          '.dupman');
+
+    # The APR libraries have projects named libapr but produce output named libapr-1
+    # The problem with this is in newer versions of Visual Studio TargetName defaults
+    # to the project name and not the basename of the output.  Since the PDB file
+    # is named based on the TargetName the pdb file ends up being named libapr.pdb
+    # instead of libapr-1.pdb.  The below call fixes this by explicitly providing
+    # a TargetName definition and shuts up some warnings about this problem as well.
+    # Without this fix the install fails when it tries to copy libapr-1.pdb.
+    # See this thread for details of the changes:
+    # http://social.msdn.microsoft.com/Forums/en-US/vcprerelease/thread/3c03e730-6a0e-4ee4-a0d6-6a5c3ce4343c
+    find(sub {
+           return unless (/\.vcxproj$/);
+           my $output_file = get_output_file($_);
+           return unless (defined($output_file));
+           my ($project_name) = fileparse($_, qr/\.[^.]*$/);
+           my ($old_style_target_name) = fileparse($output_file, qr/\.[^.]*$/);
+           return if ($old_style_target_name eq $project_name);
+           insert_property_group($_,
+             "<TargetName>$old_style_target_name</TargetName>", '.torig');
+         }, "$SRCLIB\\apr", "$SRCLIB\\apr-util", "$SRCLIB\\apr-iconv");
+  } elsif ($vs_2010) {
+    system_or_die("Failed fixing project guid references",
+      qq("$PYTHON" "$BINDIR\\ProjRef.py" -i Apache.sln"));
+  }
 
   # If you're looking here it's possible that something went
   # wrong with the httpd build.  Debugging it can be a bit of a pain
@@ -692,8 +782,9 @@ sub build_httpd {
   # configurations inside the project since we get them from the environment.
   # Once all that is done the BuildBin project should be buildable for you to
   # diagnose the problem.
+  my $target = $DEBUG ? "installd" : "installr";
   system_or_die("Failed building/installing httpd/apr/apu/api",
-    qq("$NMAKE" /f Makefile.win installr "DBM_LIST=db" "INSTDIR=$INSTDIR"));
+    qq("$NMAKE" /f Makefile.win $target "DBM_LIST=db" "INSTDIR=$INSTDIR"));
 
   chdir_or_die($TOPDIR);
 }
@@ -705,13 +796,15 @@ sub build_bdb {
   my $sln = 'build_windows\Berkeley_DB_vs2010.sln'; 
   upgrade_solution($sln);
 
+  my $platform = $DEBUG ? 'Debug|Win32' : 'Release|Win32';
+
   # Build the db Project first since the full solution fails due to a broken
   # dependency with the current version of BDB if we don't.
   system_or_die("Failed building DBD (Project db)",
-                qq("$DEVENV" "$sln" /Build "Release|Win32" /Project db));
+                qq("$DEVENV" "$sln" /Build "$platform" /Project db));
 
   system_or_die("Failed building DBD",
-                qq("$DEVENV" "$sln" /Build "Release|Win32"));
+                qq("$DEVENV" "$sln" /Build "$platform"));
 
   # BDB doesn't seem to have it's own install routines so we'll do it ourselves
   copy_or_die('build_windows\db.h', $INCDIR);
@@ -721,8 +814,22 @@ sub build_bdb {
      } elsif (/\.lib$/) {
        copy_or_die($_, $LIBDIR);
      }
-   }, 'build_windows\Win32\Release');
+   }, 'build_windows\\Win32\\' . ($DEBUG ? 'Debug' : 'Release'));
+
+  chdir_or_die($TOPDIR);
+}
 
+# Right now this doesn't actually build serf but just patches it so that it
+# can build against a debug build of OpenSSL.
+sub build_serf {
+  chdir_or_die("$TOPDIR\\serf");
+
+  modify_file_in_place('serf.mak', sub {
+      s/^(INTDIR = Release)$/$1\nOPENSSL_OUT_SUFFIX =/;
+      s/^(INTDIR = Debug)$/$1\nOPENSSL_OUT_SUFFIX = .dbg/;
+      s/(\$\(OPENSSL_SRC\)\\out32(?:dll)?)/$1\$(OPENSSL_OUT_SUFFIX)/g;
+    }, '.debug');
+  
   chdir_or_die($TOPDIR);
 }
 
@@ -731,6 +838,7 @@ sub build_dependencies {
   build_zlib();
   build_pcre();
   build_openssl();
+  build_serf();
   build_httpd();
 }
 
@@ -773,6 +881,9 @@ sub main {
   Vars::set_defaults();
   set_paths();
 
+  # Determine the Visual Studio Version and die if not supported.
+  check_vs_ver();
+
   # change directory to our TOPDIR before running any commands
   # the variable assignment might have changed it.
   chdir_or_die($TOPDIR);

Modified: subversion/branches/cache-server/tools/dev/fsfs-access-map.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/dev/fsfs-access-map.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/dev/fsfs-access-map.c (original)
+++ subversion/branches/cache-server/tools/dev/fsfs-access-map.c Tue Oct 15 08:52:06 2013
@@ -52,9 +52,15 @@ typedef struct file_stats_t
   /* number of lseek calls to clusters not previously read */
   apr_int64_t uncached_seek_count;
 
+  /* number of lseek counts not followed by a read */
+  apr_int64_t unnecessary_seeks;
+
   /* number of read() calls */
   apr_int64_t read_count;
 
+  /* number of read() calls that returned 0 bytes */
+  apr_int64_t empty_reads;
+  
   /* total number of bytes returned by those reads */
   apr_int64_t read_size;
 
@@ -86,12 +92,17 @@ typedef struct handle_info_t
   /* bytes read so far in the current series of reads started (default: 0) */
   apr_int64_t last_read_size;
 
+  /* number of read() calls in this series */
+  apr_int64_t read_count;
 } handle_info_t;
 
 /* useful typedef */
 typedef unsigned char byte;
 typedef unsigned short word;
 
+/* an RGB color */
+typedef byte color_t[3];
+
 /* global const char * file name -> *file_info_t map */
 static apr_hash_t *files = NULL;
 
@@ -136,6 +147,11 @@ store_read_info(handle_info_t *handle_in
             ++*count;
         }
     }
+  else if (handle_info->read_count == 0)
+    {
+      /* two consecutive seeks */
+      handle_info->file->unnecessary_seeks++;
+    }
 }
 
 /* Handle a open() call.  Ensures that a file_info_t for the given NAME
@@ -188,6 +204,14 @@ open_file(const char *name, int handle)
       else
         file->rev_num = -1;
 
+      /* filter out log/phys index files */
+      if (file->rev_num >= 0)
+        {
+          const char *suffix = name + strlen(name) - 4;
+          if (strcmp(suffix, ".l2p") == 0 || strcmp(suffix, ".p2l") == 0)
+            file->rev_num = -1;
+        }
+
       apr_hash_set(files, file->name, APR_HASH_KEY_STRING, file);
     }
 
@@ -220,9 +244,13 @@ read_file(int handle, apr_int64_t count)
     {
       /* known file handle -> expand current read sequence */
 
+      handle_info->read_count++;
       handle_info->last_read_size += count;
       handle_info->file->read_count++;
       handle_info->file->read_size += count;
+
+      if (count == 0)
+        handle_info->file->empty_reads++;
     }
 }
 
@@ -242,6 +270,7 @@ seek_file(int handle, apr_int64_t locati
 
       handle_info->last_read_size = 0;
       handle_info->last_read_start = location;
+      handle_info->read_count = 0;
       handle_info->file->seek_count++;
 
       /* if we seek to a location that had not been read from before,
@@ -275,10 +304,17 @@ parse_line(svn_stringbuf_t *line)
   char *return_value = strrchr(line->data, ' ');
   char *first_param_end;
   apr_int64_t func_return = 0;
+  char *func_start = strchr(line->data, ' ');
 
   if (func_end == NULL || return_value == NULL)
     return;
 
+  if (func_start == NULL || func_start > func_end)
+    func_start = line->data;
+  else
+    while(*func_start == ' ')
+      func_start++;
+  
   first_param_end = strchr(func_end, ',');
   if (first_param_end == NULL)
     first_param_end = strchr(func_end, ')');
@@ -295,7 +331,7 @@ parse_line(svn_stringbuf_t *line)
   svn_error_clear(svn_cstring_atoi64(&func_return, return_value));
 
   /* process those operations that we care about */
-  if (strcmp(line->data, "open") == 0)
+  if (strcmp(func_start, "open") == 0)
     {
       /* remove double quotes from file name parameter */
       *func_end++ = 0;
@@ -303,11 +339,11 @@ parse_line(svn_stringbuf_t *line)
 
       open_file(func_end, (int)func_return);
     }
-  else if (strcmp(line->data, "read") == 0)
+  else if (strcmp(func_start, "read") == 0)
     read_file(atoi(func_end), func_return);
-  else if (strcmp(line->data, "lseek") == 0)
+  else if (strcmp(func_start, "lseek") == 0)
     seek_file(atoi(func_end), func_return);
-  else if (strcmp(line->data, "close") == 0)
+  else if (strcmp(func_start, "close") == 0)
     close_file(atoi(func_end));
 }
 
@@ -494,17 +530,82 @@ write_bitmap_header(apr_file_t *file, in
   apr_file_write(file, header, &written);
 }
 
-/* write the cluster read map for all files in INFO as BMP image to FILE.
+/* To COLOR, add the fractional value of SOURCE from fractional indexes
+ * SOURCE_START to SOURCE_END and apply the SCALING_FACTOR.
+ */
+static void
+add_sample(color_t color,
+           color_t *source,
+           double source_start,
+           double source_end,
+           double scaling_factor)
+{
+  double factor = (source_end - source_start) / scaling_factor;
+
+  apr_size_t i;
+  for (i = 0; i < sizeof(color_t) / sizeof(*color); ++i)
+    color[i] += (source_end - source_start < 0.5) && source_start > 1.0
+              ? factor * source[(apr_size_t)source_start - 1][i]
+              : factor * source[(apr_size_t)source_start][i];
+}
+
+/* Scale the IN_LEN RGB values from IN to OUT_LEN RGB values in OUT.
+ */
+static void
+scale_line(color_t* out,
+           apr_size_t out_len,
+           color_t *in,
+           apr_size_t in_len)
+{
+  double scaling_factor = (double)(in_len) / (double)(out_len);
+  
+  apr_size_t i;
+  memset(out, 0, out_len * sizeof(color_t));
+  for (i = 0; i < out_len; ++i)
+    {
+      color_t color = { 0 };
+      
+      double source_start = i * scaling_factor;
+      double source_end = (i + 1) * scaling_factor;
+
+      if ((apr_size_t)source_start == (apr_size_t)source_end)
+        {
+          add_sample(color, in, source_start, source_end, scaling_factor);
+        }
+      else
+        {
+          apr_size_t k;
+          apr_size_t first_sample_end = (apr_size_t)source_start + 1;
+          apr_size_t last_sample_start = (apr_size_t)source_end;
+
+          add_sample(color, in, source_start, first_sample_end, scaling_factor);
+          for (k = first_sample_end; k < last_sample_start; ++k)
+            add_sample(color, in, k, k + 1, scaling_factor);
+
+          add_sample(color, in, last_sample_start, source_end, scaling_factor);
+        }
+
+      memcpy(out[i], color, sizeof(color));
+    }
+}
+
+/* Write the cluster read map for all files in INFO as BMP image to FILE.
+ * If MAX_X is not 0, scale all lines to MAX_X pixels.  Use POOL for
+ * allocations.
  */
 static void
-write_bitmap(apr_array_header_t *info, apr_file_t *file)
+write_bitmap(apr_array_header_t *info,
+             apr_size_t max_x,
+             apr_file_t *file,
+             apr_pool_t *pool)
 {
   int ysize = info->nelts;
   int xsize = 0;
   int x, y;
   int row_size;
-  int padding;
   apr_size_t written;
+  color_t *line, *scaled_line;
+  svn_boolean_t do_scale = max_x > 0;
 
   /* xsize = max cluster number */
   for (y = 0; y < ysize; ++y)
@@ -516,37 +617,40 @@ write_bitmap(apr_array_header_t *info, a
     xsize = 0x3fff;
   if (ysize >= 0x4000)
     ysize = 0x3fff;
+  if (max_x == 0)
+    max_x = xsize;
 
   /* rows in BMP files must be aligned to 4 bytes */
-  row_size = APR_ALIGN(xsize * 3, 4);
-  padding = row_size - xsize * 3;
+  row_size = APR_ALIGN(max_x * sizeof(color_t), 4);
 
+  /**/
+  line = apr_pcalloc(pool, xsize * sizeof(color_t));
+  scaled_line = apr_pcalloc(pool, row_size);
+  
   /* write header to file */
-  write_bitmap_header(file, xsize, ysize);
+  write_bitmap_header(file, max_x, ysize);
 
   /* write all rows */
   for (y = 0; y < ysize; ++y)
     {
       file_stats_t *file_info = APR_ARRAY_IDX(info, y, file_stats_t *);
+      int block_count = file_info->read_map->nelts;
       for (x = 0; x < xsize; ++x)
         {
-          byte color[3] = { 128, 128, 128 };
-          if (x < file_info->read_map->nelts)
+          color_t color = { 128, 128, 128 };
+          if (x < block_count)
             {
               word count = APR_ARRAY_IDX(file_info->read_map, x, word);
               select_color(color, count);
             }
 
-          written = sizeof(color);
-          apr_file_write(file, color, &written);
+          memcpy(line[x], color, sizeof(color));
         }
 
-      if (padding)
-        {
-          char pad[3] = { 0 };
-          written = padding;
-          apr_file_write(file, pad, &written);
-        }
+      scale_line(scaled_line, max_x, line, block_count ? block_count : 1);
+
+      written = row_size;
+      apr_file_write(file, do_scale ? scaled_line : line, &written);
     }
 }
 
@@ -592,6 +696,8 @@ print_stats(apr_pool_t *pool)
   apr_int64_t clusters_read = 0;
   apr_int64_t unique_clusters_read = 0;
   apr_int64_t uncached_seek_count = 0;
+  apr_int64_t unnecessary_seek_count = 0;
+  apr_int64_t empty_read_count = 0;
 
   apr_hash_index_t *hi;
   for (hi = apr_hash_first(pool, files); hi; hi = apr_hash_next(hi))
@@ -609,13 +715,17 @@ print_stats(apr_pool_t *pool)
       clusters_read += file->clusters_read;
       unique_clusters_read += file->unique_clusters_read;
       uncached_seek_count += file->uncached_seek_count;
+      unnecessary_seek_count += file->unnecessary_seeks;
+      empty_read_count += file->empty_reads;
     }
 
   printf("%20s files\n", svn__i64toa_sep(apr_hash_count(files), ',', pool));
   printf("%20s files opened\n", svn__i64toa_sep(open_count, ',', pool));
   printf("%20s seeks\n", svn__i64toa_sep(seek_count, ',', pool));
+  printf("%20s unnecessary seeks\n", svn__i64toa_sep(unnecessary_seek_count, ',', pool));
   printf("%20s uncached seeks\n", svn__i64toa_sep(uncached_seek_count, ',', pool));
   printf("%20s reads\n", svn__i64toa_sep(read_count, ',', pool));
+  printf("%20s empty reads\n", svn__i64toa_sep(empty_read_count, ',', pool));
   printf("%20s unique clusters read\n", svn__i64toa_sep(unique_clusters_read, ',', pool));
   printf("%20s clusters read\n", svn__i64toa_sep(clusters_read, ',', pool));
   printf("%20s bytes read\n", svn__i64toa_sep(read_size, ',', pool));
@@ -629,7 +739,7 @@ print_usage(void)
   printf("Reads strace of some FSFS-based tool from <file>, prints some stats\n");
   printf("and writes a cluster access map to 'access.bmp' the current folder.\n");
   printf("Each pixel corresponds to one 64kB cluster and every line to a rev\n");
-  printf("or packed rev file in the repository.  Turquoise and greed indicate\n");
+  printf("or packed rev file in the repository.  Turquoise and green indicate\n");
   printf("1 and 2 hits, yellow to read-ish colors for up to 20, shares of\n");
   printf("for up to 100 and black for > 200 hits.\n\n");
   printf("A typical strace invocation looks like this:\n");
@@ -665,7 +775,13 @@ int main(int argc, const char *argv[])
   apr_file_open(&file, "access.bmp",
                 APR_WRITE | APR_CREATE | APR_TRUNCATE | APR_BUFFERED,
                 APR_OS_DEFAULT, pool);
-  write_bitmap(get_rev_files(pool), file);
+  write_bitmap(get_rev_files(pool), 0, file, pool);
+  apr_file_close(file);
+
+  apr_file_open(&file, "access_scaled.bmp",
+                APR_WRITE | APR_CREATE | APR_TRUNCATE | APR_BUFFERED,
+                APR_OS_DEFAULT, pool);
+  write_bitmap(get_rev_files(pool), 1024, file, pool);
   apr_file_close(file);
 
   apr_file_open(&file, "scale.bmp",
@@ -675,4 +791,4 @@ int main(int argc, const char *argv[])
   apr_file_close(file);
 
   return 0;
-}
\ No newline at end of file
+}

Modified: subversion/branches/cache-server/tools/dev/fsfs-reorg.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/dev/fsfs-reorg.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/dev/fsfs-reorg.c (original)
+++ subversion/branches/cache-server/tools/dev/fsfs-reorg.c Tue Oct 15 08:52:06 2013
@@ -29,6 +29,7 @@
 #include <apr_file_io.h>
 #include <apr_poll.h>
 
+#include "svn_private_config.h"
 #include "svn_pools.h"
 #include "svn_diff.h"
 #include "svn_io.h"

Modified: subversion/branches/cache-server/tools/dev/po-merge.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/dev/po-merge.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/dev/po-merge.py (original)
+++ subversion/branches/cache-server/tools/dev/po-merge.py Tue Oct 15 08:52:06 2013
@@ -146,6 +146,7 @@ def main(argv):
     string_count = 0
     update_count = 0
     untranslated = 0
+    fuzzy = 0
     while True:
         comments, msgid, msgid_plural, msgstr = parse_translation(infile)
         if not comments and msgid is None:
@@ -177,14 +178,19 @@ def main(argv):
                 for i in msgstr:
                     outfile.write('msgstr[%s] %s\n' % (n, msgstr[n]))
                     n += 1
-        for m in msgstr:
-            if m == '""':
-                untranslated += 1
+        if msgstr is not None:
+            for m in msgstr:
+                if m == '""':
+                    untranslated += 1
+        for c in comments:
+            if c.startswith('#,') and 'fuzzy' in c.split(', '):
+                fuzzy += 1
 
     # We're done.  Tell the user what we did.
     print(('%d strings updated. '
+          '%d fuzzy strings. ' 
           '%d of %d strings are still untranslated (%.0f%%).' %
-          (update_count, untranslated, string_count,
+          (update_count, fuzzy, untranslated, string_count,
            100.0 * untranslated / string_count)))
 
 if __name__ == '__main__':

Modified: subversion/branches/cache-server/tools/dev/remove-trailing-whitespace.sh
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/dev/remove-trailing-whitespace.sh?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/dev/remove-trailing-whitespace.sh (original)
+++ subversion/branches/cache-server/tools/dev/remove-trailing-whitespace.sh Tue Oct 15 08:52:06 2013
@@ -20,5 +20,5 @@
  for ext in c h cpp java py pl rb hpp cmd bat; do
    find . -name "*.$ext" -exec \
      perl -pi -e 's/[ \t]*$//' {} + ;
-     # don't use \t to not strip ^L pagebreaks
+     # don't use \s to not strip ^L pagebreaks
  done                         

Modified: subversion/branches/cache-server/tools/dev/svnraisetreeconflict/svnraisetreeconflict.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/dev/svnraisetreeconflict/svnraisetreeconflict.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/dev/svnraisetreeconflict/svnraisetreeconflict.c (original)
+++ subversion/branches/cache-server/tools/dev/svnraisetreeconflict/svnraisetreeconflict.c Tue Oct 15 08:52:06 2013
@@ -187,7 +187,7 @@ raise_tree_conflict(int argc, const char
 {
   int i = 0;
   svn_wc_conflict_version_t *left, *right;
-  svn_wc_conflict_description2_t *c;
+  svn_wc_conflict_description3_t *c;
   svn_wc_context_t *wc_ctx;
 
   /* Conflict description parameters */
@@ -223,7 +223,7 @@ raise_tree_conflict(int argc, const char
                                          peg_rev1, kind1, pool);
   right = svn_wc_conflict_version_create2(repos_url2, NULL, path_in_repos2,
                                           peg_rev2, kind2, pool);
-  c = svn_wc_conflict_description_create_tree2(wc_abspath, kind,
+  c = svn_wc_conflict_description_create_tree3(wc_abspath, kind,
                                               operation, left, right, pool);
   c->action = (svn_wc_conflict_action_t)action;
   c->reason = (svn_wc_conflict_reason_t)reason;
@@ -311,7 +311,7 @@ check_lib_versions(void)
     };
   SVN_VERSION_DEFINE(my_version);
 
-  return svn_ver_check_list(&my_version, checklist);
+  return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
 }
 
 int

Modified: subversion/branches/cache-server/tools/dev/unix-build/Makefile.svn
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/dev/unix-build/Makefile.svn?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/dev/unix-build/Makefile.svn (original)
+++ subversion/branches/cache-server/tools/dev/unix-build/Makefile.svn Tue Oct 15 08:52:06 2013
@@ -39,6 +39,7 @@ endif
 USE_APR_ICONV ?= no # set to yes to use APR iconv instead of GNU iconv
 PARALLEL ?= 1
 CLEANUP ?= 1
+EXCLUSIVE_WC_LOCKS ?= 1
 USE_HTTPV1 ?= no
 USE_AUTHZ_SHORT_CIRCUIT ?= no
 RAMDISK ?= /ramdisk
@@ -69,9 +70,9 @@ APR_VER		= 1.4.6
 APR_ICONV_VER	= 1.2.1
 GNU_ICONV_VER	= 1.14
 APR_UTIL_VER	= 1.4.1
-HTTPD_VER	= 2.2.22
+HTTPD_VER	= 2.2.25
 NEON_VER	= 0.29.6
-SERF_VER	= 1.2.0
+SERF_VER	= 1.3.1
 SERF_OLD_VER	= 0.3.1
 CYRUS_SASL_VER	= 2.1.25
 SQLITE_VER	= 3071600
@@ -126,7 +127,7 @@ LIBMAGIC_URL	= ftp://ftp.astron.com/pub/
 RUBY_URL	= http://ftp.ruby-lang.org/pub/ruby/1.8/$(RUBY_DIST)
 BZ2_URL		= http://bzip.org/$(BZ2_VER)/$(BZ2_DIST)
 PYTHON_URL	= http://python.org/ftp/python/$(PYTHON_VER)/$(PYTHON_DIST)
-JUNIT_URL	= http://cloud.github.com/downloads/KentBeck/junit/$(JUNIT_DIST)
+JUNIT_URL	= http://downloads.sourceforge.net/project/junit/junit/$(JUNIT_VER)/$(JUNIT_DIST)
 
 
 BDB_SRCDIR	= $(SRCDIR)/db-$(BDB_VER)
@@ -547,15 +548,35 @@ httpd-reset:
 
 httpd-clean:
 	-(cd $(HTTPD_OBJDIR) && make clean)
+	rm -f $(HTTPD_OBJDIR)/no_ssl_v2.diff
 
 # fetch distfile for httpd
 $(DISTDIR)/$(HTTPD_DIST):
 	cd $(DISTDIR) && $(FETCH_CMD) $(HTTPD_URL)
 
+$(HTTPD_OBJDIR)/no_ssl_v2.diff:
+	mkdir -p $(dir $@)
+	echo > $@.tmp '--- modules/ssl/ssl_engine_io.c.orig	Sat Jul 13 16:49:52 2013'
+	echo >> $@.tmp '+++ modules/ssl/ssl_engine_io.c	Sat Jul 13 16:50:10 2013'
+	echo >> $@.tmp '@@ -1079,7 +1079,9 @@'
+	echo >> $@.tmp '          * IPv4 and IPv6 addresses are not permitted".)'
+	echo >> $@.tmp '          */'
+	echo >> $@.tmp '         if (hostname_note &&'
+	echo >> $@.tmp '+#ifndef OPENSSL_NO_SSL2'
+	echo >> $@.tmp '             sc->proxy->protocol != SSL_PROTOCOL_SSLV2 &&'
+	echo >> $@.tmp '+#endif'
+	echo >> $@.tmp '             sc->proxy->protocol != SSL_PROTOCOL_SSLV3 &&'
+	echo >> $@.tmp '             apr_ipsubnet_create(&ip, hostname_note, NULL,'
+	echo >> $@.tmp '                                 c->pool) != APR_SUCCESS) {'
+	mv -f $@.tmp $@
+
 # retrieve httpd
-$(HTTPD_OBJDIR)/.retrieved: $(DISTDIR)/$(HTTPD_DIST)
+$(HTTPD_OBJDIR)/.retrieved: $(DISTDIR)/$(HTTPD_DIST) \
+				$(HTTPD_OBJDIR)/no_ssl_v2.diff
 	[ -d $(HTTPD_OBJDIR) ] || mkdir -p $(HTTPD_OBJDIR)
 	tar -C $(SRCDIR) -jxf $(DISTDIR)/$(HTTPD_DIST)
+	cd $(SRCDIR)/httpd-$(HTTPD_VER) && \
+		patch -p0 < $(HTTPD_OBJDIR)/no_ssl_v2.diff
 	touch $@
 
 # configure httpd
@@ -686,7 +707,7 @@ serf-reset:
 		rm -f $(SERF_OBJDIR)/$(f);)
 
 serf-clean:
-	-(cd $(SERF_SRCDIR) && ./serfmake clean)
+	-(cd $(SERF_SRCDIR) && scons -c)
 
 
 # fetch distfile for serf
@@ -712,19 +733,17 @@ $(SERF_OBJDIR)/.retrieved:
 # compile serf (serf won't compile outside its source tree)
 $(SERF_OBJDIR)/.compiled: $(SERF_OBJDIR)/.retrieved
 	cd $(SERF_SRCDIR) && \
-		env CFLAGS="-O0 -g $(PROFILE_CFLAGS)" \
-			./serfmake --with-apr=$(PREFIX)/apr \
-			--prefix=$(PREFIX)/serf \
-			build
+		scons DEBUG=1 CFLAGS="-O0 -g $(PROFILE_CFLAGS)" \
+			APR=$(PREFIX)/apr \
+			APU=$(PREFIX)/apr \
+			PREFIX=$(PREFIX)/serf
 	touch $@
 
 # install serf
 $(SERF_OBJDIR)/.installed: $(SERF_OBJDIR)/.compiled
+	rm -rf $(PREFIX)/serf # XXX scons cannot reinstall :(
 	cd $(SERF_SRCDIR) && \
-		./serfmake --with-apr=$(PREFIX)/apr \
-			--with-apr-util=$(PREFIX)/apr \
-			--prefix=$(PREFIX)/serf \
-			install
+		scons install
 	touch $@
 
 #######################################################################
@@ -1153,6 +1172,7 @@ $(SVN_OBJDIR)/.retrieved:
 ifeq ($(BRANCH_MAJOR),1.7)
 BDB_FLAG=db.h:$(PREFIX)/bdb/include:$(PREFIX)/bdb/lib:db-$(BDB_MAJOR_VER)
 SERF_FLAG=--with-serf="$(PREFIX)/serf"
+SERF_LDFLAG=-Wl,-rpath,$(PREFIX)/serf/lib -Wl,-rpath,$(PREFIX)/bdb/lib
 MOD_DAV_SVN=modules/svn-$(WC)/mod_dav_svn.so
 MOD_AUTHZ_SVN=modules/svn-$(WC)/mod_authz_svn.so
 LIBMAGIC_FLAG=--with-libmagic=$(PREFIX)/libmagic
@@ -1161,6 +1181,7 @@ JAVAHL_CHECK_TARGET=check-javahl
 else ifeq ($(BRANCH_MAJOR),1.6)
 BDB_FLAG=db.h:$(PREFIX)/bdb/include:$(PREFIX)/bdb/lib:db-$(BDB_MAJOR_VER)
 SERF_FLAG=--with-serf="$(PREFIX)/serf"
+SERF_LDFLAG=-Wl,-rpath,$(PREFIX)/serf/lib -Wl,-rpath,$(PREFIX)/bdb/lib
 MOD_DAV_SVN=modules/svn-$(WC)/mod_dav_svn.so
 MOD_AUTHZ_SVN=modules/svn-$(WC)/mod_authz_svn.so
 W_NO_SYSTEM_HEADERS=-Wno-system-headers
@@ -1178,6 +1199,9 @@ JAVAHL_CHECK_TARGET=check-javahl
 else # 1.8
 BDB_FLAG=db.h:$(PREFIX)/bdb/include:$(PREFIX)/bdb/lib:db-$(BDB_MAJOR_VER)
 SERF_FLAG=--with-serf="$(PREFIX)/serf"
+# serf >= 1.3.0 is built with scons and no longer sets up rpath linker flags,
+# so we have to do that ourselves :(
+SERF_LDFLAG=-Wl,-rpath,$(PREFIX)/serf/lib -Wl,-rpath,$(PREFIX)/bdb/lib
 MOD_DAV_SVN=modules/svn-$(WC)/mod_dav_svn.so
 MOD_AUTHZ_SVN=modules/svn-$(WC)/mod_authz_svn.so
 LIBMAGIC_FLAG=--with-libmagic=$(PREFIX)/libmagic
@@ -1192,8 +1216,9 @@ else
 endif
 
 ifdef PROFILE
-SVN_ALL_STATIC=--enable-all-static
+SVN_STATIC_FLAG=--enable-all-static
 else
+SVN_STATIC_FLAG=--disable-static
 SVN_WITH_HTTPD=--with-apxs="$(PREFIX)/httpd/bin/apxs" \
 	--with-apache-libexecdir="$(PREFIX)/httpd/modules/svn-$(WC)"
 SVN_WITH_SASL=--with-sasl="$(PREFIX)/cyrus-sasl"
@@ -1203,7 +1228,7 @@ endif
 $(SVN_OBJDIR)/.configured: $(SVN_OBJDIR)/.retrieved $(DISTDIR)/$(JUNIT_DIST)
 	cd $(SVN_SRCDIR) && ./autogen.sh
 	cd $(svn_builddir) && \
-		env LDFLAGS="-L$(PREFIX)/neon/lib -L$(PREFIX)/apr/lib" \
+		env LDFLAGS="-L$(PREFIX)/neon/lib -L$(PREFIX)/apr/lib $(SERF_LDFLAG)" \
 			LD_LIBRARY_PATH="$(LD_LIBRARY_PATH):$$LD_LIBRARY_PATH" \
 			GREP="`which grep`" \
 			PATH=$(PREFIX)/ruby/bin:$(PREFIX)/python/bin:$$PATH \
@@ -1223,7 +1248,7 @@ $(SVN_OBJDIR)/.configured: $(SVN_OBJDIR)
 			--disable-mod-activation \
 			$(JAVAHL_FLAG) \
 			$(LIBMAGIC_FLAG) \
-			$(SVN_ALL_STATIC) \
+			$(SVN_STATIC_FLAG) \
 			$(DISABLE_NEON_VERSION_CHECK)
 	touch $@
 
@@ -1246,7 +1271,7 @@ $(SVN_OBJDIR)/.installed: $(SVN_OBJDIR)/
 # by the same version of SWIG.
 $(SVN_OBJDIR)/.pre-generated-swig-cleaned:
 	-cd $(svn_builddir) \
-		&& make extraclean-swig
+		&& make clean-swig
 	touch $@
 
 $(SVN_OBJDIR)/.bindings-compiled: $(SVN_OBJDIR)/.installed $(SVN_OBJDIR)/.pre-generated-swig-cleaned
@@ -1421,7 +1446,8 @@ define do_check
     echo "Begin test: $(subst svn-check-,,$@) x $$fs"; \
     test -d "$(RAMDISK)/tmp" && export TMPDIR="$(RAMDISK)/tmp"; \
     env LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) $(LIB_PTHREAD_HACK) \
-        make check PARALLEL=$(PARALLEL) CLEANUP=$(CLEANUP) $1 FS_TYPE=$$fs; \
+        make check PARALLEL=$(PARALLEL) CLEANUP=$(CLEANUP) \
+	  EXCLUSIVE_WC_LOCKS=$(EXCLUSIVE_WC_LOCKS) $1 FS_TYPE=$$fs; \
     for log in tests.log fails.log; do \
         test -f $$log && mv -f $$log $$log.$@-$$fs; \
     done; \

Modified: subversion/branches/cache-server/tools/dev/which-error.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/dev/which-error.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/dev/which-error.py (original)
+++ subversion/branches/cache-server/tools/dev/which-error.py Tue Oct 15 08:52:06 2013
@@ -73,6 +73,9 @@ def get_errors():
   errs.update(errno.errorcode)
   ## APR-defined errors, from apr_errno.h.
   for line in open(os.path.join(os.path.dirname(sys.argv[0]), 'aprerr.txt')):
+    # aprerr.txt parsing duplicated in gen_base.py:write_errno_table()
+    if line.startswith('#'):
+       continue
     key, _, val = line.split()
     errs[int(val)] = key
   ## Subversion errors, from svn_error_codes.h.

Modified: subversion/branches/cache-server/tools/dist/backport.pl
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/dist/backport.pl?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/dist/backport.pl (original)
+++ subversion/branches/cache-server/tools/dist/backport.pl Tue Oct 15 08:52:06 2013
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -l
+#!/usr/bin/perl
 use warnings;
 use strict;
 use feature qw/switch say/;
@@ -20,93 +20,242 @@ use feature qw/switch say/;
 # specific language governing permissions and limitations
 # under the License.
 
+use Digest ();
 use Term::ReadKey qw/ReadMode ReadKey/;
+use File::Basename qw/basename dirname/;
+use File::Copy qw/copy move/;
 use File::Temp qw/tempfile/;
-use POSIX qw/ctermid/;
+use POSIX qw/ctermid strftime/;
+use Text::Wrap qw/wrap/;
+use Tie::File ();
 
+############### Start of reading values from environment ###############
+
+# Programs we use.
+my $SVNAUTH = $ENV{SVNAUTH} // 'svnauth'; # optional dependency
 my $SVN = $ENV{SVN} || 'svn'; # passed unquoted to sh
+my $SHELL = $ENV{SHELL} // '/bin/sh';
 my $VIM = 'vim';
-my $STATUS = './STATUS';
-my $BRANCHES = '^/subversion/branches';
+my $EDITOR = $ENV{SVN_EDITOR} // $ENV{VISUAL} // $ENV{EDITOR} // 'ed';
+my $PAGER = $ENV{PAGER} // 'less -F' // 'cat';
 
-my $YES = $ENV{YES}; # batch mode: eliminate prompts, add sleeps
-my $MAY_COMMIT = qw[false true][0];
-my $DEBUG = qw[false true][0]; # 'set -x', etc
-$DEBUG = 'true' if exists $ENV{DEBUG};
+# Mode flags.
+#    svn-role:      YES=1 MAY_COMMIT=1
+#    conflicts-bot: YES=1 MAY_COMMIT=0
+#    interactive:   YES=0 MAY_COMMIT=0      (default)
+my $YES = exists $ENV{YES}; # batch mode: eliminate prompts, add sleeps
+my $MAY_COMMIT = 'false';
 $MAY_COMMIT = 'true' if ($ENV{MAY_COMMIT} // "false") =~ /^(1|yes|true)$/i;
 
-# derived values
+# Other knobs.
+my $VERBOSE = 0;
+my $DEBUG = (exists $ENV{DEBUG}) ? 'true' : 'false'; # 'set -x', etc
+
+# Username for entering votes.
+my $SVN_A_O_REALM = '<https://svn.apache.org:443> ASF Committers';            
+my ($AVAILID) = $ENV{AVAILID} // do {
+  local $_ = `$SVNAUTH list 2>/dev/null`;
+  ($? == 0 && /Auth.*realm: \Q$SVN_A_O_REALM\E\nUsername: (.*)/) ? $1 : undef
+} // do {
+  local $/; # slurp mode
+  my $filename = Digest->new("MD5")->add($SVN_A_O_REALM)->hexdigest;
+  `cat $ENV{HOME}/.subversion/auth/svn.simple/$filename`
+  =~ /K 8\nusername\nV \d+\n(.*)/ and $1 or undef
+}
+// warn "Username for commits (of votes/merges) not found";
+
+############## End of reading values from the environment ##############
+
+# Constants.
+my $STATUS = './STATUS';
+my $STATEFILE = './.backports1';
+my $BRANCHES = '^/subversion/branches';
+
+# Globals.
+my %ERRORS = ();
+my $MERGED_SOMETHING = 0;
 my $SVNq;
+
+# Derived values.
 my $SVNvsn = do {
   my ($major, $minor, $patch) = `$SVN --version -q` =~ /^(\d+)\.(\d+)\.(\d+)/;
   1e6*$major + 1e3*$minor + $patch;
 };
-
 $SVN .= " --non-interactive" if $YES or not defined ctermid;
 $SVNq = "$SVN -q ";
 $SVNq =~ s/-q// if $DEBUG eq 'true';
 
-sub usage {
-  my $basename = $0;
-  $basename =~ s#.*/##;
+
+sub backport_usage {
+  my $basename = basename $0;
   print <<EOF;
-Run this from the root of your release branch (e.g., 1.6.x) working copy.
+backport.pl: a tool for reviewing, merging, and voting on STATUS entries.
 
-For each entry in STATUS, you will be prompted whether to merge it.
+Normally, invoke this with CWD being the root of the stable branch (e.g.,
+1.8.x):
 
-WARNING:
-If you accept the prompt, $basename will revert all local changes and will
-commit the merge immediately.
+    Usage: test -e \$d/STATUS && cd \$d && \\
+           backport.pl [PATTERN]
+    (where \$d is a working copy of branches/1.8.x)
+
+Alternatively, invoke this via a symlink named "b" placed at the same directory
+as the STATUS file, in which case the CWD doesn't matter (the script will cd):
+
+    Usage: ln -s /path/to/backport.pl \$d/b && \\
+           \$d/b [PATTERN]
+    (where \$d is a working copy of branches/1.8.x)
+
+In either case, the ./STATUS file should be at HEAD.  If it has local mods,
+they will be preserved through 'revert' operations but included in 'commit'
+operations.
+
+If PATTERN is provided, only entries which match PATTERN are considered.  The
+sense of "match" is either substring (fgrep) or Perl regexp (with /msi).
+
+In interactive mode (the default), you will be prompted once per STATUS entry.
+At a prompt, you have the following options:
+
+y:   Run a merge.  It will not be committed.
+     WARNING: This will run 'update' and 'revert -R ./'.
+l:   Show logs for the entries being nominated.
+q:   Quit the "for each nomination" loop.
+±1:  Enter a +1 or -1 vote
+     You will be prompted to commit your vote at the end.
+±0:  Enter a +0 or -0 vote
+     You will be prompted to commit your vote at the end.
+a:   Move the entry to the "Approved changes" section.
+     When both approving and voting on an entry, approve first: for example,
+     to enter a third +1 vote, type "a" "+" "1".
+e:   Edit the entry in $EDITOR.
+     You will be prompted to commit your edits at the end.
+N:   Move to the next entry.  Cache the entry in '$STATEFILE' and do not
+     prompt for it again (even across runs) until it is changed.
+ :   Move to the next entry, without adding the current one to the cache.
+     (That's a space character, ASCII 0x20.)
+
+After running a merge, you have the following options:
+
+y:   Open a shell.
+d:   View a diff.
+N:   Move to the next entry.
+
+There is also a batch mode (normally used only by cron jobs and buildbot tasks,
+rather than interactively): when \$YES and \$MAY_COMMIT are defined to '1' in
+the environment, this script will iterate the "Approved:" section, and merge
+and commit each entry therein.  If only \$YES is defined, the script will
+merge every nomination (including unapproved and vetoed ones), and complain
+to stderr if it notices any conflicts.
 
 The 'svn' binary defined by the environment variable \$SVN, or otherwise the
 'svn' found in \$PATH, will be used to manage the working copy.
 EOF
 }
 
-sub prompt {
-  local $\; # disable 'perl -l' effects
-  print "$_[0] ";
+sub nominate_usage {
+  my $basename = basename $0;
+  print <<EOF;
+nominate.pl: a tool for adding entries to STATUS.
+
+Usage: $0 "foo r42 bar r43 qux 45." "\$Some_justification"
+
+Will add:
+ * r42, r43, r45
+   (log message of r42)
+   Justification:
+     \$Some_justification
+   Votes:
+     +1: $AVAILID
+to STATUS.  Backport branches are detected automatically.
+
+The STATUS file in the current directory is used (unless argv[0] is "n", in
+which case the STATUS file in the directory of argv[0] is used; the intent
+is to create a symlink named "n" in the branch wc root).
 
-  # TODO: this part was written by trial-and-error
-  ReadMode 'cbreak';
-  my $answer = (ReadKey 0);
-  print $answer, "\n";
-  return ($answer =~ /^y/i) ? 1 : 0;
+EOF
+# TODO: Optionally add a "Notes" section.
+# TODO: Look for backport branches named after issues.
+# TODO: Do a dry-run merge on added entries.
+# TODO: Do a dry-run merge on interactively-edited entries in backport.pl
+}
+
+sub digest_string {
+  Digest->new("MD5")->add(@_)->hexdigest
+}
+
+sub digest_entry($) {
+  # Canonicalize the number of trailing EOLs to two.  This matters when there's
+  # on empty line after the last entry in Approved, for example.
+  local $_ = shift;
+  s/\n*\z// and $_ .= "\n\n";
+  digest_string($_)
 }
 
+sub prompt {
+  print $_[0]; shift;
+  my %args = @_;
+  my $getchar = sub {
+    my $answer;
+    ReadMode 'cbreak';
+    eval { $answer = (ReadKey 0) };
+    ReadMode 'normal';
+    die $@ if $@;
+    print $answer;
+    return $answer;
+  };
+
+  die "$0: called prompt() in non-interactive mode!" if $YES;
+  my $answer = $getchar->();
+  $answer .= $getchar->() if exists $args{extra} and $answer =~ $args{extra};
+  say "" unless $args{dontprint};
+  return $args{verbose}
+         ? $answer
+         : ($answer =~ /^y/i) ? 1 : 0;
+}
+
+
 sub merge {
   my %entry = @_;
+  $MERGED_SOMETHING++;
 
   my ($logmsg_fh, $logmsg_filename) = tempfile();
   my ($mergeargs, $pattern);
 
-  my $backupfile = "backport_pl.$$.tmp";
+  # Include the time so it's easier to find the interesting backups.
+  my $backupfile = strftime "backport_pl.%Y%m%d-%H%M%S.$$.tmp", localtime;
+  die if -s $backupfile;
 
   if ($entry{branch}) {
-    # NOTE: This doesn't escape the branch into the pattern.
-    $pattern = sprintf '\V\(%s branch(es)?\|branches\/%s\|Branch\(es\)\?: \*\n\? \*%s\)', $entry{branch}, $entry{branch}, $entry{branch};
+    my $vim_escaped_branch = 
+      join "",
+      map { sprintf '\[%s%s]', $_, ($_ x ($_ eq '\\')) }
+      split //,
+      $entry{branch};
+    $pattern = sprintf '\VBranch: \*\n\? \*\(\.\*\/branches\/\)\?%s',
+                 $vim_escaped_branch;
     if ($SVNvsn >= 1_008_000) {
       $mergeargs = "$BRANCHES/$entry{branch}";
-      print $logmsg_fh "Merge the $entry{header}:";
+      say $logmsg_fh "Merge $entry{header}:";
     } else {
       $mergeargs = "--reintegrate $BRANCHES/$entry{branch}";
-      print $logmsg_fh "Reintegrate the $entry{header}:";
+      say $logmsg_fh "Reintegrate $entry{header}:";
     }
-    print $logmsg_fh "";
+    say $logmsg_fh "";
   } elsif (@{$entry{revisions}}) {
     $pattern = '^ [*] \V' . 'r' . $entry{revisions}->[0];
-    $mergeargs = join " ", (map { "-c$_" } @{$entry{revisions}}), '^/subversion/trunk';
-    if (@{$entry{revisions}} > 1) {
-      print $logmsg_fh "Merge the $entry{header} from trunk:";
-      print $logmsg_fh "";
-    } else {
-      print $logmsg_fh "Merge r$entry{revisions}->[0] from trunk:";
-      print $logmsg_fh "";
-    }
+    $mergeargs = join " ",
+      ($entry{accept} ? "--accept=$entry{accept}" : ()),
+      (map { "-c$_" } @{$entry{revisions}}),
+      '--',
+      '^/subversion/trunk';
+    say $logmsg_fh
+      "Merge $entry{header} from trunk",
+      $entry{accept} ? ", with --accept=$entry{accept}" : "",
+      ":";
+    say $logmsg_fh "";
   } else {
     die "Don't know how to call $entry{header}";
   }
-  print $logmsg_fh $_ for @{$entry{entry}};
+  say $logmsg_fh $_ for @{$entry{entry}};
   close $logmsg_fh or die "Can't close $logmsg_filename: $!";
 
   my $reintegrated_word = ($SVNvsn >= 1_008_000) ? "merged" : "reintegrated";
@@ -117,9 +266,13 @@ if $DEBUG; then
   set -x
 fi
 $SVN diff > $backupfile
-cp STATUS STATUS.$$
+if ! $MAY_COMMIT ; then
+  cp STATUS STATUS.$$
+fi
 $SVNq revert -R .
-mv STATUS.$$ STATUS
+if ! $MAY_COMMIT ; then
+  mv STATUS.$$ STATUS
+fi
 $SVNq up
 $SVNq merge $mergeargs
 if [ "`$SVN status -q | wc -l`" -eq 1 ]; then
@@ -135,7 +288,7 @@ fi
 if $MAY_COMMIT; then
   $VIM -e -s -n -N -i NONE -u NONE -c '/$pattern/normal! dap' -c wq $STATUS
   $SVNq commit -F $logmsg_filename
-else
+elif test -z "\$YES"; then
   echo "Would have committed:"
   echo '[[['
   $SVN status -q
@@ -152,16 +305,22 @@ if $MAY_COMMIT; then
   if [ -n "\$YES" ]; then sleep 15; fi
   $SVNq rm $BRANCHES/$entry{branch} -m "Remove the '$entry{branch}' branch, $reintegrated_word in r\$reinteg_rev."
   if [ -n "\$YES" ]; then sleep 1; fi
-else
-  echo "Removing $reintegrated_word '$entry{branch}' branch"
+elif test -z "\$YES"; then
+  echo "Would remove $reintegrated_word '$entry{branch}' branch"
 fi
 EOF
 
-  open SHELL, '|-', qw#/bin/sh# or die $!;
+  open SHELL, '|-', qw#/bin/sh# or die "$! (in '$entry{header}')";
   print SHELL $script;
-  close SHELL or warn "$0: sh($?): $!";
+  close SHELL or warn "$0: sh($?): $! (in '$entry{header}')";
+  $ERRORS{$entry{id}} = [\%entry, "sh($?): $!"] if $?;
+
+  if (-z $backupfile) {
+    unlink $backupfile;
+  } else {
+    warn "Local mods saved to '$backupfile'\n";
+  }
 
-  unlink $backupfile if -z $backupfile;
   unlink $logmsg_filename unless $? or $!;
 }
 
@@ -173,9 +332,18 @@ sub sanitize_branch {
   return $_;
 }
 
+sub logsummarysummary {
+  my $entry = shift;
+  join "",
+    $entry->{logsummary}->[0], ('[...]' x (0 < $#{$entry->{logsummary}}))
+}
+
 # TODO: may need to parse other headers too?
 sub parse_entry {
+  my $raw = shift;
   my @lines = @_;
+  my $depends;
+  my $accept;
   my (@revisions, @logsummary, $branch, @votes);
   # @lines = @_;
 
@@ -185,70 +353,460 @@ sub parse_entry {
 
   # revisions
   $branch = sanitize_branch $1
+    and shift
     if $_[0] =~ /^(\S*) branch$/ or $_[0] =~ m#branches/(\S+)#;
   while ($_[0] =~ /^r/) {
+    my $sawrevnum = 0;
     while ($_[0] =~ s/^r(\d+)(?:$|[,; ]+)//) {
       push @revisions, $1;
+      $sawrevnum++;
     }
-    shift;
+    $sawrevnum ? shift : last;
   }
 
   # summary
-  push @logsummary, shift until $_[0] =~ /^\s*\w+:/ or not defined $_[0];
+  do {
+    push @logsummary, shift
+  } until $_[0] =~ /^\s*[][\w]+:/ or not defined $_[0];
 
   # votes
   unshift @votes, pop until $_[-1] =~ /^\s*Votes:/ or not defined $_[-1];
   pop;
 
-  # branch
+  # depends, branch, notes
+  # Ignored headers: Changes[*]
   while (@_) {
-    shift and next unless $_[0] =~ s/^\s*Branch(es)?:\s*//;
-    $branch = sanitize_branch (shift || shift || die "Branch header found without value");
+    given (shift) {
+      when (/^Depends:/) {
+        $depends++;
+      }
+      if (s/^Branch:\s*//) {
+        $branch = sanitize_branch ($_ || shift || die "Branch header found without value");
+      }
+      if (s/^Notes:\s*//) {
+        my $notes = $_;
+        $notes .= shift while @_ and $_[0] !~ /^\w/;
+        my %accepts = map { $_ => 1 } ($notes =~ /--accept[ =]([a-z-]+)/g);
+        given (scalar keys %accepts) {
+          when (0) { } 
+          when (1) { $accept = [keys %accepts]->[0]; } 
+          default  {
+            warn "Too many --accept values at '",
+                 logsummarysummary({ logsummary => [@logsummary] }),
+                 "'";
+          }
+        }
+      }
+    }
   }
 
   # Compute a header.
-  my $header;
-  $header = "r$revisions[0] group" if @revisions;
-  $header = "$branch branch" if $branch;
-  warn "No header for [@lines]" unless $header;
+  my ($header, $id);
+  if ($branch) {
+    $header = "the $branch branch";
+    $id = $branch;
+  } elsif (@revisions == 1) {
+    $header = "r$revisions[0]";
+    $id = "r$revisions[0]";
+  } elsif (@revisions) {
+    $header = "the r$revisions[0] group";
+    $id = "r$revisions[0]";
+  } else {
+    die "Entry '$raw' has neither revisions nor branch";
+  }
+  my $header_start = ($header =~ /^the/ ? ucfirst($header) : $header);
+
+  warn "Entry has both branch '$branch' and --accept=$accept specified\n"
+    if $branch and $accept;
 
   return (
     revisions => [@revisions],
     logsummary => [@logsummary],
     branch => $branch,
     header => $header,
+    header_start => $header_start,
+    depends => $depends,
+    id => $id,
     votes => [@votes],
     entry => [@lines],
+    accept => $accept,
+    raw => $raw,
+    digest => digest_entry($raw),
   );
 }
 
+sub edit_string {
+  # Edits $_[0] in an editor.
+  # $_[1] is used in error messages.
+  die "$0: called edit_string() in non-interactive mode!" if $YES;
+  my $string = shift;
+  my $name = shift;
+  my %args = @_;
+  my $trailing_eol = $args{trailing_eol};
+  my ($fh, $fn) = tempfile;
+  print $fh $string;
+  $fh->flush or die $!;
+  system("$EDITOR -- $fn") == 0
+    or warn "\$EDITOR failed editing $name: $! ($?); "
+           ."edit results ($fn) ignored.";
+  my $rv = `cat $fn`;
+  $rv =~ s/\n*\z// and $rv .= ("\n" x $trailing_eol) if defined $trailing_eol;
+  $rv;
+}
+
+sub vote {
+  my ($state, $approved, $votes) = @_;
+  # TODO: use votesarray instead of votescheck
+  my (%approvedcheck, %votescheck);
+  my $raw_approved = "";
+  my @votesarray;
+  return unless %$approved or %$votes;
+
+  my $had_empty_line;
+
+  $. = 0;
+  open STATUS, "<", $STATUS;
+  open VOTES, ">", "$STATUS.$$.tmp";
+  while (<STATUS>) {
+    $had_empty_line = /\n\n\z/;
+    my $key = digest_entry $_;
+
+    $approvedcheck{$key}++ if exists $approved->{$key};
+    $votescheck{$key}++ if exists $votes->{$key};
+
+    unless (exists $votes->{$key} or exists $approved->{$key}) {
+      print VOTES;
+      next;
+    }
+
+    unless (exists $votes->{$key}) {
+      push @votesarray, {
+        entry => $approved->{$key},
+        approval => 1,
+        digest => $key,
+      };
+      $raw_approved .= $_;
+      next;
+    }
+
+    # We have a vote, and potentially an approval.
+
+    my ($vote, $entry) = @{$votes->{$key}};
+    push @votesarray, {
+      entry => $entry,
+      vote => $vote,
+      approval => (exists $approved->{$key}),
+      digest => $key,
+    };
+
+    if ($vote eq 'edit') {
+      local $_ = $entry->{raw};
+      $votesarray[-1]->{digest} = digest_entry $_;
+      (exists $approved->{$key}) ? ($raw_approved .= $_) : (print VOTES);
+      next;
+    }
+    
+    s/^(\s*\Q$vote\E:.*)/"$1, $AVAILID"/me
+    or s/(.*\w.*?\n)/"$1     $vote: $AVAILID\n"/se;
+    $_ = edit_string $_, $entry->{header}, trailing_eol => 2
+        if $vote ne '+1';
+    $votesarray[-1]->{digest} = digest_entry $_;
+    (exists $approved->{$key}) ? ($raw_approved .= $_) : (print VOTES);
+  }
+  close STATUS;
+  print VOTES "\n" if $raw_approved and !$had_empty_line;
+  print VOTES $raw_approved;
+  close VOTES;
+  warn "Some vote chunks weren't found: ",
+    join ',',
+    map $votes->{$_}->[1]->{id},
+    grep { !$votescheck{$_} } keys %$votes
+    if scalar(keys %$votes) != scalar(keys %votescheck);
+  warn "Some approval chunks weren't found: ",
+    join ',',
+    map $approved->{$_}->{id},
+    grep { !$approvedcheck{$_} } keys %$approved
+    if scalar(keys %$approved) != scalar(keys %approvedcheck);
+  prompt "Press the 'any' key to continue...\n", dontprint => 1
+    if scalar(keys %$approved) != scalar(keys %approvedcheck) 
+    or scalar(keys %$votes) != scalar(keys %votescheck);
+  move "$STATUS.$$.tmp", $STATUS;
+
+  my $logmsg = do {
+    my @sentences = map {
+       my $words_vote = ", approving" x $_->{approval};
+       my $words_edit = " and approve" x $_->{approval};
+       exists $_->{vote}
+       ? (
+         ( $_->{vote} eq 'edit'
+           ? "Edit$words_edit the $_->{entry}->{id} entry"
+           : "Vote $_->{vote} on $_->{entry}->{header}$words_vote"
+         )
+         . "."
+         )
+      : # exists only in $approved
+        "Approve $_->{entry}->{header}."
+      } @votesarray;
+    (@sentences == 1)
+    ? $sentences[0]
+    : "* STATUS:\n" . join "", map "  $_\n", @sentences;
+  };
+
+  system "$SVN diff -- $STATUS";
+  printf "[[[\n%s%s]]]\n", $logmsg, ("\n" x ($logmsg !~ /\n\z/));
+  if (prompt "Commit these votes? ") {
+    my ($logmsg_fh, $logmsg_filename) = tempfile();
+    print $logmsg_fh $logmsg;
+    close $logmsg_fh;
+    warn("Tempfile name '$logmsg_filename' not shell-safe; "
+         ."refraining from commit.\n") and return
+        unless $logmsg_filename =~ /^([A-Z0-9._-]|\x2f)+$/i;
+    system("$SVN commit -F $logmsg_filename -- $STATUS") == 0
+        or warn("Committing the votes failed($?): $!") and return;
+    unlink $logmsg_filename;
+
+    # Add to state votes that aren't '+0' or 'edit'
+    $state->{$_->{digest}}++ for grep
+                                   +{ qw/-1 t -0 t +1 t/ }->{$_->{vote}},
+                                 @votesarray;
+  }
+}
+
+sub check_local_mods_to_STATUS {
+  if (`$SVN status -q $STATUS`) {
+    die  "Local mods to STATUS file $STATUS" if $YES;
+    warn "Local mods to STATUS file $STATUS";
+    system "$SVN diff -- $STATUS";
+    prompt "Press the 'any' key to continue...\n", dontprint => 1;
+    return 1;
+  }
+  return 0;
+}
+
+sub revert {
+  copy $STATUS, "$STATUS.$$.tmp";
+  system "$SVN revert -q $STATUS";
+  system "$SVN revert -R ./" . ($YES && $MAY_COMMIT ne 'true'
+                             ? " -q" : "");
+  move "$STATUS.$$.tmp", $STATUS;
+  $MERGED_SOMETHING = 0;
+}
+
+sub maybe_revert {
+  # This is both a SIGINT handler, and the tail end of main() in normal runs.
+  # @_ is 'INT' in the former case and () in the latter.
+  delete $SIG{INT} unless @_;
+  revert if !$YES and $MERGED_SOMETHING and prompt 'Revert? ';
+  (@_ ? exit : return);
+}
+
+sub signal_handler {
+  my $sig = shift;
+
+  # Clean up after prompt()
+  ReadMode 'normal';
+
+  # Fall back to default action
+  delete $SIG{$sig};
+  kill $sig, $$;
+}
+
+sub warning_summary {
+  return unless %ERRORS;
+
+  warn "Warning summary\n";
+  warn "===============\n";
+  warn "\n";
+  for my $id (keys %ERRORS) {
+    my $title = logsummarysummary $ERRORS{$id}->[0];
+    warn "$id ($title): $ERRORS{$id}->[1]\n";
+  }
+}
+
+sub read_state {
+  # die "$0: called read_state() in non-interactive mode!" if $YES;
+
+  open my $fh, '<', $STATEFILE or do {
+    return {} if $!{ENOENT};
+    die "Can't read statefile: $!";
+  };
+
+  my %rv;
+  while (<$fh>) {
+    chomp;
+    $rv{$_}++;
+  }
+  return \%rv;
+}
+
+sub write_state {
+  my $state = shift;
+  open STATE, '>', $STATEFILE or warn("Can't write state: $!"), return;
+  say STATE for keys %$state;
+  close STATE;
+}
+
+sub exit_stage_left {
+  my $state = shift;
+  maybe_revert;
+  warning_summary if $YES;
+  vote $state, @_;
+  write_state $state;
+  exit scalar keys %ERRORS;
+}
+
 sub handle_entry {
   my $in_approved = shift;
-  my %entry = parse_entry @_;
+  my $approved = shift;
+  my $votes = shift;
+  my $state = shift;
+  my $raw = shift;
+  my $skip = shift;
+  my %entry = parse_entry $raw, @_;
   my @vetoes = grep { /^  -1:/ } @{$entry{votes}};
 
+  my $match = defined($skip) ? ($raw =~ /\Q$skip\E/ or $raw =~ /$skip/msi) : 0
+              unless $YES;
+
   if ($YES) {
-    merge %entry if $in_approved and not @vetoes;
-  } else {
-    print "";
-    print "\n>>> The $entry{header}:";
-    print join ", ", map { "r$_" } @{$entry{revisions}};
-    print "$BRANCHES/$entry{branch}" if $entry{branch};
-    print "";
-    print for @{$entry{logsummary}};
-    print "";
-    print for @{$entry{votes}};
-    print "";
-    print "Vetoes found!" if @vetoes;
-
-    if (prompt 'Go ahead?') {
-      merge %entry;
-      system($ENV{SHELL} // "/bin/sh") == 0
-        or warn "Creating an interactive subshell failed ($?): $!"
-        if prompt "Shall I open a subshell?";
-      # Don't revert.  The next merge() call will do that anyway, or maybe the
-      # user did in his interactive shell.
+    # Run a merge if:
+    unless (@vetoes) {
+      if ($MAY_COMMIT eq 'true' and $in_approved) {
+        # svn-role mode
+        merge %entry;
+      } elsif ($MAY_COMMIT ne 'true') {
+        # Scan-for-conflicts mode
+        merge %entry;
+
+        my $output = `$SVN status`;
+        my (@conflicts) = ($output =~ m#^(?:C...|.C..|...C)...\s(.*)#mg);
+        if (@conflicts and !$entry{depends}) {
+          $ERRORS{$entry{id}} //= [\%entry,
+                                   sprintf "Conflicts on %s%s%s",
+                                     '[' x !!$#conflicts,
+                                     (join ', ',
+                                      map { basename $_ }
+                                      @conflicts),
+                                     ']' x !!$#conflicts,
+                                  ];
+          say STDERR "Conflicts merging $entry{header}!";
+          say STDERR "";
+          say STDERR $output;
+          system "$SVN diff -- @conflicts";
+        } elsif (!@conflicts and $entry{depends}) {
+          # Not a warning since svn-role may commit the dependency without
+          # also committing the dependent in the same pass.
+          print "No conflicts merging $entry{header}, but conflicts were "
+              ."expected ('Depends:' header set)\n";
+        } elsif (@conflicts) {
+          say "Conflicts found merging $entry{header}, as expected.";
+        }
+        revert;
+      }
     }
+  } elsif (defined($skip) ? not $match : $state->{$entry{digest}}) {
+    print "\n\n";
+    my $reason = defined($skip) ? "doesn't match pattern"
+                                : "remove $STATEFILE to reset";
+    say "Skipping $entry{header} ($reason):";
+    say logsummarysummary \%entry;
+  } elsif ($match or not defined $skip) {
+    # This loop is just a hack because 'goto' panics.  The goto should be where
+    # the "next PROMPT;" is; there's a "last;" at the end of the loop body.
+    PROMPT: while (1) {
+    say "";
+    say "\n>>> $entry{header_start}:";
+    say join ", ", map { "r$_" } @{$entry{revisions}} if @{$entry{revisions}};
+    say "$BRANCHES/$entry{branch}" if $entry{branch};
+    say "--accept=$entry{accept}" if $entry{accept};
+    say "";
+    say for @{$entry{logsummary}};
+    say "";
+    say for @{$entry{votes}};
+    say "";
+    say "Vetoes found!" if @vetoes;
+
+    # See above for why the while(1).
+    QUESTION: while (1) {
+    my $key = $entry{digest};
+    given (prompt 'Run a merge? [y,l,±1,±0,q,e,a, ,N] ',
+                   verbose => 1, extra => qr/[+-]/) {
+      when (/^y/i) {
+        merge %entry;
+        while (1) { 
+          given (prompt "Shall I open a subshell? [ydN] ", verbose => 1) {
+            when (/^y/i) {
+              system($SHELL) == 0
+                or warn "Creating an interactive subshell failed ($?): $!"
+            }
+            when (/^d/) {
+              system("$SVN diff | $PAGER") == 0
+                or warn "diff failed ($?): $!";
+              next;
+            }
+            when (/^N/i) {
+              # fall through.
+            }
+            default {
+              next;
+            }
+          }
+          revert;
+          next PROMPT;
+        }
+        # NOTREACHED
+      }
+      when (/^l/i) {
+        if ($entry{branch}) {
+            system "$SVN log --stop-on-copy -v -g -r 0:HEAD -- "
+                   ."$BRANCHES/$entry{branch} "
+                   ."| $PAGER";
+        } elsif (@{$entry{revisions}}) {
+            system "$SVN log ".(join ' ', map { "-r$_" } @{$entry{revisions}})
+                   ." -- ^/subversion | $PAGER";
+        } else {
+            die "Assertion failed: entry has neither branch nor revisions:\n",
+                '[[[', (join ';;', %entry), ']]]';
+        }
+        next PROMPT;
+      }
+      when (/^q/i) {
+        exit_stage_left $state, $approved, $votes;
+      }
+      when (/^a/i) {
+        $approved->{$key} = \%entry;
+        next PROMPT;
+      }
+      when (/^([+-][01])\s*$/i) {
+        $votes->{$key} = [$1, \%entry];
+        say "Your '$1' vote has been recorded." if $VERBOSE;
+        last PROMPT;
+      }
+      when (/^e/i) {
+        my $original = $entry{raw};
+        $entry{raw} = edit_string $entry{raw}, $entry{header},
+                        trailing_eol => 2;
+        $votes->{$key} = ['edit', \%entry] # marker for the 2nd pass
+            if $original ne $entry{raw};
+        last PROMPT;
+      }
+      when (/^N/i) {
+        $state->{$entry{digest}}++;
+        last PROMPT;
+      }
+      when (/^\x20/) {
+        last PROMPT; # Fall off the end of the given/when block.
+      }
+      default {
+        say "Please use one of the options in brackets (q to quit)!";
+        next QUESTION;
+      }
+    }
+    last; } # QUESTION
+    last; } # PROMPT
+  } else {
+    # NOTREACHED
+    die "Unreachable code reached.";
   }
 
   # TODO: merge() changes ./STATUS, which we're reading below, but
@@ -257,37 +815,56 @@ sub handle_entry {
   1;
 }
 
-sub main {
-  usage, exit 0 if @ARGV;
+
+sub backport_main {
+  my %approved;
+  my %votes;
+  my $state = read_state;
+
+  backport_usage, exit 0 if @ARGV > ($YES ? 0 : 1) or grep /^--help$/, @ARGV;
+  backport_usage, exit 0 if grep /^(?:-h|-\?|--help|help)$/, @ARGV;
+  my $skip = shift; # maybe undef
+  # assert not defined $skip if $YES;
 
-  open STATUS, "<", $STATUS or (usage, exit 1);
+  open STATUS, "<", $STATUS or (backport_usage, exit 1);
 
   # Because we use the ':normal' command in Vim...
-  die "A vim with the +ex_extra feature is required"
-      if `${VIM} --version` !~ /[+]ex_extra/;
+  die "A vim with the +ex_extra feature is required for \$MAY_COMMIT mode"
+      if $MAY_COMMIT eq 'true' and `${VIM} --version` !~ /[+]ex_extra/;
 
   # ### TODO: need to run 'revert' here
   # ### TODO: both here and in merge(), unlink files that previous merges added
   # When running from cron, there shouldn't be local mods.  (For interactive
   # usage, we preserve local mods to STATUS.)
-  die "Local mods to STATUS file $STATUS" if $YES and `$SVN status -q $STATUS`;
+  system("$SVN info $STATUS >/dev/null") == 0
+    or die "$0: svn error; point \$SVN to an appropriate binary";
+
+  check_local_mods_to_STATUS;
 
   # Skip most of the file
+  $/ = ""; # paragraph mode
   while (<STATUS>) {
     last if /^Status of \d+\.\d+/;
   }
-  $/ = ""; # paragraph mode
+
+  $SIG{INT} = \&maybe_revert unless $YES;
+  $SIG{TERM} = \&signal_handler unless $YES;
 
   my $in_approved = 0;
   while (<STATUS>) {
+    my $lines = $_;
     my @lines = split /\n/;
 
     given ($lines[0]) {
       # Section header
       when (/^[A-Z].*:$/i) {
-        print "\n\n=== $lines[0]" unless $YES;
+        say "\n\n=== $lines[0]" unless $YES;
         $in_approved = $lines[0] =~ /^Approved changes/;
       }
+      # Comment
+      when (/^[#\x5b]/i) {
+        next;
+      }
       # Separator after section header
       when (/^=+$/i) {
         break;
@@ -296,13 +873,112 @@ sub main {
       when (/^ \*/) {
         warn "Too many bullets in $lines[0]" and next
           if grep /^ \*/, @lines[1..$#lines];
-        handle_entry $in_approved, @lines;
+        handle_entry $in_approved, \%approved, \%votes, $state, $lines, $skip,
+                     @lines;
       }
       default {
-        warn "Unknown entry '$lines[0]' at line $.\n";
+        warn "Unknown entry '$lines[0]'";
       }
     }
   }
+
+  exit_stage_left $state, \%approved, \%votes;
 }
 
-&main
+sub nominate_main {
+  my $had_local_mods;
+
+  local $Text::Wrap::columns = 79;
+
+  $had_local_mods = check_local_mods_to_STATUS;
+
+  # Argument parsing.
+  nominate_usage, exit 0 if @ARGV != 2;
+  my (@revnums) = (+shift) =~ /(\d+)/g;
+  my $justification = shift;
+
+  @revnums = sort { $a <=> $b } keys %{{ map { $_ => 1 } @revnums }};
+  die "No revision numbers specified" unless @revnums;
+
+  # Determine whether a backport branch exists
+  my ($URL) = `$SVN info` =~ /^URL: (.*)$/m;
+  die "Can't retrieve URL of cwd" unless $URL;
+
+  die if $URL !~ m%^[A-Za-z0-9:_.+/][A-Za-z0-9:_.+/-]*$%;
+  system "$SVN info -- $URL-r$revnums[0] 2>/dev/null";
+  my $branch = ($? == 0) ? basename("$URL-r$revnums[0]") : undef;
+
+  # Construct entry.
+  my $logmsg = `$SVN propget --revprop -r $revnums[0] --strict svn:log '^/'`;
+  die "Can't fetch log message of r$revnums[0]: $!" unless $logmsg;
+  
+  unless ($logmsg =~ s/^(.*?)\n\n.*/$1/s) {
+    # "* file\n  (symbol): Log message."
+
+    # Strip before and after the first symbol's log message.
+    $logmsg =~ s/^.*?: //s;
+    $logmsg =~ s/^  \x28.*//ms;
+
+    # Undo line wrapping.  (We'll re-do it later.)
+    $logmsg =~ s/\s*\n\s+/ /g;
+  }
+
+  my @lines;
+  warn "Wrapping [$logmsg]\n";
+  push @lines, wrap " * ", ' 'x3, join ', ', map "r$_", @revnums;
+  push @lines, wrap ' 'x3, ' 'x3, split /\n/, $logmsg;
+  push @lines, "   Justification:";
+  push @lines, wrap ' 'x5, ' 'x5, $justification;
+  push @lines, "   Branch: $branch" if defined $branch;
+  push @lines, "   Votes:";
+  push @lines, "     +1: $AVAILID";
+  push @lines, "";
+  my $raw = join "", map "$_\n", @lines;
+
+  # Open the file in line-mode (not paragraph-mode).
+  my @STATUS;
+  tie @STATUS, "Tie::File", $STATUS, recsep => "\n";
+  my ($index) = grep { $STATUS[$_] =~ /^Veto/ } (0..$#STATUS);
+  die "Couldn't find where to add an entry" unless $index;
+
+  # Add an empty line if needed.
+  if ($STATUS[$index-1] =~ /\S/) {
+    splice @STATUS, $index, 0, "";
+    $index++;
+  }
+
+  # Add the entry.
+  splice @STATUS, $index, 0, @lines;
+
+  # Save.
+  untie @STATUS;
+
+  # Done!
+  system "$SVN diff -- $STATUS";
+  if (prompt "Commit this nomination? ") {
+    system "$SVN commit -m 'Nominate r$revnums[0].' -- $STATUS";
+    exit $?;
+  }
+  elsif (!$had_local_mods or prompt "Revert STATUS (destroying local mods)? ") {
+    # TODO: we could be smarter and just un-splice the lines we'd added.
+    system "$SVN revert -- $STATUS";
+    exit $?;
+  }
+
+  exit 0;
+}
+
+# Dispatch to the appropriate main().
+given (basename($0)) {
+  when (/^b$|backport/) {
+    chdir dirname $0 or die "Can't chdir: $!" if /^b$/;
+    &backport_main(@ARGV);
+  }
+  when (/^n$|nominate/) {
+    chdir dirname $0 or die "Can't chdir: $!" if /^n$/;
+    &nominate_main(@ARGV);
+  }
+  default {
+    &backport_main(@ARGV);
+  }
+}