Mercurial > hg > toybox
view toys/posix/expand.c @ 715:3417db95f24b
Add expand command as described in POSIX-2008.
Erratum: Do not handle backspace.
author | Jonathan Clairembault <jonathan@clairembault.fr> |
---|---|
date | Fri, 23 Nov 2012 00:06:28 +0100 |
parents | |
children | 8c10cf7bace0 |
line wrap: on
line source
/* 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); }