From d88e232f02ed9039b4ffca77f7c6330bd8ae584fa44209f460474266152b2a42 Mon Sep 17 00:00:00 2001 From: OBS User unknown Date: Mon, 18 Dec 2006 23:15:28 +0000 Subject: [PATCH] OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/coreutils?expand=0&rev=1 --- .gitattributes | 23 + .gitignore | 1 + acl-test.diff | 89 + coreutils-5.0-pam-env.patch | 38 + coreutils-5.3.0-i18n-0.1.patch | 3998 ++++++++++++++++++++++++++++++++ coreutils-5.3.0-pie.diff | 21 + coreutils-5.3.0-sbin4su.diff | 143 ++ coreutils-6.7.diff | 764 ++++++ coreutils-6.7.tar.bz2 | 3 + coreutils-changelog.diff | 44 + coreutils-sysinfo.diff | 58 + coreutils-xattr.diff | 321 +++ coreutils.changes | 898 +++++++ coreutils.spec | 738 ++++++ getcwd.diff | 11 + i18n-infloop.diff | 12 + i18n-uninit.diff | 25 + invalid-ids.diff | 47 + no-no.diff | 10 + ready | 0 su.default | 11 + su.pamd | 7 + 22 files changed, 7262 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 acl-test.diff create mode 100644 coreutils-5.0-pam-env.patch create mode 100644 coreutils-5.3.0-i18n-0.1.patch create mode 100644 coreutils-5.3.0-pie.diff create mode 100644 coreutils-5.3.0-sbin4su.diff create mode 100644 coreutils-6.7.diff create mode 100644 coreutils-6.7.tar.bz2 create mode 100644 coreutils-changelog.diff create mode 100644 coreutils-sysinfo.diff create mode 100644 coreutils-xattr.diff create mode 100644 coreutils.changes create mode 100644 coreutils.spec create mode 100644 getcwd.diff create mode 100644 i18n-infloop.diff create mode 100644 i18n-uninit.diff create mode 100644 invalid-ids.diff create mode 100644 no-no.diff create mode 100644 ready create mode 100644 su.default create mode 100644 su.pamd diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9b03811 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,23 @@ +## Default LFS +*.7z filter=lfs diff=lfs merge=lfs -text +*.bsp filter=lfs diff=lfs merge=lfs -text +*.bz2 filter=lfs diff=lfs merge=lfs -text +*.gem filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.jar filter=lfs diff=lfs merge=lfs -text +*.lz filter=lfs diff=lfs merge=lfs -text +*.lzma filter=lfs diff=lfs merge=lfs -text +*.obscpio filter=lfs diff=lfs merge=lfs -text +*.oxt filter=lfs diff=lfs merge=lfs -text +*.pdf filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.rpm filter=lfs diff=lfs merge=lfs -text +*.tbz filter=lfs diff=lfs merge=lfs -text +*.tbz2 filter=lfs diff=lfs merge=lfs -text +*.tgz filter=lfs diff=lfs merge=lfs -text +*.ttf filter=lfs diff=lfs merge=lfs -text +*.txz filter=lfs diff=lfs merge=lfs -text +*.whl filter=lfs diff=lfs merge=lfs -text +*.xz filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.zst filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57affb6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.osc diff --git a/acl-test.diff b/acl-test.diff new file mode 100644 index 0000000..3f953b6 --- /dev/null +++ b/acl-test.diff @@ -0,0 +1,89 @@ +2006-12-13 Andreas Schwab + + * tests/mv/acl (skip): Check for acl support in the file system. + + * tests/mv/Makefile.am (XFAIL_TESTS): Remove. + (TESTS_ENVIRONMENT): Pass CONFIG_HEADER. + +2006-12-12 Jim Meyering + + * m4/acl.m4 (gl_ACL_GET_FILE): Fix logic error. + Reported by Andreas Schwab . + +diff --git a/tests/mv/Makefile.am b/tests/mv/Makefile.am +index 57581cd..0a1f2db 100644 +--- a/tests/mv/Makefile.am ++++ b/tests/mv/Makefile.am +@@ -20,7 +20,6 @@ + + AUTOMAKE_OPTIONS = 1.3 gnits + +-XFAIL_TESTS = acl + TESTS = \ + hard-verbose \ + backup-dir \ +@@ -48,4 +47,5 @@ TESTS_ENVIRONMENT = \ + PERL="$(PERL)" \ + EGREP="$(EGREP)" \ + PATH="$(VG_PATH_PREFIX)`pwd`/../../src$(PATH_SEPARATOR)$$PATH" \ ++ CONFIG_HEADER=$(CONFIG_HEADER) \ + PROG=mv +diff --git a/tests/mv/acl b/tests/mv/acl +index f570656..df3bb01 100755 +--- a/tests/mv/acl ++++ b/tests/mv/acl +@@ -24,6 +24,13 @@ + # Make sure we get English translations. + . $srcdir/../lang-default + ++# Skip this test if cp was built without ACL support: ++grep '^#define USE_ACL 0' $CONFIG_HEADER > /dev/null && \ ++ { ++ echo 1>&2 "$0: insufficient ACL support, so skipping this test" ++ (exit 77); exit 77 ++ } ++ + if test "$VERBOSE" = yes; then + set -x + mv --version +@@ -46,15 +53,26 @@ framework_failure=0 + mkdir -p $tmp || framework_failure=1 + cd $tmp || framework_failure=1 + ++touch file || framework_failure=1 ++ + if test $framework_failure = 1; then + echo 'failure in testing framework' + (exit 1); exit 1 + fi + ++skip=no ++# Ensure that setfacl and getfacl work on this file system. ++setfacl -m user:bin:rw file 2> /dev/null || skip=yes ++acl1=`getfacl file` || skip=yes ++ ++test $skip = yes && ++ { ++ echo "$0: '.' is not on a suitable file system for this test" 1>&2 ++ echo "$0: skipping this test" 1>&2 ++ (exit 77); exit 77 ++ } ++ + # move the access acl of a file +-touch file || framework_failure=1 +-setfacl -m user:bin:rw file || framework_failure=1 +-acl1=`getfacl file` || framework_failure=1 + mv file $other_partition_tmpdir || fail=1 + acl2=`cd $other_partition_tmpdir && getfacl file` || framework_failure=1 + test "$acl1" = "$acl2" || fail=1 +--- a/m4/acl.m4 ++++ b/m4/acl.m4 +@@ -53,7 +53,7 @@ + #include + ]], + [[return !! (!acl_get_file (".", ACL_TYPE_ACCESS) +- || errno == ENOENT);]])], ++ && errno == ENOENT);]])], + [gl_cv_func_working_acl_get_file=yes], + [gl_cv_func_working_acl_get_file=no], + [gl_cv_func_working_acl_get_file=cross-compiling])]) diff --git a/coreutils-5.0-pam-env.patch b/coreutils-5.0-pam-env.patch new file mode 100644 index 0000000..151bbb2 --- /dev/null +++ b/coreutils-5.0-pam-env.patch @@ -0,0 +1,38 @@ +--- coreutils-5.3.0/src/su.c ++++ coreutils-5.3.0/src/su.c +@@ -521,6 +521,21 @@ + } + + /* child shell */ ++ ++ /* Export env variables declared by PAM modules */ ++ { ++ const char *const *env; ++ ++ env = (const char *const *) pam_getenvlist (pamh); ++ while (env && *env) ++ { ++ ++ if (putenv (*env) != 0) ++ xalloc_die (); ++ env++; ++ } ++ } ++ + pam_end (pamh, 0); + #endif + +@@ -726,9 +741,12 @@ + shell = NULL; + } + shell = xstrdup (shell ? shell : pw->pw_shell); ++ change_identity (pw); ++ ++ /* Set environment after pam_open_session, which may put KRB5CCNAME ++ into the pam_env, etc. */ + modify_environment (pw, shell); + +- change_identity (pw); + if (simulate_login && chdir (pw->pw_dir) != 0) + error (0, errno, _("warning: cannot change directory to %s"), pw->pw_dir); + diff --git a/coreutils-5.3.0-i18n-0.1.patch b/coreutils-5.3.0-i18n-0.1.patch new file mode 100644 index 0000000..f4df397 --- /dev/null +++ b/coreutils-5.3.0-i18n-0.1.patch @@ -0,0 +1,3998 @@ +--- coreutils-6.5/lib/linebuffer.h ++++ coreutils-6.5/lib/linebuffer.h +@@ -22,6 +22,11 @@ + + # include + ++/* Get mbstate_t. */ ++# if HAVE_WCHAR_H ++# include ++# endif ++ + /* A `struct linebuffer' holds a line of text. */ + + struct linebuffer +@@ -29,6 +34,9 @@ + size_t size; /* Allocated. */ + size_t length; /* Used. */ + char *buffer; ++# if HAVE_WCHAR_H ++ mbstate_t state; ++# endif + }; + + /* Initialize linebuffer LINEBUFFER for use. */ +--- coreutils-6.5/src/cut.c ++++ coreutils-6.5/src/cut.c +@@ -29,6 +29,12 @@ + #include + #include + #include ++ ++/* Get mbstate_t, mbrtowc(). */ ++#if HAVE_WCHAR_H ++# include ++#endif ++ + #include "system.h" + + #include "error.h" +@@ -37,6 +43,13 @@ + #include "quote.h" + #include "xstrndup.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 ++ + /* The official name of this program (e.g., no `g' prefix). */ + #define PROGRAM_NAME "cut" + +@@ -73,6 +86,54 @@ + size_t hi; + }; + ++/* Refill the buffer BUF. */ ++#define REFILL_BUFFER(BUF, BUFPOS, BUFLEN, STREAM) \ ++ do \ ++ { \ ++ if (BUFLEN < MB_LEN_MAX && !feof (STREAM) && !ferror (STREAM)) \ ++ { \ ++ memmove (BUF, BUFPOS, BUFLEN); \ ++ BUFLEN += fread (BUF + BUFLEN, sizeof(char), BUFSIZ, STREAM); \ ++ BUFPOS = BUF; \ ++ } \ ++ } \ ++ while (0) ++ ++/* Get wide character which starts at BUFPOS. If the byte sequence is ++ not valid as a character, CONVFAIL is 1. Otherwise 0. */ ++#define GET_NEXT_WC_FROM_BUFFER(WC, BUFPOS, BUFLEN, MBLENGTH, STATE, CONVFAIL) \ ++ do \ ++ { \ ++ wchar_t tmp; \ ++ mbstate_t state_bak; \ ++ \ ++ if (BUFLEN < 1) \ ++ { \ ++ WC = WEOF; \ ++ break; \ ++ } \ ++ \ ++ /* Get a wide character. */ \ ++ CONVFAIL = 0; \ ++ state_bak = STATE; \ ++ MBLENGTH = mbrtowc (&tmp, BUFPOS, BUFLEN, &STATE); \ ++ WC = tmp; \ ++ \ ++ switch (MBLENGTH) \ ++ { \ ++ case (size_t)-1: \ ++ case (size_t)-2: \ ++ ++CONVFAIL; \ ++ STATE = state_bak; \ ++ /* Fall througn. */ \ ++ \ ++ case 0: \ ++ MBLENGTH = 1; \ ++ break; \ ++ } \ ++ } \ ++ while (0) ++ + /* 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 +@@ -85,7 +146,7 @@ + /* The number of bytes allocated for FIELD_1_BUFFER. */ + static size_t field_1_bufsize; + +-/* The largest field or byte index used as an endpoint of a closed ++/* The largest field, character or byte index used as an endpoint of a closed + or degenerate range specification; this doesn't include the starting + index of right-open-ended ranges. For example, with either range spec + `2-5,9-', `2-3,5,9-' this variable would be set to 5. */ +@@ -97,10 +158,11 @@ + + /* This is a bit vector. + In byte mode, which bytes to output. ++ In character mode, which characters to output. + In field mode, which DELIM-separated fields to output. +- Both bytes and fields are numbered starting with 1, ++ Bytes, characters and fields are numbered starting with 1, + so the zeroth bit of this array is unused. +- A field or byte K has been selected if ++ A byte, character or field K has been selected if + (K <= MAX_RANGE_ENDPOINT and is_printable_field(K)) + || (EOL_RANGE_START > 0 && K >= EOL_RANGE_START). */ + static unsigned char *printable_field; +@@ -109,9 +171,12 @@ + { + undefined_mode, + +- /* Output characters that are in the given bytes. */ ++ /* Output bytes that are in the given bytes. */ + byte_mode, + ++ /* Output characters that are at the given positions. */ ++ character_mode, ++ + /* Output the given delimeter-separated fields. */ + field_mode + }; +@@ -121,6 +186,13 @@ + + static enum operating_mode operating_mode; + ++/* If true, when in byte mode, don't split multibyte characters. */ ++static bool byte_mode_character_aware; ++ ++/* If true, the function for single byte locale is work ++ if this program runs on multibyte locale. */ ++static bool force_singlebyte_mode; ++ + /* If true do not output lines containing no delimeter characters. + Otherwise, all such lines are printed. This option is valid only + with field mode. */ +@@ -132,6 +204,9 @@ + + /* The delimeter character for field mode. */ + static unsigned char delim; ++#if HAVE_WCHAR_H ++static wchar_t wcdelim; ++#endif + + /* True if the --output-delimiter=STRING option was specified. */ + static bool output_delimiter_specified; +@@ -205,7 +280,7 @@ + -f, --fields=LIST select only these fields; also print any line\n\ + that contains no delimiter character, unless\n\ + the -s option is specified\n\ +- -n (ignored)\n\ ++ -n with -b: don't split multibyte characters\n\ + "), stdout); + fputs (_("\ + --complement complement the set of selected bytes, characters\n\ +@@ -360,7 +435,7 @@ + in_digits = false; + /* Starting a range. */ + if (dash_found) +- FATAL_ERROR (_("invalid byte or field list")); ++ FATAL_ERROR (_("invalid byte, character or field list")); + dash_found = true; + fieldstr++; + +@@ -385,14 +460,16 @@ + if (value == 0) + { + /* `n-'. From `initial' to end of line. */ +- eol_range_start = initial; ++ if (eol_range_start == 0 ++ || (eol_range_start != 0 && eol_range_start > initial)) ++ eol_range_start = initial; + field_found = true; + } + else + { + /* `m-n' or `-n' (1-n). */ + if (value < initial) +- FATAL_ERROR (_("invalid byte or field list")); ++ FATAL_ERROR (_("invalid byte, character or field list")); + + /* Is there already a range going to end of line? */ + if (eol_range_start != 0) +@@ -475,7 +552,7 @@ + fieldstr++; + } + else +- FATAL_ERROR (_("invalid byte or field list")); ++ FATAL_ERROR (_("invalid byte, character or field list")); + } + + max_range_endpoint = 0; +@@ -568,6 +645,81 @@ + } + } + ++#if HAVE_MBRTOWC ++/* This function is in use for the following case. ++ ++ 1. Read from the stream STREAM, printing to standard output any selected ++ characters. ++ ++ 2. Read from stream STREAM, printing to standard output any selected bytes, ++ without splitting multibyte characters. */ ++ ++static void ++cut_characters_or_cut_bytes_no_split (FILE *stream) ++{ ++ size_t idx; /* Number of bytes or characters in the line so far. */ ++ /* Whether to begin printing delimiters between ranges for the current line. ++ Set after we've begun printing data corresponding to the first range. */ ++ bool print_delimiter; ++ ++ char buf[MB_LEN_MAX + BUFSIZ]; /* For spooling a read byte sequence. */ ++ char *bufpos; /* Next read position of BUF. */ ++ size_t buflen; /* The length of the byte sequence in buf. */ ++ wint_t wc; /* A gotten wide character. */ ++ size_t mblength; /* The byte size of a multibyte character which shows ++ as same character as WC. */ ++ mbstate_t state; /* State of the stream. */ ++ int convfail; /* 1, when conversion is failed. Otherwise 0. */ ++ ++ ++ idx = 0; ++ print_delimiter = false; ++ buflen = 0; ++ bufpos = buf; ++ memset (&state, '\0', sizeof(mbstate_t)); ++ ++ while (1) ++ { ++ REFILL_BUFFER (buf, bufpos, buflen, stream); ++ ++ GET_NEXT_WC_FROM_BUFFER (wc, bufpos, buflen, mblength, state, convfail); ++ ++ if (wc == WEOF) ++ { ++ if (idx > 0) ++ putchar ('\n'); ++ break; ++ } ++ else if (wc == L'\n') ++ { ++ putchar ('\n'); ++ idx = 0; ++ print_delimiter = false; ++ } ++ else ++ { ++ bool range_start; ++ bool *rs = output_delimiter_specified ? &range_start : NULL; ++ ++ idx += (operating_mode == byte_mode) ? mblength : 1; ++ if (print_kth (idx, rs)) ++ { ++ if (rs && *rs && print_delimiter) ++ { ++ fwrite (output_delimiter_string, sizeof (char), ++ output_delimiter_length, stdout); ++ } ++ print_delimiter = true; ++ fwrite (bufpos, mblength, sizeof (char), stdout); ++ } ++ } ++ ++ buflen -= mblength; ++ bufpos += mblength; ++ } ++} ++#endif ++ + /* Read from stream STREAM, printing to standard output any selected fields. */ + + static void +@@ -689,13 +841,190 @@ + } + } + ++#if HAVE_MBRTOWC ++static void ++cut_fields_mb (FILE *stream) ++{ ++ int c; ++ size_t field_idx = 1; ++ bool found_any_selected_field = false; ++ bool buffer_first_field; ++ int empty_input; ++ char buf[MB_LEN_MAX + BUFSIZ]; /* For spooling a read byte sequence. */ ++ char *bufpos; /* Next read position of BUF. */ ++ size_t buflen; /* The length of the byte sequence in buf. */ ++ wint_t wc; /* A gotten wide character. */ ++ size_t mblength; /* The byte size of a multibyte character which shows ++ as same character as WC. */ ++ mbstate_t state; /* State of the stream. */ ++ int convfail; /* 1, when conversion is failed. Otherwise 0. */ ++ ++ bufpos = buf; ++ buflen = 0; ++ memset (&state, '\0', sizeof (mbstate_t)); ++ ++ c = getc (stream); ++ empty_input = (c == EOF); ++ if (c != EOF) ++ ungetc (c, stream); ++ else ++ wc = WEOF; ++ ++ /* To support the semantics of the -s flag, we may have to buffer ++ all of the first field to determine whether it is `delimited.' ++ But that is unnecessary if all non-delimited lines must be printed ++ and the first field has been selected, or if non-delimited lines ++ must be suppressed and the first field has *not* been selected. ++ That is because a non-delimited line has exactly one field. */ ++ buffer_first_field = (suppress_non_delimited ^ !print_kth (1, NULL)); ++ ++ while (1) ++ { ++ if (field_idx == 1 && buffer_first_field) ++ { ++ size_t n_bytes = 0; ++ ++ while (1) ++ { ++ REFILL_BUFFER (buf, bufpos, buflen, stream); ++ ++ GET_NEXT_WC_FROM_BUFFER ++ (wc, bufpos, buflen, mblength, state, convfail); ++ ++ if (wc == WEOF) ++ break; ++ ++ field_1_buffer = xrealloc (field_1_buffer, n_bytes + mblength); ++ memcpy (field_1_buffer + n_bytes, bufpos, mblength); ++ n_bytes += mblength; ++ buflen -= mblength; ++ bufpos += mblength; ++ ++ if (!convfail && (wc == L'\n' || wc == wcdelim)) ++ break; ++ } ++ ++ if (wc == WEOF) ++ break; ++ ++ /* If the first field extends to the end of line (it is not ++ delimited) and we are printing all non-delimited lines, ++ print this one. */ ++ if (convfail || (!convfail && wc != wcdelim)) ++ { ++ if (suppress_non_delimited) ++ { ++ /* Empty. */ ++ } ++ else ++ { ++ fwrite (field_1_buffer, sizeof (char), n_bytes, stdout); ++ /* Make sure the output line is newline terminated. */ ++ if (convfail || (!convfail && wc != L'\n')) ++ putchar ('\n'); ++ } ++ continue; ++ } ++ ++ if (print_kth (1, NULL)) ++ { ++ /* Print the field, but not the trailing delimiter. */ ++ fwrite (field_1_buffer, sizeof (char), n_bytes - 1, stdout); ++ found_any_selected_field = true; ++ } ++ ++field_idx; ++ } ++ ++ if (wc != WEOF) ++ { ++ if (print_kth (field_idx, NULL)) ++ { ++ if (found_any_selected_field) ++ { ++ fwrite (output_delimiter_string, sizeof (char), ++ output_delimiter_length, stdout); ++ } ++ found_any_selected_field = true; ++ } ++ ++ while (1) ++ { ++ REFILL_BUFFER (buf, bufpos, buflen, stream); ++ ++ GET_NEXT_WC_FROM_BUFFER ++ (wc, bufpos, buflen, mblength, state, convfail); ++ ++ if (wc == WEOF) ++ break; ++ else if (!convfail && (wc == wcdelim || wc == L'\n')) ++ { ++ buflen -= mblength; ++ bufpos += mblength; ++ break; ++ } ++ ++ if (print_kth (field_idx, NULL)) ++ fwrite (bufpos, mblength, sizeof (char), stdout); ++ ++ buflen -= mblength; ++ bufpos += mblength; ++ } ++ } ++ ++ if ((!convfail || wc == L'\n') && buflen < 1) ++ wc = WEOF; ++ ++ if (!convfail && wc == wcdelim) ++ ++field_idx; ++ else if (wc == WEOF || (!convfail && wc == L'\n')) ++ { ++ if (found_any_selected_field ++ || (!empty_input && !(suppress_non_delimited && field_idx == 1))) ++ putchar ('\n'); ++ if (wc == WEOF) ++ break; ++ field_idx = 1; ++ found_any_selected_field = false; ++ } ++ } ++} ++#endif ++ + static void + cut_stream (FILE *stream) + { +- if (operating_mode == byte_mode) +- cut_bytes (stream); ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1 && !force_singlebyte_mode) ++ { ++ switch (operating_mode) ++ { ++ case byte_mode: ++ if (byte_mode_character_aware) ++ cut_characters_or_cut_bytes_no_split (stream); ++ else ++ cut_bytes (stream); ++ break; ++ ++ case character_mode: ++ cut_characters_or_cut_bytes_no_split (stream); ++ break; ++ ++ case field_mode: ++ cut_fields_mb (stream); ++ break; ++ ++ default: ++ abort (); ++ } ++ } + else +- cut_fields (stream); ++#endif ++ { ++ if (operating_mode == field_mode) ++ cut_fields (stream); ++ else ++ cut_bytes (stream); ++ } + } + + /* Process file FILE to standard output. +@@ -745,6 +1074,8 @@ + bool ok; + bool delim_specified = false; + char *spec_list_string IF_LINT(= NULL); ++ char mbdelim[MB_LEN_MAX + 1]; ++ size_t delimlen = 0; + + initialize_main (&argc, &argv); + program_name = argv[0]; +@@ -767,7 +1098,6 @@ + switch (optc) + { + case 'b': +- case 'c': + /* Build the byte list. */ + if (operating_mode != undefined_mode) + FATAL_ERROR (_("only one type of list may be specified")); +@@ -775,6 +1105,14 @@ + spec_list_string = optarg; + break; + ++ case 'c': ++ /* Build the character list. */ ++ if (operating_mode != undefined_mode) ++ FATAL_ERROR (_("only one type of list may be specified")); ++ operating_mode = character_mode; ++ spec_list_string = optarg; ++ break; ++ + case 'f': + /* Build the field list. */ + if (operating_mode != undefined_mode) +@@ -786,9 +1124,32 @@ + case 'd': + /* New delimiter. */ + /* Interpret -d '' to mean `use the NUL byte as the delimiter.' */ +- if (optarg[0] != '\0' && optarg[1] != '\0') +- FATAL_ERROR (_("the delimiter must be a single character")); +- delim = optarg[0]; ++#if HAVE_MBRTOWC ++ if(MB_CUR_MAX > 1) ++ { ++ mbstate_t state; ++ ++ memset (&state, '\0', sizeof(mbstate_t)); ++ delimlen = mbrtowc (&wcdelim, optarg, MB_LEN_MAX, &state); ++ ++ if (delimlen == (size_t)-1 || delimlen == (size_t)-2) ++ force_singlebyte_mode = true; ++ else ++ { ++ delimlen = (delimlen < 1) ? 1 : delimlen; ++ if (wcdelim != L'\0' && *(optarg + delimlen) != '\0') ++ FATAL_ERROR (_("the delimiter must be a single character")); ++ memcpy (mbdelim, optarg, delimlen); ++ } ++ } ++ ++ if (MB_CUR_MAX <= 1 || force_singlebyte_mode) ++#endif ++ { ++ if (optarg[0] != '\0' && optarg[1] != '\0') ++ FATAL_ERROR (_("the delimiter must be a single character")); ++ delim = (unsigned char) optarg[0]; ++ } + delim_specified = true; + break; + +@@ -802,6 +1163,7 @@ + break; + + case 'n': ++ byte_mode_character_aware = true; + break; + + case 's': +@@ -824,7 +1186,7 @@ + if (operating_mode == undefined_mode) + FATAL_ERROR (_("you must specify a list of bytes, characters, or fields")); + +- if (delim != '\0' && operating_mode != field_mode) ++ if (delim_specified && operating_mode != field_mode) + FATAL_ERROR (_("an input delimiter may be specified only\ + when operating on fields")); + +@@ -851,15 +1213,34 @@ + } + + if (!delim_specified) +- delim = '\t'; ++ { ++ delim = '\t'; ++#ifdef HAVE_MBRTOWC ++ wcdelim = L'\t'; ++ mbdelim[0] = '\t'; ++ mbdelim[1] = '\0'; ++ delimlen = 1; ++ } ++#endif + + if (output_delimiter_string == NULL) + { +- static char dummy[2]; +- dummy[0] = delim; +- dummy[1] = '\0'; +- output_delimiter_string = dummy; +- output_delimiter_length = 1; ++#ifdef HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1 && !force_singlebyte_mode) ++ { ++ output_delimiter_string = xstrdup (mbdelim); ++ output_delimiter_length = delimlen; ++ } ++ ++ if (MB_CUR_MAX <= 1 || force_singlebyte_mode) ++#endif ++ { ++ static char dummy[2]; ++ dummy[0] = delim; ++ dummy[1] = '\0'; ++ output_delimiter_string = dummy; ++ output_delimiter_length = 1; ++ } + } + + if (optind == argc) +--- coreutils-6.5/src/expand.c ++++ coreutils-6.5/src/expand.c +@@ -38,11 +38,31 @@ + #include + #include + #include ++ ++/* Get mbstate_t, mbrtowc, wcwidth. */ ++#if HAVE_WCHAR_H ++# include ++#endif ++#if HAVE_WCTYPE_H ++# include ++#endif ++ + #include "system.h" + #include "error.h" + #include "quote.h" + #include "xstrndup.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 "expand" + +@@ -347,9 +367,12 @@ + } + else + { +- column++; +- if (!column) +- error (EXIT_FAILURE, 0, _("input line is too long")); ++ if (!iscntrl (c)) ++ { ++ column++; ++ if (!column) ++ error (EXIT_FAILURE, 0, _("input line is too long")); ++ } + } + + convert &= convert_entire_line | !! isblank (c); +@@ -365,6 +388,165 @@ + } + } + ++#if HAVE_MBRTOWC && HAVE_WCTYPE_H ++static void ++expand_multibyte (void) ++{ ++ /* Input stream. */ ++ FILE *fp = next_file (NULL); ++ ++ mbstate_t i_state; /* Current shift state of the input stream. */ ++ char buf[MB_LEN_MAX + BUFSIZ]; /* For spooling a read byte sequence. */ ++ char *bufpos; /* Next read position of BUF. */ ++ size_t buflen = 0; /* The length of the byte sequence in buf. */ ++ ++ if (!fp) ++ return; ++ ++ memset (&i_state, '\0', sizeof (mbstate_t)); ++ ++ for (;;) ++ { ++ /* Input character, or EOF. */ ++ wint_t wc; ++ ++ /* If true, perform translations. */ ++ bool convert = true; ++ ++ ++ /* The following variables have valid values only when CONVERT ++ is true: */ ++ ++ /* Column of next input character. */ ++ uintmax_t column = 0; ++ ++ /* Index in TAB_LIST of next tab stop to examine. */ ++ size_t tab_index = 0; ++ ++ ++ /* Convert a line of text. */ ++ ++ do ++ { ++ wchar_t w; ++ size_t mblength; /* The byte size of a multibyte character ++ which shows as same character as WC. */ ++ mbstate_t i_state_bak; /* Back up the I_STATE. */ ++ ++ /* Fill buffer */ ++ if (buflen < MB_LEN_MAX) ++ { ++ if (!feof(fp) && !ferror(fp)) ++ { ++ if (buflen > 0) ++ memmove (buf, bufpos, buflen); ++ buflen += fread (buf + buflen, sizeof (char), BUFSIZ, fp); ++ bufpos = buf; ++ } ++ } ++ ++ if (buflen < 1) ++ { ++ /* Move to the next file */ ++ if (feof (fp) || ferror (fp)) ++ fp = next_file(fp); ++ if (!fp) ++ return; ++ memset (&i_state, '\0', sizeof (mbstate_t)); ++ continue; ++ } ++ ++ i_state_bak = i_state; ++ mblength = mbrtowc (&w, bufpos, buflen, &i_state); ++ wc = w; ++ ++ if (mblength == (size_t) -1 || mblength == (size_t) -2) ++ { ++ i_state = i_state_bak; ++ wc = L'\0'; ++ column += convert; ++ mblength = 1; ++ } ++ ++ if (convert) ++ { ++ if (wc == L'\t') ++ { ++ /* Column the next input tab stop is on. */ ++ uintmax_t next_tab_column; ++ ++ if (tab_size) ++ next_tab_column = column + (tab_size - column % tab_size); ++ else ++ for (;;) ++ if (tab_index == first_free_tab) ++ { ++ next_tab_column = column + 1; ++ break; ++ } ++ else ++ { ++ uintmax_t tab = tab_list[tab_index++]; ++ if (column < tab) ++ { ++ next_tab_column = tab; ++ break; ++ } ++ } ++ ++ if (next_tab_column < column) ++ error (EXIT_FAILURE, 0, _("input line is too long")); ++ ++ while (++column < next_tab_column) ++ if (putchar (' ') < 0) ++ error (EXIT_FAILURE, errno, _("write error")); ++ ++ *bufpos = ' '; ++ } ++ else if (wc == L'\b') ++ { ++ /* Go back one column, and force recalculation of the ++ next tab stop. */ ++ column -= !!column; ++ tab_index -= !!tab_index; ++ } ++ else ++ { ++ if (!iswcntrl (wc)) ++ { ++ int width = wcwidth (wc); ++ if (width > 0) ++ { ++ if (column > (column + width)) ++ error (EXIT_FAILURE, 0, _("input line is too long")); ++ column += width; ++ } ++ } ++ } ++ ++ convert &= convert_entire_line | iswblank (wc); ++ } ++ ++ if (mblength) ++ { ++ if (fwrite (bufpos, sizeof (char), mblength, stdout) < mblength) ++ error (EXIT_FAILURE, errno, _("write error")); ++ } ++ else ++ { ++ if (putchar ('\0')) ++ error (EXIT_FAILURE, errno, _("write error")); ++ mblength = 1; ++ } ++ ++ buflen -= mblength; ++ bufpos += mblength; ++ } ++ while (wc != L'\n'); ++ } ++} ++#endif ++ + int + main (int argc, char **argv) + { +@@ -429,7 +611,12 @@ + + file_list = (optind < argc ? &argv[optind] : stdin_argv); + +- expand (); ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ expand_multibyte (); ++ else ++#endif ++ expand (); + + if (have_read_stdin && fclose (stdin) != 0) + error (EXIT_FAILURE, errno, "-"); +--- coreutils-6.5/src/fold.c ++++ coreutils-6.5/src/fold.c +@@ -23,6 +23,19 @@ + #include + #include + ++/* Get MB_CUR_MAX. */ ++#include ++ ++/* Get mbrtowc, mbstate_t, wcwidth(). */ ++#if HAVE_WCHAR_H ++# include ++#endif ++ ++/* Get iswprint(), iswctype(), wctype(). */ ++#if HAVE_WCTYPE_H ++# include ++#endif ++ + #include "system.h" + #include "error.h" + #include "quote.h" +@@ -30,14 +43,57 @@ + + #define TAB_WIDTH 8 + ++/* 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 ++ ++#ifndef HAVE_DECL_WCWIDTH ++"this configure-time declaration test was not run" ++#endif ++#if !HAVE_DECL_WCWIDTH ++extern int wcwidth (); ++#endif ++ ++/* If wcwidth() doesn't exist, assume all printable characters have ++ width 1. */ ++#if !defined wcwidth && !HAVE_WCWIDTH ++# define wcwidth(wc) ((wc) == 0 ? 0 : iswprint (wc) ? 1 : -1) ++#endif ++ + /* The official name of this program (e.g., no `g' prefix). */ + #define PROGRAM_NAME "fold" + + #define AUTHORS "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 name this program was run with. */ + char *program_name; + ++/* 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; + +@@ -47,11 +103,17 @@ + /* 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::"; ++ ++/* wide character class `blank' */ ++#if HAVE_MBRTOWC ++wctype_t blank_type; ++#endif + + 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}, +@@ -81,6 +143,7 @@ + "), stdout); + 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); +@@ -98,7 +161,7 @@ + static size_t + adjust_column (size_t column, char c) + { +- if (!count_bytes) ++ if (operating_mode != byte_mode) + { + if (c == '\b') + { +@@ -117,14 +180,9 @@ + return column; + } + +-/* Fold file FILENAME, or standard input if FILENAME is "-", +- to stdout, with maximum line length WIDTH. +- Return true if successful. */ +- +-static bool +-fold_file (char const *filename, size_t width) ++static int ++fold_text (FILE *istream, size_t width) + { +- 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. */ +@@ -132,20 +190,6 @@ + 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", filename); +- return false; +- } +- + while ((c = getc (istream)) != EOF) + { + if (offset_out + 1 >= allocated_out) +@@ -223,6 +267,234 @@ + if (offset_out) + fwrite (line_out, sizeof (char), (size_t) offset_out, stdout); + ++ return saved_errno; ++} ++ ++#if HAVE_MBRTOWC ++static void ++fold_multibyte_text (FILE *istream, size_t width) ++{ ++ int i; ++ char buf[MB_LEN_MAX + BUFSIZ]; /* For spooling a read byte sequence. */ ++ size_t buflen; /* The length of the byte sequence in buf. */ ++ char *bufpos; /* Next read position of BUF. */ ++ wint_t wc; /* A gotten wide character. */ ++ wchar_t tmp; ++ 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; /* 1, when conversion is failed. Otherwise 0. */ ++ ++ char *line_out = NULL; ++ size_t offset_out = 0; /* Index in `line_out' for next char. */ ++ size_t allocated_out = 1024; ++ ++ int increment; ++ size_t column = 0; ++ ++ size_t last_blank_pos; ++ size_t last_blank_column; ++ int is_blank_seen; ++ int last_blank_increment; ++ 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)); ++ line_out = xmalloc (allocated_out); ++ ++ buflen = fread (buf, sizeof (char), BUFSIZ, istream); ++ bufpos = buf; ++ ++ 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. */ ++ convfail = 0; ++ state_bak = state; ++ mblength = mbrtowc (&tmp, bufpos, buflen, &state); ++ wc = tmp; ++ ++ switch (mblength) ++ { ++ case (size_t)-1: ++ case (size_t)-2: ++ convfail++; ++ state = state_bak; ++ /* Fall through. */ ++ ++ case 0: ++ mblength = 1; ++ break; ++ } ++ ++ if (!convfail && wc == L'\n') ++ { ++ if (offset_out > 0) ++ { ++ fwrite (line_out, sizeof (char), offset_out, stdout); ++ START_NEW_LINE; ++ } ++ continue; ++ } ++ ++ rescan: ++ if (operating_mode == byte_mode) /* byte mode */ ++ increment = mblength; ++ else if (operating_mode == character_mode) /* character mode */ ++ increment = 1; ++ else /* column mode */ ++ { ++ if (convfail) ++ increment = 1; ++ else ++ { ++ switch (wc) ++ { ++ case L'\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 = x2nrealloc (line_out, &allocated_out, sizeof *line_out); ++ ++ for (i = 0; i < mblength; i++) ++ { ++ line_out[offset_out] = bufpos[i]; ++ ++offset_out; ++ } ++ ++ 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 && iswctype (wc, blank_type)) ++ { ++ 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; ++ } ++ } ++ ++ if (offset_out) ++ fwrite (line_out, sizeof (char), (size_t) offset_out, stdout); ++ ++ free(line_out); ++} ++#endif ++ ++/* Fold file FILENAME, or standard input if FILENAME is "-", ++ to stdout, with maximum line length WIDTH. ++ Return true if successful. */ ++ ++static bool ++fold_file (char const *filename, size_t width) ++{ ++ FILE *istream; ++ int saved_errno; ++ ++ if (STREQ (filename, "-")) ++ { ++ istream = stdin; ++ have_read_stdin = true; ++ } ++ 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); ++ else ++#endif ++ saved_errno = fold_text (istream, width); ++ + if (ferror (istream)) + { + error (0, saved_errno, "%s", filename); +@@ -255,6 +527,10 @@ + + atexit (close_stdout); + ++#if HAVE_MBRTOWC ++ blank_type = wctype ("blank"); ++#endif ++ operating_mode = column_mode; + break_spaces = count_bytes = have_read_stdin = false; + + while ((optc = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1) +@@ -264,7 +540,15 @@ + 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': /* Count characters rather than columns. */ ++ 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. */ +--- coreutils-6.5/src/join.c ++++ coreutils-6.5/src/join.c +@@ -23,6 +23,16 @@ + #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 "error.h" + #include "hard-locale.h" +@@ -33,6 +43,11 @@ + #include "xmemcoll.h" + #include "xstrtol.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" + +@@ -104,10 +119,13 @@ + /* 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 ++/* Tab character separating fields. If NULL, 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; ++static const char *tab = NULL; ++ ++/* The number of bytes used for tab. */ ++static size_t tablen = 0; + + static struct option const longopts[] = + { +@@ -199,10 +217,10 @@ + if (ptr == lim) + return; + +- if (0 <= tab) ++ if (tab != NULL) + { + char *sep; +- for (; (sep = memchr (ptr, tab, lim - ptr)) != NULL; ptr = sep + 1) ++ for (; (sep = memchr (ptr, tab[0], lim - ptr)) != NULL; ptr = sep + 1) + extract_field (line, ptr, sep - ptr); + } + else +@@ -229,6 +247,133 @@ + extract_field (line, ptr, lim - ptr); + } + ++#if HAVE_MBRTOWC ++static void ++xfields_multibyte (struct line *line) ++{ ++ int i; ++ char *ptr0 = line->buf.buffer; ++ char *ptr; ++ char *lim; ++ wchar_t wc = 0; ++ size_t mblength; ++ mbstate_t state, state_bak; ++ ++ memset (&state, 0, sizeof (mbstate_t)); ++ ++ ptr = ptr0; ++ lim = ptr0 + line->buf.length - 1; ++ ++ if (tab == NULL) ++ { ++ /* 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)) ++ break; ++ ptr += mblength; ++ } ++ } ++ ++ for (i = 0; ptr < lim; ++i) ++ { ++ if (tab != NULL) ++ { ++ char *beg = ptr; ++ 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; ++ } ++ mblength = (mblength < 1) ? 1 : mblength; ++ ++ if (mblength == tablen && !memcmp (ptr, tab, mblength)) ++ break; ++ else ++ { ++ ptr += mblength; ++ continue; ++ } ++ } ++ ++ extract_field (line, beg, ptr - beg); ++ if (ptr < lim) ++ ptr += mblength; ++ } ++ else ++ { ++ char *beg = ptr; ++ 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; ++ } ++ mblength = (mblength < 1) ? 1 : mblength; ++ ++ if (iswblank (wc)) ++ break; ++ else ++ { ++ ptr += mblength; ++ continue; ++ } ++ } ++ ++ extract_field (line, beg, ptr - beg); ++ if (ptr < lim) ++ ptr += mblength; ++ } ++ } ++ ++ if (ptr != ptr0) ++ { ++ mblength = mbrtowc (&wc, ptr - mblength, mblength, &state); ++ wc = (mbsinit (&state) && *(ptr - mblength) == '\0') ? L'\0' : wc; ++ if (tab != NULL) ++ { ++ if (mblength == (size_t) -1 || mblength == (size_t) -2) ++ mblength = 1; ++ ++ if (mblength == tablen && !memcmp (ptr - mblength, tab, mblength)) ++ /* Add one more (empty) field because the last character of ++ the line was a delimiter. */ ++ extract_field (line, NULL, 0); ++ } ++ else ++ { ++ if (mblength != (size_t) -1 && mblength != (size_t) -2) ++ { ++ if (iswblank (wc)) ++ /* Add one more (empty) field because the last character of ++ the line was a delimiter. */ ++ extract_field (line, NULL, 0); ++ } ++ } ++ } ++} ++#endif ++ + /* Read a line from FP into LINE and split it into fields. + Return true if successful. */ + +@@ -249,7 +394,13 @@ + line->nfields_allocated = 0; + line->nfields = 0; + line->fields = NULL; +- xfields (line); ++ ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ xfields_multibyte (line); ++ else ++#endif ++ xfields (line); + return true; + } + +@@ -303,56 +454,115 @@ + keycmp (struct line const *line1, struct line const *line2) + { + /* 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; + + if (join_field_1 < line1->nfields) + { +- beg1 = line1->fields[join_field_1].beg; +- len1 = line1->fields[join_field_1].len; ++ beg[0] = line1->fields[join_field_1].beg; ++ len[0] = line1->fields[join_field_1].len; + } + else + { +- beg1 = NULL; +- len1 = 0; ++ beg[0] = NULL; ++ len[0] = 0; + } + + if (join_field_2 < line2->nfields) + { +- beg2 = line2->fields[join_field_2].beg; +- len2 = line2->fields[join_field_2].len; ++ beg[1] = line2->fields[join_field_2].beg; ++ len[1] = line2->fields[join_field_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++) ++ { ++ copy[i] = alloca (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; ++ ++ memset (&state_wc, '\0', sizeof (mbstate_t)); ++ wcrtomb (copy[i] + j, uwc, &state_wc); ++ } ++ else ++ memcpy (copy[i] + j, beg[i] + j, mblength); ++ } ++ j += mblength; ++ } ++ copy[i][j] = '\0'; ++ } ++ return xmemcoll (copy[0], len[0], copy[1], len[1]); ++ } ++#endif ++ if (hard_LC_COLLATE) ++ { ++ for (i = 0; i < 2; i++) ++ { ++ copy[i] = alloca (len[i] + 1); ++ ++ for (j = 0; j < MIN (len[0], len[1]); j++) ++ copy[i][j] = toupper (beg[i][j]); ++ ++ copy[i][j] = '\0'; ++ } ++ return xmemcoll (copy[0], len[0], copy[1], len[1]); ++ } ++ else ++ diff = memcasecmp (beg[0], beg[1], MIN (len[0], len[1])); + } + else + { + if (hard_LC_COLLATE) +- return xmemcoll (beg1, len1, beg2, len2); +- diff = memcmp (beg1, beg2, MIN (len1, len2)); ++ return xmemcoll (beg[0], len[0], beg[1], len[1]); ++ diff = memcmp (beg[0], beg[1], MIN (len[0], len[1])); + } + + if (diff) + return diff; +- return len1 < len2 ? -1 : len1 != len2; ++ return len[0] < len[1] ? -1 : len[0] != len[1]; + } + + /* Print field N of LINE if it exists and is nonempty, otherwise +@@ -381,7 +591,8 @@ + prjoin (struct line const *line1, struct line const *line2) + { + const struct outlist *outlist; +- char output_separator = tab < 0 ? ' ' : tab; ++ const char *output_separator = tab == NULL ? " " : tab; ++ size_t output_separator_len = tab == NULL ? 1 : tablen; + + outlist = outlist_head.next; + if (outlist) +@@ -416,7 +627,7 @@ + o = o->next; + if (o == NULL) + break; +- putchar (output_separator); ++ fwrite (output_separator, 1, output_separator_len, stdout); + } + putchar ('\n'); + } +@@ -434,23 +645,23 @@ + prfield (join_field_1, line1); + for (i = 0; i < join_field_1 && i < line1->nfields; ++i) + { +- putchar (output_separator); ++ fwrite (output_separator, 1, output_separator_len, stdout); + prfield (i, line1); + } + for (i = join_field_1 + 1; i < line1->nfields; ++i) + { +- putchar (output_separator); ++ fwrite (output_separator, 1, output_separator_len, stdout); + prfield (i, line1); + } + + for (i = 0; i < join_field_2 && i < line2->nfields; ++i) + { +- putchar (output_separator); ++ fwrite (output_separator, 1, output_separator_len, stdout); + prfield (i, line2); + } + for (i = join_field_2 + 1; i < line2->nfields; ++i) + { +- putchar (output_separator); ++ fwrite (output_separator, 1, output_separator_len, stdout); + prfield (i, line2); + } + putchar ('\n'); +@@ -862,20 +1073,40 @@ + + case 't': + { +- unsigned char newtab = optarg[0]; +- if (! newtab) ++ const char *newtab = optarg; ++ size_t newtablen; ++ if (! newtab[0]) + error (EXIT_FAILURE, 0, _("empty tab")); +- if (optarg[1]) ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ { ++ mbstate_t state; ++ ++ memset (&state, 0, sizeof (mbstate_t)); ++ newtablen = mbrtowc (NULL, newtab, strlen (newtab), &state); ++ if (newtablen == (size_t) 0 ++ || newtablen == (size_t) -1 || newtablen == (size_t) -2) ++ newtablen = 1; ++ } ++ else ++#endif ++ newtablen = 1; ++ if (optarg[newtablen]) + { + if (STREQ (optarg, "\\0")) +- newtab = '\0'; ++ { ++ newtab = "\0"; ++ newtablen = 1; ++ } + else + error (EXIT_FAILURE, 0, _("multi-character tab %s"), + quote (optarg)); + } +- if (0 <= tab && tab != newtab) ++ if (tab != NULL ++ && (tablen != newtablen || memcmp (tab, newtab, tablen) != 0)) + error (EXIT_FAILURE, 0, _("incompatible tabs")); + tab = newtab; ++ tablen = newtablen; + } + break; + +--- coreutils-6.5/src/pr.c ++++ coreutils-6.5/src/pr.c +@@ -313,6 +313,32 @@ + + #include + #include ++ ++/* Get MB_LEN_MAX. */ ++#include ++/* MB_LEN_MAX is incorrectly defined to be 1 in at least one GCC ++ installation; work around this configuration error. */ ++#if !defined MB_LEN_MAX || MB_LEN_MAX == 1 ++# define MB_LEN_MAX 16 ++#endif ++ ++/* Get MB_CUR_MAX. */ ++#include ++ ++/* Solaris 2.5 has a bug: must be included before . */ ++/* Get mbstate_t, mbrtowc(), wcwidth(). */ ++#if HAVE_WCHAR_H ++# include ++#endif ++ ++/* Get iswprint(). -- for wcwidth(). */ ++#if HAVE_WCTYPE_H ++# include ++#endif ++#if !defined iswprint && !HAVE_ISWPRINT ++# define iswprint(wc) 1 ++#endif ++ + #include "system.h" + #include "error.h" + #include "hard-locale.h" +@@ -324,6 +350,18 @@ + #include "strftime.h" + #include "xstrtol.h" + ++/* Some systems, like BeOS, have multibyte encodings but lack mbstate_t. */ ++#if HAVE_MBRTOWC && defined mbstate_t ++# define mbrtowc(pwc, s, n, ps) (mbrtowc) (pwc, s, n, 0) ++#endif ++ ++#ifndef HAVE_DECL_WCWIDTH ++"this configure-time declaration test was not run" ++#endif ++#if !HAVE_DECL_WCWIDTH ++extern int wcwidth (); ++#endif ++ + /* The official name of this program (e.g., no `g' prefix). */ + #define PROGRAM_NAME "pr" + +@@ -415,8 +453,21 @@ + typedef struct COLUMN COLUMN; + + #define NULLCOL (COLUMN *)0 ++ ++/* Funtion pointers to switch functions for single byte locale or for ++ multibyte locale. If multibyte functions do not exist in your sysytem, ++ these pointers always point the function for single byte locale. */ ++static void (*print_char) (char c); ++static int (*char_to_clump) (char c); ++ ++/* Functions for single byte locale. */ ++static void print_char_single (char c); ++static int char_to_clump_single (char c); ++ ++/* Functions for multibyte locale. */ ++static void print_char_multi (char c); ++static int char_to_clump_multi (char c); + +-static int char_to_clump (char c); + static bool read_line (COLUMN *p); + static bool print_page (void); + static bool print_stored (COLUMN *p); +@@ -426,6 +477,7 @@ + static void pad_across_to (int position); + static void add_line_number (COLUMN *p); + static void getoptarg (char *arg, char switch_char, char *character, ++ int *character_length, int *character_width, + int *number); + void usage (int status); + static void print_files (int number_of_files, char **av); +@@ -440,7 +492,6 @@ + static void pad_down (int lines); + static void read_rest_of_line (COLUMN *p); + static void skip_read (COLUMN *p, int column_number); +-static void print_char (char c); + static void cleanup (void); + static void print_sep_string (void); + static void separator_string (const char *optarg_S); +@@ -455,7 +506,7 @@ + we store the leftmost columns contiguously in buff. + To print a line from buff, get the index of the first character + from line_vector[i], and print up to line_vector[i + 1]. */ +-static char *buff; ++static unsigned char *buff; + + /* Index of the position in buff where the next character + will be stored. */ +@@ -559,7 +610,7 @@ + static bool untabify_input = false; + + /* (-e) The input tab character. */ +-static char input_tab_char = '\t'; ++static char input_tab_char[MB_LEN_MAX] = "\t"; + + /* (-e) Tabstops are at chars_per_tab, 2*chars_per_tab, 3*chars_per_tab, ... + where the leftmost column is 1. */ +@@ -569,7 +620,10 @@ + static bool tabify_output = false; + + /* (-i) The output tab character. */ +-static char output_tab_char = '\t'; ++static char output_tab_char[MB_LEN_MAX] = "\t"; ++ ++/* (-i) The byte length of output tab character. */ ++static int output_tab_char_length = 1; + + /* (-i) The width of the output tab. */ + static int chars_per_output_tab = 8; +@@ -643,7 +697,13 @@ + static bool numbered_lines = false; + + /* (-n) Character which follows each line number. */ +-static char number_separator = '\t'; ++static char number_separator[MB_LEN_MAX] = "\t"; ++ ++/* (-n) The byte length of the character which follows each line number. */ ++static int number_separator_length = 1; ++ ++/* (-n) The character width of the character which follows each line number. */ ++static int number_separator_width = 0; + + /* (-n) line counting starts with 1st line of input file (not with 1st + line of 1st page printed). */ +@@ -696,6 +756,7 @@ + -a|COLUMN|-m is a `space' and with the -J option a `tab'. */ + static char *col_sep_string = ""; + static int col_sep_length = 0; ++static int col_sep_width = 0; + static char *column_separator = " "; + static char *line_separator = "\t"; + +@@ -852,6 +913,13 @@ + col_sep_length = (int) strlen (optarg_S); + col_sep_string = xmalloc (col_sep_length + 1); + strcpy (col_sep_string, optarg_S); ++ ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ col_sep_width = mbswidth (col_sep_string, 0); ++ else ++#endif ++ col_sep_width = col_sep_length; + } + + int +@@ -877,6 +945,21 @@ + + atexit (close_stdout); + ++/* Define which functions are used, the ones for single byte locale or the ones ++ for multibyte locale. */ ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ { ++ print_char = print_char_multi; ++ char_to_clump = char_to_clump_multi; ++ } ++ else ++#endif ++ { ++ print_char = print_char_single; ++ char_to_clump = char_to_clump_single; ++ } ++ + n_files = 0; + file_names = (argc > 1 + ? xmalloc ((argc - 1) * sizeof (char *)) +@@ -949,8 +1032,12 @@ + break; + case 'e': + if (optarg) +- getoptarg (optarg, 'e', &input_tab_char, +- &chars_per_input_tab); ++ { ++ int dummy_length, dummy_width; ++ ++ getoptarg (optarg, 'e', input_tab_char, &dummy_length, ++ &dummy_width, &chars_per_input_tab); ++ } + /* Could check tab width > 0. */ + untabify_input = true; + break; +@@ -963,8 +1050,12 @@ + break; + case 'i': + if (optarg) +- getoptarg (optarg, 'i', &output_tab_char, +- &chars_per_output_tab); ++ { ++ int dummy_width; ++ ++ getoptarg (optarg, 'i', output_tab_char, &output_tab_char_length, ++ &dummy_width, &chars_per_output_tab); ++ } + /* Could check tab width > 0. */ + tabify_output = true; + break; +@@ -991,8 +1082,8 @@ + case 'n': + numbered_lines = true; + if (optarg) +- getoptarg (optarg, 'n', &number_separator, +- &chars_per_number); ++ getoptarg (optarg, 'n', number_separator, &number_separator_length, ++ &number_separator_width, &chars_per_number); + break; + case 'N': + skip_count = false; +@@ -1031,7 +1122,7 @@ + old_s = false; + /* Reset an additional input of -s, -S dominates -s */ + col_sep_string = ""; +- col_sep_length = 0; ++ col_sep_length = col_sep_width = 0; + use_col_separator = true; + if (optarg) + separator_string (optarg); +@@ -1188,10 +1279,45 @@ + a number. */ + + static void +-getoptarg (char *arg, char switch_char, char *character, int *number) ++getoptarg (char *arg, char switch_char, char *character, int *character_length, ++ int *character_width, int *number) + { + if (!ISDIGIT (*arg)) +- *character = *arg++; ++ { ++#ifdef HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) /* for multibyte locale. */ ++ { ++ wchar_t wc; ++ size_t mblength; ++ int width; ++ mbstate_t state = {'\0'}; ++ ++ mblength = mbrtowc (&wc, arg, strlen (arg), &state); ++ ++ if (mblength == (size_t) -1 || mblength == (size_t) -2) ++ { ++ *character_length = 1; ++ *character_width = 1; ++ } ++ else ++ { ++ *character_length = (mblength < 1) ? 1 : mblength; ++ width = wcwidth (wc); ++ *character_width = (width < 0) ? 0 : width; ++ } ++ ++ strncpy (character, arg, *character_length); ++ arg += *character_length; ++ } ++ else /* for single byte locale. */ ++#endif ++ { ++ *character = *arg++; ++ *character_length = 1; ++ *character_width = 1; ++ } ++ } ++ + if (*arg) + { + long int tmp_long; +@@ -1256,7 +1382,7 @@ + else + col_sep_string = column_separator; + +- col_sep_length = 1; ++ col_sep_length = col_sep_width = 1; + use_col_separator = true; + } + /* It's rather pointless to define a TAB separator with column +@@ -1288,11 +1414,11 @@ + TAB_WIDTH (chars_per_input_tab, chars_per_number); */ + + /* Estimate chars_per_text without any margin and keep it constant. */ +- if (number_separator == '\t') ++ if (number_separator[0] == '\t') + number_width = chars_per_number + + TAB_WIDTH (chars_per_default_tab, chars_per_number); + else +- number_width = chars_per_number + 1; ++ number_width = chars_per_number + number_separator_width; + + /* The number is part of the column width unless we are + printing files in parallel. */ +@@ -1307,7 +1433,7 @@ + } + + chars_per_column = (chars_per_line - chars_used_by_number - +- (columns - 1) * col_sep_length) / columns; ++ (columns - 1) * col_sep_width) / columns; + + if (chars_per_column < 1) + error (EXIT_FAILURE, 0, _("page width too narrow")); +@@ -1432,7 +1558,7 @@ + + /* Enlarge p->start_position of first column to use the same form of + padding_not_printed with all columns. */ +- h = h + col_sep_length; ++ h = h + col_sep_width; + + /* This loop takes care of all but the rightmost column. */ + +@@ -1466,7 +1592,7 @@ + } + else + { +- h = h_next + col_sep_length; ++ h = h_next + col_sep_width; + h_next = h + chars_per_column; + } + } +@@ -1756,9 +1882,9 @@ + align_column (COLUMN *p) + { + padding_not_printed = p->start_position; +- if (padding_not_printed - col_sep_length > 0) ++ if (padding_not_printed - col_sep_width > 0) + { +- pad_across_to (padding_not_printed - col_sep_length); ++ pad_across_to (padding_not_printed - col_sep_width); + padding_not_printed = ANYWHERE; + } + +@@ -2029,13 +2155,13 @@ + /* May be too generous. */ + buff = X2REALLOC (buff, &buff_allocated); + } +- buff[buff_current++] = c; ++ buff[buff_current++] = (unsigned char) c; + } + + static void + add_line_number (COLUMN *p) + { +- int i; ++ int i, j; + char *s; + int left_cut; + +@@ -2058,22 +2184,24 @@ + /* Tabification is assumed for multiple columns, also for n-separators, + but `default n-separator = TAB' hasn't been given priority over + equal column_width also specified by POSIX. */ +- if (number_separator == '\t') ++ if (number_separator[0] == '\t') + { + i = number_width - chars_per_number; + while (i-- > 0) + (p->char_func) (' '); + } + else +- (p->char_func) (number_separator); ++ for (j = 0; j < number_separator_length; j++) ++ (p->char_func) (number_separator[j]); + } + else + /* To comply with POSIX, we avoid any expansion of default TAB + separator with a single column output. No column_width requirement + has to be considered. */ + { +- (p->char_func) (number_separator); +- if (number_separator == '\t') ++ for (j = 0; j < number_separator_length; j++) ++ (p->char_func) (number_separator[j]); ++ if (number_separator[0] == '\t') + output_position = POS_AFTER_TAB (chars_per_output_tab, + output_position); + } +@@ -2234,7 +2362,7 @@ + while (goal - h_old > 1 + && (h_new = POS_AFTER_TAB (chars_per_output_tab, h_old)) <= goal) + { +- putchar (output_tab_char); ++ fwrite (output_tab_char, 1, output_tab_char_length, stdout); + h_old = h_new; + } + while (++h_old <= goal) +@@ -2254,6 +2382,7 @@ + { + char *s; + int l = col_sep_length; ++ int not_space_flag; + + s = col_sep_string; + +@@ -2267,6 +2396,7 @@ + { + for (; separators_not_printed > 0; --separators_not_printed) + { ++ not_space_flag = 0; + while (l-- > 0) + { + /* 3 types of sep_strings: spaces only, spaces and chars, +@@ -2280,12 +2410,15 @@ + } + else + { ++ not_space_flag = 1; + if (spaces_not_printed > 0) + print_white_space (); + putchar (*s++); +- ++output_position; + } + } ++ if (not_space_flag) ++ output_position += col_sep_width; ++ + /* sep_string ends with some spaces */ + if (spaces_not_printed > 0) + print_white_space (); +@@ -2312,8 +2445,9 @@ + a nonspace is encountered, call print_white_space() to print the + required number of tabs and spaces. */ + ++ + static void +-print_char (char c) ++print_char_single (char c) + { + if (tabify_output) + { +@@ -2337,6 +2471,75 @@ + putchar (c); + } + ++#ifdef HAVE_MBRTOWC ++static void ++print_char_multi (char c) ++{ ++ static size_t mbc_pos = 0; ++ static unsigned char mbc[MB_LEN_MAX] = {'\0'}; ++ static mbstate_t state = {'\0'}; ++ mbstate_t state_bak; ++ wchar_t wc; ++ unsigned char uc = (unsigned char) c; ++ size_t mblength; ++ int width; ++ ++ if (tabify_output) ++ { ++ state_bak = state; ++ mbc[mbc_pos++] = uc; ++ mblength = mbrtowc (&wc, mbc, mbc_pos, &state); ++ ++ while (mbc_pos > 0) ++ { ++ switch (mblength) ++ { ++ case (size_t) -2: ++ state = state_bak; ++ return; ++ ++ case (size_t) -1: ++ state = state_bak; ++ ++output_position; ++ putchar (mbc[0]); ++ memmove (mbc, mbc + 1, MB_CUR_MAX - 1); ++ --mbc_pos; ++ break; ++ ++ case 0: ++ mblength = 1; ++ ++ default: ++ if (wc == L' ') ++ { ++ memmove (mbc, mbc + mblength, MB_CUR_MAX - mblength); ++ --mbc_pos; ++ ++spaces_not_printed; ++ return; ++ } ++ else if (spaces_not_printed > 0) ++ print_white_space (); ++ ++ /* Nonprintables are assumed to have width 0, except L'\b'. */ ++ if ((width = wcwidth (wc)) < 1) ++ { ++ if (wc == L'\b') ++ --output_position; ++ } ++ else ++ output_position += width; ++ ++ fwrite (mbc, 1, mblength, stdout); ++ memmove (mbc, mbc + mblength, MB_CUR_MAX - mblength); ++ mbc_pos -= mblength; ++ } ++ } ++ return; ++ } ++ putchar (uc); ++} ++#endif ++ + /* Skip to page PAGE before printing. + PAGE may be larger than total number of pages. */ + +@@ -2517,9 +2720,9 @@ + align_empty_cols = false; + } + +- if (padding_not_printed - col_sep_length > 0) ++ if (padding_not_printed - col_sep_width > 0) + { +- pad_across_to (padding_not_printed - col_sep_length); ++ pad_across_to (padding_not_printed - col_sep_width); + padding_not_printed = ANYWHERE; + } + +@@ -2620,9 +2823,9 @@ + } + } + +- if (padding_not_printed - col_sep_length > 0) ++ if (padding_not_printed - col_sep_width > 0) + { +- pad_across_to (padding_not_printed - col_sep_length); ++ pad_across_to (padding_not_printed - col_sep_width); + padding_not_printed = ANYWHERE; + } + +@@ -2635,8 +2838,8 @@ + if (spaces_not_printed == 0) + { + output_position = p->start_position + end_vector[line]; +- if (p->start_position - col_sep_length == chars_per_margin) +- output_position -= col_sep_length; ++ if (p->start_position - col_sep_width == chars_per_margin) ++ output_position -= col_sep_width; + } + + return true; +@@ -2654,8 +2857,9 @@ + characters in clump_buff. (e.g, the width of '\b' is -1, while the + number of characters is 1.) */ + ++ + static int +-char_to_clump (char c) ++char_to_clump_single (char c) + { + unsigned char uc = c; + char *s = clump_buff; +@@ -2665,10 +2869,10 @@ + int chars; + int chars_per_c = 8; + +- if (c == input_tab_char) ++ if (c == input_tab_char[0]) + chars_per_c = chars_per_input_tab; + +- if (c == input_tab_char || c == '\t') ++ if (c == input_tab_char[0] || c == '\t') + { + width = TAB_WIDTH (chars_per_c, input_position); + +@@ -2739,6 +2943,155 @@ + return chars; + } + ++#ifdef HAVE_MBRTOWC ++static int ++char_to_clump_multi (char c) ++{ ++ static size_t mbc_pos = 0; ++ static unsigned char mbc[MB_LEN_MAX] = {'\0'}; ++ static mbstate_t state = {'\0'}; ++ mbstate_t state_bak; ++ wchar_t wc; ++ unsigned char uc = (unsigned char) c; ++ size_t mblength; ++ int wc_width; ++ register char *s = clump_buff; ++ register int i, j; ++ char esc_buff[4]; ++ int width; ++ int chars; ++ int chars_per_c = 8; ++ ++ state_bak = state; ++ mbc[mbc_pos++] = uc; ++ mblength = mbrtowc (&wc, mbc, mbc_pos, &state); ++ ++ width = 0; ++ chars = 0; ++ while (mbc_pos > 0) ++ { ++ switch (mblength) ++ { ++ case (size_t) -2: ++ state = state_bak; ++ return 0; ++ ++ case (size_t) -1: ++ state = state_bak; ++ mblength = 1; ++ ++ if (use_esc_sequence || use_cntrl_prefix) ++ { ++ width = +4; ++ chars = +4; ++ *s++ = '\\'; ++ sprintf (esc_buff, "%03o", mbc[0]); ++ for (i = 0; i <= 2; ++i) ++ *s++ = (int) esc_buff[i]; ++ } ++ else ++ { ++ width += 1; ++ chars += 1; ++ *s++ = mbc[0]; ++ } ++ break; ++ ++ case 0: ++ mblength = 1; ++ /* Fall through */ ++ ++ default: ++ if (memcmp (mbc, input_tab_char, mblength) == 0) ++ chars_per_c = chars_per_input_tab; ++ ++ if (memcmp (mbc, input_tab_char, mblength) == 0 || c == '\t') ++ { ++ int width_inc; ++ ++ width_inc = TAB_WIDTH (chars_per_c, input_position); ++ width += width_inc; ++ ++ if (untabify_input) ++ { ++ for (i = width_inc; i; --i) ++ *s++ = ' '; ++ chars += width_inc; ++ } ++ else ++ { ++ for (i = 0; i < mblength; i++) ++ *s++ = mbc[i]; ++ chars += mblength; ++ } ++ } ++ else if ((wc_width = wcwidth (wc)) < 1) ++ { ++ if (use_esc_sequence) ++ { ++ for (i = 0; i < mblength; i++) ++ { ++ width += 4; ++ chars += 4; ++ *s++ = '\\'; ++ sprintf (esc_buff, "%03o", uc); ++ for (j = 0; j <= 2; ++j) ++ *s++ = (int) esc_buff[j]; ++ } ++ } ++ else if (use_cntrl_prefix) ++ { ++ if (wc < 0200) ++ { ++ width += 2; ++ chars += 2; ++ *s++ = '^'; ++ *s++ = wc ^ 0100; ++ } ++ else ++ { ++ for (i = 0; i < mblength; i++) ++ { ++ width += 4; ++ chars += 4; ++ *s++ = '\\'; ++ sprintf (esc_buff, "%03o", uc); ++ for (j = 0; j <= 2; ++j) ++ *s++ = (int) esc_buff[j]; ++ } ++ } ++ } ++ else if (wc == L'\b') ++ { ++ width += -1; ++ chars += 1; ++ *s++ = c; ++ } ++ else ++ { ++ width += 0; ++ chars += mblength; ++ for (i = 0; i < mblength; i++) ++ *s++ = mbc[i]; ++ } ++ } ++ else ++ { ++ width += wc_width; ++ chars += mblength; ++ for (i = 0; i < mblength; i++) ++ *s++ = mbc[i]; ++ } ++ } ++ memmove (mbc, mbc + mblength, MB_CUR_MAX - mblength); ++ mbc_pos -= mblength; ++ } ++ ++ input_position += width; ++ return chars; ++} ++#endif ++ + /* We've just printed some files and need to clean up things before + looking for more options and printing the next batch of files. + +--- coreutils-6.5/src/sort.c ++++ coreutils-6.5/src/sort.c +@@ -26,6 +26,19 @@ + #include + #include + #include ++#include ++ ++/* Get mbstate_t, mbrtowc(), wcrtomb(). */ ++#if HAVE_WCHAR_H ++# include ++#endif ++ ++/* Get iswprint(), iswctype() towupper(). */ ++#if HAVE_WCTYPE_H ++# include ++wctype_t blank_type; /* = wctype ("blank"); */ ++#endif ++ + #include "system.h" + #include "error.h" + #include "hard-locale.h" +@@ -50,6 +63,17 @@ + # define getrlimit(Resource, Rlp) (-1) + #endif + ++/* MB_LEN_MAX is incorrectly defined to be 1 in at least one GCC ++ installation; work around this configuration error. */ ++#if !defined MB_LEN_MAX || MB_LEN_MAX == 1 ++# define MB_LEN_MAX 16 ++#endif ++ ++/* 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 "sort" + +@@ -98,14 +122,38 @@ + /* Thousands separator; if -1, then there isn't one. */ + static int thousands_sep; + ++static int force_general_numcompare = 0; ++ + /* Nonzero if the corresponding locales are hard. */ + static bool hard_LC_COLLATE; +-#if HAVE_NL_LANGINFO ++#if HAVE_LANGINFO_CODESET + static bool hard_LC_TIME; + #endif + + #define NONZERO(x) ((x) != 0) + ++/* get a multibyte character's byte length. */ ++#define GET_BYTELEN_OF_CHAR(LIM, PTR, MBLENGTH, STATE) \ ++ do \ ++ { \ ++ wchar_t wc; \ ++ mbstate_t state_bak; \ ++ \ ++ state_bak = STATE; \ ++ mblength = mbrtowc (&wc, PTR, LIM - PTR, &STATE); \ ++ \ ++ switch (MBLENGTH) \ ++ { \ ++ case (size_t)-1: \ ++ case (size_t)-2: \ ++ STATE = state_bak; \ ++ /* Fall through. */ \ ++ case 0: \ ++ MBLENGTH = 1; \ ++ } \ ++ } \ ++ while (0) ++ + /* The kind of blanks for '-b' to skip in various options. */ + enum blanktype { bl_start, bl_end, bl_both }; + +@@ -243,13 +291,11 @@ + they were read if all keys compare equal. */ + static bool stable; + +-/* If TAB has this value, blanks separate fields. */ +-enum { TAB_DEFAULT = CHAR_MAX + 1 }; +- +-/* Tab character separating fields. If TAB_DEFAULT, then fields are +- separated by the empty string between a non-blank character and a blank ++/* Tab character separating fields. If NULL, then fields are separated by ++ the empty string between a non-blank character and a blank + character. */ +-static int tab = TAB_DEFAULT; ++static const char *tab; ++static size_t tab_length = 1; + + /* Flag to remove consecutive duplicate lines from the output. + Only the last of a sequence of equal lines will be output. */ +@@ -408,6 +454,43 @@ + static struct tempnode *volatile temphead; + static struct tempnode *volatile *temptail = &temphead; + ++/* Fucntion pointers. */ ++static char * ++(* begfield) (const struct line *line, const struct keyfield *key); ++ ++static char * ++(* limfield) (const struct line *line, const struct keyfield *key); ++ ++static int ++(*getmonth) (const char *s, size_t len); ++ ++static int ++(* keycompare) (const struct line *a, const struct line *b); ++ ++/* Test for white space multibyte character. ++ Set LENGTH the byte length of investigated multibyte character. */ ++#if HAVE_MBRTOWC ++static int ++ismbblank (const char *str, size_t *length) ++{ ++ size_t mblength; ++ wchar_t wc; ++ mbstate_t state; ++ ++ memset (&state, '\0', sizeof(mbstate_t)); ++ mblength = mbrtowc (&wc, str, MB_LEN_MAX, &state); ++ ++ if (mblength == (size_t)-1 || mblength == (size_t)-2) ++ { ++ *length = 1; ++ return 0; ++ } ++ ++ *length = (mblength < 1) ? 1 : mblength; ++ return (iswctype (wc, blank_type)); ++} ++#endif ++ + /* Clean up any remaining temporary files. */ + + static void +@@ -561,7 +644,7 @@ + free (node); + } + +-#if HAVE_NL_LANGINFO ++#if HAVE_LANGINFO_CODESET + + static int + struct_month_cmp (const void *m1, const void *m2) +@@ -588,7 +671,7 @@ + fold_toupper[i] = toupper (i); + } + +-#if HAVE_NL_LANGINFO ++#if HAVE_LANGINFO_CODESET + /* If we're not in the "C" locale, read different names for months. */ + if (hard_LC_TIME) + { +@@ -614,6 +697,71 @@ + #endif + } + ++#if HAVE_MBRTOWC ++static void ++inittables_mb (void) ++{ ++ int i, j, k, l; ++ char *name, *s; ++ size_t s_len, mblength; ++ char mbc[MB_LEN_MAX]; ++ wchar_t wc, pwc; ++ mbstate_t state_mb, state_wc; ++ ++ for (i = 0; i < MONTHS_PER_YEAR; i++) ++ { ++ s = (char *) nl_langinfo (ABMON_1 + i); ++ s_len = strlen (s); ++ monthtab[i].name = name = (char *) xmalloc (s_len + 1); ++ monthtab[i].val = i + 1; ++ ++ memset (&state_mb, '\0', sizeof (mbstate_t)); ++ memset (&state_wc, '\0', sizeof (mbstate_t)); ++ ++ for (j = 0; j < s_len;) ++ { ++ if (!ismbblank (s + j, &mblength)) ++ break; ++ j += mblength; ++ } ++ ++ for (k = 0; j < s_len;) ++ { ++ mblength = mbrtowc (&wc, (s + j), (s_len - j), &state_mb); ++ /* If conversion is failed, fall back into single byte sorting. */ ++ if (mblength == (size_t) -1 || mblength == (size_t) -2) ++ { ++ for (l = 0; l <= i; l++) ++ free ((void *) monthtab[l].name); ++ inittables(); ++ return; ++ } ++ else if (mblength == 0) ++ break; ++ ++ pwc = towupper (wc); ++ if (pwc == wc) ++ { ++ memcpy (mbc, s + j, mblength); ++ j += mblength; ++ } ++ else ++ { ++ j += mblength; ++ mblength = wcrtomb (mbc, wc, &state_wc); ++ assert (mblength != (size_t) 0 && mblength != (size_t) -1); ++ } ++ ++ for (l = 0; l < mblength; l++) ++ name[k++] = mbc[l]; ++ } ++ name[k] = '\0'; ++ } ++ qsort ((void *) monthtab, MONTHS_PER_YEAR, ++ sizeof *monthtab, struct_month_cmp); ++} ++#endif ++ + /* Specify the amount of main memory to use when sorting. */ + static void + specify_sort_size (char const *s) +@@ -824,7 +972,7 @@ + by KEY in LINE. */ + + static char * +-begfield (const struct line *line, const struct keyfield *key) ++begfield_uni (const struct line *line, const struct keyfield *key) + { + char *ptr = line->text, *lim = ptr + line->length - 1; + size_t sword = key->sword; +@@ -834,10 +982,10 @@ + /* The leading field separator itself is included in a field when -t + is absent. */ + +- if (tab != TAB_DEFAULT) ++ if (tab != NULL) + while (ptr < lim && sword--) + { +- while (ptr < lim && *ptr != tab) ++ while (ptr < lim && *ptr != tab[0]) + ++ptr; + if (ptr < lim) + ++ptr; +@@ -865,11 +1013,70 @@ + return ptr; + } + ++#if HAVE_MBRTOWC ++static char * ++begfield_mb (const struct line *line, const struct keyfield *key) ++{ ++ int i; ++ char *ptr = line->text, *lim = ptr + line->length - 1; ++ size_t sword = key->sword; ++ size_t schar = key->schar; ++ size_t mblength; ++ mbstate_t state; ++ ++ memset (&state, '\0', sizeof(mbstate_t)); ++ ++ if (tab != NULL) ++ while (ptr < lim && sword--) ++ { ++ while (ptr < lim && memcmp (ptr, tab, tab_length) != 0) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ptr += mblength; ++ } ++ if (ptr < lim) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ptr += mblength; ++ } ++ } ++ else ++ while (ptr < lim && sword--) ++ { ++ while (ptr < lim && ismbblank (ptr, &mblength)) ++ ptr += mblength; ++ if (ptr < lim) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ptr += mblength; ++ } ++ while (ptr < lim && !ismbblank (ptr, &mblength)) ++ ptr += mblength; ++ } ++ ++ if (key->skipsblanks) ++ while (ptr < lim && ismbblank (ptr, &mblength)) ++ ptr += mblength; ++ ++ for (i = 0; i < schar; i++) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ++ if (ptr + mblength > lim) ++ break; ++ else ++ ptr += mblength; ++ } ++ ++ return ptr; ++} ++#endif ++ + /* Return the limit of (a pointer to the first character after) the field + in LINE specified by KEY. */ + + static char * +-limfield (const struct line *line, const struct keyfield *key) ++limfield_uni (const struct line *line, const struct keyfield *key) + { + char *ptr = line->text, *lim = ptr + line->length - 1; + size_t eword = key->eword, echar = key->echar; +@@ -882,10 +1089,10 @@ + `beginning' is the first character following the delimiting TAB. + Otherwise, leave PTR pointing at the first `blank' character after + the preceding field. */ +- if (tab != TAB_DEFAULT) ++ if (tab != NULL) + while (ptr < lim && eword--) + { +- while (ptr < lim && *ptr != tab) ++ while (ptr < lim && *ptr != tab[0]) + ++ptr; + if (ptr < lim && (eword | echar)) + ++ptr; +@@ -931,7 +1138,7 @@ + */ + + /* Make LIM point to the end of (one byte past) the current field. */ +- if (tab != TAB_DEFAULT) ++ if (tab != NULL) + { + char *newlim; + newlim = memchr (ptr, tab, lim - ptr); +@@ -967,6 +1174,107 @@ + return ptr; + } + ++#if HAVE_MBRTOWC ++static char * ++limfield_mb (const struct line *line, const struct keyfield *key) ++{ ++ char *ptr = line->text, *lim = ptr + line->length - 1; ++ size_t eword = key->eword, echar = key->echar; ++ int i; ++ size_t mblength; ++ mbstate_t state; ++ ++ memset (&state, '\0', sizeof(mbstate_t)); ++ ++ if (tab != NULL) ++ while (ptr < lim && eword--) ++ { ++ while (ptr < lim && memcmp (ptr, tab, tab_length) != 0) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ptr += mblength; ++ } ++ if (ptr < lim) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ptr += mblength; ++ } ++ } ++ else ++ while (ptr < lim && eword--) ++ { ++ while (ptr < lim && ismbblank (ptr, &mblength)) ++ ptr += mblength; ++ if (ptr < lim) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ptr += mblength; ++ } ++ while (ptr < lim && !ismbblank (ptr, &mblength)) ++ ptr += mblength; ++ } ++ ++# ifdef POSIX_UNSPECIFIED ++ ++ /* Make LIM point to the end of (one byte past) the current field. */ ++ if (tab != NULL) ++ { ++ char *newlim, *p; ++ ++ newlim = NULL; ++ for (p = ptr; p < lim;) ++ { ++ if (memcmp (p, tab, tab_length) == 0) ++ { ++ newlim = p; ++ break; ++ } ++ ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ p += mblength; ++ } ++ } ++ else ++ { ++ char *newlim; ++ newlim = ptr; ++ ++ while (newlim < lim && ismbblank (newlim, &mblength)) ++ newlim += mblength; ++ if (ptr < lim) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ptr += mblength; ++ } ++ while (newlim < lim && !ismbblank (newlim, &mblength)) ++ newlim += mblength; ++ lim = newlim; ++ } ++# endif ++ ++ /* If we're skipping leading blanks, don't start counting characters ++ until after skipping past any leading blanks. */ ++ if (key->skipeblanks) ++ while (ptr < lim && ismbblank (ptr, &mblength)) ++ ptr += mblength; ++ ++ memset (&state, '\0', sizeof(mbstate_t)); ++ ++ /* Advance PTR by ECHAR (if possible), but no further than LIM. */ ++ for (i = 0; i < echar; i++) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ++ if (ptr + mblength > lim) ++ break; ++ else ++ ptr += mblength; ++ } ++ ++ return ptr; ++} ++#endif ++ + /* 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 +@@ -1049,8 +1357,22 @@ + else + { + if (key->skipsblanks) +- while (blanks[to_uchar (*line_start)]) +- line_start++; ++ { ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ { ++ size_t mblength; ++ ++ while (ismbblank (line_start, &mblength)) ++ line_start += mblength; ++ } ++ else ++#endif ++ { ++ while (blanks[to_uchar (*line_start)]) ++ line_start++; ++ } ++ } + line->keybeg = line_start; + } + } +@@ -1100,15 +1422,59 @@ + /* FIXME: maybe add option to try expensive FP conversion + only if A and B can't be compared more cheaply/accurately. */ + +- char *ea; +- char *eb; +- double a = strtod (sa, &ea); +- double b = strtod (sb, &eb); ++ char *bufa, *ea; ++ char *bufb, *eb; ++ double a; ++ double b; ++ ++ char *p; ++ struct lconv *lconvp = localeconv (); ++ size_t thousands_sep_len = strlen (lconvp->thousands_sep); ++ ++ bufa = (char *) xmalloc (strlen (sa) + 1); ++ bufb = (char *) xmalloc (strlen (sb) + 1); ++ strcpy (bufa, sa); ++ strcpy (bufb, sb); ++ ++ if (force_general_numcompare) ++ { ++ while (1) ++ { ++ a = strtod (bufa, &ea); ++ if (memcmp (ea, lconvp->thousands_sep, thousands_sep_len) == 0) ++ { ++ for (p = ea; *(p + thousands_sep_len) != '\0'; p++) ++ *p = *(p + thousands_sep_len); ++ *p = '\0'; ++ continue; ++ } ++ break; ++ } ++ ++ while (1) ++ { ++ b = strtod (bufb, &eb); ++ if (memcmp (eb, lconvp->thousands_sep, thousands_sep_len) == 0) ++ { ++ for (p = eb; *(p + thousands_sep_len) != '\0'; p++) ++ *p = *(p + thousands_sep_len); ++ *p = '\0'; ++ continue; ++ } ++ break; ++ } ++ } ++ else ++ { ++ a = strtod (bufa, &ea); ++ b = strtod (bufb, &eb); ++ } ++ + + /* Put conversion errors at the start of the collating sequence. */ +- if (sa == ea) +- return sb == eb ? 0 : -1; +- if (sb == eb) ++ if (bufa == ea) ++ return bufb == eb ? 0 : -1; ++ if (bufb == eb) + return 1; + + /* Sort numbers in the usual way, where -0 == +0. Put NaNs after +@@ -1126,7 +1492,7 @@ + Return 0 if the name in S is not recognized. */ + + static int +-getmonth (char const *month, size_t len) ++getmonth_uni (char const *month, size_t len) + { + size_t lo = 0; + size_t hi = MONTHS_PER_YEAR; +@@ -1281,11 +1647,79 @@ + return diff; + } + ++#if HAVE_MBRTOWC ++static int ++getmonth_mb (char const *s, size_t len) ++{ ++ char *month; ++ register size_t i; ++ register int lo = 0, hi = MONTHS_PER_YEAR, result; ++ char *tmp; ++ size_t wclength, mblength; ++ const char **pp; ++ const wchar_t **wpp; ++ wchar_t *month_wcs; ++ mbstate_t state; ++ ++ while (len > 0 && ismbblank (s, &mblength)) ++ { ++ s += mblength; ++ len -= mblength; ++ } ++ ++ if (len == 0) ++ return 0; ++ ++ month = (char *) alloca (len + 1); ++ ++ tmp = (char *) alloca (len + 1); ++ memcpy (tmp, s, len); ++ tmp[len] = '\0'; ++ pp = (const char **) &tmp; ++ month_wcs = (wchar_t *) alloca ((len + 1) * sizeof (wchar_t)); ++ memset (&state, '\0', sizeof (mbstate_t)); ++ ++ wclength = mbsrtowcs (month_wcs, pp, len + 1, &state); ++ assert (wclength != 1 && *pp == NULL); ++ ++ for (i = 0; i < wclength; i++) ++ { ++ month_wcs[i] = towupper (month_wcs[i]); ++ if (iswctype (month_wcs[i], blank_type)) ++ { ++ month_wcs[i] = L'\0'; ++ break; ++ } ++ } ++ ++ wpp = (const wchar_t **) &month_wcs; ++ ++ mblength = wcsrtombs (month, wpp, len + 1, &state); ++ assert (mblength != (-1) && *wpp == NULL); ++ ++ do ++ { ++ int ix = (lo + hi) / 2; ++ ++ if (strncmp (month, monthtab[ix].name, strlen (monthtab[ix].name)) < 0) ++ hi = ix; ++ else ++ lo = ix; ++ } ++ while (hi - lo > 1); ++ ++ result = (!strncmp (month, monthtab[lo].name, strlen (monthtab[lo].name)) ++ ? monthtab[lo].val : 0); ++ ++ return result; ++} ++#endif ++ + /* Compare two lines A and B trying every key in sequence until there + are no more keys or a difference is found. */ + + static int +-keycompare (const struct line *a, const struct line *b) ++keycompare_uni (const struct line *a, const struct line *b) + { + struct keyfield const *key = keylist; + +@@ -1452,12 +1886,189 @@ + + return 0; + +- greater: ++greater: + diff = 1; +- not_equal: ++not_equal: + return key->reverse ? -diff : diff; + } + ++#if HAVE_MBRTOWC ++static int ++keycompare_mb (const struct line *a, const struct line *b) ++{ ++ struct keyfield *key = keylist; ++ ++ /* For the first iteration only, the key positions have been ++ precomputed for us. */ ++ char *texta = a->keybeg; ++ char *textb = b->keybeg; ++ char *lima = a->keylim; ++ char *limb = b->keylim; ++ ++ size_t mblength_a, mblength_b; ++ wchar_t wc_a, wc_b; ++ mbstate_t state_a, state_b; ++ ++ int diff; ++ ++ memset (&state_a, '\0', sizeof (mbstate_t)); ++ memset (&state_b, '\0', sizeof (mbstate_t)); ++ ++ for (;;) ++ { ++ register char const *translate = key->translate; ++ register bool const *ignore = key->ignore; ++ ++ /* Find the lengths. */ ++ size_t lena = lima <= texta ? 0 : lima - texta; ++ size_t lenb = limb <= textb ? 0 : limb - textb; ++ ++ /* Actually compare the fields. */ ++ if (key->numeric | key->general_numeric) ++ { ++ char savea = *lima, saveb = *limb; ++ ++ *lima = *limb = '\0'; ++ if (force_general_numcompare) ++ diff = general_numcompare (texta, textb); ++ else ++ diff = ((key->numeric ? numcompare : general_numcompare) ++ (texta, textb)); ++ *lima = savea, *limb = saveb; ++ } ++ else if (key->month) ++ diff = getmonth (texta, lena) - getmonth (textb, lenb); ++ else ++ { ++ if (ignore || translate) ++ { ++ char buf[4000]; ++ size_t size = lena + 1 + lenb + 1; ++ char *copy_a = (size <= sizeof buf ? buf : xmalloc (size)); ++ char *copy_b = copy_a + lena + 1; ++ size_t new_len_a, new_len_b; ++ size_t i, j; ++ ++ /* Ignore and/or translate chars before comparing. */ ++# define IGNORE_CHARS(NEW_LEN, LEN, TEXT, COPY, WC, MBLENGTH, STATE) \ ++ do \ ++ { \ ++ wchar_t uwc; \ ++ char mbc[MB_LEN_MAX]; \ ++ mbstate_t state_wc; \ ++ \ ++ for (NEW_LEN = i = 0; i < LEN;) \ ++ { \ ++ mbstate_t state_bak; \ ++ \ ++ state_bak = STATE; \ ++ MBLENGTH = mbrtowc (&WC, TEXT + i, LEN - i, &STATE); \ ++ \ ++ if (MBLENGTH == (size_t)-2 || MBLENGTH == (size_t)-1 \ ++ || MBLENGTH == 0) \ ++ { \ ++ if (MBLENGTH == (size_t)-2 || MBLENGTH == (size_t)-1) \ ++ STATE = state_bak; \ ++ if (!ignore) \ ++ COPY[NEW_LEN++] = TEXT[i++]; \ ++ continue; \ ++ } \ ++ \ ++ if (ignore) \ ++ { \ ++ if ((ignore == nonprinting && !iswprint (WC)) \ ++ || (ignore == nondictionary \ ++ && !iswalnum (WC) && !iswctype (WC, blank_type))) \ ++ { \ ++ i += MBLENGTH; \ ++ continue; \ ++ } \ ++ } \ ++ \ ++ if (translate) \ ++ { \ ++ \ ++ uwc = toupper(WC); \ ++ if (WC == uwc) \ ++ { \ ++ memcpy (mbc, TEXT + i, MBLENGTH); \ ++ i += MBLENGTH; \ ++ } \ ++ else \ ++ { \ ++ i += MBLENGTH; \ ++ WC = uwc; \ ++ memset (&state_wc, '\0', sizeof (mbstate_t)); \ ++ \ ++ MBLENGTH = wcrtomb (mbc, WC, &state_wc); \ ++ assert (MBLENGTH != (size_t)-1 && MBLENGTH != 0); \ ++ } \ ++ \ ++ for (j = 0; j < MBLENGTH; j++) \ ++ COPY[NEW_LEN++] = mbc[j]; \ ++ } \ ++ else \ ++ for (j = 0; j < MBLENGTH; j++) \ ++ COPY[NEW_LEN++] = TEXT[i++]; \ ++ } \ ++ COPY[NEW_LEN] = '\0'; \ ++ } \ ++ while (0) ++ ++ IGNORE_CHARS (new_len_a, lena, texta, copy_a, ++ wc_a, mblength_a, state_a); ++ IGNORE_CHARS (new_len_b, lenb, textb, copy_b, ++ wc_b, mblength_b, state_b); ++ diff = xmemcoll (copy_a, new_len_a, copy_b, new_len_b); ++ ++ if (sizeof buf < size) ++ free (copy_a); ++ } ++ else if (lena == 0) ++ diff = - NONZERO (lenb); ++ else if (lenb == 0) ++ goto greater; ++ else ++ diff = xmemcoll (texta, lena, textb, lenb); ++ } ++ ++ if (diff) ++ goto not_equal; ++ ++ key = key->next; ++ if (! key) ++ break; ++ ++ /* Find the beginning and limit of the next field. */ ++ if (key->eword != SIZE_MAX) ++ lima = limfield (a, key), limb = limfield (b, key); ++ else ++ lima = a->text + a->length - 1, limb = b->text + b->length - 1; ++ ++ if (key->sword != SIZE_MAX) ++ texta = begfield (a, key), textb = begfield (b, key); ++ else ++ { ++ texta = a->text, textb = b->text; ++ if (key->skipsblanks) ++ { ++ while (texta < lima && ismbblank (texta, &mblength_a)) ++ texta += mblength_a; ++ while (textb < limb && ismbblank (textb, &mblength_b)) ++ textb += mblength_b; ++ } ++ } ++ } ++ ++ return 0; ++ ++greater: ++ diff = 1; ++not_equal: ++ return key->reverse ? -diff : diff; ++} ++#endif ++ + /* Compare two lines A and B, returning negative, zero, or positive + depending on whether A compares less than, equal to, or greater than B. */ + +@@ -2253,6 +2864,11 @@ + break; + case 'M': + key->month = true; ++#if HAVE_MBRTOWC ++ if (strcmp (setlocale (LC_CTYPE, NULL), setlocale (LC_TIME, NULL))) ++ error (0, 0, _("As LC_TIME differs from LC_CTYPE, the results may be strange.")); ++ inittables_mb (); ++#endif + break; + case 'n': + key->numeric = true; +@@ -2309,7 +2925,7 @@ + atexit (close_stdout); + + hard_LC_COLLATE = hard_locale (LC_COLLATE); +-#if HAVE_NL_LANGINFO ++#if HAVE_LANGINFO_CODESET + hard_LC_TIME = hard_locale (LC_TIME); + #endif + +@@ -2322,14 +2938,40 @@ + add support for multibyte decimal points. */ + decimal_point = to_uchar (locale->decimal_point[0]); + if (! decimal_point || locale->decimal_point[1]) +- decimal_point = '.'; ++ { ++ decimal_point = '.'; ++ if (locale->decimal_point[0] && locale->decimal_point[1]) ++ force_general_numcompare = 1; ++ } + + /* FIXME: add support for multibyte thousands separators. */ + thousands_sep = to_uchar (*locale->thousands_sep); + if (! thousands_sep || locale->thousands_sep[1]) +- thousands_sep = -1; ++ { ++ thousands_sep = -1; ++ if (locale->thousands_sep[0] && locale->thousands_sep[1]) ++ force_general_numcompare = 1; ++ } + } + ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ { ++ blank_type = wctype ("blank"); ++ begfield = begfield_mb; ++ limfield = limfield_mb; ++ getmonth = getmonth_mb; ++ keycompare = keycompare_mb; ++ } ++ else ++#endif ++ { ++ begfield = begfield_uni; ++ limfield = limfield_uni; ++ keycompare = keycompare_uni; ++ getmonth = getmonth_uni; ++ } ++ + have_read_stdin = false; + inittables (); + +@@ -2544,13 +3186,32 @@ + + case 't': + { +- char newtab = optarg[0]; +- if (! newtab) ++ const char *newtab = optarg; ++ size_t newtab_length; ++ if (! newtab[0]) + error (SORT_FAILURE, 0, _("empty tab")); +- if (optarg[1]) ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ { ++ mbstate_t state; ++ ++ memset (&state, 0, sizeof (mbstate_t)); ++ newtab_length = mbrtowc (NULL, newtab, strlen (newtab), &state); ++ if (newtab_length == (size_t) 0 ++ || newtab_length == (size_t) -1 ++ || newtab_length == (size_t) -2) ++ newtab_length = 1; ++ } ++ else ++#endif ++ newtab_length = 1; ++ if (optarg[newtab_length]) + { + if (STREQ (optarg, "\\0")) +- newtab = '\0'; ++ { ++ newtab = "\0"; ++ newtab_length = 1; ++ } + else + { + /* Provoke with `sort -txx'. Complain about +@@ -2561,9 +3222,12 @@ + quote (optarg)); + } + } +- if (tab != TAB_DEFAULT && tab != newtab) ++ if (tab != NULL ++ && (tab_length != newtab_length ++ || memcmp (tab, newtab, tab_length) != 0)) + error (SORT_FAILURE, 0, _("incompatible tabs")); + tab = newtab; ++ tab_length = newtab_length; + } + break; + +--- coreutils-6.5/src/unexpand.c ++++ coreutils-6.5/src/unexpand.c +@@ -39,11 +39,34 @@ + #include + #include + #include ++ ++/* Get mbstate_t, mbrtowc(), wcwidth() */ ++#if HAVE_WCHAR_H ++# include ++#endif ++/* Get iswblank */ ++#if HAVE_WCTYPE_H ++# include ++#endif ++ ++ ++/* A sentinel value that's placed at the end of the list of tab stops. ++ * This value must be a large number, but not so large that adding the ++ * length of a line to it would cause the column variable to overflow. */ ++#define TAB_STOP_SENTINEL INT_MAX ++ + #include "system.h" + #include "error.h" + #include "quote.h" + #include "xstrndup.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 ++ + /* The official name of this program (e.g., no `g' prefix). */ + #define PROGRAM_NAME "unexpand" + +@@ -453,6 +476,237 @@ + } + } + ++#if HAVE_MBRTOWC && HAVE_WCTYPE_H ++static void ++unexpand_multibyte (void) ++{ ++ /* Input stream. */ ++ FILE *fp = next_file (NULL); ++ ++ mbstate_t i_state; /* Current shift state of the input stream. */ ++ char buf[MB_LEN_MAX + BUFSIZ]; /* For spooling a read byte sequence. */ ++ char *bufpos; /* Next read position of BUF. */ ++ size_t buflen = 0; /* The length of the byte sequence in buf. */ ++ ++ /* The array of pending blanks. In non-POSIX locales, blanks can ++ include characters other than spaces, so the blanks must be ++ stored, not merely counted. */ ++ char *pending_blank; ++ ++ if (!fp) ++ return; ++ ++ /* The worst case is a non-blank character, then one blank, then a ++ tab stop, then MAX_COLUMN_WIDTH - 1 blanks, then a non-blank; so ++ allocate MAX_COLUMN_WIDTH bytes to store the blanks. */ ++ pending_blank = xmalloc (max_column_width); ++ ++ memset (&i_state, '\0', sizeof(mbstate_t)); ++ ++ for (;;) ++ { ++ /* A gotten wide character. */ ++ wint_t wc; ++ ++ /* If true, perform translations. */ ++ bool convert = true; ++ ++ /* The following variables have valid values only when CONVERT ++ is true: */ ++ ++ /* Column of next input character. */ ++ uintmax_t column = 0; ++ ++ /* Column the next input tab stop is on. */ ++ uintmax_t next_tab_column = 0; ++ ++ /* Index in TAB_LIST of next tab stop to examine. */ ++ size_t tab_index = 0; ++ ++ /* If true, the first pending blank came just before a tab stop. */ ++ bool one_blank_before_tab_stop = false; ++ ++ /* If true, the previous input character was a blank. This is ++ initially true, since initial strings of blanks are treated ++ as if the line was preceded by a blank. */ ++ bool prev_blank = true; ++ ++ /* Number of pending columns of blanks. */ ++ size_t pending = 0; ++ ++ /* Convert a line of text. */ ++ do ++ { ++ wchar_t w; ++ size_t mblength; /* The byte size of a multibyte character ++ which shows as same character as WC. */ ++ mbstate_t i_state_bak; /* Back up the I_STATE. */ ++ ++ /* Fill buffer */ ++ if (buflen < MB_LEN_MAX) ++ { ++ if (!feof (fp) && !ferror (fp)) ++ { ++ if (buflen > 0) ++ memmove (buf, bufpos, buflen); ++ buflen += fread (buf + buflen, sizeof (char), BUFSIZ, fp); ++ bufpos = buf; ++ } ++ } ++ ++ if (buflen < 1) ++ { ++ /* Move to the next file */ ++ if (feof (fp) || ferror (fp)) ++ fp = next_file (fp); ++ if (!fp) ++ { ++ if (pending) ++ { ++ if (fwrite (pending_blank, 1, pending, stdout) != pending) ++ error (EXIT_FAILURE, errno, _("write error")); ++ } ++ free (pending_blank); ++ return; ++ } ++ continue; ++ } ++ ++ i_state_bak = i_state; ++ mblength = mbrtowc (&w, bufpos, buflen, &i_state); ++ wc = w; ++ ++ if (mblength == (size_t) -1 || mblength == (size_t) -2) ++ { ++ i_state = i_state_bak; ++ wc = L'\0'; ++ column += convert; ++ mblength = 1; ++ } ++ ++ if (convert) ++ { ++ bool blank = iswblank (wc); ++ ++ if (blank) ++ { ++ if (next_tab_column <= column) ++ { ++ if (tab_size) ++ next_tab_column = ++ column + (tab_size - column % tab_size); ++ else ++ for (;;) ++ if (tab_index == first_free_tab) ++ { ++ convert = false; ++ break; ++ } ++ else ++ { ++ uintmax_t tab = tab_list[tab_index++]; ++ if (column < tab) ++ { ++ next_tab_column = tab; ++ break; ++ } ++ } ++ } ++ ++ if (convert) ++ { ++ if (next_tab_column < column) ++ error (EXIT_FAILURE, 0, _("input line is too long")); ++ ++ if (wc == L'\t') ++ { ++ column = next_tab_column; ++ ++ /* Discard pending blanks, unless it was a single ++ blank just before the previous tab stop. */ ++ if (! (pending == 1 && one_blank_before_tab_stop)) ++ { ++ pending = 0; ++ one_blank_before_tab_stop = false; ++ } ++ } ++ else ++ { ++ column++; ++ ++ if (! (prev_blank && column == next_tab_column)) ++ { ++ /* It is not yet known whether the pending blanks ++ will be replaced by tabs. */ ++ if (column == next_tab_column) ++ one_blank_before_tab_stop = true; ++ pending_blank[pending++] = ' '; ++ prev_blank = true; ++ buflen -= mblength; ++ bufpos += mblength; ++ continue; ++ } ++ ++ /* Replace the pending blanks by a tab or two. */ ++ pending_blank[0] = *bufpos = '\t'; ++ pending = one_blank_before_tab_stop; ++ } ++ } ++ } ++ else if (wc == L'\b') ++ { ++ /* Go back one column, and force recalculation of the ++ next tab stop. */ ++ column -= !!column; ++ next_tab_column = column; ++ tab_index -= !!tab_index; ++ } ++ else ++ { ++ if (!iswcntrl (wc)) ++ { ++ int width = wcwidth (wc); ++ if (width > 0) ++ { ++ if (column > (column + width)) ++ error (EXIT_FAILURE, 0, _("input line is too long")); ++ column += width; ++ } ++ } ++ } ++ ++ if (pending) ++ { ++ if (fwrite (pending_blank, 1, pending, stdout) != pending) ++ error (EXIT_FAILURE, errno, _("write error")); ++ pending = 0; ++ one_blank_before_tab_stop = false; ++ } ++ ++ prev_blank = blank; ++ convert &= convert_entire_line | blank; ++ } ++ ++ if (mblength) ++ { ++ if (fwrite (bufpos, sizeof (char), mblength, stdout) < mblength) ++ error (EXIT_FAILURE, errno, _("write error")); ++ } ++ else ++ { ++ if (putchar ('\0')) ++ error (EXIT_FAILURE, errno, _("write error")); ++ mblength = 1; ++ } ++ ++ buflen -= mblength; ++ bufpos += mblength; ++ } ++ while (wc != L'\n'); ++ } ++} ++#endif ++ + int + main (int argc, char **argv) + { +@@ -531,7 +785,12 @@ + + file_list = (optind < argc ? &argv[optind] : stdin_argv); + +- unexpand (); ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ unexpand_multibyte (); ++ else ++#endif ++ unexpand (); + + if (have_read_stdin && fclose (stdin) != 0) + error (EXIT_FAILURE, errno, "-"); +--- coreutils-6.5/src/uniq.c ++++ coreutils-6.5/src/uniq.c +@@ -23,6 +23,16 @@ + #include + #include + ++/* Get mbstate_t, mbrtowc(), wcrtomb() */ ++#if HAVE_WCHAR_H ++# include ++#endif ++ ++/* Get iswctype(), wctype(), towupper)(. */ ++#if HAVE_WCTYPE_H ++# include ++#endif ++ + #include "system.h" + #include "argmatch.h" + #include "linebuffer.h" +@@ -34,6 +44,13 @@ + #include "xstrtol.h" + #include "memcasecmp.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 ++ + /* The official name of this program (e.g., no `g' prefix). */ + #define PROGRAM_NAME "uniq" + +@@ -109,6 +126,12 @@ + /* Select whether/how to delimit groups of duplicate lines. */ + static enum delimit_method delimit_groups; + ++/* Function pointers. */ ++static char * (*find_field) (struct linebuffer *line); ++ ++/* Show the blank character class. */ ++wctype_t blank_type; ++ + static struct option const longopts[] = + { + {"count", no_argument, NULL, 'c'}, +@@ -189,7 +212,7 @@ + return a pointer to the beginning of the line's field to be compared. */ + + static char * +-find_field (const struct linebuffer *line) ++find_field_uni (struct linebuffer *line) + { + size_t count; + char *lp = line->buffer; +@@ -210,6 +233,83 @@ + return lp + 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; ++ ++ 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 || !iswctype (wc, blank_type)) ++ { ++ pos += mblength; ++ break; ++ } ++ pos += mblength; ++ } ++ ++ while (pos < size) ++ { ++ MBCHAR_TO_WCHAR (wc, mblength, lp, pos, size, statep, convfail); ++ ++ if (!convfail && iswctype (wc, blank_type)) ++ 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. +@@ -234,6 +334,73 @@ + return oldlen != newlen || memcmp (old, new, oldlen); + } + ++#if HAVE_MBRTOWC ++static int ++different_multi (const char *old, const char *new, size_t oldlen, size_t newlen, mbstate_t oldstate, mbstate_t newstate) ++{ ++ size_t i, j, chars; ++ const char *str[2]; ++ char *copy[2]; ++ size_t len[2]; ++ mbstate_t state[2]; ++ size_t mblength; ++ wchar_t wc, uwc; ++ mbstate_t state_bak; ++ ++ str[0] = old; ++ str[1] = new; ++ len[0] = oldlen; ++ len[1] = newlen; ++ state[0] = oldstate; ++ state[1] = newstate; ++ ++ for (i = 0; i < 2; i++) ++ { ++ copy[i] = alloca (len[i] + 1); ++ ++ for (j = 0, chars = 0; j < len[i] && chars < check_chars; chars++) ++ { ++ state_bak = state[i]; ++ mblength = mbrtowc (&wc, str[i] + j, len[i] - j, &state[i]); ++ ++ switch (mblength) ++ { ++ case (size_t)-1: ++ case (size_t)-2: ++ state[i] = state_bak; ++ /* Fall through */ ++ case 0: ++ mblength = 1; ++ break; ++ ++ default: ++ if (ignore_case) ++ { ++ uwc = towupper (wc); ++ ++ if (uwc != wc) ++ { ++ mbstate_t state_wc; ++ ++ memset (&state_wc, '\0', sizeof (mbstate_t)); ++ wcrtomb (copy[i] + j, uwc, &state_wc); ++ } ++ else ++ memcpy (copy[i] + j, str[i] + j, mblength); ++ } ++ else ++ memcpy (copy[i] + j, str[i] + j, mblength); ++ } ++ j += mblength; ++ } ++ copy[i][j] = '\0'; ++ len[i] = j; ++ } ++ ++ return xmemcoll (copy[0], len[0], copy[1], len[1]); ++} ++#endif ++ + /* Output the line in linebuffer LINE to standard output + provided that the switches say it should be output. + MATCH is true if the line matches the previous line. +@@ -286,15 +453,43 @@ + { + char *prevfield IF_LINT (= NULL); + size_t prevlen IF_LINT (= 0); ++#if HAVE_MBRTOWC ++ mbstate_t prevstate; ++ ++ memset (&prevstate, '\0', sizeof (mbstate_t)); ++#endif + + while (!feof (stdin)) + { + char *thisfield; + size_t thislen; ++#if HAVE_MBRTOWC ++ mbstate_t thisstate; ++#endif + if (readlinebuffer (thisline, stdin) == 0) + break; + thisfield = find_field (thisline); + thislen = thisline->length - 1 - (thisfield - thisline->buffer); ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ { ++ thisstate = thisline->state; ++ ++ if (prevline->length == 0 ++ || different_multi (thisfield, prevfield, thislen, prevlen, ++ thisstate, prevstate)) ++ { ++ fwrite (thisline->buffer, sizeof (char), ++ thisline->length, stdout); ++ ++ SWAP_LINES (prevline, thisline); ++ prevfield = thisfield; ++ prevlen = thislen; ++ prevstate = thisstate; ++ } ++ } ++ else ++#endif + if (prevline->length == 0 + || different (thisfield, prevfield, thislen, prevlen)) + { +@@ -313,17 +508,26 @@ + size_t prevlen; + uintmax_t match_count = 0; + bool first_delimiter = true; ++#if HAVE_MBRTOWC ++ mbstate_t prevstate; ++#endif + + if (readlinebuffer (prevline, stdin) == 0) + goto closefiles; + prevfield = find_field (prevline); + prevlen = prevline->length - 1 - (prevfield - prevline->buffer); ++#if HAVE_MBRTOWC ++ prevstate = prevline->state; ++#endif + + while (!feof (stdin)) + { + bool match; + char *thisfield; + size_t thislen; ++#if HAVE_MBRTOWC ++ mbstate_t thisstate; ++#endif + if (readlinebuffer (thisline, stdin) == 0) + { + if (ferror (stdin)) +@@ -332,6 +536,15 @@ + } + thisfield = find_field (thisline); + thislen = thisline->length - 1 - (thisfield - thisline->buffer); ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ { ++ thisstate = thisline->state; ++ match = !different_multi (thisfield, prevfield, ++ thislen, prevlen, thisstate, prevstate); ++ } ++ else ++#endif + match = !different (thisfield, prevfield, thislen, prevlen); + match_count += match; + +@@ -364,6 +577,9 @@ + SWAP_LINES (prevline, thisline); + prevfield = thisfield; + prevlen = thislen; ++#if HAVE_MBRTOWC ++ prevstate = thisstate; ++#endif + if (!match) + match_count = 0; + } +@@ -408,6 +624,18 @@ + + atexit (close_stdout); + ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ { ++ find_field = find_field_multi; ++ blank_type = wctype ("blank"); ++ } ++ else ++#endif ++ { ++ find_field = find_field_uni; ++ } ++ + skip_chars = 0; + skip_fields = 0; + check_chars = SIZE_MAX; diff --git a/coreutils-5.3.0-pie.diff b/coreutils-5.3.0-pie.diff new file mode 100644 index 0000000..af27733 --- /dev/null +++ b/coreutils-5.3.0-pie.diff @@ -0,0 +1,21 @@ +--- lib/Makefile.am ++++ lib/Makefile.am +@@ -20,6 +20,7 @@ + include gnulib.mk + + AM_CFLAGS = $(WARNING_CFLAGS) $(WERROR_CFLAGS) ++AM_CFLAGS += -fpie + + noinst_PROGRAMS = t-fpending + LDADD = $(noinst_LIBRARIES) +--- src/Makefile.am ++++ src/Makefile.am +@@ -105,6 +105,8 @@ + + su_SOURCES = su.c getdef.c + su_LDADD = $(LDADD) $(LIB_CRYPT) -lpam -lpam_misc -ldl ++su_CFLAGS = -fpie ++su_LDFLAGS = -pie + + dir_LDADD += $(LIB_ACL) + ls_LDADD += $(LIB_ACL) diff --git a/coreutils-5.3.0-sbin4su.diff b/coreutils-5.3.0-sbin4su.diff new file mode 100644 index 0000000..c501f62 --- /dev/null +++ b/coreutils-5.3.0-sbin4su.diff @@ -0,0 +1,143 @@ +--- src/su.c ++++ src/su.c +@@ -344,6 +344,117 @@ + #endif /* !USE_PAM */ + } + ++/* Add or clear /sbin and /usr/sbin for the su command ++ used without `-'. */ ++ ++/* Set if /sbin is found in path. */ ++#define SBIN_MASK 0x01 ++/* Set if /usr/sbin is found in path. */ ++#define USBIN_MASK 0x02 ++ ++static char * ++addsbin (const char *const path) ++{ ++ unsigned char smask = 0; ++ char *ptr, *tmp, *cur, *ret = NULL; ++ size_t len; ++ ++ if (!path || *path == 0) ++ return NULL; ++ ++ tmp = xstrdup (path); ++ cur = tmp; ++ for (ptr = strsep (&cur, ":"); ptr != NULL; ptr = strsep (&cur, ":")) ++ { ++ if (!strcmp (ptr, "/sbin")) ++ smask |= SBIN_MASK; ++ if (!strcmp (ptr, "/usr/sbin")) ++ smask |= USBIN_MASK; ++ } ++ ++ if ((smask & (USBIN_MASK|SBIN_MASK)) == (USBIN_MASK|SBIN_MASK)) ++ { ++ free (tmp); ++ return NULL; ++ } ++ ++ len = strlen (path); ++ if (!(smask & USBIN_MASK)) ++ len += strlen ("/usr/sbin:"); ++ ++ if (!(smask & SBIN_MASK)) ++ len += strlen (":/sbin"); ++ ++ ret = xmalloc (len + 1); ++ strcpy (tmp, path); ++ ++ *ret = 0; ++ cur = tmp; ++ for (ptr = strsep (&cur, ":"); ptr; ptr = strsep (&cur, ":")) ++ { ++ if (!strcmp (ptr, ".")) ++ continue; ++ if (*ret) ++ strcat (ret, ":"); ++ if (!(smask & USBIN_MASK) && !strcmp (ptr, "/bin")) ++ { ++ strcat (ret, "/usr/sbin:"); ++ strcat (ret, ptr); ++ smask |= USBIN_MASK; ++ continue; ++ } ++ if (!(smask & SBIN_MASK) && !strcmp (ptr, "/usr/bin")) ++ { ++ strcat (ret, ptr); ++ strcat (ret, ":/sbin"); ++ smask |= SBIN_MASK; ++ continue; ++ } ++ strcat (ret, ptr); ++ } ++ free (tmp); ++ ++ if (!(smask & USBIN_MASK)) ++ strcat (ret, ":/usr/sbin"); ++ ++ if (!(smask & SBIN_MASK)) ++ strcat (ret, ":/sbin"); ++ ++ return ret; ++} ++ ++static char * ++clearsbin (const char *const path) ++{ ++ char *ptr, *tmp, *cur, *ret = NULL; ++ ++ if (!path || *path == 0) ++ return NULL; ++ ++ tmp = strdup (path); ++ if (!tmp) ++ return NULL; ++ ++ ret = xmalloc (strlen (path) + 1); ++ *ret = 0; ++ cur = tmp; ++ for (ptr = strsep (&cur, ":"); ptr; ptr = strsep (&cur, ":")) ++ { ++ if (!strcmp (ptr, "/sbin")) ++ continue; ++ if (!strcmp (ptr, "/usr/sbin")) ++ continue; ++ if (!strcmp (ptr, "/usr/local/sbin")) ++ continue; ++ if (*ret) ++ strcat (ret, ":"); ++ strcat (ret, ptr); ++ } ++ free (tmp); ++ ++ return ret; ++} ++ + /* Update `environ' for the new shell based on PW, with SHELL being + the value for the SHELL environment variable. */ + +@@ -383,6 +494,22 @@ + DEFAULT_LOGIN_PATH) + : getdef_str ("SUPATH", + DEFAULT_ROOT_LOGIN_PATH))); ++ else ++ { ++ char const *path = getenv ("PATH"); ++ char *new = NULL; ++ ++ if (pw->pw_uid) ++ new = clearsbin (path); ++ else ++ new = addsbin (path); ++ ++ if (new) ++ { ++ xsetenv ("PATH", new); ++ free (new); ++ } ++ } + if (pw->pw_uid) + { + xsetenv ("USER", pw->pw_name); diff --git a/coreutils-6.7.diff b/coreutils-6.7.diff new file mode 100644 index 0000000..f8ac5dd --- /dev/null +++ b/coreutils-6.7.diff @@ -0,0 +1,764 @@ +--- doc/coreutils.texi ++++ doc/coreutils.texi +@@ -64,8 +64,6 @@ + * fold: (coreutils)fold invocation. Wrap long input lines. + * groups: (coreutils)groups invocation. Print group names a user is in. + * head: (coreutils)head invocation. Output the first part of files. +-* hostid: (coreutils)hostid invocation. Print numeric host identifier. +-* hostname: (coreutils)hostname invocation. Print or set system name. + * id: (coreutils)id invocation. Print user identity. + * install: (coreutils)install invocation. Copy and change attributes. + * join: (coreutils)join invocation. Join lines on a common field. +@@ -398,8 +396,6 @@ + + * date invocation:: Print or set system date and time + * uname invocation:: Print system information +-* hostname invocation:: Print or set system name +-* hostid invocation:: Print numeric host identifier. + + @command{date}: Print or set system date and time + +@@ -12190,8 +12186,6 @@ + @menu + * date invocation:: Print or set system date and time. + * uname invocation:: Print system information. +-* hostname invocation:: Print or set system name. +-* hostid invocation:: Print numeric host identifier. + @end menu + + +@@ -12949,55 +12943,6 @@ + @exitstatus + + +-@node hostname invocation +-@section @command{hostname}: Print or set system name +- +-@pindex hostname +-@cindex setting the hostname +-@cindex printing the hostname +-@cindex system name, printing +-@cindex appropriate privileges +- +-With no arguments, @command{hostname} prints the name of the current host +-system. With one argument, it sets the current host name to the +-specified string. You must have appropriate privileges to set the host +-name. Synopsis: +- +-@example +-hostname [@var{name}] +-@end example +- +-The only options are @option{--help} and @option{--version}. @xref{Common +-options}. +- +-@exitstatus +- +- +-@node hostid invocation +-@section @command{hostid}: Print numeric host identifier. +- +-@pindex hostid +-@cindex printing the host identifier +- +-@command{hostid} prints the numeric identifier of the current host +-in hexadecimal. This command accepts no arguments. +-The only options are @option{--help} and @option{--version}. +-@xref{Common options}. +- +-For example, here's what it prints on one system I use: +- +-@example +-$ hostid +-1bac013d +-@end example +- +-On that system, the 32-bit quantity happens to be closely +-related to the system's Internet address, but that isn't always +-the case. +- +-@exitstatus +- +- + @node Modified command invocation + @chapter Modified command invocation + +--- src/Makefile.am ++++ src/Makefile.am +@@ -103,7 +103,8 @@ + # If necessary, add -lm to resolve use of pow in lib/strtod.c. + uptime_LDADD = $(LDADD) $(POW_LIB) $(GETLOADAVG_LIBS) + +-su_LDADD = $(LDADD) $(LIB_CRYPT) ++su_SOURCES = su.c getdef.c ++su_LDADD = $(LDADD) $(LIB_CRYPT) -lpam -lpam_misc -ldl + + dir_LDADD += $(LIB_ACL) + ls_LDADD += $(LIB_ACL) +--- src/getdef.c ++++ src/getdef.c +@@ -0,0 +1,257 @@ ++/* Copyright (C) 2003, 2004, 2005 Thorsten Kukuk ++ Author: Thorsten Kukuk ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License version 2 as ++ published by the Free Software Foundation. ++ ++ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#define _GNU_SOURCE ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "getdef.h" ++ ++struct item { ++ char *name; /* name of the option. */ ++ char *value; /* value of the option. */ ++ struct item *next; /* pointer to next option. */ ++}; ++ ++static struct item *list = NULL; ++ ++void ++free_getdef_data (void) ++{ ++ struct item *ptr; ++ ++ ptr = list; ++ while (ptr != NULL) ++ { ++ struct item *tmp; ++ tmp = ptr->next; ++ free (ptr->name); ++ free (ptr->value); ++ free (ptr); ++ ptr = tmp; ++ } ++ ++ list = NULL; ++} ++ ++/* Add a new entry to the list. */ ++static void ++store (const char *name, const char *value) ++{ ++ struct item *new = malloc (sizeof (struct item)); ++ ++ if (new == NULL) ++ abort (); ++ ++ if (name == NULL) ++ abort (); ++ ++ new->name = strdup (name); ++ new->value = strdup (value?:""); ++ new->next = list; ++ list = new; ++} ++ ++/* search a special entry in the list and return the value. */ ++static const char * ++search (const char *name) ++{ ++ struct item *ptr; ++ ++ ptr = list; ++ while (ptr != NULL) ++ { ++ if (strcasecmp (name, ptr->name) == 0) ++ return ptr->value; ++ ptr = ptr->next; ++ } ++ ++ return NULL; ++} ++ ++/* Load the login.defs file (/etc/login.defs) */ ++static void ++load_defaults_internal (const char *filename) ++{ ++ FILE *fp; ++ char *buf = NULL; ++ size_t buflen = 0; ++ ++ fp = fopen (filename, "r"); ++ if (NULL == fp) ++ return; ++ ++ while (!feof (fp)) ++ { ++ char *tmp, *cp; ++#if defined(HAVE_GETLINE) ++ ssize_t n = getline (&buf, &buflen, fp); ++#elif defined (HAVE_GETDELIM) ++ ssize_t n = getdelim (&buf, &buflen, '\n', fp); ++#else ++ ssize_t n; ++ ++ if (buf == NULL) ++ { ++ buflen = 8096; ++ buf = malloc (buflen); ++ } ++ buf[0] = '\0'; ++ fgets (buf, buflen - 1, fp); ++ if (buf != NULL) ++ n = strlen (buf); ++ else ++ n = 0; ++#endif /* HAVE_GETLINE / HAVE_GETDELIM */ ++ cp = buf; ++ ++ if (n < 1) ++ break; ++ ++ tmp = strchr (cp, '#'); /* remove comments */ ++ if (tmp) ++ *tmp = '\0'; ++ while (isspace ((int)*cp)) /* remove spaces and tabs */ ++ ++cp; ++ if (*cp == '\0') /* ignore empty lines */ ++ continue; ++ ++ if (cp[strlen (cp) - 1] == '\n') ++ cp[strlen (cp) - 1] = '\0'; ++ ++ tmp = strsep (&cp, " \t="); ++ if (cp != NULL) ++ while (isspace ((int)*cp) || *cp == '=') ++ ++cp; ++ ++ store (tmp, cp); ++ } ++ fclose (fp); ++ ++ if (buf) ++ free (buf); ++} ++ ++static void ++load_defaults (void) ++{ ++ load_defaults_internal ("/etc/default/su"); ++ load_defaults_internal ("/etc/login.defs"); ++} ++ ++int ++getdef_bool (const char *name, int dflt) ++{ ++ const char *val; ++ ++ if (list == NULL) ++ load_defaults (); ++ ++ val = search (name); ++ ++ if (val == NULL) ++ return dflt; ++ ++ return (strcasecmp (val, "yes") == 0); ++} ++ ++long ++getdef_num (const char *name, long dflt) ++{ ++ const char *val; ++ char *cp; ++ long retval; ++ ++ if (list == NULL) ++ load_defaults (); ++ ++ val = search (name); ++ ++ if (val == NULL) ++ return dflt; ++ ++ retval = strtol (val, &cp, 0); ++ if (*cp != '\0' || ++ ((retval == LONG_MAX || retval == LONG_MIN) && errno == ERANGE)) ++ { ++ fprintf (stderr, ++ "%s contains invalid numerical value: %s!\n", ++ name, val); ++ retval = dflt; ++ } ++ return retval; ++} ++ ++unsigned long ++getdef_unum (const char *name, unsigned long dflt) ++{ ++ const char *val; ++ char *cp; ++ unsigned long retval; ++ ++ if (list == NULL) ++ load_defaults (); ++ ++ val = search (name); ++ ++ if (val == NULL) ++ return dflt; ++ ++ retval = strtoul (val, &cp, 0); ++ if (*cp != '\0' || (retval == ULONG_MAX && errno == ERANGE)) ++ { ++ fprintf (stderr, ++ "%s contains invalid numerical value: %s!\n", ++ name, val); ++ retval = dflt; ++ } ++ return retval; ++} ++ ++const char * ++getdef_str (const char *name, const char *dflt) ++{ ++ const char *retval; ++ ++ if (list == NULL) ++ load_defaults (); ++ ++ retval = search (name); ++ ++ return retval ?: dflt; ++} ++ ++#if defined(TEST) ++ ++int ++main () ++{ ++ printf ("CYPT=%s\n", getdef_str ("cRypt", "no")); ++ printf ("LOG_UNKFAIL_ENAB=%s\n", getdef_str ("log_unkfail_enab","")); ++ printf ("DOESNOTEXIST=%s\n", getdef_str ("DOESNOTEXIST","yes")); ++ return 0; ++} ++ ++#endif +--- src/getdef.h ++++ src/getdef.h +@@ -0,0 +1,29 @@ ++/* Copyright (C) 2003, 2005 Thorsten Kukuk ++ Author: Thorsten Kukuk ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License version 2 as ++ published by the Free Software Foundation. ++ ++ 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ ++ ++#ifndef _GETDEF_H_ ++ ++#define _GETDEF_H_ 1 ++ ++extern int getdef_bool (const char *name, int dflt); ++extern long getdef_num (const char *name, long dflt); ++extern unsigned long getdef_unum (const char *name, unsigned long dflt); ++extern const char *getdef_str (const char *name, const char *dflt); ++ ++/* Free all data allocated by getdef_* calls before. */ ++extern void free_getdef_data (void); ++ ++#endif /* _GETDEF_H_ */ +--- src/install.c ++++ src/install.c +@@ -572,7 +572,8 @@ + while (pid != wait (&status)) /* Wait for kid to finish. */ + /* Do nothing. */ ; + if (status) +- error (EXIT_FAILURE, 0, _("strip failed")); ++ /* Don't fail just because `strip' failed. */ ++ error (0, 0, _("strip failed")); + break; + } + } +--- src/su.c ++++ src/su.c +@@ -38,6 +38,12 @@ + restricts who can su to UID 0 accounts. RMS considers that to + be fascist. + ++ Actually, with PAM, su has nothing to do with whether or not a ++ wheel group is enforced by su. RMS tries to restrict your access ++ to a su which implements the wheel group, but PAM considers that ++ to be fascist, and gives the user/sysadmin the opportunity to ++ enforce a wheel group by proper editing of /etc/pam.d/su ++ + Compile-time options: + -DSYSLOG_SUCCESS Log successful su's (by default, to root) with syslog. + -DSYSLOG_FAILURE Log failed su's (by default, to root) with syslog. +@@ -53,6 +59,13 @@ + #include + #include + #include ++#ifdef USE_PAM ++#include ++#include ++#include ++#include ++#include ++#endif + + /* Hide any system prototype for getusershell. + This is necessary because some Cray systems have a conflicting +@@ -66,6 +79,9 @@ + + #if HAVE_SYSLOG_H && HAVE_SYSLOG + # include ++# define SYSLOG_SUCCESS 1 ++# define SYSLOG_FAILURE 1 ++# define SYSLOG_NON_ROOT 1 + #else + # undef SYSLOG_SUCCESS + # undef SYSLOG_FAILURE +@@ -99,19 +115,13 @@ + # include + #endif + ++#include "getdef.h" ++ + /* The default PATH for simulated logins to non-superuser accounts. */ +-#ifdef _PATH_DEFPATH +-# define DEFAULT_LOGIN_PATH _PATH_DEFPATH +-#else +-# define DEFAULT_LOGIN_PATH ":/usr/ucb:/bin:/usr/bin" +-#endif ++#define DEFAULT_LOGIN_PATH "/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin" + + /* The default PATH for simulated logins to superuser accounts. */ +-#ifdef _PATH_DEFPATH_ROOT +-# define DEFAULT_ROOT_LOGIN_PATH _PATH_DEFPATH_ROOT +-#else +-# define DEFAULT_ROOT_LOGIN_PATH "/usr/ucb:/bin:/usr/bin:/etc" +-#endif ++#define DEFAULT_ROOT_LOGIN_PATH "/usr/sbin:/bin:/usr/bin:/sbin:/usr/X11R6/bin" + + /* The shell to run if none is given in the user's passwd entry. */ + #define DEFAULT_SHELL "/bin/sh" +@@ -119,7 +129,9 @@ + /* The user to become if none is specified. */ + #define DEFAULT_USER "root" + ++#ifndef USE_PAM + char *crypt (); ++#endif + char *getusershell (); + void endusershell (); + void setusershell (); +@@ -216,7 +228,26 @@ + } + #endif + ++#ifdef USE_PAM ++ ++static pam_handle_t *pamh = NULL; ++static int retval; ++static struct pam_conv conv = ++{ ++ misc_conv, ++ NULL ++}; ++ ++#define PAM_BAIL_P(a) \ ++ if (retval) \ ++ { \ ++ pam_end (pamh, PAM_SUCCESS); \ ++ a; \ ++ } ++#endif ++ + /* Ask the user for a password. ++ If PAM is in use, let PAM ask for the password if necessary. + Return true if the user gives the correct password for entry PW, + false if not. Return true without asking for a password if run by UID 0 + or if PW has an empty password. */ +@@ -224,10 +255,49 @@ + static bool + correct_password (const struct passwd *pw) + { ++#ifdef USE_PAM ++ const struct passwd *lpw; ++ const char *cp; ++ ++ retval = pam_start ("su", pw->pw_name, &conv, &pamh); ++ PAM_BAIL_P (return false); ++ ++ if (isatty (0) && (cp = ttyname (0)) != NULL) ++ { ++ const char *tty; ++ ++ if (strncmp (cp, "/dev/", 5) == 0) ++ tty = cp + 5; ++ else ++ tty = cp; ++ retval = pam_set_item (pamh, PAM_TTY, tty); ++ PAM_BAIL_P (return false); ++ } ++ cp = getlogin (); ++ if (!(cp && *cp && (lpw = getpwnam (cp)) != NULL && lpw->pw_uid == getuid ())) ++ lpw = getpwuid (getuid ()); ++ if (lpw) ++ { ++ retval = pam_set_item (pamh, PAM_RUSER, (const void *) lpw->pw_name); ++ PAM_BAIL_P (return false); ++ } ++ retval = pam_authenticate (pamh, 0); ++ PAM_BAIL_P (return false); ++ retval = pam_acct_mgmt (pamh, 0); ++ if (retval == PAM_NEW_AUTHTOK_REQD) ++ { ++ /* password has expired. Offer option to change it. */ ++ retval = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK); ++ PAM_BAIL_P (return false); ++ } ++ PAM_BAIL_P (return false); ++ /* must be authenticated if this point was reached */ ++ return true; ++#else /* !USE_PAM */ + char *unencrypted, *encrypted, *correct; + #if HAVE_GETSPNAM && HAVE_STRUCT_SPWD_SP_PWDP + /* Shadow passwd stuff for SVR3 and maybe other systems. */ +- struct spwd *sp = getspnam (pw->pw_name); ++ const struct spwd *sp = getspnam (pw->pw_name); + + endspent (); + if (sp) +@@ -248,6 +318,7 @@ + encrypted = crypt (unencrypted, correct); + memset (unencrypted, 0, strlen (unencrypted)); + return STREQ (encrypted, correct); ++#endif /* !USE_PAM */ + } + + /* Update `environ' for the new shell based on PW, with SHELL being +@@ -272,8 +343,8 @@ + xsetenv ("USER", pw->pw_name); + xsetenv ("LOGNAME", pw->pw_name); + xsetenv ("PATH", (pw->pw_uid +- ? DEFAULT_LOGIN_PATH +- : DEFAULT_ROOT_LOGIN_PATH)); ++ ? getdef_str ("PATH", DEFAULT_LOGIN_PATH) ++ : getdef_str ("SUPATH", DEFAULT_ROOT_LOGIN_PATH))); + } + else + { +@@ -283,6 +354,12 @@ + { + xsetenv ("HOME", pw->pw_dir); + xsetenv ("SHELL", shell); ++ if (getdef_bool ("ALWAYS_SET_PATH", 0)) ++ xsetenv ("PATH", (pw->pw_uid ++ ? getdef_str ("PATH", ++ DEFAULT_LOGIN_PATH) ++ : getdef_str ("SUPATH", ++ DEFAULT_ROOT_LOGIN_PATH))); + if (pw->pw_uid) + { + xsetenv ("USER", pw->pw_name); +@@ -303,12 +380,35 @@ + error (EXIT_FAIL, errno, _("cannot set groups")); + endgrent (); + #endif ++#ifdef USE_PAM ++ retval = pam_setcred (pamh, PAM_ESTABLISH_CRED); ++ if (retval != PAM_SUCCESS) ++ error (EXIT_FAIL, 0, "%s", pam_strerror (pamh, retval)); ++ ++ retval = pam_open_session (pamh,0); ++ if (retval != PAM_SUCCESS) ++ { ++ pam_setcred (pamh, PAM_DELETE_CRED); ++ error (EXIT_FAIL, 0, "could not open session: %s", ++ pam_strerror (pamh, retval)); ++ } ++#endif /* USE_PAM */ + if (setgid (pw->pw_gid)) + error (EXIT_FAIL, errno, _("cannot set group id")); + if (setuid (pw->pw_uid)) + error (EXIT_FAIL, errno, _("cannot set user id")); + } + ++#ifdef USE_PAM ++static bool caught = false; ++/* Signal handler for parent process later */ ++static void ++su_catch_sig (int sig) ++{ ++ caught = true; ++} ++#endif ++ + /* Run SHELL, or DEFAULT_SHELL if SHELL is empty. + If COMMAND is nonzero, pass it to the shell with the -c option. + Pass ADDITIONAL_ARGS to the shell as more arguments; there +@@ -321,6 +421,88 @@ + size_t n_args = 1 + fast_startup + 2 * !!command + n_additional_args + 1; + char const **args = xnmalloc (n_args, sizeof *args); + size_t argno = 1; ++#ifdef USE_PAM ++ pid_t child; ++ sigset_t ourset; ++ int status; ++ ++ child = fork (); ++ if (child == (pid_t) -1) ++ error (EXIT_FAILURE, errno, "cannot fork"); ++ ++ if (child != 0) ++ { ++ /* parent only */ ++ sigfillset (&ourset); ++ if (sigprocmask (SIG_BLOCK, &ourset, NULL)) ++ { ++ error (0, errno, "cannot block signals"); ++ caught = true; ++ } ++ if (!caught) ++ { ++ struct sigaction action; ++ action.sa_handler = su_catch_sig; ++ sigemptyset (&action.sa_mask); ++ action.sa_flags = 0; ++ sigemptyset (&ourset); ++ if (sigaddset (&ourset, SIGTERM) ++ || sigaddset (&ourset, SIGALRM) ++ || sigaction (SIGTERM, &action, NULL) ++ || sigprocmask (SIG_UNBLOCK, &ourset, NULL)) ++ { ++ error (0, errno, "cannot set signal handler"); ++ caught = true; ++ } ++ } ++ if (!caught) ++ { ++ for (;;) ++ { ++ pid_t pid; ++ ++ pid = waitpid (child, &status, WUNTRACED); ++ ++ if (WIFSTOPPED (status)) ++ { ++ kill (getpid (), SIGSTOP); ++ /* once we get here, we must have resumed */ ++ kill (pid, SIGCONT); ++ } ++ else ++ break; ++ } ++ if (WIFSIGNALED (status)) ++ status = WTERMSIG (status) + 128; ++ else ++ status = WEXITSTATUS (status); ++ } ++ else ++ status = 1; ++ ++ if (caught) ++ { ++ fprintf (stderr, "\nSession terminated, killing shell..."); ++ kill (child, SIGTERM); ++ } ++ retval = pam_setcred (pamh, PAM_DELETE_CRED); ++ PAM_BAIL_P (exit (EXIT_FAILURE)); ++ retval = pam_close_session (pamh, 0); ++ PAM_BAIL_P (exit (EXIT_FAILURE)); ++ retval = pam_end (pamh, PAM_SUCCESS); ++ PAM_BAIL_P (exit (EXIT_FAILURE)); ++ if (caught) ++ { ++ sleep (2); ++ kill (child, SIGKILL); ++ fprintf (stderr, " ...killed.\n"); ++ } ++ exit (status); ++ } ++ ++ /* child shell */ ++ pam_end (pamh, 0); ++#endif + + if (simulate_login) + { +@@ -339,6 +521,11 @@ + args[argno++] = "-f"; + if (command) + { ++ if (simulate_login) ++ /* Bash 2.0 have to be invoked as `-su'. See the comments in ++ `shell.c (run_startup_files)'. */ ++ args[0] = "-su"; ++ + args[argno++] = "-c"; + args[argno++] = command; + } +@@ -495,6 +682,9 @@ + #ifdef SYSLOG_FAILURE + log_su (pw, false); + #endif ++#ifdef USE_PAM ++ sleep (getdef_num ("FAIL_DELAY", 1)); ++#endif + error (EXIT_FAIL, 0, _("incorrect password")); + } + #ifdef SYSLOG_SUCCESS +--- src/system.h ++++ src/system.h +@@ -173,7 +173,7 @@ + # define DEV_BSIZE BBSIZE + #endif + #ifndef DEV_BSIZE +-# define DEV_BSIZE 4096 ++# define DEV_BSIZE 512 + #endif + + /* Extract or fake data from a `struct stat'. +--- tests/help-version ++++ tests/help-version +@@ -190,7 +190,7 @@ + + for i in $all_programs; do + # Skip these. +- case $i in chroot|stty|tty|false) continue;; esac ++ case $i in chroot|stty|tty|false|df) continue;; esac + + rm -rf $tmp_in $tmp_in2 $tmp_dir $tmp_out + echo > $tmp_in +--- tests/other-fs-tmpdir ++++ tests/other-fs-tmpdir +@@ -44,6 +44,8 @@ + fi + + done ++# Autobuild hack ++test -f /bin/uname.bin && other_partition_tmpdir= + + if test -z "$other_partition_tmpdir"; then + cat <&2 diff --git a/coreutils-6.7.tar.bz2 b/coreutils-6.7.tar.bz2 new file mode 100644 index 0000000..dba9d75 --- /dev/null +++ b/coreutils-6.7.tar.bz2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4d7027cc803682decdadf850095dce7e22cc08eba2b1283e16d0fb7a675ac1f0 +size 5315342 diff --git a/coreutils-changelog.diff b/coreutils-changelog.diff new file mode 100644 index 0000000..2cafff1 --- /dev/null +++ b/coreutils-changelog.diff @@ -0,0 +1,44 @@ +2005-10-20 Andreas Gruenbacher + + * Add support for access control lists and extended attributes + (see below). + + * lib/acl.h: Remove HAVE_ACL symbol; is needed for POSIX + ACLs even if there is no acl system call. + + * lib/acl.c (file_has_acl, copy_acl, set_acl): Add Linux/POSIX ACL + implementation. + + * src/copy.c (get_dest_mode): Remove, no longer correct with acls. + + * src/copy.c (copy_internal): Check effective permissions after + mkdir(). Add S_IRWXU only after saving the default permissions, + so that the default permissions can be restored. This is needed + with POSIX 1003.1e draft 17 acls. Add dst_mode and dst_mode_valid, + which are set when the destination file mode is changed + temporarily. Remove obsolete ran_chown. + + * src/copy.c (copy_internal): Use copy_acl and set_acl instead of + chmod. + + * src/copy.c: Add --attributes option for controlling which extended + attributes to copy. + + * src/cp.c (make_path_private): Remove the mode parameter. Pass in + cp_options, instead of cp_options->xstat only. Stat the source dir, + and create the destination dir with the source dir's mode as create + mode (see analog change to src/copy.c (copy_internal)). Check if the + effective permissions include S_IRWXU. Remember the original mode + only if needed later. + + * src/cp.c, src/install.c, src/mv.c, src/copy.h: Remove umask_kill, + and never change the startup umask. The functions creating files + need the original umask to create the correct permissions inside + directories with default ACLs. + + * src/cp.c (struct dir_attr, re_protect, make_path_private): Replace + is_new_dir by mode and mode_valid variables. + + * src/ls.c: change `HAVE_ACL' to `USE_ACL' for POSIX ACLs: POSIX ACLs + have no acl syscall. + diff --git a/coreutils-sysinfo.diff b/coreutils-sysinfo.diff new file mode 100644 index 0000000..d922ee6 --- /dev/null +++ b/coreutils-sysinfo.diff @@ -0,0 +1,58 @@ +--- coreutils-5.90/src/uname.c ++++ coreutils-5.90/src/uname.c +@@ -287,6 +287,36 @@ + # endif + } + #endif ++ if (element == unknown) ++ { ++ struct utsname name; ++ static char processor[sizeof (name.machine)]; ++ if (uname (&name) != 0) ++ error (EXIT_FAILURE, errno, _("cannot get system name")); ++ strcpy (processor, name.machine); ++ element = processor; ++#ifdef __linux__ ++ if (!strcmp (element, "i686")) ++ { ++ /* Check for Athlon */ ++ char line[1024]; ++ FILE *f = fopen ("/proc/cpuinfo", "r"); ++ if (f) ++ { ++ while (fgets (line, sizeof (line), f) > 0) ++ { ++ if (strncmp (line, "vendor_id", 9) == 0) ++ { ++ if (strstr (line, "AuthenticAMD")) ++ element = "athlon"; ++ break; ++ } ++ } ++ fclose (f); ++ } ++ } ++#endif ++ } + if (! (toprint == UINT_MAX && element == unknown)) + print_element (element); + } +@@ -312,6 +342,18 @@ + element = hardware_platform; + } + #endif ++ if (element == unknown) ++ { ++ struct utsname name; ++ static char hardware_platform[sizeof (name.machine)]; ++ if (uname (&name) != 0) ++ error (EXIT_FAILURE, errno, _("cannot get system name")); ++ strcpy (hardware_platform, name.machine); ++ if (hardware_platform[0] == 'i' && hardware_platform[2] == '8' ++ && hardware_platform[3] == '6' && hardware_platform[4] == 0) ++ hardware_platform[1] = '3'; ++ element = hardware_platform; ++ } + if (! (toprint == UINT_MAX && element == unknown)) + print_element (element); + } diff --git a/coreutils-xattr.diff b/coreutils-xattr.diff new file mode 100644 index 0000000..8b6e328 --- /dev/null +++ b/coreutils-xattr.diff @@ -0,0 +1,321 @@ +Index: coreutils-6.2/configure.ac +=================================================================== +--- coreutils-6.2.orig/configure.ac ++++ coreutils-6.2/configure.ac +@@ -246,6 +246,9 @@ AC_CHECK_DECLS([strtoimax, strtoumax]) + + cu_LIB_CHECK + ++# Extended attribute copying. ++AC_FUNC_XATTR ++ + AM_GNU_GETTEXT([external], [need-formatstring-macros]) + AM_GNU_GETTEXT_VERSION([0.15]) + +Index: coreutils-6.2/m4/xattr.m4 +=================================================================== +--- /dev/null ++++ coreutils-6.2/m4/xattr.m4 +@@ -0,0 +1,38 @@ ++# xattr.m4 - check for Extended Attributes (Linux) ++ ++# Copyright (C) 2003 Free Software Foundation, Inc. ++ ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 2, or (at your option) ++# any later version. ++ ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++ ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software Foundation, ++# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ ++ ++# Written by Andreas Gruenbacher. ++ ++AC_DEFUN([AC_FUNC_XATTR], ++[ ++ AC_CHECK_HEADERS(attr/error_context.h attr/libattr.h) ++ if test "$ac_cv_header_attr_libattr_h" = yes \ ++ && test "$ac_cv_header_attr_error_context_h" = yes; then ++ use_xattr=1 ++ else ++ use_xattr=0 ++ fi ++ AC_DEFINE_UNQUOTED(USE_XATTR, $use_xattr, ++ [Define if you want extended attribute support.]) ++ xattr_saved_LIBS=$LIBS ++ AC_SEARCH_LIBS(attr_copy_file, attr, ++ [test "$ac_cv_search_attr_copy_file" = "none required" || LIB_XATTR=$ac_cv_search_attr_copy_file]) ++ AC_SUBST(LIB_XATTR) ++ AC_CHECK_FUNCS(attr_copy_file) ++ LIBS=$xattr_saved_LIBS ++]) +Index: coreutils-6.2/src/Makefile.am +=================================================================== +--- coreutils-6.2.orig/src/Makefile.am ++++ coreutils-6.2/src/Makefile.am +@@ -112,6 +112,10 @@ cp_LDADD += $(LIB_ACL) + mv_LDADD += $(LIB_ACL) + ginstall_LDADD += $(LIB_ACL) + ++cp_LDADD += $(LIB_XATTR) ++mv_LDADD += $(LIB_XATTR) ++ginstall_LDADD += $(LIB_XATTR) ++ + $(PROGRAMS): ../lib/libcoreutils.a + + SUFFIXES = .sh +Index: coreutils-6.2/src/copy.c +=================================================================== +--- coreutils-6.2.orig/src/copy.c ++++ coreutils-6.2/src/copy.c +@@ -53,6 +53,12 @@ + #include "xreadlink.h" + #include "yesno.h" + ++#if USE_XATTR ++# include ++# include ++# include ++#endif ++ + #ifndef HAVE_FCHOWN + # define HAVE_FCHOWN false + # define fchown(fd, uid, gid) (-1) +@@ -118,6 +124,98 @@ is_ancestor (const struct stat *sb, cons + return false; + } + ++#if USE_XATTR ++static void ++copy_xattr_error (struct error_context *ctx, const char *fmt, ...) ++{ ++ int err = errno; ++ va_list ap; ++ int len; ++ char *buffer; ++ ++ /* There is no error function that takes a va_list argument, ++ so we print the message in a buffer first. */ ++ ++ va_start (ap, fmt); ++ len = vsnprintf (NULL, 0, fmt, ap); ++ va_end (ap); ++ if (len > 0) ++ { ++ buffer = xmalloc (len + 1); ++ va_start (ap, fmt); ++ vsnprintf (buffer, len + 1, fmt, ap); ++ va_end (ap); ++ error (0, err, "%s", buffer); ++ free (buffer); ++ } ++} ++ ++static const char * ++copy_xattr_quote (struct error_context *ctx, const char *str) ++{ ++ return xstrdup (quote (str)); ++} ++ ++static void ++copy_xattr_free (struct error_context *ctx, const char *str) ++{ ++ free ((void *) str); ++} ++ ++struct copy_xattr_context { ++ struct error_context ctx; ++ struct cp_options *x; ++}; ++ ++static int ++copy_xattr_filter (const char *name, struct error_context *ctx) ++{ ++ struct copy_xattr_context *copy_ctx = (struct copy_xattr_context *) ctx; ++ int action; ++ ++ /* We handle POSIX ACLs separately. */ ++ if (!strcmp(name, "system.posix_acl_access") ++ || !strcmp(name, "system.posix_acl_default")) ++ return 0; ++ ++ action = attr_copy_action(name, ctx); ++ return (action != ATTR_ACTION_SKIP && ++ (!copy_ctx->x->preserve_mode ++ || action != ATTR_ACTION_PERMISSIONS)); ++} ++#endif /* USE_XATTR */ ++ ++static bool ++copy_xattrs (const char *src_path, int source_desc, const char *dst_path, ++ int dest_desc, const struct cp_options *x) ++{ ++ struct copy_xattr_context copy_xattr_ctx = { ++ { copy_xattr_error, ++ copy_xattr_quote, ++ copy_xattr_free }, ++ x ++ }; ++ ++#if USE_XATTR ++ if (x->preserve_xattrs) ++ { ++ int ret; ++ ++ if (source_desc != -1 && dest_desc != -1) ++ ret = attr_copy_fd(src_path, source_desc, dst_path, dest_desc, ++ copy_xattr_filter, ©_xattr_ctx.ctx); ++ else ++ ret = attr_copy_file (src_path, dst_path, ++ copy_xattr_filter, ©_xattr_ctx.ctx); ++ return ret == 0 || !x->require_preserve; ++ } ++ else ++ return true; ++#else /* USE_XATTR */ ++ return true; ++#endif /* USE_XATTR */ ++} ++ + /* Read the contents of the directory SRC_NAME_IN, and recursively + copy the contents to DST_NAME_IN. NEW_DST is true if + DST_NAME_IN is a directory that was created previously in the +@@ -509,6 +607,9 @@ copy_reg (char const *src_name, char con + } + } + ++ if (!copy_xattrs (src_name, source_desc, dst_name, dest_desc, x)) ++ return_val = false; ++ + set_author (dst_name, dest_desc, src_sb); + + if (x->preserve_mode || x->move_mode) +@@ -1755,6 +1856,9 @@ copy_internal (char const *src_name, cha + return false; + } + ++ if (!copy_xattrs (src_name, -1, dst_name, -1, x)) ++ delayed_ok = false; ++ + set_author (dst_name, -1, &src_sb); + + if (x->preserve_mode || x->move_mode) +Index: coreutils-6.2/src/copy.h +=================================================================== +--- coreutils-6.2.orig/src/copy.h ++++ coreutils-6.2/src/copy.h +@@ -128,6 +128,9 @@ struct cp_options + bool preserve_mode; + bool preserve_timestamps; + ++ /* If true, attempt to copy extended attributes. */ ++ bool preserve_xattrs; ++ + /* Enabled for mv, and for cp by the --preserve=links option. + If true, attempt to preserve in the destination files any + logical hard links between the source files. If used with cp's +Index: coreutils-6.2/src/cp.c +=================================================================== +--- coreutils-6.2.orig/src/cp.c ++++ coreutils-6.2/src/cp.c +@@ -191,7 +191,7 @@ Mandatory arguments to long options are + -p same as --preserve=mode,ownership,timestamps\n\ + --preserve[=ATTR_LIST] preserve the specified attributes (default:\n\ + mode,ownership,timestamps), if possible\n\ +- additional attributes: links, all\n\ ++ additional attributes: links, xattrs, all\n\ + "), stdout); + fputs (_("\ + --no-preserve=ATTR_LIST don't preserve the specified attributes\n\ +@@ -724,6 +724,7 @@ cp_option_init (struct cp_options *x) + x->preserve_links = false; + x->preserve_mode = false; + x->preserve_timestamps = false; ++ x->preserve_xattrs = false; + + x->require_preserve = false; + x->recursive = false; +@@ -752,18 +753,21 @@ decode_preserve_arg (char const *arg, st + PRESERVE_TIMESTAMPS, + PRESERVE_OWNERSHIP, + PRESERVE_LINK, ++ PRESERVE_XATTRS, + PRESERVE_ALL + }; + static enum File_attribute const preserve_vals[] = + { + PRESERVE_MODE, PRESERVE_TIMESTAMPS, +- PRESERVE_OWNERSHIP, PRESERVE_LINK, PRESERVE_ALL ++ PRESERVE_OWNERSHIP, PRESERVE_LINK, ++ PRESERVE_XATTRS, PRESERVE_ALL + }; + /* Valid arguments to the `--preserve' option. */ + static char const* const preserve_args[] = + { + "mode", "timestamps", +- "ownership", "links", "all", NULL ++ "ownership", "links", ++ "xattrs", "all", NULL + }; + ARGMATCH_VERIFY (preserve_args, preserve_vals); + +@@ -799,11 +803,16 @@ decode_preserve_arg (char const *arg, st + x->preserve_links = on_off; + break; + ++ case PRESERVE_XATTRS: ++ x->preserve_xattrs = on_off; ++ break; ++ + case PRESERVE_ALL: + x->preserve_mode = on_off; + x->preserve_timestamps = on_off; + x->preserve_ownership = on_off; + x->preserve_links = on_off; ++ x->preserve_xattrs = on_off; + break; + + default: +Index: coreutils-6.2/src/install.c +=================================================================== +--- coreutils-6.2.orig/src/install.c ++++ coreutils-6.2/src/install.c +@@ -154,6 +154,7 @@ cp_option_init (struct cp_options *x) + x->preserve_links = false; + x->preserve_mode = false; + x->preserve_timestamps = false; ++ x->preserve_xattrs = false; + x->require_preserve = false; + x->recursive = false; + x->sparse_mode = SPARSE_AUTO; +Index: coreutils-6.2/src/mv.c +=================================================================== +--- coreutils-6.2.orig/src/mv.c ++++ coreutils-6.2/src/mv.c +@@ -125,6 +125,7 @@ cp_option_init (struct cp_options *x) + x->preserve_links = true; + x->preserve_mode = true; + x->preserve_timestamps = true; ++ x->preserve_xattrs = true; + x->require_preserve = false; /* FIXME: maybe make this an option */ + x->recursive = true; + x->sparse_mode = SPARSE_AUTO; /* FIXME: maybe make this an option */ +Index: coreutils-6.2/doc/coreutils.texi +=================================================================== +--- coreutils-6.2.orig/doc/coreutils.texi ++++ coreutils-6.2/doc/coreutils.texi +@@ -6948,6 +6948,8 @@ Preserve in the destination files + any links between corresponding source files. + @c Give examples illustrating how hard links are preserved. + @c Also, show how soft links map to hard links with -L and -H. ++@itemx xattrs ++Preserve extended attributes. (See /etc/xattr.conf.) + @itemx all + Preserve all file attributes. + Equivalent to specifying all of the above. diff --git a/coreutils.changes b/coreutils.changes new file mode 100644 index 0000000..9e232c6 --- /dev/null +++ b/coreutils.changes @@ -0,0 +1,898 @@ +------------------------------------------------------------------- +Wed Dec 13 13:27:36 CET 2006 - schwab@suse.de + +- Fix acl tests. + +------------------------------------------------------------------- +Sat Dec 9 01:00:28 CET 2006 - schwab@suse.de + +- Update to coreutils 6.7. + ** Bug fixes + When cp -p copied a file with special mode bits set, the same bits + were set on the copy even when ownership could not be preserved. + This could result in files that were setuid to the wrong user. + To fix this, special mode bits are now set in the copy only if its + ownership is successfully preserved. Similar problems were fixed + with mv when copying across file system boundaries. This problem + affects all versions of coreutils through 6.6. + cp --preserve=ownership would create output files that temporarily + had too-generous permissions in some cases. For example, when + copying a file with group A and mode 644 into a group-B sticky + directory, the output file was briefly readable by group B. + Fix similar problems with cp options like -p that imply + --preserve=ownership, with install -d when combined with either -o + or -g, and with mv when copying across file system boundaries. + This bug affects coreutils 6.0 through 6.6. + du --one-file-system (-x) would skip subdirectories of any directory + listed as second or subsequent command line argument. This bug affects + coreutils-6.4, 6.5 and 6.6. + +------------------------------------------------------------------- +Wed Nov 22 16:16:52 CET 2006 - schwab@suse.de + +- Update to coreutils 6.6. + ** Bug fixes + ls would segfault (dereference a NULL pointer) for a file with a + nameless group or owner. This bug was introduced in coreutils-6.5. + A bug in the latest official m4/gettext.m4 (from gettext-0.15) + made configure fail to detect gettext support, due to the unusual + way in which coreutils uses AM_GNU_GETTEXT. + ** Improved robustness + Now, du (and the other fts clients: chmod, chgrp, chown) honor a + trailing slash in the name of a symlink-to-directory even on + Solaris 9, by working around its buggy fstatat implementation. + +------------------------------------------------------------------- +Mon Nov 20 11:34:05 CET 2006 - schwab@suse.de + +- Update to coreutils 6.5. + ** Bug fixes + du (and the other fts clients: chmod, chgrp, chown) would exit early + when encountering an inaccessible directory on a system with native + openat support (i.e., linux-2.6.16 or newer along with glibc-2.4 + or newer). This bug was introduced with the switch to gnulib's + openat-based variant of fts, for coreutils-6.0. + "ln --backup f f" now produces a sensible diagnostic + ** New features + rm accepts a new option: --one-file-system + +------------------------------------------------------------------- +Mon Oct 23 10:58:38 CEST 2006 - schwab@suse.de + +- Update to coreutils 6.4. + ** Bug fixes + chgrp and chown would malfunction when invoked with both -R and -H and + with one or more of the following: --preserve-root, --verbose, --changes, + --from=o:g (chown only). This bug was introduced with the switch to + gnulib's openat-based variant of fts, for coreutils-6.0. + cp --backup dir1 dir2, would rename an existing dir2/dir1 to dir2/dir1~. + This bug was introduced in coreutils-6.0. + With --force (-f), rm no longer fails for ENOTDIR. + For example, "rm -f existing-non-directory/anything" now exits + successfully, ignoring the error about a nonexistent file. + +------------------------------------------------------------------- +Mon Oct 9 14:48:23 CEST 2006 - schwab@suse.de + +- Update to coreutils 6.3. + ** Improved robustness + pinky no longer segfaults on Darwin 7.9.0 (MacOS X 10.3.9) due to a + buggy native getaddrinfo function. + rm works around a bug in Darwin 7.9.0 (MacOS X 10.3.9) that would + sometimes keep it from removing all entries in a directory on an HFS+ + or NFS-mounted partition. + sort would fail to handle very large input (around 40GB) on systems with a + mkstemp function that returns a file descriptor limited to 32-bit offsets. + ** Bug fixes + chmod would fail unnecessarily in an unusual case: when an initially- + inaccessible argument is rendered accessible by chmod's action on a + preceding command line argument. This bug also affects chgrp, but + it is harder to demonstrate. It does not affect chown. The bug was + introduced with the switch from explicit recursion to the use of fts + in coreutils-5.1.0 (2003-10-15). + cp -i and mv -i occasionally neglected to prompt when the copy or move + action was bound to fail. This bug dates back to before fileutils-4.0. + With --verbose (-v), cp and mv would sometimes generate no output, + or neglect to report file removal. + For the "groups" command: + "groups" no longer prefixes the output with "user :" unless more + than one user is specified; this is for compatibility with BSD. + "groups user" now exits nonzero when it gets a write error. + "groups" now processes options like --help more compatibly. + shuf would infloop, given 8KB or more of piped input + ** Portability + Versions of chmod, chown, chgrp, du, and rm (tools that use openat etc.) + compiled for Solaris 8 now also work when run on Solaris 10. + +------------------------------------------------------------------- +Thu Oct 5 00:16:42 CEST 2006 - agruen@suse.de + +- cp: Replace the old --attributes=regex option with + --preserve=xattrs. Only copy extended attributes if this + option is given. Use libattr's new copy_attr_action() function + to check which attributes to copy in /etc/xattr.conf. + +------------------------------------------------------------------- +Tue Sep 19 13:20:47 CEST 2006 - schwab@suse.de + +- Disable broken autopoint. + +------------------------------------------------------------------- +Mon Sep 18 13:13:58 CEST 2006 - schwab@suse.de + +- Update to coreutils 6.2. + ** Changes in behavior + mkdir -p and install -d (or -D) now use a method that forks a child + process if the working directory is unreadable and a later argument + uses a relative file name. This avoids some race conditions, but it + means you may need to kill two processes to stop these programs. + rm now rejects attempts to remove the root directory, e.g., `rm -fr /' + now fails without removing anything. Likewise for any file name with + a final `./' or `../' component. + tail now ignores the -f option if POSIXLY_CORRECT is set, no file + operand is given, and standard input is any FIFO; formerly it did + this only for pipes. + ** Infrastructure changes + Coreutils now uses gnulib via the gnulib-tool script. + If you check the source out from CVS, then follow the instructions + in README-cvs. Although this represents a large change to the + infrastructure, it should cause no change in how the tools work. + ** Bug fixes + cp --backup no longer fails when the last component of a source file + name is "." or "..". + "ls --color" would highlight other-writable and sticky directories + no differently than regular directories on a file system with + dirent.d_type support. + "mv -T --verbose --backup=t A B" now prints the " (backup: B.~1~)" + suffix when A and B are directories as well as when they are not. + mv and "cp -r" no longer fail when invoked with two arguments + where the first one names a directory and the second name ends in + a slash and doesn't exist. E.g., "mv dir B/", for nonexistent B, + now succeeds, once more. This bug was introduced in coreutils-5.3.0. + +------------------------------------------------------------------- +Fri Sep 1 15:56:51 CEST 2006 - schwab@suse.de + +- Fix sbin patch [#202632]. + +------------------------------------------------------------------- +Mon Aug 21 11:32:53 CEST 2006 - schwab@suse.de + +- Update to coreutils 6.1. + ** Changes in behavior + df now considers BSD "kernfs" file systems to be dummies + ** Bug fixes + cp --sparse preserves sparseness at the end of a file, even when + the file's apparent size is not a multiple of its block size. + [introduced with the original design, in fileutils-4.0r, 2000-04-29] + df (with a command line argument) once again prints its header + [introduced in coreutils-6.0] + ls -CF would misalign columns in some cases involving non-stat'able files + [introduced in coreutils-6.0] + +------------------------------------------------------------------- +Tue Aug 15 17:50:41 CEST 2006 - schwab@suse.de + +- Update to coreutils 6.0. + ** Improved robustness + df: if the file system claims to have more available than total blocks, + report the number of used blocks as being "total - available" + (a negative number) rather than as garbage. + dircolors: a new autoconf run-test for AIX's buggy strndup function + prevents malfunction on that system; may also affect cut, expand, + and unexpand. + fts no longer changes the current working directory, so its clients + (chmod, chown, chgrp, du) no longer malfunction under extreme conditions. + pwd and other programs using lib/getcwd.c work even on file systems + where dirent.d_ino values are inconsistent with those from stat.st_ino. + rm's core is now reentrant: rm --recursive (-r) now processes + hierarchies without changing the working directory at all. + ** Changes in behavior + basename and dirname now treat // as different from / on platforms + where the two are distinct. + chmod, install, and mkdir now preserve a directory's set-user-ID and + set-group-ID bits unless you explicitly request otherwise. E.g., + `chmod 755 DIR' and `chmod u=rwx,go=rx DIR' now preserve DIR's + set-user-ID and set-group-ID bits instead of clearing them, and + similarly for `mkdir -m 755 DIR' and `mkdir -m u=rwx,go=rx DIR'. To + clear the bits, mention them explicitly in a symbolic mode, e.g., + `mkdir -m u=rwx,go=rx,-s DIR'. To set them, mention them explicitly + in either a symbolic or a numeric mode, e.g., `mkdir -m 2755 DIR', + `mkdir -m u=rwx,go=rx,g+s' DIR. This change is for convenience on + systems where these bits inherit from parents. Unfortunately other + operating systems are not consistent here, and portable scripts + cannot assume the bits are set, cleared, or preserved, even when the + bits are explicitly mentioned. For example, OpenBSD 3.9 `mkdir -m + 777 D' preserves D's setgid bit but `chmod 777 D' clears it. + Conversely, Solaris 10 `mkdir -m 777 D', `mkdir -m g-s D', and + `chmod 0777 D' all preserve D's setgid bit, and you must use + something like `chmod g-s D' to clear it. + `cp --link --no-dereference' now works also on systems where the + link system call cannot create a hard link to a symbolic link. + This change has no effect on systems with a Linux-based kernel. + csplit and nl now use POSIX syntax for regular expressions, not + Emacs syntax. As a result, character classes like [[:print:]] and + interval expressions like A\{1,9\} now have their usual meaning, + . no longer matches the null character, and \ must precede the + and + ? operators. + date: a command like date -d '2006-04-23 21 days ago' would print + the wrong date in some time zones. (see the test for an example) + df now considers "none" and "proc" file systems to be dummies and + therefore does not normally display them. Also, inaccessible file + systems (which can be caused by shadowed mount points or by chrooted + bind mounts) are now dummies, too. + expr no longer complains about leading ^ in a regular expression + (the anchor is ignored), or about regular expressions like A** (the + second "*" is ignored). expr now exits with status 2 (not 3) for + errors it detects in the expression's values; exit status 3 is now + used only for internal errors (such as integer overflow, which expr + now checks for). + install and mkdir now implement the X permission symbol correctly, + e.g., `mkdir -m a+X dir'; previously the X was ignored. + install now creates parent directories with mode u=rwx,go=rx (755) + instead of using the mode specified by the -m option; and it does + not change the owner or group of parent directories. This is for + compatibility with BSD and closes some race conditions. + ln now uses different (and we hope clearer) diagnostics when it fails. + ln -v now acts more like FreeBSD, so it generates output only when + successful and the output is easier to parse. + ls now defaults to --time-style='locale', not --time-style='posix-long-iso'. + However, the 'locale' time style now behaves like 'posix-long-iso' + if your locale settings appear to be messed up. This change + attempts to have the default be the best of both worlds. + mkfifo and mknod no longer set special mode bits (setuid, setgid, + and sticky) with the -m option. + nohup's usual diagnostic now more precisely specifies the I/O + redirections, e.g., "ignoring input and appending output to + nohup.out". Also, nohup now redirects stderr to nohup.out (or + $HOME/nohup.out) if stdout is closed and stderr is a tty; this is in + response to Open Group XCU ERN 71. + rm --interactive now takes an optional argument, although the + default of using no argument still acts like -i. + rm no longer fails to remove an empty, unreadable directory + seq changes: + seq defaults to a minimal fixed point format that does not lose + information if seq's operands are all fixed point decimal numbers. + You no longer need the `-f%.f' in `seq -f%.f 1048575 1024 1050623', + for example, since the default format now has the same effect. + seq now lets you use %a, %A, %E, %F, and %G formats. + seq now uses long double internally rather than double. + sort now reports incompatible options (e.g., -i and -n) rather than + silently ignoring one of them. + stat's --format=FMT option now works the way it did before 5.3.0: + FMT is automatically newline terminated. The first stable release + containing this change was 5.92. + stat accepts the new option --printf=FMT, where FMT is *not* + automatically newline terminated. + stat: backslash escapes are interpreted in a format string specified + via --printf=FMT, but not one specified via --format=FMT. That includes + octal (\ooo, at most three octal digits), hexadecimal (\xhh, one or + two hex digits), and the standard sequences (\a, \b, \f, \n, \r, \t, + \v, \", \\). + With no operand, 'tail -f' now silently ignores the '-f' only if + standard input is a FIFO or pipe and POSIXLY_CORRECT is set. + Formerly, it ignored the '-f' when standard input was a FIFO, pipe, + or socket. + ** Scheduled for removal + ptx's --copyright (-C) option is scheduled for removal in 2007, and + now evokes a warning. Use --version instead. + rm's --directory (-d) option is scheduled for removal in 2006. This + option has been silently ignored since coreutils 5.0. On systems + that support unlinking of directories, you can use the "unlink" + command to unlink a directory. + Similarly, we are considering the removal of ln's --directory (-d, + -F) option in 2006. Please write to if this + would cause a problem for you. On systems that support hard links + to directories, you can use the "link" command to create one. + ** New programs + base64: base64 encoding and decoding (RFC 3548) functionality. + sha224sum: print or check a SHA224 (224-bit) checksum + sha256sum: print or check a SHA256 (256-bit) checksum + sha384sum: print or check a SHA384 (384-bit) checksum + sha512sum: print or check a SHA512 (512-bit) checksum + shuf: Shuffle lines of text. + ** New features + chgrp now supports --preserve-root, --no-preserve-root (default), + as it was documented to do, and just as chmod, chown, and rm do. + New dd iflag= and oflag= flags: + 'directory' causes dd to fail unless the file is a directory, on + hosts that support this (e.g., Linux kernels, version 2.1.126 and + later). This has limited utility but is present for completeness. + 'noatime' causes dd to read a file without updating its access + time, on hosts that support this (e.g., Linux kernels, version + 2.6.8 and later). + 'nolinks' causes dd to fail if the file has multiple hard links, + on hosts that support this (e.g., Solaris 10 and later). + ls accepts the new option --group-directories-first, to make it + list directories before files. + rm now accepts the -I (--interactive=once) option. This new option + prompts once if rm is invoked recursively or if more than three + files are being deleted, which is less intrusive than -i prompting + for every file, but provides almost the same level of protection + against mistakes. + shred and sort now accept the --random-source option. + sort now accepts the --random-sort (-R) option and `R' ordering option. + sort now supports obsolete usages like "sort +1 -2" unless + POSIXLY_CORRECT is set. However, when conforming to POSIX + 1003.1-2001 "sort +1" still sorts the file named "+1". + wc accepts a new option --files0-from=FILE, where FILE contains a + list of NUL-terminated file names. + ** Bug fixes + cat with any of the options, -A -v -e -E -T, when applied to a + file in /proc or /sys (linux-specific), would truncate its output, + usually printing nothing. + cp -p would fail in a /proc-less chroot, on some systems + When `cp -RL' encounters the same directory more than once in the + hierarchy beneath a single command-line argument, it no longer confuses + them with hard-linked directories. + fts-using tools (chmod, chown, chgrp, du) no longer fail due to + a double-free bug -- it could be triggered by making a directory + inaccessible while e.g., du is traversing the hierarchy under it. + fts-using tools (chmod, chown, chgrp, du) no longer misinterpret + a very long symlink chain as a dangling symlink. Before, such a + misinterpretation would cause these tools not to diagnose an ELOOP error. + ls --indicator-style=file-type would sometimes stat a symlink + unnecessarily. + ls --file-type worked like --indicator-style=slash (-p), + rather than like --indicator-style=file-type. + mv: moving a symlink into the place of an existing non-directory is + now done atomically; before, mv would first unlink the destination. + mv -T DIR EMPTY_DIR no longer fails unconditionally. Also, mv can + now remove an empty destination directory: mkdir -p a b/a; mv a b + rm (on systems with openat) can no longer exit before processing + all command-line arguments. + rm is no longer susceptible to a few low-probability memory leaks. + rm -r no longer fails to remove an inaccessible and empty directory + rm -r's cycle detection code can no longer be tricked into reporting + a false positive (introduced in fileutils-4.1.9). + shred --remove FILE no longer segfaults on Gentoo systems + sort would fail for large inputs (~50MB) on systems with a buggy + mkstemp function. sort and tac now use the replacement mkstemp + function, and hence are no longer subject to limitations (of 26 or 32, + on the maximum number of files from a given template) on HP-UX 10.20, + SunOS 4.1.4, Solaris 2.5.1 and OSF1/Tru64 V4.0F&V5.1. + tail -f once again works on a file with the append-only + attribute (affects at least Linux ext2, ext3, xfs file systems) + +------------------------------------------------------------------- +Tue Aug 8 15:29:32 CEST 2006 - schwab@suse.de + +- Move sux to %{_bindir}. + +------------------------------------------------------------------- +Mon Jun 26 13:20:23 CEST 2006 - schwab@suse.de + +- Update to coreutils 5.97. + ** Bug fixes + rebuild with better autoconf test for when the lstat replacement + function is needed -- required for Solaris 9 + cat with any of the options, -A -v -e -E -T, when applied to a + file in /proc or /sys (linux-specific), would truncate its output, + usually printing nothing. + ** Improved robustness + dircolors: a new autoconf run-test for AIX's buggy strndup function + prevents malfunction on that system; may also affect cut, expand, + and unexpand. + ** New features + chgrp now supports --preserve-root, --no-preserve-root (default), + as it was documented to do, and just as chmod, chown, and rm do. + +------------------------------------------------------------------- +Thu Jun 22 15:38:27 CEST 2006 - schwab@suse.de + +- Fix conflict with . + +------------------------------------------------------------------- +Mon May 22 13:34:26 CEST 2006 - schwab@suse.de + +- Update to coreutils 5.96. + +------------------------------------------------------------------- +Sat May 13 16:39:03 CEST 2006 - schwab@suse.de + +- Update to coreutils 5.95. + +------------------------------------------------------------------- +Fri Apr 7 16:34:42 CEST 2006 - cthiel@suse.de + +- added Obsoletes: libselinux (hack for bug #156519) + +------------------------------------------------------------------- +Mon Feb 13 01:31:31 CET 2006 - schwab@suse.de + +- Fix spurious failure with cp -LR. +- Move check for /proc. + +------------------------------------------------------------------- +Mon Jan 30 16:27:11 CET 2006 - schwab@suse.de + +- Always print newline after format in stat [#145905]. +- Barf if /proc is not mounted. + +------------------------------------------------------------------- +Wed Jan 25 21:30:02 CET 2006 - mls@suse.de + +- converted neededforbuild to BuildRequires + +------------------------------------------------------------------- +Thu Jan 19 16:43:57 CET 2006 - meissner@suse.de + +- Do not strip /bin/su. + +------------------------------------------------------------------- +Wed Jan 11 15:20:50 CET 2006 - schwab@suse.de + +- Fix infloop when ignoring characters [#141756]. + +------------------------------------------------------------------- +Mon Dec 19 12:03:23 CET 2005 - kukuk@suse.de + +- Add fallback if futimesat does not work + +------------------------------------------------------------------- +Mon Dec 5 17:15:17 CET 2005 - ke@suse.de + +- Fix typo in German translation file; reported by Olaf Hering + [#105863]. + +------------------------------------------------------------------- +Mon Dec 5 16:53:58 CET 2005 - schwab@suse.de + +- Drop SELinux support. + +------------------------------------------------------------------- +Tue Nov 15 16:47:44 CET 2005 - uli@suse.de + +- some tests fail on ARM (QEMU problem?); ignore for now + +------------------------------------------------------------------- +Sun Nov 6 17:42:27 CET 2005 - schwab@suse.de + +- Update to coreutils 5.93. + +------------------------------------------------------------------- +Wed Nov 2 14:55:10 CET 2005 - schwab@suse.de + +- Update to coreutils 5.92. +- Fix invalid use of va_list. +- Add some fixes from cvs. + +------------------------------------------------------------------- +Thu Oct 20 10:16:26 CEST 2005 - schwab@suse.de + +- Reenable DEFAULT_POSIX2_VERSION. + +------------------------------------------------------------------- +Wed Oct 19 19:04:17 CEST 2005 - agruen@suse.de + +- Add acl and xattr patches. + +------------------------------------------------------------------- +Mon Oct 17 15:45:25 CEST 2005 - schwab@suse.de + +- Update to coreutils 5.91. + +------------------------------------------------------------------- +Sat Oct 1 16:05:24 CEST 2005 - schwab@suse.de + +- Update to coreutils 5.90. +- Disable acl patches for now. + +------------------------------------------------------------------- +Sun Sep 25 21:33:05 CEST 2005 - schwab@suse.de + +- Fix warning. + +------------------------------------------------------------------- +Wed Aug 24 15:07:03 CEST 2005 - werner@suse.de + +- Let `su' handle /sbin and /usr/sbin in path + +------------------------------------------------------------------- +Mon Aug 1 16:48:44 CEST 2005 - kukuk@suse.de + +- And yet another uninitialized variable fix. + +------------------------------------------------------------------- +Fri Jul 29 16:01:07 CEST 2005 - schwab@suse.de + +- Fix another uninitialized variable. + +------------------------------------------------------------------- +Wed Jul 6 18:33:56 CEST 2005 - schwab@suse.de + +- Fix uninitialized variable. + +------------------------------------------------------------------- +Mon Jul 4 11:00:33 CEST 2005 - schwab@suse.de + +- Update i18n patch. + +------------------------------------------------------------------- +Mon Jun 20 23:11:53 CEST 2005 - schwab@suse.de + +- Fix last change. + +------------------------------------------------------------------- +Wed Jun 15 17:34:06 CEST 2005 - kukuk@suse.de + +- Compile/link su with -fpie/-pie + +------------------------------------------------------------------- +Sat May 21 16:46:32 CEST 2005 - kukuk@suse.de + +- Add support for /etc/default/su + +------------------------------------------------------------------- +Mon May 2 11:32:05 CEST 2005 - kukuk@suse.de + +- Don't overwrite PATH if su is called with "-" option. + +------------------------------------------------------------------- +Wed Mar 2 14:29:21 CET 2005 - schwab@suse.de + +- Fix merge error [#67103]. + +------------------------------------------------------------------- +Mon Feb 28 16:18:57 CET 2005 - schwab@suse.de + +- Call pam_getenvlist before pam_end. + +------------------------------------------------------------------- +Mon Feb 28 13:16:14 CET 2005 - schwab@suse.de + +- Link su to sux [#66830]. + +------------------------------------------------------------------- +Wed Feb 2 19:48:49 CET 2005 - schwab@suse.de + +- Handle xfs and jfs in stat [#50415]. + +------------------------------------------------------------------- +Wed Feb 2 02:02:28 CET 2005 - schwab@suse.de + +- Handle subfs like autofs. + +------------------------------------------------------------------- +Tue Jan 25 13:52:57 CET 2005 - schwab@suse.de + +- Fix path_concat. + +------------------------------------------------------------------- +Thu Jan 20 17:26:10 CET 2005 - schwab@suse.de + +- Use pam_xauth [#42238]. + +------------------------------------------------------------------- +Fri Jan 14 22:04:46 CET 2005 - schwab@suse.de + +- Fix merge error [#49853]. + +------------------------------------------------------------------- +Tue Jan 11 18:39:44 CET 2005 - schwab@suse.de + +- Update to coreutils 5.3.0. + +------------------------------------------------------------------- +Mon Nov 8 17:15:39 CET 2004 - kukuk@suse.de + +- Use common-* PAM config files for su PAM configuration + +------------------------------------------------------------------- +Mon Oct 25 15:01:04 CEST 2004 - schwab@suse.de + +- Fix last change. +- Fix selinux patch. + +------------------------------------------------------------------- +Wed Oct 20 01:55:31 CEST 2004 - ro@suse.de + +- remove no language support (nb is already there) + +------------------------------------------------------------------- +Sat Oct 2 03:08:31 CEST 2004 - agruen@suse.de + +- #46609: Fix chown and chgrp utilities for uid == (uid_t) -1 and + gid == (gid_t) -1 case. +- Add missing #include to have NULL defined in lib/acl.c + +------------------------------------------------------------------- +Fri Sep 10 00:13:28 CEST 2004 - schwab@suse.de + +- Fix uninitialized variable [#44929]. +- Fix selinux patch. + +------------------------------------------------------------------- +Wed Aug 25 13:32:20 CEST 2004 - schwab@suse.de + +- Fix hardlink accounting patch. + +------------------------------------------------------------------- +Mon May 24 18:07:35 CEST 2004 - schwab@suse.de + +- Update testsuite for change in chown. + +------------------------------------------------------------------- +Mon May 24 16:34:33 CEST 2004 - schwab@suse.de + +- Precompute length in caller of ismbblank to avoid quadratic behaviour + [#40741]. + +------------------------------------------------------------------- +Mon May 17 15:33:12 CEST 2004 - schwab@suse.de + +- Fix handling of symlinks in chown [#40691]. + +------------------------------------------------------------------- +Sat Apr 17 20:09:11 CEST 2004 - schwab@suse.de + +- Pacify autobuild. + +------------------------------------------------------------------- +Fri Apr 2 14:48:24 CEST 2004 - schwab@suse.de + +- Add support for IUTF8 in stty. + +------------------------------------------------------------------- +Tue Mar 30 18:39:10 CEST 2004 - schwab@suse.de + +- Fix merge error in selinux patch [#37431]. + +------------------------------------------------------------------- +Mon Mar 29 14:51:59 CEST 2004 - schwab@suse.de + +- Fix hardlink accounting in du. + +------------------------------------------------------------------- +Mon Mar 22 14:19:59 CET 2004 - schwab@suse.de + +- Fix race in the testsuite. + +------------------------------------------------------------------- +Mon Mar 15 16:21:20 CET 2004 - kukuk@suse.de + +- Update SELinux patch to new libselinux interface + +------------------------------------------------------------------- +Mon Mar 15 11:25:54 CET 2004 - schwab@suse.de + +- Fix date parsing. + +------------------------------------------------------------------- +Sat Mar 13 18:21:40 CET 2004 - schwab@suse.de + +- Update to coreutils 5.2.1. + * Includes mv fix. + * Fix sparse handling in cp. + * Fix descriptor leak in nohup. + * Fix POSIX issues in expr. + * Always allow user.group in chown. + +------------------------------------------------------------------- +Fri Mar 12 18:18:33 CET 2004 - schwab@suse.de + +- Fix sysinfo patch [#35337]. + +------------------------------------------------------------------- +Fri Mar 12 16:57:21 CET 2004 - schwab@suse.de + +- Fix preserving links in mv. + +------------------------------------------------------------------- +Wed Mar 3 15:28:06 CET 2004 - schwab@suse.de + +- Fix help output from mkdir. + +------------------------------------------------------------------- +Fri Feb 20 01:06:39 CET 2004 - schwab@suse.de + +- Update to coreutils 5.2.0. + +------------------------------------------------------------------- +Mon Feb 9 18:03:45 CET 2004 - schwab@suse.de + +- Update to coreutils 5.1.3. + +------------------------------------------------------------------- +Mon Feb 2 17:20:11 CET 2004 - agruen@suse.de + +- Update acl and xattr patches, and add some Changelog text. + +------------------------------------------------------------------- +Mon Jan 26 14:34:12 CET 2004 - schwab@suse.de + +- Update to coreutils 5.1.2. + +------------------------------------------------------------------- +Fri Jan 23 17:16:32 CET 2004 - schwab@suse.de + +- Don't link [ to test. + +------------------------------------------------------------------- +Mon Jan 19 13:26:00 CET 2004 - schwab@suse.de + +- Update to coreutils 5.1.1. +- Default to POSIX.2-1992. + +------------------------------------------------------------------- +Fri Jan 16 12:26:48 CET 2004 - kukuk@suse.de + +- Add pam-devel to neededforbuild + +------------------------------------------------------------------- +Fri Jan 9 13:51:53 CET 2004 - schwab@suse.de + +- Fix spurious test failure. + +------------------------------------------------------------------- +Thu Jan 8 16:48:32 CET 2004 - schwab@suse.de + +- Update to coreutils 5.1.0. + +------------------------------------------------------------------- +Fri Dec 12 23:08:27 CET 2003 - schwab@suse.de + +- Fix use of AC_SEARCH_LIBS. + +------------------------------------------------------------------- +Tue Dec 9 17:31:09 CET 2003 - schwab@suse.de + +- Cleanup SELinux patch. + +------------------------------------------------------------------- +Tue Dec 9 16:07:28 CET 2003 - kukuk@suse.de + +- Add SELinux patch. + +------------------------------------------------------------------- +Wed Nov 26 11:59:34 CET 2003 - schwab@suse.de + +- Fix sorting of months in multibyte case [#33299]. + +------------------------------------------------------------------- +Wed Oct 22 15:33:21 CEST 2003 - schwab@suse.de + +- Fix building without extended attributes. + +------------------------------------------------------------------- +Wed Oct 15 15:17:11 CEST 2003 - schwab@suse.de + +- Cleanup sysinfo patch. + +------------------------------------------------------------------- +Fri Sep 19 11:09:36 CEST 2003 - kukuk@suse.de + +- Add missing textutil to Provides + +------------------------------------------------------------------- +Mon Aug 25 17:42:23 CEST 2003 - agruen@suse.de + +- Fix uname command to report reasonable processor and platform + information (coreutils-sysinfo.diff: based on similar RedHat + patch). + +------------------------------------------------------------------- +Mon Jul 21 15:23:56 CEST 2003 - schwab@suse.de + +- Fix typo in i18n patch for join. + +------------------------------------------------------------------- +Fri Jul 18 12:05:56 CEST 2003 - schwab@suse.de + +- Avoid abort in sort on inconsistent locales [#26506]. + +------------------------------------------------------------------- +Tue Jul 15 15:16:37 CEST 2003 - okir@suse.de + +- make su export variables declared via pam_putenv + +------------------------------------------------------------------- +Wed May 28 10:15:39 CEST 2003 - kukuk@suse.de + +- PAM fixes for su: + - Move pam_open_session call before dropping privilegs, session + management needs max. possible credentials and needs to be done + before we change into the home directory of the user. + - Don't set PAM_TTY and PAM_RUSER to fake names. + - Use conversion function from libpam_misc. + +------------------------------------------------------------------- +Fri May 16 11:11:44 CEST 2003 - schwab@suse.de + +- Fix exit status from su. + +------------------------------------------------------------------- +Thu Apr 24 11:43:14 CEST 2003 - ro@suse.de + +- fix head calling syntax + +------------------------------------------------------------------- +Mon Apr 7 13:11:19 CEST 2003 - schwab@suse.de + +- Only delete info entries when removing last version. + +------------------------------------------------------------------- +Fri Apr 4 17:58:20 CEST 2003 - schwab@suse.de + +- Update to coreutils 5.0. + +------------------------------------------------------------------- +Mon Mar 31 12:53:29 CEST 2003 - schwab@suse.de + +- Update to coreutils 4.5.12. + +------------------------------------------------------------------- +Thu Mar 20 18:10:25 CET 2003 - schwab@suse.de + +- Update to coreutils 4.5.11. + +------------------------------------------------------------------- +Mon Mar 10 13:42:36 CET 2003 - schwab@suse.de + +- Fix LFS bug in du [#24960]. + +------------------------------------------------------------------- +Thu Feb 27 15:39:06 CET 2003 - schwab@suse.de + +- Readd textutils i18n patches. + +------------------------------------------------------------------- +Thu Feb 27 14:53:19 CET 2003 - agruen@suse.de + +- Per hint from Andreas Schwab, don't use awk in autoconf. (The + improved test is simpler, too.) + +------------------------------------------------------------------- +Thu Feb 27 05:29:05 CET 2003 - agruen@suse.de + +- Fix autoconf test for attr_copy_file that caused all binaries + to be linked needlessly against libattr.so. + +------------------------------------------------------------------- +Tue Feb 25 15:18:39 CET 2003 - agruen@suse.de + +- Extended attribute copying: Use the newly exported + attr_copy_check_permissions() callback exported by libattr.so, + so that the EA copying done by coreutils is consistent with + other apps [#24244]. + +------------------------------------------------------------------- +Mon Feb 24 16:27:21 CET 2003 - schwab@suse.de + +- Update to coreutils 4.5.8. + * Fixes bugs in du. + +------------------------------------------------------------------- +Mon Feb 17 15:00:04 CET 2003 - agruen@suse.de + +- Add extended attribute copying patch: Affects cp, mv, install. + See the cp manual page for details on the changes in cp. The + mv utility always tries to copy extended attributes; install + never does. + +------------------------------------------------------------------- +Mon Feb 10 13:16:58 CET 2003 - schwab@suse.de + +- Update to coreutils 4.5.7. + +------------------------------------------------------------------- +Fri Feb 7 13:47:58 CET 2003 - kukuk@suse.de + +- Use pam_unix2.so instead of pam_unix.so, use same rules for + password changing as passwd. + +------------------------------------------------------------------- +Thu Feb 6 17:48:08 CET 2003 - schwab@suse.de + +- Use %install_info. + +------------------------------------------------------------------- +Thu Feb 6 17:05:42 CET 2003 - schwab@suse.de + +- Update to coreutils 4.5.6. + +------------------------------------------------------------------- +Mon Feb 3 14:47:47 CET 2003 - schwab@suse.de + +- Package created, combining textutils, sh-utils and fileutils. + diff --git a/coreutils.spec b/coreutils.spec new file mode 100644 index 0000000..66c5340 --- /dev/null +++ b/coreutils.spec @@ -0,0 +1,738 @@ +# +# spec file for package coreutils (Version 6.7) +# +# Copyright (c) 2006 SUSE LINUX Products GmbH, Nuernberg, Germany. +# This file and all modifications and additions to the pristine +# package are under the same license as the package itself. +# +# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# + +# norootforbuild + +Name: coreutils +BuildRequires: help2man libacl-devel pam-devel +URL: http://www.gnu.org/software/coreutils/ +License: GNU General Public License (GPL) +Group: System/Base +Provides: fileutil fileutils sh-utils sh_utils shellutl stat textutil textutils textutl txtutils +Obsoletes: fileutil fileutils sh-utils sh_utils stat textutil textutils +Obsoletes: libselinux <= 1.23.11-3 libselinux-32bit >= 9 libselinux-64bit = 9 libselinux-x86 = 9 +Autoreqprov: on +PreReq: %{install_info_prereq} +Version: 6.7 +Release: 3 +Summary: GNU Core Utilities +Source: coreutils-%{version}.tar.bz2 +Source1: su.pamd +Source2: su.default +Patch: coreutils-%{version}.diff +Patch1: coreutils-xattr.diff +Patch30: coreutils-changelog.diff +Patch4: coreutils-5.3.0-i18n-0.1.patch +Patch5: i18n-uninit.diff +Patch6: i18n-infloop.diff +Patch7: coreutils-5.0-pam-env.patch +Patch8: coreutils-sysinfo.diff +Patch9: acl-test.diff +Patch10: getcwd.diff +Patch16: invalid-ids.diff +Patch17: no-no.diff +Patch20: coreutils-5.3.0-pie.diff +Patch21: coreutils-5.3.0-sbin4su.diff +BuildRoot: %{_tmppath}/%{name}-%{version}-build + +%description +Basic file, shell, and text manipulation utilities. The package +contains the following programs: + +basename cat chgrp chmod chown chroot cksum comm cp csplit cut date dd +df dir dircolors dirname du echo env expand expr factor false fmt fold +install groups head id join kill link ln logname ls md5sum mkdir mkfifo +mknod mv nice nl nohup od paste pathchk pinky pr printenv printf ptx +pwd readlink rm rmdir seq sha1sum shred sleep sort split stat stty su +sum sync tac tail tee test touch tr true tsort tty uname unexpand uniq +unlink uptime users vdir wc who whoami yes + + + +Authors: +-------- + Arnold Robbins + Colin Plumb + David M. Ihnat + David MacKenzie + François Pinard + H. Peter Anvin + Ian Lance Taylor + Jay Lepreau + Jim Kingdon + Jim Meyering + Joseph Arceneaux + Kaveh Ghazi + Kayvan Aghaiepour + Larry McVoy + Mark Kettenis + Michael Meskes + Michael Stone + Mike Haertel + Mike Parker + Paul Eggert + Paul Rubin + Pete TerMaat + Randy Smith + Richard M. Stallman + Richard Mlynarik + Roland Huebner + Roland McGrath + Ross Paterson + Scott Bartram + Scott Miller + Stuart Kemp + Torbjorn Granlund + Ulrich Drepper + +%prep +%setup -q +%patch1 -p1 +%patch4 -p1 +%patch5 +%patch6 +%patch +%patch7 -p1 +%patch8 -p1 +%patch9 -p1 +%patch10 +%patch16 -p1 +%patch17 +%patch20 +%patch21 +rm -f po/no.* + +%build +AUTOPOINT=true autoreconf -fi +./configure CFLAGS="-DUSE_PAM $RPM_OPT_FLAGS -Wall" \ + DEFAULT_POSIX2_VERSION=199209 \ + --prefix=%{_prefix} --mandir=%{_mandir} \ + --infodir=%{_infodir} --without-included-regex +make %{?jobs:-j%jobs} PAMLIBS="-lpam -ldl" +if test $EUID -eq 0; then + su nobody -c make check + make check-root VERBOSE=yes +else +%ifarch %arm + make -k check VERBOSE=yes || echo make check failed +%else + make check VERBOSE=yes +%endif +fi + +%install +make DESTDIR="$RPM_BUILD_ROOT" install +test -f $RPM_BUILD_ROOT%{_bindir}/su || \ + install src/su $RPM_BUILD_ROOT%{_bindir}/su +install -d $RPM_BUILD_ROOT/bin +for i in basename cat chgrp chmod chown cp date dd df echo false kill ln ls mkdir mknod mv pwd rm rmdir sleep sort stty su sync touch true uname +do + mv $RPM_BUILD_ROOT%{_bindir}/$i $RPM_BUILD_ROOT/bin/$i + test $i = su && echo -n '%%attr(4755,root,root) ' + echo /bin/$i +done > bin.files +ln -sf ../../bin/basename ../../bin/sort ../../bin/touch $RPM_BUILD_ROOT%{_bindir} +install -d -m 755 $RPM_BUILD_ROOT/etc/pam.d +install -m 644 $RPM_SOURCE_DIR/su.pamd $RPM_BUILD_ROOT/etc/pam.d/su +install -d -m 755 $RPM_BUILD_ROOT/etc/default +install -m 644 $RPM_SOURCE_DIR/su.default $RPM_BUILD_ROOT/etc/default/su +ln -sf /bin/su $RPM_BUILD_ROOT%{_bindir}/sux +rm -f $RPM_BUILD_ROOT%{_bindir}/hostid +rm -f $RPM_BUILD_ROOT%{_bindir}/hostname +rm -f $RPM_BUILD_ROOT%{_mandir}/man1/hostid.1 +rm -f $RPM_BUILD_ROOT%{_mandir}/man1/hostname.1 +%find_lang %name +cat bin.files %name.lang > extra-files + +%post +%install_info --info-dir=%{_infodir} %{_infodir}/coreutils.info.gz + +%postun +%install_info_delete --info-dir=%{_infodir} %{_infodir}/coreutils.info.gz + +%clean +rm -rf $RPM_BUILD_ROOT + +%files -f extra-files +%defattr(-,root,root) +%doc README NEWS +%config /etc/pam.d/su +%config(noreplace) /etc/default/su +%{_bindir}/* +%doc %{_infodir}/coreutils.info*.gz +%doc %{_mandir}/man1/*.1.gz +%dir %{_prefix}/share/locale/*/LC_TIME + +%changelog -n coreutils +* Wed Dec 13 2006 - schwab@suse.de +- Fix acl tests. +* Sat Dec 09 2006 - schwab@suse.de +- Update to coreutils 6.7. + ** Bug fixes + When cp -p copied a file with special mode bits set, the same bits + were set on the copy even when ownership could not be preserved. + This could result in files that were setuid to the wrong user. + To fix this, special mode bits are now set in the copy only if its + ownership is successfully preserved. Similar problems were fixed + with mv when copying across file system boundaries. This problem + affects all versions of coreutils through 6.6. + cp --preserve=ownership would create output files that temporarily + had too-generous permissions in some cases. For example, when + copying a file with group A and mode 644 into a group-B sticky + directory, the output file was briefly readable by group B. + Fix similar problems with cp options like -p that imply + --preserve=ownership, with install -d when combined with either -o + or -g, and with mv when copying across file system boundaries. + This bug affects coreutils 6.0 through 6.6. + du --one-file-system (-x) would skip subdirectories of any directory + listed as second or subsequent command line argument. This bug affects + coreutils-6.4, 6.5 and 6.6. +* Wed Nov 22 2006 - schwab@suse.de +- Update to coreutils 6.6. + ** Bug fixes + ls would segfault (dereference a NULL pointer) for a file with a + nameless group or owner. This bug was introduced in coreutils-6.5. + A bug in the latest official m4/gettext.m4 (from gettext-0.15) + made configure fail to detect gettext support, due to the unusual + way in which coreutils uses AM_GNU_GETTEXT. + ** Improved robustness + Now, du (and the other fts clients: chmod, chgrp, chown) honor a + trailing slash in the name of a symlink-to-directory even on + Solaris 9, by working around its buggy fstatat implementation. +* Mon Nov 20 2006 - schwab@suse.de +- Update to coreutils 6.5. + ** Bug fixes + du (and the other fts clients: chmod, chgrp, chown) would exit early + when encountering an inaccessible directory on a system with native + openat support (i.e., linux-2.6.16 or newer along with glibc-2.4 + or newer). This bug was introduced with the switch to gnulib's + openat-based variant of fts, for coreutils-6.0. + "ln --backup f f" now produces a sensible diagnostic + ** New features + rm accepts a new option: --one-file-system +* Mon Oct 23 2006 - schwab@suse.de +- Update to coreutils 6.4. + ** Bug fixes + chgrp and chown would malfunction when invoked with both -R and -H and + with one or more of the following: --preserve-root, --verbose, --changes, + --from=o:g (chown only). This bug was introduced with the switch to + gnulib's openat-based variant of fts, for coreutils-6.0. + cp --backup dir1 dir2, would rename an existing dir2/dir1 to dir2/dir1~. + This bug was introduced in coreutils-6.0. + With --force (-f), rm no longer fails for ENOTDIR. + For example, "rm -f existing-non-directory/anything" now exits + successfully, ignoring the error about a nonexistent file. +* Mon Oct 09 2006 - schwab@suse.de +- Update to coreutils 6.3. + ** Improved robustness + pinky no longer segfaults on Darwin 7.9.0 (MacOS X 10.3.9) due to a + buggy native getaddrinfo function. + rm works around a bug in Darwin 7.9.0 (MacOS X 10.3.9) that would + sometimes keep it from removing all entries in a directory on an HFS+ + or NFS-mounted partition. + sort would fail to handle very large input (around 40GB) on systems with a + mkstemp function that returns a file descriptor limited to 32-bit offsets. + ** Bug fixes + chmod would fail unnecessarily in an unusual case: when an initially- + inaccessible argument is rendered accessible by chmod's action on a + preceding command line argument. This bug also affects chgrp, but + it is harder to demonstrate. It does not affect chown. The bug was + introduced with the switch from explicit recursion to the use of fts + in coreutils-5.1.0 (2003-10-15). + cp -i and mv -i occasionally neglected to prompt when the copy or move + action was bound to fail. This bug dates back to before fileutils-4.0. + With --verbose (-v), cp and mv would sometimes generate no output, + or neglect to report file removal. + For the "groups" command: + "groups" no longer prefixes the output with "user :" unless more + than one user is specified; this is for compatibility with BSD. + "groups user" now exits nonzero when it gets a write error. + "groups" now processes options like --help more compatibly. + shuf would infloop, given 8KB or more of piped input + ** Portability + Versions of chmod, chown, chgrp, du, and rm (tools that use openat etc.) + compiled for Solaris 8 now also work when run on Solaris 10. +* Thu Oct 05 2006 - agruen@suse.de +- cp: Replace the old --attributes=regex option with + --preserve=xattrs. Only copy extended attributes if this + option is given. Use libattr's new copy_attr_action() function + to check which attributes to copy in /etc/xattr.conf. +* Tue Sep 19 2006 - schwab@suse.de +- Disable broken autopoint. +* Mon Sep 18 2006 - schwab@suse.de +- Update to coreutils 6.2. + ** Changes in behavior + mkdir -p and install -d (or -D) now use a method that forks a child + process if the working directory is unreadable and a later argument + uses a relative file name. This avoids some race conditions, but it + means you may need to kill two processes to stop these programs. + rm now rejects attempts to remove the root directory, e.g., `rm -fr /' + now fails without removing anything. Likewise for any file name with + a final `./' or `../' component. + tail now ignores the -f option if POSIXLY_CORRECT is set, no file + operand is given, and standard input is any FIFO; formerly it did + this only for pipes. + ** Infrastructure changes + Coreutils now uses gnulib via the gnulib-tool script. + If you check the source out from CVS, then follow the instructions + in README-cvs. Although this represents a large change to the + infrastructure, it should cause no change in how the tools work. + ** Bug fixes + cp --backup no longer fails when the last component of a source file + name is "." or "..". + "ls --color" would highlight other-writable and sticky directories + no differently than regular directories on a file system with + dirent.d_type support. + "mv -T --verbose --backup=t A B" now prints the " (backup: B.~1~)" + suffix when A and B are directories as well as when they are not. + mv and "cp -r" no longer fail when invoked with two arguments + where the first one names a directory and the second name ends in + a slash and doesn't exist. E.g., "mv dir B/", for nonexistent B, + now succeeds, once more. This bug was introduced in coreutils-5.3.0. +* Fri Sep 01 2006 - schwab@suse.de +- Fix sbin patch [#202632]. +* Mon Aug 21 2006 - schwab@suse.de +- Update to coreutils 6.1. + ** Changes in behavior + df now considers BSD "kernfs" file systems to be dummies + ** Bug fixes + cp --sparse preserves sparseness at the end of a file, even when + the file's apparent size is not a multiple of its block size. + [introduced with the original design, in fileutils-4.0r, 2000-04-29] + df (with a command line argument) once again prints its header + [introduced in coreutils-6.0] + ls -CF would misalign columns in some cases involving non-stat'able files + [introduced in coreutils-6.0] +* Tue Aug 15 2006 - schwab@suse.de +- Update to coreutils 6.0. + ** Improved robustness + df: if the file system claims to have more available than total blocks, + report the number of used blocks as being "total - available" + (a negative number) rather than as garbage. + dircolors: a new autoconf run-test for AIX's buggy strndup function + prevents malfunction on that system; may also affect cut, expand, + and unexpand. + fts no longer changes the current working directory, so its clients + (chmod, chown, chgrp, du) no longer malfunction under extreme conditions. + pwd and other programs using lib/getcwd.c work even on file systems + where dirent.d_ino values are inconsistent with those from stat.st_ino. + rm's core is now reentrant: rm --recursive (-r) now processes + hierarchies without changing the working directory at all. + ** Changes in behavior + basename and dirname now treat // as different from / on platforms + where the two are distinct. + chmod, install, and mkdir now preserve a directory's set-user-ID and + set-group-ID bits unless you explicitly request otherwise. E.g., + `chmod 755 DIR' and `chmod u=rwx,go=rx DIR' now preserve DIR's + set-user-ID and set-group-ID bits instead of clearing them, and + similarly for `mkdir -m 755 DIR' and `mkdir -m u=rwx,go=rx DIR'. To + clear the bits, mention them explicitly in a symbolic mode, e.g., + `mkdir -m u=rwx,go=rx,-s DIR'. To set them, mention them explicitly + in either a symbolic or a numeric mode, e.g., `mkdir -m 2755 DIR', + `mkdir -m u=rwx,go=rx,g+s' DIR. This change is for convenience on + systems where these bits inherit from parents. Unfortunately other + operating systems are not consistent here, and portable scripts + cannot assume the bits are set, cleared, or preserved, even when the + bits are explicitly mentioned. For example, OpenBSD 3.9 `mkdir -m + 777 D' preserves D's setgid bit but `chmod 777 D' clears it. + Conversely, Solaris 10 `mkdir -m 777 D', `mkdir -m g-s D', and + `chmod 0777 D' all preserve D's setgid bit, and you must use + something like `chmod g-s D' to clear it. + `cp --link --no-dereference' now works also on systems where the + link system call cannot create a hard link to a symbolic link. + This change has no effect on systems with a Linux-based kernel. + csplit and nl now use POSIX syntax for regular expressions, not + Emacs syntax. As a result, character classes like [[:print:]] and + interval expressions like A\{1,9\} now have their usual meaning, + . no longer matches the null character, and \ must precede the + and + ? operators. + date: a command like date -d '2006-04-23 21 days ago' would print + the wrong date in some time zones. (see the test for an example) + df now considers "none" and "proc" file systems to be dummies and + therefore does not normally display them. Also, inaccessible file + systems (which can be caused by shadowed mount points or by chrooted + bind mounts) are now dummies, too. + expr no longer complains about leading ^ in a regular expression + (the anchor is ignored), or about regular expressions like A** (the + second "*" is ignored). expr now exits with status 2 (not 3) for + errors it detects in the expression's values; exit status 3 is now + used only for internal errors (such as integer overflow, which expr + now checks for). + install and mkdir now implement the X permission symbol correctly, + e.g., `mkdir -m a+X dir'; previously the X was ignored. + install now creates parent directories with mode u=rwx,go=rx (755) + instead of using the mode specified by the -m option; and it does + not change the owner or group of parent directories. This is for + compatibility with BSD and closes some race conditions. + ln now uses different (and we hope clearer) diagnostics when it fails. + ln -v now acts more like FreeBSD, so it generates output only when + successful and the output is easier to parse. + ls now defaults to --time-style='locale', not --time-style='posix-long-iso'. + However, the 'locale' time style now behaves like 'posix-long-iso' + if your locale settings appear to be messed up. This change + attempts to have the default be the best of both worlds. + mkfifo and mknod no longer set special mode bits (setuid, setgid, + and sticky) with the -m option. + nohup's usual diagnostic now more precisely specifies the I/O + redirections, e.g., "ignoring input and appending output to + nohup.out". Also, nohup now redirects stderr to nohup.out (or + $HOME/nohup.out) if stdout is closed and stderr is a tty; this is in + response to Open Group XCU ERN 71. + rm --interactive now takes an optional argument, although the + default of using no argument still acts like -i. + rm no longer fails to remove an empty, unreadable directory + seq changes: + seq defaults to a minimal fixed point format that does not lose + information if seq's operands are all fixed point decimal numbers. + You no longer need the `-f%%.f' in `seq -f%%.f 1048575 1024 1050623', + for example, since the default format now has the same effect. + seq now lets you use %%a, %%A, %%E, %%F, and %%G formats. + seq now uses long double internally rather than double. + sort now reports incompatible options (e.g., -i and -n) rather than + silently ignoring one of them. + stat's --format=FMT option now works the way it did before 5.3.0: + FMT is automatically newline terminated. The first stable release + containing this change was 5.92. + stat accepts the new option --printf=FMT, where FMT is *not* + automatically newline terminated. + stat: backslash escapes are interpreted in a format string specified + via --printf=FMT, but not one specified via --format=FMT. That includes + octal (\ooo, at most three octal digits), hexadecimal (\xhh, one or + two hex digits), and the standard sequences (\a, \b, \f, \n, \r, \t, + \v, \", \\). + With no operand, 'tail -f' now silently ignores the '-f' only if + standard input is a FIFO or pipe and POSIXLY_CORRECT is set. + Formerly, it ignored the '-f' when standard input was a FIFO, pipe, + or socket. + ** Scheduled for removal + ptx's --copyright (-C) option is scheduled for removal in 2007, and + now evokes a warning. Use --version instead. + rm's --directory (-d) option is scheduled for removal in 2006. This + option has been silently ignored since coreutils 5.0. On systems + that support unlinking of directories, you can use the "unlink" + command to unlink a directory. + Similarly, we are considering the removal of ln's --directory (-d, + -F) option in 2006. Please write to if this + would cause a problem for you. On systems that support hard links + to directories, you can use the "link" command to create one. + ** New programs + base64: base64 encoding and decoding (RFC 3548) functionality. + sha224sum: print or check a SHA224 (224-bit) checksum + sha256sum: print or check a SHA256 (256-bit) checksum + sha384sum: print or check a SHA384 (384-bit) checksum + sha512sum: print or check a SHA512 (512-bit) checksum + shuf: Shuffle lines of text. + ** New features + chgrp now supports --preserve-root, --no-preserve-root (default), + as it was documented to do, and just as chmod, chown, and rm do. + New dd iflag= and oflag= flags: + 'directory' causes dd to fail unless the file is a directory, on + hosts that support this (e.g., Linux kernels, version 2.1.126 and + later). This has limited utility but is present for completeness. + 'noatime' causes dd to read a file without updating its access + time, on hosts that support this (e.g., Linux kernels, version + 2.6.8 and later). + 'nolinks' causes dd to fail if the file has multiple hard links, + on hosts that support this (e.g., Solaris 10 and later). + ls accepts the new option --group-directories-first, to make it + list directories before files. + rm now accepts the -I (--interactive=once) option. This new option + prompts once if rm is invoked recursively or if more than three + files are being deleted, which is less intrusive than -i prompting + for every file, but provides almost the same level of protection + against mistakes. + shred and sort now accept the --random-source option. + sort now accepts the --random-sort (-R) option and `R' ordering option. + sort now supports obsolete usages like "sort +1 -2" unless + POSIXLY_CORRECT is set. However, when conforming to POSIX + 1003.1-2001 "sort +1" still sorts the file named "+1". + wc accepts a new option --files0-from=FILE, where FILE contains a + list of NUL-terminated file names. + ** Bug fixes + cat with any of the options, -A -v -e -E -T, when applied to a + file in /proc or /sys (linux-specific), would truncate its output, + usually printing nothing. + cp -p would fail in a /proc-less chroot, on some systems + When `cp -RL' encounters the same directory more than once in the + hierarchy beneath a single command-line argument, it no longer confuses + them with hard-linked directories. + fts-using tools (chmod, chown, chgrp, du) no longer fail due to + a double-free bug -- it could be triggered by making a directory + inaccessible while e.g., du is traversing the hierarchy under it. + fts-using tools (chmod, chown, chgrp, du) no longer misinterpret + a very long symlink chain as a dangling symlink. Before, such a + misinterpretation would cause these tools not to diagnose an ELOOP error. + ls --indicator-style=file-type would sometimes stat a symlink + unnecessarily. + ls --file-type worked like --indicator-style=slash (-p), + rather than like --indicator-style=file-type. + mv: moving a symlink into the place of an existing non-directory is + now done atomically; before, mv would first unlink the destination. + mv -T DIR EMPTY_DIR no longer fails unconditionally. Also, mv can + now remove an empty destination directory: mkdir -p a b/a; mv a b + rm (on systems with openat) can no longer exit before processing + all command-line arguments. + rm is no longer susceptible to a few low-probability memory leaks. + rm -r no longer fails to remove an inaccessible and empty directory + rm -r's cycle detection code can no longer be tricked into reporting + a false positive (introduced in fileutils-4.1.9). + shred --remove FILE no longer segfaults on Gentoo systems + sort would fail for large inputs (~50MB) on systems with a buggy + mkstemp function. sort and tac now use the replacement mkstemp + function, and hence are no longer subject to limitations (of 26 or 32, + on the maximum number of files from a given template) on HP-UX 10.20, + SunOS 4.1.4, Solaris 2.5.1 and OSF1/Tru64 V4.0F&V5.1. + tail -f once again works on a file with the append-only + attribute (affects at least Linux ext2, ext3, xfs file systems) +* Tue Aug 08 2006 - schwab@suse.de +- Move sux to %%{_bindir}. +* Mon Jun 26 2006 - schwab@suse.de +- Update to coreutils 5.97. + ** Bug fixes + rebuild with better autoconf test for when the lstat replacement + function is needed -- required for Solaris 9 + cat with any of the options, -A -v -e -E -T, when applied to a + file in /proc or /sys (linux-specific), would truncate its output, + usually printing nothing. + ** Improved robustness + dircolors: a new autoconf run-test for AIX's buggy strndup function + prevents malfunction on that system; may also affect cut, expand, + and unexpand. + ** New features + chgrp now supports --preserve-root, --no-preserve-root (default), + as it was documented to do, and just as chmod, chown, and rm do. +* Thu Jun 22 2006 - schwab@suse.de +- Fix conflict with . +* Mon May 22 2006 - schwab@suse.de +- Update to coreutils 5.96. +* Sat May 13 2006 - schwab@suse.de +- Update to coreutils 5.95. +* Fri Apr 07 2006 - cthiel@suse.de +- added Obsoletes: libselinux (hack for bug #156519) +* Mon Feb 13 2006 - schwab@suse.de +- Fix spurious failure with cp -LR. +- Move check for /proc. +* Mon Jan 30 2006 - schwab@suse.de +- Always print newline after format in stat [#145905]. +- Barf if /proc is not mounted. +* Wed Jan 25 2006 - mls@suse.de +- converted neededforbuild to BuildRequires +* Thu Jan 19 2006 - meissner@suse.de +- Do not strip /bin/su. +* Wed Jan 11 2006 - schwab@suse.de +- Fix infloop when ignoring characters [#141756]. +* Mon Dec 19 2005 - kukuk@suse.de +- Add fallback if futimesat does not work +* Mon Dec 05 2005 - ke@suse.de +- Fix typo in German translation file; reported by Olaf Hering + [#105863]. +* Mon Dec 05 2005 - schwab@suse.de +- Drop SELinux support. +* Tue Nov 15 2005 - uli@suse.de +- some tests fail on ARM (QEMU problem?); ignore for now +* Sun Nov 06 2005 - schwab@suse.de +- Update to coreutils 5.93. +* Wed Nov 02 2005 - schwab@suse.de +- Update to coreutils 5.92. +- Fix invalid use of va_list. +- Add some fixes from cvs. +* Thu Oct 20 2005 - schwab@suse.de +- Reenable DEFAULT_POSIX2_VERSION. +* Wed Oct 19 2005 - agruen@suse.de +- Add acl and xattr patches. +* Mon Oct 17 2005 - schwab@suse.de +- Update to coreutils 5.91. +* Sat Oct 01 2005 - schwab@suse.de +- Update to coreutils 5.90. +- Disable acl patches for now. +* Sun Sep 25 2005 - schwab@suse.de +- Fix warning. +* Wed Aug 24 2005 - werner@suse.de +- Let `su' handle /sbin and /usr/sbin in path +* Mon Aug 01 2005 - kukuk@suse.de +- And yet another uninitialized variable fix. +* Fri Jul 29 2005 - schwab@suse.de +- Fix another uninitialized variable. +* Wed Jul 06 2005 - schwab@suse.de +- Fix uninitialized variable. +* Mon Jul 04 2005 - schwab@suse.de +- Update i18n patch. +* Mon Jun 20 2005 - schwab@suse.de +- Fix last change. +* Wed Jun 15 2005 - kukuk@suse.de +- Compile/link su with -fpie/-pie +* Sat May 21 2005 - kukuk@suse.de +- Add support for /etc/default/su +* Mon May 02 2005 - kukuk@suse.de +- Don't overwrite PATH if su is called with "-" option. +* Wed Mar 02 2005 - schwab@suse.de +- Fix merge error [#67103]. +* Mon Feb 28 2005 - schwab@suse.de +- Call pam_getenvlist before pam_end. +* Mon Feb 28 2005 - schwab@suse.de +- Link su to sux [#66830]. +* Wed Feb 02 2005 - schwab@suse.de +- Handle xfs and jfs in stat [#50415]. +* Wed Feb 02 2005 - schwab@suse.de +- Handle subfs like autofs. +* Tue Jan 25 2005 - schwab@suse.de +- Fix path_concat. +* Thu Jan 20 2005 - schwab@suse.de +- Use pam_xauth [#42238]. +* Fri Jan 14 2005 - schwab@suse.de +- Fix merge error [#49853]. +* Tue Jan 11 2005 - schwab@suse.de +- Update to coreutils 5.3.0. +* Mon Nov 08 2004 - kukuk@suse.de +- Use common-* PAM config files for su PAM configuration +* Mon Oct 25 2004 - schwab@suse.de +- Fix last change. +- Fix selinux patch. +* Wed Oct 20 2004 - ro@suse.de +- remove no language support (nb is already there) +* Sat Oct 02 2004 - agruen@suse.de +- #46609: Fix chown and chgrp utilities for uid == (uid_t) -1 and + gid == (gid_t) -1 case. +- Add missing #include to have NULL defined in lib/acl.c +* Fri Sep 10 2004 - schwab@suse.de +- Fix uninitialized variable [#44929]. +- Fix selinux patch. +* Wed Aug 25 2004 - schwab@suse.de +- Fix hardlink accounting patch. +* Mon May 24 2004 - schwab@suse.de +- Update testsuite for change in chown. +* Mon May 24 2004 - schwab@suse.de +- Precompute length in caller of ismbblank to avoid quadratic behaviour + [#40741]. +* Mon May 17 2004 - schwab@suse.de +- Fix handling of symlinks in chown [#40691]. +* Sat Apr 17 2004 - schwab@suse.de +- Pacify autobuild. +* Fri Apr 02 2004 - schwab@suse.de +- Add support for IUTF8 in stty. +* Tue Mar 30 2004 - schwab@suse.de +- Fix merge error in selinux patch [#37431]. +* Mon Mar 29 2004 - schwab@suse.de +- Fix hardlink accounting in du. +* Mon Mar 22 2004 - schwab@suse.de +- Fix race in the testsuite. +* Mon Mar 15 2004 - kukuk@suse.de +- Update SELinux patch to new libselinux interface +* Mon Mar 15 2004 - schwab@suse.de +- Fix date parsing. +* Sat Mar 13 2004 - schwab@suse.de +- Update to coreutils 5.2.1. + * Includes mv fix. + * Fix sparse handling in cp. + * Fix descriptor leak in nohup. + * Fix POSIX issues in expr. + * Always allow user.group in chown. +* Fri Mar 12 2004 - schwab@suse.de +- Fix sysinfo patch [#35337]. +* Fri Mar 12 2004 - schwab@suse.de +- Fix preserving links in mv. +* Wed Mar 03 2004 - schwab@suse.de +- Fix help output from mkdir. +* Fri Feb 20 2004 - schwab@suse.de +- Update to coreutils 5.2.0. +* Mon Feb 09 2004 - schwab@suse.de +- Update to coreutils 5.1.3. +* Mon Feb 02 2004 - agruen@suse.de +- Update acl and xattr patches, and add some Changelog text. +* Mon Jan 26 2004 - schwab@suse.de +- Update to coreutils 5.1.2. +* Fri Jan 23 2004 - schwab@suse.de +- Don't link [ to test. +* Mon Jan 19 2004 - schwab@suse.de +- Update to coreutils 5.1.1. +- Default to POSIX.2-1992. +* Fri Jan 16 2004 - kukuk@suse.de +- Add pam-devel to neededforbuild +* Fri Jan 09 2004 - schwab@suse.de +- Fix spurious test failure. +* Thu Jan 08 2004 - schwab@suse.de +- Update to coreutils 5.1.0. +* Fri Dec 12 2003 - schwab@suse.de +- Fix use of AC_SEARCH_LIBS. +* Tue Dec 09 2003 - schwab@suse.de +- Cleanup SELinux patch. +* Tue Dec 09 2003 - kukuk@suse.de +- Add SELinux patch. +* Wed Nov 26 2003 - schwab@suse.de +- Fix sorting of months in multibyte case [#33299]. +* Wed Oct 22 2003 - schwab@suse.de +- Fix building without extended attributes. +* Wed Oct 15 2003 - schwab@suse.de +- Cleanup sysinfo patch. +* Fri Sep 19 2003 - kukuk@suse.de +- Add missing textutil to Provides +* Mon Aug 25 2003 - agruen@suse.de +- Fix uname command to report reasonable processor and platform + information (coreutils-sysinfo.diff: based on similar RedHat + patch). +* Mon Jul 21 2003 - schwab@suse.de +- Fix typo in i18n patch for join. +* Fri Jul 18 2003 - schwab@suse.de +- Avoid abort in sort on inconsistent locales [#26506]. +* Tue Jul 15 2003 - okir@suse.de +- make su export variables declared via pam_putenv +* Wed May 28 2003 - kukuk@suse.de +- PAM fixes for su: + - Move pam_open_session call before dropping privilegs, session + management needs max. possible credentials and needs to be done + before we change into the home directory of the user. + - Don't set PAM_TTY and PAM_RUSER to fake names. + - Use conversion function from libpam_misc. +* Fri May 16 2003 - schwab@suse.de +- Fix exit status from su. +* Thu Apr 24 2003 - ro@suse.de +- fix head calling syntax +* Mon Apr 07 2003 - schwab@suse.de +- Only delete info entries when removing last version. +* Fri Apr 04 2003 - schwab@suse.de +- Update to coreutils 5.0. +* Mon Mar 31 2003 - schwab@suse.de +- Update to coreutils 4.5.12. +* Thu Mar 20 2003 - schwab@suse.de +- Update to coreutils 4.5.11. +* Mon Mar 10 2003 - schwab@suse.de +- Fix LFS bug in du [#24960]. +* Thu Feb 27 2003 - schwab@suse.de +- Readd textutils i18n patches. +* Thu Feb 27 2003 - agruen@suse.de +- Per hint from Andreas Schwab, don't use awk in autoconf. (The + improved test is simpler, too.) +* Thu Feb 27 2003 - agruen@suse.de +- Fix autoconf test for attr_copy_file that caused all binaries + to be linked needlessly against libattr.so. +* Tue Feb 25 2003 - agruen@suse.de +- Extended attribute copying: Use the newly exported + attr_copy_check_permissions() callback exported by libattr.so, + so that the EA copying done by coreutils is consistent with + other apps [#24244]. +* Mon Feb 24 2003 - schwab@suse.de +- Update to coreutils 4.5.8. + * Fixes bugs in du. +* Mon Feb 17 2003 - agruen@suse.de +- Add extended attribute copying patch: Affects cp, mv, install. + See the cp manual page for details on the changes in cp. The + mv utility always tries to copy extended attributes; install + never does. +* Mon Feb 10 2003 - schwab@suse.de +- Update to coreutils 4.5.7. +* Fri Feb 07 2003 - kukuk@suse.de +- Use pam_unix2.so instead of pam_unix.so, use same rules for + password changing as passwd. +* Thu Feb 06 2003 - schwab@suse.de +- Use %%install_info. +* Thu Feb 06 2003 - schwab@suse.de +- Update to coreutils 4.5.6. +* Mon Feb 03 2003 - schwab@suse.de +- Package created, combining textutils, sh-utils and fileutils. diff --git a/getcwd.diff b/getcwd.diff new file mode 100644 index 0000000..950f14e --- /dev/null +++ b/getcwd.diff @@ -0,0 +1,11 @@ +--- lib/getcwd.c ++++ lib/getcwd.c +@@ -137,7 +137,7 @@ + size_t allocated = size; + size_t used; + +-#if HAVE_PARTLY_WORKING_GETCWD && !defined AT_FDCWD ++#if HAVE_PARTLY_WORKING_GETCWD + /* The system getcwd works, except it sometimes fails when it + shouldn't, setting errno to ERANGE, ENAMETOOLONG, or ENOENT. If + AT_FDCWD is not defined, the algorithm below is O(N**2) and this diff --git a/i18n-infloop.diff b/i18n-infloop.diff new file mode 100644 index 0000000..f51a60e --- /dev/null +++ b/i18n-infloop.diff @@ -0,0 +1,12 @@ +--- src/sort.c ++++ src/sort.c +@@ -1838,7 +1838,8 @@ + if (MBLENGTH == (size_t)-2 || MBLENGTH == (size_t)-1) \ + STATE = state_bak; \ + if (!ignore) \ +- COPY[NEW_LEN++] = TEXT[i++]; \ ++ COPY[NEW_LEN++] = TEXT[i]; \ ++ i++; \ + continue; \ + } \ + \ diff --git a/i18n-uninit.diff b/i18n-uninit.diff new file mode 100644 index 0000000..a093822 --- /dev/null +++ b/i18n-uninit.diff @@ -0,0 +1,25 @@ +--- src/cut.c ++++ src/cut.c +@@ -869,7 +869,10 @@ + c = getc (stream); + empty_input = (c == EOF); + if (c != EOF) +- ungetc (c, stream); ++ { ++ ungetc (c, stream); ++ wc = 0; ++ } + else + wc = WEOF; + +--- src/expand.c ++++ src/expand.c +@@ -414,7 +414,7 @@ + for (;;) + { + /* Input character, or EOF. */ +- wint_t wc; ++ wint_t wc = 0; + + /* If true, perform translations. */ + bool convert = true; diff --git a/invalid-ids.diff b/invalid-ids.diff new file mode 100644 index 0000000..66063cf --- /dev/null +++ b/invalid-ids.diff @@ -0,0 +1,47 @@ +While uid_t and gid_t are both unsigned, the values (uid_t) -1 and +(gid_t) -1 are reserved. A uid or gid argument of -1 to the chown(2) +system call means to leave the uid/gid unchanged. Catch this case +so that trying to set a uid or gid to -1 will result in an error. + +Test cases: + + chown 4294967295 file + chown :4294967295 file + chgrp 4294967295 file + +Andreas Gruenbacher + +Index: coreutils-5.2.1/lib/userspec.c +================================================================================ +--- coreutils-5.3.0/lib/userspec.c ++++ coreutils-5.3.0/lib/userspec.c +@@ -184,7 +184,7 @@ + { + unsigned long int tmp; + if (xstrtoul (u, NULL, 10, &tmp, "") == LONGINT_OK +- && tmp <= MAXUID) ++ && tmp <= MAXUID && tmp != (uid_t) -1) + unum = tmp; + else + error_msg = E_invalid_user; +@@ -214,7 +214,8 @@ + if (grp == NULL) + { + unsigned long int tmp; +- if (xstrtoul (g, NULL, 10, &tmp, "") == LONGINT_OK && tmp <= MAXGID) ++ if (xstrtoul (g, NULL, 10, &tmp, "") == LONGINT_OK && tmp <= MAXGID ++ && tmp != (gid_t) -1) + gnum = tmp; + else + error_msg = E_invalid_group; +--- coreutils-5.3.0/src/chgrp.c ++++ coreutils-5.3.0/src/chgrp.c +@@ -91,7 +91,7 @@ + { + unsigned long int tmp; + if (! (xstrtoul (name, NULL, 10, &tmp, "") == LONGINT_OK +- && tmp <= GID_T_MAX)) ++ && tmp <= GID_T_MAX && tmp != (gid_t) -1)) + error (EXIT_FAILURE, 0, _("invalid group %s"), quote (name)); + gid = tmp; + } diff --git a/no-no.diff b/no-no.diff new file mode 100644 index 0000000..67aef34 --- /dev/null +++ b/no-no.diff @@ -0,0 +1,10 @@ +--- po/LINGUAS ++++ po/LINGUAS +@@ -19,7 +19,6 @@ + ms + nb + nl +-no + pl + pt + pt_BR diff --git a/ready b/ready new file mode 100644 index 0000000..473a0f4 diff --git a/su.default b/su.default new file mode 100644 index 0000000..d9758a8 --- /dev/null +++ b/su.default @@ -0,0 +1,11 @@ +# Per default, only "su -" will set a new PATH. +# If this variable is changed to "yes" (default is "no"), +# every su call will overwrite the PATH variable. +ALWAYS_SET_PATH=no + +# Default path. +PATH=/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin + +# Default path for a user invoking su to root. +SUPATH=/usr/sbin:/bin:/usr/bin:/sbin:/usr/X11R6/bin + diff --git a/su.pamd b/su.pamd new file mode 100644 index 0000000..b729046 --- /dev/null +++ b/su.pamd @@ -0,0 +1,7 @@ +#%PAM-1.0 +auth sufficient pam_rootok.so +auth include common-auth +account include common-account +password include common-password +session include common-session +session optional pam_xauth.so