[Toybox] new toys: expand & unexpand

Ilya Kuzmich ilya.kuzmich at gmail.com
Sun Apr 1 01:39:56 PDT 2012


Fully implements standard, plus -f (expand) and -i (unexpand)
extensions.
Also , neither "at:+af~a" nor "f~aat:+a" worked for me.
I'm doing it wrong?
-------------- next part --------------
# HG changeset patch
# User Ilya Kuzmich <ilya.kuzmich at gmail.com>
# Date 1333267623 -14400
# Node ID a9ac4067c442441ef6a4c81db3d13c3d6c583148
# Parent  feb909b2e6aa11662a3ed860027022ee35d15666
Implement expand & unexpand.

diff -r feb909b2e6aa -r a9ac4067c442 lib/expand.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/expand.c	Sun Apr 01 12:07:03 2012 +0400
@@ -0,0 +1,98 @@
+/* vi: set sw=4 ts=4 :*/
+/* expand.c - functions shared by expand and unexpand.
+ *
+ * Copyright 2012 Ilya Kuzmich <ilya.kuzmich at gmail.com>
+ */
+
+#include "toys.h"
+#include "lib/expand.h"
+
+#define TABLIST_GROWSIZ 8
+
+static void validate_tablist(struct tablist *t) {
+	int prev_tabstop, i;
+
+	if (!t->len)
+		perror_exit("empty tablist");
+
+	for (prev_tabstop = 0, i = 0; i < t->len; prev_tabstop = t->tabstops[i++]) {
+		if (!t->tabstops[i])
+			perror_exit("tabstop should be greater than zero");
+		if (t->tabstops[i] <= prev_tabstop)
+			perror_exit("tabstops must be ascending");
+	}
+}
+
+struct tablist *parse_tablist(char *tablist_str)
+{
+	struct tablist *t = xmalloc(sizeof(struct tablist));
+
+	if (!tablist_str) {
+		t->len = 1;
+		t->tabstops = xmalloc(sizeof(int));
+		t->tabstops[0] = 8;
+	} else {
+		char *endptr;
+		int tabstop;
+		size_t allocated_size = TABLIST_GROWSIZ;
+
+		t->tabstops = xmalloc(sizeof(int) * TABLIST_GROWSIZ);
+		t->len = 0;
+
+		while (*tablist_str) {
+			tabstop = strtol(tablist_str, &endptr, 10);
+
+			if (tablist_str == endptr) {
+				if (*tablist_str == ',' || isspace(*tablist_str))
+					tablist_str++;
+				else
+					perror_exit("unexpected character in tablist: '%c'",
+							*tablist_str);
+			} else {
+				if (t->len == allocated_size) {
+					t->tabstops =
+						xrealloc(t->tabstops, sizeof(int) * allocated_size);
+					allocated_size += TABLIST_GROWSIZ;
+				}
+
+				tablist_str = endptr;
+				t->tabstops[t->len] = tabstop;
+				(t->len)++;
+			}
+		}
+	}
+	validate_tablist(t);
+	return t;
+}
+
+int next_tabstop_in(int column, struct tablist *t)
+{
+	if (t->len == 1)
+		return t->tabstops[0] - (column % t->tabstops[0]);
+	else {
+		int i;
+		for (i = 0; i < t->len; i++)
+			if (t->tabstops[i] > column)
+				return t->tabstops[i] - column;
+		return 1;
+	}
+}
+
+void tablist_free(struct tablist *t)
+{
+	free(t->tabstops);
+	free(t);
+}
+
+int blanks_width(char *line, int column, size_t n, struct tablist *t)
+{
+	int cols;
+
+	for (cols = 0; n--; line++) {
+		if (*line == ' ')
+			cols++;
+		else if (*line == '\t')
+			cols += next_tabstop_in(column, t);
+	}
+	return cols;
+}
diff -r feb909b2e6aa -r a9ac4067c442 lib/expand.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/expand.h	Sun Apr 01 12:07:03 2012 +0400
@@ -0,0 +1,11 @@
+/* functions shared by expand and unexpand. */
+
+struct tablist {
+	size_t len;
+	int *tabstops;
+};
+
+struct tablist *parse_tablist(char *tablist_str);
+int next_tabstop_in(int column, struct tablist *t);
+int blanks_width(char *line, int column, size_t n, struct tablist *t);
+void tablist_free(struct tablist *t);
diff -r feb909b2e6aa -r a9ac4067c442 toys/expand.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toys/expand.c	Sun Apr 01 12:07:03 2012 +0400
@@ -0,0 +1,80 @@
+/* vi: set sw=4 ts=4:
+ *
+ * expand.c - Convert tabs to spaces.
+ *
+ * Copyright 2012 Ilya Kuzmich <ilya.kuzmich at gmail.com>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/expand.html
+
+USE_EXPAND(NEWTOY(expand, "t:i", TOYFLAG_USR|TOYFLAG_BIN))
+
+config EXPAND
+	bool "expand"
+	default n
+	help
+	  usage: expand [-t tablist] [file ...]
+
+	  Expand tabs in each file to spaces and write to stdout.
+	  If no files listed, process data from stdin.
+
+	  -i	expand leading tabs only.
+	  -t	list of explicit tab positions.
+			if only a single number is given, sets tabstop width.
+
+	  default tabstop width is 8.
+
+*/
+
+#include "toys.h"
+#include "lib/expand.h"
+
+DEFINE_GLOBALS(
+	char *tablist_arg;
+
+	struct tablist *tablist;
+)
+
+#define TT this.expand
+
+#define FLAG_i	1
+
+static void do_expand(int fd, char *name)
+{
+	FILE *f = fdopen(fd, "r");
+	char *line = NULL;
+	size_t linesize;
+
+	while (getline(&line, &linesize, f) > 0) {
+		char *p = line;
+		int column = 0;
+
+		do {
+			size_t blanks = strspn(p, "\t");
+			size_t nonblanks = strcspn(p + blanks, "\t");
+
+			if (blanks) {
+				int blank_cols = blanks_width(p, column, blanks, TT.tablist);
+				column += blank_cols;
+				xprintf("%*s", blank_cols, "");
+			}
+			fwrite(p + blanks, sizeof(char), nonblanks, stdout);
+			p += blanks + nonblanks;
+			column += nonblanks;
+		} while (!(toys.optflags & FLAG_i) && *p);
+
+		if (*p)
+			fputs(p, stdout);
+	}
+	fclose(f);
+	free(line);
+}
+
+void expand_main(void)
+{
+	TT.tablist = parse_tablist(TT.tablist_arg);
+
+	loopfiles(toys.optargs, do_expand);
+
+	if (CFG_TOYBOX_FREE)
+		tablist_free(TT.tablist);
+}
diff -r feb909b2e6aa -r a9ac4067c442 toys/unexpand.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toys/unexpand.c	Sun Apr 01 12:07:03 2012 +0400
@@ -0,0 +1,111 @@
+/* vi: set sw=4 ts=4:
+ *
+ * unexpand.c - Convert blanks to tabs.
+ *
+ * Copyright 2012 Ilya Kuzmich <ilya.kuzmich at gmail.com>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/unexpand.html
+
+USE_UNEXPAND(NEWTOY(unexpand, "fat:+a", TOYFLAG_USR|TOYFLAG_BIN))
+
+config UNEXPAND
+	bool "unexpand"
+	default n
+	help
+	  usage: unexpand [-a | -t tablist] [file ...]
+
+	  Convert leading blanks in each file to tabs, and write to stdout.
+	  If no files listed, process data from stdin.
+
+	  -a	convert blanks anywhere on the line.
+	  -f	convert leading blanks only.
+	  -t	list of explicit tab positions,
+			no conversion will occur for blanks beyond the last tabstop.
+			if only a single number is given, sets tabstop width.
+			conversion applied to the whole line.
+
+	  -t implies -a.
+	  default tabstop width is 8.
+*/
+
+#include "toys.h"
+#include "lib/expand.h"
+
+DEFINE_GLOBALS(
+	char *tablist_arg;
+
+	struct tablist *tablist;
+)
+
+#define TT this.unexpand
+
+#define FLAG_t	1
+#define FLAG_a	2
+#define FLAG_f	4
+
+static void as_tabs(int col, int blank_cols, int *tabs, int *succ_spaces)
+{
+	*tabs = 0;
+
+	if (blank_cols < 2)
+		*succ_spaces = blank_cols;
+	else {
+		int nextpos;
+
+		for (; blank_cols >= (nextpos = next_tabstop_in(col, TT.tablist));
+				(*tabs)++) {
+			blank_cols -= nextpos;
+			col += nextpos;
+		}
+		*succ_spaces = blank_cols;
+	}
+}
+
+static void do_unexpand(int fd, char *name)
+{
+	FILE *f = fdopen(fd, "r");
+	char *line = NULL;
+	size_t linesize;
+
+	while (getline(&line, &linesize, f) > 0) {
+		int column = 0;
+		char *p = line;
+
+		do {
+			size_t blanks = strspn(p, " \t");
+			size_t nonblanks = strcspn(p + blanks, " \t");
+			
+			if (blanks) {
+				int tabs, spaces;
+				int blank_cols = blanks_width(p, column, blanks, TT.tablist);
+
+				as_tabs(column, blank_cols, &tabs, &spaces);
+				while (tabs--) xprintf("\t");
+				printf("%*s", spaces, "");
+				column += blank_cols;
+			} 
+
+			fwrite(p + blanks, sizeof(char), nonblanks, stdout);
+			p += blanks + nonblanks;
+			column += nonblanks;
+		} while ((toys.optflags & FLAG_a) && *p);
+
+		if (*p)
+			fputs(p, stdout);
+	}
+	fclose(f);
+	free(line);
+}
+
+void unexpand_main(void)
+{
+	TT.tablist = parse_tablist(TT.tablist_arg);
+
+	if (toys.optflags & FLAG_f)
+		toys.optflags &= ~FLAG_a;
+
+	loopfiles(toys.optargs, do_unexpand);
+
+	if (CFG_TOYBOX_FREE)
+		tablist_free(TT.tablist);
+}


More information about the Toybox mailing list