Use termio.c/h as a new base for pretty printing in pacman. It collects
a lot of the low-level code that pacman uses to output tables.

Some key changes:

- string_length -> grapheme_count

  This better reflects the intent of the function: to return the number
  of grapheme's that'll be printed, its not returning the actual
  string's length.

- indentprint -> indentprint_r

  indentprint_r is a renterant version of indentprint. This allows for
  indentprint_r to be used in a loop easily to output the contents of
  a alpm_list_t without special duplicated logic. This version of
  indentprint should also be a bit cleaner to read and maintain.

- Add indent_string/indent_list/indent_list_linebreak

  These functions print either a string or a list of strings out on the
  terminal correctly with no fuss. These are intended to be used by
  pacman to aid in correctly formatting a table.

These new functions should replace string_display/list_display/etc
eventually. How pacman prints tables needs to be overhauled first.

In the meanwhile, however, they still offer some value as they improve
and unify unicode support.

Signed-off-by: Simon Gomizelj <[email protected]>
---
 src/pacman/Makefile.am |   1 +
 src/pacman/callback.c  |   1 +
 src/pacman/package.c   |  11 +--
 src/pacman/termio.c    | 204 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/pacman/termio.h    |  17 +++++
 src/pacman/util.c      | 158 +++-----------------------------------
 src/pacman/util.h      |   2 -
 7 files changed, 240 insertions(+), 154 deletions(-)
 create mode 100644 src/pacman/termio.c
 create mode 100644 src/pacman/termio.h

diff --git a/src/pacman/Makefile.am b/src/pacman/Makefile.am
index ed51573..b94e578 100644
--- a/src/pacman/Makefile.am
+++ b/src/pacman/Makefile.am
@@ -38,6 +38,7 @@ pacman_SOURCES = \
        sync.c \
        callback.h callback.c \
        upgrade.c \
+       termio.h termio.c \
        util.h util.c \
        util-common.h util-common.c
 
diff --git a/src/pacman/callback.c b/src/pacman/callback.c
index 71d9d04..e93ccd2 100644
--- a/src/pacman/callback.c
+++ b/src/pacman/callback.c
@@ -32,6 +32,7 @@
 
 /* pacman */
 #include "pacman.h"
+#include "termio.h"
 #include "callback.h"
 #include "util.h"
 #include "conf.h"
diff --git a/src/pacman/package.c b/src/pacman/package.c
index 403896a..5af7586 100644
--- a/src/pacman/package.c
+++ b/src/pacman/package.c
@@ -31,6 +31,7 @@
 
 /* pacman */
 #include "package.h"
+#include "termio.h"
 #include "util.h"
 #include "conf.h"
 
@@ -42,14 +43,14 @@ static void string_display(const char *title, const char 
*string,
        unsigned short len = 0;
 
        if(title) {
-               len = (unsigned short)string_length(title) + 1;
+               len = (unsigned short)grapheme_count(title) + 1;
                printf("%s%s%s ", config->colstr.title, title, 
config->colstr.nocolor);
        }
 
        if(string == NULL || string[0] == '\0') {
                printf(_("None"));
        } else {
-               indentprint(string, (unsigned short)len, maxcols);
+               indent_string(string, (unsigned short)len, maxcols);
        }
        printf("\n");
 }
@@ -97,7 +98,7 @@ void signature_display(const char *title, alpm_siglist_t 
*siglist,
        unsigned short len = 0;
 
        if(title) {
-               len = (unsigned short)string_length(title) + 1;
+               len = (unsigned short)grapheme_count(title) + 1;
                printf("%s%s%s ", config->colstr.title, title, 
config->colstr.nocolor);
        }
 
@@ -158,7 +159,7 @@ void signature_display(const char *title, alpm_siglist_t 
*siglist,
                        if(ret == -1) {
                                continue;
                        }
-                       indentprint(sigline, len, maxcols);
+                       indent_string(sigline, len, maxcols);
                        printf("\n");
                        free(sigline);
                }
@@ -513,7 +514,7 @@ int dump_pkg_search(alpm_db_t *db, alpm_list_t *targets, 
int show_status)
 
                        /* we need a newline and initial indent first */
                        fputs("\n    ", stdout);
-                       indentprint(alpm_pkg_get_desc(pkg), 4, cols);
+                       indent_string(alpm_pkg_get_desc(pkg), 4, cols);
                }
                fputc('\n', stdout);
        }
