changeset 722:08d538115f39

Largeish rewrite of expand, mostly described on the mailing list.
author Rob Landley <rob@landley.net>
date Fri, 30 Nov 2012 02:41:52 -0600
parents 8edffa9e7660
children 2818724bb8be
files toys/posix/expand.c
diffstat 1 files changed, 71 insertions(+), 89 deletions(-) [+]
line wrap: on
line diff
--- a/toys/posix/expand.c	Wed Nov 28 22:56:16 2012 -0600
+++ b/toys/posix/expand.c	Fri Nov 30 02:41:52 2012 -0600
@@ -4,7 +4,7 @@
  *
  * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/expand.html
 
-USE_EXPAND(NEWTOY(expand, "t:", TOYFLAG_USR|TOYFLAG_BIN))
+USE_EXPAND(NEWTOY(expand, "t*", TOYFLAG_USR|TOYFLAG_BIN))
 
 config EXPAND
   bool "expand"
@@ -17,112 +17,94 @@
     -t	tablist
 
       Specify tab stops, either a single number instead of the default 8,
-      or a list of increasing numbers (comma or space separated, after which
-      each additional tab becomes one space).
+      or a comma separated list of increasing numbers representing tabstop
+      positions (absolute, not increments) with each additional tab beyound
+      that becoming one space.
 */
 
 #define FOR_expand
 #include "toys.h"
 
 GLOBALS(
-  char *t_flags;
-  struct offset_list tablist;
+  struct arg_list *tabs;
+
+  unsigned tabcount, *tab;
 )
 
-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;
+  int i, len, x=0, stop = 0;
 
-  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;
+  for (;;) {
+    len = read(fd, toybuf, sizeof(toybuf));
+    if (len<0) {
+      perror_msg("%s", name);
+      toys.exitval = 1;
+      return;
+    }
+    if (!len) break;
+    for (i=0; i<len; i++) {
+      int len = 1;
+      if (toybuf[i] != '\t') {
+        if (EOF == putc(toybuf[i], stdout)) perror_exit(0);
+        if (toybuf[i] == '\n') {
+          x = stop = 0;
+          continue;
+        }
+      } else {
+        if (TT.tabcount < 2) {
+          len = TT.tabcount ? *TT.tab : 8;
+          len -= x%len;
+        } else while (stop < TT.tabcount) {
+          if (TT.tab[stop] > x) {
+            len = TT.tab[stop] - x;
+            break;
+          } else stop++;
+        }
+        xprintf("%*c", len, ' ');
       }
-      if (rdbuf[rdbufi] == '\t') { /* expand tab */
-        size_t count;
-        size_t tabsize;
+      x += len;
+    }
+  }
+}
 
-        /* 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];
-        if (rdbuf[rdbufi] == '\b') /* go back one column on backspace */
-          wrlinei -= !!wrlinei; /* do not go below zero */
-        else
-          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;
-        }
-      }
+// Parse -t options to fill out unsigned array in tablist (if not NULL)
+// return number of entries in tablist
+static int parse_tablist(unsigned *tablist)
+{
+  struct arg_list *tabs;
+  int tabcount = 0;
+
+  for (tabs = TT.tabs; tabs; tabs = tabs->next) {
+    char *s = tabs->arg;
+
+    while (*s) {
+      int count;
+      unsigned x, *t = tablist ? tablist+tabcount : &x;
+
+      if (tabcount >= sizeof(toybuf)/sizeof(unsigned)) break;
+      if (sscanf(s, "%u%n", t, &count) != 1) break;
+      if (tabcount++ && tablist && *(t-1) >= *t) break;
+      s += count;
+      if (*s==' ' || *s==',') s++;
+      else break;
     }
-  } while (rdn == rdbuflen);
-  /* flush last expand buffer */
-  writeall(STDOUT_FILENO, wrbuf, wrbufi);
+    if (*s) error_exit("bad tablist");
+  }
+
+  return tabcount;
 }
 
 void expand_main(void)
 {
-  build_tablist((toys.optflags & FLAG_t) ? TT.t_flags : "8");
+  TT.tabcount = parse_tablist(NULL);
+
+  // Determine size of tablist, allocate memory, fill out tablist
+  if (TT.tabcount) {
+    TT.tab = xmalloc(sizeof(unsigned)*TT.tabcount);
+    parse_tablist(TT.tab);
+  }
 
   loopfiles(toys.optargs, expand_file);
-  if (CFG_TOYBOX_FREE) llist_traverse(TT.tablist.next, free);
+  if (CFG_TOYBOX_FREE) free(TT.tab);
 }