diff options
Diffstat (limited to 'gnu/usr.bin/sort/sort.c')
-rw-r--r-- | gnu/usr.bin/sort/sort.c | 2149 |
1 files changed, 0 insertions, 2149 deletions
diff --git a/gnu/usr.bin/sort/sort.c b/gnu/usr.bin/sort/sort.c deleted file mode 100644 index 90082ef..0000000 --- a/gnu/usr.bin/sort/sort.c +++ /dev/null @@ -1,2149 +0,0 @@ -/* sort - sort lines of text (with all kinds of options). - Copyright (C) 1988, 1991, 1992, 1993, 1994, 1995 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 - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - Written 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. */ - -/* $FreeBSD$ */ - -#include <config.h> - -/* Get isblank from GNU libc. */ -#define _GNU_SOURCE - -#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 -#include <limits.h> -#else -#ifndef UCHAR_MAX -#define UCHAR_MAX 255 -#endif -#endif -#ifndef STDC_HEADERS -char *malloc (); -char *realloc (); -void free (); -#endif - -/* Undefine, to avoid warning about redefinition on some systems. */ -#undef min -#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; - -/* Table of digits. */ -static int digits[UCHAR_LIM]; - -/* Table of white space. */ -static int blanks[UCHAR_LIM]; - -/* Table of non-printing characters. */ -static int nonprinting[UCHAR_LIM]; - -/* Table of non-dictionary characters (not letters, digits, or blanks). */ -static int nondictionary[UCHAR_LIM]; - -/* Translation table folding lower case to upper. */ -static char fold_toupper[UCHAR_LIM]; - -/* Table mapping 3-letter month names to integers. - Alphabetic order allows binary search. */ -static struct month const monthtab[] = -{ - {"APR", 4}, - {"AUG", 8}, - {"DEC", 12}, - {"FEB", 2}, - {"JAN", 1}, - {"JUL", 7}, - {"JUN", 6}, - {"MAR", 3}, - {"MAY", 5}, - {"NOV", 11}, - {"OCT", 10}, - {"SEP", 9} -}; - -/* During the merge phase, the number of files to merge at once. */ -#define NMERGE 16 - -/* Initial buffer size for in core sorting. Will not grow unless a - line longer than this is seen. */ -static int sortalloc = 512 * 1024; - -/* 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; - -/* 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) - -/* Prefix for temporary file names. */ -static char *temp_file_prefix; - -/* Flag to reverse the order of all comparisons. */ -static int reverse; - -/* Flag for stable sort. This turns off the last ditch bytewise - comparison of lines, and instead leaves lines in the same order - they were read if all keys compare equal. */ -static int stable; - -/* Tab character separating fields. If NUL, then fields are separated - by the empty string between a non-whitespace character and a whitespace - character. */ -static char tab; - -/* Flag to remove consecutive duplicate lines from the output. - Only the last of a sequence of equal lines will be output. */ -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; - -#ifdef __FreeBSD__ -static int -COLLDIFF (int a, int b) -{ - static char s[2][2]; - - if ((unsigned char)a == (unsigned char)b) - return 0; - s[0][0] = a; - s[1][0] = b; - return strcoll(s[0], s[1]); -} - -static int -collcmp(char *a, char *b, int mini) -{ - char sa, sb; - int r; - - sa = a[mini]; - a[mini] = '\0'; - sb = b[mini]; - b[mini] = '\0'; - r = strcoll(a, b); - a[mini] = sa; - b[mini] = sb; - return r; -} -#endif /* __FreeBSD__ */ - -static void -usage (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 (_("\ -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); -} - -/* The list of temporary files. */ -static struct tempnode -{ - char *name; - struct tempnode *next; -} temphead; - -/* Clean up any remaining temporary files. */ - -static void -cleanup (void) -{ - struct tempnode *node; - - for (node = temphead.next; node; node = node->next) - unlink (node->name); -} - -/* Allocate N bytes of memory dynamically, with error checking. */ - -static char * -xmalloc (unsigned int n) -{ - char *p; - - p = malloc (n); - if (p == 0) - { - error (0, 0, _("virtual memory exhausted")); - cleanup (); - exit (2); - } - return p; -} - -/* Change the size of an allocated block of memory P to N bytes, - with error checking. - If P is NULL, run xmalloc. - If N is 0, run free and return NULL. */ - -static char * -xrealloc (char *p, unsigned int n) -{ - if (p == 0) - return xmalloc (n); - if (n == 0) - { - free (p); - return 0; - } - p = realloc (p, n); - if (p == 0) - { - error (0, 0, _("virtual memory exhausted")); - cleanup (); - exit (2); - } - return p; -} - -static FILE * -xtmpfopen (const char *file) -{ - FILE *fp; - int fd; - - fd = open (file, O_WRONLY | O_CREAT | O_TRUNC, 0600); - if (fd < 0 || (fp = fdopen (fd, "w")) == NULL) - { - 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) -{ - 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 - { - if (fclose (fp) != 0) - { - error (0, errno, _("error closing file")); - cleanup (); - exit (2); - } - } -} - -static void -xfwrite (const char *buf, int size, int nelem, FILE *fp) -{ - if (fwrite (buf, size, nelem, fp) != nelem) - { - error (0, errno, _("write error")); - cleanup (); - exit (2); - } -} - -/* Return a name for a temporary file. */ - -static char * -tempname (void) -{ - int fd; - 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%ssortXXXXXX", - temp_file_prefix, - (len && temp_file_prefix[len - 1] != '/') ? "/" : ""); - - if ((fd = mkstemp(name)) == -1) - { - error (0, errno, _("mkstemp error")); - cleanup (); - exit (2); - } - close(fd); - - node->name = name; - node->next = temphead.next; - temphead.next = node; - return name; -} - -/* Search through the list of temporary files for NAME; - remove it if it is found on the list. */ - -static void -zaptemp (char *name) -{ - struct tempnode *node, *temp; - - for (node = &temphead; node->next; node = node->next) - if (!strcmp (name, node->next->name)) - break; - if (node->next) - { - temp = node->next; - unlink (temp->name); - free (temp->name); - node->next = temp->next; - free ((char *) temp); - } -} - -/* Initialize the character class tables. */ - -static void -inittables (void) -{ - int i; - - for (i = 0; i < UCHAR_LIM; ++i) - { - if (ISBLANK (i)) - blanks[i] = 1; - if (ISDIGIT (i)) - digits[i] = 1; - if (!ISPRINT (i)) - nonprinting[i] = 1; - if (!ISALNUM (i) && !ISBLANK (i)) - nondictionary[i] = 1; - if (ISLOWER (i)) - fold_toupper[i] = toupper (i); - else - fold_toupper[i] = i; - } -} - -/* Initialize BUF, allocating ALLOC bytes initially. */ - -static void -initbuf (struct buffer *buf, int alloc) -{ - buf->alloc = alloc; - buf->buf = xmalloc (buf->alloc); - buf->used = buf->left = 0; -} - -/* Fill BUF reading from FP, moving buf->left bytes from the end - of buf->buf to the beginning first. If EOF is reached and the - file wasn't terminated by a newline, supply one. Return a count - of bytes buffered. */ - -static int -fillbuf (struct buffer *buf, FILE *fp) -{ - int cc; - - memmove (buf->buf, buf->buf + buf->used - buf->left, buf->left); - buf->used = buf->left; - - while (!feof (fp) && (buf->used == 0 || !memchr (buf->buf, '\n', buf->used))) - { - if (buf->used == buf->alloc) - { - buf->alloc *= 2; - buf->buf = xrealloc (buf->buf, buf->alloc); - } - cc = fread (buf->buf + buf->used, 1, buf->alloc - buf->used, fp); - if (ferror (fp)) - { - error (0, errno, _("read error")); - cleanup (); - exit (2); - } - buf->used += cc; - } - - if (feof (fp) && buf->used && buf->buf[buf->used - 1] != '\n') - { - if (buf->used == buf->alloc) - { - buf->alloc *= 2; - buf->buf = xrealloc (buf->buf, buf->alloc); - } - buf->buf[buf->used++] = '\n'; - } - - return buf->used; -} - -/* Initialize LINES, allocating space for ALLOC lines initially. - LIMIT is the maximum possible number of lines to allocate space - for, ever. */ - -static void -initlines (struct lines *lines, int alloc, int limit) -{ - lines->alloc = alloc; - lines->lines = (struct line *) xmalloc (lines->alloc * sizeof (struct line)); - lines->used = 0; - lines->limit = limit; -} - -/* Return a pointer to the first character of the field specified - by KEY in LINE. */ - -static char * -begfield (const struct line *line, const struct keyfield *key) -{ - register char *ptr = line->text, *lim = ptr + line->length; - register int sword = key->sword, schar = key->schar; - - if (tab) - while (ptr < lim && sword--) - { - while (ptr < lim && *ptr != tab) - ++ptr; - if (ptr < lim) - ++ptr; - } - else - while (ptr < lim && sword--) - { - while (ptr < lim && blanks[UCHAR (*ptr)]) - ++ptr; - while (ptr < lim && !blanks[UCHAR (*ptr)]) - ++ptr; - } - - if (key->skipsblanks) - while (ptr < lim && blanks[UCHAR (*ptr)]) - ++ptr; - - if (ptr + schar <= lim) - ptr += schar; - else - ptr = lim; - - return ptr; -} - -/* Return the limit of (a pointer to the first character after) the field - in LINE specified by KEY. */ - -static char * -limfield (const struct line *line, const struct keyfield *key) -{ - 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)) - ++ptr; - } - else - while (ptr < lim && eword--) - { - while (ptr < lim && blanks[UCHAR (*ptr)]) - ++ptr; - while (ptr < lim && !blanks[UCHAR (*ptr)]) - ++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) - 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; - - 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. */ - -static void -findlines (struct buffer *buf, struct lines *lines) -{ - register char *beg = buf->buf, *lim = buf->buf + buf->used, *ptr; - struct keyfield *key = keyhead.next; - - lines->used = 0; - - while (beg < lim && (ptr = memchr (beg, '\n', lim - beg)) - && lines->used < lines->limit) - { - /* There are various places in the code that rely on a NUL - being at the end of in-core lines; NULs inside the lines - will not cause trouble, though. */ - *ptr = '\0'; - - if (lines->used == lines->alloc) - { - lines->alloc *= 2; - lines->lines = (struct line *) - xrealloc ((char *) lines->lines, - lines->alloc * sizeof (struct line)); - } - - lines->lines[lines->used].text = beg; - lines->lines[lines->used].length = ptr - beg; - - /* Precompute the position of the first key for efficiency. */ - if (key) - { - if (key->eword >= 0) - lines->lines[lines->used].keylim = - limfield (&lines->lines[lines->used], key); - else - lines->lines[lines->used].keylim = ptr; - - if (key->sword >= 0) - lines->lines[lines->used].keybeg = - begfield (&lines->lines[lines->used], key); - else - { - if (key->skipsblanks) - while (blanks[UCHAR (*beg)]) - ++beg; - lines->lines[lines->used].keybeg = beg; - } - if (key->skipeblanks) - { - trim_trailing_blanks (lines->lines[lines->used].keybeg, - &lines->lines[lines->used].keylim); - } - } - else - { - lines->lines[lines->used].keybeg = 0; - lines->lines[lines->used].keylim = 0; - } - - ++lines->used; - beg = ptr + 1; - } - - buf->left = lim - beg; -} - -/* Compare strings A and B containing decimal fractions < 1. Each string - should begin with a decimal point followed immediately by the digits - of the fraction. Strings not of this form are considered to be zero. */ - -static int -fraccompare (register const char *a, register const char *b) -{ - register tmpa = UCHAR (*a), tmpb = UCHAR (*b); - - if (tmpa == '.' && tmpb == '.') - { - do - tmpa = UCHAR (*++a), tmpb = UCHAR (*++b); - while (tmpa == tmpb && digits[tmpa]); - if (digits[tmpa] && digits[tmpb]) - return tmpa - tmpb; - if (digits[tmpa]) - { - while (tmpa == '0') - tmpa = UCHAR (*++a); - if (digits[tmpa]) - return 1; - return 0; - } - if (digits[tmpb]) - { - while (tmpb == '0') - tmpb = UCHAR (*++b); - if (digits[tmpb]) - return -1; - return 0; - } - return 0; - } - else if (tmpa == '.') - { - do - tmpa = UCHAR (*++a); - while (tmpa == '0'); - if (digits[tmpa]) - return 1; - return 0; - } - else if (tmpb == '.') - { - do - tmpb = UCHAR (*++b); - while (tmpb == '0'); - if (digits[tmpb]) - return -1; - return 0; - } - return 0; -} - -/* Compare strings A and B as numbers without explicitly converting them to - machine numbers. Comparatively slow for short strings, but asymptotically - hideously fast. */ - -static int -numcompare (register const char *a, register const char *b) -{ - register int tmpa, tmpb, loga, logb, tmp; - - tmpa = UCHAR (*a); - tmpb = UCHAR (*b); - - while (blanks[tmpa]) - tmpa = UCHAR (*++a); - while (blanks[tmpb]) - tmpb = UCHAR (*++b); - - if (tmpa == '-') - { - do - tmpa = UCHAR (*++a); - while (tmpa == '0'); - 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 = UCHAR (*++b); - while (tmpb == '0'); - if (digits[tmpb]) - return -1; - return 0; - } - do - tmpb = UCHAR (*++b); - while (tmpb == '0'); - - while (tmpa == tmpb && digits[tmpa]) - tmpa = UCHAR (*++a), tmpb = UCHAR (*++b); - - if ((tmpa == '.' && !digits[tmpb]) || (tmpb == '.' && !digits[tmpa])) - return -fraccompare (a, b); - - if (digits[tmpa]) - for (loga = 1; digits[UCHAR (*++a)]; ++loga) - ; - else - loga = 0; - - if (digits[tmpb]) - for (logb = 1; digits[UCHAR (*++b)]; ++logb) - ; - else - logb = 0; - - if ((tmp = logb - loga) != 0) - return tmp; - - if (!loga) - return 0; - -#ifdef __FreeBSD__ - return COLLDIFF (tmpb, tmpa); -#else - return tmpb - tmpa; -#endif - } - else if (tmpb == '-') - { - do - tmpb = UCHAR (*++b); - while (tmpb == '0'); - if (tmpb == '.') - do - tmpb = UCHAR (*++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]) - return 1; - return 0; - } - else - { - while (tmpa == '0') - tmpa = UCHAR (*++a); - while (tmpb == '0') - tmpb = UCHAR (*++b); - - while (tmpa == tmpb && digits[tmpa]) - tmpa = UCHAR (*++a), tmpb = UCHAR (*++b); - - if ((tmpa == '.' && !digits[tmpb]) || (tmpb == '.' && !digits[tmpa])) - return fraccompare (a, b); - - if (digits[tmpa]) - for (loga = 1; digits[UCHAR (*++a)]; ++loga) - ; - else - loga = 0; - - if (digits[tmpb]) - for (logb = 1; digits[UCHAR (*++b)]; ++logb) - ; - else - logb = 0; - - if ((tmp = loga - logb) != 0) - return tmp; - - if (!loga) - return 0; - -#ifdef __FreeBSD__ - return COLLDIFF (tmpa, tmpb); -#else - return tmpa - tmpb; -#endif - } -} - -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) -{ - char month[4]; - register int i, lo = 0, hi = 12; - - while (len > 0 && blanks[UCHAR(*s)]) - ++s, --len; - - if (len < 3) - return 0; - - for (i = 0; i < 3; ++i) - month[i] = fold_toupper[UCHAR (s[i])]; - month[3] = '\0'; - - while (hi - lo > 1) - if (strcmp (month, monthtab[(lo + hi) / 2].name) < 0) - hi = (lo + hi) / 2; - else - lo = (lo + hi) / 2; - if (!strcmp (month, monthtab[lo].name)) - return monthtab[lo].val; - return 0; -} - -/* Compare two lines A and B trying every key in sequence until there - are no more keys or a difference is found. */ - -static int -keycompare (const struct line *a, const struct line *b) -{ - register char *texta, *textb, *lima, *limb, *translate; - register int *ignore; - struct keyfield *key; - int diff = 0, iter = 0, lena, lenb; - - for (key = keyhead.next; key; key = key->next, ++iter) - { - ignore = key->ignore; - translate = key->translate; - - /* Find the beginning and limit of each field. */ - if (iter || a->keybeg == NULL || b->keybeg == NULL) - { - if (key->eword >= 0) - lima = limfield (a, key), limb = limfield (b, key); - else - lima = a->text + a->length, limb = b->text + b->length; - - if (key->sword >= 0) - texta = begfield (a, key), textb = begfield (b, key); - else - { - texta = a->text, textb = b->text; - if (key->skipsblanks) - { - while (texta < lima && blanks[UCHAR (*texta)]) - ++texta; - while (textb < limb && blanks[UCHAR (*textb)]) - ++textb; - } - } - } - else - { - /* For the first iteration only, the key positions have - been precomputed for us. */ - texta = a->keybeg, lima = a->keylim; - textb = b->keybeg, limb = b->keylim; - } - - /* Find the lengths. */ - lena = lima - texta, lenb = limb - textb; - if (lena < 0) - lena = 0; - 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) - { - if (*lima || *limb) - { - char savea = *lima, saveb = *limb; - - *lima = *limb = '\0'; - diff = numcompare (texta, textb); - *lima = savea, *limb = saveb; - } - else - diff = numcompare (texta, textb); - - if (diff) - 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); - if (diff) - return key->reverse ? -diff : diff; - continue; - } - else if (ignore && translate) - -#ifdef __FreeBSD__ -#define CMP_FUNC(A, B) COLLDIFF ((A), (B)) -#else -#define CMP_FUNC(A, B) (A) - (B) -#endif -#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 = CMP_FUNC((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)]); - else if (ignore) - CMP_WITH_IGNORE (*texta, *textb); - else if (translate) - while (texta < lima && textb < limb) - { - if (translate[UCHAR (*texta++)] != translate[UCHAR (*textb++)]) - { -#ifdef __FreeBSD__ - diff = COLLDIFF (translate[UCHAR (*--texta)], - translate[UCHAR (*--textb)]); -#else - diff = (translate[UCHAR (*--texta)] - - translate[UCHAR (*--textb)]); -#endif - break; - } - } - else -#ifdef __FreeBSD__ - diff = collcmp (texta, textb, min (lena, lenb)); -#else - diff = memcmp (texta, textb, min (lena, lenb)); -#endif - - if (diff) - return key->reverse ? -diff : diff; - if ((diff = lena - lenb) != 0) - return key->reverse ? -diff : diff; - } - - return 0; -} - -/* Compare two lines A and B, returning negative, zero, or positive - 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) -{ - int diff, tmpa, tmpb, mini; - - /* First try to compare on the specified keys (if any). - The only two cases with no key at all are unadorned sort, - and unadorned sort -r. */ - if (keyhead.next) - { - diff = keycompare (a, b); - if (diff != 0) - return diff; - if (unique || stable) - return 0; - } - - /* If the keys all compare equal (or no keys were specified) - fall through to the default byte-by-byte comparison. */ - tmpa = a->length, tmpb = b->length; - mini = min (tmpa, tmpb); - if (mini == 0) - diff = tmpa - tmpb; - else - { - char *ap = a->text, *bp = b->text; - -#ifndef __FreeBSD__ - diff = UCHAR (*ap) - UCHAR (*bp); - if (diff == 0) - { -#endif -#ifdef __FreeBSD__ - diff = collcmp (ap, bp, mini); -#else - diff = memcmp (ap, bp, mini); -#endif - if (diff == 0) - diff = tmpa - tmpb; -#ifndef __FreeBSD__ - } -#endif - } - - return reverse ? -diff : diff; -} - -/* 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. */ - -static int -checkfp (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; - - initbuf (&buf, mergealloc); - initlines (&lines, mergealloc / linelength + 1, - LINEALLOC / ((NMERGE + NMERGE) * sizeof (struct line))); - alloc = linelength; - 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; - - /* 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. */ - 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; - } - } - -finish: - xfclose (fp); - free (buf.buf); - free ((char *) lines.lines); - free (temp.text); - return sorted; -} - -/* 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) -{ - struct buffer buffer[NMERGE]; /* Input buffers for each file. */ - struct lines lines[NMERGE]; /* Line tables for each buffer. */ - struct line saved; /* Saved line for unique check. */ - int savedflag = 0; /* True if there is a saved line. */ - int savealloc; /* Size allocated for the saved line. */ - int cur[NMERGE]; /* Current line in each line table. */ - int ord[NMERGE]; /* Table representing a permutation of fps, - such that lines[ord[0]].lines[cur[ord[0]]] - is the smallest line and will be next - 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) - { - savealloc = linelength; - saved.text = xmalloc (savealloc); - } - - /* Read initial lines from each input file. */ - for (i = 0; i < nfps; ++i) - { - initbuf (&buffer[i], mergealloc); - /* If a file is empty, eliminate it from future consideration. */ - while (i < nfps && !fillbuf (&buffer[i], fps[i])) - { - xfclose (fps[i]); - --nfps; - for (j = i; j < nfps; ++j) - fps[j] = fps[j + 1]; - } - if (i == nfps) - free (buffer[i].buf); - else - { - initlines (&lines[i], mergealloc / linelength + 1, - LINEALLOC / ((NMERGE + NMERGE) * sizeof (struct line))); - findlines (&buffer[i], &lines[i]); - cur[i] = 0; - } - } - - /* Set up the ord table according to comparisons among input lines. - Since this only reorders two items if one is strictly greater than - the other, it is stable. */ - for (i = 0; i < nfps; ++i) - ord[i] = i; - for (i = 1; i < nfps; ++i) - if (compare (&lines[ord[i - 1]].lines[cur[ord[i - 1]]], - &lines[ord[i]].lines[cur[ord[i]]]) > 0) - t = ord[i - 1], ord[i - 1] = ord[i], ord[i] = t, i = 0; - - /* Repeatedly output the smallest line until no input remains. */ - while (nfps) - { - /* If uniqified output is turned on, output only the first of - an identical series of lines. */ - if (unique) - { - if (savedflag && compare (&saved, &lines[ord[0]].lines[cur[ord[0]]])) - { - xfwrite (saved.text, 1, saved.length, ofp); - putc ('\n', ofp); - savedflag = 0; - } - if (!savedflag) - { - if (savealloc < lines[ord[0]].lines[cur[ord[0]]].length + 1) - { - while (savealloc < lines[ord[0]].lines[cur[ord[0]]].length + 1) - savealloc *= 2; - 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, - saved.length + 1); - if (lines[ord[0]].lines[cur[ord[0]]].keybeg != NULL) - { - saved.keybeg = saved.text + - (lines[ord[0]].lines[cur[ord[0]]].keybeg - - lines[ord[0]].lines[cur[ord[0]]].text); - } - if (lines[ord[0]].lines[cur[ord[0]]].keylim != NULL) - { - saved.keylim = saved.text + - (lines[ord[0]].lines[cur[ord[0]]].keylim - - lines[ord[0]].lines[cur[ord[0]]].text); - } - savedflag = 1; - } - } - else - { - xfwrite (lines[ord[0]].lines[cur[ord[0]]].text, 1, - lines[ord[0]].lines[cur[ord[0]]].length, ofp); - putc ('\n', ofp); - } - - /* Check if we need to read more lines into core. */ - if (++cur[ord[0]] == lines[ord[0]].used) - if (fillbuf (&buffer[ord[0]], fps[ord[0]])) - { - findlines (&buffer[ord[0]], &lines[ord[0]]); - cur[ord[0]] = 0; - } - else - { - /* We reached EOF on fps[ord[0]]. */ - for (i = 1; i < nfps; ++i) - if (ord[i] > ord[0]) - --ord[i]; - --nfps; - xfclose (fps[ord[0]]); - free (buffer[ord[0]].buf); - free ((char *) lines[ord[0]].lines); - for (i = ord[0]; i < nfps; ++i) - { - fps[i] = fps[i + 1]; - buffer[i] = buffer[i + 1]; - lines[i] = lines[i + 1]; - cur[i] = cur[i + 1]; - } - for (i = 0; i < nfps; ++i) - ord[i] = ord[i + 1]; - continue; - } - - /* The new line just read in may be larger than other lines - already in core; push it back in the queue until we encounter - a line larger than it. */ - for (i = 1; i < nfps; ++i) - { - t = compare (&lines[ord[0]].lines[cur[ord[0]]], - &lines[ord[i]].lines[cur[ord[i]]]); - if (!t) - t = ord[0] - ord[i]; - if (t < 0) - break; - } - t = ord[0]; - for (j = 1; j < i; ++j) - ord[j - 1] = ord[j]; - ord[i - 1] = t; - } - - if (unique && savedflag) - { - xfwrite (saved.text, 1, saved.length, ofp); - putc ('\n', ofp); - free (saved.text); - } -} - -/* Sort the array LINES with NLINES members, using TEMP for temporary space. */ - -static void -sortlines (struct line *lines, int nlines, struct line *temp) -{ - register struct line *lo, *hi, *t; - register int nlo, nhi; - - if (nlines == 2) - { - if (compare (&lines[0], &lines[1]) > 0) - *temp = lines[0], lines[0] = lines[1], lines[1] = *temp; - return; - } - - nlo = nlines / 2; - lo = lines; - nhi = nlines - nlo; - hi = lines + nlo; - - if (nlo > 1) - sortlines (lo, nlo, temp); - - if (nhi > 1) - sortlines (hi, nhi, temp); - - t = temp; - - while (nlo && nhi) - if (compare (lo, hi) <= 0) - *t++ = *lo++, --nlo; - else - *t++ = *hi++, --nhi; - while (nlo--) - *t++ = *lo++; - - for (lo = lines, nlo = nlines - nhi, t = temp; nlo; --nlo) - *lo++ = *t++; -} - -/* Check that each of the NFILES FILES is ordered. - Return a count of disordered files. */ - -static int -check (char **files, int nfiles) -{ - int i, disorders = 0; - FILE *fp; - - for (i = 0; i < nfiles; ++i) - { - fp = xfopen (files[i], "r"); - if (!checkfp (fp)) - { - fprintf (stderr, _("%s: disorder on %s\n"), program_name, files[i]); - ++disorders; - } - } - return disorders; -} - -/* Merge NFILES FILES onto OFP. */ - -static void -merge (char **files, int nfiles, FILE *ofp) -{ - int i, j, t; - char *temp; - FILE *fps[NMERGE], *tfp; - - while (nfiles > NMERGE) - { - t = 0; - for (i = 0; i < nfiles / NMERGE; ++i) - { - for (j = 0; j < NMERGE; ++j) - fps[j] = xfopen (files[i * NMERGE + j], "r"); - tfp = xtmpfopen (temp = tempname ()); - mergefps (fps, NMERGE, tfp); - xfclose (tfp); - for (j = 0; j < NMERGE; ++j) - zaptemp (files[i * NMERGE + j]); - files[t++] = temp; - } - for (j = 0; j < nfiles % NMERGE; ++j) - fps[j] = xfopen (files[i * NMERGE + j], "r"); - tfp = xtmpfopen (temp = tempname ()); - mergefps (fps, nfiles % NMERGE, tfp); - xfclose (tfp); - for (j = 0; j < nfiles % NMERGE; ++j) - zaptemp (files[i * NMERGE + j]); - files[t++] = temp; - nfiles = t; - } - - for (i = 0; i < nfiles; ++i) - fps[i] = xfopen (files[i], "r"); - mergefps (fps, i, ofp); - for (i = 0; i < nfiles; ++i) - zaptemp (files[i]); -} - -/* Sort NFILES FILES onto OFP. */ - -static void -sort (char **files, int nfiles, FILE *ofp) -{ - struct buffer buf; - struct lines lines; - struct line *tmp; - int i, ntmp; - FILE *fp, *tfp; - struct tempnode *node; - int n_temp_files = 0; - char **tempfiles; - - initbuf (&buf, sortalloc); - initlines (&lines, sortalloc / linelength + 1, - LINEALLOC / sizeof (struct line)); - ntmp = lines.alloc; - tmp = (struct line *) xmalloc (ntmp * sizeof (struct line)); - - while (nfiles--) - { - fp = xfopen (*files++, "r"); - while (fillbuf (&buf, fp)) - { - findlines (&buf, &lines); - if (lines.used > ntmp) - { - while (lines.used > ntmp) - ntmp *= 2; - tmp = (struct line *) - xrealloc ((char *) tmp, ntmp * sizeof (struct line)); - } - sortlines (lines.lines, lines.used, tmp); - if (feof (fp) && !nfiles && !n_temp_files && !buf.left) - tfp = ofp; - else - { - ++n_temp_files; - tfp = xtmpfopen (tempname ()); - } - for (i = 0; i < lines.used; ++i) - if (!unique || i == 0 - || compare (&lines.lines[i], &lines.lines[i - 1])) - { - xfwrite (lines.lines[i].text, 1, lines.lines[i].length, tfp); - putc ('\n', tfp); - } - if (tfp != ofp) - xfclose (tfp); - } - xfclose (fp); - } - - free (buf.buf); - free ((char *) lines.lines); - free ((char *) tmp); - - if (n_temp_files) - { - tempfiles = (char **) xmalloc (n_temp_files * sizeof (char *)); - i = n_temp_files; - for (node = temphead.next; i > 0; node = node->next) - tempfiles[--i] = node->name; - merge (tempfiles, n_temp_files, ofp); - free ((char *) tempfiles); - } -} - -/* Insert key KEY at the end of the list (`keyhead'). */ - -static void -insertkey (struct keyfield *key) -{ - struct keyfield *k = &keyhead; - - while (k->next) - k = k->next; - k->next = key; - key->next = NULL; -} - -static void -badfieldspec (const char *s) -{ - error (2, 0, _("invalid field specification `%s'"), s); -} - -/* Handle interrupts and hangups. */ - -static void -sighandler (int sig) -{ -#ifdef SA_INTERRUPT - struct sigaction sigact; - - sigact.sa_handler = SIG_DFL; - sigemptyset (&sigact.sa_mask); - sigact.sa_flags = 0; - sigaction (sig, &sigact, NULL); -#else /* !SA_INTERRUPT */ - signal (sig, SIG_DFL); -#endif /* SA_INTERRUPT */ - cleanup (); - kill (getpid (), sig); -} - -/* Set the ordering options for KEY specified in S. - Return the address of the first character in S that - is not a valid ordering option. - BLANKTYPE is the kind of blanks that 'b' should skip. */ - -static char * -set_ordering (register const char *s, struct keyfield *key, - enum blanktype blanktype) -{ - while (*s) - { - switch (*s) - { - case 'b': - if (blanktype == bl_start || blanktype == bl_both) - key->skipsblanks = 1; - if (blanktype == bl_end || blanktype == bl_both) - key->skipeblanks = 1; - break; - case 'd': - key->ignore = nondictionary; - break; - case 'f': - key->translate = fold_toupper; - break; - case 'g': - key->general_numeric = 1; - break; - case 'i': - key->ignore = nonprinting; - break; - case 'M': - key->month = 1; - 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; - } - ++s; - } - return (char *) s; -} - -int -main (int argc, char **argv) -{ - struct keyfield *key = NULL, gkey; - char *s; - int i, t, t2; - int checkonly = 0, mergeonly = 0, nfiles = 0; - char *minus = "-", *outfile = minus, **files, *tmp; - FILE *ofp; -#ifdef SA_INTERRUPT - struct sigaction oldact, newact; -#endif /* SA_INTERRUPT */ - -#ifdef __FreeBSD__ - (void) setlocale(LC_ALL, ""); -#endif - program_name = argv[0]; - - parse_long_options (argc, argv, "sort", version_string, usage); - - have_read_stdin = 0; - inittables (); - - temp_file_prefix = getenv ("TMPDIR"); - if (temp_file_prefix == NULL) - temp_file_prefix = DEFAULT_TMPDIR; - -#ifdef SA_INTERRUPT - newact.sa_handler = sighandler; - sigemptyset (&newact.sa_mask); - newact.sa_flags = 0; - - sigaction (SIGINT, NULL, &oldact); - if (oldact.sa_handler != SIG_IGN) - sigaction (SIGINT, &newact, NULL); - sigaction (SIGHUP, NULL, &oldact); - if (oldact.sa_handler != SIG_IGN) - sigaction (SIGHUP, &newact, NULL); - sigaction (SIGPIPE, NULL, &oldact); - if (oldact.sa_handler != SIG_IGN) - sigaction (SIGPIPE, &newact, NULL); - sigaction (SIGTERM, NULL, &oldact); - if (oldact.sa_handler != SIG_IGN) - sigaction (SIGTERM, &newact, NULL); -#else /* !SA_INTERRUPT */ - if (signal (SIGINT, SIG_IGN) != SIG_IGN) - signal (SIGINT, sighandler); - if (signal (SIGHUP, SIG_IGN) != SIG_IGN) - signal (SIGHUP, sighandler); - if (signal (SIGPIPE, SIG_IGN) != SIG_IGN) - signal (SIGPIPE, sighandler); - if (signal (SIGTERM, SIG_IGN) != SIG_IGN) - signal (SIGTERM, sighandler); -#endif /* !SA_INTERRUPT */ - - gkey.sword = gkey.eword = -1; - gkey.ignore = NULL; - gkey.translate = NULL; - gkey.numeric = gkey.general_numeric = gkey.month = gkey.reverse = 0; - gkey.skipsblanks = gkey.skipeblanks = 0; - - files = (char **) xmalloc (sizeof (char *) * argc); - - for (i = 1; i < argc; ++i) - { - if (argv[i][0] == '+') - { - if (key) - insertkey (key); - key = (struct keyfield *) xmalloc (sizeof (struct keyfield)); - key->eword = -1; - key->ignore = NULL; - key->translate = NULL; - key->skipsblanks = key->skipeblanks = 0; - key->numeric = key->general_numeric = key->month = key->reverse = 0; - s = argv[i] + 1; - if (! (digits[UCHAR (*s)] || (*s == '.' && digits[UCHAR (s[1])]))) - badfieldspec (argv[i]); - for (t = 0; digits[UCHAR (*s)]; ++s) - t = 10 * t + *s - '0'; - t2 = 0; - if (*s == '.') - for (++s; digits[UCHAR (*s)]; ++s) - t2 = 10 * t2 + *s - '0'; - if (t2 || t) - { - key->sword = t; - key->schar = t2; - } - else - key->sword = -1; - s = set_ordering (s, key, bl_start); - if (*s) - badfieldspec (argv[i]); - } - else if (argv[i][0] == '-' && argv[i][1]) - { - s = argv[i] + 1; - if (digits[UCHAR (*s)] || (*s == '.' && digits[UCHAR (s[1])])) - { - if (!key) - usage (2); - for (t = 0; digits[UCHAR (*s)]; ++s) - t = t * 10 + *s - '0'; - t2 = 0; - if (*s == '.') - for (++s; digits[UCHAR (*s)]; ++s) - t2 = t2 * 10 + *s - '0'; - key->eword = t; - key->echar = t2; - s = set_ordering (s, key, bl_end); - if (*s) - badfieldspec (argv[i]); - insertkey (key); - key = NULL; - } - else - while (*s) - { - s = set_ordering (s, &gkey, bl_both); - switch (*s) - { - case '\0': - break; - case 'c': - checkonly = 1; - break; - case 'k': - if (s[1]) - ++s; - else - { - if (i == argc - 1) - error (2, 0, _("option `-k' requires an argument")); - else - s = argv[++i]; - } - if (key) - insertkey (key); - key = (struct keyfield *) - xmalloc (sizeof (struct keyfield)); - memset (key, 0, sizeof (struct keyfield)); - key->eword = -1; - key->ignore = NULL; - key->translate = NULL; - key->skipsblanks = key->skipeblanks = 0; - key->numeric = key->month = key->reverse = 0; - /* Get POS1. */ - if (!digits[UCHAR (*s)]) - 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; - 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 || t) - { - key->sword = t; - key->schar = t2; - } - else - key->sword = -1; - s = set_ordering (s, key, bl_start); - if (*s == 0) - { - key->eword = -1; - key->echar = 0; - } - else if (*s != ',') - badfieldspec (argv[i]); - 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; - } - key->eword = t; - key->echar = t2; - s = set_ordering (s, key, bl_end); - if (*s) - badfieldspec (argv[i]); - } - insertkey (key); - key = NULL; - goto outer; - case 'm': - mergeonly = 1; - break; - case 'o': - if (s[1]) - outfile = s + 1; - else - { - if (i == argc - 1) - error (2, 0, _("option `-o' requires an argument")); - else - outfile = argv[++i]; - } - goto outer; - case 's': - stable = 1; - break; - case 't': - if (s[1]) - tab = *++s; - else if (i < argc - 1) - { - tab = *argv[++i]; - goto outer; - } - else - error (2, 0, _("option `-t' requires an argument")); - break; - case 'T': - if (s[1]) - temp_file_prefix = ++s; - else - { - if (i < argc - 1) - temp_file_prefix = argv[++i]; - else - error (2, 0, _("option `-T' requires an argument")); - } - goto outer; - /* break; */ - case 'u': - unique = 1; - break; - case 'y': - /* Accept and ignore e.g. -y0 for compatibility with - Solaris 2. */ - goto outer; - default: - fprintf (stderr, _("%s: unrecognized option `-%c'\n"), - argv[0], *s); - usage (2); - } - if (*s) - ++s; - } - } - else /* Not an option. */ - { - files[nfiles++] = argv[i]; - } - outer:; - } - - if (key) - insertkey (key); - - /* 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->ignore = gkey.ignore; - key->translate = gkey.translate; - key->skipsblanks = gkey.skipsblanks; - 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)) - insertkey (&gkey); - reverse = gkey.reverse; - - if (nfiles == 0) - { - nfiles = 1; - files = − - } - - if (checkonly) - exit (check (files, nfiles) != 0); - - if (strcmp (outfile, "-")) - { - struct stat outstat; - if (stat (outfile, &outstat) == 0) - { - /* 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; - 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; - } - } - ofp = xfopen (outfile, "w"); - } - else - ofp = stdout; - - if (mergeonly) - merge (files, nfiles, ofp); - else - sort (files, nfiles, ofp); - cleanup (); - - /* If we wait for the implicit flush on exit, and the parent process - has closed stdout (e.g., exec >&- in a shell), then the output file - winds up empty. I don't understand why. This is under SunOS, - 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); - - if (have_read_stdin && fclose (stdin) == EOF) - error (1, errno, outfile); - if (ferror (stdout) || fclose (stdout) == EOF) - error (1, errno, _("%s: write error"), outfile); - - exit (0); -} |