diff --git a/src/pacman/termio.c b/src/pacman/termio.c
new file mode 100644
index 0000000..11d564d
--- /dev/null
+++ b/src/pacman/termio.c
@@ -0,0 +1,204 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+/* pacman */
+#include "termio.h"
+
+/* gets the current screen column width */
+unsigned short getcols(int fd)
+{
+       const unsigned short default_tty = 80;
+       const unsigned short default_notty = 0;
+       unsigned short termwidth = 0;
+
+       if(!isatty(fd)) {
+               return default_notty;
+       }
+
+#if defined(TIOCGSIZE)
+       struct ttysize win;
+       if(ioctl(fd, TIOCGSIZE, &win) == 0) {
+               termwidth = win.ts_cols;
+       }
+#elif defined(TIOCGWINSZ)
+       struct winsize win;
+       if(ioctl(fd, TIOCGWINSZ, &win) == 0) {
+               termwidth = win.ws_col;
+       }
+#endif
+       return termwidth == 0 ? default_tty : termwidth;
+}
+
+static wchar_t *wide_string(const char *str, size_t *wclen, size_t *graphemes)
+{
+       wchar_t *wcstr = NULL;
+       size_t len = 0;
+
+       if(str && str[0] != '\0') {
+               len = strlen(str) + 1;
+               wcstr = calloc(len, sizeof(wchar_t));
+               len = mbstowcs(wcstr, str, len);
+       }
+
+       if(wclen) {
+               *wclen = wcstr ? len : 0;
+       }
+
+       if(graphemes) {
+               *graphemes = len ? wcswidth(wcstr, len) : 0;
+       }
+
+       return wcstr;
+}
+
+size_t grapheme_count(const char *str)
+{
+       wchar_t *wcstr;
+       size_t graphemes;
+
+       wcstr = wide_string(str, NULL, &graphemes);
+
+       free(wcstr);
+       return graphemes;
+}
+
+static wchar_t *indentword_r(wchar_t *wcstr, unsigned short indent,
+               unsigned short maxcols, unsigned short *cidx)
+{
+       size_t len;
+       wchar_t *next;
+
+       /* find the first space, set it to \0 */
+       next = wcschr(wcstr, L' ');
+       if(next != NULL) {
+               *next++ = L'\0';
+       }
+
+       /* calculate the number of columns needed to print the current word */
+       len = wcslen(wcstr);
+       len = wcswidth(wcstr, len);
+
+       /* line is going to be too long, don't even bother trying to wrap it */
+       if(len + 1 > maxcols - indent) {
+               if(*cidx > indent)
+                       printf("\n%-*s", (int)indent, "");
+
+               printf("%ls", wcstr);
+               *cidx = maxcols - 1;
+               return next;
+       }
+
+       /* if the message is long enough, wrap to a newline and re-indent */
+       if(len + 1 > maxcols - *cidx) {
+               printf("\n%-*s", (int)indent, "");
+               *cidx = indent;
+       }
+
+       /* print the word */
+       if(next) {
+               printf("%ls ", wcstr);
+               *cidx += len + 1;
+       } else {
+               printf("%ls" , wcstr);
+               *cidx += len;
+       }
+
+       return next;
+}
+
+static unsigned short indentprint_r(const char *str, unsigned short indent,
+               unsigned short maxcols, unsigned short cidx)
+{
+       wchar_t *wcstr;
+       size_t len;
+
+       if(!str) {
+               return cidx;
+       }
+
+       if(cidx < indent) {
+               cidx = indent;
+       }
+
+       /* if we're not a tty, or our tty is not wide enough that wrapping even 
makes
+        * sense, print without indenting */
+       if(maxcols == 0 || indent > maxcols) {
+               fputs(str, stdout);
+               return cidx;
+       }
+
+       /* convert to a wide string */
+       wcstr = wide_string(str, NULL, &len);
+
+       /* if it turns out the string will fit, just print it */
+       if(len < maxcols - cidx) {
+               printf("%s", str);
+               cidx += len;
+       } else {
+               wchar_t *buf = wcstr;
+               while(buf) {
+                       buf = indentword_r(buf, indent, maxcols, &cidx);
+               }
+       }
+
+       free(wcstr);
+       return cidx;
+}
+
+static unsigned short indentpad_r(int pad, unsigned short maxcols, unsigned 
short cidx)
+{
+       /* add as many spaces as we can until we hit the right edge of the
+        * screen */
+       if(maxcols) {
+               while(pad-- && cidx++ < maxcols - 1) {
+                       putchar(' ');
+               }
+       } else {
+               printf("%-*s", pad, "");
+               cidx += pad;
+       }
+
+       return cidx;
+}
+
+void indent_string(const char *str, unsigned short indent, unsigned short 
maxcols)
+{
+       indentprint_r(str, indent, maxcols, 0);
+}
+
+void indent_list(const alpm_list_t *list, unsigned short indent,
+               unsigned short maxcols)
+{
+       const alpm_list_t *i;
+       unsigned short cidx = 0;
+
+       for(i = list; i; i = alpm_list_next(i)) {
+                       const char *entry = i->data;
+                       cidx = indentprint_r(entry, indent, maxcols, cidx);
+
+                       if(i->next) {
+                               cidx = indentpad_r(2, maxcols, cidx);
+                       }
+       }
+}
+
+void indent_list_linebreak(const alpm_list_t *list, unsigned short indent,
+               unsigned short maxcols)
+{
+       const alpm_list_t *i;
+
+       for(i = list; i; i = alpm_list_next(i)) {
+                       const char *entry = i->data;
+                       indent_string(entry, indent, maxcols);
+
+                       if(i->next) {
+                               printf("\n%-*s", (int)indent, "");
+                       }
+       }
+}
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/termio.h b/src/pacman/termio.h
new file mode 100644
index 0000000..6d0490a
--- /dev/null
+++ b/src/pacman/termio.h
@@ -0,0 +1,17 @@
+#ifndef _PM_TERMIO_H
+#define _PM_TERMIO_H
+
+#include <stddef.h>
+#include <alpm_list.h>
+
+unsigned short getcols(int fd);
+
+size_t grapheme_count(const char *s);
+
+void indent_string(const char *str, unsigned short indent, unsigned short 
maxcols);
+void indent_list(const alpm_list_t *list, unsigned short indent, unsigned 
short maxcols);
+void indent_list_linebreak(const alpm_list_t *list, unsigned short indent, 
unsigned short maxcols);
+
+#endif /* _PM_TERMIO_H */
+
+/* vim: set ts=2 sw=2 noet: */
diff --git a/src/pacman/util.c b/src/pacman/util.c
index c2f0669..062c019 100644
--- a/src/pacman/util.c
+++ b/src/pacman/util.c
@@ -43,10 +43,10 @@
 
 /* pacman */
 #include "util.h"
+#include "termio.h"
 #include "conf.h"
 #include "callback.h"
 
-
 struct table_row_t {
        const char *label;
        int size;
@@ -147,31 +147,6 @@ static int flush_term_input(int fd)
        return 0;
 }
 
-/* gets the current screen column width */
-unsigned short getcols(int fd)
-{
-       const unsigned short default_tty = 80;
-       const unsigned short default_notty = 0;
-       unsigned short termwidth = 0;
-
-       if(!isatty(fd)) {
-               return default_notty;
-       }
-
-#if defined(TIOCGSIZE)
-       struct ttysize win;
-       if(ioctl(fd, TIOCGSIZE, &win) == 0) {
-               termwidth = win.ts_cols;
-       }
-#elif defined(TIOCGWINSZ)
-       struct winsize win;
-       if(ioctl(fd, TIOCGWINSZ, &win) == 0) {
-               termwidth = win.ws_col;
-       }
-#endif
-       return termwidth == 0 ? default_tty : termwidth;
-}
-
 /* does the same thing as 'rm -rf' */
 int rmrf(const char *path)
 {
@@ -216,67 +191,6 @@ int rmrf(const char *path)
        }
 }
 
-/* output a string, but wrap words properly with a specified indentation
- */
-void indentprint(const char *str, unsigned short indent, unsigned short cols)
-{
-       wchar_t *wcstr;
-       const wchar_t *p;
-       size_t len, cidx;
-
-       if(!str) {
-               return;
-       }
-
-       /* if we're not a tty, or our tty is not wide enough that wrapping even 
makes
-        * sense, print without indenting */
-       if(cols == 0 || indent > cols) {
-               fputs(str, stdout);
-               return;
-       }
-
-       len = strlen(str) + 1;
-       wcstr = calloc(len, sizeof(wchar_t));
-       len = mbstowcs(wcstr, str, len);
-       p = wcstr;
-       cidx = indent;
-
-       if(!p || !len) {
-               return;
-       }
-
-       while(*p) {
-               if(*p == L' ') {
-                       const wchar_t *q, *next;
-                       p++;
-                       if(p == NULL || *p == L' ') continue;
-                       next = wcschr(p, L' ');
-                       if(next == NULL) {
-                               next = p + wcslen(p);
-                       }
-                       /* len captures # cols */
-                       len = 0;
-                       q = p;
-                       while(q < next) {
-                               len += wcwidth(*q++);
-                       }
-                       if((len + 1) > (cols - cidx)) {
-                               /* wrap to a newline and reindent */
-                               printf("\n%-*s", (int)indent, "");
-                               cidx = indent;
-                       } else {
-                               printf(" ");
-                               cidx++;
-                       }
-                       continue;
-               }
-               printf("%lc", (wint_t)*p);
-               cidx += wcwidth(*p);
-               p++;
-       }
-       free(wcstr);
-}
-
 /* Trim whitespace and newlines from a string
  */
 size_t strtrim(char *str)
