1
0
mirror of git://git.sv.gnu.org/coreutils.git synced 2026-04-18 17:56:54 +02:00

merge from trunk

This commit is contained in:
Jim Meyering
2007-01-26 11:15:31 +01:00
parent cece28a8bc
commit ebc2235bd7
17 changed files with 885 additions and 116 deletions

129
ChangeLog
View File

@@ -1,3 +1,132 @@
2007-01-26 Jim Meyering <jim@meyering.net>
* .x-sc_cast_of_argument_to_free: Remove this file.
* Makefile.am (EXTRA_DIST): Likewise.
2007-01-25 Dan Hipschman <dsh@linux.ucla.edu>
* src/sort.c (create_temp): Remove superfluous access-X_OK
check. find_in_path does this for us.
2007-01-24 Jim Meyering <jim@meyering.net>
Remove usually-skipped test.
* tests/cp/open-perm-race: Remove this file. It is subsumed
by parent-perm-race.
* tests/cp/Makefile.am (TESTS): Remove open-perm-race.
* tests/sort/Makefile.am: Regenerate.
Pass "make distcheck" again.
* src/sort.c (usage): Split a diagnostic that had grown to be
longer than the C89 maximum of 509 bytes.
* .x-sc_cast_of_argument_to_free: New file. Allow a cast in sort.c.
FIXME: this is just temporary, while we wait to remove the offending
access-calling code.
* Makefile.am (EXTRA_DIST): Add .x-sc_cast_of_argument_to_free.
* Makefile.maint (sc_cast_of_argument_to_free): Use the
canonical, $$($(CVS_LIST_EXCEPT)).
* m4/.gitignore, m4/.cvsignore, lib/.gitignore, lib/.cvsignore: Update.
2007-01-24 Paul Eggert <eggert@cs.ucla.edu>
* NEWS: New option sort -C, proposed by XCU ERN 127, which looks
like it will be approved. Also add --check=quiet, --check=silent
as long aliases, and --check=diagnose-first as an alias for -c.
* doc/coreutils.texi (sort invocation): Document this.
Also, mention that sort -c can take at most one file.
* src/sort.c: Implement this.
Include argmatch.h.
(usage): Document the change.
(CHECK_OPTION): New constant.
(long_options): --check now takes an optional argument, and is now
treated differently from 'c'.
(check_args, check_types): New constant arrays.
(check): New arg CHECKONLY, which suppresses diagnostic if -C.
(main): Parse the new options.
* tests/sort/Test.pm (02d, 02d, incompat5, incompat6):
New tests for -C.
2007-01-24 Jim Meyering <jim@meyering.net>
Fix a typo.
* tests/misc/sort-compress: Use $abs_top_builddir, not $top_builddir.
* tests/misc/Makefile.am (TESTS_ENVIRONMENT): Likewise.
Don't depend on "which".
* tests/misc/sort-compress (SORT): Use $abs_builddir, now which.
* tests/misc/Makefile.am (TESTS_ENVIRONMENT): Export top_builddir.
2007-01-24 Dan Hipschman <dsh@linux.ucla.edu>
Test sort compression.
* tests/misc/Makefile.am: Add the test.
* tests/misc/sort-compress: New file containing the tests.
2007-01-24 Jim Meyering <jim@meyering.net>
* NEWS: sort temp file compression: tweak wording.
* src/sort.c (struct sortfile) [name]: Declare member to be const.
2007-01-21 Jim Meyering <jim@meyering.net>
* src/sort.c (MAX_FORK_RETRIES_COMPRESS, MAX_FORK_RETRIES_DECOMPRESS):
In pipe_fork callers, use these named constants, not "2" and "8".
(proctab, nprocs): Declare to be "static".
(pipe_fork) [lint]: Initialize local, pid,
to avoid unwarranted may-be-used-uninitialized warning.
(create_temp): Use the active voice. Describe parameters, too.
2007-01-21 James Youngman <jay@gnu.org>
Centralize all the uses of sigprocmask(). Don't restore an invalid
saved mask.
* src/sort.c (enter_cs, leave_cs): New functions for protecting
code sequences against signal delivery.
* (exit_cleanup): Use enter_cs and leave_cs instead of
calling sigprocmask directly.
(create_temp_file, pipe_fork, zaptemp): Likewise
2007-01-21 Dan Hipschman <dsh@linux.ucla.edu>
Add compression of temp files to sort.
* NEWS: Mention this.
* bootstrap.conf: Import findprog.
* configure.ac: Add AC_FUNC_FORK.
* doc/coreutils.texi: Document GNUSORT_COMPRESSOR environment
variable.
* src/sort.c (compress_program): New global, holds the name of the
external compression program.
(struct sortfile): New type used by mergepfs and friends instead
of filenames to hold PIDs of compressor processes.
(proctab): New global, holds compressor PIDs on which to wait.
(enum procstate, struct procnode): New types used by proctab.
(proctab_hasher, proctab_comparator): New functions for proctab.
(nprocs): New global, number of forked but unreaped children.
(reap, reap_some): New function, wait for/cleanup forked processes.
(register_proc, update_proc, wait_proc): New functions for adding,
modifying and removing proctab entries.
(create_temp_file): Change parameter type to pointer to file
descriptor, and return type to pointer to struct tempnode.
(dup2_or_die): New function used in create_temp and open_temp.
(pipe_fork): New function, creates a pipe and child process.
(create_temp): Creates a temp file and possibly a compression
program to which we filter output.
(open_temp): Opens a compressed temp file and creates a
decompression process through which to filter the input.
(mergefps): Change FILES parameter type to struct sortfile array
and update access accordingly. Use open_temp and reap_some.
(avoid_trashing_input, merge): Change FILES parameter like
mergefps and call create_temp instead of create_temp_file.
(sort): Call create_temp instead of create_temp_file.
Use reap_some.
(avoid_trashing_input, merge, sort, main): Adapt to mergefps.
2007-01-20 Jim Meyering <jim@meyering.net>
* tests/misc/pwd-long: Work properly even when run from the
wrong one of two or more bind-mounted sibling directories.
Suggestion from Mike Stone in <http://bugs.debian.org/380552>.
2007-01-20 Paul Eggert <eggert@cs.ucla.edu>
Standardize on list of signals when an app catches signals.

View File

@@ -1,7 +1,6 @@
# Make coreutils. -*-Makefile-*-
# Copyright (C) 1990, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
# 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
# Copyright (C) 1990, 1993-2007 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by

View File

