From ea01f254b4e0dc2f59db4cba9dd4fa98e9f5b2e5 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Thu, 13 Nov 2025 07:50:50 +0000 Subject: paste: fix output when file lengths differ If the files being pasted had different numbers of lines the output was incorrect. Rewrite the loop over all lines to allow for this. Add tests for such conditions. function old new delta paste_main 458 526 +68 Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- coreutils/paste.c | 36 ++++++++++++++++++++++++------------ testsuite/paste/paste-long-short | 19 +++++++++++++++++++ testsuite/paste/paste-short-long | 19 +++++++++++++++++++ 3 files changed, 62 insertions(+), 12 deletions(-) create mode 100644 testsuite/paste/paste-long-short create mode 100644 testsuite/paste/paste-short-long diff --git a/coreutils/paste.c b/coreutils/paste.c index 3e5f20158..363340e3d 100644 --- a/coreutils/paste.c +++ b/coreutils/paste.c @@ -34,27 +34,38 @@ static void paste_files(FILE** files, int file_cnt, char* delims, int del_cnt) { - char *line; + char **line = xmalloc(file_cnt * sizeof(char *)); char delim; int active_files = file_cnt; int i; while (active_files > 0) { int del_idx = 0; + int got_line = FALSE; 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; + if (files[i]) { + line[i] = xmalloc_fgetline(files[i]); + if (!line[i]) { + fclose_if_not_stdin(files[i]); + files[i] = NULL; + --active_files; + } else { + got_line = TRUE; + } + } else { + line[i] = NULL; + } + } + + if (!got_line) + break; + + for (i = 0; i < file_cnt; ++i) { + if (line[i]) { + fputs_stdout(line[i]); + free(line[i]); } - fputs_stdout(line); - free(line); delim = '\n'; if (i != file_cnt - 1) { delim = delims[del_idx++]; @@ -65,6 +76,7 @@ static void paste_files(FILE** files, int file_cnt, char* delims, int del_cnt) fputc(delim, stdout); } } + free(line); } static void paste_files_separate(FILE** files, char* delims, int del_cnt) diff --git a/testsuite/paste/paste-long-short b/testsuite/paste/paste-long-short new file mode 100644 index 000000000..e626d730e --- /dev/null +++ b/testsuite/paste/paste-long-short @@ -0,0 +1,19 @@ +cat > foo < bar < baz < qux +diff -u baz qux diff --git a/testsuite/paste/paste-short-long b/testsuite/paste/paste-short-long new file mode 100644 index 000000000..785da60a7 --- /dev/null +++ b/testsuite/paste/paste-short-long @@ -0,0 +1,19 @@ +cat > foo < bar < baz < qux +diff -u baz qux -- cgit v1.2.3-55-g6feb