blob: a322926a1cd268001392be980d9403ec09946541 [file] [log] [blame]
/* expand.c - expands tabs to space
*
* Copyright 2012 Jonathan Clairembault <jonathan at clairembault dot fr>
*
* 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; 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];
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);
}