public inbox for cygwin-apps@cygwin.com
 help / color / mirror / Atom feed
From: Jon Turney <jon.turney@dronecode.org.uk>
To: cygwin-apps@cygwin.com
Cc: Jon Turney <jon.turney@dronecode.org.uk>
Subject: [PATCH setup 13/16] Split out hash checking progress reporting
Date: Fri,  8 Mar 2024 18:34:32 +0000	[thread overview]
Message-ID: <20240308183440.4263-14-jon.turney@dronecode.org.uk> (raw)
In-Reply-To: <20240308183440.4263-1-jon.turney@dronecode.org.uk>

---
 Feedback.h                  |  4 ++++
 Makefile.am                 |  2 ++
 choose.cc                   |  4 +++-
 cli/CliFeedback.h           |  5 +++++
 cli/CliHashCheckFeedback.cc | 30 ++++++++++++++++++++++++++++++
 download.cc                 | 24 ++++++++++++------------
 download.h                  |  6 +++---
 gui/GuiFeedback.h           |  5 +++++
 gui/GuiHashCheckFeedback.cc | 34 ++++++++++++++++++++++++++++++++++
 install.cc                  |  4 +++-
 package_db.cc               |  6 +++---
 package_db.h                |  3 ++-
 package_meta.cc             | 10 +++++-----
 package_meta.h              |  5 +++--
 package_source.cc           | 33 ++++++++++-----------------------
 package_source.h            |  8 +++++---
 16 files changed, 129 insertions(+), 54 deletions(-)
 create mode 100644 cli/CliHashCheckFeedback.cc
 create mode 100644 gui/GuiHashCheckFeedback.cc

diff --git a/Feedback.h b/Feedback.h
index 8f603a6..4db7c4a 100644
--- a/Feedback.h
+++ b/Feedback.h
@@ -47,6 +47,10 @@ public:
   virtual void fetch_finish (int total_bytes) = 0;
   virtual void fetch_fatal (const char *filename, const char *err) = 0;
 
+  // hash checking
+  virtual void hash_init (const char *hashalg, const std::string &url) = 0;
+  virtual void hash_progress (int bytes, int total_bytes) = 0;
+
   //
   virtual HWND owner () = 0;
 };
diff --git a/Makefile.am b/Makefile.am
index f257e3a..def20a4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -67,6 +67,7 @@ inilint_SOURCES = \
 	filemanip.h \
 	cli/CliParseFeedback.cc \
 	cli/CliGetUrlFeedback.cc \
+	cli/CliHashCheckFeedback.cc \
 	cli/CliFeedback.h \
 	LogSingleton.cc \
 	LogSingleton.h \
@@ -186,6 +187,7 @@ endif
 	gui/GuiGetNetAuth.h \
 	gui/GuiGetUrlFeedback.cc \
 	gui/GuiFeedback.h \
+	gui/GuiHashCheckFeedback.cc \
 	ini.cc \
 	ini.h \
 	IniDBBuilder.h \
diff --git a/choose.cc b/choose.cc
index 8deab87..451b390 100644
--- a/choose.cc
+++ b/choose.cc
@@ -50,6 +50,7 @@
 #include "package_meta.h"
 
 #include "threebar.h"
+#include "gui/GuiFeedback.h"
 #include "Generic.h"
 #include "ControlAdjuster.h"
 #include "prereq.h"
@@ -317,9 +318,10 @@ void
 ChooserPage::OnActivate()
 {
   SetBusy();
+  GuiFeedback feedback(GetHWND());
 
   packagedb db;
-  db.prep();
+  db.prep(feedback);
 
   if (!activated)
     {
diff --git a/cli/CliFeedback.h b/cli/CliFeedback.h
index 3bcc23c..cb59650 100644
--- a/cli/CliFeedback.h
+++ b/cli/CliFeedback.h
@@ -48,6 +48,11 @@ private:
   unsigned int last_tics;
   unsigned int start_tics;
 
+  // hash checking
+public:
+  void hash_init (const char *hashalg, const std::string &url);
+  void hash_progress (int bytes, int total_bytes);
+
   // owner
 public:
   HWND owner () { return NULL; }
diff --git a/cli/CliHashCheckFeedback.cc b/cli/CliHashCheckFeedback.cc
new file mode 100644
index 0000000..f5df9fc
--- /dev/null
+++ b/cli/CliHashCheckFeedback.cc
@@ -0,0 +1,30 @@
+/*
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     A copy of the GNU General Public License can be found at
+ *     http://www.gnu.org/
+ *
+ */
+
+#include "cli/CliFeedback.h"
+#include "resource.h"
+#include "String++.h"
+#include <iostream>
+
+void
+CliFeedback::hash_init(const char *hashalg, const std::string &shortname)
+{
+  std::wstring fmt = LoadStringW(IDS_PROGRESS_CHECKING_HASH);
+  std::wstring s = format(fmt, hashalg, shortname.c_str());
+  std::cout << wstring_to_string(s) << std::endl;
+}
+
+void
+CliFeedback::hash_progress(int bytes, int total_bytes)
+{
+  std::cout << bytes << "/" << total_bytes << "\r";
+}
diff --git a/download.cc b/download.cc
index 02fd484..fbe36e5 100644
--- a/download.cc
+++ b/download.cc
@@ -52,14 +52,14 @@ extern ThreeBarProgressPage Progress;
 // user chooses to delete the file; otherwise throw an exception.
 static bool
 validateCachedPackage (const std::string& fullname, packagesource & pkgsource,
-		       HWND owner, bool check_hash, bool check_size)
+                       Feedback &feedback, bool check_hash, bool check_size)
 {
   try
     {
       if (check_size)
 	pkgsource.check_size_and_cache (fullname);
       if (check_hash)
-	pkgsource.check_hash ();
+	pkgsource.check_hash (feedback);
       return true;
     }
   catch (Exception *e)
@@ -69,7 +69,7 @@ validateCachedPackage (const std::string& fullname, packagesource & pkgsource,
       if (strncmp (filename, "file://", 7) == 0)
 	filename += 7;
       if (e->errNo() == APPERR_CORRUPT_PACKAGE
-	  && yesno (owner, IDS_QUERY_CORRUPT, filename) == IDYES)
+	  && yesno (feedback.owner(), IDS_QUERY_CORRUPT, filename) == IDYES)
 	remove (filename);
       else
 	throw e;
@@ -80,8 +80,8 @@ validateCachedPackage (const std::string& fullname, packagesource & pkgsource,
 /* 0 if not cached; may throw exception if validation fails.
  */
 int
-check_for_cached (packagesource & pkgsource, HWND owner, bool mirror_mode,
-		  bool check_hash)
+check_for_cached (packagesource & pkgsource, Feedback &feedback,
+                  bool mirror_mode, bool check_hash)
 {
   /* If the packagesource doesn't have a filename, it can't possibly be in the
      cache */
@@ -105,7 +105,7 @@ check_for_cached (packagesource & pkgsource, HWND owner, bool mirror_mode,
   // Already found one, which we can assume to have the right size.
   if (pkgsource.Cached())
     {
-      if (validateCachedPackage (pkgsource.Cached(), pkgsource, owner,
+      if (validateCachedPackage (pkgsource.Cached(), pkgsource, feedback,
 				 check_hash, false))
 	return 1;
       // If we get here, pkgsource.Cached() was corrupt and deleted.
@@ -117,7 +117,7 @@ check_for_cached (packagesource & pkgsource, HWND owner, bool mirror_mode,
   */
   if (io_stream::exists (fullname))
     {
-      if (validateCachedPackage (fullname, pkgsource, owner, check_hash, true))
+      if (validateCachedPackage (fullname, pkgsource, feedback, check_hash, true))
 	return 1;
       // If we get here, fullname was corrupt and deleted, but it
       // might have been cached.
@@ -134,7 +134,7 @@ check_for_cached (packagesource & pkgsource, HWND owner, bool mirror_mode,
       pkgsource.Canonical ();
     if (io_stream::exists(fullname))
 	{
-	  if (validateCachedPackage (fullname, pkgsource, owner, check_hash,
+	  if (validateCachedPackage (fullname, pkgsource, feedback, check_hash,
 				     true))
 	    return 1;
 	  // If we get here, fullname was corrupt and deleted, but it
@@ -151,7 +151,7 @@ download_one (packagesource & pkgsource, Feedback &feedback)
 {
   try
     {
-      if (check_for_cached (pkgsource, feedback.owner()))
+      if (check_for_cached (pkgsource, feedback))
         return 0;
     }
   catch (Exception * e)
@@ -190,7 +190,7 @@ download_one (packagesource & pkgsource, Feedback &feedback)
 		remove (local.c_str());
 	      rename ((local + ".tmp").c_str(), local.c_str());
 	      pkgsource.check_size_and_cache ("file://" + local);
-	      pkgsource.check_hash ();
+	      pkgsource.check_hash (feedback);
 	      Log (LOG_PLAIN) << "Downloaded " << local << endLog;
 	      success = 1;
 	      // FIXME: move the downloaded file to the 
@@ -277,6 +277,7 @@ do_download_thread (HINSTANCE h, HWND owner)
   int errors = 0;
   download_failures.clear ();
 
+  GuiFeedback feedback(owner);
   Progress.SetText1 (IDS_PROGRESS_CHECKING);
   Progress.SetText2 ("");
   Progress.SetText3 ("");
@@ -294,7 +295,7 @@ do_download_thread (HINSTANCE h, HWND owner)
 
       try
         {
-          if (!check_for_cached (*version.source(), owner))
+          if (!check_for_cached (*version.source(), feedback))
             total_download_bytes += version.source()->size;
         }
       catch (Exception * e)
@@ -308,7 +309,6 @@ do_download_thread (HINSTANCE h, HWND owner)
       Progress.SetBar2(std::distance(t.begin(), i) + 1, t.size());
     }
 
-  GuiFeedback feedback(owner);
   feedback.fetch_set_total_length(total_download_bytes);
 
   /* and do the download. FIXME: This here we assign a new name for the cached version
diff --git a/download.h b/download.h
index 9f4cb8e..3f65153 100644
--- a/download.h
+++ b/download.h
@@ -16,10 +16,10 @@
 #ifndef SETUP_DOWNLOAD_H
 #define SETUP_DOWNLOAD_H
 
-#include "win32.h"
+#include "Feedback.h"
 
 class packagesource;
-int check_for_cached (packagesource & pkgsource, HWND owner,
-		      bool mirror_mode = false, bool check_hash = true);
+int check_for_cached (packagesource & pkgsource, Feedback &feedback,
+                      bool mirror_mode = false, bool check_hash = true);
 
 #endif /* SETUP_DOWNLOAD_H */
diff --git a/gui/GuiFeedback.h b/gui/GuiFeedback.h
index 1dcd42f..7ac0c80 100644
--- a/gui/GuiFeedback.h
+++ b/gui/GuiFeedback.h
@@ -55,6 +55,11 @@ private:
   long long int total_download_bytes_sofar = 0;
   DWORD start_tics;
 
+  // hash checking
+public:
+  void hash_init (const char *hashalg, const std::string &url);
+  void hash_progress (int bytes, int total_bytes);
+
 public:
   // owner
   HWND owner () { return owner_window; }
diff --git a/gui/GuiHashCheckFeedback.cc b/gui/GuiHashCheckFeedback.cc
new file mode 100644
index 0000000..a0b4c57
--- /dev/null
+++ b/gui/GuiHashCheckFeedback.cc
@@ -0,0 +1,34 @@
+/*
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     A copy of the GNU General Public License can be found at
+ *     http://www.gnu.org/
+ *
+ */
+
+#include "gui/GuiFeedback.h"
+#include "resource.h"
+#include "threebar.h"
+#include "String++.h"
+
+extern ThreeBarProgressPage Progress;
+
+void
+GuiFeedback::hash_init(const char *hashalg, const std::string &shortname)
+{
+  std::wstring fmt = LoadStringW(IDS_PROGRESS_CHECKING_HASH);
+  std::wstring s = format(fmt, hashalg, shortname.c_str());
+  Progress.SetText1(s.c_str());
+  Progress.SetText4(IDS_PROGRESS_PROGRESS);
+  Progress.SetBar1(0);
+}
+
+void
+GuiFeedback::hash_progress(int bytes, int total_bytes)
+{
+  Progress.SetBar1(bytes, total_bytes);
+}
diff --git a/install.cc b/install.cc
index 001529b..578ff7f 100644
--- a/install.cc
+++ b/install.cc
@@ -62,6 +62,7 @@
 #include "threebar.h"
 #include "Exception.h"
 #include "processlist.h"
+#include "gui/GuiFeedback.h"
 
 extern ThreeBarProgressPage Progress;
 
@@ -805,6 +806,7 @@ static void
 do_install_thread (HINSTANCE h, HWND owner)
 {
   int i;
+  GuiFeedback feedback(owner);
 
   num_installs = 0, num_uninstalls = 0;
   rebootneeded = false;
@@ -870,7 +872,7 @@ do_install_thread (HINSTANCE h, HWND owner)
     {
       try
       {
-        (*version.source ()).check_hash ();
+        (*version.source ()).check_hash (feedback);
       }
       catch (Exception *e)
       {
diff --git a/package_db.cc b/package_db.cc
index 26ecc3c..244b6bb 100644
--- a/package_db.cc
+++ b/package_db.cc
@@ -752,7 +752,7 @@ packagedb::fixup_source_package_ids()
 }
 
 void
-packagedb::prep()
+packagedb::prep(Feedback &feedback)
 {
   /* make packagedb ready for use for chooser */
   if (prepped)
@@ -767,8 +767,8 @@ packagedb::prep()
 
   /* XXX: this needs to be broken out somewhere where it can do progress
      reporting, as it can take a long time... */
-  if (source == IDC_SOURCE_DOWNLOAD || source ==IDC_SOURCE_LOCALDIR)
-    packagemeta::ScanDownloadedFiles (MirrorOption);
+  if (source == IDC_SOURCE_DOWNLOAD || source == IDC_SOURCE_LOCALDIR)
+    packagemeta::ScanDownloadedFiles (MirrorOption, feedback);
 
   setExistence ();
   fillMissingCategory ();
diff --git a/package_db.h b/package_db.h
index 3886bf2..c3c2b05 100644
--- a/package_db.h
+++ b/package_db.h
@@ -23,6 +23,7 @@
 class packagemeta;
 class io_stream;
 class PackageSpecification;
+class Feedback;
 
 typedef enum {
   PackageDB_Install,
@@ -67,7 +68,7 @@ public:
   void init();
   /* 0 on success */
   int flush ();
-  void prep();
+  void prep(Feedback &);
   /* Set the database to a "no changes requested" state.  */
   void noChanges ();
 
diff --git a/package_meta.cc b/package_meta.cc
index 3daa970..0c5e2f5 100644
--- a/package_meta.cc
+++ b/package_meta.cc
@@ -833,7 +833,7 @@ packagemeta::logSelectionStatus() const
 
 /* scan for local copies of package */
 bool
-packagemeta::scan (const packageversion &pkg, bool mirror_mode)
+packagemeta::scan (const packageversion &pkg, bool mirror_mode, Feedback &feedback)
 {
   /* empty version */
   if (!pkg)
@@ -841,7 +841,7 @@ packagemeta::scan (const packageversion &pkg, bool mirror_mode)
 
   try
     {
-      if (!check_for_cached (*(pkg.source ()), NULL, mirror_mode, false)
+      if (!check_for_cached (*(pkg.source ()), feedback, mirror_mode, false)
           && ::source == IDC_SOURCE_LOCALDIR)
         return false;
     }
@@ -859,7 +859,7 @@ packagemeta::scan (const packageversion &pkg, bool mirror_mode)
 }
 
 void
-packagemeta::ScanDownloadedFiles (bool mirror_mode)
+packagemeta::ScanDownloadedFiles (bool mirror_mode, Feedback &feedback)
 {
   /* Look at every known package, in all the known mirror dirs,
    * and fill in the Cached attribute if it exists.
@@ -877,10 +877,10 @@ packagemeta::ScanDownloadedFiles (bool mirror_mode)
 			   && (*i != pkg.installed
 			       || pkg.installed == pkg.curr
 			       || pkg.installed == pkg.exp);
-	  bool accessible = scan (*i, lazy_scan);
+	  bool accessible = scan (*i, lazy_scan, feedback);
 	  packageversion foo = *i;
 	  packageversion pkgsrcver = foo.sourcePackage ();
-	  bool src_accessible = scan (pkgsrcver, lazy_scan);
+	  bool src_accessible = scan (pkgsrcver, lazy_scan, feedback);
 
 	  /* For local installs, if there is no src and no bin, the version
 	   * is unavailable
diff --git a/package_meta.h b/package_meta.h
index fee385b..896d848 100644
--- a/package_meta.h
+++ b/package_meta.h
@@ -19,6 +19,7 @@
 class SolvableVersion;
 typedef SolvableVersion packageversion;
 class packagemeta;
+class Feedback;
 
 #include <set>
 #include <vector>
@@ -34,7 +35,7 @@ typedef std::pair<const std::string, std::vector<packagemeta *> > Category;
 class packagemeta
 {
 public:
-  static void ScanDownloadedFiles (bool);
+  static void ScanDownloadedFiles (bool, Feedback&);
   packagemeta (packagemeta const &);
   packagemeta (const std::string& pkgname)
     : name (pkgname), user_picked (false),
@@ -162,7 +163,7 @@ protected:
 private:
   std::string trustLabel(packageversion const &) const;
   std::vector <Script> scripts_;
-  static bool scan (const packageversion &pkg, bool mirror_mode);
+  static bool scan (const packageversion &pkg, bool mirror_mode, Feedback &feedback);
   const packageversion * findVersion(std::string &version) const;
 
   _actions _action;
diff --git a/package_source.cc b/package_source.cc
index 2b5909b..a652a3b 100644
--- a/package_source.cc
+++ b/package_source.cc
@@ -21,13 +21,10 @@
 #include "sha2.h"
 #include "csu_util/MD5Sum.h"
 #include "LogFile.h"
-#include "threebar.h"
 #include "Exception.h"
 #include "filemanip.h"
 #include "io_stream.h"
-#include "resource.h"
-
-extern ThreeBarProgressPage Progress;
+#include "Feedback.h"
 
 site::site (const std::string& newkey) : key(newkey)
 {
@@ -65,19 +62,19 @@ packagesource::check_size_and_cache (const std::string fullname)
 }
 
 void
-packagesource::check_hash ()
+packagesource::check_hash (Feedback &feedback)
 {
   if (validated || cached.empty ())
     return;
 
   if (sha512_isSet)
     {
-      check_sha512 (cached);
+      check_sha512 (cached, feedback);
       validated = true;
     }
   else if (md5.isSet())
     {
-      check_md5 (cached);
+      check_md5 (cached, feedback);
       validated = true;
     }
   else
@@ -97,7 +94,7 @@ sha512_str (const unsigned char *in, char *buf)
 }
 
 void
-packagesource::check_sha512 (const std::string fullname) const
+packagesource::check_sha512 (const std::string fullname, Feedback &feedback) const
 {
   io_stream *thefile = io_stream::open (fullname, "rb", 0);
   if (!thefile)
@@ -112,19 +109,14 @@ packagesource::check_sha512 (const std::string fullname) const
   SHA512Init (&ctx);
 
   Log (LOG_BABBLE) << "Checking SHA512 for " << fullname << endLog;
-
-  std::wstring fmt = LoadStringW(IDS_PROGRESS_CHECKING_HASH);
-  std::wstring s = format(fmt, "SHA512", shortname.c_str());
-  Progress.SetText1 (s.c_str());
-  Progress.SetText4 (IDS_PROGRESS_PROGRESS);
-  Progress.SetBar1 (0);
+  feedback.hash_init("SHA512", shortname);
 
   unsigned char buffer[64 * 1024];
   ssize_t count;
   while ((count = thefile->read (buffer, sizeof (buffer))) > 0)
   {
     SHA512Update (&ctx, buffer, count);
-    Progress.SetBar1 (thefile->tell (), thefile->get_size ());
+    feedback.hash_progress(thefile->tell (), thefile->get_size ());
   }
   delete thefile;
   if (count < 0)
@@ -152,7 +144,7 @@ packagesource::check_sha512 (const std::string fullname) const
 }
 
 void
-packagesource::check_md5 (const std::string fullname) const
+packagesource::check_md5 (const std::string fullname, Feedback &feedback) const
 {
   io_stream *thefile = io_stream::open (fullname, "rb", 0);
   if (!thefile)
@@ -163,19 +155,14 @@ packagesource::check_md5 (const std::string fullname) const
   tempMD5.begin ();
 
   Log (LOG_BABBLE) << "Checking MD5 for " << fullname << endLog;
-
-  std::wstring fmt = LoadStringW(IDS_PROGRESS_CHECKING_HASH);
-  std::wstring s = format(fmt, "MD5", shortname);
-  Progress.SetText1 (s.c_str());
-  Progress.SetText4 (IDS_PROGRESS_PROGRESS);
-  Progress.SetBar1 (0);
+  feedback.hash_init("MD5", shortname);
 
   unsigned char buffer[64 * 1024];
   ssize_t count;
   while ((count = thefile->read (buffer, sizeof (buffer))) > 0)
     {
       tempMD5.append (buffer, count);
-      Progress.SetBar1 (thefile->tell (), thefile->get_size ());
+      feedback.hash_progress(thefile->tell (), thefile->get_size ());
     }
   delete thefile;
   if (count < 0)
diff --git a/package_source.h b/package_source.h
index fae46f7..663a603 100644
--- a/package_source.h
+++ b/package_source.h
@@ -26,6 +26,8 @@
 #include "csu_util/MD5Sum.h"
 #include <vector>
 
+class Feedback;
+
 class site
 {
 public:
@@ -79,7 +81,7 @@ public:
   MD5Sum md5;
   /* The next two functions throw exceptions on failure.  */
   void check_size_and_cache (const std::string fullname);
-  void check_hash ();
+  void check_hash (Feedback &feedback);
   typedef std::vector <site> sitestype;
   sitestype sites;
 
@@ -89,8 +91,8 @@ private:
   std::string shortname;
   std::string cached;
   bool validated;
-  void check_sha512 (const std::string fullname) const;
-  void check_md5 (const std::string fullname) const;
+  void check_sha512 (const std::string fullname, Feedback &feedback) const;
+  void check_md5 (const std::string fullname, Feedback &feedback) const;
 };
 
 #endif /* SETUP_PACKAGE_SOURCE_H */
-- 
2.43.0


  parent reply	other threads:[~2024-03-08 18:36 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-03-08 18:34 [PATCH setup 00/16] Groundwork for a GUI-less installation tool Jon Turney
2024-03-08 18:34 ` [PATCH setup 01/16] Drop forward declaration of non-existent class IniState Jon Turney
2024-03-08 18:34 ` [PATCH setup 02/16] Move setup_exts[] to the only place it's used Jon Turney
2024-03-08 18:34 ` [PATCH setup 03/16] Split GuiParseFeedback out from ini fetcher Jon Turney
2024-03-08 18:34 ` [PATCH setup 04/16] Split out site into SiteSettings and SitePage Jon Turney
2024-03-08 18:34 ` [PATCH setup 05/16] Don't call Antivirus::AtExit() directly from Logger::exit() Jon Turney
2024-03-08 18:34 ` [PATCH setup 06/16] Simplify invocation of UserSettings::open_settings() Jon Turney
2024-03-08 18:34 ` [PATCH setup 07/16] Split out URL fetching progress reporting Jon Turney
2024-03-08 18:34 ` [PATCH setup 08/16] Instantiate found_ini_list in ini.cc Jon Turney
2024-03-08 18:34 ` [PATCH setup 09/16] Move is_64bit to state Jon Turney
2024-03-08 18:34 ` [PATCH setup 10/16] Move setup.ini pathame components to ini.cc Jon Turney
2024-03-08 18:34 ` [PATCH setup 11/16] Drop hinstance global Jon Turney
2024-03-08 18:34 ` [PATCH setup 12/16] Spit out GetNetAuth from NetIO Jon Turney
2024-03-08 18:34 ` Jon Turney [this message]
2024-03-08 18:34 ` [PATCH setup 14/16] Push check_for_cached into package_source Jon Turney
2024-03-08 18:34 ` [PATCH setup 15/16] Put various shared subcomponents into a convenience library Jon Turney
2024-03-08 18:34 ` [PATCH setup 16/16] Add beginnings of a command line installation tool Jon Turney

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=20240308183440.4263-14-jon.turney@dronecode.org.uk \
    --to=jon.turney@dronecode.org.uk \
    --cc=cygwin-apps@cygwin.com \
    /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;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).