@@ -98,7 +98,7 @@ syntax-check: $(syntax-check-rules)
# FIXME: don't allow `#include .strings\.h' anywhere
sc_cast_of_argument_to_free:
@grep -nE '\<free \(\(' $(srcdir)/{lib,src}/*.[chly] && \
@grep -nE '\<free \(\(' $$($(CVS_LIST_EXCEPT)) && \
{ echo '$(ME): don'\''t cast free argument' 1>&2; \
exit 1; } || :

13
NEWS
View File

@@ -29,6 +29,19 @@ GNU coreutils NEWS -*- outline -*-
"rm --interactive=never F" no longer prompts for an unwritable F
** New features
By default, sort usually compresses each temporary file it writes.
When sorting very large inputs, this can result in sort using far
less temporary disk space and in improved performance.
** New features
sort accepts the new option -C, which acts like -c except no diagnostic
is printed. Its --check option now accepts an optional argument, and
--check=quiet and --check=silent are now aliases for -C, while
--check=diagnose-first is an alias for -c or plain --check.
* Noteworthy changes in release 6.7 (2006-12-08) [stable]

View File

@@ -43,7 +43,8 @@ gnulib_modules="
config-h configmake
closeout cycle-check d-ino d-type diacrit dirfd dirname dup2
error euidaccess exclude exitfail fchdir fcntl fcntl-safer fdl
file-type fileblocks filemode filenamecat fnmatch-gnu fopen-safer
file-type fileblocks filemode filenamecat findprog fnmatch-gnu
fopen-safer
fprintftime fsusage ftruncate fts getdate getgroups gethrxtime
getline getloadavg getndelim2 getopt getpagesize getpass-gnu
gettext gettime gettimeofday getugroups getusershell gnupload

View File

@@ -39,6 +39,8 @@ gl_EARLY
gl_INIT
coreutils_MACROS
AC_FUNC_FORK
AC_CHECK_FUNCS(uname,
OPTIONAL_BIN_PROGS="$OPTIONAL_BIN_PROGS uname\$(EXEEXT)"
MAN="$MAN uname.1")

View File

@@ -3342,12 +3342,26 @@ mode:
@item -c
@itemx --check
@itemx --check=diagnose-first
@opindex -c
@opindex --check
@cindex checking for sortedness
Check whether the given files are already sorted: if they are not all
sorted, print an error message and exit with a status of 1.
Check whether the given file is already sorted: if it is not all
sorted, print a diagnostic containing the first out-of-order line and
exit with a status of 1.
Otherwise, exit successfully.
At most one input file can be given.
@item -C
@itemx --check=quiet
@itemx --check=silent
@opindex -c
@opindex --check
@cindex checking for sortedness
Exit successfully if the given file is already sorted, and
exit with status 1 otherwise.
At most one input file can be given.
This is like @option{-c}, except it does not print a diagnostic.
@item -m
@itemx --merge
@@ -3401,7 +3415,7 @@ Exit status:
@display
0 if no error occurred
1 if invoked with @option{-c} and the input is not properly sorted
1 if invoked with @option{-c} or @option{-C} and the input is not sorted
2 if an error occurred
@end display
@@ -3411,6 +3425,19 @@ value as the directory for temporary files instead of @file{/tmp}. The
@option{--temporary-directory} (@option{-T}) option in turn overrides
the environment variable.
@vindex GNUSORT_COMPRESSOR
To improve performance when sorting very large files, GNU sort will,
by default, try to compress temporary files with the program
@file{gzip}. The environment variable @env{GNUSORT_COMPRESSOR} can be
set to the name of another program to be used. The program specified
must compress standard input to standard output when no arguments are
given to it, and it must decompress standard input to standard output
when the @option{-d} argument is given to it. If the program exits
with nonzero status, sort will terminate with an error. To disable
compression of temporary files, set the variable to the empty string.
Whitespace and the backslash character should not appear in the
program name. They are reserved for future use.
The following options affect the ordering of output lines. They may be
specified globally or as part of a specific key field. If no key
@@ -3703,7 +3730,7 @@ disks and controllers.
@cindex uniquifying output
Normally, output only the first of a sequence of lines that compare
equal. For the @option{--check} (@option{-c}) option,
equal. For the @option{--check} (@option{-c} or @option{-C}) option,
check that no pair of consecutive lines compares equal.
This option also disables the default last-resort comparison.

View File

@@ -40,6 +40,7 @@ close-stream.c
close-stream.h
closeout.c
closeout.h
concatpath.c
config.charset
config.h
config.hin
@@ -85,6 +86,8 @@ filemode.c
filemode.h
filenamecat.c
filenamecat.h
findprog.c
findprog.h
fnmatch.c
fnmatch.h
fnmatch_.h
@@ -220,6 +223,7 @@ openat-proc.c
openat.c
openat.h
pathmax.h
pathname.h
physmem.c
physmem.h
pipe-safer.c

4
lib/.gitignore vendored
View File

@@ -37,6 +37,7 @@ close-stream.c
close-stream.h
closeout.c
closeout.h
concatpath.c
config.charset
configmake.h
creat-safer.c
@@ -80,6 +81,8 @@ filemode.c
filemode.h
filenamecat.c
filenamecat.h
findprog.c
findprog.h
fnmatch.c
fnmatch.h
fnmatch_.h
@@ -214,6 +217,7 @@ openat-proc.c
openat.c
openat.h
pathmax.h
pathname.h
physmem.c
physmem.h
pipe-safer.c

View File

@@ -30,6 +30,7 @@ dirname.m4
dos.m4
double-slash-root.m4
dup2.m4
eaccess.m4
eealloc.m4
eoverflow.m4
error.m4
@@ -44,6 +45,7 @@ file-type.m4
fileblocks.m4
filemode.m4
filenamecat.m4
findprog.m4
flexmember.m4
fnmatch.m4
fpending.m4

2
m4/.gitignore vendored
View File

@@ -29,6 +29,7 @@ dirname.m4
dos.m4
double-slash-root.m4
dup2.m4
eaccess.m4
eealloc.m4
eoverflow.m4
error.m4
@@ -43,6 +44,7 @@ file-type.m4
fileblocks.m4
filemode.m4
filenamecat.m4
findprog.m4
flexmember.m4
fnmatch.m4
fpending.m4

View File

@@ -1,5 +1,5 @@
/* sort - sort lines of text (with all kinds of options).
Copyright (C) 1988, 1991-2006 Free Software Foundation, Inc.
Copyright (C) 1988, 1991-2007 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -25,10 +25,14 @@
#include <getopt.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include "system.h"
#include "argmatch.h"
#include "error.h"
#include "findprog.h"
#include "hard-locale.h"
#include "hash.h"
#include "inttostr.h"
#include "md5.h"
#include "physmem.h"
@@ -63,7 +67,8 @@ struct rlimit { size_t rlim_cur; };
present. */
#ifndef SA_NOCLDSTOP
# define SA_NOCLDSTOP 0
# define sigprocmask(How, Set, Oset) /* empty */
/* No sigprocmask. Always 'return' zero. */
# define sigprocmask(How, Set, Oset) (0)
# define sigset_t int
# if ! HAVE_SIGINTERRUPT
# define siginterrupt(sig, flag) /* empty */
@@ -92,6 +97,20 @@ enum
SORT_FAILURE = 2
};
enum
{
/* The number of times we should try to fork a compression process
(we retry if the fork call fails). We don't _need_ to compress
temp files, this is just to reduce disk access, so this number
can be small. */
MAX_FORK_TRIES_COMPRESS = 2,
/* The number of times we should try to fork a decompression process.
If we can't fork a decompression process, we can't sort, so this
number should be big. */
MAX_FORK_TRIES_DECOMPRESS = 8
};
/* The representation of the decimal point in the current locale. */
static int decimal_point;
@@ -261,6 +280,9 @@ static bool have_read_stdin;
/* List of key field comparisons to be tried. */
static struct keyfield *keylist;
/* Program used to (de)compress temp files. Must accept -d. */
static const char *compress_program;
static void sortlines_temp (struct line *, size_t, struct line *);
/* Report MESSAGE for FILE, then clean up and exit.
@@ -315,9 +337,12 @@ Ordering options:\n\
fputs (_("\
Other options:\n\
\n\
-c, --check check whether input is sorted; do not sort\n\
-c, --check, --check=diagnose-first check for sorted input; do not sort\n\
-C, --check=quiet, --check=silent like -c, but do not report first bad line\n\
-k, --key=POS1[,POS2] start a key at POS1, end it at POS2 (origin 1)\n\
-m, --merge merge already sorted files; do not sort\n\
"), stdout);
fputs (_("\
-o, --output=FILE write result to FILE instead of standard output\n\
-s, --stable stabilize sort by disabling last-resort comparison\n\
-S, --buffer-size=SIZE use SIZE for main memory buffer\n\
@@ -364,15 +389,16 @@ native byte values.\n\
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum
{
RANDOM_SOURCE_OPTION = CHAR_MAX + 1
CHECK_OPTION = CHAR_MAX + 1,
RANDOM_SOURCE_OPTION
};
static char const short_options[] = "-bcdfgik:mMno:rRsS:t:T:uy:z";
static char const short_options[] = "-bcCdfgik:mMno:rRsS:t:T:uy:z";
static struct option const long_options[] =
{
{"ignore-leading-blanks", no_argument, NULL, 'b'},
{"check", no_argument, NULL, 'c'},
{"check", optional_argument, NULL, CHECK_OPTION},
{"dictionary-order", no_argument, NULL, 'd'},
{"ignore-case", no_argument, NULL, 'f'},
{"general-numeric-sort", no_argument, NULL, 'g'},
@@ -396,18 +422,220 @@ static struct option const long_options[] =
{NULL, 0, NULL, 0},
};
static char const *const check_args[] =
{
"quiet", "silent", "diagnose-first", NULL
};
static char const check_types[] =
{
'C', 'C', 'c'
};
ARGMATCH_VERIFY (check_args, check_types);
/* The set of signals that are caught. */
static sigset_t caught_signals;
/* Critical section status. */
struct cs_status
{
bool valid;
sigset_t sigs;
};
/* Enter a critical section. */
static struct cs_status
cs_enter (void)
{
struct cs_status status;
status.valid = (sigprocmask (SIG_BLOCK, &caught_signals, &status.sigs) == 0);
return status;
}
/* Leave a critical section. */
static void
cs_leave (struct cs_status status)
{
if (status.valid)
{
/* Ignore failure when restoring the signal mask. */
sigprocmask (SIG_SETMASK, &status.sigs, NULL);
}
}
/* The list of temporary files. */
struct tempnode
{
struct tempnode *volatile next;
pid_t pid; /* If compressed, the pid of compressor, else zero */
char name[1]; /* Actual size is 1 + file name length. */
};
static struct tempnode *volatile temphead;
static struct tempnode *volatile *temptail = &temphead;
struct sortfile
{
char const *name;
pid_t pid; /* If compressed, the pid of compressor, else zero */
};
/* A table where we store compression process states. We clean up all
processes in a timely manner so as not to exhaust system resources,
so we store the info on whether the process is still running, or has
been reaped here. */
static Hash_table *proctab;
enum { INIT_PROCTAB_SIZE = 47 };
enum procstate { ALIVE, ZOMBIE };
/* A proctab entry. The COUNT field is there in case we fork a new
compression process that has the same PID as an old zombie process
that is still in the table (because the process to decompress the
temp file it was associated with hasn't started yet). */
struct procnode
{
pid_t pid;
enum procstate state;
size_t count;
};
static size_t
proctab_hasher (const void *entry, size_t tabsize)
{
const struct procnode *node = entry;
return node->pid % tabsize;
}
static bool
proctab_comparator (const void *e1, const void *e2)
{
const struct procnode *n1 = e1, *n2 = e2;
return n1->pid == n2->pid;
}
/* The total number of forked processes (compressors and decompressors)
that have not been reaped yet. */
static size_t nprocs;
/* The number of child processes we'll allow before we try to reap some. */
enum { MAX_PROCS_BEFORE_REAP = 2 };
/* If 0 < PID, wait for the child process with that PID to exit.
If PID is -1, clean up a random child process which has finished and
return the process ID of that child. If PID is -1 and no processes
have quit yet, return 0 without waiting. */
static pid_t
reap (pid_t pid)
{
int status;
pid_t cpid = waitpid (pid, &status, pid < 0 ? WNOHANG : 0);
if (cpid < 0)
error (SORT_FAILURE, errno, _("waiting for %s [-d]"),
compress_program);
else if (0 < cpid)
{
if (! WIFEXITED (status) || WEXITSTATUS (status))
error (SORT_FAILURE, 0, _("%s [-d] terminated abnormally"),
compress_program);
--nprocs;
}
return cpid;
}
/* Add the PID of a running compression process to proctab, or update
the entry COUNT and STATE fields if it's already there. This also
creates the table for us the first time it's called. */
static void
register_proc (pid_t pid)
{
struct procnode test, *node;
if (! proctab)
{
proctab = hash_initialize (INIT_PROCTAB_SIZE, NULL,
proctab_hasher,
proctab_comparator,
free);
if (! proctab)
xalloc_die ();
}
test.pid = pid;
node = hash_lookup (proctab, &test);
if (node)
{
node->state = ALIVE;
++node->count;
}
else
{
node = xmalloc (sizeof *node);
node->pid = pid;
node->state = ALIVE;
node->count = 1;
hash_insert (proctab, node);
}
}
/* This is called when we reap a random process. We don't know
whether we have reaped a compression process or a decompression
process until we look in the table. If there's an ALIVE entry for
it, then we have reaped a compression process, so change the state
to ZOMBIE. Otherwise, it's a decompression processes, so ignore it. */
static void
update_proc (pid_t pid)
{
struct procnode test, *node;
test.pid = pid;
node = hash_lookup (proctab, &test);
if (node)
node->state = ZOMBIE;
}
/* This is for when we need to wait for a compression process to exit.
If it has a ZOMBIE entry in the table then it's already dead and has
been reaped. Note that if there's an ALIVE entry for it, it still may
already have died and been reaped if a second process was created with
the same PID. This is probably exceedingly rare, but to be on the safe
side we will have to wait for any compression process with this PID. */
static void
wait_proc (pid_t pid)
{
struct procnode test, *node;
test.pid = pid;
node = hash_lookup (proctab, &test);
if (node->state == ALIVE)
reap (pid);
node->state = ZOMBIE;
if (! --node->count)
{
hash_delete (proctab, node);
free (node);
}
}
/* Keep reaping finished children as long as there are more to reap.
This doesn't block waiting for any of them, it only reaps those
that are already dead. */
static void
reap_some (void)
{
pid_t pid;
while (0 < nprocs && (pid = reap (-1)))
update_proc (pid);
}
/* Clean up any remaining temporary files. */
static void
@@ -429,24 +657,22 @@ exit_cleanup (void)
{
/* Clean up any remaining temporary files in a critical section so
that a signal handler does not try to clean them too. */
sigset_t oldset;
sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
struct cs_status cs = cs_enter ();
cleanup ();
sigprocmask (SIG_SETMASK, &oldset, NULL);
cs_leave (cs);
}
close_stdout ();
}
/* Create a new temporary file, returning its newly allocated name.
Store into *PFP a stream open for writing. */
/* Create a new temporary file, returning its newly allocated tempnode.
Store into *PFD the file descriptor open for writing. */
static char *
create_temp_file (FILE **pfp)
static struct tempnode *
create_temp_file (int *pfd)
{
static char const slashbase[] = "/sortXXXXXX";
static size_t temp_dir_index;
sigset_t oldset;
int fd;
int saved_errno;
char const *temp_dir = temp_dirs[temp_dir_index];
@@ -454,15 +680,17 @@ create_temp_file (FILE **pfp)
struct tempnode *node =
xmalloc (offsetof (struct tempnode, name) + len + sizeof slashbase);
char *file = node->name;
struct cs_status cs;
memcpy (file, temp_dir, len);
memcpy (file + len, slashbase, sizeof slashbase);
node->next = NULL;
node->pid = 0;
if (++temp_dir_index == temp_dir_count)
temp_dir_index = 0;
/* Create the temporary file in a critical section, to avoid races. */
sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
cs = cs_enter ();
fd = mkstemp (file);
if (0 <= fd)
{
@@ -470,13 +698,14 @@ create_temp_file (FILE **pfp)
temptail = &node->next;
}
saved_errno = errno;
sigprocmask (SIG_SETMASK, &oldset, NULL);
cs_leave (cs);
errno = saved_errno;
if (fd < 0 || (*pfp = fdopen (fd, "w")) == NULL)
if (fd < 0)
die (_("cannot create temporary file"), file);
return file;
*pfd = fd;
return node;
}
/* Return a stream for FILE, opened with mode HOW. A null FILE means
@@ -533,6 +762,192 @@ xfclose (FILE *fp, char const *file)
}
}
static void
dup2_or_die (int oldfd, int newfd)
{
if (dup2 (oldfd, newfd) < 0)
error (SORT_FAILURE, errno, _("dup2 failed"));
}
/* Fork a child process for piping to and do common cleanup. The
TRIES parameter tells us how many times to try to fork before
giving up. Return the PID of the child or -1 if fork failed. */
static pid_t
pipe_fork (int pipefds[2], size_t tries)
{
#if HAVE_WORKING_FORK
struct tempnode *saved_temphead;
int saved_errno;
unsigned int wait_retry = 1;
pid_t pid IF_LINT (= -1);
struct cs_status cs;
if (pipe (pipefds) < 0)
return -1;
while (tries--)
{
/* This is so the child process won't delete our temp files
if it receives a signal before exec-ing. */
cs = cs_enter ();
saved_temphead = temphead;
temphead = NULL;
pid = fork ();
saved_errno = errno;
if (pid)
temphead = saved_temphead;
cs_leave (cs);
errno = saved_errno;
if (0 <= pid || errno != EAGAIN)
break;
else
{
sleep (wait_retry);
wait_retry *= 2;
reap_some ();
}
}
if (pid < 0)
{
close (pipefds[0]);
close (pipefds[1]);
}
else if (pid == 0)
{
close (STDIN_FILENO);
close (STDOUT_FILENO);
}
else
++nprocs;
return pid;
#else /* ! HAVE_WORKING_FORK */
return -1;
#endif
}
/* Create a temporary file and start a compression program to filter output
to that file. Set *PFP to the file handle and if *PPID is non-NULL,
set it to the PID of the newly-created process. */
static char *
create_temp (FILE **pfp, pid_t *ppid)
{
static bool compress_program_known;
int tempfd;
struct tempnode *node = create_temp_file (&tempfd);
char *name = node->name;
if (! compress_program_known)
{
compress_program = getenv ("GNUSORT_COMPRESSOR");
if (compress_program == NULL)
{
static const char *default_program = "gzip";
const char *path_program = find_in_path (default_program);
if (path_program != default_program)
compress_program = path_program;
}
else if (*compress_program == '\0')
compress_program = NULL;
compress_program_known = true;
}
if (compress_program)
{
int pipefds[2];
node->pid = pipe_fork (pipefds, MAX_FORK_TRIES_COMPRESS);
if (0 < node->pid)
{
close (tempfd);
close (pipefds[0]);
tempfd = pipefds[1];
register_proc (node->pid);
}
else if (node->pid == 0)
{
close (pipefds[1]);
dup2_or_die (tempfd, STDOUT_FILENO);
close (tempfd);
dup2_or_die (pipefds[0], STDIN_FILENO);
close (pipefds[0]);
if (execlp (compress_program, compress_program,
(char *) NULL) < 0)
error (SORT_FAILURE, errno, _("couldn't execute %s"),
compress_program);
}
else
node->pid = 0;
}
*pfp = fdopen (tempfd, "w");
if (! *pfp)
die (_("couldn't create temporary file"), name);
if (ppid)
*ppid = node->pid;
return name;
}
/* Open a compressed temp file and start a decompression process through
which to filter the input. PID must be the valid processes ID of the
process used to compress the file. */
static FILE *
open_temp (const char *name, pid_t pid)
{
int tempfd, pipefds[2];
pid_t child_pid;
FILE *fp;
wait_proc (pid);
tempfd = open (name, O_RDONLY);
if (tempfd < 0)
die (_("couldn't open temporary file"), name);
child_pid = pipe_fork (pipefds, MAX_FORK_TRIES_DECOMPRESS);
if (0 < child_pid)
{
close (tempfd);
close (pipefds[1]);
}
else if (child_pid == 0)
{
close (pipefds[0]);
dup2_or_die (tempfd, STDIN_FILENO);
close (tempfd);
dup2_or_die (pipefds[1], STDOUT_FILENO);
close (pipefds[1]);
if (execlp (compress_program, compress_program,
"-d", (char *) NULL) < 0)
error (SORT_FAILURE, errno, _("couldn't execute %s -d"),
compress_program);
}
else
error (SORT_FAILURE, errno, _("couldn't create process for %s -d"),
compress_program);
fp = fdopen (pipefds[0], "r");
if (! fp)
die (_("couldn't create temporary file"), name);
return fp;
}
static void
write_bytes (const char *buf, size_t n_bytes, FILE *fp, const char *output_file)
{
@@ -558,20 +973,20 @@ zaptemp (const char *name)
struct tempnode *volatile *pnode;
struct tempnode *node;
struct tempnode *next;
sigset_t oldset;
int unlink_status;
int unlink_errno = 0;
struct cs_status cs;
for (pnode = &temphead; (node = *pnode)->name != name; pnode = &node->next)
continue;
/* Unlink the temporary file in a critical section to avoid races. */
next = node->next;
sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
cs = cs_enter ();
unlink_status = unlink (name);
unlink_errno = errno;
*pnode = next;
sigprocmask (SIG_SETMASK, &oldset, NULL);
cs_leave (cs);
if (unlink_status != 0)
error (0, unlink_errno, _("warning: cannot remove: %s"), name);
@@ -1512,13 +1927,13 @@ compare (const struct line *a, const struct line *b)
return reverse ? -diff : diff;
}
/* Check that the lines read from FILE_NAME come in order. Print a
diagnostic (FILE_NAME, line number, contents of line) to stderr and return
false if they are not in order. Otherwise, print no diagnostic
and return true. */
/* Check that the lines read from FILE_NAME come in order. Return
true if they are in order. If CHECKONLY == 'c', also print a
diagnostic (FILE_NAME, line number, contents of line) to stderr if
they are not in order. */
static bool
check (char const *file_name)
check (char const *file_name, char checkonly)
{
FILE *fp = xfopen (file_name, "r");
struct buffer buf; /* Input buffer. */
@@ -1544,15 +1959,19 @@ check (char const *file_name)
{
found_disorder:
{
struct line const *disorder_line = line - 1;
uintmax_t disorder_line_number =
buffer_linelim (&buf) - disorder_line + line_number;
char hr_buf[INT_BUFSIZE_BOUND (uintmax_t)];
fprintf (stderr, _("%s: %s:%s: disorder: "),
program_name, file_name,
umaxtostr (disorder_line_number, hr_buf));
write_bytes (disorder_line->text, disorder_line->length, stderr,
_("standard error"));
if (checkonly == 'c')
{
struct line const *disorder_line = line - 1;
uintmax_t disorder_line_number =
buffer_linelim (&buf) - disorder_line + line_number;
char hr_buf[INT_BUFSIZE_BOUND (uintmax_t)];
fprintf (stderr, _("%s: %s:%s: disorder: "),
program_name, file_name,
umaxtostr (disorder_line_number, hr_buf));
write_bytes (disorder_line->text, disorder_line->length,
stderr, _("standard error"));
}
ordered = false;
break;
}
@@ -1605,7 +2024,7 @@ check (char const *file_name)
file has not been opened yet (or written to, if standard output). */
static void
mergefps (char **files, size_t ntemps, size_t nfiles,
mergefps (struct sortfile *files, size_t ntemps, size_t nfiles,
FILE *ofp, char const *output_file)
{
FILE *fps[NMERGE]; /* Input streams for each file. */
@@ -1628,10 +2047,12 @@ mergefps (char **files, size_t ntemps, size_t nfiles,
/* Read initial lines from each input file. */
for (i = 0; i < nfiles; )
{
fps[i] = xfopen (files[i], "r");
fps[i] = (files[i].pid
? open_temp (files[i].name, files[i].pid)
: xfopen (files[i].name, "r"));
initbuf (&buffer[i], sizeof (struct line),
MAX (merge_buffer_size, sort_size / nfiles));
if (fillbuf (&buffer[i], fps[i], files[i]))
if (fillbuf (&buffer[i], fps[i], files[i].name))
{
struct line const *linelim = buffer_linelim (&buffer[i]);
cur[i] = linelim - 1;
@@ -1641,11 +2062,11 @@ mergefps (char **files, size_t ntemps, size_t nfiles,
else
{
/* fps[i] is empty; eliminate it from future consideration. */
xfclose (fps[i], files[i]);
xfclose (fps[i], files[i].name);
if (i < ntemps)
{
ntemps--;
zaptemp (files[i]);
zaptemp (files[i].name);
}
free (buffer[i].buf);
--nfiles;
@@ -1714,7 +2135,7 @@ mergefps (char **files, size_t ntemps, size_t nfiles,
cur[ord[0]] = smallest - 1;
else
{
if (fillbuf (&buffer[ord[0]], fps[ord[0]], files[ord[0]]))
if (fillbuf (&buffer[ord[0]], fps[ord[0]], files[ord[0]].name))
{
struct line const *linelim = buffer_linelim (&buffer[ord[0]]);
cur[ord[0]] = linelim - 1;
@@ -1727,11 +2148,11 @@ mergefps (char **files, size_t ntemps, size_t nfiles,
if (ord[i] > ord[0])
--ord[i];
--nfiles;
xfclose (fps[ord[0]], files[ord[0]]);
xfclose (fps[ord[0]], files[ord[0]].name);
if (ord[0] < ntemps)
{
ntemps--;
zaptemp (files[ord[0]]);
zaptemp (files[ord[0]].name);
}
free (buffer[ord[0]].buf);
for (i = ord[0]; i < nfiles; ++i)
@@ -1774,6 +2195,10 @@ mergefps (char **files, size_t ntemps, size_t nfiles,
ord[j] = ord[j + 1];
ord[count_of_smaller_lines] = ord0;
}
/* Free up some resources every once in a while. */
if (MAX_PROCS_BEFORE_REAP < nprocs)
reap_some ();
}
if (unique && savedline)
@@ -1912,8 +2337,8 @@ sortlines_temp (struct line *lines, size_t nlines, struct line *temp)
common cases. */
static size_t
avoid_trashing_input (char **files, size_t ntemps, size_t nfiles,
char const *outfile)
avoid_trashing_input (struct sortfile *files, size_t ntemps,
size_t nfiles, char const *outfile)
{
size_t i;
bool got_outstat = false;
@@ -1921,11 +2346,11 @@ avoid_trashing_input (char **files, size_t ntemps, size_t nfiles,
for (i = ntemps; i < nfiles; i++)
{
bool is_stdin = STREQ (files[i], "-");
bool is_stdin = STREQ (files[i].name, "-");
bool same;
struct stat instat;
if (outfile && STREQ (outfile, files[i]) && !is_stdin)
if (outfile && STREQ (outfile, files[i].name) && !is_stdin)
same = true;
else
{
@@ -1941,7 +2366,7 @@ avoid_trashing_input (char **files, size_t ntemps, size_t nfiles,
same = (((is_stdin
? fstat (STDIN_FILENO, &instat)
: stat (files[i], &instat))
: stat (files[i].name, &instat))
== 0)
&& SAME_INODE (instat, outstat));
}
@@ -1949,9 +2374,11 @@ avoid_trashing_input (char **files, size_t ntemps, size_t nfiles,
if (same)
{
FILE *tftp;
char *temp = create_temp_file (&tftp);
mergefps (&files[i], 0, nfiles - i, tftp, temp);
files[i] = temp;
pid_t pid;
char *temp = create_temp (&tftp, &pid);
mergefps (&files[i],0, nfiles - i, tftp, temp);
files[i].name = temp;
files[i].pid = pid;
return i + 1;
}
}
@@ -1965,7 +2392,8 @@ avoid_trashing_input (char **files, size_t ntemps, size_t nfiles,
OUTPUT_FILE; a null OUTPUT_FILE stands for standard output. */
static void
merge (char **files, size_t ntemps, size_t nfiles, char const *output_file)
merge (struct sortfile *files, size_t ntemps, size_t nfiles,
char const *output_file)
{
while (NMERGE < nfiles)
{
@@ -1986,11 +2414,13 @@ merge (char **files, size_t ntemps, size_t nfiles, char const *output_file)
for (out = in = 0; out < nfiles / NMERGE; out++, in += NMERGE)
{
FILE *tfp;
char *temp = create_temp_file (&tfp);
pid_t pid;
char *temp = create_temp (&tfp, &pid);
size_t nt = MIN (ntemps, NMERGE);
ntemps -= nt;
mergefps (&files[in], nt, NMERGE, tfp, temp);
files[out] = temp;
files[out].name = temp;
files[out].pid = pid;
}
remainder = nfiles - in;
@@ -2003,11 +2433,13 @@ merge (char **files, size_t ntemps, size_t nfiles, char const *output_file)
files as possible, to avoid needless I/O. */
size_t nshortmerge = remainder - cheap_slots + 1;
FILE *tfp;
char *temp = create_temp_file (&tfp);
pid_t pid;
char *temp = create_temp (&tfp, &pid);
size_t nt = MIN (ntemps, nshortmerge);
ntemps -= nt;
mergefps (&files[in], nt, nshortmerge, tfp, temp);
files[out++] = temp;
files[out].name = temp;
files[out++].pid = pid;
in += nshortmerge;
}
@@ -2079,7 +2511,7 @@ sort (char * const *files, size_t nfiles, char const *output_file)
else
{
++ntemps;
temp_output = create_temp_file (&tfp);
temp_output = create_temp (&tfp, NULL);
}
do
@@ -2094,6 +2526,10 @@ sort (char * const *files, size_t nfiles, char const *output_file)
xfclose (tfp, temp_output);
/* Free up some resources every once in a while. */
if (MAX_PROCS_BEFORE_REAP < nprocs)
reap_some ();
if (output_file_created)
goto finish;
}
@@ -2107,10 +2543,11 @@ sort (char * const *files, size_t nfiles, char const *output_file)
{
size_t i;
struct tempnode *node = temphead;
char **tempfiles = xnmalloc (ntemps, sizeof *tempfiles);
struct sortfile *tempfiles = xnmalloc (ntemps, sizeof *tempfiles);
for (i = 0; node; i++)
{
tempfiles[i] = node->name;
tempfiles[i].name = node->name;
tempfiles[i].pid = node->pid;
node = node->next;
}
merge (tempfiles, ntemps, ntemps, output_file);
@@ -2305,7 +2742,7 @@ main (int argc, char **argv)
struct keyfield gkey;
char const *s;
int c = 0;
bool checkonly = false;
char checkonly = 0;
bool mergeonly = false;
char *random_source = NULL;
bool need_random = false;
@@ -2497,8 +2934,16 @@ main (int argc, char **argv)
}
break;
case CHECK_OPTION:
c = (optarg
? XARGMATCH ("--check", optarg, check_args, check_types)
: 'c');
/* Fall through. */
case 'c':
checkonly = true;
case 'C':
if (checkonly && checkonly != c)
incompatible_options ("cC");
checkonly = c;
break;
case 'k':
@@ -2705,19 +3150,31 @@ main (int argc, char **argv)
if (checkonly)
{
if (nfiles > 1)
error (SORT_FAILURE, 0, _("extra operand %s not allowed with -c"),
quote (files[1]));
error (SORT_FAILURE, 0, _("extra operand %s not allowed with -%c"),
quote (files[1]), checkonly);
if (outfile)
incompatible_options ("co");
{
static char opts[] = {0, 'o', 0};
opts[0] = checkonly;
incompatible_options (opts);
}
/* POSIX requires that sort return 1 IFF invoked with -c and the
/* POSIX requires that sort return 1 IFF invoked with -c or -C and the
input is not properly sorted. */
exit (check (files[0]) ? EXIT_SUCCESS : SORT_OUT_OF_ORDER);
exit (check (files[0], checkonly) ? EXIT_SUCCESS : SORT_OUT_OF_ORDER);
}
if (mergeonly)
merge (files, 0, nfiles, outfile);
{
struct sortfile *sortfiles = xcalloc (nfiles, sizeof *sortfiles);
size_t i;
for (i = 0; i < nfiles; ++i)
sortfiles[i].name = files[i];
merge (sortfiles, 0, nfiles, outfile);
}
else
sort (files, nfiles, outfile);

View File

@@ -1,7 +1,6 @@
# Make miscellaneous coreutils tests. -*-Makefile-*-
# Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Free Software
# Foundation, Inc.
# Copyright (C) 200-2007 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,6 +21,7 @@ EXTRA_DIST = $(TESTS)
TESTS_ENVIRONMENT = \
top_srcdir=$(top_srcdir) \
abs_top_builddir=$(abs_top_builddir) \
srcdir=$(srcdir) \
PACKAGE_VERSION=$(PACKAGE_VERSION) \
PERL="$(PERL)" \
@@ -69,6 +69,7 @@ TESTS = \
sha384sum \
sha512sum \
shuf \
sort-compress \
sort-merge \
sort-rand \
split-a \

View File

@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that pwd works even when run from a very deep directory.
# Copyright (C) 2006 Free Software Foundation, Inc.
# Copyright (C) 2006-2007 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -51,18 +51,39 @@ $PERL -Tw -- - <<\EOF
# Show that pwd works even when the length of the resulting
# directory name is longer than PATH_MAX.
use strict;
use Cwd;
(my $ME = $ENV{ARGV_0}) =~ s|.*/||;
sub normalize_to_cwd_relative ($$$)
{
my ($dir, $dev, $ino) = @_;
my $slash = -1;
my $next_slash;
while (1)
{
$slash = index $dir, '/', $slash + 1;
$slash <= -1
and die "$ME: $dir does not contain old CWD\n";
my $dir_prefix = $slash ? substr ($dir, 0, $slash) : '/';
my ($d, $i) = (stat $dir_prefix)[0, 1];
$d == $dev && $i == $ino
and return substr $dir, $slash + 1;
}
}
# Set up a safe, well-known environment
delete @ENV{qw(BASH_ENV CDPATH ENV PATH)};
$ENV{IFS} = '';
my $cwd = $ENV{CWD};
# Save CWD's device and inode numbers.
my ($dev, $ino) = (stat '.')[0, 1];
# Construct the expected "."-relative part of pwd's output.
my $z = 'z' x 31;
my $n = 256;
my $expected = $cwd . ("/$z" x $n);
my $expected = "/$z" x $n;
# Remove the leading "/".
substr ($expected, 0, 1) = '';
my $i = 0;
do
@@ -89,6 +110,15 @@ my $pwd_binary = "$build_src_dir/pwd";
-x $pwd_binary
or die "$ME: $pwd_binary is not an executable file\n";
chomp (my $actual = `$pwd_binary`);
# Convert the absolute name from pwd into a $CWD-relative name.
# This is necessary in order to avoid a spurious failure when run
# from a directory in a bind-mounted partition. What happens is
# pwd reads a ".." that contains two or more entries with identical
# dev,ino that match the ones we're looking for, and it chooses a
# name that does not correspond to the one already recorded in $CWD.
$actual = normalize_to_cwd_relative $actual, $dev, $ino;
if ($expected ne $actual)
{
my $e_len = length $expected;

92
tests/misc/sort-compress Executable file
View File

@@ -0,0 +1,92 @@
#!/bin/sh
# Test use of compression by sort
# Copyright (C) 2007 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
if test "$VERBOSE" = yes; then
set -x
sort --version
fi
pwd=`pwd`
t0=`echo "$0"|sed 's,.*/,,'`.tmp; tmp=$t0/$$
trap 'status=$?; cd "$pwd" && chmod -R u+rwx $t0 && rm -rf $t0 && exit $status' 0
trap '(exit $?); exit $?' 1 2 13 15
framework_failure=0
mkdir -p $tmp || framework_failure=1
cd $tmp || framework_failure=1
seq -w 2000 > exp || framework_failure=1
tac exp > in || framework_failure=1
SORT=$abs_top_builddir/src/sort
if test $framework_failure = 1; then
echo "$0: failure in testing framework" 1>&2
(exit 1); exit 1
fi
fail=0
# This should force the use of temp files compressed with the default gzip
sort -S 1k in > out || fail=1
cmp exp out || fail=1
test $fail = 1 && diff out exp 2> /dev/null
# Create our own gzip program that will be used as the default
cat <<\EOF > gzip || fail=1
#!/bin/sh
tr 41 14
touch ok
EOF
chmod +x gzip
# This will find our new gzip in PATH
PATH=.:$PATH sort -S 1k in > out || fail=1
cmp exp out || fail=1
test $fail = 1 && diff out exp 2> /dev/null
test -f ok || fail=1
rm -f ok
# This is to make sure we can disable compression
PATH=.:$PATH GNUSORT_COMPRESSOR= sort -S 1k in > out || fail=1
cmp exp out || fail=1
test $fail = 1 && diff out exp 2> /dev/null
test -f ok && fail=1
# This is to make sure we can use something other than gzip
mv gzip dzip || fail=1
GNUSORT_COMPRESSOR=./dzip sort -S 1k in > out || fail=1
cmp exp out || fail=1
test $fail = 1 && diff out exp 2> /dev/null
test -f ok || fail=1
rm -f ok
# Make sure it can find other programs in PATH correctly
PATH=.:$PATH GNUSORT_COMPRESSOR=dzip sort -S 1k in > out || fail=1
cmp exp out || fail=1
test $fail = 1 && diff out exp 2> /dev/null
test -f ok || fail=1
rm -f dzip ok
# This is to make sure sort functions if it can't find the default gzip
PATH=. "$SORT" -S 1k in > out || fail=1
cmp exp out || fail=1
test $fail = 1 && diff out exp 2> /dev/null
(exit $fail); exit $fail

View File

@@ -24,44 +24,46 @@ explicit =
maint_gen = n1.I n1.X n2.I n2.X n3.I n3.X n4.I n4.X n5.I n5.X n6.I n6.X n7.I \
n7.X n8a.I n8a.X n8b.I n8b.X n9a.I n9a.X n9b.I n9b.X n10a.I n10a.X n10b.I \
n10b.X n11a.I n11a.X n11b.I n11b.X 01a.I 01a.X 02a.I 02a.X 02b.I 02b.X 02c.I \
02c.X 02m.I 02m.X 02n.I 02n.X 02o.I 02o.X 02p.I 02p.X 03a.I 03a.X 03b.I 03b.X \
03c.I 03c.X 03d.I 03d.X 03e.I 03e.X 03f.I 03f.X 03g.I 03g.X 03h.I 03h.X 03i.I \
03i.X 04a.I 04a.X 04b.I 04b.X 04c.I 04c.X 04d.I 04d.X 04e.I 04e.X 05a.I 05a.X \
05b.I 05b.X 05c.I 05c.X 05d.I 05d.X 05e.I 05e.X 05f.I 05f.X 06a.I 06a.X 06b.I \
06b.X 06c.I 06c.X 06d.I 06d.X 06e.I 06e.X 06f.I 06f.X 07a.I 07a.X 07b.I 07b.X \
07c.I 07c.X 07d.I 07d.X 08a.I 08a.X 08b.I 08b.X 09a.I 09a.X 09b.I 09b.X 09c.I \
09c.X 09d.I 09d.X 10a.I 10a.X 10b.I 10b.X 10c.I 10c.X 10d.I 10d.X 10a0.I \
10a0.X 10a1.I 10a1.X 10a2.I 10a2.X 10e.I 10e.X 10f.I 10f.X 10g.I 10g.X 11a.I \
11a.X 11b.I 11b.X 11c.I 11c.X 11d.I 11d.X 12a.I 12a.X 12b.I 12b.X 12c.I 12c.X \
12d.I 12d.X 13a.I 13a.X 13b.I 13b.X 14a.I 14a.X 14b.I 14b.X 15a.I 15a.X 15b.I \
15b.X 15c.I 15c.X 15d.I 15d.X 15e.I 15e.X 16a.I 16a.X 17.I 17.X 18a.I 18a.X \
18b.I 18b.X 18c.I 18c.X 18d.I 18d.X 18e.I 18e.X 19a.I 19a.X 19b.I 19b.X 20a.I \
20a.X 21a.I 21a.X 21b.I 21b.X 21c.I 21c.X 21d.I 21d.X 21e.I 21e.X 21f.I 21f.X \
21g.I 21g.X 22a.I 22a.X 22b.I 22b.X no-file1.X o-no-file1.X create-empty.X \
neg-nls.I neg-nls.X nul-nls.I nul-nls.X use-nl.I use-nl.X o2.I o2.X \
incompat1.I incompat1.X incompat2.I incompat2.X incompat3.I incompat3.X \
incompat4.I incompat4.X nul-tab.I nul-tab.X bigfield.I bigfield.X
02c.X 02d.I 02d.X 02e.I 02e.X 02m.I 02m.X 02n.I 02n.X 02o.I 02o.X 02p.I 02p.X \
03a.I 03a.X 03b.I 03b.X 03c.I 03c.X 03d.I 03d.X 03e.I 03e.X 03f.I 03f.X 03g.I \
03g.X 03h.I 03h.X 03i.I 03i.X 04a.I 04a.X 04b.I 04b.X 04c.I 04c.X 04d.I 04d.X \
04e.I 04e.X 05a.I 05a.X 05b.I 05b.X 05c.I 05c.X 05d.I 05d.X 05e.I 05e.X 05f.I \
05f.X 06a.I 06a.X 06b.I 06b.X 06c.I 06c.X 06d.I 06d.X 06e.I 06e.X 06f.I 06f.X \
07a.I 07a.X 07b.I 07b.X 07c.I 07c.X 07d.I 07d.X 08a.I 08a.X 08b.I 08b.X 09a.I \
09a.X 09b.I 09b.X 09c.I 09c.X 09d.I 09d.X 10a.I 10a.X 10b.I 10b.X 10c.I 10c.X \
10d.I 10d.X 10a0.I 10a0.X 10a1.I 10a1.X 10a2.I 10a2.X 10e.I 10e.X 10f.I 10f.X \
10g.I 10g.X 11a.I 11a.X 11b.I 11b.X 11c.I 11c.X 11d.I 11d.X 12a.I 12a.X 12b.I \
12b.X 12c.I 12c.X 12d.I 12d.X 13a.I 13a.X 13b.I 13b.X 14a.I 14a.X 14b.I 14b.X \
15a.I 15a.X 15b.I 15b.X 15c.I 15c.X 15d.I 15d.X 15e.I 15e.X 16a.I 16a.X 17.I \
17.X 18a.I 18a.X 18b.I 18b.X 18c.I 18c.X 18d.I 18d.X 18e.I 18e.X 19a.I 19a.X \
19b.I 19b.X 20a.I 20a.X 21a.I 21a.X 21b.I 21b.X 21c.I 21c.X 21d.I 21d.X 21e.I \
21e.X 21f.I 21f.X 21g.I 21g.X 22a.I 22a.X 22b.I 22b.X no-file1.X o-no-file1.X \
create-empty.X neg-nls.I neg-nls.X nul-nls.I nul-nls.X use-nl.I use-nl.X o2.I \
o2.X incompat1.I incompat1.X incompat2.I incompat2.X incompat3.I incompat3.X \
incompat4.I incompat4.X incompat5.I incompat5.X incompat6.I incompat6.X \
nul-tab.I nul-tab.X bigfield.I bigfield.X
run_gen = n1.O n1.E n2.O n2.E n3.O n3.E n4.O n4.E n5.O n5.E n6.O n6.E n7.O \
n7.E n8a.O n8a.E n8b.O n8b.E n9a.O n9a.E n9b.O n9b.E n10a.O n10a.E n10b.O \
n10b.E n11a.O n11a.E n11b.O n11b.E 01a.O 01a.E 02a.O 02a.E 02b.O 02b.E 02c.O \
02c.E 02m.O 02m.E 02n.O 02n.E 02o.O 02o.E 02p.O 02p.E 03a.O 03a.E 03b.O 03b.E \
03c.O 03c.E 03d.O 03d.E 03e.O 03e.E 03f.O 03f.E 03g.O 03g.E 03h.O 03h.E 03i.O \
03i.E 04a.O 04a.E 04b.O 04b.E 04c.O 04c.E 04d.O 04d.E 04e.O 04e.E 05a.O 05a.E \
05b.O 05b.E 05c.O 05c.E 05d.O 05d.E 05e.O 05e.E 05f.O 05f.E 06a.O 06a.E 06b.O \
06b.E 06c.O 06c.E 06d.O 06d.E 06e.O 06e.E 06f.O 06f.E 07a.O 07a.E 07b.O 07b.E \
07c.O 07c.E 07d.O 07d.E 08a.O 08a.E 08b.O 08b.E 09a.O 09a.E 09b.O 09b.E 09c.O \
09c.E 09d.O 09d.E 10a.O 10a.E 10b.O 10b.E 10c.O 10c.E 10d.O 10d.E 10a0.O \
10a0.E 10a1.O 10a1.E 10a2.O 10a2.E 10e.O 10e.E 10f.O 10f.E 10g.O 10g.E 11a.O \
11a.E 11b.O 11b.E 11c.O 11c.E 11d.O 11d.E 12a.O 12a.E 12b.O 12b.E 12c.O 12c.E \
12d.O 12d.E 13a.O 13a.E 13b.O 13b.E 14a.O 14a.E 14b.O 14b.E 15a.O 15a.E 15b.O \
15b.E 15c.O 15c.E 15d.O 15d.E 15e.O 15e.E 16a.O 16a.E 17.O 17.E 18a.O 18a.E \
18b.O 18b.E 18c.O 18c.E 18d.O 18d.E 18e.O 18e.E 19a.O 19a.E 19b.O 19b.E 20a.O \
20a.E 21a.O 21a.E 21b.O 21b.E 21c.O 21c.E 21d.O 21d.E 21e.O 21e.E 21f.O 21f.E \
21g.O 21g.E 22a.O 22a.E 22b.O 22b.E no-file1.O no-file1.E o-no-file1.O \
o-no-file1.E create-empty.O create-empty.E neg-nls.O neg-nls.E nul-nls.O \
nul-nls.E use-nl.O use-nl.E o2.O o2.E incompat1.O incompat1.E incompat2.O \
incompat2.E incompat3.O incompat3.E incompat4.O incompat4.E nul-tab.O \
nul-tab.E bigfield.O bigfield.E
02c.E 02d.O 02d.E 02e.O 02e.E 02m.O 02m.E 02n.O 02n.E 02o.O 02o.E 02p.O 02p.E \
03a.O 03a.E 03b.O 03b.E 03c.O 03c.E 03d.O 03d.E 03e.O 03e.E 03f.O 03f.E 03g.O \
03g.E 03h.O 03h.E 03i.O 03i.E 04a.O 04a.E 04b.O 04b.E 04c.O 04c.E 04d.O 04d.E \
04e.O 04e.E 05a.O 05a.E 05b.O 05b.E 05c.O 05c.E 05d.O 05d.E 05e.O 05e.E 05f.O \
05f.E 06a.O 06a.E 06b.O 06b.E 06c.O 06c.E 06d.O 06d.E 06e.O 06e.E 06f.O 06f.E \
07a.O 07a.E 07b.O 07b.E 07c.O 07c.E 07d.O 07d.E 08a.O 08a.E 08b.O 08b.E 09a.O \
09a.E 09b.O 09b.E 09c.O 09c.E 09d.O 09d.E 10a.O 10a.E 10b.O 10b.E 10c.O 10c.E \
10d.O 10d.E 10a0.O 10a0.E 10a1.O 10a1.E 10a2.O 10a2.E 10e.O 10e.E 10f.O 10f.E \
10g.O 10g.E 11a.O 11a.E 11b.O 11b.E 11c.O 11c.E 11d.O 11d.E 12a.O 12a.E 12b.O \
12b.E 12c.O 12c.E 12d.O 12d.E 13a.O 13a.E 13b.O 13b.E 14a.O 14a.E 14b.O 14b.E \
15a.O 15a.E 15b.O 15b.E 15c.O 15c.E 15d.O 15d.E 15e.O 15e.E 16a.O 16a.E 17.O \
17.E 18a.O 18a.E 18b.O 18b.E 18c.O 18c.E 18d.O 18d.E 18e.O 18e.E 19a.O 19a.E \
19b.O 19b.E 20a.O 20a.E 21a.O 21a.E 21b.O 21b.E 21c.O 21c.E 21d.O 21d.E 21e.O \
21e.E 21f.O 21f.E 21g.O 21g.E 22a.O 22a.E 22b.O 22b.E no-file1.O no-file1.E \
o-no-file1.O o-no-file1.E create-empty.O create-empty.E neg-nls.O neg-nls.E \
nul-nls.O nul-nls.E use-nl.O use-nl.E o2.O o2.E incompat1.O incompat1.E \
incompat2.O incompat2.E incompat3.O incompat3.E incompat4.O incompat4.E \
incompat5.O incompat5.E incompat6.O incompat6.E nul-tab.O nul-tab.E \
bigfield.O bigfield.E
##test-files-end
EXTRA_DIST = Test.pm $x-tests $(explicit) $(maint_gen)

View File

@@ -51,6 +51,8 @@ my @tv = (
["02a", '-c', "A\nB\nC\n", '', 0],
["02b", '-c', "A\nC\nB\n", '', 1],
["02c", '-c -k1,1', "a\na b\n", '', 0],
["02d", '-C', "A\nB\nC\n", '', 0],
["02e", '-C', "A\nC\nB\n", '', 1],
# This should fail because there are duplicate keys
["02m", '-cu', "A\nA\n", '', 1],
["02n", '-cu', "A\nB\n", '', 0],
@@ -272,6 +274,8 @@ my @tv = (
["incompat2", '-fR', '', '', 2],
["incompat3", '-dfgiMnR', '', '', 2],
["incompat4", '-c -o /dev/null', '', '', 2],
["incompat5", '-C -o /dev/null', '', '', 2],
["incompat6", '-cC', '', '', 2],
# -t '\0' is accepted, as of coreutils-5.0.91
['nul-tab', "-k2,2 -t '\\0'", "a\0z\01\nb\0y\02\n", "b\0y\02\na\0z\01\n", 0],