@@ -406,24 +320,6 @@ alpm_list_t *strsplit(const char *str, const char 
splitchar)
        return list;
 }
 
-size_t string_length(const char *s)
-{
-       int len;
-       wchar_t *wcstr;
-
-       if(!s || s[0] == '\0') {
-               return 0;
-       }
-       /* len goes from # bytes -> # chars -> # cols */
-       len = strlen(s) + 1;
-       wcstr = calloc(len, sizeof(wchar_t));
-       len = mbstowcs(wcstr, s, len);
-       len = wcswidth(wcstr, len);
-       free(wcstr);
-
-       return len;
-}
-
 static void table_print_line(const alpm_list_t *line, short col_padding,
                size_t colcount, size_t *widths, int *has_data)
 {
@@ -452,7 +348,7 @@ static void table_print_line(const alpm_list_t *line, short 
col_padding,
                        value = "";
                }
                /* silly printf requires padding size to be an int */
-               cell_padding = (int)widths[i] - (int)string_length(value);
+               cell_padding = (int)widths[i] - (int)grapheme_count(value);
                if(cell_padding < 0) {
                        cell_padding = 0;
                }
@@ -505,7 +401,7 @@ static size_t table_calc_widths(const alpm_list_t *header,
        }
        /* header determines column count and initial values of longest_strs */
        for(i = header, curcol = 0; i; i = alpm_list_next(i), curcol++) {
-               colwidths[curcol] = string_length(i->data);
+               colwidths[curcol] = grapheme_count(i->data);
                /* note: header does not determine whether column has data */
        }
 
@@ -515,7 +411,7 @@ static size_t table_calc_widths(const alpm_list_t *header,
                const alpm_list_t *j = i->data;
                for(curcol = 0; j; j = alpm_list_next(j), curcol++) {
                        const char *str = j->data;
-                       size_t str_len = string_length(str);
+                       size_t str_len = grapheme_count(str);
 
                        if(str_len > colwidths[curcol]) {
                                colwidths[curcol] = str_len;
@@ -601,38 +497,14 @@ void list_display(const char *title, const alpm_list_t 
*list,
        unsigned short len = 0;
 
        if(title) {
-               len = (unsigned short)string_length(title) + 1;
+               len = (unsigned short)grapheme_count(title) + 1;
                printf("%s%s%s ", config->colstr.title, title, 
config->colstr.nocolor);
        }
 
        if(!list) {
                printf("%s\n", _("None"));
        } else {
-               const alpm_list_t *i;
-               const char *str = list->data;
-               size_t cols = len;
-
-               fputs(str, stdout);
-               cols += string_length(str);
-               for(i = alpm_list_next(list); i; i = alpm_list_next(i)) {
-                       str = i->data;
-                       size_t s = string_length(str);
-                       /* wrap only if we have enough usable column space */
-                       if(maxcols > len && cols + s + 2 >= maxcols) {
-                               size_t j;
-                               cols = len;
-                               printf("\n");
-                               for(j = 1; j <= len; j++) {
-                                       printf(" ");
-                               }
-                       } else if(cols != len) {
-                               /* 2 spaces are added if this is not the first 
element on a line. */
-                               printf("  ");
-                               cols += 2;
-                       }
-                       fputs(str, stdout);
-                       cols += s;
-               }
+               indent_list(list, len, maxcols);
                putchar('\n');
        }
 }
@@ -643,25 +515,17 @@ void list_display_linebreak(const char *title, const 
alpm_list_t *list,
        unsigned short len = 0;
 
        if(title) {
-               len = (unsigned short)string_length(title) + 1;
+               len = (unsigned short)grapheme_count(title) + 1;
                printf("%s%s%s ", config->colstr.title, title, 
config->colstr.nocolor);
        }
 
        if(!list) {
                printf("%s\n", _("None"));
        } else {
-               const alpm_list_t *i;
-
-               /* Print the first element */
-               indentprint((const char *)list->data, len, maxcols);
-               printf("\n");
-               /* Print the rest */
-               for(i = alpm_list_next(list); i; i = alpm_list_next(i)) {
-                       printf("%-*s", (int)len, "");
-                       indentprint((const char *)i->data, len, maxcols);
-                       printf("\n");
-               }
+               indent_list_linebreak(list, len, maxcols);
+               putchar('\n');
        }
+
 }
 
 static alpm_list_t *create_verbose_header(void)
@@ -750,7 +614,7 @@ static void display_transaction_sizes(alpm_list_t *table)
 
        for(i = table; i; i = alpm_list_next(i)) {
                struct table_row_t *row = i->data;
-               int len = string_length(row->label);
+               int len = grapheme_count(row->label);
 
                if(len > max_len)
                        max_len = len;
diff --git a/src/pacman/util.h b/src/pacman/util.h
index 2566913..ff405c4 100644
--- a/src/pacman/util.h
+++ b/src/pacman/util.h
@@ -49,9 +49,7 @@ int trans_init(alpm_transflag_t flags, int check_valid);
 int trans_release(void);
 int needs_root(void);
 int check_syncdbs(size_t need_repos, int check_valid);
-unsigned short getcols(int fd);
 int rmrf(const char *path);
-void indentprint(const char *str, unsigned short indent, unsigned short cols);
 size_t strtrim(char *str);
 char *strreplace(const char *str, const char *needle, const char *replace);
 alpm_list_t *strsplit(const char *str, const char splitchar);
-- 
1.8.2


Reply via email to