diff options
-rw-r--r-- | AUTHORS | 3 | ||||
-rw-r--r-- | coreutils/paste.c | 138 | ||||
-rw-r--r-- | docs/posix_conformance.txt | 8 | ||||
-rw-r--r-- | testsuite/paste/paste | 20 | ||||
-rw-r--r-- | testsuite/paste/paste-back-cuted-lines | 9 | ||||
-rw-r--r-- | testsuite/paste/paste-multi-stdin | 16 | ||||
-rw-r--r-- | testsuite/paste/paste-pairs | 16 | ||||
-rw-r--r-- | testsuite/paste/paste-separate | 19 |
8 files changed, 228 insertions, 1 deletions
@@ -178,3 +178,6 @@ Mike Frysinger <vapier@gentoo.org> Jie Zhang <jie.zhang@analog.com> fixed two bugs in msh and hush (exitcode of killed processes) + +Maxime Coste <mawww@kakoune.org> + paste implementation diff --git a/coreutils/paste.c b/coreutils/paste.c new file mode 100644 index 0000000..4eab138 --- /dev/null +++ b/coreutils/paste.c @@ -0,0 +1,138 @@ +/* vi: set sw=4 ts=4: */ +/* + * paste.c - implementation of the posix paste command + * + * Written by Maxime Coste <mawww@kakoune.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config PASTE +//config: bool "paste" +//config: default y +//config: help +//config: paste is used to paste lines of different files together +//config: and write the result to stdout + +//applet:IF_PASTE(APPLET_NOEXEC(paste, paste, BB_DIR_USR_BIN, BB_SUID_DROP, paste)) + +//kbuild:lib-$(CONFIG_PASTE) += paste.o + +//usage:#define paste_trivial_usage +//usage: "[OPTIONS] [FILE]..." +//usage:#define paste_full_usage "\n\n" +//usage: "Paste lines from each input file, seperated with tab\n" +//usage: "\n -d LIST Use delimiters from LIST, not tab" +//usage: "\n -s Serial: one file at a time" +//usage: +//usage:#define paste_example_usage +//usage: "# write out directory in four columns\n" +//usage: "$ ls | paste - - - -\n" +//usage: "# combine pairs of lines from a file into single lines\n" +//usage: "$ paste -s -d '\\t\\n' file\n" + +#include "libbb.h" + +static void paste_files(FILE** files, int file_cnt, char* delims, int del_cnt) +{ + char *line; + char delim; + int del_idx = 0; + int active_files = file_cnt; + int i; + + while (active_files > 0) { + for (i = 0; i < file_cnt; ++i) { + if (files[i] == NULL) + continue; + + line = xmalloc_fgetline(files[i]); + if (!line) { + fclose_if_not_stdin(files[i]); + files[i] = NULL; + --active_files; + continue; + } + fputs(line, stdout); + free(line); + delim = '\n'; + if (i != file_cnt - 1) { + delim = delims[del_idx++]; + if (del_idx == del_cnt) + del_idx = 0; + } + if (delim != '\0') + fputc(delim, stdout); + } + } +} + +static void paste_files_separate(FILE** files, char* delims, int del_cnt) +{ + char *line, *next_line; + char delim; + int del_idx = 0; + int i; + + for (i = 0; files[i]; ++i) { + line = NULL; + while ((next_line = xmalloc_fgetline(files[i])) != NULL) { + if (line) { + fputs(line, stdout); + free(line); + delim = delims[del_idx++]; + if (del_idx == del_cnt) + del_idx = 0; + if (delim != '\0') + fputc(delim, stdout); + } + line = next_line; + } + if (line) { + /* coreutils adds \n even if this is a final line + * of the last file and it was not \n-terminated. + */ + printf("%s\n", line); + free(line); + } + fclose_if_not_stdin(files[i]); + } +} + +#define PASTE_OPT_DELIMITERS (1 << 0) +#define PASTE_OPT_SEPARATE (1 << 1) + +int paste_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int paste_main(int argc UNUSED_PARAM, char **argv) +{ + char *delims = (char*)"\t"; + int del_cnt = 1; + unsigned opt; + int i; + + opt = getopt32(argv, "d:s", &delims); + argv += optind; + + if (opt & PASTE_OPT_DELIMITERS) { + if (!delims[0]) + bb_error_msg_and_die("-d '' is not supported"); + /* unknown mappings are not changed: "\z" -> '\\' 'z' */ + /* trailing backslash, if any, is preserved */ + del_cnt = strcpy_and_process_escape_sequences(delims, delims) - delims; + /* note: handle NUL properly (do not stop at it!): try -d'\t\0\t' */ + } + + if (!argv[0]) + (--argv)[0] = (char*) "-"; + for (i = 0; argv[i]; ++i) { + argv[i] = (void*) fopen_or_warn_stdin(argv[i]); + if (!argv[i]) + xfunc_die(); + } + + if (opt & PASTE_OPT_SEPARATE) + paste_files_separate((FILE**)argv, delims, del_cnt); + else + paste_files((FILE**)argv, i, delims, del_cnt); + + fflush_stdout_and_exit(0); +} diff --git a/docs/posix_conformance.txt b/docs/posix_conformance.txt index c0582dc..8b91120 100644 --- a/docs/posix_conformance.txt +++ b/docs/posix_conformance.txt @@ -22,7 +22,7 @@ POSIX Tools supported only as shell built-ins (ash shell): POSIX Tools not supported: asa, at, batch, bc, c99, command, compress, csplit, ex, fc, file, gencat, getconf, iconv, join, link, locale, localedef, lp, m4, - mailx, newgrp, nl, paste, pathchk, pax, pr, qalter, qdel, qhold, qmove, + mailx, newgrp, nl, pathchk, pax, pr, qalter, qdel, qhold, qmove, qmsg, qrerun, qrls, qselect, qsig, qstat, qsub, tabs, talk, tput, tsort, unlink, uucp, uustat, uux @@ -469,6 +469,12 @@ od POSIX options -x | no | no | od Busybox specific options: None +paste POSIX options + option | exists | compliant | remarks + -d list | yes | yes | + -s | yes | yes | +paste Busybox specific options: None + patch POSIX options option | exists | compliant | remarks -D define | no | no | diff --git a/testsuite/paste/paste b/testsuite/paste/paste new file mode 100644 index 0000000..349b49d --- /dev/null +++ b/testsuite/paste/paste @@ -0,0 +1,20 @@ +cat > foo <<EOF +foo1 +foo2 +foo3 +EOF + +cat > bar <<EOF +bar1 +bar2 +bar3 +EOF + +cat > baz <<EOF +foo1 bar1 +foo2 bar2 +foo3 bar3 +EOF + +busybox paste foo bar > qux +diff -u baz qux diff --git a/testsuite/paste/paste-back-cuted-lines b/testsuite/paste/paste-back-cuted-lines new file mode 100644 index 0000000..a8171bf --- /dev/null +++ b/testsuite/paste/paste-back-cuted-lines @@ -0,0 +1,9 @@ +cat > foo <<EOF +this is the first line +this is the second line +this is the third line +EOF +cut -b 1-13 -n foo > foo1 +cut -b 14- -n foo > foo2 +busybox paste -d '\0' foo1 foo2 > bar +cmp foo bar diff --git a/testsuite/paste/paste-multi-stdin b/testsuite/paste/paste-multi-stdin new file mode 100644 index 0000000..fee5430 --- /dev/null +++ b/testsuite/paste/paste-multi-stdin @@ -0,0 +1,16 @@ +cat > foo <<EOF +line1 +line2 +line3 +line4 +line5 +line6 +EOF + +cat > bar <<EOF +line1 line2 line3 +line4 line5 line6 +EOF + +busybox paste - - - < foo > baz +cmp bar baz diff --git a/testsuite/paste/paste-pairs b/testsuite/paste/paste-pairs new file mode 100644 index 0000000..90725fa --- /dev/null +++ b/testsuite/paste/paste-pairs @@ -0,0 +1,16 @@ +cat > foo <<EOF +foo1 +bar1 +foo2 +bar2 +foo3 +EOF + +cat > bar <<EOF +foo1 bar1 +foo2 bar2 +foo3 +EOF + +busybox paste -s -d "\t\n" foo > baz +cmp bar baz diff --git a/testsuite/paste/paste-separate b/testsuite/paste/paste-separate new file mode 100644 index 0000000..40793fb --- /dev/null +++ b/testsuite/paste/paste-separate @@ -0,0 +1,19 @@ +cat > foo <<EOF +foo1 +foo2 +foo3 +EOF + +cat > bar <<EOF +bar1 +bar2 +bar3 +EOF + +cat > baz <<EOF +foo1 foo2 foo3 +bar1 bar2 bar3 +EOF + +busybox paste -s foo bar > qux +cmp baz qux |