add richtext support to zypper info (bnc #224677).
authorJosef Reidinger <jreidinger@suse.cz>
Tue, 22 Apr 2008 07:26:44 +0000 (07:26 +0000)
committerJosef Reidinger <jreidinger@suse.cz>
Tue, 22 Apr 2008 07:26:44 +0000 (07:26 +0000)
src/CMakeLists.txt
src/zypper-info.cc
src/zypper-richtext.cc [new file with mode: 0644]
src/zypper-richtext.h [new file with mode: 0644]

index e3ec2ba..f67ccd0 100644 (file)
@@ -16,6 +16,7 @@ SET (zypper_HEADERS
   zypper-search.h
   zypper-info.h
   zypper-prompt.h
+  zypper-richtext.h
   zypper-tabulator.h
   zypper-locks.h
   zypper-keyring-callbacks.h
@@ -36,6 +37,7 @@ SET( zypper_SRCS
   zypper-info.cc
   zypper-tabulator.cc
   zypper-prompt.cc
+  zypper-richtext.cc
   zypper-locks.cc
   ${zypper_HEADERS}
 )
index d3e5a39..5cd1751 100644 (file)
@@ -14,6 +14,7 @@
 #include "zypper-misc.h"
 #include "zypper-tabulator.h"
 #include "zypper-info.h"
+#include "zypper-richtext.h"
 
 using namespace std;
 using namespace zypp;
@@ -21,7 +22,6 @@ using boost::format;
 
 extern ZYpp::Ptr God;
 
-
 void printNVA(const ResObject::constPtr & res)
 {
   cout << _("Name: ") << res->name() << endl;
@@ -34,7 +34,15 @@ void printSummaryDesc(const ResObject::constPtr & res)
 {
   cout << _("Summary: ") << res->summary() << endl;
   cout << _("Description: ") << endl;
-  cout << res->description() << endl;
+  const string& s = res->description();
+  if (s.find("DT:Rich")!=s.npos){
+    string ns = processRichText(s);
+    cout << ns << endl;
+  }
+  else
+  {
+    cout << s << endl;
+  }
 }
 
 /**
diff --git a/src/zypper-richtext.cc b/src/zypper-richtext.cc
new file mode 100644 (file)
index 0000000..df060b0
--- /dev/null
@@ -0,0 +1,254 @@
+#include <sstream>
+#include <map>
+#include <vector>
+#include <string>
+
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+
+using namespace std;
+
+enum tags {
+  PARAGRAPH,
+  PRE,
+  BLOCKQUOTE,
+  BOLD,
+  UNDERLINED,
+  ANCHOR,
+  HEADER1,
+  HEADER2,
+  HEADER3,
+  BREAK_LINE,
+  EM,
+  ITALIC,
+  HR,
+  LI,
+  OL,
+  UL,
+  TT,
+  QT,
+  BIG,
+  CODE,
+  CENTER,
+  //special for unknown tags
+  UNKNOWN
+};
+
+std::map<string,tags> _RTtagmap;
+
+bool pre;
+bool ordered;
+unsigned count_list_items;
+
+void fillTagmap()
+{
+  _RTtagmap["p"] = PARAGRAPH;
+  _RTtagmap["a"] = ANCHOR;
+  _RTtagmap["b"] = BOLD;
+  _RTtagmap["u"] = UNDERLINED;
+  _RTtagmap["i"] = ITALIC;
+  _RTtagmap["br"] = BREAK_LINE;
+  _RTtagmap["em"] = EM;
+  _RTtagmap["h1"] = HEADER1;
+  _RTtagmap["h2"] = HEADER2;
+  _RTtagmap["h3"] = HEADER3;
+  _RTtagmap["hr"] = HR;
+  _RTtagmap["li"] = LI;
+  _RTtagmap["ol"] = OL;
+  _RTtagmap["ul"] = UL;
+  _RTtagmap["qt"] = QT;
+  _RTtagmap["tt"] = TT;
+  _RTtagmap["big"] = BIG;
+  _RTtagmap["pre"] = PRE;
+  _RTtagmap["bold"] = BOLD;
+  _RTtagmap["code"] = CODE;
+  _RTtagmap["font"] = UNKNOWN; //not parsed in parser
+  _RTtagmap["large"] = UNKNOWN; //same as ncurses
+  _RTtagmap["small"] = UNKNOWN; // same as necurses
+  _RTtagmap["center"] = CENTER;
+  _RTtagmap["strong"] = BOLD; // same as necurses
+  _RTtagmap["blockquote"] = BLOCKQUOTE; // same as necurses
+  
+}
+
+string closeTag(vector<tags>& tagStack)
+{
+  if(tagStack.empty())
+  {
+    WAR << "closing tag before any opening" << endl;;
+  }
+  tags t = tagStack.back();
+  tagStack.pop_back();
+  switch(t)
+  {
+    case PARAGRAPH:
+      return "\n\n";
+    case LI:
+      return "\n";
+    case PRE:
+      pre = false; //fall thrue
+    default:
+      return "";
+  }
+}
+
+string openTag(vector<tags>& tagStack, string& tag)
+{
+  tag = zypp::str::trim(tag);
+  std::map<string,tags>::const_iterator it = _RTtagmap.find(tag);
+  tags t;
+  if (it == _RTtagmap.end())
+  {
+    if (tag.size()>3 && tag[0]=='!' && tag[1]=='-' && tag[2]=='-')
+      return ""; //comment
+    WAR << "unknown rich text tag "<<tag << endl;
+    t = UNKNOWN;
+  }
+  else
+  {
+    t = it->second;
+  }
+  tagStack.push_back(t);
+  switch(t)
+  {
+    case PARAGRAPH:
+      return "";
+    case BREAK_LINE:
+      return "\n";
+    case OL:
+      ordered = true;
+      count_list_items = 0;
+      return "\n";
+    case UL:
+      ordered = false;
+      return "\n";
+    case LI:
+      if (ordered)
+      {
+        ostringstream res;
+        res << ++count_list_items << ") ";
+        res.flush();
+        return res.str();
+      }
+      else
+      {
+        return "- ";
+      }
+    case PRE:
+      pre = true; //fall thrue
+    default:
+      return "";
+  }
+}
+
+std::map<string,string> ampersmap;
+
+void fillAmpersmap()
+{
+  ampersmap["gt"]=">";
+  ampersmap["lt"]="<";
+  ampersmap["amp"]="&";
+  ampersmap["quot"]="\"";
+  ampersmap["nbsp"]=" "; //TODO REAL NBSP
+  ampersmap["product"]="product"; //TODO replace with real name
+}
+
+string getStringFromAmpr(const string& str)
+{
+  if (ampersmap.empty())
+    fillAmpersmap();
+
+  string::size_type end = str.find(';');
+  DBG << "val ampr is: " << str << endl;
+  if (str[0] == '#') //first is value
+  {
+    int res = 0;
+    istringstream sstr(str.substr(1,end));
+    sstr >> res;
+    DBG << res << endl;
+    if (res!=0)
+    {
+      return string(1,(char)res); //return char
+    }
+    else
+    {
+      WAR << "unknown number " << str << endl;
+      return "";
+    }
+  }
+  
+  DBG << end <<" "<<str.substr(0,end) << endl;
+  return ampersmap[str.substr(0,end)];
+
+}
+
+std::string processRichText(const std::string& text)
+{
+  if (_RTtagmap.empty())
+    fillTagmap();
+  //state machine vars
+  pre = false;
+
+  vector<tags> tagStack;
+
+  string res("");
+  res.reserve(text.size());
+  string::size_type pos = 0;
+  do {
+    switch( text[pos] )
+    {
+      case ' ':
+      case '\n':
+      case '\t':
+      case '\v':
+      case '\r':
+        if (pre)
+          res.push_back(text[pos]);
+        else
+        {
+          if (text[pos]==' ')
+            res.push_back(' ');
+        }
+        break;
+      case '<':
+        if (pos+1==text.npos)
+        {
+          WAR << "ended with nonclosed tag."<< endl;
+          return res; //chyba, tohle by se nemelo stavat
+        }
+        if (text[pos+1]=='/') //close tag
+        {
+          pos = text.find('>',pos);
+          res.append(closeTag(tagStack));
+        }
+        else
+        {
+          string::size_type tagEndPos = text.find('>',pos);
+          if(tagEndPos==text.npos)
+          {
+            WAR << "ended with non-closed tag " << endl;
+            return res; 
+          }
+          string tagname = text.substr(pos+1,tagEndPos-pos-1);
+          pos = tagEndPos;
+          res.append(openTag(tagStack,tagname));
+        }
+        break;
+      case '&':
+      {
+        string::size_type semipos = text.find(';',pos);
+        string tmp = getStringFromAmpr(text.substr(pos+1,pos-semipos-1));
+        DBG << "tmp is: " << tmp << endl;
+        res.append(tmp);
+        pos = semipos;
+        break;
+      }
+      default:
+        res.push_back(text[pos]);
+    }
+
+    ++pos;
+  } while (pos!=text.size());
+  return res;
+}
+
diff --git a/src/zypper-richtext.h b/src/zypper-richtext.h
new file mode 100644 (file)
index 0000000..3de33ff
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef ZYPPERRICHTEXT_H_
+#define ZYPPERRICHTEXT_H_
+
+
+#include <string>
+
+std::string processRichText(const std::string& text);
+
+#endif
+