[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Columnar package list style (patch included)



Hey Super Cow Power team,

While browsing through the package list of a recent "apt-get dist-upgrade" I thought that it'd be useful to show the list of packages in columnar format, just like the traditional "ls" command.

Would anyone be interested in this feature?

Since I have no idea how to send a proper apt-style "pull request", I have attached a patch that implements it (should apply cleanly to master). Do tell me how to do it properly or push me in the right direction :)

Cheers,

Christian Blichmann


PS:

Example output of a patched apt-get:
$ sudo -i
# echo 'APT::Get::Show-Columns "true";' > /etc/apt/apt.conf.d/90columns
# apt-get dist-upgrade
Reading package lists... Done
Building dependency tree
Reading state information... Done
Calculating upgrade... Done
The following packages were automatically installed and are no longer required:
  libasn1-8-heimdal    libheimntlm0-heimdal libkrb5-26-heimdal libtrio2
libgssapi3-heimdal libhx509-5-heimdal libntdb1 libwind0-heimdal libhcrypto4-heimdal libilmbase6v5 libopenexr6v5 libx265-68
  libheimbase1-heimdal libjsoncpp0v5        libroken18-heimdal
Use 'sudo apt autoremove' to remove them.
The following packages will be REMOVED:
  fonts-droid
The following NEW packages will be installed:
  fonts-droid-fallback libcdk5             libjsoncpp1  libwacom-bin
  fonts-noto-mono      libgudev-1.0-0:i386 libopenjp2-7 libwacom2:i386
The following packages will be upgraded:
  abiword                      keyboard-configuration    libcups2
  abiword-common               libabiword-3.0            libcupscgi1
  abiword-plugin-grammar       libatk-bridge2.0-0        libcupsimage2
  binfmt-support               libatk-bridge2.0-dev      libcupsmime1
  cmake                        libatk-wrapper-java       libcupsppdc1
console-setup libatk-wrapper-java-jni libdebconfclient0 console-setup-linux libav-tools libdevmapper-event1.02.1 coreutils libavcodec-ffmpeg56 libdevmapper1.02.1 cups libavdevice-ffmpeg56 libdirectfb-1.2-9 cups-bsd libavfilter-ffmpeg5 libebackend-1.2-10
  cups-client                  libavformat-ffmpeg56      libebook-1.2-16
cups-common libavresample-ffmpeg2 libebook-contacts-1.2-2
  cups-core-drivers            libavutil-ffmpeg54        libecal-1.2-19
cups-daemon libboost-filesystem1.58.0 libedata-book-1.2-25 cups-ppdc libboost-iostreams1.58.0 libedata-cal-1.2-28 cups-server-common libboost-system1.58.0 libedataserver-1.2-21 debootstrap libboost-thread1.58.0 libfftw3-double3 dh-make libc-ares2 libfftw3-single3 dmeventd libc-bin libfreerdp-cache1.1 dmsetup libc-dev-bin libfreerdp-client1.1 evolution-data-server libc-l10n libfreerdp-codec1.1 evolution-data-server-common libc6 libfreerdp-common1.1.0 ffmpeg libc6:i386 libfreerdp-core1.1 ghostscript libc6-dbg libfreerdp-crypto1.1 gir1.2-gtk-3.0 libc6-dev libfreerdp-gdi1.1 gir1.2-upowerglib-1.0 libc6-dev:i386 libfreerdp-locale1.1 gnome-online-accounts libc6-dev-i386 libfreerdp-primitives1.1 google-chrome-beta libc6-dev-x32 libfreerdp-utils1.1
  gphoto2                      libc6-i386                libgail-3-0
gstreamer1.0-clutter-3.0 libc6-i686:i386 libgeocode-glib0
  gstreamer1.0-plugins-bad     libc6-x32                 libgoa-1.0-0b
  hostname                     libcamel-1.2-54
  iproute2                     libclutter-gst-3.0-0
[...]
213 upgraded, 8 newly installed, 1 to remove and 0 not upgraded.
Need to get 232 MB of archives.
After this operation, 3,471 kB disk space will be freed.
Do you want to continue? [Y/n] n

