diff --git a/.cvsignore b/.cvsignore index 2161c47..464a8cb 100644 --- a/.cvsignore +++ b/.cvsignore @@ -1 +1 @@ -coreutils-5.0.tar.bz2 +coreutils-5.2.1.tar.bz2 diff --git a/coreutils-i18n.patch b/coreutils-i18n.patch new file mode 100644 index 0000000..724e5e8 --- /dev/null +++ b/coreutils-i18n.patch @@ -0,0 +1,4097 @@ +--- coreutils-5.1.3/lib/linebuffer.h.i18n 2003-06-18 07:58:01.000000000 +0100 ++++ coreutils-5.1.3/lib/linebuffer.h 2004-02-16 15:36:40.000000000 +0000 +@@ -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-5.1.3/src/cut.c.i18n 2004-01-21 22:27:02.000000000 +0000 ++++ coreutils-5.1.3/src/cut.c 2004-02-16 15:45:41.000000000 +0000 +@@ -29,6 +29,11 @@ + #include + #include + #include ++ ++/* Get mbstate_t, mbrtowc(). */ ++#if HAVE_WCHAR_H ++# include ++#endif + #include "system.h" + + #include "error.h" +@@ -37,6 +42,18 @@ + #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 ++ ++/* Some systems, like BeOS, have multibyte encodings but lack mbstate_t. */ ++#if HAVE_MBRTOWC && defined mbstate_t ++# define mbrtowc(pwc, s, n, ps) (mbrtowc) (pwc, s, n, 0) ++#endif ++ + /* The official name of this program (e.g., no `g' prefix). */ + #define PROGRAM_NAME "cut" + +@@ -67,6 +84,52 @@ + } \ + while (0) + ++/* Refill the buffer BUF to get a multibyte character. */ ++#define REFILL_BUFFER(BUF, BUFPOS, BUFLEN, STREAM) \ ++ do \ ++ { \ ++ if (BUFLEN < MB_LEN_MAX && !feof (STREAM) && !ferror (STREAM)) \ ++ { \ ++ memmove (BUF, BUFPOS, BUFLEN); \ ++ BUFLEN += fread (BUF + BUFLEN, sizeof(char), BUFSIZ, STREAM); \ ++ BUFPOS = BUF; \ ++ } \ ++ } \ ++ while (0) ++ ++/* Get wide character on BUFPOS. BUFPOS is not included after that. ++ If byte sequence is not valid as a character, CONVFAIL is 1. Otherwise 0. */ ++#define GET_NEXT_WC_FROM_BUFFER(WC, BUFPOS, BUFLEN, MBLENGTH, STATE, CONVFAIL) \ ++ do \ ++ { \ ++ mbstate_t state_bak; \ ++ \ ++ if (BUFLEN < 1) \ ++ { \ ++ WC = WEOF; \ ++ break; \ ++ } \ ++ \ ++ /* Get a wide character. */ \ ++ CONVFAIL = 0; \ ++ state_bak = STATE; \ ++ MBLENGTH = mbrtowc ((wchar_t *)&WC, BUFPOS, BUFLEN, &STATE); \ ++ \ ++ switch (MBLENGTH) \ ++ { \ ++ case (size_t)-1: \ ++ case (size_t)-2: \ ++ CONVFAIL++; \ ++ STATE = state_bak; \ ++ /* Fall througn. */ \ ++ \ ++ case 0: \ ++ MBLENGTH = 1; \ ++ break; \ ++ } \ ++ } \ ++ while (0) ++ + struct range_pair + { + size_t lo; +@@ -85,7 +148,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 byte, character or field 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 +160,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 +173,12 @@ + { + undefined_mode, + +- /* Output characters that are in the given bytes. */ ++ /* Output bytes that are at the given positions. */ + byte_mode, + ++ /* Output characters that are at the given positions. */ ++ character_mode, ++ + /* Output the given delimeter-separated fields. */ + field_mode + }; +@@ -121,6 +188,13 @@ + + static enum operating_mode operating_mode; + ++/* If nonzero, when in byte mode, don't split multibyte characters. */ ++static int byte_mode_character_aware; ++ ++/* If nonzero, the function for single byte locale is work ++ if this program runs on multibyte locale. */ ++static int force_singlebyte_mode; ++ + /* If true do not output lines containing no delimeter characters. + Otherwise, all such lines are printed. This option is valid only + with field mode. */ +@@ -128,6 +202,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; +@@ -199,7 +276,7 @@ + -f, --fields=LIST output 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 (_("\ + -s, --only-delimited do not print lines not containing delimiters\n\ +@@ -327,7 +404,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++; + +@@ -352,14 +429,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) +@@ -434,6 +513,9 @@ + if (operating_mode == byte_mode) + error (0, 0, + _("byte offset %s is too large"), quote (bad_num)); ++ else if (operating_mode == character_mode) ++ error (0, 0, ++ _("character offset %s is too large"), quote (bad_num)); + else + error (0, 0, + _("field number %s is too large"), quote (bad_num)); +@@ -445,7 +527,7 @@ + fieldstr++; + } + else +- FATAL_ERROR (_("invalid byte or field list")); ++ FATAL_ERROR (_("invalid byte, character or field list")); + } + + max_range_endpoint = 0; +@@ -515,7 +597,7 @@ + print_delimiter = false; + while (1) + { +- register int c; /* Each character from the file. */ ++ int c; /* Each character from the file. */ + + c = getc (stream); + +@@ -549,6 +631,63 @@ + } + } + ++#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) ++{ ++ int idx; /* number of bytes or characters in the line so far. */ ++ char buf[MB_LEN_MAX + BUFSIZ]; /* For spooling a read byte sequence. */ ++ char *bufpos; /* Next read position of BUF. */ ++ size_t buflen; /* The length of the byte sequence in buf. */ ++ wint_t wc; /* A gotten wide character. */ ++ size_t mblength; /* The byte size of a multibyte character which shows ++ as same character as WC. */ ++ mbstate_t state; /* State of the stream. */ ++ int convfail; /* 1, when conversion is failed. Otherwise 0. */ ++ ++ idx = 0; ++ 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; ++ } ++ else ++ { ++ idx += (operating_mode == byte_mode) ? mblength : 1; ++ if (print_kth (idx, NULL)) ++ fwrite (bufpos, mblength, sizeof(char), stdout); ++ } ++ ++ buflen -= mblength; ++ bufpos += mblength; ++ } ++} ++#endif ++ + /* Read from stream STREAM, printing to standard output any selected fields. */ + + static void +@@ -669,13 +808,192 @@ + } + } + ++#if HAVE_MBRTOWC ++static void ++cut_fields_mb (FILE *stream) ++{ ++ int c; ++ unsigned int field_idx; ++ int found_any_selected_field; ++ int buffer_first_field; ++ int empty_input; ++ char buf[MB_LEN_MAX + BUFSIZ]; /* For spooling a read byte sequence. */ ++ char *bufpos; /* Next read position of BUF. */ ++ size_t buflen; /* The length of the byte sequence in buf. */ ++ wint_t wc; /* 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. */ ++ ++ found_any_selected_field = 0; ++ field_idx = 1; ++ bufpos = buf; ++ buflen = 0; ++ memset (&state, '\0', sizeof(mbstate_t)); ++ ++ c = getc (stream); ++ empty_input = (c == EOF); ++ if (c != EOF) ++ ungetc (c, stream); ++ 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) ++ { ++ int len = 0; ++ ++ while (1) ++ { ++ REFILL_BUFFER (buf, bufpos, buflen, stream); ++ ++ GET_NEXT_WC_FROM_BUFFER ++ (wc, bufpos, buflen, mblength, state, convfail); ++ ++ if (wc == WEOF) ++ break; ++ ++ field_1_buffer = xrealloc (field_1_buffer, len + mblength); ++ memcpy (field_1_buffer + len, bufpos, mblength); ++ len += mblength; ++ buflen -= mblength; ++ bufpos += mblength; ++ ++ if (!convfail && (wc == 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), len, 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), len - 1, stdout); ++ found_any_selected_field = 1; ++ } ++ ++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 = 1; ++ } ++ ++ while (1) ++ { ++ REFILL_BUFFER (buf, bufpos, buflen, stream); ++ ++ GET_NEXT_WC_FROM_BUFFER ++ (wc, bufpos, buflen, mblength, state, convfail); ++ ++ if (wc == WEOF) ++ break; ++ else if (!convfail && (wc == wcdelim || wc == 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 = 0; ++ } ++ } ++} ++#endif ++ + static void + cut_stream (FILE *stream) + { +- if (operating_mode == byte_mode) +- cut_bytes (stream); ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1 && !force_singlebyte_mode) ++ { ++ switch (operating_mode) ++ { ++ case byte_mode: ++ if (byte_mode_character_aware) ++ cut_characters_or_cut_bytes_no_split (stream); ++ else ++ cut_bytes (stream); ++ break; ++ ++ case character_mode: ++ cut_characters_or_cut_bytes_no_split (stream); ++ break; ++ ++ case field_mode: ++ 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. +@@ -724,6 +1042,8 @@ + int optc, exit_status = 0; + 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]; +@@ -749,7 +1069,6 @@ + break; + + case 'b': +- case 'c': + /* Build the byte list. */ + if (operating_mode != undefined_mode) + FATAL_ERROR (_("only one type of list may be specified")); +@@ -757,6 +1076,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) +@@ -768,10 +1095,35 @@ + case 'd': + /* New delimiter. */ + /* Interpret -d '' to mean `use the NUL byte as the delimiter.' */ +- if (optarg[0] != '\0' && optarg[1] != '\0') +- FATAL_ERROR (_("the delimiter must be a single character")); +- delim = optarg[0]; +- delim_specified = true; ++#if HAVE_MBRTOWC ++ { ++ if(MB_CUR_MAX > 1) ++ { ++ mbstate_t state; ++ ++ memset (&state, '\0', sizeof(mbstate_t)); ++ delimlen = mbrtowc (&wcdelim, optarg, strnlen(optarg, MB_LEN_MAX), &state); ++ ++ if (delimlen == (size_t)-1 || delimlen == (size_t)-2) ++ ++force_singlebyte_mode; ++ else ++ { ++ delimlen = (delimlen < 1) ? 1 : delimlen; ++ if (wcdelim != L'\0' && *(optarg + delimlen) != '\0') ++ FATAL_ERROR (_("the delimiter must be a single character")); ++ memcpy (mbdelim, optarg, delimlen); ++ } ++ } ++ ++ if (MB_CUR_MAX <= 1 || force_singlebyte_mode) ++#endif ++ { ++ if (optarg[0] != '\0' && optarg[1] != '\0') ++ FATAL_ERROR (_("the delimiter must be a single character")); ++ delim = (unsigned char) optarg[0]; ++ } ++ delim_specified = true; ++ } + break; + + case OUTPUT_DELIMITER_OPTION: +@@ -784,6 +1136,7 @@ + break; + + case 'n': ++ byte_mode_character_aware = 1; + break; + + case 's': +@@ -802,7 +1155,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")); + +@@ -829,15 +1182,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-5.1.3/src/pr.c.i18n 2004-01-21 22:27:02.000000000 +0000 ++++ coreutils-5.1.3/src/pr.c 2004-02-16 15:36:40.000000000 +0000 +@@ -314,12 +314,50 @@ + #include + #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 "mbswidth.h" + #include "posixver.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" + +@@ -412,7 +450,20 @@ + + #define NULLCOL (COLUMN *)0 + +-static int char_to_clump (int c); ++/* Funtion pointers to switch functions for single byte locale or for ++ multibyte locale. If multibyte functions do not exist in your sysytem, ++ these pointers always point the function for single byte locale. */ ++static void (*print_char) (int c); ++static int (*char_to_clump) (int c); ++ ++/* Functions for single byte locale. */ ++static void print_char_single (int c); ++static int char_to_clump_single (int c); ++ ++/* Functions for multibyte locale. */ ++static void print_char_multi (int c); ++static int char_to_clump_multi (int c); ++ + static int read_line (COLUMN *p); + static int print_page (void); + static int print_stored (COLUMN *p); +@@ -422,6 +473,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); +@@ -436,7 +488,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 (int c); + static void cleanup (void); + static void first_last_page (char *pages); + static void print_sep_string (void); +@@ -452,7 +503,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. */ +@@ -556,7 +607,7 @@ + static int 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. */ +@@ -566,7 +617,10 @@ + static int 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; +@@ -640,7 +694,13 @@ + static int 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). */ +@@ -693,6 +753,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"; + +@@ -842,6 +903,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 +@@ -866,6 +934,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 *)) +@@ -943,8 +1026,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; +@@ -957,8 +1044,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; +@@ -985,8 +1076,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; +@@ -1025,7 +1116,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); +@@ -1173,10 +1264,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, strnlen(arg, MB_LEN_MAX), &state); ++ ++ if (mblength == (size_t)-1 || mblength == (size_t)-2) ++ { ++ *character_length = 1; ++ *character_width = 1; ++ } ++ else ++ { ++ *character_length = (mblength < 1) ? 1 : mblength; ++ width = wcwidth (wc); ++ *character_width = (width < 0) ? 0 : width; ++ } ++ ++ strncpy (character, arg, *character_length); ++ arg += *character_length; ++ } ++ else /* for single byte locale. */ ++#endif ++ { ++ *character = *arg++; ++ *character_length = 1; ++ *character_width = 1; ++ } ++ } ++ + if (*arg) + { + long int tmp_long; +@@ -1241,7 +1367,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 +@@ -1273,11 +1399,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. */ +@@ -1292,7 +1418,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")); +@@ -1421,7 +1547,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. */ + +@@ -1455,7 +1581,7 @@ + } + else + { +- h = h_next + col_sep_length; ++ h = h_next + col_sep_width; + h_next = h + chars_per_column; + } + } +@@ -1739,9 +1865,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; + } + +@@ -2015,13 +2141,13 @@ + /* May be too generous. */ + buff = x2nrealloc (buff, &buff_allocated, sizeof *buff); + } +- buff[buff_current++] = (char) c; ++ buff[buff_current++] = (unsigned char) c; + } + + static void + add_line_number (COLUMN *p) + { +- int i; ++ int i, j; + char *s; + int left_cut; + +@@ -2044,22 +2170,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) ((int) ' '); + } + else +- (p->char_func) ((int) number_separator); ++ for (j = 0; j < number_separator_length; j++) ++ (p->char_func) ((int) 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) ((int) number_separator); +- if (number_separator == '\t') ++ for (j = 0; j < number_separator_length; j++) ++ (p->char_func) ((int) number_separator[j]); ++ if (number_separator[0] == '\t') + output_position = POS_AFTER_TAB (chars_per_output_tab, + output_position); + } +@@ -2219,7 +2347,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, sizeof(char), output_tab_char_length, stdout); + h_old = h_new; + } + while (++h_old <= goal) +@@ -2239,6 +2367,7 @@ + { + char *s; + int l = col_sep_length; ++ int not_space_flag; + + s = col_sep_string; + +@@ -2252,6 +2381,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, +@@ -2265,12 +2395,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 (); +@@ -2297,8 +2430,9 @@ + a nonspace is encountered, call print_white_space() to print the + required number of tabs and spaces. */ + ++ + static void +-print_char (int c) ++print_char_single (int c) + { + if (tabify_output) + { +@@ -2322,6 +2456,74 @@ + putchar (c); + } + ++#ifdef HAVE_MBRTOWC ++static void ++print_char_multi (int 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; ++ size_t mblength; ++ int width; ++ ++ if (tabify_output) ++ { ++ state_bak = state; ++ mbc[mbc_pos++] = (unsigned char)c; ++ mblength = mbrtowc (&wc, mbc, mbc_pos, &state); ++ ++ while (mbc_pos > 0) ++ { ++ switch (mblength) ++ { ++ case (size_t)-2: ++ state = state_bak; ++ return; ++ ++ case (size_t)-1: ++ state = state_bak; ++ ++output_position; ++ putchar (mbc[0]); ++ memmove (mbc, mbc + 1, MB_CUR_MAX - 1); ++ --mbc_pos; ++ break; ++ ++ case 0: ++ mblength = 1; ++ ++ default: ++ if (wc == L' ') ++ { ++ memmove (mbc, mbc + mblength, MB_CUR_MAX - mblength); ++ --mbc_pos; ++ ++spaces_not_printed; ++ return; ++ } ++ else if (spaces_not_printed > 0) ++ print_white_space (); ++ ++ /* Nonprintables are assumed to have width 0, except L'\b'. */ ++ if ((width = wcwidth (wc)) < 1) ++ { ++ if (wc == L'\b') ++ --output_position; ++ } ++ else ++ output_position += width; ++ ++ fwrite (mbc, sizeof(char), mblength, stdout); ++ memmove (mbc, mbc + mblength, MB_CUR_MAX - mblength); ++ mbc_pos -= mblength; ++ } ++ } ++ return; ++ } ++ putchar (c); ++} ++#endif ++ + /* Skip to page PAGE before printing. + PAGE may be larger than total number of pages. */ + +@@ -2496,9 +2698,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; + } + +@@ -2599,9 +2801,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; + } + +@@ -2614,8 +2816,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; +@@ -2633,8 +2835,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 (int c) ++char_to_clump_single (int c) + { + register int *s = clump_buff; + register int i; +@@ -2643,10 +2846,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); + +@@ -2717,6 +2920,154 @@ + return chars; + } + ++#ifdef HAVE_MBRTOWC ++static int ++char_to_clump_multi (int 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; ++ size_t mblength; ++ int wc_width; ++ register int *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++] = (unsigned char)c; ++ mblength = mbrtowc (&wc, mbc, mbc_pos, &state); ++ ++ width = 0; ++ chars = 0; ++ while (mbc_pos > 0) ++ { ++ switch (mblength) ++ { ++ case (size_t)-2: ++ state = state_bak; ++ return 0; ++ ++ case (size_t)-1: ++ state = state_bak; ++ mblength = 1; ++ ++ if (use_esc_sequence || use_cntrl_prefix) ++ { ++ width = +4; ++ chars = +4; ++ *s++ = '\\'; ++ sprintf (esc_buff, "%03o", 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", c); ++ 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", c); ++ 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-5.1.3/src/sort.c.i18n 2004-01-21 22:27:02.000000000 +0000 ++++ coreutils-5.1.3/src/sort.c 2004-02-16 15:36:40.000000000 +0000 +@@ -23,10 +23,31 @@ + + #include + ++#include + #include + #include + #include + #include ++ ++/* Solaris 2.5 has a bug: must be included before . */ ++/* Get mbstate_t, mbrtowc(), wcwidth(). */ ++#if HAVE_WCHAR_H ++# include ++#endif ++ ++/* Get isw* functions. */ ++#if HAVE_WCTYPE_H ++# include ++#endif ++ ++/* Get nl_langinfo(). */ ++#if HAVE_LANGINFO_CODESET ++# include ++#endif ++ ++/* Include this after wctype.h so that we `#undef' ISPRINT ++ (from Solaris's euc.h, from widec.h, from wctype.h) before ++ redefining and using it. */ + #include "system.h" + #include "error.h" + #include "hard-locale.h" +@@ -46,6 +67,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" + +@@ -91,6 +123,7 @@ + + static char decimal_point; + static int th_sep; /* if CHAR_MAX + 1, then there is no thousands separator */ ++static int force_general_numcompare = 0; + + /* Nonzero if the corresponding locales are hard. */ + static bool hard_LC_COLLATE; +@@ -109,6 +142,28 @@ + + #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 }; + +@@ -251,7 +306,8 @@ + /* Tab character separating fields. If TAB_DEFAULT, then fields are + separated by the empty string between a non-blank character and a blank + character. */ +-static int tab = TAB_DEFAULT; ++static int tab[MB_LEN_MAX + 1] = { TAB_DEFAULT }; ++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. */ +@@ -384,6 +440,46 @@ + }; + static struct tempnode *volatile temphead; + ++/* Fucntion pointers. */ ++static void ++(*inittables) (void); ++ ++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 len, size_t *length) ++{ ++ size_t mblength; ++ wchar_t wc; ++ mbstate_t state; ++ ++ memset (&state, '\0', sizeof(mbstate_t)); ++ mblength = mbrtowc (&wc, str, len, &state); ++ ++ if (mblength == (size_t)-1 || mblength == (size_t)-2) ++ { ++ *length = 1; ++ return 0; ++ } ++ ++ *length = (mblength < 1) ? 1 : mblength; ++ return iswblank (wc); ++} ++#endif ++ + /* Clean up any remaining temporary files. */ + + static void +@@ -521,7 +617,7 @@ + } + } + +-#if HAVE_NL_LANGINFO ++#if HAVE_LANGINFO_CODESET + + static int + struct_month_cmp (const void *m1, const void *m2) +@@ -536,7 +632,7 @@ + /* Initialize the character class tables. */ + + static void +-inittables (void) ++inittables_uni (void) + { + int i; + +@@ -574,6 +670,64 @@ + #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, s_len - j, &mblength)) ++ break; ++ j += mblength; ++ } ++ ++ for (k = 0; j < s_len;) ++ { ++ mblength = mbrtowc (&wc, (s + j), (s_len - j), &state_mb); ++ assert (mblength != (size_t)-1 && mblength != (size_t)-2); ++ if (mblength == 0) ++ break; ++ ++ pwc = towupper (wc); ++ if (pwc == wc) ++ { ++ memcpy (mbc, s + j, mblength); ++ j += mblength; ++ } ++ else ++ { ++ j += mblength; ++ mblength = wcrtomb (mbc, pwc, &state_wc); ++ assert (mblength != (size_t)0 && mblength != (size_t)-1); ++ } ++ ++ for (l = 0; l < mblength; l++) ++ name[k++] = mbc[l]; ++ } ++ name[k] = '\0'; ++ } ++ qsort ((void *) monthtab, MONTHS_PER_YEAR, ++ sizeof (struct month), struct_month_cmp); ++} ++#endif ++ + /* Specify the amount of main memory to use when sorting. */ + static void + specify_sort_size (char const *s) +@@ -784,7 +938,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) + { + register char *ptr = line->text, *lim = ptr + line->length - 1; + register size_t sword = key->sword; +@@ -794,10 +948,10 @@ + /* The leading field separator itself is included in a field when -t + is absent. */ + +- if (tab != TAB_DEFAULT) ++ if (tab[0] != TAB_DEFAULT) + while (ptr < lim && sword--) + { +- while (ptr < lim && *ptr != tab) ++ while (ptr < lim && *ptr != tab[0]) + ++ptr; + if (ptr < lim) + ++ptr; +@@ -825,11 +979,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[0] != TAB_DEFAULT) ++ while (ptr < lim && sword--) ++ { ++ while (ptr < lim && memcmp (ptr, tab, tab_length) != 0) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ptr += mblength; ++ } ++ if (ptr < lim) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ptr += mblength; ++ } ++ } ++ else ++ while (ptr < lim && sword--) ++ { ++ while (ptr < lim && ismbblank (ptr, lim - ptr, &mblength)) ++ ptr += mblength; ++ if (ptr < lim) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ptr += mblength; ++ } ++ while (ptr < lim && !ismbblank (ptr, lim - ptr, &mblength)) ++ ptr += mblength; ++ } ++ ++ if (key->skipsblanks) ++ while (ptr < lim && ismbblank (ptr, lim - ptr, &mblength)) ++ ptr += mblength; ++ ++ for (i = 0; i < schar; i++) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ++ if (ptr + mblength > lim) ++ break; ++ else ++ ptr += mblength; ++ } ++ ++ return ptr; ++} ++#endif ++ + /* Return the limit of (a pointer to the first character after) the field + in LINE specified by KEY. */ + + static char * +-limfield (const struct line *line, const struct keyfield *key) ++limfield_uni (const struct line *line, const struct keyfield *key) + { + register char *ptr = line->text, *lim = ptr + line->length - 1; + register size_t eword = key->eword, echar = key->echar; +@@ -842,10 +1055,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[0] != TAB_DEFAULT) + while (ptr < lim && eword--) + { +- while (ptr < lim && *ptr != tab) ++ while (ptr < lim && *ptr != tab[0]) + ++ptr; + if (ptr < lim && (eword | echar)) + ++ptr; +@@ -891,10 +1104,10 @@ + */ + + /* Make LIM point to the end of (one byte past) the current field. */ +- if (tab != TAB_DEFAULT) ++ if (tab[0] != TAB_DEFAULT) + { + char *newlim; +- newlim = memchr (ptr, tab, lim - ptr); ++ newlim = memchr (ptr, tab[0], lim - ptr); + if (newlim) + lim = newlim; + } +@@ -926,15 +1139,137 @@ + 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[0]) ++ while (ptr < lim && eword--) ++ { ++ while (ptr < lim && memcmp (ptr, tab, tab_length) != 0) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ptr += mblength; ++ } ++ if (ptr < lim && (eword | echar)) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ptr += mblength; ++ } ++ } ++ else ++ while (ptr < lim && eword--) ++ { ++ while (ptr < lim && ismbblank (ptr, lim - ptr, &mblength)) ++ ptr += mblength; ++ if (ptr < lim) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ptr += mblength; ++ } ++ while (ptr < lim && !ismbblank (ptr, lim - ptr, &mblength)) ++ ptr += mblength; ++ } ++ ++ ++# ifdef POSIX_UNSPECIFIED ++ /* Make LIM point to the end of (one byte past) the current field. */ ++ if (tab[0]) ++ { ++ char *newlim, *p; ++ ++ newlim = NULL; ++ for (p = ptr; p < lim;) ++ { ++ if (memcmp (p, tab, tab_length) == 0) ++ { ++ newlim = p; ++ break; ++ } ++ ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ p += mblength; ++ } ++ } ++ else ++ { ++ char *newlim; ++ newlim = ptr; ++ ++ while (newlim < lim && ismbblank (newlim, lim - newlim, &mblength)) ++ newlim += mblength; ++ if (ptr < lim) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ptr += mblength; ++ } ++ while (newlim < lim && !ismbblank (newlim, lim - newlim, &mblength)) ++ newlim += mblength; ++ lim = newlim; ++ } ++# endif ++ ++ /* If we're skipping leading blanks, don't start counting characters ++ * until after skipping past any leading blanks. */ ++ if (key->skipsblanks) ++ while (ptr < lim && ismbblank (ptr, lim - ptr, &mblength)) ++ ptr += mblength; ++ ++ memset (&state, '\0', sizeof(mbstate_t)); ++ ++ /* Advance PTR by ECHAR (if possible), but no further than LIM. */ ++ for (i = 0; i < echar; i++) ++ { ++ GET_BYTELEN_OF_CHAR (lim, ptr, mblength, state); ++ ++ if (ptr + mblength > lim) ++ break; ++ else ++ ptr += mblength; ++ } ++ ++ return ptr; ++} ++#endif ++ + /* Return the number of trailing blanks in FIELD, with LEN bytes. */ + + static size_t + trailing_blanks (char const *field, size_t len) + { +- size_t i; +- for (i = len; 0 < i && blanks[UCHAR (field[i - 1])]; i--) +- continue; +- return len - i; ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ { ++ size_t blanks = 0; ++ ++ while (len) { ++ size_t mblength; ++ if (ismbblank (field, len, &mblength)) ++ blanks++; ++ else ++ blanks = 0; ++ ++ field += mblength, len -= mblength; ++ } ++ ++ return blanks; ++ } ++ else ++#endif ++ { ++ size_t i; ++ for (i = len; 0 < i && blanks[UCHAR (field[i - 1])]; i--) ++ continue; ++ return len - i; ++ } + } + + /* Fill BUF reading from FP, moving buf->left bytes from the end +@@ -1019,8 +1354,22 @@ + else + { + if (key->skipsblanks) +- while (blanks[UCHAR (*line_start)]) +- line_start++; ++#if HAVE_MBRTOWC ++ { ++ if (MB_CUR_MAX > 1) ++ { ++ size_t mblength; ++ ++ while (ismbblank (line_start, ptr - line_start, &mblength)) ++ line_start += mblength; ++ } ++ else ++#endif ++ { ++ while (blanks[UCHAR (*line_start)]) ++ line_start++; ++ } ++ } + line->keybeg = line_start; + } + if (key->skipeblanks) +@@ -1128,13 +1477,32 @@ + register int tmpa, tmpb, tmp; + register size_t log_a, log_b; + +- tmpa = *a; +- tmpb = *b; ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ { ++ size_t mblength; ++ size_t alen = strnlen (a, MB_LEN_MAX); ++ size_t blen = strnlen (b, MB_LEN_MAX); ++ ++ while (ismbblank (a, alen, &mblength)) ++ a += mblength, alen -= mblength; ++ while (ismbblank (b, blen, &mblength)) ++ b += mblength, blen -= mblength; + +- while (blanks[UCHAR (tmpa)]) +- tmpa = *++a; +- while (blanks[UCHAR (tmpb)]) +- tmpb = *++b; ++ tmpa = *a; ++ tmpb = *b; ++ } ++ else ++#endif ++ { ++ tmpa = *a; ++ tmpb = *b; ++ ++ while (blanks[UCHAR (tmpa)]) ++ tmpa = *++a; ++ while (blanks[UCHAR (tmpb)]) ++ tmpb = *++b; ++ } + + if (tmpa == NEGATION_SIGN) + { +@@ -1268,15 +1636,60 @@ + /* 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) ++ free (bufa); ++ free (bufb); ++ 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 +@@ -1294,7 +1707,7 @@ + Return 0 if the name in S is not recognized. */ + + static int +-getmonth (const char *s, size_t len) ++getmonth_uni (const char *s, size_t len) + { + char *month; + register size_t i; +@@ -1332,11 +1745,79 @@ + return result; + } + ++#if HAVE_MBRTOWC ++static int ++getmonth_mb (const char *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, len, &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 != (size_t)-1 && *pp == NULL); ++ ++ for (i = 0; i < wclength; i++) ++ { ++ month_wcs[i] = towupper(month_wcs[i]); ++ if (iswblank (month_wcs[i])) ++ { ++ 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; + +@@ -1507,6 +1988,187 @@ + 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 (;;) ++ { ++ unsigned char *translate = (unsigned char *) key->translate; ++ bool const *ignore = key->ignore; ++ ++ /* Find the lengths. */ ++ size_t lena = lima <= texta ? 0 : lima - texta; ++ size_t lenb = limb <= textb ? 0 : limb - textb; ++ ++ if (key->skipeblanks) ++ { ++ char *a_end = texta + lena; ++ char *b_end = textb + lenb; ++ a_end -= trailing_blanks (texta, lena); ++ b_end -= trailing_blanks (textb, lenb); ++ lena = a_end - texta; ++ lenb = b_end - 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 *copy_a = (char *) alloca (lena + 1 + lenb + 1); ++ 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) && !iswblank (WC))) \ ++ { \ ++ 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); ++ } ++ 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 != -1) ++ lima = limfield (a, key), limb = limfield (b, key); ++ else ++ lima = a->text + a->length - 1, limb = b->text + b->length - 1; ++ ++ if (key->sword != -1) ++ texta = begfield (a, key), textb = begfield (b, key); ++ else ++ { ++ texta = a->text, textb = b->text; ++ if (key->skipsblanks) ++ { ++ while (texta < lima && ismbblank (texta, lima - texta, &mblength_a)) ++ texta += mblength_a; ++ while (textb < limb && ismbblank (textb, limb - textb, &mblength_b)) ++ textb += mblength_b; ++ } ++ } ++ } ++ ++ 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. */ + +@@ -2252,20 +2914,44 @@ + { + struct lconv const *lconvp = localeconv (); + +- /* If the locale doesn't define a decimal point, or if the decimal +- point is multibyte, use the C decimal point. We don't support +- multibyte decimal points yet. */ + decimal_point = *lconvp->decimal_point; + if (! decimal_point || lconvp->decimal_point[1]) +- decimal_point = C_DECIMAL_POINT; ++ { ++ decimal_point = C_DECIMAL_POINT; ++ if (lconvp->decimal_point[0] && lconvp->decimal_point[1]) ++ force_general_numcompare = 1; ++ } + + /* We don't support multibyte thousands separators yet. */ + th_sep = *lconvp->thousands_sep; + if (! th_sep || lconvp->thousands_sep[1]) +- th_sep = CHAR_MAX + 1; ++ { ++ th_sep = CHAR_MAX + 1; ++ if (lconvp->thousands_sep[0] && lconvp->thousands_sep[1]) ++ force_general_numcompare = 1; ++ } + } + #endif + ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ { ++ inittables = inittables_mb; ++ begfield = begfield_mb; ++ limfield = limfield_mb; ++ getmonth = getmonth_mb; ++ keycompare = keycompare_mb; ++ } ++ else ++#endif ++ { ++ inittables = inittables_uni; ++ begfield = begfield_uni; ++ limfield = limfield_uni; ++ keycompare = keycompare_uni; ++ getmonth = getmonth_uni; ++ } ++ + have_read_stdin = false; + inittables (); + +@@ -2462,13 +3148,47 @@ + + case 't': + { +- int newtab = optarg[0]; +- if (! newtab) ++ char newtab[MB_LEN_MAX + 1]; ++ strncpy (newtab, optarg, MB_LEN_MAX); ++ if (! newtab[0]) + error (SORT_FAILURE, 0, _("empty tab")); ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ { ++ wchar_t wc; ++ mbstate_t state; ++ size_t newtab_length, i; ++ ++ memset (&state, '\0', sizeof (mbstate_t)); ++ newtab_length = mbrtowc (&wc, newtab, strnlen (newtab, MB_LEN_MAX), &state); ++ switch (newtab_length) ++ { ++ case (size_t) -1: ++ case (size_t) -2: ++ case 0: ++ newtab_length = 1; ++ } ++ ++ if (optarg[tab_length]) ++ { ++ /* Provoke with `sort -txx'. Complain about ++ "multi-character tab" instead of "multibyte tab", so ++ that the diagnostic's wording does not need to be ++ changed once multibyte characters are supported. */ ++ error (SORT_FAILURE, 0, _("multi-character tab `%s'"), ++ optarg); ++ } ++ ++ for (i = 0; i < newtab_length; i++) ++ tab[i] = newtab[i]; ++ } ++ else ++#endif ++ + if (optarg[1]) + { + if (strcmp (optarg, "\\0") == 0) +- newtab = '\0'; ++ newtab[0] = '\0'; + else + { + /* Provoke with `sort -txx'. Complain about +@@ -2479,9 +3199,9 @@ + optarg); + } + } +- if (tab != TAB_DEFAULT && tab != newtab) ++ if (tab[0] != TAB_DEFAULT && tab[0] != newtab[0]) + error (SORT_FAILURE, 0, _("incompatible tabs")); +- tab = newtab; ++ tab[0] = newtab[0]; + } + break; + +--- coreutils-5.1.3/src/uniq.c.i18n 2004-01-21 22:27:02.000000000 +0000 ++++ coreutils-5.1.3/src/uniq.c 2004-02-16 15:36:40.000000000 +0000 +@@ -23,6 +23,16 @@ + #include + #include + ++/* Get mbstate_t, mbrtowc(). */ ++#if HAVE_WCHAR_H ++# include ++#endif ++ ++/* Get isw* functions. */ ++#if HAVE_WCTYPE_H ++# include ++#endif ++ + #include "system.h" + #include "argmatch.h" + #include "linebuffer.h" +@@ -31,7 +41,19 @@ + #include "posixver.h" + #include "xmemcoll.h" + #include "xstrtol.h" +-#include "memcasecmp.h" ++#include "xmemcoll.h" ++ ++/* MB_LEN_MAX is incorrectly defined to be 1 in at least one GCC ++ installation; work around this configuration error. */ ++#if !defined MB_LEN_MAX || MB_LEN_MAX < 2 ++# define MB_LEN_MAX 16 ++#endif ++ ++/* Some systems, like BeOS, have multibyte encodings but lack mbstate_t. */ ++#if HAVE_MBRTOWC && defined mbstate_t ++# define mbrtowc(pwc, s, n, ps) (mbrtowc) (pwc, s, n, 0) ++#endif ++ + + /* The official name of this program (e.g., no `g' prefix). */ + #define PROGRAM_NAME "uniq" +@@ -108,6 +130,10 @@ + /* Select whether/how to delimit groups of duplicate lines. */ + static enum delimit_method delimit_groups; + ++/* Function pointers. */ ++static char * ++(*find_field) (struct linebuffer *line); ++ + static struct option const longopts[] = + { + {"count", no_argument, NULL, 'c'}, +@@ -188,7 +214,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) + { + register size_t count; + register char *lp = line->buffer; +@@ -209,6 +235,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 || !iswblank (wc)) ++ { ++ pos += mblength; ++ break; ++ } ++ pos += mblength; ++ } ++ ++ while (pos < size) ++ { ++ MBCHAR_TO_WCHAR (wc, mblength, lp, pos, size, statep, convfail); ++ ++ if (!convfail && iswblank (wc)) ++ 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 zero if two strings OLD and NEW match, nonzero if not. + OLD and NEW point not to the beginnings of the lines + but rather to the beginnings of the fields to compare. +@@ -217,6 +320,8 @@ + static int + different (char *old, char *new, size_t oldlen, size_t newlen) + { ++ char *copy_old, *copy_new; ++ + if (check_chars < oldlen) + oldlen = check_chars; + if (check_chars < newlen) +@@ -224,15 +329,93 @@ + + if (ignore_case) + { +- /* FIXME: This should invoke strcoll somehow. */ +- return oldlen != newlen || memcasecmp (old, new, oldlen); ++ size_t i; ++ ++ copy_old = alloca (oldlen + 1); ++ copy_new = alloca (oldlen + 1); ++ ++ for (i = 0; i < oldlen; i++) ++ { ++ copy_old[i] = toupper (old[i]); ++ copy_new[i] = toupper (new[i]); ++ } + } +- else if (HAVE_SETLOCALE && hard_LC_COLLATE) +- return xmemcoll (old, oldlen, new, newlen); + else +- return oldlen != newlen || memcmp (old, new, oldlen); ++ { ++ copy_old = (char *)old; ++ copy_new = (char *)new; ++ } ++ ++ return xmemcoll (copy_old, oldlen, copy_new, newlen); + } + ++#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 stream STREAM + provided that the switches say it should be output. + MATCH is true if the line matches the previous line. +@@ -296,15 +479,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 (istream)) + { + char *thisfield; + size_t thislen; ++#if HAVE_MBRTOWC ++ mbstate_t thisstate; ++#endif ++ + if (readlinebuffer (thisline, istream) == 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, ostream); ++ ++ SWAP_LINES (prevline, thisline); ++ prevfield = thisfield; ++ prevlen = thislen; ++ prevstate = thisstate; ++ } ++ } ++ else ++#endif + if (prevline->length == 0 + || different (thisfield, prevfield, thislen, prevlen)) + { +@@ -323,17 +534,26 @@ + size_t prevlen; + int match_count = 0; + int first_delimiter = 1; ++#if HAVE_MBRTOWC ++ mbstate_t prevstate; ++#endif + + if (readlinebuffer (prevline, istream) == 0) + goto closefiles; + prevfield = find_field (prevline); + prevlen = prevline->length - 1 - (prevfield - prevline->buffer); ++#if HAVE_MBRTOWC ++ prevstate = prevline->state; ++#endif + + while (!feof (istream)) + { + bool match; + char *thisfield; + size_t thislen; ++#if HAVE_MBRTOWC ++ mbstate_t thisstate; ++#endif + if (readlinebuffer (thisline, istream) == 0) + { + if (ferror (istream)) +@@ -342,6 +562,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); + + if (match) +@@ -369,6 +598,9 @@ + SWAP_LINES (prevline, thisline); + prevfield = thisfield; + prevlen = thislen; ++#if HAVE_MBRTOWC ++ prevstate = thisstate; ++#endif + if (!match) + match_count = 0; + } +@@ -411,6 +643,19 @@ + + atexit (close_stdout); + ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ { ++ find_field = find_field_multi; ++ } ++ else ++#endif ++ { ++ find_field = find_field_uni; ++ } ++ ++ ++ + skip_chars = 0; + skip_fields = 0; + check_chars = SIZE_MAX; +--- coreutils-5.1.3/src/expand.c.i18n 2004-01-21 22:27:02.000000000 +0000 ++++ coreutils-5.1.3/src/expand.c 2004-02-16 15:36:40.000000000 +0000 +@@ -38,12 +38,29 @@ + #include + #include + #include ++ ++/* Get mbstate_t, mbrtowc(), wcwidth(). */ ++#if HAVE_WCHAR_H ++# include ++#endif ++ + #include "system.h" + #include "error.h" + #include "posixver.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" + +@@ -332,6 +349,7 @@ + ++column; + } + } ++ + else + { + if (convert) +@@ -343,7 +361,8 @@ + } + else + { +- ++column; ++ if (!ISCNTRL (c)) ++ ++column; + if (convert_entire_line == 0) + convert = 0; + } +@@ -353,6 +372,146 @@ + } + } + ++#if HAVE_MBRTOWC ++static void ++expand_multibyte (void) ++{ ++ FILE *fp; /* Input strem. */ ++ mbstate_t i_state; /* Current shift state of the input stream. */ ++ mbstate_t i_state_bak; /* Back up the I_STATE. */ ++ mbstate_t o_state; /* Current shift state of the output 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. */ ++ wchar_t wc; /* A gotten wide character. */ ++ size_t mblength; /* The byte size of a multibyte character ++ which shows as same character as WC. */ ++ int tab_index = 0; /* Index in `tab_list' of next tabstop. */ ++ int column = 0; /* Column on screen of the next char. */ ++ int next_tab_column; /* Column the next tab stop is on. */ ++ int convert = 1; /* If nonzero, perform translations. */ ++ ++ fp = next_file ((FILE *) NULL); ++ if (fp == NULL) ++ return; ++ ++ /* Binary I/O will preserve the original EOL style (DOS/Unix) of files. */ ++ SET_BINARY2 (fileno (fp), STDOUT_FILENO); ++ ++ memset (&o_state, '\0', sizeof(mbstate_t)); ++ memset (&i_state, '\0', sizeof(mbstate_t)); ++ ++ for (;;) ++ { ++ /* Refill the buffer BUF. */ ++ if (buflen < MB_LEN_MAX && !feof(fp) && !ferror(fp)) ++ { ++ memmove (buf, bufpos, buflen); ++ buflen += fread (buf + buflen, sizeof(char), BUFSIZ, fp); ++ bufpos = buf; ++ } ++ ++ /* No character is left in BUF. */ ++ if (buflen < 1) ++ { ++ fp = next_file (fp); ++ ++ if (fp == NULL) ++ break; /* No more files. */ ++ else ++ { ++ memset (&i_state, '\0', sizeof(mbstate_t)); ++ SET_BINARY2 (fileno (fp), STDOUT_FILENO); ++ continue; ++ } ++ } ++ ++ /* Get a wide character. */ ++ i_state_bak = i_state; ++ mblength = mbrtowc (&wc, bufpos, buflen, &i_state); ++ ++ switch (mblength) ++ { ++ case (size_t)-1: /* illegal byte sequence. */ ++ case (size_t)-2: ++ mblength = 1; ++ i_state = i_state_bak; ++ if (convert) ++ { ++ ++column; ++ if (convert_entire_line == 0) ++ convert = 0; ++ } ++ putchar (*bufpos); ++ break; ++ ++ case 0: /* null. */ ++ mblength = 1; ++ if (convert && convert_entire_line == 0) ++ convert = 0; ++ putchar ('\0'); ++ break; ++ ++ default: ++ if (wc == L'\n') /* LF. */ ++ { ++ tab_index = 0; ++ column = 0; ++ convert = 1; ++ putchar ('\n'); ++ } ++ else if (wc == L'\t' && convert) /* Tab. */ ++ { ++ if (tab_size == 0) ++ { ++ /* Do not let tab_index == first_free_tab; ++ stop when it is 1 less. */ ++ while (tab_index < first_free_tab - 1 ++ && column >= tab_list[tab_index]) ++ tab_index++; ++ next_tab_column = tab_list[tab_index]; ++ if (tab_index < first_free_tab - 1) ++ tab_index++; ++ if (column >= next_tab_column) ++ next_tab_column = column + 1; ++ } ++ else ++ next_tab_column = column + tab_size - column % tab_size; ++ ++ while (column < next_tab_column) ++ { ++ putchar (' '); ++ ++column; ++ } ++ } ++ else /* Others. */ ++ { ++ if (convert) ++ { ++ if (wc == L'\b') ++ { ++ if (column > 0) ++ --column; ++ } ++ else ++ { ++ int width; /* The width of WC. */ ++ ++ width = wcwidth (wc); ++ column += (width > 0) ? width : 0; ++ if (convert_entire_line == 0) ++ convert = 0; ++ } ++ } ++ fwrite (bufpos, sizeof(char), mblength, stdout); ++ } ++ } ++ buflen -= mblength; ++ bufpos += mblength; ++ } ++} ++#endif ++ + int + main (int argc, char **argv) + { +@@ -424,7 +583,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) == EOF) + error (EXIT_FAILURE, errno, "-"); +--- coreutils-5.1.3/src/fold.c.i18n 2004-01-21 22:27:02.000000000 +0000 ++++ coreutils-5.1.3/src/fold.c 2004-02-16 15:36:40.000000000 +0000 +@@ -23,31 +23,74 @@ + #include + #include + ++/* Get mbstate_t, mbrtowc(), wcwidth(). */ ++#if HAVE_WCHAR_H ++# include ++#endif ++ ++/* Get iswprint(), iswblank(), wcwidth(). */ ++#if HAVE_WCTYPE_H ++# include ++#endif ++ + #include "system.h" + #include "error.h" + #include "posixver.h" + #include "xstrtol.h" + ++/* MB_LEN_MAX is incorrectly defined to be 1 in at least one GCC ++ installation; work around this configuration error. */ ++#if !defined MB_LEN_MAX || MB_LEN_MAX < 2 ++# undef MB_LEN_MAX ++# define MB_LEN_MAX 16 ++#endif ++ ++/* Some systems, like BeOS, have multibyte encodings but lack mbstate_t. */ ++#if HAVE_MBRTOWC && defined mbstate_t ++# define mbrtowc(pwc, s, n, ps) (mbrtowc) (pwc, s, n, 0) ++#endif ++ + /* The official name of this program (e.g., no `g' prefix). */ + #define PROGRAM_NAME "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; + +-/* If nonzero, count bytes, not column positions. */ +-static bool count_bytes; +- + /* If nonzero, at least one of the files we read was standard input. */ + static bool have_read_stdin; + + static 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}, +@@ -77,6 +120,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); +@@ -94,7 +138,7 @@ + static size_t + adjust_column (size_t column, char c) + { +- if (!count_bytes) ++ if (operating_mode != byte_mode) + { + if (c == '\b') + { +@@ -113,34 +157,14 @@ + return column; + } + +-/* Fold file FILENAME, or standard input if FILENAME is "-", +- to stdout, with maximum line length WIDTH. +- Return 0 if successful, 1 if an error occurs. */ +- + static int +-fold_file (char *filename, int width) ++fold_text (FILE *istream, int width, int *saved_errno) + { +- FILE *istream; + register int c; + size_t column = 0; /* Screen column where next char will go. */ + size_t offset_out = 0; /* Index in `line_out' for next char. */ + static char *line_out = NULL; + static size_t allocated_out = 0; +- int saved_errno; +- +- if (STREQ (filename, "-")) +- { +- istream = stdin; +- have_read_stdin = true; +- } +- else +- istream = fopen (filename, "r"); +- +- if (istream == NULL) +- { +- error (0, errno, "%s", filename); +- return 1; +- } + + while ((c = getc (istream)) != EOF) + { +@@ -168,6 +192,15 @@ + bool found_blank = false; + size_t logical_end = offset_out; + ++ /* If LINE_OUT has no wide character, ++ put a new wide character in LINE_OUT ++ if column is bigger than width. */ ++ if (offset_out == 0) ++ { ++ line_out[offset_out++] = c; ++ continue; ++ } ++ + /* Look for the last blank. */ + while (logical_end) + { +@@ -214,11 +247,225 @@ + line_out[offset_out++] = c; + } + +- saved_errno = errno; ++ *saved_errno = errno; + + if (offset_out) + fwrite (line_out, sizeof (char), (size_t) offset_out, stdout); + ++ free(line_out); ++} ++ ++#if HAVE_MBRTOWC ++static void ++fold_multibyte_text (FILE *istream, int width, int *saved_errno) ++{ ++ char buf[MB_LEN_MAX + BUFSIZ]; /* For spooling a read byte sequence. */ ++ size_t buflen = 0; /* The length of the byte sequence in buf. */ ++ char *bufpos; /* Next read position of BUF. */ ++ wint_t wc; /* A gotten wide character. */ ++ size_t mblength; /* The byte size of a multibyte character which shows ++ as same character as WC. */ ++ mbstate_t state, state_bak; /* State of the stream. */ ++ int convfail; /* 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 = 0; ++ ++ int increment; ++ size_t column = 0; ++ ++ size_t last_blank_pos; ++ size_t last_blank_column; ++ int is_blank_seen; ++ int last_blank_increment; ++ int is_bs_following_last_blank; ++ size_t bs_following_last_blank_num; ++ int is_cr_after_last_blank; ++ ++#define CLEAR_FLAGS \ ++ do \ ++ { \ ++ last_blank_pos = 0; \ ++ last_blank_column = 0; \ ++ is_blank_seen = 0; \ ++ is_bs_following_last_blank = 0; \ ++ bs_following_last_blank_num = 0; \ ++ is_cr_after_last_blank = 0; \ ++ } \ ++ while (0) ++ ++#define START_NEW_LINE \ ++ do \ ++ { \ ++ putchar ('\n'); \ ++ column = 0; \ ++ offset_out = 0; \ ++ CLEAR_FLAGS; \ ++ } \ ++ while (0) ++ ++ CLEAR_FLAGS; ++ memset (&state, '\0', sizeof(mbstate_t)); ++ ++ for (;; bufpos += mblength, buflen -= mblength) ++ { ++ if (buflen < MB_LEN_MAX && !feof (istream) && !ferror (istream)) ++ { ++ memmove (buf, bufpos, buflen); ++ buflen += fread (buf + buflen, sizeof(char), BUFSIZ, istream); ++ bufpos = buf; ++ } ++ ++ if (buflen < 1) ++ break; ++ ++ /* Get a wide character. */ ++ convfail = 0; ++ state_bak = state; ++ mblength = mbrtowc ((wchar_t *)&wc, bufpos, buflen, &state); ++ ++ switch (mblength) ++ { ++ case (size_t)-1: ++ case (size_t)-2: ++ convfail++; ++ state = state_bak; ++ /* Fall through. */ ++ ++ case 0: ++ mblength = 1; ++ break; ++ } ++ ++rescan: ++ if (operating_mode == byte_mode) /* byte mode */ ++ increment = mblength; ++ else if (operating_mode == character_mode) /* character mode */ ++ increment = 1; ++ else /* column mode */ ++ { ++ if (convfail) ++ increment = 1; ++ else ++ { ++ switch (wc) ++ { ++ case L'\n': ++ fwrite (line_out, sizeof(char), offset_out, stdout); ++ START_NEW_LINE; ++ continue; ++ ++ case L'\b': ++ increment = (column > 0) ? -1 : 0; ++ break; ++ ++ case L'\r': ++ increment = -1 * column; ++ break; ++ ++ case L'\t': ++ increment = 8 - column % 8; ++ break; ++ ++ default: ++ increment = wcwidth (wc); ++ increment = (increment < 0) ? 0 : increment; ++ } ++ } ++ } ++ ++ if (column + increment > width && break_spaces && last_blank_pos) ++ { ++ fwrite (line_out, sizeof(char), last_blank_pos, stdout); ++ putchar ('\n'); ++ ++ offset_out = offset_out - last_blank_pos; ++ column = column - last_blank_column + ((is_cr_after_last_blank) ++ ? last_blank_increment : bs_following_last_blank_num); ++ memmove (line_out, line_out + last_blank_pos, offset_out); ++ CLEAR_FLAGS; ++ goto rescan; ++ } ++ ++ if (column + increment > width && column != 0) ++ { ++ fwrite (line_out, sizeof(char), offset_out, stdout); ++ START_NEW_LINE; ++ goto rescan; ++ } ++ ++ if (allocated_out < offset_out + mblength) ++ { ++ allocated_out += 1024; ++ line_out = xrealloc (line_out, allocated_out); ++ } ++ ++ memcpy (line_out + offset_out, bufpos, mblength); ++ offset_out += mblength; ++ column += increment; ++ ++ if (is_blank_seen && !convfail && wc == L'\r') ++ is_cr_after_last_blank = 1; ++ ++ if (is_bs_following_last_blank && !convfail && wc == L'\b') ++ ++bs_following_last_blank_num; ++ else ++ is_bs_following_last_blank = 0; ++ ++ if (break_spaces && !convfail && iswblank (wc)) ++ { ++ last_blank_pos = offset_out; ++ last_blank_column = column; ++ is_blank_seen = 1; ++ last_blank_increment = increment; ++ is_bs_following_last_blank = 1; ++ bs_following_last_blank_num = 0; ++ is_cr_after_last_blank = 0; ++ } ++ } ++ ++ *saved_errno = errno; ++ ++ if (offset_out) ++ fwrite (line_out, sizeof (char), (size_t) offset_out, stdout); ++ ++ free(line_out); ++} ++#endif ++ ++/* Fold file FILENAME, or standard input if FILENAME is "-", ++ to stdout, with maximum line length WIDTH. ++ Return 0 if successful, 1 if an error occurs. */ ++ ++static int ++fold_file (char *filename, int width) ++{ ++ FILE *istream; ++ int saved_errno; ++ ++ if (STREQ (filename, "-")) ++ { ++ istream = stdin; ++ have_read_stdin = 1; ++ } ++ else ++ istream = fopen (filename, "r"); ++ ++ if (istream == NULL) ++ { ++ error (0, errno, "%s", filename); ++ return 1; ++ } ++ ++ /* Define how ISTREAM is being folded. */ ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ fold_multibyte_text (istream, width, &saved_errno); ++ else ++#endif ++ fold_text (istream, width, &saved_errno); ++ + if (ferror (istream)) + { + error (0, saved_errno, "%s", filename); +@@ -251,7 +498,8 @@ + + atexit (close_stdout); + +- break_spaces = count_bytes = have_read_stdin = false; ++ operating_mode = column_mode; ++ break_spaces = have_read_stdin = false; + + /* Turn any numeric options into -w options. */ + for (i = 1; i < argc; i++) +@@ -278,7 +526,7 @@ + } + } + +- while ((optc = getopt_long (argc, argv, "bsw:", longopts, NULL)) != -1) ++ while ((optc = getopt_long (argc, argv, "bcsw:", longopts, NULL)) != -1) + { + switch (optc) + { +@@ -286,7 +534,15 @@ + break; + + case 'b': /* Count bytes rather than columns. */ +- count_bytes = true; ++ if (operating_mode != column_mode) ++ FATAL_ERROR (_("only one way of folding may be specified")); ++ operating_mode = byte_mode; ++ break; ++ ++ case 'c': ++ if (operating_mode != column_mode) ++ FATAL_ERROR (_("only one way of folding may be specified")); ++ operating_mode = character_mode; + break; + + case 's': /* Break at word boundaries. */ +--- coreutils-5.1.3/src/join.c.i18n 2004-01-21 22:27:02.000000000 +0000 ++++ coreutils-5.1.3/src/join.c 2004-02-16 15:36:40.000000000 +0000 +@@ -24,16 +24,30 @@ + #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" + #include "linebuffer.h" +-#include "memcasecmp.h" + #include "posixver.h" + #include "quote.h" + #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" + +@@ -110,7 +124,10 @@ + /* Tab character separating fields; if this is NUL fields are separated + by any nonempty string of white space, otherwise by exactly one + tab character. */ +-static char tab; ++static char *tab = NULL; ++ ++/* The number of bytes used for tab. */ ++static size_t tablen = 0; + + /* When using getopt_long_only, no long option can start with + a character that is a short option. */ +@@ -222,6 +239,8 @@ + + /* Fill in the `fields' structure in LINE. */ + ++/* Fill in the `fields' structure in LINE. */ ++ + static void + xfields (struct line *line) + { +@@ -231,9 +250,9 @@ + if (ptr == lim) + return; + +- if (tab) ++ if (tab != NULL) + { +- unsigned char t = tab; ++ unsigned char t = tab[0]; + char *sep; + for (; (sep = memchr (ptr, t, lim - ptr)) != NULL; ptr = sep + 1) + extract_field (line, ptr, sep - ptr); +@@ -262,6 +281,147 @@ + extract_field (line, ptr, lim - ptr); + } + ++#if HAVE_MBRTOWC ++static void ++xfields_multibyte (struct line *line) ++{ ++ char *ptr = line->buf.buffer; ++ char const *lim = ptr + line->buf.length - 1; ++ wchar_t wc = 0; ++ size_t mblength; ++ mbstate_t state, state_bak; ++ ++ memset (&state, 0, sizeof (mbstate_t)); ++ ++ if (ptr == lim) ++ return; ++ ++ if (tab != NULL) ++ { ++ unsigned char t = tab[0]; ++ char *sep = ptr; ++ for (; ptr < lim; ptr = sep + 1) ++ { ++ while (sep < lim) ++ { ++ state_bak = state; ++ mblength = mbrtowc (&wc, sep, lim - sep + 1, &state); ++ ++ if (mblength == (size_t)-1 || mblength == (size_t)-2) ++ { ++ mblength = 1; ++ state = state_bak; ++ } ++ mblength = (mblength < 1) ? 1 : mblength; ++ ++ if (mblength == tablen && !memcmp (sep, tab, mblength)) ++ break; ++ else ++ { ++ sep += mblength; ++ continue; ++ } ++ } ++ ++ if (sep == lim) ++ break; ++ ++ extract_field (line, ptr, sep - ptr); ++ } ++ } ++ else ++ { ++ /* Skip leading blanks before the first field. */ ++ while(ptr < lim) ++ { ++ state_bak = state; ++ mblength = mbrtowc (&wc, ptr, lim - ptr + 1, &state); ++ ++ if (mblength == (size_t)-1 || mblength == (size_t)-2) ++ { ++ mblength = 1; ++ state = state_bak; ++ break; ++ } ++ mblength = (mblength < 1) ? 1 : mblength; ++ ++ if (!iswblank(wc)) ++ break; ++ ptr += mblength; ++ } ++ ++ do ++ { ++ char *sep; ++ state_bak = state; ++ mblength = mbrtowc (&wc, ptr, lim - ptr + 1, &state); ++ if (mblength == (size_t)-1 || mblength == (size_t)-2) ++ { ++ mblength = 1; ++ state = state_bak; ++ break; ++ } ++ mblength = (mblength < 1) ? 1 : mblength; ++ ++ sep = ptr + mblength; ++ while (sep != lim) ++ { ++ state_bak = state; ++ mblength = mbrtowc (&wc, sep, lim - sep + 1, &state); ++ if (mblength == (size_t)-1 || mblength == (size_t)-2) ++ { ++ mblength = 1; ++ state = state_bak; ++ break; ++ } ++ mblength = (mblength < 1) ? 1 : mblength; ++ ++ if (iswblank (wc)) ++ break; ++ ++ sep += mblength; ++ } ++ ++ extract_field (line, ptr, sep - ptr); ++ if (sep == lim) ++ return; ++ ++ state_bak = state; ++ mblength = mbrtowc (&wc, sep, lim - sep + 1, &state); ++ if (mblength == (size_t)-1 || mblength == (size_t)-2) ++ { ++ mblength = 1; ++ state = state_bak; ++ break; ++ } ++ mblength = (mblength < 1) ? 1 : mblength; ++ ++ ptr = sep + mblength; ++ while (ptr != lim) ++ { ++ state_bak = state; ++ mblength = mbrtowc (&wc, ptr, lim - ptr + 1, &state); ++ if (mblength == (size_t)-1 || mblength == (size_t)-2) ++ { ++ mblength = 1; ++ state = state_bak; ++ break; ++ } ++ mblength = (mblength < 1) ? 1 : mblength; ++ ++ if (!iswblank (wc)) ++ break; ++ ++ ptr += mblength; ++ } ++ } ++ while (ptr != lim); ++ } ++ ++ extract_field (line, ptr, lim - ptr); ++} ++#endif ++ + /* Read a line from FP into LINE and split it into fields. + Return true if successful. */ + +@@ -282,6 +442,11 @@ + line->nfields_allocated = 0; + line->nfields = 0; + line->fields = NULL; ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ xfields_multibyte (line); ++ else ++#endif + xfields (line); + return true; + } +@@ -336,56 +501,116 @@ + 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'; ++ } ++ } ++ else ++#endif ++ { ++ 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'; ++ } ++ } + } + else + { +- if (HAVE_SETLOCALE && hard_LC_COLLATE) +- return xmemcoll (beg1, len1, beg2, len2); +- diff = memcmp (beg1, beg2, MIN (len1, len2)); ++ copy[0] = (unsigned char *) beg[0]; ++ copy[1] = (unsigned char *) beg[1]; + } + ++#ifdef HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ return xmemcoll ((char *) copy[0], len[0], (char *) copy[1], len[1]); ++#endif ++ diff = memcmp (copy[0], copy[1], MIN (len[0], len[1])); ++ + if (diff) + return diff; +- return len1 < len2 ? -1 : len1 != len2; ++ return len[0] - len[1]; + } + + /* Print field N of LINE if it exists and is nonempty, otherwise +@@ -410,11 +635,18 @@ + + /* Print the join of LINE1 and LINE2. */ + ++#define PUT_TAB_CHAR \ ++ do \ ++ { \ ++ (tab != NULL) ? \ ++ fwrite(tab, sizeof(char), tablen, stdout) : putchar (' '); \ ++ } \ ++ while (0) ++ + static void + prjoin (struct line const *line1, struct line const *line2) + { + const struct outlist *outlist; +- char output_separator = tab ? tab : ' '; + + outlist = outlist_head.next; + if (outlist) +@@ -430,12 +662,12 @@ + if (o->file == 0) + { + if (line1 == &uni_blank) +- { ++ { + line = line2; + field = join_field_2; + } + else +- { ++ { + line = line1; + field = join_field_1; + } +@@ -449,7 +681,7 @@ + o = o->next; + if (o == NULL) + break; +- putchar (output_separator); ++ PUT_TAB_CHAR; + } + putchar ('\n'); + } +@@ -467,23 +699,23 @@ + prfield (join_field_1, line1); + for (i = 0; i < join_field_1 && i < line1->nfields; ++i) + { +- putchar (output_separator); ++ PUT_TAB_CHAR; + prfield (i, line1); + } + for (i = join_field_1 + 1; i < line1->nfields; ++i) + { +- putchar (output_separator); ++ PUT_TAB_CHAR; + prfield (i, line1); + } + + for (i = 0; i < join_field_2 && i < line2->nfields; ++i) + { +- putchar (output_separator); ++ PUT_TAB_CHAR; + prfield (i, line2); + } + for (i = join_field_2 + 1; i < line2->nfields; ++i) + { +- putchar (output_separator); ++ PUT_TAB_CHAR; + prfield (i, line2); + } + putchar ('\n'); +@@ -678,7 +910,7 @@ + valid = true; + } + else +- { ++ { + /* `0' must be all alone -- no `.FIELD'. */ + error (0, 0, _("invalid field specifier: `%s'"), s); + } +@@ -817,7 +1049,23 @@ + break; + + case 't': +- tab = *optarg; ++ tab = xstrdup(optarg); ++#if HAVE_MBRTOWC ++ if (MB_CUR_MAX > 1) ++ { ++ mbstate_t state; ++ ++ memset(&state, 0, sizeof(mbstate_t)); ++ tablen = mbrtowc(NULL, optarg, strnlen(optarg, MB_LEN_MAX), &state); ++ if (tablen == (size_t) 0 ++ || tablen == (size_t)-1 || tablen == (size_t)-2) ++ tablen = 1; ++ } ++ else ++ { ++#endif ++ tablen = 1; ++ } + break; + + case 1: /* Non-option argument. */ +--- coreutils-5.1.3/src/unexpand.c.i18n 2004-01-21 22:27:02.000000000 +0000 ++++ coreutils-5.1.3/src/unexpand.c 2004-02-16 15:36:40.000000000 +0000 +@@ -40,10 +40,27 @@ + #include + #include + #include ++ ++/* Get mbstate_t, mbrtowc(), wcwidth(). */ ++#if HAVE_WCHAR_H ++# include ++#endif ++ + #include "system.h" + #include "error.h" + #include "posixver.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 "unexpand" + +@@ -348,7 +365,8 @@ + } + else + { +- ++column; ++ if (!ISCNTRL (c)) ++ ++column; + if (convert_entire_line == 0) + convert = 0; + } +@@ -366,6 +384,210 @@ + } + } + ++#if HAVE_MBRTOWC ++static void ++unexpand_multibyte (void) ++{ ++ FILE *fp; /* Input stream. */ ++ mbstate_t i_state; /* Current shift state of the input stream. */ ++ mbstate_t i_state_bak; /* Back up the I_STATE. */ ++ mbstate_t o_state; /* Current shift state of the output 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. */ ++ wint_t wc; /* A gotten wide character. */ ++ size_t mblength; /* The byte size of a multibyte character ++ which shows as same character as WC. */ ++ ++ /* Index in `tab_list' of next tabstop: */ ++ int tab_index = 0; /* For calculating width of pending tabs. */ ++ int print_tab_index = 0; /* For printing as many tabs as possible. */ ++ unsigned int column = 0; /* Column on screen of next char. */ ++ int next_tab_column; /* Column the next tab stop is on. */ ++ int convert = 1; /* If nonzero, perform translations. */ ++ unsigned int pending = 0; /* Pending columns of blanks. */ ++ ++ fp = next_file ((FILE *) NULL); ++ if (fp == NULL) ++ return; ++ ++ /* Binary I/O will preserve the original EOL style (DOS/Unix) of files. */ ++ SET_BINARY2 (fileno (fp), STDOUT_FILENO); ++ ++ memset (&o_state, '\0', sizeof(mbstate_t)); ++ memset (&i_state, '\0', sizeof(mbstate_t)); ++ ++ for (;;) ++ { ++ if (buflen < MB_LEN_MAX && !feof(fp) && !ferror(fp)) ++ { ++ memmove (buf, bufpos, buflen); ++ buflen += fread (buf + buflen, sizeof(char), BUFSIZ, fp); ++ bufpos = buf; ++ } ++ ++ /* Get a wide character. */ ++ if (buflen < 1) ++ { ++ mblength = 1; ++ wc = WEOF; ++ } ++ else ++ { ++ i_state_bak = i_state; ++ mblength = mbrtowc ((wchar_t *)&wc, bufpos, buflen, &i_state); ++ } ++ ++ if (mblength == (size_t)-1 || mblength == (size_t)-2) ++ { ++ i_state = i_state_bak; ++ wc = L'\0'; ++ } ++ ++ if (wc == L' ' && convert && column < TAB_STOP_SENTINEL) ++ { ++ ++pending; ++ ++column; ++ } ++ else if (wc == L'\t' && convert) ++ { ++ if (tab_size == 0) ++ { ++ /* Do not let tab_index == first_free_tab; ++ stop when it is 1 less. */ ++ while (tab_index < first_free_tab - 1 ++ && column >= tab_list[tab_index]) ++ tab_index++; ++ next_tab_column = tab_list[tab_index]; ++ if (tab_index < first_free_tab - 1) ++ tab_index++; ++ if (column >= next_tab_column) ++ { ++ convert = 0; /* Ran out of tab stops. */ ++ goto flush_pend_mb; ++ } ++ } ++ else ++ { ++ next_tab_column = column + tab_size - column % tab_size; ++ } ++ pending += next_tab_column - column; ++ column = next_tab_column; ++ } ++ else ++ { ++flush_pend_mb: ++ /* Flush pending spaces. Print as many tabs as possible, ++ then print the rest as spaces. */ ++ if (pending == 1) ++ { ++ putchar (' '); ++ pending = 0; ++ } ++ column -= pending; ++ while (pending > 0) ++ { ++ if (tab_size == 0) ++ { ++ /* Do not let print_tab_index == first_free_tab; ++ stop when it is 1 less. */ ++ while (print_tab_index < first_free_tab - 1 ++ && column >= tab_list[print_tab_index]) ++ print_tab_index++; ++ next_tab_column = tab_list[print_tab_index]; ++ if (print_tab_index < first_free_tab - 1) ++ print_tab_index++; ++ } ++ else ++ { ++ next_tab_column = ++ column + tab_size - column % tab_size; ++ } ++ if (next_tab_column - column <= pending) ++ { ++ putchar ('\t'); ++ pending -= next_tab_column - column; ++ column = next_tab_column; ++ } ++ else ++ { ++ --print_tab_index; ++ column += pending; ++ while (pending != 0) ++ { ++ putchar (' '); ++ pending--; ++ } ++ } ++ } ++ ++ if (wc == WEOF) ++ { ++ fp = next_file (fp); ++ if (fp == NULL) ++ break; /* No more files. */ ++ else ++ { ++ memset (&i_state, '\0', sizeof(mbstate_t)); ++ SET_BINARY2 (fileno (fp), STDOUT_FILENO); ++ continue; ++ } ++ } ++ ++ if (mblength == (size_t)-1 || mblength == (size_t)-2) ++ { ++ if (convert) ++ { ++ ++column; ++ if (convert_entire_line == 0) ++ convert = 0; ++ } ++ mblength = 1; ++ putchar (buf[0]); ++ } ++ else if (mblength == 0) ++ { ++ if (convert && convert_entire_line == 0) ++ convert = 0; ++ mblength = 1; ++ putchar ('\0'); ++ } ++ else ++ { ++ if (convert) ++ { ++ if (wc == L'\b') ++ { ++ if (column > 0) ++ --column; ++ } ++ else ++ { ++ int width; /* The width of WC. */ ++ ++ width = wcwidth (wc); ++ column += (width > 0) ? width : 0; ++ if (convert_entire_line == 0) ++ convert = 0; ++ } ++ } ++ ++ if (wc == L'\n') ++ { ++ tab_index = print_tab_index = 0; ++ column = pending = 0; ++ convert = 1; ++ } ++ fwrite (bufpos, sizeof(char), mblength, stdout); ++ } ++ } ++ buflen -= mblength; ++ bufpos += mblength; ++ } ++} ++#endif ++ ++ + void + usage (int status) + { +@@ -488,7 +710,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) == EOF) + error (EXIT_FAILURE, errno, "-"); diff --git a/coreutils-pam.patch b/coreutils-pam.patch new file mode 100644 index 0000000..f24d0e6 --- /dev/null +++ b/coreutils-pam.patch @@ -0,0 +1,353 @@ +--- coreutils-5.2.0/src/Makefile.am.pam 2004-02-23 17:40:54.000000000 +0000 ++++ coreutils-5.2.0/src/Makefile.am 2004-02-23 17:40:54.000000000 +0000 +@@ -66,7 +66,7 @@ + + uptime_LDADD = $(LDADD) $(GETLOADAVG_LIBS) + +-su_LDADD = $(LDADD) $(LIB_CRYPT) ++su_LDADD = $(LDADD) $(LIB_CRYPT) @LIB_PAM@ + + $(PROGRAMS): ../lib/libfetish.a + +--- coreutils-5.2.0/src/su.c.pam 2004-02-23 17:40:54.000000000 +0000 ++++ coreutils-5.2.0/src/su.c 2004-02-23 17:40:54.000000000 +0000 +@@ -38,6 +38,16 @@ + restricts who can su to UID 0 accounts. RMS considers that to + be fascist. + ++#ifdef USE_PAM ++ ++ 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.conf ++ ++#endif ++ + Options: + -, -l, --login Make the subshell a login shell. + Unset all environment variables except +@@ -81,6 +91,14 @@ + prototype (returning `int') in . */ + #define getusershell _getusershell_sys_proto_ + ++#ifdef USE_PAM ++# include ++# include ++# include ++# include ++# include ++#endif /* USE_PAM */ ++ + #include "system.h" + #include "dirname.h" + +@@ -150,7 +168,9 @@ + /* The user to become if none is specified. */ + #define DEFAULT_USER "root" + ++#ifndef USE_PAM + char *crypt (); ++#endif + char *getpass (); + char *getusershell (); + void endusershell (); +@@ -158,8 +178,12 @@ + + extern char **environ; + +-static void run_shell (const char *, const char *, char **) ++static void run_shell (const char *, const char *, char **, const struct passwd *) ++#ifdef USE_PAM ++ ; ++#else + ATTRIBUTE_NORETURN; ++#endif + + /* The name this program was run with. */ + char *program_name; +@@ -271,7 +295,22 @@ + } + #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 if (retval) { \ ++ pam_end(pamh, PAM_SUCCESS); \ ++ return 0; \ ++} ++#endif ++ + /* Ask the user for a password. ++ If PAM is in use, let PAM ask for the password if necessary. + Return 1 if the user gives the correct password for entry PW, + 0 if not. Return 1 without asking for a password if run by UID 0 + or if PW has an empty password. */ +@@ -279,6 +318,34 @@ + static int + correct_password (const struct passwd *pw) + { ++#ifdef USE_PAM ++ struct passwd *caller; ++ retval = pam_start(PROGRAM_NAME, pw->pw_name, &conv, &pamh); ++ PAM_BAIL_P; ++ ++ if (getuid() != 0 && !isatty(0)) { ++ fprintf(stderr, "standard in must be a tty\n"); ++ exit(1); ++ } ++ ++ caller = getpwuid(getuid()); ++ if(caller != NULL && caller->pw_name != NULL) { ++ retval = pam_set_item(pamh, PAM_RUSER, caller->pw_name); ++ PAM_BAIL_P; ++ } ++ ++ retval = pam_authenticate(pamh, 0); ++ PAM_BAIL_P; ++ 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; ++ } ++ PAM_BAIL_P; ++ /* must be authenticated if this point was reached */ ++ return 1; ++#else /* !USE_PAM */ + char *unencrypted, *encrypted, *correct; + #if HAVE_GETSPNAM && HAVE_STRUCT_SPWD_SP_PWDP + /* Shadow passwd stuff for SVR3 and maybe other systems. */ +@@ -303,6 +370,7 @@ + encrypted = crypt (unencrypted, correct); + memset (unencrypted, 0, strlen (unencrypted)); + return strcmp (encrypted, correct) == 0; ++#endif /* !USE_PAM */ + } + + /* Update `environ' for the new shell based on PW, with SHELL being +@@ -312,16 +380,24 @@ + modify_environment (const struct passwd *pw, const char *shell) + { + char *term; ++ char *display; ++ char *xauthority; + + if (simulate_login) + { +- /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH. ++ /* Leave TERM, DISPLAY, XAUTHORITY unchanged. Set HOME, SHELL, USER, LOGNAME, PATH. + Unset all other environment variables. */ + term = getenv ("TERM"); ++ display = getenv ("DISPLAY"); ++ xauthority = getenv ("XAUTHORITY"); + environ = xmalloc (2 * sizeof (char *)); + environ[0] = 0; + if (term) + xputenv (concat ("TERM", "=", term)); ++ if (display) ++ xputenv (concat ("DISPLAY", "=", display)); ++ if (xauthority) ++ xputenv (concat ("XAUTHORITY", "=", xauthority)); + xputenv (concat ("HOME", "=", pw->pw_dir)); + xputenv (concat ("SHELL", "=", shell)); + xputenv (concat ("USER", "=", pw->pw_name)); +@@ -358,22 +434,73 @@ + error (EXIT_FAIL, errno, _("cannot set groups")); + endgrent (); + #endif ++#ifdef USE_PAM ++ retval = pam_setcred(pamh, PAM_ESTABLISH_CRED); ++ if (retval != PAM_SUCCESS) ++ error (1, 0, 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 int caught=0; ++/* Signal handler for parent process later */ ++static void su_catch_sig(int sig) ++{ ++ ++caught; ++} ++ ++int ++pam_copyenv (pam_handle_t *pamh) ++{ ++ char **env; ++ ++ env = pam_getenvlist(pamh); ++ if(env) { ++ while(*env) { ++ xputenv(*env); ++ env++; ++ } ++ } ++ return(0); ++} ++#endif ++ + /* Run SHELL, or DEFAULT_SHELL if SHELL is empty. + If COMMAND is nonzero, pass it to the shell with the -c option. + If ADDITIONAL_ARGS is nonzero, pass it to the shell as more + arguments. */ + + static void +-run_shell (const char *shell, const char *command, char **additional_args) ++run_shell (const char *shell, const char *command, char **additional_args, const struct passwd *pw) + { + const char **args; + int argno = 1; ++#ifdef USE_PAM ++ int child; ++ sigset_t ourset; ++ int status; ++ ++ retval = pam_open_session(pamh,0); ++ if (retval != PAM_SUCCESS) { ++ fprintf (stderr, "could not open session\n"); ++ exit (1); ++ } ++ ++/* do this at the last possible moment, because environment variables may ++ be passed even in the session phase ++*/ ++ if(pam_copyenv(pamh) != PAM_SUCCESS) ++ fprintf (stderr, "error copying PAM environment\n"); ++ ++ child = fork(); ++ if (child == 0) { /* child shell */ ++ change_identity (pw); ++ pam_end(pamh, 0); ++#endif + + if (additional_args) + args = xmalloc (sizeof (char *) +@@ -385,6 +512,9 @@ + char *arg0; + char *shell_basename; + ++ if(chdir(pw->pw_dir)) ++ error(0, errno, _("warning: cannot change directory to %s"), pw->pw_dir); ++ + shell_basename = base_name (shell); + arg0 = xmalloc (strlen (shell_basename) + 2); + arg0[0] = '-'; +@@ -411,6 +541,61 @@ + error (0, errno, "%s", shell); + exit (exit_status); + } ++#ifdef USE_PAM ++ } else if (child == -1) { ++ fprintf(stderr, "can not fork user shell: %s", strerror(errno)); ++ exit(1); ++ } ++ /* parent only */ ++ sigfillset(&ourset); ++ if (sigprocmask(SIG_BLOCK, &ourset, NULL)) { ++ fprintf(stderr, "%s: signal malfunction\n", PROGRAM_NAME); ++ caught = 1; ++ } ++ 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)) { ++ fprintf(stderr, "%s: signal masking malfunction\n", PROGRAM_NAME); ++ caught = 1; ++ } ++ } ++ if (!caught) { ++ do { ++ int pid; ++ ++ pid = waitpid(-1, &status, WUNTRACED); ++ ++ if (WIFSTOPPED(status)) { ++ kill(getpid(), SIGSTOP); ++ /* once we get here, we must have resumed */ ++ kill(pid, SIGCONT); ++ } ++ } while (WIFSTOPPED(status)); ++ } ++ ++ if (caught) { ++ fprintf(stderr, "\nSession terminated, killing shell..."); ++ kill (child, SIGTERM); ++ } ++ retval = pam_close_session(pamh, 0); ++ PAM_BAIL_P; ++ retval = pam_end(pamh, PAM_SUCCESS); ++ PAM_BAIL_P; ++ if (caught) { ++ sleep(2); ++ kill(child, SIGKILL); ++ fprintf(stderr, " ...killed.\n"); ++ exit(-1); ++ } ++ exit (WEXITSTATUS(status)); ++#endif /* USE_PAM */ + } + + /* Return 1 if SHELL is a restricted shell (one not returned by +@@ -586,9 +771,13 @@ + } + modify_environment (pw, shell); + ++ ++#ifdef USE_PAM ++ setfsuid(pw->pw_uid); ++ setfsgid(pw->pw_gid); ++#else + change_identity (pw); +- if (simulate_login && chdir (pw->pw_dir)) +- error (0, errno, _("warning: cannot change directory to %s"), pw->pw_dir); ++#endif + +- run_shell (shell, command, additional_args); ++ run_shell (shell, command, additional_args, pw); + } +--- coreutils-5.2.0/configure.ac.pam 2004-02-23 17:40:54.000000000 +0000 ++++ coreutils-5.2.0/configure.ac 2004-02-23 17:40:54.000000000 +0000 +@@ -7,6 +7,13 @@ + + AM_INIT_AUTOMAKE([1.8 gnits dist-bzip2]) + ++dnl Give the chance to enable PAM ++AC_ARG_ENABLE(pam, dnl ++[ --enable-pam Enable use of the PAM libraries], ++[AC_DEFINE(USE_PAM, 1, [Define if you want to use PAM]) ++LIB_PAM="-ldl -lpam -lpam_misc" ++AC_SUBST(LIB_PAM)]) ++ + gl_DEFAULT_POSIX2_VERSION + gl_USE_SYSTEM_EXTENSIONS + jm_PERL +--- coreutils-5.2.0/config.hin.pam 2004-02-23 17:40:54.000000000 +0000 ++++ coreutils-5.2.0/config.hin 2004-02-23 17:40:54.000000000 +0000 +@@ -1365,6 +1365,9 @@ + /* Define if you want access control list support. */ + #undef USE_ACL + ++/* Define if you want to use PAM */ ++#undef USE_PAM ++ + /* Version number of package */ + #undef VERSION + diff --git a/coreutils-selinux.patch b/coreutils-selinux.patch index fca5753..6ae5094 100644 --- a/coreutils-selinux.patch +++ b/coreutils-selinux.patch @@ -1,2073 +1,112 @@ ---- /dev/null 2003-09-15 09:40:47.000000000 -0400 -+++ coreutils-5.0/src/chcon.c 2003-12-12 13:03:00.709576564 -0500 -@@ -0,0 +1,415 @@ -+/* chcontext -- change security context of a pathname */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "system.h" -+#include "error.h" -+#include "savedir.h" -+#include "group-member.h" -+ -+enum Change_status -+{ -+ CH_SUCCEEDED, -+ CH_FAILED, -+ CH_NO_CHANGE_REQUESTED -+}; -+ -+enum Verbosity -+{ -+ /* Print a message for each file that is processed. */ -+ V_high, -+ -+ /* Print a message for each file whose attributes we change. */ -+ V_changes_only, -+ -+ /* Do not be verbose. This is the default. */ -+ V_off -+}; -+ -+static int change_dir_context PARAMS ((const char *dir, const struct stat *statp)); -+ -+/* The name the program was run with. */ -+char *program_name; -+ -+/* If nonzero, and the systems has support for it, change the context -+ of symbolic links rather than any files they point to. */ -+static int change_symlinks; -+ -+/* If nonzero, change the context of directories recursively. */ -+static int recurse; -+ -+/* If nonzero, force silence (no error messages). */ -+static int force_silent; -+ -+/* Level of verbosity. */ -+static enum Verbosity verbosity = V_off; -+ -+/* The name of the context file is being given. */ -+static const char *specified_context; -+ -+/* Specific components of the context */ -+static const char *specified_user; -+static const char *specified_role; -+static const char *specified_range; -+static const char *specified_type; -+ -+/* The argument to the --reference option. Use the context of this file. -+ This file must exist. */ -+static char *reference_file; -+ -+/* If nonzero, display usage information and exit. */ -+static int show_help; -+ -+/* If nonzero, print the version on standard output and exit. */ -+static int show_version; -+ -+static struct option const long_options[] = -+{ -+ {"recursive", no_argument, 0, 'R'}, -+ {"changes", no_argument, 0, 'c'}, -+ {"no-dereference", no_argument, 0, 'h'}, -+ {"silent", no_argument, 0, 'f'}, -+ {"quiet", no_argument, 0, 'f'}, -+ {"reference", required_argument, 0, CHAR_MAX + 1}, -+ {"context", required_argument, 0, CHAR_MAX + 2}, -+ {"user", required_argument, 0, 'u'}, -+ {"role", required_argument, 0, 'r'}, -+ {"type", required_argument, 0, 't'}, -+ {"range", required_argument, 0, 'l'}, -+ {"verbose", no_argument, 0, 'v'}, -+ {"help", no_argument, &show_help, 1}, -+ {"version", no_argument, &show_version, 1}, -+ {0, 0, 0, 0} -+}; -+ -+/* Tell the user how/if the context of FILE has been changed. -+ CHANGED describes what (if anything) has happened. */ -+ -+static void -+describe_change (const char *file, security_context_t newcontext, enum Change_status changed) -+{ -+ const char *fmt; -+ switch (changed) -+ { -+ case CH_SUCCEEDED: -+ fmt = _("context of %s changed to %s\n"); -+ break; -+ case CH_FAILED: -+ fmt = _("failed to change context of %s to %s\n"); -+ break; -+ case CH_NO_CHANGE_REQUESTED: -+ fmt = _("context of %s retained as %s\n"); -+ break; -+ default: -+ abort (); -+ } -+ printf (fmt, file, newcontext); -+} -+ -+static int -+compute_context_from_mask (security_context_t context, context_t *ret) -+{ -+ context_t newcontext = context_new (context); -+ if (!newcontext) -+ return 1; -+#define SETCOMPONENT(comp) \ -+ do { \ -+ if (specified_ ## comp) \ -+ if (context_ ## comp ## _set (newcontext, specified_ ## comp)) \ -+ goto lose; \ -+ } while (0) -+ -+ SETCOMPONENT(user); -+ SETCOMPONENT(range); -+ SETCOMPONENT(role); -+ SETCOMPONENT(type); -+#undef SETCOMPONENT -+ -+ *ret = newcontext; -+ return 0; -+ lose: -+ context_free (newcontext); -+ return 1; -+} -+ -+/* Change the context of FILE, using specified components. -+ If it is a directory and -R is given, recurse. -+ Return 0 if successful, 1 if errors occurred. */ -+ -+static int -+change_file_context (const char *file) -+{ -+ struct stat file_stats; -+ security_context_t file_context=NULL; -+ context_t context; -+ security_context_t context_string; -+ int errors = 0; -+ -+ if ((lgetfilecon(file, &file_context)<0) && (errno != ENODATA)) -+ { -+ if (force_silent == 0) -+ error (0, errno, "%s", file); -+ return 1; -+ } -+ -+ /* If the file doesn't have a context, and we're not setting all of -+ the context components, there isn't really an obvious default. -+ Thus, we just give up. */ -+ if (file_context == NULL && specified_context == NULL) -+ { -+ error (0, 0, _("can't apply partial context to unlabeled file %s"), file); -+ return 1; -+ } -+ -+ if (specified_context == NULL) -+ { -+ if (compute_context_from_mask (file_context, &context)) -+ { -+ error (0, 0, _("couldn't compute security context from %s"), file_context); -+ return 1; -+ } -+ } -+ else -+ { -+ context = context_new (specified_context); -+ if (!context) -+ error (1, 0,_("invalid context: %s"),specified_context); -+ } -+ -+ context_string = context_str (context); -+ -+ if (strcmp(context_string,file_context)!=0) -+ { -+ int fail; -+ -+ if (change_symlinks) -+ fail = lsetfilecon (file, context_string); -+ else -+ fail = setfilecon (file, context_string); -+ -+ if (verbosity == V_high || (verbosity == V_changes_only && !fail)) -+ describe_change (file, context_string, (fail ? CH_FAILED : CH_SUCCEEDED)); -+ -+ if (fail) -+ { -+ errors = 1; -+ if (force_silent == 0) -+ { -+ error (0, errno, _("failed to change context of %s to %s"), file, context_string); -+ } -+ } -+ } -+ else if (verbosity == V_high) -+ { -+ describe_change (file, context_string, CH_NO_CHANGE_REQUESTED); -+ } -+ -+ context_free(context); -+ freecon(file_context); -+ -+ if (recurse) { -+ if (lstat(file, &file_stats)==0) -+ if (S_ISDIR (file_stats.st_mode)) -+ errors |= change_dir_context (file, &file_stats); -+ } -+ return errors; -+} -+ -+/* Recursively change context of the files in directory DIR -+ using specified context components. -+ STATP points to the results of lstat on DIR. -+ Return 0 if successful, 1 if errors occurred. */ -+ -+static int -+change_dir_context (const char *dir, const struct stat *statp) -+{ -+ char *name_space, *namep; -+ char *path; /* Full path of each entry to process. */ -+ unsigned dirlength; /* Length of `dir' and '\0'. */ -+ unsigned filelength; /* Length of each pathname to process. */ -+ unsigned pathlength; /* Bytes allocated for `path'. */ -+ int errors = 0; -+ -+ errno = 0; -+ name_space = savedir (dir); -+ if (name_space == NULL) -+ { -+ if (errno) -+ { -+ if (force_silent == 0) -+ error (0, errno, "%s", dir); -+ return 1; -+ } -+ else -+ error (1, 0, _("virtual memory exhausted")); -+ } +--- coreutils-5.2.1/configure.ac.selinux 2004-03-16 14:25:05.461535240 -0500 ++++ coreutils-5.2.1/configure.ac 2004-03-16 14:25:05.594515024 -0500 +@@ -14,6 +14,13 @@ + LIB_PAM="-ldl -lpam -lpam_misc" + AC_SUBST(LIB_PAM)]) + ++dnl Give the chance to enable SELINUX ++AC_ARG_ENABLE(selinux, dnl ++[ --enable-selinux Enable use of the SELINUX libraries], ++[AC_DEFINE(WITH_SELINUX, 1, [Define if you want to use SELINUX]) ++LIB_SELINUX="-lselinux" ++AC_SUBST(LIB_SELINUX)]) + -+ dirlength = strlen (dir) + 1; /* + 1 is for the trailing '/'. */ -+ pathlength = dirlength + 1; -+ /* Give `path' a dummy value; it will be reallocated before first use. */ -+ path = xmalloc (pathlength); -+ strcpy (path, dir); -+ path[dirlength - 1] = '/'; -+ -+ for (namep = name_space; *namep; namep += filelength - dirlength) -+ { -+ filelength = dirlength + strlen (namep) + 1; -+ if (filelength > pathlength) -+ { -+ pathlength = filelength * 2; -+ path = xrealloc (path, pathlength); -+ } -+ strcpy (path + dirlength, namep); -+ errors |= change_file_context (path); -+ } -+ free (path); -+ free (name_space); -+ return errors; -+} -+ -+static void -+usage (int status) -+{ -+ if (status != 0) -+ fprintf (stderr, _("Try `%s --help' for more information.\n"), -+ program_name); -+ else -+ { -+ printf (_("\ -+Usage: %s [OPTION]... CONTEXT FILE...\n\ -+ or: %s [OPTION]... [-u USER] [-r ROLE] [-l RANGE] [-t TYPE] FILE...\n\ -+ or: %s [OPTION]... --reference=RFILE FILE...\n\ -+"), -+ program_name, program_name, program_name); -+ printf (_("\ -+Change the security context of each FILE to CONTEXT.\n\ -+\n\ -+ -c, --changes like verbose but report only when a change is made\n\ -+ -h, --no-dereference affect symbolic links instead of any referenced file\n\ -+ (available only on systems with lchown system call)\n\ -+ -f, --silent, --quiet suppress most error messages\n\ -+ --reference=RFILE use RFILE's group instead of using a CONTEXT value\n\ -+ -u, --user=USER set user USER in the target security context\n\ -+ -r, --role=ROLE set role ROLE in the target security context\n\ -+ -t, --type=TYPE set type TYPE in the target security context\n\ -+ -l, --range=RANGE set range RANGE in the target security context\n\ -+ -R, --recursive change files and directories recursively\n\ -+ -v, --verbose output a diagnostic for every file processed\n\ -+ --help display this help and exit\n\ -+ --version output version information and exit\n\ -+")); -+ close_stdout (); -+ } -+ exit (status); -+} -+ -+int -+main (int argc, char **argv) -+{ -+ security_context_t ref_context = NULL; -+ int errors = 0; -+ int optc; -+ int component_specified = 0; -+ -+ program_name = argv[0]; -+ setlocale (LC_ALL, ""); -+ bindtextdomain (PACKAGE, LOCALEDIR); -+ textdomain (PACKAGE); -+ -+ recurse = force_silent = 0; -+ -+ while ((optc = getopt_long (argc, argv, "Rcfhvu:r:t:l:", long_options, NULL)) != -1) -+ { -+ switch (optc) -+ { -+ case 0: -+ break; -+ case 'u': -+ specified_user = optarg; -+ component_specified = 1; -+ break; -+ case 'r': -+ specified_role = optarg; -+ component_specified = 1; -+ break; -+ case 't': -+ specified_type = optarg; -+ component_specified = 1; -+ break; -+ case 'l': -+ specified_range = optarg; -+ component_specified = 1; -+ break; -+ case CHAR_MAX + 1: -+ reference_file = optarg; -+ break; -+ case 'R': -+ recurse = 1; -+ break; -+ case 'c': -+ verbosity = V_changes_only; -+ break; -+ case 'f': -+ force_silent = 1; -+ break; -+ case 'h': -+ change_symlinks = 1; -+ break; -+ case 'v': -+ verbosity = V_high; -+ break; -+ default: -+ usage (1); -+ } -+ } -+ -+ if (show_version) -+ { -+ printf ("chcon (%s) %s\n", GNU_PACKAGE, VERSION); -+ close_stdout (); -+ exit (0); -+ } -+ -+ if (show_help) -+ usage (0); -+ -+ -+ if (reference_file && component_specified) -+ { -+ error (0, 0, _("conflicting security context specifiers given")); -+ usage (1); -+ } -+ -+ if (!(((reference_file || component_specified) -+ && (argc - optind > 0)) -+ || (argc - optind > 1))) -+ { -+ error (0, 0, _("too few arguments")); -+ usage (1); -+ } -+ -+ if (reference_file) -+ { -+ if (getfilecon (reference_file, &ref_context)<0) -+ error (1, errno, "%s", reference_file); -+ -+ specified_context = ref_context; -+ } -+ else if (!component_specified) { -+ specified_context = argv[optind++]; -+ } -+ for (; optind < argc; ++optind) -+ errors |= change_file_context (argv[optind]); -+ -+ if (verbosity != V_off) -+ close_stdout (); -+ if (ref_context != NULL) -+ freecon(ref_context); -+ exit (errors); -+} ---- coreutils-5.0/src/copy.c.selinux 2003-12-09 20:44:56.000000000 -0500 -+++ coreutils-5.0/src/copy.c 2003-12-09 20:45:30.000000000 -0500 -@@ -46,6 +46,11 @@ - #include "same.h" - #include "xreadlink.h" - -+#ifdef WITH_SELINUX -+#include /* for is_selinux_enabled() */ -+extern int selinux_enabled; -+#endif -+ - #define DO_CHOWN(Chown, File, New_uid, New_gid) \ - (Chown (File, New_uid, New_gid) \ - /* If non-root uses -p, it's ok if we can't preserve ownership. \ -@@ -1233,6 +1238,32 @@ - In such cases, set this variable to zero. */ - preserve_metadata = 1; - -+#ifdef WITH_SELINUX -+ if (x->preserve_security_context && selinux_enabled) -+ { -+ security_context_t con; -+ -+ if (lgetfilecon (src_path, &con) >= 0) -+ { -+ if (setfscreatecon(con) < 0) -+ { -+ freecon(con); -+ error (0, errno, _("cannot set setfscreatecon %s"), quote (con)); -+ return 1; -+ } -+ freecon(con); -+ } -+ else { -+ if ( errno == ENOTSUP ) { -+ error (0, errno, _("warning: security context not preserved %s"), quote (src_path)); -+ } else { -+ error (0, errno, _("cannot lgetfilecon %s"), quote (src_path)); -+ return 1; -+ } -+ } -+ } -+#endif -+ - if (S_ISDIR (src_mode)) - { - struct dir_list *dir; -@@ -1302,8 +1333,13 @@ - } - - /* Are we crossing a file system boundary? */ -- if (x->one_file_system && device != 0 && device != src_sb.st_dev) -+ if (x->one_file_system && device != 0 && device != src_sb.st_dev) { -+#ifdef WITH_SELINUX -+ if (x->preserve_security_context && selinux_enabled) -+ setfscreatecon(NULL); -+#endif - return 0; -+ } - - /* Copy the contents of the directory. */ - -@@ -1442,6 +1478,11 @@ - } - } - -+#ifdef WITH_SELINUX -+ if (x->preserve_security_context && selinux_enabled) -+ setfscreatecon(NULL); -+#endif -+ - /* There's no need to preserve timestamps or permissions. */ - preserve_metadata = 0; - -@@ -1474,7 +1515,7 @@ - if (command_line_arg) - record_file (x->dest_info, dst_path, NULL); - -- if ( ! preserve_metadata) -+ if ( ! preserve_metadata) - return 0; - - /* POSIX says that `cp -p' must restore the following: -@@ -1576,6 +1617,11 @@ - - un_backup: - -+#ifdef WITH_SELINUX -+ if (x->preserve_security_context && selinux_enabled) -+ setfscreatecon(NULL); -+#endif -+ - /* We have failed to create the destination file. - If we've just added a dev/ino entry via the remember_copied - call above (i.e., unless we've just failed to create a hard link), ---- coreutils-5.0/src/copy.h.selinux 2003-12-09 20:44:56.000000000 -0500 -+++ coreutils-5.0/src/copy.h 2003-12-09 20:44:57.000000000 -0500 -@@ -105,6 +105,9 @@ - int preserve_ownership; - int preserve_mode; - int preserve_timestamps; -+#ifdef WITH_SELINUX -+ int preserve_security_context; -+#endif - - /* Enabled for mv, and for cp by the --preserve=links option. - If nonzero, attempt to preserve in the destination files any ---- coreutils-5.0/src/cp.c.selinux 2003-12-09 20:44:56.000000000 -0500 -+++ coreutils-5.0/src/cp.c 2003-12-09 20:44:57.000000000 -0500 -@@ -52,6 +52,11 @@ - - #define AUTHORS N_ ("Torbjorn Granlund, David MacKenzie, and Jim Meyering") - -+#ifdef WITH_SELINUX -+#include /* for is_selinux_enabled() */ -+int selinux_enabled=0; -+#endif -+ - #ifndef _POSIX_VERSION - uid_t geteuid (); - #endif -@@ -149,6 +154,9 @@ - {"update", no_argument, NULL, 'u'}, - {"verbose", no_argument, NULL, 'v'}, - {"version-control", required_argument, NULL, 'V'}, /* Deprecated. FIXME. */ -+#ifdef WITH_SELINUX -+ {"context", required_argument, NULL, 'Z'}, -+#endif - {GETOPT_HELP_OPTION_DECL}, - {GETOPT_VERSION_OPTION_DECL}, - {NULL, 0, NULL, 0} -@@ -198,6 +206,9 @@ - additional attributes: links, all\n\ - "), stdout); - fputs (_("\ -+ -c same as --preserve=context\n\ -+"), stdout); -+ fputs (_("\ - --no-preserve=ATTR_LIST don't preserve the specified attributes\n\ - --parents append source path to DIRECTORY\n\ - -P same as `--no-dereference'\n\ -@@ -225,6 +236,7 @@ - destination file is missing\n\ - -v, --verbose explain what is being done\n\ - -x, --one-file-system stay on this file system\n\ -+ -Z, --context=CONTEXT set security context of copy to CONTEXT\n\ - "), stdout); - fputs (HELP_OPTION_DESCRIPTION, stdout); - fputs (VERSION_OPTION_DESCRIPTION, stdout); -@@ -756,8 +768,8 @@ - { - new_dest = (char *) dest; - } -- -- return copy (source, new_dest, new_dst, x, &unused, NULL); -+ ret=copy (source, new_dest, new_dst, x, &unused, NULL); -+ return ret; - } - - /* unreachable */ -@@ -781,6 +793,10 @@ - x->preserve_mode = 0; - x->preserve_timestamps = 0; - -+#ifdef WITH_SELINUX -+ x->preserve_security_context = 0; -+#endif -+ - x->require_preserve = 0; - x->recursive = 0; - x->sparse_mode = SPARSE_AUTO; -@@ -808,19 +824,20 @@ - PRESERVE_TIMESTAMPS, - PRESERVE_OWNERSHIP, - PRESERVE_LINK, -+ PRESERVE_CONTEXT, - PRESERVE_ALL - }; - static enum File_attribute const preserve_vals[] = - { - PRESERVE_MODE, PRESERVE_TIMESTAMPS, -- PRESERVE_OWNERSHIP, PRESERVE_LINK, PRESERVE_ALL -+ PRESERVE_OWNERSHIP, PRESERVE_LINK, PRESERVE_CONTEXT, PRESERVE_ALL - }; - - /* Valid arguments to the `--preserve' option. */ - static char const* const preserve_args[] = - { - "mode", "timestamps", -- "ownership", "links", "all", 0 -+ "ownership", "links", "context", "all", 0 - }; - - char *arg_writable = xstrdup (arg); -@@ -855,11 +872,16 @@ - x->preserve_links = on_off; - break; - -+ case PRESERVE_CONTEXT: -+ x->preserve_security_context = 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_security_context = on_off; - break; - - default: -@@ -882,6 +904,10 @@ - struct cp_options x; - int copy_contents = 0; - char *target_directory = NULL; -+#ifdef WITH_SELINUX -+ security_context_t scontext = NULL; -+ selinux_enabled= (is_selinux_enabled()>0); -+#endif - - program_name = argv[0]; - setlocale (LC_ALL, ""); -@@ -896,7 +922,11 @@ - we'll actually use backup_suffix_string. */ - backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); - -+#ifdef WITH_SELINUX -+ while ((c = getopt_long (argc, argv, "abcdfHilLprsuvxPRS:V:Z:", long_opts, NULL)) -+#else - while ((c = getopt_long (argc, argv, "abdfHilLprsuvxPRS:V:", long_opts, NULL)) -+#endif - != -1) - { - switch (c) -@@ -988,6 +1018,36 @@ - x.preserve_timestamps = 1; - x.require_preserve = 1; - break; -+#ifdef WITH_SELINUX -+ case 'c': -+ if ( scontext != NULL ) { -+ (void) fprintf(stderr, "%s: cannot force target context <-- %s and preserve it\n", argv[0], scontext); -+ exit( 1 ); -+ } -+ else if (selinux_enabled) -+ x.preserve_security_context = 1; -+ break; -+ -+ case 'Z': -+ /* politely decline if we're not on a selinux-enabled kernel. */ -+ if( !selinux_enabled ) { -+ fprintf( stderr, "Warning: ignoring --context (-Z). " -+ "It requires a SELinux enabled kernel.\n" ); -+ break; -+ } -+ if ( x.preserve_security_context ) { -+ (void) fprintf(stderr, "%s: cannot force target context to '%s' and preserve it\n", argv[0], optarg); -+ exit( 1 ); -+ } -+ scontext = optarg; -+ /* if there's a security_context given set new path -+ components to that context, too */ -+ if ( setfscreatecon(scontext) < 0 ) { -+ (void) fprintf(stderr, _("cannot set default security context %s"), scontext); -+ exit( 1 ); -+ } -+ break; -+#endif - - case PARENTS_OPTION: - flag_path = 1; ---- coreutils-5.0/src/id.c.selinux 2003-03-27 17:39:46.000000000 -0500 -+++ coreutils-5.0/src/id.c 2003-12-09 20:44:57.000000000 -0500 -@@ -46,6 +46,20 @@ - - int getugroups (); - -+#ifdef WITH_SELINUX -+#include -+static void print_context PARAMS ((char* context)); -+/* Print the SELinux context */ -+static void -+print_context(char *context) -+{ -+ printf ("%s", context); -+} -+ -+/* If nonzero, output only the SELinux context. -Z */ -+static int just_context = 0; -+ -+#endif - static void print_user (uid_t uid); - static void print_group (gid_t gid); - static void print_group_list (const char *username); -@@ -64,8 +78,14 @@ - /* The number of errors encountered so far. */ - static int problems = 0; - -+/* The SELinux context */ -+/* Set `context' to a known invalid value so print_full_info() will * -+ * know when `context' has not been set to a meaningful value. */ -+static security_context_t context=NULL; -+ - static struct option const longopts[] = - { -+ {"context", no_argument, NULL, 'Z'}, - {"group", no_argument, NULL, 'g'}, - {"groups", no_argument, NULL, 'G'}, - {"name", no_argument, NULL, 'n'}, -@@ -89,6 +109,7 @@ - Print information for USERNAME, or the current user.\n\ - \n\ - -a ignore, for compatibility with other versions\n\ -+ -Z, --context print only the context\n\ - -g, --group print only the effective group ID\n\ - -G, --groups print all group IDs\n\ - -n, --name print a name instead of a number, for -ugG\n\ -@@ -110,6 +131,7 @@ - main (int argc, char **argv) - { - int optc; -+ int selinux_enabled=(is_selinux_enabled()>0); - - /* If nonzero, output the list of all group IDs. -G */ - int just_group_list = 0; -@@ -127,7 +149,7 @@ - - atexit (close_stdout); - -- while ((optc = getopt_long (argc, argv, "agnruG", longopts, NULL)) != -1) -+ while ((optc = getopt_long (argc, argv, "agnruGZ", longopts, NULL)) != -1) - { - switch (optc) - { -@@ -136,6 +158,17 @@ - case 'a': - /* Ignore -a, for compatibility with SVR4. */ - break; -+#ifdef WITH_SELINUX -+ case 'Z': -+ /* politely decline if we're not on a selinux-enabled kernel. */ -+ if( !selinux_enabled ) { -+ fprintf( stderr, "Sorry, --context (-Z) can be used only on " -+ "a selinux-enabled kernel.\n" ); -+ exit( 1 ); -+ } -+ just_context = 1; -+ break; -+#endif - case 'g': - just_group = 1; - break; -@@ -158,8 +191,28 @@ - } - } - -- if (just_user + just_group + just_group_list > 1) -- error (EXIT_FAILURE, 0, _("cannot print only user and only group")); -+#ifdef WITH_SELINUX -+ if (argc - optind == 1) -+ selinux_enabled = 0; -+ -+ if( just_context && !selinux_enabled) -+ error (1, 0, _("\ -+cannot display context when selinux not enabled or when displaying the id\n\ -+of a different user")); -+ -+ /* If we are on a selinux-enabled kernel, get our context. * -+ * Otherwise, leave the context variable alone - it has * -+ * been initialized known invalid value; if we see this invalid * -+ * value later, we will know we are on a non-selinux kernel. */ -+ if( selinux_enabled ) -+ { -+ if (getcon(&context)) -+ error (1, 0, "can't get process context"); -+ } -+#endif -+ -+ if (just_user + just_group + just_group_list + just_context > 1) -+ error (EXIT_FAILURE, 0, _("cannot print \"only\" of more than one choice")); - - if (just_user + just_group + just_group_list == 0 && (use_real || use_name)) - error (EXIT_FAILURE, 0, -@@ -190,6 +243,10 @@ - print_group (use_real ? rgid : egid); - else if (just_group_list) - print_group_list (argv[optind]); -+#ifdef WITH_SELINUX -+ else if (just_context) -+ print_context (context); -+#endif - else - print_full_info (argv[optind]); - putchar ('\n'); -@@ -397,4 +454,9 @@ - free (groups); - } - #endif /* HAVE_GETGROUPS */ -+#ifdef WITH_SELINUX -+ if ( context != NULL ) { -+ printf(" context=%s",context); -+ } -+#endif - } ---- coreutils-5.0/src/install.c.selinux 2003-12-09 20:44:56.000000000 -0500 -+++ coreutils-5.0/src/install.c 2003-12-09 20:44:57.000000000 -0500 -@@ -50,6 +50,11 @@ - # include - #endif - -+#ifdef WITH_SELINUX -+#include /* for is_selinux_enabled() */ -+int selinux_enabled=0; -+#endif -+ - struct passwd *getpwnam (); - struct group *getgrnam (); - -@@ -126,11 +131,17 @@ - static struct option const long_options[] = - { - {"backup", optional_argument, NULL, 'b'}, -+#ifdef WITH_SELINUX -+ {"context", required_argument, NULL, 'Z'}, -+#endif - {"directory", no_argument, NULL, 'd'}, - {"group", required_argument, NULL, 'g'}, - {"mode", required_argument, NULL, 'm'}, - {"owner", required_argument, NULL, 'o'}, - {"preserve-timestamps", no_argument, NULL, 'p'}, -+#ifdef WITH_SELINUX -+ {"preserve_context", no_argument, NULL, 'P'}, -+#endif - {"strip", no_argument, NULL, 's'}, - {"suffix", required_argument, NULL, 'S'}, - {"version-control", required_argument, NULL, 'V'}, /* Deprecated. FIXME. */ -@@ -247,6 +258,9 @@ - - x->update = 0; - x->verbose = 0; -+#ifdef WITH_SELINUX -+ x->preserve_security_context = 0; -+#endif - x->xstat = stat; - x->dest_info = NULL; - x->src_info = NULL; -@@ -265,6 +279,11 @@ - struct cp_options x; - int n_files; - char **file; -+#ifdef WITH_SELINUX -+ security_context_t scontext = NULL; -+ /* set iff kernel has extra selinux system calls */ -+ selinux_enabled = (is_selinux_enabled()>0); -+#endif - - program_name = argv[0]; - setlocale (LC_ALL, ""); -@@ -285,7 +304,11 @@ - we'll actually use backup_suffix_string. */ - backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); - -+#ifdef WITH_SELINUX -+ while ((optc = getopt_long (argc, argv, "bcCsDdg:m:o:pPvV:S:Z:", long_options, -+#else - while ((optc = getopt_long (argc, argv, "bcCsDdg:m:o:pvV:S:", long_options, -+#endif - NULL)) != -1) - { - switch (optc) -@@ -338,6 +361,39 @@ - make_backups = 1; - backup_suffix_string = optarg; - break; -+#ifdef WITH_SELINUX -+ case 'P': -+ /* politely decline if we're not on a selinux-enabled kernel. */ -+ if( !selinux_enabled ) { -+ fprintf( stderr, "Warning: ignoring --preserve_context (-P) " -+ "because the kernel is not selinux-enabled.\n" ); -+ break; -+ } -+ if ( scontext!=NULL ) { /* scontext could be NULL because of calloc() failure */ -+ (void) fprintf(stderr, "%s: cannot force target context to '%s' and preserve it\n", argv[0], scontext); -+ exit( 1 ); -+ } -+ x.preserve_security_context = 1; -+ break ; -+ case 'Z': -+ /* politely decline if we're not on a selinux-enabled kernel. */ -+ if( !selinux_enabled) { -+ fprintf( stderr, "Warning: ignoring --context (-Z) " -+ "because the kernel is not selinux-enabled.\n" ); -+ break; -+ } -+ if ( x.preserve_security_context ) { -+ -+ (void) fprintf(stderr, "%s: cannot force target context == '%s' and preserve it\n", argv[0], optarg); -+ exit( 1 ); -+ } -+ scontext = optarg; -+ if (setfscreatecon(scontext)) { -+ (void) fprintf(stderr, "%s: cannot setup default context == '%s'\n", argv[0], scontext); -+ exit(1); -+ } -+ break; -+#endif - case_GETOPT_HELP_CHAR; - case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); - default: -@@ -721,6 +777,11 @@ - -S, --suffix=SUFFIX override the usual backup suffix\n\ - -v, --verbose print the name of each directory as it is created\n\ - "), stdout); -+ fputs (_("\ -+ -P, --preserve_context (SELinux) Preserve security context\n\ -+ -Z, --context=CONTEXT (SELinux) Set security context of files and directories\n\ -+"), stdout); -+ - fputs (HELP_OPTION_DESCRIPTION, stdout); - fputs (VERSION_OPTION_DESCRIPTION, stdout); - fputs (_("\ ---- coreutils-5.0/src/ls.c.selinux 2003-12-09 20:44:57.000000000 -0500 -+++ coreutils-5.0/src/ls.c 2003-12-09 20:44:57.000000000 -0500 -@@ -132,6 +132,18 @@ - - #define AUTHORS N_ ("Richard Stallman and David MacKenzie") - -+#ifdef WITH_SELINUX -+#include -+int selinux_enabled= 0; -+static int print_scontext = 0; -+#define check_selinux() if (!selinux_enabled) { \ -+ fprintf( stderr, "Sorry, this option can only be used " \ -+ "on a SELinux kernel.\n" ); \ -+ exit( EXIT_FAILURE ); \ -+} -+ -+#endif -+ - #define obstack_chunk_alloc malloc - #define obstack_chunk_free free - -@@ -209,6 +221,10 @@ - /* For long listings, true if the file has an access control list. */ - bool have_acl; - #endif -+ -+#ifdef WITH_SELINUX -+ security_context_t scontext; -+#endif - }; - - #if HAVE_ACL || USE_ACL -@@ -274,6 +290,9 @@ - static void sort_files (void); - static void parse_ls_color (void); - void usage (int status); -+#ifdef WITH_SELINUX -+static void print_scontext_format PARAMS ((const struct fileinfo *f)); -+#endif - - /* The name the program was run with, stripped of any leading path. */ - char *program_name; -@@ -372,7 +391,10 @@ - one_per_line, /* -1 */ - many_per_line, /* -C */ - horizontal, /* -x */ -- with_commas /* -m */ -+#ifdef WITH_SELINUX -+ security_format, /* -Z */ -+#endif -+ with_commas /* -m */ - }; - - static enum format format; -@@ -697,6 +719,11 @@ - SHOW_CONTROL_CHARS_OPTION, - SI_OPTION, - SORT_OPTION, -+#ifdef WITH_SELINUX -+ CONTEXT_OPTION, -+ LCONTEXT_OPTION, -+ SCONTEXT_OPTION, -+#endif - TIME_OPTION, - TIME_STYLE_OPTION - }; -@@ -740,6 +767,11 @@ - {"time-style", required_argument, 0, TIME_STYLE_OPTION}, - {"color", optional_argument, 0, COLOR_OPTION}, - {"block-size", required_argument, 0, BLOCK_SIZE_OPTION}, -+#ifdef WITH_SELINUX -+ {"context", no_argument, 0, CONTEXT_OPTION}, -+ {"lcontext", no_argument, 0, LCONTEXT_OPTION}, -+ {"scontext", no_argument, 0, SCONTEXT_OPTION}, -+#endif - {"author", no_argument, 0, AUTHOR_OPTION}, - {GETOPT_HELP_OPTION_DECL}, - {GETOPT_VERSION_OPTION_DECL}, -@@ -749,12 +781,19 @@ - static char const *const format_args[] = - { - "verbose", "long", "commas", "horizontal", "across", -- "vertical", "single-column", 0 -+ "vertical", "single-column", -+#ifdef WITH_SELINUX -+ "context", -+#endif -+ 0 - }; - - static enum format const format_types[] = - { - long_format, long_format, with_commas, horizontal, horizontal, -+#ifdef WITH_SELINUX -+ security_format, -+#endif - many_per_line, one_per_line - }; - -@@ -1138,6 +1177,9 @@ - - format_needs_stat = sort_type == sort_time || sort_type == sort_size - || format == long_format -+#ifdef WITH_SELINUX -+ || format == security_format || print_scontext -+#endif - || dereference == DEREF_ALWAYS - || print_block_size || print_inode; - format_needs_type = (format_needs_stat == 0 -@@ -1260,6 +1302,11 @@ - /* Record whether there is an option specifying sort type. */ - int sort_type_specified = 0; - -+#ifdef WITH_SELINUX -+ /* 1 iff kernel has new selinux system calls */ -+ selinux_enabled= (is_selinux_enabled()>0); -+#endif -+ - qmark_funny_chars = 0; - - /* initialize all switches to default settings */ -@@ -1310,6 +1357,9 @@ - all_files = 0; - really_all_files = 0; - ignore_patterns = 0; -+#ifdef WITH_SELINUX -+ print_scontext = 0; -+#endif - - /* FIXME: put this in a function. */ - { -@@ -1387,7 +1437,7 @@ - } - - while ((c = getopt_long (argc, argv, -- "abcdfghiklmnopqrstuvw:xABCDFGHI:LNQRST:UX1", -+ "abcdfghiklmnopqrstuvw:xABCDFGHI:LNQRST:UX1Z", - long_options, NULL)) != -1) - { - switch (c) -@@ -1507,6 +1557,13 @@ - format = horizontal; - break; - -+#ifdef WITH_SELINUX -+ case 'Z': -+ check_selinux(); -+ print_scontext = 1; -+ format = security_format; -+ break; -+#endif - case 'A': - really_all_files = 0; - all_files = 1; -@@ -1676,6 +1733,25 @@ - - case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); - -+#ifdef WITH_SELINUX -+ -+ case CONTEXT_OPTION: /* new security format */ -+ check_selinux(); -+ print_scontext = 1; -+ format = security_format; -+ break; -+ case LCONTEXT_OPTION: /* long format plus security context */ -+ check_selinux(); -+ print_scontext = 1; -+ format = long_format; -+ break; -+ case SCONTEXT_OPTION: /* short form of new security format */ -+ check_selinux(); -+ print_scontext = 0; -+ format = security_format; -+ break; -+#endif -+ - default: - usage (EXIT_FAILURE); - } -@@ -2339,6 +2415,12 @@ - free (files[i].name); - if (files[i].linkname) - free (files[i].linkname); -+#ifdef WITH_SELINUX -+ if (files[i].scontext) { -+ freecon (files[i].scontext); -+ files[i].scontext=NULL; -+ } -+#endif - } - - files_index = 0; -@@ -2375,6 +2457,9 @@ - f->linkname = 0; - f->linkmode = 0; - f->linkok = 0; -+#ifdef WITH_SELINUX -+ f->scontext = NULL; -+#endif - - if (explicit_arg - || format_needs_stat -@@ -2420,6 +2505,11 @@ - { - int need_lstat; - err = stat (path, &f->stat); -+#ifdef WITH_SELINUX -+ if (err>=0) -+ if (selinux_enabled && (format == security_format || print_scontext)) -+ getfilecon(path, &f->scontext); -+#endif - - if (dereference == DEREF_COMMAND_LINE_ARGUMENTS) - break; -@@ -2438,6 +2528,11 @@ - - default: /* DEREF_NEVER */ - err = lstat (path, &f->stat); -+#ifdef WITH_SELINUX -+ if (err>=0) -+ if (selinux_enabled && (format == security_format || print_scontext)) -+ lgetfilecon(path, &f->scontext); -+#endif - break; - } - -@@ -2924,6 +3019,16 @@ - DIRED_PUTCHAR ('\n'); - } - break; -+ -+#ifdef WITH_SELINUX -+ case security_format: -+ for (i = 0; i < files_index; i++) -+ { -+ print_scontext_format (files + i); -+ DIRED_PUTCHAR ('\n'); -+ } -+ break; -+#endif - } - } - -@@ -3147,6 +3252,14 @@ - } - p += sizeof modebuf + nlink_width + 1; - -+#ifdef WITH_SELINUX -+ -+ if ( print_scontext ) { -+ sprintf (p, "%-32s ", f->scontext); -+ p += strlen (p); -+ } -+#endif -+ - DIRED_INDENT (); - - if (print_owner | print_group | print_author) -@@ -4057,6 +4170,16 @@ - -X sort alphabetically by entry extension\n\ - -1 list one file per line\n\ - "), stdout); -+#ifdef WITH_SELINUX -+printf(_("SELINUX options:\n\n\ -+ --lcontext Display security context. Enable -l. Lines\n\ -+ will probably be too wide for most displays.\n\ -+ --context Display security context so it fits on most\n\ -+ displays. Displays only mode, user, group,\n\ -+ security context and file name.\n\ -+ --scontext Display only security context and file name.\n\ -+")); -+#endif - fputs (HELP_OPTION_DESCRIPTION, stdout); - fputs (VERSION_OPTION_DESCRIPTION, stdout); - fputs (_("\n\ -@@ -4075,3 +4198,79 @@ - } - exit (status); - } -+ -+#ifdef WITH_SELINUX -+ -+static void -+print_scontext_format (const struct fileinfo *f) -+{ -+ char modebuf[12]; -+ -+ /* 7 fields that may require LONGEST_HUMAN_READABLE bytes, -+ 1 10-byte mode string, -+ 9 spaces, one following each of these fields, and -+ 1 trailing NUL byte. */ -+ -+ char init_bigbuf[7 * LONGEST_HUMAN_READABLE + 10 + 9 + 1]; -+ char *buf = init_bigbuf; -+ size_t bufsize = sizeof (init_bigbuf); -+ size_t s; -+ char *p; -+ const char *fmt; -+ char *user_name; -+ char *group_name; -+ int rv; -+ char *scontext; -+ -+ p = buf; -+ -+ if ( print_scontext ) { /* zero means terse listing */ -+ mode_string (f->stat.st_mode, modebuf); -+ modebuf[10] = (FILE_HAS_ACL (f) ? '+' : ' '); -+ modebuf[11] = '\0'; -+ -+ /* print mode */ -+ -+ (void) sprintf (p, "%s ", modebuf); -+ p += strlen (p); -+ -+ /* print standard user and group */ -+ -+ user_name = (numeric_ids ? NULL : getuser (f->stat.st_uid)); -+ if (user_name) -+ (void) sprintf (p, "%-8.8s ", user_name); -+ else -+ (void) sprintf (p, "%-8u ", (unsigned int) f->stat.st_uid); -+ p += strlen (p); -+ -+ if ( print_group ) { -+ group_name = (numeric_ids ? NULL : getgroup (f->stat.st_gid)); -+ if (group_name) -+ (void) sprintf (p, "%-8.8s ", group_name); -+ else -+ (void) sprintf (p, "%-8u ", (unsigned int) f->stat.st_gid); -+ p += strlen (p); -+ } -+ } -+ -+ (void) sprintf (p, "%-32s ", f->scontext); -+ p += strlen (p); -+ -+ DIRED_INDENT (); -+ DIRED_FPUTS (buf, stdout, p - buf); -+ print_name_with_quoting (f->name, f->stat.st_mode, f->linkok, &dired_obstack); -+ -+ if (f->filetype == symbolic_link) { -+ if (f->linkname) { -+ DIRED_FPUTS_LITERAL (" -> ", stdout); -+ print_name_with_quoting (f->linkname, f->linkmode, f->linkok - 1, NULL); -+ if (indicator_style != none) -+ print_type_indicator (f->linkmode); -+ } -+ } -+ else { -+ if (indicator_style != none) -+ print_type_indicator (f->stat.st_mode); -+ } -+} -+#endif ---- coreutils-5.0/src/Makefile.am.selinux 2003-12-09 20:44:56.000000000 -0500 -+++ coreutils-5.0/src/Makefile.am 2003-12-09 20:44:57.000000000 -0500 -@@ -4,13 +4,13 @@ - EXTRA_SCRIPTS = nohup - - bin_SCRIPTS = groups @OPTIONAL_BIN_ZCRIPTS@ --bin_PROGRAMS = chgrp chown chmod cp dd dircolors du \ -+bin_PROGRAMS = chgrp chown chmod chcon cp dd dircolors du \ - ginstall link ln dir vdir ls mkdir \ - mkfifo mknod mv readlink rm rmdir shred stat sync touch unlink \ - cat cksum comm csplit cut expand fmt fold head join md5sum \ - nl od paste pr ptx sha1sum sort split sum tac tail tr tsort unexpand uniq wc \ - basename date dirname echo env expr factor false \ -- id kill logname pathchk printenv printf pwd seq sleep tee \ -+ id kill logname pathchk printenv printf pwd runcon seq sleep tee \ - test true tty whoami yes \ - @OPTIONAL_BIN_PROGS@ @DF_PROG@ - -@@ -34,13 +34,21 @@ - # replacement functions defined in libfetish.a. - LDADD = ../lib/libfetish.a @LIBINTL@ ../lib/libfetish.a - --dir_LDADD = $(LDADD) @LIB_CLOCK_GETTIME@ -ltermcap @LIBACL@ --ls_LDADD = $(LDADD) @LIB_CLOCK_GETTIME@ -ltermcap @LIBACL@ -+dir_LDADD = $(LDADD) @LIB_CLOCK_GETTIME@ -ltermcap @LIBACL@ @LIB_SELINUX@ -+ls_LDADD = $(LDADD) @LIB_CLOCK_GETTIME@ -ltermcap @LIBACL@ @LIB_SELINUX@ - shred_LDADD = $(LDADD) @LIB_CLOCK_GETTIME@ --vdir_LDADD = $(LDADD) @LIB_CLOCK_GETTIME@ -ltermcap @LIBACL@ --cp_LDADD = $(LDADD) @LIBACL@ --ginstall_LDADD = $(LDADD) @LIBACL@ --mv_LDADD = $(LDADD) @LIBACL@ -+vdir_LDADD = $(LDADD) @LIB_CLOCK_GETTIME@ -ltermcap @LIBACL@ @LIB_SELINUX@ -+cp_LDADD = $(LDADD) @LIBACL@ @LIB_SELINUX@ -+ginstall_LDADD = $(LDADD) @LIBACL@ @LIB_SELINUX@ -+mv_LDADD = $(LDADD) @LIBACL@ @LIB_SELINUX@ -+chcon_LDADD = $(LDADD) @LIB_SELINUX@ -+id_LDADD = $(LDADD) @LIB_SELINUX@ -+mkdir_LDADD = $(LDADD) @LIB_SELINUX@ -+mkfifo_LDADD = $(LDADD) @LIB_SELINUX@ -+mknod_LDADD = $(LDADD) @LIB_SELINUX@ -+stat_LDADD = $(LDADD) @LIB_SELINUX@ -+runcon_LDADD = $(LDADD) @LIB_SELINUX@ -+ - - ## If necessary, add -lm to resolve use of pow in lib/strtod.c. - sort_LDADD = $(LDADD) @POW_LIB@ ---- coreutils-5.0/src/mkdir.c.selinux 2002-09-23 03:35:27.000000000 -0400 -+++ coreutils-5.0/src/mkdir.c 2003-12-09 20:44:57.000000000 -0500 -@@ -34,6 +34,10 @@ - - #define AUTHORS "David MacKenzie" - -+#ifdef WITH_SELINUX -+#include /* for is_selinux_enabled() */ -+#endif -+ - /* The name this program was run with. */ - char *program_name; - -@@ -42,6 +46,9 @@ - - static struct option const longopts[] = - { -+#ifdef WITH_SELINUX -+ {"context", required_argument, NULL, 'Z'}, -+#endif - {"mode", required_argument, NULL, 'm'}, - {"parents", no_argument, NULL, 'p'}, - {"verbose", no_argument, NULL, 'v'}, -@@ -63,6 +70,11 @@ - Create the DIRECTORY(ies), if they do not already exist.\n\ - \n\ - "), stdout); -+#ifdef WITH_SELINUX -+ printf (_("\ -+ -Z, --context=CONTEXT (SELinux) set security context to CONTEXT\n\ -+")); -+#endif - fputs (_("\ - Mandatory arguments to long options are mandatory for short options too.\n\ - "), stdout); -@@ -97,7 +109,11 @@ - - create_parents = 0; - -+#ifdef WITH_SELINUX -+ while ((optc = getopt_long (argc, argv, "pm:vZ:", longopts, NULL)) != -1) -+#else - while ((optc = getopt_long (argc, argv, "pm:v", longopts, NULL)) != -1) -+#endif - { - switch (optc) - { -@@ -112,6 +128,20 @@ - case 'v': /* --verbose */ - verbose_fmt_string = _("created directory %s"); - break; -+#ifdef WITH_SELINUX -+ case 'Z': -+ /* politely decline if we're not on a selinux-enabled kernel. */ -+ if( !(is_selinux_enabled()>0)) { -+ fprintf( stderr, "Sorry, --context (-Z) can be used only on " -+ "a selinux-enabled kernel.\n" ); -+ exit( 1 ); -+ } -+ if (setfscreatecon(optarg)) { -+ fprintf( stderr, "Sorry, cannot set default context to %s.\n", optarg); -+ exit( 1 ); -+ } -+ break; -+#endif - case_GETOPT_HELP_CHAR; - case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); - default: ---- coreutils-5.0/src/mkfifo.c.selinux 2002-08-31 03:29:21.000000000 -0400 -+++ coreutils-5.0/src/mkfifo.c 2003-12-09 20:44:57.000000000 -0500 -@@ -32,11 +32,18 @@ - - #define AUTHORS "David MacKenzie" - -+#ifdef WITH_SELINUX -+#include /* for is_selinux_enabled() */ -+#endif -+ - /* The name this program was run with. */ - char *program_name; - - static struct option const longopts[] = - { -+#ifdef WITH_SELINUX -+ {"context", required_argument, NULL, 'Z'}, -+#endif - {"mode", required_argument, NULL, 'm'}, - {GETOPT_HELP_OPTION_DECL}, - {GETOPT_VERSION_OPTION_DECL}, -@@ -57,6 +64,11 @@ - Create named pipes (FIFOs) with the given NAMEs.\n\ - \n\ - "), stdout); -+#ifdef WITH_SELINUX -+ printf (_("\ -+ -Z, --context=CONTEXT set security context (quoted string)\n\ -+"), stdout); -+#endif - fputs (_("\ - Mandatory arguments to long options are mandatory for short options too.\n\ - "), stdout); -@@ -92,7 +104,11 @@ - #ifndef S_ISFIFO - error (4, 0, _("fifo files not supported")); - #else -+#ifdef WITH_SELINUX -+ while ((optc = getopt_long (argc, argv, "m:Z:", longopts, NULL)) != -1) -+#else - while ((optc = getopt_long (argc, argv, "m:", longopts, NULL)) != -1) -+#endif - { - switch (optc) - { -@@ -101,6 +117,19 @@ - case 'm': - specified_mode = optarg; - break; -+#ifdef WITH_SELINUX -+ case 'Z': -+ if( !(is_selinux_enabled()>0)) { -+ fprintf( stderr, "Sorry, --context (-Z) can be used only on " -+ "a selinux-enabled kernel.\n" ); -+ exit( 1 ); -+ } -+ if (setfscreatecon(optarg)) { -+ fprintf( stderr, "Sorry, cannot set default context to %s.\n", optarg); -+ exit( 1 ); -+ } -+ break; -+#endif - case_GETOPT_HELP_CHAR; - case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); - default: ---- coreutils-5.0/src/mknod.c.selinux 2002-12-14 09:14:59.000000000 -0500 -+++ coreutils-5.0/src/mknod.c 2003-12-09 20:44:57.000000000 -0500 -@@ -36,8 +36,15 @@ - /* The name this program was run with. */ - char *program_name; - -+#ifdef WITH_SELINUX -+#include -+#endif -+ - static struct option const longopts[] = - { -+#ifdef WITH_SELINUX -+ {"context", required_argument, NULL, 'Z'}, -+#endif - {"mode", required_argument, NULL, 'm'}, - {GETOPT_HELP_OPTION_DECL}, - {GETOPT_VERSION_OPTION_DECL}, -@@ -58,6 +65,11 @@ - Create the special file NAME of the given TYPE.\n\ - \n\ - "), stdout); -+#ifdef WITH_SELINUX -+ fputs(_("\ -+ -Z, --context=CONTEXT set security context (quoted string)\n\ -+"), stdout); -+#endif - fputs (_("\ - Mandatory arguments to long options are mandatory for short options too.\n\ - "), stdout); -@@ -102,7 +114,11 @@ - - specified_mode = NULL; - -+#ifdef WITH_SELINUX -+ while ((optc = getopt_long (argc, argv, "m:Z:", longopts, NULL)) != -1) -+#else - while ((optc = getopt_long (argc, argv, "m:", longopts, NULL)) != -1) -+#endif - { - switch (optc) - { -@@ -111,6 +127,20 @@ - case 'm': - specified_mode = optarg; - break; -+#ifdef WITH_SELINUX -+ case 'Z': -+ /* politely decline if we're not on a selinux-enabled kernel. */ -+ if( !(is_selinux_enabled()>0)) { -+ fprintf( stderr, "Sorry, --context (-Z) can be used only on " -+ "a selinux-enabled kernel.\n" ); -+ exit( 1 ); -+ } -+ if (setfscreatecon(optarg)) { -+ fprintf( stderr, "Sorry, cannot set default context to %s.\n", optarg); -+ exit( 1 ); -+ } -+ break; -+#endif - case_GETOPT_HELP_CHAR; - case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); - default: ---- /dev/null 2003-09-15 09:40:47.000000000 -0400 -+++ coreutils-5.0/src/runcon.c 2003-12-09 20:44:57.000000000 -0500 -@@ -0,0 +1,174 @@ -+/* -+ * runcon [ context | -+ * ( [ -r role ] [-t type] [ -u user ] [ -l levelrange ] ) -+ * command [arg1 [arg2 ...] ] -+ * -+ * attempt to run the specified command with the specified context. -+ * -+ * -r role : use the current context with the specified role -+ * -t type : use the current context with the specified type -+ * -u user : use the current context with the specified user -+ * -l level : use the current context with the specified level range -+ * -+ * Contexts are interpreted as follows: -+ * -+ * Number of MLS -+ * components system? -+ * -+ * 1 - type -+ * 2 - role:type -+ * 3 Y role:type:range -+ * 3 N user:role:type -+ * 4 Y user:role:type:range -+ * 4 N error -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "system.h" -+extern int errno; -+ -+/* The name the program was run with. */ -+char *program_name; -+ -+void -+usage(char *str) -+{ -+ printf(_("Usage: %s [OPTION]... command [args]\n" -+ "Run a program in a different security context.\n\n" -+ " context Complete security context\n" -+ " -t type (for same role as parent)\n" -+ " -u user identity\n" -+ " -r role\n" -+ " -l levelrange\n" -+ " --help display this help and exit\n"), -+ program_name); -+ exit(1); -+} -+ -+int -+main(int argc,char **argv,char **envp ) -+{ -+ char *role = 0; -+ char *range = 0; -+ char *user = 0; -+ char *type = 0; -+ char *context = NULL; -+ security_context_t cur_context = NULL; -+ -+ context_t con; -+ -+ program_name = argv[0]; -+ setlocale (LC_ALL, ""); -+ bindtextdomain (PACKAGE, LOCALEDIR); -+ textdomain (PACKAGE); -+ -+ while (1) { -+ int c; -+ int this_option_optind = optind ? optind : 1; -+ int option_index = 0; -+ static struct option long_options[] = { -+ { "role", 1, 0, 'r' }, -+ { "type", 1, 0, 't' }, -+ { "user", 1, 0, 'u' }, -+ { "range", 1, 0, 'l' }, -+ { "help", 0, 0, '?' }, -+ { 0, 0, 0, 0 } -+ }; -+ c = getopt_long(argc, argv, "s:r:t:u:l:?", long_options, &option_index); -+ if ( c == -1 ) { -+ break; -+ } -+ switch ( c ) { -+ case 'r': -+ if ( role ) { -+ fprintf(stderr,_("multiple roles\n")); -+ exit(1); -+ } -+ role = optarg; -+ break; -+ case 't': -+ if ( type ) { -+ fprintf(stderr,_("multiple types\n")); -+ exit(1); -+ } -+ type = optarg; -+ break; -+ case 'u': -+ if ( user ) { -+ fprintf(stderr,_("multiple users\n")); -+ exit(1); -+ } -+ user = optarg; -+ break; -+ case 'l': -+ if ( range ) { -+ fprintf(stderr,_("multiple levelranges\n")); -+ exit(1); -+ } -+ range = optarg; -+ break; -+ default: -+ fprintf(stderr,_("unrecognised option %c\n"),c); -+ case '?': -+ usage(0); -+ break; -+ } -+ } -+ if ( !(user || role || type || range)) { -+ if ( optind >= argc ) { -+ usage(_("must specify -t, -u, -l, -r, or context")); -+ } -+ context = argv[optind++]; -+ } -+ -+ if ( optind >= argc ) { -+ usage(_("no command found")); -+ } -+ -+ if ( context ) { -+ con = context_new(context); -+ if (!con) { -+ fprintf(stderr,_("%s is not a valid context\n"), context); -+ exit(1); -+ } -+ } -+ else { -+ getcon(&cur_context); -+ con = context_new(cur_context); -+ if (!con) { -+ fprintf(stderr,_("%s is not a valid context\n"), context); -+ exit(1); -+ } -+ if ( user ) { -+ context_user_set(con,user); -+ } -+ if ( type ) { -+ context_type_set(con,type); -+ } -+ if ( range ) { -+ context_range_set(con,range); -+ } -+ if ( role ) { -+ context_role_set(con,role); -+ } -+ } -+ -+ if (setexeccon(context_str(con))!=0) { -+ fprintf(stderr,_("unable to setup security context %s\n"), context_str(con)); -+ exit(1); -+ } -+ if (cur_context!=NULL) -+ freecon(cur_context); -+ -+ if ( execvp(argv[optind],argv+optind) ) { -+ perror("execvp"); -+ exit(1); -+ } -+ return 1; /* can't reach this statement.... */ -+} ---- coreutils-5.0/src/stat.c.selinux 2003-03-22 17:32:02.000000000 -0500 -+++ coreutils-5.0/src/stat.c 2003-12-09 20:44:57.000000000 -0500 -@@ -32,6 +32,13 @@ - # include - #endif - -+#ifdef WITH_SELINUX -+#include -+#define SECURITY_ID_T security_context_t -+#else -+#define SECURITY_ID_T char * -+#endif -+ - /* NetBSD 1.5.2 needs these, for the declaration of struct statfs. */ - #if !HAVE_SYS_STATVFS_H && !HAVE_SYS_VFS_H - # if HAVE_SYS_MOUNT_H && HAVE_SYS_PARAM_H -@@ -93,6 +100,7 @@ - {"dereference", no_argument, 0, 'L'}, - {"format", required_argument, 0, 'c'}, - {"filesystem", no_argument, 0, 'f'}, -+ {"context", no_argument, 0, 'Z'}, - {"terse", no_argument, 0, 't'}, - {GETOPT_HELP_OPTION_DECL}, - {GETOPT_VERSION_OPTION_DECL}, -@@ -332,7 +340,7 @@ - /* print statfs info */ - static void - print_statfs (char *pformat, char m, char const *filename, -- void const *data) -+ void const *data,SECURITY_ID_T scontext) - { - STRUCT_STATVFS const *statfsbuf = data; - -@@ -394,7 +402,10 @@ - strcat (pformat, PRIdMAX); - printf (pformat, (intmax_t) (statfsbuf->f_ffree)); - break; -- -+ case 'C': -+ strcat (pformat, "s"); -+ printf(scontext); -+ break; - default: - strcat (pformat, "c"); - printf (pformat, m); -@@ -404,7 +415,7 @@ - - /* print stat info */ - static void --print_stat (char *pformat, char m, char const *filename, void const *data) -+print_stat (char *pformat, char m, char const *filename, void const *data, SECURITY_ID_T scontext) - { - struct stat *statbuf = (struct stat *) data; - struct passwd *pw_ent; -@@ -537,6 +548,10 @@ - strcat (pformat, "d"); - printf (pformat, (int) statbuf->st_ctime); - break; -+ case 'C': -+ strcat (pformat, "s"); -+ printf(pformat,scontext); -+ break; - default: - strcat (pformat, "c"); - printf (pformat, m); -@@ -546,8 +561,8 @@ - - static void - print_it (char const *masterformat, char const *filename, -- void (*print_func) (char *, char, char const *, void const *), -- void const *data) -+ void (*print_func) (char *, char, char const *, void const *,SECURITY_ID_T ), -+ void const *data, SECURITY_ID_T scontext) - { - char *b; - -@@ -580,7 +595,7 @@ - putchar ('%'); - break; - default: -- print_func (dest, *p, filename, data); -+ print_func (dest, *p, filename, data,scontext); - break; - } - b = p + 1; -@@ -598,9 +613,17 @@ - - /* stat the filesystem and print what we find */ - static void --do_statfs (char const *filename, int terse, char const *format) -+do_statfs (char const *filename, int terse, int secure, char const *format) - { - STRUCT_STATVFS statfsbuf; -+ SECURITY_ID_T scontext = NULL; -+#ifdef WITH_SELINUX -+ if(secure) -+ if (getfilecon(filename,&scontext)<0) { -+ perror (filename); -+ return; -+ } -+#endif - int i = statfs (filename, &statfsbuf); - - if (i == -1) -@@ -612,23 +635,40 @@ - - if (format == NULL) - { -- format = (terse -- ? "%n %i %l %t %b %f %a %s %c %d" -- : " File: \"%n\"\n" -- " ID: %-8i Namelen: %-7l Type: %T\n" -- "Blocks: Total: %-10b Free: %-10f Available: %-10a Size: %s\n" -- "Inodes: Total: %-10c Free: %-10d"); -- } -- -- print_it (format, filename, print_statfs, &statfsbuf); -+ if (terse) { -+ if(secure) -+ format = "%n %i %l %t %b %f %a %s %c %d %C"; -+ else -+ format = "%n %i %l %t %b %f %a %s %c %d"; -+ } -+ else -+ { -+ if(secure) -+ format = " File: \"%n\"\n" -+ " ID: %-8i Namelen: %-7l Type: %T\n" -+ "Blocks: Total: %-10b Free: %-10f Available: %-10a Size: %s\n" -+ "Inodes: Total: %-10c Free: %-10d\n" -+ " S_Context: %C\n"; -+ else -+ format= " File: \"%n\"\n" -+ " ID: %-8i Namelen: %-7l Type: %T\n" -+ "Blocks: Total: %-10b Free: %-10f Available: %-10a Size: %s\n" -+ "Inodes: Total: %-10c Free: %-10d"; -+ } -+ } -+ print_it (format, filename, print_statfs, &statfsbuf,scontext); -+#ifdef WITH_SELINUX -+ if (scontext != NULL) -+ freecon(scontext); -+#endif - } -- - /* stat the file and print what we find */ - static void --do_stat (char const *filename, int follow_links, int terse, -+ do_stat (char const *filename, int follow_links, int terse,int secure, - char const *format) - { - struct stat statbuf; -+ SECURITY_ID_T scontext = NULL; - int i = ((follow_links == 1) - ? stat (filename, &statbuf) - : lstat (filename, &statbuf)); -@@ -639,11 +679,28 @@ - return; - } - -+#ifdef WITH_SELINUX -+ if(secure) { -+ if (link) -+ i=lgetfilecon(filename, &scontext); -+ else -+ i=getfilecon(filename, &scontext); -+ if (i == -1) -+ { -+ perror (filename); -+ return; -+ } -+ } -+#endif -+ - if (format == NULL) - { - if (terse != 0) - { -- format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o"; -+ if (secure) -+ format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o %C"; -+ else -+ format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o"; - } - else - { -@@ -651,7 +708,17 @@ - i = statbuf.st_mode & S_IFMT; - if (i == S_IFCHR || i == S_IFBLK) - { -- format = -+ if (secure) -+ format = -+ " File: %N\n" -+ " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" -+ "Device: %Dh/%dd\tInode: %-10i Links: %-5h" -+ " Device type: %t,%T\n" -+ "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" -+ " S_Context: %C\n" -+ "Access: %x\n" "Modify: %y\n" "Change: %z\n"; -+ else -+ format = - " File: %N\n" - " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" - "Device: %Dh/%dd\tInode: %-10i Links: %-5h" -@@ -661,6 +728,15 @@ - } - else - { -+ if (secure) -+ format = -+ " File: %N\n" -+ " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" -+ "Device: %Dh/%dd\tInode: %-10i Links: %-5h\n" -+ "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" -+ "S_Context: %C\n" -+ "Access: %x\n" "Modify: %y\n" "Change: %z\n"; -+ else - format = - " File: %N\n" - " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" -@@ -670,7 +746,11 @@ - } - } - } -- print_it (format, filename, print_stat, &statbuf); -+ print_it (format, filename, print_stat, &statbuf,scontext); -+#ifdef WITH_SELINUX -+ if (scontext) -+ freecon(scontext); -+#endif - } - - void -@@ -688,6 +768,7 @@ - -f, --filesystem display filesystem status instead of file status\n\ - -c --format=FORMAT use the specified FORMAT instead of the default\n\ - -L, --dereference follow links\n\ -+ -Z, --context print the security context \n\ - -t, --terse print the information in terse form\n\ - "), stdout); - fputs (HELP_OPTION_DESCRIPTION, stdout); -@@ -739,6 +820,7 @@ - %c Total file nodes in file system\n\ - %d Free file nodes in file system\n\ - %f Free blocks in file system\n\ -+ %C - Security context in SELinux\n\ - "), stdout); - fputs (_("\ - %i File System id in hex\n\ -@@ -761,6 +843,7 @@ - int follow_links = 0; - int fs = 0; - int terse = 0; -+ int secure = 0; - char *format = NULL; - - program_name = argv[0]; -@@ -770,7 +853,7 @@ - - atexit (close_stdout); - -- while ((c = getopt_long (argc, argv, "c:fLlt", long_options, NULL)) != -1) -+ while ((c = getopt_long (argc, argv, "c:fLltZ", long_options, NULL)) != -1) - { - switch (c) - { -@@ -787,6 +870,14 @@ - case 't': - terse = 1; - break; -+ case 'Z': -+ if((is_selinux_enabled()>0)) -+ secure = 1; -+ else { -+ error (0, 0, _("Kernel is not SELinux enabled")); -+ usage (EXIT_FAILURE); -+ } -+ break; - - case_GETOPT_HELP_CHAR; - -@@ -806,9 +897,9 @@ - for (i = optind; i < argc; i++) - { - if (fs == 0) -- do_stat (argv[i], follow_links, terse, format); -+ do_stat (argv[i], follow_links, terse, secure, format); - else -- do_statfs (argv[i], terse, format); -+ do_statfs (argv[i], terse, secure, format); - } - - exit (G_fail ? EXIT_FAILURE : EXIT_SUCCESS); ---- coreutils-5.0/src/mv.c.selinux 2003-12-09 20:44:56.000000000 -0500 -+++ coreutils-5.0/src/mv.c 2003-12-09 20:44:57.000000000 -0500 -@@ -38,6 +38,11 @@ - #include "quote.h" - #include "remove.h" - -+#ifdef WITH_SELINUX -+#include /* for is_selinux_enabled() */ -+int selinux_enabled=0; -+#endif -+ - /* The official name of this program (e.g., no `g' prefix). */ - #define PROGRAM_NAME "mv" - -@@ -381,6 +386,10 @@ - - cp_option_init (&x); - -+#ifdef WITH_SELINUX -+ selinux_enabled= (is_selinux_enabled()>0); -+#endif -+ - /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless - we'll actually use backup_suffix_string. */ - backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); ---- /dev/null 2003-09-15 09:40:47.000000000 -0400 -+++ coreutils-5.0/man/chcon.x 2003-12-09 20:44:57.000000000 -0500 -@@ -0,0 +1,4 @@ -+[NAME] -+chcon \- change file security context -+[DESCRIPTION] -+.\" Add any additional description here ---- coreutils-5.0/man/Makefile.am.selinux 2003-12-09 20:44:56.000000000 -0500 -+++ coreutils-5.0/man/Makefile.am 2003-12-09 20:44:57.000000000 -0500 -@@ -9,7 +9,7 @@ - rm.1 rmdir.1 seq.1 sha1sum.1 shred.1 sleep.1 sort.1 split.1 stat.1 stty.1 \ - su.1 sum.1 sync.1 tac.1 tail.1 tee.1 test.1 touch.1 tr.1 true.1 tsort.1 \ - tty.1 uname.1 unexpand.1 uniq.1 unlink.1 uptime.1 users.1 vdir.1 wc.1 \ -- who.1 whoami.1 yes.1 -+ who.1 whoami.1 yes.1 chcon.1 runcon.1 - - man_aux = $(dist_man_MANS:.1=.x) - -@@ -109,6 +109,8 @@ - who.1: $(common_dep) $(srcdir)/who.x ../src/who.c - whoami.1: $(common_dep) $(srcdir)/whoami.x ../src/whoami.c - yes.1: $(common_dep) $(srcdir)/yes.x ../src/yes.c -+chcon.1: $(common_dep) $(srcdir)/chcon.x ../src/chcon.c -+runcon.1: $(common_dep) $(srcdir)/runcon.x ../src/runcon.c - - SUFFIXES = .x .1 - ---- coreutils-5.0/man/Makefile.in.selinux 2003-04-02 09:28:42.000000000 -0500 -+++ coreutils-5.0/man/Makefile.in 2003-12-09 20:44:57.000000000 -0500 -@@ -1,4 +1,4 @@ --# Makefile.in generated by automake 1.7.3 from Makefile.am. -+# Makefile.in generated by automake 1.7.7 from Makefile.am. - # @configure_input@ - - # Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 -@@ -72,6 +72,7 @@ + gl_DEFAULT_POSIX2_VERSION + gl_USE_SYSTEM_EXTENSIONS + jm_PERL +--- coreutils-5.2.1/man/vdir.1.selinux 2004-03-02 17:52:33.000000000 -0500 ++++ coreutils-5.2.1/man/vdir.1 2004-03-16 14:25:05.596514720 -0500 +@@ -195,6 +195,20 @@ + .TP + \fB\-1\fR + list one file per line ++.PP ++SELINUX options: ++.TP ++\fB\-\-lcontext\fR ++Display security context. Enable \fB\-l\fR. Lines ++will probably be too wide for most displays. ++.TP ++\fB\-\-context\fR ++Display security context so it fits on most ++displays. Displays only mode, user, group, ++security context and file name. ++.TP ++\fB\-\-scontext\fR ++Display only security context and file name. + .TP + \fB\-\-help\fR + display this help and exit +--- coreutils-5.2.1/man/cp.1.selinux 2004-03-02 17:51:05.000000000 -0500 ++++ coreutils-5.2.1/man/cp.1 2004-03-16 14:25:05.598514416 -0500 +@@ -57,7 +57,7 @@ + .TP + \fB\-\-preserve\fR[=\fIATTR_LIST\fR] + preserve the specified attributes (default: +-mode,ownership,timestamps), if possible ++mode,ownership,timestamps) and security contexts, if possible + additional attributes: links, all + .TP + \fB\-\-no\-preserve\fR=\fIATTR_LIST\fR +@@ -109,6 +109,9 @@ + \fB\-\-help\fR + display this help and exit + .TP ++\fB\-Z\fR, \fB\-\-context\fR=\fICONTEXT\fR ++set security context of copy to CONTEXT ++.TP + \fB\-\-version\fR + output version information and exit + .PP +--- coreutils-5.2.1/man/mkdir.1.selinux 2004-03-02 17:52:28.000000000 -0500 ++++ coreutils-5.2.1/man/mkdir.1 2004-03-16 14:25:05.604513504 -0500 +@@ -12,6 +12,8 @@ + .PP + Mandatory arguments to long options are mandatory for short options too. + .TP ++\fB\-Z\fR, \fB\-\-context\fR=\fICONTEXT\fR (SELinux) set security context to CONTEXT ++.TP + \fB\-m\fR, \fB\-\-mode\fR=\fIMODE\fR + set permission mode (as in chmod), not rwxrwxrwx - umask + .TP +--- coreutils-5.2.1/man/ls.1.selinux 2004-03-02 17:52:28.000000000 -0500 ++++ coreutils-5.2.1/man/ls.1 2004-03-16 14:25:05.606513200 -0500 +@@ -195,6 +195,20 @@ + .TP + \fB\-1\fR + list one file per line ++.PP ++SELinux options: ++.TP ++\fB\-\-lcontext\fR ++Display security context. Enable \fB\-l\fR. Lines ++will probably be too wide for most displays. ++.TP ++\fB\-Z\fR, \fB\-\-context\fR ++Display security context so it fits on most ++displays. Displays only mode, user, group, ++security context and file name. ++.TP ++\fB\-\-scontext\fR ++Display only security context and file name. + .TP + \fB\-\-help\fR + display this help and exit +--- coreutils-5.2.1/man/mkfifo.1.selinux 2004-03-02 17:52:28.000000000 -0500 ++++ coreutils-5.2.1/man/mkfifo.1 2004-03-16 14:25:05.609512744 -0500 +@@ -12,6 +12,9 @@ + .PP + Mandatory arguments to long options are mandatory for short options too. + .TP ++\fB\-Z\fR, \fB\-\-context\fR=\fICONTEXT\fR ++set security context (quoted string) ++.TP + \fB\-m\fR, \fB\-\-mode\fR=\fIMODE\fR + set permission mode (as in chmod), not a=rw - umask + .TP +--- coreutils-5.2.1/man/Makefile.in.selinux 2004-03-11 03:58:00.000000000 -0500 ++++ coreutils-5.2.1/man/Makefile.in 2004-03-16 14:25:05.612512288 -0500 +@@ -185,6 +185,7 @@ INTLLIBS = @INTLLIBS@ KMEM_GROUP = @KMEM_GROUP@ LDFLAGS = @LDFLAGS@ @@ -2075,7 +114,7 @@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBOBJS = @LIBOBJS@ -@@ -79,6 +80,8 @@ +@@ -192,6 +193,8 @@ LIB_CLOCK_GETTIME = @LIB_CLOCK_GETTIME@ LIB_CRYPT = @LIB_CRYPT@ LIB_NANOSLEEP = @LIB_NANOSLEEP@ @@ -2084,131 +123,100 @@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ -@@ -152,13 +155,13 @@ - basename.1 cat.1 chgrp.1 chmod.1 chown.1 chroot.1 cksum.1 comm.1 \ - cp.1 csplit.1 cut.1 date.1 dd.1 df.1 dir.1 dircolors.1 dirname.1 du.1 \ - echo.1 env.1 expand.1 expr.1 factor.1 false.1 fmt.1 fold.1 groups.1 \ -- head.1 hostid.1 hostname.1 id.1 install.1 join.1 link.1 ln.1 logname.1 \ -+ head.1 hostid.1 id.1 install.1 join.1 link.1 ln.1 logname.1 \ - ls.1 md5sum.1 mkdir.1 mkfifo.1 mknod.1 mv.1 nice.1 nl.1 nohup.1 od.1 \ - paste.1 pathchk.1 pinky.1 pr.1 printenv.1 printf.1 ptx.1 pwd.1 readlink.1 \ +@@ -273,7 +276,7 @@ rm.1 rmdir.1 seq.1 sha1sum.1 shred.1 sleep.1 sort.1 split.1 stat.1 stty.1 \ su.1 sum.1 sync.1 tac.1 tail.1 tee.1 test.1 touch.1 tr.1 true.1 tsort.1 \ tty.1 uname.1 unexpand.1 uniq.1 unlink.1 uptime.1 users.1 vdir.1 wc.1 \ - who.1 whoami.1 yes.1 + who.1 whoami.1 yes.1 chcon.1 runcon.1 - man_aux = $(dist_man_MANS:.1=.x) -@@ -184,7 +187,7 @@ + EXTRA_DIST = $(man_aux) help2man +@@ -595,6 +598,8 @@ + who.1: $(common_dep) $(srcdir)/who.x ../src/who.c + whoami.1: $(common_dep) $(srcdir)/whoami.x ../src/whoami.c + yes.1: $(common_dep) $(srcdir)/yes.x ../src/yes.c ++chcon.1: $(common_dep) $(srcdir)/chcon.x ../src/chcon.c ++runcon.1: $(common_dep) $(srcdir)/runcon.x ../src/runcon.c - NROFF = nroff - MANS = $(dist_man_MANS) --DIST_COMMON = $(dist_man_MANS) Makefile.am Makefile.in -+DIST_COMMON = $(dist_man_MANS) $(srcdir)/Makefile.in Makefile.am - all: all-am + # Note the use of $t/$*, rather than just `$*' as in other packages. + # That is necessary to avoid failures for programs that are also shell built-in +--- coreutils-5.2.1/man/Makefile.am.selinux 2004-01-23 10:54:23.000000000 -0500 ++++ coreutils-5.2.1/man/Makefile.am 2004-03-16 14:25:05.614511984 -0500 +@@ -10,7 +10,7 @@ + rm.1 rmdir.1 seq.1 sha1sum.1 shred.1 sleep.1 sort.1 split.1 stat.1 stty.1 \ + su.1 sum.1 sync.1 tac.1 tail.1 tee.1 test.1 touch.1 tr.1 true.1 tsort.1 \ + tty.1 uname.1 unexpand.1 uniq.1 unlink.1 uptime.1 users.1 vdir.1 wc.1 \ +- who.1 whoami.1 yes.1 ++ who.1 whoami.1 yes.1 chcon.1 runcon.1 - .SUFFIXES: -@@ -287,7 +290,6 @@ + man_aux = $(dist_man_MANS:.1=.x) - installdirs: - $(mkinstalldirs) $(DESTDIR)$(man1dir) -- - install: install-am - install-exec: install-exec-am - install-data: install-data-am -@@ -307,7 +309,7 @@ - clean-generic: - - distclean-generic: -- -rm -f Makefile $(CONFIG_CLEAN_FILES) -+ -rm -f $(CONFIG_CLEAN_FILES) - - maintainer-clean-generic: - @echo "This command is intended for maintainers to use" -@@ -318,6 +320,7 @@ - clean-am: clean-generic mostlyclean-am - - distclean: distclean-am -+ -rm -f Makefile - - distclean-am: clean-am distclean-generic - -@@ -340,6 +343,7 @@ - installcheck-am: - - maintainer-clean: maintainer-clean-am -+ -rm -f Makefile - - maintainer-clean-am: distclean-am maintainer-clean-generic - -@@ -401,7 +405,6 @@ - groups.1: $(common_dep) $(srcdir)/groups.x ../src/groups.sh - head.1: $(common_dep) $(srcdir)/head.x ../src/head.c - hostid.1: $(common_dep) $(srcdir)/hostid.x ../src/hostid.c --hostname.1: $(common_dep) $(srcdir)/hostname.x ../src/hostname.c - id.1: $(common_dep) $(srcdir)/id.x ../src/id.c - install.1: $(common_dep) $(srcdir)/install.x ../src/install.c - join.1: $(common_dep) $(srcdir)/join.x ../src/join.c -@@ -460,6 +463,8 @@ +@@ -112,6 +112,8 @@ who.1: $(common_dep) $(srcdir)/who.x ../src/who.c whoami.1: $(common_dep) $(srcdir)/whoami.x ../src/whoami.c yes.1: $(common_dep) $(srcdir)/yes.x ../src/yes.c +chcon.1: $(common_dep) $(srcdir)/chcon.x ../src/chcon.c +runcon.1: $(common_dep) $(srcdir)/runcon.x ../src/runcon.c - # Note the use of $t/$*, rather than just `$*' as in other packages. - # That is necessary to avoid failures for programs that are also shell built-in ---- /dev/null 2003-09-15 09:40:47.000000000 -0400 -+++ coreutils-5.0/man/runcon.x 2003-12-09 20:44:57.000000000 -0500 -@@ -0,0 +1,2 @@ -+[DESCRIPTION] -+.\" Add any additional description here ---- coreutils-5.0/man/stat.1.selinux 2003-03-30 07:13:41.000000000 -0500 -+++ coreutils-5.0/man/stat.1 2003-12-09 20:44:57.000000000 -0500 -@@ -22,6 +22,9 @@ - \fB\-t\fR, \fB\-\-terse\fR - print the information in terse form + SUFFIXES = .x .1 + +--- coreutils-5.2.1/man/mknod.1.selinux 2004-03-02 17:52:28.000000000 -0500 ++++ coreutils-5.2.1/man/mknod.1 2004-03-16 14:25:05.617511528 -0500 +@@ -12,6 +12,9 @@ + .PP + Mandatory arguments to long options are mandatory for short options too. .TP -+\fB\-Z\fR, \fB\-\-context\fR -+print security context information for SELinux if available. ++\fB\-Z\fR, \fB\-\-context\fR=\fICONTEXT\fR ++set security context (quoted string) +.TP - \fB\-\-help\fR - display this help and exit - .TP -@@ -42,6 +45,9 @@ - %b - Number of blocks allocated (see %B) + \fB\-m\fR, \fB\-\-mode\fR=\fIMODE\fR + set permission mode (as in chmod), not a=rw - umask .TP -+%C -+SELinux security context +--- /dev/null 2004-02-23 16:02:56.000000000 -0500 ++++ coreutils-5.2.1/man/runcon.1 2004-03-16 14:25:05.619511224 -0500 +@@ -0,0 +1,39 @@ ++.TH RUNCON "1" "July 2003" "runcon (coreutils) 5.0" "selinux" ++.SH NAME ++runcon \- run command with specified security context ++.SH SYNOPSIS ++.B runcon ++[\fI-t TYPE\fR] [\fI-l LEVEL\fR] [\fI-u USER\fR] [\fI-r ROLE\fR] \fICOMMAND\fR [\fIARGS...\fR] ++.PP ++or ++.PP ++.B runcon ++\fICONTEXT\fR \fICOMMAND\fR [\fIargs...\fR] ++.PP ++.br ++.SH DESCRIPTION ++.PP ++.\" Add any additional description here ++.PP ++Run COMMAND with current security context modified by one or more of LEVEL, ++ROLE, TYPE, and USER, or with completely-specified CONTEXT. +.TP - %D - Device number in hex - .TP ---- coreutils-5.0/man/cp.1.selinux 2003-03-30 07:13:35.000000000 -0500 -+++ coreutils-5.0/man/cp.1 2003-12-09 20:44:57.000000000 -0500 -@@ -57,7 +57,7 @@ - .TP - \fB\-\-preserve\fR[=\fIATTR_LIST\fR] - preserve the specified attributes (default: --mode,ownership,timestamps), if possible -+mode,ownership,timestamps) and security contexts, if possible - additional attributes: links, all - .TP - \fB\-\-no\-preserve\fR=\fIATTR_LIST\fR -@@ -109,6 +109,9 @@ - \fB\-\-help\fR - display this help and exit - .TP -+\fB\-Z\fR, \fB\-\-context\fR=\fICONTEXT\fR -+set security context of copy to CONTEXT ++\fB\-t\fR ++change current type to the specified type +.TP - \fB\-\-version\fR - output version information and exit - .PP ---- /dev/null 2003-09-15 09:40:47.000000000 -0400 -+++ coreutils-5.0/man/chcon.1 2003-12-12 13:07:12.023157635 -0500 ++\fB\-l\fR ++change current level range to the specified range ++.TP ++\fB\-r\fR ++change current role to the specified role ++.TP ++\fB\-u\fR ++change current user to the specified user ++.PP ++If none of \fI-t\fR, \fI-u\fR, \fI-r\fR, or \fI-l\fR, is specified, ++the first argument is used as the complete context. Any additional ++arguments after \fICOMMAND\fR are interpreted as arguments to the ++command. ++.PP ++Note that only carefully-chosen contexts are likely to successfully ++run. +--- /dev/null 2004-02-23 16:02:56.000000000 -0500 ++++ coreutils-5.2.1/man/chcon.1 2004-03-16 14:25:05.622510768 -0500 @@ -0,0 +1,64 @@ +.TH CHCON 1 "July 2003" "chcon (coreutils) 5.0" "User Commands" +.SH NAME @@ -2252,92 +260,30 @@ +\fB\-u\fR, \fB\-\-user\fR +set user USER in the target security context +.TP -+\fB\-v\fR, \fB\-\-verbose\fR -+output a diagnostic for every file processed -+.TP -+\fB\-\-help\fR -+display this help and exit -+.TP -+\fB\-\-version\fR -+output version information and exit -+.SH "REPORTING BUGS" -+Report bugs to . -+.SH "SEE ALSO" -+The full documentation for -+.B chcon -+is maintained as a Texinfo manual. If the -+.B info -+and -+.B chcon -+programs are properly installed at your site, the command -+.IP -+.B info chcon -+.PP -+should give you access to the complete manual. ---- coreutils-5.0/man/ls.1.selinux 2003-03-30 07:13:38.000000000 -0500 -+++ coreutils-5.0/man/ls.1 2003-12-09 20:44:57.000000000 -0500 -@@ -1,5 +1,5 @@ --.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.29. --.TH LS "1" "March 2003" "ls (coreutils) 5.0" "User Commands" -+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.022. -+.TH LS "1" "September 2003" "ls (coreutils) 5.0" FSF - .SH NAME - ls \- list directory contents - .SH SYNOPSIS -@@ -195,6 +195,20 @@ - .TP - \fB\-1\fR - list one file per line -+.PP -+SELinux options: -+.TP -+\fB\-\-lcontext\fR -+Display security context. Enable \fB\-l\fR. Lines -+will probably be too wide for most displays. -+.TP -+\fB\-Z\fR, \fB\-\-context\fR -+Display security context so it fits on most -+displays. Displays only mode, user, group, -+security context and file name. -+.TP -+\fB\-\-scontext\fR -+Display only security context and file name. - .TP - \fB\-\-help\fR - display this help and exit ---- coreutils-5.0/man/dir.1.selinux 2003-03-30 07:13:36.000000000 -0500 -+++ coreutils-5.0/man/dir.1 2003-12-09 20:44:57.000000000 -0500 -@@ -1,5 +1,5 @@ --.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.29. --.TH DIR "1" "March 2003" "dir (coreutils) 5.0" "User Commands" -+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.022. -+.TH DIR "1" "September 2003" "dir (coreutils) 5.0" FSF - .SH NAME - dir \- list directory contents - .SH SYNOPSIS -@@ -195,6 +195,20 @@ - .TP - \fB\-1\fR - list one file per line -+.PP -+SELINUX options: -+.TP -+\fB\-\-lcontext\fR -+Display security context. Enable \fB\-l\fR. Lines -+will probably be too wide for most displays. ++\fB\-v\fR, \fB\-\-verbose\fR ++output a diagnostic for every file processed +.TP -+\fB\-\-context\fR -+Display security context so it fits on most -+displays. Displays only mode, user, group, -+security context and file name. ++\fB\-\-help\fR ++display this help and exit +.TP -+\fB\-\-scontext\fR -+Display only security context and file name. - .TP - \fB\-\-help\fR - display this help and exit ---- coreutils-5.0/man/id.1.selinux 2003-03-30 07:13:37.000000000 -0500 -+++ coreutils-5.0/man/id.1 2003-12-09 20:44:57.000000000 -0500 ++\fB\-\-version\fR ++output version information and exit ++.SH "REPORTING BUGS" ++Report bugs to . ++.SH "SEE ALSO" ++The full documentation for ++.B chcon ++is maintained as a Texinfo manual. If the ++.B info ++and ++.B chcon ++programs are properly installed at your site, the command ++.IP ++.B info chcon ++.PP ++should give you access to the complete manual. +--- coreutils-5.2.1/man/id.1.selinux 2004-03-02 17:52:27.000000000 -0500 ++++ coreutils-5.2.1/man/id.1 2004-03-16 14:25:05.624510464 -0500 @@ -13,6 +13,9 @@ \fB\-a\fR ignore, for compatibility with other versions @@ -2348,16 +294,37 @@ \fB\-g\fR, \fB\-\-group\fR print only the effective group ID .TP ---- coreutils-5.0/man/vdir.1.selinux 2003-03-30 07:13:43.000000000 -0500 -+++ coreutils-5.0/man/vdir.1 2003-12-09 20:44:57.000000000 -0500 -@@ -1,5 +1,5 @@ --.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.29. --.TH VDIR "1" "March 2003" "vdir (coreutils) 5.0" "User Commands" -+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.022. -+.TH VDIR "1" "September 2003" "vdir (coreutils) 5.0" FSF - .SH NAME - vdir \- list directory contents - .SH SYNOPSIS +--- coreutils-5.2.1/man/stat.1.selinux 2004-03-02 17:52:31.000000000 -0500 ++++ coreutils-5.2.1/man/stat.1 2004-03-16 14:25:05.627510008 -0500 +@@ -22,6 +22,9 @@ + \fB\-t\fR, \fB\-\-terse\fR + print the information in terse form + .TP ++\fB\-Z\fR, \fB\-\-context\fR ++print security context information for SELinux if available. ++.TP + \fB\-\-help\fR + display this help and exit + .TP +@@ -42,6 +45,9 @@ + %b + Number of blocks allocated (see %B) + .TP ++%C ++SELinux security context ++.TP + %D + Device number in hex + .TP +--- /dev/null 2004-02-23 16:02:56.000000000 -0500 ++++ coreutils-5.2.1/man/chcon.x 2004-03-16 14:25:05.629509704 -0500 +@@ -0,0 +1,4 @@ ++[NAME] ++chcon \- change file security context ++[DESCRIPTION] ++.\" Add any additional description here +--- coreutils-5.2.1/man/dir.1.selinux 2004-03-02 17:51:06.000000000 -0500 ++++ coreutils-5.2.1/man/dir.1 2004-03-16 14:25:05.632509248 -0500 @@ -195,6 +195,20 @@ .TP \fB\-1\fR @@ -2379,16 +346,13 @@ .TP \fB\-\-help\fR display this help and exit ---- coreutils-5.0/man/install.1.selinux 2003-12-09 20:44:54.000000000 -0500 -+++ coreutils-5.0/man/install.1 2003-12-09 20:44:57.000000000 -0500 -@@ -1,5 +1,5 @@ --.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.29. --.TH INSTALL "1" "March 2003" "install (coreutils) 5.0" "User Commands" -+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.022. -+.TH INSTALL "1" "September 2003" "install (coreutils) 5.0" FSF - .SH NAME - ginstall \- copy files and set attributes - .SH SYNOPSIS +--- /dev/null 2004-02-23 16:02:56.000000000 -0500 ++++ coreutils-5.2.1/man/runcon.x 2004-03-16 14:25:05.634508944 -0500 +@@ -0,0 +1,2 @@ ++[DESCRIPTION] ++.\" Add any additional description here +--- coreutils-5.2.1/man/install.1.selinux 2004-03-16 14:25:05.238569136 -0500 ++++ coreutils-5.2.1/man/install.1 2004-03-16 14:25:05.636508640 -0500 @@ -60,6 +60,11 @@ .TP \fB\-v\fR, \fB\-\-verbose\fR @@ -2401,152 +365,2104 @@ .TP \fB\-\-help\fR display this help and exit ---- coreutils-5.0/man/mkdir.1.selinux 2003-03-30 07:13:38.000000000 -0500 -+++ coreutils-5.0/man/mkdir.1 2003-12-09 20:44:57.000000000 -0500 -@@ -12,6 +12,8 @@ - .PP - Mandatory arguments to long options are mandatory for short options too. - .TP -+\fB\-Z\fR, \fB\-\-context\fR=\fICONTEXT\fR (SELinux) set security context to CONTEXT -+.TP - \fB\-m\fR, \fB\-\-mode\fR=\fIMODE\fR - set permission mode (as in chmod), not rwxrwxrwx - umask - .TP ---- coreutils-5.0/man/mkfifo.1.selinux 2003-03-30 07:13:38.000000000 -0500 -+++ coreutils-5.0/man/mkfifo.1 2003-12-09 20:44:57.000000000 -0500 -@@ -12,6 +12,9 @@ - .PP - Mandatory arguments to long options are mandatory for short options too. - .TP -+\fB\-Z\fR, \fB\-\-context\fR=\fICONTEXT\fR -+set security context (quoted string) -+.TP - \fB\-m\fR, \fB\-\-mode\fR=\fIMODE\fR - set permission mode (as in chmod), not a=rw - umask - .TP ---- coreutils-5.0/man/mknod.1.selinux 2003-03-30 07:13:38.000000000 -0500 -+++ coreutils-5.0/man/mknod.1 2003-12-09 20:44:58.000000000 -0500 -@@ -12,6 +12,9 @@ - .PP - Mandatory arguments to long options are mandatory for short options too. - .TP -+\fB\-Z\fR, \fB\-\-context\fR=\fICONTEXT\fR -+set security context (quoted string) -+.TP - \fB\-m\fR, \fB\-\-mode\fR=\fIMODE\fR - set permission mode (as in chmod), not a=rw - umask - .TP ---- /dev/null 2003-09-15 09:40:47.000000000 -0400 -+++ coreutils-5.0/man/runcon.1 2003-12-09 20:44:58.000000000 -0500 -@@ -0,0 +1,39 @@ -+.TH RUNCON "1" "July 2003" "runcon (coreutils) 5.0" "selinux" -+.SH NAME -+runcon \- run command with specified security context -+.SH SYNOPSIS -+.B runcon -+[\fI-t TYPE\fR] [\fI-l LEVEL\fR] [\fI-u USER\fR] [\fI-r ROLE\fR] \fICOMMAND\fR [\fIARGS...\fR] -+.PP -+or -+.PP -+.B runcon -+\fICONTEXT\fR \fICOMMAND\fR [\fIargs...\fR] -+.PP -+.br -+.SH DESCRIPTION -+.PP -+.\" Add any additional description here -+.PP -+Run COMMAND with current security context modified by one or more of LEVEL, -+ROLE, TYPE, and USER, or with completely-specified CONTEXT. -+.TP -+\fB\-t\fR -+change current type to the specified type -+.TP -+\fB\-l\fR -+change current level range to the specified range -+.TP -+\fB\-r\fR -+change current role to the specified role -+.TP -+\fB\-u\fR -+change current user to the specified user -+.PP -+If none of \fI-t\fR, \fI-u\fR, \fI-r\fR, or \fI-l\fR, is specified, -+the first argument is used as the complete context. Any additional -+arguments after \fICOMMAND\fR are interpreted as arguments to the -+command. -+.PP -+Note that only carefully-chosen contexts are likely to successfully -+run. ---- coreutils-5.0/README.selinux 2003-12-09 20:44:56.000000000 -0500 -+++ coreutils-5.0/README 2003-12-09 20:44:58.000000000 -0500 +--- coreutils-5.2.1/config.hin.selinux 2004-03-16 14:25:05.467534328 -0500 ++++ coreutils-5.2.1/config.hin 2004-03-16 14:25:05.640508032 -0500 +@@ -1374,6 +1374,9 @@ + /* Define if sys/ptem.h is required for struct winsize. */ + #undef WINSIZE_IN_PTEM + ++/* Define if you want to use SELINUX */ ++#undef WITH_SELINUX ++ + /* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ + #undef WORDS_BIGENDIAN +--- coreutils-5.2.1/README.selinux 2004-01-18 02:59:41.000000000 -0500 ++++ coreutils-5.2.1/README 2004-03-16 14:25:05.641507880 -0500 @@ -7,11 +7,11 @@ - The programs that can be built with this package are: + The programs that can be built with this package are: + +- [ basename cat chgrp chmod chown chroot cksum comm cp csplit cut date dd ++ [ basename cat chcon chgrp chmod chown chroot cksum comm cp csplit cut date dd + df dir dircolors dirname du echo env expand expr factor false fmt fold + ginstall groups head hostid hostname 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 ++ printenv printf ptx pwd readlink rm rmdir runcon 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 + +--- coreutils-5.2.1/tests/help-version.selinux 2004-02-17 11:04:23.000000000 -0500 ++++ coreutils-5.2.1/tests/help-version 2004-03-16 14:25:05.643507576 -0500 +@@ -42,6 +42,8 @@ + + # Skip `test'; it doesn't accept --help or --version. + test $i = test && continue; ++ test $i = chcon && continue; ++ test $i = runcon && continue; + + # false fails even when invoked with --help or --version. + if test $i = false; then +@@ -154,7 +156,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|chcon|runcon) continue;; esac + + rm -rf $tmp_in $tmp_in2 $tmp_dir $tmp_out + echo > $tmp_in +--- coreutils-5.2.1/src/mkdir.c.selinux 2004-01-21 17:27:02.000000000 -0500 ++++ coreutils-5.2.1/src/mkdir.c 2004-03-16 14:25:05.645507272 -0500 +@@ -34,6 +34,10 @@ + + #define AUTHORS "David MacKenzie" + ++#ifdef WITH_SELINUX ++#include /* for is_selinux_enabled() */ ++#endif ++ + /* The name this program was run with. */ + char *program_name; + +@@ -42,6 +46,9 @@ + + static struct option const longopts[] = + { ++#ifdef WITH_SELINUX ++ {"context", required_argument, NULL, 'Z'}, ++#endif + {"mode", required_argument, NULL, 'm'}, + {"parents", no_argument, NULL, 'p'}, + {"verbose", no_argument, NULL, 'v'}, +@@ -63,6 +70,11 @@ + Create the DIRECTORY(ies), if they do not already exist.\n\ + \n\ + "), stdout); ++#ifdef WITH_SELINUX ++ printf (_("\ ++ -Z, --context=CONTEXT (SELinux) set security context to CONTEXT\n\ ++")); ++#endif + fputs (_("\ + Mandatory arguments to long options are mandatory for short options too.\n\ + "), stdout); +@@ -98,7 +110,11 @@ + + create_parents = 0; + ++#ifdef WITH_SELINUX ++ while ((optc = getopt_long (argc, argv, "pm:vZ:", longopts, NULL)) != -1) ++#else + while ((optc = getopt_long (argc, argv, "pm:v", longopts, NULL)) != -1) ++#endif + { + switch (optc) + { +@@ -113,6 +129,20 @@ + case 'v': /* --verbose */ + verbose_fmt_string = _("created directory %s"); + break; ++#ifdef WITH_SELINUX ++ case 'Z': ++ /* politely decline if we're not on a selinux-enabled kernel. */ ++ if( !(is_selinux_enabled()>0)) { ++ fprintf( stderr, "Sorry, --context (-Z) can be used only on " ++ "a selinux-enabled kernel.\n" ); ++ exit( 1 ); ++ } ++ if (setfscreatecon(optarg)) { ++ fprintf( stderr, "Sorry, cannot set default context to %s.\n", optarg); ++ exit( 1 ); ++ } ++ break; ++#endif + case_GETOPT_HELP_CHAR; + case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); + default: +--- coreutils-5.2.1/src/cp.c.selinux 2004-03-16 14:25:05.411542840 -0500 ++++ coreutils-5.2.1/src/cp.c 2004-03-16 14:25:05.648506816 -0500 +@@ -49,6 +49,11 @@ + + #define AUTHORS "Torbjorn Granlund", "David MacKenzie", "Jim Meyering" + ++#ifdef WITH_SELINUX ++#include /* for is_selinux_enabled() */ ++int selinux_enabled=0; ++#endif ++ + #ifndef _POSIX_VERSION + uid_t geteuid (); + #endif +@@ -143,6 +148,9 @@ + {"update", no_argument, NULL, 'u'}, + {"verbose", no_argument, NULL, 'v'}, + {"version-control", required_argument, NULL, 'V'}, /* Deprecated. FIXME. */ ++#ifdef WITH_SELINUX ++ {"context", required_argument, NULL, 'Z'}, ++#endif + {GETOPT_HELP_OPTION_DECL}, + {GETOPT_VERSION_OPTION_DECL}, + {NULL, 0, NULL, 0} +@@ -192,6 +200,9 @@ + additional attributes: links, all\n\ + "), stdout); + fputs (_("\ ++ -c same as --preserve=context\n\ ++"), stdout); ++ fputs (_("\ + --no-preserve=ATTR_LIST don't preserve the specified attributes\n\ + --parents append source path to DIRECTORY\n\ + -P same as `--no-dereference'\n\ +@@ -219,6 +230,7 @@ + destination file is missing\n\ + -v, --verbose explain what is being done\n\ + -x, --one-file-system stay on this file system\n\ ++ -Z, --context=CONTEXT set security context of copy to CONTEXT\n\ + "), stdout); + fputs (HELP_OPTION_DESCRIPTION, stdout); + fputs (VERSION_OPTION_DESCRIPTION, stdout); +@@ -748,8 +760,8 @@ + { + new_dest = (char *) dest; + } +- +- return copy (source, new_dest, new_dst, x, &unused, NULL); ++ ret=copy (source, new_dest, new_dst, x, &unused, NULL); ++ return ret; + } + + /* unreachable */ +@@ -773,6 +785,10 @@ + x->preserve_mode = 0; + x->preserve_timestamps = 0; + ++#ifdef WITH_SELINUX ++ x->preserve_security_context = 0; ++#endif ++ + x->require_preserve = 0; + x->recursive = 0; + x->sparse_mode = SPARSE_AUTO; +@@ -800,19 +816,20 @@ + PRESERVE_TIMESTAMPS, + PRESERVE_OWNERSHIP, + PRESERVE_LINK, ++ PRESERVE_CONTEXT, + PRESERVE_ALL + }; + static enum File_attribute const preserve_vals[] = + { + PRESERVE_MODE, PRESERVE_TIMESTAMPS, +- PRESERVE_OWNERSHIP, PRESERVE_LINK, PRESERVE_ALL ++ PRESERVE_OWNERSHIP, PRESERVE_LINK, PRESERVE_CONTEXT, PRESERVE_ALL + }; + + /* Valid arguments to the `--preserve' option. */ + static char const* const preserve_args[] = + { + "mode", "timestamps", +- "ownership", "links", "all", 0 ++ "ownership", "links", "context", "all", 0 + }; + + char *arg_writable = xstrdup (arg); +@@ -847,11 +864,16 @@ + x->preserve_links = on_off; + break; + ++ case PRESERVE_CONTEXT: ++ x->preserve_security_context = 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_security_context = on_off; + break; + + default: +@@ -875,6 +897,10 @@ + struct cp_options x; + int copy_contents = 0; + char *target_directory = NULL; ++#ifdef WITH_SELINUX ++ security_context_t scontext = NULL; ++ selinux_enabled= (is_selinux_enabled()>0); ++#endif + + initialize_main (&argc, &argv); + program_name = argv[0]; +@@ -890,7 +916,11 @@ + we'll actually use backup_suffix_string. */ + backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); + ++#ifdef WITH_SELINUX ++ while ((c = getopt_long (argc, argv, "abcdfHilLprsuvxPRS:V:Z:", long_opts, NULL)) ++#else + while ((c = getopt_long (argc, argv, "abdfHilLprsuvxPRS:V:", long_opts, NULL)) ++#endif + != -1) + { + switch (c) +@@ -981,6 +1011,36 @@ + x.preserve_timestamps = 1; + x.require_preserve = 1; + break; ++#ifdef WITH_SELINUX ++ case 'c': ++ if ( scontext != NULL ) { ++ (void) fprintf(stderr, "%s: cannot force target context <-- %s and preserve it\n", argv[0], scontext); ++ exit( 1 ); ++ } ++ else if (selinux_enabled) ++ x.preserve_security_context = 1; ++ break; ++ ++ case 'Z': ++ /* politely decline if we're not on a selinux-enabled kernel. */ ++ if( !selinux_enabled ) { ++ fprintf( stderr, "Warning: ignoring --context (-Z). " ++ "It requires a SELinux enabled kernel.\n" ); ++ break; ++ } ++ if ( x.preserve_security_context ) { ++ (void) fprintf(stderr, "%s: cannot force target context to '%s' and preserve it\n", argv[0], optarg); ++ exit( 1 ); ++ } ++ scontext = optarg; ++ /* if there's a security_context given set new path ++ components to that context, too */ ++ if ( setfscreatecon(scontext) < 0 ) { ++ (void) fprintf(stderr, _("cannot set default security context %s"), scontext); ++ exit( 1 ); ++ } ++ break; ++#endif + + case PARENTS_OPTION: + flag_path = 1; +--- coreutils-5.2.1/src/stat.c.selinux 2004-02-05 08:46:12.000000000 -0500 ++++ coreutils-5.2.1/src/stat.c 2004-03-16 14:25:05.652506208 -0500 +@@ -42,6 +42,13 @@ + # endif + #endif + ++#ifdef WITH_SELINUX ++#include ++#define SECURITY_ID_T security_context_t ++#else ++#define SECURITY_ID_T char * ++#endif ++ + #include "system.h" + + #include "error.h" +@@ -95,6 +102,7 @@ + {"dereference", no_argument, 0, 'L'}, + {"format", required_argument, 0, 'c'}, + {"filesystem", no_argument, 0, 'f'}, ++ {"context", no_argument, 0, 'Z'}, + {"terse", no_argument, 0, 't'}, + {GETOPT_HELP_OPTION_DECL}, + {GETOPT_VERSION_OPTION_DECL}, +@@ -345,7 +353,7 @@ + /* print statfs info */ + static void + print_statfs (char *pformat, char m, char const *filename, +- void const *data) ++ void const *data,SECURITY_ID_T scontext) + { + STRUCT_STATVFS const *statfsbuf = data; + +@@ -407,7 +415,10 @@ + strcat (pformat, PRIdMAX); + printf (pformat, (intmax_t) (statfsbuf->f_ffree)); + break; +- ++ case 'C': ++ strcat (pformat, "s"); ++ printf(scontext); ++ break; + default: + strcat (pformat, "c"); + printf (pformat, m); +@@ -417,7 +428,7 @@ + + /* print stat info */ + static void +-print_stat (char *pformat, char m, char const *filename, void const *data) ++print_stat (char *pformat, char m, char const *filename, void const *data, SECURITY_ID_T scontext) + { + struct stat *statbuf = (struct stat *) data; + struct passwd *pw_ent; +@@ -553,6 +564,10 @@ + strcat (pformat, "d"); + printf (pformat, (int) statbuf->st_ctime); + break; ++ case 'C': ++ strcat (pformat, "s"); ++ printf(pformat,scontext); ++ break; + default: + strcat (pformat, "c"); + printf (pformat, m); +@@ -562,8 +577,8 @@ + + static void + print_it (char const *masterformat, char const *filename, +- void (*print_func) (char *, char, char const *, void const *), +- void const *data) ++ void (*print_func) (char *, char, char const *, void const *,SECURITY_ID_T ), ++ void const *data, SECURITY_ID_T scontext) + { + char *b; + +@@ -598,7 +613,7 @@ + putchar ('%'); + break; + default: +- print_func (dest, *p, filename, data); ++ print_func (dest, *p, filename, data,scontext); + break; + } + } +@@ -615,9 +630,17 @@ + + /* stat the filesystem and print what we find */ + static void +-do_statfs (char const *filename, int terse, char const *format) ++do_statfs (char const *filename, int terse, int secure, char const *format) + { + STRUCT_STATVFS statfsbuf; ++ SECURITY_ID_T scontext = NULL; ++#ifdef WITH_SELINUX ++ if(secure) ++ if (getfilecon(filename,&scontext)<0) { ++ perror (filename); ++ return; ++ } ++#endif + int i = statfs (filename, &statfsbuf); + + if (i == -1) +@@ -629,23 +652,40 @@ + + if (format == NULL) + { +- format = (terse +- ? "%n %i %l %t %b %f %a %s %c %d" +- : " File: \"%n\"\n" +- " ID: %-8i Namelen: %-7l Type: %T\n" +- "Blocks: Total: %-10b Free: %-10f Available: %-10a Size: %s\n" +- "Inodes: Total: %-10c Free: %-10d"); +- } +- +- print_it (format, filename, print_statfs, &statfsbuf); ++ if (terse) { ++ if(secure) ++ format = "%n %i %l %t %b %f %a %s %c %d %C"; ++ else ++ format = "%n %i %l %t %b %f %a %s %c %d"; ++ } ++ else ++ { ++ if(secure) ++ format = " File: \"%n\"\n" ++ " ID: %-8i Namelen: %-7l Type: %T\n" ++ "Blocks: Total: %-10b Free: %-10f Available: %-10a Size: %s\n" ++ "Inodes: Total: %-10c Free: %-10d\n" ++ " S_Context: %C\n"; ++ else ++ format= " File: \"%n\"\n" ++ " ID: %-8i Namelen: %-7l Type: %T\n" ++ "Blocks: Total: %-10b Free: %-10f Available: %-10a Size: %s\n" ++ "Inodes: Total: %-10c Free: %-10d"; ++ } ++ } ++ print_it (format, filename, print_statfs, &statfsbuf,scontext); ++#ifdef WITH_SELINUX ++ if (scontext != NULL) ++ freecon(scontext); ++#endif + } +- + /* stat the file and print what we find */ + static void +-do_stat (char const *filename, int follow_links, int terse, ++ do_stat (char const *filename, int follow_links, int terse,int secure, + char const *format) + { + struct stat statbuf; ++ SECURITY_ID_T scontext = NULL; + int i = ((follow_links == 1) + ? stat (filename, &statbuf) + : lstat (filename, &statbuf)); +@@ -656,11 +696,28 @@ + return; + } + ++#ifdef WITH_SELINUX ++ if(secure) { ++ if (link) ++ i=lgetfilecon(filename, &scontext); ++ else ++ i=getfilecon(filename, &scontext); ++ if (i == -1) ++ { ++ perror (filename); ++ return; ++ } ++ } ++#endif ++ + if (format == NULL) + { + if (terse != 0) + { +- format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o"; ++ if (secure) ++ format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o %C"; ++ else ++ format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o"; + } + else + { +@@ -668,7 +725,17 @@ + i = statbuf.st_mode & S_IFMT; + if (i == S_IFCHR || i == S_IFBLK) + { +- format = ++ if (secure) ++ format = ++ " File: %N\n" ++ " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" ++ "Device: %Dh/%dd\tInode: %-10i Links: %-5h" ++ " Device type: %t,%T\n" ++ "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" ++ " S_Context: %C\n" ++ "Access: %x\n" "Modify: %y\n" "Change: %z\n"; ++ else ++ format = + " File: %N\n" + " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" + "Device: %Dh/%dd\tInode: %-10i Links: %-5h" +@@ -678,6 +745,15 @@ + } + else + { ++ if (secure) ++ format = ++ " File: %N\n" ++ " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" ++ "Device: %Dh/%dd\tInode: %-10i Links: %-5h\n" ++ "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" ++ "S_Context: %C\n" ++ "Access: %x\n" "Modify: %y\n" "Change: %z\n"; ++ else + format = + " File: %N\n" + " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" +@@ -687,7 +763,11 @@ + } + } + } +- print_it (format, filename, print_stat, &statbuf); ++ print_it (format, filename, print_stat, &statbuf,scontext); ++#ifdef WITH_SELINUX ++ if (scontext) ++ freecon(scontext); ++#endif + } + + void +@@ -705,6 +785,7 @@ + -f, --filesystem display filesystem status instead of file status\n\ + -c --format=FORMAT use the specified FORMAT instead of the default\n\ + -L, --dereference follow links\n\ ++ -Z, --context print the security context \n\ + -t, --terse print the information in terse form\n\ + "), stdout); + fputs (HELP_OPTION_DESCRIPTION, stdout); +@@ -756,6 +837,7 @@ + %c Total file nodes in file system\n\ + %d Free file nodes in file system\n\ + %f Free blocks in file system\n\ ++ %C - Security context in SELinux\n\ + "), stdout); + fputs (_("\ + %i File System id in hex\n\ +@@ -778,6 +860,7 @@ + int follow_links = 0; + int fs = 0; + int terse = 0; ++ int secure = 0; + char *format = NULL; + + initialize_main (&argc, &argv); +@@ -788,7 +871,7 @@ + + atexit (close_stdout); + +- while ((c = getopt_long (argc, argv, "c:fLlt", long_options, NULL)) != -1) ++ while ((c = getopt_long (argc, argv, "c:fLltZ", long_options, NULL)) != -1) + { + switch (c) + { +@@ -810,6 +893,14 @@ + case 't': + terse = 1; + break; ++ case 'Z': ++ if((is_selinux_enabled()>0)) ++ secure = 1; ++ else { ++ error (0, 0, _("Kernel is not SELinux enabled")); ++ usage (EXIT_FAILURE); ++ } ++ break; + + case_GETOPT_HELP_CHAR; + +@@ -829,9 +920,9 @@ + for (i = optind; i < argc; i++) + { + if (fs == 0) +- do_stat (argv[i], follow_links, terse, format); ++ do_stat (argv[i], follow_links, terse, secure, format); + else +- do_statfs (argv[i], terse, format); ++ do_statfs (argv[i], terse, secure, format); + } + + exit (G_fail ? EXIT_FAILURE : EXIT_SUCCESS); +--- coreutils-5.2.1/src/ls.c.selinux 2004-03-16 14:25:05.406543600 -0500 ++++ coreutils-5.2.1/src/ls.c 2004-03-16 14:25:05.675502712 -0500 +@@ -121,6 +121,18 @@ + + #define AUTHORS "Richard Stallman", "David MacKenzie" + ++#ifdef WITH_SELINUX ++#include ++int selinux_enabled= 0; ++static int print_scontext = 0; ++#define check_selinux() if (!selinux_enabled) { \ ++ fprintf( stderr, "Sorry, this option can only be used " \ ++ "on a SELinux kernel.\n" ); \ ++ exit( EXIT_FAILURE ); \ ++} ++ ++#endif ++ + #define obstack_chunk_alloc malloc + #define obstack_chunk_free free + +@@ -192,6 +204,10 @@ + /* For long listings, true if the file has an access control list. */ + bool have_acl; + #endif ++ ++#ifdef WITH_SELINUX ++ security_context_t scontext; ++#endif + }; + + #if HAVE_ACL || USE_ACL +@@ -256,6 +272,9 @@ + static void sort_files (void); + static void parse_ls_color (void); + void usage (int status); ++#ifdef WITH_SELINUX ++static void print_scontext_format (const struct fileinfo *f); ++#endif + + /* The name the program was run with, stripped of any leading path. */ + char *program_name; +@@ -354,7 +373,10 @@ + one_per_line, /* -1 */ + many_per_line, /* -C */ + horizontal, /* -x */ +- with_commas /* -m */ ++#ifdef WITH_SELINUX ++ security_format, /* -Z */ ++#endif ++ with_commas /* -m */ + }; + + static enum format format; +@@ -679,6 +701,11 @@ + SHOW_CONTROL_CHARS_OPTION, + SI_OPTION, + SORT_OPTION, ++#ifdef WITH_SELINUX ++ CONTEXT_OPTION, ++ LCONTEXT_OPTION, ++ SCONTEXT_OPTION, ++#endif + TIME_OPTION, + TIME_STYLE_OPTION + }; +@@ -722,6 +749,11 @@ + {"time-style", required_argument, 0, TIME_STYLE_OPTION}, + {"color", optional_argument, 0, COLOR_OPTION}, + {"block-size", required_argument, 0, BLOCK_SIZE_OPTION}, ++#ifdef WITH_SELINUX ++ {"context", no_argument, 0, CONTEXT_OPTION}, ++ {"lcontext", no_argument, 0, LCONTEXT_OPTION}, ++ {"scontext", no_argument, 0, SCONTEXT_OPTION}, ++#endif + {"author", no_argument, 0, AUTHOR_OPTION}, + {GETOPT_HELP_OPTION_DECL}, + {GETOPT_VERSION_OPTION_DECL}, +@@ -731,12 +763,19 @@ + static char const *const format_args[] = + { + "verbose", "long", "commas", "horizontal", "across", +- "vertical", "single-column", 0 ++ "vertical", "single-column", ++#ifdef WITH_SELINUX ++ "context", ++#endif ++ 0 + }; + + static enum format const format_types[] = + { + long_format, long_format, with_commas, horizontal, horizontal, ++#ifdef WITH_SELINUX ++ security_format, ++#endif + many_per_line, one_per_line + }; + +@@ -1101,6 +1140,9 @@ + + format_needs_stat = sort_type == sort_time || sort_type == sort_size + || format == long_format ++#ifdef WITH_SELINUX ++ || format == security_format || print_scontext ++#endif + || dereference == DEREF_ALWAYS + || print_block_size || print_inode; + format_needs_type = (format_needs_stat == 0 +@@ -1223,6 +1265,11 @@ + /* Record whether there is an option specifying sort type. */ + int sort_type_specified = 0; + ++#ifdef WITH_SELINUX ++ /* 1 iff kernel has new selinux system calls */ ++ selinux_enabled= (is_selinux_enabled()>0); ++#endif ++ + qmark_funny_chars = 0; + + /* initialize all switches to default settings */ +@@ -1273,6 +1320,9 @@ + all_files = 0; + really_all_files = 0; + ignore_patterns = 0; ++#ifdef WITH_SELINUX ++ print_scontext = 0; ++#endif + + /* FIXME: put this in a function. */ + { +@@ -1350,7 +1400,7 @@ + } + + while ((c = getopt_long (argc, argv, +- "abcdfghiklmnopqrstuvw:xABCDFGHI:LNQRST:UX1", ++ "abcdfghiklmnopqrstuvw:xABCDFGHI:LNQRST:UX1Z", + long_options, NULL)) != -1) + { + switch (c) +@@ -1470,6 +1520,13 @@ + format = horizontal; + break; + ++#ifdef WITH_SELINUX ++ case 'Z': ++ check_selinux(); ++ print_scontext = 1; ++ format = security_format; ++ break; ++#endif + case 'A': + really_all_files = 0; + all_files = 1; +@@ -1637,6 +1694,25 @@ + + case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); + ++#ifdef WITH_SELINUX ++ ++ case CONTEXT_OPTION: /* new security format */ ++ check_selinux(); ++ print_scontext = 1; ++ format = security_format; ++ break; ++ case LCONTEXT_OPTION: /* long format plus security context */ ++ check_selinux(); ++ print_scontext = 1; ++ format = long_format; ++ break; ++ case SCONTEXT_OPTION: /* short form of new security format */ ++ check_selinux(); ++ print_scontext = 0; ++ format = security_format; ++ break; ++#endif ++ + default: + usage (EXIT_FAILURE); + } +@@ -2300,6 +2376,12 @@ + free (files[i].name); + if (files[i].linkname) + free (files[i].linkname); ++#ifdef WITH_SELINUX ++ if (files[i].scontext) { ++ freecon (files[i].scontext); ++ files[i].scontext=NULL; ++ } ++#endif + } + + files_index = 0; +@@ -2336,6 +2418,9 @@ + f->linkname = 0; + f->linkmode = 0; + f->linkok = 0; ++#ifdef WITH_SELINUX ++ f->scontext = NULL; ++#endif + + if (explicit_arg + || format_needs_stat +@@ -2381,6 +2466,11 @@ + { + int need_lstat; + err = stat (path, &f->stat); ++#ifdef WITH_SELINUX ++ if (err>=0) ++ if (selinux_enabled && (format == security_format || print_scontext)) ++ getfilecon(path, &f->scontext); ++#endif + + if (dereference == DEREF_COMMAND_LINE_ARGUMENTS) + break; +@@ -2399,6 +2489,11 @@ + + default: /* DEREF_NEVER */ + err = lstat (path, &f->stat); ++#ifdef WITH_SELINUX ++ if (err>=0) ++ if (selinux_enabled && (format == security_format || print_scontext)) ++ lgetfilecon(path, &f->scontext); ++#endif + break; + } + +@@ -2893,6 +2988,16 @@ + DIRED_PUTCHAR ('\n'); + } + break; ++ ++#ifdef WITH_SELINUX ++ case security_format: ++ for (i = 0; i < files_index; i++) ++ { ++ print_scontext_format (files + i); ++ DIRED_PUTCHAR ('\n'); ++ } ++ break; ++#endif + } + } + +@@ -3117,6 +3222,14 @@ + } + p += sizeof modebuf + nlink_width + 1; + ++#ifdef WITH_SELINUX ++ ++ if ( print_scontext ) { ++ sprintf (p, "%-32s ", f->scontext); ++ p += strlen (p); ++ } ++#endif ++ + DIRED_INDENT (); + + if (print_owner | print_group | print_author) +@@ -4037,6 +4150,16 @@ + -X sort alphabetically by entry extension\n\ + -1 list one file per line\n\ + "), stdout); ++#ifdef WITH_SELINUX ++printf(_("\nSELINUX options:\n\n\ ++ --lcontext Display security context. Enable -l. Lines\n\ ++ will probably be too wide for most displays.\n\ ++ -Z, --context Display security context so it fits on most\n\ ++ displays. Displays only mode, user, group,\n\ ++ security context and file name.\n\ ++ --scontext Display only security context and file name.\n\ ++\n\n")); ++#endif + fputs (HELP_OPTION_DESCRIPTION, stdout); + fputs (VERSION_OPTION_DESCRIPTION, stdout); + fputs (_("\n\ +@@ -4055,3 +4178,79 @@ + } + exit (status); + } ++ ++#ifdef WITH_SELINUX ++ ++static void ++print_scontext_format (const struct fileinfo *f) ++{ ++ char modebuf[12]; ++ ++ /* 7 fields that may require LONGEST_HUMAN_READABLE bytes, ++ 1 10-byte mode string, ++ 9 spaces, one following each of these fields, and ++ 1 trailing NUL byte. */ ++ ++ char init_bigbuf[7 * LONGEST_HUMAN_READABLE + 10 + 9 + 1]; ++ char *buf = init_bigbuf; ++ size_t bufsize = sizeof (init_bigbuf); ++ size_t s; ++ char *p; ++ const char *fmt; ++ char *user_name; ++ char *group_name; ++ int rv; ++ char *scontext; ++ ++ p = buf; ++ ++ if ( print_scontext ) { /* zero means terse listing */ ++ mode_string (f->stat.st_mode, modebuf); ++ modebuf[10] = (FILE_HAS_ACL (f) ? '+' : ' '); ++ modebuf[11] = '\0'; ++ ++ /* print mode */ ++ ++ (void) sprintf (p, "%s ", modebuf); ++ p += strlen (p); ++ ++ /* print standard user and group */ ++ ++ user_name = (numeric_ids ? NULL : getuser (f->stat.st_uid)); ++ if (user_name) ++ (void) sprintf (p, "%-8.8s ", user_name); ++ else ++ (void) sprintf (p, "%-8u ", (unsigned int) f->stat.st_uid); ++ p += strlen (p); ++ ++ if ( print_group ) { ++ group_name = (numeric_ids ? NULL : getgroup (f->stat.st_gid)); ++ if (group_name) ++ (void) sprintf (p, "%-8.8s ", group_name); ++ else ++ (void) sprintf (p, "%-8u ", (unsigned int) f->stat.st_gid); ++ p += strlen (p); ++ } ++ } ++ ++ (void) sprintf (p, "%-32s ", f->scontext); ++ p += strlen (p); ++ ++ DIRED_INDENT (); ++ DIRED_FPUTS (buf, stdout, p - buf); ++ print_name_with_quoting (f->name, f->stat.st_mode, f->linkok, &dired_obstack); ++ ++ if (f->filetype == symbolic_link) { ++ if (f->linkname) { ++ DIRED_FPUTS_LITERAL (" -> ", stdout); ++ print_name_with_quoting (f->linkname, f->linkmode, f->linkok - 1, NULL); ++ if (indicator_style != none) ++ print_type_indicator (f->linkmode); ++ } ++ } ++ else { ++ if (indicator_style != none) ++ print_type_indicator (f->stat.st_mode); ++ } ++} ++#endif +--- coreutils-5.2.1/src/copy.h.selinux 2004-03-16 14:25:05.413542536 -0500 ++++ coreutils-5.2.1/src/copy.h 2004-03-16 14:25:05.677502408 -0500 +@@ -105,6 +105,9 @@ + int preserve_ownership; + int preserve_mode; + int preserve_timestamps; ++#ifdef WITH_SELINUX ++ int preserve_security_context; ++#endif + + /* Enabled for mv, and for cp by the --preserve=links option. + If nonzero, attempt to preserve in the destination files any +--- coreutils-5.2.1/src/mknod.c.selinux 2004-01-21 17:27:02.000000000 -0500 ++++ coreutils-5.2.1/src/mknod.c 2004-03-16 14:25:05.679502104 -0500 +@@ -36,8 +36,15 @@ + /* The name this program was run with. */ + char *program_name; + ++#ifdef WITH_SELINUX ++#include ++#endif ++ + static struct option const longopts[] = + { ++#ifdef WITH_SELINUX ++ {"context", required_argument, NULL, 'Z'}, ++#endif + {"mode", required_argument, NULL, 'm'}, + {GETOPT_HELP_OPTION_DECL}, + {GETOPT_VERSION_OPTION_DECL}, +@@ -58,6 +65,11 @@ + Create the special file NAME of the given TYPE.\n\ + \n\ + "), stdout); ++#ifdef WITH_SELINUX ++ fputs(_("\ ++ -Z, --context=CONTEXT set security context (quoted string)\n\ ++"), stdout); ++#endif + fputs (_("\ + Mandatory arguments to long options are mandatory for short options too.\n\ + "), stdout); +@@ -103,7 +115,11 @@ + + specified_mode = NULL; + ++#ifdef WITH_SELINUX ++ while ((optc = getopt_long (argc, argv, "m:Z:", longopts, NULL)) != -1) ++#else + while ((optc = getopt_long (argc, argv, "m:", longopts, NULL)) != -1) ++#endif + { + switch (optc) + { +@@ -112,6 +128,20 @@ + case 'm': + specified_mode = optarg; + break; ++#ifdef WITH_SELINUX ++ case 'Z': ++ /* politely decline if we're not on a selinux-enabled kernel. */ ++ if( !(is_selinux_enabled()>0)) { ++ fprintf( stderr, "Sorry, --context (-Z) can be used only on " ++ "a selinux-enabled kernel.\n" ); ++ exit( 1 ); ++ } ++ if (setfscreatecon(optarg)) { ++ fprintf( stderr, "Sorry, cannot set default context to %s.\n", optarg); ++ exit( 1 ); ++ } ++ break; ++#endif + case_GETOPT_HELP_CHAR; + case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); + default: +--- coreutils-5.2.1/src/mkfifo.c.selinux 2004-01-21 17:27:02.000000000 -0500 ++++ coreutils-5.2.1/src/mkfifo.c 2004-03-16 14:25:05.682501648 -0500 +@@ -32,11 +32,18 @@ + + #define AUTHORS "David MacKenzie" + ++#ifdef WITH_SELINUX ++#include /* for is_selinux_enabled() */ ++#endif ++ + /* The name this program was run with. */ + char *program_name; + + static struct option const longopts[] = + { ++#ifdef WITH_SELINUX ++ {"context", required_argument, NULL, 'Z'}, ++#endif + {"mode", required_argument, NULL, 'm'}, + {GETOPT_HELP_OPTION_DECL}, + {GETOPT_VERSION_OPTION_DECL}, +@@ -57,6 +64,11 @@ + Create named pipes (FIFOs) with the given NAMEs.\n\ + \n\ + "), stdout); ++#ifdef WITH_SELINUX ++ printf (_("\ ++ -Z, --context=CONTEXT set security context (quoted string)\n\ ++"), stdout); ++#endif + fputs (_("\ + Mandatory arguments to long options are mandatory for short options too.\n\ + "), stdout); +@@ -93,7 +105,11 @@ + #ifndef S_ISFIFO + error (EXIT_FAILURE, 0, _("fifo files not supported")); + #else ++#ifdef WITH_SELINUX ++ while ((optc = getopt_long (argc, argv, "m:Z:", longopts, NULL)) != -1) ++#else + while ((optc = getopt_long (argc, argv, "m:", longopts, NULL)) != -1) ++#endif + { + switch (optc) + { +@@ -102,6 +118,19 @@ + case 'm': + specified_mode = optarg; + break; ++#ifdef WITH_SELINUX ++ case 'Z': ++ if( !(is_selinux_enabled()>0)) { ++ fprintf( stderr, "Sorry, --context (-Z) can be used only on " ++ "a selinux-enabled kernel.\n" ); ++ exit( 1 ); ++ } ++ if (setfscreatecon(optarg)) { ++ fprintf( stderr, "Sorry, cannot set default context to %s.\n", optarg); ++ exit( 1 ); ++ } ++ break; ++#endif + case_GETOPT_HELP_CHAR; + case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); + default: +--- coreutils-5.2.1/src/Makefile.am.selinux 2004-03-16 14:25:05.455536152 -0500 ++++ coreutils-5.2.1/src/Makefile.am 2004-03-16 14:25:05.684501344 -0500 +@@ -3,13 +3,13 @@ + EXTRA_PROGRAMS = chroot df hostid nice pinky stty su uname uptime users who + + bin_SCRIPTS = groups +-bin_PROGRAMS = [ chgrp chown chmod cp dd dircolors du \ ++bin_PROGRAMS = [ chgrp chown chmod chcon cp dd dircolors du \ + ginstall link ln dir vdir ls mkdir \ + mkfifo mknod mv nohup readlink rm rmdir shred stat sync touch unlink \ + cat cksum comm csplit cut expand fmt fold head join md5sum \ + nl od paste pr ptx sha1sum sort split sum tac tail tr tsort unexpand uniq wc \ + basename date dirname echo env expr factor false \ +- hostname id kill logname pathchk printenv printf pwd seq sleep tee \ ++ hostname id kill logname pathchk printenv printf pwd runcon seq sleep tee \ + test true tty whoami yes \ + $(OPTIONAL_BIN_PROGS) $(DF_PROG) + +@@ -32,13 +32,20 @@ + # replacement functions defined in libfetish.a. + LDADD = ../lib/libfetish.a $(LIBINTL) ../lib/libfetish.a + +-dir_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) @LIBACL@ +-ls_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) @LIBACL@ ++dir_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) @LIBACL@ @LIB_SELINUX@ ++ls_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) @LIBACL@ @LIB_SELINUX@ + shred_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) +-vdir_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) @LIBACL@ +-cp_LDADD = $(LDADD) @LIBACL@ +-ginstall_LDADD = $(LDADD) @LIBACL@ +-mv_LDADD = $(LDADD) @LIBACL@ ++vdir_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) @LIBACL@ @LIB_SELINUX@ ++cp_LDADD = $(LDADD) @LIBACL@ @LIB_SELINUX@ ++ginstall_LDADD = $(LDADD) @LIBACL@ @LIB_SELINUX@ ++mv_LDADD = $(LDADD) @LIBACL@ @LIB_SELINUX@ ++chcon_LDADD = $(LDADD) @LIB_SELINUX@ ++id_LDADD = $(LDADD) @LIB_SELINUX@ ++mkdir_LDADD = $(LDADD) @LIB_SELINUX@ ++mkfifo_LDADD = $(LDADD) @LIB_SELINUX@ ++mknod_LDADD = $(LDADD) @LIB_SELINUX@ ++stat_LDADD = $(LDADD) @LIB_SELINUX@ ++runcon_LDADD = $(LDADD) @LIB_SELINUX@ + + ## If necessary, add -lm to resolve use of pow in lib/strtod.c. + sort_LDADD = $(LDADD) $(POW_LIB) +--- /dev/null 2004-02-23 16:02:56.000000000 -0500 ++++ coreutils-5.2.1/src/chcon.c 2004-03-16 14:25:05.688500736 -0500 +@@ -0,0 +1,423 @@ ++/* chcontext -- change security context of a pathname */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "system.h" ++#include "error.h" ++#include "savedir.h" ++#include "group-member.h" ++ ++enum Change_status ++{ ++ CH_SUCCEEDED, ++ CH_FAILED, ++ CH_NO_CHANGE_REQUESTED ++}; ++ ++enum Verbosity ++{ ++ /* Print a message for each file that is processed. */ ++ V_high, ++ ++ /* Print a message for each file whose attributes we change. */ ++ V_changes_only, ++ ++ /* Do not be verbose. This is the default. */ ++ V_off ++}; ++ ++static int change_dir_context (const char *dir, const struct stat *statp); ++ ++/* The name the program was run with. */ ++char *program_name; ++ ++/* If nonzero, and the systems has support for it, change the context ++ of symbolic links rather than any files they point to. */ ++static int change_symlinks; ++ ++/* If nonzero, change the context of directories recursively. */ ++static int recurse; ++ ++/* If nonzero, force silence (no error messages). */ ++static int force_silent; ++ ++/* Level of verbosity. */ ++static enum Verbosity verbosity = V_off; ++ ++/* The name of the context file is being given. */ ++static const char *specified_context; ++ ++/* Specific components of the context */ ++static const char *specified_user; ++static const char *specified_role; ++static const char *specified_range; ++static const char *specified_type; ++ ++/* The argument to the --reference option. Use the context of this file. ++ This file must exist. */ ++static char *reference_file; ++ ++/* If nonzero, display usage information and exit. */ ++static int show_help; ++ ++/* If nonzero, print the version on standard output and exit. */ ++static int show_version; ++ ++static struct option const long_options[] = ++{ ++ {"recursive", no_argument, 0, 'R'}, ++ {"changes", no_argument, 0, 'c'}, ++ {"no-dereference", no_argument, 0, 'h'}, ++ {"silent", no_argument, 0, 'f'}, ++ {"quiet", no_argument, 0, 'f'}, ++ {"reference", required_argument, 0, CHAR_MAX + 1}, ++ {"context", required_argument, 0, CHAR_MAX + 2}, ++ {"user", required_argument, 0, 'u'}, ++ {"role", required_argument, 0, 'r'}, ++ {"type", required_argument, 0, 't'}, ++ {"range", required_argument, 0, 'l'}, ++ {"verbose", no_argument, 0, 'v'}, ++ {"help", no_argument, &show_help, 1}, ++ {"version", no_argument, &show_version, 1}, ++ {0, 0, 0, 0} ++}; ++ ++/* Tell the user how/if the context of FILE has been changed. ++ CHANGED describes what (if anything) has happened. */ ++ ++static void ++describe_change (const char *file, security_context_t newcontext, enum Change_status changed) ++{ ++ const char *fmt; ++ switch (changed) ++ { ++ case CH_SUCCEEDED: ++ fmt = _("context of %s changed to %s\n"); ++ break; ++ case CH_FAILED: ++ fmt = _("failed to change context of %s to %s\n"); ++ break; ++ case CH_NO_CHANGE_REQUESTED: ++ fmt = _("context of %s retained as %s\n"); ++ break; ++ default: ++ abort (); ++ } ++ printf (fmt, file, newcontext); ++} ++ ++static int ++compute_context_from_mask (security_context_t context, context_t *ret) ++{ ++ context_t newcontext = context_new (context); ++ if (!newcontext) ++ return 1; ++#define SETCOMPONENT(comp) \ ++ do { \ ++ if (specified_ ## comp) \ ++ if (context_ ## comp ## _set (newcontext, specified_ ## comp)) \ ++ goto lose; \ ++ } while (0) ++ ++ SETCOMPONENT(user); ++ SETCOMPONENT(range); ++ SETCOMPONENT(role); ++ SETCOMPONENT(type); ++#undef SETCOMPONENT ++ ++ *ret = newcontext; ++ return 0; ++ lose: ++ context_free (newcontext); ++ return 1; ++} ++ ++/* Change the context of FILE, using specified components. ++ If it is a directory and -R is given, recurse. ++ Return 0 if successful, 1 if errors occurred. */ ++ ++static int ++change_file_context (const char *file) ++{ ++ struct stat file_stats; ++ security_context_t file_context=NULL; ++ context_t context; ++ security_context_t context_string; ++ int errors = 0; ++ int status = 0; ++ ++ if (change_symlinks) ++ status = lgetfilecon(file, &file_context); ++ else ++ status = getfilecon(file, &file_context); ++ ++ if ((status < 0) && (errno != ENODATA)) ++ { ++ if (force_silent == 0) ++ error (0, errno, "%s", file); ++ return 1; ++ } ++ ++ /* If the file doesn't have a context, and we're not setting all of ++ the context components, there isn't really an obvious default. ++ Thus, we just give up. */ ++ if (file_context == NULL && specified_context == NULL) ++ { ++ error (0, 0, _("can't apply partial context to unlabeled file %s"), file); ++ return 1; ++ } ++ ++ if (specified_context == NULL) ++ { ++ if (compute_context_from_mask (file_context, &context)) ++ { ++ error (0, 0, _("couldn't compute security context from %s"), file_context); ++ return 1; ++ } ++ } ++ else ++ { ++ context = context_new (specified_context); ++ if (!context) ++ error (1, 0,_("invalid context: %s"),specified_context); ++ } ++ ++ context_string = context_str (context); ++ ++ if (file_context == NULL || strcmp(context_string,file_context)!=0) ++ { ++ int fail; ++ ++ if (change_symlinks) ++ fail = lsetfilecon (file, context_string); ++ else ++ fail = setfilecon (file, context_string); ++ ++ if (verbosity == V_high || (verbosity == V_changes_only && !fail)) ++ describe_change (file, context_string, (fail ? CH_FAILED : CH_SUCCEEDED)); ++ ++ if (fail) ++ { ++ errors = 1; ++ if (force_silent == 0) ++ { ++ error (0, errno, _("failed to change context of %s to %s"), file, context_string); ++ } ++ } ++ } ++ else if (verbosity == V_high) ++ { ++ describe_change (file, context_string, CH_NO_CHANGE_REQUESTED); ++ } ++ ++ context_free(context); ++ freecon(file_context); ++ ++ if (recurse) { ++ if (lstat(file, &file_stats)==0) ++ if (S_ISDIR (file_stats.st_mode) && ++ (strcmp(file,"..") !=0) && ++ (strcmp(file,".") !=0)) ++ errors |= change_dir_context (file, &file_stats); ++ } ++ return errors; ++} ++ ++/* Recursively change context of the files in directory DIR ++ using specified context components. ++ STATP points to the results of lstat on DIR. ++ Return 0 if successful, 1 if errors occurred. */ ++ ++static int ++change_dir_context (const char *dir, const struct stat *statp) ++{ ++ char *name_space, *namep; ++ char *path; /* Full path of each entry to process. */ ++ unsigned dirlength; /* Length of `dir' and '\0'. */ ++ unsigned filelength; /* Length of each pathname to process. */ ++ unsigned pathlength; /* Bytes allocated for `path'. */ ++ int errors = 0; ++ ++ errno = 0; ++ name_space = savedir (dir); ++ if (name_space == NULL) ++ { ++ if (errno) ++ { ++ if (force_silent == 0) ++ error (0, errno, "%s", dir); ++ return 1; ++ } ++ else ++ error (1, 0, _("virtual memory exhausted")); ++ } ++ ++ dirlength = strlen (dir) + 1; /* + 1 is for the trailing '/'. */ ++ pathlength = dirlength + 1; ++ /* Give `path' a dummy value; it will be reallocated before first use. */ ++ path = xmalloc (pathlength); ++ strcpy (path, dir); ++ path[dirlength - 1] = '/'; ++ ++ for (namep = name_space; *namep; namep += filelength - dirlength) ++ { ++ filelength = dirlength + strlen (namep) + 1; ++ if (filelength > pathlength) ++ { ++ pathlength = filelength * 2; ++ path = xrealloc (path, pathlength); ++ } ++ strcpy (path + dirlength, namep); ++ errors |= change_file_context (path); ++ } ++ free (path); ++ free (name_space); ++ return errors; ++} ++ ++static void ++usage (int status) ++{ ++ if (status != 0) ++ fprintf (stderr, _("Try `%s --help' for more information.\n"), ++ program_name); ++ else ++ { ++ printf (_("\ ++Usage: %s [OPTION]... CONTEXT FILE...\n\ ++ or: %s [OPTION]... [-u USER] [-r ROLE] [-l RANGE] [-t TYPE] FILE...\n\ ++ or: %s [OPTION]... --reference=RFILE FILE...\n\ ++"), ++ program_name, program_name, program_name); ++ printf (_("\ ++Change the security context of each FILE to CONTEXT.\n\ ++\n\ ++ -c, --changes like verbose but report only when a change is made\n\ ++ -h, --no-dereference affect symbolic links instead of any referenced file\n\ ++ (available only on systems with lchown system call)\n\ ++ -f, --silent, --quiet suppress most error messages\n\ ++ --reference=RFILE use RFILE's group instead of using a CONTEXT value\n\ ++ -u, --user=USER set user USER in the target security context\n\ ++ -r, --role=ROLE set role ROLE in the target security context\n\ ++ -t, --type=TYPE set type TYPE in the target security context\n\ ++ -l, --range=RANGE set range RANGE in the target security context\n\ ++ -R, --recursive change files and directories recursively\n\ ++ -v, --verbose output a diagnostic for every file processed\n\ ++ --help display this help and exit\n\ ++ --version output version information and exit\n\ ++")); ++ close_stdout (); ++ } ++ exit (status); ++} ++ ++int ++main (int argc, char **argv) ++{ ++ security_context_t ref_context = NULL; ++ int errors = 0; ++ int optc; ++ int component_specified = 0; ++ ++ program_name = argv[0]; ++ setlocale (LC_ALL, ""); ++ bindtextdomain (PACKAGE, LOCALEDIR); ++ textdomain (PACKAGE); ++ ++ recurse = force_silent = 0; ++ ++ while ((optc = getopt_long (argc, argv, "Rcfhvu:r:t:l:", long_options, NULL)) != -1) ++ { ++ switch (optc) ++ { ++ case 0: ++ break; ++ case 'u': ++ specified_user = optarg; ++ component_specified = 1; ++ break; ++ case 'r': ++ specified_role = optarg; ++ component_specified = 1; ++ break; ++ case 't': ++ specified_type = optarg; ++ component_specified = 1; ++ break; ++ case 'l': ++ specified_range = optarg; ++ component_specified = 1; ++ break; ++ case CHAR_MAX + 1: ++ reference_file = optarg; ++ break; ++ case 'R': ++ recurse = 1; ++ break; ++ case 'c': ++ verbosity = V_changes_only; ++ break; ++ case 'f': ++ force_silent = 1; ++ break; ++ case 'h': ++ change_symlinks = 1; ++ break; ++ case 'v': ++ verbosity = V_high; ++ break; ++ default: ++ usage (1); ++ } ++ } ++ ++ if (show_version) ++ { ++ printf ("chcon (%s) %s\n", GNU_PACKAGE, VERSION); ++ close_stdout (); ++ exit (0); ++ } ++ ++ if (show_help) ++ usage (0); ++ ++ ++ if (reference_file && component_specified) ++ { ++ error (0, 0, _("conflicting security context specifiers given")); ++ usage (1); ++ } ++ ++ if (!(((reference_file || component_specified) ++ && (argc - optind > 0)) ++ || (argc - optind > 1))) ++ { ++ error (0, 0, _("too few arguments")); ++ usage (1); ++ } ++ ++ if (reference_file) ++ { ++ if (getfilecon (reference_file, &ref_context)<0) ++ error (1, errno, "%s", reference_file); ++ ++ specified_context = ref_context; ++ } ++ else if (!component_specified) { ++ specified_context = argv[optind++]; ++ } ++ for (; optind < argc; ++optind) ++ errors |= change_file_context (argv[optind]); ++ ++ if (verbosity != V_off) ++ close_stdout (); ++ if (ref_context != NULL) ++ freecon(ref_context); ++ exit (errors); ++} +--- coreutils-5.2.1/src/copy.c.selinux 2004-03-16 14:25:05.417541928 -0500 ++++ coreutils-5.2.1/src/copy.c 2004-03-16 14:25:56.729741288 -0500 +@@ -42,6 +42,11 @@ + #include "utimens.h" + #include "xreadlink.h" + ++#ifdef WITH_SELINUX ++#include /* for is_selinux_enabled() */ ++extern int selinux_enabled; ++#endif ++ + #define DO_CHOWN(Chown, File, New_uid, New_gid) \ + (Chown (File, New_uid, New_gid) \ + /* If non-root uses -p, it's ok if we can't preserve ownership. \ +@@ -1288,6 +1293,34 @@ + In such cases, set this variable to zero. */ + preserve_metadata = 1; + ++#ifdef WITH_SELINUX ++ if (x->preserve_security_context && selinux_enabled) ++ { ++ security_context_t con; ++ ++ if (lgetfilecon (src_path, &con) >= 0) ++ { ++ if (setfscreatecon(con) < 0) ++ { ++ error (0, errno, _("cannot set setfscreatecon %s"), quote (con)); ++ if (x->require_preserve) { ++ freecon(con); ++ return 1; ++ } ++ } ++ freecon(con); ++ } ++ else { ++ if ( errno == ENOTSUP ) { ++ error (0, errno, _("warning: security context not preserved %s"), quote (src_path)); ++ } else { ++ error (0, errno, _("cannot lgetfilecon %s"), quote (src_path)); ++ return 1; ++ } ++ } ++ } ++#endif ++ + if (S_ISDIR (src_mode)) + { + struct dir_list *dir; +@@ -1357,8 +1390,13 @@ + } + + /* Are we crossing a file system boundary? */ +- if (x->one_file_system && device != 0 && device != src_sb.st_dev) ++ if (x->one_file_system && device != 0 && device != src_sb.st_dev) { ++#ifdef WITH_SELINUX ++ if (x->preserve_security_context && selinux_enabled) ++ setfscreatecon(NULL); ++#endif + return 0; ++ } -- basename cat chgrp chmod chown chroot cksum comm cp csplit cut date dd -+ basename cat chcon chgrp chmod chown chroot cksum comm cp csplit cut date dd - df dir dircolors dirname du echo env expand expr factor false fmt fold - ginstall groups head hostid 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 -+ printenv printf ptx pwd readlink rm rmdir runcon 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 + /* Copy the contents of the directory. */ ---- coreutils-5.0/configure.ac.selinux 2003-12-09 20:44:56.000000000 -0500 -+++ coreutils-5.0/configure.ac 2003-12-09 20:44:58.000000000 -0500 -@@ -17,6 +17,13 @@ - LIB_PAM="-ldl -lpam -lpam_misc" - AC_SUBST(LIB_PAM)]) +@@ -1491,6 +1529,11 @@ + } + } -+dnl Give the chance to enable PAM -+AC_ARG_ENABLE(selinux, dnl -+[ --enable-selinux Enable use of the SELINUX libraries], -+[AC_DEFINE(WITH_SELINUX, 1, [Define if you want to use SELINUX]) -+LIB_SELINUX="-lselinux" -+AC_SUBST(LIB_SELINUX)]) ++#ifdef WITH_SELINUX ++ if (x->preserve_security_context && selinux_enabled) ++ setfscreatecon(NULL); ++#endif ++ + /* There's no need to preserve timestamps or permissions. */ + preserve_metadata = 0; + +@@ -1523,7 +1566,7 @@ + if (command_line_arg) + record_file (x->dest_info, dst_path, NULL); + +- if ( ! preserve_metadata) ++ if ( ! preserve_metadata) + return 0; + + /* POSIX says that `cp -p' must restore the following: +@@ -1629,6 +1672,11 @@ + + un_backup: + ++#ifdef WITH_SELINUX ++ if (x->preserve_security_context && selinux_enabled) ++ setfscreatecon(NULL); ++#endif ++ + /* We have failed to create the destination file. + If we've just added a dev/ino entry via the remember_copied + call above (i.e., unless we've just failed to create a hard link), +--- coreutils-5.2.1/src/id.c.selinux 2004-01-21 17:27:02.000000000 -0500 ++++ coreutils-5.2.1/src/id.c 2004-03-16 14:25:05.695499672 -0500 +@@ -45,6 +45,20 @@ + + int getugroups (); + ++#ifdef WITH_SELINUX ++#include ++static void print_context (char* context); ++/* Print the SELinux context */ ++static void ++print_context(char *context) ++{ ++ printf ("%s", context); ++} ++ ++/* If nonzero, output only the SELinux context. -Z */ ++static int just_context = 0; ++ ++#endif + static void print_user (uid_t uid); + static void print_group (gid_t gid); + static void print_group_list (const char *username); +@@ -63,8 +77,14 @@ + /* Nonzero if errors have been encountered. */ + static int problems = 0; + ++/* The SELinux context */ ++/* Set `context' to a known invalid value so print_full_info() will * ++ * know when `context' has not been set to a meaningful value. */ ++static security_context_t context=NULL; ++ + static struct option const longopts[] = + { ++ {"context", no_argument, NULL, 'Z'}, + {"group", no_argument, NULL, 'g'}, + {"groups", no_argument, NULL, 'G'}, + {"name", no_argument, NULL, 'n'}, +@@ -88,6 +108,7 @@ + Print information for USERNAME, or the current user.\n\ + \n\ + -a ignore, for compatibility with other versions\n\ ++ -Z, --context print only the context\n\ + -g, --group print only the effective group ID\n\ + -G, --groups print all group IDs\n\ + -n, --name print a name instead of a number, for -ugG\n\ +@@ -109,6 +130,7 @@ + main (int argc, char **argv) + { + int optc; ++ int selinux_enabled=(is_selinux_enabled()>0); + + /* If nonzero, output the list of all group IDs. -G */ + int just_group_list = 0; +@@ -127,7 +149,7 @@ + + atexit (close_stdout); + +- while ((optc = getopt_long (argc, argv, "agnruG", longopts, NULL)) != -1) ++ while ((optc = getopt_long (argc, argv, "agnruGZ", longopts, NULL)) != -1) + { + switch (optc) + { +@@ -136,6 +158,17 @@ + case 'a': + /* Ignore -a, for compatibility with SVR4. */ + break; ++#ifdef WITH_SELINUX ++ case 'Z': ++ /* politely decline if we're not on a selinux-enabled kernel. */ ++ if( !selinux_enabled ) { ++ fprintf( stderr, "Sorry, --context (-Z) can be used only on " ++ "a selinux-enabled kernel.\n" ); ++ exit( 1 ); ++ } ++ just_context = 1; ++ break; ++#endif + case 'g': + just_group = 1; + break; +@@ -158,8 +191,28 @@ + } + } + +- if (just_user + just_group + just_group_list > 1) +- error (EXIT_FAILURE, 0, _("cannot print only user and only group")); ++#ifdef WITH_SELINUX ++ if (argc - optind == 1) ++ selinux_enabled = 0; ++ ++ if( just_context && !selinux_enabled) ++ error (1, 0, _("\ ++cannot display context when selinux not enabled or when displaying the id\n\ ++of a different user")); ++ ++ /* If we are on a selinux-enabled kernel, get our context. * ++ * Otherwise, leave the context variable alone - it has * ++ * been initialized known invalid value; if we see this invalid * ++ * value later, we will know we are on a non-selinux kernel. */ ++ if( selinux_enabled ) ++ { ++ if (getcon(&context)) ++ error (1, 0, "can't get process context"); ++ } ++#endif ++ ++ if (just_user + just_group + just_group_list + just_context > 1) ++ error (EXIT_FAILURE, 0, _("cannot print \"only\" of more than one choice")); + + if (just_user + just_group + just_group_list == 0 && (use_real || use_name)) + error (EXIT_FAILURE, 0, +@@ -190,6 +243,10 @@ + print_group (use_real ? rgid : egid); + else if (just_group_list) + print_group_list (argv[optind]); ++#ifdef WITH_SELINUX ++ else if (just_context) ++ print_context (context); ++#endif + else + print_full_info (argv[optind]); + putchar ('\n'); +@@ -397,4 +454,9 @@ + free (groups); + } + #endif /* HAVE_GETGROUPS */ ++#ifdef WITH_SELINUX ++ if ( context != NULL ) { ++ printf(" context=%s",context); ++ } ++#endif + } +--- coreutils-5.2.1/src/mv.c.selinux 2004-03-16 14:25:05.401544360 -0500 ++++ coreutils-5.2.1/src/mv.c 2004-03-16 14:25:25.906427144 -0500 +@@ -34,6 +34,11 @@ + #include "quote.h" + #include "remove.h" + ++#ifdef WITH_SELINUX ++#include /* for is_selinux_enabled() */ ++int selinux_enabled=0; ++#endif ++ + /* The official name of this program (e.g., no `g' prefix). */ + #define PROGRAM_NAME "mv" + +@@ -124,6 +129,9 @@ + x->preserve_links = 1; + x->preserve_mode = 1; + x->preserve_timestamps = 1; ++#ifdef WITH_SELINUX ++ x->preserve_security_context = 1; ++#endif + x->require_preserve = 0; /* FIXME: maybe make this an option */ + x->recursive = 1; + x->sparse_mode = SPARSE_AUTO; /* FIXME: maybe make this an option */ +@@ -376,6 +384,10 @@ + + cp_option_init (&x); + ++#ifdef WITH_SELINUX ++ selinux_enabled= (is_selinux_enabled()>0); ++#endif ++ + /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless + we'll actually use backup_suffix_string. */ + backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); +--- /dev/null 2004-02-23 16:02:56.000000000 -0500 ++++ coreutils-5.2.1/src/runcon.c 2004-03-16 14:25:05.701498760 -0500 +@@ -0,0 +1,174 @@ ++/* ++ * runcon [ context | ++ * ( [ -r role ] [-t type] [ -u user ] [ -l levelrange ] ) ++ * command [arg1 [arg2 ...] ] ++ * ++ * attempt to run the specified command with the specified context. ++ * ++ * -r role : use the current context with the specified role ++ * -t type : use the current context with the specified type ++ * -u user : use the current context with the specified user ++ * -l level : use the current context with the specified level range ++ * ++ * Contexts are interpreted as follows: ++ * ++ * Number of MLS ++ * components system? ++ * ++ * 1 - type ++ * 2 - role:type ++ * 3 Y role:type:range ++ * 3 N user:role:type ++ * 4 Y user:role:type:range ++ * 4 N error ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "system.h" ++extern int errno; + - jm_PERL - AC_PROG_CC - AC_PROG_CPP ---- coreutils-5.0/config.hin.selinux 2003-12-09 20:44:56.000000000 -0500 -+++ coreutils-5.0/config.hin 2003-12-09 20:44:58.000000000 -0500 -@@ -504,9 +504,6 @@ - /* Define to 1 if you have the `lchown' function. */ - #undef HAVE_LCHOWN - --/* Define to 1 if you have the `acl' library (-lacl). */ --#undef HAVE_LIBACL -- - /* Define to 1 if you have the `dgc' library (-ldgc). */ - #undef HAVE_LIBDGC ++/* The name the program was run with. */ ++char *program_name; ++ ++void ++usage(char *str) ++{ ++ printf(_("Usage: %s [OPTION]... command [args]\n" ++ "Run a program in a different security context.\n\n" ++ " context Complete security context\n" ++ " -t type (for same role as parent)\n" ++ " -u user identity\n" ++ " -r role\n" ++ " -l levelrange\n" ++ " --help display this help and exit\n"), ++ program_name); ++ exit(1); ++} ++ ++int ++main(int argc,char **argv,char **envp ) ++{ ++ char *role = 0; ++ char *range = 0; ++ char *user = 0; ++ char *type = 0; ++ char *context = NULL; ++ security_context_t cur_context = NULL; ++ ++ context_t con; ++ ++ program_name = argv[0]; ++ setlocale (LC_ALL, ""); ++ bindtextdomain (PACKAGE, LOCALEDIR); ++ textdomain (PACKAGE); ++ ++ while (1) { ++ int c; ++ int this_option_optind = optind ? optind : 1; ++ int option_index = 0; ++ static struct option long_options[] = { ++ { "role", 1, 0, 'r' }, ++ { "type", 1, 0, 't' }, ++ { "user", 1, 0, 'u' }, ++ { "range", 1, 0, 'l' }, ++ { "help", 0, 0, '?' }, ++ { 0, 0, 0, 0 } ++ }; ++ c = getopt_long(argc, argv, "s:r:t:u:l:?", long_options, &option_index); ++ if ( c == -1 ) { ++ break; ++ } ++ switch ( c ) { ++ case 'r': ++ if ( role ) { ++ fprintf(stderr,_("multiple roles\n")); ++ exit(1); ++ } ++ role = optarg; ++ break; ++ case 't': ++ if ( type ) { ++ fprintf(stderr,_("multiple types\n")); ++ exit(1); ++ } ++ type = optarg; ++ break; ++ case 'u': ++ if ( user ) { ++ fprintf(stderr,_("multiple users\n")); ++ exit(1); ++ } ++ user = optarg; ++ break; ++ case 'l': ++ if ( range ) { ++ fprintf(stderr,_("multiple levelranges\n")); ++ exit(1); ++ } ++ range = optarg; ++ break; ++ default: ++ fprintf(stderr,_("unrecognised option %c\n"),c); ++ case '?': ++ usage(0); ++ break; ++ } ++ } ++ if ( !(user || role || type || range)) { ++ if ( optind >= argc ) { ++ usage(_("must specify -t, -u, -l, -r, or context")); ++ } ++ context = argv[optind++]; ++ } ++ ++ if ( optind >= argc ) { ++ usage(_("no command found")); ++ } ++ ++ if ( context ) { ++ con = context_new(context); ++ if (!con) { ++ fprintf(stderr,_("%s is not a valid context\n"), context); ++ exit(1); ++ } ++ } ++ else { ++ getcon(&cur_context); ++ con = context_new(cur_context); ++ if (!con) { ++ fprintf(stderr,_("%s is not a valid context\n"), context); ++ exit(1); ++ } ++ if ( user ) { ++ context_user_set(con,user); ++ } ++ if ( type ) { ++ context_type_set(con,type); ++ } ++ if ( range ) { ++ context_range_set(con,range); ++ } ++ if ( role ) { ++ context_role_set(con,role); ++ } ++ } ++ ++ if (setexeccon(context_str(con))!=0) { ++ fprintf(stderr,_("unable to setup security context %s\n"), context_str(con)); ++ exit(1); ++ } ++ if (cur_context!=NULL) ++ freecon(cur_context); ++ ++ if ( execvp(argv[optind],argv+optind) ) { ++ perror("execvp"); ++ exit(1); ++ } ++ return 1; /* can't reach this statement.... */ ++} +--- coreutils-5.2.1/src/install.c.selinux 2004-03-16 14:25:05.408543296 -0500 ++++ coreutils-5.2.1/src/install.c 2004-03-16 14:25:05.704498304 -0500 +@@ -47,6 +47,11 @@ + # include + #endif -@@ -1309,18 +1306,24 @@ - . */ - #undef UMAX4_3 ++#ifdef WITH_SELINUX ++#include /* for is_selinux_enabled() */ ++int selinux_enabled=0; ++#endif ++ + struct passwd *getpwnam (); + struct group *getgrnam (); --/* the maximum number of simultaneously open files per process */ --#undef UTILS_OPEN_MAX -- - /* Define if you want access control list support. */ - #undef USE_ACL +@@ -123,11 +128,17 @@ + static struct option const long_options[] = + { + {"backup", optional_argument, NULL, 'b'}, ++#ifdef WITH_SELINUX ++ {"context", required_argument, NULL, 'Z'}, ++#endif + {"directory", no_argument, NULL, 'd'}, + {"group", required_argument, NULL, 'g'}, + {"mode", required_argument, NULL, 'm'}, + {"owner", required_argument, NULL, 'o'}, + {"preserve-timestamps", no_argument, NULL, 'p'}, ++#ifdef WITH_SELINUX ++ {"preserve_context", no_argument, NULL, 'P'}, ++#endif + {"strip", no_argument, NULL, 's'}, + {"suffix", required_argument, NULL, 'S'}, + {"version-control", required_argument, NULL, 'V'}, /* Deprecated. FIXME. */ +@@ -244,6 +255,9 @@ -+/* Define if you want to use PAM */ -+#undef USE_PAM -+ -+/* the maximum number of simultaneously open files per process */ -+#undef UTILS_OPEN_MAX -+ - /* Version number of package */ - #undef VERSION + x->update = 0; + x->verbose = 0; ++#ifdef WITH_SELINUX ++ x->preserve_security_context = 0; ++#endif + x->dest_info = NULL; + x->src_info = NULL; + } +@@ -261,6 +275,11 @@ + struct cp_options x; + int n_files; + char **file; ++#ifdef WITH_SELINUX ++ security_context_t scontext = NULL; ++ /* set iff kernel has extra selinux system calls */ ++ selinux_enabled = (is_selinux_enabled()>0); ++#endif - /* Define if sys/ptem.h is required for struct winsize. */ - #undef WINSIZE_IN_PTEM + initialize_main (&argc, &argv); + program_name = argv[0]; +@@ -282,7 +301,11 @@ + we'll actually use backup_suffix_string. */ + backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); -+/* Define if you want to use SELINUX */ -+#undef WITH_SELINUX ++#ifdef WITH_SELINUX ++ while ((optc = getopt_long (argc, argv, "bcCsDdg:m:o:pPvV:S:Z:", long_options, ++#else + while ((optc = getopt_long (argc, argv, "bcCsDdg:m:o:pvV:S:", long_options, ++#endif + NULL)) != -1) + { + switch (optc) +@@ -335,6 +358,39 @@ + make_backups = 1; + backup_suffix_string = optarg; + break; ++#ifdef WITH_SELINUX ++ case 'P': ++ /* politely decline if we're not on a selinux-enabled kernel. */ ++ if( !selinux_enabled ) { ++ fprintf( stderr, "Warning: ignoring --preserve_context (-P) " ++ "because the kernel is not selinux-enabled.\n" ); ++ break; ++ } ++ if ( scontext!=NULL ) { /* scontext could be NULL because of calloc() failure */ ++ (void) fprintf(stderr, "%s: cannot force target context to '%s' and preserve it\n", argv[0], scontext); ++ exit( 1 ); ++ } ++ x.preserve_security_context = 1; ++ break ; ++ case 'Z': ++ /* politely decline if we're not on a selinux-enabled kernel. */ ++ if( !selinux_enabled) { ++ fprintf( stderr, "Warning: ignoring --context (-Z) " ++ "because the kernel is not selinux-enabled.\n" ); ++ break; ++ } ++ if ( x.preserve_security_context ) { + - /* Define to 1 if your processor stores words with the most significant byte - first (like Motorola and SPARC, unlike Intel and VAX). */ - #undef WORDS_BIGENDIAN ++ (void) fprintf(stderr, "%s: cannot force target context == '%s' and preserve it\n", argv[0], optarg); ++ exit( 1 ); ++ } ++ scontext = optarg; ++ if (setfscreatecon(scontext)) { ++ (void) fprintf(stderr, "%s: cannot setup default context == '%s'\n", argv[0], scontext); ++ exit(1); ++ } ++ break; ++#endif + case_GETOPT_HELP_CHAR; + case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); + default: +@@ -716,6 +772,11 @@ + -S, --suffix=SUFFIX override the usual backup suffix\n\ + -v, --verbose print the name of each directory as it is created\n\ + "), stdout); ++ fputs (_("\ ++ -P, --preserve_context (SELinux) Preserve security context\n\ ++ -Z, --context=CONTEXT (SELinux) Set security context of files and directories\n\ ++"), stdout); ++ + fputs (HELP_OPTION_DESCRIPTION, stdout); + fputs (VERSION_OPTION_DESCRIPTION, stdout); + fputs (_("\ diff --git a/coreutils.spec b/coreutils.spec index e50eb59..aef0543 100644 --- a/coreutils.spec +++ b/coreutils.spec @@ -3,8 +3,8 @@ %endif Summary: The GNU core utilities: a set of tools commonly used in shell scripts Name: coreutils -Version: 5.0 -Release: 39 +Version: 5.2.1 +Release: 3 License: GPL Group: System Environment/Base Url: ftp://alpha.gnu.org/gnu/coreutils/ @@ -12,73 +12,41 @@ Url: ftp://alpha.gnu.org/gnu/coreutils/ BuildRequires: libselinux-devel %endif -Source0: ftp://prep.ai.mit.edu/pub/gnu/%name/%name-%version.tar.bz2 +Source0: ftp://ftp.gnu.org/gnu/%{name}/%{name}-%{version}.tar.bz2 Source101: DIR_COLORS Source102: DIR_COLORS.xterm Source105: colorls.sh Source106: colorls.csh Source200: su.pamd -Source201: help2man # fileutils -Patch101: fileutils-4.0-spacedir.patch -Patch102: fileutils-4.0s-sparc.patch -Patch103: coreutils-4.5.2-trunc.patch Patch105: coreutils-4.5.2-C.patch Patch107: fileutils-4.1.10-timestyle.patch Patch108: fileutils-4.1.5-afs.patch -Patch111: coreutils-4.5.2-dumbterm.patch -Patch112: fileutils-4.0u-glibc22.patch -Patch114: fileutils-4.1-restorecolor.patch -Patch115: fileutils-4.1.1-FBoptions.patch -Patch1155: fileutils-4.1-force-option--override--interactive-option.patch Patch116: fileutils-4.1-dircolors_c.patch -Patch117: fileutils-4.1-ls_c.patch -Patch118: fileutils-4.1-ls_h.patch Patch153: fileutils-4.1.10-utmp.patch -Patch182: coreutils-4.5.3-acl.patch -Patch183: coreutils-4.5.3-aclcompile.patch -Patch188: coreutils-4.5.3-suidfail.patch -Patch189: coreutils-4.5.3-stoneage.patch - -# textutils -Patch502: textutils-2.0.21-man.patch +Patch182: coreutils-acl.patch # sh-utils -Patch702: sh-utils-2.0-utmp.patch Patch703: sh-utils-2.0.11-dateman.patch Patch704: sh-utils-1.16-paths.patch # RMS will never accept the PAM patch because it removes his historical # rant about Twenex and the wheel group, so we'll continue to maintain # it here indefinitely. -Patch706: coreutils-4.5.2-pam.patch -Patch710: sh-utils-2.0-rfc822.patch -Patch711: coreutils-4.5.3-hname.patch -Patch712: coreutils-4.5.3-chdir.patch +Patch706: coreutils-pam.patch Patch713: coreutils-4.5.3-langinfo.patch Patch714: coreutils-4.5.3-printf-ll.patch Patch715: coreutils-4.5.3-sysinfo.patch -Patch716: coreutils-4.5.3-nogetline.patch # (sb) lin18nux/lsb compliance -Patch800: coreutils-4.5.3-i18n.patch +Patch800: coreutils-i18n.patch -# Think the test suite failure is a bug.. -Patch900: coreutils-4.5.3-test-bugs.patch -Patch901: coreutils-4.5.3-signal.patch -Patch903: coreutils-4.5.3-manpage.patch Patch904: coreutils-5.0-allow_old_options.patch -Patch905: coreutils-5.0-90563.patch -Patch906: coreutils-5.0-datealign.patch -Patch907: coreutils-largefile.patch -Patch908: coreutils-5.0-md5.patch -Patch909: coreutils-lsw.patch -Patch910: coreutils-lsw2.patch -Patch911: coreutils-nonerequired.patch #SELINUX Patch %if %{WITH_SELINUX} Patch950: coreutils-selinux.patch +Patch951: coreutils-ls-stat.patch %endif BuildRoot: %_tmppath/%{name}-root @@ -106,64 +74,34 @@ the old GNU fileutils, sh-utils, and textutils packages. %setup -q # fileutils -%patch101 -p1 -b .space -%patch102 -p1 -b .sparc -%patch103 -p0 -b .trunc %patch105 -p0 -b .Coption %patch107 -p1 -b .timestyle %patch108 -p1 -b .afs -%patch111 -p0 -b .dumbterm -%patch112 -p1 -b .glibc22 -%patch114 -p1 -b .restore -%patch115 -p1 -b .FBopts -%patch1155 -p1 %patch116 -p1 -%patch117 -p1 -%patch118 -p1 %patch153 -p1 %patch182 -p1 -b .acl -%patch183 -p1 -b .aclcompile -%patch188 -p1 -b .suidfail -%patch189 -p1 -b .stoneage - -# textutils -# patch in new ALL_LINGUAS -%patch502 -p1 # sh-utils -%patch702 -p1 -b .utmp %patch703 -p1 -b .dateman %patch704 -p1 -b .paths %patch706 -p1 -b .pam -%patch710 -p1 -b .rfc822 -%patch711 -p1 -b .hname -%patch712 -p1 -b .chdir %patch713 -p1 -b .langinfo %patch714 -p1 -b .printf-ll %patch715 -p1 -b .sysinfo -%patch716 -p1 -b .nogetline # li18nux/lsb %patch800 -p1 -b .i18n # Coreutils -%patch900 -p1 -b .test-bugs -%patch901 -p1 -b .signal -%patch903 -p1 -b .manpage %patch904 -p1 -b .allow_old_options -%patch905 -p0 -b .90563 -%patch906 -p1 -b .datealign -%patch907 -p1 -b .largefile -%patch908 -p1 -b .md5 -%patch909 -p1 -b .lsw -%patch910 -p1 -b .lsw2 -%patch911 -p1 -b .nonerequired %if %{WITH_SELINUX} #SELinux %patch950 -p1 -b .selinux +%patch951 -p1 -b .ls-stat %endif + # Don't run basic-1 test, since it breaks when run in the background # (bug #102033). perl -pi -e 's/basic-1//g' tests/stty/Makefile* @@ -171,14 +109,10 @@ perl -pi -e 's/basic-1//g' tests/stty/Makefile* %build %{expand:%%global optflags %{optflags} -D_GNU_SOURCE=1} touch aclocal.m4 configure config.hin Makefile.in */Makefile.in */*/Makefile.in -cp %SOURCE201 man/help2man -chmod +x man/help2man -HELP2MAN=$(pwd)/man/help2man -export HELP2MAN aclocal -I m4 autoconf --force automake --copy --force -%configure --enable-largefile %{?!nopam:--enable-pam} \ +%configure --enable-largefile --with-afs %{?!nopam:--enable-pam} \ %if %{WITH_SELINUX} --enable-selinux \ %endif @@ -233,14 +167,12 @@ install -c -m755 %SOURCE106 $RPM_BUILD_ROOT/etc/profile.d install -m 4755 src/su $RPM_BUILD_ROOT/bin # These come from util-linux and/or procps. -for i in hostname uptime ; do +for i in hostname uptime kill ; do rm -f $RPM_BUILD_ROOT{%_bindir/$i,%_mandir/man1/${i}.1} done %{?!nopam:install -m 644 %SOURCE200 $RPM_BUILD_ROOT%_sysconfdir/pam.d/su} -ln -sf test $RPM_BUILD_ROOT%_bindir/[ - bzip2 -f9 old/*/C* || : %find_lang %name @@ -293,6 +225,65 @@ fi %_sbindir/chroot %changelog +* Tue Mar 16 2004 Dan Walsh 5.2.1-3 +- If preserve fails, report as warning unless user requires preserve + +* Tue Mar 16 2004 Dan Walsh 5.2.1-2 +- Make mv default to preserve on context + +* Sat Mar 13 2004 Tim Waugh 5.2.1-1 +- 5.2.1. + +* Fri Mar 12 2004 Tim Waugh 5.2.0-9 +- Add '-Z' to 'ls --help' output (bug #118108). + +* Fri Mar 5 2004 Tim Waugh +- Fix deref-args test case for rebuilding under SELinux (bug #117556). + +* Wed Feb 25 2004 Tim Waugh 5.2.0-8 +- kill(1) offloaded to util-linux altogether. + +* Tue Feb 24 2004 Tim Waugh 5.2.0-7 +- Ship the real '[', not a symlink. + +* Mon Feb 23 2004 Tim Waugh 5.2.0-6 +- Apply Paul Eggert's chown patch (bug #116536). +- Merged chdir patch into pam patch where it belongs. + +* Mon Feb 23 2004 Tim Waugh 5.2.0-5 +- Fixed i18n patch bug causing sort -M not to work (bug #116575). + +* Sat Feb 21 2004 Tim Waugh 5.2.0-4 +- Reinstate kill binary, just not its man page (bug #116463). + +* Sat Feb 21 2004 Tim Waugh 5.2.0-3 +- Updated ls-stat patch. + +* Fri Feb 20 2004 Dan Walsh 5.2.0-2 +- fix chcon to ignore . and .. directories for recursing + +* Fri Feb 20 2004 Tim Waugh 5.2.0-1 +- Patch ls so that failed stat() is handled gracefully (Ulrich Drepper). +- 5.2.0. + +* Thu Feb 19 2004 Tim Waugh +- More AFS patch tidying. + +* Wed Feb 18 2004 Dan Walsh 5.1.3-0.2 +- fix chcon to handle -h qualifier properly, eliminate potential crash + +* Wed Feb 18 2004 Tim Waugh +- Stop 'sort -g' leaking memory (i18n patch bug #115620). +- Don't ship kill, since util-linux already does. +- Tidy AFS patch. + +* Mon Feb 16 2004 Tim Waugh 5.1.3-0.1 +- 5.1.3. +- Patches ported forward or removed. + +* Fri Feb 13 2004 Elliot Lee 5.0-40 +- rebuilt + * Tue Jan 20 2004 Dan Walsh 5.0-39 - Change /etc/pam.d/su to remove preservuser and add multiple diff --git a/sources b/sources index 8d26bae..9c1aa50 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -94e5558ee2a65723d4840bfde2d323f0 coreutils-5.0.tar.bz2 +172ee3c315af93d3385ddfbeb843c53f coreutils-5.2.1.tar.bz2