From 56aef1cb2d0f52e10b3612667987c2bbad024e4a Mon Sep 17 00:00:00 2001
From: Raphael Hertzog <hertzog@debian.org>
Date: Fri, 27 Jun 2008 22:18:58 +0200
Subject: [PATCH] Add wide-character support to dselect

Thanks to Changwoo Ryu <cwryu@debian.org> and Eugeniy Meshcheryakov
<eugen@univ.kiev.ua>.
---
 configure.ac          |    1 +
 debian/control        |    2 +-
 dselect/Makefile.am   |    2 +
 dselect/baselist.cc   |   91 ++++++++++++++++++-------------------
 dselect/dselect.h     |   11 +++++
 dselect/main.cc       |   21 ++++++---
 dselect/mbhelpers.cc  |  118 +++++++++++++++++++++++++++++++++++++++++++++++
 dselect/methlist.cc   |   12 +++--
 dselect/pkgdisplay.cc |   41 +++++++++--------
 dselect/pkgtop.cc     |  121 +++++++++++++++++++++++++-----------------------
 m4/libs.m4            |    9 ++++
 11 files changed, 291 insertions(+), 138 deletions(-)
 create mode 100644 dselect/mbhelpers.cc

diff --git a/configure.ac b/configure.ac
index 0d79b6d..8744525 100644
--- a/configure.ac
+++ b/configure.ac
@@ -66,6 +66,7 @@ DPKG_LIB_BZ2
 DPKG_LIB_SELINUX
 if test "x$build_dselect" = "xyes"; then
    DPKG_LIB_CURSES
+   DPKG_LIB_TEXTWRAP
 fi
 if test "x$build_start_stop_daemon" = "xyes"; then
    DPKG_LIB_SSD
diff --git a/debian/control b/debian/control
index 38aac8d..0c332f0 100644
--- a/debian/control
+++ b/debian/control
@@ -11,7 +11,7 @@ Vcs-Browser: http://git.debian.org/?p=dpkg/dpkg.git
 Vcs-Git: git://git.debian.org/git/dpkg/dpkg.git
 Standards-Version: 3.7.3
 Build-Depends: debhelper (>= 4.1.81), pkg-config, po4a (>= 0.33.1),
- libncursesw5-dev, zlib1g-dev (>= 1:1.1.3-19.1), libbz2-dev, flex,
+ libncursesw5-dev, libtextwrap-dev, zlib1g-dev (>= 1:1.1.3-19.1), libbz2-dev, flex,
  libselinux1-dev (>= 1.28-4) [!hurd-i386 !kfreebsd-i386 !kfreebsd-amd64],
  libtimedate-perl, libio-string-perl
 
diff --git a/dselect/Makefile.am b/dselect/Makefile.am
index c0bb6bc..7e519db 100644
--- a/dselect/Makefile.am
+++ b/dselect/Makefile.am
@@ -21,6 +21,7 @@ dselect_SOURCES = \
 	curkeys.cc \
 	helpmsgs.cc helpmsgs.h \
 	main.cc \
+	mbhelpers.cc \
 	methkeys.cc \
 	methlist.cc \
 	method.cc method.h \
@@ -36,6 +37,7 @@ dselect_SOURCES = \
 
 dselect_LDADD = \
 	$(CURSES_LIBS) \
+	$(TEXTWRAP_LIBS) \
 	../lib/libdpkg.a \
 	../libcompat/libcompat.a \
 	$(LIBINTL)
