[pacman-dev] [PATCH 4/4] add termio.c/h and replace formatting functions
Simon Gomizelj
simongmzlj at gmail.com
Tue Mar 26 13:39:32 EDT 2013
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 <simongmzlj at gmail.com>
---
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
More information about the pacman-dev
mailing list