/* expand.c - expands tabs to space * * Copyright 2012 Jonathan Clairembault * * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/expand.html USE_EXPAND(NEWTOY(expand, "t:", TOYFLAG_USR|TOYFLAG_BIN)) config EXPAND bool "expand" default y help usage: expand [-t tablist] [file...] Expand tabs to spaces according to tabstops. -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). */ #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; rdbufioff : 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; } } } } 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"); loopfiles(toys.optargs, expand_file); if (CFG_TOYBOX_FREE) llist_traverse(TT.tablist.next, free); }