aboutsummaryrefslogtreecommitdiff
path: root/toys/posix/expand.c
diff options
context:
space:
mode:
authorJonathan Clairembault <jonathan@clairembault.fr>2012-11-23 00:06:28 +0100
committerJonathan Clairembault <jonathan@clairembault.fr>2012-11-23 00:06:28 +0100
commit939fa7408fa68af8568fd07de64a1606af0a0c06 (patch)
tree86dda7e86cadc1831dd5d69277ec926eb3d43f6b /toys/posix/expand.c
parenta5f8c733d478a57ad03c0b0efe7fa995e4c364a2 (diff)
downloadtoybox-939fa7408fa68af8568fd07de64a1606af0a0c06.tar.gz
Add expand command as described in POSIX-2008.
Erratum: Do not handle backspace.
Diffstat (limited to 'toys/posix/expand.c')
-rw-r--r--toys/posix/expand.c141
1 files changed, 141 insertions, 0 deletions
diff --git a/toys/posix/expand.c b/toys/posix/expand.c
new file mode 100644
index 00000000..739c326d
--- /dev/null
+++ b/toys/posix/expand.c
@@ -0,0 +1,141 @@
+/* expand.c - expands tabs to space
+ *
+ * FIXME: handle backspace.
+ *
+ * Copyright 2012 Jonathan Clairembault <jonathan at clairembault dot fr>
+ *
+ * See http://http://pubs.opengroup.org/onlinepubs/9699919799/nframe.html
+
+USE_EXPAND(NEWTOY(expand, "t:", TOYFLAG_USR|TOYFLAG_BIN))
+
+config EXPAND
+ bool "expand"
+ default n
+ help
+ usage: expand [-t tablist] [file...]
+
+ Command expand. Expands tabs to space according to tabstops.
+
+ -t tablist
+ Specify the tab stops. The argument tablist consists of either a single
+ strictly positive decimal integer or a list of tabstops. If a single number
+ is given, tabs are set that number of column positions apart instead of the
+ default 8.
+
+ If a list of tabstops is given, the list is made of two or more strictly
+ positive decimal integers, separated by <blank> or <comma> characters, in
+ strictly ascending order. The <tab> characters are set at those specific
+ column positions.
+
+ In the event of expand having to process a <tab> at a position beyond the
+ last of those specified in a multiple tab-stop list, the <tab> is replaced
+ by a single <space> in the output.
+
+ Any <backspace> characters shall be copied to the output and cause the
+ column position count for tab stop calculations to be decremented; the
+ column position count shall not be decremented below zero.
+*/
+
+#define FOR_expand
+#include "toys.h"
+
+GLOBALS(
+ char *t_flags;
+ struct offset_list tablist;
+)
+
+static void build_tablist(char *tabstops)
+{
+ char *ctx;
+ struct offset_list *tablist = &TT.tablist;
+ char *s, *ref;
+ off_t stop, last_stop;
+
+ /* for every tabstop decode and add to list */
+ for (stop = last_stop = 0, s = ref = xstrdup(tabstops); ;
+ last_stop = stop, s = NULL) {
+ char *tabstop = strtok_r(s, " ,", &ctx);
+
+ if (!tabstop) return;
+
+ stop = xstrtoul(tabstop, NULL, 0);
+ if (stop <= last_stop) {
+ free(ref);
+ toys.exithelp = 1;
+ error_exit("tablist ascending order");
+ }
+ tablist->next = xzalloc(sizeof(*tablist));
+ tablist->next->off = stop;
+ tablist = tablist->next;
+ }
+
+ free(ref);
+}
+
+static void expand_file(int fd, char *name)
+{
+ ssize_t rdn;
+ char *rdbuf, *wrbuf;
+ size_t wrbuflen, rdbuflen;
+ ssize_t rdbufi = 0, wrbufi = 0;
+ ssize_t wrlinei;
+ int hastablist = !!TT.tablist.next->next;
+ struct offset_list *tablist = TT.tablist.next;
+ ssize_t stop = tablist->off;
+
+ wrbuflen = rdbuflen = ARRAY_LEN(toybuf)/2;
+ rdbuf = toybuf;
+ wrbuf = toybuf + rdbuflen;
+ do {
+ rdn = readall(fd, rdbuf, rdbuflen);
+ if (rdn < 0) perror_exit("%s", name);
+ for (rdbufi=0, wrbufi=0; rdbufi<rdn; rdbufi++) {
+ if (wrbufi == wrbuflen) { /* flush expand buffer when full */
+ writeall(STDOUT_FILENO, wrbuf, wrbuflen);
+ wrbufi = 0;
+ }
+ if (rdbuf[rdbufi] == '\t') { /* expand tab */
+ size_t count;
+ size_t tabsize;
+
+ /* search next tab stop */
+ while(tablist && (stop <= wrlinei)) {
+ stop = hastablist ? tablist->off : stop + tablist->off;
+ tablist = hastablist ? tablist->next : tablist;
+ }
+ tabsize = ((stop - wrlinei < 2)) ? 1 : stop - wrlinei;
+ while (tabsize) { /* long expand */
+ count = min(tabsize, wrbuflen - wrbufi);
+ memset(wrbuf + wrbufi, ' ', count);
+ tabsize -= count;
+ if (tabsize) { /* flush expand buffer when full */
+ writeall(STDOUT_FILENO, wrbuf, wrbuflen);
+ wrbufi = 0;
+ } else wrbufi += count;
+ }
+ wrlinei += count;
+ } else { /* copy input to output */
+ wrbuf[wrbufi++] = rdbuf[rdbufi];
+ wrlinei += 1;
+ /* flush expand buffer and reset tablist at newline */
+ if (rdbuf[rdbufi] == '\n') {
+ writeall(STDOUT_FILENO, wrbuf, wrbufi);
+ tablist = TT.tablist.next;
+ stop = tablist->off;
+ wrbufi = wrlinei = 0;
+ }
+ }
+ }
+ } while (rdn == rdbuflen);
+ /* flush last expand buffer */
+ writeall(STDOUT_FILENO, wrbuf, wrbufi);
+}
+
+void expand_main(void)
+{
+ build_tablist((toys.optflags & FLAG_t) ? TT.t_flags : "8");
+ /* expand every file */
+ loopfiles(toys.optargs, expand_file);
+ /* free tablist */
+ llist_traverse(TT.tablist.next, free);
+}