From d2383f57cd1ede7c43c69953e101b89d920353f6 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Thu, 23 Mar 2017 17:35:20 +0100 Subject: [PATCH] paste: new applet function old new delta paste_main - 493 +493 packed_usage 31019 31070 +51 applet_names 2569 2575 +6 applet_main 1484 1488 +4 ------------------------------------------------------------------------------ (add/remove: 2/0 grow/shrink: 3/0 up/down: 554/0) Total: 554 bytes Signed-off-by: Maxime Coste Signed-off-by: Denys Vlasenko --- AUTHORS | 3 + coreutils/paste.c | 138 +++++++++++++++++++++++++ docs/posix_conformance.txt | 8 +- testsuite/paste/paste | 20 ++++ testsuite/paste/paste-back-cuted-lines | 9 ++ testsuite/paste/paste-multi-stdin | 16 +++ testsuite/paste/paste-pairs | 16 +++ testsuite/paste/paste-separate | 19 ++++ 8 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 coreutils/paste.c create mode 100644 testsuite/paste/paste create mode 100644 testsuite/paste/paste-back-cuted-lines create mode 100644 testsuite/paste/paste-multi-stdin create mode 100644 testsuite/paste/paste-pairs create mode 100644 testsuite/paste/paste-separate diff --git a/AUTHORS b/AUTHORS index fa58697f7..5c9a634c9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -178,3 +178,6 @@ Mike Frysinger Jie Zhang fixed two bugs in msh and hush (exitcode of killed processes) + +Maxime Coste + paste implementation diff --git a/coreutils/paste.c b/coreutils/paste.c new file mode 100644 index 000000000..4eab13839 --- /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 + * + * 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 c0582dc23..8b9112020 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 000000000..349b49d49 --- /dev/null +++ b/testsuite/paste/paste @@ -0,0 +1,20 @@ +cat > foo < bar < baz < 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 000000000..a8171bf1e --- /dev/null +++ b/testsuite/paste/paste-back-cuted-lines @@ -0,0 +1,9 @@ +cat > 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 000000000..fee543058 --- /dev/null +++ b/testsuite/paste/paste-multi-stdin @@ -0,0 +1,16 @@ +cat > foo < bar < baz +cmp bar baz diff --git a/testsuite/paste/paste-pairs b/testsuite/paste/paste-pairs new file mode 100644 index 000000000..90725fa87 --- /dev/null +++ b/testsuite/paste/paste-pairs @@ -0,0 +1,16 @@ +cat > foo < bar < baz +cmp bar baz diff --git a/testsuite/paste/paste-separate b/testsuite/paste/paste-separate new file mode 100644 index 000000000..40793fb31 --- /dev/null +++ b/testsuite/paste/paste-separate @@ -0,0 +1,19 @@ +cat > foo < bar < baz < qux +cmp baz qux