diff --git a/dselect/baselist.cc b/dselect/baselist.cc
index 8ba7ff0..cb2911d 100644
--- a/dselect/baselist.cc
+++ b/dselect/baselist.cc
@@ -30,6 +30,7 @@ extern "C" {
 #include <unistd.h>
 #include <sys/ioctl.h>
 #include <sys/termios.h>
+#include <textwrap.h>
 
 extern "C" {
 #include <dpkg.h>
@@ -319,59 +320,55 @@ void baselist::refreshinfo() {
   }
 }
 
+static char *reformat(int offset, const char *s) {
+  int pos = 0;
+  /* if offset == 0:
+   *   all '^.\n -> '\n\n'
+   *   all '^\n' -> '\n\n'
+   *   so we may need 2 times more space
+   * if offset > 1
+   *   we need le amount of space
+   */
+  char *news = (char *)malloc(2*strlen(s)+1);
+  const char *p = s;
+  for (;;) {
+    int offleft = offset;
+    if (*p == ' ' && offleft > 0) {p++; offleft--;};
+    const char *e = strchr(p, '\n');
+    int l = e ? e-p : strlen(p);
+    if ((l == 1 && *p == '.') || l==0 ) { news[pos++] = '\n'; news[pos++] = '\n'; }
+    else { 
+      if (p > s && *p == ' ')
+        news[pos-1] = '\n';
+      memcpy(news+pos, p, l); pos += l; news[pos++]=' ';
+    }
+    if (!e) break;
+    p = ++e;
+  }
+  news[pos] = 0;
+  return news;
+}
+
 void baselist::wordwrapinfo(int offset, const char *m) {
   int usemax= xmax-5;
   if (debug) fprintf(debug,"baselist[%p]::wordwrapinfo(%d, `%s')\n",this,offset,m);
-  int wrapping=0;
-  for (;;) {
-    int offleft=offset; while (*m == ' ' && offleft>0) { m++; offleft--; }
-    const char *p= strchr(m,'\n');
-    int l= p ? (int)(p-m) : strlen(m);
-    while (l && isspace(m[l-1])) l--;
-    if (!l || *m == '.' && l == 1) {
-      if (wrapping) waddch(infopad,'\n');
-      waddch(infopad,'\n');
-      wrapping= 0;
-    } else if (*m == ' ' || usemax < 10) {
-      if (wrapping) waddch(infopad,'\n');
-      waddnstr(infopad, m, l);
-      waddch(infopad,'\n'); wrapping= 0;
-    } else {
-      int x,y;
-      if (wrapping) {
-        getyx(infopad, y,x);
-        if (x+1 >= usemax) {
-          waddch(infopad,'\n');
-        } else {
-          waddch(infopad,' ');
-        }
-      }
-      for (;;) {
-        getyx(infopad, y,x);
-        int dosend= usemax-x;
-        if (l <= dosend) {
-          dosend=l;
-        } else {
-          int i=dosend;
-          while (i > 0 && m[i] != ' ') i--;
-          if (i > 0 || x > 0) dosend=i;
-        }
-        if (dosend) waddnstr(infopad, m, dosend);
-        while (dosend < l && m[dosend] == ' ') dosend++;
-        l-= dosend; m+= dosend;
-        if (l <= 0) break;
-        waddch(infopad,'\n');
-      }
-      wrapping= 1;
-    }
-    if (!p) break;
-    if (getcury(infopad) == (MAX_DISPLAY_INFO - 1)) {
-      waddstr(infopad,
-              "[The package description is too long and has been truncated...]");
+  textwrap_t prop;
+  textwrap_init(&prop);
+  textwrap_columns(&prop, usemax);
+  char *rs = reformat(offset, m);
+  char *os = textwrap(&prop, rs);
+  int nblines = 0;
+  char *eol = os;
+  while(eol = strchr(eol, '\n')) {
+    eol++;
+    if (++nblines == (MAX_DISPLAY_INFO - 1)) {
+      *eol = '\0'; /* Truncate description */
       break;
     }
-    m= ++p;
   }
+  waddstr(infopad, os);
+  free(os);
+  free(rs);
   if (debug) fprintf(debug,"baselist[%p]::wordwrapinfo() done\n",this);
 }
 
diff --git a/dselect/dselect.h b/dselect/dselect.h
index cf5f553..fbf50f5 100644
--- a/dselect/dselect.h
+++ b/dselect/dselect.h
@@ -183,5 +183,16 @@ urqfunction urq_setup, urq_update, urq_install, urq_config, urq_remove;
 
 urqresult falliblesubprocess(const char *exepath, const char *name,
                              const char *const *args);
+extern int addstrwidth(WINDOW *, const char *, int);
+extern void helper_printleft(WINDOW*, const char *, int);
+
+/* multibyte helpers */
+extern int mbsnwidth(const char *str, int len);
+extern int mbswidth(const char *str);
+extern int mb_mvwaddnstr(WINDOW *win, int y, int x, const char *str, int n);
+extern int mb_waddnstr(WINDOW *win, const char *str, int n);
+extern int mb_mvaddnstr(int y, int x, const char *str, int n);
+extern int mb_printleft(const char *str, int w);
+extern int mb_wprintleft(WINDOW *win, const char *str, int w);
 
 #endif /* DSELECT_H */
diff --git a/dselect/main.cc b/dselect/main.cc
index ac277e9..0ba415a 100644
--- a/dselect/main.cc
+++ b/dselect/main.cc
@@ -37,6 +37,7 @@ extern "C" {
 #include <ctype.h>
 #include <assert.h>
 
+#include <wchar.h>
 #include <ncursesw/term.h>
 
 extern "C" {
@@ -357,16 +358,22 @@ urqresult urq_list(void) {
 void dme(int i, int so) {
   char buf[120];
   const menuentry *me= &menuentries[i];
-  sprintf(buf," %c %d. %-11.11s %-80.80s ",
-          so ? '*' : ' ', i,
-          gettext(me->option),
-          gettext(me->menuent));
-  
+  snprintf(buf, sizeof(buf)/sizeof(*buf), " %c %d. ",
+          so ? '*' : ' ', i);
   int y,x;
   getmaxyx(stdscr,y,x);
 
+  int rwidth = x-1; 
+
   attrset(so ? A_REVERSE : A_NORMAL);
-  mvaddnstr(i+2,0, buf,x-1);
+  mb_mvaddnstr(i+2,0, buf, rwidth);
+  rwidth -= strlen(buf);
+  if (rwidth > 0) {
+    mb_printleft(gettext(me->option), rwidth < 11? rwidth : 11);
+    if (rwidth > 11) addch(' ');
+    rwidth -= 12;
+    if (rwidth > 0) mb_printleft(gettext(me->menuent), rwidth);
+  }
   attrset(A_NORMAL);
 }
 
@@ -383,7 +390,7 @@ int refreshmenu(void) {
   clear();
   attrset(A_BOLD);
   sprintf(buf, gettext(programdesc), DSELECT, DPKG_VERSION_ARCH);
-  mvaddnstr(0,0,buf,x-1);
+  mb_mvaddnstr(0,0,buf,x-1);
 
   attrset(A_NORMAL);
   const struct menuentry *mep; int i;
diff --git a/dselect/mbhelpers.cc b/dselect/mbhelpers.cc
new file mode 100644
index 0000000..dead6e7
--- /dev/null
+++ b/dselect/mbhelpers.cc
@@ -0,0 +1,118 @@
+#include <wchar.h>
+#include <stdlib.h>
+#include <string.h>
+#include <curses.h>
+
+/* returns width of first LEN bytes of STR */
+int mbsnwidth(const char *str, int len) {
+  mbstate_t state;
+  int n, w = 0;
+  wchar_t wc;
+  int ret;
+  
+  memset(&state, 0, sizeof(state));
+  while (len>0) {
+    ret = mbrtowc(&wc, str, len, &state);
+    if (ret <= 0)
+      break;
+    len -= ret;
+    str += ret;
+    n = wcwidth(wc);
+    if (n == -1) break;
+    w += n;
+  }
+  return w;
+}
+
+int mbswidth(const char *str) {
+  size_t len;
+  
+  len = mbstowcs(0, str, 0);
+
+  wchar_t *wcs = new wchar_t[len];
+
+  len = mbstowcs(wcs, str, len);
+  int width = wcswidth(wcs, len);
+  
+  delete [] wcs;
+  return width;
+}
+
+/* returns number of bytes of the STR prefix, whose width is at most WIDTH */
+static int mbscrop(const char *str, int width) {
+  mbstate_t state;
+  int totallength = 0;
+  int totalwidth = 0;
+
+  memset(&state, 0, sizeof(state));
+  while(1) {
+    wchar_t wc;
+    int w;
+    int length = mbrtowc(&wc, str, strlen(str), &state);
+    if (length <= 0) break;
+
+    w = wcwidth(wc);
+
+    if ((totalwidth + w) > width)
+      break;
+    totalwidth += w;
+    totallength += length;
+    str += length;
+  }
+  return totallength;
+}
+
+int mb_mvwaddnstr(WINDOW *win, int y, int x, const char *str, int n) {
+  mbstate_t state;
+  int totallength = 0;
+  const char *p = str;
+  memset(&state, 0, sizeof(state));
+
+  while (n > 0) {		// for each characters, in the given width (n)
+    wchar_t wc;
+    int length;
+    int width;
+
+    length = mbrtowc(&wc, p, strlen(p), &state);
+    if (length <= 0) break;
+
+    width = wcwidth(wc);
+    n -= width;
+
+    if (n < 0) break;
+
+    totallength += length;
+    p += length;
+  }
+
+  if (win)
+    return mvwaddnstr(win, y, x, str, totallength);
+  else
+    return mvaddnstr(y, x, str, totallength);
+}
+
+int mb_waddnstr(WINDOW *win, const char *str, int n) {
+  int x, y;
+  getyx(win, y, x);
+  return mb_mvwaddnstr(win, y, x, str, n);
+}
+
+int mb_mvaddnstr(int y, int x, const char *str, int n) {
+  return mb_mvwaddnstr(stdscr, y, x, str, n);
+}
+
+int mb_wprintleft(WINDOW *win, const char *s, int w) {
+  int len = mbscrop(s, w);
+  int width = mbsnwidth(s, len);
+  mb_waddnstr(win, s, width);
+
+  int spcs = w - width;
+  for (int i = 0; i < spcs; i++)
+    waddch(win, ' ');
+  return OK;
+};
+
+int mb_printleft(const char *s, int w) {
+  return mb_wprintleft(stdscr, s, w);
+};
+
diff --git a/dselect/methlist.cc b/dselect/methlist.cc
index 9e3b7d7..35ae647 100644
--- a/dselect/methlist.cc
+++ b/dselect/methlist.cc
@@ -75,7 +75,7 @@ void methodlist::setwidths() {
 void methodlist::redrawtitle() {
   if (title_height) {
     mywerase(titlewin);
-    mvwaddnstr(titlewin,0,0,_("dselect - list of access methods"),xmax);
+    mb_mvwaddnstr(titlewin,0,0,_("dselect - list of access methods"),xmax);
     wnoutrefresh(titlewin);
   }
 }
@@ -98,8 +98,10 @@ void methodlist::redraw1itemsel(int index, int selected) {
   mvwaddch(listpad,index,0,
            table[index] == coption ? '*' : ' ');
   wattrset(listpad, selected ? listsel_attr : list_attr);
-  mvwprintw(listpad,index,name_column-1, " %-*.*s ",
-            name_width, name_width, table[index]->name);
+  wmove(listpad,index,name_column-1);
+  waddch(listpad, ' ');
+  mb_wprintleft(listpad,table[index]->name, name_width);
+  waddch(listpad, ' ');
   
   i= description_width;
   p= table[index]->summary ? table[index]->summary : "";
@@ -118,8 +120,8 @@ void methodlist::redrawcolheads() {
     wattrset(colheadspad,colheads_attr);
     mywerase(colheadspad);
     mvwaddstr(colheadspad,0,0, "  ");
-    mvwaddnstr(colheadspad,0,name_column, _("Abbrev."), name_width);
-    mvwaddnstr(colheadspad,0,description_column, _("Description"), description_width);
+    mb_mvwaddnstr(colheadspad,0,name_column, _("Abbrev."), name_width);
+    mb_mvwaddnstr(colheadspad,0,description_column, _("Description"), description_width);
   }
   refreshcolheads();
 }
diff --git a/dselect/pkgdisplay.cc b/dselect/pkgdisplay.cc
index 6e9e5fc..e82242d 100644
--- a/dselect/pkgdisplay.cc
+++ b/dselect/pkgdisplay.cc
@@ -122,12 +122,13 @@ const char
                          N_("Removed"),
                          N_("Purged") };
 
-static int maximumstring(const char *const *array) {
+static int maximumstringwidth(const char *const *array) {
   int maxlen= 0;
   while (*array) {
-    int l= strlen(gettext(*array));
+    int l= mbswidth(gettext(*array));
     const char *p= strchr(*array, '(');
     if (p && p > *array && *--p == ' ') l= p - *array;
+    l = mbsnwidth(*array, l);
     if (l > maxlen) maxlen= l;
     array++;
   }
@@ -139,8 +140,8 @@ void packagelist::setwidths() {
 
   if (verbose) {
     status_hold_width= 9;
-    status_status_width= maximumstring(statusstrings);
-    status_want_width= maximumstring(wantstrings);
+    status_status_width= maximumstringwidth(statusstrings);
+    status_want_width= maximumstringwidth(wantstrings);
     status_width= status_hold_width+status_status_width+status_want_width*2+3;
     priority_width= 8;
     package_width= 16;
@@ -199,24 +200,24 @@ void packagelist::redrawtitle() {
   
   if (title_height) {
     mywerase(titlewin);
-    mvwaddnstr(titlewin,0,0,
-               recursive ?  _("dselect - recursive package listing") :
-               !readwrite ? _("dselect - inspection of package states") :
-                            _("dselect - main package listing"),
-               xmax);
+    mb_mvwaddnstr(titlewin,0,0,
+                  recursive ?  _("dselect - recursive package listing") :
+                  !readwrite ? _("dselect - inspection of package states") :
+                  _("dselect - main package listing"),
+                  xmax);
     getyx(titlewin,y,x);
     if (x < xmax) {
       switch (sortorder) {
       case so_section:
         switch (statsortorder) {
         case sso_unsorted:
-          waddnstr(titlewin, _(" (by section)"), xmax-x);
+          mb_waddnstr(titlewin, _(" (by section)"), xmax-x);
           break;
         case sso_avail:
-          waddnstr(titlewin, _(" (avail., section)"), xmax-x);
+          mb_waddnstr(titlewin, _(" (avail., section)"), xmax-x);
           break;
         case sso_state:
-          waddnstr(titlewin, _(" (status, section)"), xmax-x);
+          mb_waddnstr(titlewin, _(" (status, section)"), xmax-x);
           break;
         default:
           internerr("bad statsort in redrawtitle/so_section");
@@ -225,13 +226,13 @@ void packagelist::redrawtitle() {
       case so_priority:
         switch (statsortorder) {
         case sso_unsorted:
-          waddnstr(titlewin, _(" (by priority)"), xmax-x);
+          mb_waddnstr(titlewin, _(" (by priority)"), xmax-x);
           break;
         case sso_avail:
-          waddnstr(titlewin, _(" (avail., priority)"), xmax-x);
+          mb_waddnstr(titlewin, _(" (avail., priority)"), xmax-x);
           break;
         case sso_state:
-          waddnstr(titlewin, _(" (status, priority)"), xmax-x);
+          mb_waddnstr(titlewin, _(" (status, priority)"), xmax-x);
           break;
         default:
           internerr("bad statsort in redrawtitle/so_priority");
@@ -240,19 +241,19 @@ void packagelist::redrawtitle() {
       case so_alpha:
         switch (statsortorder) {
         case sso_unsorted:
-          waddnstr(titlewin, _(" (alphabetically)"), xmax-x);
+          mb_waddnstr(titlewin, _(" (alphabetically)"), xmax-x);
           break;
         case sso_avail:
-          waddnstr(titlewin, _(" (by availability)"), xmax-x);
+          mb_waddnstr(titlewin, _(" (by availability)"), xmax-x);
           break;
         case sso_state:
-          waddnstr(titlewin, _(" (by status)"), xmax-x);
+          mb_waddnstr(titlewin, _(" (by status)"), xmax-x);
           break;
         default:
           internerr("bad statsort in redrawtitle/so_priority");
         }
         break;
-        waddnstr(titlewin, _(" (alphabetically)"), xmax-x);
+        mb_waddnstr(titlewin, _(" (alphabetically)"), xmax-x);
         break;
       case so_unsorted:
         break;
@@ -264,7 +265,7 @@ void packagelist::redrawtitle() {
                                                  : _(" mark:+/=/- verbose:v help:?"))
                                       : (verbose ? _(" terse:v help:?")
                                                  : _(" verbose:v help:?"));
-    int l= strlen(helpstring);
+    int l= mbswidth(helpstring);
     getyx(titlewin,y,x);
     if (xmax-l > 0) {
       mvwaddstr(titlewin,0,xmax-l, helpstring);
diff --git a/dselect/pkgtop.cc b/dselect/pkgtop.cc
index 8edde41..1136958 100644
--- a/dselect/pkgtop.cc
+++ b/dselect/pkgtop.cc
@@ -127,7 +127,7 @@ void packagelist::redrawthisstate() {
   } else {
     describemany(buf,priority,section,table[cursorline]->pkg->clientdata);
   }
-  mvwaddnstr(thisstatepad,0,0, buf, total_width);
+  mb_mvwaddnstr(thisstatepad,0,0, buf, total_width);
   pnoutrefresh(thisstatepad, 0,leftofscreen, thisstate_row,0,
                thisstate_row, lesserint(total_width - 1, xmax - 1));
 
@@ -145,29 +145,27 @@ void packagelist::redraw1itemsel(int index, int selected) {
   if (pkg->name) {
 
     if (verbose) {
-
-      mvwprintw(listpad,index,0, "%-*.*s ",
-                status_hold_width, status_hold_width,
-                gettext(eflagstrings[pkg->eflag]));
-      wprintw(listpad, "%-*.*s ",
-              status_status_width, status_status_width,
-              gettext(statusstrings[pkg->status]));
-      wprintw(listpad, "%-*.*s ",
-              status_want_width, status_want_width,
+      wmove(listpad,index,0);
+      mb_wprintleft(listpad,gettext(eflagstrings[pkg->eflag]),
+		    status_hold_width);
+      waddch(listpad, ' ');
+      mb_wprintleft(listpad,gettext(statusstrings[pkg->status]),
+		    status_status_width);
+      waddch(listpad, ' ');
+      mb_wprintleft(listpad,
               /* fixme: keep this ? */
               /*table[index]->original == table[index]->selected ? "(same)"
-              : */gettext(wantstrings[table[index]->original]));
+              : */gettext(wantstrings[table[index]->original]),status_want_width);
+      waddch(listpad, ' ');
       wattrset(listpad, selected ? selstatesel_attr : selstate_attr);
-      wprintw(listpad, "%-*.*s",
-              status_want_width, status_want_width,
-              gettext(wantstrings[table[index]->selected]));
+      mb_wprintleft(listpad,gettext(wantstrings[table[index]->selected]),
+		    status_want_width);
       wattrset(listpad, selected ? listsel_attr : list_attr);
       waddch(listpad, ' ');
-  
-      mvwprintw(listpad,index,priority_column-1, " %-*.*s",
-                priority_width, priority_width,
-                pkg->priority == pkginfo::pri_other ? pkg->otherpriority :
-                gettext(prioritystrings[pkg->priority]));
+      wmove(listpad,index,priority_column-1); 
+      waddch(listpad, ' ');
+      mb_wprintleft(listpad,pkg->priority == pkginfo::pri_other ? pkg->otherpriority :
+                gettext(prioritystrings[pkg->priority]),priority_width);
 
     } else {
 
@@ -189,33 +187,39 @@ void packagelist::redraw1itemsel(int index, int selected) {
         for (i=priority_width, p=pkg->otherpriority;
              i > 0 && *p;
              i--, p++)
+	  /* FIXME: priority is ASCII string */
           waddch(listpad, tolower(*p));
         while (i-- > 0) waddch(listpad,' ');
       } else {
-        wprintw(listpad, "%-*.*s", priority_width, priority_width,
-                gettext(priorityabbrevs[pkg->priority]));
+	mb_wprintleft(listpad,gettext(priorityabbrevs[pkg->priority]),
+                      priority_width);
       }
 
     }
 
-    mvwprintw(listpad,index,section_column-1, " %-*.*s",
-              section_width, section_width,
-              pkg->section ? pkg->section : "?");
-  
-    mvwprintw(listpad,index,package_column-1, " %-*.*s ",
-              package_width, package_width, pkg->name);
+    wmove(listpad, index, section_column-1);
+    waddch(listpad, ' ');
+    mb_wprintleft(listpad, pkg->section ? pkg->section : "?", section_width);
+    wmove(listpad, index, package_column-1);
+    waddch(listpad, ' ');
+    mb_wprintleft(listpad, pkg->name, package_width);
+    waddch(listpad, ' ');
 
-    if (versioninstalled_width)
-      mvwprintw(listpad,index,versioninstalled_column, "%-*.*s ",
-                versioninstalled_width, versioninstalled_width,
-                versiondescribe(&pkg->installed.version, vdew_nonambig));
+    if (versioninstalled_width) {
+      wmove(listpad, index, versioninstalled_column);
+      mb_wprintleft(listpad,
+                    versiondescribe(&pkg->installed.version, vdew_nonambig),
+                    versioninstalled_width);
+      waddch(listpad, ' ');
+    }
     if (versionavailable_width) {
       if (informativeversion(&pkg->available.version) &&
           versioncompare(&pkg->available.version,&pkg->installed.version) > 0)
         wattrset(listpad, selected ? selstatesel_attr : selstate_attr);
-      mvwprintw(listpad,index,versionavailable_column, "%-*.*s",
-                versionavailable_width, versionavailable_width,
-                versiondescribe(&pkg->available.version, vdew_nonambig));
+      wmove(listpad, index, versionavailable_column);
+      mb_wprintleft(listpad,
+                    versiondescribe(&pkg->available.version, vdew_nonambig),
+                    versionavailable_width);
       wattrset(listpad, selected ? listsel_attr : list_attr);
       waddch(listpad,' ');
     }
@@ -255,6 +259,7 @@ void packagelist::redraw1itemsel(int index, int selected) {
   }
 
   while (i>0) { waddch(listpad,' '); i--; }
+  
 }
 
 void packagelist::redrawcolheads() {
@@ -264,37 +269,37 @@ void packagelist::redrawcolheads() {
     if (verbose) {
       wmove(colheadspad,0,0);
       for (int i=0; i<status_width-status_want_width; i++) waddch(colheadspad,'.');
-      mvwaddnstr(colheadspad,0,
-                 0,
-                 _("Error"),
-                 status_hold_width);
-      mvwaddnstr(colheadspad,0,
-                 status_hold_width+1,
-                 _("Installed?"),
-                 status_status_width);
-      mvwaddnstr(colheadspad,0,
-                 status_hold_width+status_status_width+2,
-                 _("Old mark"),
-                 status_want_width);
-      mvwaddnstr(colheadspad,0,
-                 status_hold_width+status_status_width+status_want_width+3,
-                 _("Marked for"),
-                 status_want_width);
+      mb_mvwaddnstr(colheadspad,0,
+		    0,
+		    _("Error"),
+		    status_hold_width);
+      mb_mvwaddnstr(colheadspad,0,
+		    status_hold_width+1,
+		    _("Installed?"),
+		    status_status_width);
+      mb_mvwaddnstr(colheadspad,0,
+		    status_hold_width+status_status_width+2,
+		    _("Old mark"),
+		    status_want_width);
+      mb_mvwaddnstr(colheadspad,0,
+		    status_hold_width+status_status_width+status_want_width+3,
+		    _("Marked for"),
+		    status_want_width);
     } else {
       mvwaddstr(colheadspad,0,0, _("EIOM"));
     }
-    mvwaddnstr(colheadspad,0,section_column, _("Section"), section_width);
-    mvwaddnstr(colheadspad,0,priority_column, _("Priority"), priority_width);
-    mvwaddnstr(colheadspad,0,package_column, _("Package"), package_width);
+    mb_mvwaddnstr(colheadspad,0,section_column, _("Section"), section_width);
+    mb_mvwaddnstr(colheadspad,0,priority_column, _("Priority"), priority_width);
+    mb_mvwaddnstr(colheadspad,0,package_column, _("Package"), package_width);
 
     if (versioninstalled_width)
-      mvwaddnstr(colheadspad,0,versioninstalled_column,
-                 _("Inst.ver"),versioninstalled_width);
+      mb_mvwaddnstr(colheadspad,0,versioninstalled_column,
+		    _("Inst.ver"),versioninstalled_width);
     if (versionavailable_width)
-      mvwaddnstr(colheadspad,0,versionavailable_column,
-                 _("Avail.ver"),versionavailable_width);
+      mb_mvwaddnstr(colheadspad,0,versionavailable_column,
+		    _("Avail.ver"),versionavailable_width);
 
-    mvwaddnstr(colheadspad,0,description_column, _("Description"), description_width);
+    mb_mvwaddnstr(colheadspad,0,description_column, _("Description"), description_width);
   }
   refreshcolheads();
 }
diff --git a/m4/libs.m4 b/m4/libs.m4
index 1b3cb24..51b3316 100644
--- a/m4/libs.m4
+++ b/m4/libs.m4
@@ -99,6 +99,15 @@ AC_CHECK_LIB([ncursesw], [initscr], [CURSES_LIBS="${CURSES_LIBS:+$CURSES_LIBS }-
 			[AC_MSG_WARN([no curses library found])])])])
 ])# DPKG_LIB_CURSES
 
+# DPKG_LIB_TEXTWRAP
+# -----------------
+# Check for textwrap library.
+AC_DEFUN([DPKG_LIB_TEXTWRAP],
+[AC_ARG_VAR([TEXTWRAP_LIBS], [linker flags for textwrap library])dnl
+AC_CHECK_LIB([textwrap], [textwrap], [TEXTWRAP_LIBS="${TEXTWRAP_LIBS:+$TEXTWRAP_LIBS}-ltextwrap"],
+	[AC_MSG_WARN([no curses library found])])
+])# DPKG_LIB_TEXTWRAP
+
 # DPKG_LIB_SSD
 # ------------
 # Check for start-stop-daemon libraries.
-- 
1.5.6

