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:
129
ChangeLog
129
ChangeLog
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
13
NEWS
@@ -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]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
4
lib/.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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
2
m4/.gitignore
vendored
@@ -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
|
||||
|
||||
591
src/sort.c
591
src/sort.c
@@ -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);
|
||||
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
92
tests/misc/sort-compress
Executable 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
|
||||
@@ -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)
|
||||
|
||||
@@ -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],
|
||||
|
||||
Reference in New Issue
Block a user