monotone-devel
[Top][All Lists]
Advanced

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

[Monotone-devel] patch: formatting of automate output


From: rghetta
Subject: [Monotone-devel] patch: formatting of automate output
Date: Sun, 17 Apr 2005 22:48:02 +0200
User-agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.6) Gecko/20050322

The attached patch adds some optional formatting to automate output.
It's more a proof of concept than a complete implementation.
Right now works only at the cert level, changeset still need some thought.
It adds two options applicable to all automate commands outputting
revision ids:

--xml   
formats as an xml stream. Takes precedence over --format

-- format="format spec"
Uses a printf like format string. Right now are implemented these format
specifiers:
%a      : value of author cert
%d      : value of date cert
%e      : value of comment cert
%i      : revision id
%l      : value of changelog cert
%s      : value of testresult cert
%t      : value of tag cert
%%      : the % char

It knows also about \n, \r, \a, \t, \b, \f, \v (same meaning of printf).
\\ is used to obtain the \ char.  \ is also an escape char of most
shells, use ' as the string delimiter.
Traditional automate output is equivalent to '%i\n' (default if no fmt
string specified)

Comments welcome,
        Riccardo


# 
# add_file "format.cc"
# 
# add_file "format.hh"
# 
# patch "Makefile.am"
#  from [ad9e4d27f5baadaf99c7485aebecc2388b757d9a]
#    to [d0becf27faf8004f507fb69d1a39640c3e7c792e]
# 
# patch "app_state.cc"
#  from [63120a266ad19ebd72022cf172ccbd0c299aa413]
#    to [f765950b2adc0a5ca9d583668871ea54313ffc8f]
# 
# patch "app_state.hh"
#  from [9cfdb7a5976dc31edda11e99ee74046fdc318469]
#    to [2688c96ff296b8457bbb52233376c7030027ad9e]
# 
# patch "automate.cc"
#  from [f10d2c6090c993b5c05fb824581aded812f59871]
#    to [aebfc244cd560872619a0f678be33cc6d552fefb]
# 
# patch "format.cc"
#  from []
#    to [8426726b657b73d7879d2115c21b04670197597c]
# 
# patch "format.hh"
#  from []
#    to [42e3fffe2c47370ec888babd6acc961877dd554d]
# 
# patch "monotone.cc"
#  from [2419f27fdb3cba77c658c0f4eac4d0b5026e1884]
#    to [b7be4b6724b9ac314ce588a6bbf13c4b75003e96]
# 
--- Makefile.am
+++ Makefile.am
@@ -9,7 +9,7 @@
          constants.cc netsync.cc netcmd.cc merkle_tree.cc basic_io.cc  \
          mkstemp.cc lcs.cc rcs_import.hh rcs_import.cc revision.cc     \
         change_set.cc mt_version.cc automate.cc database_check.cc      \
-        path_component.cc epoch.cc inodeprint.cc                       \
+        path_component.cc epoch.cc inodeprint.cc format.cc             \
                                                                        \
          app_state.hh commands.hh file_io.hh manifest.hh packet.hh     \
         sanity.hh update.hh work.hh cert.hh database.hh keys.hh        \
@@ -23,7 +23,7 @@
         mkstemp.hh mt_version.hh automate.hh database_check.hh smap.hh \
         gettext.h package_revision.c package_full_revision.c           \
         path_component.hh epoch.hh package_full_revision.h             \
-        package_revision.h inodeprint.hh
+        package_revision.h inodeprint.hh format.hh
 
 NETXX_SOURCES =                                                                
        \
        netxx/accept.cxx netxx/accept.h netxx/address.cxx                       
\
--- app_state.cc
+++ app_state.cc
@@ -31,7 +31,8 @@
 
 app_state::app_state() 
   : branch_name(""), db(""), stdhooks(true), rcfiles(true),
-    search_root("/"), depth(-1)
+    search_root("/"), depth(-1),
+    format_string("%i\\n"), xml_enabled(false)
 {
   db.set_app(this);
 }
@@ -281,6 +282,18 @@
 }
 
 void
+app_state::set_fmtstring(utf8 const & f)
+{
+  format_string = f;
+}
+
+void
+app_state::set_xml()
+{
+  xml_enabled = true;
+}
+
+void
 app_state::add_revision(utf8 const & selector)
 {
   revision_selectors.push_back(selector);
--- app_state.hh
+++ app_state.hh
@@ -40,7 +40,10 @@
   file_path relative_directory;
   bool found_working_copy;
   long depth;
+  utf8 format_string;
+  bool xml_enabled;    
 
+
   void allow_working_copy();
   void require_working_copy();
   void create_working_copy(std::string const & dir);
@@ -69,6 +72,10 @@
   void set_rcfiles(bool b);
   void add_rcfile(utf8 const & filename);
 
+  void set_fmtstring(utf8 const & fmtstring);
+  void set_xml();
+
+
   explicit app_state();
   ~app_state();
 
--- automate.cc
+++ automate.cc
@@ -11,6 +11,7 @@
 #include "app_state.hh"
 #include "commands.hh"
 #include "revision.hh"
+#include "format.hh"
 
 static std::string const interface_version = "0.1";
 
@@ -55,8 +56,9 @@
 
   std::set<revision_id> heads;
   get_branch_heads(idx(args, 0)(), app, heads);
-  for (std::set<revision_id>::const_iterator i = heads.begin(); i != 
heads.end(); ++i)
-    output << (*i).inner()() << std::endl;
+
+  FormatFunc fmt(output, app);
+  for_each(heads.begin(), heads.end(), fmt);
 }
 
 // Name: descendents
@@ -101,9 +103,9 @@
             }
         }
     }
-  for (std::set<revision_id>::const_iterator i = descendents.begin();
-       i != descendents.end(); ++i)
-    output << (*i).inner()() << std::endl;
+
+  FormatFunc fmt(output, app);
+  for_each(descendents.begin(), descendents.end(), fmt);
 }
 
 // Name: erase_ancestors
@@ -133,8 +135,9 @@
       revs.insert(rid);
     }
   erase_ancestors(revs, app);
-  for (std::set<revision_id>::const_iterator i = revs.begin(); i != 
revs.end(); ++i)
-    output << (*i).inner()() << std::endl;
+
+  FormatFunc fmt(output, app);
+  for_each(revs.begin(), revs.end(), fmt);
 }
 
 // Name: toposort
@@ -162,9 +165,9 @@
     }
   std::vector<revision_id> sorted;
   toposort(revs, sorted, app);
-  for (std::vector<revision_id>::const_iterator i = sorted.begin();
-       i != sorted.end(); ++i)
-    output << (*i).inner()() << std::endl;
+
+  FormatFunc fmt(output, app);
+  for_each(sorted.begin(), sorted.end(), fmt);
 }
 
 // Name: ancestry_difference
@@ -208,9 +211,9 @@
 
   std::vector<revision_id> sorted;
   toposort(ancestors, sorted, app);
-  for (std::vector<revision_id>::const_iterator i = sorted.begin();
-       i != sorted.end(); ++i)
-    output << (*i).inner()() << std::endl;
+
+  FormatFunc fmt(output, app);
+  for_each(sorted.begin(), sorted.end(), fmt);
 }
 
 // Name: leaves
@@ -243,8 +246,9 @@
   for (std::multimap<revision_id, revision_id>::const_iterator i = 
graph.begin();
        i != graph.end(); ++i)
     leaves.erase(i->first);
-  for (std::set<revision_id>::const_iterator i = leaves.begin(); i != 
leaves.end(); ++i)
-    output << (*i).inner()() << std::endl;
+
+  FormatFunc fmt(output, app);
+  for_each(leaves.begin(), leaves.end(), fmt);
 }
 
 void
--- format.cc
+++ format.cc
@@ -0,0 +1,332 @@
+// copyright (C) 2005 R.Ghetta <address@hidden>
+// all rights reserved.
+// licensed to the public under the terms of the GNU GPL (>= 2)
+// see the file COPYING for details
+
+#include <app_state.hh>
+#include <revision.hh>
+#include <transforms.hh>
+#include <format.hh>
+
+using namespace std;
+
+// helper functions to navigate the given revision - shamelessy copied from 
'log' command
+void
+walk_edges (const app_state & app, const revision_set & rev,
+           set < revision_id > &ancestors, vector < change_set > &changes,
+           set < file_path > &modified_files)
+{
+  for (edge_map::const_iterator e = rev.edges.begin ();
+       e != rev.edges.end (); ++e)
+    {
+      ancestors.insert (edge_old_revision (e));
+
+      change_set const &cs = edge_changes (e);
+      change_set::path_rearrangement const &pr = cs.rearrangement;
+
+      changes.push_back (cs);
+
+      for (change_set::delta_map::const_iterator i = cs.deltas.begin ();
+           i != cs.deltas.end (); i++)
+      {
+        if (pr.added_files.find (i->first ()) == pr.added_files.end ())
+          modified_files.insert (i->first ());
+      }
+    }
+}
+
+// ---------------------- formatting functor ----------------------------
+// IMPORTANT: to complete formatting, it *must* go out of scope (i.e. its 
destructor
+// called)
+FormatFunc::FormatFunc(std::ostream &out, app_state &app)
+{
+  if (app.xml_enabled)
+    fmt = auto_ptr<BaseFormatter>(new XMLFormatter(out, app));
+  else 
+    fmt = auto_ptr<BaseFormatter>(new PrintFormatter(out, app, 
app.format_string));
+}
+
+FormatFunc::~FormatFunc()
+{
+}
+   
+// ---------------------- base formatter ----------------------------
+BaseFormatter::BaseFormatter(app_state &a):
+app(a)
+{
+}
+
+BaseFormatter::~BaseFormatter()
+{
+}
+
+// ---------------------- format string support ----------------------------
+PrintFormatter::PrintFormatter(std::ostream & o, app_state &a, const utf8 
&fmt):
+BaseFormatter(a),
+fmtstring(fmt),
+out(o)
+{
+}
+
+PrintFormatter::~PrintFormatter()
+{
+}
+
+void
+PrintFormatter::format_cert (vector < revision < cert > >&certs, const string 
&name)
+{
+  for (vector < revision < cert > >::const_iterator i = certs.begin ();
+       i != certs.end (); ++i)
+    {
+      if (i->inner ().name () == name)
+        {
+          cert_value tv;
+          decode_base64 (i->inner ().value, tv);
+          out << tv;
+          return;
+        }
+    }
+}
+
+void 
+PrintFormatter::handle_control(string::const_iterator &it, const 
string::const_iterator &end)
+{
+  ++it;
+  if (it == end)
+    return;
+  switch (*it)
+    {
+    case '\\':
+      out << '\\';
+      break;
+    case 'n':
+      out<< endl;
+      break;
+    case 't':
+      out << '\t';
+      break;
+    case 'a':
+      out << '\a';
+      break;
+    case 'b':
+      out << '\b';
+      break;
+    case 'f':
+      out << '\f';
+      break;
+    case 'r':
+      out << '\r';
+      break;
+    case 'v':
+      out << '\v';
+      break;
+    default:
+      N (false, F ("\ninvalid control char %c\n") % (*it));
+      return;
+    }
+}
+
+void
+PrintFormatter::apply(const revision_id & rid)
+{
+  if (!app.db.revision_exists (rid))
+    {
+      L (F ("revision %s does not exist in db\n") % rid);
+      return;
+    }
+
+  revision_set rev;
+  app.db.get_revision (rid, rev);
+
+  vector < revision < cert > >certs;
+  app.db.get_revision_certs (rid, certs);
+  erase_bogus_certs (certs, app);
+
+  string::const_iterator it = fmtstring ().begin ();
+  while (it != fmtstring ().end ())
+    {
+      if ((*it) == '%')
+        {
+          ++it;
+          if (it == fmtstring ().end ())
+            return;
+          switch (*it)
+            {
+            case '%':
+              out << '%';
+              break;
+            case 'd':
+              format_cert (certs, date_cert_name);
+              break;
+            case 'a':
+              format_cert (certs, author_cert_name);
+              break;
+            case 't':
+              format_cert (certs, tag_cert_name);
+              break;
+            case 'l':
+              format_cert (certs, changelog_cert_name);
+              break;
+            case 'e':
+              format_cert (certs, comment_cert_name);
+              break;
+            case 's':
+              format_cert (certs, testresult_cert_name);
+              break;
+            case 'i':
+              out << rev.new_manifest.inner()();
+              break;
+            default:
+              N (false, F ("invalid format string"));
+              return;
+            }
+        }
+      else if ( (*it) == '\\')
+        handle_control(it, fmtstring ().end ());
+      else
+        out << (*it);
+      
+      ++it;
+    }
+}
+
+
+// --------------- XML support -----------------
+
+XMLWriter::XMLWriter (ostream & o):
+out (o),
+open_tags(),
+decl_emitted(false)
+{
+}
+
+XMLWriter::~XMLWriter ()
+{
+  I (open_tags.size () == 0);  // forgot to closing some tags ?
+}
+
+void
+XMLWriter::tag (const utf8 & tagname)
+{
+  if (!decl_emitted)
+  {
+     out << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>" 
<< endl;
+     decl_emitted=true;
+  }
+  out << "<" << tagname << ">" << endl;
+  open_tags.push_back (tagname);
+}
+
+void
+XMLWriter::end ()
+{
+  I (open_tags.size () > 0);
+  out << "</" << open_tags.back () << ">" << endl;
+  open_tags.pop_back ();
+}
+
+void
+XMLWriter::cdata (const string & opq)
+{
+  I (open_tags.size () > 0);
+  
+  for (string::const_iterator i = opq.begin(); i != opq.end(); ++i)
+  {
+     switch ((*i))
+    {
+        case '<': out << "&lt;"; break;
+        case '>': out << "&gt;"; break;
+        case '&': out << "&amp;"; break;
+        case '"': out << "&quot;"; break;
+        case '\'': out << "&apos;"; break;
+        default: out << *i; break;
+    }
+  }     
+}
+
+// ---------------- the xml formatter -----------------------
+XMLFormatter::XMLFormatter(ostream &out, app_state &a):
+BaseFormatter(a),
+xw(out)
+{
+   xw.tag("monotone"); // docroot
+}
+
+XMLFormatter::~XMLFormatter()
+{
+   xw.end();
+}
+
+void
+XMLFormatter::xml_manifest(const manifest_id & mid)
+{
+  xw.tag("manifest");
+  xw.cdata (mid.inner ()());   
+  xw.end();
+}
+
+// dumps all *valid* certs associated to a revision
+// FIXME: could be useful to optionally dump an invalid cert marking it with 
another
+// tag (invalid_cert) or an attribute (valid=true/false)
+void
+XMLFormatter::xml_certs (const revision_id & rid)
+{
+  vector < revision < cert > >certs;
+
+  app.db.get_revision_certs (rid, certs);
+  erase_bogus_certs (certs, app);
+  for (vector < revision < cert > >::const_iterator i = certs.begin ();
+       i != certs.end (); ++i)
+    {
+      xw.tag ("cert");
+      xw.tag ("name");
+      xw.cdata (i->inner ().name ());
+      xw.end ();
+
+      xw.tag ("value");
+      cert_value tv;
+      decode_base64 (i->inner ().value, tv);
+      xw.cdata (tv ());
+      xw.end ();
+
+      xw.tag ("key-id");
+      xw.cdata (i->inner ().key ());
+      xw.end ();
+
+      xw.tag ("signature");
+      xw.cdata (i->inner ().sig ());   // only makes sense if encoded
+      xw.end ();
+
+      xw.end ();
+    }
+
+}
+
+void
+XMLFormatter::xml_changeset(const revision_set & rev)
+{
+}
+
+// dumps recursively a revision
+void
+XMLFormatter::apply(const revision_id & rid)
+{
+  if (!app.db.revision_exists (rid))
+    {
+      L (F ("revision %s does not exist in db\n") % rid);
+      return;
+    }
+
+  revision_set rev;
+  app.db.get_revision (rid, rev);
+
+  xw.tag ("revision");
+  xw.tag ("id");
+  xw.cdata(rid.inner()());
+  xw.end();
+  xml_manifest(rev.new_manifest);
+  xml_certs (rid);
+  xml_changeset(rev);
+   
+  xw.end ();
+}
--- format.hh
+++ format.hh
@@ -0,0 +1,100 @@
+#ifndef __FORMAT_HH__
+#define __FORMAT_HH__
+
+// copyright (C) 2005 R.Ghetta <address@hidden>
+// all rights reserved.
+// licensed to the public under the terms of the GNU GPL (>= 2)
+// see the file COPYING for details
+
+#include "vocab.hh"
+
+#include <ostream>
+#include <vector>
+#include <string>
+#include <algorithm>
+#include <memory>
+
+// base class of all formatters
+class BaseFormatter 
+{
+public:
+   BaseFormatter(app_state &app); 
+   virtual ~BaseFormatter(); 
+
+   virtual void apply(const revision_id &rid) = 0;
+
+protected:
+  app_state &app;
+};
+
+// functor formatter
+// IMPORTANT: to complete formatting, it *must* go out of scope (i.e. its 
destructor
+// called)
+class FormatFunc : public std::unary_function<revision_id, void>
+{
+public:
+   FormatFunc(std::ostream &out, app_state &app);
+   ~FormatFunc();
+   void operator ()(const revision_id &rid) { fmt->apply(rid); }
+private:
+   std::auto_ptr<BaseFormatter> fmt;   
+};
+
+
+class PrintFormatter : public BaseFormatter 
+{
+public:
+  PrintFormatter(std::ostream & out, app_state &app, const utf8 &fmtstring);
+  ~PrintFormatter();
+
+  // applies the fmt string to the given revision
+  void apply(const revision_id &rid);
+
+private:
+  void format_cert (std::vector < revision < cert > >&certs, const std::string 
&name);
+  void handle_control(std::string::const_iterator &it, const 
std::string::const_iterator &end);
+
+private:
+  const utf8 &fmtstring;
+  std::ostream & out; 
+};
+
+
+// a very rudimentary XML writer
+class XMLWriter
+{
+public:
+  explicit XMLWriter (std::ostream & );
+  ~XMLWriter ();      
+
+  void tag(const utf8 &tagname);
+  void tag(const std::string &tagname) { tag(utf8(tagname)); }
+  void cdata(const utf8 &opq) { cdata(opq()); }
+  void cdata(const std::string &opq);
+  void end();
+
+private:
+  std::ostream &out;
+  std::vector<utf8> open_tags;
+  bool decl_emitted;
+};
+
+class XMLFormatter: public BaseFormatter 
+{
+public:
+  XMLFormatter(std::ostream &out, app_state &app);
+  ~XMLFormatter();
+
+  // applies the formatting to the given revision
+  void apply(const revision_id &rid);
+
+private:
+  void xml_manifest(const manifest_id & mid);
+  void xml_certs (const revision_id & rid);
+  void xml_changeset(const revision_set & rev);
+
+private:
+  XMLWriter xw;
+};
+
+#endif  // header guard
--- monotone.cc
+++ monotone.cc
@@ -45,6 +45,8 @@
 #define OPT_ROOT 16
 #define OPT_DEPTH 17
 #define OPT_ARGFILE 18
+#define OPT_FORMAT 19
+#define OPT_XML 20
 
 // main option processing and exception handling code
 
@@ -73,6 +75,8 @@
     {"root", 0, POPT_ARG_STRING, &argstr, OPT_ROOT, "limit search for working 
copy to specified root", NULL},
     {"depth", 0, POPT_ARG_LONG, &arglong, OPT_DEPTH, "limit the log output to 
the given number of entries", NULL},
     {"xargs", '@', POPT_ARG_STRING, &argstr, OPT_ARGFILE, "insert command line 
arguments taken from the given file", NULL},
+    {"format", 0, POPT_ARG_STRING, &argstr, OPT_FORMAT, "specifies a format 
string on automate output", NULL},
+    {"xml", 0, POPT_ARG_NONE, NULL, OPT_XML, "automate output will be in XML", 
NULL},
     { NULL, 0, 0, NULL, 0 }
   };
 
@@ -337,6 +341,14 @@
                                                      utf8(string(argstr))));
               break;
 
+            case OPT_FORMAT:
+              app.set_fmtstring(string(argstr));
+              break;
+
+            case OPT_XML:
+              app.set_xml();
+              break;
+
             case OPT_HELP:
             default:
               requested_help = true;


reply via email to

[Prev in Thread] Current Thread [Next in Thread]