From 3a3681837cc156122f3487c6cf9f9a0f7a22ccd3 Mon Sep 17 00:00:00 2001
From: Michael Davis <michael.davis@cern.ch>
Date: Thu, 6 Jun 2019 17:08:40 +0200
Subject: [PATCH] [cta-admin] Moves text formatting to new
 CtaAdminTextFormatter class

---
 cmdline/CMakeLists.txt            |   2 +-
 cmdline/CtaAdminCmd.cpp           | 453 ++----------------------------
 cmdline/CtaAdminCmd.hpp           |  36 ---
 cmdline/CtaAdminTextFormatter.cpp | 420 +++++++++++++++++++++++++++
 cmdline/CtaAdminTextFormatter.hpp |  69 +++++
 5 files changed, 517 insertions(+), 463 deletions(-)
 create mode 100644 cmdline/CtaAdminTextFormatter.cpp
 create mode 100644 cmdline/CtaAdminTextFormatter.hpp

diff --git a/cmdline/CMakeLists.txt b/cmdline/CMakeLists.txt
index 01eb4f7388..3a9fc2e3af 100644
--- a/cmdline/CMakeLists.txt
+++ b/cmdline/CMakeLists.txt
@@ -32,7 +32,7 @@ include_directories(${CMAKE_BINARY_DIR}/eos_cta ${PROTOBUF3_INCLUDE_DIRS})
 #
 # cta-admin <admin_command> is the SSI version of "cta <admin_command>"
 #
-add_executable(cta-admin CtaAdminCmd.cpp CtaAdminCmdParse.cpp)
+add_executable(cta-admin CtaAdminCmd.cpp CtaAdminCmdParse.cpp CtaAdminTextFormatter.cpp)
 target_link_libraries(cta-admin XrdSsiPbEosCta XrdSsi-4 XrdSsiLib XrdUtils)
 set_property (TARGET cta-admin APPEND PROPERTY INSTALL_RPATH ${PROTOBUF3_RPATH})
 
diff --git a/cmdline/CtaAdminCmd.cpp b/cmdline/CtaAdminCmd.cpp
index 223670e7ea..6279e19ed8 100644
--- a/cmdline/CtaAdminCmd.cpp
+++ b/cmdline/CtaAdminCmd.cpp
@@ -19,19 +19,18 @@
 
 #include <sstream>
 #include <iostream>
-#include <iomanip>
 
 #include <XrdSsiPbLog.hpp>
 #include <XrdSsiPbIStreamBuffer.hpp>
 
-#include "CtaAdminCmd.hpp"
+#include <cmdline/CtaAdminCmd.hpp>
+#include <cmdline/CtaAdminTextFormatter.hpp>
 
-// synchronisation flag between main thread and stream handler thread
-std::atomic<bool> isHeaderSent(false);
 
+// global synchronisation flag between main thread and stream handler thread
+std::atomic<bool> isHeaderSent(false);
 
 
-// Define XRootD SSI Alert message callback
 namespace XrdSsiPb {
 
 /*!
@@ -47,7 +46,6 @@ void RequestCallback<cta::xrd::Alert>::operator()(const cta::xrd::Alert &alert)
 }
 
 
-
 /*!
  * Data/Stream callback.
  *
@@ -78,24 +76,24 @@ void IStreamBuffer<cta::xrd::Data>::DataCallback(cta::xrd::Data record) const
          case Data::kLprSummary:    std::cout << Log::DumpProtobuf(&record.lpr_summary());  break;
          case Data::kTplsItem:      std::cout << Log::DumpProtobuf(&record.tpls_item());    break;
          case Data::kTalsItem:      std::cout << Log::DumpProtobuf(&record.tals_item());    break;
-         case Data::kRelsItem:       std::cout << Log::DumpProtobuf(&record.rels_item());   break;
+         case Data::kRelsItem:      std::cout << Log::DumpProtobuf(&record.rels_item());    break;
          default:
             throw std::runtime_error("Received invalid stream data from CTA Frontend.");
       }
    }
    // Format results in a tabular format for a human
    else switch(record.data_case()) {
-         case Data::kAflsItem:      CtaAdminCmd::print(record.afls_item());    break;
-         case Data::kAflsSummary:   CtaAdminCmd::print(record.afls_summary()); break;
-         case Data::kFrlsItem:      CtaAdminCmd::print(record.frls_item());    break;
-         case Data::kFrlsSummary:   CtaAdminCmd::print(record.frls_summary()); break;
-         case Data::kLpaItem:       CtaAdminCmd::print(record.lpa_item());     break;
-         case Data::kLpaSummary:    CtaAdminCmd::print(record.lpa_summary());  break;
-         case Data::kLprItem:       CtaAdminCmd::print(record.lpr_item());     break;
-         case Data::kLprSummary:    CtaAdminCmd::print(record.lpr_summary());  break;
-         case Data::kTplsItem:      CtaAdminCmd::print(record.tpls_item());    break;
-         case Data::kTalsItem:      CtaAdminCmd::print(record.tals_item());    break;
-         case Data::kRelsItem:      CtaAdminCmd::print(record.rels_item());    break;
+         case Data::kAflsItem:      CtaAdminTextFormatter::print(record.afls_item());    break;
+         case Data::kAflsSummary:   CtaAdminTextFormatter::print(record.afls_summary()); break;
+         case Data::kFrlsItem:      CtaAdminTextFormatter::print(record.frls_item());    break;
+         case Data::kFrlsSummary:   CtaAdminTextFormatter::print(record.frls_summary()); break;
+         case Data::kLpaItem:       CtaAdminTextFormatter::print(record.lpa_item());     break;
+         case Data::kLpaSummary:    CtaAdminTextFormatter::print(record.lpa_summary());  break;
+         case Data::kLprItem:       CtaAdminTextFormatter::print(record.lpr_item());     break;
+         case Data::kLprSummary:    CtaAdminTextFormatter::print(record.lpr_summary());  break;
+         case Data::kTplsItem:      CtaAdminTextFormatter::print(record.tpls_item());    break;
+         case Data::kTalsItem:      CtaAdminTextFormatter::print(record.tals_item());    break;
+         case Data::kRelsItem:      CtaAdminTextFormatter::print(record.rels_item());    break;
          default:
             throw std::runtime_error("Received invalid stream data from CTA Frontend.");
    }
@@ -229,17 +227,17 @@ void CtaAdminCmd::send() const
          std::cout << response.message_txt();
          // Print streaming response header
          if(!isJson()) switch(response.show_header()) {
-            case HeaderType::ARCHIVEFILE_LS:               printAfLsHeader(); break;
-            case HeaderType::ARCHIVEFILE_LS_SUMMARY:       printAfLsSummaryHeader(); break;
-            case HeaderType::FAILEDREQUEST_LS:             printFrLsHeader(); break;
-            case HeaderType::FAILEDREQUEST_LS_SUMMARY:     printFrLsSummaryHeader(); break;
-            case HeaderType::LISTPENDINGARCHIVES:          printLpaHeader(); break;
-            case HeaderType::LISTPENDINGARCHIVES_SUMMARY:  printLpaSummaryHeader(); break;
-            case HeaderType::LISTPENDINGRETRIEVES:         printLprHeader(); break;
-            case HeaderType::LISTPENDINGRETRIEVES_SUMMARY: printLprSummaryHeader(); break;
-            case HeaderType::TAPEPOOL_LS:                  printTpLsHeader(); break;
-            case HeaderType::TAPE_LS:                      printTapeLsHeader(); break;
-            case HeaderType::REPACK_LS:                    printRepackLsHeader(); break;
+            case HeaderType::ARCHIVEFILE_LS:               CtaAdminTextFormatter::printAfLsHeader(); break;
+            case HeaderType::ARCHIVEFILE_LS_SUMMARY:       CtaAdminTextFormatter::printAfLsSummaryHeader(); break;
+            case HeaderType::FAILEDREQUEST_LS:             CtaAdminTextFormatter::printFrLsHeader(); break;
+            case HeaderType::FAILEDREQUEST_LS_SUMMARY:     CtaAdminTextFormatter::printFrLsSummaryHeader(); break;
+            case HeaderType::LISTPENDINGARCHIVES:          CtaAdminTextFormatter::printLpaHeader(); break;
+            case HeaderType::LISTPENDINGARCHIVES_SUMMARY:  CtaAdminTextFormatter::printLpaSummaryHeader(); break;
+            case HeaderType::LISTPENDINGRETRIEVES:         CtaAdminTextFormatter::printLprHeader(); break;
+            case HeaderType::LISTPENDINGRETRIEVES_SUMMARY: CtaAdminTextFormatter::printLprSummaryHeader(); break;
+            case HeaderType::TAPEPOOL_LS:                  CtaAdminTextFormatter::printTpLsHeader(); break;
+            case HeaderType::TAPE_LS:                      CtaAdminTextFormatter::printTapeLsHeader(); break;
+            case HeaderType::REPACK_LS:                    CtaAdminTextFormatter::printRepackLsHeader(); break;
             case HeaderType::NONE:
             default:                                       break;
          }
@@ -404,403 +402,6 @@ void CtaAdminCmd::throwUsage(const std::string &error_txt) const
    throw std::runtime_error(help.str());
 }
 
-
-
-// static methods (for printing stream results)
-
-void CtaAdminCmd::printAfLsHeader()
-{
-   std::cout << TEXT_RED
-             << std::setfill(' ') << std::setw(11) << std::right << "archive id"     << ' '
-             << std::setfill(' ') << std::setw(7)  << std::right << "copy no"        << ' '
-             << std::setfill(' ') << std::setw(7)  << std::right << "vid"            << ' '
-             << std::setfill(' ') << std::setw(7)  << std::right << "fseq"           << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << "block id"       << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << "instance"       << ' '
-             << std::setfill(' ') << std::setw(7)  << std::right << "disk id"        << ' '
-             << std::setfill(' ') << std::setw(12) << std::right << "size"           << ' '
-             << std::setfill(' ') << std::setw(13) << std::right << "checksum type"  << ' '
-             << std::setfill(' ') << std::setw(14) << std::right << "checksum value" << ' '
-             << std::setfill(' ') << std::setw(16) << std::right << "storage class"  << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << "owner"          << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << "group"          << ' '
-             << std::setfill(' ') << std::setw(13) << std::right << "creation time"  << ' '
-             << std::setfill(' ') << std::setw(7)  << std::right << "ss vid"         << ' '
-             << std::setfill(' ') << std::setw(7)  << std::right << "ss fseq"        << ' '
-                                                                 << "path"
-             << TEXT_NORMAL << std::endl;
-}
-
-void CtaAdminCmd::print(const cta::admin::ArchiveFileLsItem &afls_item)
-{
-   std::cout << std::setfill(' ') << std::setw(11) << std::right << afls_item.af().archive_id()    << ' '
-             << std::setfill(' ') << std::setw(7)  << std::right << afls_item.copy_nb()            << ' '
-             << std::setfill(' ') << std::setw(7)  << std::right << afls_item.tf().vid()           << ' '
-             << std::setfill(' ') << std::setw(7)  << std::right << afls_item.tf().f_seq()         << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << afls_item.tf().block_id()      << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << afls_item.af().disk_instance() << ' '
-             << std::setfill(' ') << std::setw(7)  << std::right << afls_item.af().disk_id()       << ' '
-             << std::setfill(' ') << std::setw(12) << std::right << afls_item.af().size()          << ' '
-             << std::setfill(' ') << std::setw(13) << std::right << afls_item.af().cs().type()     << ' '
-             << std::setfill(' ') << std::setw(14) << std::right << afls_item.af().cs().value()    << ' '
-             << std::setfill(' ') << std::setw(16) << std::right << afls_item.af().storage_class() << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << afls_item.af().df().owner()    << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << afls_item.af().df().group()    << ' '
-             << std::setfill(' ') << std::setw(13) << std::right << afls_item.af().creation_time() << ' ';
-
-   if (afls_item.tf().superseded_by_vid().size()) {
-     std::cout << std::setfill(' ') << std::setw(7)  << std::right << afls_item.tf().superseded_by_vid()   << ' '
-               << std::setfill(' ') << std::setw(7)  << std::right << afls_item.tf().superseded_by_f_seq() << ' ';
-   } else {
-     std::cout << std::setfill(' ') << std::setw(7)  << std::right << "-" << ' '
-               << std::setfill(' ') << std::setw(7)  << std::right << "-" << ' ';
-   }
-   std::cout << afls_item.af().df().path()
-             << std::endl;
-}
-
-void CtaAdminCmd::printAfLsSummaryHeader()
-{
-   std::cout << TEXT_RED
-             << std::setfill(' ') << std::setw(13) << std::right << "total files" << ' '
-             << std::setfill(' ') << std::setw(12) << std::right << "total size"  << ' '
-             << TEXT_NORMAL << std::endl;
-}
-
-void CtaAdminCmd::print(const cta::admin::ArchiveFileLsSummary &afls_summary)
-{
-   std::cout << std::setfill(' ') << std::setw(13) << std::right << afls_summary.total_files() << ' '
-             << std::setfill(' ') << std::setw(12) << std::right << afls_summary.total_size()  << ' '
-             << std::endl;
-}
-
-void CtaAdminCmd::printFrLsHeader()
-{
-   std::cout << TEXT_RED
-             << std::setfill(' ') << std::setw(12) << std::right << "request type"   << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << "copy no"        << ' '
-             << std::setfill(' ') << std::setw(13) << std::right << "tapepool/vid"   << ' '
-             << std::setfill(' ') << std::setw(10) << std::right << "requester"      << ' '
-             << std::setfill(' ') << std::setw(6)  << std::right << "group"          << ' '
-                                                                 << "path"
-             << TEXT_NORMAL << std::endl;
-}
-
-void CtaAdminCmd::print(const cta::admin::FailedRequestLsItem &frls_item)
-{
-   std::string request_type;
-   std::string tapepool_vid;
-
-   switch(frls_item.request_type()) {
-      case admin::RequestType::ARCHIVE_REQUEST:
-         request_type = "archive";
-         tapepool_vid = frls_item.tapepool();
-         break;
-      case admin::RequestType::RETRIEVE_REQUEST:
-         request_type = "retrieve";
-         tapepool_vid = frls_item.tf().vid();
-         break;
-      default:
-         throw std::runtime_error("Unrecognised request type: " + std::to_string(frls_item.request_type()));
-   }
-
-   std::cout << std::setfill(' ') << std::setw(11) << std::right << request_type                      << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << frls_item.copy_nb()               << ' '
-             << std::setfill(' ') << std::setw(14) << std::right << tapepool_vid                      << ' '
-             << std::setfill(' ') << std::setw(10) << std::right << frls_item.requester().username()  << ' '
-             << std::setfill(' ') << std::setw(6)  << std::right << frls_item.requester().groupname() << ' '
-                                                                 << frls_item.af().df().path()
-             << std::endl;
-
-   for(auto &errLogMsg : frls_item.failurelogs()) {
-     std::cout << errLogMsg << std::endl;
-   }
-}
-
-void CtaAdminCmd::printFrLsSummaryHeader()
-{
-   std::cout << TEXT_RED
-             << std::setfill(' ') << std::setw(12) << std::right << "request type"        << ' '
-             << std::setfill(' ') << std::setw(13) << std::right << "total files"         << ' '
-             << std::setfill(' ') << std::setw(20) << std::right << "total size (bytes)"  << ' '
-             << TEXT_NORMAL << std::endl;
-}
-
-void CtaAdminCmd::print(const cta::admin::FailedRequestLsSummary &frls_summary)
-{
-   std::string request_type =
-      frls_summary.request_type() == cta::admin::RequestType::ARCHIVE_REQUEST  ? "archive" :
-      frls_summary.request_type() == cta::admin::RequestType::RETRIEVE_REQUEST ? "retrieve" : "total";
-
-   std::cout << std::setfill(' ') << std::setw(11) << std::right << request_type               << ' '
-             << std::setfill(' ') << std::setw(13) << std::right << frls_summary.total_files() << ' '
-             << std::setfill(' ') << std::setw(20) << std::right << frls_summary.total_size()  << ' '
-             << std::endl;
-}
-
-void CtaAdminCmd::printLpaHeader()
-{
-   std::cout << TEXT_RED
-             << std::setfill(' ') << std::setw(18) << std::right << "tapepool"       << ' '
-             << std::setfill(' ') << std::setw(11) << std::right << "archive id"     << ' '
-             << std::setfill(' ') << std::setw(13) << std::right << "storage class"  << ' '
-             << std::setfill(' ') << std::setw(7)  << std::right << "copy no"        << ' '
-             << std::setfill(' ') << std::setw(7)  << std::right << "disk id"        << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << "instance"       << ' '
-             << std::setfill(' ') << std::setw(13) << std::right << "checksum type"  << ' '
-             << std::setfill(' ') << std::setw(14) << std::right << "checksum value" << ' '
-             << std::setfill(' ') << std::setw(12) << std::right << "size"           << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << "user"           << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << "group"          << ' '
-             <<                                                     "path"
-             << TEXT_NORMAL << std::endl;
-}
-
-void CtaAdminCmd::print(const cta::admin::ListPendingArchivesItem &lpa_item)
-{
-   std::cout << std::setfill(' ') << std::setw(18) << std::right << lpa_item.tapepool()           << ' '
-             << std::setfill(' ') << std::setw(11) << std::right << lpa_item.af().archive_id()    << ' '
-             << std::setfill(' ') << std::setw(13) << std::right << lpa_item.af().storage_class() << ' '
-             << std::setfill(' ') << std::setw(7)  << std::right << lpa_item.copy_nb()            << ' '
-             << std::setfill(' ') << std::setw(7)  << std::right << lpa_item.af().disk_id()       << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << lpa_item.af().disk_instance() << ' '
-             << std::setfill(' ') << std::setw(13) << std::right << lpa_item.af().cs().type()     << ' '
-             << std::setfill(' ') << std::setw(14) << std::right << lpa_item.af().cs().value()    << ' '
-             << std::setfill(' ') << std::setw(12) << std::right << lpa_item.af().size()          << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << lpa_item.af().df().owner()    << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << lpa_item.af().df().group()    << ' '
-             <<                                                     lpa_item.af().df().path()
-             << std::endl;
-}
-
-void CtaAdminCmd::printLpaSummaryHeader()
-{
-   std::cout << TEXT_RED
-             << std::setfill(' ') << std::setw(18) << std::right << "tapepool"    << ' '
-             << std::setfill(' ') << std::setw(13) << std::right << "total files" << ' '
-             << std::setfill(' ') << std::setw(12) << std::right << "total size"  << ' '
-             << TEXT_NORMAL << std::endl;
-}
-
-void CtaAdminCmd::print(const cta::admin::ListPendingArchivesSummary &lpa_summary)
-{
-   std::cout << std::setfill(' ') << std::setw(18) << std::right << lpa_summary.tapepool()    << ' '
-             << std::setfill(' ') << std::setw(13) << std::right << lpa_summary.total_files() << ' '
-             << std::setfill(' ') << std::setw(12) << std::right << lpa_summary.total_size()  << ' '
-             << std::endl;
-}
-
-void CtaAdminCmd::printLprHeader()
-{
-   std::cout << TEXT_RED
-             << std::setfill(' ') << std::setw(13) << std::right << "vid"        << ' '
-             << std::setfill(' ') << std::setw(11) << std::right << "archive id" << ' '
-             << std::setfill(' ') << std::setw(7)  << std::right << "copy no"    << ' '
-             << std::setfill(' ') << std::setw(7)  << std::right << "fseq"       << ' '
-             << std::setfill(' ') << std::setw(9)  << std::right << "block id"   << ' '
-             << std::setfill(' ') << std::setw(12) << std::right << "size"       << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << "user"       << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << "group"      << ' '
-             <<                                                     "path"
-             << TEXT_NORMAL << std::endl;
-}
-
-void CtaAdminCmd::print(const cta::admin::ListPendingRetrievesItem &lpr_item)
-{
-   std::cout << std::setfill(' ') << std::setw(13) << std::right << lpr_item.tf().vid()        << ' '
-             << std::setfill(' ') << std::setw(11) << std::right << lpr_item.af().archive_id() << ' '
-             << std::setfill(' ') << std::setw(7)  << std::right << lpr_item.copy_nb()         << ' '
-             << std::setfill(' ') << std::setw(7)  << std::right << lpr_item.tf().f_seq()      << ' '
-             << std::setfill(' ') << std::setw(9)  << std::right << lpr_item.tf().block_id()   << ' '
-             << std::setfill(' ') << std::setw(12) << std::right << lpr_item.af().size()       << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << lpr_item.af().df().owner() << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << lpr_item.af().df().group() << ' '
-             <<                                                     lpr_item.af().df().path()
-             << std::endl;
-}
-
-void CtaAdminCmd::printLprSummaryHeader()
-{
-   std::cout << TEXT_RED
-             << std::setfill(' ') << std::setw(13) << std::right << "vid"         << ' '
-             << std::setfill(' ') << std::setw(13) << std::right << "total files" << ' '
-             << std::setfill(' ') << std::setw(12) << std::right << "total size"  << ' '
-             << TEXT_NORMAL << std::endl;
-}
-
-void CtaAdminCmd::print(const cta::admin::ListPendingRetrievesSummary &lpr_summary)
-{
-   std::cout << std::setfill(' ') << std::setw(13) << std::right << lpr_summary.vid()         << ' '
-             << std::setfill(' ') << std::setw(13) << std::right << lpr_summary.total_files() << ' '
-             << std::setfill(' ') << std::setw(12) << std::right << lpr_summary.total_size()  << ' '
-             << std::endl;
-}
-
-void CtaAdminCmd::printTpLsHeader()
-{
-   std::cout << TEXT_RED
-             << std::setfill(' ') << std::setw(18) << std::right << "name"        << ' '
-             << std::setfill(' ') << std::setw(10) << std::right << "vo"          << ' '
-             << std::setfill(' ') << std::setw(7)  << std::right << "#tapes"      << ' '
-             << std::setfill(' ') << std::setw(9)  << std::right << "#partial"    << ' '
-             << std::setfill(' ') << std::setw(12) << std::right << "#phys files" << ' '
-             << std::setfill(' ') << std::setw(5)  << std::right << "size"        << ' '
-             << std::setfill(' ') << std::setw(5)  << std::right << "used"        << ' '
-             << std::setfill(' ') << std::setw(6)  << std::right << "avail"       << ' '
-             << std::setfill(' ') << std::setw(6)  << std::right << "use%"        << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << "encrypt"     << ' '
-             << std::setfill(' ') << std::setw(20) << std::right << "supply"      << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << "c.user"      << ' '
-             << std::setfill(' ') << std::setw(25) << std::right << "c.host"      << ' '
-             << std::setfill(' ') << std::setw(24) << std::right << "c.time"      << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << "m.user"      << ' '
-             << std::setfill(' ') << std::setw(25) << std::right << "m.host"      << ' '
-             << std::setfill(' ') << std::setw(24) << std::right << "m.time"      << ' '
-             <<                                                     "comment"     << ' '
-             << TEXT_NORMAL << std::endl;
-}
-
-void CtaAdminCmd::printTapeLsHeader(){
-  std::cout << TEXT_RED
-            << std::setfill(' ') << std::setw(7) << std::right << "vid"              << ' '
-            << std::setfill(' ') << std::setw(10) << std::right << "media type"       << ' '
-            << std::setfill(' ') << std::setw(7)  << std::right << "vendor"           << ' '
-            << std::setfill(' ') << std::setw(20)  << std::right << "logical library"  << ' '
-            << std::setfill(' ') << std::setw(18) << std::right << "tapepool"         << ' '
-            << std::setfill(' ') << std::setw(10)  << std::right << "vo"               << ' '
-            << std::setfill(' ') << std::setw(20)  << std::right << "encryption key"   << ' '
-            << std::setfill(' ') << std::setw(12)  << std::right << "capacity"         << ' '
-            << std::setfill(' ') << std::setw(12)  << std::right << "occupancy"        << ' '
-            << std::setfill(' ') << std::setw(9)  << std::right << "last fseq"        << ' '
-            << std::setfill(' ') << std::setw(5)  << std::right << "full"             << ' '
-            << std::setfill(' ') << std::setw(8) << std::right << "disabled"         << ' '
-            << std::setfill(' ') << std::setw(12) << std::right << "label drive"      << ' '
-            << std::setfill(' ') << std::setw(12)  << std::right << "label time"       << ' '
-            << std::setfill(' ') << std::setw(12) << std::right << "last w drive"     << ' '
-            << std::setfill(' ') << std::setw(12) << std::right << "last w time"      << ' '
-            << std::setfill(' ') << std::setw(12) << std::right << "last r drive"     << ' '
-            << std::setfill(' ') << std::setw(12) << std::right << "last r time"      << ' '
-            << std::setfill(' ') << std::setw(20) << std::right << "c.user"           << ' '
-            << std::setfill(' ') << std::setw(25) << std::right << "c.host"           << ' '
-            << std::setfill(' ') << std::setw(13) << std::right << "c.time"           << ' '
-            << std::setfill(' ') << std::setw(20) << std::right << "m.user"           << ' '
-            << std::setfill(' ') << std::setw(25) << std::right << "m.host"           << ' '
-            << std::setfill(' ') << std::setw(13) << std::right << "m.time"           << ' '
-            <<                                                     "comment"          << ' '
-            << TEXT_NORMAL << std::endl;
-}
-
-
-void CtaAdminCmd::print(const cta::admin::TapeLsItem &tals_item){
-  std::cout << std::setfill(' ') << std::setw(7) << std::right << tals_item.vid()        << ' '
-            << std::setfill(' ') << std::setw(10) << std::right << tals_item.media_type() << ' '
-            << std::setfill(' ') << std::setw(7)  << std::right << tals_item.vendor()     << ' '
-            << std::setfill(' ') << std::setw(20)  << std::right << tals_item.logical_library() << ' '
-            << std::setfill(' ') << std::setw(18) << std::right << tals_item.tapepool()         << ' '
-            << std::setfill(' ') << std::setw(10)  << std::right << tals_item.vo()               << ' '
-            << std::setfill(' ') << std::setw(20)  << std::right << tals_item.encryption_key()   << ' '
-            << std::setfill(' ') << std::setw(12)  << std::right << tals_item.capacity()         << ' '
-            << std::setfill(' ') << std::setw(12)  << std::right << tals_item.occupancy()       << ' '
-            << std::setfill(' ') << std::setw(9)  << std::right << tals_item.last_fseq()       << ' '
-            << std::setfill(' ') << std::setw(5)  << std::right << tals_item.full()           << ' '
-            << std::setfill(' ') << std::setw(8) << std::right << tals_item.disabled()         << ' ';
-  if(tals_item.has_label_log()){
-    std::cout << std::setfill(' ') << std::setw(12) << std::right << tals_item.label_log().drive() << ' '
-              << std::setfill(' ') << std::setw(12) << std::right << tals_item.label_log().time() << ' ';
-  } else {
-    std::cout << std::setfill(' ') << std::setw(12) << std::right << "-" << ' '
-              << std::setfill(' ') << std::setw(12) << std::right << "-" << ' ';
-  }
-  if(tals_item.has_last_written_log()){
-    std::cout << std::setfill(' ') << std::setw(12) << std::right << tals_item.last_written_log().drive() << ' '
-              << std::setfill(' ') << std::setw(12) << std::right << tals_item.last_written_log().time() << ' ';
-  } else {
-    std::cout << std::setfill(' ') << std::setw(12) << "-" << ' '
-              << std::setfill(' ') << std::setw(12) << "-" << ' ';
-  }
-  if(tals_item.has_last_read_log()){
-    std::cout << std::setfill(' ') << std::setw(12) << std::right << tals_item.last_read_log().drive() << ' '
-              << std::setfill(' ') << std::setw(12) << std::right << tals_item.last_read_log().time() << ' ';
-  } else {
-    std::cout << std::setfill(' ') << std::setw(12) << std::right << "-" << ' '
-              << std::setfill(' ') << std::setw(12) << std::right << "-" << ' ';
-  }
-    std::cout << std::setfill(' ') << std::setw(20) << std::right << tals_item.creation_log().username()          << ' '
-              << std::setfill(' ') << std::setw(25) << std::right << tals_item.creation_log().host()          << ' '
-              << std::setfill(' ') << std::setw(13) << std::right << tals_item.creation_log().time()           << ' '
-              << std::setfill(' ') << std::setw(20) << std::right << tals_item.last_modification_log().username()        << ' '
-              << std::setfill(' ') << std::setw(25) << std::right << tals_item.last_modification_log().host()          << ' '
-              << std::setfill(' ') << std::setw(13) << std::right << tals_item.last_modification_log().time()           << ' '
-              << std::endl;
-}
-
-void CtaAdminCmd::printRepackLsHeader(){
-  std::cout << TEXT_RED
-            << std::setfill(' ') << std::setw(7) << std::right << "vid"              << ' '
-            << std::setfill(' ') << std::setw(50) << std::right << "repackBufferURL"       << ' '
-            << std::setfill(' ') << std::setw(17)  << std::right << "userProvidedFiles"           << ' '
-            << std::setfill(' ') << std::setw(20)  << std::right << "totalFilesToRetrieve" << ' '
-            << std::setfill(' ') << std::setw(19) << std::right <<  "totalBytesToRetrieve" << ' '
-            << std::setfill(' ') << std::setw(20)  << std::right << "totalFilesToArchive"  << ' '
-            << std::setfill(' ') << std::setw(19) << std::right <<  "totalBytesToArchive"  << ' '
-            << std::setfill(' ') << std::setw(14)  << std::right << "retrievedFiles"               << ' '
-            << std::setfill(' ') << std::setw(13)  << std::right << "archivedFiles"   << ' '
-            << std::setfill(' ') << std::setw(21)  << std::right << "failedToRetrieveFiles"         << ' '
-            << std::setfill(' ') << std::setw(20)  << std::right << "failedToRetrieveBytes"        << ' '
-            << std::setfill(' ') << std::setw(20)  << std::right <<  "failedToArchiveFiles"        << ' '
-            << std::setfill(' ') << std::setw(20)  << std::right <<  "failedToArchiveBytes"             << ' '
-            << std::setfill(' ') << std::setw(16)  << std::right <<  "lastExpandedFSeq"             << ' '
-            <<                                                      "status"         << ' '
-            << TEXT_NORMAL << std::endl;
-}
-
-void CtaAdminCmd::print(const cta::admin::RepackLsItem &rels_item){
-  std::cout << std::setfill(' ') << std::setw(7) << std::right << rels_item.vid()           << ' '
-            << std::setfill(' ') << std::setw(50) << std::right << rels_item.repack_buffer_url()      << ' '
-            << std::setfill(' ') << std::setw(17)  << std::right << rels_item.user_provided_files()           << ' '
-            << std::setfill(' ') << std::setw(20)  << std::right << rels_item.total_files_to_retrieve() << ' '
-            << std::setfill(' ') << std::setw(19) << std::right <<  rels_item.total_bytes_to_retrieve() << ' '
-            << std::setfill(' ') << std::setw(20)  << std::right << rels_item.total_files_to_archive()  << ' '
-            << std::setfill(' ') << std::setw(19) << std::right <<  rels_item.total_bytes_to_archive()  << ' '
-            << std::setfill(' ') << std::setw(14)  << std::right << rels_item.retrieved_files()               << ' '
-            << std::setfill(' ') << std::setw(13)  << std::right << rels_item.archived_files()   << ' '
-            << std::setfill(' ') << std::setw(21)  << std::right << rels_item.failed_to_retrieve_files()        << ' '
-            << std::setfill(' ') << std::setw(20)  << std::right << rels_item.failed_to_retrieve_bytes()        << ' '
-            << std::setfill(' ') << std::setw(20)  << std::right <<  rels_item.failed_to_archive_files()       << ' '
-            << std::setfill(' ') << std::setw(20)  << std::right <<  rels_item.failed_to_retrieve_bytes()         << ' '
-            << std::setfill(' ') << std::setw(10)  << std::right <<  rels_item.last_expanded_fseq()             << ' '
-            << rels_item.status() << std::endl;
-}
-
-void CtaAdminCmd::print(const cta::admin::TapePoolLsItem &tpls_item)
-{
-   std::string encrypt_str = tpls_item.encrypt() ? "true" : "false";
-   uint64_t avail = tpls_item.capacity_bytes() > tpls_item.data_bytes() ?
-      tpls_item.capacity_bytes()-tpls_item.data_bytes() : 0; 
-   double use_percent = tpls_item.capacity_bytes() > 0 ?
-      (static_cast<double>(tpls_item.data_bytes())/static_cast<double>(tpls_item.capacity_bytes()))*100.0 : 0.0;
-
-   std::cout << std::setfill(' ') << std::setw(18) << std::right << tpls_item.name()                          << ' '
-             << std::setfill(' ') << std::setw(10) << std::right << tpls_item.vo()                            << ' '
-             << std::setfill(' ') << std::setw(7)  << std::right << tpls_item.num_tapes()                     << ' '
-             << std::setfill(' ') << std::setw(9)  << std::right << tpls_item.num_partial_tapes()             << ' '
-             << std::setfill(' ') << std::setw(12) << std::right << tpls_item.num_physical_files()            << ' '
-             << std::setfill(' ') << std::setw(4)  << std::right << tpls_item.capacity_bytes() / 1000000000   << "G "
-             << std::setfill(' ') << std::setw(4)  << std::right << tpls_item.data_bytes()     / 1000000000   << "G "
-             << std::setfill(' ') << std::setw(5)  << std::right << avail                      / 1000000000   << "G "
-             << std::setfill(' ') << std::setw(5)  << std::right << std::fixed << std::setprecision(1) << use_percent << "% "
-             << std::setfill(' ') << std::setw(8)  << std::right << encrypt_str                               << ' '
-             << std::setfill(' ') << std::setw(20) << std::right << tpls_item.supply()                        << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << tpls_item.created().username()            << ' '
-             << std::setfill(' ') << std::setw(25) << std::right << tpls_item.created().host()                << ' '
-             << std::setfill(' ') << std::setw(24) << std::right << timeToString(tpls_item.created().time())  << ' '
-             << std::setfill(' ') << std::setw(8)  << std::right << tpls_item.modified().username()           << ' '
-             << std::setfill(' ') << std::setw(25) << std::right << tpls_item.modified().host()               << ' '
-             << std::setfill(' ') << std::setw(24) << std::right << timeToString(tpls_item.modified().time()) << ' '
-             <<                                                     tpls_item.comment()
-             << std::endl;
-}
-
 }} // namespace cta::admin
 
 
diff --git a/cmdline/CtaAdminCmd.hpp b/cmdline/CtaAdminCmd.hpp
index f86617efd0..7ded20e7a4 100644
--- a/cmdline/CtaAdminCmd.hpp
+++ b/cmdline/CtaAdminCmd.hpp
@@ -43,40 +43,6 @@ public:
       return is_first_record ? "[]" : "]";
    }
 
-   // Static method to convert time to string
-   static std::string timeToString(const time_t &time)
-   {
-      std::string timeString(ctime(&time));
-      timeString.resize(timeString.size()-1); //remove newline
-      return timeString;
-   }
-
-   // Output headers
-   static void printAfLsHeader();
-   static void printAfLsSummaryHeader();
-   static void printFrLsHeader();
-   static void printFrLsSummaryHeader();
-   static void printLpaHeader();
-   static void printLpaSummaryHeader();
-   static void printLprHeader();
-   static void printLprSummaryHeader();
-   static void printTpLsHeader();
-   static void printTapeLsHeader();
-   static void printRepackLsHeader();
-   
-   // Output records
-   static void print(const ArchiveFileLsItem &afls_item);
-   static void print(const ArchiveFileLsSummary &afls_summary);
-   static void print(const FailedRequestLsItem &frls_item);
-   static void print(const FailedRequestLsSummary &frls_summary);
-   static void print(const ListPendingArchivesItem &lpa_item);
-   static void print(const ListPendingArchivesSummary &lpa_summary);
-   static void print(const ListPendingRetrievesItem &lpr_item);
-   static void print(const ListPendingRetrievesSummary &lpr_summary);
-   static void print(const TapePoolLsItem &tpls_item);
-   static void print(const TapeLsItem &tals_item);
-   static void print(const RepackLsItem &rels_item);
-
 private:
    //! Parse the options for a specific command/subcommand
    void parseOptions(int start, int argc, const char *const *const argv, const cmd_val_t &options);
@@ -105,8 +71,6 @@ private:
    static std::atomic<bool> is_json;                                  //!< Display results in JSON format
    static std::atomic<bool> is_first_record;                          //!< Delimiter for JSON records
 
-   static constexpr const char* const TEXT_RED    = "\x1b[31;1m";     //!< Terminal formatting code for red text
-   static constexpr const char* const TEXT_NORMAL = "\x1b[0m";        //!< Terminal formatting code for normal text
    static constexpr const char* const LOG_SUFFIX  = "CtaAdminCmd";    //!< Identifier for log messages
 };
 
diff --git a/cmdline/CtaAdminTextFormatter.cpp b/cmdline/CtaAdminTextFormatter.cpp
new file mode 100644
index 0000000000..2c344e9a93
--- /dev/null
+++ b/cmdline/CtaAdminTextFormatter.cpp
@@ -0,0 +1,420 @@
+/*!
+ * @project        The CERN Tape Archive (CTA)
+ * @brief          Text formatter for CTA Admin command tool
+ * @description    CTA Admin command using Google Protocol Buffers and XRootD SSI transport
+ * @copyright      Copyright 2017 CERN
+ * @license        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 3 of the License, or
+ *                 (at your option) any later version.
+ *
+ *                 This program is distributed in the hope that it will be useful,
+ *                 but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *                 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *                 GNU General Public License for more details.
+ *
+ *                 You should have received a copy of the GNU General Public License
+ *                 along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <iostream>
+#include <iomanip>
+#include <cmdline/CtaAdminTextFormatter.hpp>
+
+
+namespace cta { namespace admin {
+
+void CtaAdminTextFormatter::printAfLsHeader()
+{
+   std::cout << TEXT_RED
+             << std::setfill(' ') << std::setw(11) << std::right << "archive id"     << ' '
+             << std::setfill(' ') << std::setw(7)  << std::right << "copy no"        << ' '
+             << std::setfill(' ') << std::setw(7)  << std::right << "vid"            << ' '
+             << std::setfill(' ') << std::setw(7)  << std::right << "fseq"           << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << "block id"       << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << "instance"       << ' '
+             << std::setfill(' ') << std::setw(7)  << std::right << "disk id"        << ' '
+             << std::setfill(' ') << std::setw(12) << std::right << "size"           << ' '
+             << std::setfill(' ') << std::setw(13) << std::right << "checksum type"  << ' '
+             << std::setfill(' ') << std::setw(14) << std::right << "checksum value" << ' '
+             << std::setfill(' ') << std::setw(16) << std::right << "storage class"  << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << "owner"          << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << "group"          << ' '
+             << std::setfill(' ') << std::setw(13) << std::right << "creation time"  << ' '
+             << std::setfill(' ') << std::setw(7)  << std::right << "ss vid"         << ' '
+             << std::setfill(' ') << std::setw(7)  << std::right << "ss fseq"        << ' '
+                                                                 << "path"
+             << TEXT_NORMAL << std::endl;
+}
+
+void CtaAdminTextFormatter::print(const cta::admin::ArchiveFileLsItem &afls_item)
+{
+   std::cout << std::setfill(' ') << std::setw(11) << std::right << afls_item.af().archive_id()    << ' '
+             << std::setfill(' ') << std::setw(7)  << std::right << afls_item.copy_nb()            << ' '
+             << std::setfill(' ') << std::setw(7)  << std::right << afls_item.tf().vid()           << ' '
+             << std::setfill(' ') << std::setw(7)  << std::right << afls_item.tf().f_seq()         << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << afls_item.tf().block_id()      << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << afls_item.af().disk_instance() << ' '
+             << std::setfill(' ') << std::setw(7)  << std::right << afls_item.af().disk_id()       << ' '
+             << std::setfill(' ') << std::setw(12) << std::right << afls_item.af().size()          << ' '
+             << std::setfill(' ') << std::setw(13) << std::right << afls_item.af().cs().type()     << ' '
+             << std::setfill(' ') << std::setw(14) << std::right << afls_item.af().cs().value()    << ' '
+             << std::setfill(' ') << std::setw(16) << std::right << afls_item.af().storage_class() << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << afls_item.af().df().owner()    << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << afls_item.af().df().group()    << ' '
+             << std::setfill(' ') << std::setw(13) << std::right << afls_item.af().creation_time() << ' ';
+
+   if (afls_item.tf().superseded_by_vid().size()) {
+     std::cout << std::setfill(' ') << std::setw(7)  << std::right << afls_item.tf().superseded_by_vid()   << ' '
+               << std::setfill(' ') << std::setw(7)  << std::right << afls_item.tf().superseded_by_f_seq() << ' ';
+   } else {
+     std::cout << std::setfill(' ') << std::setw(7)  << std::right << "-" << ' '
+               << std::setfill(' ') << std::setw(7)  << std::right << "-" << ' ';
+   }
+   std::cout << afls_item.af().df().path()
+             << std::endl;
+}
+
+void CtaAdminTextFormatter::printAfLsSummaryHeader()
+{
+   std::cout << TEXT_RED
+             << std::setfill(' ') << std::setw(13) << std::right << "total files" << ' '
+             << std::setfill(' ') << std::setw(12) << std::right << "total size"  << ' '
+             << TEXT_NORMAL << std::endl;
+}
+
+void CtaAdminTextFormatter::print(const cta::admin::ArchiveFileLsSummary &afls_summary)
+{
+   std::cout << std::setfill(' ') << std::setw(13) << std::right << afls_summary.total_files() << ' '
+             << std::setfill(' ') << std::setw(12) << std::right << afls_summary.total_size()  << ' '
+             << std::endl;
+}
+
+void CtaAdminTextFormatter::printFrLsHeader()
+{
+   std::cout << TEXT_RED
+             << std::setfill(' ') << std::setw(12) << std::right << "request type"   << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << "copy no"        << ' '
+             << std::setfill(' ') << std::setw(13) << std::right << "tapepool/vid"   << ' '
+             << std::setfill(' ') << std::setw(10) << std::right << "requester"      << ' '
+             << std::setfill(' ') << std::setw(6)  << std::right << "group"          << ' '
+                                                                 << "path"
+             << TEXT_NORMAL << std::endl;
+}
+
+void CtaAdminTextFormatter::print(const cta::admin::FailedRequestLsItem &frls_item)
+{
+   std::string request_type;
+   std::string tapepool_vid;
+
+   switch(frls_item.request_type()) {
+      case admin::RequestType::ARCHIVE_REQUEST:
+         request_type = "archive";
+         tapepool_vid = frls_item.tapepool();
+         break;
+      case admin::RequestType::RETRIEVE_REQUEST:
+         request_type = "retrieve";
+         tapepool_vid = frls_item.tf().vid();
+         break;
+      default:
+         throw std::runtime_error("Unrecognised request type: " + std::to_string(frls_item.request_type()));
+   }
+
+   std::cout << std::setfill(' ') << std::setw(11) << std::right << request_type                      << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << frls_item.copy_nb()               << ' '
+             << std::setfill(' ') << std::setw(14) << std::right << tapepool_vid                      << ' '
+             << std::setfill(' ') << std::setw(10) << std::right << frls_item.requester().username()  << ' '
+             << std::setfill(' ') << std::setw(6)  << std::right << frls_item.requester().groupname() << ' '
+                                                                 << frls_item.af().df().path()
+             << std::endl;
+
+   for(auto &errLogMsg : frls_item.failurelogs()) {
+     std::cout << errLogMsg << std::endl;
+   }
+}
+
+void CtaAdminTextFormatter::printFrLsSummaryHeader()
+{
+   std::cout << TEXT_RED
+             << std::setfill(' ') << std::setw(12) << std::right << "request type"        << ' '
+             << std::setfill(' ') << std::setw(13) << std::right << "total files"         << ' '
+             << std::setfill(' ') << std::setw(20) << std::right << "total size (bytes)"  << ' '
+             << TEXT_NORMAL << std::endl;
+}
+
+void CtaAdminTextFormatter::print(const cta::admin::FailedRequestLsSummary &frls_summary)
+{
+   std::string request_type =
+      frls_summary.request_type() == cta::admin::RequestType::ARCHIVE_REQUEST  ? "archive" :
+      frls_summary.request_type() == cta::admin::RequestType::RETRIEVE_REQUEST ? "retrieve" : "total";
+
+   std::cout << std::setfill(' ') << std::setw(11) << std::right << request_type               << ' '
+             << std::setfill(' ') << std::setw(13) << std::right << frls_summary.total_files() << ' '
+             << std::setfill(' ') << std::setw(20) << std::right << frls_summary.total_size()  << ' '
+             << std::endl;
+}
+
+void CtaAdminTextFormatter::printLpaHeader()
+{
+   std::cout << TEXT_RED
+             << std::setfill(' ') << std::setw(18) << std::right << "tapepool"       << ' '
+             << std::setfill(' ') << std::setw(11) << std::right << "archive id"     << ' '
+             << std::setfill(' ') << std::setw(13) << std::right << "storage class"  << ' '
+             << std::setfill(' ') << std::setw(7)  << std::right << "copy no"        << ' '
+             << std::setfill(' ') << std::setw(7)  << std::right << "disk id"        << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << "instance"       << ' '
+             << std::setfill(' ') << std::setw(13) << std::right << "checksum type"  << ' '
+             << std::setfill(' ') << std::setw(14) << std::right << "checksum value" << ' '
+             << std::setfill(' ') << std::setw(12) << std::right << "size"           << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << "user"           << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << "group"          << ' '
+             <<                                                     "path"
+             << TEXT_NORMAL << std::endl;
+}
+
+void CtaAdminTextFormatter::print(const cta::admin::ListPendingArchivesItem &lpa_item)
+{
+   std::cout << std::setfill(' ') << std::setw(18) << std::right << lpa_item.tapepool()           << ' '
+             << std::setfill(' ') << std::setw(11) << std::right << lpa_item.af().archive_id()    << ' '
+             << std::setfill(' ') << std::setw(13) << std::right << lpa_item.af().storage_class() << ' '
+             << std::setfill(' ') << std::setw(7)  << std::right << lpa_item.copy_nb()            << ' '
+             << std::setfill(' ') << std::setw(7)  << std::right << lpa_item.af().disk_id()       << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << lpa_item.af().disk_instance() << ' '
+             << std::setfill(' ') << std::setw(13) << std::right << lpa_item.af().cs().type()     << ' '
+             << std::setfill(' ') << std::setw(14) << std::right << lpa_item.af().cs().value()    << ' '
+             << std::setfill(' ') << std::setw(12) << std::right << lpa_item.af().size()          << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << lpa_item.af().df().owner()    << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << lpa_item.af().df().group()    << ' '
+             <<                                                     lpa_item.af().df().path()
+             << std::endl;
+}
+
+void CtaAdminTextFormatter::printLpaSummaryHeader()
+{
+   std::cout << TEXT_RED
+             << std::setfill(' ') << std::setw(18) << std::right << "tapepool"    << ' '
+             << std::setfill(' ') << std::setw(13) << std::right << "total files" << ' '
+             << std::setfill(' ') << std::setw(12) << std::right << "total size"  << ' '
+             << TEXT_NORMAL << std::endl;
+}
+
+void CtaAdminTextFormatter::print(const cta::admin::ListPendingArchivesSummary &lpa_summary)
+{
+   std::cout << std::setfill(' ') << std::setw(18) << std::right << lpa_summary.tapepool()    << ' '
+             << std::setfill(' ') << std::setw(13) << std::right << lpa_summary.total_files() << ' '
+             << std::setfill(' ') << std::setw(12) << std::right << lpa_summary.total_size()  << ' '
+             << std::endl;
+}
+
+void CtaAdminTextFormatter::printLprHeader()
+{
+   std::cout << TEXT_RED
+             << std::setfill(' ') << std::setw(13) << std::right << "vid"        << ' '
+             << std::setfill(' ') << std::setw(11) << std::right << "archive id" << ' '
+             << std::setfill(' ') << std::setw(7)  << std::right << "copy no"    << ' '
+             << std::setfill(' ') << std::setw(7)  << std::right << "fseq"       << ' '
+             << std::setfill(' ') << std::setw(9)  << std::right << "block id"   << ' '
+             << std::setfill(' ') << std::setw(12) << std::right << "size"       << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << "user"       << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << "group"      << ' '
+             <<                                                     "path"
+             << TEXT_NORMAL << std::endl;
+}
+
+void CtaAdminTextFormatter::print(const cta::admin::ListPendingRetrievesItem &lpr_item)
+{
+   std::cout << std::setfill(' ') << std::setw(13) << std::right << lpr_item.tf().vid()        << ' '
+             << std::setfill(' ') << std::setw(11) << std::right << lpr_item.af().archive_id() << ' '
+             << std::setfill(' ') << std::setw(7)  << std::right << lpr_item.copy_nb()         << ' '
+             << std::setfill(' ') << std::setw(7)  << std::right << lpr_item.tf().f_seq()      << ' '
+             << std::setfill(' ') << std::setw(9)  << std::right << lpr_item.tf().block_id()   << ' '
+             << std::setfill(' ') << std::setw(12) << std::right << lpr_item.af().size()       << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << lpr_item.af().df().owner() << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << lpr_item.af().df().group() << ' '
+             <<                                                     lpr_item.af().df().path()
+             << std::endl;
+}
+
+void CtaAdminTextFormatter::printLprSummaryHeader()
+{
+   std::cout << TEXT_RED
+             << std::setfill(' ') << std::setw(13) << std::right << "vid"         << ' '
+             << std::setfill(' ') << std::setw(13) << std::right << "total files" << ' '
+             << std::setfill(' ') << std::setw(12) << std::right << "total size"  << ' '
+             << TEXT_NORMAL << std::endl;
+}
+
+void CtaAdminTextFormatter::print(const cta::admin::ListPendingRetrievesSummary &lpr_summary)
+{
+   std::cout << std::setfill(' ') << std::setw(13) << std::right << lpr_summary.vid()         << ' '
+             << std::setfill(' ') << std::setw(13) << std::right << lpr_summary.total_files() << ' '
+             << std::setfill(' ') << std::setw(12) << std::right << lpr_summary.total_size()  << ' '
+             << std::endl;
+}
+
+void CtaAdminTextFormatter::printTpLsHeader()
+{
+   std::cout << TEXT_RED
+             << std::setfill(' ') << std::setw(18) << std::right << "name"        << ' '
+             << std::setfill(' ') << std::setw(10) << std::right << "vo"          << ' '
+             << std::setfill(' ') << std::setw(7)  << std::right << "#tapes"      << ' '
+             << std::setfill(' ') << std::setw(9)  << std::right << "#partial"    << ' '
+             << std::setfill(' ') << std::setw(12) << std::right << "#phys files" << ' '
+             << std::setfill(' ') << std::setw(5)  << std::right << "size"        << ' '
+             << std::setfill(' ') << std::setw(5)  << std::right << "used"        << ' '
+             << std::setfill(' ') << std::setw(6)  << std::right << "avail"       << ' '
+             << std::setfill(' ') << std::setw(6)  << std::right << "use%"        << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << "encrypt"     << ' '
+             << std::setfill(' ') << std::setw(20) << std::right << "supply"      << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << "c.user"      << ' '
+             << std::setfill(' ') << std::setw(25) << std::right << "c.host"      << ' '
+             << std::setfill(' ') << std::setw(24) << std::right << "c.time"      << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << "m.user"      << ' '
+             << std::setfill(' ') << std::setw(25) << std::right << "m.host"      << ' '
+             << std::setfill(' ') << std::setw(24) << std::right << "m.time"      << ' '
+             <<                                                     "comment"     << ' '
+             << TEXT_NORMAL << std::endl;
+}
+
+void CtaAdminTextFormatter::printTapeLsHeader(){
+  std::cout << TEXT_RED
+            << std::setfill(' ') << std::setw(7) << std::right << "vid"              << ' '
+            << std::setfill(' ') << std::setw(10) << std::right << "media type"       << ' '
+            << std::setfill(' ') << std::setw(7)  << std::right << "vendor"           << ' '
+            << std::setfill(' ') << std::setw(20)  << std::right << "logical library"  << ' '
+            << std::setfill(' ') << std::setw(18) << std::right << "tapepool"         << ' '
+            << std::setfill(' ') << std::setw(10)  << std::right << "vo"               << ' '
+            << std::setfill(' ') << std::setw(20)  << std::right << "encryption key"   << ' '
+            << std::setfill(' ') << std::setw(12)  << std::right << "capacity"         << ' '
+            << std::setfill(' ') << std::setw(12)  << std::right << "occupancy"        << ' '
+            << std::setfill(' ') << std::setw(9)  << std::right << "last fseq"        << ' '
+            << std::setfill(' ') << std::setw(5)  << std::right << "full"             << ' '
+            << std::setfill(' ') << std::setw(8) << std::right << "disabled"         << ' '
+            << std::setfill(' ') << std::setw(12) << std::right << "label drive"      << ' '
+            << std::setfill(' ') << std::setw(12)  << std::right << "label time"       << ' '
+            << std::setfill(' ') << std::setw(12) << std::right << "last w drive"     << ' '
+            << std::setfill(' ') << std::setw(12) << std::right << "last w time"      << ' '
+            << std::setfill(' ') << std::setw(12) << std::right << "last r drive"     << ' '
+            << std::setfill(' ') << std::setw(12) << std::right << "last r time"      << ' '
+            << std::setfill(' ') << std::setw(20) << std::right << "c.user"           << ' '
+            << std::setfill(' ') << std::setw(25) << std::right << "c.host"           << ' '
+            << std::setfill(' ') << std::setw(13) << std::right << "c.time"           << ' '
+            << std::setfill(' ') << std::setw(20) << std::right << "m.user"           << ' '
+            << std::setfill(' ') << std::setw(25) << std::right << "m.host"           << ' '
+            << std::setfill(' ') << std::setw(13) << std::right << "m.time"           << ' '
+            <<                                                     "comment"          << ' '
+            << TEXT_NORMAL << std::endl;
+}
+
+
+void CtaAdminTextFormatter::print(const cta::admin::TapeLsItem &tals_item){
+  std::cout << std::setfill(' ') << std::setw(7) << std::right << tals_item.vid()        << ' '
+            << std::setfill(' ') << std::setw(10) << std::right << tals_item.media_type() << ' '
+            << std::setfill(' ') << std::setw(7)  << std::right << tals_item.vendor()     << ' '
+            << std::setfill(' ') << std::setw(20)  << std::right << tals_item.logical_library() << ' '
+            << std::setfill(' ') << std::setw(18) << std::right << tals_item.tapepool()         << ' '
+            << std::setfill(' ') << std::setw(10)  << std::right << tals_item.vo()               << ' '
+            << std::setfill(' ') << std::setw(20)  << std::right << tals_item.encryption_key()   << ' '
+            << std::setfill(' ') << std::setw(12)  << std::right << tals_item.capacity()         << ' '
+            << std::setfill(' ') << std::setw(12)  << std::right << tals_item.occupancy()       << ' '
+            << std::setfill(' ') << std::setw(9)  << std::right << tals_item.last_fseq()       << ' '
+            << std::setfill(' ') << std::setw(5)  << std::right << tals_item.full()           << ' '
+            << std::setfill(' ') << std::setw(8) << std::right << tals_item.disabled()         << ' ';
+  if(tals_item.has_label_log()){
+    std::cout << std::setfill(' ') << std::setw(12) << std::right << tals_item.label_log().drive() << ' '
+              << std::setfill(' ') << std::setw(12) << std::right << tals_item.label_log().time() << ' ';
+  } else {
+    std::cout << std::setfill(' ') << std::setw(12) << std::right << "-" << ' '
+              << std::setfill(' ') << std::setw(12) << std::right << "-" << ' ';
+  }
+  if(tals_item.has_last_written_log()){
+    std::cout << std::setfill(' ') << std::setw(12) << std::right << tals_item.last_written_log().drive() << ' '
+              << std::setfill(' ') << std::setw(12) << std::right << tals_item.last_written_log().time() << ' ';
+  } else {
+    std::cout << std::setfill(' ') << std::setw(12) << "-" << ' '
+              << std::setfill(' ') << std::setw(12) << "-" << ' ';
+  }
+  if(tals_item.has_last_read_log()){
+    std::cout << std::setfill(' ') << std::setw(12) << std::right << tals_item.last_read_log().drive() << ' '
+              << std::setfill(' ') << std::setw(12) << std::right << tals_item.last_read_log().time() << ' ';
+  } else {
+    std::cout << std::setfill(' ') << std::setw(12) << std::right << "-" << ' '
+              << std::setfill(' ') << std::setw(12) << std::right << "-" << ' ';
+  }
+    std::cout << std::setfill(' ') << std::setw(20) << std::right << tals_item.creation_log().username()          << ' '
+              << std::setfill(' ') << std::setw(25) << std::right << tals_item.creation_log().host()          << ' '
+              << std::setfill(' ') << std::setw(13) << std::right << tals_item.creation_log().time()           << ' '
+              << std::setfill(' ') << std::setw(20) << std::right << tals_item.last_modification_log().username()        << ' '
+              << std::setfill(' ') << std::setw(25) << std::right << tals_item.last_modification_log().host()          << ' '
+              << std::setfill(' ') << std::setw(13) << std::right << tals_item.last_modification_log().time()           << ' '
+              << std::endl;
+}
+
+void CtaAdminTextFormatter::printRepackLsHeader(){
+  std::cout << TEXT_RED
+            << std::setfill(' ') << std::setw(7) << std::right << "vid"              << ' '
+            << std::setfill(' ') << std::setw(50) << std::right << "repackBufferURL"       << ' '
+            << std::setfill(' ') << std::setw(17)  << std::right << "userProvidedFiles"           << ' '
+            << std::setfill(' ') << std::setw(20)  << std::right << "totalFilesToRetrieve" << ' '
+            << std::setfill(' ') << std::setw(19) << std::right <<  "totalBytesToRetrieve" << ' '
+            << std::setfill(' ') << std::setw(20)  << std::right << "totalFilesToArchive"  << ' '
+            << std::setfill(' ') << std::setw(19) << std::right <<  "totalBytesToArchive"  << ' '
+            << std::setfill(' ') << std::setw(14)  << std::right << "retrievedFiles"               << ' '
+            << std::setfill(' ') << std::setw(13)  << std::right << "archivedFiles"   << ' '
+            << std::setfill(' ') << std::setw(21)  << std::right << "failedToRetrieveFiles"         << ' '
+            << std::setfill(' ') << std::setw(20)  << std::right << "failedToRetrieveBytes"        << ' '
+            << std::setfill(' ') << std::setw(20)  << std::right <<  "failedToArchiveFiles"        << ' '
+            << std::setfill(' ') << std::setw(20)  << std::right <<  "failedToArchiveBytes"             << ' '
+            << std::setfill(' ') << std::setw(16)  << std::right <<  "lastExpandedFSeq"             << ' '
+            <<                                                      "status"         << ' '
+            << TEXT_NORMAL << std::endl;
+}
+
+void CtaAdminTextFormatter::print(const cta::admin::RepackLsItem &rels_item){
+  std::cout << std::setfill(' ') << std::setw(7) << std::right << rels_item.vid()           << ' '
+            << std::setfill(' ') << std::setw(50) << std::right << rels_item.repack_buffer_url()      << ' '
+            << std::setfill(' ') << std::setw(17)  << std::right << rels_item.user_provided_files()           << ' '
+            << std::setfill(' ') << std::setw(20)  << std::right << rels_item.total_files_to_retrieve() << ' '
+            << std::setfill(' ') << std::setw(19) << std::right <<  rels_item.total_bytes_to_retrieve() << ' '
+            << std::setfill(' ') << std::setw(20)  << std::right << rels_item.total_files_to_archive()  << ' '
+            << std::setfill(' ') << std::setw(19) << std::right <<  rels_item.total_bytes_to_archive()  << ' '
+            << std::setfill(' ') << std::setw(14)  << std::right << rels_item.retrieved_files()               << ' '
+            << std::setfill(' ') << std::setw(13)  << std::right << rels_item.archived_files()   << ' '
+            << std::setfill(' ') << std::setw(21)  << std::right << rels_item.failed_to_retrieve_files()        << ' '
+            << std::setfill(' ') << std::setw(20)  << std::right << rels_item.failed_to_retrieve_bytes()        << ' '
+            << std::setfill(' ') << std::setw(20)  << std::right <<  rels_item.failed_to_archive_files()       << ' '
+            << std::setfill(' ') << std::setw(20)  << std::right <<  rels_item.failed_to_retrieve_bytes()         << ' '
+            << std::setfill(' ') << std::setw(10)  << std::right <<  rels_item.last_expanded_fseq()             << ' '
+            << rels_item.status() << std::endl;
+}
+
+void CtaAdminTextFormatter::print(const cta::admin::TapePoolLsItem &tpls_item)
+{
+   std::string encrypt_str = tpls_item.encrypt() ? "true" : "false";
+   uint64_t avail = tpls_item.capacity_bytes() > tpls_item.data_bytes() ?
+      tpls_item.capacity_bytes()-tpls_item.data_bytes() : 0; 
+   double use_percent = tpls_item.capacity_bytes() > 0 ?
+      (static_cast<double>(tpls_item.data_bytes())/static_cast<double>(tpls_item.capacity_bytes()))*100.0 : 0.0;
+
+   std::cout << std::setfill(' ') << std::setw(18) << std::right << tpls_item.name()                          << ' '
+             << std::setfill(' ') << std::setw(10) << std::right << tpls_item.vo()                            << ' '
+             << std::setfill(' ') << std::setw(7)  << std::right << tpls_item.num_tapes()                     << ' '
+             << std::setfill(' ') << std::setw(9)  << std::right << tpls_item.num_partial_tapes()             << ' '
+             << std::setfill(' ') << std::setw(12) << std::right << tpls_item.num_physical_files()            << ' '
+             << std::setfill(' ') << std::setw(4)  << std::right << tpls_item.capacity_bytes() / 1000000000   << "G "
+             << std::setfill(' ') << std::setw(4)  << std::right << tpls_item.data_bytes()     / 1000000000   << "G "
+             << std::setfill(' ') << std::setw(5)  << std::right << avail                      / 1000000000   << "G "
+             << std::setfill(' ') << std::setw(5)  << std::right << std::fixed << std::setprecision(1) << use_percent << "% "
+             << std::setfill(' ') << std::setw(8)  << std::right << encrypt_str                               << ' '
+             << std::setfill(' ') << std::setw(20) << std::right << tpls_item.supply()                        << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << tpls_item.created().username()            << ' '
+             << std::setfill(' ') << std::setw(25) << std::right << tpls_item.created().host()                << ' '
+             << std::setfill(' ') << std::setw(24) << std::right << timeToString(tpls_item.created().time())  << ' '
+             << std::setfill(' ') << std::setw(8)  << std::right << tpls_item.modified().username()           << ' '
+             << std::setfill(' ') << std::setw(25) << std::right << tpls_item.modified().host()               << ' '
+             << std::setfill(' ') << std::setw(24) << std::right << timeToString(tpls_item.modified().time()) << ' '
+             <<                                                     tpls_item.comment()
+             << std::endl;
+}
+
+}}
diff --git a/cmdline/CtaAdminTextFormatter.hpp b/cmdline/CtaAdminTextFormatter.hpp
new file mode 100644
index 0000000000..3c3c36b3ef
--- /dev/null
+++ b/cmdline/CtaAdminTextFormatter.hpp
@@ -0,0 +1,69 @@
+/*!
+ * @project        The CERN Tape Archive (CTA)
+ * @brief          Text formatter for CTA Admin command tool
+ * @description    CTA Admin command using Google Protocol Buffers and XRootD SSI transport
+ * @copyright      Copyright 2019 CERN
+ * @license        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 3 of the License, or
+ *                 (at your option) any later version.
+ *
+ *                 This program is distributed in the hope that it will be useful,
+ *                 but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *                 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *                 GNU General Public License for more details.
+ *
+ *                 You should have received a copy of the GNU General Public License
+ *                 along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "CtaFrontendApi.hpp"
+
+namespace cta {
+namespace admin {
+
+class CtaAdminTextFormatter
+{
+public:
+   // Output headers
+   static void printAfLsHeader();
+   static void printAfLsSummaryHeader();
+   static void printFrLsHeader();
+   static void printFrLsSummaryHeader();
+   static void printLpaHeader();
+   static void printLpaSummaryHeader();
+   static void printLprHeader();
+   static void printLprSummaryHeader();
+   static void printTpLsHeader();
+   static void printTapeLsHeader();
+   static void printRepackLsHeader();
+   
+   // Output records
+   static void print(const ArchiveFileLsItem &afls_item);
+   static void print(const ArchiveFileLsSummary &afls_summary);
+   static void print(const FailedRequestLsItem &frls_item);
+   static void print(const FailedRequestLsSummary &frls_summary);
+   static void print(const ListPendingArchivesItem &lpa_item);
+   static void print(const ListPendingArchivesSummary &lpa_summary);
+   static void print(const ListPendingRetrievesItem &lpr_item);
+   static void print(const ListPendingRetrievesSummary &lpr_summary);
+   static void print(const TapePoolLsItem &tpls_item);
+   static void print(const TapeLsItem &tals_item);
+   static void print(const RepackLsItem &rels_item);
+
+private:
+   // Static method to convert time to string
+   static std::string timeToString(const time_t &time)
+   {
+      std::string timeString(ctime(&time));
+      timeString.resize(timeString.size()-1); //remove newline
+      return timeString;
+   }
+
+   static constexpr const char* const TEXT_RED    = "\x1b[31;1m";     //!< Terminal formatting code for red text
+   static constexpr const char* const TEXT_NORMAL = "\x1b[0m";        //!< Terminal formatting code for normal text
+};
+
+}}
-- 
GitLab