PPS: I resent this mail, my first one was black-/grey-/whatever-listed.
diff --git a/apt-private/private-cmndline.cc b/apt-private/private-cmndline.cc
index 8290859..406f9ba 100644
--- a/apt-private/private-cmndline.cc
+++ b/apt-private/private-cmndline.cc
@@ -173,7 +173,8 @@ static bool addArgumentsAPTGet(std::vector<CommandLine::Args> &Args, char const
       addArg(0, "show-progress", "DpkgPM::Progress", 0);
       addArg('f', "fix-broken", "APT::Get::Fix-Broken", 0);
       addArg(0, "purge", "APT::Get::Purge", 0);
-      addArg('V',"verbose-versions","APT::Get::Show-Versions",0);
+      addArg('V', "verbose-versions", "APT::Get::Show-Versions", 0);
+      addArg(0, "list-columns", "APT::Get::Show-Columns", 0);
       addArg(0, "auto-remove", "APT::Get::AutomaticRemove", 0);
       addArg(0, "reinstall", "APT::Get::ReInstall", 0);
       addArg(0, "solver", "APT::Solver", CommandLine::HasArg);
diff --git a/apt-private/private-output.cc b/apt-private/private-output.cc
index 458d78d..81e08ef 100644
--- a/apt-private/private-output.cc
+++ b/apt-private/private-output.cc
@@ -24,6 +24,7 @@
 #include <unistd.h>
 #include <signal.h>
 #include <sys/ioctl.h>
+#include <vector>
 
 #include <sstream>
 
@@ -301,6 +302,82 @@ void ListSingleVersion(pkgCacheFile &CacheFile, pkgRecords &records,	/*{{{*/
    out << output;
 }
 									/*}}}*/
+// ShowColumnarList - Show a list in the style of ls			/*{{{*/
+// ---------------------------------------------------------------------
+/* This prints out a vector of strings with the given indent and in as
+   many columns as will fit the screen width.
+   
+   The output looks like:
+  abiword                debootstrap                  gir1.2-upowerglib-1.0
+  abiword-common         dh-make                      google-chrome-beta
+  abiword-plugin-grammar dmeventd                     gstreamer1.0-clutter-3.0
+  binfmt-support         dmsetup                      hostname
+  console-setup          evolution-data-server        iproute2
+  console-setup-linux    evolution-data-server-common
+  coreutils              ffmpeg
+ */
+struct columnInfo
+{
+   bool ValidLen;
+   size_t LineWidth;
+   vector<size_t> RemainingWidths;
+};
+void ShowColumnarList(ostream &out, vector<string> const &List, size_t Indent, size_t ScreenWidth)
+{
+   enum { MinColumnWidth = 2, ColumnSpace = 1 };
+
+   size_t const ListSize = List.size();
+   size_t const MaxScreenCols = (ScreenWidth - Indent) /
+         MinColumnWidth;
+   size_t const MaxNumCols = min(MaxScreenCols, ListSize);
+
+   vector<columnInfo> ColumnInfo(MaxNumCols);
+   for (size_t I = 0; I < MaxNumCols; ++I) {
+      ColumnInfo[I].ValidLen = true;
+      ColumnInfo[I].LineWidth = (I + 1) * MinColumnWidth;
+      ColumnInfo[I].RemainingWidths.resize(I + 1, MinColumnWidth);
+   }
+
+   for (size_t I = 0; I < ListSize; ++I) {
+      for (size_t J = 0; J < MaxNumCols; ++J) {
+         auto& Col = ColumnInfo[J];
+         if (!Col.ValidLen)
+            continue;
+
+         size_t Idx = I / ((ListSize + J) / (J + 1));
+         size_t RealColLen = List[I].size() + (Idx == J ? 0 : ColumnSpace);
+         if (Col.RemainingWidths[Idx] < RealColLen) {
+            Col.LineWidth += RealColLen - Col.RemainingWidths[Idx];
+            Col.RemainingWidths[Idx] = RealColLen;
+            Col.ValidLen = Col.LineWidth < ScreenWidth;
+         }
+      }
+   }
+   size_t NumCols = MaxNumCols;
+   while (NumCols > 1 && !ColumnInfo[NumCols - 1].ValidLen)
+      --NumCols;
+
+   size_t NumRows = ListSize / NumCols + (ListSize % NumCols != 0);
+   auto const &LineFormat = ColumnInfo[NumCols - 1];
+   for (size_t Row = 0; Row < NumRows; ++Row) {
+      size_t Col = 0;
+      size_t I = Row;
+      out << string(Indent, ' ');
+      while (true) {
+         out << List[I];
+
+         size_t CurLen = List[I].size();
+         size_t MaxLen = LineFormat.RemainingWidths[Col++];
+         I += NumRows;
+         if (I >= ListSize)
+            break;
+
+         out << string(MaxLen - CurLen, ' ');
+      }
+      out << endl;
+   }
+}
+									/*}}}*/
 // ShowBroken - Debugging aide						/*{{{*/
 // ---------------------------------------------------------------------
 /* This prints out the names of all the packages that are broken along
diff --git a/apt-private/private-output.h b/apt-private/private-output.h
index 8e4b50e..d99e2af 100644
--- a/apt-private/private-output.h
+++ b/apt-private/private-output.h
@@ -9,6 +9,7 @@
 #include <fstream>
 #include <string>
 #include <iostream>
+#include <vector>
 
 // forward declaration
 class pkgCacheFile;
@@ -34,6 +35,8 @@ void ListSingleVersion(pkgCacheFile &CacheFile, pkgRecords &records,
 APT_PUBLIC void ShowBroken(std::ostream &out, CacheFile &Cache, bool const Now);
 APT_PUBLIC void ShowBroken(std::ostream &out, pkgCacheFile &Cache, bool const Now);
 
+void ShowColumnarList(std::ostream &out, const std::vector<std::string> &List, size_t Indent, size_t ScreenWidth);
+
 template<class Container, class PredicateC, class DisplayP, class DisplayV> bool ShowList(std::ostream &out, std::string const &Title,
       Container const &cont,
       PredicateC Predicate,
@@ -43,7 +46,9 @@ template<class Container, class PredicateC, class DisplayP, class DisplayV> bool
    size_t const ScreenWidth = (::ScreenWidth > 3) ? ::ScreenWidth - 3 : 0;
    int ScreenUsed = 0;
    bool const ShowVersions = _config->FindB("APT::Get::Show-Versions", false);
+   bool const ShowColumnar = _config->FindB("APT::Get::Show-Columns", false);
    bool printedTitle = false;
+   std::vector<std::string> PackageList;
 
    for (auto const &Pkg: cont)
    {
@@ -66,24 +71,34 @@ template<class Container, class PredicateC, class DisplayP, class DisplayV> bool
       else
       {
 	 std::string const PkgName = PkgDisplay(Pkg);
-	 if (ScreenUsed == 0 || (ScreenUsed + PkgName.length()) >= ScreenWidth)
-	 {
-	    out << std::endl << "  ";
-	    ScreenUsed = 0;
-	 }
-	 else if (ScreenUsed != 0)
-	 {
-	    out << " ";
-	    ++ScreenUsed;
+	 if (ShowColumnar)
+	    PackageList.push_back(PkgName);
+	 else {
+	    if (ScreenUsed == 0 || (ScreenUsed + PkgName.length()) >= ScreenWidth)
+	    {
+	       out << std::endl << "  ";
+	       ScreenUsed = 0;
+	    }
+	    else if (ScreenUsed != 0)
+	    {
+	       out << " ";
+	       ++ScreenUsed;
+	    }
+	    out << PkgName;
+	    ScreenUsed += PkgName.length();
 	 }
-	 out << PkgName;
-	 ScreenUsed += PkgName.length();
       }
    }
 
    if (printedTitle == true)
    {
-      out << std::endl;
+      if (ShowColumnar)
+      {
+	 out << std::endl;
+	 ShowColumnarList(out, PackageList, 2, ScreenWidth);
+      }
+      else
+	 out << std::endl;
       return false;
    }
    return true;
diff --git a/debian/changelog b/debian/changelog
index 6aabe9e..e295155 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+apt (1.2.5) unstable; urgency=medium
+
+  * Add columnar output for package lists in the style of 'ls'
+
+ -- Christian Blichmann <mail@blichmann.eu>  Tue, 01 Mar 2016 22:59:29 +0100
+
 apt (1.2.4) unstable; urgency=medium
 
   [ David Kalnischkies ]
diff --git a/doc/apt-get.8.xml b/doc/apt-get.8.xml
index 60fa46f..58b65da 100644
--- a/doc/apt-get.8.xml
+++ b/doc/apt-get.8.xml
@@ -368,6 +368,12 @@
      Configuration Item: <literal>APT::Get::Show-Versions</literal>.</para></listitem>
      </varlistentry>
 
+     <varlistentry><term><option>--list-columns</option></term>
+     <listitem><para>Display package lists in columnar format; prints package lists in the
+     style of the "ls" command.
+     Configuration Item: <literal>APT::Get::Show-Columns</literal>.</para></listitem>
+     </varlistentry>
+
      <varlistentry><term><option>-a</option></term>
                    <term><option>--host-architecture</option></term>
      <listitem><para>This option controls the architecture packages are built for

Reply to: