commit fb68527903d626fb5fdb91d7f0d55bc40e210255 Author: CentOS Sources Date: Tue Nov 15 01:56:25 2022 -0500 import coreutils-8.32-32.el9 diff --git a/.coreutils.metadata b/.coreutils.metadata new file mode 100644 index 0000000..bb2b01c --- /dev/null +++ b/.coreutils.metadata @@ -0,0 +1 @@ +b2b12195e276c64c8e850cf40ea2cff9b3aa53f6 SOURCES/coreutils-8.32.tar.xz diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..84a3d27 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/coreutils-8.32.tar.xz diff --git a/SOURCES/coreutils-4.5.3-langinfo.patch b/SOURCES/coreutils-4.5.3-langinfo.patch new file mode 100644 index 0000000..a8af3bd --- /dev/null +++ b/SOURCES/coreutils-4.5.3-langinfo.patch @@ -0,0 +1,20 @@ +diff --git a/src/date.c b/src/date.c +index ddb011e..619a72b 100644 +--- a/src/date.c ++++ b/src/date.c +@@ -490,14 +490,7 @@ main (int argc, char **argv) + format = DATE_FMT_LANGINFO (); + if (! *format) + { +- /* Do not wrap the following literal format string with _(...). +- For example, suppose LC_ALL is unset, LC_TIME=POSIX, +- and LANG="ko_KR". In that case, POSIX says that LC_TIME +- determines the format and contents of date and time strings +- written by date, which means "date" must generate output +- using the POSIX locale; but adding _() would cause "date" +- to use a Korean translation of the format. */ +- format = "%a %b %e %H:%M:%S %Z %Y"; ++ format = dcgettext(NULL, N_("%a %b %e %H:%M:%S %Z %Y"), LC_TIME); + } + } + diff --git a/SOURCES/coreutils-8.2-uname-processortype.patch b/SOURCES/coreutils-8.2-uname-processortype.patch new file mode 100644 index 0000000..ed01ab8 --- /dev/null +++ b/SOURCES/coreutils-8.2-uname-processortype.patch @@ -0,0 +1,49 @@ +diff --git a/src/uname.c b/src/uname.c +index 6371ca2..1ad8fd7 100644 +--- a/src/uname.c ++++ b/src/uname.c +@@ -300,13 +300,19 @@ main (int argc, char **argv) + + if (toprint & PRINT_PROCESSOR) + { +- char const *element = unknown; ++ char *element = unknown; + #if HAVE_SYSINFO && defined SI_ARCHITECTURE + { + static char processor[257]; + if (0 <= sysinfo (SI_ARCHITECTURE, processor, sizeof processor)) + element = processor; + } ++#else ++ { ++ static struct utsname u; ++ uname(&u); ++ element = u.machine; ++ } + #endif + #ifdef UNAME_PROCESSOR + if (element == unknown) +@@ -344,7 +350,7 @@ main (int argc, char **argv) + + if (toprint & PRINT_HARDWARE_PLATFORM) + { +- char const *element = unknown; ++ char *element = unknown; + #if HAVE_SYSINFO && defined SI_PLATFORM + { + static char hardware_platform[257]; +@@ -352,6 +358,14 @@ main (int argc, char **argv) + hardware_platform, sizeof hardware_platform)) + element = hardware_platform; + } ++#else ++ { ++ static struct utsname u; ++ uname(&u); ++ element = u.machine; ++ if(strlen(element)==4 && element[0]=='i' && element[2]=='8' && element[3]=='6') ++ element[1]='3'; ++ } + #endif + #ifdef UNAME_HARDWARE_PLATFORM + if (element == unknown) diff --git a/SOURCES/coreutils-8.26-selinuxenable.patch b/SOURCES/coreutils-8.26-selinuxenable.patch new file mode 100644 index 0000000..d9b625a --- /dev/null +++ b/SOURCES/coreutils-8.26-selinuxenable.patch @@ -0,0 +1,26 @@ +From 6880c3dc9098b3337612850d1500b474aeb944ca Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +Date: Tue, 29 Aug 2017 17:33:51 +0200 +Subject: [PATCH] require_selinux_(): use selinuxenabled(8) if available + +--- + init.cfg | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/init.cfg b/init.cfg +index af6b581..f887b3a 100644 +--- a/init.cfg ++++ b/init.cfg +@@ -114,6 +114,9 @@ require_selinux_() + grep 'selinuxfs$' /proc/filesystems > /dev/null \ + || skip_ "this system lacks SELinux support" + ++ # use the 'selinuxenabled' utility if available ++ selinuxenabled; [ $? = 1 ] && skip_ "SELinux is disabled" ++ + # Independent of whether SELinux is enabled system-wide, + # the current file system may lack SELinux support. + # Also the current build may have SELinux support disabled. +-- +2.9.5 + diff --git a/SOURCES/coreutils-8.26-test-lock.patch b/SOURCES/coreutils-8.26-test-lock.patch new file mode 100644 index 0000000..d66928c --- /dev/null +++ b/SOURCES/coreutils-8.26-test-lock.patch @@ -0,0 +1,29 @@ +From 0d04ee8ddedb2bf33d64f148f246a3b7ec4fef21 Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +Date: Mon, 23 Jan 2017 12:35:41 +0100 +Subject: [PATCH] test-lock: disable the rwlock test + +It hangs indefinitely if the system rwlock implementation does not +prevent writer starvation (and glibc does not implement it). + +Bug: http://www.mail-archive.com/bug-gnulib@gnu.org/msg33017.html +--- + gnulib-tests/test-lock.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/gnulib-tests/test-lock.c b/gnulib-tests/test-lock.c +index aa6de27..5af0a6c 100644 +--- a/gnulib-tests/test-lock.c ++++ b/gnulib-tests/test-lock.c +@@ -42,7 +42,7 @@ + Uncomment some of these, to verify that all tests crash if no locking + is enabled. */ + #define DO_TEST_LOCK 1 +-#define DO_TEST_RWLOCK 1 ++#define DO_TEST_RWLOCK 0 + #define DO_TEST_RECURSIVE_LOCK 1 + #define DO_TEST_ONCE 1 + +-- +2.7.4 + diff --git a/SOURCES/coreutils-8.32-DIR_COLORS.patch b/SOURCES/coreutils-8.32-DIR_COLORS.patch new file mode 100644 index 0000000..9d422f4 --- /dev/null +++ b/SOURCES/coreutils-8.32-DIR_COLORS.patch @@ -0,0 +1,100 @@ +From 81e25c8521937ecf7f444bab11fddaaf81cc3efd Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +Date: Fri, 17 Jun 2016 16:58:18 +0200 +Subject: [PATCH] downstream changes to default DIR_COLORS + +--- + DIR_COLORS | 9 ++++++++- + DIR_COLORS.lightbgcolor | 21 +++++++++++++++------ + 2 files changed, 23 insertions(+), 7 deletions(-) + +diff --git a/DIR_COLORS b/DIR_COLORS +index bd5df23..84f2417 100644 +--- a/DIR_COLORS ++++ b/DIR_COLORS +@@ -1,3 +1,7 @@ ++# This file goes in the /etc directory, and must be world readable. ++# You can copy this file to .dir_colors in your $HOME directory to override ++# the system defaults. ++ + # Configuration file for dircolors, a utility to help you set the + # LS_COLORS environment variable used by GNU ls with the --color option. + +@@ -8,6 +12,9 @@ + # The keywords COLOR, OPTIONS, and EIGHTBIT (honored by the + # slackware version of dircolors) are recognized but ignored. + ++# For compatibility, the pattern "^COLOR.*none" is recognized as a way to ++# disable colorization. See https://bugzilla.redhat.com/1349579 for details. ++ + # Below are TERM entries, which can be a glob patterns, to match + # against the TERM environment variable to determine if it is colorizable. + TERM Eterm +@@ -58,7 +65,7 @@ DOOR 01;35 # door + BLK 40;33;01 # block device driver + CHR 40;33;01 # character device driver + ORPHAN 40;31;01 # symlink to nonexistent file, or non-stat'able file ... +-MISSING 00 # ... and the files they point to ++MISSING 01;37;41 # ... and the files they point to + SETUID 37;41 # file that is setuid (u+s) + SETGID 30;43 # file that is setgid (g+s) + CAPABILITY 30;41 # file with capability +diff --git a/DIR_COLORS.lightbgcolor b/DIR_COLORS.lightbgcolor +index 4316832..6402854 100644 +--- a/DIR_COLORS.lightbgcolor ++++ b/DIR_COLORS.lightbgcolor +@@ -1,3 +1,9 @@ ++# Configuration file for the color ls utility - modified for lighter backgrounds ++ ++# This file goes in the /etc directory, and must be world readable. ++# You can copy this file to .dir_colors in your $HOME directory to override ++# the system defaults. ++ + # Configuration file for dircolors, a utility to help you set the + # LS_COLORS environment variable used by GNU ls with the --color option. + +@@ -8,6 +14,9 @@ + # The keywords COLOR, OPTIONS, and EIGHTBIT (honored by the + # slackware version of dircolors) are recognized but ignored. + ++# For compatibility, the pattern "^COLOR.*none" is recognized as a way to ++# disable colorization. See https://bugzilla.redhat.com/1349579 for details. ++ + # Below are TERM entries, which can be a glob patterns, to match + # against the TERM environment variable to determine if it is colorizable. + TERM Eterm +@@ -48,17 +57,17 @@ TERM xterm* + #NORMAL 00 # no color code at all + #FILE 00 # regular file: use no color at all + RESET 0 # reset to "normal" color +-DIR 01;34 # directory +-LINK 01;36 # symbolic link. (If you set this to 'target' instead of a ++DIR 00;34 # directory ++LINK 00;36 # symbolic link. (If you set this to 'target' instead of a + # numerical value, the color is as for the file pointed to.) + MULTIHARDLINK 00 # regular file with more than one link + FIFO 40;33 # pipe +-SOCK 01;35 # socket +-DOOR 01;35 # door ++SOCK 00;35 # socket ++DOOR 00;35 # door + BLK 40;33;01 # block device driver + CHR 40;33;01 # character device driver + ORPHAN 40;31;01 # symlink to nonexistent file, or non-stat'able file ... +-MISSING 00 # ... and the files they point to ++MISSING 01;37;41 # ... and the files they point to + SETUID 37;41 # file that is setuid (u+s) + SETGID 30;43 # file that is setgid (g+s) + CAPABILITY 30;41 # file with capability +@@ -67,7 +76,7 @@ OTHER_WRITABLE 34;42 # dir that is other-writable (o+w) and not sticky + STICKY 37;44 # dir with the sticky bit set (+t) and not other-writable + + # This is for files with execute permission: +-EXEC 01;32 ++EXEC 00;32 + + # List any file extensions like '.gz' or '.tar' that you would like ls + # to colorize below. Put the extension, a space, and the color init string. +-- +2.21.1 + diff --git a/SOURCES/coreutils-8.32-coverity-utimens.patch b/SOURCES/coreutils-8.32-coverity-utimens.patch new file mode 100644 index 0000000..5bbbb6e --- /dev/null +++ b/SOURCES/coreutils-8.32-coverity-utimens.patch @@ -0,0 +1,52 @@ +From 51b9a8ba0974d262e0b0f81a2078b3c7907b25ed Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Wed, 7 Apr 2021 17:29:59 -0700 +Subject: [PATCH] utimens: fix confusing arg type in internal func +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Although the old code was technically correct, this was accidental +and it understandably confused Coverity. Reported by Ondrej Dubaj in: +https://lists.gnu.org/r/bug-tar/2021-04/msg00000.html +* lib/utimens.c (update_timespec): Change arg type from ‘struct +timespec *[2]’ (pointer to array of 2 pointers to timespecs) to +‘struct timespec **’ (pointer to pointer to the first timespec in +an array of 2 timespecs). Although the old code happened to be +technically correct, it was misleading and confused Coverity. +And though the type ‘struct timespec (**)[2]’ (pointer to pointer +to array of 2 timespecs) would perhaps be more technically +correct, it would be almost as confusing and would require changes +elsewhere in this file; let’s quit while we’re ahead. + +Upstream-commit: a3a946f670718d0dee5a7425ad5ac0a29fb46ea1 +Signed-off-by: Kamil Dudka +--- + lib/utimens.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/lib/utimens.c b/lib/utimens.c +index 3f53942..ea8c672 100644 +--- a/lib/utimens.c ++++ b/lib/utimens.c +@@ -123,14 +123,14 @@ validate_timespec (struct timespec timespec[2]) + return result + (utime_omit_count == 1); + } + +-/* Normalize any UTIME_NOW or UTIME_OMIT values in *TS, using stat +- buffer STATBUF to obtain the current timestamps of the file. If ++/* Normalize any UTIME_NOW or UTIME_OMIT values in (*TS)[0] and (*TS)[1], ++ using STATBUF to obtain the current timestamps of the file. If + both times are UTIME_NOW, set *TS to NULL (as this can avoid some + permissions issues). If both times are UTIME_OMIT, return true + (nothing further beyond the prior collection of STATBUF is + necessary); otherwise return false. */ + static bool +-update_timespec (struct stat const *statbuf, struct timespec *ts[2]) ++update_timespec (struct stat const *statbuf, struct timespec **ts) + { + struct timespec *timespec = *ts; + if (timespec[0].tv_nsec == UTIME_OMIT +-- +2.26.3 + diff --git a/SOURCES/coreutils-8.32-cp-file-range.patch b/SOURCES/coreutils-8.32-cp-file-range.patch new file mode 100644 index 0000000..6d3f651 --- /dev/null +++ b/SOURCES/coreutils-8.32-cp-file-range.patch @@ -0,0 +1,1339 @@ +From 5f2dac18054d9d9b3d84e7fba8c2a6e750d2c245 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Wed, 1 Apr 2020 12:51:34 +0100 +Subject: [PATCH 01/12] cp: ensure --attributes-only doesn't remove files + +* src/copy.c (copy_internal): Ensure we don't unlink the destination +unless explicitly requested. +* tests/cp/attr-existing.sh: Add test cases. +* NEWS: Mention the bug fix. +Fixes https://bugs.gnu.org/40352 + +Upstream-commit: 7b5f0fa47cd04c84975250d5b5da7c98e097e99f +Signed-off-by: Kamil Dudka +--- + src/copy.c | 9 +++++---- + tests/cp/attr-existing.sh | 21 ++++++++++++++++++--- + 2 files changed, 23 insertions(+), 7 deletions(-) + +diff --git a/src/copy.c b/src/copy.c +index 6e5efc7..54601ce 100644 +--- a/src/copy.c ++++ b/src/copy.c +@@ -2211,10 +2211,11 @@ copy_internal (char const *src_name, char const *dst_name, + /* Never unlink dst_name when in move mode. */ + && ! x->move_mode + && (x->unlink_dest_before_opening +- || (x->preserve_links && 1 < dst_sb.st_nlink) +- || (x->dereference == DEREF_NEVER +- && ! S_ISREG (src_sb.st_mode)) +- )) ++ || (x->data_copy_required ++ && ((x->preserve_links && 1 < dst_sb.st_nlink) ++ || (x->dereference == DEREF_NEVER ++ && ! S_ISREG (src_sb.st_mode)))) ++ )) + { + if (unlink (dst_name) != 0 && errno != ENOENT) + { +diff --git a/tests/cp/attr-existing.sh b/tests/cp/attr-existing.sh +index 59ce641..14fc844 100755 +--- a/tests/cp/attr-existing.sh ++++ b/tests/cp/attr-existing.sh +@@ -19,11 +19,26 @@ + . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src + print_ver_ cp + +-printf '1' > file1 +-printf '2' > file2 +-printf '2' > file2.exp ++printf '1' > file1 || framework_failure_ ++printf '2' > file2 || framework_failure_ ++printf '2' > file2.exp || framework_failure_ + + cp --attributes-only file1 file2 || fail=1 + cmp file2 file2.exp || fail=1 + ++# coreutils v8.32 and before would remove destination files ++# if hardlinked or the source was not a regular file. ++ln file2 link2 || framework_failure_ ++cp -a --attributes-only file1 file2 || fail=1 ++cmp file2 file2.exp || fail=1 ++ ++ln -s file1 sym1 || framework_failure_ ++returns_ 1 cp -a --attributes-only sym1 file2 || fail=1 ++cmp file2 file2.exp || fail=1 ++ ++# One can still force removal though ++cp -a --remove-destination --attributes-only sym1 file2 || fail=1 ++test -L file2 || fail=1 ++cmp file1 file2 || fail=1 ++ + Exit $fail +-- +2.26.3 + + +From c728747b06e71894c96d1f27434f2484af992c75 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Tue, 23 Jun 2020 19:18:04 -0700 +Subject: [PATCH 02/12] cp: refactor extent_copy + +* src/copy.c (extent_copy): New arg SCAN, replacing +REQUIRE_NORMAL_COPY. All callers changed. +(enum scantype): New type. +(infer_scantype): Rename from is_probably_sparse and return +the new type. Add args FD and SCAN. All callers changed. + +Upstream-commit: 761ba28400a04ee24eefe9cd4973ec8850cd7a52 +Signed-off-by: Kamil Dudka +--- + src/copy.c | 119 +++++++++++++++++++++++++---------------------------- + 1 file changed, 55 insertions(+), 64 deletions(-) + +diff --git a/src/copy.c b/src/copy.c +index 54601ce..f694f91 100644 +--- a/src/copy.c ++++ b/src/copy.c +@@ -422,9 +422,8 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, + size_t hole_size, off_t src_total_size, + enum Sparse_type sparse_mode, + char const *src_name, char const *dst_name, +- bool *require_normal_copy) ++ struct extent_scan *scan) + { +- struct extent_scan scan; + off_t last_ext_start = 0; + off_t last_ext_len = 0; + +@@ -432,45 +431,25 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, + We may need this at the end, for a final ftruncate. */ + off_t dest_pos = 0; + +- extent_scan_init (src_fd, &scan); +- +- *require_normal_copy = false; + bool wrote_hole_at_eof = true; +- do ++ while (true) + { +- bool ok = extent_scan_read (&scan); +- if (! ok) +- { +- if (scan.hit_final_extent) +- break; +- +- if (scan.initial_scan_failed) +- { +- *require_normal_copy = true; +- return false; +- } +- +- error (0, errno, _("%s: failed to get extents info"), +- quotef (src_name)); +- return false; +- } +- + bool empty_extent = false; +- for (unsigned int i = 0; i < scan.ei_count || empty_extent; i++) ++ for (unsigned int i = 0; i < scan->ei_count || empty_extent; i++) + { + off_t ext_start; + off_t ext_len; + off_t ext_hole_size; + +- if (i < scan.ei_count) ++ if (i < scan->ei_count) + { +- ext_start = scan.ext_info[i].ext_logical; +- ext_len = scan.ext_info[i].ext_length; ++ ext_start = scan->ext_info[i].ext_logical; ++ ext_len = scan->ext_info[i].ext_length; + } + else /* empty extent at EOF. */ + { + i--; +- ext_start = last_ext_start + scan.ext_info[i].ext_length; ++ ext_start = last_ext_start + scan->ext_info[i].ext_length; + ext_len = 0; + } + +@@ -498,7 +477,7 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, + { + error (0, errno, _("cannot lseek %s"), quoteaf (src_name)); + fail: +- extent_scan_free (&scan); ++ extent_scan_free (scan); + return false; + } + +@@ -539,7 +518,7 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, + /* For now, do not treat FIEMAP_EXTENT_UNWRITTEN specially, + because that (in combination with no sync) would lead to data + loss at least on XFS and ext4 when using 2.6.39-rc3 kernels. */ +- if (0 && (scan.ext_info[i].ext_flags & FIEMAP_EXTENT_UNWRITTEN)) ++ if (0 && (scan->ext_info[i].ext_flags & FIEMAP_EXTENT_UNWRITTEN)) + { + empty_extent = true; + last_ext_len = 0; +@@ -571,16 +550,23 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, + extents beyond the apparent size. */ + if (dest_pos == src_total_size) + { +- scan.hit_final_extent = true; ++ scan->hit_final_extent = true; + break; + } + } + + /* Release the space allocated to scan->ext_info. */ +- extent_scan_free (&scan); ++ extent_scan_free (scan); + ++ if (scan->hit_final_extent) ++ break; ++ if (! extent_scan_read (scan) && ! scan->hit_final_extent) ++ { ++ error (0, errno, _("%s: failed to get extents info"), ++ quotef (src_name)); ++ return false; ++ } + } +- while (! scan.hit_final_extent); + + /* When the source file ends with a hole, we have to do a little more work, + since the above copied only up to and including the final extent. +@@ -1021,16 +1007,35 @@ fchmod_or_lchmod (int desc, char const *name, mode_t mode) + # define HAVE_STRUCT_STAT_ST_BLOCKS 0 + #endif + ++/* Type of scan being done on the input when looking for sparseness. */ ++enum scantype ++ { ++ /* No fancy scanning; just read and write. */ ++ PLAIN_SCANTYPE, ++ ++ /* Read and examine data looking for zero blocks; useful when ++ attempting to create sparse output. */ ++ ZERO_SCANTYPE, ++ ++ /* Extent information is available. */ ++ EXTENT_SCANTYPE ++ }; ++ + /* Use a heuristic to determine whether stat buffer SB comes from a file + with sparse blocks. If the file has fewer blocks than would normally + be needed for a file of its size, then at least one of the blocks in + the file is a hole. In that case, return true. */ +-static bool +-is_probably_sparse (struct stat const *sb) ++static enum scantype ++infer_scantype (int fd, struct stat const *sb, struct extent_scan *scan) + { +- return (HAVE_STRUCT_STAT_ST_BLOCKS +- && S_ISREG (sb->st_mode) +- && ST_NBLOCKS (*sb) < sb->st_size / ST_NBLOCKSIZE); ++ if (! (HAVE_STRUCT_STAT_ST_BLOCKS ++ && S_ISREG (sb->st_mode) ++ && ST_NBLOCKS (*sb) < sb->st_size / ST_NBLOCKSIZE)) ++ return PLAIN_SCANTYPE; ++ ++ extent_scan_init (fd, scan); ++ extent_scan_read (scan); ++ return scan->initial_scan_failed ? ZERO_SCANTYPE : EXTENT_SCANTYPE; + } + + +@@ -1061,6 +1066,7 @@ copy_reg (char const *src_name, char const *dst_name, + mode_t src_mode = src_sb->st_mode; + struct stat sb; + struct stat src_open_sb; ++ struct extent_scan scan; + bool return_val = true; + bool data_copy_required = x->data_copy_required; + +@@ -1260,23 +1266,13 @@ copy_reg (char const *src_name, char const *dst_name, + fdadvise (source_desc, 0, 0, FADVISE_SEQUENTIAL); + + /* Deal with sparse files. */ +- bool make_holes = false; +- bool sparse_src = is_probably_sparse (&src_open_sb); +- +- if (S_ISREG (sb.st_mode)) +- { +- /* Even with --sparse=always, try to create holes only +- if the destination is a regular file. */ +- if (x->sparse_mode == SPARSE_ALWAYS) +- make_holes = true; +- +- /* Use a heuristic to determine whether SRC_NAME contains any sparse +- blocks. If the file has fewer blocks than would normally be +- needed for a file of its size, then at least one of the blocks in +- the file is a hole. */ +- if (x->sparse_mode == SPARSE_AUTO && sparse_src) +- make_holes = true; +- } ++ enum scantype scantype = infer_scantype (source_desc, &src_open_sb, ++ &scan); ++ bool make_holes ++ = (S_ISREG (sb.st_mode) ++ && (x->sparse_mode == SPARSE_ALWAYS ++ || (x->sparse_mode == SPARSE_AUTO ++ && scantype != PLAIN_SCANTYPE))); + + /* If not making a sparse file, try to use a more-efficient + buffer size. */ +@@ -1305,10 +1301,8 @@ copy_reg (char const *src_name, char const *dst_name, + buf_alloc = xmalloc (buf_size + buf_alignment); + buf = ptr_align (buf_alloc, buf_alignment); + +- if (sparse_src) ++ if (scantype == EXTENT_SCANTYPE) + { +- bool normal_copy_required; +- + /* Perform an efficient extent-based copy, falling back to the + standard copy only if the initial extent scan fails. If the + '--sparse=never' option is specified, write all data but use +@@ -1316,14 +1310,11 @@ copy_reg (char const *src_name, char const *dst_name, + if (extent_copy (source_desc, dest_desc, buf, buf_size, hole_size, + src_open_sb.st_size, + make_holes ? x->sparse_mode : SPARSE_NEVER, +- src_name, dst_name, &normal_copy_required)) ++ src_name, dst_name, &scan)) + goto preserve_metadata; + +- if (! normal_copy_required) +- { +- return_val = false; +- goto close_src_and_dst_desc; +- } ++ return_val = false; ++ goto close_src_and_dst_desc; + } + + off_t n_read; +-- +2.26.3 + + +From ed7ff81de507bef46991f4caac550f41ab65e3ed Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Wed, 24 Jun 2020 17:05:20 -0700 +Subject: [PATCH 03/12] cp: avoid copy_reg goto + +* src/copy.c (copy_reg): Redo to avoid label and goto. + +Upstream-commit: 2fcd0f3328f5181a2986905fa5469a0152c67279 +Signed-off-by: Kamil Dudka +--- + src/copy.c | 34 +++++++++++----------------------- + 1 file changed, 11 insertions(+), 23 deletions(-) + +diff --git a/src/copy.c b/src/copy.c +index f694f91..b382cfa 100644 +--- a/src/copy.c ++++ b/src/copy.c +@@ -1301,29 +1301,18 @@ copy_reg (char const *src_name, char const *dst_name, + buf_alloc = xmalloc (buf_size + buf_alignment); + buf = ptr_align (buf_alloc, buf_alignment); + +- if (scantype == EXTENT_SCANTYPE) +- { +- /* Perform an efficient extent-based copy, falling back to the +- standard copy only if the initial extent scan fails. If the +- '--sparse=never' option is specified, write all data but use +- any extents to read more efficiently. */ +- if (extent_copy (source_desc, dest_desc, buf, buf_size, hole_size, +- src_open_sb.st_size, +- make_holes ? x->sparse_mode : SPARSE_NEVER, +- src_name, dst_name, &scan)) +- goto preserve_metadata; +- +- return_val = false; +- goto close_src_and_dst_desc; +- } +- + off_t n_read; +- bool wrote_hole_at_eof; +- if (! sparse_copy (source_desc, dest_desc, buf, buf_size, +- make_holes ? hole_size : 0, +- x->sparse_mode == SPARSE_ALWAYS, src_name, dst_name, +- UINTMAX_MAX, &n_read, +- &wrote_hole_at_eof)) ++ bool wrote_hole_at_eof = false; ++ if (! (scantype == EXTENT_SCANTYPE ++ ? extent_copy (source_desc, dest_desc, buf, buf_size, hole_size, ++ src_open_sb.st_size, ++ make_holes ? x->sparse_mode : SPARSE_NEVER, ++ src_name, dst_name, &scan) ++ : sparse_copy (source_desc, dest_desc, buf, buf_size, ++ make_holes ? hole_size : 0, ++ x->sparse_mode == SPARSE_ALWAYS, ++ src_name, dst_name, UINTMAX_MAX, &n_read, ++ &wrote_hole_at_eof))) + { + return_val = false; + goto close_src_and_dst_desc; +@@ -1336,7 +1325,6 @@ copy_reg (char const *src_name, char const *dst_name, + } + } + +-preserve_metadata: + if (x->preserve_timestamps) + { + struct timespec timespec[2]; +-- +2.26.3 + + +From 5631bded3a385ca0bbd77456b50767fe5580240c Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Thu, 25 Jun 2020 16:31:44 -0700 +Subject: [PATCH 04/12] cp: use SEEK_DATA/SEEK_HOLE if available + +If it works, prefer lseek with SEEK_DATA and SEEK_HOLE to FIEMAP, +as lseek is simpler and more portable (will be in next POSIX). +Problem reported in 2011 by Jeff Liu (Bug#8061). +* NEWS: Mention this. +* src/copy.c (lseek_copy) [SEEK_HOLE]: New function. +(enum scantype): New constants ERROR_SCANTYPE, LSEEK_SCANTYPE. +(union scan_inference): New type. +(infer_scantype): Last arg is now union scan_inference *, +not struct extent_scan *. All callers changed. +Prefer SEEK_HOLE to FIEMAP if both work, since +SEEK_HOLE is simpler and more portable. +(copy_reg): Do the fdadvise after initial scan, in case the scan +fails. Report an error if the initial scan fails. +(copy_reg) [SEEK_HOLE]: Use lseek_copy if scantype says so. + +Upstream-commit: a6eaee501f6ec0c152abe88640203a64c390993e +Signed-off-by: Kamil Dudka +--- + src/copy.c | 209 ++++++++++++++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 198 insertions(+), 11 deletions(-) + +diff --git a/src/copy.c b/src/copy.c +index b382cfa..d88f8cf 100644 +--- a/src/copy.c ++++ b/src/copy.c +@@ -416,7 +416,12 @@ write_zeros (int fd, off_t n_bytes) + Upon a successful copy, return true. If the initial extent scan + fails, set *NORMAL_COPY_REQUIRED to true and return false. + Upon any other failure, set *NORMAL_COPY_REQUIRED to false and +- return false. */ ++ return false. ++ ++ FIXME: Once we no longer need to support Linux kernel versions ++ before 3.1 (2011), this function can be retired as it is superseded ++ by lseek_copy. That is, we no longer need extent-scan.h and can ++ remove any of the code that uses it. */ + static bool + extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, + size_t hole_size, off_t src_total_size, +@@ -595,6 +600,150 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, + return true; + } + ++#ifdef SEEK_HOLE ++/* Perform an efficient extent copy, if possible. This avoids ++ the overhead of detecting holes in hole-introducing/preserving ++ copy, and thus makes copying sparse files much more efficient. ++ Copy from SRC_FD to DEST_FD, using BUF (of size BUF_SIZE) for a buffer. ++ Look for holes of size HOLE_SIZE in the input. ++ The input file is of size SRC_TOTAL_SIZE. ++ Use SPARSE_MODE to determine whether to create holes in the output. ++ SRC_NAME and DST_NAME are the input and output file names. ++ Return true if successful, false (with a diagnostic) otherwise. */ ++ ++static bool ++lseek_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, ++ size_t hole_size, off_t ext_start, off_t src_total_size, ++ enum Sparse_type sparse_mode, ++ char const *src_name, char const *dst_name) ++{ ++ off_t last_ext_start = 0; ++ off_t last_ext_len = 0; ++ off_t dest_pos = 0; ++ bool wrote_hole_at_eof = true; ++ ++ while (0 <= ext_start) ++ { ++ off_t ext_end = lseek (src_fd, ext_start, SEEK_HOLE); ++ if (ext_end < 0) ++ { ++ if (errno != ENXIO) ++ goto cannot_lseek; ++ ext_end = src_total_size; ++ if (ext_end <= ext_start) ++ { ++ /* The input file grew; get its current size. */ ++ src_total_size = lseek (src_fd, 0, SEEK_END); ++ if (src_total_size < 0) ++ goto cannot_lseek; ++ ++ /* If the input file shrank after growing, stop copying. */ ++ if (src_total_size <= ext_start) ++ break; ++ ++ ext_end = src_total_size; ++ } ++ } ++ /* If the input file must have grown, increase its measured size. */ ++ if (src_total_size < ext_end) ++ src_total_size = ext_end; ++ ++ if (lseek (src_fd, ext_start, SEEK_SET) < 0) ++ goto cannot_lseek; ++ ++ wrote_hole_at_eof = false; ++ off_t ext_hole_size = ext_start - last_ext_start - last_ext_len; ++ ++ if (ext_hole_size) ++ { ++ if (sparse_mode != SPARSE_NEVER) ++ { ++ if (! create_hole (dest_fd, dst_name, ++ sparse_mode == SPARSE_ALWAYS, ++ ext_hole_size)) ++ return false; ++ wrote_hole_at_eof = true; ++ } ++ else ++ { ++ /* When not inducing holes and when there is a hole between ++ the end of the previous extent and the beginning of the ++ current one, write zeros to the destination file. */ ++ if (! write_zeros (dest_fd, ext_hole_size)) ++ { ++ error (0, errno, _("%s: write failed"), ++ quotef (dst_name)); ++ return false; ++ } ++ } ++ } ++ ++ off_t ext_len = ext_end - ext_start; ++ last_ext_start = ext_start; ++ last_ext_len = ext_len; ++ ++ /* Copy this extent, looking for further opportunities to not ++ bother to write zeros unless --sparse=never, since SEEK_HOLE ++ is conservative and may miss some holes. */ ++ off_t n_read; ++ bool read_hole; ++ if ( ! sparse_copy (src_fd, dest_fd, buf, buf_size, ++ sparse_mode == SPARSE_NEVER ? 0 : hole_size, ++ true, src_name, dst_name, ext_len, &n_read, ++ &read_hole)) ++ return false; ++ ++ dest_pos = ext_start + n_read; ++ if (n_read) ++ wrote_hole_at_eof = read_hole; ++ if (n_read < ext_len) ++ { ++ /* The input file shrank. */ ++ src_total_size = dest_pos; ++ break; ++ } ++ ++ ext_start = lseek (src_fd, dest_pos, SEEK_DATA); ++ if (ext_start < 0) ++ { ++ if (errno != ENXIO) ++ goto cannot_lseek; ++ break; ++ } ++ } ++ ++ /* When the source file ends with a hole, we have to do a little more work, ++ since the above copied only up to and including the final extent. ++ In order to complete the copy, we may have to insert a hole or write ++ zeros in the destination corresponding to the source file's hole-at-EOF. ++ ++ In addition, if the final extent was a block of zeros at EOF and we've ++ just converted them to a hole in the destination, we must call ftruncate ++ here in order to record the proper length in the destination. */ ++ if ((dest_pos < src_total_size || wrote_hole_at_eof) ++ && ! (sparse_mode == SPARSE_NEVER ++ ? write_zeros (dest_fd, src_total_size - dest_pos) ++ : ftruncate (dest_fd, src_total_size) == 0)) ++ { ++ error (0, errno, _("failed to extend %s"), quoteaf (dst_name)); ++ return false; ++ } ++ ++ if (sparse_mode == SPARSE_ALWAYS && dest_pos < src_total_size ++ && punch_hole (dest_fd, dest_pos, src_total_size - dest_pos) < 0) ++ { ++ error (0, errno, _("error deallocating %s"), quoteaf (dst_name)); ++ return false; ++ } ++ ++ return true; ++ ++ cannot_lseek: ++ error (0, errno, _("cannot lseek %s"), quoteaf (src_name)); ++ return false; ++} ++#endif ++ + /* FIXME: describe */ + /* FIXME: rewrite this to use a hash table so we avoid the quadratic + performance hit that's probably noticeable only on trees deeper +@@ -1010,6 +1159,9 @@ fchmod_or_lchmod (int desc, char const *name, mode_t mode) + /* Type of scan being done on the input when looking for sparseness. */ + enum scantype + { ++ /* An error was found when determining scantype. */ ++ ERROR_SCANTYPE, ++ + /* No fancy scanning; just read and write. */ + PLAIN_SCANTYPE, + +@@ -1017,22 +1169,44 @@ enum scantype + attempting to create sparse output. */ + ZERO_SCANTYPE, + ++ /* lseek information is available. */ ++ LSEEK_SCANTYPE, ++ + /* Extent information is available. */ + EXTENT_SCANTYPE + }; + +-/* Use a heuristic to determine whether stat buffer SB comes from a file +- with sparse blocks. If the file has fewer blocks than would normally +- be needed for a file of its size, then at least one of the blocks in +- the file is a hole. In that case, return true. */ ++/* Result of infer_scantype. */ ++union scan_inference ++{ ++ /* Used if infer_scantype returns LSEEK_SCANTYPE. This is the ++ offset of the first data block, or -1 if the file has no data. */ ++ off_t ext_start; ++ ++ /* Used if infer_scantype returns EXTENT_SCANTYPE. */ ++ struct extent_scan extent_scan; ++}; ++ ++/* Return how to scan a file with descriptor FD and stat buffer SB. ++ Store any information gathered into *SCAN. */ + static enum scantype +-infer_scantype (int fd, struct stat const *sb, struct extent_scan *scan) ++infer_scantype (int fd, struct stat const *sb, ++ union scan_inference *scan_inference) + { + if (! (HAVE_STRUCT_STAT_ST_BLOCKS + && S_ISREG (sb->st_mode) + && ST_NBLOCKS (*sb) < sb->st_size / ST_NBLOCKSIZE)) + return PLAIN_SCANTYPE; + ++#ifdef SEEK_HOLE ++ scan_inference->ext_start = lseek (fd, 0, SEEK_DATA); ++ if (0 <= scan_inference->ext_start) ++ return LSEEK_SCANTYPE; ++ else if (errno != EINVAL && errno != ENOTSUP) ++ return errno == ENXIO ? LSEEK_SCANTYPE : ERROR_SCANTYPE; ++#endif ++ ++ struct extent_scan *scan = &scan_inference->extent_scan; + extent_scan_init (fd, scan); + extent_scan_read (scan); + return scan->initial_scan_failed ? ZERO_SCANTYPE : EXTENT_SCANTYPE; +@@ -1066,7 +1240,7 @@ copy_reg (char const *src_name, char const *dst_name, + mode_t src_mode = src_sb->st_mode; + struct stat sb; + struct stat src_open_sb; +- struct extent_scan scan; ++ union scan_inference scan_inference; + bool return_val = true; + bool data_copy_required = x->data_copy_required; + +@@ -1263,17 +1437,23 @@ copy_reg (char const *src_name, char const *dst_name, + size_t buf_size = io_blksize (sb); + size_t hole_size = ST_BLKSIZE (sb); + +- fdadvise (source_desc, 0, 0, FADVISE_SEQUENTIAL); +- + /* Deal with sparse files. */ + enum scantype scantype = infer_scantype (source_desc, &src_open_sb, +- &scan); ++ &scan_inference); ++ if (scantype == ERROR_SCANTYPE) ++ { ++ error (0, errno, _("cannot lseek %s"), quoteaf (src_name)); ++ return_val = false; ++ goto close_src_and_dst_desc; ++ } + bool make_holes + = (S_ISREG (sb.st_mode) + && (x->sparse_mode == SPARSE_ALWAYS + || (x->sparse_mode == SPARSE_AUTO + && scantype != PLAIN_SCANTYPE))); + ++ fdadvise (source_desc, 0, 0, FADVISE_SEQUENTIAL); ++ + /* If not making a sparse file, try to use a more-efficient + buffer size. */ + if (! make_holes) +@@ -1307,7 +1487,14 @@ copy_reg (char const *src_name, char const *dst_name, + ? extent_copy (source_desc, dest_desc, buf, buf_size, hole_size, + src_open_sb.st_size, + make_holes ? x->sparse_mode : SPARSE_NEVER, +- src_name, dst_name, &scan) ++ src_name, dst_name, &scan_inference.extent_scan) ++#ifdef SEEK_HOLE ++ : scantype == LSEEK_SCANTYPE ++ ? lseek_copy (source_desc, dest_desc, buf, buf_size, hole_size, ++ scan_inference.ext_start, src_open_sb.st_size, ++ make_holes ? x->sparse_mode : SPARSE_NEVER, ++ src_name, dst_name) ++#endif + : sparse_copy (source_desc, dest_desc, buf, buf_size, + make_holes ? hole_size : 0, + x->sparse_mode == SPARSE_ALWAYS, +-- +2.26.3 + + +From be7466be92d779cfbece418d4de33191ae52ab4a Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +Date: Wed, 24 Mar 2021 16:06:53 +0100 +Subject: [PATCH 05/12] import the copy-file-range module from gnulib + +--- + aclocal.m4 | 1 + + lib/config.hin | 3 +++ + lib/copy-file-range.c | 33 +++++++++++++++++++++++++++++++++ + lib/gnulib.mk | 10 ++++++++++ + m4/copy-file-range.m4 | 36 ++++++++++++++++++++++++++++++++++++ + m4/gnulib-comp.m4 | 8 ++++++++ + 6 files changed, 91 insertions(+) + create mode 100644 lib/copy-file-range.c + create mode 100644 m4/copy-file-range.m4 + +diff --git a/aclocal.m4 b/aclocal.m4 +index 713f7c5..09a7ea8 100644 +--- a/aclocal.m4 ++++ b/aclocal.m4 +@@ -1165,6 +1165,7 @@ m4_include([m4/closedir.m4]) + m4_include([m4/codeset.m4]) + m4_include([m4/config-h.m4]) + m4_include([m4/configmake.m4]) ++m4_include([m4/copy-file-range.m4]) + m4_include([m4/ctype.m4]) + m4_include([m4/cycle-check.m4]) + m4_include([m4/d-ino.m4]) +diff --git a/lib/config.hin b/lib/config.hin +index 9769c39..bf9f9f8 100644 +--- a/lib/config.hin ++++ b/lib/config.hin +@@ -370,6 +370,9 @@ + /* Define to 1 when the gnulib module connect should be tested. */ + #undef GNULIB_TEST_CONNECT + ++/* Define to 1 when the gnulib module copy-file-range should be tested. */ ++#undef GNULIB_TEST_COPY_FILE_RANGE ++ + /* Define to 1 when the gnulib module dirfd should be tested. */ + #undef GNULIB_TEST_DIRFD + +diff --git a/lib/copy-file-range.c b/lib/copy-file-range.c +new file mode 100644 +index 0000000..069f144 +--- /dev/null ++++ b/lib/copy-file-range.c +@@ -0,0 +1,33 @@ ++/* Stub for copy_file_range ++ Copyright 2019-2020 Free Software Foundation, Inc. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++#include ++ ++#include ++ ++#include ++ ++ssize_t ++copy_file_range (int infd, off_t *pinoff, ++ int outfd, off_t *poutoff, ++ size_t length, unsigned int flags) ++{ ++ /* There is little need to emulate copy_file_range with read+write, ++ since programs that use copy_file_range must fall back on ++ read+write anyway. */ ++ errno = ENOSYS; ++ return -1; ++} +diff --git a/lib/gnulib.mk b/lib/gnulib.mk +index b3633b8..86829f3 100644 +--- a/lib/gnulib.mk ++++ b/lib/gnulib.mk +@@ -65,6 +65,7 @@ + # closeout \ + # config-h \ + # configmake \ ++# copy-file-range \ + # crypto/md5 \ + # crypto/sha1 \ + # crypto/sha256 \ +@@ -800,6 +801,15 @@ CLEANFILES += lib/configmake.h lib/configmake.h-t + + ## end gnulib module configmake + ++## begin gnulib module copy-file-range ++ ++ ++EXTRA_DIST += lib/copy-file-range.c ++ ++EXTRA_lib_libcoreutils_a_SOURCES += lib/copy-file-range.c ++ ++## end gnulib module copy-file-range ++ + ## begin gnulib module count-leading-zeros + + lib_libcoreutils_a_SOURCES += lib/count-leading-zeros.c +diff --git a/m4/copy-file-range.m4 b/m4/copy-file-range.m4 +new file mode 100644 +index 0000000..5c5a274 +--- /dev/null ++++ b/m4/copy-file-range.m4 +@@ -0,0 +1,36 @@ ++# copy-file-range.m4 ++dnl Copyright 2019-2020 Free Software Foundation, Inc. ++dnl This file is free software; the Free Software Foundation ++dnl gives unlimited permission to copy and/or distribute it, ++dnl with or without modifications, as long as this notice is preserved. ++ ++AC_DEFUN([gl_FUNC_COPY_FILE_RANGE], ++[ ++ AC_REQUIRE([gl_UNISTD_H_DEFAULTS]) ++ ++ dnl Persuade glibc to declare copy_file_range. ++ AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) ++ ++ dnl Use AC_LINK_IFELSE, rather than AC_CHECK_FUNCS or a variant, ++ dnl since we don't want AC_CHECK_FUNCS's checks for glibc stubs. ++ dnl Programs that use copy_file_range must fall back on read+write ++ dnl anyway, and there's little point to substituting the Gnulib stub ++ dnl for a glibc stub. ++ AC_CACHE_CHECK([for copy_file_range], [gl_cv_func_copy_file_range], ++ [AC_LINK_IFELSE( ++ [AC_LANG_PROGRAM( ++ [[#include ++ ]], ++ [[ssize_t (*func) (int, off_t *, int, off_t, size_t, unsigned) ++ = copy_file_range; ++ return func (0, 0, 0, 0, 0, 0) & 127; ++ ]]) ++ ], ++ [gl_cv_func_copy_file_range=yes], ++ [gl_cv_func_copy_file_range=no]) ++ ]) ++ ++ if test "$gl_cv_func_copy_file_range" != yes; then ++ HAVE_COPY_FILE_RANGE=0 ++ fi ++]) +diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 +index dead90e..953e7f0 100644 +--- a/m4/gnulib-comp.m4 ++++ b/m4/gnulib-comp.m4 +@@ -129,6 +129,7 @@ AC_DEFUN([gl_EARLY], + # Code from module configmake: + # Code from module connect: + # Code from module connect-tests: ++ # Code from module copy-file-range: + # Code from module count-leading-zeros: + # Code from module count-leading-zeros-tests: + # Code from module crypto/af_alg: +@@ -977,6 +978,11 @@ AC_DEFUN([gl_INIT], + gl_DIRENT_MODULE_INDICATOR([closedir]) + gl_CONFIG_H + gl_CONFIGMAKE_PREP ++ gl_FUNC_COPY_FILE_RANGE ++ if test $HAVE_COPY_FILE_RANGE = 0; then ++ AC_LIBOBJ([copy-file-range]) ++ fi ++ gl_UNISTD_MODULE_INDICATOR([copy-file-range]) + gl_AF_ALG + AC_DEFINE([GL_COMPILE_CRYPTO_STREAM], 1, [Compile Gnulib crypto stream ops.]) + AC_REQUIRE([AC_C_RESTRICT]) +@@ -2746,6 +2752,7 @@ AC_DEFUN([gl_FILE_LIST], [ + lib/closeout.c + lib/closeout.h + lib/copy-acl.c ++ lib/copy-file-range.c + lib/count-leading-zeros.c + lib/count-leading-zeros.h + lib/creat-safer.c +@@ -3438,6 +3445,7 @@ AC_DEFUN([gl_FILE_LIST], [ + m4/codeset.m4 + m4/config-h.m4 + m4/configmake.m4 ++ m4/copy-file-range.m4 + m4/ctype.m4 + m4/cycle-check.m4 + m4/d-ino.m4 +-- +2.26.3 + + +From 48370c95bcf7c25ce021fbd2145062d3d29ae6d5 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Thu, 25 Jun 2020 17:34:23 -0700 +Subject: [PATCH 06/12] cp: use copy_file_range if available + +* NEWS: Mention this. +* bootstrap.conf (gnulib_modules): Add copy-file-range. +* src/copy.c (sparse_copy): Try copy_file_range if not +looking for holes. + +Upstream-commit: 4b04a0c3b792d27909670a81d21f2a3b3e0ea563 +Signed-off-by: Kamil Dudka +--- + bootstrap.conf | 1 + + src/copy.c | 40 ++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 41 insertions(+) + +diff --git a/bootstrap.conf b/bootstrap.conf +index 2a342c1..7d53e28 100644 +--- a/bootstrap.conf ++++ b/bootstrap.conf +@@ -54,6 +54,7 @@ gnulib_modules=" + closeout + config-h + configmake ++ copy-file-range + crypto/md5 + crypto/sha1 + crypto/sha256 +diff --git a/src/copy.c b/src/copy.c +index d88f8cf..4050f69 100644 +--- a/src/copy.c ++++ b/src/copy.c +@@ -265,6 +265,46 @@ sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, + { + *last_write_made_hole = false; + *total_n_read = 0; ++ ++ /* If not looking for holes, use copy_file_range if available. */ ++ if (!hole_size) ++ while (max_n_read) ++ { ++ /* Copy at most COPY_MAX bytes at a time; this is min ++ (PTRDIFF_MAX, SIZE_MAX) truncated to a value that is ++ surely aligned well. */ ++ ssize_t ssize_max = TYPE_MAXIMUM (ssize_t); ++ ptrdiff_t copy_max = MIN (ssize_max, SIZE_MAX) >> 30 << 30; ++ ssize_t n_copied = copy_file_range (src_fd, NULL, dest_fd, NULL, ++ MIN (max_n_read, copy_max), 0); ++ if (n_copied == 0) ++ { ++ /* copy_file_range incorrectly returns 0 when reading from ++ the proc file system on the Linux kernel through at ++ least 5.6.19 (2020), so fall back on 'read' if the ++ input file seems empty. */ ++ if (*total_n_read == 0) ++ break; ++ return true; ++ } ++ if (n_copied < 0) ++ { ++ if (errno == ENOSYS || errno == EINVAL ++ || errno == EBADF || errno == EXDEV) ++ break; ++ if (errno == EINTR) ++ n_copied = 0; ++ else ++ { ++ error (0, errno, _("error copying %s to %s"), ++ quoteaf_n (0, src_name), quoteaf_n (1, dst_name)); ++ return false; ++ } ++ } ++ max_n_read -= n_copied; ++ *total_n_read += n_copied; ++ } ++ + bool make_hole = false; + off_t psize = 0; + +-- +2.26.3 + + +From 23ea1ba463d33e268f35847059e637a5935e4581 Mon Sep 17 00:00:00 2001 +From: Zorro Lang +Date: Mon, 26 Apr 2021 17:25:18 +0200 +Subject: [PATCH 07/12] copy: do not refuse to copy a swap file + +* src/copy.c (sparse_copy): Fallback to read() if copy_file_range() +fails with ETXTBSY. Otherwise it would be impossible to copy files +that are being used as swap. This used to work before introducing +the support for copy_file_range() in coreutils. (Bug#48036) + +Upstream-commit: 785478013b416cde50794be35475c0c4fdbb48b4 +Signed-off-by: Kamil Dudka +--- + src/copy.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/copy.c b/src/copy.c +index 4050f69..1798bb7 100644 +--- a/src/copy.c ++++ b/src/copy.c +@@ -290,7 +290,7 @@ sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, + if (n_copied < 0) + { + if (errno == ENOSYS || errno == EINVAL +- || errno == EBADF || errno == EXDEV) ++ || errno == EBADF || errno == EXDEV || errno == ETXTBSY) + break; + if (errno == EINTR) + n_copied = 0; +-- +2.31.1 + + +From cd7c7a6b5ad89ef0a61722552d532901fc1bed05 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Sun, 2 May 2021 21:27:17 +0100 +Subject: [PATCH 08/12] copy: ensure we enforce --reflink=never + +* src/copy.c (sparse_copy): Don't use copy_file_range() +with --reflink=never as copy_file_range() may implicitly +use acceleration techniques like reflinking. +(extent_copy): Pass through whether we allow reflinking. +(lseek_copy): Likewise. +Fixes https://bugs.gnu.org/48164 + +Upstream-commit: ea9af99234031ab8d5169c8a669434e2a6b4f864 +Signed-off-by: Kamil Dudka +--- + src/copy.c | 20 +++++++++++++------- + 1 file changed, 13 insertions(+), 7 deletions(-) + +diff --git a/src/copy.c b/src/copy.c +index 4050f69..0337538 100644 +--- a/src/copy.c ++++ b/src/copy.c +@@ -258,7 +258,7 @@ create_hole (int fd, char const *name, bool punch_holes, off_t size) + bytes read. */ + static bool + sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, +- size_t hole_size, bool punch_holes, ++ size_t hole_size, bool punch_holes, bool allow_reflink, + char const *src_name, char const *dst_name, + uintmax_t max_n_read, off_t *total_n_read, + bool *last_write_made_hole) +@@ -266,8 +266,9 @@ sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, + *last_write_made_hole = false; + *total_n_read = 0; + +- /* If not looking for holes, use copy_file_range if available. */ +- if (!hole_size) ++ /* If not looking for holes, use copy_file_range if available, ++ but don't use if reflink disallowed as that may be implicit. */ ++ if ((! hole_size) && allow_reflink) + while (max_n_read) + { + /* Copy at most COPY_MAX bytes at a time; this is min +@@ -466,6 +467,7 @@ static bool + extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, + size_t hole_size, off_t src_total_size, + enum Sparse_type sparse_mode, ++ bool allow_reflink, + char const *src_name, char const *dst_name, + struct extent_scan *scan) + { +@@ -579,8 +581,8 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, + + if ( ! sparse_copy (src_fd, dest_fd, buf, buf_size, + sparse_mode == SPARSE_ALWAYS ? hole_size: 0, +- true, src_name, dst_name, ext_len, &n_read, +- &read_hole)) ++ true, allow_reflink, src_name, dst_name, ++ ext_len, &n_read, &read_hole)) + goto fail; + + dest_pos = ext_start + n_read; +@@ -655,6 +657,7 @@ static bool + lseek_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, + size_t hole_size, off_t ext_start, off_t src_total_size, + enum Sparse_type sparse_mode, ++ bool allow_reflink, + char const *src_name, char const *dst_name) + { + off_t last_ext_start = 0; +@@ -729,8 +732,8 @@ lseek_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, + bool read_hole; + if ( ! sparse_copy (src_fd, dest_fd, buf, buf_size, + sparse_mode == SPARSE_NEVER ? 0 : hole_size, +- true, src_name, dst_name, ext_len, &n_read, +- &read_hole)) ++ true, allow_reflink, src_name, dst_name, ++ ext_len, &n_read, &read_hole)) + return false; + + dest_pos = ext_start + n_read; +@@ -1527,17 +1530,20 @@ copy_reg (char const *src_name, char const *dst_name, + ? extent_copy (source_desc, dest_desc, buf, buf_size, hole_size, + src_open_sb.st_size, + make_holes ? x->sparse_mode : SPARSE_NEVER, ++ x->reflink_mode != REFLINK_NEVER, + src_name, dst_name, &scan_inference.extent_scan) + #ifdef SEEK_HOLE + : scantype == LSEEK_SCANTYPE + ? lseek_copy (source_desc, dest_desc, buf, buf_size, hole_size, + scan_inference.ext_start, src_open_sb.st_size, + make_holes ? x->sparse_mode : SPARSE_NEVER, ++ x->reflink_mode != REFLINK_NEVER, + src_name, dst_name) + #endif + : sparse_copy (source_desc, dest_desc, buf, buf_size, + make_holes ? hole_size : 0, + x->sparse_mode == SPARSE_ALWAYS, ++ x->reflink_mode != REFLINK_NEVER, + src_name, dst_name, UINTMAX_MAX, &n_read, + &wrote_hole_at_eof))) + { +-- +2.30.2 + + +From 7978f1de88dcdb17b67db9268038930e9c71154f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Sat, 8 May 2021 17:18:54 +0100 +Subject: [PATCH 09/12] copy: handle ENOTSUP from copy_file_range() + +* src/copy.c (sparse_copy): Ensure we fall back to +a standard copy if copy_file_range() returns ENOTSUP. +This generally is best checked when checking ENOSYS, +but it also seems to be a practical concern on Centos 7, +as a quick search gave https://bugzilla.redhat.com/1840284 + +Upstream-commit: 8ec0d1799e19a079b8a661c6bb69f6c58e52f1aa +Signed-off-by: Kamil Dudka +--- + src/copy.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/copy.c b/src/copy.c +index 9977193..e3977cd 100644 +--- a/src/copy.c ++++ b/src/copy.c +@@ -290,8 +290,9 @@ sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, + } + if (n_copied < 0) + { +- if (errno == ENOSYS || errno == EINVAL +- || errno == EBADF || errno == EXDEV || errno == ETXTBSY) ++ if (errno == ENOSYS || is_ENOTSUP (errno) ++ || errno == EINVAL || errno == EBADF ++ || errno == EXDEV || errno == ETXTBSY) + break; + if (errno == EINTR) + n_copied = 0; +-- +2.31.1 + + +From d8d3edbfc13ff13c185f23436209b788f906aa41 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Sun, 9 May 2021 21:55:22 +0100 +Subject: [PATCH 10/12] copy: handle EOPNOTSUPP from SEEK_DATA + +* src/copy.c (infer_scantype): Ensure we don't error out +if SEEK_DATA returns EOPNOTSUPP, on systems where this value +is distinct from ENOTSUP. Generally both of these should be checked. + +Upstream-commit: 017877bd088284d515753d78b81ca6e6a88c1350 +Signed-off-by: Kamil Dudka +--- + src/copy.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/copy.c b/src/copy.c +index e3977cd..de8030d 100644 +--- a/src/copy.c ++++ b/src/copy.c +@@ -1246,7 +1246,7 @@ infer_scantype (int fd, struct stat const *sb, + scan_inference->ext_start = lseek (fd, 0, SEEK_DATA); + if (0 <= scan_inference->ext_start) + return LSEEK_SCANTYPE; +- else if (errno != EINVAL && errno != ENOTSUP) ++ else if (errno != EINVAL && !is_ENOTSUP (errno)) + return errno == ENXIO ? LSEEK_SCANTYPE : ERROR_SCANTYPE; + #endif + +-- +2.31.1 + + +From 1daf8c0fc9a5766c22b7ea84bea8c88c86a0c495 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Sat, 8 May 2021 19:23:20 +0100 +Subject: [PATCH 11/12] copy: handle system security config issues with + copy_file_range() + +* src/copy.c (sparse_copy): Upon EPERM from copy_file_range(), +fall back to a standard copy, which will give a more accurate +error as to whether the issue is with the source or destination. +Also this will avoid the issue where seccomp or apparmor are +not configured to handle copy_file_range(), in which case +the fall back standard copy would succeed without issue. +This specific issue with seccomp was noticed for example in: +https://github.com/golang/go/issues/40900 + +Upstream-commit: 2e66e1732fced7af20fa76c60e636d39a1767d48 +Signed-off-by: Kamil Dudka +--- + src/copy.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/src/copy.c b/src/copy.c +index de8030d..62eec7b 100644 +--- a/src/copy.c ++++ b/src/copy.c +@@ -294,6 +294,15 @@ sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, + || errno == EINVAL || errno == EBADF + || errno == EXDEV || errno == ETXTBSY) + break; ++ ++ /* copy_file_range might not be enabled in seccomp filters, ++ so retry with a standard copy. EPERM can also occur ++ for immutable files, but that would only be in the edge case ++ where the file is made immutable after creating/truncating, ++ in which case the (more accurate) error is still shown. */ ++ if (errno == EPERM && *total_n_read == 0) ++ break; ++ + if (errno == EINTR) + n_copied = 0; + else +-- +2.31.1 + + +From 42c9e598f61ba6bc27a615e39e40023a676a523b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Wed, 12 May 2021 23:47:38 +0100 +Subject: [PATCH 12/12] copy: disallow copy_file_range() on Linux kernels + before 5.3 + +copy_file_range() before Linux kernel release 5.3 had many issues, +as described at https://lwn.net/Articles/789527/, which was +referenced from https://lwn.net/Articles/846403/; a more general +article discussing the generality of copy_file_range(). +Linux kernel 5.3 was released in September 2019, which is new enough +that we need to actively avoid older kernels. + +* src/copy.c (functional_copy_file_range): A new function +that returns false for Linux kernels before version 5.3. +(sparse_copy): Call this new function to gate use of +copy_file_range(). + +Upstream-commit: ba5e6885d2c255648cddb87b4e795659c1990374 +Signed-off-by: Kamil Dudka +--- + src/copy.c | 47 +++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 45 insertions(+), 2 deletions(-) + +diff --git a/src/copy.c b/src/copy.c +index 62eec7b..2e1699b 100644 +--- a/src/copy.c ++++ b/src/copy.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + #include + + #if HAVE_HURD_H +@@ -64,6 +65,7 @@ + #include "write-any-file.h" + #include "areadlink.h" + #include "yesno.h" ++#include "xstrtol.h" + #include "selinux.h" + + #if USE_XATTR +@@ -244,6 +246,47 @@ create_hole (int fd, char const *name, bool punch_holes, off_t size) + return true; + } + ++/* copy_file_range() before Linux kernel release 5.3 had many issues, ++ as described at https://lwn.net/Articles/789527/, ++ so return FALSE for Linux kernels earlier than that. ++ This function can be removed when such kernels (released before Sep 2019) ++ are no longer a consideration. */ ++ ++static bool ++functional_copy_file_range (void) ++{ ++#ifdef __linux__ ++ static int version_allowed = -1; ++ ++ if (version_allowed == -1) ++ version_allowed = 0; ++ else ++ return version_allowed; ++ ++ struct utsname name; ++ if (uname (&name) == -1) ++ return version_allowed; ++ ++ char *p = name.release; ++ uintmax_t ver[2] = {0, 0}; ++ size_t iver = 0; ++ ++ do ++ { ++ strtol_error err = xstrtoumax (p, &p, 10, &ver[iver], NULL); ++ if (err != LONGINT_OK || *p++ != '.') ++ break; ++ } ++ while (++iver < ARRAY_CARDINALITY (ver)); ++ ++ version_allowed = (ver[0] > 5 || (ver[0] == 5 && ver[1] >= 3)); ++ ++ return version_allowed; ++#else ++ return true; ++#endif ++ ++} + + /* Copy the regular file open on SRC_FD/SRC_NAME to DST_FD/DST_NAME, + honoring the MAKE_HOLES setting and using the BUF_SIZE-byte buffer +@@ -266,9 +309,9 @@ sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, + *last_write_made_hole = false; + *total_n_read = 0; + +- /* If not looking for holes, use copy_file_range if available, ++ /* If not looking for holes, use copy_file_range if functional, + but don't use if reflink disallowed as that may be implicit. */ +- if ((! hole_size) && allow_reflink) ++ if ((! hole_size) && allow_reflink && functional_copy_file_range ()) + while (max_n_read) + { + /* Copy at most COPY_MAX bytes at a time; this is min +-- +2.31.1 + diff --git a/SOURCES/coreutils-8.32-cp-reflink-auto.patch b/SOURCES/coreutils-8.32-cp-reflink-auto.patch new file mode 100644 index 0000000..a36c89c --- /dev/null +++ b/SOURCES/coreutils-8.32-cp-reflink-auto.patch @@ -0,0 +1,119 @@ +From 76126e2831580d0df20530f4d6f72189bd4f0b9a Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Thu, 18 Jun 2020 22:16:24 -0700 +Subject: [PATCH] cp: default to COW +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Likewise for ‘install’. Proposed in Bug#24400, and long past due. +* NEWS: +* doc/coreutils.texi (cp invocation): +* src/copy.h (enum Reflink_type): Document this. +* src/cp.c (cp_option_init): +* src/install.c (cp_option_init): Implement this. + +Upstream-commit: 25725f9d41735d176d73a757430739fb71c7d043 +Signed-off-by: Kamil Dudka +--- + doc/coreutils.texi | 19 ++++++++++++------- + src/copy.h | 4 ++-- + src/cp.c | 2 +- + src/install.c | 2 +- + 4 files changed, 16 insertions(+), 11 deletions(-) + +diff --git a/doc/coreutils.texi b/doc/coreutils.texi +index 02e0c1c..2382a16 100644 +--- a/doc/coreutils.texi ++++ b/doc/coreutils.texi +@@ -8854,12 +8854,14 @@ The @var{when} value can be one of the following: + + @table @samp + @item always +-The default behavior: if the copy-on-write operation is not supported ++If the copy-on-write operation is not supported + then report the failure for each file and exit with a failure status. ++Plain @option{--reflink} is equivalent to @option{--reflink=when}. + + @item auto + If the copy-on-write operation is not supported then fall back + to the standard copy behavior. ++This is the default if no @option{--reflink} option is given. + + @item never + Disable copy-on-write operation and use the standard copy behavior. +@@ -8868,12 +8870,6 @@ Disable copy-on-write operation and use the standard copy behavior. + This option is overridden by the @option{--link}, @option{--symbolic-link} + and @option{--attributes-only} options, thus allowing it to be used + to configure the default data copying behavior for @command{cp}. +-For example, with the following alias, @command{cp} will use the +-minimum amount of space supported by the file system. +- +-@example +-alias cp='cp --reflink=auto --sparse=always' +-@end example + + @item --remove-destination + @opindex --remove-destination +@@ -8918,6 +8914,15 @@ This is useful in creating a file for use with the @command{mkswap} command, + since such a file must not have any holes. + @end table + ++For example, with the following alias, @command{cp} will use the ++minimum amount of space supported by the file system. ++(Older versions of @command{cp} can also benefit from ++@option{--reflink=auto} here.) ++ ++@example ++alias cp='cp --sparse=always' ++@end example ++ + @optStripTrailingSlashes + + @item -s +diff --git a/src/copy.h b/src/copy.h +index 874d6f7..a0ad494 100644 +--- a/src/copy.h ++++ b/src/copy.h +@@ -46,10 +46,10 @@ enum Sparse_type + /* Control creation of COW files. */ + enum Reflink_type + { +- /* Default to a standard copy. */ ++ /* Do a standard copy. */ + REFLINK_NEVER, + +- /* Try a COW copy and fall back to a standard copy. */ ++ /* Try a COW copy and fall back to a standard copy; this is the default. */ + REFLINK_AUTO, + + /* Require a COW copy and fail if not available. */ +diff --git a/src/cp.c b/src/cp.c +index 0193df8..9e7ad14 100644 +--- a/src/cp.c ++++ b/src/cp.c +@@ -796,7 +796,7 @@ cp_option_init (struct cp_options *x) + x->move_mode = false; + x->install_mode = false; + x->one_file_system = false; +- x->reflink_mode = REFLINK_NEVER; ++ x->reflink_mode = REFLINK_AUTO; + + x->preserve_ownership = false; + x->preserve_links = false; +diff --git a/src/install.c b/src/install.c +index 4ab44a6..aef16ca 100644 +--- a/src/install.c ++++ b/src/install.c +@@ -264,7 +264,7 @@ cp_option_init (struct cp_options *x) + { + cp_options_default (x); + x->copy_as_regular = true; +- x->reflink_mode = REFLINK_NEVER; ++ x->reflink_mode = REFLINK_AUTO; + x->dereference = DEREF_ALWAYS; + x->unlink_dest_before_opening = true; + x->unlink_dest_after_failed_open = false; +-- +2.25.4 + diff --git a/SOURCES/coreutils-8.32-df-duplicated-entries.patch b/SOURCES/coreutils-8.32-df-duplicated-entries.patch new file mode 100644 index 0000000..5d39c1e --- /dev/null +++ b/SOURCES/coreutils-8.32-df-duplicated-entries.patch @@ -0,0 +1,72 @@ +From 0f053de4bc3ca0cfd88a42d236881dfdddb10ee9 Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +Date: Wed, 30 Jun 2021 17:53:22 +0200 +Subject: [PATCH] df: fix duplicated remote entries due to bind mounts + +As originally reported in , +df invoked without -a printed duplicated entries for NFS mounts +of bind mounts. This is a regression from commit v8.25-54-g1c17f61ef99, +which introduced the use of a hash table. + +The proposed patch makes sure that the devlist entry seen the last time +is used for comparison when eliminating duplicated mount entries. This +way it worked before introducing the hash table. + +Patch co-authored by Roberto Bergantinos. + +* src/ls.c (struct devlist): Introduce the seen_last pointer. +(devlist_for_dev): Return the devlist entry seen the last time if found. +(filter_mount_list): Remember the devlist entry seen the last time for +each hashed item. +Fixes https://bugs.gnu.org/49298 + +Upstream-commit: d6125af095c9553f38cba0696f15158f5abe4ecc +Signed-off-by: Kamil Dudka +--- + src/df.c | 15 +++++++++++++-- + 1 file changed, 13 insertions(+), 2 deletions(-) + +diff --git a/src/df.c b/src/df.c +index 7e01839..3e9247f 100644 +--- a/src/df.c ++++ b/src/df.c +@@ -54,6 +54,7 @@ struct devlist + dev_t dev_num; + struct mount_entry *me; + struct devlist *next; ++ struct devlist *seen_last; /* valid for hashed devlist entries only */ + }; + + /* Filled with device numbers of examined file systems to avoid +@@ -689,7 +690,13 @@ devlist_for_dev (dev_t dev) + return NULL; + struct devlist dev_entry; + dev_entry.dev_num = dev; +- return hash_lookup (devlist_table, &dev_entry); ++ ++ struct devlist *found = hash_lookup (devlist_table, &dev_entry); ++ if (found == NULL) ++ return NULL; ++ ++ /* Return the last devlist entry we have seen with this dev_num */ ++ return found->seen_last; + } + + static void +@@ -807,8 +814,12 @@ filter_mount_list (bool devices_only) + devlist->dev_num = buf.st_dev; + devlist->next = device_list; + device_list = devlist; +- if (hash_insert (devlist_table, devlist) == NULL) ++ ++ struct devlist *hash_entry = hash_insert (devlist_table, devlist); ++ if (hash_entry == NULL) + xalloc_die (); ++ /* Ensure lookups use this latest devlist. */ ++ hash_entry->seen_last = devlist; + + me = me->me_next; + } +-- +2.31.1 + diff --git a/SOURCES/coreutils-8.32-expr-unmatched-par.patch b/SOURCES/coreutils-8.32-expr-unmatched-par.patch new file mode 100644 index 0000000..1a82384 --- /dev/null +++ b/SOURCES/coreutils-8.32-expr-unmatched-par.patch @@ -0,0 +1,81 @@ +From 9618fb718b75920f37e5be2049ad1d0bb5c4a28c Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Tue, 26 Jan 2021 09:23:54 -0800 +Subject: [PATCH] expr: fix bug with unmatched \(...\) + +Problem reported by Qiuhao Li. +* doc/coreutils.texi (String expressions): +Document the correct behavior, which POSIX requires. +* src/expr.c (docolon): Treat unmatched \(...\) as empty. +* tests/misc/expr.pl: New test. + +Upstream-commit: 735083ba24878075235007b4417982ad5700436d +Signed-off-by: Kamil Dudka +--- + doc/coreutils.texi | 14 ++++++++------ + src/expr.c | 9 +++++++-- + tests/misc/expr.pl | 3 +++ + 3 files changed, 18 insertions(+), 8 deletions(-) + +diff --git a/doc/coreutils.texi b/doc/coreutils.texi +index 2382a16..5b2bb2c 100644 +--- a/doc/coreutils.texi ++++ b/doc/coreutils.texi +@@ -13529,12 +13529,14 @@ second is considered to be a (basic, a la GNU @code{grep}) regular + expression, with a @code{^} implicitly prepended. The first argument is + then matched against this regular expression. + +-If the match succeeds and @var{regex} uses @samp{\(} and @samp{\)}, the +-@code{:} expression returns the part of @var{string} that matched the +-subexpression; otherwise, it returns the number of characters matched. +- +-If the match fails, the @code{:} operator returns the null string if +-@samp{\(} and @samp{\)} are used in @var{regex}, otherwise 0. ++If @var{regex} does not use @samp{\(} and @samp{\)}, the @code{:} ++expression returns the number of characters matched, or 0 if the match ++fails. ++ ++If @var{regex} uses @samp{\(} and @samp{\)}, the @code{:} expression ++returns the part of @var{string} that matched the subexpression, or ++the null string if the match failed or the subexpression did not ++contribute to the match. + + @kindex \( @r{regexp operator} + Only the first @samp{\( @dots{} \)} pair is relevant to the return +diff --git a/src/expr.c b/src/expr.c +index e134872..0616a42 100644 +--- a/src/expr.c ++++ b/src/expr.c +@@ -721,8 +721,13 @@ docolon (VALUE *sv, VALUE *pv) + /* Were \(...\) used? */ + if (re_buffer.re_nsub > 0) + { +- sv->u.s[re_regs.end[1]] = '\0'; +- v = str_value (sv->u.s + re_regs.start[1]); ++ if (re_regs.end[1] < 0) ++ v = str_value (""); ++ else ++ { ++ sv->u.s[re_regs.end[1]] = '\0'; ++ v = str_value (sv->u.s + re_regs.start[1]); ++ } + } + else + { +diff --git a/tests/misc/expr.pl b/tests/misc/expr.pl +index e45f8e7..e57f79d 100755 +--- a/tests/misc/expr.pl ++++ b/tests/misc/expr.pl +@@ -84,6 +84,9 @@ my @Tests = + # In 5.94 and earlier, anchors incorrectly matched newlines. + ['anchor', "'a\nb' : 'a\$'", {OUT => '0'}, {EXIT => 1}], + ++ # In 8.32, \( ... \) that did not match caused memory errors. ++ ['emptysub', '"a" : "\\(b\\)*"', {OUT => ''}, {EXIT => 1}], ++ + # These tests are taken from grep/tests/bre.tests. + ['bre1', '"abc" : "a\\(b\\)c"', {OUT => 'b'}], + ['bre2', '"a(" : "a("', {OUT => '2'}], +-- +2.26.2 + diff --git a/SOURCES/coreutils-8.32-fuse-portal.patch b/SOURCES/coreutils-8.32-fuse-portal.patch new file mode 100644 index 0000000..54b7975 --- /dev/null +++ b/SOURCES/coreutils-8.32-fuse-portal.patch @@ -0,0 +1,38 @@ +From 602fb566468d3837b7871c17a0fab1a20228d119 Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +Date: Mon, 7 Jun 2021 14:43:03 +0200 +Subject: [PATCH] mountlist: recognize fuse.portal as dummy file system + +This was originally proposed at: + + https://lists.gnu.org/archive/html/bug-gnulib/2021-02/msg00053.html + +As the full review might take some time, would it be possible to apply +at least the part related to fuse.portal file systems? They started to +cause problems recently: + + https://bugs.launchpad.net/ubuntu/+source/xdg-desktop-portal/+bug/1905623 + https://github.com/muesli/duf/issues/35 + https://bugzilla.redhat.com/1913358 + +Upstream-commit: 9a38d499ca16f2f4304992eb1ab0894cd0b478e1 +Signed-off-by: Kamil Dudka +--- + lib/mountlist.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/lib/mountlist.c b/lib/mountlist.c +index e0227b7..e5f6b07 100644 +--- a/lib/mountlist.c ++++ b/lib/mountlist.c +@@ -170,6 +170,7 @@ + || strcmp (Fs_type, "debugfs") == 0 \ + || strcmp (Fs_type, "devpts") == 0 \ + || strcmp (Fs_type, "fusectl") == 0 \ ++ || strcmp (Fs_type, "fuse.portal") == 0 \ + || strcmp (Fs_type, "mqueue") == 0 \ + || strcmp (Fs_type, "rpc_pipefs") == 0 \ + || strcmp (Fs_type, "sysfs") == 0 \ +-- +2.31.1 + diff --git a/SOURCES/coreutils-8.32-gnulib-perror-test.patch b/SOURCES/coreutils-8.32-gnulib-perror-test.patch new file mode 100644 index 0000000..e545290 --- /dev/null +++ b/SOURCES/coreutils-8.32-gnulib-perror-test.patch @@ -0,0 +1,48 @@ +From f61085aaa37f169365c56e44f5129d0491913b6a Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Thu, 27 Aug 2020 17:52:58 -0700 +Subject: [PATCH] perror, strerror_r: remove unportable tests + +Problem reported by Florian Weimer in: +https://lists.gnu.org/r/bug-gnulib/2020-08/msg00220.html +* tests/test-perror2.c (main): +* tests/test-strerror_r.c (main): Omit unportable tests. + +Upstream-commit: 175e0bc72808d564074c4adcc72aeadb74adfcc6 +Signed-off-by: Kamil Dudka +--- + gnulib-tests/test-perror2.c | 3 --- + gnulib-tests/test-strerror_r.c | 3 --- + 2 files changed, 6 deletions(-) + +diff --git a/gnulib-tests/test-perror2.c b/gnulib-tests/test-perror2.c +index 1d14eda..c6214dd 100644 +--- a/gnulib-tests/test-perror2.c ++++ b/gnulib-tests/test-perror2.c +@@ -79,9 +79,6 @@ main (void) + errno = -5; + perror (""); + ASSERT (!ferror (stderr)); +- ASSERT (msg1 == msg2 || msg1 == msg4 || STREQ (msg1, str1)); +- ASSERT (msg2 == msg4 || STREQ (msg2, str2)); +- ASSERT (msg3 == msg4 || STREQ (msg3, str3)); + ASSERT (STREQ (msg4, str4)); + + free (str1); +diff --git a/gnulib-tests/test-strerror_r.c b/gnulib-tests/test-strerror_r.c +index b11d6fd..c1dbcf8 100644 +--- a/gnulib-tests/test-strerror_r.c ++++ b/gnulib-tests/test-strerror_r.c +@@ -165,9 +165,6 @@ main (void) + + strerror_r (EACCES, buf, sizeof buf); + strerror_r (-5, buf, sizeof buf); +- ASSERT (msg1 == msg2 || msg1 == msg4 || STREQ (msg1, str1)); +- ASSERT (msg2 == msg4 || STREQ (msg2, str2)); +- ASSERT (msg3 == msg4 || STREQ (msg3, str3)); + ASSERT (STREQ (msg4, str4)); + + free (str1); +-- +2.25.4 + diff --git a/SOURCES/coreutils-8.32-leaf-opt-xfs.patch b/SOURCES/coreutils-8.32-leaf-opt-xfs.patch new file mode 100644 index 0000000..c02db71 --- /dev/null +++ b/SOURCES/coreutils-8.32-leaf-opt-xfs.patch @@ -0,0 +1,164 @@ +From b9f9ed14bda93ecb407129b69e6476813c250046 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Wed, 15 Apr 2020 20:50:32 -0700 +Subject: [PATCH] fts: remove NOSTAT_LEAF_OPTIMIZATION +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It caused ‘find’ and ‘du’ to dump core, and it was useful +only for obsolescent Linux filesystems anyway. Problem reported in: +https://lists.gnu.org/r/bug-gnulib/2020-04/msg00068.html +Quite possibly there is still a serious underlying fts bug with +tight-loop-check and mutating file systems, but if so this patch +should cause the bug to be triggered less often. +* lib/fts.c (enum leaf_optimization): Remove +NOSTAT_LEAF_OPTIMIZATION, as it’s problematic. +(S_MAGIC_REISERFS, S_MAGIC_XFS): Remove; no longer needed. +(leaf_optimization): Remove special cases for ReiserFS and XFS. +(fts_read): Remove NOSTAT_LEAF_OPTIMIZATION code. +* lib/fts_.h (struct _ftsent.fts_n_dirs_remaining): +Remove. All uses removed. + +Upstream-commit: 47bf2cf3184027c1eb9c1dfeea5c5b8b2d69710d +Signed-off-by: Kamil Dudka +--- + lib/fts.c | 56 ++++++++------------------------------------------- + lib/fts_.h | 5 ----- + 2 files changed, 8 insertions(+), 53 deletions(-) + +diff --git a/lib/fts.c b/lib/fts.c +index 1093ce5..dfe3fef 100644 +--- a/lib/fts.c ++++ b/lib/fts.c +@@ -445,7 +445,6 @@ fts_open (char * const *argv, + if ((parent = fts_alloc(sp, "", 0)) == NULL) + goto mem2; + parent->fts_level = FTS_ROOTPARENTLEVEL; +- parent->fts_n_dirs_remaining = -1; + } + + /* The classic fts implementation would call fts_stat with +@@ -634,9 +633,8 @@ fts_close (FTS *sp) + } + + /* Minimum link count of a traditional Unix directory. When leaf +- optimization is OK and MIN_DIR_NLINK <= st_nlink, then st_nlink is +- an upper bound on the number of subdirectories (counting "." and +- ".."). */ ++ optimization is OK and a directory's st_nlink == MIN_DIR_NLINK, ++ then the directory has no subdirectories. */ + enum { MIN_DIR_NLINK = 2 }; + + /* Whether leaf optimization is OK for a directory. */ +@@ -645,12 +643,8 @@ enum leaf_optimization + /* st_nlink is not reliable for this directory's subdirectories. */ + NO_LEAF_OPTIMIZATION, + +- /* Leaf optimization is OK, but is not useful for avoiding stat calls. */ +- OK_LEAF_OPTIMIZATION, +- +- /* Leaf optimization is not only OK: it is useful for avoiding +- stat calls, because dirent.d_type does not work. */ +- NOSTAT_LEAF_OPTIMIZATION ++ /* st_nlink == 2 means the directory lacks subdirectories. */ ++ OK_LEAF_OPTIMIZATION + }; + + #if (defined __linux__ || defined __ANDROID__) \ +@@ -663,9 +657,7 @@ enum leaf_optimization + # define S_MAGIC_CIFS 0xFF534D42 + # define S_MAGIC_NFS 0x6969 + # define S_MAGIC_PROC 0x9FA0 +-# define S_MAGIC_REISERFS 0x52654973 + # define S_MAGIC_TMPFS 0x1021994 +-# define S_MAGIC_XFS 0x58465342 + + # ifdef HAVE___FSWORD_T + typedef __fsword_t fsword; +@@ -786,23 +778,15 @@ dirent_inode_sort_may_be_useful (FTSENT const *p, int dir_fd) + } + + /* Given an FTS entry P for a directory with descriptor DIR_FD, +- return true if it is both useful and valid to apply leaf optimization. +- The optimization is useful only for file systems that lack usable +- dirent.d_type info. The optimization is valid if an st_nlink value +- of at least MIN_DIR_NLINK is an upper bound on the number of +- subdirectories of D, counting "." and ".." as subdirectories. ++ return whether it is valid to apply leaf optimization. ++ The optimization is valid if a directory's st_nlink value equal ++ to MIN_DIR_NLINK means the directory has no subdirectories. + DIR_FD is negative if unavailable. */ + static enum leaf_optimization + leaf_optimization (FTSENT const *p, int dir_fd) + { + switch (filesystem_type (p, dir_fd)) + { +- /* List here the file system types that may lack usable dirent.d_type +- info, yet for which the optimization does apply. */ +- case S_MAGIC_REISERFS: +- case S_MAGIC_XFS: /* XFS lacked it until 2013-08-22 commit. */ +- return NOSTAT_LEAF_OPTIMIZATION; +- + case 0: + /* Leaf optimization is unsafe if the file system type is unknown. */ + FALLTHROUGH; +@@ -1027,26 +1011,7 @@ check_for_dir: + if (p->fts_info == FTS_NSOK) + { + if (p->fts_statp->st_size == FTS_STAT_REQUIRED) +- { +- FTSENT *parent = p->fts_parent; +- if (parent->fts_n_dirs_remaining == 0 +- && ISSET(FTS_NOSTAT) +- && ISSET(FTS_PHYSICAL) +- && (leaf_optimization (parent, sp->fts_cwd_fd) +- == NOSTAT_LEAF_OPTIMIZATION)) +- { +- /* nothing more needed */ +- } +- else +- { +- p->fts_info = fts_stat(sp, p, false); +- if (S_ISDIR(p->fts_statp->st_mode) +- && p->fts_level != FTS_ROOTLEVEL +- && 0 < parent->fts_n_dirs_remaining +- && parent->fts_n_dirs_remaining != (nlink_t) -1) +- parent->fts_n_dirs_remaining--; +- } +- } ++ p->fts_info = fts_stat(sp, p, false); + else + fts_assert (p->fts_statp->st_size == FTS_NO_STAT_REQUIRED); + } +@@ -1830,11 +1795,6 @@ err: memset(sbp, 0, sizeof(struct stat)); + } + + if (S_ISDIR(sbp->st_mode)) { +- p->fts_n_dirs_remaining +- = ((sbp->st_nlink < MIN_DIR_NLINK +- || p->fts_level <= FTS_ROOTLEVEL) +- ? -1 +- : sbp->st_nlink - (ISSET (FTS_SEEDOT) ? 0 : MIN_DIR_NLINK)); + if (ISDOT(p->fts_name)) { + /* Command-line "." and ".." are real directories. */ + return (p->fts_level == FTS_ROOTLEVEL ? FTS_D : FTS_DOT); +diff --git a/lib/fts_.h b/lib/fts_.h +index d40a116..2e76cc4 100644 +--- a/lib/fts_.h ++++ b/lib/fts_.h +@@ -227,11 +227,6 @@ typedef struct _ftsent { + + size_t fts_namelen; /* strlen(fts_name) */ + +- /* If not (nlink_t) -1, an upper bound on the number of +- remaining subdirectories of interest. If this becomes +- zero, some work can be avoided. */ +- nlink_t fts_n_dirs_remaining; +- + # define FTS_D 1 /* preorder directory */ + # define FTS_DC 2 /* directory that causes cycles */ + # define FTS_DEFAULT 3 /* none of the above */ +-- +2.21.1 + diff --git a/SOURCES/coreutils-8.32-ls-removed-dir.patch b/SOURCES/coreutils-8.32-ls-removed-dir.patch new file mode 100644 index 0000000..77dce89 --- /dev/null +++ b/SOURCES/coreutils-8.32-ls-removed-dir.patch @@ -0,0 +1,153 @@ +From 8c022656320592dbad146f5d3a3ae1875f419446 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Thu, 5 Mar 2020 17:25:29 -0800 +Subject: [PATCH 1/2] ls: restore 8.31 behavior on removed directories + +* NEWS: Mention this. +* src/ls.c: Do not include +(print_dir): Don't worry about whether the directory is removed. +* tests/ls/removed-directory.sh: Adjust to match new (i.e., old) +behavior. + +Upstream-commit: 10fcb97bd728f09d4a027eddf8ad2900f0819b0a +Signed-off-by: Kamil Dudka +--- + src/ls.c | 22 ---------------------- + tests/ls/removed-directory.sh | 10 ++-------- + 2 files changed, 2 insertions(+), 30 deletions(-) + +diff --git a/src/ls.c b/src/ls.c +index 9d25f62..850ecc2 100644 +--- a/src/ls.c ++++ b/src/ls.c +@@ -49,10 +49,6 @@ + # include + #endif + +-#ifdef __linux__ +-# include +-#endif +- + #include + #include + #include +@@ -2896,7 +2892,6 @@ print_dir (char const *name, char const *realname, bool command_line_arg) + struct dirent *next; + uintmax_t total_blocks = 0; + static bool first = true; +- bool found_any_entries = false; + + errno = 0; + dirp = opendir (name); +@@ -2972,7 +2967,6 @@ print_dir (char const *name, char const *realname, bool command_line_arg) + next = readdir (dirp); + if (next) + { +- found_any_entries = true; + if (! file_ignored (next->d_name)) + { + enum filetype type = unknown; +@@ -3018,22 +3012,6 @@ print_dir (char const *name, char const *realname, bool command_line_arg) + if (errno != EOVERFLOW) + break; + } +-#ifdef __linux__ +- else if (! found_any_entries) +- { +- /* If readdir finds no directory entries at all, not even "." or +- "..", then double check that the directory exists. */ +- if (syscall (SYS_getdents, dirfd (dirp), NULL, 0) == -1 +- && errno != EINVAL) +- { +- /* We exclude EINVAL as that pertains to buffer handling, +- and we've passed NULL as the buffer for simplicity. +- ENOENT is returned if appropriate before buffer handling. */ +- file_failure (command_line_arg, _("reading directory %s"), name); +- } +- break; +- } +-#endif + else + break; + +diff --git a/tests/ls/removed-directory.sh b/tests/ls/removed-directory.sh +index e8c835d..fe8f929 100755 +--- a/tests/ls/removed-directory.sh ++++ b/tests/ls/removed-directory.sh +@@ -26,20 +26,14 @@ case $host_triplet in + *) skip_ 'non linux kernel' ;; + esac + +-LS_FAILURE=2 +- +-cat <<\EOF >exp-err || framework_failure_ +-ls: reading directory '.': No such file or directory +-EOF +- + cwd=$(pwd) + mkdir d || framework_failure_ + cd d || framework_failure_ + rmdir ../d || framework_failure_ + +-returns_ $LS_FAILURE ls >../out 2>../err || fail=1 ++ls >../out 2>../err || fail=1 + cd "$cwd" || framework_failure_ + compare /dev/null out || fail=1 +-compare exp-err err || fail=1 ++compare /dev/null err || fail=1 + + Exit $fail +-- +2.21.1 + + +From 847324a0debd9d12062c79e7a7a9d3d8ce76390d Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Sat, 7 Mar 2020 10:29:51 -0800 +Subject: [PATCH 2/2] ls: improve removed-directory test + +* tests/ls/removed-directory.sh: Remove host_triplet test. +Skip this test if one cannot remove the working directory. +From a suggestion by Bernhard Voelker (Bug#39929). + +Upstream-commit: 672819c73f2e94e61386dc0584bddf9da860cc26 +Signed-off-by: Kamil Dudka +--- + tests/ls/removed-directory.sh | 13 ++++--------- + 1 file changed, 4 insertions(+), 9 deletions(-) + +diff --git a/tests/ls/removed-directory.sh b/tests/ls/removed-directory.sh +index fe8f929..63b209d 100755 +--- a/tests/ls/removed-directory.sh ++++ b/tests/ls/removed-directory.sh +@@ -1,7 +1,7 @@ + #!/bin/sh +-# If ls is asked to list a removed directory (e.g. the parent process's +-# current working directory that has been removed by another process), it +-# emits an error message. ++# If ls is asked to list a removed directory (e.g., the parent process's ++# current working directory has been removed by another process), it ++# should not emit an error message merely because the directory is removed. + + # Copyright (C) 2020 Free Software Foundation, Inc. + +@@ -21,15 +21,10 @@ + . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src + print_ver_ ls + +-case $host_triplet in +- *linux*) ;; +- *) skip_ 'non linux kernel' ;; +-esac +- + cwd=$(pwd) + mkdir d || framework_failure_ + cd d || framework_failure_ +-rmdir ../d || framework_failure_ ++rmdir ../d || skip_ "can't remove working directory on this platform" + + ls >../out 2>../err || fail=1 + cd "$cwd" || framework_failure_ +-- +2.21.1 + diff --git a/SOURCES/coreutils-8.32-ls-scontext-crash.patch b/SOURCES/coreutils-8.32-ls-scontext-crash.patch new file mode 100644 index 0000000..a3ddff9 --- /dev/null +++ b/SOURCES/coreutils-8.32-ls-scontext-crash.patch @@ -0,0 +1,85 @@ +From 53c6b01e8e3fd338d7f53e5ff817ef86f9efa852 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Wed, 11 Nov 2020 17:22:33 +0000 +Subject: [PATCH] ls: fix crash printing SELinux context for unstatable files + +This crash was identified by Cyber Independent Testing Lab: +https://cyber-itl.org/2020/10/28/citl-7000-defects.html +and was introduced with commit v6.9.90-11-g4245876e2 + +* src/ls.c (gobble_file): Ensure scontext is initialized +in the case where files are not statable. +* tests/ls/selinux-segfault.sh: Renamed from proc-selinux-segfault.sh, +and added test case for broken symlinks. +* tests/local.mk: Adjust for the renamed test. + +Upstream-commit: 6fc695cb4a26f09dfeef8b1c24895a707055334e +Signed-off-by: Kamil Dudka +--- + src/ls.c | 3 +++ + tests/local.mk | 2 +- + .../{proc-selinux-segfault.sh => selinux-segfault.sh} | 10 ++++++++-- + 3 files changed, 12 insertions(+), 3 deletions(-) + rename tests/ls/{proc-selinux-segfault.sh => selinux-segfault.sh} (77%) + +diff --git a/src/ls.c b/src/ls.c +index 4acf5f4..8eb483d 100644 +--- a/src/ls.c ++++ b/src/ls.c +@@ -3412,6 +3412,9 @@ gobble_file (char const *name, enum filetype type, ino_t inode, + provokes an exit status of 1. */ + file_failure (command_line_arg, + _("cannot access %s"), full_name); ++ ++ f->scontext = UNKNOWN_SECURITY_CONTEXT; ++ + if (command_line_arg) + return 0; + +diff --git a/tests/local.mk b/tests/local.mk +index 2aeff2b..2441fdc 100644 +--- a/tests/local.mk ++++ b/tests/local.mk +@@ -616,7 +616,7 @@ all_tests = \ + tests/ls/multihardlink.sh \ + tests/ls/no-arg.sh \ + tests/ls/no-cap.sh \ +- tests/ls/proc-selinux-segfault.sh \ ++ tests/ls/selinux-segfault.sh \ + tests/ls/quote-align.sh \ + tests/ls/readdir-mountpoint-inode.sh \ + tests/ls/recursive.sh \ +diff --git a/tests/ls/proc-selinux-segfault.sh b/tests/ls/selinux-segfault.sh +similarity index 77% +rename from tests/ls/proc-selinux-segfault.sh +rename to tests/ls/selinux-segfault.sh +index 831a00e..e2b7ef6 100755 +--- a/tests/ls/proc-selinux-segfault.sh ++++ b/tests/ls/selinux-segfault.sh +@@ -1,5 +1,5 @@ + #!/bin/sh +-# ls -l /proc/sys would segfault when built against libselinux1 2.0.15-2+b1 ++# Ensure we don't segfault in selinux handling + + # Copyright (C) 2008-2020 Free Software Foundation, Inc. + +@@ -19,9 +19,15 @@ + . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src + print_ver_ ls + ++# ls -l /proc/sys would segfault when built against libselinux1 2.0.15-2+b1 + f=/proc/sys + test -r $f || f=. +- + ls -l $f > out || fail=1 + ++# ls <= 8.32 would segfault when printing ++# the security context of broken symlink targets ++mkdir sedir || framework_failure_ ++ln -sf missing sedir/broken || framework_failure_ ++returns_ 1 ls -L -R -Z -m sedir > out || fail=1 ++ + Exit $fail +-- +2.26.2 + diff --git a/SOURCES/coreutils-8.32-mem-leaks.patch b/SOURCES/coreutils-8.32-mem-leaks.patch new file mode 100644 index 0000000..78a90fc --- /dev/null +++ b/SOURCES/coreutils-8.32-mem-leaks.patch @@ -0,0 +1,186 @@ +From be77b4ab7cb68fd2daf9de90bd75d844392788ac Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +Date: Thu, 25 Mar 2021 11:57:56 +0100 +Subject: [PATCH 1/4] ln: fix memory leaks in do_link + +* src/ln.c (do_link): Free memory allocated by convert_abs_rel +on all code paths (Bug#47373). + +Upstream-commit: 6e98f67758260579d7d44ea5f2df4c82d28c9f58 +Signed-off-by: Kamil Dudka +--- + src/ln.c | 19 +++++++++++++------ + 1 file changed, 13 insertions(+), 6 deletions(-) + +diff --git a/src/ln.c b/src/ln.c +index ffa278e..9b52602 100644 +--- a/src/ln.c ++++ b/src/ln.c +@@ -229,14 +229,14 @@ do_link (char const *source, int destdir_fd, char const *dest_base, + if (errno != ENOENT) + { + error (0, errno, _("failed to access %s"), quoteaf (dest)); +- return false; ++ goto fail; + } + force = false; + } + else if (S_ISDIR (dest_stats.st_mode)) + { + error (0, 0, _("%s: cannot overwrite directory"), quotef (dest)); +- return false; ++ goto fail; + } + else if (seen_file (dest_set, dest, &dest_stats)) + { +@@ -245,7 +245,7 @@ do_link (char const *source, int destdir_fd, char const *dest_base, + error (0, 0, + _("will not overwrite just-created %s with %s"), + quoteaf_n (0, dest), quoteaf_n (1, source)); +- return false; ++ goto fail; + } + else + { +@@ -274,7 +274,7 @@ do_link (char const *source, int destdir_fd, char const *dest_base, + { + error (0, 0, _("%s and %s are the same file"), + quoteaf_n (0, source), quoteaf_n (1, dest)); +- return false; ++ goto fail; + } + } + +@@ -285,7 +285,10 @@ do_link (char const *source, int destdir_fd, char const *dest_base, + fprintf (stderr, _("%s: replace %s? "), + program_name, quoteaf (dest)); + if (!yesno ()) +- return true; ++ { ++ free(rel_source); ++ return true; ++ } + } + + if (backup_type != no_backups) +@@ -304,7 +307,7 @@ do_link (char const *source, int destdir_fd, char const *dest_base, + { + error (0, rename_errno, _("cannot backup %s"), + quoteaf (dest)); +- return false; ++ goto fail; + } + force = false; + } +@@ -397,6 +400,10 @@ do_link (char const *source, int destdir_fd, char const *dest_base, + free (backup_base); + free (rel_source); + return link_errno <= 0; ++ ++fail: ++ free (rel_source); ++ return false; + } + + void +-- +2.26.3 + + +From c051578e69bd8acf8f8a469566ae34e855345532 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Thu, 25 Mar 2021 09:15:50 -0700 +Subject: [PATCH 2/4] maint: indenting + +* src/ln.c: Fix indenting. + +Upstream-commit: 8980b7c898046d899646da01c296fd15f0cced21 +Signed-off-by: Kamil Dudka +--- + src/ln.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/ln.c b/src/ln.c +index 9b52602..8881d6a 100644 +--- a/src/ln.c ++++ b/src/ln.c +@@ -286,7 +286,7 @@ do_link (char const *source, int destdir_fd, char const *dest_base, + program_name, quoteaf (dest)); + if (!yesno ()) + { +- free(rel_source); ++ free (rel_source); + return true; + } + } +@@ -304,7 +304,7 @@ do_link (char const *source, int destdir_fd, char const *dest_base, + free (backup_base); + backup_base = NULL; + if (rename_errno != ENOENT) +- { ++ { + error (0, rename_errno, _("cannot backup %s"), + quoteaf (dest)); + goto fail; +-- +2.26.3 + + +From 0d6a4afe5bee0e397fb2fc3b205a29b32a69af9d Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Thu, 25 Mar 2021 09:16:36 -0700 +Subject: [PATCH 3/4] hostname: use puts + +* src/hostname.c (main): Prefer puts to printf "%s\n". + +Upstream-commit: c7a588ac3632aae21642d4d568497177950d36bf +Signed-off-by: Kamil Dudka +--- + src/hostname.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/hostname.c b/src/hostname.c +index 0b5c0cf..62cc98c 100644 +--- a/src/hostname.c ++++ b/src/hostname.c +@@ -103,7 +103,7 @@ main (int argc, char **argv) + hostname = xgethostname (); + if (hostname == NULL) + die (EXIT_FAILURE, errno, _("cannot determine hostname")); +- printf ("%s\n", hostname); ++ puts (hostname); + } + + if (optind + 1 < argc) +-- +2.26.3 + + +From 19c98d2080251edbaad9fb271aa10ad34f953500 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Thu, 25 Mar 2021 11:20:18 -0700 +Subject: [PATCH 4/4] hostname: pacify valgrind + +* src/hostname.c (main) [IF_LINT]: Free hostname (Bug#47384). + +Upstream-commit: 4698e284f37844bc9b9f63f00eb556ccaaed5030 +Signed-off-by: Kamil Dudka +--- + src/hostname.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/hostname.c b/src/hostname.c +index 62cc98c..7210248 100644 +--- a/src/hostname.c ++++ b/src/hostname.c +@@ -104,6 +104,7 @@ main (int argc, char **argv) + if (hostname == NULL) + die (EXIT_FAILURE, errno, _("cannot determine hostname")); + puts (hostname); ++ IF_LINT (free (hostname)); + } + + if (optind + 1 < argc) +-- +2.26.3 + diff --git a/SOURCES/coreutils-8.32-new-fs-types.patch b/SOURCES/coreutils-8.32-new-fs-types.patch new file mode 100644 index 0000000..a8eb35c --- /dev/null +++ b/SOURCES/coreutils-8.32-new-fs-types.patch @@ -0,0 +1,104 @@ +From 09400b7f7f48d8eedc0df55de8073a43bc0aac96 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Tue, 27 Oct 2020 20:15:43 +0000 +Subject: [PATCH 1/2] stat,tail: sync file system constants from the linux + kernel + +* src/stat.c: Add magic constants for "devmem", and +"zonefs" file systems. +* NEWS: Mention the improvement. + +Upstream-commit: ff80b6b0a0507e24f39cc1aad09d147f5187430b +Signed-off-by: Kamil Dudka +--- + src/stat.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/stat.c b/src/stat.c +index 5012622..8cd69da 100644 +--- a/src/stat.c ++++ b/src/stat.c +@@ -347,6 +347,8 @@ human_fstype (STRUCT_STATVFS const *statfsbuf) + return "debugfs"; + case S_MAGIC_DEVFS: /* 0x1373 local */ + return "devfs"; ++ case S_MAGIC_DEVMEM: /* 0x454D444D local */ ++ return "devmem"; + case S_MAGIC_DEVPTS: /* 0x1CD1 local */ + return "devpts"; + case S_MAGIC_DMA_BUF: /* 0x444D4142 local */ +@@ -549,6 +551,8 @@ human_fstype (STRUCT_STATVFS const *statfsbuf) + return "z3fold"; + case S_MAGIC_ZFS: /* 0x2FC12FC1 local */ + return "zfs"; ++ case S_MAGIC_ZONEFS: /* 0x5A4F4653 local */ ++ return "zonefs"; + case S_MAGIC_ZSMALLOC: /* 0x58295829 local */ + return "zsmallocfs"; + +-- +2.25.4 + + +From d5948fd41013dfe4d2d10083111821667977c6d1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Tue, 27 Oct 2020 21:04:14 +0000 +Subject: [PATCH 2/2] mountlist: recognize more file system types as remote + +Sync "remote" file systems from stat.c in coreutils. +Note we only consider file systems that do not use host:resource +mount source. I.e. those that don't generally use a colon when +mounting, as that case is already considered. Searching for +" /etc/fstab" was informative for identifying these. +The full list of "remote" file systems in coreutils is currently: + acfs afs ceph cifs coda fhgfs fuseblk fusectl + gfs gfs2 gpfs ibrix k-afs lustre novell nfs nfsd + ocfs2 panfs prl_fs smb smb2 snfs vboxsf vmhgfs vxfs +Note also we do not include virtual machine file systems, +as even though they're remote to the current kernel, +they are generally not distributed to separate hosts. + +* lib/mountlist.c (ME_REMOTE): Sync previously unconsidered +"remote" file systems from stat.c in coreutils. + +Upstream-commit: dd1fc46be12d671c1a9d9dc5a6fa8c766e99aa2f +Signed-off-by: Kamil Dudka +--- + lib/mountlist.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/lib/mountlist.c b/lib/mountlist.c +index 7abe024..e0227b7 100644 +--- a/lib/mountlist.c ++++ b/lib/mountlist.c +@@ -221,8 +221,9 @@ me_remote (char const *fs_name, char const *fs_type _GL_UNUSED) + #ifndef ME_REMOTE + /* A file system is "remote" if its Fs_name contains a ':' + or if (it is of type (smbfs or cifs) and its Fs_name starts with '//') +- or if it is of type (afs or auristorfs) +- or Fs_name is equal to "-hosts" (used by autofs to mount remote fs). */ ++ or if it is of any other of the listed types ++ or Fs_name is equal to "-hosts" (used by autofs to mount remote fs). ++ "VM" file systems like prl_fs or vboxsf are not considered remote here. */ + # define ME_REMOTE(Fs_name, Fs_type) \ + (strchr (Fs_name, ':') != NULL \ + || ((Fs_name)[0] == '/' \ +@@ -230,8 +231,15 @@ me_remote (char const *fs_name, char const *fs_type _GL_UNUSED) + && (strcmp (Fs_type, "smbfs") == 0 \ + || strcmp (Fs_type, "smb3") == 0 \ + || strcmp (Fs_type, "cifs") == 0)) \ ++ || strcmp (Fs_type, "acfs") == 0 \ + || strcmp (Fs_type, "afs") == 0 \ ++ || strcmp (Fs_type, "coda") == 0 \ + || strcmp (Fs_type, "auristorfs") == 0 \ ++ || strcmp (Fs_type, "fhgfs") == 0 \ ++ || strcmp (Fs_type, "gpfs") == 0 \ ++ || strcmp (Fs_type, "ibrix") == 0 \ ++ || strcmp (Fs_type, "ocfs2") == 0 \ ++ || strcmp (Fs_type, "vxfs") == 0 \ + || strcmp ("-hosts", Fs_name) == 0) + #endif + +-- +2.25.4 + diff --git a/SOURCES/coreutils-8.32-rm-stray-skip.patch b/SOURCES/coreutils-8.32-rm-stray-skip.patch new file mode 100644 index 0000000..66a1efc --- /dev/null +++ b/SOURCES/coreutils-8.32-rm-stray-skip.patch @@ -0,0 +1,109 @@ +From 11b37b65d08c2a8b6d967fd866ebbdbe7e864949 Mon Sep 17 00:00:00 2001 +From: Nishant Nayan +Date: Thu, 26 Nov 2020 14:35:17 +0000 +Subject: [PATCH] rm: do not skip files upon failure to remove an empty dir + +When removing a directory fails for some reason, and that directory +is empty, the rm_fts code gets the return value of the excise call +confused with the return value of its earlier call to prompt, +causing fts_skip_tree to be called again and the next file +that rm would otherwise have deleted to survive. + +* src/remove.c (rm_fts): Ensure we only skip a single fts entry, +when processing empty dirs. I.e. only skip the entry +having successfully removed it. +* tests/rm/empty-immutable-skip.sh: New root-only test. +* tests/local.mk: Add it. +* NEWS: Mention the bug fix. +Fixes https://bugs.gnu.org/44883 + +Upstream-commit: 6bf108358a6104ec1c694c9530b3cd56b95f4b57 +Signed-off-by: Kamil Dudka +--- + src/remove.c | 3 ++- + tests/local.mk | 1 + + tests/rm/empty-immutable-skip.sh | 46 ++++++++++++++++++++++++++++++++ + 3 files changed, 49 insertions(+), 1 deletion(-) + create mode 100755 tests/rm/empty-immutable-skip.sh + +diff --git a/src/remove.c b/src/remove.c +index 2d40c55..adf9489 100644 +--- a/src/remove.c ++++ b/src/remove.c +@@ -506,7 +506,8 @@ rm_fts (FTS *fts, FTSENT *ent, struct rm_options const *x) + /* When we know (from prompt when in interactive mode) + that this is an empty directory, don't prompt twice. */ + s = excise (fts, ent, x, true); +- fts_skip_tree (fts, ent); ++ if (s == RM_OK) ++ fts_skip_tree (fts, ent); + } + + if (s != RM_OK) +diff --git a/tests/local.mk b/tests/local.mk +index 5f7f775..2aeff2b 100644 +--- a/tests/local.mk ++++ b/tests/local.mk +@@ -136,6 +136,7 @@ all_root_tests = \ + tests/rm/no-give-up.sh \ + tests/rm/one-file-system.sh \ + tests/rm/read-only.sh \ ++ tests/rm/empty-immutable-skip.sh \ + tests/tail-2/append-only.sh \ + tests/tail-2/end-of-device.sh \ + tests/touch/now-owned-by-other.sh +diff --git a/tests/rm/empty-immutable-skip.sh b/tests/rm/empty-immutable-skip.sh +new file mode 100755 +index 0000000..c91d8d4 +--- /dev/null ++++ b/tests/rm/empty-immutable-skip.sh +@@ -0,0 +1,46 @@ ++#!/bin/sh ++# Ensure that rm does not skip extra files after hitting an empty immutable dir. ++# Requires root access to do chattr +i, as well as an ext[23] or xfs file system ++ ++# Copyright (C) 2020 Free Software Foundation, Inc. ++ ++# This program is free software: you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation, either version 3 of the License, or ++# (at your option) any later version. ++ ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++ ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++ ++. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src ++print_ver_ rm ++require_root_ ++ ++# These simple one-file operations are expected to work even in the ++# presence of this bug, and we need them to set up the rest of the test. ++chattr_i_works=1 ++touch f ++chattr +i f 2>/dev/null || chattr_i_works=0 ++rm f 2>/dev/null ++test -f f || chattr_i_works=0 ++chattr -i f 2>/dev/null || chattr_i_works=0 ++rm f 2>/dev/null || chattr_i_works=0 ++test -f f && chattr_i_works=0 ++ ++if test $chattr_i_works = 0; then ++ skip_ "chattr +i doesn't work on this file system" ++fi ++ ++mkdir empty || framework_failure_ ++touch x y || framework_failure_ ++chattr +i empty || framework_failure_ ++rm -rf empty x y ++{ test -f x || test -f y; } && fail=1 ++chattr -i empty ++ ++Exit $fail +-- +2.26.2 + diff --git a/SOURCES/coreutils-8.32-split-number.patch b/SOURCES/coreutils-8.32-split-number.patch new file mode 100644 index 0000000..89541bf --- /dev/null +++ b/SOURCES/coreutils-8.32-split-number.patch @@ -0,0 +1,100 @@ +From bb0e7fabcaed9a7e71e30f05e638e9f243cdb13e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Mon, 25 Jan 2021 14:12:48 +0000 +Subject: [PATCH] split: fix --number=K/N to output correct part of file + +This functionality regressed with the adjustments +in commit v8.25-4-g62e7af032 + +* src/split.c (bytes_chunk_extract): Account for already read data +when seeking into the file. +* tests/split/b-chunk.sh: Use the hidden ---io-blksize option, +to test this functionality. +Fixes https://bugs.gnu.org/46048 + +Upstream-commit: bb21daa125aeb4e32546309d370918ca47e612db +Signed-off-by: Kamil Dudka +--- + src/split.c | 2 +- + tests/split/b-chunk.sh | 45 ++++++++++++++++++++++++------------------ + 2 files changed, 27 insertions(+), 20 deletions(-) + +diff --git a/src/split.c b/src/split.c +index 09e610b..19248f6 100644 +--- a/src/split.c ++++ b/src/split.c +@@ -1001,7 +1001,7 @@ bytes_chunk_extract (uintmax_t k, uintmax_t n, char *buf, size_t bufsize, + } + else + { +- if (lseek (STDIN_FILENO, start, SEEK_CUR) < 0) ++ if (lseek (STDIN_FILENO, start - initial_read, SEEK_CUR) < 0) + die (EXIT_FAILURE, errno, "%s", quotef (infile)); + initial_read = SIZE_MAX; + } +diff --git a/tests/split/b-chunk.sh b/tests/split/b-chunk.sh +index 864ce55..39a6799 100755 +--- a/tests/split/b-chunk.sh ++++ b/tests/split/b-chunk.sh +@@ -35,32 +35,39 @@ split -e -n 10 /dev/null || fail=1 + returns_ 1 stat x?? 2>/dev/null || fail=1 + + printf '1\n2\n3\n4\n5\n' > input || framework_failure_ ++printf '1\n2' > exp-1 || framework_failure_ ++printf '\n3\n' > exp-2 || framework_failure_ ++printf '4\n5\n' > exp-3 || framework_failure_ + + for file in input /proc/version /sys/kernel/profiling; do + test -f $file || continue + +- split -n 3 $file > out || fail=1 +- split -n 1/3 $file > b1 || fail=1 +- split -n 2/3 $file > b2 || fail=1 +- split -n 3/3 $file > b3 || fail=1 ++ for blksize in 1 2 4096; do ++ if ! test "$file" = 'input'; then ++ # For /proc like files we must be able to read all ++ # into the internal buffer to be able to determine size. ++ test "$blksize" = 4096 || continue ++ fi + +- case $file in +- input) +- printf '1\n2' > exp-1 +- printf '\n3\n' > exp-2 +- printf '4\n5\n' > exp-3 ++ split -n 3 ---io-blksize=$blksize $file > out || fail=1 ++ split -n 1/3 ---io-blksize=$blksize $file > b1 || fail=1 ++ split -n 2/3 ---io-blksize=$blksize $file > b2 || fail=1 ++ split -n 3/3 ---io-blksize=$blksize $file > b3 || fail=1 + +- compare exp-1 xaa || fail=1 +- compare exp-2 xab || fail=1 +- compare exp-3 xac || fail=1 +- ;; +- esac ++ case $file in ++ input) ++ compare exp-1 xaa || fail=1 ++ compare exp-2 xab || fail=1 ++ compare exp-3 xac || fail=1 ++ ;; ++ esac + +- compare xaa b1 || fail=1 +- compare xab b2 || fail=1 +- compare xac b3 || fail=1 +- cat xaa xab xac | compare - $file || fail=1 +- test -f xad && fail=1 ++ compare xaa b1 || fail=1 ++ compare xab b2 || fail=1 ++ compare xac b3 || fail=1 ++ cat xaa xab xac | compare - $file || fail=1 ++ test -f xad && fail=1 ++ done + done + + Exit $fail +-- +2.26.2 + diff --git a/SOURCES/coreutils-8.32-stat-exfat.patch b/SOURCES/coreutils-8.32-stat-exfat.patch new file mode 100644 index 0000000..ea8d0cd --- /dev/null +++ b/SOURCES/coreutils-8.32-stat-exfat.patch @@ -0,0 +1,32 @@ +From b87f944c87ffe04db6e5476b007a8e4979de933d Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +Date: Thu, 18 Feb 2021 11:18:04 +0100 +Subject: [PATCH] stat,tail: add support for the exfat file system + +Bug: https://bugzilla.redhat.com/1921427 + +* src/stat.c (human_fstype): Add case for the 'exfat' file system type. +Fixes https://bugs.gnu.org/46613 + +Upstream-commit: a5e0d8f387e81e854427addbbaf2504541bbf4b9 +Signed-off-by: Kamil Dudka +--- + src/stat.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/stat.c b/src/stat.c +index 8cd69da..4e1c8e3 100644 +--- a/src/stat.c ++++ b/src/stat.c +@@ -361,6 +361,8 @@ human_fstype (STRUCT_STATVFS const *statfsbuf) + return "efs"; + case S_MAGIC_EROFS_V1: /* 0xE0F5E1E2 local */ + return "erofs"; ++ case S_MAGIC_EXFAT: /* 0x2011BAB0 local */ ++ return "exfat"; + case S_MAGIC_EXFS: /* 0x45584653 local */ + return "exfs"; + case S_MAGIC_EXOFS: /* 0x5DF5 local */ +-- +2.26.2 + diff --git a/SOURCES/coreutils-8.32-tail-use-poll.patch b/SOURCES/coreutils-8.32-tail-use-poll.patch new file mode 100644 index 0000000..ed3a8f3 --- /dev/null +++ b/SOURCES/coreutils-8.32-tail-use-poll.patch @@ -0,0 +1,181 @@ +From c7a04cef4075da864a3468e63a5bb79334d8f556 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Sat, 26 Jun 2021 18:23:52 -0700 +Subject: [PATCH] tail: use poll, not select +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This fixes an unlikely stack out-of-bounds write reported by +Stepan Broz via Kamil Dudka (Bug#49209). +* src/tail.c: Do not include . +[!_AIX]: Include poll.h. +(check_output_alive) [!_AIX]: Use poll instead of select. +(tail_forever_inotify): Likewise. Simplify logic, as there is no +need for a ‘while (len <= evbuf_off)’ loop. + +Upstream-commit: da0d448bca62c6305fc432f67e2c5ccc2da75346 +Signed-off-by: Kamil Dudka +--- + src/tail.c | 100 +++++++++++++++++++++-------------------------------- + 1 file changed, 39 insertions(+), 61 deletions(-) + +diff --git a/src/tail.c b/src/tail.c +index 1c88723..5b4f21a 100644 +--- a/src/tail.c ++++ b/src/tail.c +@@ -28,12 +28,9 @@ + #include + #include + #include +-#include ++#include + #include + #include +-#ifdef _AIX +-# include +-#endif + + #include "system.h" + #include "argmatch.h" +@@ -351,27 +348,12 @@ check_output_alive (void) + if (! monitor_output) + return; + +-#ifdef _AIX +- /* select on AIX was seen to give a readable event immediately. */ + struct pollfd pfd; + pfd.fd = STDOUT_FILENO; + pfd.events = POLLERR; + + if (poll (&pfd, 1, 0) >= 0 && (pfd.revents & POLLERR)) + die_pipe (); +-#else +- struct timeval delay; +- delay.tv_sec = delay.tv_usec = 0; +- +- fd_set rfd; +- FD_ZERO (&rfd); +- FD_SET (STDOUT_FILENO, &rfd); +- +- /* readable event on STDOUT is equivalent to POLLERR, +- and implies an error condition on output like broken pipe. */ +- if (select (STDOUT_FILENO + 1, &rfd, NULL, NULL, &delay) == 1) +- die_pipe (); +-#endif + } + + static bool +@@ -1612,7 +1594,7 @@ tail_forever_inotify (int wd, struct File_spec *f, size_t n_files, + /* Wait for inotify events and handle them. Events on directories + ensure that watched files can be re-added when following by name. + This loop blocks on the 'safe_read' call until a new event is notified. +- But when --pid=P is specified, tail usually waits via the select. */ ++ But when --pid=P is specified, tail usually waits via poll. */ + while (1) + { + struct File_spec *fspec; +@@ -1629,54 +1611,51 @@ tail_forever_inotify (int wd, struct File_spec *f, size_t n_files, + return false; + } + +- /* When watching a PID, ensure that a read from WD will not block +- indefinitely. */ +- while (len <= evbuf_off) ++ if (len <= evbuf_off) + { +- struct timeval delay; /* how long to wait for file changes. */ ++ /* Poll for inotify events. When watching a PID, ensure ++ that a read from WD will not block indefinitely. ++ If MONITOR_OUTPUT, also poll for a broken output pipe. */ + +- if (pid) ++ int file_change; ++ struct pollfd pfd[2]; ++ do + { +- if (writer_is_dead) +- exit (EXIT_SUCCESS); ++ /* How many ms to wait for changes. -1 means wait forever. */ ++ int delay = -1; + +- writer_is_dead = (kill (pid, 0) != 0 && errno != EPERM); +- +- if (writer_is_dead) +- delay.tv_sec = delay.tv_usec = 0; +- else ++ if (pid) + { +- delay.tv_sec = (time_t) sleep_interval; +- delay.tv_usec = 1000000 * (sleep_interval - delay.tv_sec); ++ if (writer_is_dead) ++ exit (EXIT_SUCCESS); ++ ++ writer_is_dead = (kill (pid, 0) != 0 && errno != EPERM); ++ ++ if (writer_is_dead || sleep_interval <= 0) ++ delay = 0; ++ else if (sleep_interval < INT_MAX / 1000 - 1) ++ { ++ /* delay = ceil (sleep_interval * 1000), sans libm. */ ++ double ddelay = sleep_interval * 1000; ++ delay = ddelay; ++ delay += delay < ddelay; ++ } + } ++ ++ pfd[0].fd = wd; ++ pfd[0].events = POLLIN; ++ pfd[1].fd = STDOUT_FILENO; ++ pfd[1].events = pfd[1].revents = 0; ++ file_change = poll (pfd, monitor_output + 1, delay); + } ++ while (file_change == 0); + +- fd_set rfd; +- FD_ZERO (&rfd); +- FD_SET (wd, &rfd); +- if (monitor_output) +- FD_SET (STDOUT_FILENO, &rfd); +- +- int file_change = select (MAX (wd, STDOUT_FILENO) + 1, +- &rfd, NULL, NULL, pid ? &delay: NULL); +- +- if (file_change == 0) +- continue; +- else if (file_change == -1) +- die (EXIT_FAILURE, errno, +- _("error waiting for inotify and output events")); +- else if (FD_ISSET (STDOUT_FILENO, &rfd)) +- { +- /* readable event on STDOUT is equivalent to POLLERR, +- and implies an error on output like broken pipe. */ +- die_pipe (); +- } +- else +- break; +- } ++ if (file_change < 0) ++ die (EXIT_FAILURE, errno, ++ _("error waiting for inotify and output events")); ++ if (pfd[1].revents) ++ die_pipe (); + +- if (len <= evbuf_off) +- { + len = safe_read (wd, evbuf, evlen); + evbuf_off = 0; + +@@ -2437,8 +2416,7 @@ main (int argc, char **argv) + if (forever && ignore_fifo_and_pipe (F, n_files)) + { + /* If stdout is a fifo or pipe, then monitor it +- so that we exit if the reader goes away. +- Note select() on a regular file is always readable. */ ++ so that we exit if the reader goes away. */ + struct stat out_stat; + if (fstat (STDOUT_FILENO, &out_stat) < 0) + die (EXIT_FAILURE, errno, _("standard output")); +-- +2.31.1 + diff --git a/SOURCES/coreutils-8.32-tests-false-positives.patch b/SOURCES/coreutils-8.32-tests-false-positives.patch new file mode 100644 index 0000000..0b0a301 --- /dev/null +++ b/SOURCES/coreutils-8.32-tests-false-positives.patch @@ -0,0 +1,99 @@ +From fc6318841f008dadc1e7c93e539f10d24aa83e90 Mon Sep 17 00:00:00 2001 +From: Bernhard Voelker +Date: Wed, 21 Apr 2021 00:12:00 +0200 +Subject: [PATCH 1/2] tests: fix FP in ls/stat-free-color.sh + +On newer systems like Fedora 34 and openSUSE Tumbleweed, ls(1) calls +newfstatat(STDOUT_FILENO, ...), but only when there is something to +output. + +* tests/ls/stat-free-color.sh: Add -a option to the reference invocation +of ls, thus enforcing something gets output. + +Upstream-commit: b7091093bb6505c33279f9bc940b2e94763a6e5d +Signed-off-by: Kamil Dudka +--- + tests/ls/stat-free-color.sh | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/tests/ls/stat-free-color.sh b/tests/ls/stat-free-color.sh +index 00942f7..87bed1c 100755 +--- a/tests/ls/stat-free-color.sh ++++ b/tests/ls/stat-free-color.sh +@@ -56,12 +56,14 @@ eval $(dircolors -b color-without-stat) + # The system may perform additional stat-like calls before main. + # Furthermore, underlying library functions may also implicitly + # add an extra stat call, e.g. opendir since glibc-2.21-360-g46f894d. +-# To avoid counting those, first get a baseline count for running +-# ls with one empty directory argument. Then, compare that with the +-# invocation under test. ++# Finally, ls(1) makes a stat call for stdout, but only in the case ++# when there is something to output. ++# To get the comparison right, first get a baseline count for running ++# 'ls -a' with one empty directory argument. Then, compare that with ++# the invocation under test. + mkdir d || framework_failure_ + +-strace -q -o log1 -e $stats ls --color=always d || fail=1 ++strace -q -o log1 -e $stats ls -a --color=always d || fail=1 + n_stat1=$(grep -vF '+++' log1 | wc -l) || framework_failure_ + + test $n_stat1 = 0 \ +-- +2.31.1 + + +From c16ca58f17a088e925c0d1c4015c48332c380a00 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Sun, 9 May 2021 23:41:00 +0100 +Subject: [PATCH 2/2] tests: fix tests/cp/sparse-2.sh false failure on some + systems + +* tests/cp/sparse-2.sh: Double check cp --sparse=always, +with dd conv=sparse, in the case where the former didn't +create a sparse file. Now that this test is being newly run +on macos, we're seeing a failure due to seek() not creating +holes on apfs unless the size is >= 16MiB. + +Upstream-commit: 6b499720fecae935dc00e236d6aefe94d9010482 +Signed-off-by: Kamil Dudka +--- + tests/cp/fiemap-2.sh | 13 ++++++++++--- + 1 file changed, 10 insertions(+), 3 deletions(-) + +diff --git a/tests/cp/fiemap-2.sh b/tests/cp/fiemap-2.sh +index 548a376..e20ce54 100755 +--- a/tests/cp/fiemap-2.sh ++++ b/tests/cp/fiemap-2.sh +@@ -17,7 +17,7 @@ + # along with this program. If not, see . + + . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +-print_ver_ cp ++print_ver_ cp stat dd + + # Require a fiemap-enabled FS. + touch fiemap_chk # check a file rather than current dir for best coverage +@@ -46,10 +46,17 @@ dd bs=1k seek=1 of=k count=255 < /dev/zero || framework_failure_ + + # cp should detect the all-zero blocks and convert some of them to holes. + # How many it detects/converts currently depends on io_blksize. +-# Currently, on my F14/ext4 desktop, this K starts off with size 256KiB, ++# Currently, on my F14/ext4 desktop, this K file starts off with size 256KiB, + # (note that the K in the preceding test starts off with size 4KiB). + # cp from coreutils-8.9 with --sparse=always reduces the size to 32KiB. + cp --sparse=always k k2 || fail=1 +-test $(stat -c %b k2) -lt $(stat -c %b k) || fail=1 ++if test $(stat -c %b k2) -ge $(stat -c %b k); then ++ # If not sparse, then double check by creating with dd ++ # as we're not guaranteed that seek will create a hole. ++ # apfs on darwin 19.2.0 for example was seen to not to create holes < 16MiB. ++ hole_size=$(stat -c %o k2) || framework_failure_ ++ dd if=k of=k2.dd bs=$hole_size conv=sparse || framework_failure_ ++ test $(stat -c %b k2) -eq $(stat -c %b k2.dd) || fail=1 ++fi + + Exit $fail +-- +2.31.1 + diff --git a/SOURCES/coreutils-8.4-mkdir-modenote.patch b/SOURCES/coreutils-8.4-mkdir-modenote.patch new file mode 100644 index 0000000..51a129e --- /dev/null +++ b/SOURCES/coreutils-8.4-mkdir-modenote.patch @@ -0,0 +1,13 @@ +diff --git a/doc/coreutils.texi b/doc/coreutils.texi +index 400e135..47e4480 100644 +--- a/doc/coreutils.texi ++++ b/doc/coreutils.texi +@@ -10074,6 +10074,8 @@ incorrect. @xref{Directory Setuid and Setgid}, for how the + set-user-ID and set-group-ID bits of directories are inherited unless + overridden in this way. + ++Note: The @option{--mode},@option{-m} option only applies to the right-most directories listed on the command line. When combined with @option{--parents}, @option{-p} option, any parent directories are created with @samp{u+wx} modified by umask. ++ + @item -p + @itemx --parents + @opindex -p diff --git a/SOURCES/coreutils-9.0-autofs-no-mount.patch b/SOURCES/coreutils-9.0-autofs-no-mount.patch new file mode 100644 index 0000000..b6d6523 --- /dev/null +++ b/SOURCES/coreutils-9.0-autofs-no-mount.patch @@ -0,0 +1,87 @@ +From f4422844dbcd839ce486bcbc15b7bd5b72c9198d Mon Sep 17 00:00:00 2001 +From: Rohan Sable +Date: Mon, 7 Mar 2022 14:14:13 +0000 +Subject: [PATCH 1/2] ls: avoid triggering automounts + +statx() has different defaults wrt automounting +compared to stat() or lstat(), so explicitly +set the AT_NO_AUTOMOUNT flag to suppress that behavior, +and avoid unintended operations or potential errors. + +* src/ls.c (do_statx): Pass AT_NO_AUTOMOUNT to avoid this behavior. +Fixes https://bugs.gnu.org/54286 + +Signed-off-by: Rohan Sable + +Upstream-commit: 85c975df2c25bd799370b04bb294e568e001102f +Signed-off-by: Kamil Dudka +--- + src/ls.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/ls.c b/src/ls.c +index 1047801..fe0e9f8 100644 +--- a/src/ls.c ++++ b/src/ls.c +@@ -1175,7 +1175,7 @@ do_statx (int fd, char const *name, struct stat *st, int flags, + { + struct statx stx; + bool want_btime = mask & STATX_BTIME; +- int ret = statx (fd, name, flags, mask, &stx); ++ int ret = statx (fd, name, flags | AT_NO_AUTOMOUNT, mask, &stx); + if (ret >= 0) + { + statx_to_stat (&stx, st); +-- +2.34.1 + + +From 3d227f9e4f3fe806064721e4b9451ee06526bc80 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Mon, 7 Mar 2022 23:29:20 +0000 +Subject: [PATCH 2/2] stat: only automount with --cached=never + +Revert to the default behavior before the introduction of statx(). + +* src/stat.c (do_stat): Set AT_NO_AUTOMOUNT without --cached=never. +* doc/coreutils.texi (stat invocation): Mention the automount +behavior with --cached=never. + +Fixes https://bugs.gnu.org/54287 + +Upstream-commit: 92cb8427c537f37edd43c5cef1909585201372ab +Signed-off-by: Kamil Dudka +--- + doc/coreutils.texi | 1 + + src/stat.c | 3 +++ + 2 files changed, 4 insertions(+) + +diff --git a/doc/coreutils.texi b/doc/coreutils.texi +index 19b535c..0f5c16a 100644 +--- a/doc/coreutils.texi ++++ b/doc/coreutils.texi +@@ -12564,6 +12564,7 @@ Always read the already cached attributes if available. + + @item never + Always sychronize with the latest file system attributes. ++This also mounts automounted files. + + @item default + Leave the caching behavior to the underlying file system. +diff --git a/src/stat.c b/src/stat.c +index 0c34501..803340a 100644 +--- a/src/stat.c ++++ b/src/stat.c +@@ -1381,6 +1381,9 @@ do_stat (char const *filename, char const *format, char const *format2) + else if (force_sync) + flags |= AT_STATX_FORCE_SYNC; + ++ if (! force_sync) ++ flags |= AT_NO_AUTOMOUNT; ++ + fd = statx (fd, pathname, flags, format_to_mask (format), &stx); + if (fd < 0) + { +-- +2.34.1 + diff --git a/SOURCES/coreutils-colorls.csh b/SOURCES/coreutils-colorls.csh new file mode 100755 index 0000000..66ec2fa --- /dev/null +++ b/SOURCES/coreutils-colorls.csh @@ -0,0 +1,63 @@ +# skip everything for non-interactive shells +if (! $?prompt) exit + +# color-ls initialization +if ( $?USER_LS_COLORS ) then + if ( "$USER_LS_COLORS" != "" ) then + #when USER_LS_COLORS defined do not override user + #specified LS_COLORS and use them + goto finish + endif +endif + +alias ll 'ls -l' +alias l. 'ls -d .*' +set COLORS=/etc/DIR_COLORS + +if ($?TERM) then + if ( -e "/etc/DIR_COLORS.$TERM" ) then + set COLORS="/etc/DIR_COLORS.$TERM" + endif +endif +if ( -f ~/.dircolors ) set COLORS=~/.dircolors +if ( -f ~/.dir_colors ) set COLORS=~/.dir_colors +if ($?TERM) then + if ( -f ~/.dircolors."$TERM" ) set COLORS=~/.dircolors."$TERM" + if ( -f ~/.dir_colors."$TERM" ) set COLORS=~/.dir_colors."$TERM" +endif +set INCLUDE="`/usr/bin/cat "$COLORS" | /usr/bin/grep '^INCLUDE' | /usr/bin/cut -d ' ' -f2-`" + +if ( ! -e "$COLORS" ) exit + +set _tmp="`/usr/bin/mktemp .colorlsXXX -q --tmpdir=/tmp`" +#if mktemp fails, exit when include was active, otherwise use $COLORS file +if ( "$_tmp" == '' ) then + if ( "$INCLUDE" == '' ) then + eval "`/usr/bin/dircolors -c $COLORS`" + endif + goto cleanup +endif + +if ( "$INCLUDE" != '' ) /usr/bin/cat "$INCLUDE" >> $_tmp +/usr/bin/grep -v '^INCLUDE' "$COLORS" >> $_tmp + +eval "`/usr/bin/dircolors -c $_tmp`" + +/usr/bin/rm -f $_tmp + +if ( "$LS_COLORS" == '' ) exit +cleanup: +set color_none=`/usr/bin/sed -n '/^COLOR.*none/Ip' < $COLORS` +if ( "$color_none" != '' ) then + unset color_none + exit +endif +unset color_none +unset _tmp +unset INCLUDE +unset COLORS + +finish: +alias ll 'ls -l --color=auto' +alias l. 'ls -d .* --color=auto' +alias ls 'ls --color=auto' diff --git a/SOURCES/coreutils-colorls.sh b/SOURCES/coreutils-colorls.sh new file mode 100755 index 0000000..5162f1e --- /dev/null +++ b/SOURCES/coreutils-colorls.sh @@ -0,0 +1,53 @@ +# color-ls initialization + +# Skip all for noninteractive shells. +[ ! -t 0 ] && return + +#when USER_LS_COLORS defined do not override user LS_COLORS, but use them. +if [ -z "$USER_LS_COLORS" ]; then + + alias ll='ls -l' 2>/dev/null + alias l.='ls -d .*' 2>/dev/null + + INCLUDE= + COLORS= + + for colors in "$HOME/.dir_colors.$TERM" "$HOME/.dircolors.$TERM" \ + "$HOME/.dir_colors" "$HOME/.dircolors"; do + [ -e "$colors" ] && COLORS="$colors" && \ + INCLUDE="`/usr/bin/cat "$COLORS" | /usr/bin/grep '^INCLUDE' | /usr/bin/cut -d ' ' -f2-`" && \ + break + done + + [ -z "$COLORS" ] && [ -e "/etc/DIR_COLORS.$TERM" ] && \ + COLORS="/etc/DIR_COLORS.$TERM" + + [ -z "$COLORS" ] && [ -e "/etc/DIR_COLORS" ] && \ + COLORS="/etc/DIR_COLORS" + + # Existence of $COLORS already checked above. + [ -n "$COLORS" ] || return + + if [ -e "$INCLUDE" ]; + then + TMP="`/usr/bin/mktemp .colorlsXXX -q --tmpdir=/tmp`" + [ -z "$TMP" ] && return + + /usr/bin/cat "$INCLUDE" >> $TMP + /usr/bin/grep -v '^INCLUDE' "$COLORS" >> $TMP + + eval "`/usr/bin/dircolors --sh $TMP 2>/dev/null`" + /usr/bin/rm -f $TMP + else + eval "`/usr/bin/dircolors --sh $COLORS 2>/dev/null`" + fi + + [ -z "$LS_COLORS" ] && return + /usr/bin/grep -qi "^COLOR.*none" $COLORS >/dev/null 2>/dev/null && return +fi + +unset TMP COLORS INCLUDE + +alias ll='ls -l --color=auto' 2>/dev/null +alias l.='ls -d .* --color=auto' 2>/dev/null +alias ls='ls --color=auto' 2>/dev/null diff --git a/SOURCES/coreutils-df-direct.patch b/SOURCES/coreutils-df-direct.patch new file mode 100644 index 0000000..88015a1 --- /dev/null +++ b/SOURCES/coreutils-df-direct.patch @@ -0,0 +1,187 @@ +From 6e36198f10a2f63b89c89ebb5d5c185b20fb3a63 Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +Date: Mon, 29 Mar 2010 17:20:34 +0000 +Subject: [PATCH] coreutils-df-direct.patch + +--- + doc/coreutils.texi | 7 ++++++ + src/df.c | 34 ++++++++++++++++++++++++++-- + tests/df/direct.sh | 55 ++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 94 insertions(+), 2 deletions(-) + create mode 100755 tests/df/direct.sh + +diff --git a/doc/coreutils.texi b/doc/coreutils.texi +index 5b9a597..6810c15 100644 +--- a/doc/coreutils.texi ++++ b/doc/coreutils.texi +@@ -11898,6 +11898,13 @@ some systems (notably SunOS), doing this yields more up to date results, + but in general this option makes @command{df} much slower, especially when + there are many or very busy file systems. + ++@item --direct ++@opindex --direct ++@cindex direct statfs for a file ++Do not resolve mount point and show statistics directly for a file. It can be ++especially useful for NFS mount points if there is a boundary between two ++storage policies behind the mount point. ++ + @item --total + @opindex --total + @cindex grand total of disk size, usage and available space +diff --git a/src/df.c b/src/df.c +index 48025b9..c8efa5b 100644 +--- a/src/df.c ++++ b/src/df.c +@@ -125,6 +125,9 @@ static bool print_type; + /* If true, print a grand total at the end. */ + static bool print_grand_total; + ++/* If true, show statistics for a file instead of mount point. */ ++static bool direct_statfs; ++ + /* Grand total data. */ + static struct fs_usage grand_fsu; + +@@ -252,13 +255,15 @@ enum + NO_SYNC_OPTION = CHAR_MAX + 1, + SYNC_OPTION, + TOTAL_OPTION, +- OUTPUT_OPTION ++ OUTPUT_OPTION, ++ DIRECT_OPTION + }; + + static struct option const long_options[] = + { + {"all", no_argument, NULL, 'a'}, + {"block-size", required_argument, NULL, 'B'}, ++ {"direct", no_argument, NULL, DIRECT_OPTION}, + {"inodes", no_argument, NULL, 'i'}, + {"human-readable", no_argument, NULL, 'h'}, + {"si", no_argument, NULL, 'H'}, +@@ -561,7 +566,10 @@ get_header (void) + for (col = 0; col < ncolumns; col++) + { + char *cell = NULL; +- char const *header = _(columns[col]->caption); ++ char const *header = (columns[col]->field == TARGET_FIELD ++ && direct_statfs)? ++ _("File") : ++ _(columns[col]->caption); + + if (columns[col]->field == SIZE_FIELD + && (header_mode == DEFAULT_MODE +@@ -1464,6 +1472,17 @@ get_point (const char *point, const struct stat *statp) + static void + get_entry (char const *name, struct stat const *statp) + { ++ if (direct_statfs) ++ { ++ char *resolved = canonicalize_file_name (name); ++ if (resolved) ++ { ++ get_dev (NULL, resolved, name, NULL, NULL, false, false, NULL, false); ++ free (resolved); ++ return; ++ } ++ } ++ + if ((S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode)) + && get_disk (name)) + return; +@@ -1534,6 +1553,7 @@ or all file systems by default.\n\ + -B, --block-size=SIZE scale sizes by SIZE before printing them; e.g.,\n\ + '-BM' prints sizes in units of 1,048,576 bytes;\n\ + see SIZE format below\n\ ++ --direct show statistics for a file instead of mount point\n\ + -h, --human-readable print sizes in powers of 1024 (e.g., 1023M)\n\ + -H, --si print sizes in powers of 1000 (e.g., 1.1G)\n\ + "), stdout); +@@ -1624,6 +1644,9 @@ main (int argc, char **argv) + xstrtol_fatal (e, oi, c, long_options, optarg); + } + break; ++ case DIRECT_OPTION: ++ direct_statfs = true; ++ break; + case 'i': + if (header_mode == OUTPUT_MODE) + { +@@ -1720,6 +1743,13 @@ main (int argc, char **argv) + } + } + ++ if (direct_statfs && show_local_fs) ++ { ++ error (0, 0, _("options --direct and --local (-l) are mutually " ++ "exclusive")); ++ usage (EXIT_FAILURE); ++ } ++ + if (human_output_opts == -1) + { + if (posix_format) +diff --git a/tests/df/direct.sh b/tests/df/direct.sh +new file mode 100755 +index 0000000..8e4cfb8 +--- /dev/null ++++ b/tests/df/direct.sh +@@ -0,0 +1,55 @@ ++#!/bin/sh ++# Ensure "df --direct" works as documented ++ ++# Copyright (C) 2010 Free Software Foundation, Inc. ++ ++# This program is free software: you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation, either version 3 of the License, or ++# (at your option) any later version. ++ ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++ ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++ ++. "${srcdir=.}/init.sh"; path_prepend_ ../src ++print_ver_ df ++ ++df || skip_ "df fails" ++ ++DIR=`pwd` || framework_failure ++FILE="$DIR/file" ++touch "$FILE" || framework_failure ++echo "$FILE" > file_exp || framework_failure ++echo "Mounted on" > header_mounted_exp || framework_failure ++echo "File" > header_file_exp || framework_failure ++ ++fail=0 ++ ++df --portability "$FILE" > df_out || fail=1 ++df --portability --direct "$FILE" > df_direct_out || fail=1 ++df --portability --direct --local "$FILE" > /dev/null 2>&1 && fail=1 ++ ++# check df header ++$AWK '{ if (NR==1) print $6 " " $7; }' df_out > header_mounted_out \ ++ || framework_failure ++$AWK '{ if (NR==1) print $6; }' df_direct_out > header_file_out \ ++ || framework_failure ++compare header_mounted_out header_mounted_exp || fail=1 ++compare header_file_out header_file_exp || fail=1 ++ ++# check df output (without --direct) ++$AWK '{ if (NR==2) print $6; }' df_out > file_out \ ++ || framework_failure ++compare file_out file_exp && fail=1 ++ ++# check df output (with --direct) ++$AWK '{ if (NR==2) print $6; }' df_direct_out > file_out \ ++ || framework_failure ++compare file_out file_exp || fail=1 ++ ++Exit $fail +-- +2.31.1 + diff --git a/SOURCES/coreutils-getgrouplist.patch b/SOURCES/coreutils-getgrouplist.patch new file mode 100644 index 0000000..5349337 --- /dev/null +++ b/SOURCES/coreutils-getgrouplist.patch @@ -0,0 +1,94 @@ +diff --git a/lib/getugroups.c b/lib/getugroups.c +index 299bae6..8ece29b 100644 +--- a/lib/getugroups.c ++++ b/lib/getugroups.c +@@ -19,6 +19,9 @@ + + #include + ++/* We do not need this code if getgrouplist(3) is available. */ ++#ifndef HAVE_GETGROUPLIST ++ + #include "getugroups.h" + + #include +@@ -126,3 +129,4 @@ getugroups (int maxcount, gid_t *grouplist, char const *username, + } + + #endif /* HAVE_GRP_H */ ++#endif /* have getgrouplist */ +diff --git a/lib/mgetgroups.c b/lib/mgetgroups.c +index 76474c2..0a9d221 100644 +--- a/lib/mgetgroups.c ++++ b/lib/mgetgroups.c +@@ -31,6 +31,7 @@ + #endif + + #include "getugroups.h" ++#include "xalloc.h" + #include "xalloc-oversized.h" + + /* Work around an incompatibility of OS X 10.11: getgrouplist +@@ -121,9 +122,17 @@ mgetgroups (char const *username, gid_t gid, gid_t **groups) + /* else no username, so fall through and use getgroups. */ + #endif + +- max_n_groups = (username +- ? getugroups (0, NULL, username, gid) +- : getgroups (0, NULL)); ++ if (!username) ++ max_n_groups = getgroups(0, NULL); ++ else ++ { ++#ifdef HAVE_GETGROUPLIST ++ max_n_groups = 0; ++ getgrouplist (username, gid, NULL, &max_n_groups); ++#else ++ max_n_groups = getugroups (0, NULL, username, gid); ++#endif ++ } + + /* If we failed to count groups because there is no supplemental + group support, then return an array containing just GID. +@@ -145,10 +154,25 @@ mgetgroups (char const *username, gid_t gid, gid_t **groups) + if (g == NULL) + return -1; + +- ng = (username +- ? getugroups (max_n_groups, g, username, gid) +- : getgroups (max_n_groups - (gid != (gid_t) -1), +- g + (gid != (gid_t) -1))); ++ if (!username) ++ ng = getgroups (max_n_groups - (gid != (gid_t)-1), g + (gid != (gid_t)-1)); ++ else ++ { ++#ifdef HAVE_GETGROUPLIST ++ int e; ++ ng = max_n_groups; ++ while ((e = getgrouplist (username, gid, g, &ng)) == -1 ++ && ng > max_n_groups) ++ { ++ max_n_groups = ng; ++ g = xrealloc (g, max_n_groups * sizeof (GETGROUPS_T)); ++ } ++ if (e == -1) ++ ng = -1; ++#else ++ ng = getugroups (max_n_groups, g, username, gid); ++#endif ++ } + + if (ng < 0) + { +diff --git a/m4/jm-macros.m4 b/m4/jm-macros.m4 +index 62777c7..5180243 100644 +--- a/m4/jm-macros.m4 ++++ b/m4/jm-macros.m4 +@@ -82,6 +82,7 @@ AC_DEFUN([coreutils_MACROS], + fchown + fchmod + ftruncate ++ getgrouplist + iswspace + mkfifo + mbrlen diff --git a/SOURCES/coreutils-i18n-cut-old.patch b/SOURCES/coreutils-i18n-cut-old.patch new file mode 100644 index 0000000..757ee0f --- /dev/null +++ b/SOURCES/coreutils-i18n-cut-old.patch @@ -0,0 +1,565 @@ +diff --git a/src/cut.c b/src/cut.c +index 7ab6be4..022d0ad 100644 +--- a/src/cut.c ++++ b/src/cut.c +@@ -28,6 +28,11 @@ + #include + #include + #include ++ ++/* Get mbstate_t, mbrtowc(). */ ++#if HAVE_WCHAR_H ++# include ++#endif + #include "system.h" + + #include "error.h" +@@ -38,6 +43,18 @@ + + #include "set-fields.h" + ++/* MB_LEN_MAX is incorrectly defined to be 1 in at least one GCC ++ installation; work around this configuration error. */ ++#if !defined MB_LEN_MAX || MB_LEN_MAX < 2 ++# undef MB_LEN_MAX ++# define MB_LEN_MAX 16 ++#endif ++ ++/* Some systems, like BeOS, have multibyte encodings but lack mbstate_t. */ ++#if HAVE_MBRTOWC && defined mbstate_t ++# define mbrtowc(pwc, s, n, ps) (mbrtowc) (pwc, s, n, 0) ++#endif ++ + /* The official name of this program (e.g., no 'g' prefix). */ + #define PROGRAM_NAME "cut" + +@@ -54,6 +71,52 @@ + } \ + while (0) + ++/* Refill the buffer BUF to get a multibyte character. */ ++#define REFILL_BUFFER(BUF, BUFPOS, BUFLEN, STREAM) \ ++ do \ ++ { \ ++ if (BUFLEN < MB_LEN_MAX && !feof (STREAM) && !ferror (STREAM)) \ ++ { \ ++ memmove (BUF, BUFPOS, BUFLEN); \ ++ BUFLEN += fread (BUF + BUFLEN, sizeof(char), BUFSIZ, STREAM); \ ++ BUFPOS = BUF; \ ++ } \ ++ } \ ++ while (0) ++ ++/* Get wide character on BUFPOS. BUFPOS is not included after that. ++ If byte sequence is not valid as a character, CONVFAIL is true. Otherwise false. */ ++#define GET_NEXT_WC_FROM_BUFFER(WC, BUFPOS, BUFLEN, MBLENGTH, STATE, CONVFAIL) \ ++ do \ ++ { \ ++ mbstate_t state_bak; \ ++ \ ++ if (BUFLEN < 1) \ ++ { \ ++ WC = WEOF; \ ++ break; \ ++ } \ ++ \ ++ /* Get a wide character. */ \ ++ CONVFAIL = false; \ ++ state_bak = STATE; \ ++ MBLENGTH = mbrtowc ((wchar_t *)&WC, BUFPOS, BUFLEN, &STATE); \ ++ \ ++ switch (MBLENGTH) \ ++ { \ ++ case (size_t)-1: \ ++ case (size_t)-2: \ ++ CONVFAIL = true; \ ++ STATE = state_bak; \ ++ /* Fall througn. */ \ ++ \ ++ case 0: \ ++ MBLENGTH = 1; \ ++ break; \ ++ } \ ++ } \ ++ while (0) ++ + + /* Pointer inside RP. When checking if a byte or field is selected + by a finite range, we check if it is between CURRENT_RP.LO +@@ -61,6 +124,9 @@ + CURRENT_RP.HI then we make CURRENT_RP to point to the next range pair. */ + static struct field_range_pair *current_rp; + ++/* Length of the delimiter given as argument to -d. */ ++size_t delimlen; ++ + /* This buffer is used to support the semantics of the -s option + (or lack of same) when the specified field list includes (does + not include) the first field. In both of those cases, the entire +@@ -77,15 +143,25 @@ enum operating_mode + { + undefined_mode, + +- /* Output characters that are in the given bytes. */ ++ /* Output bytes that are at the given positions. */ + byte_mode, + ++ /* Output characters that are at the given positions. */ ++ character_mode, ++ + /* Output the given delimiter-separated fields. */ + field_mode + }; + + static enum operating_mode operating_mode; + ++/* If nonzero, when in byte mode, don't split multibyte characters. */ ++static int byte_mode_character_aware; ++ ++/* If nonzero, the function for single byte locale is work ++ if this program runs on multibyte locale. */ ++static int force_singlebyte_mode; ++ + /* If true do not output lines containing no delimiter characters. + Otherwise, all such lines are printed. This option is valid only + with field mode. */ +@@ -97,6 +173,9 @@ static bool complement; + + /* The delimiter character for field mode. */ + static unsigned char delim; ++#if HAVE_WCHAR_H ++static wchar_t wcdelim; ++#endif + + /* The delimiter for each line/record. */ + static unsigned char line_delim = '\n'; +@@ -164,7 +243,7 @@ Print selected parts of lines from each FILE to standard output.\n\ + -f, --fields=LIST select only these fields; also print any line\n\ + that contains no delimiter character, unless\n\ + the -s option is specified\n\ +- -n (ignored)\n\ ++ -n with -b: don't split multibyte characters\n\ + "), stdout); + fputs (_("\ + --complement complement the set of selected bytes, characters\n\ +@@ -280,6 +359,82 @@ cut_bytes (FILE *stream) + } + } + ++#if HAVE_MBRTOWC ++/* This function is in use for the following case. ++ ++ 1. Read from the stream STREAM, printing to standard output any selected ++ characters. ++ ++ 2. Read from stream STREAM, printing to standard output any selected bytes, ++ without splitting multibyte characters. */ ++ ++static void ++cut_characters_or_cut_bytes_no_split (FILE *stream) ++{ ++ uintmax_t idx; /* number of bytes or characters in the line so far. */ ++ char buf[MB_LEN_MAX + BUFSIZ]; /* For spooling a read byte sequence. */ ++ char *bufpos; /* Next read position of BUF. */ ++ size_t buflen; /* The length of the byte sequence in buf. */ ++ wint_t wc; /* A gotten wide character. */ ++ size_t mblength; /* The byte size of a multibyte character which shows ++ as same character as WC. */ ++ mbstate_t state; /* State of the stream. */ ++ bool convfail = false; /* true, when conversion failed. Otherwise false. */ ++ /* Whether to begin printing delimiters between ranges for the current line. ++ Set after we've begun printing data corresponding to the first range. */ ++ bool print_delimiter = false; ++ ++ idx = 0; ++ buflen = 0; ++ bufpos = buf; ++ memset (&state, '\0', sizeof(mbstate_t)); ++ ++ current_rp = frp; ++ ++ while (1) ++ { ++ REFILL_BUFFER (buf, bufpos, buflen, stream); ++ ++ GET_NEXT_WC_FROM_BUFFER (wc, bufpos, buflen, mblength, state, convfail); ++ (void) convfail; /* ignore unused */ ++ ++ if (wc == WEOF) ++ { ++ if (idx > 0) ++ putchar (line_delim); ++ break; ++ } ++ else if (wc == line_delim) ++ { ++ putchar (line_delim); ++ idx = 0; ++ print_delimiter = false; ++ current_rp = frp; ++ } ++ else ++ { ++ next_item (&idx); ++ if (print_kth (idx)) ++ { ++ if (output_delimiter_specified) ++ { ++ if (print_delimiter && is_range_start_index (idx)) ++ { ++ fwrite (output_delimiter_string, sizeof (char), ++ output_delimiter_length, stdout); ++ } ++ print_delimiter = true; ++ } ++ fwrite (bufpos, mblength, sizeof(char), stdout); ++ } ++ } ++ ++ buflen -= mblength; ++ bufpos += mblength; ++ } ++} ++#endif ++ + /* Read from stream STREAM, printing to standard output any selected fields. */ + + static void +@@ -425,13 +580,211 @@ cut_fields (FILE *stream) + } + } + ++#if HAVE_MBRTOWC ++static void ++cut_fields_mb (FILE *stream) ++{ ++ int c; ++ uintmax_t field_idx; ++ int found_any_selected_field; ++ int buffer_first_field; ++ int empty_input; ++ char buf[MB_LEN_MAX + BUFSIZ]; /* For spooling a read byte sequence. */ ++ char *bufpos; /* Next read position of BUF. */ ++ size_t buflen; /* The length of the byte sequence in buf. */ ++ wint_t wc = 0; /* A gotten wide character. */ ++ size_t mblength; /* The byte size of a multibyte character which shows ++ as same character as WC. */ ++ mbstate_t state; /* State of the stream. */ ++ bool convfail = false; /* true, when conversion failed. Otherwise false. */ ++ ++ current_rp = frp; ++ ++ found_any_selected_field = 0; ++ field_idx = 1; ++ bufpos = buf; ++ buflen = 0; ++ memset (&state, '\0', sizeof(mbstate_t)); ++ ++ c = getc (stream); ++ empty_input = (c == EOF); ++ if (c != EOF) ++ { ++ ungetc (c, stream); ++ wc = 0; ++ } ++ else ++ wc = WEOF; ++ ++ /* To support the semantics of the -s flag, we may have to buffer ++ all of the first field to determine whether it is `delimited.' ++ But that is unnecessary if all non-delimited lines must be printed ++ and the first field has been selected, or if non-delimited lines ++ must be suppressed and the first field has *not* been selected. ++ That is because a non-delimited line has exactly one field. */ ++ buffer_first_field = (suppress_non_delimited ^ !print_kth (1)); ++ ++ while (1) ++ { ++ if (field_idx == 1 && buffer_first_field) ++ { ++ int len = 0; ++ ++ while (1) ++ { ++ REFILL_BUFFER (buf, bufpos, buflen, stream); ++ ++ GET_NEXT_WC_FROM_BUFFER ++ (wc, bufpos, buflen, mblength, state, convfail); ++ ++ if (wc == WEOF) ++ break; ++ ++ field_1_buffer = xrealloc (field_1_buffer, len + mblength); ++ memcpy (field_1_buffer + len, bufpos, mblength); ++ len += mblength; ++ buflen -= mblength; ++ bufpos += mblength; ++ ++ if (!convfail && (wc == line_delim || wc == wcdelim)) ++ break; ++ } ++ ++ if (len <= 0 && wc == WEOF) ++ break; ++ ++ /* If the first field extends to the end of line (it is not ++ delimited) and we are printing all non-delimited lines, ++ print this one. */ ++ if (convfail || (!convfail && wc != wcdelim)) ++ { ++ if (suppress_non_delimited) ++ { ++ /* Empty. */ ++ } ++ else ++ { ++ fwrite (field_1_buffer, sizeof (char), len, stdout); ++ /* Make sure the output line is newline terminated. */ ++ if (convfail || (!convfail && wc != line_delim)) ++ putchar (line_delim); ++ } ++ continue; ++ } ++ ++ if (print_kth (1)) ++ { ++ /* Print the field, but not the trailing delimiter. */ ++ fwrite (field_1_buffer, sizeof (char), len - 1, stdout); ++ found_any_selected_field = 1; ++ } ++ next_item (&field_idx); ++ } ++ ++ if (wc != WEOF) ++ { ++ if (print_kth (field_idx)) ++ { ++ if (found_any_selected_field) ++ { ++ fwrite (output_delimiter_string, sizeof (char), ++ output_delimiter_length, stdout); ++ } ++ found_any_selected_field = 1; ++ } ++ ++ while (1) ++ { ++ REFILL_BUFFER (buf, bufpos, buflen, stream); ++ ++ GET_NEXT_WC_FROM_BUFFER ++ (wc, bufpos, buflen, mblength, state, convfail); ++ ++ if (wc == WEOF) ++ break; ++ else if (!convfail && (wc == wcdelim || wc == line_delim)) ++ { ++ buflen -= mblength; ++ bufpos += mblength; ++ break; ++ } ++ ++ if (print_kth (field_idx)) ++ fwrite (bufpos, mblength, sizeof(char), stdout); ++ ++ buflen -= mblength; ++ bufpos += mblength; ++ } ++ } ++ ++ if ((!convfail || wc == line_delim) && buflen < 1) ++ wc = WEOF; ++ ++ if (!convfail && wc == wcdelim) ++ next_item (&field_idx); ++ else if (wc == WEOF || (!convfail && wc == line_delim)) ++ { ++ if (found_any_selected_field ++ || (!empty_input && !(suppress_non_delimited && field_idx == 1))) ++ putchar (line_delim); ++ if (wc == WEOF) ++ break; ++ field_idx = 1; ++ current_rp = frp; ++ found_any_selected_field = 0; ++ } ++ } ++} ++#endif ++ + static void + cut_stream (FILE *stream) + { +- if (operating_mode == byte_mode) +- cut_bytes (stream); ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1 && !force_singlebyte_mode) ++ { ++ switch (operating_mode) ++ { ++ case byte_mode: ++ if (byte_mode_character_aware) ++ cut_characters_or_cut_bytes_no_split (stream); ++ else ++ cut_bytes (stream); ++ break; ++ ++ case character_mode: ++ cut_characters_or_cut_bytes_no_split (stream); ++ break; ++ ++ case field_mode: ++ if (delimlen == 1) ++ { ++ /* Check if we have utf8 multibyte locale, so we can use this ++ optimization because of uniqueness of characters, which is ++ not true for e.g. SJIS */ ++ char * loc = setlocale(LC_CTYPE, NULL); ++ if (loc && (strstr (loc, "UTF-8") || strstr (loc, "utf-8") || ++ strstr (loc, "UTF8") || strstr (loc, "utf8"))) ++ { ++ cut_fields (stream); ++ break; ++ } ++ } ++ cut_fields_mb (stream); ++ break; ++ ++ default: ++ abort (); ++ } ++ } + else +- cut_fields (stream); ++#endif ++ { ++ if (operating_mode == field_mode) ++ cut_fields (stream); ++ else ++ cut_bytes (stream); ++ } + } + + /* Process file FILE to standard output. +@@ -483,6 +836,7 @@ main (int argc, char **argv) + bool ok; + bool delim_specified = false; + char *spec_list_string IF_LINT ( = NULL); ++ char mbdelim[MB_LEN_MAX + 1]; + + initialize_main (&argc, &argv); + set_program_name (argv[0]); +@@ -505,7 +859,6 @@ main (int argc, char **argv) + switch (optc) + { + case 'b': +- case 'c': + /* Build the byte list. */ + if (operating_mode != undefined_mode) + FATAL_ERROR (_("only one type of list may be specified")); +@@ -513,6 +866,14 @@ main (int argc, char **argv) + spec_list_string = optarg; + break; + ++ case 'c': ++ /* Build the character list. */ ++ if (operating_mode != undefined_mode) ++ FATAL_ERROR (_("only one type of list may be specified")); ++ operating_mode = character_mode; ++ spec_list_string = optarg; ++ break; ++ + case 'f': + /* Build the field list. */ + if (operating_mode != undefined_mode) +@@ -524,10 +885,38 @@ main (int argc, char **argv) + case 'd': + /* New delimiter. */ + /* Interpret -d '' to mean 'use the NUL byte as the delimiter.' */ +- if (optarg[0] != '\0' && optarg[1] != '\0') +- FATAL_ERROR (_("the delimiter must be a single character")); +- delim = optarg[0]; +- delim_specified = true; ++ { ++#if HAVE_MBRTOWC ++ if(MB_CUR_MAX > 1) ++ { ++ mbstate_t state; ++ ++ memset (&state, '\0', sizeof(mbstate_t)); ++ delimlen = mbrtowc (&wcdelim, optarg, strnlen(optarg, MB_LEN_MAX), &state); ++ ++ if (delimlen == (size_t)-1 || delimlen == (size_t)-2) ++ ++force_singlebyte_mode; ++ else ++ { ++ delimlen = (delimlen < 1) ? 1 : delimlen; ++ if (wcdelim != L'\0' && *(optarg + delimlen) != '\0') ++ FATAL_ERROR (_("the delimiter must be a single character")); ++ memcpy (mbdelim, optarg, delimlen); ++ mbdelim[delimlen] = '\0'; ++ if (delimlen == 1) ++ delim = *optarg; ++ } ++ } ++ ++ if (MB_CUR_MAX <= 1 || force_singlebyte_mode) ++#endif ++ { ++ if (optarg[0] != '\0' && optarg[1] != '\0') ++ FATAL_ERROR (_("the delimiter must be a single character")); ++ delim = (unsigned char) optarg[0]; ++ } ++ delim_specified = true; ++ } + break; + + case OUTPUT_DELIMITER_OPTION: +@@ -540,6 +929,7 @@ main (int argc, char **argv) + break; + + case 'n': ++ byte_mode_character_aware = 1; + break; + + case 's': +@@ -579,15 +969,34 @@ main (int argc, char **argv) + | (complement ? SETFLD_COMPLEMENT : 0) ); + + if (!delim_specified) +- delim = '\t'; ++ { ++ delim = '\t'; ++#ifdef HAVE_MBRTOWC ++ wcdelim = L'\t'; ++ mbdelim[0] = '\t'; ++ mbdelim[1] = '\0'; ++ delimlen = 1; ++#endif ++ } + + if (output_delimiter_string == NULL) + { +- static char dummy[2]; +- dummy[0] = delim; +- dummy[1] = '\0'; +- output_delimiter_string = dummy; +- output_delimiter_length = 1; ++#ifdef HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1 && !force_singlebyte_mode) ++ { ++ output_delimiter_string = xstrdup(mbdelim); ++ output_delimiter_length = delimlen; ++ } ++ ++ if (MB_CUR_MAX <= 1 || force_singlebyte_mode) ++#endif ++ { ++ static char dummy[2]; ++ dummy[0] = delim; ++ dummy[1] = '\0'; ++ output_delimiter_string = dummy; ++ output_delimiter_length = 1; ++ } + } + + if (optind == argc) diff --git a/SOURCES/coreutils-i18n-expand-unexpand.patch b/SOURCES/coreutils-i18n-expand-unexpand.patch new file mode 100644 index 0000000..32089cf --- /dev/null +++ b/SOURCES/coreutils-i18n-expand-unexpand.patch @@ -0,0 +1,864 @@ +From e87ab5b991b08092a7e07af82b3ec822a8604151 Mon Sep 17 00:00:00 2001 +From: Ondrej Oprala +Date: Wed, 5 Aug 2015 09:15:09 +0200 +Subject: [PATCH] expand,unexpand: add multibyte support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +* NEWS: Mention the changes. +* bootstrap.conf: Add mbfile to the list of modules. +* configure.ac: Properly initialize mbfile. +* src/expand.c (expand): Iterate over multibyte characters properly. +* src/unexpand.c (unexpand): Iterate over multibyte characters +properly. +* tests/local.mk: Add new tests. +* tests/{expand,unexpand}/mb.sh: New tests. + +Co-authored-by: Pádraig Brady +--- + bootstrap.conf | 1 + + configure.ac | 2 + + lib/mbfile.c | 3 + + lib/mbfile.h | 255 +++++++++++++++++++++++++++++++++++++++++++++++++++ + m4/mbfile.m4 | 14 +++ + src/expand.c | 43 +++++---- + src/local.mk | 4 +- + src/unexpand.c | 54 +++++++---- + tests/expand/mb.sh | 98 ++++++++++++++++++++ + tests/local.mk | 2 + + tests/unexpand/mb.sh | 97 ++++++++++++++++++++ + 10 files changed, 535 insertions(+), 34 deletions(-) + create mode 100644 lib/mbfile.c + create mode 100644 lib/mbfile.h + create mode 100644 m4/mbfile.m4 + create mode 100755 tests/expand/mb.sh + create mode 100755 tests/unexpand/mb.sh + +diff --git a/bootstrap.conf b/bootstrap.conf +index 8a0ff31..a1c78b2 100644 +--- a/bootstrap.conf ++++ b/bootstrap.conf +@@ -152,6 +152,7 @@ gnulib_modules=" + maintainer-makefile + malloc-gnu + manywarnings ++ mbfile + mbrlen + mbrtowc + mbsalign +diff --git a/configure.ac b/configure.ac +index 1e74b36..24c9725 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -427,6 +427,8 @@ fi + # I'm leaving it here for now. This whole thing needs to be modernized... + gl_WINSIZE_IN_PTEM + ++gl_MBFILE ++ + gl_HEADER_TIOCGWINSZ_IN_TERMIOS_H + + if test $gl_cv_sys_tiocgwinsz_needs_termios_h = no && \ +diff --git a/lib/mbfile.c b/lib/mbfile.c +new file mode 100644 +index 0000000..b0a468e +--- /dev/null ++++ b/lib/mbfile.c +@@ -0,0 +1,3 @@ ++#include ++#define MBFILE_INLINE _GL_EXTERN_INLINE ++#include "mbfile.h" +diff --git a/lib/mbfile.h b/lib/mbfile.h +new file mode 100644 +index 0000000..11f1b12 +--- /dev/null ++++ b/lib/mbfile.h +@@ -0,0 +1,255 @@ ++/* Multibyte character I/O: macros for multi-byte encodings. ++ Copyright (C) 2001, 2005, 2009-2015 Free Software Foundation, Inc. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++/* Written by Mitsuru Chinen ++ and Bruno Haible . */ ++ ++/* The macros in this file implement multi-byte character input from a ++ stream. ++ ++ mb_file_t ++ is the type for multibyte character input stream, usable for variable ++ declarations. ++ ++ mbf_char_t ++ is the type for multibyte character or EOF, usable for variable ++ declarations. ++ ++ mbf_init (mbf, stream) ++ initializes the MB_FILE for reading from stream. ++ ++ mbf_getc (mbc, mbf) ++ reads the next multibyte character from mbf and stores it in mbc. ++ ++ mb_iseof (mbc) ++ returns true if mbc represents the EOF value. ++ ++ Here are the function prototypes of the macros. ++ ++ extern void mbf_init (mb_file_t mbf, FILE *stream); ++ extern void mbf_getc (mbf_char_t mbc, mb_file_t mbf); ++ extern bool mb_iseof (const mbf_char_t mbc); ++ */ ++ ++#ifndef _MBFILE_H ++#define _MBFILE_H 1 ++ ++#include ++#include ++#include ++#include ++ ++/* Tru64 with Desktop Toolkit C has a bug: must be included before ++ . ++ BSD/OS 4.1 has a bug: and must be included before ++ . */ ++#include ++#include ++#include ++ ++#include "mbchar.h" ++ ++#ifndef _GL_INLINE_HEADER_BEGIN ++ #error "Please include config.h first." ++#endif ++_GL_INLINE_HEADER_BEGIN ++#ifndef MBFILE_INLINE ++# define MBFILE_INLINE _GL_INLINE ++#endif ++ ++struct mbfile_multi { ++ FILE *fp; ++ bool eof_seen; ++ bool have_pushback; ++ mbstate_t state; ++ unsigned int bufcount; ++ char buf[MBCHAR_BUF_SIZE]; ++ struct mbchar pushback; ++}; ++ ++MBFILE_INLINE void ++mbfile_multi_getc (struct mbchar *mbc, struct mbfile_multi *mbf) ++{ ++ size_t bytes; ++ ++ /* If EOF has already been seen, don't use getc. This matters if ++ mbf->fp is connected to an interactive tty. */ ++ if (mbf->eof_seen) ++ goto eof; ++ ++ /* Return character pushed back, if there is one. */ ++ if (mbf->have_pushback) ++ { ++ mb_copy (mbc, &mbf->pushback); ++ mbf->have_pushback = false; ++ return; ++ } ++ ++ /* Before using mbrtowc, we need at least one byte. */ ++ if (mbf->bufcount == 0) ++ { ++ int c = getc (mbf->fp); ++ if (c == EOF) ++ { ++ mbf->eof_seen = true; ++ goto eof; ++ } ++ mbf->buf[0] = (unsigned char) c; ++ mbf->bufcount++; ++ } ++ ++ /* Handle most ASCII characters quickly, without calling mbrtowc(). */ ++ if (mbf->bufcount == 1 && mbsinit (&mbf->state) && is_basic (mbf->buf[0])) ++ { ++ /* These characters are part of the basic character set. ISO C 99 ++ guarantees that their wide character code is identical to their ++ char code. */ ++ mbc->wc = mbc->buf[0] = mbf->buf[0]; ++ mbc->wc_valid = true; ++ mbc->ptr = &mbc->buf[0]; ++ mbc->bytes = 1; ++ mbf->bufcount = 0; ++ return; ++ } ++ ++ /* Use mbrtowc on an increasing number of bytes. Read only as many bytes ++ from mbf->fp as needed. This is needed to give reasonable interactive ++ behaviour when mbf->fp is connected to an interactive tty. */ ++ for (;;) ++ { ++ /* We don't know whether the 'mbrtowc' function updates the state when ++ it returns -2, - this is the ISO C 99 and glibc-2.2 behaviour - or ++ not - amended ANSI C, glibc-2.1 and Solaris 2.7 behaviour. We ++ don't have an autoconf test for this, yet. ++ The new behaviour would allow us to feed the bytes one by one into ++ mbrtowc. But the old behaviour forces us to feed all bytes since ++ the end of the last character into mbrtowc. Since we want to retry ++ with more bytes when mbrtowc returns -2, we must backup the state ++ before calling mbrtowc, because implementations with the new ++ behaviour will clobber it. */ ++ mbstate_t backup_state = mbf->state; ++ ++ bytes = mbrtowc (&mbc->wc, &mbf->buf[0], mbf->bufcount, &mbf->state); ++ ++ if (bytes == (size_t) -1) ++ { ++ /* An invalid multibyte sequence was encountered. */ ++ /* Return a single byte. */ ++ bytes = 1; ++ mbc->wc_valid = false; ++ break; ++ } ++ else if (bytes == (size_t) -2) ++ { ++ /* An incomplete multibyte character. */ ++ mbf->state = backup_state; ++ if (mbf->bufcount == MBCHAR_BUF_SIZE) ++ { ++ /* An overlong incomplete multibyte sequence was encountered. */ ++ /* Return a single byte. */ ++ bytes = 1; ++ mbc->wc_valid = false; ++ break; ++ } ++ else ++ { ++ /* Read one more byte and retry mbrtowc. */ ++ int c = getc (mbf->fp); ++ if (c == EOF) ++ { ++ /* An incomplete multibyte character at the end. */ ++ mbf->eof_seen = true; ++ bytes = mbf->bufcount; ++ mbc->wc_valid = false; ++ break; ++ } ++ mbf->buf[mbf->bufcount] = (unsigned char) c; ++ mbf->bufcount++; ++ } ++ } ++ else ++ { ++ if (bytes == 0) ++ { ++ /* A null wide character was encountered. */ ++ bytes = 1; ++ assert (mbf->buf[0] == '\0'); ++ assert (mbc->wc == 0); ++ } ++ mbc->wc_valid = true; ++ break; ++ } ++ } ++ ++ /* Return the multibyte sequence mbf->buf[0..bytes-1]. */ ++ mbc->ptr = &mbc->buf[0]; ++ memcpy (&mbc->buf[0], &mbf->buf[0], bytes); ++ mbc->bytes = bytes; ++ ++ mbf->bufcount -= bytes; ++ if (mbf->bufcount > 0) ++ { ++ /* It's not worth calling memmove() for so few bytes. */ ++ unsigned int count = mbf->bufcount; ++ char *p = &mbf->buf[0]; ++ ++ do ++ { ++ *p = *(p + bytes); ++ p++; ++ } ++ while (--count > 0); ++ } ++ return; ++ ++eof: ++ /* An mbchar_t with bytes == 0 is used to indicate EOF. */ ++ mbc->ptr = NULL; ++ mbc->bytes = 0; ++ mbc->wc_valid = false; ++ return; ++} ++ ++MBFILE_INLINE void ++mbfile_multi_ungetc (const struct mbchar *mbc, struct mbfile_multi *mbf) ++{ ++ mb_copy (&mbf->pushback, mbc); ++ mbf->have_pushback = true; ++} ++ ++typedef struct mbfile_multi mb_file_t; ++ ++typedef mbchar_t mbf_char_t; ++ ++#define mbf_init(mbf, stream) \ ++ ((mbf).fp = (stream), \ ++ (mbf).eof_seen = false, \ ++ (mbf).have_pushback = false, \ ++ memset (&(mbf).state, '\0', sizeof (mbstate_t)), \ ++ (mbf).bufcount = 0) ++ ++#define mbf_getc(mbc, mbf) mbfile_multi_getc (&(mbc), &(mbf)) ++ ++#define mbf_ungetc(mbc, mbf) mbfile_multi_ungetc (&(mbc), &(mbf)) ++ ++#define mb_iseof(mbc) ((mbc).bytes == 0) ++ ++#ifndef _GL_INLINE_HEADER_BEGIN ++ #error "Please include config.h first." ++#endif ++_GL_INLINE_HEADER_BEGIN ++ ++#endif /* _MBFILE_H */ +diff --git a/m4/mbfile.m4 b/m4/mbfile.m4 +new file mode 100644 +index 0000000..8589902 +--- /dev/null ++++ b/m4/mbfile.m4 +@@ -0,0 +1,14 @@ ++# mbfile.m4 serial 7 ++dnl Copyright (C) 2005, 2008-2015 Free Software Foundation, Inc. ++dnl This file is free software; the Free Software Foundation ++dnl gives unlimited permission to copy and/or distribute it, ++dnl with or without modifications, as long as this notice is preserved. ++ ++dnl autoconf tests required for use of mbfile.h ++dnl From Bruno Haible. ++ ++AC_DEFUN([gl_MBFILE], ++[ ++ AC_REQUIRE([AC_TYPE_MBSTATE_T]) ++ : ++]) +diff --git a/src/expand.c b/src/expand.c +index 9fa2e10..380e020 100644 +--- a/src/expand.c ++++ b/src/expand.c +@@ -37,6 +37,9 @@ + #include + #include + #include ++ ++#include ++ + #include "system.h" + #include "die.h" + #include "xstrndup.h" +@@ -100,19 +103,19 @@ expand (void) + { + /* Input stream. */ + FILE *fp = next_file (NULL); ++ mb_file_t mbf; ++ mbf_char_t c; + + if (!fp) + return; + ++ mbf_init (mbf, fp); ++ + while (true) + { +- /* Input character, or EOF. */ +- int c; +- + /* If true, perform translations. */ + bool convert = true; + +- + /* The following variables have valid values only when CONVERT + is true: */ + +@@ -122,17 +125,23 @@ expand (void) + /* Index in TAB_LIST of next tab stop to examine. */ + size_t tab_index = 0; + +- + /* Convert a line of text. */ + + do + { +- while ((c = getc (fp)) < 0 && (fp = next_file (fp))) +- continue; ++ do { ++ mbf_getc (c, mbf); ++ if (mb_iseof (c)) ++ { ++ mbf_init (mbf, fp = next_file (fp)); ++ continue; ++ } ++ } ++ while (false); + + if (convert) + { +- if (c == '\t') ++ if (mb_iseq (c, '\t')) + { + /* Column the next input tab stop is on. */ + uintmax_t next_tab_column; +@@ -151,32 +160,34 @@ expand (void) + if (putchar (' ') < 0) + die (EXIT_FAILURE, errno, _("write error")); + +- c = ' '; ++ mb_setascii (&c, ' '); + } +- else if (c == '\b') ++ else if (mb_iseq (c, '\b')) + { + /* Go back one column, and force recalculation of the + next tab stop. */ + column -= !!column; + tab_index -= !!tab_index; + } +- else ++ /* A leading control character could make us trip over. */ ++ else if (!mb_iscntrl (c)) + { +- column++; ++ column += mb_width (c); + if (!column) + die (EXIT_FAILURE, 0, _("input line is too long")); + } + +- convert &= convert_entire_line || !! isblank (c); ++ convert &= convert_entire_line || mb_isblank (c); + } + +- if (c < 0) ++ if (mb_iseof (c)) + return; + +- if (putchar (c) < 0) ++ mb_putc (c, stdout); ++ if (ferror (stdout)) + die (EXIT_FAILURE, errno, _("write error")); + } +- while (c != '\n'); ++ while (!mb_iseq (c, '\n')); + } + } + +diff --git a/src/local.mk b/src/local.mk +index 72db9c704..ef3bfa469 100644 +--- a/src/local.mk ++++ b/src/local.mk +@@ -415,8 +415,8 @@ src_basenc_CPPFLAGS = -DBASE_TYPE=42 $(AM_CPPFLAGS) + + src_ginstall_CPPFLAGS = -DENABLE_MATCHPATHCON=1 $(AM_CPPFLAGS) + +-src_expand_SOURCES = src/expand.c src/expand-common.c +-src_unexpand_SOURCES = src/unexpand.c src/expand-common.c ++src_expand_SOURCES = src/expand.c src/expand-common.c lib/mbfile.c ++src_unexpand_SOURCES = src/unexpand.c src/expand-common.c lib/mbfile.c + + # Ensure we don't link against libcoreutils.a as that lib is + # not compiled with -fPIC which causes issues on 64 bit at least +diff --git a/src/unexpand.c b/src/unexpand.c +index 7801274..569a7ee 100644 +--- a/src/unexpand.c ++++ b/src/unexpand.c +@@ -38,6 +38,9 @@ + #include + #include + #include ++ ++#include ++ + #include "system.h" + #include "die.h" + #include "xstrndup.h" +@@ -107,11 +110,12 @@ unexpand (void) + { + /* Input stream. */ + FILE *fp = next_file (NULL); ++ mb_file_t mbf; + + /* The array of pending blanks. In non-POSIX locales, blanks can + include characters other than spaces, so the blanks must be + stored, not merely counted. */ +- char *pending_blank; ++ mbf_char_t *pending_blank; + + if (!fp) + return; +@@ -119,12 +123,14 @@ unexpand (void) + /* The worst case is a non-blank character, then one blank, then a + tab stop, then MAX_COLUMN_WIDTH - 1 blanks, then a non-blank; so + allocate MAX_COLUMN_WIDTH bytes to store the blanks. */ +- pending_blank = xmalloc (max_column_width); ++ pending_blank = xmalloc (max_column_width * sizeof (mbf_char_t)); ++ ++ mbf_init (mbf, fp); + + while (true) + { + /* Input character, or EOF. */ +- int c; ++ mbf_char_t c; + + /* If true, perform translations. */ + bool convert = true; +@@ -158,12 +164,19 @@ unexpand (void) + + do + { +- while ((c = getc (fp)) < 0 && (fp = next_file (fp))) +- continue; ++ do { ++ mbf_getc (c, mbf); ++ if (mb_iseof (c)) ++ { ++ mbf_init (mbf, fp = next_file (fp)); ++ continue; ++ } ++ } ++ while (false); + + if (convert) + { +- bool blank = !! isblank (c); ++ bool blank = mb_isblank (c); + + if (blank) + { +@@ -180,16 +193,16 @@ unexpand (void) + if (next_tab_column < column) + die (EXIT_FAILURE, 0, _("input line is too long")); + +- if (c == '\t') ++ if (mb_iseq (c, '\t')) + { + column = next_tab_column; + + if (pending) +- pending_blank[0] = '\t'; ++ mb_setascii (&pending_blank[0], '\t'); + } + else + { +- column++; ++ column += mb_width (c); + + if (! (prev_blank && column == next_tab_column)) + { +@@ -197,13 +210,14 @@ unexpand (void) + will be replaced by tabs. */ + if (column == next_tab_column) + one_blank_before_tab_stop = true; +- pending_blank[pending++] = c; ++ mb_copy (&pending_blank[pending++], &c); + prev_blank = true; + continue; + } + + /* Replace the pending blanks by a tab or two. */ +- pending_blank[0] = c = '\t'; ++ mb_setascii (&c, '\t'); ++ mb_setascii (&pending_blank[0], '\t'); + } + + /* Discard pending blanks, unless it was a single +@@ -211,7 +225,7 @@ unexpand (void) + pending = one_blank_before_tab_stop; + } + } +- else if (c == '\b') ++ else if (mb_iseq (c, '\b')) + { + /* Go back one column, and force recalculation of the + next tab stop. */ +@@ -221,7 +235,7 @@ unexpand (void) + } + else + { +- column++; ++ column += mb_width (c); + if (!column) + die (EXIT_FAILURE, 0, _("input line is too long")); + } +@@ -229,8 +243,11 @@ unexpand (void) + if (pending) + { + if (pending > 1 && one_blank_before_tab_stop) +- pending_blank[0] = '\t'; +- if (fwrite (pending_blank, 1, pending, stdout) != pending) ++ mb_setascii (&pending_blank[0], '\t'); ++ ++ for (int n = 0; n < pending; ++n) ++ mb_putc (pending_blank[n], stdout); ++ if (ferror (stdout)) + die (EXIT_FAILURE, errno, _("write error")); + pending = 0; + one_blank_before_tab_stop = false; +@@ -240,16 +257,17 @@ unexpand (void) + convert &= convert_entire_line || blank; + } + +- if (c < 0) ++ if (mb_iseof (c)) + { + free (pending_blank); + return; + } + +- if (putchar (c) < 0) ++ mb_putc (c, stdout); ++ if (ferror (stdout)) + die (EXIT_FAILURE, errno, _("write error")); + } +- while (c != '\n'); ++ while (!mb_iseq (c, '\n')); + } + } + +diff --git a/tests/expand/mb.sh b/tests/expand/mb.sh +new file mode 100755 +index 0000000..7971e18 +--- /dev/null ++++ b/tests/expand/mb.sh +@@ -0,0 +1,98 @@ ++#!/bin/sh ++ ++# Copyright (C) 2012-2015 Free Software Foundation, Inc. ++ ++# This program is free software: you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation, either version 3 of the License, or ++# (at your option) any later version. ++ ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++ ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++ ++. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src ++print_ver_ expand ++ ++export LC_ALL=en_US.UTF-8 ++ ++#input containing multibyte characters ++cat <<\EOF > in || framework_failure_ ++1234567812345678123456781 ++. . . . ++a b c d ++. . . . ++ä ö ü ß ++. . . . ++EOF ++env printf ' äöü\t. öüä. \tä xx\n' >> in || framework_failure_ ++ ++cat <<\EOF > exp || framework_failure_ ++1234567812345678123456781 ++. . . . ++a b c d ++. . . . ++ä ö ü ß ++. . . . ++ äöü . öüä. ä xx ++EOF ++ ++expand < in > out || fail=1 ++compare exp out > /dev/null 2>&1 || fail=1 ++ ++#test characters with display widths != 1 ++env printf '12345678 ++e\t|ascii(1) ++\u00E9\t|composed(1) ++e\u0301\t|decomposed(1) ++\u3000\t|ideo-space(2) ++\uFF0D\t|full-hypen(2) ++' > in || framework_failure_ ++ ++env printf '12345678 ++e |ascii(1) ++\u00E9 |composed(1) ++e\u0301 |decomposed(1) ++\u3000 |ideo-space(2) ++\uFF0D |full-hypen(2) ++' > exp || framework_failure_ ++ ++expand < in > out || fail=1 ++compare exp out > /dev/null 2>&1 || fail=1 ++ ++#shouldn't fail with "input line too long" ++#when a line starts with a control character ++env printf '\n' > in || framework_failure_ ++ ++expand < in > out || fail=1 ++compare in out > /dev/null 2>&1 || fail=1 ++ ++#non-Unicode characters interspersed between Unicode ones ++env printf '12345678 ++\t\xFF| ++\xFF\t| ++\t\xFFä| ++ä\xFF\t| ++\tä\xFF| ++\xFF\tä| ++äbcdef\xFF\t| ++' > in || framework_failure_ ++ ++env printf '12345678 ++ \xFF| ++\xFF | ++ \xFFä| ++ä\xFF | ++ ä\xFF| ++\xFF ä| ++äbcdef\xFF | ++' > exp || framework_failure_ ++ ++expand < in > out || fail=1 ++compare exp out > /dev/null 2>&1 || fail=1 ++ ++exit $fail +diff --git a/tests/local.mk b/tests/local.mk +index 192f776..8053397 100644 +--- a/tests/local.mk ++++ b/tests/local.mk +@@ -544,6 +544,7 @@ all_tests = \ + tests/du/threshold.sh \ + tests/du/trailing-slash.sh \ + tests/du/two-args.sh \ ++ tests/expand/mb.sh \ + tests/id/gnu-zero-uids.sh \ + tests/id/no-context.sh \ + tests/id/context.sh \ +@@ -684,6 +685,7 @@ all_tests = \ + tests/touch/read-only.sh \ + tests/touch/relative.sh \ + tests/touch/trailing-slash.sh \ ++ tests/unexpand/mb.sh \ + $(all_root_tests) + + # See tests/factor/create-test.sh. +diff --git a/tests/unexpand/mb.sh b/tests/unexpand/mb.sh +new file mode 100755 +index 0000000..60d4c1a +--- /dev/null ++++ b/tests/unexpand/mb.sh +@@ -0,0 +1,97 @@ ++#!/bin/sh ++ ++# Copyright (C) 2012-2015 Free Software Foundation, Inc. ++ ++# This program is free software: you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation, either version 3 of the License, or ++# (at your option) any later version. ++ ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++ ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++ ++. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src ++print_ver_ unexpand ++ ++export LC_ALL=en_US.UTF-8 ++ ++#input containing multibyte characters ++cat > in <<\EOF ++1234567812345678123456781 ++. . . . ++a b c d ++. . . . ++ä ö ü ß ++. . . . ++ äöü . öüä. ä xx ++EOF ++ ++cat > exp <<\EOF ++1234567812345678123456781 ++. . . . ++a b c d ++. . . . ++ä ö ü ß ++. . . . ++ äöü . öüä. ä xx ++EOF ++ ++unexpand -a < in > out || fail=1 ++compare exp out > /dev/null 2>&1 || fail=1 ++ ++#test characters with a display width larger than 1 ++ ++env printf '12345678 ++e |ascii(1) ++\u00E9 |composed(1) ++e\u0301 |decomposed(1) ++\u3000 |ideo-space(2) ++\uFF0D |full-hypen(2) ++' > in || framework_failure_ ++ ++env printf '12345678 ++e\t|ascii(1) ++\u00E9\t|composed(1) ++e\u0301\t|decomposed(1) ++\u3000\t|ideo-space(2) ++\uFF0D\t|full-hypen(2) ++' > exp || framework_failure_ ++ ++unexpand -a < in > out || fail=1 ++compare exp out > /dev/null 2>&1 || fail=1 ++ ++#test input where a blank of width > 1 is not being substituted ++in="$(LC_ALL=en_US.UTF-8 printf ' \u3000 ö ü ß')" ++exp='   ö ü ß' ++ ++unexpand -a < in > out || fail=1 ++compare exp out > /dev/null 2>&1 || fail=1 ++ ++#non-Unicode characters interspersed between Unicode ones ++env printf '12345678 ++ \xFF| ++\xFF | ++ \xFFä| ++ä\xFF | ++ ä\xFF| ++\xFF ä| ++äbcdef\xFF | ++' > in || framework_failure_ ++ ++env printf '12345678 ++\t\xFF| ++\xFF\t| ++\t\xFFä| ++ä\xFF\t| ++\tä\xFF| ++\xFF\tä| ++äbcdef\xFF\t| ++' > exp || framework_failure_ ++ ++unexpand -a < in > out || fail=1 ++compare exp out > /dev/null 2>&1 || fail=1 +-- +2.7.4 + diff --git a/SOURCES/coreutils-i18n-fix-unexpand.patch b/SOURCES/coreutils-i18n-fix-unexpand.patch new file mode 100644 index 0000000..f0c347c --- /dev/null +++ b/SOURCES/coreutils-i18n-fix-unexpand.patch @@ -0,0 +1,28 @@ +From 02424bfcd719bbaa695f4e1c3ef17ad91b0d23c0 Mon Sep 17 00:00:00 2001 +From: Lubomir Rintel +Date: Thu, 28 Jan 2016 20:57:22 +0100 +Subject: [PATCH] unexpand: fix blank line handling + + echo '' |./src/unexpand -a + +Really? +--- + src/unexpand.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/unexpand.c b/src/unexpand.c +index 569a7ee..3bbbd66 100644 +--- a/src/unexpand.c ++++ b/src/unexpand.c +@@ -233,7 +233,7 @@ unexpand (void) + next_tab_column = column; + tab_index -= !!tab_index; + } +- else ++ else if (!mb_iseq (c, '\n')) + { + column += mb_width (c); + if (!column) +-- +2.7.4 + diff --git a/SOURCES/coreutils-i18n-fix2-expand-unexpand.patch b/SOURCES/coreutils-i18n-fix2-expand-unexpand.patch new file mode 100644 index 0000000..b34d7b7 --- /dev/null +++ b/SOURCES/coreutils-i18n-fix2-expand-unexpand.patch @@ -0,0 +1,108 @@ +diff --git a/src/expand.c b/src/expand.c +index 380e020..310b349 100644 +--- a/src/expand.c ++++ b/src/expand.c +@@ -129,15 +129,19 @@ expand (void) + + do + { +- do { ++ while (true) { + mbf_getc (c, mbf); +- if (mb_iseof (c)) ++ if ((mb_iseof (c)) && (fp = next_file (fp))) + { +- mbf_init (mbf, fp = next_file (fp)); ++ mbf_init (mbf, fp); + continue; + } ++ else ++ { ++ break; ++ } + } +- while (false); ++ + + if (convert) + { +diff --git a/src/unexpand.c b/src/unexpand.c +index 3bbbd66..863a90a 100644 +--- a/src/unexpand.c ++++ b/src/unexpand.c +@@ -164,15 +164,19 @@ unexpand (void) + + do + { +- do { ++ while (true) { + mbf_getc (c, mbf); +- if (mb_iseof (c)) ++ if ((mb_iseof (c)) && (fp = next_file (fp))) + { +- mbf_init (mbf, fp = next_file (fp)); ++ mbf_init (mbf, fp); + continue; + } ++ else ++ { ++ break; ++ } + } +- while (false); ++ + + if (convert) + { +diff --git a/tests/expand/mb.sh b/tests/expand/mb.sh +index 7971e18..031be7a 100755 +--- a/tests/expand/mb.sh ++++ b/tests/expand/mb.sh +@@ -44,6 +44,20 @@ EOF + expand < in > out || fail=1 + compare exp out > /dev/null 2>&1 || fail=1 + ++#multiple files as an input ++cat <<\EOF >> exp || framework_failure_ ++1234567812345678123456781 ++. . . . ++a b c d ++. . . . ++ä ö ü ß ++. . . . ++ äöü . öüä. ä xx ++EOF ++ ++expand ./in ./in > out || fail=1 ++compare exp out > /dev/null 2>&1 || fail=1 ++ + #test characters with display widths != 1 + env printf '12345678 + e\t|ascii(1) +diff --git a/tests/unexpand/mb.sh b/tests/unexpand/mb.sh +index 60d4c1a..8d75652 100755 +--- a/tests/unexpand/mb.sh ++++ b/tests/unexpand/mb.sh +@@ -44,6 +44,22 @@ EOF + unexpand -a < in > out || fail=1 + compare exp out > /dev/null 2>&1 || fail=1 + ++ ++#multiple files as an input ++cat >> exp <<\EOF ++1234567812345678123456781 ++. . . . ++a b c d ++. . . . ++ä ö ü ß ++. . . . ++ äöü . öüä. ä xx ++EOF ++ ++ ++unexpand -a ./in ./in > out || fail=1 ++compare exp out > /dev/null 2>&1 || fail=1 ++ + #test characters with a display width larger than 1 + + env printf '12345678 diff --git a/SOURCES/coreutils-i18n-fold-newline.patch b/SOURCES/coreutils-i18n-fold-newline.patch new file mode 100644 index 0000000..f7286ef --- /dev/null +++ b/SOURCES/coreutils-i18n-fold-newline.patch @@ -0,0 +1,80 @@ +From ff424639fe863cbd6963add1a79b97290c1606c6 Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Fri, 3 Feb 2017 12:26:53 +0100 +Subject: [PATCH] fold.c: preserve new-lines in mutlibyte text + +--- + src/fold.c | 49 ++++++++++++++++++++++++------------------------- + 1 file changed, 24 insertions(+), 25 deletions(-) + +diff --git a/src/fold.c b/src/fold.c +index d23edd5..8c232a7 100644 +--- a/src/fold.c ++++ b/src/fold.c +@@ -342,39 +342,38 @@ fold_multibyte_text (FILE *istream, size_t width, int *saved_errno) + } + + rescan: +- if (operating_mode == byte_mode) /* byte mode */ ++ if (convfail) ++ increment = 1; ++ else if (wc == L'\n') ++ { ++ /* preserve newline */ ++ fwrite (line_out, sizeof(char), offset_out, stdout); ++ START_NEW_LINE; ++ continue; ++ } ++ else if (operating_mode == byte_mode) /* byte mode */ + increment = mblength; + else if (operating_mode == character_mode) /* character mode */ + increment = 1; +- else /* column mode */ ++ else /* column mode */ + { +- if (convfail) +- increment = 1; +- else ++ switch (wc) + { +- switch (wc) +- { +- case L'\n': +- fwrite (line_out, sizeof(char), offset_out, stdout); +- START_NEW_LINE; +- continue; ++ case L'\b': ++ increment = (column > 0) ? -1 : 0; ++ break; + +- case L'\b': +- increment = (column > 0) ? -1 : 0; +- break; ++ case L'\r': ++ increment = -1 * column; ++ break; + +- case L'\r': +- increment = -1 * column; +- break; ++ case L'\t': ++ increment = 8 - column % 8; ++ break; + +- case L'\t': +- increment = 8 - column % 8; +- break; +- +- default: +- increment = wcwidth (wc); +- increment = (increment < 0) ? 0 : increment; +- } ++ default: ++ increment = wcwidth (wc); ++ increment = (increment < 0) ? 0 : increment; + } + } + +-- +2.7.4 + diff --git a/SOURCES/coreutils-i18n-sort-human.patch b/SOURCES/coreutils-i18n-sort-human.patch new file mode 100644 index 0000000..6752493 --- /dev/null +++ b/SOURCES/coreutils-i18n-sort-human.patch @@ -0,0 +1,35 @@ +From 3976ef5a20369d8b490907ab2cba2d617305a5e0 Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +Date: Mon, 30 May 2016 16:19:20 +0200 +Subject: [PATCH] sort: do not use static array 'blanks' in human_numcompare() + +... because the array is not initialized with MB locales. Note this is +rather a conservative fix. I plan to do more cleanup of the i18n patch +in Fedora to prevent mistakes like this in future updates of coreutils. +--- + src/sort.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +diff --git a/src/sort.c b/src/sort.c +index 9e07ad8..e47b039 100644 +--- a/src/sort.c ++++ b/src/sort.c +@@ -2304,12 +2304,10 @@ find_unit_order (char const *number) + < K/k < M < G < T < P < E < Z < Y */ + + static int +-human_numcompare (char const *a, char const *b) ++human_numcompare (char *a, char *b) + { +- while (blanks[to_uchar (*a)]) +- a++; +- while (blanks[to_uchar (*b)]) +- b++; ++ skipblanks(&a, a + strlen(a)); ++ skipblanks(&b, b + strlen(b)); + + int diff = find_unit_order (a) - find_unit_order (b); + return (diff ? diff : strnumcmp (a, b, decimal_point, thousands_sep)); +-- +2.5.5 + diff --git a/SOURCES/coreutils-i18n-un-expand-BOM.patch b/SOURCES/coreutils-i18n-un-expand-BOM.patch new file mode 100644 index 0000000..6210ce7 --- /dev/null +++ b/SOURCES/coreutils-i18n-un-expand-BOM.patch @@ -0,0 +1,456 @@ +From 7a7c776a4e228d180e74614fd8c8afcad5d4bdf7 Mon Sep 17 00:00:00 2001 +From: Jakub Martisko +Date: Thu, 7 Jul 2016 12:53:26 +0200 +Subject: [PATCH] coreutils-i18n-un-expand-BOM.patch + +--- + src/expand-common.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++ + src/expand-common.h | 12 ++++++ + src/expand.c | 45 +++++++++++++++++++- + src/unexpand.c | 43 ++++++++++++++++++- + tests/expand/mb.sh | 71 ++++++++++++++++++++++++++++++++ + tests/unexpand/mb.sh | 59 ++++++++++++++++++++++++++ + 6 files changed, 342 insertions(+), 2 deletions(-) + +diff --git a/src/expand-common.c b/src/expand-common.c +index 4657e46..97cbb09 100644 +--- a/src/expand-common.c ++++ b/src/expand-common.c +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + #include "system.h" + #include "die.h" + #include "error.h" +@@ -126,6 +127,119 @@ set_increment_size (uintmax_t tabval) + return ok; + } + ++extern int ++set_utf_locale (void) ++{ ++ /*try using some predefined locale */ ++ const char* predef_locales[] = {"C.UTF8","en_US.UTF8","en_GB.UTF8"}; ++ ++ const int predef_locales_count=3; ++ for (int i=0;ibufcount=0; ++ if (c == 0xEF) ++ { ++ c=fgetc(fp); ++ } ++ else ++ { ++ if (c != EOF) ++ { ++ ungetc(c,fp); ++ } ++ return false; ++ } ++ ++ if (c == 0xBB) ++ { ++ c=fgetc(fp); ++ } ++ else ++ { ++ if ( c!= EOF ) ++ { ++ mbf->buf[0]=(unsigned char) 0xEF; ++ mbf->bufcount=1; ++ ungetc(c,fp); ++ return false; ++ } ++ else ++ { ++ ungetc(0xEF,fp); ++ return false; ++ } ++ } ++ if (c == 0xBF) ++ { ++ mbf->bufcount=0; ++ return true; ++ } ++ else ++ { ++ if (c != EOF) ++ { ++ mbf->buf[0]=(unsigned char) 0xEF; ++ mbf->buf[1]=(unsigned char) 0xBB; ++ mbf->bufcount=2; ++ ungetc(c,fp); ++ return false; ++ } ++ else ++ { ++ mbf->buf[0]=(unsigned char) 0xEF; ++ mbf->bufcount=1; ++ ungetc(0xBB,fp); ++ return false; ++ } ++ } ++ return false; ++} ++ ++extern void ++print_bom(void) ++{ ++ putc (0xEF, stdout); ++ putc (0xBB, stdout); ++ putc (0xBF, stdout); ++} ++ + /* Add the comma or blank separated list of tab stops STOPS + to the list of tab stops. */ + extern void +diff --git a/src/expand-common.h b/src/expand-common.h +index 8cb2079..763bfda 100644 +--- a/src/expand-common.h ++++ b/src/expand-common.h +@@ -34,6 +34,18 @@ extern size_t max_column_width; + /* The desired exit status. */ + extern int exit_status; + ++extern int ++set_utf_locale (void); ++ ++extern bool ++check_utf_locale(void); ++ ++extern bool ++check_bom(FILE* fp, mb_file_t *mbf); ++ ++extern void ++print_bom(void); ++ + /* Add tab stop TABVAL to the end of 'tab_list'. */ + extern void + add_tab_stop (uintmax_t tabval); +diff --git a/src/expand.c b/src/expand.c +index 310b349..4136824 100644 +--- a/src/expand.c ++++ b/src/expand.c +@@ -103,11 +103,33 @@ expand (void) + FILE *fp = next_file (NULL); + mb_file_t mbf; + mbf_char_t c; ++ /* True if the starting locale is utf8. */ ++ bool using_utf_locale; ++ ++ /* True if the first file contains BOM header. */ ++ bool found_bom; ++ using_utf_locale=check_utf_locale(); + + if (!fp) + return; +- + mbf_init (mbf, fp); ++ found_bom=check_bom(fp,&mbf); ++ ++ if (using_utf_locale == false && found_bom == true) ++ { ++ /*try using some predefined locale */ ++ ++ if (set_utf_locale () != 0) ++ { ++ error (EXIT_FAILURE, errno, _("cannot set UTF-8 locale")); ++ } ++ } ++ ++ ++ if (found_bom == true) ++ { ++ print_bom(); ++ } + + while (true) + { +@@ -132,6 +154,27 @@ expand (void) + if ((mb_iseof (c)) && (fp = next_file (fp))) + { + mbf_init (mbf, fp); ++ if (fp!=NULL) ++ { ++ if (check_bom(fp,&mbf)==true) ++ { ++ /*Not the first file - check BOM header*/ ++ if (using_utf_locale==false && found_bom==false) ++ { ++ /*BOM header in subsequent file but not in the first one. */ ++ error (EXIT_FAILURE, errno, _("combination of files with and without BOM header")); ++ } ++ } ++ else ++ { ++ if(using_utf_locale==false && found_bom==true) ++ { ++ /*First file conatined BOM header - locale was switched to UTF ++ *all subsequent files should contain BOM. */ ++ error (EXIT_FAILURE, errno, _("combination of files with and without BOM header")); ++ } ++ } ++ } + continue; + } + else +diff --git a/src/unexpand.c b/src/unexpand.c +index 863a90a..5681b58 100644 +--- a/src/unexpand.c ++++ b/src/unexpand.c +@@ -116,16 +116,36 @@ unexpand (void) + include characters other than spaces, so the blanks must be + stored, not merely counted. */ + mbf_char_t *pending_blank; ++ /* True if the starting locale is utf8. */ ++ bool using_utf_locale; ++ ++ /* True if the first file contains BOM header. */ ++ bool found_bom; ++ using_utf_locale=check_utf_locale(); + + if (!fp) + return; ++ mbf_init (mbf, fp); ++ found_bom=check_bom(fp,&mbf); ++ ++ if (using_utf_locale == false && found_bom == true) ++ { ++ /*try using some predefined locale */ + ++ if (set_utf_locale () != 0) ++ { ++ error (EXIT_FAILURE, errno, _("cannot set UTF-8 locale")); ++ } ++ } + /* The worst case is a non-blank character, then one blank, then a + tab stop, then MAX_COLUMN_WIDTH - 1 blanks, then a non-blank; so + allocate MAX_COLUMN_WIDTH bytes to store the blanks. */ + pending_blank = xmalloc (max_column_width * sizeof (mbf_char_t)); + +- mbf_init (mbf, fp); ++ if (found_bom == true) ++ { ++ print_bom(); ++ } + + while (true) + { +@@ -169,6 +189,27 @@ unexpand (void) + if ((mb_iseof (c)) && (fp = next_file (fp))) + { + mbf_init (mbf, fp); ++ if (fp!=NULL) ++ { ++ if (check_bom(fp,&mbf)==true) ++ { ++ /*Not the first file - check BOM header*/ ++ if (using_utf_locale==false && found_bom==false) ++ { ++ /*BOM header in subsequent file but not in the first one. */ ++ error (EXIT_FAILURE, errno, _("combination of files with and without BOM header")); ++ } ++ } ++ else ++ { ++ if(using_utf_locale==false && found_bom==true) ++ { ++ /*First file conatined BOM header - locale was switched to UTF ++ *all subsequent files should contain BOM. */ ++ error (EXIT_FAILURE, errno, _("combination of files with and without BOM header")); ++ } ++ } ++ } + continue; + } + else +diff --git a/tests/expand/mb.sh b/tests/expand/mb.sh +index 031be7a..1621c84 100755 +--- a/tests/expand/mb.sh ++++ b/tests/expand/mb.sh +@@ -109,4 +109,75 @@ env printf '12345678 + expand < in > out || fail=1 + compare exp out > /dev/null 2>&1 || fail=1 + ++ ++ ++#BOM header test 1 ++printf "\xEF\xBB\xBF" > in; cat <<\EOF >> in || framework_failure_ ++1234567812345678123456781 ++. . . . ++a b c d ++. . . . ++ä ö ü ß ++. . . . ++EOF ++env printf ' äöü\t. öüä. \tä xx\n' >> in || framework_failure_ ++ ++printf "\xEF\xBB\xBF" > exp; cat <<\EOF >> exp || framework_failure_ ++1234567812345678123456781 ++. . . . ++a b c d ++. . . . ++ä ö ü ß ++. . . . ++ äöü . öüä. ä xx ++EOF ++ ++ ++expand < in > out || fail=1 ++compare exp out > /dev/null 2>&1 || fail=1 ++ ++LANG=C expand < in > out || fail=1 ++compare exp out > /dev/null 2>&1 || fail=1 ++ ++LC_ALL=C expand < in > out || fail=1 ++compare exp out > /dev/null 2>&1 || fail=1 ++ ++ ++printf '\xEF\xBB\xBF' > in1; cat <<\EOF >> in1 || framework_failure_ ++1234567812345678123456781 ++. . . . ++a b c d ++. . . . ++ä ö ü ß ++. . . . ++EOF ++env printf ' äöü\t. öüä. \tä xx\n' >> in1 || framework_failure_ ++ ++ ++printf '\xEF\xBB\xBF' > exp; cat <<\EOF >> exp || framework_failure_ ++1234567812345678123456781 ++. . . . ++a b c d ++. . . . ++ä ö ü ß ++. . . . ++ äöü . öüä. ä xx ++1234567812345678123456781 ++. . . . ++a b c d ++. . . . ++ä ö ü ß ++. . . . ++ äöü . öüä. ä xx ++EOF ++ ++expand in1 in1 > out || fail=1 ++compare exp out > /dev/null 2>&1 || fail=1 ++ ++LANG=C expand in1 in1 > out || fail=1 ++compare exp out > /dev/null 2>&1 || fail=1 ++ ++LC_ALL=C expand in1 in1 > out || fail=1 ++compare exp out > /dev/null 2>&1 || fail=1 ++ + exit $fail +diff --git a/tests/unexpand/mb.sh b/tests/unexpand/mb.sh +index 8d75652..9d4ee3e 100755 +--- a/tests/unexpand/mb.sh ++++ b/tests/unexpand/mb.sh +@@ -111,3 +111,62 @@ env printf '12345678 + + unexpand -a < in > out || fail=1 + compare exp out > /dev/null 2>&1 || fail=1 ++ ++#BOM header test 1 ++printf "\xEF\xBB\xBF" > in; cat <<\EOF >> in || framework_failure_ ++1234567812345678123456781 ++. . . . ++a b c d ++. . . . ++ä ö ü ß ++. . . . ++ äöü . öüä. ä xx ++EOF ++env printf ' äöü\t. öüä. \tä xx\n' >> in || framework_failure_ ++ ++printf "\xEF\xBB\xBF" > exp; cat <<\EOF >> exp || framework_failure_ ++1234567812345678123456781 ++. . . . ++a b c d ++. . . . ++ä ö ü ß ++. . . . ++ äöü . öüä. ä xx ++EOF ++ ++unexpand < in > out || fail=1 ++compare exp out > /dev/null 2>&1 || fail=1 ++ ++LANG=C unexpand < in > out || fail=1 ++compare exp out > /dev/null 2>&1 || fail=1 ++ ++LC_ALL=C unexpand < in > out || fail=1 ++compare exp out > /dev/null 2>&1 || fail=1 ++ ++ ++printf "\xEF\xBB\xBF" > exp; cat <<\EOF >> exp || framework_failure_ ++1234567812345678123456781 ++. . . . ++a b c d ++. . . . ++ä ö ü ß ++. . . . ++ äöü . öüä. ä xx ++1234567812345678123456781 ++. . . . ++a b c d ++. . . . ++ä ö ü ß ++. . . . ++ äöü . öüä. ä xx ++EOF ++ ++ ++unexpand in in > out || fail=1 ++compare exp out > /dev/null 2>&1 || fail=1 ++ ++LANG=C unexpand in in > out || fail=1 ++compare exp out > /dev/null 2>&1 || fail=1 ++ ++LC_ALL=C unexpand in in > out || fail=1 ++compare exp out > /dev/null 2>&1 || fail=1 +-- +2.9.3 + diff --git a/SOURCES/coreutils-i18n.patch b/SOURCES/coreutils-i18n.patch new file mode 100644 index 0000000..ad2fa2e --- /dev/null +++ b/SOURCES/coreutils-i18n.patch @@ -0,0 +1,3536 @@ +From 29117b2d07af00f4d4b87cf778e4294588ab1a83 Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +Date: Thu, 1 Dec 2016 15:10:04 +0100 +Subject: [PATCH] coreutils-i18n.patch + +TODO: merge upstream +--- + lib/linebuffer.h | 8 + + src/fold.c | 308 +++++++++++++-- + src/join.c | 359 ++++++++++++++--- + src/pr.c | 443 +++++++++++++++++++-- + src/sort.c | 764 ++++++++++++++++++++++++++++++++++-- + src/uniq.c | 119 +++++- + tests/i18n/sort.sh | 29 ++ + tests/local.mk | 2 + + tests/misc/expand.pl | 42 ++ + tests/misc/fold.pl | 50 ++- + tests/misc/join.pl | 50 +++ + tests/misc/sort-mb-tests.sh | 45 +++ + tests/misc/sort-merge.pl | 42 ++ + tests/misc/sort.pl | 40 +- + tests/misc/unexpand.pl | 39 ++ + tests/misc/uniq.pl | 55 +++ + tests/pr/pr-tests.pl | 49 +++ + 17 files changed, 2290 insertions(+), 154 deletions(-) + create mode 100755 tests/i18n/sort.sh + create mode 100755 tests/misc/sort-mb-tests.sh + +diff --git a/lib/linebuffer.h b/lib/linebuffer.h +index 64181af..9b8fe5a 100644 +--- a/lib/linebuffer.h ++++ b/lib/linebuffer.h +@@ -21,6 +21,11 @@ + + # include + ++/* Get mbstate_t. */ ++# if HAVE_WCHAR_H ++# include ++# endif ++ + /* A 'struct linebuffer' holds a line of text. */ + + struct linebuffer +@@ -28,6 +33,9 @@ struct linebuffer + size_t size; /* Allocated. */ + size_t length; /* Used. */ + char *buffer; ++# if HAVE_WCHAR_H ++ mbstate_t state; ++# endif + }; + + /* Initialize linebuffer LINEBUFFER for use. */ +diff --git a/src/fold.c b/src/fold.c +index 8cd0d6b..d23edd5 100644 +--- a/src/fold.c ++++ b/src/fold.c +@@ -22,12 +22,34 @@ + #include + #include + ++/* Get mbstate_t, mbrtowc(), wcwidth(). */ ++#if HAVE_WCHAR_H ++# include ++#endif ++ ++/* Get iswprint(), iswblank(), wcwidth(). */ ++#if HAVE_WCTYPE_H ++# include ++#endif ++ + #include "system.h" + #include "die.h" + #include "error.h" + #include "fadvise.h" + #include "xdectoint.h" + ++/* MB_LEN_MAX is incorrectly defined to be 1 in at least one GCC ++ installation; work around this configuration error. */ ++#if !defined MB_LEN_MAX || MB_LEN_MAX < 2 ++# undef MB_LEN_MAX ++# define MB_LEN_MAX 16 ++#endif ++ ++/* Some systems, like BeOS, have multibyte encodings but lack mbstate_t. */ ++#if HAVE_MBRTOWC && defined mbstate_t ++# define mbrtowc(pwc, s, n, ps) (mbrtowc) (pwc, s, n, 0) ++#endif ++ + #define TAB_WIDTH 8 + + /* The official name of this program (e.g., no 'g' prefix). */ +@@ -35,20 +57,41 @@ + + #define AUTHORS proper_name ("David MacKenzie") + ++#define FATAL_ERROR(Message) \ ++ do \ ++ { \ ++ error (0, 0, (Message)); \ ++ usage (2); \ ++ } \ ++ while (0) ++ ++enum operating_mode ++{ ++ /* Fold texts by columns that are at the given positions. */ ++ column_mode, ++ ++ /* Fold texts by bytes that are at the given positions. */ ++ byte_mode, ++ ++ /* Fold texts by characters that are at the given positions. */ ++ character_mode, ++}; ++ ++/* The argument shows current mode. (Default: column_mode) */ ++static enum operating_mode operating_mode; ++ + /* If nonzero, try to break on whitespace. */ + static bool break_spaces; + +-/* If nonzero, count bytes, not column positions. */ +-static bool count_bytes; +- + /* If nonzero, at least one of the files we read was standard input. */ + static bool have_read_stdin; + +-static char const shortopts[] = "bsw:0::1::2::3::4::5::6::7::8::9::"; ++static char const shortopts[] = "bcsw:0::1::2::3::4::5::6::7::8::9::"; + + static struct option const longopts[] = + { + {"bytes", no_argument, NULL, 'b'}, ++ {"characters", no_argument, NULL, 'c'}, + {"spaces", no_argument, NULL, 's'}, + {"width", required_argument, NULL, 'w'}, + {GETOPT_HELP_OPTION_DECL}, +@@ -76,6 +119,7 @@ Wrap input lines in each FILE, writing to standard output.\n\ + + fputs (_("\ + -b, --bytes count bytes rather than columns\n\ ++ -c, --characters count characters rather than columns\n\ + -s, --spaces break at spaces\n\ + -w, --width=WIDTH use WIDTH columns instead of 80\n\ + "), stdout); +@@ -93,7 +137,7 @@ Wrap input lines in each FILE, writing to standard output.\n\ + static size_t + adjust_column (size_t column, char c) + { +- if (!count_bytes) ++ if (operating_mode != byte_mode) + { + if (c == '\b') + { +@@ -116,30 +160,14 @@ adjust_column (size_t column, char c) + to stdout, with maximum line length WIDTH. + Return true if successful. */ + +-static bool +-fold_file (char const *filename, size_t width) ++static void ++fold_text (FILE *istream, size_t width, int *saved_errno) + { +- FILE *istream; + int c; + size_t column = 0; /* Screen column where next char will go. */ + size_t offset_out = 0; /* Index in 'line_out' for next char. */ + static char *line_out = NULL; + static size_t allocated_out = 0; +- int saved_errno; +- +- if (STREQ (filename, "-")) +- { +- istream = stdin; +- have_read_stdin = true; +- } +- else +- istream = fopen (filename, "r"); +- +- if (istream == NULL) +- { +- error (0, errno, "%s", quotef (filename)); +- return false; +- } + + fadvise (istream, FADVISE_SEQUENTIAL); + +@@ -169,6 +197,15 @@ fold_file (char const *filename, size_t width) + bool found_blank = false; + size_t logical_end = offset_out; + ++ /* If LINE_OUT has no wide character, ++ put a new wide character in LINE_OUT ++ if column is bigger than width. */ ++ if (offset_out == 0) ++ { ++ line_out[offset_out++] = c; ++ continue; ++ } ++ + /* Look for the last blank. */ + while (logical_end) + { +@@ -215,11 +252,221 @@ fold_file (char const *filename, size_t width) + line_out[offset_out++] = c; + } + +- saved_errno = errno; ++ *saved_errno = errno; + + if (offset_out) + fwrite (line_out, sizeof (char), (size_t) offset_out, stdout); + ++} ++ ++#if HAVE_MBRTOWC ++static void ++fold_multibyte_text (FILE *istream, size_t width, int *saved_errno) ++{ ++ char buf[MB_LEN_MAX + BUFSIZ]; /* For spooling a read byte sequence. */ ++ size_t buflen = 0; /* The length of the byte sequence in buf. */ ++ char *bufpos = buf; /* Next read position of BUF. */ ++ wint_t wc; /* A gotten wide character. */ ++ size_t mblength; /* The byte size of a multibyte character which shows ++ as same character as WC. */ ++ mbstate_t state, state_bak; /* State of the stream. */ ++ int convfail = 0; /* 1, when conversion is failed. Otherwise 0. */ ++ ++ static char *line_out = NULL; ++ size_t offset_out = 0; /* Index in `line_out' for next char. */ ++ static size_t allocated_out = 0; ++ ++ int increment; ++ size_t column = 0; ++ ++ size_t last_blank_pos; ++ size_t last_blank_column; ++ int is_blank_seen; ++ int last_blank_increment = 0; ++ int is_bs_following_last_blank; ++ size_t bs_following_last_blank_num; ++ int is_cr_after_last_blank; ++ ++#define CLEAR_FLAGS \ ++ do \ ++ { \ ++ last_blank_pos = 0; \ ++ last_blank_column = 0; \ ++ is_blank_seen = 0; \ ++ is_bs_following_last_blank = 0; \ ++ bs_following_last_blank_num = 0; \ ++ is_cr_after_last_blank = 0; \ ++ } \ ++ while (0) ++ ++#define START_NEW_LINE \ ++ do \ ++ { \ ++ putchar ('\n'); \ ++ column = 0; \ ++ offset_out = 0; \ ++ CLEAR_FLAGS; \ ++ } \ ++ while (0) ++ ++ CLEAR_FLAGS; ++ memset (&state, '\0', sizeof(mbstate_t)); ++ ++ for (;; bufpos += mblength, buflen -= mblength) ++ { ++ if (buflen < MB_LEN_MAX && !feof (istream) && !ferror (istream)) ++ { ++ memmove (buf, bufpos, buflen); ++ buflen += fread (buf + buflen, sizeof(char), BUFSIZ, istream); ++ bufpos = buf; ++ } ++ ++ if (buflen < 1) ++ break; ++ ++ /* Get a wide character. */ ++ state_bak = state; ++ mblength = mbrtowc ((wchar_t *)&wc, bufpos, buflen, &state); ++ ++ switch (mblength) ++ { ++ case (size_t)-1: ++ case (size_t)-2: ++ convfail++; ++ state = state_bak; ++ /* Fall through. */ ++ ++ case 0: ++ mblength = 1; ++ break; ++ } ++ ++rescan: ++ if (operating_mode == byte_mode) /* byte mode */ ++ increment = mblength; ++ else if (operating_mode == character_mode) /* character mode */ ++ increment = 1; ++ else /* column mode */ ++ { ++ if (convfail) ++ increment = 1; ++ else ++ { ++ switch (wc) ++ { ++ case L'\n': ++ fwrite (line_out, sizeof(char), offset_out, stdout); ++ START_NEW_LINE; ++ continue; ++ ++ case L'\b': ++ increment = (column > 0) ? -1 : 0; ++ break; ++ ++ case L'\r': ++ increment = -1 * column; ++ break; ++ ++ case L'\t': ++ increment = 8 - column % 8; ++ break; ++ ++ default: ++ increment = wcwidth (wc); ++ increment = (increment < 0) ? 0 : increment; ++ } ++ } ++ } ++ ++ if (column + increment > width && break_spaces && last_blank_pos) ++ { ++ fwrite (line_out, sizeof(char), last_blank_pos, stdout); ++ putchar ('\n'); ++ ++ offset_out = offset_out - last_blank_pos; ++ column = column - last_blank_column + ((is_cr_after_last_blank) ++ ? last_blank_increment : bs_following_last_blank_num); ++ memmove (line_out, line_out + last_blank_pos, offset_out); ++ CLEAR_FLAGS; ++ goto rescan; ++ } ++ ++ if (column + increment > width && column != 0) ++ { ++ fwrite (line_out, sizeof(char), offset_out, stdout); ++ START_NEW_LINE; ++ goto rescan; ++ } ++ ++ if (allocated_out < offset_out + mblength) ++ { ++ line_out = X2REALLOC (line_out, &allocated_out); ++ } ++ ++ memcpy (line_out + offset_out, bufpos, mblength); ++ offset_out += mblength; ++ column += increment; ++ ++ if (is_blank_seen && !convfail && wc == L'\r') ++ is_cr_after_last_blank = 1; ++ ++ if (is_bs_following_last_blank && !convfail && wc == L'\b') ++ ++bs_following_last_blank_num; ++ else ++ is_bs_following_last_blank = 0; ++ ++ if (break_spaces && !convfail && iswblank (wc)) ++ { ++ last_blank_pos = offset_out; ++ last_blank_column = column; ++ is_blank_seen = 1; ++ last_blank_increment = increment; ++ is_bs_following_last_blank = 1; ++ bs_following_last_blank_num = 0; ++ is_cr_after_last_blank = 0; ++ } ++ } ++ ++ *saved_errno = errno; ++ ++ if (offset_out) ++ fwrite (line_out, sizeof (char), (size_t) offset_out, stdout); ++ ++} ++#endif ++ ++/* Fold file FILENAME, or standard input if FILENAME is "-", ++ to stdout, with maximum line length WIDTH. ++ Return 0 if successful, 1 if an error occurs. */ ++ ++static bool ++fold_file (char const *filename, size_t width) ++{ ++ FILE *istream; ++ int saved_errno; ++ ++ if (STREQ (filename, "-")) ++ { ++ istream = stdin; ++ have_read_stdin = 1; ++ } ++ else ++ istream = fopen (filename, "r"); ++ ++ if (istream == NULL) ++ { ++ error (0, errno, "%s", filename); ++ return 1; ++ } ++ ++ /* Define how ISTREAM is being folded. */ ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ fold_multibyte_text (istream, width, &saved_errno); ++ else ++#endif ++ fold_text (istream, width, &saved_errno); ++ + if (ferror (istream)) + { + error (0, saved_errno, "%s", quotef (filename)); +@@ -252,7 +499,8 @@ main (int argc, char **argv) + + atexit (close_stdout); + +- break_spaces = count_bytes = have_read_stdin = false; ++ operating_mode = column_mode; ++ break_spaces = have_read_stdin = false; + + while ((optc = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1) + { +@@ -261,7 +509,15 @@ main (int argc, char **argv) + switch (optc) + { + case 'b': /* Count bytes rather than columns. */ +- count_bytes = true; ++ if (operating_mode != column_mode) ++ FATAL_ERROR (_("only one way of folding may be specified")); ++ operating_mode = byte_mode; ++ break; ++ ++ case 'c': ++ if (operating_mode != column_mode) ++ FATAL_ERROR (_("only one way of folding may be specified")); ++ operating_mode = character_mode; + break; + + case 's': /* Break at word boundaries. */ +diff --git a/src/join.c b/src/join.c +index 98b461c..9990f38 100644 +--- a/src/join.c ++++ b/src/join.c +@@ -22,19 +22,33 @@ + #include + #include + ++/* Get mbstate_t, mbrtowc(), mbrtowc(), wcwidth(). */ ++#if HAVE_WCHAR_H ++# include ++#endif ++ ++/* Get iswblank(), towupper. */ ++#if HAVE_WCTYPE_H ++# include ++#endif ++ + #include "system.h" + #include "die.h" + #include "error.h" + #include "fadvise.h" + #include "hard-locale.h" + #include "linebuffer.h" +-#include "memcasecmp.h" + #include "quote.h" + #include "stdio--.h" + #include "xmemcoll.h" + #include "xstrtol.h" + #include "argmatch.h" + ++/* Some systems, like BeOS, have multibyte encodings but lack mbstate_t. */ ++#if HAVE_MBRTOWC && defined mbstate_t ++# define mbrtowc(pwc, s, n, ps) (mbrtowc) (pwc, s, n, 0) ++#endif ++ + /* The official name of this program (e.g., no 'g' prefix). */ + #define PROGRAM_NAME "join" + +@@ -136,10 +150,12 @@ static struct outlist outlist_head; + /* Last element in 'outlist', where a new element can be added. */ + static struct outlist *outlist_end = &outlist_head; + +-/* Tab character separating fields. If negative, fields are separated +- by any nonempty string of blanks, otherwise by exactly one +- tab character whose value (when cast to unsigned char) equals TAB. */ +-static int tab = -1; ++/* Tab character separating fields. If NULL, fields are separated ++ by any nonempty string of blanks. */ ++static char *tab = NULL; ++ ++/* The number of bytes used for tab. */ ++static size_t tablen = 0; + + /* If nonzero, check that the input is correctly ordered. */ + static enum +@@ -276,13 +292,14 @@ xfields (struct line *line) + if (ptr == lim) + return; + +- if (0 <= tab && tab != '\n') ++ if (tab != NULL) + { ++ unsigned char t = tab[0]; + char *sep; +- for (; (sep = memchr (ptr, tab, lim - ptr)) != NULL; ptr = sep + 1) ++ for (; (sep = memchr (ptr, t, lim - ptr)) != NULL; ptr = sep + 1) + extract_field (line, ptr, sep - ptr); + } +- else if (tab < 0) ++ else + { + /* Skip leading blanks before the first field. */ + while (field_sep (*ptr)) +@@ -306,6 +323,147 @@ xfields (struct line *line) + extract_field (line, ptr, lim - ptr); + } + ++#if HAVE_MBRTOWC ++static void ++xfields_multibyte (struct line *line) ++{ ++ char *ptr = line->buf.buffer; ++ char const *lim = ptr + line->buf.length - 1; ++ wchar_t wc = 0; ++ size_t mblength = 1; ++ mbstate_t state, state_bak; ++ ++ memset (&state, 0, sizeof (mbstate_t)); ++ ++ if (ptr >= lim) ++ return; ++ ++ if (tab != NULL) ++ { ++ char *sep = ptr; ++ for (; ptr < lim; ptr = sep + mblength) ++ { ++ sep = ptr; ++ while (sep < lim) ++ { ++ state_bak = state; ++ mblength = mbrtowc (&wc, sep, lim - sep + 1, &state); ++ ++ if (mblength == (size_t)-1 || mblength == (size_t)-2) ++ { ++ mblength = 1; ++ state = state_bak; ++ } ++ mblength = (mblength < 1) ? 1 : mblength; ++ ++ if (mblength == tablen && !memcmp (sep, tab, mblength)) ++ break; ++ else ++ { ++ sep += mblength; ++ continue; ++ } ++ } ++ ++ if (sep >= lim) ++ break; ++ ++ extract_field (line, ptr, sep - ptr); ++ } ++ } ++ else ++ { ++ /* Skip leading blanks before the first field. */ ++ while(ptr < lim) ++ { ++ state_bak = state; ++ mblength = mbrtowc (&wc, ptr, lim - ptr + 1, &state); ++ ++ if (mblength == (size_t)-1 || mblength == (size_t)-2) ++ { ++ mblength = 1; ++ state = state_bak; ++ break; ++ } ++ mblength = (mblength < 1) ? 1 : mblength; ++ ++ if (!iswblank(wc) && wc != '\n') ++ break; ++ ptr += mblength; ++ } ++ ++ do ++ { ++ char *sep; ++ state_bak = state; ++ mblength = mbrtowc (&wc, ptr, lim - ptr + 1, &state); ++ if (mblength == (size_t)-1 || mblength == (size_t)-2) ++ { ++ mblength = 1; ++ state = state_bak; ++ break; ++ } ++ mblength = (mblength < 1) ? 1 : mblength; ++ ++ sep = ptr + mblength; ++ while (sep < lim) ++ { ++ state_bak = state; ++ mblength = mbrtowc (&wc, sep, lim - sep + 1, &state); ++ if (mblength == (size_t)-1 || mblength == (size_t)-2) ++ { ++ mblength = 1; ++ state = state_bak; ++ break; ++ } ++ mblength = (mblength < 1) ? 1 : mblength; ++ ++ if (iswblank (wc) || wc == '\n') ++ break; ++ ++ sep += mblength; ++ } ++ ++ extract_field (line, ptr, sep - ptr); ++ if (sep >= lim) ++ return; ++ ++ state_bak = state; ++ mblength = mbrtowc (&wc, sep, lim - sep + 1, &state); ++ if (mblength == (size_t)-1 || mblength == (size_t)-2) ++ { ++ mblength = 1; ++ state = state_bak; ++ break; ++ } ++ mblength = (mblength < 1) ? 1 : mblength; ++ ++ ptr = sep + mblength; ++ while (ptr < lim) ++ { ++ state_bak = state; ++ mblength = mbrtowc (&wc, ptr, lim - ptr + 1, &state); ++ if (mblength == (size_t)-1 || mblength == (size_t)-2) ++ { ++ mblength = 1; ++ state = state_bak; ++ break; ++ } ++ mblength = (mblength < 1) ? 1 : mblength; ++ ++ if (!iswblank (wc) && wc != '\n') ++ break; ++ ++ ptr += mblength; ++ } ++ } ++ while (ptr < lim); ++ } ++ ++ extract_field (line, ptr, lim - ptr); ++} ++#endif ++ + static void + freeline (struct line *line) + { +@@ -327,56 +485,133 @@ keycmp (struct line const *line1, struct line const *line2, + size_t jf_1, size_t jf_2) + { + /* Start of field to compare in each file. */ +- char *beg1; +- char *beg2; +- +- size_t len1; +- size_t len2; /* Length of fields to compare. */ ++ char *beg[2]; ++ char *copy[2]; ++ size_t len[2]; /* Length of fields to compare. */ + int diff; ++ int i, j; ++ int mallocd = 0; + + if (jf_1 < line1->nfields) + { +- beg1 = line1->fields[jf_1].beg; +- len1 = line1->fields[jf_1].len; ++ beg[0] = line1->fields[jf_1].beg; ++ len[0] = line1->fields[jf_1].len; + } + else + { +- beg1 = NULL; +- len1 = 0; ++ beg[0] = NULL; ++ len[0] = 0; + } + + if (jf_2 < line2->nfields) + { +- beg2 = line2->fields[jf_2].beg; +- len2 = line2->fields[jf_2].len; ++ beg[1] = line2->fields[jf_2].beg; ++ len[1] = line2->fields[jf_2].len; + } + else + { +- beg2 = NULL; +- len2 = 0; ++ beg[1] = NULL; ++ len[1] = 0; + } + +- if (len1 == 0) +- return len2 == 0 ? 0 : -1; +- if (len2 == 0) ++ if (len[0] == 0) ++ return len[1] == 0 ? 0 : -1; ++ if (len[1] == 0) + return 1; + + if (ignore_case) + { +- /* FIXME: ignore_case does not work with NLS (in particular, +- with multibyte chars). */ +- diff = memcasecmp (beg1, beg2, MIN (len1, len2)); ++#ifdef HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ { ++ size_t mblength; ++ wchar_t wc, uwc; ++ mbstate_t state, state_bak; ++ ++ memset (&state, '\0', sizeof (mbstate_t)); ++ ++ for (i = 0; i < 2; i++) ++ { ++ mallocd = 1; ++ copy[i] = xmalloc (len[i] + 1); ++ memset (copy[i], '\0',len[i] + 1); ++ ++ for (j = 0; j < MIN (len[0], len[1]);) ++ { ++ state_bak = state; ++ mblength = mbrtowc (&wc, beg[i] + j, len[i] - j, &state); ++ ++ switch (mblength) ++ { ++ case (size_t) -1: ++ case (size_t) -2: ++ state = state_bak; ++ /* Fall through */ ++ case 0: ++ mblength = 1; ++ break; ++ ++ default: ++ uwc = towupper (wc); ++ ++ if (uwc != wc) ++ { ++ mbstate_t state_wc; ++ size_t mblen; ++ ++ memset (&state_wc, '\0', sizeof (mbstate_t)); ++ mblen = wcrtomb (copy[i] + j, uwc, &state_wc); ++ assert (mblen != (size_t)-1); ++ } ++ else ++ memcpy (copy[i] + j, beg[i] + j, mblength); ++ } ++ j += mblength; ++ } ++ copy[i][j] = '\0'; ++ } ++ } ++ else ++#endif ++ { ++ for (i = 0; i < 2; i++) ++ { ++ mallocd = 1; ++ copy[i] = xmalloc (len[i] + 1); ++ ++ for (j = 0; j < MIN (len[0], len[1]); j++) ++ copy[i][j] = toupper (beg[i][j]); ++ ++ copy[i][j] = '\0'; ++ } ++ } + } + else + { +- if (hard_LC_COLLATE) +- return xmemcoll (beg1, len1, beg2, len2); +- diff = memcmp (beg1, beg2, MIN (len1, len2)); ++ copy[0] = beg[0]; ++ copy[1] = beg[1]; + } + ++ if (hard_LC_COLLATE) ++ { ++ diff = xmemcoll ((char *) copy[0], len[0], (char *) copy[1], len[1]); ++ ++ if (mallocd) ++ for (i = 0; i < 2; i++) ++ free (copy[i]); ++ ++ return diff; ++ } ++ diff = memcmp (copy[0], copy[1], MIN (len[0], len[1])); ++ ++ if (mallocd) ++ for (i = 0; i < 2; i++) ++ free (copy[i]); ++ ++ + if (diff) + return diff; +- return len1 < len2 ? -1 : len1 != len2; ++ return len[0] - len[1]; + } + + /* Check that successive input lines PREV and CURRENT from input file +@@ -468,6 +703,11 @@ get_line (FILE *fp, struct line **linep, int which) + } + ++line_no[which - 1]; + ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ xfields_multibyte (line); ++ else ++#endif + xfields (line); + + if (prevline[which - 1]) +@@ -563,21 +803,28 @@ prfield (size_t n, struct line const *line) + + /* Output all the fields in line, other than the join field. */ + ++#define PUT_TAB_CHAR \ ++ do \ ++ { \ ++ (tab != NULL) ? \ ++ fwrite(tab, sizeof(char), tablen, stdout) : putchar (' '); \ ++ } \ ++ while (0) ++ + static void + prfields (struct line const *line, size_t join_field, size_t autocount) + { + size_t i; + size_t nfields = autoformat ? autocount : line->nfields; +- char output_separator = tab < 0 ? ' ' : tab; + + for (i = 0; i < join_field && i < nfields; ++i) + { +- putchar (output_separator); ++ PUT_TAB_CHAR; + prfield (i, line); + } + for (i = join_field + 1; i < nfields; ++i) + { +- putchar (output_separator); ++ PUT_TAB_CHAR; + prfield (i, line); + } + } +@@ -588,7 +835,6 @@ static void + prjoin (struct line const *line1, struct line const *line2) + { + const struct outlist *outlist; +- char output_separator = tab < 0 ? ' ' : tab; + size_t field; + struct line const *line; + +@@ -622,7 +868,7 @@ prjoin (struct line const *line1, struct line const *line2) + o = o->next; + if (o == NULL) + break; +- putchar (output_separator); ++ PUT_TAB_CHAR; + } + putchar (eolchar); + } +@@ -1098,20 +1344,43 @@ main (int argc, char **argv) + + case 't': + { +- unsigned char newtab = optarg[0]; ++ char *newtab = NULL; ++ size_t newtablen; ++ newtab = xstrdup (optarg); ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ { ++ mbstate_t state; ++ ++ memset (&state, 0, sizeof (mbstate_t)); ++ newtablen = mbrtowc (NULL, newtab, ++ strnlen (newtab, MB_LEN_MAX), ++ &state); ++ if (newtablen == (size_t) 0 ++ || newtablen == (size_t) -1 ++ || newtablen == (size_t) -2) ++ newtablen = 1; ++ } ++ else ++#endif ++ newtablen = 1; + if (! newtab) +- newtab = '\n'; /* '' => process the whole line. */ ++ newtab = (char*)"\n"; /* '' => process the whole line. */ + else if (optarg[1]) + { +- if (STREQ (optarg, "\\0")) +- newtab = '\0'; +- else +- die (EXIT_FAILURE, 0, _("multi-character tab %s"), +- quote (optarg)); ++ if (newtablen == 1 && newtab[1]) ++ { ++ if (STREQ (newtab, "\\0")) ++ newtab[0] = '\0'; ++ } ++ } ++ if (tab != NULL && strcmp (tab, newtab)) ++ { ++ free (newtab); ++ die (EXIT_FAILURE, 0, _("incompatible tabs")); + } +- if (0 <= tab && tab != newtab) +- die (EXIT_FAILURE, 0, _("incompatible tabs")); + tab = newtab; ++ tablen = newtablen; + } + break; + +diff --git a/src/pr.c b/src/pr.c +index 26f221f..633f50e 100644 +--- a/src/pr.c ++++ b/src/pr.c +@@ -311,6 +311,24 @@ + + #include + #include ++ ++/* Get MB_LEN_MAX. */ ++#include ++/* MB_LEN_MAX is incorrectly defined to be 1 in at least one GCC ++ installation; work around this configuration error. */ ++#if !defined MB_LEN_MAX || MB_LEN_MAX == 1 ++# define MB_LEN_MAX 16 ++#endif ++ ++/* Get MB_CUR_MAX. */ ++#include ++ ++/* Solaris 2.5 has a bug: must be included before . */ ++/* Get mbstate_t, mbrtowc(), wcwidth(). */ ++#if HAVE_WCHAR_H ++# include ++#endif ++ + #include "system.h" + #include "die.h" + #include "error.h" +@@ -325,6 +343,18 @@ + #include "xstrtol-error.h" + #include "xdectoint.h" + ++/* Some systems, like BeOS, have multibyte encodings but lack mbstate_t. */ ++#if HAVE_MBRTOWC && defined mbstate_t ++# define mbrtowc(pwc, s, n, ps) (mbrtowc) (pwc, s, n, 0) ++#endif ++ ++#ifndef HAVE_DECL_WCWIDTH ++"this configure-time declaration test was not run" ++#endif ++#if !HAVE_DECL_WCWIDTH ++extern int wcwidth (); ++#endif ++ + /* The official name of this program (e.g., no 'g' prefix). */ + #define PROGRAM_NAME "pr" + +@@ -417,7 +447,20 @@ struct COLUMN + + typedef struct COLUMN COLUMN; + +-static int char_to_clump (char c); ++/* Funtion pointers to switch functions for single byte locale or for ++ multibyte locale. If multibyte functions do not exist in your sysytem, ++ these pointers always point the function for single byte locale. */ ++static void (*print_char) (char c); ++static int (*char_to_clump) (char c); ++ ++/* Functions for single byte locale. */ ++static void print_char_single (char c); ++static int char_to_clump_single (char c); ++ ++/* Functions for multibyte locale. */ ++static void print_char_multi (char c); ++static int char_to_clump_multi (char c); ++ + static bool read_line (COLUMN *p); + static bool print_page (void); + static bool print_stored (COLUMN *p); +@@ -429,6 +472,7 @@ static void add_line_number (COLUMN *p); + static void getoptnum (const char *n_str, int min, int *num, + const char *errfmt); + static void getoptarg (char *arg, char switch_char, char *character, ++ int *character_length, int *character_width, + int *number); + static void print_files (int number_of_files, char **av); + static void init_parameters (int number_of_files); +@@ -442,7 +486,6 @@ static void store_char (char c); + static void pad_down (unsigned int lines); + static void read_rest_of_line (COLUMN *p); + static void skip_read (COLUMN *p, int column_number); +-static void print_char (char c); + static void cleanup (void); + static void print_sep_string (void); + static void separator_string (const char *optarg_S); +@@ -454,7 +497,7 @@ static COLUMN *column_vector; + we store the leftmost columns contiguously in buff. + To print a line from buff, get the index of the first character + from line_vector[i], and print up to line_vector[i + 1]. */ +-static char *buff; ++static unsigned char *buff; + + /* Index of the position in buff where the next character + will be stored. */ +@@ -558,7 +601,7 @@ static int chars_per_column; + static bool untabify_input = false; + + /* (-e) The input tab character. */ +-static char input_tab_char = '\t'; ++static char input_tab_char[MB_LEN_MAX] = "\t"; + + /* (-e) Tabstops are at chars_per_tab, 2*chars_per_tab, 3*chars_per_tab, ... + where the leftmost column is 1. */ +@@ -568,7 +611,10 @@ static int chars_per_input_tab = 8; + static bool tabify_output = false; + + /* (-i) The output tab character. */ +-static char output_tab_char = '\t'; ++static char output_tab_char[MB_LEN_MAX] = "\t"; ++ ++/* (-i) The byte length of output tab character. */ ++static int output_tab_char_length = 1; + + /* (-i) The width of the output tab. */ + static int chars_per_output_tab = 8; +@@ -638,7 +684,13 @@ static int line_number; + static bool numbered_lines = false; + + /* (-n) Character which follows each line number. */ +-static char number_separator = '\t'; ++static char number_separator[MB_LEN_MAX] = "\t"; ++ ++/* (-n) The byte length of the character which follows each line number. */ ++static int number_separator_length = 1; ++ ++/* (-n) The character width of the character which follows each line number. */ ++static int number_separator_width = 0; + + /* (-n) line counting starts with 1st line of input file (not with 1st + line of 1st page printed). */ +@@ -691,6 +743,7 @@ static bool use_col_separator = false; + -a|COLUMN|-m is a 'space' and with the -J option a 'tab'. */ + static char const *col_sep_string = ""; + static int col_sep_length = 0; ++static int col_sep_width = 0; + static char *column_separator = (char *) " "; + static char *line_separator = (char *) "\t"; + +@@ -852,6 +905,13 @@ separator_string (const char *optarg_S) + integer_overflow (); + col_sep_length = len; + col_sep_string = optarg_S; ++ ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ col_sep_width = mbswidth (col_sep_string, 0); ++ else ++#endif ++ col_sep_width = col_sep_length; + } + + int +@@ -876,6 +936,21 @@ main (int argc, char **argv) + + atexit (close_stdout); + ++/* Define which functions are used, the ones for single byte locale or the ones ++ for multibyte locale. */ ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ { ++ print_char = print_char_multi; ++ char_to_clump = char_to_clump_multi; ++ } ++ else ++#endif ++ { ++ print_char = print_char_single; ++ char_to_clump = char_to_clump_single; ++ } ++ + n_files = 0; + file_names = (argc > 1 + ? xnmalloc (argc - 1, sizeof (char *)) +@@ -952,8 +1027,12 @@ main (int argc, char **argv) + break; + case 'e': + if (optarg) +- getoptarg (optarg, 'e', &input_tab_char, +- &chars_per_input_tab); ++ { ++ int dummy_length, dummy_width; ++ ++ getoptarg (optarg, 'e', input_tab_char, &dummy_length, ++ &dummy_width, &chars_per_input_tab); ++ } + /* Could check tab width > 0. */ + untabify_input = true; + break; +@@ -966,8 +1045,12 @@ main (int argc, char **argv) + break; + case 'i': + if (optarg) +- getoptarg (optarg, 'i', &output_tab_char, +- &chars_per_output_tab); ++ { ++ int dummy_width; ++ ++ getoptarg (optarg, 'i', output_tab_char, &output_tab_char_length, ++ &dummy_width, &chars_per_output_tab); ++ } + /* Could check tab width > 0. */ + tabify_output = true; + break; +@@ -985,8 +1068,8 @@ main (int argc, char **argv) + case 'n': + numbered_lines = true; + if (optarg) +- getoptarg (optarg, 'n', &number_separator, +- &chars_per_number); ++ getoptarg (optarg, 'n', number_separator, &number_separator_length, ++ &number_separator_width, &chars_per_number); + break; + case 'N': + skip_count = false; +@@ -1011,6 +1094,7 @@ main (int argc, char **argv) + /* Reset an additional input of -s, -S dominates -s */ + col_sep_string = ""; + col_sep_length = 0; ++ col_sep_width = 0; + use_col_separator = true; + if (optarg) + separator_string (optarg); +@@ -1166,10 +1250,45 @@ getoptnum (const char *n_str, int min, int *num, const char *err) + a number. */ + + static void +-getoptarg (char *arg, char switch_char, char *character, int *number) ++getoptarg (char *arg, char switch_char, char *character, int *character_length, ++ int *character_width, int *number) + { + if (!ISDIGIT (*arg)) +- *character = *arg++; ++ { ++#ifdef HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) /* for multibyte locale. */ ++ { ++ wchar_t wc; ++ size_t mblength; ++ int width; ++ mbstate_t state = {'\0'}; ++ ++ mblength = mbrtowc (&wc, arg, strnlen(arg, MB_LEN_MAX), &state); ++ ++ if (mblength == (size_t)-1 || mblength == (size_t)-2) ++ { ++ *character_length = 1; ++ *character_width = 1; ++ } ++ else ++ { ++ *character_length = (mblength < 1) ? 1 : mblength; ++ width = wcwidth (wc); ++ *character_width = (width < 0) ? 0 : width; ++ } ++ ++ strncpy (character, arg, *character_length); ++ arg += *character_length; ++ } ++ else /* for single byte locale. */ ++#endif ++ { ++ *character = *arg++; ++ *character_length = 1; ++ *character_width = 1; ++ } ++ } ++ + if (*arg) + { + long int tmp_long; +@@ -1191,6 +1310,11 @@ static void + init_parameters (int number_of_files) + { + int chars_used_by_number = 0; ++ int mb_len = 1; ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ mb_len = MB_LEN_MAX; ++#endif + + lines_per_body = lines_per_page - lines_per_header - lines_per_footer; + if (lines_per_body <= 0) +@@ -1228,7 +1352,7 @@ init_parameters (int number_of_files) + else + col_sep_string = column_separator; + +- col_sep_length = 1; ++ col_sep_length = col_sep_width = 1; + use_col_separator = true; + } + /* It's rather pointless to define a TAB separator with column +@@ -1258,11 +1382,11 @@ init_parameters (int number_of_files) + + TAB_WIDTH (chars_per_input_tab, chars_per_number); */ + + /* Estimate chars_per_text without any margin and keep it constant. */ +- if (number_separator == '\t') ++ if (number_separator[0] == '\t') + number_width = (chars_per_number + + TAB_WIDTH (chars_per_default_tab, chars_per_number)); + else +- number_width = chars_per_number + 1; ++ number_width = chars_per_number + number_separator_width; + + /* The number is part of the column width unless we are + printing files in parallel. */ +@@ -1271,7 +1395,7 @@ init_parameters (int number_of_files) + } + + int sep_chars, useful_chars; +- if (INT_MULTIPLY_WRAPV (columns - 1, col_sep_length, &sep_chars)) ++ if (INT_MULTIPLY_WRAPV (columns - 1, col_sep_width, &sep_chars)) + sep_chars = INT_MAX; + if (INT_SUBTRACT_WRAPV (chars_per_line - chars_used_by_number, sep_chars, + &useful_chars)) +@@ -1294,7 +1418,7 @@ init_parameters (int number_of_files) + We've to use 8 as the lower limit, if we use chars_per_default_tab = 8 + to expand a tab which is not an input_tab-char. */ + free (clump_buff); +- clump_buff = xmalloc (MAX (8, chars_per_input_tab)); ++ clump_buff = xmalloc (mb_len * MAX (8, chars_per_input_tab)); + } + + /* Open the necessary files, +@@ -1400,7 +1524,7 @@ init_funcs (void) + + /* Enlarge p->start_position of first column to use the same form of + padding_not_printed with all columns. */ +- h = h + col_sep_length; ++ h = h + col_sep_width; + + /* This loop takes care of all but the rightmost column. */ + +@@ -1434,7 +1558,7 @@ init_funcs (void) + } + else + { +- h = h_next + col_sep_length; ++ h = h_next + col_sep_width; + h_next = h + chars_per_column; + } + } +@@ -1725,9 +1849,9 @@ static void + align_column (COLUMN *p) + { + padding_not_printed = p->start_position; +- if (col_sep_length < padding_not_printed) ++ if (col_sep_width < padding_not_printed) + { +- pad_across_to (padding_not_printed - col_sep_length); ++ pad_across_to (padding_not_printed - col_sep_width); + padding_not_printed = ANYWHERE; + } + +@@ -2002,13 +2126,13 @@ store_char (char c) + /* May be too generous. */ + buff = X2REALLOC (buff, &buff_allocated); + } +- buff[buff_current++] = c; ++ buff[buff_current++] = (unsigned char) c; + } + + static void + add_line_number (COLUMN *p) + { +- int i; ++ int i, j; + char *s; + int num_width; + +@@ -2025,22 +2149,24 @@ add_line_number (COLUMN *p) + /* Tabification is assumed for multiple columns, also for n-separators, + but 'default n-separator = TAB' hasn't been given priority over + equal column_width also specified by POSIX. */ +- if (number_separator == '\t') ++ if (number_separator[0] == '\t') + { + i = number_width - chars_per_number; + while (i-- > 0) + (p->char_func) (' '); + } + else +- (p->char_func) (number_separator); ++ for (j = 0; j < number_separator_length; j++) ++ (p->char_func) (number_separator[j]); + } + else + /* To comply with POSIX, we avoid any expansion of default TAB + separator with a single column output. No column_width requirement + has to be considered. */ + { +- (p->char_func) (number_separator); +- if (number_separator == '\t') ++ for (j = 0; j < number_separator_length; j++) ++ (p->char_func) (number_separator[j]); ++ if (number_separator[0] == '\t') + output_position = POS_AFTER_TAB (chars_per_output_tab, + output_position); + } +@@ -2199,7 +2325,7 @@ print_white_space (void) + while (goal - h_old > 1 + && (h_new = POS_AFTER_TAB (chars_per_output_tab, h_old)) <= goal) + { +- putchar (output_tab_char); ++ fwrite (output_tab_char, sizeof(char), output_tab_char_length, stdout); + h_old = h_new; + } + while (++h_old <= goal) +@@ -2219,6 +2345,7 @@ print_sep_string (void) + { + char const *s = col_sep_string; + int l = col_sep_length; ++ int not_space_flag; + + if (separators_not_printed <= 0) + { +@@ -2230,6 +2357,7 @@ print_sep_string (void) + { + for (; separators_not_printed > 0; --separators_not_printed) + { ++ not_space_flag = 0; + while (l-- > 0) + { + /* 3 types of sep_strings: spaces only, spaces and chars, +@@ -2243,12 +2371,15 @@ print_sep_string (void) + } + else + { ++ not_space_flag = 1; + if (spaces_not_printed > 0) + print_white_space (); + putchar (*s++); +- ++output_position; + } + } ++ if (not_space_flag) ++ output_position += col_sep_width; ++ + /* sep_string ends with some spaces */ + if (spaces_not_printed > 0) + print_white_space (); +@@ -2276,7 +2407,7 @@ print_clump (COLUMN *p, int n, char *clump) + required number of tabs and spaces. */ + + static void +-print_char (char c) ++print_char_single (char c) + { + if (tabify_output) + { +@@ -2300,6 +2431,74 @@ print_char (char c) + putchar (c); + } + ++#ifdef HAVE_MBRTOWC ++static void ++print_char_multi (char c) ++{ ++ static size_t mbc_pos = 0; ++ static char mbc[MB_LEN_MAX] = {'\0'}; ++ static mbstate_t state = {'\0'}; ++ mbstate_t state_bak; ++ wchar_t wc; ++ size_t mblength; ++ int width; ++ ++ if (tabify_output) ++ { ++ state_bak = state; ++ mbc[mbc_pos++] = c; ++ mblength = mbrtowc (&wc, mbc, mbc_pos, &state); ++ ++ while (mbc_pos > 0) ++ { ++ switch (mblength) ++ { ++ case (size_t)-2: ++ state = state_bak; ++ return; ++ ++ case (size_t)-1: ++ state = state_bak; ++ ++output_position; ++ putchar (mbc[0]); ++ memmove (mbc, mbc + 1, MB_CUR_MAX - 1); ++ --mbc_pos; ++ break; ++ ++ case 0: ++ mblength = 1; ++ ++ default: ++ if (wc == L' ') ++ { ++ memmove (mbc, mbc + mblength, MB_CUR_MAX - mblength); ++ --mbc_pos; ++ ++spaces_not_printed; ++ return; ++ } ++ else if (spaces_not_printed > 0) ++ print_white_space (); ++ ++ /* Nonprintables are assumed to have width 0, except L'\b'. */ ++ if ((width = wcwidth (wc)) < 1) ++ { ++ if (wc == L'\b') ++ --output_position; ++ } ++ else ++ output_position += width; ++ ++ fwrite (mbc, sizeof(char), mblength, stdout); ++ memmove (mbc, mbc + mblength, MB_CUR_MAX - mblength); ++ mbc_pos -= mblength; ++ } ++ } ++ return; ++ } ++ putchar (c); ++} ++#endif ++ + /* Skip to page PAGE before printing. + PAGE may be larger than total number of pages. */ + +@@ -2477,9 +2676,9 @@ read_line (COLUMN *p) + align_empty_cols = false; + } + +- if (col_sep_length < padding_not_printed) ++ if (col_sep_width < padding_not_printed) + { +- pad_across_to (padding_not_printed - col_sep_length); ++ pad_across_to (padding_not_printed - col_sep_width); + padding_not_printed = ANYWHERE; + } + +@@ -2548,7 +2747,7 @@ print_stored (COLUMN *p) + COLUMN *q; + + int line = p->current_line++; +- char *first = &buff[line_vector[line]]; ++ unsigned char *first = &buff[line_vector[line]]; + /* FIXME + UMR: Uninitialized memory read: + * This is occurring while in: +@@ -2560,7 +2759,7 @@ print_stored (COLUMN *p) + xmalloc [xmalloc.c:94] + init_store_cols [pr.c:1648] + */ +- char *last = &buff[line_vector[line + 1]]; ++ unsigned char *last = &buff[line_vector[line + 1]]; + + pad_vertically = true; + +@@ -2580,9 +2779,9 @@ print_stored (COLUMN *p) + } + } + +- if (col_sep_length < padding_not_printed) ++ if (col_sep_width < padding_not_printed) + { +- pad_across_to (padding_not_printed - col_sep_length); ++ pad_across_to (padding_not_printed - col_sep_width); + padding_not_printed = ANYWHERE; + } + +@@ -2595,8 +2794,8 @@ print_stored (COLUMN *p) + if (spaces_not_printed == 0) + { + output_position = p->start_position + end_vector[line]; +- if (p->start_position - col_sep_length == chars_per_margin) +- output_position -= col_sep_length; ++ if (p->start_position - col_sep_width == chars_per_margin) ++ output_position -= col_sep_width; + } + + return true; +@@ -2615,7 +2814,7 @@ print_stored (COLUMN *p) + number of characters is 1.) */ + + static int +-char_to_clump (char c) ++char_to_clump_single (char c) + { + unsigned char uc = c; + char *s = clump_buff; +@@ -2625,10 +2824,10 @@ char_to_clump (char c) + int chars; + int chars_per_c = 8; + +- if (c == input_tab_char) ++ if (c == input_tab_char[0]) + chars_per_c = chars_per_input_tab; + +- if (c == input_tab_char || c == '\t') ++ if (c == input_tab_char[0] || c == '\t') + { + width = TAB_WIDTH (chars_per_c, input_position); + +@@ -2709,6 +2908,164 @@ char_to_clump (char c) + return chars; + } + ++#ifdef HAVE_MBRTOWC ++static int ++char_to_clump_multi (char c) ++{ ++ static size_t mbc_pos = 0; ++ static char mbc[MB_LEN_MAX] = {'\0'}; ++ static mbstate_t state = {'\0'}; ++ mbstate_t state_bak; ++ wchar_t wc; ++ size_t mblength; ++ int wc_width; ++ register char *s = clump_buff; ++ register int i, j; ++ char esc_buff[4]; ++ int width; ++ int chars; ++ int chars_per_c = 8; ++ ++ state_bak = state; ++ mbc[mbc_pos++] = c; ++ mblength = mbrtowc (&wc, mbc, mbc_pos, &state); ++ ++ width = 0; ++ chars = 0; ++ while (mbc_pos > 0) ++ { ++ switch (mblength) ++ { ++ case (size_t)-2: ++ state = state_bak; ++ return 0; ++ ++ case (size_t)-1: ++ state = state_bak; ++ mblength = 1; ++ ++ if (use_esc_sequence || use_cntrl_prefix) ++ { ++ width = +4; ++ chars = +4; ++ *s++ = '\\'; ++ sprintf (esc_buff, "%03o", (unsigned char) mbc[0]); ++ for (i = 0; i <= 2; ++i) ++ *s++ = (int) esc_buff[i]; ++ } ++ else ++ { ++ width += 1; ++ chars += 1; ++ *s++ = mbc[0]; ++ } ++ break; ++ ++ case 0: ++ mblength = 1; ++ /* Fall through */ ++ ++ default: ++ if (memcmp (mbc, input_tab_char, mblength) == 0) ++ chars_per_c = chars_per_input_tab; ++ ++ if (memcmp (mbc, input_tab_char, mblength) == 0 || c == '\t') ++ { ++ int width_inc; ++ ++ width_inc = TAB_WIDTH (chars_per_c, input_position); ++ width += width_inc; ++ ++ if (untabify_input) ++ { ++ for (i = width_inc; i; --i) ++ *s++ = ' '; ++ chars += width_inc; ++ } ++ else ++ { ++ for (i = 0; i < mblength; i++) ++ *s++ = mbc[i]; ++ chars += mblength; ++ } ++ } ++ else if ((wc_width = wcwidth (wc)) < 1) ++ { ++ if (use_esc_sequence) ++ { ++ for (i = 0; i < mblength; i++) ++ { ++ width += 4; ++ chars += 4; ++ *s++ = '\\'; ++ sprintf (esc_buff, "%03o", (unsigned char) mbc[i]); ++ for (j = 0; j <= 2; ++j) ++ *s++ = (int) esc_buff[j]; ++ } ++ } ++ else if (use_cntrl_prefix) ++ { ++ if (wc < 0200) ++ { ++ width += 2; ++ chars += 2; ++ *s++ = '^'; ++ *s++ = wc ^ 0100; ++ } ++ else ++ { ++ for (i = 0; i < mblength; i++) ++ { ++ width += 4; ++ chars += 4; ++ *s++ = '\\'; ++ sprintf (esc_buff, "%03o", (unsigned char) mbc[i]); ++ for (j = 0; j <= 2; ++j) ++ *s++ = (int) esc_buff[j]; ++ } ++ } ++ } ++ else if (wc == L'\b') ++ { ++ width += -1; ++ chars += 1; ++ *s++ = c; ++ } ++ else ++ { ++ width += 0; ++ chars += mblength; ++ for (i = 0; i < mblength; i++) ++ *s++ = mbc[i]; ++ } ++ } ++ else ++ { ++ width += wc_width; ++ chars += mblength; ++ for (i = 0; i < mblength; i++) ++ *s++ = mbc[i]; ++ } ++ } ++ memmove (mbc, mbc + mblength, MB_CUR_MAX - mblength); ++ mbc_pos -= mblength; ++ } ++ ++ /* Too many backspaces must put us in position 0 -- never negative. */ ++ if (width < 0 && input_position == 0) ++ { ++ chars = 0; ++ input_position = 0; ++ } ++ else if (width < 0 && input_position <= -width) ++ input_position = 0; ++ else ++ input_position += width; ++ ++ return chars; ++} ++#endif ++ + /* We've just printed some files and need to clean up things before + looking for more options and printing the next batch of files. + +diff --git a/src/sort.c b/src/sort.c +index 6d2eec5..f189a0d 100644 +--- a/src/sort.c ++++ b/src/sort.c +@@ -29,6 +29,14 @@ + #include + #include + #include ++#if HAVE_WCHAR_H ++# include ++#endif ++/* Get isw* functions. */ ++#if HAVE_WCTYPE_H ++# include ++#endif ++ + #include "system.h" + #include "argmatch.h" + #include "die.h" +@@ -157,14 +165,39 @@ static int decimal_point; + /* Thousands separator; if -1, then there isn't one. */ + static int thousands_sep; + ++/* True if -f is specified. */ ++static bool folding; ++ + /* Nonzero if the corresponding locales are hard. */ + static bool hard_LC_COLLATE; +-#if HAVE_NL_LANGINFO ++#if HAVE_LANGINFO_CODESET + static bool hard_LC_TIME; + #endif + + #define NONZERO(x) ((x) != 0) + ++/* get a multibyte character's byte length. */ ++#define GET_BYTELEN_OF_CHAR(LIM, PTR, MBLENGTH, STATE) \ ++ do \ ++ { \ ++ wchar_t wc; \ ++ mbstate_t state_bak; \ ++ \ ++ state_bak = STATE; \ ++ mblength = mbrtowc (&wc, PTR, LIM - PTR, &STATE); \ ++ \ ++ switch (MBLENGTH) \ ++ { \ ++ case (size_t)-1: \ ++ case (size_t)-2: \ ++ STATE = state_bak; \ ++ /* Fall through. */ \ ++ case 0: \ ++ MBLENGTH = 1; \ ++ } \ ++ } \ ++ while (0) ++ + /* The kind of blanks for '-b' to skip in various options. */ + enum blanktype { bl_start, bl_end, bl_both }; + +@@ -338,13 +371,11 @@ static bool reverse; + they were read if all keys compare equal. */ + static bool stable; + +-/* If TAB has this value, blanks separate fields. */ +-enum { TAB_DEFAULT = CHAR_MAX + 1 }; +- +-/* Tab character separating fields. If TAB_DEFAULT, then fields are ++/* Tab character separating fields. If tab_length is 0, then fields are + separated by the empty string between a non-blank character and a blank + character. */ +-static int tab = TAB_DEFAULT; ++static char tab[MB_LEN_MAX + 1]; ++static size_t tab_length = 0; + + /* Flag to remove consecutive duplicate lines from the output. + Only the last of a sequence of equal lines will be output. */ +@@ -802,6 +833,46 @@ reap_all (void) + reap (-1); + } + ++/* Function pointers. */ ++static void ++(*inittables) (void); ++static char * ++(*begfield) (const struct line*, const struct keyfield *); ++static char * ++(*limfield) (const struct line*, const struct keyfield *); ++static void ++(*skipblanks) (char **ptr, char *lim); ++static int ++(*getmonth) (char const *, size_t, char **); ++static int ++(*keycompare) (const struct line *, const struct line *); ++static int ++(*numcompare) (const char *, const char *); ++ ++/* Test for white space multibyte character. ++ Set LENGTH the byte length of investigated multibyte character. */ ++#if HAVE_MBRTOWC ++static int ++ismbblank (const char *str, size_t len, size_t *length) ++{ ++ size_t mblength; ++ wchar_t wc; ++ mbstate_t state; ++ ++ memset (&state, '\0', sizeof(mbstate_t)); ++ mblength = mbrtowc (&wc, str, len, &state); ++ ++ if (mblength == (size_t)-1 || mblength == (size_t)-2) ++ { ++ *length = 1; ++ return 0; ++ } ++ ++ *length = (mblength < 1) ? 1 : mblength; ++ return iswblank (wc) || wc == '\n'; ++} ++#endif ++ + /* Clean up any remaining temporary files. */ + + static void +@@ -1270,7 +1341,7 @@ zaptemp (char const *name) + free (node); + } + +-#if HAVE_NL_LANGINFO ++#if HAVE_LANGINFO_CODESET + + static int + struct_month_cmp (void const *m1, void const *m2) +@@ -1285,7 +1356,7 @@ struct_month_cmp (void const *m1, void const *m2) + /* Initialize the character class tables. */ + + static void +-inittables (void) ++inittables_uni (void) + { + size_t i; + +@@ -1297,7 +1368,7 @@ inittables (void) + fold_toupper[i] = toupper (i); + } + +-#if HAVE_NL_LANGINFO ++#if HAVE_LANGINFO_CODESET + /* If we're not in the "C" locale, read different names for months. */ + if (hard_LC_TIME) + { +@@ -1379,6 +1450,84 @@ specify_nmerge (int oi, char c, char const *s) + xstrtol_fatal (e, oi, c, long_options, s); + } + ++#if HAVE_MBRTOWC ++static void ++inittables_mb (void) ++{ ++ int i, j, k, l; ++ char *name, *s, *lc_time, *lc_ctype; ++ size_t s_len, mblength; ++ char mbc[MB_LEN_MAX]; ++ wchar_t wc, pwc; ++ mbstate_t state_mb, state_wc; ++ ++ lc_time = setlocale (LC_TIME, ""); ++ if (lc_time) ++ lc_time = xstrdup (lc_time); ++ ++ lc_ctype = setlocale (LC_CTYPE, ""); ++ if (lc_ctype) ++ lc_ctype = xstrdup (lc_ctype); ++ ++ if (lc_time && lc_ctype) ++ /* temporarily set LC_CTYPE to match LC_TIME, so that we can convert ++ * the names of months to upper case */ ++ setlocale (LC_CTYPE, lc_time); ++ ++ for (i = 0; i < MONTHS_PER_YEAR; i++) ++ { ++ s = (char *) nl_langinfo (ABMON_1 + i); ++ s_len = strlen (s); ++ monthtab[i].name = name = (char *) xmalloc (s_len + 1); ++ monthtab[i].val = i + 1; ++ ++ memset (&state_mb, '\0', sizeof (mbstate_t)); ++ memset (&state_wc, '\0', sizeof (mbstate_t)); ++ ++ for (j = 0; j < s_len;) ++ { ++ if (!ismbblank (s + j, s_len - j, &mblength)) ++ break; ++ j += mblength; ++ } ++ ++ for (k = 0; j < s_len;) ++ { ++ mblength = mbrtowc (&wc, (s + j), (s_len - j), &state_mb); ++ assert (mblength != (size_t)-1 && mblength != (size_t)-2); ++ if (mblength == 0) ++ break; ++ ++ pwc = towupper (wc); ++ if (pwc == wc) ++ { ++ memcpy (mbc, s + j, mblength); ++ j += mblength; ++ } ++ else ++ { ++ j += mblength; ++ mblength = wcrtomb (mbc, pwc, &state_wc); ++ assert (mblength != (size_t)0 && mblength != (size_t)-1); ++ } ++ ++ for (l = 0; l < mblength; l++) ++ name[k++] = mbc[l]; ++ } ++ name[k] = '\0'; ++ } ++ qsort ((void *) monthtab, MONTHS_PER_YEAR, ++ sizeof (struct month), struct_month_cmp); ++ ++ if (lc_time && lc_ctype) ++ /* restore the original locales */ ++ setlocale (LC_CTYPE, lc_ctype); ++ ++ free (lc_ctype); ++ free (lc_time); ++} ++#endif ++ + /* Specify the amount of main memory to use when sorting. */ + static void + specify_sort_size (int oi, char c, char const *s) +@@ -1610,7 +1759,7 @@ buffer_linelim (struct buffer const *buf) + by KEY in LINE. */ + + static char * +-begfield (struct line const *line, struct keyfield const *key) ++begfield_uni (const struct line *line, const struct keyfield *key) + { + char *ptr = line->text, *lim = ptr + line->length - 1; + size_t sword = key->sword; +@@ -1619,10 +1768,10 @@ begfield (struct line const *line, struct keyfield const *key) + /* The leading field separator itself is included in a field when -t + is absent. */ + +- if (tab != TAB_DEFAULT) ++ if (tab_length) + while (ptr < lim && sword--) + { +- while (ptr < lim && *ptr != tab) ++ while (ptr < lim && *ptr != tab[0]) + ++ptr; + if (ptr < lim) + ++ptr; +@@ -1648,11 +1797,70 @@ begfield (struct line const *line, struct keyfield const *key) + return ptr; + } + ++#if HAVE_MBRTOWC ++static char * ++begfield_mb (const struct line *line, const struct keyfield *key) ++{ ++ int i; ++ char *ptr = line->text, *lim = ptr + line->length - 1; ++ size_t sword = key->sword; ++ size_t schar = key->schar; ++ size_t mblength; ++ mbstate_t state; ++ ++ memset (&state, '\0', sizeof(mbstate_t)); ++ ++ if (tab_length) ++ while (ptr < lim && sword--) ++ { ++ while (ptr < lim && memcmp (ptr, tab, tab_length) != 0) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ptr += mblength; ++ } ++ if (ptr < lim) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ptr += mblength; ++ } ++ } ++ else ++ while (ptr < lim && sword--) ++ { ++ while (ptr < lim && ismbblank (ptr, lim - ptr, &mblength)) ++ ptr += mblength; ++ if (ptr < lim) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ptr += mblength; ++ } ++ while (ptr < lim && !ismbblank (ptr, lim - ptr, &mblength)) ++ ptr += mblength; ++ } ++ ++ if (key->skipsblanks) ++ while (ptr < lim && ismbblank (ptr, lim - ptr, &mblength)) ++ ptr += mblength; ++ ++ for (i = 0; i < schar; i++) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ++ if (ptr + mblength > lim) ++ break; ++ else ++ ptr += mblength; ++ } ++ ++ return ptr; ++} ++#endif ++ + /* Return the limit of (a pointer to the first character after) the field + in LINE specified by KEY. */ + + static char * +-limfield (struct line const *line, struct keyfield const *key) ++limfield_uni (const struct line *line, const struct keyfield *key) + { + char *ptr = line->text, *lim = ptr + line->length - 1; + size_t eword = key->eword, echar = key->echar; +@@ -1667,10 +1875,10 @@ limfield (struct line const *line, struct keyfield const *key) + 'beginning' is the first character following the delimiting TAB. + Otherwise, leave PTR pointing at the first 'blank' character after + the preceding field. */ +- if (tab != TAB_DEFAULT) ++ if (tab_length) + while (ptr < lim && eword--) + { +- while (ptr < lim && *ptr != tab) ++ while (ptr < lim && *ptr != tab[0]) + ++ptr; + if (ptr < lim && (eword || echar)) + ++ptr; +@@ -1716,10 +1924,10 @@ limfield (struct line const *line, struct keyfield const *key) + */ + + /* Make LIM point to the end of (one byte past) the current field. */ +- if (tab != TAB_DEFAULT) ++ if (tab_length) + { + char *newlim; +- newlim = memchr (ptr, tab, lim - ptr); ++ newlim = memchr (ptr, tab[0], lim - ptr); + if (newlim) + lim = newlim; + } +@@ -1750,6 +1958,130 @@ limfield (struct line const *line, struct keyfield const *key) + return ptr; + } + ++#if HAVE_MBRTOWC ++static char * ++limfield_mb (const struct line *line, const struct keyfield *key) ++{ ++ char *ptr = line->text, *lim = ptr + line->length - 1; ++ size_t eword = key->eword, echar = key->echar; ++ int i; ++ size_t mblength; ++ mbstate_t state; ++ ++ if (echar == 0) ++ eword++; /* skip all of end field. */ ++ ++ memset (&state, '\0', sizeof(mbstate_t)); ++ ++ if (tab_length) ++ while (ptr < lim && eword--) ++ { ++ while (ptr < lim && memcmp (ptr, tab, tab_length) != 0) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ptr += mblength; ++ } ++ if (ptr < lim && (eword | echar)) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ptr += mblength; ++ } ++ } ++ else ++ while (ptr < lim && eword--) ++ { ++ while (ptr < lim && ismbblank (ptr, lim - ptr, &mblength)) ++ ptr += mblength; ++ if (ptr < lim) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ptr += mblength; ++ } ++ while (ptr < lim && !ismbblank (ptr, lim - ptr, &mblength)) ++ ptr += mblength; ++ } ++ ++ ++# ifdef POSIX_UNSPECIFIED ++ /* Make LIM point to the end of (one byte past) the current field. */ ++ if (tab_length) ++ { ++ char *newlim, *p; ++ ++ newlim = NULL; ++ for (p = ptr; p < lim;) ++ { ++ if (memcmp (p, tab, tab_length) == 0) ++ { ++ newlim = p; ++ break; ++ } ++ ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ p += mblength; ++ } ++ } ++ else ++ { ++ char *newlim; ++ newlim = ptr; ++ ++ while (newlim < lim && ismbblank (newlim, lim - newlim, &mblength)) ++ newlim += mblength; ++ if (ptr < lim) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ptr += mblength; ++ } ++ while (newlim < lim && !ismbblank (newlim, lim - newlim, &mblength)) ++ newlim += mblength; ++ lim = newlim; ++ } ++# endif ++ ++ if (echar != 0) ++ { ++ /* If we're skipping leading blanks, don't start counting characters ++ * until after skipping past any leading blanks. */ ++ if (key->skipeblanks) ++ while (ptr < lim && ismbblank (ptr, lim - ptr, &mblength)) ++ ptr += mblength; ++ ++ memset (&state, '\0', sizeof(mbstate_t)); ++ ++ /* Advance PTR by ECHAR (if possible), but no further than LIM. */ ++ for (i = 0; i < echar; i++) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ++ if (ptr + mblength > lim) ++ break; ++ else ++ ptr += mblength; ++ } ++ } ++ ++ return ptr; ++} ++#endif ++ ++static void ++skipblanks_uni (char **ptr, char *lim) ++{ ++ while (*ptr < lim && blanks[to_uchar (**ptr)]) ++ ++(*ptr); ++} ++ ++#if HAVE_MBRTOWC ++static void ++skipblanks_mb (char **ptr, char *lim) ++{ ++ size_t mblength; ++ while (*ptr < lim && ismbblank (*ptr, lim - *ptr, &mblength)) ++ (*ptr) += mblength; ++} ++#endif ++ + /* Fill BUF reading from FP, moving buf->left bytes from the end + of buf->buf to the beginning first. If EOF is reached and the + file wasn't terminated by a newline, supply one. Set up BUF's line +@@ -1836,8 +2168,22 @@ fillbuf (struct buffer *buf, FILE *fp, char const *file) + else + { + if (key->skipsblanks) +- while (blanks[to_uchar (*line_start)]) +- line_start++; ++ { ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ { ++ size_t mblength; ++ while (line_start < line->keylim && ++ ismbblank (line_start, ++ line->keylim - line_start, ++ &mblength)) ++ line_start += mblength; ++ } ++ else ++#endif ++ while (blanks[to_uchar (*line_start)]) ++ line_start++; ++ } + line->keybeg = line_start; + } + } +@@ -1987,7 +2333,7 @@ human_numcompare (char const *a, char const *b) + hideously fast. */ + + static int +-numcompare (char const *a, char const *b) ++numcompare_uni (const char *a, const char *b) + { + while (blanks[to_uchar (*a)]) + a++; +@@ -1997,6 +2343,25 @@ numcompare (char const *a, char const *b) + return strnumcmp (a, b, decimal_point, thousands_sep); + } + ++#if HAVE_MBRTOWC ++static int ++numcompare_mb (const char *a, const char *b) ++{ ++ size_t mblength, len; ++ len = strlen (a); /* okay for UTF-8 */ ++ while (*a && ismbblank (a, len > MB_CUR_MAX ? MB_CUR_MAX : len, &mblength)) ++ { ++ a += mblength; ++ len -= mblength; ++ } ++ len = strlen (b); /* okay for UTF-8 */ ++ while (*b && ismbblank (b, len > MB_CUR_MAX ? MB_CUR_MAX : len, &mblength)) ++ b += mblength; ++ ++ return strnumcmp (a, b, decimal_point, thousands_sep); ++} ++#endif /* HAV_EMBRTOWC */ ++ + /* Work around a problem whereby the long double value returned by glibc's + strtold ("NaN", ...) contains uninitialized bits: clear all bytes of + A and B before calling strtold. FIXME: remove this function if +@@ -2047,7 +2412,7 @@ general_numcompare (char const *sa, char const *sb) + Return 0 if the name in S is not recognized. */ + + static int +-getmonth (char const *month, char **ea) ++getmonth_uni (char const *month, size_t len, char **ea) + { + size_t lo = 0; + size_t hi = MONTHS_PER_YEAR; +@@ -2323,15 +2688,14 @@ debug_key (struct line const *line, struct keyfield const *key) + char saved = *lim; + *lim = '\0'; + +- while (blanks[to_uchar (*beg)]) +- beg++; ++ skipblanks (&beg, lim); + + char *tighter_lim = beg; + + if (lim < beg) + tighter_lim = lim; + else if (key->month) +- getmonth (beg, &tighter_lim); ++ getmonth (beg, lim-beg, &tighter_lim); + else if (key->general_numeric) + ignore_value (strtold (beg, &tighter_lim)); + else if (key->numeric || key->human_numeric) +@@ -2465,7 +2829,7 @@ key_warnings (struct keyfield const *gkey, bool gkey_only) + /* Warn about significant leading blanks. */ + bool implicit_skip = key_numeric (key) || key->month; + bool line_offset = key->eword == 0 && key->echar != 0; /* -k1.x,1.y */ +- if (!zero_width && !gkey_only && tab == TAB_DEFAULT && !line_offset ++ if (!zero_width && !gkey_only && !tab_length && !line_offset + && ((!key->skipsblanks && !implicit_skip) + || (!key->skipsblanks && key->schar) + || (!key->skipeblanks && key->echar))) +@@ -2523,11 +2887,87 @@ key_warnings (struct keyfield const *gkey, bool gkey_only) + error (0, 0, _("option '-r' only applies to last-resort comparison")); + } + ++#if HAVE_MBRTOWC ++static int ++getmonth_mb (const char *s, size_t len, char **ea) ++{ ++ char *month; ++ register size_t i; ++ register int lo = 0, hi = MONTHS_PER_YEAR, result; ++ char *tmp; ++ size_t wclength, mblength; ++ const char *pp; ++ const wchar_t *wpp; ++ wchar_t *month_wcs; ++ mbstate_t state; ++ ++ while (len > 0 && ismbblank (s, len, &mblength)) ++ { ++ s += mblength; ++ len -= mblength; ++ } ++ ++ if (len == 0) ++ return 0; ++ ++ if (SIZE_MAX - len < 1) ++ xalloc_die (); ++ ++ month = (char *) xnmalloc (len + 1, MB_CUR_MAX); ++ ++ pp = tmp = (char *) xnmalloc (len + 1, MB_CUR_MAX); ++ memcpy (tmp, s, len); ++ tmp[len] = '\0'; ++ wpp = month_wcs = (wchar_t *) xnmalloc (len + 1, sizeof (wchar_t)); ++ memset (&state, '\0', sizeof (mbstate_t)); ++ ++ wclength = mbsrtowcs (month_wcs, &pp, len + 1, &state); ++ if (wclength == (size_t)-1 || pp != NULL) ++ error (SORT_FAILURE, 0, _("Invalid multibyte input %s."), quote(s)); ++ ++ for (i = 0; i < wclength; i++) ++ { ++ month_wcs[i] = towupper(month_wcs[i]); ++ if (iswblank (month_wcs[i])) ++ { ++ month_wcs[i] = L'\0'; ++ break; ++ } ++ } ++ ++ mblength = wcsrtombs (month, &wpp, (len + 1) * MB_CUR_MAX, &state); ++ assert (mblength != (-1) && wpp == NULL); ++ ++ do ++ { ++ int ix = (lo + hi) / 2; ++ ++ if (strncmp (month, monthtab[ix].name, strlen (monthtab[ix].name)) < 0) ++ hi = ix; ++ else ++ lo = ix; ++ } ++ while (hi - lo > 1); ++ ++ result = (!strncmp (month, monthtab[lo].name, strlen (monthtab[lo].name)) ++ ? monthtab[lo].val : 0); ++ ++ if (ea && result) ++ *ea = (char*) s + strlen (monthtab[lo].name); ++ ++ free (month); ++ free (tmp); ++ free (month_wcs); ++ ++ return result; ++} ++#endif ++ + /* Compare two lines A and B trying every key in sequence until there + are no more keys or a difference is found. */ + + static int +-keycompare (struct line const *a, struct line const *b) ++keycompare_uni (const struct line *a, const struct line *b) + { + struct keyfield *key = keylist; + +@@ -2612,7 +3052,7 @@ keycompare (struct line const *a, struct line const *b) + else if (key->human_numeric) + diff = human_numcompare (ta, tb); + else if (key->month) +- diff = getmonth (ta, NULL) - getmonth (tb, NULL); ++ diff = getmonth (ta, tlena, NULL) - getmonth (tb, tlenb, NULL); + else if (key->random) + diff = compare_random (ta, tlena, tb, tlenb); + else if (key->version) +@@ -2728,6 +3168,211 @@ keycompare (struct line const *a, struct line const *b) + return key->reverse ? -diff : diff; + } + ++#if HAVE_MBRTOWC ++static int ++keycompare_mb (const struct line *a, const struct line *b) ++{ ++ struct keyfield *key = keylist; ++ ++ /* For the first iteration only, the key positions have been ++ precomputed for us. */ ++ char *texta = a->keybeg; ++ char *textb = b->keybeg; ++ char *lima = a->keylim; ++ char *limb = b->keylim; ++ ++ size_t mblength_a, mblength_b; ++ wchar_t wc_a, wc_b; ++ mbstate_t state_a, state_b; ++ ++ int diff = 0; ++ ++ memset (&state_a, '\0', sizeof(mbstate_t)); ++ memset (&state_b, '\0', sizeof(mbstate_t)); ++ /* Ignore keys with start after end. */ ++ if (a->keybeg - a->keylim > 0) ++ return 0; ++ ++ ++ /* Ignore and/or translate chars before comparing. */ ++# define IGNORE_CHARS(NEW_LEN, LEN, TEXT, COPY, WC, MBLENGTH, STATE) \ ++ do \ ++ { \ ++ wchar_t uwc; \ ++ char mbc[MB_LEN_MAX]; \ ++ mbstate_t state_wc; \ ++ \ ++ for (NEW_LEN = i = 0; i < LEN;) \ ++ { \ ++ mbstate_t state_bak; \ ++ \ ++ state_bak = STATE; \ ++ MBLENGTH = mbrtowc (&WC, TEXT + i, LEN - i, &STATE); \ ++ \ ++ if (MBLENGTH == (size_t)-2 || MBLENGTH == (size_t)-1 \ ++ || MBLENGTH == 0) \ ++ { \ ++ if (MBLENGTH == (size_t)-2 || MBLENGTH == (size_t)-1) \ ++ STATE = state_bak; \ ++ if (!ignore) \ ++ COPY[NEW_LEN++] = TEXT[i]; \ ++ i++; \ ++ continue; \ ++ } \ ++ \ ++ if (ignore) \ ++ { \ ++ if ((ignore == nonprinting && !iswprint (WC)) \ ++ || (ignore == nondictionary \ ++ && !iswalnum (WC) && !iswblank (WC))) \ ++ { \ ++ i += MBLENGTH; \ ++ continue; \ ++ } \ ++ } \ ++ \ ++ if (translate) \ ++ { \ ++ \ ++ uwc = towupper(WC); \ ++ if (WC == uwc) \ ++ { \ ++ memcpy (mbc, TEXT + i, MBLENGTH); \ ++ i += MBLENGTH; \ ++ } \ ++ else \ ++ { \ ++ i += MBLENGTH; \ ++ WC = uwc; \ ++ memset (&state_wc, '\0', sizeof (mbstate_t)); \ ++ \ ++ MBLENGTH = wcrtomb (mbc, WC, &state_wc); \ ++ assert (MBLENGTH != (size_t)-1 && MBLENGTH != 0); \ ++ } \ ++ \ ++ for (j = 0; j < MBLENGTH; j++) \ ++ COPY[NEW_LEN++] = mbc[j]; \ ++ } \ ++ else \ ++ for (j = 0; j < MBLENGTH; j++) \ ++ COPY[NEW_LEN++] = TEXT[i++]; \ ++ } \ ++ COPY[NEW_LEN] = '\0'; \ ++ } \ ++ while (0) ++ ++ /* Actually compare the fields. */ ++ ++ for (;;) ++ { ++ /* Find the lengths. */ ++ size_t lena = lima <= texta ? 0 : lima - texta; ++ size_t lenb = limb <= textb ? 0 : limb - textb; ++ ++ char enda IF_LINT (= 0); ++ char endb IF_LINT (= 0); ++ ++ char const *translate = key->translate; ++ bool const *ignore = key->ignore; ++ ++ if (ignore || translate) ++ { ++ if (SIZE_MAX - lenb - 2 < lena) ++ xalloc_die (); ++ char *copy_a = (char *) xnmalloc (lena + lenb + 2, MB_CUR_MAX); ++ char *copy_b = copy_a + lena * MB_CUR_MAX + 1; ++ size_t new_len_a, new_len_b; ++ size_t i, j; ++ ++ IGNORE_CHARS (new_len_a, lena, texta, copy_a, ++ wc_a, mblength_a, state_a); ++ IGNORE_CHARS (new_len_b, lenb, textb, copy_b, ++ wc_b, mblength_b, state_b); ++ texta = copy_a; textb = copy_b; ++ lena = new_len_a; lenb = new_len_b; ++ } ++ else ++ { ++ /* Use the keys in-place, temporarily null-terminated. */ ++ enda = texta[lena]; texta[lena] = '\0'; ++ endb = textb[lenb]; textb[lenb] = '\0'; ++ } ++ ++ if (key->random) ++ diff = compare_random (texta, lena, textb, lenb); ++ else if (key->numeric | key->general_numeric | key->human_numeric) ++ { ++ char savea = *lima, saveb = *limb; ++ ++ *lima = *limb = '\0'; ++ diff = (key->numeric ? numcompare (texta, textb) ++ : key->general_numeric ? general_numcompare (texta, textb) ++ : human_numcompare (texta, textb)); ++ *lima = savea, *limb = saveb; ++ } ++ else if (key->version) ++ diff = filevercmp (texta, textb); ++ else if (key->month) ++ diff = getmonth (texta, lena, NULL) - getmonth (textb, lenb, NULL); ++ else if (lena == 0) ++ diff = - NONZERO (lenb); ++ else if (lenb == 0) ++ diff = 1; ++ else if (hard_LC_COLLATE && !folding) ++ { ++ diff = xmemcoll0 (texta, lena + 1, textb, lenb + 1); ++ } ++ else ++ { ++ diff = memcmp (texta, textb, MIN (lena, lenb)); ++ if (diff == 0) ++ diff = lena < lenb ? -1 : lena != lenb; ++ } ++ ++ if (ignore || translate) ++ free (texta); ++ else ++ { ++ texta[lena] = enda; ++ textb[lenb] = endb; ++ } ++ ++ if (diff) ++ goto not_equal; ++ ++ key = key->next; ++ if (! key) ++ break; ++ ++ /* Find the beginning and limit of the next field. */ ++ if (key->eword != -1) ++ lima = limfield (a, key), limb = limfield (b, key); ++ else ++ lima = a->text + a->length - 1, limb = b->text + b->length - 1; ++ ++ if (key->sword != -1) ++ texta = begfield (a, key), textb = begfield (b, key); ++ else ++ { ++ texta = a->text, textb = b->text; ++ if (key->skipsblanks) ++ { ++ while (texta < lima && ismbblank (texta, lima - texta, &mblength_a)) ++ texta += mblength_a; ++ while (textb < limb && ismbblank (textb, limb - textb, &mblength_b)) ++ textb += mblength_b; ++ } ++ } ++ } ++ ++not_equal: ++ if (key && key->reverse) ++ return -diff; ++ else ++ return diff; ++} ++#endif ++ + /* Compare two lines A and B, returning negative, zero, or positive + depending on whether A compares less than, equal to, or greater than B. */ + +@@ -2755,7 +3400,7 @@ compare (struct line const *a, struct line const *b) + diff = - NONZERO (blen); + else if (blen == 0) + diff = 1; +- else if (hard_LC_COLLATE) ++ else if (hard_LC_COLLATE && !folding) + { + /* xmemcoll0 is a performance enhancement as + it will not unconditionally write '\0' after the +@@ -4145,6 +4790,7 @@ set_ordering (char const *s, struct keyfield *key, enum blanktype blanktype) + break; + case 'f': + key->translate = fold_toupper; ++ folding = true; + break; + case 'g': + key->general_numeric = true; +@@ -4224,7 +4870,7 @@ main (int argc, char **argv) + initialize_exit_failure (SORT_FAILURE); + + hard_LC_COLLATE = hard_locale (LC_COLLATE); +-#if HAVE_NL_LANGINFO ++#if HAVE_LANGINFO_CODESET + hard_LC_TIME = hard_locale (LC_TIME); + #endif + +@@ -4245,6 +4891,29 @@ main (int argc, char **argv) + thousands_sep = -1; + } + ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ { ++ inittables = inittables_mb; ++ begfield = begfield_mb; ++ limfield = limfield_mb; ++ skipblanks = skipblanks_mb; ++ getmonth = getmonth_mb; ++ keycompare = keycompare_mb; ++ numcompare = numcompare_mb; ++ } ++ else ++#endif ++ { ++ inittables = inittables_uni; ++ begfield = begfield_uni; ++ limfield = limfield_uni; ++ skipblanks = skipblanks_uni; ++ getmonth = getmonth_uni; ++ keycompare = keycompare_uni; ++ numcompare = numcompare_uni; ++ } ++ + have_read_stdin = false; + inittables (); + +@@ -4519,13 +5188,34 @@ main (int argc, char **argv) + + case 't': + { +- char newtab = optarg[0]; +- if (! newtab) ++ char newtab[MB_LEN_MAX + 1]; ++ size_t newtab_length = 1; ++ strncpy (newtab, optarg, MB_LEN_MAX); ++ if (! newtab[0]) + die (SORT_FAILURE, 0, _("empty tab")); +- if (optarg[1]) ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ { ++ wchar_t wc; ++ mbstate_t state; ++ ++ memset (&state, '\0', sizeof (mbstate_t)); ++ newtab_length = mbrtowc (&wc, newtab, strnlen (newtab, ++ MB_LEN_MAX), ++ &state); ++ switch (newtab_length) ++ { ++ case (size_t) -1: ++ case (size_t) -2: ++ case 0: ++ newtab_length = 1; ++ } ++ } ++#endif ++ if (newtab_length == 1 && optarg[1]) + { + if (STREQ (optarg, "\\0")) +- newtab = '\0'; ++ newtab[0] = '\0'; + else + { + /* Provoke with 'sort -txx'. Complain about +@@ -4536,9 +5226,11 @@ main (int argc, char **argv) + quote (optarg)); + } + } +- if (tab != TAB_DEFAULT && tab != newtab) ++ if (tab_length && (tab_length != newtab_length ++ || memcmp (tab, newtab, tab_length) != 0)) + die (SORT_FAILURE, 0, _("incompatible tabs")); +- tab = newtab; ++ memcpy (tab, newtab, newtab_length); ++ tab_length = newtab_length; + } + break; + +@@ -4767,12 +5459,10 @@ main (int argc, char **argv) + sort (files, nfiles, outfile, nthreads); + } + +-#ifdef lint + if (files_from) + readtokens0_free (&tok); + else + free (files); +-#endif + + if (have_read_stdin && fclose (stdin) == EOF) + sort_die (_("close failed"), "-"); +diff --git a/src/uniq.c b/src/uniq.c +index 87a0c93..9f755d9 100644 +--- a/src/uniq.c ++++ b/src/uniq.c +@@ -21,6 +21,17 @@ + #include + #include + ++/* Get mbstate_t, mbrtowc(). */ ++#if HAVE_WCHAR_H ++# include ++#endif ++ ++/* Get isw* functions. */ ++#if HAVE_WCTYPE_H ++# include ++#endif ++#include ++ + #include "system.h" + #include "argmatch.h" + #include "linebuffer.h" +@@ -33,6 +44,18 @@ + #include "memcasecmp.h" + #include "quote.h" + ++/* MB_LEN_MAX is incorrectly defined to be 1 in at least one GCC ++ installation; work around this configuration error. */ ++#if !defined MB_LEN_MAX || MB_LEN_MAX < 2 ++# define MB_LEN_MAX 16 ++#endif ++ ++/* Some systems, like BeOS, have multibyte encodings but lack mbstate_t. */ ++#if HAVE_MBRTOWC && defined mbstate_t ++# define mbrtowc(pwc, s, n, ps) (mbrtowc) (pwc, s, n, 0) ++#endif ++ ++ + /* The official name of this program (e.g., no 'g' prefix). */ + #define PROGRAM_NAME "uniq" + +@@ -139,6 +162,10 @@ enum + GROUP_OPTION = CHAR_MAX + 1 + }; + ++/* Function pointers. */ ++static char * ++(*find_field) (struct linebuffer *line); ++ + static struct option const longopts[] = + { + {"count", no_argument, NULL, 'c'}, +@@ -253,7 +280,7 @@ size_opt (char const *opt, char const *msgid) + return a pointer to the beginning of the line's field to be compared. */ + + static char * _GL_ATTRIBUTE_PURE +-find_field (struct linebuffer const *line) ++find_field_uni (struct linebuffer *line) + { + size_t count; + char const *lp = line->buffer; +@@ -273,6 +300,83 @@ find_field (struct linebuffer const *line) + return line->buffer + i; + } + ++#if HAVE_MBRTOWC ++ ++# define MBCHAR_TO_WCHAR(WC, MBLENGTH, LP, POS, SIZE, STATEP, CONVFAIL) \ ++ do \ ++ { \ ++ mbstate_t state_bak; \ ++ \ ++ CONVFAIL = 0; \ ++ state_bak = *STATEP; \ ++ \ ++ MBLENGTH = mbrtowc (&WC, LP + POS, SIZE - POS, STATEP); \ ++ \ ++ switch (MBLENGTH) \ ++ { \ ++ case (size_t)-2: \ ++ case (size_t)-1: \ ++ *STATEP = state_bak; \ ++ CONVFAIL++; \ ++ /* Fall through */ \ ++ case 0: \ ++ MBLENGTH = 1; \ ++ } \ ++ } \ ++ while (0) ++ ++static char * ++find_field_multi (struct linebuffer *line) ++{ ++ size_t count; ++ char *lp = line->buffer; ++ size_t size = line->length - 1; ++ size_t pos; ++ size_t mblength; ++ wchar_t wc; ++ mbstate_t *statep; ++ int convfail = 0; ++ ++ pos = 0; ++ statep = &(line->state); ++ ++ /* skip fields. */ ++ for (count = 0; count < skip_fields && pos < size; count++) ++ { ++ while (pos < size) ++ { ++ MBCHAR_TO_WCHAR (wc, mblength, lp, pos, size, statep, convfail); ++ ++ if (convfail || !(iswblank (wc) || wc == '\n')) ++ { ++ pos += mblength; ++ break; ++ } ++ pos += mblength; ++ } ++ ++ while (pos < size) ++ { ++ MBCHAR_TO_WCHAR (wc, mblength, lp, pos, size, statep, convfail); ++ ++ if (!convfail && (iswblank (wc) || wc == '\n')) ++ break; ++ ++ pos += mblength; ++ } ++ } ++ ++ /* skip fields. */ ++ for (count = 0; count < skip_chars && pos < size; count++) ++ { ++ MBCHAR_TO_WCHAR (wc, mblength, lp, pos, size, statep, convfail); ++ pos += mblength; ++ } ++ ++ return lp + pos; ++} ++#endif ++ + /* Return false if two strings OLD and NEW match, true if not. + OLD and NEW point not to the beginnings of the lines + but rather to the beginnings of the fields to compare. +@@ -493,6 +597,19 @@ main (int argc, char **argv) + + atexit (close_stdout); + ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ { ++ find_field = find_field_multi; ++ } ++ else ++#endif ++ { ++ find_field = find_field_uni; ++ } ++ ++ ++ + skip_chars = 0; + skip_fields = 0; + check_chars = SIZE_MAX; +diff --git a/tests/i18n/sort.sh b/tests/i18n/sort.sh +new file mode 100755 +index 0000000..26c95de +--- /dev/null ++++ b/tests/i18n/sort.sh +@@ -0,0 +1,29 @@ ++#!/bin/sh ++# Verify sort's multi-byte support. ++ ++. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src ++print_ver_ sort ++ ++export LC_ALL=en_US.UTF-8 ++locale -k LC_CTYPE | grep -q "charmap.*UTF-8" \ ++ || skip_ "No UTF-8 locale available" ++ ++# Enable heap consistency checkng on older systems ++export MALLOC_CHECK_=2 ++ ++ ++# check buffer overflow issue due to ++# expanding multi-byte representation due to case conversion ++# https://bugzilla.suse.com/show_bug.cgi?id=928749 ++cat < exp ++. ++ɑ ++EOF ++cat < out || fail=1 ++. ++ɑ ++EOF ++compare exp out || { fail=1; cat out; } ++ ++ ++Exit $fail +diff --git a/tests/local.mk b/tests/local.mk +index 568944e..192f776 100644 +--- a/tests/local.mk ++++ b/tests/local.mk +@@ -369,6 +369,8 @@ all_tests = \ + tests/misc/sort-discrim.sh \ + tests/misc/sort-files0-from.pl \ + tests/misc/sort-float.sh \ ++ tests/misc/sort-mb-tests.sh \ ++ tests/i18n/sort.sh \ + tests/misc/sort-h-thousands-sep.sh \ + tests/misc/sort-merge.pl \ + tests/misc/sort-merge-fdlimit.sh \ +diff --git a/tests/misc/expand.pl b/tests/misc/expand.pl +index 8a9cad1..9293e39 100755 +--- a/tests/misc/expand.pl ++++ b/tests/misc/expand.pl +@@ -27,6 +27,15 @@ my $prog = 'expand'; + # Turn off localization of executable's output. + @ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + ++#comment out next line to disable multibyte tests ++my $mb_locale = $ENV{LOCALE_FR_UTF8}; ++! defined $mb_locale || $mb_locale eq 'none' ++ and $mb_locale = 'C'; ++ ++my $prog = 'expand'; ++my $try = "Try \`$prog --help' for more information.\n"; ++my $inval = "$prog: invalid byte, character or field list\n$try"; ++ + my @Tests = + ( + ['t1', '--tabs=3', {IN=>"a\tb"}, {OUT=>"a b"}], +@@ -168,6 +177,8 @@ my @Tests = + + + # Test errors ++ # FIXME: The following tests contain ‘quoting’ specific to LC_MESSAGES ++ # So we force LC_MESSAGES=C to make them pass. + ['e1', '--tabs="a"', {IN=>''}, {OUT=>''}, {EXIT=>1}, + {ERR => "$prog: tab size contains invalid character(s): 'a'\n"}], + ['e2', "-t $UINTMAX_OFLOW", {IN=>''}, {OUT=>''}, {EXIT=>1}, +@@ -184,6 +195,37 @@ my @Tests = + {ERR => "$prog: '/' specifier not at start of number: '/'\n"}], + ); + ++if ($mb_locale ne 'C') ++ { ++ # Duplicate each test vector, appending "-mb" to the test name and ++ # inserting {ENV => "LC_ALL=$mb_locale"} in the copy, so that we ++ # provide coverage for the distro-added multi-byte code paths. ++ my @new; ++ foreach my $t (@Tests) ++ { ++ my @new_t = @$t; ++ my $test_name = shift @new_t; ++ ++ # Depending on whether expand is multi-byte-patched, ++ # it emits different diagnostics: ++ # non-MB: invalid byte or field list ++ # MB: invalid byte, character or field list ++ # Adjust the expected error output accordingly. ++ if (grep {ref $_ eq 'HASH' && exists $_->{ERR} && $_->{ERR} eq $inval} ++ (@new_t)) ++ { ++ my $sub = {ERR_SUBST => 's/, character//'}; ++ push @new_t, $sub; ++ push @$t, $sub; ++ } ++ push @new, ["$test_name-mb", @new_t, {ENV => "LANG=$mb_locale LC_MESSAGES=C"}]; ++ } ++ push @Tests, @new; ++ } ++ ++ ++@Tests = triple_test \@Tests; ++ + my $save_temps = $ENV{DEBUG}; + my $verbose = $ENV{VERBOSE}; + +diff --git a/tests/misc/fold.pl b/tests/misc/fold.pl +index 7b192b4..76f073f 100755 +--- a/tests/misc/fold.pl ++++ b/tests/misc/fold.pl +@@ -20,9 +20,18 @@ use strict; + + (my $program_name = $0) =~ s|.*/||; + ++my $prog = 'fold'; ++my $try = "Try \`$prog --help' for more information.\n"; ++my $inval = "$prog: invalid byte, character or field list\n$try"; ++ + # Turn off localization of executable's output. + @ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + ++# uncommented to enable multibyte paths ++my $mb_locale = $ENV{LOCALE_FR_UTF8}; ++! defined $mb_locale || $mb_locale eq 'none' ++ and $mb_locale = 'C'; ++ + my @Tests = + ( + ['s1', '-w2 -s', {IN=>"a\t"}, {OUT=>"a\n\t"}], +@@ -31,9 +40,48 @@ my @Tests = + ['s4', '-w4 -s', {IN=>"abc ef\n"}, {OUT=>"abc \nef\n"}], + ); + ++# Add _POSIX2_VERSION=199209 to the environment of each test ++# that uses an old-style option like +1. ++if ($mb_locale ne 'C') ++ { ++ # Duplicate each test vector, appending "-mb" to the test name and ++ # inserting {ENV => "LC_ALL=$mb_locale"} in the copy, so that we ++ # provide coverage for the distro-added multi-byte code paths. ++ my @new; ++ foreach my $t (@Tests) ++ { ++ my @new_t = @$t; ++ my $test_name = shift @new_t; ++ ++ # Depending on whether fold is multi-byte-patched, ++ # it emits different diagnostics: ++ # non-MB: invalid byte or field list ++ # MB: invalid byte, character or field list ++ # Adjust the expected error output accordingly. ++ if (grep {ref $_ eq 'HASH' && exists $_->{ERR} && $_->{ERR} eq $inval} ++ (@new_t)) ++ { ++ my $sub = {ERR_SUBST => 's/, character//'}; ++ push @new_t, $sub; ++ push @$t, $sub; ++ } ++ push @new, ["$test_name-mb", @new_t, {ENV => "LC_ALL=$mb_locale"}]; ++ } ++ push @Tests, @new; ++ } ++ ++@Tests = triple_test \@Tests; ++ ++# Remember that triple_test creates from each test with exactly one "IN" ++# file two more tests (.p and .r suffix on name) corresponding to reading ++# input from a file and from a pipe. The pipe-reading test would fail ++# due to a race condition about 1 in 20 times. ++# Remove the IN_PIPE version of the "output-is-input" test above. ++# The others aren't susceptible because they have three inputs each. ++@Tests = grep {$_->[0] ne 'output-is-input.p'} @Tests; ++ + my $save_temps = $ENV{DEBUG}; + my $verbose = $ENV{VERBOSE}; + +-my $prog = 'fold'; + my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose); + exit $fail; +diff --git a/tests/misc/join.pl b/tests/misc/join.pl +index 4d399d8..07f2823 100755 +--- a/tests/misc/join.pl ++++ b/tests/misc/join.pl +@@ -25,6 +25,15 @@ my $limits = getlimits (); + + my $prog = 'join'; + ++my $try = "Try \`$prog --help' for more information.\n"; ++my $inval = "$prog: invalid byte, character or field list\n$try"; ++ ++my $mb_locale; ++#Comment out next line to disable multibyte tests ++$mb_locale = $ENV{LOCALE_FR_UTF8}; ++! defined $mb_locale || $mb_locale eq 'none' ++ and $mb_locale = 'C'; ++ + my $delim = chr 0247; + sub t_subst ($) + { +@@ -333,8 +342,49 @@ foreach my $t (@tv) + push @Tests, $new_ent; + } + ++# Add _POSIX2_VERSION=199209 to the environment of each test ++# that uses an old-style option like +1. ++if ($mb_locale ne 'C') ++ { ++ # Duplicate each test vector, appending "-mb" to the test name and ++ # inserting {ENV => "LC_ALL=$mb_locale"} in the copy, so that we ++ # provide coverage for the distro-added multi-byte code paths. ++ my @new; ++ foreach my $t (@Tests) ++ { ++ my @new_t = @$t; ++ my $test_name = shift @new_t; ++ ++ # Depending on whether join is multi-byte-patched, ++ # it emits different diagnostics: ++ # non-MB: invalid byte or field list ++ # MB: invalid byte, character or field list ++ # Adjust the expected error output accordingly. ++ if (grep {ref $_ eq 'HASH' && exists $_->{ERR} && $_->{ERR} eq $inval} ++ (@new_t)) ++ { ++ my $sub = {ERR_SUBST => 's/, character//'}; ++ push @new_t, $sub; ++ push @$t, $sub; ++ } ++ #Adjust the output some error messages including test_name for mb ++ if (grep {ref $_ eq 'HASH' && exists $_->{ERR}} ++ (@new_t)) ++ { ++ my $sub2 = {ERR_SUBST => "s/$test_name-mb/$test_name/"}; ++ push @new_t, $sub2; ++ push @$t, $sub2; ++ } ++ push @new, ["$test_name-mb", @new_t, {ENV => "LC_ALL=$mb_locale"}]; ++ } ++ push @Tests, @new; ++ } ++ + @Tests = triple_test \@Tests; + ++#skip invalid-j-mb test, it is failing because of the format ++@Tests = grep {$_->[0] ne 'invalid-j-mb'} @Tests; ++ + my $save_temps = $ENV{DEBUG}; + my $verbose = $ENV{VERBOSE}; + +diff --git a/tests/misc/sort-mb-tests.sh b/tests/misc/sort-mb-tests.sh +new file mode 100755 +index 0000000..11836ba +--- /dev/null ++++ b/tests/misc/sort-mb-tests.sh +@@ -0,0 +1,45 @@ ++#!/bin/sh ++# Verify sort's multi-byte support. ++ ++. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src ++print_ver_ sort ++ ++export LC_ALL=en_US.UTF-8 ++locale -k LC_CTYPE | grep -q "charmap.*UTF-8" \ ++ || skip_ "No UTF-8 locale available" ++ ++ ++cat < exp ++Banana@5 ++Apple@10 ++Citrus@20 ++Cherry@30 ++EOF ++ ++cat < out || fail=1 ++Apple@10 ++Banana@5 ++Citrus@20 ++Cherry@30 ++EOF ++ ++compare exp out || { fail=1; cat out; } ++ ++ ++cat < exp ++Citrus@AA20@@5 ++Cherry@AA30@@10 ++Apple@AA10@@20 ++Banana@AA5@@30 ++EOF ++ ++cat < out || fail=1 ++Apple@AA10@@20 ++Banana@AA5@@30 ++Citrus@AA20@@5 ++Cherry@AA30@@10 ++EOF ++ ++compare exp out || { fail=1; cat out; } ++ ++Exit $fail +diff --git a/tests/misc/sort-merge.pl b/tests/misc/sort-merge.pl +index 23f6ed2..402a987 100755 +--- a/tests/misc/sort-merge.pl ++++ b/tests/misc/sort-merge.pl +@@ -26,6 +26,15 @@ my $prog = 'sort'; + # Turn off localization of executable's output. + @ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + ++my $mb_locale; ++# uncommented according to upstream commit enabling multibyte paths ++$mb_locale = $ENV{LOCALE_FR_UTF8}; ++! defined $mb_locale || $mb_locale eq 'none' ++ and $mb_locale = 'C'; ++ ++my $try = "Try \`$prog --help' for more information.\n"; ++my $inval = "$prog: invalid byte, character or field list\n$try"; ++ + # three empty files and one that says 'foo' + my @inputs = (+(map{{IN=> {"empty$_"=> ''}}}1..3), {IN=> {foo=> "foo\n"}}); + +@@ -77,6 +86,39 @@ my @Tests = + {OUT=>$big_input}], + ); + ++# Add _POSIX2_VERSION=199209 to the environment of each test ++# that uses an old-style option like +1. ++if ($mb_locale ne 'C') ++ { ++ # Duplicate each test vector, appending "-mb" to the test name and ++ # inserting {ENV => "LC_ALL=$mb_locale"} in the copy, so that we ++ # provide coverage for the distro-added multi-byte code paths. ++ my @new; ++ foreach my $t (@Tests) ++ { ++ my @new_t = @$t; ++ my $test_name = shift @new_t; ++ ++ # Depending on whether sort is multi-byte-patched, ++ # it emits different diagnostics: ++ # non-MB: invalid byte or field list ++ # MB: invalid byte, character or field list ++ # Adjust the expected error output accordingly. ++ if (grep {ref $_ eq 'HASH' && exists $_->{ERR} && $_->{ERR} eq $inval} ++ (@new_t)) ++ { ++ my $sub = {ERR_SUBST => 's/, character//'}; ++ push @new_t, $sub; ++ push @$t, $sub; ++ } ++ next if ($test_name =~ "nmerge-."); ++ push @new, ["$test_name-mb", @new_t, {ENV => "LC_ALL=$mb_locale"}]; ++ } ++ push @Tests, @new; ++ } ++ ++@Tests = triple_test \@Tests; ++ + my $save_temps = $ENV{DEBUG}; + my $verbose = $ENV{VERBOSE}; + +diff --git a/tests/misc/sort.pl b/tests/misc/sort.pl +index c3e7f8e..6ecd3ff 100755 +--- a/tests/misc/sort.pl ++++ b/tests/misc/sort.pl +@@ -24,10 +24,15 @@ my $prog = 'sort'; + # Turn off localization of executable's output. + @ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + +-my $mb_locale = $ENV{LOCALE_FR_UTF8}; ++my $mb_locale; ++#Comment out next line to disable multibyte tests ++$mb_locale = $ENV{LOCALE_FR_UTF8}; + ! defined $mb_locale || $mb_locale eq 'none' + and $mb_locale = 'C'; + ++my $try = "Try \`$prog --help' for more information.\n"; ++my $inval = "$prog: invalid byte, character or field list\n$try"; ++ + # Since each test is run with a file name and with redirected stdin, + # the name in the diagnostic is either the file name or "-". + # Normalize each diagnostic to use '-'. +@@ -423,6 +428,38 @@ foreach my $t (@Tests) + } + } + ++if ($mb_locale ne 'C') ++ { ++ # Duplicate each test vector, appending "-mb" to the test name and ++ # inserting {ENV => "LC_ALL=$mb_locale"} in the copy, so that we ++ # provide coverage for the distro-added multi-byte code paths. ++ my @new; ++ foreach my $t (@Tests) ++ { ++ my @new_t = @$t; ++ my $test_name = shift @new_t; ++ ++ # Depending on whether sort is multi-byte-patched, ++ # it emits different diagnostics: ++ # non-MB: invalid byte or field list ++ # MB: invalid byte, character or field list ++ # Adjust the expected error output accordingly. ++ if (grep {ref $_ eq 'HASH' && exists $_->{ERR} && $_->{ERR} eq $inval} ++ (@new_t)) ++ { ++ my $sub = {ERR_SUBST => 's/, character//'}; ++ push @new_t, $sub; ++ push @$t, $sub; ++ } ++ #disable several failing tests until investigation, disable all tests with envvars set ++ next if (grep {ref $_ eq 'HASH' && exists $_->{ENV}} (@new_t)); ++ next if ($test_name =~ "18g" or $test_name =~ "sort-numeric" or $test_name =~ "08[ab]" or $test_name =~ "03[def]" or $test_name =~ "h4" or $test_name =~ "n1" or $test_name =~ "2[01]a"); ++ next if ($test_name =~ "11[ab]"); # avoid FP: expected result differs to MB result due to collation rules. ++ push @new, ["$test_name-mb", @new_t, {ENV => "LC_ALL=$mb_locale"}]; ++ } ++ push @Tests, @new; ++ } ++ + @Tests = triple_test \@Tests; + + # Remember that triple_test creates from each test with exactly one "IN" +@@ -432,6 +469,7 @@ foreach my $t (@Tests) + # Remove the IN_PIPE version of the "output-is-input" test above. + # The others aren't susceptible because they have three inputs each. + @Tests = grep {$_->[0] ne 'output-is-input.p'} @Tests; ++@Tests = grep {$_->[0] ne 'output-is-input-mb.p'} @Tests; + + my $save_temps = $ENV{DEBUG}; + my $verbose = $ENV{VERBOSE}; +diff --git a/tests/misc/unexpand.pl b/tests/misc/unexpand.pl +index 6ba6d40..de86723 100755 +--- a/tests/misc/unexpand.pl ++++ b/tests/misc/unexpand.pl +@@ -27,6 +27,14 @@ my $limits = getlimits (); + + my $prog = 'unexpand'; + ++# comment out next line to disable multibyte tests ++my $mb_locale = $ENV{LOCALE_FR_UTF8}; ++! defined $mb_locale || $mb_locale eq 'none' ++ and $mb_locale = 'C'; ++ ++my $try = "Try \`$prog --help' for more information.\n"; ++my $inval = "$prog: invalid byte, character or field list\n$try"; ++ + my @Tests = + ( + ['a1', {IN=> ' 'x 1 ."y\n"}, {OUT=> ' 'x 1 ."y\n"}], +@@ -128,6 +136,37 @@ my @Tests = + ['ts2', '-t5,8', {IN=>"x\t \t y\n"}, {OUT=>"x\t\t y\n"}], + ); + ++if ($mb_locale ne 'C') ++ { ++ # Duplicate each test vector, appending "-mb" to the test name and ++ # inserting {ENV => "LC_ALL=$mb_locale"} in the copy, so that we ++ # provide coverage for the distro-added multi-byte code paths. ++ my @new; ++ foreach my $t (@Tests) ++ { ++ my @new_t = @$t; ++ my $test_name = shift @new_t; ++ ++ # Depending on whether unexpand is multi-byte-patched, ++ # it emits different diagnostics: ++ # non-MB: invalid byte or field list ++ # MB: invalid byte, character or field list ++ # Adjust the expected error output accordingly. ++ if (grep {ref $_ eq 'HASH' && exists $_->{ERR} && $_->{ERR} eq $inval} ++ (@new_t)) ++ { ++ my $sub = {ERR_SUBST => 's/, character//'}; ++ push @new_t, $sub; ++ push @$t, $sub; ++ } ++ next if ($test_name =~ 'b-1'); ++ push @new, ["$test_name-mb", @new_t, {ENV => "LC_ALL=$mb_locale"}]; ++ } ++ push @Tests, @new; ++ } ++ ++@Tests = triple_test \@Tests; ++ + my $save_temps = $ENV{DEBUG}; + my $verbose = $ENV{VERBOSE}; + +diff --git a/tests/misc/uniq.pl b/tests/misc/uniq.pl +index f028036..8eaf59a 100755 +--- a/tests/misc/uniq.pl ++++ b/tests/misc/uniq.pl +@@ -23,9 +23,17 @@ my $limits = getlimits (); + my $prog = 'uniq'; + my $try = "Try '$prog --help' for more information.\n"; + ++my $inval = "$prog: invalid byte, character or field list\n$try"; ++ + # Turn off localization of executable's output. + @ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + ++my $mb_locale; ++#Comment out next line to disable multibyte tests ++$mb_locale = $ENV{LOCALE_FR_UTF8}; ++! defined $mb_locale || $mb_locale eq 'none' ++ and $mb_locale = 'C'; ++ + # When possible, create a "-z"-testing variant of each test. + sub add_z_variants($) + { +@@ -262,6 +270,53 @@ foreach my $t (@Tests) + and push @$t, {ENV=>'_POSIX2_VERSION=199209'}; + } + ++if ($mb_locale ne 'C') ++ { ++ # Duplicate each test vector, appending "-mb" to the test name and ++ # inserting {ENV => "LC_ALL=$mb_locale"} in the copy, so that we ++ # provide coverage for the distro-added multi-byte code paths. ++ my @new; ++ foreach my $t (@Tests) ++ { ++ my @new_t = @$t; ++ my $test_name = shift @new_t; ++ ++ # Depending on whether uniq is multi-byte-patched, ++ # it emits different diagnostics: ++ # non-MB: invalid byte or field list ++ # MB: invalid byte, character or field list ++ # Adjust the expected error output accordingly. ++ if (grep {ref $_ eq 'HASH' && exists $_->{ERR} && $_->{ERR} eq $inval} ++ (@new_t)) ++ { ++ my $sub = {ERR_SUBST => 's/, character//'}; ++ push @new_t, $sub; ++ push @$t, $sub; ++ } ++ # In test #145, replace the each ‘...’ by '...'. ++ if ($test_name =~ "145") ++ { ++ my $sub = { ERR_SUBST => "s/‘([^’]+)’/'\$1'/g"}; ++ push @new_t, $sub; ++ push @$t, $sub; ++ } ++ next if ( $test_name =~ "schar" ++ or $test_name =~ "^obs-plus" ++ or $test_name =~ "119"); ++ push @new, ["$test_name-mb", @new_t, {ENV => "LC_ALL=$mb_locale"}]; ++ } ++ push @Tests, @new; ++ } ++ ++# Remember that triple_test creates from each test with exactly one "IN" ++# file two more tests (.p and .r suffix on name) corresponding to reading ++# input from a file and from a pipe. The pipe-reading test would fail ++# due to a race condition about 1 in 20 times. ++# Remove the IN_PIPE version of the "output-is-input" test above. ++# The others aren't susceptible because they have three inputs each. ++ ++@Tests = grep {$_->[0] ne 'output-is-input.p'} @Tests; ++ + @Tests = add_z_variants \@Tests; + @Tests = triple_test \@Tests; + +diff --git a/tests/pr/pr-tests.pl b/tests/pr/pr-tests.pl +index ec3980a..136657d 100755 +--- a/tests/pr/pr-tests.pl ++++ b/tests/pr/pr-tests.pl +@@ -24,6 +24,15 @@ use strict; + my $prog = 'pr'; + my $normalize_strerror = "s/': .*/'/"; + ++my $mb_locale; ++#Uncomment the following line to enable multibyte tests ++$mb_locale = $ENV{LOCALE_FR_UTF8}; ++! defined $mb_locale || $mb_locale eq 'none' ++ and $mb_locale = 'C'; ++ ++my $try = "Try \`$prog --help' for more information.\n"; ++my $inval = "$prog: invalid byte, character or field list\n$try"; ++ + my @tv = ( + + # -b option is no longer an official option. But it's still working to +@@ -474,8 +483,48 @@ push @Tests, + {IN=>{2=>"a\n"}}, + {OUT=>"a\t\t\t\t \t\t\ta\n"} ]; + ++# Add _POSIX2_VERSION=199209 to the environment of each test ++# that uses an old-style option like +1. ++if ($mb_locale ne 'C') ++ { ++ # Duplicate each test vector, appending "-mb" to the test name and ++ # inserting {ENV => "LC_ALL=$mb_locale"} in the copy, so that we ++ # provide coverage for the distro-added multi-byte code paths. ++ my @new; ++ foreach my $t (@Tests) ++ { ++ my @new_t = @$t; ++ my $test_name = shift @new_t; ++ ++ # Depending on whether pr is multi-byte-patched, ++ # it emits different diagnostics: ++ # non-MB: invalid byte or field list ++ # MB: invalid byte, character or field list ++ # Adjust the expected error output accordingly. ++ if (grep {ref $_ eq 'HASH' && exists $_->{ERR} && $_->{ERR} eq $inval} ++ (@new_t)) ++ { ++ my $sub = {ERR_SUBST => 's/, character//'}; ++ push @new_t, $sub; ++ push @$t, $sub; ++ } ++ #temporarily skip some failing tests ++ next if ($test_name =~ "col-0" or $test_name =~ "col-inval" or $test_name =~ "asan1"); ++ push @new, ["$test_name-mb", @new_t, {ENV => "LC_ALL=$mb_locale"}]; ++ } ++ push @Tests, @new; ++ } ++ + @Tests = triple_test \@Tests; + ++# Remember that triple_test creates from each test with exactly one "IN" ++# file two more tests (.p and .r suffix on name) corresponding to reading ++# input from a file and from a pipe. The pipe-reading test would fail ++# due to a race condition about 1 in 20 times. ++# Remove the IN_PIPE version of the "output-is-input" test above. ++# The others aren't susceptible because they have three inputs each. ++@Tests = grep {$_->[0] ne 'output-is-input.p'} @Tests; ++ + my $save_temps = $ENV{DEBUG}; + my $verbose = $ENV{VERBOSE}; + +-- +2.7.4 + diff --git a/SOURCES/coreutils-provides.inc b/SOURCES/coreutils-provides.inc new file mode 100644 index 0000000..8314a4e --- /dev/null +++ b/SOURCES/coreutils-provides.inc @@ -0,0 +1,9 @@ +Provides: bundled(gnulib) + +# make it possible to install the latest available Adobe Reader RPM for Linux +Provides: /bin/cat +Provides: /bin/chmod +Provides: /bin/echo +Provides: /bin/ln +Provides: /bin/rm +Provides: /bin/touch diff --git a/SOURCES/coreutils-selinux.patch b/SOURCES/coreutils-selinux.patch new file mode 100644 index 0000000..8274a23 --- /dev/null +++ b/SOURCES/coreutils-selinux.patch @@ -0,0 +1,120 @@ +diff --git a/man/chcon.x b/man/chcon.x +index 8c1ff6f..c84fb96 100644 +--- a/man/chcon.x ++++ b/man/chcon.x +@@ -1,4 +1,4 @@ + [NAME] +-chcon \- change file security context ++chcon \- change file SELinux security context + [DESCRIPTION] + .\" Add any additional description here +diff --git a/man/runcon.x b/man/runcon.x +index d2df13e..5c5f5d8 100644 +--- a/man/runcon.x ++++ b/man/runcon.x +@@ -1,5 +1,5 @@ + [NAME] +-runcon \- run command with specified security context ++runcon \- run command with specified SELinux security context + [DESCRIPTION] + Run COMMAND with completely-specified CONTEXT, or with current or + transitioned security context modified by one or more of LEVEL, +diff --git a/src/cp.c b/src/cp.c +index 1b528c6..25dbb88 100644 +--- a/src/cp.c ++++ b/src/cp.c +@@ -203,6 +203,9 @@ Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.\n\ + all\n\ + "), stdout); + fputs (_("\ ++ -c deprecated, same as --preserve=context\n\ ++"), stdout); ++ fputs (_("\ + --no-preserve=ATTR_LIST don't preserve the specified attributes\n\ + --parents use full source file name under DIRECTORY\n\ + "), stdout); +@@ -929,7 +932,7 @@ main (int argc, char **argv) + selinux_enabled = (0 < is_selinux_enabled ()); + cp_option_init (&x); + +- while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:TZ", ++ while ((c = getopt_long (argc, argv, "abcdfHilLnprst:uvxPRS:TZ", + long_opts, NULL)) + != -1) + { +@@ -977,6 +980,17 @@ main (int argc, char **argv) + copy_contents = true; + break; + ++ case 'c': ++ fprintf (stderr, "%s: warning: option '-c' is deprecated, please use '--preserve=context' instead\n", argv[0]); ++ if ( x.set_security_context ) { ++ (void) fprintf(stderr, "%s: cannot force target context and preserve it\n", argv[0]); ++ exit( 1 ); ++ } ++ else if (selinux_enabled) { ++ x.preserve_security_context = true; ++ x.require_preserve_context = true; ++ } ++ break; + case 'd': + x.preserve_links = true; + x.dereference = DEREF_NEVER; +diff --git a/doc/coreutils.texi b/doc/coreutils.texi +index 47e4480..cff2ead 100644 +--- a/doc/coreutils.texi ++++ b/doc/coreutils.texi +@@ -8083,6 +8083,11 @@ done + exit $fail + @end example + ++@item -c ++@cindex SELinux security context information, preserving ++Preserve SELinux security context of the original files if possible. ++Some file systems don't support storing of SELinux security context. ++ + @item --copy-contents + @cindex directories, copying recursively + @cindex copying directories recursively +diff --git a/src/install.c b/src/install.c +index d79d597..437889a 100644 +--- a/src/install.c ++++ b/src/install.c +@@ -673,7 +673,7 @@ In the 4th form, create all components of the given DIRECTORY(ies).\n\ + -v, --verbose print the name of each directory as it is created\n\ + "), stdout); + fputs (_("\ +- --preserve-context preserve SELinux security context\n\ ++ -P, --preserve-context preserve SELinux security context (-P deprecated)\n\ + -Z set SELinux security context of destination\n\ + file and each created directory to default type\n\ + --context[=CTX] like -Z, or if CTX is specified then set the\n\ +@@ -824,7 +824,7 @@ main (int argc, char **argv) + dir_arg = false; + umask (0); + +- while ((optc = getopt_long (argc, argv, "bcCsDdg:m:o:pt:TvS:Z", long_options, ++ while ((optc = getopt_long (argc, argv, "bcCsDdg:m:o:pPt:TvS:Z", long_options, + NULL)) != -1) + { + switch (optc) +@@ -885,6 +885,8 @@ main (int argc, char **argv) + no_target_directory = true; + break; + ++ case 'P': ++ fprintf (stderr, "%s: warning: option '-P' is deprecated, please use '--preserve-context' instead\n", argv[0]); + case PRESERVE_CONTEXT_OPTION: + if (! selinux_enabled) + { +@@ -892,6 +894,10 @@ main (int argc, char **argv) + "this kernel is not SELinux-enabled")); + break; + } ++ if ( x.set_security_context ) { ++ (void) fprintf(stderr, "%s: cannot force target context and preserve it\n", argv[0]); ++ exit( 1 ); ++ } + x.preserve_security_context = true; + use_default_selinux_context = false; + break; diff --git a/SOURCES/sh-utils-2.0.11-dateman.patch b/SOURCES/sh-utils-2.0.11-dateman.patch new file mode 100644 index 0000000..60cdaa6 --- /dev/null +++ b/SOURCES/sh-utils-2.0.11-dateman.patch @@ -0,0 +1,12 @@ +diff -urNp coreutils-5.97-orig/man/date.x coreutils-5.97/man/date.x +--- coreutils-5.97-orig/man/date.x 1999-11-02 15:07:36.000000000 +0100 ++++ coreutils-5.97/man/date.x 2008-10-15 10:13:31.000000000 +0200 +@@ -11,3 +11,8 @@ calendar date, time of day, time zone, day of week, relative time, + relative date, and numbers. An empty string indicates the beginning + of the day. The date string format is more complex than is easily + documented here but is fully described in the info documentation. ++[ENVIRONMENT] ++.TP ++TZ ++Specifies the timezone, unless overridden by command line parameters. ++If neither is specified, the setting from /etc/localtime is used. diff --git a/SOURCES/supported_utils b/SOURCES/supported_utils new file mode 100644 index 0000000..6db4fb5 --- /dev/null +++ b/SOURCES/supported_utils @@ -0,0 +1,105 @@ +%{_bindir}/arch +%{_bindir}/b2sum +%{_bindir}/basename +%{_bindir}/basenc +%{_bindir}/cat +%{_bindir}/chgrp +%{_bindir}/chmod +%{_bindir}/chown +%{_bindir}/cp +%{_bindir}/cut +%{_bindir}/date +%{_bindir}/dd +%{_bindir}/df +%{_bindir}/echo +%{_bindir}/env +%{_bindir}/false +%{_bindir}/link +%{_bindir}/ln +%{_bindir}/ls +%{_bindir}/mkdir +%{_bindir}/mknod +%{_bindir}/mv +%{_bindir}/nice +%{_bindir}/pwd +%{_bindir}/readlink +%{_bindir}/rm +%{_bindir}/rmdir +%{_bindir}/sleep +%{_bindir}/sort +%{_bindir}/stty +%{_bindir}/sync +%{_bindir}/mktemp +%{_bindir}/touch +%{_bindir}/true +%{_bindir}/uname +%{_bindir}/unlink +%{_bindir}/[ +%{_bindir}/base32 +%{_bindir}/base64 +%{_bindir}/chcon +%{_bindir}/cksum +%{_bindir}/comm +%{_bindir}/csplit +%{_bindir}/dir +%{_bindir}/dircolors +%{_bindir}/dirname +%{_bindir}/du +%{_bindir}/expand +%{_bindir}/expr +%{_bindir}/factor +%{_bindir}/fmt +%{_bindir}/fold +%{_bindir}/groups +%{_bindir}/head +%{_bindir}/hostid +%{_bindir}/id +%{_bindir}/install +%{_bindir}/join +%{_bindir}/logname +%{_bindir}/md5sum +%{_bindir}/mkfifo +%{_bindir}/nl +%{_bindir}/nohup +%{_bindir}/nproc +%{_bindir}/numfmt +%{_bindir}/od +%{_bindir}/paste +%{_bindir}/pathchk +%{_bindir}/pinky +%{_bindir}/pr +%{_bindir}/printenv +%{_bindir}/printf +%{_bindir}/ptx +%{_bindir}/realpath +%{_bindir}/runcon +%{_bindir}/seq +%{_bindir}/sha1sum +%{_bindir}/sha224sum +%{_bindir}/sha256sum +%{_bindir}/sha384sum +%{_bindir}/sha512sum +%{_bindir}/shred +%{_bindir}/shuf +%{_bindir}/split +%{_bindir}/stat +%{_bindir}/stdbuf +%{_bindir}/sum +%{_bindir}/tac +%{_bindir}/tail +%{_bindir}/tee +%{_bindir}/test +%{_bindir}/timeout +%{_bindir}/tr +%{_bindir}/truncate +%{_bindir}/tsort +%{_bindir}/tty +%{_bindir}/unexpand +%{_bindir}/uniq +%{_bindir}/users +%{_bindir}/vdir +%{_bindir}/wc +%{_bindir}/who +%{_bindir}/whoami +%{_bindir}/yes +%{_sbindir}/chroot diff --git a/SPECS/coreutils.spec b/SPECS/coreutils.spec new file mode 100644 index 0000000..16c70c0 --- /dev/null +++ b/SPECS/coreutils.spec @@ -0,0 +1,2374 @@ +Summary: A set of basic GNU tools commonly used in shell scripts +Name: coreutils +Version: 8.32 +Release: 32%{?dist} +License: GPLv3+ +Url: https://www.gnu.org/software/coreutils/ +Source0: https://ftp.gnu.org/gnu/%{name}/%{name}-%{version}.tar.xz +Source50: supported_utils +Source51: coreutils-provides.inc +Source105: coreutils-colorls.sh +Source106: coreutils-colorls.csh + +# do not make coreutils-single depend on /usr/bin/coreutils +%global __requires_exclude ^%{_bindir}/coreutils$ + +# ls: restore 8.31 behavior on removed directories +Patch1: coreutils-8.32-ls-removed-dir.patch + +# du: simplify leaf optimization for XFS (#1823247) +Patch2: coreutils-8.32-leaf-opt-xfs.patch + +# cp: default to --reflink=auto (#1861108) +Patch3: coreutils-8.32-cp-reflink-auto.patch + +# eliminate unportable gnulib tests +Patch4: coreutils-8.32-gnulib-perror-test.patch + +# df,stat,tail: recognize more file system types +Patch5: coreutils-8.32-new-fs-types.patch + +# rm: do not skip files upon failure to remove an empty dir (#1905481) +Patch6: coreutils-8.32-rm-stray-skip.patch + +# expr: fix invalid read with unmatched \(...\) (#1919775) +Patch7: coreutils-8.32-expr-unmatched-par.patch + +# split: fix --number=K/N to output correct part of file (#1921246) +Patch8: coreutils-8.32-split-number.patch + +# ls: fix crash printing SELinux context for unstatable files (#1921249) +Patch9: coreutils-8.32-ls-scontext-crash.patch + +# stat: add support for the exfat file system (#1921427) +Patch10: coreutils-8.32-stat-exfat.patch + +# cp: use copy_file_range if available +Patch11: coreutils-8.32-cp-file-range.patch + +# hostname,ln: fix memory leaks detected by Coverity +Patch12: coreutils-8.32-mem-leaks.patch + +# utimens: fix confusing arg type in internal func +Patch13: coreutils-8.32-coverity-utimens.patch + +# fix false positives in the upstrem test-suite (#1960792) +Patch14: coreutils-8.32-tests-false-positives.patch + +# mountlist: recognize fuse.portal as dummy file system (#1913358) +Patch15: coreutils-8.32-fuse-portal.patch + +# tail: fix stack out-of-bounds write with --follow +Patch16: coreutils-8.32-tail-use-poll.patch + +# df: fix duplicated remote entries due to bind mounts (#1979814) +Patch17: coreutils-8.32-df-duplicated-entries.patch + +# ls, stat: avoid triggering automounts (#2044981) +Patch18: coreutils-9.0-autofs-no-mount.patch + +# disable the test-lock gnulib test prone to deadlock +Patch100: coreutils-8.26-test-lock.patch + +# require_selinux_(): use selinuxenabled(8) if available +Patch105: coreutils-8.26-selinuxenable.patch + +# downstream changes to default DIR_COLORS +Patch102: coreutils-8.32-DIR_COLORS.patch +#do display processor type for uname -p/-i based on uname(2) syscall +Patch103: coreutils-8.2-uname-processortype.patch +#df --direct +Patch104: coreutils-df-direct.patch +#add note about mkdir --mode behaviour into info documentation(#610559) +Patch107: coreutils-8.4-mkdir-modenote.patch + +# sh-utils +#add info about TZ envvar to date manpage +Patch703: sh-utils-2.0.11-dateman.patch +Patch713: coreutils-4.5.3-langinfo.patch + +# (sb) lin18nux/lsb compliance - multibyte functionality patch +Patch800: coreutils-i18n.patch +# (sb) lin18nux/lsb compliance - expand/unexpand +Patch801: coreutils-i18n-expand-unexpand.patch +# i18n patch for cut - old version - used +Patch804: coreutils-i18n-cut-old.patch +# The unexpand patch above is not correct. Sent to the patch authors +Patch803: coreutils-i18n-fix-unexpand.patch +#(un)expand - allow multiple files on input - broken by patch 801 +Patch805: coreutils-i18n-fix2-expand-unexpand.patch +#(un)expand - test BOM headers +Patch806: coreutils-i18n-un-expand-BOM.patch +# make 'sort -h' work for arbitrary column even when using UTF-8 locales +Patch807: coreutils-i18n-sort-human.patch +# fold: preserve new-lines in mutlibyte text (#1418505) +Patch808: coreutils-i18n-fold-newline.patch + +#getgrouplist() patch from Ulrich Drepper. +Patch908: coreutils-getgrouplist.patch + +#SELINUX Patch - implements Redhat changes +#(upstream did some SELinux implementation unlike with RedHat patch) +Patch950: coreutils-selinux.patch + +Conflicts: filesystem < 3 +# To avoid clobbering installs +Conflicts: coreutils-single + +BuildRequires: attr +BuildRequires: autoconf +BuildRequires: automake +BuildRequires: gcc +BuildRequires: gettext-devel +BuildRequires: gmp-devel +BuildRequires: hostname +BuildRequires: libacl-devel +BuildRequires: libattr-devel +BuildRequires: libcap-devel +BuildRequires: libselinux-devel +BuildRequires: libselinux-utils +BuildRequires: make +BuildRequires: openssl-devel +BuildRequires: strace +BuildRequires: texinfo + +# test-only dependencies +BuildRequires: perl-interpreter +BuildRequires: perl(FileHandle) +%if 23 < 0%{?fedora} || 7 < 0%{?rhel} +# needed by i18n test-cases +BuildRequires: glibc-langpack-en +BuildRequires: glibc-langpack-fr +BuildRequires: glibc-langpack-ko +%endif + +Requires: %{name}-common = %{version}-%{release} + +Provides: coreutils-full = %{version}-%{release} +%include %{SOURCE51} +Obsoletes: %{name} < 8.24-100 + +%description +These are the GNU core utilities. This package is the combination of +the old GNU fileutils, sh-utils, and textutils packages. + +%package single +Summary: coreutils multicall binary +Suggests: coreutils-common +Provides: coreutils = %{version}-%{release} +Provides: coreutils%{?_isa} = %{version}-%{release} +%include %{SOURCE51} +# To avoid clobbering installs +Conflicts: coreutils < 8.24-100 +# Note RPM doesn't support separate buildroots for %files +# http://rpm.org/ticket/874 so use RemovePathPostfixes +# (new in rpm 4.13) to support separate file sets. +RemovePathPostfixes: .single +%description single +These are the GNU core utilities, +packaged as a single multicall binary. + + +%package common +# yum obsoleting rules explained at: +# https://bugzilla.redhat.com/show_bug.cgi?id=1107973#c7 +Obsoletes: %{name} < 8.24-100 + +# info doc refers to "Specifying the Time Zone" from glibc-doc (#959597) +Suggests: glibc-doc + +Summary: coreutils common optional components +%description common +Optional though recommended components, +including documentation and translations. + +%prep +%autosetup -N + +# will be regenerated in the build directories +rm -f src/fs.h + +# will be further modified by coreutils-8.32-DIR_COLORS.patch +sed src/dircolors.hin \ + -e 's| 00;36$| 01;36|' \ + > DIR_COLORS +sed src/dircolors.hin \ + -e 's| 01;31$| 00;31|' \ + -e 's| 01;35$| 00;35|' \ + > DIR_COLORS.lightbgcolor + +# git add DIR_COLORS{,.lightbgcolor} +# git commit -m "clone DIR_COLORS before patching" + +# apply all patches +%autopatch -p1 + +(echo ">>> Fixing permissions on tests") 2>/dev/null +find tests -name '*.sh' -perm 0644 -print -exec chmod 0755 '{}' '+' +(echo "<<< done") 2>/dev/null + +autoreconf -fiv + +%build +export CFLAGS="$RPM_OPT_FLAGS -fno-strict-aliasing -fpic" + +# disable -flto on ppc64le to make test-float pass (#1789115) +%ifarch ppc64le +CFLAGS="$CFLAGS -fno-lto" +%endif + +# Upstream suggests to build with -Dlint for static analyzers: +# https://lists.gnu.org/archive/html/coreutils/2018-06/msg00110.html +# ... and even for production binary RPMs: +# https://lists.gnu.org/archive/html/bug-gnulib/2020-05/msg00130.html +# There is currently no measurable performance drop or other known downside. +CFLAGS="$CFLAGS -Dlint" + +# make mknod work again in chroot without /proc being mounted (#1811038) +export ac_cv_func_lchmod="no" + +# needed for out-of-tree build +%global _configure ../configure + +%{expand:%%global optflags %{optflags} -D_GNU_SOURCE=1} +for type in separate single; do + mkdir -p $type && \ + (cd $type || exit $? + if test $type = 'single'; then + config_single='--enable-single-binary' + config_single="$config_single --without-openssl" # smaller/slower sha*sum + config_single="$config_single --without-gmp" # expr/factor machine ints + else + config_single='--with-openssl' # faster sha*sum + fi + %configure $config_single \ + --cache-file=../config.cache \ + --enable-install-program=arch \ + --enable-no-install-program=kill,uptime \ + --with-tty-group \ + DEFAULT_POSIX2_VERSION=200112 alternative=199209 || : + %make_build all V=1 + + # make sure that parse-datetime.{c,y} ends up in debuginfo (#1555079) + ln -fv ../lib/parse-datetime.{c,y} . + ) +done + +# Get the list of supported utilities +cp %SOURCE50 . + +%check +for type in separate single; do + test $type = 'single' && subdirs='SUBDIRS=.' # Only check gnulib once + (cd $type && make check %{?_smp_mflags} $subdirs) +done + +%install +for type in separate single; do + install=install + if test $type = 'single'; then + subdir=%{_libexecdir}/%{name}; install=install-exec + fi + (cd $type && make DESTDIR=$RPM_BUILD_ROOT/$subdir $install) + + # chroot was in /usr/sbin : + mkdir -p $RPM_BUILD_ROOT/$subdir/{%{_bindir},%{_sbindir}} + mv $RPM_BUILD_ROOT/$subdir/{%_bindir,%_sbindir}/chroot + + # Move multicall variants to *.single. + # RemovePathPostfixes will strip that later. + if test $type = 'single'; then + for dir in %{_bindir} %{_sbindir} %{_libexecdir}/%{name}; do + for bin in $RPM_BUILD_ROOT/%{_libexecdir}/%{name}/$dir/*; do + basebin=$(basename $bin) + mv $bin $RPM_BUILD_ROOT/$dir/$basebin.single + done + done + fi +done + +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/profile.d +install -p -c -m644 DIR_COLORS{,.lightbgcolor} $RPM_BUILD_ROOT%{_sysconfdir} +install -p -c -m644 %SOURCE105 $RPM_BUILD_ROOT%{_sysconfdir}/profile.d/colorls.sh +install -p -c -m644 %SOURCE106 $RPM_BUILD_ROOT%{_sysconfdir}/profile.d/colorls.csh + +%find_lang %name +# Add the %%lang(xyz) ownership for the LC_TIME dirs as well... +grep LC_TIME %name.lang | cut -d'/' -f1-6 | sed -e 's/) /) %%dir /g' >>%name.lang + +# (sb) Deal with Installed (but unpackaged) file(s) found +rm -f $RPM_BUILD_ROOT%{_infodir}/dir + +%files -f supported_utils +%exclude %{_bindir}/*.single +%dir %{_libexecdir}/coreutils +%{_libexecdir}/coreutils/*.so + +%files single +%{_bindir}/*.single +%{_sbindir}/chroot.single +%dir %{_libexecdir}/coreutils +%{_libexecdir}/coreutils/*.so.single +# duplicate the license because coreutils-common does not need to be installed +%{!?_licensedir:%global license %%doc} +%license COPYING + +%files common -f %{name}.lang +%config(noreplace) %{_sysconfdir}/DIR_COLORS* +%config(noreplace) %{_sysconfdir}/profile.d/* +%{_infodir}/coreutils* +%{_mandir}/man*/* +# The following go to /usr/share/doc/coreutils-common +%doc ABOUT-NLS NEWS README THANKS TODO +%license COPYING + +%changelog +* Tue Mar 01 2022 Kamil Dudka - 8.32-32 +- ls, stat: avoid triggering automounts (#2044981) +- make `df --direct` work again (#2058686) + +* Mon Aug 09 2021 Mohan Boddu +- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags + Related: rhbz#1991688 + +* Wed Jul 07 2021 Kamil Dudka - 8.32-30 +- df: fix duplicated remote entries due to bind mounts (#1979814) + +* Thu Jul 01 2021 Kamil Dudka - 8.32-29 +- tail: fix stack out-of-bounds write with --follow (#1976935) + +* Tue Jun 15 2021 Mohan Boddu - 8.32-28 +- Rebuilt for RHEL 9 BETA for openssl 3.0 (#1971065) + +* Tue Jun 08 2021 Kamil Dudka - 8.32-27 +- mountlist: recognize fuse.portal as dummy file system (#1913358) + +* Mon May 17 2021 Kamil Dudka - 8.32-26 +- cp: pick additional copy_file_range()-related fixes from upstream + +* Mon May 03 2021 Kamil Dudka - 8.32-25 +- copy: ensure we enforce --reflink=never + +* Tue Apr 27 2021 Kamil Dudka - 8.32-24 +- copy: do not refuse to copy a swap file (#1953669) + +* Thu Apr 15 2021 Mohan Boddu +- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937 + +* Fri Apr 09 2021 Kamil Dudka - 8.32-22 +- weaken the dependency on glibc-doc to reduce minimal installations +- drop the last use of ncurses no longer needed (#1830318) +- utimens: fix confusing arg type in internal func + +* Fri Mar 26 2021 Kamil Dudka - 8.32-21 +- hostname,ln: fix memory leaks detected by Coverity + +* Wed Mar 24 2021 Kamil Dudka - 8.32-20 +- cp: use copy_file_range if available + +* Thu Feb 18 2021 Kamil Dudka - 8.32-19 +- stat: add support for the exfat file system (#1921427) + +* Wed Feb 03 2021 Kamil Dudka - 8.32-18 +- make coreutils-common recommend glibc-doc for info doc refs (#959597) + +* Tue Feb 02 2021 Kamil Dudka - 8.32-17 +- ls: fix crash printing SELinux context for unstatable files (#1921249) +- split: fix --number=K/N to output correct part of file (#1921246) +- expr: fix invalid read with unmatched \(...\) (#1919775) + +* Tue Jan 26 2021 Fedora Release Engineering +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Tue Dec 08 2020 Kamil Dudka - 8.32-15 +- rm: do not skip files upon failure to remove an empty dir (#1905481) + +* Tue Nov 03 2020 Kamil Dudka - 8.32-14 +- df,stat,tail: recognize more file system types + +* Wed Oct 14 2020 Kamil Dudka - 8.32-13 +- make the %%build section idempotent + +* Mon Aug 17 2020 Kamil Dudka - 8.32-12 +- do not install /etc/DIR_COLORS.256color (#1830318) + +* Thu Jul 30 2020 Kamil Dudka - 8.32-11 +- cp: default to --reflink=auto (#1861108) + +* Mon Jul 27 2020 Fedora Release Engineering +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Fri Jul 24 2020 Kamil Dudka - 8.32-9 +- disable -flto on ppc64le to make test-float pass (#1789115) + +* Mon Jul 13 2020 Tom Stellard - 8.32-8 +- Use make macros +- https://fedoraproject.org/wiki/Changes/UseMakeBuildInstallMacro + +* Fri Jun 26 2020 James Cassell - 8.32-7 +- move ncurses to -common package since it's needed for colorls.sh +- make ncurses optional + +* Fri May 15 2020 Kamil Dudka - 8.32-6 +- compile with -Dlint to enable optional initialization and cleanup code + +* Thu Apr 23 2020 Kamil Dudka - 8.32-5 +- du: simplify leaf optimization for XFS (#1823247) + +* Fri Apr 17 2020 Tom Stellard - 8.32-4 +- Fix missing inline function definition + +* Wed Mar 11 2020 Kamil Dudka - 8.32-3 +- uniq: remove collation handling as required by newer POSIX + +* Mon Mar 09 2020 Kamil Dudka - 8.32-2 +- make mknod work again in chroot without /proc being mounted (#1811038) +- ls: restore 8.31 behavior on removed directories + +* Thu Mar 05 2020 Kamil Dudka - 8.32-1 +- new upstream release 8.32 + +* Tue Feb 11 2020 Kamil Dudka - 8.31-10 +- make upstream test-suite work with root privileges (#1800597) + +* Wed Feb 05 2020 Kamil Dudka - 8.31-9 +- use upstream fix the cp/proc-short-read test + +* Thu Jan 30 2020 Kamil Dudka - 8.31-8 +- skip a test that relies on /proc/kallsyms having immutable content + +* Tue Jan 28 2020 Fedora Release Engineering +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Thu Oct 17 2019 Kamil Dudka - 8.31-6 +- temporarily disable the use of statx (#1760300) + +* Fri Oct 11 2019 Kamil Dudka - 8.31-5 +- use statx instead of stat when available (#1760300) + +* Wed Jul 24 2019 Fedora Release Engineering +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Tue Jul 16 2019 Kamil Dudka - 8.31-3 +- disable flashing in ls colors for broken symbolic links (#1728986) + +* Mon Mar 18 2019 Kamil Dudka - 8.31-2 +- fix formatting of sha512sum(1) man page (#1688740) + +* Mon Mar 11 2019 Kamil Dudka - 8.31-1 +- new upstream release 8.31 + +* Thu Jan 31 2019 Fedora Release Engineering +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Wed Nov 07 2018 Kamil Dudka - 8.30-8 +- sync: fix open() fallback bug +- fix implicit declaration warning in coreutils-getgrouplist.patch + +* Sat Nov 03 2018 Kevin Fenzi - 8.30-7 +- Also remove Requires pre/post used by info scriptlets. + +* Sat Nov 03 2018 Kevin Fenzi - 8.30-6 +- Remove no longer needed info scriptlets + +* Thu Oct 11 2018 Kamil Dudka - 8.30-5 +- fix heap-based buffer overflow in vasnprintf() (CVE-2018-17942) + +* Thu Jul 12 2018 Fedora Release Engineering - 8.30-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Tue Jul 10 2018 Kamil Dudka - 8.30-3 +- rename gnulib's renameat2 to renameatu to avoid clash with glibc (#1598518) + +* Wed Jul 04 2018 Kamil Dudka - 8.30-2 +- sync i18n patches with Suse (patch by Bernhard Voelker) + +* Mon Jul 02 2018 Kamil Dudka - 8.30-1 +- new upstream release 8.30 + +* Wed May 30 2018 Kamil Dudka - 8.29-12 +- add provides to coreutils-single to make it a drop-in replacement + +* Mon May 28 2018 Kamil Dudka - 8.29-11 +- ls: increase the allowed abmon width from 5 to 12 (#1577872) +- date, ls: pick strftime fixes from glibc to improve locale support (#1577872) + +* Fri Apr 20 2018 Kamil Dudka - 8.29-10 +- fix crash caused by mistakenly enabled leaf optimization (#1558249) + +* Fri Mar 23 2018 Kamil Dudka - 8.29-9 +- make it possible to install the latest available Adobe Reader RPM for Linux + +* Mon Mar 19 2018 Kamil Dudka - 8.29-8 +- drop BR for bison, which is not used during the build + +* Fri Mar 16 2018 Kamil Dudka - 8.29-7 +- make sure that parse-datetime.{c,y} ends up in debuginfo (#1555079) + +* Tue Mar 06 2018 Kamil Dudka - 8.29-6 +- fix build failure with glibc-2.28 + +* Mon Feb 26 2018 Igor Gnatenko - 8.29-5 +- Remove /bin/* Provides + +* Mon Feb 19 2018 Kamil Dudka - 8.29-4 +- add explicit BR for the gcc compiler + +* Wed Feb 07 2018 Fedora Release Engineering - 8.29-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Tue Jan 23 2018 Kamil Dudka - 8.29-2 +- doc: warn about following symlinks recursively in chown/chgrp (CVE-2017-18018) +- mv -n: do not overwrite the destination + +* Tue Jan 02 2018 Kamil Dudka - 8.29-1 +- new upstream release 8.29 + +* Tue Nov 07 2017 Igor Gnatenko - 8.28-2 +- Remove very old Provides (mktemp, sh-utils, textwrap, fileutils, stat) + +* Mon Sep 04 2017 Kamil Dudka - 8.28-1 +- new upstream release 8.28 + +* Tue Aug 22 2017 Ville Skyttä - 8.27-16 +- Own the %%{_libexecdir}/coreutils dir + +* Fri Aug 18 2017 Kamil Dudka - 8.27-15 +- ptx: fix a possible crash caused by integer overflow (#1482445) + +* Wed Aug 02 2017 Fedora Release Engineering - 8.27-14 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Fri Jul 28 2017 Igor Gnatenko - 8.27-13 +- Enable separate debuginfo back + +* Wed Jul 26 2017 Kamil Dudka - 8.27-12 +- avoid build failure caused broken RPM code that produces debuginfo packages + +* Wed Jul 26 2017 Fedora Release Engineering - 8.27-11 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Tue May 30 2017 Sebastian Kisela - 8.27-10 +- doc: mention `setpriv --no-new-privs` feature in runcon info + +* Tue May 16 2017 Kamil Dudka - 8.27-9 +- add coreutils-full provides for coreutils to make it explicitly installable + +* Wed May 03 2017 Kamil Dudka - 8.27-8 +- drop coreutils-overflow.patch no longer needed (#158405) + +* Wed May 03 2017 Kamil Dudka - 8.27-7 +- drop workaround for already fixed rpm-build bug (#1306559) + +* Wed May 03 2017 Kamil Dudka - 8.27-6 +- do not mention a deprecated option in localized man pages +- drop workaround no longer needed for 10 years old rpm-build bug (#246729) +- drop unnecessary uses of %%defattr + +* Fri Apr 28 2017 Sebastian Kisela - 8.27-5 +- tail: revert to polling if a followed directory is replaced (#1283760) + +* Thu Apr 27 2017 Kamil Dudka - 8.27-4 +- date, touch: fix out-of-bounds write via large TZ variable (CVE-2017-7476) + +* Tue Apr 25 2017 Kamil Dudka - 8.27-3 +- do not obsolete coreutils-single, so it can be installed by DNF2 (#1444802) + +* Wed Mar 15 2017 Kamil Dudka - 8.27-2 +- fix spurious build failure caused by the misc/date-debug test + +* Thu Mar 09 2017 Kamil Dudka - 8.27-1 +- new upstream release 8.27 + +* Fri Feb 10 2017 Fedora Release Engineering - 8.26-7 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Fri Feb 03 2017 Kamil Dudka - 8.26-6 +- fold: preserve new-lines in mutlibyte text (#1418505) + +* Mon Jan 23 2017 Kamil Dudka - 8.26-5 +- date: fix TZ= regression (patch by Pádraig Brady) + +* Mon Jan 02 2017 Kamil Dudka - 8.26-4 +- use upstream patch for gnulib's test-lock (instead of disabling it) + +* Thu Dec 15 2016 Kamil Dudka - 8.26-3 +- drop build fixes no longer needed + +* Fri Dec 02 2016 Kamil Dudka - 8.26-2 +- apply patches automatically to ease maintenance + +* Thu Dec 01 2016 Kamil Dudka - 8.26-1 +- new upstream release 8.26 + +* Mon Oct 31 2016 Kamil Dudka - 8.25-17 +- md5sum,sha*sum: fix --ignore-missing with checksums starting with 00 + +* Tue Oct 11 2016 Tomáš Mráz - 8.25-16 +- rebuild with OpenSSL 1.1.0 + +* Wed Sep 07 2016 Kamil Dudka - 8.25-15 +- ls: allow interruption when reading slow directories (#1365933) + +* Tue Jul 19 2016 Kamil Dudka - 8.25-14 +- run autoreconf in %%prep +- drop post-install fix for Japanese locales that no longer applies +- fix 'sort -h -k' in locales that use blank as thousands separator (#1355780) + +* Thu Jul 14 2016 Kamil Dudka - 8.25-13 +- make 'sort -h' work for arbitrary column even when using UTF-8 locales + +* Mon Jul 11 2016 Kamil Dudka - 8.25-12 +- install -Z now sets default SELinux context for created directories (#1339135) +- drop the %%pre scriptlet, which is no longer needed (#1354078) +- clarify recognition of "^COLOR.*none" in /etc/DIR_COLORS (#1349579) + +* Thu Jul 07 2016 Jakub Martisko - 8.25-11 +- switch to UTF8 locale when (un)expand input contains BOM header + (#1158494) +- fixed regression where (un)expand would end with "long input line" + error when BOM header is present + +* Fri Jun 24 2016 Ondrej Vasik - 8.25-10 +- change way of detection of interactive shell in colorls.sh script + (#1321648) + +* Mon Jun 20 2016 Kamil Dudka - 8.25-9 +- add BR for glibc-langpack-en to prevent the expand/mb test from failing +- do not use /bin/mv in %%post to avoid a circular dependency (#1348043) + +* Fri Jun 17 2016 Kamil Dudka - 8.25-8 +- sync /etc/DIR_COLORS with latest upstream (#1335320) + +* Wed Jun 15 2016 Kamil Dudka - 8.25-7 +- handle info doc in RPM scriptlets of coreutils-common, which provides it +- make sure that the license file is installed, even if coreutils-common is not + +* Thu Jun 09 2016 Jakub Martisko - 8.25-6 +- (un)expand: fix regression in handling input files, where only + the first file was processed. + +* Sat Mar 05 2016 Ondrej Vasik - 8.25-5 +- cut: move back to the old i18n implementation (#1314722) + +* Mon Feb 15 2016 Ondrej Vasik - 8.25-4 +- cut: fix regression in handling fields for lines wider + than 64 chars (#1304839) + +* Thu Feb 11 2016 Lubomir Rintel - 8.25-3 +- Fix a regression in unexpand empty line handling + +* Thu Jan 21 2016 Ondrej Vasik - 8.25-2 +- Adjust the i18n patch for coreutils-8.25 +- add new base32 binary + +* Wed Jan 20 2016 Ondrej Vasik - 8.25-1 +- new upstream release(#1300282) + +* Fri Jan 15 2016 Ondrej Oprala - 8.24-108 +- cut: be MB for ALL archs + +* Fri Jan 15 2016 Ondrej Oprala - 8.24-107 +- Use the new i18n implementation for the cut utility + +* Wed Jan 13 2016 Ondrej Vasik - 8.24-106 +- mv: prevent dataloss when source dir is specified multiple + times (#1297464, by P.Brady) + +* Mon Dec 14 2015 Pádraig Brady - 8.24-105 +- Give explicit priority to coreutils over coreutils-single + +* Thu Dec 03 2015 Pádraig Brady - 8.24-104 +- Avoid libgmp and libcrypto dependencies from coreutils-single + +* Thu Dec 03 2015 Pádraig Brady - 8.24-103 +- Remove erroneous /usr/bin/kill from coreutils-single + +* Tue Dec 01 2015 Ondrej Oprala - 8.24-102 +- Use the new i18n implementation for expand/unexpand + +* Mon Nov 30 2015 Ondrej Vasik - 8.24-101 +- coreutils-single should provide versioned coreutils (#1286338) + +* Wed Nov 18 2015 Pádraig Brady - 8.24-100 +- Split package to more easily support smaller installs + +* Wed Sep 16 2015 Kamil Dudka - 8.24-4 +- fix memory leak in sort/I18N (patches written by Pádraig, #1259942) + +* Sat Sep 12 2015 Ondrej Vasik 8.24-3 +- fix one still existing occurance of non-full path in colorls.sh + +* Thu Jul 16 2015 Ondrej Vasik 8.24-2 +- use newer version of sort/I18N fix for CVE-2015-4041 + and CVE-2015-4042 + +* Sun Jul 05 2015 Ondrej Vasik 8.24-1 +- new upstream release 8.24 + +* Sat Jul 4 2015 Peter Robinson 8.23-14 +- Disable failing test-update-copyright to fix FTBFS + +* Wed Jun 17 2015 Fedora Release Engineering - 8.23-13 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Thu Jun 04 2015 Ondrej Vasik - 8.23-12 +- call utilities in colorls.* scripts with full path (#1222140) + +* Thu May 14 2015 Kamil Dudka - 8.23-11 +- run 'make check' in parallel to speed up the build + +* Wed May 13 2015 Ondrej Oprala - 8.23-10 +- sort - fix buffer overflow in some case conversions + - patch by Pádraig Brady + +* Mon Apr 20 2015 Pádraig Brady - 8.23-9 +- Adjust LS_COLORS in 256 color mode; brighten some, remove hardlink colors (#1196642) + +* Sun Mar 22 2015 Peter Robinson 8.23-8 +- Drop large ancient docs + +* Sat Feb 21 2015 Till Maas - 8.23-7 +- Rebuilt for Fedora 23 Change + https://fedoraproject.org/wiki/Changes/Harden_all_packages_with_position-independent_code + +* Mon Dec 01 2014 Ondrej Vasik - 8.23-6 +- have the LC_TIME subdirs with lang macro (#1169027) + +* Wed Oct 15 2014 Ondrej Vasik - 8.23-5 +- handle situation with ro /tmp in colorls scripts (#1149761) + +* Wed Oct 01 2014 Ondrej Vasik - 8.23-4 +- fix the sorting in multibyte locales (NUL-terminate sort keys) + - patch by Andreas Schwab (#1146185) + +* Sat Aug 16 2014 Fedora Release Engineering - 8.23-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Tue Aug 05 2014 Ondrej Vasik - 8.23-2 +- enable smp_flags again (by B.Voelker) +- fix regression in chroot + +* Tue Jul 22 2014 Ondrej Vasik - 8.23-1 +- new upstream release 8.23 +- synchronize the old differences in ls SELinux options + with upstream +- skip df/skip-duplicates.sh test for now (passing locally, failing in koji) + +* Fri Jul 11 2014 Tom Callaway - 8.22-17 +- fix license handling + +* Mon Jun 23 2014 Jakub Čajka - 8.22-16 +- fix failed tests on ppc(backport from gnulib upstream) + +* Sat Jun 07 2014 Fedora Release Engineering - 8.22-15 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Sat Apr 12 2014 Ondrej Vasik 8.22-14 +- fix dd sparse test failure on xfs filesystem(#1085727, + by P.Brady) + +* Wed Mar 05 2014 Ondrej Vasik 8.22-13 +- drop the util-linux requirements (smaller docker images), + drop ancient obsoletes of -libs subpackage + +* Sun Mar 02 2014 Ondrej Vasik 8.22-12 +- fix the date crash or infloop in TZ="" parsing (#1069657) + +* Mon Jan 13 2014 Ondrej Vasik 8.22-11 +- cp/mv/install: do not crash when getfscreatecon() is + returning a NULL context + +* Mon Jan 13 2014 Ondrej Vasik 8.22-10 +- unset the unnecessary envvars after colorls scripts(#1051703) +- improve the limitation (check for both utf8 and utf-8) + +* Fri Jan 10 2014 Ondrej Oprala 8.22-9 +- Limit the cut optimizations to UTF-8 locales only + +* Wed Jan 08 2014 Ondrej Oprala 8.22-8 +- Don't use cut mb path if not necessary (#1021403, #499220) +- several i18n patch improvements merged from OpenSUSE (fixed + compilation warnings, simplify mb handling in uniq) + +* Mon Jan 06 2014 Ondrej Oprala 8.22-7 +- Fix sorting by non-first field (#1003544) + +* Fri Jan 03 2014 Ondrej Vasik 8.22-6 +- do not modify SELinux contexts of existing parent + directories when copying files (fix by P.Brady, #1045122) + +* Thu Jan 02 2014 Ondrej Oprala 8.22-5 +- reverted an old change and constricted it's condition +- turned off two multibyte tests (wrong strcoll return value) + +* Mon Dec 23 2013 Ondrej Vasik 8.22-4 +- skip even the ls aliases in noninteractive mode + (suggested by T. Cordes, #988152) + +* Sun Dec 22 2013 Ondrej Vasik 8.22-3 +- reset buffer before copying to prevent some rare cases of + invalid output in join and uniq(#1036289) + +* Sat Dec 14 2013 Ondrej Vasik 8.22-1 +- new upstream version 8.22 +- temporarily disable multibyte cut.pl part and df symlink + tests + +* Thu Dec 12 2013 Ondrej Vasik 8.21-23 +- skip output-is-input-mb.p test - failing on armv7l (reported + by B.Voelker) + +* Mon Dec 9 2013 Peter Robinson 8.21-22 +- Add upstream patch to fix test failures on aarch64 + +* Thu Nov 28 2013 Ondrej Vasik 8.21-21 +- turn on the multibyte path in the testsuite to cover + i18n regressions + +* Wed Nov 06 2013 Ondrej Vasik 8.21-20 +- fix possible colorls.csh script errors for tcsh with + noclobber set and entered include file (#1027279) + +* Mon Oct 14 2013 Ondrej Vasik 8.21-19 +- cp: correct error message for invalid arguments + of '--no-preserve' (#1018206) + +* Thu Aug 15 2013 Ondrej Vasik 8.21-18 +- pr -e, with a mix of backspaces and TABs, could corrupt the heap + in multibyte locales (analyzed by J.Koncicky) + +* Wed Aug 14 2013 Ondrej Oprala 8.21-17 +- Fix sort multibyte incompatibilities + +* Sat Aug 03 2013 Fedora Release Engineering - 8.21-16 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild + +* Wed Jul 17 2013 Ondrej Oprala 8.21-15 +- change the TMP variable name in colorls.csh to _tmp (#981373) + +* Fri May 17 2013 Ondrej Vasik 8.21-10 +- DIR_COLORS.$TERM should have higher priority than + DIR_COLORS.256color (#921651) + +* Mon Mar 11 2013 Ondrej Oprala 8.21-9 +- add support for INCLUDE in colorls scripts (#818069) + +* Mon Mar 04 2013 Ondrej Vasik 8.21-8 +- fix factor on AArch64 (M.Salter, #917735) + +* Fri Mar 01 2013 Ondrej Vasik 8.21-7 +- ls: colorize several new archive/compressed types (#868510) + +* Sat Feb 23 2013 Ondrej Vasik 8.21-6 +- install: do proper cleanup when strip fails + (O.Oprala, B.Voekler, #632444) + +* Wed Feb 20 2013 Ondrej Vasik 8.21-5 +- fix multibyte issue in unexpand(by R.Kollar, #821262) + +* Mon Feb 18 2013 Ondrej Oprala 8.21-4 +- fix sort-mb-tests.sh test (B.Voelker) + +* Mon Feb 18 2013 Mark Wielaard 8.21-3 +- fix coreutils-i18n.patch to terminate mbdelim string (#911929) + +* Mon Feb 18 2013 Ondrej Vasik 8.21-2 +- remove unnecessary powerpc factor patch + +* Fri Feb 15 2013 Ondrej Vasik 8.21-1 +- new upstream release 8.21, update patches + +* Thu Feb 07 2013 Ondrej Oprala 8.20-8 +- add missing sort-mb-tests.sh to local.mk + +* Tue Feb 05 2013 Ondrej Vasik 8.20-7 +- add support for DTR/DSR control flow in stty(#445213) + +* Wed Jan 23 2013 Ondrej Vasik 8.20-6 +- fix multiple segmantation faults in i18n patch (by SUSE) + (#869442, #902917) + +* Thu Dec 20 2012 Ondrej Vasik 8.20-5 +- seq: fix newline output when -s specified (upstream) + +* Mon Dec 10 2012 Ondrej Vasik 8.20-4 +- fix showing duplicates in df (#709351, O.Oprala, B.Voelker) + +* Thu Dec 06 2012 Ondrej Vasik 8.20-3 +- fix factor on 32bit powerpc (upstream, #884715) + +* Mon Nov 05 2012 Ondrej Vasik 8.20-2 +- disable the temporary O_SYNC fix (glibc is fixed - #872366) + +* Sat Oct 27 2012 Ondrej Vasik 8.20-1 +- new upstream release 8.20 +- Temporarily require util-linux >= 2.22.1-3 (to prevent missing + su/runuser on system) + +* Mon Aug 20 2012 Ondrej Vasik 8.19-1 +- new upstream release 8.19 +- fix multibyte issues in cut and expand (M.Briza, #821260) + +* Sun Aug 12 2012 Ondrej Vasik 8.18-1 +- new upstream release 8.18 +- su/runuser moved to util-linux + +* Wed Jul 18 2012 Fedora Release Engineering - 8.17-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Tue May 15 2012 Ondrej Vasik 8.17-3 +- add virtual provides for bundled(gnulib) copylib (#821748) + +* Fri May 11 2012 Ondrej Vasik 8.17-2 +- ls: upstream fix - correctly show symlinks in / + +* Fri May 11 2012 Ondrej Vasik 8.17-1 +- new upstream release 8.17 + +* Fri May 04 2012 Ondrej Vasik 8.16-3 +- add .htm and .shtml to colorized DIR_COLORS document + type (#817218) + +* Mon Apr 16 2012 Ondrej Vasik 8.16-2 +- fix the tcsh colorls.csh behaviour in non-interactive + mode (#804604) + +* Mon Mar 26 2012 Ondrej Vasik 8.16-1 +- new upstream release 8.16 +- defuzz patches, remove already applied patches + +* Thu Mar 08 2012 Ondrej Vasik 8.15-8 +- fix regression in du -x with nondir argument (by J.Meyering) + +* Wed Mar 07 2012 Ondrej Vasik 8.15-7 +- fix sort segfault with multibyte locales (by P.Brady) + +* Fri Feb 10 2012 Harald Hoyer 8.15-6 +- turn on testsuite again + +* Wed Jan 25 2012 Harald Hoyer 8.15-5 +- add filesystem guard + +* Wed Jan 25 2012 Harald Hoyer 8.15-4 +- add missing provides for the /usr-move + +* Wed Jan 25 2012 Harald Hoyer 8.15-3 +- install everything in /usr + https://fedoraproject.org/wiki/Features/UsrMove + +* Mon Jan 16 2012 Kamil Dudka - 8.15-2 +- fix stack smashing, buffer overflow, and invalid output of pr (#772172) + +* Sat Jan 07 2012 Ondrej Vasik - 8.15-1 +- new upstream release 8.15 + +* Thu Jan 05 2012 Ondrej Vasik - 8.14-6 +- do not use shebang in sourced colorls.csh + +* Thu Jan 05 2012 Ondrej Vasik - 8.14-5 +- fix pr -c and pr -v segfault with multibyte locales + +* Mon Oct 31 2011 Rex Dieter 8.14-4 +- rebuild (gmp), last time, I promise + +* Mon Oct 24 2011 Ondrej Vasik - 8.14-3 +- require at least pam 1.1.3-7 (#748215) + +* Thu Oct 20 2011 Ondrej Vasik - 8.14-2 +- rebuild for gmp + +* Wed Oct 12 2011 Ondrej Vasik - 8.14-1 +- new upstream release 8.14 + +* Mon Sep 26 2011 Peter Schiffer - 8.13-2.2 +- rebuild with new gmp + +* Mon Sep 12 2011 Ondrej Vasik - 8.13-2 +- Obsolete coreutils-libs (#737287) + +* Fri Sep 09 2011 Ondrej Vasik - 8.13-1 +- new upstream release 8.13 +- temporarily disable recently added multibyte checks in + misc/cut test +- fix the SUSE fix for cut output-delimiter +- drop coreutils-libs subpackage, no longer needed + +* Mon Sep 05 2011 Ondrej Vasik - 8.12-7 +- incorporate some i18n patch fixes from OpenSUSE: + - fix cut output-delimiter option + - prevent infinite loop in sort when ignoring chars + - prevent using unitialized variable in cut + +* Tue Aug 23 2011 Ondrej Vasik - 8.12-6 +- su: fix shell suspend in tcsh (#597928) + +* Thu Aug 18 2011 Ondrej Vasik - 8.12-5 +- variable "u" should be static in uname processor type patch + +* Thu Aug 11 2011 Ondrej Vasik - 8.12-4 +- deprecate non-upstream cp -Z/--context (install should be + used instead of it), make it working if destination exists + (#715557) + +* Fri Jul 29 2011 Ondrej Vasik - 8.12-3 +- use acl_extended_file_nofollow() if available (#692823) + +* Fri Jul 15 2011 Ondrej Vasik - 8.12-2 +- support ecryptfs mount of Private (postlogin into su.pamd) + (#722323) + +* Wed Apr 27 2011 Ondrej Vasik - 8.12-1 +- new upstream release 8.12 + +* Thu Apr 14 2011 Ondrej Vasik - 8.11-2 +- fix issue with df --direct(extra new line) + +* Thu Apr 14 2011 Ondrej Vasik - 8.11-1 +- new upstream release 8.11, defuzz patches + +* Tue Mar 22 2011 Ondrej Vasik - 8.10-7 +- add note about mkdir mode behaviour into info + documentation (#610559) + +* Mon Mar 14 2011 Ondrej Vasik - 8.10-6 +- fix possible uninitalized variables usage caused by i18n + patch(#683799) + +* Fri Mar 4 2011 Ondrej Vasik - 8.10-5 +- make coreutils build even without patches (with + nopam, norunuser and noselinux variables) + +* Thu Feb 17 2011 Ondrej Vasik - 8.10-4 +- colorize documents by DIR_COLORS files(brown like mc) + +* Thu Feb 17 2011 Ondrej Vasik - 8.10-3 +- add several new TERMs to DIR_COLORS files(#678147) + +* Tue Feb 08 2011 Fedora Release Engineering - 8.10-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Fri Feb 04 2011 Ondrej Vasik - 8.10-1 +- new upstream release coreutils-8.10 + +* Sat Jan 08 2011 Dennis Gilmore - 8.9-2 +- drop no longer needed mkstemp patch for sparc + +* Tue Jan 04 2011 Ondrej Vasik - 8.9-1 +- new upstream release coreutils-8.9 + +* Fri Dec 31 2010 Ondrej Vasik - 8.8-2 +- The suffix length was dependent on the number of bytes + or lines per file (#666293) + +* Thu Dec 23 2010 Ondrej Vasik - 8.8-1 +- fix parallel sorting issue (#655096) +- new upstream release coreutils-8.8 (#665164) + +* Thu Nov 18 2010 Ondrej Vasik - 8.7-2 +- don't prompt for password with runuser(#654367) + +* Mon Nov 15 2010 Ondrej Vasik - 8.7-1 +- new upstream release coreutils-8.7 +- pam support in su consolidation with SUSE(#622700) + +* Wed Nov 03 2010 Kamil Dudka - 8.6-3 +- prevent sort from assertion failure in case LC_CTYPE does not match LC_TIME + (#647938) + +* Tue Oct 26 2010 Kamil Dudka - 8.6-2 +- improve i18n support in sort (debug-keys test is now back) + +* Wed Oct 20 2010 Ondrej Vasik - 8.6-1 +- new upstream release 8.6 +- remove applied patches, temporarily disable sort + debug-keys test for multibyte locales (failing + because of i18n patch) + +* Thu Sep 30 2010 Ondrej Vasik - 8.5-10 +- various fixes for case conversion in tr(#611274) + +* Wed Sep 29 2010 jkeating - 8.5-9 +- Rebuilt for gcc bug 634757 + +* Mon Sep 20 2010 Ondrej Vasik - 8.5-8 +- change assertion failure for invalid multibyte input + in sort to less confusing error message(#591352) + +* Wed Sep 08 2010 Ondrej Vasik - 8.5-7 +- add RELRO protection to su as well (#630017) + +* Mon Sep 06 2010 Ondrej Vasik - 8.5-6 +- compile su with pie again (#630017) + +* Mon Aug 30 2010 Ondrej Vasik - 8.5-5 +- fix double free abort in tac (#628213) + +* Thu Jul 22 2010 Ondrej Vasik - 8.5-4 +- Add .ear, .war, .sar , for Java jar-like archives to + dircolors (#616497) + +* Fri Jul 2 2010 Dan Horák - 8.5-3 +- rebuilt with the updated configuration patch +- drop the old -O1 exception for s390(x) +- updated the getgrouplist patch (Kamil Dudka) + +* Tue Apr 27 2010 Ondrej Vasik - 8.5-2 +- doublequote LS_COLORS in colorls.*sh scripts to speedup + shell start(#586029) +- add patch for mkstemp on sparc64(Dennis Gilmore) +- update /etc/DIR_COLORS* files + +* Mon Apr 26 2010 Ondrej Vasik - 8.5-1 +- new upstream release 8.5 + +* Thu Apr 15 2010 Ondrej Vasik - 8.4-8 +- move readlink from /usr/bin to bin, keep symlink in + /usr/bin(#580682) + +* Mon Mar 29 2010 Kamil Dudka - 8.4-7 +- a new option df --direct + +* Sat Mar 20 2010 Ondrej Vasik - 8.4-6 +- run tput colors in colorls profile.d scripts only + in the interactive mode(#450424) + +* Fri Feb 12 2010 Ondrej Vasik - 8.4-5 +- fix exit status of terminated child processes in su with + pam(#559098) + +* Fri Feb 05 2010 Ondrej Vasik - 8.4-4 +- do not depend on selinux patch application in + _require_selinux tests(#556350) + +* Fri Jan 29 2010 Ondrej Vasik - 8.4-3 +- do not fail tests if there are no loopdevices left + (#558898) + +* Tue Jan 26 2010 Ondrej Vasik - 8.4-2 +- who doesn't determine user's message status correctly + (#454261) + +* Thu Jan 14 2010 Ondrej Vasik - 8.4-1 +- new upstream release 8.4 + +* Fri Jan 08 2010 Ondrej Vasik - 8.3-1 +- new upstream release 8.3 + +* Wed Jan 06 2010 Ondrej Vasik - 8.2-6 +- require gmp-devel/gmp for large numbers support(#552846) + +* Sun Dec 27 2009 Ondrej Vasik - 8.2-5 +- fix misc/selinux root-only test(#550494) + +* Sat Dec 19 2009 Ondrej Vasik - 8.2-4 +- bring back uname -p/-i functionality except of the + athlon hack(#548834) +- comment patches + +* Wed Dec 16 2009 Ondrej Vasik - 8.2-3 +- use grep instead of deprecated egrep in colorls.sh script + (#548174) +- remove unnecessary versioned requires/conflicts +- remove non-upstream hack for uname -p + +* Wed Dec 16 2009 Ondrej Vasik - 8.2-2 +- fix DIR_COLORS.256color file + +* Fri Dec 11 2009 Ondrej Vasik - 8.2-1 +- new upstream release 8.2 +- removed applied patches, temporarily do not run dup_cloexec() + dependent gnulib tests failing in koji + +* Fri Nov 27 2009 Ondrej Vasik - 8.1-1 +- new upstream release 8.1 +- fix build under koji (no test failures with underlying + RHEL-5 XEN kernel due to unsearchable path and lack of + futimens functionality) + +* Wed Oct 07 2009 Ondrej Vasik - 8.0-2 +- update /etc/DIR_COLORS* files + +* Wed Oct 07 2009 Ondrej Vasik - 8.0-1 +- New upstream release 8.0 (beta), defuzz patches, + remove applied patches + +* Mon Oct 05 2009 Ondrej Vasik - 7.6-7 +- chcon no longer aborts on a selinux disabled system + (#527142) + +* Fri Oct 02 2009 Ondrej Vasik - 7.6-6 +- ls -LR exits with status 2, not 0, when it encounters + a cycle(#525402) +- ls: print "?", not "0" as inode of dereferenced dangling + symlink(#525400) +- call the install-info on .gz info files + +* Tue Sep 22 2009 Ondrej Vasik - 7.6-5 +- improve and correct runuser documentation (#524805) + +* Mon Sep 21 2009 Ondrej Vasik - 7.6-4 +- add dircolors color for GNU lzip (#516897) + +* Fri Sep 18 2009 Ondrej Vasik - 7.6-3 +- fixed typo in DIR_COLORS.256color causing no color for + multihardlink + +* Wed Sep 16 2009 Ondrej Vasik - 7.6-2 +- fix copying of extended attributes for read only source + files + +* Sat Sep 12 2009 Ondrej Vasik - 7.6-1 +- new upstream bugfix release 7.6, removed applied patches, + defuzzed the rest + +* Thu Sep 10 2009 Ondrej Vasik - 7.5-6 +- fix double free error in fold for singlebyte locales + (caused by multibyte patch) + +* Tue Sep 08 2009 Ondrej Vasik - 7.5-5 +- fix sort -h for multibyte locales (reported via + http://bugs.archlinux.org/task/16022) + +* Thu Sep 03 2009 Ondrej Vasik - 7.5-4 +- fixed regression where df -l as regular user + cause "Permission denied" (#520630, introduced by fix for + rhbz #497830) + +* Fri Aug 28 2009 Ondrej Vasik - 7.5-3 +- ls -i: print consistent inode numbers also for mount points + (#453709) + +* Mon Aug 24 2009 Ondrej Vasik - 7.5-2 +- Better fix than workaround the koji insufficient utimensat + support issue to prevent failures in other packages + +* Fri Aug 21 2009 Ondrej Vasik - 7.5-1 +- New upstream release 7.5, remove already applied patches, + defuzz few others, xz in default set(by dependencies), + so no explicit br required +- skip two new tests on system with insufficient utimensat + support(e.g. koji) +- libstdbuf.so in separate coreutils-libs subpackage +- update /etc/DIRCOLORS* + +* Thu Aug 06 2009 Ondrej Vasik - 7.4-6 +- do process install-info only with info files present(#515970) +- BuildRequires for xz, use xz tarball + +* Wed Aug 05 2009 Kamil Dudka - 7.4-5 +- ls -1U with two or more arguments (or with -R or -s) works properly again +- install runs faster again with SELinux enabled (#479502) + +* Fri Jul 24 2009 Fedora Release Engineering - 7.4-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild + +* Mon Jul 06 2009 Ondrej Vasik 7.4-3 +- do not ignore sort's version sort for multibyte locales + (#509688) + +* Thu Jun 18 2009 Ondrej Vasik 7.4-2 +- temporarily workaround probable kernel issue with + TCSADRAIN(#504798) + +* Mon May 25 2009 Ondrej Vasik 7.4-1 +- new upstream release 7.4, removed applied patches + +* Thu Apr 23 2009 Ondrej Vasik 7.2-3 +- fix segfaults in join (i18n patch) when using multibyte + locales(#497368) + +* Fri Apr 17 2009 Ondrej Vasik 7.2-2 +- make mv xattr support failures silent (as is done for + cp -a) - #496142 + +* Tue Mar 31 2009 Ondrej Vasik 7.2-1 +- New upstream bugfix release 7.2 +- removed applied patches +- temporarily disable strverscmp failing gnulib test + +* Thu Mar 19 2009 Ondrej Vasik 7.1-7 +- do not ship /etc/DIR_COLORS.xterm - as many terminals + use TERM xterm and black background as default - making + ls color output unreadable +- shipping /etc/DIR_COLORS.lightbgcolor instead of it for + light(white/gray) backgrounds +- try to preserve xattrs in cp -a when possible + +* Mon Mar 02 2009 Ondrej Vasik 7.1-6 +- fix sort bugs (including #485715) for multibyte locales + as well + +* Fri Feb 27 2009 Ondrej Vasik 7.1-5 +- fix infinite loop in recursive cp (upstream, introduced + by 7.1) + +* Thu Feb 26 2009 Ondrej Vasik 7.1-4 +- fix showing ACL's for ls -Z (#487374), fix automatic + column width for it as well + +* Wed Feb 25 2009 Ondrej Vasik 7.1-3 +- fix couple of bugs (including #485715) in sort with + determining end of fields(upstream) + +* Wed Feb 25 2009 Ondrej Vasik 7.1-2 +- workaround libcap issue with broken headers (#483548) +- fix gnulib testsuite failure (4x77 (skip) is not + 77(skip) ;) ) + +* Tue Feb 24 2009 Ondrej Vasik - 7.1-1 +- New upstream release 7.1 (temporarily using tar.gz tarball + as there are no xz utils in Fedora), removed applied + patches, amended patches and LS_COLORS files + +* Tue Feb 24 2009 Fedora Release Engineering - 7.0-8 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild + +* Wed Jan 28 2009 Kamil Dudka - 7.0-7 +- added BuildRequires for libattr-devel and attr + +* Wed Jan 28 2009 Kamil Dudka - 7.0-6 +- cp/mv: add --no-clobber (-n) option to not overwrite target +- cp/mv: add xattr support (#202823) + +* Thu Dec 04 2008 Ondrej Vasik - 7.0-5 +- fix info documentation for expr command as well(#474434) + +* Thu Dec 04 2008 Ondrej Vasik - 7.0-4 +- fixed syntax error w/ "expr" command using negative + string/integer as first (i.e expr -125) - due to + complexity of changes used diff against upstream git-head + (#474434) +- enable total-awk test again (and skip it when df not working) + +* Tue Nov 25 2008 Ondrej Vasik - 7.0-3 +- package summary tuning + +* Fri Nov 21 2008 Ondrej Vasik - 7.0-2 +- added requirements for util-linux-ng >= 2.14 + because of file conflict in update from F-8/F-9(#472445) +- some sed cleanup, df totaltests patch changes (not working + correctly yet :( ) + +* Wed Nov 12 2008 Ondrej Vasik - 7.0-1 +- new upstream release +- modification/removal of related patches +- use automake 1.10.1 instead of 1.10a +- temporarily skip df --total tests (failures), + timeout-paramaters (failure on ppc64) + +* Mon Nov 03 2008 Ondrej Vasik - 6.12-17 +- Requires: ncurses (#469277) + +* Wed Oct 22 2008 Ondrej Vasik - 6.12-16 +- make possible to disable capability in ls due to + performance impact when not cached(#467508) +- do not patch generated manpages - generate them at build + time +- do not mistakenly display -g and -G runuser option in su + --help output + +* Mon Oct 13 2008 Ondrej Vasik - 6.12-15 +- fix several date issues(e.g. countable dayshifts, ignoring + some cases of relative offset, locales conversions...) +- clarify ls exit statuses documentation (#446294) + +* Sun Oct 12 2008 Ondrej Vasik - 6.12-14 +- cp -Z now correctly separated in man page (#466646) +- cp -Z works again (#466653) +- make preservation of SELinux CTX non-mandatory for + preserve=all cp option + +* Wed Oct 08 2008 Ondrej Vasik - 6.12-13 +- remove unimplemented (never accepted by upstream) option + for chcon changes only. Removed from help and man. +- remove ugly lzma hack as lzma is now supported by setup + macro + +* Mon Oct 06 2008 Jarod Wilson - 6.12-12 +- fix up potential test failures when building in certain + slightly quirky environments (part of bz#442352) + +* Mon Oct 06 2008 Ondrej Vasik - 6.12-11 +- added requires for libattr (#465569) + +* Mon Sep 29 2008 Ondrej Vasik - 6.12-10 +- seq should no longer fail to display final number of some + float usages of seq with utf8 locales(#463556) + +* Wed Aug 13 2008 Ondrej Vasik - 6.12-9 +- mention that DISPLAY and XAUTHORITY envvars are preserved + for pam_xauth in su -l (#450505) + +* Mon Aug 04 2008 Kamil Dudka - 6.12-8 +- ls -U1 now uses constant memory + +* Wed Jul 23 2008 Kamil Dudka - 6.12-7 +- dd: iflag=fullblock now read full blocks if possible + (#431997, #449263) +- ls: --color now highlights files with capabilities (#449985) + +* Wed Jul 16 2008 Ondrej Vasik - 6.12-6 +- Get rid off fuzz in patches + +* Fri Jul 04 2008 Ondrej Vasik - 6.12-5 +- fix authors for basename and echo +- fix who info pages, print last runlevel only for printable + chars + +* Mon Jun 16 2008 Ondrej Vasik - 6.12-4 +- print verbose output of chcon with newline after each + message (#451478) + +* Fri Jun 06 2008 Ondrej Vasik - 6.12-3 +- workaround for koji failures(#449910, #442352) now + preserves timestamps correctly - fallback to supported + functions, added test case +- runuser binary is no longer doubled in /usr/bin/runuser + +* Wed Jun 04 2008 Ondrej Vasik - 6.12-2 +- workaround for strange koji failures(#449910,#442352) +- fixed ls -ZC segfault(#449866, introduced by 6.10-1 + SELinux patch reworking) + +* Mon Jun 02 2008 Ondrej Vasik - 6.12-1 +- New upstream release 6.12, adapted patches + +* Thu May 29 2008 Tom "spot" Callaway - 6.11-5 +- fix SHA256/SHA512 to work on sparc + +* Tue May 20 2008 Ondrej Vasik - 6.11-4 +- fixed a HUGE memory leak in install binary(#447410) + +* Mon May 19 2008 Ondrej Vasik - 6.11-3 +- added arch utility (from util-linux-ng) +- do not show executable file types without executable bit + in colored ls as executable + +* Wed Apr 23 2008 Ondrej Vasik - 6.11-2 +- Do not show misleading scontext in id command when user + is specified (#443485) +- Avoid possible test failures on non-english locales + +* Mon Apr 21 2008 Ondrej Vasik - 6.11-1 +- New upstream release 6.11 +- removed accepted patches + few minor patch changes + +* Fri Apr 18 2008 Ondrej Vasik - 6.10-21 +- fix wrong checksum line handling in sha1sum -c + command(#439531) + +* Tue Apr 15 2008 Ondrej Vasik - 6.10-20 +- fix possible segfault in sha1sum/md5sum command + +* Mon Apr 14 2008 Ondrej Vasik - 6.10-19 +- fix possible build-failure typo in i18n patch(#442205) + +* Mon Apr 7 2008 Ondrej Vasik - 6.10-18 +- fix colorls.sh syntax with Zsh (#440652) +- mention that cp -a includes -c option + mention cp -c + option in manpages (#440056) +- fix typo in runuser manpages (#439410) + +* Sat Mar 29 2008 Ondrej Vasik - 6.10-17 +- better workaround of glibc getoptc change(factor test) +- don't segfault mknod, mkfifo with invalid-selinux-context + +* Thu Mar 27 2008 Ondrej Vasik - 6.10-16 +- keep LS_COLORS when USER_LS_COLORS defined +- someupstream fixes: +- mkdir -Z invalid-selinux-context dir no longer segfaults +- ptx with odd number of backslashes no longer leads to buffer + overflow +- paste -d'\' file" no longer ovveruns memory + +* Wed Mar 26 2008 Ondrej Vasik - 6.10-15 +- covered correct handling for some test conditions failures + e.g. root build+selinux active and not running mcstrans(d) + or selinux enforcing (#436717) + +* Wed Mar 19 2008 Ondrej Vasik - 6.10-14 +- mv: never unlink a destination file before calling rename + (upstream, #438076) + +* Mon Mar 17 2008 Ondrej Vasik - 6.10-13 +- disable echo option separator behavior(added by #431005, + request for removal #437653 + upstream) +- temporarily disabled longoptions change until full + clarification upstreamery (#431005) + +* Tue Mar 11 2008 Ondrej Vasik - 6.10-12 +- fixed harmless double close of stdout in dd(#436368) + +* Thu Mar 6 2008 Ondrej Vasik - 6.10-11 +- fixed broken order of params in stat(#435669) + +* Tue Mar 4 2008 Ondrej Vasik - 6.10-10 +- colorls.csh missing doublequotes (#435789) +- fixed possibility to localize verbose outputs + +* Mon Mar 3 2008 Ondrej Vasik - 6.10-9 +- consolidation of verbose output to stdout (upstream) + +* Mon Feb 18 2008 Ondrej Vasik - 6.10-8 +- use default security context in install - broken by + coreutils-6.10 update(#319231) +- some sh/csh scripts optimalizations(by ville.skytta@iki.fi, + - #433189, #433190) + +* Mon Feb 11 2008 Ondrej Vasik - 6.10-7 +- keep old csh/sh usermodified colorls shell scripts + but use the new ones(#432154) + +* Thu Feb 7 2008 Ondrej Vasik - 6.10-6 +- better 256-color support in colorls shell scripts +- color tuning(based on feedback in #429121) + +* Mon Feb 4 2008 Ondrej Vasik - 6.10-5 +- enabled 256-color support in colorls shell scripts(#429121) +- fixed syntax error in csh script(#431315) + +* Thu Jan 31 2008 Ondrej Vasik - 6.10-4 +- forgotten return in colorls.sh change + +* Thu Jan 31 2008 Ondrej Vasik - 6.10-3 +- fix unability of echo to display certain strings(added -- + separator, #431005) +- do not require only one long_opt for certain commands + e.g. sleep, yes - but use first usable (#431005) +- do not override userspecified LS_COLORS variable, but + use it for colored ls(#430827) +- discard errors from dircolors to /dev/null + some tuning + of lscolor sh/csh scripts(#430823) +- do not consider files with SELinux security context as + files having ACL in ls long format(#430779) + +* Mon Jan 28 2008 Ondrej Vasik - 6.10-2 +- some manpages improvements(#406981,#284881) +- fix non-versioned obsoletes of mktemp(#430407) + +* Fri Jan 25 2008 Ondrej Vasik - 6.10-1 +- New upstream release(changed %%prep because of lack of lzma + support in %%setup macro) +- License GPLv3+ +- removed patches cp-i-u,du-ls-upstream,statsecuritycontext, + futimens,getdateYYYYMMDD,ls-x +- modified patches to be compilable after upstream changes +- selinux patch reworked to have backward compatibility with + F8(cp,ls and stat behaviour differ from upstream in SELinux + options) +- su-l/runuser-l pam file usage a bit documented(#368721) +- more TERMs for DIR_COLORS, added colors for audio files, + more image/compress file types(taken from upstream + dircolors.hin) +- new file DIR_COLORS.256color which takes advantage from + 256color term types-not really used yet(#429121) + +* Wed Jan 16 2008 Ondrej Vasik - 6.9-17 +- added several missing colored TERMs(including rxvt-unicode, + screen-256color and xterm-256color) to DIR_COLORS and + DIR_COLORS.xterm(#239266) + +* Wed Dec 05 2007 Ondrej Vasik - 6.9-16 +- fix displaying of security context in stat(#411181) + +* Thu Nov 29 2007 Ondrej Vasik - 6.9-15 +- completed fix of wrong colored broken symlinks in ls(#404511) + +* Fri Nov 23 2007 Ondrej Vasik - 6.9-14 +- fixed bug in handling YYYYMMDD date format with relative + signed offset(#377821) + +* Tue Nov 13 2007 Ondrej Vasik - 6.9-13 +- fixed bug in selinux patch which caused bad preserving + of security context in install(#319231) + +* Fri Nov 02 2007 Ondrej Vasik - 6.9-12 +- added some upstream supported dircolors TERMs(#239266) +- fixed du output for unaccesible dirs(#250089) +- a bit of upstream tunning for symlinks + +* Tue Oct 30 2007 Ondrej Vasik - 6.9-11 +- allow cp -a to rewrite file on different filesystem(#219900) + (based on upstream patch) + +* Mon Oct 29 2007 Ondrej Vasik - 6.9-10 +- modified coreutils-i18n.patch because of sort -R in + a non C locales(fix by Andreas Schwab) (#249315) + +* Mon Oct 29 2007 Ondrej Vasik - 6.9-9 +- applied upstream patch for runuser to coreutils-selinux.patch(#232652) +- License tag to GPLv2+ + +* Thu Oct 25 2007 Ondrej Vasik - 6.9-8 +- applied upstream patch for cp and mv(#248591) + +* Thu Aug 23 2007 Pete Graner - 6.9-7 +- Fix typo in spec file. (CVS merge conflict leftovers) + +* Thu Aug 23 2007 Pete Graner - 6.9-6 +- Remove --all-name from spec file its now provided in the upstream rpm's find-lang.sh +- Rebuild + +* Tue Aug 14 2007 Tim Waugh 6.9-5 +- Don't generate runuser.1 since we ship a complete manpage for it + (bug #241662). + +* Wed Jul 4 2007 Tim Waugh 6.9-4 +- Use hard links instead of symbolic links for LC_TIME files (bug #246729). + +* Wed Jun 13 2007 Tim Waugh 6.9-3 +- Fixed 'ls -x' output (bug #240298). +- Disambiguate futimens() from the glibc implementation (bug #242321). + +* Mon Apr 02 2007 Karsten Hopp 6.9-2 +- /bin/mv in %%post requires libselinux + +* Mon Mar 26 2007 Tim Waugh 6.9-1 +- 6.9. + +* Fri Mar 9 2007 Tim Waugh +- Better install-info scriptlets (bug #225655). + +* Thu Mar 1 2007 Tim Waugh 6.8-1 +- 6.8+, in preparation for 6.9. + +* Thu Feb 22 2007 Tim Waugh 6.7-9 +- Use sed instead of perl for text replacement (bug #225655). +- Use install-info scriptlets from the guidelines (bug #225655). + +* Tue Feb 20 2007 Tim Waugh 6.7-8 +- Don't mark profile scripts as config files (bug #225655). +- Avoid extra directory separators (bug #225655). + +* Mon Feb 19 2007 Tim Waugh 6.7-7 +- Better Obsoletes/Provides versioning (bug #225655). +- Use better defattr (bug #225655). +- Be info file compression tolerant (bug #225655). +- Moved changelog compression to %%install (bug #225655). +- Prevent upstream changes being masked (bug #225655). +- Added a comment (bug #225655). +- Use install -p for non-compiled files (bug #225655). +- Use sysconfdir macro for /etc (bug #225655). +- Use Requires(pre) etc for install-info (bug #225655). + +* Fri Feb 16 2007 Tim Waugh 6.7-6 +- Provide version for stat (bug #225655). +- Fixed permissions on profile scripts (bug #225655). + +* Wed Feb 14 2007 Tim Waugh 6.7-5 +- Removed unnecessary stuff in pre scriptlet (bug #225655). +- Prefix sources with 'coreutils-' (bug #225655). +- Avoid %%makeinstall (bug #225655). + +* Tue Feb 13 2007 Tim Waugh 6.7-4 +- Ship COPYING file (bug #225655). +- Use datadir and infodir macros in %%pre scriptlet (bug #225655). +- Use spaces not tabs (bug #225655). +- Fixed build root. +- Change prereq to requires (bug #225655). +- Explicitly version some obsoletes tags (bug #225655). +- Removed obsolete pl translation fix. + +* Mon Jan 22 2007 Tim Waugh 6.7-3 +- Make scriptlet unconditionally succeed (bug #223681). + +* Fri Jan 19 2007 Tim Waugh 6.7-2 +- Build does not require libtermcap-devel. + +* Tue Jan 9 2007 Tim Waugh 6.7-1 +- 6.7. No longer need sort-compatibility, rename, newhashes, timestyle, + acl, df-cifs, afs or autoconf patches. + +* Tue Jan 2 2007 Tim Waugh +- Prevent 'su --help' showing runuser-only options such as --group. + +* Fri Nov 24 2006 Tim Waugh 5.97-16 +- Unbreak id (bug #217177). + +* Thu Nov 23 2006 Tim Waugh 5.97-15 +- Fixed stat's 'C' format specifier (bug #216676). +- Misleading 'id -Z root' error message (bug #211089). + +* Fri Nov 10 2006 Tim Waugh 5.97-14 +- Clarified runcon man page (bug #213846). + +* Tue Oct 17 2006 Tim Waugh 5.97-13 +- Own LC_TIME locale directories (bug #210751). + +* Wed Oct 4 2006 Tim Waugh 5.97-12 +- Fixed 'cp -Z' when destination exists, again (bug #189967). + +* Thu Sep 28 2006 Tim Waugh 5.97-11 +- Back-ported rename patch (bug #205744). + +* Tue Sep 12 2006 Tim Waugh 5.97-10 +- Ignore 'cifs' filesystems for 'df -l' (bug #183703). +- Include -g/-G in runuser man page (part of bug #199344). +- Corrected runuser man page (bug #200620). + +* Thu Aug 24 2006 Tim Waugh 5.97-9 +- Fixed warnings in pam, i18n, sysinfo, selinux and acl patches (bug #203166). + +* Wed Aug 23 2006 Tim Waugh 5.97-8 +- Don't chdir until after PAM bits in su (bug #197659). + +* Tue Aug 15 2006 Tim Waugh 5.97-7 +- Fixed 'sort -b' multibyte problem (bug #199986). + +* Fri Jul 21 2006 Tim Waugh 5.97-6 +- Added runuser '-g' and '-G' options (bug #199344). +- Added su '--session-command' option (bug #199066). + +* Tue Jul 18 2006 Tomas Mraz 5.97-5 +- 'include' su and runuser scripts in su-l and runuser-l scripts + +* Thu Jul 13 2006 David Howells 5.97-4 +- split the PAM scripts for "su -l"/"runuser -l" from that of normal "su" and + "runuser" (#198639) +- add keyinit instructions to PAM scripts + +* Wed Jul 12 2006 Jesse Keating - 5.97-3.1 +- rebuild + +* Tue Jul 11 2006 Tomas Mraz 5.97-3 +- allow root to su to expired user (#152420) + +* Thu Jun 29 2006 Tim Waugh 5.97-2 +- Allow 'sort +1 -2' (patch from upstream). + +* Sun Jun 25 2006 Tim Waugh 5.97-1 +- 5.97. No longer need tempname or tee patches, or pl translation. + +* Sun Jun 25 2006 Tim Waugh 5.96-4 +- Include new hashes (bug #196369). Patch from upstream. +- Build at -O1 on s390 for the moment (bug #196369). + +* Fri Jun 9 2006 Tim Waugh +- Fix large file support for temporary files. + +* Mon Jun 5 2006 Tim Waugh 5.96-3 +- Fixed Polish translation. + +* Mon May 22 2006 Tim Waugh 5.96-2 +- 5.96. No longer need proc patch. + +* Fri May 19 2006 Tim Waugh +- Fixed pr properly in multibyte locales (bug #192381). + +* Tue May 16 2006 Tim Waugh 5.95-3 +- Upstream patch to fix cp -p when proc is not mounted (bug #190601). +- BuildRequires libacl-devel. + +* Mon May 15 2006 Tim Waugh +- Fixed pr in multibyte locales (bug #189663). + +* Mon May 15 2006 Tim Waugh 5.95-2 +- 5.95. + +* Wed Apr 26 2006 Tim Waugh 5.94-4 +- Avoid redeclared 'tee' function. +- Fix 'cp -Z' when the destination exists (bug #189967). + +* Thu Apr 20 2006 Tim Waugh 5.94-3 +- Make 'ls -Z' output more consistent with other output formats. + +* Fri Mar 24 2006 Tim Waugh 5.94-2 +- 5.94. + +* Fri Feb 10 2006 Jesse Keating - 5.93-7.2 +- bump again for double-long bug on ppc(64) + +* Tue Feb 07 2006 Jesse Keating - 5.93-7.1 +- rebuilt for new gcc4.1 snapshot and glibc changes + +* Mon Jan 23 2006 Tim Waugh +- Fixed chcon(1) bug reporting address (bug #178523). + +* Thu Jan 5 2006 Tim Waugh 5.93-7 +- Don't suppress chown/chgrp errors in install(1) (bug #176708). + +* Mon Jan 2 2006 Dan Walsh 5.93-6 +- Remove pam_selinux.so from su.pamd, not needed for targeted and Strict/MLS + will have to newrole before using. + +* Fri Dec 23 2005 Tim Waugh 5.93-5 +- Fix "sort -n" (bug #176468). + +* Fri Dec 16 2005 Tim Waugh +- Explicitly set default POSIX2 version during configure stage. + +* Fri Dec 09 2005 Jesse Keating +- rebuilt + +* Fri Dec 2 2005 Tim Waugh +- Parametrize SELinux (bug #174067). +- Fix runuser.pamd (bug #173807). + +* Thu Nov 24 2005 Tim Waugh 5.93-4 +- Rebuild to pick up new glibc *at functions. +- Apply runuser PAM patch from bug #173807. Ship runuser PAM file. + +* Tue Nov 15 2005 Dan Walsh 5.93-3 +- Remove multiple from su.pamd + +* Mon Nov 14 2005 Tim Waugh 5.93-2 +- Call setsid() in su under some circumstances (bug #173008). +- Prevent runuser operating when setuid (bug #173113). + +* Tue Nov 8 2005 Tim Waugh 5.93-1 +- 5.93. +- No longer need alt-md5sum-binary, dircolors, mkdir, mkdir2 or tac patches. + +* Fri Oct 28 2005 Tim Waugh 5.92-1 +- Finished porting i18n patch to sort.c. +- Fixed for sort-mb-tests (avoid +n syntax). + +* Fri Oct 28 2005 Tim Waugh 5.92-0.2 +- Fix chgrp basic test. +- Include md5sum patch from ALT. + +* Mon Oct 24 2005 Tim Waugh 5.92-0.1 +- 5.92. +- No longer need afs, dircolors, utmp, gcc4, brokentest, dateseconds, + chown, rmaccess, copy, stale-utmp, no-sign-extend, fchown patches. +- Updated acl, dateman, pam, langinfo, i18n, getgrouplist, selinux patches. +- Dropped printf-ll, allow_old_options, jday, zh_CN patches. +- NOTE: i18n patch not ported for sort(1) yet. + +* Fri Sep 30 2005 Tomas Mraz - 5.2.1-56 +- use include instead of pam_stack in pam config + +* Fri Sep 9 2005 Dan Walsh 5.2.1-55 +- Reverse change to use raw functions + +* Thu Sep 8 2005 Tim Waugh 5.2.1-54 +- Explicit setuid bit for /bin/su in file manifest (bug #167745). + +* Tue Sep 6 2005 Dan Walsh 5.2.1-53 +- Allow id to run even when SELinux security context can not be run +- Change chcon to use raw functions. + +* Tue Jun 28 2005 Tim Waugh +- Corrected comments in DIR_COLORS.xterm (bug #161711). + +* Wed Jun 22 2005 Tim Waugh 5.2.1-52 +- Fixed stale-utmp patch so that 'who -r' and 'who -b' work + again (bug #161264). + +* Fri Jun 17 2005 Tim Waugh 5.2.1-51 +- Use upstream hostid fix. + +* Thu Jun 16 2005 Tim Waugh 5.2.1-50 +- Don't display the sign-extended part of the host id (bug #160078). + +* Tue May 31 2005 Dan Walsh 5.2.1-49 +- Eliminate bogus "can not preserve context" message when moving files. + +* Wed May 25 2005 Tim Waugh 5.2.1-48 +- Prevent buffer overflow in who(1) (bug #158405). + +* Fri May 20 2005 Tim Waugh 5.2.1-47 +- Better error checking in the pam patch (bug #158189). + +* Mon May 16 2005 Dan Walsh 5.2.1-46 +- Fix SELinux patch to better handle MLS integration + +* Mon May 16 2005 Tim Waugh 5.2.1-45 +- Applied Russell Coker's selinux changes (bug #157856). + +* Fri Apr 8 2005 Tim Waugh +- Fixed pam patch from Steve Grubb (bug #154946). +- Use better upstream patch for "stale utmp". + +* Tue Mar 29 2005 Tim Waugh 5.2.1-44 +- Added "stale utmp" patch from upstream. + +* Thu Mar 24 2005 Tim Waugh 5.2.1-43 +- Removed patch that adds -C option to install(1). + +* Wed Mar 16 2005 Tim Waugh 5.2.1-42 +- Fixed pam patch. +- Fixed broken configure test. +- Fixed build with GCC 4 (bug #151045). + +* Wed Feb 9 2005 Tim Waugh 5.2.1-41 +- Jakub Jelinek's sort -t multibyte fixes (bug #147567). + +* Sat Feb 5 2005 Tim Waugh 5.2.1-40 +- Undo last change (bug #145266). + +* Fri Feb 4 2005 Tim Waugh 5.2.1-38 +- Special case for ia32e in uname (bug #145266). + +* Thu Jan 13 2005 Tim Waugh 5.2.1-37 +- Fixed zh_CN translation (bug #144845). Patch from Mitrophan Chin. + +* Tue Dec 28 2004 Dan Walsh 5.2.1-36 +- Fix to only setdefaultfilecon if not overridden by command line + +* Mon Dec 27 2004 Dan Walsh 5.2.1-35 +- Change install to restorecon if it can + +* Wed Dec 15 2004 Tim Waugh +- Fixed small bug in i18n patch. + +* Mon Dec 6 2004 Tim Waugh 5.2.1-34 +- Don't set fs uid until after pam_open_session (bug #77791). + +* Thu Nov 25 2004 Tim Waugh 5.2.1-33 +- Fixed colorls.csh (bug #139988). Patch from Miloslav Trmac. + +* Mon Nov 8 2004 Tim Waugh +- Updated URL (bug #138279). + +* Mon Oct 25 2004 Steve Grubb 5.2.1-32 +- Handle the return code of function calls in runcon. + +* Mon Oct 18 2004 Tim Waugh +- Prevent compiler warning in coreutils-i18n.patch (bug #136090). + +* Tue Oct 5 2004 Tim Waugh 5.2.1-31 +- getgrouplist() patch from Ulrich Drepper. +- The selinux patch should be applied last. + +* Mon Oct 4 2004 Dan Walsh 5.2.1-30 +- Mv runuser to /sbin + +* Mon Oct 4 2004 Dan Walsh 5.2.1-28 +- Fix runuser man page. + +* Mon Oct 4 2004 Tim Waugh +- Fixed build. + +* Fri Sep 24 2004 Dan Walsh 5.2.1-26 +- Add runuser as similar to su, but only runable by root + +* Fri Sep 24 2004 Tim Waugh 5.2.1-25 +- chown(1) patch from Ulrich Drepper. + +* Tue Sep 14 2004 Tim Waugh 5.2.1-24 +- SELinux patch fix: don't display '(null)' if getfilecon() fails + (bug #131196). + +* Fri Aug 20 2004 Tim Waugh 5.2.1-23 +- Fixed colorls.csh quoting (bug #102412). +- Fixed another join LSB test failure (bug #121153). + +* Mon Aug 16 2004 Tim Waugh 5.2.1-22 +- Fixed sort -t LSB test failure (bug #121154). +- Fixed join LSB test failure (bug #121153). + +* Wed Aug 11 2004 Tim Waugh 5.2.1-21 +- Apply upstream patch to fix 'cp -a' onto multiply-linked files (bug #128874). +- SELinux patch fix: don't error out if lgetfilecon() returns ENODATA. + +* Tue Aug 10 2004 Tim Waugh 5.2.1-20 +- Added 'konsole' TERM to DIR_COLORS (bug #129544). + +* Wed Aug 4 2004 Tim Waugh 5.2.1-19 +- Added 'gnome' TERM to DIR_COLORS (bug #129112). +- Worked around a bash bug #129128. +- Fixed an i18n patch bug in cut (bug #129114). + +* Tue Aug 3 2004 Tim Waugh +- Fixed colorls.{sh,csh} so that the l. and ll aliases are always defined + (bug #128948). + +* Tue Jul 13 2004 Tim Waugh 5.2.1-18 +- Fixed field extraction in sort (bug #127694). + +* Fri Jun 25 2004 Tim Waugh +- Added 'TERM screen.linux' to DIR_COLORS (bug #78816). + +* Wed Jun 23 2004 Dan Walsh 5.2.1-17 +- Move pam-xauth to after pam-selinux + +* Tue Jun 15 2004 Elliot Lee +- rebuilt + +* Mon Jun 7 2004 Tim Waugh 5.2.1-15 +- Fix ls -Z (bug #125447). + +* Fri Jun 4 2004 Tim Waugh +- Build requires bison (bug #125290). + +* Fri Jun 4 2004 Tim Waugh 5.2.1-14 +- Fix selinux patch causing problems with ls --format=... (bug #125238). + +* Thu Jun 3 2004 Dan Walsh 5.2.1-13 +- Change su to use pam_selinux open and pam_selinux close + +* Wed Jun 2 2004 Tim Waugh 5.2.1-12 +- Don't call access() on symlinks about to be removed (bug #124699). + +* Wed Jun 2 2004 Tim Waugh 5.2.1-11 +- Fix ja translation (bug #124862). + +* Tue May 18 2004 Jeremy Katz 5.2.1-10 +- rebuild + +* Mon May 17 2004 Tim Waugh 5.2.1-9 +- Mention pam in the info for su (bug #122592). +- Remove wheel group rant again (bug #122886). +- Change default behaviour for chgrp/chown (bug #123263). Patch from + upstream. + +* Mon May 17 2004 Thomas Woerner 5.2.1-8 +- compiling su PIE + +* Wed May 12 2004 Tim Waugh +- Build requires new versions of autoconf and automake (bug #123098). + +* Tue May 4 2004 Tim Waugh 5.2.1-7 +- Fix join -t (bug #122435). + +* Tue Apr 20 2004 Tim Waugh 5.2.1-6 +- Fix 'ls -Z' displaying users/groups if stat() failed (bug #121292). + +* Fri Apr 9 2004 Dan Walsh 5.2.1-5 +- Add ls -LZ fix +- Fix chcon to handle "." + +* Wed Mar 17 2004 Tim Waugh +- Apply upstream fix for non-zero seconds for --date="10:00 +0100". + +* Tue Mar 16 2004 Dan Walsh 5.2.1-3 +- If preserve fails, report as warning unless user requires preserve + +* Tue Mar 16 2004 Dan Walsh 5.2.1-2 +- Make mv default to preserve on context + +* Sat Mar 13 2004 Tim Waugh 5.2.1-1 +- 5.2.1. + +* Fri Mar 12 2004 Tim Waugh 5.2.0-9 +- Add '-Z' to 'ls --help' output (bug #118108). + +* Fri Mar 5 2004 Tim Waugh +- Fix deref-args test case for rebuilding under SELinux (bug #117556). + +* Wed Feb 25 2004 Tim Waugh 5.2.0-8 +- kill(1) offloaded to util-linux altogether. + +* Tue Feb 24 2004 Tim Waugh 5.2.0-7 +- Ship the real '[', not a symlink. + +* Mon Feb 23 2004 Tim Waugh 5.2.0-6 +- Apply Paul Eggert's chown patch (bug #116536). +- Merged chdir patch into pam patch where it belongs. + +* Mon Feb 23 2004 Tim Waugh 5.2.0-5 +- Fixed i18n patch bug causing sort -M not to work (bug #116575). + +* Sat Feb 21 2004 Tim Waugh 5.2.0-4 +- Reinstate kill binary, just not its man page (bug #116463). + +* Sat Feb 21 2004 Tim Waugh 5.2.0-3 +- Updated ls-stat patch. + +* Fri Feb 20 2004 Dan Walsh 5.2.0-2 +- fix chcon to ignore . and .. directories for recursing + +* Fri Feb 20 2004 Tim Waugh 5.2.0-1 +- Patch ls so that failed stat() is handled gracefully (Ulrich Drepper). +- 5.2.0. + +* Thu Feb 19 2004 Tim Waugh +- More AFS patch tidying. + +* Wed Feb 18 2004 Dan Walsh 5.1.3-0.2 +- fix chcon to handle -h qualifier properly, eliminate potential crash + +* Wed Feb 18 2004 Tim Waugh +- Stop 'sort -g' leaking memory (i18n patch bug #115620). +- Don't ship kill, since util-linux already does. +- Tidy AFS patch. + +* Mon Feb 16 2004 Tim Waugh 5.1.3-0.1 +- 5.1.3. +- Patches ported forward or removed. + +* Fri Feb 13 2004 Elliot Lee 5.0-40 +- rebuilt + +* Tue Jan 20 2004 Dan Walsh 5.0-39 +- Change /etc/pam.d/su to remove preservuser and add multiple + +* Tue Jan 20 2004 Dan Walsh 5.0-38 +- Change is_selinux_enabled to is_selinux_enabled > 0 + +* Tue Jan 20 2004 Dan Walsh 5.0-37 +- Add pam_selinux to pam file to allow switching of roles within selinux + +* Fri Jan 16 2004 Tim Waugh +- The textutils-2.0.17-mem.patch is no longer needed. + +* Thu Jan 15 2004 Tim Waugh 5.0-36 +- Fixed autoconf test causing builds to fail. + +* Tue Dec 9 2003 Dan Walsh 5.0-35 +- Fix copying to non xattr files + +* Thu Dec 4 2003 Tim Waugh 5.0-34.sel +- Fix column widths problems in ls. + +* Tue Dec 2 2003 Tim Waugh 5.0-33.sel +- Speed up md5sum by disabling speed-up asm. + +* Wed Nov 19 2003 Dan Walsh 5.0-32.sel +- Try again + +* Wed Nov 19 2003 Dan Walsh 5.0-31.sel +- Fix move on non SELinux kernels + +* Fri Nov 14 2003 Tim Waugh 5.0-30.sel +- Fixed useless acl dependencies (bug #106141). + +* Fri Oct 24 2003 Dan Walsh 5.0-29.sel +- Fix id -Z + +* Tue Oct 21 2003 Dan Walsh 5.0-28.sel +- Turn on SELinux +- Fix chcon error handling + +* Wed Oct 15 2003 Dan Walsh 5.0-28 +- Turn off SELinux + +* Mon Oct 13 2003 Dan Walsh 5.0-27.sel +- Turn on SELinux + +* Mon Oct 13 2003 Dan Walsh 5.0-27 +- Turn off SELinux + +* Mon Oct 13 2003 Dan Walsh 5.0-26.sel +- Turn on SELinux + +* Sun Oct 12 2003 Florian La Roche +- allow compiling without pam support + +* Fri Oct 10 2003 Tim Waugh 5.0-23 +- Make split(1) handle large files (bug #106700). + +* Thu Oct 9 2003 Dan Walsh 5.0-22 +- Turn off SELinux + +* Wed Oct 8 2003 Dan Walsh 5.0-21.sel +- Cleanup SELinux patch + +* Fri Oct 3 2003 Tim Waugh 5.0-20 +- Restrict ACL support to only those programs needing it (bug #106141). +- Fix default PATH for LSB (bug #102567). + +* Thu Sep 11 2003 Dan Walsh 5.0-19 +- Turn off SELinux + +* Wed Sep 10 2003 Dan Walsh 5.0-18.sel +- Turn on SELinux + +* Fri Sep 5 2003 Dan Walsh 5.0-17 +- Turn off SELinux + +* Tue Sep 2 2003 Dan Walsh 5.0-16.sel +- Only call getfilecon if the user requested it. +- build with selinux + +* Wed Aug 20 2003 Tim Waugh 5.0-14 +- Documentation fix (bug #102697). + +* Tue Aug 12 2003 Tim Waugh 5.0-13 +- Made su use pam again (oops). +- Fixed another i18n bug causing sort --month-sort to fail. +- Don't run dubious stty test, since it fails when backgrounded + (bug #102033). +- Re-enable make check. + +* Fri Aug 8 2003 Tim Waugh 5.0-12 +- Don't run 'make check' for this build (build environment problem). +- Another uninitialized variable in i18n (from bug #98683). + +* Wed Aug 6 2003 Dan Walsh 5.0-11 +- Internationalize runcon +- Update latest chcon from NSA + +* Wed Jul 30 2003 Tim Waugh +- Re-enable make check. + +* Wed Jul 30 2003 Tim Waugh 5.0-9 +- Don't run 'make check' for this build (build environment problem). + +* Mon Jul 28 2003 Tim Waugh 5.0-8 +- Actually use the ACL patch (bug #100519). + +* Wed Jul 16 2003 Dan Walsh 5.0-7 +- Convert to SELinux + +* Mon Jun 9 2003 Tim Waugh +- Removed samefile patch. Now the test suite passes. + +* Wed Jun 04 2003 Elliot Lee +- rebuilt + +* Wed May 28 2003 Tim Waugh 5.0-5 +- Both kon and kterm support colours (bug #83701). +- Fix 'ls -l' alignment in zh_CN locale (bug #88346). + +* Mon May 12 2003 Tim Waugh 5.0-4 +- Prevent file descriptor leakage in du (bug #90563). +- Build requires recent texinfo (bug #90439). + +* Wed Apr 30 2003 Tim Waugh 5.0-3 +- Allow obsolete options unless POSIXLY_CORRECT is set. + +* Sat Apr 12 2003 Tim Waugh +- Fold bug was introduced by i18n patch; fixed there instead. + +* Fri Apr 11 2003 Matt Wilson 5.0-2 +- fix segfault in fold (#88683) + +* Sat Apr 5 2003 Tim Waugh 5.0-1 +- 5.0. + +* Mon Mar 24 2003 Tim Waugh +- Use _smp_mflags. + +* Mon Mar 24 2003 Tim Waugh 4.5.11-2 +- Remove overwrite patch. +- No longer seem to need nolibrt, errno patches. + +* Thu Mar 20 2003 Tim Waugh +- No longer seem to need danglinglink, prompt, lug, touch_errno patches. + +* Thu Mar 20 2003 Tim Waugh 4.5.11-1 +- 4.5.11. +- Use packaged readlink. + +* Wed Mar 19 2003 Tim Waugh 4.5.10-1 +- 4.5.10. +- Update lug, touch_errno, acl, utmp, printf-ll, i18n, test-bugs patches. +- Drop fr_fix, LC_TIME, preserve, regex patches. + +* Wed Mar 12 2003 Tim Waugh 4.5.3-21 +- Fixed another i18n patch bug (bug #82032). + +* Tue Mar 11 2003 Tim Waugh 4.5.3-20 +- Fix sort(1) efficiency in multibyte encoding (bug #82032). + +* Tue Feb 18 2003 Tim Waugh 4.5.3-19 +- Ship readlink(1) (bug #84200). + +* Thu Feb 13 2003 Tim Waugh 4.5.3-18 +- Deal with glibc < 2.2 in %%pre scriplet (bug #84090). + +* Wed Feb 12 2003 Tim Waugh 4.5.3-16 +- Require glibc >= 2.2 (bug #84090). + +* Tue Feb 11 2003 Bill Nottingham 4.5.3-15 +- fix group (#84095) + +* Wed Jan 22 2003 Tim Powers 4.5.3-14 +- rebuilt + +* Thu Jan 16 2003 Tim Waugh +- Fix rm(1) man page. + +* Thu Jan 16 2003 Tim Waugh 4.5.3-13 +- Fix re_compile_pattern check. +- Fix su hang (bug #81653). + +* Tue Jan 14 2003 Tim Waugh 4.5.3-11 +- Fix memory size calculation. + +* Tue Dec 17 2002 Tim Waugh 4.5.3-10 +- Fix mv error message (bug #79809). + +* Mon Dec 16 2002 Tim Powers 4.5.3-9 +- added PreReq on grep + +* Fri Dec 13 2002 Tim Waugh +- Fix cp --preserve with multiple arguments. + +* Thu Dec 12 2002 Tim Waugh 4.5.3-8 +- Turn on colorls for screen (bug #78816). + +* Mon Dec 9 2002 Tim Waugh 4.5.3-7 +- Fix mv (bug #79283). +- Add patch27 (nogetline). + +* Sun Dec 1 2002 Tim Powers 4.5.3-6 +- use the su.pamd from sh-utils since it works properly with multilib systems + +* Fri Nov 29 2002 Tim Waugh 4.5.3-5 +- Fix test suite quoting problems. + +* Fri Nov 29 2002 Tim Waugh 4.5.3-4 +- Fix scriplets. +- Fix i18n patch so it doesn't break uniq. +- Fix several other patches to either make the test suite pass or + not run the relevant tests. +- Run 'make check'. +- Fix file list. + +* Thu Nov 28 2002 Tim Waugh 4.5.3-3 +- Adapted for Red Hat Linux. +- Self-host for help2man. +- Don't ship readlink just yet (maybe later). +- Merge patches from fileutils and sh-utils (textutils ones are already + merged it seems). +- Keep the binaries where the used to be (in particular, id and stat). + +* Sun Nov 17 2002 Stew Benedict 4.5.3-2mdk +- LI18NUX/LSB compliance (patch800) +- Installed (but unpackaged) file(s) - /usr/share/info/dir + +* Thu Oct 31 2002 Thierry Vignaud 4.5.3-1mdk +- new release +- rediff patch 180 +- merge patch 150 into 180 + +* Mon Oct 14 2002 Thierry Vignaud 4.5.2-6mdk +- move su back to /bin + +* Mon Oct 14 2002 Thierry Vignaud 4.5.2-5mdk +- patch 0 : lg locale is illegal and must be renamed lug (pablo) + +* Mon Oct 14 2002 Thierry Vignaud 4.5.2-4mdk +- fix conflict with procps + +* Mon Oct 14 2002 Thierry Vignaud 4.5.2-3mdk +- patch 105 : fix install -s + +* Mon Oct 14 2002 Thierry Vignaud 4.5.2-2mdk +- fix build +- don't chmode two times su +- build with large file support +- fix description +- various spec cleanups +- fix chroot installation +- fix missing /bin/env +- add old fileutils, sh-utils & textutils ChangeLogs + +* Fri Oct 11 2002 Thierry Vignaud 4.5.2-1mdk +- initial release (merge fileutils, sh-utils & textutils) +- obsoletes/provides: sh-utils/fileutils/textutils +- fileutils stuff go in 1xx range +- sh-utils stuff go in 7xx range +- textutils stuff go in 5xx range +- drop obsoletes patches 1, 2, 10 (somes files're gone but we didn't ship + most of them) +- rediff patches 103, 105, 111, 113, 180, 706 +- temporary disable patch 3 & 4 +- fix fileutils url