[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