[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