# HG changeset patch # User Rob Landley # Date 1354264912 21600 # Node ID 08d538115f396cb800a9be3d0fda745e009171ea # Parent 8edffa9e766019a553fe5e3a7cec2ee2e0f9778f Largeish rewrite of expand, mostly described on the mailing list. diff -r 8edffa9e7660 -r 08d538115f39 toys/posix/expand.c --- 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; -) - -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); + struct arg_list *tabs; - 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); -} + unsigned tabcount, *tab; +) 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; rdbufioff : stop + tablist->off; - tablist = hastablist ? tablist->next : tablist; + 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 x) { + len = TT.tab[stop] - x; + break; + } else stop++; } - 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; - } + xprintf("%*c", len, ' '); } + x += len; } - } while (rdn == rdbuflen); - /* flush last expand buffer */ - writeall(STDOUT_FILENO, wrbuf, wrbufi); + } +} + +// 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; + } + 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); }