You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
131 lines
3.0 KiB
131 lines
3.0 KiB
/* paste.c - Merge corresponding lines
|
|
*
|
|
* Copyright 2012 Felix Janda <felix.janda@posteo.de>
|
|
*
|
|
* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/paste.html
|
|
*
|
|
* Deviations from posix: the FILE argument isn't mandatory, none == '-'
|
|
|
|
USE_PASTE(NEWTOY(paste, "d:s", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
|
|
|
|
config PASTE
|
|
bool "paste"
|
|
default y
|
|
help
|
|
usage: paste [-s] [-d DELIMITERS] [FILE...]
|
|
|
|
Merge corresponding lines from each input file.
|
|
|
|
-d List of delimiter characters to separate fields with (default is \t)
|
|
-s Sequential mode: turn each input file into one line of output
|
|
*/
|
|
|
|
#define FOR_paste
|
|
#include "toys.h"
|
|
|
|
GLOBALS(
|
|
char *d;
|
|
|
|
int files;
|
|
)
|
|
|
|
// \0 is weird, and -d "" is also weird.
|
|
|
|
static void paste_files(void)
|
|
{
|
|
FILE **fps = (void *)toybuf;
|
|
char *dpos, *dstr, *buf, c;
|
|
int i, any, dcount, dlen, len, seq = toys.optflags&FLAG_s;
|
|
|
|
// Loop through lines until no input left
|
|
for (;;) {
|
|
|
|
// Start of each line/file resets delimiter cycle
|
|
dpos = TT.d;
|
|
|
|
for (i = any = dcount = dlen = 0; seq || i<TT.files; i++) {
|
|
size_t blen;
|
|
wchar_t wc;
|
|
FILE *ff = seq ? *fps : fps[i];
|
|
|
|
// Read and output line, preserving embedded NUL bytes.
|
|
|
|
buf = 0;
|
|
len = 0;
|
|
if (!ff || 0>=(len = getline(&buf, &blen, ff))) {
|
|
if (ff && ff!=stdin) fclose(ff);
|
|
if (seq) return;
|
|
fps[i] = 0;
|
|
if (!any) continue;
|
|
}
|
|
dcount = any ? 1 : i;
|
|
any = 1;
|
|
|
|
// Output delimiters as necessary: not at beginning/end of line,
|
|
// catch up if first few files had no input but a later one did.
|
|
// Entire line with no input means no output.
|
|
|
|
while (dcount) {
|
|
|
|
// Find next delimiter, which can be "", \n, or UTF8 w/combining chars
|
|
dstr = dpos;
|
|
dlen = 0;
|
|
dcount--;
|
|
|
|
if (!*TT.d) {;}
|
|
else if (*dpos == '\\') {
|
|
if (*++dpos=='0') dpos++;
|
|
else {
|
|
dlen = 1;
|
|
if ((c = unescape(*dpos))) {
|
|
dstr = &c;
|
|
dpos++;
|
|
}
|
|
}
|
|
} else {
|
|
while (0<(dlen = utf8towc(&wc, dpos, 99))) {
|
|
dpos += dlen;
|
|
if (!(dlen = wcwidth(wc))) continue;
|
|
if (dlen<0) dpos = dstr+1;
|
|
break;
|
|
}
|
|
dlen = dpos-dstr;
|
|
}
|
|
if (!*dpos) dpos = TT.d;
|
|
|
|
if (dlen) fwrite(dstr, dlen, 1, stdout);
|
|
}
|
|
|
|
if (0<len) {
|
|
fwrite(buf, len-(buf[len-1]=='\n'), 1, stdout);
|
|
free(buf);
|
|
}
|
|
}
|
|
|
|
// Only need a newline if we output something
|
|
if (any) xputc('\n');
|
|
else break;
|
|
}
|
|
}
|
|
|
|
static void do_paste(int fd, char *name)
|
|
{
|
|
FILE **fps = (void *)toybuf;
|
|
|
|
if (!(fps[TT.files++] = (fd ? fdopen(fd, "r") : stdin))) perror_exit(0);
|
|
if (TT.files >= sizeof(toybuf)/sizeof(FILE *)) perror_exit("tilt");
|
|
if (toys.optflags&FLAG_s) {
|
|
paste_files();
|
|
xputc('\n');
|
|
TT.files = 0;
|
|
}
|
|
}
|
|
|
|
void paste_main(void)
|
|
{
|
|
if (!(toys.optflags&FLAG_d)) TT.d = "\t";
|
|
|
|
loopfiles_rw(toys.optargs, O_RDONLY, 0, do_paste);
|
|
if (!(toys.optflags&FLAG_s)) paste_files();
|
|
}
|