From: "Timo Gurr (tgurr)" <tgurr@gentoo.org>
To: gentoo-commits@lists.gentoo.org
Subject: [gentoo-commits] gentoo-x86 commit in dev-util/cmake/files: cmake-2.6.1-rpath.patch cmake-2.6.1-gc-sections.patch
Date: Wed, 27 Aug 2008 21:19:11 +0000 [thread overview]
Message-ID: <E1KYSQ3-0001kV-BK@stork.gentoo.org> (raw)
tgurr 08/08/27 21:19:11
Added: cmake-2.6.1-rpath.patch
cmake-2.6.1-gc-sections.patch
Log:
Version bump. Big thanks to Brad King from kitware, thewtex and Arfrever Frehtes Taifersar Arahesis and everyone else involved. Fixes bug #224901, #232111, #233772 and #235731.
(Portage version: 2.2_rc8/cvs/Linux 2.6.26-gentoo x86_64)
Revision Changes Path
1.1 dev-util/cmake/files/cmake-2.6.1-rpath.patch
file : http://sources.gentoo.org/viewcvs.py/gentoo-x86/dev-util/cmake/files/cmake-2.6.1-rpath.patch?rev=1.1&view=markup
plain: http://sources.gentoo.org/viewcvs.py/gentoo-x86/dev-util/cmake/files/cmake-2.6.1-rpath.patch?rev=1.1&content-type=text/plain
Index: cmake-2.6.1-rpath.patch
===================================================================
diff --git cmake-2.6.1-orig/Source/cmFileCommand.cxx cmake-2.6.1/Source/cmFileCommand.cxx
index 6ac6bcc..e8e64a4 100644
--- cmake-2.6.1-orig/Source/cmFileCommand.cxx
+++ cmake-2.6.1/Source/cmFileCommand.cxx
@@ -1486,7 +1486,8 @@ cmFileCommand::HandleRPathRemoveCommand(std::vector<std::string> const& args)
cmSystemToolsFileTime* ft = cmSystemTools::FileTimeNew();
bool have_ft = cmSystemTools::FileTimeGet(file, ft);
std::string emsg;
- if(!cmSystemTools::RemoveRPath(file, &emsg))
+ bool removed;
+ if(!cmSystemTools::RemoveRPath(file, &emsg, &removed))
{
cmOStringStream e;
e << "RPATH_REMOVE could not remove RPATH from file:\n"
@@ -1495,9 +1496,19 @@ cmFileCommand::HandleRPathRemoveCommand(std::vector<std::string> const& args)
this->SetError(e.str().c_str());
success = false;
}
- if(success && have_ft)
+ if(success)
{
- cmSystemTools::FileTimeSet(file, ft);
+ if(removed)
+ {
+ std::string message = "Removed runtime path from \"";
+ message += file;
+ message += "\"";
+ this->Makefile->DisplayStatus(message.c_str(), -1);
+ }
+ if(have_ft)
+ {
+ cmSystemTools::FileTimeSet(file, ft);
+ }
}
cmSystemTools::FileTimeDelete(ft);
return success;
diff --git cmake-2.6.1-orig/Source/cmSystemTools.cxx cmake-2.6.1/Source/cmSystemTools.cxx
index 4e1f945..f333a4c 100644
--- cmake-2.6.1-orig/Source/cmSystemTools.cxx
+++ cmake-2.6.1/Source/cmSystemTools.cxx
@@ -26,6 +26,7 @@
#if defined(CMAKE_BUILD_WITH_CMAKE)
# include <cmsys/Terminal.h>
#endif
+#include <cmsys/stl/algorithm>
#if defined(_WIN32)
# include <windows.h>
@@ -2328,6 +2329,16 @@ std::string::size_type cmSystemToolsFindRPath(std::string const& have,
}
#endif
+#if defined(CMAKE_USE_ELF_PARSER)
+struct cmSystemToolsRPathInfo
+{
+ unsigned long Position;
+ unsigned long Size;
+ std::string Name;
+ std::string Value;
+};
+#endif
+
//----------------------------------------------------------------------------
bool cmSystemTools::ChangeRPath(std::string const& file,
std::string const& oldRPath,
@@ -2340,37 +2351,71 @@ bool cmSystemTools::ChangeRPath(std::string const& file,
{
*changed = false;
}
- unsigned long rpathPosition = 0;
- unsigned long rpathSize = 0;
- std::string rpathPrefix;
- std::string rpathSuffix;
+ int rp_count = 0;
+ cmSystemToolsRPathInfo rp[2];
{
// Parse the ELF binary.
cmELF elf(file.c_str());
- // Get the RPATH or RUNPATH entry from it.
- cmELF::StringEntry const* se = elf.GetRPath();
- if(!se)
+ // Get the RPATH and RUNPATH entries from it.
+ int se_count = 0;
+ cmELF::StringEntry const* se[2] = {0, 0};
+ const char* se_name[2] = {0, 0};
+ if(cmELF::StringEntry const* se_rpath = elf.GetRPath())
{
- se = elf.GetRunPath();
+ se[se_count] = se_rpath;
+ se_name[se_count] = "RPATH";
+ ++se_count;
+ }
+ if(cmELF::StringEntry const* se_runpath = elf.GetRunPath())
+ {
+ se[se_count] = se_runpath;
+ se_name[se_count] = "RUNPATH";
+ ++se_count;
+ }
+ if(se_count == 0)
+ {
+ if(newRPath.empty())
+ {
+ // The new rpath is empty and there is no rpath anyway so it is
+ // okay.
+ return true;
+ }
+ else
+ {
+ if(emsg)
+ {
+ *emsg = "No valid ELF RPATH or RUNPATH entry exists in the file; ";
+ *emsg += elf.GetErrorMessage();
+ }
+ return false;
+ }
}
- if(se)
+ for(int i=0; i < se_count; ++i)
{
+ // If both RPATH and RUNPATH refer to the same string literal it
+ // needs to be changed only once.
+ if(rp_count && rp[0].Position == se[i]->Position)
+ {
+ continue;
+ }
+
// Make sure the current rpath contains the old rpath.
- std::string::size_type pos = cmSystemToolsFindRPath(se->Value, oldRPath);
+ std::string::size_type pos =
+ cmSystemToolsFindRPath(se[i]->Value, oldRPath);
if(pos == std::string::npos)
{
// If it contains the new rpath instead then it is okay.
- if(cmSystemToolsFindRPath(se->Value, newRPath) != std::string::npos)
+ if(cmSystemToolsFindRPath(se[i]->Value, newRPath) != std::string::npos)
{
- return true;
+ continue;
}
if(emsg)
{
cmOStringStream e;
- e << "The current RPATH is:\n"
- << " " << se->Value << "\n"
+ e << "The current " << se_name[i] << " is:\n"
+ << " " << se[i]->Value << "\n"
<< "which does not contain:\n"
<< " " << oldRPath << "\n"
<< "as was expected.";
@@ -2379,47 +2424,43 @@ bool cmSystemTools::ChangeRPath(std::string const& file,
return false;
}
- // Store information about the entry.
- rpathPosition = se->Position;
- rpathSize = se->Size;
+ // Store information about the entry in the file.
+ rp[rp_count].Position = se[i]->Position;
+ rp[rp_count].Size = se[i]->Size;
+ rp[rp_count].Name = se_name[i];
- // Store the part of the path we must preserve.
- rpathPrefix = se->Value.substr(0, pos);
- rpathSuffix = se->Value.substr(pos+oldRPath.length(), oldRPath.npos);
- }
- else if(newRPath.empty())
- {
- // The new rpath is empty and there is no rpath anyway so it is
- // okay.
- return true;
- }
- else
- {
- if(emsg)
+ // Construct the new value which preserves the part of the path
+ // not being changed.
+ rp[rp_count].Value = se[i]->Value.substr(0, pos);
+ rp[rp_count].Value += newRPath;
+ rp[rp_count].Value += se[i]->Value.substr(pos+oldRPath.length(),
+ oldRPath.npos);
+
+ // Make sure there is enough room to store the new rpath and at
+ // least one null terminator.
+ if(rp[rp_count].Size < rp[rp_count].Value.length()+1)
{
- *emsg = "No valid ELF RPATH entry exists in the file; ";
- *emsg += elf.GetErrorMessage();
+ if(emsg)
+ {
+ *emsg = "The replacement path is too long for the ";
+ *emsg += se_name[i];
+ *emsg += " entry.";
+ }
+ return false;
}
- return false;
+
+ // This entry is ready for update.
+ ++rp_count;
}
}
- // Compute the full new rpath.
- std::string rpath = rpathPrefix;
- rpath += newRPath;
- rpath += rpathSuffix;
- // Make sure there is enough room to store the new rpath and at
- // least one null terminator.
- if(rpathSize < rpath.length()+1)
+ // If no runtime path needs to be changed, we are done.
+ if(rp_count == 0)
{
- if(emsg)
- {
- *emsg = "The replacement RPATH is too long.";
- }
- return false;
+ return true;
}
- // Open the file for update and seek to the RPATH position.
+ // Open the file for update.
std::ofstream f(file.c_str(),
std::ios::in | std::ios::out | std::ios::binary);
if(!f)
@@ -2430,40 +2471,49 @@ bool cmSystemTools::ChangeRPath(std::string const& file,
}
return false;
}
- if(!f.seekp(rpathPosition))
+
+ // Store the new RPATH and RUNPATH strings.
+ for(int i=0; i < rp_count; ++i)
{
- if(emsg)
+ // Seek to the RPATH position.
+ if(!f.seekp(rp[i].Position))
{
- *emsg = "Error seeking to RPATH position.";
+ if(emsg)
+ {
+ *emsg = "Error seeking to ";
+ *emsg += rp[i].Name;
+ *emsg += " position.";
+ }
+ return false;
}
- return false;
- }
- // Write the new rpath. Follow it with enough null terminators to
- // fill the string table entry.
- f << rpath;
- for(unsigned long i=rpath.length(); i < rpathSize; ++i)
- {
- f << '\0';
- }
+ // Write the new rpath. Follow it with enough null terminators to
+ // fill the string table entry.
+ f << rp[i].Value;
+ for(unsigned long j=rp[i].Value.length(); j < rp[i].Size; ++j)
+ {
+ f << '\0';
+ }
- // Make sure everything was okay.
- if(f)
- {
- if(changed)
+ // Make sure it wrote correctly.
+ if(!f)
{
- *changed = true;
+ if(emsg)
+ {
+ *emsg = "Error writing the new ";
+ *emsg += rp[i].Name;
+ *emsg += " string to the file.";
+ }
+ return false;
}
- return true;
}
- else
+
+ // Everything was updated successfully.
+ if(changed)
{
- if(emsg)
- {
- *emsg = "Error writing the new rpath to the file.";
- }
- return false;
+ *changed = true;
}
+ return true;
#else
(void)file;
(void)oldRPath;
@@ -2475,57 +2525,95 @@ bool cmSystemTools::ChangeRPath(std::string const& file,
}
//----------------------------------------------------------------------------
-bool cmSystemTools::RemoveRPath(std::string const& file, std::string* emsg)
+bool cmSystemTools::RemoveRPath(std::string const& file, std::string* emsg,
+ bool* removed)
{
#if defined(CMAKE_USE_ELF_PARSER)
- unsigned long rpathPosition = 0;
- unsigned long rpathSize = 0;
- unsigned long rpathEntryPosition = 0;
+ if(removed)
+ {
+ *removed = false;
+ }
+ int zeroCount = 0;
+ unsigned long zeroPosition[2] = {0,0};
+ unsigned long zeroSize[2] = {0,0};
+ unsigned long bytesBegin = 0;
std::vector<char> bytes;
{
// Parse the ELF binary.
cmELF elf(file.c_str());
- // Get the RPATH or RUNPATH entry from it.
- cmELF::StringEntry const* se = elf.GetRPath();
- if(!se)
+ // Get the RPATH and RUNPATH entries from it and sort them by index
+ // in the dynamic section header.
+ int se_count = 0;
+ cmELF::StringEntry const* se[2] = {0, 0};
+ if(cmELF::StringEntry const* se_rpath = elf.GetRPath())
{
- se = elf.GetRunPath();
+ se[se_count++] = se_rpath;
}
-
- if(se)
+ if(cmELF::StringEntry const* se_runpath = elf.GetRunPath())
{
- // Store information about the entry.
- rpathPosition = se->Position;
- rpathSize = se->Size;
- rpathEntryPosition = elf.GetDynamicEntryPosition(se->IndexInSection);
+ se[se_count++] = se_runpath;
+ }
+ if(se_count == 0)
+ {
+ // There is no RPATH or RUNPATH anyway.
+ return true;
+ }
+ if(se_count == 2 && se[1]->IndexInSection < se[0]->IndexInSection)
+ {
+ cmsys_stl::swap(se[0], se[1]);
+ }
- // Get the file range containing the rest of the DYNAMIC table
- // after the RPATH entry.
- unsigned long nextEntryPosition =
- elf.GetDynamicEntryPosition(se->IndexInSection+1);
- unsigned int count = elf.GetDynamicEntryCount();
- if(count == 0)
+ // Get the size of the dynamic section header.
+ unsigned int count = elf.GetDynamicEntryCount();
+ if(count == 0)
+ {
+ // This should happen only for invalid ELF files where a DT_NULL
+ // appears before the end of the table.
+ if(emsg)
{
- // This should happen only for invalid ELF files where a DT_NULL
- // appears before the end of the table.
- if(emsg)
- {
- *emsg = "DYNAMIC section contains a DT_NULL before the end.";
- }
- return false;
+ *emsg = "DYNAMIC section contains a DT_NULL before the end.";
}
- unsigned long nullEntryPosition = elf.GetDynamicEntryPosition(count);
+ return false;
+ }
+
+ // Save information about the string entries to be zeroed.
+ zeroCount = se_count;
+ for(int i=0; i < se_count; ++i)
+ {
+ zeroPosition[i] = se[i]->Position;
+ zeroSize[i] = se[i]->Size;
+ }
+
+ // Get the range of file positions corresponding to each entry and
+ // the rest of the table after them.
+ unsigned long entryBegin[3] = {0,0,0};
+ unsigned long entryEnd[2] = {0,0};
+ for(int i=0; i < se_count; ++i)
+ {
+ entryBegin[i] = elf.GetDynamicEntryPosition(se[i]->IndexInSection);
+ entryEnd[i] = elf.GetDynamicEntryPosition(se[i]->IndexInSection+1);
+ }
+ entryBegin[se_count] = elf.GetDynamicEntryPosition(count);
+
+ // The data are to be written over the old table entries starting at
+ // the first one being removed.
+ bytesBegin = entryBegin[0];
+ unsigned long bytesEnd = entryBegin[se_count];
- // Allocate and fill a buffer with zeros.
- bytes.resize(nullEntryPosition - rpathEntryPosition, 0);
+ // Allocate a buffer to hold the part of the file to be written.
+ // Initialize it with zeros.
+ bytes.resize(bytesEnd - bytesBegin, 0);
- // Read the part of the DYNAMIC section header that will move.
- // The remainder of the buffer will be left with zeros which
- // represent a DT_NULL entry.
- if(!elf.ReadBytes(nextEntryPosition,
- nullEntryPosition - nextEntryPosition,
- &bytes[0]))
+ // Read the part of the DYNAMIC section header that will move.
+ // The remainder of the buffer will be left with zeros which
+ // represent a DT_NULL entry.
+ char* data = &bytes[0];
+ for(int i=0; i < se_count; ++i)
+ {
+ // Read data between the entries being removed.
+ unsigned long sz = entryBegin[i+1] - entryEnd[i];
+ if(sz > 0 && !elf.ReadBytes(entryEnd[i], sz, data))
{
if(emsg)
{
@@ -2533,11 +2621,7 @@ bool cmSystemTools::RemoveRPath(std::string const& file, std::string* emsg)
}
return false;
}
- }
- else
- {
- // There is no RPATH or RUNPATH anyway.
- return true;
+ data += sz;
}
}
@@ -2554,7 +2638,7 @@ bool cmSystemTools::RemoveRPath(std::string const& file, std::string* emsg)
}
// Write the new DYNAMIC table header.
- if(!f.seekp(rpathEntryPosition))
+ if(!f.seekp(bytesBegin))
{
if(emsg)
{
@@ -2571,36 +2655,41 @@ bool cmSystemTools::RemoveRPath(std::string const& file, std::string* emsg)
return false;
}
- // Fill the RPATH string with zero bytes.
- if(!f.seekp(rpathPosition))
+ // Fill the RPATH and RUNPATH strings with zero bytes.
+ for(int i=0; i < zeroCount; ++i)
{
- if(emsg)
+ if(!f.seekp(zeroPosition[i]))
{
- *emsg = "Error seeking to RPATH position.";
+ if(emsg)
+ {
+ *emsg = "Error seeking to RPATH position.";
+ }
+ return false;
+ }
+ for(unsigned long j=0; j < zeroSize[i]; ++j)
+ {
+ f << '\0';
+ }
+ if(!f)
+ {
+ if(emsg)
+ {
+ *emsg = "Error writing the empty rpath string to the file.";
+ }
+ return false;
}
- return false;
- }
- for(unsigned long i=0; i < rpathSize; ++i)
- {
- f << '\0';
}
- // Make sure everything was okay.
- if(f)
- {
- return true;
- }
- else
+ // Everything was updated successfully.
+ if(removed)
{
- if(emsg)
- {
- *emsg = "Error writing the empty rpath to the file.";
- }
- return false;
+ *removed = true;
}
+ return true;
#else
(void)file;
(void)emsg;
+ (void)removed;
return false;
#endif
}
diff --git cmake-2.6.1-orig/Source/cmSystemTools.h cmake-2.6.1/Source/cmSystemTools.h
index 493ff71..1ff12bf 100644
--- cmake-2.6.1-orig/Source/cmSystemTools.h
+++ cmake-2.6.1/Source/cmSystemTools.h
@@ -396,7 +396,8 @@ public:
bool* changed = 0);
/** Try to remove the RPATH from an ELF binary. */
- static bool RemoveRPath(std::string const& file, std::string* emsg = 0);
+ static bool RemoveRPath(std::string const& file, std::string* emsg = 0,
+ bool* removed = 0);
/** Check whether the RPATH in an ELF binary contains the path
given. */
1.1 dev-util/cmake/files/cmake-2.6.1-gc-sections.patch
file : http://sources.gentoo.org/viewcvs.py/gentoo-x86/dev-util/cmake/files/cmake-2.6.1-gc-sections.patch?rev=1.1&view=markup
plain: http://sources.gentoo.org/viewcvs.py/gentoo-x86/dev-util/cmake/files/cmake-2.6.1-gc-sections.patch?rev=1.1&content-type=text/plain
Index: cmake-2.6.1-gc-sections.patch
===================================================================
Fix compatibility with -Wl,--gc-sections.
http://www.cmake.org/pipermail/cmake/2008-August/023240.html
http://www.cmake.org/pipermail/cmake-commits/2008-August/004477.html
--- Modules/CheckTypeSizeC.c.in
+++ Modules/CheckTypeSizeC.c.in
@@ -29,15 +29,16 @@
('0' + (SIZE % 10)),
']','\0'};
-
#ifdef __CLASSIC_C__
-int main(){
- int ac;
- char*av[];
+int main(argc, argv) int argc; char *argv[];
#else
-int main(int ac, char*av[]){
+int main(int argc, char *argv[])
#endif
- return (&info_sizeof[0] != &info_sizeof[0]);
+{
+ int require = 0;
+ require += info_sizeof[argc];
+ (void)argv;
+ return require;
}
#else /* CHECK_TYPE_SIZE_TYPE */
--- Modules/CMakeCCompilerABI.c
+++ Modules/CMakeCCompilerABI.c
@@ -12,17 +12,17 @@
/*--------------------------------------------------------------------------*/
-/* Make sure the information strings are referenced. */
-#define REQUIRE(x) (&x[0] != &require)
-
-int main()
+#ifdef __CLASSIC_C__
+int main(argc, argv) int argc; char *argv[];
+#else
+int main(int argc, char *argv[])
+#endif
{
- const char require = 0;
- return
- (
- REQUIRE(info_sizeof_dptr)
+ int require = 0;
+ require += info_sizeof_dptr[argc];
#if defined(ABI_ID)
- && REQUIRE(info_abi)
+ require += info_abi[argc];
#endif
- );
+ (void)argv;
+ return require;
}
--- Modules/CMakeCCompilerId.c.in
+++ Modules/CMakeCCompilerId.c.in
@@ -2,15 +2,9 @@
# error "A C++ compiler has been selected for C."
#endif
-/* Provide main() so the program can link. */
#if defined(__18CXX)
# define ID_VOID_MAIN
#endif
-#ifdef ID_VOID_MAIN
-void main() {}
-#else
-int main() { return 0; }
-#endif
#if defined(__INTEL_COMPILER) || defined(__ICC)
# define COMPILER_ID "Intel"
@@ -82,3 +76,18 @@
char* info_compiler = "INFO" ":" "compiler[" COMPILER_ID "]";
@CMAKE_C_COMPILER_ID_PLATFORM_CONTENT@
+
+/*--------------------------------------------------------------------------*/
+
+#ifdef ID_VOID_MAIN
+void main() {}
+#else
+int main(int argc, char* argv[])
+{
+ int require = 0;
+ require += info_compiler[argc];
+ require += info_platform[argc];
+ (void)argv;
+ return require;
+}
+#endif
--- Modules/CMakeCXXCompilerABI.cpp
+++ Modules/CMakeCXXCompilerABI.cpp
@@ -8,17 +8,13 @@
/*--------------------------------------------------------------------------*/
-/* Make sure the information strings are referenced. */
-#define REQUIRE(x) (&x[0] != &require)
-
-int main()
+int main(int argc, char* argv[])
{
- const char require = 0;
- return
- (
- REQUIRE(info_sizeof_dptr)
+ int require = 0;
+ require += info_sizeof_dptr[argc];
#if defined(ABI_ID)
- && REQUIRE(info_abi)
+ require += info_abi[argc];
#endif
- );
+ (void)argv;
+ return require;
}
--- Modules/CMakeCXXCompilerId.cpp.in
+++ Modules/CMakeCXXCompilerId.cpp.in
@@ -5,9 +5,6 @@
# error "A C compiler has been selected for C++."
#endif
-/* Provide main() so the program can link. */
-int main() { return 0; }
-
#if defined(__COMO__)
# define COMPILER_ID "Comeau"
@@ -70,3 +67,14 @@
char* info_compiler = "INFO" ":" "compiler[" COMPILER_ID "]";
@CMAKE_CXX_COMPILER_ID_PLATFORM_CONTENT@
+
+/*--------------------------------------------------------------------------*/
+
+int main(int argc, char* argv[])
+{
+ int require = 0;
+ require += info_compiler[argc];
+ require += info_platform[argc];
+ (void)argv;
+ return require;
+}
--- Modules/TestEndianess.c.in
+++ Modules/TestEndianess.c.in
@@ -10,11 +10,14 @@
const cmakeint16 info_big[] = {0x5448, 0x4953, 0x2049, 0x5320, 0x4249, 0x4720, 0x454e, 0x4449, 0x414e, 0x2e2e, 0x0000};
#ifdef __CLASSIC_C__
-int main(){
- int ac;
- char*av[];
+int main(argc, argv) int argc; char *argv[];
#else
-int main(int ac, char*av[]){
+int main(int argc, char *argv[])
#endif
- return (&info_little[0] != &info_big[0]);
+{
+ int require = 0;
+ require += info_little[argc];
+ require += info_big[argc];
+ (void)argv;
+ return require;
}
next reply other threads:[~2008-08-27 21:19 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-08-27 21:19 Timo Gurr (tgurr) [this message]
-- strict thread matches above, loose matches on Subject: below --
2008-12-02 23:25 [gentoo-commits] gentoo-x86 commit in dev-util/cmake/files: cmake-2.6.1-rpath.patch cmake-2.6.1-gc-sections.patch Tomas Chvatal (scarabeus)
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=E1KYSQ3-0001kV-BK@stork.gentoo.org \
--to=tgurr@gentoo.org \
--cc=gentoo-commits@lists.gentoo.org \
--cc=gentoo-dev@lists.gentoo.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox