Text processing functions added.
authorJán Kupec <jkupec@suse.cz>
Tue, 24 Mar 2009 11:07:08 +0000 (12:07 +0100)
committerJán Kupec <jkupec@suse.cz>
Tue, 24 Mar 2009 11:07:08 +0000 (12:07 +0100)
- string_to_columns() taken out of Table to text.h
- get_screen_width() taken out of Table to misc.h
- wrap_text() added to text.h

src/CMakeLists.txt
src/Table.cc
src/utils/misc.cc
src/utils/misc.h
src/utils/text.cc [new file with mode: 0644]
src/utils/text.h [new file with mode: 0644]

index 18229f8..dae4a9f 100644 (file)
@@ -68,6 +68,7 @@ SET( zypper_utils_HEADERS
   utils/pager.h
   utils/prompt.h
   utils/richtext.h
+  utils/text.h
 )
 
 SET( zypper_utils_SRCS
@@ -79,6 +80,7 @@ SET( zypper_utils_SRCS
   utils/pager.cc
   utils/prompt.cc
   utils/richtext.cc
+  utils/text.cc
   ${zypper_utils_HEADERS}
 )
 
index cdaa4be..921a0c6 100644 (file)
@@ -2,10 +2,12 @@
 #include <cstring>
 #include <cstdlib>
 #include <readline/readline.h>
-#include <wchar.h>
 
 #include "zypp/base/Logger.h"
 
+#include "utils/misc.h"
+#include "utils/text.h"
+
 #include "Table.h"
 
 // libzypp logger settings
@@ -31,50 +33,6 @@ const char * lines[][3] = {
   { "\xE2\x95\x91", "\xE2\x94\x80", "\xE2\x95\xAB"}, ///< v double, h light
 };
 
-// A non-ASCII string has 3 different lengths:
-// - bytes
-// - characters (non-ASCII ones have multiple bytes in UTF-8)
-// - columns (Chinese characters are 2 columns wide)
-// In #328918 see how confusing these leads to misalignment.
-
-// return the number of columns in str, or -1 if there's an error
-static
-int string_to_columns_e (const string& str) {
-  // from smpppd.src.rpm/format.cc, thanks arvin
-
-  const char* ptr = str.c_str ();
-  size_t s_bytes = str.length ();
-  int s_cols = 0;
-
-  mbstate_t shift_state;
-  memset (&shift_state, 0, sizeof (shift_state));
-
-  wchar_t wc;
-  size_t c_bytes;
-
-  // mbrtowc produces one wide character from a multibyte string
-  while ((c_bytes = mbrtowc (&wc, ptr, s_bytes, &shift_state)) > 0) {
-    if (c_bytes >= (size_t) -2) // incomplete (-2) or invalid (-1) sequence
-      return -1;
-
-    s_cols += wcwidth (wc);
-
-    s_bytes -= c_bytes;
-    ptr += c_bytes;
-  }
-
-  return s_cols;
-}
-
-static
-unsigned string_to_columns (const string& str) {
-  int c = string_to_columns_e (str);
-  if (c < 0)
-    return str.length();       // fallback if there was an error
-  else
-    return (unsigned) c;
-}
-
 void TableRow::add (const string& s) {
   _columns.push_back (s);
 }
@@ -136,27 +94,9 @@ Table::Table() :
   _has_header (false),
   _max_col (0),
   _width(0),
-  _style (defaultStyle)
-{
-  //! \todo move this to utils
-
-  const char *cols_env = getenv("COLUMNS");
-  if (cols_env)
-    _screen_width  = ::atoi (cols_env);
-  else
-  {
-    ::rl_initialize();
-    //::rl_reset_screen_size();
-    ::rl_get_screen_size (NULL, &_screen_width);
-    DBG << "readline says we have " << _screen_width << " char wide console screen" << endl;
-  }
-
-  // safe default
-  if (!_screen_width)
-     _screen_width = 80;
-
-  DBG << "got screen width of " << _screen_width << endl;
-}
+  _style (defaultStyle),
+  _screen_width(get_screen_width())
+{}
 
 void Table::add (const TableRow& tr) {
   _rows.push_back (tr);
index 04ab29f..83d0d04 100644 (file)
@@ -59,6 +59,29 @@ string readline_getline()
 
 // ----------------------------------------------------------------------------
 
+unsigned get_screen_width()
+{
+  int width = 80;
+
+  const char *cols_env = getenv("COLUMNS");
+  if (cols_env)
+    width  = ::atoi (cols_env);
+  else
+  {
+    ::rl_initialize();
+    //::rl_reset_screen_size();
+    ::rl_get_screen_size (NULL, &width);
+  }
+
+  // safe default
+  if (!width)
+    width = 80;
+
+  return width;
+}
+
+// ----------------------------------------------------------------------------
+
 bool is_changeable_media(const zypp::Url & url)
 {
   MIL << "Checking if this is a changeable medium" << endl;
index ffc3088..67f9b7f 100644 (file)
@@ -34,6 +34,12 @@ typedef std::set<zypp::ResKind> ResKindSet;
 
 std::string readline_getline();
 
+/**
+ * Reads COLUMNS environment variable or gets the screen width from readline,
+ * in that order. Falls back to 80 if all that fails.
+ */
+unsigned get_screen_width();
+
 bool is_changeable_media(const zypp::Url & url);
 
 std::string kind_to_string_localized(
@@ -47,29 +53,29 @@ bool equalNVRA(const zypp::Resolvable & lhs, const zypp::Resolvable & rhs);
  * Creates a Url out of \a urls_s. If the url_s looks looks_like_url()
  * Url(url_s) is returned. Otherwise if \a url_s represends a valid path to
  * a file or directory, a dir:// Url is returned. Otherwise an empty Url is
- * returned. 
+ * returned.
  */
 zypp::Url make_url (const std::string & url_s);
 
 /**
  * Returns <code>true</code> if the string \a s contains a substring starting
  * at the beginning and ending before the first colon matches any of registered
- * schemes (Url::isRegisteredScheme()). 
+ * schemes (Url::isRegisteredScheme()).
  */
 bool looks_like_url (const std::string& s);
 
 /**
  * Returns <code>true</code> if \a s ends with ".rpm" or starts with "/", "./",
- * or "../". 
+ * or "../".
  */
 bool looks_like_rpm_file(const std::string & s);
 
 /**
  * Download the RPM file specified by \a rpm_uri_str and copy it into
  * \a cache_dir.
- * 
+ *
  * \return The local Pathname of the file in the cache on success, empty
- *      Pathname if a problem occurs. 
+ *      Pathname if a problem occurs.
  */
 zypp::Pathname cache_rpm(const std::string & rpm_uri_str,
                          const std::string & cache_dir);
diff --git a/src/utils/text.cc b/src/utils/text.cc
new file mode 100644 (file)
index 0000000..e58a32e
--- /dev/null
@@ -0,0 +1,143 @@
+/*---------------------------------------------------------------------------*\
+                          ____  _ _ __ _ __  ___ _ _
+                         |_ / || | '_ \ '_ \/ -_) '_|
+                         /__|\_, | .__/ .__/\___|_|
+                             |__/|_|  |_|
+\*---------------------------------------------------------------------------*/
+
+#include <wchar.h>
+#include <cstring>
+#include <sstream>
+
+#include "utils/text.h"
+
+using namespace std;
+
+// A non-ASCII string has 3 different lengths:
+// - bytes
+// - characters (non-ASCII ones have multiple bytes in UTF-8)
+// - columns (Chinese characters are 2 columns wide)
+// In #328918 see how confusing these leads to misalignment.
+
+// return the number of columns in str, or -1 if there's an error
+static
+int string_to_columns_e (const string & str)
+{
+  // from smpppd.src.rpm/format.cc, thanks arvin
+
+  const char* ptr = str.c_str ();
+  size_t s_bytes = str.length ();
+  int s_cols = 0;
+
+  mbstate_t shift_state;
+  memset (&shift_state, 0, sizeof (shift_state));
+
+  wchar_t wc;
+  size_t c_bytes;
+
+  // mbrtowc produces one wide character from a multibyte string
+  while ((c_bytes = mbrtowc (&wc, ptr, s_bytes, &shift_state)) > 0)
+  {
+    if (c_bytes >= (size_t) -2) // incomplete (-2) or invalid (-1) sequence
+      return -1;
+
+    s_cols += wcwidth (wc);
+
+    s_bytes -= c_bytes;
+    ptr += c_bytes;
+  }
+
+  return s_cols;
+}
+
+unsigned string_to_columns (const string& str)
+{
+  int c = string_to_columns_e (str);
+  if (c < 0)
+    return str.length();        // fallback if there was an error
+  else
+    return (unsigned) c;
+}
+
+
+void wrap_text(ostream & out, const string & text,
+    unsigned indent, unsigned wrap_width, int initial)
+{
+  const char * s = text.c_str();
+  size_t s_bytes = text.length ();
+  const char * prevwp = s;
+  const char * linep = s;
+  wchar_t wc;
+  size_t bytes_read;
+  bool in_word = false;
+
+  mbstate_t shift_state;
+  memset (&shift_state, 0, sizeof (shift_state));
+
+  unsigned col = 0;
+  unsigned toindent = initial < 0 ? indent : initial;
+  //wchar_t ws[2] = L" ";
+  do
+  {
+    // indentation
+    if (!col)
+    {
+      out << string(toindent, ' ');
+      col = toindent;
+    }
+
+    bytes_read = mbrtowc (&wc, s, s_bytes, &shift_state);
+    if (bytes_read > 0)
+    {
+      col += ::wcwidth(wc);
+
+      if (::iswspace(wc))
+        in_word = false;
+      //else if (::wcscmp(wc, L"\n"))
+      //{
+      //  if (!in_word)
+      //    prevwp = s;
+      //  in_word = true;
+      //}
+      else
+      {
+        if (!in_word)
+          prevwp = s;
+        in_word = true;
+      }
+
+      // current wc exceeded the wrap width
+      if (col > wrap_width)
+      {
+        // update the size of the string to read
+        s_bytes -= (prevwp - linep);
+        // print the line, leave linep point to the start of the last word.
+        for (; linep < prevwp; ++linep)
+          out << *linep;
+        out << endl;
+        // reset column counter
+        col = 0;
+        toindent = indent;
+        // reset original text pointer (points to current wc, not the start of the word)
+        s = linep;
+        // reset shift state
+        ::memset (&shift_state, 0, sizeof (shift_state));
+      }
+      else
+        s += bytes_read;
+    }
+    // we're at the end of the string
+    else if (bytes_read == 0)
+    {
+      // print the rest of the text
+      for(; *linep; ++linep)
+        out << *linep;
+    }
+    else
+    {
+      out << endl << "WCHAR ERROR" << endl;
+      return;
+    }
+  }
+  while(bytes_read > 0);
+}
diff --git a/src/utils/text.h b/src/utils/text.h
new file mode 100644 (file)
index 0000000..010a15d
--- /dev/null
@@ -0,0 +1,25 @@
+/*---------------------------------------------------------------------------*\
+                          ____  _ _ __ _ __  ___ _ _
+                         |_ / || | '_ \ '_ \/ -_) '_|
+                         /__|\_, | .__/ .__/\___|_|
+                             |__/|_|  |_|
+\*---------------------------------------------------------------------------*/
+
+#ifndef ZYPPER_UTILS_TEXT_H_
+#define ZYPPER_UTILS_TEXT_H_
+
+#include <string>
+#include <iosfwd>
+
+/** Returns the length of the string in columns */
+/*
+ * TODO
+ * - delete whitespace at the end of lines
+ * - keep one-letter words with the next
+ */
+unsigned string_to_columns (const std::string & str);
+
+void wrap_text(std::ostream & out, const std::string & text,
+    unsigned indent, unsigned wrap_width, int initial = -1);
+
+#endif /* ZYPPER_UTILS_TEXT_H_ */