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: