diff --git a/coreutils-8.32-DIR_COLORS.patch b/coreutils-8.32-DIR_COLORS.patch index 37ce3e6..ee037bf 100644 --- a/coreutils-8.32-DIR_COLORS.patch +++ b/coreutils-8.32-DIR_COLORS.patch @@ -1,4 +1,4 @@ -From bca11e30e8a6281a8cbddc9fb196dd86ab09c955 Mon Sep 17 00:00:00 2001 +From c7b13f5e1a7ad012c510a8bdd5a8943ab4b55833 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 @@ -9,7 +9,7 @@ Subject: [PATCH] downstream changes to default DIR_COLORS 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/DIR_COLORS b/DIR_COLORS -index 540f6cd..b4785b6 100644 +index b465771..ad42b09 100644 --- a/DIR_COLORS +++ b/DIR_COLORS @@ -1,3 +1,7 @@ @@ -30,17 +30,17 @@ index 540f6cd..b4785b6 100644 # =================================================================== # Terminal filters # =================================================================== -@@ -70,7 +77,7 @@ DOOR 01;35 # door +@@ -69,7 +76,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 # regular file that is setuid (u+s) - SETGID 30;43 # regular file that is setgid (g+s) - CAPABILITY 00 # regular file with capability (very expensive to lookup) + SETUID 37;41 # file that is setuid (u+s) + SETGID 30;43 # file that is setgid (g+s) + CAPABILITY 00 # file with capability (very expensive to lookup) diff --git a/DIR_COLORS.lightbgcolor b/DIR_COLORS.lightbgcolor -index e3b0ec3..39a0a4c 100644 +index eab6258..1627b63 100644 --- a/DIR_COLORS.lightbgcolor +++ b/DIR_COLORS.lightbgcolor @@ -1,3 +1,9 @@ @@ -63,7 +63,7 @@ index e3b0ec3..39a0a4c 100644 # =================================================================== # Terminal filters # =================================================================== -@@ -60,17 +69,17 @@ TERM xterm* +@@ -59,17 +68,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 @@ -83,18 +83,18 @@ index e3b0ec3..39a0a4c 100644 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 # regular file that is setuid (u+s) - SETGID 30;43 # regular file that is setgid (g+s) - CAPABILITY 00 # regular file with capability (very expensive to lookup) -@@ -79,7 +88,7 @@ OTHER_WRITABLE 34;42 # dir that is other-writable (o+w) and not sticky + SETUID 37;41 # file that is setuid (u+s) + SETGID 30;43 # file that is setgid (g+s) + CAPABILITY 00 # file with capability (very expensive to lookup) +@@ -78,7 +87,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 regular files with execute permission: + # This is for files with execute permission: -EXEC 01;32 +EXEC 00;32 # =================================================================== # File extension attributes -- -2.49.0 +2.34.1 diff --git a/coreutils-9.2-CVE-2024-0684.patch b/coreutils-9.2-CVE-2024-0684.patch new file mode 100644 index 0000000..64583af --- /dev/null +++ b/coreutils-9.2-CVE-2024-0684.patch @@ -0,0 +1,31 @@ +From c4c5ed8f4e9cd55a12966d4f520e3a13101637d9 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Tue, 16 Jan 2024 13:48:32 -0800 +Subject: [PATCH] split: do not shrink hold buffer +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +* src/split.c (line_bytes_split): Do not shrink hold buffer. +If it’s large for this batch it’s likely to be large for the next +batch, and for ‘split’ it’s not worth the complexity/CPU hassle to +shrink it. Do not assume hold_size can be bufsize. +--- + src/split.c | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/src/split.c b/src/split.c +index 64020c859..037960a59 100644 +--- a/src/split.c ++++ b/src/split.c +@@ -809,10 +809,7 @@ line_bytes_split (intmax_t n_bytes, char *buf, idx_t bufsize) + { + cwrite (n_out == 0, hold, n_hold); + n_out += n_hold; +- if (n_hold > bufsize) +- hold = xirealloc (hold, bufsize); + n_hold = 0; +- hold_size = bufsize; + } + + /* Output to eol if present. */ diff --git a/coreutils-9.3-cp--p-should-copy-NFSv4-acls.patch b/coreutils-9.3-cp--p-should-copy-NFSv4-acls.patch new file mode 100644 index 0000000..e1ff720 --- /dev/null +++ b/coreutils-9.3-cp--p-should-copy-NFSv4-acls.patch @@ -0,0 +1,217 @@ +commit ccda8a2aedad84d9b730fdd6b36baa9582f54bfd +Author: rpm-build +Date: Mon Sep 11 14:28:05 2023 +0200 + + coreutils-9.3-cp--p-should-copy-NFSv4-acls.patch + +Cherry picked from the following gnulib upstream commits: +* b851a965da62cd858d71b2e5a7261a211f00b297 ("file-has-acl: port to Fedora 39") +* f01d8792778b637f7464533ac019e42f58adb310 ("file-has-acl: don’t access freed storage") +* 735716931755c74f3788ac83fead717c0bb22dfe ("file-has-acl: improve port to Fedora 39") + +diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c +index b31a2ea..4cddc80 100644 +--- a/lib/file-has-acl.c ++++ b/lib/file-has-acl.c +@@ -29,7 +29,10 @@ + + #include "acl-internal.h" + +-#if USE_ACL && GETXATTR_WITH_POSIX_ACLS ++#include "minmax.h" ++ ++#if USE_ACL && HAVE_LINUX_XATTR_H && HAVE_LISTXATTR ++# include + # include + # include + # include +@@ -44,6 +47,20 @@ enum { + ACE4_IDENTIFIER_GROUP = 0x00000040 + }; + ++/* Return true if ATTR is in the set represented by the NUL-terminated ++ strings in LISTBUF, which is of size LISTSIZE. */ ++ ++static bool ++have_xattr (char const *attr, char const *listbuf, ssize_t listsize) ++{ ++ char const *blim = listbuf + listsize; ++ for (char const *b = listbuf; b < blim; b += strlen (b) + 1) ++ for (char const *a = attr; *a == *b; a++, b++) ++ if (!*a) ++ return true; ++ return false; ++} ++ + /* Return 1 if given ACL in XDR format is non-trivial, 0 if it is trivial. + -1 upon failure to determine it. Possibly change errno. Assume that + the ACL is valid, except avoid undefined behavior even if invalid. +@@ -137,37 +154,77 @@ file_has_acl (char const *name, struct stat const *sb) + if (! S_ISLNK (sb->st_mode)) + { + +-# if GETXATTR_WITH_POSIX_ACLS +- +- ssize_t ret; ++# if HAVE_LINUX_XATTR_H && HAVE_LISTXATTR + int initial_errno = errno; + +- ret = getxattr (name, XATTR_NAME_POSIX_ACL_ACCESS, NULL, 0); +- if (ret < 0 && errno == ENODATA) +- ret = 0; +- else if (ret > 0) +- return 1; +- +- if (ret == 0 && S_ISDIR (sb->st_mode)) ++ /* The max length of a trivial NFSv4 ACL is 6 words for owner, ++ 6 for group, 7 for everyone, all times 2 because there are ++ both allow and deny ACEs. There are 6 words for owner ++ because of type, flag, mask, wholen, "OWNER@"+pad and ++ similarly for group; everyone is another word to hold ++ "EVERYONE@". */ ++ typedef uint32_t trivial_NFSv4_xattr_buf[2 * (6 + 6 + 7)]; ++ ++ /* A buffer large enough to hold any trivial NFSv4 ACL, ++ and also useful as a small array of char. */ ++ union { ++ trivial_NFSv4_xattr_buf xattr; ++ char ch[sizeof (trivial_NFSv4_xattr_buf)]; ++ } stackbuf; ++ ++ char *listbuf = stackbuf.ch; ++ ssize_t listbufsize = sizeof stackbuf.ch; ++ char *heapbuf = NULL; ++ ssize_t listsize; ++ ++ /* Use listxattr first, as this means just one syscall in the ++ typical case where the file lacks an ACL. Try stackbuf ++ first, falling back on malloc if stackbuf is too small. */ ++ while ((listsize = listxattr (name, listbuf, listbufsize)) < 0 ++ && errno == ERANGE) + { +- ret = getxattr (name, XATTR_NAME_POSIX_ACL_DEFAULT, NULL, 0); +- if (ret < 0 && errno == ENODATA) +- ret = 0; +- else if (ret > 0) +- return 1; ++ free (heapbuf); ++ ssize_t newsize = listxattr (name, NULL, 0); ++ if (newsize <= 0) ++ return newsize; ++ ++ /* Grow LISTBUFSIZE to at least NEWSIZE. Grow it by a ++ nontrivial amount too, to defend against denial of ++ service by an adversary that fiddles with ACLs. */ ++ bool overflow = ckd_add (&listbufsize, listbufsize, listbufsize >> 1); ++ listbufsize = MAX (listbufsize, newsize); ++ if (overflow || SIZE_MAX < listbufsize) ++ { ++ errno = ENOMEM; ++ return -1; ++ } ++ ++ listbuf = heapbuf = malloc (listbufsize); ++ if (!listbuf) ++ return -1; + } + +- if (ret < 0) ++ /* In Fedora 39, a file can have both NFSv4 and POSIX ACLs, ++ but if it has an NFSv4 ACL that's the one that matters. ++ In earlier Fedora the two types of ACLs were mutually exclusive. ++ Attempt to work correctly on both kinds of systems. */ ++ bool nfsv4_acl ++ = 0 < listsize && have_xattr (XATTR_NAME_NFSV4_ACL, listbuf, listsize); ++ int ret ++ = (listsize <= 0 ? listsize ++ : (nfsv4_acl ++ || have_xattr (XATTR_NAME_POSIX_ACL_ACCESS, listbuf, listsize) ++ || (S_ISDIR (sb->st_mode) ++ && have_xattr (XATTR_NAME_POSIX_ACL_DEFAULT, ++ listbuf, listsize)))); ++ free (heapbuf); ++ ++ /* If there is an NFSv4 ACL, follow up with a getxattr syscall ++ to see whether the NFSv4 ACL is nontrivial. */ ++ if (nfsv4_acl) + { +- /* Check for NFSv4 ACLs. The max length of a trivial +- ACL is 6 words for owner, 6 for group, 7 for everyone, +- all times 2 because there are both allow and deny ACEs. +- There are 6 words for owner because of type, flag, mask, +- wholen, "OWNER@"+pad and similarly for group; everyone is +- another word to hold "EVERYONE@". */ +- uint32_t xattr[2 * (6 + 6 + 7)]; +- +- ret = getxattr (name, XATTR_NAME_NFSV4_ACL, xattr, sizeof xattr); ++ ret = getxattr (name, XATTR_NAME_NFSV4_ACL, ++ stackbuf.xattr, sizeof stackbuf.xattr); + if (ret < 0) + switch (errno) + { +@@ -177,7 +234,7 @@ file_has_acl (char const *name, struct stat const *sb) + else + { + /* It looks like a trivial ACL, but investigate further. */ +- ret = acl_nfs4_nontrivial (xattr, ret); ++ ret = acl_nfs4_nontrivial (stackbuf.xattr, ret); + if (ret < 0) + { + errno = EINVAL; +diff --git a/m4/acl.m4 b/m4/acl.m4 +index dc9853a..3fa0779 100644 +--- a/m4/acl.m4 ++++ b/m4/acl.m4 +@@ -177,37 +177,23 @@ AC_DEFUN([gl_ACL_GET_FILE], + AS_IF([test "$gl_cv_func_working_acl_get_file" != no], [$1], [$2]) + ]) + +-# On GNU/Linux, testing if a file has an acl can be done with the getxattr +-# syscall which doesn't require linking against additional libraries. ++# On GNU/Linux, testing if a file has an acl can be done with the ++# listxattr and getxattr syscalls, which don't require linking ++# against additional libraries. Assume this works if linux/attr.h ++# and listxattr are present. + AC_DEFUN([gl_FILE_HAS_ACL], + [ + AC_REQUIRE([gl_FUNC_ACL_ARG]) +- if test "$enable_acl" != no; then +- AC_CACHE_CHECK([for getxattr with XATTR_NAME_POSIX_ACL macros], +- [gl_cv_getxattr_with_posix_acls], +- [gl_cv_getxattr_with_posix_acls=no +- AC_LINK_IFELSE( +- [AC_LANG_PROGRAM( +- [[#include +- #include +- #include +- ]], +- [[ssize_t a = getxattr (".", XATTR_NAME_POSIX_ACL_ACCESS, 0, 0); +- ssize_t b = getxattr (".", XATTR_NAME_POSIX_ACL_DEFAULT, 0, 0); +- return a < 0 || b < 0; +- ]])], +- [gl_cv_getxattr_with_posix_acls=yes])]) +- fi +- if test "$gl_cv_getxattr_with_posix_acls" = yes; then +- FILE_HAS_ACL_LIB= +- AC_DEFINE([GETXATTR_WITH_POSIX_ACLS], 1, +- [Define to 1 if getxattr works with XATTR_NAME_POSIX_ACL_ACCESS +- and XATTR_NAME_POSIX_ACL_DEFAULT.]) +- else +- dnl Set gl_need_lib_has_acl to a nonempty value, so that any +- dnl later gl_FUNC_ACL call will set FILE_HAS_ACL_LIB=$LIB_ACL. +- gl_need_lib_has_acl=1 +- FILE_HAS_ACL_LIB=$LIB_ACL +- fi ++ AC_CHECK_HEADERS_ONCE([linux/xattr.h]) ++ AC_CHECK_FUNCS_ONCE([listxattr]) ++ FILE_HAS_ACL_LIB= ++ AS_CASE([$enable_acl,$ac_cv_header_linux_xattr_h,$ac_cv_func_listxattr], ++ [no,*,*], [], ++ [*,yes,yes], [], ++ [*], ++ [dnl Set gl_need_lib_has_acl to a nonempty value, so that any ++ dnl later gl_FUNC_ACL call will set FILE_HAS_ACL_LIB=$LIB_ACL. ++ gl_need_lib_has_acl=1 ++ FILE_HAS_ACL_LIB=$LIB_ACL]) + AC_SUBST([FILE_HAS_ACL_LIB]) + ]) diff --git a/coreutils-9.3-gnulib-strtol.patch b/coreutils-9.3-gnulib-strtol.patch new file mode 100644 index 0000000..1aac281 --- /dev/null +++ b/coreutils-9.3-gnulib-strtol.patch @@ -0,0 +1,276 @@ +From eca8f2dd212de534778c874a52ca079659e30140 Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +Date: Tue, 18 Apr 2023 17:57:42 +0200 +Subject: [PATCH] Revert "strtol, strtoll, strtoul, strtoull: Make ISO C 23 + compliant." + +This reverts commit 4e38f4a0c65d4d19883b404a84169088b84b60d2. +--- + gnulib-tests/test-strtoll.c | 62 ------------------------------------ + gnulib-tests/test-strtoull.c | 62 ------------------------------------ + lib/strtol.c | 14 ++------ + m4/strtoll.m4 | 19 +++-------- + m4/strtoull.m4 | 19 +++-------- + 5 files changed, 11 insertions(+), 165 deletions(-) + +diff --git a/gnulib-tests/test-strtoll.c b/gnulib-tests/test-strtoll.c +index 24cb4eb..ecedbe6 100644 +--- a/gnulib-tests/test-strtoll.c ++++ b/gnulib-tests/test-strtoll.c +@@ -239,67 +239,5 @@ main (void) + ASSERT (errno == 0); + } + +- /* Binary integer syntax. */ +- { +- const char input[] = "0b111010"; +- char *ptr; +- long long result; +- errno = 0; +- result = strtoll (input, &ptr, 10); +- ASSERT (result == 0LL); +- ASSERT (ptr == input + 1); +- ASSERT (errno == 0); +- } +- { +- const char input[] = "0b111010"; +- char *ptr; +- long long result; +- errno = 0; +- result = strtoll (input, &ptr, 2); +- ASSERT (result == 58LL); +- ASSERT (ptr == input + 8); +- ASSERT (errno == 0); +- } +- { +- const char input[] = "0b111010"; +- char *ptr; +- long long result; +- errno = 0; +- result = strtoll (input, &ptr, 0); +- ASSERT (result == 58LL); +- ASSERT (ptr == input + 8); +- ASSERT (errno == 0); +- } +- { +- const char input[] = "0b"; +- char *ptr; +- long long result; +- errno = 0; +- result = strtoll (input, &ptr, 10); +- ASSERT (result == 0LL); +- ASSERT (ptr == input + 1); +- ASSERT (errno == 0); +- } +- { +- const char input[] = "0b"; +- char *ptr; +- long long result; +- errno = 0; +- result = strtoll (input, &ptr, 2); +- ASSERT (result == 0LL); +- ASSERT (ptr == input + 1); +- ASSERT (errno == 0); +- } +- { +- const char input[] = "0b"; +- char *ptr; +- long long result; +- errno = 0; +- result = strtoll (input, &ptr, 0); +- ASSERT (result == 0LL); +- ASSERT (ptr == input + 1); +- ASSERT (errno == 0); +- } +- + return 0; + } +diff --git a/gnulib-tests/test-strtoull.c b/gnulib-tests/test-strtoull.c +index 7b0027f..dd6ec2a 100644 +--- a/gnulib-tests/test-strtoull.c ++++ b/gnulib-tests/test-strtoull.c +@@ -238,67 +238,5 @@ main (void) + ASSERT (errno == 0); + } + +- /* Binary integer syntax. */ +- { +- const char input[] = "0b111010"; +- char *ptr; +- unsigned long long result; +- errno = 0; +- result = strtoull (input, &ptr, 10); +- ASSERT (result == 0ULL); +- ASSERT (ptr == input + 1); +- ASSERT (errno == 0); +- } +- { +- const char input[] = "0b111010"; +- char *ptr; +- unsigned long long result; +- errno = 0; +- result = strtoull (input, &ptr, 2); +- ASSERT (result == 58ULL); +- ASSERT (ptr == input + 8); +- ASSERT (errno == 0); +- } +- { +- const char input[] = "0b111010"; +- char *ptr; +- unsigned long long result; +- errno = 0; +- result = strtoull (input, &ptr, 0); +- ASSERT (result == 58ULL); +- ASSERT (ptr == input + 8); +- ASSERT (errno == 0); +- } +- { +- const char input[] = "0b"; +- char *ptr; +- unsigned long long result; +- errno = 0; +- result = strtoull (input, &ptr, 10); +- ASSERT (result == 0ULL); +- ASSERT (ptr == input + 1); +- ASSERT (errno == 0); +- } +- { +- const char input[] = "0b"; +- char *ptr; +- unsigned long long result; +- errno = 0; +- result = strtoull (input, &ptr, 2); +- ASSERT (result == 0ULL); +- ASSERT (ptr == input + 1); +- ASSERT (errno == 0); +- } +- { +- const char input[] = "0b"; +- char *ptr; +- unsigned long long result; +- errno = 0; +- result = strtoull (input, &ptr, 0); +- ASSERT (result == 0ULL); +- ASSERT (ptr == input + 1); +- ASSERT (errno == 0); +- } +- + return 0; + } +diff --git a/lib/strtol.c b/lib/strtol.c +index b93483d..d11269b 100644 +--- a/lib/strtol.c ++++ b/lib/strtol.c +@@ -288,11 +288,6 @@ INTERNAL (strtol) (const STRING_TYPE *nptr, STRING_TYPE **endptr, + s += 2; + base = 16; + } +- else if ((base == 0 || base == 2) && TOUPPER (s[1]) == L_('B')) +- { +- s += 2; +- base = 2; +- } + else if (base == 0) + base = 8; + } +@@ -383,14 +378,11 @@ INTERNAL (strtol) (const STRING_TYPE *nptr, STRING_TYPE **endptr, + noconv: + /* We must handle a special case here: the base is 0 or 16 and the + first two characters are '0' and 'x', but the rest are no +- hexadecimal digits. Likewise when the base is 0 or 2 and the +- first two characters are '0' and 'b', but the rest are no binary +- digits. This is no error case. We return 0 and ENDPTR points to +- the 'x' or 'b'. */ ++ hexadecimal digits. This is no error case. We return 0 and ++ ENDPTR points to the 'x'. */ + if (endptr != NULL) + { +- if (save - nptr >= 2 +- && (TOUPPER (save[-1]) == L_('X') || TOUPPER (save[-1]) == L_('B')) ++ if (save - nptr >= 2 && TOUPPER (save[-1]) == L_('X') + && save[-2] == L_('0')) + *endptr = (STRING_TYPE *) &save[-1]; + else +diff --git a/m4/strtoll.m4 b/m4/strtoll.m4 +index ec09609..ede630c 100644 +--- a/m4/strtoll.m4 ++++ b/m4/strtoll.m4 +@@ -1,4 +1,4 @@ +-# strtoll.m4 serial 10 ++# strtoll.m4 serial 9 + dnl Copyright (C) 2002, 2004, 2006, 2008-2023 Free Software Foundation, Inc. + dnl This file is free software; the Free Software Foundation + dnl gives unlimited permission to copy and/or distribute it, +@@ -24,26 +24,15 @@ AC_DEFUN([gl_FUNC_STRTOLL], + if (term != input + 1) + result |= 1; + } +- /* This test fails on pre-C23 platforms. */ +- { +- const char input[] = "0b1"; +- (void) strtoll (input, &term, 2); +- if (term != input + 3) +- result |= 2; +- } + return result; + ]]) + ], + [gl_cv_func_strtoll_works=yes], + [gl_cv_func_strtoll_works=no], + [case "$host_os" in +- # Guess no on native Windows. +- mingw*) gl_cv_func_strtoll_works="guessing no" ;; +- # Guess no on glibc systems. +- *-gnu* | gnu*) gl_cv_func_strtoll_works="guessing no" ;; +- # Guess no on musl systems. +- *-musl* | midipix*) gl_cv_func_strtoll_works="guessing no" ;; +- *) gl_cv_func_strtoll_works="$gl_cross_guess_normal" ;; ++ # Guess no on native Windows. ++ mingw*) gl_cv_func_strtoll_works="guessing no" ;; ++ *) gl_cv_func_strtoll_works="$gl_cross_guess_normal" ;; + esac + ]) + ]) +diff --git a/m4/strtoull.m4 b/m4/strtoull.m4 +index 4f895c7..a9b0ddf 100644 +--- a/m4/strtoull.m4 ++++ b/m4/strtoull.m4 +@@ -1,4 +1,4 @@ +-# strtoull.m4 serial 10 ++# strtoull.m4 serial 9 + dnl Copyright (C) 2002, 2004, 2006, 2008-2023 Free Software Foundation, Inc. + dnl This file is free software; the Free Software Foundation + dnl gives unlimited permission to copy and/or distribute it, +@@ -24,26 +24,15 @@ AC_DEFUN([gl_FUNC_STRTOULL], + if (term != input + 1) + result |= 1; + } +- /* This test fails on pre-C23 platforms. */ +- { +- const char input[] = "0b1"; +- (void) strtoull (input, &term, 2); +- if (term != input + 3) +- result |= 2; +- } + return result; + ]]) + ], + [gl_cv_func_strtoull_works=yes], + [gl_cv_func_strtoull_works=no], + [case "$host_os" in +- # Guess no on native Windows. +- mingw*) gl_cv_func_strtoull_works="guessing no" ;; +- # Guess no on glibc systems. +- *-gnu* | gnu*) gl_cv_func_strtoull_works="guessing no" ;; +- # Guess no on musl systems. +- *-musl* | midipix*) gl_cv_func_strtoull_works="guessing no" ;; +- *) gl_cv_func_strtoull_works="$gl_cross_guess_normal" ;; ++ # Guess no on native Windows. ++ mingw*) gl_cv_func_strtoull_works="guessing no" ;; ++ *) gl_cv_func_strtoull_works="$gl_cross_guess_normal" ;; + esac + ]) + ]) +-- +2.39.2 + diff --git a/coreutils-9.3-reduce--iu-verbosity-with-verbose.patch b/coreutils-9.3-reduce--iu-verbosity-with-verbose.patch new file mode 100644 index 0000000..062944b --- /dev/null +++ b/coreutils-9.3-reduce--iu-verbosity-with-verbose.patch @@ -0,0 +1,141 @@ +From cc078f747f3db00e70b2ae2ad2ab34e8d54316d3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A1draig=20Brady?= +Date: Tue, 25 Apr 2023 11:07:36 +0100 +Subject: [PATCH] copy: reduce verbosity of -i and -u with --verbose + +Since skipping of files is central to the operation of -i and -u, +and with -u one may be updating few files out of many, +reinstate the verbosity of this functionality as it was before 9.3. + +* src/copy.c (copy_internal): Only output "skipped" message +with --debug. Also adjust so message never changes with --debug. +* tests/cp/cp-i.sh: Adjust accordingly. +* tests/mv/mv-n.sh: Likewise. +* tests/cp/debug.sh: Add explicit test case for message. + +Cherry-picked-from: cc078f747f3db00e70b2ae2ad2ab34e8d54316d3 +Signed-off-by: Lukáš Zaoral +--- + src/copy.c | 6 +++--- + tests/cp/cp-i.sh | 9 ++++----- + tests/cp/debug.sh | 4 ++++ + tests/mv/mv-n.sh | 9 ++++----- + 4 files changed, 15 insertions(+), 13 deletions(-) + +diff --git a/src/copy.c b/src/copy.c +index 13d93324f..0dd059d2e 100644 +--- a/src/copy.c ++++ b/src/copy.c +@@ -2433,10 +2433,10 @@ copy_internal (char const *src_name, char const *dst_name, + skip: + if (skipped) + { +- if (x->verbose) +- printf (_("skipped %s\n"), quoteaf (dst_name)); +- else if (x->interactive == I_ALWAYS_NO) ++ if (x->interactive == I_ALWAYS_NO) + error (0, 0, _("not replacing %s"), quoteaf (dst_name)); ++ else if (x->debug) ++ printf (_("skipped %s\n"), quoteaf (dst_name)); + + return_now = true; + } +diff --git a/tests/cp/cp-i.sh b/tests/cp/cp-i.sh +index 4458b2edd..a9178a99e 100755 +--- a/tests/cp/cp-i.sh ++++ b/tests/cp/cp-i.sh +@@ -29,13 +29,12 @@ echo n | returns_ 1 cp -iR a b 2>/dev/null || fail=1 + # test miscellaneous combinations of -f -i -n parameters + touch c d || framework_failure_ + echo "'c' -> 'd'" > out_copy || framework_failure_ +-echo "skipped 'd'" > out_skip || framework_failure_ + echo "cp: not replacing 'd'" > err_skip || framework_failure_ + touch out_empty || framework_failure_ + + # ask for overwrite, answer no + echo n | returns_ 1 cp -vi c d 2>/dev/null > out1 || fail=1 +-compare out1 out_skip || fail=1 ++compare out1 out_empty || fail=1 + + # ask for overwrite, answer yes + echo y | cp -vi c d 2>/dev/null > out2 || fail=1 +@@ -47,7 +46,7 @@ compare out3 out_copy || fail=1 + + # -n wins over -i + echo y | returns_ 1 cp -vin c d 2>/dev/null > out4 || fail=1 +-compare out4 out_skip || fail=1 ++compare out4 out_empty || fail=1 + + # -n wins over -i non verbose + echo y | returns_ 1 cp -in c d 2>err4 > out4 || fail=1 +@@ -60,11 +59,11 @@ compare out5 out_copy || fail=1 + + # do not ask, prevent from overwrite + echo n | returns_ 1 cp -vfn c d 2>/dev/null > out6 || fail=1 +-compare out6 out_skip || fail=1 ++compare out6 out_empty || fail=1 + + # do not ask, prevent from overwrite + echo n | returns_ 1 cp -vnf c d 2>/dev/null > out7 || fail=1 +-compare out7 out_skip || fail=1 ++compare out7 out_empty || fail=1 + + # options --backup and --no-clobber are mutually exclusive + returns_ 1 cp -bn c d 2>/dev/null || fail=1 +diff --git a/tests/cp/debug.sh b/tests/cp/debug.sh +index b46adc637..242894de2 100755 +--- a/tests/cp/debug.sh ++++ b/tests/cp/debug.sh +@@ -25,4 +25,8 @@ grep 'copy offload:.*reflink:.*sparse detection:' cp.out || fail=1 + cp --debug --attributes-only file file.cp >cp.out || fail=1 + returns_ 1 grep 'copy offload:.*reflink:.*sparse detection:' cp.out || fail=1 + ++touch file.cp || framework_failure_ ++cp --debug --update=none file file.cp >cp.out || fail=1 ++grep 'skipped' cp.out || fail=1 ++ + Exit $fail +diff --git a/tests/mv/mv-n.sh b/tests/mv/mv-n.sh +index 45d74eb93..60547807c 100755 +--- a/tests/mv/mv-n.sh ++++ b/tests/mv/mv-n.sh +@@ -23,14 +23,13 @@ print_ver_ mv + # test miscellaneous combinations of -f -i -n parameters + touch a b || framework_failure_ + echo "renamed 'a' -> 'b'" > out_move +-echo "skipped 'b'" > out_skip || framework_failure_ + echo "mv: not replacing 'b'" > err_skip || framework_failure_ + > out_empty + + # ask for overwrite, answer no + touch a b || framework_failure_ + echo n | returns_ 1 mv -vi a b 2>/dev/null > out1 || fail=1 +-compare out1 out_skip || fail=1 ++compare out1 out_empty || fail=1 + + # ask for overwrite, answer yes + touch a b || framework_failure_ +@@ -40,7 +39,7 @@ compare out2 out_move || fail=1 + # -n wins (as the last option) + touch a b || framework_failure_ + echo y | returns_ 1 mv -vin a b 2>/dev/null > out3 || fail=1 +-compare out3 out_skip || fail=1 ++compare out3 out_empty || fail=1 + + # -n wins (non verbose) + touch a b || framework_failure_ +@@ -51,12 +50,12 @@ compare err3 err_skip || fail=1 + # -n wins (as the last option) + touch a b || framework_failure_ + echo y | returns_ 1 mv -vfn a b 2>/dev/null > out4 || fail=1 +-compare out4 out_skip || fail=1 ++compare out4 out_empty || fail=1 + + # -n wins (as the last option) + touch a b || framework_failure_ + echo y | returns_ 1 mv -vifn a b 2>/dev/null > out5 || fail=1 +-compare out5 out_skip || fail=1 ++compare out5 out_empty || fail=1 + + # options --backup and --no-clobber are mutually exclusive + touch a || framework_failure_ diff --git a/coreutils-9.9-fix-cut-test-aarch64.patch b/coreutils-9.9-fix-cut-test-aarch64.patch deleted file mode 100644 index 600f87b..0000000 --- a/coreutils-9.9-fix-cut-test-aarch64.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 95044cb5eaea83d02f768feb5ab79fcf5e6ad782 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?P=C3=A1draig=20Brady?= -Date: Mon, 22 Dec 2025 17:12:48 +0000 -Subject: [PATCH] tests: avoid false failure due to ulimit on aarch64 - -* tests/cut/cut-huge-range.sh: Add an extra 1MiB headroom, -which was seen with aarch64. -Reported at https://bugzilla.redhat.com/2424302 - -Cherry-picked-by: Lukáš Zaoral -Upstream-commit: 95044cb5eaea83d02f768feb5ab79fcf5e6ad782 ---- - tests/cut/cut-huge-range.sh | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/tests/cut/cut-huge-range.sh b/tests/cut/cut-huge-range.sh -index 4bd1b129d8..98d7e8f0b9 100755 ---- a/tests/cut/cut-huge-range.sh -+++ b/tests/cut/cut-huge-range.sh -@@ -22,6 +22,7 @@ getlimits_ - - vm=$(get_min_ulimit_v_ returns_ 0 cut -b1 /dev/null) \ - || skip_ 'shell lacks ulimit, or ASAN enabled' -+vm=$(($vm+1000)) # https://bugzilla.redhat.com/2424302 - - # Ensure we can cut up to our sentinel value. - # Don't use expr to subtract one, - diff --git a/coreutils-9.9-gnulib-c23.patch b/coreutils-9.9-gnulib-c23.patch deleted file mode 100644 index 82e3899..0000000 --- a/coreutils-9.9-gnulib-c23.patch +++ /dev/null @@ -1,169 +0,0 @@ -From 891761bca1aa78336e5b18c121075b6e4696c5d4 Mon Sep 17 00:00:00 2001 -From: Paul Eggert -Date: Sun, 23 Nov 2025 00:50:40 -0800 -Subject: [PATCH] Port to C23 qualifier-generic fns like strchr -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -This ports Gnulib to strict C23 platforms that reject code -like ‘char *q = strchr (P, 'x');’ when P is a pointer to const, -because in C23 strchr is a qualifier-generic function so -strchr (P, 'x') returns char const *. -This patch does not attempt to do the following two things, -which might be useful in the future: -1. When compiling on non-C23 platforms, check user code for -portability to platforms that define qualifier-generic functions. -2. Port Gnulib to platforms that have qualifier-generic functions -not listed in the C23 standard, e.g., strchrnul. I don’t know -of any such platforms. -* lib/mbschr.c (mbschr): -* lib/memchr2.c (memchr2): -Port to C23, where functions like strchr are qualifier-generic. -* lib/c++defs.h (_GL_FUNCDECL_SYS_NAME): New macro. -* lib/c++defs.h (_GL_FUNCDECL_SYS): -* lib/stdlib.in.h (bsearch): -Use it, to prevent C23 names like strchr from acting like macros. -* lib/string.in.h (memchr, strchr, strpbrk, strrchr): -Do not #undef when GNULIB_POSIXCHECK is defined, as this could -cause conforming C23 code to fail to conform. It’s not clear why -_GL_WARN_ON_USE_CXX; perhaps it was needed but isn’t any more? -But for now, limit the removal of #undef to these four functions -where #undeffing is clearly undesirable in C23. -* lib/wchar.in.h (wmemchr): Parenthesize function name in decl, -to prevent it from acting like a macro. - -Cherry-picked-by: Lukáš Zaoral -Upstream-commit: df17f4f37ed3ca373d23ad42eae51122bdb96626 ---- - lib/c++defs.h | 12 +++++++++++- - lib/mbschr.c | 2 +- - lib/memchr2.c | 2 +- - lib/stdlib.in.h | 6 +++--- - lib/string.in.h | 4 ---- - lib/wchar.in.h | 2 +- - 6 files changed, 17 insertions(+), 11 deletions(-) - -diff --git a/lib/c++defs.h b/lib/c++defs.h -index b77979a..7384457 100644 ---- a/lib/c++defs.h -+++ b/lib/c++defs.h -@@ -127,6 +127,16 @@ - #define _GL_FUNCDECL_RPL_1(rpl_func,rettype,parameters,...) \ - _GL_EXTERN_C_FUNC __VA_ARGS__ rettype rpl_func parameters - -+/* _GL_FUNCDECL_SYS_NAME (func) expands to plain func if C++, and to -+ parenthsized func otherwise. Parenthesization is needed in C23 if -+ the function is like strchr and so is a qualifier-generic macro -+ that expands to something more complicated. */ -+#ifdef __cplusplus -+# define _GL_FUNCDECL_SYS_NAME(func) func -+#else -+# define _GL_FUNCDECL_SYS_NAME(func) (func) -+#endif -+ - /* _GL_FUNCDECL_SYS (func, rettype, parameters, [attributes]); - declares the system function, named func, with the given prototype, - consisting of return type, parameters, and attributes. -@@ -139,7 +149,7 @@ - _GL_FUNCDECL_SYS (posix_openpt, int, (int flags), _GL_ATTRIBUTE_NODISCARD); - */ - #define _GL_FUNCDECL_SYS(func,rettype,parameters,...) \ -- _GL_EXTERN_C_FUNC __VA_ARGS__ rettype func parameters -+ _GL_EXTERN_C_FUNC __VA_ARGS__ rettype _GL_FUNCDECL_SYS_NAME (func) parameters - - /* _GL_CXXALIAS_RPL (func, rettype, parameters); - declares a C++ alias called GNULIB_NAMESPACE::func -diff --git a/lib/mbschr.c b/lib/mbschr.c -index c9e14b5..6582134 100644 ---- a/lib/mbschr.c -+++ b/lib/mbschr.c -@@ -65,5 +65,5 @@ mbschr (const char *string, int c) - return NULL; - } - else -- return strchr (string, c); -+ return (char *) strchr (string, c); - } -diff --git a/lib/memchr2.c b/lib/memchr2.c -index 7493823..d7724ae 100644 ---- a/lib/memchr2.c -+++ b/lib/memchr2.c -@@ -55,7 +55,7 @@ memchr2 (void const *s, int c1_in, int c2_in, size_t n) - c2 = (unsigned char) c2_in; - - if (c1 == c2) -- return memchr (s, c1, n); -+ return (void *) memchr (s, c1, n); - - /* Handle the first few bytes by reading one byte at a time. - Do this until VOID_PTR is aligned on a longword boundary. */ -diff --git a/lib/stdlib.in.h b/lib/stdlib.in.h -index bef0aaa..fd0e1e0 100644 ---- a/lib/stdlib.in.h -+++ b/lib/stdlib.in.h -@@ -224,9 +224,9 @@ _GL_INLINE_HEADER_BEGIN - - /* Declarations for ISO C N3322. */ - #if defined __GNUC__ && __GNUC__ >= 15 && !defined __clang__ --_GL_EXTERN_C void *bsearch (const void *__key, -- const void *__base, size_t __nmemb, size_t __size, -- int (*__compare) (const void *, const void *)) -+_GL_EXTERN_C void *_GL_FUNCDECL_SYS_NAME (bsearch) -+ (const void *__key, const void *__base, size_t __nmemb, size_t __size, -+ int (*__compare) (const void *, const void *)) - _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 3) _GL_ARG_NONNULL ((5)); - _GL_EXTERN_C void qsort (void *__base, size_t __nmemb, size_t __size, - int (*__compare) (const void *, const void *)) -diff --git a/lib/string.in.h b/lib/string.in.h -index fdcdd21..8b56acf 100644 ---- a/lib/string.in.h -+++ b/lib/string.in.h -@@ -409,7 +409,6 @@ _GL_CXXALIASWARN1 (memchr, void const *, - _GL_CXXALIASWARN (memchr); - # endif - #elif defined GNULIB_POSIXCHECK --# undef memchr - /* Assume memchr is always declared. */ - _GL_WARN_ON_USE (memchr, "memchr has platform-specific bugs - " - "use gnulib module memchr for portability" ); -@@ -674,7 +673,6 @@ _GL_WARN_ON_USE (stpncpy, "stpncpy is unportable - " - #if defined GNULIB_POSIXCHECK - /* strchr() does not work with multibyte strings if the locale encoding is - GB18030 and the character to be searched is a digit. */ --# undef strchr - /* Assume strchr is always declared. */ - _GL_WARN_ON_USE_CXX (strchr, - const char *, char *, (const char *, int), -@@ -981,7 +979,6 @@ _GL_CXXALIASWARN (strpbrk); - Even in this simple case, it does not work with multibyte strings if the - locale encoding is GB18030 and one of the characters to be searched is a - digit. */ --# undef strpbrk - _GL_WARN_ON_USE_CXX (strpbrk, - const char *, char *, (const char *, const char *), - "strpbrk cannot work correctly on character strings " -@@ -1011,7 +1008,6 @@ _GL_WARN_ON_USE (strspn, "strspn cannot work correctly on character strings " - #if defined GNULIB_POSIXCHECK - /* strrchr() does not work with multibyte strings if the locale encoding is - GB18030 and the character to be searched is a digit. */ --# undef strrchr - /* Assume strrchr is always declared. */ - _GL_WARN_ON_USE_CXX (strrchr, - const char *, char *, (const char *, int), -diff --git a/lib/wchar.in.h b/lib/wchar.in.h -index ab602a2..6be4515 100644 ---- a/lib/wchar.in.h -+++ b/lib/wchar.in.h -@@ -301,7 +301,7 @@ _GL_EXTERN_C int wcsncmp (const wchar_t *__s1, const wchar_t *__s2, size_t __n) - _GL_ATTRIBUTE_NONNULL_IF_NONZERO (1, 3) - _GL_ATTRIBUTE_NONNULL_IF_NONZERO (2, 3); - # ifndef __cplusplus --_GL_EXTERN_C wchar_t *wmemchr (const wchar_t *__s, wchar_t __wc, size_t __n) -+_GL_EXTERN_C wchar_t *(wmemchr) (const wchar_t *__s, wchar_t __wc, size_t __n) - _GL_ATTRIBUTE_NONNULL_IF_NONZERO (1, 3); - # endif - _GL_EXTERN_C wchar_t *wmemset (wchar_t *__s, wchar_t __wc, size_t __n) --- -2.52.0 - diff --git a/coreutils-df-direct.patch b/coreutils-df-direct.patch index 341ee2c..2571fa4 100644 --- a/coreutils-df-direct.patch +++ b/coreutils-df-direct.patch @@ -1,4 +1,4 @@ -From 91be1a584108a6a3d96f64382bbf206c4213b3db Mon Sep 17 00:00:00 2001 +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 @@ -11,10 +11,10 @@ Subject: [PATCH] coreutils-df-direct.patch create mode 100755 tests/df/direct.sh diff --git a/doc/coreutils.texi b/doc/coreutils.texi -index b420606..0ccb368 100644 +index 5b9a597..6810c15 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi -@@ -12597,6 +12597,13 @@ some systems (notably Solaris), doing this yields more up to date results, +@@ -12074,6 +12074,13 @@ some systems (notably Solaris), 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. @@ -29,10 +29,10 @@ index b420606..0ccb368 100644 @opindex --total @cindex grand total of file system size, usage and available space diff --git a/src/df.c b/src/df.c -index 75e638c..ef9f0a7 100644 +index 48025b9..c8efa5b 100644 --- a/src/df.c +++ b/src/df.c -@@ -121,6 +121,9 @@ static bool print_type; +@@ -125,6 +125,9 @@ static bool print_type; /* If true, print a grand total at the end. */ static bool print_grand_total; @@ -42,7 +42,7 @@ index 75e638c..ef9f0a7 100644 /* Grand total data. */ static struct fs_usage grand_fsu; -@@ -248,13 +251,15 @@ enum +@@ -252,13 +255,15 @@ enum NO_SYNC_OPTION = CHAR_MAX + 1, SYNC_OPTION, TOTAL_OPTION, @@ -53,16 +53,16 @@ index 75e638c..ef9f0a7 100644 static struct option const long_options[] = { - {"all", no_argument, nullptr, 'a'}, - {"block-size", required_argument, nullptr, 'B'}, -+ {"direct", no_argument, nullptr, DIRECT_OPTION}, - {"inodes", no_argument, nullptr, 'i'}, - {"human-readable", no_argument, nullptr, 'h'}, - {"si", no_argument, nullptr, 'H'}, -@@ -571,7 +576,10 @@ get_header (void) - for (idx_t col = 0; col < ncolumns; col++) + {"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'}, +@@ -583,7 +588,10 @@ get_header (void) + for (col = 0; col < ncolumns; col++) { - char *cell; + char *cell = NULL; - char const *header = _(columns[col]->caption); + char const *header = (columns[col]->field == TARGET_FIELD + && direct_statfs)? @@ -71,7 +71,7 @@ index 75e638c..ef9f0a7 100644 if (columns[col]->field == SIZE_FIELD && (header_mode == DEFAULT_MODE -@@ -1446,6 +1454,17 @@ get_point (char const *point, const struct stat *statp) +@@ -1486,6 +1494,17 @@ get_point (char const *point, const struct stat *statp) static void get_entry (char const *name, struct stat const *statp) { @@ -79,17 +79,17 @@ index 75e638c..ef9f0a7 100644 + { + char *resolved = canonicalize_file_name (name); + if (resolved) -+ { -+ get_dev (NULL, resolved, name, NULL, NULL, false, false, NULL, false); -+ free (resolved); -+ return; -+ } ++ { ++ 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_device (name)) return; -@@ -1516,6 +1535,7 @@ or all file systems by default.\n\ +@@ -1556,6 +1575,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\ @@ -97,7 +97,7 @@ index 75e638c..ef9f0a7 100644 -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); -@@ -1610,6 +1630,9 @@ main (int argc, char **argv) +@@ -1646,6 +1666,9 @@ main (int argc, char **argv) xstrtol_fatal (e, oi, c, long_options, optarg); } break; @@ -107,14 +107,14 @@ index 75e638c..ef9f0a7 100644 case 'i': if (header_mode == OUTPUT_MODE) { -@@ -1706,6 +1729,13 @@ main (int argc, char **argv) +@@ -1742,6 +1765,13 @@ main (int argc, char **argv) } } + if (direct_statfs && show_local_fs) + { + error (0, 0, _("options --direct and --local (-l) are mutually " -+ "exclusive")); ++ "exclusive")); + usage (EXIT_FAILURE); + } + @@ -183,5 +183,5 @@ index 0000000..8e4cfb8 + +Exit $fail -- -2.52.0 +2.31.1 diff --git a/coreutils-i18n.patch b/coreutils-i18n.patch index 83579e9..ba7d44d 100644 --- a/coreutils-i18n.patch +++ b/coreutils-i18n.patch @@ -1,80 +1,81 @@ -From a81b096084524e9aeef5e8b81fc829eb9efec581 Mon Sep 17 00:00:00 2001 +From 0c6ef7372e7e480b7977ee22fb108fe114060673 Mon Sep 17 00:00:00 2001 From: rpm-build -Date: Wed, 30 Aug 2023 17:19:58 +0200 +Date: Fri, 27 Sep 2024 02:00:00 +0200 Subject: [PATCH] coreutils-i18n.patch --- - bootstrap.conf | 2 + - configure.ac | 6 + + bootstrap.conf | 1 + + configure.ac | 2 + lib/linebuffer.h | 8 + - lib/mbfile.c | 20 + - lib/mbfile.h | 283 +++++++++++++ - m4/mbfile.m4 | 16 + + lib/mbfile.c | 3 + + lib/mbfile.h | 255 ++++++++++++ + m4/mbfile.m4 | 14 + src/cut.c | 508 +++++++++++++++++++++-- src/expand-common.c | 114 ++++++ src/expand-common.h | 12 + src/expand.c | 90 +++- + src/fold.c | 311 ++++++++++++-- + src/join.c | 359 ++++++++++++++-- src/local.mk | 4 +- src/pr.c | 443 ++++++++++++++++++-- - src/sort.c | 791 +++++++++++++++++++++++++++++++++--- - src/unexpand.c | 101 ++++- + src/sort.c | 792 +++++++++++++++++++++++++++++++++--- + src/unexpand.c | 102 ++++- + src/uniq.c | 119 +++++- tests/Coreutils.pm | 3 + tests/expand/mb.sh | 183 +++++++++ tests/i18n/sort.sh | 29 ++ tests/local.mk | 4 + 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 +++ - tests/sort/sort-merge.pl | 42 ++ - tests/sort/sort.pl | 40 +- tests/unexpand/mb.sh | 172 ++++++++ - 25 files changed, 2879 insertions(+), 167 deletions(-) + 31 files changed, 3698 insertions(+), 242 deletions(-) create mode 100644 lib/mbfile.c create mode 100644 lib/mbfile.h create mode 100644 m4/mbfile.m4 - create mode 100644 tests/expand/mb.sh - create mode 100644 tests/i18n/sort.sh - create mode 100644 tests/misc/sort-mb-tests.sh - create mode 100644 tests/unexpand/mb.sh + create mode 100755 tests/expand/mb.sh + create mode 100755 tests/i18n/sort.sh + create mode 100755 tests/misc/sort-mb-tests.sh + create mode 100755 tests/unexpand/mb.sh diff --git a/bootstrap.conf b/bootstrap.conf -index ec68ac8..ec2fbbe 100644 +index eca4edb..71fb8b8 100644 --- a/bootstrap.conf +++ b/bootstrap.conf -@@ -171,6 +171,8 @@ gnulib_modules=" +@@ -165,6 +165,7 @@ gnulib_modules=" + maintainer-makefile malloc-gnu manywarnings - mbbuf -+ mbchar + mbfile mbrlen - mbrtoc32 mbrtowc + mbsalign diff --git a/configure.ac b/configure.ac -index 5e99ef3..ac07577 100644 +index 2b2f946..2793720 100644 --- a/configure.ac +++ b/configure.ac -@@ -465,6 +465,12 @@ fi +@@ -477,6 +477,8 @@ fi # I'm leaving it here for now. This whole thing needs to be modernized... gl_WINSIZE_IN_PTEM +gl_MBFILE -+dnl Do not use gl_MODULE_INDICATOR([mbfile]) here: we don't want 'struct mbchar' -+dnl to have a different size in lib/ than in tests/. -+AC_DEFINE([GNULIB_MBFILE], [1], -+ [Define to 1 if the gnulib module 'mbfile' is in use.]) + gl_HEADER_TIOCGWINSZ_IN_TERMIOS_H if test $gl_cv_sys_tiocgwinsz_needs_termios_h = no && \ diff --git a/lib/linebuffer.h b/lib/linebuffer.h -index ca56f80..509b7e6 100644 +index b4cc8e4..f2bbb52 100644 --- a/lib/linebuffer.h +++ b/lib/linebuffer.h -@@ -27,6 +27,11 @@ extern "C" { - #endif - +@@ -22,6 +22,11 @@ + # include "idx.h" + # include +/* Get mbstate_t. */ +# if HAVE_WCHAR_H @@ -84,7 +85,7 @@ index ca56f80..509b7e6 100644 /* A 'struct linebuffer' holds a line of text. */ struct linebuffer -@@ -34,6 +39,9 @@ struct linebuffer +@@ -29,6 +34,9 @@ struct linebuffer idx_t size; /* Allocated. */ idx_t length; /* Used. */ char *buffer; @@ -96,51 +97,34 @@ index ca56f80..509b7e6 100644 /* Initialize linebuffer LINEBUFFER for use. */ diff --git a/lib/mbfile.c b/lib/mbfile.c new file mode 100644 -index 0000000..f4e3e77 +index 0000000..b0a468e --- /dev/null +++ b/lib/mbfile.c -@@ -0,0 +1,20 @@ -+/* Multibyte character I/O: macros for multi-byte encodings. -+ Copyright (C) 2012-2025 Free Software Foundation, Inc. -+ -+ This file is free software: you can redistribute it and/or modify -+ it under the terms of the GNU Lesser General Public License as -+ published by the Free Software Foundation, either version 3 of the -+ License, or (at your option) any later version. -+ -+ This file 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 Lesser General Public License for more details. -+ -+ You should have received a copy of the GNU Lesser General Public License -+ along with this program. If not, see . */ -+ +@@ -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..c852f31 +index 0000000..11f1b12 --- /dev/null +++ b/lib/mbfile.h -@@ -0,0 +1,283 @@ +@@ -0,0 +1,255 @@ +/* Multibyte character I/O: macros for multi-byte encodings. -+ Copyright (C) 2001, 2005, 2009-2025 Free Software Foundation, Inc. ++ Copyright (C) 2001, 2005, 2009-2015 Free Software Foundation, Inc. + -+ This file is free software: you can redistribute it and/or modify -+ it under the terms of the GNU Lesser 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 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 file is distributed in the hope that it will be useful, ++ 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 Lesser General Public License for more details. ++ GNU General Public License for more details. + -+ You should have received a copy of the GNU Lesser General Public License -+ along with this program. If not, see . */ ++ 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 . */ @@ -175,125 +159,115 @@ index 0000000..c852f31 +#ifndef _MBFILE_H +#define _MBFILE_H 1 + -+/* This file uses _GL_INLINE_HEADER_BEGIN, _GL_INLINE. */ -+#if !_GL_CONFIG_H_INCLUDED -+ #error "Please include config.h first." -+#endif -+ +#include ++#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 + -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+ -+/* Guarantee two characters of pushback. */ -+#define MBFILE_MAX_PUSHBACK 2 -+ +struct mbfile_multi { + FILE *fp; + bool eof_seen; -+ unsigned int pushback_count; /* <= MBFILE_MAX_PUSHBACK */ ++ bool have_pushback; + mbstate_t state; + unsigned int bufcount; + char buf[MBCHAR_BUF_SIZE]; -+ struct mbchar pushback[MBFILE_MAX_PUSHBACK]; ++ struct mbchar pushback; +}; + +MBFILE_INLINE void +mbfile_multi_getc (struct mbchar *mbc, struct mbfile_multi *mbf) +{ -+ unsigned int new_bufcount; + size_t bytes; + -+ /* Return character pushed back, if there is one. */ -+ if (mbf->pushback_count > 0) -+ { -+ mb_copy (mbc, &mbf->pushback[mbf->pushback_count - 1]); -+ mbf->pushback_count--; -+ return; -+ } -+ + /* 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; + -+ new_bufcount = mbf->bufcount; -+ -+ /* If mbf->state is not in an initial state, some more 32-bit wide character -+ may be hiding in the state. We need to call mbrtoc32 again. */ -+ #if GNULIB_MBRTOC32_REGULAR -+ assert (mbsinit (&mbf->state)); -+ #else -+ if (mbsinit (&mbf->state)) -+ #endif ++ /* Return character pushed back, if there is one. */ ++ if (mbf->have_pushback) + { -+ /* Before using mbrtoc32, we need at least one byte. */ -+ if (new_bufcount == 0) -+ { -+ int c = getc (mbf->fp); -+ if (c == EOF) -+ { -+ mbf->eof_seen = true; -+ goto eof; -+ } -+ mbf->buf[0] = (unsigned char) c; -+ new_bufcount++; -+ } -+ -+ /* Handle most ASCII characters quickly, without calling mbrtoc32(). */ -+ if (new_bufcount == 1 && is_basic (mbf->buf[0])) -+ { -+ /* These characters are part of the POSIX portable character set. -+ For most of them, namely those in the ISO C basic character set, -+ ISO C 99 guarantees that their wide character code is identical to -+ their char code. For the few other ones, this is the case as well, -+ in all locale encodings that are in use. The 32-bit wide character -+ code is the same as well. */ -+ mbc->wc = mbc->buf[0] = mbf->buf[0]; -+ mbc->wc_valid = true; -+ mbc->ptr = &mbc->buf[0]; -+ mbc->bytes = 1; -+ mbf->bufcount = 0; -+ return; -+ } ++ mb_copy (mbc, &mbf->pushback); ++ mbf->have_pushback = false; ++ return; + } + -+ /* Use mbrtoc32 on an increasing number of bytes. Read only as many bytes ++ /* 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 (;;) + { -+ /* Feed the bytes one by one into mbrtoc32. */ -+ bytes = mbrtoc32 (&mbc->wc, &mbf->buf[mbf->bufcount], new_bufcount - mbf->bufcount, &mbf->state); ++ /* 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. */ -+ mbf->bufcount = new_bufcount; + /* Return a single byte. */ + bytes = 1; + mbc->wc_valid = false; -+ /* Allow the next invocation to continue from a sane state. */ -+ mbszero (&mbf->state); + break; + } + else if (bytes == (size_t) -2) + { + /* An incomplete multibyte character. */ -+ mbf->bufcount = new_bufcount; ++ mbf->state = backup_state; + if (mbf->bufcount == MBCHAR_BUF_SIZE) + { + /* An overlong incomplete multibyte sequence was encountered. */ @@ -304,42 +278,28 @@ index 0000000..c852f31 + } + else + { -+ /* Read one more byte and retry mbrtoc32. */ ++ /* 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 = new_bufcount; ++ bytes = mbf->bufcount; + mbc->wc_valid = false; + break; + } -+ mbf->buf[new_bufcount] = (unsigned char) c; -+ new_bufcount++; ++ mbf->buf[mbf->bufcount] = (unsigned char) c; ++ mbf->bufcount++; + } + } + else + { -+ #if !GNULIB_MBRTOC32_REGULAR -+ if (bytes == (size_t) -3) ++ if (bytes == 0) + { -+ /* The previous multibyte sequence produced an additional 32-bit -+ wide character. */ -+ mbf->bufcount = new_bufcount; -+ bytes = 0; -+ } -+ else -+ #endif -+ { -+ bytes = mbf->bufcount + bytes; -+ mbf->bufcount = new_bufcount; -+ if (bytes == 0) -+ { -+ /* A null 32-bit wide character was encountered. */ -+ bytes = 1; -+ assert (mbf->buf[0] == '\0'); -+ assert (mbc->wc == 0); -+ } ++ /* A null wide character was encountered. */ ++ bytes = 1; ++ assert (mbf->buf[0] == '\0'); ++ assert (mbc->wc == 0); + } + mbc->wc_valid = true; + break; @@ -378,10 +338,8 @@ index 0000000..c852f31 +MBFILE_INLINE void +mbfile_multi_ungetc (const struct mbchar *mbc, struct mbfile_multi *mbf) +{ -+ if (mbf->pushback_count == MBFILE_MAX_PUSHBACK) -+ abort (); -+ mb_copy (&mbf->pushback[mbf->pushback_count], mbc); -+ mbf->pushback_count++; ++ mb_copy (&mbf->pushback, mbc); ++ mbf->have_pushback = true; +} + +typedef struct mbfile_multi mb_file_t; @@ -391,8 +349,8 @@ index 0000000..c852f31 +#define mbf_init(mbf, stream) \ + ((mbf).fp = (stream), \ + (mbf).eof_seen = false, \ -+ (mbf).pushback_count = 0, \ -+ mbszero (&(mbf).state), \ ++ (mbf).have_pushback = false, \ ++ memset (&(mbf).state, '\0', sizeof (mbstate_t)), \ + (mbf).bufcount = 0) + +#define mbf_getc(mbc, mbf) mbfile_multi_getc (&(mbc), &(mbf)) @@ -401,27 +359,23 @@ index 0000000..c852f31 + +#define mb_iseof(mbc) ((mbc).bytes == 0) + -+ -+#ifdef __cplusplus -+} ++#ifndef _GL_INLINE_HEADER_BEGIN ++ #error "Please include config.h first." +#endif -+ -+_GL_INLINE_HEADER_END ++_GL_INLINE_HEADER_BEGIN + +#endif /* _MBFILE_H */ diff --git a/m4/mbfile.m4 b/m4/mbfile.m4 new file mode 100644 -index 0000000..1d126e0 +index 0000000..8589902 --- /dev/null +++ b/m4/mbfile.m4 -@@ -0,0 +1,16 @@ -+# mbfile.m4 -+# serial 7 -+dnl Copyright (C) 2005, 2008-2025 Free Software Foundation, Inc. +@@ -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 This file is offered as-is, without any warranty. + +dnl autoconf tests required for use of mbfile.h +dnl From Bruno Haible. @@ -432,11 +386,11 @@ index 0000000..1d126e0 + : +]) diff --git a/src/cut.c b/src/cut.c -index f0effb9..36479d6 100644 +index e8346b7..5a341a7 100644 --- a/src/cut.c +++ b/src/cut.c -@@ -27,6 +27,11 @@ - #include +@@ -28,6 +28,11 @@ + #include #include #include + @@ -446,8 +400,8 @@ index f0effb9..36479d6 100644 +#endif #include "system.h" - #include "assure.h" -@@ -35,6 +40,18 @@ + #include "error.h" +@@ -36,6 +41,18 @@ #include "set-fields.h" @@ -466,7 +420,7 @@ index f0effb9..36479d6 100644 /* The official name of this program (e.g., no 'g' prefix). */ #define PROGRAM_NAME "cut" -@@ -51,6 +68,52 @@ +@@ -52,6 +69,52 @@ } \ while (0) @@ -519,8 +473,8 @@ index f0effb9..36479d6 100644 /* 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 -@@ -58,6 +121,9 @@ - CURRENT_RP.HI then we make CURRENT_RP to point to the next range pair. */ +@@ -59,6 +122,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. */ @@ -529,7 +483,7 @@ index f0effb9..36479d6 100644 /* 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 -@@ -70,6 +136,29 @@ static char *field_1_buffer; +@@ -71,6 +137,29 @@ static char *field_1_buffer; /* The number of bytes allocated for FIELD_1_BUFFER. */ static size_t field_1_bufsize; @@ -556,18 +510,18 @@ index f0effb9..36479d6 100644 + if this program runs on multibyte locale. */ +static int force_singlebyte_mode; + - /* If true, do not output lines containing no delimiter characters. + /* If true do not output lines containing no delimiter characters. Otherwise, all such lines are printed. This option is valid only with field mode. */ -@@ -81,10 +170,16 @@ static bool complement; +@@ -82,10 +171,16 @@ static bool complement; - /* The delimiter character for field mode. */ + /* 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. */ + /* The delimiter for each line/record. */ static unsigned char line_delim = '\n'; +/* True if the --output-delimiter=STRING option was specified. */ @@ -576,17 +530,17 @@ index f0effb9..36479d6 100644 /* The length of output_delimiter_string. */ static size_t output_delimiter_length; -@@ -92,9 +187,6 @@ static size_t output_delimiter_length; +@@ -93,9 +188,6 @@ static size_t output_delimiter_length; string consisting of the input delimiter. */ static char *output_delimiter_string; -/* The output delimiter string contents, if the default. */ -static char output_delimiter_default[1]; - - /* True if we have ever read standard input. */ + /* True if we have ever read standard input. */ static bool have_read_stdin; -@@ -148,7 +240,7 @@ Print selected parts of lines from each FILE to standard output.\n\ +@@ -149,7 +241,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\ @@ -595,7 +549,7 @@ index f0effb9..36479d6 100644 "), stdout); fputs (_("\ --complement complement the set of selected bytes, characters\n\ -@@ -252,7 +344,7 @@ cut_bytes (FILE *stream) +@@ -249,7 +341,7 @@ cut_bytes (FILE *stream) next_item (&byte_idx); if (print_kth (byte_idx)) { @@ -604,7 +558,7 @@ index f0effb9..36479d6 100644 { if (print_delimiter && is_range_start_index (byte_idx)) { -@@ -271,6 +363,82 @@ cut_bytes (FILE *stream) +@@ -265,6 +357,82 @@ cut_bytes (FILE *stream) } } @@ -687,7 +641,7 @@ index f0effb9..36479d6 100644 /* Read from stream STREAM, printing to standard output any selected fields. */ static void -@@ -433,11 +601,218 @@ cut_fields (FILE *stream) +@@ -410,11 +578,218 @@ cut_fields (FILE *stream) } } @@ -908,18 +862,18 @@ index f0effb9..36479d6 100644 { FILE *stream; -@@ -482,8 +857,8 @@ main (int argc, char **argv) +@@ -458,8 +833,8 @@ main (int argc, char **argv) int optc; bool ok; bool delim_specified = false; - bool byte_mode = false; -- char *spec_list_string = nullptr; -+ char *spec_list_string IF_LINT ( = nullptr); +- char *spec_list_string = NULL; ++ char *spec_list_string IF_LINT ( = NULL); + char mbdelim[MB_LEN_MAX + 1]; initialize_main (&argc, &argv); set_program_name (argv[0]); -@@ -493,6 +868,8 @@ main (int argc, char **argv) +@@ -469,6 +844,8 @@ main (int argc, char **argv) atexit (close_stdout); @@ -928,12 +882,12 @@ index f0effb9..36479d6 100644 /* By default, all non-delimited lines are printed. */ suppress_non_delimited = false; -@@ -505,35 +882,77 @@ main (int argc, char **argv) +@@ -480,35 +857,77 @@ main (int argc, char **argv) switch (optc) { case 'b': - case 'c': - /* Build the byte list. */ + /* Build the byte list. */ - byte_mode = true; - FALLTHROUGH; + if (operating_mode != undefined_mode) @@ -943,7 +897,7 @@ index f0effb9..36479d6 100644 + break; + + case 'c': -+ /* Build the character list. */ ++ /* Build the character list. */ + if (operating_mode != undefined_mode) + FATAL_ERROR (_("only one type of list may be specified")); + operating_mode = character_mode; @@ -951,7 +905,7 @@ index f0effb9..36479d6 100644 + break; + case 'f': - /* Build the field list. */ + /* Build the field list. */ - if (spec_list_string) - FATAL_ERROR (_("only one list may be specified")); + if (operating_mode != undefined_mode) @@ -961,7 +915,7 @@ index f0effb9..36479d6 100644 break; case 'd': - /* New delimiter. */ + /* 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")); @@ -1016,7 +970,7 @@ index f0effb9..36479d6 100644 break; case 's': -@@ -555,40 +974,57 @@ main (int argc, char **argv) +@@ -532,40 +951,57 @@ main (int argc, char **argv) } } @@ -1057,7 +1011,7 @@ index f0effb9..36479d6 100644 +#endif + } - if (output_delimiter_string == nullptr) + if (output_delimiter_string == NULL) { - output_delimiter_default[0] = delim; - output_delimiter_string = output_delimiter_default; @@ -1092,18 +1046,18 @@ index f0effb9..36479d6 100644 if (have_read_stdin && fclose (stdin) == EOF) diff --git a/src/expand-common.c b/src/expand-common.c -index 14dd804..0d8eaaa 100644 +index 4969e2e..dd47440 100644 --- a/src/expand-common.c +++ b/src/expand-common.c @@ -19,6 +19,7 @@ - #include + #include #include #include +#include #include "system.h" - #include "c-ctype.h" - #include "fadvise.h" -@@ -132,6 +133,119 @@ set_increment_size (colno tabval) + #include "die.h" + #include "error.h" +@@ -125,6 +126,119 @@ set_increment_size (uintmax_t tabval) return ok; } @@ -1224,10 +1178,10 @@ index 14dd804..0d8eaaa 100644 to the list of tab stops. */ extern void diff --git a/src/expand-common.h b/src/expand-common.h -index 46ef4e3..e19469b 100644 +index 33976ad..a45c600 100644 --- a/src/expand-common.h +++ b/src/expand-common.h -@@ -29,6 +29,18 @@ extern idx_t max_column_width; +@@ -25,6 +25,18 @@ extern size_t max_column_width; /* The desired exit status. */ extern int exit_status; @@ -1245,12 +1199,12 @@ index 46ef4e3..e19469b 100644 + /* Add tab stop TABVAL to the end of 'tab_list'. */ extern void - add_tab_stop (colno tabval); + add_tab_stop (uintmax_t tabval); diff --git a/src/expand.c b/src/expand.c -index 5ec7ce9..65ac315 100644 +index d682a86..98ba345 100644 --- a/src/expand.c +++ b/src/expand.c -@@ -38,6 +38,9 @@ +@@ -37,6 +37,9 @@ #include #include #include @@ -1258,12 +1212,12 @@ index 5ec7ce9..65ac315 100644 +#include + #include "system.h" - #include "expand-common.h" + #include "die.h" -@@ -96,19 +99,41 @@ expand (void) +@@ -97,19 +100,41 @@ expand (void) { /* Input stream. */ - FILE *fp = next_file (nullptr); + FILE *fp = next_file (NULL); + mb_file_t mbf; + mbf_char_t c; + /* True if the starting locale is utf8. */ @@ -1306,9 +1260,9 @@ index 5ec7ce9..65ac315 100644 /* The following variables have valid values only when CONVERT is true: */ -@@ -118,17 +143,48 @@ expand (void) +@@ -119,17 +144,48 @@ expand (void) /* Index in TAB_LIST of next tab stop to examine. */ - idx_t tab_index = 0; + size_t tab_index = 0; - /* Convert a line of text. */ @@ -1358,10 +1312,10 @@ index 5ec7ce9..65ac315 100644 + if (mb_iseq (c, '\t')) { /* Column the next input tab stop is on. */ - bool last_tab; -@@ -139,31 +195,33 @@ expand (void) + uintmax_t next_tab_column; +@@ -148,32 +204,34 @@ expand (void) if (putchar (' ') < 0) - write_error (); + die (EXIT_FAILURE, errno, _("write error")); - c = ' '; + mb_setascii (&c, ' '); @@ -1378,13 +1332,14 @@ index 5ec7ce9..65ac315 100644 + /* A leading control character could make us trip over. */ + else if (!mb_iscntrl (c)) { -- if (ckd_add (&column, column, 1)) -+ if (ckd_add (&column, column, mb_width (c))) - error (EXIT_FAILURE, 0, _("input line is too long")); +- 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); ++ convert &= convert_entire_line || mb_isblank (c); } - if (c < 0) @@ -1394,34 +1349,932 @@ index 5ec7ce9..65ac315 100644 - if (putchar (c) < 0) + mb_putc (c, stdout); + if (ferror (stdout)) - write_error (); + die (EXIT_FAILURE, errno, _("write error")); } - while (c != '\n'); + while (!mb_iseq (c, '\n')); } } +diff --git a/src/fold.c b/src/fold.c +index 0ee39c3..ff783b1 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,13 +252,224 @@ fold_file (char const *filename, size_t width) + line_out[offset_out++] = c; + } + +- saved_errno = errno; ++ *saved_errno = errno; + if (!ferror (istream)) +- saved_errno = 0; ++ *saved_errno = 0; + + 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 (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 */ ++ { ++ switch (wc) ++ { ++ 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 (!ferror (istream)) ++ *saved_errno = 0; ++ ++ 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 false; ++ } ++ ++ /* 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 (STREQ (filename, "-")) + clearerr (istream); + else if (fclose (istream) != 0 && !saved_errno) +@@ -252,7 +500,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 +510,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 7965a69..9e79c28 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 +@@ -280,13 +296,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)) +@@ -310,6 +327,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) + { +@@ -331,56 +489,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) - (len1 < len2); ++ return len[0] - len[1]; + } + + /* Check that successive input lines PREV and CURRENT from input file +@@ -472,6 +707,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]) +@@ -567,21 +807,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); + } + } +@@ -592,7 +839,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; + +@@ -626,7 +872,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); + } +@@ -1102,20 +1348,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/local.mk b/src/local.mk -index a8ad6b4..b0e61ec 100644 +index 13eeea8..06e6687 100644 --- a/src/local.mk +++ b/src/local.mk -@@ -490,8 +490,8 @@ src_base32_CPPFLAGS = -DBASE_TYPE=32 $(AM_CPPFLAGS) +@@ -438,8 +438,8 @@ src_base32_CPPFLAGS = -DBASE_TYPE=32 $(AM_CPPFLAGS) src_basenc_SOURCES = src/basenc.c src_basenc_CPPFLAGS = -DBASE_TYPE=42 $(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 lib/mbchar.c -+src_unexpand_SOURCES = src/unexpand.c src/expand-common.c lib/mbfile.c lib/mbchar.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 src_wc_SOURCES = src/wc.c - if USE_AVX512_WC_LINECOUNT + if USE_AVX2_WC_LINECOUNT diff --git a/src/pr.c b/src/pr.c -index 10b8c52..079c86c 100644 +index 2c5cdce..5694488 100644 --- a/src/pr.c +++ b/src/pr.c -@@ -312,6 +312,24 @@ - #include +@@ -311,6 +311,24 @@ + #include #include + @@ -1443,8 +2296,8 @@ index 10b8c52..079c86c 100644 +#endif + #include "system.h" - #include "c-ctype.h" - #include "fadvise.h" + #include "die.h" + #include "error.h" @@ -325,6 +343,18 @@ #include "xstrtol-error.h" #include "xdectoint.h" @@ -1486,15 +2339,15 @@ index 10b8c52..079c86c 100644 static bool read_line (COLUMN *p); static bool print_page (void); static bool print_stored (COLUMN *p); -@@ -428,6 +471,7 @@ static void pad_across_to (int position); - static void add_line_number (COLUMN *p); - static int getoptnum (char const *n_str, int min, char const *errfmt); +@@ -429,6 +472,7 @@ static void add_line_number (COLUMN *p); + static void getoptnum (char const *n_str, int min, int *num, + char const *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); -@@ -441,7 +485,6 @@ static void store_char (char c); +@@ -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); @@ -1502,7 +2355,7 @@ index 10b8c52..079c86c 100644 static void cleanup (void); static void print_sep_string (void); static void separator_string (char const *optarg_S); -@@ -453,7 +496,7 @@ static COLUMN *column_vector; +@@ -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]. */ @@ -1511,7 +2364,7 @@ index 10b8c52..079c86c 100644 /* Index of the position in buff where the next character will be stored. */ -@@ -557,7 +600,7 @@ static int chars_per_column; +@@ -558,7 +601,7 @@ static int chars_per_column; static bool untabify_input = false; /* (-e) The input tab character. */ @@ -1520,7 +2373,7 @@ index 10b8c52..079c86c 100644 /* (-e) Tabstops are at chars_per_tab, 2*chars_per_tab, 3*chars_per_tab, ... where the leftmost column is 1. */ -@@ -567,7 +610,10 @@ static int chars_per_input_tab = 8; +@@ -568,7 +611,10 @@ static int chars_per_input_tab = 8; static bool tabify_output = false; /* (-i) The output tab character. */ @@ -1532,7 +2385,7 @@ index 10b8c52..079c86c 100644 /* (-i) The width of the output tab. */ static int chars_per_output_tab = 8; -@@ -637,7 +683,13 @@ static int line_number; +@@ -638,7 +684,13 @@ static int line_number; static bool numbered_lines = false; /* (-n) Character which follows each line number. */ @@ -1547,7 +2400,7 @@ index 10b8c52..079c86c 100644 /* (-n) line counting starts with 1st line of input file (not with 1st line of 1st page printed). */ -@@ -690,6 +742,7 @@ static bool use_col_separator = false; +@@ -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; @@ -1555,7 +2408,7 @@ index 10b8c52..079c86c 100644 static char *column_separator = (char *) " "; static char *line_separator = (char *) "\t"; -@@ -852,6 +905,13 @@ separator_string (char const *optarg_S) +@@ -853,6 +906,13 @@ separator_string (char const *optarg_S) integer_overflow (); col_sep_length = len; col_sep_string = optarg_S; @@ -1569,7 +2422,7 @@ index 10b8c52..079c86c 100644 } int -@@ -876,6 +936,21 @@ main (int argc, char **argv) +@@ -877,6 +937,21 @@ main (int argc, char **argv) atexit (close_stdout); @@ -1591,7 +2444,7 @@ index 10b8c52..079c86c 100644 n_files = 0; file_names = (argc > 1 ? xnmalloc (argc - 1, sizeof (char *)) -@@ -952,8 +1027,12 @@ main (int argc, char **argv) +@@ -953,8 +1028,12 @@ main (int argc, char **argv) break; case 'e': if (optarg) @@ -1606,7 +2459,7 @@ index 10b8c52..079c86c 100644 /* Could check tab width > 0. */ untabify_input = true; break; -@@ -966,8 +1045,12 @@ main (int argc, char **argv) +@@ -967,8 +1046,12 @@ main (int argc, char **argv) break; case 'i': if (optarg) @@ -1632,7 +2485,7 @@ index 10b8c52..079c86c 100644 break; case 'N': skip_count = false; -@@ -1013,6 +1096,7 @@ main (int argc, char **argv) +@@ -1012,6 +1095,7 @@ main (int argc, char **argv) /* Reset an additional input of -s, -S dominates -s */ col_sep_string = ""; col_sep_length = 0; @@ -1640,7 +2493,7 @@ index 10b8c52..079c86c 100644 use_col_separator = true; if (optarg) separator_string (optarg); -@@ -1168,7 +1252,8 @@ getoptnum (char const *n_str, int min, char const *err) +@@ -1166,10 +1250,45 @@ getoptnum (char const *n_str, int min, int *num, char const *err) a number. */ static void @@ -1648,12 +2501,7 @@ index 10b8c52..079c86c 100644 +getoptarg (char *arg, char switch_char, char *character, int *character_length, + int *character_width, int *number) { - if (!*arg) - { -@@ -1177,7 +1262,41 @@ getoptarg (char *arg, char switch_char, char *character, int *number) - } - - if (!c_isdigit (*arg)) + if (!ISDIGIT (*arg)) - *character = *arg++; + { +#ifdef HAVE_MBRTOWC @@ -1693,7 +2541,7 @@ index 10b8c52..079c86c 100644 if (*arg) { long int tmp_long; -@@ -1206,6 +1325,11 @@ static void +@@ -1198,6 +1317,11 @@ static void init_parameters (int number_of_files) { int chars_used_by_number = 0; @@ -1705,7 +2553,7 @@ index 10b8c52..079c86c 100644 lines_per_body = lines_per_page - lines_per_header - lines_per_footer; if (lines_per_body <= 0) -@@ -1243,7 +1367,7 @@ init_parameters (int number_of_files) +@@ -1235,7 +1359,7 @@ init_parameters (int number_of_files) else col_sep_string = column_separator; @@ -1714,7 +2562,7 @@ index 10b8c52..079c86c 100644 use_col_separator = true; } /* It's rather pointless to define a TAB separator with column -@@ -1275,11 +1399,11 @@ init_parameters (int number_of_files) +@@ -1267,11 +1391,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. */ @@ -1728,16 +2576,16 @@ index 10b8c52..079c86c 100644 /* The number is part of the column width unless we are printing files in parallel. */ -@@ -1288,7 +1412,7 @@ init_parameters (int number_of_files) +@@ -1280,7 +1404,7 @@ init_parameters (int number_of_files) } int sep_chars, useful_chars; -- if (ckd_mul (&sep_chars, columns - 1, col_sep_length)) -+ if (ckd_mul (&sep_chars, columns - 1, col_sep_width)) +- 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 (ckd_sub (&useful_chars, chars_per_line - chars_used_by_number, - sep_chars)) -@@ -1311,7 +1435,7 @@ init_parameters (int number_of_files) + if (INT_SUBTRACT_WRAPV (chars_per_line - chars_used_by_number, sep_chars, + &useful_chars)) +@@ -1303,7 +1427,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); @@ -1746,7 +2594,7 @@ index 10b8c52..079c86c 100644 } /* Open the necessary files, -@@ -1417,7 +1541,7 @@ init_funcs (void) +@@ -1409,7 +1533,7 @@ init_funcs (void) /* Enlarge p->start_position of first column to use the same form of padding_not_printed with all columns. */ @@ -1755,7 +2603,7 @@ index 10b8c52..079c86c 100644 /* This loop takes care of all but the rightmost column. */ -@@ -1451,7 +1575,7 @@ init_funcs (void) +@@ -1443,7 +1567,7 @@ init_funcs (void) } else { @@ -1764,7 +2612,7 @@ index 10b8c52..079c86c 100644 h_next = h + chars_per_column; } } -@@ -1751,9 +1875,9 @@ static void +@@ -1740,9 +1864,9 @@ static void align_column (COLUMN *p) { padding_not_printed = p->start_position; @@ -1776,9 +2624,9 @@ index 10b8c52..079c86c 100644 padding_not_printed = ANYWHERE; } -@@ -2030,13 +2154,13 @@ store_char (char c) +@@ -2017,13 +2141,13 @@ store_char (char c) /* May be too generous. */ - buff = xpalloc (buff, &buff_allocated, 1, -1, sizeof *buff); + buff = X2REALLOC (buff, &buff_allocated); } - buff[buff_current++] = c; + buff[buff_current++] = (unsigned char) c; @@ -1792,7 +2640,7 @@ index 10b8c52..079c86c 100644 char *s; int num_width; -@@ -2053,22 +2177,24 @@ add_line_number (COLUMN *p) +@@ -2040,22 +2164,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. */ @@ -1821,7 +2669,7 @@ index 10b8c52..079c86c 100644 output_position = POS_AFTER_TAB (chars_per_output_tab, output_position); } -@@ -2227,7 +2353,7 @@ print_white_space (void) +@@ -2214,7 +2340,7 @@ print_white_space (void) while (goal - h_old > 1 && (h_new = POS_AFTER_TAB (chars_per_output_tab, h_old)) <= goal) { @@ -1830,7 +2678,7 @@ index 10b8c52..079c86c 100644 h_old = h_new; } while (++h_old <= goal) -@@ -2247,6 +2373,7 @@ print_sep_string (void) +@@ -2234,6 +2360,7 @@ print_sep_string (void) { char const *s = col_sep_string; int l = col_sep_length; @@ -1838,7 +2686,7 @@ index 10b8c52..079c86c 100644 if (separators_not_printed <= 0) { -@@ -2258,6 +2385,7 @@ print_sep_string (void) +@@ -2245,6 +2372,7 @@ print_sep_string (void) { for (; separators_not_printed > 0; --separators_not_printed) { @@ -1846,7 +2694,7 @@ index 10b8c52..079c86c 100644 while (l-- > 0) { /* 3 types of sep_strings: spaces only, spaces and chars, -@@ -2271,12 +2399,15 @@ print_sep_string (void) +@@ -2258,12 +2386,15 @@ print_sep_string (void) } else { @@ -1863,7 +2711,7 @@ index 10b8c52..079c86c 100644 /* sep_string ends with some spaces */ if (spaces_not_printed > 0) print_white_space (); -@@ -2307,7 +2438,7 @@ print_clump (COLUMN *p, int n, char *clump) +@@ -2291,7 +2422,7 @@ print_clump (COLUMN *p, int n, char *clump) required number of tabs and spaces. */ static void @@ -1872,7 +2720,7 @@ index 10b8c52..079c86c 100644 { if (tabify_output) { -@@ -2331,6 +2462,74 @@ print_char (char c) +@@ -2315,6 +2446,74 @@ print_char (char c) putchar (c); } @@ -1947,7 +2795,7 @@ index 10b8c52..079c86c 100644 /* Skip to page PAGE before printing. PAGE may be larger than total number of pages. */ -@@ -2507,9 +2706,9 @@ read_line (COLUMN *p) +@@ -2492,9 +2691,9 @@ read_line (COLUMN *p) align_empty_cols = false; } @@ -1959,7 +2807,7 @@ index 10b8c52..079c86c 100644 padding_not_printed = ANYWHERE; } -@@ -2578,7 +2777,7 @@ print_stored (COLUMN *p) +@@ -2563,7 +2762,7 @@ print_stored (COLUMN *p) COLUMN *q; int line = p->current_line++; @@ -1968,7 +2816,7 @@ index 10b8c52..079c86c 100644 /* FIXME UMR: Uninitialized memory read: * This is occurring while in: -@@ -2590,7 +2789,7 @@ print_stored (COLUMN *p) +@@ -2575,7 +2774,7 @@ print_stored (COLUMN *p) xmalloc [xmalloc.c:94] init_store_cols [pr.c:1648] */ @@ -1977,7 +2825,7 @@ index 10b8c52..079c86c 100644 pad_vertically = true; -@@ -2610,9 +2809,9 @@ print_stored (COLUMN *p) +@@ -2595,9 +2794,9 @@ print_stored (COLUMN *p) } } @@ -1989,7 +2837,7 @@ index 10b8c52..079c86c 100644 padding_not_printed = ANYWHERE; } -@@ -2625,8 +2824,8 @@ print_stored (COLUMN *p) +@@ -2610,8 +2809,8 @@ print_stored (COLUMN *p) if (spaces_not_printed == 0) { output_position = p->start_position + end_vector[line]; @@ -2000,7 +2848,7 @@ index 10b8c52..079c86c 100644 } return true; -@@ -2645,7 +2844,7 @@ print_stored (COLUMN *p) +@@ -2630,7 +2829,7 @@ print_stored (COLUMN *p) number of characters is 1.) */ static int @@ -2009,7 +2857,7 @@ index 10b8c52..079c86c 100644 { unsigned char uc = c; char *s = clump_buff; -@@ -2655,10 +2854,10 @@ char_to_clump (char c) +@@ -2640,10 +2839,10 @@ char_to_clump (char c) int chars; int chars_per_c = 8; @@ -2022,7 +2870,7 @@ index 10b8c52..079c86c 100644 { width = TAB_WIDTH (chars_per_c, input_position); -@@ -2739,6 +2938,164 @@ char_to_clump (char c) +@@ -2724,6 +2923,164 @@ char_to_clump (char c) return chars; } @@ -2188,14 +3036,13 @@ index 10b8c52..079c86c 100644 looking for more options and printing the next batch of files. diff --git a/src/sort.c b/src/sort.c -index 05d00cc..eb51f20 100644 +index 8ca7a88..220bdd4 100644 --- a/src/sort.c +++ b/src/sort.c -@@ -30,6 +30,15 @@ +@@ -29,6 +29,14 @@ #include #include - #include -+ + #include +#if HAVE_WCHAR_H +# include +#endif @@ -2206,8 +3053,8 @@ index 05d00cc..eb51f20 100644 + #include "system.h" #include "argmatch.h" - #include "assure.h" -@@ -160,14 +169,39 @@ static int thousands_sep; + #include "die.h" +@@ -159,14 +167,39 @@ static int thousands_sep; /* We currently ignore multi-byte grouping chars. */ static bool thousands_sep_ignored; @@ -2248,7 +3095,7 @@ index 05d00cc..eb51f20 100644 /* The kind of blanks for '-b' to skip in various options. */ enum blanktype { bl_start, bl_end, bl_both }; -@@ -344,13 +378,11 @@ static bool stable; +@@ -343,13 +376,11 @@ static bool stable; /* An int value outside char range. */ enum { NON_CHAR = CHAR_MAX + 1 }; @@ -2265,9 +3112,9 @@ index 05d00cc..eb51f20 100644 /* Flag to remove consecutive duplicate lines from the output. Only the last of a sequence of equal lines will be output. */ -@@ -386,6 +418,46 @@ struct tempnode - static struct tempnode *volatile temphead; - static struct tempnode *volatile *temptail = &temphead; +@@ -805,6 +836,46 @@ reap_all (void) + reap (-1); + } +/* Function pointers. */ +static void @@ -2312,7 +3159,7 @@ index 05d00cc..eb51f20 100644 /* Clean up any remaining temporary files. */ static void -@@ -1343,7 +1415,7 @@ zaptemp (char const *name) +@@ -1272,7 +1343,7 @@ zaptemp (char const *name) free (node); } @@ -2321,7 +3168,7 @@ index 05d00cc..eb51f20 100644 static int struct_month_cmp (void const *m1, void const *m2) -@@ -1358,7 +1430,7 @@ struct_month_cmp (void const *m1, void const *m2) +@@ -1287,7 +1358,7 @@ struct_month_cmp (void const *m1, void const *m2) /* Initialize the character class tables. */ static void @@ -2330,7 +3177,7 @@ index 05d00cc..eb51f20 100644 { size_t i; -@@ -1370,7 +1442,7 @@ inittables (void) +@@ -1299,7 +1370,7 @@ inittables (void) fold_toupper[i] = toupper (i); } @@ -2339,7 +3186,7 @@ index 05d00cc..eb51f20 100644 /* If we're not in the "C" locale, read different names for months. */ if (hard_LC_TIME) { -@@ -1450,6 +1522,84 @@ specify_nmerge (int oi, char c, char const *s) +@@ -1381,6 +1452,84 @@ specify_nmerge (int oi, char c, char const *s) xstrtol_fatal (e, oi, c, long_options, s); } @@ -2424,7 +3271,7 @@ index 05d00cc..eb51f20 100644 /* Specify the amount of main memory to use when sorting. */ static void specify_sort_size (int oi, char c, char const *s) -@@ -1676,7 +1826,7 @@ buffer_linelim (struct buffer const *buf) +@@ -1612,7 +1761,7 @@ buffer_linelim (struct buffer const *buf) by KEY in LINE. */ static char * @@ -2433,7 +3280,7 @@ index 05d00cc..eb51f20 100644 { char *ptr = line->text, *lim = ptr + line->length - 1; size_t sword = key->sword; -@@ -1685,10 +1835,10 @@ begfield (struct line const *line, struct keyfield const *key) +@@ -1621,10 +1770,10 @@ begfield (struct line const *line, struct keyfield const *key) /* The leading field separator itself is included in a field when -t is absent. */ @@ -2446,7 +3293,7 @@ index 05d00cc..eb51f20 100644 ++ptr; if (ptr < lim) ++ptr; -@@ -1718,12 +1868,71 @@ begfield (struct line const *line, struct keyfield const *key) +@@ -1650,12 +1799,71 @@ begfield (struct line const *line, struct keyfield const *key) return ptr; } @@ -2519,7 +3366,7 @@ index 05d00cc..eb51f20 100644 { char *ptr = line->text, *lim = ptr + line->length - 1; size_t eword = key->eword, echar = key->echar; -@@ -1738,10 +1947,10 @@ limfield (struct line const *line, struct keyfield const *key) +@@ -1670,10 +1878,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. */ @@ -2532,7 +3379,7 @@ index 05d00cc..eb51f20 100644 ++ptr; if (ptr < lim && (eword || echar)) ++ptr; -@@ -1787,10 +1996,10 @@ limfield (struct line const *line, struct keyfield const *key) +@@ -1719,10 +1927,10 @@ limfield (struct line const *line, struct keyfield const *key) */ /* Make LIM point to the end of (one byte past) the current field. */ @@ -2545,7 +3392,7 @@ index 05d00cc..eb51f20 100644 if (newlim) lim = newlim; } -@@ -1825,6 +2034,130 @@ limfield (struct line const *line, struct keyfield const *key) +@@ -1753,6 +1961,130 @@ limfield (struct line const *line, struct keyfield const *key) return ptr; } @@ -2676,7 +3523,7 @@ index 05d00cc..eb51f20 100644 /* 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 -@@ -1911,8 +2244,22 @@ fillbuf (struct buffer *buf, FILE *fp, char const *file) +@@ -1839,8 +2171,22 @@ fillbuf (struct buffer *buf, FILE *fp, char const *file) else { if (key->skipsblanks) @@ -2701,7 +3548,7 @@ index 05d00cc..eb51f20 100644 line->keybeg = line_start; } } -@@ -2050,12 +2397,10 @@ find_unit_order (char const *number) +@@ -1978,12 +2324,10 @@ find_unit_order (char const *number) ATTRIBUTE_PURE static int @@ -2717,7 +3564,7 @@ index 05d00cc..eb51f20 100644 int diff = find_unit_order (a) - find_unit_order (b); return (diff ? diff : strnumcmp (a, b, decimal_point, thousands_sep)); -@@ -2067,7 +2412,7 @@ human_numcompare (char const *a, char const *b) +@@ -1995,7 +2339,7 @@ human_numcompare (char const *a, char const *b) ATTRIBUTE_PURE static int @@ -2726,7 +3573,7 @@ index 05d00cc..eb51f20 100644 { while (blanks[to_uchar (*a)]) a++; -@@ -2077,6 +2422,25 @@ numcompare (char const *a, char const *b) +@@ -2005,6 +2349,25 @@ numcompare (char const *a, char const *b) return strnumcmp (a, b, decimal_point, thousands_sep); } @@ -2752,7 +3599,7 @@ index 05d00cc..eb51f20 100644 static int nan_compare (long double a, long double b) { -@@ -2118,7 +2482,7 @@ general_numcompare (char const *sa, char const *sb) +@@ -2046,7 +2409,7 @@ general_numcompare (char const *sa, char const *sb) Return 0 if the name in S is not recognized. */ static int @@ -2761,7 +3608,7 @@ index 05d00cc..eb51f20 100644 { size_t lo = 0; size_t hi = MONTHS_PER_YEAR; -@@ -2457,15 +2821,14 @@ debug_key (struct line const *line, struct keyfield const *key) +@@ -2322,15 +2685,14 @@ debug_key (struct line const *line, struct keyfield const *key) char saved = *lim; *lim = '\0'; @@ -2779,7 +3626,7 @@ index 05d00cc..eb51f20 100644 else if (key->general_numeric) ignore_value (strtold (beg, &tighter_lim)); else if (key->numeric || key->human_numeric) -@@ -2611,7 +2974,7 @@ key_warnings (struct keyfield const *gkey, bool gkey_only) +@@ -2476,7 +2838,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 */ @@ -2788,7 +3635,7 @@ index 05d00cc..eb51f20 100644 && ((!key->skipsblanks && !implicit_skip) || (!key->skipsblanks && key->schar) || (!key->skipeblanks && key->echar))) -@@ -2659,9 +3022,9 @@ key_warnings (struct keyfield const *gkey, bool gkey_only) +@@ -2524,9 +2886,9 @@ key_warnings (struct keyfield const *gkey, bool gkey_only) bool number_locale_warned = false; if (basic_numeric_field_span) { @@ -2801,7 +3648,7 @@ index 05d00cc..eb51f20 100644 { error (0, 0, _("field separator %s is treated as a " -@@ -2672,9 +3035,9 @@ key_warnings (struct keyfield const *gkey, bool gkey_only) +@@ -2537,9 +2899,9 @@ key_warnings (struct keyfield const *gkey, bool gkey_only) } if (basic_numeric_field_span || general_numeric_field_span) { @@ -2814,7 +3661,7 @@ index 05d00cc..eb51f20 100644 { error (0, 0, _("field separator %s is treated as a " -@@ -2682,19 +3045,19 @@ key_warnings (struct keyfield const *gkey, bool gkey_only) +@@ -2547,19 +2909,19 @@ key_warnings (struct keyfield const *gkey, bool gkey_only) quote (((char []) {decimal_point, 0}))); number_locale_warned = true; } @@ -2838,8 +3685,17 @@ index 05d00cc..eb51f20 100644 } } -@@ -2746,11 +3109,87 @@ diff_reversed (int diff, bool reversed) - return reversed ? _GL_CMP (0, diff) : diff; +@@ -2570,7 +2932,7 @@ key_warnings (struct keyfield const *gkey, bool gkey_only) + { + error (0, 0, + _("%snumbers use %s as a decimal point in this locale"), +- tab == decimal_point ? "" : _("note "), ++ (tab_length && tab[0] == decimal_point) ? "" : _("note "), + quote (((char []) {decimal_point, 0}))); + + } +@@ -2612,11 +2974,87 @@ diff_reversed (int diff, bool reversed) + return reversed ? (diff < 0) - (diff > 0) : diff; } +#if HAVE_MBRTOWC @@ -2927,16 +3783,16 @@ index 05d00cc..eb51f20 100644 { struct keyfield *key = keylist; -@@ -2831,7 +3270,7 @@ keycompare (struct line const *a, struct line const *b) +@@ -2697,7 +3135,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, nullptr) - getmonth (tb, nullptr); -+ diff = getmonth (ta, tlena, nullptr) - getmonth (tb, tlenb, nullptr); +- 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) -@@ -2941,6 +3380,211 @@ keycompare (struct line const *a, struct line const *b) +@@ -2807,6 +3245,211 @@ keycompare (struct line const *a, struct line const *b) return diff_reversed (diff, key->reverse); } @@ -3148,7 +4004,7 @@ index 05d00cc..eb51f20 100644 /* Compare two lines A and B, returning negative, zero, or positive depending on whether A compares less than, equal to, or greater than B. */ -@@ -2968,7 +3612,7 @@ compare (struct line const *a, struct line const *b) +@@ -2834,7 +3477,7 @@ compare (struct line const *a, struct line const *b) diff = - NONZERO (blen); else if (blen == 0) diff = 1; @@ -3157,7 +4013,7 @@ index 05d00cc..eb51f20 100644 { /* xmemcoll0 is a performance enhancement as it will not unconditionally write '\0' after the -@@ -4340,6 +4984,7 @@ set_ordering (char const *s, struct keyfield *key, enum blanktype blanktype) +@@ -4222,6 +4865,7 @@ set_ordering (char const *s, struct keyfield *key, enum blanktype blanktype) break; case 'f': key->translate = fold_toupper; @@ -3165,7 +4021,7 @@ index 05d00cc..eb51f20 100644 break; case 'g': key->general_numeric = true; -@@ -4419,7 +5064,7 @@ main (int argc, char **argv) +@@ -4301,7 +4945,7 @@ main (int argc, char **argv) initialize_exit_failure (SORT_FAILURE); hard_LC_COLLATE = hard_locale (LC_COLLATE); @@ -3174,7 +4030,7 @@ index 05d00cc..eb51f20 100644 hard_LC_TIME = hard_locale (LC_TIME); #endif -@@ -4442,6 +5087,29 @@ main (int argc, char **argv) +@@ -4324,6 +4968,29 @@ main (int argc, char **argv) thousands_sep = NON_CHAR; } @@ -3204,7 +4060,7 @@ index 05d00cc..eb51f20 100644 have_read_stdin = false; inittables (); -@@ -4717,13 +5385,34 @@ main (int argc, char **argv) +@@ -4598,13 +5265,34 @@ main (int argc, char **argv) case 't': { @@ -3214,7 +4070,7 @@ index 05d00cc..eb51f20 100644 + size_t newtab_length = 1; + strncpy (newtab, optarg, MB_LEN_MAX); + if (! newtab[0]) - error (SORT_FAILURE, 0, _("empty tab")); + die (SORT_FAILURE, 0, _("empty tab")); - if (optarg[1]) +#if HAVE_MBRTOWC + if (MB_CUR_MAX > 1) @@ -3237,20 +4093,20 @@ index 05d00cc..eb51f20 100644 +#endif + if (newtab_length == 1 && optarg[1]) { - if (streq (optarg, "\\0")) + if (STREQ (optarg, "\\0")) - newtab = '\0'; + newtab[0] = '\0'; else { /* Provoke with 'sort -txx'. Complain about -@@ -4734,9 +5423,11 @@ main (int argc, char **argv) - quote (optarg)); +@@ -4615,9 +5303,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)) - error (SORT_FAILURE, 0, _("incompatible tabs")); + die (SORT_FAILURE, 0, _("incompatible tabs")); - tab = newtab; + memcpy (tab, newtab, newtab_length); + tab_length = newtab_length; @@ -3258,10 +4114,10 @@ index 05d00cc..eb51f20 100644 break; diff --git a/src/unexpand.c b/src/unexpand.c -index ff234d7..7c36ef6 100644 +index 8c97f0d..516cb95 100644 --- a/src/unexpand.c +++ b/src/unexpand.c -@@ -39,6 +39,9 @@ +@@ -38,6 +38,9 @@ #include #include #include @@ -3269,12 +4125,12 @@ index ff234d7..7c36ef6 100644 +#include + #include "system.h" - #include "expand-common.h" + #include "die.h" -@@ -105,24 +108,46 @@ unexpand (void) +@@ -106,24 +109,47 @@ unexpand (void) { /* Input stream. */ - FILE *fp = next_file (nullptr); + FILE *fp = next_file (NULL); + mb_file_t mbf; /* The array of pending blanks. In non-POSIX locales, blanks can @@ -3291,27 +4147,28 @@ index ff234d7..7c36ef6 100644 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) + { -+ /* Try using some predefined locale */ -+ if (set_utf_locale () != 0) -+ { -+ error (EXIT_FAILURE, errno, _("cannot set UTF-8 locale")); -+ } ++ 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 = ximalloc (max_column_width); -+ pending_blank = ximalloc (max_column_width * sizeof (mbf_char_t)); +- pending_blank = xmalloc (max_column_width); ++ pending_blank = xmalloc (max_column_width * sizeof (mbf_char_t)); + + if (found_bom == true) ++ { + print_bom(); ++ } while (true) { @@ -3321,7 +4178,7 @@ index ff234d7..7c36ef6 100644 /* If true, perform translations. */ bool convert = true; -@@ -156,12 +181,44 @@ unexpand (void) +@@ -157,12 +183,44 @@ unexpand (void) do { @@ -3369,10 +4226,10 @@ index ff234d7..7c36ef6 100644 if (blank) { -@@ -175,16 +232,16 @@ unexpand (void) +@@ -179,16 +237,16 @@ unexpand (void) + if (next_tab_column < column) + die (EXIT_FAILURE, 0, _("input line is too long")); - if (convert) - { - if (c == '\t') + if (mb_iseq (c, '\t')) { @@ -3389,7 +4246,7 @@ index ff234d7..7c36ef6 100644 if (! (prev_blank && column == next_tab_column)) { -@@ -192,13 +249,14 @@ unexpand (void) +@@ -196,13 +254,14 @@ unexpand (void) will be replaced by tabs. */ if (column == next_tab_column) one_blank_before_tab_stop = true; @@ -3406,7 +4263,7 @@ index ff234d7..7c36ef6 100644 } /* Discard pending blanks, unless it was a single -@@ -206,7 +264,7 @@ unexpand (void) +@@ -210,7 +269,7 @@ unexpand (void) pending = one_blank_before_tab_stop; } } @@ -3415,7 +4272,7 @@ index ff234d7..7c36ef6 100644 { /* Go back one column, and force recalculation of the next tab stop. */ -@@ -216,16 +274,20 @@ unexpand (void) +@@ -220,16 +279,20 @@ unexpand (void) } else { @@ -3424,7 +4281,7 @@ index ff234d7..7c36ef6 100644 + const uintmax_t orig_column = column; + column += mb_width (c); + if (column < orig_column) - error (EXIT_FAILURE, 0, _("input line is too long")); + die (EXIT_FAILURE, 0, _("input line is too long")); } if (pending) @@ -3437,10 +4294,10 @@ index ff234d7..7c36ef6 100644 + for (int n = 0; n < pending; ++n) + mb_putc (pending_blank[n], stdout); + if (ferror (stdout)) - write_error (); + die (EXIT_FAILURE, errno, _("write error")); pending = 0; one_blank_before_tab_stop = false; -@@ -235,16 +297,17 @@ unexpand (void) +@@ -239,16 +302,17 @@ unexpand (void) convert &= convert_entire_line || blank; } @@ -3454,15 +4311,180 @@ index ff234d7..7c36ef6 100644 - if (putchar (c) < 0) + mb_putc (c, stdout); + if (ferror (stdout)) - write_error (); + die (EXIT_FAILURE, errno, _("write error")); } - while (c != '\n'); + while (!mb_iseq (c, '\n')); } } +diff --git a/src/uniq.c b/src/uniq.c +index ad0c1d6..2a5a182 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'}, +@@ -254,7 +281,7 @@ size_opt (char const *opt, char const *msgid) + + ATTRIBUTE_PURE + static char * +-find_field (struct linebuffer const *line) ++find_field_uni (struct linebuffer *line) + { + size_t count; + char const *lp = line->buffer; +@@ -274,6 +301,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. +@@ -494,6 +598,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/Coreutils.pm b/tests/Coreutils.pm -index b55fb9d..ac80f49 100644 +index f147401..3ce5da9 100644 --- a/tests/Coreutils.pm +++ b/tests/Coreutils.pm @@ -269,6 +269,9 @@ sub run_tests ($$$$$) @@ -3476,7 +4498,7 @@ index b55fb9d..ac80f49 100644 { warn "$program_name: $test_name: test name is too long (> $max)\n"; diff --git a/tests/expand/mb.sh b/tests/expand/mb.sh -new file mode 100644 +new file mode 100755 index 0000000..dd6007c --- /dev/null +++ b/tests/expand/mb.sh @@ -3665,7 +4687,7 @@ index 0000000..dd6007c + +exit $fail diff --git a/tests/i18n/sort.sh b/tests/i18n/sort.sh -new file mode 100644 +new file mode 100755 index 0000000..26c95de --- /dev/null +++ b/tests/i18n/sort.sh @@ -3700,19 +4722,19 @@ index 0000000..26c95de + +Exit $fail diff --git a/tests/local.mk b/tests/local.mk -index 53fc53e..0148422 100644 +index a0c0249..3b932f0 100644 --- a/tests/local.mk +++ b/tests/local.mk -@@ -412,6 +412,8 @@ all_tests = \ - tests/sort/sort-field-limit.sh \ - tests/sort/sort-files0-from.pl \ - tests/sort/sort-float.sh \ +@@ -383,6 +383,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/sort/sort-h-thousands-sep.sh \ - tests/sort/sort-merge.pl \ - tests/sort/sort-merge-fdlimit.sh \ -@@ -618,6 +620,7 @@ all_tests = \ + tests/misc/sort-h-thousands-sep.sh \ + tests/misc/sort-merge.pl \ + tests/misc/sort-merge-fdlimit.sh \ +@@ -584,6 +586,7 @@ all_tests = \ tests/du/threshold.sh \ tests/du/trailing-slash.sh \ tests/du/two-args.sh \ @@ -3720,7 +4742,7 @@ index 53fc53e..0148422 100644 tests/id/gnu-zero-uids.sh \ tests/id/no-context.sh \ tests/id/context.sh \ -@@ -774,6 +777,7 @@ all_tests = \ +@@ -736,6 +739,7 @@ all_tests = \ tests/touch/read-only.sh \ tests/touch/relative.sh \ tests/touch/trailing-slash.sh \ @@ -3729,7 +4751,7 @@ index 53fc53e..0148422 100644 # See tests/factor/create-test.sh. diff --git a/tests/misc/expand.pl b/tests/misc/expand.pl -index 4b07210..68b9ea1 100755 +index 06261ac..7dd813e 100755 --- a/tests/misc/expand.pl +++ b/tests/misc/expand.pl @@ -27,6 +27,15 @@ my $prog = 'expand'; @@ -3795,8 +4817,151 @@ index 4b07210..68b9ea1 100755 my $save_temps = $ENV{DEBUG}; my $verbose = $ENV{VERBOSE}; +diff --git a/tests/misc/fold.pl b/tests/misc/fold.pl +index a94072f..136a82e 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 2ca8567..1d01a3d 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 100644 +new file mode 100755 index 0000000..11836ba --- /dev/null +++ b/tests/misc/sort-mb-tests.sh @@ -3846,136 +5011,10 @@ index 0000000..11836ba +compare exp out || { fail=1; cat out; } + +Exit $fail -diff --git a/tests/misc/unexpand.pl b/tests/misc/unexpand.pl -index bb7469c..c1dec95 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"}], -@@ -132,6 +140,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/pr/pr-tests.pl b/tests/pr/pr-tests.pl -index 60e6106..3c64a08 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 -@@ -515,8 +524,48 @@ push @Tests, - {IN=>"x\tx\tx\tx\tx\nx\tx\tx\tx\tx\n"}, - {OUT=>"x\tx\tx\tx\tx\tx\tx\tx\tx\tx\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}; - -diff --git a/tests/sort/sort-merge.pl b/tests/sort/sort-merge.pl -index a3204d3..40942a5 100755 ---- a/tests/sort/sort-merge.pl -+++ b/tests/sort/sort-merge.pl +diff --git a/tests/misc/sort-merge.pl b/tests/misc/sort-merge.pl +index bd439ef..2ccdf87 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; @@ -4032,10 +5071,10 @@ index a3204d3..40942a5 100755 my $save_temps = $ENV{DEBUG}; my $verbose = $ENV{VERBOSE}; -diff --git a/tests/sort/sort.pl b/tests/sort/sort.pl -index 5fa9d52..a66952a 100755 ---- a/tests/sort/sort.pl -+++ b/tests/sort/sort.pl +diff --git a/tests/misc/sort.pl b/tests/misc/sort.pl +index 46f1d7a..bb38f5b 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; @@ -4053,7 +5092,7 @@ index 5fa9d52..a66952a 100755 # 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 '-'. -@@ -428,6 +433,38 @@ foreach my $t (@Tests) +@@ -423,6 +428,38 @@ foreach my $t (@Tests) } } @@ -4092,7 +5131,7 @@ index 5fa9d52..a66952a 100755 @Tests = triple_test \@Tests; # Remember that triple_test creates from each test with exactly one "IN" -@@ -437,6 +474,7 @@ foreach my $t (@Tests) +@@ -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; @@ -4100,8 +5139,210 @@ index 5fa9d52..a66952a 100755 my $save_temps = $ENV{DEBUG}; my $verbose = $ENV{VERBOSE}; +diff --git a/tests/misc/unexpand.pl b/tests/misc/unexpand.pl +index d78a1bc..2b9137d 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 a6354dc..e43cd6e 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 265e6e1..83a8b29 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 +@@ -512,8 +521,48 @@ push @Tests, + {IN=>"x\tx\tx\tx\tx\nx\tx\tx\tx\tx\n"}, + {OUT=>"x\tx\tx\tx\tx\tx\tx\tx\tx\tx\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}; + diff --git a/tests/unexpand/mb.sh b/tests/unexpand/mb.sh -new file mode 100644 +new file mode 100755 index 0000000..8a82d74 --- /dev/null +++ b/tests/unexpand/mb.sh @@ -4279,5 +5520,5 @@ index 0000000..8a82d74 +LC_ALL=C unexpand in in > out || fail=1 +compare exp out > /dev/null 2>&1 || fail=1 -- -2.52.0 +2.46.2 diff --git a/coreutils-python3.patch b/coreutils-python3.patch deleted file mode 100644 index 447fdbc..0000000 --- a/coreutils-python3.patch +++ /dev/null @@ -1,65 +0,0 @@ -From 8927d505ecb5334f09c48ef98ef1f464f581d0f7 Mon Sep 17 00:00:00 2001 -From: rpm-build -Date: Tue, 2 Apr 2024 14:11:26 +0100 -Subject: [PATCH] coreutils-python3.patch - ---- - init.cfg | 4 ++-- - tests/d_type-check | 2 +- - tests/du/move-dir-while-traversing.sh | 6 +++--- - 3 files changed, 6 insertions(+), 6 deletions(-) - -diff --git a/init.cfg b/init.cfg -index ac05f7b..26d9516 100644 ---- a/init.cfg -+++ b/init.cfg -@@ -601,10 +601,10 @@ seek_data_capable_() - # Skip the current test if "." lacks d_type support. - require_dirent_d_type_() - { -- python < /dev/null \ -+ python3 < /dev/null \ - || skip_ python missing: assuming no d_type support - -- python "$abs_srcdir"/tests/d_type-check \ -+ python3 "$abs_srcdir"/tests/d_type-check \ - || skip_ requires d_type support - } - -diff --git a/tests/d_type-check b/tests/d_type-check -index 1a2f76f..42d3924 100644 ---- a/tests/d_type-check -+++ b/tests/d_type-check -@@ -1,4 +1,4 @@ --#!/usr/bin/python -+#!/usr/bin/python3 - # Exit 0 if "." and "./tempfile" have useful d_type information, else 1. - # Intended to exit 0 only on Linux/GNU systems. - import os -diff --git a/tests/du/move-dir-while-traversing.sh b/tests/du/move-dir-while-traversing.sh -index adf482b..cf9214a 100755 ---- a/tests/du/move-dir-while-traversing.sh -+++ b/tests/du/move-dir-while-traversing.sh -@@ -21,8 +21,8 @@ print_ver_ du - require_trap_signame_ - - # We use a python-inotify script, so... --python -m pyinotify -h > /dev/null \ -- || skip_ 'python inotify package not installed' -+python3 -m pyinotify -h > /dev/null \ -+ || skip_ 'python3 inotify package not installed' - - # Move a directory "up" while du is processing its sub-directories. - # While du is processing a hierarchy .../B/C/D/... this script -@@ -33,7 +33,7 @@ python -m pyinotify -h > /dev/null \ - # rename syscall before du finishes processing the subtree under D/. - - cat <<'EOF' > inotify-watch-for-dir-access.py --#!/usr/bin/env python -+#!/usr/bin/env python3 - import pyinotify as pn - import os,sys - --- -2.51.0 - diff --git a/coreutils-selinux.patch b/coreutils-selinux.patch new file mode 100644 index 0000000..7136f3f --- /dev/null +++ b/coreutils-selinux.patch @@ -0,0 +1,87 @@ +From d70ddb3eb845c494280e7365e2b889242e7e1bb9 Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Mon, 4 Oct 2021 08:45:53 +0200 +Subject: [PATCH] coreutils-selinux.patch + +--- + src/cp.c | 19 ++++++++++++++++++- + src/install.c | 12 +++++++++++- + 2 files changed, 29 insertions(+), 2 deletions(-) + +diff --git a/src/cp.c b/src/cp.c +index c97a675..89fb8ec 100644 +--- a/src/cp.c ++++ b/src/cp.c +@@ -952,7 +952,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) + { +@@ -1000,6 +1000,23 @@ 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) ++ { ++ 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/src/install.c b/src/install.c +index c9456fe..2b1bee9 100644 +--- a/src/install.c ++++ b/src/install.c +@@ -794,7 +794,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) +@@ -855,6 +855,9 @@ 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) + { +@@ -862,6 +865,13 @@ main (int argc, char **argv) + "this kernel is not SELinux-enabled")); + break; + } ++ if (x.set_security_context) ++ { ++ 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; +-- +2.31.1 + diff --git a/coreutils.spec b/coreutils.spec index 6712263..eb80749 100644 --- a/coreutils.spec +++ b/coreutils.spec @@ -1,9 +1,8 @@ Summary: A set of basic GNU tools commonly used in shell scripts Name: coreutils -Version: 9.9 -Release: 2%{?dist} -# some used parts of gnulib are under various variants of LGPL -License: GPL-3.0-or-later AND GFDL-1.3-no-invariants-or-later AND LGPL-2.1-or-later AND LGPL-3.0-or-later +Version: 9.3 +Release: 7%{?dist} +License: GPL-3.0-or-later Url: https://www.gnu.org/software/coreutils/ Source0: https://ftp.gnu.org/gnu/%{name}/%{name}-%{version}.tar.xz Source1: https://ftp.gnu.org/gnu/%{name}/%{name}-%{version}.tar.xz.sig @@ -14,6 +13,9 @@ Source50: supported_utils Source105: coreutils-colorls.sh Source106: coreutils-colorls.csh +# revert a gnulib patch that broke the build +Patch1: coreutils-9.3-gnulib-strtol.patch + # do not make coreutils-single depend on /usr/bin/coreutils %global __requires_exclude ^%{_bindir}/coreutils$ @@ -26,23 +28,26 @@ Patch101: coreutils-8.26-selinuxenable.patch # downstream changes to default DIR_COLORS Patch102: coreutils-8.32-DIR_COLORS.patch -# use python3 in tests -Patch103: coreutils-python3.patch - # df --direct Patch104: coreutils-df-direct.patch -# gnulib C23 support -# https://github.com/coreutils/gnulib/commit/df17f4f37ed3ca373d23ad42eae51122bdb96626 -Patch105: coreutils-9.9-gnulib-c23.patch +# copy: reduce verbosity of -i and -u with --verbose (#2236321) +# backport of cc078f747f3db00e70b2ae2ad2ab34e8d54316d3. +Patch105: coreutils-9.3-reduce--iu-verbosity-with-verbose.patch -# fix cut test failure on aarch64 rawhide (rhbz#2424302) -# https://github.com/coreutils/coreutils/commit/95044cb5eaea83d02f768feb5ab79fcf5e6ad782 -Patch106: coreutils-9.9-fix-cut-test-aarch64.patch +# cp -p should copy NFSv4 acls (#2160675) +# see the patch for the list of backported commits +Patch106: coreutils-9.3-cp--p-should-copy-NFSv4-acls.patch + +# fix buffer overflow in split (CVE-2024-0684) +Patch107: coreutils-9.2-CVE-2024-0684.patch # (sb) lin18nux/lsb compliance - multibyte functionality patch Patch800: coreutils-i18n.patch +# downstream SELinux options deprecated since 2009 +Patch950: coreutils-selinux.patch + Conflicts: filesystem < 3 # To avoid clobbering installs @@ -63,34 +68,19 @@ BuildRequires: libselinux-utils BuildRequires: make BuildRequires: openssl-devel BuildRequires: strace -BuildRequires: systemd-devel BuildRequires: texinfo # For gpg verification of source tarball BuildRequires: gnupg2 # test-only dependencies -BuildRequires: acl -BuildRequires: gdb BuildRequires: perl-interpreter BuildRequires: perl(FileHandle) -BuildRequires: python3 -BuildRequires: tzdata -%ifarch %valgrind_arches -BuildRequires: valgrind -%endif - -%if 0%{?fedora} -BuildRequires: perl(Expect) -BuildRequires: python3-inotify -%endif - %if 23 < 0%{?fedora} || 7 < 0%{?rhel} # needed by i18n test-cases BuildRequires: glibc-langpack-en BuildRequires: glibc-langpack-fr BuildRequires: glibc-langpack-ko -BuildRequires: glibc-langpack-sv %endif Requires: %{name}-common = %{version}-%{release} @@ -125,9 +115,6 @@ packaged as a single multicall binary. # https://bugzilla.redhat.com/show_bug.cgi?id=1107973#c7 Obsoletes: %{name} < 8.24-100 -# Gnulib translations are maintained seprately since coreutils 9.6 (#2393892) -Requires: gnulib-l10n - # info doc refers to "Specifying the Time Zone" from glibc-doc (#959597) Suggests: glibc-doc @@ -162,15 +149,16 @@ sed src/dircolors.hin \ find tests -name '*.sh' -perm 0644 -print -exec chmod 0755 '{}' '+' (echo "<<< done") 2>/dev/null -# FIXME: Force a newer gettext version to workaround `autoreconf -i` errors -# with coreutils 9.6 and bundled gettext 0.19.2 from gettext-common-devel. -sed -i "s/0.19.2/$(rpm -q --queryformat '%%{VERSION}\n' gettext-devel)/" bootstrap.conf configure.ac - 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: @@ -199,7 +187,6 @@ for type in separate single; do --cache-file=../config.cache \ --enable-install-program=arch \ --enable-no-install-program=kill,uptime \ - --enable-systemd \ --with-tty-group \ DEFAULT_POSIX2_VERSION=200112 alternative=199209 || : %make_build all V=1 @@ -226,20 +213,14 @@ for type in separate single; do fi (cd $type && make DESTDIR=$RPM_BUILD_ROOT/$subdir $install) -%if "%{_sbindir}" != "%{_bindir}" # chroot was in /usr/sbin : - mkdir -p $RPM_BUILD_ROOT/$subdir/%_sbindir + mkdir -p $RPM_BUILD_ROOT/$subdir/{%{_bindir},%{_sbindir}} mv $RPM_BUILD_ROOT/$subdir/{%_bindir,%_sbindir}/chroot -%endif # Move multicall variants to *.single. # RemovePathPostfixes will strip that later. if test $type = 'single'; then - for dir in %{_bindir} \ -%if "%{_sbindir}" != "%{_bindir}" -%{_sbindir} \ -%endif -%{_libexecdir}/%{name}; do + 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 @@ -267,9 +248,7 @@ rm -f $RPM_BUILD_ROOT%{_infodir}/dir %files single %{_bindir}/*.single -%if "%{_sbindir}" != "%{_bindir}" %{_sbindir}/chroot.single -%endif %dir %{_libexecdir}/coreutils %{_libexecdir}/coreutils/*.so.single # duplicate the license because coreutils-common does not need to be installed @@ -286,104 +265,20 @@ rm -f $RPM_BUILD_ROOT%{_infodir}/dir %license COPYING %changelog -* Tue Jan 13 2026 Lukáš Zaoral - 9.9-2 -- fix cut test failure on aarch64 rawhide (rhbz#2424302) - -* Wed Nov 26 2025 Lukáš Zaoral - 9.9-1 -- rebase to latest upstream release (rhbz#2413803) - -* Mon Sep 29 2025 Lukáš Zaoral - 9.8-3 -- require gnulib-l10n for translations of gnulib messages (rhbz#2393892) - -* Thu Sep 25 2025 Lukáš Zaoral - 9.8-2 -- tail: fix tailing larger number of lines in regular files (rhbz#2398008) - -* Wed Sep 24 2025 Lukáš Zaoral - 9.8-1 -- rebase to latest upstream release (rhbz#2397467) -- remove downstream patch for selinux options deprecated since 2009 - -* Wed Jul 23 2025 Fedora Release Engineering - 9.7-5 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild - -* Mon Jun 30 2025 Lukáš Zaoral - 9.7-4 -- stty: add support for arbitrary baud rates (rhbz#2375439) - -* Wed May 28 2025 Lukáš Zaoral - 9.7-3 -- sort: fix buffer under-read (CVE-2025-5278) - -* Mon May 19 2025 Lukáš Zaoral - 9.7-2 -- cp/mv: do not fail when copying of trivial NFSv4 ACLs fails (rhbz#2363149) - -* Wed Apr 09 2025 Lukáš Zaoral - 9.7-1 -- rebase to latest upstream release (rhbz#2358624) - -* Tue Feb 25 2025 Lukáš Zaoral - 9.6-2 -- fix 'who -m' with guessed tty names (rhbz#2343998) - -* Mon Jan 20 2025 Lukáš Zaoral - 9.6-1 -- rebase to latest upstream version (rhbz#2338620) -- sync i18n patch with SUSE (Kudos to Berny Völker!) - -* Thu Jan 16 2025 Fedora Release Engineering - 9.5-13 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild - -* Sun Jan 12 2025 Zbigniew Jędrzejewski-Szmek - 9.5-12 -- Rebuilt for the bin-sbin merge (2nd attempt) - -* Wed Nov 13 2024 Florian Weimer - 9.5-11 -- Affinity mask handling in nproc for large CPU counts (rhbz#2325167) - -* Fri Sep 27 2024 Lukáš Zaoral - 9.5-10 +* Fri Sep 27 2024 Lukáš Zaoral - 9.3-7 - fix fold -b with UTF8 locale (RHEL-60295) -* Tue Aug 27 2024 Lukáš Zaoral - 9.5-9 -- show web sessions in who output (rhbz#2307847) - -* Wed Aug 21 2024 Lukáš Zaoral - 9.5-8 -- add missing systemd-devel buildrequires - -* Wed Jul 17 2024 Fedora Release Engineering - 9.5-7 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild - -* Mon Jul 15 2024 Lukáš Zaoral - 9.5-6 -- Rebuilt for the bin-sbin merge - -* Mon Jul 15 2024 Sohum Mendon - 9.5-5 +* Mon Jul 15 2024 Sohum Mendon - 9.3-6 - fix incorrect exit status when fold is called with a non-existent file -* Tue Jul 09 2024 Zbigniew Jędrzejewski-Szmek - 9.5-4 -- Rebuilt for the bin-sbin merge - -* Thu Jul 04 2024 Lukáš Zaoral - 9.5-3 -- do not buildrequire perl(Expect) on ELN - -* Tue Jun 04 2024 Lukáš Zaoral - 9.5-2 -- enable LTO on ppc64le - -* Tue Apr 02 2024 Lukáš Zaoral - 9.5-1 -- rebase to latest upstream version (rhbz#2272063) -- sync i18n patch with SUSE (Kudos to Berny Völker!) -- add some test dependencies to execute additional part of the upstream test-suite - -* Mon Jan 29 2024 Lukáš Zaoral - 9.4-6 -- fix tail on kernels with 64k page sizes (RHEL-22866) - -* Wed Jan 24 2024 Fedora Release Engineering - 9.4-5 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild - -* Fri Jan 19 2024 Fedora Release Engineering - 9.4-4 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild - -* Thu Jan 18 2024 Lukáš Zaoral - 9.4-3 -- fix compilation on i686 - -* Thu Jan 18 2024 Lukáš Zaoral - 9.4-2 +* Thu Jan 18 2024 Lukáš Zaoral - 9.3-5 - fix buffer overflow in split (CVE-2024-0684) -* Fri Sep 15 2023 Lukáš Zaoral - 9.4-1 -- new upstream release 9.4 (#2235759) -- enable integration with systemd -- fix the license field +* Mon Sep 11 2023 Lukáš Zaoral - 9.3-4 +- fix cp -p not copying NFSv4 acls (rhbz#2160675) + +* Thu Aug 31 2023 Lukáš Zaoral - 9.3-3 +- copy: reduce verbosity of -i and -u with --verbose (#2236321) * Wed Jul 19 2023 Fedora Release Engineering - 9.3-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild diff --git a/sources b/sources index 0952ab1..f5e06f7 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (coreutils-9.9.tar.xz.sig) = 0a3dfdfa6b4234e2e1d42142269f959bdf3cf8f6605a50270a27eff84dd22588f182121f7dd3eeb04be45f5109d02690215065b3d3b43882874d0e165a1435d0 -SHA512 (coreutils-9.9.tar.xz) = e7b0e59f7732d2c098ea4934014f470248bd5c4764210e9200a698010a8e3b95bbb26e543f0cd73ed5a4b8e1f8cda932c73f39954d68175e4deaa47526610c65 +SHA512 (coreutils-9.3.tar.xz) = 242271f212a6860bdc6c8d7e5c4f85ce66c1b48ef781aca9daa56e0fe7c2b7809ef72b4392120219fe5b687637c83ce89ceef8bb35f6274f43f8f968a6901694 +SHA512 (coreutils-9.3.tar.xz.sig) = 522a2072f8ef940228ccdd856a4041c3c16b98e309168ccf2066fe7c1013685ba6cdea8a7317dfa1f4507b37ca016ecedaf54438d4a5007927b0e1a8fd223eb5 diff --git a/tests/readlink-cannot-handle-recursive-symlinks/Makefile b/tests/readlink-cannot-handle-recursive-symlinks/Makefile new file mode 100644 index 0000000..49d37a7 --- /dev/null +++ b/tests/readlink-cannot-handle-recursive-symlinks/Makefile @@ -0,0 +1,63 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Makefile of /CoreOS/coreutils/readlink-cannot-handle-recursive-symlink s +# Description: Test for readlink cannot handle recursive symlinks +# Author: Jan Scotka +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2010 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# 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, write to the Free +# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +export TEST=/CoreOS/coreutils/readlink-cannot-handle-recursive-symlinks +export TESTVERSION=1.0 + +BUILT_FILES= + +FILES=$(METADATA) runtest.sh Makefile PURPOSE + +.PHONY: all install download clean + +run: $(FILES) build + ./runtest.sh + +build: $(BUILT_FILES) + chmod a+x runtest.sh + +clean: + rm -f *~ $(BUILT_FILES) + + +include /usr/share/rhts/lib/rhts-make.include + +$(METADATA): Makefile + @echo "Owner: Jan Scotka " > $(METADATA) + @echo "Name: $(TEST)" >> $(METADATA) + @echo "TestVersion: $(TESTVERSION)" >> $(METADATA) + @echo "Path: $(TEST_DIR)" >> $(METADATA) + @echo "Description: Test for readlink cannot handle recursive symlinks" >> $(METADATA) + @echo "Type: Sanity" >> $(METADATA) + @echo "TestTime: 5m" >> $(METADATA) + @echo "RunFor: coreutils" >> $(METADATA) + @echo "Requires: coreutils" >> $(METADATA) + @echo "Priority: Normal" >> $(METADATA) + @echo "License: GPLv2" >> $(METADATA) + @echo "Confidential: no" >> $(METADATA) + @echo "Destructive: no" >> $(METADATA) + + rhts-lint $(METADATA) diff --git a/tests/readlink-cannot-handle-recursive-symlinks/PURPOSE b/tests/readlink-cannot-handle-recursive-symlinks/PURPOSE new file mode 100644 index 0000000..b9fd740 --- /dev/null +++ b/tests/readlink-cannot-handle-recursive-symlinks/PURPOSE @@ -0,0 +1,54 @@ +PURPOSE of /CoreOS/coreutils/readlink-cannot-handle-recursive-symlinks +Description: Test for readlink cannot handle recursive symlinks +Author: Jan Scotka +Bug summary: readlink cannot handle recursive symlinks + +Description: + +Description of problem: +The readlink command fails with an error "Too many levels of symbolic links", even if there are only 2 levels. + +The readlink command from RHEL 3 and RHEL 4 and from Fedora 11 all work fine. + +Among other changes between RHEL 4 and RHEL 5, RHEL 5's coreutils added calls to cycle_check() in lib/canonicalize.c, but in upstream canonicalize.c (now in gnulib instead of coreutils), we see the comment: + /* Detect loops. We cannot use the cycle-check module here, + since it's actually possible to encounter the same symlink + more than once in a given traversal. However, encountering + the same symlink,NAME pair twice does indicate a loop. */ + +http://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=blob;f=lib/canonicalize.c;h=4f348398fd69ae516396313d18ac294a4ca3dde3;hb=b653eda3ac4864de205419d9f41eec267cb89eeb#l262 + +The latest canonicalize.c uses seen_triple() instead of cycle_check(). + + +Version-Release number of selected component (if applicable): +coreutils-5.97-19.el5 + +How reproducible: +every time + +Steps to Reproduce: +1. Create a directory with a symlink to itself + mkdir /tmp/dir + cd /tmp/dir + ln -s ../dir dirlink + +2. Run readlink using the 'dirlink' symlink recursively + readlink -v -f dirlink + readlink -v -f dirlink/dirlink + readlink -v -f dirlink/dirlink/dirlink + +Actual results: +The first readlink command on just dirlink succeeds, but the 2nd and 3rd commands fail with + readlink: dirlink/dirlink: Too many levels of symbolic links + +Expected results: +$ readlink -v -f dirlink +/tmp/dir +$ readlink -v -f dirlink/dirlink +/tmp/dir +$ readlink -v -f dirlink/dirlink/dirlink +/tmp/dir + + +Additional info: diff --git a/tests/readlink-cannot-handle-recursive-symlinks/runtest.sh b/tests/readlink-cannot-handle-recursive-symlinks/runtest.sh new file mode 100755 index 0000000..6ee251f --- /dev/null +++ b/tests/readlink-cannot-handle-recursive-symlinks/runtest.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# vim: dict=/usr/share/beakerlib/dictionary.vim cpt=.,w,b,u,t,i,k +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# runtest.sh of /CoreOS/coreutils/readlink-cannot-handle-recursive-symlinks +# Description: Test for readlink cannot handle recursive symlinks +# Author: Jan Scotka +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2010 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# 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, write to the Free +# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +# Include rhts environment +. /usr/bin/rhts-environment.sh +. /usr/lib/beakerlib/beakerlib.sh + +PACKAGE="coreutils" + +rlJournalStart + rlPhaseStartSetup + rlAssertRpm $PACKAGE + rlRun "TmpDir=\`mktemp -d\`" 0 "Creating tmp directory" + rlRun "pushd $TmpDir" + rlRun "mkdir link" 0 "Creating test directory" + rlRun "cd link" 0 "cd to this dir" + rlRun "ln -s ../link link" 0 "creating symlink to ../link -> link" + rlPhaseEnd + + rlPhaseStartTest + rlLog "Test of readlink up to 20 iteration" + export TMPVAR="link" + for foo in `seq 20` + do echo $TMPVAR + rlRun "readlink -v -f $TMPVAR" 0 "readlink of $TMPVAR" + TMPVAR="$TMPVAR/link" + done + rlPhaseEnd + + rlPhaseStartCleanup + rlRun "popd" + rlRun "rm -r $TmpDir" 0 "Removing tmp directory" + rlPhaseEnd +rlJournalPrintText +rlJournalEnd diff --git a/tests/test-basics b/tests/test-basics new file mode 100755 index 0000000..7324553 --- /dev/null +++ b/tests/test-basics @@ -0,0 +1,39 @@ +#!/bin/sh + +# Checks that touch ls rm and foo work +# https://www.mankier.com/1/beakerlib#Examples +. /usr/share/beakerlib/beakerlib.sh + +# Set the full test name +TEST="/examples/beakerlib/Sanity/phases" + +# Package being tested +PACKAGE="coreutils" + +rlJournalStart + # Setup phase: Prepare test directory + rlPhaseStartSetup + rlAssertRpm $PACKAGE + rlRun 'TmpDir=$(mktemp -d)' 0 'Creating tmp directory' # no-reboot + rlRun "pushd $TmpDir" + rlPhaseEnd + + # Test phase: Testing touch, ls and rm commands + rlPhaseStartTest + rlRun "touch foo" 0 "Creating the foo test file" + rlAssertExists "foo" + rlRun "ls -l foo" 0 "Listing the foo test file" + rlRun "rm foo" 0 "Removing the foo test file" + rlAssertNotExists "foo" + rlRun "ls -l foo" 2 "Listing foo should now report an error" + rlPhaseEnd + + # Cleanup phase: Remove test directory + rlPhaseStartCleanup + rlRun "popd" + rlRun "rm -r $TmpDir" 0 "Removing tmp directory" + rlPhaseEnd +rlJournalEnd + +# Print the test report +rlJournalPrintText diff --git a/tests/test_basics.yml b/tests/test_basics.yml new file mode 100644 index 0000000..d5727cf --- /dev/null +++ b/tests/test_basics.yml @@ -0,0 +1,9 @@ +--- +# This first play always runs on the local staging system +- hosts: localhost + tags: + - atomic + - classic + - container + roles: + - { role: standard-test-beakerlib, tests: [ test-basics, readlink-cannot-handle-recursive-symlinks ] } diff --git a/tests/tests.yml b/tests/tests.yml new file mode 100644 index 0000000..529263d --- /dev/null +++ b/tests/tests.yml @@ -0,0 +1,2 @@ +# Fedora Continuous Integration: https://fedoraproject.org/wiki/CI +- include: test_basics.yml