[monotone] Add patches for building with Botan 1.10, Lua 5.2, and Texinfo >= 5.0.

Thomas Moschny thm at fedoraproject.org
Tue Oct 8 19:48:49 UTC 2013


commit 5235e919579bd021c86bc7dd1f6b208e7033db7c
Author: Thomas Moschny <thm at fedoraproject.org>
Date:   Tue Oct 8 21:44:31 2013 +0200

    Add patches for building with Botan 1.10, Lua 5.2, and Texinfo >= 5.0.

 monotone-1.0-botan-1.10.patch  |  944 ++++++++++++++++++++++++++++++++++++++++
 monotone-1.0-fix-texinfo.patch |  623 ++++++++++++++++++++++++++
 monotone-1.0-lua-5.2.patch     |  752 ++++++++++++++++++++++++++++++++
 monotone.spec                  |   18 +-
 4 files changed, 2336 insertions(+), 1 deletions(-)
---
diff --git a/monotone-1.0-botan-1.10.patch b/monotone-1.0-botan-1.10.patch
new file mode 100644
index 0000000..0da8f2f
--- /dev/null
+++ b/monotone-1.0-botan-1.10.patch
@@ -0,0 +1,944 @@
+From: Thomas Moschny <thomas.moschny at gmx.de>
+Subject: [PATCH] t/botan-1.10
+
+Botan 1.10 support.
+
+Apply patches from these upstream commits:
+
+f4feb3fdc68e4f955909450b2dcb3ff9312dbc9e
+9ff6e41adc6f40ae054fb4487f356bf69324dbdb
+8861f8555ef90a04a99404e9f8d5510b2ddb10e6
+
+---
+ src/database.cc   |  42 ++++++++++++------
+ src/gzip.cc       |  48 +++++++++++++-------
+ src/gzip.hh       |  17 ++++++-
+ src/key_packet.cc |   4 +-
+ src/key_store.cc  | 129 +++++++++++++++++++++++++++++++++++++++++++++---------
+ src/monotone.cc   |  56 +++++++++++++++++-------
+ src/packet.cc     |  22 ++++++++--
+ src/packet.hh     |  19 ++++++++
+ src/sha1.cc       |   5 ++-
+ src/ssh_agent.cc  |   6 +--
+ src/transforms.cc |  15 ++++---
+ 11 files changed, 279 insertions(+), 84 deletions(-)
+
+diff --git a/src/database.cc b/src/database.cc
+index 520fddd..c18517c 100644
+--- a/src/database.cc
++++ b/src/database.cc
+@@ -98,12 +98,15 @@ using boost::get;
+ using boost::tuple;
+ using boost::lexical_cast;
+ 
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,5)
++using Botan::PK_Encryptor_EME;
++#else
+ using Botan::PK_Encryptor;
++#endif
+ using Botan::PK_Verifier;
+ using Botan::SecureVector;
+ using Botan::X509_PublicKey;
+ using Botan::RSA_PublicKey;
+-using Botan::get_pk_encryptor;
+ 
+ int const one_row = 1;
+ int const one_col = 1;
+@@ -3425,9 +3428,8 @@ database::encrypt_rsa(key_id const & pub_id,
+   rsa_pub_key pub;
+   get_key(pub_id, pub);
+ 
+-  SecureVector<Botan::byte> pub_block;
+-  pub_block.set(reinterpret_cast<Botan::byte const *>(pub().data()),
+-                pub().size());
++  SecureVector<Botan::byte> pub_block
++    (reinterpret_cast<Botan::byte const *>(pub().data()), pub().size());
+ 
+   shared_ptr<X509_PublicKey> x509_key(Botan::X509::load_key(pub_block));
+   shared_ptr<RSA_PublicKey> pub_key
+@@ -3436,23 +3438,32 @@ database::encrypt_rsa(key_id const & pub_id,
+     throw recoverable_failure(origin::system,
+                               "Failed to get RSA encrypting key");
+ 
++  SecureVector<Botan::byte> ct;
++
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,5)
++  PK_Encryptor_EME encryptor(*pub_key, "EME1(SHA-1)");
++  ct = encryptor.encrypt(
++          reinterpret_cast<Botan::byte const *>(plaintext.data()),
++          plaintext.size(), lazy_rng::get());
++#elif BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
+   shared_ptr<PK_Encryptor>
+     encryptor(get_pk_encryptor(*pub_key, "EME1(SHA-1)"));
+ 
+-  SecureVector<Botan::byte> ct;
+-
+-#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
+   ct = encryptor->encrypt(
+           reinterpret_cast<Botan::byte const *>(plaintext.data()),
+           plaintext.size(), lazy_rng::get());
+ #else
++  shared_ptr<PK_Encryptor>
++    encryptor(Botan::get_pk_encryptor(*pub_key, "EME1(SHA-1)"));
++
+   ct = encryptor->encrypt(
+           reinterpret_cast<Botan::byte const *>(plaintext.data()),
+           plaintext.size());
+ #endif
+-  ciphertext = rsa_oaep_sha_data(string(reinterpret_cast<char const *>(ct.begin()),
+-                                        ct.size()),
+-                                 origin::database);
++
++  ciphertext = rsa_oaep_sha_data(
++    string(reinterpret_cast<char const *>(ct.begin()), ct.size()),
++    origin::database);
+ }
+ 
+ cert_status
+@@ -3471,14 +3482,13 @@ database::check_signature(key_id const & id,
+   else
+     {
+       rsa_pub_key pub;
+-      SecureVector<Botan::byte> pub_block;
+ 
+       if (!public_key_exists(id))
+         return cert_unknown;
+ 
+       get_key(id, pub);
+-      pub_block.set(reinterpret_cast<Botan::byte const *>(pub().data()),
+-                    pub().size());
++      SecureVector<Botan::byte> pub_block
++        (reinterpret_cast<Botan::byte const *>(pub().data()), pub().size());
+ 
+       L(FL("building verifier for %d-byte pub key") % pub_block.size());
+       shared_ptr<X509_PublicKey> x509_key(Botan::X509::load_key(pub_block));
+@@ -3488,7 +3498,11 @@ database::check_signature(key_id const & id,
+       E(pub_key, id.inner().made_from,
+         F("Failed to get RSA verifying key for %s") % id);
+ 
+-      verifier.reset(get_pk_verifier(*pub_key, "EMSA3(SHA-1)"));
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,10,0)
++      verifier.reset(new Botan::PK_Verifier(*pub_key, "EMSA3(SHA1)"));
++#else
++      verifier.reset(Botan::get_pk_verifier(*pub_key, "EMSA3(SHA-1)"));
++#endif
+ 
+       /* XXX This is ugly. We need to keep the key around
+        * as long as the verifier is around, but the shared_ptr will go
+diff --git a/src/gzip.cc b/src/gzip.cc
+index b74af6d..2cfea09 100644
+--- a/src/gzip.cc
++++ b/src/gzip.cc
+@@ -110,7 +110,7 @@ Gzip_Compression::Gzip_Compression(u32bit l) :
+    if(deflateInit2(&(zlib->stream), level, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) != Z_OK)
+       {
+       delete zlib; zlib = 0;
+-      throw Exception("Gzip_Compression: Memory allocation error");
++      throw Memory_Exhaustion();
+       }
+    }
+ 
+@@ -137,7 +137,7 @@ void Gzip_Compression::start_msg()
+ /*************************************************
+ * Compress Input with Gzip                       *
+ *************************************************/
+-void Gzip_Compression::write(const byte input[], u32bit length)
++void Gzip_Compression::write(const byte input[], filter_length_t length)
+    {
+ 
+    count += length;
+@@ -152,7 +152,7 @@ void Gzip_Compression::write(const byte input[], u32bit length)
+       zlib->stream.avail_out = buffer.size();
+       int rc = deflate(&(zlib->stream), Z_NO_FLUSH);
+       if (rc != Z_OK && rc != Z_STREAM_END)
+-         throw Exception("Internal error in Gzip_Compression deflate.");
++         throw Invalid_State("Internal error in Gzip_Compression deflate.");
+       send(buffer.begin(), buffer.size() - zlib->stream.avail_out);
+       }
+    }
+@@ -172,7 +172,7 @@ void Gzip_Compression::end_msg()
+       zlib->stream.avail_out = buffer.size();
+       rc = deflate(&(zlib->stream), Z_FINISH);
+       if (rc != Z_OK && rc != Z_STREAM_END)
+-         throw Exception("Internal error in Gzip_Compression finishing deflate.");
++         throw Invalid_State("Internal error in Gzip_Compression finishing deflate.");
+       send(buffer.begin(), buffer.size() - zlib->stream.avail_out);
+       }
+ 
+@@ -228,7 +228,7 @@ Gzip_Decompression::Gzip_Decompression() : buffer(DEFAULT_BUFFERSIZE),
+    no_writes(true), pipe(new Hash_Filter("CRC32")), footer(0)
+    {
+    if (DEFAULT_BUFFERSIZE < sizeof(GZIP::GZIP_HEADER))
+-      throw Exception("DEFAULT_BUFFERSIZE is too small");
++      throw Decoding_Error("DEFAULT_BUFFERSIZE is too small");
+ 
+    zlib = new Zlib_Stream;
+ 
+@@ -237,7 +237,7 @@ Gzip_Decompression::Gzip_Decompression() : buffer(DEFAULT_BUFFERSIZE),
+    if(inflateInit2(&(zlib->stream), -15) != Z_OK)
+       {
+       delete zlib; zlib = 0;
+-      throw Exception("Gzip_Decompression: Memory allocation error");
++      throw Memory_Exhaustion();
+       }
+    }
+ 
+@@ -256,7 +256,7 @@ Gzip_Decompression::~Gzip_Decompression()
+ void Gzip_Decompression::start_msg()
+    {
+    if (!no_writes)
+-      throw Exception("Gzip_Decompression: start_msg after already writing");
++      throw Decoding_Error("Gzip_Decompression: start_msg after already writing");
+ 
+    pipe.start_msg();
+    datacount = 0;
+@@ -267,7 +267,7 @@ void Gzip_Decompression::start_msg()
+ /*************************************************
+ * Decompress Input with Gzip                     *
+ *************************************************/
+-void Gzip_Decompression::write(const byte input[], u32bit length)
++void Gzip_Decompression::write(const byte input[], filter_length_t length)
+    {
+    if(length) no_writes = false;
+ 
+@@ -277,15 +277,16 @@ void Gzip_Decompression::write(const byte input[], u32bit length)
+          u32bit eat_len = eat_footer(input, length);
+          input += eat_len;
+          length -= eat_len;
+-         if (length == 0)
+-            return;
+       }
+ 
++   if (length == 0)
++     return;
++
+    // Check the gzip header
+    if (pos < sizeof(GZIP::GZIP_HEADER))
+       {
+-      u32bit len = std::min((u32bit)sizeof(GZIP::GZIP_HEADER)-pos, length);
+-      u32bit cmplen = len;
++      filter_length_t len = std::min((filter_length_t)sizeof(GZIP::GZIP_HEADER)-pos, length);
++      filter_length_t cmplen = len;
+       // The last byte is the OS flag - we don't care about that
+       if (pos + len - 1 >= GZIP::HEADER_POS_OS)
+          cmplen--;
+@@ -317,8 +318,8 @@ void Gzip_Decompression::write(const byte input[], u32bit length)
+          if(rc == Z_NEED_DICT)
+             throw Decoding_Error("Gzip_Decompression: Need preset dictionary");
+          if(rc == Z_MEM_ERROR)
+-            throw Exception("Gzip_Decompression: Memory allocation error");
+-         throw Exception("Gzip_Decompression: Unknown decompress error");
++            throw Memory_Exhaustion();
++         throw Decoding_Error("Gzip_Decompression: Unknown decompress error");
+          }
+       send(buffer.begin(), buffer.size() - zlib->stream.avail_out);
+       pipe.write(buffer.begin(), buffer.size() - zlib->stream.avail_out);
+@@ -346,8 +347,14 @@ u32bit Gzip_Decompression::eat_footer(const byte input[], u32bit length)
+       if (footer.size() >= GZIP::FOOTER_LENGTH)
+          throw Decoding_Error("Gzip_Decompression: Data integrity error in footer");
+ 
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,11)
++      size_t eat_len = std::min(GZIP::FOOTER_LENGTH-footer.size(),
++                                static_cast<size_t>(length));
++      footer += std::make_pair(input, eat_len);
++#else
+       u32bit eat_len = std::min(GZIP::FOOTER_LENGTH-footer.size(), length);
+       footer.append(input, eat_len);
++#endif
+ 
+       if (footer.size() == GZIP::FOOTER_LENGTH)
+          {
+@@ -364,7 +371,7 @@ u32bit Gzip_Decompression::eat_footer(const byte input[], u32bit length)
+ void Gzip_Decompression::check_footer()
+    {
+    if (footer.size() != GZIP::FOOTER_LENGTH)
+-      throw Exception("Gzip_Decompression: Error finalizing decompression");
++      throw Decoding_Error("Gzip_Decompression: Error finalizing decompression");
+ 
+    pipe.end_msg();
+ 
+@@ -377,7 +384,12 @@ void Gzip_Decompression::check_footer()
+   for (int i = 0; i < 4; i++)
+      buf[3-i] = tmpbuf[i];
+ 
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,11)
++  tmpbuf.resize(4);
++  tmpbuf.copy(footer.begin(), 4);
++#else
+   tmpbuf.set(footer.begin(), 4);
++#endif
+   if (buf != tmpbuf)
+       throw Decoding_Error("Gzip_Decompression: Data integrity error - CRC32 error");
+ 
+@@ -400,7 +412,7 @@ void Gzip_Decompression::end_msg()
+    // read, clear() will reset no_writes
+    if(no_writes) return;
+ 
+-   throw Exception("Gzip_Decompression: didn't find footer");
++   throw Decoding_Error("Gzip_Decompression: didn't find footer");
+ 
+    }
+ 
+@@ -412,7 +424,11 @@ void Gzip_Decompression::clear()
+    no_writes = true;
+    inflateReset(&(zlib->stream));
+ 
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,11)
++   footer.clear();
++#else
+    footer.destroy();
++#endif
+    pos = 0;
+    datacount = 0;
+    }
+diff --git a/src/gzip.hh b/src/gzip.hh
+index f3ea628..5c25fc9 100644
+--- a/src/gzip.hh
++++ b/src/gzip.hh
+@@ -7,11 +7,18 @@
+ #ifndef BOTAN_EXT_GZIP_H__
+ #define BOTAN_EXT_GZIP_H__
+ 
++#include <botan/version.h>
+ #include <botan/filter.h>
+ #include <botan/pipe.h>
+ 
+ namespace Botan {
+ 
++#if BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(1,9,4)
++// Only 1.9.4 and newer export the Memory_Exception. Give this gzip
++// implementation something compatible to work with.
++typedef std::bad_alloc Memory_Exhaustion;
++#endif
++
+ namespace GZIP {
+ 
+    /* A basic header - we only need to set the IDs and compression method */
+@@ -30,13 +37,19 @@ namespace GZIP {
+ 
+ }
+ 
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,11)
++typedef size_t filter_length_t;
++#else
++typedef u32bit filter_length_t;
++#endif
++
+ /*************************************************
+ * Gzip Compression Filter                        *
+ *************************************************/
+ class Gzip_Compression : public Filter
+    {
+    public:
+-      void write(const byte input[], u32bit length);
++      void write(const byte input[], filter_length_t length);
+       void start_msg();
+       void end_msg();
+       std::string name() const { return "Gzip_Compression"; }
+@@ -60,7 +73,7 @@ class Gzip_Compression : public Filter
+ class Gzip_Decompression : public Filter
+    {
+    public:
+-      void write(const byte input[], u32bit length);
++      void write(const byte input[], filter_length_t length);
+       void start_msg();
+       void end_msg();
+       std::string name() const { return "Gzip_Decompression"; }
+diff --git a/src/key_packet.cc b/src/key_packet.cc
+index ccd0e06..3a56c69 100644
+--- a/src/key_packet.cc
++++ b/src/key_packet.cc
+@@ -106,8 +106,8 @@ namespace
+     void validate_public_key_data(string const & name, string const & keydata) const
+     {
+       string decoded = decode_base64_as<string>(keydata, origin::user);
+-      Botan::SecureVector<Botan::byte> key_block;
+-      key_block.set(reinterpret_cast<Botan::byte const *>(decoded.c_str()), decoded.size());
++      Botan::SecureVector<Botan::byte> key_block
++        (reinterpret_cast<Botan::byte const *>(decoded.c_str()), decoded.size());
+       try
+         {
+           Botan::X509::load_key(key_block);
+diff --git a/src/key_store.cc b/src/key_store.cc
+index ac8b8bb..a0c6494 100644
+--- a/src/key_store.cc
++++ b/src/key_store.cc
+@@ -55,6 +55,7 @@ using Botan::PK_Signer;
+ using Botan::Pipe;
+ using Botan::get_pk_decryptor;
+ using Botan::get_cipher;
++using Botan::byte;
+ 
+ 
+ typedef pair<key_name, keypair> key_info;
+@@ -572,13 +573,21 @@ key_store_state::decrypt_private_key(key_id const & id,
+   try // with empty passphrase
+     {
+       Botan::DataSource_Memory ds(kp.priv());
+-#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,11)
++      pkcs8_key.reset(Botan::PKCS8::load_key(ds, lazy_rng::get(), Dummy_UI()));
++#elif BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
+       pkcs8_key.reset(Botan::PKCS8::load_key(ds, lazy_rng::get(), ""));
+ #else
+       pkcs8_key.reset(Botan::PKCS8::load_key(ds, ""));
+ #endif
+     }
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,11)
++  catch (Passphrase_Required & e)
++#elif BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,4)
++  catch (Botan::Invalid_Argument & e)
++#else
+   catch (Botan::Exception & e)
++#endif
+     {
+       L(FL("failed to load key with no passphrase: %s") % e.what());
+ 
+@@ -605,13 +614,18 @@ key_store_state::decrypt_private_key(key_id const & id,
+           {
+             Botan::DataSource_Memory ds(kp.priv());
+ #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
+-            pkcs8_key.reset(Botan::PKCS8::load_key(ds, lazy_rng::get(), phrase()));
++            pkcs8_key.reset(Botan::PKCS8::load_key(ds, lazy_rng::get(),
++                                                   phrase()));
+ #else
+             pkcs8_key.reset(Botan::PKCS8::load_key(ds, phrase()));
+ #endif
+             break;
+           }
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,4)
++        catch (Botan::Invalid_Argument)
++#else
+         catch (Botan::Exception & e)
++#endif
+           {
+             cycles++;
+             L(FL("decrypt_private_key: failure %d to load encrypted key: %s")
+@@ -697,9 +711,18 @@ key_store::create_key_pair(database & db,
+ 
+   // serialize and maybe encrypt the private key
+   keypair kp;
+-  SecureVector<Botan::byte> pubkey, privkey;
++  SecureVector<byte> pubkey, privkey;
+ 
+   unfiltered_pipe->start_msg();
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,10,0)
++  if ((*maybe_passphrase)().length())
++    unfiltered_pipe->write(
++      Botan::PKCS8::BER_encode(priv, lazy_rng::get(),
++                               (*maybe_passphrase)(),
++                               "PBE-PKCS5v20(SHA-1,TripleDES/CBC)"));
++  else
++    unfiltered_pipe->write(Botan::PKCS8::PEM_encode(priv));
++#else
+   if ((*maybe_passphrase)().length())
+     Botan::PKCS8::encrypt_key(priv, *unfiltered_pipe,
+ #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
+@@ -710,13 +733,20 @@ key_store::create_key_pair(database & db,
+                               Botan::RAW_BER);
+   else
+     Botan::PKCS8::encode(priv, *unfiltered_pipe);
++#endif
+   unfiltered_pipe->end_msg();
+-  kp.priv = rsa_priv_key(unfiltered_pipe->read_all_as_string(Pipe::LAST_MESSAGE),
+-                         origin::internal);
++
++  kp.priv = rsa_priv_key(
++    unfiltered_pipe->read_all_as_string(Pipe::LAST_MESSAGE),
++    origin::internal);
+ 
+   // serialize the public key
+   unfiltered_pipe->start_msg();
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,10,0)
++  unfiltered_pipe->write(Botan::X509::BER_encode(priv));
++#else
+   Botan::X509::encode(priv, *unfiltered_pipe, Botan::RAW_BER);
++#endif
+   unfiltered_pipe->end_msg();
+   kp.pub = rsa_pub_key(unfiltered_pipe->read_all_as_string(Pipe::LAST_MESSAGE),
+                        origin::internal);
+@@ -783,6 +813,15 @@ key_store::change_key_passphrase(key_id const & id)
+   get_passphrase(new_phrase, name, id, true, false);
+ 
+   unfiltered_pipe->start_msg();
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,10,0)
++  if (new_phrase().length())
++    unfiltered_pipe->write(
++      Botan::PKCS8::BER_encode(*priv, lazy_rng::get(),
++                               new_phrase(),
++                               "PBE-PKCS5v20(SHA-1,TripleDES/CBC)"));
++  else
++    unfiltered_pipe->write(Botan::PKCS8::PEM_encode(*priv));
++#else
+   if (new_phrase().length())
+     Botan::PKCS8::encrypt_key(*priv, *unfiltered_pipe,
+ #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
+@@ -793,6 +832,7 @@ key_store::change_key_passphrase(key_id const & id)
+                               Botan::RAW_BER);
+   else
+     Botan::PKCS8::encode(*priv, *unfiltered_pipe);
++#endif
+ 
+   unfiltered_pipe->end_msg();
+   kp.priv = rsa_priv_key(unfiltered_pipe->read_all_as_string(Pipe::LAST_MESSAGE),
+@@ -813,19 +853,32 @@ key_store::decrypt_rsa(key_id const & id,
+       load_key_pair(*this, id, kp);
+       shared_ptr<RSA_PrivateKey> priv_key = s->decrypt_private_key(id);
+ 
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,10,0)
++      Botan::PK_Decryptor_EME decryptor(*priv_key, "EME1(SHA-1)");
++
++      SecureVector<byte> plain =
++        decryptor.decrypt(reinterpret_cast<byte const *>(ciphertext().data()),
++                          ciphertext().size());
++      plaintext = string(plain.begin(), plain.end());
++#else
+       shared_ptr<PK_Decryptor>
+-        decryptor(get_pk_decryptor(*priv_key, "EME1(SHA-1)"));
++        decryptor(Botan::get_pk_decryptor(*priv_key, "EME1(SHA-1)"));
+ 
+-      SecureVector<Botan::byte> plain =
+-        decryptor->decrypt(reinterpret_cast<Botan::byte const *>(ciphertext().data()),
++      SecureVector<byte> plain =
++        decryptor->decrypt(reinterpret_cast<byte const *>(ciphertext().data()),
+                            ciphertext().size());
+       plaintext = string(reinterpret_cast<char const*>(plain.begin()),
+                          plain.size());
++#endif
+     }
+-  catch (Botan::Exception & ex)
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,4)
++  catch (std::exception & e)
++#else
++  catch (Botan::Exception & e)
++#endif
+     {
+       E(false, ciphertext.made_from,
+-        F("Botan error decrypting data: '%s'") % ex.what());
++        F("Botan error decrypting data: '%s'") % e.what());
+     }
+ }
+ 
+@@ -856,9 +909,9 @@ key_store::make_signature(database & db,
+     {
+       if (agent.connected()) {
+         //grab the monotone public key as an RSA_PublicKey
+-        SecureVector<Botan::byte> pub_block;
+-        pub_block.set(reinterpret_cast<Botan::byte const *>(key.pub().data()),
+-                      key.pub().size());
++        SecureVector<byte> pub_block
++          (reinterpret_cast<byte const *>(key.pub().data()),
++           key.pub().size());
+         L(FL("make_signature: building %d-byte pub key") % pub_block.size());
+         shared_ptr<X509_PublicKey> x509_key =
+           shared_ptr<X509_PublicKey>(Botan::X509::load_key(pub_block));
+@@ -884,7 +937,7 @@ key_store::make_signature(database & db,
+       || s->ssh_sign_mode == "check"
+       || s->ssh_sign_mode == "no")
+     {
+-      SecureVector<Botan::byte> sig;
++      SecureVector<byte> sig;
+ 
+       // we permit the user to relax security here, by caching a decrypted key
+       // (if they permit it) through the life of a program run. this helps when
+@@ -908,7 +961,13 @@ key_store::make_signature(database & db,
+             L(FL("make_signature: adding private key (%s) to ssh-agent") % id);
+             agent.add_identity(*priv_key, name());
+           }
+-          signer = shared_ptr<PK_Signer>(get_pk_signer(*priv_key, "EMSA3(SHA-1)"));
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,10,0)
++          signer = shared_ptr<PK_Signer>(
++                     new PK_Signer(*priv_key, "EMSA3(SHA-1)"));
++#else
++          signer = shared_ptr<PK_Signer>(
++                     get_pk_signer(*priv_key, "EMSA3(SHA-1)"));
++#endif
+ 
+           /* If persist_phrase is true, the RSA_PrivateKey object is
+              cached in s->active_keys and will survive as long as the
+@@ -919,11 +978,11 @@ key_store::make_signature(database & db,
+ 
+ #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
+       sig = signer->sign_message(
+-        reinterpret_cast<Botan::byte const *>(tosign.data()),
++        reinterpret_cast<byte const *>(tosign.data()),
+         tosign.size(), lazy_rng::get());
+ #else
+       sig = signer->sign_message(
+-        reinterpret_cast<Botan::byte const *>(tosign.data()),
++        reinterpret_cast<byte const *>(tosign.data()),
+         tosign.size());
+ #endif
+       sig_string = string(reinterpret_cast<char const*>(sig.begin()), sig.size());
+@@ -988,6 +1047,15 @@ key_store::export_key_for_agent(key_id const & id,
+   // This pipe cannot sensibly be recycled.
+   Pipe p(new Botan::DataSink_Stream(os));
+   p.start_msg();
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,10,0)
++  if (new_phrase().length())
++    p.write(Botan::PKCS8::PEM_encode(*priv,
++                                     lazy_rng::get(),
++                                     new_phrase(),
++                                     "PBE-PKCS5v20(SHA-1,TripleDES/CBC)"));
++  else
++    p.write(Botan::PKCS8::PEM_encode(*priv));
++#else
+   if (new_phrase().length())
+     Botan::PKCS8::encrypt_key(*priv,
+                               p,
+@@ -998,6 +1066,7 @@ key_store::export_key_for_agent(key_id const & id,
+                               "PBE-PKCS5v20(SHA-1,TripleDES/CBC)");
+   else
+     Botan::PKCS8::encode(*priv, p);
++#endif
+   p.end_msg();
+ }
+ 
+@@ -1013,7 +1082,7 @@ key_store_state::migrate_old_key_pair
+      rsa_pub_key const & pub)
+ {
+   keypair kp;
+-  SecureVector<Botan::byte> arc4_key;
++  SecureVector<byte> arc4_key;
+   utf8 phrase;
+   shared_ptr<PKCS8_PrivateKey> pkcs8_key;
+   shared_ptr<RSA_PrivateKey> priv_key;
+@@ -1031,8 +1100,14 @@ key_store_state::migrate_old_key_pair
+   for (;;)
+     try
+       {
+-        arc4_key.set(reinterpret_cast<Botan::byte const *>(phrase().data()),
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,11)
++        arc4_key.resize(phrase().size());
++        arc4_key.copy(reinterpret_cast<byte const *>(phrase().data()),
++                      phrase().size());
++#else
++        arc4_key.set(reinterpret_cast<byte const *>(phrase().data()),
+                      phrase().size());
++#endif
+ 
+         Pipe arc4_decryptor(get_cipher("ARC4", arc4_key, Botan::DECRYPTION));
+ 
+@@ -1041,7 +1116,7 @@ key_store_state::migrate_old_key_pair
+         // This is necessary because PKCS8::load_key() cannot currently
+         // recognize an unencrypted, raw-BER blob as such, but gets it
+         // right if it's PEM-coded.
+-        SecureVector<Botan::byte> arc4_decrypt(arc4_decryptor.read_all());
++        SecureVector<byte> arc4_decrypt(arc4_decryptor.read_all());
+         Botan::DataSource_Memory ds(Botan::PEM_Code::encode(arc4_decrypt,
+                                                             "PRIVATE KEY"));
+ #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
+@@ -1051,7 +1126,11 @@ key_store_state::migrate_old_key_pair
+ #endif
+         break;
+       }
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,4)
++    catch (Botan::Invalid_Argument & e)
++#else
+     catch (Botan::Exception & e)
++#endif
+       {
+         L(FL("migrate_old_key_pair: failure %d to load old private key: %s")
+           % cycles % e.what());
+@@ -1070,6 +1149,11 @@ key_store_state::migrate_old_key_pair
+ 
+   // now we can write out the new key
+   unfiltered_pipe->start_msg();
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,10,0)
++  unfiltered_pipe->write(Botan::PKCS8::BER_encode(
++    *priv_key, lazy_rng::get(), phrase(),
++    "PBE-PKCS5v20(SHA-1,TripleDES/CBC)"));
++#else
+   Botan::PKCS8::encrypt_key(*priv_key, *unfiltered_pipe,
+ #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
+                             lazy_rng::get(),
+@@ -1077,6 +1161,7 @@ key_store_state::migrate_old_key_pair
+                             phrase(),
+                             "PBE-PKCS5v20(SHA-1,TripleDES/CBC)",
+                             Botan::RAW_BER);
++#endif
+   unfiltered_pipe->end_msg();
+   kp.priv = rsa_priv_key(unfiltered_pipe->read_all_as_string(Pipe::LAST_MESSAGE),
+                          origin::internal);
+@@ -1085,7 +1170,11 @@ key_store_state::migrate_old_key_pair
+   // Botan for the X.509 encoding of the private key implies that we want
+   // it to derive and produce the public key)
+   unfiltered_pipe->start_msg();
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,10,0)
++  unfiltered_pipe->write(Botan::X509::BER_encode(*priv_key));
++#else
+   Botan::X509::encode(*priv_key, *unfiltered_pipe, Botan::RAW_BER);
++#endif
+   unfiltered_pipe->end_msg();
+   kp.pub = rsa_pub_key(unfiltered_pipe->read_all_as_string(Pipe::LAST_MESSAGE),
+                        origin::internal);
+diff --git a/src/monotone.cc b/src/monotone.cc
+index 281e940..f2c844a 100644
+--- a/src/monotone.cc
++++ b/src/monotone.cc
+@@ -156,27 +156,53 @@ cpp_main(int argc, char ** argv)
+       E(linked_botan_version != BOTAN_VERSION_CODE_FOR(1,7,14), origin::system,
+         F("monotone does not support Botan 1.7.14"));
+ 
+-#if BOTAN_VERSION_CODE <= BOTAN_VERSION_CODE_FOR(1,7,6)
++      // In Botan 1.9.9, the DataSink_Stream cannot be instantiated per
++      // se. As 1.10.1 is already out, let's simply disable support for
++      // that specific (testing) version of botan.
++      E(linked_botan_version != BOTAN_VERSION_CODE_FOR(1,9,9), origin::system,
++        F("monotone does not support Botan 1.9.9"));
++
++#if BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(1,7,7)
++      // motonote binary compiled against botan younger than 1.7.7
+       E(linked_botan_version >= BOTAN_VERSION_CODE_FOR(1,6,3), origin::system,
+         F("this monotone binary requires Botan 1.6.3 or newer"));
+-      E(linked_botan_version <= BOTAN_VERSION_CODE_FOR(1,7,6), origin::system,
+-        F("this monotone binary does not work with Botan newer than 1.7.6"));
+-#elif BOTAN_VERSION_CODE <= BOTAN_VERSION_CODE_FOR(1,7,22)
+-      E(linked_botan_version > BOTAN_VERSION_CODE_FOR(1,7,6), origin::system,
++      E(linked_botan_version < BOTAN_VERSION_CODE_FOR(1,7,7), origin::system,
++        F("this monotone binary does not work with Botan 1.7.7 or newer"));
++
++#elif BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(1,7,22)
++      // motonote binary compiled against botan 1.7.7 - 1.7.21
++      E(linked_botan_version >= BOTAN_VERSION_CODE_FOR(1,7,7), origin::system,
+         F("this monotone binary requires Botan 1.7.7 or newer"));
+-      // While compiling against 1.7.22 or newer is recommended, because
+-      // it enables new features of Botan, the monotone binary compiled
+-      // against Botan 1.7.21 and before should still work with newer Botan
+-      // versions, including all of the stable branch 1.8.x.
+-      E(linked_botan_version < BOTAN_VERSION_CODE_FOR(1,9,0), origin::system,
+-        F("this monotone binary does not work with Botan 1.9.x"));
+-#else
+-      E(linked_botan_version > BOTAN_VERSION_CODE_FOR(1,7,22), origin::system,
++      // While compiling against 1.7.22 or newer is recommended, because it
++      // enables new features of Botan, the monotone binary compiled against
++      // Botan 1.7.21 and before should still work with newer Botan version,
++      // including all of the stable branch 1.8.x, up to and including
++      // 1.9.3.
++      E(linked_botan_version < BOTAN_VERSION_CODE_FOR(1,9,4), origin::system,
++        F("this monotone binary does not work with Botan 1.9.4 or newer"));
++
++#elif BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(1,9,4)
++      // motonote binary compiled against botan 1.7.22 - 1.9.3
++      E(linked_botan_version >= BOTAN_VERSION_CODE_FOR(1,7,22), origin::system,
+         F("this monotone binary requires Botan 1.7.22 or newer"));
+-      E(linked_botan_version < BOTAN_VERSION_CODE_FOR(1,9,0), origin::system,
+-        F("this monotone binary does not work with Botan 1.9.x"));
++      E(linked_botan_version < BOTAN_VERSION_CODE_FOR(1,9,4), origin::system,
++        F("this monotone binary does not work with Botan 1.9.4 or newer"));
++
++#elif BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(1,9,11)
++      // motonote binary compiled against botan 1.9.4 - 1.9.10
++#pragma message ( "The resulting monotone binary won't be able to run with any stable release of botan." )
++      E(linked_botan_version >= BOTAN_VERSION_CODE_FOR(1,9,4), origin::system,
++        F("this monotone binary requires Botan 1.9.4 or newer"));
++      E(linked_botan_version < BOTAN_VERSION_CODE_FOR(1,9,11), origin::system,
++        F("this monotone binary does not work with Botan 1.9.11 or newer"));
++
++#else
++      // motonote binary compiled against botan 1.9.11 and newer
++      E(linked_botan_version >= BOTAN_VERSION_CODE_FOR(1,9,11), origin::system,
++        F("this monotone binary requires Botan 1.9.11 or newer"));
+ #endif
+ 
++
+       app_state app;
+       try
+         {
+diff --git a/src/packet.cc b/src/packet.cc
+index 87ff24a..f592d50 100644
+--- a/src/packet.cc
++++ b/src/packet.cc
+@@ -156,8 +156,8 @@ namespace
+     void validate_public_key_data(string const & name, string const & keydata) const
+     {
+       string decoded = decode_base64_as<string>(keydata, origin::user);
+-      Botan::SecureVector<Botan::byte> key_block;
+-      key_block.set(reinterpret_cast<Botan::byte const *>(decoded.c_str()), decoded.size());
++      Botan::SecureVector<Botan::byte> key_block
++        (reinterpret_cast<Botan::byte const *>(decoded.c_str()), decoded.size());
+       try
+         {
+           Botan::X509::load_key(key_block);
+@@ -175,7 +175,9 @@ namespace
+       Botan::DataSource_Memory ds(decoded);
+       try
+         {
+-#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,11)
++          Botan::PKCS8::load_key(ds, lazy_rng::get(), Dummy_UI());
++#elif BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
+           Botan::PKCS8::load_key(ds, lazy_rng::get(), string());
+ #else
+           Botan::PKCS8::load_key(ds, string());
+@@ -189,7 +191,11 @@ namespace
+         }
+       // since we do not want to prompt for a password to decode it finally,
+       // we ignore all other exceptions
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,11)
++      catch (Passphrase_Required) {}
++#else
+       catch (Botan::Invalid_Argument) {}
++#endif
+     }
+     void validate_certname(string const & cn) const
+     {
+@@ -460,7 +466,15 @@ read_packets(istream & in, packet_consumer & cons)
+   return count;
+ }
+ 
+-
++// Dummy User_Interface implementation for Botan
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,11)
++std::string
++Dummy_UI::get_passphrase(const std::string &, const std::string &,
++                         Botan::User_Interface::UI_Result&) const
++{
++  throw Passphrase_Required("Passphrase required");
++}
++#endif
+ 
+ // Local Variables:
+ // mode: C++
+diff --git a/src/packet.hh b/src/packet.hh
+index 239f996..3d5c1b5 100644
+--- a/src/packet.hh
++++ b/src/packet.hh
+@@ -10,6 +10,10 @@
+ #ifndef __PACKET_HH__
+ #define __PACKET_HH__
+ 
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,11)
++#include <botan/ui.h>
++#endif
++
+ #include "vocab.hh"
+ 
+ struct cert;
+@@ -84,6 +88,21 @@ struct packet_writer : public packet_consumer
+ 
+ size_t read_packets(std::istream & in, packet_consumer & cons);
+ 
++#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,11)
++// A helper class implementing Botan::User_Interface - which doesn't really
++// interface with the user, but provides the necessary plumbing for Botan.
++//
++// See Botan commit 2d09d7d0cd4bd0e7155d001dd65a4f29103b158c
++typedef std::runtime_error Passphrase_Required;
++
++class Dummy_UI : public Botan::User_Interface
++{
++public:
++  virtual std::string get_passphrase(const std::string &, const std::string &,
++                                     Botan::User_Interface::UI_Result &) const;
++};
++#endif
++
+ #endif
+ 
+ // Local Variables:
+diff --git a/src/sha1.cc b/src/sha1.cc
+index d4d7232..5f47f90 100644
+--- a/src/sha1.cc
++++ b/src/sha1.cc
+@@ -50,9 +50,12 @@ CMD_HIDDEN(benchmark_sha1, "benchmark_sha1", "", CMD_REF(debug), "",
+   Botan::Default_Benchmark_Timer timer;
+   std::map<std::string, double> results =
+     Botan::algorithm_benchmark("SHA-1",  milliseconds, timer, rng, af);
+-#else
++#elif BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(1,9,11)
+   std::map<std::string, double> results =
+     Botan::algorithm_benchmark("SHA-1",  milliseconds, rng, af);
++#else
++  std::map<std::string, double> results =
++    Botan::algorithm_benchmark("SHA-1",  af, rng, milliseconds, 16);
+ #endif
+ 
+   for(std::map<std::string, double>::const_iterator i = results.begin();
+diff --git a/src/ssh_agent.cc b/src/ssh_agent.cc
+index 157f59a..051e2f7 100644
+--- a/src/ssh_agent.cc
++++ b/src/ssh_agent.cc
+@@ -385,9 +385,9 @@ bool
+ ssh_agent::has_key(const keypair & key)
+ {
+   //grab the monotone public key as an RSA_PublicKey
+-  SecureVector<Botan::byte> pub_block;
+-  pub_block.set(reinterpret_cast<Botan::byte const *>((key.pub)().data()),
+-                (key.pub)().size());
++  SecureVector<Botan::byte> pub_block
++    (reinterpret_cast<Botan::byte const *>((key.pub)().data()),
++     (key.pub)().size());
+   L(FL("has_key: building %d-byte pub key") % pub_block.size());
+   shared_ptr<X509_PublicKey> x509_key =
+     shared_ptr<X509_PublicKey>(Botan::X509::load_key(pub_block));
+diff --git a/src/transforms.cc b/src/transforms.cc
+index e4d9443..546d5ab 100644
+--- a/src/transforms.cc
++++ b/src/transforms.cc
+@@ -53,15 +53,16 @@ using Botan::Hash_Filter;
+ // paradigm "must" be used. this program is intended for source code
+ // control and I make no bones about it.
+ 
+-NORETURN(static inline void error_in_transform(Botan::Exception & e));
++NORETURN(static inline void error_in_transform(std::exception & e));
+ 
+ static inline void
+-error_in_transform(Botan::Exception & e, origin::type caused_by)
++error_in_transform(std::exception & e, origin::type caused_by)
+ {
+   // these classes can all indicate data corruption
+   if (typeid(e) == typeid(Botan::Encoding_Error)
+       || typeid(e) == typeid(Botan::Decoding_Error)
+       || typeid(e) == typeid(Botan::Stream_IO_Error)
++      || typeid(e) == typeid(Botan::Invalid_Argument)
+       || typeid(e) == typeid(Botan::Integrity_Failure))
+     {
+       // clean up the what() string a little: throw away the
+@@ -107,7 +108,7 @@ error_in_transform(Botan::Exception & e, origin::type caused_by)
+         pipe->process_msg(in);                                  \
+         out = pipe->read_all_as_string(Pipe::LAST_MESSAGE);     \
+       }                                                         \
+-    catch (Botan::Exception & e)                                \
++    catch (std::exception & e)                                   \
+       {                                                         \
+         pipe.reset(new Pipe(new T(carg)));                      \
+         error_in_transform(e, made_from);                       \
+@@ -173,7 +174,7 @@ template<> string xform<Botan::Hex_Decoder>(string const & in,
+             {
+               throw Botan::Decoding_Error(string("invalid hex character '") + (char)c + "'");
+             }
+-          catch(Botan::Exception & e)
++          catch(std::exception & e)
+             {
+               error_in_transform(e, made_from);
+             }
+@@ -219,7 +220,7 @@ void pack(T const & in, base64< gzip<T> > & out)
+       tmp = pipe->read_all_as_string(Pipe::LAST_MESSAGE);
+       out = base64< gzip<T> >(tmp, in.made_from);
+     }
+-  catch (Botan::Exception & e)
++  catch (std::exception & e)
+     {
+       pipe.reset(new Pipe(new Gzip_Compression,
+                           new Base64_Encoder));
+@@ -237,7 +238,7 @@ void unpack(base64< gzip<T> > const & in, T & out)
+       pipe->process_msg(in());
+       out = T(pipe->read_all_as_string(Pipe::LAST_MESSAGE), in.made_from);
+     }
+-  catch (Botan::Exception & e)
++  catch (std::exception & e)
+     {
+       pipe.reset(new Pipe(new Base64_Decoder,
+                           new Gzip_Decompression));
+@@ -264,7 +265,7 @@ calculate_ident(data const & dat,
+       p->process_msg(dat());
+       ident = id(p->read_all_as_string(Pipe::LAST_MESSAGE), dat.made_from);
+     }
+-  catch (Botan::Exception & e)
++  catch (std::exception & e)
+     {
+       p.reset(new Pipe(new Hash_Filter("SHA-160")));
+       error_in_transform(e, dat.made_from);
+-- 
+tg: (1150daa..) t/botan-1.10 (depends on: master)
diff --git a/monotone-1.0-fix-texinfo.patch b/monotone-1.0-fix-texinfo.patch
new file mode 100644
index 0000000..6a4e1ec
--- /dev/null
+++ b/monotone-1.0-fix-texinfo.patch
@@ -0,0 +1,623 @@
+Description: fix texinfo errors with texinfo >= 5.0
+Origin: upstream, commit: 0d22697daf2fd169db15445cd88f8a7be7371f64
+Author: Markus Wanner <markus at bluegap.ch>
+--- a/doc/monotone.texi
++++ b/doc/monotone.texi
+@@ -4779,7 +4779,7 @@
+ Cancel all previous @option{--rcfile} options (standard rcfiles are
+ still loaded). See @ref{rcfiles}.
+ 
+- at itemx --root <arg>
++ at item --root <arg>
+ Limit the search for a workspace to the specified root directory
+ 
+ @item --ssh-sign <arg>
+@@ -6150,7 +6150,7 @@
+ Specifying only the pathname "." will restrict the search for unknown
+ files to the current subdirectory of the workspace.
+ 
+- at itemx mtn list vars [@var{domain}]
++ at item mtn list vars [@var{domain}]
+ @itemx mtn ls vars
+ @command{ls vars} is an alias for @command{list vars}.
+ 
+--- a/doc/pcrepattern.texi
++++ b/doc/pcrepattern.texi
+@@ -74,32 +74,32 @@
+ brackets, the metacharacters are as follows:
+ 
+ @table @code
+- at itemx \
++ at item \
+ general escape character with several uses
+- at itemx ^
++ at item ^
+ assert start of string (or line, in multiline mode)
+- at itemx $
++ at item $
+ assert end of string (or line, in multiline mode)
+- at itemx .
++ at item .
+ match any character except newline (by default)
+- at itemx [
++ at item [
+ start character class definition
+- at itemx |
++ at item |
+ start of alternative branch
+- at itemx (
++ at item (
+ start subpattern
+- at itemx )
++ at item )
+ end subpattern
+- at itemx ?
++ at item ?
+ extends the meaning of @samp{(}
+          also 0 or 1 quantifier
+          also quantifier minimizer
+- at itemx *
++ at item *
+ 0 or more quantifier
+- at itemx +
++ at item +
+ 1 or more quantifier
+          also ``possessive quantifier''
+- at itemx @{
++ at item @{
+ start min/max quantifier
+ @end table
+ 
+@@ -108,16 +108,16 @@
+ class". In a character class the only metacharacters are:
+ 
+ @table @code
+- at itemx \
++ at item \
+ general escape character
+- at itemx ^
++ at item ^
+ negate the class, but only if the first character
+- at itemx -
++ at item -
+ indicates character range
+- at itemx [
++ at item [
+ POSIX character class (only if followed by POSIX
+            syntax)
+- at itemx ]
++ at item ]
+ terminates the character class
+ @end table
+ 
+@@ -159,25 +159,25 @@
+ represents:
+ 
+ @table @code
+- at itemx \a
++ at item \a
+ alarm, that is, the BEL character (hex 07)
+- at itemx \c at var{x}
++ at item \c at var{x}
+ "control- at var{x}", where @var{x} is any character
+- at itemx \e
++ at item \e
+ escape (hex 1B)
+- at itemx \f
++ at item \f
+ formfeed (hex 0C)
+- at itemx \n
++ at item \n
+ linefeed (hex 0A)
+- at itemx \r
++ at item \r
+ carriage return (hex 0D)
+- at itemx \t
++ at item \t
+ tab (hex 09)
+- at itemx \@var{ddd}
++ at item \@var{ddd}
+ character with octal code @var{ddd}, or backreference
+- at itemx \x at var{hh}
++ at item \x at var{hh}
+ character with hex code @var{hh}
+- at itemx \x@{@var{hhh...}@}
++ at item \x@{@var{hhh...}@}
+ character with hex code @var{hhh...}
+ @end table
+ 
+@@ -244,27 +244,27 @@
+ @samp{\777} are permitted. For example:
+ 
+ @table @code
+- at itemx \040
++ at item \040
+ is another way of writing a space
+- at itemx \40
++ at item \40
+ is the same, provided there are fewer than 40
+             previous capturing subpatterns
+- at itemx \7
++ at item \7
+ is always a back reference
+- at itemx \11
++ at item \11
+ might be a back reference, or another way of
+             writing a tab
+- at itemx \011
++ at item \011
+ is always a tab
+- at itemx \0113
++ at item \0113
+ is a tab followed by the character @samp{3}
+- at itemx \113
++ at item \113
+ might be a back reference, otherwise the
+             character with octal code 113
+- at itemx \377
++ at item \377
+ might be a back reference, otherwise
+             the byte consisting entirely of 1 bits
+- at itemx \81
++ at item \81
+ is either a back reference, or a binary zero
+             followed by the two characters @samp{8} and @samp{1}
+ @end table
+@@ -294,25 +294,25 @@
+ following are always recognized:
+ 
+ @table @code
+- at itemx \d
++ at item \d
+ any decimal digit
+- at itemx \D
++ at item \D
+ any character that is not a decimal digit
+- at itemx \h
++ at item \h
+ any horizontal whitespace character
+- at itemx \H
++ at item \H
+ any character that is not a horizontal whitespace character
+- at itemx \s
++ at item \s
+ any whitespace character
+- at itemx \S
++ at item \S
+ any character that is not a whitespace character
+- at itemx \v
++ at item \v
+ any vertical whitespace character
+- at itemx \V
++ at item \V
+ any character that is not a vertical whitespace character
+- at itemx \w
++ at item \w
+ any ``word'' character
+- at itemx \W
++ at item \W
+ any ``non-word'' character
+ @end table
+ 
+@@ -342,43 +342,43 @@
+ characters are:
+ 
+ @table @code
+- at itemx U+0009
++ at item U+0009
+ Horizontal tab
+- at itemx U+0020
++ at item U+0020
+ Space
+- at itemx U+00A0
++ at item U+00A0
+ Non-break space
+- at itemx U+1680
++ at item U+1680
+ Ogham space mark
+- at itemx U+180E
++ at item U+180E
+ Mongolian vowel separator
+ @item U+2000
+ En quad
+- at itemx U+2001
++ at item U+2001
+ Em quad
+- at itemx U+2002
++ at item U+2002
+ En space
+- at itemx U+2003
++ at item U+2003
+ Em space
+- at itemx U+2004
++ at item U+2004
+ Three-per-em space
+- at itemx U+2005
++ at item U+2005
+ Four-per-em space
+- at itemx U+2006
++ at item U+2006
+ Six-per-em space
+ @item U+2007
+ Figure space
+- at itemx U+2008
++ at item U+2008
+ Punctuation space
+- at itemx U+2009
++ at item U+2009
+ Thin space
+- at itemx U+200A
++ at item U+200A
+ Hair space
+- at itemx U+202F
++ at item U+202F
+ Narrow no-break space
+- at itemx U+205F
++ at item U+205F
+ Medium mathematical space
+- at itemx U+3000
++ at item U+3000
+ Ideographic space
+ @end table
+ 
+@@ -386,19 +386,19 @@
+ The vertical space characters are:
+ 
+ @table @code
+- at itemx U+000A
++ at item U+000A
+ Linefeed
+- at itemx U+000B
++ at item U+000B
+ Vertical tab
+- at itemx U+000C
++ at item U+000C
+ Formfeed
+- at itemx U+000D
++ at item U+000D
+ Carriage return
+- at itemx U+0085
++ at item U+0085
+ Next line
+- at itemx U+2028
++ at item U+2028
+ Line separator
+- at itemx U+2029
++ at item U+2029
+ Paragraph separator
+ @end table
+ 
+@@ -418,15 +418,15 @@
+ the following five sequences:
+ 
+ @table @code
+- at itemx (*CR)
++ at item (*CR)
+   carriage return
+- at itemx (*LF)
++ at item (*LF)
+   linefeed
+- at itemx (*CRLF)
++ at item (*CRLF)
+   carriage return, followed by linefeed
+- at itemx (*ANYCRLF)
++ at item (*ANYCRLF)
+   any of the three above
+- at itemx (*ANY)
++ at item (*ANY)
+   all Unicode newline sequences
+ @end table
+ 
+@@ -474,9 +474,9 @@
+ pattern string with one of the following sequences:
+ 
+ @table @code
+- at itemx (*BSR_ANYCRLF)
++ at item (*BSR_ANYCRLF)
+ @key{CR}, @key{LF}, or @key{CR}@key{LF} only
+- at itemx (*BSR_UNICODE)
++ at item (*BSR_UNICODE)
+ any Unicode newline sequence (the default)
+ @end table
+ 
+@@ -501,11 +501,11 @@
+ 256, but they do work in this mode.  The extra escape sequences are:
+ 
+ @table @code
+- at itemx \p@{@var{xx}@}
++ at item \p@{@var{xx}@}
+ a character with the @var{xx} property
+- at itemx \P@{@var{xx}@}
++ at item \P@{@var{xx}@}
+ a character without the @var{xx} property
+- at itemx \X
++ at item \X
+ an extended Unicode sequence
+ @end table
+ 
+@@ -618,83 +618,83 @@
+ @table @code
+ @item C
+ Other
+- at itemx Cc
++ at item Cc
+ Control
+- at itemx Cf
++ at item Cf
+ Format
+- at itemx Cn
++ at item Cn
+ Unassigned
+- at itemx Co
++ at item Co
+ Private use
+- at itemx Cs
++ at item Cs
+ Surrogate
+ 
+ @item L
+ Letter
+- at itemx Ll
++ at item Ll
+ Lower case letter
+- at itemx Lm
++ at item Lm
+ Modifier letter
+- at itemx Lo
++ at item Lo
+ Other letter
+- at itemx Lt
++ at item Lt
+ Title case letter
+- at itemx Lu
++ at item Lu
+ Upper case letter
+ 
+ @item M
+ Mark
+- at itemx Mc
++ at item Mc
+ Spacing mark
+- at itemx Me
++ at item Me
+ Enclosing mark
+- at itemx Mn
++ at item Mn
+ Non-spacing mark
+ 
+ @item N
+ Number
+- at itemx Nd
++ at item Nd
+ Decimal number
+- at itemx Nl
++ at item Nl
+ Letter number
+- at itemx No
++ at item No
+ Other number
+ 
+ @item P
+ Punctuation
+- at itemx Pc
++ at item Pc
+ Connector punctuation
+- at itemx Pd
++ at item Pd
+ Dash punctuation
+- at itemx Pe
++ at item Pe
+ Close punctuation
+- at itemx Pf
++ at item Pf
+ Final punctuation
+- at itemx Pi
++ at item Pi
+ Initial punctuation
+- at itemx Po
++ at item Po
+ Other punctuation
+- at itemx Ps
++ at item Ps
+ Open punctuation
+ 
+ @item S
+ Symbol
+- at itemx Sc
++ at item Sc
+ Currency symbol
+- at itemx Sk
++ at item Sk
+ Modifier symbol
+- at itemx Sm
++ at item Sm
+ Mathematical symbol
+- at itemx So
++ at item So
+ Other symbol
+ 
+ @item Z
+ Separator
+- at itemx Zl
++ at item Zl
+ Line separator
+- at itemx Zp
++ at item Zp
+ Paragraph separator
+- at itemx Zs
++ at item Zs
+ Space separator
+ @end table
+ 
+@@ -771,18 +771,18 @@
+ described below.  The backslashed assertions are:
+ 
+ @table @code
+- at itemx \b
++ at item \b
+ matches at a word boundary
+- at itemx \B
++ at item \B
+ matches when not at a word boundary
+- at itemx \A
++ at item \A
+ matches at the start of the subject
+- at itemx \Z
++ at item \Z
+ matches at the end of the subject
+           also matches before a newline at the end of the subject
+- at itemx \z
++ at item \z
+ matches only at the end of the subject
+- at itemx \G
++ at item \G
+ matches at the first matching position in the subject
+ @end table
+ 
+@@ -993,33 +993,33 @@
+ supported class names are
+ 
+ @table @code
+- at itemx alnum
++ at item alnum
+ letters and digits
+- at itemx alpha
++ at item alpha
+ letters
+- at itemx ascii
++ at item ascii
+ character codes 0 -- 127
+- at itemx blank
++ at item blank
+ space or tab only
+- at itemx cntrl
++ at item cntrl
+ control characters
+- at itemx digit
++ at item digit
+ decimal digits (same as @samp{\d})
+- at itemx graph
++ at item graph
+ printing characters, excluding space
+- at itemx lower
++ at item lower
+ lower case letters
+- at itemx print
++ at item print
+ printing characters, including space
+- at itemx punct
++ at item punct
+ printing characters, excluding letters and digits
+- at itemx space
++ at item space
+ white space (not quite the same as @samp{\s})
+- at itemx upper
++ at item upper
+ upper case letters
+- at itemx word
++ at item word
+ ``word'' characters (same as @samp{\w})
+- at itemx xdigit
++ at item xdigit
+ hexadecimal digits
+ @end table
+ 
+@@ -1071,22 +1071,22 @@
+ @samp{)}.  The option letters are
+ 
+ @table @code
+- at itemx i
++ at item i
+ Caseless: characters in one case match the corresponding
+      characters in other cases as well.
+- at itemx m
++ at item m
+ Multiline: @samp{^} and @samp{$} match at newlines
+      as well as at beginning and end of string.
+- at itemx s
++ at item s
+ Dotall: dot matches any character, including newline characters.
+- at itemx x
++ at item x
+ Extended syntax: unescaped white space is ignored and embedded
+      comments are possible.
+- at itemx J
++ at item J
+ Dupnames: names for capturing subpattern need not be unique.
+- at itemx U
++ at item U
+ Ungreedy: quantifiers match as few times as possible by default.
+- at itemx X
++ at item X
+ Extra: for forward compatibility, give an error if any escape sequence
+ with no defined meaning appears.
+ @end table
+@@ -1358,11 +1358,11 @@
+ single-character abbreviations:
+ 
+ @table @code
+- at itemx *
++ at item *
+ is equivalent to @{0,@}
+- at itemx +
++ at item +
+ is equivalent to @{1,@}
+- at itemx ?
++ at item ?
+ is equivalent to @{0,1@}
+ @end table
+ 
+@@ -2302,7 +2302,7 @@
+ The following verbs act as soon as they are encountered:
+ 
+ @table @code
+- at itemx (*ACCEPT)
++ at item (*ACCEPT)
+ 
+ This verb causes the match to end successfully, skipping the remainder
+ of the pattern. When inside a recursion, only the innermost pattern is
+@@ -2317,7 +2317,7 @@
+ This matches @samp{AB}, @samp{AAD}, or @samp{ACD}, but when it matches
+ @samp{AB}, no data is captured.
+ 
+- at itemx (*FAIL) @r{or} (*F)
++ at item (*FAIL) @r{or} (*F)
+ 
+ This verb causes the match to fail, forcing backtracking to occur. It
+ is equivalent to @samp{(?!)} but easier to read.  It is not clear
+@@ -2334,7 +2334,7 @@
+ occurs.
+ 
+ @table @code
+- at itemx (*COMMIT)
++ at item (*COMMIT)
+ 
+ This verb causes the whole match to fail outright if the rest of the
+ pattern does not match. Even if the pattern is unanchored, no further
+@@ -2350,7 +2350,7 @@
+ This matches @samp{xxaab} but not @samp{aacaab}. It can be thought of
+ as a kind of dynamic anchor, or ``I've started, so I must finish.''
+ 
+- at itemx (*PRUNE)
++ at item (*PRUNE)
+ 
+ This verb causes the match to fail at the current position if the rest
+ of the pattern does not match. If the pattern is unanchored, the
+@@ -2363,7 +2363,7 @@
+ are some uses of @code{(*PRUNE)} that cannot be expressed in any other
+ way.
+ 
+- at itemx (*SKIP)
++ at item (*SKIP)
+ 
+ This verb is like @code{(*PRUNE)}, except that if the pattern is
+ unanchored, the "bumpalong" advance is not to the next character, but
+@@ -2383,7 +2383,7 @@
+ attempt would start at the second character instead of skipping on to
+ @samp{c}.
+ 
+- at itemx (*THEN)
++ at item (*THEN)
+ 
+ This verb causes a skip to the next alternation if the rest of the
+ pattern does not match. That is, it cancels pending backtracking, but
diff --git a/monotone-1.0-lua-5.2.patch b/monotone-1.0-lua-5.2.patch
new file mode 100644
index 0000000..2493490
--- /dev/null
+++ b/monotone-1.0-lua-5.2.patch
@@ -0,0 +1,752 @@
+From: Thomas Moschny <thomas.moschny at gmx.de>
+Subject: [PATCH] lua-5.2
+
+Support Lua 5.2.
+
+Apply patches from these upstream commits:
+53e02eaa302bc05e96a18e3882b0e9843b53cf9a (partially)
+fd98d953ca93454c66a55aadf2adbeb87de86f69 (with some tweaking)
+fe1180f754da5d552c308b61e8129e59039bc559
+
+---
+ extra/mtn-hooks/monotone-cvs-ignore.lua            |  2 +-
+ src/lua.cc                                         | 40 +++++++++++---
+ src/lua.hh                                         | 14 ++---
+ src/luaext_parse_basic_io.cc                       |  4 ++
+ src/luaext_platform.cc                             | 62 ++++++++++++++++------
+ src/std_hooks.lua                                  |  9 +++-
+ test/func-testsuite.lua                            |  7 +++
+ test/func/automate_get_attributes/__driver__.lua   |  2 +-
+ .../func/automate_interface_version/__driver__.lua |  2 +-
+ .../automate_inventory_ignore_dirs/__driver__.lua  |  2 +-
+ test/func/automate_put_revision/__driver__.lua     |  2 +-
+ .../automate_set_drop_attribute/__driver__.lua     |  4 +-
+ .../func/automate_stdio_band_output/__driver__.lua | 10 ++--
+ test/func/clone_weird_branch_names/__driver__.lua  |  2 +-
+ test/func/list_databases/__driver__.lua            |  6 +--
+ test/func/list_workspaces/__driver__.lua           |  8 +--
+ .../log_--no-files_and_--merges/__driver__.lua     |  4 +-
+ test/func/manpage/__driver__.lua                   |  2 +-
+ test/func/netsync_negotiation/__driver__.lua       |  8 +--
+ test/func/serve-automate-single-run/__driver__.lua |  2 +-
+ test/func/serve-automate/__driver__.lua            |  4 +-
+ test/func/ssh_agent/__driver__.lua                 |  8 +--
+ .../__driver__.lua                                 |  4 +-
+ test/func/user_commands/extra_rc                   |  3 +-
+ test/src/testlib.lua                               | 39 +++++++-------
+ 25 files changed, 161 insertions(+), 89 deletions(-)
+
+diff --git a/extra/mtn-hooks/monotone-cvs-ignore.lua b/extra/mtn-hooks/monotone-cvs-ignore.lua
+index 6a59d12..0e9feb5 100644
+--- a/extra/mtn-hooks/monotone-cvs-ignore.lua
++++ b/extra/mtn-hooks/monotone-cvs-ignore.lua
+@@ -23,7 +23,7 @@ do
+ 
+       local handle, msg = io.open(dir .. ".cvsignore")
+       if (handle) then
+-	 for line in handle:lines(dir .. ".cvsignore") do
++	 for line in handle:lines() do
+ 	    pat2 = _glob_to_pattern(line) .. "$"
+ 	    if (string.find(name, pat1 .. pat2)) then
+ 	       return true
+diff --git a/src/lua.cc b/src/lua.cc
+index 29f1033..9456c64 100644
+--- a/src/lua.cc
++++ b/src/lua.cc
+@@ -44,7 +44,11 @@ dump_stack(lua_State * st)
+     switch (t) {
+     case LUA_TSTRING:  /* strings */
+       out += '`';
++#ifdef lua_strlen
+       out += string(lua_tostring(st, i), lua_strlen(st, i));
++#else
++      out += string(lua_tostring(st, i), lua_rawlen(st, i));
++#endif
+       out += '\'';
+       break;
+ 
+@@ -95,7 +99,11 @@ void
+ Lua::report_error()
+ {
+ //  I(lua_isstring(st, -1));
++#ifdef lua_strlen
+   string err = string(lua_tostring(st, -1), lua_strlen(st, -1));
++#else
++  string err = string(lua_tostring(st, -1), lua_rawlen(st, -1));
++#endif
+   W(i18n_format("%s") % err);
+   L(FL("lua stack: %s") % dump_stack(st));
+   lua_pop(st, 1);
+@@ -107,7 +115,11 @@ Lua::check_stack(int count)
+ {
+   if (!lua_checkstack(st, count))
+     {
++#ifdef LUAI_MAXCSTACK
+       fail((FL("lua stack limit '%d' reached") % LUAI_MAXCSTACK).str());
++#else
++      fail((FL("lua stack limit '%d' reached") % LUAI_MAXSTACK).str());
++#endif
+       return false;
+     }
+   return true;
+@@ -119,17 +131,27 @@ Lua &
+ Lua::get(int idx)
+ {
+   if (failed) return *this;
+-  if (!lua_istable (st, idx))
+-    {
+-      fail("istable() in get");
+-      return *this;
+-    }
+   if (lua_gettop (st) < 1)
+     {
+       fail("stack top > 0 in get");
+       return *this;
+     }
+-  lua_gettable(st, idx);
++  if (idx)
++    {
++      if (!lua_istable (st, idx))
++        {
++          fail("istable() in get");
++          return *this;
++        }
++      lua_gettable(st, idx);
++    }
++  else
++    {
++      string name;
++      extract_str(name);
++      pop();
++      lua_getglobal(st, name.c_str());
++    }
+   return *this;
+ }
+ 
+@@ -194,7 +216,11 @@ Lua::extract_str_nolog(string & str)
+       fail("isstring() in extract_str");
+       return *this;
+     }
++#ifdef lua_strlen
+   str = string(lua_tostring(st, -1), lua_strlen(st, -1));
++#else
++  str = string(lua_tostring(st, -1), lua_rawlen(st, -1));
++#endif
+   return *this;
+ }
+ 
+@@ -460,7 +486,7 @@ void add_functions(lua_State * st)
+         {
+           lua_newtable(st);
+           lua_pushvalue(st, -1);
+-          lua_setfield(st, LUA_GLOBALSINDEX, table.c_str());
++          lua_setglobal(st, table.c_str());
+         }
+       for (luaext::fmap::const_iterator j = i->second.begin();
+            j != i->second.end(); ++j)
+diff --git a/src/lua.hh b/src/lua.hh
+index ae87329..6f9783a 100644
+--- a/src/lua.hh
++++ b/src/lua.hh
+@@ -37,13 +37,13 @@ Lua
+   void report_error();
+   bool check_stack(int count);
+ 
+-  // getters
+-  Lua & get(int idx = LUA_GLOBALSINDEX);
+-  Lua & get_fn(int idx = LUA_GLOBALSINDEX);
+-  Lua & get_tab(int idx = LUA_GLOBALSINDEX);
+-  Lua & get_str(int idx = LUA_GLOBALSINDEX);
+-  Lua & get_num(int idx = LUA_GLOBALSINDEX);
+-  Lua & get_bool(int idx = LUA_GLOBALSINDEX);
++  // getters (0 is an invalid index in lua, and is used here to represent the global table)
++  Lua & get(int idx = 0);
++  Lua & get_fn(int idx = 0);
++  Lua & get_tab(int idx = 0);
++  Lua & get_str(int idx = 0);
++  Lua & get_num(int idx = 0);
++  Lua & get_bool(int idx = 0);
+ 
+   // extractors
+   Lua & extract_str_nolog(std::string & str);
+diff --git a/src/luaext_parse_basic_io.cc b/src/luaext_parse_basic_io.cc
+index 84cea98..4d2308a 100644
+--- a/src/luaext_parse_basic_io.cc
++++ b/src/luaext_parse_basic_io.cc
+@@ -23,7 +23,11 @@ LUAEXT(parse_basic_io, )
+   // followed by one or more string or hex values. It returns a table of
+   // lines.
+   vector<pair<string, vector<string> > > res;
++#ifdef lua_strlen
+   const string str(luaL_checkstring(LS, -1), lua_strlen(LS, -1));
++#else
++  const string str(luaL_checkstring(LS, -1), lua_rawlen(LS, -1));
++#endif
+   basic_io::input_source in(str, "monotone_parse_basic_io_for_lua");
+   in.made_from = origin::user;
+   basic_io::tokenizer tok(in);
+diff --git a/src/luaext_platform.cc b/src/luaext_platform.cc
+index 643250c..c89b45a 100644
+--- a/src/luaext_platform.cc
++++ b/src/luaext_platform.cc
+@@ -96,31 +96,58 @@ LUAEXT(spawn_redirected, )
+   return 1;
+ }
+ 
+-// borrowed from lua/liolib.cc
+-// Note that making C functions that return FILE* in Lua is tricky
++// Making C functions that return FILE* in Lua is tricky. Especially if it
++// actually needs to work with multiple lua versions.
++//
++// The following routines are inspired by lua/liolib.c from both versions.
++// The mtn_lua_Stream struct is closer to the 5.2 variant, but the
++// additional field compared to 5.1 (which only uses FILE*) shouldn't hurt
++// in Lua 5.1.
++//
+ // There is a Lua FAQ entitled:
+ // "Why does my library-created file segfault on :close() but work otherwise?"
++//
++// However, it's advice seems out-dated and applies more to 5.1.
++
++typedef struct mtn_lua_Stream {
++  FILE *f;
++  lua_CFunction closef;
++} mtn_lua_Stream;
++
++#define topfile(LS)     ((mtn_lua_Stream *)luaL_checkudata(LS, 1, LUA_FILEHANDLE))
+ 
+-#define topfile(LS)     ((FILE **)luaL_checkudata(LS, 1, LUA_FILEHANDLE))
++static int io_pclose (lua_State *LS) {
++  mtn_lua_Stream *s = topfile(LS);
++
++  // Note that in Lua 5.2, aux_close() already resets s->closef to NULL and for
++  // Lua 5.1, it's not relevant, at all. But we've set it to &io_pclose(), so
++  // contents of s->closef different between Lua versions.
++
++  int ok;
++  if (s->f != NULL)
++    ok = (pclose(s->f) == 0);
++
++  s->f = NULL;
++  s->closef = NULL;  // just to be extra sure this won't do any harm
+ 
+-static int io_fclose (lua_State *LS) {
+-  FILE **p = topfile(LS);
+-  int ok = (fclose(*p) == 0);
+-  *p = NULL;
+   lua_pushboolean(LS, ok);
+   return 1;
+ }
+ 
+-static FILE **newfile (lua_State *LS) {
+-  FILE **pf = (FILE **)lua_newuserdata(LS, sizeof(FILE *));
+-  *pf = NULL;  /* file handle is currently `closed' */
++static mtn_lua_Stream *newstream (lua_State *LS) {
++  mtn_lua_Stream *s = (mtn_lua_Stream *)lua_newuserdata(LS, sizeof(mtn_lua_Stream));
++  s->f = NULL;  /* file handle is currently `closed' */
++  s->closef = NULL;
+   luaL_getmetatable(LS, LUA_FILEHANDLE);
+   lua_setmetatable(LS, -2);
+ 
+-  lua_pushcfunction(LS, io_fclose);
++#ifdef LUA_ENVIRONINDEX
++  // Lua 5.2 removes C function environments
++  lua_pushcfunction(LS, io_pclose);
+   lua_setfield(LS, LUA_ENVIRONINDEX, "__close");
++#endif
+ 
+-  return pf;
++  return s;
+ }
+ 
+ LUAEXT(spawn_pipe, )
+@@ -136,12 +163,13 @@ LUAEXT(spawn_pipe, )
+   for (i=0; i<n; i++) argv[i] = (char*)luaL_checkstring(LS,  i+1);
+   argv[i] = NULL;
+ 
+-  int infd;
+-  FILE **inpf = newfile(LS);
+-  int outfd;
+-  FILE **outpf = newfile(LS);
++  mtn_lua_Stream *ins = newstream(LS);
++  ins->closef = &io_pclose;
++
++  mtn_lua_Stream *outs = newstream(LS);
++  outs->closef = &io_pclose;
+ 
+-  pid = process_spawn_pipe(argv, inpf, outpf);
++  pid = process_spawn_pipe(argv, &ins->f, &outs->f);
+   free(argv);
+ 
+   lua_pushnumber(LS, pid);
+diff --git a/src/std_hooks.lua b/src/std_hooks.lua
+index bfef561..7099f83 100644
+--- a/src/std_hooks.lua
++++ b/src/std_hooks.lua
+@@ -10,6 +10,13 @@
+ -- this is the standard set of lua hooks for monotone;
+ -- user-provided files can override it or add to it.
+ 
++-- Since Lua 5.2, unpack and loadstrings are deprecated and are either moved
++-- to table.unpack() or replaced by load(). If lua was compiled without
++-- LUA_COMPAT_UNPACK and/or LUA_COMPAT_LOADSTRING, these two are not
++-- available and we add a similar compatibility layer, ourselves.
++unpack = unpack or table.unpack
++loadstring = loadstring or load
++
+ function temp_file(namehint)
+    local tdir
+    tdir = os.getenv("TMPDIR")
+@@ -1465,7 +1472,7 @@ do
+       return true, warning
+    end
+    function push_hook_functions(functions)
+-      local n = table.maxn(hook_functions) + 1
++      local n = #hook_functions + 1
+       return add_hook_functions(functions, n)
+    end
+ 
+diff --git a/test/func-testsuite.lua b/test/func-testsuite.lua
+index 64a6f26..454cd9c 100755
+--- a/test/func-testsuite.lua
++++ b/test/func-testsuite.lua
+@@ -10,6 +10,13 @@
+ monotone_path = nil
+ no_network_tests = false
+ 
++-- Since Lua 5.2, unpack and loadstrings are deprecated and are either moved
++-- to table.unpack() or replaced by load(). If lua was compiled without
++-- LUA_COMPAT_UNPACK and/or LUA_COMPAT_LOADSTRING, these two are not
++-- available and we add a similar compatibility layer, ourselves.
++unpack = unpack or table.unpack
++loadstring = loadstring or load
++
+ function safe_mtn(...)
+   if monotone_path == nil then
+     monotone_path = os.getenv("mtn")
+diff --git a/test/func/automate_get_attributes/__driver__.lua b/test/func/automate_get_attributes/__driver__.lua
+index 2e92654..eb91015 100644
+--- a/test/func/automate_get_attributes/__driver__.lua
++++ b/test/func/automate_get_attributes/__driver__.lua
+@@ -28,7 +28,7 @@ check(mtn("automate", "get_attributes", "testfile"), 0, true, true)
+ check(fsize("stderr") == 0)
+ parsed = parse_basic_io(readfile("stdout"))
+ -- make sure the output generated 8 stanzas
+-check(table.getn(parsed) == 8)
++check(#parsed == 8)
+ lastkey = ""
+ checked = {}
+ for _,l in pairs(parsed) do
+diff --git a/test/func/automate_interface_version/__driver__.lua b/test/func/automate_interface_version/__driver__.lua
+index d969d7d..7ff79e9 100644
+--- a/test/func/automate_interface_version/__driver__.lua
++++ b/test/func/automate_interface_version/__driver__.lua
+@@ -7,4 +7,4 @@ rename("stdout", "a_v")
+ -- MinGW's wc produces "      1" as output.  Arithmetic comparison works, string comparison doesn't
+ check(numlines("a_v") == 1)
+ -- This is really ^[0-9]+\.[0-9]+$, but m4 is obfuscatory.
+-check(qgrep("^[0-9]+\.[0-9]+$", "a_v"))
++check(qgrep("^[0-9]+\\.[0-9]+$", "a_v"))
+diff --git a/test/func/automate_inventory_ignore_dirs/__driver__.lua b/test/func/automate_inventory_ignore_dirs/__driver__.lua
+index bfb5c25..72a89bb 100644
+--- a/test/func/automate_inventory_ignore_dirs/__driver__.lua
++++ b/test/func/automate_inventory_ignore_dirs/__driver__.lua
+@@ -14,7 +14,7 @@ function sortContentsByLine(input)
+   table.insert(lines, string.sub(input, theStart))
+   table.sort(lines)
+ 
+-  local len = table.getn(lines)
++  local len = #lines
+   local output = lines[1]
+   for i = 2, len do
+     output = output .. delimiter .. lines[i]
+diff --git a/test/func/automate_put_revision/__driver__.lua b/test/func/automate_put_revision/__driver__.lua
+index 2626ec3..32bdacb 100644
+--- a/test/func/automate_put_revision/__driver__.lua
++++ b/test/func/automate_put_revision/__driver__.lua
+@@ -33,5 +33,5 @@ check(mtn("automate", "put_revision", rev), 3, false, false)
+ -- but this should work (tests that we can use put_revision to commit a
+ -- single-parent revision)
+ check(mtn("automate", "put_file", ""), 0, false, false)
+-rev = "format_version \"1\"\n\nnew_manifest [0000000000000000000000000000000000000000]\n\nold_revision [4c2c1d846fa561601254200918fba1fd71e6795d]\n\patch \"foo\"\n from [5bf1fd927dfb8679496a2e6cf00cbe50c1c87145] to [da39a3ee5e6b4b0d3255bfef95601890afd80709]\n"
++rev = "format_version \"1\"\n\nnew_manifest [0000000000000000000000000000000000000000]\n\nold_revision [4c2c1d846fa561601254200918fba1fd71e6795d]\n\npatch \"foo\"\n from [5bf1fd927dfb8679496a2e6cf00cbe50c1c87145] to [da39a3ee5e6b4b0d3255bfef95601890afd80709]\n"
+ check(mtn("automate", "put_revision", rev), 0, false, false)
+diff --git a/test/func/automate_set_drop_attribute/__driver__.lua b/test/func/automate_set_drop_attribute/__driver__.lua
+index 5369cd9..12ff4dc 100644
+--- a/test/func/automate_set_drop_attribute/__driver__.lua
++++ b/test/func/automate_set_drop_attribute/__driver__.lua
+@@ -25,7 +25,7 @@ check(mtn("automate", "set_attribute", "testfile", "foo", "bar"), 0, false, fals
+ check(mtn("automate", "get_attributes", "testfile"), 0, true, false)
+ parsed = parse_basic_io(readfile("stdout"))
+ 
+-check(table.getn(parsed) == 2)
++check(#parsed == 2)
+ for _,l in pairs(parsed) do
+     if l.name == "attr" then
+         key = l.values[1]
+@@ -44,7 +44,7 @@ check(mtn("automate", "drop_attribute", "testfile", "foo"), 0, true, true)
+ -- check if it has been really dropped
+ check(mtn("automate", "get_attributes", "testfile"), 0, true, false)
+ parsed = parse_basic_io(readfile("stdout"))
+-check(table.getn(parsed) == 0)
++check(#parsed == 0)
+ 
+ -- check if it escalates properly if there is no such attr to drop
+ check(mtn("automate", "drop_attribute", "testfile", "foo"), 1, false, true)
+diff --git a/test/func/automate_stdio_band_output/__driver__.lua b/test/func/automate_stdio_band_output/__driver__.lua
+index 4262d0e..88c8d63 100755
+--- a/test/func/automate_stdio_band_output/__driver__.lua
++++ b/test/func/automate_stdio_band_output/__driver__.lua
+@@ -4,13 +4,13 @@ mtn_setup()
+ 
+ -- check informational messages, warnings and errors
+ out = run_stdio("l8:bandtest4:infoe", 0, 0, "p")
+-check(type(out) == "table" and table.maxn(out) == 1)
++check(type(out) == "table" and #out == 1)
+ 
+ out = run_stdio("l8:bandtest7:warninge", 0, 0, "w")
+-check(type(out) == "table" and table.maxn(out) == 1)
++check(type(out) == "table" and #out == 1)
+ 
+ out = run_stdio("l8:bandtest5:errore", 2, 0, "e")
+-check(type(out) == "table" and table.maxn(out) == 1)
++check(type(out) == "table" and #out == 1)
+ 
+ -- check tickers
+ tickers = run_stdio("l8:bandtest6:tickere", 0, 0, "t")
+@@ -38,7 +38,7 @@ end
+ ticker_data = {}
+ for _,tick in ipairs(tickers) do
+     ticks = split(tick, ";")
+-    check(table.maxn(ticks) > 0)
++    check(#ticks > 0)
+     for _,mtick in ipairs(ticks) do
+         if string.len(mtick) > 0 then
+             local begin,End,short,ticktype,content =
+@@ -78,6 +78,6 @@ for _,tick in ipairs(tickers) do
+ end
+ 
+ -- finally check if all tickers are completed
+-check(table.maxn(ticker_data) == 0)
++check(#ticker_data == 0)
+ 
+ 
+diff --git a/test/func/clone_weird_branch_names/__driver__.lua b/test/func/clone_weird_branch_names/__driver__.lua
+index 4948a72..a97c6ea 100644
+--- a/test/func/clone_weird_branch_names/__driver__.lua
++++ b/test/func/clone_weird_branch_names/__driver__.lua
+@@ -7,7 +7,7 @@ commit("my-branch[1,2]-1^3")
+ 
+ copy("test.db", "test-clone.db")
+ -- some of the special chars need to get double-escaped to get "through"
+-testURI="file://" .. test.root .. "/test-clone.db?my-branch\\\[1,2\\\]-1^3"
++testURI="file://" .. test.root .. "/test-clone.db?my-branch\\[1,2\\]-1^3"
+ 
+ check(nodb_mtn("clone", testURI), 0, false, false)
+ check(exists("my-branch[1,2]-1^3"))
+diff --git a/test/func/list_databases/__driver__.lua b/test/func/list_databases/__driver__.lua
+index e3da0e9..48cd75c 100644
+--- a/test/func/list_databases/__driver__.lua
++++ b/test/func/list_databases/__driver__.lua
+@@ -25,15 +25,15 @@ check(mt("db", "init", "-d", ":bar"), 0, false, false)
+ check(exists("managed_databases/bar.mtn"))
+ 
+ check(mt("ls", "dbs"), 0, true, false)
+-check(qgrep(":bar.mtn.+in.+list_databases\/managed_databases", "stdout"))
++check(qgrep(":bar.mtn.+in.+list_databases/managed_databases", "stdout"))
+ check(qgrep("\tno known valid workspaces", "stdout"))
+ 
+ check(mt("setup", "-d", ":bar", "-b", "test.foo.branch", "test_foo"), 0, false, false)
+ 
+ check(mt("ls", "dbs"), 0, true, false)
+ check(not qgrep("\tno known valid workspaces", "stdout"))
+-check(qgrep("\ttest.foo.branch.+in.+list_databases\/test_foo", "stdout"))
++check(qgrep("\ttest.foo.branch.+in.+list_databases/test_foo", "stdout"))
+ 
+ check(rename("managed_databases/bar.mtn", "managed_databases/bar.db"))
+ check(mt("ls", "dbs"), 0, true, false)
+-check(qgrep(":bar.db.+in.+list_databases\/managed_databases", "stdout"))
++check(qgrep(":bar.db.+in.+list_databases/managed_databases", "stdout"))
+diff --git a/test/func/list_workspaces/__driver__.lua b/test/func/list_workspaces/__driver__.lua
+index 06dbfef..8895ed4 100644
+--- a/test/func/list_workspaces/__driver__.lua
++++ b/test/func/list_workspaces/__driver__.lua
+@@ -10,17 +10,17 @@ check(samelines("stdout", {"no known valid workspaces"}))
+ check(raw_mtn("setup", "-d", "test.mtn", "-b", "test.branch1", "work1"), 0, false, false)
+ 
+ check(raw_mtn("ls", "workspaces", "-d", "test.mtn"), 0, true, false)
+-check(qgrep("test.branch1.+in.+list_workspaces\/work1", "stdout"))
++check(qgrep("test.branch1.+in.+list_workspaces/work1", "stdout"))
+ 
+ check(raw_mtn("setup", "-d", "test.mtn", "-b", "test.branch2", "work2"), 0, false, false)
+ check(rename("work1", "work3"))
+ 
+ check(raw_mtn("ls", "workspaces", "-d", "test.mtn"), 0, true, false)
+-check(qgrep("test.branch2.+in.+list_workspaces\/work2", "stdout"))
+-check(not qgrep("test.branch1.+in.+list_workspaces\/work1", "stdout"))
++check(qgrep("test.branch2.+in.+list_workspaces/work2", "stdout"))
++check(not qgrep("test.branch1.+in.+list_workspaces/work1", "stdout"))
+ 
+ check(indir("work3", raw_mtn("register_workspace", "-d", "../test.mtn")), 0, false, false)
+ 
+ check(raw_mtn("ls", "workspaces", "-d", "test.mtn"), 0, true, false)
+-check(qgrep("test.branch1.+in.+list_workspaces\/work3", "stdout"))
++check(qgrep("test.branch1.+in.+list_workspaces/work3", "stdout"))
+ 
+diff --git a/test/func/log_--no-files_and_--merges/__driver__.lua b/test/func/log_--no-files_and_--merges/__driver__.lua
+index b27551e..de4d26d 100644
+--- a/test/func/log_--no-files_and_--merges/__driver__.lua
++++ b/test/func/log_--no-files_and_--merges/__driver__.lua
+@@ -29,8 +29,8 @@ R2=base_revision()
+ 
+ -- check that merge is included by default
+ check(mtn("log"), 0, true, false)
+-check(qgrep("^[\\|\\\\\/ ]+Revision.*"..R2, "stdout"))
++check(qgrep("^[\\|\\\\/ ]+Revision.*"..R2, "stdout"))
+ 
+ -- and that it is excluded by --no-merges
+ check(mtn("log", "--no-merges"), 0, true, false)
+-check(not qgrep("^[\\|\\\\\/ ]+Revision.*"..R2, "stdout"))
++check(not qgrep("^[\\|\\\\/ ]+Revision.*"..R2, "stdout"))
+diff --git a/test/func/manpage/__driver__.lua b/test/func/manpage/__driver__.lua
+index 775714e..38e5ee7 100644
+--- a/test/func/manpage/__driver__.lua
++++ b/test/func/manpage/__driver__.lua
+@@ -5,7 +5,7 @@ rename("stdout", "manpage")
+ 
+ -- check for a proper header line
+ check(mtn("version"), 0, true, false)
+-local s,e,version = string.find(readfile("stdout"), "(monotone %d+\.%d+%S*)")
++local s,e,version = string.find(readfile("stdout"), "(monotone %d+%.%d+%S*)")
+ check(qgrep(".TH \"monotone\" 1 \"[0-9]{4}-[0-9]{2}-[0-9]{2}\" \"" .. version .. "\"", "manpage"))
+ 
+ -- check required sections
+diff --git a/test/func/netsync_negotiation/__driver__.lua b/test/func/netsync_negotiation/__driver__.lua
+index 2efe421..c776f93 100644
+--- a/test/func/netsync_negotiation/__driver__.lua
++++ b/test/func/netsync_negotiation/__driver__.lua
+@@ -87,10 +87,10 @@ function check_same_revs(cmd1, cmd2)
+    check(cmd2, 0, true, false)
+    local data2 = {}
+    for l in io.lines("stdout") do table.insert(data2, l) end
+-   L("Command 1 has ", table.getn(data1), " lines.")
+-   L("Command 2 has ", table.getn(data2), " lines.")
+-   check(table.getn(data1) == table.getn(data2))
+-   for i = 1, table.getn(data1) do
++   L("Command 1 has ", #data1, " lines.")
++   L("Command 2 has ", #data2, " lines.")
++   check(#data1 == #data2)
++   for i = 1, #data1 do
+       local hash_len = 40
+       check(data1[i]:sub(1, hash_len) == data2[i]:sub(1, hash_len))
+    end
+diff --git a/test/func/serve-automate-single-run/__driver__.lua b/test/func/serve-automate-single-run/__driver__.lua
+index 87b1ab7..b65b9c1 100644
+--- a/test/func/serve-automate-single-run/__driver__.lua
++++ b/test/func/serve-automate-single-run/__driver__.lua
+@@ -28,7 +28,7 @@ server = netsync.start({"--rcfile=allow-automate.lua"})
+ 
+ check(mtn2("automate", "remote", "--remote-stdio-host", server.address,
+     "interface_version"), 0, true, false)
+-check(qgrep("^[0-9]{2,}\.[0-9]+$", "stdout"))
++check(qgrep("^[0-9]{2,}\\.[0-9]+$", "stdout"))
+ 
+ check(mtn2("automate", "remote", "--remote-stdio-host", server.address,
+     "leaves"), 0, true, false)
+diff --git a/test/func/serve-automate/__driver__.lua b/test/func/serve-automate/__driver__.lua
+index 32b2eaf..9e993dc 100644
+--- a/test/func/serve-automate/__driver__.lua
++++ b/test/func/serve-automate/__driver__.lua
+@@ -14,7 +14,7 @@ server = netsync.start({"--rcfile=deny-automate.lua"})
+ 
+ local errors = run_remote_stdio(server, "l17:interface_versione", 1, 0, "e")
+ check(
+-    table.maxn(errors) == 1 and
++    #errors == 1 and
+     errors[1] == "misuse: Sorry, you aren't allowed to do that."
+ )
+ 
+@@ -45,7 +45,7 @@ check(
+ 
+ local errors = run_remote_stdio(server, "l5:stdioe", 1, 0, "e")
+ check(
+-    table.maxn(errors) == 1 and
++    #errors == 1 and
+     errors[1] == "error: sorry, that can't be run remotely or over stdio"
+ )
+ 
+diff --git a/test/func/ssh_agent/__driver__.lua b/test/func/ssh_agent/__driver__.lua
+index d927360..a36ce90 100644
+--- a/test/func/ssh_agent/__driver__.lua
++++ b/test/func/ssh_agent/__driver__.lua
+@@ -81,7 +81,7 @@ end
+ 
+ check({"ssh-agent"}, 0, true, false)
+ for line in io.lines("stdout") do
+-   for k, v in string.gmatch(line, "([%w_]+)=([%w/\.-]+)") do
++   for k, v in string.gmatch(line, "([%w_]+)=([%w/%.-]+)") do
+       set_env(k, v)
+    end
+ end
+@@ -91,7 +91,7 @@ check(mtn("ssh_agent_add"), 0, false, false)
+ check({"ssh-add", "-l"}, 0, true, false)
+ ok = false
+ for line in io.lines("stdout") do
+-    for k in string.gmatch(line, "tester at test\.net") do
++    for k in string.gmatch(line, "tester at test%.net") do
+     	ok = true
+     end
+ end
+@@ -288,7 +288,7 @@ check(mtn("ssh_agent_add", "--key", "test2 at tester.net"), 0, false, false)
+ check({"ssh-add", "-l"}, 0, true, false)
+ ok = false
+ for line in io.lines("stdout") do
+-    for k in string.gmatch(line, "test2 at tester\.net") do
++    for k in string.gmatch(line, "test2 at tester%.net") do
+     	ok = true
+     end
+ end
+@@ -338,7 +338,7 @@ check(mtn("ssh_agent_add", "--key", "test_pass at tester.net"), 0, false, false, "p
+ check({"ssh-add", "-l"}, 0, true, false)
+ ok = false
+ for line in io.lines("stdout") do
+-    for k in string.gmatch(line, "test_pass at tester\.net") do
++    for k in string.gmatch(line, "test_pass at tester%.net") do
+     	ok = true
+     end
+ end
+diff --git a/test/func/two_parent_workspace_inodeprints/__driver__.lua b/test/func/two_parent_workspace_inodeprints/__driver__.lua
+index bd6c558..66d5150 100644
+--- a/test/func/two_parent_workspace_inodeprints/__driver__.lua
++++ b/test/func/two_parent_workspace_inodeprints/__driver__.lua
+@@ -19,13 +19,13 @@ check(mtn("merge_into_workspace", other), 0, false, false)
+ 
+ -- check that we've got the expected initial status
+ check(mtn("status"), 0, true, false)
+-check(qgrep("patched[ 	]\+foo", "stdout"))
++check(qgrep("patched[ 	]+foo", "stdout"))
+ 
+ -- enable inodeprints
+ writefile("_MTN/inodeprints")
+ 
+ check(mtn("status"), 0, true, false)
+-check(qgrep("patched[ 	]\+foo", "stdout"))
++check(qgrep("patched[ 	]+foo", "stdout"))
+ 
+ sleep(5)
+ 
+diff --git a/test/func/user_commands/extra_rc b/test/func/user_commands/extra_rc
+index 2ec5f9f..aad3764 100644
+--- a/test/func/user_commands/extra_rc
++++ b/test/func/user_commands/extra_rc
+@@ -5,7 +5,8 @@ function check_head(...)
+       io.stderr:write("automate call failed\n")
+       return
+     end
+-    arghead = unpack(arg)
++    local arg = {...}
++    arghead = arg[1]
+     heads = heads:gsub("^%s*(.-)%s*$", "%1")	-- trim leading and trailing whitespace
+     if (heads == arghead) then
+         io.write("heads are equal\n")
+diff --git a/test/src/testlib.lua b/test/src/testlib.lua
+index 6d21a93..19eb278 100644
+--- a/test/src/testlib.lua
++++ b/test/src/testlib.lua
+@@ -30,6 +30,13 @@ files = {stdout = nil, stdin = nil, stderr = nil}
+ -- for convenience, this is the first word of what get_ostype() returns.
+ ostype = string.sub(get_ostype(), 1, string.find(get_ostype(), " ")-1)
+ 
++-- Since Lua 5.2, unpack and loadstrings are deprecated and are either moved
++-- to table.unpack() or replaced by load(). If lua was compiled without
++-- LUA_COMPAT_UNPACK and/or LUA_COMPAT_LOADSTRING, these two are not
++-- available and we add a similar compatibility layer, ourselves.
++unpack = unpack or table.unpack
++loadstring = loadstring or load
++
+ -- table of per-test values
+ test = {}
+ -- misc per-test values
+@@ -310,17 +317,11 @@ end
+ -- to want to include from the dir for the current test,
+ -- since in that case it could just go in the driver file.
+ function include(name)
+-  local func, e = loadfile(testdir.."/"..name)
+-  if func == nil then err(e, 2) end
+-  setfenv(func, getfenv(2))
+-  func()
++  dofile(testdir.."/"..name)
+ end
+ 
+ function includecommon(name)
+-  local func, e = loadfile(srcdir.."/common/"..name)
+-  if func == nil then err(e, 2) end
+-  setfenv(func, getfenv(2))
+-  func()
++  dofile(srcdir.."/common/"..name)
+ end
+ 
+ function trim(str)
+@@ -479,12 +480,11 @@ end
+ function samelines(f, t)
+   local fl = {}
+   for l in io.lines(f) do table.insert(fl, l) end
+-  if not (table.getn(fl) == table.getn(t)) then
+-    L(locheader(), string.format("file has %s lines; table has %s\n",
+-                                 table.getn(fl), table.getn(t)))
++  if not (#fl == #t) then
++    L(locheader(), string.format("file has %s lines; table has %s\n", #fl, #t))
+     return false
+   end
+-  for i=1,table.getn(t) do
++  for i=1,#t do
+     if fl[i] ~= t[i] then
+       if fl[i] then
+         L(locheader(), string.format("file[%d] = '%s'; table[%d] = '%s'\n",
+@@ -502,12 +502,11 @@ end
+ function greplines(f, t)
+   local fl = {}
+   for l in io.lines(f) do table.insert(fl, l) end
+-  if not (table.getn(fl) == table.getn(t)) then
+-    L(locheader(), string.format("file has %s lines; table has %s\n",
+-                                 table.getn(fl), table.getn(t)))
++  if not (#fl == #t) then
++    L(locheader(), string.format("file has %s lines; table has %s\n", #fl, #t))
+     return false
+   end
+-  for i=1,table.getn(t) do
++  for i=1,#t do
+     if not regex.search(t[i], fl[i]) then
+       L(locheader(), string.format("file[i] = '%s'; table[i] = '%s'\n",
+                                    fl[i], t[i]))
+@@ -577,7 +576,7 @@ function tail(...)
+     local mylines = {}
+     for l in io.lines(file) do
+       table.insert(mylines, l)
+-      if table.getn(mylines) > num then
++      if #mylines > num then
+         table.remove(mylines, 1)
+       end
+     end
+@@ -932,8 +931,8 @@ function run_tests(debugging, list_only, run_dir, logname, args, progress)
+     if _1 then
+       l = l + 0
+       r = r + 0
+-      if l < 1 then l = table.getn(tests) + l + 1 end
+-      if r < 1 then r = table.getn(tests) + r + 1 end
++      if l < 1 then l = #tests + l + 1 end
++      if r < 1 then r = #tests + r + 1 end
+       if l > r then l,r = r,l end
+       for j = l,r do
+         torun[j] = tests[j]
+@@ -941,7 +940,7 @@ function run_tests(debugging, list_only, run_dir, logname, args, progress)
+       run_all = false
+     elseif string.find(a, "^-?%d+$") then
+       r = a + 0
+-      if r < 1 then r = table.getn(tests) + r + 1 end
++      if r < 1 then r = #tests + r + 1 end
+       torun[r] = tests[r]
+       run_all = false
+     else
+-- 
+tg: (1150daa..) lua-5.2 (depends on: master)
diff --git a/monotone.spec b/monotone.spec
index 2696f24..57e244a 100644
--- a/monotone.spec
+++ b/monotone.spec
@@ -1,6 +1,6 @@
 Name:            monotone
 Version:         1.0
-Release:         14%{?dist}
+Release:         15%{?dist}
 Summary:         A free, distributed version control system
 Group:           Development/Tools
 License:         GPLv2+
@@ -23,6 +23,13 @@ Patch3:          monotone-1.0-pcre.patch
 Patch4:          monotone-1.0-fix-bash_completion-test.patch
 # See rhbz#919827
 Patch5:          monotone-1.0-fix-diff_patch_drop-test.patch
+# Fix FTBFS for Botan 1.10.X
+# https://code.monotone.ca/p/monotone/issues/182/
+Patch6:          monotone-1.0-botan-1.10.patch
+# Fix FTBFS for Lua 5.2
+Patch7:          monotone-1.0-lua-5.2.patch
+# Fix texinfo errors with texinfo >= 5.0
+Patch8:          monotone-1.0-fix-texinfo.patch
 BuildRoot:       %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 BuildRequires:   zlib-devel
 BuildRequires:   boost-devel >= 1.33.1
@@ -85,6 +92,9 @@ and then pass commands to it.
 %patch3 -p1
 %patch4 -p1
 %patch5 -p1
+%patch6 -p1
+%patch7 -p1
+%patch8 -p1
 
 # fix FTBFS rhbz#914191
 sed -e "s|shared_dynamic_cast|dynamic_pointer_cast|g" \
@@ -98,12 +108,14 @@ make %{?_smp_mflags}
 
 
 %check
+export LC_MESSAGES=en_US
 export DISABLE_NETWORK_TESTS=1
 make %{?_smp_mflags} check || { head -n-0 test/work/*.log; false; }
 
 
 %install
 rm -rf %{buildroot}
+export LC_MESSAGES=en_US
 make install DESTDIR=%{buildroot}
 rm -f %{buildroot}%{_infodir}/dir
 mv %{buildroot}%{_datadir}/doc/%{name} _doc
@@ -234,6 +246,10 @@ fi
 
 
 %changelog
+* Tue Oct  8 2013 Thomas Moschny <thomas.moschny at gmx.de> - 1.0-15
+- Add patches for building with Botan 1.10, Lua 5.2,
+  and Texinfo >= 5.0.
+
 * Sat Aug 03 2013 Fedora Release Engineering <rel-eng at lists.fedoraproject.org> - 1.0-14
 - Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild
 


More information about the scm-commits mailing list