diff options
Diffstat (limited to 'gnu/usr.bin/sort/sort.c')
-rw-r--r-- | gnu/usr.bin/sort/sort.c | 1003 |
1 files changed, 380 insertions, 623 deletions
diff --git a/gnu/usr.bin/sort/sort.c b/gnu/usr.bin/sort/sort.c index dc3addb..a6c7539 100644 --- a/gnu/usr.bin/sort/sort.c +++ b/gnu/usr.bin/sort/sort.c @@ -1,5 +1,5 @@ /* sort - sort lines of text (with all kinds of options). - Copyright (C) 1988, 1991, 1992, 1993, 1994, 1995 Free Software Foundation + Copyright (C) 1988, 1991 Free Software Foundation 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 @@ -13,13 +13,22 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Written December 1988 by Mike Haertel. The author may be reached (Email) at the address mike@gnu.ai.mit.edu, or (US mail) as Mike Haertel c/o Free Software Foundation. */ +#ifdef HAVE_CONFIG_H +#if defined (CONFIG_BROKETS) +/* We use <config.h> instead of "config.h" so that a compilation + using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h + (which it would do because it found this file in $srcdir). */ #include <config.h> +#else +#include "config.h" +#endif +#endif /* Get isblank from GNU libc. */ #define _GNU_SOURCE @@ -27,13 +36,13 @@ #include <sys/types.h> #include <signal.h> #include <stdio.h> +#ifdef __FreeBSD__ +#include <locale.h> +#endif #include "system.h" -#include "version.h" #include "long-options.h" -#include "error.h" -#include "xstrtod.h" -#ifdef HAVE_LIMITS_H +#ifdef _POSIX_VERSION #include <limits.h> #else #ifndef UCHAR_MAX @@ -46,73 +55,16 @@ char *realloc (); void free (); #endif -/* Undefine, to avoid warning about redefinition on some systems. */ -#undef min -#define min(a, b) ((a) < (b) ? (a) : (b)) +void error (); +static void usage (); +#define min(a, b) ((a) < (b) ? (a) : (b)) #define UCHAR_LIM (UCHAR_MAX + 1) #define UCHAR(c) ((unsigned char) (c)) -#ifndef DEFAULT_TMPDIR -#define DEFAULT_TMPDIR "/tmp" -#endif - /* The kind of blanks for '-b' to skip in various options. */ enum blanktype { bl_start, bl_end, bl_both }; -/* Lines are held in core as counted strings. */ -struct line -{ - char *text; /* Text of the line. */ - int length; /* Length not including final newline. */ - char *keybeg; /* Start of first key. */ - char *keylim; /* Limit of first key. */ -}; - -/* Arrays of lines. */ -struct lines -{ - struct line *lines; /* Dynamically allocated array of lines. */ - int used; /* Number of slots used. */ - int alloc; /* Number of slots allocated. */ - int limit; /* Max number of slots to allocate. */ -}; - -/* Input buffers. */ -struct buffer -{ - char *buf; /* Dynamically allocated buffer. */ - int used; /* Number of bytes used. */ - int alloc; /* Number of bytes allocated. */ - int left; /* Number of bytes left after line parsing. */ -}; - -struct keyfield -{ - int sword; /* Zero-origin 'word' to start at. */ - int schar; /* Additional characters to skip. */ - int skipsblanks; /* Skip leading white space at start. */ - int eword; /* Zero-origin first word after field. */ - int echar; /* Additional characters in field. */ - int skipeblanks; /* Skip trailing white space at finish. */ - int *ignore; /* Boolean array of characters to ignore. */ - char *translate; /* Translation applied to characters. */ - int numeric; /* Flag for numeric comparison. Handle - strings of digits with optional decimal - point, but no exponential notation. */ - int general_numeric; /* Flag for general, numeric comparison. - Handle numbers in exponential notation. */ - int month; /* Flag for comparison by month name. */ - int reverse; /* Reverse the sense of comparison. */ - struct keyfield *next; /* Next keyfield to try. */ -}; - -struct month -{ - char *name; - int val; -}; - /* The name this program was run with. */ char *program_name; @@ -133,7 +85,11 @@ static char fold_toupper[UCHAR_LIM]; /* Table mapping 3-letter month names to integers. Alphabetic order allows binary search. */ -static struct month const monthtab[] = +static struct month +{ + char *name; + int val; +} const monthtab[] = { {"APR", 4}, {"AUG", 8}, @@ -154,17 +110,17 @@ static struct month const monthtab[] = /* Initial buffer size for in core sorting. Will not grow unless a line longer than this is seen. */ -static int sortalloc = 512 * 1024; +static int sortalloc = 524288; /* Initial buffer size for in core merge buffers. Bear in mind that up to NMERGE * mergealloc bytes may be allocated for merge buffers. */ -static int mergealloc = 16 * 1024; +static int mergealloc = 16384; /* Guess of average line length. */ static int linelength = 30; /* Maximum number of elements for the array(s) of struct line's, in bytes. */ -#define LINEALLOC (256 * 1024) +#define LINEALLOC 262144 /* Prefix for temporary file names. */ static char *temp_file_prefix; @@ -189,55 +145,49 @@ static int unique; /* Nonzero if any of the input files are the standard input. */ static int have_read_stdin; -/* Lists of key field comparisons to be tried. */ -static struct keyfield keyhead; +/* Lines are held in core as counted strings. */ +struct line +{ + char *text; /* Text of the line. */ + int length; /* Length not including final newline. */ + char *keybeg; /* Start of first key. */ + char *keylim; /* Limit of first key. */ +}; -static void -usage (int status) +/* Arrays of lines. */ +struct lines { - if (status != 0) - fprintf (stderr, _("Try `%s --help' for more information.\n"), - program_name); - else - { - printf (_("\ -Usage: %s [OPTION]... [FILE]...\n\ -"), - program_name); - printf (_("\ -Write sorted concatenation of all FILE(s) to standard output.\n\ -\n\ - +POS1 [-POS2] start a key at POS1, end it before POS2\n\ - -M compare (unknown) < `JAN' < ... < `DEC', imply -b\n\ - -T DIRECT use DIRECT for temporary files, not $TMPDIR or %s\n\ - -b ignore leading blanks in sort fields or keys\n\ - -c check if given files already sorted, do not sort\n\ - -d consider only [a-zA-Z0-9 ] characters in keys\n\ - -f fold lower case to upper case characters in keys\n\ - -g compare according to general numerical value, imply -b\n\ - -i consider only [\\040-\\0176] characters in keys\n\ - -k POS1[,POS2] same as +POS1 [-POS2], but all positions counted from 1\n\ - -m merge already sorted files, do not sort\n\ - -n compare according to string numerical value, imply -b\n\ - -o FILE write result on FILE instead of standard output\n\ - -r reverse the result of comparisons\n\ - -s stabilize sort by disabling last resort comparison\n\ - -t SEP use SEParator instead of non- to whitespace transition\n\ - -u with -c, check for strict ordering\n\ - -u with -m, only output the first of an equal sequence\n\ - --help display this help and exit\n\ - --version output version information and exit\n\ -\n\ -POS is F[.C][OPTS], where F is the field number and C the character\n\ -position in the field, both counted from zero. OPTS is made up of one\n\ -or more of Mbdfinr, this effectively disable global -Mbdfinr settings\n\ -for that key. If no key given, use the entire line as key. With no\n\ -FILE, or when FILE is -, read standard input.\n\ -") - , DEFAULT_TMPDIR); - } - exit (status); -} + struct line *lines; /* Dynamically allocated array of lines. */ + int used; /* Number of slots used. */ + int alloc; /* Number of slots allocated. */ + int limit; /* Max number of slots to allocate. */ +}; + +/* Input buffers. */ +struct buffer +{ + char *buf; /* Dynamically allocated buffer. */ + int used; /* Number of bytes used. */ + int alloc; /* Number of bytes allocated. */ + int left; /* Number of bytes left after line parsing. */ +}; + +/* Lists of key field comparisons to be tried. */ +static struct keyfield +{ + int sword; /* Zero-origin 'word' to start at. */ + int schar; /* Additional characters to skip. */ + int skipsblanks; /* Skip leading white space at start. */ + int eword; /* Zero-origin first word after field. */ + int echar; /* Additional characters in field. */ + int skipeblanks; /* Skip trailing white space at finish. */ + int *ignore; /* Boolean array of characters to ignore. */ + char *translate; /* Translation applied to characters. */ + int numeric; /* Flag for numeric comparison. */ + int month; /* Flag for comparison by month name. */ + int reverse; /* Reverse the sense of comparison. */ + struct keyfield *next; /* Next keyfield to try. */ +} keyhead; /* The list of temporary files. */ static struct tempnode @@ -249,7 +199,7 @@ static struct tempnode /* Clean up any remaining temporary files. */ static void -cleanup (void) +cleanup () { struct tempnode *node; @@ -259,15 +209,16 @@ cleanup (void) /* Allocate N bytes of memory dynamically, with error checking. */ -static char * -xmalloc (unsigned int n) +char * +xmalloc (n) + unsigned n; { char *p; p = malloc (n); if (p == 0) { - error (0, 0, _("virtual memory exhausted")); + error (0, 0, "virtual memory exhausted"); cleanup (); exit (2); } @@ -279,8 +230,10 @@ xmalloc (unsigned int n) If P is NULL, run xmalloc. If N is 0, run free and return NULL. */ -static char * -xrealloc (char *p, unsigned int n) +char * +xrealloc (p, n) + char *p; + unsigned n; { if (p == 0) return xmalloc (n); @@ -292,7 +245,7 @@ xrealloc (char *p, unsigned int n) p = realloc (p, n); if (p == 0) { - error (0, 0, _("virtual memory exhausted")); + error (0, 0, "virtual memory exhausted"); cleanup (); exit (2); } @@ -300,81 +253,50 @@ xrealloc (char *p, unsigned int n) } static FILE * -xtmpfopen (const char *file) +xfopen (file, how) + char *file, *how; { - FILE *fp; - int fd; + FILE *fp = strcmp (file, "-") ? fopen (file, how) : stdin; - fd = open (file, O_WRONLY | O_CREAT | O_TRUNC, 0600); - if (fd < 0 || (fp = fdopen (fd, "w")) == NULL) + if (fp == 0) { error (0, errno, "%s", file); cleanup (); exit (2); } - - return fp; -} - -static FILE * -xfopen (const char *file, const char *how) -{ - FILE *fp; - - if (strcmp (file, "-") == 0) - { - fp = stdin; - } - else - { - if ((fp = fopen (file, how)) == NULL) - { - error (0, errno, "%s", file); - cleanup (); - exit (2); - } - } - if (fp == stdin) have_read_stdin = 1; return fp; } static void -xfclose (FILE *fp) +xfclose (fp) + FILE *fp; { - if (fp == stdin) - { - /* Allow reading stdin from tty more than once. */ - if (feof (fp)) - clearerr (fp); - } - else if (fp == stdout) - { - if (fflush (fp) != 0) - { - error (0, errno, _("flushing file")); - cleanup (); - exit (2); - } - } - else + fflush (fp); + if (fp != stdin && fp != stdout) { if (fclose (fp) != 0) { - error (0, errno, _("error closing file")); + error (0, errno, "error closing file"); cleanup (); exit (2); } } + else + /* Allow reading stdin from tty more than once. */ + clearerr (fp); } static void -xfwrite (const char *buf, int size, int nelem, FILE *fp) +xfwrite (buf, size, nelem, fp) + char *buf; + int size, nelem; + FILE *fp; { if (fwrite (buf, size, nelem, fp) != nelem) { - error (0, errno, _("write error")); + error (0, errno, "write error"); cleanup (); exit (2); } @@ -383,25 +305,18 @@ xfwrite (const char *buf, int size, int nelem, FILE *fp) /* Return a name for a temporary file. */ static char * -tempname (void) +tempname () { - static unsigned int seq; + static int seq; int len = strlen (temp_file_prefix); - char *name = xmalloc (len + 1 + sizeof ("sort") - 1 + 5 + 5 + 1); - struct tempnode *node; - - node = (struct tempnode *) xmalloc (sizeof (struct tempnode)); - sprintf (name, - "%s%ssort%5.5d%5.5d", - temp_file_prefix, - (len && temp_file_prefix[len - 1] != '/') ? "/" : "", - (unsigned int) getpid () & 0xffff, seq); - - /* Make sure that SEQ's value fits in 5 digits. */ - ++seq; - if (seq >= 100000) - seq = 0; + char *name = xmalloc (len + 16); + struct tempnode *node = + (struct tempnode *) xmalloc (sizeof (struct tempnode)); + if (len && temp_file_prefix[len - 1] != '/') + sprintf (name, "%s/sort%5.5d%5.5d", temp_file_prefix, getpid (), ++seq); + else + sprintf (name, "%ssort%5.5d%5.5d", temp_file_prefix, getpid (), ++seq); node->name = name; node->next = temphead.next; temphead.next = node; @@ -412,7 +327,8 @@ tempname (void) remove it if it is found on the list. */ static void -zaptemp (char *name) +zaptemp (name) + char *name; { struct tempnode *node, *temp; @@ -432,7 +348,7 @@ zaptemp (char *name) /* Initialize the character class tables. */ static void -inittables (void) +inittables () { int i; @@ -456,7 +372,9 @@ inittables (void) /* Initialize BUF, allocating ALLOC bytes initially. */ static void -initbuf (struct buffer *buf, int alloc) +initbuf (buf, alloc) + struct buffer *buf; + int alloc; { buf->alloc = alloc; buf->buf = xmalloc (buf->alloc); @@ -469,11 +387,13 @@ initbuf (struct buffer *buf, int alloc) of bytes buffered. */ static int -fillbuf (struct buffer *buf, FILE *fp) +fillbuf (buf, fp) + struct buffer *buf; + FILE *fp; { int cc; - memmove (buf->buf, buf->buf + buf->used - buf->left, buf->left); + bcopy (buf->buf + buf->used - buf->left, buf->buf, buf->left); buf->used = buf->left; while (!feof (fp) && (buf->used == 0 || !memchr (buf->buf, '\n', buf->used))) @@ -486,7 +406,7 @@ fillbuf (struct buffer *buf, FILE *fp) cc = fread (buf->buf + buf->used, 1, buf->alloc - buf->used, fp); if (ferror (fp)) { - error (0, errno, _("read error")); + error (0, errno, "read error"); cleanup (); exit (2); } @@ -511,7 +431,10 @@ fillbuf (struct buffer *buf, FILE *fp) for, ever. */ static void -initlines (struct lines *lines, int alloc, int limit) +initlines (lines, alloc, limit) + struct lines *lines; + int alloc; + int limit; { lines->alloc = alloc; lines->lines = (struct line *) xmalloc (lines->alloc * sizeof (struct line)); @@ -523,7 +446,9 @@ initlines (struct lines *lines, int alloc, int limit) by KEY in LINE. */ static char * -begfield (const struct line *line, const struct keyfield *key) +begfield (line, key) + struct line *line; + struct keyfield *key; { register char *ptr = line->text, *lim = ptr + line->length; register int sword = key->sword, schar = key->schar; @@ -549,10 +474,8 @@ begfield (const struct line *line, const struct keyfield *key) while (ptr < lim && blanks[UCHAR (*ptr)]) ++ptr; - if (ptr + schar <= lim) - ptr += schar; - else - ptr = lim; + while (ptr < lim && schar--) + ++ptr; return ptr; } @@ -561,28 +484,19 @@ begfield (const struct line *line, const struct keyfield *key) in LINE specified by KEY. */ static char * -limfield (const struct line *line, const struct keyfield *key) +limfield (line, key) + struct line *line; + struct keyfield *key; { register char *ptr = line->text, *lim = ptr + line->length; register int eword = key->eword, echar = key->echar; - /* Note: from the POSIX spec: - The leading field separator itself is included in - a field when -t is not used. FIXME: move this comment up... */ - - /* Move PTR past EWORD fields or to one past the last byte on LINE, - whichever comes first. If there are more than EWORD fields, leave - PTR pointing at the beginning of the field having zero-based index, - EWORD. If a delimiter character was specified (via -t), then that - `beginning' is the first character following the delimiting TAB. - Otherwise, leave PTR pointing at the first `blank' character after - the preceding field. */ if (tab) while (ptr < lim && eword--) { while (ptr < lim && *ptr != tab) ++ptr; - if (ptr < lim && (eword || echar > 0)) + if (ptr < lim && (eword || key->skipeblanks)) ++ptr; } else @@ -594,54 +508,23 @@ limfield (const struct line *line, const struct keyfield *key) ++ptr; } - /* Make LIM point to the end of (one byte past) the current field. */ - if (tab) - { - char *newlim; - newlim = memchr (ptr, tab, lim - ptr); - if (newlim) - lim = newlim; - } - else - { - char *newlim; - newlim = ptr; - while (newlim < lim && blanks[UCHAR (*newlim)]) - ++newlim; - while (newlim < lim && !blanks[UCHAR (*newlim)]) - ++newlim; - lim = newlim; - } - - /* If we're skipping leading blanks, don't start counting characters - until after skipping past any leading blanks. */ - if (key->skipsblanks) + if (key->skipeblanks) while (ptr < lim && blanks[UCHAR (*ptr)]) ++ptr; - /* Advance PTR by ECHAR (if possible), but no further than LIM. */ - if (ptr + echar <= lim) - ptr += echar; - else - ptr = lim; + while (ptr < lim && echar--) + ++ptr; return ptr; } -/* FIXME */ - -void -trim_trailing_blanks (const char *a_start, char **a_end) -{ - while (*a_end > a_start && blanks[UCHAR (*(*a_end - 1))]) - --(*a_end); -} - /* Find the lines in BUF, storing pointers and lengths in LINES. - Also replace newlines in BUF with NULs. */ + Also replace newlines with NULs. */ static void -findlines (struct buffer *buf, struct lines *lines) +findlines (buf, lines) + struct buffer *buf; + struct lines *lines; { register char *beg = buf->buf, *lim = buf->buf + buf->used, *ptr; struct keyfield *key = keyhead.next; @@ -686,11 +569,6 @@ findlines (struct buffer *buf, struct lines *lines) ++beg; lines->lines[lines->used].keybeg = beg; } - if (key->skipeblanks) - { - trim_trailing_blanks (lines->lines[lines->used].keybeg, - &lines->lines[lines->used].keylim); - } } else { @@ -710,7 +588,8 @@ findlines (struct buffer *buf, struct lines *lines) of the fraction. Strings not of this form are considered to be zero. */ static int -fraccompare (register const char *a, register const char *b) +fraccompare (a, b) + register char *a, *b; { register tmpa = UCHAR (*a), tmpb = UCHAR (*b); @@ -765,12 +644,12 @@ fraccompare (register const char *a, register const char *b) hideously fast. */ static int -numcompare (register const char *a, register const char *b) +numcompare (a, b) + register char *a, *b; { register int tmpa, tmpb, loga, logb, tmp; - tmpa = UCHAR (*a); - tmpb = UCHAR (*b); + tmpa = UCHAR (*a), tmpb = UCHAR (*b); while (blanks[tmpa]) tmpa = UCHAR (*++a); @@ -779,30 +658,19 @@ numcompare (register const char *a, register const char *b) if (tmpa == '-') { - do - tmpa = UCHAR (*++a); - while (tmpa == '0'); + tmpa = UCHAR (*++a); if (tmpb != '-') { - if (tmpa == '.') - do - tmpa = UCHAR (*++a); - while (tmpa == '0'); - if (digits[tmpa]) - return -1; - while (tmpb == '0') - tmpb = UCHAR (*++b); - if (tmpb == '.') - do - tmpb = *++b; - while (tmpb == '0'); - if (digits[tmpb]) + if (digits[tmpa] && digits[tmpb]) return -1; return 0; } - do + tmpb = UCHAR (*++b); + + while (tmpa == '0') + tmpa = UCHAR (*++a); + while (tmpb == '0') tmpb = UCHAR (*++b); - while (tmpb == '0'); while (tmpa == tmpb && digits[tmpa]) tmpa = UCHAR (*++a), tmpb = UCHAR (*++b); @@ -832,22 +700,7 @@ numcompare (register const char *a, register const char *b) } else if (tmpb == '-') { - do - tmpb = UCHAR (*++b); - while (tmpb == '0'); - if (tmpb == '.') - do - tmpb = *++b; - while (tmpb == '0'); - if (digits[tmpb]) - return 1; - while (tmpa == '0') - tmpa = UCHAR (*++a); - if (tmpa == '.') - do - tmpa = UCHAR (*++a); - while (tmpa == '0'); - if (digits[tmpa]) + if (digits[UCHAR (tmpa)] && digits[UCHAR (*++b)]) return 1; return 0; } @@ -886,29 +739,13 @@ numcompare (register const char *a, register const char *b) } } -static int -general_numcompare (const char *sa, const char *sb) -{ - double a, b; - /* FIXME: add option to warn about failed conversions. */ - /* FIXME: maybe add option to try expensive FP conversion - only if A and B can't be compared more cheaply/accurately. */ - if (xstrtod (sa, NULL, &a)) - { - a = 0; - } - if (xstrtod (sb, NULL, &b)) - { - b = 0; - } - return a == b ? 0 : a < b ? -1 : 1; -} - /* Return an integer <= 12 associated with month name S with length LEN, 0 if the name in S is not recognized. */ static int -getmonth (const char *s, int len) +getmonth (s, len) + char *s; + int len; { char month[4]; register int i, lo = 0, hi = 12; @@ -937,7 +774,8 @@ getmonth (const char *s, int len) are no more keys or a difference is found. */ static int -keycompare (const struct line *a, const struct line *b) +keycompare (a, b) + struct line *a, *b; { register char *texta, *textb, *lima, *limb, *translate; register int *ignore; @@ -986,16 +824,6 @@ keycompare (const struct line *a, const struct line *b) if (lenb < 0) lenb = 0; - if (key->skipeblanks) - { - char *a_end = texta + lena; - char *b_end = textb + lenb; - trim_trailing_blanks (texta, &a_end); - trim_trailing_blanks (textb, &b_end); - lena = a_end - texta; - lenb = b_end - textb; - } - /* Actually compare the fields. */ if (key->numeric) { @@ -1014,23 +842,6 @@ keycompare (const struct line *a, const struct line *b) return key->reverse ? -diff : diff; continue; } - else if (key->general_numeric) - { - if (*lima || *limb) - { - char savea = *lima, saveb = *limb; - - *lima = *limb = '\0'; - diff = general_numcompare (texta, textb); - *lima = savea, *limb = saveb; - } - else - diff = general_numcompare (texta, textb); - - if (diff) - return key->reverse ? -diff : diff; - continue; - } else if (key->month) { diff = getmonth (texta, lena) - getmonth (textb, lenb); @@ -1039,64 +850,38 @@ keycompare (const struct line *a, const struct line *b) continue; } else if (ignore && translate) - -#define CMP_WITH_IGNORE(A, B) \ - do \ - { \ - while (texta < lima && textb < limb) \ - { \ - while (texta < lima && ignore[UCHAR (*texta)]) \ - ++texta; \ - while (textb < limb && ignore[UCHAR (*textb)]) \ - ++textb; \ - if (texta < lima && textb < limb) \ - { \ - if ((A) != (B)) \ - { \ - diff = (A) - (B); \ - break; \ - } \ - ++texta; \ - ++textb; \ - } \ - \ - if (texta == lima && textb < limb && !ignore[UCHAR (*textb)]) \ - diff = -1; \ - else if (texta < lima && textb == limb \ - && !ignore[UCHAR (*texta)]) \ - diff = 1; \ - } \ - \ - if (diff == 0) \ - { \ - while (texta < lima && ignore[UCHAR (*texta)]) \ - ++texta; \ - while (textb < limb && ignore[UCHAR (*textb)]) \ - ++textb; \ - \ - if (texta == lima && textb < limb) \ - diff = -1; \ - else if (texta < lima && textb == limb) \ - diff = 1; \ - } \ - /* Relative lengths are meaningless if characters were ignored. \ - Handling this case here avoids what might be an invalid length \ - comparison below. */ \ - if (diff == 0 && texta == lima && textb == limb) \ - return 0; \ - } \ - while (0) - - CMP_WITH_IGNORE (translate[UCHAR (*texta)], translate[UCHAR (*textb)]); + while (texta < lima && textb < limb) + { + while (texta < lima && ignore[UCHAR (*texta)]) + ++texta; + while (textb < limb && ignore[UCHAR (*textb)]) + ++textb; + if (texta < lima && textb < limb && + translate[UCHAR (*texta++)] != translate[UCHAR (*textb++)]) + { + diff = translate[UCHAR (*--texta)] - translate[UCHAR (*--textb)]; + break; + } + } else if (ignore) - CMP_WITH_IGNORE (*texta, *textb); + while (texta < lima && textb < limb) + { + while (texta < lima && ignore[UCHAR (*texta)]) + ++texta; + while (textb < limb && ignore[UCHAR (*textb)]) + ++textb; + if (texta < lima && textb < limb && *texta++ != *textb++) + { + diff = *--texta - *--textb; + break; + } + } else if (translate) while (texta < lima && textb < limb) { if (translate[UCHAR (*texta++)] != translate[UCHAR (*textb++)]) { - diff = (translate[UCHAR (*--texta)] - - translate[UCHAR (*--textb)]); + diff = translate[UCHAR (*--texta)] - translate[UCHAR (*--textb)]; break; } } @@ -1116,7 +901,8 @@ keycompare (const struct line *a, const struct line *b) depending on whether A compares less than, equal to, or greater than B. */ static int -compare (register const struct line *a, register const struct line *b) +compare (a, b) + register struct line *a, *b; { int diff, tmpa, tmpb, mini; @@ -1155,17 +941,18 @@ compare (register const struct line *a, register const struct line *b) } /* Check that the lines read from the given FP come in order. Return - 1 if they do and 0 if there is a disorder. - FIXME: return number of first out-of-order line if not sorted. */ + 1 if they do and 0 if there is a disorder. */ static int -checkfp (FILE *fp) +checkfp (fp) + FILE *fp; { struct buffer buf; /* Input buffer. */ struct lines lines; /* Lines scanned from the buffer. */ struct line temp; /* Copy of previous line. */ int cc; /* Character count. */ - int alloc, sorted = 1; + int cmp; /* Result of calling compare. */ + int alloc, i, success = 1; initbuf (&buf, mergealloc); initlines (&lines, mergealloc / linelength + 1, @@ -1174,69 +961,64 @@ checkfp (FILE *fp) temp.text = xmalloc (alloc); cc = fillbuf (&buf, fp); - if (cc == 0) - goto finish; - findlines (&buf, &lines); - while (1) - { - struct line *prev_line; /* Pointer to previous line. */ - int cmp; /* Result of calling compare. */ - int i; + if (cc) + do + { + /* Compare each line in the buffer with its successor. */ + for (i = 0; i < lines.used - 1; ++i) + { + cmp = compare (&lines.lines[i], &lines.lines[i + 1]); + if ((unique && cmp >= 0) || (cmp > 0)) + { + success = 0; + goto finish; + } + } - /* Compare each line in the buffer with its successor. */ - for (i = 0; i < lines.used - 1; ++i) - { - cmp = compare (&lines.lines[i], &lines.lines[i + 1]); - if ((unique && cmp >= 0) || (cmp > 0)) - { - sorted = 0; - goto finish; - } - } + /* Save the last line of the buffer and refill the buffer. */ + if (lines.lines[lines.used - 1].length > alloc) + { + while (lines.lines[lines.used - 1].length + 1 > alloc) + alloc *= 2; + temp.text = xrealloc (temp.text, alloc); + } + bcopy (lines.lines[lines.used - 1].text, temp.text, + lines.lines[lines.used - 1].length + 1); + temp.length = lines.lines[lines.used - 1].length; - /* Save the last line of the buffer and refill the buffer. */ - prev_line = lines.lines + (lines.used - 1); - if (prev_line->length > alloc) - { - while (prev_line->length + 1 > alloc) - alloc *= 2; - temp.text = xrealloc (temp.text, alloc); - } - memcpy (temp.text, prev_line->text, prev_line->length + 1); - temp.length = prev_line->length; - temp.keybeg = temp.text + (prev_line->keybeg - prev_line->text); - temp.keylim = temp.text + (prev_line->keylim - prev_line->text); - - cc = fillbuf (&buf, fp); - if (cc == 0) - break; - - findlines (&buf, &lines); - /* Make sure the line saved from the old buffer contents is - less than or equal to the first line of the new buffer. */ - cmp = compare (&temp, &lines.lines[0]); - if ((unique && cmp >= 0) || (cmp > 0)) - { - sorted = 0; - break; - } - } + cc = fillbuf (&buf, fp); + if (cc) + { + findlines (&buf, &lines); + /* Make sure the line saved from the old buffer contents is + less than or equal to the first line of the new buffer. */ + cmp = compare (&temp, &lines.lines[0]); + if ((unique && cmp >= 0) || (cmp > 0)) + { + success = 0; + break; + } + } + } + while (cc); finish: xfclose (fp); free (buf.buf); free ((char *) lines.lines); free (temp.text); - return sorted; + return success; } /* Merge lines from FPS onto OFP. NFPS cannot be greater than NMERGE. Close FPS before returning. */ static void -mergefps (FILE **fps, register int nfps, FILE *ofp) +mergefps (fps, nfps, ofp) + FILE *fps[], *ofp; + register int nfps; { struct buffer buffer[NMERGE]; /* Input buffers for each file. */ struct lines lines[NMERGE]; /* Line tables for each buffer. */ @@ -1250,10 +1032,6 @@ mergefps (FILE **fps, register int nfps, FILE *ofp) output. */ register int i, j, t; -#ifdef lint /* Suppress `used before initialized' warning. */ - savealloc = 0; -#endif - /* Allocate space for a saved line if necessary. */ if (unique) { @@ -1297,7 +1075,7 @@ mergefps (FILE **fps, register int nfps, FILE *ofp) /* Repeatedly output the smallest line until no input remains. */ while (nfps) { - /* If uniqified output is turned on, output only the first of + /* If uniqified output is turned out, output only the first of an identical series of lines. */ if (unique) { @@ -1316,7 +1094,7 @@ mergefps (FILE **fps, register int nfps, FILE *ofp) saved.text = xrealloc (saved.text, savealloc); } saved.length = lines[ord[0]].lines[cur[ord[0]]].length; - memcpy (saved.text, lines[ord[0]].lines[cur[ord[0]]].text, + bcopy (lines[ord[0]].lines[cur[ord[0]]].text, saved.text, saved.length + 1); if (lines[ord[0]].lines[cur[ord[0]]].keybeg != NULL) { @@ -1398,7 +1176,9 @@ mergefps (FILE **fps, register int nfps, FILE *ofp) /* Sort the array LINES with NLINES members, using TEMP for temporary space. */ static void -sortlines (struct line *lines, int nlines, struct line *temp) +sortlines (lines, nlines, temp) + struct line *lines, *temp; + int nlines; { register struct line *lo, *hi, *t; register int nlo, nhi; @@ -1439,7 +1219,9 @@ sortlines (struct line *lines, int nlines, struct line *temp) Return a count of disordered files. */ static int -check (char **files, int nfiles) +check (files, nfiles) + char *files[]; + int nfiles; { int i, disorders = 0; FILE *fp; @@ -1449,7 +1231,7 @@ check (char **files, int nfiles) fp = xfopen (files[i], "r"); if (!checkfp (fp)) { - fprintf (stderr, _("%s: disorder on %s\n"), program_name, files[i]); + printf ("%s: disorder on %s\n", program_name, files[i]); ++disorders; } } @@ -1459,7 +1241,10 @@ check (char **files, int nfiles) /* Merge NFILES FILES onto OFP. */ static void -merge (char **files, int nfiles, FILE *ofp) +merge (files, nfiles, ofp) + char *files[]; + int nfiles; + FILE *ofp; { int i, j, t; char *temp; @@ -1472,7 +1257,7 @@ merge (char **files, int nfiles, FILE *ofp) { for (j = 0; j < NMERGE; ++j) fps[j] = xfopen (files[i * NMERGE + j], "r"); - tfp = xtmpfopen (temp = tempname ()); + tfp = xfopen (temp = tempname (), "w"); mergefps (fps, NMERGE, tfp); xfclose (tfp); for (j = 0; j < NMERGE; ++j) @@ -1481,7 +1266,7 @@ merge (char **files, int nfiles, FILE *ofp) } for (j = 0; j < nfiles % NMERGE; ++j) fps[j] = xfopen (files[i * NMERGE + j], "r"); - tfp = xtmpfopen (temp = tempname ()); + tfp = xfopen (temp = tempname (), "w"); mergefps (fps, nfiles % NMERGE, tfp); xfclose (tfp); for (j = 0; j < nfiles % NMERGE; ++j) @@ -1500,7 +1285,10 @@ merge (char **files, int nfiles, FILE *ofp) /* Sort NFILES FILES onto OFP. */ static void -sort (char **files, int nfiles, FILE *ofp) +sort (files, nfiles, ofp) + char **files; + int nfiles; + FILE *ofp; { struct buffer buf; struct lines lines; @@ -1508,7 +1296,7 @@ sort (char **files, int nfiles, FILE *ofp) int i, ntmp; FILE *fp, *tfp; struct tempnode *node; - int n_temp_files = 0; + int ntemp = 0; char **tempfiles; initbuf (&buf, sortalloc); @@ -1531,12 +1319,12 @@ sort (char **files, int nfiles, FILE *ofp) xrealloc ((char *) tmp, ntmp * sizeof (struct line)); } sortlines (lines.lines, lines.used, tmp); - if (feof (fp) && !nfiles && !n_temp_files && !buf.left) + if (feof (fp) && !nfiles && !ntemp && !buf.left) tfp = ofp; else { - ++n_temp_files; - tfp = xtmpfopen (tempname ()); + ++ntemp; + tfp = xfopen (tempname (), "w"); } for (i = 0; i < lines.used; ++i) if (!unique || i == 0 @@ -1555,13 +1343,13 @@ sort (char **files, int nfiles, FILE *ofp) free ((char *) lines.lines); free ((char *) tmp); - if (n_temp_files) + if (ntemp) { - tempfiles = (char **) xmalloc (n_temp_files * sizeof (char *)); - i = n_temp_files; + tempfiles = (char **) xmalloc (ntemp * sizeof (char *)); + i = ntemp; for (node = temphead.next; i > 0; node = node->next) tempfiles[--i] = node->name; - merge (tempfiles, n_temp_files, ofp); + merge (tempfiles, ntemp, ofp); free ((char *) tempfiles); } } @@ -1569,7 +1357,8 @@ sort (char **files, int nfiles, FILE *ofp) /* Insert key KEY at the end of the list (`keyhead'). */ static void -insertkey (struct keyfield *key) +insertkey (key) + struct keyfield *key; { struct keyfield *k = &keyhead; @@ -1580,26 +1369,28 @@ insertkey (struct keyfield *key) } static void -badfieldspec (const char *s) +badfieldspec (s) + char *s; { - error (2, 0, _("invalid field specification `%s'"), s); + error (2, 0, "invalid field specification `%s'", s); } /* Handle interrupts and hangups. */ static void -sighandler (int sig) +sighandler (sig) + int sig; { -#ifdef SA_INTERRUPT +#ifdef _POSIX_VERSION struct sigaction sigact; sigact.sa_handler = SIG_DFL; sigemptyset (&sigact.sa_mask); sigact.sa_flags = 0; sigaction (sig, &sigact, NULL); -#else /* !SA_INTERRUPT */ +#else /* !_POSIX_VERSION */ signal (sig, SIG_DFL); -#endif /* SA_INTERRUPT */ +#endif /* _POSIX_VERSION */ cleanup (); kill (getpid (), sig); } @@ -1610,8 +1401,10 @@ sighandler (int sig) BLANKTYPE is the kind of blanks that 'b' should skip. */ static char * -set_ordering (register const char *s, struct keyfield *key, - enum blanktype blanktype) +set_ordering (s, key, blanktype) + register char *s; + struct keyfield *key; + enum blanktype blanktype; { while (*s) { @@ -1629,9 +1422,11 @@ set_ordering (register const char *s, struct keyfield *key, case 'f': key->translate = fold_toupper; break; +#if 0 case 'g': - key->general_numeric = 1; + /* Reserved for comparing floating-point numbers. */ break; +#endif case 'i': key->ignore = nonprinting; break; @@ -1640,24 +1435,22 @@ set_ordering (register const char *s, struct keyfield *key, break; case 'n': key->numeric = 1; - if (blanktype == bl_start || blanktype == bl_both) - key->skipsblanks = 1; - if (blanktype == bl_end || blanktype == bl_both) - key->skipeblanks = 1; break; case 'r': key->reverse = 1; break; default: - return (char *) s; + return s; } ++s; } - return (char *) s; + return s; } void -main (int argc, char **argv) +main (argc, argv) + int argc; + char *argv[]; { struct keyfield *key = NULL, gkey; char *s; @@ -1665,22 +1458,25 @@ main (int argc, char **argv) int checkonly = 0, mergeonly = 0, nfiles = 0; char *minus = "-", *outfile = minus, **files, *tmp; FILE *ofp; -#ifdef SA_INTERRUPT +#ifdef _POSIX_VERSION struct sigaction oldact, newact; -#endif /* SA_INTERRUPT */ +#endif /* _POSIX_VERSION */ +#ifdef __FreeBSD__ + (void) setlocale(LC_CTYPE, ""); +#endif program_name = argv[0]; - parse_long_options (argc, argv, "sort", version_string, usage); + parse_long_options (argc, argv, usage); have_read_stdin = 0; inittables (); temp_file_prefix = getenv ("TMPDIR"); if (temp_file_prefix == NULL) - temp_file_prefix = DEFAULT_TMPDIR; + temp_file_prefix = "/tmp"; -#ifdef SA_INTERRUPT +#ifdef _POSIX_VERSION newact.sa_handler = sighandler; sigemptyset (&newact.sa_mask); newact.sa_flags = 0; @@ -1697,7 +1493,7 @@ main (int argc, char **argv) sigaction (SIGTERM, NULL, &oldact); if (oldact.sa_handler != SIG_IGN) sigaction (SIGTERM, &newact, NULL); -#else /* !SA_INTERRUPT */ +#else /* !_POSIX_VERSION */ if (signal (SIGINT, SIG_IGN) != SIG_IGN) signal (SIGINT, sighandler); if (signal (SIGHUP, SIG_IGN) != SIG_IGN) @@ -1706,12 +1502,12 @@ main (int argc, char **argv) signal (SIGPIPE, sighandler); if (signal (SIGTERM, SIG_IGN) != SIG_IGN) signal (SIGTERM, sighandler); -#endif /* !SA_INTERRUPT */ +#endif /* !_POSIX_VERSION */ gkey.sword = gkey.eword = -1; gkey.ignore = NULL; gkey.translate = NULL; - gkey.numeric = gkey.general_numeric = gkey.month = gkey.reverse = 0; + gkey.numeric = gkey.month = gkey.reverse = 0; gkey.skipsblanks = gkey.skipeblanks = 0; files = (char **) xmalloc (sizeof (char *) * argc); @@ -1727,9 +1523,9 @@ main (int argc, char **argv) key->ignore = NULL; key->translate = NULL; key->skipsblanks = key->skipeblanks = 0; - key->numeric = key->general_numeric = key->month = key->reverse = 0; + key->numeric = key->month = key->reverse = 0; s = argv[i] + 1; - if (! (digits[UCHAR (*s)] || (*s == '.' && digits[UCHAR (s[1])]))) + if (!digits[UCHAR (*s)]) badfieldspec (argv[i]); for (t = 0; digits[UCHAR (*s)]; ++s) t = 10 * t + *s - '0'; @@ -1751,7 +1547,7 @@ main (int argc, char **argv) else if (argv[i][0] == '-' && argv[i][1]) { s = argv[i] + 1; - if (digits[UCHAR (*s)] || (*s == '.' && digits[UCHAR (s[1])])) + if (digits[UCHAR (*s)]) { if (!key) usage (2); @@ -1786,7 +1582,7 @@ main (int argc, char **argv) else { if (i == argc - 1) - error (2, 0, _("option `-k' requires an argument")); + error (2, 0, "option `-k' requires an argument"); else s = argv[++i]; } @@ -1804,34 +1600,15 @@ main (int argc, char **argv) badfieldspec (argv[i]); for (t = 0; digits[UCHAR (*s)]; ++s) t = 10 * t + *s - '0'; - if (t == 0) - { - /* Provoke with `sort -k0' */ - error (0, 0, _("the starting field number argument \ -to the `-k' option must be positive")); - badfieldspec (argv[i]); - } - --t; + if (t) + t--; t2 = 0; if (*s == '.') { - if (!digits[UCHAR (s[1])]) - { - /* Provoke with `sort -k1.' */ - error (0, 0, _("starting field spec has `.' but \ -lacks following character offset")); - badfieldspec (argv[i]); - } for (++s; digits[UCHAR (*s)]; ++s) t2 = 10 * t2 + *s - '0'; - if (t2 == 0) - { - /* Provoke with `sort -k1.0' */ - error (0, 0, _("starting field character offset \ -argument to the `-k' option\nmust be positive")); - badfieldspec (argv[i]); - } - --t2; + if (t2) + t2--; } if (t2 || t) { @@ -1841,52 +1618,20 @@ argument to the `-k' option\nmust be positive")); else key->sword = -1; s = set_ordering (s, key, bl_start); - if (*s == 0) - { - key->eword = -1; - key->echar = 0; - } - else if (*s != ',') + if (*s && *s != ',') badfieldspec (argv[i]); - else if (*s == ',') + else if (*s++) { - /* Skip over comma. */ - ++s; - if (*s == 0) - { - /* Provoke with `sort -k1,' */ - error (0, 0, _("field specification has `,' but \ -lacks following field spec")); - badfieldspec (argv[i]); - } /* Get POS2. */ for (t = 0; digits[UCHAR (*s)]; ++s) t = t * 10 + *s - '0'; - if (t == 0) - { - /* Provoke with `sort -k1,0' */ - error (0, 0, _("ending field number argument \ -to the `-k' option must be positive")); - badfieldspec (argv[i]); - } - --t; t2 = 0; if (*s == '.') { - if (!digits[UCHAR (s[1])]) - { - /* Provoke with `sort -k1,1.' */ - error (0, 0, _("ending field spec has `.' \ -but lacks following character offset")); - badfieldspec (argv[i]); - } for (++s; digits[UCHAR (*s)]; ++s) t2 = t2 * 10 + *s - '0'; - } - else - { - /* `-k 2,3' is equivalent to `+1 -3'. */ - ++t; + if (t2) + t--; } key->eword = t; key->echar = t2; @@ -1906,7 +1651,7 @@ but lacks following character offset")); else { if (i == argc - 1) - error (2, 0, _("option `-o' requires an argument")); + error (2, 0, "option `-o' requires an argument"); else outfile = argv[++i]; } @@ -1923,20 +1668,19 @@ but lacks following character offset")); goto outer; } else - error (2, 0, _("option `-t' requires an argument")); + error (2, 0, "option `-t' requires an argument"); break; case 'T': if (s[1]) temp_file_prefix = ++s; - else + else if (i < argc - 1) { - if (i < argc - 1) - temp_file_prefix = argv[++i]; - else - error (2, 0, _("option `-T' requires an argument")); + temp_file_prefix = argv[++i]; + goto outer; } - goto outer; - /* break; */ + else + error (2, 0, "option `-T' requires an argument"); + break; case 'u': unique = 1; break; @@ -1945,7 +1689,7 @@ but lacks following character offset")); Solaris 2. */ goto outer; default: - fprintf (stderr, _("%s: unrecognized option `-%c'\n"), + fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], *s); usage (2); } @@ -1966,8 +1710,7 @@ but lacks following character offset")); /* Inheritance of global options to individual keys. */ for (key = keyhead.next; key; key = key->next) if (!key->ignore && !key->translate && !key->skipsblanks && !key->reverse - && !key->skipeblanks && !key->month && !key->numeric - && !key->general_numeric) + && !key->skipeblanks && !key->month && !key->numeric) { key->ignore = gkey.ignore; key->translate = gkey.translate; @@ -1975,13 +1718,11 @@ but lacks following character offset")); key->skipeblanks = gkey.skipeblanks; key->month = gkey.month; key->numeric = gkey.numeric; - key->general_numeric = gkey.general_numeric; key->reverse = gkey.reverse; } if (!keyhead.next && (gkey.ignore || gkey.translate || gkey.skipsblanks - || gkey.skipeblanks || gkey.month || gkey.numeric - || gkey.general_numeric)) + || gkey.skipeblanks || gkey.month || gkey.numeric)) insertkey (&gkey); reverse = gkey.reverse; @@ -1996,61 +1737,32 @@ but lacks following character offset")); if (strcmp (outfile, "-")) { - struct stat outstat; - if (stat (outfile, &outstat) == 0) + for (i = 0; i < nfiles; ++i) + if (!strcmp (outfile, files[i])) + break; + if (i == nfiles) + ofp = xfopen (outfile, "w"); + else { - /* The following code prevents a race condition when - people use the brain dead shell programming idiom: - cat file | sort -o file - This feature is provided for historical compatibility, - but we strongly discourage ever relying on this in - new shell programs. */ - - /* Temporarily copy each input file that might be another name - for the output file. When in doubt (e.g. a pipe), copy. */ - for (i = 0; i < nfiles; ++i) + char buf[8192]; + FILE *fp = xfopen (outfile, "r"); + int cc; + + tmp = tempname (); + ofp = xfopen (tmp, "w"); + while ((cc = fread (buf, 1, sizeof buf, fp)) > 0) + xfwrite (buf, 1, cc, ofp); + if (ferror (fp)) { - char buf[8192]; - FILE *fp; - int cc; - - if (S_ISREG (outstat.st_mode) && strcmp (outfile, files[i])) - { - struct stat instat; - if ((strcmp (files[i], "-") - ? stat (files[i], &instat) - : fstat (fileno (stdin), &instat)) != 0) - { - error (0, errno, "%s", files[i]); - cleanup (); - exit (2); - } - if (S_ISREG (instat.st_mode) - && (instat.st_ino != outstat.st_ino - || instat.st_dev != outstat.st_dev)) - { - /* We know the files are distinct. */ - continue; - } - } - - fp = xfopen (files[i], "r"); - tmp = tempname (); - ofp = xtmpfopen (tmp); - while ((cc = fread (buf, 1, sizeof buf, fp)) > 0) - xfwrite (buf, 1, cc, ofp); - if (ferror (fp)) - { - error (0, errno, "%s", files[i]); - cleanup (); - exit (2); - } - xfclose (ofp); - xfclose (fp); - files[i] = tmp; + error (0, errno, "%s", outfile); + cleanup (); + exit (2); } + xfclose (ofp); + xfclose (fp); + files[i] = tmp; + ofp = xfopen (outfile, "w"); } - ofp = xfopen (outfile, "w"); } else ofp = stdout; @@ -2067,12 +1779,57 @@ but lacks following character offset")); Solaris, Ultrix, and Irix. This premature fflush makes the output reappear. --karl@cs.umb.edu */ if (fflush (ofp) < 0) - error (1, errno, _("%s: write error"), outfile); + error (1, errno, "fflush", outfile); if (have_read_stdin && fclose (stdin) == EOF) - error (1, errno, outfile); + error (1, errno, "-"); if (ferror (stdout) || fclose (stdout) == EOF) - error (1, errno, _("%s: write error"), outfile); + error (1, errno, "write error"); exit (0); } + +static void +usage (status) + int status; +{ + if (status != 0) + fprintf (stderr, "Try `%s --help' for more information.\n", + program_name); + else + { + printf ("\ +Usage: %s [OPTION]... [FILE]...\n\ +", + program_name); + printf ("\ +\n\ + +POS1 [-POS2] start a key at POS1, end it before POS2\n\ + -M compare (unknown) < `JAN' < ... < `DEC', imply -b\n\ + -T DIRECT use DIRECTfor temporary files, not $TEMPDIR nor /tmp\n\ + -b ignore leading blanks in sort fields or keys\n\ + -c check if given files already sorted, do not sort\n\ + -d consider only [a-zA-Z0-9 ] characters in keys\n\ + -f fold lower case to upper case characters in keys\n\ + -i consider only [\\040-\\0176] characters in keys\n\ + -k POS1[,POS2] same as +POS1 [-POS2], but all positions counted from 1\n\ + -m merge already sorted files, do not sort\n\ + -n compare according to string numerical value, imply -b\n\ + -o FILE write result on FILE instead of standard output\n\ + -r reverse the result of comparisons\n\ + -s stabilize sort by disabling last resort comparison\n\ + -t SEP use SEParator instead of non- to whitespace transition\n\ + -u with -c, check for strict ordering\n\ + -u with -m, only output the first of an equal sequence\n\ + --help display this help and exit\n\ + --version output version information and exit\n\ +\n\ +POS is F[.C][OPTS], where F is the field number and C the character\n\ +position in the field, both counted from zero. OPTS is made up of one\n\ +or more of Mbdfinr, this effectively disable global -Mbdfinr settings\n\ +for that key. If no key given, use the entire line as key. With no\n\ +FILE, or when FILE is -, read standard input.\n\ +"); + } + exit (status); +} |