From 08a3faaa5c971e3de45dd8828ab58c0e612549f2 Mon Sep 17 00:00:00 2001 From: Theo de Raadt Date: Mon, 16 Jun 1997 22:43:54 +0000 Subject: replaced by a BSD sort --- gnu/usr.bin/sort/COPYING | 249 ------ gnu/usr.bin/sort/Makefile | 6 - gnu/usr.bin/sort/sort.1 | 186 ----- gnu/usr.bin/sort/sort.c | 1862 --------------------------------------------- 4 files changed, 2303 deletions(-) delete mode 100644 gnu/usr.bin/sort/COPYING delete mode 100644 gnu/usr.bin/sort/Makefile delete mode 100644 gnu/usr.bin/sort/sort.1 delete mode 100644 gnu/usr.bin/sort/sort.c diff --git a/gnu/usr.bin/sort/COPYING b/gnu/usr.bin/sort/COPYING deleted file mode 100644 index 9a170375811..00000000000 --- a/gnu/usr.bin/sort/COPYING +++ /dev/null @@ -1,249 +0,0 @@ - - GNU GENERAL PUBLIC LICENSE - Version 1, February 1989 - - Copyright (C) 1989 Free Software Foundation, Inc. - 675 Mass Ave, Cambridge, MA 02139, USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The license agreements of most software companies try to keep users -at the mercy of those companies. By contrast, our General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. The -General Public License applies to the Free Software Foundation's -software and to any other program whose authors commit to using it. -You can use it for your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Specifically, the General Public License is designed to make -sure that you have the freedom to give away or sell copies of free -software, that you receive source code or can get it if you want it, -that you can change the software or use pieces of it in new free -programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of a such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must tell them their rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any program or other work which -contains a notice placed by the copyright holder saying it may be -distributed under the terms of this General Public License. The -"Program", below, refers to any such program or work, and a "work based -on the Program" means either the Program or any work containing the -Program or a portion of it, either verbatim or with modifications. Each -licensee is addressed as "you". - - 1. You may copy and distribute verbatim copies of the Program's source -code as you receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice and -disclaimer of warranty; keep intact all the notices that refer to this -General Public License and to the absence of any warranty; and give any -other recipients of the Program a copy of this General Public License -along with the Program. You may charge a fee for the physical act of -transferring a copy. - - 2. You may modify your copy or copies of the Program or any portion of -it, and copy and distribute such modifications under the terms of Paragraph -1 above, provided that you also do the following: - - a) cause the modified files to carry prominent notices stating that - you changed the files and the date of any change; and - - b) cause the whole of any work that you distribute or publish, that - in whole or in part contains the Program or any part thereof, either - with or without modifications, to be licensed at no charge to all - third parties under the terms of this General Public License (except - that you may choose to grant warranty protection to some or all - third parties, at your option). - - c) If the modified program normally reads commands interactively when - run, you must cause it, when started running for such interactive use - in the simplest and most usual way, to print or display an - announcement including an appropriate copyright notice and a notice - that there is no warranty (or else, saying that you provide a - warranty) and that users may redistribute the program under these - conditions, and telling the user how to view a copy of this General - Public License. - - d) You may charge a fee for the physical act of transferring a - copy, and you may at your option offer warranty protection in - exchange for a fee. - -Mere aggregation of another independent work with the Program (or its -derivative) on a volume of a storage or distribution medium does not bring -the other work under the scope of these terms. - - 3. You may copy and distribute the Program (or a portion or derivative of -it, under Paragraph 2) in object code or executable form under the terms of -Paragraphs 1 and 2 above provided that you also do one of the following: - - a) accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of - Paragraphs 1 and 2 above; or, - - b) accompany it with a written offer, valid for at least three - years, to give any third party free (except for a nominal charge - for the cost of distribution) a complete machine-readable copy of the - corresponding source code, to be distributed under the terms of - Paragraphs 1 and 2 above; or, - - c) accompany it with the information you received as to where the - corresponding source code may be obtained. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form alone.) - -Source code for a work means the preferred form of the work for making -modifications to it. For an executable file, complete source code means -all the source code for all modules it contains; but, as a special -exception, it need not include source code for modules which are standard -libraries that accompany the operating system on which the executable -file runs, or for standard header files or definitions files that -accompany that operating system. - - 4. You may not copy, modify, sublicense, distribute or transfer the -Program except as expressly provided under this General Public License. -Any attempt otherwise to copy, modify, sublicense, distribute or transfer -the Program is void, and will automatically terminate your rights to use -the Program under this License. However, parties who have received -copies, or rights to use copies, from you under this General Public -License will not have their licenses terminated so long as such parties -remain in full compliance. - - 5. By copying, distributing or modifying the Program (or any work based -on the Program) you indicate your acceptance of this license to do so, -and all its terms and conditions. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the original -licensor to copy, distribute or modify the Program subject to these -terms and conditions. You may not impose any further restrictions on the -recipients' exercise of the rights granted herein. - - 7. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of the license which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -the license, you may choose any version ever published by the Free Software -Foundation. - - 8. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - Appendix: How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to humanity, the best way to achieve this is to make it -free software which everyone can redistribute and change under these -terms. - - To do so, attach the following notices to the program. It is safest to -attach them to the start of each source file to most effectively convey -the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) 19yy - - 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) 19xx name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the -appropriate parts of the General Public License. Of course, the -commands you use may be called something other than `show w' and `show -c'; they could even be mouse-clicks or menu items--whatever suits your -program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - program `Gnomovision' (a program to direct compilers to make passes - at assemblers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/gnu/usr.bin/sort/Makefile b/gnu/usr.bin/sort/Makefile deleted file mode 100644 index 2697b77ba1c..00000000000 --- a/gnu/usr.bin/sort/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# $OpenBSD: Makefile,v 1.2 1996/08/20 05:14:11 tholo Exp $ -# $NetBSD: Makefile,v 1.4 1995/04/23 07:58:53 cgd Exp $ - -PROG= sort - -.include diff --git a/gnu/usr.bin/sort/sort.1 b/gnu/usr.bin/sort/sort.1 deleted file mode 100644 index f7c4726b57b..00000000000 --- a/gnu/usr.bin/sort/sort.1 +++ /dev/null @@ -1,186 +0,0 @@ -.\" $Id: sort.1,v 1.1 1995/10/18 08:41:07 deraadt Exp $ -*- nroff -*- -.TH SORT 1 -.SH NAME -sort \- sort lines of text files -.SH SYNOPSIS -.B sort -[\-cmus] [\-t separator] [\-o output-file] [\-bdfiMnr] [+POS1 [\-POS2]] -[\-k POS1[,POS2]] [file...] -.SH DESCRIPTION -This manual page -documents the GNU version of -.BR sort . -.B sort -sorts, merges, or compares all the lines from the given files, or the standard -input if no files are given. A file name of `-' means standard input. -By default, -.B sort -writes the results to the standard output. -.PP -.B sort -has three modes of operation: sort (the default), merge, and check for -sortedness. The following options change the operation mode: -.TP -.I \-c -Check whether the given files are already sorted: if they are not all -sorted, print an error message and exit with a status of 1. -.TP -.I \-m -Merge the given files by sorting them as a group. Each input file -should already be individually sorted. It always works to sort -instead of merge; merging is provided because it is faster, in the -case where it works. -.PP -A pair of lines is compared as follows: -if any key fields have been specified, -.B sort -compares each pair of fields, in the order specified on the command -line, according to the associated ordering options, until a difference -is found or no fields are left. -.PP -If any of the global options -.I Mbdfinr -are given but no key fields are -specified, -.B sort -compares the entire lines according to the global options. -.PP -Finally, as a last resort when all keys compare equal -(or if no ordering options were specified at all), -.B sort -compares the lines byte by byte in machine collating sequence. The -.I \-s -option disables this last resort comparison, producing a stable sort. -.PP -GNU -.B sort -has no limits on input line length or restrictions on bytes allowed -within lines. In addition, if the final byte of an input file is not -a newline, GNU -.B sort -silently supplies one. In some cases, such as exactly what the -.I \-b -and -.I \-f -options do, BSD and System V -.B sort -programs produce different output; GNU -.B sort -follows the POSIX behavior, which is usually like the System V behavior. -.PP -If the environment variable TMPDIR is set, -.B sort -uses it as the directory in which to put temporary files instead of -the default, /tmp. -.PP -The following options affect the ordering of output lines. They may -be specified globally or as part of a specific key field. If no key -fields are specified, global options apply to comparison of entire -lines; otherwise the global options are inherited by key fields that -do not specify any special options of their own. -.TP -.I \-b -Ignore leading blanks when finding sort keys in each line. -.TP -.I \-d -Sort in `dictionary order': ignore all characters except letters, -digits and blanks when sorting. -.TP -.I \-f -Fold lower case characters into the equivalent upper case characters -when sorting so that, for example, `b' is sorted the same way `B' is. -.TP -.I \-i -Ignore characters outside the ASCII range 040-0176 (inclusive) when sorting. -.TP -.I \-M -An initial string, consisting of any amount of white space, followed -by three letters abbreviating a month name, is folded to lower case -and compared in the order `jan' < `feb' < ... < `dec.' Invalid names -compare low to valid names. This option implies -.IR \-b . -.TP -.I \-n -Compare according to arithmetic value an initial numeric string -consisting of optional white space, an optional \- sign, and zero or -more digits, optionally followed by a decimal point and zero or more -digits. This option implies -.IR \-b . -.TP -.I \-r -Reverse the result of comparison, so that lines with greater key -values appear earlier in the output instead of later. -.PP -Other options are: -.TP -.I "\-o output-file" -Write output to -.I output-file -instead of to the standard output. If -.I output-file -is one of the input files, -.B sort -copies it to a temporary file before sorting and writing the output to -.IR output-file . -.TP -.I "\-t separator" -Use character -.I separator -as the field separator when finding the sort keys in each line. By -default, fields are separated by the empty string between a -non-whitespace character and a whitespace character. That is to say, -given the input line ` foo bar', -.B sort -breaks it into fields ` foo' and ` bar'. The field separator is not -considered to be part of either the field preceding or the field -following it. -.TP -.I \-u -For the default case or the -.I \-m -option, only output the first of a sequence of lines that compare -equal. For the -.I \-c -option, check that no pair of consecutive lines compares equal. -.TP -.I "+POS1 [\-POS2]" -Specify a field within each line to use as a sorting key. The field -consists of the portion of the line starting at POS1 and up to (but -not including) POS2 (or to the end of the line if POS2 is not given). -The fields and character positions are numbered starting with 0. -.TP -.I "\-k POS1[,POS2]" -An alternate syntax for specifying sorting keys. -The fields and character positions are numbered starting with 1. -.PP -A position has the form \fIf\fP.\fIc\fP, where \fIf\fP is the number -of the field to use and \fIc\fP is the number of the first character -from the beginning of the field (for \fI+pos\fP) or from the end of -the previous field (for \fI\-pos\fP). The .\fIc\fP part of a position -may be omitted in which case it is taken to be the first character in -the field. If the -.I \-b -option has been given, the .\fIc\fP part of a field specification is -counted from the first nonblank character of the field (for -\fI+pos\fP) or from the first nonblank character following the -previous field (for \fI\-pos\fP). -.PP -A \fI+pos\fP or \fI-pos\fP argument may also have any of the option -letters -.I Mbdfinr -appended to it, in which case the global ordering options are not used -for that particular field. The -.I \-b -option may be independently attached to either or both of the -\fI+pos\fP and \fI\-pos\fP parts of a field specification, and if it -is inherited from the global options it will be attached to both. -If a -.I \-n -or -.I \-M -option is used, thus implying a -.I \-b -option, the -.I \-b -option is taken to apply to both the \fI+pos\fP and the \fI\-pos\fP -parts of a key specification. Keys may span multiple fields. diff --git a/gnu/usr.bin/sort/sort.c b/gnu/usr.bin/sort/sort.c deleted file mode 100644 index a5ab7a28234..00000000000 --- a/gnu/usr.bin/sort/sort.c +++ /dev/null @@ -1,1862 +0,0 @@ -/* sort - sort lines of text (with all kinds of options). - 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 - 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., 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 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 -#else -#include "config.h" -#endif -#endif - -/* Get isblank from GNU libc. */ -#define _GNU_SOURCE -#include -#ifndef ISBLANK -#define ISBLANK(c) ((c) == ' ' || (c) == '\t') -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void usage (); - -#define min(a, b) ((a) < (b) ? (a) : (b)) -#define UCHAR_LIM (UCHAR_MAX + 1) -#define UCHAR(c) ((unsigned char) (c)) - -#ifdef isascii -#define ISALNUM(c) (isascii(c) && isalnum(c)) -#define ISDIGIT(c) (isascii(c) && isdigit(c)) -#define ISPRINT(c) (isascii(c) && isprint(c)) -#define ISLOWER(c) (isascii(c) && islower(c)) -#else -#define ISALNUM(c) isalnum(c) -#define ISDIGIT(c) isdigit(c) -#define ISPRINT(c) isprint(c) -#define ISLOWER(c) islower(c) -#endif - -/* The kind of blanks for '-b' to skip in various options. */ -enum blanktype { bl_start, bl_end, bl_both }; - -/* 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 -{ - char *name; - int val; -} 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 = 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 = 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 262144 - -/* 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; - -/* 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. */ -}; - -/* 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 -{ - char *name; - struct tempnode *next; -} temphead; - -/* Clean up any remaining temporary files. */ - -static void -cleanup () -{ - struct tempnode *node; - - for (node = temphead.next; node; node = node->next) - unlink (node->name); -} - -/* Allocate N bytes of memory dynamically, with error checking. */ - -char * -xmalloc (n) - unsigned 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. */ - -char * -xrealloc (p, n) - char *p; - unsigned 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 * -xfopen (file, how) - char *file, *how; -{ - FILE *fp = strcmp (file, "-") ? fopen (file, how) : stdin; - - if (fp == 0) - { - error (0, errno, "%s", file); - cleanup (); - exit (2); - } - if (fp == stdin) - have_read_stdin = 1; - return fp; -} - -static void -xfclose (fp) - FILE *fp; -{ - fflush (fp); - if (fp != stdin && fp != stdout) - { - if (fclose (fp) != 0) - { - error (0, errno, "error closing file"); - cleanup (); - exit (2); - } - } - else - /* Allow reading stdin from tty more than once. */ - clearerr (fp); -} - -static void -xfwrite (buf, size, nelem, fp) - char *buf; - int size, 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 () -{ - static int seq; - int len = strlen (temp_file_prefix); - 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; - return name; -} - -/* Search through the list of temporary files for NAME; - remove it if it is found on the list. */ - -static void -zaptemp (name) - 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 () -{ - 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 (buf, alloc) - 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 (buf, fp) - struct buffer *buf; - FILE *fp; -{ - int cc; - - 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))) - { - 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 (lines, alloc, limit) - 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 (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; - - 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; - - while (ptr < lim && schar--) - ++ptr; - - return ptr; -} - -/* Return the limit of (a pointer to the first character after) the field - in LINE specified by KEY. */ - -static char * -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; - - if (tab) - while (ptr < lim && eword--) - { - while (ptr < lim && *ptr != tab) - ++ptr; - if (ptr < lim && (eword || key->skipeblanks)) - ++ptr; - } - else - while (ptr < lim && eword--) - { - while (ptr < lim && blanks[UCHAR (*ptr)]) - ++ptr; - while (ptr < lim && !blanks[UCHAR (*ptr)]) - ++ptr; - } - - if (key->skipeblanks) - while (ptr < lim && blanks[UCHAR (*ptr)]) - ++ptr; - - while (ptr < lim && echar--) - ++ptr; - - return ptr; -} - -/* Find the lines in BUF, storing pointers and lengths in LINES. - Also replace newlines with NULs. */ - -static void -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; - - 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; - } - } - 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 (a, b) - register char *a, *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 (a, b) - register char *a, *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 == '-') - { - tmpa = UCHAR (*++a); - if (tmpb != '-') - { - if (digits[tmpa] && digits[tmpb]) - return -1; - return 0; - } - tmpb = UCHAR (*++b); - - 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 = logb - loga) != 0) - return tmp; - - if (!loga) - return 0; - - return tmpb - tmpa; - } - else if (tmpb == '-') - { - if (digits[UCHAR (tmpa)] && digits[UCHAR (*++b)]) - 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; - - return tmpa - tmpb; - } -} - -/* Return an integer <= 12 associated with month name S with length LEN, - 0 if the name in S is not recognized. */ - -static int -getmonth (s, len) - 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 (a, b) - struct line *a, *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; - - /* 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->month) - { - diff = getmonth (texta, lena) - getmonth (textb, lenb); - if (diff) - return key->reverse ? -diff : diff; - continue; - } - else if (ignore && translate) - 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) - 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)]; - break; - } - } - else - diff = memcmp (texta, textb, min (lena, lenb)); - - 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 (a, b) - register struct line *a, *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; - - diff = UCHAR (*ap) - UCHAR (*bp); - if (diff == 0) - { - diff = memcmp (ap, bp, mini); - if (diff == 0) - diff = tmpa - tmpb; - } - } - - 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. */ - -static int -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 cmp; /* Result of calling compare. */ - int alloc, i, success = 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); - findlines (&buf, &lines); - - 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; - } - } - - /* 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; - - 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 success; -} - -/* Merge lines from FPS onto OFP. NFPS cannot be greater than NMERGE. - Close FPS before returning. */ - -static void -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. */ - 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; - - /* 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 out, 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; - bcopy (lines[ord[0]].lines[cur[ord[0]]].text, saved.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 (lines, nlines, temp) - struct line *lines, *temp; - int nlines; -{ - 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 (files, nfiles) - char *files[]; - int nfiles; -{ - int i, disorders = 0; - FILE *fp; - - for (i = 0; i < nfiles; ++i) - { - fp = xfopen (files[i], "r"); - if (!checkfp (fp)) - { - printf ("%s: disorder on %s\n", program_name, files[i]); - ++disorders; - } - } - return disorders; -} - -/* Merge NFILES FILES onto OFP. */ - -static void -merge (files, nfiles, ofp) - 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 = xfopen (temp = tempname (), "w"); - 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 = xfopen (temp = tempname (), "w"); - 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 (files, nfiles, ofp) - 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 ntemp = 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 && !ntemp && !buf.left) - tfp = ofp; - else - { - ++ntemp; - tfp = xfopen (tempname (), "w"); - } - 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 (ntemp) - { - tempfiles = (char **) xmalloc (ntemp * sizeof (char *)); - i = ntemp; - for (node = temphead.next; i > 0; node = node->next) - tempfiles[--i] = node->name; - merge (tempfiles, ntemp, ofp); - free ((char *) tempfiles); - } -} - -/* Insert key KEY at the end of the list (`keyhead'). */ - -static void -insertkey (key) - struct keyfield *key; -{ - struct keyfield *k = &keyhead; - - while (k->next) - k = k->next; - k->next = key; - key->next = NULL; -} - -static void -badfieldspec (s) - char *s; -{ - error (2, 0, "invalid field specification `%s'", s); -} - -/* Handle interrupts and hangups. */ - -static void -sighandler (sig) - int sig; -{ -#ifdef _POSIX_VERSION - struct sigaction sigact; - - sigact.sa_handler = SIG_DFL; - sigemptyset (&sigact.sa_mask); - sigact.sa_flags = 0; - sigaction (sig, &sigact, NULL); -#else /* !_POSIX_VERSION */ - signal (sig, SIG_DFL); -#endif /* _POSIX_VERSION */ - 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 (s, key, blanktype) - register 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; -#if 0 - case 'g': - /* Reserved for comparing floating-point numbers. */ - break; -#endif - case 'i': - key->ignore = nonprinting; - break; - case 'M': - key->month = 1; - break; - case 'n': - key->numeric = 1; - break; - case 'r': - key->reverse = 1; - break; - default: - return s; - } - ++s; - } - return s; -} - -int -main (argc, argv) - 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 _POSIX_VERSION - struct sigaction oldact, newact; -#endif /* _POSIX_VERSION */ - - program_name = argv[0]; - -#if 0 - parse_long_options (argc, argv, usage); -#endif - - have_read_stdin = 0; - inittables (); - - temp_file_prefix = getenv ("TMPDIR"); - if (temp_file_prefix == NULL) - temp_file_prefix = "/tmp"; - -#ifdef _POSIX_VERSION - 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 /* !_POSIX_VERSION */ - 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 /* !_POSIX_VERSION */ - - gkey.sword = gkey.eword = -1; - gkey.ignore = NULL; - gkey.translate = NULL; - gkey.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->month = key->reverse = 0; - s = argv[i] + 1; - if (!digits[UCHAR (*s)]) - 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)]) - { - 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)); - 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) - t--; - t2 = 0; - if (*s == '.') - { - for (++s; digits[UCHAR (*s)]; ++s) - t2 = 10 * t2 + *s - '0'; - if (t2) - t2--; - } - if (t2 || t) - { - key->sword = t; - key->schar = t2; - } - else - key->sword = -1; - s = set_ordering (s, key, bl_start); - if (*s && *s != ',') - badfieldspec (argv[i]); - else if (*s++) - { - /* Get POS2. */ - 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'; - if (t2) - 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]; - goto outer; - } - else - error (2, 0, "option `-T' requires an argument"); - 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->ignore = gkey.ignore; - key->translate = gkey.translate; - key->skipsblanks = gkey.skipsblanks; - key->skipeblanks = gkey.skipeblanks; - key->month = gkey.month; - key->numeric = gkey.numeric; - key->reverse = gkey.reverse; - } - - if (!keyhead.next && (gkey.ignore || gkey.translate || gkey.skipsblanks - || gkey.skipeblanks || gkey.month || gkey.numeric)) - insertkey (&gkey); - reverse = gkey.reverse; - - if (nfiles == 0) - { - nfiles = 1; - files = − - } - - if (checkonly) - exit (check (files, nfiles) != 0); - - if (strcmp (outfile, "-")) - { - for (i = 0; i < nfiles; ++i) - if (!strcmp (outfile, files[i])) - break; - if (i == nfiles) - ofp = xfopen (outfile, "w"); - else - { - 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)) - { - error (0, errno, "%s", outfile); - 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, "fflush", outfile); - - if (have_read_stdin && fclose (stdin) == EOF) - error (1, errno, "-"); - if (ferror (stdout) || fclose (stdout) == EOF) - error (1, errno, "write error"); - - exit (0); -} - -#ifdef BUILTIN_HELP -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); -} -#else /* use the man page */ -static void -usage (status) - int status; -{ - fprintf (stderr, "\ -Usage: %s [-cmus] [-t separator] [-o output-file] [-bdfiMnr] [+POS1 [-POS2]]\n\ - [-k POS1[,POS2]] [file...]\n", - program_name); - exit (status); -} -#endif - -error (n, e, s, s1) - int n, e; - char *s, *s1; -{ - if (e) - fprintf(stderr,"error %d:", e); - fprintf(stderr, s, s1); - if (n) - exit(n); -} -- cgit v1.2.3